mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +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:
@@ -264,7 +264,6 @@ exports[`transformConfigSpec transformConfigSpec(bitcoind) 1`] = `
|
|||||||
"disabled": false,
|
"disabled": false,
|
||||||
"immutable": false,
|
"immutable": false,
|
||||||
"name": "Pruning Mode",
|
"name": "Pruning Mode",
|
||||||
"required": true,
|
|
||||||
"type": "union",
|
"type": "union",
|
||||||
"variants": {
|
"variants": {
|
||||||
"automatic": {
|
"automatic": {
|
||||||
@@ -524,7 +523,6 @@ exports[`transformConfigSpec transformConfigSpec(embassyPages) 1`] = `
|
|||||||
"disabled": false,
|
"disabled": false,
|
||||||
"immutable": false,
|
"immutable": false,
|
||||||
"name": "Type",
|
"name": "Type",
|
||||||
"required": true,
|
|
||||||
"type": "union",
|
"type": "union",
|
||||||
"variants": {
|
"variants": {
|
||||||
"index": {
|
"index": {
|
||||||
@@ -589,7 +587,6 @@ exports[`transformConfigSpec transformConfigSpec(embassyPages) 1`] = `
|
|||||||
"disabled": false,
|
"disabled": false,
|
||||||
"immutable": false,
|
"immutable": false,
|
||||||
"name": "Folder Location",
|
"name": "Folder Location",
|
||||||
"required": false,
|
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"values": {
|
"values": {
|
||||||
"filebrowser": "filebrowser",
|
"filebrowser": "filebrowser",
|
||||||
@@ -644,7 +641,6 @@ exports[`transformConfigSpec transformConfigSpec(embassyPages) 1`] = `
|
|||||||
"disabled": false,
|
"disabled": false,
|
||||||
"immutable": false,
|
"immutable": false,
|
||||||
"name": "Type",
|
"name": "Type",
|
||||||
"required": true,
|
|
||||||
"type": "union",
|
"type": "union",
|
||||||
"variants": {
|
"variants": {
|
||||||
"redirect": {
|
"redirect": {
|
||||||
@@ -705,7 +701,6 @@ exports[`transformConfigSpec transformConfigSpec(embassyPages) 1`] = `
|
|||||||
"disabled": false,
|
"disabled": false,
|
||||||
"immutable": false,
|
"immutable": false,
|
||||||
"name": "Folder Location",
|
"name": "Folder Location",
|
||||||
"required": false,
|
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"values": {
|
"values": {
|
||||||
"filebrowser": "filebrowser",
|
"filebrowser": "filebrowser",
|
||||||
@@ -758,7 +753,6 @@ exports[`transformConfigSpec transformConfigSpec(nostr2) 1`] = `
|
|||||||
"disabled": false,
|
"disabled": false,
|
||||||
"immutable": false,
|
"immutable": false,
|
||||||
"name": "Relay Type",
|
"name": "Relay Type",
|
||||||
"required": true,
|
|
||||||
"type": "union",
|
"type": "union",
|
||||||
"variants": {
|
"variants": {
|
||||||
"private": {
|
"private": {
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export function transformConfigSpec(oldSpec: OldConfigSpec): IST.InputSpec {
|
|||||||
}),
|
}),
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
required: false,
|
|
||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: false,
|
immutable: false,
|
||||||
}
|
}
|
||||||
@@ -127,7 +126,6 @@ export function transformConfigSpec(oldSpec: OldConfigSpec): IST.InputSpec {
|
|||||||
{} as Record<string, { name: string; spec: IST.InputSpec }>,
|
{} as Record<string, { name: string; spec: IST.InputSpec }>,
|
||||||
),
|
),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
required: true,
|
|
||||||
default: oldVal.default,
|
default: oldVal.default,
|
||||||
immutable: false,
|
immutable: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as T from "../types"
|
import * as T from "../types"
|
||||||
import * as IST from "../actions/input/inputSpecTypes"
|
import * as IST from "../actions/input/inputSpecTypes"
|
||||||
import { Action } from "./setupActions"
|
import { Action } from "./setupActions"
|
||||||
|
import { ExtractInputSpecType } from "./input/builder/inputSpec"
|
||||||
|
|
||||||
export type RunActionInput<Input> =
|
export type RunActionInput<Input> =
|
||||||
| Input
|
| Input
|
||||||
@@ -44,36 +45,32 @@ export const runAction = async <
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type GetActionInputType<
|
type GetActionInputType<A extends Action<T.ActionId, any, any>> =
|
||||||
A extends Action<T.ActionId, any, any, Record<string, unknown>>,
|
A extends Action<T.ActionId, any, infer I> ? ExtractInputSpecType<I> : never
|
||||||
> = A extends Action<T.ActionId, any, any, infer I> ? I : never
|
|
||||||
|
|
||||||
type ActionRequestBase = {
|
type ActionRequestBase = {
|
||||||
reason?: string
|
reason?: string
|
||||||
replayId?: string
|
replayId?: string
|
||||||
}
|
}
|
||||||
type ActionRequestInput<
|
type ActionRequestInput<T extends Action<T.ActionId, any, any>> = {
|
||||||
T extends Action<T.ActionId, any, any, Record<string, unknown>>,
|
|
||||||
> = {
|
|
||||||
kind: "partial"
|
kind: "partial"
|
||||||
value: Partial<GetActionInputType<T>>
|
value: Partial<GetActionInputType<T>>
|
||||||
}
|
}
|
||||||
export type ActionRequestOptions<
|
export type ActionRequestOptions<T extends Action<T.ActionId, any, any>> =
|
||||||
T extends Action<T.ActionId, any, any, Record<string, unknown>>,
|
ActionRequestBase &
|
||||||
> = ActionRequestBase &
|
(
|
||||||
(
|
| {
|
||||||
| {
|
when?: Exclude<
|
||||||
when?: Exclude<
|
T.ActionRequestTrigger,
|
||||||
T.ActionRequestTrigger,
|
{ condition: "input-not-matches" }
|
||||||
{ condition: "input-not-matches" }
|
>
|
||||||
>
|
input?: ActionRequestInput<T>
|
||||||
input?: ActionRequestInput<T>
|
}
|
||||||
}
|
| {
|
||||||
| {
|
when: T.ActionRequestTrigger & { condition: "input-not-matches" }
|
||||||
when: T.ActionRequestTrigger & { condition: "input-not-matches" }
|
input: ActionRequestInput<T>
|
||||||
input: ActionRequestInput<T>
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
const _validate: T.ActionRequest = {} as ActionRequestOptions<any> & {
|
const _validate: T.ActionRequest = {} as ActionRequestOptions<any> & {
|
||||||
actionId: string
|
actionId: string
|
||||||
@@ -81,9 +78,7 @@ const _validate: T.ActionRequest = {} as ActionRequestOptions<any> & {
|
|||||||
severity: T.ActionSeverity
|
severity: T.ActionSeverity
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestAction = <
|
export const requestAction = <T extends Action<T.ActionId, any, any>>(options: {
|
||||||
T extends Action<T.ActionId, any, any, Record<string, unknown>>,
|
|
||||||
>(options: {
|
|
||||||
effects: T.Effects
|
effects: T.Effects
|
||||||
packageId: T.PackageId
|
packageId: T.PackageId
|
||||||
action: T
|
action: T
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ValueSpec } from "../inputSpecTypes"
|
import { ValueSpec } from "../inputSpecTypes"
|
||||||
import { Value } from "./value"
|
import { PartialValue, Value } from "./value"
|
||||||
import { _ } from "../../../util"
|
import { _ } from "../../../util"
|
||||||
import { Effects } from "../../../Effects"
|
import { Effects } from "../../../Effects"
|
||||||
import { Parser, object } from "ts-matches"
|
import { Parser, object } from "ts-matches"
|
||||||
@@ -16,6 +16,15 @@ export type ExtractInputSpecType<A extends Record<string, any> | InputSpec<Recor
|
|||||||
A extends InputSpec<infer B, any> | InputSpec<infer B, never> ? B :
|
A extends InputSpec<infer B, any> | InputSpec<infer B, never> ? B :
|
||||||
A
|
A
|
||||||
|
|
||||||
|
export type ExtractPartialInputSpecType<
|
||||||
|
A extends
|
||||||
|
| Record<string, any>
|
||||||
|
| InputSpec<Record<string, any>, any>
|
||||||
|
| InputSpec<Record<string, any>, never>,
|
||||||
|
> = A extends InputSpec<infer B, any> | InputSpec<infer B, never>
|
||||||
|
? PartialValue<B>
|
||||||
|
: PartialValue<A>
|
||||||
|
|
||||||
export type InputSpecOf<A extends Record<string, any>, Store = never> = {
|
export type InputSpecOf<A extends Record<string, any>, Store = never> = {
|
||||||
[K in keyof A]: Value<A[K], Store>
|
[K in keyof A]: Value<A[K], Store>
|
||||||
}
|
}
|
||||||
@@ -84,6 +93,8 @@ export class InputSpec<Type extends Record<string, any>, Store = never> {
|
|||||||
},
|
},
|
||||||
public validator: Parser<unknown, Type>,
|
public validator: Parser<unknown, Type>,
|
||||||
) {}
|
) {}
|
||||||
|
_TYPE: Type = null as any as Type
|
||||||
|
_PARTIAL: PartialValue<Type> = null as any as PartialValue<Type>
|
||||||
async build(options: LazyBuildOptions<Store>) {
|
async build(options: LazyBuildOptions<Store>) {
|
||||||
const answer = {} as {
|
const answer = {} as {
|
||||||
[K in keyof Type]: ValueSpec
|
[K in keyof Type]: ValueSpec
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { InputSpec, LazyBuild } from "./inputSpec"
|
import { InputSpec, LazyBuild } from "./inputSpec"
|
||||||
import { List } from "./list"
|
import { List } from "./list"
|
||||||
import { Variants } from "./variants"
|
import { PartialUnionRes, UnionRes, Variants } from "./variants"
|
||||||
import {
|
import {
|
||||||
FilePath,
|
FilePath,
|
||||||
Pattern,
|
Pattern,
|
||||||
@@ -26,37 +26,14 @@ import {
|
|||||||
string,
|
string,
|
||||||
unknown,
|
unknown,
|
||||||
} from "ts-matches"
|
} from "ts-matches"
|
||||||
|
import { DeepPartial } from "../../../types"
|
||||||
|
|
||||||
export type RequiredDefault<A> =
|
type AsRequired<T, Required extends boolean> = Required extends true
|
||||||
| false
|
? T
|
||||||
| {
|
: T | null | undefined
|
||||||
default: A | null
|
|
||||||
}
|
|
||||||
|
|
||||||
function requiredLikeToAbove<Input extends RequiredDefault<A>, A>(
|
|
||||||
requiredLike: Input,
|
|
||||||
) {
|
|
||||||
// prettier-ignore
|
|
||||||
return {
|
|
||||||
required: (typeof requiredLike === 'object' ? true : requiredLike) as (
|
|
||||||
Input extends { default: unknown} ? true:
|
|
||||||
Input extends true ? true :
|
|
||||||
false
|
|
||||||
),
|
|
||||||
default:(typeof requiredLike === 'object' ? requiredLike.default : null) as (
|
|
||||||
Input extends { default: infer Default } ? Default :
|
|
||||||
null
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
type AsRequired<Type, MaybeRequiredType> = MaybeRequiredType extends
|
|
||||||
| { default: unknown }
|
|
||||||
| never
|
|
||||||
? Type
|
|
||||||
: Type | null | undefined
|
|
||||||
|
|
||||||
const testForAsRequiredParser = once(
|
const testForAsRequiredParser = once(
|
||||||
() => object({ required: object({ default: unknown }) }).test,
|
() => object({ required: literal(true) }).test,
|
||||||
)
|
)
|
||||||
function asRequiredParser<
|
function asRequiredParser<
|
||||||
Type,
|
Type,
|
||||||
@@ -69,6 +46,13 @@ function asRequiredParser<
|
|||||||
return parser.optional() as any
|
return parser.optional() as any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PartialValue<T> =
|
||||||
|
T extends UnionRes<infer A, infer B>
|
||||||
|
? PartialUnionRes<A, B>
|
||||||
|
: T extends {}
|
||||||
|
? { [P in keyof T]?: PartialValue<T[P]> }
|
||||||
|
: T
|
||||||
|
|
||||||
export class Value<Type, Store> {
|
export class Value<Type, Store> {
|
||||||
protected constructor(
|
protected constructor(
|
||||||
public build: LazyBuild<Store, ValueSpec>,
|
public build: LazyBuild<Store, ValueSpec>,
|
||||||
@@ -122,19 +106,19 @@ export class Value<Type, Store> {
|
|||||||
boolean,
|
boolean,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
static text<Required extends RequiredDefault<DefaultString>>(a: {
|
static text<Required extends boolean>(a: {
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* provide a default value.
|
||||||
* @type { false | { default: string | RandomString | null } }
|
* @type { string | RandomString | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: 'World'
|
||||||
* @example required: { default: 'World' }
|
* @example default: { charset: 'abcdefg', len: 16 }
|
||||||
* @example required: { default: { charset: 'abcdefg', len: 16 } }
|
|
||||||
*/
|
*/
|
||||||
|
default: string | RandomString | null
|
||||||
required: Required
|
required: Required
|
||||||
/**
|
/**
|
||||||
* @description Mask (aka camouflage) text input with dots: ● ● ●
|
* @description Mask (aka camouflage) text input with dots: ● ● ●
|
||||||
@@ -188,7 +172,6 @@ export class Value<Type, Store> {
|
|||||||
immutable: a.immutable ?? false,
|
immutable: a.immutable ?? false,
|
||||||
generate: a.generate ?? null,
|
generate: a.generate ?? null,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}),
|
}),
|
||||||
asRequiredParser(string, a),
|
asRequiredParser(string, a),
|
||||||
)
|
)
|
||||||
@@ -200,7 +183,8 @@ export class Value<Type, Store> {
|
|||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
required: RequiredDefault<DefaultString>
|
default: DefaultString | null
|
||||||
|
required: boolean
|
||||||
masked?: boolean
|
masked?: boolean
|
||||||
placeholder?: string | null
|
placeholder?: string | null
|
||||||
minLength?: number | null
|
minLength?: number | null
|
||||||
@@ -228,19 +212,16 @@ export class Value<Type, Store> {
|
|||||||
immutable: false,
|
immutable: false,
|
||||||
generate: a.generate ?? null,
|
generate: a.generate ?? null,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}
|
}
|
||||||
}, string.optional())
|
}, string.optional())
|
||||||
}
|
}
|
||||||
static textarea(a: {
|
static textarea<Required extends boolean>(a: {
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
default: string | null
|
||||||
* @description Unlike other "required" fields, for textarea this is a simple boolean.
|
required: Required
|
||||||
*/
|
|
||||||
required: boolean
|
|
||||||
minLength?: number | null
|
minLength?: number | null
|
||||||
maxLength?: number | null
|
maxLength?: number | null
|
||||||
placeholder?: string | null
|
placeholder?: string | null
|
||||||
@@ -250,20 +231,23 @@ export class Value<Type, Store> {
|
|||||||
*/
|
*/
|
||||||
immutable?: boolean
|
immutable?: boolean
|
||||||
}) {
|
}) {
|
||||||
return new Value<string, never>(async () => {
|
return new Value<AsRequired<string, Required>, never>(
|
||||||
const built: ValueSpecTextarea = {
|
async () => {
|
||||||
description: null,
|
const built: ValueSpecTextarea = {
|
||||||
warning: null,
|
description: null,
|
||||||
minLength: null,
|
warning: null,
|
||||||
maxLength: null,
|
minLength: null,
|
||||||
placeholder: null,
|
maxLength: null,
|
||||||
type: "textarea" as const,
|
placeholder: null,
|
||||||
disabled: false,
|
type: "textarea" as const,
|
||||||
immutable: a.immutable ?? false,
|
disabled: false,
|
||||||
...a,
|
immutable: a.immutable ?? false,
|
||||||
}
|
...a,
|
||||||
return built
|
}
|
||||||
}, string)
|
return built
|
||||||
|
},
|
||||||
|
asRequiredParser(string, a),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
static dynamicTextarea<Store = never>(
|
static dynamicTextarea<Store = never>(
|
||||||
getA: LazyBuild<
|
getA: LazyBuild<
|
||||||
@@ -272,6 +256,7 @@ export class Value<Type, Store> {
|
|||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
|
default: string | null
|
||||||
required: boolean
|
required: boolean
|
||||||
minLength?: number | null
|
minLength?: number | null
|
||||||
maxLength?: number | null
|
maxLength?: number | null
|
||||||
@@ -280,7 +265,7 @@ export class Value<Type, Store> {
|
|||||||
}
|
}
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
return new Value<string, Store>(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,
|
||||||
@@ -293,20 +278,20 @@ export class Value<Type, Store> {
|
|||||||
immutable: false,
|
immutable: false,
|
||||||
...a,
|
...a,
|
||||||
}
|
}
|
||||||
}, string)
|
}, string.optional())
|
||||||
}
|
}
|
||||||
static number<Required extends RequiredDefault<number>>(a: {
|
static number<Required extends boolean>(a: {
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* @description optionally provide a default value.
|
||||||
* @type { false | { default: number | null } }
|
* @type { default: number | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: 7
|
||||||
* @example required: { default: 7 }
|
|
||||||
*/
|
*/
|
||||||
|
default: number | null
|
||||||
required: Required
|
required: Required
|
||||||
min?: number | null
|
min?: number | null
|
||||||
max?: number | null
|
max?: number | null
|
||||||
@@ -343,7 +328,6 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: a.immutable ?? false,
|
immutable: a.immutable ?? false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}),
|
}),
|
||||||
asRequiredParser(number, a),
|
asRequiredParser(number, a),
|
||||||
)
|
)
|
||||||
@@ -355,7 +339,8 @@ export class Value<Type, Store> {
|
|||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
required: RequiredDefault<number>
|
default: number | null
|
||||||
|
required: boolean
|
||||||
min?: number | null
|
min?: number | null
|
||||||
max?: number | null
|
max?: number | null
|
||||||
step?: number | null
|
step?: number | null
|
||||||
@@ -380,22 +365,21 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: false,
|
immutable: false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}
|
}
|
||||||
}, number.optional())
|
}, number.optional())
|
||||||
}
|
}
|
||||||
static color<Required extends RequiredDefault<string>>(a: {
|
static color<Required extends boolean>(a: {
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* @description optionally provide a default value.
|
||||||
* @type { false | { default: string | null } }
|
* @type { default: string | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: 'ffffff'
|
||||||
* @example required: { default: 'ffffff' }
|
|
||||||
*/
|
*/
|
||||||
|
default: string | null
|
||||||
required: Required
|
required: Required
|
||||||
/**
|
/**
|
||||||
* @description Once set, the value can never be changed.
|
* @description Once set, the value can never be changed.
|
||||||
@@ -411,9 +395,7 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: a.immutable ?? false,
|
immutable: a.immutable ?? false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
asRequiredParser(string, a),
|
asRequiredParser(string, a),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -425,7 +407,8 @@ export class Value<Type, Store> {
|
|||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
required: RequiredDefault<string>
|
default: string | null
|
||||||
|
required: boolean
|
||||||
disabled?: false | string
|
disabled?: false | string
|
||||||
}
|
}
|
||||||
>,
|
>,
|
||||||
@@ -439,22 +422,21 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: false,
|
immutable: false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}
|
}
|
||||||
}, string.optional())
|
}, string.optional())
|
||||||
}
|
}
|
||||||
static datetime<Required extends RequiredDefault<string>>(a: {
|
static datetime<Required extends boolean>(a: {
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* @description optionally provide a default value.
|
||||||
* @type { false | { default: string | null } }
|
* @type { default: string | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: '1985-12-16 18:00:00.000'
|
||||||
* @example required: { default: '1985-12-16 18:00:00.000' }
|
|
||||||
*/
|
*/
|
||||||
|
default: string | null
|
||||||
required: Required
|
required: Required
|
||||||
/**
|
/**
|
||||||
* @description Informs the browser how to behave and which date/time component to display.
|
* @description Informs the browser how to behave and which date/time component to display.
|
||||||
@@ -481,7 +463,6 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: a.immutable ?? false,
|
immutable: a.immutable ?? false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}),
|
}),
|
||||||
asRequiredParser(string, a),
|
asRequiredParser(string, a),
|
||||||
)
|
)
|
||||||
@@ -493,7 +474,8 @@ export class Value<Type, Store> {
|
|||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
required: RequiredDefault<string>
|
default: string | null
|
||||||
|
required: boolean
|
||||||
inputmode?: ValueSpecDatetime["inputmode"]
|
inputmode?: ValueSpecDatetime["inputmode"]
|
||||||
min?: string | null
|
min?: string | null
|
||||||
max?: string | null
|
max?: string | null
|
||||||
@@ -513,26 +495,21 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: false,
|
immutable: false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}
|
}
|
||||||
}, string.optional())
|
}, string.optional())
|
||||||
}
|
}
|
||||||
static select<
|
static select<Values extends Record<string, string>>(a: {
|
||||||
Required extends RequiredDefault<string>,
|
|
||||||
Values extends Record<string, string>,
|
|
||||||
>(a: {
|
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value from the list of values.
|
* @description Determines if the field is required. If so, optionally provide a default value from the list of values.
|
||||||
* @type { false | { default: string | null } }
|
* @type { (keyof Values & string) | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: 'radio1'
|
||||||
* @example required: { default: 'radio1' }
|
|
||||||
*/
|
*/
|
||||||
required: Required
|
default: keyof Values & string
|
||||||
/**
|
/**
|
||||||
* @description A mapping of unique radio options to their human readable display format.
|
* @description A mapping of unique radio options to their human readable display format.
|
||||||
* @example
|
* @example
|
||||||
@@ -551,7 +528,7 @@ export class Value<Type, Store> {
|
|||||||
*/
|
*/
|
||||||
immutable?: boolean
|
immutable?: boolean
|
||||||
}) {
|
}) {
|
||||||
return new Value<AsRequired<keyof Values, Required>, never>(
|
return new Value<keyof Values & string, never>(
|
||||||
() => ({
|
() => ({
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
@@ -559,16 +536,10 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: a.immutable ?? false,
|
immutable: a.immutable ?? false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}),
|
}),
|
||||||
asRequiredParser(
|
anyOf(
|
||||||
anyOf(
|
...Object.keys(a.values).map((x: keyof Values & string) => literal(x)),
|
||||||
...Object.keys(a.values).map((x: keyof Values & string) =>
|
),
|
||||||
literal(x),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
a,
|
|
||||||
) as any,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
static dynamicSelect<Store = never>(
|
static dynamicSelect<Store = never>(
|
||||||
@@ -578,13 +549,13 @@ export class Value<Type, Store> {
|
|||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
required: RequiredDefault<string>
|
default: string
|
||||||
values: Record<string, string>
|
values: Record<string, string>
|
||||||
disabled?: false | string | string[]
|
disabled?: false | string | string[]
|
||||||
}
|
}
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
return new Value<string | null | undefined, Store>(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,
|
||||||
@@ -593,9 +564,8 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
immutable: false,
|
immutable: false,
|
||||||
...a,
|
...a,
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
}
|
}
|
||||||
}, string.optional())
|
}, string)
|
||||||
}
|
}
|
||||||
static multiselect<Values extends Record<string, string>>(a: {
|
static multiselect<Values extends Record<string, string>>(a: {
|
||||||
name: string
|
name: string
|
||||||
@@ -605,7 +575,7 @@ export class Value<Type, Store> {
|
|||||||
/**
|
/**
|
||||||
* @description A simple list of which options should be checked by default.
|
* @description A simple list of which options should be checked by default.
|
||||||
*/
|
*/
|
||||||
default: string[]
|
default: (keyof Values & string)[]
|
||||||
/**
|
/**
|
||||||
* @description A mapping of checkbox options to their human readable display format.
|
* @description A mapping of checkbox options to their human readable display format.
|
||||||
* @example
|
* @example
|
||||||
@@ -689,11 +659,11 @@ export class Value<Type, Store> {
|
|||||||
}
|
}
|
||||||
}, spec.validator)
|
}, spec.validator)
|
||||||
}
|
}
|
||||||
// static file<Store>(a: {
|
// static file<Store, Required extends boolean>(a: {
|
||||||
// name: string
|
// name: string
|
||||||
// description?: string | null
|
// description?: string | null
|
||||||
// extensions: string[]
|
// extensions: string[]
|
||||||
// required: boolean
|
// required: Required
|
||||||
// }) {
|
// }) {
|
||||||
// const buildValue = {
|
// const buildValue = {
|
||||||
// type: "file" as const,
|
// type: "file" as const,
|
||||||
@@ -701,14 +671,14 @@ export class Value<Type, Store> {
|
|||||||
// warning: null,
|
// warning: null,
|
||||||
// ...a,
|
// ...a,
|
||||||
// }
|
// }
|
||||||
// return new Value<FilePath, Store>(
|
// return new Value<AsRequired<FilePath, Required>, Store>(
|
||||||
// () => ({
|
// () => ({
|
||||||
// ...buildValue,
|
// ...buildValue,
|
||||||
// }),
|
// }),
|
||||||
// asRequiredParser(object({ filePath: string }), a),
|
// asRequiredParser(object({ filePath: string }), a),
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
// static dynamicFile<Required extends boolean, Store>(
|
// static dynamicFile<Store>(
|
||||||
// a: LazyBuild<
|
// a: LazyBuild<
|
||||||
// Store,
|
// Store,
|
||||||
// {
|
// {
|
||||||
@@ -716,43 +686,49 @@ export class Value<Type, Store> {
|
|||||||
// description?: string | null
|
// description?: string | null
|
||||||
// warning?: string | null
|
// warning?: string | null
|
||||||
// extensions: string[]
|
// extensions: string[]
|
||||||
// required: Required
|
// required: boolean
|
||||||
// }
|
// }
|
||||||
// >,
|
// >,
|
||||||
// ) {
|
// ) {
|
||||||
// return new Value<string | null | undefined, Store>(
|
// return new Value<FilePath | null | undefined, Store>(
|
||||||
// async (options) => ({
|
// async (options) => ({
|
||||||
// type: "file" as const,
|
// type: "file" as const,
|
||||||
// description: null,
|
// description: null,
|
||||||
// warning: null,
|
// warning: null,
|
||||||
// ...(await a(options)),
|
// ...(await a(options)),
|
||||||
// }),
|
// }),
|
||||||
// string.optional(),
|
// object({ filePath: string }).optional(),
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
static union<Required extends RequiredDefault<string>, Type, Store>(
|
static union<
|
||||||
|
VariantValues extends {
|
||||||
|
[K in string]: {
|
||||||
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Store,
|
||||||
|
>(
|
||||||
a: {
|
a: {
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value from the list of variants.
|
* @description Provide a default value from the list of variants.
|
||||||
* @type { false | { default: string | null } }
|
* @type { string }
|
||||||
* @example required: false
|
* @example default: 'variant1'
|
||||||
* @example required: { default: null }
|
|
||||||
* @example required: { default: 'variant1' }
|
|
||||||
*/
|
*/
|
||||||
required: Required
|
default: keyof VariantValues & string
|
||||||
/**
|
/**
|
||||||
* @description Once set, the value can never be changed.
|
* @description Once set, the value can never be changed.
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
immutable?: boolean
|
immutable?: boolean
|
||||||
},
|
},
|
||||||
aVariants: Variants<Type, Store>,
|
aVariants: Variants<VariantValues, Store>,
|
||||||
) {
|
) {
|
||||||
return new Value<AsRequired<Type, Required>, Store>(
|
return new Value<typeof aVariants.validator._TYPE, Store>(
|
||||||
async (options) => ({
|
async (options) => ({
|
||||||
type: "union" as const,
|
type: "union" as const,
|
||||||
description: null,
|
description: null,
|
||||||
@@ -760,44 +736,50 @@ export class Value<Type, Store> {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
...a,
|
...a,
|
||||||
variants: await aVariants.build(options as any),
|
variants: await aVariants.build(options as any),
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
immutable: a.immutable ?? false,
|
immutable: a.immutable ?? false,
|
||||||
}),
|
}),
|
||||||
asRequiredParser(aVariants.validator, a),
|
aVariants.validator,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
static filteredUnion<
|
static filteredUnion<
|
||||||
Required extends RequiredDefault<string>,
|
VariantValues extends {
|
||||||
Type extends Record<string, any>,
|
[K in string]: {
|
||||||
Store = never,
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Store,
|
||||||
>(
|
>(
|
||||||
getDisabledFn: LazyBuild<Store, string[] | false | string>,
|
getDisabledFn: LazyBuild<Store, string[] | false | string>,
|
||||||
a: {
|
a: {
|
||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: 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>,
|
||||||
) {
|
) {
|
||||||
return new Value<AsRequired<Type, Required>, Store>(
|
return new Value<typeof aVariants.validator._TYPE, Store>(
|
||||||
async (options) => ({
|
async (options) => ({
|
||||||
type: "union" as const,
|
type: "union" as const,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
...a,
|
...a,
|
||||||
variants: await aVariants.build(options as any),
|
variants: await aVariants.build(options as any),
|
||||||
...requiredLikeToAbove(a.required),
|
|
||||||
disabled: (await getDisabledFn(options)) || false,
|
disabled: (await getDisabledFn(options)) || false,
|
||||||
immutable: false,
|
immutable: false,
|
||||||
}),
|
}),
|
||||||
asRequiredParser(aVariants.validator, a),
|
aVariants.validator,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
static dynamicUnion<
|
static dynamicUnion<
|
||||||
Required extends RequiredDefault<string>,
|
VariantValues extends {
|
||||||
Type extends Record<string, any>,
|
[K in string]: {
|
||||||
Store = never,
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Store,
|
||||||
>(
|
>(
|
||||||
getA: LazyBuild<
|
getA: LazyBuild<
|
||||||
Store,
|
Store,
|
||||||
@@ -805,24 +787,26 @@ export class Value<Type, Store> {
|
|||||||
name: string
|
name: string
|
||||||
description?: string | null
|
description?: string | null
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
required: Required
|
default: keyof VariantValues & string
|
||||||
disabled: string[] | false | string
|
disabled: string[] | false | string
|
||||||
}
|
}
|
||||||
>,
|
>,
|
||||||
aVariants: Variants<Type, Store> | Variants<Type, never>,
|
aVariants: Variants<VariantValues, Store> | Variants<VariantValues, never>,
|
||||||
) {
|
) {
|
||||||
return new Value<Type | null | undefined, Store>(async (options) => {
|
return new Value<typeof aVariants.validator._TYPE, Store>(
|
||||||
const newValues = await getA(options)
|
async (options) => {
|
||||||
return {
|
const newValues = await getA(options)
|
||||||
type: "union" as const,
|
return {
|
||||||
description: null,
|
type: "union" as const,
|
||||||
warning: null,
|
description: null,
|
||||||
...newValues,
|
warning: null,
|
||||||
variants: await aVariants.build(options as any),
|
...newValues,
|
||||||
...requiredLikeToAbove(newValues.required),
|
variants: await aVariants.build(options as any),
|
||||||
immutable: false,
|
immutable: false,
|
||||||
}
|
}
|
||||||
}, aVariants.validator.optional())
|
},
|
||||||
|
aVariants.validator,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static list<Type, Store>(a: List<Type, Store>) {
|
static list<Type, Store>(a: List<Type, Store>) {
|
||||||
|
|||||||
@@ -1,6 +1,54 @@
|
|||||||
|
import { DeepPartial } from "../../../types"
|
||||||
import { ValueSpec, ValueSpecUnion } from "../inputSpecTypes"
|
import { ValueSpec, ValueSpecUnion } from "../inputSpecTypes"
|
||||||
import { LazyBuild, InputSpec } from "./inputSpec"
|
import {
|
||||||
import { Parser, anyOf, literals, object } from "ts-matches"
|
LazyBuild,
|
||||||
|
InputSpec,
|
||||||
|
ExtractInputSpecType,
|
||||||
|
ExtractPartialInputSpecType,
|
||||||
|
} from "./inputSpec"
|
||||||
|
import { Parser, anyOf, literal, object } from "ts-matches"
|
||||||
|
|
||||||
|
export type UnionRes<
|
||||||
|
Store,
|
||||||
|
VariantValues extends {
|
||||||
|
[K in string]: {
|
||||||
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
K extends keyof VariantValues & string = keyof VariantValues & string,
|
||||||
|
> = {
|
||||||
|
[key in keyof VariantValues]: {
|
||||||
|
selection: key
|
||||||
|
value: ExtractInputSpecType<VariantValues[key]["spec"]>
|
||||||
|
other?: {
|
||||||
|
[key2 in Exclude<keyof VariantValues & string, key>]?: DeepPartial<
|
||||||
|
ExtractInputSpecType<VariantValues[key2]["spec"]>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}[K]
|
||||||
|
|
||||||
|
export type PartialUnionRes<
|
||||||
|
Store,
|
||||||
|
VariantValues extends {
|
||||||
|
[K in string]: {
|
||||||
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
K extends keyof VariantValues & string = keyof VariantValues & string,
|
||||||
|
> = {
|
||||||
|
[key in keyof VariantValues]: {
|
||||||
|
selection?: key
|
||||||
|
value?: ExtractPartialInputSpecType<VariantValues[key]["spec"]>
|
||||||
|
other?: {
|
||||||
|
[key2 in Exclude<keyof VariantValues & string, key>]?: DeepPartial<
|
||||||
|
ExtractInputSpecType<VariantValues[key2]["spec"]>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}[K]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in the the Value.select { @link './value.ts' }
|
* Used in the the Value.select { @link './value.ts' }
|
||||||
@@ -44,18 +92,24 @@ export const pruning = Value.union(
|
|||||||
description:
|
description:
|
||||||
'- Disabled: Disable pruning\n- Automatic: Limit blockchain size on disk to a certain number of megabytes\n- Manual: Prune blockchain with the "pruneblockchain" RPC\n',
|
'- Disabled: Disable pruning\n- Automatic: Limit blockchain size on disk to a certain number of megabytes\n- Manual: Prune blockchain with the "pruneblockchain" RPC\n',
|
||||||
warning: null,
|
warning: null,
|
||||||
required: true,
|
|
||||||
default: "disabled",
|
default: "disabled",
|
||||||
},
|
},
|
||||||
pruningSettingsVariants
|
pruningSettingsVariants
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
export class Variants<Type, Store> {
|
export class Variants<
|
||||||
static text: any
|
VariantValues extends {
|
||||||
|
[K in string]: {
|
||||||
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Store,
|
||||||
|
> {
|
||||||
private constructor(
|
private constructor(
|
||||||
public build: LazyBuild<Store, ValueSpecUnion["variants"]>,
|
public build: LazyBuild<Store, ValueSpecUnion["variants"]>,
|
||||||
public validator: Parser<unknown, Type>,
|
public validator: Parser<unknown, UnionRes<Store, VariantValues>>,
|
||||||
) {}
|
) {}
|
||||||
static of<
|
static of<
|
||||||
VariantValues extends {
|
VariantValues extends {
|
||||||
@@ -67,26 +121,15 @@ export class Variants<Type, Store> {
|
|||||||
Store = never,
|
Store = never,
|
||||||
>(a: VariantValues) {
|
>(a: VariantValues) {
|
||||||
const validator = anyOf(
|
const validator = anyOf(
|
||||||
...Object.entries(a).map(([name, { spec }]) =>
|
...Object.entries(a).map(([id, { spec }]) =>
|
||||||
object({
|
object({
|
||||||
selection: literals(name),
|
selection: literal(id),
|
||||||
value: spec.validator,
|
value: spec.validator,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
) as Parser<unknown, any>
|
) as Parser<unknown, any>
|
||||||
|
|
||||||
return new Variants<
|
return new Variants<VariantValues, Store>(async (options) => {
|
||||||
{
|
|
||||||
[K in keyof VariantValues]: {
|
|
||||||
selection: K
|
|
||||||
// prettier-ignore
|
|
||||||
value:
|
|
||||||
VariantValues[K]["spec"] extends (InputSpec<infer B, Store> | InputSpec<infer B, never>) ? B :
|
|
||||||
never
|
|
||||||
}
|
|
||||||
}[keyof VariantValues],
|
|
||||||
Store
|
|
||||||
>(async (options) => {
|
|
||||||
const variants = {} as {
|
const variants = {} as {
|
||||||
[K in keyof VariantValues]: {
|
[K in keyof VariantValues]: {
|
||||||
name: string
|
name: string
|
||||||
@@ -118,6 +161,6 @@ export class Variants<Type, Store> {
|
|||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
withStore<NewStore extends Store extends never ? any : Store>() {
|
withStore<NewStore extends Store extends never ? any : Store>() {
|
||||||
return this as any as Variants<Type, NewStore>
|
return this as any as Variants<VariantValues, NewStore>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,35 +10,34 @@ import { Variants } from "./builder/variants"
|
|||||||
export const customSmtp = InputSpec.of<InputSpecOf<SmtpValue>, never>({
|
export const customSmtp = InputSpec.of<InputSpecOf<SmtpValue>, never>({
|
||||||
server: Value.text({
|
server: Value.text({
|
||||||
name: "SMTP Server",
|
name: "SMTP Server",
|
||||||
required: {
|
required: true,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
port: Value.number({
|
port: Value.number({
|
||||||
name: "Port",
|
name: "Port",
|
||||||
required: { default: 587 },
|
required: true,
|
||||||
|
default: 587,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 65535,
|
max: 65535,
|
||||||
integer: true,
|
integer: true,
|
||||||
}),
|
}),
|
||||||
from: Value.text({
|
from: Value.text({
|
||||||
name: "From Address",
|
name: "From Address",
|
||||||
required: {
|
required: true,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
placeholder: "<name>test@example.com",
|
placeholder: "<name>test@example.com",
|
||||||
inputmode: "email",
|
inputmode: "email",
|
||||||
patterns: [Patterns.email],
|
patterns: [Patterns.email],
|
||||||
}),
|
}),
|
||||||
login: Value.text({
|
login: Value.text({
|
||||||
name: "Login",
|
name: "Login",
|
||||||
required: {
|
required: true,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
password: Value.text({
|
password: Value.text({
|
||||||
name: "Password",
|
name: "Password",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
masked: true,
|
masked: true,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
@@ -54,7 +53,7 @@ export const smtpInputSpec = Value.filteredUnion(
|
|||||||
{
|
{
|
||||||
name: "SMTP",
|
name: "SMTP",
|
||||||
description: "Optionally provide an SMTP server for sending emails",
|
description: "Optionally provide an SMTP server for sending emails",
|
||||||
required: { default: "disabled" },
|
default: "disabled",
|
||||||
},
|
},
|
||||||
Variants.of({
|
Variants.of({
|
||||||
disabled: { name: "Disabled", spec: InputSpec.of({}) },
|
disabled: { name: "Disabled", spec: InputSpec.of({}) },
|
||||||
@@ -66,6 +65,7 @@ export const smtpInputSpec = Value.filteredUnion(
|
|||||||
description:
|
description:
|
||||||
"A custom from address for this service. If not provided, the system from address will be used.",
|
"A custom from address for this service. If not provided, the system from address will be used.",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
placeholder: "<name>test@example.com",
|
placeholder: "<name>test@example.com",
|
||||||
inputmode: "email",
|
inputmode: "email",
|
||||||
patterns: [Patterns.email],
|
patterns: [Patterns.email],
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ export type ValueSpecSelect = {
|
|||||||
description: string | null
|
description: string | null
|
||||||
warning: string | null
|
warning: string | null
|
||||||
type: "select"
|
type: "select"
|
||||||
required: boolean
|
|
||||||
default: string | null
|
default: string | null
|
||||||
disabled: false | string | string[]
|
disabled: false | string | string[]
|
||||||
immutable: boolean
|
immutable: boolean
|
||||||
@@ -158,7 +157,6 @@ export type ValueSpecUnion = {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
disabled: false | string | string[]
|
disabled: false | string | string[]
|
||||||
required: boolean
|
|
||||||
default: string | null
|
default: string | null
|
||||||
immutable: boolean
|
immutable: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { InputSpec } from "./input/builder"
|
import { InputSpec } from "./input/builder"
|
||||||
import { ExtractInputSpecType } from "./input/builder/inputSpec"
|
import {
|
||||||
|
ExtractInputSpecType,
|
||||||
|
ExtractPartialInputSpecType,
|
||||||
|
} from "./input/builder/inputSpec"
|
||||||
import * as T from "../types"
|
import * as T from "../types"
|
||||||
import { once } from "../util"
|
import { once } from "../util"
|
||||||
|
|
||||||
@@ -20,7 +23,10 @@ export type GetInput<
|
|||||||
> = (options: {
|
> = (options: {
|
||||||
effects: T.Effects
|
effects: T.Effects
|
||||||
}) => Promise<
|
}) => Promise<
|
||||||
null | void | undefined | (ExtractInputSpecType<A> & Record<string, any>)
|
| null
|
||||||
|
| void
|
||||||
|
| undefined
|
||||||
|
| (ExtractPartialInputSpecType<A> & Record<string, any>)
|
||||||
>
|
>
|
||||||
|
|
||||||
export type MaybeFn<T> = T | ((options: { effects: T.Effects }) => Promise<T>)
|
export type MaybeFn<T> = T | ((options: { effects: T.Effects }) => Promise<T>)
|
||||||
@@ -52,15 +58,13 @@ export class Action<
|
|||||||
| Record<string, any>
|
| Record<string, any>
|
||||||
| InputSpec<any, Store>
|
| InputSpec<any, Store>
|
||||||
| InputSpec<any, never>,
|
| InputSpec<any, never>,
|
||||||
Type extends
|
|
||||||
ExtractInputSpecType<InputSpecType> = ExtractInputSpecType<InputSpecType>,
|
|
||||||
> {
|
> {
|
||||||
private constructor(
|
private constructor(
|
||||||
readonly id: Id,
|
readonly id: Id,
|
||||||
private readonly metadataFn: MaybeFn<T.ActionMetadata>,
|
private readonly metadataFn: MaybeFn<T.ActionMetadata>,
|
||||||
private readonly inputSpec: InputSpecType,
|
private readonly inputSpec: InputSpecType,
|
||||||
private readonly getInputFn: GetInput<Type>,
|
private readonly getInputFn: GetInput<ExtractInputSpecType<InputSpecType>>,
|
||||||
private readonly runFn: Run<Type>,
|
private readonly runFn: Run<ExtractInputSpecType<InputSpecType>>,
|
||||||
) {}
|
) {}
|
||||||
static withInput<
|
static withInput<
|
||||||
Id extends T.ActionId,
|
Id extends T.ActionId,
|
||||||
@@ -69,15 +73,13 @@ export class Action<
|
|||||||
| Record<string, any>
|
| Record<string, any>
|
||||||
| InputSpec<any, Store>
|
| InputSpec<any, Store>
|
||||||
| InputSpec<any, never>,
|
| InputSpec<any, never>,
|
||||||
Type extends
|
|
||||||
ExtractInputSpecType<InputSpecType> = ExtractInputSpecType<InputSpecType>,
|
|
||||||
>(
|
>(
|
||||||
id: Id,
|
id: Id,
|
||||||
metadata: MaybeFn<Omit<T.ActionMetadata, "hasInput">>,
|
metadata: MaybeFn<Omit<T.ActionMetadata, "hasInput">>,
|
||||||
inputSpec: InputSpecType,
|
inputSpec: InputSpecType,
|
||||||
getInput: GetInput<Type>,
|
getInput: GetInput<ExtractInputSpecType<InputSpecType>>,
|
||||||
run: Run<Type>,
|
run: Run<ExtractInputSpecType<InputSpecType>>,
|
||||||
): Action<Id, Store, InputSpecType, Type> {
|
): Action<Id, Store, InputSpecType> {
|
||||||
return new Action(
|
return new Action(
|
||||||
id,
|
id,
|
||||||
mapMaybeFn(metadata, (m) => ({ ...m, hasInput: true })),
|
mapMaybeFn(metadata, (m) => ({ ...m, hasInput: true })),
|
||||||
@@ -90,7 +92,7 @@ export class Action<
|
|||||||
id: Id,
|
id: Id,
|
||||||
metadata: MaybeFn<Omit<T.ActionMetadata, "hasInput">>,
|
metadata: MaybeFn<Omit<T.ActionMetadata, "hasInput">>,
|
||||||
run: Run<{}>,
|
run: Run<{}>,
|
||||||
): Action<Id, Store, {}, {}> {
|
): Action<Id, Store, {}> {
|
||||||
return new Action(
|
return new Action(
|
||||||
id,
|
id,
|
||||||
mapMaybeFn(metadata, (m) => ({ ...m, hasInput: false })),
|
mapMaybeFn(metadata, (m) => ({ ...m, hasInput: false })),
|
||||||
@@ -114,7 +116,7 @@ export class Action<
|
|||||||
}
|
}
|
||||||
async run(options: {
|
async run(options: {
|
||||||
effects: T.Effects
|
effects: T.Effects
|
||||||
input: Type
|
input: ExtractInputSpecType<InputSpecType>
|
||||||
}): Promise<T.ActionResult | null> {
|
}): Promise<T.ActionResult | null> {
|
||||||
return (await this.runFn(options)) || null
|
return (await this.runFn(options)) || null
|
||||||
}
|
}
|
||||||
@@ -122,13 +124,13 @@ export class Action<
|
|||||||
|
|
||||||
export class Actions<
|
export class Actions<
|
||||||
Store,
|
Store,
|
||||||
AllActions extends Record<T.ActionId, Action<T.ActionId, Store, any, any>>,
|
AllActions extends Record<T.ActionId, Action<T.ActionId, Store, any>>,
|
||||||
> {
|
> {
|
||||||
private constructor(private readonly actions: AllActions) {}
|
private constructor(private readonly actions: AllActions) {}
|
||||||
static of<Store>(): Actions<Store, {}> {
|
static of<Store>(): Actions<Store, {}> {
|
||||||
return new Actions({})
|
return new Actions({})
|
||||||
}
|
}
|
||||||
addAction<A extends Action<T.ActionId, Store, any, any>>(
|
addAction<A extends Action<T.ActionId, Store, any>>(
|
||||||
action: A,
|
action: A,
|
||||||
): Actions<Store, AllActions & { [id in A["id"]]: A }> {
|
): Actions<Store, AllActions & { [id in A["id"]]: A }> {
|
||||||
return new Actions({ ...this.actions, [action.id]: action })
|
return new Actions({ ...this.actions, [action.id]: action })
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export namespace ExpectedExports {
|
|||||||
|
|
||||||
export type actions = Actions<
|
export type actions = Actions<
|
||||||
any,
|
any,
|
||||||
Record<ActionId, Action<ActionId, any, any, any>>
|
Record<ActionId, Action<ActionId, any, any>>
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
export type ABI = {
|
export type ABI = {
|
||||||
|
|||||||
9
sdk/base/package-lock.json
generated
9
sdk/base/package-lock.json
generated
@@ -14,7 +14,7 @@
|
|||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"ts-matches": "^5.5.1",
|
"ts-matches": "^6.0.0",
|
||||||
"yaml": "^2.2.2"
|
"yaml": "^2.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -3897,9 +3897,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/ts-matches": {
|
"node_modules/ts-matches": {
|
||||||
"version": "5.5.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.0.0.tgz",
|
||||||
"integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg=="
|
"integrity": "sha512-vR4hhz9bYMW30qIJUuLaeAWlsR54vse6ZI2riVhVLMBE6/vss43jwrOvbHheiyU7e26ssT/yWx69aJHD2REJSA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ts-morph": {
|
"node_modules/ts-morph": {
|
||||||
"version": "18.0.0",
|
"version": "18.0.0",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"ts-matches": "^5.5.1",
|
"ts-matches": "^6.0.0",
|
||||||
"yaml": "^2.2.2"
|
"yaml": "^2.2.2"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import { Value } from "../../base/lib/actions/input/builder/value"
|
||||||
RequiredDefault,
|
|
||||||
Value,
|
|
||||||
} from "../../base/lib/actions/input/builder/value"
|
|
||||||
import {
|
import {
|
||||||
InputSpec,
|
InputSpec,
|
||||||
ExtractInputSpecType,
|
ExtractInputSpecType,
|
||||||
@@ -141,9 +138,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
...startSdkEffectWrapper,
|
...startSdkEffectWrapper,
|
||||||
action: {
|
action: {
|
||||||
run: actions.runAction,
|
run: actions.runAction,
|
||||||
request: <
|
request: <T extends Action<T.ActionId, any, any>>(
|
||||||
T extends Action<T.ActionId, any, any, Record<string, unknown>>,
|
|
||||||
>(
|
|
||||||
effects: T.Effects,
|
effects: T.Effects,
|
||||||
packageId: T.PackageId,
|
packageId: T.PackageId,
|
||||||
action: T,
|
action: T,
|
||||||
@@ -157,9 +152,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
severity,
|
severity,
|
||||||
options: options,
|
options: options,
|
||||||
}),
|
}),
|
||||||
requestOwn: <
|
requestOwn: <T extends Action<T.ActionId, Store, any>>(
|
||||||
T extends Action<T.ActionId, Store, any, Record<string, unknown>>,
|
|
||||||
>(
|
|
||||||
effects: T.Effects,
|
effects: T.Effects,
|
||||||
action: T,
|
action: T,
|
||||||
severity: T.ActionSeverity,
|
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. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* @description optionally provide a default value.
|
||||||
* @type { false | { default: string | RandomString | null } }
|
* @type { string | RandomString | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: 'World'
|
||||||
* @example required: { default: 'World' }
|
* @example default: { charset: 'abcdefg', len: 16 }
|
||||||
* @example required: { default: { charset: 'abcdefg', len: 16 } }
|
|
||||||
*/
|
*/
|
||||||
required: RequiredDefault<DefaultString>
|
default: DefaultString | null
|
||||||
|
required: boolean
|
||||||
/**
|
/**
|
||||||
* @description Mask (aka camouflage) text input with dots: ● ● ●
|
* @description Mask (aka camouflage) text input with dots: ● ● ●
|
||||||
* @default false
|
* @default false
|
||||||
@@ -1110,15 +1103,12 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
description?: string | null
|
description?: string | null
|
||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
default: string | null
|
||||||
* @description Unlike other "required" fields, for textarea this is a simple boolean.
|
|
||||||
*/
|
|
||||||
required: boolean
|
required: boolean
|
||||||
minLength?: number | null
|
minLength?: number | null
|
||||||
maxLength?: number | null
|
maxLength?: number | null
|
||||||
placeholder?: string | null
|
placeholder?: string | null
|
||||||
disabled?: false | string
|
disabled?: false | string
|
||||||
generate?: null | RandomString
|
|
||||||
}
|
}
|
||||||
>,
|
>,
|
||||||
) => Value.dynamicTextarea<Store>(getA),
|
) => 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. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* @description optionally provide a default value.
|
||||||
* @type { false | { default: number | null } }
|
* @type { number | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: 7
|
||||||
* @example required: { default: 7 }
|
|
||||||
*/
|
*/
|
||||||
required: RequiredDefault<number>
|
default: number | null
|
||||||
|
required: boolean
|
||||||
min?: number | null
|
min?: number | null
|
||||||
max?: 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. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* @description optionally provide a default value.
|
||||||
* @type { false | { default: string | null } }
|
* @type { string | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: 'ffffff'
|
||||||
* @example required: { default: 'ffffff' }
|
|
||||||
*/
|
*/
|
||||||
required: RequiredDefault<string>
|
default: string | null
|
||||||
|
required: boolean
|
||||||
disabled?: false | string
|
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. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value.
|
* @description optionally provide a default value.
|
||||||
* @type { false | { default: string | null } }
|
* @type { string | null }
|
||||||
* @example required: false
|
* @example default: null
|
||||||
* @example required: { default: null }
|
* @example default: '1985-12-16 18:00:00.000'
|
||||||
* @example required: { 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.
|
* @description Informs the browser how to behave and which date/time component to display.
|
||||||
* @default "datetime-local"
|
* @default "datetime-local"
|
||||||
@@ -1205,7 +1195,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
}
|
}
|
||||||
>,
|
>,
|
||||||
) => Value.dynamicDatetime<Store>(getA),
|
) => Value.dynamicDatetime<Store>(getA),
|
||||||
dynamicSelect: (
|
dynamicSelect: <Variants extends Record<string, string>>(
|
||||||
getA: LazyBuild<
|
getA: LazyBuild<
|
||||||
Store,
|
Store,
|
||||||
{
|
{
|
||||||
@@ -1214,13 +1204,12 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value from the list of values.
|
* @description provide a default value from the list of values.
|
||||||
* @type { false | { default: string | null } }
|
* @type { default: string }
|
||||||
* @example required: false
|
* @example default: 'radio1'
|
||||||
* @example required: { default: null }
|
|
||||||
* @example required: { default: 'radio1' }
|
|
||||||
*/
|
*/
|
||||||
required: RequiredDefault<string>
|
default: keyof Variants & string
|
||||||
|
required: boolean
|
||||||
/**
|
/**
|
||||||
* @description A mapping of unique radio options to their human readable display format.
|
* @description A mapping of unique radio options to their human readable display format.
|
||||||
* @example
|
* @example
|
||||||
@@ -1232,7 +1221,7 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
}
|
}
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
values: Record<string, string>
|
values: Variants
|
||||||
/**
|
/**
|
||||||
* @options
|
* @options
|
||||||
* - false - The field can be modified.
|
* - false - The field can be modified.
|
||||||
@@ -1282,27 +1271,37 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
>,
|
>,
|
||||||
) => Value.dynamicMultiselect<Store>(getA),
|
) => Value.dynamicMultiselect<Store>(getA),
|
||||||
filteredUnion: <
|
filteredUnion: <
|
||||||
Required extends RequiredDefault<string>,
|
VariantValues extends {
|
||||||
Type extends Record<string, any>,
|
[K in string]: {
|
||||||
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
>(
|
>(
|
||||||
getDisabledFn: LazyBuild<Store, 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
|
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,
|
getDisabledFn,
|
||||||
a,
|
a,
|
||||||
aVariants,
|
aVariants,
|
||||||
),
|
),
|
||||||
|
|
||||||
dynamicUnion: <
|
dynamicUnion: <
|
||||||
Required extends RequiredDefault<string>,
|
VariantValues extends {
|
||||||
Type extends Record<string, any>,
|
[K in string]: {
|
||||||
|
name: string
|
||||||
|
spec: InputSpec<any, Store> | InputSpec<any, never>
|
||||||
|
}
|
||||||
|
},
|
||||||
>(
|
>(
|
||||||
getA: LazyBuild<
|
getA: LazyBuild<
|
||||||
Store,
|
Store,
|
||||||
@@ -1312,13 +1311,12 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
/** Presents a warning prompt before permitting the value to change. */
|
/** Presents a warning prompt before permitting the value to change. */
|
||||||
warning?: string | null
|
warning?: string | null
|
||||||
/**
|
/**
|
||||||
* @description Determines if the field is required. If so, optionally provide a default value from the list of variants.
|
* @description provide a default value from the list of variants.
|
||||||
* @type { false | { default: string | null } }
|
* @type { string }
|
||||||
* @example required: false
|
* @example default: 'variant1'
|
||||||
* @example required: { default: null }
|
|
||||||
* @example required: { default: 'variant1' }
|
|
||||||
*/
|
*/
|
||||||
required: Required
|
default: keyof VariantValues & string
|
||||||
|
required: boolean
|
||||||
/**
|
/**
|
||||||
* @options
|
* @options
|
||||||
* - false - The field can be modified.
|
* - false - The field can be modified.
|
||||||
@@ -1329,8 +1327,10 @@ export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
|||||||
disabled: false | string | string[]
|
disabled: false | string | string[]
|
||||||
}
|
}
|
||||||
>,
|
>,
|
||||||
aVariants: Variants<Type, Store> | Variants<Type, never>,
|
aVariants:
|
||||||
) => Value.dynamicUnion<Required, Type, Store>(getA, aVariants),
|
| Variants<VariantValues, Store>
|
||||||
|
| Variants<VariantValues, never>,
|
||||||
|
) => Value.dynamicUnion<VariantValues, Store>(getA, aVariants),
|
||||||
},
|
},
|
||||||
Variants: {
|
Variants: {
|
||||||
of: <
|
of: <
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ describe("builder tests", () => {
|
|||||||
"peer-tor-address": Value.text({
|
"peer-tor-address": Value.text({
|
||||||
name: "Peer tor address",
|
name: "Peer tor address",
|
||||||
description: "The Tor address of the peer interface",
|
description: "The Tor address of the peer interface",
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
}).build({} as any)
|
}).build({} as any)
|
||||||
expect(bitcoinPropertiesBuilt).toMatchObject({
|
expect(bitcoinPropertiesBuilt).toMatchObject({
|
||||||
@@ -55,7 +56,8 @@ describe("values", () => {
|
|||||||
test("text", async () => {
|
test("text", async () => {
|
||||||
const value = Value.text({
|
const value = Value.text({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
})
|
})
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
const rawIs = await value.build({} as any)
|
const rawIs = await value.build({} as any)
|
||||||
@@ -66,7 +68,8 @@ describe("values", () => {
|
|||||||
test("text with default", async () => {
|
test("text with default", async () => {
|
||||||
const value = Value.text({
|
const value = Value.text({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: "this is a default value" },
|
required: true,
|
||||||
|
default: "this is a default value",
|
||||||
})
|
})
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
const rawIs = await value.build({} as any)
|
const rawIs = await value.build({} as any)
|
||||||
@@ -78,6 +81,7 @@ describe("values", () => {
|
|||||||
const value = Value.text({
|
const value = Value.text({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
})
|
})
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
const rawIs = await value.build({} as any)
|
const rawIs = await value.build({} as any)
|
||||||
@@ -89,6 +93,7 @@ describe("values", () => {
|
|||||||
const value = Value.color({
|
const value = Value.color({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
})
|
})
|
||||||
@@ -99,7 +104,8 @@ describe("values", () => {
|
|||||||
test("datetime", async () => {
|
test("datetime", async () => {
|
||||||
const value = Value.datetime({
|
const value = Value.datetime({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
inputmode: "date",
|
inputmode: "date",
|
||||||
@@ -114,6 +120,7 @@ describe("values", () => {
|
|||||||
const value = Value.datetime({
|
const value = Value.datetime({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
inputmode: "date",
|
inputmode: "date",
|
||||||
@@ -128,6 +135,7 @@ describe("values", () => {
|
|||||||
const value = Value.textarea({
|
const value = Value.textarea({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
minLength: null,
|
minLength: null,
|
||||||
@@ -136,12 +144,13 @@ describe("values", () => {
|
|||||||
})
|
})
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
validator.unsafeCast("test text")
|
validator.unsafeCast("test text")
|
||||||
testOutput<typeof validator._TYPE, string>()(null)
|
testOutput<typeof validator._TYPE, string | null | undefined>()(null)
|
||||||
})
|
})
|
||||||
test("number", async () => {
|
test("number", async () => {
|
||||||
const value = Value.number({
|
const value = Value.number({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
integer: false,
|
integer: false,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
@@ -159,6 +168,7 @@ describe("values", () => {
|
|||||||
const value = Value.number({
|
const value = Value.number({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
integer: false,
|
integer: false,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
@@ -175,7 +185,7 @@ describe("values", () => {
|
|||||||
test("select", async () => {
|
test("select", async () => {
|
||||||
const value = Value.select({
|
const value = Value.select({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
default: "a",
|
||||||
values: {
|
values: {
|
||||||
a: "A",
|
a: "A",
|
||||||
b: "B",
|
b: "B",
|
||||||
@@ -192,7 +202,7 @@ describe("values", () => {
|
|||||||
test("nullable select", async () => {
|
test("nullable select", async () => {
|
||||||
const value = Value.select({
|
const value = Value.select({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
default: "a",
|
||||||
values: {
|
values: {
|
||||||
a: "A",
|
a: "A",
|
||||||
b: "B",
|
b: "B",
|
||||||
@@ -203,8 +213,7 @@ describe("values", () => {
|
|||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
validator.unsafeCast("a")
|
validator.unsafeCast("a")
|
||||||
validator.unsafeCast("b")
|
validator.unsafeCast("b")
|
||||||
validator.unsafeCast(null)
|
testOutput<typeof validator._TYPE, "a" | "b">()(null)
|
||||||
testOutput<typeof validator._TYPE, "a" | "b" | null | undefined>()(null)
|
|
||||||
})
|
})
|
||||||
test("multiselect", async () => {
|
test("multiselect", async () => {
|
||||||
const value = Value.multiselect({
|
const value = Value.multiselect({
|
||||||
@@ -250,7 +259,7 @@ describe("values", () => {
|
|||||||
const value = Value.union(
|
const value = Value.union(
|
||||||
{
|
{
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
default: "a",
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
},
|
},
|
||||||
@@ -271,7 +280,16 @@ describe("values", () => {
|
|||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
validator.unsafeCast({ selection: "a", value: { b: false } })
|
validator.unsafeCast({ selection: "a", value: { b: false } })
|
||||||
type Test = typeof validator._TYPE
|
type Test = typeof validator._TYPE
|
||||||
testOutput<Test, { selection: "a"; value: { b: boolean } }>()(null)
|
testOutput<
|
||||||
|
Test,
|
||||||
|
{
|
||||||
|
selection: "a"
|
||||||
|
value: {
|
||||||
|
b: boolean
|
||||||
|
}
|
||||||
|
other?: {}
|
||||||
|
}
|
||||||
|
>()(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("dynamic", () => {
|
describe("dynamic", () => {
|
||||||
@@ -301,7 +319,8 @@ describe("values", () => {
|
|||||||
test("text", async () => {
|
test("text", async () => {
|
||||||
const value = Value.dynamicText(async () => ({
|
const value = Value.dynamicText(async () => ({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
}))
|
}))
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
const rawIs = await value.build({} as any)
|
const rawIs = await value.build({} as any)
|
||||||
@@ -317,7 +336,8 @@ describe("values", () => {
|
|||||||
test("text with default", async () => {
|
test("text with default", async () => {
|
||||||
const value = Value.dynamicText(async () => ({
|
const value = Value.dynamicText(async () => ({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: "this is a default value" },
|
required: true,
|
||||||
|
default: "this is a default value",
|
||||||
}))
|
}))
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
validator.unsafeCast("test text")
|
validator.unsafeCast("test text")
|
||||||
@@ -333,6 +353,7 @@ describe("values", () => {
|
|||||||
const value = Value.dynamicText(async () => ({
|
const value = Value.dynamicText(async () => ({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
}))
|
}))
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
const rawIs = await value.build({} as any)
|
const rawIs = await value.build({} as any)
|
||||||
@@ -349,6 +370,7 @@ describe("values", () => {
|
|||||||
const value = Value.dynamicColor(async () => ({
|
const value = Value.dynamicColor(async () => ({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
}))
|
}))
|
||||||
@@ -414,7 +436,8 @@ describe("values", () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
inputmode: "date",
|
inputmode: "date",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -436,6 +459,7 @@ describe("values", () => {
|
|||||||
const value = Value.dynamicTextarea(async () => ({
|
const value = Value.dynamicTextarea(async () => ({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
minLength: null,
|
minLength: null,
|
||||||
@@ -444,8 +468,7 @@ describe("values", () => {
|
|||||||
}))
|
}))
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
validator.unsafeCast("test text")
|
validator.unsafeCast("test text")
|
||||||
expect(() => validator.unsafeCast(null)).toThrowError()
|
testOutput<typeof validator._TYPE, string | null | undefined>()(null)
|
||||||
testOutput<typeof validator._TYPE, string>()(null)
|
|
||||||
expect(await value.build(fakeOptions)).toMatchObject({
|
expect(await value.build(fakeOptions)).toMatchObject({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
@@ -454,7 +477,8 @@ describe("values", () => {
|
|||||||
test("number", async () => {
|
test("number", async () => {
|
||||||
const value = Value.dynamicNumber(() => ({
|
const value = Value.dynamicNumber(() => ({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
integer: false,
|
integer: false,
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
@@ -477,7 +501,7 @@ describe("values", () => {
|
|||||||
test("select", async () => {
|
test("select", async () => {
|
||||||
const value = Value.dynamicSelect(() => ({
|
const value = Value.dynamicSelect(() => ({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
default: "a",
|
||||||
values: {
|
values: {
|
||||||
a: "A",
|
a: "A",
|
||||||
b: "B",
|
b: "B",
|
||||||
@@ -489,11 +513,9 @@ describe("values", () => {
|
|||||||
validator.unsafeCast("a")
|
validator.unsafeCast("a")
|
||||||
validator.unsafeCast("b")
|
validator.unsafeCast("b")
|
||||||
validator.unsafeCast("c")
|
validator.unsafeCast("c")
|
||||||
validator.unsafeCast(null)
|
testOutput<typeof validator._TYPE, string>()(null)
|
||||||
testOutput<typeof validator._TYPE, string | null | undefined>()(null)
|
|
||||||
expect(await value.build(fakeOptions)).toMatchObject({
|
expect(await value.build(fakeOptions)).toMatchObject({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: true,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test("multiselect", async () => {
|
test("multiselect", async () => {
|
||||||
@@ -529,7 +551,7 @@ describe("values", () => {
|
|||||||
() => ["a", "c"],
|
() => ["a", "c"],
|
||||||
{
|
{
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
default: "a",
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
},
|
},
|
||||||
@@ -563,8 +585,28 @@ describe("values", () => {
|
|||||||
type Test = typeof validator._TYPE
|
type Test = typeof validator._TYPE
|
||||||
testOutput<
|
testOutput<
|
||||||
Test,
|
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)
|
>()(null)
|
||||||
|
|
||||||
const built = await value.build({} as any)
|
const built = await value.build({} as any)
|
||||||
@@ -596,7 +638,7 @@ describe("values", () => {
|
|||||||
() => ({
|
() => ({
|
||||||
disabled: ["a", "c"],
|
disabled: ["a", "c"],
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: { default: null },
|
default: "b",
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
}),
|
}),
|
||||||
@@ -630,10 +672,28 @@ describe("values", () => {
|
|||||||
type Test = typeof validator._TYPE
|
type Test = typeof validator._TYPE
|
||||||
testOutput<
|
testOutput<
|
||||||
Test,
|
Test,
|
||||||
| { selection: "a"; value: { b: boolean } }
|
| {
|
||||||
| { selection: "b"; value: { b: boolean } }
|
selection: "a"
|
||||||
| null
|
value: {
|
||||||
| undefined
|
b: boolean
|
||||||
|
}
|
||||||
|
other?: {
|
||||||
|
b?: {
|
||||||
|
b?: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
selection: "b"
|
||||||
|
value: {
|
||||||
|
b: boolean
|
||||||
|
}
|
||||||
|
other?: {
|
||||||
|
a?: {
|
||||||
|
b?: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
>()(null)
|
>()(null)
|
||||||
|
|
||||||
const built = await value.build({} as any)
|
const built = await value.build({} as any)
|
||||||
@@ -728,6 +788,7 @@ describe("Nested nullable values", () => {
|
|||||||
description:
|
description:
|
||||||
"If no name is provided, the name from inputSpec will be used",
|
"If no name is provided, the name from inputSpec will be used",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
@@ -743,6 +804,7 @@ describe("Nested nullable values", () => {
|
|||||||
description:
|
description:
|
||||||
"If no name is provided, the name from inputSpec will be used",
|
"If no name is provided, the name from inputSpec will be used",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
placeholder: null,
|
placeholder: null,
|
||||||
integer: false,
|
integer: false,
|
||||||
@@ -765,6 +827,7 @@ describe("Nested nullable values", () => {
|
|||||||
description:
|
description:
|
||||||
"If no name is provided, the name from inputSpec will be used",
|
"If no name is provided, the name from inputSpec will be used",
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
@@ -780,7 +843,7 @@ describe("Nested nullable values", () => {
|
|||||||
name: "Temp Name",
|
name: "Temp Name",
|
||||||
description:
|
description:
|
||||||
"If no name is provided, the name from inputSpec will be used",
|
"If no name is provided, the name from inputSpec will be used",
|
||||||
required: false,
|
default: "a",
|
||||||
warning: null,
|
warning: null,
|
||||||
values: {
|
values: {
|
||||||
a: "A",
|
a: "A",
|
||||||
@@ -791,7 +854,7 @@ describe("Nested nullable values", () => {
|
|||||||
name: "Temp Name",
|
name: "Temp Name",
|
||||||
description:
|
description:
|
||||||
"If no name is provided, the name from inputSpec will be used",
|
"If no name is provided, the name from inputSpec will be used",
|
||||||
required: false,
|
default: "a",
|
||||||
warning: null,
|
warning: null,
|
||||||
values: {
|
values: {
|
||||||
a: "A",
|
a: "A",
|
||||||
@@ -799,10 +862,9 @@ describe("Nested nullable values", () => {
|
|||||||
}).build({} as any)
|
}).build({} as any)
|
||||||
|
|
||||||
const validator = value.validator
|
const validator = value.validator
|
||||||
validator.unsafeCast({ a: null })
|
|
||||||
validator.unsafeCast({ a: "a" })
|
validator.unsafeCast({ a: "a" })
|
||||||
expect(() => validator.unsafeCast({ a: "4" })).toThrowError()
|
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 () => {
|
test("Testing multiselect", async () => {
|
||||||
const value = InputSpec.of({
|
const value = InputSpec.of({
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ describe("Inputs", () => {
|
|||||||
dbcache: 5,
|
dbcache: 5,
|
||||||
pruning: {
|
pruning: {
|
||||||
selection: "disabled",
|
selection: "disabled",
|
||||||
value: {},
|
value: { disabled: {} },
|
||||||
},
|
},
|
||||||
blockfilters: {
|
blockfilters: {
|
||||||
blockfilterindex: false,
|
blockfilterindex: false,
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ export class FileHelper<A> {
|
|||||||
protected constructor(
|
protected constructor(
|
||||||
readonly path: string,
|
readonly path: string,
|
||||||
readonly writeData: (dataIn: A) => 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
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async readFile(): Promise<unknown> {
|
||||||
* Reads the file from disk and converts it to structured data.
|
|
||||||
*/
|
|
||||||
private async readOnce(): Promise<A | null> {
|
|
||||||
if (!(await exists(this.path))) {
|
if (!(await exists(this.path))) {
|
||||||
return null
|
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> {
|
private async readConst(effects: T.Effects): Promise<A | null> {
|
||||||
const watch = this.readWatch()
|
const watch = this.readWatch()
|
||||||
const res = await watch.next()
|
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.
|
* Accepts full structured data and performs a merge with the existing file on disk if it exists.
|
||||||
*/
|
*/
|
||||||
async write(data: A) {
|
async write(data: A) {
|
||||||
const fileData = (await this.readOnce()) || {}
|
const fileData = (await this.readFile()) || {}
|
||||||
const mergeData = merge({}, fileData, data)
|
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.
|
* 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 =
|
const fileData =
|
||||||
(await this.readOnce()) ||
|
(await this.readFile()) ||
|
||||||
(() => {
|
(() => {
|
||||||
throw new Error(`${this.path}: does not exist`)
|
throw new Error(`${this.path}: does not exist`)
|
||||||
})()
|
})()
|
||||||
const mergeData = merge({}, fileData, data)
|
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.
|
* Like one behaviour of another dependency or something similar.
|
||||||
*/
|
*/
|
||||||
withPath(path: string) {
|
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>(
|
static raw<A>(
|
||||||
path: string,
|
path: string,
|
||||||
toFile: (dataIn: A) => 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.
|
* 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>) {
|
static json<A>(path: string, shape: matches.Validator<unknown, A>) {
|
||||||
return new FileHelper<A>(
|
return new FileHelper<A>(
|
||||||
path,
|
path,
|
||||||
(inData) => {
|
(inData) => JSON.stringify(inData, null, 2),
|
||||||
return JSON.stringify(inData, null, 2)
|
(inString) => JSON.parse(inString),
|
||||||
},
|
(data) => shape.unsafeCast(data),
|
||||||
(inString) => {
|
|
||||||
return shape.unsafeCast(JSON.parse(inString))
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -217,12 +222,9 @@ export class FileHelper<A> {
|
|||||||
) {
|
) {
|
||||||
return new FileHelper<A>(
|
return new FileHelper<A>(
|
||||||
path,
|
path,
|
||||||
(inData) => {
|
(inData) => TOML.stringify(inData as any),
|
||||||
return TOML.stringify(inData as any)
|
(inString) => TOML.parse(inString),
|
||||||
},
|
(data) => shape.unsafeCast(data),
|
||||||
(inString) => {
|
|
||||||
return shape.unsafeCast(TOML.parse(inString))
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -234,12 +236,9 @@ export class FileHelper<A> {
|
|||||||
) {
|
) {
|
||||||
return new FileHelper<A>(
|
return new FileHelper<A>(
|
||||||
path,
|
path,
|
||||||
(inData) => {
|
(inData) => YAML.stringify(inData, null, 2),
|
||||||
return YAML.stringify(inData, null, 2)
|
(inString) => YAML.parse(inString),
|
||||||
},
|
(data) => shape.unsafeCast(data),
|
||||||
(inString) => {
|
|
||||||
return shape.unsafeCast(YAML.parse(inString))
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
sdk/package/package-lock.json
generated
13
sdk/package/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.3.6-alpha.13",
|
"version": "0.3.6-alpha.16",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.3.6-alpha.13",
|
"version": "0.3.6-alpha.16",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"ts-matches": "^5.5.1",
|
"ts-matches": "^6.0.0",
|
||||||
"yaml": "^2.2.2"
|
"yaml": "^2.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -3918,9 +3918,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/ts-matches": {
|
"node_modules/ts-matches": {
|
||||||
"version": "5.5.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.0.0.tgz",
|
||||||
"integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg=="
|
"integrity": "sha512-vR4hhz9bYMW30qIJUuLaeAWlsR54vse6ZI2riVhVLMBE6/vss43jwrOvbHheiyU7e26ssT/yWx69aJHD2REJSA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ts-morph": {
|
"node_modules/ts-morph": {
|
||||||
"version": "18.0.0",
|
"version": "18.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@start9labs/start-sdk",
|
"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",
|
"description": "Software development kit to facilitate packaging services for StartOS",
|
||||||
"main": "./package/lib/index.js",
|
"main": "./package/lib/index.js",
|
||||||
"types": "./package/lib/index.d.ts",
|
"types": "./package/lib/index.d.ts",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"ts-matches": "^5.5.1",
|
"ts-matches": "^6.0.0",
|
||||||
"yaml": "^2.2.2",
|
"yaml": "^2.2.2",
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@noble/curves": "^1.4.0",
|
"@noble/curves": "^1.4.0",
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
|||||||
description: value.description || null,
|
description: value.description || null,
|
||||||
warning: value.warning || null,
|
warning: value.warning || null,
|
||||||
required: !(value.nullable || false),
|
required: !(value.nullable || false),
|
||||||
|
default: value.default,
|
||||||
placeholder: value.placeholder || null,
|
placeholder: value.placeholder || null,
|
||||||
maxLength: null,
|
maxLength: null,
|
||||||
minLength: null,
|
minLength: null,
|
||||||
@@ -96,12 +97,8 @@ const {InputSpec, List, Value, Variants} = sdk
|
|||||||
return `${rangeToTodoComment(value?.range)}Value.text(${JSON.stringify(
|
return `${rangeToTodoComment(value?.range)}Value.text(${JSON.stringify(
|
||||||
{
|
{
|
||||||
name: value.name || null,
|
name: value.name || null,
|
||||||
// prettier-ignore
|
default: value.default || null,
|
||||||
required: (
|
required: !value.nullable,
|
||||||
value.default != null ? {default: value.default} :
|
|
||||||
value.nullable === false ? {default: null} :
|
|
||||||
!value.nullable
|
|
||||||
),
|
|
||||||
description: value.description || null,
|
description: value.description || null,
|
||||||
warning: value.warning || null,
|
warning: value.warning || null,
|
||||||
masked: value.masked || false,
|
masked: value.masked || false,
|
||||||
@@ -130,12 +127,8 @@ const {InputSpec, List, Value, Variants} = sdk
|
|||||||
name: value.name || null,
|
name: value.name || null,
|
||||||
description: value.description || null,
|
description: value.description || null,
|
||||||
warning: value.warning || null,
|
warning: value.warning || null,
|
||||||
// prettier-ignore
|
default: value.default || null,
|
||||||
required: (
|
required: !value.nullable,
|
||||||
value.default != null ? {default: value.default} :
|
|
||||||
value.nullable === false ? {default: null} :
|
|
||||||
!value.nullable
|
|
||||||
),
|
|
||||||
min: null,
|
min: null,
|
||||||
max: null,
|
max: null,
|
||||||
step: null,
|
step: null,
|
||||||
@@ -174,13 +167,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
|||||||
name: value.name || null,
|
name: value.name || null,
|
||||||
description: value.description || null,
|
description: value.description || null,
|
||||||
warning: value.warning || null,
|
warning: value.warning || null,
|
||||||
|
default: value.default,
|
||||||
// prettier-ignore
|
|
||||||
required:(
|
|
||||||
value.default != null ? {default: value.default} :
|
|
||||||
value.nullable === false ? {default: null} :
|
|
||||||
!value.nullable
|
|
||||||
),
|
|
||||||
values,
|
values,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
@@ -207,14 +194,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
|||||||
name: ${JSON.stringify(value.name || null)},
|
name: ${JSON.stringify(value.name || null)},
|
||||||
description: ${JSON.stringify(value.tag.description || null)},
|
description: ${JSON.stringify(value.tag.description || null)},
|
||||||
warning: ${JSON.stringify(value.tag.warning || null)},
|
warning: ${JSON.stringify(value.tag.warning || null)},
|
||||||
|
default: ${JSON.stringify(value.default)},
|
||||||
// prettier-ignore
|
|
||||||
required: ${JSON.stringify(
|
|
||||||
// prettier-ignore
|
|
||||||
value.default != null ? {default: value.default} :
|
|
||||||
value.nullable === false ? {default: null} :
|
|
||||||
!value.nullable,
|
|
||||||
)},
|
|
||||||
}, ${variants})`
|
}, ${variants})`
|
||||||
}
|
}
|
||||||
case "list": {
|
case "list": {
|
||||||
@@ -341,12 +321,7 @@ const {InputSpec, List, Value, Variants} = sdk
|
|||||||
value?.spec?.tag?.description || null,
|
value?.spec?.tag?.description || null,
|
||||||
)},
|
)},
|
||||||
warning: ${JSON.stringify(value?.spec?.tag?.warning || null)},
|
warning: ${JSON.stringify(value?.spec?.tag?.warning || null)},
|
||||||
required: ${JSON.stringify(
|
default: ${JSON.stringify(value?.spec?.default || null)},
|
||||||
// prettier-ignore
|
|
||||||
'default' in value?.spec ? {default: value?.spec?.default} :
|
|
||||||
!!value?.spec?.tag?.nullable || false ? {default: null} :
|
|
||||||
false,
|
|
||||||
)},
|
|
||||||
}, ${variants})
|
}, ${variants})
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -268,25 +268,29 @@ const cifsSpec = ISB.InputSpec.of({
|
|||||||
'The hostname of your target device on the Local Area Network.',
|
'The hostname of your target device on the Local Area Network.',
|
||||||
warning: null,
|
warning: null,
|
||||||
placeholder: `e.g. 'My Computer' OR 'my-computer.local'`,
|
placeholder: `e.g. 'My Computer' OR 'my-computer.local'`,
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
patterns: [],
|
patterns: [],
|
||||||
}),
|
}),
|
||||||
path: ISB.Value.text({
|
path: ISB.Value.text({
|
||||||
name: 'Path',
|
name: 'Path',
|
||||||
description: `On Windows, this is the fully qualified path to the shared folder, (e.g. /Desktop/my-folder).\n\n On Linux and Mac, this is the literal name of the shared folder (e.g. my-shared-folder).`,
|
description: `On Windows, this is the fully qualified path to the shared folder, (e.g. /Desktop/my-folder).\n\n On Linux and Mac, this is the literal name of the shared folder (e.g. my-shared-folder).`,
|
||||||
placeholder: 'e.g. my-shared-folder or /Desktop/my-folder',
|
placeholder: 'e.g. my-shared-folder or /Desktop/my-folder',
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
username: ISB.Value.text({
|
username: ISB.Value.text({
|
||||||
name: 'Username',
|
name: 'Username',
|
||||||
description: `On Linux, this is the samba username you created when sharing the folder.\n\n On Mac and Windows, this is the username of the user who is sharing the folder.`,
|
description: `On Linux, this is the samba username you created when sharing the folder.\n\n On Mac and Windows, this is the username of the user who is sharing the folder.`,
|
||||||
required: { default: null },
|
required: true,
|
||||||
|
default: null,
|
||||||
placeholder: 'My Network Folder',
|
placeholder: 'My Network Folder',
|
||||||
}),
|
}),
|
||||||
password: ISB.Value.text({
|
password: ISB.Value.text({
|
||||||
name: 'Password',
|
name: 'Password',
|
||||||
description: `On Linux, this is the samba password you created when sharing the folder.\n\n On Mac and Windows, this is the password of the user who is sharing the folder.`,
|
description: `On Linux, this is the samba password you created when sharing the folder.\n\n On Mac and Windows, this is the password of the user who is sharing the folder.`,
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
masked: true,
|
masked: true,
|
||||||
placeholder: 'My Network Folder',
|
placeholder: 'My Network Folder',
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
[tuiHintContent]="spec | hint"
|
[tuiHintContent]="spec | hint"
|
||||||
[disabled]="disabled"
|
[disabled]="disabled"
|
||||||
[readOnly]="readOnly"
|
[readOnly]="readOnly"
|
||||||
[tuiTextfieldCleaner]="!spec.required"
|
[tuiTextfieldCleaner]="false"
|
||||||
[pseudoInvalid]="invalid"
|
[pseudoInvalid]="invalid"
|
||||||
[(ngModel)]="selected"
|
[(ngModel)]="selected"
|
||||||
(focusedChange)="onFocus($event)"
|
(focusedChange)="onFocus($event)"
|
||||||
>
|
>
|
||||||
{{ spec.name }}
|
{{ spec.name }}*
|
||||||
<span *ngIf="spec.required">*</span>
|
|
||||||
<select
|
<select
|
||||||
tuiSelect
|
tuiSelect
|
||||||
[placeholder]="spec.name"
|
[placeholder]="spec.name"
|
||||||
|
|||||||
@@ -697,23 +697,20 @@ interface SettingBtn {
|
|||||||
const passwordSpec = ISB.InputSpec.of({
|
const passwordSpec = ISB.InputSpec.of({
|
||||||
currentPassword: ISB.Value.text({
|
currentPassword: ISB.Value.text({
|
||||||
name: 'Current Password',
|
name: 'Current Password',
|
||||||
required: {
|
required: true,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
masked: true,
|
masked: true,
|
||||||
}),
|
}),
|
||||||
newPassword1: ISB.Value.text({
|
newPassword1: ISB.Value.text({
|
||||||
name: 'New Password',
|
name: 'New Password',
|
||||||
required: {
|
required: true,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
masked: true,
|
masked: true,
|
||||||
}),
|
}),
|
||||||
newPassword2: ISB.Value.text({
|
newPassword2: ISB.Value.text({
|
||||||
name: 'Retype New Password',
|
name: 'Retype New Password',
|
||||||
required: {
|
required: true,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
masked: true,
|
masked: true,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1151,7 +1151,7 @@ export module Mock {
|
|||||||
name: 'P2P Settings',
|
name: 'P2P Settings',
|
||||||
description:
|
description:
|
||||||
'<p>The Bitcoin Core node to connect to over the peer-to-peer (P2P) interface:</p><ul><li><strong>Bitcoin Core</strong>: The Bitcoin Core service installed on this device</li><li><strong>External Node</strong>: A Bitcoin node running on a different device</li></ul>',
|
'<p>The Bitcoin Core node to connect to over the peer-to-peer (P2P) interface:</p><ul><li><strong>Bitcoin Core</strong>: The Bitcoin Core service installed on this device</li><li><strong>External Node</strong>: A Bitcoin node running on a different device</li></ul>',
|
||||||
required: { default: 'internal' },
|
default: 'internal',
|
||||||
},
|
},
|
||||||
ISB.Variants.of({
|
ISB.Variants.of({
|
||||||
internal: { name: 'Bitcoin Core', spec: ISB.InputSpec.of({}) },
|
internal: { name: 'Bitcoin Core', spec: ISB.InputSpec.of({}) },
|
||||||
@@ -1160,9 +1160,8 @@ export module Mock {
|
|||||||
spec: ISB.InputSpec.of({
|
spec: ISB.InputSpec.of({
|
||||||
'p2p-host': ISB.Value.text({
|
'p2p-host': ISB.Value.text({
|
||||||
name: 'Public Address',
|
name: 'Public Address',
|
||||||
required: {
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
description:
|
description:
|
||||||
'The public address of your Bitcoin Core server',
|
'The public address of your Bitcoin Core server',
|
||||||
}),
|
}),
|
||||||
@@ -1170,9 +1169,8 @@ export module Mock {
|
|||||||
name: 'P2P Port',
|
name: 'P2P Port',
|
||||||
description:
|
description:
|
||||||
'The port that your Bitcoin Core P2P server is bound to',
|
'The port that your Bitcoin Core P2P server is bound to',
|
||||||
required: {
|
required: true,
|
||||||
default: 8333,
|
default: 8333,
|
||||||
},
|
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 65535,
|
max: 65535,
|
||||||
integer: true,
|
integer: true,
|
||||||
@@ -1186,10 +1184,12 @@ export module Mock {
|
|||||||
color: ISB.Value.color({
|
color: ISB.Value.color({
|
||||||
name: 'Color',
|
name: 'Color',
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
datetime: ISB.Value.datetime({
|
datetime: ISB.Value.datetime({
|
||||||
name: 'Datetime',
|
name: 'Datetime',
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
// file: ISB.Value.file({
|
// file: ISB.Value.file({
|
||||||
// name: 'File',
|
// name: 'File',
|
||||||
@@ -1221,9 +1221,8 @@ export module Mock {
|
|||||||
ISB.InputSpec.of({
|
ISB.InputSpec.of({
|
||||||
rpcuser2: ISB.Value.text({
|
rpcuser2: ISB.Value.text({
|
||||||
name: 'RPC Username',
|
name: 'RPC Username',
|
||||||
required: {
|
required: false,
|
||||||
default: 'defaultrpcusername',
|
default: 'defaultrpcusername',
|
||||||
},
|
|
||||||
description: 'rpc username',
|
description: 'rpc username',
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
@@ -1234,9 +1233,8 @@ export module Mock {
|
|||||||
}),
|
}),
|
||||||
rpcuser: ISB.Value.text({
|
rpcuser: ISB.Value.text({
|
||||||
name: 'RPC Username',
|
name: 'RPC Username',
|
||||||
required: {
|
required: true,
|
||||||
default: 'defaultrpcusername',
|
default: 'defaultrpcusername',
|
||||||
},
|
|
||||||
description: 'rpc username',
|
description: 'rpc username',
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
@@ -1247,21 +1245,19 @@ export module Mock {
|
|||||||
}),
|
}),
|
||||||
rpcpass: ISB.Value.text({
|
rpcpass: ISB.Value.text({
|
||||||
name: 'RPC User Password',
|
name: 'RPC User Password',
|
||||||
required: {
|
required: true,
|
||||||
default: {
|
default: {
|
||||||
charset: 'a-z,A-Z,2-9',
|
charset: 'a-z,A-Z,2-9',
|
||||||
len: 20,
|
len: 20,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
description: 'rpc password',
|
description: 'rpc password',
|
||||||
}),
|
}),
|
||||||
rpcpass2: ISB.Value.text({
|
rpcpass2: ISB.Value.text({
|
||||||
name: 'RPC User Password',
|
name: 'RPC User Password',
|
||||||
required: {
|
required: true,
|
||||||
default: {
|
default: {
|
||||||
charset: 'a-z,A-Z,2-9',
|
charset: 'a-z,A-Z,2-9',
|
||||||
len: 20,
|
len: 20,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
description: 'rpc password',
|
description: 'rpc password',
|
||||||
}),
|
}),
|
||||||
@@ -1294,14 +1290,14 @@ export module Mock {
|
|||||||
name: 'First Name',
|
name: 'First Name',
|
||||||
required: false,
|
required: false,
|
||||||
description: 'User first name',
|
description: 'User first name',
|
||||||
|
default: 'Matt',
|
||||||
}),
|
}),
|
||||||
'last-name': ISB.Value.text({
|
'last-name': ISB.Value.text({
|
||||||
name: 'Last Name',
|
name: 'Last Name',
|
||||||
required: {
|
required: true,
|
||||||
default: {
|
default: {
|
||||||
charset: 'a-g,2-9',
|
charset: 'a-g,2-9',
|
||||||
len: 12,
|
len: 12,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
description: 'User first name',
|
description: 'User first name',
|
||||||
patterns: [
|
patterns: [
|
||||||
@@ -1316,6 +1312,7 @@ export module Mock {
|
|||||||
description: 'The age of the user',
|
description: 'The age of the user',
|
||||||
warning: 'User must be at least 18.',
|
warning: 'User must be at least 18.',
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
min: 18,
|
min: 18,
|
||||||
integer: false,
|
integer: false,
|
||||||
}),
|
}),
|
||||||
@@ -1343,7 +1340,7 @@ export module Mock {
|
|||||||
name: 'Preference',
|
name: 'Preference',
|
||||||
description: null,
|
description: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
required: { default: 'summer' },
|
default: 'summer',
|
||||||
},
|
},
|
||||||
ISB.Variants.of({
|
ISB.Variants.of({
|
||||||
summer: {
|
summer: {
|
||||||
@@ -1351,17 +1348,14 @@ export module Mock {
|
|||||||
spec: ISB.InputSpec.of({
|
spec: ISB.InputSpec.of({
|
||||||
'favorite-tree': ISB.Value.text({
|
'favorite-tree': ISB.Value.text({
|
||||||
name: 'Favorite Tree',
|
name: 'Favorite Tree',
|
||||||
required: {
|
required: true,
|
||||||
default: 'Maple',
|
default: 'Maple',
|
||||||
},
|
|
||||||
description: 'What is your favorite tree?',
|
description: 'What is your favorite tree?',
|
||||||
}),
|
}),
|
||||||
'favorite-flower': ISB.Value.select({
|
'favorite-flower': ISB.Value.select({
|
||||||
name: 'Favorite Flower',
|
name: 'Favorite Flower',
|
||||||
description: 'Select your favorite flower',
|
description: 'Select your favorite flower',
|
||||||
required: {
|
default: 'none',
|
||||||
default: 'none',
|
|
||||||
},
|
|
||||||
values: {
|
values: {
|
||||||
none: 'none',
|
none: 'none',
|
||||||
red: 'red',
|
red: 'red',
|
||||||
@@ -1392,9 +1386,7 @@ export module Mock {
|
|||||||
name: 'Random select',
|
name: 'Random select',
|
||||||
description: 'This is not even real.',
|
description: 'This is not even real.',
|
||||||
warning: 'Be careful changing this!',
|
warning: 'Be careful changing this!',
|
||||||
required: {
|
default: 'option1',
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
values: {
|
values: {
|
||||||
option1: 'option1',
|
option1: 'option1',
|
||||||
option2: 'option2',
|
option2: 'option2',
|
||||||
@@ -1409,9 +1401,8 @@ export module Mock {
|
|||||||
description: 'Your favorite number of all time',
|
description: 'Your favorite number of all time',
|
||||||
warning:
|
warning:
|
||||||
'Once you set this number, it can never be changed without severe consequences.',
|
'Once you set this number, it can never be changed without severe consequences.',
|
||||||
required: {
|
required: false,
|
||||||
default: 7,
|
default: 7,
|
||||||
},
|
|
||||||
integer: false,
|
integer: false,
|
||||||
units: 'BTC',
|
units: 'BTC',
|
||||||
},
|
},
|
||||||
@@ -1432,11 +1423,13 @@ export module Mock {
|
|||||||
name: 'First Law',
|
name: 'First Law',
|
||||||
required: false,
|
required: false,
|
||||||
description: 'the first law',
|
description: 'the first law',
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
law2: ISB.Value.text({
|
law2: ISB.Value.text({
|
||||||
name: 'Second Law',
|
name: 'Second Law',
|
||||||
required: false,
|
required: false,
|
||||||
description: 'the second law',
|
description: 'the second law',
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -1452,19 +1445,17 @@ export module Mock {
|
|||||||
spec: ISB.InputSpec.of({
|
spec: ISB.InputSpec.of({
|
||||||
rulemakername: ISB.Value.text({
|
rulemakername: ISB.Value.text({
|
||||||
name: 'Rulemaker Name',
|
name: 'Rulemaker Name',
|
||||||
required: {
|
required: true,
|
||||||
default: {
|
default: {
|
||||||
charset: 'a-g,2-9',
|
charset: 'a-g,2-9',
|
||||||
len: 12,
|
len: 12,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
description: 'the name of the rule maker',
|
description: 'the name of the rule maker',
|
||||||
}),
|
}),
|
||||||
rulemakerip: ISB.Value.text({
|
rulemakerip: ISB.Value.text({
|
||||||
name: 'Rulemaker IP',
|
name: 'Rulemaker IP',
|
||||||
required: {
|
required: true,
|
||||||
default: '192.168.1.0',
|
default: '192.168.1.0',
|
||||||
},
|
|
||||||
description: 'the ip of the rule maker',
|
description: 'the ip of the rule maker',
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
@@ -1480,9 +1471,8 @@ export module Mock {
|
|||||||
),
|
),
|
||||||
rpcuser: ISB.Value.text({
|
rpcuser: ISB.Value.text({
|
||||||
name: 'RPC Username',
|
name: 'RPC Username',
|
||||||
required: {
|
required: true,
|
||||||
default: 'defaultrpcusername',
|
default: 'defaultrpcusername',
|
||||||
},
|
|
||||||
description: 'rpc username',
|
description: 'rpc username',
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
@@ -1493,11 +1483,10 @@ export module Mock {
|
|||||||
}),
|
}),
|
||||||
rpcpass: ISB.Value.text({
|
rpcpass: ISB.Value.text({
|
||||||
name: 'RPC User Password',
|
name: 'RPC User Password',
|
||||||
required: {
|
required: true,
|
||||||
default: {
|
default: {
|
||||||
charset: 'a-z,A-Z,2-9',
|
charset: 'a-z,A-Z,2-9',
|
||||||
len: 20,
|
len: 20,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
description: 'rpc password',
|
description: 'rpc password',
|
||||||
masked: true,
|
masked: true,
|
||||||
@@ -1509,7 +1498,7 @@ export module Mock {
|
|||||||
name: 'Bitcoin Node',
|
name: 'Bitcoin Node',
|
||||||
description: 'Options<ul><li>Item 1</li><li>Item 2</li></ul>',
|
description: 'Options<ul><li>Item 1</li><li>Item 2</li></ul>',
|
||||||
warning: 'Careful changing this',
|
warning: 'Careful changing this',
|
||||||
required: { default: 'internal' },
|
default: 'internal',
|
||||||
},
|
},
|
||||||
ISB.Variants.of({
|
ISB.Variants.of({
|
||||||
fake: {
|
fake: {
|
||||||
@@ -1531,9 +1520,8 @@ export module Mock {
|
|||||||
ISB.InputSpec.of({
|
ISB.InputSpec.of({
|
||||||
name: ISB.Value.text({
|
name: ISB.Value.text({
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
required: {
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
regex: '^[a-zA-Z]+$',
|
regex: '^[a-zA-Z]+$',
|
||||||
@@ -1544,17 +1532,15 @@ export module Mock {
|
|||||||
email: ISB.Value.text({
|
email: ISB.Value.text({
|
||||||
name: 'Email',
|
name: 'Email',
|
||||||
inputmode: 'email',
|
inputmode: 'email',
|
||||||
required: {
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
'public-domain': ISB.Value.text({
|
'public-domain': ISB.Value.text({
|
||||||
name: 'Public Domain',
|
name: 'Public Domain',
|
||||||
required: {
|
required: true,
|
||||||
default: 'bitcoinnode.com',
|
default: 'bitcoinnode.com',
|
||||||
},
|
|
||||||
description: 'the public address of the node',
|
description: 'the public address of the node',
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
@@ -1565,9 +1551,8 @@ export module Mock {
|
|||||||
}),
|
}),
|
||||||
'private-domain': ISB.Value.text({
|
'private-domain': ISB.Value.text({
|
||||||
name: 'Private Domain',
|
name: 'Private Domain',
|
||||||
required: {
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
description: 'the private address of the node',
|
description: 'the private address of the node',
|
||||||
masked: true,
|
masked: true,
|
||||||
inputmode: 'url',
|
inputmode: 'url',
|
||||||
@@ -1580,9 +1565,8 @@ export module Mock {
|
|||||||
name: 'Port',
|
name: 'Port',
|
||||||
description:
|
description:
|
||||||
'the default port for your Bitcoin node. default: 8333, testnet: 18333, regtest: 18444',
|
'the default port for your Bitcoin node. default: 8333, testnet: 18333, regtest: 18444',
|
||||||
required: {
|
required: true,
|
||||||
default: 8333,
|
default: 8333,
|
||||||
},
|
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 9998,
|
max: 9998,
|
||||||
step: 1,
|
step: 1,
|
||||||
@@ -1595,6 +1579,7 @@ export module Mock {
|
|||||||
len: 20,
|
len: 20,
|
||||||
},
|
},
|
||||||
required: false,
|
required: false,
|
||||||
|
default: null,
|
||||||
description:
|
description:
|
||||||
'You most favorite slogan in the whole world, used for paying you.',
|
'You most favorite slogan in the whole world, used for paying you.',
|
||||||
masked: true,
|
masked: true,
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ export class FormService {
|
|||||||
return this.formBuilder.control(value)
|
return this.formBuilder.control(value)
|
||||||
case 'select':
|
case 'select':
|
||||||
value = currentValue === undefined ? spec.default : currentValue
|
value = currentValue === undefined ? spec.default : currentValue
|
||||||
return this.formBuilder.control(value, selectValidators(spec))
|
return this.formBuilder.control(value)
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
value = currentValue === undefined ? spec.default : currentValue
|
value = currentValue === undefined ? spec.default : currentValue
|
||||||
return this.formBuilder.control(value, multiselectValidators(spec))
|
return this.formBuilder.control(value, multiselectValidators(spec))
|
||||||
@@ -231,16 +231,6 @@ function numberValidators(spec: IST.ValueSpecNumber): ValidatorFn[] {
|
|||||||
return validators
|
return validators
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectValidators(spec: IST.ValueSpecSelect): ValidatorFn[] {
|
|
||||||
const validators: ValidatorFn[] = []
|
|
||||||
|
|
||||||
if (spec.required) {
|
|
||||||
validators.push(Validators.required)
|
|
||||||
}
|
|
||||||
|
|
||||||
return validators
|
|
||||||
}
|
|
||||||
|
|
||||||
function multiselectValidators(spec: IST.ValueSpecMultiselect): ValidatorFn[] {
|
function multiselectValidators(spec: IST.ValueSpecMultiselect): ValidatorFn[] {
|
||||||
const validators: ValidatorFn[] = []
|
const validators: ValidatorFn[] = []
|
||||||
validators.push(listInRange(spec.minLength, spec.maxLength))
|
validators.push(listInRange(spec.minLength, spec.maxLength))
|
||||||
|
|||||||
Reference in New Issue
Block a user