mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
make service interfaces and hosts one to one
This commit is contained in:
@@ -1,28 +1,28 @@
|
||||
import { Address, Effects, HostName, NetworkInterface } from "../types"
|
||||
import { AddressInfo, Effects, Hostname, ServiceInterface } from "../types"
|
||||
import * as regexes from "./regexes"
|
||||
import { NetworkInterfaceType } from "./utils"
|
||||
import { ServiceInterfaceType } from "./utils"
|
||||
|
||||
export type UrlString = string
|
||||
export type HostId = string
|
||||
|
||||
const getHostnameRegex = /^(\w+:\/\/)?([^\/\:]+)(:\d{1,3})?(\/)?/
|
||||
export const getHostname = (url: string): HostName | null => {
|
||||
export const getHostname = (url: string): Hostname | null => {
|
||||
const founds = url.match(getHostnameRegex)?.[2]
|
||||
if (!founds) return null
|
||||
const parts = founds.split("@")
|
||||
const last = parts[parts.length - 1] as HostName | null
|
||||
const last = parts[parts.length - 1] as Hostname | null
|
||||
return last
|
||||
}
|
||||
|
||||
export type Filled = {
|
||||
hostnames: HostName[]
|
||||
onionHostnames: HostName[]
|
||||
localHostnames: HostName[]
|
||||
ipHostnames: HostName[]
|
||||
ipv4Hostnames: HostName[]
|
||||
ipv6Hostnames: HostName[]
|
||||
nonIpHostnames: HostName[]
|
||||
allHostnames: HostName[]
|
||||
hostnames: Hostname[]
|
||||
onionHostnames: Hostname[]
|
||||
localHostnames: Hostname[]
|
||||
ipHostnames: Hostname[]
|
||||
ipv4Hostnames: Hostname[]
|
||||
ipv6Hostnames: Hostname[]
|
||||
nonIpHostnames: Hostname[]
|
||||
allHostnames: Hostname[]
|
||||
|
||||
urls: UrlString[]
|
||||
onionUrls: UrlString[]
|
||||
@@ -33,9 +33,9 @@ export type Filled = {
|
||||
nonIpUrls: UrlString[]
|
||||
allUrls: UrlString[]
|
||||
}
|
||||
export type FilledAddress = Address & Filled
|
||||
export type NetworkInterfaceFilled = {
|
||||
interfaceId: string
|
||||
export type FilledAddressInfo = AddressInfo & Filled
|
||||
export type ServiceInterfaceFilled = {
|
||||
id: string
|
||||
/** The title of this field to be displayed */
|
||||
name: string
|
||||
/** Human readable description, used as tooltip usually */
|
||||
@@ -44,15 +44,15 @@ export type NetworkInterfaceFilled = {
|
||||
hasPrimary: boolean
|
||||
/** Whether or not the interface disabled */
|
||||
disabled: boolean
|
||||
/** All URIs */
|
||||
addresses: FilledAddress[]
|
||||
|
||||
/** Indicates if we are a ui/ p2p/ api/ other for the kind of interface that this is representing */
|
||||
type: NetworkInterfaceType
|
||||
|
||||
primaryHostname: HostName | null
|
||||
/** URI information */
|
||||
addressInfo: FilledAddressInfo
|
||||
/** Indicates if we are a ui/p2p/api for the kind of interface that this is representing */
|
||||
type: ServiceInterfaceType
|
||||
/** The primary hostname for the service, as chosen by the user */
|
||||
primaryHostname: Hostname | null
|
||||
/** The primary URL for the service, as chosen by the user */
|
||||
primaryUrl: UrlString | null
|
||||
} & Filled
|
||||
}
|
||||
const either =
|
||||
<A>(...args: ((a: A) => boolean)[]) =>
|
||||
(a: A) =>
|
||||
@@ -63,8 +63,8 @@ const negate =
|
||||
!fn(a)
|
||||
const unique = <A>(values: A[]) => Array.from(new Set(values))
|
||||
const addressHostToUrl = (
|
||||
{ options, username, suffix }: Address,
|
||||
host: HostName,
|
||||
{ options, username, suffix }: AddressInfo,
|
||||
host: Hostname,
|
||||
): UrlString => {
|
||||
const scheme = host.endsWith(".onion")
|
||||
? options.scheme
|
||||
@@ -76,15 +76,12 @@ const addressHostToUrl = (
|
||||
}${host}${suffix}`
|
||||
}
|
||||
export const filledAddress = (
|
||||
mapHostnames: {
|
||||
[hostId: string]: HostName[]
|
||||
},
|
||||
address: Address,
|
||||
): FilledAddress => {
|
||||
const toUrl = addressHostToUrl.bind(null, address)
|
||||
const hostnames = mapHostnames[address.hostId] ?? []
|
||||
hostnames: Hostname[],
|
||||
addressInfo: AddressInfo,
|
||||
): FilledAddressInfo => {
|
||||
const toUrl = addressHostToUrl.bind(null, addressInfo)
|
||||
return {
|
||||
...address,
|
||||
...addressInfo,
|
||||
hostnames,
|
||||
get onionHostnames() {
|
||||
return hostnames.filter(regexes.torHostname.test)
|
||||
@@ -138,131 +135,60 @@ export const filledAddress = (
|
||||
}
|
||||
}
|
||||
|
||||
export const networkInterfaceFilled = (
|
||||
interfaceValue: NetworkInterface,
|
||||
primaryUrl: UrlString | null,
|
||||
addresses: FilledAddress[],
|
||||
): NetworkInterfaceFilled => {
|
||||
return {
|
||||
...interfaceValue,
|
||||
addresses,
|
||||
get hostnames() {
|
||||
return unique(addresses.flatMap((x) => x.hostnames))
|
||||
},
|
||||
get onionHostnames() {
|
||||
return unique(addresses.flatMap((x) => x.onionHostnames))
|
||||
},
|
||||
get localHostnames() {
|
||||
return unique(addresses.flatMap((x) => x.localHostnames))
|
||||
},
|
||||
get ipHostnames() {
|
||||
return unique(addresses.flatMap((x) => x.ipHostnames))
|
||||
},
|
||||
get ipv4Hostnames() {
|
||||
return unique(addresses.flatMap((x) => x.ipv4Hostnames))
|
||||
},
|
||||
get ipv6Hostnames() {
|
||||
return unique(addresses.flatMap((x) => x.ipv6Hostnames))
|
||||
},
|
||||
get nonIpHostnames() {
|
||||
return unique(addresses.flatMap((x) => x.nonIpHostnames))
|
||||
},
|
||||
get allHostnames() {
|
||||
return unique(addresses.flatMap((x) => x.allHostnames))
|
||||
},
|
||||
get primaryHostname() {
|
||||
if (primaryUrl == null) return null
|
||||
return getHostname(primaryUrl)
|
||||
},
|
||||
get urls() {
|
||||
return unique(addresses.flatMap((x) => x.urls))
|
||||
},
|
||||
get onionUrls() {
|
||||
return unique(addresses.flatMap((x) => x.onionUrls))
|
||||
},
|
||||
get localUrls() {
|
||||
return unique(addresses.flatMap((x) => x.localUrls))
|
||||
},
|
||||
get ipUrls() {
|
||||
return unique(addresses.flatMap((x) => x.ipUrls))
|
||||
},
|
||||
get ipv4Urls() {
|
||||
return unique(addresses.flatMap((x) => x.ipv4Urls))
|
||||
},
|
||||
get ipv6Urls() {
|
||||
return unique(addresses.flatMap((x) => x.ipv6Urls))
|
||||
},
|
||||
get nonIpUrls() {
|
||||
return unique(addresses.flatMap((x) => x.nonIpUrls))
|
||||
},
|
||||
get allUrls() {
|
||||
return unique(addresses.flatMap((x) => x.allUrls))
|
||||
},
|
||||
primaryUrl,
|
||||
}
|
||||
}
|
||||
const makeInterfaceFilled = async ({
|
||||
effects,
|
||||
interfaceId,
|
||||
id,
|
||||
packageId,
|
||||
callback,
|
||||
}: {
|
||||
effects: Effects
|
||||
interfaceId: string
|
||||
id: string
|
||||
packageId: string | undefined
|
||||
callback: () => void
|
||||
}) => {
|
||||
const interfaceValue = await effects.getInterface({
|
||||
interfaceId,
|
||||
const serviceInterfaceValue = await effects.getServiceInterface({
|
||||
serviceInterfaceId: id,
|
||||
packageId,
|
||||
callback,
|
||||
})
|
||||
const hostIdsRecord = Promise.all(
|
||||
unique(interfaceValue.addresses.map((x) => x.hostId)).map(
|
||||
async (hostId) =>
|
||||
[
|
||||
hostId,
|
||||
await effects.getHostnames({
|
||||
packageId,
|
||||
hostId,
|
||||
callback,
|
||||
}),
|
||||
] as const,
|
||||
),
|
||||
)
|
||||
const primaryUrl = effects.getPrimaryUrl({
|
||||
interfaceId,
|
||||
const hostIdRecord = await effects.getHostnames({
|
||||
packageId,
|
||||
hostId: serviceInterfaceValue.addressInfo.hostId,
|
||||
callback,
|
||||
})
|
||||
const primaryUrl = await effects.getPrimaryUrl({
|
||||
serviceInterfaceId: id,
|
||||
packageId,
|
||||
callback,
|
||||
})
|
||||
|
||||
const fillAddress = filledAddress.bind(
|
||||
null,
|
||||
Object.fromEntries(await hostIdsRecord),
|
||||
)
|
||||
const interfaceFilled: NetworkInterfaceFilled = networkInterfaceFilled(
|
||||
interfaceValue,
|
||||
await primaryUrl,
|
||||
interfaceValue.addresses.map(fillAddress),
|
||||
)
|
||||
const interfaceFilled: ServiceInterfaceFilled = {
|
||||
...serviceInterfaceValue,
|
||||
primaryUrl: primaryUrl,
|
||||
addressInfo: filledAddress(hostIdRecord, serviceInterfaceValue.addressInfo),
|
||||
get primaryHostname() {
|
||||
if (primaryUrl == null) return null
|
||||
return getHostname(primaryUrl)
|
||||
},
|
||||
}
|
||||
return interfaceFilled
|
||||
}
|
||||
|
||||
export class GetNetworkInterface {
|
||||
export class GetServiceInterface {
|
||||
constructor(
|
||||
readonly effects: Effects,
|
||||
readonly opts: { interfaceId: string; packageId?: string },
|
||||
readonly opts: { id: string; packageId?: string },
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns the value of Store at the provided path. Restart the service if the value changes
|
||||
*/
|
||||
async const() {
|
||||
const { interfaceId, packageId } = this.opts
|
||||
const { id, packageId } = this.opts
|
||||
const callback = this.effects.restart
|
||||
const interfaceFilled: NetworkInterfaceFilled = await makeInterfaceFilled({
|
||||
const interfaceFilled: ServiceInterfaceFilled = await makeInterfaceFilled({
|
||||
effects: this.effects,
|
||||
interfaceId,
|
||||
id,
|
||||
packageId,
|
||||
callback,
|
||||
})
|
||||
@@ -270,14 +196,14 @@ export class GetNetworkInterface {
|
||||
return interfaceFilled
|
||||
}
|
||||
/**
|
||||
* Returns the value of NetworkInterfacesFilled at the provided path. Does nothing if the value changes
|
||||
* Returns the value of ServiceInterfacesFilled at the provided path. Does nothing if the value changes
|
||||
*/
|
||||
async once() {
|
||||
const { interfaceId, packageId } = this.opts
|
||||
const { id, packageId } = this.opts
|
||||
const callback = () => {}
|
||||
const interfaceFilled: NetworkInterfaceFilled = await makeInterfaceFilled({
|
||||
const interfaceFilled: ServiceInterfaceFilled = await makeInterfaceFilled({
|
||||
effects: this.effects,
|
||||
interfaceId,
|
||||
id,
|
||||
packageId,
|
||||
callback,
|
||||
})
|
||||
@@ -286,10 +212,10 @@ export class GetNetworkInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Watches the value of NetworkInterfacesFilled at the provided path. Takes a custom callback function to run whenever the value changes
|
||||
* Watches the value of ServiceInterfacesFilled at the provided path. Takes a custom callback function to run whenever the value changes
|
||||
*/
|
||||
async *watch() {
|
||||
const { interfaceId, packageId } = this.opts
|
||||
const { id, packageId } = this.opts
|
||||
while (true) {
|
||||
let callback: () => void = () => {}
|
||||
const waitForNext = new Promise<void>((resolve) => {
|
||||
@@ -297,7 +223,7 @@ export class GetNetworkInterface {
|
||||
})
|
||||
yield await makeInterfaceFilled({
|
||||
effects: this.effects,
|
||||
interfaceId,
|
||||
id,
|
||||
packageId,
|
||||
callback,
|
||||
})
|
||||
@@ -305,9 +231,9 @@ export class GetNetworkInterface {
|
||||
}
|
||||
}
|
||||
}
|
||||
export function getNetworkInterface(
|
||||
export function getServiceInterface(
|
||||
effects: Effects,
|
||||
opts: { interfaceId: string; packageId?: string },
|
||||
opts: { id: string; packageId?: string },
|
||||
) {
|
||||
return new GetNetworkInterface(effects, opts)
|
||||
return new GetServiceInterface(effects, opts)
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Effects, HostName } from "../types"
|
||||
import { Effects } from "../types"
|
||||
import {
|
||||
HostId,
|
||||
NetworkInterfaceFilled,
|
||||
ServiceInterfaceFilled,
|
||||
filledAddress,
|
||||
networkInterfaceFilled,
|
||||
} from "./getNetworkInterface"
|
||||
getHostname,
|
||||
} from "./getServiceInterface"
|
||||
|
||||
const makeManyInterfaceFilled = async ({
|
||||
effects,
|
||||
@@ -15,7 +14,7 @@ const makeManyInterfaceFilled = async ({
|
||||
packageId: string | undefined
|
||||
callback: () => void
|
||||
}) => {
|
||||
const interfaceValues = await effects.listInterface({
|
||||
const serviceInterfaceValues = await effects.listServiceInterfaces({
|
||||
packageId,
|
||||
callback,
|
||||
})
|
||||
@@ -23,7 +22,9 @@ const makeManyInterfaceFilled = async ({
|
||||
await Promise.all(
|
||||
Array.from(
|
||||
new Set(
|
||||
interfaceValues.flatMap((x) => x.addresses).map((x) => x.hostId),
|
||||
serviceInterfaceValues
|
||||
.flatMap((x) => x.addressInfo)
|
||||
.map((x) => x.hostId),
|
||||
),
|
||||
).map(
|
||||
async (hostId) =>
|
||||
@@ -38,25 +39,37 @@ const makeManyInterfaceFilled = async ({
|
||||
),
|
||||
),
|
||||
)
|
||||
const fillAddress = filledAddress.bind(null, hostIdsRecord)
|
||||
|
||||
const interfacesFilled: NetworkInterfaceFilled[] = await Promise.all(
|
||||
interfaceValues.map(async (interfaceValue) =>
|
||||
networkInterfaceFilled(
|
||||
interfaceValue,
|
||||
await effects.getPrimaryUrl({
|
||||
interfaceId: interfaceValue.interfaceId,
|
||||
packageId,
|
||||
callback,
|
||||
}),
|
||||
interfaceValue.addresses.map(fillAddress),
|
||||
),
|
||||
),
|
||||
const serviceInterfacesFilled: ServiceInterfaceFilled[] = await Promise.all(
|
||||
serviceInterfaceValues.map(async (serviceInterfaceValue) => {
|
||||
const hostIdRecord = await effects.getHostnames({
|
||||
packageId,
|
||||
hostId: serviceInterfaceValue.addressInfo.hostId,
|
||||
callback,
|
||||
})
|
||||
const primaryUrl = await effects.getPrimaryUrl({
|
||||
serviceInterfaceId: serviceInterfaceValue.id,
|
||||
packageId,
|
||||
callback,
|
||||
})
|
||||
return {
|
||||
...serviceInterfaceValue,
|
||||
primaryUrl: primaryUrl,
|
||||
addressInfo: filledAddress(
|
||||
hostIdRecord,
|
||||
serviceInterfaceValue.addressInfo,
|
||||
),
|
||||
get primaryHostname() {
|
||||
if (primaryUrl == null) return null
|
||||
return getHostname(primaryUrl)
|
||||
},
|
||||
}
|
||||
}),
|
||||
)
|
||||
return interfacesFilled
|
||||
return serviceInterfacesFilled
|
||||
}
|
||||
|
||||
export class GetNetworkInterfaces {
|
||||
export class GetServiceInterfaces {
|
||||
constructor(
|
||||
readonly effects: Effects,
|
||||
readonly opts: { packageId?: string },
|
||||
@@ -68,7 +81,7 @@ export class GetNetworkInterfaces {
|
||||
async const() {
|
||||
const { packageId } = this.opts
|
||||
const callback = this.effects.restart
|
||||
const interfaceFilled: NetworkInterfaceFilled[] =
|
||||
const interfaceFilled: ServiceInterfaceFilled[] =
|
||||
await makeManyInterfaceFilled({
|
||||
effects: this.effects,
|
||||
packageId,
|
||||
@@ -78,12 +91,12 @@ export class GetNetworkInterfaces {
|
||||
return interfaceFilled
|
||||
}
|
||||
/**
|
||||
* Returns the value of NetworkInterfacesFilled at the provided path. Does nothing if the value changes
|
||||
* Returns the value of ServiceInterfacesFilled at the provided path. Does nothing if the value changes
|
||||
*/
|
||||
async once() {
|
||||
const { packageId } = this.opts
|
||||
const callback = () => {}
|
||||
const interfaceFilled: NetworkInterfaceFilled[] =
|
||||
const interfaceFilled: ServiceInterfaceFilled[] =
|
||||
await makeManyInterfaceFilled({
|
||||
effects: this.effects,
|
||||
packageId,
|
||||
@@ -94,7 +107,7 @@ export class GetNetworkInterfaces {
|
||||
}
|
||||
|
||||
/**
|
||||
* Watches the value of NetworkInterfacesFilled at the provided path. Takes a custom callback function to run whenever the value changes
|
||||
* Watches the value of ServiceInterfacesFilled at the provided path. Takes a custom callback function to run whenever the value changes
|
||||
*/
|
||||
async *watch() {
|
||||
const { packageId } = this.opts
|
||||
@@ -112,9 +125,9 @@ export class GetNetworkInterfaces {
|
||||
}
|
||||
}
|
||||
}
|
||||
export function getNetworkInterfaces(
|
||||
export function getServiceInterfaces(
|
||||
effects: Effects,
|
||||
opts: { packageId?: string },
|
||||
) {
|
||||
return new GetNetworkInterfaces(effects, opts)
|
||||
return new GetServiceInterfaces(effects, opts)
|
||||
}
|
||||
@@ -9,13 +9,11 @@ import {
|
||||
Effects,
|
||||
EnsureStorePath,
|
||||
ExtractStore,
|
||||
InterfaceId,
|
||||
ServiceInterfaceId,
|
||||
PackageId,
|
||||
ValidIfNoStupidEscape,
|
||||
} from "../types"
|
||||
import { GetSystemSmtp } from "./GetSystemSmtp"
|
||||
import { DefaultString } from "../config/configTypes"
|
||||
import { getDefaultString } from "./getDefaultString"
|
||||
import { GetStore, getStore } from "../store/getStore"
|
||||
import {
|
||||
MountDependenciesOut,
|
||||
@@ -27,13 +25,13 @@ import {
|
||||
NamedPath,
|
||||
Path,
|
||||
} from "../dependency/setupDependencyMounts"
|
||||
import { Host, MultiHost, SingleHost, StaticHost } from "../interfaces/Host"
|
||||
import { NetworkInterfaceBuilder } from "../interfaces/NetworkInterfaceBuilder"
|
||||
import { GetNetworkInterface, getNetworkInterface } from "./getNetworkInterface"
|
||||
import { MultiHost, SingleHost, StaticHost } from "../interfaces/Host"
|
||||
import { ServiceInterfaceBuilder } from "../interfaces/ServiceInterfaceBuilder"
|
||||
import { GetServiceInterface, getServiceInterface } from "./getServiceInterface"
|
||||
import {
|
||||
GetNetworkInterfaces,
|
||||
getNetworkInterfaces,
|
||||
} from "./getNetworkInterfaces"
|
||||
GetServiceInterfaces,
|
||||
getServiceInterfaces,
|
||||
} from "./getServiceInterfaces"
|
||||
import * as CP from "node:child_process"
|
||||
import { promisify } from "node:util"
|
||||
import { splitCommand } from "./splitCommand"
|
||||
@@ -50,7 +48,7 @@ const childProcess = {
|
||||
execFile: promisify(CP.execFile),
|
||||
}
|
||||
|
||||
export type NetworkInterfaceType = "ui" | "p2p" | "api" | "other"
|
||||
export type ServiceInterfaceType = "ui" | "p2p" | "api"
|
||||
|
||||
export type Utils<
|
||||
Manifest extends SDKManifest,
|
||||
@@ -81,11 +79,11 @@ export type Utils<
|
||||
description: string
|
||||
hasPrimary: boolean
|
||||
disabled: boolean
|
||||
type: NetworkInterfaceType
|
||||
type: ServiceInterfaceType
|
||||
username: null | string
|
||||
path: string
|
||||
search: Record<string, string>
|
||||
}) => NetworkInterfaceBuilder
|
||||
}) => ServiceInterfaceBuilder
|
||||
getSystemSmtp: () => GetSystemSmtp & WrapperOverWrite
|
||||
host: {
|
||||
static: (id: string) => StaticHost
|
||||
@@ -101,16 +99,16 @@ export type Utils<
|
||||
>(
|
||||
value: In,
|
||||
) => Promise<MountDependenciesOut<In>>
|
||||
networkInterface: {
|
||||
getOwn: (interfaceId: InterfaceId) => GetNetworkInterface & WrapperOverWrite
|
||||
serviceInterface: {
|
||||
getOwn: (id: ServiceInterfaceId) => GetServiceInterface & WrapperOverWrite
|
||||
get: (opts: {
|
||||
interfaceId: InterfaceId
|
||||
id: ServiceInterfaceId
|
||||
packageId: PackageId
|
||||
}) => GetNetworkInterface & WrapperOverWrite
|
||||
getAllOwn: () => GetNetworkInterfaces & WrapperOverWrite
|
||||
}) => GetServiceInterface & WrapperOverWrite
|
||||
getAllOwn: () => GetServiceInterfaces & WrapperOverWrite
|
||||
getAll: (opts: {
|
||||
packageId: PackageId
|
||||
}) => GetNetworkInterfaces & WrapperOverWrite
|
||||
}) => GetServiceInterfaces & WrapperOverWrite
|
||||
}
|
||||
nullIfEmpty: typeof nullIfEmpty
|
||||
runCommand: <A extends string>(
|
||||
@@ -156,11 +154,11 @@ export const createUtils = <
|
||||
description: string
|
||||
hasPrimary: boolean
|
||||
disabled: boolean
|
||||
type: NetworkInterfaceType
|
||||
type: ServiceInterfaceType
|
||||
username: null | string
|
||||
path: string
|
||||
search: Record<string, string>
|
||||
}) => new NetworkInterfaceBuilder({ ...options, effects }),
|
||||
}) => new ServiceInterfaceBuilder({ ...options, effects }),
|
||||
childProcess,
|
||||
getSystemSmtp: () =>
|
||||
new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite,
|
||||
@@ -172,18 +170,18 @@ export const createUtils = <
|
||||
},
|
||||
nullIfEmpty,
|
||||
|
||||
networkInterface: {
|
||||
getOwn: (interfaceId: InterfaceId) =>
|
||||
getNetworkInterface(effects, { interfaceId }) as GetNetworkInterface &
|
||||
serviceInterface: {
|
||||
getOwn: (id: ServiceInterfaceId) =>
|
||||
getServiceInterface(effects, { id }) as GetServiceInterface &
|
||||
WrapperOverWrite,
|
||||
get: (opts: { interfaceId: InterfaceId; packageId: PackageId }) =>
|
||||
getNetworkInterface(effects, opts) as GetNetworkInterface &
|
||||
get: (opts: { id: ServiceInterfaceId; packageId: PackageId }) =>
|
||||
getServiceInterface(effects, opts) as GetServiceInterface &
|
||||
WrapperOverWrite,
|
||||
getAllOwn: () =>
|
||||
getNetworkInterfaces(effects, {}) as GetNetworkInterfaces &
|
||||
getServiceInterfaces(effects, {}) as GetServiceInterfaces &
|
||||
WrapperOverWrite,
|
||||
getAll: (opts: { packageId: PackageId }) =>
|
||||
getNetworkInterfaces(effects, opts) as GetNetworkInterfaces &
|
||||
getServiceInterfaces(effects, opts) as GetServiceInterfaces &
|
||||
WrapperOverWrite,
|
||||
},
|
||||
store: {
|
||||
|
||||
Reference in New Issue
Block a user