diff --git a/config/builder.ts b/config/builder.ts new file mode 100644 index 0000000..dbc844c --- /dev/null +++ b/config/builder.ts @@ -0,0 +1,9 @@ +export class IBuilder { + protected constructor(readonly a: A) {} + + public build(): A { + return this.a; + } +} + +export type BuilderExtract = A extends IBuilder ? B : never; diff --git a/config/config.ts b/config/config.ts new file mode 100644 index 0000000..ae89c38 --- /dev/null +++ b/config/config.ts @@ -0,0 +1,24 @@ +import { BuilderExtract, IBuilder } from "./builder.ts"; +import { Value } from "./value.ts"; + +export class Config extends IBuilder { + static empty() { + return new Config({}); + } + + static of }>(spec: B) { + // deno-lint-ignore no-explicit-any + const answer: { [K in keyof B]: BuilderExtract } = {} as any; + for (const key in spec) { + // deno-lint-ignore no-explicit-any + answer[key] = spec[key].build() as any; + } + return new Config(answer); + } + addValue(key: K, value: Value) { + return new Config({ + ...this.a, + [key]: value.build(), + } as A & { [key in K]: B }); + } +} diff --git a/config/index.test.ts b/config/index.test.ts new file mode 100644 index 0000000..b9a9a02 --- /dev/null +++ b/config/index.test.ts @@ -0,0 +1,48 @@ +import { Config } from "./config.ts"; +import { Pointer } from "./pointer.ts"; +import { Value } from "./value.ts"; +import { expect } from "https://deno.land/x/expect@v0.2.9/mod.ts"; +const { test } = Deno; + +test("Pointer", () => { + const bitcoinPropertiesBuilt: { + "peer-tor-address": { + name: string; + description: string; + type: "pointer"; + subtype: "package"; + "package-id": string; + target: "tor-address"; + interface: string; + }; + } = Config.empty() + .addValue( + "peer-tor-address", + Value.pointer( + Pointer.packageTorAddress({ + name: "Peer Tor Address", + description: "The Tor address of the peer interface", + "package-id": "bitcoind", + interface: "peer", + warning: null, + }) + ) + ) + .build(); + expect(JSON.stringify(bitcoinPropertiesBuilt)).toEqual( + /*json*/ `{ + "peer-tor-address": { + "type": "pointer", + "subtype": "package", + "target": "tor-address", + "name": "Peer Tor Address", + "description": "The Tor address of the peer interface", + "package-id": "bitcoind", + "interface": "peer", + "warning": null + }}` + .replaceAll("\n", " ") + .replaceAll(/\s{2,}/g, "") + .replaceAll(": ", ":") + ); +}); diff --git a/config/index.ts b/config/index.ts new file mode 100644 index 0000000..1e9cba0 --- /dev/null +++ b/config/index.ts @@ -0,0 +1,4 @@ +export { Config } from "./config.ts"; +export { List } from "./list.ts"; +export { Pointer } from "./pointer.ts"; +export { Value } from "./value.ts"; diff --git a/config/list.ts b/config/list.ts new file mode 100644 index 0000000..4971222 --- /dev/null +++ b/config/list.ts @@ -0,0 +1,116 @@ +import { UniqueBy } from "../types.ts"; +import { BuilderExtract, IBuilder } from "./builder.ts"; +import { Config } from "./config.ts"; +import { Default, NullableDefault, NumberSpec, StringSpec } from "./value.ts"; +import { Description } from "./value.ts"; + +export class List extends IBuilder { + // deno-lint-ignore ban-types + static boolean & { range: string; spec: {} }>(a: A) { + return new List({ + subtype: "boolean" as const, + ...a, + }); + } + + static string< + A extends Description & Default + >(a: A) { + return new List({ + subtype: "string" as const, + ...a, + }); + } + static number & { range: string; spec: NumberSpec }>(a: A) { + return new List({ + subtype: "number" as const, + ...a, + }); + } + static enum< + A extends Description & + Default & { + range: string; + spec: { + values: string[]; + "value-names": { + [key: string]: string; + }; + }; + } + >(a: A) { + return new List({ + subtype: "enum" as const, + ...a, + }); + } + static object< + A extends Description & + NullableDefault[]> & { + range: string; + spec: { + spec: Config; + "display-as": null | string; + "unique-by": null | UniqueBy; + }; + }, + B + >(a: A) { + const { spec: previousSpec, ...rest } = a; + const { spec: previousSpecSpec, ...restSpec } = previousSpec; + const specSpec = previousSpecSpec.build(); + const spec = { + ...restSpec, + spec: specSpec, + }; + const value = { + spec, + ...rest, + }; + return new List({ + subtype: "object" as const, + ...value, + }); + } + static union< + A extends Description & + Default & { + range: string; + spec: { + tag: { + id: string; + name: null | string | undefined; + description: null | string | undefined; + "variant-names": { + [key: string]: string; + }; + }; + variants: Variants; + "display-as": null | string | undefined; + "unique-by": null | UniqueBy | undefined; + }; + }, + Variants extends { [key: string]: Config } + >(a: A) { + const { spec: previousSpec, ...rest } = a; + const { variants: previousVariants, ...restSpec } = previousSpec; + // deno-lint-ignore no-explicit-any + const variants: { [K in keyof Variants]: BuilderExtract } = {} as any; + for (const key in previousVariants) { + // deno-lint-ignore no-explicit-any + variants[key] = previousVariants[key].build() as any; + } + const spec = { + ...restSpec, + variants, + }; + const value = { + spec, + ...rest, + }; + return new List({ + subtype: "union" as const, + ...value, + }); + } +} diff --git a/config/pointer.ts b/config/pointer.ts new file mode 100644 index 0000000..26ae61e --- /dev/null +++ b/config/pointer.ts @@ -0,0 +1,44 @@ +import { IBuilder } from "./builder.ts"; +import { Description } from "./value.ts"; + +export class Pointer extends IBuilder { + static packageTorKey(a: A) { + return new Pointer({ + type: "pointer" as const, + subtype: "package" as const, + target: "tor-key" as const, + ...a, + }); + } + static packageTorAddress(a: A) { + return new Pointer({ + type: "pointer" as const, + subtype: "package" as const, + target: "tor-address" as const, + ...a, + }); + } + static packageLanAddress(a: A) { + return new Pointer({ + type: "pointer" as const, + subtype: "package" as const, + target: "lan-address" as const, + ...a, + }); + } + static packageConfig(a: A) { + return new Pointer({ + type: "pointer" as const, + subtype: "package" as const, + target: "config" as const, + ...a, + }); + } + static system>(a: A) { + return new Pointer({ + type: "pointer" as const, + subtype: "system" as const, + ...a, + }); + } +} diff --git a/config/v2_spec.ts b/config/v2_spec.ts new file mode 100644 index 0000000..1047832 --- /dev/null +++ b/config/v2_spec.ts @@ -0,0 +1,616 @@ +// deno-lint-ignore-file ban-types +import { DefaultString } from "../types.ts"; +import { matches } from "../dependencies.ts"; + +type Validator = matches.Validator; +const { shape, boolean, string, dictionary, anyOf, arrayOf, every, deferred, number, recursive, unknown } = matches; + +export const [matchConfig, setMatchConfig] = deferred(); +export type Config = { + [key: string]: AnyValue; +}; + +export const matchDescription = shape( + { + name: string, + description: string, + warning: string, + }, + ["description", "warning"] +); + +export const matchUniqueBy = recursive<_UniqueBy>((x) => anyOf(string, shape({ any: x }))).optional(); +type _UniqueBy = { any: _UniqueBy } | string; +type UniqueBy = null | undefined | _UniqueBy; + +export const matchBoolValue: Validator = shape({ + boolean: every( + matchDescription, + shape({ + default: boolean, + }) + ), +}); +export type BoolValue = { + boolean: { + name: string; + description?: string; + warning?: string; + default: boolean; + }; +}; + +export const matchDefaultString = anyOf( + string, + shape( + { + charset: string, + len: number, + }, + ["charset"] + ) +); + +export const matchStringValue: Validator = shape({ + string: every( + shape( + { + name: string, + nullable: boolean, + // optionals + description: string, + warning: string, + default: matchDefaultString, + copyable: boolean, + masked: boolean, + placeholder: string, + }, + ["description", "warning", "default", "copyable", "masked", "placeholder"] + ), + anyOf( + shape({}), + shape({ + pattern: string, + "pattern-description": string, + }) + ) + ), +}); +export type StringValue = { + string: { + name: string; + description?: string; + warning?: string; + default?: DefaultString; + nullable: boolean; + copyable?: boolean; + masked?: boolean; + placeholder?: string; + } & ( + | {} + | { + pattern: string; + "pattern-description": string; + } + ); +}; + +export const matchNumberValue: Validator = shape({ + number: every( + matchDescription, + shape( + { + default: number, + }, + ["default"] + ) + ), +}); +export type NumberValue = { + number: { + name: string; + default?: number; + description?: string; + warning?: string; + }; +}; + +export const matchEnumValue: Validator = shape({ + enum: shape( + { + name: string, + default: string, + values: arrayOf(string), + "value-names": dictionary([string, string]), + // optionals + description: string, + warning: string, + }, + ["description", "warning"] + ), +}); +export type EnumValue = { + enum: { + name: string; + default: string; + values: string[] | readonly string[]; + "value-names": { + [key: string]: string; + }; + // optionals + description?: string; + warning?: string; + }; +}; + +const matchListBooleanValue: Validator = shape({ + boolean: shape( + { + name: string, + default: arrayOf(boolean), + spec: shape({}), + range: string, + + //optionals + description: string, + warning: string, + }, + ["warning", "description"] + ), +}); +const matchListStringValue: Validator = shape({ + string: shape( + { + name: string, + default: arrayOf(string), + spec: every( + shape( + { + copyable: boolean, + masked: boolean, + placeholder: string, + }, + ["copyable", "masked", "placeholder"] + ), + anyOf( + shape({}), + shape({ + pattern: string, + "pattern-description": string, + }) + ) + ), + range: string, + + //optionals + description: string, + warning: string, + }, + ["warning", "description"] + ), +}); +const matchListNumberValue: Validator = shape({ + number: shape( + { + name: string, + default: arrayOf(number), + spec: shape( + { + range: string, + integral: boolean, + units: string, + placeholder: number, + }, + ["range", "integral", "units", "placeholder"] + ), + range: string, + + //optionals + description: string, + warning: string, + }, + ["warning", "description"] + ), +}); +const matchListEnumValue: Validator = shape({ + enum: shape( + { + name: string, + default: arrayOf(string), + spec: shape({ + values: arrayOf(string), + "value-names": dictionary([string, string]), + }), + range: string, + + //optionals + description: string, + warning: string, + }, + ["warning", "description"] + ), +}); +const matchListObjectValue: Validator = shape({ + object: shape( + { + name: string, + spec: shape( + { + spec: matchConfig, + "display-as": string, + "unique-by": matchUniqueBy, + }, + ["display-as", "unique-by"] + ), + range: string, + + //optionals + default: arrayOf(dictionary([string, unknown])), + description: string, + warning: string, + }, + ["default", "warning", "description"] + ), +}); +const matchListUnionValue: Validator = shape({ + union: shape( + { + name: string, + default: arrayOf(string), + spec: shape( + { + /** What tag for the specification, for tag unions */ + tag: shape( + { + id: string, + name: string, + description: string, + "variant-names": dictionary([string, string]), + }, + ["name", "description"] + ), + /** The possible enum values */ + variants: dictionary([string, matchConfig]), + "display-as": string, + "unique-by": matchUniqueBy, + }, + ["display-as", "unique-by"] + ), + range: string, + + //optionals + description: string, + warning: string, + }, + ["warning", "description"] + ), +}); +export const matchListValue: Validator = shape({ + list: anyOf( + matchListBooleanValue, + matchListStringValue, + matchListNumberValue, + matchListEnumValue, + matchListObjectValue, + matchListUnionValue + ), +}); +export type ListValue = { + list: + | { + boolean: { + name: string; + default: boolean[] | readonly boolean[]; + spec: {}; + range: string; + + //optionals + description?: string; + warning?: string; + }; + } + | { + string: { + name: string; + default: string[] | readonly string[]; + spec: + | { + copyable?: boolean | undefined; + masked?: boolean | undefined; + placeholder?: string | undefined; + } + | ({ + pattern: string; + "pattern-description": string; + } & {}); + range: string; + + //optionals + description?: string; + warning?: string; + }; + } + | { + number: { + name: string; + default: number[] | readonly number[]; + spec: { + range?: string | undefined; + integral?: boolean | undefined; + units?: string | undefined; + placeholder?: number | undefined; + }; + range: string; + + //optionals + description?: string; + warning?: string; + }; + } + | { + enum: { + name: string; + default: string[] | readonly string[]; + spec: { + values: string[] | readonly string[]; + "value-names": { + [key: string]: string; + }; + }; + range: string; + + //optionals + description?: string; + warning?: string; + }; + } + | { + object: { + name: string; + spec: { + spec: Config; + "display-as"?: string | undefined; + "unique-by"?: UniqueBy | undefined; + }; + range: string; + + //optionals + default?: Record[] | readonly Record[]; + description?: string; + warning?: string; + }; + } + | { + union: { + name: string; + default: string[] | readonly string[]; + spec: { + tag: { + id: string; + name?: string | undefined; + description?: string | undefined; + "variant-names": { + [key: string]: string; + }; + }; + variants: { + [key: string]: Config; + }; + "display-as"?: string | undefined; + "unique-by"?: UniqueBy | undefined; + }; + range: string; + + //optionals + description?: string; + warning?: string; + }; + }; +}; + +export const matchObjectValue: Validator = shape({ + object: shape( + { + name: string, + default: matchConfig, + values: arrayOf(string), + "value-names": dictionary([string, string]), + // optionals + description: string, + warning: string, + }, + ["description", "warning"] + ), +}); +export type ObjectValue = { + object: { + name: string; + default: Config; + values: string[] | readonly string[]; + "value-names": { + [key: string]: string; + }; + // optionals + description?: string; + warning?: string; + }; +}; + +export const matchUnionValue: Validator = shape({ + union: shape( + { + name: string, + default: string, + tag: shape( + { + id: string, + "variant-names": dictionary([string, string]), + // optionals + name: string, + description: string, + }, + ["name", "description"] + ), + variants: dictionary([string, matchConfig]), + + //optionals + description: string, + warning: string, + "display-as": string, + "unique-by": matchUniqueBy, + }, + ["description", "warning", "display-as", "unique-by"] + ), +}); +export type UnionValue = { + union: { + name: string; + default: string; + tag: { + id: string; + name?: string | undefined; + description?: string | undefined; + "variant-names": { + [key: string]: string; + }; + }; + variants: { + [key: string]: Config; + }; + + //optionals + description?: string; + warning?: string; + "display-as"?: string | undefined; + "unique-by"?: UniqueBy | undefined; + }; +}; + +export const matchPointerValue: Validator = shape({ + pointer: every( + shape( + { + name: string, + + //optionals + description: string, + warning: string, + }, + ["description", "warning"] + ), + anyOf( + shape({ + package: anyOf( + shape({ "tor-key": shape({ "package-id": string, inferface: string }) }), + shape({ "tor-address": shape({ "package-id": string, inferface: string }) }), + shape({ "lan-address": shape({ "package-id": string, inferface: string }) }), + shape({ config: shape({ "package-id": string, selector: string, multi: boolean }, ["multi"]) }) + ), + }), + shape({ + system: dictionary([string, unknown]), + }) + ) + ), +}); +export type PointerValue = { + pointer: { + name: string; + + //optionals + description?: string; + warning?: string; + } & ( + | { + package: + | { "tor-key": { "package-id": string; inferface: string } } + | { "tor-address": { "package-id": string; inferface: string } } + | { "lan-address": { "package-id": string; inferface: string } } + | { config: { "package-id": string; selector: string; multi?: boolean } }; + } + | { system: Record } + ); +}; + +export const matchAnyValue = anyOf( + matchBoolValue, + matchStringValue, + matchNumberValue, + matchEnumValue, + matchListValue, + matchObjectValue, + matchUnionValue, + matchPointerValue +); +export type AnyValue = typeof matchAnyValue._TYPE; + +export const matchSpec = dictionary([string, matchAnyValue]); +export type Spec = typeof matchSpec._TYPE; + +export type V2to1Spec = { + [key in keyof A]: V2to1AnyValue; +}; +// prettier-ignore +// deno-fmt-ignore +export type V2to1AnyValue = + A extends BoolValue ? ( + { + tag: "boolean", + } & A["boolean"] + ) : + A extends StringValue ? ( + { + tag: "string", + } & A["string"] + ) : + A extends NumberValue ? ( + { + tag: "number", + } & A["number"] + ) : + A extends EnumValue ? ( + { + tag: "enum", + subtype: keyof A["enum"] + } & A["enum"][keyof A["enum"]] + ) : + A extends ListValue ? ( + { + tag: "list", + } & A["list"] + ) : + A extends ObjectValue ? ( + { + tag: "object", + } & A["object"] + ) : + A extends PointerValue ? ( + { + name: string, description?: string, warning?: string,} + & ( + A["pointer"] extends {'package': {'tor-key': infer Target }} ? ( + {tag: "pointer", + subtype: 'package', target: 'tor-key' } & Target + ) : + A["pointer"] extends {'package': {'tor-address': infer Target }} ? ( + {tag: "pointer", + subtype: 'package', target: 'tor-address' } & Target + ) : + A["pointer"] extends {'package': {'lan-address': infer Target }} ? ( + {tag: "pointer", + subtype: 'package', target: 'lan-address' } & Target + ) : + A["pointer"] extends {'package': {'config': infer Target }} ? ( + {tag: "pointer", + subtype: 'package', target: 'config' } & Target + ) : + A["pointer"] extends {'system': infer System } ? ( + {tag: "pointer", + subtype: 'system'} & System + ) : + never + ) + ) : + never + +setMatchConfig(dictionary([string, matchAnyValue])); diff --git a/config/value.ts b/config/value.ts new file mode 100644 index 0000000..c656a8d --- /dev/null +++ b/config/value.ts @@ -0,0 +1,135 @@ +import { BuilderExtract, IBuilder } from "./builder.ts"; +import { Config } from "./config.ts"; +import { List } from "./list.ts"; +import { Pointer } from "./pointer.ts"; + +export type Description = { + name: string; + description: string | null; + warning: string | null; +}; +export type Default = { + default: A; +}; +export type NullableDefault = { + default: A | null; +}; + +export type StringSpec = { + copyable: boolean | null; + masked: boolean | null; + placeholder: string | null; +} & ( + | { + pattern: string; + "pattern-description": string; + } + // deno-lint-ignore ban-types + | {} +); +export type NumberSpec = { + range: string | null; + integral: boolean | null; + units: string | null; + placeholder: number | null; +}; +export type Nullable = { + nullable: boolean; +}; + +type _UniqueBy = + | null + | string + | { + any: string; + }; + +export class Value extends IBuilder { + static boolean>(a: A) { + return new Value({ + type: "boolean" as const, + ...a, + }); + } + static string & Nullable & StringSpec>(a: A) { + return new Value({ + type: "string" as const, + ...a, + }); + } + static number & Nullable & NumberSpec>(a: A) { + return new Value({ + type: "number" as const, + ...a, + }); + } + static enum< + A extends Description & + Default & { values: readonly string[] | string[]; "value-names": Record } + >(a: A) { + return new Value({ + type: "enum" as const, + ...a, + }); + } + static object< + A extends Description & + NullableDefault> & { values: readonly string[] | string[]; "value-names": Record }, + B + >(a: A) { + const { default: previousDefault, ...rest } = a; + if (previousDefault == null) { + return new Value({ + type: "object" as const, + ...rest, + }); + } + return new Value({ + type: "object" as const, + ...rest, + default: previousDefault.build(), + }); + } + static union< + A extends Description & + Default & { + tag: { + id: string; + name: string | null; + description: string | null; + "variant-names": { + [key: string]: string; + }; + }; + variants: Variants; + "display-as": string | null; + "unique-by": _UniqueBy | null; + }, + Variants extends { + [key: string]: Config; + } + >(a: A) { + const { variants: previousVariants, ...rest } = a; + // deno-lint-ignore no-explicit-any + const variants: { [K in keyof Variants]: BuilderExtract } = {} as any; + for (const key in previousVariants) { + // deno-lint-ignore no-explicit-any + variants[key] = previousVariants[key].build() as any; + } + return new Value({ + type: "union" as const, + ...rest, + variants, + }); + } + + static pointer(a: Pointer) { + return new Value(a.build()); + } + static list, B>(a: A) { + return new Value({ + type: "list" as const, + ...a.build(), + }); + } +} diff --git a/dependencies.ts b/dependencies.ts index 8007570..1eaec6f 100644 --- a/dependencies.ts +++ b/dependencies.ts @@ -1,2 +1,2 @@ -export * as matches from "https://deno.land/x/ts_matches@v5.2.0/mod.ts"; +export * as matches from "https://deno.land/x/ts_matches@v5.3.0/mod.ts"; export * as YAML from "https://deno.land/std@0.140.0/encoding/yaml.ts"; diff --git a/mod.ts b/mod.ts index 23cd38c..c146c6f 100644 --- a/mod.ts +++ b/mod.ts @@ -4,4 +4,4 @@ export * as compat from "./compat/mod.ts"; export * as migrations from "./migrations.ts"; export * as healthUtil from "./healthUtil.ts"; export * as util from "./util.ts"; -export { Backups } from "./backups.ts"; \ No newline at end of file +export { Backups } from "./backups.ts"; diff --git a/test.ts b/test.ts index daadb3a..afe6e4a 100644 --- a/test.ts +++ b/test.ts @@ -1,2 +1,3 @@ import "./emver-lite/test.ts"; import "./utils/propertiesMatcher.test.ts"; +import "./config/index.test.ts"; diff --git a/types.ts b/types.ts index d3702a5..d24698f 100644 --- a/types.ts +++ b/types.ts @@ -1,10 +1,7 @@ // deno-lint-ignore no-namespace export namespace ExpectedExports { /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */ - export type setConfig = ( - effects: Effects, - input: Config, - ) => Promise>; + export type setConfig = (effects: Effects, input: Config) => Promise>; /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */ export type getConfig = (effects: Effects) => Promise>; /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ @@ -12,31 +9,31 @@ export namespace ExpectedExports { /** For backing up service data though the embassyOS UI */ export type createBackup = (effects: Effects) => Promise>; /** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */ - export type restoreBackup = ( - effects: Effects, - ) => Promise>; + export type restoreBackup = (effects: Effects) => Promise>; /** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */ - export type properties = ( - effects: Effects, - ) => Promise>; + export type properties = (effects: Effects) => Promise>; + /** Health checks are used to determine if the service is working properly after starting + * A good use case is if we are using a web server, seeing if we can get to the web server. + */ export type health = { /** Should be the health check id */ - [id: string]: ( - effects: Effects, - dateMs: number, - ) => Promise>; + [id: string]: (effects: Effects, dateMs: number) => Promise>; }; - export type migration = ( - effects: Effects, - version: string, - ...args: unknown[] - ) => Promise>; + + /** + * Migrations are used when we are changing versions when updating/ downgrading. + * There are times that we need to move files around, and do other operations during a migration. + */ + export type migration = (effects: Effects, version: string, ...args: unknown[]) => Promise>; + + /** + * Actions are used so we can effect the service, like deleting a directory. + * One old use case is to add a action where we add a file, that will then be run during the + * service starting, and that file would indicate that it would rescan all the data. + */ export type action = { - [id: string]: ( - effects: Effects, - config?: Config, - ) => Promise>; + [id: string]: (effects: Effects, config?: Config) => Promise>; }; /** @@ -49,30 +46,24 @@ export namespace ExpectedExports { /** Used to reach out from the pure js runtime */ export type Effects = { /** Usable when not sandboxed */ - writeFile( - input: { path: string; volumeId: string; toWrite: string }, - ): Promise; + writeFile(input: { path: string; volumeId: string; toWrite: string }): Promise; readFile(input: { volumeId: string; path: string }): Promise; metadata(input: { volumeId: string; path: string }): Promise; /** Create a directory. Usable when not sandboxed */ createDir(input: { volumeId: string; path: string }): Promise; + /** Create a directory. Usable when not sandboxed */ + createDir(input: { volumeId: string; path: string }): Promise; /** Remove a directory. Usable when not sandboxed */ removeDir(input: { volumeId: string; path: string }): Promise; removeFile(input: { volumeId: string; path: string }): Promise; /** Write a json file into an object. Usable when not sandboxed */ - writeJsonFile( - input: { volumeId: string; path: string; toWrite: Record }, - ): Promise; + writeJsonFile(input: { volumeId: string; path: string; toWrite: Record }): Promise; /** Read a json file into an object */ - readJsonFile( - input: { volumeId: string; path: string }, - ): Promise>; + readJsonFile(input: { volumeId: string; path: string }): Promise>; - runCommand( - input: { command: string; args?: string[]; timeoutMillis?: number }, - ): Promise>; + runCommand(input: { command: string; args?: string[]; timeoutMillis?: number }): Promise>; runDaemon(input: { command: string; args?: string[] }): { wait(): Promise>; term(): Promise; @@ -102,7 +93,7 @@ export type Effects = { method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH"; headers?: Record; body?: string; - }, + } ): Promise<{ method: string; ok: boolean; @@ -209,8 +200,8 @@ export type Target = V & { export type UniqueBy = | { - any: UniqueBy[]; - } + any: UniqueBy[]; + } | string | null; @@ -220,24 +211,24 @@ export type WithNullable = T & { export type DefaultString = | string | { - /** The chars available for the randome generation */ - charset?: string; - /** Length that we generate to */ - len: number; - }; + /** The chars available for the randome generation */ + charset?: string; + /** Length that we generate to */ + len: number; + }; -export type ValueSpecString = // deno-lint-ignore ban-types - ( - | {} - | { +export type ValueSpecString = ( + | never // deno-lint-ignore ban-types + | {} + | { pattern: string; "pattern-description": string; } - ) & { - copyable?: boolean; - masked?: boolean; - placeholder?: string; - }; +) & { + copyable?: boolean; + masked?: boolean; + placeholder?: string; +}; export type ValueSpecNumber = { /** Something like [3,6] or [0, *) */ range?: string; @@ -249,76 +240,68 @@ export type ValueSpecNumber = { export type ValueSpecBoolean = Record; export type ValueSpecAny = | Tag<"boolean", WithDescription>> + | Tag<"string", WithDescription, DefaultString>>> + | Tag<"number", WithDescription, number>>> | Tag< - "string", - WithDescription< - WithNullableDefault, DefaultString> - > - > - | Tag< - "number", - WithDescription, number>> - > - | Tag< - "enum", - WithDescription< - WithDefault< - { - values: readonly string[] | string[]; - "value-names": { - [key: string]: string; - }; - }, - string + "enum", + WithDescription< + WithDefault< + { + values: readonly string[] | string[]; + "value-names": { + [key: string]: string; + }; + }, + string + > > > - > | Tag<"list", ValueSpecList> | Tag<"object", WithDescription>> | Tag<"union", WithDescription>> | Tag< - "pointer", - WithDescription< - | Subtype< - "package", - | Target< - "tor-key", - { - "package-id": string; - interface: string; - } - > - | Target< - "tor-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "lan-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "config", - { - "package-id": string; - selector: string; - multi: boolean; - } - > + "pointer", + WithDescription< + | Subtype< + "package", + | Target< + "tor-key", + { + "package-id": string; + interface: string; + } + > + | Target< + "tor-address", + { + "package-id": string; + interface: string; + } + > + | Target< + "lan-address", + { + "package-id": string; + interface: string; + } + > + | Target< + "config", + { + "package-id": string; + selector: string; + multi: boolean; + } + > + > + | Subtype<"system", Record> > - | Subtype<"system", Record> - > - >; + >; export type ValueSpecUnion = { /** What tag for the specification, for tag unions */ tag: { id: string; - name: string; + name?: string; description?: string; "variant-names": { [key: string]: string; @@ -337,32 +320,12 @@ export type ValueSpecObject = { "unique-by"?: UniqueBy; }; export type ValueSpecList = - | Subtype< - "boolean", - WithDescription, boolean[]>> - > - | Subtype< - "string", - WithDescription, string[]>> - > - | Subtype< - "number", - WithDescription, number[]>> - > - | Subtype< - "enum", - WithDescription, string[]>> - > - | Subtype< - "object", - WithDescription< - WithNullableDefault, Record[]> - > - > - | Subtype< - "union", - WithDescription, string[]>> - >; + | Subtype<"boolean", WithDescription, boolean[]>>> + | Subtype<"string", WithDescription, string[]>>> + | Subtype<"number", WithDescription, number[]>>> + | Subtype<"enum", WithDescription, string[]>>> + | Subtype<"object", WithDescription, Record[]>>> + | Subtype<"union", WithDescription, string[]>>>; export type ValueSpecEnum = { values: string[]; "value-names": { [key: string]: string }; @@ -414,8 +377,8 @@ export type DependsOn = { export type KnownError = | { error: string } | { - "error-code": [number, string] | readonly [number, string]; - }; + "error-code": [number, string] | readonly [number, string]; + }; export type ResultType = KnownError | { result: T }; export type PackagePropertiesV2 = {