@btravstack/core 0.12.0 → 0.13.0
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 +5 -4
- package/dist/index.cjs +36 -8
- package/dist/index.d.cts +44 -33
- package/dist/index.d.mts +44 -33
- package/dist/index.mjs +36 -8
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -68,8 +68,8 @@ const ticker: Runtime<typeof Greeter> = {
|
|
|
68
68
|
// and stays out of it. A timer has nowhere to return one, so it observes
|
|
69
69
|
// it instead; dropping it would hide the work's `Err` *and* a `Defect`.
|
|
70
70
|
void host
|
|
71
|
-
.run({ kind: "tick", id: `${Date.now()}` }, (
|
|
72
|
-
signal.aborted ? Ok("") : Ok(ctx.get(Greeter).greet("world")),
|
|
71
|
+
.run({ kind: "tick", id: `${Date.now()}` }, (unit, signal) =>
|
|
72
|
+
signal.aborted ? Ok("") : Ok(unit.ctx.get(Greeter).greet("world")),
|
|
73
73
|
)
|
|
74
74
|
.tapFailure((failure) => {
|
|
75
75
|
process.stderr.write(`${JSON.stringify({ tick: failure.tag })}\n`);
|
|
@@ -145,8 +145,9 @@ them and none should own a private copy:
|
|
|
145
145
|
bad value is a `ConfigInvalid` naming every fault at once, and exit `78`.
|
|
146
146
|
- **Teardown on every path**, with failing finalisers in
|
|
147
147
|
`ExitReport.teardownErrors`, never masking the exit reason.
|
|
148
|
-
- **A per-unit scope no handler manages** —
|
|
149
|
-
`
|
|
148
|
+
- **A per-unit scope no handler manages** — the runtime calls
|
|
149
|
+
`unit.fork(module, seed)` from inside `host.run`'s work callback, and the
|
|
150
|
+
kernel closes the scope when the unit settles.
|
|
150
151
|
- **An ambient record, not an ambient container** — `currentUnit()` reads
|
|
151
152
|
`{ unitId, traceId, tenantId, signal }`; services never travel
|
|
152
153
|
there. `signal` is the very `AbortSignal` the unit's work callback is handed,
|
package/dist/index.cjs
CHANGED
|
@@ -613,15 +613,43 @@ const start = (module, options = {}) => {
|
|
|
613
613
|
runtimeName = runtime.name;
|
|
614
614
|
healthChecks = ctx.get(HealthChecks);
|
|
615
615
|
const runtimeCtx = ctx;
|
|
616
|
-
const unit = options.unit;
|
|
617
616
|
const run = (meta, work) => registry.run(meta, (signal) => {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
617
|
+
const settled = Promise.withResolvers();
|
|
618
|
+
let closing;
|
|
619
|
+
let hasSettled = false;
|
|
620
|
+
const fork = (module, seed) => {
|
|
621
|
+
if (hasSettled) return (0, unthrown.fromSafePromise)(Promise.reject(/* @__PURE__ */ new Error("a unit forks after it has already settled")));
|
|
622
|
+
if (closing !== void 0) return (0, unthrown.fromSafePromise)(Promise.reject(/* @__PURE__ */ new Error("a unit forks its scope once")));
|
|
623
|
+
const ready = Promise.withResolvers();
|
|
624
|
+
closing = _btravstack_di.Module.forkScope(ctx, module, (forked) => {
|
|
625
|
+
ready.resolve(forked);
|
|
626
|
+
return (0, unthrown.fromSafePromise)(settled.promise);
|
|
627
|
+
}, {
|
|
628
|
+
seed,
|
|
629
|
+
onTeardownError: (port, cause) => emit({
|
|
630
|
+
type: "teardownError",
|
|
631
|
+
port,
|
|
632
|
+
cause
|
|
633
|
+
})
|
|
634
|
+
}).recoverDefect((cause) => {
|
|
635
|
+
ready.reject(cause);
|
|
636
|
+
return (0, unthrown.Ok)();
|
|
637
|
+
}).get();
|
|
638
|
+
return (0, unthrown.fromSafePromise)(ready.promise);
|
|
639
|
+
};
|
|
640
|
+
const outcome = (async () => {
|
|
641
|
+
try {
|
|
642
|
+
return await work({
|
|
643
|
+
ctx: runtimeCtx,
|
|
644
|
+
fork
|
|
645
|
+
}, signal);
|
|
646
|
+
} finally {
|
|
647
|
+
hasSettled = true;
|
|
648
|
+
settled.resolve();
|
|
649
|
+
if (closing !== void 0) await closing;
|
|
650
|
+
}
|
|
651
|
+
})();
|
|
652
|
+
return (0, unthrown.fromSafePromise)(outcome).flatMap((result) => result);
|
|
625
653
|
});
|
|
626
654
|
const host = {
|
|
627
655
|
ctx: runtimeCtx,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AsyncResult, Result } from "unthrown";
|
|
2
|
-
import { AnyPort, Context, Module, PortClass, PortInstance, Scope, ServiceOf } from "@btravstack/di";
|
|
2
|
+
import { AnyPort, Context, DependencyGate, Module, PortClass, PortInstance, Scope, SeedEntry, ServiceOf } from "@btravstack/di";
|
|
3
3
|
import { Env, Environment } from "@btravstack/config";
|
|
4
4
|
//#region src/instrumentation.d.ts
|
|
5
5
|
/**
|
|
@@ -372,11 +372,28 @@ declare class RuntimeStartFailed extends RuntimeStartFailed_base<{
|
|
|
372
372
|
* `UnitMeta.id` must be unique per unit unless a `traceId` is supplied — see
|
|
373
373
|
* {@link UnitMeta}.
|
|
374
374
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
375
|
+
* A runtime forks the unit's scope itself, through `UnitHost.fork`, at the
|
|
376
|
+
* moment it holds the unit's input; the kernel closes that scope when the
|
|
377
|
+
* unit settles, after the response is flushed. Work that subscribes to an
|
|
378
|
+
* event after a `fork` must first check whether it already fired.
|
|
378
379
|
*/
|
|
379
|
-
type RunUnit<Resolves extends AnyPort> = <T, E>(meta: UnitMeta, work: (
|
|
380
|
+
type RunUnit<Resolves extends AnyPort> = <T, E>(meta: UnitMeta, work: (unit: UnitHost<Resolves>, signal: AbortSignal) => ReturnType<UnitWork<T, E>>) => AsyncResult<T, E>;
|
|
381
|
+
/**
|
|
382
|
+
* What the kernel hands a unit's work: the application context, and the one
|
|
383
|
+
* way to open the unit's own scope.
|
|
384
|
+
*
|
|
385
|
+
* `fork` builds `module` over the application context plus `seed` and hands
|
|
386
|
+
* the forked context back. The scope is torn down when the unit closes —
|
|
387
|
+
* inside the registry's unit, so the unit is not counted closed until its
|
|
388
|
+
* finalisers have run, and inside the unit's ambient record, so a teardown
|
|
389
|
+
* log line carries the unit's ids. A construction failure rides the unit's
|
|
390
|
+
* defect path. A unit forks once; a second call is a defect, and so is a call
|
|
391
|
+
* made after the unit has settled — nothing awaits that scope's teardown.
|
|
392
|
+
*/
|
|
393
|
+
type UnitHost<Resolves extends AnyPort> = {
|
|
394
|
+
readonly ctx: Context<InstanceType<Resolves>>;
|
|
395
|
+
readonly fork: <UnitX, N, Seeded extends AnyPort = never>(module: Module<UnitX, never, N> & DependencyGate<Exclude<N, InstanceType<Resolves> | InstanceType<Seeded> | Scope>>, seed: readonly SeedEntry<Seeded>[]) => AsyncResult<Context<InstanceType<Resolves> | UnitX | InstanceType<Seeded>>, never>;
|
|
396
|
+
};
|
|
380
397
|
/**
|
|
381
398
|
* What a runtime is handed at `start`: the application services **and** the
|
|
382
399
|
* kernel's {@link RunUnit}. Handing it a bare `Context` would leave every
|
|
@@ -388,8 +405,8 @@ type RunUnit<Resolves extends AnyPort> = <T, E>(meta: UnitMeta, work: (ctx: Cont
|
|
|
388
405
|
* {@link RunUnit}), and `UnitMeta.id` must be unique per unit unless a
|
|
389
406
|
* `traceId` is supplied (see {@link UnitMeta}).
|
|
390
407
|
*
|
|
391
|
-
* `ctx` is the **application** context: a port a
|
|
392
|
-
*
|
|
408
|
+
* `ctx` is the **application** context: a port a unit's `fork` module provides
|
|
409
|
+
* exists only in the `Context` `fork` hands back, so a runtime naming it in
|
|
393
410
|
* `resolves` is rejected at `start`'s call site.
|
|
394
411
|
*/
|
|
395
412
|
type RuntimeHost<Resolves extends AnyPort> = {
|
|
@@ -430,8 +447,12 @@ declare const RuntimePort: PortClass<"Runtime">;
|
|
|
430
447
|
type RuntimeInstance = InstanceType<PortClass<"Runtime">>;
|
|
431
448
|
/** The `Runtime<Resolves, Info>` a module exports, or `never` when it exports none. */
|
|
432
449
|
type RuntimeOf<X> = ServiceOf<Extract<X, RuntimeInstance>>;
|
|
433
|
-
type RuntimeResolvesOf<X> = RuntimeOf<X> extends
|
|
434
|
-
|
|
450
|
+
type RuntimeResolvesOf<X> = RuntimeOf<X> extends {
|
|
451
|
+
readonly resolves: readonly (infer Resolves extends AnyPort)[];
|
|
452
|
+
} ? Resolves : never;
|
|
453
|
+
type RuntimeInfoOf<X> = RuntimeOf<X> extends {
|
|
454
|
+
readonly start: (host: never) => AsyncResult<Serving<infer Info>, RuntimeStartFailed>;
|
|
455
|
+
} ? Info : never;
|
|
435
456
|
/**
|
|
436
457
|
* The trace id inside a W3C `traceparent` header, and nothing else of it.
|
|
437
458
|
*
|
|
@@ -555,26 +576,13 @@ type ExitReport = {
|
|
|
555
576
|
readonly teardownErrors: readonly TeardownError[];
|
|
556
577
|
readonly uptimeMs: number;
|
|
557
578
|
};
|
|
558
|
-
type StartOptions
|
|
579
|
+
type StartOptions = {
|
|
559
580
|
/**
|
|
560
581
|
* The environment the graph is configured from, provided to it as the `Env`
|
|
561
582
|
* port and read for the kernel's own `PROBE_PORT`. Defaults to
|
|
562
583
|
* `process.env`.
|
|
563
584
|
*/
|
|
564
585
|
readonly env?: Environment;
|
|
565
|
-
/**
|
|
566
|
-
* A module forked around **every unit** — a unit being one bounded piece of
|
|
567
|
-
* work, whatever the transport calls it: an HTTP request, a Temporal
|
|
568
|
-
* activity, an AMQP delivery. Built as the unit opens, torn down as it
|
|
569
|
-
* closes — while the unit's ambient record is still open — reading anything
|
|
570
|
-
* the application context carries.
|
|
571
|
-
*
|
|
572
|
-
* A failing unit finaliser is reported as a `teardownError` event and never
|
|
573
|
-
* in `ExitReport.teardownErrors`. With this option the unit's work runs only
|
|
574
|
-
* once the fork is built, so a runtime that subscribes to an event from
|
|
575
|
-
* inside its work must be ready for it to have already fired.
|
|
576
|
-
*/
|
|
577
|
-
readonly unit?: Module<UnitX, never, UnitNeeds>;
|
|
578
586
|
readonly clock?: Clock;
|
|
579
587
|
readonly signals?: boolean;
|
|
580
588
|
/**
|
|
@@ -627,15 +635,18 @@ type RunningApp<E, Info = never> = {
|
|
|
627
635
|
/**
|
|
628
636
|
* The phantom marker `start`, `runMain` and `Boot` all intersect onto their
|
|
629
637
|
* `module` parameter: `unknown` — and invisible — when nothing the module
|
|
630
|
-
* needs is unprovided, it exports a runtime
|
|
631
|
-
* runtime resolves
|
|
632
|
-
*
|
|
633
|
-
* not match.
|
|
638
|
+
* needs is unprovided, it exports a runtime and its exports cover what that
|
|
639
|
+
* runtime resolves; a diagnostic otherwise, printed at the call site as the
|
|
640
|
+
* parameter type the argument did not match.
|
|
634
641
|
*
|
|
635
642
|
* `unknown` is the satisfied arm because intersecting it leaves the module
|
|
636
643
|
* type untouched. A runtime's `resolves` is checked against the module's
|
|
637
|
-
* exports only
|
|
638
|
-
*
|
|
644
|
+
* exports only: `RuntimeHost.ctx` is the application context, so a port only
|
|
645
|
+
* a `fork` provides would resolve to nothing there. A `fork`'s own needs are
|
|
646
|
+
* not this marker's business at all — a `fork` module is forked over the
|
|
647
|
+
* application context, so its needs are exactly what a starter's own `needs`
|
|
648
|
+
* channel already asks the composition root to supply, and di's ordinary
|
|
649
|
+
* `UNSATISFIED DEPENDENCIES` gate is what refuses a root that does not.
|
|
639
650
|
*
|
|
640
651
|
* **Unmet needs are checked FIRST, and answered in di's own words rather than
|
|
641
652
|
* a sentence of the kernel's.** A root that forgot `provides: [router]` used
|
|
@@ -654,10 +665,10 @@ type RunningApp<E, Info = never> = {
|
|
|
654
665
|
* name is reached.
|
|
655
666
|
*/
|
|
656
667
|
type PortIdOf<P> = P extends PortInstance<infer Id, infer _Service> ? Id : P;
|
|
657
|
-
type StartGate<X,
|
|
668
|
+
type StartGate<X, N = never> = [Exclude<N, Scope | Env>] extends [never] ? [Extract<X, RuntimeInstance>] extends [never] ? "NO RUNTIME — the module exports no port declared over RuntimePort" : [InstanceType<RuntimeResolvesOf<X>>] extends [X] ? unknown : "UNSATISFIED RUNTIME PORTS — the runtime resolves a port the module does not export" : {
|
|
658
669
|
readonly "UNSATISFIED DEPENDENCIES — nothing provides": PortIdOf<Exclude<N, Scope | Env>>;
|
|
659
670
|
};
|
|
660
|
-
declare const start: <X, E, N
|
|
671
|
+
declare const start: <X, E, N>(module: Module<X, E, N> & StartGate<X, N>, options?: StartOptions) => RunningApp<E, RuntimeInfoOf<X>>;
|
|
661
672
|
//#endregion
|
|
662
673
|
//#region src/run-main.d.ts
|
|
663
674
|
/**
|
|
@@ -692,6 +703,6 @@ declare const start: <X, E, N, UnitX = never, UnitNeeds = never>(module: Module<
|
|
|
692
703
|
* await runMain(OrderApi);
|
|
693
704
|
* ```
|
|
694
705
|
*/
|
|
695
|
-
declare const runMain: <X, E, N
|
|
706
|
+
declare const runMain: <X, E, N>(module: Module<X, E, N> & StartGate<X, N>, options?: StartOptions, exit?: (code: number) => void) => Promise<void>;
|
|
696
707
|
//#endregion
|
|
697
|
-
export { type Attributes, type Clock, type ComponentHealth, type Counter, type DrainReport, type EventSink, type ExitReport, type HealthCheck, HealthCheckFailed, HealthChecks, type HealthReport, type Histogram, type InstrumentationLoader, Instrumentations, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, Observers, type Operation, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Settle, type Settled, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, noObserver, observe, releasedBy, runHealthChecks, runMain, start, stderrSink, systemClock, traceIdOfTraceparent };
|
|
708
|
+
export { type Attributes, type Clock, type ComponentHealth, type Counter, type DrainReport, type EventSink, type ExitReport, type HealthCheck, HealthCheckFailed, HealthChecks, type HealthReport, type Histogram, type InstrumentationLoader, Instrumentations, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, Observers, type Operation, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Settle, type Settled, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitHost, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, noObserver, observe, releasedBy, runHealthChecks, runMain, start, stderrSink, systemClock, traceIdOfTraceparent };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyPort, Context, Module, PortClass, PortInstance, Scope, ServiceOf } from "@btravstack/di";
|
|
1
|
+
import { AnyPort, Context, DependencyGate, Module, PortClass, PortInstance, Scope, SeedEntry, ServiceOf } from "@btravstack/di";
|
|
2
2
|
import { AsyncResult, Result } from "unthrown";
|
|
3
3
|
import { Env, Environment } from "@btravstack/config";
|
|
4
4
|
//#region src/instrumentation.d.ts
|
|
@@ -372,11 +372,28 @@ declare class RuntimeStartFailed extends RuntimeStartFailed_base<{
|
|
|
372
372
|
* `UnitMeta.id` must be unique per unit unless a `traceId` is supplied — see
|
|
373
373
|
* {@link UnitMeta}.
|
|
374
374
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
375
|
+
* A runtime forks the unit's scope itself, through `UnitHost.fork`, at the
|
|
376
|
+
* moment it holds the unit's input; the kernel closes that scope when the
|
|
377
|
+
* unit settles, after the response is flushed. Work that subscribes to an
|
|
378
|
+
* event after a `fork` must first check whether it already fired.
|
|
378
379
|
*/
|
|
379
|
-
type RunUnit<Resolves extends AnyPort> = <T, E>(meta: UnitMeta, work: (
|
|
380
|
+
type RunUnit<Resolves extends AnyPort> = <T, E>(meta: UnitMeta, work: (unit: UnitHost<Resolves>, signal: AbortSignal) => ReturnType<UnitWork<T, E>>) => AsyncResult<T, E>;
|
|
381
|
+
/**
|
|
382
|
+
* What the kernel hands a unit's work: the application context, and the one
|
|
383
|
+
* way to open the unit's own scope.
|
|
384
|
+
*
|
|
385
|
+
* `fork` builds `module` over the application context plus `seed` and hands
|
|
386
|
+
* the forked context back. The scope is torn down when the unit closes —
|
|
387
|
+
* inside the registry's unit, so the unit is not counted closed until its
|
|
388
|
+
* finalisers have run, and inside the unit's ambient record, so a teardown
|
|
389
|
+
* log line carries the unit's ids. A construction failure rides the unit's
|
|
390
|
+
* defect path. A unit forks once; a second call is a defect, and so is a call
|
|
391
|
+
* made after the unit has settled — nothing awaits that scope's teardown.
|
|
392
|
+
*/
|
|
393
|
+
type UnitHost<Resolves extends AnyPort> = {
|
|
394
|
+
readonly ctx: Context<InstanceType<Resolves>>;
|
|
395
|
+
readonly fork: <UnitX, N, Seeded extends AnyPort = never>(module: Module<UnitX, never, N> & DependencyGate<Exclude<N, InstanceType<Resolves> | InstanceType<Seeded> | Scope>>, seed: readonly SeedEntry<Seeded>[]) => AsyncResult<Context<InstanceType<Resolves> | UnitX | InstanceType<Seeded>>, never>;
|
|
396
|
+
};
|
|
380
397
|
/**
|
|
381
398
|
* What a runtime is handed at `start`: the application services **and** the
|
|
382
399
|
* kernel's {@link RunUnit}. Handing it a bare `Context` would leave every
|
|
@@ -388,8 +405,8 @@ type RunUnit<Resolves extends AnyPort> = <T, E>(meta: UnitMeta, work: (ctx: Cont
|
|
|
388
405
|
* {@link RunUnit}), and `UnitMeta.id` must be unique per unit unless a
|
|
389
406
|
* `traceId` is supplied (see {@link UnitMeta}).
|
|
390
407
|
*
|
|
391
|
-
* `ctx` is the **application** context: a port a
|
|
392
|
-
*
|
|
408
|
+
* `ctx` is the **application** context: a port a unit's `fork` module provides
|
|
409
|
+
* exists only in the `Context` `fork` hands back, so a runtime naming it in
|
|
393
410
|
* `resolves` is rejected at `start`'s call site.
|
|
394
411
|
*/
|
|
395
412
|
type RuntimeHost<Resolves extends AnyPort> = {
|
|
@@ -430,8 +447,12 @@ declare const RuntimePort: PortClass<"Runtime">;
|
|
|
430
447
|
type RuntimeInstance = InstanceType<PortClass<"Runtime">>;
|
|
431
448
|
/** The `Runtime<Resolves, Info>` a module exports, or `never` when it exports none. */
|
|
432
449
|
type RuntimeOf<X> = ServiceOf<Extract<X, RuntimeInstance>>;
|
|
433
|
-
type RuntimeResolvesOf<X> = RuntimeOf<X> extends
|
|
434
|
-
|
|
450
|
+
type RuntimeResolvesOf<X> = RuntimeOf<X> extends {
|
|
451
|
+
readonly resolves: readonly (infer Resolves extends AnyPort)[];
|
|
452
|
+
} ? Resolves : never;
|
|
453
|
+
type RuntimeInfoOf<X> = RuntimeOf<X> extends {
|
|
454
|
+
readonly start: (host: never) => AsyncResult<Serving<infer Info>, RuntimeStartFailed>;
|
|
455
|
+
} ? Info : never;
|
|
435
456
|
/**
|
|
436
457
|
* The trace id inside a W3C `traceparent` header, and nothing else of it.
|
|
437
458
|
*
|
|
@@ -555,26 +576,13 @@ type ExitReport = {
|
|
|
555
576
|
readonly teardownErrors: readonly TeardownError[];
|
|
556
577
|
readonly uptimeMs: number;
|
|
557
578
|
};
|
|
558
|
-
type StartOptions
|
|
579
|
+
type StartOptions = {
|
|
559
580
|
/**
|
|
560
581
|
* The environment the graph is configured from, provided to it as the `Env`
|
|
561
582
|
* port and read for the kernel's own `PROBE_PORT`. Defaults to
|
|
562
583
|
* `process.env`.
|
|
563
584
|
*/
|
|
564
585
|
readonly env?: Environment;
|
|
565
|
-
/**
|
|
566
|
-
* A module forked around **every unit** — a unit being one bounded piece of
|
|
567
|
-
* work, whatever the transport calls it: an HTTP request, a Temporal
|
|
568
|
-
* activity, an AMQP delivery. Built as the unit opens, torn down as it
|
|
569
|
-
* closes — while the unit's ambient record is still open — reading anything
|
|
570
|
-
* the application context carries.
|
|
571
|
-
*
|
|
572
|
-
* A failing unit finaliser is reported as a `teardownError` event and never
|
|
573
|
-
* in `ExitReport.teardownErrors`. With this option the unit's work runs only
|
|
574
|
-
* once the fork is built, so a runtime that subscribes to an event from
|
|
575
|
-
* inside its work must be ready for it to have already fired.
|
|
576
|
-
*/
|
|
577
|
-
readonly unit?: Module<UnitX, never, UnitNeeds>;
|
|
578
586
|
readonly clock?: Clock;
|
|
579
587
|
readonly signals?: boolean;
|
|
580
588
|
/**
|
|
@@ -627,15 +635,18 @@ type RunningApp<E, Info = never> = {
|
|
|
627
635
|
/**
|
|
628
636
|
* The phantom marker `start`, `runMain` and `Boot` all intersect onto their
|
|
629
637
|
* `module` parameter: `unknown` — and invisible — when nothing the module
|
|
630
|
-
* needs is unprovided, it exports a runtime
|
|
631
|
-
* runtime resolves
|
|
632
|
-
*
|
|
633
|
-
* not match.
|
|
638
|
+
* needs is unprovided, it exports a runtime and its exports cover what that
|
|
639
|
+
* runtime resolves; a diagnostic otherwise, printed at the call site as the
|
|
640
|
+
* parameter type the argument did not match.
|
|
634
641
|
*
|
|
635
642
|
* `unknown` is the satisfied arm because intersecting it leaves the module
|
|
636
643
|
* type untouched. A runtime's `resolves` is checked against the module's
|
|
637
|
-
* exports only
|
|
638
|
-
*
|
|
644
|
+
* exports only: `RuntimeHost.ctx` is the application context, so a port only
|
|
645
|
+
* a `fork` provides would resolve to nothing there. A `fork`'s own needs are
|
|
646
|
+
* not this marker's business at all — a `fork` module is forked over the
|
|
647
|
+
* application context, so its needs are exactly what a starter's own `needs`
|
|
648
|
+
* channel already asks the composition root to supply, and di's ordinary
|
|
649
|
+
* `UNSATISFIED DEPENDENCIES` gate is what refuses a root that does not.
|
|
639
650
|
*
|
|
640
651
|
* **Unmet needs are checked FIRST, and answered in di's own words rather than
|
|
641
652
|
* a sentence of the kernel's.** A root that forgot `provides: [router]` used
|
|
@@ -654,10 +665,10 @@ type RunningApp<E, Info = never> = {
|
|
|
654
665
|
* name is reached.
|
|
655
666
|
*/
|
|
656
667
|
type PortIdOf<P> = P extends PortInstance<infer Id, infer _Service> ? Id : P;
|
|
657
|
-
type StartGate<X,
|
|
668
|
+
type StartGate<X, N = never> = [Exclude<N, Scope | Env>] extends [never] ? [Extract<X, RuntimeInstance>] extends [never] ? "NO RUNTIME — the module exports no port declared over RuntimePort" : [InstanceType<RuntimeResolvesOf<X>>] extends [X] ? unknown : "UNSATISFIED RUNTIME PORTS — the runtime resolves a port the module does not export" : {
|
|
658
669
|
readonly "UNSATISFIED DEPENDENCIES — nothing provides": PortIdOf<Exclude<N, Scope | Env>>;
|
|
659
670
|
};
|
|
660
|
-
declare const start: <X, E, N
|
|
671
|
+
declare const start: <X, E, N>(module: Module<X, E, N> & StartGate<X, N>, options?: StartOptions) => RunningApp<E, RuntimeInfoOf<X>>;
|
|
661
672
|
//#endregion
|
|
662
673
|
//#region src/run-main.d.ts
|
|
663
674
|
/**
|
|
@@ -692,6 +703,6 @@ declare const start: <X, E, N, UnitX = never, UnitNeeds = never>(module: Module<
|
|
|
692
703
|
* await runMain(OrderApi);
|
|
693
704
|
* ```
|
|
694
705
|
*/
|
|
695
|
-
declare const runMain: <X, E, N
|
|
706
|
+
declare const runMain: <X, E, N>(module: Module<X, E, N> & StartGate<X, N>, options?: StartOptions, exit?: (code: number) => void) => Promise<void>;
|
|
696
707
|
//#endregion
|
|
697
|
-
export { type Attributes, type Clock, type ComponentHealth, type Counter, type DrainReport, type EventSink, type ExitReport, type HealthCheck, HealthCheckFailed, HealthChecks, type HealthReport, type Histogram, type InstrumentationLoader, Instrumentations, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, Observers, type Operation, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Settle, type Settled, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, noObserver, observe, releasedBy, runHealthChecks, runMain, start, stderrSink, systemClock, traceIdOfTraceparent };
|
|
708
|
+
export { type Attributes, type Clock, type ComponentHealth, type Counter, type DrainReport, type EventSink, type ExitReport, type HealthCheck, HealthCheckFailed, HealthChecks, type HealthReport, type Histogram, type InstrumentationLoader, Instrumentations, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, Observers, type Operation, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Settle, type Settled, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitHost, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, noObserver, observe, releasedBy, runHealthChecks, runMain, start, stderrSink, systemClock, traceIdOfTraceparent };
|
package/dist/index.mjs
CHANGED
|
@@ -612,15 +612,43 @@ const start = (module, options = {}) => {
|
|
|
612
612
|
runtimeName = runtime.name;
|
|
613
613
|
healthChecks = ctx.get(HealthChecks);
|
|
614
614
|
const runtimeCtx = ctx;
|
|
615
|
-
const unit = options.unit;
|
|
616
615
|
const run = (meta, work) => registry.run(meta, (signal) => {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
616
|
+
const settled = Promise.withResolvers();
|
|
617
|
+
let closing;
|
|
618
|
+
let hasSettled = false;
|
|
619
|
+
const fork = (module, seed) => {
|
|
620
|
+
if (hasSettled) return fromSafePromise(Promise.reject(/* @__PURE__ */ new Error("a unit forks after it has already settled")));
|
|
621
|
+
if (closing !== void 0) return fromSafePromise(Promise.reject(/* @__PURE__ */ new Error("a unit forks its scope once")));
|
|
622
|
+
const ready = Promise.withResolvers();
|
|
623
|
+
closing = Module.forkScope(ctx, module, (forked) => {
|
|
624
|
+
ready.resolve(forked);
|
|
625
|
+
return fromSafePromise(settled.promise);
|
|
626
|
+
}, {
|
|
627
|
+
seed,
|
|
628
|
+
onTeardownError: (port, cause) => emit({
|
|
629
|
+
type: "teardownError",
|
|
630
|
+
port,
|
|
631
|
+
cause
|
|
632
|
+
})
|
|
633
|
+
}).recoverDefect((cause) => {
|
|
634
|
+
ready.reject(cause);
|
|
635
|
+
return Ok();
|
|
636
|
+
}).get();
|
|
637
|
+
return fromSafePromise(ready.promise);
|
|
638
|
+
};
|
|
639
|
+
const outcome = (async () => {
|
|
640
|
+
try {
|
|
641
|
+
return await work({
|
|
642
|
+
ctx: runtimeCtx,
|
|
643
|
+
fork
|
|
644
|
+
}, signal);
|
|
645
|
+
} finally {
|
|
646
|
+
hasSettled = true;
|
|
647
|
+
settled.resolve();
|
|
648
|
+
if (closing !== void 0) await closing;
|
|
649
|
+
}
|
|
650
|
+
})();
|
|
651
|
+
return fromSafePromise(outcome).flatMap((result) => result);
|
|
624
652
|
});
|
|
625
653
|
const host = {
|
|
626
654
|
ctx: runtimeCtx,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btravstack/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "The application kernel: boot a di module into a running process, and stop it without losing work",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"application",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"./package.json": "./package.json"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@btravstack/tsconfig": "0.
|
|
48
|
+
"@btravstack/tsconfig": "0.4.0",
|
|
49
49
|
"@types/node": "26.4.0",
|
|
50
50
|
"@unthrown/vitest": "5.7.0",
|
|
51
51
|
"@vitest/coverage-v8": "4.1.11",
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"typescript": "7.0.2",
|
|
54
54
|
"unthrown": "5.7.0",
|
|
55
55
|
"vitest": "4.1.11",
|
|
56
|
-
"@btravstack/config": "0.
|
|
57
|
-
"@btravstack/di": "0.
|
|
56
|
+
"@btravstack/config": "0.13.0",
|
|
57
|
+
"@btravstack/di": "0.13.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"unthrown": "^5.0.0",
|
|
61
|
-
"@btravstack/config": "^0.
|
|
62
|
-
"@btravstack/di": "^0.
|
|
61
|
+
"@btravstack/config": "^0.13.0",
|
|
62
|
+
"@btravstack/di": "^0.13.0"
|
|
63
63
|
},
|
|
64
64
|
"engines": {
|
|
65
65
|
"node": ">=22"
|