mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
sdk input spec improvements (#2785)
* sdk input spec improvements * more sdk changes * fe changes * alpha.14 * fix tests * separate validator in filehelper * use deeppartial for getinput * fix union type and update ts-matches * alpha.15 * alpha.16 * alpha.17 --------- Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
RequiredDefault,
|
||||
Value,
|
||||
} from "../../base/lib/actions/input/builder/value"
|
||||
import { Value } from "../../base/lib/actions/input/builder/value"
|
||||
import {
|
||||
InputSpec,
|
||||
ExtractInputSpecType,
|
||||
@@ -141,9 +138,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
...startSdkEffectWrapper,
|
||||
action: {
|
||||
run: actions.runAction,
|
||||
request: <
|
||||
T extends Action<T.ActionId, any, any, Record<string, unknown>>,
|
||||
>(
|
||||
request: <T extends Action<T.ActionId, any, any>>(
|
||||
effects: T.Effects,
|
||||
packageId: T.PackageId,
|
||||
action: T,
|
||||
@@ -157,9 +152,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
severity,
|
||||
options: options,
|
||||
}),
|
||||
requestOwn: <
|
||||
T extends Action<T.ActionId, Store, any, Record<string, unknown>>,
|
||||
>(
|
||||
requestOwn: <T extends Action<T.ActionId, Store, any>>(
|
||||
effects: T.Effects,
|
||||
action: T,
|
||||
severity: T.ActionSeverity,
|
||||
@@ -1060,14 +1053,14 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
/** Presents a warning prompt before permitting the value to change. */
|
||||
warning?: string | null
|
||||
/**
|
||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
||||
* @type { false | { default: string | RandomString | null } }
|
||||
* @example required: false
|
||||
* @example required: { default: null }
|
||||
* @example required: { default: 'World' }
|
||||
* @example required: { default: { charset: 'abcdefg', len: 16 } }
|
||||
* @description optionally provide a default value.
|
||||
* @type { string | RandomString | null }
|
||||
* @example default: null
|
||||
* @example default: 'World'
|
||||
* @example default: { charset: 'abcdefg', len: 16 }
|
||||
*/
|
||||
required: RequiredDefault<DefaultString>
|
||||
default: DefaultString | null
|
||||
required: boolean
|
||||
/**
|
||||
* @description Mask (aka camouflage) text input with dots: ● ● ●
|
||||
* @default false
|
||||
@@ -1110,15 +1103,12 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
description?: string | null
|
||||
/** Presents a warning prompt before permitting the value to change. */
|
||||
warning?: string | null
|
||||
/**
|
||||
* @description Unlike other "required" fields, for textarea this is a simple boolean.
|
||||
*/
|
||||
default: string | null
|
||||
required: boolean
|
||||
minLength?: number | null
|
||||
maxLength?: number | null
|
||||
placeholder?: string | null
|
||||
disabled?: false | string
|
||||
generate?: null | RandomString
|
||||
}
|
||||
>,
|
||||
) => Value.dynamicTextarea<Store>(getA),
|
||||
@@ -1131,13 +1121,13 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
/** Presents a warning prompt before permitting the value to change. */
|
||||
warning?: string | null
|
||||
/**
|
||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
||||
* @type { false | { default: number | null } }
|
||||
* @example required: false
|
||||
* @example required: { default: null }
|
||||
* @example required: { default: 7 }
|
||||
* @description optionally provide a default value.
|
||||
* @type { number | null }
|
||||
* @example default: null
|
||||
* @example default: 7
|
||||
*/
|
||||
required: RequiredDefault<number>
|
||||
default: number | null
|
||||
required: boolean
|
||||
min?: number | null
|
||||
max?: number | null
|
||||
/**
|
||||
@@ -1167,13 +1157,13 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
/** Presents a warning prompt before permitting the value to change. */
|
||||
warning?: string | null
|
||||
/**
|
||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
||||
* @type { false | { default: string | null } }
|
||||
* @example required: false
|
||||
* @example required: { default: null }
|
||||
* @example required: { default: 'ffffff' }
|
||||
* @description optionally provide a default value.
|
||||
* @type { string | null }
|
||||
* @example default: null
|
||||
* @example default: 'ffffff'
|
||||
*/
|
||||
required: RequiredDefault<string>
|
||||
default: string | null
|
||||
required: boolean
|
||||
disabled?: false | string
|
||||
}
|
||||
>,
|
||||
@@ -1187,13 +1177,13 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
/** Presents a warning prompt before permitting the value to change. */
|
||||
warning?: string | null
|
||||
/**
|
||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
||||
* @type { false | { default: string | null } }
|
||||
* @example required: false
|
||||
* @example required: { default: null }
|
||||
* @example required: { default: '1985-12-16 18:00:00.000' }
|
||||
* @description optionally provide a default value.
|
||||
* @type { string | null }
|
||||
* @example default: null
|
||||
* @example default: '1985-12-16 18:00:00.000'
|
||||
*/
|
||||
required: RequiredDefault<string>
|
||||
default: string
|
||||
required: boolean
|
||||
/**
|
||||
* @description Informs the browser how to behave and which date/time component to display.
|
||||
* @default "datetime-local"
|
||||
@@ -1205,7 +1195,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
}
|
||||
>,
|
||||
) => Value.dynamicDatetime<Store>(getA),
|
||||
dynamicSelect: (
|
||||
dynamicSelect: <Variants extends Record<string, string>>(
|
||||
getA: LazyBuild<
|
||||
Store,
|
||||
{
|
||||
@@ -1214,13 +1204,12 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
/** Presents a warning prompt before permitting the value to change. */
|
||||
warning?: string | null
|
||||
/**
|
||||
* @description Determines if the field is required. If so, optionally provide a default value from the list of values.
|
||||
* @type { false | { default: string | null } }
|
||||
* @example required: false
|
||||
* @example required: { default: null }
|
||||
* @example required: { default: 'radio1' }
|
||||
* @description provide a default value from the list of values.
|
||||
* @type { default: string }
|
||||
* @example default: 'radio1'
|
||||
*/
|
||||
required: RequiredDefault<string>
|
||||
default: keyof Variants & string
|
||||
required: boolean
|
||||
/**
|
||||
* @description A mapping of unique radio options to their human readable display format.
|
||||
* @example
|
||||
@@ -1232,7 +1221,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
}
|
||||
* ```
|
||||
*/
|
||||
values: Record<string, string>
|
||||
values: Variants
|
||||
/**
|
||||
* @options
|
||||
* - false - The field can be modified.
|
||||
@@ -1282,27 +1271,37 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
>,
|
||||
) => Value.dynamicMultiselect<Store>(getA),
|
||||
filteredUnion: <
|
||||
Required extends RequiredDefault<string>,
|
||||
Type extends Record<string, any>,
|
||||
VariantValues extends {
|
||||
[K in string]: {
|
||||
name: string
|
||||
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||
}
|
||||
},
|
||||
>(
|
||||
getDisabledFn: LazyBuild<Store, string[]>,
|
||||
a: {
|
||||
name: string
|
||||
description?: string | null
|
||||
warning?: string | null
|
||||
required: Required
|
||||
default: keyof VariantValues & string
|
||||
},
|
||||
aVariants: Variants<Type, Store> | Variants<Type, never>,
|
||||
aVariants:
|
||||
| Variants<VariantValues, Store>
|
||||
| Variants<VariantValues, never>,
|
||||
) =>
|
||||
Value.filteredUnion<Required, Type, Store>(
|
||||
Value.filteredUnion<VariantValues, Store>(
|
||||
getDisabledFn,
|
||||
a,
|
||||
aVariants,
|
||||
),
|
||||
|
||||
dynamicUnion: <
|
||||
Required extends RequiredDefault<string>,
|
||||
Type extends Record<string, any>,
|
||||
VariantValues extends {
|
||||
[K in string]: {
|
||||
name: string
|
||||
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||
}
|
||||
},
|
||||
>(
|
||||
getA: LazyBuild<
|
||||
Store,
|
||||
@@ -1312,13 +1311,12 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
/** Presents a warning prompt before permitting the value to change. */
|
||||
warning?: string | null
|
||||
/**
|
||||
* @description Determines if the field is required. If so, optionally provide a default value from the list of variants.
|
||||
* @type { false | { default: string | null } }
|
||||
* @example required: false
|
||||
* @example required: { default: null }
|
||||
* @example required: { default: 'variant1' }
|
||||
* @description provide a default value from the list of variants.
|
||||
* @type { string }
|
||||
* @example default: 'variant1'
|
||||
*/
|
||||
required: Required
|
||||
default: keyof VariantValues & string
|
||||
required: boolean
|
||||
/**
|
||||
* @options
|
||||
* - false - The field can be modified.
|
||||
@@ -1329,8 +1327,10 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
disabled: false | string | string[]
|
||||
}
|
||||
>,
|
||||
aVariants: Variants<Type, Store> | Variants<Type, never>,
|
||||
) => Value.dynamicUnion<Required, Type, Store>(getA, aVariants),
|
||||
aVariants:
|
||||
| Variants<VariantValues, Store>
|
||||
| Variants<VariantValues, never>,
|
||||
) => Value.dynamicUnion<VariantValues, Store>(getA, aVariants),
|
||||
},
|
||||
Variants: {
|
||||
of: <
|
||||
|
||||
@@ -17,7 +17,8 @@ describe("builder tests", () => {
|
||||
"peer-tor-address": Value.text({
|
||||
name: "Peer tor address",
|
||||
description: "The Tor address of the peer interface",
|
||||
required: { default: null },
|
||||
required: true,
|
||||
default: null,
|
||||
}),
|
||||
}).build({} as any)
|
||||
expect(bitcoinPropertiesBuilt).toMatchObject({
|
||||
@@ -55,7 +56,8 @@ describe("values", () => {
|
||||
test("text", async () => {
|
||||
const value = Value.text({
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
required: true,
|
||||
default: null,
|
||||
})
|
||||
const validator = value.validator
|
||||
const rawIs = await value.build({} as any)
|
||||
@@ -66,7 +68,8 @@ describe("values", () => {
|
||||
test("text with default", async () => {
|
||||
const value = Value.text({
|
||||
name: "Testing",
|
||||
required: { default: "this is a default value" },
|
||||
required: true,
|
||||
default: "this is a default value",
|
||||
})
|
||||
const validator = value.validator
|
||||
const rawIs = await value.build({} as any)
|
||||
@@ -78,6 +81,7 @@ describe("values", () => {
|
||||
const value = Value.text({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
})
|
||||
const validator = value.validator
|
||||
const rawIs = await value.build({} as any)
|
||||
@@ -89,6 +93,7 @@ describe("values", () => {
|
||||
const value = Value.color({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
description: null,
|
||||
warning: null,
|
||||
})
|
||||
@@ -99,7 +104,8 @@ describe("values", () => {
|
||||
test("datetime", async () => {
|
||||
const value = Value.datetime({
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
required: true,
|
||||
default: null,
|
||||
description: null,
|
||||
warning: null,
|
||||
inputmode: "date",
|
||||
@@ -114,6 +120,7 @@ describe("values", () => {
|
||||
const value = Value.datetime({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
description: null,
|
||||
warning: null,
|
||||
inputmode: "date",
|
||||
@@ -128,6 +135,7 @@ describe("values", () => {
|
||||
const value = Value.textarea({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
description: null,
|
||||
warning: null,
|
||||
minLength: null,
|
||||
@@ -136,12 +144,13 @@ describe("values", () => {
|
||||
})
|
||||
const validator = value.validator
|
||||
validator.unsafeCast("test text")
|
||||
testOutput<typeof validator._TYPE, string>()(null)
|
||||
testOutput<typeof validator._TYPE, string | null | undefined>()(null)
|
||||
})
|
||||
test("number", async () => {
|
||||
const value = Value.number({
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
required: true,
|
||||
default: null,
|
||||
integer: false,
|
||||
description: null,
|
||||
warning: null,
|
||||
@@ -159,6 +168,7 @@ describe("values", () => {
|
||||
const value = Value.number({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
integer: false,
|
||||
description: null,
|
||||
warning: null,
|
||||
@@ -175,7 +185,7 @@ describe("values", () => {
|
||||
test("select", async () => {
|
||||
const value = Value.select({
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
default: "a",
|
||||
values: {
|
||||
a: "A",
|
||||
b: "B",
|
||||
@@ -192,7 +202,7 @@ describe("values", () => {
|
||||
test("nullable select", async () => {
|
||||
const value = Value.select({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: "a",
|
||||
values: {
|
||||
a: "A",
|
||||
b: "B",
|
||||
@@ -203,8 +213,7 @@ describe("values", () => {
|
||||
const validator = value.validator
|
||||
validator.unsafeCast("a")
|
||||
validator.unsafeCast("b")
|
||||
validator.unsafeCast(null)
|
||||
testOutput<typeof validator._TYPE, "a" | "b" | null | undefined>()(null)
|
||||
testOutput<typeof validator._TYPE, "a" | "b">()(null)
|
||||
})
|
||||
test("multiselect", async () => {
|
||||
const value = Value.multiselect({
|
||||
@@ -250,7 +259,7 @@ describe("values", () => {
|
||||
const value = Value.union(
|
||||
{
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
default: "a",
|
||||
description: null,
|
||||
warning: null,
|
||||
},
|
||||
@@ -271,7 +280,16 @@ describe("values", () => {
|
||||
const validator = value.validator
|
||||
validator.unsafeCast({ selection: "a", value: { b: false } })
|
||||
type Test = typeof validator._TYPE
|
||||
testOutput<Test, { selection: "a"; value: { b: boolean } }>()(null)
|
||||
testOutput<
|
||||
Test,
|
||||
{
|
||||
selection: "a"
|
||||
value: {
|
||||
b: boolean
|
||||
}
|
||||
other?: {}
|
||||
}
|
||||
>()(null)
|
||||
})
|
||||
|
||||
describe("dynamic", () => {
|
||||
@@ -301,7 +319,8 @@ describe("values", () => {
|
||||
test("text", async () => {
|
||||
const value = Value.dynamicText(async () => ({
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
required: true,
|
||||
default: null,
|
||||
}))
|
||||
const validator = value.validator
|
||||
const rawIs = await value.build({} as any)
|
||||
@@ -317,7 +336,8 @@ describe("values", () => {
|
||||
test("text with default", async () => {
|
||||
const value = Value.dynamicText(async () => ({
|
||||
name: "Testing",
|
||||
required: { default: "this is a default value" },
|
||||
required: true,
|
||||
default: "this is a default value",
|
||||
}))
|
||||
const validator = value.validator
|
||||
validator.unsafeCast("test text")
|
||||
@@ -333,6 +353,7 @@ describe("values", () => {
|
||||
const value = Value.dynamicText(async () => ({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
}))
|
||||
const validator = value.validator
|
||||
const rawIs = await value.build({} as any)
|
||||
@@ -349,6 +370,7 @@ describe("values", () => {
|
||||
const value = Value.dynamicColor(async () => ({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
description: null,
|
||||
warning: null,
|
||||
}))
|
||||
@@ -414,7 +436,8 @@ describe("values", () => {
|
||||
|
||||
return {
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
required: true,
|
||||
default: null,
|
||||
inputmode: "date",
|
||||
}
|
||||
},
|
||||
@@ -436,6 +459,7 @@ describe("values", () => {
|
||||
const value = Value.dynamicTextarea(async () => ({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
default: null,
|
||||
description: null,
|
||||
warning: null,
|
||||
minLength: null,
|
||||
@@ -444,8 +468,7 @@ describe("values", () => {
|
||||
}))
|
||||
const validator = value.validator
|
||||
validator.unsafeCast("test text")
|
||||
expect(() => validator.unsafeCast(null)).toThrowError()
|
||||
testOutput<typeof validator._TYPE, string>()(null)
|
||||
testOutput<typeof validator._TYPE, string | null | undefined>()(null)
|
||||
expect(await value.build(fakeOptions)).toMatchObject({
|
||||
name: "Testing",
|
||||
required: false,
|
||||
@@ -454,7 +477,8 @@ describe("values", () => {
|
||||
test("number", async () => {
|
||||
const value = Value.dynamicNumber(() => ({
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
required: true,
|
||||
default: null,
|
||||
integer: false,
|
||||
description: null,
|
||||
warning: null,
|
||||
@@ -477,7 +501,7 @@ describe("values", () => {
|
||||
test("select", async () => {
|
||||
const value = Value.dynamicSelect(() => ({
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
default: "a",
|
||||
values: {
|
||||
a: "A",
|
||||
b: "B",
|
||||
@@ -489,11 +513,9 @@ describe("values", () => {
|
||||
validator.unsafeCast("a")
|
||||
validator.unsafeCast("b")
|
||||
validator.unsafeCast("c")
|
||||
validator.unsafeCast(null)
|
||||
testOutput<typeof validator._TYPE, string | null | undefined>()(null)
|
||||
testOutput<typeof validator._TYPE, string>()(null)
|
||||
expect(await value.build(fakeOptions)).toMatchObject({
|
||||
name: "Testing",
|
||||
required: true,
|
||||
})
|
||||
})
|
||||
test("multiselect", async () => {
|
||||
@@ -529,7 +551,7 @@ describe("values", () => {
|
||||
() => ["a", "c"],
|
||||
{
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
default: "a",
|
||||
description: null,
|
||||
warning: null,
|
||||
},
|
||||
@@ -563,8 +585,28 @@ describe("values", () => {
|
||||
type Test = typeof validator._TYPE
|
||||
testOutput<
|
||||
Test,
|
||||
| { selection: "a"; value: { b: boolean } }
|
||||
| { selection: "b"; value: { b: boolean } }
|
||||
| {
|
||||
selection: "a"
|
||||
value: {
|
||||
b: boolean
|
||||
}
|
||||
other?: {
|
||||
b?: {
|
||||
b?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
| {
|
||||
selection: "b"
|
||||
value: {
|
||||
b: boolean
|
||||
}
|
||||
other?: {
|
||||
a?: {
|
||||
b?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
>()(null)
|
||||
|
||||
const built = await value.build({} as any)
|
||||
@@ -596,7 +638,7 @@ describe("values", () => {
|
||||
() => ({
|
||||
disabled: ["a", "c"],
|
||||
name: "Testing",
|
||||
required: { default: null },
|
||||
default: "b",
|
||||
description: null,
|
||||
warning: null,
|
||||
}),
|
||||
@@ -630,10 +672,28 @@ describe("values", () => {
|
||||
type Test = typeof validator._TYPE
|
||||
testOutput<
|
||||
Test,
|
||||
| { selection: "a"; value: { b: boolean } }
|
||||
| { selection: "b"; value: { b: boolean } }
|
||||
| null
|
||||
| undefined
|
||||
| {
|
||||
selection: "a"
|
||||
value: {
|
||||
b: boolean
|
||||
}
|
||||
other?: {
|
||||
b?: {
|
||||
b?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
| {
|
||||
selection: "b"
|
||||
value: {
|
||||
b: boolean
|
||||
}
|
||||
other?: {
|
||||
a?: {
|
||||
b?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
>()(null)
|
||||
|
||||
const built = await value.build({} as any)
|
||||
@@ -728,6 +788,7 @@ describe("Nested nullable values", () => {
|
||||
description:
|
||||
"If no name is provided, the name from inputSpec will be used",
|
||||
required: false,
|
||||
default: null,
|
||||
}),
|
||||
})
|
||||
const validator = value.validator
|
||||
@@ -743,6 +804,7 @@ describe("Nested nullable values", () => {
|
||||
description:
|
||||
"If no name is provided, the name from inputSpec will be used",
|
||||
required: false,
|
||||
default: null,
|
||||
warning: null,
|
||||
placeholder: null,
|
||||
integer: false,
|
||||
@@ -765,6 +827,7 @@ describe("Nested nullable values", () => {
|
||||
description:
|
||||
"If no name is provided, the name from inputSpec will be used",
|
||||
required: false,
|
||||
default: null,
|
||||
warning: null,
|
||||
}),
|
||||
})
|
||||
@@ -780,7 +843,7 @@ describe("Nested nullable values", () => {
|
||||
name: "Temp Name",
|
||||
description:
|
||||
"If no name is provided, the name from inputSpec will be used",
|
||||
required: false,
|
||||
default: "a",
|
||||
warning: null,
|
||||
values: {
|
||||
a: "A",
|
||||
@@ -791,7 +854,7 @@ describe("Nested nullable values", () => {
|
||||
name: "Temp Name",
|
||||
description:
|
||||
"If no name is provided, the name from inputSpec will be used",
|
||||
required: false,
|
||||
default: "a",
|
||||
warning: null,
|
||||
values: {
|
||||
a: "A",
|
||||
@@ -799,10 +862,9 @@ describe("Nested nullable values", () => {
|
||||
}).build({} as any)
|
||||
|
||||
const validator = value.validator
|
||||
validator.unsafeCast({ a: null })
|
||||
validator.unsafeCast({ a: "a" })
|
||||
expect(() => validator.unsafeCast({ a: "4" })).toThrowError()
|
||||
testOutput<typeof validator._TYPE, { a: "a" | null | undefined }>()(null)
|
||||
testOutput<typeof validator._TYPE, { a: "a" }>()(null)
|
||||
})
|
||||
test("Testing multiselect", async () => {
|
||||
const value = InputSpec.of({
|
||||
|
||||
@@ -87,7 +87,7 @@ describe("Inputs", () => {
|
||||
dbcache: 5,
|
||||
pruning: {
|
||||
selection: "disabled",
|
||||
value: {},
|
||||
value: { disabled: {} },
|
||||
},
|
||||
blockfilters: {
|
||||
blockfilterindex: false,
|
||||
|
||||
@@ -80,7 +80,8 @@ export class FileHelper<A> {
|
||||
protected constructor(
|
||||
readonly path: string,
|
||||
readonly writeData: (dataIn: A) => string,
|
||||
readonly readData: (stringValue: string) => A,
|
||||
readonly readData: (stringValue: string) => unknown,
|
||||
readonly validate: (value: unknown) => A,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -97,10 +98,7 @@ export class FileHelper<A> {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the file from disk and converts it to structured data.
|
||||
*/
|
||||
private async readOnce(): Promise<A | null> {
|
||||
private async readFile(): Promise<unknown> {
|
||||
if (!(await exists(this.path))) {
|
||||
return null
|
||||
}
|
||||
@@ -109,6 +107,15 @@ export class FileHelper<A> {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the file from disk and converts it to structured data.
|
||||
*/
|
||||
private async readOnce(): Promise<A | null> {
|
||||
const data = await this.readFile()
|
||||
if (!data) return null
|
||||
return this.validate(data)
|
||||
}
|
||||
|
||||
private async readConst(effects: T.Effects): Promise<A | null> {
|
||||
const watch = this.readWatch()
|
||||
const res = await watch.next()
|
||||
@@ -156,22 +163,22 @@ export class FileHelper<A> {
|
||||
* Accepts full structured data and performs a merge with the existing file on disk if it exists.
|
||||
*/
|
||||
async write(data: A) {
|
||||
const fileData = (await this.readOnce()) || {}
|
||||
const fileData = (await this.readFile()) || {}
|
||||
const mergeData = merge({}, fileData, data)
|
||||
return await this.writeFile(mergeData)
|
||||
return await this.writeFile(this.validate(mergeData))
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts partial structured data and performs a merge with the existing file on disk.
|
||||
*/
|
||||
async merge(data: Partial<A>) {
|
||||
async merge(data: T.DeepPartial<A>) {
|
||||
const fileData =
|
||||
(await this.readOnce()) ||
|
||||
(await this.readFile()) ||
|
||||
(() => {
|
||||
throw new Error(`${this.path}: does not exist`)
|
||||
})()
|
||||
const mergeData = merge({}, fileData, data)
|
||||
return await this.writeFile(mergeData)
|
||||
return await this.writeFile(this.validate(mergeData))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +186,7 @@ export class FileHelper<A> {
|
||||
* Like one behaviour of another dependency or something similar.
|
||||
*/
|
||||
withPath(path: string) {
|
||||
return new FileHelper<A>(path, this.writeData, this.readData)
|
||||
return new FileHelper<A>(path, this.writeData, this.readData, this.validate)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,9 +197,10 @@ export class FileHelper<A> {
|
||||
static raw<A>(
|
||||
path: string,
|
||||
toFile: (dataIn: A) => string,
|
||||
fromFile: (rawData: string) => A,
|
||||
fromFile: (rawData: string) => unknown,
|
||||
validate: (data: unknown) => A,
|
||||
) {
|
||||
return new FileHelper<A>(path, toFile, fromFile)
|
||||
return new FileHelper<A>(path, toFile, fromFile, validate)
|
||||
}
|
||||
/**
|
||||
* Create a File Helper for a .json file.
|
||||
@@ -200,12 +208,9 @@ export class FileHelper<A> {
|
||||
static json<A>(path: string, shape: matches.Validator<unknown, A>) {
|
||||
return new FileHelper<A>(
|
||||
path,
|
||||
(inData) => {
|
||||
return JSON.stringify(inData, null, 2)
|
||||
},
|
||||
(inString) => {
|
||||
return shape.unsafeCast(JSON.parse(inString))
|
||||
},
|
||||
(inData) => JSON.stringify(inData, null, 2),
|
||||
(inString) => JSON.parse(inString),
|
||||
(data) => shape.unsafeCast(data),
|
||||
)
|
||||
}
|
||||
/**
|
||||
@@ -217,12 +222,9 @@ export class FileHelper<A> {
|
||||
) {
|
||||
return new FileHelper<A>(
|
||||
path,
|
||||
(inData) => {
|
||||
return TOML.stringify(inData as any)
|
||||
},
|
||||
(inString) => {
|
||||
return shape.unsafeCast(TOML.parse(inString))
|
||||
},
|
||||
(inData) => TOML.stringify(inData as any),
|
||||
(inString) => TOML.parse(inString),
|
||||
(data) => shape.unsafeCast(data),
|
||||
)
|
||||
}
|
||||
/**
|
||||
@@ -234,12 +236,9 @@ export class FileHelper<A> {
|
||||
) {
|
||||
return new FileHelper<A>(
|
||||
path,
|
||||
(inData) => {
|
||||
return YAML.stringify(inData, null, 2)
|
||||
},
|
||||
(inString) => {
|
||||
return shape.unsafeCast(YAML.parse(inString))
|
||||
},
|
||||
(inData) => YAML.stringify(inData, null, 2),
|
||||
(inString) => YAML.parse(inString),
|
||||
(data) => shape.unsafeCast(data),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
13
sdk/package/package-lock.json
generated
13
sdk/package/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.3.6-alpha.13",
|
||||
"version": "0.3.6-alpha.16",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.3.6-alpha.13",
|
||||
"version": "0.3.6-alpha.16",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.5",
|
||||
@@ -15,7 +15,7 @@
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"mime-types": "^2.1.35",
|
||||
"ts-matches": "^5.5.1",
|
||||
"ts-matches": "^6.0.0",
|
||||
"yaml": "^2.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -3918,9 +3918,10 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ts-matches": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz",
|
||||
"integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg=="
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.0.0.tgz",
|
||||
"integrity": "sha512-vR4hhz9bYMW30qIJUuLaeAWlsR54vse6ZI2riVhVLMBE6/vss43jwrOvbHheiyU7e26ssT/yWx69aJHD2REJSA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ts-morph": {
|
||||
"version": "18.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.3.6-alpha.13",
|
||||
"version": "0.3.6-alpha.17",
|
||||
"description": "Software development kit to facilitate packaging services for StartOS",
|
||||
"main": "./package/lib/index.js",
|
||||
"types": "./package/lib/index.d.ts",
|
||||
@@ -33,7 +33,7 @@
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"mime-types": "^2.1.35",
|
||||
"ts-matches": "^5.5.1",
|
||||
"ts-matches": "^6.0.0",
|
||||
"yaml": "^2.2.2",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@noble/curves": "^1.4.0",
|
||||
|
||||
@@ -85,6 +85,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
||||
description: value.description || null,
|
||||
warning: value.warning || null,
|
||||
required: !(value.nullable || false),
|
||||
default: value.default,
|
||||
placeholder: value.placeholder || null,
|
||||
maxLength: null,
|
||||
minLength: null,
|
||||
@@ -96,12 +97,8 @@ const {InputSpec, List, Value, Variants} = sdk
|
||||
return `${rangeToTodoComment(value?.range)}Value.text(${JSON.stringify(
|
||||
{
|
||||
name: value.name || null,
|
||||
// prettier-ignore
|
||||
required: (
|
||||
value.default != null ? {default: value.default} :
|
||||
value.nullable === false ? {default: null} :
|
||||
!value.nullable
|
||||
),
|
||||
default: value.default || null,
|
||||
required: !value.nullable,
|
||||
description: value.description || null,
|
||||
warning: value.warning || null,
|
||||
masked: value.masked || false,
|
||||
@@ -130,12 +127,8 @@ const {InputSpec, List, Value, Variants} = sdk
|
||||
name: value.name || null,
|
||||
description: value.description || null,
|
||||
warning: value.warning || null,
|
||||
// prettier-ignore
|
||||
required: (
|
||||
value.default != null ? {default: value.default} :
|
||||
value.nullable === false ? {default: null} :
|
||||
!value.nullable
|
||||
),
|
||||
default: value.default || null,
|
||||
required: !value.nullable,
|
||||
min: null,
|
||||
max: null,
|
||||
step: null,
|
||||
@@ -174,13 +167,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
||||
name: value.name || null,
|
||||
description: value.description || null,
|
||||
warning: value.warning || null,
|
||||
|
||||
// prettier-ignore
|
||||
required:(
|
||||
value.default != null ? {default: value.default} :
|
||||
value.nullable === false ? {default: null} :
|
||||
!value.nullable
|
||||
),
|
||||
default: value.default,
|
||||
values,
|
||||
},
|
||||
null,
|
||||
@@ -207,14 +194,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
||||
name: ${JSON.stringify(value.name || null)},
|
||||
description: ${JSON.stringify(value.tag.description || null)},
|
||||
warning: ${JSON.stringify(value.tag.warning || null)},
|
||||
|
||||
// prettier-ignore
|
||||
required: ${JSON.stringify(
|
||||
// prettier-ignore
|
||||
value.default != null ? {default: value.default} :
|
||||
value.nullable === false ? {default: null} :
|
||||
!value.nullable,
|
||||
)},
|
||||
default: ${JSON.stringify(value.default)},
|
||||
}, ${variants})`
|
||||
}
|
||||
case "list": {
|
||||
@@ -341,12 +321,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
||||
value?.spec?.tag?.description || null,
|
||||
)},
|
||||
warning: ${JSON.stringify(value?.spec?.tag?.warning || null)},
|
||||
required: ${JSON.stringify(
|
||||
// prettier-ignore
|
||||
'default' in value?.spec ? {default: value?.spec?.default} :
|
||||
!!value?.spec?.tag?.nullable || false ? {default: null} :
|
||||
false,
|
||||
)},
|
||||
default: ${JSON.stringify(value?.spec?.default || null)},
|
||||
}, ${variants})
|
||||
`,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user