feat: creating the rest of the sdk

This commit is contained in:
BluJ
2023-05-09 11:42:26 -06:00
parent 72df4cb502
commit 07493551b1
30 changed files with 568 additions and 504 deletions

View File

@@ -1,11 +1,13 @@
import { AnyParser } from "ts-matches" import { AnyParser } from "ts-matches"
import { SDKManifest } from "./manifest/ManifestTypes" import { ManifestVersion, SDKManifest } from "./manifest/ManifestTypes"
import { RequiredDefault, Value } from "./config/builder/value" import { RequiredDefault, Value } from "./config/builder/value"
import { Config, ExtractConfigType, LazyBuild } from "./config/builder/config" import { Config, ExtractConfigType, LazyBuild } from "./config/builder/config"
import { import {
DefaultString, DefaultString,
ListValueSpecText,
Pattern, Pattern,
RandomString, RandomString,
UniqueBy,
ValueSpecDatetime, ValueSpecDatetime,
ValueSpecText, ValueSpecText,
} from "./config/configTypes" } from "./config/configTypes"
@@ -18,10 +20,34 @@ import {
Metadata, Metadata,
BackupOptions, BackupOptions,
} from "./types" } from "./types"
import { Utils } from "./util" import { Utils } from "./util/utils"
import { AutoConfig, AutoConfigFrom } from "./autoconfig/AutoConfig" import { AutoConfig, AutoConfigFrom } from "./autoconfig/AutoConfig"
import { BackupSet, Backups } from "./backup/Backups" import { BackupSet, Backups } from "./backup/Backups"
import { smtpConfig } from "./config/configConstants" import { smtpConfig } from "./config/configConstants"
import { Daemons } from "./mainFn/Daemons"
import { healthCheck } from "./health/HealthCheck"
import {
checkPortListening,
containsAddress,
} from "./health/checkFns/checkPortListening"
import { checkWebUrl, runHealthScript } from "./health/checkFns"
import { List } from "./config/builder/list"
import { Migration } from "./inits/migrations/Migration"
import { Install, InstallFn, setupInstall } from "./inits/setupInstall"
import { setupActions } from "./actions/setupActions"
import { setupAutoConfig } from "./autoconfig/setupAutoConfig"
import { SetupBackupsParams, setupBackups } from "./backup/setupBackups"
import { setupInit } from "./inits/setupInit"
import {
EnsureUniqueId,
Migrations,
setupMigrations,
} from "./inits/migrations/setupMigrations"
import { Uninstall, UninstallFn, setupUninstall } from "./inits/setupUninstall"
import { setupMain } from "./mainFn"
import { defaultTrigger } from "./trigger/defaultTrigger"
import { changeOnFirstSuccess, cooldownTrigger } from "./trigger"
import setupConfig, { Read, Save } from "./config/setupConfig"
// prettier-ignore // prettier-ignore
type AnyNeverCond<T extends any[], Then, Else> = type AnyNeverCond<T extends any[], Then, Else> =
@@ -30,7 +56,7 @@ type AnyNeverCond<T extends any[], Then, Else> =
T extends [any, ...infer U] ? AnyNeverCond<U,Then, Else> : T extends [any, ...infer U] ? AnyNeverCond<U,Then, Else> :
never never
class StartSDK<Manifest extends SDKManifest, Store> { export class StartSDK<Manifest extends SDKManifest, Store> {
private constructor() {} private constructor() {}
private anyOf<A>( private anyOf<A>(
a: A, a: A,
@@ -65,10 +91,10 @@ class StartSDK<Manifest extends SDKManifest, Store> {
}, },
Config: { Config: {
of: < of: <
Spec extends Record<string, Value<any, Manifest> | Value<any, never>>, Spec extends Record<string, Value<any, Store> | Value<any, never>>,
>( >(
spec: Spec, spec: Spec,
) => Config.of(spec), ) => Config.of<Spec, Store>(spec),
}, },
configConstants: { smtpConfig }, configConstants: { smtpConfig },
createAction: < createAction: <
@@ -88,22 +114,137 @@ class StartSDK<Manifest extends SDKManifest, Store> {
input: Type input: Type
}) => Promise<ActionResult>, }) => Promise<ActionResult>,
) => createAction<Store, ConfigType, Type>(metaData, fn), ) => createAction<Store, ConfigType, Type>(metaData, fn),
// TODO Daemons Daemons: { of: Daemons.of },
// TODO HealthCheck healthCheck: {
// TODO healthCheckFns checkPortListening,
// TODO List checkWebUrl,
// TODO mainNetwork of: healthCheck,
// TODO Migration runHealthScript,
// TODO setupActions },
// TODO setupAutoConfig List: {
// TODO setupBackup text: List.text,
// TODO setupInit number: List.number,
// TODO setupInstall obj: <Type extends Record<string, any>>(
// TODO setupMain a: {
// TODO setupManifest name: string
// TODO setupMigrations description?: string | null
// TODO setupUninstall warning?: string | null
// TODO trigger changeOnFirstSuccess, cooldown, default /** Default [] */
default?: []
minLength?: number | null
maxLength?: number | null
},
aSpec: {
spec: Config<Type, Store>
displayAs?: null | string
uniqueBy?: null | UniqueBy
},
) => List.obj<Type, Store>(a, aSpec),
dynamicText: (
getA: LazyBuild<
Store,
{
name: string
description?: string | null
warning?: string | null
/** Default = [] */
default?: string[]
minLength?: number | null
maxLength?: number | null
disabled?: false | string
generate?: null | RandomString
spec: {
/** Default = false */
masked?: boolean
placeholder?: string | null
minLength?: number | null
maxLength?: number | null
patterns: Pattern[]
/** Default = "text" */
inputmode?: ListValueSpecText["inputmode"]
}
}
>,
) => List.dynamicText<Store>(getA),
dynamicNumber: (
getA: LazyBuild<
Store,
{
name: string
description?: string | null
warning?: string | null
/** Default = [] */
default?: string[]
minLength?: number | null
maxLength?: number | null
disabled?: false | string
spec: {
integer: boolean
min?: number | null
max?: number | null
step?: string | null
units?: string | null
placeholder?: string | null
}
}
>,
) => List.dynamicNumber<Store>(getA),
},
Migration: {
of: <Version extends ManifestVersion>(options: {
version: Version
up: (opts: { effects: Effects; utils: Utils<Store> }) => Promise<void>
down: (opts: {
effects: Effects
utils: Utils<Store>
}) => Promise<void>
}) => Migration.of<Store, Version>(options),
},
setupActions,
setupAutoConfig: <
Input,
NestedConfigs extends {
[key in keyof Manifest["dependencies"]]: unknown
},
>(
configs: AutoConfigFrom<Store, Input, NestedConfigs>,
) => setupAutoConfig<Store, Input, Manifest, NestedConfigs>(configs),
setupBackups: (...args: SetupBackupsParams<Manifest>) =>
setupBackups<Manifest>(...args),
setupConfig: <
ConfigType extends
| Record<string, any>
| Config<any, any>
| Config<any, never>,
Type extends Record<string, any> = ExtractConfigType<ConfigType>,
>(
spec: Config<Type, Store> | Config<Type, never>,
write: Save<Store, Type, Manifest>,
read: Read<Store, Type>,
) => setupConfig<Store, ConfigType, Manifest, Type>(spec, write, read),
setupInit: (
migrations: Migrations<Store>,
install: Install<Store>,
uninstall: Uninstall<Store>,
) => setupInit<Store>(migrations, install, uninstall),
setupInstall: (fn: InstallFn<Store>) => Install.of(fn),
setupMain: (
fn: (o: {
effects: Effects
started(onTerm: () => void): null
utils: Utils<Store, {}>
}) => Promise<Daemons<any>>,
) => setupMain<Store>(fn),
setupMigrations: <Migrations extends Array<Migration<Store, any>>>(
manifest: SDKManifest,
...migrations: EnsureUniqueId<Migrations>
) => setupMigrations<Store, Migrations>(manifest, ...migrations),
setupUninstall: (fn: UninstallFn<Store>) => setupUninstall<Store>(fn),
trigger: {
defaultTrigger,
cooldownTrigger,
changeOnFirstSuccess,
},
Value: { Value: {
toggle: Value.toggle, toggle: Value.toggle,
text: Value.text, text: Value.text,
@@ -261,13 +402,18 @@ class StartSDK<Manifest extends SDKManifest, Store> {
aVariants, aVariants,
), ),
}, },
// TODO Variants Variants: {
of: <
VariantValues extends {
[K in string]: {
name: string
spec: Config<any, Store>
}
},
>(
a: VariantValues,
) => Variants.of<VariantValues, Store>(a),
},
}) })
} }
} }
// TODO Test output.ts with sdk
// const test = StartSDK.of()
// .withManifest<any>()
// .withStore<{}>()
// .Value.dynamicToggle({} as any, {} as any)

View File

@@ -1,6 +1,7 @@
import { Config, ExtractConfigType } from "../config/builder/config" import { Config, ExtractConfigType } from "../config/builder/config"
import { ActionMetaData, ActionResult, Effects, ExportedAction } from "../types" import { ActionMetaData, ActionResult, Effects, ExportedAction } from "../types"
import { Utils, createUtils, utils } from "../util" import { createUtils } from "../util"
import { Utils, utils } from "../util/utils"
export class CreatedAction< export class CreatedAction<
Store, Store,

View File

@@ -1,5 +1,5 @@
import { AutoConfigure, DeepPartial, Effects, ExpectedExports } from "../types" import { AutoConfigure, DeepPartial, Effects, ExpectedExports } from "../types"
import { Utils, utils } from "../util" import { Utils, utils } from "../util/utils"
import { deepEqual } from "../util/deepEqual" import { deepEqual } from "../util/deepEqual"
import { deepMerge } from "../util/deepMerge" import { deepMerge } from "../util/deepMerge"

