@nodii/saga 0.0.2 → 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 (90) hide show
  1. package/dist/admin-service.d.ts +55 -0
  2. package/dist/admin-service.d.ts.map +1 -0
  3. package/dist/admin-service.js +161 -0
  4. package/dist/admin-service.js.map +1 -0
  5. package/dist/async-step.d.ts +43 -0
  6. package/dist/async-step.d.ts.map +1 -0
  7. package/dist/async-step.js +166 -0
  8. package/dist/async-step.js.map +1 -0
  9. package/dist/context.d.ts +18 -0
  10. package/dist/context.d.ts.map +1 -0
  11. package/dist/context.js +28 -0
  12. package/dist/context.js.map +1 -0
  13. package/dist/decorator.d.ts +24 -0
  14. package/dist/decorator.d.ts.map +1 -0
  15. package/dist/decorator.js +69 -0
  16. package/dist/decorator.js.map +1 -0
  17. package/dist/idempotency.d.ts +22 -0
  18. package/dist/idempotency.d.ts.map +1 -0
  19. package/dist/idempotency.js +44 -0
  20. package/dist/idempotency.js.map +1 -0
  21. package/dist/index.d.ts +20 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +34 -4
  24. package/dist/index.js.map +1 -1
  25. package/dist/init.d.ts +45 -0
  26. package/dist/init.d.ts.map +1 -0
  27. package/dist/init.js +86 -0
  28. package/dist/init.js.map +1 -0
  29. package/dist/interceptor.d.ts +5 -0
  30. package/dist/interceptor.d.ts.map +1 -0
  31. package/dist/interceptor.js +34 -0
  32. package/dist/interceptor.js.map +1 -0
  33. package/dist/migrations/index.d.ts +19 -0
  34. package/dist/migrations/index.d.ts.map +1 -0
  35. package/dist/migrations/index.js +106 -0
  36. package/dist/migrations/index.js.map +1 -0
  37. package/dist/reaper.d.ts +27 -0
  38. package/dist/reaper.d.ts.map +1 -0
  39. package/dist/reaper.js +79 -0
  40. package/dist/reaper.js.map +1 -0
  41. package/dist/registry.d.ts +7 -0
  42. package/dist/registry.d.ts.map +1 -0
  43. package/dist/registry.js +23 -0
  44. package/dist/registry.js.map +1 -0
  45. package/dist/saga.d.ts +48 -0
  46. package/dist/saga.d.ts.map +1 -0
  47. package/dist/saga.js +384 -0
  48. package/dist/saga.js.map +1 -0
  49. package/dist/signal-bus/index.d.ts +2 -0
  50. package/dist/signal-bus/index.d.ts.map +1 -0
  51. package/dist/signal-bus/index.js +6 -0
  52. package/dist/signal-bus/index.js.map +1 -0
  53. package/dist/signal-bus/redis-stream.d.ts +43 -0
  54. package/dist/signal-bus/redis-stream.d.ts.map +1 -0
  55. package/dist/signal-bus/redis-stream.js +189 -0
  56. package/dist/signal-bus/redis-stream.js.map +1 -0
  57. package/dist/signals.d.ts +29 -0
  58. package/dist/signals.d.ts.map +1 -0
  59. package/dist/signals.js +113 -0
  60. package/dist/signals.js.map +1 -0
  61. package/dist/state-store/index.d.ts +2 -0
  62. package/dist/state-store/index.d.ts.map +1 -0
  63. package/dist/state-store/index.js +6 -0
  64. package/dist/state-store/index.js.map +1 -0
  65. package/dist/state-store/postgres.d.ts +47 -0
  66. package/dist/state-store/postgres.d.ts.map +1 -0
  67. package/dist/state-store/postgres.js +270 -0
  68. package/dist/state-store/postgres.js.map +1 -0
  69. package/dist/test-doubles/in-memory-signal-bus.d.ts +38 -0
  70. package/dist/test-doubles/in-memory-signal-bus.d.ts.map +1 -0
  71. package/dist/test-doubles/in-memory-signal-bus.js +68 -0
  72. package/dist/test-doubles/in-memory-signal-bus.js.map +1 -0
  73. package/dist/test-doubles/in-memory-state-store.d.ts +26 -0
  74. package/dist/test-doubles/in-memory-state-store.d.ts.map +1 -0
  75. package/dist/test-doubles/in-memory-state-store.js +87 -0
  76. package/dist/test-doubles/in-memory-state-store.js.map +1 -0
  77. package/dist/test-doubles/index.d.ts +4 -0
  78. package/dist/test-doubles/index.d.ts.map +1 -0
  79. package/dist/test-doubles/index.js +14 -0
  80. package/dist/test-doubles/index.js.map +1 -0
  81. package/dist/types.d.ts +260 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +103 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/uuid.d.ts +3 -0
  86. package/dist/uuid.d.ts.map +1 -0
  87. package/dist/uuid.js +62 -0
  88. package/dist/uuid.js.map +1 -0
  89. package/package.json +33 -7
  90. package/src/migrations/001-saga-state.sql +79 -0
