@nwire/forge 0.7.0 → 0.8.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 +104 -48
- package/dist/__tests__/action-hooks.test.d.ts +8 -0
- package/dist/__tests__/action-hooks.test.d.ts.map +1 -0
- package/dist/__tests__/action-hooks.test.js +95 -0
- package/dist/__tests__/action-hooks.test.js.map +1 -0
- package/dist/__tests__/actor-workflow-hooks.test.d.ts +8 -0
- package/dist/__tests__/actor-workflow-hooks.test.d.ts.map +1 -0
- package/dist/__tests__/actor-workflow-hooks.test.js +104 -0
- package/dist/__tests__/actor-workflow-hooks.test.js.map +1 -0
- package/dist/__tests__/lifecycle-logging.test.js +4 -2
- package/dist/__tests__/lifecycle-logging.test.js.map +1 -1
- package/dist/__tests__/plugin-stress.test.d.ts +21 -0
- package/dist/__tests__/plugin-stress.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-stress.test.js +203 -0
- package/dist/__tests__/plugin-stress.test.js.map +1 -0
- package/dist/actor-store.d.ts +24 -0
- package/dist/actor-store.d.ts.map +1 -1
- package/dist/actor-store.js +29 -0
- package/dist/actor-store.js.map +1 -1
- package/dist/create-app.d.ts +7 -0
- package/dist/create-app.d.ts.map +1 -1
- package/dist/create-app.js +101 -10
- package/dist/create-app.js.map +1 -1
- package/dist/define-action.d.ts +4 -2
- package/dist/define-action.d.ts.map +1 -1
- package/dist/define-action.js +9 -6
- package/dist/define-action.js.map +1 -1
- package/dist/define-actor.d.ts +3 -1
- package/dist/define-actor.d.ts.map +1 -1
- package/dist/define-actor.js +11 -4
- package/dist/define-actor.js.map +1 -1
- package/dist/define-handler.d.ts +21 -2
- package/dist/define-handler.d.ts.map +1 -1
- package/dist/define-handler.js +3 -1
- package/dist/define-handler.js.map +1 -1
- package/dist/define-module.d.ts +3 -0
- package/dist/define-module.d.ts.map +1 -1
- package/dist/define-module.js +3 -0
- package/dist/define-module.js.map +1 -1
- package/dist/define-plugin.d.ts +4 -1
- package/dist/define-plugin.d.ts.map +1 -1
- package/dist/define-plugin.js +34 -14
- package/dist/define-plugin.js.map +1 -1
- package/dist/define-projection.d.ts +3 -1
- package/dist/define-projection.d.ts.map +1 -1
- package/dist/define-projection.js +3 -0
- package/dist/define-projection.js.map +1 -1
- package/dist/define-query.d.ts +3 -1
- package/dist/define-query.d.ts.map +1 -1
- package/dist/define-query.js +2 -0
- package/dist/define-query.js.map +1 -1
- package/dist/define-workflow.d.ts +19 -1
- package/dist/define-workflow.d.ts.map +1 -1
- package/dist/define-workflow.js +4 -0
- package/dist/define-workflow.js.map +1 -1
- package/dist/dev-logger.d.ts.map +1 -1
- package/dist/dev-logger.js +19 -1
- package/dist/dev-logger.js.map +1 -1
- package/dist/framework-events.d.ts +1 -64
- package/dist/framework-events.d.ts.map +1 -1
- package/dist/framework-events.js +3 -0
- package/dist/framework-events.js.map +1 -1
- package/dist/idempotency-store.d.ts +35 -0
- package/dist/idempotency-store.d.ts.map +1 -0
- package/dist/idempotency-store.js +32 -0
- package/dist/idempotency-store.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +197 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +495 -44
- package/dist/runtime.js.map +1 -1
- package/package.json +14 -11
package/README.md
CHANGED
|
@@ -1,77 +1,133 @@
|
|
|
1
1
|
# @nwire/forge
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> The framework's domain primitives — actions, actors, events, workflows, projections, modules, plugins, and the runtime that fires them.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Install
|
|
5
|
+
Forge composes the lower packages (`@nwire/messages`, `@nwire/handler`,
|
|
6
|
+
`@nwire/app`) into one ergonomic surface. You can drop down to any of
|
|
7
|
+
them when you want a narrower import; forge stays the default for app code.
|
|
10
8
|
|
|
11
9
|
```bash
|
|
12
|
-
pnpm add @nwire/forge
|
|
10
|
+
pnpm add @nwire/forge zod
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
## Quick
|
|
13
|
+
## Quick example
|
|
16
14
|
|
|
17
15
|
```ts
|
|
18
16
|
import { z } from "zod";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
import { defineEvent } from "@nwire/messages";
|
|
18
|
+
import {
|
|
19
|
+
defineAction,
|
|
20
|
+
defineActor,
|
|
21
|
+
defineModule,
|
|
22
|
+
createApp,
|
|
23
|
+
AppBooted,
|
|
24
|
+
ActionCompleted,
|
|
25
|
+
} from "@nwire/forge";
|
|
26
|
+
|
|
27
|
+
export const StudentWasEnrolled = defineEvent({
|
|
28
|
+
name: "enrollments.student-was-enrolled",
|
|
22
29
|
schema: z.object({ studentId: z.string(), courseId: z.string() }),
|
|
23
|
-
})
|
|
30
|
+
});
|
|
24
31
|
|
|
25
|
-
const Student = defineActor("Student", {
|
|
26
|
-
|
|
32
|
+
export const Student = defineActor("Student", {
|
|
33
|
+
schema: z.object({ studentId: z.string(), enrolments: z.array(z.string()) }),
|
|
34
|
+
initial: (id: string) => ({ studentId: id, enrolments: [] }),
|
|
27
35
|
methods: {
|
|
28
36
|
enrol(state, courseId: string) {
|
|
37
|
+
if (state.enrolments.includes(courseId)) return state;
|
|
29
38
|
return { ...state, enrolments: [...state.enrolments, courseId] };
|
|
30
39
|
},
|
|
31
40
|
},
|
|
32
41
|
});
|
|
33
42
|
|
|
34
|
-
const enrolStudent = defineAction(
|
|
35
|
-
|
|
43
|
+
export const enrolStudent = defineAction({
|
|
44
|
+
name: "enrollments.enrol-student",
|
|
45
|
+
schema: z.object({ studentId: z.string(), courseId: z.string() }),
|
|
36
46
|
emits: [StudentWasEnrolled],
|
|
37
|
-
handler: async (
|
|
47
|
+
handler: async (input, { use }) => {
|
|
38
48
|
const student = await use(Student, input.studentId);
|
|
39
49
|
student.enrol(input.courseId);
|
|
40
|
-
|
|
50
|
+
return StudentWasEnrolled(input);
|
|
41
51
|
},
|
|
42
|
-
}).public();
|
|
43
|
-
|
|
44
|
-
export const enrolments = defineModule("enrolments", {
|
|
45
|
-
actors: [Student],
|
|
46
|
-
actions: [enrolStudent],
|
|
47
|
-
events: [StudentWasEnrolled],
|
|
48
52
|
});
|
|
49
53
|
|
|
50
|
-
export const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- Handler sugar: `defineAction`, `defineQuery`, `defineHandler`, `defineMiddleware`, `pipe`, response helpers.
|
|
56
|
-
- Domain primitives: `defineActor`, `defineProjection`, `defineWorkflow`, `defineListener`, `defineCron`, `defineInbox`, `defineOutbox`, `defineExternalCall`, `defineInboundWebhook`.
|
|
57
|
-
- Composition: `defineModule`, `defineApp`, `createApp`, `definePlugin`.
|
|
58
|
-
- Models/errors: `defineResource`, `defineError`, `defineSchema`.
|
|
59
|
-
- Runtime + InMemory store defaults; `MessageEnvelope` re-exported from `@nwire/envelope`.
|
|
60
|
-
|
|
61
|
-
## When to use
|
|
62
|
-
|
|
63
|
-
The default surface for L3+ Nwire apps. Pull `@nwire/forge` directly when you want the full framework DX (actions, actors, projections, workflows, modules) without picking each lower package by hand.
|
|
64
|
-
|
|
65
|
-
## Used only within nwire-app
|
|
54
|
+
export const enrollments = defineModule("enrollments", {
|
|
55
|
+
events: [StudentWasEnrolled],
|
|
56
|
+
actors: [Student],
|
|
57
|
+
actions: [enrolStudent.public()],
|
|
58
|
+
});
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
const app = createApp({ modules: [enrollments] });
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- [`@nwire/endpoint`](../nwire-endpoint/README.md) — graceful shutdown for any host
|
|
62
|
+
// Subscribe to lifecycle + dispatch events through the same bus.
|
|
63
|
+
app.bus.on(AppBooted, ({ appName }) => console.log("booted:", appName));
|
|
64
|
+
app.bus.on(ActionCompleted, ({ action }) => console.log("ran:", action.name));
|
|
73
65
|
|
|
74
|
-
|
|
66
|
+
await app.start();
|
|
67
|
+
await app.runtime.dispatch(enrolStudent, { studentId: "avi", courseId: "heb-1" });
|
|
68
|
+
```
|
|
75
69
|
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
## Surface
|
|
71
|
+
|
|
72
|
+
### Domain primitives
|
|
73
|
+
|
|
74
|
+
| Export | Role |
|
|
75
|
+
| --------------------- | ------------------------------------------------------------- |
|
|
76
|
+
| `defineAction` | User-visible command — validated input, emits events |
|
|
77
|
+
| `defineEvent` | Past-tense fact (re-exported from `@nwire/messages`) |
|
|
78
|
+
| `defineHandler` | Operation primitive — transport-agnostic |
|
|
79
|
+
| `defineActor` | Aggregate with state, methods, optional state machine |
|
|
80
|
+
| `defineSchema` | Data shape + lifecycle states + storage hints |
|
|
81
|
+
| `defineProjection` | Read-model fold over events |
|
|
82
|
+
| `defineQuery` | Projection-backed read function |
|
|
83
|
+
| `defineWorkflow` | Reaction + saga unified — stateless or stateful |
|
|
84
|
+
| `defineModule` | Bundle of actors + actions + events + projections + queries |
|
|
85
|
+
| `defineApp` | App declaration (multi-wire instantiation) |
|
|
86
|
+
| `createApp` | App runtime — boots modules, owns the bus and runtime |
|
|
87
|
+
| `definePlugin` | Forge-richer plugin: middleware + actor hooks + before/after |
|
|
88
|
+
| `defineResource` | Public response shape (field allowlist + OpenAPI schema) |
|
|
89
|
+
| `defineError` | Typed throwable with status code |
|
|
90
|
+
| `defineMiddleware` / `defineHook` / `pipe` | Reusable resolver chain pieces |
|
|
91
|
+
| `defineCron` / `defineInbox` / `defineOutbox` / `defineExternalCall` / `defineInboundWebhook` | Orchestrator primitives |
|
|
92
|
+
| `runCli(app, argv)` | Argv dispatcher — operator CLI without HTTP |
|
|
93
|
+
|
|
94
|
+
### Framework events
|
|
95
|
+
|
|
96
|
+
Forge re-exports the app-lifecycle events from `@nwire/app`
|
|
97
|
+
(`AppRegistering` / `AppBooting` / `AppBooted` / `AppReady` /
|
|
98
|
+
`AppShuttingDown` / `AppShutdown`, `PluginRegistered` …, `WireMounting` …)
|
|
99
|
+
and adds its own forge-local events:
|
|
100
|
+
|
|
101
|
+
| Event | Mode | When |
|
|
102
|
+
| --------------------- | ------------- | --------------------------------------------------- |
|
|
103
|
+
| `ActionDispatching` | series-bail | Before handler runs. Return `false` to short-circuit. |
|
|
104
|
+
| `ActionCompleted` | parallel | After handler returns successfully. |
|
|
105
|
+
| `ActionFailed` | parallel | After handler throws. |
|
|
106
|
+
| `EventRecording` | series-bail | Before an event is appended to the store. |
|
|
107
|
+
| `EventRecorded` | parallel | After append succeeds. |
|
|
108
|
+
| `builtInFrameworkEvents` | — | Catalog of every built-in (lifecycle + forge-local).|
|
|
109
|
+
|
|
110
|
+
### Re-exports
|
|
111
|
+
|
|
112
|
+
- `MessageEnvelope`, `seedEnvelope`, `deriveEnvelope` from `@nwire/envelope`
|
|
113
|
+
- `Logger` contract + `NoopLogger` / `ConsoleLogger` from `@nwire/logger`
|
|
114
|
+
- `DeadLetterSink` + `InMemoryDeadLetterSink` from `@nwire/dead-letter`
|
|
115
|
+
|
|
116
|
+
## Picking the right entry point
|
|
117
|
+
|
|
118
|
+
| You want… | Import from |
|
|
119
|
+
| ------------------------------------------ | ------------------ |
|
|
120
|
+
| Full framework DX in one import | `@nwire/forge` |
|
|
121
|
+
| Just operations (handlers / middleware) | `@nwire/handler` |
|
|
122
|
+
| Just lifecycle + plugins | `@nwire/app` |
|
|
123
|
+
| Just events + envelope | `@nwire/messages` |
|
|
124
|
+
|
|
125
|
+
## Related
|
|
126
|
+
|
|
127
|
+
- `@nwire/endpoint` — wraps the runtime in a process (graceful shutdown, probes).
|
|
128
|
+
- `@nwire/http` — HTTP transport over the runtime.
|
|
129
|
+
- `@nwire/hooks` — the dispatch substrate that powers `runtime.use(...)` and every framework event.
|
|
130
|
+
|
|
131
|
+
## Status
|
|
132
|
+
|
|
133
|
+
v0.x — public surface is stable; `createApp` will extract to `@nwire/app` in a follow-up phase but imports won't change (re-exports preserved).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-action `action.before:<name>` and `action.after:<name>` hooks —
|
|
3
|
+
* proves the named-hook surface that plugin `before()`/`after()` sugar
|
|
4
|
+
* registers chain steps on, AND that the dispatcher honors veto + observes
|
|
5
|
+
* after-success.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=action-hooks.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-hooks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/action-hooks.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-action `action.before:<name>` and `action.after:<name>` hooks —
|
|
3
|
+
* proves the named-hook surface that plugin `before()`/`after()` sugar
|
|
4
|
+
* registers chain steps on, AND that the dispatcher honors veto + observes
|
|
5
|
+
* after-success.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect } from "vitest";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { listHooks } from "@nwire/hooks";
|
|
10
|
+
import { createApp, defineAction, defineHandler, defineModule, definePlugin } from "../foundation.js";
|
|
11
|
+
const Ping = defineAction({
|
|
12
|
+
name: "test/action-hooks-ping",
|
|
13
|
+
schema: z.object({ n: z.number() }),
|
|
14
|
+
});
|
|
15
|
+
const pingHandler = defineHandler(Ping, async (input) => undefined);
|
|
16
|
+
const mod = defineModule("test-action-hooks", {
|
|
17
|
+
actions: [Ping],
|
|
18
|
+
handlers: [pingHandler],
|
|
19
|
+
});
|
|
20
|
+
describe("per-action hooks", () => {
|
|
21
|
+
it("registers action.before:<name> + action.after:<name> on handler register", async () => {
|
|
22
|
+
const app = createApp({ appName: "action-hooks-test", modules: [mod] });
|
|
23
|
+
await app.start();
|
|
24
|
+
const names = listHooks().map((h) => h.name);
|
|
25
|
+
expect(names).toContain("action.before:test/action-hooks-ping");
|
|
26
|
+
expect(names).toContain("action.after:test/action-hooks-ping");
|
|
27
|
+
await app.stop();
|
|
28
|
+
});
|
|
29
|
+
it("plugin.before chain step that returns false vetoes the dispatch", async () => {
|
|
30
|
+
let handlerRan = false;
|
|
31
|
+
let beforeRan = false;
|
|
32
|
+
let afterRan = false;
|
|
33
|
+
const ping2 = defineHandler(defineAction({ name: "test/veto-ping", schema: z.object({}) }), async () => {
|
|
34
|
+
handlerRan = true;
|
|
35
|
+
return undefined;
|
|
36
|
+
});
|
|
37
|
+
const m = defineModule("test-veto", { actions: [ping2.action], handlers: [ping2] });
|
|
38
|
+
const vetoer = definePlugin("vetoer", ({ before, after }) => {
|
|
39
|
+
before("test/veto-ping", () => {
|
|
40
|
+
beforeRan = true;
|
|
41
|
+
return false;
|
|
42
|
+
});
|
|
43
|
+
after("test/veto-ping", () => {
|
|
44
|
+
afterRan = true;
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
const app = createApp({ appName: "veto-test", modules: [m], plugins: [vetoer] });
|
|
48
|
+
await app.start();
|
|
49
|
+
await app.runtime.dispatch(ping2.action, {});
|
|
50
|
+
expect(beforeRan).toBe(true);
|
|
51
|
+
expect(handlerRan).toBe(false);
|
|
52
|
+
expect(afterRan).toBe(false);
|
|
53
|
+
await app.stop();
|
|
54
|
+
});
|
|
55
|
+
it("plugin.after observes the result + durationMs of a successful dispatch", async () => {
|
|
56
|
+
let captured = null;
|
|
57
|
+
const ping3 = defineHandler(defineAction({ name: "test/observe-ping", schema: z.object({}) }), async () => undefined);
|
|
58
|
+
const m = defineModule("test-observe", { actions: [ping3.action], handlers: [ping3] });
|
|
59
|
+
const observer = definePlugin("observer", ({ after }) => {
|
|
60
|
+
after("test/observe-ping", ({ result, durationMs }) => {
|
|
61
|
+
captured = { result, durationMs };
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
const app = createApp({ appName: "observe-test", modules: [m], plugins: [observer] });
|
|
65
|
+
await app.start();
|
|
66
|
+
await app.runtime.dispatch(ping3.action, {});
|
|
67
|
+
expect(captured).not.toBeNull();
|
|
68
|
+
expect(typeof captured.durationMs).toBe("number");
|
|
69
|
+
await app.stop();
|
|
70
|
+
});
|
|
71
|
+
it("multiple plugins.before steps stack as named chain steps on the same hook", async () => {
|
|
72
|
+
const callOrder = [];
|
|
73
|
+
const ping4 = defineHandler(defineAction({ name: "test/stack-ping", schema: z.object({}) }), async () => {
|
|
74
|
+
callOrder.push("handler");
|
|
75
|
+
return undefined;
|
|
76
|
+
});
|
|
77
|
+
const m = defineModule("test-stack", { actions: [ping4.action], handlers: [ping4] });
|
|
78
|
+
const pluginA = definePlugin("a", ({ before }) => {
|
|
79
|
+
before("test/stack-ping", () => {
|
|
80
|
+
callOrder.push("a.before");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
const pluginB = definePlugin("b", ({ before }) => {
|
|
84
|
+
before("test/stack-ping", () => {
|
|
85
|
+
callOrder.push("b.before");
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
const app = createApp({ appName: "stack-test", modules: [m], plugins: [pluginA, pluginB] });
|
|
89
|
+
await app.start();
|
|
90
|
+
await app.runtime.dispatch(ping4.action, {});
|
|
91
|
+
expect(callOrder).toEqual(["a.before", "b.before", "handler"]);
|
|
92
|
+
await app.stop();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=action-hooks.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-hooks.test.js","sourceRoot":"","sources":["../../src/__tests__/action-hooks.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-actor `actor.transition:<name>` and per-workflow `workflow.fire:<name>`
|
|
3
|
+
* hooks (A7). Proves both:
|
|
4
|
+
* - hooks appear in listHooks() right after registerActor/Workflow
|
|
5
|
+
* - chain steps observe transitions / fires during real dispatch
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=actor-workflow-hooks.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actor-workflow-hooks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/actor-workflow-hooks.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-actor `actor.transition:<name>` and per-workflow `workflow.fire:<name>`
|
|
3
|
+
* hooks (A7). Proves both:
|
|
4
|
+
* - hooks appear in listHooks() right after registerActor/Workflow
|
|
5
|
+
* - chain steps observe transitions / fires during real dispatch
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect } from "vitest";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { defineEvent } from "@nwire/messages";
|
|
10
|
+
import { listHooks } from "@nwire/hooks";
|
|
11
|
+
import { createApp, defineAction, defineActor, defineModule, defineWorkflow, eventFactory, } from "../foundation.js";
|
|
12
|
+
// ─── Domain ────────────────────────────────────────────────────────
|
|
13
|
+
const OrderState = z.object({ orderId: z.string(), total: z.number() });
|
|
14
|
+
const Placed = defineEvent({
|
|
15
|
+
name: "orders.placed",
|
|
16
|
+
schema: z.object({ orderId: z.string(), total: z.number() }),
|
|
17
|
+
});
|
|
18
|
+
const Confirmed = defineEvent({
|
|
19
|
+
name: "orders.confirmed",
|
|
20
|
+
schema: z.object({ orderId: z.string() }),
|
|
21
|
+
});
|
|
22
|
+
const PlacedEvent = eventFactory(Placed);
|
|
23
|
+
const ConfirmedEvent = eventFactory(Confirmed);
|
|
24
|
+
const Order = defineActor("order", {
|
|
25
|
+
schema: OrderState,
|
|
26
|
+
key: "orderId",
|
|
27
|
+
initial: "draft",
|
|
28
|
+
states: {
|
|
29
|
+
draft: { on: { [Placed.name]: { target: "placed", assign: (_, e) => ({ ...e }) } } },
|
|
30
|
+
placed: { on: { [Confirmed.name]: { target: "confirmed" } } },
|
|
31
|
+
confirmed: {},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const placeOrder = defineAction({
|
|
35
|
+
name: "orders.place",
|
|
36
|
+
schema: z.object({ orderId: z.string(), total: z.number() }),
|
|
37
|
+
handler: async (input) => PlacedEvent(input),
|
|
38
|
+
});
|
|
39
|
+
const confirmOrder = defineAction({
|
|
40
|
+
name: "orders.confirm",
|
|
41
|
+
schema: z.object({ orderId: z.string() }),
|
|
42
|
+
handler: async (input) => ConfirmedEvent({ orderId: input.orderId }),
|
|
43
|
+
});
|
|
44
|
+
let workflowFiredCount = 0;
|
|
45
|
+
const notifyOnPlace = defineWorkflow("notify-on-place", ({ on }) => {
|
|
46
|
+
on(Placed, async () => {
|
|
47
|
+
workflowFiredCount++;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
const mod = defineModule("orders", {
|
|
51
|
+
actors: [Order],
|
|
52
|
+
actions: [placeOrder, confirmOrder],
|
|
53
|
+
events: [Placed, Confirmed],
|
|
54
|
+
workflows: [notifyOnPlace],
|
|
55
|
+
});
|
|
56
|
+
// ─── Tests ─────────────────────────────────────────────────────────
|
|
57
|
+
describe("per-actor + per-workflow hooks", () => {
|
|
58
|
+
it("registerActor + registerWorkflow create named hooks visible in listHooks", async () => {
|
|
59
|
+
const app = createApp({ appName: "actor-workflow-hooks-list", modules: [mod] });
|
|
60
|
+
await app.start();
|
|
61
|
+
const names = listHooks().map((h) => h.name);
|
|
62
|
+
expect(names).toContain("actor.transition:order");
|
|
63
|
+
expect(names).toContain("workflow.fire:notify-on-place");
|
|
64
|
+
await app.stop();
|
|
65
|
+
});
|
|
66
|
+
it("actor.transition hook observes state changes during real dispatch", async () => {
|
|
67
|
+
const transitions = [];
|
|
68
|
+
const app = createApp({ appName: "actor-transition-test", modules: [mod] });
|
|
69
|
+
await app.start();
|
|
70
|
+
app.runtime.ensureActorTransitionHook("order").use(async (hctx, next) => {
|
|
71
|
+
transitions.push({ from: hctx.fromState, to: hctx.toState });
|
|
72
|
+
await next();
|
|
73
|
+
}, { name: "observer" });
|
|
74
|
+
await app.runtime.dispatch(placeOrder, { orderId: "o1", total: 100 });
|
|
75
|
+
expect(transitions).toEqual([{ from: "draft", to: "placed" }]);
|
|
76
|
+
await app.runtime.dispatch(confirmOrder, { orderId: "o1" });
|
|
77
|
+
expect(transitions).toEqual([
|
|
78
|
+
{ from: "draft", to: "placed" },
|
|
79
|
+
{ from: "placed", to: "confirmed" },
|
|
80
|
+
]);
|
|
81
|
+
await app.stop();
|
|
82
|
+
});
|
|
83
|
+
it("workflow.fire hook observes saga invocations", async () => {
|
|
84
|
+
const fires = [];
|
|
85
|
+
workflowFiredCount = 0;
|
|
86
|
+
const app = createApp({ appName: "workflow-fire-test", modules: [mod] });
|
|
87
|
+
await app.start();
|
|
88
|
+
app.runtime.ensureWorkflowFireHook("notify-on-place").use(async (hctx, next) => {
|
|
89
|
+
fires.push({
|
|
90
|
+
workflow: hctx.workflow.name,
|
|
91
|
+
event: hctx.event.eventName,
|
|
92
|
+
correlationKey: hctx.correlationKey,
|
|
93
|
+
});
|
|
94
|
+
await next();
|
|
95
|
+
}, { name: "observer" });
|
|
96
|
+
await app.runtime.dispatch(placeOrder, { orderId: "o2", total: 50 });
|
|
97
|
+
expect(fires).toHaveLength(1);
|
|
98
|
+
expect(fires[0].workflow).toBe("notify-on-place");
|
|
99
|
+
expect(fires[0].event).toBe("orders.placed");
|
|
100
|
+
expect(workflowFiredCount).toBe(1);
|
|
101
|
+
await app.stop();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=actor-workflow-hooks.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actor-workflow-hooks.test.js","sourceRoot":"","sources":["../../src/__tests__/actor-workflow-hooks.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,SAAS,EACT,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,cAAc,EACd,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,sEAAsE;AAEtE,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAExE,MAAM,MAAM,GAAG,WAAW,CAAC;IACzB,IAAI,EAAE,eAAe;IACrB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;CAC7D,CAAC,CAAC;AACH,MAAM,SAAS,GAAG,WAAW,CAAC;IAC5B,IAAI,EAAE,kBAAkB;IACxB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;CAC1C,CAAC,CAAC;AACH,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AACzC,MAAM,cAAc,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;AAE/C,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE;IACjC,MAAM,EAAE,UAAU;IAClB,GAAG,EAAE,SAAS;IACd,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE;QACN,KAAK,EAAM,EAAE,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAK,EAAE,MAAM,EAAE,QAAQ,EAAK,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAI,CAAY,EAAE,CAAC,EAAE,EAAE,EAAE;QAC1G,MAAM,EAAK,EAAE,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;QAChE,SAAS,EAAE,EAAE;KACd;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,YAAY,CAAC;IAC9B,IAAI,EAAK,cAAc;IACvB,MAAM,EAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC7D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC;CAC7C,CAAC,CAAC;AACH,MAAM,YAAY,GAAG,YAAY,CAAC;IAChC,IAAI,EAAK,gBAAgB;IACzB,MAAM,EAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC1C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;CACrE,CAAC,CAAC;AAEH,IAAI,kBAAkB,GAAG,CAAC,CAAC;AAC3B,MAAM,aAAa,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;IACjE,EAAE,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QACpB,kBAAkB,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE;IACjC,MAAM,EAAK,CAAC,KAAK,CAAC;IAClB,OAAO,EAAI,CAAC,UAAU,EAAE,YAAY,CAAC;IACrC,MAAM,EAAK,CAAC,MAAM,EAAE,SAAS,CAAC;IAC9B,SAAS,EAAE,CAAC,aAAa,CAAC;CAC3B,CAAC,CAAC;AAEH,sEAAsE;AAEtE,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,OAAO,EAAE,2BAA2B,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChF,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QAElB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;QAEzD,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,WAAW,GAAwC,EAAE,CAAC;QAC5D,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QAElB,GAAG,CAAC,OAAO,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC,GAAG,CAChD,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;YACnB,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7D,MAAM,IAAI,EAAE,CAAC;QACf,CAAC,EACD,EAAE,IAAI,EAAE,UAAU,EAAE,CACrB,CAAC;QAEF,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACtE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAE/D,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC;YAC1B,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE;YAC/B,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE;SACpC,CAAC,CAAC;QAEH,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,KAAK,GAAuE,EAAE,CAAC;QACrF,kBAAkB,GAAG,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzE,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QAElB,GAAG,CAAC,OAAO,CAAC,sBAAsB,CAAC,iBAAiB,CAAC,CAAC,GAAG,CACvD,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;YACnB,KAAK,CAAC,IAAI,CAAC;gBACT,QAAQ,EAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI;gBAClC,KAAK,EAAW,IAAI,CAAC,KAAK,CAAC,SAAS;gBACpC,cAAc,EAAE,IAAI,CAAC,cAAc;aACpC,CAAC,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC,EACD,EAAE,IAAI,EAAE,UAAU,EAAE,CACrB,CAAC;QAEF,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAErE,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC9C,MAAM,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEnC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -71,8 +71,10 @@ describe("framework events → telemetry → dev logger", () => {
|
|
|
71
71
|
// Multiple lifecycle lines should be present.
|
|
72
72
|
const lifecycleLines = lines.filter((l) => l.includes("nwire:"));
|
|
73
73
|
expect(lifecycleLines.length).toBeGreaterThan(5);
|
|
74
|
-
// Plugin-booted line
|
|
75
|
-
|
|
74
|
+
// Plugin-booted line for the `trace` plugin specifically. Modules now
|
|
75
|
+
// also fire PluginBooted with `kind:"module"` first, so we need to find
|
|
76
|
+
// the booted line that names the `trace` plugin — not just the first.
|
|
77
|
+
const booted = lifecycleLines.find((l) => l.includes("plugin.booted") && l.includes("trace"));
|
|
76
78
|
expect(booted).toBeTruthy();
|
|
77
79
|
expect(booted).toContain("trace");
|
|
78
80
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-logging.test.js","sourceRoot":"","sources":["../../src/__tests__/lifecycle-logging.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"lifecycle-logging.test.js","sourceRoot":"","sources":["../../src/__tests__/lifecycle-logging.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin + module lifecycle stress test.
|
|
3
|
+
*
|
|
4
|
+
* Covers the failure modes that aren't exercised by `plugin.test.ts` /
|
|
5
|
+
* `plugin-closure.test.ts` / `lifecycle-logging.test.ts`:
|
|
6
|
+
*
|
|
7
|
+
* 1. Cross-plugin DI — plugin B reads plugin A's binding in its boot.
|
|
8
|
+
* 2. Slow boot timing — PluginBooted carries a real `durationMs`.
|
|
9
|
+
* 3. Boot failure recovery — A boots, B throws, stop() still cleans A.
|
|
10
|
+
* 4. Shutdown isolation — first plugin's shutdown throws but second still runs;
|
|
11
|
+
* the error surfaces from app.stop().
|
|
12
|
+
* 5. Ordering at scale — 10 plugins boot in registration order, shutdown reverse.
|
|
13
|
+
* 6. Module + plugin mixed — PluginRegistered fires with `kind:"module"` for
|
|
14
|
+
* modules first (topo order) then `kind:"plugin"` for plugins.
|
|
15
|
+
*
|
|
16
|
+
* Style follows `lifecycle-logging.test.ts` — real `createApp`, real
|
|
17
|
+
* `app.start()/stop()`, observe via the runtime's framework event bus
|
|
18
|
+
* (`runtime.frameworkEvents.onFire(...)`) and per-event subscriptions.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=plugin-stress.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-stress.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/plugin-stress.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}
|