chore: Update the types

This commit is contained in:
BluJ
2023-04-11 11:54:57 -06:00
parent 9810c222dd
commit be2019fda1
13 changed files with 188 additions and 101 deletions

View File

@@ -35,7 +35,12 @@ const username = Value.string({
```
*/
export class Value<A extends ValueSpec> extends IBuilder<A> {
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<A extends ValueSpec> extends IBuilder<A> {
spec,
});
}
static union<V extends Variants<{ [key: string]: { name: string; spec: InputSpec } }>>(
static union<
V extends Variants<{ [key: string]: { name: string; spec: InputSpec } }>
>(
a: {
name: string;
description?: string | null;

View File

@@ -8,9 +8,14 @@ export async function specToBuilderFile(
inputData: Promise<InputSpecRaw> | InputSpecRaw,
options: Parameters<typeof specToBuilder>[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> | InputSpecRaw, { startSdk = "start-sdk" } = {}) {
export async function specToBuilder(
inputData: Promise<InputSpecRaw> | 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<InputSpecRaw> | 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<InputSpecRaw> | 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<InputSpecRaw> | 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)},

View File

@@ -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";

View File

@@ -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 extends { daemon: Daemon }>(a: A) {
return a as A & ReadyReceipt;
}
export function readyCheck(o: {
effects: Effects;
started(onTerm: () => void): null;
interfaceReceipt: InterfaceReceipt;
healthReceipts: Iterable<HealthReceipt>;
daemonReceipt: Daemon;
name: string;
trigger?: Trigger;
fn(): Promise<CheckResult> | CheckResult;
onFirstSuccess?: () => () => Promise<unknown> | 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,
});
}

79
lib/mainFn/Daemons.ts Normal file
View File

@@ -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<Command> | [string, ...string[]];
ready: {
display: null | {
name: "Websocket Live";
message: "The websocket is live";
};
fn: () => Promise<CheckResult> | CheckResult;
trigger?: Trigger;
};
requires?: Exclude<Ids, Id>[];
};
const todo = <A>(): 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<Ids extends string | never> {
private constructor(
readonly effects: Effects,
readonly started: (onTerm: () => void) => null,
readonly daemons?: Daemon<Ids, "command", never>[]
) {}
static with(config: {
effects: Effects;
started: (onTerm: () => void) => null;
interfaceReceipt: InterfaceReceipt;
healthReceipts: HealthReceipt[];
}) {
return new Daemons<never>(config.effects, config.started);
}
addDaemon<Id extends string, Command extends string>(
newDaemon: Daemon<Ids, Command, Id>
) {
const daemons = [...(this?.daemons ?? [])];
daemons.push(newDaemon as any);
return new Daemons<Ids | Id>(this.effects, this.started, daemons);
}
build() {
return todo<RunningMainRet>();
}
}

View File

@@ -1,5 +1,5 @@
import { Daemon } from "../types";
import { ReadyProof } from "../health/ReadyProof";
import { ReadyProof } from "./ReadyProof";
export type RunningMainRet = {
[ReadyProof]: never;

View File

@@ -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;

View File

@@ -113,14 +113,16 @@ describe("values", () => {
testOutput<typeof validator._TYPE, Array<"a" | "b">>()(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<typeof validator._TYPE, { a: boolean }>()(null);

View File

@@ -79,7 +79,7 @@ export namespace ExpectedExports {
export type TimeMs = number;
export type VersionString = string;
type ValidIfNoStupidEscape<A> = A extends
export type ValidIfNoStupidEscape<A> = A extends
| `${string}'"'"'${string}`
| `${string}\\"${string}`
? never
@@ -236,23 +236,20 @@ export type Effects = {
callback?: (config: unknown, previousConfig: unknown) => void;
}): Promise<unknown>;
/** Get the address for another service for local internet*/
getServiceLocalAddress(options: {
packageId: string;
interfaceName: string;
}): Promise<string>;
getLocalHostname(): Promise<string>;
getIPHostname(): Promise<string>;
/** Get the address for another service for tor interfaces */
getServiceTorAddress(options: {
packageId: string;
interfaceName: string;
}): Promise<string>;
getServiceTorHostname(
interfaceId: string,
packageId?: string
): Promise<string>;
/**
* Get the port address for another service
*/
getServicePortForward(options: {
packageId: string;
internalPort: number;
}): Promise<string>;
getServicePortForward(
internalPort: number,
packageId?: string
): Promise<number>;
/** When we want to create a link in the front end interfaces, and example is
* exposing a url to view a web service

4
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -8,10 +8,15 @@ export async function writeConvertedFile(
inputData: Promise<any> | any,
options: Parameters<typeof makeFileContent>[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> | any, { startSdk = "start-sdk" } = {}) {
export default async function makeFileContent(
inputData: Promise<any> | 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> | 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> | 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> | 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> | 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> | 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> | 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> | any, { s
throw new Error(`Unknown subtype "${value.subtype}"`);
}
function convertVariants(variants: Record<string, unknown>, variantNames: Record<string, string>): string {
function convertVariants(
variants: Record<string, unknown>,
variantNames: Record<string, string>
): 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}})`;
}