package/dist/saga.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { type CreateSagaOpts, type SagaHandle, type StepOpts } from "./types";
2
+ /**
3
+ * § 5.2 + § 5.8 — createSaga.
4
+ *
5
+ * Resume detection: if a `ResumeContext` is primed on the ALS (set by
6
+ * `withSaga`), the function reloads the saga from the state store and
7
+ * passes the persisted `step_outputs` map so completed steps short-circuit
8
+ * to their cached output via the idempotency layer.
9
+ */
10
+ export declare function createSaga<O>(opts: CreateSagaOpts, fn: () => Promise<O>): Promise<O>;
11
+ /**
12
+ * § 5.8 — withSaga primes a ResumeContext + an inner createSaga reloads.
13
+ *
14
+ * v0.1.0 surface: `withSaga(handle, fn)` runs `fn` in the saga's ALS scope
15
+ * with `resume.isResume = true`. The expected usage is for the inner
16
+ * function to call `createSaga` with the same type — that's how the registry
17
+ * gets re-entered.
18
+ */
19
+ export declare function withSaga<O>(sagaId: string, fn: () => Promise<O>): Promise<O>;
20
+ /**
21
+ * § 5.2 — step.
22
+ *
23
+ * Pipeline:
24
+ * 1. update saga_state.current_step = name
25
+ * 2. run handler via idempotency.wrapForSagaStep
26
+ * 3. on success: persist output, push undo record onto saga's undo stack
27
+ * 4. on failure: throw SagaStepFailed; createSaga catches + runs compensation
28
+ *
29
+ * `mode: 'best-effort'` per-step (or via the undo object form) makes
30
+ * compensation lenient — a thrown undo records `compensated_with_errors`
31
+ * instead of `compensation_failed`.
32
+ */
33
+ export declare function step<O>(name: string, opts: StepOpts<O>): Promise<O>;
34
+ /**
35
+ * § 5.2 — stepParallel. Fans out concurrently; collects results into an array.
36
+ *
37
+ * Compensation semantics match `step` — undos are pushed onto the stack in
38
+ * the order they completed, so reverse-order traversal is preserved.
39
+ */
40
+ export declare function stepParallel<O>(steps: {
41
+ name: string;
42
+ opts: StepOpts<O>;
43
+ }[]): Promise<O[]>;
44
+ /** Public cancel entry-point (also reachable via handle.cancel). */
45
+ export declare function cancel(sagaId: string, reason: string): Promise<void>;
46
+ /** Read-only accessor for the active saga handle in scope. */
47
+ export declare function currentSaga(): SagaHandle | null;
48
+ //# sourceMappingURL=saga.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga.d.ts","sourceRoot":"","sources":["../src/saga.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,UAAU,EAEf,KAAK,QAAQ,EAGd,MAAM,SAAS,CAAC;AAmFjB;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CA8EZ;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAgGzE;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;CAAE,EAAE,GAC3C,OAAO,CAAC,CAAC,EAAE,CAAC,CAEd;AAuFD,oEAAoE;AACpE,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,IAAI,UAAU,GAAG,IAAI,CAE/C"}
package/dist/saga.js ADDED
@@ -0,0 +1,384 @@
1
+ // createSaga / withSaga / step / stepParallel / saga.cancel.
2
+ //
3
+ // Spec §§ 5.2, 5.3, 5.8. v0.1.0 ships the deterministic core: state-store
4
+ // CRUD, ALS-primed handle, idempotency-wrapped step execution, reverse-order
5
+ // undo on failure (strict-mode halt vs best-effort `compensated_with_errors`),
6
+ // `cancel()` → status='cancelled' + D33 audit event.
7
+ //
8
+ // Resume detection (§ 5.8) is wired but the reaper / grace-period orchestrator
9
+ // is deferred (`// TODO saga-v0.1.x:`); same for cross-saga compensation as
10
+ // library-spawned child saga (we record entries on `compensation_log` instead).
11
+ import { getResumeContext, getSagaContext, requireSagaContext, runInSagaContext, runWithResumeContext, } from "./context";
12
+ import { requireSaga } from "./init";
13
+ import { uuidv7 } from "./uuid";
14
+ import { SagaCompensationFailed, SagaStepFailed, } from "./types";
15
+ import { beginAsyncStep, SagaPausedSentinel } from "./async-step";
16
+ const nowIso = () => new Date().toISOString();
17
+ const TERMINAL_STATUSES = new Set([
18
+ "completed",
19
+ "compensated",
20
+ "compensation_failed",
21
+ "compensated_with_errors",
22
+ "compensated_manual",
23
+ "technical_failure",
24
+ "cancelled",
25
+ ]);
26
+ // We track per-saga undo stacks on the context so resume + cancel paths
27
+ // don't lose them. Keyed by saga.id to support nested withSaga calls.
28
+ const undoStacks = new Map();
29
+ function pushUndo(sagaId, record) {
30
+ const stack = undoStacks.get(sagaId) ?? [];
31
+ stack.push(record);
32
+ undoStacks.set(sagaId, stack);
33
+ }
34
+ function takeUndoStack(sagaId) {
35
+ const stack = undoStacks.get(sagaId) ?? [];
36
+ undoStacks.delete(sagaId);
37
+ return stack;
38
+ }
39
+ function newSagaRow(opts) {
40
+ const id = opts.sagaId ?? uuidv7();
41
+ return {
42
+ id,
43
+ tenant_id: opts.tenantId ?? null,
44
+ type: opts.type,
45
+ status: "active",
46
+ current_step: null,
47
+ step_outputs: {},
48
+ episode: 1,
49
+ trigger_trace_id: opts.triggerTraceId ?? id,
50
+ trigger_span_id: opts.triggerSpanId ?? id,
51
+ trigger_request_id: opts.triggerRequestId ?? id,
52
+ parent_saga_id: opts.parent?.sagaId ?? null,
53
+ parent_relationship: opts.parentRelationship ?? "child",
54
+ children: [],
55
+ started_at: nowIso(),
56
+ completed_at: null,
57
+ next_resume_at: null,
58
+ failure_reason: null,
59
+ failure_step: null,
60
+ compensation_log: [],
61
+ last_admin_action: null,
62
+ last_admin_actor_id: null,
63
+ last_admin_at: null,
64
+ input: opts.input,
65
+ cancel_reason: null,
66
+ };
67
+ }
68
+ async function emitAuditEvent(action, sagaId, payload) {
69
+ const cfg = requireSaga();
70
+ if (!cfg.telemetryAudit)
71
+ return;
72
+ await cfg.telemetryAudit.emit({
73
+ action,
74
+ target_kind: "saga",
75
+ target_id: sagaId,
76
+ payload,
77
+ });
78
+ }
79
+ /**
80
+ * § 5.2 + § 5.8 — createSaga.
81
+ *
82
+ * Resume detection: if a `ResumeContext` is primed on the ALS (set by
83
+ * `withSaga`), the function reloads the saga from the state store and
84
+ * passes the persisted `step_outputs` map so completed steps short-circuit
85
+ * to their cached output via the idempotency layer.
86
+ */
87
+ export async function createSaga(opts, fn) {
88
+ const cfg = requireSaga();
89
+ const resume = getResumeContext();
90
+ let row;
91
+ if (resume?.isResume) {
92
+ const existing = await cfg.stateStore.getSaga(resume.sagaId);
93
+ if (!existing) {
94
+ // Treat as a fresh saga — defensive: resume context referenced a row
95
+ // the store no longer has.
96
+ row = newSagaRow({ ...opts, sagaId: resume.sagaId });
97
+ await cfg.stateStore.createSaga(row);
98
+ await emitAuditEvent("saga_started", row.id, { type: row.type });
99
+ }
100
+ else {
101
+ row = existing;
102
+ await cfg.stateStore.updateSagaStatus(row.id, { status: "active" });
103
+ row.status = "active";
104
+ await emitAuditEvent("saga_resumed", row.id, { episode: row.episode });
105
+ }
106
+ }
107
+ else {
108
+ row = newSagaRow(opts);
109
+ await cfg.stateStore.createSaga(row);
110
+ await emitAuditEvent("saga_started", row.id, { type: row.type });
111
+ }
112
+ const handle = {
113
+ id: row.id,
114
+ type: row.type,
115
+ tenantId: row.tenant_id,
116
+ async cancel(reason) {
117
+ await cancelSaga(row.id, reason);
118
+ },
119
+ };
120
+ try {
121
+ const result = await runInSagaContext({ handle, row, resume, completedSteps: [] }, fn);
122
+ // If the saga was cancelled (or otherwise terminalized) mid-flight,
123
+ // do NOT overwrite to "completed". Re-read once to settle.
124
+ const latest = await cfg.stateStore.getSaga(row.id);
125
+ if (latest &&
126
+ !TERMINAL_STATUSES.has(latest.status) &&
127
+ latest.status !== "paused") {
128
+ await cfg.stateStore.updateSagaStatus(row.id, {
129
+ status: "completed",
130
+ completed_at: nowIso(),
131
+ });
132
+ await emitAuditEvent("saga_completed", row.id);
133
+ }
134
+ undoStacks.delete(row.id);
135
+ return result;
136
+ }
137
+ catch (err) {
138
+ // Pattern 2 sentinel — saga paused at an async step boundary. NOT a
139
+ // failure; the saga remains 'paused' and a downstream completion-event
140
+ // resumes it via withSaga(). Return the saga handle's id as the
141
+ // result placeholder (sentinel.sagaId).
142
+ if (err instanceof SagaPausedSentinel) {
143
+ // Don't touch status — beginAsyncStep already set it.
144
+ // Don't clear the undo stack — a future resume might need it.
145
+ return undefined;
146
+ }
147
+ // step() throws SagaStepFailed when its do-fn fails; we run compensation
148
+ // here so the user-facing API stays "createSaga rejects if any step
149
+ // fails after compensation runs". v0.1.0 compensation walks the per-saga
150
+ // undoStack in reverse.
151
+ if (err instanceof SagaStepFailed) {
152
+ await runCompensation(row.id, err.stepName);
153
+ throw err;
154
+ }
155
+ // Non-step errors (programming errors, etc.) propagate without changing
156
+ // status — the caller decides what to do.
157
+ throw err;
158
+ }
159
+ }
160
+ /**
161
+ * § 5.8 — withSaga primes a ResumeContext + an inner createSaga reloads.
162
+ *
163
+ * v0.1.0 surface: `withSaga(handle, fn)` runs `fn` in the saga's ALS scope
164
+ * with `resume.isResume = true`. The expected usage is for the inner
165
+ * function to call `createSaga` with the same type — that's how the registry
166
+ * gets re-entered.
167
+ */
168
+ export async function withSaga(sagaId, fn) {
169
+ const ctx = { isResume: true, sagaId };
170
+ return runWithResumeContext(ctx, fn);
171
+ }
172
+ /**
173
+ * § 5.2 — step.
174
+ *
175
+ * Pipeline:
176
+ * 1. update saga_state.current_step = name
177
+ * 2. run handler via idempotency.wrapForSagaStep
178
+ * 3. on success: persist output, push undo record onto saga's undo stack
179
+ * 4. on failure: throw SagaStepFailed; createSaga catches + runs compensation
180
+ *
181
+ * `mode: 'best-effort'` per-step (or via the undo object form) makes
182
+ * compensation lenient — a thrown undo records `compensated_with_errors`
183
+ * instead of `compensation_failed`.
184
+ */
185
+ export async function step(name, opts) {
186
+ if (opts.async === true) {
187
+ // Pattern 2: beginAsyncStep persists `paused` + writes the outbox row
188
+ // and throws SagaPausedSentinel — control unwinds back through
189
+ // createSaga which catches the sentinel and returns the saga handle.
190
+ return beginAsyncStep(name, opts);
191
+ }
192
+ const cfg = requireSaga();
193
+ const sagaCtx = requireSagaContext("step");
194
+ const row = sagaCtx.row;
195
+ await cfg.stateStore.updateSagaStatus(row.id, { current_step: name });
196
+ row.current_step = name;
197
+ // Idempotency-key payload — narrow + alphabetically-keyed JSON to be
198
+ // byte-equal with the Go (stepKeyPayload struct) and Python
199
+ // (json.dumps(..., sort_keys=True)) ports. Spec § 5.4: the cache key
200
+ // is sha256(saga_id + step_name + serialized_input) and parity-fence
201
+ // requires the three languages produce the same hash.
202
+ let serializedInput;
203
+ try {
204
+ const payload = {
205
+ name,
206
+ persist_fields: [...(opts.persistFields ?? [])].sort(),
207
+ };
208
+ if (opts.input !== undefined)
209
+ payload.input = opts.input;
210
+ // Stable key order: input < name < persist_fields (alphabetical).
211
+ serializedInput = JSON.stringify(payload, [
212
+ "input",
213
+ "name",
214
+ "persist_fields",
215
+ ]);
216
+ }
217
+ catch {
218
+ serializedInput = `${name}::nonserializable::${row.id}`;
219
+ }
220
+ const handler = opts.do;
221
+ let result;
222
+ try {
223
+ result = await cfg.idempotency.wrapForSagaStep({
224
+ sagaId: row.id,
225
+ stepName: name,
226
+ serializedInput: opts.idempotencyKey ?? serializedInput,
227
+ handler,
228
+ });
229
+ }
230
+ catch (err) {
231
+ // Surface the store-write failure alongside the step failure —
232
+ // matches Go's errors.Join + Python's logger.warn-then-raise.
233
+ // Silently swallowing it would lose visibility into transient
234
+ // production-store errors.
235
+ try {
236
+ await cfg.stateStore.updateSagaStatus(row.id, {
237
+ failure_reason: err instanceof Error ? err.message : String(err),
238
+ failure_step: name,
239
+ });
240
+ }
241
+ catch (updErr) {
242
+ cfg.logger?.warn?.("updateSagaStatus failed while recording step failure", {
243
+ saga_id: row.id,
244
+ step: name,
245
+ error: updErr instanceof Error ? updErr.message : String(updErr),
246
+ });
247
+ }
248
+ throw new SagaStepFailed(name, err);
249
+ }
250
+ // Persist the output + push the undo record (when undo provided).
251
+ await cfg.stateStore.appendStepOutput(row.id, name, result);
252
+ row.step_outputs[name] = result;
253
+ sagaCtx.completedSteps.push({ name, output: result });
254
+ if (opts.undo) {
255
+ const { fn: undoFn, mode } = normalizeUndo(opts.undo, opts.mode);
256
+ pushUndo(row.id, {
257
+ stepName: name,
258
+ output: result,
259
+ fn: undoFn,
260
+ mode,
261
+ });
262
+ }
263
+ else {
264
+ // Even when there's no undo, record an entry so compensation walks can
265
+ // log "skipped_no_undo" — keeps the compensation_log shape consistent.
266
+ pushUndo(row.id, {
267
+ stepName: name,
268
+ output: result,
269
+ fn: async () => {
270
+ /* no-op; logged as skipped_no_undo */
271
+ },
272
+ mode: opts.mode === "best-effort" ? "best-effort" : "strict",
273
+ });
274
+ }
275
+ return result;
276
+ }
277
+ function normalizeUndo(undo, stepMode) {
278
+ if (typeof undo === "function") {
279
+ return {
280
+ fn: undo,
281
+ mode: stepMode === "best-effort" ? "best-effort" : "strict",
282
+ };
283
+ }
284
+ return { fn: undo.fn, mode: undo.mode };
285
+ }
286
+ /**
287
+ * § 5.2 — stepParallel. Fans out concurrently; collects results into an array.
288
+ *
289
+ * Compensation semantics match `step` — undos are pushed onto the stack in
290
+ * the order they completed, so reverse-order traversal is preserved.
291
+ */
292
+ export async function stepParallel(steps) {
293
+ return Promise.all(steps.map((s) => step(s.name, s.opts)));
294
+ }
295
+ async function runCompensation(sagaId, failedStep) {
296
+ const cfg = requireSaga();
297
+ await cfg.stateStore.updateSagaStatus(sagaId, {
298
+ status: "compensating",
299
+ failure_step: failedStep,
300
+ });
301
+ await emitAuditEvent("saga_compensation_started", sagaId, {
302
+ failed_step: failedStep,
303
+ });
304
+ const stack = takeUndoStack(sagaId).reverse();
305
+ let hadLenientError = false;
306
+ for (const entry of stack) {
307
+ if (entry.stepName === failedStep) {
308
+ // The failing step never produced a checkpoint we trust, so we skip
309
+ // its undo. compensation_log records skipped_no_undo for visibility.
310
+ const skipEntry = {
311
+ step_name: entry.stepName,
312
+ outcome: "skipped_no_undo",
313
+ ts: nowIso(),
314
+ };
315
+ await cfg.stateStore.appendCompensationLog(sagaId, skipEntry);
316
+ continue;
317
+ }
318
+ try {
319
+ await entry.fn(entry.output);
320
+ const okEntry = {
321
+ step_name: entry.stepName,
322
+ outcome: "undone",
323
+ ts: nowIso(),
324
+ };
325
+ await cfg.stateStore.appendCompensationLog(sagaId, okEntry);
326
+ }
327
+ catch (undoErr) {
328
+ const failEntry = {
329
+ step_name: entry.stepName,
330
+ outcome: "undo_failed",
331
+ error: undoErr instanceof Error ? undoErr.message : String(undoErr),
332
+ ts: nowIso(),
333
+ };
334
+ await cfg.stateStore.appendCompensationLog(sagaId, failEntry);
335
+ if (entry.mode === "strict") {
336
+ await cfg.stateStore.updateSagaStatus(sagaId, {
337
+ status: "compensation_failed",
338
+ });
339
+ await emitAuditEvent("saga_compensation_failed", sagaId, {
340
+ step: entry.stepName,
341
+ error: failEntry.error,
342
+ });
343
+ throw new SagaCompensationFailed(entry.stepName, undoErr);
344
+ }
345
+ hadLenientError = true;
346
+ }
347
+ }
348
+ const terminal = hadLenientError ? "compensated_with_errors" : "compensated";
349
+ await cfg.stateStore.updateSagaStatus(sagaId, { status: terminal });
350
+ if (hadLenientError) {
351
+ await emitAuditEvent("saga_compensation_completed", sagaId, {
352
+ outcome: "compensated_with_errors",
353
+ });
354
+ }
355
+ else {
356
+ await emitAuditEvent("saga_compensation_completed", sagaId);
357
+ }
358
+ }
359
+ async function cancelSaga(sagaId, reason) {
360
+ const cfg = requireSaga();
361
+ await cfg.stateStore.updateSagaStatus(sagaId, {
362
+ status: "cancelled",
363
+ cancel_reason: reason,
364
+ completed_at: nowIso(),
365
+ });
366
+ if (cfg.telemetryAudit) {
367
+ await cfg.telemetryAudit.emit({
368
+ action: "saga_admin_cancel",
369
+ target_kind: "saga",
370
+ target_id: sagaId,
371
+ payload: { reason },
372
+ });
373
+ }
374
+ undoStacks.delete(sagaId);
375
+ }
376
+ /** Public cancel entry-point (also reachable via handle.cancel). */
377
+ export async function cancel(sagaId, reason) {
378
+ await cancelSaga(sagaId, reason);
379
+ }
380
+ /** Read-only accessor for the active saga handle in scope. */
381
+ export function currentSaga() {
382
+ return getSagaContext()?.handle ?? null;
383
+ }
384
+ //# sourceMappingURL=saga.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga.js","sourceRoot":"","sources":["../src/saga.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { RedisSignalBus, type RedisSignalBusOpts } from "./redis-stream";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signal-bus/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,6 @@
1
+ // Production-bound signal-bus exports.
2
+ //
3
+ // `RedisSignalBus` is the only signal bus production code should use.
4
+ // For tests, see `@nodii/saga/test-doubles`.
5
+ export { RedisSignalBus } from "./redis-stream";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signal-bus/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,sEAAsE;AACtE,6CAA6C;AAE7C,OAAO,EAAE,cAAc,EAA2B,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { Redis } from "ioredis";
2
+ import type { SagaSignalBus } from "../types";
3
+ export interface RedisSignalBusOpts {
4
+ redis: Redis;
5
+ serviceId: string;
6
+ /** Override the stream name; defaults to nodii:<serviceId>:saga:signals. */
7
+ streamName?: string;
8
+ /** XREADGROUP block window in ms (default 50). */
9
+ blockMs?: number;
10
+ }
11
+ export declare class RedisSignalBus implements SagaSignalBus {
12
+ private readonly redis;
13
+ private readonly stream;
14
+ private readonly blockMs;
15
+ private readonly groupsEnsured;
16
+ constructor(opts: RedisSignalBusOpts);
17
+ emit(args: {
18
+ sagaId: string;
19
+ eventType: string;
20
+ payload: unknown;
21
+ scope?: "siblings" | "global";
22
+ to?: string;
23
+ }): Promise<void>;
24
+ consume(args: {
25
+ sagaId: string;
26
+ eventType: string;
27
+ from?: string;
28
+ predicate?: (payload: unknown) => boolean;
29
+ }): Promise<unknown | null>;
30
+ list(args: {
31
+ sagaId: string;
32
+ eventType?: string;
33
+ }): Promise<{
34
+ sagaId: string;
35
+ eventType: string;
36
+ payload: unknown;
37
+ }[]>;
38
+ /** Test helper — drop the stream + reset ensure cache. */
39
+ resetForTests(): Promise<void>;
40
+ close(): Promise<void>;
41
+ private ensureGroup;
42
+ }
43
+ //# sourceMappingURL=redis-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-stream.d.ts","sourceRoot":"","sources":["../../src/signal-bus/redis-stream.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAWD,qBAAa,cAAe,YAAW,aAAa;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAIjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;gBAEvC,IAAI,EAAE,kBAAkB;IAM9B,IAAI,CAAC,IAAI,EAAE;QACf,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;QAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBX,OAAO,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;KAC3C,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAwDrB,IAAI,CAAC,IAAI,EAAE;QACf,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAyBtE,0DAA0D;IACpD,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAId,WAAW;CAa1B"}