mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 10:21:55 +00:00
chore: something about fixing the builder
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import camelCase from "lodash/camelCase";
|
||||
import * as fs from "fs";
|
||||
import { string } from "ts-matches";
|
||||
|
||||
export async function writeConvertedFile(
|
||||
file: string,
|
||||
@@ -97,14 +98,22 @@ export default async function makeFileContent(
|
||||
)})`;
|
||||
}
|
||||
case "enum": {
|
||||
const allValueNames = new Set(
|
||||
...value?.spec?.["values"] || [],
|
||||
...Object.keys(value?.spec?.["value-names"] || {})
|
||||
);
|
||||
const values = Object.fromEntries(
|
||||
Array.from(allValueNames)
|
||||
.filter(string.test)
|
||||
.map(key => [key, value?.spec?.["value-names"]?.[key] || key])
|
||||
)
|
||||
return `Value.select(${JSON.stringify(
|
||||
{
|
||||
name: value.name || null,
|
||||
description: value.description || null,
|
||||
warning: value.warning || null,
|
||||
default: value.default || null,
|
||||
values: value.values || null,
|
||||
valueNames: value["value-names"] || null,
|
||||
values,
|
||||
},
|
||||
null,
|
||||
2
|
||||
@@ -205,15 +214,23 @@ export default async function makeFileContent(
|
||||
)})`;
|
||||
}
|
||||
case "enum": {
|
||||
const allValueNames = new Set(
|
||||
...value?.spec?.["values"] || [],
|
||||
...Object.keys(value?.spec?.["value-names"] || {})
|
||||
);
|
||||
const values = Object.fromEntries(
|
||||
Array.from(allValueNames)
|
||||
.filter(string.test)
|
||||
.map(key => [key, value?.spec?.["value-names"]?.[key] || key])
|
||||
)
|
||||
return `Value.multiselect(${JSON.stringify(
|
||||
{
|
||||
name: value.name || null,
|
||||
range: value.range || null,
|
||||
values: value?.spec?.["values"] || null,
|
||||
valueNames: value?.spec?.["value-names"] || {},
|
||||
default: value.default || null,
|
||||
description: value.description || null,
|
||||
warning: value.warning || null,
|
||||
values,
|
||||
},
|
||||
null,
|
||||
2
|
||||
@@ -230,8 +247,8 @@ export default async function makeFileContent(
|
||||
spec: {
|
||||
spec: ${specName},
|
||||
displayAs: ${JSON.stringify(
|
||||
value?.spec?.["display-as"] || null
|
||||
)},
|
||||
value?.spec?.["display-as"] || null
|
||||
)},
|
||||
uniqueBy: ${JSON.stringify(value?.spec?.["unique-by"] || null)},
|
||||
},
|
||||
default: ${JSON.stringify(value.default || null)},
|
||||
@@ -249,28 +266,14 @@ export default async function makeFileContent(
|
||||
name:${JSON.stringify(value.name || null)},
|
||||
range:${JSON.stringify(value.range || null)},
|
||||
spec: {
|
||||
tag: {
|
||||
"id":${JSON.stringify(value?.spec?.tag?.["id"] || null)},
|
||||
"name": ${JSON.stringify(
|
||||
value?.spec?.tag?.name || null
|
||||
)},
|
||||
"description": ${JSON.stringify(
|
||||
value?.spec?.tag?.description || null
|
||||
)},
|
||||
"warning": ${JSON.stringify(
|
||||
value?.spec?.tag?.warning || null
|
||||
)},
|
||||
variantNames: ${JSON.stringify(
|
||||
value?.spec?.tag?.["variant-names"] || {}
|
||||
)},
|
||||
},
|
||||
|
||||
variants: ${variants},
|
||||
displayAs: ${JSON.stringify(
|
||||
value?.spec?.["display-as"] || null
|
||||
)},
|
||||
value?.spec?.["display-as"] || null
|
||||
)},
|
||||
uniqueBy: ${JSON.stringify(
|
||||
value?.spec?.["unique-by"] || null
|
||||
)},
|
||||
value?.spec?.["unique-by"] || null
|
||||
)},
|
||||
default: ${JSON.stringify(value?.spec?.default || null)},
|
||||
},
|
||||
default: ${JSON.stringify(value.default || null)},
|
||||
|
||||
@@ -49,6 +49,7 @@ export interface ValueSpecNumber extends ListValueSpecNumber, WithStandalone {
|
||||
export interface ValueSpecSelect extends SelectBase, WithStandalone {
|
||||
type: "select";
|
||||
default: string;
|
||||
|
||||
}
|
||||
|
||||
export interface ValueSpecMultiselect extends SelectBase, WithStandalone {
|
||||
@@ -114,14 +115,14 @@ export interface ValueSpecListOf<T extends ListValueSpecType>
|
||||
spec: ListValueSpecOf<T>;
|
||||
range: string; // '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules
|
||||
default:
|
||||
| string[]
|
||||
| number[]
|
||||
| DefaultString[]
|
||||
| Record<string, unknown>[]
|
||||
| readonly string[]
|
||||
| readonly number[]
|
||||
| readonly DefaultString[]
|
||||
| readonly Record<string, unknown>[];
|
||||
| string[]
|
||||
| number[]
|
||||
| DefaultString[]
|
||||
| Record<string, unknown>[]
|
||||
| readonly string[]
|
||||
| readonly number[]
|
||||
| readonly DefaultString[]
|
||||
| readonly Record<string, unknown>[];
|
||||
}
|
||||
|
||||
// sometimes the type checker needs just a little bit of help
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import {Config, Value, List, Variants} from '../../config/builder';
|
||||
import { Config, Value, List, Variants } from '../../config/builder';
|
||||
|
||||
export const enable = Value.boolean({
|
||||
"name": "Enable",
|
||||
@@ -54,11 +54,7 @@ export const serialversion = Value.select({
|
||||
"description": "Return raw transaction or block hex with Segwit or non-SegWit serialization.",
|
||||
"warning": null,
|
||||
"default": "segwit",
|
||||
"values": [
|
||||
"non-segwit",
|
||||
"segwit"
|
||||
],
|
||||
"valueNames": {}
|
||||
"values": {}
|
||||
});
|
||||
export const servertimeout = Value.number({
|
||||
"name": "Rpc Server Timeout",
|
||||
@@ -93,28 +89,28 @@ export const workqueue = Value.number({
|
||||
"units": "requests",
|
||||
"placeholder": null
|
||||
});
|
||||
export const advancedSpec = Config.of({"auth": auth,"serialversion": serialversion,"servertimeout": servertimeout,"threads": threads,"workqueue": workqueue,});;
|
||||
export const advancedSpec = Config.of({ "auth": auth, "serialversion": serialversion, "servertimeout": servertimeout, "threads": threads, "workqueue": workqueue, });;
|
||||
export const advanced = Value.object({
|
||||
name: "Advanced",
|
||||
description: "Advanced RPC Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: advancedSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const rpcSettingsSpec = Config.of({"enable": enable,"username": username,"password": password,"advanced": advanced,});;
|
||||
name: "Advanced",
|
||||
description: "Advanced RPC Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: advancedSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const rpcSettingsSpec = Config.of({ "enable": enable, "username": username, "password": password, "advanced": advanced, });;
|
||||
export const rpc = Value.object({
|
||||
name: "RPC Settings",
|
||||
description: "RPC configuration options.",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: rpcSettingsSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
name: "RPC Settings",
|
||||
description: "RPC configuration options.",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: rpcSettingsSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const zmqEnabled = Value.boolean({
|
||||
"name": "ZeroMQ Enabled",
|
||||
"default": true,
|
||||
@@ -150,17 +146,17 @@ export const discardfee = Value.number({
|
||||
"units": "BTC/kB",
|
||||
"placeholder": null
|
||||
});
|
||||
export const walletSpec = Config.of({"enable": enable1,"avoidpartialspends": avoidpartialspends,"discardfee": discardfee,});;
|
||||
export const walletSpec = Config.of({ "enable": enable1, "avoidpartialspends": avoidpartialspends, "discardfee": discardfee, });;
|
||||
export const wallet = Value.object({
|
||||
name: "Wallet",
|
||||
description: "Wallet Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: walletSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
name: "Wallet",
|
||||
description: "Wallet Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: walletSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const mempoolfullrbf = Value.boolean({
|
||||
"name": "Enable Full RBF",
|
||||
"default": false,
|
||||
@@ -195,17 +191,17 @@ export const mempoolexpiry = Value.number({
|
||||
"units": "Hr",
|
||||
"placeholder": null
|
||||
});
|
||||
export const mempoolSpec = Config.of({"mempoolfullrbf": mempoolfullrbf,"persistmempool": persistmempool,"maxmempool": maxmempool,"mempoolexpiry": mempoolexpiry,});;
|
||||
export const mempoolSpec = Config.of({ "mempoolfullrbf": mempoolfullrbf, "persistmempool": persistmempool, "maxmempool": maxmempool, "mempoolexpiry": mempoolexpiry, });;
|
||||
export const mempool = Value.object({
|
||||
name: "Mempool",
|
||||
description: "Mempool Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: mempoolSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
name: "Mempool",
|
||||
description: "Mempool Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: mempoolSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const listen = Value.boolean({
|
||||
"name": "Make Public",
|
||||
"default": true,
|
||||
@@ -247,31 +243,31 @@ export const port = Value.number({
|
||||
"units": null,
|
||||
"placeholder": null
|
||||
});
|
||||
export const addNodesSpec = Config.of({"hostname": hostname,"port": port,});;
|
||||
export const addNodesSpec = Config.of({ "hostname": hostname, "port": port, });;
|
||||
export const addNodesList = List.obj({
|
||||
name: "Add Nodes",
|
||||
range: "[0,*)",
|
||||
spec: {
|
||||
spec: addNodesSpec,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
},
|
||||
default: [],
|
||||
description: "Add addresses of nodes to connect to.",
|
||||
warning: null,
|
||||
});
|
||||
name: "Add Nodes",
|
||||
range: "[0,*)",
|
||||
spec: {
|
||||
spec: addNodesSpec,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
},
|
||||
default: [],
|
||||
description: "Add addresses of nodes to connect to.",
|
||||
warning: null,
|
||||
});
|
||||
export const addnode = Value.list(addNodesList);
|
||||
export const peersSpec = Config.of({"listen": listen,"onlyconnect": onlyconnect,"onlyonion": onlyonion,"addnode": addnode,});;
|
||||
export const peersSpec = Config.of({ "listen": listen, "onlyconnect": onlyconnect, "onlyonion": onlyonion, "addnode": addnode, });;
|
||||
export const peers = Value.object({
|
||||
name: "Peers",
|
||||
description: "Peer Connection Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: peersSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
name: "Peers",
|
||||
description: "Peer Connection Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: peersSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const dbcache = Value.number({
|
||||
"name": "Database Cache",
|
||||
"default": null,
|
||||
@@ -295,7 +291,7 @@ export const size = Value.number({
|
||||
"units": "MiB",
|
||||
"placeholder": null
|
||||
});
|
||||
export const automatic = Config.of({"size": size,});;
|
||||
export const automatic = Config.of({ "size": size, });;
|
||||
export const size1 = Value.number({
|
||||
"name": "Failsafe Chain Size",
|
||||
"default": 65536,
|
||||
@@ -307,19 +303,19 @@ export const size1 = Value.number({
|
||||
"units": "MiB",
|
||||
"placeholder": null
|
||||
});
|
||||
export const manual = Config.of({"size": size1,});;
|
||||
export const pruningSettingsVariants = Variants.of({"disabled": disabled,"automatic": automatic,"manual": manual,});
|
||||
export const manual = Config.of({ "size": size1, });;
|
||||
export const pruningSettingsVariants = Variants.of({ "disabled": disabled, "automatic": automatic, "manual": manual, });
|
||||
export const pruning = Value.union({
|
||||
name: "Pruning Settings",
|
||||
description: "Blockchain Pruning Options\nReduce the blockchain size on disk\n",
|
||||
warning: "If you set pruning to Manual and your disk is smaller than the total size of the blockchain, you MUST have something running that prunes these blocks or you may overfill your disk!\nDisabling pruning will convert your node into a full archival node. This requires a resync of the entire blockchain, a process that may take several days. Make sure you have enough free disk space or you may fill up your disk.\n",
|
||||
default: "disabled",
|
||||
variants: pruningSettingsVariants,
|
||||
tag: {"id":"mode","name":"Pruning Mode","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","warning":null,"variantNames":{"disabled":"Disabled","automatic":"Automatic","manual":"Manual"}},
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
variantNames: null,
|
||||
});
|
||||
name: "Pruning Settings",
|
||||
description: "Blockchain Pruning Options\nReduce the blockchain size on disk\n",
|
||||
warning: "If you set pruning to Manual and your disk is smaller than the total size of the blockchain, you MUST have something running that prunes these blocks or you may overfill your disk!\nDisabling pruning will convert your node into a full archival node. This requires a resync of the entire blockchain, a process that may take several days. Make sure you have enough free disk space or you may fill up your disk.\n",
|
||||
default: "disabled",
|
||||
variants: pruningSettingsVariants,
|
||||
tag: { "id": "mode", "name": "Pruning Mode", "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", "warning": null, "variantNames": { "disabled": "Disabled", "automatic": "Automatic", "manual": "Manual" } },
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
variantNames: null,
|
||||
});
|
||||
export const blockfilterindex = Value.boolean({
|
||||
"name": "Compute Compact Block Filters (BIP158)",
|
||||
"default": true,
|
||||
@@ -332,45 +328,45 @@ export const peerblockfilters = Value.boolean({
|
||||
"description": "Serve Compact Block Filters as a peer service to other nodes on the network. This is useful if you wish to connect an SPV client to your node to make it efficient to scan transactions without having to download all block data. 'Compute Compact Block Filters (BIP158)' is required.",
|
||||
"warning": null
|
||||
});
|
||||
export const blockFiltersSpec = Config.of({"blockfilterindex": blockfilterindex,"peerblockfilters": peerblockfilters,});;
|
||||
export const blockFiltersSpec = Config.of({ "blockfilterindex": blockfilterindex, "peerblockfilters": peerblockfilters, });;
|
||||
export const blockfilters = Value.object({
|
||||
name: "Block Filters",
|
||||
description: "Settings for storing and serving compact block filters",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: blockFiltersSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
name: "Block Filters",
|
||||
description: "Settings for storing and serving compact block filters",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: blockFiltersSpec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const peerbloomfilters = Value.boolean({
|
||||
"name": "Serve Bloom Filters to Peers",
|
||||
"default": false,
|
||||
"description": "Peers have the option of setting filters on each connection they make after the version handshake has completed. Bloom filters are for clients implementing SPV (Simplified Payment Verification) that want to check that block headers connect together correctly, without needing to verify the full blockchain. The client must trust that the transactions in the chain are in fact valid. It is highly recommended AGAINST using for anything except Bisq integration.",
|
||||
"warning": "This is ONLY for use with Bisq integration, please use Block Filters for all other applications."
|
||||
});
|
||||
export const bloomFiltersBip37Spec = Config.of({"peerbloomfilters": peerbloomfilters,});;
|
||||
export const bloomFiltersBip37Spec = Config.of({ "peerbloomfilters": peerbloomfilters, });;
|
||||
export const bloomfilters = Value.object({
|
||||
name: "Bloom Filters (BIP37)",
|
||||
description: "Setting for serving Bloom Filters",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: bloomFiltersBip37Spec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const advancedSpec1 = Config.of({"mempool": mempool,"peers": peers,"dbcache": dbcache,"pruning": pruning,"blockfilters": blockfilters,"bloomfilters": bloomfilters,});;
|
||||
name: "Bloom Filters (BIP37)",
|
||||
description: "Setting for serving Bloom Filters",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: bloomFiltersBip37Spec,
|
||||
valueNames: {},
|
||||
});
|
||||
export const advancedSpec1 = Config.of({ "mempool": mempool, "peers": peers, "dbcache": dbcache, "pruning": pruning, "blockfilters": blockfilters, "bloomfilters": bloomfilters, });;
|
||||
export const advanced1 = Value.object({
|
||||
name: "Advanced",
|
||||
description: "Advanced Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: advancedSpec1,
|
||||
valueNames: {},
|
||||
});
|
||||
export const inputSpec = Config.of({"rpc": rpc,"zmq-enabled": zmqEnabled,"txindex": txindex,"wallet": wallet,"advanced": advanced1,});;
|
||||
name: "Advanced",
|
||||
description: "Advanced Settings",
|
||||
warning: null,
|
||||
default: null,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
spec: advancedSpec1,
|
||||
valueNames: {},
|
||||
});
|
||||
export const inputSpec = Config.of({ "rpc": rpc, "zmq-enabled": zmqEnabled, "txindex": txindex, "wallet": wallet, "advanced": advanced1, });;
|
||||
export const matchInputSpec = inputSpec.validator();
|
||||
export type InputSpec = typeof matchInputSpec._TYPE;
|
||||
@@ -1,6 +1,9 @@
|
||||
import * as matches from "ts-matches";
|
||||
import { Parser } from "ts-matches";
|
||||
import { InputSpec, ValueSpec as ValueSpecAny } from "../types/config-types";
|
||||
|
||||
const { string, some, object, arrayOf, dictionary, unknown, number, literals, boolean, array } = matches
|
||||
|
||||
type TypeBoolean = "boolean";
|
||||
type TypeString = "string";
|
||||
type TypeNumber = "number";
|
||||
@@ -8,7 +11,6 @@ type TypeObject = "object";
|
||||
type TypeList = "list";
|
||||
type TypeSelect = "select";
|
||||
type TypeMultiselect = "multiselect";
|
||||
type TypePointer = "pointer";
|
||||
type TypeUnion = "union";
|
||||
|
||||
// prettier-ignore
|
||||
@@ -46,20 +48,22 @@ export type GuardList<A> =
|
||||
A extends { readonly type: TypeList, readonly subtype: infer B, spec?: {} } ? ReadonlyArray<GuardAll<Omit<A, "type"> & ({ type: B })>> :
|
||||
unknown
|
||||
// prettier-ignore
|
||||
type GuardPointer<A> =
|
||||
A extends { readonly type: TypePointer } ? (string | null) :
|
||||
unknown
|
||||
// prettier-ignore
|
||||
type GuardSelect<A> =
|
||||
A extends { readonly type: TypeSelect, readonly values: ArrayLike<infer B> } ? GuardDefaultNullable<A, B> :
|
||||
A extends { readonly type: TypeSelect, variants: { [key in infer B & string]: string } } ? B :
|
||||
unknown
|
||||
// prettier-ignore
|
||||
type GuardMultiselect<A> =
|
||||
A extends { readonly type: TypeMultiselect, readonly values: ArrayLike<infer B> } ? GuardDefaultNullable<A, B> :
|
||||
A extends { readonly type: TypeMultiselect, variants: { [key in infer B & string]: string } } ? B[] :
|
||||
unknown
|
||||
|
||||
// prettier-ignore
|
||||
type VariantValue<A> =
|
||||
A extends { name: string, spec: infer B } ? { name: A['name'], spec: TypeFromProps<B> } :
|
||||
never
|
||||
// prettier-ignore
|
||||
type GuardUnion<A> =
|
||||
A extends { readonly type: TypeUnion, readonly tag: { id: infer Id & string }, variants: infer Variants & Record<string, unknown> } ? { [K in keyof Variants]: { [keyType in Id & string]: K } & TypeFromProps<Variants[K]> }[keyof Variants] :
|
||||
A extends { readonly type: TypeUnion, variants: infer Variants & Record<string, unknown> } ?
|
||||
{ [K in keyof Variants]: { [key in K]: VariantValue<Variants[K]>['spec'] } }[keyof Variants] :
|
||||
unknown
|
||||
|
||||
type _<T> = T;
|
||||
@@ -68,7 +72,6 @@ export type GuardAll<A> = GuardNumber<A> &
|
||||
GuardBoolean<A> &
|
||||
GuardObject<A> &
|
||||
GuardList<A> &
|
||||
GuardPointer<A> &
|
||||
GuardUnion<A> &
|
||||
GuardSelect<A> &
|
||||
GuardMultiselect<A>;
|
||||
@@ -77,22 +80,25 @@ export type TypeFromProps<A> =
|
||||
A extends Record<string, unknown> ? { readonly [K in keyof A & string]: _<GuardAll<A[K]>> } :
|
||||
unknown;
|
||||
|
||||
const isType = matches.shape({ type: matches.string });
|
||||
const recordString = matches.dictionary([matches.string, matches.unknown]);
|
||||
const matchDefault = matches.shape({ default: matches.unknown });
|
||||
const matchNullable = matches.shape({ nullable: matches.literal(true) });
|
||||
const matchPattern = matches.shape({ pattern: matches.string });
|
||||
const isType = object({ type: string });
|
||||
const matchVariant = object({
|
||||
name: string,
|
||||
spec: unknown
|
||||
})
|
||||
const recordString = dictionary([string, unknown]);
|
||||
const matchDefault = object({ default: unknown });
|
||||
const matchNullable = object({ nullable: literals(true) });
|
||||
const matchPattern = object({ pattern: string });
|
||||
const rangeRegex = /(\[|\()(\*|(\d|\.)+),(\*|(\d|\.)+)(\]|\))/;
|
||||
const matchRange = matches.shape({ range: matches.regex(rangeRegex) });
|
||||
const matchIntegral = matches.shape({ integral: matches.literal(true) });
|
||||
const matchSpec = matches.shape({ spec: recordString });
|
||||
const matchSubType = matches.shape({ subtype: matches.string });
|
||||
const matchUnion = matches.shape({
|
||||
tag: matches.shape({ id: matches.string }),
|
||||
variants: recordString,
|
||||
const matchRange = object({ range: string });
|
||||
const matchIntegral = object({ integral: literals(true) });
|
||||
const matchSpec = object({ spec: recordString });
|
||||
const matchSubType = object({ subtype: string });
|
||||
const matchUnion = object({
|
||||
variants: dictionary([string, matchVariant]),
|
||||
});
|
||||
const matchValues = matches.shape({
|
||||
values: matches.arrayOf(matches.string),
|
||||
const matchValues = object({
|
||||
values: dictionary([string, string]),
|
||||
});
|
||||
|
||||
function charRange(value = "") {
|
||||
@@ -142,15 +148,11 @@ export function generateDefault(
|
||||
return answer.join("");
|
||||
}
|
||||
|
||||
function withPattern<A>(value: unknown) {
|
||||
if (matchPattern.test(value)) return matches.regex(RegExp(value.pattern));
|
||||
return matches.string;
|
||||
}
|
||||
export function matchNumberWithRange(range: string) {
|
||||
const matched = rangeRegex.exec(range);
|
||||
if (!matched) return matches.number;
|
||||
if (!matched) return number;
|
||||
const [, left, leftValue, , rightValue, , right] = matched;
|
||||
return matches.number
|
||||
return number
|
||||
.validate(
|
||||
leftValue === "*"
|
||||
? (_) => true
|
||||
@@ -174,7 +176,7 @@ export function matchNumberWithRange(range: string) {
|
||||
`lessThan${rightValue}`
|
||||
);
|
||||
}
|
||||
function withIntegral(parser: matches.Parser<unknown, number>, value: unknown) {
|
||||
function withIntegral(parser: Parser<unknown, number>, value: unknown) {
|
||||
if (matchIntegral.test(value)) {
|
||||
return parser.validate(Number.isInteger, "isIntegral");
|
||||
}
|
||||
@@ -184,14 +186,14 @@ function withRange(value: unknown) {
|
||||
if (matchRange.test(value)) {
|
||||
return matchNumberWithRange(value.range);
|
||||
}
|
||||
return matches.number;
|
||||
return number;
|
||||
}
|
||||
const isGenerator = matches.shape({
|
||||
charset: matches.string,
|
||||
len: matches.number,
|
||||
const isGenerator = object({
|
||||
charset: string,
|
||||
len: number,
|
||||
}).test;
|
||||
function defaultNullable<A>(
|
||||
parser: matches.Parser<unknown, A>,
|
||||
parser: Parser<unknown, A>,
|
||||
value: unknown
|
||||
) {
|
||||
if (matchDefault.test(value)) {
|
||||
@@ -216,16 +218,16 @@ function defaultNullable<A>(
|
||||
*/
|
||||
export function guardAll<A extends ValueSpecAny>(
|
||||
value: A
|
||||
): matches.Parser<unknown, GuardAll<A>> {
|
||||
): Parser<unknown, GuardAll<A>> {
|
||||
if (!isType.test(value)) {
|
||||
return matches.unknown as any;
|
||||
return unknown as any;
|
||||
}
|
||||
switch (value.type) {
|
||||
case "boolean":
|
||||
return defaultNullable(matches.boolean, value) as any;
|
||||
return defaultNullable(boolean, value) as any;
|
||||
|
||||
case "string":
|
||||
return defaultNullable(withPattern(value), value) as any;
|
||||
return defaultNullable(string, value) as any;
|
||||
|
||||
case "number":
|
||||
return defaultNullable(
|
||||
@@ -237,7 +239,7 @@ export function guardAll<A extends ValueSpecAny>(
|
||||
if (matchSpec.test(value)) {
|
||||
return defaultNullable(typeFromProps(value.spec), value) as any;
|
||||
}
|
||||
return matches.unknown as any;
|
||||
return unknown as any;
|
||||
|
||||
case "list": {
|
||||
const spec = (matchSpec.test(value) && value.spec) || {};
|
||||
@@ -255,40 +257,40 @@ export function guardAll<A extends ValueSpecAny>(
|
||||
}
|
||||
case "select":
|
||||
if (matchValues.test(value)) {
|
||||
const valueKeys = Object.keys(value.values)
|
||||
return defaultNullable(
|
||||
matches.literals(value.values[0], ...value.values),
|
||||
literals(valueKeys[0], ...valueKeys),
|
||||
value
|
||||
) as any;
|
||||
}
|
||||
return matches.unknown as any;
|
||||
return unknown as any;
|
||||
case "multiselect":
|
||||
if (matchValues.test(value)) {
|
||||
const rangeValidate =
|
||||
(matchRange.test(value) && matchNumberWithRange(value.range).test) ||
|
||||
(() => true);
|
||||
|
||||
const valueKeys = Object.keys(value.values)
|
||||
return defaultNullable(
|
||||
matches
|
||||
.literals(value.values[0], ...value.values)
|
||||
.literals(valueKeys[0], ...valueKeys)
|
||||
.validate((x) => rangeValidate(x.length), "valid length"),
|
||||
value
|
||||
) as any;
|
||||
}
|
||||
return matches.unknown as any;
|
||||
return unknown as any;
|
||||
case "union":
|
||||
if (matchUnion.test(value)) {
|
||||
return matches.some(
|
||||
...Object.entries(value.variants).map(([variant, spec]) =>
|
||||
matches
|
||||
.shape({ [value.tag.id]: matches.literal(variant) })
|
||||
.concat(typeFromProps(spec))
|
||||
return some(
|
||||
...Object.entries(value.variants).map(([_, { spec }]) =>
|
||||
typeFromProps(spec)
|
||||
)
|
||||
) as any;
|
||||
}
|
||||
return matches.unknown as any;
|
||||
return unknown as any;
|
||||
}
|
||||
|
||||
return matches.unknown as any;
|
||||
return unknown as any;
|
||||
}
|
||||
/**
|
||||
* InputSpec: Tells the UI how to ask for information, verification, and will send the service a config in a shape via the spec.
|
||||
@@ -300,9 +302,9 @@ export function guardAll<A extends ValueSpecAny>(
|
||||
*/
|
||||
export function typeFromProps<A extends InputSpec>(
|
||||
valueDictionary: A
|
||||
): matches.Parser<unknown, TypeFromProps<A>> {
|
||||
if (!recordString.test(valueDictionary)) return matches.unknown as any;
|
||||
return matches.shape(
|
||||
): Parser<unknown, TypeFromProps<A>> {
|
||||
if (!recordString.test(valueDictionary)) return unknown as any;
|
||||
return object(
|
||||
Object.fromEntries(
|
||||
Object.entries(valueDictionary).map(([key, value]) => [
|
||||
key,
|
||||
|
||||
Reference in New Issue
Block a user