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}})`;
}