mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 02:11:56 +00:00
chore: Use the types from the fronted system
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 extends string, type extends "up" | "down"> {
|
||||
version: version;
|
||||
@@ -16,14 +17,11 @@ export interface NoRepeat<version extends string, type extends "up" | "down"> {
|
||||
* @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<T.Config>,
|
||||
configured: boolean,
|
||||
noRepeat?: NoRepeat<version, type>,
|
||||
noFail = false,
|
||||
export function updateConfig<version extends string, type extends "up" | "down">(
|
||||
fn: (config: ConfigSpec, effects: T.Effects) => ConfigSpec | Promise<ConfigSpec>,
|
||||
configured: boolean,
|
||||
noRepeat?: NoRepeat<version, type>,
|
||||
noFail = false
|
||||
): M.MigrationFn<version, type> {
|
||||
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<version, type> | undefined,
|
||||
fn: () => Promise<void>,
|
||||
export async function noRepeatGuard<version extends string, type extends "up" | "down">(
|
||||
effects: T.Effects,
|
||||
noRepeat: NoRepeat<version, type> | undefined,
|
||||
fn: () => Promise<void>
|
||||
): Promise<void> {
|
||||
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<versions extends string>(
|
||||
effects: T.Effects,
|
||||
migrations: M.MigrationMapping<versions>,
|
||||
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<versions extends string>(
|
||||
|
||||
export function fromMapping<versions extends string>(
|
||||
migrations: M.MigrationMapping<versions>,
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<A extends ConfigSpec> extends IBuilder<A> {
|
||||
static empty() {
|
||||
return new Config({});
|
||||
}
|
||||
static withValue<K extends string, B extends ValueSpecAny>(key: K, value: Value<B>) {
|
||||
static withValue<K extends string, B extends ValueSpec>(key: K, value: Value<B>) {
|
||||
return new Config({
|
||||
[key]: value.build(),
|
||||
} as { [key in K]: B });
|
||||
}
|
||||
|
||||
static of<B extends { [key: string]: Value<C> }, C extends ValueSpecAny>(spec: B) {
|
||||
static of<B extends { [key: string]: Value<C> }, C extends ValueSpec>(spec: B) {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const answer: { [K in keyof B]: BuilderExtract<B[K]> } = {} as any;
|
||||
for (const key in spec) {
|
||||
@@ -21,13 +21,13 @@ export class Config<A extends ConfigSpec> extends IBuilder<A> {
|
||||
}
|
||||
return new Config(answer);
|
||||
}
|
||||
withValue<K extends string, B extends ValueSpecAny>(key: K, value: Value<B>) {
|
||||
withValue<K extends string, B extends ValueSpec>(key: K, value: Value<B>) {
|
||||
return new Config({
|
||||
...this.a,
|
||||
[key]: value.build(),
|
||||
} as A & { [key in K]: B });
|
||||
}
|
||||
addValue<K extends string, B extends ValueSpecAny>(key: K, value: Value<B>) {
|
||||
addValue<K extends string, B extends ValueSpec>(key: K, value: Value<B>) {
|
||||
return new Config({
|
||||
...this.a,
|
||||
[key]: value.build(),
|
||||
|
||||
@@ -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<A extends Tag<"list", ValueSpecList>> extends IBuilder<A> {
|
||||
// deno-lint-ignore ban-types
|
||||
static boolean<A extends Description & Default<boolean[]> & { range: string; spec: {} }>(a: A) {
|
||||
return new List({
|
||||
type: "list" as const,
|
||||
subtype: "boolean" as const,
|
||||
...a,
|
||||
});
|
||||
}
|
||||
export class List<A extends ValueSpecList> extends IBuilder<A> {
|
||||
// // deno-lint-ignore ban-types
|
||||
// static boolean<A extends Description & Default<boolean[]> & { range: string; spec: {}; default: boolean }>(a: A) {
|
||||
// return new List({
|
||||
// type: "list" as const,
|
||||
// subtype: "boolean" as const,
|
||||
// ...a,
|
||||
// });
|
||||
// }
|
||||
|
||||
static string<A extends Description & Default<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<T.WithDefault<T.ListSpec<T.ValueSpecString>, string[]>>>>);
|
||||
} as ValueSpecListOf<"string">);
|
||||
}
|
||||
static number<A extends Description & Default<number[]> & { range: string; spec: NumberSpec }>(a: A) {
|
||||
return new List({
|
||||
@@ -75,7 +75,7 @@ export class List<A extends Tag<"list", ValueSpecList>> extends IBuilder<A> {
|
||||
type: "list" as const,
|
||||
subtype: "object" as const,
|
||||
...value,
|
||||
} as T.Tag<"list", T.Subtype<"object", T.WithDescription<T.WithNullableDefault<T.ListSpec<T.ValueSpecObject>, Record<string, unknown>[]>>>>);
|
||||
} as ValueSpecListOf<"object">);
|
||||
}
|
||||
static union<
|
||||
A extends Description &
|
||||
@@ -84,15 +84,16 @@ export class List<A extends Tag<"list", ValueSpecList>> extends IBuilder<A> {
|
||||
spec: {
|
||||
tag: {
|
||||
id: string;
|
||||
name: null | string | undefined;
|
||||
description: null | string | undefined;
|
||||
name: string;
|
||||
description: null | string;
|
||||
"variant-names": {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
variants: Variants<B>;
|
||||
"display-as": null | string | undefined;
|
||||
"unique-by": null | UniqueBy | undefined;
|
||||
"display-as": null | string;
|
||||
"unique-by": UniqueBy;
|
||||
default: string;
|
||||
};
|
||||
},
|
||||
B extends { [key: string]: ConfigSpec }
|
||||
|
||||
@@ -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<A extends ValueSpecAny> extends IBuilder<A> {
|
||||
export class Pointer<A extends ValueSpec> extends IBuilder<A> {
|
||||
static packageTorKey<A extends Description & { "package-id": string; interface: string }>(a: A) {
|
||||
return new Pointer({
|
||||
type: "pointer" as const,
|
||||
@@ -27,7 +27,9 @@ export class Pointer<A extends ValueSpecAny> extends IBuilder<A> {
|
||||
...a,
|
||||
});
|
||||
}
|
||||
static packageConfig<A extends Description & { "package-id": string; selector: string; multi: boolean }>(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<A extends ValueSpecAny> extends IBuilder<A> {
|
||||
...a,
|
||||
});
|
||||
}
|
||||
static system<A extends Description & Record<string, unknown>>(a: A) {
|
||||
static system<A extends Description & { "package-id": string; selector: string; multi: boolean; interface: string }>(
|
||||
a: A
|
||||
) {
|
||||
return new Pointer({
|
||||
type: "pointer" as const,
|
||||
subtype: "system" as const,
|
||||
target: "system" as const,
|
||||
...a,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<A extends ValueSpecAny> extends IBuilder<A> {
|
||||
export class Value<A extends ValueSpec> extends IBuilder<A> {
|
||||
static boolean<A extends Description & Default<boolean>>(a: A) {
|
||||
return new Value({
|
||||
type: "boolean" as const,
|
||||
@@ -63,13 +62,13 @@ export class Value<A extends ValueSpecAny> extends IBuilder<A> {
|
||||
return new Value({
|
||||
type: "string" as const,
|
||||
...a,
|
||||
} as Tag<"string", T.WithDescription<T.WithNullableDefault<T.WithNullable<T.ValueSpecString>, DefaultString>>>);
|
||||
} as ValueSpecString);
|
||||
}
|
||||
static number<A extends Description & NullableDefault<number> & Nullable & NumberSpec>(a: A) {
|
||||
return new Value({
|
||||
type: "number" as const,
|
||||
...a,
|
||||
} as Tag<"number", T.WithDescription<T.WithNullableDefault<T.WithNullable<T.ValueSpecNumber>, number>>>);
|
||||
} as ValueSpecNumber);
|
||||
}
|
||||
static enum<
|
||||
A extends Description &
|
||||
@@ -103,15 +102,16 @@ export class Value<A extends ValueSpecAny> extends IBuilder<A> {
|
||||
Default<string> & {
|
||||
tag: {
|
||||
id: string;
|
||||
name: string | null;
|
||||
name: string;
|
||||
description: string | null;
|
||||
warning: string | null;
|
||||
"variant-names": {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
variants: Variants<B>;
|
||||
"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<A extends ValueSpecAny> extends IBuilder<A> {
|
||||
});
|
||||
}
|
||||
|
||||
static pointer<A extends ValueSpecAny>(a: Pointer<A>) {
|
||||
static pointer<A extends ValueSpec>(a: Pointer<A>) {
|
||||
return new Value(a.build());
|
||||
}
|
||||
static list<A extends List<B>, B extends Tag<"list", ValueSpecList>>(a: A) {
|
||||
static list<A extends List<B>, B extends ValueSpecList>(a: A) {
|
||||
return new Value(a.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
1
mod.ts
1
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";
|
||||
|
||||
195
types.ts
195
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<ResultType<SetResult>>;
|
||||
export type setConfig = (effects: Effects, input: ConfigSpec) => Promise<ResultType<SetResult>>;
|
||||
/** 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<ResultType<ConfigRes>>;
|
||||
/** 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<ResultType<ActionResult>>;
|
||||
[id: string]: (effects: Effects, config?: ConfigSpec) => Promise<ResultType<ActionResult>>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -43,6 +46,12 @@ export namespace ExpectedExports {
|
||||
export type main = (effects: Effects) => Promise<ResultType<unknown>>;
|
||||
}
|
||||
|
||||
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> = T & {
|
||||
default: Default;
|
||||
};
|
||||
export type WithNullableDefault<T, Default> = T & {
|
||||
default?: Default;
|
||||
};
|
||||
|
||||
export type WithDescription<T> = T & {
|
||||
description?: null | string;
|
||||
name: string;
|
||||
warning?: null | string;
|
||||
};
|
||||
|
||||
export type ListSpec<T> = {
|
||||
spec: T;
|
||||
range: string;
|
||||
};
|
||||
|
||||
export type Tag<T extends string, V> = V & {
|
||||
type: T;
|
||||
};
|
||||
|
||||
export type Subtype<T extends string, V> = V & {
|
||||
subtype: T;
|
||||
};
|
||||
|
||||
export type Target<T extends string, V> = V & {
|
||||
target: T;
|
||||
};
|
||||
|
||||
export type UniqueBy =
|
||||
| {
|
||||
any: UniqueBy[];
|
||||
}
|
||||
| string
|
||||
| null;
|
||||
|
||||
export type WithNullable<T> = 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<string, unknown>;
|
||||
export type ValueSpecAny =
|
||||
| Tag<"boolean", WithDescription<WithDefault<ValueSpecBoolean, boolean>>>
|
||||
| Tag<"string", WithDescription<WithNullableDefault<WithNullable<ValueSpecString>, DefaultString>>>
|
||||
| Tag<"number", WithDescription<WithNullableDefault<WithNullable<ValueSpecNumber>, number>>>
|
||||
| Tag<
|
||||
"enum",
|
||||
WithDescription<
|
||||
WithDefault<
|
||||
{
|
||||
values: readonly string[] | string[];
|
||||
"value-names": {
|
||||
[key: string]: string;
|
||||
};
|
||||
},
|
||||
string
|
||||
>
|
||||
>
|
||||
>
|
||||
| Tag<"list", ValueSpecList>
|
||||
| Tag<"object", WithDescription<WithNullableDefault<ValueSpecObject, Config>>>
|
||||
| Tag<"union", WithDescription<WithDefault<ValueSpecUnion, string>>>
|
||||
| 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<string, unknown>>
|
||||
>
|
||||
>;
|
||||
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<WithDefault<ListSpec<ValueSpecBoolean>, boolean[]>>>
|
||||
| Subtype<"string", WithDescription<WithDefault<ListSpec<ValueSpecString>, string[]>>>
|
||||
| Subtype<"number", WithDescription<WithDefault<ListSpec<ValueSpecNumber>, number[]>>>
|
||||
| Subtype<"enum", WithDescription<WithDefault<ListSpec<ValueSpecEnum>, string[]>>>
|
||||
| Subtype<"object", WithDescription<WithNullableDefault<ListSpec<ValueSpecObject>, Record<string, unknown>[]>>>
|
||||
| Subtype<"union", WithDescription<WithDefault<ListSpec<ValueSpecUnion>, 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<ResultType<void | null>>;
|
||||
check(effects: Effects, input: ConfigSpec): Promise<ResultType<void | null>>;
|
||||
/** 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<ResultType<Config>>;
|
||||
autoConfigure(effects: Effects, input: ConfigSpec): Promise<ResultType<ConfigSpec>>;
|
||||
};
|
||||
};
|
||||
|
||||
166
types/config-types.ts
Normal file
166
types/config-types.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
// deno-lint-ignore-file ban-types
|
||||
export type ConfigSpec = Record<string, ValueSpec>;
|
||||
|
||||
export type ValueType = "string" | "number" | "boolean" | "enum" | "list" | "object" | "pointer" | "union";
|
||||
export type ValueSpec = ValueSpecOf<ValueType>;
|
||||
|
||||
// core spec types. These types provide the metadata for performing validations
|
||||
export type ValueSpecOf<T extends ValueType> = 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 ListValueSpecType> = 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<ListValueSpecType>;
|
||||
export interface ValueSpecListOf<T extends ListValueSpecType> extends WithStandalone {
|
||||
type: "list";
|
||||
subtype: T;
|
||||
spec: ListValueSpecOf<T>;
|
||||
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<S extends ListValueSpecType>(t: ValueSpecList, s: S): t is ValueSpecListOf<S> {
|
||||
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 };
|
||||
@@ -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 "<USERNAME>:<SALT>$<HASH>".',
|
||||
masked: false,
|
||||
},
|
||||
range: "[0,*)",
|
||||
},
|
||||
@@ -243,6 +244,7 @@ const bitcoinProperties = {
|
||||
range: "[0,*)",
|
||||
default: Array<Record<string, unknown>>(),
|
||||
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":
|
||||
|
||||
@@ -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<A extends ValueSpecAny>(value: A): matches.Parser<unkno
|
||||
const spec = (matchSpec.test(value) && value.spec) || {};
|
||||
const rangeValidate = (matchRange.test(value) && matchNumberWithRange(value.range).test) || (() => 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
|
||||
|
||||
Reference in New Issue
Block a user