mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-30 20:24:47 +00:00
wip: Working so far
This commit is contained in:
@@ -1,27 +1,39 @@
|
|||||||
|
import { Parser } from "ts-matches";
|
||||||
|
import { Config } from "../config/builder";
|
||||||
import {
|
import {
|
||||||
ActionMetaData,
|
ActionMetaData,
|
||||||
ActionResult,
|
ActionResult,
|
||||||
Effects,
|
Effects,
|
||||||
ExportedAction,
|
ExportedAction,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import { Utils, utils } from "../util";
|
import { Utils, once, utils } from "../util";
|
||||||
|
import { TypeFromProps } from "../util/propertiesMatcher";
|
||||||
|
import { InputSpec } from "../config/configTypes";
|
||||||
|
|
||||||
export class CreatedAction<WrapperData, Input> {
|
export class CreatedAction<WrapperData, Input extends Config<InputSpec>> {
|
||||||
private constructor(
|
private constructor(
|
||||||
readonly metaData: ActionMetaData,
|
private myMetaData: Omit<ActionMetaData, "input"> & { input: Input },
|
||||||
readonly fn: (options: {
|
readonly fn: (options: {
|
||||||
effects: Effects;
|
effects: Effects;
|
||||||
utils: Utils<WrapperData>;
|
utils: Utils<WrapperData>;
|
||||||
input: Input;
|
input: TypeFromProps<Input>;
|
||||||
}) => Promise<ActionResult>,
|
}) => Promise<ActionResult>,
|
||||||
) {}
|
) {}
|
||||||
|
private validator = this.myMetaData.input.validator() as Parser<
|
||||||
|
unknown,
|
||||||
|
TypeFromProps<Input>
|
||||||
|
>;
|
||||||
|
metaData = {
|
||||||
|
...this.myMetaData,
|
||||||
|
input: this.myMetaData.input.build(),
|
||||||
|
};
|
||||||
|
|
||||||
static of<WrapperData, Input>(
|
static of<WrapperData, Input extends Config<InputSpec>>(
|
||||||
metaData: ActionMetaData,
|
metaData: Omit<ActionMetaData, "input"> & { input: Input },
|
||||||
fn: (options: {
|
fn: (options: {
|
||||||
effects: Effects;
|
effects: Effects;
|
||||||
utils: Utils<WrapperData>;
|
utils: Utils<WrapperData>;
|
||||||
input: Input;
|
input: TypeFromProps<Input>;
|
||||||
}) => Promise<ActionResult>,
|
}) => Promise<ActionResult>,
|
||||||
) {
|
) {
|
||||||
return new CreatedAction<WrapperData, Input>(metaData, fn);
|
return new CreatedAction<WrapperData, Input>(metaData, fn);
|
||||||
@@ -31,7 +43,7 @@ export class CreatedAction<WrapperData, Input> {
|
|||||||
return this.fn({
|
return this.fn({
|
||||||
effects,
|
effects,
|
||||||
utils: utils<WrapperData>(effects),
|
utils: utils<WrapperData>(effects),
|
||||||
input: input as Input,
|
input: this.validator.unsafeCast(input),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,24 @@
|
|||||||
import { string } from "ts-matches";
|
import { string } from "ts-matches";
|
||||||
import { Backups } from ".";
|
import { Backups } from ".";
|
||||||
import { GenericManifest } from "../manifest/ManifestTypes";
|
import { GenericManifest } from "../manifest/ManifestTypes";
|
||||||
import { BackupOptions, ExpectedExports } from "../types";
|
import { BackupOptions } from "../types";
|
||||||
|
import { _ } from "../util";
|
||||||
|
|
||||||
export type SetupBackupsParams<M extends GenericManifest> =
|
export type SetupBackupsParams<M extends GenericManifest> = Array<
|
||||||
| [Partial<BackupOptions>, ...Array<keyof M["volumes"] & string>]
|
keyof M["volumes"] & string
|
||||||
| Array<keyof M["volumes"] & string>;
|
>;
|
||||||
|
|
||||||
export function setupBackups<M extends GenericManifest>(
|
export function setupBackups<M extends GenericManifest>(
|
||||||
...args: SetupBackupsParams<M>
|
...args: _<SetupBackupsParams<M>>
|
||||||
) {
|
) {
|
||||||
const [options, volumes] = splitOptions(args);
|
return Backups.volumes(...args).build();
|
||||||
if (!options) {
|
|
||||||
return Backups.volumes(...volumes).build();
|
|
||||||
}
|
|
||||||
return Backups.with_options(options)
|
|
||||||
.volumes(...volumes)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function splitOptions<M extends GenericManifest>(
|
export function setupBackupsOptions<M extends GenericManifest>(
|
||||||
args: SetupBackupsParams<M>,
|
options: Partial<BackupOptions>,
|
||||||
): [null | Partial<BackupOptions>, Array<keyof M["volumes"] & string>] {
|
...args: _<SetupBackupsParams<M>>
|
||||||
if (args.length > 0 && !string.test(args[0])) {
|
) {
|
||||||
const [options, ...restVolumes] = args;
|
return Backups.with_options(options)
|
||||||
return [options, restVolumes as Array<keyof M["volumes"] & string>];
|
.volumes(...args)
|
||||||
}
|
.build();
|
||||||
return [null, args as Array<keyof M["volumes"] & string>];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { _ } from "../../util";
|
||||||
export class IBuilder<A> {
|
export class IBuilder<A> {
|
||||||
protected constructor(readonly a: A) {}
|
protected constructor(readonly a: A) {}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { InputSpec, ValueSpec } from "../configTypes";
|
|||||||
import { typeFromProps } from "../../util";
|
import { typeFromProps } from "../../util";
|
||||||
import { BuilderExtract, IBuilder } from "./builder";
|
import { BuilderExtract, IBuilder } from "./builder";
|
||||||
import { Value } from "./value";
|
import { Value } from "./value";
|
||||||
|
import { _ } from "../../util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configs are the specs that are used by the os configuration form for this service.
|
* Configs are the specs that are used by the os configuration form for this service.
|
||||||
|
|||||||
@@ -10,11 +10,17 @@ import {
|
|||||||
ValueSpecDatetime,
|
ValueSpecDatetime,
|
||||||
ValueSpecList,
|
ValueSpecList,
|
||||||
ValueSpecNumber,
|
ValueSpecNumber,
|
||||||
|
ValueSpecSelect,
|
||||||
ValueSpecText,
|
ValueSpecText,
|
||||||
ValueSpecTextarea,
|
ValueSpecTextarea,
|
||||||
} from "../configTypes";
|
} from "../configTypes";
|
||||||
import { guardAll } from "../../util";
|
import { guardAll } from "../../util";
|
||||||
import { DefaultString } from "../configTypes";
|
import { DefaultString } from "../configTypes";
|
||||||
|
import { _ } from "../../util";
|
||||||
|
|
||||||
|
function flatten<A>(a: A): _<A> {
|
||||||
|
return a as _<A>;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* A value is going to be part of the form in the FE of the OS.
|
* A value is going to be part of the form in the FE of the OS.
|
||||||
* Something like a boolean, a string, a number, etc.
|
* Something like a boolean, a string, a number, etc.
|
||||||
@@ -40,179 +46,146 @@ const username = Value.string({
|
|||||||
export class Value<A extends ValueSpec> extends IBuilder<A> {
|
export class Value<A extends ValueSpec> extends IBuilder<A> {
|
||||||
static toggle(a: {
|
static toggle(a: {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string | null;
|
description: string | null;
|
||||||
warning?: string | null;
|
warning: string | null;
|
||||||
default?: boolean | null;
|
default: boolean | null;
|
||||||
}) {
|
}) {
|
||||||
return new Value({
|
return new Value({
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
default: null,
|
|
||||||
type: "toggle" as const,
|
type: "toggle" as const,
|
||||||
...a,
|
...a,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static text(a: {
|
static text<
|
||||||
name: string;
|
A extends {
|
||||||
description?: string | null;
|
name: string;
|
||||||
warning?: string | null;
|
description: string | null;
|
||||||
required: boolean;
|
warning: string | null;
|
||||||
default?: DefaultString | null;
|
required: boolean;
|
||||||
/** Default = false */
|
default: DefaultString | null;
|
||||||
masked?: boolean;
|
/** Default = false */
|
||||||
placeholder?: string | null;
|
masked: boolean;
|
||||||
minLength?: number | null;
|
placeholder: string | null;
|
||||||
maxLength?: number | null;
|
minLength: number | null;
|
||||||
patterns?: Pattern[];
|
maxLength: number | null;
|
||||||
/** Default = 'text' */
|
patterns: Pattern[];
|
||||||
inputmode?: ValueSpecText["inputmode"];
|
/** Default = 'text' */
|
||||||
}) {
|
inputmode: ValueSpecText["inputmode"];
|
||||||
|
},
|
||||||
|
>(a: A) {
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "text" as const,
|
type: "text" as const,
|
||||||
default: null,
|
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
masked: false,
|
|
||||||
placeholder: null,
|
|
||||||
minLength: null,
|
|
||||||
maxLength: null,
|
|
||||||
patterns: [],
|
|
||||||
inputmode: "text",
|
|
||||||
...a,
|
...a,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static textarea(a: {
|
static textarea(a: {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string | null;
|
description: string | null;
|
||||||
warning?: string | null;
|
warning: string | null;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
minLength?: number | null;
|
minLength: number | null;
|
||||||
maxLength?: number | null;
|
maxLength: number | null;
|
||||||
placeholder?: string | null;
|
placeholder: string | null;
|
||||||
}) {
|
}) {
|
||||||
return new Value({
|
return new Value({
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
minLength: null,
|
|
||||||
maxLength: null,
|
|
||||||
placeholder: null,
|
|
||||||
type: "textarea" as const,
|
type: "textarea" as const,
|
||||||
...a,
|
...a,
|
||||||
} as ValueSpecTextarea);
|
} as ValueSpecTextarea);
|
||||||
}
|
}
|
||||||
static number(a: {
|
static number<
|
||||||
name: string;
|
A extends {
|
||||||
description?: string | null;
|
name: string;
|
||||||
warning?: string | null;
|
description: string | null;
|
||||||
required: boolean;
|
warning: string | null;
|
||||||
default?: number | null;
|
required: boolean;
|
||||||
min?: number | null;
|
default: number | null;
|
||||||
max?: number | null;
|
min: number | null;
|
||||||
/** Default = '1' */
|
max: number | null;
|
||||||
step?: string | null;
|
/** Default = '1' */
|
||||||
integer: boolean;
|
step: string | null;
|
||||||
units?: string | null;
|
integer: boolean;
|
||||||
placeholder?: string | null;
|
units: string | null;
|
||||||
}) {
|
placeholder: string | null;
|
||||||
|
},
|
||||||
|
>(a: A) {
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "number" as const,
|
type: "number" as const,
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
default: null,
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
step: null,
|
|
||||||
units: null,
|
|
||||||
placeholder: null,
|
|
||||||
...a,
|
...a,
|
||||||
} as ValueSpecNumber);
|
});
|
||||||
}
|
}
|
||||||
static color(a: {
|
static color<
|
||||||
name: string;
|
A extends {
|
||||||
description?: string | null;
|
name: string;
|
||||||
warning?: string | null;
|
description: string | null;
|
||||||
required: boolean;
|
warning: string | null;
|
||||||
default?: number | null;
|
required: boolean;
|
||||||
}) {
|
default: string | null;
|
||||||
|
},
|
||||||
|
>(a: A) {
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "color" as const,
|
type: "color" as const,
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
default: null,
|
|
||||||
...a,
|
...a,
|
||||||
} as ValueSpecColor);
|
});
|
||||||
}
|
}
|
||||||
static datetime(a: {
|
static datetime(a: {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string | null;
|
description: string | null;
|
||||||
warning?: string | null;
|
warning: string | null;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
/** Default = 'datetime-local' */
|
/** Default = 'datetime-local' */
|
||||||
inputmode?: ValueSpecDatetime["inputmode"];
|
inputmode: ValueSpecDatetime["inputmode"];
|
||||||
min?: string | null;
|
min: string | null;
|
||||||
max?: string | null;
|
max: string | null;
|
||||||
step?: string | null;
|
step: string | null;
|
||||||
default?: number | null;
|
default: string | null;
|
||||||
}) {
|
}) {
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "datetime" as const,
|
type: "datetime" as const,
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
inputmode: "datetime-local",
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
step: null,
|
|
||||||
default: null,
|
|
||||||
...a,
|
...a,
|
||||||
} as ValueSpecDatetime);
|
});
|
||||||
}
|
}
|
||||||
static select<B extends Record<string, string>>(a: {
|
static select<
|
||||||
name: string;
|
A extends {
|
||||||
description?: string | null;
|
name: string;
|
||||||
warning?: string | null;
|
description: string | null;
|
||||||
required: boolean;
|
warning: string | null;
|
||||||
default?: string | null;
|
required: boolean;
|
||||||
values: B;
|
default: string | null;
|
||||||
}) {
|
values: { [key: string]: string };
|
||||||
|
},
|
||||||
|
>(a: A) {
|
||||||
return new Value({
|
return new Value({
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
default: null,
|
|
||||||
type: "select" as const,
|
type: "select" as const,
|
||||||
...a,
|
...a,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static multiselect<Values extends Record<string, string>>(a: {
|
static multiselect<
|
||||||
name: string;
|
A extends {
|
||||||
description?: string | null;
|
name: string;
|
||||||
warning?: string | null;
|
description: string | null;
|
||||||
default: string[];
|
warning: string | null;
|
||||||
values: Values;
|
default: string[];
|
||||||
minLength?: number | null;
|
values: Values;
|
||||||
maxLength?: number | null;
|
minLength: number | null;
|
||||||
}) {
|
maxLength: number | null;
|
||||||
|
},
|
||||||
|
Values extends Record<string, string>,
|
||||||
|
>(a: A) {
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "multiselect" as const,
|
type: "multiselect" as const,
|
||||||
minLength: null,
|
|
||||||
maxLength: null,
|
|
||||||
warning: null,
|
|
||||||
description: null,
|
|
||||||
...a,
|
...a,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static object<Spec extends Config<InputSpec>>(
|
static object<Spec extends Config<InputSpec>>(
|
||||||
a: {
|
a: {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string | null;
|
description: string | null;
|
||||||
warning?: string | null;
|
warning: string | null;
|
||||||
},
|
},
|
||||||
previousSpec: Spec,
|
previousSpec: Spec,
|
||||||
) {
|
) {
|
||||||
const spec = previousSpec.build() as BuilderExtract<Spec>;
|
const spec = previousSpec.build() as BuilderExtract<Spec>;
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "object" as const,
|
type: "object" as const,
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
...a,
|
...a,
|
||||||
spec,
|
spec,
|
||||||
});
|
});
|
||||||
@@ -222,19 +195,16 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
|
|||||||
>(
|
>(
|
||||||
a: {
|
a: {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string | null;
|
description: string | null;
|
||||||
warning?: string | null;
|
warning: string | null;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
default?: string | null;
|
default: string | null;
|
||||||
},
|
},
|
||||||
aVariants: V,
|
aVariants: V,
|
||||||
) {
|
) {
|
||||||
const variants = aVariants.build() as BuilderExtract<V>;
|
const variants = aVariants.build() as BuilderExtract<V>;
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "union" as const,
|
type: "union" as const,
|
||||||
description: null,
|
|
||||||
warning: null,
|
|
||||||
default: null,
|
|
||||||
...a,
|
...a,
|
||||||
variants,
|
variants,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ export interface Container {
|
|||||||
image: string;
|
image: string;
|
||||||
/** These should match the manifest data volumes */
|
/** These should match the manifest data volumes */
|
||||||
mounts: Record<string, string>;
|
mounts: Record<string, string>;
|
||||||
/** if greater */
|
/** Default is 64mb */
|
||||||
shmSizeMb?: number;
|
shmSizeMb?: `${number}${"mb" | "gb" | "b" | "kb"}`;
|
||||||
/** if more than 30s to shutdown */
|
/** if more than 30s to shutdown */
|
||||||
sigtermTimeout?: string;
|
sigtermTimeout?: `${number}${"s" | "m" | "h"}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ManifestVersion = ValidEmVer;
|
export type ManifestVersion = ValidEmVer;
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ export function setupManifest<
|
|||||||
Id extends string,
|
Id extends string,
|
||||||
Version extends ManifestVersion,
|
Version extends ManifestVersion,
|
||||||
Dependencies extends Record<string, unknown>,
|
Dependencies extends Record<string, unknown>,
|
||||||
>(
|
Volumes extends Record<string, unknown>,
|
||||||
manifest: GenericManifest & {
|
Manifest extends GenericManifest & {
|
||||||
dependencies: Dependencies;
|
dependencies: Dependencies;
|
||||||
id: Id;
|
id: Id;
|
||||||
version: Version;
|
version: Version;
|
||||||
|
volumes: Volumes;
|
||||||
},
|
},
|
||||||
): GenericManifest & { dependencies: Dependencies; id: Id; version: Version } {
|
>(manifest: Manifest): Manifest {
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ExpectedExports, Properties } from "../types";
|
import { ExpectedExports, Properties } from "../types";
|
||||||
import { Utils, utils } from "../util";
|
|
||||||
import "../util/extensions";
|
|
||||||
import { PropertyGroup } from "./PropertyGroup";
|
import { PropertyGroup } from "./PropertyGroup";
|
||||||
import { PropertyString } from "./PropertyString";
|
import { PropertyString } from "./PropertyString";
|
||||||
export { PropertyGroup } from "./PropertyGroup";
|
export { PropertyGroup } from "./PropertyGroup";
|
||||||
|
|||||||
@@ -24,23 +24,24 @@ describe("builder tests", () => {
|
|||||||
minLength: null,
|
minLength: null,
|
||||||
maxLength: null,
|
maxLength: null,
|
||||||
patterns: [],
|
patterns: [],
|
||||||
|
inputmode: "text",
|
||||||
}),
|
}),
|
||||||
}).build();
|
}).build();
|
||||||
expect(JSON.stringify(bitcoinPropertiesBuilt)).toEqual(
|
expect(JSON.stringify(bitcoinPropertiesBuilt)).toEqual(
|
||||||
/*json*/ `{
|
/*json*/ `{
|
||||||
"peer-tor-address": {
|
"peer-tor-address": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
"name": "Peer tor address",
|
||||||
"default": null,
|
"default": null,
|
||||||
"description": "The Tor address of the peer interface",
|
"description": "The Tor address of the peer interface",
|
||||||
"warning": null,
|
"warning": null,
|
||||||
|
"required": true,
|
||||||
"masked": true,
|
"masked": true,
|
||||||
"placeholder": null,
|
"placeholder": null,
|
||||||
"minLength": null,
|
"minLength": null,
|
||||||
"maxLength": null,
|
"maxLength": null,
|
||||||
"patterns": [],
|
"patterns": [],
|
||||||
"inputmode":"text",
|
"inputmode":"text"
|
||||||
"name": "Peer tor address",
|
|
||||||
"required": true
|
|
||||||
}}`
|
}}`
|
||||||
.replaceAll("\n", " ")
|
.replaceAll("\n", " ")
|
||||||
.replaceAll(/\s{2,}/g, "")
|
.replaceAll(/\s{2,}/g, "")
|
||||||
@@ -53,6 +54,9 @@ describe("values", () => {
|
|||||||
test("toggle", () => {
|
test("toggle", () => {
|
||||||
const value = Value.toggle({
|
const value = Value.toggle({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast(false);
|
validator.unsafeCast(false);
|
||||||
@@ -62,6 +66,33 @@ describe("values", () => {
|
|||||||
const value = Value.text({
|
const value = Value.text({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
masked: false,
|
||||||
|
placeholder: null,
|
||||||
|
minLength: null,
|
||||||
|
maxLength: null,
|
||||||
|
patterns: [],
|
||||||
|
inputmode: "text",
|
||||||
|
});
|
||||||
|
const validator = value.validator();
|
||||||
|
validator.unsafeCast("test text");
|
||||||
|
testOutput<typeof validator._TYPE, string | null | undefined>()(null);
|
||||||
|
});
|
||||||
|
test("text", () => {
|
||||||
|
const value = Value.text({
|
||||||
|
name: "Testing",
|
||||||
|
required: true,
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
masked: false,
|
||||||
|
placeholder: null,
|
||||||
|
minLength: null,
|
||||||
|
maxLength: null,
|
||||||
|
patterns: [],
|
||||||
|
inputmode: "text",
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast("test text");
|
validator.unsafeCast("test text");
|
||||||
@@ -71,15 +102,25 @@ describe("values", () => {
|
|||||||
const value = Value.color({
|
const value = Value.color({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast("#000000");
|
validator.unsafeCast("#000000");
|
||||||
testOutput<typeof validator._TYPE, string>()(null);
|
testOutput<typeof validator._TYPE, string | null | undefined>()(null);
|
||||||
});
|
});
|
||||||
test("datetime", () => {
|
test("datetime", () => {
|
||||||
const value = Value.datetime({
|
const value = Value.datetime({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
inputmode: "date",
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
step: null,
|
||||||
|
default: null,
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast("2021-01-01");
|
validator.unsafeCast("2021-01-01");
|
||||||
@@ -89,6 +130,11 @@ describe("values", () => {
|
|||||||
const value = Value.textarea({
|
const value = Value.textarea({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
minLength: null,
|
||||||
|
maxLength: null,
|
||||||
|
placeholder: null,
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast("test text");
|
validator.unsafeCast("test text");
|
||||||
@@ -99,12 +145,38 @@ describe("values", () => {
|
|||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
integer: false,
|
integer: false,
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
step: null,
|
||||||
|
units: null,
|
||||||
|
placeholder: null,
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast(2);
|
validator.unsafeCast(2);
|
||||||
testOutput<typeof validator._TYPE, number>()(null);
|
testOutput<typeof validator._TYPE, number | null | undefined>()(null);
|
||||||
});
|
});
|
||||||
test("select", () => {
|
test("select", () => {
|
||||||
|
const value = Value.select({
|
||||||
|
name: "Testing",
|
||||||
|
required: true,
|
||||||
|
values: {
|
||||||
|
a: "A",
|
||||||
|
b: "B",
|
||||||
|
},
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
|
const validator = value.validator();
|
||||||
|
validator.unsafeCast("a");
|
||||||
|
validator.unsafeCast("b");
|
||||||
|
expect(() => validator.unsafeCast(null)).toThrowError();
|
||||||
|
testOutput<typeof validator._TYPE, "a" | "b">()(null);
|
||||||
|
});
|
||||||
|
test("nullable select", () => {
|
||||||
const value = Value.select({
|
const value = Value.select({
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: false,
|
required: false,
|
||||||
@@ -112,11 +184,15 @@ describe("values", () => {
|
|||||||
a: "A",
|
a: "A",
|
||||||
b: "B",
|
b: "B",
|
||||||
},
|
},
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast("a");
|
validator.unsafeCast("a");
|
||||||
validator.unsafeCast("b");
|
validator.unsafeCast("b");
|
||||||
testOutput<typeof validator._TYPE, "a" | "b">()(null);
|
validator.unsafeCast(null);
|
||||||
|
testOutput<typeof validator._TYPE, "a" | "b" | null | undefined>()(null);
|
||||||
});
|
});
|
||||||
test("multiselect", () => {
|
test("multiselect", () => {
|
||||||
const value = Value.multiselect({
|
const value = Value.multiselect({
|
||||||
@@ -126,6 +202,10 @@ describe("values", () => {
|
|||||||
b: "B",
|
b: "B",
|
||||||
},
|
},
|
||||||
default: [],
|
default: [],
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
minLength: null,
|
||||||
|
maxLength: null,
|
||||||
});
|
});
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast([]);
|
validator.unsafeCast([]);
|
||||||
@@ -136,10 +216,15 @@ describe("values", () => {
|
|||||||
const value = Value.object(
|
const value = Value.object(
|
||||||
{
|
{
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
},
|
},
|
||||||
Config.of({
|
Config.of({
|
||||||
a: Value.toggle({
|
a: Value.toggle({
|
||||||
name: "test",
|
name: "test",
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -152,21 +237,30 @@ describe("values", () => {
|
|||||||
{
|
{
|
||||||
name: "Testing",
|
name: "Testing",
|
||||||
required: true,
|
required: true,
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
},
|
},
|
||||||
Variants.of({
|
Variants.of({
|
||||||
a: {
|
a: {
|
||||||
name: "a",
|
name: "a",
|
||||||
spec: Config.of({ b: Value.toggle({ name: "b" }) }),
|
spec: Config.of({
|
||||||
|
b: Value.toggle({
|
||||||
|
name: "b",
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const validator = value.validator();
|
const validator = value.validator();
|
||||||
validator.unsafeCast({ unionSelectKey: "a", unionValueKey: { b: false } });
|
validator.unsafeCast({ unionSelectKey: "a", unionValueKey: { b: false } });
|
||||||
type Test = typeof validator._TYPE;
|
type Test = typeof validator._TYPE;
|
||||||
testOutput<
|
testOutput<Test, { unionSelectKey: "a"; unionValueKey: { b: boolean } }>()(
|
||||||
Test,
|
null,
|
||||||
{ unionSelectKey: "a" } & { unionValueKey: { b: boolean } }
|
);
|
||||||
>()(null);
|
|
||||||
});
|
});
|
||||||
test("list", () => {
|
test("list", () => {
|
||||||
const value = Value.list(
|
const value = Value.list(
|
||||||
@@ -193,7 +287,14 @@ describe("Builder List", () => {
|
|||||||
name: "test",
|
name: "test",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
spec: Config.of({ test: Value.toggle({ name: "test" }) }),
|
spec: Config.of({
|
||||||
|
test: Value.toggle({
|
||||||
|
name: "test",
|
||||||
|
description: null,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -230,3 +331,128 @@ describe("Builder List", () => {
|
|||||||
testOutput<typeof validator._TYPE, number[]>()(null);
|
testOutput<typeof validator._TYPE, number[]>()(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Nested nullable values", () => {
|
||||||
|
test("Testing text", () => {
|
||||||
|
const value = Config.of({
|
||||||
|
a: Value.text({
|
||||||
|
name: "Temp Name",
|
||||||
|
description:
|
||||||
|
"If no name is provided, the name from config will be used",
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
warning: null,
|
||||||
|
masked: false,
|
||||||
|
placeholder: null,
|
||||||
|
minLength: null,
|
||||||
|
maxLength: null,
|
||||||
|
patterns: [],
|
||||||
|
inputmode: "text",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const validator = value.validator();
|
||||||
|
validator.unsafeCast({ a: null });
|
||||||
|
validator.unsafeCast({ a: "test" });
|
||||||
|
expect(() => validator.unsafeCast({ a: 4 })).toThrowError();
|
||||||
|
testOutput<typeof validator._TYPE, { a: string | null | undefined }>()(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test("Testing number", () => {
|
||||||
|
const value = Config.of({
|
||||||
|
a: Value.number({
|
||||||
|
name: "Temp Name",
|
||||||
|
description:
|
||||||
|
"If no name is provided, the name from config will be used",
|
||||||
|
required: false,
|
||||||
|
warning: null,
|
||||||
|
placeholder: null,
|
||||||
|
integer: false,
|
||||||
|
default: null,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
step: null,
|
||||||
|
units: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const validator = value.validator();
|
||||||
|
validator.unsafeCast({ a: null });
|
||||||
|
validator.unsafeCast({ a: 5 });
|
||||||
|
expect(() => validator.unsafeCast({ a: "4" })).toThrowError();
|
||||||
|
testOutput<typeof validator._TYPE, { a: number | null | undefined }>()(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test("Testing color", () => {
|
||||||
|
const value = Config.of({
|
||||||
|
a: Value.color({
|
||||||
|
name: "Temp Name",
|
||||||
|
description:
|
||||||
|
"If no name is provided, the name from config will be used",
|
||||||
|
required: false,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const validator = value.validator();
|
||||||
|
validator.unsafeCast({ a: null });
|
||||||
|
validator.unsafeCast({ a: "5" });
|
||||||
|
expect(() => validator.unsafeCast({ a: 4 })).toThrowError();
|
||||||
|
testOutput<typeof validator._TYPE, { a: string | null | undefined }>()(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test("Testing select", () => {
|
||||||
|
const value = Config.of({
|
||||||
|
a: Value.select({
|
||||||
|
name: "Temp Name",
|
||||||
|
description:
|
||||||
|
"If no name is provided, the name from config will be used",
|
||||||
|
required: false,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
values: {
|
||||||
|
a: "A",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const higher = Value.select({
|
||||||
|
name: "Temp Name",
|
||||||
|
description: "If no name is provided, the name from config will be used",
|
||||||
|
required: false,
|
||||||
|
warning: null,
|
||||||
|
default: null,
|
||||||
|
values: {
|
||||||
|
a: "A",
|
||||||
|
},
|
||||||
|
}).build();
|
||||||
|
|
||||||
|
const validator = value.validator();
|
||||||
|
validator.unsafeCast({ a: null });
|
||||||
|
validator.unsafeCast({ a: "a" });
|
||||||
|
expect(() => validator.unsafeCast({ a: "4" })).toThrowError();
|
||||||
|
testOutput<typeof validator._TYPE, { a: "a" | null | undefined }>()(null);
|
||||||
|
});
|
||||||
|
test("Testing multiselect", () => {
|
||||||
|
const value = Config.of({
|
||||||
|
a: Value.multiselect({
|
||||||
|
name: "Temp Name",
|
||||||
|
description:
|
||||||
|
"If no name is provided, the name from config will be used",
|
||||||
|
|
||||||
|
warning: null,
|
||||||
|
default: [],
|
||||||
|
values: {
|
||||||
|
a: "A",
|
||||||
|
},
|
||||||
|
minLength: null,
|
||||||
|
maxLength: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const validator = value.validator();
|
||||||
|
validator.unsafeCast({ a: [] });
|
||||||
|
validator.unsafeCast({ a: ["a"] });
|
||||||
|
expect(() => validator.unsafeCast({ a: "4" })).toThrowError();
|
||||||
|
testOutput<typeof validator._TYPE, { a: "a"[] }>()(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ const todo = <A>(): A => {
|
|||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
describe("wrapperData", () => {
|
describe("wrapperData", () => {
|
||||||
test.skip("types", async () => {
|
test.skip("types", async () => {
|
||||||
utils<WrapperType>(todo<T.Effects>()).setWrapperData(
|
utils<WrapperType>(todo<T.Effects>()).setOwnWrapperData(
|
||||||
"/config/someValue",
|
"/config/someValue",
|
||||||
"someValue",
|
"someValue",
|
||||||
);
|
);
|
||||||
utils<WrapperType>(todo<T.Effects>()).setWrapperData(
|
utils<WrapperType>(todo<T.Effects>()).setOwnWrapperData(
|
||||||
"/config/someValue",
|
"/config/someValue",
|
||||||
|
|
||||||
// @ts-expect-error Type is wrong for the setting value
|
// @ts-expect-error Type is wrong for the setting value
|
||||||
5,
|
5,
|
||||||
);
|
);
|
||||||
utils<WrapperType>(todo<T.Effects>()).setWrapperData(
|
utils<WrapperType>(todo<T.Effects>()).setOwnWrapperData(
|
||||||
// @ts-expect-error Path is wrong
|
// @ts-expect-error Path is wrong
|
||||||
"/config/someVae3lue",
|
"/config/someVae3lue",
|
||||||
"someValue",
|
"someValue",
|
||||||
@@ -45,22 +45,22 @@ describe("wrapperData", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
(await utils<WrapperType>(todo<T.Effects>())
|
(await utils<WrapperType>(todo<T.Effects>())
|
||||||
.getWrapperData("/config/someValue")
|
.getOwnWrapperData("/config/someValue")
|
||||||
.const()) satisfies string;
|
.const()) satisfies string;
|
||||||
(await utils<WrapperType>(todo<T.Effects>())
|
(await utils<WrapperType>(todo<T.Effects>())
|
||||||
.getWrapperData("/config")
|
.getOwnWrapperData("/config")
|
||||||
.const()) satisfies WrapperType["config"];
|
.const()) satisfies WrapperType["config"];
|
||||||
await utils<WrapperType>(todo<T.Effects>())
|
await utils<WrapperType>(todo<T.Effects>())
|
||||||
// @ts-expect-error Path is wrong
|
// @ts-expect-error Path is wrong
|
||||||
.getWrapperData("/config/somdsfeValue")
|
.getOwnWrapperData("/config/somdsfeValue")
|
||||||
.const();
|
.const();
|
||||||
(await utils<WrapperType>(todo<T.Effects>())
|
(await utils<WrapperType>(todo<T.Effects>())
|
||||||
.getWrapperData("/config/someValue")
|
.getOwnWrapperData("/config/someValue")
|
||||||
// @ts-expect-error satisfies type is wrong
|
// @ts-expect-error satisfies type is wrong
|
||||||
.const()) satisfies number;
|
.const()) satisfies number;
|
||||||
(await utils<WrapperType>(todo<T.Effects>())
|
(await utils<WrapperType>(todo<T.Effects>())
|
||||||
// @ts-expect-error Path is wrong
|
// @ts-expect-error Path is wrong
|
||||||
.getWrapperData("/config/")
|
.getOwnWrapperData("/config/")
|
||||||
.const()) satisfies WrapperType["config"];
|
.const()) satisfies WrapperType["config"];
|
||||||
|
|
||||||
(await todo<T.Effects>().getWrapperData<WrapperType, "/config/someValue">({
|
(await todo<T.Effects>().getWrapperData<WrapperType, "/config/someValue">({
|
||||||
|
|||||||
@@ -437,9 +437,11 @@ export type MigrationRes = {
|
|||||||
|
|
||||||
export type ActionResult = {
|
export type ActionResult = {
|
||||||
message: string;
|
message: string;
|
||||||
value: null | string;
|
value: null | {
|
||||||
copyable: boolean;
|
value: string;
|
||||||
qr: boolean;
|
copyable: boolean;
|
||||||
|
qr: boolean;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
export type SetResult = {
|
export type SetResult = {
|
||||||
/** These are the unix process signals */
|
/** These are the unix process signals */
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
export type UnionToIntersection<T> = (
|
|
||||||
T extends any ? (x: T) => any : never
|
|
||||||
) extends (x: infer R) => any
|
|
||||||
? R
|
|
||||||
: never;
|
|
||||||
type _<A> = A;
|
|
||||||
type UnReadonly<A> = { -readonly [k in keyof A]: A[k] };
|
|
||||||
declare global {
|
|
||||||
interface Object {
|
|
||||||
entries<T extends {}>(
|
|
||||||
this: T,
|
|
||||||
): Array<{ -readonly [K in keyof T]: [K, T[K]] }[keyof T]>;
|
|
||||||
values<T extends {}>(this: T): Array<T[keyof T]>;
|
|
||||||
keys<T extends {}>(this: T): Array<keyof T>;
|
|
||||||
}
|
|
||||||
interface Array<T> {
|
|
||||||
fromEntries(): UnionToIntersection<
|
|
||||||
T extends [infer Key, infer Value]
|
|
||||||
? { [k in Key extends string | number ? Key : never]: Value }
|
|
||||||
: never
|
|
||||||
>;
|
|
||||||
assignObject(): UnionToIntersection<T & {}>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.prototype.entries = function () {
|
|
||||||
return Object.entries(this) as any;
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.prototype.values = function () {
|
|
||||||
return Object.values(this) as any;
|
|
||||||
};
|
|
||||||
Object.prototype.keys = function () {
|
|
||||||
return Object.keys(this) as any;
|
|
||||||
};
|
|
||||||
|
|
||||||
Array.prototype.fromEntries = function () {
|
|
||||||
return Object.fromEntries(this) as any;
|
|
||||||
};
|
|
||||||
|
|
||||||
Array.prototype.assignObject = function () {
|
|
||||||
return Object.assign({}, ...this) as any;
|
|
||||||
};
|
|
||||||
@@ -19,7 +19,7 @@ export class WrapperData<WrapperData, Path extends string> {
|
|||||||
callback: this.effects.restart,
|
callback: this.effects.restart,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
first() {
|
once() {
|
||||||
return this.effects.getWrapperData<WrapperData, Path>({
|
return this.effects.getWrapperData<WrapperData, Path>({
|
||||||
...this.options,
|
...this.options,
|
||||||
path: this.path as any,
|
path: this.path as any,
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ export { deepEqual } from "./deepEqual";
|
|||||||
export { deepMerge } from "./deepMerge";
|
export { deepMerge } from "./deepMerge";
|
||||||
export { once } from "./once";
|
export { once } from "./once";
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
export type FlattenIntersection<T> =
|
||||||
|
T extends ArrayLike<any> ? T :
|
||||||
|
T extends object ? {} & {[P in keyof T]: T[P]} :
|
||||||
|
T;
|
||||||
|
|
||||||
|
export type _<T> = FlattenIntersection<T>;
|
||||||
|
|
||||||
/** Used to check if the file exists before hand */
|
/** Used to check if the file exists before hand */
|
||||||
export const exists = (
|
export const exists = (
|
||||||
effects: T.Effects,
|
effects: T.Effects,
|
||||||
@@ -53,10 +61,13 @@ export type Utils<WD> = {
|
|||||||
data: A,
|
data: A,
|
||||||
) => ReturnType<FileHelper<A>["write"]>;
|
) => ReturnType<FileHelper<A>["write"]>;
|
||||||
getWrapperData: <Path extends string>(
|
getWrapperData: <Path extends string>(
|
||||||
|
packageId: string,
|
||||||
path: T.EnsureWrapperDataPath<WD, Path>,
|
path: T.EnsureWrapperDataPath<WD, Path>,
|
||||||
options?: WrapperDataOptionals<WD, Path>,
|
|
||||||
) => WrapperData<WD, Path>;
|
) => WrapperData<WD, Path>;
|
||||||
setWrapperData: <Path extends string | never>(
|
getOwnWrapperData: <Path extends string>(
|
||||||
|
path: T.EnsureWrapperDataPath<WD, Path>,
|
||||||
|
) => WrapperData<WD, Path>;
|
||||||
|
setOwnWrapperData: <Path extends string | never>(
|
||||||
path: T.EnsureWrapperDataPath<WD, Path>,
|
path: T.EnsureWrapperDataPath<WD, Path>,
|
||||||
value: ExtractWrapperData<WD, Path>,
|
value: ExtractWrapperData<WD, Path>,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
@@ -89,15 +100,14 @@ export const utils = <WrapperData = never>(
|
|||||||
fileHelper.write(data, effects),
|
fileHelper.write(data, effects),
|
||||||
exists: (props: { path: string; volumeId: string }) => exists(effects, props),
|
exists: (props: { path: string; volumeId: string }) => exists(effects, props),
|
||||||
nullIfEmpty,
|
nullIfEmpty,
|
||||||
getWrapperData: <Path extends string>(
|
getWrapperData: <WrapperData = never, Path extends string = never>(
|
||||||
|
packageId: string,
|
||||||
path: T.EnsureWrapperDataPath<WrapperData, Path>,
|
path: T.EnsureWrapperDataPath<WrapperData, Path>,
|
||||||
options: {
|
) => getWrapperData<WrapperData, Path>(effects, path as any, { packageId }),
|
||||||
validator?: Parser<unknown, ExtractWrapperData<WrapperData, Path>>;
|
getOwnWrapperData: <Path extends string>(
|
||||||
/** Defaults to what ever the package currently in */
|
path: T.EnsureWrapperDataPath<WrapperData, Path>,
|
||||||
packageId?: string | undefined;
|
) => getWrapperData<WrapperData, Path>(effects, path as any),
|
||||||
} = {},
|
setOwnWrapperData: <Path extends string | never>(
|
||||||
) => getWrapperData<WrapperData, Path>(effects, path as any, options),
|
|
||||||
setWrapperData: <Path extends string | never>(
|
|
||||||
path: T.EnsureWrapperDataPath<WrapperData, Path>,
|
path: T.EnsureWrapperDataPath<WrapperData, Path>,
|
||||||
value: ExtractWrapperData<WrapperData, Path>,
|
value: ExtractWrapperData<WrapperData, Path>,
|
||||||
) => effects.setWrapperData<WrapperData, Path>({ value, path: path as any }),
|
) => effects.setWrapperData<WrapperData, Path>({ value, path: path as any }),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
InputSpec,
|
InputSpec,
|
||||||
} from "../config/configTypes";
|
} from "../config/configTypes";
|
||||||
import { Config } from "../config/builder/config";
|
import { Config } from "../config/builder/config";
|
||||||
|
import { _ } from "../util";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
string,
|
string,
|
||||||
@@ -18,6 +19,7 @@ const {
|
|||||||
number,
|
number,
|
||||||
literals,
|
literals,
|
||||||
boolean,
|
boolean,
|
||||||
|
nill,
|
||||||
} = matches;
|
} = matches;
|
||||||
|
|
||||||
type TypeToggle = "toggle";
|
type TypeToggle = "toggle";
|
||||||
@@ -34,9 +36,7 @@ type TypeUnion = "union";
|
|||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardDefaultRequired<A, Type> =
|
type GuardDefaultRequired<A, Type> =
|
||||||
A extends { default: unknown } ? Type :
|
A extends { required: false; default: null | undefined | never } ? Type | undefined | null:
|
||||||
A extends { required: false } ? Type :
|
|
||||||
A extends { required: true } ? Type | null | undefined :
|
|
||||||
Type
|
Type
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -55,11 +55,12 @@ type GuardTextarea<A> =
|
|||||||
type GuardToggle<A> =
|
type GuardToggle<A> =
|
||||||
A extends { type: TypeToggle } ? GuardDefaultRequired<A, boolean> :
|
A extends { type: TypeToggle } ? GuardDefaultRequired<A, boolean> :
|
||||||
unknown
|
unknown
|
||||||
|
|
||||||
|
type TrueKeyOf<T> = _<T> extends Record<string, unknown> ? keyof T : never;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardObject<A> =
|
type GuardObject<A> =
|
||||||
A extends { type: TypeObject, spec: infer B } ? (
|
A extends { type: TypeObject, spec: infer B } ? (
|
||||||
B extends Record<string, unknown> ? { [K in keyof B & string]: _<GuardAll<B[K]>> } :
|
{ [K in TrueKeyOf<B> & string]: _<GuardAll<B[K]>> }
|
||||||
{ _error: "Invalid Spec" }
|
|
||||||
) :
|
) :
|
||||||
unknown
|
unknown
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -70,7 +71,7 @@ export type GuardList<A> =
|
|||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardSelect<A> =
|
type GuardSelect<A> =
|
||||||
A extends { type: TypeSelect, values: infer B } ? (
|
A extends { type: TypeSelect, values: infer B } ? (
|
||||||
B extends Record<string, string> ? keyof B : never
|
GuardDefaultRequired<A, TrueKeyOf<B>>
|
||||||
) :
|
) :
|
||||||
unknown
|
unknown
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -85,11 +86,19 @@ type GuardColor<A> =
|
|||||||
type GuardDatetime<A> =
|
type GuardDatetime<A> =
|
||||||
A extends { type: TypeDatetime } ? GuardDefaultRequired<A, string> :
|
A extends { type: TypeDatetime } ? GuardDefaultRequired<A, string> :
|
||||||
unknown
|
unknown
|
||||||
|
type AsString<A> = A extends
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| bigint
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
? `${A}`
|
||||||
|
: "UnknownValue";
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type VariantValue<A> =
|
type VariantValue<A> =
|
||||||
A extends { name: string, spec: infer B } ? TypeFromProps<B> :
|
A extends { name: string, spec: infer B } ? TypeFromProps<_<B>> :
|
||||||
never
|
`neverVariantValue${AsString<A>}`
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardUnion<A> =
|
type GuardUnion<A> =
|
||||||
A extends { type: TypeUnion, variants: infer Variants & Record<string, unknown> } ? (
|
A extends { type: TypeUnion, variants: infer Variants & Record<string, unknown> } ? (
|
||||||
@@ -97,7 +106,6 @@ type GuardUnion<A> =
|
|||||||
) :
|
) :
|
||||||
unknown
|
unknown
|
||||||
|
|
||||||
type _<T> = T;
|
|
||||||
export type GuardAll<A> = GuardNumber<A> &
|
export type GuardAll<A> = GuardNumber<A> &
|
||||||
GuardText<A> &
|
GuardText<A> &
|
||||||
GuardTextarea<A> &
|
GuardTextarea<A> &
|
||||||
@@ -114,7 +122,6 @@ export type TypeFromProps<A> =
|
|||||||
A extends Config<infer B> ? TypeFromProps<B> :
|
A extends Config<infer B> ? TypeFromProps<B> :
|
||||||
A extends Record<string, unknown> ? { [K in keyof A & string]: _<GuardAll<A[K]>> } :
|
A extends Record<string, unknown> ? { [K in keyof A & string]: _<GuardAll<A[K]>> } :
|
||||||
unknown;
|
unknown;
|
||||||
|
|
||||||
const isType = object({ type: string });
|
const isType = object({ type: string });
|
||||||
const matchVariant = object({
|
const matchVariant = object({
|
||||||
name: string,
|
name: string,
|
||||||
@@ -122,7 +129,13 @@ const matchVariant = object({
|
|||||||
});
|
});
|
||||||
const recordString = dictionary([string, unknown]);
|
const recordString = dictionary([string, unknown]);
|
||||||
const matchDefault = object({ default: unknown });
|
const matchDefault = object({ default: unknown });
|
||||||
const matchRequired = object({ required: literals(false) });
|
const matchRequired = object(
|
||||||
|
{
|
||||||
|
required: literals(false),
|
||||||
|
default: nill,
|
||||||
|
},
|
||||||
|
["default"],
|
||||||
|
);
|
||||||
const matchInteger = object({ integer: literals(true) });
|
const matchInteger = object({ integer: literals(true) });
|
||||||
const matchSpec = object({ spec: recordString });
|
const matchSpec = object({ spec: recordString });
|
||||||
const matchUnion = object({
|
const matchUnion = object({
|
||||||
@@ -139,7 +152,7 @@ function withInteger(parser: Parser<unknown, number>, value: unknown) {
|
|||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
function requiredParser<A>(parser: Parser<unknown, A>, value: unknown) {
|
function requiredParser<A>(parser: Parser<unknown, A>, value: unknown) {
|
||||||
if (!matchRequired.test(value)) return parser.optional();
|
if (matchRequired.test(value)) return parser.optional();
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ export default async function makeFileContentFromOld(
|
|||||||
warning: value.warning || null,
|
warning: value.warning || null,
|
||||||
required: !(value.nullable || false),
|
required: !(value.nullable || false),
|
||||||
placeholder: value.placeholder || null,
|
placeholder: value.placeholder || null,
|
||||||
|
maxLength: null,
|
||||||
|
minLength: null,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
@@ -81,6 +83,7 @@ export default async function makeFileContentFromOld(
|
|||||||
required: !(value.nullable || false),
|
required: !(value.nullable || false),
|
||||||
masked: value.masked || false,
|
masked: value.masked || false,
|
||||||
placeholder: value.placeholder || null,
|
placeholder: value.placeholder || null,
|
||||||
|
inputmode: "text",
|
||||||
patterns: value.pattern
|
patterns: value.pattern
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user