@pattern-js/runtime-node 0.2.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/bin/pattern.d.ts +20 -0
  4. package/dist/bin/pattern.d.ts.map +1 -0
  5. package/dist/bin/pattern.js +365 -0
  6. package/dist/bin/pattern.js.map +1 -0
  7. package/dist/cli.d.ts +21 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +75 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/filesystem.d.ts +38 -0
  12. package/dist/filesystem.d.ts.map +1 -0
  13. package/dist/filesystem.js +59 -0
  14. package/dist/filesystem.js.map +1 -0
  15. package/dist/http.d.ts +94 -0
  16. package/dist/http.d.ts.map +1 -0
  17. package/dist/http.js +799 -0
  18. package/dist/http.js.map +1 -0
  19. package/dist/index.d.ts +21 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +23 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/load/generator.d.ts +19 -0
  24. package/dist/load/generator.d.ts.map +1 -0
  25. package/dist/load/generator.js +94 -0
  26. package/dist/load/generator.js.map +1 -0
  27. package/dist/load/index.d.ts +43 -0
  28. package/dist/load/index.d.ts.map +1 -0
  29. package/dist/load/index.js +87 -0
  30. package/dist/load/index.js.map +1 -0
  31. package/dist/load/recorder.d.ts +36 -0
  32. package/dist/load/recorder.d.ts.map +1 -0
  33. package/dist/load/recorder.js +106 -0
  34. package/dist/load/recorder.js.map +1 -0
  35. package/dist/load/report.d.ts +28 -0
  36. package/dist/load/report.d.ts.map +1 -0
  37. package/dist/load/report.js +84 -0
  38. package/dist/load/report.js.map +1 -0
  39. package/dist/load/types.d.ts +85 -0
  40. package/dist/load/types.d.ts.map +1 -0
  41. package/dist/load/types.js +51 -0
  42. package/dist/load/types.js.map +1 -0
  43. package/dist/mods.d.ts +23 -0
  44. package/dist/mods.d.ts.map +1 -0
  45. package/dist/mods.js +48 -0
  46. package/dist/mods.js.map +1 -0
  47. package/dist/project.d.ts +94 -0
  48. package/dist/project.d.ts.map +1 -0
  49. package/dist/project.js +168 -0
  50. package/dist/project.js.map +1 -0
  51. package/dist/schedule.d.ts +33 -0
  52. package/dist/schedule.d.ts.map +1 -0
  53. package/dist/schedule.js +132 -0
  54. package/dist/schedule.js.map +1 -0
  55. package/dist/trace/index.d.ts +32 -0
  56. package/dist/trace/index.d.ts.map +1 -0
  57. package/dist/trace/index.js +48 -0
  58. package/dist/trace/index.js.map +1 -0
  59. package/dist/trace/memory.d.ts +83 -0
  60. package/dist/trace/memory.d.ts.map +1 -0
  61. package/dist/trace/memory.js +260 -0
  62. package/dist/trace/memory.js.map +1 -0
  63. package/dist/trace/sqlite.d.ts +80 -0
  64. package/dist/trace/sqlite.d.ts.map +1 -0
  65. package/dist/trace/sqlite.js +344 -0
  66. package/dist/trace/sqlite.js.map +1 -0
  67. package/dist/worker/entry.d.ts +16 -0
  68. package/dist/worker/entry.d.ts.map +1 -0
  69. package/dist/worker/entry.js +135 -0
  70. package/dist/worker/entry.js.map +1 -0
  71. package/dist/worker-pool.d.ts +49 -0
  72. package/dist/worker-pool.d.ts.map +1 -0
  73. package/dist/worker-pool.js +203 -0
  74. package/dist/worker-pool.js.map +1 -0
  75. package/dist/ws-registry.d.ts +39 -0
  76. package/dist/ws-registry.d.ts.map +1 -0
  77. package/dist/ws-registry.js +99 -0
  78. package/dist/ws-registry.js.map +1 -0
  79. package/dist/ws.d.ts +72 -0
  80. package/dist/ws.d.ts.map +1 -0
  81. package/dist/ws.js +236 -0
  82. package/dist/ws.js.map +1 -0
  83. package/package.json +60 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @pattern-js/runtime-node — worker-thread pool transport (§4, §8).
