mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-30 20:24:47 +00:00
chore: Fix the testing of the output.ts
This commit is contained in:
@@ -1,10 +1,6 @@
|
|||||||
import { BuilderExtract, IBuilder } from "./builder";
|
import { BuilderExtract, IBuilder } from "./builder";
|
||||||
import { Config } from "./config";
|
import { Config } from "./config";
|
||||||
import {
|
import { InputSpec, UniqueBy, ValueSpecList } from "../config-types";
|
||||||
InputSpec,
|
|
||||||
UniqueBy,
|
|
||||||
ValueSpecList,
|
|
||||||
} from "../config-types";
|
|
||||||
import { guardAll } from "../../util";
|
import { guardAll } from "../../util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export interface ValueSpecBoolean extends WithStandalone {
|
|||||||
export interface ValueSpecUnion extends WithStandalone {
|
export interface ValueSpecUnion extends WithStandalone {
|
||||||
type: "union";
|
type: "union";
|
||||||
nullable: boolean;
|
nullable: boolean;
|
||||||
default: null | string
|
default: null | string;
|
||||||
variants: Record<string, { name: string; spec: InputSpec }>;
|
variants: Record<string, { name: string; spec: InputSpec }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +105,7 @@ export type ListValueSpecOf<T extends ListValueSpecType> = T extends "string"
|
|||||||
|
|
||||||
/** represents a spec for a list */
|
/** represents a spec for a list */
|
||||||
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>;
|
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>;
|
||||||
export interface ValueSpecListOf<T extends ListValueSpecType>
|
export interface ValueSpecListOf<T extends ListValueSpecType> extends WithStandalone {
|
||||||
extends WithStandalone {
|
|
||||||
type: "list";
|
type: "list";
|
||||||
subtype: T;
|
subtype: T;
|
||||||
spec: ListValueSpecOf<T>;
|
spec: ListValueSpecOf<T>;
|
||||||
@@ -123,10 +122,7 @@ export interface ValueSpecListOf<T extends ListValueSpecType>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sometimes the type checker needs just a little bit of help
|
// sometimes the type checker needs just a little bit of help
|
||||||
export function isValueSpecListOf<S extends ListValueSpecType>(
|
export function isValueSpecListOf<S extends ListValueSpecType>(t: ValueSpecList, s: S): t is ValueSpecListOf<S> {
|
||||||
t: ValueSpecList,
|
|
||||||
s: S
|
|
||||||
): t is ValueSpecListOf<S> {
|
|
||||||
return t.subtype === s;
|
return t.subtype === s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,4 +158,5 @@ export type UniqueBy =
|
|||||||
|
|
||||||
export type DefaultString = string | { charset: string; len: number };
|
export type DefaultString = string | { charset: string; len: number };
|
||||||
|
|
||||||
export const unionSelectKey = 'unionSelectKey' as const
|
export const unionSelectKey = "unionSelectKey" as const;
|
||||||
|
export type UnionSelectKey = typeof unionSelectKey;
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ async function timeoutHealth(
|
|||||||
const defaultParams = {
|
const defaultParams = {
|
||||||
defaultIntervalS: 60,
|
defaultIntervalS: 60,
|
||||||
defaultTimeoutS: 10,
|
defaultTimeoutS: 10,
|
||||||
defaultDelayS: 10
|
defaultDelayS: 10,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function healthRunner(
|
export default function healthRunner(
|
||||||
name: string,
|
name: string,
|
||||||
@@ -56,11 +56,10 @@ export default function healthRunner(
|
|||||||
* timeout: 10s
|
* timeout: 10s
|
||||||
* delay: 10s
|
* delay: 10s
|
||||||
*/
|
*/
|
||||||
create(effects: Types.Effects, {
|
create(
|
||||||
interval = 60,
|
effects: Types.Effects,
|
||||||
timeout = 10,
|
{ interval = 60, timeout = 10, delay = 10 } = {}
|
||||||
delay = 10,
|
) {
|
||||||
} = {}) {
|
|
||||||
let running: any;
|
let running: any;
|
||||||
function startFn() {
|
function startFn() {
|
||||||
clearInterval(running);
|
clearInterval(running);
|
||||||
|
|||||||
@@ -1,19 +1,108 @@
|
|||||||
import { InputSpec, matchInputSpec } from "./output";
|
import { unionSelectKey } from "../config/config-types";
|
||||||
|
import { InputSpec, matchInputSpec, threads } from "./output";
|
||||||
|
|
||||||
type TEqual<A, B> = A extends B ? (B extends A ? null : never) : never;
|
type IfEquals<T, U, Y = unknown, N = never> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2
|
||||||
function testOutput<A, B>(): (c: TEqual<A, B>) => null {
|
? Y
|
||||||
|
: N;
|
||||||
|
function testOutput<A, B>(): (c: IfEquals<A, B>) => null {
|
||||||
return () => null;
|
return () => null;
|
||||||
}
|
}
|
||||||
const testValue = null as unknown;
|
|
||||||
|
function isObject(item: unknown): item is object {
|
||||||
|
return !!(item && typeof item === "object" && !Array.isArray(item));
|
||||||
|
}
|
||||||
|
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
|
||||||
|
export function mergeDeep<A extends unknown[]>(...sources: A) {
|
||||||
|
return _mergeDeep({}, ...sources);
|
||||||
|
}
|
||||||
|
function _mergeDeep<A extends unknown[]>(
|
||||||
|
target: UnionToIntersection<A[number]> | {},
|
||||||
|
...sources: A
|
||||||
|
): UnionToIntersection<A[number]> | {} {
|
||||||
|
if (!sources.length) return target;
|
||||||
|
const source = sources.shift();
|
||||||
|
|
||||||
|
if (isObject(target) && isObject(source)) {
|
||||||
|
for (const key in source) {
|
||||||
|
if (isObject((source as any)[key])) {
|
||||||
|
if (!(target as any)[key]) Object.assign(target, { [key]: {} });
|
||||||
|
_mergeDeep((target as any)[key], (source as any)[key]);
|
||||||
|
} else {
|
||||||
|
Object.assign(target, { [key]: (source as any)[key] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mergeDeep(target, ...sources);
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-expect-error Because enable should be a boolean
|
// @ts-expect-error Because enable should be a boolean
|
||||||
testOutput<InputSpec["rpc"]["enable"], string>()(null);
|
testOutput<InputSpec["rpc"]["enable"], string>()(null);
|
||||||
testOutput<InputSpec["rpc"]["enable"], boolean>()(null);
|
testOutput<InputSpec["rpc"]["enable"], boolean>()(null);
|
||||||
testOutput<InputSpec["rpc"]["username"], string>()(null);
|
testOutput<InputSpec["rpc"]["username"], string>()(null);
|
||||||
|
|
||||||
testOutput<InputSpec["rpc"]["advanced"]["auth"], readonly string[]>()(null);
|
testOutput<InputSpec["rpc"]["advanced"]["auth"], string[]>()(null);
|
||||||
testOutput<InputSpec["rpc"]["advanced"]["serialversion"], readonly string[]>()(null);
|
testOutput<InputSpec["rpc"]["advanced"]["serialversion"], "segwit" | "non-segwit">()(null);
|
||||||
|
testOutput<InputSpec["rpc"]["advanced"]["servertimeout"], number>()(null);
|
||||||
|
testOutput<InputSpec["advanced"]["peers"]["addnode"][0]["hostname"], string>()(null);
|
||||||
describe("Inputs", () => {
|
describe("Inputs", () => {
|
||||||
test("test", () => {
|
const validInput: InputSpec = {
|
||||||
expect(true).toEqual(true);
|
rpc: {
|
||||||
|
enable: true,
|
||||||
|
username: "test",
|
||||||
|
password: "test",
|
||||||
|
advanced: {
|
||||||
|
auth: ["test"],
|
||||||
|
serialversion: "segwit",
|
||||||
|
servertimeout: 6,
|
||||||
|
threads: 3,
|
||||||
|
workqueue: 9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"zmq-enabled": false,
|
||||||
|
txindex: false,
|
||||||
|
wallet: { enable: false, avoidpartialspends: false, discardfee: 0.0001 },
|
||||||
|
advanced: {
|
||||||
|
mempool: {
|
||||||
|
maxmempool: 1,
|
||||||
|
persistmempool: true,
|
||||||
|
mempoolexpiry: 23,
|
||||||
|
mempoolfullrbf: true,
|
||||||
|
},
|
||||||
|
peers: {
|
||||||
|
listen: true,
|
||||||
|
onlyconnect: true,
|
||||||
|
onlyonion: true,
|
||||||
|
addnode: [
|
||||||
|
{
|
||||||
|
hostname: "test",
|
||||||
|
port: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
dbcache: 5,
|
||||||
|
pruning: {
|
||||||
|
[unionSelectKey]: "disabled",
|
||||||
|
},
|
||||||
|
blockfilters: {
|
||||||
|
blockfilterindex: false,
|
||||||
|
peerblockfilters: false,
|
||||||
|
},
|
||||||
|
bloomfilters: { peerbloomfilters: false },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
test("test valid input", () => {
|
||||||
|
const output = matchInputSpec.unsafeCast(validInput);
|
||||||
|
expect(output).toEqual(validInput);
|
||||||
|
});
|
||||||
|
test("test errors", () => {
|
||||||
|
expect(() =>
|
||||||
|
matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { advanced: { threads: 0 } } }))
|
||||||
|
).toThrowError();
|
||||||
|
expect(() => matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { enable: 2 } }))).toThrowError();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { advanced: { serialversion: "testing" } } }))
|
||||||
|
).toThrowError();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import * as matches from "ts-matches";
|
import * as matches from "ts-matches";
|
||||||
import { Parser } from "ts-matches";
|
import { Parser } from "ts-matches";
|
||||||
import { InputSpec, ValueSpec as ValueSpecAny } from "../config/config-types";
|
import { InputSpec, unionSelectKey, ValueSpec as ValueSpecAny } from "../config/config-types";
|
||||||
|
|
||||||
const { string, some, object, dictionary, unknown, number, literals, boolean } =
|
const { string, some, object, dictionary, unknown, number, literals, boolean } = matches;
|
||||||
matches;
|
|
||||||
|
|
||||||
type TypeBoolean = "boolean";
|
type TypeBoolean = "boolean";
|
||||||
type TypeString = "string";
|
type TypeString = "string";
|
||||||
@@ -16,69 +15,57 @@ type TypeUnion = "union";
|
|||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardDefaultNullable<A, Type> =
|
type GuardDefaultNullable<A, Type> =
|
||||||
A extends { readonly default: unknown } ? Type :
|
A extends { default: unknown } ? Type :
|
||||||
A extends { readonly nullable: true } ? Type :
|
A extends { nullable: true } ? Type :
|
||||||
A extends { readonly nullable: false } ? Type | null | undefined :
|
A extends { nullable: false } ? Type | null | undefined :
|
||||||
Type
|
Type
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardNumber<A> =
|
type GuardNumber<A> =
|
||||||
A extends { readonly type: TypeNumber } ? GuardDefaultNullable<A, number> :
|
A extends { type: TypeNumber } ? GuardDefaultNullable<A, number> :
|
||||||
unknown
|
unknown
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardString<A> =
|
type GuardString<A> =
|
||||||
A extends { readonly type: TypeString } ? GuardDefaultNullable<A, string> :
|
A extends { type: TypeString } ? GuardDefaultNullable<A, string> :
|
||||||
unknown
|
unknown
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardBoolean<A> =
|
type GuardBoolean<A> =
|
||||||
A extends { readonly type: TypeBoolean } ? GuardDefaultNullable<A, boolean> :
|
A extends { type: TypeBoolean } ? GuardDefaultNullable<A, boolean> :
|
||||||
unknown
|
unknown
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardObject<A> =
|
type GuardObject<A> =
|
||||||
A extends { readonly type: TypeObject, readonly spec: infer B } ? (
|
A extends { type: TypeObject, spec: infer B } ? (
|
||||||
B extends Record<string, unknown> ? { readonly [K in keyof B & string]: _<GuardAll<B[K]>> } :
|
B extends Record<string, unknown> ? { [K in keyof B & string]: _<GuardAll<B[K]>> } :
|
||||||
{ _error: "Invalid Spec" }
|
{ _error: "Invalid Spec" }
|
||||||
) :
|
) :
|
||||||
unknown
|
unknown
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export type GuardList<A> =
|
export type GuardList<A> =
|
||||||
A extends { readonly type: TypeList, readonly subtype: infer B, spec?: { spec?: infer C } } ? ReadonlyArray<GuardAll<Omit<A, "type" | "subtype" | "spec"> & ({ type: B, spec: C })>> :
|
A extends { type: TypeList, subtype: infer B, spec?: { spec?: infer C } } ? Array<GuardAll<Omit<A, "type" | "subtype" | "spec"> & ({ type: B, spec: C })>> :
|
||||||
A extends { readonly type: TypeList, readonly subtype: infer B, spec?: {} } ? ReadonlyArray<GuardAll<Omit<A, "type"> & ({ type: B })>> :
|
A extends { type: TypeList, subtype: infer B, spec?: {} } ? Array<GuardAll<Omit<A, "type"> & ({ type: B })>> :
|
||||||
unknown
|
unknown
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardSelect<A> =
|
type GuardSelect<A> =
|
||||||
A extends { readonly type: TypeSelect, variants: infer B } ? (
|
A extends { type: TypeSelect, values: infer B } ? (
|
||||||
B extends Record<string, string> ? keyof B : never
|
B extends Record<string, string> ? keyof B : never
|
||||||
) :
|
) :
|
||||||
unknown
|
unknown
|
||||||
|
|
||||||
const bluj: GuardSelect<{
|
|
||||||
type: "select";
|
|
||||||
} & {
|
|
||||||
readonly name: "Serialization Version";
|
|
||||||
readonly description: "Return raw transaction or block hex with Segwit or non-SegWit serialization.";
|
|
||||||
readonly warning: null;
|
|
||||||
readonly default: "segwit";
|
|
||||||
readonly nullable: false;
|
|
||||||
readonly values: {
|
|
||||||
...;
|
|
||||||
};
|
|
||||||
}>
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardMultiselect<A> =
|
type GuardMultiselect<A> =
|
||||||
A extends { readonly type: TypeMultiselect, variants: { [key in infer B & string]: string } } ?B[] :
|
A extends { type: TypeMultiselect, variants: { [key in infer B & string]: string } } ?B[] :
|
||||||
unknown
|
unknown
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type VariantValue<A> =
|
type VariantValue<A> =
|
||||||
A extends { name: string, spec: infer B } ? { name: A['name'], spec: TypeFromProps<B> } :
|
A extends { name: string, spec: infer B } ? TypeFromProps<B> :
|
||||||
never
|
never
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardUnion<A> =
|
type GuardUnion<A> =
|
||||||
A extends { readonly type: TypeUnion, variants: infer Variants & Record<string, unknown> } ?
|
A extends { type: TypeUnion, variants: infer Variants & Record<string, unknown> } ?
|
||||||
{ [K in keyof Variants]: { [key in K]: VariantValue<Variants[K]>['spec'] } }[keyof Variants] :
|
{ [key in keyof Variants]: _<{[unionSelectKey]: key} & VariantValue<Variants[key]>> }[keyof Variants] :
|
||||||
unknown
|
unknown
|
||||||
|
|
||||||
type _<T> = T;
|
type _<T> = T;
|
||||||
@@ -92,7 +79,7 @@ export type GuardAll<A> = GuardNumber<A> &
|
|||||||
GuardMultiselect<A>;
|
GuardMultiselect<A>;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export type TypeFromProps<A> =
|
export type TypeFromProps<A> =
|
||||||
A extends Record<string, unknown> ? { readonly [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 });
|
||||||
@@ -131,28 +118,18 @@ function charRange(value = "") {
|
|||||||
* @param param1
|
* @param param1
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function generateDefault(
|
export function generateDefault(generate: { charset: string; len: number }, { random = () => Math.random() } = {}) {
|
||||||
generate: { charset: string; len: number },
|
const validCharSets: number[][] = generate.charset.split(",").map(charRange).filter(Array.isArray);
|
||||||
{ random = () => Math.random() } = {}
|
|
||||||
) {
|
|
||||||
const validCharSets: number[][] = generate.charset
|
|
||||||
.split(",")
|
|
||||||
.map(charRange)
|
|
||||||
.filter(Array.isArray);
|
|
||||||
if (validCharSets.length === 0) {
|
if (validCharSets.length === 0) {
|
||||||
throw new Error("Expecing that we have a valid charset");
|
throw new Error("Expecing that we have a valid charset");
|
||||||
}
|
}
|
||||||
const max = validCharSets.reduce(
|
const max = validCharSets.reduce((acc, x) => x.reduce((x, y) => Math.max(x, y), acc), 0);
|
||||||
(acc, x) => x.reduce((x, y) => Math.max(x, y), acc),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
const answer: string[] = Array(generate.len);
|
const answer: string[] = Array(generate.len);
|
||||||
while (i < generate.len) {
|
while (i < generate.len) {
|
||||||
const nextValue = Math.round(random() * max);
|
const nextValue = Math.round(random() * max);
|
||||||
const inRange = validCharSets.reduce(
|
const inRange = validCharSets.reduce(
|
||||||
(acc, [lower, upper]) =>
|
(acc, [lower, upper]) => acc || (nextValue >= lower && nextValue <= upper),
|
||||||
acc || (nextValue >= lower && nextValue <= upper),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
if (!inRange) continue;
|
if (!inRange) continue;
|
||||||
@@ -168,16 +145,8 @@ export function matchNumberWithRange(range: string) {
|
|||||||
const [, left, leftValue, , rightValue, , right] = matched;
|
const [, left, leftValue, , rightValue, , right] = matched;
|
||||||
return number
|
return number
|
||||||
.validate(
|
.validate(
|
||||||
leftValue === "*"
|
leftValue === "*" ? (_) => true : left === "[" ? (x) => x >= Number(leftValue) : (x) => x > Number(leftValue),
|
||||||
? (_) => true
|
leftValue === "*" ? "any" : left === "[" ? `greaterThanOrEqualTo${leftValue}` : `greaterThan${leftValue}`
|
||||||
: left === "["
|
|
||||||
? (x) => x >= Number(leftValue)
|
|
||||||
: (x) => x > Number(leftValue),
|
|
||||||
leftValue === "*"
|
|
||||||
? "any"
|
|
||||||
: left === "["
|
|
||||||
? `greaterThanOrEqualTo${leftValue}`
|
|
||||||
: `greaterThan${leftValue}`
|
|
||||||
)
|
)
|
||||||
.validate(
|
.validate(
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -209,9 +178,7 @@ const isGenerator = object({
|
|||||||
function defaultNullable<A>(parser: Parser<unknown, A>, value: unknown) {
|
function defaultNullable<A>(parser: Parser<unknown, A>, value: unknown) {
|
||||||
if (matchDefault.test(value)) {
|
if (matchDefault.test(value)) {
|
||||||
if (isGenerator(value.default)) {
|
if (isGenerator(value.default)) {
|
||||||
return parser.defaultTo(
|
return parser.defaultTo(parser.unsafeCast(generateDefault(value.default)));
|
||||||
parser.unsafeCast(generateDefault(value.default))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return parser.defaultTo(value.default);
|
return parser.defaultTo(value.default);
|
||||||
}
|
}
|
||||||
@@ -227,9 +194,7 @@ function defaultNullable<A>(parser: Parser<unknown, A>, value: unknown) {
|
|||||||
* @param value
|
* @param value
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function guardAll<A extends ValueSpecAny>(
|
export function guardAll<A extends ValueSpecAny>(value: A): Parser<unknown, GuardAll<A>> {
|
||||||
value: A
|
|
||||||
): Parser<unknown, GuardAll<A>> {
|
|
||||||
if (!isType.test(value)) {
|
if (!isType.test(value)) {
|
||||||
return unknown as any;
|
return unknown as any;
|
||||||
}
|
}
|
||||||
@@ -241,10 +206,7 @@ export function guardAll<A extends ValueSpecAny>(
|
|||||||
return defaultNullable(string, value) as any;
|
return defaultNullable(string, value) as any;
|
||||||
|
|
||||||
case "number":
|
case "number":
|
||||||
return defaultNullable(
|
return defaultNullable(withIntegral(withRange(value), value), value) as any;
|
||||||
withIntegral(withRange(value), value),
|
|
||||||
value
|
|
||||||
) as any;
|
|
||||||
|
|
||||||
case "object":
|
case "object":
|
||||||
if (matchSpec.test(value)) {
|
if (matchSpec.test(value)) {
|
||||||
@@ -254,9 +216,7 @@ export function guardAll<A extends ValueSpecAny>(
|
|||||||
|
|
||||||
case "list": {
|
case "list": {
|
||||||
const spec = (matchSpec.test(value) && value.spec) || {};
|
const spec = (matchSpec.test(value) && value.spec) || {};
|
||||||
const rangeValidate =
|
const rangeValidate = (matchRange.test(value) && matchNumberWithRange(value.range).test) || (() => true);
|
||||||
(matchRange.test(value) && matchNumberWithRange(value.range).test) ||
|
|
||||||
(() => true);
|
|
||||||
|
|
||||||
const subtype = matchSubType.unsafeCast(value).subtype;
|
const subtype = matchSubType.unsafeCast(value).subtype;
|
||||||
return defaultNullable(
|
return defaultNullable(
|
||||||
@@ -269,34 +229,23 @@ export function guardAll<A extends ValueSpecAny>(
|
|||||||
case "select":
|
case "select":
|
||||||
if (matchValues.test(value)) {
|
if (matchValues.test(value)) {
|
||||||
const valueKeys = Object.keys(value.values);
|
const valueKeys = Object.keys(value.values);
|
||||||
return defaultNullable(
|
return defaultNullable(literals(valueKeys[0], ...valueKeys), value) as any;
|
||||||
literals(valueKeys[0], ...valueKeys),
|
|
||||||
value
|
|
||||||
) as any;
|
|
||||||
}
|
}
|
||||||
return unknown as any;
|
return unknown as any;
|
||||||
case "multiselect":
|
case "multiselect":
|
||||||
if (matchValues.test(value)) {
|
if (matchValues.test(value)) {
|
||||||
const rangeValidate =
|
const rangeValidate = (matchRange.test(value) && matchNumberWithRange(value.range).test) || (() => true);
|
||||||
(matchRange.test(value) && matchNumberWithRange(value.range).test) ||
|
|
||||||
(() => true);
|
|
||||||
|
|
||||||
const valueKeys = Object.keys(value.values);
|
const valueKeys = Object.keys(value.values);
|
||||||
return defaultNullable(
|
return defaultNullable(
|
||||||
matches
|
matches.literals(valueKeys[0], ...valueKeys).validate((x) => rangeValidate(x.length), "valid length"),
|
||||||
.literals(valueKeys[0], ...valueKeys)
|
|
||||||
.validate((x) => rangeValidate(x.length), "valid length"),
|
|
||||||
value
|
value
|
||||||
) as any;
|
) as any;
|
||||||
}
|
}
|
||||||
return unknown as any;
|
return unknown as any;
|
||||||
case "union":
|
case "union":
|
||||||
if (matchUnion.test(value)) {
|
if (matchUnion.test(value)) {
|
||||||
return some(
|
return some(...Object.entries(value.variants).map(([_, { spec }]) => typeFromProps(spec))) as any;
|
||||||
...Object.entries(value.variants).map(([_, { spec }]) =>
|
|
||||||
typeFromProps(spec)
|
|
||||||
)
|
|
||||||
) as any;
|
|
||||||
}
|
}
|
||||||
return unknown as any;
|
return unknown as any;
|
||||||
}
|
}
|
||||||
@@ -311,16 +260,9 @@ export function guardAll<A extends ValueSpecAny>(
|
|||||||
* @param valueDictionary
|
* @param valueDictionary
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function typeFromProps<A extends InputSpec>(
|
export function typeFromProps<A extends InputSpec>(valueDictionary: A): Parser<unknown, TypeFromProps<A>> {
|
||||||
valueDictionary: A
|
|
||||||
): Parser<unknown, TypeFromProps<A>> {
|
|
||||||
if (!recordString.test(valueDictionary)) return unknown as any;
|
if (!recordString.test(valueDictionary)) return unknown as any;
|
||||||
return object(
|
return object(
|
||||||
Object.fromEntries(
|
Object.fromEntries(Object.entries(valueDictionary).map(([key, value]) => [key, guardAll(value)]))
|
||||||
Object.entries(valueDictionary).map(([key, value]) => [
|
|
||||||
key,
|
|
||||||
guardAll(value),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
) as any;
|
) as any;
|
||||||
}
|
}
|
||||||
|
|||||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "start-sdk",
|
"name": "start-sdk",
|
||||||
"version": "0.4.0-lib0.beta1",
|
"version": "0.4.0-lib0.beta3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "start-sdk",
|
"name": "start-sdk",
|
||||||
"version": "0.4.0-lib0.beta1",
|
"version": "0.4.0-lib0.beta3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tsc-multi": "^0.6.1",
|
"tsc-multi": "^0.6.1",
|
||||||
"tsconfig-paths": "^3.14.2",
|
"tsconfig-paths": "^3.14.2",
|
||||||
"typescript": "^4.9.5",
|
|
||||||
"vitest": "^0.29.2"
|
"vitest": "^0.29.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4896,6 +4895,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "start-sdk",
|
"name": "start-sdk",
|
||||||
"version": "0.4.0-lib0.beta1",
|
"version": "0.4.0-lib0.beta3",
|
||||||
"description": "For making the patterns that are wanted in making services for the startOS.",
|
"description": "For making the patterns that are wanted in making services for the startOS.",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
@@ -33,7 +33,6 @@
|
|||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tsc-multi": "^0.6.1",
|
"tsc-multi": "^0.6.1",
|
||||||
"tsconfig-paths": "^3.14.2",
|
"tsconfig-paths": "^3.14.2",
|
||||||
"typescript": "^4.9.5",
|
|
||||||
"vitest": "^0.29.2"
|
"vitest": "^0.29.2"
|
||||||
},
|
},
|
||||||
"declaration": true
|
"declaration": true
|
||||||
|
|||||||
@@ -8,15 +8,10 @@ export async function writeConvertedFile(
|
|||||||
inputData: Promise<any> | any,
|
inputData: Promise<any> | any,
|
||||||
options: Parameters<typeof makeFileContent>[1]
|
options: Parameters<typeof makeFileContent>[1]
|
||||||
) {
|
) {
|
||||||
await fs.writeFile(file, await makeFileContent(inputData, options), (err) =>
|
await fs.writeFile(file, await makeFileContent(inputData, options), (err) => console.error(err));
|
||||||
console.error(err)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function makeFileContent(
|
export default async function makeFileContent(inputData: Promise<any> | any, { startSdk = "start-sdk" } = {}) {
|
||||||
inputData: Promise<any> | any,
|
|
||||||
{ startSdk = "start-sdk" } = {}
|
|
||||||
) {
|
|
||||||
const outputLines: string[] = [];
|
const outputLines: string[] = [];
|
||||||
outputLines.push(`
|
outputLines.push(`
|
||||||
import {Config, Value, List, Variants} from '${startSdk}/config/builder';
|
import {Config, Value, List, Variants} from '${startSdk}/config/builder';
|
||||||
@@ -25,13 +20,8 @@ export default async function makeFileContent(
|
|||||||
|
|
||||||
const namedConsts = new Set(["Config", "Value", "List"]);
|
const namedConsts = new Set(["Config", "Value", "List"]);
|
||||||
const configName = newConst("InputSpec", convertInputSpec(data));
|
const configName = newConst("InputSpec", convertInputSpec(data));
|
||||||
const configMatcherName = newConst(
|
const configMatcherName = newConst("matchInputSpec", `${configName}.validator()`);
|
||||||
"matchInputSpec",
|
outputLines.push(`export type InputSpec = typeof ${configMatcherName}._TYPE;`);
|
||||||
`${configName}.validator()`
|
|
||||||
);
|
|
||||||
outputLines.push(
|
|
||||||
`export type InputSpec = typeof ${configMatcherName}._TYPE;`
|
|
||||||
);
|
|
||||||
|
|
||||||
return outputLines.join("\n");
|
return outputLines.join("\n");
|
||||||
|
|
||||||
@@ -99,10 +89,7 @@ export default async function makeFileContent(
|
|||||||
)})`;
|
)})`;
|
||||||
}
|
}
|
||||||
case "enum": {
|
case "enum": {
|
||||||
const allValueNames = new Set([
|
const allValueNames = new Set([...(value?.["values"] || []), ...Object.keys(value?.["value-names"] || {})]);
|
||||||
...(value?.["values"] || []),
|
|
||||||
...Object.keys(value?.["value-names"] || {})]
|
|
||||||
);
|
|
||||||
const values = Object.fromEntries(
|
const values = Object.fromEntries(
|
||||||
Array.from(allValueNames)
|
Array.from(allValueNames)
|
||||||
.filter(string.test)
|
.filter(string.test)
|
||||||
@@ -122,10 +109,7 @@ export default async function makeFileContent(
|
|||||||
)} as const)`;
|
)} as const)`;
|
||||||
}
|
}
|
||||||
case "object": {
|
case "object": {
|
||||||
const specName = newConst(
|
const specName = newConst(value.name + "_spec", convertInputSpec(value.spec));
|
||||||
value.name + "_spec",
|
|
||||||
convertInputSpec(value.spec)
|
|
||||||
);
|
|
||||||
return `Value.object({
|
return `Value.object({
|
||||||
name: ${JSON.stringify(value.name || null)},
|
name: ${JSON.stringify(value.name || null)},
|
||||||
description: ${JSON.stringify(value.description || null)},
|
description: ${JSON.stringify(value.description || null)},
|
||||||
@@ -168,7 +152,7 @@ export default async function makeFileContent(
|
|||||||
name: value.name || null,
|
name: value.name || null,
|
||||||
range: value.range || null,
|
range: value.range || null,
|
||||||
spec: {
|
spec: {
|
||||||
masked: value?.spec?.masked || null,
|
masked: value?.spec?.masked || false,
|
||||||
placeholder: value?.spec?.placeholder || null,
|
placeholder: value?.spec?.placeholder || null,
|
||||||
pattern: value?.spec?.pattern || null,
|
pattern: value?.spec?.pattern || null,
|
||||||
patternDescription: value?.spec?.["pattern-description"] || null,
|
patternDescription: value?.spec?.["pattern-description"] || null,
|
||||||
@@ -225,10 +209,7 @@ export default async function makeFileContent(
|
|||||||
)})`;
|
)})`;
|
||||||
}
|
}
|
||||||
case "object": {
|
case "object": {
|
||||||
const specName = newConst(
|
const specName = newConst(value.name + "_spec", convertInputSpec(value.spec.spec));
|
||||||
value.name + "_spec",
|
|
||||||
convertInputSpec(value.spec.spec)
|
|
||||||
);
|
|
||||||
return `List.obj({
|
return `List.obj({
|
||||||
name: ${JSON.stringify(value.name || null)},
|
name: ${JSON.stringify(value.name || null)},
|
||||||
range: ${JSON.stringify(value.range || null)},
|
range: ${JSON.stringify(value.range || null)},
|
||||||
@@ -245,10 +226,7 @@ export default async function makeFileContent(
|
|||||||
case "union": {
|
case "union": {
|
||||||
const variants = newConst(
|
const variants = newConst(
|
||||||
value.name + "_variants",
|
value.name + "_variants",
|
||||||
convertVariants(
|
convertVariants(value.spec.variants, value.spec["variant-names"] || {})
|
||||||
value.spec.variants,
|
|
||||||
value.spec["variant-names"] || {}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
// @TODO BluJ
|
// @TODO BluJ
|
||||||
return `List.obj({
|
return `List.obj({
|
||||||
@@ -258,15 +236,9 @@ export default async function makeFileContent(
|
|||||||
spec: {
|
spec: {
|
||||||
${unionSelectKey}: {
|
${unionSelectKey}: {
|
||||||
type: "union",
|
type: "union",
|
||||||
name: ${JSON.stringify(
|
name: ${JSON.stringify(value?.spec?.tag?.name || null)},
|
||||||
value?.spec?.tag?.name || null
|
description: ${JSON.stringify(value?.spec?.tag?.description || null)},
|
||||||
)},
|
warning: ${JSON.stringify(value?.spec?.tag?.warning || null)},
|
||||||
description: ${JSON.stringify(
|
|
||||||
value?.spec?.tag?.description || null
|
|
||||||
)},
|
|
||||||
warning: ${JSON.stringify(
|
|
||||||
value?.spec?.tag?.warning || null
|
|
||||||
)},
|
|
||||||
variants: ${variants},
|
variants: ${variants},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
}
|
}
|
||||||
@@ -283,15 +255,11 @@ export default async function makeFileContent(
|
|||||||
throw new Error(`Unknown subtype "${value.subtype}"`);
|
throw new Error(`Unknown subtype "${value.subtype}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertVariants(
|
function convertVariants(variants: Record<string, unknown>, variantNames: Record<string, string>): string {
|
||||||
variants: Record<string, unknown>,
|
|
||||||
variantNames: Record<string, string>
|
|
||||||
): string {
|
|
||||||
let answer = "Variants.of({";
|
let answer = "Variants.of({";
|
||||||
for (const [key, value] of Object.entries(variants)) {
|
for (const [key, value] of Object.entries(variants)) {
|
||||||
const variantSpec = newConst(key, convertInputSpec(value));
|
const variantSpec = newConst(key, convertInputSpec(value));
|
||||||
answer += `"${key}": {name: "${variantNames[key] || key
|
answer += `"${key}": {name: "${variantNames[key] || key}", spec: ${variantSpec}},`;
|
||||||
}", spec: ${variantSpec}},`;
|
|
||||||
}
|
}
|
||||||
return `${answer}})`;
|
return `${answer}})`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user