From f8a63f6e385fb4be55eeb03e7e72bf5c5b7eaac3 Mon Sep 17 00:00:00 2001 From: BluJ Date: Tue, 9 May 2023 15:55:19 -0600 Subject: [PATCH] feat: Add vault through utils + sdk --- lib/StartSdk.ts | 142 ++++++++------ lib/actions/createAction.ts | 24 +-- lib/actions/setupActions.ts | 8 +- lib/autoconfig/AutoConfig.ts | 14 +- lib/autoconfig/setupAutoConfig.ts | 8 +- lib/config/builder/config.ts | 45 +++-- lib/config/builder/list.ts | 26 +-- lib/config/builder/value.ts | 245 +++++++++++++----------- lib/config/builder/variants.ts | 14 +- lib/config/configConstants.ts | 2 +- lib/config/setupConfig.ts | 27 +-- lib/inits/migrations/Migration.ts | 30 ++- lib/inits/migrations/setupMigrations.ts | 32 ++-- lib/inits/setupInit.ts | 8 +- lib/inits/setupInstall.ts | 12 +- lib/inits/setupUninstall.ts | 12 +- lib/mainFn/index.ts | 6 +- lib/store/getStore.ts | 2 - lib/test/output.sdk.ts | 1 + lib/test/store.test.ts | 47 ++--- lib/types.ts | 2 +- lib/util/index.ts | 4 +- lib/util/utils.ts | 27 ++- 23 files changed, 427 insertions(+), 311 deletions(-) diff --git a/lib/StartSdk.ts b/lib/StartSdk.ts index 2ec771d..833d2b3 100644 --- a/lib/StartSdk.ts +++ b/lib/StartSdk.ts @@ -55,24 +55,29 @@ type AnyNeverCond = T extends [any, ...infer U] ? AnyNeverCond : never -export class StartSdk { +export class StartSdk { private constructor(readonly manifest: Manifest) {} static of() { - return new StartSdk(null as never) + return new StartSdk(null as never) } withManifest(manifest: Manifest) { - return new StartSdk(manifest) + return new StartSdk(manifest) } withStore>() { - return new StartSdk(this.manifest) + return new StartSdk(this.manifest) + } + withVault>() { + return new StartSdk(this.manifest) } - build(isReady: AnyNeverCond<[Manifest, Store], "Build not ready", true>) { + build( + isReady: AnyNeverCond<[Manifest, Store, Vault], "Build not ready", true>, + ) { return { AutoConfig: >( - configs: AutoConfigFrom, - path: keyof AutoConfigFrom, - ) => new AutoConfig(configs, path), + configs: AutoConfigFrom, + path: keyof AutoConfigFrom, + ) => new AutoConfig(configs, path), Backups: { volumes: (...volumeNames: Array) => Backups.volumes(...volumeNames), @@ -84,29 +89,32 @@ export class StartSdk { }, Config: { of: < - Spec extends Record | Value>, + Spec extends Record< + string, + Value | Value + >, >( spec: Spec, - ) => Config.of(spec), + ) => Config.of(spec), }, configConstants: { smtpConfig }, createAction: < Store, ConfigType extends | Record - | Config - | Config, + | Config + | Config, Type extends Record = ExtractConfigType, >( metaData: Omit & { - input: Config | Config + input: Config | Config }, fn: (options: { effects: Effects - utils: Utils + utils: Utils input: Type }) => Promise, - ) => createAction(metaData, fn), + ) => createAction(metaData, fn), Daemons: { of: Daemons.of }, healthCheck: { checkPortListening, @@ -128,14 +136,15 @@ export class StartSdk { maxLength?: number | null }, aSpec: { - spec: Config + spec: Config displayAs?: null | string uniqueBy?: null | UniqueBy }, - ) => List.obj(a, aSpec), + ) => List.obj(a, aSpec), dynamicText: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -158,10 +167,11 @@ export class StartSdk { } } >, - ) => List.dynamicText(getA), + ) => List.dynamicText(getA), dynamicNumber: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -181,30 +191,33 @@ export class StartSdk { } } >, - ) => List.dynamicNumber(getA), + ) => List.dynamicNumber(getA), }, Migration: { of: (options: { version: Version - up: (opts: { effects: Effects; utils: Utils }) => Promise + up: (opts: { + effects: Effects + utils: Utils + }) => Promise down: (opts: { effects: Effects - utils: Utils + utils: Utils }) => Promise - }) => Migration.of(options), + }) => Migration.of(options), }, - setupActions: (...createdActions: CreatedAction[]) => - setupActions(...createdActions), + setupActions: (...createdActions: CreatedAction[]) => + setupActions(...createdActions), setupAutoConfig: < Input extends Record, NestedConfigs extends { [key in keyof Manifest["dependencies"]]: unknown }, >( - config: Config, - autoConfigs: AutoConfigFrom, + config: Config, + autoConfigs: AutoConfigFrom, ) => - setupAutoConfig( + setupAutoConfig( config, autoConfigs, ), @@ -213,31 +226,38 @@ export class StartSdk { setupConfig: < ConfigType extends | Record - | Config - | Config, + | Config + | Config, Type extends Record = ExtractConfigType, >( - spec: Config | Config, - write: Save, - read: Read, - ) => setupConfig(spec, write, read), + spec: Config | Config, + write: Save, + read: Read, + ) => + setupConfig( + spec, + write, + read, + ), setupInit: ( - migrations: Migrations, - install: Install, - uninstall: Uninstall, - ) => setupInit(migrations, install, uninstall), - setupInstall: (fn: InstallFn) => Install.of(fn), + migrations: Migrations, + install: Install, + uninstall: Uninstall, + ) => setupInit(migrations, install, uninstall), + setupInstall: (fn: InstallFn) => Install.of(fn), setupMain: ( fn: (o: { effects: Effects started(onTerm: () => void): null - utils: Utils + utils: Utils }) => Promise>, - ) => setupMain(fn), - setupMigrations: >>( + ) => setupMain(fn), + setupMigrations: >>( ...migrations: EnsureUniqueId - ) => setupMigrations(this.manifest, ...migrations), - setupUninstall: (fn: UninstallFn) => setupUninstall(fn), + ) => + setupMigrations(this.manifest, ...migrations), + setupUninstall: (fn: UninstallFn) => + setupUninstall(fn), trigger: { defaultTrigger, cooldownTrigger, @@ -258,6 +278,7 @@ export class StartSdk { dynamicToggle: ( a: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -266,10 +287,11 @@ export class StartSdk { disabled?: false | string } >, - ) => Value.dynamicToggle(a), + ) => Value.dynamicToggle(a), dynamicText: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -287,10 +309,11 @@ export class StartSdk { generate?: null | RandomString } >, - ) => Value.dynamicText(getA), + ) => Value.dynamicText(getA), dynamicTextarea: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -303,10 +326,11 @@ export class StartSdk { generate?: null | RandomString } >, - ) => Value.dynamicTextarea(getA), + ) => Value.dynamicTextarea(getA), dynamicNumber: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -322,10 +346,11 @@ export class StartSdk { disabled?: false | string } >, - ) => Value.dynamicNumber(getA), + ) => Value.dynamicNumber(getA), dynamicColor: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -335,10 +360,11 @@ export class StartSdk { disabled?: false | string } >, - ) => Value.dynamicColor(getA), + ) => Value.dynamicColor(getA), dynamicDatetime: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -352,10 +378,11 @@ export class StartSdk { disabled?: false | string } >, - ) => Value.dynamicDatetime(getA), + ) => Value.dynamicDatetime(getA), dynamicSelect: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -365,10 +392,11 @@ export class StartSdk { disabled?: false | string } >, - ) => Value.dynamicSelect(getA), + ) => Value.dynamicSelect(getA), dynamicMultiselect: ( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -380,21 +408,23 @@ export class StartSdk { disabled?: false | string } >, - ) => Value.dynamicMultiselect(getA), + ) => Value.dynamicMultiselect(getA), filteredUnion: < Required extends RequiredDefault, Type extends Record, >( - getDisabledFn: LazyBuild, + getDisabledFn: LazyBuild, a: { name: string description?: string | null warning?: string | null required: Required }, - aVariants: Variants | Variants, + aVariants: + | Variants + | Variants, ) => - Value.filteredUnion( + Value.filteredUnion( getDisabledFn, a, aVariants, @@ -405,12 +435,12 @@ export class StartSdk { VariantValues extends { [K in string]: { name: string - spec: Config + spec: Config } }, >( a: VariantValues, - ) => Variants.of(a), + ) => Variants.of(a), }, } } diff --git a/lib/actions/createAction.ts b/lib/actions/createAction.ts index ff4ba1e..7a9a7c5 100644 --- a/lib/actions/createAction.ts +++ b/lib/actions/createAction.ts @@ -5,45 +5,47 @@ import { Utils, utils } from "../util/utils" export class CreatedAction< Store, + Vault, ConfigType extends | Record - | Config - | Config, + | Config + | Config, Type extends Record = ExtractConfigType, > { private constructor( public readonly myMetaData: Omit, readonly fn: (options: { effects: Effects - utils: Utils + utils: Utils input: Type }) => Promise, - readonly input: Config, + readonly input: Config, ) {} public validator = this.input.validator static of< Store, + Vault, ConfigType extends | Record - | Config - | Config, + | Config + | Config, Type extends Record = ExtractConfigType, >( metaData: Omit & { - input: Config | Config + input: Config | Config }, fn: (options: { effects: Effects - utils: Utils + utils: Utils input: Type }) => Promise, ) { const { input, ...rest } = metaData - return new CreatedAction( + return new CreatedAction( rest, fn, - input as Config, + input as Config, ) } @@ -65,7 +67,7 @@ export class CreatedAction< async actionMetaData(options: { effects: Effects - utils: Utils + utils: Utils }): Promise { return { ...this.myMetaData, diff --git a/lib/actions/setupActions.ts b/lib/actions/setupActions.ts index aa095b4..f2230ed 100644 --- a/lib/actions/setupActions.ts +++ b/lib/actions/setupActions.ts @@ -3,11 +3,11 @@ import { createUtils } from "../util" import { once } from "../util/once" import { CreatedAction } from "./createAction" -export function setupActions( - ...createdActions: CreatedAction[] +export function setupActions( + ...createdActions: CreatedAction[] ) { const myActions = once(() => { - const actions: Record> = {} + const actions: Record> = {} for (const action of createdActions) { actions[action.myMetaData.id] = action } @@ -18,7 +18,7 @@ export function setupActions( return myActions() }, async actionsMetaData({ effects }: { effects: Effects }) { - const utils = createUtils(effects) + const utils = createUtils(effects) return Promise.all( createdActions.map((x) => x.actionMetaData({ effects, utils })), ) diff --git a/lib/autoconfig/AutoConfig.ts b/lib/autoconfig/AutoConfig.ts index e6ff02d..484f3bb 100644 --- a/lib/autoconfig/AutoConfig.ts +++ b/lib/autoconfig/AutoConfig.ts @@ -6,27 +6,29 @@ import { Config } from "../config/builder/config" export type AutoConfigFrom< Store, + Vault, Input, NestedConfigs extends Record, > = { [key in keyof NestedConfigs & string]: { - serviceConfig: Config + serviceConfig: Config autoConfig: (options: { effects: Effects localConfig: Input remoteConfig: NestedConfigs[key] - utils: Utils + utils: Utils }) => Promise> } } export class AutoConfig< Store, + Vault, Input, NestedConfigs extends Record, > { constructor( - readonly configs: AutoConfigFrom, - readonly path: keyof AutoConfigFrom, + readonly configs: AutoConfigFrom, + readonly path: keyof AutoConfigFrom, ) {} async check( @@ -35,7 +37,7 @@ export class AutoConfig< const origConfig = JSON.parse(JSON.stringify(options.localConfig)) const newOptions = { ...options, - utils: utils(options.effects), + utils: utils(options.effects), localConfig: options.localConfig as Input, remoteConfig: options.remoteConfig as any, } @@ -56,7 +58,7 @@ export class AutoConfig< ): ReturnType { const newOptions = { ...options, - utils: utils(options.effects), + utils: utils(options.effects), localConfig: options.localConfig as Input, remoteConfig: options.remoteConfig as any, } diff --git a/lib/autoconfig/setupAutoConfig.ts b/lib/autoconfig/setupAutoConfig.ts index a501ebc..6055929 100644 --- a/lib/autoconfig/setupAutoConfig.ts +++ b/lib/autoconfig/setupAutoConfig.ts @@ -4,22 +4,24 @@ import { AutoConfig, AutoConfigFrom } from "./AutoConfig" export function setupAutoConfig< Store, + Vault, Input extends Record, Manifest extends SDKManifest, NestedConfigs extends { [key in keyof Manifest["dependencies"]]: unknown }, >( - config: Config, - autoConfigs: AutoConfigFrom, + _config: Config, + autoConfigs: AutoConfigFrom, ) { type C = typeof autoConfigs const answer = { ...autoConfigs } as unknown as { - [k in keyof C]: AutoConfig + [k in keyof C]: AutoConfig } for (const key in autoConfigs) { answer[key as keyof typeof autoConfigs] = new AutoConfig< Store, + Vault, Input, NestedConfigs >(autoConfigs, key as keyof typeof autoConfigs) diff --git a/lib/config/builder/config.ts b/lib/config/builder/config.ts index 8f2e1ba..e9cd9fd 100644 --- a/lib/config/builder/config.ts +++ b/lib/config/builder/config.ts @@ -5,24 +5,28 @@ import { _ } from "../../util" import { Effects } from "../../types" import { Parser, object } from "ts-matches" -export type LazyBuildOptions = { +export type LazyBuildOptions = { effects: Effects - utils: Utils + utils: Utils } -export type LazyBuild = ( - options: LazyBuildOptions, +export type LazyBuild = ( + options: LazyBuildOptions, ) => Promise | ExpectedOut // prettier-ignore -export type ExtractConfigType | Config, any> | Config, never>> = - A extends Config | Config ? B : +export type ExtractConfigType | Config, any, any> | Config, never, never>> = + A extends Config | Config ? B : A -export type ConfigSpecOf, Store = never> = { - [K in keyof A]: Value +export type ConfigSpecOf< + A extends Record, + Store = never, + Vault = never, +> = { + [K in keyof A]: Value } -export type MaybeLazyValues = LazyBuild | A +export type MaybeLazyValues = LazyBuild | A /** * Configs are the specs that are used by the os configuration form for this service. * Here is an example of a simple configuration @@ -79,14 +83,16 @@ export const addNodesSpec = Config.of({ hostname: hostname, port: port }); ``` */ -export class Config, Store> { +export class Config, Store, Vault> { private constructor( private readonly spec: { - [K in keyof Type]: Value | Value + [K in keyof Type]: + | Value + | Value }, public validator: Parser, ) {} - async build(options: LazyBuildOptions) { + async build(options: LazyBuildOptions) { const answer = {} as { [K in keyof Type]: ValueSpec } @@ -97,8 +103,12 @@ export class Config, Store> { } static of< - Spec extends Record | Value>, + Spec extends Record< + string, + Value | Value + >, Store, + Vault, >(spec: Spec) { const validatorObj = {} as { [K in keyof Spec]: Parser @@ -110,12 +120,13 @@ export class Config, Store> { return new Config< { [K in keyof Spec]: Spec[K] extends - | Value - | Value + | Value + | Value ? T : never }, - Store + Store, + Vault >(spec, validator as any) } @@ -134,6 +145,6 @@ export class Config, Store> { ``` */ withStore() { - return this as any as Config + return this as any as Config } } diff --git a/lib/config/builder/list.ts b/lib/config/builder/list.ts index 2b8c2b4..e31e623 100644 --- a/lib/config/builder/list.ts +++ b/lib/config/builder/list.ts @@ -22,9 +22,9 @@ export const authorizationList = List.string({ export const auth = Value.list(authorizationList); ``` */ -export class List { +export class List { private constructor( - public build: LazyBuild, + public build: LazyBuild, public validator: Parser, ) {} static text( @@ -49,7 +49,7 @@ export class List { generate?: null | RandomString }, ) { - return new List(() => { + return new List(() => { const spec = { type: "text" as const, placeholder: null, @@ -73,9 +73,10 @@ export class List { } satisfies ValueSpecListOf<"text"> }, arrayOf(string)) } - static dynamicText( + static dynamicText( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -99,7 +100,7 @@ export class List { } >, ) { - return new List(async (options) => { + return new List(async (options) => { const { spec: aSpec, ...a } = await getA(options) const spec = { type: "text" as const, @@ -143,7 +144,7 @@ export class List { placeholder?: string | null }, ) { - return new List(() => { + return new List(() => { const spec = { type: "number" as const, placeholder: null, @@ -166,9 +167,10 @@ export class List { } satisfies ValueSpecListOf<"number"> }, arrayOf(number)) } - static dynamicNumber( + static dynamicNumber( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -189,7 +191,7 @@ export class List { } >, ) { - return new List(async (options) => { + return new List(async (options) => { const { spec: aSpec, ...a } = await getA(options) const spec = { type: "number" as const, @@ -213,7 +215,7 @@ export class List { } }, arrayOf(number)) } - static obj, Store>( + static obj, Store, Vault>( a: { name: string description?: string | null @@ -224,12 +226,12 @@ export class List { maxLength?: number | null }, aSpec: { - spec: Config + spec: Config displayAs?: null | string uniqueBy?: null | UniqueBy }, ) { - return new List(async (options) => { + return new List(async (options) => { const { spec: previousSpecSpec, ...restSpec } = aSpec const specSpec = await previousSpecSpec.build(options) const spec = { @@ -271,6 +273,6 @@ export class List { ``` */ withStore() { - return this as any as List + return this as any as List } } diff --git a/lib/config/builder/value.ts b/lib/config/builder/value.ts index 7d1963e..315e378 100644 --- a/lib/config/builder/value.ts +++ b/lib/config/builder/value.ts @@ -94,9 +94,9 @@ const username = Value.string({ }); ``` */ -export class Value { +export class Value { protected constructor( - public build: LazyBuild, + public build: LazyBuild, public validator: Parser, ) {} static toggle(a: { @@ -108,7 +108,7 @@ export class Value { Default is false */ immutable?: boolean }) { - return new Value( + return new Value( async () => ({ description: null, warning: null, @@ -120,9 +120,10 @@ export class Value { boolean, ) } - static dynamicToggle( + static dynamicToggle( a: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -132,7 +133,7 @@ export class Value { } >, ) { - return new Value( + return new Value( async (options) => ({ description: null, warning: null, @@ -163,7 +164,7 @@ export class Value { immutable?: boolean generate?: null | RandomString }) { - return new Value, never>( + return new Value, never, never>( async () => ({ type: "text" as const, description: null, @@ -183,9 +184,10 @@ export class Value { asRequiredParser(string, a), ) } - static dynamicText( + static dynamicText( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -204,25 +206,28 @@ export class Value { } >, ) { - return new Value(async (options) => { - const a = await getA(options) - return { - type: "text" as const, - description: null, - warning: null, - masked: false, - placeholder: null, - minLength: null, - maxLength: null, - patterns: [], - inputmode: "text", - disabled: false, - immutable: false, - generate: a.generate ?? null, - ...a, - ...requiredLikeToAbove(a.required), - } - }, string.optional()) + return new Value( + async (options) => { + const a = await getA(options) + return { + type: "text" as const, + description: null, + warning: null, + masked: false, + placeholder: null, + minLength: null, + maxLength: null, + patterns: [], + inputmode: "text", + disabled: false, + immutable: false, + generate: a.generate ?? null, + ...a, + ...requiredLikeToAbove(a.required), + } + }, + string.optional(), + ) } static textarea(a: { name: string @@ -237,7 +242,7 @@ export class Value { immutable?: boolean generate?: null | RandomString }) { - return new Value( + return new Value( async () => ({ description: null, @@ -254,9 +259,10 @@ export class Value { string, ) } - static dynamicTextarea( + static dynamicTextarea( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -270,7 +276,7 @@ export class Value { } >, ) { - return new Value(async (options) => { + return new Value(async (options) => { const a = await getA(options) return { description: null, @@ -302,7 +308,7 @@ export class Value { Default is false */ immutable?: boolean }) { - return new Value, never>( + return new Value, never, never>( () => ({ type: "number" as const, description: null, @@ -320,9 +326,10 @@ export class Value { asRequiredParser(number, a), ) } - static dynamicNumber( + static dynamicNumber( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -339,23 +346,26 @@ export class Value { } >, ) { - return new Value(async (options) => { - const a = await getA(options) - return { - type: "number" as const, - description: null, - warning: null, - min: null, - max: null, - step: null, - units: null, - placeholder: null, - disabled: false, - immutable: false, - ...a, - ...requiredLikeToAbove(a.required), - } - }, number.optional()) + return new Value( + async (options) => { + const a = await getA(options) + return { + type: "number" as const, + description: null, + warning: null, + min: null, + max: null, + step: null, + units: null, + placeholder: null, + disabled: false, + immutable: false, + ...a, + ...requiredLikeToAbove(a.required), + } + }, + number.optional(), + ) } static color>(a: { name: string @@ -366,7 +376,7 @@ export class Value { Default is false */ immutable?: boolean }) { - return new Value, never>( + return new Value, never, never>( () => ({ type: "color" as const, description: null, @@ -381,9 +391,10 @@ export class Value { ) } - static dynamicColor( + static dynamicColor( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -394,18 +405,21 @@ export class Value { } >, ) { - return new Value(async (options) => { - const a = await getA(options) - return { - type: "color" as const, - description: null, - warning: null, - disabled: false, - immutable: false, - ...a, - ...requiredLikeToAbove(a.required), - } - }, string.optional()) + return new Value( + async (options) => { + const a = await getA(options) + return { + type: "color" as const, + description: null, + warning: null, + disabled: false, + immutable: false, + ...a, + ...requiredLikeToAbove(a.required), + } + }, + string.optional(), + ) } static datetime>(a: { name: string @@ -421,7 +435,7 @@ export class Value { Default is false */ immutable?: boolean }) { - return new Value, never>( + return new Value, never, never>( () => ({ type: "datetime" as const, description: null, @@ -438,9 +452,10 @@ export class Value { asRequiredParser(string, a), ) } - static dynamicDatetime( + static dynamicDatetime( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -455,22 +470,25 @@ export class Value { } >, ) { - return new Value(async (options) => { - const a = await getA(options) - return { - type: "datetime" as const, - description: null, - warning: null, - inputmode: "datetime-local", - min: null, - max: null, - step: null, - disabled: false, - immutable: false, - ...a, - ...requiredLikeToAbove(a.required), - } - }, string.optional()) + return new Value( + async (options) => { + const a = await getA(options) + return { + type: "datetime" as const, + description: null, + warning: null, + inputmode: "datetime-local", + min: null, + max: null, + step: null, + disabled: false, + immutable: false, + ...a, + ...requiredLikeToAbove(a.required), + } + }, + string.optional(), + ) } static select< Required extends RequiredDefault, @@ -485,7 +503,7 @@ export class Value { Default is false */ immutable?: boolean }) { - return new Value, never>( + return new Value, never, never>( () => ({ description: null, warning: null, @@ -503,9 +521,10 @@ export class Value { ) as any, ) } - static dynamicSelect( + static dynamicSelect( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -516,18 +535,21 @@ export class Value { } >, ) { - return new Value(async (options) => { - const a = await getA(options) - return { - description: null, - warning: null, - type: "select" as const, - disabled: false, - immutable: false, - ...a, - ...requiredLikeToAbove(a.required), - } - }, string.optional()) + return new Value( + async (options) => { + const a = await getA(options) + return { + description: null, + warning: null, + type: "select" as const, + disabled: false, + immutable: false, + ...a, + ...requiredLikeToAbove(a.required), + } + }, + string.optional(), + ) } static multiselect>(a: { name: string @@ -541,7 +563,7 @@ export class Value { Default is false */ immutable?: boolean }) { - return new Value<(keyof Values)[], never>( + return new Value<(keyof Values)[], never, never>( () => ({ type: "multiselect" as const, minLength: null, @@ -557,9 +579,10 @@ export class Value { ), ) } - static dynamicMultiselect( + static dynamicMultiselect( getA: LazyBuild< Store, + Vault, { name: string description?: string | null @@ -572,7 +595,7 @@ export class Value { } >, ) { - return new Value(async (options) => { + return new Value(async (options) => { const a = await getA(options) return { type: "multiselect" as const, @@ -586,15 +609,15 @@ export class Value { } }, arrayOf(string)) } - static object, Store>( + static object, Store, Vault>( a: { name: string description?: string | null warning?: string | null }, - previousSpec: Config, + previousSpec: Config, ) { - return new Value(async (options) => { + return new Value(async (options) => { const spec = await previousSpec.build(options as any) return { type: "object" as const, @@ -605,7 +628,7 @@ export class Value { } }, previousSpec.validator) } - static union, Type, Store>( + static union, Type, Store, Vault>( a: { name: string description?: string | null @@ -615,9 +638,9 @@ export class Value { Default is false */ immutable?: boolean }, - aVariants: Variants, + aVariants: Variants, ) { - return new Value, Store>( + return new Value, Store, Vault>( async (options) => ({ type: "union" as const, description: null, @@ -634,17 +657,18 @@ export class Value { Required extends RequiredDefault, Type extends Record, Store = never, + Vault = never, >( - getDisabledFn: LazyBuild, + getDisabledFn: LazyBuild, a: { name: string description?: string | null warning?: string | null required: Required }, - aVariants: Variants | Variants, + aVariants: Variants | Variants, ) { - return new Value, Store>( + return new Value, Store, Vault>( async (options) => ({ type: "union" as const, description: null, @@ -659,8 +683,11 @@ export class Value { ) } - static list(a: List) { - return new Value((options) => a.build(options), a.validator) + static list(a: List) { + return new Value( + (options) => a.build(options), + a.validator, + ) } /** @@ -678,6 +705,6 @@ export class Value { ``` */ withStore() { - return this as any as Value + return this as any as Value } } diff --git a/lib/config/builder/variants.ts b/lib/config/builder/variants.ts index 01f11be..06ee57c 100644 --- a/lib/config/builder/variants.ts +++ b/lib/config/builder/variants.ts @@ -51,20 +51,21 @@ export const pruning = Value.union( ); ``` */ -export class Variants { +export class Variants { static text: any private constructor( - public build: LazyBuild, + public build: LazyBuild, public validator: Parser, ) {} static of< VariantValues extends { [K in string]: { name: string - spec: Config | Config + spec: Config | Config } }, Store, + Vault, >(a: VariantValues) { const validator = anyOf( ...Object.entries(a).map(([name, { spec }]) => @@ -81,11 +82,12 @@ export class Variants { unionSelectKey: K // prettier-ignore unionValueKey: - VariantValues[K]["spec"] extends (Config | Config) ? B : + VariantValues[K]["spec"] extends (Config | Config) ? B : never } }[keyof VariantValues], - Store + Store, + Vault >(async (options) => { const variants = {} as { [K in keyof VariantValues]: { name: string; spec: InputSpec } @@ -115,6 +117,6 @@ export class Variants { ``` */ withStore() { - return this as any as Variants + return this as any as Variants } } diff --git a/lib/config/configConstants.ts b/lib/config/configConstants.ts index bddb668..54e0e62 100644 --- a/lib/config/configConstants.ts +++ b/lib/config/configConstants.ts @@ -18,7 +18,7 @@ export const smtpConfig = Value.filteredUnion( system: { name: "System Credentials", spec: Config.of({}) }, custom: { name: "Custom Credentials", - spec: Config.of, never>({ + spec: Config.of, never, never>({ server: Value.text({ name: "SMTP Server", required: { diff --git a/lib/config/setupConfig.ts b/lib/config/setupConfig.ts index cfd1979..50cb93b 100644 --- a/lib/config/setupConfig.ts +++ b/lib/config/setupConfig.ts @@ -12,15 +12,16 @@ export type DependenciesReceipt = void & { export type Save< Store, + Vault, A extends | Record - | Config, any> - | Config, never>, + | Config, any, any> + | Config, never, never>, Manifest extends SDKManifest, > = (options: { effects: Effects input: ExtractConfigType & Record - utils: Utils + utils: Utils dependencies: D.ConfigDependencies }) => Promise<{ dependenciesReceipt: DependenciesReceipt @@ -28,13 +29,14 @@ export type Save< }> export type Read< Store, + Vault, A extends | Record - | Config, any> - | Config, never>, + | Config, any, any> + | Config, never, never>, > = (options: { effects: Effects - utils: Utils + utils: Utils }) => Promise & Record)> /** * We want to setup a config export with a get and set, this @@ -45,16 +47,17 @@ export type Read< */ export function setupConfig< Store, + Vault, ConfigType extends | Record - | Config - | Config, + | Config + | Config, Manifest extends SDKManifest, Type extends Record = ExtractConfigType, >( - spec: Config | Config, - write: Save, - read: Read, + spec: Config | Config, + write: Save, + read: Read, ) { const validator = spec.validator return { @@ -74,7 +77,7 @@ export function setupConfig< } }) as ExpectedExports.setConfig, getConfig: (async ({ effects }) => { - const myUtils = utils(effects) + const myUtils = utils(effects) const configValue = nullIfEmpty( (await read({ effects, utils: myUtils })) || null, ) diff --git a/lib/inits/migrations/Migration.ts b/lib/inits/migrations/Migration.ts index 15d23ac..2aed622 100644 --- a/lib/inits/migrations/Migration.ts +++ b/lib/inits/migrations/Migration.ts @@ -2,27 +2,39 @@ import { ManifestVersion } from "../../manifest/ManifestTypes" import { Effects } from "../../types" import { Utils } from "../../util/utils" -export class Migration { +export class Migration { constructor( readonly options: { version: Version - up: (opts: { effects: Effects; utils: Utils }) => Promise - down: (opts: { effects: Effects; utils: Utils }) => Promise + up: (opts: { + effects: Effects + utils: Utils + }) => Promise + down: (opts: { + effects: Effects + utils: Utils + }) => Promise }, ) {} - static of(options: { + static of(options: { version: Version - up: (opts: { effects: Effects; utils: Utils }) => Promise - down: (opts: { effects: Effects; utils: Utils }) => Promise + up: (opts: { + effects: Effects + utils: Utils + }) => Promise + down: (opts: { + effects: Effects + utils: Utils + }) => Promise }) { - return new Migration(options) + return new Migration(options) } - async up(opts: { effects: Effects; utils: Utils }) { + async up(opts: { effects: Effects; utils: Utils }) { this.up(opts) } - async down(opts: { effects: Effects; utils: Utils }) { + async down(opts: { effects: Effects; utils: Utils }) { this.down(opts) } } diff --git a/lib/inits/migrations/setupMigrations.ts b/lib/inits/migrations/setupMigrations.ts index 8ca7453..a7278c7 100644 --- a/lib/inits/migrations/setupMigrations.ts +++ b/lib/inits/migrations/setupMigrations.ts @@ -1,4 +1,3 @@ -import { setupActions } from "../../actions/setupActions" import { EmVer } from "../../emverLite/mod" import { SDKManifest } from "../../manifest/ManifestTypes" import { ExpectedExports } from "../../types" @@ -6,30 +5,34 @@ import { createUtils } from "../../util" import { once } from "../../util/once" import { Migration } from "./Migration" -export class Migrations { +export class Migrations { private constructor( readonly manifest: SDKManifest, - readonly migrations: Array>, + readonly migrations: Array>, ) {} private sortedMigrations = once(() => { const migrationsAsVersions = ( - this.migrations as Array> + this.migrations as Array> ).map((x) => [EmVer.parse(x.options.version), x] as const) migrationsAsVersions.sort((a, b) => a[0].compareForSort(b[0])) return migrationsAsVersions }) private currentVersion = once(() => EmVer.parse(this.manifest.version)) - static of>>( - manifest: SDKManifest, - ...migrations: EnsureUniqueId - ) { - return new Migrations(manifest, migrations as Array>) + static of< + Store, + Vault, + Migrations extends Array>, + >(manifest: SDKManifest, ...migrations: EnsureUniqueId) { + return new Migrations( + manifest, + migrations as Array>, + ) } async init({ effects, previousVersion, }: Parameters[0]) { - const utils = createUtils(effects) + const utils = createUtils(effects) if (!!previousVersion) { const previousVersionEmVer = EmVer.parse(previousVersion) for (const [_, migration] of this.sortedMigrations() @@ -43,7 +46,7 @@ export class Migrations { effects, nextVersion, }: Parameters[0]) { - const utils = createUtils(effects) + const utils = createUtils(effects) if (!!nextVersion) { const nextVersionEmVer = EmVer.parse(nextVersion) const reversed = [...this.sortedMigrations()].reverse() @@ -58,15 +61,16 @@ export class Migrations { export function setupMigrations< Store, - Migrations extends Array>, + Vault, + Migrations extends Array>, >(manifest: SDKManifest, ...migrations: EnsureUniqueId) { - return Migrations.of(manifest, ...migrations) + return Migrations.of(manifest, ...migrations) } // prettier-ignore export type EnsureUniqueId = B extends [] ? A : - B extends [Migration, ...infer Rest] ? ( + B extends [Migration, ...infer Rest] ? ( id extends ids ? "One of the ids are not unique"[] : EnsureUniqueId ) : "There exists a migration that is not a Migration"[] diff --git a/lib/inits/setupInit.ts b/lib/inits/setupInit.ts index fac028f..ad5414b 100644 --- a/lib/inits/setupInit.ts +++ b/lib/inits/setupInit.ts @@ -3,10 +3,10 @@ import { Migrations } from "./migrations/setupMigrations" import { Install } from "./setupInstall" import { Uninstall } from "./setupUninstall" -export function setupInit( - migrations: Migrations, - install: Install, - uninstall: Uninstall, +export function setupInit( + migrations: Migrations, + install: Install, + uninstall: Uninstall, ): { init: ExpectedExports.init uninit: ExpectedExports.uninit diff --git a/lib/inits/setupInstall.ts b/lib/inits/setupInstall.ts index 98e0be5..a24f0e3 100644 --- a/lib/inits/setupInstall.ts +++ b/lib/inits/setupInstall.ts @@ -1,13 +1,13 @@ import { Effects, ExpectedExports } from "../types" import { Utils, utils } from "../util/utils" -export type InstallFn = (opts: { +export type InstallFn = (opts: { effects: Effects - utils: Utils + utils: Utils }) => Promise -export class Install { - private constructor(readonly fn: InstallFn) {} - static of(fn: InstallFn) { +export class Install { + private constructor(readonly fn: InstallFn) {} + static of(fn: InstallFn) { return new Install(fn) } @@ -23,6 +23,6 @@ export class Install { } } -export function setupInstall(fn: InstallFn) { +export function setupInstall(fn: InstallFn) { return Install.of(fn) } diff --git a/lib/inits/setupUninstall.ts b/lib/inits/setupUninstall.ts index 6a76463..209ffd9 100644 --- a/lib/inits/setupUninstall.ts +++ b/lib/inits/setupUninstall.ts @@ -1,13 +1,13 @@ import { Effects, ExpectedExports } from "../types" import { Utils, utils } from "../util/utils" -export type UninstallFn = (opts: { +export type UninstallFn = (opts: { effects: Effects - utils: Utils + utils: Utils }) => Promise -export class Uninstall { - private constructor(readonly fn: UninstallFn) {} - static of(fn: UninstallFn) { +export class Uninstall { + private constructor(readonly fn: UninstallFn) {} + static of(fn: UninstallFn) { return new Uninstall(fn) } @@ -23,6 +23,6 @@ export class Uninstall { } } -export function setupUninstall(fn: UninstallFn) { +export function setupUninstall(fn: UninstallFn) { return Uninstall.of(fn) } diff --git a/lib/mainFn/index.ts b/lib/mainFn/index.ts index 380dc7d..833107e 100644 --- a/lib/mainFn/index.ts +++ b/lib/mainFn/index.ts @@ -23,17 +23,17 @@ import "./Daemons" * @param fn * @returns */ -export const setupMain = ( +export const setupMain = ( fn: (o: { effects: Effects started(onTerm: () => void): null - utils: Utils + utils: Utils }) => Promise>, ): ExpectedExports.main => { return async (options) => { const result = await fn({ ...options, - utils: createMainUtils(options.effects), + utils: createMainUtils(options.effects), }) await result.build().then((x) => x.wait()) } diff --git a/lib/store/getStore.ts b/lib/store/getStore.ts index bff6eec..4ea3a94 100644 --- a/lib/store/getStore.ts +++ b/lib/store/getStore.ts @@ -1,6 +1,4 @@ -import { Parser } from "ts-matches" import { Effects, EnsureStorePath } from "../types" -import { NoAny } from "../util" export class GetStore { constructor( diff --git a/lib/test/output.sdk.ts b/lib/test/output.sdk.ts index a69ccb8..453b896 100644 --- a/lib/test/output.sdk.ts +++ b/lib/test/output.sdk.ts @@ -4,4 +4,5 @@ export type Manifest = any export const sdk = StartSdk.of() .withManifest({} as any) .withStore<{ storeRoot: { storeLeaf: "value" } }>() + .withVault<{ vaultRoot: "value" }>() .build(true) diff --git a/lib/test/store.test.ts b/lib/test/store.test.ts index 5eb43a3..e3d7931 100644 --- a/lib/test/store.test.ts +++ b/lib/test/store.test.ts @@ -2,11 +2,14 @@ import { Effects } from "../types" import { createMainUtils } from "../util" import { utils } from "../util/utils" -type WrapperType = { +type Store = { config: { someValue: "a" | "b" } } +type Vault = { + hello: string +} const todo = (): A => { throw new Error("not implemented") } @@ -14,14 +17,14 @@ const noop = () => {} describe("Store", () => { test("types", async () => { ;async () => { - utils(todo()).store.setOwn("/config", { + utils(todo()).store.setOwn("/config", { someValue: "a", }) - utils(todo()).store.setOwn("/config/someValue", "b") - utils(todo()).store.setOwn("", { + utils(todo()).store.setOwn("/config/someValue", "b") + utils(todo()).store.setOwn("", { config: { someValue: "b" }, }) - utils(todo()).store.setOwn( + utils(todo()).store.setOwn( "/config/someValue", // @ts-expect-error Type is wrong for the setting value @@ -33,75 +36,75 @@ describe("Store", () => { "someValue", ) - todo().store.set({ + todo().store.set({ path: "/config/someValue", value: "b", }) - todo().store.set({ + todo().store.set({ //@ts-expect-error Path is wrong path: "/config/someValue", //@ts-expect-error Path is wrong value: "someValueIn", }) - todo().store.set({ + todo().store.set({ //@ts-expect-error Path is wrong path: "/config/some2Value", value: "a", }) - ;(await createMainUtils(todo()) + ;(await createMainUtils(todo()) .store.getOwn("/config/someValue") .const()) satisfies string - ;(await createMainUtils(todo()) + ;(await createMainUtils(todo()) .store.getOwn("/config") - .const()) satisfies WrapperType["config"] + .const()) satisfies Store["config"] await createMainUtils(todo()) // @ts-expect-error Path is wrong .store.getOwn("/config/somdsfeValue") .const() /// ----------------- ERRORS ----------------- - utils(todo()).store.setOwn("", { + utils(todo()).store.setOwn("", { // @ts-expect-error Type is wrong for the setting value config: { someValue: "notInAOrB" }, }) - utils(todo()).store.setOwn( + utils(todo()).store.setOwn( "/config/someValue", // @ts-expect-error Type is wrong for the setting value "notInAOrB", ) - ;(await utils(todo()) + ;(await utils(todo()) .store.getOwn("/config/someValue") // @ts-expect-error Const should normally not be callable .const()) satisfies string - ;(await utils(todo()) + ;(await utils(todo()) .store.getOwn("/config") // @ts-expect-error Const should normally not be callable - .const()) satisfies WrapperType["config"] - await utils(todo()) + .const()) satisfies Store["config"] + await utils(todo()) // @ts-expect-error Path is wrong .store.getOwn("/config/somdsfeValue") // @ts-expect-error Const should normally not be callable .const() /// - ;(await utils(todo()) + ;(await utils(todo()) .store.getOwn("/config/someValue") // @ts-expect-error satisfies type is wrong .const()) satisfies number ;(await createMainUtils(todo()) // @ts-expect-error Path is wrong .store.getOwn("/config/") - .const()) satisfies WrapperType["config"] - ;(await todo().store.get({ + .const()) satisfies Store["config"] + ;(await todo().store.get({ path: "/config/someValue", callback: noop, })) satisfies string - await todo().store.get({ + await todo().store.get({ // @ts-expect-error Path is wrong as in it doesn't match above path: "/config/someV2alue", callback: noop, }) - await todo().store.get({ + await todo().store.get({ // @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 2176a88..12be419 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -398,7 +398,7 @@ export type Effects = { vault: { list(): Promise - get(opt: { key: string }): Promise + get(opt: { key: string; callback: () => void }): Promise set(opt: { key: string; value: string }): Promise move(opt: { fromKey: string; toKey: string }): Promise delete(opt: { key: string }): Promise diff --git a/lib/util/index.ts b/lib/util/index.ts index ab8ee08..03dac09 100644 --- a/lib/util/index.ts +++ b/lib/util/index.ts @@ -22,8 +22,8 @@ export const isKnownError = (e: unknown): e is T.KnownError => declare const affine: unique symbol export const createUtils = utils -export const createMainUtils = (effects: T.Effects) => - createUtils(effects) +export const createMainUtils = (effects: T.Effects) => + createUtils(effects) type NeverPossible = { [affine]: string } export type NoAny = NeverPossible extends A diff --git a/lib/util/utils.ts b/lib/util/utils.ts index 7979ce6..5e5e54d 100644 --- a/lib/util/utils.ts +++ b/lib/util/utils.ts @@ -15,8 +15,9 @@ import { TorHostname } from "../mainFn/TorHostname" import { DefaultString } from "../config/configTypes" import { getDefaultString } from "./getDefaultString" import { GetStore, getStore } from "../store/getStore" +import { GetVault, getVault } from "./getVault" -export type Utils = { +export type Utils = { createOrUpdateVault: (opts: { key: string value: string | null | undefined @@ -41,6 +42,10 @@ export type Utils = { value: ExtractStore, ) => Promise } + vault: { + get: (key: keyof Vault & string) => GetVault & WrapperOverWrite + set: (key: keyof Vault & string, value: string) => Promise + } checkPortListening( port: number, options: { @@ -63,9 +68,13 @@ export type Utils = { torHostName: (id: string) => TorHostname nullIfEmpty: typeof nullIfEmpty } -export const utils = ( +export const utils = < + Store = never, + Vault = never, + WrapperOverWrite = { const: never }, +>( effects: T.Effects, -): Utils => ({ +): Utils => ({ createOrUpdateVault: async ({ key, value, @@ -79,7 +88,7 @@ export const utils = ( await effects.vault.set({ key, value }) return value } - if (await effects.vault.get({ key })) { + if (await effects.vault.get({ key, callback: noop })) { return null } const newValue = getDefaultString(generator) @@ -93,7 +102,7 @@ export const utils = ( fileHelper.write(data, effects), nullIfEmpty, store: { - get: ( + get: ( packageId: string, path: T.EnsureStorePath, ) => @@ -112,4 +121,12 @@ export const utils = ( bindLan: async (port: number) => LocalPort.bindLan(effects, port), networkBuilder: () => NetworkBuilder.of(effects), torHostName: (id: string) => TorHostname.of(effects, id), + + vault: { + get: (key: keyof Vault & string) => + getVault(effects, key) as GetVault & WrapperOverWrite, + set: (key: keyof Vault & string, value: string) => + effects.vault.set({ key, value }), + }, }) +function noop(): void {}