mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-31 04:33:40 +00:00
Merge pull request #2 from Start9Labs/feat/multiselect
Rename enum and add multiselect (replaces list of enums)
This commit is contained in:
@@ -19,7 +19,7 @@ import { Value } from "./value";
|
|||||||
|
|
||||||
The idea of a config is that now the form is going to ask for
|
The idea of a config is that now the form is going to ask for
|
||||||
Test: [ ] and the value is going to be checked as a boolean.
|
Test: [ ] and the value is going to be checked as a boolean.
|
||||||
There are more complex values like enums, lists, and objects. See {@link Value}
|
There are more complex values like selects, lists, and objects. See {@link Value}
|
||||||
|
|
||||||
Also, there is the ability to get a validator/parser from this config spec.
|
Also, there is the ability to get a validator/parser from this config spec.
|
||||||
```ts
|
```ts
|
||||||
@@ -81,7 +81,7 @@ import { Value } from "./value";
|
|||||||
"warning": null,
|
"warning": null,
|
||||||
});
|
});
|
||||||
export const auth = Value.list(authorizationList);
|
export const auth = Value.list(authorizationList);
|
||||||
export const serialversion = Value.enum({
|
export const serialversion = Value.select({
|
||||||
"name": "Serialization Version",
|
"name": "Serialization Version",
|
||||||
"description":
|
"description":
|
||||||
"Return raw transaction or block hex with Segwit or non-SegWit serialization.",
|
"Return raw transaction or block hex with Segwit or non-SegWit serialization.",
|
||||||
|
|||||||
@@ -59,24 +59,6 @@ export class List<A extends ValueSpecList> extends IBuilder<A> {
|
|||||||
...a,
|
...a,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static enum<
|
|
||||||
A extends Description &
|
|
||||||
Default<string[]> & {
|
|
||||||
range: string;
|
|
||||||
spec: {
|
|
||||||
values: string[];
|
|
||||||
"value-names": {
|
|
||||||
[key: string]: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
>(a: A) {
|
|
||||||
return new List({
|
|
||||||
type: "list" as const,
|
|
||||||
subtype: "enum" as const,
|
|
||||||
...a,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
static obj<
|
static obj<
|
||||||
A extends Description &
|
A extends Description &
|
||||||
Default<Record<string, unknown>[]> & {
|
Default<Record<string, unknown>[]> & {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
|
|||||||
...a,
|
...a,
|
||||||
} as ValueSpecNumber);
|
} as ValueSpecNumber);
|
||||||
}
|
}
|
||||||
static enum<
|
static select<
|
||||||
A extends Description &
|
A extends Description &
|
||||||
Default<string> & {
|
Default<string> & {
|
||||||
values: readonly string[] | string[];
|
values: readonly string[] | string[];
|
||||||
@@ -103,7 +103,7 @@ export class Value<A extends ValueSpec> extends IBuilder<A> {
|
|||||||
}
|
}
|
||||||
>(a: A) {
|
>(a: A) {
|
||||||
return new Value({
|
return new Value({
|
||||||
type: "enum" as const,
|
type: "select" as const,
|
||||||
...a,
|
...a,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { BuilderExtract, IBuilder } from "./builder";
|
|||||||
import { Config } from ".";
|
import { Config } from ".";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in the the Value.enum { @link './value.ts' }
|
* Used in the the Value.select { @link './value.ts' }
|
||||||
* to indicate the type of enums variants that are available. The key for the record passed in will be the
|
* to indicate the type of select variants that are available. The key for the record passed in will be the
|
||||||
* key to the tag.id in the Value.enum
|
* key to the tag.id in the Value.select
|
||||||
```ts
|
```ts
|
||||||
export const pruningSettingsVariants = Variants.of({
|
export const pruningSettingsVariants = Variants.of({
|
||||||
"disabled": disabled,
|
"disabled": disabled,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export default async function makeFileContent(
|
|||||||
)})`;
|
)})`;
|
||||||
}
|
}
|
||||||
case "enum": {
|
case "enum": {
|
||||||
return `Value.enum(${JSON.stringify(
|
return `Value.select(${JSON.stringify(
|
||||||
{
|
{
|
||||||
name: value.name || null,
|
name: value.name || null,
|
||||||
description: value.description || null,
|
description: value.description || null,
|
||||||
@@ -205,14 +205,12 @@ export default async function makeFileContent(
|
|||||||
)})`;
|
)})`;
|
||||||
}
|
}
|
||||||
case "enum": {
|
case "enum": {
|
||||||
return `List.enum(${JSON.stringify(
|
return `Value.multiselect(${JSON.stringify(
|
||||||
{
|
{
|
||||||
name: value.name || null,
|
name: value.name || null,
|
||||||
range: value.range || null,
|
range: value.range || null,
|
||||||
spec: {
|
values: value?.spec?.["values"] || null,
|
||||||
values: value?.spec?.["values"] || null,
|
"value-names": value?.spec?.["value-names"] || {},
|
||||||
"value-names": value?.spec?.["value-names"] || {},
|
|
||||||
},
|
|
||||||
default: value.default || null,
|
default: value.default || null,
|
||||||
description: value.description || null,
|
description: value.description || null,
|
||||||
warning: value.warning || null,
|
warning: value.warning || null,
|
||||||
|
|||||||
@@ -4,22 +4,25 @@ export type ValueType =
|
|||||||
| "string"
|
| "string"
|
||||||
| "number"
|
| "number"
|
||||||
| "boolean"
|
| "boolean"
|
||||||
| "enum"
|
| "select"
|
||||||
|
| "multiselect"
|
||||||
| "list"
|
| "list"
|
||||||
| "object"
|
| "object"
|
||||||
| "file"
|
| "file"
|
||||||
| "union";
|
| "union";
|
||||||
export type ValueSpec = ValueSpecOf<ValueType>;
|
export type ValueSpec = ValueSpecOf<ValueType>;
|
||||||
|
|
||||||
// core spec types. These types provide the metadata for performing validations
|
/** core spec types. These types provide the metadata for performing validations */
|
||||||
export type ValueSpecOf<T extends ValueType> = T extends "string"
|
export type ValueSpecOf<T extends ValueType> = T extends "string"
|
||||||
? ValueSpecString
|
? ValueSpecString
|
||||||
: T extends "number"
|
: T extends "number"
|
||||||
? ValueSpecNumber
|
? ValueSpecNumber
|
||||||
: T extends "boolean"
|
: T extends "boolean"
|
||||||
? ValueSpecBoolean
|
? ValueSpecBoolean
|
||||||
: T extends "enum"
|
: T extends "select"
|
||||||
? ValueSpecEnum
|
? ValueSpecSelect
|
||||||
|
: T extends "multiselect"
|
||||||
|
? ValueSpecMultiselect
|
||||||
: T extends "list"
|
: T extends "list"
|
||||||
? ValueSpecList
|
? ValueSpecList
|
||||||
: T extends "object"
|
: T extends "object"
|
||||||
@@ -43,11 +46,18 @@ export interface ValueSpecNumber extends ListValueSpecNumber, WithStandalone {
|
|||||||
default: null | number;
|
default: null | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValueSpecEnum extends ListValueSpecEnum, WithStandalone {
|
export interface ValueSpecSelect extends SelectBase, WithStandalone {
|
||||||
type: "enum";
|
type: "select";
|
||||||
default: string;
|
default: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ValueSpecMultiselect extends SelectBase, WithStandalone {
|
||||||
|
type: "multiselect";
|
||||||
|
/**'[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules */
|
||||||
|
range: string;
|
||||||
|
default: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ValueSpecBoolean extends WithStandalone {
|
export interface ValueSpecBoolean extends WithStandalone {
|
||||||
type: "boolean";
|
type: "boolean";
|
||||||
default: boolean;
|
default: boolean;
|
||||||
@@ -61,7 +71,7 @@ export interface ValueSpecUnion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ValueSpecFile extends WithStandalone {
|
export interface ValueSpecFile extends WithStandalone {
|
||||||
type: 'file';
|
type: "file";
|
||||||
placeholder: null | string;
|
placeholder: null | string;
|
||||||
nullable: boolean;
|
nullable: boolean;
|
||||||
extensions: string[];
|
extensions: string[];
|
||||||
@@ -78,31 +88,28 @@ export interface WithStandalone {
|
|||||||
warning: null | string;
|
warning: null | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no lists of booleans, lists
|
export interface SelectBase {
|
||||||
export type ListValueSpecType =
|
values: string[] | readonly string[];
|
||||||
| "string"
|
"value-names": { [value: string]: string };
|
||||||
| "number"
|
}
|
||||||
| "enum"
|
|
||||||
| "object"
|
|
||||||
| "union";
|
|
||||||
|
|
||||||
// represents a spec for the values of a list
|
/** no lists of booleans, lists*/
|
||||||
|
export type ListValueSpecType = "string" | "number" | "object" | "union";
|
||||||
|
|
||||||
|
/** represents a spec for the values of a list */
|
||||||
export type ListValueSpecOf<T extends ListValueSpecType> = T extends "string"
|
export type ListValueSpecOf<T extends ListValueSpecType> = T extends "string"
|
||||||
? ListValueSpecString
|
? ListValueSpecString
|
||||||
: T extends "number"
|
: T extends "number"
|
||||||
? ListValueSpecNumber
|
? ListValueSpecNumber
|
||||||
: T extends "enum"
|
|
||||||
? ListValueSpecEnum
|
|
||||||
: T extends "object"
|
: T extends "object"
|
||||||
? ListValueSpecObject
|
? ListValueSpecObject
|
||||||
: T extends "union"
|
: T extends "union"
|
||||||
? ListValueSpecUnion
|
? ListValueSpecUnion
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
// 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>;
|
||||||
@@ -119,10 +126,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,21 +138,20 @@ export interface ListValueSpecString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ListValueSpecNumber {
|
export interface ListValueSpecNumber {
|
||||||
|
/** '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules */
|
||||||
range: string;
|
range: string;
|
||||||
integral: boolean;
|
integral: boolean;
|
||||||
units: null | string;
|
units: null | string;
|
||||||
placeholder: null | string;
|
placeholder: null | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ListValueSpecEnum {
|
|
||||||
values: string[] | readonly string[];
|
|
||||||
"value-names": { [value: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListValueSpecObject {
|
export interface ListValueSpecObject {
|
||||||
spec: InputSpec; // this is a mapped type of the config object at this level, replacing the object's values with specs on those values
|
/** this is a mapped type of the config object at this level, replacing the object's values with specs on those values */
|
||||||
"unique-by": UniqueBy; // indicates whether duplicates can be permitted in the list
|
spec: InputSpec;
|
||||||
"display-as": null | string; // this should be a handlebars template which can make use of the entire config which corresponds to 'spec'
|
/** indicates whether duplicates can be permitted in the list */
|
||||||
|
"unique-by": UniqueBy;
|
||||||
|
/** this should be a handlebars template which can make use of the entire config which corresponds to 'spec' */
|
||||||
|
"display-as": null | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UniqueBy =
|
export type UniqueBy =
|
||||||
@@ -161,15 +164,18 @@ export type UniqueBy =
|
|||||||
export interface ListValueSpecUnion {
|
export interface ListValueSpecUnion {
|
||||||
tag: UnionTagSpec;
|
tag: UnionTagSpec;
|
||||||
variants: { [key: string]: InputSpec };
|
variants: { [key: string]: InputSpec };
|
||||||
"display-as": null | string; // 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*/
|
||||||
|
"display-as": null | string;
|
||||||
"unique-by": UniqueBy;
|
"unique-by": UniqueBy;
|
||||||
default: string; // this should be the variantName which one prefers a user to start with by default when creating a new union instance in a list
|
/** this should be the variantName which one prefers a user to start with by default when creating a new union instance in a list*/
|
||||||
|
default: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UnionTagSpec {
|
export interface UnionTagSpec {
|
||||||
id: string; // The name of the field containing one of the union variants
|
/** The name of the field containing one of the union variants*/
|
||||||
|
id: string;
|
||||||
"variant-names": {
|
"variant-names": {
|
||||||
// the name of each variant
|
/** the name of each variant*/
|
||||||
[variant: string]: string;
|
[variant: string]: string;
|
||||||
};
|
};
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { writeConvertedFile } from "../../scripts/oldSpecToBuilder";
|
import { writeConvertedFile } from "../../scripts/oldSpecToBuilder";
|
||||||
import { writeFileSync, readFileSync } from "fs";
|
|
||||||
|
|
||||||
writeConvertedFile(
|
writeConvertedFile(
|
||||||
"./lib/util/artifacts/output.ts",
|
"./lib/util/artifacts/output.ts",
|
||||||
@@ -66,7 +65,7 @@ writeConvertedFile(
|
|||||||
name: "Serialization Version",
|
name: "Serialization Version",
|
||||||
description:
|
description:
|
||||||
"Return raw transaction or block hex with Segwit or non-SegWit serialization.",
|
"Return raw transaction or block hex with Segwit or non-SegWit serialization.",
|
||||||
type: "enum",
|
type: "select",
|
||||||
values: ["non-segwit", "segwit"],
|
values: ["non-segwit", "segwit"],
|
||||||
"value-names": {},
|
"value-names": {},
|
||||||
default: "segwit",
|
default: "segwit",
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const authorizationList = List.string({
|
|||||||
warning: null,
|
warning: null,
|
||||||
});
|
});
|
||||||
export const auth = Value.list(authorizationList);
|
export const auth = Value.list(authorizationList);
|
||||||
export const serialversion = Value.enum({
|
export const serialversion = Value.select({
|
||||||
name: "Serialization Version",
|
name: "Serialization Version",
|
||||||
description:
|
description:
|
||||||
"Return raw transaction or block hex with Segwit or non-SegWit serialization.",
|
"Return raw transaction or block hex with Segwit or non-SegWit serialization.",
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ type TypeString = "string";
|
|||||||
type TypeNumber = "number";
|
type TypeNumber = "number";
|
||||||
type TypeObject = "object";
|
type TypeObject = "object";
|
||||||
type TypeList = "list";
|
type TypeList = "list";
|
||||||
type TypeEnum = "enum";
|
type TypeSelect = "select";
|
||||||
|
type TypeMultiselect = "multiselect";
|
||||||
type TypePointer = "pointer";
|
type TypePointer = "pointer";
|
||||||
type TypeUnion = "union";
|
type TypeUnion = "union";
|
||||||
|
|
||||||
@@ -49,8 +50,12 @@ type GuardPointer<A> =
|
|||||||
A extends {readonly type:TypePointer} ? (string | null) :
|
A extends {readonly type:TypePointer} ? (string | null) :
|
||||||
unknown
|
unknown
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardEnum<A> =
|
type GuardSelect<A> =
|
||||||
A extends {readonly type:TypeEnum, readonly values: ArrayLike<infer B>} ? GuardDefaultNullable<A, B> :
|
A extends {readonly type:TypeSelect, readonly values: ArrayLike<infer B>} ? GuardDefaultNullable<A, B> :
|
||||||
|
unknown
|
||||||
|
// prettier-ignore
|
||||||
|
type GuardMultiselect<A> =
|
||||||
|
A extends {readonly type:TypeMultiselect, readonly values: ArrayLike<infer B>} ? GuardDefaultNullable<A, B> :
|
||||||
unknown
|
unknown
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type GuardUnion<A> =
|
type GuardUnion<A> =
|
||||||
@@ -65,7 +70,8 @@ export type GuardAll<A> = GuardNumber<A> &
|
|||||||
GuardList<A> &
|
GuardList<A> &
|
||||||
GuardPointer<A> &
|
GuardPointer<A> &
|
||||||
GuardUnion<A> &
|
GuardUnion<A> &
|
||||||
GuardEnum<A>;
|
GuardSelect<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> ? {readonly [K in keyof A & string]: _<GuardAll<A[K]>>} :
|
||||||
@@ -247,7 +253,7 @@ export function guardAll<A extends ValueSpecAny>(
|
|||||||
value
|
value
|
||||||
) as any;
|
) as any;
|
||||||
}
|
}
|
||||||
case "enum":
|
case "select":
|
||||||
if (matchValues.test(value)) {
|
if (matchValues.test(value)) {
|
||||||
return defaultNullable(
|
return defaultNullable(
|
||||||
matches.literals(value.values[0], ...value.values),
|
matches.literals(value.values[0], ...value.values),
|
||||||
@@ -255,6 +261,20 @@ export function guardAll<A extends ValueSpecAny>(
|
|||||||
) as any;
|
) as any;
|
||||||
}
|
}
|
||||||
return matches.unknown as any;
|
return matches.unknown as any;
|
||||||
|
case "multiselect":
|
||||||
|
if (matchValues.test(value)) {
|
||||||
|
const rangeValidate =
|
||||||
|
(matchRange.test(value) && matchNumberWithRange(value.range).test) ||
|
||||||
|
(() => true);
|
||||||
|
|
||||||
|
return defaultNullable(
|
||||||
|
matches
|
||||||
|
.literals(value.values[0], ...value.values)
|
||||||
|
.validate((x) => rangeValidate(x.length), "valid length"),
|
||||||
|
value
|
||||||
|
) as any;
|
||||||
|
}
|
||||||
|
return matches.unknown as any;
|
||||||
case "union":
|
case "union":
|
||||||
if (matchUnion.test(value)) {
|
if (matchUnion.test(value)) {
|
||||||
return matches.some(
|
return matches.some(
|
||||||
|
|||||||
Reference in New Issue
Block a user