@nwire/runtime 0.11.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Gefter / 200apps Ltd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,68 @@
1
+ /**
2
+ * A Capability is a by-reference contribution to the Runtime. It can add
3
+ * any combination of:
4
+ *
5
+ * 1. A per-request ctx member (`provideCtx`) — merged into the handler
6
+ * ctx every dispatch. Lets handler code see typed verbs like
7
+ * `ctx.enqueue`, `ctx.publish`, `ctx.mail.send`.
8
+ * 2. A runtime method (`provideRuntime`) — installed once at
9
+ * `runtime.add(cap)`. Lets external code call e.g.
10
+ * `runtime.publish(...)` directly when no envelope is in scope.
11
+ *
12
+ * `TMark` is the phantom carrier that App's `<TCaps>` accumulates across
13
+ * `.with(plugin)`. The encoding is structural intersection: every plugin
14
+ * advertises its contribution as a structural type, and the App's
15
+ * accumulated `TCaps` is the intersection of all installed marks.
16
+ */
17
+ import type { MessageEnvelope } from "@nwire/envelope";
18
+ import type { Container } from "@nwire/container";
19
+ import type { Runtime } from "./runtime.js";
20
+ /**
21
+ * Base shape passed to `provideCtx`. The capability returns a Record that is
22
+ * spread into the handler ctx for one dispatch. `envelope`, `container`, and
23
+ * `runtime` are the three references every capability has access to.
24
+ */
25
+ export interface CapabilityBase {
26
+ readonly envelope: MessageEnvelope;
27
+ readonly container: Container;
28
+ readonly runtime: Runtime;
29
+ }
30
+ /**
31
+ * A Capability contributes to the Runtime by reference.
32
+ *
33
+ * @typeParam TMark — phantom shape advertising what this capability adds to
34
+ * the handler ctx. Accumulated by the App layer via
35
+ * structural intersection. Never read at runtime; the
36
+ * `__mark` field exists only to carry the type.
37
+ */
38
+ export interface Capability<TMark = unknown> {
39
+ /** Stable identifier — used for diagnostics and duplicate-install detection. */
40
+ readonly name: string;
41
+ /**
42
+ * Build the per-request ctx contribution. Called once per dispatch with
43
+ * the current envelope + container scope. The returned object is spread
44
+ * into the handler ctx. Should read but not mutate the base values.
45
+ */
46
+ provideCtx?(base: CapabilityBase): Record<string, unknown>;
47
+ /**
48
+ * Install a runtime-level method. Called once at `runtime.add(cap)`.
49
+ * Returned members are attached to the runtime instance directly so
50
+ * external code can call `runtime.<method>(...)` without dispatching.
51
+ */
52
+ provideRuntime?(runtime: Runtime): Record<string, (...args: any[]) => unknown>;
53
+ /** Phantom marker — carries the ctx-shape type into App<TCaps>. */
54
+ readonly __mark?: TMark;
55
+ }
56
+ /**
57
+ * Typed capability declaration that locks the ctx shape into both the type
58
+ * system and the runtime contract.
59
+ *
60
+ * const enqueueCap = defineCapability<{ enqueue: typeof enqueueFn }>({
61
+ * name: "queue.enqueue",
62
+ * provideCtx: ({ envelope }) => ({ enqueue: enqueueFn }),
63
+ * });
64
+ *
65
+ * Pure factory — no runtime cost beyond returning the input. The `TMark`
66
+ * generic is carried through `Capability<TMark>` so the App layer sees it.
67
+ */
68
+ export declare function defineCapability<TMark>(cap: Capability<TMark>): Capability<TMark>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * A Capability is a by-reference contribution to the Runtime. It can add
3
+ * any combination of:
4
+ *
5
+ * 1. A per-request ctx member (`provideCtx`) — merged into the handler
6
+ * ctx every dispatch. Lets handler code see typed verbs like
7
+ * `ctx.enqueue`, `ctx.publish`, `ctx.mail.send`.
8
+ * 2. A runtime method (`provideRuntime`) — installed once at
9
+ * `runtime.add(cap)`. Lets external code call e.g.
10
+ * `runtime.publish(...)` directly when no envelope is in scope.
11
+ *
12
+ * `TMark` is the phantom carrier that App's `<TCaps>` accumulates across
13
+ * `.with(plugin)`. The encoding is structural intersection: every plugin
14
+ * advertises its contribution as a structural type, and the App's
15
+ * accumulated `TCaps` is the intersection of all installed marks.
16
+ */
17
+ /**
18
+ * Typed capability declaration that locks the ctx shape into both the type
19
+ * system and the runtime contract.
20
+ *
21
+ * const enqueueCap = defineCapability<{ enqueue: typeof enqueueFn }>({
22
+ * name: "queue.enqueue",
23
+ * provideCtx: ({ envelope }) => ({ enqueue: enqueueFn }),
24
+ * });
25
+ *
26
+ * Pure factory — no runtime cost beyond returning the input. The `TMark`
27
+ * generic is carried through `Capability<TMark>` so the App layer sees it.
28
+ */
29
+ export function defineCapability(cap) {
30
+ return cap;
31
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Per-runtime framework-hook registry. Each slot is a `Hook<TPayload>`
3
+ * accessed as a typed property on `runtime.hooks`. Plugins extend via
4
+ * TS module augmentation + `runtime.defineHook(name)`.
5
+ */
6
+ import { type Hook } from "@nwire/hooks";
7
+ /**
8
+ * Plugins can distinguish "module compiled onto plugin lifecycle" from a
9
+ * normal plugin. Lifecycle payloads include this so observers can filter.
10
+ */
11
+ export type PluginKind = "plugin" | "module";
12
+ /**
13
+ * The typed registry. Plugin packages augment this interface to add their
14
+ * own slots; the foundation contributes the lifecycle slots below.
15
+ *
16
+ * Every slot is `Hook<TPayload>` — no dispatch-mode discriminator.
17
+ * Consumers call `.use()` to participate in the chain (with optional veto
18
+ * via not calling `next()`), `.on()` to observe in parallel, `.tap()` to
19
+ * receive step-level telemetry.
20
+ */
21
+ export interface FrameworkHooks {
22
+ AppRegistering: Hook<{
23
+ readonly appName: string;
24
+ }>;
25
+ AppBooting: Hook<{
26
+ readonly appName: string;
27
+ }>;
28
+ AppBooted: Hook<{
29
+ readonly appName: string;
30
+ readonly bootedAt: string;
31
+ }>;
32
+ AppReady: Hook<{
33
+ readonly appName: string;
34
+ readonly readyAt: string;
35
+ }>;
36
+ AppShuttingDown: Hook<{
37
+ readonly appName: string;
38
+ readonly reason?: string;
39
+ }>;
40
+ AppShutdown: Hook<{
41
+ readonly appName: string;
42
+ }>;
43
+ PluginRegistered: Hook<{
44
+ readonly appName: string;
45
+ readonly pluginName: string;
46
+ readonly kind?: PluginKind;
47
+ }>;
48
+ PluginBooting: Hook<{
49
+ readonly appName: string;
50
+ readonly pluginName: string;
51
+ readonly kind?: PluginKind;
52
+ }>;
53
+ PluginBooted: Hook<{
54
+ readonly appName: string;
55
+ readonly pluginName: string;
56
+ readonly durationMs: number;
57
+ readonly kind?: PluginKind;
58
+ }>;
59
+ PluginShuttingDown: Hook<{
60
+ readonly appName: string;
61
+ readonly pluginName: string;
62
+ readonly kind?: PluginKind;
63
+ }>;
64
+ PluginShutdown: Hook<{
65
+ readonly appName: string;
66
+ readonly pluginName: string;
67
+ readonly durationMs: number;
68
+ readonly kind?: PluginKind;
69
+ }>;
70
+ WireMounting: Hook<{
71
+ readonly appName: string;
72
+ readonly transport: string;
73
+ readonly manifest: unknown;
74
+ }>;
75
+ WireMounted: Hook<{
76
+ readonly appName: string;
77
+ readonly transport: string;
78
+ readonly manifest: unknown;
79
+ }>;
80
+ WireUnmounted: Hook<{
81
+ readonly appName: string;
82
+ readonly transport: string;
83
+ }>;
84
+ }
85
+ /** Canonical names for the built-in slots — used to label the underlying Hooks. */
86
+ declare const BUILT_IN_HOOK_NAMES: {
87
+ readonly AppRegistering: "nwire.app.registering";
88
+ readonly AppBooting: "nwire.app.booting";
89
+ readonly AppBooted: "nwire.app.booted";
90
+ readonly AppReady: "nwire.app.ready";
91
+ readonly AppShuttingDown: "nwire.app.shutting-down";
92
+ readonly AppShutdown: "nwire.app.shutdown";
93
+ readonly PluginRegistered: "nwire.plugin.registered";
94
+ readonly PluginBooting: "nwire.plugin.booting";
95
+ readonly PluginBooted: "nwire.plugin.booted";
96
+ readonly PluginShuttingDown: "nwire.plugin.shutting-down";
97
+ readonly PluginShutdown: "nwire.plugin.shutdown";
98
+ readonly WireMounting: "nwire.wire.mounting";
99
+ readonly WireMounted: "nwire.wire.mounted";
100
+ readonly WireUnmounted: "nwire.wire.unmounted";
101
+ };
102
+ /**
103
+ * Construct the per-runtime registry, with every built-in slot
104
+ * pre-instantiated. Plugin slots added via `runtime.defineHook(name)`
105
+ * land on the same registry instance.
106
+ */
107
+ export declare function createFrameworkHooks(): FrameworkHooks;
108
+ /** True if `slot` is one of the built-in hook keys. */
109
+ export declare function isBuiltInHook(slot: string): slot is keyof typeof BUILT_IN_HOOK_NAMES;
110
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Per-runtime framework-hook registry. Each slot is a `Hook<TPayload>`
3
+ * accessed as a typed property on `runtime.hooks`. Plugins extend via
4
+ * TS module augmentation + `runtime.defineHook(name)`.
5
+ */
6
+ import { hook } from "@nwire/hooks";
7
+ /** Canonical names for the built-in slots — used to label the underlying Hooks. */
8
+ const BUILT_IN_HOOK_NAMES = {
9
+ AppRegistering: "nwire.app.registering",
10
+ AppBooting: "nwire.app.booting",
11
+ AppBooted: "nwire.app.booted",
12
+ AppReady: "nwire.app.ready",
13
+ AppShuttingDown: "nwire.app.shutting-down",
14
+ AppShutdown: "nwire.app.shutdown",
15
+ PluginRegistered: "nwire.plugin.registered",
16
+ PluginBooting: "nwire.plugin.booting",
17
+ PluginBooted: "nwire.plugin.booted",
18
+ PluginShuttingDown: "nwire.plugin.shutting-down",
19
+ PluginShutdown: "nwire.plugin.shutdown",
20
+ WireMounting: "nwire.wire.mounting",
21
+ WireMounted: "nwire.wire.mounted",
22
+ WireUnmounted: "nwire.wire.unmounted",
23
+ };
24
+ /**
25
+ * Construct the per-runtime registry, with every built-in slot
26
+ * pre-instantiated. Plugin slots added via `runtime.defineHook(name)`
27
+ * land on the same registry instance.
28
+ */
29
+ export function createFrameworkHooks() {
30
+ const registry = {};
31
+ for (const [slot, name] of Object.entries(BUILT_IN_HOOK_NAMES)) {
32
+ registry[slot] = hook(name);
33
+ }
34
+ return registry;
35
+ }
36
+ /** True if `slot` is one of the built-in hook keys. */
37
+ export function isBuiltInHook(slot) {
38
+ return slot in BUILT_IN_HOOK_NAMES;
39
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@nwire/runtime` — the envelope-execution substrate.
3
+ *
4
+ * The Runtime is the dispatch + emit + listen substrate the App builds on.
5
+ * It owns the container reference, the dispatch hook chain, the capability
6
+ * registry, the sink chain, and the framework hooks. Standalone — container
7
+ * plus runtime alone is enough to dispatch handlers with envelopes, install
8
+ * capabilities, install middleware, and react to events without any App.
9
+ */
10
+ export { Runtime, createRuntime, isBuiltInHook, serializeError, type RuntimeOptions, type Telemetry, type TelemetryListener, type HookStepTelemetry, type DispatchHookCtx, type DispatchMiddleware, type SerializedError, type PluginDefinition, type PluginContext, } from "./runtime.js";
11
+ export { createFrameworkHooks, type FrameworkHooks, type PluginKind } from "./framework-hooks.js";
12
+ export { defineCapability, type Capability, type CapabilityBase } from "./capability.js";
13
+ export type { OutboundStage, StagePosition, StageContext } from "./sink.js";
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `@nwire/runtime` — the envelope-execution substrate.
3
+ *
4
+ * The Runtime is the dispatch + emit + listen substrate the App builds on.
5
+ * It owns the container reference, the dispatch hook chain, the capability
6
+ * registry, the sink chain, and the framework hooks. Standalone — container
7
+ * plus runtime alone is enough to dispatch handlers with envelopes, install
8
+ * capabilities, install middleware, and react to events without any App.
9
+ */
10
+ // Runtime core
11
+ export { Runtime, createRuntime, isBuiltInHook, serializeError, } from "./runtime.js";
12
+ export { createFrameworkHooks } from "./framework-hooks.js";
13
+ // Capability primitive — by-reference Runtime contribution.
14
+ export { defineCapability } from "./capability.js";