Fix StartOS 0.4 TypeScript packaging to match SDK API

This commit is contained in:
MacPro
2026-04-09 15:10:44 -05:00
parent 68ec875ee7
commit 8298c083c7
3436 changed files with 867051 additions and 92 deletions
+169
View File
@@ -0,0 +1,169 @@
import { ActionId, ActionInput, ActionMetadata, SetMainStatus, DependencyRequirement, CheckDependenciesResult, SetHealth, BindParams, HostId, NetInfo, Host, ExportServiceInterfaceParams, ServiceInterface, CreateTaskParams, MountParams, StatusInfo, Manifest } from './osBindings';
import { PackageId, Dependencies, ServiceInterfaceId, SmtpValue, ActionResult, PluginHostnameInfo } from './types';
/** Used to reach out from the pure js runtime */
export type Effects = {
readonly eventId: string | null;
child: (name: string) => Effects;
constRetry?: () => void;
isInContext: boolean;
onLeaveContext: (fn: () => void | null | undefined) => void;
clearCallbacks: (options: {
only: number[];
} | {
except: number[];
}) => Promise<null>;
action: {
/** Define an action that can be invoked by a user or service */
export(options: {
id: ActionId;
metadata: ActionMetadata;
}): Promise<null>;
/** Remove all exported actions */
clear(options: {
except: ActionId[];
}): Promise<null>;
getInput(options: {
packageId?: PackageId;
actionId: ActionId;
}): Promise<ActionInput | null>;
run<Input extends Record<string, unknown>>(options: {
packageId?: PackageId;
actionId: ActionId;
input?: Input;
}): Promise<ActionResult | null>;
createTask(options: CreateTaskParams): Promise<null>;
clearTasks(options: {
only: string[];
} | {
except: string[];
}): Promise<null>;
};
/** restart this service's main function */
restart(): Promise<null>;
/** stop this service's main function */
shutdown(): Promise<null>;
/** ask the host os what the service's current status is */
getStatus(options: {
packageId?: PackageId;
callback?: () => void;
}): Promise<StatusInfo | null>;
/** DEPRECATED: indicate to the host os what runstate the service is in */
setMainStatus(options: SetMainStatus): Promise<null>;
/** Set the dependencies of what the service needs, usually run during the inputSpec action as a best practice */
setDependencies(options: {
dependencies: Dependencies;
}): Promise<null>;
/** Get the list of the dependencies, both the dynamic set by the effect of setDependencies and the end result any required in the manifest */
getDependencies(): Promise<DependencyRequirement[]>;
/** Test whether current dependency requirements are satisfied */
checkDependencies(options: {
packageIds?: PackageId[];
}): Promise<CheckDependenciesResult[]>;
/** mount a volume of a dependency */
mount(options: MountParams): Promise<string>;
/** Returns a list of the ids of all installed packages */
getInstalledPackages(): Promise<string[]>;
/** Returns the manifest of a service */
getServiceManifest(options: {
packageId: PackageId;
callback?: () => void;
}): Promise<Manifest>;
/** sets the result of a health check */
setHealth(o: SetHealth): Promise<null>;
subcontainer: {
/** A low level api used by SubContainer */
createFs(options: {
imageId: string;
name: string | null;
}): Promise<[string, string]>;
/** A low level api used by SubContainer */
destroyFs(options: {
guid: string;
}): Promise<null>;
};
/** Creates a host connected to the specified port with the provided options */
bind(options: BindParams): Promise<null>;
/** Get the port address for a service */
getServicePortForward(options: {
packageId?: PackageId;
hostId: HostId;
internalPort: number;
}): Promise<NetInfo>;
/** Removes all network bindings, called in the setupInputSpec */
clearBindings(options: {
except: {
id: HostId;
internalPort: number;
}[];
}): Promise<null>;
/** Returns information about the specified host, if it exists */
getHostInfo(options: {
packageId?: PackageId;
hostId: HostId;
callback?: () => void;
}): Promise<Host | null>;
/** Returns the IP address of the container */
getContainerIp(options: {
packageId?: PackageId;
callback?: () => void;
}): Promise<string>;
/** Returns the IP address of StartOS */
getOsIp(): Promise<string>;
/** Returns the effective outbound gateway for this service */
getOutboundGateway(options: {
callback?: () => void;
}): Promise<string>;
/** Creates an interface bound to a specific host and port to show to the user */
exportServiceInterface(options: ExportServiceInterfaceParams): Promise<null>;
/** Returns an exported service interface */
getServiceInterface(options: {
packageId?: PackageId;
serviceInterfaceId: ServiceInterfaceId;
callback?: () => void;
}): Promise<ServiceInterface | null>;
/** Returns all exported service interfaces for a package */
listServiceInterfaces(options: {
packageId?: PackageId;
callback?: () => void;
}): Promise<Record<ServiceInterfaceId, ServiceInterface>>;
/** Removes all service interfaces */
clearServiceInterfaces(options: {
except: ServiceInterfaceId[];
}): Promise<null>;
plugin: {
url: {
register(options: {
tableAction: ActionId;
}): Promise<null>;
exportUrl(options: {
hostnameInfo: PluginHostnameInfo;
removeAction: ActionId | null;
overflowActions: ActionId[];
}): Promise<null>;
clearUrls(options: {
except: PluginHostnameInfo[];
}): Promise<null>;
};
};
/** Returns a PEM encoded fullchain for the hostnames specified */
getSslCertificate: (options: {
hostnames: string[];
algorithm?: 'ecdsa' | 'ed25519';
callback?: () => void;
}) => Promise<[string, string, string]>;
/** Returns a PEM encoded private key corresponding to the certificate for the hostnames specified */
getSslKey: (options: {
hostnames: string[];
algorithm?: 'ecdsa' | 'ed25519';
}) => Promise<string>;
/** sets the version that this service's data has been migrated to */
setDataVersion(options: {
version: string | null;
}): Promise<null>;
/** returns the version that this service's data has been migrated to */
getDataVersion(): Promise<string | null>;
/** Returns globally configured SMTP settings, if they exist */
getSystemSmtp(options: {
callback?: () => void;
}): Promise<SmtpValue | null>;
};
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=Effects.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Effects.js","sourceRoot":"","sources":["../../../base/lib/Effects.ts"],"names":[],"mappings":""}
+40
View File
@@ -0,0 +1,40 @@
import * as T from '../types';
import * as IST from '../actions/input/inputSpecTypes';
import { Action, ActionInfo } from './setupActions';
export type RunActionInput<Input> = Input | ((prev?: {
spec: IST.InputSpec;
value: Input | null;
}) => Input);
export declare const runAction: <Input extends Record<string, unknown>>(options: {
effects: T.Effects;
actionId: T.ActionId;
input?: RunActionInput<Input>;
}) => Promise<T.ActionResult | null>;
type GetActionInputType<A extends ActionInfo<T.ActionId, any>> = A extends Action<T.ActionId, infer I> ? I : never;
type TaskBase = {
reason?: string;
replayId?: string;
};
type TaskInput<T extends ActionInfo<T.ActionId, any>> = {
kind: 'partial';
value: T.DeepPartial<GetActionInputType<T>>;
};
export type TaskOptions<T extends ActionInfo<T.ActionId, any>> = TaskBase & ({
when?: Exclude<T.TaskTrigger, {
condition: 'input-not-matches';
}>;
input?: TaskInput<T>;
} | {
when: T.TaskTrigger & {
condition: 'input-not-matches';
};
input: TaskInput<T>;
});
export declare const createTask: <T extends ActionInfo<T.ActionId, any>>(options: {
effects: T.Effects;
packageId: T.PackageId;
action: T;
severity: T.TaskSeverity;
options?: TaskOptions<T>;
}) => Promise<null>;
export {};
+70
View File
@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTask = exports.runAction = void 0;
const runAction = async (options) => {
if (options.input) {
if (options.input instanceof Function) {
const prev = await options.effects.action.getInput({
// packageId: options.packageId,
actionId: options.actionId,
});
const input = options.input(prev
? { spec: prev.spec, value: prev.value }
: undefined);
return options.effects.action.run({
// packageId: options.packageId,
actionId: options.actionId,
input,
});
}
else {
return options.effects.action.run({
// packageId: options.packageId,
actionId: options.actionId,
input: options.input,
});
}
}
else {
return options.effects.action.run({
// packageId: options.packageId,
actionId: options.actionId,
});
}
};
exports.runAction = runAction;
const _validate = {};
/** Recursively converts undefined values to null so they survive JSON serialization */
function undefinedToNull(obj) {
if (obj === undefined)
return null;
if (obj === null || typeof obj !== 'object')
return obj;
if (Array.isArray(obj))
return obj.map(undefinedToNull);
const result = {};
for (const [k, v] of Object.entries(obj)) {
result[k] = undefinedToNull(v);
}
return result;
}
const createTask = (options) => {
const request = options.options || {};
const actionId = options.action.id;
const input = 'input' in request && request.input
? { ...request.input, value: undefinedToNull(request.input.value) }
: request.input;
const req = {
...request,
input,
actionId,
packageId: options.packageId,
action: undefined,
severity: options.severity,
replayId: request.replayId || `${options.packageId}:${actionId}`,
};
delete req.action;
return options.effects.action.createTask(req);
};
exports.createTask = createTask;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../base/lib/actions/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
import { InputSpec } from './inputSpec';
import { List } from './list';
import { Value } from './value';
import { Variants } from './variants';
export { InputSpec as InputSpec, List, Value, Variants };
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Variants = exports.Value = exports.List = exports.InputSpec = void 0;
const inputSpec_1 = require("./inputSpec");
Object.defineProperty(exports, "InputSpec", { enumerable: true, get: function () { return inputSpec_1.InputSpec; } });
const list_1 = require("./list");
Object.defineProperty(exports, "List", { enumerable: true, get: function () { return list_1.List; } });
const value_1 = require("./value");
Object.defineProperty(exports, "Value", { enumerable: true, get: function () { return value_1.Value; } });
const variants_1 = require("./variants");
Object.defineProperty(exports, "Variants", { enumerable: true, get: function () { return variants_1.Variants; } });
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../base/lib/actions/input/builder/index.ts"],"names":[],"mappings":";;;AAAA,2CAAuC;AAKjB,0FALb,qBAAS,OAKa;AAJ/B,iCAA6B;AAII,qFAJxB,WAAI,OAIwB;AAHrC,mCAA+B;AAGQ,sFAH9B,aAAK,OAG8B;AAF5C,yCAAqC;AAES,yFAFrC,mBAAQ,OAEqC"}
@@ -0,0 +1,222 @@
import { ValueSpec } from '../inputSpecTypes';
import { Value } from './value';
import { Effects } from '../../../Effects';
import { z } from 'zod';
import { DeepPartial } from '../../../types';
import { InputSpecTools } from './inputSpecTools';
/** Options passed to a lazy builder function when resolving dynamic form field values. */
export type LazyBuildOptions<Type> = {
/** The effects interface for runtime operations (e.g. reading files, querying state). */
effects: Effects;
/** Previously saved form data to pre-fill the form with, or `null` for fresh creation. */
prefill: DeepPartial<Type> | null;
};
/**
* A function that lazily produces a value, potentially using effects and prefill data.
* Used by `dynamic*` variants of {@link Value} to compute form field options at runtime.
*/
export type LazyBuild<ExpectedOut, Type> = (options: LazyBuildOptions<Type>) => Promise<ExpectedOut> | ExpectedOut;
/**
* Defines which keys to keep when filtering an InputSpec.
* Use `true` to keep a field as-is, or a nested object to filter sub-fields of an object-typed field.
*/
export type FilterKeys<F> = {
[K in keyof F]?: F[K] extends Record<string, any> ? boolean | FilterKeys<F[K]> : boolean;
};
type RetainKey<T, F, Default extends boolean> = {
[K in keyof T]: K extends keyof F ? F[K] extends false ? never : K : Default extends true ? K : never;
}[keyof T];
/**
* Computes the resulting type after applying a {@link FilterKeys} shape to a type.
*/
export type ApplyFilter<T, F, Default extends boolean = false> = {
[K in RetainKey<T, F, Default>]: K extends keyof F ? true extends F[K] ? F[K] extends true ? T[K] : T[K] | undefined : T[K] extends Record<string, any> ? F[K] extends FilterKeys<T[K]> ? ApplyFilter<T[K], F[K]> : undefined : undefined : Default extends true ? T[K] : undefined;
};
/**
* Computes the union of all valid key-path tuples through a nested type.
* Each tuple represents a path from root to a field, recursing into object-typed sub-fields.
*/
export type KeyPaths<T> = {
[K in keyof T & string]: T[K] extends any[] ? [K] : T[K] extends Record<string, any> ? [K] | [K, ...KeyPaths<T[K]>] : [K];
}[keyof T & string];
/** Extracts the runtime type from an {@link InputSpec}. */
export type ExtractInputSpecType<A extends InputSpec<Record<string, any>, any>> = A extends InputSpec<infer B, any> ? B : never;
/** Extracts the static validation type from an {@link InputSpec}. */
export type ExtractInputSpecStaticValidatedAs<A extends InputSpec<any, Record<string, any>>> = A extends InputSpec<any, infer B> ? B : never;
/** Maps an object type to a record of {@link Value} entries for use with `InputSpec.of`. */
export type InputSpecOf<A extends Record<string, any>> = {
[K in keyof A]: Value<A[K]>;
};
/** A value that is either directly provided or lazily computed via a {@link LazyBuild} function. */
export type MaybeLazyValues<A, T> = LazyBuild<A, T> | A;
/**
* InputSpecs are the specs that are used by the os input specification form for this service.
* Here is an example of a simple input specification
```ts
const smallInputSpec = InputSpec.of({
test: Value.boolean({
name: "Test",
description: "This is the description for the test",
warning: null,
default: false,
}),
});
```
The idea of an inputSpec is that now the form is going to ask for
Test: [ ] and the value is going to be checked as a boolean.
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 inputSpec spec.
```ts
const matchSmallInputSpec = smallInputSpec.validator();
type SmallInputSpec = typeof matchSmallInputSpec._TYPE;
```
Here is an example of a more complex input specification which came from an input specification for a service
that works with bitcoin, like c-lightning.
```ts
export const hostname = Value.string({
name: "Hostname",
default: null,
description: "Domain or IP address of bitcoin peer",
warning: null,
required: true,
masked: false,
placeholder: null,
pattern:
"(^(?:(?: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]$))",
patternDescription:
"Must be either a domain name, or an IPv4 or IPv6 address. Do not include protocol scheme (eg 'http://') or port.",
});
export const port = Value.number({
name: "Port",
default: null,
description: "Port that peer is listening on for inbound p2p connections",
warning: null,
required: false,
range: "[0,65535]",
integral: true,
units: null,
placeholder: null,
});
export const addNodesSpec = InputSpec.of({ hostname: hostname, port: port });
```
*/
export declare class InputSpec<Type extends StaticValidatedAs, StaticValidatedAs extends Record<string, any> = Type> {
private readonly spec;
readonly validator: z.ZodType<StaticValidatedAs>;
private constructor();
_TYPE: Type;
_PARTIAL: DeepPartial<Type>;
readonly partialValidator: z.ZodType<DeepPartial<StaticValidatedAs>>;
/**
* Builds the runtime form specification and combined Zod validator from this InputSpec's fields.
*
* @returns An object containing the resolved `spec` (field specs keyed by name) and a combined `validator`
*/
build<OuterType>(options: LazyBuildOptions<OuterType>): Promise<{
spec: {
[K in keyof Type]: ValueSpec;
};
validator: z.ZodType<Type>;
}>;
/**
* Adds multiple fields to this spec at once, returning a new `InputSpec` with extended types.
*
* @param build - A record of {@link Value} entries, or a function receiving typed tools that returns one
*/
add<AddSpec extends Record<string, Value<any, any, any>>>(build: AddSpec | ((tools: InputSpecTools<Type>) => AddSpec)): InputSpec<Type & {
[K in keyof AddSpec]: AddSpec[K] extends Value<infer T, any, any> ? T : never;
}, StaticValidatedAs & {
[K in keyof AddSpec]: AddSpec[K] extends Value<any, infer S, any> ? S : never;
}>;
/**
* Returns a new InputSpec containing only the specified keys.
* Use `true` to keep a field as-is, or a nested object to filter sub-fields of object-typed fields.
*
* @example
* ```ts
* const full = InputSpec.of({
* name: Value.text({ name: 'Name', required: true, default: null }),
* settings: Value.object({ name: 'Settings' }, InputSpec.of({
* debug: Value.toggle({ name: 'Debug', default: false }),
* port: Value.number({ name: 'Port', required: true, default: 8080, integer: true }),
* })),
* })
* const filtered = full.filter({ name: true, settings: { debug: true } })
* ```
*/
filter<F extends FilterKeys<Type>, Default extends boolean = false>(keys: F, keepByDefault?: Default): InputSpec<ApplyFilter<Type, F, Default> & ApplyFilter<StaticValidatedAs, F, Default>, ApplyFilter<StaticValidatedAs, F, Default>>;
/**
* Returns a new InputSpec with the specified keys disabled.
* Use `true` to disable a field, or a nested object to disable sub-fields of object-typed fields.
* All fields remain in the spec — disabled fields simply cannot be edited by the user.
*
* @param keys - Which fields to disable, using the same shape as {@link FilterKeys}
* @param message - The reason the fields are disabled, displayed to the user
*
* @example
* ```ts
* const spec = InputSpec.of({
* name: Value.text({ name: 'Name', required: true, default: null }),
* settings: Value.object({ name: 'Settings' }, InputSpec.of({
* debug: Value.toggle({ name: 'Debug', default: false }),
* port: Value.number({ name: 'Port', required: true, default: 8080, integer: true }),
* })),
* })
* const disabled = spec.disable({ name: true, settings: { debug: true } }, 'Managed by the system')
* ```
*/
disable(keys: FilterKeys<Type>, message: string): InputSpec<Type, StaticValidatedAs>;
/**
* Resolves a key path to its corresponding display name path.
* Each key is mapped to the `name` property of its built {@link ValueSpec}.
* Recurses into `Value.object` sub-specs for nested paths.
*
* @param path - Typed tuple of field keys (e.g. `["settings", "debug"]`)
* @param options - Build options providing effects and prefill data
* @returns Array of display names (e.g. `["Settings", "Debug"]`)
*/
namePath<OuterType>(path: KeyPaths<Type>, options: LazyBuildOptions<OuterType>): Promise<string[]>;
/**
* Resolves a key path to the description of the target field.
* Recurses into `Value.object` sub-specs for nested paths.
*
* @param path - Typed tuple of field keys (e.g. `["settings", "debug"]`)
* @param options - Build options providing effects and prefill data
* @returns The description string, or `null` if the field has no description or was not found
*/
description<OuterType>(path: KeyPaths<Type>, options: LazyBuildOptions<OuterType>): Promise<string | null>;
/**
* Returns a new InputSpec filtered to only include keys present in the given partial object.
* For nested `Value.object` fields, recurses into the partial value to filter sub-fields.
*
* @param partial - A deep-partial object whose defined keys determine which fields to keep
*/
filterFromPartial(partial: DeepPartial<Type>): InputSpec<DeepPartial<Type> & DeepPartial<StaticValidatedAs>, DeepPartial<StaticValidatedAs>>;
/**
* Returns a new InputSpec with fields disabled based on which keys are present in the given partial object.
* For nested `Value.object` fields, recurses into the partial value to disable sub-fields.
* All fields remain in the spec — disabled fields simply cannot be edited by the user.
*
* @param partial - A deep-partial object whose defined keys determine which fields to disable
* @param message - The reason the fields are disabled, displayed to the user
*/
disableFromPartial(partial: DeepPartial<Type>, message: string): InputSpec<Type, StaticValidatedAs>;
/**
* Creates an `InputSpec` from a plain record of {@link Value} entries.
*
* @example
* ```ts
* const spec = InputSpec.of({
* username: Value.text({ name: 'Username', required: true, default: null }),
* verbose: Value.toggle({ name: 'Verbose Logging', default: false }),
* })
* ```
*/
static of<Spec extends Record<string, Value<any, any>>>(spec: Spec): InputSpec<{ [K in keyof Spec]: Spec[K] extends Value<infer T extends any, any, unknown> ? T : never; }, { [K_1 in keyof Spec]: Spec[K_1] extends Value<any, infer T_1, unknown> ? T_1 : never; }>;
}
export {};
@@ -0,0 +1,319 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputSpec = void 0;
const value_1 = require("./value");
const zod_1 = require("zod");
const zod_deep_partial_1 = require("zod-deep-partial");
const inputSpecTools_1 = require("./inputSpecTools");
/**
* InputSpecs are the specs that are used by the os input specification form for this service.
* Here is an example of a simple input specification
```ts
const smallInputSpec = InputSpec.of({
test: Value.boolean({
name: "Test",
description: "This is the description for the test",
warning: null,
default: false,
}),
});
```
The idea of an inputSpec is that now the form is going to ask for
Test: [ ] and the value is going to be checked as a boolean.
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 inputSpec spec.
```ts
const matchSmallInputSpec = smallInputSpec.validator();
type SmallInputSpec = typeof matchSmallInputSpec._TYPE;
```
Here is an example of a more complex input specification which came from an input specification for a service
that works with bitcoin, like c-lightning.
```ts
export const hostname = Value.string({
name: "Hostname",
default: null,
description: "Domain or IP address of bitcoin peer",
warning: null,
required: true,
masked: false,
placeholder: null,
pattern:
"(^(?:(?: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]$))",
patternDescription:
"Must be either a domain name, or an IPv4 or IPv6 address. Do not include protocol scheme (eg 'http://') or port.",
});
export const port = Value.number({
name: "Port",
default: null,
description: "Port that peer is listening on for inbound p2p connections",
warning: null,
required: false,
range: "[0,65535]",
integral: true,
units: null,
placeholder: null,
});
export const addNodesSpec = InputSpec.of({ hostname: hostname, port: port });
```
*/
class InputSpec {
constructor(spec, validator) {
this.spec = spec;
this.validator = validator;
this._TYPE = null;
this._PARTIAL = null;
this.partialValidator = (0, zod_deep_partial_1.zodDeepPartial)(this.validator);
}
/**
* Builds the runtime form specification and combined Zod validator from this InputSpec's fields.
*
* @returns An object containing the resolved `spec` (field specs keyed by name) and a combined `validator`
*/
async build(options) {
const answer = {};
const validator = {};
for (const k in this.spec) {
const built = await this.spec[k].build(options);
answer[k] = built.spec;
validator[k] = built.validator;
}
return {
spec: answer,
validator: zod_1.z.object(validator),
};
}
/**
* Adds multiple fields to this spec at once, returning a new `InputSpec` with extended types.
*
* @param build - A record of {@link Value} entries, or a function receiving typed tools that returns one
*/
add(build) {
const addedValues = build instanceof Function ? build((0, inputSpecTools_1.createInputSpecTools)()) : build;
const newSpec = { ...this.spec, ...addedValues };
const newValidator = zod_1.z.object(Object.fromEntries(Object.entries(newSpec).map(([k, v]) => [
k,
v.validator,
])));
return new InputSpec(newSpec, newValidator);
}
/**
* Returns a new InputSpec containing only the specified keys.
* Use `true` to keep a field as-is, or a nested object to filter sub-fields of object-typed fields.
*
* @example
* ```ts
* const full = InputSpec.of({
* name: Value.text({ name: 'Name', required: true, default: null }),
* settings: Value.object({ name: 'Settings' }, InputSpec.of({
* debug: Value.toggle({ name: 'Debug', default: false }),
* port: Value.number({ name: 'Port', required: true, default: 8080, integer: true }),
* })),
* })
* const filtered = full.filter({ name: true, settings: { debug: true } })
* ```
*/
filter(keys, keepByDefault) {
const newSpec = {};
for (const k of Object.keys(this.spec)) {
const filterVal = keys[k];
const value = this.spec[k];
if (!value)
continue;
if (filterVal === true) {
newSpec[k] = value;
}
else if (typeof filterVal === 'object' && filterVal !== null) {
const objectMeta = value._objectSpec;
if (objectMeta) {
const filteredInner = objectMeta.inputSpec.filter(filterVal, keepByDefault);
newSpec[k] = value_1.Value.object(objectMeta.params, filteredInner);
}
else {
newSpec[k] = value;
}
}
else if (keepByDefault && filterVal !== false) {
newSpec[k] = value;
}
}
const newValidator = zod_1.z.object(Object.fromEntries(Object.entries(newSpec).map(([k, v]) => [k, v.validator])));
return new InputSpec(newSpec, newValidator);
}
/**
* Returns a new InputSpec with the specified keys disabled.
* Use `true` to disable a field, or a nested object to disable sub-fields of object-typed fields.
* All fields remain in the spec — disabled fields simply cannot be edited by the user.
*
* @param keys - Which fields to disable, using the same shape as {@link FilterKeys}
* @param message - The reason the fields are disabled, displayed to the user
*
* @example
* ```ts
* const spec = InputSpec.of({
* name: Value.text({ name: 'Name', required: true, default: null }),
* settings: Value.object({ name: 'Settings' }, InputSpec.of({
* debug: Value.toggle({ name: 'Debug', default: false }),
* port: Value.number({ name: 'Port', required: true, default: 8080, integer: true }),
* })),
* })
* const disabled = spec.disable({ name: true, settings: { debug: true } }, 'Managed by the system')
* ```
*/
disable(keys, message) {
const newSpec = {};
for (const k in this.spec) {
const filterVal = keys[k];
const value = this.spec[k];
if (!filterVal) {
newSpec[k] = value;
}
else if (filterVal === true) {
newSpec[k] = value.withDisabled(message);
}
else if (typeof filterVal === 'object' && filterVal !== null) {
const objectMeta = value._objectSpec;
if (objectMeta) {
const disabledInner = objectMeta.inputSpec.disable(filterVal, message);
newSpec[k] = value_1.Value.object(objectMeta.params, disabledInner);
}
else {
newSpec[k] = value.withDisabled(message);
}
}
}
const newValidator = zod_1.z.object(Object.fromEntries(Object.entries(newSpec).map(([k, v]) => [k, v.validator])));
return new InputSpec(newSpec, newValidator);
}
/**
* Resolves a key path to its corresponding display name path.
* Each key is mapped to the `name` property of its built {@link ValueSpec}.
* Recurses into `Value.object` sub-specs for nested paths.
*
* @param path - Typed tuple of field keys (e.g. `["settings", "debug"]`)
* @param options - Build options providing effects and prefill data
* @returns Array of display names (e.g. `["Settings", "Debug"]`)
*/
async namePath(path, options) {
if (path.length === 0)
return [];
const [key, ...rest] = path;
const value = this.spec[key];
if (!value)
return [];
const built = await value.build(options);
const name = 'name' in built.spec ? built.spec.name : key;
if (rest.length === 0)
return [name];
const objectMeta = value._objectSpec;
if (objectMeta) {
const innerNames = await objectMeta.inputSpec.namePath(rest, options);
return [name, ...innerNames];
}
return [name];
}
/**
* Resolves a key path to the description of the target field.
* Recurses into `Value.object` sub-specs for nested paths.
*
* @param path - Typed tuple of field keys (e.g. `["settings", "debug"]`)
* @param options - Build options providing effects and prefill data
* @returns The description string, or `null` if the field has no description or was not found
*/
async description(path, options) {
if (path.length === 0)
return null;
const [key, ...rest] = path;
const value = this.spec[key];
if (!value)
return null;
if (rest.length === 0) {
const built = await value.build(options);
return 'description' in built.spec
? built.spec.description
: null;
}
const objectMeta = value._objectSpec;
if (objectMeta) {
return objectMeta.inputSpec.description(rest, options);
}
return null;
}
/**
* Returns a new InputSpec filtered to only include keys present in the given partial object.
* For nested `Value.object` fields, recurses into the partial value to filter sub-fields.
*
* @param partial - A deep-partial object whose defined keys determine which fields to keep
*/
filterFromPartial(partial) {
const newSpec = {};
for (const k of Object.keys(partial)) {
const value = this.spec[k];
if (!value)
continue;
const objectMeta = value._objectSpec;
if (objectMeta) {
const partialVal = partial[k];
if (typeof partialVal === 'object' && partialVal !== null) {
const filteredInner = objectMeta.inputSpec.filterFromPartial(partialVal);
newSpec[k] = value_1.Value.object(objectMeta.params, filteredInner);
continue;
}
}
newSpec[k] = value;
}
const newValidator = zod_1.z.object(Object.fromEntries(Object.entries(newSpec).map(([k, v]) => [k, v.validator])));
return new InputSpec(newSpec, newValidator);
}
/**
* Returns a new InputSpec with fields disabled based on which keys are present in the given partial object.
* For nested `Value.object` fields, recurses into the partial value to disable sub-fields.
* All fields remain in the spec — disabled fields simply cannot be edited by the user.
*
* @param partial - A deep-partial object whose defined keys determine which fields to disable
* @param message - The reason the fields are disabled, displayed to the user
*/
disableFromPartial(partial, message) {
const newSpec = {};
for (const k in this.spec) {
const value = this.spec[k];
if (!(k in partial)) {
newSpec[k] = value;
continue;
}
const objectMeta = value._objectSpec;
if (objectMeta) {
const partialVal = partial[k];
if (typeof partialVal === 'object' && partialVal !== null) {
const disabledInner = objectMeta.inputSpec.disableFromPartial(partialVal, message);
newSpec[k] = value_1.Value.object(objectMeta.params, disabledInner);
continue;
}
}
newSpec[k] = value.withDisabled(message);
}
const newValidator = zod_1.z.object(Object.fromEntries(Object.entries(newSpec).map(([k, v]) => [k, v.validator])));
return new InputSpec(newSpec, newValidator);
}
/**
* Creates an `InputSpec` from a plain record of {@link Value} entries.
*
* @example
* ```ts
* const spec = InputSpec.of({
* username: Value.text({ name: 'Username', required: true, default: null }),
* verbose: Value.toggle({ name: 'Verbose Logging', default: false }),
* })
* ```
*/
static of(spec) {
const validator = zod_1.z.object(Object.fromEntries(Object.entries(spec).map(([k, v]) => [k, v.validator])));
return new InputSpec(spec, validator);
}
}
exports.InputSpec = InputSpec;
//# sourceMappingURL=inputSpec.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,173 @@
import { InputSpec, LazyBuild } from './inputSpec';
import { AsRequired, FileInfo, Value } from './value';
import { List } from './list';
import { UnionRes, UnionResStaticValidatedAs, Variants } from './variants';
import { Pattern, RandomString, ValueSpecDatetime, ValueSpecText } from '../inputSpecTypes';
import { DefaultString } from '../inputSpecTypes';
import { z } from 'zod';
import { ListValueSpecText } from '../inputSpecTypes';
export interface InputSpecTools<OuterType> {
Value: BoundValue<OuterType>;
Variants: typeof Variants;
InputSpec: typeof InputSpec;
List: BoundList<OuterType>;
}
export interface BoundValue<OuterType> {
toggle: typeof Value.toggle;
text: typeof Value.text;
textarea: typeof Value.textarea;
number: typeof Value.number;
color: typeof Value.color;
datetime: typeof Value.datetime;
select: typeof Value.select;
multiselect: typeof Value.multiselect;
object: typeof Value.object;
file: typeof Value.file;
list: typeof Value.list;
hidden: typeof Value.hidden;
union: typeof Value.union;
dynamicToggle(a: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: boolean;
disabled?: false | string;
}, OuterType>): Value<boolean, boolean, OuterType>;
dynamicText<Required extends boolean>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: DefaultString | null;
required: Required;
masked?: boolean;
placeholder?: string | null;
minLength?: number | null;
maxLength?: number | null;
patterns?: Pattern[];
inputmode?: ValueSpecText['inputmode'];
disabled?: string | false;
generate?: null | RandomString;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
dynamicTextarea<Required extends boolean>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string | null;
required: Required;
minLength?: number | null;
maxLength?: number | null;
patterns?: Pattern[];
minRows?: number;
maxRows?: number;
placeholder?: string | null;
disabled?: false | string;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
dynamicNumber<Required extends boolean>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: number | null;
required: Required;
min?: number | null;
max?: number | null;
step?: number | null;
integer: boolean;
units?: string | null;
placeholder?: string | null;
disabled?: false | string;
}, OuterType>): Value<AsRequired<number, Required>, number | null, OuterType>;
dynamicColor<Required extends boolean>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string | null;
required: Required;
disabled?: false | string;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
dynamicDatetime<Required extends boolean>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string | null;
required: Required;
inputmode?: ValueSpecDatetime['inputmode'];
min?: string | null;
max?: string | null;
disabled?: false | string;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
dynamicSelect<Values extends Record<string, string>>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string;
values: Values;
disabled?: false | string | string[];
}, OuterType>): Value<keyof Values & string, keyof Values & string, OuterType>;
dynamicMultiselect<Values extends Record<string, string>>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string[];
values: Values;
minLength?: number | null;
maxLength?: number | null;
disabled?: false | string | string[];
}, OuterType>): Value<(keyof Values & string)[], (keyof Values & string)[], OuterType>;
dynamicFile<Required extends boolean>(a: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
extensions: string[];
required: Required;
}, OuterType>): Value<AsRequired<FileInfo, Required>, FileInfo | null, OuterType>;
dynamicUnion<VariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any>;
};
}>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
variants: Variants<VariantValues>;
default: keyof VariantValues & string;
disabled: string[] | false | string;
}, OuterType>): Value<UnionRes<VariantValues>, UnionRes<VariantValues>, OuterType>;
dynamicUnion<StaticVariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any, any>;
};
}, VariantValues extends StaticVariantValues>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
variants: Variants<VariantValues>;
default: keyof VariantValues & string;
disabled: string[] | false | string;
}, OuterType>, validator: z.ZodType<UnionResStaticValidatedAs<StaticVariantValues>>): Value<UnionRes<VariantValues>, UnionResStaticValidatedAs<StaticVariantValues>, OuterType>;
dynamicHidden<T>(getParser: LazyBuild<z.ZodType<T>, OuterType>): Value<T, T, OuterType>;
}
export interface BoundList<OuterType> {
text: typeof List.text;
obj: typeof List.obj;
dynamicText(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default?: string[];
minLength?: number | null;
maxLength?: number | null;
disabled?: false | string;
generate?: null | RandomString;
spec: {
masked?: boolean;
placeholder?: string | null;
minLength?: number | null;
maxLength?: number | null;
patterns?: Pattern[];
inputmode?: ListValueSpecText['inputmode'];
};
}, OuterType>): List<string[], string[], OuterType>;
}
export declare function createInputSpecTools<OuterType>(): InputSpecTools<OuterType>;
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createInputSpecTools = createInputSpecTools;
const inputSpec_1 = require("./inputSpec");
const value_1 = require("./value");
const list_1 = require("./list");
const variants_1 = require("./variants");
function createInputSpecTools() {
return {
Value: value_1.Value,
Variants: variants_1.Variants,
InputSpec: inputSpec_1.InputSpec,
List: list_1.List,
};
}
//# sourceMappingURL=inputSpecTools.js.map
@@ -0,0 +1 @@
{"version":3,"file":"inputSpecTools.js","sourceRoot":"","sources":["../../../../../../base/lib/actions/input/builder/inputSpecTools.ts"],"names":[],"mappings":";;AA0QA,oDAOC;AAjRD,2CAAkD;AAClD,mCAAqD;AACrD,iCAA6B;AAC7B,yCAA0E;AAuQ1E,SAAgB,oBAAoB;IAClC,OAAO;QACL,KAAK,EAAE,aAAqC;QAC5C,QAAQ,EAAR,mBAAQ;QACR,SAAS,EAAT,qBAAS;QACT,IAAI,EAAE,WAAmC;KAC1C,CAAA;AACH,CAAC"}
@@ -0,0 +1,103 @@
import { InputSpec, LazyBuild } from './inputSpec';
import { ListValueSpecText, Pattern, RandomString, UniqueBy, ValueSpecList } from '../inputSpecTypes';
import { z } from 'zod';
/**
* Builder class for defining list-type form fields.
*
* A list presents an interface to add, remove, and reorder items. Items can be
* either text strings ({@link List.text}) or structured objects ({@link List.obj}).
*
* Used with {@link Value.list} to include a list field in an {@link InputSpec}.
*/
export declare class List<Type extends StaticValidatedAs, StaticValidatedAs = Type, OuterType = unknown> {
build: LazyBuild<{
spec: ValueSpecList;
validator: z.ZodType<Type>;
}, OuterType>;
readonly validator: z.ZodType<StaticValidatedAs>;
private constructor();
readonly _TYPE: Type;
/**
* Creates a list of text input items.
*
* @param a - List-level options (name, description, min/max length, defaults)
* @param aSpec - Item-level options (patterns, input mode, masking, generation)
*/
static text(a: {
name: string;
description?: string | null;
warning?: string | null;
default?: string[];
minLength?: number | null;
maxLength?: number | null;
}, aSpec: {
/**
* @description Mask (aka camouflage) text input with dots: ● ● ●
* @default false
*/
masked?: boolean;
placeholder?: string | null;
minLength?: number | null;
maxLength?: number | null;
/**
* @description A list of regular expressions to which the text must conform to pass validation. A human readable description is provided in case the validation fails.
* @default []
* @example
* ```
[
{
regex: "[a-z]",
description: "May only contain lower case letters from the English alphabet."
}
]
* ```
*/
patterns?: Pattern[];
/**
* @description Informs the browser how to behave and which keyboard to display on mobile
* @default "text"
*/
inputmode?: ListValueSpecText['inputmode'];
/**
* @description Displays a button that will generate a random string according to the provided charset and len attributes.
*/
generate?: null | RandomString;
}): List<string[], string[], unknown>;
/** Like {@link List.text} but options are resolved lazily at runtime via a builder function. */
static dynamicText<OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default?: string[];
minLength?: number | null;
maxLength?: number | null;
disabled?: false | string;
generate?: null | RandomString;
spec: {
masked?: boolean;
placeholder?: string | null;
minLength?: number | null;
maxLength?: number | null;
patterns?: Pattern[];
inputmode?: ListValueSpecText['inputmode'];
};
}, OuterType>): List<string[], string[], OuterType>;
/**
* Creates a list of structured object items, each defined by a nested {@link InputSpec}.
*
* @param a - List-level options (name, description, min/max length)
* @param aSpec - Item-level options (the nested spec, display expression, uniqueness constraint)
*/
static obj<Type extends StaticValidatedAs, StaticValidatedAs extends Record<string, any>>(a: {
name: string;
description?: string | null;
warning?: string | null;
default?: [];
minLength?: number | null;
maxLength?: number | null;
}, aSpec: {
spec: InputSpec<Type, StaticValidatedAs>;
displayAs?: null | string;
uniqueBy?: null | UniqueBy;
}): List<Type[], StaticValidatedAs[], unknown>;
}
@@ -0,0 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.List = void 0;
const zod_1 = require("zod");
/**
* Builder class for defining list-type form fields.
*
* A list presents an interface to add, remove, and reorder items. Items can be
* either text strings ({@link List.text}) or structured objects ({@link List.obj}).
*
* Used with {@link Value.list} to include a list field in an {@link InputSpec}.
*/
class List {
constructor(build, validator) {
this.build = build;
this.validator = validator;
this._TYPE = null;
}
/**
* Creates a list of text input items.
*
* @param a - List-level options (name, description, min/max length, defaults)
* @param aSpec - Item-level options (patterns, input mode, masking, generation)
*/
static text(a, aSpec) {
const validator = zod_1.z.array(zod_1.z.string());
return new List(() => {
const spec = {
type: 'text',
placeholder: null,
minLength: null,
maxLength: null,
masked: false,
inputmode: 'text',
generate: null,
patterns: aSpec.patterns || [],
...aSpec,
};
const built = {
description: null,
warning: null,
default: [],
type: 'list',
minLength: null,
maxLength: null,
disabled: false,
...a,
spec,
};
return { spec: built, validator };
}, validator);
}
/** Like {@link List.text} but options are resolved lazily at runtime via a builder function. */
static dynamicText(getA) {
const validator = zod_1.z.array(zod_1.z.string());
return new List(async (options) => {
const { spec: aSpec, ...a } = await getA(options);
const spec = {
type: 'text',
placeholder: null,
minLength: null,
maxLength: null,
masked: false,
inputmode: 'text',
generate: null,
patterns: aSpec.patterns || [],
...aSpec,
};
const built = {
description: null,
warning: null,
default: [],
type: 'list',
minLength: null,
maxLength: null,
disabled: false,
...a,
spec,
};
return { spec: built, validator };
}, validator);
}
/**
* Creates a list of structured object items, each defined by a nested {@link InputSpec}.
*
* @param a - List-level options (name, description, min/max length)
* @param aSpec - Item-level options (the nested spec, display expression, uniqueness constraint)
*/
static obj(a, aSpec) {
return new List(async (options) => {
const { spec: previousSpecSpec, ...restSpec } = aSpec;
const built = await previousSpecSpec.build(options);
const spec = {
type: 'object',
displayAs: null,
uniqueBy: null,
...restSpec,
spec: built.spec,
};
const value = {
spec,
default: [],
...a,
};
return {
spec: {
description: null,
warning: null,
minLength: null,
maxLength: null,
type: 'list',
disabled: false,
...value,
},
validator: zod_1.z.array(built.validator),
};
}, zod_1.z.array(aSpec.spec.validator));
}
}
exports.List = List;
//# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../../../base/lib/actions/input/builder/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,791 @@
import { InputSpec, LazyBuild } from './inputSpec';
import { List } from './list';
import { UnionRes, UnionResStaticValidatedAs, Variants } from './variants';
import { Pattern, RandomString, ValueSpec, ValueSpecDatetime, ValueSpecText } from '../inputSpecTypes';
import { DefaultString } from '../inputSpecTypes';
import { z } from 'zod';
import { DeepPartial } from '../../../types';
/** Zod schema for a file upload result — validates `{ path, commitment: { hash, size } }`. */
export declare const fileInfoParser: z.ZodObject<{
path: z.ZodString;
commitment: z.ZodObject<{
hash: z.ZodString;
size: z.ZodNumber;
}, z.core.$strip>;
}, z.core.$strip>;
/** The parsed result of a file upload, containing the file path and its content commitment (hash + size). */
export type FileInfo = z.infer<typeof fileInfoParser>;
/** Conditional type: returns `T` if `Required` is `true`, otherwise `T | null`. */
export type AsRequired<T, Required extends boolean> = Required extends true ? T : T | null;
/**
* Core builder class for defining a single form field in a service configuration spec.
*
* Each static factory method (e.g. `Value.text()`, `Value.toggle()`, `Value.select()`) creates
* a typed `Value` instance representing a specific field type. Dynamic variants (e.g. `Value.dynamicText()`)
* allow the field options to be computed lazily at runtime.
*
* Use with {@link InputSpec} to compose complete form specifications.
*
* @typeParam Type - The runtime type this field produces when filled in
* @typeParam StaticValidatedAs - The compile-time validated type (usually same as Type)
* @typeParam OuterType - The parent form's type context (used by dynamic variants)
*/
export declare class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type, OuterType = unknown> {
build: LazyBuild<{
spec: ValueSpec;
validator: z.ZodType<Type>;
}, OuterType>;
readonly validator: z.ZodType<StaticValidatedAs>;
protected constructor(build: LazyBuild<{
spec: ValueSpec;
validator: z.ZodType<Type>;
}, OuterType>, validator: z.ZodType<StaticValidatedAs>);
_TYPE: Type;
_PARTIAL: DeepPartial<Type>;
/** @internal Used by {@link InputSpec.filter} to support nested filtering of object-typed fields. */
_objectSpec?: {
inputSpec: InputSpec<any, any>;
params: {
name: string;
description?: string | null;
};
};
/**
* @description Displays a boolean toggle to enable/disable
* @example
* ```
toggleExample: Value.toggle({
// required
name: 'Toggle Example',
default: true,
// optional
description: null,
warning: null,
immutable: false,
}),
* ```
*/
static toggle(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
default: boolean;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<boolean, boolean, unknown>;
/** Like {@link Value.toggle} but options are resolved lazily at runtime via a builder function. */
static dynamicToggle<OuterType = unknown>(a: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: boolean;
disabled?: false | string;
}, OuterType>): Value<boolean, boolean, OuterType>;
/**
* @description Displays a text input field
* @example
* ```
textExample: Value.text({
// required
name: 'Text Example',
required: false,
default: null,
// optional
description: null,
placeholder: null,
warning: null,
generate: null,
inputmode: 'text',
masked: false,
minLength: null,
maxLength: null,
patterns: [],
immutable: false,
}),
* ```
*/
static text<Required extends boolean>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
/**
* provide a default value.
* @type { string | RandomString | null }
* @example default: null
* @example default: 'World'
* @example default: { charset: 'abcdefg', len: 16 }
*/
default: string | RandomString | null;
required: Required;
/**
* @description Mask (aka camouflage) text input with dots: ● ● ●
* @default false
*/
masked?: boolean;
placeholder?: string | null;
minLength?: number | null;
maxLength?: number | null;
/**
* @description A list of regular expressions to which the text must conform to pass validation. A human readable description is provided in case the validation fails.
* @default []
* @example
* ```
[
{
regex: "[a-z]",
description: "May only contain lower case letters from the English alphabet."
}
]
* ```
*/
patterns?: Pattern[];
/**
* @description Informs the browser how to behave and which keyboard to display on mobile
* @default "text"
*/
inputmode?: ValueSpecText['inputmode'];
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
/**
* @description Displays a button that will generate a random string according to the provided charset and len attributes.
*/
generate?: RandomString | null;
}): Value<AsRequired<string, Required>, AsRequired<string, Required>, unknown>;
/** Like {@link Value.text} but options are resolved lazily at runtime via a builder function. */
static dynamicText<Required extends boolean, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: DefaultString | null;
required: Required;
masked?: boolean;
placeholder?: string | null;
minLength?: number | null;
maxLength?: number | null;
patterns?: Pattern[];
inputmode?: ValueSpecText['inputmode'];
disabled?: string | false;
generate?: null | RandomString;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
/**
* @description Displays a large textarea field for long form entry.
* @example
* ```
textareaExample: Value.textarea({
// required
name: 'Textarea Example',
required: false,
default: null,
// optional
description: null,
placeholder: null,
warning: null,
minLength: null,
maxLength: null,
minRows: 3
maxRows: 6
immutable: false,
}),
* ```
*/
static textarea<Required extends boolean>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
default: string | null;
required: Required;
minLength?: number | null;
maxLength?: number | null;
/**
* @description A list of regular expressions to which the text must conform to pass validation. A human readable description is provided in case the validation fails.
* @default []
* @example
* ```
[
{
regex: "[a-z]",
description: "May only contain lower case letters from the English alphabet."
}
]
* ```
*/
patterns?: Pattern[];
/** Defaults to 3 */
minRows?: number;
/** Maximum number of rows before scroll appears. Defaults to 6 */
maxRows?: number;
placeholder?: string | null;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<AsRequired<string, Required>, AsRequired<string, Required>, unknown>;
/** Like {@link Value.textarea} but options are resolved lazily at runtime via a builder function. */
static dynamicTextarea<Required extends boolean, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string | null;
required: Required;
minLength?: number | null;
maxLength?: number | null;
patterns?: Pattern[];
minRows?: number;
maxRows?: number;
placeholder?: string | null;
disabled?: false | string;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
/**
* @description Displays a number input field
* @example
* ```
numberExample: Value.number({
// required
name: 'Number Example',
required: false,
default: null,
integer: true,
// optional
description: null,
placeholder: null,
warning: null,
min: null,
max: null,
immutable: false,
step: null,
units: null,
}),
* ```
*/
static number<Required extends boolean>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
/**
* @description optionally provide a default value.
* @type { default: number | null }
* @example default: null
* @example default: 7
*/
default: number | null;
required: Required;
min?: number | null;
max?: number | null;
/**
* @description How much does the number increase/decrease when using the arrows provided by the browser.
* @default 1
*/
step?: number | null;
/**
* @description Requires the number to be an integer.
*/
integer: boolean;
/**
* @description Optionally display units to the right of the input box.
*/
units?: string | null;
placeholder?: string | null;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<AsRequired<number, Required>, AsRequired<number, Required>, unknown>;
/** Like {@link Value.number} but options are resolved lazily at runtime via a builder function. */
static dynamicNumber<Required extends boolean, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: number | null;
required: Required;
min?: number | null;
max?: number | null;
step?: number | null;
integer: boolean;
units?: string | null;
placeholder?: string | null;
disabled?: false | string;
}, OuterType>): Value<AsRequired<number, Required>, number | null, OuterType>;
/**
* @description Displays a browser-native color selector.
* @example
* ```
colorExample: Value.color({
// required
name: 'Color Example',
required: false,
default: null,
// optional
description: null,
warning: null,
immutable: false,
}),
* ```
*/
static color<Required extends boolean>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
/**
* @description optionally provide a default value.
* @type { default: string | null }
* @example default: null
* @example default: 'ffffff'
*/
default: string | null;
required: Required;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<AsRequired<string, Required>, AsRequired<string, Required>, unknown>;
/** Like {@link Value.color} but options are resolved lazily at runtime via a builder function. */
static dynamicColor<Required extends boolean, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string | null;
required: Required;
disabled?: false | string;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
/**
* @description Displays a browser-native date/time selector.
* @example
* ```
datetimeExample: Value.datetime({
// required
name: 'Datetime Example',
required: false,
default: null,
// optional
description: null,
warning: null,
immutable: false,
inputmode: 'datetime-local',
min: null,
max: null,
}),
* ```
*/
static datetime<Required extends boolean>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
/**
* @description optionally provide a default value.
* @type { default: string | null }
* @example default: null
* @example default: '1985-12-16 18:00:00.000'
*/
default: string | null;
required: Required;
/**
* @description Informs the browser how to behave and which date/time component to display.
* @default "datetime-local"
*/
inputmode?: ValueSpecDatetime['inputmode'];
min?: string | null;
max?: string | null;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<AsRequired<string, Required>, AsRequired<string, Required>, unknown>;
/** Like {@link Value.datetime} but options are resolved lazily at runtime via a builder function. */
static dynamicDatetime<Required extends boolean, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string | null;
required: Required;
inputmode?: ValueSpecDatetime['inputmode'];
min?: string | null;
max?: string | null;
disabled?: false | string;
}, OuterType>): Value<AsRequired<string, Required>, string | null, OuterType>;
/**
* @description Displays a select modal with radio buttons, allowing for a single selection.
* @example
* ```
selectExample: Value.select({
// required
name: 'Select Example',
default: 'radio1',
values: {
radio1: 'Radio 1',
radio2: 'Radio 2',
},
// optional
description: null,
warning: null,
immutable: false,
disabled: false,
}),
* ```
*/
static select<Values extends Record<string, string>>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
/**
* @description Determines if the field is required. If so, optionally provide a default value from the list of values.
* @type { (keyof Values & string) | null }
* @example default: null
* @example default: 'radio1'
*/
default: keyof Values & string;
/**
* @description A mapping of unique radio options to their human readable display format.
* @example
* ```
{
radio1: "Radio 1"
radio2: "Radio 2"
radio3: "Radio 3"
}
* ```
*/
values: Values;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<keyof Values & string, keyof Values & string, unknown>;
/** Like {@link Value.select} but options are resolved lazily at runtime via a builder function. */
static dynamicSelect<Values extends Record<string, string>, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string;
values: Values;
disabled?: false | string | string[];
}, OuterType>): Value<keyof Values & string, keyof Values & string, OuterType>;
/**
* @description Displays a select modal with checkboxes, allowing for multiple selections.
* @example
* ```
multiselectExample: Value.multiselect({
// required
name: 'Multiselect Example',
values: {
option1: 'Option 1',
option2: 'Option 2',
},
default: [],
// optional
description: null,
warning: null,
immutable: false,
disabled: false,
minlength: null,
maxLength: null,
}),
* ```
*/
static multiselect<Values extends Record<string, string>>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
/**
* @description A simple list of which options should be checked by default.
*/
default: (keyof Values & string)[];
/**
* @description A mapping of checkbox options to their human readable display format.
* @example
* ```
{
option1: "Option 1"
option2: "Option 2"
option3: "Option 3"
}
* ```
*/
values: Values;
minLength?: number | null;
maxLength?: number | null;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<(keyof Values & string)[], (keyof Values & string)[], unknown>;
/** Like {@link Value.multiselect} but options are resolved lazily at runtime via a builder function. */
static dynamicMultiselect<Values extends Record<string, string>, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
default: string[];
values: Values;
minLength?: number | null;
maxLength?: number | null;
disabled?: false | string | string[];
}, OuterType>): Value<(keyof Values & string)[], (keyof Values & string)[], OuterType>;
/**
* @description Display a collapsable grouping of additional fields, a "sub form". The second value is the inputSpec spec for the sub form.
* @example
* ```
objectExample: Value.object(
{
// required
name: 'Object Example',
// optional
description: null,
warning: null,
},
InputSpec.of({}),
),
* ```
*/
static object<Type extends StaticValidatedAs, StaticValidatedAs extends Record<string, any>>(a: {
name: string;
description?: string | null;
}, spec: InputSpec<Type, StaticValidatedAs>): Value<Type, StaticValidatedAs, unknown>;
/**
* Displays a file upload input field.
*
* @param a.extensions - Allowed file extensions (e.g. `[".pem", ".crt"]`)
* @param a.required - Whether a file must be selected
*/
static file<Required extends boolean>(a: {
name: string;
description?: string | null;
warning?: string | null;
extensions: string[];
required: Required;
}): Value<AsRequired<{
path: string;
commitment: {
hash: string;
size: number;
};
}, Required>, AsRequired<{
path: string;
commitment: {
hash: string;
size: number;
};
}, Required>, unknown>;
/** Like {@link Value.file} but options are resolved lazily at runtime via a builder function. */
static dynamicFile<Required extends boolean, OuterType = unknown>(a: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
extensions: string[];
required: Required;
}, OuterType>): Value<AsRequired<{
path: string;
commitment: {
hash: string;
size: number;
};
}, Required>, {
path: string;
commitment: {
hash: string;
size: number;
};
} | null, OuterType>;
/**
* @description Displays a dropdown, allowing for a single selection. Depending on the selection, a different object ("sub form") is presented.
* @example
* ```
unionExample: Value.union(
{
// required
name: 'Union Example',
default: 'option1',
// optional
description: null,
warning: null,
disabled: false,
immutable: false,
},
Variants.of({
option1: {
name: 'Option 1',
spec: InputSpec.of({}),
},
option2: {
name: 'Option 2',
spec: InputSpec.of({}),
},
}),
),
* ```
*/
static union<VariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any>;
};
}>(a: {
name: string;
description?: string | null;
/** Presents a warning prompt before permitting the value to change. */
warning?: string | null;
variants: Variants<VariantValues>;
/**
* @description Provide a default value from the list of variants.
* @type { string }
* @example default: 'variant1'
*/
default: keyof VariantValues & string;
/**
* @description Once set, the value can never be changed.
* @default false
*/
immutable?: boolean;
}): Value<UnionRes<VariantValues, keyof VariantValues & string>, UnionResStaticValidatedAs<VariantValues, keyof VariantValues & string>, unknown>;
/** Like {@link Value.union} but options (including which variants are available) are resolved lazily at runtime. */
static dynamicUnion<VariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any>;
};
}, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
variants: Variants<VariantValues>;
default: keyof VariantValues & string;
disabled: string[] | false | string;
}, OuterType>): Value<UnionRes<VariantValues>, UnionRes<VariantValues>, OuterType>;
/** Like {@link Value.union} but options are resolved lazily, with an explicit static validator type. */
static dynamicUnion<StaticVariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any, any>;
};
}, VariantValues extends StaticVariantValues, OuterType = unknown>(getA: LazyBuild<{
name: string;
description?: string | null;
warning?: string | null;
variants: Variants<VariantValues>;
default: keyof VariantValues & string;
disabled: string[] | false | string;
}, OuterType>, validator: z.ZodType<UnionResStaticValidatedAs<StaticVariantValues>>): Value<UnionRes<VariantValues>, UnionResStaticValidatedAs<StaticVariantValues>, OuterType>;
/**
* @description Presents an interface to add/remove/edit items in a list.
* @example
* In this example, we create a list of text inputs.
*
* ```
listExampleText: Value.list(
List.text(
{
// required
name: 'Text List',
// optional
description: null,
warning: null,
default: [],
minLength: null,
maxLength: null,
},
{
// required
patterns: [],
// optional
placeholder: null,
generate: null,
inputmode: 'url',
masked: false,
minLength: null,
maxLength: null,
},
),
),
* ```
* @example
* In this example, we create a list of objects.
*
* ```
listExampleObject: Value.list(
List.obj(
{
// required
name: 'Object List',
// optional
description: null,
warning: null,
default: [],
minLength: null,
maxLength: null,
},
{
// required
spec: InputSpec.of({}),
// optional
displayAs: null,
uniqueBy: null,
},
),
),
* ```
*/
static list<Type>(a: List<Type>): Value<Type, Type, unknown>;
/**
* @description Provides a way to define a hidden field with a static value. Useful for tracking
* @example
* ```
hiddenExample: Value.hidden(),
* ```
*/
static hidden<T>(): Value<T>;
static hidden<T>(parser: z.ZodType<T>): Value<T>;
/**
* @description Provides a way to define a hidden field with a static value. Useful for tracking
* @example
* ```
hiddenExample: Value.hidden(),
* ```
*/
static dynamicHidden<T, OuterType = unknown>(getParser: LazyBuild<z.ZodType<T>, OuterType>): Value<T, T, OuterType>;
/**
* Returns a new Value that produces the same field spec but with `disabled` set to the given message.
* The field remains in the form but cannot be edited by the user.
*
* @param message - The reason the field is disabled, displayed to the user
*/
withDisabled(message: string): Value<Type, StaticValidatedAs, OuterType>;
/**
* Transforms the validated output value using a mapping function.
* The form field itself remains unchanged, but the value is transformed after validation.
*
* @param fn - A function to transform the validated value
*/
map<U>(fn: (value: StaticValidatedAs) => U): Value<U, U, OuterType>;
}
@@ -0,0 +1,764 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Value = exports.fileInfoParser = void 0;
const util_1 = require("../../../util");
const zod_1 = require("zod");
/** Build a union-of-literals validator from object keys, falling back to z.string() when empty */
function literalKeysValidator(values) {
const keys = Object.keys(values);
if (keys.length === 0)
return zod_1.z.string();
return zod_1.z.union(keys.map((x) => zod_1.z.literal(x)));
}
/** Zod schema for a file upload result — validates `{ path, commitment: { hash, size } }`. */
exports.fileInfoParser = zod_1.z.object({
path: zod_1.z.string(),
commitment: zod_1.z.object({ hash: zod_1.z.string(), size: zod_1.z.number() }),
});
const testForAsRequiredParser = (0, util_1.once)(() => (v) => zod_1.z.object({ required: zod_1.z.literal(true) }).safeParse(v).success);
function asRequiredParser(parser, input) {
if (testForAsRequiredParser()(input))
return parser;
return parser.nullable();
}
/**
* Core builder class for defining a single form field in a service configuration spec.
*
* Each static factory method (e.g. `Value.text()`, `Value.toggle()`, `Value.select()`) creates
* a typed `Value` instance representing a specific field type. Dynamic variants (e.g. `Value.dynamicText()`)
* allow the field options to be computed lazily at runtime.
*
* Use with {@link InputSpec} to compose complete form specifications.
*
* @typeParam Type - The runtime type this field produces when filled in
* @typeParam StaticValidatedAs - The compile-time validated type (usually same as Type)
* @typeParam OuterType - The parent form's type context (used by dynamic variants)
*/
class Value {
constructor(build, validator) {
this.build = build;
this.validator = validator;
this._TYPE = null;
this._PARTIAL = null;
}
/**
* @description Displays a boolean toggle to enable/disable
* @example
* ```
toggleExample: Value.toggle({
// required
name: 'Toggle Example',
default: true,
// optional
description: null,
warning: null,
immutable: false,
}),
* ```
*/
static toggle(a) {
const validator = zod_1.z.boolean();
return new Value(async () => ({
spec: {
description: null,
warning: null,
type: 'toggle',
disabled: false,
immutable: a.immutable ?? false,
...a,
},
validator,
}), validator);
}
/** Like {@link Value.toggle} but options are resolved lazily at runtime via a builder function. */
static dynamicToggle(a) {
const validator = zod_1.z.boolean();
return new Value(async (options) => ({
spec: {
description: null,
warning: null,
type: 'toggle',
disabled: false,
immutable: false,
...(await a(options)),
},
validator,
}), validator);
}
/**
* @description Displays a text input field
* @example
* ```
textExample: Value.text({
// required
name: 'Text Example',
required: false,
default: null,
// optional
description: null,
placeholder: null,
warning: null,
generate: null,
inputmode: 'text',
masked: false,
minLength: null,
maxLength: null,
patterns: [],
immutable: false,
}),
* ```
*/
static text(a) {
const validator = asRequiredParser(zod_1.z.string(), a);
return new Value(async () => ({
spec: {
type: 'text',
description: null,
warning: null,
masked: false,
placeholder: null,
minLength: null,
maxLength: null,
patterns: [],
inputmode: 'text',
disabled: false,
immutable: a.immutable ?? false,
generate: a.generate ?? null,
...a,
},
validator,
}), validator);
}
/** Like {@link Value.text} but options are resolved lazily at runtime via a builder function. */
static dynamicText(getA) {
return new Value(async (options) => {
const a = await getA(options);
return {
spec: {
type: 'text',
description: null,
warning: null,
masked: false,
placeholder: null,
minLength: null,
maxLength: null,
patterns: [],
inputmode: 'text',
disabled: false,
immutable: false,
generate: a.generate ?? null,
...a,
},
validator: asRequiredParser(zod_1.z.string(), a),
};
}, zod_1.z.string().nullable());
}
/**
* @description Displays a large textarea field for long form entry.
* @example
* ```
textareaExample: Value.textarea({
// required
name: 'Textarea Example',
required: false,
default: null,
// optional
description: null,
placeholder: null,
warning: null,
minLength: null,
maxLength: null,
minRows: 3
maxRows: 6
immutable: false,
}),
* ```
*/
static textarea(a) {
const validator = asRequiredParser(zod_1.z.string(), a);
return new Value(async () => {
const built = {
description: null,
warning: null,
minLength: null,
maxLength: null,
patterns: [],
minRows: 3,
maxRows: 6,
placeholder: null,
type: 'textarea',
disabled: false,
immutable: a.immutable ?? false,
...a,
};
return { spec: built, validator };
}, validator);
}
/** Like {@link Value.textarea} but options are resolved lazily at runtime via a builder function. */
static dynamicTextarea(getA) {
return new Value(async (options) => {
const a = await getA(options);
return {
spec: {
description: null,
warning: null,
minLength: null,
maxLength: null,
patterns: [],
minRows: 3,
maxRows: 6,
placeholder: null,
type: 'textarea',
disabled: false,
immutable: false,
...a,
},
validator: asRequiredParser(zod_1.z.string(), a),
};
}, zod_1.z.string().nullable());
}
/**
* @description Displays a number input field
* @example
* ```
numberExample: Value.number({
// required
name: 'Number Example',
required: false,
default: null,
integer: true,
// optional
description: null,
placeholder: null,
warning: null,
min: null,
max: null,
immutable: false,
step: null,
units: null,
}),
* ```
*/
static number(a) {
const validator = asRequiredParser(zod_1.z.number(), a);
return new Value(() => ({
spec: {
type: 'number',
description: null,
warning: null,
min: null,
max: null,
step: null,
units: null,
placeholder: null,
disabled: false,
immutable: a.immutable ?? false,
...a,
},
validator,
}), validator);
}
/** Like {@link Value.number} but options are resolved lazily at runtime via a builder function. */
static dynamicNumber(getA) {
return new Value(async (options) => {
const a = await getA(options);
return {
spec: {
type: 'number',
description: null,
warning: null,
min: null,
max: null,
step: null,
units: null,
placeholder: null,
disabled: false,
immutable: false,
...a,
},
validator: asRequiredParser(zod_1.z.number(), a),
};
}, zod_1.z.number().nullable());
}
/**
* @description Displays a browser-native color selector.
* @example
* ```
colorExample: Value.color({
// required
name: 'Color Example',
required: false,
default: null,
// optional
description: null,
warning: null,
immutable: false,
}),
* ```
*/
static color(a) {
const validator = asRequiredParser(zod_1.z.string(), a);
return new Value(() => ({
spec: {
type: 'color',
description: null,
warning: null,
disabled: false,
immutable: a.immutable ?? false,
...a,
},
validator,
}), validator);
}
/** Like {@link Value.color} but options are resolved lazily at runtime via a builder function. */
static dynamicColor(getA) {
return new Value(async (options) => {
const a = await getA(options);
return {
spec: {
type: 'color',
description: null,
warning: null,
disabled: false,
immutable: false,
...a,
},
validator: asRequiredParser(zod_1.z.string(), a),
};
}, zod_1.z.string().nullable());
}
/**
* @description Displays a browser-native date/time selector.
* @example
* ```
datetimeExample: Value.datetime({
// required
name: 'Datetime Example',
required: false,
default: null,
// optional
description: null,
warning: null,
immutable: false,
inputmode: 'datetime-local',
min: null,
max: null,
}),
* ```
*/
static datetime(a) {
const validator = asRequiredParser(zod_1.z.string(), a);
return new Value(() => ({
spec: {
type: 'datetime',
description: null,
warning: null,
inputmode: 'datetime-local',
min: null,
max: null,
step: null,
disabled: false,
immutable: a.immutable ?? false,
...a,
},
validator,
}), validator);
}
/** Like {@link Value.datetime} but options are resolved lazily at runtime via a builder function. */
static dynamicDatetime(getA) {
return new Value(async (options) => {
const a = await getA(options);
return {
spec: {
type: 'datetime',
description: null,
warning: null,
inputmode: 'datetime-local',
min: null,
max: null,
disabled: false,
immutable: false,
...a,
},
validator: asRequiredParser(zod_1.z.string(), a),
};
}, zod_1.z.string().nullable());
}
/**
* @description Displays a select modal with radio buttons, allowing for a single selection.
* @example
* ```
selectExample: Value.select({
// required
name: 'Select Example',
default: 'radio1',
values: {
radio1: 'Radio 1',
radio2: 'Radio 2',
},
// optional
description: null,
warning: null,
immutable: false,
disabled: false,
}),
* ```
*/
static select(a) {
const validator = literalKeysValidator(a.values);
return new Value(() => ({
spec: {
description: null,
warning: null,
type: 'select',
disabled: false,
immutable: a.immutable ?? false,
...a,
},
validator,
}), validator);
}
/** Like {@link Value.select} but options are resolved lazily at runtime via a builder function. */
static dynamicSelect(getA) {
return new Value(async (options) => {
const a = await getA(options);
return {
spec: {
description: null,
warning: null,
type: 'select',
disabled: false,
immutable: false,
...a,
},
validator: literalKeysValidator(a.values),
};
}, zod_1.z.string());
}
/**
* @description Displays a select modal with checkboxes, allowing for multiple selections.
* @example
* ```
multiselectExample: Value.multiselect({
// required
name: 'Multiselect Example',
values: {
option1: 'Option 1',
option2: 'Option 2',
},
default: [],
// optional
description: null,
warning: null,
immutable: false,
disabled: false,
minlength: null,
maxLength: null,
}),
* ```
*/
static multiselect(a) {
const validator = zod_1.z.array(literalKeysValidator(a.values));
return new Value(() => ({
spec: {
type: 'multiselect',
minLength: null,
maxLength: null,
warning: null,
description: null,
disabled: false,
immutable: a.immutable ?? false,
...a,
},
validator,
}), validator);
}
/** Like {@link Value.multiselect} but options are resolved lazily at runtime via a builder function. */
static dynamicMultiselect(getA) {
return new Value(async (options) => {
const a = await getA(options);
return {
spec: {
type: 'multiselect',
minLength: null,
maxLength: null,
warning: null,
description: null,
disabled: false,
immutable: false,
...a,
},
validator: zod_1.z.array(literalKeysValidator(a.values)),
};
}, zod_1.z.array(zod_1.z.string()));
}
/**
* @description Display a collapsable grouping of additional fields, a "sub form". The second value is the inputSpec spec for the sub form.
* @example
* ```
objectExample: Value.object(
{
// required
name: 'Object Example',
// optional
description: null,
warning: null,
},
InputSpec.of({}),
),
* ```
*/
static object(a, spec) {
const value = new Value(async (options) => {
const built = await spec.build(options);
return {
spec: {
type: 'object',
description: null,
warning: null,
...a,
spec: built.spec,
},
validator: built.validator,
};
}, spec.validator);
value._objectSpec = { inputSpec: spec, params: a };
return value;
}
/**
* Displays a file upload input field.
*
* @param a.extensions - Allowed file extensions (e.g. `[".pem", ".crt"]`)
* @param a.required - Whether a file must be selected
*/
static file(a) {
const buildValue = {
type: 'file',
description: null,
warning: null,
...a,
};
return new Value(() => ({
spec: {
...buildValue,
},
validator: asRequiredParser(exports.fileInfoParser, a),
}), asRequiredParser(exports.fileInfoParser, a));
}
/** Like {@link Value.file} but options are resolved lazily at runtime via a builder function. */
static dynamicFile(a) {
return new Value(async (options) => {
const spec = {
type: 'file',
description: null,
warning: null,
...(await a(options)),
};
return {
spec,
validator: asRequiredParser(exports.fileInfoParser, spec),
};
}, exports.fileInfoParser.nullable());
}
/**
* @description Displays a dropdown, allowing for a single selection. Depending on the selection, a different object ("sub form") is presented.
* @example
* ```
unionExample: Value.union(
{
// required
name: 'Union Example',
default: 'option1',
// optional
description: null,
warning: null,
disabled: false,
immutable: false,
},
Variants.of({
option1: {
name: 'Option 1',
spec: InputSpec.of({}),
},
option2: {
name: 'Option 2',
spec: InputSpec.of({}),
},
}),
),
* ```
*/
static union(a) {
return new Value(async (options) => {
const built = await a.variants.build(options);
return {
spec: {
type: 'union',
description: null,
warning: null,
disabled: false,
...a,
variants: built.spec,
immutable: a.immutable ?? false,
},
validator: built.validator,
};
}, a.variants.validator);
}
static dynamicUnion(getA, validator = zod_1.z.any()) {
return new Value(async (options) => {
const newValues = await getA(options);
const built = await newValues.variants.build(options);
return {
spec: {
type: 'union',
description: null,
warning: null,
...newValues,
variants: built.spec,
immutable: false,
},
validator: built.validator,
};
}, validator);
}
/**
* @description Presents an interface to add/remove/edit items in a list.
* @example
* In this example, we create a list of text inputs.
*
* ```
listExampleText: Value.list(
List.text(
{
// required
name: 'Text List',
// optional
description: null,
warning: null,
default: [],
minLength: null,
maxLength: null,
},
{
// required
patterns: [],
// optional
placeholder: null,
generate: null,
inputmode: 'url',
masked: false,
minLength: null,
maxLength: null,
},
),
),
* ```
* @example
* In this example, we create a list of objects.
*
* ```
listExampleObject: Value.list(
List.obj(
{
// required
name: 'Object List',
// optional
description: null,
warning: null,
default: [],
minLength: null,
maxLength: null,
},
{
// required
spec: InputSpec.of({}),
// optional
displayAs: null,
uniqueBy: null,
},
),
),
* ```
*/
static list(a) {
return new Value((options) => a.build(options), a.validator);
}
static hidden(parser = zod_1.z.any()) {
return new Value(async () => {
return {
spec: {
type: 'hidden',
},
validator: parser,
};
}, parser);
}
/**
* @description Provides a way to define a hidden field with a static value. Useful for tracking
* @example
* ```
hiddenExample: Value.hidden(),
* ```
*/
static dynamicHidden(getParser) {
return new Value(async (options) => {
const validator = await getParser(options);
return {
spec: {
type: 'hidden',
},
validator,
};
}, zod_1.z.any());
}
/**
* Returns a new Value that produces the same field spec but with `disabled` set to the given message.
* The field remains in the form but cannot be edited by the user.
*
* @param message - The reason the field is disabled, displayed to the user
*/
withDisabled(message) {
const original = this;
const v = new Value(async (options) => {
const built = await original.build(options);
return {
spec: { ...built.spec, disabled: message },
validator: built.validator,
};
}, this.validator);
v._objectSpec = this._objectSpec;
return v;
}
/**
* Transforms the validated output value using a mapping function.
* The form field itself remains unchanged, but the value is transformed after validation.
*
* @param fn - A function to transform the validated value
*/
map(fn) {
return new Value(async (options) => {
const built = await this.build(options);
return {
spec: built.spec,
validator: built.validator.transform(fn),
};
}, this.validator.transform(fn));
}
}
exports.Value = Value;
//# sourceMappingURL=value.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,111 @@
import { DeepPartial } from '../../../types';
import { ValueSpecUnion } from '../inputSpecTypes';
import { LazyBuild, InputSpec, ExtractInputSpecType, ExtractInputSpecStaticValidatedAs } from './inputSpec';
import { z } from 'zod';
/**
* The runtime result type of a discriminated union form field.
* Contains `selection` (the chosen variant key), `value` (the variant's form data),
* and optionally `other` (partial data from previously selected variants).
*/
export type UnionRes<VariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any>;
};
}, K extends keyof VariantValues & string = keyof VariantValues & string> = {
[key in keyof VariantValues]: {
selection: key;
value: ExtractInputSpecType<VariantValues[key]['spec']>;
other?: {
[key2 in Exclude<keyof VariantValues & string, key>]?: DeepPartial<ExtractInputSpecType<VariantValues[key2]['spec']>>;
};
};
}[K];
/** Like {@link UnionRes} but using the static (Zod-inferred) validated types. */
export type UnionResStaticValidatedAs<VariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any>;
};
}, K extends keyof VariantValues & string = keyof VariantValues & string> = {
[key in keyof VariantValues]: {
selection: key;
value: ExtractInputSpecStaticValidatedAs<VariantValues[key]['spec']>;
other?: {
[key2 in Exclude<keyof VariantValues & string, key>]?: DeepPartial<ExtractInputSpecStaticValidatedAs<VariantValues[key2]['spec']>>;
};
};
}[K];
/**
* Used in the the Value.select { @link './value.ts' }
* 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.select
```ts
export const disabled = InputSpec.of({});
export const size = Value.number({
name: "Max Chain Size",
default: 550,
description: "Limit of blockchain size on disk.",
warning: "Increasing this value will require re-syncing your node.",
required: true,
range: "[550,1000000)",
integral: true,
units: "MiB",
placeholder: null,
});
export const automatic = InputSpec.of({ size: size });
export const size1 = Value.number({
name: "Failsafe Chain Size",
default: 65536,
description: "Prune blockchain if size expands beyond this.",
warning: null,
required: true,
range: "[550,1000000)",
integral: true,
units: "MiB",
placeholder: null,
});
export const manual = InputSpec.of({ size: size1 });
export const pruningSettingsVariants = Variants.of({
disabled: { name: "Disabled", spec: disabled },
automatic: { name: "Automatic", spec: automatic },
manual: { name: "Manual", spec: manual },
});
export const pruning = Value.union(
{
name: "Pruning Settings",
description:
'- Disabled: Disable pruning\n- Automatic: Limit blockchain size on disk to a certain number of megabytes\n- Manual: Prune blockchain with the "pruneblockchain" RPC\n',
warning: null,
default: "disabled",
},
pruningSettingsVariants
);
```
*/
export declare class Variants<VariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any, any>;
};
}, OuterType = unknown> {
build: LazyBuild<{
spec: ValueSpecUnion['variants'];
validator: z.ZodType<UnionRes<VariantValues>>;
}, OuterType>;
readonly validator: z.ZodType<UnionResStaticValidatedAs<VariantValues>>;
private constructor();
readonly _TYPE: UnionRes<VariantValues>;
/**
* Creates a `Variants` instance from a record mapping variant keys to their display name and form spec.
*
* @param a - A record of `{ name: string, spec: InputSpec }` entries, one per variant
*/
static of<VariantValues extends {
[K in string]: {
name: string;
spec: InputSpec<any>;
};
}>(a: VariantValues): Variants<VariantValues, unknown>;
}
@@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Variants = void 0;
const zod_1 = require("zod");
/**
* Used in the the Value.select { @link './value.ts' }
* 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.select
```ts
export const disabled = InputSpec.of({});
export const size = Value.number({
name: "Max Chain Size",
default: 550,
description: "Limit of blockchain size on disk.",
warning: "Increasing this value will require re-syncing your node.",
required: true,
range: "[550,1000000)",
integral: true,
units: "MiB",
placeholder: null,
});
export const automatic = InputSpec.of({ size: size });
export const size1 = Value.number({
name: "Failsafe Chain Size",
default: 65536,
description: "Prune blockchain if size expands beyond this.",
warning: null,
required: true,
range: "[550,1000000)",
integral: true,
units: "MiB",
placeholder: null,
});
export const manual = InputSpec.of({ size: size1 });
export const pruningSettingsVariants = Variants.of({
disabled: { name: "Disabled", spec: disabled },
automatic: { name: "Automatic", spec: automatic },
manual: { name: "Manual", spec: manual },
});
export const pruning = Value.union(
{
name: "Pruning Settings",
description:
'- Disabled: Disable pruning\n- Automatic: Limit blockchain size on disk to a certain number of megabytes\n- Manual: Prune blockchain with the "pruneblockchain" RPC\n',
warning: null,
default: "disabled",
},
pruningSettingsVariants
);
```
*/
class Variants {
constructor(build, validator) {
this.build = build;
this.validator = validator;
this._TYPE = null;
}
/**
* Creates a `Variants` instance from a record mapping variant keys to their display name and form spec.
*
* @param a - A record of `{ name: string, spec: InputSpec }` entries, one per variant
*/
static of(a) {
const staticValidators = {};
for (const key in a) {
const value = a[key];
staticValidators[key] = value.spec.validator;
}
const other = zod_1.z
.object(Object.fromEntries(Object.entries(staticValidators).map(([k, v]) => [
k,
zod_1.z.any().optional(),
])))
.optional();
return new Variants(async (options) => {
const validators = {};
const variants = {};
for (const key in a) {
const value = a[key];
const built = await value.spec.build(options);
variants[key] = {
name: value.name,
spec: built.spec,
};
validators[key] = built.validator;
}
const other = zod_1.z
.object(Object.fromEntries(Object.entries(validators).map(([k, v]) => [
k,
zod_1.z.any().optional(),
])))
.optional();
return {
spec: variants,
validator: zod_1.z.union(Object.entries(validators).map(([k, v]) => zod_1.z.object({
selection: zod_1.z.literal(k),
value: v,
other,
}))),
};
}, zod_1.z.union(Object.entries(staticValidators).map(([k, v]) => zod_1.z.object({
selection: zod_1.z.literal(k),
value: v,
other,
}))));
}
}
exports.Variants = Variants;
//# sourceMappingURL=variants.js.map
@@ -0,0 +1 @@
{"version":3,"file":"variants.js","sourceRoot":"","sources":["../../../../../../base/lib/actions/input/builder/variants.ts"],"names":[],"mappings":";;;AAQA,6BAAuB;AAgDvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAa,QAAQ;IASnB,YACS,KAMN,EACe,SAEf;QATM,UAAK,GAAL,KAAK,CAMX;QACe,cAAS,GAAT,SAAS,CAExB;QAEM,UAAK,GAA4B,IAAW,CAAA;IADlD,CAAC;IAEJ;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAOP,CAAgB;QAChB,MAAM,gBAAgB,GAAG,EAIxB,CAAA;QACD,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;YACpB,gBAAgB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAA;QAC9C,CAAC;QACD,MAAM,KAAK,GAAG,OAAC;aACZ,MAAM,CACL,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/C,CAAC;YACD,OAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACnB,CAAC,CACH,CACF;aACA,QAAQ,EAAE,CAAA;QACb,OAAO,IAAI,QAAQ,CACjB,KAAK,EAAE,OAAO,EAAE,EAAE;YAChB,MAAM,UAAU,GAAG,EAIlB,CAAA;YACD,MAAM,QAAQ,GAAG,EAKhB,CAAA;YACD,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;gBACpB,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;gBACpB,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAc,CAAC,CAAA;gBACpD,QAAQ,CAAC,GAAG,CAAC,GAAG;oBACd,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;iBACjB,CAAA;gBACD,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAA;YACnC,CAAC;YACD,MAAM,KAAK,GAAG,OAAC;iBACZ,MAAM,CACL,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;gBACzC,CAAC;gBACD,OAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;aACnB,CAAC,CACH,CACF;iBACA,QAAQ,EAAE,CAAA;YACb,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,OAAC,CAAC,KAAK,CAChB,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CACxC,OAAC,CAAC,MAAM,CAAC;oBACP,SAAS,EAAE,OAAC,CAAC,OAAO,CAAC,CAAC,CAAC;oBACvB,KAAK,EAAE,CAAC;oBACR,KAAK;iBACN,CAAC,CAC4D,CAC1D;aACT,CAAA;QACH,CAAC,EACD,OAAC,CAAC,KAAK,CACL,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAC9C,OAAC,CAAC,MAAM,CAAC;YACP,SAAS,EAAE,OAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACvB,KAAK,EAAE,CAAC;YACR,KAAK;SACN,CAAC,CAC4D,CAC1D,CACT,CAAA;IACH,CAAC;CACF;AA9GD,4BA8GC"}
+3
View File
@@ -0,0 +1,3 @@
export * as constants from './inputSpecConstants';
export * as types from './inputSpecTypes';
export * as builder from './builder';
+40
View File
@@ -0,0 +1,40 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.builder = exports.types = exports.constants = void 0;
exports.constants = __importStar(require("./inputSpecConstants"));
exports.types = __importStar(require("./inputSpecTypes"));
exports.builder = __importStar(require("./builder"));
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../base/lib/actions/input/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kEAAiD;AACjD,0DAAyC;AACzC,qDAAoC"}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,247 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.smtpShape = exports.smtpInputSpec = exports.systemSmtpSpec = exports.smtpProviderVariants = exports.customSmtp = void 0;
exports.smtpPrefill = smtpPrefill;
const util_1 = require("../../util");
const inputSpec_1 = require("./builder/inputSpec");
const value_1 = require("./builder/value");
const variants_1 = require("./builder/variants");
const zod_1 = require("zod");
const securityVariants = variants_1.Variants.of({
tls: {
name: 'TLS',
spec: inputSpec_1.InputSpec.of({
port: value_1.Value.dynamicText(async () => ({
name: 'Port',
required: true,
default: '465',
disabled: 'Fixed for TLS',
})),
}),
},
starttls: {
name: 'STARTTLS',
spec: inputSpec_1.InputSpec.of({
port: value_1.Value.select({
name: 'Port',
default: '587',
values: { '25': '25', '587': '587', '2525': '2525' },
}),
}),
},
});
/**
* Creates an SMTP field spec with provider-specific defaults pre-filled.
*/
function smtpFields(defaults = {}) {
const hostSpec = value_1.Value.text({
name: 'Host',
required: true,
default: defaults.host ?? null,
placeholder: 'smtp.example.com',
});
return inputSpec_1.InputSpec.of({
host: defaults.hostDisabled
? hostSpec.withDisabled('Fixed for this provider')
: hostSpec,
security: value_1.Value.union({
name: 'Connection Security',
default: defaults.security ?? 'tls',
variants: securityVariants,
}),
from: value_1.Value.text({
name: 'From Address',
required: true,
default: null,
placeholder: 'Example Name <test@example.com>',
patterns: [util_1.Patterns.emailWithName],
}),
username: value_1.Value.text({
name: 'Username',
required: true,
default: null,
}),
password: value_1.Value.text({
name: 'Password',
required: false,
default: null,
masked: true,
}),
});
}
/**
* Base SMTP settings with no provider-specific defaults.
*/
exports.customSmtp = smtpFields();
/**
* Provider presets for SMTP configuration.
* Each variant has SMTP fields pre-filled with the provider's recommended settings.
*/
exports.smtpProviderVariants = variants_1.Variants.of({
gmail: {
name: 'Gmail',
spec: smtpFields({
host: 'smtp.gmail.com',
security: 'tls',
hostDisabled: true,
}),
},
ses: {
name: 'Amazon SES',
spec: smtpFields({
host: 'email-smtp.us-east-1.amazonaws.com',
security: 'tls',
}),
},
sendgrid: {
name: 'SendGrid',
spec: smtpFields({
host: 'smtp.sendgrid.net',
security: 'tls',
hostDisabled: true,
}),
},
mailgun: {
name: 'Mailgun',
spec: smtpFields({
host: 'smtp.mailgun.org',
security: 'tls',
hostDisabled: true,
}),
},
protonmail: {
name: 'Proton Mail',
spec: smtpFields({
host: 'smtp.protonmail.ch',
security: 'tls',
hostDisabled: true,
}),
},
other: {
name: 'Other',
spec: exports.customSmtp,
},
});
/**
* System SMTP settings with provider presets.
* Wraps smtpProviderVariants in a union for use by the system email settings page.
*/
exports.systemSmtpSpec = inputSpec_1.InputSpec.of({
provider: value_1.Value.union({
name: 'Provider',
default: 'gmail',
variants: exports.smtpProviderVariants,
}),
});
const smtpVariants = variants_1.Variants.of({
disabled: { name: 'Disabled', spec: inputSpec_1.InputSpec.of({}) },
system: {
name: 'System Credentials',
spec: inputSpec_1.InputSpec.of({
customFrom: value_1.Value.text({
name: 'Custom From Address',
description: 'A custom from address for this service. If not provided, the system from address will be used.',
required: false,
default: null,
placeholder: 'Name <test@example.com>',
patterns: [util_1.Patterns.emailWithName],
}),
}),
},
custom: {
name: 'Custom Credentials',
spec: inputSpec_1.InputSpec.of({
provider: value_1.Value.union({
name: 'Provider',
default: null,
variants: exports.smtpProviderVariants,
}),
}),
},
});
/**
* For service inputSpec. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings with provider presets
*/
exports.smtpInputSpec = value_1.Value.dynamicUnion(async ({ effects }) => {
const smtp = await new util_1.GetSystemSmtp(effects).once();
const disabled = smtp ? [] : ['system'];
return {
name: 'SMTP',
description: 'Optionally provide an SMTP server for sending emails',
default: 'disabled',
disabled,
variants: smtpVariants,
};
}, smtpVariants.validator);
const securityShape = zod_1.z
.object({
selection: zod_1.z.enum(['tls', 'starttls']).catch('tls'),
value: zod_1.z.object({ port: zod_1.z.string().catch('465') }).catch({ port: '465' }),
})
.catch({ selection: 'tls', value: { port: '465' } });
const providerShape = zod_1.z
.object({
selection: zod_1.z.string().catch('other'),
value: zod_1.z
.object({
host: zod_1.z.string().catch(''),
from: zod_1.z.string().catch(''),
username: zod_1.z.string().catch(''),
password: zod_1.z.string().nullable().optional().catch(null),
security: securityShape,
})
.catch({
host: '',
from: '',
username: '',
password: null,
security: securityShape.parse(undefined),
}),
})
.catch({
selection: 'other',
value: {
host: '',
from: '',
username: '',
password: null,
security: securityShape.parse(undefined),
},
});
/**
* Zod schema for persisting SMTP selection in a store file model.
* Use this instead of `smtpInputSpec.validator` to avoid cross-zod-instance issues.
*/
exports.smtpShape = zod_1.z
.discriminatedUnion('selection', [
zod_1.z.object({
selection: zod_1.z.literal('disabled'),
value: zod_1.z.object({}).catch({}),
}),
zod_1.z.object({
selection: zod_1.z.literal('system'),
value: zod_1.z
.object({ customFrom: zod_1.z.string().nullable().optional().catch(null) })
.catch({ customFrom: null }),
}),
zod_1.z.object({
selection: zod_1.z.literal('custom'),
value: zod_1.z
.object({ provider: providerShape })
.catch({ provider: providerShape.parse(undefined) }),
}),
])
.catch({ selection: 'disabled', value: {} });
/**
* Convert a stored SmtpSelection to a value suitable for prefilling smtpInputSpec.
*
* The stored type (SmtpSelection from smtpShape) uses flat unions for provider/security
* selection, while the input spec (smtpInputSpec) uses distributed discriminated unions
* (UnionRes). These are structurally incompatible in TypeScript's type system, even
* though the runtime values are identical. This function bridges the two types so that
* service code doesn't need `as any`.
*/
function smtpPrefill(smtp) {
return smtp || undefined;
}
//# sourceMappingURL=inputSpecConstants.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,285 @@
/**
* A record mapping field keys to their {@link ValueSpec} definitions.
* This is the root shape of a dynamic form specification — it defines the complete set
* of configurable fields for a service or action.
*/
export type InputSpec = Record<string, ValueSpec>;
/**
* The discriminator for all supported form field types.
*/
export type ValueType = 'text' | 'textarea' | 'number' | 'color' | 'datetime' | 'toggle' | 'select' | 'multiselect' | 'list' | 'object' | 'file' | 'union' | 'hidden';
/** Union of all concrete form field spec types. Discriminate on the `type` field. */
export type ValueSpec = ValueSpecOf<ValueType>;
/** core spec types. These types provide the metadata for performing validations */
export type ValueSpecOf<T extends ValueType> = T extends "text" ? ValueSpecText : T extends "textarea" ? ValueSpecTextarea : T extends "number" ? ValueSpecNumber : T extends "color" ? ValueSpecColor : T extends "datetime" ? ValueSpecDatetime : T extends "toggle" ? ValueSpecToggle : T extends "select" ? ValueSpecSelect : T extends "multiselect" ? ValueSpecMultiselect : T extends "list" ? ValueSpecList : T extends "object" ? ValueSpecObject : T extends "file" ? ValueSpecFile : T extends "union" ? ValueSpecUnion : T extends "hidden" ? ValueSpecHidden : never;
/** Spec for a single-line text input field. */
export type ValueSpecText = {
/** Display label for the field. */
name: string;
/** Optional help text displayed below the field. */
description: string | null;
/** Optional warning message displayed to the user. */
warning: string | null;
type: 'text';
/** Regex patterns used to validate the input value. */
patterns: Pattern[];
/** Minimum character length, or `null` for no minimum. */
minLength: number | null;
/** Maximum character length, or `null` for no maximum. */
maxLength: number | null;
/** Whether the field should obscure input (e.g. for passwords). */
masked: boolean;
/** HTML input mode hint for mobile keyboards. */
inputmode: 'text' | 'email' | 'tel' | 'url';
/** Placeholder text shown when the field is empty. */
placeholder: string | null;
/** Whether the field must have a value. */
required: boolean;
/** Default value, which may be a literal string or a {@link RandomString} generation spec. */
default: DefaultString | null;
/** `false` if editable, or a string message explaining why the field is disabled. */
disabled: false | string;
/** If set, provides a "generate" button that fills the field with a random string matching this spec. */
generate: null | RandomString;
/** Whether the field value cannot be changed after initial configuration. */
immutable: boolean;
};
/** Spec for a multi-line textarea input field. */
export type ValueSpecTextarea = {
name: string;
description: string | null;
warning: string | null;
type: 'textarea';
/** Regex patterns used to validate the input value. */
patterns: Pattern[];
placeholder: string | null;
minLength: number | null;
maxLength: number | null;
/** Minimum number of visible rows. */
minRows: number;
/** Maximum number of visible rows before scrolling. */
maxRows: number;
required: boolean;
default: string | null;
disabled: false | string;
immutable: boolean;
};
/** Spec for a numeric input field. */
export type ValueSpecNumber = {
type: 'number';
/** Minimum allowed value, or `null` for unbounded. */
min: number | null;
/** Maximum allowed value, or `null` for unbounded. */
max: number | null;
/** Whether only whole numbers are accepted. */
integer: boolean;
/** Step increment for the input spinner, or `null` for any precision. */
step: number | null;
/** Display label for the unit (e.g. `"MB"`, `"seconds"`), shown next to the field. */
units: string | null;
placeholder: string | null;
name: string;
description: string | null;
warning: string | null;
required: boolean;
default: number | null;
disabled: false | string;
immutable: boolean;
};
/** Spec for a browser-native color picker field. */
export type ValueSpecColor = {
name: string;
description: string | null;
warning: string | null;
type: 'color';
required: boolean;
/** Default hex color string (e.g. `"#ff0000"`), or `null`. */
default: string | null;
disabled: false | string;
immutable: boolean;
};
/** Spec for a date, time, or datetime input field. */
export type ValueSpecDatetime = {
name: string;
description: string | null;
warning: string | null;
type: 'datetime';
required: boolean;
/** Controls which kind of picker is displayed. */
inputmode: 'date' | 'time' | 'datetime-local';
/** Minimum selectable date/time as an ISO string, or `null`. */
min: string | null;
/** Maximum selectable date/time as an ISO string, or `null`. */
max: string | null;
default: string | null;
disabled: false | string;
immutable: boolean;
};
/** Spec for a single-select field displayed as radio buttons in a modal. */
export type ValueSpecSelect = {
/** Map of option keys to display labels. */
values: Record<string, string>;
name: string;
description: string | null;
warning: string | null;
type: 'select';
default: string | null;
/** `false` if all enabled, a string disabling the whole field, or an array of disabled option keys. */
disabled: false | string | string[];
immutable: boolean;
};
/** Spec for a multi-select field displayed as checkboxes in a modal. */
export type ValueSpecMultiselect = {
/** Map of option keys to display labels. */
values: Record<string, string>;
name: string;
description: string | null;
warning: string | null;
type: 'multiselect';
/** Minimum number of selections required, or `null`. */
minLength: number | null;
/** Maximum number of selections allowed, or `null`. */
maxLength: number | null;
/** `false` if all enabled, a string disabling the whole field, or an array of disabled option keys. */
disabled: false | string | string[];
/** Array of option keys selected by default. */
default: string[];
immutable: boolean;
};
/** Spec for a boolean toggle (on/off switch). */
export type ValueSpecToggle = {
name: string;
description: string | null;
warning: string | null;
type: 'toggle';
default: boolean | null;
disabled: false | string;
immutable: boolean;
};
/**
* Spec for a discriminated union field — displays a dropdown for variant selection,
* and each variant can have its own nested sub-form.
*/
export type ValueSpecUnion = {
name: string;
description: string | null;
warning: string | null;
type: 'union';
/** Map of variant keys to their display name and nested form spec. */
variants: Record<string, {
/** Display name for this variant in the dropdown. */
name: string;
/** Nested form spec shown when this variant is selected. */
spec: InputSpec;
}>;
/** `false` if all enabled, a string disabling the whole field, or an array of disabled variant keys. */
disabled: false | string | string[];
default: string | null;
immutable: boolean;
};
/** Spec for a file upload input field. */
export type ValueSpecFile = {
name: string;
description: string | null;
warning: string | null;
type: 'file';
/** Allowed file extensions (e.g. `[".pem", ".crt"]`). */
extensions: string[];
required: boolean;
};
/** Spec for a collapsible grouping of nested fields (a "sub-form"). */
export type ValueSpecObject = {
name: string;
description: string | null;
warning: string | null;
type: 'object';
/** The nested form spec containing this object's fields. */
spec: InputSpec;
};
/** Spec for a hidden field — not displayed to the user but included in the form data. */
export type ValueSpecHidden = {
type: 'hidden';
};
/** The two supported list item types. */
export type ListValueSpecType = 'text' | 'object';
/** Maps a {@link ListValueSpecType} to its concrete list item spec. */
export type ListValueSpecOf<T extends ListValueSpecType> = T extends "text" ? ListValueSpecText : T extends "object" ? ListValueSpecObject : never;
/** A list field spec — union of text-list and object-list variants. */
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>;
/**
* Spec for a list field — an interface to add, remove, and edit items in an ordered collection.
* The `spec` field determines whether list items are text strings or structured objects.
*/
export type ValueSpecListOf<T extends ListValueSpecType> = {
name: string;
description: string | null;
warning: string | null;
type: 'list';
/** The item spec — determines whether this is a list of text values or objects. */
spec: ListValueSpecOf<T>;
/** Minimum number of items, or `null` for no minimum. */
minLength: number | null;
/** Maximum number of items, or `null` for no maximum. */
maxLength: number | null;
disabled: false | string;
/** Default list items to populate on creation. */
default: string[] | DefaultString[] | Record<string, unknown>[] | readonly string[] | readonly DefaultString[] | readonly Record<string, unknown>[];
};
/** A regex validation pattern with a human-readable description of what it enforces. */
export type Pattern = {
/** The regex pattern string (without delimiters). */
regex: string;
/** A user-facing explanation shown when validation fails (e.g. `"Must be a valid email"`). */
description: string;
};
/** Spec for text items within a list field. */
export type ListValueSpecText = {
type: 'text';
patterns: Pattern[];
minLength: number | null;
maxLength: number | null;
masked: boolean;
generate: null | RandomString;
inputmode: 'text' | 'email' | 'tel' | 'url';
placeholder: string | null;
};
/** Spec for object items within a list field. */
export type ListValueSpecObject = {
type: 'object';
/** The form spec for each object item. */
spec: InputSpec;
/** Defines how uniqueness is determined among list items. */
uniqueBy: UniqueBy;
/** An expression used to generate the display string for each item in the list summary (e.g. a key path). */
displayAs: string | null;
};
/**
* Describes how list items determine uniqueness.
* - `null`: no uniqueness constraint
* - `string`: unique by a specific field key
* - `{ any: UniqueBy[] }`: unique if any of the sub-constraints match
* - `{ all: UniqueBy[] }`: unique if all sub-constraints match together
*/
export type UniqueBy = null | string | {
any: readonly UniqueBy[] | UniqueBy[];
} | {
all: readonly UniqueBy[] | UniqueBy[];
};
/** A default value that is either a literal string or a {@link RandomString} generation spec. */
export type DefaultString = string | RandomString;
/** Spec for generating a random string — used for default passwords, API keys, etc. */
export type RandomString = {
/** The character set to draw from (e.g. `"a-zA-Z0-9"`). */
charset: string;
/** The length of the generated string. */
len: number;
};
/**
* Type guard that narrows a {@link ValueSpec} to a {@link ValueSpecListOf} of a specific item type.
*
* @param t - The value spec to check
* @param s - The list item type to narrow to (`"text"` or `"object"`)
*/
export declare function isValueSpecListOf<S extends ListValueSpecType>(t: ValueSpec, s: S): t is ValueSpecListOf<S> & {
spec: ListValueSpecOf<S>;
};
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isValueSpecListOf = isValueSpecListOf;
/**
* Type guard that narrows a {@link ValueSpec} to a {@link ValueSpecListOf} of a specific item type.
*
* @param t - The value spec to check
* @param s - The list item type to narrow to (`"text"` or `"object"`)
*/
function isValueSpecListOf(t, s) {
return 'spec' in t && t.spec.type === s;
}
//# sourceMappingURL=inputSpecTypes.js.map
@@ -0,0 +1 @@
{"version":3,"file":"inputSpecTypes.js","sourceRoot":"","sources":["../../../../../base/lib/actions/input/inputSpecTypes.ts"],"names":[],"mappings":";;AAoVA,8CAKC;AAXD;;;;;GAKG;AACH,SAAgB,iBAAiB,CAC/B,CAAY,EACZ,CAAI;IAEJ,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAA;AACzC,CAAC"}
+58
View File
@@ -0,0 +1,58 @@
import { InputSpec } from './input/builder';
import { ExtractInputSpecType } from './input/builder/inputSpec';
import * as T from '../types';
import { InitScript } from '../inits';
export type Run<A extends Record<string, any>> = (options: {
effects: T.Effects;
input: A;
spec: T.inputSpecTypes.InputSpec;
}) => Promise<(T.ActionResult & {
version: '1';
}) | null | void | undefined>;
export type GetInput<A extends Record<string, any>> = (options: {
effects: T.Effects;
prefill: T.DeepPartial<A> | null;
}) => Promise<null | void | undefined | T.DeepPartial<A>>;
export type MaybeFn<T, Opts = {
effects: T.Effects;
}> = T | ((options: Opts) => Promise<T>);
export interface ActionInfo<Id extends T.ActionId, Type extends Record<string, any>> {
readonly id: Id;
readonly _INPUT: Type;
}
export declare class Action<Id extends T.ActionId, Type extends Record<string, any>> implements ActionInfo<Id, Type> {
readonly id: Id;
private readonly metadataFn;
private readonly inputSpec;
private readonly getInputFn;
private readonly runFn;
readonly _INPUT: Type;
private prevInputSpec;
private constructor();
static withInput<Id extends T.ActionId, InputSpecType extends InputSpec<Record<string, any>>>(id: Id, metadata: MaybeFn<Omit<T.ActionMetadata, 'hasInput'>>, inputSpec: MaybeFn<InputSpecType, {
effects: T.Effects;
prefill: unknown | null;
}>, getInput: GetInput<ExtractInputSpecType<InputSpecType>>, run: Run<ExtractInputSpecType<InputSpecType>>): Action<Id, ExtractInputSpecType<InputSpecType>>;
static withoutInput<Id extends T.ActionId>(id: Id, metadata: MaybeFn<Omit<T.ActionMetadata, 'hasInput'>>, run: Run<{}>): Action<Id, {}>;
exportMetadata(options: {
effects: T.Effects;
}): Promise<T.ActionMetadata>;
getInput(options: {
effects: T.Effects;
prefill: T.DeepPartial<Type> | null;
}): Promise<T.ActionInput>;
run(options: {
effects: T.Effects;
input: Type;
}): Promise<T.ActionResult | null>;
}
export declare class Actions<AllActions extends Record<T.ActionId, Action<T.ActionId, any>>> implements InitScript {
private readonly actions;
private constructor();
static of(): Actions<{}>;
addAction<A extends Action<T.ActionId, any>>(action: A): Actions<AllActions & {
[id in A['id']]: A;
}>;
init(effects: T.Effects): Promise<void>;
get<Id extends T.ActionId>(actionId: Id): AllActions[Id];
}
+117
View File
@@ -0,0 +1,117 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Actions = exports.Action = void 0;
const util_1 = require("../util");
function callMaybeFn(maybeFn, options) {
if (maybeFn instanceof Function) {
return maybeFn(options);
}
else {
return Promise.resolve(maybeFn);
}
}
function mapMaybeFn(maybeFn, map) {
if (maybeFn instanceof Function) {
return async (...args) => map(await maybeFn(...args));
}
else {
return map(maybeFn);
}
}
class Action {
constructor(id, metadataFn, inputSpec, getInputFn, runFn) {
this.id = id;
this.metadataFn = metadataFn;
this.inputSpec = inputSpec;
this.getInputFn = getInputFn;
this.runFn = runFn;
this._INPUT = null;
this.prevInputSpec = {};
}
static withInput(id, metadata, inputSpec, getInput, run) {
return new Action(id, mapMaybeFn(metadata, (m) => ({ ...m, hasInput: true })), inputSpec, getInput, run);
}
static withoutInput(id, metadata, run) {
return new Action(id, mapMaybeFn(metadata, (m) => ({ ...m, hasInput: false })), null, async () => null, run);
}
async exportMetadata(options) {
const childEffects = options.effects.child(`setupActions/${this.id}`);
childEffects.constRetry = (0, util_1.once)(() => {
this.exportMetadata(options);
});
const metadata = await callMaybeFn(this.metadataFn, {
effects: childEffects,
});
await options.effects.action.export({ id: this.id, metadata });
return metadata;
}
async getInput(options) {
let spec = {};
if (this.inputSpec) {
const inputSpec = await callMaybeFn(this.inputSpec, options);
const built = await inputSpec?.build(options);
if (built) {
this.prevInputSpec[options.effects.eventId] = built;
spec = built.spec;
}
}
return {
eventId: options.effects.eventId,
spec,
value: (await this.getInputFn(options)) || null,
};
}
async run(options) {
let spec = {};
if (this.inputSpec) {
const prevInputSpec = this.prevInputSpec[options.effects.eventId];
if (!prevInputSpec) {
throw new Error(`getActionInput has not been called for EventID ${options.effects.eventId}`);
}
options.input = prevInputSpec.validator.parse(options.input);
spec = prevInputSpec.spec;
}
return ((await this.runFn({
effects: options.effects,
input: options.input,
spec,
})) ?? null);
}
}
exports.Action = Action;
class Actions {
constructor(actions) {
this.actions = actions;
}
static of() {
return new Actions({});
}
addAction(action) {
return new Actions({ ...this.actions, [action.id]: action });
}
async init(effects) {
for (let action of Object.values(this.actions)) {
const fn = async () => {
let res = () => { };
const complete = new Promise((resolve) => {
res = resolve;
});
const e = effects.child(action.id);
e.constRetry = (0, util_1.once)(() => complete.then(() => fn()).catch(console.error));
try {
await action.exportMetadata({ effects: e });
}
finally {
res();
}
};
await fn();
}
await effects.action.clear({ except: Object.keys(this.actions) });
}
get(actionId) {
return this.actions[actionId];
}
}
exports.Actions = Actions;
//# sourceMappingURL=setupActions.js.map
@@ -0,0 +1 @@
{"version":3,"file":"setupActions.js","sourceRoot":"","sources":["../../../../base/lib/actions/setupActions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
/* eslint-disable */
var jumpToCode = (function init() {
// Classes of code we would like to highlight in the file view
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
// Elements to highlight in the file listing view
var fileListingElements = ['td.pct.low'];
// We don't want to select elements that are direct descendants of another match
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
// Selecter that finds elements on the page to which we can jump
var selector =
fileListingElements.join(', ') +
', ' +
notSelector +
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
// The NodeList of matching elements
var missingCoverageElements = document.querySelectorAll(selector);
var currentIndex;
function toggleClass(index) {
missingCoverageElements
.item(currentIndex)
.classList.remove('highlighted');
missingCoverageElements.item(index).classList.add('highlighted');
}
function makeCurrent(index) {
toggleClass(index);
currentIndex = index;
missingCoverageElements.item(index).scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});
}
function goToPrevious() {
var nextIndex = 0;
if (typeof currentIndex !== 'number' || currentIndex === 0) {
nextIndex = missingCoverageElements.length - 1;
} else if (missingCoverageElements.length > 1) {
nextIndex = currentIndex - 1;
}
makeCurrent(nextIndex);
}
function goToNext() {
var nextIndex = 0;
if (
typeof currentIndex === 'number' &&
currentIndex < missingCoverageElements.length - 1
) {
nextIndex = currentIndex + 1;
}
makeCurrent(nextIndex);
}
return function jump(event) {
if (
document.getElementById('fileSearch') === document.activeElement &&
document.activeElement != null
) {
// if we're currently focused on the search input, we don't want to navigate
return;
}
switch (event.which) {
case 78: // n
case 74: // j
goToNext();
break;
case 66: // b
case 75: // k
case 80: // p
goToPrevious();
break;
}
};
})();
window.addEventListener('keydown', jumpToCode);
File diff suppressed because one or more lines are too long
@@ -0,0 +1,196 @@
/* eslint-disable */
var addSorting = (function() {
'use strict';
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() {
return document.querySelector('.coverage-summary');
}
// returns the thead element of the summary table
function getTableHeader() {
return getTable().querySelector('thead tr');
}
// returns the tbody element of the summary table
function getTableBody() {
return getTable().querySelector('tbody');
}
// returns the th element for nth column
function getNthColumn(n) {
return getTableHeader().querySelectorAll('th')[n];
}
function onFilterInput() {
const searchValue = document.getElementById('fileSearch').value;
const rows = document.getElementsByTagName('tbody')[0].children;
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
if (
row.textContent
.toLowerCase()
.includes(searchValue.toLowerCase())
) {
row.style.display = '';
} else {
row.style.display = 'none';
}
}
}
// loads the search box
function addSearchBox() {
var template = document.getElementById('filterTemplate');
var templateClone = template.content.cloneNode(true);
templateClone.getElementById('fileSearch').oninput = onFilterInput;
template.parentElement.appendChild(templateClone);
}
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML =
colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function(a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function(a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc
? ' sorted-desc'
: ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function() {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i = 0; i < cols.length; i += 1) {
if (cols[i].sortable) {
// add the click event handler on the th so users
// dont have to click on those tiny arrows
el = getNthColumn(i).querySelector('.sorter').parentElement;
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function() {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData();
addSearchBox();
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);
@@ -0,0 +1,21 @@
import { PackageId, HealthCheckId, DependencyRequirement, CheckDependenciesResult } from '../types';
import { Effects } from '../Effects';
export type CheckDependencies<DependencyId extends PackageId = PackageId> = {
infoFor: (packageId: DependencyId) => {
requirement: DependencyRequirement;
result: CheckDependenciesResult;
};
installedSatisfied: (packageId: DependencyId) => boolean;
installedVersionSatisfied: (packageId: DependencyId) => boolean;
runningSatisfied: (packageId: DependencyId) => boolean;
tasksSatisfied: (packageId: DependencyId) => boolean;
healthCheckSatisfied: (packageId: DependencyId, healthCheckId: HealthCheckId) => boolean;
satisfied: () => boolean;
throwIfInstalledNotSatisfied: (packageId: DependencyId) => null;
throwIfInstalledVersionNotSatisfied: (packageId: DependencyId) => null;
throwIfRunningNotSatisfied: (packageId: DependencyId) => null;
throwIfTasksNotSatisfied: (packageId: DependencyId) => null;
throwIfHealthNotSatisfied: (packageId: DependencyId, healthCheckId?: HealthCheckId) => null;
throwIfNotSatisfied: (packageId?: DependencyId) => null;
};
export declare function checkDependencies<DependencyId extends PackageId = PackageId>(effects: Effects, packageIds?: DependencyId[]): Promise<CheckDependencies<DependencyId>>;
@@ -0,0 +1,156 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkDependencies = checkDependencies;
const exver_1 = require("../exver");
async function checkDependencies(effects, packageIds) {
let [dependencies, results] = await Promise.all([
effects.getDependencies(),
effects.checkDependencies({
packageIds,
}),
]);
if (packageIds) {
dependencies = dependencies.filter((d) => packageIds.includes(d.id));
}
const infoFor = (packageId) => {
const dependencyRequirement = dependencies.find((d) => d.id === packageId);
const dependencyResult = results.find((d) => d.packageId === packageId);
if (!dependencyRequirement || !dependencyResult) {
throw new Error(`Unknown DependencyId ${packageId}`);
}
return { requirement: dependencyRequirement, result: dependencyResult };
};
const installedSatisfied = (packageId) => !!infoFor(packageId).result.installedVersion;
const installedVersionSatisfied = (packageId) => {
const dep = infoFor(packageId);
return (!!dep.result.installedVersion &&
exver_1.ExtendedVersion.parse(dep.result.installedVersion).satisfies(exver_1.VersionRange.parse(dep.requirement.versionRange)));
};
const runningSatisfied = (packageId) => {
const dep = infoFor(packageId);
return dep.requirement.kind !== 'running' || dep.result.isRunning;
};
const tasksSatisfied = (packageId) => Object.entries(infoFor(packageId).result.tasks).filter(([_, t]) => t?.active && t.task.severity === 'critical').length === 0;
const healthCheckSatisfied = (packageId, healthCheckId) => {
const dep = infoFor(packageId);
if (healthCheckId &&
(dep.requirement.kind !== 'running' ||
!dep.requirement.healthChecks.includes(healthCheckId))) {
throw new Error(`Unknown HealthCheckId ${healthCheckId}`);
}
const errors = dep.requirement.kind === 'running'
? dep.requirement.healthChecks
.map((id) => [id, dep.result.healthChecks[id] ?? null])
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
.filter(([_, res]) => res?.result !== 'success')
: [];
return errors.length === 0;
};
const pkgSatisfied = (packageId) => installedSatisfied(packageId) &&
installedVersionSatisfied(packageId) &&
runningSatisfied(packageId) &&
tasksSatisfied(packageId) &&
healthCheckSatisfied(packageId);
const satisfied = (packageId) => packageId
? pkgSatisfied(packageId)
: dependencies.every((d) => pkgSatisfied(d.id));
const throwIfInstalledNotSatisfied = (packageId) => {
const dep = infoFor(packageId);
if (!dep.result.installedVersion) {
throw new Error(`${dep.result.title || packageId} is not installed`);
}
return null;
};
const throwIfInstalledVersionNotSatisfied = (packageId) => {
const dep = infoFor(packageId);
if (!dep.result.installedVersion) {
throw new Error(`${dep.result.title || packageId} is not installed`);
}
if (![dep.result.installedVersion, ...dep.result.satisfies].find((v) => exver_1.ExtendedVersion.parse(v).satisfies(exver_1.VersionRange.parse(dep.requirement.versionRange)))) {
throw new Error(`Installed version ${dep.result.installedVersion} of ${dep.result.title || packageId} does not match expected version range ${dep.requirement.versionRange}`);
}
return null;
};
const throwIfRunningNotSatisfied = (packageId) => {
const dep = infoFor(packageId);
if (dep.requirement.kind === 'running' && !dep.result.isRunning) {
throw new Error(`${dep.result.title || packageId} is not running`);
}
return null;
};
const throwIfTasksNotSatisfied = (packageId) => {
const dep = infoFor(packageId);
const reqs = Object.entries(dep.result.tasks)
.filter(([_, t]) => t?.active && t.task.severity === 'critical')
.map(([id, _]) => id);
if (reqs.length) {
throw new Error(`The following action requests have not been fulfilled: ${reqs.join(', ')}`);
}
return null;
};
const throwIfHealthNotSatisfied = (packageId, healthCheckId) => {
const dep = infoFor(packageId);
if (healthCheckId &&
(dep.requirement.kind !== 'running' ||
!dep.requirement.healthChecks.includes(healthCheckId))) {
throw new Error(`Unknown HealthCheckId ${healthCheckId}`);
}
const errors = dep.requirement.kind === 'running'
? dep.requirement.healthChecks
.map((id) => [id, dep.result.healthChecks[id] ?? null])
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
.filter(([_, res]) => res?.result !== 'success')
: [];
if (errors.length) {
throw new Error(errors
.map(([id, e]) => e
? `Health Check ${e.name} of ${dep.result.title || packageId} failed with status ${e.result}${e.message ? `: ${e.message}` : ''}`
: `Health Check ${id} of ${dep.result.title} does not exist`)
.join('; '));
}
return null;
};
const throwIfPkgNotSatisfied = (packageId) => {
throwIfInstalledNotSatisfied(packageId);
throwIfInstalledVersionNotSatisfied(packageId);
throwIfRunningNotSatisfied(packageId);
throwIfTasksNotSatisfied(packageId);
throwIfHealthNotSatisfied(packageId);
return null;
};
const throwIfNotSatisfied = (packageId) => packageId
? throwIfPkgNotSatisfied(packageId)
: (() => {
const err = dependencies.flatMap((d) => {
try {
throwIfPkgNotSatisfied(d.id);
}
catch (e) {
if (e instanceof Error)
return [e.message];
throw e;
}
return [];
});
if (err.length) {
throw new Error(err.join('; '));
}
return null;
})();
return {
infoFor,
installedSatisfied,
installedVersionSatisfied,
runningSatisfied,
tasksSatisfied,
healthCheckSatisfied,
satisfied,
throwIfInstalledNotSatisfied,
throwIfInstalledVersionNotSatisfied,
throwIfRunningNotSatisfied,
throwIfTasksNotSatisfied,
throwIfHealthNotSatisfied,
throwIfNotSatisfied,
};
}
//# sourceMappingURL=dependencies.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,22 @@
import * as T from '../types';
export type RequiredDependenciesOf<Manifest extends T.SDKManifest> = {
[K in keyof Manifest['dependencies']]: Exclude<Manifest['dependencies'][K], undefined>['optional'] extends false ? K : never;
}[keyof Manifest['dependencies']];
export type OptionalDependenciesOf<Manifest extends T.SDKManifest> = Exclude<keyof Manifest['dependencies'], RequiredDependenciesOf<Manifest>>;
type DependencyRequirement = {
kind: 'running';
healthChecks: Array<T.HealthCheckId>;
versionRange: string;
} | {
kind: 'exists';
versionRange: string;
};
export type CurrentDependenciesResult<Manifest extends T.SDKManifest> = {
[K in RequiredDependenciesOf<Manifest>]: DependencyRequirement;
} & {
[K in OptionalDependenciesOf<Manifest>]?: DependencyRequirement;
};
export declare function setupDependencies<Manifest extends T.SDKManifest>(fn: (options: {
effects: T.Effects;
}) => Promise<CurrentDependenciesResult<Manifest>>): (effects: T.Effects) => Promise<null>;
export {};
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupDependencies = setupDependencies;
const _checkType = null;
function setupDependencies(fn) {
return async (effects) => {
const dependencyType = await fn({ effects });
return await effects.setDependencies({
dependencies: Object.entries(dependencyType)
.map(([k, v]) => [k, v])
.map(([id, { versionRange, ...x }]) => ({
id,
...x,
versionRange: versionRange.toString(),
})),
});
};
}
//# sourceMappingURL=setupDependencies.js.map
@@ -0,0 +1 @@
{"version":3,"file":"setupDependencies.js","sourceRoot":"","sources":["../../../../base/lib/dependencies/setupDependencies.ts"],"names":[],"mappings":";;AAsCA,8CAoBC;AA/BD,MAAM,UAAU,GAGZ,IAAI,CAAA;AAQR,SAAgB,iBAAiB,CAC/B,EAEkD;IAElD,OAAO,KAAK,EAAE,OAAkB,EAAE,EAAE;QAClC,MAAM,cAAc,GAAG,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;QAC5C,OAAO,MAAM,OAAO,CAAC,eAAe,CAAC;YACnC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;iBACzC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAA0B,CAAU,CAAC;iBACzD,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAC/B,CAAC;gBACC,EAAE;gBACF,GAAG,CAAC;gBACJ,YAAY,EAAE,YAAY,CAAC,QAAQ,EAAE;aACtC,CAA4B,CAChC;SACJ,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
+156
View File
@@ -0,0 +1,156 @@
export interface FilePosition {
offset: number;
line: number;
column: number;
}
export interface FileRange {
start: FilePosition;
end: FilePosition;
source: string;
}
export interface LiteralExpectation {
type: "literal";
text: string;
ignoreCase: boolean;
}
export interface ClassParts extends Array<string | ClassParts> {
}
export interface ClassExpectation {
type: "class";
parts: ClassParts;
inverted: boolean;
ignoreCase: boolean;
}
export interface AnyExpectation {
type: "any";
}
export interface EndExpectation {
type: "end";
}
export interface OtherExpectation {
type: "other";
description: string;
}
export type Expectation = LiteralExpectation | ClassExpectation | AnyExpectation | EndExpectation | OtherExpectation;
declare class _PeggySyntaxError extends Error {
static buildMessage(expected: Expectation[], found: string | null): string;
message: string;
expected: Expectation[];
found: string | null;
location: FileRange;
name: string;
constructor(message: string, expected: Expectation[], found: string | null, location: FileRange);
format(sources: {
source?: any;
text: string;
}[]): string;
}
export interface TraceEvent {
type: string;
rule: string;
result?: any;
location: FileRange;
}
export interface ParseOptions {
filename?: string;
startRule?: "VersionRange" | "Or" | "And" | "VersionRangeAtom" | "Parens" | "Anchor" | "VersionSpec" | "FlavorAtom" | "Not" | "Any" | "None" | "CmpOp" | "ExtendedVersion" | "EmverVersionRange" | "EmverVersionRangeAtom" | "EmverParens" | "EmverAnchor" | "EmverNot" | "Emver" | "Flavor" | "FlavorString" | "String" | "Version" | "PreRelease" | "PreReleaseSegment" | "VersionNumber" | "Digit" | "_";
tracer?: any;
[key: string]: any;
}
export type ParseFunction = <Options extends ParseOptions>(input: string, options?: Options) => Options extends {
startRule: infer StartRule;
} ? StartRule extends "VersionRange" ? VersionRange : StartRule extends "Or" ? Or : StartRule extends "And" ? And : StartRule extends "VersionRangeAtom" ? VersionRangeAtom : StartRule extends "Parens" ? Parens : StartRule extends "Anchor" ? Anchor : StartRule extends "VersionSpec" ? VersionSpec : StartRule extends "FlavorAtom" ? FlavorAtom : StartRule extends "Not" ? Not : StartRule extends "Any" ? Any : StartRule extends "None" ? None : StartRule extends "CmpOp" ? CmpOp : StartRule extends "ExtendedVersion" ? ExtendedVersion : StartRule extends "EmverVersionRange" ? EmverVersionRange : StartRule extends "EmverVersionRangeAtom" ? EmverVersionRangeAtom : StartRule extends "EmverParens" ? EmverParens : StartRule extends "EmverAnchor" ? EmverAnchor : StartRule extends "EmverNot" ? EmverNot : StartRule extends "Emver" ? Emver : StartRule extends "Flavor" ? Flavor : StartRule extends "FlavorString" ? FlavorString : StartRule extends "String" ? String_1 : StartRule extends "Version" ? Version : StartRule extends "PreRelease" ? PreRelease : StartRule extends "PreReleaseSegment" ? PreReleaseSegment : StartRule extends "VersionNumber" ? VersionNumber : StartRule extends "Digit" ? Digit : StartRule extends "_" ? _ : VersionRange : VersionRange;
export declare const parse: ParseFunction;
export declare const PeggySyntaxError: typeof _PeggySyntaxError;
export type PeggySyntaxError = _PeggySyntaxError;
export type VersionRange = [
VersionRangeAtom,
[
_,
[Or | And, _] | null,
VersionRangeAtom
][]
];
export type Or = "||";
export type And = "&&";
export type VersionRangeAtom = Parens | Anchor | Not | Any | None | FlavorAtom;
export type Parens = {
type: "Parens";
expr: VersionRange;
};
export type Anchor = {
type: "Anchor";
operator: CmpOp | null;
version: VersionSpec;
};
export type VersionSpec = {
flavor: NonNullable<Flavor | null> | null;
upstream: Version;
downstream: any;
};
export type FlavorAtom = {
type: "Flavor";
flavor: FlavorString;
};
export type Not = {
type: "Not";
value: VersionRangeAtom;
};
export type Any = {
type: "Any";
};
export type None = {
type: "None";
};
export type CmpOp = ">=" | "<=" | ">" | "<" | "=" | "!=" | "^" | "~";
export type ExtendedVersion = {
flavor: NonNullable<Flavor | null> | null;
upstream: Version;
downstream: Version;
};
export type EmverVersionRange = [
EmverVersionRangeAtom,
[
_,
[Or | And, _] | null,
EmverVersionRangeAtom
][]
];
export type EmverVersionRangeAtom = EmverParens | EmverAnchor | EmverNot | Any | None;
export type EmverParens = {
type: "Parens";
expr: EmverVersionRange;
};
export type EmverAnchor = {
type: "Anchor";
operator: CmpOp | null;
version: Emver;
};
export type EmverNot = {
type: "Not";
value: EmverVersionRangeAtom;
};
export type Emver = {
flavor: null;
upstream: {
number: [Digit, Digit, Digit];
prerelease: [];
};
downstream: {
number: [0 | NonNullable<Digit | null>];
prerelease: [];
};
};
export type Flavor = FlavorString;
export type FlavorString = string;
export type String_1 = string;
export type Version = {
number: VersionNumber;
prerelease: never[] | NonNullable<PreRelease | null>;
};
export type PreRelease = PreReleaseSegment[];
export type PreReleaseSegment = Digit | String_1;
export type VersionNumber = Digit[];
export type Digit = number;
export type _ = string[];
export {};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+389
View File
@@ -0,0 +1,389 @@
import { DeepMap } from 'deep-equality-data-structures';
import * as P from './exver';
/**
* Compile-time utility type that validates a version string literal conforms to semver format.
*
* Resolves to `unknown` if valid, `never` if invalid. Used with {@link testTypeVersion}.
*
* @example
* ```ts
* type Valid = ValidateVersion<"1.2.3"> // unknown (valid)
* type Invalid = ValidateVersion<"-3"> // never (invalid)
* ```
*/
export type ValidateVersion<T extends String> = T extends `-${infer A}` ? never : T extends `${infer A}-${string}` ? ValidateVersion<A> : T extends `${bigint}` ? unknown : T extends `${bigint}.${infer A}` ? ValidateVersion<A> : never;
/**
* Compile-time utility type that validates an extended version string literal.
*
* Extended versions have the format `upstream:downstream` or `#flavor:upstream:downstream`.
*
* @example
* ```ts
* type Valid = ValidateExVer<"1.2.3:0"> // valid
* type Flavored = ValidateExVer<"#bitcoin:1.0:0"> // valid
* type Bad = ValidateExVer<"1.2-3"> // never (invalid)
* ```
*/
export type ValidateExVer<T extends string> = T extends `#${string}:${infer A}:${infer B}` ? ValidateVersion<A> & ValidateVersion<B> : T extends `${infer A}:${infer B}` ? ValidateVersion<A> & ValidateVersion<B> : never;
/**
* Validates a tuple of extended version string literals at compile time.
*
* @example
* ```ts
* type Valid = ValidateExVers<["1.0:0", "2.0:0"]> // valid
* ```
*/
export type ValidateExVers<T> = T extends [] ? unknown[] : T extends [infer A, ...infer B] ? ValidateExVer<A & string> & ValidateExVers<B> : never[];
type Anchor = {
type: 'Anchor';
operator: P.CmpOp;
version: ExtendedVersion;
};
type And = {
type: 'And';
left: VersionRange;
right: VersionRange;
};
type Or = {
type: 'Or';
left: VersionRange;
right: VersionRange;
};
type Not = {
type: 'Not';
value: VersionRange;
};
type Flavor = {
type: 'Flavor';
flavor: string | null;
};
type FlavorNot = {
type: 'FlavorNot';
flavors: Set<string | null>;
};
type FlavorAtom = Flavor | FlavorNot;
/**
* Splits a number line of versions in half, so that every possible semver is either to the left or right.
* The `side` field handles inclusively.
*
* # Example
* Consider the version `1.2.3`. For side=-1 the version point is like `1.2.2.999*.999*.**` (that is, 1.2.3.0.0.** is greater) and
* for side=+1 the point is like `1.2.3.0.0.**.1` (that is, 1.2.3.0.0.** is less).
*/
type VersionRangePoint = {
upstream: Version;
downstream: Version;
side: -1 | 1;
};
/**
* Truth tables for version numbers and flavors. For each flavor we need a separate table, which
* is quite straightforward. But in order to exhaustively enumerate the boolean values of every
* combination of flavors and versions we also need tables for flavor negations.
*/
type VersionRangeTables = DeepMap<FlavorAtom, VersionRangeTable> | boolean;
/**
* A truth table for version numbers. This is easiest to picture as a number line, cut up into
* ranges of versions between version points.
*/
declare class VersionRangeTable {
protected points: Array<VersionRangePoint>;
protected values: boolean[];
private constructor();
static zip(a: VersionRangeTable, b: VersionRangeTable, func: (a: boolean, b: boolean) => boolean): VersionRangeTable;
/**
* Creates a version table which is `true` for the given flavor, and `false` for any other flavor.
*/
static eqFlavor(flavor: string | null): VersionRangeTables;
/**
* Creates a version table with exactly two ranges (to the left and right of the given point) and with `false` for any other flavor.
* This is easiest to understand by looking at `VersionRange.tables`.
*/
static cmpPoint(flavor: string | null, point: VersionRangePoint, left: boolean, right: boolean): VersionRangeTables;
/**
* Helper for `cmpPoint`.
*/
static cmp(version: ExtendedVersion, side: -1 | 1, left: boolean, right: boolean): VersionRangeTables;
static not(tables: VersionRangeTables): boolean | DeepMap<FlavorAtom, VersionRangeTable, FlavorAtom, VersionRangeTable>;
static and(a_tables: VersionRangeTables, b_tables: VersionRangeTables): VersionRangeTables;
static or(...in_tables: VersionRangeTables[]): VersionRangeTables;
/**
* If this is true for all versions or false for all versions, returen that value. Otherwise return null.
*/
static collapse(tables: VersionRangeTables): boolean | null;
/**
* Expresses this truth table as a series of version range operators.
* https://en.wikipedia.org/wiki/Canonical_normal_form#Minterms
*/
static minterms(tables: VersionRangeTables): VersionRange;
}
/**
* Represents a parsed version range expression used to match against {@link Version} or {@link ExtendedVersion} values.
*
* Version ranges support standard comparison operators (`=`, `>`, `<`, `>=`, `<=`, `!=`),
* caret (`^`) and tilde (`~`) ranges, boolean logic (`&&`, `||`, `!`), and flavor matching (`#flavor`).
*
* @example
* ```ts
* const range = VersionRange.parse(">=1.0.0:0 && <2.0.0:0")
* const version = ExtendedVersion.parse("1.5.0:0")
* console.log(range.satisfiedBy(version)) // true
*
* // Combine ranges with boolean logic
* const combined = VersionRange.and(
* VersionRange.parse(">=1.0:0"),
* VersionRange.parse("<3.0:0"),
* )
*
* // Match a specific flavor
* const flavored = VersionRange.parse("#bitcoin")
* ```
*/
export declare class VersionRange {
atom: Anchor | And | Or | Not | P.Any | P.None | Flavor;
constructor(atom: Anchor | And | Or | Not | P.Any | P.None | Flavor);
toStringParens(parent: 'And' | 'Or' | 'Not'): string;
/** Serializes this version range back to its canonical string representation. */
toString(): string;
private static parseAtom;
private static parseRange;
/**
* Parses a version range string into a `VersionRange`.
*
* @param range - A version range expression, e.g. `">=1.0.0:0 && <2.0.0:0"`, `"^1.2:0"`, `"*"`
* @returns The parsed `VersionRange`
* @throws If the string is not a valid version range expression
*/
static parse(range: string): VersionRange;
/**
* Creates a version range from a comparison operator and an {@link ExtendedVersion}.
*
* @param operator - One of `"="`, `">"`, `"<"`, `">="`, `"<="`, `"!="`, `"^"`, `"~"`
* @param version - The version to compare against
*/
static anchor(operator: P.CmpOp, version: ExtendedVersion): VersionRange;
/**
* Creates a version range that matches only versions with the specified flavor.
*
* @param flavor - The flavor string to match, or `null` for the default (unflavored) variant
*/
static flavor(flavor: string | null): VersionRange;
/**
* Parses a legacy "emver" format version range string.
*
* @param range - A version range in the legacy emver format
* @returns The parsed `VersionRange`
*/
static parseEmver(range: string): VersionRange;
/** Returns the intersection of this range with another (logical AND). */
and(right: VersionRange): VersionRange;
/** Returns the union of this range with another (logical OR). */
or(right: VersionRange): VersionRange;
/** Returns the negation of this range (logical NOT). */
not(): VersionRange;
/**
* Returns the logical AND (intersection) of multiple version ranges.
* Short-circuits on `none()` and skips `any()`.
*/
static and(...xs: Array<VersionRange>): VersionRange;
/**
* Returns the logical OR (union) of multiple version ranges.
* Short-circuits on `any()` and skips `none()`.
*/
static or(...xs: Array<VersionRange>): VersionRange;
/** Returns a version range that matches all versions (wildcard `*`). */
static any(): VersionRange;
/** Returns a version range that matches no versions (`!`). */
static none(): VersionRange;
/**
* Returns `true` if the given version satisfies this range.
*
* @param version - A {@link Version} or {@link ExtendedVersion} to test
*/
satisfiedBy(version: Version | ExtendedVersion): boolean;
tables(): VersionRangeTables;
/** Returns `true` if any version exists that could satisfy this range. */
satisfiable(): boolean;
/** Returns `true` if this range and `other` share at least one satisfying version. */
intersects(other: VersionRange): boolean;
/**
* Returns a canonical (simplified) form of this range using minterm expansion.
* Useful for normalizing complex boolean expressions into a minimal representation.
*/
normalize(): VersionRange;
}
/**
* Represents a semantic version number with numeric segments and optional prerelease identifiers.
*
* Follows semver precedence rules: numeric segments are compared left-to-right,
* and a version with prerelease identifiers has lower precedence than the same version without.
*
* @example
* ```ts
* const v = Version.parse("1.2.3")
* console.log(v.toString()) // "1.2.3"
* console.log(v.compare(Version.parse("1.3.0"))) // "less"
*
* const pre = Version.parse("2.0.0-beta.1")
* console.log(pre.compare(Version.parse("2.0.0"))) // "less" (prerelease < release)
* ```
*/
export declare class Version {
/** The numeric version segments (e.g. `[1, 2, 3]` for `"1.2.3"`). */
number: number[];
/** Optional prerelease identifiers (e.g. `["beta", 1]` for `"-beta.1"`). */
prerelease: (string | number)[];
constructor(
/** The numeric version segments (e.g. `[1, 2, 3]` for `"1.2.3"`). */
number: number[],
/** Optional prerelease identifiers (e.g. `["beta", 1]` for `"-beta.1"`). */
prerelease: (string | number)[]);
/** Serializes this version to its string form (e.g. `"1.2.3"` or `"1.0.0-beta.1"`). */
toString(): string;
/**
* Compares this version against another using semver precedence rules.
*
* @param other - The version to compare against
* @returns `'greater'`, `'equal'`, or `'less'`
*/
compare(other: Version): 'greater' | 'equal' | 'less';
/**
* Compares two versions, returning a numeric value suitable for use with `Array.sort()`.
*
* @returns `-1` if less, `0` if equal, `1` if greater
*/
compareForSort(other: Version): -1 | 0 | 1;
/**
* Parses a version string into a `Version` instance.
*
* @param version - A semver-compatible string, e.g. `"1.2.3"` or `"1.0.0-beta.1"`
* @throws If the string is not a valid version
*/
static parse(version: string): Version;
/**
* Returns `true` if this version satisfies the given {@link VersionRange}.
* Internally treats this as an unflavored {@link ExtendedVersion} with downstream `0`.
*/
satisfies(versionRange: VersionRange): boolean;
}
/**
* Represents an extended version with an optional flavor, an upstream version, and a downstream version.
*
* The format is `#flavor:upstream:downstream` (e.g. `#bitcoin:1.2.3:0`) or `upstream:downstream`
* for unflavored versions. Flavors allow multiple variants of a package to coexist.
*
* - **flavor**: An optional string identifier for the variant (e.g. `"bitcoin"`, `"litecoin"`)
* - **upstream**: The version of the upstream software being packaged
* - **downstream**: The version of the StartOS packaging itself
*
* Versions with different flavors are incomparable (comparison returns `null`).
*
* @example
* ```ts
* const v = ExtendedVersion.parse("#bitcoin:1.2.3:0")
* console.log(v.flavor) // "bitcoin"
* console.log(v.upstream) // Version { number: [1, 2, 3] }
* console.log(v.downstream) // Version { number: [0] }
* console.log(v.toString()) // "#bitcoin:1.2.3:0"
*
* const range = VersionRange.parse(">=1.0.0:0")
* console.log(v.satisfies(range)) // true
* ```
*/
export declare class ExtendedVersion {
/** The flavor identifier (e.g. `"bitcoin"`), or `null` for unflavored versions. */
flavor: string | null;
/** The upstream software version. */
upstream: Version;
/** The downstream packaging version. */
downstream: Version;
constructor(
/** The flavor identifier (e.g. `"bitcoin"`), or `null` for unflavored versions. */
flavor: string | null,
/** The upstream software version. */
upstream: Version,
/** The downstream packaging version. */
downstream: Version);
/** Serializes this extended version to its string form (e.g. `"#bitcoin:1.2.3:0"` or `"1.0.0:1"`). */
toString(): string;
/**
* Compares this extended version against another.
*
* @returns `'greater'`, `'equal'`, `'less'`, or `null` if the flavors differ (incomparable)
*/
compare(other: ExtendedVersion): 'greater' | 'equal' | 'less' | null;
/**
* Lexicographic comparison — compares flavors alphabetically first, then versions.
* Unlike {@link compare}, this never returns `null`: different flavors are ordered alphabetically.
*/
compareLexicographic(other: ExtendedVersion): 'greater' | 'equal' | 'less';
/**
* Returns a numeric comparison result suitable for use with `Array.sort()`.
* Uses lexicographic ordering (flavors sorted alphabetically, then by version).
*/
compareForSort(other: ExtendedVersion): 1 | 0 | -1;
/** Returns `true` if this version is strictly greater than `other`. Returns `false` if flavors differ. */
greaterThan(other: ExtendedVersion): boolean;
/** Returns `true` if this version is greater than or equal to `other`. Returns `false` if flavors differ. */
greaterThanOrEqual(other: ExtendedVersion): boolean;
/** Returns `true` if this version equals `other` (same flavor, upstream, and downstream). */
equals(other: ExtendedVersion): boolean;
/** Returns `true` if this version is strictly less than `other`. Returns `false` if flavors differ. */
lessThan(other: ExtendedVersion): boolean;
/** Returns `true` if this version is less than or equal to `other`. Returns `false` if flavors differ. */
lessThanOrEqual(other: ExtendedVersion): boolean;
/**
* Parses an extended version string into an `ExtendedVersion`.
*
* @param extendedVersion - A string like `"1.2.3:0"` or `"#bitcoin:1.0.0:0"`
* @throws If the string is not a valid extended version
*/
static parse(extendedVersion: string): ExtendedVersion;
/**
* Parses a legacy "emver" format extended version string.
*
* @param extendedVersion - A version string in the legacy emver format
* @throws If the string is not a valid emver version (error message includes the input string)
*/
static parseEmver(extendedVersion: string): ExtendedVersion;
/**
* Returns an ExtendedVersion with the Upstream major version version incremented by 1
* and sets subsequent digits to zero.
* If no non-zero upstream digit can be found the last upstream digit will be incremented.
*/
incrementMajor(): ExtendedVersion;
/**
* Returns an ExtendedVersion with the Upstream minor version version incremented by 1
* also sets subsequent digits to zero.
* If no non-zero upstream digit can be found the last digit will be incremented.
*/
incrementMinor(): ExtendedVersion;
/**
* Returns a boolean indicating whether a given version satisfies the VersionRange
* !( >= 1:1 <= 2:2) || <=#bitcoin:1.2.0-alpha:0
*/
satisfies(versionRange: VersionRange): boolean;
}
/**
* Compile-time type-checking helper that validates an extended version string literal.
* If the string is invalid, TypeScript will report a type error at the call site.
*
* @example
* ```ts
* testTypeExVer("1.2.3:0") // compiles
* testTypeExVer("#bitcoin:1.0:0") // compiles
* testTypeExVer("invalid") // type error
* ```
*/
export declare const testTypeExVer: <T extends string>(t: T & ValidateExVer<T>) => T & ValidateExVer<T>;
/**
* Compile-time type-checking helper that validates a version string literal.
* If the string is invalid, TypeScript will report a type error at the call site.
*
* @example
* ```ts
* testTypeVersion("1.2.3") // compiles
* testTypeVersion("-3") // type error
* ```
*/
export declare const testTypeVersion: <T extends string>(t: T & ValidateVersion<T>) => T & ValidateVersion<T>;
export {};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
export { S9pk } from './s9pk';
export { VersionRange, ExtendedVersion, Version } from './exver';
export * as inputSpec from './actions/input';
export * as ISB from './actions/input/builder';
export * as IST from './actions/input/inputSpecTypes';
export * as types from './types';
export * as T from './types';
export * as yaml from 'yaml';
export * as inits from './inits';
export { z } from './zExport';
export * as utils from './util';
+53
View File
@@ -0,0 +1,53 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.utils = exports.z = exports.inits = exports.yaml = exports.T = exports.types = exports.IST = exports.ISB = exports.inputSpec = exports.Version = exports.ExtendedVersion = exports.VersionRange = exports.S9pk = void 0;
var s9pk_1 = require("./s9pk");
Object.defineProperty(exports, "S9pk", { enumerable: true, get: function () { return s9pk_1.S9pk; } });
var exver_1 = require("./exver");
Object.defineProperty(exports, "VersionRange", { enumerable: true, get: function () { return exver_1.VersionRange; } });
Object.defineProperty(exports, "ExtendedVersion", { enumerable: true, get: function () { return exver_1.ExtendedVersion; } });
Object.defineProperty(exports, "Version", { enumerable: true, get: function () { return exver_1.Version; } });
exports.inputSpec = __importStar(require("./actions/input"));
exports.ISB = __importStar(require("./actions/input/builder"));
exports.IST = __importStar(require("./actions/input/inputSpecTypes"));
exports.types = __importStar(require("./types"));
exports.T = __importStar(require("./types"));
exports.yaml = __importStar(require("yaml"));
exports.inits = __importStar(require("./inits"));
var zExport_1 = require("./zExport");
Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zExport_1.z; } });
exports.utils = __importStar(require("./util"));
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../base/lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+BAA6B;AAApB,4FAAA,IAAI,OAAA;AACb,iCAAgE;AAAvD,qGAAA,YAAY,OAAA;AAAE,wGAAA,eAAe,OAAA;AAAE,gGAAA,OAAO,OAAA;AAE/C,6DAA4C;AAC5C,+DAA8C;AAC9C,sEAAqD;AACrD,iDAAgC;AAChC,6CAA4B;AAC5B,6CAA4B;AAC5B,iDAAgC;AAChC,qCAA6B;AAApB,4FAAA,CAAC,OAAA;AAEV,gDAA+B"}
+2
View File
@@ -0,0 +1,2 @@
export * from './setupInit';
export * from './setupUninit';
+19
View File
@@ -0,0 +1,19 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./setupInit"), exports);
__exportStar(require("./setupUninit"), exports);
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../base/lib/inits/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B;AAC3B,gDAA6B"}
+26
View File
@@ -0,0 +1,26 @@
import * as T from '../../../base/lib/types';
/**
* The reason a service's init function is being called:
* - `'install'` — first-time installation
* - `'update'` — after a package update
* - `'restore'` — after restoring from backup
* - `null` — regular startup (no special lifecycle event)
*/
export type InitKind = 'install' | 'update' | 'restore' | null;
/** Function signature for an init handler that runs during service startup. */
export type InitFn<Kind extends InitKind = InitKind> = (effects: T.Effects, kind: Kind) => Promise<void | null | undefined>;
/** Object form of an init handler — implements an `init()` method. */
export interface InitScript<Kind extends InitKind = InitKind> {
init(effects: T.Effects, kind: Kind): Promise<void>;
}
/** Either an {@link InitScript} object or an {@link InitFn} function. */
export type InitScriptOrFn<Kind extends InitKind = InitKind> = InitScript<Kind> | InitFn<Kind>;
/**
* Composes multiple init handlers into a single `ExpectedExports.init`-compatible function.
* Handlers are executed sequentially in the order provided.
*
* @param inits - One or more init handlers to compose
*/
export declare function setupInit(...inits: InitScriptOrFn[]): T.ExpectedExports.init;
/** Normalizes an {@link InitScriptOrFn} into an {@link InitScript} object. */
export declare function setupOnInit(onInit: InitScriptOrFn): InitScript;
+47
View File
@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupInit = setupInit;
exports.setupOnInit = setupOnInit;
const util_1 = require("../util");
/**
* Composes multiple init handlers into a single `ExpectedExports.init`-compatible function.
* Handlers are executed sequentially in the order provided.
*
* @param inits - One or more init handlers to compose
*/
function setupInit(...inits) {
return async (opts) => {
for (const idx in inits) {
const init = inits[idx];
const fn = async () => {
let res = () => { };
const complete = new Promise((resolve) => {
res = resolve;
});
const e = opts.effects.child(`init_${idx}`);
e.constRetry = (0, util_1.once)(() => complete.then(() => fn()).catch(console.error));
try {
if ('init' in init)
await init.init(e, opts.kind);
else
await init(e, opts.kind);
}
finally {
res();
}
};
await fn();
}
};
}
/** Normalizes an {@link InitScriptOrFn} into an {@link InitScript} object. */
function setupOnInit(onInit) {
return 'init' in onInit
? onInit
: {
init: async (effects, kind) => {
await onInit(effects, kind);
},
};
}
//# sourceMappingURL=setupInit.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"setupInit.js","sourceRoot":"","sources":["../../../../base/lib/inits/setupInit.ts"],"names":[],"mappings":";;AAmCA,8BAuBC;AAGD,kCAQC;AAnED,kCAA8B;AA2B9B;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,GAAG,KAAuB;IAClD,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;QACpB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;YACvB,MAAM,EAAE,GAAG,KAAK,IAAI,EAAE;gBACpB,IAAI,GAAG,GAAgC,GAAG,EAAE,GAAE,CAAC,CAAA;gBAC/C,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;oBACvC,GAAG,GAAG,OAAO,CAAA;gBACf,CAAC,CAAC,CAAA;gBACF,MAAM,CAAC,GAAc,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAA;gBACtD,CAAC,CAAC,UAAU,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,CACvB,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAC/C,CAAA;gBACD,IAAI,CAAC;oBACH,IAAI,MAAM,IAAI,IAAI;wBAAE,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;;wBAC5C,MAAM,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC/B,CAAC;wBAAS,CAAC;oBACT,GAAG,EAAE,CAAA;gBACP,CAAC;YACH,CAAC,CAAA;YACD,MAAM,EAAE,EAAE,CAAA;QACZ,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,SAAgB,WAAW,CAAC,MAAsB;IAChD,OAAO,MAAM,IAAI,MAAM;QACrB,CAAC,CAAC,MAAM;QACR,CAAC,CAAC;YACE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;gBAC5B,MAAM,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YAC7B,CAAC;SACF,CAAA;AACP,CAAC"}
+37
View File
@@ -0,0 +1,37 @@
import { ExtendedVersion, VersionRange } from '../../../base/lib/exver';
import * as T from '../../../base/lib/types';
/**
* Function signature for an uninit handler that runs during service shutdown/uninstall.
*/
export type UninitFn = (effects: T.Effects,
/**
* @description the target version to prepare for
*
* on update: the canMigrateFrom of the new package
* on uninstall: null
* on shutdown: the current version
*/
target: VersionRange | ExtendedVersion | null) => Promise<void | null | undefined>;
/** Object form of an uninit handler — implements an `uninit()` method. */
export interface UninitScript {
uninit(effects: T.Effects,
/**
* @description the target version to prepare for
*
* on update: the canMigrateFrom of the new package
* on uninstall: null
* on shutdown: the current version
*/
target: VersionRange | ExtendedVersion | null): Promise<void>;
}
/** Either a {@link UninitScript} object or a {@link UninitFn} function. */
export type UninitScriptOrFn = UninitScript | UninitFn;
/**
* Composes multiple uninit handlers into a single `ExpectedExports.uninit`-compatible function.
* Handlers are executed sequentially in the order provided.
*
* @param uninits - One or more uninit handlers to compose
*/
export declare function setupUninit(...uninits: UninitScriptOrFn[]): T.ExpectedExports.uninit;
/** Normalizes a {@link UninitScriptOrFn} into a {@link UninitScript} object. */
export declare function setupOnUninit(onUninit: UninitScriptOrFn): UninitScript;
+31
View File
@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupUninit = setupUninit;
exports.setupOnUninit = setupOnUninit;
/**
* Composes multiple uninit handlers into a single `ExpectedExports.uninit`-compatible function.
* Handlers are executed sequentially in the order provided.
*
* @param uninits - One or more uninit handlers to compose
*/
function setupUninit(...uninits) {
return async (opts) => {
for (const uninit of uninits) {
if ('uninit' in uninit)
await uninit.uninit(opts.effects, opts.target);
else
await uninit(opts.effects, opts.target);
}
};
}
/** Normalizes a {@link UninitScriptOrFn} into a {@link UninitScript} object. */
function setupOnUninit(onUninit) {
return 'uninit' in onUninit
? onUninit
: {
uninit: async (effects, target) => {
await onUninit(effects, target);
},
};
}
//# sourceMappingURL=setupUninit.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"setupUninit.js","sourceRoot":"","sources":["../../../../base/lib/inits/setupUninit.ts"],"names":[],"mappings":";;AA0CA,kCASC;AAGD,sCAQC;AA1BD;;;;;GAKG;AACH,SAAgB,WAAW,CACzB,GAAG,OAA2B;IAE9B,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;QACpB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,QAAQ,IAAI,MAAM;gBAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;;gBACjE,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAC9C,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED,gFAAgF;AAChF,SAAgB,aAAa,CAAC,QAA0B;IACtD,OAAO,QAAQ,IAAI,QAAQ;QACzB,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC;YACE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;gBAChC,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YACjC,CAAC;SACF,CAAA;AACP,CAAC"}
@@ -0,0 +1,5 @@
declare const AddressProof: unique symbol;
export type AddressReceipt = {
[AddressProof]: never;
};
export {};
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=AddressReceipt.js.map
@@ -0,0 +1 @@
{"version":3,"file":"AddressReceipt.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/AddressReceipt.ts"],"names":[],"mappings":""}
+101
View File
@@ -0,0 +1,101 @@
import { Effects } from '../Effects';
import { Origin } from './Origin';
import { AddSslOptions } from '../osBindings';
import { Security } from '../osBindings';
import { BindOptions } from '../osBindings';
import { AlpnInfo } from '../osBindings';
export { AddSslOptions, Security, BindOptions };
export declare const knownProtocols: {
readonly http: {
readonly secure: null;
readonly defaultPort: 80;
readonly withSsl: "https";
readonly alpn: AlpnInfo;
readonly addXForwardedHeaders: true;
};
readonly https: {
readonly secure: {
readonly ssl: true;
};
readonly defaultPort: 443;
readonly addXForwardedHeaders: true;
};
readonly ws: {
readonly secure: null;
readonly defaultPort: 80;
readonly withSsl: "wss";
readonly alpn: AlpnInfo;
readonly addXForwardedHeaders: true;
};
readonly wss: {
readonly secure: {
readonly ssl: true;
};
readonly defaultPort: 443;
readonly addXForwardedHeaders: true;
};
readonly ssh: {
readonly secure: {
readonly ssl: false;
};
readonly defaultPort: 22;
readonly addXForwardedHeaders: false;
};
readonly dns: {
readonly secure: {
readonly ssl: false;
};
readonly defaultPort: 53;
readonly addXForwardedHeaders: false;
};
};
export type Scheme = string | null;
type KnownProtocols = typeof knownProtocols;
type ProtocolsWithSslVariants = {
[K in keyof KnownProtocols]: KnownProtocols[K] extends {
withSsl: string;
} ? K : never;
}[keyof KnownProtocols];
type NotProtocolsWithSslVariants = Exclude<keyof KnownProtocols, ProtocolsWithSslVariants>;
type BindOptionsByKnownProtocol = {
protocol: ProtocolsWithSslVariants;
preferredExternalPort?: number;
addSsl?: Partial<AddSslOptions>;
} | {
protocol: NotProtocolsWithSslVariants;
preferredExternalPort?: number;
addSsl?: AddSslOptions;
};
export type BindOptionsByProtocol = BindOptionsByKnownProtocol | (BindOptions & {
protocol: null;
});
export declare class MultiHost {
readonly options: {
effects: Effects;
id: string;
};
constructor(options: {
effects: Effects;
id: string;
});
/**
* @description Use this function to bind the host to an internal port and configured options for protocol, security, and external port.
*
* @param internalPort - The internal port to be bound.
* @param options - The protocol options for this binding.
* @returns A multi-origin that is capable of exporting one or more service interfaces.
* @example
* In this example, we bind a previously created multi-host to port 80, then select the http protocol and request an external port of 8332.
*
* ```
const uiMultiOrigin = await uiMulti.bindPort(80, {
protocol: 'http',
preferredExternalPort: 8332,
})
* ```
*/
bindPort(internalPort: number, options: BindOptionsByProtocol): Promise<Origin>;
private bindPortForUnknown;
private bindPortForKnown;
private getSslProto;
}
+128
View File
@@ -0,0 +1,128 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiHost = exports.knownProtocols = void 0;
const zod_1 = require("zod");
const Origin_1 = require("./Origin");
exports.knownProtocols = {
http: {
secure: null,
defaultPort: 80,
withSsl: 'https',
alpn: { specified: ['http/1.1'] },
addXForwardedHeaders: true,
},
https: {
secure: { ssl: true },
defaultPort: 443,
addXForwardedHeaders: true,
},
ws: {
secure: null,
defaultPort: 80,
withSsl: 'wss',
alpn: { specified: ['http/1.1'] },
addXForwardedHeaders: true,
},
wss: {
secure: { ssl: true },
defaultPort: 443,
addXForwardedHeaders: true,
},
ssh: {
secure: { ssl: false },
defaultPort: 22,
addXForwardedHeaders: false,
},
dns: {
secure: { ssl: false },
defaultPort: 53,
addXForwardedHeaders: false,
},
};
const hasStringProtocol = (v) => zod_1.z.object({ protocol: zod_1.z.string() }).safeParse(v).success;
class MultiHost {
constructor(options) {
this.options = options;
}
/**
* @description Use this function to bind the host to an internal port and configured options for protocol, security, and external port.
*
* @param internalPort - The internal port to be bound.
* @param options - The protocol options for this binding.
* @returns A multi-origin that is capable of exporting one or more service interfaces.
* @example
* In this example, we bind a previously created multi-host to port 80, then select the http protocol and request an external port of 8332.
*
* ```
const uiMultiOrigin = await uiMulti.bindPort(80, {
protocol: 'http',
preferredExternalPort: 8332,
})
* ```
*/
async bindPort(internalPort, options) {
if (hasStringProtocol(options)) {
return await this.bindPortForKnown(options, internalPort);
}
else {
return await this.bindPortForUnknown(internalPort, options);
}
}
async bindPortForUnknown(internalPort, options) {
const binderOptions = {
id: this.options.id,
internalPort,
...options,
};
await this.options.effects.bind(binderOptions);
return new Origin_1.Origin(this, internalPort, null, null);
}
async bindPortForKnown(options, internalPort) {
const protoInfo = exports.knownProtocols[options.protocol];
const preferredExternalPort = options.preferredExternalPort ||
exports.knownProtocols[options.protocol].defaultPort;
const sslProto = this.getSslProto(options);
const addSsl = sslProto
? {
addXForwardedHeaders: exports.knownProtocols[sslProto].addXForwardedHeaders,
preferredExternalPort: exports.knownProtocols[sslProto].defaultPort,
scheme: sslProto,
alpn: 'alpn' in protoInfo ? protoInfo.alpn : null,
...('addSsl' in options ? options.addSsl : null),
}
: options.addSsl
? {
addXForwardedHeaders: false,
preferredExternalPort: 443,
scheme: sslProto,
alpn: null,
...options.addSsl,
}
: null;
const secure = protoInfo.secure ?? null;
await this.options.effects.bind({
id: this.options.id,
internalPort,
preferredExternalPort,
addSsl,
secure,
});
return new Origin_1.Origin(this, internalPort, options.protocol, sslProto);
}
getSslProto(options) {
const proto = options.protocol;
const protoInfo = exports.knownProtocols[proto];
if (inObject('noAddSsl', options) && options.noAddSsl)
return null;
if ('withSsl' in protoInfo && protoInfo.withSsl)
return protoInfo.withSsl;
if (protoInfo.secure?.ssl)
return proto;
return null;
}
}
exports.MultiHost = MultiHost;
function inObject(key, obj) {
return key in obj;
}
//# sourceMappingURL=Host.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Host.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/Host.ts"],"names":[],"mappings":"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"}
+31
View File
@@ -0,0 +1,31 @@
import { AddressInfo } from '../types';
import { AddressReceipt } from './AddressReceipt';
import { MultiHost, Scheme } from './Host';
import { ServiceInterfaceBuilder } from './ServiceInterfaceBuilder';
export declare class Origin {
readonly host: MultiHost;
readonly internalPort: number;
readonly scheme: string | null;
readonly sslScheme: string | null;
constructor(host: MultiHost, internalPort: number, scheme: string | null, sslScheme: string | null);
build({ username, path, query: search, schemeOverride, }: BuildOptions): AddressInfo;
/**
* @description A function to register a group of origins (<PROTOCOL> :// <HOSTNAME> : <PORT>) with StartOS
*
* The returned addressReceipt serves as proof that the addresses were registered
*
* @param addressInfo
* @returns
*/
export(serviceInterfaces: ServiceInterfaceBuilder[]): Promise<AddressInfo[] & AddressReceipt>;
}
type BuildOptions = {
schemeOverride: {
ssl: Scheme;
noSsl: Scheme;
} | null;
username: string | null;
path: string;
query: Record<string, string>;
};
export {};
+57
View File
@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Origin = void 0;
class Origin {
constructor(host, internalPort, scheme, sslScheme) {
this.host = host;
this.internalPort = internalPort;
this.scheme = scheme;
this.sslScheme = sslScheme;
}
build({ username, path, query: search, schemeOverride, }) {
const qpEntries = Object.entries(search)
.map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`)
.join('&');
const qp = qpEntries.length ? `?${qpEntries}` : '';
return {
hostId: this.host.options.id,
internalPort: this.internalPort,
scheme: schemeOverride ? schemeOverride.noSsl : this.scheme,
sslScheme: schemeOverride ? schemeOverride.ssl : this.sslScheme,
suffix: `${path}${qp}`,
username,
};
}
/**
* @description A function to register a group of origins (<PROTOCOL> :// <HOSTNAME> : <PORT>) with StartOS
*
* The returned addressReceipt serves as proof that the addresses were registered
*
* @param addressInfo
* @returns
*/
async export(serviceInterfaces) {
const addressesInfo = [];
for (let serviceInterface of serviceInterfaces) {
const { name, description, id, type, username, path, query: search, schemeOverride, masked, } = serviceInterface.options;
const addressInfo = this.build({
username,
path,
query: search,
schemeOverride,
});
await serviceInterface.options.effects.exportServiceInterface({
id,
name,
description,
addressInfo,
type,
masked,
});
addressesInfo.push(addressInfo);
}
return addressesInfo;
}
}
exports.Origin = Origin;
//# sourceMappingURL=Origin.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Origin.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/Origin.ts"],"names":[],"mappings":";;;AAKA,MAAa,MAAM;IACjB,YACW,IAAe,EACf,YAAoB,EACpB,MAAqB,EACrB,SAAwB;QAHxB,SAAI,GAAJ,IAAI,CAAW;QACf,iBAAY,GAAZ,YAAY,CAAQ;QACpB,WAAM,GAAN,MAAM,CAAe;QACrB,cAAS,GAAT,SAAS,CAAe;IAChC,CAAC;IAEJ,KAAK,CAAC,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EAAE,MAAM,EACb,cAAc,GACD;QACb,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;aACrC,GAAG,CACF,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CACxE;aACA,IAAI,CAAC,GAAG,CAAC,CAAA;QAEZ,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAElD,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAC5B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;YAC3D,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS;YAC/D,MAAM,EAAE,GAAG,IAAI,GAAG,EAAE,EAAE;YACtB,QAAQ;SACT,CAAA;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,CACV,iBAA4C;QAE5C,MAAM,aAAa,GAAG,EAAE,CAAA;QACxB,KAAK,IAAI,gBAAgB,IAAI,iBAAiB,EAAE,CAAC;YAC/C,MAAM,EACJ,IAAI,EACJ,WAAW,EACX,EAAE,EACF,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EAAE,MAAM,EACb,cAAc,EACd,MAAM,GACP,GAAG,gBAAgB,CAAC,OAAO,CAAA;YAE5B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;gBAC7B,QAAQ;gBACR,IAAI;gBACJ,KAAK,EAAE,MAAM;gBACb,cAAc;aACf,CAAC,CAAA;YAEF,MAAM,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC;gBAC5D,EAAE;gBACF,IAAI;gBACJ,WAAW;gBACX,WAAW;gBACX,IAAI;gBACJ,MAAM;aACP,CAAC,CAAA;YAEF,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACjC,CAAC;QAED,OAAO,aAA+C,CAAA;IACxD,CAAC;CACF;AA9ED,wBA8EC"}
@@ -0,0 +1,46 @@
import { ServiceInterfaceType } from '../types';
import { Effects } from '../Effects';
import { Scheme } from './Host';
/**
* A helper class for creating a Network Interface
*
* Network Interfaces are collections of web addresses that expose the same API or other resource,
* display to the user with under a common name and description.
*
* All URIs on an interface inherit the same ui: bool, basic auth credentials, path, and search (query) params
*
* @param options
* @returns
*/
export declare class ServiceInterfaceBuilder {
readonly options: {
effects: Effects;
name: string;
id: string;
description: string;
type: ServiceInterfaceType;
username: string | null;
path: string;
query: Record<string, string>;
schemeOverride: {
ssl: Scheme;
noSsl: Scheme;
} | null;
masked: boolean;
};
constructor(options: {
effects: Effects;
name: string;
id: string;
description: string;
type: ServiceInterfaceType;
username: string | null;
path: string;
query: Record<string, string>;
schemeOverride: {
ssl: Scheme;
noSsl: Scheme;
} | null;
masked: boolean;
});
}
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceInterfaceBuilder = void 0;
/**
* A helper class for creating a Network Interface
*
* Network Interfaces are collections of web addresses that expose the same API or other resource,
* display to the user with under a common name and description.
*
* All URIs on an interface inherit the same ui: bool, basic auth credentials, path, and search (query) params
*
* @param options
* @returns
*/
class ServiceInterfaceBuilder {
constructor(options) {
this.options = options;
}
}
exports.ServiceInterfaceBuilder = ServiceInterfaceBuilder;
//# sourceMappingURL=ServiceInterfaceBuilder.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ServiceInterfaceBuilder.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/ServiceInterfaceBuilder.ts"],"names":[],"mappings":";;;AAIA;;;;;;;;;;GAUG;AACH,MAAa,uBAAuB;IAClC,YACW,OAWR;QAXQ,YAAO,GAAP,OAAO,CAWf;IACA,CAAC;CACL;AAfD,0DAeC"}
@@ -0,0 +1,7 @@
import { Effects } from '../types';
export type SetExportedUrls = (opts: {
effects: Effects;
}) => Promise<void>;
export type UpdateExportedUrls = (effects: Effects) => Promise<null>;
export type SetupExportedUrls = (fn: SetExportedUrls) => UpdateExportedUrls;
export declare const setupExportedUrls: SetupExportedUrls;
@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupExportedUrls = void 0;
const setupExportedUrls = (fn) => {
return (async (effects) => {
const urls = [];
await fn({
effects: {
...effects,
plugin: {
...effects.plugin,
url: {
...effects.plugin.url,
exportUrl: (params) => {
urls.push(params.hostnameInfo);
return effects.plugin.url.exportUrl(params);
},
},
},
},
});
await effects.plugin.url.clearUrls({ except: urls });
return null;
});
};
exports.setupExportedUrls = setupExportedUrls;
//# sourceMappingURL=setupExportedUrls.js.map
@@ -0,0 +1 @@
{"version":3,"file":"setupExportedUrls.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/setupExportedUrls.ts"],"names":[],"mappings":";;;AAMO,MAAM,iBAAiB,GAAsB,CAAC,EAAmB,EAAE,EAAE;IAC1E,OAAO,CAAC,KAAK,EAAE,OAAgB,EAAE,EAAE;QACjC,MAAM,IAAI,GAAyB,EAAE,CAAA;QACrC,MAAM,EAAE,CAAC;YACP,OAAO,EAAE;gBACP,GAAG,OAAO;gBACV,MAAM,EAAE;oBACN,GAAG,OAAO,CAAC,MAAM;oBACjB,GAAG,EAAE;wBACH,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG;wBACrB,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;4BACpB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;4BAC9B,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;wBAC7C,CAAC;qBACF;iBACF;aACF;SACF,CAAC,CAAA;QACF,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,OAAO,IAAI,CAAA;IACb,CAAC,CAAuB,CAAA;AAC1B,CAAC,CAAA;AArBY,QAAA,iBAAiB,qBAqB7B"}
@@ -0,0 +1,15 @@
import * as T from '../types';
import { AddressReceipt } from './AddressReceipt';
declare const UpdateServiceInterfacesProof: unique symbol;
export type UpdateServiceInterfacesReceipt = {
[UpdateServiceInterfacesProof]: never;
};
export type ServiceInterfacesReceipt = Array<T.AddressInfo[] & AddressReceipt>;
export type SetServiceInterfaces<Output extends ServiceInterfacesReceipt> = (opts: {
effects: T.Effects;
}) => Promise<Output>;
export type UpdateServiceInterfaces = (effects: T.Effects) => Promise<null>;
export type SetupServiceInterfaces = <Output extends ServiceInterfacesReceipt>(fn: SetServiceInterfaces<Output>) => UpdateServiceInterfaces;
export declare const NO_INTERFACE_CHANGES: UpdateServiceInterfacesReceipt;
export declare const setupServiceInterfaces: SetupServiceInterfaces;
export {};
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupServiceInterfaces = exports.NO_INTERFACE_CHANGES = void 0;
exports.NO_INTERFACE_CHANGES = {};
const setupServiceInterfaces = (fn) => {
return (async (effects) => {
const bindings = [];
const interfaces = [];
await fn({
effects: {
...effects,
bind: (params) => {
bindings.push({ id: params.id, internalPort: params.internalPort });
return effects.bind(params);
},
exportServiceInterface: (params) => {
interfaces.push(params.id);
return effects.exportServiceInterface(params);
},
},
});
await effects.clearBindings({ except: bindings });
await effects.clearServiceInterfaces({ except: interfaces });
return null;
});
};
exports.setupServiceInterfaces = setupServiceInterfaces;
//# sourceMappingURL=setupInterfaces.js.map
@@ -0,0 +1 @@
{"version":3,"file":"setupInterfaces.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/setupInterfaces.ts"],"names":[],"mappings":";;;AAgBa,QAAA,oBAAoB,GAAG,EAAoC,CAAA;AACjE,MAAM,sBAAsB,GAA2B,CAG5D,EAAgC,EAChC,EAAE;IACF,OAAO,CAAC,KAAK,EAAE,OAAkB,EAAE,EAAE;QACnC,MAAM,QAAQ,GAAe,EAAE,CAAA;QAC/B,MAAM,UAAU,GAA2B,EAAE,CAAA;QAC7C,MAAM,EAAE,CAAC;YACP,OAAO,EAAE;gBACP,GAAG,OAAO;gBACV,IAAI,EAAE,CAAC,MAAoB,EAAE,EAAE;oBAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAA;oBACnE,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC7B,CAAC;gBACD,sBAAsB,EAAE,CAAC,MAAsC,EAAE,EAAE;oBACjE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;oBAC1B,OAAO,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAA;gBAC/C,CAAC;aACF;SACF,CAAC,CAAA;QACF,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;QACjD,MAAM,OAAO,CAAC,sBAAsB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;QAC5D,OAAO,IAAI,CAAA;IACb,CAAC,CAA4B,CAAA;AAC/B,CAAC,CAAA;AAzBY,QAAA,sBAAsB,0BAyBlC"}
@@ -0,0 +1,8 @@
import type { AnyVerifyingKey } from './AnyVerifyingKey';
export type AcceptSigners = {
signer: AnyVerifyingKey;
} | {
any: Array<AcceptSigners>;
} | {
all: Array<AcceptSigners>;
};
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=AcceptSigners.js.map
@@ -0,0 +1 @@
{"version":3,"file":"AcceptSigners.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/AcceptSigners.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
export type AcmeProvider = string;
@@ -0,0 +1,4 @@
"use strict";
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=AcmeProvider.js.map
@@ -0,0 +1 @@
{"version":3,"file":"AcmeProvider.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/AcmeProvider.ts"],"names":[],"mappings":";AAAA,4GAA4G"}
@@ -0,0 +1,3 @@
export type AcmeSettings = {
contact: Array<string>;
};
@@ -0,0 +1,4 @@
"use strict";
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=AcmeSettings.js.map
@@ -0,0 +1 @@
{"version":3,"file":"AcmeSettings.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/AcmeSettings.ts"],"names":[],"mappings":";AAAA,4GAA4G"}
+1
View File
@@ -0,0 +1 @@
export type ActionId = string;
+4
View File
@@ -0,0 +1,4 @@
"use strict";
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=ActionId.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ActionId.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/ActionId.ts"],"names":[],"mappings":";AAAA,4GAA4G"}
@@ -0,0 +1,6 @@
import type { Guid } from './Guid';
export type ActionInput = {
eventId: Guid;
spec: Record<string, unknown>;
value: Record<string, unknown> | null;
};
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=ActionInput.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ActionInput.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/ActionInput.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
import type { ActionVisibility } from './ActionVisibility';
import type { AllowedStatuses } from './AllowedStatuses';
export type ActionMetadata = {
/**
* A human-readable name
*/
name: string;
/**
* A detailed description of what the action will do
*/
description: string;
/**
* Presents as an alert prior to executing the action. Should be used sparingly but important if the action could have harmful, unintended consequences
*/
warning: string | null;
/**
* One of: "enabled", "hidden", or { disabled: "" }
* - "enabled" - the action is available be run
* - "hidden" - the action cannot be seen or run
* - { disabled: "example explanation" } means the action is visible but cannot be run. Replace "example explanation" with a reason why the action is disable to prevent user confusion.
*/
visibility: ActionVisibility;
/**
* One of: "only-stopped", "only-running", "all"
* - "only-stopped" - the action can only be run when the service is stopped
* - "only-running" - the action can only be run when the service is running
* - "any" - the action can only be run regardless of the service's status
*/
allowedStatuses: AllowedStatuses;
hasInput: boolean;
/**
* If provided, this action will be nested under a header of this value, along with other actions of the same group
*/
group: string | null;
};
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=ActionMetadata.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ActionMetadata.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/ActionMetadata.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
import type { ActionResultV0 } from './ActionResultV0';
import type { ActionResultV1 } from './ActionResultV1';
export type ActionResult = ({
version: '0';
} & ActionResultV0) | ({
version: '1';
} & ActionResultV1);

Some files were not shown because too many files have changed in this diff Show More