@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.
- package/dist/admin-service.d.ts +55 -0
- package/dist/admin-service.d.ts.map +1 -0
- package/dist/admin-service.js +161 -0
- package/dist/admin-service.js.map +1 -0
- package/dist/async-step.d.ts +43 -0
- package/dist/async-step.d.ts.map +1 -0
- package/dist/async-step.js +166 -0
- package/dist/async-step.js.map +1 -0
- package/dist/context.d.ts +18 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +28 -0
- package/dist/context.js.map +1 -0
- package/dist/decorator.d.ts +24 -0
- package/dist/decorator.d.ts.map +1 -0
- package/dist/decorator.js +69 -0
- package/dist/decorator.js.map +1 -0
- package/dist/idempotency.d.ts +22 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +44 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/index.d.ts +20 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -4
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +45 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +86 -0
- package/dist/init.js.map +1 -0
- package/dist/interceptor.d.ts +5 -0
- package/dist/interceptor.d.ts.map +1 -0
- package/dist/interceptor.js +34 -0
- package/dist/interceptor.js.map +1 -0
- package/dist/migrations/index.d.ts +19 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +106 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/reaper.d.ts +27 -0
- package/dist/reaper.d.ts.map +1 -0
- package/dist/reaper.js +79 -0
- package/dist/reaper.js.map +1 -0
- package/dist/registry.d.ts +7 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +23 -0
- package/dist/registry.js.map +1 -0
- package/dist/saga.d.ts +48 -0
- package/dist/saga.d.ts.map +1 -0
- package/dist/saga.js +384 -0
- package/dist/saga.js.map +1 -0
- package/dist/signal-bus/index.d.ts +2 -0
- package/dist/signal-bus/index.d.ts.map +1 -0
- package/dist/signal-bus/index.js +6 -0
- package/dist/signal-bus/index.js.map +1 -0
- package/dist/signal-bus/redis-stream.d.ts +43 -0
- package/dist/signal-bus/redis-stream.d.ts.map +1 -0
- package/dist/signal-bus/redis-stream.js +189 -0
- package/dist/signal-bus/redis-stream.js.map +1 -0
- package/dist/signals.d.ts +29 -0
- package/dist/signals.d.ts.map +1 -0
- package/dist/signals.js +113 -0
- package/dist/signals.js.map +1 -0
- package/dist/state-store/index.d.ts +2 -0
- package/dist/state-store/index.d.ts.map +1 -0
- package/dist/state-store/index.js +6 -0
- package/dist/state-store/index.js.map +1 -0
- package/dist/state-store/postgres.d.ts +47 -0
- package/dist/state-store/postgres.d.ts.map +1 -0
- package/dist/state-store/postgres.js +270 -0
- package/dist/state-store/postgres.js.map +1 -0
- package/dist/test-doubles/in-memory-signal-bus.d.ts +38 -0
- package/dist/test-doubles/in-memory-signal-bus.d.ts.map +1 -0
- package/dist/test-doubles/in-memory-signal-bus.js +68 -0
- package/dist/test-doubles/in-memory-signal-bus.js.map +1 -0
- package/dist/test-doubles/in-memory-state-store.d.ts +26 -0
- package/dist/test-doubles/in-memory-state-store.d.ts.map +1 -0
- package/dist/test-doubles/in-memory-state-store.js +87 -0
- package/dist/test-doubles/in-memory-state-store.js.map +1 -0
- package/dist/test-doubles/index.d.ts +4 -0
- package/dist/test-doubles/index.d.ts.map +1 -0
- package/dist/test-doubles/index.js +14 -0
- package/dist/test-doubles/index.js.map +1 -0
- package/dist/types.d.ts +260 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +103 -0
- package/dist/types.js.map +1 -0
- package/dist/uuid.d.ts +3 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +62 -0
- package/dist/uuid.js.map +1 -0
- package/package.json +33 -7
- package/src/migrations/001-saga-state.sql +79 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// RedisSignalBus — real, Redis-Streams-backed implementation of
|
|
2
|
+
// `SagaSignalBus`. Spec § 5.9 + comm doctrine § 9.2.
|
|
3
|
+
//
|
|
4
|
+
// Wire design:
|
|
5
|
+
// - One Redis stream per service: `nodii:<serviceId>:saga:signals`.
|
|
6
|
+
// - Each saga that calls `awaitSagaSignal` opens an XREAD with its own
|
|
7
|
+
// consumer group keyed by `saga:<sagaId>`. This gives per-saga
|
|
8
|
+
// isolation — a parent and child saga listening for the same event
|
|
9
|
+
// type don't steal each other's messages.
|
|
10
|
+
// - Sibling fan-out uses the `to` field on the message body; the
|
|
11
|
+
// consume() filter rejects messages addressed to a different saga.
|
|
12
|
+
// - We claim with a short block (50ms) so callers can poll without
|
|
13
|
+
// blocking forever; the higher-level `awaitSagaSignal` does the
|
|
14
|
+
// timeout-budget loop.
|
|
15
|
+
export class RedisSignalBus {
|
|
16
|
+
redis;
|
|
17
|
+
stream;
|
|
18
|
+
blockMs;
|
|
19
|
+
// Consumer group / consumer name dedup. We attempt MKSTREAM-create
|
|
20
|
+
// lazily; second concurrent call collides but Redis errors are
|
|
21
|
+
// benignly ignored.
|
|
22
|
+
groupsEnsured = new Set();
|
|
23
|
+
constructor(opts) {
|
|
24
|
+
this.redis = opts.redis;
|
|
25
|
+
this.stream = opts.streamName ?? `nodii:${opts.serviceId}:saga:signals`;
|
|
26
|
+
this.blockMs = opts.blockMs ?? 50;
|
|
27
|
+
}
|
|
28
|
+
async emit(args) {
|
|
29
|
+
await this.redis.xadd(this.stream, "*", "saga_id", args.sagaId, "event_type", args.eventType, "payload", JSON.stringify(args.payload ?? null), "scope", args.scope ?? "siblings", "to", args.to ?? "");
|
|
30
|
+
}
|
|
31
|
+
async consume(args) {
|
|
32
|
+
const group = `saga:${args.sagaId}`;
|
|
33
|
+
await this.ensureGroup(group);
|
|
34
|
+
const consumer = `c:${args.sagaId}`;
|
|
35
|
+
// XREADGROUP with `>` to pick up new messages only. We pull up to 32
|
|
36
|
+
// and filter in-memory because Redis Streams don't support
|
|
37
|
+
// server-side payload predicates.
|
|
38
|
+
// biome-ignore lint/suspicious/noExplicitAny: ioredis XREADGROUP returns nested string arrays — we narrow to the structure we know
|
|
39
|
+
const raw = await this.redis.xreadgroup("GROUP", group, consumer, "COUNT", 32, "BLOCK", this.blockMs, "STREAMS", this.stream, ">");
|
|
40
|
+
if (!raw)
|
|
41
|
+
return null;
|
|
42
|
+
const messages = extractMessages(raw, this.stream);
|
|
43
|
+
for (const msg of messages) {
|
|
44
|
+
const sig = parseSignal(msg.id, msg.fields);
|
|
45
|
+
if (!sig) {
|
|
46
|
+
// Bad message — XACK + skip.
|
|
47
|
+
await this.redis.xack(this.stream, group, msg.id);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (sig.eventType !== args.eventType) {
|
|
51
|
+
// Not what this caller wants — XACK so the same group doesn't
|
|
52
|
+
// see it again (it's addressed to the saga, not to a particular
|
|
53
|
+
// event-type subscription).
|
|
54
|
+
await this.redis.xack(this.stream, group, msg.id);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (sig.to && sig.to !== args.sagaId) {
|
|
58
|
+
await this.redis.xack(this.stream, group, msg.id);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (args.from && sig.sagaId !== args.from) {
|
|
62
|
+
await this.redis.xack(this.stream, group, msg.id);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (args.predicate && !args.predicate(sig.payload)) {
|
|
66
|
+
await this.redis.xack(this.stream, group, msg.id);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
await this.redis.xack(this.stream, group, msg.id);
|
|
70
|
+
return sig.payload;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
async list(args) {
|
|
75
|
+
// XRANGE pulls the full stream; for v0.1.0 this is acceptable since
|
|
76
|
+
// saga.signals is per-service and bounded. Production may layer
|
|
77
|
+
// pagination on top.
|
|
78
|
+
// biome-ignore lint/suspicious/noExplicitAny: XRANGE returns [id, [field, value, ...]][]
|
|
79
|
+
const raw = await this.redis.xrange(this.stream, "-", "+");
|
|
80
|
+
const out = [];
|
|
81
|
+
if (!Array.isArray(raw))
|
|
82
|
+
return out;
|
|
83
|
+
for (const entry of raw) {
|
|
84
|
+
if (!Array.isArray(entry) || entry.length < 2)
|
|
85
|
+
continue;
|
|
86
|
+
const id = entry[0];
|
|
87
|
+
const fields = entry[1];
|
|
88
|
+
const sig = parseSignal(id, fields);
|
|
89
|
+
if (!sig)
|
|
90
|
+
continue;
|
|
91
|
+
if (sig.to && sig.to !== args.sagaId)
|
|
92
|
+
continue;
|
|
93
|
+
if (args.eventType && sig.eventType !== args.eventType)
|
|
94
|
+
continue;
|
|
95
|
+
out.push({
|
|
96
|
+
sagaId: sig.sagaId,
|
|
97
|
+
eventType: sig.eventType,
|
|
98
|
+
payload: sig.payload,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** Test helper — drop the stream + reset ensure cache. */
|
|
104
|
+
async resetForTests() {
|
|
105
|
+
try {
|
|
106
|
+
await this.redis.del(this.stream);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
/* swallow */
|
|
110
|
+
}
|
|
111
|
+
this.groupsEnsured.clear();
|
|
112
|
+
}
|
|
113
|
+
async close() {
|
|
114
|
+
/* leaves Redis connection management to the caller */
|
|
115
|
+
}
|
|
116
|
+
async ensureGroup(group) {
|
|
117
|
+
if (this.groupsEnsured.has(group))
|
|
118
|
+
return;
|
|
119
|
+
try {
|
|
120
|
+
await this.redis.xgroup("CREATE", this.stream, group, "0", "MKSTREAM");
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
// BUSYGROUP is fine — group already exists.
|
|
124
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
125
|
+
if (!msg.includes("BUSYGROUP")) {
|
|
126
|
+
// Other errors are surfaced; XREADGROUP would also fail.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this.groupsEnsured.add(group);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function extractMessages(raw, expectedStream) {
|
|
133
|
+
// raw shape: [[ streamName, [[ id, [field, value, ...] ], ...] ], ...]
|
|
134
|
+
const out = [];
|
|
135
|
+
if (!Array.isArray(raw))
|
|
136
|
+
return out;
|
|
137
|
+
for (const streamEntry of raw) {
|
|
138
|
+
if (!Array.isArray(streamEntry) || streamEntry.length < 2)
|
|
139
|
+
continue;
|
|
140
|
+
const streamName = streamEntry[0];
|
|
141
|
+
if (streamName !== expectedStream)
|
|
142
|
+
continue;
|
|
143
|
+
const entries = streamEntry[1];
|
|
144
|
+
if (!Array.isArray(entries))
|
|
145
|
+
continue;
|
|
146
|
+
for (const entry of entries) {
|
|
147
|
+
if (!Array.isArray(entry) || entry.length < 2)
|
|
148
|
+
continue;
|
|
149
|
+
const id = entry[0];
|
|
150
|
+
const fields = entry[1];
|
|
151
|
+
if (typeof id === "string" && Array.isArray(fields)) {
|
|
152
|
+
out.push({ id, fields: fields });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
function parseSignal(id, fields) {
|
|
159
|
+
const map = new Map();
|
|
160
|
+
for (let i = 0; i + 1 < fields.length; i += 2) {
|
|
161
|
+
const k = fields[i];
|
|
162
|
+
const v = fields[i + 1];
|
|
163
|
+
if (k !== undefined && v !== undefined)
|
|
164
|
+
map.set(k, v);
|
|
165
|
+
}
|
|
166
|
+
const sagaId = map.get("saga_id");
|
|
167
|
+
const eventType = map.get("event_type");
|
|
168
|
+
const payloadStr = map.get("payload");
|
|
169
|
+
if (!sagaId || !eventType || payloadStr === undefined)
|
|
170
|
+
return null;
|
|
171
|
+
let payload;
|
|
172
|
+
try {
|
|
173
|
+
payload = JSON.parse(payloadStr);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
payload = payloadStr;
|
|
177
|
+
}
|
|
178
|
+
const scope = map.get("scope") === "global" ? "global" : "siblings";
|
|
179
|
+
const to = map.get("to");
|
|
180
|
+
return {
|
|
181
|
+
id,
|
|
182
|
+
sagaId,
|
|
183
|
+
eventType,
|
|
184
|
+
payload,
|
|
185
|
+
scope,
|
|
186
|
+
to: to === undefined || to === "" ? null : to,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=redis-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-stream.js","sourceRoot":"","sources":["../../src/signal-bus/redis-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare function emitSagaSignal(eventType: string, payload: unknown, opts?: {
|
|
2
|
+
to?: string;
|
|
3
|
+
scope?: "siblings" | "global";
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
export declare function awaitSagaSignal<T = unknown>(eventType: string, opts?: {
|
|
6
|
+
from?: string;
|
|
7
|
+
predicate?: (payload: unknown) => boolean;
|
|
8
|
+
/** Poll budget in milliseconds. Default 0 = single check. */
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
/** Test hook — defaults to setTimeout. */
|
|
11
|
+
sleep?: (ms: number) => Promise<void>;
|
|
12
|
+
}): Promise<T | null>;
|
|
13
|
+
export declare function awaitChildren(childIds: readonly string[], opts?: {
|
|
14
|
+
status?: "completed" | "any-terminal";
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
sleep?: (ms: number) => Promise<void>;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
export declare function awaitAnySibling<T = unknown>(eventType: string, opts?: {
|
|
19
|
+
predicate?: (payload: unknown) => boolean;
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
sleep?: (ms: number) => Promise<void>;
|
|
22
|
+
}): Promise<T | null>;
|
|
23
|
+
export declare function getSiblings(opts?: {
|
|
24
|
+
status?: string;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
id: string;
|
|
27
|
+
status: string;
|
|
28
|
+
}[]>;
|
|
29
|
+
//# sourceMappingURL=signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAOA,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAA;CAAO,GACxD,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAC/C,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1C,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC,GACL,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CA8BnB;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,IAAI,GAAE;IACJ,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC,GACL,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAYD,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAC/C,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE;IACJ,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC,GACL,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAEnB;AAED,wBAAsB,WAAW,CAC/B,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAc3C"}
|
package/dist/signals.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Cross-saga primitives — § 5.9. Layered on the pluggable SagaSignalBus so
|
|
2
|
+
// the v0.1.0 in-memory bus and the deferred Redis-stream bus expose the
|
|
3
|
+
// same surface.
|
|
4
|
+
import { requireSagaContext } from "./context";
|
|
5
|
+
import { requireSaga } from "./init";
|
|
6
|
+
export async function emitSagaSignal(eventType, payload, opts = {}) {
|
|
7
|
+
const cfg = requireSaga();
|
|
8
|
+
const ctx = requireSagaContext("emitSagaSignal");
|
|
9
|
+
await cfg.signalBus.emit({
|
|
10
|
+
sagaId: ctx.row.id,
|
|
11
|
+
eventType,
|
|
12
|
+
payload,
|
|
13
|
+
scope: opts.scope ?? "siblings",
|
|
14
|
+
to: opts.to,
|
|
15
|
+
});
|
|
16
|
+
if (cfg.telemetryAudit) {
|
|
17
|
+
await cfg.telemetryAudit.emit({
|
|
18
|
+
action: "saga_signal_emitted",
|
|
19
|
+
target_kind: "saga",
|
|
20
|
+
target_id: ctx.row.id,
|
|
21
|
+
payload: { event_type: eventType },
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function awaitSagaSignal(eventType, opts = {}) {
|
|
26
|
+
const cfg = requireSaga();
|
|
27
|
+
const ctx = requireSagaContext("awaitSagaSignal");
|
|
28
|
+
const start = Date.now();
|
|
29
|
+
const timeoutMs = opts.timeoutMs ?? 0;
|
|
30
|
+
const sleep = opts.sleep ??
|
|
31
|
+
((ms) => new Promise((res) => setTimeout(res, ms)));
|
|
32
|
+
for (;;) {
|
|
33
|
+
const hit = await cfg.signalBus.consume({
|
|
34
|
+
sagaId: ctx.row.id,
|
|
35
|
+
eventType,
|
|
36
|
+
from: opts.from,
|
|
37
|
+
predicate: opts.predicate,
|
|
38
|
+
});
|
|
39
|
+
if (hit !== null) {
|
|
40
|
+
if (cfg.telemetryAudit) {
|
|
41
|
+
await cfg.telemetryAudit.emit({
|
|
42
|
+
action: "saga_signal_received",
|
|
43
|
+
target_kind: "saga",
|
|
44
|
+
target_id: ctx.row.id,
|
|
45
|
+
payload: { event_type: eventType },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return hit;
|
|
49
|
+
}
|
|
50
|
+
if (Date.now() - start >= timeoutMs)
|
|
51
|
+
return null;
|
|
52
|
+
await sleep(10);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function awaitChildren(childIds, opts = {}) {
|
|
56
|
+
const cfg = requireSaga();
|
|
57
|
+
requireSagaContext("awaitChildren");
|
|
58
|
+
const want = opts.status ?? "any-terminal";
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
const timeoutMs = opts.timeoutMs ?? 0;
|
|
61
|
+
const sleep = opts.sleep ??
|
|
62
|
+
((ms) => new Promise((res) => setTimeout(res, ms)));
|
|
63
|
+
for (;;) {
|
|
64
|
+
let allDone = true;
|
|
65
|
+
for (const childId of childIds) {
|
|
66
|
+
const row = await cfg.stateStore.getSaga(childId);
|
|
67
|
+
if (!row) {
|
|
68
|
+
allDone = false;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
if (want === "completed" && row.status !== "completed") {
|
|
72
|
+
allDone = false;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
if (want === "any-terminal" && !TERMINAL_STATUSES.has(row.status)) {
|
|
76
|
+
allDone = false;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (allDone)
|
|
81
|
+
return;
|
|
82
|
+
if (Date.now() - start >= timeoutMs)
|
|
83
|
+
return;
|
|
84
|
+
await sleep(10);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const TERMINAL_STATUSES = new Set([
|
|
88
|
+
"completed",
|
|
89
|
+
"compensated",
|
|
90
|
+
"compensation_failed",
|
|
91
|
+
"compensated_with_errors",
|
|
92
|
+
"compensated_manual",
|
|
93
|
+
"technical_failure",
|
|
94
|
+
"cancelled",
|
|
95
|
+
]);
|
|
96
|
+
export async function awaitAnySibling(eventType, opts = {}) {
|
|
97
|
+
return awaitSagaSignal(eventType, opts);
|
|
98
|
+
}
|
|
99
|
+
export async function getSiblings(opts = {}) {
|
|
100
|
+
const cfg = requireSaga();
|
|
101
|
+
const ctx = requireSagaContext("getSiblings");
|
|
102
|
+
if (!ctx.row.parent_saga_id)
|
|
103
|
+
return [];
|
|
104
|
+
// SagaStateStore.list is the public async accessor (matches Py + Go).
|
|
105
|
+
// Awaiting is mandatory — earlier versions cast to sync and `.filter`'d
|
|
106
|
+
// a Promise; the resulting TypeError was hidden by the cast.
|
|
107
|
+
const all = (await cfg.stateStore.list?.({})) ?? [];
|
|
108
|
+
return all
|
|
109
|
+
.filter((r) => r.parent_saga_id === ctx.row.parent_saga_id && r.id !== ctx.row.id)
|
|
110
|
+
.filter((r) => !opts.status || r.status === opts.status)
|
|
111
|
+
.map((r) => ({ id: r.id, status: r.status }));
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.js","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state-store/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,sBAAsB,EACtB,KAAK,0BAA0B,GAChC,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state-store/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,0EAA0E;AAC1E,kDAAkD;AAElD,OAAO,EACL,sBAAsB,GAEvB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CompensationLogEntry, ListSagasFilter, SagaStateRow, SagaStateStore } from "../types";
|
|
2
|
+
type SqlClient = {
|
|
3
|
+
unsafe: (sql: string, args?: unknown[]) => Promise<any>;
|
|
4
|
+
end?: () => Promise<void>;
|
|
5
|
+
};
|
|
6
|
+
export interface PostgresSagaStateStoreOpts {
|
|
7
|
+
/** Live postgres.js client (or any object exposing `.unsafe(sql, args)`). */
|
|
8
|
+
sql: SqlClient;
|
|
9
|
+
/** Override the default table name; defaults to `saga_state`. */
|
|
10
|
+
tableName?: string;
|
|
11
|
+
/** Optional hook fired when an entry is truncated; used by tests + metrics. */
|
|
12
|
+
onTruncate?: (info: {
|
|
13
|
+
sagaId: string;
|
|
14
|
+
stepName: string;
|
|
15
|
+
originalBytes: number;
|
|
16
|
+
}) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare class PostgresSagaStateStore implements SagaStateStore {
|
|
19
|
+
private readonly sql;
|
|
20
|
+
private readonly table;
|
|
21
|
+
private readonly onTruncate?;
|
|
22
|
+
constructor(opts: PostgresSagaStateStoreOpts);
|
|
23
|
+
createSaga(row: SagaStateRow): Promise<void>;
|
|
24
|
+
getSaga(sagaId: string): Promise<SagaStateRow | null>;
|
|
25
|
+
updateSagaStatus(sagaId: string, update: Partial<Pick<SagaStateRow, "status" | "current_step" | "completed_at" | "failure_reason" | "failure_step" | "last_admin_action" | "last_admin_actor_id" | "last_admin_at" | "cancel_reason" | "next_resume_at">>): Promise<void>;
|
|
26
|
+
appendStepOutput(sagaId: string, stepName: string, output: unknown): Promise<void>;
|
|
27
|
+
appendCompensationLog(sagaId: string, entry: CompensationLogEntry): Promise<void>;
|
|
28
|
+
list(filter?: ListSagasFilter): Promise<SagaStateRow[]>;
|
|
29
|
+
/** Reaper-side scan — used by `startSagaReaper`. */
|
|
30
|
+
listStalePaused(cutoffIso: string): Promise<SagaStateRow[]>;
|
|
31
|
+
/** Outbox helpers — see `async-step.ts`. */
|
|
32
|
+
writeOutboxBegin(args: {
|
|
33
|
+
sagaId: string;
|
|
34
|
+
stepName: string;
|
|
35
|
+
eventType: string;
|
|
36
|
+
payload: unknown;
|
|
37
|
+
tenantId: string | null;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
readOutboxForSaga(sagaId: string): Promise<{
|
|
40
|
+
stepName: string;
|
|
41
|
+
eventType: string;
|
|
42
|
+
payload: unknown;
|
|
43
|
+
createdAt: string;
|
|
44
|
+
}[]>;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/state-store/postgres.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,cAAc,EAEf,MAAM,UAAU,CAAC;AAIlB,KAAK,SAAS,GAAG;IAGf,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACxD,GAAG,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,CAAC;AAIF,MAAM,WAAW,0BAA0B;IACzC,6EAA6E;IAC7E,GAAG,EAAE,SAAS,CAAC;IACf,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;KACvB,KAAK,IAAI,CAAC;CACZ;AAED,qBAAa,sBAAuB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAIjB;gBAEC,IAAI,EAAE,0BAA0B;IAMtC,UAAU,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C5C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAmBrD,gBAAgB,CACpB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,CACb,IAAI,CACF,YAAY,EACV,QAAQ,GACR,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,cAAc,GACd,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,eAAe,GACf,gBAAgB,CACnB,CACF,GACA,OAAO,CAAC,IAAI,CAAC;IAkCV,gBAAgB,CACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC;IA6BV,qBAAqB,CACzB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,IAAI,CAAC;IAQV,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAmC7D,oDAAoD;IAC9C,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAiBjE,4CAA4C;IACtC,gBAAgB,CAAC,IAAI,EAAE;QAC3B,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYX,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAC9C;QACE,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,EAAE,CACJ;CAiBF"}
|