View File

@@ -1,28 +1,24 @@
import { SDKManifest } from "../manifest/ManifestTypes" import { SDKManifest } from "../manifest/ManifestTypes"
import { WrapperDataContract } from "../wrapperData/wrapperDataContract"
import { AutoConfig, AutoConfigFrom } from "./AutoConfig" import { AutoConfig, AutoConfigFrom } from "./AutoConfig"
export function setupAutoConfig< export function setupAutoConfig<
WD, Store,
Input, Input,
Manifest extends SDKManifest, Manifest extends SDKManifest,
NestedConfigs extends { NestedConfigs extends {
[key in keyof Manifest["dependencies"]]: unknown [key in keyof Manifest["dependencies"]]: unknown
}, },
>( >(configs: AutoConfigFrom<Store, Input, NestedConfigs>) {
wrapperDataContract: WrapperDataContract<WD>,
configs: AutoConfigFrom<WD, Input, NestedConfigs>,
) {
type C = typeof configs type C = typeof configs
const answer = { ...configs } as unknown as { const answer = { ...configs } as unknown as {
[k in keyof C]: AutoConfig<WD, Input, NestedConfigs> [k in keyof C]: AutoConfig<Store, Input, NestedConfigs>
} }
for (const key in configs) { for (const key in configs) {
answer[key as keyof typeof configs] = new AutoConfig< answer[key as keyof typeof configs] = new AutoConfig<
WD, Store,
Input, Input,
NestedConfigs NestedConfigs
>(wrapperDataContract, configs, key as keyof typeof configs) >(configs, key as keyof typeof configs)
} }
return answer return answer
} }

View File

@@ -1,16 +1,16 @@
import { ValueSpec } from "../configTypes" import { ValueSpec } from "../configTypes"
import { Utils } from "../../util" import { Utils } from "../../util/utils"
import { Value } from "./value" import { Value } from "./value"
import { _ } from "../../util" import { _ } from "../../util"
import { Effects } from "../../types" import { Effects } from "../../types"
import { Parser, object } from "ts-matches" import { Parser, object } from "ts-matches"
export type LazyBuildOptions<WD> = { export type LazyBuildOptions<Store> = {
effects: Effects effects: Effects
utils: Utils<WD> utils: Utils<Store>
} }
export type LazyBuild<WD, ExpectedOut> = ( export type LazyBuild<Store, ExpectedOut> = (
options: LazyBuildOptions<WD>, options: LazyBuildOptions<Store>,
) => Promise<ExpectedOut> | ExpectedOut ) => Promise<ExpectedOut> | ExpectedOut
// prettier-ignore // prettier-ignore
@@ -18,8 +18,8 @@ export type ExtractConfigType<A extends Record<string, any> | Config<Record<stri
A extends Config<infer B, any> | Config<infer B, never> ? B : A extends Config<infer B, any> | Config<infer B, never> ? B :
A A
export type ConfigSpecOf<A extends Record<string, any>, WD = never> = { export type ConfigSpecOf<A extends Record<string, any>, Store = never> = {
[K in keyof A]: Value<A[K], WD> [K in keyof A]: Value<A[K], Store>
} }
export type MaybeLazyValues<A> = LazyBuild<any, A> | A export type MaybeLazyValues<A> = LazyBuild<any, A> | A
@@ -79,14 +79,14 @@ export const addNodesSpec = Config.of({ hostname: hostname, port: port });
``` ```
*/ */
export class Config<Type extends Record<string, any>, WD> { export class Config<Type extends Record<string, any>, Store> {
private constructor( private constructor(
private readonly spec: { private readonly spec: {
[K in keyof Type]: Value<Type[K], WD> | Value<Type[K], never> [K in keyof Type]: Value<Type[K], Store> | Value<Type[K], never>
}, },
public validator: Parser<unknown, Type>, public validator: Parser<unknown, Type>,
) {} ) {}
async build(options: LazyBuildOptions<WD>) { async build(options: LazyBuildOptions<Store>) {
const answer = {} as { const answer = {} as {
[K in keyof Type]: ValueSpec [K in keyof Type]: ValueSpec
} }
@@ -96,9 +96,10 @@ export class Config<Type extends Record<string, any>, WD> {
return answer return answer
} }
static of<Spec extends Record<string, Value<any, any> | Value<any, never>>>( static of<
spec: Spec, Spec extends Record<string, Value<any, Store> | Value<any, never>>,
) { Store,
>(spec: Spec) {
const validatorObj = {} as { const validatorObj = {} as {
[K in keyof Spec]: Parser<unknown, any> [K in keyof Spec]: Parser<unknown, any>
} }
@@ -109,14 +110,12 @@ export class Config<Type extends Record<string, any>, WD> {
return new Config< return new Config<
{ {
[K in keyof Spec]: Spec[K] extends [K in keyof Spec]: Spec[K] extends
| Value<infer T, any> | Value<infer T, Store>
| Value<infer T, never> | Value<infer T, never>
? T ? T
: never : never
}, },
{ Store
[K in keyof Spec]: Spec[K] extends Value<any, infer WD> ? WD : never
}[keyof Spec]
>(spec, validator as any) >(spec, validator as any)
} }
@@ -129,12 +128,12 @@ export class Config<Type extends Record<string, any>, WD> {
required: false, required: false,
}) })
return Config.of<WrapperData>()({ return Config.of<Store>()({
myValue: a.withWrapperData(), myValue: a.withStore(),
}) })
``` ```
*/ */
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() { withStore<NewStore extends Store extends never ? any : Store>() {
return this as any as Config<Type, NewWrapperData> return this as any as Config<Type, NewStore>
} }
} }

View File

