@ariestools/cli-kit 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.mjs +17 -1
- package/dist/node/index.mjs.map +3 -3
- package/dist/node/runServiceUntilInterrupt.d.ts +16 -0
- package/dist/node/runServiceUntilInterrupt.d.ts.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -12,6 +12,20 @@ The package also provides structural `ProcessHost` and `ProcessIO` boundaries fo
|
|
|
12
12
|
|
|
13
13
|
`createCommandCatalog` registers ordered command factories against a structural `CliApplicationContext`. The command type is supplied by the application, so parser-specific types and domain-specific configuration remain outside the core. A catalog is immutable and reusable; each application invocation materializes fresh command objects with its own context.
|
|
14
14
|
|
|
15
|
+
`runServiceUntilInterrupt` blocks a long-running command handler until the first host interrupt, disposes the listener, then awaits an application-supplied `stop` cleanup. Use it for serve-style commands that should drain cleanly on SIGINT/SIGTERM.
|
|
16
|
+
|
|
17
|
+
## Application shell pattern (reference)
|
|
18
|
+
|
|
19
|
+
The public package [`@xyo-network/lifehash-indexer`](https://github.com/XYOracleNetwork/lifehash/tree/main/packages/indexer) is the golden template for a product CLI on this kit:
|
|
20
|
+
|
|
21
|
+
1. **Bin / process boundary** — `runProcessApplication` + `createNodeProcessHost` (or `nodeProcessHost`) so the library bundle stays free of process side effects.
|
|
22
|
+
2. **Yargs adapter** — `runYargsApplication` with an app-owned `configure*` helper that applies `rejectUnknownCommands`, `.strictOptions()`, help/version, and `host.io.columns` wrapping.
|
|
23
|
+
3. **Command catalog** — `createCommandCatalog` over a `CliApplicationContext` that at least carries `host`, so tests inject a recording `ProcessHost` without touching `process`.
|
|
24
|
+
4. **Failure policy** — a `FailureExitCodeMapper` that maps parse vs handler failures onto `SYS_EXITS` (`usage` / `config` / `software`, …).
|
|
25
|
+
5. **Domain commands** — yargs `CommandModule` factories that take context and never import Node process globals for env/argv/exit.
|
|
26
|
+
|
|
27
|
+
Sibling shells that follow the same shape include `@xyo-network/webble-cli` and `@xyo-network/webble-dapp`. Actor-heavy CLIs (for example `@xyo-network/xl1-cli-lib`) layer `createActorBuilderCatalog`, `ManagedActor`, and `RuntimeSession` on top of the same process boundary.
|
|
28
|
+
|
|
15
29
|
## Lifecycle guarantees
|
|
16
30
|
|
|
17
31
|
- Actors start sequentially in registration order.
|
|
@@ -27,6 +41,7 @@ The package also provides structural `ProcessHost` and `ProcessIO` boundaries fo
|
|
|
27
41
|
- Process applications absorb host-routed exits and map unexpected failures to exit code one, or to the code selected by an application-supplied mapper.
|
|
28
42
|
- Command catalogs preserve registration order and reject blank or duplicate ids.
|
|
29
43
|
- Command factories receive the exact per-application context and are invoked again for each materialization.
|
|
44
|
+
- Service-until-interrupt disposes its host listener before stop and surfaces stop failures to the caller.
|
|
30
45
|
|
|
31
46
|
## License
|
|
32
47
|
|
package/dist/node/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './CommandCatalog.ts';
|
|
|
4
4
|
export * from './ManagedActor.ts';
|
|
5
5
|
export * from './ProcessApplication.ts';
|
|
6
6
|
export * from './ProcessHost.ts';
|
|
7
|
+
export * from './runServiceUntilInterrupt.ts';
|
|
7
8
|
export * from './RuntimeSession.ts';
|
|
8
9
|
export * from './sysExits.ts';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/node/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,yBAAyB,CAAA;AACvC,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,yBAAyB,CAAA;AACvC,cAAc,kBAAkB,CAAA;AAChC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA"}
|
package/dist/node/index.mjs
CHANGED
|
@@ -273,6 +273,21 @@ async function runProcessApplication({
|
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
// src/runServiceUntilInterrupt.ts
|
|
277
|
+
async function runServiceUntilInterrupt(host, stop) {
|
|
278
|
+
await new Promise((resolve, reject) => {
|
|
279
|
+
const dispose = host.onInterrupt(async () => {
|
|
280
|
+
dispose();
|
|
281
|
+
try {
|
|
282
|
+
await stop();
|
|
283
|
+
resolve();
|
|
284
|
+
} catch (error) {
|
|
285
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
276
291
|
// src/RuntimeSession.ts
|
|
277
292
|
var RuntimeSession = class {
|
|
278
293
|
_exitCode;
|
|
@@ -402,6 +417,7 @@ export {
|
|
|
402
417
|
createCommandCatalog,
|
|
403
418
|
exitProcess,
|
|
404
419
|
resolveFailureExitCode,
|
|
405
|
-
runProcessApplication
|
|
420
|
+
runProcessApplication,
|
|
421
|
+
runServiceUntilInterrupt
|
|
406
422
|
};
|
|
407
423
|
//# sourceMappingURL=index.mjs.map
|
package/dist/node/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/ActorBuilderCatalog.ts", "../../src/ActorSupervisor.ts", "../../src/CommandCatalog.ts", "../../src/ProcessHost.ts", "../../src/ProcessApplication.ts", "../../src/RuntimeSession.ts", "../../src/sysExits.ts"],
|
|
4
|
-
"sourcesContent": ["export interface ActorBuilderDefinition<TContext, TActor> {\n readonly name: string\n build(context: TContext): Promise<TActor>\n}\n\nexport interface ActorBuilderCatalog<TContext, TActor> {\n readonly names: readonly string[]\n build(name: string, context: TContext): Promise<TActor>\n}\n\n/** Creates an ordered, instance-scoped actor builder catalog. */\nexport function createActorBuilderCatalog<TContext, TActor>(\n definitions: readonly ActorBuilderDefinition<TContext, TActor>[],\n): ActorBuilderCatalog<TContext, TActor> {\n const definitionsByName = new Map<string, ActorBuilderDefinition<TContext, TActor>>()\n const names: string[] = []\n for (const definition of definitions) {\n if (definitionsByName.has(definition.name)) {\n throw new Error(`Actor builder \"${definition.name}\" is already registered`)\n }\n definitionsByName.set(definition.name, definition)\n names.push(definition.name)\n }\n\n return {\n names: Object.freeze(names),\n async build(name: string, context: TContext): Promise<TActor> {\n const definition = definitionsByName.get(name)\n if (definition === undefined) throw new Error(`Unknown actor: ${name}`)\n return await definition.build(context)\n },\n }\n}\n", "import type {\n ManagedActor, ManagedActorLifecycleResult, ReadinessSignal, ReadyState,\n} from './ManagedActor.ts'\n\nexport type ActorSupervisorState = 'failed' | 'idle' | 'started' | 'starting' | 'stopped' | 'stopping'\n\nexport class ActorRegistrationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'ActorRegistrationError'\n }\n}\n\nexport class ActorSupervisorStateError extends Error {\n readonly state: ActorSupervisorState\n\n constructor(operation: string, state: ActorSupervisorState) {\n super(`Cannot ${operation} while actor supervisor is ${state}`)\n this.name = 'ActorSupervisorStateError'\n this.state = state\n }\n}\n\nexport class ActorStartupError extends Error {\n readonly actorName: string\n readonly rollbackErrors: readonly Error[]\n\n constructor(actorName: string, cause: unknown, rollbackErrors: readonly Error[]) {\n super(`Actor \"${actorName}\" failed to start`, { cause })\n this.name = 'ActorStartupError'\n this.actorName = actorName\n this.rollbackErrors = rollbackErrors\n }\n}\n\nexport class ActorReadinessError extends Error {\n readonly actorName: string\n\n constructor(actorName: string, cause: unknown) {\n super(`Actor \"${actorName}\" failed to become ready`, { cause })\n this.name = 'ActorReadinessError'\n this.actorName = actorName\n }\n}\n\nexport class ActorReadinessTimeoutError extends Error {\n readonly timeoutMs: number\n\n constructor(timeoutMs: number) {\n super(`Actors did not become ready within ${timeoutMs}ms`)\n this.name = 'ActorReadinessTimeoutError'\n this.timeoutMs = timeoutMs\n }\n}\n\nexport class ActorShutdownError extends AggregateError {\n constructor(errors: readonly Error[]) {\n super(errors, `${errors.length} actor(s) failed to stop`)\n this.name = 'ActorShutdownError'\n }\n}\n\ninterface TimerHost {\n clearTimeout(handle: unknown): void\n setTimeout(callback: () => void, timeoutMs: number): unknown\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error))\n}\n\nfunction assertLifecycleSucceeded(actor: ManagedActor, operation: 'start' | 'stop', result: ManagedActorLifecycleResult): void {\n if (result === false) {\n throw new Error(`Actor \"${actor.name}\" ${operation}() reported failure`)\n }\n}\n\nfunction assertTimeout(timeoutMs: number): void {\n if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {\n throw new RangeError('Readiness timeout must be a finite, non-negative number')\n }\n}\n\nasync function waitWithTimeout(signal: ReadinessSignal, timeoutMs: number): Promise<void> {\n const timerHost = globalThis as unknown as TimerHost\n let timeoutHandle: unknown\n const expired = new Promise<never>((_resolve, reject) => {\n timeoutHandle = timerHost.setTimeout(() => reject(new ActorReadinessTimeoutError(timeoutMs)), timeoutMs)\n })\n try {\n await Promise.race([signal.whenReady(timeoutMs), expired])\n } finally {\n timerHost.clearTimeout(timeoutHandle)\n }\n}\n\n/**\n * Instance-scoped coordinator for structural managed actors.\n *\n * Startup and readiness follow registration order. Rollback and ordinary shutdown\n * follow reverse startup order. A supervisor is single-use after it has stopped or\n * failed; construct a new instance for a new lifecycle.\n */\nexport class ActorSupervisor implements ReadinessSignal {\n private readonly _actorNames = new Set<string>()\n private readonly _actors: ManagedActor[] = []\n private _readyError?: Error\n private _startPromise?: Promise<void>\n private readonly _startedActors: ManagedActor[] = []\n private _state: ActorSupervisorState = 'idle'\n private _stopPromise?: Promise<void>\n\n constructor(actors: readonly ManagedActor[] = []) {\n for (const actor of actors) this.register(actor)\n }\n\n get actors(): readonly ManagedActor[] {\n return [...this._actors]\n }\n\n get readyError(): Error | undefined {\n if (this._readyError !== undefined) return this._readyError\n return this._actors.find(actor => actor.readyState === 'failed')?.readyError\n }\n\n get readyState(): ReadyState {\n if (this._readyError !== undefined || this._state === 'failed' || this._actors.some(actor => actor.readyState === 'failed')) {\n return 'failed'\n }\n if (this._state === 'started' && this._actors.every(actor => actor.readyState === 'ready')) return 'ready'\n return 'pending'\n }\n\n get state(): ActorSupervisorState {\n return this._state\n }\n\n isReady(): boolean {\n return this.readyState === 'ready'\n }\n\n register(actor: ManagedActor): void {\n if (this._state !== 'idle') throw new ActorSupervisorStateError('register an actor', this._state)\n if (actor.name.trim().length === 0) throw new ActorRegistrationError('Actor name must not be empty')\n if (this._actorNames.has(actor.name)) {\n throw new ActorRegistrationError(`Actor \"${actor.name}\" is already registered`)\n }\n this._actorNames.add(actor.name)\n this._actors.push(actor)\n }\n\n start(): Promise<void> {\n if (this._startPromise !== undefined) return this._startPromise\n if (this._state !== 'idle') return Promise.reject(new ActorSupervisorStateError('start', this._state))\n this._state = 'starting'\n this._startPromise = this.startActors()\n return this._startPromise\n }\n\n stop(): Promise<void> {\n this._stopPromise ??= this.stopActors()\n return this._stopPromise\n }\n\n async whenReady(timeoutMs?: number): Promise<void> {\n if (timeoutMs !== undefined) assertTimeout(timeoutMs)\n if (this._state === 'starting' && this._startPromise !== undefined) {\n await this._startPromise\n }\n if (this._state !== 'started') throw new ActorSupervisorStateError('wait for readiness', this._state)\n\n const startedAt = Date.now()\n for (const actor of this._actors) {\n try {\n if (timeoutMs === undefined) {\n await actor.whenReady()\n } else {\n const elapsed = Date.now() - startedAt\n const remainingTimeoutMs = timeoutMs - elapsed\n if (remainingTimeoutMs < 0) throw new ActorReadinessTimeoutError(timeoutMs)\n await waitWithTimeout(actor, remainingTimeoutMs)\n }\n } catch (error) {\n const readinessError = new ActorReadinessError(actor.name, error)\n this._readyError = readinessError\n throw readinessError\n }\n }\n }\n\n private async startActors(): Promise<void> {\n for (const actor of this._actors) {\n try {\n const result = await actor.start()\n assertLifecycleSucceeded(actor, 'start', result)\n this._startedActors.push(actor)\n } catch (error) {\n const rollbackErrors = await this.stopStartedActors()\n const startupError = new ActorStartupError(actor.name, error, rollbackErrors)\n this._readyError = startupError\n this._state = 'failed'\n throw startupError\n }\n }\n this._state = 'started'\n }\n\n private async stopActors(): Promise<void> {\n if (this._state === 'starting' && this._startPromise !== undefined) {\n try {\n await this._startPromise\n } catch {\n // Startup already rolled back every actor that reached started state.\n }\n }\n\n if (this._state === 'stopped') return\n if (this._state === 'idle' || (this._state === 'failed' && this._startedActors.length === 0)) {\n this._state = 'stopped'\n return\n }\n\n this._state = 'stopping'\n const errors = await this.stopStartedActors()\n this._state = 'stopped'\n if (errors.length > 0) throw new ActorShutdownError(errors)\n }\n\n private async stopStartedActors(): Promise<Error[]> {\n const errors: Error[] = []\n while (this._startedActors.length > 0) {\n const actor = this._startedActors.pop()\n if (actor === undefined) continue\n try {\n const result = await actor.stop()\n assertLifecycleSucceeded(actor, 'stop', result)\n } catch (error) {\n errors.push(new Error(`Actor \"${actor.name}\" failed to stop`, { cause: asError(error) }))\n }\n }\n return errors\n }\n}\n", "import type { ProcessHost } from './ProcessHost.ts'\n\n/** Process capability shared by every command created for one CLI app. */\nexport interface CliApplicationContext {\n readonly host: ProcessHost\n}\n\n/** Adapter-neutral factory for one parser-specific command object. */\nexport interface CommandDefinition<TContext extends CliApplicationContext, TCommand> {\n readonly create: (context: TContext) => TCommand\n /** Internal registration identity; it does not define parser syntax or aliases. */\n readonly id: string\n}\n\n/** Ordered command factories materialized for one app context. */\nexport interface CommandCatalog<TContext extends CliApplicationContext, TCommand> {\n readonly createCommands: (context: TContext) => readonly TCommand[]\n readonly ids: readonly string[]\n}\n\n/** Creates an immutable, adapter-neutral command catalog. */\nexport function createCommandCatalog<TContext extends CliApplicationContext, TCommand>(\n definitions: readonly CommandDefinition<TContext, TCommand>[],\n): CommandCatalog<TContext, TCommand> {\n const registeredIds = new Set<string>()\n const retainedDefinitions = definitions.map(definition => Object.freeze({\n create: definition.create,\n id: definition.id,\n }))\n const ids: string[] = []\n\n for (const definition of retainedDefinitions) {\n if (definition.id.trim().length === 0) throw new Error('Command id must not be empty')\n if (registeredIds.has(definition.id)) throw new Error(`Command \"${definition.id}\" is already registered`)\n registeredIds.add(definition.id)\n ids.push(definition.id)\n }\n\n return Object.freeze({\n ids: Object.freeze(ids),\n createCommands: (context: TContext): readonly TCommand[] =>\n Object.freeze(retainedDefinitions.map(definition => definition.create(context))),\n })\n}\n", "/** Process-facing input and output used by a command-line app. */\nexport interface ProcessIO {\n /** Available terminal width when output is attached to a sized terminal. */\n readonly columns?: number\n readonly isInteractive: boolean\n error(...values: readonly unknown[]): void\n log(...values: readonly unknown[]): void\n question(prompt: string): Promise<string>\n warn(...values: readonly unknown[]): void\n}\n\n/** Control-flow signal used when a non-terminating host records an immediate exit. */\nexport class ProcessExitError extends Error {\n readonly exitCode: number\n\n constructor(exitCode: number) {\n super(`Process requested exit with code ${exitCode}`)\n this.name = 'ProcessExitError'\n this.exitCode = exitCode\n }\n}\n\n/**\n * Structural process boundary for a command-line app.\n *\n * Production implementations may terminate from {@link exit}; recording hosts\n * may return after retaining the requested status.\n */\nexport interface ProcessHost {\n /** Full argument vector, including the executable and script entries. */\n readonly argv: readonly string[]\n /** Environment visible to this app invocation. */\n readonly environment: Readonly<Record<string, string | undefined>>\n readonly io: ProcessIO\n readonly isDevelopment: boolean\n exit(code: number): void\n onInterrupt(listener: () => Promise<void> | void): () => void\n}\n\n/**\n * Requests immediate process termination and prevents a recording host from\n * accidentally continuing through code that follows the exit point.\n */\nexport function exitProcess(host: Pick<ProcessHost, 'exit'>, exitCode: number): never {\n host.exit(exitCode)\n throw new ProcessExitError(exitCode)\n}\n", "import { ProcessExitError, type ProcessHost } from './ProcessHost.ts'\n\n/** Application entry point executed against an explicit process boundary. */\nexport type ProcessApplication = (host: ProcessHost) => Promise<void>\n\n/**\n * Where an unexpected failure originated.\n *\n * - `'parse'`: argument parsing or validation failed before any command\n * handler ran (for example a Yargs usage error).\n * - `'handler'`: the app body, command handler, or middleware failed after\n * arguments were accepted.\n */\nexport type FailureOrigin = 'handler' | 'parse'\n\n/**\n * Optional application-supplied failure policy. Returning a number selects\n * the exit code for that failure; returning `undefined` retains the default\n * exit code one. The `origin` argument distinguishes parse/validation\n * failures from handler failures so mappers such as `usage` (64) versus\n * `software` (70) need no runner-specific error sniffing. Intentional exits\n * carried by `ProcessExitError` are never offered to the mapper.\n *\n * Results outside the integer range 0\u2013255 (including `NaN`) and errors\n * thrown by the mapper itself fall back to exit code one; the mapper is\n * inside the failure boundary, not above it. Prefer drawing codes from\n * `SYS_EXITS`.\n */\nexport type FailureExitCodeMapper = (error: unknown, origin: FailureOrigin) => number | undefined\n\n/**\n * Resolves the exit code for an unexpected failure through an optional\n * mapper. A missing mapper, an `undefined` result, a thrown mapper, or a\n * result outside the integer range 0\u2013255 all resolve to the default exit\n * code one, keeping the failure boundary intact.\n */\nexport function resolveFailureExitCode(\n mapFailureToExitCode: FailureExitCodeMapper | undefined,\n error: unknown,\n origin: FailureOrigin,\n): number {\n if (mapFailureToExitCode === undefined) return 1\n try {\n const code = mapFailureToExitCode(error, origin)\n return code !== undefined && Number.isSafeInteger(code) && code >= 0 && code <= 255 ? code : 1\n } catch {\n return 1\n }\n}\n\nexport interface RunProcessApplicationOptions {\n readonly application: ProcessApplication\n readonly host: ProcessHost\n readonly mapFailureToExitCode?: FailureExitCodeMapper\n}\n\n/**\n * Runs one CLI app at the outermost process boundary.\n *\n * Immediate exits requested through `exitProcess` have already been\n * routed to the host and are absorbed here. Unexpected failures retain the\n * established CLI policy of logging details only in development before\n * requesting exit code one, or the code selected by `mapFailureToExitCode`.\n */\nexport async function runProcessApplication({\n application,\n host,\n mapFailureToExitCode,\n}: RunProcessApplicationOptions): Promise<void> {\n try {\n await application(host)\n } catch (error) {\n if (error instanceof ProcessExitError) return\n if (host.isDevelopment) host.io.error('An error occurred during startup:', error)\n host.exit(resolveFailureExitCode(mapFailureToExitCode, error, 'handler'))\n }\n}\n", "import type { ProcessHost } from './ProcessHost.ts'\n\n/** Lifecycle state of one active runtime session. */\nexport type RuntimeSessionState = 'active' | 'failed' | 'stopped' | 'stopping'\n\n/** Application-owned cleanup invoked when a runtime session stops. */\nexport type RuntimeStopDelegate = () => Promise<void> | void\n\n/** Application-owned work invoked for the first process interrupt. */\nexport type RuntimeInterruptListener = () => Promise<void> | void\n\n/**\n * Instance-scoped owner for one runtime cleanup operation.\n *\n * Every call to {@link stop} returns the same promise. A cleanup failure is\n * retained and rethrown without wrapping so callers can preserve their\n * existing fail-fast behavior.\n */\nexport class RuntimeSession {\n private _exitCode?: number\n private _exitHost?: Pick<ProcessHost, 'exit'>\n private _exitPromise?: Promise<void>\n private _hasInterruptBinding = false\n private _interruptDisposer?: () => void\n private _interruptPromise?: Promise<void>\n private _isInterruptDisposed = false\n private _state: RuntimeSessionState = 'active'\n private readonly _stopDelegate: RuntimeStopDelegate\n private _stopError?: unknown\n private _stopPromise?: Promise<void>\n\n constructor(stopDelegate: RuntimeStopDelegate) {\n this._stopDelegate = stopDelegate\n }\n\n get state(): RuntimeSessionState {\n return this._state\n }\n\n get stopError(): unknown {\n return this._stopError\n }\n\n bindInterrupt(host: Pick<ProcessHost, 'onInterrupt'>, listener: RuntimeInterruptListener): void {\n if (this._hasInterruptBinding) throw new Error('An interrupt listener is already bound to this runtime session.')\n if (this.state !== 'active') throw new Error('An interrupt listener cannot be bound after runtime shutdown has started.')\n\n this._hasInterruptBinding = true\n let isRegistrationComplete = false\n let isInterruptPending = false\n const handleInterrupt = (): Promise<void> | void => {\n if (!isRegistrationComplete) {\n isInterruptPending = true\n return\n }\n return this.handleInterrupt(listener)\n }\n this._interruptDisposer = host.onInterrupt(handleInterrupt)\n isRegistrationComplete = true\n\n // A structural host may deliver an interrupt while registering the listener.\n // Defer that delivery until the returned disposer is retained so it can be\n // removed after the interrupt handler settles.\n if (isInterruptPending) void this.handleInterrupt(listener)\n }\n\n /**\n * Coalesces competing process-exit requests for this session. Requests made\n * in the same turn retain the highest exit code and every caller observes\n * the same exactly-once exit operation.\n */\n requestExit(host: Pick<ProcessHost, 'exit'>, exitCode: number): Promise<void> {\n this._exitCode = Math.max(this._exitCode ?? exitCode, exitCode)\n this._exitHost ??= host\n this._exitPromise ??= Promise.try(async () => {\n await Promise.resolve()\n this._exitHost?.exit(this._exitCode ?? exitCode)\n })\n return this._exitPromise\n }\n\n stop(): Promise<void> {\n // Manual shutdown owns listener disposal immediately. Interrupt-driven\n // shutdown retains the listener until its handler settles so repeated\n // interrupts are coalesced instead of reaching the host's default handler.\n if (this._interruptPromise === undefined) this.disposeInterrupt()\n if (this._stopPromise === undefined) {\n this._state = 'stopping'\n this._stopPromise = Promise.try(async () => {\n await Promise.resolve()\n await this.stopOnce()\n })\n }\n return this._stopPromise\n }\n\n private disposeInterrupt(): void {\n if (!this._hasInterruptBinding || this._isInterruptDisposed) return\n this._isInterruptDisposed = true\n const dispose = this._interruptDisposer\n this._interruptDisposer = undefined\n dispose?.()\n }\n\n private handleInterrupt(listener: RuntimeInterruptListener): Promise<void> {\n if (this._interruptPromise === undefined) {\n if (this._isInterruptDisposed) return Promise.resolve()\n this._interruptPromise = Promise.try(async () => {\n await Promise.resolve()\n try {\n await listener()\n } finally {\n this.disposeInterrupt()\n }\n })\n }\n return this._interruptPromise\n }\n\n private async stopOnce(): Promise<void> {\n try {\n await this._stopDelegate()\n this._state = 'stopped'\n } catch (error) {\n this._stopError = error\n this._state = 'failed'\n throw error\n }\n }\n}\n", "/**\n * BSD `sysexits.h` exit codes shared across the CLI fleet.\n *\n * Only the subset actually used by apps is included so exit-code\n * policy stays reviewable. Use these with {@link exitProcess} or a\n * failure-to-exit-code mapper instead of repeating numeric literals.\n */\nexport const SYS_EXITS = {\n /** `EX_OK` (0): successful termination. */\n ok: 0,\n /** `EX_USAGE` (64): command line usage error, such as bad arguments or flags. */\n usage: 64,\n /** `EX_DATAERR` (65): input data was incorrect in some way. */\n dataErr: 65,\n /** `EX_NOINPUT` (66): an input file did not exist or was not readable. */\n noInput: 66,\n /** `EX_SOFTWARE` (70): internal software error not caused by the operating system. */\n software: 70,\n /** `EX_IOERR` (74): an error occurred while doing I/O on a file. */\n ioErr: 74,\n /** `EX_CONFIG` (78): something was found in an unconfigured or misconfigured state. */\n config: 78,\n} as const\n\n/** Exit code drawn from the supported `sysexits.h` vocabulary. */\nexport type SysExitCode = typeof SYS_EXITS[keyof typeof SYS_EXITS]\n"],
|
|
5
|
-
"mappings": "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
|
|
3
|
+
"sources": ["../../src/ActorBuilderCatalog.ts", "../../src/ActorSupervisor.ts", "../../src/CommandCatalog.ts", "../../src/ProcessHost.ts", "../../src/ProcessApplication.ts", "../../src/runServiceUntilInterrupt.ts", "../../src/RuntimeSession.ts", "../../src/sysExits.ts"],
|
|
4
|
+
"sourcesContent": ["export interface ActorBuilderDefinition<TContext, TActor> {\n readonly name: string\n build(context: TContext): Promise<TActor>\n}\n\nexport interface ActorBuilderCatalog<TContext, TActor> {\n readonly names: readonly string[]\n build(name: string, context: TContext): Promise<TActor>\n}\n\n/** Creates an ordered, instance-scoped actor builder catalog. */\nexport function createActorBuilderCatalog<TContext, TActor>(\n definitions: readonly ActorBuilderDefinition<TContext, TActor>[],\n): ActorBuilderCatalog<TContext, TActor> {\n const definitionsByName = new Map<string, ActorBuilderDefinition<TContext, TActor>>()\n const names: string[] = []\n for (const definition of definitions) {\n if (definitionsByName.has(definition.name)) {\n throw new Error(`Actor builder \"${definition.name}\" is already registered`)\n }\n definitionsByName.set(definition.name, definition)\n names.push(definition.name)\n }\n\n return {\n names: Object.freeze(names),\n async build(name: string, context: TContext): Promise<TActor> {\n const definition = definitionsByName.get(name)\n if (definition === undefined) throw new Error(`Unknown actor: ${name}`)\n return await definition.build(context)\n },\n }\n}\n", "import type {\n ManagedActor, ManagedActorLifecycleResult, ReadinessSignal, ReadyState,\n} from './ManagedActor.ts'\n\nexport type ActorSupervisorState = 'failed' | 'idle' | 'started' | 'starting' | 'stopped' | 'stopping'\n\nexport class ActorRegistrationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'ActorRegistrationError'\n }\n}\n\nexport class ActorSupervisorStateError extends Error {\n readonly state: ActorSupervisorState\n\n constructor(operation: string, state: ActorSupervisorState) {\n super(`Cannot ${operation} while actor supervisor is ${state}`)\n this.name = 'ActorSupervisorStateError'\n this.state = state\n }\n}\n\nexport class ActorStartupError extends Error {\n readonly actorName: string\n readonly rollbackErrors: readonly Error[]\n\n constructor(actorName: string, cause: unknown, rollbackErrors: readonly Error[]) {\n super(`Actor \"${actorName}\" failed to start`, { cause })\n this.name = 'ActorStartupError'\n this.actorName = actorName\n this.rollbackErrors = rollbackErrors\n }\n}\n\nexport class ActorReadinessError extends Error {\n readonly actorName: string\n\n constructor(actorName: string, cause: unknown) {\n super(`Actor \"${actorName}\" failed to become ready`, { cause })\n this.name = 'ActorReadinessError'\n this.actorName = actorName\n }\n}\n\nexport class ActorReadinessTimeoutError extends Error {\n readonly timeoutMs: number\n\n constructor(timeoutMs: number) {\n super(`Actors did not become ready within ${timeoutMs}ms`)\n this.name = 'ActorReadinessTimeoutError'\n this.timeoutMs = timeoutMs\n }\n}\n\nexport class ActorShutdownError extends AggregateError {\n constructor(errors: readonly Error[]) {\n super(errors, `${errors.length} actor(s) failed to stop`)\n this.name = 'ActorShutdownError'\n }\n}\n\ninterface TimerHost {\n clearTimeout(handle: unknown): void\n setTimeout(callback: () => void, timeoutMs: number): unknown\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error))\n}\n\nfunction assertLifecycleSucceeded(actor: ManagedActor, operation: 'start' | 'stop', result: ManagedActorLifecycleResult): void {\n if (result === false) {\n throw new Error(`Actor \"${actor.name}\" ${operation}() reported failure`)\n }\n}\n\nfunction assertTimeout(timeoutMs: number): void {\n if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {\n throw new RangeError('Readiness timeout must be a finite, non-negative number')\n }\n}\n\nasync function waitWithTimeout(signal: ReadinessSignal, timeoutMs: number): Promise<void> {\n const timerHost = globalThis as unknown as TimerHost\n let timeoutHandle: unknown\n const expired = new Promise<never>((_resolve, reject) => {\n timeoutHandle = timerHost.setTimeout(() => reject(new ActorReadinessTimeoutError(timeoutMs)), timeoutMs)\n })\n try {\n await Promise.race([signal.whenReady(timeoutMs), expired])\n } finally {\n timerHost.clearTimeout(timeoutHandle)\n }\n}\n\n/**\n * Instance-scoped coordinator for structural managed actors.\n *\n * Startup and readiness follow registration order. Rollback and ordinary shutdown\n * follow reverse startup order. A supervisor is single-use after it has stopped or\n * failed; construct a new instance for a new lifecycle.\n */\nexport class ActorSupervisor implements ReadinessSignal {\n private readonly _actorNames = new Set<string>()\n private readonly _actors: ManagedActor[] = []\n private _readyError?: Error\n private _startPromise?: Promise<void>\n private readonly _startedActors: ManagedActor[] = []\n private _state: ActorSupervisorState = 'idle'\n private _stopPromise?: Promise<void>\n\n constructor(actors: readonly ManagedActor[] = []) {\n for (const actor of actors) this.register(actor)\n }\n\n get actors(): readonly ManagedActor[] {\n return [...this._actors]\n }\n\n get readyError(): Error | undefined {\n if (this._readyError !== undefined) return this._readyError\n return this._actors.find(actor => actor.readyState === 'failed')?.readyError\n }\n\n get readyState(): ReadyState {\n if (this._readyError !== undefined || this._state === 'failed' || this._actors.some(actor => actor.readyState === 'failed')) {\n return 'failed'\n }\n if (this._state === 'started' && this._actors.every(actor => actor.readyState === 'ready')) return 'ready'\n return 'pending'\n }\n\n get state(): ActorSupervisorState {\n return this._state\n }\n\n isReady(): boolean {\n return this.readyState === 'ready'\n }\n\n register(actor: ManagedActor): void {\n if (this._state !== 'idle') throw new ActorSupervisorStateError('register an actor', this._state)\n if (actor.name.trim().length === 0) throw new ActorRegistrationError('Actor name must not be empty')\n if (this._actorNames.has(actor.name)) {\n throw new ActorRegistrationError(`Actor \"${actor.name}\" is already registered`)\n }\n this._actorNames.add(actor.name)\n this._actors.push(actor)\n }\n\n start(): Promise<void> {\n if (this._startPromise !== undefined) return this._startPromise\n if (this._state !== 'idle') return Promise.reject(new ActorSupervisorStateError('start', this._state))\n this._state = 'starting'\n this._startPromise = this.startActors()\n return this._startPromise\n }\n\n stop(): Promise<void> {\n this._stopPromise ??= this.stopActors()\n return this._stopPromise\n }\n\n async whenReady(timeoutMs?: number): Promise<void> {\n if (timeoutMs !== undefined) assertTimeout(timeoutMs)\n if (this._state === 'starting' && this._startPromise !== undefined) {\n await this._startPromise\n }\n if (this._state !== 'started') throw new ActorSupervisorStateError('wait for readiness', this._state)\n\n const startedAt = Date.now()\n for (const actor of this._actors) {\n try {\n if (timeoutMs === undefined) {\n await actor.whenReady()\n } else {\n const elapsed = Date.now() - startedAt\n const remainingTimeoutMs = timeoutMs - elapsed\n if (remainingTimeoutMs < 0) throw new ActorReadinessTimeoutError(timeoutMs)\n await waitWithTimeout(actor, remainingTimeoutMs)\n }\n } catch (error) {\n const readinessError = new ActorReadinessError(actor.name, error)\n this._readyError = readinessError\n throw readinessError\n }\n }\n }\n\n private async startActors(): Promise<void> {\n for (const actor of this._actors) {\n try {\n const result = await actor.start()\n assertLifecycleSucceeded(actor, 'start', result)\n this._startedActors.push(actor)\n } catch (error) {\n const rollbackErrors = await this.stopStartedActors()\n const startupError = new ActorStartupError(actor.name, error, rollbackErrors)\n this._readyError = startupError\n this._state = 'failed'\n throw startupError\n }\n }\n this._state = 'started'\n }\n\n private async stopActors(): Promise<void> {\n if (this._state === 'starting' && this._startPromise !== undefined) {\n try {\n await this._startPromise\n } catch {\n // Startup already rolled back every actor that reached started state.\n }\n }\n\n if (this._state === 'stopped') return\n if (this._state === 'idle' || (this._state === 'failed' && this._startedActors.length === 0)) {\n this._state = 'stopped'\n return\n }\n\n this._state = 'stopping'\n const errors = await this.stopStartedActors()\n this._state = 'stopped'\n if (errors.length > 0) throw new ActorShutdownError(errors)\n }\n\n private async stopStartedActors(): Promise<Error[]> {\n const errors: Error[] = []\n while (this._startedActors.length > 0) {\n const actor = this._startedActors.pop()\n if (actor === undefined) continue\n try {\n const result = await actor.stop()\n assertLifecycleSucceeded(actor, 'stop', result)\n } catch (error) {\n errors.push(new Error(`Actor \"${actor.name}\" failed to stop`, { cause: asError(error) }))\n }\n }\n return errors\n }\n}\n", "import type { ProcessHost } from './ProcessHost.ts'\n\n/** Process capability shared by every command created for one CLI app. */\nexport interface CliApplicationContext {\n readonly host: ProcessHost\n}\n\n/** Adapter-neutral factory for one parser-specific command object. */\nexport interface CommandDefinition<TContext extends CliApplicationContext, TCommand> {\n readonly create: (context: TContext) => TCommand\n /** Internal registration identity; it does not define parser syntax or aliases. */\n readonly id: string\n}\n\n/** Ordered command factories materialized for one app context. */\nexport interface CommandCatalog<TContext extends CliApplicationContext, TCommand> {\n readonly createCommands: (context: TContext) => readonly TCommand[]\n readonly ids: readonly string[]\n}\n\n/** Creates an immutable, adapter-neutral command catalog. */\nexport function createCommandCatalog<TContext extends CliApplicationContext, TCommand>(\n definitions: readonly CommandDefinition<TContext, TCommand>[],\n): CommandCatalog<TContext, TCommand> {\n const registeredIds = new Set<string>()\n const retainedDefinitions = definitions.map(definition => Object.freeze({\n create: definition.create,\n id: definition.id,\n }))\n const ids: string[] = []\n\n for (const definition of retainedDefinitions) {\n if (definition.id.trim().length === 0) throw new Error('Command id must not be empty')\n if (registeredIds.has(definition.id)) throw new Error(`Command \"${definition.id}\" is already registered`)\n registeredIds.add(definition.id)\n ids.push(definition.id)\n }\n\n return Object.freeze({\n ids: Object.freeze(ids),\n createCommands: (context: TContext): readonly TCommand[] =>\n Object.freeze(retainedDefinitions.map(definition => definition.create(context))),\n })\n}\n", "/** Process-facing input and output used by a command-line app. */\nexport interface ProcessIO {\n /** Available terminal width when output is attached to a sized terminal. */\n readonly columns?: number\n readonly isInteractive: boolean\n error(...values: readonly unknown[]): void\n log(...values: readonly unknown[]): void\n question(prompt: string): Promise<string>\n warn(...values: readonly unknown[]): void\n}\n\n/** Control-flow signal used when a non-terminating host records an immediate exit. */\nexport class ProcessExitError extends Error {\n readonly exitCode: number\n\n constructor(exitCode: number) {\n super(`Process requested exit with code ${exitCode}`)\n this.name = 'ProcessExitError'\n this.exitCode = exitCode\n }\n}\n\n/**\n * Structural process boundary for a command-line app.\n *\n * Production implementations may terminate from {@link exit}; recording hosts\n * may return after retaining the requested status.\n */\nexport interface ProcessHost {\n /** Full argument vector, including the executable and script entries. */\n readonly argv: readonly string[]\n /** Environment visible to this app invocation. */\n readonly environment: Readonly<Record<string, string | undefined>>\n readonly io: ProcessIO\n readonly isDevelopment: boolean\n exit(code: number): void\n onInterrupt(listener: () => Promise<void> | void): () => void\n}\n\n/**\n * Requests immediate process termination and prevents a recording host from\n * accidentally continuing through code that follows the exit point.\n */\nexport function exitProcess(host: Pick<ProcessHost, 'exit'>, exitCode: number): never {\n host.exit(exitCode)\n throw new ProcessExitError(exitCode)\n}\n", "import { ProcessExitError, type ProcessHost } from './ProcessHost.ts'\n\n/** Application entry point executed against an explicit process boundary. */\nexport type ProcessApplication = (host: ProcessHost) => Promise<void>\n\n/**\n * Where an unexpected failure originated.\n *\n * - `'parse'`: argument parsing or validation failed before any command\n * handler ran (for example a Yargs usage error).\n * - `'handler'`: the app body, command handler, or middleware failed after\n * arguments were accepted.\n */\nexport type FailureOrigin = 'handler' | 'parse'\n\n/**\n * Optional application-supplied failure policy. Returning a number selects\n * the exit code for that failure; returning `undefined` retains the default\n * exit code one. The `origin` argument distinguishes parse/validation\n * failures from handler failures so mappers such as `usage` (64) versus\n * `software` (70) need no runner-specific error sniffing. Intentional exits\n * carried by `ProcessExitError` are never offered to the mapper.\n *\n * Results outside the integer range 0\u2013255 (including `NaN`) and errors\n * thrown by the mapper itself fall back to exit code one; the mapper is\n * inside the failure boundary, not above it. Prefer drawing codes from\n * `SYS_EXITS`.\n */\nexport type FailureExitCodeMapper = (error: unknown, origin: FailureOrigin) => number | undefined\n\n/**\n * Resolves the exit code for an unexpected failure through an optional\n * mapper. A missing mapper, an `undefined` result, a thrown mapper, or a\n * result outside the integer range 0\u2013255 all resolve to the default exit\n * code one, keeping the failure boundary intact.\n */\nexport function resolveFailureExitCode(\n mapFailureToExitCode: FailureExitCodeMapper | undefined,\n error: unknown,\n origin: FailureOrigin,\n): number {\n if (mapFailureToExitCode === undefined) return 1\n try {\n const code = mapFailureToExitCode(error, origin)\n return code !== undefined && Number.isSafeInteger(code) && code >= 0 && code <= 255 ? code : 1\n } catch {\n return 1\n }\n}\n\nexport interface RunProcessApplicationOptions {\n readonly application: ProcessApplication\n readonly host: ProcessHost\n readonly mapFailureToExitCode?: FailureExitCodeMapper\n}\n\n/**\n * Runs one CLI app at the outermost process boundary.\n *\n * Immediate exits requested through `exitProcess` have already been\n * routed to the host and are absorbed here. Unexpected failures retain the\n * established CLI policy of logging details only in development before\n * requesting exit code one, or the code selected by `mapFailureToExitCode`.\n */\nexport async function runProcessApplication({\n application,\n host,\n mapFailureToExitCode,\n}: RunProcessApplicationOptions): Promise<void> {\n try {\n await application(host)\n } catch (error) {\n if (error instanceof ProcessExitError) return\n if (host.isDevelopment) host.io.error('An error occurred during startup:', error)\n host.exit(resolveFailureExitCode(mapFailureToExitCode, error, 'handler'))\n }\n}\n", "import type { ProcessHost } from './ProcessHost.ts'\n\n/** Application-owned cleanup invoked on the first process interrupt. */\nexport type ServiceStop = () => Promise<void> | void\n\n/**\n * Blocks until the first process interrupt (SIGINT/SIGTERM via the host),\n * disposes the listener, then awaits `stop()`.\n *\n * Resolves once shutdown completes so the caller can drain to a clean exit.\n * Rejects if `stop()` fails so the failure surfaces through the normal exit-code\n * mapper (typically `software`, 70) rather than being reported as success.\n *\n * Use this for long-running command handlers that start a server or actor and\n * should exit only when the operator interrupts the process.\n */\nexport async function runServiceUntilInterrupt(\n host: Pick<ProcessHost, 'onInterrupt'>,\n stop: ServiceStop,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const dispose = host.onInterrupt(async () => {\n dispose()\n try {\n await stop()\n resolve()\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)))\n }\n })\n })\n}\n", "import type { ProcessHost } from './ProcessHost.ts'\n\n/** Lifecycle state of one active runtime session. */\nexport type RuntimeSessionState = 'active' | 'failed' | 'stopped' | 'stopping'\n\n/** Application-owned cleanup invoked when a runtime session stops. */\nexport type RuntimeStopDelegate = () => Promise<void> | void\n\n/** Application-owned work invoked for the first process interrupt. */\nexport type RuntimeInterruptListener = () => Promise<void> | void\n\n/**\n * Instance-scoped owner for one runtime cleanup operation.\n *\n * Every call to {@link stop} returns the same promise. A cleanup failure is\n * retained and rethrown without wrapping so callers can preserve their\n * existing fail-fast behavior.\n */\nexport class RuntimeSession {\n private _exitCode?: number\n private _exitHost?: Pick<ProcessHost, 'exit'>\n private _exitPromise?: Promise<void>\n private _hasInterruptBinding = false\n private _interruptDisposer?: () => void\n private _interruptPromise?: Promise<void>\n private _isInterruptDisposed = false\n private _state: RuntimeSessionState = 'active'\n private readonly _stopDelegate: RuntimeStopDelegate\n private _stopError?: unknown\n private _stopPromise?: Promise<void>\n\n constructor(stopDelegate: RuntimeStopDelegate) {\n this._stopDelegate = stopDelegate\n }\n\n get state(): RuntimeSessionState {\n return this._state\n }\n\n get stopError(): unknown {\n return this._stopError\n }\n\n bindInterrupt(host: Pick<ProcessHost, 'onInterrupt'>, listener: RuntimeInterruptListener): void {\n if (this._hasInterruptBinding) throw new Error('An interrupt listener is already bound to this runtime session.')\n if (this.state !== 'active') throw new Error('An interrupt listener cannot be bound after runtime shutdown has started.')\n\n this._hasInterruptBinding = true\n let isRegistrationComplete = false\n let isInterruptPending = false\n const handleInterrupt = (): Promise<void> | void => {\n if (!isRegistrationComplete) {\n isInterruptPending = true\n return\n }\n return this.handleInterrupt(listener)\n }\n this._interruptDisposer = host.onInterrupt(handleInterrupt)\n isRegistrationComplete = true\n\n // A structural host may deliver an interrupt while registering the listener.\n // Defer that delivery until the returned disposer is retained so it can be\n // removed after the interrupt handler settles.\n if (isInterruptPending) void this.handleInterrupt(listener)\n }\n\n /**\n * Coalesces competing process-exit requests for this session. Requests made\n * in the same turn retain the highest exit code and every caller observes\n * the same exactly-once exit operation.\n */\n requestExit(host: Pick<ProcessHost, 'exit'>, exitCode: number): Promise<void> {\n this._exitCode = Math.max(this._exitCode ?? exitCode, exitCode)\n this._exitHost ??= host\n this._exitPromise ??= Promise.try(async () => {\n await Promise.resolve()\n this._exitHost?.exit(this._exitCode ?? exitCode)\n })\n return this._exitPromise\n }\n\n stop(): Promise<void> {\n // Manual shutdown owns listener disposal immediately. Interrupt-driven\n // shutdown retains the listener until its handler settles so repeated\n // interrupts are coalesced instead of reaching the host's default handler.\n if (this._interruptPromise === undefined) this.disposeInterrupt()\n if (this._stopPromise === undefined) {\n this._state = 'stopping'\n this._stopPromise = Promise.try(async () => {\n await Promise.resolve()\n await this.stopOnce()\n })\n }\n return this._stopPromise\n }\n\n private disposeInterrupt(): void {\n if (!this._hasInterruptBinding || this._isInterruptDisposed) return\n this._isInterruptDisposed = true\n const dispose = this._interruptDisposer\n this._interruptDisposer = undefined\n dispose?.()\n }\n\n private handleInterrupt(listener: RuntimeInterruptListener): Promise<void> {\n if (this._interruptPromise === undefined) {\n if (this._isInterruptDisposed) return Promise.resolve()\n this._interruptPromise = Promise.try(async () => {\n await Promise.resolve()\n try {\n await listener()\n } finally {\n this.disposeInterrupt()\n }\n })\n }\n return this._interruptPromise\n }\n\n private async stopOnce(): Promise<void> {\n try {\n await this._stopDelegate()\n this._state = 'stopped'\n } catch (error) {\n this._stopError = error\n this._state = 'failed'\n throw error\n }\n }\n}\n", "/**\n * BSD `sysexits.h` exit codes shared across the CLI fleet.\n *\n * Only the subset actually used by apps is included so exit-code\n * policy stays reviewable. Use these with {@link exitProcess} or a\n * failure-to-exit-code mapper instead of repeating numeric literals.\n */\nexport const SYS_EXITS = {\n /** `EX_OK` (0): successful termination. */\n ok: 0,\n /** `EX_USAGE` (64): command line usage error, such as bad arguments or flags. */\n usage: 64,\n /** `EX_DATAERR` (65): input data was incorrect in some way. */\n dataErr: 65,\n /** `EX_NOINPUT` (66): an input file did not exist or was not readable. */\n noInput: 66,\n /** `EX_SOFTWARE` (70): internal software error not caused by the operating system. */\n software: 70,\n /** `EX_IOERR` (74): an error occurred while doing I/O on a file. */\n ioErr: 74,\n /** `EX_CONFIG` (78): something was found in an unconfigured or misconfigured state. */\n config: 78,\n} as const\n\n/** Exit code drawn from the supported `sysexits.h` vocabulary. */\nexport type SysExitCode = typeof SYS_EXITS[keyof typeof SYS_EXITS]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ProcessHost } from './ProcessHost.ts';
|
|
2
|
+
/** Application-owned cleanup invoked on the first process interrupt. */
|
|
3
|
+
export type ServiceStop = () => Promise<void> | void;
|
|
4
|
+
/**
|
|
5
|
+
* Blocks until the first process interrupt (SIGINT/SIGTERM via the host),
|
|
6
|
+
* disposes the listener, then awaits `stop()`.
|
|
7
|
+
*
|
|
8
|
+
* Resolves once shutdown completes so the caller can drain to a clean exit.
|
|
9
|
+
* Rejects if `stop()` fails so the failure surfaces through the normal exit-code
|
|
10
|
+
* mapper (typically `software`, 70) rather than being reported as success.
|
|
11
|
+
*
|
|
12
|
+
* Use this for long-running command handlers that start a server or actor and
|
|
13
|
+
* should exit only when the operator interrupts the process.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runServiceUntilInterrupt(host: Pick<ProcessHost, 'onInterrupt'>, stop: ServiceStop): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=runServiceUntilInterrupt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runServiceUntilInterrupt.d.ts","sourceRoot":"","sources":["../../src/runServiceUntilInterrupt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAEpD;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,EACtC,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,IAAI,CAAC,CAYf"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ariestools/cli-kit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Reusable actor lifecycle core for command-line applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ariestools",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"LICENSE"
|
|
44
44
|
],
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@ariestools/toolchain": "~8.7.
|
|
47
|
-
"@ariestools/tsconfig": "~8.7.
|
|
46
|
+
"@ariestools/toolchain": "~8.7.24",
|
|
47
|
+
"@ariestools/tsconfig": "~8.7.24",
|
|
48
48
|
"eslint": "~10.7.0",
|
|
49
49
|
"eslint-import-resolver-typescript": "~4.4.5",
|
|
50
50
|
"typescript": "~6.0.3",
|