3
+ *
4
+ * A `RunTransport` that dispatches each run to a `node:worker_threads` worker,
5
+ * so a run executes off the host event loop (the workflow is the unit of
6
+ * isolation, §1). Streamed out-gate results are reconstructed on the host;
7
+ * cancellation crosses the seam. It is a drop-in for `InProcessTransport` — the
8
+ * scheduler never knows the difference — and the same interface later allows a
9
+ * queue + remote workers (distribution).
10
+ *
11
+ * Workers register the base op catalog plus any `mods` passed in options (each
12
+ * a module specifier the worker `import()`s and `engine.use()`s), so workflows
13
+ * using mod-contributed ops can run on the pool too. A crashed worker rejects
14
+ * its in-flight runs and is respawned in place.
15
+ */
16
+ import type { RunHandle, RunRequest, RunTransport, TraceEvent } from "@pattern-js/core";
17
+ export interface WorkerPoolOptions {
18
+ /** Number of workers (default: available parallelism − 1, min 1). */
19
+ size?: number;
20
+ /**
21
+ * Mod module specifiers each worker loads at startup (same resolution as
22
+ * `loadMods`: bare npm/workspace specifiers, or absolute paths/file URLs).
23
+ * Without this, workers have only the base op catalog and a workflow using a
24
+ * mod-contributed op fails with "unknown op".
25
+ */
26
+ mods?: string[];
27
+ /**
28
+ * Receive each offloaded run's trace lifecycle (run start/spans/ready/end),
29
+ * tagged with the worker that ran it — wire it to the host `engine.ingestTrace`
30
+ * so offloaded runs appear in the Runs view like inline ones. Omit to drop
31
+ * worker traces (the runs still execute fine).
32
+ */
33
+ onTrace?: (evt: TraceEvent) => void;
34
+ }
35
+ export declare class WorkerPoolTransport implements RunTransport {
36
+ private workers;
37
+ private rr;
38
+ private closed;
39
+ private readonly mods;
40
+ private readonly onTrace?;
41
+ constructor(opts?: WorkerPoolOptions);
42
+ private spawn;
43
+ /** Replace a crashed worker so the pool keeps its capacity. */
44
+ private respawn;
45
+ describe(): Record<string, unknown>;
46
+ dispatch(req: RunRequest): RunHandle;
47
+ close(): Promise<void>;
48
+ }
49
+ //# sourceMappingURL=worker-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../src/worker-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAa,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAyInG,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CAAC;CACrC;AAED,qBAAa,mBAAoB,YAAW,YAAY;IACtD,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,EAAE,CAAK;IACf,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAW;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA4B;gBAEzC,IAAI,GAAE,iBAAsB;IAOxC,OAAO,CAAC,KAAK;IAIb,+DAA+D;IAC/D,OAAO,CAAC,OAAO;IAOf,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IASnC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS;IAsB9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * @pattern-js/runtime-node — worker-thread pool transport (§4, §8).
3
+ *
4
+ * A `RunTransport` that dispatches each run to a `node:worker_threads` worker,
5
+ * so a run executes off the host event loop (the workflow is the unit of
6
+ * isolation, §1). Streamed out-gate results are reconstructed on the host;
7
+ * cancellation crosses the seam. It is a drop-in for `InProcessTransport` — the
8
+ * scheduler never knows the difference — and the same interface later allows a
9
+ * queue + remote workers (distribution).
10
+ *
11
+ * Workers register the base op catalog plus any `mods` passed in options (each
12
+ * a module specifier the worker `import()`s and `engine.use()`s), so workflows
13
+ * using mod-contributed ops can run on the pool too. A crashed worker rejects
14
+ * its in-flight runs and is respawned in place.
15
+ */
16
+ import { Worker } from "node:worker_threads";
17
+ import { availableParallelism } from "node:os";
18
+ const WORKER_URL = new URL("./worker/entry.js", import.meta.url);
19
+ class WorkerWrapper {
20
+ onTrace;
21
+ worker;
22
+ pending = new Map();
23
+ inflight = 0;
24
+ constructor(mods, onFatal, onTrace) {
25
+ this.onTrace = onTrace;
26
+ this.worker = new Worker(WORKER_URL, { workerData: { mods } });
27
+ this.worker.on("message", (msg) => this.onMessage(msg));
28
+ this.worker.on("error", (err) => {
29
+ // The thread is dead: fail every in-flight run and hand the slot back to
30
+ // the pool for a respawn. Resetting `inflight` matters — a leaked count
31
+ // would silently remove this slot from least-inflight selection forever.
32
+ for (const p of this.pending.values())
33
+ if (!p.settled)
34
+ p.reject(err);
35
+ this.pending.clear();
36
+ this.inflight = 0;
37
+ onFatal(this, err);
38
+ });
39
+ }
40
+ onMessage(msg) {
41
+ // Trace events carry their own runId (inside the event) and aren't tied to a
42
+ // pending entry — handle them before the pending-lookup guard. Tag which
43
+ // worker ran it so the Runs view can show "ran on worker N".
44
+ if (msg.type === "trace") {
45
+ this.tagAndForward(msg.event);
46
+ return;
47
+ }
48
+ const p = this.pending.get(msg.id ?? msg.runId);
49
+ if (!p)
50
+ return;
51
+ switch (msg.type) {
52
+ case "result": {
53
+ const outputs = {};
54
+ for (const [nodeId, payload] of Object.entries(msg.outputs)) {
55
+ const sp = {};
56
+ for (const [k, v] of Object.entries(payload)) {
57
+ if (v && typeof v === "object" && "__patternStream" in v) {
58
+ const streamId = v.__patternStream;
59
+ sp[k] = new ReadableStream({
60
+ start: (controller) => p.controllers.set(streamId, controller),
61
+ });
62
+ }
63
+ else {
64
+ sp[k] = v;
65
+ }
66
+ }
67
+ outputs[nodeId] = sp;
68
+ }
69
+ p.settled = true;
70
+ p.resolve({
71
+ runId: msg.id,
72
+ status: msg.status,
73
+ outputs,
74
+ error: msg.error ? Object.assign(new Error(msg.error.message), { name: msg.error.name }) : undefined,
75
+ });
76
+ break;
77
+ }
78
+ case "chunk":
79
+ p.controllers.get(msg.streamId)?.enqueue(msg.value);
80
+ break;
81
+ case "streamEnd":
82
+ try {
83
+ p.controllers.get(msg.streamId)?.close();
84
+ }
85
+ catch {
86
+ /* ignore */
87
+ }
88
+ p.controllers.delete(msg.streamId);
89
+ break;
90
+ case "streamError":
91
+ try {
92
+ p.controllers.get(msg.streamId)?.error(new Error(msg.error?.message ?? "stream error"));
93
+ }
94
+ catch {
95
+ /* ignore */
96
+ }
97
+ p.controllers.delete(msg.streamId);
98
+ break;
99
+ case "done":
100
+ this.pending.delete(msg.id);
101
+ this.inflight = Math.max(0, this.inflight - 1);
102
+ break;
103
+ }
104
+ }
105
+ /** Stamp the worker's identity onto a forwarded trace event, then hand it up. */
106
+ tagAndForward(evt) {
107
+ if (!this.onTrace)
108
+ return;
109
+ const executor = `worker:${this.worker.threadId}`;
110
+ if (evt.kind === "spanEnd") {
111
+ evt.span.attributes = { ...evt.span.attributes, executor };
112
+ }
113
+ else if (evt.kind === "runStart") {
114
+ evt.run.executor = executor;
115
+ }
116
+ this.onTrace(evt);
117
+ }
118
+ run(req, runId) {
119
+ this.inflight++;
120
+ return new Promise((resolve, reject) => {
121
+ this.pending.set(runId, { resolve, reject, controllers: new Map(), settled: false });
122
+ this.worker.postMessage({
123
+ type: "run",
124
+ id: runId,
125
+ workflow: req.workflow,
126
+ triggerNodeId: req.triggerNodeId,
127
+ input: req.input,
128
+ principal: req.principal,
129
+ params: req.params,
130
+ sampleIo: req.sampleIo,
131
+ hookDepth: req.hookDepth,
132
+ parent: req.parent,
133
+ });
134
+ });
135
+ }
136
+ abort(runId) {
137
+ this.worker.postMessage({ type: "abort", id: runId });
138
+ }
139
+ terminate() {
140
+ return this.worker.terminate();
141
+ }
142
+ }
143
+ export class WorkerPoolTransport {
144
+ workers;
145
+ rr = 0;
146
+ closed = false;
147
+ mods;
148
+ onTrace;
149
+ constructor(opts = {}) {
150
+ const size = Math.max(1, opts.size ?? availableParallelism() - 1);
151
+ this.mods = opts.mods ?? [];
152
+ this.onTrace = opts.onTrace;
153
+ this.workers = Array.from({ length: size }, () => this.spawn());
154
+ }
155
+ spawn() {
156
+ return new WorkerWrapper(this.mods, (dead) => this.respawn(dead), this.onTrace);
157
+ }
158
+ /** Replace a crashed worker so the pool keeps its capacity. */
159
+ respawn(dead) {
160
+ void dead.terminate().catch(() => { });
161
+ if (this.closed)
162
+ return;
163
+ const i = this.workers.indexOf(dead);
164
+ if (i >= 0)
165
+ this.workers[i] = this.spawn();
166
+ }
167
+ describe() {
168
+ return {
169
+ kind: "worker-pool",
170
+ size: this.workers.length,
171
+ inflight: this.workers.map((w) => w.inflight),
172
+ threadIds: this.workers.map((w) => w.worker.threadId),
173
+ };
174
+ }
175
+ dispatch(req) {
176
+ if (this.closed || this.workers.length === 0) {
177
+ throw new Error("WorkerPoolTransport is closed");
178
+ }
179
+ // Least-inflight selection; the rotating scan start breaks ties fairly.
180
+ const n = this.workers.length;
181
+ let worker = this.workers[this.rr % n];
182
+ for (let i = 1; i < n; i++) {
183
+ const w = this.workers[(this.rr + i) % n];
184
+ if (w.inflight < worker.inflight)
185
+ worker = w;
186
+ }
187
+ this.rr = (this.rr + 1) % n;
188
+ const runId = req.runId ?? crypto.randomUUID();
189
+ const result = worker.run(req, runId);
190
+ return {
191
+ runId,
192
+ result,
193
+ abort: () => worker.abort(runId),
194
+ };
195
+ }
196
+ async close() {
197
+ this.closed = true;
198
+ const workers = this.workers;
199
+ this.workers = [];
200
+ await Promise.all(workers.map((w) => w.terminate()));
201
+ }
202
+ }
203
+ //# sourceMappingURL=worker-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-pool.js","sourceRoot":"","sources":["../src/worker-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @pattern-js/runtime-node — connection registry bound to live WebSocket sockets (§7).
3
+ *
4
+ * Implements core's `ConnectionRegistry` interface. Connections are host-local
5
+ * (like a socket), so this registry is the one place that knows about sockets;
6
+ * `core.ws.*` ops and the `boundary.ws.send` out-gate route through the interface
7
+ * and stay socket-agnostic — a pub/sub backplane could replace this for
8
+ * distribution without touching any workflow.
9
+ */
10
+ import type { WebSocket } from "ws";
11
+ import type { ConnectionRef, ConnectionRegistry, Principal } from "@pattern-js/core";
12
+ export declare class NodeConnectionRegistry implements ConnectionRegistry {
13
+ private sockets;
14
+ private rooms;
15
+ private principals;
16
+ /** Register a freshly-opened socket; returns its connection ref. */
17
+ add(socket: WebSocket, id?: string, principal?: Principal): ConnectionRef;
18
+ remove(id: string): void;
19
+ /** The principal resolved at upgrade time, fixed for the connection's life (§9). */
20
+ principalOf(id: string): Principal | undefined;
21
+ get(id: string): WebSocket | undefined;
22
+ /** Ids of every live connection (observability; tests). */
23
+ ids(): string[];
24
+ send(connection: ConnectionRef | string, data: unknown): Promise<void>;
25
+ sendStream(connection: ConnectionRef | string, data: ReadableStream<unknown>): Promise<void>;
26
+ broadcast(room: string, data: unknown): Promise<void>;
27
+ join(connection: ConnectionRef | string, room: string): Promise<void>;
28
+ leave(connection: ConnectionRef | string, room: string): Promise<void>;
29
+ close(connection: ConnectionRef | string, code?: number, reason?: string): Promise<void>;
30
+ /** Members of a room (inspection/testing). */
31
+ members(room: string): string[];
32
+ /**
33
+ * Close every member of a room (§9: session revocation closes the session's
34
+ * sockets via the `session:{id}` room). Copies the member list first — close
35
+ * mutates room membership.
36
+ */
37
+ closeRoom(room: string, code?: number, reason?: string): Promise<void>;
38
+ }
39
+ //# sourceMappingURL=ws-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-registry.d.ts","sourceRoot":"","sources":["../src/ws-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAUrF,qBAAa,sBAAuB,YAAW,kBAAkB;IAC/D,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,UAAU,CAAgC;IAElD,oEAAoE;IACpE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,GAAE,MAA4B,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,aAAa;IAM9F,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAMxB,oFAAoF;IACpF,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI9C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAItC,2DAA2D;IAC3D,GAAG,IAAI,MAAM,EAAE;IAIT,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,UAAU,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5F,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrE,KAAK,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,KAAK,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9F,8CAA8C;IAC9C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAI/B;;;;OAIG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7E"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @pattern-js/runtime-node — connection registry bound to live WebSocket sockets (§7).
3
+ *
4
+ * Implements core's `ConnectionRegistry` interface. Connections are host-local
5
+ * (like a socket), so this registry is the one place that knows about sockets;
6
+ * `core.ws.*` ops and the `boundary.ws.send` out-gate route through the interface
7
+ * and stay socket-agnostic — a pub/sub backplane could replace this for
8
+ * distribution without touching any workflow.
9
+ */
10
+ const idOf = (c) => (typeof c === "string" ? c : c.id);
11
+ function encode(data) {
12
+ if (typeof data === "string")
13
+ return data;
14
+ if (data instanceof Uint8Array)
15
+ return data;
16
+ return JSON.stringify(data);
17
+ }
18
+ export class NodeConnectionRegistry {
19
+ sockets = new Map();
20
+ rooms = new Map();
21
+ principals = new Map();
22
+ /** Register a freshly-opened socket; returns its connection ref. */
23
+ add(socket, id = crypto.randomUUID(), principal) {
24
+ this.sockets.set(id, socket);
25
+ if (principal)
26
+ this.principals.set(id, principal);
27
+ return { id };
28
+ }
29
+ remove(id) {
30
+ this.sockets.delete(id);
31
+ this.principals.delete(id);
32
+ for (const set of this.rooms.values())
33
+ set.delete(id);
34
+ }
35
+ /** The principal resolved at upgrade time, fixed for the connection's life (§9). */
36
+ principalOf(id) {
37
+ return this.principals.get(id);
38
+ }
39
+ get(id) {
40
+ return this.sockets.get(id);
41
+ }
42
+ /** Ids of every live connection (observability; tests). */
43
+ ids() {
44
+ return [...this.sockets.keys()];
45
+ }
46
+ async send(connection, data) {
47
+ this.sockets.get(idOf(connection))?.send(encode(data));
48
+ }
49
+ async sendStream(connection, data) {
50
+ const socket = this.sockets.get(idOf(connection));
51
+ if (!socket)
52
+ return;
53
+ const reader = data.getReader();
54
+ try {
55
+ for (;;) {
56
+ const { done, value } = await reader.read();
57
+ if (done)
58
+ break;
59
+ socket.send(encode(value));
60
+ }
61
+ }
62
+ finally {
63
+ reader.releaseLock();
64
+ }
65
+ }
66
+ async broadcast(room, data) {
67
+ const encoded = encode(data);
68
+ for (const id of this.rooms.get(room) ?? [])
69
+ this.sockets.get(id)?.send(encoded);
70
+ }
71
+ async join(connection, room) {
72
+ let set = this.rooms.get(room);
73
+ if (!set)
74
+ this.rooms.set(room, (set = new Set()));
75
+ set.add(idOf(connection));
76
+ }
77
+ async leave(connection, room) {
78
+ this.rooms.get(room)?.delete(idOf(connection));
79
+ }
80
+ async close(connection, code, reason) {
81
+ const id = idOf(connection);
82
+ this.sockets.get(id)?.close(code, reason);
83
+ this.remove(id);
84
+ }
85
+ /** Members of a room (inspection/testing). */
86
+ members(room) {
87
+ return [...(this.rooms.get(room) ?? [])];
88
+ }
89
+ /**
90
+ * Close every member of a room (§9: session revocation closes the session's
91
+ * sockets via the `session:{id}` room). Copies the member list first — close
92
+ * mutates room membership.
93
+ */
94
+ async closeRoom(room, code, reason) {
95
+ for (const id of this.members(room))
96
+ await this.close(id, code, reason);
97
+ }
98
+ }
99
+ //# sourceMappingURL=ws-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-registry.js","sourceRoot":"","sources":["../src/ws-registry.ts"],"names":[],"mappings":"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"}
package/dist/ws.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @pattern-js/runtime-node — WebSocket host (§7, §9).
3
+ *
4
+ * Node has no built-in WS *server*, so this host uses `ws` on the HTTP upgrade
5
+ * (an adapter-level dependency; core stays clean). It fires a run **per inbound
6
+ * message** through `boundary.ws.message`, plus optional `boundary.ws.open` /
7
+ * `boundary.ws.close` lifecycle workflows, and sends `boundary.ws.send` results
8
+ * back on the originating connection.
9
+ *
10
+ * Auth (§9) happens **at upgrade time**: the provider chain resolves a principal
11
+ * from the upgrade request's headers (same cookie the HTTP host sees), every
12
+ * bound trigger's `requireAuth` is enforced *before* the socket is accepted
13
+ * (raw 401 + destroy on failure), and the principal is fixed to the connection
14
+ * for its lifetime — open/message/close runs all execute as it and see it on
15
+ * their `user` port. Authenticated connections auto-join the `user:{id}` and
16
+ * `session:{sessionId}` rooms, so "message all of this user's devices" is a
17
+ * broadcast and session revocation is a `closeRoom`.
18
+ */
19
+ import type { Server } from "node:http";
20
+ import { WebSocketServer } from "ws";
21
+ import { type Engine, type Workflow } from "@pattern-js/core";
22
+ import { NodeConnectionRegistry } from "./ws-registry.js";
23
+ export interface WsBinding {
24
+ workflow: Workflow | string;
25
+ trigger?: string;
26
+ }
27
+ export interface WsHostOptions {
28
+ /** Run per inbound message (boundary.ws.message). */
29
+ onMessage?: WsBinding;
30
+ /** Run on connection open (boundary.ws.open). */
31
+ onOpen?: WsBinding;
32
+ /** Run on connection close (boundary.ws.close). */
33
+ onClose?: WsBinding;
34
+ /** Restrict to a path (e.g. "/ws"). */
35
+ path?: string;
36
+ }
37
+ export declare class WsHost {
38
+ private readonly engine;
39
+ private readonly opts;
40
+ readonly wss: WebSocketServer;
41
+ readonly connections: NodeConnectionRegistry;
42
+ constructor(engine: Engine, opts: WsHostOptions, connections?: NodeConnectionRegistry);
43
+ /** Attach to an HTTP server's upgrade event. */
44
+ attach(server: Server): this;
45
+ /** Resolve a principal from the upgrade request via the provider chain (§9). */
46
+ private authenticate;
47
+ /**
48
+ * Enforce every bound trigger's `requireAuth` (§9). One socket serves all
49
+ * three lifecycle triggers, so the connection must satisfy the union of
50
+ * their requirements — a protected message handler protects the upgrade.
51
+ */
52
+ private deniedReason;
53
+ /** Explicit binding (when configured) or auto mode is on for this host. */
54
+ private get auto();
55
+ /**
56
+ * Bindings to enforce/fire for a trigger op. Explicit options win; auto mode
57
+ * resolves LIVE from the workflow registry (workflows deployed from the
58
+ * admin bind without a restart, mirroring HttpHost's declarative routing).
59
+ * Auth enforces ALL matching workflows' requirements; firing uses the first
60
+ * (one socket, no path dispatch — multi-handler WS is an explicit-options
61
+ * setup).
62
+ */
63
+ private resolved;
64
+ private bindingFor;
65
+ private onConnection;
66
+ private resolve;
67
+ private fire;
68
+ private fireAndSend;
69
+ }
70
+ /** Create a WS host (attach it to an HTTP server with `.attach(server)`). */
71
+ export declare function createWsHost(engine: Engine, opts: WsHostOptions, connections?: NodeConnectionRegistry): WsHost;
72
+ //# sourceMappingURL=ws.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,eAAe,EAAkB,MAAM,IAAI,CAAC;AACrD,OAAO,EAIL,KAAK,MAAM,EAGX,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,iDAAiD;IACjD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,qBAAa,MAAM;IAKf,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IALvB,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,sBAAsB,CAAC;gBAG1B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,EACpC,WAAW,CAAC,EAAE,sBAAsB;IAYtC,gDAAgD;IAChD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IA+B5B,gFAAgF;YAClE,YAAY;IAS1B;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAcpB,2EAA2E;IAC3E,OAAO,KAAK,IAAI,GAEf;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,YAAY;IA0CpB,OAAO,CAAC,OAAO;YASD,IAAI;YAWJ,WAAW;CAoC1B;AAsBD,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAE9G"}