@dudousxd/nestjs-durable-core 0.51.0 → 0.53.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/index.cjs +198 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -1
- package/dist/index.d.ts +125 -1
- package/dist/index.js +196 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -173,6 +173,13 @@ function currentWorkflowCtx() {
|
|
|
173
173
|
}
|
|
174
174
|
__name(currentWorkflowCtx, "currentWorkflowCtx");
|
|
175
175
|
|
|
176
|
+
// src/control-flow-signal.ts
|
|
177
|
+
var CONTROL_FLOW_SIGNAL = Symbol.for("aviary:durable:control-flow");
|
|
178
|
+
function isWorkflowControlFlowSignal(error) {
|
|
179
|
+
return typeof error === "object" && error !== null && error[CONTROL_FLOW_SIGNAL] === true;
|
|
180
|
+
}
|
|
181
|
+
__name(isWorkflowControlFlowSignal, "isWorkflowControlFlowSignal");
|
|
182
|
+
|
|
176
183
|
// src/errors.ts
|
|
177
184
|
var FatalError = class extends Error {
|
|
178
185
|
static {
|
|
@@ -271,6 +278,8 @@ var WorkflowSuspended = class extends Error {
|
|
|
271
278
|
}
|
|
272
279
|
/** Epoch ms to auto-resume (durable sleep), or undefined when waiting on an external signal. */
|
|
273
280
|
wakeAt;
|
|
281
|
+
/** Marks this as a control-flow signal — see {@link isWorkflowControlFlowSignal}. */
|
|
282
|
+
[CONTROL_FLOW_SIGNAL] = true;
|
|
274
283
|
constructor(wakeAt) {
|
|
275
284
|
super("workflow suspended");
|
|
276
285
|
this.name = "WorkflowSuspended";
|
|
@@ -282,6 +291,8 @@ var ContinueAsNew = class extends Error {
|
|
|
282
291
|
__name(this, "ContinueAsNew");
|
|
283
292
|
}
|
|
284
293
|
input;
|
|
294
|
+
/** Marks this as a control-flow signal — see {@link isWorkflowControlFlowSignal}. */
|
|
295
|
+
[CONTROL_FLOW_SIGNAL] = true;
|
|
285
296
|
constructor(input) {
|
|
286
297
|
super("workflow continued as new"), this.input = input;
|
|
287
298
|
this.name = "ContinueAsNew";
|
|
@@ -854,6 +865,47 @@ var RemoteWorkflowExecutor = class {
|
|
|
854
865
|
}
|
|
855
866
|
};
|
|
856
867
|
|
|
868
|
+
// src/run-waiting.ts
|
|
869
|
+
var WEBHOOK_PREFIX = "wh:";
|
|
870
|
+
var CHILD_PREFIX = "child:";
|
|
871
|
+
var BREAKPOINT_PREFIX = "bp:";
|
|
872
|
+
var EVENT_PREFIX = "event:";
|
|
873
|
+
function classifyWaiterToken(token) {
|
|
874
|
+
if (token.startsWith(WEBHOOK_PREFIX)) return {
|
|
875
|
+
on: "webhook",
|
|
876
|
+
name: token
|
|
877
|
+
};
|
|
878
|
+
if (token.startsWith(BREAKPOINT_PREFIX)) return {
|
|
879
|
+
on: "breakpoint",
|
|
880
|
+
name: "breakpoint"
|
|
881
|
+
};
|
|
882
|
+
if (token.startsWith(CHILD_PREFIX)) return {
|
|
883
|
+
on: "child",
|
|
884
|
+
name: token.slice(CHILD_PREFIX.length)
|
|
885
|
+
};
|
|
886
|
+
if (token.startsWith(EVENT_PREFIX)) return {
|
|
887
|
+
on: "signal",
|
|
888
|
+
name: eventNameOf(token)
|
|
889
|
+
};
|
|
890
|
+
return {
|
|
891
|
+
on: "signal",
|
|
892
|
+
name: token
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
__name(classifyWaiterToken, "classifyWaiterToken");
|
|
896
|
+
function resolveRunWaiting(run, waiterByRun) {
|
|
897
|
+
if (run.status !== "suspended") return void 0;
|
|
898
|
+
const waiter = waiterByRun.get(run.id);
|
|
899
|
+
return waiter ? classifyWaiterToken(waiter.token) : void 0;
|
|
900
|
+
}
|
|
901
|
+
__name(resolveRunWaiting, "resolveRunWaiting");
|
|
902
|
+
function indexWaitersByRun(waiters) {
|
|
903
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
904
|
+
for (const w of waiters) if (!byRun.has(w.runId)) byRun.set(w.runId, w);
|
|
905
|
+
return byRun;
|
|
906
|
+
}
|
|
907
|
+
__name(indexWaitersByRun, "indexWaitersByRun");
|
|
908
|
+
|
|
857
909
|
// src/singleton-gate.ts
|
|
858
910
|
var SINGLETON_RETRY_MS = 1e3;
|
|
859
911
|
var SINGLETON_RETRY_JITTER_MS = 250;
|
|
@@ -1309,6 +1361,15 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
1309
1361
|
runId,
|
|
1310
1362
|
seq: current
|
|
1311
1363
|
});
|
|
1364
|
+
const lateBuffered2 = await consumeBuffered(token, current);
|
|
1365
|
+
if (lateBuffered2) {
|
|
1366
|
+
await store.removeSignalWaiter({
|
|
1367
|
+
token,
|
|
1368
|
+
runId,
|
|
1369
|
+
seq: current
|
|
1370
|
+
});
|
|
1371
|
+
return lateBuffered2.value;
|
|
1372
|
+
}
|
|
1312
1373
|
throw new WorkflowSuspended();
|
|
1313
1374
|
}
|
|
1314
1375
|
const timeoutMs = opts.timeoutMs;
|
|
@@ -1320,7 +1381,11 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
1320
1381
|
const buffered = await consumeBuffered(token, waitSeq);
|
|
1321
1382
|
if (buffered) return buffered.value;
|
|
1322
1383
|
if (host.clock() >= deadline) {
|
|
1323
|
-
await store.
|
|
1384
|
+
await store.removeSignalWaiter({
|
|
1385
|
+
token,
|
|
1386
|
+
runId,
|
|
1387
|
+
seq: waitSeq
|
|
1388
|
+
}).catch(() => void 0);
|
|
1324
1389
|
throw new SignalTimeoutError(token, timeoutMs);
|
|
1325
1390
|
}
|
|
1326
1391
|
await store.putSignalWaiter({
|
|
@@ -1328,6 +1393,15 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
1328
1393
|
runId,
|
|
1329
1394
|
seq: waitSeq
|
|
1330
1395
|
});
|
|
1396
|
+
const lateBuffered = await consumeBuffered(token, waitSeq);
|
|
1397
|
+
if (lateBuffered) {
|
|
1398
|
+
await store.removeSignalWaiter({
|
|
1399
|
+
token,
|
|
1400
|
+
runId,
|
|
1401
|
+
seq: waitSeq
|
|
1402
|
+
});
|
|
1403
|
+
return lateBuffered.value;
|
|
1404
|
+
}
|
|
1331
1405
|
throw new WorkflowSuspended(deadline);
|
|
1332
1406
|
}, "waitForSignal");
|
|
1333
1407
|
const waitForEvent = /* @__PURE__ */ __name(async (name, opts) => {
|
|
@@ -1351,7 +1425,11 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
1351
1425
|
const waited = await readCheckpoint(waitSeq);
|
|
1352
1426
|
if (waited && waited.status === "completed") return waited.output;
|
|
1353
1427
|
if (host.clock() >= deadline) {
|
|
1354
|
-
await store.
|
|
1428
|
+
await store.removeSignalWaiter({
|
|
1429
|
+
token,
|
|
1430
|
+
runId,
|
|
1431
|
+
seq: waitSeq
|
|
1432
|
+
}).catch(() => void 0);
|
|
1355
1433
|
throw new SignalTimeoutError(`event:${name}`, timeoutMs);
|
|
1356
1434
|
}
|
|
1357
1435
|
await store.putSignalWaiter({
|
|
@@ -2497,9 +2575,46 @@ var WorkflowEngine = class {
|
|
|
2497
2575
|
/**
|
|
2498
2576
|
* Resume every suspended run whose durable timer is due. Call periodically (a poller) and on
|
|
2499
2577
|
* boot. A run still not due re-suspends cheaply without running new work.
|
|
2578
|
+
*
|
|
2579
|
+
* Piggy-backs the signal-waiter reconcile safety net (see {@link reconcileSignalWaiter}): a crash
|
|
2580
|
+
* between the waiter/buffer pairing ops on EITHER side (this run's own `putSignalWaiter` →
|
|
2581
|
+
* `takeBufferedSignal`, or a signaler's `takeSignalWaiter` → `bufferSignal` → re-check — see
|
|
2582
|
+
* `signal`'s interleaving proof) can leave both rows stranded with nothing left to pair them: the
|
|
2583
|
+
* waiter's turn already suspended, the signaler that buffered it already returned. Event-wait
|
|
2584
|
+
* suspends already carry a `reconcileMs` fallback `wakeAt` (see {@link reconcileWakeAt}) precisely so
|
|
2585
|
+
* they show up HERE — that's what makes this due-timer pass the natural place for the sweep. One
|
|
2586
|
+
* bulk waiter scan, filtered to just this tick's batch, keeps it cheap regardless of store size.
|
|
2500
2587
|
*/
|
|
2501
2588
|
async resumeDueTimers(nowMs = this.clock()) {
|
|
2502
|
-
|
|
2589
|
+
const due = await this.store.listDueTimers(nowMs, this.namespace);
|
|
2590
|
+
if (due.length === 0) return [];
|
|
2591
|
+
const waiterByRun = indexWaitersByRun(await this.store.listSignalWaiters(""));
|
|
2592
|
+
return this.resumeLeased(due, nowMs, (run) => this.reconcileSignalWaiter(run, waiterByRun.get(run.id)));
|
|
2593
|
+
}
|
|
2594
|
+
/**
|
|
2595
|
+
* The reconcile safety net's per-run check (see {@link resumeDueTimers}): if this suspended run has
|
|
2596
|
+
* a registered signal waiter AND its token already has a buffered payload, pair and deliver them
|
|
2597
|
+
* directly instead of falling through to a full replay. Event/child/breakpoint waiter tokens are
|
|
2598
|
+
* never buffered (`publishEvent` doesn't buffer, and a child/breakpoint notifies via `signal` only
|
|
2599
|
+
* once its counterpart is already registered) — so `takeBufferedSignal` on those is a guaranteed
|
|
2600
|
+
* miss, and this is naturally signal-only without needing to classify the token.
|
|
2601
|
+
*
|
|
2602
|
+
* Returns `undefined` (not settled — `resumeLeased` proceeds with its normal `resume`) when there's
|
|
2603
|
+
* no waiter or no buffered hit. Deliberately never returns `null`: a delivered signal always resumes
|
|
2604
|
+
* via {@link deliverSignal}, and even a `NamespaceMismatch` there is reported as a real (non-nullish)
|
|
2605
|
+
* result — returning `null` would read as "not settled" to `resumeLeased`'s `??` fallback and
|
|
2606
|
+
* trigger a second, redundant `resume` call on the same run.
|
|
2607
|
+
*/
|
|
2608
|
+
async reconcileSignalWaiter(run, waiter) {
|
|
2609
|
+
if (!waiter) return void 0;
|
|
2610
|
+
const buffered = await this.store.takeBufferedSignal(waiter.token);
|
|
2611
|
+
if (!buffered) return void 0;
|
|
2612
|
+
await this.store.removeSignalWaiter(waiter);
|
|
2613
|
+
const settled = await this.deliverSignal(waiter, buffered.payload);
|
|
2614
|
+
return settled ?? {
|
|
2615
|
+
runId: run.id,
|
|
2616
|
+
status: run.status
|
|
2617
|
+
};
|
|
2503
2618
|
}
|
|
2504
2619
|
/**
|
|
2505
2620
|
* Lease and execute one run by id — the worker side of dispatch. Acquires the recovery lease (so
|
|
@@ -2717,16 +2832,17 @@ var WorkflowEngine = class {
|
|
|
2717
2832
|
}
|
|
2718
2833
|
return touched;
|
|
2719
2834
|
}
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2835
|
+
/**
|
|
2836
|
+
* Deliver a signal to an already-known waiter: write its resolving `signal:<token>` checkpoint and
|
|
2837
|
+
* resume the run. Shared by {@link signal}'s direct hit, its post-buffer re-check reclaim, and the
|
|
2838
|
+
* due-timer reconcile sweep ({@link reconcileSignalWaiter}) — all three resolve a waiter the exact
|
|
2839
|
+
* same way.
|
|
2840
|
+
*/
|
|
2841
|
+
async deliverSignal(waiter, payload) {
|
|
2726
2842
|
await this.store.saveCheckpoint(instantCheckpoint({
|
|
2727
2843
|
runId: waiter.runId,
|
|
2728
2844
|
seq: waiter.seq,
|
|
2729
|
-
name: `signal:${token}`,
|
|
2845
|
+
name: `signal:${waiter.token}`,
|
|
2730
2846
|
kind: "signal",
|
|
2731
2847
|
output: payload,
|
|
2732
2848
|
// Carry the awaiting command's fan group (set when a `ctx.gather_children`/`ctx.all` fan-out
|
|
@@ -2741,6 +2857,42 @@ var WorkflowEngine = class {
|
|
|
2741
2857
|
});
|
|
2742
2858
|
}
|
|
2743
2859
|
/**
|
|
2860
|
+
* Deliver an external signal to the run waiting on `token`, resume it with `payload`, and return
|
|
2861
|
+
* the run result — or null if no run is (or ends up) waiting for it.
|
|
2862
|
+
*
|
|
2863
|
+
* Race with the waiter side (`waitForSignal`'s check → `putSignalWaiter` → re-check, same shape in
|
|
2864
|
+
* the remote `waitSignal` command): a waiter can register in the sliver between this method's
|
|
2865
|
+
* initial `takeSignalWaiter` miss and its `bufferSignal` write, and the naive old flow (buffer and
|
|
2866
|
+
* return) would then leave BOTH a buffered payload and a registered waiter sitting in the store with
|
|
2867
|
+
* nothing left to pair them — a lost wake, forever suspended.
|
|
2868
|
+
*
|
|
2869
|
+
* The interleaving proof: the waiter side does check → put → re-check, this side does take → buffer
|
|
2870
|
+
* → re-check. Whichever side's SECOND look runs last is guaranteed to observe the other side's
|
|
2871
|
+
* write, because it always follows that write in real time:
|
|
2872
|
+
* - If the waiter's `put` lands before this method's first `take`, the take just finds it directly
|
|
2873
|
+
* (the pre-existing, non-racy path).
|
|
2874
|
+
* - If the waiter's `put` lands after this method's `bufferSignal`, our re-check below either sees
|
|
2875
|
+
* it (if `put` also lands before the re-check) or the waiter's OWN post-put re-check sees our
|
|
2876
|
+
* buffer (if `put` lands after our re-check but the waiter hasn't re-checked yet either — its
|
|
2877
|
+
* re-check strictly follows its own put).
|
|
2878
|
+
* - If the waiter's `put` lands between this method's `take` and its re-`take`, the re-check below
|
|
2879
|
+
* catches it.
|
|
2880
|
+
* Either way, the destructive `take*` ops (on the waiter row, on the buffer) arbitrate: whichever
|
|
2881
|
+
* side actually removes the row is the one that delivers, so a signal is never delivered twice.
|
|
2882
|
+
*/
|
|
2883
|
+
async signal(token, payload) {
|
|
2884
|
+
const waiter = await this.store.takeSignalWaiter(token);
|
|
2885
|
+
if (waiter) return this.deliverSignal(waiter, payload);
|
|
2886
|
+
await this.store.bufferSignal(token, payload);
|
|
2887
|
+
const lateWaiter = await this.store.takeSignalWaiter(token);
|
|
2888
|
+
if (!lateWaiter) return null;
|
|
2889
|
+
const reclaimed = await this.store.takeBufferedSignal(token);
|
|
2890
|
+
if (!reclaimed) {
|
|
2891
|
+
return null;
|
|
2892
|
+
}
|
|
2893
|
+
return this.deliverSignal(lateWaiter, reclaimed.payload);
|
|
2894
|
+
}
|
|
2895
|
+
/**
|
|
2744
2896
|
* Ensure a run exists for `runId`, then deliver a signal to it — atomically race-free thanks to
|
|
2745
2897
|
* signal buffering: if the run is new (or busy / not yet waiting), the signal is buffered and
|
|
2746
2898
|
* consumed when it reaches `waitForSignal(token)`. The canonical **durable-entity / accumulator**
|
|
@@ -3213,6 +3365,14 @@ var WorkflowEngine = class {
|
|
|
3213
3365
|
error: outcome.error
|
|
3214
3366
|
};
|
|
3215
3367
|
}
|
|
3368
|
+
const latest = await this.store.getRun(run.id);
|
|
3369
|
+
if (latest?.status === "cancelled") {
|
|
3370
|
+
return {
|
|
3371
|
+
runId: run.id,
|
|
3372
|
+
status: "cancelled",
|
|
3373
|
+
error: latest.error
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3216
3376
|
await this.store.updateRun(run.id, {
|
|
3217
3377
|
status: "suspended",
|
|
3218
3378
|
wakeAt: this.reconcileWakeAt(outcome.wakeAt),
|
|
@@ -3555,22 +3715,34 @@ var WorkflowEngine = class {
|
|
|
3555
3715
|
}));
|
|
3556
3716
|
wakeAt = wakeAt == null ? deadline : Math.min(wakeAt, deadline);
|
|
3557
3717
|
} else if (cmd.kind === "waitSignal") {
|
|
3558
|
-
const
|
|
3559
|
-
if (buffered) {
|
|
3718
|
+
const deliverBuffered = /* @__PURE__ */ __name(async (payload) => {
|
|
3560
3719
|
await this.store.saveCheckpoint(instantCheckpoint({
|
|
3561
3720
|
runId: run.id,
|
|
3562
3721
|
seq: cmd.seq,
|
|
3563
3722
|
name: `signal:${cmd.signal}`,
|
|
3564
3723
|
kind: "signal",
|
|
3565
|
-
output:
|
|
3724
|
+
output: payload
|
|
3566
3725
|
}));
|
|
3567
3726
|
setTimeout(() => void this.resume(run.id).catch(() => void 0), 0);
|
|
3727
|
+
}, "deliverBuffered");
|
|
3728
|
+
const buffered = await this.store.takeBufferedSignal(cmd.signal);
|
|
3729
|
+
if (buffered) {
|
|
3730
|
+
await deliverBuffered(buffered.payload);
|
|
3568
3731
|
} else {
|
|
3569
3732
|
await this.store.putSignalWaiter({
|
|
3570
3733
|
token: cmd.signal,
|
|
3571
3734
|
runId: run.id,
|
|
3572
3735
|
seq: cmd.seq
|
|
3573
3736
|
});
|
|
3737
|
+
const lateBuffered = await this.store.takeBufferedSignal(cmd.signal);
|
|
3738
|
+
if (lateBuffered) {
|
|
3739
|
+
await this.store.removeSignalWaiter({
|
|
3740
|
+
token: cmd.signal,
|
|
3741
|
+
runId: run.id,
|
|
3742
|
+
seq: cmd.seq
|
|
3743
|
+
});
|
|
3744
|
+
await deliverBuffered(lateBuffered.payload);
|
|
3745
|
+
}
|
|
3574
3746
|
}
|
|
3575
3747
|
} else if (cmd.kind === "startChild") {
|
|
3576
3748
|
const childId = `${run.id}.child.${cmd.seq}`;
|
|
@@ -4430,47 +4602,6 @@ var TERMINAL_RUN_STATUSES = [
|
|
|
4430
4602
|
"dead"
|
|
4431
4603
|
];
|
|
4432
4604
|
|
|
4433
|
-
// src/run-waiting.ts
|
|
4434
|
-
var WEBHOOK_PREFIX = "wh:";
|
|
4435
|
-
var CHILD_PREFIX = "child:";
|
|
4436
|
-
var BREAKPOINT_PREFIX = "bp:";
|
|
4437
|
-
var EVENT_PREFIX = "event:";
|
|
4438
|
-
function classifyWaiterToken(token) {
|
|
4439
|
-
if (token.startsWith(WEBHOOK_PREFIX)) return {
|
|
4440
|
-
on: "webhook",
|
|
4441
|
-
name: token
|
|
4442
|
-
};
|
|
4443
|
-
if (token.startsWith(BREAKPOINT_PREFIX)) return {
|
|
4444
|
-
on: "breakpoint",
|
|
4445
|
-
name: "breakpoint"
|
|
4446
|
-
};
|
|
4447
|
-
if (token.startsWith(CHILD_PREFIX)) return {
|
|
4448
|
-
on: "child",
|
|
4449
|
-
name: token.slice(CHILD_PREFIX.length)
|
|
4450
|
-
};
|
|
4451
|
-
if (token.startsWith(EVENT_PREFIX)) return {
|
|
4452
|
-
on: "signal",
|
|
4453
|
-
name: eventNameOf(token)
|
|
4454
|
-
};
|
|
4455
|
-
return {
|
|
4456
|
-
on: "signal",
|
|
4457
|
-
name: token
|
|
4458
|
-
};
|
|
4459
|
-
}
|
|
4460
|
-
__name(classifyWaiterToken, "classifyWaiterToken");
|
|
4461
|
-
function resolveRunWaiting(run, waiterByRun) {
|
|
4462
|
-
if (run.status !== "suspended") return void 0;
|
|
4463
|
-
const waiter = waiterByRun.get(run.id);
|
|
4464
|
-
return waiter ? classifyWaiterToken(waiter.token) : void 0;
|
|
4465
|
-
}
|
|
4466
|
-
__name(resolveRunWaiting, "resolveRunWaiting");
|
|
4467
|
-
function indexWaitersByRun(waiters) {
|
|
4468
|
-
const byRun = /* @__PURE__ */ new Map();
|
|
4469
|
-
for (const w of waiters) if (!byRun.has(w.runId)) byRun.set(w.runId, w);
|
|
4470
|
-
return byRun;
|
|
4471
|
-
}
|
|
4472
|
-
__name(indexWaitersByRun, "indexWaitersByRun");
|
|
4473
|
-
|
|
4474
4605
|
// src/codec-state-store.ts
|
|
4475
4606
|
var CodecStateStore = class {
|
|
4476
4607
|
static {
|
|
@@ -4578,6 +4709,9 @@ var CodecStateStore = class {
|
|
|
4578
4709
|
listSignalWaiters(prefix) {
|
|
4579
4710
|
return this.inner.listSignalWaiters(prefix);
|
|
4580
4711
|
}
|
|
4712
|
+
removeSignalWaiter(waiter) {
|
|
4713
|
+
return this.inner.removeSignalWaiter(waiter);
|
|
4714
|
+
}
|
|
4581
4715
|
bufferSignal(token, payload) {
|
|
4582
4716
|
return this.inner.bufferSignal(token, this.enc(payload));
|
|
4583
4717
|
}
|
|
@@ -5030,6 +5164,12 @@ var InMemoryStateStore = class {
|
|
|
5030
5164
|
...w
|
|
5031
5165
|
}));
|
|
5032
5166
|
}
|
|
5167
|
+
async removeSignalWaiter(waiter) {
|
|
5168
|
+
const current = this.signalWaiters.get(waiter.token);
|
|
5169
|
+
if (current && current.runId === waiter.runId && current.seq === waiter.seq) {
|
|
5170
|
+
this.signalWaiters.delete(waiter.token);
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5033
5173
|
// No real DB transaction (there's no DB) — run the work and save the checkpoint. Exactly-once
|
|
5034
5174
|
// across a crash needs a SQL store; this keeps `ctx.transaction` usable in tests / local dev.
|
|
5035
5175
|
async transaction(work) {
|
|
@@ -5203,6 +5343,7 @@ var InMemoryTransport = class {
|
|
|
5203
5343
|
}
|
|
5204
5344
|
};
|
|
5205
5345
|
export {
|
|
5346
|
+
CONTROL_FLOW_SIGNAL,
|
|
5206
5347
|
CodecStateStore,
|
|
5207
5348
|
ContinueAsNew,
|
|
5208
5349
|
DURABLE_OPTIONS,
|
|
@@ -5248,6 +5389,7 @@ export {
|
|
|
5248
5389
|
eventPrefix,
|
|
5249
5390
|
eventToken,
|
|
5250
5391
|
indexWaitersByRun,
|
|
5392
|
+
isWorkflowControlFlowSignal,
|
|
5251
5393
|
matchesAttributes,
|
|
5252
5394
|
normalizeAttributeRows,
|
|
5253
5395
|
parseDuration,
|