From 1c4a14d63110e55781cb66a46fc21c06c4a8e287 Mon Sep 17 00:00:00 2001 From: BluJ Date: Mon, 13 Feb 2023 16:58:04 -0700 Subject: [PATCH] chore: Use the types from the fronted system --- compat/getConfig.ts | 2 +- compat/migrations.ts | 51 +++------ compat/setConfig.ts | 15 +-- config/config.ts | 10 +- config/list.ts | 33 +++--- config/pointer.ts | 13 ++- config/value.ts | 26 ++--- config/variants.ts | 2 +- mod.ts | 1 + types.ts | 195 +++----------------------------- types/config-types.ts | 166 +++++++++++++++++++++++++++ utils/propertiesMatcher.test.ts | 5 +- utils/propertiesMatcher.ts | 6 +- 13 files changed, 252 insertions(+), 273 deletions(-) create mode 100644 types/config-types.ts diff --git a/compat/getConfig.ts b/compat/getConfig.ts index 91ae3b7..17e4205 100644 --- a/compat/getConfig.ts +++ b/compat/getConfig.ts @@ -1,7 +1,7 @@ import { YAML } from "../dependencies.ts"; import { matches } from "../dependencies.ts"; import { ExpectedExports } from "../types.ts"; -import { ConfigSpec } from "../types.ts"; +import { ConfigSpec } from "../types/config-types.ts"; import { typeFromProps, TypeFromProps } from "../utils/propertiesMatcher.ts"; const { any, string, dictionary } = matches; diff --git a/compat/migrations.ts b/compat/migrations.ts index fac5034..01e8185 100644 --- a/compat/migrations.ts +++ b/compat/migrations.ts @@ -3,6 +3,7 @@ import * as T from "../types.ts"; import * as M from "../migrations.ts"; import * as util from "../util.ts"; import { EmVer } from "../emver-lite/mod.ts"; +import { ConfigSpec } from "../types/config-types.ts"; export interface NoRepeat { version: version; @@ -16,14 +17,11 @@ export interface NoRepeat { * @param noFail (optional, default:false) whether or not to fail the migration if fn throws an error * @returns a migraion function */ -export function updateConfig< - version extends string, - type extends "up" | "down", - >( - fn: (config: T.Config, effects: T.Effects) => T.Config | Promise, - configured: boolean, - noRepeat?: NoRepeat, - noFail = false, +export function updateConfig( + fn: (config: ConfigSpec, effects: T.Effects) => ConfigSpec | Promise, + configured: boolean, + noRepeat?: NoRepeat, + noFail = false ): M.MigrationFn { return M.migrationFn(async (effects: T.Effects) => { await noRepeatGuard(effects, noRepeat, async () => { @@ -45,20 +43,15 @@ export function updateConfig< }); } -export async function noRepeatGuard< - version extends string, - type extends "up" | "down", - >( - effects: T.Effects, - noRepeat: NoRepeat | undefined, - fn: () => Promise, +export async function noRepeatGuard( + effects: T.Effects, + noRepeat: NoRepeat | undefined, + fn: () => Promise ): Promise { if (!noRepeat) { return fn(); } - if ( - !await util.exists(effects, { path: "start9/migrations", volumeId: "main" }) - ) { + if (!(await util.exists(effects, { path: "start9/migrations", volumeId: "main" }))) { await effects.createDir({ path: "start9/migrations", volumeId: "main" }); } const migrationPath = { @@ -66,7 +59,7 @@ export async function noRepeatGuard< volumeId: "main", }; if (noRepeat.type === "up") { - if (!await util.exists(effects, migrationPath)) { + if (!(await util.exists(effects, migrationPath))) { await fn(); await effects.writeFile({ ...migrationPath, toWrite: "" }); } @@ -81,11 +74,9 @@ export async function noRepeatGuard< export async function initNoRepeat( effects: T.Effects, migrations: M.MigrationMapping, - startingVersion: string, + startingVersion: string ) { - if ( - !await util.exists(effects, { path: "start9/migrations", volumeId: "main" }) - ) { + if (!(await util.exists(effects, { path: "start9/migrations", volumeId: "main" }))) { const starting = EmVer.parse(startingVersion); await effects.createDir({ path: "start9/migrations", volumeId: "main" }); for (const version in migrations) { @@ -103,19 +94,11 @@ export async function initNoRepeat( export function fromMapping( migrations: M.MigrationMapping, - currentVersion: string, + currentVersion: string ): T.ExpectedExports.migration { const inner = M.fromMapping(migrations, currentVersion); - return async ( - effects: T.Effects, - version: string, - direction?: unknown, - ) => { - await initNoRepeat( - effects, - migrations, - direction === "from" ? version : currentVersion, - ); + return async (effects: T.Effects, version: string, direction?: unknown) => { + await initNoRepeat(effects, migrations, direction === "from" ? version : currentVersion); return inner(effects, version, direction); }; } diff --git a/compat/setConfig.ts b/compat/setConfig.ts index 5a5a9e8..1381238 100644 --- a/compat/setConfig.ts +++ b/compat/setConfig.ts @@ -1,11 +1,6 @@ import { YAML } from "../dependencies.ts"; -import { - Config, - DependsOn, - Effects, - ExpectedExports, - SetResult, -} from "../types.ts"; +import { DependsOn, Effects, ExpectedExports, SetResult } from "../types.ts"; +import { ConfigSpec } from "../types/config-types.ts"; /** * Will set the config to the default start9/config.yaml @@ -15,11 +10,7 @@ import { * @param depends_on This would be the depends on for condition depends_on * @returns */ -export const setConfig = async ( - effects: Effects, - newConfig: Config, - dependsOn: DependsOn = {}, -) => { +export const setConfig = async (effects: Effects, newConfig: ConfigSpec, dependsOn: DependsOn = {}) => { await effects.createDir({ path: "start9", volumeId: "main", diff --git a/config/config.ts b/config/config.ts index 242dd3a..2c5e020 100644 --- a/config/config.ts +++ b/config/config.ts @@ -1,4 +1,4 @@ -import { ConfigSpec, ValueSpecAny } from "../types.ts"; +import { ConfigSpec, ValueSpec } from "../types/config-types.ts"; import { BuilderExtract, IBuilder } from "./builder.ts"; import { Value } from "./value.ts"; @@ -6,13 +6,13 @@ export class Config extends IBuilder { static empty() { return new Config({}); } - static withValue(key: K, value: Value) { + static withValue(key: K, value: Value) { return new Config({ [key]: value.build(), } as { [key in K]: B }); } - static of }, C extends ValueSpecAny>(spec: B) { + static of }, C extends ValueSpec>(spec: B) { // deno-lint-ignore no-explicit-any const answer: { [K in keyof B]: BuilderExtract } = {} as any; for (const key in spec) { @@ -21,13 +21,13 @@ export class Config extends IBuilder { } return new Config(answer); } - withValue(key: K, value: Value) { + withValue(key: K, value: Value) { return new Config({ ...this.a, [key]: value.build(), } as A & { [key in K]: B }); } - addValue(key: K, value: Value) { + addValue(key: K, value: Value) { return new Config({ ...this.a, [key]: value.build(), diff --git a/config/list.ts b/config/list.ts index fe7051a..f2ffaad 100644 --- a/config/list.ts +++ b/config/list.ts @@ -1,27 +1,27 @@ -import { ConfigSpec, Tag, UniqueBy, ValueSpecList } from "../types.ts"; import { IBuilder } from "./builder.ts"; import { Config } from "./config.ts"; import { Default, NullableDefault, NumberSpec, StringSpec } from "./value.ts"; import { Description } from "./value.ts"; import * as T from "../types.ts"; import { Variants } from "./variants.ts"; +import { ConfigSpec, UniqueBy, ValueSpecList, ValueSpecListOf } from "../types/config-types.ts"; -export class List> extends IBuilder { - // deno-lint-ignore ban-types - static boolean & { range: string; spec: {} }>(a: A) { - return new List({ - type: "list" as const, - subtype: "boolean" as const, - ...a, - }); - } +export class List extends IBuilder { + // // deno-lint-ignore ban-types + // static boolean & { range: string; spec: {}; default: boolean }>(a: A) { + // return new List({ + // type: "list" as const, + // subtype: "boolean" as const, + // ...a, + // }); + // } static string & { range: string; spec: StringSpec }>(a: A) { return new List({ type: "list" as const, subtype: "string" as const, ...a, - } as T.Tag<"list", T.Subtype<"string", T.WithDescription, string[]>>>>); + } as ValueSpecListOf<"string">); } static number & { range: string; spec: NumberSpec }>(a: A) { return new List({ @@ -75,7 +75,7 @@ export class List> extends IBuilder { type: "list" as const, subtype: "object" as const, ...value, - } as T.Tag<"list", T.Subtype<"object", T.WithDescription, Record[]>>>>); + } as ValueSpecListOf<"object">); } static union< A extends Description & @@ -84,15 +84,16 @@ export class List> extends IBuilder { spec: { tag: { id: string; - name: null | string | undefined; - description: null | string | undefined; + name: string; + description: null | string; "variant-names": { [key: string]: string; }; }; variants: Variants; - "display-as": null | string | undefined; - "unique-by": null | UniqueBy | undefined; + "display-as": null | string; + "unique-by": UniqueBy; + default: string; }; }, B extends { [key: string]: ConfigSpec } diff --git a/config/pointer.ts b/config/pointer.ts index efd4ae2..a3ddb6a 100644 --- a/config/pointer.ts +++ b/config/pointer.ts @@ -1,8 +1,8 @@ -import { ValueSpecAny } from "../types.ts"; +import { ValueSpec } from "../types/config-types.ts"; import { IBuilder } from "./builder.ts"; import { Description } from "./value.ts"; -export class Pointer extends IBuilder { +export class Pointer extends IBuilder { static packageTorKey(a: A) { return new Pointer({ type: "pointer" as const, @@ -27,7 +27,9 @@ export class Pointer extends IBuilder { ...a, }); } - static packageConfig(a: A) { + static packageConfig< + A extends Description & { "package-id": string; selector: string; multi: boolean; interface: string } + >(a: A) { return new Pointer({ type: "pointer" as const, subtype: "package" as const, @@ -35,10 +37,13 @@ export class Pointer extends IBuilder { ...a, }); } - static system>(a: A) { + static system( + a: A + ) { return new Pointer({ type: "pointer" as const, subtype: "system" as const, + target: "system" as const, ...a, }); } diff --git a/config/value.ts b/config/value.ts index 0f53079..5afa9fa 100644 --- a/config/value.ts +++ b/config/value.ts @@ -1,10 +1,9 @@ -import { ConfigSpec, Tag, ValueSpecAny, ValueSpecList } from "../types.ts"; -import * as T from "../types.ts"; -import { BuilderExtract, IBuilder } from "./builder.ts"; +import { IBuilder } from "./builder.ts"; import { Config } from "./config.ts"; import { List } from "./list.ts"; import { Pointer } from "./pointer.ts"; import { Variants } from "./variants.ts"; +import { ConfigSpec, ValueSpec, ValueSpecList, ValueSpecNumber, ValueSpecString } from "../types/config-types.ts"; export type DefaultString = | string @@ -37,10 +36,10 @@ export type StringSpec = { | {} ); export type NumberSpec = { - range: string | null; - integral: boolean | null; + range: string; + integral: boolean; units: string | null; - placeholder: number | null; + placeholder: string | null; }; export type Nullable = { nullable: boolean; @@ -52,7 +51,7 @@ type _UniqueBy = any: _UniqueBy[]; }; -export class Value extends IBuilder { +export class Value extends IBuilder { static boolean>(a: A) { return new Value({ type: "boolean" as const, @@ -63,13 +62,13 @@ export class Value extends IBuilder { return new Value({ type: "string" as const, ...a, - } as Tag<"string", T.WithDescription, DefaultString>>>); + } as ValueSpecString); } static number & Nullable & NumberSpec>(a: A) { return new Value({ type: "number" as const, ...a, - } as Tag<"number", T.WithDescription, number>>>); + } as ValueSpecNumber); } static enum< A extends Description & @@ -103,15 +102,16 @@ export class Value extends IBuilder { Default & { tag: { id: string; - name: string | null; + name: string; description: string | null; + warning: string | null; "variant-names": { [key: string]: string; }; }; variants: Variants; "display-as": string | null; - "unique-by": _UniqueBy | null; + "unique-by": _UniqueBy; }, B extends { [key: string]: ConfigSpec } >(a: A) { @@ -124,10 +124,10 @@ export class Value extends IBuilder { }); } - static pointer(a: Pointer) { + static pointer(a: Pointer) { return new Value(a.build()); } - static list, B extends Tag<"list", ValueSpecList>>(a: A) { + static list, B extends ValueSpecList>(a: A) { return new Value(a.build()); } } diff --git a/config/variants.ts b/config/variants.ts index 64e2bc8..5512fa5 100644 --- a/config/variants.ts +++ b/config/variants.ts @@ -1,4 +1,4 @@ -import { ConfigSpec } from "../types.ts"; +import { ConfigSpec } from "../types/config-types.ts"; import { BuilderExtract, IBuilder } from "./builder.ts"; import { Config } from "./mod.ts"; diff --git a/mod.ts b/mod.ts index 4d6be02..46b4e13 100644 --- a/mod.ts +++ b/mod.ts @@ -7,3 +7,4 @@ export * as util from "./util.ts"; export * as C from "./config/mod.ts"; export * as config from "./config/mod.ts"; export { Backups } from "./backups.ts"; +export * as configTypes from "./types/config-types.ts"; diff --git a/types.ts b/types.ts index d5329b7..b50dbab 100644 --- a/types.ts +++ b/types.ts @@ -1,7 +1,10 @@ +export * as configTypes from "./types/config-types.ts"; +import { ConfigSpec } from "./types/config-types.ts"; + // deno-lint-ignore no-namespace export namespace ExpectedExports { /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */ - export type setConfig = (effects: Effects, input: Config) => Promise>; + export type setConfig = (effects: Effects, input: ConfigSpec) => Promise>; /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */ export type getConfig = (effects: Effects) => Promise>; /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ @@ -33,7 +36,7 @@ export namespace ExpectedExports { * service starting, and that file would indicate that it would rescan all the data. */ export type action = { - [id: string]: (effects: Effects, config?: Config) => Promise>; + [id: string]: (effects: Effects, config?: ConfigSpec) => Promise>; }; /** @@ -43,6 +46,12 @@ export namespace ExpectedExports { export type main = (effects: Effects) => Promise>; } +export type ConfigRes = { + /** This should be the previous config, that way during set config we start with the previous */ + config?: ConfigSpec; + /** Shape that is describing the form in the ui */ + spec: ConfigSpec; +}; /** Used to reach out from the pure js runtime */ export type Effects = { /** Usable when not sandboxed */ @@ -153,184 +162,6 @@ export type ActionResult = { copyable: boolean; qr: boolean; }; - -export type ConfigRes = { - /** This should be the previous config, that way during set config we start with the previous */ - config?: Config; - /** Shape that is describing the form in the ui */ - spec: ConfigSpec; -}; -export type Config = { - [propertyName: string]: unknown; -}; - -export type ConfigSpec = { - /** Given a config value, define what it should render with the following spec */ - [configValue: string]: ValueSpecAny; -}; -export type WithDefault = T & { - default: Default; -}; -export type WithNullableDefault = T & { - default?: Default; -}; - -export type WithDescription = T & { - description?: null | string; - name: string; - warning?: null | string; -}; - -export type ListSpec = { - spec: T; - range: string; -}; - -export type Tag = V & { - type: T; -}; - -export type Subtype = V & { - subtype: T; -}; - -export type Target = V & { - target: T; -}; - -export type UniqueBy = - | { - any: UniqueBy[]; - } - | string - | null; - -export type WithNullable = T & { - nullable: boolean; -}; -export type DefaultString = - | string - | { - /** The chars available for the randome generation */ - charset?: null | string; - /** Length that we generate to */ - len: number; - }; - -export type ValueSpecString = ( - | never // deno-lint-ignore ban-types - | {} - | { - pattern: string; - "pattern-description": string; - } -) & { - copyable?: null | boolean; - masked?: null | boolean; - placeholder?: null | string; -}; -export type ValueSpecNumber = { - /** Something like [3,6] or [0, *) */ - range?: null | string; - integral?: null | boolean; - /** Used a description of the units */ - units?: null | string; - placeholder?: null | number; -}; -export type ValueSpecBoolean = Record; -export type ValueSpecAny = - | Tag<"boolean", WithDescription>> - | Tag<"string", WithDescription, DefaultString>>> - | Tag<"number", WithDescription, number>>> - | Tag< - "enum", - WithDescription< - WithDefault< - { - values: readonly string[] | string[]; - "value-names": { - [key: string]: string; - }; - }, - string - > - > - > - | Tag<"list", ValueSpecList> - | Tag<"object", WithDescription>> - | Tag<"union", WithDescription>> - | Tag< - "pointer", - WithDescription< - | Subtype< - "package", - | Target< - "tor-key", - { - "package-id": string; - interface: string; - } - > - | Target< - "tor-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "lan-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "config", - { - "package-id": string; - selector: string; - multi: boolean; - } - > - > - | Subtype<"system", Record> - > - >; -export type ValueSpecUnion = { - /** What tag for the specification, for tag unions */ - tag: { - id: string; - name?: null | string; - description?: null | string; - "variant-names": { - [key: string]: string; - }; - }; - /** The possible enum values */ - variants: { - [key: string]: ConfigSpec; - }; - "display-as"?: null | string; - "unique-by"?: null | UniqueBy; -}; -export type ValueSpecObject = { - spec: ConfigSpec; - "display-as"?: null | string; - "unique-by"?: null | UniqueBy; -}; -export type ValueSpecList = - | Subtype<"boolean", WithDescription, boolean[]>>> - | Subtype<"string", WithDescription, string[]>>> - | Subtype<"number", WithDescription, number[]>>> - | Subtype<"enum", WithDescription, string[]>>> - | Subtype<"object", WithDescription, Record[]>>> - | Subtype<"union", WithDescription, string[]>>>; -export type ValueSpecEnum = { - values: string[]; - "value-names": { [key: string]: string }; -}; - export type SetResult = { /** These are the unix process signals */ signal: @@ -410,8 +241,8 @@ export type Dependencies = { /** Id is the id of the package, should be the same as the manifest */ [id: string]: { /** Checks are called to make sure that our dependency is in the correct shape. If a known error is returned we know that the dependency needs modification */ - check(effects: Effects, input: Config): Promise>; + check(effects: Effects, input: ConfigSpec): Promise>; /** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */ - autoConfigure(effects: Effects, input: Config): Promise>; + autoConfigure(effects: Effects, input: ConfigSpec): Promise>; }; }; diff --git a/types/config-types.ts b/types/config-types.ts new file mode 100644 index 0000000..0483504 --- /dev/null +++ b/types/config-types.ts @@ -0,0 +1,166 @@ +// deno-lint-ignore-file ban-types +export type ConfigSpec = Record; + +export type ValueType = "string" | "number" | "boolean" | "enum" | "list" | "object" | "pointer" | "union"; +export type ValueSpec = ValueSpecOf; + +// core spec types. These types provide the metadata for performing validations +export type ValueSpecOf = T extends "string" + ? ValueSpecString + : T extends "number" + ? ValueSpecNumber + : T extends "boolean" + ? ValueSpecBoolean + : T extends "enum" + ? ValueSpecEnum + : T extends "list" + ? ValueSpecList + : T extends "object" + ? ValueSpecObject + : T extends "pointer" + ? ValueSpecPointer + : T extends "union" + ? ValueSpecUnion + : never; + +export interface ValueSpecString extends ListValueSpecString, WithStandalone { + type: "string"; + default?: DefaultString; + nullable: boolean; + textarea?: boolean; +} + +export interface ValueSpecNumber extends ListValueSpecNumber, WithStandalone { + type: "number"; + nullable: boolean; + default?: number; +} + +export interface ValueSpecEnum extends ListValueSpecEnum, WithStandalone { + type: "enum"; + default: string; +} + +export interface ValueSpecBoolean extends WithStandalone { + type: "boolean"; + default: boolean; +} + +export interface ValueSpecUnion { + type: "union"; + tag: UnionTagSpec; + variants: { [key: string]: ConfigSpec }; + default: string; +} + +export interface ValueSpecPointer extends WithStandalone { + type: "pointer"; + subtype: "package" | "system"; + "package-id": string; + target: "system" | "lan-address" | "tor-address" | "config" | "tor-key"; + interface: string; // will only exist if target = tor-key || tor-address || lan-address + selector?: string; // will only exist if target = config + multi?: boolean; // will only exist if target = config +} + +export interface ValueSpecObject extends WithStandalone { + type: "object"; + spec: ConfigSpec; +} + +export interface WithStandalone { + name: string; + description?: null | string; + warning?: null | string; +} + +// no lists of booleans, lists, pointers +export type ListValueSpecType = "string" | "number" | "enum" | "object" | "union"; + +// represents a spec for the values of a list +export type ListValueSpecOf = T extends "string" + ? ListValueSpecString + : T extends "number" + ? ListValueSpecNumber + : T extends "enum" + ? ListValueSpecEnum + : T extends "object" + ? ListValueSpecObject + : T extends "union" + ? ListValueSpecUnion + : never; + +// represents a spec for a list +export type ValueSpecList = ValueSpecListOf; +export interface ValueSpecListOf extends WithStandalone { + type: "list"; + subtype: T; + spec: ListValueSpecOf; + range: string; // '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules + default: + | string[] + | number[] + | DefaultString[] + | object[] + | readonly string[] + | readonly number[] + | readonly DefaultString[] + | readonly object[]; +} + +// sometimes the type checker needs just a little bit of help +export function isValueSpecListOf(t: ValueSpecList, s: S): t is ValueSpecListOf { + return t.subtype === s; +} + +export interface ListValueSpecString { + pattern?: string; + "pattern-description"?: string; + masked: boolean; + placeholder?: string; +} + +export interface ListValueSpecNumber { + range: string; + integral: boolean; + units?: null | string; + placeholder?: null | string; +} + +export interface ListValueSpecEnum { + values: string[] | readonly string[]; + "value-names": { [value: string]: string }; +} + +export interface ListValueSpecObject { + spec: ConfigSpec; // 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 + "display-as"?: string; // this should be a handlebars template which can make use of the entire config which corresponds to 'spec' +} + +export type UniqueBy = + | null + | string + | { any: readonly UniqueBy[] | UniqueBy[] } + | { all: readonly UniqueBy[] | UniqueBy[] }; + +export interface ListValueSpecUnion { + tag: UnionTagSpec; + variants: { [key: string]: ConfigSpec }; + "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 + "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 +} + +export interface UnionTagSpec { + id: string; // The name of the field containing one of the union variants + "variant-names": { + // the name of each variant + [variant: string]: string; + }; + name: string; + description?: null | string; + warning?: null | string; +} + +export type DefaultString = string | { charset: string; len: number }; diff --git a/utils/propertiesMatcher.test.ts b/utils/propertiesMatcher.test.ts index e462fef..04f61e7 100644 --- a/utils/propertiesMatcher.test.ts +++ b/utils/propertiesMatcher.test.ts @@ -77,6 +77,7 @@ const bitcoinProperties = { spec: { pattern: "^[a-zA-Z0-9_-]+:([0-9a-fA-F]{2})+\\$([0-9a-fA-F]{2})+$", "pattern-description": 'Each item must be of the form ":$".', + masked: false, }, range: "[0,*)", }, @@ -243,6 +244,7 @@ const bitcoinProperties = { range: "[0,*)", default: Array>(), spec: { + "unique-by": null, spec: { hostname: { type: "string", @@ -253,6 +255,7 @@ const bitcoinProperties = { "(^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$)|((^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$)|(^[a-z2-7]{16}\\.onion$)|(^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$))", "pattern-description": "Must be either a domain name, or an IPv4 or IPv6 address. Do not include protocol scheme (eg 'http://') or port.", + masked: false, }, port: { type: "number", @@ -604,10 +607,10 @@ const { test } = Deno; description: "A list of pubkeys that are permitted to publish through your relay. A minimum, you need to enter your own Nostr hex (not npub) pubkey. Go to https://damus.io/key/ to convert from npub to hex.", type: "list", - nullable: true, range: "[1,*)", subtype: "string", spec: { + masked: false, placeholder: "hex (not npub) pubkey", pattern: "[0-9a-fA-F]{3}", "pattern-description": diff --git a/utils/propertiesMatcher.ts b/utils/propertiesMatcher.ts index e9a2862..636ab6e 100644 --- a/utils/propertiesMatcher.ts +++ b/utils/propertiesMatcher.ts @@ -1,5 +1,5 @@ import { matches } from "../dependencies.ts"; -import { ConfigSpec, ValueSpecAny } from "../types.ts"; +import { ConfigSpec, ValueSpec as ValueSpecAny } from "../types/config-types.ts"; type TypeBoolean = "boolean"; type TypeString = "string"; @@ -215,13 +215,11 @@ export function guardAll(value: A): matches.Parser true); - const { default: _, ...arrayOfSpec } = spec; - const subtype = matchSubType.unsafeCast(value).subtype; return defaultNullable( matches // deno-lint-ignore no-explicit-any - .arrayOf(guardAll({ type: subtype, ...arrayOfSpec } as any)) + .arrayOf(guardAll({ type: subtype, ...spec } as any)) .validate((x) => rangeValidate(x.length), "valid length"), value // deno-lint-ignore no-explicit-any