This commit is contained in:
Matt Hill
2023-03-27 15:35:20 -06:00
parent 5e2d87c11b
commit 42951b4fa2
7 changed files with 101 additions and 88 deletions

View File

@@ -36,7 +36,7 @@ export class List<A extends ValueSpecList> extends IBuilder<A> {
name: string; name: string;
description: string | null; description: string | null;
warning: string | null; warning: string | null;
default: string[] default: string[];
range: string; range: string;
spec: { spec: {
masked: boolean | null; masked: boolean | null;
@@ -58,7 +58,7 @@ export class List<A extends ValueSpecList> extends IBuilder<A> {
name: string; name: string;
description: string | null; description: string | null;
warning: string | null; warning: string | null;
default: string[] default: string[];
range: string; range: string;
spec: { spec: {
range: string; range: string;
@@ -79,7 +79,7 @@ export class List<A extends ValueSpecList> extends IBuilder<A> {
name: string; name: string;
description: string | null; description: string | null;
warning: string | null; warning: string | null;
default: Record<string, unknown>[] default: Record<string, unknown>[];
range: string; range: string;
spec: { spec: {
spec: Config<InputSpec>; spec: Config<InputSpec>;
@@ -112,10 +112,12 @@ export class List<A extends ValueSpecList> extends IBuilder<A> {
name: string; name: string;
description: string | null; description: string | null;
warning: string | null; warning: string | null;
default: Record<string, unknown>[] default: Record<string, unknown>[];
range: string; range: string;
spec: { spec: {
variants: Variants<{ [key: string]: { name: string, spec: InputSpec } }>; variants: Variants<{
[key: string]: { name: string; spec: InputSpec };
}>;
displayAs: null | string; displayAs: null | string;
uniqueBy: UniqueBy; uniqueBy: UniqueBy;
default: string; default: string;

View File

@@ -13,9 +13,9 @@ import {
export type DefaultString = export type DefaultString =
| string | string
| { | {
charset: string | null | undefined; charset: string | null | undefined;
len: number; len: number;
}; };
/** /**
* 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.
@@ -43,10 +43,10 @@ export type DefaultString =
export class Value<A extends ValueSpec> extends IBuilder<A> { export class Value<A extends ValueSpec> extends IBuilder<A> {
static boolean< static boolean<
A extends { A extends {
name: string, name: string;
description: string | null description: string | null;
warning: string | null warning: string | null;
default: boolean | null default: boolean | null;
} }
>(a: A) { >(a: A) {
return new Value({ return new Value({
@@ -56,11 +56,11 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
} }
static string< static string<
A extends { A extends {
name: string, name: string;
description: string | null description: string | null;
warning: string | null warning: string | null;
nullable: boolean nullable: boolean;
default: DefaultString | null default: DefaultString | null;
masked: boolean | null; masked: boolean | null;
placeholder: string | null; placeholder: string | null;
pattern: string | null; pattern: string | null;
@@ -75,11 +75,11 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
} }
static number< static number<
A extends { A extends {
name: string name: string;
description: string | null description: string | null;
warning: string | null warning: string | null;
nullable: boolean nullable: boolean;
default: number | null default: number | null;
range: string; range: string;
integral: boolean; integral: boolean;
units: string | null; units: string | null;
@@ -93,11 +93,11 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
} }
static select< static select<
A extends { A extends {
name: string name: string;
description: string | null description: string | null;
warning: string | null warning: string | null;
nullable: boolean nullable: boolean;
default: string | null default: string | null;
values: Record<string, string>; values: Record<string, string>;
} }
>(a: A) { >(a: A) {
@@ -108,12 +108,12 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
} }
static multiselect< static multiselect<
A extends { A extends {
name: string name: string;
description: string | null description: string | null;
warning: string | null warning: string | null;
default: string[] default: string[];
values: Record<string, string>; values: Record<string, string>;
range: string range: string;
} }
>(a: A) { >(a: A) {
return new Value({ return new Value({
@@ -139,11 +139,11 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
}); });
} }
static union< static union<
A extends { A extends {
name: string; name: string;
description: string | null; description: string | null;
warning: string | null; warning: string | null;
variants: Variants<{ [key: string]: { name: string, spec: InputSpec } }>; variants: Variants<{ [key: string]: { name: string; spec: InputSpec } }>;
default: string; default: string;
} }
>(a: A) { >(a: A) {

View File

@@ -41,22 +41,25 @@ import { Config } from ".";
export class Variants< export class Variants<
A extends { A extends {
[key: string]: { [key: string]: {
name: string, name: string;
spec: InputSpec spec: InputSpec;
} };
} }
> extends IBuilder<A> { > extends IBuilder<A> {
static of< static of<
A extends { A extends {
[key: string]: { name: string, spec: Config<InputSpec> }; [key: string]: { name: string; spec: Config<InputSpec> };
} }
>(a: A) { >(a: A) {
const variants: { [K in keyof A]: { name: string, spec: BuilderExtract<A[K]['spec']> } } = {} as any; const variants: {
[K in keyof A]: { name: string; spec: BuilderExtract<A[K]["spec"]> };
} = {} as any;
for (const key in a) { for (const key in a) {
const value = a[key] const value = a[key];
variants[key] = { variants[key] = {
name: value.name, spec: value.spec.build() as any name: value.name,
} spec: value.spec.build() as any,
};
} }
return new Variants(variants); return new Variants(variants);
} }

View File

@@ -48,9 +48,8 @@ export interface ValueSpecNumber extends ListValueSpecNumber, WithStandalone {
export interface ValueSpecSelect extends SelectBase, WithStandalone { export interface ValueSpecSelect extends SelectBase, WithStandalone {
type: "select"; type: "select";
nullable: boolean nullable: boolean;
default: null | string; default: null | string;
} }
export interface ValueSpecMultiselect extends SelectBase, WithStandalone { export interface ValueSpecMultiselect extends SelectBase, WithStandalone {
@@ -67,7 +66,7 @@ export interface ValueSpecBoolean extends WithStandalone {
export interface ValueSpecUnion extends WithStandalone { export interface ValueSpecUnion extends WithStandalone {
type: "union"; type: "union";
variants: { [key: string]: { name: string, spec: InputSpec } }; variants: { [key: string]: { name: string; spec: InputSpec } };
default: null | string; default: null | string;
} }
@@ -116,14 +115,14 @@ export interface ValueSpecListOf<T extends ListValueSpecType>
spec: ListValueSpecOf<T>; spec: ListValueSpecOf<T>;
range: string; // '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules range: string; // '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules
default: default:
| string[] | string[]
| number[] | number[]
| DefaultString[] | DefaultString[]
| Record<string, unknown>[] | Record<string, unknown>[]
| readonly string[] | readonly string[]
| readonly number[] | readonly number[]
| readonly DefaultString[] | readonly DefaultString[]
| readonly Record<string, unknown>[]; | readonly Record<string, unknown>[];
} }
// sometimes the type checker needs just a little bit of help // sometimes the type checker needs just a little bit of help
@@ -166,7 +165,7 @@ export type UniqueBy =
| { all: readonly UniqueBy[] | UniqueBy[] }; | { all: readonly UniqueBy[] | UniqueBy[] };
export interface ListValueSpecUnion { export interface ListValueSpecUnion {
variants: { [key: string]: { name: string, spec: InputSpec } }; variants: { [key: string]: { name: string; spec: InputSpec } };
/** this may be a handlebars template which can conditionally (on tag.id) make use of each union's entries, or if left blank will display as tag.id*/ /** this may be a handlebars template which can conditionally (on tag.id) make use of each union's entries, or if left blank will display as tag.id*/
displayAs: null | string; displayAs: null | string;
uniqueBy: UniqueBy; uniqueBy: UniqueBy;

View File

@@ -2,7 +2,16 @@ 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, ValueSpec as ValueSpecAny } from "../config/config-types";
const { string, some, object, arrayOf, dictionary, unknown, number, literals, boolean, array } = matches const {
string,
some,
object,
dictionary,
unknown,
number,
literals,
boolean,
} = matches;
type TypeBoolean = "boolean"; type TypeBoolean = "boolean";
type TypeString = "string"; type TypeString = "string";
@@ -83,8 +92,8 @@ export type TypeFromProps<A> =
const isType = object({ type: string }); const isType = object({ type: string });
const matchVariant = object({ const matchVariant = object({
name: string, name: string,
spec: unknown spec: unknown,
}) });
const recordString = dictionary([string, unknown]); const recordString = dictionary([string, unknown]);
const matchDefault = object({ default: unknown }); const matchDefault = object({ default: unknown });
const matchNullable = object({ nullable: literals(true) }); const matchNullable = object({ nullable: literals(true) });
@@ -157,13 +166,13 @@ export function matchNumberWithRange(range: string) {
leftValue === "*" leftValue === "*"
? (_) => true ? (_) => true
: left === "[" : left === "["
? (x) => x >= Number(leftValue) ? (x) => x >= Number(leftValue)
: (x) => x > Number(leftValue), : (x) => x > Number(leftValue),
leftValue === "*" leftValue === "*"
? "any" ? "any"
: left === "[" : left === "["
? `greaterThanOrEqualTo${leftValue}` ? `greaterThanOrEqualTo${leftValue}`
: `greaterThan${leftValue}` : `greaterThan${leftValue}`
) )
.validate( .validate(
// prettier-ignore // prettier-ignore
@@ -192,10 +201,7 @@ const isGenerator = object({
charset: string, charset: string,
len: number, len: number,
}).test; }).test;
function defaultNullable<A>( function defaultNullable<A>(parser: Parser<unknown, A>, value: unknown) {
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(
@@ -257,7 +263,7 @@ 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), literals(valueKeys[0], ...valueKeys),
value value
@@ -270,7 +276,7 @@ export function guardAll<A extends ValueSpecAny>(
(matchRange.test(value) && matchNumberWithRange(value.range).test) || (matchRange.test(value) && matchNumberWithRange(value.range).test) ||
(() => true); (() => true);
const valueKeys = Object.keys(value.values) const valueKeys = Object.keys(value.values);
return defaultNullable( return defaultNullable(
matches matches
.literals(valueKeys[0], ...valueKeys) .literals(valueKeys[0], ...valueKeys)

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "start-sdk", "name": "start-sdk",
"version": "0.4.0-lib0.alpha6", "version": "0.4.0-lib0.alpha9",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "start-sdk", "name": "start-sdk",
"version": "0.4.0-lib0.alpha6", "version": "0.4.0-lib0.alpha9",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",

View File

@@ -99,14 +99,14 @@ export default async function makeFileContent(
} }
case "enum": { case "enum": {
const allValueNames = new Set( const allValueNames = new Set(
...value?.spec?.["values"] || [], ...(value?.spec?.["values"] || []),
...Object.keys(value?.spec?.["value-names"] || {}) ...Object.keys(value?.spec?.["value-names"] || {})
); );
const values = Object.fromEntries( const values = Object.fromEntries(
Array.from(allValueNames) Array.from(allValueNames)
.filter(string.test) .filter(string.test)
.map(key => [key, value?.spec?.["value-names"]?.[key] || key]) .map((key) => [key, value?.spec?.["value-names"]?.[key] || key])
) );
return `Value.select(${JSON.stringify( return `Value.select(${JSON.stringify(
{ {
name: value.name || null, name: value.name || null,
@@ -169,8 +169,7 @@ export default async function makeFileContent(
masked: value?.spec?.masked || null, masked: value?.spec?.masked || null,
placeholder: value?.spec?.placeholder || null, placeholder: value?.spec?.placeholder || null,
pattern: value?.spec?.pattern || null, pattern: value?.spec?.pattern || null,
patternDescription: patternDescription: value?.spec?.["pattern-description"] || null,
value?.spec?.["pattern-description"] || null,
textarea: value?.spec?.textarea || false, textarea: value?.spec?.textarea || false,
}, },
default: value.default || null, default: value.default || null,
@@ -202,14 +201,14 @@ export default async function makeFileContent(
} }
case "enum": { case "enum": {
const allValueNames = new Set( const allValueNames = new Set(
...value?.spec?.["values"] || [], ...(value?.spec?.["values"] || []),
...Object.keys(value?.spec?.["value-names"] || {}) ...Object.keys(value?.spec?.["value-names"] || {})
); );
const values = Object.fromEntries( const values = Object.fromEntries(
Array.from(allValueNames) Array.from(allValueNames)
.filter(string.test) .filter(string.test)
.map(key => [key, value?.spec?.["value-names"]?.[key] || key]) .map((key) => [key, value?.spec?.["value-names"]?.[key] || key])
) );
return `Value.multiselect(${JSON.stringify( return `Value.multiselect(${JSON.stringify(
{ {
name: value.name || null, name: value.name || null,
@@ -233,9 +232,7 @@ export default async function makeFileContent(
range: ${JSON.stringify(value.range || null)}, range: ${JSON.stringify(value.range || null)},
spec: { spec: {
spec: ${specName}, spec: ${specName},
displayAs: ${JSON.stringify( displayAs: ${JSON.stringify(value?.spec?.["display-as"] || null)},
value?.spec?.["display-as"] || null
)},
uniqueBy: ${JSON.stringify(value?.spec?.["unique-by"] || null)}, uniqueBy: ${JSON.stringify(value?.spec?.["unique-by"] || null)},
}, },
default: ${JSON.stringify(value.default || null)}, default: ${JSON.stringify(value.default || null)},
@@ -246,7 +243,10 @@ export default async function makeFileContent(
case "union": { case "union": {
const variants = newConst( const variants = newConst(
value.name + "_variants", value.name + "_variants",
convertVariants(value.spec.variants, value.spec['variant-names'] || {}) convertVariants(
value.spec.variants,
value.spec["variant-names"] || {}
)
); );
return `List.union( return `List.union(
{ {
@@ -255,11 +255,9 @@ export default async function makeFileContent(
spec: { spec: {
variants: ${variants}, variants: ${variants},
displayAs: ${JSON.stringify( displayAs: ${JSON.stringify(
value?.spec?.["display-as"] || null value?.spec?.["display-as"] || null
)}, )},
uniqueBy: ${JSON.stringify( uniqueBy: ${JSON.stringify(value?.spec?.["unique-by"] || null)},
value?.spec?.["unique-by"] || null
)},
default: ${JSON.stringify(value?.spec?.default || null)}, default: ${JSON.stringify(value?.spec?.default || null)},
}, },
default: ${JSON.stringify(value.default || null)}, default: ${JSON.stringify(value.default || null)},
@@ -272,11 +270,16 @@ export default async function makeFileContent(
throw new Error(`Unknown subtype "${value.subtype}"`); throw new Error(`Unknown subtype "${value.subtype}"`);
} }
function convertVariants(variants: Record<string, unknown>, variantNames: Record<string, string>): string { function convertVariants(
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}", spec: ${variantSpec}},`; answer += `"${key}": {name: "${
variantNames[key] || key
}", spec: ${variantSpec}},`;
} }
return `${answer}})`; return `${answer}})`;
} }