@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 +21 -0
- package/dist/capability.d.ts +68 -0
- package/dist/capability.js +31 -0
- package/dist/framework-hooks.d.ts +110 -0
- package/dist/framework-hooks.js +39 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/runtime.d.ts +402 -0
- package/dist/runtime.js +588 -0
- package/dist/sink.d.ts +54 -0
- package/dist/sink.js +13 -0
- package/package.json +47 -0
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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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";
|