Fix StartOS 0.4 TypeScript packaging to match SDK API
This commit is contained in:
+171
@@ -0,0 +1,171 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.0-beta.66 (2026-03-24)
|
||||
|
||||
- **Breaking:** `withPgDump()` replaces `pgdata` with required `mountpoint` + `pgdataPath`
|
||||
- Passwordless/trust auth support for `withPgDump()` and `withMysqlDump()`
|
||||
- New options: `pgOptions` for postgres, `mysqldOptions` for mysql/mariadb
|
||||
- Fixed MariaDB backup/restore support
|
||||
|
||||
## 0.4.0-beta.65 (2026-03-23)
|
||||
|
||||
### Added
|
||||
|
||||
- `Backups.withPgDump()`: dump-based PostgreSQL backup using `pg_dump`/`pg_restore`, replacing raw volume rsync of PG data directories
|
||||
- `Backups.withMysqlDump()`: dump-based MySQL/MariaDB backup using `mysqldump`/`mysql`
|
||||
- Password configs accept `string | (() => string | Promise<string>)` for deferred resolution during restore
|
||||
|
||||
## 0.4.0-beta.63 — StartOS v0.4.0-alpha.22 (2026-03-22)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `createTask` failing when input values are undefined
|
||||
- Fixed daemon lifecycle cleanup and error logging improvements
|
||||
- Replaced fire-and-forget restart loop in `Daemon` with tracked `AbortController`
|
||||
- Fixed graceful shutdown for subcontainer daemons
|
||||
- Fixed rsync progress regex never matching, spamming logs during backup
|
||||
- Fixed rsync backup bugs and optimized flags for encrypted CIFS targets
|
||||
- Fixed types in `inputSpecConstants`, `StartSdk`, and exports
|
||||
|
||||
## 0.4.0-beta.62 (2026-03-19)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `Value.dynamicSelect` and `Value.dynamicMultiselect` crashing with `z.union([])` when `values` is empty (zod v4 compatibility)
|
||||
|
||||
### Added
|
||||
|
||||
- `FileHelper.xml`: file helper for XML files using `fast-xml-parser`
|
||||
- `smtpShape`: typed zod schema for persisting SMTP selection in store file models, replacing direct use of `smtpInputSpec.validator` which caused cross-zod-instance errors
|
||||
|
||||
## 0.4.0-beta.61 — StartOS v0.4.0-alpha.21 (2026-03-16)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed bug where leaving the effect context triggered consts
|
||||
|
||||
## 0.4.0-beta.60 — StartOS v0.4.0-alpha.20 (2026-03-16)
|
||||
|
||||
### Added
|
||||
|
||||
- Tunnel TS type exports and port forward labels
|
||||
- Secure Boot MOK key enrollment fields in `SetupInfo`
|
||||
|
||||
### Changed
|
||||
|
||||
- Consolidated `Watchable` base class with generic `map`/`eq` support; renamed `call` to `fetch`
|
||||
- Moved `GetServiceManifest` and `GetSslCertificate` from `package/` to `base/`
|
||||
- Simplified `getServiceInterface`, `getServiceInterfaces`, `GetOutboundGateway`, `GetSystemSmtp`, and `fileHelper` using `Watchable` base class
|
||||
- Simplified SDK Makefile with rsync
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added `restart_again` flag to `DesiredStatus::Restarting`
|
||||
|
||||
## 0.4.0-beta.59 — StartOS v0.4.0-alpha.20 (2026-03-06)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for preferred external ports besides 443
|
||||
- Bridge filter kind on service interfaces
|
||||
|
||||
### Fixed
|
||||
|
||||
- Merge version ranges when adding existing package signer
|
||||
- Task fix for action task system
|
||||
|
||||
## 0.4.0-beta.56 — StartOS v0.4.0-alpha.19 (2026-02-02)
|
||||
|
||||
### Added
|
||||
|
||||
- `getOutboundGateway` effect and SDK wrapper
|
||||
- Improved service version migration and data version handling
|
||||
- `zod-deep-partial` integration with `partialValidator` on `InputSpec`
|
||||
- SMTP rework with improved provider variants and system SMTP spec
|
||||
|
||||
### Changed
|
||||
|
||||
- Migrated from `ts-matches` to `zod` across all TypeScript packages
|
||||
- Builder-style `InputSpec` API with prefill plumbing
|
||||
- Split `row_actions` into `remove_action` and `overflow_actions` for URL plugins
|
||||
|
||||
### Fixed
|
||||
|
||||
- Scoped public domain to single binding and return single port check
|
||||
- Preserved `z` namespace types for SDK consumers
|
||||
- `--arch` flag falls back to emulation when native image unavailable
|
||||
|
||||
## 0.4.0-beta.54 — StartOS v0.4.0-alpha.18 (2026-01-27)
|
||||
|
||||
### Added
|
||||
|
||||
- Device info RPC
|
||||
- Hardware acceleration and NVIDIA card support on nonfree images
|
||||
|
||||
### Changed
|
||||
|
||||
- Consolidated setup flow
|
||||
- Improved SDK abort handling and `InputSpec` filtering
|
||||
|
||||
## 0.4.0-beta.49 — StartOS v0.4.0-alpha.17 (2026-01-10)
|
||||
|
||||
### Added
|
||||
|
||||
- JSDoc comments on all consumer-facing APIs
|
||||
- StartTunnel random subnet support
|
||||
- Port 80 to 5443 tunnel mapping
|
||||
|
||||
### Fixed
|
||||
|
||||
- `EffectCreator` type corrections
|
||||
- Allow multiple equal signs in ENV `FileHelper` values
|
||||
- Miscellaneous alpha.16 follow-up fixes
|
||||
|
||||
## 0.4.0-beta.45 — StartOS v0.4.0-alpha.16 (2025-12-18)
|
||||
|
||||
### Added
|
||||
|
||||
- `map` and `eq` on `getServiceInterface` watcher
|
||||
- Flavor-aware version range handling
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored `StatusInfo` types
|
||||
- Improved shutdown ordering for daemons
|
||||
- Improved StartTunnel validation and garbage collection
|
||||
|
||||
## 0.4.0-beta.43 — StartOS v0.4.0-alpha.15 (2025-11-26)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Minor bugfixes for alpha.14
|
||||
|
||||
## 0.4.0-beta.42 — StartOS v0.4.0-alpha.14 (2025-11-20)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bugfixes for alpha.13
|
||||
|
||||
## 0.4.0-beta.41 — StartOS v0.4.0-alpha.13 (2025-11-15)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bugfixes for alpha.12
|
||||
|
||||
## 0.4.0-beta.40 — StartOS v0.4.0-alpha.12 (2025-11-07)
|
||||
|
||||
### Added
|
||||
|
||||
- StartTunnel integration
|
||||
- Configurable `textarea` rows in `InputSpec`
|
||||
|
||||
## 0.4.0-beta.39 — StartOS v0.4.0-alpha.11 (2025-09-24)
|
||||
|
||||
### Added
|
||||
|
||||
- Gateway limiting for StartTunnel
|
||||
- Improved copy UX around Tor SSL
|
||||
|
||||
### Changed
|
||||
|
||||
- SDK type updates and internal improvements
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Start9 Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
# Start SDK
|
||||
|
||||
The Start SDK (`@start9labs/start-sdk`) is a TypeScript framework for packaging services to run on [StartOS](https://github.com/Start9Labs/start-os). It provides a strongly-typed, builder-pattern API for defining every aspect of a service package: manifests, daemons, health checks, networking interfaces, actions, backups, dependencies, configuration, and more.
|
||||
|
||||
## Features
|
||||
|
||||
- **Type-safe manifest definitions** - Declare your service's identity, metadata, images, volumes, and dependencies with full TypeScript inference.
|
||||
- **Daemon management** - Define multi-process topologies with startup ordering, ready probes, and graceful shutdown via `Daemons.of().addDaemon()`.
|
||||
- **Health checks** - Built-in checks for TCP port listening, HTTP(S) endpoints, and custom scripts, with configurable polling strategies (fixed interval, cooldown, adaptive).
|
||||
- **Network interfaces** - Bind ports, export UI/API/P2P interfaces, and manage hostnames with MultiHost and ServiceInterfaceBuilder.
|
||||
- **User actions** - Create interactive operations with validated form inputs (text, select, toggle, list, union/variants, and more) that users can trigger from the StartOS UI.
|
||||
- **Backup and restore** - Rsync-based volume backups with exclude patterns and custom sync paths.
|
||||
- **Dependency management** - Declare inter-service dependencies with version ranges, health check requirements, and volume mounts.
|
||||
- **Configuration file helpers** - Read, write, and merge JSON, YAML, TOML, INI, and ENV files with type-safe `FileHelper`.
|
||||
- **Reactive subscriptions** - Watch for changes to container IPs, SSL certificates, SMTP config, service status, and more with `const()`, `once()`, `watch()`, `onChange()`, and `waitFor()` patterns.
|
||||
- **Extended versioning (ExVer)** - Flavor-aware semantic versioning with range matching, supporting independent upstream and downstream version tracking.
|
||||
- **Internationalization** - Built-in i18n support with locale fallback and parameter substitution.
|
||||
- **Container execution** - Run commands in subcontainers with volume mounts, environment variables, and entrypoint overrides.
|
||||
- **Plugin system** - Extensible plugin architecture (e.g. `url-v0` for URL management).
|
||||
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { setupManifest, buildManifest } from '@start9labs/start-sdk'
|
||||
|
||||
const manifest = setupManifest({
|
||||
id: 'my-service',
|
||||
title: 'My Service',
|
||||
license: 'MIT',
|
||||
// ...
|
||||
})
|
||||
|
||||
export default buildManifest(manifest)
|
||||
```
|
||||
|
||||
The primary entry point is the `StartSdk` facade:
|
||||
|
||||
```typescript
|
||||
import { StartSdk } from '@start9labs/start-sdk'
|
||||
import { manifest } from './manifest'
|
||||
|
||||
export const sdk = StartSdk.of().withManifest(manifest).build(true)
|
||||
```
|
||||
|
||||
From there, `sdk` exposes the full toolkit:
|
||||
|
||||
```typescript
|
||||
// Define daemons
|
||||
export const main = sdk.setupMain(async ({ effects }) =>
|
||||
sdk.Daemons.of(effects)
|
||||
.addDaemon('primary', { /* ... */ })
|
||||
)
|
||||
|
||||
// Define actions
|
||||
export const setName = sdk.Action.withInput('set-name', /* ... */)
|
||||
|
||||
// Define interfaces
|
||||
export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
|
||||
const multi = sdk.MultiHost.of(effects, 'web')
|
||||
const origin = await multi.bindPort(80, { protocol: 'http' })
|
||||
const ui = sdk.createInterface(effects, { name: 'Web UI', id: 'ui', /* ... */ })
|
||||
return [await origin.export([ui])]
|
||||
})
|
||||
|
||||
// Define backups
|
||||
export const { createBackup, restoreBackup } = sdk.setupBackups(
|
||||
async () => sdk.Backups.ofVolumes('main')
|
||||
)
|
||||
```
|
||||
|
||||
## Packages
|
||||
|
||||
| Package | npm | Description |
|
||||
|---------|-----|-------------|
|
||||
| `package/` | `@start9labs/start-sdk` | Full SDK for service developers |
|
||||
| `base/` | `@start9labs/start-sdk-base` | Core types, ABI definitions, and effects interface |
|
||||
|
||||
## Documentation
|
||||
|
||||
For comprehensive packaging guides, tutorials, and API reference:
|
||||
|
||||
**[docs.start9.com/packaging](https://docs.start9.com/packaging)**
|
||||
|
||||
The packaging docs cover:
|
||||
- Environment setup and prerequisites
|
||||
- Project structure and conventions
|
||||
- Manifest, main, interfaces, actions, and all other service modules
|
||||
- File models and configuration management
|
||||
- Versioning, migrations, and initialization
|
||||
- Dependencies and cross-service communication
|
||||
- Building and installing `.s9pk` packages
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for environment setup, building from source, testing, and development workflow.
|
||||
|
||||
## Architecture
|
||||
|
||||
See [ARCHITECTURE.md](ARCHITECTURE.md) for a detailed overview of the SDK's internal structure, module responsibilities, and data flow.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
+169
@@ -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
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=Effects.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Effects.js","sourceRoot":"","sources":["../../../base/lib/Effects.ts"],"names":[],"mappings":""}
|
||||
+40
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../base/lib/actions/index.ts"],"names":[],"mappings":"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"}
|
||||
+5
@@ -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 };
|
||||
+12
@@ -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
|
||||
+1
@@ -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"}
|
||||
+222
@@ -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 {};
|
||||
+319
@@ -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
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+173
@@ -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>;
|
||||
Generated
Vendored
+16
@@ -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
|
||||
Generated
Vendored
+1
@@ -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"}
|
||||
+103
@@ -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>;
|
||||
}
|
||||
+121
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../../../base/lib/actions/input/builder/list.ts"],"names":[],"mappings":"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"}
|
||||
+791
@@ -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>;
|
||||
}
|
||||
+764
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+111
@@ -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>;
|
||||
}
|
||||
+110
@@ -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
|
||||
Generated
Vendored
+1
@@ -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
@@ -0,0 +1,3 @@
|
||||
export * as constants from './inputSpecConstants';
|
||||
export * as types from './inputSpecTypes';
|
||||
export * as builder from './builder';
|
||||
+40
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../base/lib/actions/input/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kEAAiD;AACjD,0DAAyC;AACzC,qDAAoC"}
|
||||
Generated
Vendored
+2264
File diff suppressed because it is too large
Load Diff
+247
@@ -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
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
+285
@@ -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>;
|
||||
};
|
||||
+13
@@ -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
|
||||
+1
@@ -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
@@ -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
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"setupActions.js","sourceRoot":"","sources":["../../../../base/lib/actions/setupActions.ts"],"names":[],"mappings":"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"}
|
||||
Generated
Vendored
+87
@@ -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);
|
||||
+2
File diff suppressed because one or more lines are too long
+196
@@ -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);
|
||||
+21
@@ -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>>;
|
||||
+156
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+22
@@ -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 {};
|
||||
+19
@@ -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
|
||||
Generated
Vendored
+1
@@ -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
@@ -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 {};
|
||||
+2727
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+389
@@ -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 {};
|
||||
+1038
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+11
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
|
||||
export * from './setupInit';
|
||||
export * from './setupUninit';
|
||||
+19
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../base/lib/inits/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B;AAC3B,gDAA6B"}
|
||||
+26
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
declare const AddressProof: unique symbol;
|
||||
export type AddressReceipt = {
|
||||
[AddressProof]: never;
|
||||
};
|
||||
export {};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=AddressReceipt.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"AddressReceipt.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/AddressReceipt.ts"],"names":[],"mappings":""}
|
||||
+101
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Host.js","sourceRoot":"","sources":["../../../../base/lib/interfaces/Host.ts"],"names":[],"mappings":"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"}
|
||||
+31
@@ -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
@@ -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
@@ -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"}
|
||||
Generated
Vendored
+46
@@ -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;
|
||||
});
|
||||
}
|
||||
Generated
Vendored
+21
@@ -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
|
||||
Generated
Vendored
+1
@@ -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"}
|
||||
+7
@@ -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;
|
||||
+27
@@ -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
|
||||
+1
@@ -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"}
|
||||
+15
@@ -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 {};
|
||||
+28
@@ -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
|
||||
+1
@@ -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"}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { AnyVerifyingKey } from './AnyVerifyingKey';
|
||||
export type AcceptSigners = {
|
||||
signer: AnyVerifyingKey;
|
||||
} | {
|
||||
any: Array<AcceptSigners>;
|
||||
} | {
|
||||
all: Array<AcceptSigners>;
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=AcceptSigners.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"AcceptSigners.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/AcceptSigners.ts"],"names":[],"mappings":""}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export type AcmeProvider = string;
|
||||
+4
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"AcmeProvider.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/AcmeProvider.ts"],"names":[],"mappings":";AAAA,4GAA4G"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export type AcmeSettings = {
|
||||
contact: Array<string>;
|
||||
};
|
||||
+4
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"AcmeSettings.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/AcmeSettings.ts"],"names":[],"mappings":";AAAA,4GAA4G"}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export type ActionId = string;
|
||||
+4
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ActionId.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/ActionId.ts"],"names":[],"mappings":";AAAA,4GAA4G"}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import type { Guid } from './Guid';
|
||||
export type ActionInput = {
|
||||
eventId: Guid;
|
||||
spec: Record<string, unknown>;
|
||||
value: Record<string, unknown> | null;
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=ActionInput.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ActionInput.js","sourceRoot":"","sources":["../../../../base/lib/osBindings/ActionInput.ts"],"names":[],"mappings":""}
|
||||
+35
@@ -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;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user