@dudousxd/nestjs-durable-core 0.36.0 → 0.38.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.d.cts CHANGED
@@ -507,17 +507,18 @@ declare const TERMINAL_RUN_STATUSES: readonly RunStatus[];
507
507
  /**
508
508
  * One retention rule for hard-pruning terminal run history. Within `statuses` (terminal only), a run
509
509
  * is KEPT only if it satisfies EVERY bound you set, and pruned the moment it violates any:
510
- * - `maxAgeMs` — prune runs whose `updatedAt` (≈ the time they reached terminal) is older than
511
- * `now - maxAgeMs`.
510
+ * - `maxAge` — prune runs whose `updatedAt` (≈ the time they reached terminal) is older than
511
+ * `now - maxAge`. A number is milliseconds; a string is an `ms`-style duration parsed by
512
+ * {@link parseDuration} (`'7d'`, `'2w'`, `'90m'` — note `'m'` is MINUTES; use `'30d'` for a month).
512
513
  * - `maxCount` — keep only the `maxCount` most-recent (by `updatedAt`) runs in the status set; prune
513
514
  * everything past that.
514
515
  *
515
516
  * Set one or both (both = the most-restrictive wins: capped at `maxCount` rows AND nothing older than
516
- * `maxAgeMs`). Statuses not named by any policy are never pruned.
517
+ * `maxAge`). Statuses not named by any policy are never pruned.
517
518
  */
518
519
  interface RetentionPolicy {
519
520
  statuses: RunStatus[];
520
- maxAgeMs?: number;
521
+ maxAge?: number | string;
521
522
  maxCount?: number;
522
523
  }
523
524
  /** Typed, queryable per-run data — exact values for `eq`/`ne`, numbers/strings for range ops. */
@@ -1121,6 +1122,15 @@ interface WorkflowCtx {
1121
1122
  random(): Promise<number>;
1122
1123
  /** Deterministic UUID v4: recorded once, then replayed. Use instead of `crypto.randomUUID()`. */
1123
1124
  uuid(): Promise<string>;
1125
+ /**
1126
+ * Merge `attrs` into THIS run's {@link WorkflowRun.searchAttributes} — the indexed metadata the
1127
+ * dashboard and {@link RunQuery} filter on. Shallow merge: keys you don't pass are kept. Durable +
1128
+ * exactly-once — recorded at this position on the first run and SKIPPED on replay, so it does one
1129
+ * write, not one per turn. Use this instead of injecting the {@link StateStore} to mutate the run
1130
+ * you're executing (`@Inject(state-store)` + `store.updateRun(ctx.runId, …)` becomes
1131
+ * `ctx.upsertSearchAttributes(…)`).
1132
+ */
1133
+ upsertSearchAttributes(attrs: SearchAttributes): Promise<void>;
1124
1134
  }
1125
1135
  /** Result of executing or resuming a workflow run. */
