@dudousxd/nestjs-durable 0.43.0 → 0.43.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.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
9
  var __export = (target, all) => {
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/index.ts
@@ -25,6 +35,7 @@ __export(index_exports, {
25
35
  DEAD_LETTER_METADATA: () => DEAD_LETTER_METADATA,
26
36
  DURABLE_STEP_METADATA: () => DURABLE_STEP_METADATA,
27
37
  DURABLE_WORKER_RUNNERS: () => DURABLE_WORKER_RUNNERS,
38
+ DURABLE_WORKER_RUNTIME: () => DURABLE_WORKER_RUNTIME,
28
39
  DeadLetter: () => DeadLetter,
29
40
  DurableModule: () => DurableModule,
30
41
  DurableStartClient: () => DurableStartClient,
@@ -313,9 +324,29 @@ function attributesOf(workflow, run) {
313
324
  __name(attributesOf, "attributesOf");
314
325
 
315
326
  // src/durable-start-client.ts
316
- var import_durable_worker = require("@dudousxd/durable-worker");
317
327
  var import_nestjs_durable_core3 = require("@dudousxd/nestjs-durable-core");
318
328
  var import_common = require("@nestjs/common");
329
+
330
+ // src/worker-sdk.ts
331
+ async function workerSdk() {
332
+ try {
333
+ return await import("@dudousxd/durable-worker");
334
+ } catch (cause) {
335
+ throw new Error("@dudousxd/durable-worker is not installed. It is an optional peer needed only by the roles that run workflow bodies (`connection` set on DurableModule.forRoot) \u2014 install it, or drop `connection`.", {
336
+ cause
337
+ });
338
+ }
339
+ }
340
+ __name(workerSdk, "workerSdk");
341
+ var startRun = /* @__PURE__ */ __name(async (connection, options) => (await workerSdk()).startRun(connection, options), "startRun");
342
+ var runRedisWorker = /* @__PURE__ */ __name(async (options) => (await workerSdk()).runRedisWorker(options), "runRedisWorker");
343
+ async function createWorkerRuntime(options) {
344
+ const { DurableWorkerRuntime: Runtime } = await workerSdk();
345
+ return new Runtime(options);
346
+ }
347
+ __name(createWorkerRuntime, "createWorkerRuntime");
348
+
349
+ // src/durable-start-client.ts
319
350
  function _ts_decorate(decorators, target, key, desc) {
320
351
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
321
352
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -341,7 +372,7 @@ var DurableStartClient = class {
341
372
  }
342
373
  async start(workflow, input, runId = globalThis.crypto.randomUUID(), opts) {
343
374
  const name = (0, import_nestjs_durable_core3.workflowName)(workflow);
344
- await (0, import_durable_worker.startRun)(this.options.connection, {
375
+ await startRun(this.options.connection, {
345
376
  tenant: this.tenant,
346
377
  workflow: name,
347
378
  input,
@@ -408,7 +439,6 @@ function tenantUnsupported(method) {
408
439
  __name(tenantUnsupported, "tenantUnsupported");
409
440
 
410
441
  // src/durable-worker.module.ts
411
- var import_durable_worker2 = require("@dudousxd/durable-worker");
412
442
  var import_nestjs_durable_core4 = require("@dudousxd/nestjs-durable-core");
413
443
  var import_common2 = require("@nestjs/common");
414
444
  var import_core = require("@nestjs/core");
@@ -469,6 +499,7 @@ function _ts_param(paramIndex, decorator) {
469
499
  }
470
500
  __name(_ts_param, "_ts_param");
471
501
  var RUN_REDIS_WORKER = Symbol("nestjs-durable:run-redis-worker");
502
+ var DURABLE_WORKER_RUNTIME = Symbol("nestjs-durable:worker-runtime");
472
503
  var DURABLE_WORKER_RUNNERS = Symbol("nestjs-durable:worker-runners");
473
504
  function isPureThinWorker(options) {
474
505
  return options.store === void 0 && options.connection !== void 0;
@@ -487,8 +518,9 @@ var ThinWorkflowRegistrar = class {
487
518
  this.options = options;
488
519
  }
489
520
  onModuleInit() {
490
- if (!isPureThinWorker(this.options)) return;
491
- scanWorkflows(this.discovery, (meta, instance, origin) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
521
+ const runtime = this.runtime;
522
+ if (runtime === null || !isPureThinWorker(this.options)) return;
523
+ scanWorkflows(this.discovery, (meta, instance, origin) => runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
492
524
  version: meta.version,
493
525
  ...meta.requires !== void 0 ? {
494
526
  requires: meta.requires
@@ -501,11 +533,12 @@ var ThinWorkflowRegistrar = class {
501
533
  };
502
534
  ThinWorkflowRegistrar = _ts_decorate2([
503
535
  (0, import_common2.Injectable)(),
536
+ _ts_param(1, (0, import_common2.Inject)(DURABLE_WORKER_RUNTIME)),
504
537
  _ts_param(2, (0, import_common2.Inject)(import_nestjs_durable_core4.DURABLE_OPTIONS_CANONICAL)),
505
538
  _ts_metadata2("design:type", Function),
506
539
  _ts_metadata2("design:paramtypes", [
507
540
  typeof import_core.DiscoveryService === "undefined" ? Object : import_core.DiscoveryService,
508
- typeof import_durable_worker2.DurableWorkerRuntime === "undefined" ? Object : import_durable_worker2.DurableWorkerRuntime,
541
+ Object,
509
542
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
510
543
  ])
511
544
  ], ThinWorkflowRegistrar);
@@ -524,18 +557,20 @@ var ThinStepRegistrar = class {
524
557
  this.options = options;
525
558
  }
526
559
  onModuleInit() {
527
- if (!isPureThinWorker(this.options)) return;
528
- scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
560
+ const runtime = this.runtime;
561
+ if (runtime === null || !isPureThinWorker(this.options)) return;
562
+ scanSteps(this.discovery, this.metadataScanner, (meta, handler) => runtime.registerStep(meta.name, handler));
529
563
  }
530
564
  };
531
565
  ThinStepRegistrar = _ts_decorate2([
532
566
  (0, import_common2.Injectable)(),
567
+ _ts_param(2, (0, import_common2.Inject)(DURABLE_WORKER_RUNTIME)),
533
568
  _ts_param(3, (0, import_common2.Inject)(import_nestjs_durable_core4.DURABLE_OPTIONS_CANONICAL)),
534
569
  _ts_metadata2("design:type", Function),
535
570
  _ts_metadata2("design:paramtypes", [
536
571
  typeof import_core.DiscoveryService === "undefined" ? Object : import_core.DiscoveryService,
537
572
  typeof import_core.MetadataScanner === "undefined" ? Object : import_core.MetadataScanner,
538
- typeof import_durable_worker2.DurableWorkerRuntime === "undefined" ? Object : import_durable_worker2.DurableWorkerRuntime,
573
+ Object,
539
574
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
540
575
  ])
541
576
  ], ThinStepRegistrar);
@@ -548,17 +583,18 @@ var ThinWorkerBootstrap = class {
548
583
  runRedisWorker;
549
584
  runnersSink;
550
585
  runners = [];
551
- constructor(runtime, options, runRedisWorker, runnersSink) {
586
+ constructor(runtime, options, runRedisWorker2, runnersSink) {
552
587
  this.runtime = runtime;
553
588
  this.options = options;
554
- this.runRedisWorker = runRedisWorker;
589
+ this.runRedisWorker = runRedisWorker2;
555
590
  this.runnersSink = runnersSink;
556
591
  }
557
592
  async onApplicationBootstrap() {
558
593
  const options = this.options;
559
- if (!isPureThinWorker(options) || options.connection === void 0) return;
594
+ const runtime = this.runtime;
595
+ if (runtime === null || !isPureThinWorker(options) || options.connection === void 0) return;
560
596
  const handle = await this.runRedisWorker({
561
- runtime: this.runtime,
597
+ runtime,
562
598
  connection: options.connection,
563
599
  ...options.partition !== void 0 ? {
564
600
  partition: options.partition
@@ -582,12 +618,13 @@ var ThinWorkerBootstrap = class {
582
618
  };
583
619
  ThinWorkerBootstrap = _ts_decorate2([
584
620
  (0, import_common2.Injectable)(),
621
+ _ts_param(0, (0, import_common2.Inject)(DURABLE_WORKER_RUNTIME)),
585
622
  _ts_param(1, (0, import_common2.Inject)(import_nestjs_durable_core4.DURABLE_OPTIONS_CANONICAL)),
586
623
  _ts_param(2, (0, import_common2.Inject)(RUN_REDIS_WORKER)),
587
624
  _ts_param(3, (0, import_common2.Inject)(DURABLE_WORKER_RUNNERS)),
588
625
  _ts_metadata2("design:type", Function),
589
626
  _ts_metadata2("design:paramtypes", [
590
- typeof import_durable_worker2.DurableWorkerRuntime === "undefined" ? Object : import_durable_worker2.DurableWorkerRuntime,
627
+ Object,
591
628
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
592
629
  typeof RunRedisWorkerFn === "undefined" ? Object : RunRedisWorkerFn,
593
630
  Array
@@ -596,7 +633,7 @@ ThinWorkerBootstrap = _ts_decorate2([
596
633
  function thinWorkerProviders() {
597
634
  return [
598
635
  {
599
- provide: import_durable_worker2.DurableWorkerRuntime,
636
+ provide: DURABLE_WORKER_RUNTIME,
600
637
  // The runtime's `WorkflowWorker` falls back to its own `group` ctor param as the WORKFLOW's
601
638
  // `workflowPartition` for any `ctx.step` call (see `workflow-context.ts`'s `resolveCallGroup`)
602
639
  // — that fallback MUST equal this module's own `partition`, or a dispatched step's decision
@@ -604,16 +641,20 @@ function thinWorkerProviders() {
604
641
  // to. Explicit `''` (never
605
642
  // `undefined`) so it does NOT fall through to `WorkflowWorker`'s unrelated `'workflows'`
606
643
  // default parameter.
607
- useFactory: /* @__PURE__ */ __name((options) => new import_durable_worker2.DurableWorkerRuntime({
644
+ //
645
+ // The role gate lives HERE, not just in the consumers: constructing the runtime resolves the
646
+ // optional `@dudousxd/durable-worker` peer, so an operator that never runs a worker must not
647
+ // reach this branch at all.
648
+ useFactory: /* @__PURE__ */ __name((options) => isPureThinWorker(options) ? createWorkerRuntime({
608
649
  workflowGroup: options.partition ?? ""
609
- }), "useFactory"),
650
+ }) : null, "useFactory"),
610
651
  inject: [
611
652
  import_nestjs_durable_core4.DURABLE_OPTIONS_CANONICAL
612
653
  ]
613
654
  },
614
655
  {
615
656
  provide: RUN_REDIS_WORKER,
616
- useValue: import_durable_worker2.runRedisWorker
657
+ useValue: runRedisWorker
617
658
  },
618
659
  {
619
660
  provide: DURABLE_WORKER_RUNNERS,
@@ -833,7 +874,6 @@ EntityService = _ts_decorate4([
833
874
  ], EntityService);
834
875
 
835
876
  // src/in-app-worker.ts
836
- var import_durable_worker3 = require("@dudousxd/durable-worker");
837
877
  var import_nestjs_durable_core7 = require("@dudousxd/nestjs-durable-core");
838
878
  var import_common5 = require("@nestjs/common");
839
879
  var import_core3 = require("@nestjs/core");
@@ -886,17 +926,18 @@ var InAppWorkerBootstrap = class {
886
926
  runRedisWorker;
887
927
  runnersSink;
888
928
  runners = [];
889
- constructor(discovery, metadataScanner, options, runtime, runRedisWorker, runnersSink) {
929
+ constructor(discovery, metadataScanner, options, runtime, runRedisWorker2, runnersSink) {
890
930
  this.discovery = discovery;
891
931
  this.metadataScanner = metadataScanner;
892
932
  this.options = options;
893
933
  this.runtime = runtime;
894
- this.runRedisWorker = runRedisWorker;
934
+ this.runRedisWorker = runRedisWorker2;
895
935
  this.runnersSink = runnersSink;
896
936
  }
897
937
  onModuleInit() {
898
- if (!isCoLocatedWorker(this.options)) return;
899
- scanWorkflows(this.discovery, (meta, instance, origin) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
938
+ const runtime = this.runtime;
939
+ if (runtime === null || !isCoLocatedWorker(this.options)) return;
940
+ scanWorkflows(this.discovery, (meta, instance, origin) => runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
900
941
  version: meta.version,
901
942
  ...meta.requires !== void 0 ? {
902
943
  requires: meta.requires
@@ -905,13 +946,14 @@ var InAppWorkerBootstrap = class {
905
946
  origin
906
947
  } : {}
907
948
  }));
908
- scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
949
+ scanSteps(this.discovery, this.metadataScanner, (meta, handler) => runtime.registerStep(meta.name, handler));
909
950
  }
910
951
  async onApplicationBootstrap() {
911
952
  const options = this.options;
912
- if (!isCoLocatedWorker(options) || options.connection === void 0) return;
953
+ const runtime = this.runtime;
954
+ if (runtime === null || !isCoLocatedWorker(options) || options.connection === void 0) return;
913
955
  const handle = await this.runRedisWorker({
914
- runtime: this.runtime,
956
+ runtime,
915
957
  connection: options.connection,
916
958
  ...options.partition !== void 0 ? {
917
959
  partition: options.partition
@@ -944,7 +986,7 @@ InAppWorkerBootstrap = _ts_decorate5([
944
986
  typeof import_core3.DiscoveryService === "undefined" ? Object : import_core3.DiscoveryService,
945
987
  typeof import_core3.MetadataScanner === "undefined" ? Object : import_core3.MetadataScanner,
946
988
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
947
- typeof import_durable_worker3.DurableWorkerRuntime === "undefined" ? Object : import_durable_worker3.DurableWorkerRuntime,
989
+ Object,
948
990
  typeof RunRedisWorkerFn === "undefined" ? Object : RunRedisWorkerFn,
949
991
  Array
950
992
  ])
@@ -968,16 +1010,20 @@ function inAppWorkerProviders() {
968
1010
  // to. Explicit `''` (never
969
1011
  // `undefined`) so it does NOT fall through to `WorkflowWorker`'s unrelated `'workflows'`
970
1012
  // default parameter.
971
- useFactory: /* @__PURE__ */ __name((options) => new import_durable_worker3.DurableWorkerRuntime({
1013
+ //
1014
+ // The role gate lives HERE, not just in the consumer: constructing the runtime resolves the
1015
+ // optional `@dudousxd/durable-worker` peer, so an operator that never runs a worker must not
1016
+ // reach this branch at all.
1017
+ useFactory: /* @__PURE__ */ __name((options) => isCoLocatedWorker(options) ? createWorkerRuntime({
972
1018
  workflowGroup: options.partition ?? ""
973
- }), "useFactory"),
1019
+ }) : null, "useFactory"),
974
1020
  inject: [
975
1021
  import_nestjs_durable_core7.DURABLE_OPTIONS_CANONICAL
976
1022
  ]
977
1023
  },
978
1024
  {
979
1025
  provide: IN_APP_RUN_REDIS_WORKER,
980
- useValue: import_durable_worker3.runRedisWorker
1026
+ useValue: runRedisWorker
981
1027
  },
982
1028
  {
983
1029
  provide: IN_APP_WORKER_RUNNERS,
@@ -1238,6 +1284,15 @@ var RetentionPoller = class {
1238
1284
  onModuleDestroy() {
1239
1285
  if (this.timer) clearInterval(this.timer);
1240
1286
  }
1287
+ /**
1288
+ * Prune one round.
1289
+ *
1290
+ * Never rejects, for the same reason {@link TimerPoller.poll} does not: it is awaited inside
1291
+ * `onApplicationBootstrap` (so a rejection propagates into the host's `bootstrap()` and kills the
1292
+ * process) and fired as `void` on the interval (so a rejection is an unhandled one, also fatal).
1293
+ * Pruning history is the least urgent thing this package does — a `pruneTerminalRuns` that fails
1294
+ * on a locked table must cost this sweep and nothing more.
1295
+ */
1241
1296
  async sweep() {
1242
1297
  if (this.sweeping) return;
1243
1298
  const retention = this.options.retention;
@@ -1254,6 +1309,8 @@ var RetentionPoller = class {
1254
1309
  if (deleted < batchSize) break;
1255
1310
  }
1256
1311
  }
1312
+ } catch (error) {
1313
+ console.error("[nestjs-durable] retention sweep failed; retrying next interval:", error);
1257
1314
  } finally {
1258
1315
  this.sweeping = false;
1259
1316
  }
@@ -1648,7 +1705,11 @@ var TimerPoller = class {
1648
1705
  }
1649
1706
  async onApplicationBootstrap() {
1650
1707
  if (!isDrivingOperator(this.options)) return;
1651
- this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => void this.engine.runOne(runId));
1708
+ this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => {
1709
+ void this.engine.runOne(runId).catch((error) => {
1710
+ console.error(`[nestjs-durable] could not run enqueued run ${runId}:`, error);
1711
+ });
1712
+ });
1652
1713
  await this.poll();
1653
1714
  const intervalMs = this.options.timerPollMs ?? 1e3;
1654
1715
  if (intervalMs > 0) {
@@ -1660,22 +1721,45 @@ var TimerPoller = class {
1660
1721
  if (this.timer) clearInterval(this.timer);
1661
1722
  this.unsubscribeEnqueued?.();
1662
1723
  }
1724
+ /**
1725
+ * One sweep of the engine's recovery work.
1726
+ *
1727
+ * **This method never rejects, and that is a hard requirement rather than defensiveness.** It has
1728
+ * exactly two callers and a rejection out of either one kills the process: on boot it is awaited
1729
+ * inside `onApplicationBootstrap`, so the rejection propagates through `NestApplication.init()`
1730
+ * into the host's `bootstrap()`; on the interval it is a fire-and-forget `void`, so it surfaces as
1731
+ * an unhandled rejection. Both are fatal, and both re-occur on the next boot with the same state,
1732
+ * so a single unrecoverable run crash-loops every worker in the deployment — taking down every
1733
+ * other workflow with it. A poll that goes wrong must cost this tick, not the pod.
1734
+ *
1735
+ * Each sweep is isolated separately because they are independent: a store that fails
1736
+ * `recoverIncomplete` says nothing about whether durable timers should still fire, and skipping
1737
+ * the rest of the tick would silently widen one subsystem's outage into all of them.
1738
+ */
1663
1739
  async poll() {
1664
1740
  if (this.polling) return;
1665
1741
  this.polling = true;
1666
1742
  try {
1667
- await this.engine.runPending();
1668
- await this.engine.recoverIncomplete();
1669
- await this.engine.resumeDueTimers();
1670
- await this.engine.sweepTimeouts();
1743
+ await this.sweep("runPending", () => this.engine.runPending());
1744
+ await this.sweep("recoverIncomplete", () => this.engine.recoverIncomplete());
1745
+ await this.sweep("resumeDueTimers", () => this.engine.resumeDueTimers());
1746
+ await this.sweep("sweepTimeouts", () => this.engine.sweepTimeouts());
1671
1747
  const schedules = this.options.schedules;
1672
1748
  if (schedules && schedules.length > 0) {
1673
- await (0, import_nestjs_durable_core10.runSchedules)(this.engine, schedules, Date.now());
1749
+ await this.sweep("schedules", () => (0, import_nestjs_durable_core10.runSchedules)(this.engine, schedules, Date.now()));
1674
1750
  }
1675
1751
  } finally {
1676
1752
  this.polling = false;
1677
1753
  }
1678
1754
  }
1755
+ /** Run one sweep, absorbing and reporting its failure — see {@link poll}. */
1756
+ async sweep(name, run) {
1757
+ try {
1758
+ await run();
1759
+ } catch (error) {
1760
+ console.error(`[nestjs-durable] timer poll: ${name} failed; continuing:`, error);
1761
+ }
1762
+ }
1679
1763
  };
1680
1764
  TimerPoller = _ts_decorate9([
1681
1765
  (0, import_common9.Injectable)(),
@@ -1699,15 +1783,15 @@ var import_core4 = require("@nestjs/core");
1699
1783
 
1700
1784
  // src/input-validation.ts
1701
1785
  function classValidatorInput(cls) {
1702
- let cv;
1703
- let ct;
1704
- try {
1705
- cv = require("class-validator");
1706
- ct = require("class-transformer");
1707
- } catch {
1708
- throw new Error('@Workflow({ inputSchema }) needs the optional peers "class-validator" and "class-transformer" \u2014 install them, or pass a `validateInput` function instead.');
1709
- }
1710
1786
  return async (input) => {
1787
+ let cv;
1788
+ let ct;
1789
+ try {
1790
+ cv = await import("class-validator");
1791
+ ct = await import("class-transformer");
1792
+ } catch {
1793
+ throw new Error('@Workflow({ inputSchema }) needs the optional peers "class-validator" and "class-transformer" \u2014 install them, or pass a `validateInput` function instead.');
1794
+ }
1711
1795
  const instance = ct.plainToInstance(cls, input);
1712
1796
  const errors = await cv.validate(instance, {
1713
1797
  whitelist: true
@@ -2396,6 +2480,7 @@ var import_nestjs_durable_core15 = require("@dudousxd/nestjs-durable-core");
2396
2480
  DEAD_LETTER_METADATA,
2397
2481
  DURABLE_STEP_METADATA,
2398
2482
  DURABLE_WORKER_RUNNERS,
2483
+ DURABLE_WORKER_RUNTIME,
2399
2484
  DeadLetter,
2400
2485
  DurableModule,
2401
2486
  DurableStartClient,