chore: Update the types of config

This commit is contained in:
BluJ
2023-05-03 07:47:21 -06:00
parent 31a0988eef
commit 31c2131ca9
11 changed files with 185 additions and 138 deletions

View File

@@ -4,7 +4,10 @@ import { Utils, utils } from "../util"
export class CreatedAction<
WrapperData,
ConfigType extends Record<string, any> | Config<any, any>,
ConfigType extends
| Record<string, any>
| Config<any, WrapperData>
| Config<any, never>,
Type extends Record<string, any> = ExtractConfigType<ConfigType>,
> {
private constructor(

View File

@@ -14,10 +14,14 @@ export type LazyBuild<WD, ExpectedOut> = (
) => Promise<ExpectedOut> | ExpectedOut
// prettier-ignore
export type ExtractConfigType<A extends Record<string, any> | Config<Record<string, any>, any>> =
A extends Config<infer B, any> ? B :
export type ExtractConfigType<A extends Record<string, any> | Config<Record<string, any>, any> | Config<Record<string, any>, never>> =
A extends Config<infer B, any> | Config<infer B, never> ? B :
A
export type TypeAsConfigOf<A extends Record<string, any>, WD> = {
[K in keyof A]: Value<A[K], WD>
}
export type MaybeLazyValues<A> = LazyBuild<any, A> | A
/**
* Configs are the specs that are used by the os configuration form for this service.
@@ -92,19 +96,28 @@ export class Config<Type extends Record<string, any>, WD> {
return answer
}
static of<WrapperData>() {
return <Type extends Record<string, any>>(spec: {
[K in keyof Type]: Value<Type[K], WrapperData>
}) => {
const validatorObj = {} as {
[K in keyof Type]: Parser<unknown, Type[K]>
}
for (const key in spec) {
validatorObj[key] = spec[key].validator
}
const validator = object(validatorObj)
return new Config<Type, WrapperData>(spec, validator)
static of<Spec extends Record<string, Value<any, any> | Value<any, never>>>(
spec: Spec,
) {
const validatorObj = {} as {
[K in keyof Spec]: Parser<unknown, any>
}
for (const key in spec) {
validatorObj[key] = spec[key].validator
}
const validator = object(validatorObj)
return new Config<
{
[K in keyof Spec]: Spec[K] extends
| Value<infer T, any>
| Value<infer T, never>
? T
: never
},
{
[K in keyof Spec]: Spec[K] extends Value<any, infer WD> ? WD : never
}[keyof Spec]
>(spec as any, validator as any)
}
/**
@@ -121,7 +134,7 @@ export class Config<Type extends Record<string, any>, WD> {
})
```
*/
withWrapperData<NewWrapperData extends WD>() {
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() {
return this as any as Config<Type, NewWrapperData>
}
}

View File

@@ -26,7 +26,7 @@ export class List<Type, WD> {
public build: LazyBuild<WD, ValueSpecList>,
public validator: Parser<unknown, Type>,
) {}
static text<WD>(
static text(
a: {
name: string
description?: string | null
@@ -47,7 +47,7 @@ export class List<Type, WD> {
inputmode?: ListValueSpecText["inputmode"]
},
) {
return new List<string[], WD>(() => {
return new List<string[], never>(() => {
const spec = {
type: "text" as const,
placeholder: null,
@@ -70,7 +70,7 @@ export class List<Type, WD> {
} satisfies ValueSpecListOf<"text">
}, arrayOf(string))
}
static dynamicText<WD>(
static dynamicText<WD = never>(
getA: LazyBuild<
WD,
{
@@ -119,7 +119,7 @@ export class List<Type, WD> {
} satisfies ValueSpecListOf<"text">
}, arrayOf(string))
}
static number<WD>(
static number(
a: {
name: string
description?: string | null
@@ -138,7 +138,7 @@ export class List<Type, WD> {
placeholder?: string | null
},
) {
return new List<number[], WD>(() => {
return new List<number[], never>(() => {
const spec = {
type: "number" as const,
placeholder: null,
@@ -161,7 +161,7 @@ export class List<Type, WD> {
} satisfies ValueSpecListOf<"number">
}, arrayOf(number))
}
static dynamicNumber<WD>(
static dynamicNumber<WD = never>(
getA: LazyBuild<
WD,
{
@@ -265,7 +265,7 @@ export class List<Type, WD> {
})
```
*/
withWrapperData<NewWrapperData extends WD>() {
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() {
return this as any as List<Type, NewWrapperData>
}
}

View File

@@ -98,7 +98,7 @@ export class Value<Type, WD> {
public build: LazyBuild<WD, ValueSpec>,
public validator: Parser<unknown, Type>,
) {}
static toggle<WD>(a: {
static toggle(a: {
name: string
description?: string | null
warning?: string | null
@@ -107,7 +107,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<boolean, WD>(
return new Value<boolean, never>(
async () => ({
description: null,
warning: null,
@@ -120,7 +120,7 @@ export class Value<Type, WD> {
boolean,
)
}
static dynamicToggle<WD>(
static dynamicToggle<WD = never>(
a: LazyBuild<
WD,
{
@@ -145,7 +145,7 @@ export class Value<Type, WD> {
boolean,
)
}
static text<Required extends RequiredDefault<DefaultString>, WD>(a: {
static text<Required extends RequiredDefault<DefaultString>>(a: {
name: string
description?: string | null
warning?: string | null
@@ -163,7 +163,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<AsRequired<string, Required>, WD>(
return new Value<AsRequired<string, Required>, never>(
async () => ({
type: "text" as const,
description: null,
@@ -182,7 +182,7 @@ export class Value<Type, WD> {
asRequiredParser(string, a),
)
}
static dynamicText<WD>(
static dynamicText<WD = never>(
getA: LazyBuild<
WD,
{
@@ -221,7 +221,7 @@ export class Value<Type, WD> {
}
}, string.optional())
}
static textarea<WD>(a: {
static textarea(a: {
name: string
description?: string | null
warning?: string | null
@@ -233,7 +233,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<string, WD>(
return new Value<string, never>(
async () =>
({
description: null,
@@ -249,7 +249,7 @@ export class Value<Type, WD> {
string,
)
}
static dynamicTextarea<WD>(
static dynamicTextarea<WD = never>(
getA: LazyBuild<
WD,
{
@@ -279,7 +279,7 @@ export class Value<Type, WD> {
}
}, string)
}
static number<Required extends RequiredDefault<number>, WD>(a: {
static number<Required extends RequiredDefault<number>>(a: {
name: string
description?: string | null
warning?: string | null
@@ -295,7 +295,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<AsRequired<number, Required>, WD>(
return new Value<AsRequired<number, Required>, never>(
() => ({
type: "number" as const,
description: null,
@@ -313,7 +313,7 @@ export class Value<Type, WD> {
asRequiredParser(number, a),
)
}
static dynamicNumber<WD>(
static dynamicNumber<WD = never>(
getA: LazyBuild<
WD,
{
@@ -350,7 +350,7 @@ export class Value<Type, WD> {
}
}, number.optional())
}
static color<Required extends RequiredDefault<string>, WD>(a: {
static color<Required extends RequiredDefault<string>>(a: {
name: string
description?: string | null
warning?: string | null
@@ -359,7 +359,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<AsRequired<string, Required>, WD>(
return new Value<AsRequired<string, Required>, never>(
() => ({
type: "color" as const,
description: null,
@@ -374,7 +374,7 @@ export class Value<Type, WD> {
)
}
static dynamicColor<WD>(
static dynamicColor<WD = never>(
getA: LazyBuild<
WD,
{
@@ -400,7 +400,7 @@ export class Value<Type, WD> {
}
}, string.optional())
}
static datetime<Required extends RequiredDefault<string>, WD>(a: {
static datetime<Required extends RequiredDefault<string>>(a: {
name: string
description?: string | null
warning?: string | null
@@ -414,7 +414,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<AsRequired<string, Required>, WD>(
return new Value<AsRequired<string, Required>, never>(
() => ({
type: "datetime" as const,
description: null,
@@ -431,7 +431,7 @@ export class Value<Type, WD> {
asRequiredParser(string, a),
)
}
static dynamicDatetime<WD>(
static dynamicDatetime<WD = never>(
getA: LazyBuild<
WD,
{
@@ -468,7 +468,6 @@ export class Value<Type, WD> {
static select<
Required extends RequiredDefault<string>,
B extends Record<string, string>,
WD,
>(a: {
name: string
description?: string | null
@@ -479,7 +478,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<AsRequired<keyof B, Required>, WD>(
return new Value<AsRequired<keyof B, Required>, never>(
() => ({
description: null,
warning: null,
@@ -497,7 +496,7 @@ export class Value<Type, WD> {
) as any,
)
}
static dynamicSelect<WD>(
static dynamicSelect<WD = never>(
getA: LazyBuild<
WD,
{
@@ -523,7 +522,7 @@ export class Value<Type, WD> {
}
}, string.optional())
}
static multiselect<Values extends Record<string, string>, WD>(a: {
static multiselect<Values extends Record<string, string>>(a: {
name: string
description?: string | null
warning?: string | null
@@ -535,7 +534,7 @@ export class Value<Type, WD> {
Default is false */
immutable?: boolean
}) {
return new Value<(keyof Values)[], WD>(
return new Value<(keyof Values)[], never>(
() => ({
type: "multiselect" as const,
minLength: null,
@@ -551,7 +550,7 @@ export class Value<Type, WD> {
),
)
}
static dynamicMultiselect<WD>(
static dynamicMultiselect<WD = never>(
getA: LazyBuild<
WD,
{
@@ -625,41 +624,36 @@ export class Value<Type, WD> {
asRequiredParser(aVariants.validator, a),
)
}
static filteredUnion<
Required extends RequiredDefault<string>,
Type,
WrapperData,
>(
a: {
name: string
description?: string | null
warning?: string | null
required: Required
default?: string | null
},
aVariants: Variants<Type, WrapperData>,
getDisabledFn: LazyBuild<
WrapperData,
Array<Type extends { unionSelectKey: infer B } ? B & string : never>
>,
static filteredUnion<WrapperData = never>(
getDisabledFn: LazyBuild<WrapperData, string[]>,
) {
return new Value<Type | null | undefined, WrapperData>(
async (options) => ({
type: "union" as const,
description: null,
warning: null,
...a,
variants: await aVariants.build(options as any),
...requiredLikeToAbove(a.required),
disabled: (await getDisabledFn(options)) || [],
immutable: false,
}),
aVariants.validator.optional(),
)
return <Type extends Record<string, any>>(
a: {
name: string
description?: string | null
warning?: string | null
required: RequiredDefault<string>
default?: string | null
},
aVariants: Variants<Type, WrapperData> | Variants<Type, never>,
) => {
return new Value<Type | null | undefined, WrapperData>(
async (options) => ({
type: "union" as const,
description: null,
warning: null,
...a,
variants: await aVariants.build(options as any),
...requiredLikeToAbove(a.required),
disabled: (await getDisabledFn(options)) || [],
immutable: false,
}),
aVariants.validator.optional(),
)
}
}
static list<Type, WrapperData>(a: List<Type, WrapperData>) {
/// TODO
return new Value<Type, WrapperData>(
(options) => a.build(options),
a.validator,
@@ -680,7 +674,37 @@ export class Value<Type, WD> {
})
```
*/
withWrapperData<NewWrapperData extends WD>() {
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() {
return this as any as Value<Type, NewWrapperData>
}
}
type Wrapper = { test: 1 | "5" }
const valueA = Value.dynamicText<Wrapper>(() => ({
name: "a",
required: false,
}))
const variantForC = Variants.of({
lnd: {
name: "lnd Name",
spec: Config.of({
name: Value.text({
name: "Node Name",
required: false,
}),
}),
},
})
const valueC = Value.filteredUnion<Wrapper>(() => [])(
{ name: "a", required: false },
variantForC,
)
const valueB = Value.text({
name: "a",
required: false,
})
const test = Config.of({
a: valueA,
b: valueB,
c: valueC,
})

View File

@@ -63,20 +63,13 @@ export class Variants<Type, WD> {
// }
// },
static of<
TypeMap extends Record<string, Record<string, any>>,
WrapperData,
TypeOut = {
[K in keyof TypeMap & string]: {
unionSelectKey: K
unionValueKey: TypeMap[K]
VariantValues extends {
[K in string]: {
name: string
spec: Config<any, any> | Config<any, never>
}
}[keyof TypeMap & string],
>(a: {
[K in keyof TypeMap]: {
name: string
spec: Config<TypeMap[K], WrapperData>
}
}) {
},
>(a: VariantValues) {
const validator = anyOf(
...Object.entries(a).map(([name, { spec }]) =>
object({
@@ -84,17 +77,36 @@ export class Variants<Type, WD> {
unionValueKey: spec.validator,
}),
),
) as Parser<unknown, TypeOut>
) as Parser<unknown, any>
return new Variants<TypeOut, WrapperData>(async (options) => {
return new Variants<
{
[K in keyof VariantValues]: {
unionSelectKey: K
unionValueKey: VariantValues[K]["spec"] extends
| Config<infer B, any>
| Config<infer B, never>
? B
: never
}
}[keyof VariantValues],
{
[K in keyof VariantValues]: VariantValues[K] extends Config<
any,
infer C
>
? C
: never
}[keyof VariantValues]
>(async (options) => {
const variants = {} as {
[K in keyof TypeMap]: { name: string; spec: InputSpec }
[K in keyof VariantValues]: { name: string; spec: InputSpec }
}
for (const key in a) {
const value = a[key]
variants[key] = {
name: value.name,
spec: await value.spec.build(options),
spec: await value.spec.build(options as any),
}
}
return variants
@@ -114,7 +126,7 @@ export class Variants<Type, WD> {
})
```
*/
withWrapperData<NewWrapperData extends WD>() {
withWrapperData<NewWrapperData extends WD extends never ? any : WD>() {
return this as any as Variants<Type, NewWrapperData>
}
}

View File

@@ -1,5 +1,5 @@
import { SmtpValue } from "../types"
import { Config } from "./builder/config"
import { Config, TypeAsConfigOf } from "./builder/config"
import { Value } from "./builder/value"
import { Variants } from "./builder/variants"
@@ -10,11 +10,11 @@ export const smtpConfig = Value.union(
required: { default: "disabled" },
},
Variants.of({
disabled: { name: "Disabled", spec: Config.of()({}) },
system: { name: "System Credentials", spec: Config.of()({}) },
disabled: { name: "Disabled", spec: Config.of({}) },
system: { name: "System Credentials", spec: Config.of({}) },
custom: {
name: "Custom Credentials",
spec: Config.of()<SmtpValue>({
spec: Config.of<TypeAsConfigOf<SmtpValue, never>>({
server: Value.text({
name: "SMTP Server",
required: {
@@ -56,11 +56,3 @@ export const smtpConfig = Value.union(
},
}),
)
export function getConst<WrapperData>() {
return {
get smtp() {
return smtpConfig.withWrapperData<WrapperData>()
},
}
}

View File

@@ -13,7 +13,10 @@ export type DependenciesReceipt = void & {
export type Save<
WD,
A extends Record<string, any> | Config<Record<string, any>, any>,
A extends
| Record<string, any>
| Config<Record<string, any>, any>
| Config<Record<string, never>, never>,
Manifest extends SDKManifest,
> = (options: {
effects: Effects
@@ -26,7 +29,10 @@ export type Save<
}>
export type Read<
WD,
A extends Record<string, any> | Config<Record<string, any>, any>,
A extends
| Record<string, any>
| Config<Record<string, any>, any>
| Config<Record<string, any>, never>,
> = (options: {
effects: Effects
utils: Utils<WD>
@@ -40,11 +46,14 @@ export type Read<
*/
export function setupConfig<
WD,
ConfigType extends Record<string, any> | Config<any, any>,
ConfigType extends
| Record<string, any>
| Config<any, any>
| Config<any, never>,
Manifest extends SDKManifest,
Type extends Record<string, any> = ExtractConfigType<ConfigType>,
>(
spec: Config<Type, WD>,
spec: Config<Type, WD> | Config<Type, never>,
write: Save<WD, Type, Manifest>,
read: Read<WD, Type>,
) {
@@ -73,7 +82,7 @@ export function setupConfig<
return {
spec: await spec.build({
effects,
utils: myUtils,
utils: myUtils as any,
}),
config: configValue,
}

View File

@@ -11,7 +11,7 @@ describe("builder tests", () => {
test("text", async () => {
const bitcoinPropertiesBuilt: {
"peer-tor-address": ValueSpec
} = await Config.of<unknown>()({
} = await Config.of({
"peer-tor-address": Value.text({
name: "Peer tor address",
description: "The Tor address of the peer interface",
@@ -234,7 +234,7 @@ describe("values", () => {
description: null,
warning: null,
},
Config.of<null>()({
Config.of({
a: Value.toggle({
name: "test",
description: null,
@@ -259,7 +259,7 @@ describe("values", () => {
Variants.of({
a: {
name: "a",
spec: Config.of<unknown>()({
spec: Config.of({
b: Value.toggle({
name: "b",
description: null,
@@ -501,7 +501,7 @@ describe("values", () => {
})
describe("filtering", () => {
test("union", async () => {
const value = Value.filteredUnion(
const value = Value.filteredUnion(() => ["a", "c"])(
{
name: "Testing",
required: { default: null },
@@ -512,7 +512,7 @@ describe("values", () => {
Variants.of({
a: {
name: "a",
spec: Config.of<unknown>()({
spec: Config.of({
b: Value.toggle({
name: "b",
description: null,
@@ -523,7 +523,7 @@ describe("values", () => {
},
b: {
name: "b",
spec: Config.of<unknown>()({
spec: Config.of({
b: Value.toggle({
name: "b",
description: null,
@@ -533,11 +533,6 @@ describe("values", () => {
}),
},
}),
() => [
"a",
// @ts-expect-error
"c",
],
)
const validator = value.validator
validator.unsafeCast({ unionSelectKey: "a", unionValueKey: { b: false } })
@@ -584,7 +579,7 @@ describe("Builder List", () => {
name: "test",
},
{
spec: Config.of<unknown>()({
spec: Config.of({
test: Value.toggle({
name: "test",
description: null,
@@ -655,7 +650,7 @@ describe("Builder List", () => {
describe("Nested nullable values", () => {
test("Testing text", async () => {
const value = Config.of<unknown>()({
const value = Config.of({
a: Value.text({
name: "Temp Name",
description:
@@ -670,7 +665,7 @@ describe("Nested nullable values", () => {
testOutput<typeof validator._TYPE, { a: string | null | undefined }>()(null)
})
test("Testing number", async () => {
const value = Config.of<unknown>()({
const value = Config.of({
a: Value.number({
name: "Temp Name",
description:
@@ -692,7 +687,7 @@ describe("Nested nullable values", () => {
testOutput<typeof validator._TYPE, { a: number | null | undefined }>()(null)
})
test("Testing color", async () => {
const value = Config.of<unknown>()({
const value = Config.of({
a: Value.color({
name: "Temp Name",
description:
@@ -708,7 +703,7 @@ describe("Nested nullable values", () => {
testOutput<typeof validator._TYPE, { a: string | null | undefined }>()(null)
})
test("Testing select", async () => {
const value = Config.of<unknown>()({
const value = Config.of({
a: Value.select({
name: "Temp Name",
description:
@@ -737,7 +732,7 @@ describe("Nested nullable values", () => {
testOutput<typeof validator._TYPE, { a: "a" | null | undefined }>()(null)
})
test("Testing multiselect", async () => {
const value = Config.of<unknown>()({
const value = Config.of({
a: Value.multiselect({
name: "Temp Name",
description:

View File

@@ -9,7 +9,7 @@ describe("Config Types", () => {
for (const option of options) {
const test = (option as any)(
{} as any,
{ spec: Config.of()({}) } as any,
{ spec: Config.of({}) } as any,
) as any
const someList = await Value.list(test).build({} as any)
if (isValueSpecListOf(someList, "text")) {

View File

@@ -436,7 +436,7 @@ export type ExtractWrapperData<WrapperData, Path extends string> =
// prettier-ignore
type _EnsureWrapperDataPath<WrapperData, 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 }` ? (WrapperData extends {[K in A]: any} ? Origin : never) :
Path extends `/${infer A }` ? (WrapperData extends {[K in A]: infer B} ? Origin : never) :
Path extends '' ? Origin :
never
// prettier-ignore

View File

@@ -43,7 +43,6 @@ import { Variants } from "${startSdk}/lib/config/builder/variants"
import {WrapperData} from '${wrapperData}'
`)
const data = await inputData
const hammerWrapperData = !nested ? ".withWrapperData()" : ""
const namedConsts = new Set(["Config", "Value", "List"])
const configName = newConst("configSpec", convertInputSpec(data))
@@ -73,13 +72,13 @@ import { Variants } from "${startSdk}/lib/config/builder/variants"
for (const [key, value] of Object.entries(data)) {
const variableName = maybeNewConst(key, convertValueSpec(value))
answer += `${JSON.stringify(key)}: ${variableName}${hammerWrapperData},`
answer += `${JSON.stringify(key)}: ${variableName},`
}
return `${answer}}`
}
function convertInputSpec(data: any) {
return `Config.of<WrapperData>()(${convertInputSpecInner(data)})`
return `Config.of(${convertInputSpecInner(data)})`
}
function convertValueSpec(value: any): string {
switch (value.type) {
@@ -363,8 +362,8 @@ import { Variants } from "${startSdk}/lib/config/builder/variants"
const listConfig = maybeNewConst(
value.name + "_list_config",
`
Config.of<WrapperData>()({
"union": ${unionValueName}${hammerWrapperData}
Config.of({
"union": ${unionValueName}
})
`,
)