mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
chore: Remove the utils
This commit is contained in:
@@ -19,9 +19,14 @@ import {
|
|||||||
BackupOptions,
|
BackupOptions,
|
||||||
DeepPartial,
|
DeepPartial,
|
||||||
MaybePromise,
|
MaybePromise,
|
||||||
|
ServiceInterfaceId,
|
||||||
|
PackageId,
|
||||||
|
EnsureStorePath,
|
||||||
|
ExtractStore,
|
||||||
|
DaemonReturned,
|
||||||
|
ValidIfNoStupidEscape,
|
||||||
} from "./types"
|
} from "./types"
|
||||||
import * as patterns from "./util/patterns"
|
import * as patterns from "./util/patterns"
|
||||||
import { Utils } from "./util/utils"
|
|
||||||
import { DependencyConfig, Update } from "./dependencyConfig/DependencyConfig"
|
import { DependencyConfig, Update } from "./dependencyConfig/DependencyConfig"
|
||||||
import { BackupSet, Backups } from "./backup/Backups"
|
import { BackupSet, Backups } from "./backup/Backups"
|
||||||
import { smtpConfig } from "./config/configConstants"
|
import { smtpConfig } from "./config/configConstants"
|
||||||
@@ -46,7 +51,13 @@ import { setupMain } from "./mainFn"
|
|||||||
import { defaultTrigger } from "./trigger/defaultTrigger"
|
import { defaultTrigger } from "./trigger/defaultTrigger"
|
||||||
import { changeOnFirstSuccess, cooldownTrigger } from "./trigger"
|
import { changeOnFirstSuccess, cooldownTrigger } from "./trigger"
|
||||||
import setupConfig, { Read, Save } from "./config/setupConfig"
|
import setupConfig, { Read, Save } from "./config/setupConfig"
|
||||||
import { setupDependencyMounts } from "./dependency/setupDependencyMounts"
|
import {
|
||||||
|
ManifestId,
|
||||||
|
NamedPath,
|
||||||
|
Path,
|
||||||
|
VolumeName,
|
||||||
|
setupDependencyMounts,
|
||||||
|
} from "./dependency/setupDependencyMounts"
|
||||||
import {
|
import {
|
||||||
InterfacesReceipt,
|
InterfacesReceipt,
|
||||||
SetInterfaces,
|
SetInterfaces,
|
||||||
@@ -55,6 +66,19 @@ import {
|
|||||||
import { successFailure } from "./trigger/successFailure"
|
import { successFailure } from "./trigger/successFailure"
|
||||||
import { SetupExports } from "./inits/setupExports"
|
import { SetupExports } from "./inits/setupExports"
|
||||||
import { HealthReceipt } from "./health/HealthReceipt"
|
import { HealthReceipt } from "./health/HealthReceipt"
|
||||||
|
import { MultiHost, Scheme, SingleHost, StaticHost } from "./interfaces/Host"
|
||||||
|
import { ServiceInterfaceBuilder } from "./interfaces/ServiceInterfaceBuilder"
|
||||||
|
import { GetSystemSmtp } from "./util/GetSystemSmtp"
|
||||||
|
import nullIfEmpty from "./util/nullIfEmpty"
|
||||||
|
import {
|
||||||
|
GetServiceInterface,
|
||||||
|
getServiceInterface,
|
||||||
|
} from "./util/getServiceInterface"
|
||||||
|
import { getServiceInterfaces } from "./util/getServiceInterfaces"
|
||||||
|
import { getStore } from "./store/getStore"
|
||||||
|
import { mountDependencies } from "./dependency/mountDependencies"
|
||||||
|
import { CommandOptions, MountOptions, Overlay } from "./util/Overlay"
|
||||||
|
import { splitCommand } from "./util/splitCommand"
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type AnyNeverCond<T extends any[], Then, Else> =
|
type AnyNeverCond<T extends any[], Then, Else> =
|
||||||
@@ -63,6 +87,17 @@ type AnyNeverCond<T extends any[], Then, Else> =
|
|||||||
T extends [any, ...infer U] ? AnyNeverCond<U,Then, Else> :
|
T extends [any, ...infer U] ? AnyNeverCond<U,Then, Else> :
|
||||||
never
|
never
|
||||||
|
|
||||||
|
export type ServiceInterfaceType = "ui" | "p2p" | "api"
|
||||||
|
export type MainEffects = Effects & { _type: "main" }
|
||||||
|
export type Signals = NodeJS.Signals
|
||||||
|
export const SIGTERM: Signals = "SIGTERM"
|
||||||
|
export const SIGKILL: Signals = "SIGTERM"
|
||||||
|
export const NO_TIMEOUT = -1
|
||||||
|
|
||||||
|
function removeConstType<E>() {
|
||||||
|
return <T>(t: T) => t as T & (E extends MainEffects ? {} : { const: never })
|
||||||
|
}
|
||||||
|
|
||||||
export class StartSdk<Manifest extends SDKManifest, Store> {
|
export class StartSdk<Manifest extends SDKManifest, Store> {
|
||||||
private constructor(readonly manifest: Manifest) {}
|
private constructor(readonly manifest: Manifest) {}
|
||||||
static of() {
|
static of() {
|
||||||
@@ -77,7 +112,78 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
|||||||
|
|
||||||
build(isReady: AnyNeverCond<[Manifest, Store], "Build not ready", true>) {
|
build(isReady: AnyNeverCond<[Manifest, Store], "Build not ready", true>) {
|
||||||
return {
|
return {
|
||||||
|
serviceInterface: {
|
||||||
|
getOwn: <E extends Effects>(effects: E, id: ServiceInterfaceId) =>
|
||||||
|
removeConstType<E>()(
|
||||||
|
getServiceInterface(effects, {
|
||||||
|
id,
|
||||||
|
packageId: null,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
get: <E extends Effects>(
|
||||||
|
effects: E,
|
||||||
|
opts: { id: ServiceInterfaceId; packageId: PackageId },
|
||||||
|
) => removeConstType<E>()(getServiceInterface(effects, opts)),
|
||||||
|
getAllOwn: <E extends Effects>(effects: E) =>
|
||||||
|
removeConstType<E>()(
|
||||||
|
getServiceInterfaces(effects, {
|
||||||
|
packageId: null,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
getAll: <E extends Effects>(
|
||||||
|
effects: E,
|
||||||
|
opts: { packageId: PackageId },
|
||||||
|
) => removeConstType<E>()(getServiceInterfaces(effects, opts)),
|
||||||
|
},
|
||||||
|
|
||||||
|
store: {
|
||||||
|
get: <E extends Effects, Path extends string = never>(
|
||||||
|
effects: E,
|
||||||
|
packageId: string,
|
||||||
|
path: EnsureStorePath<Store, Path>,
|
||||||
|
) =>
|
||||||
|
removeConstType<E>()(
|
||||||
|
getStore<Store, Path>(effects, path as any, {
|
||||||
|
packageId,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
getOwn: <E extends Effects, Path extends string>(
|
||||||
|
effects: E,
|
||||||
|
path: EnsureStorePath<Store, Path>,
|
||||||
|
) => removeConstType<E>()(getStore<Store, Path>(effects, path as any)),
|
||||||
|
setOwn: <E extends Effects, Path extends string | never>(
|
||||||
|
effects: E,
|
||||||
|
path: EnsureStorePath<Store, Path>,
|
||||||
|
value: ExtractStore<Store, Path>,
|
||||||
|
) => effects.store.set<Store, Path>({ value, path: path as any }),
|
||||||
|
},
|
||||||
|
|
||||||
|
host: {
|
||||||
|
static: (effects: Effects, id: string) =>
|
||||||
|
new StaticHost({ id, effects }),
|
||||||
|
single: (effects: Effects, id: string) =>
|
||||||
|
new SingleHost({ id, effects }),
|
||||||
|
multi: (effects: Effects, id: string) => new MultiHost({ id, effects }),
|
||||||
|
},
|
||||||
|
nullIfEmpty,
|
||||||
|
|
||||||
configConstants: { smtpConfig },
|
configConstants: { smtpConfig },
|
||||||
|
createInterface: (
|
||||||
|
effects: Effects,
|
||||||
|
options: {
|
||||||
|
name: string
|
||||||
|
id: string
|
||||||
|
description: string
|
||||||
|
hasPrimary: boolean
|
||||||
|
disabled: boolean
|
||||||
|
type: ServiceInterfaceType
|
||||||
|
username: null | string
|
||||||
|
path: string
|
||||||
|
search: Record<string, string>
|
||||||
|
schemeOverride: { ssl: Scheme; noSsl: Scheme } | null
|
||||||
|
masked: boolean
|
||||||
|
},
|
||||||
|
) => new ServiceInterfaceBuilder({ ...options, effects }),
|
||||||
createAction: <
|
createAction: <
|
||||||
ConfigType extends
|
ConfigType extends
|
||||||
| Record<string, any>
|
| Record<string, any>
|
||||||
@@ -90,13 +196,44 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
|||||||
},
|
},
|
||||||
fn: (options: {
|
fn: (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
input: Type
|
input: Type
|
||||||
}) => Promise<ActionResult>,
|
}) => Promise<ActionResult>,
|
||||||
) => {
|
) => {
|
||||||
const { input, ...rest } = metaData
|
const { input, ...rest } = metaData
|
||||||
return createAction<Manifest, Store, ConfigType, Type>(rest, fn, input)
|
return createAction<Manifest, Store, ConfigType, Type>(rest, fn, input)
|
||||||
},
|
},
|
||||||
|
getSystemSmtp: <E extends Effects>(effects: E) =>
|
||||||
|
removeConstType<E>()(new GetSystemSmtp(effects)),
|
||||||
|
runCommand: async <A extends string>(
|
||||||
|
effects: Effects,
|
||||||
|
imageId: Manifest["images"][number],
|
||||||
|
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||||
|
options: CommandOptions & {
|
||||||
|
mounts?: { path: string; options: MountOptions }[]
|
||||||
|
},
|
||||||
|
): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => {
|
||||||
|
const commands = splitCommand(command)
|
||||||
|
const overlay = await Overlay.of(effects, imageId)
|
||||||
|
try {
|
||||||
|
for (let mount of options.mounts || []) {
|
||||||
|
await overlay.mount(mount.options, mount.path)
|
||||||
|
}
|
||||||
|
return await overlay.exec(commands)
|
||||||
|
} finally {
|
||||||
|
await overlay.destroy()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mountDependencies: <
|
||||||
|
In extends
|
||||||
|
| Record<ManifestId, Record<VolumeName, Record<NamedPath, Path>>>
|
||||||
|
| Record<VolumeName, Record<NamedPath, Path>>
|
||||||
|
| Record<NamedPath, Path>
|
||||||
|
| Path,
|
||||||
|
>(
|
||||||
|
effects: Effects,
|
||||||
|
value: In,
|
||||||
|
) => mountDependencies(effects, value),
|
||||||
createDynamicAction: <
|
createDynamicAction: <
|
||||||
ConfigType extends
|
ConfigType extends
|
||||||
| Record<string, any>
|
| Record<string, any>
|
||||||
@@ -106,11 +243,9 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
|||||||
>(
|
>(
|
||||||
metaData: (options: {
|
metaData: (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => MaybePromise<Omit<ActionMetadata, "input">>,
|
}) => MaybePromise<Omit<ActionMetadata, "input">>,
|
||||||
fn: (options: {
|
fn: (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
input: Type
|
input: Type
|
||||||
}) => Promise<ActionResult>,
|
}) => Promise<ActionResult>,
|
||||||
input: Config<Type, Store> | Config<Type, never>,
|
input: Config<Type, Store> | Config<Type, never>,
|
||||||
@@ -196,9 +331,8 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
|||||||
) => setupInterfaces(config, fn),
|
) => setupInterfaces(config, fn),
|
||||||
setupMain: (
|
setupMain: (
|
||||||
fn: (o: {
|
fn: (o: {
|
||||||
effects: Effects
|
effects: MainEffects
|
||||||
started(onTerm: () => PromiseLike<void>): PromiseLike<void>
|
started(onTerm: () => PromiseLike<void>): PromiseLike<void>
|
||||||
utils: Utils<Manifest, Store, {}>
|
|
||||||
}) => Promise<Daemons<Manifest, any>>,
|
}) => Promise<Daemons<Manifest, any>>,
|
||||||
) => setupMain<Manifest, Store>(fn),
|
) => setupMain<Manifest, Store>(fn),
|
||||||
setupMigrations: <
|
setupMigrations: <
|
||||||
@@ -259,7 +393,6 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
|||||||
dependencyConfig: (options: {
|
dependencyConfig: (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
localConfig: LocalConfig
|
localConfig: LocalConfig
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void | DeepPartial<RemoteConfig>>
|
}) => Promise<void | DeepPartial<RemoteConfig>>
|
||||||
update?: Update<void | DeepPartial<RemoteConfig>, RemoteConfig>
|
update?: Update<void | DeepPartial<RemoteConfig>, RemoteConfig>
|
||||||
}) {
|
}) {
|
||||||
@@ -343,14 +476,8 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
|||||||
Migration: {
|
Migration: {
|
||||||
of: <Version extends ManifestVersion>(options: {
|
of: <Version extends ManifestVersion>(options: {
|
||||||
version: Version
|
version: Version
|
||||||
up: (opts: {
|
up: (opts: { effects: Effects }) => Promise<void>
|
||||||
effects: Effects
|
down: (opts: { effects: Effects }) => Promise<void>
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
|
||||||
down: (opts: {
|
|
||||||
effects: Effects
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
|
||||||
}) => Migration.of<Manifest, Store, Version>(options),
|
}) => Migration.of<Manifest, Store, Version>(options),
|
||||||
},
|
},
|
||||||
Value: {
|
Value: {
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import { Config, ExtractConfigType } from "../config/builder/config"
|
import { Config, ExtractConfigType } from "../config/builder/config"
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import { ActionMetadata, ActionResult, Effects, ExportedAction } from "../types"
|
import { ActionMetadata, ActionResult, Effects, ExportedAction } from "../types"
|
||||||
import { createUtils } from "../util"
|
|
||||||
import { Utils } from "../util/utils"
|
|
||||||
|
|
||||||
export type MaybeFn<Manifest extends SDKManifest, Store, Value> =
|
export type MaybeFn<Manifest extends SDKManifest, Store, Value> =
|
||||||
| Value
|
| Value
|
||||||
| ((options: {
|
| ((options: { effects: Effects }) => Promise<Value> | Value)
|
||||||
effects: Effects
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<Value> | Value)
|
|
||||||
export class CreatedAction<
|
export class CreatedAction<
|
||||||
Manifest extends SDKManifest,
|
Manifest extends SDKManifest,
|
||||||
Store,
|
Store,
|
||||||
@@ -27,7 +22,6 @@ export class CreatedAction<
|
|||||||
>,
|
>,
|
||||||
readonly fn: (options: {
|
readonly fn: (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
input: Type
|
input: Type
|
||||||
}) => Promise<ActionResult>,
|
}) => Promise<ActionResult>,
|
||||||
readonly input: Config<Type, Store>,
|
readonly input: Config<Type, Store>,
|
||||||
@@ -44,11 +38,7 @@ export class CreatedAction<
|
|||||||
Type extends Record<string, any> = ExtractConfigType<ConfigType>,
|
Type extends Record<string, any> = ExtractConfigType<ConfigType>,
|
||||||
>(
|
>(
|
||||||
metaData: MaybeFn<Manifest, Store, Omit<ActionMetadata, "input">>,
|
metaData: MaybeFn<Manifest, Store, Omit<ActionMetadata, "input">>,
|
||||||
fn: (options: {
|
fn: (options: { effects: Effects; input: Type }) => Promise<ActionResult>,
|
||||||
effects: Effects
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
input: Type
|
|
||||||
}) => Promise<ActionResult>,
|
|
||||||
inputConfig: Config<Type, Store> | Config<Type, never>,
|
inputConfig: Config<Type, Store> | Config<Type, never>,
|
||||||
) {
|
) {
|
||||||
return new CreatedAction<Manifest, Store, ConfigType, Type>(
|
return new CreatedAction<Manifest, Store, ConfigType, Type>(
|
||||||
@@ -61,7 +51,6 @@ export class CreatedAction<
|
|||||||
exportedAction: ExportedAction = ({ effects, input }) => {
|
exportedAction: ExportedAction = ({ effects, input }) => {
|
||||||
return this.fn({
|
return this.fn({
|
||||||
effects,
|
effects,
|
||||||
utils: createUtils(effects),
|
|
||||||
input: this.validator.unsafeCast(input),
|
input: this.validator.unsafeCast(input),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -69,21 +58,17 @@ export class CreatedAction<
|
|||||||
run = async ({ effects, input }: { effects: Effects; input?: Type }) => {
|
run = async ({ effects, input }: { effects: Effects; input?: Type }) => {
|
||||||
return this.fn({
|
return this.fn({
|
||||||
effects,
|
effects,
|
||||||
utils: createUtils(effects),
|
|
||||||
input: this.validator.unsafeCast(input),
|
input: this.validator.unsafeCast(input),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async metaData(options: { effects: Effects; utils: Utils<Manifest, Store> }) {
|
async metaData(options: { effects: Effects }) {
|
||||||
if (this.myMetaData instanceof Function)
|
if (this.myMetaData instanceof Function)
|
||||||
return await this.myMetaData(options)
|
return await this.myMetaData(options)
|
||||||
return this.myMetaData
|
return this.myMetaData
|
||||||
}
|
}
|
||||||
|
|
||||||
async ActionMetadata(options: {
|
async ActionMetadata(options: { effects: Effects }): Promise<ActionMetadata> {
|
||||||
effects: Effects
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}): Promise<ActionMetadata> {
|
|
||||||
return {
|
return {
|
||||||
...(await this.metaData(options)),
|
...(await this.metaData(options)),
|
||||||
input: await this.input.build(options),
|
input: await this.input.build(options),
|
||||||
@@ -93,7 +78,6 @@ export class CreatedAction<
|
|||||||
async getConfig({ effects }: { effects: Effects }) {
|
async getConfig({ effects }: { effects: Effects }) {
|
||||||
return this.input.build({
|
return this.input.build({
|
||||||
effects,
|
effects,
|
||||||
utils: createUtils(effects) as any,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import { Effects, ExpectedExports } from "../types"
|
import { Effects, ExpectedExports } from "../types"
|
||||||
import { createUtils } from "../util"
|
|
||||||
import { once } from "../util/once"
|
import { once } from "../util/once"
|
||||||
import { Utils } from "../util/utils"
|
|
||||||
import { CreatedAction } from "./createAction"
|
import { CreatedAction } from "./createAction"
|
||||||
|
|
||||||
export function setupActions<Manifest extends SDKManifest, Store>(
|
export function setupActions<Manifest extends SDKManifest, Store>(
|
||||||
...createdActions: CreatedAction<Manifest, Store, any>[]
|
...createdActions: CreatedAction<Manifest, Store, any>[]
|
||||||
) {
|
) {
|
||||||
const myActions = async (options: {
|
const myActions = async (options: { effects: Effects }) => {
|
||||||
effects: Effects
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => {
|
|
||||||
const actions: Record<string, CreatedAction<Manifest, Store, any>> = {}
|
const actions: Record<string, CreatedAction<Manifest, Store, any>> = {}
|
||||||
for (const action of createdActions) {
|
for (const action of createdActions) {
|
||||||
const actionMetadata = await action.metaData(options)
|
const actionMetadata = await action.metaData(options)
|
||||||
@@ -24,17 +19,11 @@ export function setupActions<Manifest extends SDKManifest, Store>(
|
|||||||
actionsMetadata: ExpectedExports.actionsMetadata
|
actionsMetadata: ExpectedExports.actionsMetadata
|
||||||
} = {
|
} = {
|
||||||
actions(options: { effects: Effects }) {
|
actions(options: { effects: Effects }) {
|
||||||
const utils = createUtils<Manifest, Store>(options.effects)
|
return myActions(options)
|
||||||
|
|
||||||
return myActions({
|
|
||||||
...options,
|
|
||||||
utils,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
async actionsMetadata({ effects }: { effects: Effects }) {
|
async actionsMetadata({ effects }: { effects: Effects }) {
|
||||||
const utils = createUtils<Manifest, Store>(effects)
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
createdActions.map((x) => x.ActionMetadata({ effects, utils })),
|
createdActions.map((x) => x.ActionMetadata({ effects })),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ValueSpec } from "../configTypes"
|
import { ValueSpec } from "../configTypes"
|
||||||
import { Utils } from "../../util/utils"
|
|
||||||
import { Value } from "./value"
|
import { Value } from "./value"
|
||||||
import { _ } from "../../util"
|
import { _ } from "../../util"
|
||||||
import { Effects } from "../../types"
|
import { Effects } from "../../types"
|
||||||
@@ -7,7 +6,6 @@ import { Parser, object } from "ts-matches"
|
|||||||
|
|
||||||
export type LazyBuildOptions<Store> = {
|
export type LazyBuildOptions<Store> = {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<any, Store>
|
|
||||||
}
|
}
|
||||||
export type LazyBuild<Store, ExpectedOut> = (
|
export type LazyBuild<Store, ExpectedOut> = (
|
||||||
options: LazyBuildOptions<Store>,
|
options: LazyBuildOptions<Store>,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { SmtpValue } from "../types"
|
import { SmtpValue } from "../types"
|
||||||
|
import { GetSystemSmtp } from "../util/GetSystemSmtp"
|
||||||
import { email } from "../util/patterns"
|
import { email } from "../util/patterns"
|
||||||
import { Config, ConfigSpecOf } from "./builder/config"
|
import { Config, ConfigSpecOf } from "./builder/config"
|
||||||
import { Value } from "./builder/value"
|
import { Value } from "./builder/value"
|
||||||
@@ -47,8 +48,8 @@ export const customSmtp = Config.of<ConfigSpecOf<SmtpValue>, never>({
|
|||||||
* For service config. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings
|
* For service config. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings
|
||||||
*/
|
*/
|
||||||
export const smtpConfig = Value.filteredUnion(
|
export const smtpConfig = Value.filteredUnion(
|
||||||
async ({ effects, utils }) => {
|
async ({ effects }) => {
|
||||||
const smtp = await utils.getSystemSmtp().once()
|
const smtp = await new GetSystemSmtp(effects).once()
|
||||||
return smtp ? [] : ["system"]
|
return smtp ? [] : ["system"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Effects, ExpectedExports } from "../types"
|
|||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import * as D from "./configDependencies"
|
import * as D from "./configDependencies"
|
||||||
import { Config, ExtractConfigType } from "./builder/config"
|
import { Config, ExtractConfigType } from "./builder/config"
|
||||||
import { Utils, createUtils } from "../util/utils"
|
|
||||||
import nullIfEmpty from "../util/nullIfEmpty"
|
import nullIfEmpty from "../util/nullIfEmpty"
|
||||||
import { InterfaceReceipt } from "../interfaces/interfaceReceipt"
|
import { InterfaceReceipt } from "../interfaces/interfaceReceipt"
|
||||||
import { InterfacesReceipt as InterfacesReceipt } from "../interfaces/setupInterfaces"
|
import { InterfacesReceipt as InterfacesReceipt } from "../interfaces/setupInterfaces"
|
||||||
@@ -22,7 +21,6 @@ export type Save<
|
|||||||
> = (options: {
|
> = (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
input: ExtractConfigType<A> & Record<string, any>
|
input: ExtractConfigType<A> & Record<string, any>
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
dependencies: D.ConfigDependencies<Manifest>
|
dependencies: D.ConfigDependencies<Manifest>
|
||||||
}) => Promise<{
|
}) => Promise<{
|
||||||
dependenciesReceipt: DependenciesReceipt
|
dependenciesReceipt: DependenciesReceipt
|
||||||
@@ -38,7 +36,6 @@ export type Read<
|
|||||||
| Config<Record<string, any>, never>,
|
| Config<Record<string, any>, never>,
|
||||||
> = (options: {
|
> = (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void | (ExtractConfigType<A> & Record<string, any>)>
|
}) => Promise<void | (ExtractConfigType<A> & Record<string, any>)>
|
||||||
/**
|
/**
|
||||||
* We want to setup a config export with a get and set, this
|
* We want to setup a config export with a get and set, this
|
||||||
@@ -72,7 +69,6 @@ export function setupConfig<
|
|||||||
const { restart } = await write({
|
const { restart } = await write({
|
||||||
input: JSON.parse(JSON.stringify(input)),
|
input: JSON.parse(JSON.stringify(input)),
|
||||||
effects,
|
effects,
|
||||||
utils: createUtils(effects),
|
|
||||||
dependencies: D.configDependenciesSet<Manifest>(),
|
dependencies: D.configDependenciesSet<Manifest>(),
|
||||||
})
|
})
|
||||||
if (restart) {
|
if (restart) {
|
||||||
@@ -80,14 +76,10 @@ export function setupConfig<
|
|||||||
}
|
}
|
||||||
}) as ExpectedExports.setConfig,
|
}) as ExpectedExports.setConfig,
|
||||||
getConfig: (async ({ effects }) => {
|
getConfig: (async ({ effects }) => {
|
||||||
const myUtils = createUtils<Manifest, Store>(effects)
|
const configValue = nullIfEmpty((await read({ effects })) || null)
|
||||||
const configValue = nullIfEmpty(
|
|
||||||
(await read({ effects, utils: myUtils })) || null,
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
spec: await spec.build({
|
spec: await spec.build({
|
||||||
effects,
|
effects,
|
||||||
utils: myUtils as any,
|
|
||||||
}),
|
}),
|
||||||
config: configValue,
|
config: configValue,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
DeepPartial,
|
DeepPartial,
|
||||||
Effects,
|
Effects,
|
||||||
} from "../types"
|
} from "../types"
|
||||||
import { Utils, createUtils } from "../util/utils"
|
|
||||||
import { deepEqual } from "../util/deepEqual"
|
import { deepEqual } from "../util/deepEqual"
|
||||||
import { deepMerge } from "../util/deepMerge"
|
import { deepMerge } from "../util/deepMerge"
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
@@ -29,7 +28,6 @@ export class DependencyConfig<
|
|||||||
readonly dependencyConfig: (options: {
|
readonly dependencyConfig: (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
localConfig: Input
|
localConfig: Input
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void | DeepPartial<RemoteConfig>>,
|
}) => Promise<void | DeepPartial<RemoteConfig>>,
|
||||||
readonly update: Update<
|
readonly update: Update<
|
||||||
void | DeepPartial<RemoteConfig>,
|
void | DeepPartial<RemoteConfig>,
|
||||||
@@ -41,7 +39,6 @@ export class DependencyConfig<
|
|||||||
return this.dependencyConfig({
|
return this.dependencyConfig({
|
||||||
localConfig: options.localConfig as Input,
|
localConfig: options.localConfig as Input,
|
||||||
effects: options.effects,
|
effects: options.effects,
|
||||||
utils: createUtils<Manifest, Store>(options.effects),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,52 +6,59 @@ import { Trigger } from "../trigger"
|
|||||||
import { TriggerInput } from "../trigger/TriggerInput"
|
import { TriggerInput } from "../trigger/TriggerInput"
|
||||||
import { defaultTrigger } from "../trigger/defaultTrigger"
|
import { defaultTrigger } from "../trigger/defaultTrigger"
|
||||||
import { once } from "../util/once"
|
import { once } from "../util/once"
|
||||||
|
import { Overlay } from "../util/Overlay"
|
||||||
|
|
||||||
export function healthCheck(o: {
|
export function healthCheck(o: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
name: string
|
name: string
|
||||||
|
imageId: string
|
||||||
trigger?: Trigger
|
trigger?: Trigger
|
||||||
fn(): Promise<CheckResult> | CheckResult
|
fn(overlay: Overlay): Promise<CheckResult> | CheckResult
|
||||||
onFirstSuccess?: () => unknown | Promise<unknown>
|
onFirstSuccess?: () => unknown | Promise<unknown>
|
||||||
}) {
|
}) {
|
||||||
new Promise(async () => {
|
new Promise(async () => {
|
||||||
let currentValue: TriggerInput = {
|
const overlay = await Overlay.of(o.effects, o.imageId)
|
||||||
hadSuccess: false,
|
try {
|
||||||
}
|
let currentValue: TriggerInput = {
|
||||||
const getCurrentValue = () => currentValue
|
hadSuccess: false,
|
||||||
const trigger = (o.trigger ?? defaultTrigger)(getCurrentValue)
|
|
||||||
const triggerFirstSuccess = once(() =>
|
|
||||||
Promise.resolve(
|
|
||||||
"onFirstSuccess" in o && o.onFirstSuccess
|
|
||||||
? o.onFirstSuccess()
|
|
||||||
: undefined,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
for (
|
|
||||||
let res = await trigger.next();
|
|
||||||
!res.done;
|
|
||||||
res = await trigger.next()
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const { status, message } = await o.fn()
|
|
||||||
await o.effects.setHealth({
|
|
||||||
name: o.name,
|
|
||||||
status,
|
|
||||||
message,
|
|
||||||
})
|
|
||||||
currentValue.hadSuccess = true
|
|
||||||
currentValue.lastResult = "passing"
|
|
||||||
await triggerFirstSuccess().catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
await o.effects.setHealth({
|
|
||||||
name: o.name,
|
|
||||||
status: "failure",
|
|
||||||
message: asMessage(e),
|
|
||||||
})
|
|
||||||
currentValue.lastResult = "failure"
|
|
||||||
}
|
}
|
||||||
|
const getCurrentValue = () => currentValue
|
||||||
|
const trigger = (o.trigger ?? defaultTrigger)(getCurrentValue)
|
||||||
|
const triggerFirstSuccess = once(() =>
|
||||||
|
Promise.resolve(
|
||||||
|
"onFirstSuccess" in o && o.onFirstSuccess
|
||||||
|
? o.onFirstSuccess()
|
||||||
|
: undefined,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for (
|
||||||
|
let res = await trigger.next();
|
||||||
|
!res.done;
|
||||||
|
res = await trigger.next()
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const { status, message } = await o.fn(overlay)
|
||||||
|
await o.effects.setHealth({
|
||||||
|
name: o.name,
|
||||||
|
status,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
currentValue.hadSuccess = true
|
||||||
|
currentValue.lastResult = "passing"
|
||||||
|
await triggerFirstSuccess().catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
await o.effects.setHealth({
|
||||||
|
name: o.name,
|
||||||
|
status: "failure",
|
||||||
|
message: asMessage(e),
|
||||||
|
})
|
||||||
|
currentValue.lastResult = "failure"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await overlay.destroy()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return {} as HealthReceipt
|
return {} as HealthReceipt
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { Effects } from "../../types"
|
import { Effects } from "../../types"
|
||||||
import { createUtils } from "../../util"
|
|
||||||
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
|
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
|
||||||
import { CheckResult } from "./CheckResult"
|
import { CheckResult } from "./CheckResult"
|
||||||
|
|
||||||
|
import { promisify } from "node:util"
|
||||||
|
import * as CP from "node:child_process"
|
||||||
|
|
||||||
|
const cpExec = promisify(CP.exec)
|
||||||
|
const cpExecFile = promisify(CP.execFile)
|
||||||
export function containsAddress(x: string, port: number) {
|
export function containsAddress(x: string, port: number) {
|
||||||
const readPorts = x
|
const readPorts = x
|
||||||
.split("\n")
|
.split("\n")
|
||||||
@@ -28,20 +33,15 @@ export async function checkPortListening(
|
|||||||
timeout?: number
|
timeout?: number
|
||||||
},
|
},
|
||||||
): Promise<CheckResult> {
|
): Promise<CheckResult> {
|
||||||
const utils = createUtils(effects)
|
|
||||||
return Promise.race<CheckResult>([
|
return Promise.race<CheckResult>([
|
||||||
Promise.resolve().then(async () => {
|
Promise.resolve().then(async () => {
|
||||||
const hasAddress =
|
const hasAddress =
|
||||||
containsAddress(
|
containsAddress(
|
||||||
await utils.childProcess
|
await cpExec(`cat /proc/net/tcp`, {}).then(stringFromStdErrOut),
|
||||||
.exec(`cat /proc/net/tcp`, {})
|
|
||||||
.then(stringFromStdErrOut),
|
|
||||||
port,
|
port,
|
||||||
) ||
|
) ||
|
||||||
containsAddress(
|
containsAddress(
|
||||||
await utils.childProcess
|
await cpExec("cat /proc/net/udp", {}).then(stringFromStdErrOut),
|
||||||
.exec("cat /proc/net/udp", {})
|
|
||||||
.then(stringFromStdErrOut),
|
|
||||||
port,
|
port,
|
||||||
)
|
)
|
||||||
if (hasAddress) {
|
if (hasAddress) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CommandType, Effects } from "../../types"
|
import { Effects } from "../../types"
|
||||||
import { createUtils } from "../../util"
|
import { Overlay } from "../../util/Overlay"
|
||||||
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
|
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
|
||||||
import { CheckResult } from "./CheckResult"
|
import { CheckResult } from "./CheckResult"
|
||||||
import { timeoutPromise } from "./index"
|
import { timeoutPromise } from "./index"
|
||||||
@@ -13,7 +13,8 @@ import { timeoutPromise } from "./index"
|
|||||||
*/
|
*/
|
||||||
export const runHealthScript = async (
|
export const runHealthScript = async (
|
||||||
effects: Effects,
|
effects: Effects,
|
||||||
runCommand: string,
|
runCommand: string[],
|
||||||
|
overlay: Overlay,
|
||||||
{
|
{
|
||||||
timeout = 30000,
|
timeout = 30000,
|
||||||
errorMessage = `Error while running command: ${runCommand}`,
|
errorMessage = `Error while running command: ${runCommand}`,
|
||||||
@@ -21,9 +22,8 @@ export const runHealthScript = async (
|
|||||||
`Have ran script ${runCommand} and the result: ${res}`,
|
`Have ran script ${runCommand} and the result: ${res}`,
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<CheckResult> => {
|
): Promise<CheckResult> => {
|
||||||
const utils = createUtils(effects)
|
|
||||||
const res = await Promise.race([
|
const res = await Promise.race([
|
||||||
utils.childProcess.exec(runCommand, { timeout }).then(stringFromStdErrOut),
|
overlay.exec(runCommand),
|
||||||
timeoutPromise(timeout),
|
timeoutPromise(timeout),
|
||||||
]).catch((e) => {
|
]).catch((e) => {
|
||||||
console.warn(errorMessage)
|
console.warn(errorMessage)
|
||||||
@@ -33,6 +33,6 @@ export const runHealthScript = async (
|
|||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
status: "passing",
|
status: "passing",
|
||||||
message: message(res),
|
message: message(res.stdout.toString()),
|
||||||
} as CheckResult
|
} as CheckResult
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
export { Daemons } from "./mainFn/Daemons"
|
export { Daemons } from "./mainFn/Daemons"
|
||||||
export { EmVer } from "./emverLite/mod"
|
export { EmVer } from "./emverLite/mod"
|
||||||
export { Overlay } from "./util/Overlay"
|
export { Overlay } from "./util/Overlay"
|
||||||
export { Utils } from "./util/utils"
|
|
||||||
export { StartSdk } from "./StartSdk"
|
export { StartSdk } from "./StartSdk"
|
||||||
export { setupManifest } from "./manifest/setupManifest"
|
export { setupManifest } from "./manifest/setupManifest"
|
||||||
export { FileHelper } from "./util/fileHelper"
|
export { FileHelper } from "./util/fileHelper"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ManifestVersion, SDKManifest } from "../../manifest/ManifestTypes"
|
import { ManifestVersion, SDKManifest } from "../../manifest/ManifestTypes"
|
||||||
import { Effects } from "../../types"
|
import { Effects } from "../../types"
|
||||||
import { Utils } from "../../util/utils"
|
|
||||||
|
|
||||||
export class Migration<
|
export class Migration<
|
||||||
Manifest extends SDKManifest,
|
Manifest extends SDKManifest,
|
||||||
@@ -10,14 +9,8 @@ export class Migration<
|
|||||||
constructor(
|
constructor(
|
||||||
readonly options: {
|
readonly options: {
|
||||||
version: Version
|
version: Version
|
||||||
up: (opts: {
|
up: (opts: { effects: Effects }) => Promise<void>
|
||||||
effects: Effects
|
down: (opts: { effects: Effects }) => Promise<void>
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
|
||||||
down: (opts: {
|
|
||||||
effects: Effects
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
|
||||||
},
|
},
|
||||||
) {}
|
) {}
|
||||||
static of<
|
static of<
|
||||||
@@ -26,23 +19,17 @@ export class Migration<
|
|||||||
Version extends ManifestVersion,
|
Version extends ManifestVersion,
|
||||||
>(options: {
|
>(options: {
|
||||||
version: Version
|
version: Version
|
||||||
up: (opts: {
|
up: (opts: { effects: Effects }) => Promise<void>
|
||||||
effects: Effects
|
down: (opts: { effects: Effects }) => Promise<void>
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
|
||||||
down: (opts: {
|
|
||||||
effects: Effects
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
|
||||||
}) {
|
}) {
|
||||||
return new Migration<Manifest, Store, Version>(options)
|
return new Migration<Manifest, Store, Version>(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
async up(opts: { effects: Effects; utils: Utils<Manifest, Store> }) {
|
async up(opts: { effects: Effects }) {
|
||||||
this.up(opts)
|
this.up(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
async down(opts: { effects: Effects; utils: Utils<Manifest, Store> }) {
|
async down(opts: { effects: Effects }) {
|
||||||
this.down(opts)
|
this.down(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { EmVer } from "../../emverLite/mod"
|
import { EmVer } from "../../emverLite/mod"
|
||||||
import { SDKManifest } from "../../manifest/ManifestTypes"
|
import { SDKManifest } from "../../manifest/ManifestTypes"
|
||||||
import { ExpectedExports } from "../../types"
|
import { ExpectedExports } from "../../types"
|
||||||
import { createUtils } from "../../util"
|
|
||||||
import { once } from "../../util/once"
|
import { once } from "../../util/once"
|
||||||
import { Migration } from "./Migration"
|
import { Migration } from "./Migration"
|
||||||
|
|
||||||
@@ -32,13 +31,12 @@ export class Migrations<Manifest extends SDKManifest, Store> {
|
|||||||
effects,
|
effects,
|
||||||
previousVersion,
|
previousVersion,
|
||||||
}: Parameters<ExpectedExports.init>[0]) {
|
}: Parameters<ExpectedExports.init>[0]) {
|
||||||
const utils = createUtils<Manifest, Store>(effects)
|
|
||||||
if (!!previousVersion) {
|
if (!!previousVersion) {
|
||||||
const previousVersionEmVer = EmVer.parse(previousVersion)
|
const previousVersionEmVer = EmVer.parse(previousVersion)
|
||||||
for (const [_, migration] of this.sortedMigrations()
|
for (const [_, migration] of this.sortedMigrations()
|
||||||
.filter((x) => x[0].greaterThan(previousVersionEmVer))
|
.filter((x) => x[0].greaterThan(previousVersionEmVer))
|
||||||
.filter((x) => x[0].lessThanOrEqual(this.currentVersion()))) {
|
.filter((x) => x[0].lessThanOrEqual(this.currentVersion()))) {
|
||||||
await migration.up({ effects, utils })
|
await migration.up({ effects })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,14 +44,13 @@ export class Migrations<Manifest extends SDKManifest, Store> {
|
|||||||
effects,
|
effects,
|
||||||
nextVersion,
|
nextVersion,
|
||||||
}: Parameters<ExpectedExports.uninit>[0]) {
|
}: Parameters<ExpectedExports.uninit>[0]) {
|
||||||
const utils = createUtils<Manifest, Store>(effects)
|
|
||||||
if (!!nextVersion) {
|
if (!!nextVersion) {
|
||||||
const nextVersionEmVer = EmVer.parse(nextVersion)
|
const nextVersionEmVer = EmVer.parse(nextVersion)
|
||||||
const reversed = [...this.sortedMigrations()].reverse()
|
const reversed = [...this.sortedMigrations()].reverse()
|
||||||
for (const [_, migration] of reversed
|
for (const [_, migration] of reversed
|
||||||
.filter((x) => x[0].greaterThan(nextVersionEmVer))
|
.filter((x) => x[0].greaterThan(nextVersionEmVer))
|
||||||
.filter((x) => x[0].lessThanOrEqual(this.currentVersion()))) {
|
.filter((x) => x[0].lessThanOrEqual(this.currentVersion()))) {
|
||||||
await migration.down({ effects, utils })
|
await migration.down({ effects })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { Effects, ExposeServicePaths, ExposeUiPaths } from "../types"
|
import { Effects, ExposeServicePaths, ExposeUiPaths } from "../types"
|
||||||
import { Utils } from "../util/utils"
|
|
||||||
|
|
||||||
export type SetupExports<Store> = (opts: {
|
export type SetupExports<Store> = (opts: { effects: Effects }) =>
|
||||||
effects: Effects
|
|
||||||
utils: Utils<any, Store>
|
|
||||||
}) =>
|
|
||||||
| {
|
| {
|
||||||
ui: { [k: string]: ExposeUiPaths<Store> }
|
ui: { [k: string]: ExposeUiPaths<Store> }
|
||||||
services: ExposeServicePaths<Store>
|
services: ExposeServicePaths<Store>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { SetInterfaces } from "../interfaces/setupInterfaces"
|
import { SetInterfaces } from "../interfaces/setupInterfaces"
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import { ExpectedExports, ExposeUiPaths, ExposeUiPathsAll } from "../types"
|
import { ExpectedExports, ExposeUiPaths, ExposeUiPathsAll } from "../types"
|
||||||
import { createUtils } from "../util"
|
|
||||||
import { Migrations } from "./migrations/setupMigrations"
|
import { Migrations } from "./migrations/setupMigrations"
|
||||||
import { SetupExports } from "./setupExports"
|
import { SetupExports } from "./setupExports"
|
||||||
import { Install } from "./setupInstall"
|
import { Install } from "./setupInstall"
|
||||||
@@ -19,18 +18,13 @@ export function setupInit<Manifest extends SDKManifest, Store>(
|
|||||||
} {
|
} {
|
||||||
return {
|
return {
|
||||||
init: async (opts) => {
|
init: async (opts) => {
|
||||||
const utils = createUtils<Manifest, Store>(opts.effects)
|
|
||||||
await migrations.init(opts)
|
await migrations.init(opts)
|
||||||
await install.init(opts)
|
await install.init(opts)
|
||||||
await setInterfaces({
|
await setInterfaces({
|
||||||
...opts,
|
...opts,
|
||||||
input: null,
|
input: null,
|
||||||
utils,
|
|
||||||
})
|
|
||||||
const { services, ui } = await setupExports({
|
|
||||||
...opts,
|
|
||||||
utils,
|
|
||||||
})
|
})
|
||||||
|
const { services, ui } = await setupExports(opts)
|
||||||
await opts.effects.exposeForDependents(services)
|
await opts.effects.exposeForDependents(services)
|
||||||
await opts.effects.exposeUi(
|
await opts.effects.exposeUi(
|
||||||
forExpose({
|
forExpose({
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import { Effects, ExpectedExports } from "../types"
|
import { Effects, ExpectedExports } from "../types"
|
||||||
import { Utils, createUtils } from "../util/utils"
|
|
||||||
|
|
||||||
export type InstallFn<Manifest extends SDKManifest, Store> = (opts: {
|
export type InstallFn<Manifest extends SDKManifest, Store> = (opts: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
}) => Promise<void>
|
||||||
export class Install<Manifest extends SDKManifest, Store> {
|
export class Install<Manifest extends SDKManifest, Store> {
|
||||||
private constructor(readonly fn: InstallFn<Manifest, Store>) {}
|
private constructor(readonly fn: InstallFn<Manifest, Store>) {}
|
||||||
@@ -21,7 +19,6 @@ export class Install<Manifest extends SDKManifest, Store> {
|
|||||||
if (!previousVersion)
|
if (!previousVersion)
|
||||||
await this.fn({
|
await this.fn({
|
||||||
effects,
|
effects,
|
||||||
utils: createUtils(effects),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import { Effects, ExpectedExports } from "../types"
|
import { Effects, ExpectedExports } from "../types"
|
||||||
import { Utils, createUtils } from "../util/utils"
|
|
||||||
|
|
||||||
export type UninstallFn<Manifest extends SDKManifest, Store> = (opts: {
|
export type UninstallFn<Manifest extends SDKManifest, Store> = (opts: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<void>
|
}) => Promise<void>
|
||||||
export class Uninstall<Manifest extends SDKManifest, Store> {
|
export class Uninstall<Manifest extends SDKManifest, Store> {
|
||||||
private constructor(readonly fn: UninstallFn<Manifest, Store>) {}
|
private constructor(readonly fn: UninstallFn<Manifest, Store>) {}
|
||||||
@@ -21,7 +19,6 @@ export class Uninstall<Manifest extends SDKManifest, Store> {
|
|||||||
if (!nextVersion)
|
if (!nextVersion)
|
||||||
await this.fn({
|
await this.fn({
|
||||||
effects,
|
effects,
|
||||||
utils: createUtils(effects),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { ServiceInterfaceType } from "../StartSdk"
|
||||||
import { Effects } from "../types"
|
import { Effects } from "../types"
|
||||||
import { ServiceInterfaceType } from "../util/utils"
|
|
||||||
import { Scheme } from "./Host"
|
import { Scheme } from "./Host"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Config } from "../config/builder/config"
|
import { Config } from "../config/builder/config"
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import { AddressInfo, Effects } from "../types"
|
import { AddressInfo, Effects } from "../types"
|
||||||
import { Utils } from "../util/utils"
|
|
||||||
import { AddressReceipt } from "./AddressReceipt"
|
import { AddressReceipt } from "./AddressReceipt"
|
||||||
|
|
||||||
export type InterfacesReceipt = Array<AddressInfo[] & AddressReceipt>
|
export type InterfacesReceipt = Array<AddressInfo[] & AddressReceipt>
|
||||||
@@ -10,11 +9,7 @@ export type SetInterfaces<
|
|||||||
Store,
|
Store,
|
||||||
ConfigInput extends Record<string, any>,
|
ConfigInput extends Record<string, any>,
|
||||||
Output extends InterfacesReceipt,
|
Output extends InterfacesReceipt,
|
||||||
> = (opts: {
|
> = (opts: { effects: Effects; input: null | ConfigInput }) => Promise<Output>
|
||||||
effects: Effects
|
|
||||||
input: null | ConfigInput
|
|
||||||
utils: Utils<Manifest, Store>
|
|
||||||
}) => Promise<Output>
|
|
||||||
export type SetupInterfaces = <
|
export type SetupInterfaces = <
|
||||||
Manifest extends SDKManifest,
|
Manifest extends SDKManifest,
|
||||||
Store,
|
Store,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { NO_TIMEOUT, SIGKILL, SIGTERM, Signals } from "../StartSdk"
|
||||||
import { HealthReceipt } from "../health/HealthReceipt"
|
import { HealthReceipt } from "../health/HealthReceipt"
|
||||||
import { CheckResult } from "../health/checkFns"
|
import { CheckResult } from "../health/checkFns"
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
@@ -5,8 +6,21 @@ import { Trigger } from "../trigger"
|
|||||||
import { TriggerInput } from "../trigger/TriggerInput"
|
import { TriggerInput } from "../trigger/TriggerInput"
|
||||||
import { defaultTrigger } from "../trigger/defaultTrigger"
|
import { defaultTrigger } from "../trigger/defaultTrigger"
|
||||||
import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types"
|
import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types"
|
||||||
import { createUtils } from "../util"
|
import { CommandOptions, MountOptions, Overlay } from "../util/Overlay"
|
||||||
import { Signals } from "../util/utils"
|
import { splitCommand } from "../util/splitCommand"
|
||||||
|
|
||||||
|
import { promisify } from "node:util"
|
||||||
|
import * as CP from "node:child_process"
|
||||||
|
|
||||||
|
const cpExec = promisify(CP.exec)
|
||||||
|
const cpExecFile = promisify(CP.execFile)
|
||||||
|
async function psTree(pid: number, overlay: Overlay): Promise<number[]> {
|
||||||
|
const { stdout } = await cpExec(`pstree -p ${pid}`)
|
||||||
|
const regex: RegExp = /\((\d+)\)/g
|
||||||
|
return [...stdout.toString().matchAll(regex)].map(([_all, pid]) =>
|
||||||
|
parseInt(pid),
|
||||||
|
)
|
||||||
|
}
|
||||||
type Daemon<
|
type Daemon<
|
||||||
Manifest extends SDKManifest,
|
Manifest extends SDKManifest,
|
||||||
Ids extends string,
|
Ids extends string,
|
||||||
@@ -26,6 +40,89 @@ type Daemon<
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used`
|
type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used`
|
||||||
|
|
||||||
|
const runDaemon =
|
||||||
|
<Manifest extends SDKManifest>() =>
|
||||||
|
async <A extends string>(
|
||||||
|
effects: Effects,
|
||||||
|
imageId: Manifest["images"][number],
|
||||||
|
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||||
|
options: CommandOptions & {
|
||||||
|
mounts?: { path: string; options: MountOptions }[]
|
||||||
|
overlay?: Overlay
|
||||||
|
},
|
||||||
|
): Promise<DaemonReturned> => {
|
||||||
|
const commands = splitCommand(command)
|
||||||
|
const overlay = options.overlay || (await Overlay.of(effects, imageId))
|
||||||
|
for (let mount of options.mounts || []) {
|
||||||
|
await overlay.mount(mount.options, mount.path)
|
||||||
|
}
|
||||||
|
const childProcess = await overlay.spawn(commands, {
|
||||||
|
env: options.env,
|
||||||
|
})
|
||||||
|
const answer = new Promise<null>((resolve, reject) => {
|
||||||
|
childProcess.stdout.on("data", (data: any) => {
|
||||||
|
console.log(data.toString())
|
||||||
|
})
|
||||||
|
childProcess.stderr.on("data", (data: any) => {
|
||||||
|
console.error(data.toString())
|
||||||
|
})
|
||||||
|
|
||||||
|
childProcess.on("exit", (code: any) => {
|
||||||
|
if (code === 0) {
|
||||||
|
return resolve(null)
|
||||||
|
}
|
||||||
|
return reject(new Error(`${commands[0]} exited with code ${code}`))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const pid = childProcess.pid
|
||||||
|
return {
|
||||||
|
async wait() {
|
||||||
|
const pids = pid ? await psTree(pid, overlay) : []
|
||||||
|
try {
|
||||||
|
return await answer
|
||||||
|
} finally {
|
||||||
|
for (const process of pids) {
|
||||||
|
cpExecFile("kill", [`-9`, String(process)]).catch((_) => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) {
|
||||||
|
const pids = pid ? await psTree(pid, overlay) : []
|
||||||
|
try {
|
||||||
|
childProcess.kill(signal)
|
||||||
|
|
||||||
|
if (timeout > NO_TIMEOUT) {
|
||||||
|
const didTimeout = await Promise.race([
|
||||||
|
new Promise((resolve) => setTimeout(resolve, timeout)).then(
|
||||||
|
() => true,
|
||||||
|
),
|
||||||
|
answer.then(() => false),
|
||||||
|
])
|
||||||
|
if (didTimeout) {
|
||||||
|
childProcess.kill(SIGKILL)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await answer
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await overlay.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const process of pids) {
|
||||||
|
await cpExecFile("kill", [`-${signal}`, String(process)])
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
for (const process of pids) {
|
||||||
|
cpExecFile("kill", [`-9`, String(process)]).catch((_) => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for defining and controlling the service daemons
|
* A class for defining and controlling the service daemons
|
||||||
```ts
|
```ts
|
||||||
@@ -104,9 +201,10 @@ export class Daemons<Manifest extends SDKManifest, Ids extends string> {
|
|||||||
)
|
)
|
||||||
daemonsStarted[daemon.id] = requiredPromise.then(async () => {
|
daemonsStarted[daemon.id] = requiredPromise.then(async () => {
|
||||||
const { command, imageId } = daemon
|
const { command, imageId } = daemon
|
||||||
const utils = createUtils<Manifest>(effects)
|
|
||||||
|
|
||||||
const child = utils.runDaemon(imageId, command, { env: daemon.env })
|
const child = runDaemon<Manifest>()(effects, imageId, command, {
|
||||||
|
env: daemon.env,
|
||||||
|
})
|
||||||
let currentInput: TriggerInput = {}
|
let currentInput: TriggerInput = {}
|
||||||
const getCurrentInput = () => currentInput
|
const getCurrentInput = () => currentInput
|
||||||
const trigger = (daemon.ready.trigger ?? defaultTrigger)(
|
const trigger = (daemon.ready.trigger ?? defaultTrigger)(
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Effects, ExpectedExports } from "../types"
|
import { ExpectedExports } from "../types"
|
||||||
import { createMainUtils } from "../util"
|
|
||||||
import { Utils, createUtils } from "../util/utils"
|
|
||||||
import { Daemons } from "./Daemons"
|
import { Daemons } from "./Daemons"
|
||||||
import "../interfaces/ServiceInterfaceBuilder"
|
import "../interfaces/ServiceInterfaceBuilder"
|
||||||
import "../interfaces/Origin"
|
import "../interfaces/Origin"
|
||||||
|
|
||||||
import "./Daemons"
|
import "./Daemons"
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
|
import { MainEffects } from "../StartSdk"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to ensure that the main function is running with the valid proofs.
|
* Used to ensure that the main function is running with the valid proofs.
|
||||||
@@ -20,16 +19,12 @@ import { SDKManifest } from "../manifest/ManifestTypes"
|
|||||||
*/
|
*/
|
||||||
export const setupMain = <Manifest extends SDKManifest, Store>(
|
export const setupMain = <Manifest extends SDKManifest, Store>(
|
||||||
fn: (o: {
|
fn: (o: {
|
||||||
effects: Effects
|
effects: MainEffects
|
||||||
started(onTerm: () => PromiseLike<void>): PromiseLike<void>
|
started(onTerm: () => PromiseLike<void>): PromiseLike<void>
|
||||||
utils: Utils<Manifest, Store, {}>
|
|
||||||
}) => Promise<Daemons<Manifest, any>>,
|
}) => Promise<Daemons<Manifest, any>>,
|
||||||
): ExpectedExports.main => {
|
): ExpectedExports.main => {
|
||||||
return async (options) => {
|
return async (options) => {
|
||||||
const result = await fn({
|
const result = await fn(options)
|
||||||
...options,
|
|
||||||
utils: createMainUtils<Manifest, Store>(options.effects),
|
|
||||||
})
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { List } from "../config/builder/list"
|
|||||||
import { Value } from "../config/builder/value"
|
import { Value } from "../config/builder/value"
|
||||||
import { Variants } from "../config/builder/variants"
|
import { Variants } from "../config/builder/variants"
|
||||||
import { ValueSpec } from "../config/configTypes"
|
import { ValueSpec } from "../config/configTypes"
|
||||||
|
import { setupManifest } from "../manifest/setupManifest"
|
||||||
|
import { StartSdk } from "../StartSdk"
|
||||||
|
|
||||||
describe("builder tests", () => {
|
describe("builder tests", () => {
|
||||||
test("text", async () => {
|
test("text", async () => {
|
||||||
@@ -379,17 +381,61 @@ describe("values", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
test("datetime", async () => {
|
test("datetime", async () => {
|
||||||
const value = Value.dynamicDatetime<{ test: "a" }>(async ({ utils }) => {
|
const sdk = StartSdk.of()
|
||||||
;async () => {
|
.withManifest(
|
||||||
;(await utils.store.getOwn("/test").once()) satisfies "a"
|
setupManifest({
|
||||||
}
|
id: "testOutput",
|
||||||
|
title: "",
|
||||||
|
version: "1.0",
|
||||||
|
releaseNotes: "",
|
||||||
|
license: "",
|
||||||
|
replaces: [],
|
||||||
|
wrapperRepo: "",
|
||||||
|
upstreamRepo: "",
|
||||||
|
supportSite: "",
|
||||||
|
marketingSite: "",
|
||||||
|
donationUrl: null,
|
||||||
|
description: {
|
||||||
|
short: "",
|
||||||
|
long: "",
|
||||||
|
},
|
||||||
|
containers: {},
|
||||||
|
images: [],
|
||||||
|
volumes: [],
|
||||||
|
assets: [],
|
||||||
|
alerts: {
|
||||||
|
install: null,
|
||||||
|
update: null,
|
||||||
|
uninstall: null,
|
||||||
|
restore: null,
|
||||||
|
start: null,
|
||||||
|
stop: null,
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
remoteTest: {
|
||||||
|
description: "",
|
||||||
|
requirement: { how: "", type: "opt-in" },
|
||||||
|
version: "1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.withStore<{ test: "a" }>()
|
||||||
|
.build(true)
|
||||||
|
|
||||||
return {
|
const value = Value.dynamicDatetime<{ test: "a" }>(
|
||||||
name: "Testing",
|
async ({ effects }) => {
|
||||||
required: { default: null },
|
;async () => {
|
||||||
inputmode: "date",
|
;(await sdk.store.getOwn(effects, "/test").once()) satisfies "a"
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
return {
|
||||||
|
name: "Testing",
|
||||||
|
required: { default: null },
|
||||||
|
inputmode: "date",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
validator.unsafeCast("2021-01-01")
|
validator.unsafeCast("2021-01-01")
|
||||||
validator.unsafeCast(null)
|
validator.unsafeCast(null)
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { ServiceInterfaceBuilder } from "../interfaces/ServiceInterfaceBuilder"
|
import { ServiceInterfaceBuilder } from "../interfaces/ServiceInterfaceBuilder"
|
||||||
import { Effects } from "../types"
|
import { Effects } from "../types"
|
||||||
import { createUtils } from "../util"
|
import { sdk } from "./output.sdk"
|
||||||
|
|
||||||
describe("host", () => {
|
describe("host", () => {
|
||||||
test("Testing that the types work", () => {
|
test("Testing that the types work", () => {
|
||||||
async function test(effects: Effects) {
|
async function test(effects: Effects) {
|
||||||
const utils = createUtils<never, never>(effects)
|
const foo = sdk.host.multi(effects, "foo")
|
||||||
const foo = utils.host.multi("foo")
|
|
||||||
const fooOrigin = await foo.bindPort(80, {
|
const fooOrigin = await foo.bindPort(80, {
|
||||||
protocol: "http" as const,
|
protocol: "http" as const,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
|
import { MainEffects, StartSdk } from "../StartSdk"
|
||||||
import { Effects } from "../types"
|
import { Effects } from "../types"
|
||||||
import { createMainUtils } from "../util"
|
|
||||||
import { createUtils } from "../util/utils"
|
|
||||||
|
|
||||||
type Store = {
|
type Store = {
|
||||||
config: {
|
config: {
|
||||||
@@ -12,26 +11,31 @@ const todo = <A>(): A => {
|
|||||||
throw new Error("not implemented")
|
throw new Error("not implemented")
|
||||||
}
|
}
|
||||||
const noop = () => {}
|
const noop = () => {}
|
||||||
|
|
||||||
|
const sdk = StartSdk.of()
|
||||||
|
.withManifest({} as Manifest)
|
||||||
|
.withStore<Store>()
|
||||||
|
.build(true)
|
||||||
|
|
||||||
describe("Store", () => {
|
describe("Store", () => {
|
||||||
test("types", async () => {
|
test("types", async () => {
|
||||||
;async () => {
|
;async () => {
|
||||||
createUtils<Manifest, Store>(todo<Effects>()).store.setOwn("/config", {
|
sdk.store.setOwn(todo<Effects>(), "/config", {
|
||||||
someValue: "a",
|
someValue: "a",
|
||||||
})
|
})
|
||||||
createUtils<Manifest, Store>(todo<Effects>()).store.setOwn(
|
sdk.store.setOwn(todo<Effects>(), "/config/someValue", "b")
|
||||||
"/config/someValue",
|
sdk.store.setOwn(todo<Effects>(), "", {
|
||||||
"b",
|
|
||||||
)
|
|
||||||
createUtils<Manifest, Store>(todo<Effects>()).store.setOwn("", {
|
|
||||||
config: { someValue: "b" },
|
config: { someValue: "b" },
|
||||||
})
|
})
|
||||||
createUtils<Manifest, Store>(todo<Effects>()).store.setOwn(
|
sdk.store.setOwn(
|
||||||
|
todo<Effects>(),
|
||||||
"/config/someValue",
|
"/config/someValue",
|
||||||
|
|
||||||
// @ts-expect-error Type is wrong for the setting value
|
// @ts-expect-error Type is wrong for the setting value
|
||||||
5,
|
5,
|
||||||
)
|
)
|
||||||
createUtils(todo<Effects>()).store.setOwn(
|
sdk.store.setOwn(
|
||||||
|
todo<Effects>(),
|
||||||
// @ts-expect-error Path is wrong
|
// @ts-expect-error Path is wrong
|
||||||
"/config/someVae3lue",
|
"/config/someVae3lue",
|
||||||
"someValue",
|
"someValue",
|
||||||
@@ -52,49 +56,47 @@ describe("Store", () => {
|
|||||||
path: "/config/some2Value",
|
path: "/config/some2Value",
|
||||||
value: "a",
|
value: "a",
|
||||||
})
|
})
|
||||||
;(await createMainUtils<Manifest, Store>(todo<Effects>())
|
;(await sdk.store
|
||||||
.store.getOwn("/config/someValue")
|
.getOwn(todo<MainEffects>(), "/config/someValue")
|
||||||
.const()) satisfies string
|
.const()) satisfies string
|
||||||
;(await createMainUtils<Manifest, Store>(todo<Effects>())
|
;(await sdk.store
|
||||||
.store.getOwn("/config")
|
.getOwn(todo<MainEffects>(), "/config")
|
||||||
.const()) satisfies Store["config"]
|
.const()) satisfies Store["config"]
|
||||||
await createMainUtils(todo<Effects>())
|
await sdk.store // @ts-expect-error Path is wrong
|
||||||
// @ts-expect-error Path is wrong
|
.getOwn(todo<MainEffects>(), "/config/somdsfeValue")
|
||||||
.store.getOwn("/config/somdsfeValue")
|
|
||||||
.const()
|
.const()
|
||||||
/// ----------------- ERRORS -----------------
|
/// ----------------- ERRORS -----------------
|
||||||
|
|
||||||
createUtils<Manifest, Store>(todo<Effects>()).store.setOwn("", {
|
sdk.store.setOwn(todo<MainEffects>(), "", {
|
||||||
// @ts-expect-error Type is wrong for the setting value
|
// @ts-expect-error Type is wrong for the setting value
|
||||||
config: { someValue: "notInAOrB" },
|
config: { someValue: "notInAOrB" },
|
||||||
})
|
})
|
||||||
createUtils<Manifest, Store>(todo<Effects>()).store.setOwn(
|
sdk.store.setOwn(
|
||||||
|
todo<MainEffects>(),
|
||||||
"/config/someValue",
|
"/config/someValue",
|
||||||
// @ts-expect-error Type is wrong for the setting value
|
// @ts-expect-error Type is wrong for the setting value
|
||||||
"notInAOrB",
|
"notInAOrB",
|
||||||
)
|
)
|
||||||
;(await createUtils<Manifest, Store>(todo<Effects>())
|
;(await sdk.store
|
||||||
.store.getOwn("/config/someValue")
|
.getOwn(todo<Effects>(), "/config/someValue")
|
||||||
// @ts-expect-error Const should normally not be callable
|
// @ts-expect-error Const should normally not be callable
|
||||||
.const()) satisfies string
|
.const()) satisfies string
|
||||||
;(await createUtils<Manifest, Store>(todo<Effects>())
|
;(await sdk.store
|
||||||
.store.getOwn("/config")
|
.getOwn(todo<Effects>(), "/config")
|
||||||
// @ts-expect-error Const should normally not be callable
|
// @ts-expect-error Const should normally not be callable
|
||||||
.const()) satisfies Store["config"]
|
.const()) satisfies Store["config"]
|
||||||
await createUtils<Manifest, Store>(todo<Effects>())
|
await sdk.store // @ts-expect-error Path is wrong
|
||||||
// @ts-expect-error Path is wrong
|
.getOwn("/config/somdsfeValue")
|
||||||
.store.getOwn("/config/somdsfeValue")
|
|
||||||
// @ts-expect-error Const should normally not be callable
|
// @ts-expect-error Const should normally not be callable
|
||||||
.const()
|
.const()
|
||||||
|
|
||||||
///
|
///
|
||||||
;(await createUtils<Manifest, Store>(todo<Effects>())
|
;(await sdk.store
|
||||||
.store.getOwn("/config/someValue")
|
.getOwn(todo<MainEffects>(), "/config/someValue")
|
||||||
// @ts-expect-error satisfies type is wrong
|
// @ts-expect-error satisfies type is wrong
|
||||||
.const()) satisfies number
|
.const()) satisfies number
|
||||||
;(await createMainUtils(todo<Effects>())
|
;(await sdk.store // @ts-expect-error Path is wrong
|
||||||
// @ts-expect-error Path is wrong
|
.getOwn(todo<MainEffects>(), "/config/")
|
||||||
.store.getOwn("/config/")
|
|
||||||
.const()) satisfies Store["config"]
|
.const()) satisfies Store["config"]
|
||||||
;(await todo<Effects>().store.get<Store, "/config/someValue">({
|
;(await todo<Effects>().store.get<Store, "/config/someValue">({
|
||||||
path: "/config/someValue",
|
path: "/config/someValue",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
export * as configTypes from "./config/configTypes"
|
export * as configTypes from "./config/configTypes"
|
||||||
import { AddSslOptions } from "../../core/startos/bindings/AddSslOptions"
|
import { AddSslOptions } from "../../core/startos/bindings/AddSslOptions"
|
||||||
|
import { MainEffects, ServiceInterfaceType, Signals } from "./StartSdk"
|
||||||
import { InputSpec } from "./config/configTypes"
|
import { InputSpec } from "./config/configTypes"
|
||||||
import { DependenciesReceipt } from "./config/setupConfig"
|
import { DependenciesReceipt } from "./config/setupConfig"
|
||||||
import { BindOptions, Scheme } from "./interfaces/Host"
|
import { BindOptions, Scheme } from "./interfaces/Host"
|
||||||
import { Daemons } from "./mainFn/Daemons"
|
import { Daemons } from "./mainFn/Daemons"
|
||||||
import { UrlString } from "./util/getServiceInterface"
|
import { UrlString } from "./util/getServiceInterface"
|
||||||
import { ServiceInterfaceType, Signals } from "./util/utils"
|
|
||||||
|
|
||||||
export type ExportedAction = (options: {
|
export type ExportedAction = (options: {
|
||||||
effects: Effects
|
effects: Effects
|
||||||
@@ -59,7 +59,7 @@ export namespace ExpectedExports {
|
|||||||
* package represents, like running a bitcoind in a bitcoind-wrapper.
|
* package represents, like running a bitcoind in a bitcoind-wrapper.
|
||||||
*/
|
*/
|
||||||
export type main = (options: {
|
export type main = (options: {
|
||||||
effects: Effects
|
effects: MainEffects
|
||||||
started(onTerm: () => PromiseLike<void>): PromiseLike<void>
|
started(onTerm: () => PromiseLike<void>): PromiseLike<void>
|
||||||
}) => Promise<Daemons<any, any>>
|
}) => Promise<Daemons<any, any>>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ServiceInterfaceType } from "../StartSdk"
|
||||||
import {
|
import {
|
||||||
AddressInfo,
|
AddressInfo,
|
||||||
Effects,
|
Effects,
|
||||||
@@ -5,7 +6,6 @@ import {
|
|||||||
Hostname,
|
Hostname,
|
||||||
HostnameInfo,
|
HostnameInfo,
|
||||||
} from "../types"
|
} from "../types"
|
||||||
import { ServiceInterfaceType } from "./utils"
|
|
||||||
|
|
||||||
export type UrlString = string
|
export type UrlString = string
|
||||||
export type HostId = string
|
export type HostId = string
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import "./deepEqual"
|
|||||||
import "./deepMerge"
|
import "./deepMerge"
|
||||||
import "./Overlay"
|
import "./Overlay"
|
||||||
import "./once"
|
import "./once"
|
||||||
import * as utils from "./utils"
|
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -23,11 +22,6 @@ export const isKnownError = (e: unknown): e is T.KnownError =>
|
|||||||
|
|
||||||
declare const affine: unique symbol
|
declare const affine: unique symbol
|
||||||
|
|
||||||
export const createUtils = utils.createUtils
|
|
||||||
export const createMainUtils = <Manifest extends SDKManifest, Store>(
|
|
||||||
effects: T.Effects,
|
|
||||||
) => createUtils<Manifest, Store, {}>(effects)
|
|
||||||
|
|
||||||
type NeverPossible = { [affine]: string }
|
type NeverPossible = { [affine]: string }
|
||||||
export type NoAny<A> = NeverPossible extends A
|
export type NoAny<A> = NeverPossible extends A
|
||||||
? keyof NeverPossible extends keyof A
|
? keyof NeverPossible extends keyof A
|
||||||
|
|||||||
@@ -1,310 +0,0 @@
|
|||||||
import nullIfEmpty from "./nullIfEmpty"
|
|
||||||
import {
|
|
||||||
CheckResult,
|
|
||||||
checkPortListening,
|
|
||||||
checkWebUrl,
|
|
||||||
} from "../health/checkFns"
|
|
||||||
import {
|
|
||||||
DaemonReturned,
|
|
||||||
Effects,
|
|
||||||
EnsureStorePath,
|
|
||||||
ExtractStore,
|
|
||||||
ServiceInterfaceId,
|
|
||||||
PackageId,
|
|
||||||
ValidIfNoStupidEscape,
|
|
||||||
} from "../types"
|
|
||||||
import { GetSystemSmtp } from "./GetSystemSmtp"
|
|
||||||
import { GetStore, getStore } from "../store/getStore"
|
|
||||||
import {
|
|
||||||
MountDependenciesOut,
|
|
||||||
mountDependencies,
|
|
||||||
} from "../dependency/mountDependencies"
|
|
||||||
import {
|
|
||||||
ManifestId,
|
|
||||||
VolumeName,
|
|
||||||
NamedPath,
|
|
||||||
Path,
|
|
||||||
} from "../dependency/setupDependencyMounts"
|
|
||||||
import { MultiHost, Scheme, SingleHost, StaticHost } from "../interfaces/Host"
|
|
||||||
import { ServiceInterfaceBuilder } from "../interfaces/ServiceInterfaceBuilder"
|
|
||||||
import { GetServiceInterface, getServiceInterface } from "./getServiceInterface"
|
|
||||||
import {
|
|
||||||
GetServiceInterfaces,
|
|
||||||
getServiceInterfaces,
|
|
||||||
} from "./getServiceInterfaces"
|
|
||||||
import * as CP from "node:child_process"
|
|
||||||
import { promisify } from "node:util"
|
|
||||||
import { splitCommand } from "./splitCommand"
|
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
|
||||||
import { MountOptions, Overlay, CommandOptions } from "./Overlay"
|
|
||||||
export type Signals = NodeJS.Signals
|
|
||||||
|
|
||||||
export const SIGTERM: Signals = "SIGTERM"
|
|
||||||
export const SIGKILL: Signals = "SIGTERM"
|
|
||||||
export const NO_TIMEOUT = -1
|
|
||||||
|
|
||||||
const childProcess = {
|
|
||||||
exec: promisify(CP.exec),
|
|
||||||
execFile: promisify(CP.execFile),
|
|
||||||
}
|
|
||||||
const cp = childProcess
|
|
||||||
|
|
||||||
export type ServiceInterfaceType = "ui" | "p2p" | "api"
|
|
||||||
|
|
||||||
export type Utils<
|
|
||||||
Manifest extends SDKManifest,
|
|
||||||
Store,
|
|
||||||
WrapperOverWrite = { const: never },
|
|
||||||
> = {
|
|
||||||
childProcess: typeof childProcess
|
|
||||||
createInterface: (options: {
|
|
||||||
name: string
|
|
||||||
id: string
|
|
||||||
description: string
|
|
||||||
hasPrimary: boolean
|
|
||||||
disabled: boolean
|
|
||||||
type: ServiceInterfaceType
|
|
||||||
username: null | string
|
|
||||||
path: string
|
|
||||||
search: Record<string, string>
|
|
||||||
schemeOverride: { ssl: Scheme; noSsl: Scheme } | null
|
|
||||||
masked: boolean
|
|
||||||
}) => ServiceInterfaceBuilder
|
|
||||||
getSystemSmtp: () => GetSystemSmtp & WrapperOverWrite
|
|
||||||
host: {
|
|
||||||
static: (id: string) => StaticHost
|
|
||||||
single: (id: string) => SingleHost
|
|
||||||
multi: (id: string) => MultiHost
|
|
||||||
}
|
|
||||||
mountDependencies: <
|
|
||||||
In extends
|
|
||||||
| Record<ManifestId, Record<VolumeName, Record<NamedPath, Path>>>
|
|
||||||
| Record<VolumeName, Record<NamedPath, Path>>
|
|
||||||
| Record<NamedPath, Path>
|
|
||||||
| Path,
|
|
||||||
>(
|
|
||||||
value: In,
|
|
||||||
) => Promise<MountDependenciesOut<In>>
|
|
||||||
serviceInterface: {
|
|
||||||
getOwn: (id: ServiceInterfaceId) => GetServiceInterface & WrapperOverWrite
|
|
||||||
get: (opts: {
|
|
||||||
id: ServiceInterfaceId
|
|
||||||
packageId: PackageId
|
|
||||||
}) => GetServiceInterface & WrapperOverWrite
|
|
||||||
getAllOwn: () => GetServiceInterfaces & WrapperOverWrite
|
|
||||||
getAll: (opts: {
|
|
||||||
packageId: PackageId
|
|
||||||
}) => GetServiceInterfaces & WrapperOverWrite
|
|
||||||
}
|
|
||||||
nullIfEmpty: typeof nullIfEmpty
|
|
||||||
runCommand: <A extends string>(
|
|
||||||
imageId: Manifest["images"][number],
|
|
||||||
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
|
||||||
options: CommandOptions & {
|
|
||||||
mounts?: { path: string; options: MountOptions }[]
|
|
||||||
},
|
|
||||||
) => Promise<{ stdout: string | Buffer; stderr: string | Buffer }>
|
|
||||||
runDaemon: <A extends string>(
|
|
||||||
imageId: Manifest["images"][number],
|
|
||||||
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
|
||||||
options: CommandOptions & {
|
|
||||||
mounts?: { path: string; options: MountOptions }[]
|
|
||||||
overlay?: Overlay
|
|
||||||
},
|
|
||||||
) => Promise<DaemonReturned>
|
|
||||||
store: {
|
|
||||||
get: <Path extends string>(
|
|
||||||
packageId: string,
|
|
||||||
path: EnsureStorePath<Store, Path>,
|
|
||||||
) => GetStore<Store, Path> & WrapperOverWrite
|
|
||||||
getOwn: <Path extends string>(
|
|
||||||
path: EnsureStorePath<Store, Path>,
|
|
||||||
) => GetStore<Store, Path> & WrapperOverWrite
|
|
||||||
setOwn: <Path extends string | never>(
|
|
||||||
path: EnsureStorePath<Store, Path>,
|
|
||||||
value: ExtractStore<Store, Path>,
|
|
||||||
) => Promise<void>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const createUtils = <
|
|
||||||
Manifest extends SDKManifest,
|
|
||||||
Store = never,
|
|
||||||
WrapperOverWrite = { const: never },
|
|
||||||
>(
|
|
||||||
effects: Effects,
|
|
||||||
): Utils<Manifest, Store, WrapperOverWrite> => {
|
|
||||||
return {
|
|
||||||
createInterface: (options: {
|
|
||||||
name: string
|
|
||||||
id: string
|
|
||||||
description: string
|
|
||||||
hasPrimary: boolean
|
|
||||||
disabled: boolean
|
|
||||||
type: ServiceInterfaceType
|
|
||||||
username: null | string
|
|
||||||
path: string
|
|
||||||
search: Record<string, string>
|
|
||||||
schemeOverride: { ssl: Scheme; noSsl: Scheme } | null
|
|
||||||
masked: boolean
|
|
||||||
}) => new ServiceInterfaceBuilder({ ...options, effects }),
|
|
||||||
childProcess,
|
|
||||||
getSystemSmtp: () =>
|
|
||||||
new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite,
|
|
||||||
|
|
||||||
host: {
|
|
||||||
static: (id: string) => new StaticHost({ id, effects }),
|
|
||||||
single: (id: string) => new SingleHost({ id, effects }),
|
|
||||||
multi: (id: string) => new MultiHost({ id, effects }),
|
|
||||||
},
|
|
||||||
nullIfEmpty,
|
|
||||||
|
|
||||||
serviceInterface: {
|
|
||||||
getOwn: (id: ServiceInterfaceId) =>
|
|
||||||
getServiceInterface(effects, {
|
|
||||||
id,
|
|
||||||
packageId: null,
|
|
||||||
}) as GetServiceInterface & WrapperOverWrite,
|
|
||||||
get: (opts: { id: ServiceInterfaceId; packageId: PackageId }) =>
|
|
||||||
getServiceInterface(effects, opts) as GetServiceInterface &
|
|
||||||
WrapperOverWrite,
|
|
||||||
getAllOwn: () =>
|
|
||||||
getServiceInterfaces(effects, {
|
|
||||||
packageId: null,
|
|
||||||
}) as GetServiceInterfaces & WrapperOverWrite,
|
|
||||||
getAll: (opts: { packageId: PackageId }) =>
|
|
||||||
getServiceInterfaces(effects, opts) as GetServiceInterfaces &
|
|
||||||
WrapperOverWrite,
|
|
||||||
},
|
|
||||||
store: {
|
|
||||||
get: <Path extends string = never>(
|
|
||||||
packageId: string,
|
|
||||||
path: EnsureStorePath<Store, Path>,
|
|
||||||
) =>
|
|
||||||
getStore<Store, Path>(effects, path as any, {
|
|
||||||
packageId,
|
|
||||||
}) as any,
|
|
||||||
getOwn: <Path extends string>(path: EnsureStorePath<Store, Path>) =>
|
|
||||||
getStore<Store, Path>(effects, path as any) as any,
|
|
||||||
setOwn: <Path extends string | never>(
|
|
||||||
path: EnsureStorePath<Store, Path>,
|
|
||||||
value: ExtractStore<Store, Path>,
|
|
||||||
) => effects.store.set<Store, Path>({ value, path: path as any }),
|
|
||||||
},
|
|
||||||
|
|
||||||
runCommand: async <A extends string>(
|
|
||||||
imageId: Manifest["images"][number],
|
|
||||||
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
|
||||||
options: CommandOptions & {
|
|
||||||
mounts?: { path: string; options: MountOptions }[]
|
|
||||||
},
|
|
||||||
): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => {
|
|
||||||
const commands = splitCommand(command)
|
|
||||||
const overlay = await Overlay.of(effects, imageId)
|
|
||||||
try {
|
|
||||||
for (let mount of options.mounts || []) {
|
|
||||||
await overlay.mount(mount.options, mount.path)
|
|
||||||
}
|
|
||||||
return await overlay.exec(commands)
|
|
||||||
} finally {
|
|
||||||
await overlay.destroy()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
runDaemon: async <A extends string>(
|
|
||||||
imageId: Manifest["images"][number],
|
|
||||||
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
|
||||||
options: CommandOptions & {
|
|
||||||
mounts?: { path: string; options: MountOptions }[]
|
|
||||||
overlay?: Overlay
|
|
||||||
},
|
|
||||||
): Promise<DaemonReturned> => {
|
|
||||||
const commands = splitCommand(command)
|
|
||||||
const overlay = options.overlay || (await Overlay.of(effects, imageId))
|
|
||||||
for (let mount of options.mounts || []) {
|
|
||||||
await overlay.mount(mount.options, mount.path)
|
|
||||||
}
|
|
||||||
const childProcess = await overlay.spawn(commands, {
|
|
||||||
env: options.env,
|
|
||||||
})
|
|
||||||
const answer = new Promise<null>((resolve, reject) => {
|
|
||||||
childProcess.stdout.on("data", (data: any) => {
|
|
||||||
console.log(data.toString())
|
|
||||||
})
|
|
||||||
childProcess.stderr.on("data", (data: any) => {
|
|
||||||
console.error(data.toString())
|
|
||||||
})
|
|
||||||
|
|
||||||
childProcess.on("exit", (code: any) => {
|
|
||||||
if (code === 0) {
|
|
||||||
return resolve(null)
|
|
||||||
}
|
|
||||||
return reject(new Error(`${commands[0]} exited with code ${code}`))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const pid = childProcess.pid
|
|
||||||
return {
|
|
||||||
async wait() {
|
|
||||||
const pids = pid ? await psTree(pid, overlay) : []
|
|
||||||
try {
|
|
||||||
return await answer
|
|
||||||
} finally {
|
|
||||||
for (const process of pids) {
|
|
||||||
cp.execFile("kill", [`-9`, String(process)]).catch((_) => {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) {
|
|
||||||
const pids = pid ? await psTree(pid, overlay) : []
|
|
||||||
try {
|
|
||||||
childProcess.kill(signal)
|
|
||||||
|
|
||||||
if (timeout > NO_TIMEOUT) {
|
|
||||||
const didTimeout = await Promise.race([
|
|
||||||
new Promise((resolve) => setTimeout(resolve, timeout)).then(
|
|
||||||
() => true,
|
|
||||||
),
|
|
||||||
answer.then(() => false),
|
|
||||||
])
|
|
||||||
if (didTimeout) {
|
|
||||||
childProcess.kill(SIGKILL)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await answer
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
await overlay.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (const process of pids) {
|
|
||||||
await cp.execFile("kill", [`-${signal}`, String(process)])
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
for (const process of pids) {
|
|
||||||
cp.execFile("kill", [`-9`, String(process)]).catch((_) => {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mountDependencies: <
|
|
||||||
In extends
|
|
||||||
| Record<ManifestId, Record<VolumeName, Record<NamedPath, Path>>>
|
|
||||||
| Record<VolumeName, Record<NamedPath, Path>>
|
|
||||||
| Record<NamedPath, Path>
|
|
||||||
| Path,
|
|
||||||
>(
|
|
||||||
value: In,
|
|
||||||
) => mountDependencies(effects, value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function noop(): void {}
|
|
||||||
|
|
||||||
async function psTree(pid: number, overlay: Overlay): Promise<number[]> {
|
|
||||||
const { stdout } = await childProcess.exec(`pstree -p ${pid}`)
|
|
||||||
const regex: RegExp = /\((\d+)\)/g
|
|
||||||
return [...stdout.toString().matchAll(regex)].map(([_all, pid]) =>
|
|
||||||
parseInt(pid),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user