@dudousxd/nestjs-durable-core 0.49.1 → 0.49.2

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.d.cts CHANGED
@@ -1784,6 +1784,19 @@ interface WorkflowEngineDeps {
1784
1784
  * one (this rearmed window for heartbeat-aware workers, or the fixed `timeoutMs` otherwise).
1785
1785
  */
1786
1786
  remoteAdvanceSilenceMs?: number | undefined;
1787
+ /**
1788
+ * Fallback re-drive window (ms) for a run that SUSPENDS with no natural timer — i.e. it's waiting on
1789
+ * an EVENT to wake it (a child's completion, a signal, a remote step with no `timeoutMs`) rather than
1790
+ * a `ctx.sleep`. Those events re-drive the run directly; but if the wake is LOST (the delivering pod
1791
+ * crashed / rolled mid-handoff), the run would otherwise sit `suspended` forever with `wakeAt: null` —
1792
+ * invisible to both the timer poller (no `wakeAt`) and crash-recovery (no lease). This stamps a
1793
+ * fallback `wakeAt` so {@link resumeDueTimers} re-drives it after the window; the re-drive is an
1794
+ * idempotent replay (a still-pending dependency re-suspends via the checkpoint guard, a settled one
1795
+ * advances), so it's a safe self-heal, not a retry that can double-dispatch. The primary event path
1796
+ * still wins first for a healthy run (it re-drives long before this fires), so this only ever fires
1797
+ * for a genuinely-orphaned run. Default 5min; set `0` to disable (the prior wake-forever behavior).
1798
+ */
1799
+ reconcileMs?: number | undefined;
1787
1800
  /**
1788
1801
  * Build the public callback URL for a `ctx.webhook()` token (e.g.
1789
1802
  * ``(t) => `https://api.example.com/durable/webhooks/${t}` ``). Populates
@@ -1859,6 +1872,7 @@ declare class WorkflowEngine {
1859
1872
  private readonly leaseMs;
1860
1873
  private readonly maxRecoveryAttempts?;
1861
1874
  private readonly remoteAdvanceSilenceMs?;
1875
+ private readonly reconcileMs;
1862
1876
  private readonly webhookUrl?;
1863
1877
  private readonly traceparent?;
1864
1878
  private readonly context?;
@@ -2324,6 +2338,13 @@ declare class WorkflowEngine {
2324
2338
  * keeps its own lease handling (the TS executor's `finally`, the remote path's caller).
2325
2339
  */
2326
2340
  private settleRun;
2341
+ /**
2342
+ * A wakeAt to persist on a suspend: the natural timer if the turn produced one (a `ctx.sleep`, a
2343
+ * remote-step deadline), else the {@link reconcileMs} fallback so an event-waiting run (child/signal/
2344
+ * timeout-less remote step) can't be orphaned forever if its wake is lost. `undefined` only when the
2345
+ * fallback is disabled (`reconcileMs: 0`). See {@link WorkflowEngineDeps.reconcileMs}.
2346
+ */
2347
+ private reconcileWakeAt;
2327
2348
  private runRemoteExecution;
2328
2349
  /**
2329
2350
  * Apply a remote workflow turn's {@link WorkflowDecision}: persist the local steps it ran, then
package/dist/index.d.ts CHANGED
@@ -1784,6 +1784,19 @@ interface WorkflowEngineDeps {
1784
1784
  * one (this rearmed window for heartbeat-aware workers, or the fixed `timeoutMs` otherwise).
1785
1785
  */
1786
1786
  remoteAdvanceSilenceMs?: number | undefined;
1787
+ /**
1788
+ * Fallback re-drive window (ms) for a run that SUSPENDS with no natural timer — i.e. it's waiting on
1789
+ * an EVENT to wake it (a child's completion, a signal, a remote step with no `timeoutMs`) rather than
1790
+ * a `ctx.sleep`. Those events re-drive the run directly; but if the wake is LOST (the delivering pod
1791
+ * crashed / rolled mid-handoff), the run would otherwise sit `suspended` forever with `wakeAt: null` —
1792
+ * invisible to both the timer poller (no `wakeAt`) and crash-recovery (no lease). This stamps a
1793
+ * fallback `wakeAt` so {@link resumeDueTimers} re-drives it after the window; the re-drive is an
1794
+ * idempotent replay (a still-pending dependency re-suspends via the checkpoint guard, a settled one
1795
+ * advances), so it's a safe self-heal, not a retry that can double-dispatch. The primary event path
1796
+ * still wins first for a healthy run (it re-drives long before this fires), so this only ever fires
1797
+ * for a genuinely-orphaned run. Default 5min; set `0` to disable (the prior wake-forever behavior).
1798
+ */
1799
+ reconcileMs?: number | undefined;
1787
1800
  /**
1788
1801
  * Build the public callback URL for a `ctx.webhook()` token (e.g.
1789
1802
  * ``(t) => `https://api.example.com/durable/webhooks/${t}` ``). Populates
@@ -1859,6 +1872,7 @@ declare class WorkflowEngine {
1859
1872
  private readonly leaseMs;
1860
1873
  private readonly maxRecoveryAttempts?;
1861
1874
  private readonly remoteAdvanceSilenceMs?;
1875
+ private readonly reconcileMs;
1862
1876
  private readonly webhookUrl?;
1863
1877
  private readonly traceparent?;
1864
1878
  private readonly context?;
@@ -2324,6 +2338,13 @@ declare class WorkflowEngine {
2324
2338
  * keeps its own lease handling (the TS executor's `finally`, the remote path's caller).
2325
2339
  */
2326
2340
  private settleRun;
2341
+ /**
2342
+ * A wakeAt to persist on a suspend: the natural timer if the turn produced one (a `ctx.sleep`, a
2343
+ * remote-step deadline), else the {@link reconcileMs} fallback so an event-waiting run (child/signal/
2344
+ * timeout-less remote step) can't be orphaned forever if its wake is lost. `undefined` only when the
2345
+ * fallback is disabled (`reconcileMs: 0`). See {@link WorkflowEngineDeps.reconcileMs}.
2346
+ */
2347
+ private reconcileWakeAt;
2327
2348
  private runRemoteExecution;
2328
2349
  /**
2329
2350
  * Apply a remote workflow turn's {@link WorkflowDecision}: persist the local steps it ran, then
package/dist/index.js CHANGED
@@ -1720,6 +1720,7 @@ var WorkflowEngine = class {
1720
1720
  leaseMs;
1721
1721
  maxRecoveryAttempts;
1722
1722
  remoteAdvanceSilenceMs;
1723
+ reconcileMs;
1723
1724
  webhookUrl;
1724
1725
  traceparent;
1725
1726
  context;
@@ -1790,6 +1791,7 @@ var WorkflowEngine = class {
1790
1791
  this.leaseMs = deps.leaseMs ?? 3e4;
1791
1792
  this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
1792
1793
  this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
1794
+ this.reconcileMs = deps.reconcileMs === void 0 ? 3e5 : deps.reconcileMs || void 0;
1793
1795
  this.webhookUrl = deps.webhookUrl;
1794
1796
  this.traceparent = deps.traceparent;
1795
1797
  this.context = deps.context;
@@ -3134,7 +3136,7 @@ var WorkflowEngine = class {
3134
3136
  }
3135
3137
  await this.store.updateRun(run.id, {
3136
3138
  status: "suspended",
3137
- wakeAt: outcome.wakeAt,
3139
+ wakeAt: this.reconcileWakeAt(outcome.wakeAt),
3138
3140
  updatedAt
3139
3141
  });
3140
3142
  this.emit({
@@ -3148,6 +3150,16 @@ var WorkflowEngine = class {
3148
3150
  status: "suspended"
3149
3151
  };
3150
3152
  }
3153
+ /**
3154
+ * A wakeAt to persist on a suspend: the natural timer if the turn produced one (a `ctx.sleep`, a
3155
+ * remote-step deadline), else the {@link reconcileMs} fallback so an event-waiting run (child/signal/
3156
+ * timeout-less remote step) can't be orphaned forever if its wake is lost. `undefined` only when the
3157
+ * fallback is disabled (`reconcileMs: 0`). See {@link WorkflowEngineDeps.reconcileMs}.
3158
+ */
3159
+ reconcileWakeAt(wakeAt) {
3160
+ if (wakeAt != null) return wakeAt;
3161
+ return this.reconcileMs != null ? this.clock() + this.reconcileMs : void 0;
3162
+ }
3151
3163
  async runRemoteExecution(run, registered) {
3152
3164
  if (run.status === "cancelling") {
3153
3165
  const error = {