From be2019fda17fa58767474b76f6c1da354e7ae088 Mon Sep 17 00:00:00 2001 From: BluJ Date: Tue, 11 Apr 2023 11:54:57 -0600 Subject: [PATCH] chore: Update the types --- lib/config/builder/value.ts | 11 +++- lib/config/specToBuilder.ts | 28 +++++++--- lib/health/index.ts | 3 +- lib/health/readyCheck.ts | 55 ------------------- lib/mainFn/Daemons.ts | 79 ++++++++++++++++++++++++++++ lib/{health => mainFn}/ReadyProof.ts | 0 lib/mainFn/RunningMainRet.ts | 2 +- lib/mainFn/index.ts | 12 +++++ lib/test/configBuilder.test.ts | 12 +++-- lib/types.ts | 25 ++++----- package-lock.json | 4 +- package.json | 2 +- scripts/oldSpecToBuilder.ts | 56 +++++++++++++++----- 13 files changed, 188 insertions(+), 101 deletions(-) delete mode 100644 lib/health/readyCheck.ts create mode 100644 lib/mainFn/Daemons.ts rename lib/{health => mainFn}/ReadyProof.ts (100%) diff --git a/lib/config/builder/value.ts b/lib/config/builder/value.ts index 147f34f..12f561d 100644 --- a/lib/config/builder/value.ts +++ b/lib/config/builder/value.ts @@ -35,7 +35,12 @@ const username = Value.string({ ``` */ export class Value extends IBuilder { - static boolean(a: { name: string; description?: string | null; warning?: string | null; default?: boolean | null }) { + static boolean(a: { + name: string; + description?: string | null; + warning?: string | null; + default?: boolean | null; + }) { return new Value({ description: null, warning: null, @@ -160,7 +165,9 @@ export class Value extends IBuilder { spec, }); } - static union>( + static union< + V extends Variants<{ [key: string]: { name: string; spec: InputSpec } }> + >( a: { name: string; description?: string | null; diff --git a/lib/config/specToBuilder.ts b/lib/config/specToBuilder.ts index 2fb8b6b..8004e0d 100644 --- a/lib/config/specToBuilder.ts +++ b/lib/config/specToBuilder.ts @@ -8,9 +8,14 @@ export async function specToBuilderFile( inputData: Promise | InputSpecRaw, options: Parameters[1] ) { - await fs.writeFile(file, await specToBuilder(inputData, options), (err) => console.error(err)); + await fs.writeFile(file, await specToBuilder(inputData, options), (err) => + console.error(err) + ); } -export async function specToBuilder(inputData: Promise | InputSpecRaw, { startSdk = "start-sdk" } = {}) { +export async function specToBuilder( + inputData: Promise | InputSpecRaw, + { startSdk = "start-sdk" } = {} +) { const outputLines: string[] = []; outputLines.push(` import {Config, Value, List, Variants} from '${startSdk}/config/builder'; @@ -19,8 +24,13 @@ export async function specToBuilder(inputData: Promise | InputSpec const namedConsts = new Set(["Config", "Value", "List"]); const configName = newConst("InputSpec", convertInputSpec(data)); - const configMatcherName = newConst("matchInputSpec", `${configName}.validator()`); - outputLines.push(`export type InputSpec = typeof ${configMatcherName}._TYPE;`); + const configMatcherName = newConst( + "matchInputSpec", + `${configName}.validator()` + ); + outputLines.push( + `export type InputSpec = typeof ${configMatcherName}._TYPE;` + ); return outputLines.join("\n"); @@ -61,7 +71,10 @@ export async function specToBuilder(inputData: Promise | InputSpec } case "union": { const { variants, type, ...rest } = value; - const variantVariable = newConst(value.name + "_variants", convertVariants(variants)); + const variantVariable = newConst( + value.name + "_variants", + convertVariants(variants) + ); return `Value.union(${JSON.stringify(rest)}, ${variantVariable})`; } @@ -116,7 +129,10 @@ export async function specToBuilder(inputData: Promise | InputSpec })})`; } case "object": { - const specName = newConst(value.name + "_spec", convertInputSpec(spec.spec)); + const specName = newConst( + value.name + "_spec", + convertInputSpec(spec.spec) + ); return `List.obj({ name: ${JSON.stringify(value.name || null)}, range: ${JSON.stringify(value.range || null)}, diff --git a/lib/health/index.ts b/lib/health/index.ts index 37eef96..6a50a75 100644 --- a/lib/health/index.ts +++ b/lib/health/index.ts @@ -3,5 +3,4 @@ export * as trigger from "./trigger"; export { TriggerInput } from "./trigger/TriggerInput"; export { HealthReceipt } from "./HealthReceipt"; -export { readyCheck } from "./readyCheck"; -export { ReadyProof } from "./ReadyProof"; +export { ReadyProof } from "../mainFn/ReadyProof"; diff --git a/lib/health/readyCheck.ts b/lib/health/readyCheck.ts deleted file mode 100644 index ae5369b..0000000 --- a/lib/health/readyCheck.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { InterfaceReceipt } from "../mainFn/interfaceReceipt"; -import { Daemon, Effects } from "../types"; -import { CheckResult } from "./checkFns/CheckResult"; -import { ReadyReceipt } from "./ReadyProof"; -import { HealthReceipt } from "./HealthReceipt"; -import { Trigger } from "./trigger"; -import { TriggerInput } from "./trigger/TriggerInput"; -import { defaultTrigger } from "./trigger/defaultTrigger"; - -function readReciptOf(a: A) { - return a as A & ReadyReceipt; -} -export function readyCheck(o: { - effects: Effects; - started(onTerm: () => void): null; - interfaceReceipt: InterfaceReceipt; - healthReceipts: Iterable; - daemonReceipt: Daemon; - name: string; - trigger?: Trigger; - fn(): Promise | CheckResult; - onFirstSuccess?: () => () => Promise | unknown; -}) { - new Promise(async () => { - const trigger = (o.trigger ?? defaultTrigger)(); - let currentValue: TriggerInput = { - lastResult: null, - hadSuccess: false, - }; - for ( - let res = await trigger.next(currentValue); - !res.done; - res = await trigger.next(currentValue) - ) { - try { - const { status, message } = await o.fn(); - if (!currentValue.hadSuccess) { - await o.started(o?.onFirstSuccess ?? (() => o.daemonReceipt.term())); - } - await o.effects.setHealth({ - name: o.name, - status, - message, - }); - currentValue.hadSuccess = true; - currentValue.lastResult = "success"; - } catch (_) { - currentValue.lastResult = "failure"; - } - } - }); - return readReciptOf({ - daemon: o.daemonReceipt, - }); -} diff --git a/lib/mainFn/Daemons.ts b/lib/mainFn/Daemons.ts new file mode 100644 index 0000000..73c2d30 --- /dev/null +++ b/lib/mainFn/Daemons.ts @@ -0,0 +1,79 @@ +import { HealthReceipt, ReadyProof } from "../health"; +import { CheckResult } from "../health/checkFns"; +import { Trigger } from "../health/trigger"; +import { Effects, ValidIfNoStupidEscape } from "../types"; +import { InterfaceReceipt } from "./interfaceReceipt"; +import { RunningMainRet } from "./RunningMainRet"; +type Daemon< + Ids extends string | never, + Command extends string, + Id extends string +> = { + id: Id; + command: ValidIfNoStupidEscape | [string, ...string[]]; + + ready: { + display: null | { + name: "Websocket Live"; + message: "The websocket is live"; + }; + fn: () => Promise | CheckResult; + trigger?: Trigger; + }; + requires?: Exclude[]; +}; + +const todo = (): A => { + throw new Error("TODO"); +}; +/** + * Used during the main of a function, it allows us to describe and ensure a set of daemons are running. + * With the dependency, we are using this like an init system, where we can ensure that a daemon is running + * The return type of this is used in the runningMain +```ts +Daemons.with({ + effects, + started, + interfacesReceipt, + }) + .addDaemon({ + id: "nostr", + command: "./nostr-rs-relay --db /data", + ready: { + display: { + name: "Websocket Live", + message: "The websocket is live", + }, + fn: () => checkPortListening(effects, 8080), + }, + }) + .build() +``` + */ +export class Daemons { + private constructor( + readonly effects: Effects, + readonly started: (onTerm: () => void) => null, + readonly daemons?: Daemon[] + ) {} + + static with(config: { + effects: Effects; + started: (onTerm: () => void) => null; + interfaceReceipt: InterfaceReceipt; + healthReceipts: HealthReceipt[]; + }) { + return new Daemons(config.effects, config.started); + } + addDaemon( + newDaemon: Daemon + ) { + const daemons = [...(this?.daemons ?? [])]; + daemons.push(newDaemon as any); + return new Daemons(this.effects, this.started, daemons); + } + + build() { + return todo(); + } +} diff --git a/lib/health/ReadyProof.ts b/lib/mainFn/ReadyProof.ts similarity index 100% rename from lib/health/ReadyProof.ts rename to lib/mainFn/ReadyProof.ts diff --git a/lib/mainFn/RunningMainRet.ts b/lib/mainFn/RunningMainRet.ts index d8d9d22..ef1fd25 100644 --- a/lib/mainFn/RunningMainRet.ts +++ b/lib/mainFn/RunningMainRet.ts @@ -1,5 +1,5 @@ import { Daemon } from "../types"; -import { ReadyProof } from "../health/ReadyProof"; +import { ReadyProof } from "./ReadyProof"; export type RunningMainRet = { [ReadyProof]: never; diff --git a/lib/mainFn/index.ts b/lib/mainFn/index.ts index 80587fc..6322655 100644 --- a/lib/mainFn/index.ts +++ b/lib/mainFn/index.ts @@ -9,6 +9,18 @@ export { Origin } from "./Origin"; export { TorBinding } from "./TorBinding"; export { TorHostname } from "./TorHostname"; +export { Daemons } from "./Daemons"; + +/** + * Used to ensure that the main function is running with the valid proofs. + * We first do the folowing order of things + * 1. We get the interfaces + * 2. We setup all the commands to setup the system + * 3. We create the health checks + * 4. We setup the daemons init system + * @param fn + * @returns + */ export const runningMain: ( fn: (o: { effects: Effects; diff --git a/lib/test/configBuilder.test.ts b/lib/test/configBuilder.test.ts index bd098ca..93ea126 100644 --- a/lib/test/configBuilder.test.ts +++ b/lib/test/configBuilder.test.ts @@ -113,14 +113,16 @@ describe("values", () => { testOutput>()(null); }); test("object", () => { - const value = Value.object({ - name: "Testing", - spec: Config.of({ + const value = Value.object( + { + name: "Testing", + }, + Config.of({ a: Value.boolean({ name: "test", }), - }), - }); + }) + ); const validator = value.validator(); validator.unsafeCast({ a: true }); testOutput()(null); diff --git a/lib/types.ts b/lib/types.ts index 133bedc..6f4e7f4 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -79,7 +79,7 @@ export namespace ExpectedExports { export type TimeMs = number; export type VersionString = string; -type ValidIfNoStupidEscape = A extends +export type ValidIfNoStupidEscape = A extends | `${string}'"'"'${string}` | `${string}\\"${string}` ? never @@ -236,23 +236,20 @@ export type Effects = { callback?: (config: unknown, previousConfig: unknown) => void; }): Promise; - /** Get the address for another service for local internet*/ - getServiceLocalAddress(options: { - packageId: string; - interfaceName: string; - }): Promise; + getLocalHostname(): Promise; + getIPHostname(): Promise; /** Get the address for another service for tor interfaces */ - getServiceTorAddress(options: { - packageId: string; - interfaceName: string; - }): Promise; + getServiceTorHostname( + interfaceId: string, + packageId?: string + ): Promise; /** * Get the port address for another service */ - getServicePortForward(options: { - packageId: string; - internalPort: number; - }): Promise; + getServicePortForward( + internalPort: number, + packageId?: string + ): Promise; /** When we want to create a link in the front end interfaces, and example is * exposing a url to view a web service diff --git a/package-lock.json b/package-lock.json index aecbf2f..a66ce2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "start-sdk", - "version": "0.4.0-lib0.charlie10", + "version": "0.4.0-lib0.charlie11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "start-sdk", - "version": "0.4.0-lib0.charlie10", + "version": "0.4.0-lib0.charlie11", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index e72e728..30c1f7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "start-sdk", - "version": "0.4.0-lib0.charlie10", + "version": "0.4.0-lib0.charlie11", "description": "For making the patterns that are wanted in making services for the startOS.", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/scripts/oldSpecToBuilder.ts b/scripts/oldSpecToBuilder.ts index fc10d4e..072d564 100644 --- a/scripts/oldSpecToBuilder.ts +++ b/scripts/oldSpecToBuilder.ts @@ -8,10 +8,15 @@ export async function writeConvertedFile( inputData: Promise | any, options: Parameters[1] ) { - await fs.writeFile(file, await makeFileContent(inputData, options), (err) => console.error(err)); + await fs.writeFile(file, await makeFileContent(inputData, options), (err) => + console.error(err) + ); } -export default async function makeFileContent(inputData: Promise | any, { startSdk = "start-sdk" } = {}) { +export default async function makeFileContent( + inputData: Promise | any, + { startSdk = "start-sdk" } = {} +) { const outputLines: string[] = []; outputLines.push(` import {Config, Value, List, Variants} from '${startSdk}/config/builder'; @@ -20,8 +25,13 @@ export default async function makeFileContent(inputData: Promise | any, { s const namedConsts = new Set(["Config", "Value", "List"]); const configName = newConst("InputSpec", convertInputSpec(data)); - const configMatcherName = newConst("matchInputSpec", `${configName}.validator()`); - outputLines.push(`export type InputSpec = typeof ${configMatcherName}._TYPE;`); + const configMatcherName = newConst( + "matchInputSpec", + `${configName}.validator()` + ); + outputLines.push( + `export type InputSpec = typeof ${configMatcherName}._TYPE;` + ); return outputLines.join("\n"); @@ -101,7 +111,10 @@ export default async function makeFileContent(inputData: Promise | any, { s )})`; } case "enum": { - const allValueNames = new Set([...(value?.["values"] || []), ...Object.keys(value?.["value-names"] || {})]); + const allValueNames = new Set([ + ...(value?.["values"] || []), + ...Object.keys(value?.["value-names"] || {}), + ]); const values = Object.fromEntries( Array.from(allValueNames) .filter(string.test) @@ -121,12 +134,14 @@ export default async function makeFileContent(inputData: Promise | any, { s )} as const)`; } case "object": { - const specName = newConst(value.name + "_spec", convertInputSpec(value.spec)); + const specName = newConst( + value.name + "_spec", + convertInputSpec(value.spec) + ); return `Value.object({ name: ${JSON.stringify(value.name || null)}, description: ${JSON.stringify(value.description || null)}, warning: ${JSON.stringify(value.warning || null)}, - default: ${JSON.stringify(value.default || null)}, }, ${specName})`; } case "union": { @@ -157,7 +172,9 @@ export default async function makeFileContent(inputData: Promise | any, { s function convertList(value: any) { switch (value.subtype) { case "string": { - return `List.${value?.spec?.textarea === true ? "textarea" : "string"}(${JSON.stringify( + return `List.${ + value?.spec?.textarea === true ? "textarea" : "string" + }(${JSON.stringify( { name: value.name || null, range: value.range || null, @@ -216,7 +233,10 @@ export default async function makeFileContent(inputData: Promise | any, { s )})`; } case "object": { - const specName = newConst(value.name + "_spec", convertInputSpec(value.spec.spec)); + const specName = newConst( + value.name + "_spec", + convertInputSpec(value.spec.spec) + ); return `List.obj({ name: ${JSON.stringify(value.name || null)}, range: ${JSON.stringify(value.range || null)}, @@ -232,14 +252,19 @@ export default async function makeFileContent(inputData: Promise | any, { s case "union": { const variants = newConst( value.name + "_variants", - convertVariants(value.spec.variants, value.spec["variant-names"] || {}) + convertVariants( + value.spec.variants, + value.spec["variant-names"] || {} + ) ); const unionValueName = newConst( value.name + "_union", ` Value.union({ name: ${JSON.stringify(value?.spec?.tag?.name || null)}, - description: ${JSON.stringify(value?.spec?.tag?.description || null)}, + description: ${JSON.stringify( + value?.spec?.tag?.description || null + )}, warning: ${JSON.stringify(value?.spec?.tag?.warning || null)}, required: ${JSON.stringify(!(value?.spec?.tag?.nullable || false))}, default: ${JSON.stringify(value?.spec?.default || null)}, @@ -270,11 +295,16 @@ export default async function makeFileContent(inputData: Promise | any, { s throw new Error(`Unknown subtype "${value.subtype}"`); } - function convertVariants(variants: Record, variantNames: Record): string { + function convertVariants( + variants: Record, + variantNames: Record + ): string { let answer = "Variants.of({"; for (const [key, value] of Object.entries(variants)) { const variantSpec = newConst(key, convertInputSpec(value)); - answer += `"${key}": {name: "${variantNames[key] || key}", spec: ${variantSpec}},`; + answer += `"${key}": {name: "${ + variantNames[key] || key + }", spec: ${variantSpec}},`; } return `${answer}})`; }