@dudousxd/nestjs-durable-core 0.82.0 → 0.83.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 CHANGED
@@ -72,6 +72,7 @@ __export(index_exports, {
72
72
  ScheduleAlreadyExistsError: () => ScheduleAlreadyExistsError,
73
73
  ScheduleClient: () => ScheduleClient,
74
74
  ScheduleNotFoundError: () => ScheduleNotFoundError,
75
+ SchemaValidationError: () => SchemaValidationError,
75
76
  SignalTimeoutError: () => SignalTimeoutError,
76
77
  SingletonQueueFullError: () => SingletonQueueFullError,
77
78
  TERMINAL_RUN_STATUSES: () => TERMINAL_RUN_STATUSES,
@@ -183,6 +184,7 @@ __export(index_exports, {
183
184
  runStepHandler: () => runStepHandler,
184
185
  runValueFacetsFromRuns: () => runValueFacetsFromRuns,
185
186
  sanitizeQueueToken: () => sanitizeQueueToken,
187
+ scheduleRunIdAt: () => scheduleRunIdAt,
186
188
  scheduledRunId: () => scheduledRunId,
187
189
  singletonAdmissionDenialPatch: () => singletonAdmissionDenialPatch,
188
190
  singletonAdmissionTags: () => singletonAdmissionTags,
@@ -481,6 +483,31 @@ var FatalError = class extends Error {
481
483
  this.details = details;
482
484
  }
483
485
  };
486
+ var SchemaValidationError = class extends FatalError {
487
+ static {
488
+ __name(this, "SchemaValidationError");
489
+ }
490
+ static code = "schema_validation";
491
+ issues;
492
+ /**
493
+ * @param label What was validated, e.g. `workflow "checkout" input`; the message is
494
+ * `invalid <label>: <path>: <message>; …`.
495
+ * @param summary Overrides the `<path>: <message>; …` part, for issues whose messages already
496
+ * name their field (class-validator's `orderId must be a string`).
497
+ */
498
+ constructor(label, issues, code = "schema_validation", summary) {
499
+ const normalized = issues.map((issue) => ({
500
+ message: issue.message,
501
+ path: (issue.path ?? []).map((segment) => String(typeof segment === "object" && segment !== null ? segment.key : segment))
502
+ }));
503
+ const text = summary ?? normalized.map((issue) => issue.path.length > 0 ? `${issue.path.join(".")}: ${issue.message}` : issue.message).join("; ");
504
+ super(`invalid ${label}: ${text}`, code, {
505
+ issues: normalized
506
+ });
507
+ this.name = "SchemaValidationError";
508
+ this.issues = normalized;
509
+ }
510
+ };
484
511
  var ConcurrencyLimitError = class extends Error {
485
512
  static {
486
513
  __name(this, "ConcurrencyLimitError");
@@ -2212,9 +2239,14 @@ function liveHandoff(runId, checkpoints) {
2212
2239
  return live;
2213
2240
  }
2214
2241
  __name(liveHandoff, "liveHandoff");
2242
+ var SCHEMA_VALIDATION_CODES = /* @__PURE__ */ new Set([
2243
+ "invalid_input",
2244
+ "invalid_output"
2245
+ ]);
2215
2246
  function routeFor(routes, error2) {
2216
2247
  if (!routes?.length || error2.code && UNROUTABLE_CODES.has(error2.code)) return void 0;
2217
- return (error2.code ? routes.find((r) => r.code === error2.code) : void 0) ?? routes.find((r) => r.code === void 0);
2248
+ const { code } = error2;
2249
+ return (code ? routes.find((r) => r.code === code) : void 0) ?? (code && SCHEMA_VALIDATION_CODES.has(code) ? routes.find((r) => r.code === "schema_validation") : void 0) ?? routes.find((r) => r.code === void 0);
2218
2250
  }
2219
2251
  __name(routeFor, "routeFor");
2220
2252
  function assertRoutes(workflow, routes) {
@@ -2486,9 +2518,13 @@ __name(scheduleRunIdsAt, "scheduleRunIdsAt");
2486
2518
  async function runSchedules(engine, schedules, nowMs, opts) {
2487
2519
  const random = opts?.random ?? Math.random;
2488
2520
  const sleep = opts?.sleep ?? defaultSleep;
2521
+ const settled = settledIdsFor(engine);
2489
2522
  const ids = [];
2490
2523
  for (const s of schedules) {
2524
+ if (engine.isDraining?.()) break;
2491
2525
  if (s.paused) continue;
2526
+ const candidates = scheduleRunIdsAt(s, nowMs).filter((id) => !settled.get(s.key)?.has(id));
2527
+ if (candidates.length === 0) continue;
2492
2528
  if (s.overlap === "skip" && s.everyMs) {
2493
2529
  const prevBucket = Math.floor(nowMs / s.everyMs) - 1;
2494
2530
  if (prevBucket >= 0) {
@@ -2497,14 +2533,44 @@ async function runSchedules(engine, schedules, nowMs, opts) {
2497
2533
  }
2498
2534
  }
2499
2535
  if (s.jitter && s.jitter > 0) await sleep(Math.floor(random() * s.jitter));
2500
- for (const runId of scheduleRunIdsAt(s, nowMs)) {
2501
- await engine.start(s.workflow, s.input, runId);
2536
+ for (const runId of candidates) {
2537
+ if (engine.isDraining?.()) break;
2538
+ const existing = await engine.getRun(runId);
2539
+ remember(settled, s.key, runId);
2540
+ if (existing) continue;
2541
+ await engine.start(workflowName(s.workflow), s.input, runId, {
2542
+ namespace: s.namespace
2543
+ });
2502
2544
  ids.push(runId);
2503
2545
  }
2504
2546
  }
2505
2547
  return ids;
2506
2548
  }
2507
2549
  __name(runSchedules, "runSchedules");
2550
+ var settledRunIds = /* @__PURE__ */ new WeakMap();
2551
+ function settledIdsFor(engine) {
2552
+ let memo = settledRunIds.get(engine);
2553
+ if (memo === void 0) {
2554
+ memo = /* @__PURE__ */ new Map();
2555
+ settledRunIds.set(engine, memo);
2556
+ }
2557
+ return memo;
2558
+ }
2559
+ __name(settledIdsFor, "settledIdsFor");
2560
+ function remember(memo, key, runId) {
2561
+ const seen = memo.get(key);
2562
+ if (seen === void 0) {
2563
+ memo.set(key, /* @__PURE__ */ new Set([
2564
+ runId
2565
+ ]));
2566
+ return;
2567
+ }
2568
+ seen.add(runId);
2569
+ if (seen.size > 64) memo.set(key, /* @__PURE__ */ new Set([
2570
+ runId
2571
+ ]));
2572
+ }
2573
+ __name(remember, "remember");
2508
2574
 
2509
2575
  // src/schedules.ts
2510
2576
  var ScheduleAlreadyExistsError = class extends Error {
@@ -3303,11 +3369,6 @@ function normalizeChildOptions(options) {
3303
3369
  } : options ?? {};
3304
3370
  }
3305
3371
  __name(normalizeChildOptions, "normalizeChildOptions");
3306
- function formatIssuePath(path) {
3307
- if (!path || path.length === 0) return "(root)";
3308
- return path.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
3309
- }
3310
- __name(formatIssuePath, "formatIssuePath");
3311
3372
  function resolveStepPolicy(config, opts) {
3312
3373
  const policy = {};
3313
3374
  const retries = opts?.retries ?? config?.retries;
@@ -4139,11 +4200,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
4139
4200
  throw new Error(`workflow "${workflow}": ctx.upsertSearchAttributes schema validation must be synchronous (the searchAttributes schema's \`~standard.validate\` returned a Promise) \u2014 replay-determinism requires a sync validator; drop any async refinement from the schema passed to @Workflow({ searchAttributes }).`);
4140
4201
  }
4141
4202
  if (result.issues) {
4142
- const offendingKeys = [
4143
- ...new Set(result.issues.map((issue) => formatIssuePath(issue.path)))
4144
- ];
4145
- const details = result.issues.map((issue) => `${formatIssuePath(issue.path)}: ${issue.message}`).join("; ");
4146
- throw new Error(`workflow "${workflow}": ctx.upsertSearchAttributes rejected by its searchAttributes schema \u2014 offending key(s) ${offendingKeys.join(", ")} \u2014 ${details}`);
4203
+ throw new SchemaValidationError(`workflow "${workflow}" ctx.upsertSearchAttributes`, result.issues);
4147
4204
  }
4148
4205
  }
4149
4206
  await host.upsertSearchAttributes(runId, attrs);
@@ -5349,6 +5406,10 @@ var WorkflowEngine = class {
5349
5406
  for (const rearm of this.drainRearms) rearm();
5350
5407
  }
5351
5408
  }
5409
+ /** True once {@link beginDrain} (or {@link drain}) has been called. */
5410
+ isDraining() {
5411
+ return this.draining;
5412
+ }
5352
5413
  /**
5353
5414
  * Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
5354
5415
  * in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
@@ -10940,6 +11001,7 @@ var InMemoryTransport = class {
10940
11001
  ScheduleAlreadyExistsError,
10941
11002
  ScheduleClient,
10942
11003
  ScheduleNotFoundError,
11004
+ SchemaValidationError,
10943
11005
  SignalTimeoutError,
10944
11006
  SingletonQueueFullError,
10945
11007
  TERMINAL_RUN_STATUSES,
@@ -11051,6 +11113,7 @@ var InMemoryTransport = class {
11051
11113
  runStepHandler,
11052
11114
  runValueFacetsFromRuns,
11053
11115
  sanitizeQueueToken,
11116
+ scheduleRunIdAt,
11054
11117
  scheduledRunId,
11055
11118
  singletonAdmissionDenialPatch,
11056
11119
  singletonAdmissionTags,