1126
1136
  interface RunResult {
package/dist/index.d.ts CHANGED
@@ -507,17 +507,18 @@ declare const TERMINAL_RUN_STATUSES: readonly RunStatus[];
507
507
  /**
508
508
  * One retention rule for hard-pruning terminal run history. Within `statuses` (terminal only), a run
509
509
  * is KEPT only if it satisfies EVERY bound you set, and pruned the moment it violates any:
510
- * - `maxAgeMs` — prune runs whose `updatedAt` (≈ the time they reached terminal) is older than
511
- * `now - maxAgeMs`.
510
+ * - `maxAge` — prune runs whose `updatedAt` (≈ the time they reached terminal) is older than
511
+ * `now - maxAge`. A number is milliseconds; a string is an `ms`-style duration parsed by
512
+ * {@link parseDuration} (`'7d'`, `'2w'`, `'90m'` — note `'m'` is MINUTES; use `'30d'` for a month).
512
513
  * - `maxCount` — keep only the `maxCount` most-recent (by `updatedAt`) runs in the status set; prune
513
514
  * everything past that.
514
515
  *
515
516
  * Set one or both (both = the most-restrictive wins: capped at `maxCount` rows AND nothing older than
516
- * `maxAgeMs`). Statuses not named by any policy are never pruned.
517
+ * `maxAge`). Statuses not named by any policy are never pruned.
517
518
  */
518
519
  interface RetentionPolicy {
519
520
  statuses: RunStatus[];
520
- maxAgeMs?: number;
521
+ maxAge?: number | string;
521
522
  maxCount?: number;
522
523
  }
523
524
  /** Typed, queryable per-run data — exact values for `eq`/`ne`, numbers/strings for range ops. */
@@ -1121,6 +1122,15 @@ interface WorkflowCtx {
1121
1122
  random(): Promise<number>;
1122
1123
  /** Deterministic UUID v4: recorded once, then replayed. Use instead of `crypto.randomUUID()`. */
1123
1124
  uuid(): Promise<string>;
1125
+ /**
1126
+ * Merge `attrs` into THIS run's {@link WorkflowRun.searchAttributes} — the indexed metadata the
1127
+ * dashboard and {@link RunQuery} filter on. Shallow merge: keys you don't pass are kept. Durable +
1128
+ * exactly-once — recorded at this position on the first run and SKIPPED on replay, so it does one
1129
+ * write, not one per turn. Use this instead of injecting the {@link StateStore} to mutate the run
1130
+ * you're executing (`@Inject(state-store)` + `store.updateRun(ctx.runId, …)` becomes
1131
+ * `ctx.upsertSearchAttributes(…)`).
1132
+ */
1133
+ upsertSearchAttributes(attrs: SearchAttributes): Promise<void>;
1124
1134
  }
1125
1135
  /** Result of executing or resuming a workflow run. */
1126
1136
  interface RunResult {
package/dist/index.js CHANGED
@@ -1362,9 +1362,28 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
1362
1362
  const now = /* @__PURE__ */ __name(() => step("now", async () => host.clock()), "now");
1363
1363
  const random = /* @__PURE__ */ __name(() => step("random", async () => Math.random()), "random");
1364
1364
  const uuid = /* @__PURE__ */ __name(() => step("uuid", async () => globalThis.crypto.randomUUID()), "uuid");
1365
+ const upsertSearchAttributes = /* @__PURE__ */ __name(async (attrs) => {
1366
+ const current = pos.next();
1367
+ const existing = await readCheckpoint(current);
1368
+ if (existing) {
1369
+ if (existing.name !== "searchAttributes") {
1370
+ throw new NonDeterminismError(runId, current, "searchAttributes", existing.name);
1371
+ }
1372
+ return;
1373
+ }
1374
+ await host.upsertSearchAttributes(runId, attrs);
1375
+ await writeCheckpoint(instantCheckpoint({
1376
+ runId,
1377
+ seq: current,
1378
+ name: "searchAttributes",
1379
+ kind: "local",
1380
+ output: attrs
1381
+ }));
1382
+ }, "upsertSearchAttributes");
1365
1383
  return {
1366
1384
  runId,
1367
1385
  step,
1386
+ upsertSearchAttributes,
1368
1387
  transaction,
1369
1388
  callEntity,
1370
1389
  signalEntity,
@@ -3093,6 +3112,17 @@ var WorkflowEngine = class {
3093
3112
  priority
3094
3113
  }).catch(() => void 0));
3095
3114
  }, "startChild"),
3115
+ // Shallow-merge into the run's searchAttributes (the ctx primitive makes this exactly-once).
3116
+ upsertSearchAttributes: /* @__PURE__ */ __name(async (runId, attrs) => {
3117
+ const run = await this.store.getRun(runId);
3118
+ await this.store.updateRun(runId, {
3119
+ searchAttributes: {
3120
+ ...run?.searchAttributes ?? {},
3121
+ ...attrs
3122
+ },
3123
+ updatedAt: /* @__PURE__ */ new Date()
3124
+ });
3125
+ }, "upsertSearchAttributes"),
3096
3126
  signalEntity: /* @__PURE__ */ __name((name, key, op, arg, reply) => {
3097
3127
  queueMicrotask(() => void this.entities.dispatch(name, key, op, arg, reply).catch(() => void 0));
3098
3128
  }, "signalEntity"),