@@ -9,7 +9,6 @@ import {
ValueSpecText, ValueSpecText,
} from "../configTypes" } from "../configTypes"
import { Parser, arrayOf, number, string } from "ts-matches" import { Parser, arrayOf, number, string } from "ts-matches"
import { WrapperDataContract } from "../../wrapperData/wrapperDataContract"
/** /**
* Used as a subtype of Value.list * Used as a subtype of Value.list
```ts ```ts
@@ -23,9 +22,9 @@ export const authorizationList = List.string({
export const auth = Value.list(authorizationList); export const auth = Value.list(authorizationList);
``` ```
*/ */
export class List<Type, WD> { export class List<Type, Store> {
private constructor( private constructor(
public build: LazyBuild<WD, ValueSpecList>, public build: LazyBuild<Store, ValueSpecList>,
public validator: Parser<unknown, Type>, public validator: Parser<unknown, Type>,
) {} ) {}
static text( static text(
@@ -74,10 +73,9 @@ export class List<Type, WD> {
} satisfies ValueSpecListOf<"text"> } satisfies ValueSpecListOf<"text">
}, arrayOf(string)) }, arrayOf(string))
} }
static dynamicText<WD = never>( static dynamicText<Store = never>(
_wrapperDataContract: WrapperDataContract<WD>,
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -101,7 +99,7 @@ export class List<Type, WD> {
} }
>, >,
) { ) {
return new List<string[], WD>(async (options) => { return new List<string[], Store>(async (options) => {
const { spec: aSpec, ...a } = await getA(options) const { spec: aSpec, ...a } = await getA(options)
const spec = { const spec = {
type: "text" as const, type: "text" as const,
@@ -168,10 +166,9 @@ export class List<Type, WD> {
} satisfies ValueSpecListOf<"number"> } satisfies ValueSpecListOf<"number">
}, arrayOf(number)) }, arrayOf(number))
} }
static dynamicNumber<WD = never>( static dynamicNumber<Store = never>(
_wrapperDataContract: WrapperDataContract<WD>,
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -192,7 +189,7 @@ export class List<Type, WD> {
} }
>, >,
) { ) {
return new List<number[], WD>(async (options) => { return new List<number[], Store>(async (options) => {
const { spec: aSpec, ...a } = await getA(options) const { spec: aSpec, ...a } = await getA(options)
const spec = { const spec = {
type: "number" as const, type: "number" as const,
@@ -216,7 +213,7 @@ export class List<Type, WD> {
} }
}, arrayOf(number)) }, arrayOf(number))
} }
static obj<Type extends Record<string, any>, WrapperData>( static obj<Type extends Record<string, any>, Store>(
a: { a: {
name: string name: string
description?: string | null description?: string | null
@@ -227,12 +224,12 @@ export class List<Type, WD> {
maxLength?: number | null maxLength?: number | null
}, },
aSpec: { aSpec: {
spec: Config<Type, WrapperData> spec: Config<Type, Store>
displayAs?: null | string displayAs?: null | string
uniqueBy?: null | UniqueBy uniqueBy?: null | UniqueBy
}, },
) { ) {
return new List<Type[], WrapperData>(async (options) => { return new List<Type[], Store>(async (options) => {
const { spec: previousSpecSpec, ...restSpec } = aSpec const { spec: previousSpecSpec, ...restSpec } = aSpec
const specSpec = await previousSpecSpec.build(options) const specSpec = await previousSpecSpec.build(options)
const spec = { const spec = {
@@ -268,12 +265,12 @@ export class List<Type, WD> {
required: false, required: false,
}) })
return Config.of<WrapperData>()({ return Config.of<Store>()({
myValue: a.withWrapperData(), myValue: a.withStore(),
}) })
``` ```
*/ */
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() { withStore<NewStore extends Store extends never ? any : Store>() {
return this as any as List<Type, NewWrapperData> return this as any as List<Type, NewStore>
} }
} }

View File

@@ -94,9 +94,9 @@ const username = Value.string({
}); });
``` ```
*/ */
export class Value<Type, WD> { export class Value<Type, Store> {
protected constructor( protected constructor(
public build: LazyBuild<WD, ValueSpec>, public build: LazyBuild<Store, ValueSpec>,
public validator: Parser<unknown, Type>, public validator: Parser<unknown, Type>,
) {} ) {}
static toggle(a: { static toggle(a: {
@@ -120,9 +120,9 @@ export class Value<Type, WD> {
boolean, boolean,
) )
} }
static dynamicToggle<WD = never>( static dynamicToggle<Store = never>(
a: LazyBuild< a: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -132,7 +132,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<boolean, WD>( return new Value<boolean, Store>(
async (options) => ({ async (options) => ({
description: null, description: null,
warning: null, warning: null,
@@ -183,9 +183,9 @@ export class Value<Type, WD> {
asRequiredParser(string, a), asRequiredParser(string, a),
) )
} }
static dynamicText<WD = never>( static dynamicText<Store = never>(
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -204,7 +204,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<string | null | undefined, WD>(async (options) => { return new Value<string | null | undefined, Store>(async (options) => {
const a = await getA(options) const a = await getA(options)
return { return {
type: "text" as const, type: "text" as const,
@@ -254,9 +254,9 @@ export class Value<Type, WD> {
string, string,
) )
} }
static dynamicTextarea<WD = never>( static dynamicTextarea<Store = never>(
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -270,7 +270,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<string, WD>(async (options) => { return new Value<string, Store>(async (options) => {
const a = await getA(options) const a = await getA(options)
return { return {
description: null, description: null,
@@ -320,9 +320,9 @@ export class Value<Type, WD> {
asRequiredParser(number, a), asRequiredParser(number, a),
) )
} }
static dynamicNumber<WD = never>( static dynamicNumber<Store = never>(
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -339,7 +339,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<number | null | undefined, WD>(async (options) => { return new Value<number | null | undefined, Store>(async (options) => {
const a = await getA(options) const a = await getA(options)
return { return {
type: "number" as const, type: "number" as const,
@@ -381,9 +381,9 @@ export class Value<Type, WD> {
) )
} }
static dynamicColor<WD = never>( static dynamicColor<Store = never>(
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -394,7 +394,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<string | null | undefined, WD>(async (options) => { return new Value<string | null | undefined, Store>(async (options) => {
const a = await getA(options) const a = await getA(options)
return { return {
type: "color" as const, type: "color" as const,
@@ -438,9 +438,9 @@ export class Value<Type, WD> {
asRequiredParser(string, a), asRequiredParser(string, a),
) )
} }
static dynamicDatetime<WD = never>( static dynamicDatetime<Store = never>(
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -455,7 +455,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<string | null | undefined, WD>(async (options) => { return new Value<string | null | undefined, Store>(async (options) => {
const a = await getA(options) const a = await getA(options)
return { return {
type: "datetime" as const, type: "datetime" as const,
@@ -503,9 +503,9 @@ export class Value<Type, WD> {
) as any, ) as any,
) )
} }
static dynamicSelect<WD = never>( static dynamicSelect<Store = never>(
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -516,7 +516,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<string | null | undefined, WD>(async (options) => { return new Value<string | null | undefined, Store>(async (options) => {
const a = await getA(options) const a = await getA(options)
return { return {
description: null, description: null,
@@ -557,9 +557,9 @@ export class Value<Type, WD> {
), ),
) )
} }
static dynamicMultiselect<WD = never>( static dynamicMultiselect<Store = never>(
getA: LazyBuild< getA: LazyBuild<
WD, Store,
{ {
name: string name: string
description?: string | null description?: string | null
@@ -572,7 +572,7 @@ export class Value<Type, WD> {
} }
>, >,
) { ) {
return new Value<string[], WD>(async (options) => { return new Value<string[], Store>(async (options) => {
const a = await getA(options) const a = await getA(options)
return { return {
type: "multiselect" as const, type: "multiselect" as const,
@@ -586,15 +586,15 @@ export class Value<Type, WD> {
} }
}, arrayOf(string)) }, arrayOf(string))
} }
static object<Type extends Record<string, any>, WrapperData>( static object<Type extends Record<string, any>, Store>(
a: { a: {
name: string name: string
description?: string | null description?: string | null
warning?: string | null warning?: string | null
}, },
previousSpec: Config<Type, WrapperData>, previousSpec: Config<Type, Store>,
) { ) {
return new Value<Type, WrapperData>(async (options) => { return new Value<Type, Store>(async (options) => {
const spec = await previousSpec.build(options as any) const spec = await previousSpec.build(options as any)
return { return {
type: "object" as const, type: "object" as const,
@@ -605,7 +605,7 @@ export class Value<Type, WD> {
} }
}, previousSpec.validator) }, previousSpec.validator)
} }
static union<Required extends RequiredDefault<string>, Type, WrapperData>( static union<Required extends RequiredDefault<string>, Type, Store>(
a: { a: {
name: string name: string
description?: string | null description?: string | null
@@ -615,9 +615,9 @@ export class Value<Type, WD> {
Default is false */ Default is false */
immutable?: boolean immutable?: boolean
}, },
aVariants: Variants<Type, WrapperData>, aVariants: Variants<Type, Store>,
) { ) {
return new Value<AsRequired<Type, Required>, WrapperData>( return new Value<AsRequired<Type, Required>, Store>(
async (options) => ({ async (options) => ({
type: "union" as const, type: "union" as const,
description: null, description: null,
@@ -633,18 +633,18 @@ export class Value<Type, WD> {
static filteredUnion< static filteredUnion<
Required extends RequiredDefault<string>, Required extends RequiredDefault<string>,
Type extends Record<string, any>, Type extends Record<string, any>,
WD = never, Store = never,
>( >(
getDisabledFn: LazyBuild<WD, string[]>, getDisabledFn: LazyBuild<Store, string[]>,
a: { a: {
name: string name: string
description?: string | null description?: string | null
warning?: string | null warning?: string | null
required: Required required: Required
}, },
aVariants: Variants<Type, WD> | Variants<Type, never>, aVariants: Variants<Type, Store> | Variants<Type, never>,
) { ) {
return new Value<AsRequired<Type, Required>, WD>( return new Value<AsRequired<Type, Required>, Store>(
async (options) => ({ async (options) => ({
type: "union" as const, type: "union" as const,
description: null, description: null,
@@ -659,11 +659,8 @@ export class Value<Type, WD> {
) )
} }
static list<Type, WrapperData>(a: List<Type, WrapperData>) { static list<Type, Store>(a: List<Type, Store>) {
return new Value<Type, WrapperData>( return new Value<Type, Store>((options) => a.build(options), a.validator)
(options) => a.build(options),
a.validator,
)
} }
/** /**
@@ -675,12 +672,12 @@ export class Value<Type, WD> {
required: false, required: false,
}) })
return Config.of<WrapperData>()({ return Config.of<Store>()({
myValue: a.withWrapperData(), myValue: a.withStore(),
}) })
``` ```
*/ */
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() { withStore<NewStore extends Store extends never ? any : Store>() {
return this as any as Value<Type, NewWrapperData> return this as any as Value<Type, NewStore>
} }
} }

View File

@@ -51,24 +51,20 @@ export const pruning = Value.union(
); );
``` ```
*/ */
export class Variants<Type, WD> { export class Variants<Type, Store> {
static text: any
private constructor( private constructor(
public build: LazyBuild<WD, ValueSpecUnion["variants"]>, public build: LazyBuild<Store, ValueSpecUnion["variants"]>,
public validator: Parser<unknown, Type>, public validator: Parser<unknown, Type>,
) {} ) {}
// A extends {
// [key: string]: {
// name: string
// spec: InputSpec
// }
// },
static of< static of<
VariantValues extends { VariantValues extends {
[K in string]: { [K in string]: {
name: string name: string
spec: Config<any, any> | Config<any, never> spec: Config<any, Store> | Config<any, never>
} }
}, },
Store,
>(a: VariantValues) { >(a: VariantValues) {
const validator = anyOf( const validator = anyOf(
...Object.entries(a).map(([name, { spec }]) => ...Object.entries(a).map(([name, { spec }]) =>
@@ -83,21 +79,13 @@ export class Variants<Type, WD> {
{ {
[K in keyof VariantValues]: { [K in keyof VariantValues]: {
unionSelectKey: K unionSelectKey: K
unionValueKey: VariantValues[K]["spec"] extends // prettier-ignore
| Config<infer B, any> unionValueKey:
| Config<infer B, never> VariantValues[K]["spec"] extends (Config<infer B, Store> | Config<infer B, never>) ? B :
? B never
: never
} }
}[keyof VariantValues], }[keyof VariantValues],
{ Store
[K in keyof VariantValues]: VariantValues[K] extends Config<
any,
infer C
>
? C
: never
}[keyof VariantValues]
>(async (options) => { >(async (options) => {
const variants = {} as { const variants = {} as {
[K in keyof VariantValues]: { name: string; spec: InputSpec } [K in keyof VariantValues]: { name: string; spec: InputSpec }
@@ -121,12 +109,12 @@ export class Variants<Type, WD> {
required: false, required: false,
}) })
return Config.of<WrapperData>()({ return Config.of<Store>()({
myValue: a.withWrapperData(), myValue: a.withStore(),
}) })
``` ```
*/ */
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() { withStore<NewStore extends Store extends never ? any : Store>() {
return this as any as Variants<Type, NewWrapperData> return this as any as Variants<Type, NewStore>
} }
} }

View File

@@ -18,7 +18,7 @@ export const smtpConfig = Value.filteredUnion(
system: { name: "System Credentials", spec: Config.of({}) }, system: { name: "System Credentials", spec: Config.of({}) },
custom: { custom: {
name: "Custom Credentials", name: "Custom Credentials",
spec: Config.of<ConfigSpecOf<SmtpValue>>({ spec: Config.of<ConfigSpecOf<SmtpValue>, never>({
server: Value.text({ server: Value.text({
name: "SMTP Server", name: "SMTP Server",
required: { required: {

View File

@@ -2,9 +2,8 @@ import { Effects, ExpectedExports } from "../types"
import { SDKManifest } from "../manifest/ManifestTypes" import { SDKManifest } from "../manifest/ManifestTypes"
import * as D from "./configDependencies" import * as D from "./configDependencies"
import { Config, ExtractConfigType } from "./builder/config" import { Config, ExtractConfigType } from "./builder/config"
import { Utils, utils } from "../util" import { Utils, utils } from "../util/utils"
import nullIfEmpty from "../util/nullIfEmpty" import nullIfEmpty from "../util/nullIfEmpty"
import { WrapperDataContract } from "../wrapperData/wrapperDataContract"
declare const dependencyProof: unique symbol declare const dependencyProof: unique symbol
export type DependenciesReceipt = void & { export type DependenciesReceipt = void & {
@@ -12,7 +11,7 @@ export type DependenciesReceipt = void & {
} }
export type Save< export type Save<
WD, Store,
A extends A extends
| Record<string, any> | Record<string, any>
| Config<Record<string, any>, any> | Config<Record<string, any>, any>
@@ -21,21 +20,21 @@ export type Save<
> = (options: { > = (options: {
effects: Effects effects: Effects
input: ExtractConfigType<A> & Record<string, any> input: ExtractConfigType<A> & Record<string, any>
utils: Utils<WD> utils: Utils<Store>
dependencies: D.ConfigDependencies<Manifest> dependencies: D.ConfigDependencies<Manifest>
}) => Promise<{ }) => Promise<{
dependenciesReceipt: DependenciesReceipt dependenciesReceipt: DependenciesReceipt
restart: boolean restart: boolean
}> }>
export type Read< export type Read<
WD, Store,
A extends A extends
| Record<string, any> | Record<string, any>
| Config<Record<string, any>, any> | Config<Record<string, any>, any>
| Config<Record<string, any>, never>, | Config<Record<string, any>, never>,
> = (options: { > = (options: {
effects: Effects effects: Effects
utils: Utils<WD> utils: Utils<Store>
}) => Promise<void | (ExtractConfigType<A> & Record<string, any>)> }) => Promise<void | (ExtractConfigType<A> & Record<string, any>)>
/** /**
* We want to setup a config export with a get and set, this * We want to setup a config export with a get and set, this
@@ -45,7 +44,7 @@ export type Read<
* @returns * @returns
*/ */
export function setupConfig< export function setupConfig<
WD, Store,
ConfigType extends ConfigType extends
| Record<string, any> | Record<string, any>
| Config<any, any> | Config<any, any>
@@ -53,11 +52,9 @@ export function setupConfig<
Manifest extends SDKManifest, Manifest extends SDKManifest,
Type extends Record<string, any> = ExtractConfigType<ConfigType>, Type extends Record<string, any> = ExtractConfigType<ConfigType>,
>( >(
wrapperDataContract: WrapperDataContract<WD>, spec: Config<Type, Store> | Config<Type, never>,
_manifest: Manifest, write: Save<Store, Type, Manifest>,
spec: Config<Type, WD> | Config<Type, never>, read: Read<Store, Type>,
write: Save<WD, Type, Manifest>,
read: Read<WD, Type>,
) { ) {
const validator = spec.validator const validator = spec.validator
return { return {
@@ -69,7 +66,7 @@ export function setupConfig<
const { restart } = await write({ const { restart } = await write({
input: JSON.parse(JSON.stringify(input)), input: JSON.parse(JSON.stringify(input)),
effects, effects,
utils: utils(wrapperDataContract, effects), utils: utils(effects),
dependencies: D.configDependenciesSet<Manifest>(), dependencies: D.configDependenciesSet<Manifest>(),
}) })
if (restart) { if (restart) {
@@ -77,7 +74,7 @@ export function setupConfig<
} }
}) as ExpectedExports.setConfig, }) as ExpectedExports.setConfig,
getConfig: (async ({ effects }) => { getConfig: (async ({ effects }) => {
const myUtils = utils(wrapperDataContract, effects) const myUtils = utils<Store>(effects)
const configValue = nullIfEmpty( const configValue = nullIfEmpty(
(await read({ effects, utils: myUtils })) || null, (await read({ effects, utils: myUtils })) || null,
) )

View File

@@ -2,9 +2,9 @@ import { InterfaceReceipt } from "../mainFn/interfaceReceipt"
import { Daemon, Effects } from "../types" import { Daemon, Effects } from "../types"
import { CheckResult } from "./checkFns/CheckResult" import { CheckResult } from "./checkFns/CheckResult"
import { HealthReceipt } from "./HealthReceipt" import { HealthReceipt } from "./HealthReceipt"
import { Trigger } from "./trigger" import { Trigger } from "../trigger"
import { TriggerInput } from "./trigger/TriggerInput" import { TriggerInput } from "../trigger/TriggerInput"
import { defaultTrigger } from "./trigger/defaultTrigger" import { defaultTrigger } from "../trigger/defaultTrigger"
import { once } from "../util/once" import { once } from "../util/once"
export function healthCheck(o: { export function healthCheck(o: {

View File

@@ -19,5 +19,3 @@ import "./inits"
export * as matches from "ts-matches" export * as matches from "ts-matches"
export * as YAML from "yaml" export * as YAML from "yaml"
export * as TOML from "@iarna/toml" export * as TOML from "@iarna/toml"
export class<Manifest extends SDKManifest>

View File

@@ -1,33 +1,28 @@
import { ManifestVersion } from "../../manifest/ManifestTypes" import { ManifestVersion } from "../../manifest/ManifestTypes"
import { Effects } from "../../types" import { Effects } from "../../types"
import { Utils } from "../../util" import { Utils } from "../../util/utils"
import { WrapperDataContract } from "../../wrapperData/wrapperDataContract"
export class Migration<WD, Version extends ManifestVersion> { export class Migration<Store, Version extends ManifestVersion> {
constructor( constructor(
readonly wrapperDataContract: WrapperDataContract<WD>,
readonly options: { readonly options: {
version: Version version: Version
up: (opts: { effects: Effects; utils: Utils<WD> }) => Promise<void> up: (opts: { effects: Effects; utils: Utils<Store> }) => Promise<void>
down: (opts: { effects: Effects; utils: Utils<WD> }) => Promise<void> down: (opts: { effects: Effects; utils: Utils<Store> }) => Promise<void>
}, },
) {} ) {}
static of<WD, Version extends ManifestVersion>( static of<Store, Version extends ManifestVersion>(options: {
wrapperDataContract: WrapperDataContract<WD>, version: Version
options: { up: (opts: { effects: Effects; utils: Utils<Store> }) => Promise<void>
version: Version down: (opts: { effects: Effects; utils: Utils<Store> }) => Promise<void>
up: (opts: { effects: Effects; utils: Utils<WD> }) => Promise<void> }) {
down: (opts: { effects: Effects; utils: Utils<WD> }) => Promise<void> return new Migration(options)
},
) {
return new Migration(wrapperDataContract, options)
} }
async up(opts: { effects: Effects; utils: Utils<WD> }) { async up(opts: { effects: Effects; utils: Utils<Store> }) {
this.up(opts) this.up(opts)
} }
async down(opts: { effects: Effects; utils: Utils<WD> }) { async down(opts: { effects: Effects; utils: Utils<Store> }) {
this.down(opts) this.down(opts)
} }
} }

View File

@@ -4,39 +4,32 @@ import { SDKManifest } from "../../manifest/ManifestTypes"
import { ExpectedExports } from "../../types" import { ExpectedExports } from "../../types"
import { createUtils } from "../../util" import { createUtils } from "../../util"
import { once } from "../../util/once" import { once } from "../../util/once"
import { WrapperDataContract } from "../../wrapperData/wrapperDataContract"
import { Migration } from "./Migration" import { Migration } from "./Migration"
export class Migrations<WD> { export class Migrations<Store> {
private constructor( private constructor(
readonly wrapperDataContract: WrapperDataContract<WD>,
readonly manifest: SDKManifest, readonly manifest: SDKManifest,
readonly migrations: Array<Migration<WD, any>>, readonly migrations: Array<Migration<Store, any>>,
) {} ) {}
private sortedMigrations = once(() => { private sortedMigrations = once(() => {
const migrationsAsVersions = ( const migrationsAsVersions = (
this.migrations as Array<Migration<WD, any>> this.migrations as Array<Migration<Store, any>>
).map((x) => [EmVer.parse(x.options.version), x] as const) ).map((x) => [EmVer.parse(x.options.version), x] as const)
migrationsAsVersions.sort((a, b) => a[0].compareForSort(b[0])) migrationsAsVersions.sort((a, b) => a[0].compareForSort(b[0]))
return migrationsAsVersions return migrationsAsVersions
}) })
private currentVersion = once(() => EmVer.parse(this.manifest.version)) private currentVersion = once(() => EmVer.parse(this.manifest.version))
static of<WD, Migrations extends Array<Migration<WD, any>>>( static of<Store, Migrations extends Array<Migration<Store, any>>>(
wrapperDataContract: WrapperDataContract<WD>,
manifest: SDKManifest, manifest: SDKManifest,
...migrations: EnsureUniqueId<Migrations> ...migrations: EnsureUniqueId<Migrations>
) { ) {
return new Migrations( return new Migrations(manifest, migrations as Array<Migration<Store, any>>)
wrapperDataContract,
manifest,
migrations as Array<Migration<WD, any>>,
)
} }
async init({ async init({
effects, effects,
previousVersion, previousVersion,
}: Parameters<ExpectedExports.init>[0]) { }: Parameters<ExpectedExports.init>[0]) {
const utils = createUtils(this.wrapperDataContract, effects) const utils = createUtils<Store>(effects)
if (!!previousVersion) { if (!!previousVersion) {
const previousVersionEmVer = EmVer.parse(previousVersion) const previousVersionEmVer = EmVer.parse(previousVersion)
for (const [_, migration] of this.sortedMigrations() for (const [_, migration] of this.sortedMigrations()
@@ -50,7 +43,7 @@ export class Migrations<WD> {
effects, effects,
nextVersion, nextVersion,
}: Parameters<ExpectedExports.uninit>[0]) { }: Parameters<ExpectedExports.uninit>[0]) {
const utils = createUtils(this.wrapperDataContract, effects) const utils = createUtils<Store>(effects)
if (!!nextVersion) { if (!!nextVersion) {
const nextVersionEmVer = EmVer.parse(nextVersion) const nextVersionEmVer = EmVer.parse(nextVersion)
const reversed = [...this.sortedMigrations()].reverse() const reversed = [...this.sortedMigrations()].reverse()
@@ -64,14 +57,10 @@ export class Migrations<WD> {
} }
export function setupMigrations< export function setupMigrations<
WD, Store,
Migrations extends Array<Migration<WD, any>>, Migrations extends Array<Migration<Store, any>>,
>( >(manifest: SDKManifest, ...migrations: EnsureUniqueId<Migrations>) {
wrapperDataContract: WrapperDataContract<WD>, return Migrations.of(manifest, ...migrations)
manifest: SDKManifest,
...migrations: EnsureUniqueId<Migrations>
) {
return Migrations.of(wrapperDataContract, manifest, ...migrations)
} }
// prettier-ignore // prettier-ignore

View File

@@ -3,10 +3,10 @@ import { Migrations } from "./migrations/setupMigrations"
import { Install } from "./setupInstall" import { Install } from "./setupInstall"
import { Uninstall } from "./setupUninstall" import { Uninstall } from "./setupUninstall"
export function setupInit<WrapperData>( export function setupInit<Store>(
migrations: Migrations<WrapperData>, migrations: Migrations<Store>,
install: Install<WrapperData>, install: Install<Store>,
uninstall: Uninstall<WrapperData>, uninstall: Uninstall<Store>,
): { ): {
init: ExpectedExports.init init: ExpectedExports.init
uninit: ExpectedExports.uninit uninit: ExpectedExports.uninit

View File

@@ -1,21 +1,14 @@
import { Effects, ExpectedExports } from "../types" import { Effects, ExpectedExports } from "../types"
import { Utils, utils } from "../util" import { Utils, utils } from "../util/utils"
import { WrapperDataContract } from "../wrapperData/wrapperDataContract"
export type InstallFn<WD> = (opts: { export type InstallFn<Store> = (opts: {
effects: Effects effects: Effects
utils: Utils<WD> utils: Utils<Store>
}) => Promise<void> }) => Promise<void>
export class Install<WD> { export class Install<Store> {
private constructor( private constructor(readonly fn: InstallFn<Store>) {}
readonly wrapperDataContract: WrapperDataContract<WD>, static of<Store>(fn: InstallFn<Store>) {
readonly fn: InstallFn<WD>, return new Install(fn)
) {}
static of<WD>(
wrapperDataContract: WrapperDataContract<WD>,
fn: InstallFn<WD>,
) {
return new Install(wrapperDataContract, fn)
} }
async init({ async init({
@@ -25,14 +18,11 @@ export class Install<WD> {
if (!previousVersion) if (!previousVersion)
await this.fn({ await this.fn({
effects, effects,
utils: utils(this.wrapperDataContract, effects), utils: utils(effects),
}) })
} }
} }
export function setupInstall<WD>( export function setupInstall<Store>(fn: InstallFn<Store>) {
wrapperDataContract: WrapperDataContract<WD>, return Install.of(fn)
fn: InstallFn<WD>,
) {
return Install.of(wrapperDataContract, fn)
} }

View File

@@ -1,21 +1,14 @@
import { Effects, ExpectedExports } from "../types" import { Effects, ExpectedExports } from "../types"
import { Utils, utils } from "../util" import { Utils, utils } from "../util/utils"
import { WrapperDataContract } from "../wrapperData/wrapperDataContract"
export type UninstallFn<WrapperData> = (opts: { export type UninstallFn<Store> = (opts: {
effects: Effects effects: Effects
utils: Utils<WrapperData> utils: Utils<Store>
}) => Promise<void> }) => Promise<void>
export class Uninstall<WD> { export class Uninstall<Store> {
private constructor( private constructor(readonly fn: UninstallFn<Store>) {}
readonly wrapperDataContract: WrapperDataContract<WD>, static of<Store>(fn: UninstallFn<Store>) {
readonly fn: UninstallFn<WD>, return new Uninstall(fn)
) {}
static of<WD>(
wrapperDataContract: WrapperDataContract<WD>,
fn: UninstallFn<WD>,
) {
return new Uninstall(wrapperDataContract, fn)
} }
async uninit({ async uninit({
@@ -25,14 +18,11 @@ export class Uninstall<WD> {
if (!nextVersion) if (!nextVersion)
await this.fn({ await this.fn({
effects, effects,
utils: utils(this.wrapperDataContract, effects), utils: utils(effects),
}) })
} }
} }
export function setupUninstall<WD>( export function setupUninstall<Store>(fn: UninstallFn<Store>) {
wrapperDataContract: WrapperDataContract<WD>, return Uninstall.of(fn)
fn: UninstallFn<WD>,
) {
return Uninstall.of(wrapperDataContract, fn)
} }

View File

@@ -1,8 +1,8 @@
import { HealthReceipt } from "../health/HealthReceipt" import { HealthReceipt } from "../health/HealthReceipt"
import { CheckResult } from "../health/checkFns" import { CheckResult } from "../health/checkFns"
import { Trigger } from "../health/trigger" import { Trigger } from "../trigger"
import { TriggerInput } from "../health/trigger/TriggerInput" import { TriggerInput } from "../trigger/TriggerInput"
import { defaultTrigger } from "../health/trigger/defaultTrigger" import { defaultTrigger } from "../trigger/defaultTrigger"
import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types" import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types"
import { InterfaceReceipt } from "./interfaceReceipt" import { InterfaceReceipt } from "./interfaceReceipt"
type Daemon<Ids extends string, Command extends string, Id extends string> = { type Daemon<Ids extends string, Command extends string, Id extends string> = {

View File

@@ -1,5 +1,6 @@
import { Effects, ExpectedExports } from "../types" import { Effects, ExpectedExports } from "../types"
import { createMainUtils, Utils, utils } from "../util" import { createMainUtils } from "../util"
import { Utils, utils } from "../util/utils"
import { Daemons } from "./Daemons" import { Daemons } from "./Daemons"
import "./exportInterfaces" import "./exportInterfaces"
import "./LocalBinding" import "./LocalBinding"
@@ -11,7 +12,6 @@ import "./TorBinding"
import "./TorHostname" import "./TorHostname"
import "./Daemons" import "./Daemons"
import { WrapperDataContract } from "../wrapperData/wrapperDataContract"
/** /**
* Used to ensure that the main function is running with the valid proofs. * Used to ensure that the main function is running with the valid proofs.
@@ -23,18 +23,17 @@ import { WrapperDataContract } from "../wrapperData/wrapperDataContract"
* @param fn * @param fn
* @returns * @returns
*/ */
export const setupMain = <WD>( export const setupMain = <Store>(
wrapperDataContract: WrapperDataContract<WD>,
fn: (o: { fn: (o: {
effects: Effects effects: Effects
started(onTerm: () => void): null started(onTerm: () => void): null
utils: Utils<WD, {}> utils: Utils<Store, {}>
}) => Promise<Daemons<any>>, }) => Promise<Daemons<any>>,
): ExpectedExports.main => { ): ExpectedExports.main => {
return async (options) => { return async (options) => {
const result = await fn({ const result = await fn({
...options, ...options,
utils: createMainUtils(wrapperDataContract, options.effects), utils: createMainUtils<Store>(options.effects),
}) })
await result.build().then((x) => x.wait()) await result.build().then((x) => x.wait())
} }

View File

@@ -1,11 +1,11 @@
import { Parser } from "ts-matches" import { Parser } from "ts-matches"
import { Effects, EnsureWrapperDataPath, ExtractWrapperData } from "../types" import { Effects, EnsureStorePath } from "../types"
import { NoAny } from "../util" import { NoAny } from "../util"
export class GetWrapperData<WrapperData, Path extends string> { export class GetStore<Store, Path extends string> {
constructor( constructor(
readonly effects: Effects, readonly effects: Effects,
readonly path: Path & EnsureWrapperDataPath<WrapperData, Path>, readonly path: Path & EnsureStorePath<Store, Path>,
readonly options: { readonly options: {
/** Defaults to what ever the package currently in */ /** Defaults to what ever the package currently in */
packageId?: string | undefined packageId?: string | undefined
@@ -13,20 +13,20 @@ export class GetWrapperData<WrapperData, Path extends string> {
) {} ) {}
/** /**
* Returns the value of WrapperData at the provided path. Restart the service if the value changes * Returns the value of Store at the provided path. Restart the service if the value changes
*/ */
const() { const() {
return this.effects.getWrapperData<WrapperData, Path>({ return this.effects.store.get<Store, Path>({
...this.options, ...this.options,
path: this.path as any, path: this.path as any,
callback: this.effects.restart, callback: this.effects.restart,
}) })
} }
/** /**
* Returns the value of WrapperData at the provided path. Does nothing if the value changes * Returns the value of Store at the provided path. Does nothing if the value changes
*/ */
once() { once() {
return this.effects.getWrapperData<WrapperData, Path>({ return this.effects.store.get<Store, Path>({
...this.options, ...this.options,
path: this.path as any, path: this.path as any,
callback: () => {}, callback: () => {},
@@ -34,7 +34,7 @@ export class GetWrapperData<WrapperData, Path extends string> {
} }
/** /**
* Watches the value of WrapperData at the provided path. Takes a custom callback function to run whenever the value changes * Watches the value of Store at the provided path. Takes a custom callback function to run whenever the value changes
*/ */
async *watch() { async *watch() {
while (true) { while (true) {
@@ -42,7 +42,7 @@ export class GetWrapperData<WrapperData, Path extends string> {
const waitForNext = new Promise<void>((resolve) => { const waitForNext = new Promise<void>((resolve) => {
callback = resolve callback = resolve
}) })
yield await this.effects.getWrapperData<WrapperData, Path>({ yield await this.effects.store.get<Store, Path>({
...this.options, ...this.options,
path: this.path as any, path: this.path as any,
callback: () => callback(), callback: () => callback(),
@@ -51,13 +51,13 @@ export class GetWrapperData<WrapperData, Path extends string> {
} }
} }
} }
export function getWrapperData<WrapperData, Path extends string>( export function getStore<Store, Path extends string>(
effects: Effects, effects: Effects,
path: Path & EnsureWrapperDataPath<WrapperData, Path>, path: Path & EnsureStorePath<Store, Path>,
options: { options: {
/** Defaults to what ever the package currently in */ /** Defaults to what ever the package currently in */
packageId?: string | undefined packageId?: string | undefined
} = {}, } = {},
) { ) {
return new GetWrapperData<WrapperData, Path>(effects, path as any, options) return new GetStore<Store, Path>(effects, path as any, options)
} }

View File

@@ -4,13 +4,7 @@ import { List } from "../config/builder/list"
import { Value } from "../config/builder/value" import { Value } from "../config/builder/value"
import { Variants } from "../config/builder/variants" import { Variants } from "../config/builder/variants"
import { ValueSpec } from "../config/configTypes" import { ValueSpec } from "../config/configTypes"
import { Parser } from "ts-matches"
import {
createWrapperDataContract,
neverWrapperDataContract,
} from "../wrapperData/wrapperDataContract"
type test = unknown | { test: 5 }
describe("builder tests", () => { describe("builder tests", () => {
test("text", async () => { test("text", async () => {
const bitcoinPropertiesBuilt: { const bitcoinPropertiesBuilt: {
@@ -303,7 +297,7 @@ describe("values", () => {
utils: "utils", utils: "utils",
} as any } as any
test("toggle", async () => { test("toggle", async () => {
const value = Value.dynamicToggle(neverWrapperDataContract, async () => ({ const value = Value.dynamicToggle(async () => ({
name: "Testing", name: "Testing",
description: null, description: null,
warning: null, warning: null,
@@ -321,7 +315,7 @@ describe("values", () => {
}) })
}) })
test("text", async () => { test("text", async () => {
const value = Value.dynamicText(neverWrapperDataContract, async () => ({ const value = Value.dynamicText(async () => ({
name: "Testing", name: "Testing",
required: { default: null }, required: { default: null },
})) }))
@@ -337,7 +331,7 @@ describe("values", () => {
}) })
}) })
test("text with default", async () => { test("text with default", async () => {
const value = Value.dynamicText(neverWrapperDataContract, async () => ({ const value = Value.dynamicText(async () => ({
name: "Testing", name: "Testing",
required: { default: "this is a default value" }, required: { default: "this is a default value" },
})) }))
@@ -352,7 +346,7 @@ describe("values", () => {
}) })
}) })
test("optional text", async () => { test("optional text", async () => {
const value = Value.dynamicText(neverWrapperDataContract, async () => ({ const value = Value.dynamicText(async () => ({
name: "Testing", name: "Testing",
required: false, required: false,
})) }))
@@ -368,7 +362,7 @@ describe("values", () => {
}) })
}) })
test("color", async () => { test("color", async () => {
const value = Value.dynamicColor(neverWrapperDataContract, async () => ({ const value = Value.dynamicColor(async () => ({
name: "Testing", name: "Testing",
required: false, required: false,
description: null, description: null,
@@ -387,20 +381,17 @@ describe("values", () => {
}) })
}) })
test("datetime", async () => { test("datetime", async () => {
const value = Value.dynamicDatetime( const value = Value.dynamicDatetime<{ test: "a" }>(async ({ utils }) => {
createWrapperDataContract<{ test: "a" }>(), ;async () => {
async ({ utils }) => { ;(await utils.store.getOwn("/test").once()) satisfies "a"
;async () => { }
;(await utils.getOwnWrapperData("/test").once()) satisfies "a"
}
return { return {
name: "Testing", name: "Testing",
required: { default: null }, required: { default: null },
inputmode: "date", inputmode: "date",
} }
}, })
)
const validator = value.validator const validator = value.validator
validator.unsafeCast("2021-01-01") validator.unsafeCast("2021-01-01")
validator.unsafeCast(null) validator.unsafeCast(null)
@@ -415,18 +406,15 @@ describe("values", () => {
}) })
}) })
test("textarea", async () => { test("textarea", async () => {
const value = Value.dynamicTextarea( const value = Value.dynamicTextarea(async () => ({
neverWrapperDataContract, name: "Testing",
async () => ({ required: false,
name: "Testing", description: null,
required: false, warning: null,
description: null, minLength: null,
warning: null, maxLength: null,
minLength: null, placeholder: null,
maxLength: null, }))
placeholder: null,
}),
)
const validator = value.validator const validator = value.validator
validator.unsafeCast("test text") validator.unsafeCast("test text")
expect(() => validator.unsafeCast(null)).toThrowError() expect(() => validator.unsafeCast(null)).toThrowError()
@@ -437,7 +425,7 @@ describe("values", () => {
}) })
}) })
test("number", async () => { test("number", async () => {
const value = Value.dynamicNumber(neverWrapperDataContract, () => ({ const value = Value.dynamicNumber(() => ({
name: "Testing", name: "Testing",
required: { default: null }, required: { default: null },
integer: false, integer: false,
@@ -511,7 +499,6 @@ describe("values", () => {
describe("filtering", () => { describe("filtering", () => {
test("union", async () => { test("union", async () => {
const value = Value.filteredUnion( const value = Value.filteredUnion(
neverWrapperDataContract,
() => ["a", "c"], () => ["a", "c"],
{ {
name: "Testing", name: "Testing",
@@ -620,7 +607,7 @@ describe("Builder List", () => {
describe("dynamic", () => { describe("dynamic", () => {
test("text", async () => { test("text", async () => {
const value = Value.list( const value = Value.list(
List.dynamicText(neverWrapperDataContract, () => ({ List.dynamicText(() => ({
name: "test", name: "test",
spec: { patterns: [] }, spec: { patterns: [] },
})), })),
@@ -638,7 +625,7 @@ describe("Builder List", () => {
}) })
test("number", async () => { test("number", async () => {
const value = Value.list( const value = Value.list(
List.dynamicNumber(neverWrapperDataContract, () => ({ List.dynamicNumber(() => ({
name: "test", name: "test",
spec: { integer: true }, spec: { integer: true },
})), })),

View File

@@ -423,7 +423,6 @@ oldSpecToBuilder(
}, },
{ {
// convert this to `start-sdk/lib` for conversions // convert this to `start-sdk/lib` for conversions
startSdk: "../..", startSdk: "./output.sdk",
wrapperData: "./output.wrapperData",
}, },
) )

View File

@@ -1 +1,7 @@
export type WrapperData = {} import { StartSDK } from "../StartSDK"
export type WrapperData = any
export const sdk = StartSDK.of()
.withManifest<WrapperData>()
.withStore<{ storeRoot: { storeLeaf: "value" } }>()
.build()

View File

@@ -1,13 +1,12 @@
import { Effects } from "../types" import { Effects } from "../types"
import { createMainUtils, utils } from "../util" import { createMainUtils } from "../util"
import { createWrapperDataContract } from "../wrapperData/wrapperDataContract" import { utils } from "../util/utils"
type WrapperType = { type WrapperType = {
config: { config: {
someValue: "a" | "b" someValue: "a" | "b"
} }
} }
const wrapperDataContract = createWrapperDataContract<WrapperType>()
const todo = <A>(): A => { const todo = <A>(): A => {
throw new Error("not implemented") throw new Error("not implemented")
} }
@@ -15,97 +14,94 @@ const noop = () => {}
describe("wrapperData", () => { describe("wrapperData", () => {
test("types", async () => { test("types", async () => {
;async () => { ;async () => {
utils(wrapperDataContract, todo<Effects>()).setOwnWrapperData("/config", { utils<WrapperType>(todo<Effects>()).store.setOwn("/config", {
someValue: "a", someValue: "a",
}) })
utils(wrapperDataContract, todo<Effects>()).setOwnWrapperData( utils<WrapperType>(todo<Effects>()).store.setOwn("/config/someValue", "b")
"/config/someValue", utils<WrapperType>(todo<Effects>()).store.setOwn("", {
"b",
)
utils(wrapperDataContract, todo<Effects>()).setOwnWrapperData("", {
config: { someValue: "b" }, config: { someValue: "b" },
}) })
utils(wrapperDataContract, todo<Effects>()).setOwnWrapperData( utils<WrapperType>(todo<Effects>()).store.setOwn(
"/config/someValue", "/config/someValue",
// @ts-expect-error Type is wrong for the setting value // @ts-expect-error Type is wrong for the setting value
5, 5,
) )
utils(wrapperDataContract, todo<Effects>()).setOwnWrapperData( utils(todo<Effects>()).store.setOwn(
// @ts-expect-error Path is wrong // @ts-expect-error Path is wrong
"/config/someVae3lue", "/config/someVae3lue",
"someValue", "someValue",
) )
todo<Effects>().setWrapperData<WrapperType, "/config/someValue">({ todo<Effects>().store.set<WrapperType, "/config/someValue">({
path: "/config/someValue", path: "/config/someValue",
value: "b", value: "b",
}) })
todo<Effects>().setWrapperData<WrapperType, "/config/some2Value">({ todo<Effects>().store.set<WrapperType, "/config/some2Value">({
//@ts-expect-error Path is wrong //@ts-expect-error Path is wrong
path: "/config/someValue", path: "/config/someValue",
//@ts-expect-error Path is wrong //@ts-expect-error Path is wrong
value: "someValueIn", value: "someValueIn",
}) })
todo<Effects>().setWrapperData<WrapperType, "/config/someValue">({ todo<Effects>().store.set<WrapperType, "/config/someValue">({
//@ts-expect-error Path is wrong //@ts-expect-error Path is wrong
path: "/config/some2Value", path: "/config/some2Value",
value: "a", value: "a",
}) })
;(await createMainUtils(wrapperDataContract, todo<Effects>()) ;(await createMainUtils<WrapperType>(todo<Effects>())
.getOwnWrapperData("/config/someValue") .store.getOwn("/config/someValue")
.const()) satisfies string .const()) satisfies string
;(await createMainUtils(wrapperDataContract, todo<Effects>()) ;(await createMainUtils<WrapperType>(todo<Effects>())
.getOwnWrapperData("/config") .store.getOwn("/config")
.const()) satisfies WrapperType["config"] .const()) satisfies WrapperType["config"]
await createMainUtils(wrapperDataContract, todo<Effects>()) await createMainUtils(todo<Effects>())
// @ts-expect-error Path is wrong // @ts-expect-error Path is wrong
.getOwnWrapperData("/config/somdsfeValue") .store.getOwn("/config/somdsfeValue")
.const() .const()
/// ----------------- ERRORS ----------------- /// ----------------- ERRORS -----------------
utils(wrapperDataContract, todo<Effects>()).setOwnWrapperData("", { utils<WrapperType>(todo<Effects>()).store.setOwn("", {
// @ts-expect-error Type is wrong for the setting value // @ts-expect-error Type is wrong for the setting value
config: { someValue: "notInAOrB" }, config: { someValue: "notInAOrB" },
}) })
utils(wrapperDataContract, todo<Effects>()).setOwnWrapperData( utils<WrapperType>(todo<Effects>()).store.setOwn(
"/config/someValue", "/config/someValue",
// @ts-expect-error Type is wrong for the setting value // @ts-expect-error Type is wrong for the setting value
"notInAOrB", "notInAOrB",
) )
;(await utils(wrapperDataContract, todo<Effects>()) ;(await utils<WrapperType>(todo<Effects>())
.getOwnWrapperData("/config/someValue") .store.getOwn("/config/someValue")
// @ts-expect-error Const should normally not be callable // @ts-expect-error Const should normally not be callable
.const()) satisfies string .const()) satisfies string
;(await utils(wrapperDataContract, todo<Effects>()) ;(await utils<WrapperType>(todo<Effects>())
.getOwnWrapperData("/config") .store.getOwn("/config")
// @ts-expect-error Const should normally not be callable // @ts-expect-error Const should normally not be callable
.const()) satisfies WrapperType["config"] .const()) satisfies WrapperType["config"]
await utils(wrapperDataContract, todo<Effects>()) await utils<WrapperType>(todo<Effects>())
// @ts-expect-error Path is wrong // @ts-expect-error Path is wrong
.getOwnWrapperData("/config/somdsfeValue") .store.getOwn("/config/somdsfeValue")
// @ts-expect-error Const should normally not be callable // @ts-expect-error Const should normally not be callable
.const() .const()
/// ///
;(await utils(wrapperDataContract, todo<Effects>()) ;(await utils<WrapperType>(todo<Effects>())
.getOwnWrapperData("/config/someValue") .store.getOwn("/config/someValue")
// @ts-expect-error satisfies type is wrong // @ts-expect-error satisfies type is wrong
.const()) satisfies number .const()) satisfies number
;(await createMainUtils(wrapperDataContract, todo<Effects>()) ;(await createMainUtils(todo<Effects>())
// @ts-expect-error Path is wrong // @ts-expect-error Path is wrong
.getOwnWrapperData("/config/") .store.getOwn("/config/")
.const()) satisfies WrapperType["config"] .const()) satisfies WrapperType["config"]
;(await todo<Effects>().getWrapperData<WrapperType, "/config/someValue">({ ;(await todo<Effects>().store.get<WrapperType, "/config/someValue">({
path: "/config/someValue", path: "/config/someValue",
callback: noop, callback: noop,
})) satisfies string })) satisfies string
await todo<Effects>().getWrapperData<WrapperType, "/config/someValue">({ await todo<Effects>().store.get<WrapperType, "/config/someValue">({
// @ts-expect-error Path is wrong as in it doesn't match above // @ts-expect-error Path is wrong as in it doesn't match above
path: "/config/someV2alue", path: "/config/someV2alue",
callback: noop, callback: noop,
}) })
await todo<Effects>().getWrapperData<WrapperType, "/config/someV2alue">({ await todo<Effects>().store.get<WrapperType, "/config/someV2alue">({
// @ts-expect-error Path is wrong as in it doesn't exists in wrapper type // @ts-expect-error Path is wrong as in it doesn't exists in wrapper type
path: "/config/someV2alue", path: "/config/someV2alue",
callback: noop, callback: noop,

View File

@@ -1,4 +1,4 @@
import { HealthStatus } from "../../types" import { HealthStatus } from "../types"
export type TriggerInput = { export type TriggerInput = {
lastResult?: HealthStatus lastResult?: HealthStatus

View File

@@ -1,4 +1,3 @@
import { TriggerInput } from "./TriggerInput"
import { Trigger } from "./index" import { Trigger } from "./index"
export function changeOnFirstSuccess(o: { export function changeOnFirstSuccess(o: {

View File

@@ -251,26 +251,27 @@ export type Effects = {
progress: () => Promise<number> progress: () => Promise<number>
} }
/** Get a value in a json like data, can be observed and subscribed */ store: {
getWrapperData<WrapperData = never, Path extends string = never>(options: { /** Get a value in a json like data, can be observed and subscribed */
/** If there is no packageId it is assumed the current package */ get<Store = never, Path extends string = never>(options: {
packageId?: string /** If there is no packageId it is assumed the current package */
/** The path defaults to root level, using the [JsonPath](https://jsonpath.com/) */ packageId?: string
path: Path & EnsureWrapperDataPath<WrapperData, Path> /** The path defaults to root level, using the [JsonPath](https://jsonpath.com/) */
callback: (config: unknown, previousConfig: unknown) => void path: Path & EnsureStorePath<Store, Path>
}): Promise<ExtractWrapperData<WrapperData, Path>> callback: (config: unknown, previousConfig: unknown) => void
}): Promise<ExtractStore<Store, Path>>
/** Used to store values that can be accessed and subscribed to */
set<Store = never, Path extends string = never>(options: {
/** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */
path: Path & EnsureStorePath<Store, Path>
value: ExtractStore<Store, Path>
}): Promise<void>
}
getSystemSmtp(input: { getSystemSmtp(input: {
callback: (config: unknown, previousConfig: unknown) => void callback: (config: unknown, previousConfig: unknown) => void
}): Promise<SmtpValue> }): Promise<SmtpValue>
/** Used to store values that can be accessed and subscribed to */
setWrapperData<WrapperData = never, Path extends string = never>(options: {
/** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */
path: Path & EnsureWrapperDataPath<WrapperData, Path>
value: ExtractWrapperData<WrapperData, Path>
}): Promise<void>
getLocalHostname(): Promise<string> getLocalHostname(): Promise<string>
getIPHostname(): Promise<string[]> getIPHostname(): Promise<string[]>
/** Get the address for another service for tor interfaces */ /** Get the address for another service for tor interfaces */
@@ -400,20 +401,20 @@ export type Effects = {
} }
// prettier-ignore // prettier-ignore
export type ExtractWrapperData<WrapperData, Path extends string> = export type ExtractStore<Store, Path extends string> =
Path extends `/${infer A }/${infer Rest }` ? (A extends keyof WrapperData ? ExtractWrapperData<WrapperData[A], `/${Rest}`> : never) : Path extends `/${infer A }/${infer Rest }` ? (A extends keyof Store ? ExtractStore<Store[A], `/${Rest}`> : never) :
Path extends `/${infer A }` ? (A extends keyof WrapperData ? WrapperData[A] : never) : Path extends `/${infer A }` ? (A extends keyof Store ? Store[A] : never) :
Path extends '' ? WrapperData : Path extends '' ? Store :
never never
// prettier-ignore // prettier-ignore
type _EnsureWrapperDataPath<WrapperData, Path extends string, Origin extends string> = type _EnsureStorePath<Store, Path extends string, Origin extends string> =
Path extends`/${infer A }/${infer Rest}` ? (WrapperData extends {[K in A & string]: infer NextWrapperData} ? _EnsureWrapperDataPath<NextWrapperData, `/${Rest}`, Origin> : never) : Path extends`/${infer A }/${infer Rest}` ? (Store extends {[K in A & string]: infer NextStore} ? _EnsureStorePath<NextStore, `/${Rest}`, Origin> : never) :
Path extends `/${infer A }` ? (WrapperData extends {[K in A]: infer B} ? Origin : never) : Path extends `/${infer A }` ? (Store extends {[K in A]: infer B} ? Origin : never) :
Path extends '' ? Origin : Path extends '' ? Origin :
never never
// prettier-ignore // prettier-ignore
export type EnsureWrapperDataPath<WrapperData, Path extends string> = _EnsureWrapperDataPath<WrapperData, Path, Path> export type EnsureStorePath<Store, Path extends string> = _EnsureStorePath<Store, Path, Path>
/** rsync options: https://linux.die.net/man/1/rsync /** rsync options: https://linux.die.net/man/1/rsync
*/ */

View File

@@ -1,29 +1,12 @@
import { Parser, string } from "ts-matches"
import * as T from "../types" import * as T from "../types"
import FileHelper from "./fileHelper"
import nullIfEmpty from "./nullIfEmpty"
import { GetWrapperData, getWrapperData } from "../wrapperData/getWrapperData"
import {
CheckResult,
checkPortListening,
checkWebUrl,
} from "../health/checkFns"
import { ExtractWrapperData } from "../types"
import { GetSystemSmtp } from "./GetSystemSmtp"
import "./nullIfEmpty" import "./nullIfEmpty"
import "./fileHelper" import "./fileHelper"
import "../wrapperData/getWrapperData" import "../store/getStore"
import "./deepEqual" import "./deepEqual"
import "./deepMerge" import "./deepMerge"
import "./once" import "./once"
import { LocalBinding } from "../mainFn/LocalBinding" import { utils } from "./utils"
import { LocalPort } from "../mainFn/LocalPort"
import { NetworkBuilder } from "../mainFn/NetworkBuilder"
import { TorHostname } from "../mainFn/TorHostname"
import { DefaultString } from "../config/configTypes"
import { getDefaultString } from "./getDefaultString"
import { WrapperDataContract } from "../wrapperData/wrapperDataContract"
// prettier-ignore // prettier-ignore
export type FlattenIntersection<T> = export type FlattenIntersection<T> =
@@ -38,106 +21,9 @@ export const isKnownError = (e: unknown): e is T.KnownError =>
declare const affine: unique symbol declare const affine: unique symbol
export type Utils<WD, WrapperOverWrite = { const: never }> = {
createOrUpdateVault: (opts: {
key: string
value: string | null | undefined
generator: DefaultString
}) => Promise<null | string>
readFile: <A>(fileHelper: FileHelper<A>) => ReturnType<FileHelper<A>["read"]>
writeFile: <A>(
fileHelper: FileHelper<A>,
data: A,
) => ReturnType<FileHelper<A>["write"]>
getSystemSmtp: () => GetSystemSmtp & WrapperOverWrite
getWrapperData: <Path extends string>(
packageId: string,
path: T.EnsureWrapperDataPath<WD, Path>,
) => GetWrapperData<WD, Path> & WrapperOverWrite
getOwnWrapperData: <Path extends string>(
path: T.EnsureWrapperDataPath<WD, Path>,
) => GetWrapperData<WD, Path> & WrapperOverWrite
setOwnWrapperData: <Path extends string | never>(
path: T.EnsureWrapperDataPath<WD, Path>,
value: ExtractWrapperData<WD, Path>,
) => Promise<void>
checkPortListening(
port: number,
options: {
errorMessage: string
successMessage: string
timeoutMessage?: string
timeout?: number
},
): Promise<CheckResult>
checkWebUrl(
url: string,
options?: {
timeout?: number
successMessage?: string
errorMessage?: string
},
): Promise<CheckResult>
bindLan: (port: number) => Promise<LocalBinding>
networkBuilder: () => NetworkBuilder
torHostName: (id: string) => TorHostname
nullIfEmpty: typeof nullIfEmpty
}
export const utils = <WD = never, WrapperOverWrite = { const: never }>(
effects: T.Effects,
): Utils<WD, WrapperOverWrite> => ({
createOrUpdateVault: async ({
key,
value,
generator,
}: {
key: string
value: string | null | undefined
generator: DefaultString
}) => {
if (value) {
await effects.vault.set({ key, value })
return value
}
if (await effects.vault.get({ key })) {
return null
}
const newValue = getDefaultString(generator)
await effects.vault.set({ key, value: newValue })
return newValue
},
getSystemSmtp: () =>
new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite,
readFile: <A>(fileHelper: FileHelper<A>) => fileHelper.read(effects),
writeFile: <A>(fileHelper: FileHelper<A>, data: A) =>
fileHelper.write(data, effects),
nullIfEmpty,
getWrapperData: <WrapperData = never, Path extends string = never>(
packageId: string,
path: T.EnsureWrapperDataPath<WrapperData, Path>,
) =>
getWrapperData<WrapperData, Path>(effects, path as any, {
packageId,
}) as any,
getOwnWrapperData: <Path extends string>(
path: T.EnsureWrapperDataPath<WD, Path>,
) => getWrapperData<WD, Path>(effects, path as any) as any,
setOwnWrapperData: <Path extends string | never>(
path: T.EnsureWrapperDataPath<WD, Path>,
value: ExtractWrapperData<WD, Path>,
) => effects.setWrapperData<WD, Path>({ value, path: path as any }),
checkPortListening: checkPortListening.bind(null, effects),
checkWebUrl: checkWebUrl.bind(null, effects),
bindLan: async (port: number) => LocalPort.bindLan(effects, port),
networkBuilder: () => NetworkBuilder.of(effects),
torHostName: (id: string) => TorHostname.of(effects, id),
})
export const createUtils = utils export const createUtils = utils
export const createMainUtils = <WD>( export const createMainUtils = <Store>(effects: T.Effects) =>
wrapperDataContract: WrapperDataContract<WD>, createUtils<Store, {}>(effects)
effects: T.Effects,
) => createUtils<WD, {}>(wrapperDataContract, effects)
type NeverPossible = { [affine]: string } type NeverPossible = { [affine]: string }
export type NoAny<A> = NeverPossible extends A export type NoAny<A> = NeverPossible extends A

115
lib/util/utils.ts Normal file
View File

@@ -0,0 +1,115 @@
import * as T from "../types"
import FileHelper from "./fileHelper"
import nullIfEmpty from "./nullIfEmpty"
import {
CheckResult,
checkPortListening,
checkWebUrl,
} from "../health/checkFns"
import { ExtractStore } from "../types"
import { GetSystemSmtp } from "./GetSystemSmtp"
import { LocalBinding } from "../mainFn/LocalBinding"
import { LocalPort } from "../mainFn/LocalPort"
import { NetworkBuilder } from "../mainFn/NetworkBuilder"
import { TorHostname } from "../mainFn/TorHostname"
import { DefaultString } from "../config/configTypes"
import { getDefaultString } from "./getDefaultString"
import { GetStore, getStore } from "../store/getStore"
export type Utils<Store, WrapperOverWrite = { const: never }> = {
createOrUpdateVault: (opts: {
key: string
value: string | null | undefined
generator: DefaultString
}) => Promise<null | string>
readFile: <A>(fileHelper: FileHelper<A>) => ReturnType<FileHelper<A>["read"]>
writeFile: <A>(
fileHelper: FileHelper<A>,
data: A,
) => ReturnType<FileHelper<A>["write"]>
getSystemSmtp: () => GetSystemSmtp & WrapperOverWrite
store: {
get: <Path extends string>(
packageId: string,
path: T.EnsureStorePath<Store, Path>,
) => GetStore<Store, Path> & WrapperOverWrite
getOwn: <Path extends string>(
path: T.EnsureStorePath<Store, Path>,
) => GetStore<Store, Path> & WrapperOverWrite
setOwn: <Path extends string | never>(
path: T.EnsureStorePath<Store, Path>,
value: ExtractStore<Store, Path>,
) => Promise<void>
}
checkPortListening(
port: number,
options: {
errorMessage: string
successMessage: string
timeoutMessage?: string
timeout?: number
},
): Promise<CheckResult>
checkWebUrl(
url: string,
options?: {
timeout?: number
successMessage?: string
errorMessage?: string
},
): Promise<CheckResult>
bindLan: (port: number) => Promise<LocalBinding>
networkBuilder: () => NetworkBuilder
torHostName: (id: string) => TorHostname
nullIfEmpty: typeof nullIfEmpty
}
export const utils = <Store = never, WrapperOverWrite = { const: never }>(
effects: T.Effects,
): Utils<Store, WrapperOverWrite> => ({
createOrUpdateVault: async ({
key,
value,
generator,
}: {
key: string
value: string | null | undefined
generator: DefaultString
}) => {
if (value) {
await effects.vault.set({ key, value })
return value
}
if (await effects.vault.get({ key })) {
return null
}
const newValue = getDefaultString(generator)
await effects.vault.set({ key, value: newValue })
return newValue
},
getSystemSmtp: () =>
new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite,
readFile: <A>(fileHelper: FileHelper<A>) => fileHelper.read(effects),
writeFile: <A>(fileHelper: FileHelper<A>, data: A) =>
fileHelper.write(data, effects),
nullIfEmpty,
store: {
get: <Store = never, Path extends string = never>(
packageId: string,
path: T.EnsureStorePath<Store, Path>,
) =>
getStore<Store, Path>(effects, path as any, {
packageId,
}) as any,
getOwn: <Path extends string>(path: T.EnsureStorePath<Store, Path>) =>
getStore<Store, Path>(effects, path as any) as any,
setOwn: <Path extends string | never>(
path: T.EnsureStorePath<Store, Path>,
value: ExtractStore<Store, Path>,
) => effects.store.set<Store, Path>({ value, path: path as any }),
},
checkPortListening: checkPortListening.bind(null, effects),
checkWebUrl: checkWebUrl.bind(null, effects),
bindLan: async (port: number) => LocalPort.bindLan(effects, port),
networkBuilder: () => NetworkBuilder.of(effects),
torHostName: (id: string) => TorHostname.of(effects, id),
})

View File

@@ -28,19 +28,12 @@ function isString(x: unknown): x is string {
export default async function makeFileContentFromOld( export default async function makeFileContentFromOld(
inputData: Promise<any> | any, inputData: Promise<any> | any,
{ { startSdk = "start-sdk", nested = true } = {},
startSdk = "start-sdk",
nested = true,
wrapperData = "../../wrapperData",
} = {},
) { ) {
const outputLines: string[] = [] const outputLines: string[] = []
outputLines.push(` outputLines.push(`
import { Config } from "${startSdk}/lib/config/builder/config" import { sdk } from "${startSdk}"
import { List } from "${startSdk}/lib/config/builder/list" const {Config, List, Value, Variants} = sdk
import { Value } from "${startSdk}/lib/config/builder/value"
import { Variants } from "${startSdk}/lib/config/builder/variants"
import {WrapperData} from '${wrapperData}'
`) `)
const data = await inputData const data = await inputData