From e279711f8e1ed096a4ed44e4d357474f9537b35a Mon Sep 17 00:00:00 2001 From: BluJ Date: Wed, 19 Apr 2023 17:23:16 -0600 Subject: [PATCH] feat: add autoConfig/ better types for wrapperData --- lib/autoconfig/AutoConfig.ts | 100 ++++++++-------------- lib/autoconfig/autoconfigSetup.ts | 14 --- lib/autoconfig/index.ts | 2 +- lib/autoconfig/setupAutoConfig.ts | 9 ++ lib/backup/index.ts | 12 +-- lib/config/builder/config.ts | 4 +- lib/config/builder/list.ts | 6 +- lib/config/builder/value.ts | 6 +- lib/config/builder/variants.ts | 6 +- lib/config/configTypes.ts | 2 +- lib/config/setupConfigExports.ts | 48 ++++++----- lib/config/specToBuilder.ts | 20 ++--- lib/emverLite/mod.ts | 2 +- lib/health/checkFns/checkPortListening.ts | 2 +- lib/health/checkFns/checkWebUrl.ts | 2 +- lib/health/checkFns/index.ts | 2 +- lib/health/checkFns/runHealthScript.ts | 2 +- lib/init/index.ts | 6 +- lib/mainFn/Daemons.ts | 16 ++-- lib/mainFn/NetworkInterfaceBuilder.ts | 2 +- lib/mainFn/Origin.ts | 2 +- lib/mainFn/index.ts | 2 +- lib/properties/index.ts | 2 +- lib/test/configBuilder.test.ts | 22 ++--- lib/test/configTypes.test.ts | 2 +- lib/test/makeOutput.ts | 2 +- lib/test/output.test.ts | 16 ++-- lib/test/wrapperData.test.ts | 81 ++++++++++++++++++ lib/types.ts | 54 ++++++++---- lib/util/deepEqual.ts | 19 ++++ lib/util/deepMerge.ts | 16 ++++ lib/util/extensions.ts | 2 +- lib/util/fileHelper.ts | 18 ++-- lib/util/getWrapperData.ts | 82 ++++++++++-------- lib/util/index.ts | 41 ++++++--- lib/util/propertiesMatcher.ts | 36 ++++---- package-lock.json | 4 +- package.json | 8 +- scripts/oldSpecToBuilder.ts | 50 +++++------ 39 files changed, 431 insertions(+), 291 deletions(-) delete mode 100644 lib/autoconfig/autoconfigSetup.ts create mode 100644 lib/autoconfig/setupAutoConfig.ts create mode 100644 lib/test/wrapperData.test.ts create mode 100644 lib/util/deepEqual.ts create mode 100644 lib/util/deepMerge.ts diff --git a/lib/autoconfig/AutoConfig.ts b/lib/autoconfig/AutoConfig.ts index 966389c..115a62a 100644 --- a/lib/autoconfig/AutoConfig.ts +++ b/lib/autoconfig/AutoConfig.ts @@ -1,70 +1,42 @@ -import deepmerge from "deepmerge"; -import { AutoConfigure, Effects } from "../types"; -import { Message, MaybePromise, ReadonlyDeep } from "."; +import { AutoConfigure, Effects, ExpectedExports } from "../types"; +import { deepEqual, deepMerge } from "../util"; -class AutoConfigBuilt implements AutoConfigure { - constructor(private autoConfig: AutoConfig) {} - - async check(effects: Effects, config: Config): Promise { - for (const [message, configure] of this.autoConfig.getConfigures()) { - const value = await configure({ effects, config }); - if (value !== null) { - throw new Error(message); - } - } - } - /** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */ - async autoConfigure(effects: Effects, config: Config): Promise { - const input = { effects, config }; - const newOverwrites = ( - await Promise.all(this.autoConfig.getConfigures().map((x) => x[1](input))) - ).filter((x): x is NonNullable => x !== null); - return deepmerge.all([config, ...newOverwrites]); - } -} -export class AutoConfig { - private constructor( - private configures: Array< - [ - Message, - ( - options: Readonly<{ config: Config; effects: Effects }> - ) => MaybePromise> - ] - > +export type AutoConfigFrom = { + [key: string]: (options: { + effects: Effects; + localConfig: unknown; + remoteConfig: unknown; + }) => Promise>; +}; +export class AutoConfig { + constructor( + readonly configs: AutoConfigFrom, + readonly path: keyof AutoConfigFrom, ) {} - getConfigures(): ReadonlyDeep< - Array< - [ - Message, - ( - options: Readonly<{ config: Config; effects: Effects }> - ) => MaybePromise> - ] - > - > { - return this.configures; - } - static autoConfig( - message: Message, - configure: ( - options: Readonly<{ config: Config; effects: Effects }> - ) => MaybePromise> - ): AutoConfig { - return new AutoConfig([[message, configure]]); + async check( + options: Parameters[0], + ): ReturnType { + const origConfig = JSON.parse(JSON.stringify(options.localConfig)); + if ( + !deepEqual( + origConfig, + deepMerge( + {}, + options.localConfig, + await this.configs[this.path](options), + ), + ) + ) + throw new Error(`Check failed for ${this.path}`); } - autoConfig( - message: Message, - configure: ( - options: Readonly<{ config: Config; effects: Effects }> - ) => MaybePromise> - ): AutoConfig { - this.configures.push([message, configure]); - return this; - } - - build() { - return new AutoConfigBuilt(this); + async autoConfigure( + options: Parameters[0], + ): ReturnType { + return deepMerge( + {}, + options.localConfig, + await this.configs[this.path](options), + ); } } diff --git a/lib/autoconfig/autoconfigSetup.ts b/lib/autoconfig/autoconfigSetup.ts deleted file mode 100644 index 388bd83..0000000 --- a/lib/autoconfig/autoconfigSetup.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ExpectedExports, PackageId } from "../types"; -import { AutoConfig } from "./AutoConfig"; - -export function autoconfigSetup( - autoconfigs: Record> -) { - const autoconfig: ExpectedExports.autoConfig = {}; - - for (const [id, autoconfigValue] of Object.entries(autoconfigs)) { - autoconfig[id] = autoconfigValue.build(); - } - - return autoconfig; -} diff --git a/lib/autoconfig/index.ts b/lib/autoconfig/index.ts index 68192f5..df6fc2c 100644 --- a/lib/autoconfig/index.ts +++ b/lib/autoconfig/index.ts @@ -6,4 +6,4 @@ export type MaybePromise = Promise | A; export type Message = string; export { AutoConfig } from "./AutoConfig"; -export { autoconfigSetup } from "./autoconfigSetup"; +export { setupAutoConfig } from "./setupAutoConfig"; diff --git a/lib/autoconfig/setupAutoConfig.ts b/lib/autoconfig/setupAutoConfig.ts new file mode 100644 index 0000000..55a459a --- /dev/null +++ b/lib/autoconfig/setupAutoConfig.ts @@ -0,0 +1,9 @@ +import { AutoConfig, AutoConfigFrom } from "./AutoConfig"; + +export function setupAutoConfig(configs: C) { + const answer = { ...configs } as unknown as { [k in keyof C]: AutoConfig }; + for (const key in configs) { + answer[key] = new AutoConfig(configs, key); + } + return answer; +} diff --git a/lib/backup/index.ts b/lib/backup/index.ts index 32ccd1a..e6a4026 100644 --- a/lib/backup/index.ts +++ b/lib/backup/index.ts @@ -40,7 +40,7 @@ export class Backups { constructor( private options = DEFAULT_OPTIONS, - private backupSet = [] as BackupSet[] + private backupSet = [] as BackupSet[], ) {} static volumes(...volumeNames: string[]) { return new Backups().addSets( @@ -49,7 +49,7 @@ export class Backups { srcPath: "./", dstPath: `./${srcVolume}/`, dstVolume: Backups.BACKUP, - })) + })), ); } static addSets(...options: BackupSet[]) { @@ -72,12 +72,12 @@ export class Backups { srcPath: "./", dstPath: `./${srcVolume}/`, dstVolume: Backups.BACKUP, - })) + })), ); } addSets(...options: BackupSet[]) { options.forEach((x) => - this.backupSet.push({ ...x, options: { ...this.options, ...x.options } }) + this.backupSet.push({ ...x, options: { ...this.options, ...x.options } }), ); return this; } @@ -98,7 +98,7 @@ export class Backups { .map((x) => x.dstPath) .map((x) => x.replace(/\.\/([^]*)\//, "$1")); const filteredItems = previousItems.filter( - (x) => backupPaths.indexOf(x) === -1 + (x) => backupPaths.indexOf(x) === -1, ); for (const itemToRemove of filteredItems) { effects.error(`Trying to remove ${itemToRemove}`); @@ -111,7 +111,7 @@ export class Backups { effects.removeFile({ volumeId: Backups.BACKUP, path: itemToRemove, - }) + }), ) .catch(() => { effects.warn(`Failed to remove ${itemToRemove} from backup volume`); diff --git a/lib/config/builder/config.ts b/lib/config/builder/config.ts index d63568a..4741f40 100644 --- a/lib/config/builder/config.ts +++ b/lib/config/builder/config.ts @@ -65,13 +65,13 @@ export class Config extends IBuilder { } static withValue( key: K, - value: Value + value: Value, ) { return Config.empty().withValue(key, value); } static addValue( key: K, - value: Value + value: Value, ) { return Config.empty().withValue(key, value); } diff --git a/lib/config/builder/list.ts b/lib/config/builder/list.ts index b59c398..12ad60c 100644 --- a/lib/config/builder/list.ts +++ b/lib/config/builder/list.ts @@ -40,7 +40,7 @@ export class List extends IBuilder { patternDescription?: string | null; /** Default = "text" */ inputmode?: ListValueSpecString["inputmode"]; - } + }, ) { const spec = { type: "string" as const, @@ -77,7 +77,7 @@ export class List extends IBuilder { range?: string; units?: string | null; placeholder?: string | null; - } + }, ) { const spec = { type: "number" as const, @@ -111,7 +111,7 @@ export class List extends IBuilder { spec: Spec; displayAs?: null | string; uniqueBy?: null | UniqueBy; - } + }, ) { const { spec: previousSpecSpec, ...restSpec } = aSpec; const specSpec = previousSpecSpec.build() as BuilderExtract; diff --git a/lib/config/builder/value.ts b/lib/config/builder/value.ts index 12f561d..39f6c08 100644 --- a/lib/config/builder/value.ts +++ b/lib/config/builder/value.ts @@ -154,7 +154,7 @@ export class Value extends IBuilder { description?: string | null; warning?: string | null; }, - previousSpec: Spec + previousSpec: Spec, ) { const spec = previousSpec.build() as BuilderExtract; return new Value({ @@ -166,7 +166,7 @@ export class Value extends IBuilder { }); } static union< - V extends Variants<{ [key: string]: { name: string; spec: InputSpec } }> + V extends Variants<{ [key: string]: { name: string; spec: InputSpec } }>, >( a: { name: string; @@ -175,7 +175,7 @@ export class Value extends IBuilder { required: boolean; default?: string | null; }, - aVariants: V + aVariants: V, ) { const variants = aVariants.build() as BuilderExtract; return new Value({ diff --git a/lib/config/builder/variants.ts b/lib/config/builder/variants.ts index 26bfc46..99eb515 100644 --- a/lib/config/builder/variants.ts +++ b/lib/config/builder/variants.ts @@ -57,12 +57,12 @@ export class Variants< name: string; spec: InputSpec; }; - } + }, > extends IBuilder { static of< A extends { [key: string]: { name: string; spec: Config }; - } + }, >(a: A) { const variants: { [K in keyof A]: { name: string; spec: BuilderExtract }; @@ -82,7 +82,7 @@ export class Variants< } static withVariant( key: K, - value: Config + value: Config, ) { return Variants.empty().withVariant(key, value); } diff --git a/lib/config/configTypes.ts b/lib/config/configTypes.ts index 333ee3e..6da0dd1 100644 --- a/lib/config/configTypes.ts +++ b/lib/config/configTypes.ts @@ -160,7 +160,7 @@ export type DefaultString = // sometimes the type checker needs just a little bit of help export function isValueSpecListOf( t: ValueSpecListOf, - s: S + s: S, ): t is ValueSpecListOf & { spec: ListValueSpecOf } { return t.spec.type === s; } diff --git a/lib/config/setupConfigExports.ts b/lib/config/setupConfigExports.ts index 4dbce26..9e25cf5 100644 --- a/lib/config/setupConfigExports.ts +++ b/lib/config/setupConfigExports.ts @@ -1,18 +1,25 @@ import { Config } from "./builder"; -import { DeepPartial, Dependencies, DependsOn, Effects, ExpectedExports } from "../types"; +import { + DeepPartial, + Dependencies, + DependsOn, + Effects, + ExpectedExports, +} from "../types"; import { InputSpec } from "./configTypes"; import { nullIfEmpty } from "../util"; import { TypeFromProps } from "../util/propertiesMatcher"; -export type Write = (options: { effects: Effects; input: TypeFromProps }) => Promise; -export type Read = (options: { - effects: Effects; - config: ConfigType; -}) => Promise>>; -export type DependenciesFn = (options: { +export type Write = (options: { + effects: Effects; + input: TypeFromProps; +}) => Promise; +export type Read = (options: { + effects: Effects; +}) => Promise>>; +export type DependenciesFn = (options: { effects: Effects; input: TypeFromProps; - config: ConfigType; }) => Promise; /** * We want to setup a config export with a get and set, this @@ -21,31 +28,30 @@ export type DependenciesFn = (options: { * @param options * @returns */ -export function setupConfigExports(options: { - spec: Config; - write: Write; - read: Read; - dependencies: DependenciesFn; -}) { - const validator = options.spec.validator(); +export function setupConfigExports( + spec: Config, + write: Write, + read: Read, + dependencies: DependenciesFn, +) { + const validator = spec.validator(); return { setConfig: (async ({ effects, input }) => { if (!validator.test(input)) { await effects.error(String(validator.errorMessage(input))); return { error: "Set config type error for config" }; } - const config = await options.write({ + await write({ input: JSON.parse(JSON.stringify(input)), effects, }); - const dependencies = (await options.dependencies({ effects, input, config })) || []; - await effects.setDependencies(dependencies); - await effects.setWrapperData({ path: "config", value: config || null }); + const dependenciesToSet = (await dependencies({ effects, input })) || []; + await effects.setDependencies(dependenciesToSet); }) as ExpectedExports.setConfig, getConfig: (async ({ effects, config }) => { return { - spec: options.spec.build(), - config: nullIfEmpty(await options.read({ effects, config: config as ConfigType })), + spec: spec.build(), + config: nullIfEmpty(await read({ effects })), }; }) as ExpectedExports.getConfig, }; diff --git a/lib/config/specToBuilder.ts b/lib/config/specToBuilder.ts index a3b470a..a70cc7a 100644 --- a/lib/config/specToBuilder.ts +++ b/lib/config/specToBuilder.ts @@ -6,15 +6,15 @@ import * as C from "./configTypes"; export async function specToBuilderFile( file: string, inputData: Promise | InputSpec, - options: Parameters[1] + options: Parameters[1], ) { await fs.writeFile(file, await specToBuilder(inputData, options), (err) => - console.error(err) + console.error(err), ); } export async function specToBuilder( inputData: Promise | InputSpec, - { startSdk = "start-sdk" } = {} + { startSdk = "start-sdk" } = {}, ) { const outputLines: string[] = []; outputLines.push(` @@ -26,10 +26,10 @@ export async function specToBuilder( const configName = newConst("InputSpec", convertInputSpec(data)); const configMatcherName = newConst( "matchInputSpec", - `${configName}.validator()` + `${configName}.validator()`, ); outputLines.push( - `export type InputSpec = typeof ${configMatcherName}._TYPE;` + `export type InputSpec = typeof ${configMatcherName}._TYPE;`, ); return outputLines.join("\n"); @@ -73,7 +73,7 @@ export async function specToBuilder( const { variants, type, ...rest } = value; const variantVariable = newConst( value.name + "_variants", - convertVariants(variants) + convertVariants(variants), ); return `Value.union(${JSON.stringify(rest)}, ${variantVariable})`; @@ -101,7 +101,7 @@ export async function specToBuilder( warning: value.warning || null, }, null, - 2 + 2, )}, ${JSON.stringify({ masked: spec?.masked || false, placeholder: spec?.placeholder || null, @@ -120,7 +120,7 @@ export async function specToBuilder( warning: value.warning || null, }, null, - 2 + 2, )}, ${JSON.stringify({ range: spec?.range || null, integral: spec?.integral || false, @@ -131,7 +131,7 @@ export async function specToBuilder( case "object": { const specName = newConst( value.name + "_spec", - convertInputSpec(spec.spec) + convertInputSpec(spec.spec), ); return `List.obj({ name: ${JSON.stringify(value.name || null)}, @@ -155,7 +155,7 @@ export async function specToBuilder( name: string; spec: C.InputSpec; } - > + >, ): string { let answer = "Variants.of({"; for (const [key, { name, spec }] of Object.entries(variants)) { diff --git a/lib/emverLite/mod.ts b/lib/emverLite/mod.ts index cb23e43..5f66c44 100644 --- a/lib/emverLite/mod.ts +++ b/lib/emverLite/mod.ts @@ -248,7 +248,7 @@ export class Checker { * Check is the function that will be given a emver or unparsed emver and should give if it follows * a pattern */ - public readonly check: (value: string | EmVer) => boolean + public readonly check: (value: string | EmVer) => boolean, ) {} /** diff --git a/lib/health/checkFns/checkPortListening.ts b/lib/health/checkFns/checkPortListening.ts index 2e7fb64..9d840cb 100644 --- a/lib/health/checkFns/checkPortListening.ts +++ b/lib/health/checkFns/checkPortListening.ts @@ -22,7 +22,7 @@ export async function checkPortListening( { error = `Port ${port} is not listening`, message = `Port ${port} is available`, - } = {} + } = {}, ): Promise { const hasAddress = containsAddress(await effects.runCommand(`cat /proc/net/tcp`), port) || diff --git a/lib/health/checkFns/checkWebUrl.ts b/lib/health/checkFns/checkWebUrl.ts index a8ebf45..8f9ffdf 100644 --- a/lib/health/checkFns/checkWebUrl.ts +++ b/lib/health/checkFns/checkWebUrl.ts @@ -15,7 +15,7 @@ export const checkWebUrl = async ( timeout = 1000, successMessage = `Reached ${url}`, errorMessage = `Error while fetching URL: ${url}`, - } = {} + } = {}, ): Promise => { return Promise.race([effects.fetch(url), timeoutPromise(timeout)]) .then((x) => ({ diff --git a/lib/health/checkFns/index.ts b/lib/health/checkFns/index.ts index b4fb0bb..184f273 100644 --- a/lib/health/checkFns/index.ts +++ b/lib/health/checkFns/index.ts @@ -5,7 +5,7 @@ export { checkWebUrl } from "./checkWebUrl"; export function timeoutPromise(ms: number, { message = "Timed out" } = {}) { return new Promise((resolve, reject) => - setTimeout(() => reject(new Error(message)), ms) + setTimeout(() => reject(new Error(message)), ms), ); } export { runHealthScript }; diff --git a/lib/health/checkFns/runHealthScript.ts b/lib/health/checkFns/runHealthScript.ts index 0808972..f84b942 100644 --- a/lib/health/checkFns/runHealthScript.ts +++ b/lib/health/checkFns/runHealthScript.ts @@ -17,7 +17,7 @@ export const runHealthScript = async ( errorMessage = `Error while running command: ${runCommand}`, message = (res: string) => `Have ran script ${runCommand} and the result: ${res}`, - } = {} + } = {}, ): Promise => { const res = await Promise.race([ effects.runCommand(runCommand, { timeoutMillis: timeout }), diff --git a/lib/init/index.ts b/lib/init/index.ts index 6132790..812706c 100644 --- a/lib/init/index.ts +++ b/lib/init/index.ts @@ -28,7 +28,7 @@ export function noMigrationsDown(): MigrationDownReceipt { return {} as MigrationDownReceipt; } export function migrationDown( - fn: () => Promise + fn: () => Promise, ): MigrationDownReceipt { fn(); return {} as MigrationDownReceipt; @@ -37,7 +37,7 @@ export function migrationDown( export function setupInit( fn: ( ...args: Parameters - ) => Promise<[MigrationReceipt, ActionReceipt]> + ) => Promise<[MigrationReceipt, ActionReceipt]>, ) { const initFn: ExpectedExports.init = (...args) => fn(...args); return initFn; @@ -46,7 +46,7 @@ export function setupInit( export function setupUninit( fn: ( ...args: Parameters - ) => Promise<[MigrationDownReceipt]> + ) => Promise<[MigrationDownReceipt]>, ) { const uninitFn: ExpectedExports.uninit = (...args) => fn(...args); return uninitFn; diff --git a/lib/mainFn/Daemons.ts b/lib/mainFn/Daemons.ts index 163db49..540dc0c 100644 --- a/lib/mainFn/Daemons.ts +++ b/lib/mainFn/Daemons.ts @@ -7,7 +7,7 @@ import { InterfaceReceipt } from "./interfaceReceipt"; type Daemon< Ids extends string | never, Command extends string, - Id extends string + Id extends string, > = { id: Id; command: ValidIfNoStupidEscape | [string, ...string[]]; @@ -52,7 +52,7 @@ export class Daemons { private constructor( readonly effects: Effects, readonly started: (onTerm: () => void) => null, - readonly daemons?: Daemon[] + readonly daemons?: Daemon[], ) {} static of(config: { @@ -64,7 +64,7 @@ export class Daemons { return new Daemons(config.effects, config.started); } addDaemon( - newDaemon: Daemon + newDaemon: Daemon, ) { const daemons = ((this?.daemons ?? []) as any[]).concat(newDaemon); return new Daemons(this.effects, this.started, daemons); @@ -76,7 +76,7 @@ export class Daemons { const daemons = this.daemons ?? []; for (const daemon of daemons) { const requiredPromise = Promise.all( - daemon.requires?.map((id) => daemonsStarted[id]) ?? [] + daemon.requires?.map((id) => daemonsStarted[id]) ?? [], ); daemonsStarted[daemon.id] = requiredPromise.then(async () => { const { command } = daemon; @@ -100,15 +100,15 @@ export class Daemons { async term() { await Promise.all( Object.values>(daemonsStarted).map((x) => - x.then((x) => x.term()) - ) + x.then((x) => x.term()), + ), ); }, async wait() { await Promise.all( Object.values>(daemonsStarted).map((x) => - x.then((x) => x.wait()) - ) + x.then((x) => x.wait()), + ), ); }, }; diff --git a/lib/mainFn/NetworkInterfaceBuilder.ts b/lib/mainFn/NetworkInterfaceBuilder.ts index ec72320..936819a 100644 --- a/lib/mainFn/NetworkInterfaceBuilder.ts +++ b/lib/mainFn/NetworkInterfaceBuilder.ts @@ -13,7 +13,7 @@ export class NetworkInterfaceBuilder { basic?: null | { password: string; username: string }; path?: string; search?: Record; - } + }, ) {} async exportAddresses(addresses: Iterable) { diff --git a/lib/mainFn/Origin.ts b/lib/mainFn/Origin.ts index 5824323..2d1961c 100644 --- a/lib/mainFn/Origin.ts +++ b/lib/mainFn/Origin.ts @@ -8,7 +8,7 @@ export class Origin { username: string; } | null - | undefined + | undefined, ) { // prettier-ignore const urlAuth = !!(origin) ? `${origin.username}:${origin.password}@` : diff --git a/lib/mainFn/index.ts b/lib/mainFn/index.ts index d87d296..61252ed 100644 --- a/lib/mainFn/index.ts +++ b/lib/mainFn/index.ts @@ -25,7 +25,7 @@ export const runningMain: ( fn: (o: { effects: Effects; started(onTerm: () => void): null; - }) => Promise> + }) => Promise>, ) => ExpectedExports.main = (fn) => { return async (options) => { const result = await fn(options); diff --git a/lib/properties/index.ts b/lib/properties/index.ts index 395bdd3..4af6395 100644 --- a/lib/properties/index.ts +++ b/lib/properties/index.ts @@ -21,7 +21,7 @@ export type UnionToIntersection = ((x: T) => any) extends (x: infer R) => any export function setupPropertiesExport( fn: ( ...args: Parameters - ) => void | Promise | Promise<(PropertyGroup | PropertyString)[]> + ) => void | Promise | Promise<(PropertyGroup | PropertyString)[]>, ): ExpectedExports.properties { return (async (...args) => { const result = await fn(...args); diff --git a/lib/test/configBuilder.test.ts b/lib/test/configBuilder.test.ts index 93ea126..832bf32 100644 --- a/lib/test/configBuilder.test.ts +++ b/lib/test/configBuilder.test.ts @@ -43,7 +43,7 @@ describe("builder tests", () => { }}` .replaceAll("\n", " ") .replaceAll(/\s{2,}/g, "") - .replaceAll(": ", ":") + .replaceAll(": ", ":"), ); }); }); @@ -121,7 +121,7 @@ describe("values", () => { a: Value.boolean({ name: "test", }), - }) + }), ); const validator = value.validator(); validator.unsafeCast({ a: true }); @@ -138,7 +138,7 @@ describe("values", () => { name: "a", spec: Config.of({ b: Value.boolean({ name: "b" }) }), }, - }) + }), ); const validator = value.validator(); validator.unsafeCast({ unionSelectKey: "a", unionValueKey: { b: false } }); @@ -156,8 +156,8 @@ describe("values", () => { }, { integral: false, - } - ) + }, + ), ); const validator = value.validator(); validator.unsafeCast([1, 2, 3]); @@ -174,8 +174,8 @@ describe("Builder List", () => { }, { spec: Config.of({ test: Value.boolean({ name: "test" }) }), - } - ) + }, + ), ); const validator = value.validator(); validator.unsafeCast([{ test: true }]); @@ -187,8 +187,8 @@ describe("Builder List", () => { { name: "test", }, - {} - ) + {}, + ), ); const validator = value.validator(); validator.unsafeCast(["test", "text"]); @@ -200,8 +200,8 @@ describe("Builder List", () => { { name: "test", }, - { integral: true } - ) + { integral: true }, + ), ); const validator = value.validator(); validator.unsafeCast([12, 45]); diff --git a/lib/test/configTypes.test.ts b/lib/test/configTypes.test.ts index 0e6cf55..e42b1f1 100644 --- a/lib/test/configTypes.test.ts +++ b/lib/test/configTypes.test.ts @@ -21,7 +21,7 @@ describe("Config Types", () => { someList.spec satisfies ListValueSpecOf<"object">; } else { throw new Error( - "Failed to figure out the type: " + JSON.stringify(someList) + "Failed to figure out the type: " + JSON.stringify(someList), ); } } diff --git a/lib/test/makeOutput.ts b/lib/test/makeOutput.ts index 1d62d25..61115b5 100644 --- a/lib/test/makeOutput.ts +++ b/lib/test/makeOutput.ts @@ -407,5 +407,5 @@ writeConvertedFile( }, { startSdk: "../", - } + }, ); diff --git a/lib/test/output.test.ts b/lib/test/output.test.ts index ed39cd5..c26fa58 100644 --- a/lib/test/output.test.ts +++ b/lib/test/output.test.ts @@ -20,7 +20,7 @@ function isObject(item: unknown): item is object { return !!(item && typeof item === "object" && !Array.isArray(item)); } type UnionToIntersection = (T extends any ? (x: T) => any : never) extends ( - x: infer R + x: infer R, ) => any ? R : never; @@ -61,14 +61,14 @@ testOutput< >()(null); testOutput()(null); testOutput()( - null + null, ); testOutput< InputSpec["testListUnion"][0]["union"][UnionValueKey]["name"], string >()(null); testOutput()( - null + null, ); // prettier-ignore // @ts-expect-error Expect that the string is the one above @@ -139,19 +139,19 @@ describe("Inputs", () => { test("test errors", () => { expect(() => matchInputSpec.unsafeCast( - mergeDeep(validInput, { rpc: { advanced: { threads: 0 } } }) - ) + mergeDeep(validInput, { rpc: { advanced: { threads: 0 } } }), + ), ).toThrowError(); expect(() => - matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { enable: 2 } })) + matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { enable: 2 } })), ).toThrowError(); expect(() => matchInputSpec.unsafeCast( mergeDeep(validInput, { rpc: { advanced: { serialversion: "testing" } }, - }) - ) + }), + ), ).toThrowError(); }); }); diff --git a/lib/test/wrapperData.test.ts b/lib/test/wrapperData.test.ts new file mode 100644 index 0000000..4cb2b12 --- /dev/null +++ b/lib/test/wrapperData.test.ts @@ -0,0 +1,81 @@ +import { T } from ".."; +import { utils } from "../util"; + +type WrapperType = { + config: { + someValue: string; + }; +}; +const todo = (): A => { + throw new Error("not implemented"); +}; +const noop = () => {}; +describe("wrapperData", () => { + test.skip("types", async () => { + utils(todo()).setWrapperData( + "/config/someValue", + "someValue", + ); + utils(todo()).setWrapperData( + "/config/someValue", + + // @ts-expect-error Type is wrong for the setting value + 5, + ); + utils(todo()).setWrapperData( + // @ts-expect-error Path is wrong + "/config/someVae3lue", + "someValue", + ); + + todo().setWrapperData({ + path: "/config/someValue", + value: "someValueIn", + }); + todo().setWrapperData({ + //@ts-expect-error Path is wrong + path: "/config/someValue", + //@ts-expect-error Path is wrong + value: "someValueIn", + }); + todo().setWrapperData({ + //@ts-expect-error Path is wrong + path: "/config/some2Value", + value: "someValueIn", + }); + + (await utils(todo()) + .getWrapperData("/config/someValue") + .const()) satisfies string; + (await utils(todo()) + .getWrapperData("/config") + .const()) satisfies WrapperType["config"]; + await utils(todo()) + // @ts-expect-error Path is wrong + .getWrapperData("/config/somdsfeValue") + .const(); + (await utils(todo()) + .getWrapperData("/config/someValue") + // @ts-expect-error satisfies type is wrong + .const()) satisfies number; + (await utils(todo()) + // @ts-expect-error Path is wrong + .getWrapperData("/config/") + .const()) satisfies WrapperType["config"]; + + (await todo().getWrapperData({ + path: "/config/someValue", + callback: noop, + })) satisfies string; + await todo().getWrapperData({ + // @ts-expect-error Path is wrong as in it doesn't match above + path: "/config/someV2alue", + callback: noop, + }); + await todo().getWrapperData({ + // @ts-expect-error Path is wrong as in it doesn't exists in wrapper type + path: "/config/someV2alue", + callback: noop, + }); + }); +}); diff --git a/lib/types.ts b/lib/types.ts index f21cf93..f856e16 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -85,7 +85,7 @@ export namespace ExpectedExports { /** Auto configure is used to make sure that other dependencies have the values t * that this service could use. */ - export type autoConfig = Record>; + export type autoConfig = Record; } export type TimeMs = number; export type VersionString = string; @@ -94,11 +94,19 @@ export type VersionString = string; * AutoConfigure is used as the value to the key of package id, * this is used to make sure that other dependencies have the values that this service could use. */ -export type AutoConfigure = { +export type AutoConfigure = { /** Checks are called to make sure that our dependency is in the correct shape. If a known error is returned we know that the dependency needs modification */ - check(effects: Effects, input: Config): Promise; + check(options: { + effects: Effects; + localConfig: unknown; + remoteConfig: unknown; + }): Promise; /** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */ - autoConfigure(effects: Effects, input: Config): Promise; + autoConfigure(options: { + effects: Effects; + localConfig: unknown; + remoteConfig: unknown; + }): Promise; }; export type ValidIfNoStupidEscape = A extends @@ -172,14 +180,14 @@ export type Effects = { command: ValidIfNoStupidEscape | [string, ...string[]], input?: { timeoutMillis?: number; - } + }, ): Promise; runShellDaemon(command: string): { wait(): Promise; term(): Promise; }; runDaemon( - command: ValidIfNoStupidEscape | [string, ...string[]] + command: ValidIfNoStupidEscape | [string, ...string[]], ): DaemonReturned; /** Uses the chown on the system */ @@ -225,7 +233,7 @@ export type Effects = { method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH"; headers?: Record; body?: string; - } + }, ): Promise<{ method: string; ok: boolean; @@ -256,19 +264,19 @@ export type Effects = { }; /** Get a value in a json like data, can be observed and subscribed */ - getWrapperData(options: { + getWrapperData(options: { /** If there is no packageId it is assumed the current package */ packageId?: string; /** The path defaults to root level, using the [JsonPath](https://jsonpath.com/) */ - path?: string; + path: Path & EnsureWrapperDataPath; callback: (config: unknown, previousConfig: unknown) => void; - }): Promise; + }): Promise>; /** Used to store values that can be accessed and subscribed to */ - setWrapperData(options: { + setWrapperData(options: { /** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */ - path?: string; - value: unknown; + path: Path & EnsureWrapperDataPath; + value: ExtractWrapperData; }): Promise; getLocalHostname(): Promise; @@ -276,14 +284,14 @@ export type Effects = { /** Get the address for another service for tor interfaces */ getServiceTorHostname( interfaceId: string, - packageId?: string + packageId?: string, ): Promise; /** * Get the port address for another service */ getServicePortForward( internalPort: number, - packageId?: string + packageId?: string, ): Promise; /** When we want to create a link in the front end interfaces, and example is @@ -356,7 +364,7 @@ export type Effects = { */ getSslCertificate: ( packageId: string, - algorithm?: "ecdsa" | "ed25519" + algorithm?: "ecdsa" | "ed25519", ) => [string, string, string]; /** * @returns PEM encoded ssl key (ecdsa) @@ -379,6 +387,20 @@ export type Effects = { shutdown(): void; }; +// prettier-ignore +export type ExtractWrapperData = + Path extends `/${infer A }/${infer Rest }` ? (A extends keyof WrapperData ? ExtractWrapperData : never) : + Path extends `/${infer A }` ? (A extends keyof WrapperData ? WrapperData[A] : never) : + never + +// prettier-ignore +type _EnsureWrapperDataPath = +Path extends `/${infer A }/${infer Rest }` ? (A extends keyof WrapperData ? ExtractWrapperData : never) : +Path extends `/${infer A }` ? (A extends keyof WrapperData ? Origin : never) : +never +// prettier-ignore +export type EnsureWrapperDataPath = _EnsureWrapperDataPath + /* rsync options: https://linux.die.net/man/1/rsync */ export type BackupOptions = { diff --git a/lib/util/deepEqual.ts b/lib/util/deepEqual.ts new file mode 100644 index 0000000..b5a533f --- /dev/null +++ b/lib/util/deepEqual.ts @@ -0,0 +1,19 @@ +import { object } from "ts-matches"; + +export function deepEqual(...args: unknown[]) { + if (!object.test(args[args.length - 1])) return args[args.length - 1]; + const objects = args.filter(object.test); + if (objects.length === 0) { + for (const x of args) if (x !== args[0]) return false; + return true; + } + if (objects.length !== args.length) return false; + const allKeys = new Set(objects.flatMap((x) => Object.keys(x))); + for (const key of allKeys) { + for (const x of objects) { + if (!(key in x)) return false; + if (!deepEqual((objects[0] as any)[key], (x as any)[key])) return false; + } + } + return true; +} diff --git a/lib/util/deepMerge.ts b/lib/util/deepMerge.ts new file mode 100644 index 0000000..c835b18 --- /dev/null +++ b/lib/util/deepMerge.ts @@ -0,0 +1,16 @@ +import { object } from "ts-matches"; + +export function deepMerge(...args: unknown[]): unknown { + const lastItem = (args as any)[args.length - 1]; + if (!object.test(lastItem)) return lastItem; + const objects = args.filter(object.test).filter((x) => !Array.isArray(x)); + if (objects.length === 0) return lastItem as any; + const allKeys = new Set(objects.flatMap((x) => Object.keys(x))); + for (const key of allKeys) { + const filteredValues = objects.flatMap((x) => + key in x ? [(x as any)[key]] : [], + ); + (objects as any)[0][key] = deepMerge(...filteredValues); + } + return objects[0] as any; +} diff --git a/lib/util/extensions.ts b/lib/util/extensions.ts index 8ed2cfd..686b3ec 100644 --- a/lib/util/extensions.ts +++ b/lib/util/extensions.ts @@ -8,7 +8,7 @@ type UnReadonly = { -readonly [k in keyof A]: A[k] }; declare global { interface Object { entries( - this: T + this: T, ): Array<{ -readonly [K in keyof T]: [K, T[K]] }[keyof T]>; values(this: T): Array; keys(this: T): Array; diff --git a/lib/util/fileHelper.ts b/lib/util/fileHelper.ts index e03a526..787caec 100644 --- a/lib/util/fileHelper.ts +++ b/lib/util/fileHelper.ts @@ -56,7 +56,7 @@ export class FileHelper { readonly path: string, readonly volume: string, readonly writeData: (dataIn: A) => string, - readonly readData: (stringValue: string) => A + readonly readData: (stringValue: string) => A, ) {} async write(data: A, effects: T.Effects) { let matched; @@ -86,21 +86,21 @@ export class FileHelper { await effects.readFile({ path: this.path, volumeId: this.volume, - }) + }), ); } static raw( path: string, volume: string, toFile: (dataIn: A) => string, - fromFile: (rawData: string) => A + fromFile: (rawData: string) => A, ) { return new FileHelper(path, volume, toFile, fromFile); } static json( path: string, volume: string, - shape: matches.Validator + shape: matches.Validator, ) { return new FileHelper( path, @@ -110,13 +110,13 @@ export class FileHelper { }, (inString) => { return shape.unsafeCast(JSON.parse(inString)); - } + }, ); } static toml>( path: string, volume: string, - shape: matches.Validator + shape: matches.Validator, ) { return new FileHelper( path, @@ -126,13 +126,13 @@ export class FileHelper { }, (inString) => { return shape.unsafeCast(TOML.parse(inString)); - } + }, ); } static yaml>( path: string, volume: string, - shape: matches.Validator + shape: matches.Validator, ) { return new FileHelper( path, @@ -142,7 +142,7 @@ export class FileHelper { }, (inString) => { return shape.unsafeCast(YAML.parse(inString)); - } + }, ); } } diff --git a/lib/util/getWrapperData.ts b/lib/util/getWrapperData.ts index 9592c37..e428203 100644 --- a/lib/util/getWrapperData.ts +++ b/lib/util/getWrapperData.ts @@ -1,45 +1,53 @@ import { Parser } from "ts-matches"; -import { Effects } from "../types"; +import { Effects, EnsureWrapperDataPath, ExtractWrapperData } from "../types"; +import { NoAny } from "."; -export function getWrapperData( +export class WrapperData { + constructor( + readonly effects: Effects, + readonly path: Path & EnsureWrapperDataPath, + readonly options: { + /** Defaults to what ever the package currently in */ + packageId?: string | undefined; + } = {}, + ) {} + + const() { + return this.effects.getWrapperData({ + ...this.options, + path: this.path as any, + callback: this.effects.restart, + }); + } + first() { + return this.effects.getWrapperData({ + ...this.options, + path: this.path as any, + callback: () => {}, + }); + } + async *overTime() { + while (true) { + let callback: () => void; + const waitForNext = new Promise((resolve) => { + callback = resolve; + }); + yield await this.effects.getWrapperData({ + ...this.options, + path: this.path as any, + callback: () => callback(), + }); + await waitForNext; + } + } +} +export function getWrapperData( effects: Effects, - validator: Parser, + path: Path & EnsureWrapperDataPath, options: { /** Defaults to what ever the package currently in */ packageId?: string | undefined; - /** JsonPath */ - path?: string | undefined; - } = {} + } = {}, ) { - return { - const: () => - effects - .getWrapperData({ - ...options, - callback: effects.restart, - }) - .then(validator.unsafeCast), - first: () => - effects - .getWrapperData({ - ...options, - callback: () => {}, - }) - .then(validator.unsafeCast), - overTime: async function* () { - while (true) { - let callback: () => void; - const waitForNext = new Promise((resolve) => { - callback = resolve; - }); - yield await effects - .getWrapperData({ - ...options, - callback: () => callback(), - }) - .then(validator.unsafeCast); - await waitForNext; - } - }, - }; + return new WrapperData(effects, path as any, options); } diff --git a/lib/util/index.ts b/lib/util/index.ts index 4d4df9d..737a31a 100644 --- a/lib/util/index.ts +++ b/lib/util/index.ts @@ -5,20 +5,23 @@ import nullIfEmpty from "./nullIfEmpty"; import { getWrapperData } from "./getWrapperData"; import { checkPortListening, checkWebUrl } from "../health/checkFns"; import { LocalPort, NetworkBuilder, TorHostname } from "../mainFn"; +import { ExtractWrapperData } from "../types"; export { guardAll, typeFromProps } from "./propertiesMatcher"; export { default as nullIfEmpty } from "./nullIfEmpty"; export { FileHelper } from "./fileHelper"; export { getWrapperData } from "./getWrapperData"; +export { deepEqual } from "./deepEqual"; +export { deepMerge } from "./deepMerge"; /** Used to check if the file exists before hand */ export const exists = ( effects: T.Effects, - props: { path: string; volumeId: string } + props: { path: string; volumeId: string }, ) => effects.metadata(props).then( (_) => true, - (_) => false + (_) => false, ); export const isKnownError = (e: unknown): e is T.KnownError => @@ -26,28 +29,40 @@ export const isKnownError = (e: unknown): e is T.KnownError => type Cdr = A extends [unknown, ...infer Cdr] ? Cdr : []; -export const utils = (effects: T.Effects) => ({ +declare const affine: unique symbol; + +function withAffine() { + return {} as { [affine]: B }; +} + +export const utils = (effects: T.Effects) => ({ readFile: (fileHelper: FileHelper) => fileHelper.read(effects), writeFile: (fileHelper: FileHelper, data: A) => fileHelper.write(data, effects), exists: (props: { path: string; volumeId: string }) => exists(effects, props), nullIfEmpty, - getWrapperData: ( - validator: Parser, + getWrapperData: ( + path: T.EnsureWrapperDataPath, options: { + validator?: Parser>; /** Defaults to what ever the package currently in */ packageId?: string | undefined; - /** JsonPath */ - path?: string | undefined; - } = {} - ) => getWrapperData(effects, validator, options), - setWrapperData: ( - value: A, - options: { packageId?: string | undefined; path?: string | undefined } = {} - ) => effects.setWrapperData({ ...options, value }), + } = {}, + ) => getWrapperData(effects, path as any, options), + setWrapperData: ( + path: T.EnsureWrapperDataPath, + value: ExtractWrapperData, + ) => effects.setWrapperData({ value, path: path as any }), checkPortListening: checkPortListening.bind(null, effects), checkWebUrl: checkWebUrl.bind(null, effects), localPort: LocalPort.bind(null, effects), networkBuilder: NetworkBuilder.of.bind(null, effects), torHostName: TorHostname.of.bind(null, effects), }); + +type NeverPossible = { [affine]: string }; +export type NoAny = NeverPossible extends A + ? keyof NeverPossible extends keyof A + ? never + : A + : A; diff --git a/lib/util/propertiesMatcher.ts b/lib/util/propertiesMatcher.ts index 0779d6c..419c877 100644 --- a/lib/util/propertiesMatcher.ts +++ b/lib/util/propertiesMatcher.ts @@ -141,7 +141,7 @@ function charRange(value = "") { */ export function generateDefault( generate: { charset: string; len: number }, - { random = () => Math.random() } = {} + { random = () => Math.random() } = {}, ) { const validCharSets: number[][] = generate.charset .split(",") @@ -152,7 +152,7 @@ export function generateDefault( } const max = validCharSets.reduce( (acc, x) => x.reduce((x, y) => Math.max(x, y), acc), - 0 + 0, ); let i = 0; const answer: string[] = Array(generate.len); @@ -161,7 +161,7 @@ export function generateDefault( const inRange = validCharSets.reduce( (acc, [lower, upper]) => acc || (nextValue >= lower && nextValue <= upper), - false + false, ); if (!inRange) continue; answer[i] = String.fromCharCode(nextValue); @@ -186,7 +186,7 @@ export function matchNumberWithRange(range: string) { ? "any" : left === "[" ? `greaterThanOrEqualTo${leftValue}` - : `greaterThan${leftValue}` + : `greaterThan${leftValue}`, ) .validate( // prettier-ignore @@ -196,7 +196,7 @@ export function matchNumberWithRange(range: string) { // prettier-ignore rightValue === "*" ? "any" : right === "]" ? `lessThanOrEqualTo${rightValue}` : - `lessThan${rightValue}` + `lessThan${rightValue}`, ); } function withIntegral(parser: Parser, value: unknown) { @@ -219,7 +219,7 @@ function defaultRequired(parser: Parser, value: unknown) { if (matchDefault.test(value)) { if (isGenerator(value.default)) { return parser.defaultTo( - parser.unsafeCast(generateDefault(value.default)) + parser.unsafeCast(generateDefault(value.default)), ); } return parser.defaultTo(value.default); @@ -237,7 +237,7 @@ function defaultRequired(parser: Parser, value: unknown) { * @returns */ export function guardAll( - value: A + value: A, ): Parser> { if (!isType.test(value)) { return unknown as any; @@ -255,7 +255,7 @@ export function guardAll( case "number": return defaultRequired( withIntegral(withRange(value), value), - value + value, ) as any; case "object": @@ -274,7 +274,7 @@ export function guardAll( matches .arrayOf(guardAll(spec as any)) .validate((x) => rangeValidate(x.length), "valid length"), - value + value, ) as any; } case "select": @@ -282,7 +282,7 @@ export function guardAll( const valueKeys = Object.keys(value.values); return defaultRequired( literals(valueKeys[0], ...valueKeys), - value + value, ) as any; } return unknown as any; @@ -290,19 +290,19 @@ export function guardAll( case "multiselect": if (matchValues.test(value)) { const maybeAddRangeValidate = , B>( - x: X + x: X, ) => { if (!matchRange.test(value)) return x; return x.validate( (x) => matchNumberWithRange(value.range).test(x.length), - "validLength" + "validLength", ); }; const valueKeys = Object.keys(value.values); return defaultRequired( maybeAddRangeValidate(arrayOf(literals(valueKeys[0], ...valueKeys))), - value + value, ) as any; } return unknown as any; @@ -316,8 +316,8 @@ export function guardAll( object({ unionSelectKey: literals(name), unionValueKey: typeFromProps(spec), - }) - ) + }), + ), ) as any; } return unknown as any; @@ -334,7 +334,7 @@ export function guardAll( * @returns */ export function typeFromProps( - valueDictionary: A + valueDictionary: A, ): Parser> { if (!recordString.test(valueDictionary)) return unknown as any; return object( @@ -342,7 +342,7 @@ export function typeFromProps( Object.entries(valueDictionary).map(([key, value]) => [ key, guardAll(value), - ]) - ) + ]), + ), ) as any; } diff --git a/package-lock.json b/package-lock.json index 128c242..e681629 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "start-sdk", - "version": "0.4.0-lib0.charlie30", + "version": "0.4.0-lib0.charlie31", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "start-sdk", - "version": "0.4.0-lib0.charlie30", + "version": "0.4.0-lib0.charlie31", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index 525ef64..3a2c98c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "start-sdk", - "version": "0.4.0-lib0.charlie30", + "version": "0.4.0-lib0.charlie31", "description": "For making the patterns that are wanted in making services for the startOS.", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -26,6 +26,12 @@ "ts-matches": "^5.4.1", "yaml": "^2.2.1" }, + "prettier": { + "trailingComma": "all", + "tabWidth": 2, + "semi": true, + "singleQuote": false + }, "devDependencies": { "@types/jest": "^29.4.0", "@types/lodash": "^4.14.191", diff --git a/scripts/oldSpecToBuilder.ts b/scripts/oldSpecToBuilder.ts index 91c32fa..ee15d0a 100644 --- a/scripts/oldSpecToBuilder.ts +++ b/scripts/oldSpecToBuilder.ts @@ -6,16 +6,16 @@ import { unionSelectKey } from "../lib/config/configTypes"; export async function writeConvertedFile( file: string, inputData: Promise | any, - options: Parameters[1] + options: Parameters[1], ) { await fs.writeFile(file, await makeFileContent(inputData, options), (err) => - console.error(err) + console.error(err), ); } export default async function makeFileContent( inputData: Promise | any, - { startSdk = "start-sdk" } = {} + { startSdk = "start-sdk" } = {}, ) { const outputLines: string[] = []; outputLines.push(` @@ -27,10 +27,10 @@ export default async function makeFileContent( const configName = newConst("InputSpec", convertInputSpec(data)); const configMatcherName = newConst( "matchInputSpec", - `${configName}.validator()` + `${configName}.validator()`, ); outputLines.push( - `export type InputSpec = typeof ${configMatcherName}._TYPE;` + `export type InputSpec = typeof ${configMatcherName}._TYPE;`, ); return outputLines.join("\n"); @@ -62,7 +62,7 @@ export default async function makeFileContent( placeholder: value.placeholder || null, }, null, - 2 + 2, )})`; } return `Value.string(${JSON.stringify( @@ -78,7 +78,7 @@ export default async function makeFileContent( patternDescription: value["pattern-description"] || null, }, null, - 2 + 2, )})`; } case "number": { @@ -95,7 +95,7 @@ export default async function makeFileContent( placeholder: value.placeholder || null, }, null, - 2 + 2, )})`; } case "boolean": { @@ -107,7 +107,7 @@ export default async function makeFileContent( warning: value.warning || null, }, null, - 2 + 2, )})`; } case "enum": { @@ -118,7 +118,7 @@ export default async function makeFileContent( const values = Object.fromEntries( Array.from(allValueNames) .filter(string.test) - .map((key) => [key, value?.spec?.["value-names"]?.[key] || key]) + .map((key) => [key, value?.spec?.["value-names"]?.[key] || key]), ); return `Value.select(${JSON.stringify( { @@ -130,13 +130,13 @@ export default async function makeFileContent( values, }, null, - 2 + 2, )} as const)`; } case "object": { const specName = newConst( value.name + "_spec", - convertInputSpec(value.spec) + convertInputSpec(value.spec), ); return `Value.object({ name: ${JSON.stringify(value.name || null)}, @@ -147,7 +147,7 @@ export default async function makeFileContent( case "union": { const variants = newConst( value.name + "_variants", - convertVariants(value.variants, value.tag["variant-names"] || {}) + convertVariants(value.variants, value.tag["variant-names"] || {}), ); return `Value.union({ @@ -183,7 +183,7 @@ export default async function makeFileContent( warning: value.warning || null, }, null, - 2 + 2, )}, ${JSON.stringify({ masked: value?.spec?.masked || false, placeholder: value?.spec?.placeholder || null, @@ -201,7 +201,7 @@ export default async function makeFileContent( warning: value.warning || null, }, null, - 2 + 2, )}, ${JSON.stringify({ range: value?.spec?.range || null, integral: value?.spec?.integral || false, @@ -212,7 +212,7 @@ export default async function makeFileContent( case "enum": { const allValueNames = new Set( ...(value?.spec?.["values"] || []), - ...Object.keys(value?.spec?.["value-names"] || {}) + ...Object.keys(value?.spec?.["value-names"] || {}), ); const values = Object.fromEntries( Array.from(allValueNames) @@ -220,7 +220,7 @@ export default async function makeFileContent( .map((key: string) => [ key, value?.spec?.["value-names"]?.[key] || key, - ]) + ]), ); return `Value.multiselect(${JSON.stringify( { @@ -232,13 +232,13 @@ export default async function makeFileContent( values, }, null, - 2 + 2, )})`; } case "object": { const specName = newConst( value.name + "_spec", - convertInputSpec(value.spec.spec) + convertInputSpec(value.spec.spec), ); return `List.obj({ name: ${JSON.stringify(value.name || null)}, @@ -257,8 +257,8 @@ export default async function makeFileContent( value.name + "_variants", convertVariants( value.spec.variants, - value.spec["variant-names"] || {} - ) + value.spec["variant-names"] || {}, + ), ); const unionValueName = newConst( value.name + "_union", @@ -266,13 +266,13 @@ export default async function makeFileContent( Value.union({ name: ${JSON.stringify(value?.spec?.tag?.name || null)}, description: ${JSON.stringify( - value?.spec?.tag?.description || null + 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)}, }, ${variants}) - ` + `, ); const listConfig = newConst( value.name + "_list_config", @@ -280,7 +280,7 @@ export default async function makeFileContent( Config.of({ "union": ${unionValueName} }) - ` + `, ); return `List.obj({ name:${JSON.stringify(value.name || null)}, @@ -300,7 +300,7 @@ export default async function makeFileContent( function convertVariants( variants: Record, - variantNames: Record + variantNames: Record, ): string { let answer = "Variants.of({"; for (const [key, value] of Object.entries(variants)) {