@lostgradient/weft 0.12.0 → 0.14.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.
Files changed (226) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/api.d.ts +2 -0
  3. package/dist/cli/api.js +3 -4
  4. package/dist/cli/generated/operation-catalog.snapshot.json +508 -12
  5. package/dist/cli/generated/operation-client.generated.d.ts +118 -10
  6. package/dist/cli/generated/operation-client.generated.js +74 -1
  7. package/dist/cli/help-text.d.ts +3 -3
  8. package/dist/cli/help-text.js +4 -4
  9. package/dist/cli/operation-catalog-snapshot.d.ts +12 -3
  10. package/dist/cli/operation-catalog-snapshot.js +14 -5
  11. package/dist/cli/operation-client-runtime.d.ts +29 -0
  12. package/dist/cli/schedule.js +1 -3
  13. package/dist/cli/serve-registrations.d.ts +3 -3
  14. package/dist/cli/serve-registrations.js +2 -2
  15. package/dist/cli/shutdown.d.ts +11 -0
  16. package/dist/cli/shutdown.js +25 -0
  17. package/dist/cli/validate.d.ts +1 -1
  18. package/dist/cli/version-check.d.ts +1 -1
  19. package/dist/cli/version-check.js +1 -1
  20. package/dist/cli-main.js +10 -29
  21. package/dist/client/client-storage.d.ts +24 -0
  22. package/dist/client/client-storage.js +14 -0
  23. package/dist/client/handle-delegation.d.ts +3 -3
  24. package/dist/client/handle-delegation.js +2 -2
  25. package/dist/client/http-client-requests.d.ts +5 -3
  26. package/dist/client/http-client-requests.js +20 -2
  27. package/dist/client/http-client-storage.d.ts +3 -0
  28. package/dist/client/http-client-storage.js +82 -0
  29. package/dist/client/http-client.d.ts +9 -6
  30. package/dist/client/http-client.js +13 -18
  31. package/dist/client/http-operations.d.ts +3 -1
  32. package/dist/client/http-operations.js +77 -1
  33. package/dist/client/http-request.d.ts +20 -14
  34. package/dist/client/http-request.js +21 -31
  35. package/dist/client/in-process-operations.d.ts +2 -2
  36. package/dist/client/in-process-operations.js +2 -2
  37. package/dist/client/index.d.ts +5 -1
  38. package/dist/client/index.js +13 -2
  39. package/dist/client/interface.d.ts +26 -26
  40. package/dist/client/local.d.ts +9 -6
  41. package/dist/client/local.js +9 -4
  42. package/dist/client/search-params.js +10 -6
  43. package/dist/client/start-body.d.ts +3 -1
  44. package/dist/client/start-body.js +9 -0
  45. package/dist/core/bulk-workflow-filter.d.ts +2 -1
  46. package/dist/core/bulk-workflow-filter.js +5 -1
  47. package/dist/core/context/parallel-operations.d.ts +0 -2
  48. package/dist/core/context/parallel-operations.js +0 -7
  49. package/dist/core/engine/async-activity-records.d.ts +12 -1
  50. package/dist/core/engine/async-activity-records.js +81 -2
  51. package/dist/core/engine/bulk-operations-purge.js +16 -1
  52. package/dist/core/engine/bulk-operations-shared.js +2 -0
  53. package/dist/core/engine/callback-checkpoint-persistence.d.ts +2 -0
  54. package/dist/core/engine/callback-checkpoint-persistence.js +6 -3
  55. package/dist/core/engine/callback-creators-schedule.d.ts +2 -1
  56. package/dist/core/engine/callback-creators-schedule.js +3 -3
  57. package/dist/core/engine/callback-creators.js +4 -22
  58. package/dist/core/engine/checkpoint-io.d.ts +1 -1
  59. package/dist/core/engine/checkpoint-reads.js +11 -1
  60. package/dist/core/engine/child-workflow.js +15 -6
  61. package/dist/core/engine/construction.js +3 -0
  62. package/dist/core/engine/disposal.js +2 -0
  63. package/dist/core/engine/engine-internal-types.d.ts +4 -1
  64. package/dist/core/engine/fenced-write.d.ts +5 -3
  65. package/dist/core/engine/finalizer-status.d.ts +10 -0
  66. package/dist/core/engine/finalizer-status.js +82 -0
  67. package/dist/core/engine/index.d.ts +26 -2
  68. package/dist/core/engine/index.js +71 -8
  69. package/dist/core/engine/internals.d.ts +3 -0
  70. package/dist/core/engine/lease-health.d.ts +64 -0
  71. package/dist/core/engine/lease-health.js +0 -0
  72. package/dist/core/engine/lease-manager.d.ts +4 -2
  73. package/dist/core/engine/lease-manager.js +32 -4
  74. package/dist/core/engine/lifecycle/recovered-services.js +0 -2
  75. package/dist/core/engine/lifecycle/shared.d.ts +11 -8
  76. package/dist/core/engine/lifecycle/start-batch.js +12 -0
  77. package/dist/core/engine/lifecycle/start.d.ts +1 -1
  78. package/dist/core/engine/lifecycle/start.js +34 -5
  79. package/dist/core/engine/operations-coordination.d.ts +1 -12
  80. package/dist/core/engine/operations-coordination.js +58 -11
  81. package/dist/core/engine/operations-speculate.d.ts +1 -1
  82. package/dist/core/engine/operations-speculate.js +19 -5
  83. package/dist/core/engine/parallel-dispatch.d.ts +1 -1
  84. package/dist/core/engine/parallel-dispatch.js +1 -1
  85. package/dist/core/engine/registration.js +14 -0
  86. package/dist/core/engine/review-list-entries.d.ts +1 -1
  87. package/dist/core/engine/review-list-entries.js +6 -30
  88. package/dist/core/engine/schedule-handle.d.ts +4 -4
  89. package/dist/core/engine/schedule-handle.js +2 -2
  90. package/dist/core/engine/schedule-overlap.d.ts +11 -0
  91. package/dist/core/engine/schedule-overlap.js +50 -0
  92. package/dist/core/engine/schedule-run.d.ts +11 -3
  93. package/dist/core/engine/schedule-run.js +27 -4
  94. package/dist/core/engine/schedule-timer.js +10 -1
  95. package/dist/core/engine/schedules.d.ts +5 -4
  96. package/dist/core/engine/schedules.js +62 -56
  97. package/dist/core/engine/state-utilities.js +1 -1
  98. package/dist/core/engine/storage-io.d.ts +12 -0
  99. package/dist/core/engine/storage-io.js +49 -10
  100. package/dist/core/engine/termination/finalizer-claim.d.ts +8 -6
  101. package/dist/core/engine/termination/finalizer-claim.js +2 -1
  102. package/dist/core/engine/termination/finalizer.js +10 -14
  103. package/dist/core/engine/timeline-coordinator-constants.d.ts +3 -0
  104. package/dist/core/engine/timeline-coordinator-constants.js +1 -0
  105. package/dist/core/engine/timeline-coordinator-detail.d.ts +13 -0
  106. package/dist/core/engine/timeline-coordinator-detail.js +76 -0
  107. package/dist/core/engine/validation/schedule-options.d.ts +8 -1
  108. package/dist/core/engine/validation/schedule-options.js +18 -0
  109. package/dist/core/engine/validation/schedule.d.ts +1 -1
  110. package/dist/core/engine/validation/schedule.js +27 -3
  111. package/dist/core/engine/validation.js +66 -2
  112. package/dist/core/engine/workflow-state-stream.d.ts +5 -1
  113. package/dist/core/engine/workflow-state-stream.js +37 -14
  114. package/dist/core/events/schedule-events.d.ts +3 -4
  115. package/dist/core/events/workflow-events.d.ts +2 -1
  116. package/dist/core/list-filter-validation.d.ts +3 -0
  117. package/dist/core/list-filter-validation.js +11 -0
  118. package/dist/core/registry-snapshot.d.ts +32 -0
  119. package/dist/core/registry-snapshot.js +28 -0
  120. package/dist/core/review/index.d.ts +91 -4
  121. package/dist/core/review/index.js +29 -0
  122. package/dist/core/types/activity.d.ts +52 -5
  123. package/dist/core/types/bulk.d.ts +1 -0
  124. package/dist/core/types/list-options.d.ts +6 -0
  125. package/dist/core/types/options.d.ts +6 -5
  126. package/dist/core/types/reviews.d.ts +2 -2
  127. package/dist/core/types/schedules.d.ts +45 -2
  128. package/dist/core/types/services-resolution.d.ts +5 -5
  129. package/dist/core/types/state.d.ts +64 -0
  130. package/dist/core/types/workflow-log.d.ts +3 -2
  131. package/dist/core/types/workflow-observability.d.ts +50 -0
  132. package/dist/core/types/workflow-observability.js +0 -0
  133. package/dist/core/types/workflow-registry.d.ts +7 -0
  134. package/dist/core/types.d.ts +1 -0
  135. package/dist/core/types.js +1 -0
  136. package/dist/diagnostics/validate.d.ts +9 -30
  137. package/dist/diagnostics/validate.js +83 -21
  138. package/dist/diagnostics/version-check.d.ts +2 -2
  139. package/dist/http.js +2 -2
  140. package/dist/index.d.ts +4 -3
  141. package/dist/indexeddb.js +1 -1
  142. package/dist/server/access-policy-metadata.d.ts +92 -0
  143. package/dist/server/access-policy-metadata.js +78 -0
  144. package/dist/server/fault-to-http.d.ts +3 -7
  145. package/dist/server/fault-to-http.js +2 -34
  146. package/dist/server/handler/index.js +1 -1
  147. package/dist/server/handler/route-dispatch.d.ts +10 -3
  148. package/dist/server/handler/route-dispatch.js +19 -4
  149. package/dist/server/index.d.ts +1 -1
  150. package/dist/server/index.js +1 -0
  151. package/dist/server/interactive-operations.js +2 -0
  152. package/dist/server/openapi-error-responses.js +41 -4
  153. package/dist/server/openapi.d.ts +3 -1
  154. package/dist/server/openapi.js +4 -0
  155. package/dist/server/openrpc-document-schema.d.ts +64 -0
  156. package/dist/server/openrpc-document-schema.js +6 -26
  157. package/dist/server/openrpc.d.ts +4 -0
  158. package/dist/server/openrpc.js +9 -33
  159. package/dist/server/operation-catalog/index.d.ts +1 -1
  160. package/dist/server/operation-catalog/raise-fault.d.ts +2 -1
  161. package/dist/server/operation-catalog/types.d.ts +2 -1
  162. package/dist/server/operation-fault.d.ts +22 -8
  163. package/dist/server/operation-fault.js +65 -13
  164. package/dist/server/operation-registry.d.ts +1 -1
  165. package/dist/server/operations/aggregate-workflows.d.ts +6 -0
  166. package/dist/server/operations/aggregate-workflows.js +3 -2
  167. package/dist/server/operations/async-activity.d.ts +14 -0
  168. package/dist/server/operations/async-activity.js +88 -4
  169. package/dist/server/operations/bulk-filter-helpers.d.ts +3 -0
  170. package/dist/server/operations/bulk-filter-helpers.js +27 -3
  171. package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +6 -0
  172. package/dist/server/operations/bulk-signal-workflows.d.ts +6 -0
  173. package/dist/server/operations/get-stream-chunks.js +2 -7
  174. package/dist/server/operations/get-system-lease.d.ts +19 -0
  175. package/dist/server/operations/get-system-lease.js +48 -0
  176. package/dist/server/operations/get-workflow-observability.d.ts +24 -0
  177. package/dist/server/operations/get-workflow-observability.js +80 -0
  178. package/dist/server/operations/get-workflow-result.js +2 -2
  179. package/dist/server/operations/get-workflow.d.ts +2 -2
  180. package/dist/server/operations/list-filter-query-extractor.js +15 -0
  181. package/dist/server/operations/list-reviews.js +2 -19
  182. package/dist/server/operations/list-workflows.d.ts +6 -0
  183. package/dist/server/operations/list-workflows.js +12 -3
  184. package/dist/server/operations/operation-helpers.d.ts +9 -18
  185. package/dist/server/operations/operation-helpers.js +5 -23
  186. package/dist/server/operations/purge-workflows.d.ts +3 -0
  187. package/dist/server/operations/recover-all.js +1 -9
  188. package/dist/server/operations/single-workflow-control-operation.d.ts +2 -1
  189. package/dist/server/operations/single-workflow-tag-mutation.js +2 -2
  190. package/dist/server/operations/sse-stream.js +2 -7
  191. package/dist/server/operations/storage-capabilities.d.ts +34 -0
  192. package/dist/server/operations/storage-capabilities.js +38 -0
  193. package/dist/server/operations/storage.js +8 -17
  194. package/dist/server/operations/stream-workflow-sse.js +3 -9
  195. package/dist/server/operations/update-schedule.d.ts +8 -0
  196. package/dist/server/operations/update-schedule.js +70 -7
  197. package/dist/server/operations/workflow-events-sse.js +2 -2
  198. package/dist/server/rest-binding.d.ts +7 -6
  199. package/dist/server/rest-bindings.d.ts +2 -13
  200. package/dist/server/rest-bindings.js +19 -10
  201. package/dist/server/task-queue.d.ts +7 -5
  202. package/dist/storage/bounded-ndjson-response.d.ts +5 -0
  203. package/dist/storage/bounded-ndjson-response.js +33 -0
  204. package/dist/storage/capabilities.d.ts +1 -1
  205. package/dist/storage/cloudflare-value-codec.d.ts +5 -3
  206. package/dist/storage/cloudflare-value-codec.js +1 -1
  207. package/dist/storage/compressed-storage.js +1 -1
  208. package/dist/storage/http.js +5 -37
  209. package/dist/storage/index.d.ts +7 -1
  210. package/dist/storage/interface.d.ts +16 -4
  211. package/dist/storage/interface.js +1 -1
  212. package/dist/storage/key-prefixes.d.ts +1 -1
  213. package/dist/storage/key-prefixes.js +4 -0
  214. package/dist/storage/lmdb.js +1 -1
  215. package/dist/storage/memory.js +1 -1
  216. package/dist/storage/neon.d.ts +3 -16
  217. package/dist/storage/neon.js +4 -4
  218. package/dist/storage/postgres.js +4 -4
  219. package/dist/storage/resolve.js +1 -1
  220. package/dist/storage/scoped-storage.js +1 -1
  221. package/dist/storage/testing.js +1 -1
  222. package/dist/storage/turso.js +2 -2
  223. package/dist/version.d.ts +1 -1
  224. package/dist/version.js +1 -1
  225. package/dist/web-extension.js +1 -1
  226. package/package.json +1 -1
@@ -64,12 +64,13 @@ export async function settleOnRunningClaim(internals, workflowId, runningBytes,
64
64
  { key: KEYS.teardownOwed(workflowId), expectedValue: runningBytes }
65
65
  ]);
66
66
  }
67
- export async function deadLetterTeardown(internals, workflowId, workflowType, attempts, expectedBytes, details) {
67
+ export async function deadLetterTeardown(internals, workflowId, workflowType, attempts, expectedBytes, details, workflowExecutionToken) {
68
68
  const deadLetter = {
69
69
  type: workflowType,
70
70
  lastError: details.lastError,
71
71
  attempts,
72
72
  deadLetteredAt: internals.options.getNow(),
73
+ ...workflowExecutionToken === void 0 ? {} : { workflowExecutionToken },
73
74
  ...details.finalizerInput === void 0 ? {} : { finalizerInput: details.finalizerInput }
74
75
  };
75
76
  return settleOnRunningClaim(internals, workflowId, expectedBytes, [
@@ -1,6 +1,7 @@
1
1
  import { KEYS } from "../../../storage/interface.js";
2
2
  import { decode } from "../../codec.js";
3
3
  import { WorkflowTeardownEvent } from "../../events.js";
4
+ import { buildTeardownSuccessOperations } from "../finalizer-status.js";
4
5
  import { isTeardownClaim, parseTeardownTimerId } from "../state-utilities.js";
5
6
  import { runFinalizerActivity } from "./finalizer-activity.js";
6
7
  import {
@@ -21,10 +22,7 @@ export { teardownStaleThresholdMs } from "./finalizer-claim.js";
21
22
  const TERMINAL_STATUSES_OWED_TEARDOWN = new Set([
22
23
  "cancelled",
23
24
  "timed-out"
24
- ]);
25
- function errorMessage(error) {
26
- return error instanceof Error ? error.message : String(error);
27
- }
25
+ ]), errorMessage = (error) => error instanceof Error ? error.message : String(error);
28
26
  async function rearmOnLostSettle(internals, workflowId, token) {
29
27
  await rearmTeardownTimer(internals, workflowId, token, TEARDOWN_SELF_HEAL_DELAY_MS);
30
28
  }
@@ -63,7 +61,7 @@ async function resolveTeardownDrive(internals, workflowId, token, callbacks) {
63
61
  return { kind: "rearm", token };
64
62
  const finalizerStateBytes = await internals.storage.get(KEYS.finalizerState(workflowId));
65
63
  if (finalizerStateBytes === null) {
66
- await deadLetterMissingState(internals, workflowId, state.type, claim.attempts, markerBytes, callbacks);
64
+ await deadLetterMissingState(internals, workflowId, state.type, state.workflowExecutionToken, claim.attempts, markerBytes, callbacks);
67
65
  return { kind: "cleared" };
68
66
  }
69
67
  return {
@@ -71,12 +69,12 @@ async function resolveTeardownDrive(internals, workflowId, token, callbacks) {
71
69
  drive: { state, claim, markerBytes, finalizer, finalizerInput: decode(finalizerStateBytes) }
72
70
  };
73
71
  }
74
- async function deadLetterMissingState(internals, workflowId, workflowType, attempts, expectedBytes, callbacks) {
72
+ async function deadLetterMissingState(internals, workflowId, workflowType, workflowExecutionToken, attempts, expectedBytes, callbacks) {
75
73
  const lastError = "finalizer state missing \u2014 recorded resource cannot be torn down";
76
74
  if (await deadLetterTeardown(internals, workflowId, workflowType, attempts, expectedBytes, {
77
75
  lastError,
78
76
  finalizerInput: void 0
79
- }))
77
+ }, workflowExecutionToken))
80
78
  callbacks.dispatchEvent(new WorkflowTeardownEvent(workflowId, workflowType, "dead-lettered", attempts, lastError));
81
79
  }
82
80
  export async function runWorkflowFinalizer(internals, workflowId, timerId, callbacks) {
@@ -105,7 +103,7 @@ async function driveResolvedTeardown(internals, workflowId, token, drive, callba
105
103
  }
106
104
  const result = await runFinalizerActivity(finalizer, finalizerInput, attempt, internals.abortController.signal, state.workflowExecutionToken);
107
105
  if (result.ok) {
108
- await settleTeardownSuccess(internals, workflowId, state.type, token, attempt, runningBytes, callbacks);
106
+ await settleTeardownSuccess(internals, workflowId, state.type, state.workflowExecutionToken, token, attempt, runningBytes, callbacks);
109
107
  return;
110
108
  }
111
109
  if (result.abortedByShutdown) {
@@ -123,11 +121,9 @@ async function driveResolvedTeardown(internals, workflowId, token, drive, callba
123
121
  }
124
122
  await settleTeardownFailure(internals, workflowId, state, attempt, token, runningBytes, result.error, callbacks);
125
123
  }
126
- async function settleTeardownSuccess(internals, workflowId, workflowType, token, attempt, runningBytes, callbacks) {
127
- if (!await settleOnRunningClaim(internals, workflowId, runningBytes, [
128
- { type: "delete", key: KEYS.teardownOwed(workflowId) },
129
- { type: "delete", key: KEYS.finalizerState(workflowId) }
130
- ])) {
124
+ async function settleTeardownSuccess(internals, workflowId, workflowType, workflowExecutionToken, token, attempt, runningBytes, callbacks) {
125
+ const operations = buildTeardownSuccessOperations(workflowId, attempt, internals.options.getNow(), workflowExecutionToken);
126
+ if (!await settleOnRunningClaim(internals, workflowId, runningBytes, operations)) {
131
127
  await rearmOnLostSettle(internals, workflowId, token);
132
128
  return;
133
129
  }
@@ -140,7 +136,7 @@ async function settleTeardownFailure(internals, workflowId, state, attempt, toke
140
136
  if (!await deadLetterTeardown(internals, workflowId, state.type, attempt, runningBytes, {
141
137
  lastError: message,
142
138
  finalizerInput: finalizerStateBytes === null ? void 0 : decode(finalizerStateBytes)
143
- })) {
139
+ }, state.workflowExecutionToken)) {
144
140
  await rearmOnLostSettle(internals, workflowId, token);
145
141
  return;
146
142
  }
@@ -0,0 +1,3 @@
1
+ /** Prevent one fan-out operation from making a timeline record grow without bound. */
2
+ export declare const MAX_TIMELINE_COORDINATOR_DETAILS = 100;
3
+ export declare const MAX_TIMELINE_DETAIL_STRING_LENGTH = 512;
@@ -0,0 +1 @@
1
+ export const MAX_TIMELINE_COORDINATOR_DETAILS = 100, MAX_TIMELINE_DETAIL_STRING_LENGTH = 512;
@@ -0,0 +1,13 @@
1
+ import type { ContextOperationRequest } from '../context.ts';
2
+ import type { ParallelBranchSlot } from '../context/parallel-cache-entry.ts';
3
+ import type { WorkflowTimelineOperationDetail } from '../types.ts';
4
+ import type { EngineInternals } from './internals.ts';
5
+ export declare function operationTimelineDetail(operation: ContextOperationRequest, index: number, outcome: WorkflowTimelineOperationDetail['outcome'], options?: {
6
+ error?: unknown;
7
+ key?: string;
8
+ }): WorkflowTimelineOperationDetail;
9
+ export declare function parallelTimelineDetails(operations: readonly ContextOperationRequest[], slots: readonly ParallelBranchSlot[], branchNames?: readonly string[]): WorkflowTimelineOperationDetail[];
10
+ export declare function raceTimelineDetails(operations: readonly ContextOperationRequest[], winnerIndex: number, branchNames?: readonly string[], winnerError?: unknown): WorkflowTimelineOperationDetail[];
11
+ export declare function runAllTimelineDetails(branchNames: readonly string[], slots: readonly ParallelBranchSlot[], branches: Record<string, readonly [Function] | readonly [Function, unknown]>): WorkflowTimelineOperationDetail[];
12
+ export declare function recordTimelineBranches(internals: EngineInternals, workflowId: string, details: readonly WorkflowTimelineOperationDetail[]): void;
13
+ export declare function recordTimelineSpeculation(internals: EngineInternals, workflowId: string, details: readonly WorkflowTimelineOperationDetail[], outcome: 'committed' | 'rolled-back'): void;
@@ -0,0 +1,76 @@
1
+ import { getTimelineOperationLabel, summarizeTimelineValue } from "./state-utilities.js";
2
+ import {
3
+ MAX_TIMELINE_COORDINATOR_DETAILS,
4
+ MAX_TIMELINE_DETAIL_STRING_LENGTH
5
+ } from "./timeline-coordinator-constants.js";
6
+ function boundedString(value) {
7
+ return value.length <= MAX_TIMELINE_DETAIL_STRING_LENGTH ? value : `${value.slice(0, MAX_TIMELINE_DETAIL_STRING_LENGTH - 1)}\u2026`;
8
+ }
9
+ function boundedErrorSummary(error) {
10
+ return boundedString(summarizeTimelineValue(error));
11
+ }
12
+ export function operationTimelineDetail(operation, index, outcome, options = {}) {
13
+ return {
14
+ index,
15
+ ...options.key === void 0 ? {} : { key: boundedString(options.key) },
16
+ operationId: boundedString(operation.operationId),
17
+ operationType: operation.type,
18
+ operationLabel: boundedString(getTimelineOperationLabel(operation)),
19
+ outcome,
20
+ ...options.error === void 0 ? {} : { errorSummary: boundedErrorSummary(options.error) }
21
+ };
22
+ }
23
+ export function parallelTimelineDetails(operations, slots, branchNames) {
24
+ return operations.map((operation, index) => {
25
+ const slot = slots[index];
26
+ return operationTimelineDetail(operation, index, slot?.status === "fulfilled" ? "fulfilled" : "rejected", {
27
+ ...branchNames?.[index] === void 0 ? {} : { key: branchNames[index] },
28
+ ...slot?.status === "rejected" ? { error: slot.reason } : {}
29
+ });
30
+ });
31
+ }
32
+ export function raceTimelineDetails(operations, winnerIndex, branchNames, winnerError) {
33
+ return operations.map((operation, index) => operationTimelineDetail(operation, index, index === winnerIndex ? "won" : "lost", {
34
+ ...branchNames?.[index] === void 0 ? {} : { key: branchNames[index] },
35
+ ...index === winnerIndex && winnerError !== void 0 ? { error: winnerError } : {}
36
+ }));
37
+ }
38
+ export function runAllTimelineDetails(branchNames, slots, branches) {
39
+ return branchNames.map((key, index) => {
40
+ const slot = slots[index], branchFunction = branches[key]?.[0];
41
+ return {
42
+ index,
43
+ key: boundedString(key),
44
+ operationId: boundedString(slot?.operationId ?? `run-all:${String(index)}:${key}`),
45
+ operationType: "activity",
46
+ operationLabel: boundedString(branchFunction?.name || key),
47
+ outcome: slot?.status === "fulfilled" ? "fulfilled" : "rejected",
48
+ ...slot?.status === "rejected" ? { errorSummary: boundedErrorSummary(slot.reason) } : {}
49
+ };
50
+ });
51
+ }
52
+ function boundedDetails(details) {
53
+ return {
54
+ details: details.slice(0, MAX_TIMELINE_COORDINATOR_DETAILS),
55
+ omitted: Math.max(0, details.length - MAX_TIMELINE_COORDINATOR_DETAILS)
56
+ };
57
+ }
58
+ export function recordTimelineBranches(internals, workflowId, details) {
59
+ const entry = internals.pendingTimelineEntries?.get(workflowId)?.entry;
60
+ if (entry === void 0)
61
+ return;
62
+ const bounded = boundedDetails(details);
63
+ entry.branches = bounded.details;
64
+ if (bounded.omitted > 0)
65
+ entry.branchesOmitted = bounded.omitted;
66
+ }
67
+ export function recordTimelineSpeculation(internals, workflowId, details, outcome) {
68
+ const entry = internals.pendingTimelineEntries?.get(workflowId)?.entry;
69
+ if (entry === void 0)
70
+ return;
71
+ const bounded = boundedDetails(details);
72
+ entry.children = bounded.details;
73
+ if (bounded.omitted > 0)
74
+ entry.childrenOmitted = bounded.omitted;
75
+ entry.speculationOutcome = outcome;
76
+ }
@@ -1,9 +1,16 @@
1
- import type { ScheduleOptions, ScheduleOverlapPolicy } from '../../types.ts';
1
+ import type { ScheduleOptions, ScheduleOverlapPolicy, ScheduleUpdateOptions } from '../../types.ts';
2
2
  export declare const SCHEDULE_OVERLAP_POLICIES: Set<ScheduleOverlapPolicy>;
3
3
  type NormalizedScheduleOptions = Required<Pick<ScheduleOptions, 'overlap' | 'backfill'>> & {
4
4
  id?: string;
5
5
  description?: string;
6
6
  jitterMs?: number;
7
7
  };
8
+ type NormalizedScheduleUpdateOptions = {
9
+ description?: string;
10
+ overlap?: ScheduleOverlapPolicy;
11
+ backfill?: boolean;
12
+ jitterMs?: number;
13
+ };
8
14
  export declare function normalizeScheduleOptions(options: ScheduleOptions | undefined): NormalizedScheduleOptions;
15
+ export declare function normalizeScheduleUpdateOptions(options: ScheduleUpdateOptions | undefined): NormalizedScheduleUpdateOptions;
9
16
  export {};
@@ -23,6 +23,24 @@ export function normalizeScheduleOptions(options) {
23
23
  }
24
24
  };
25
25
  }
26
+ export function normalizeScheduleUpdateOptions(options) {
27
+ if (options === void 0)
28
+ return {};
29
+ if (typeof options !== "object" || options === null)
30
+ throw Error("options must be an object when provided");
31
+ return {
32
+ ...options.description !== void 0 && {
33
+ description: normalizeScheduleDescription(options.description)
34
+ },
35
+ ...options.overlap !== void 0 && { overlap: normalizeScheduleOverlap(options.overlap) },
36
+ ...options.backfill !== void 0 && {
37
+ backfill: normalizeScheduleBackfill(options.backfill)
38
+ },
39
+ ...options.jitter !== void 0 && {
40
+ jitterMs: normalizeScheduleJitter(options.jitter, "options.jitter")
41
+ }
42
+ };
43
+ }
26
44
  function normalizeScheduleDescription(description) {
27
45
  if (typeof description !== "string")
28
46
  throw Error("options.description must be a string when provided");
@@ -1,5 +1,5 @@
1
1
  import type { ScheduleFilter, ScheduleOverlapPolicy, ScheduleSpec, ScheduleState, ScheduleStatus } from '../../types.ts';
2
- export { normalizeScheduleOptions, SCHEDULE_OVERLAP_POLICIES } from './schedule-options.ts';
2
+ export { normalizeScheduleOptions, normalizeScheduleUpdateOptions, SCHEDULE_OVERLAP_POLICIES, } from './schedule-options.ts';
3
3
  export declare const SCHEDULE_STATUSES: Set<ScheduleStatus>;
4
4
  export declare function isValidScheduleTimestamp(value: unknown): value is number;
5
5
  export declare function isValidScheduleStatus(value: unknown): value is ScheduleStatus;
@@ -4,7 +4,11 @@ import { parseCronExpression } from "../../schedule.js";
4
4
  import { parseDuration } from "../../scheduler.js";
5
5
  import { coerceStartWorkflowId } from "../../start-workflow-validation.js";
6
6
  import { SCHEDULE_OVERLAP_POLICIES } from "./schedule-options.js";
7
- export { normalizeScheduleOptions, SCHEDULE_OVERLAP_POLICIES } from "./schedule-options.js";
7
+ export {
8
+ normalizeScheduleOptions,
9
+ normalizeScheduleUpdateOptions,
10
+ SCHEDULE_OVERLAP_POLICIES
11
+ } from "./schedule-options.js";
8
12
  export const SCHEDULE_STATUSES = new Set(["active", "paused", "cancelled"]);
9
13
  export function isValidScheduleTimestamp(value) {
10
14
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
@@ -206,11 +210,29 @@ function decodeScheduleCurrentWorkflowId(decoded, scheduleId) {
206
210
  }
207
211
  function decodeScheduleQueuedRuns(decoded, scheduleId) {
208
212
  const queuedRuns = decoded.queuedRuns;
209
- if (typeof queuedRuns !== "number" || !Number.isSafeInteger(queuedRuns) || queuedRuns < 0) {
213
+ if (!Array.isArray(queuedRuns)) {
210
214
  rejectInvalidScheduleRecord(scheduleId, "with invalid queuedRuns");
211
215
  return null;
212
216
  }
213
- return queuedRuns;
217
+ const result = [], workflowIds = new Set;
218
+ for (const queuedRun of queuedRuns) {
219
+ if (!isRecord(queuedRun)) {
220
+ rejectInvalidScheduleRecord(scheduleId, "with invalid queuedRuns");
221
+ return null;
222
+ }
223
+ const { workflowId, queuedAt, occurrence } = queuedRun;
224
+ if (!isValidScheduleIdentifier(workflowId) || !isValidScheduleTimestamp(queuedAt) || occurrence !== void 0 && !isValidScheduleTimestamp(occurrence)) {
225
+ rejectInvalidScheduleRecord(scheduleId, "with invalid queuedRuns");
226
+ return null;
227
+ }
228
+ if (workflowIds.has(workflowId)) {
229
+ rejectInvalidScheduleRecord(scheduleId, "with duplicate queued workflow ids");
230
+ return null;
231
+ }
232
+ workflowIds.add(workflowId);
233
+ result.push({ workflowId, queuedAt, ...occurrence !== void 0 && { occurrence } });
234
+ }
235
+ return result;
214
236
  }
215
237
  function decodeScheduleMissedFireCount(decoded, scheduleId) {
216
238
  const missedFireCount = decoded.missedFireCount;
@@ -263,6 +285,8 @@ export function decodeScheduleRuntimeFields(decoded, scheduleId) {
263
285
  const queueFields = decodeScheduleQueueFields(decoded, scheduleId);
264
286
  if (queueFields === null)
265
287
  return null;
288
+ if (currentWorkflow.value !== void 0 && queueFields.queuedRuns.some((queuedRun) => queuedRun.workflowId === currentWorkflow.value))
289
+ return rejectInvalidScheduleRecord(scheduleId, "whose current workflow is also queued");
266
290
  return {
267
291
  ...optionFields,
268
292
  ...timestamps,
@@ -4,6 +4,10 @@ import { normalizeFailureCategory } from "../failure-categories.js";
4
4
  import { coerceStartWorkflowId, parseStartWorkflowDuration } from "../start-workflow-validation.js";
5
5
  import { DEFAULT_WORKFLOW_VERSION } from "../versioning.js";
6
6
  import { isWorkflowTagArray } from "../workflow-tags.js";
7
+ import {
8
+ MAX_TIMELINE_COORDINATOR_DETAILS,
9
+ MAX_TIMELINE_DETAIL_STRING_LENGTH
10
+ } from "./timeline-coordinator-constants.js";
7
11
  const WORKFLOW_TIMELINE_STATUSES = new Set([
8
12
  "running",
9
13
  "completed",
@@ -28,12 +32,15 @@ const WORKFLOW_STATE_FIELD_NAMES = new Set(defineCompleteWorkflowStateFieldNames
28
32
  "versionTuple",
29
33
  "workflowExecutionToken",
30
34
  "executionStateOwnerId",
35
+ "parentWorkflowId",
36
+ "parentWorkflowExecutionToken",
31
37
  "createdAt",
32
38
  "startedAt",
33
39
  "updatedAt",
34
40
  "terminalCleanupToken",
35
41
  "executionDeadline",
36
- "forkedFrom"
42
+ "forkedFrom",
43
+ "restartedFrom"
37
44
  ]));
38
45
  export function isSanitizedSearchAttributeValue(value) {
39
46
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
@@ -53,6 +60,30 @@ export function isFiniteNumber(value) {
53
60
  export function isTimelineStep(value) {
54
61
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
55
62
  }
63
+ const WORKFLOW_TIMELINE_DETAIL_OUTCOMES = new Set(["fulfilled", "rejected", "won", "lost"]);
64
+ function isBoundedTimelineDetailString(value) {
65
+ return typeof value === "string" && value.length <= MAX_TIMELINE_DETAIL_STRING_LENGTH;
66
+ }
67
+ const TIMELINE_DETAIL_FIELD_CHECKS = [
68
+ (detail) => typeof detail.index === "number" && Number.isSafeInteger(detail.index) && detail.index >= 0,
69
+ (detail) => detail.key === void 0 || isBoundedTimelineDetailString(detail.key),
70
+ (detail) => isBoundedTimelineDetailString(detail.operationId),
71
+ (detail) => isBoundedTimelineDetailString(detail.operationType),
72
+ (detail) => isBoundedTimelineDetailString(detail.operationLabel),
73
+ (detail) => WORKFLOW_TIMELINE_DETAIL_OUTCOMES.has(detail.outcome),
74
+ (detail) => detail.errorSummary === void 0 || isBoundedTimelineDetailString(detail.errorSummary)
75
+ ];
76
+ function isWorkflowTimelineOperationDetail(value) {
77
+ if (!isRecord(value))
78
+ return !1;
79
+ return TIMELINE_DETAIL_FIELD_CHECKS.every((check) => check(value));
80
+ }
81
+ function isWorkflowTimelineOperationDetails(value) {
82
+ return value === void 0 || Array.isArray(value) && value.length <= MAX_TIMELINE_COORDINATOR_DETAILS && value.every(isWorkflowTimelineOperationDetail);
83
+ }
84
+ function isOmittedTimelineDetailCount(value) {
85
+ return value === void 0 || typeof value === "number" && Number.isSafeInteger(value) && value > 0;
86
+ }
56
87
  const TIMELINE_ENTRY_FIELD_CHECKS = [
57
88
  (entry) => isTimelineStep(entry.step),
58
89
  (entry) => typeof entry.operationType === "string",
@@ -62,7 +93,12 @@ const TIMELINE_ENTRY_FIELD_CHECKS = [
62
93
  (entry) => WORKFLOW_TIMELINE_STATUSES.has(entry.status),
63
94
  (entry) => entry.outputSummary === void 0 || typeof entry.outputSummary === "string",
64
95
  (entry) => entry.duration === void 0 || isFiniteNumber(entry.duration),
65
- (entry) => entry.versionTuple === void 0 || isWorkflowVersionTuple(entry.versionTuple)
96
+ (entry) => entry.versionTuple === void 0 || isWorkflowVersionTuple(entry.versionTuple),
97
+ (entry) => isWorkflowTimelineOperationDetails(entry.branches),
98
+ (entry) => isOmittedTimelineDetailCount(entry.branchesOmitted),
99
+ (entry) => isWorkflowTimelineOperationDetails(entry.children),
100
+ (entry) => isOmittedTimelineDetailCount(entry.childrenOmitted),
101
+ (entry) => entry.speculationOutcome === void 0 || entry.speculationOutcome === "committed" || entry.speculationOutcome === "rolled-back"
66
102
  ];
67
103
  export function isWorkflowTimelineEntry(value) {
68
104
  if (!isRecord(value))
@@ -102,8 +138,36 @@ export function decodeWorkflowState(bytes) {
102
138
  console.warn(`[weft] Decoded workflow state for "${String(state.id)}" has an invalid executionStateOwnerId field; falling back to the workflow id as the execution owner. This usually indicates corruption or tampering of the storage record.`);
103
139
  delete state.executionStateOwnerId;
104
140
  }
141
+ sanitizeDecodedParentLineage(state);
142
+ sanitizeDecodedRestartLineage(state);
105
143
  return decodedRecord === void 0 ? state : stripUnknownWorkflowStateFields(state, decodedRecord);
106
144
  }
145
+ function sanitizeDecodedParentLineage(state) {
146
+ if (state.parentWorkflowId !== void 0)
147
+ try {
148
+ coerceStartWorkflowId(state.parentWorkflowId, "parentWorkflowId");
149
+ } catch {
150
+ delete state.parentWorkflowId;
151
+ }
152
+ if (typeof state.parentWorkflowExecutionToken !== "string" || state.parentWorkflowExecutionToken.length === 0 || state.parentWorkflowId === void 0)
153
+ delete state.parentWorkflowExecutionToken;
154
+ }
155
+ function sanitizeDecodedRestartLineage(state) {
156
+ const restartedFrom = state.restartedFrom;
157
+ if (!isRecord(restartedFrom)) {
158
+ delete state.restartedFrom;
159
+ return;
160
+ }
161
+ try {
162
+ coerceStartWorkflowId(restartedFrom.workflowId, "restartedFrom.workflowId");
163
+ } catch {
164
+ delete state.restartedFrom;
165
+ return;
166
+ }
167
+ const { workflowExecutionToken, replacedAt } = restartedFrom;
168
+ if (workflowExecutionToken !== void 0 && (typeof workflowExecutionToken !== "string" || workflowExecutionToken.length === 0) || typeof replacedAt !== "number" || !Number.isSafeInteger(replacedAt) || replacedAt < 0)
169
+ delete state.restartedFrom;
170
+ }
107
171
  function stripUnknownWorkflowStateFields(state, stateFields) {
108
172
  for (const fieldName of Object.keys(stateFields))
109
173
  if (!WORKFLOW_STATE_FIELD_NAMES.has(fieldName))
@@ -2,8 +2,12 @@ import type { AttributeFilter, ListFilter, WorkflowState } from '../types.ts';
2
2
  import type { EngineInternals } from './internals.ts';
3
3
  /** Stream decoded workflow states that match a list filter. */
4
4
  export declare function streamMatchingWorkflowStates(internals: EngineInternals, filter?: ListFilter): AsyncGenerator<WorkflowState>;
5
- /** Resolve the indexed workflow IDs implied by tag and search-attribute filters. */
5
+ /** Resolve indexed workflow IDs implied by lineage, schedule, tag, and search-attribute filters. */
6
6
  export declare function resolveConstrainedIds(internals: EngineInternals, filter: ListFilter | undefined, normalizedTagFilters: readonly string[] | undefined): Promise<Set<string> | null>;
7
7
  /** Query a single search-attribute index filter and return matching workflow IDs. */
8
8
  export declare function queryAttributeIndex(internals: EngineInternals, filter: AttributeFilter): Promise<Set<string>>;
9
+ /** Resolve the durable reverse index from one concrete parent run to its direct children. */
10
+ export declare function queryChildWorkflowIndex(internals: EngineInternals, parentWorkflowId: string, parentWorkflowExecutionToken: string | undefined): Promise<Set<string>>;
11
+ /** Resolve the durable reverse index from a schedule id to launched workflow ids. */
12
+ export declare function queryScheduleRunIndex(internals: EngineInternals, scheduleId: string): Promise<Set<string>>;
9
13
  export declare function isTopLevelWorkflowStateKey(key: string): boolean;
@@ -15,6 +15,7 @@ import {
15
15
  matchesListFilter
16
16
  } from "./state-utilities.js";
17
17
  import { decodeWorkflowState } from "./validation.js";
18
+ import { MAX_LIST_SCAN_ROWS, WorkflowListScanCapExceededError } from "./workflow-indexes.js";
18
19
  const ATTRIBUTE_SCAN_CONCURRENCY = 8;
19
20
  export async function* streamMatchingWorkflowStates(internals, filter) {
20
21
  const normalizedTagFilters = normalizeWorkflowTags(filter?.tags), constrainedIds = await resolveConstrainedIds(internals, filter, normalizedTagFilters);
@@ -54,10 +55,9 @@ async function readSearchAttributesForFilter(internals, workflowId, filter) {
54
55
  return decodeSearchAttributeRecord(await internals.storage.get(KEYS.attribute(workflowId)));
55
56
  }
56
57
  export async function resolveConstrainedIds(internals, filter, normalizedTagFilters) {
57
- const attributeFilters = filter?.attributes, hasAttributeFilters = attributeFilters !== void 0 && attributeFilters.length > 0, hasTagFilters = normalizedTagFilters !== void 0 && normalizedTagFilters.length > 0;
58
- if (!hasAttributeFilters && !hasTagFilters)
58
+ const attributeFilters = filter?.attributes, queries = buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters, filter?.scheduleId, filter?.parentWorkflowId, filter?.parentWorkflowExecutionToken);
59
+ if (queries.length === 0)
59
60
  return null;
60
- const queries = buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters);
61
61
  return intersectIdentifierSets(await runConstrainedIdQueries(queries));
62
62
  }
63
63
  export async function queryAttributeIndex(internals, filter) {
@@ -68,29 +68,52 @@ export async function queryAttributeIndex(internals, filter) {
68
68
  await collectRangeAttributeMatches(internals, filter, prefix, ids);
69
69
  return ids;
70
70
  }
71
- function buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters) {
71
+ function buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters, scheduleId, parentWorkflowId, parentWorkflowExecutionToken) {
72
72
  return [
73
+ ...scheduleId === void 0 ? [] : [() => queryScheduleRunIndex(internals, scheduleId)],
74
+ ...parentWorkflowId === void 0 ? [] : [() => queryChildWorkflowIndex(internals, parentWorkflowId, parentWorkflowExecutionToken)],
73
75
  ...normalizedTagFilters?.map((tag) => () => queryTagIndex(internals, tag)) ?? [],
74
76
  ...attributeFilters?.map((attributeFilter) => () => queryAttributeIndex(internals, attributeFilter)) ?? []
75
77
  ];
76
78
  }
79
+ export async function queryChildWorkflowIndex(internals, parentWorkflowId, parentWorkflowExecutionToken) {
80
+ const workflowIds = new Set, prefix = KEYS.childWorkflowByParentPrefix(parentWorkflowId, parentWorkflowExecutionToken);
81
+ for await (const [key] of internals.storage.scan(prefix)) {
82
+ if (workflowIds.size >= MAX_LIST_SCAN_ROWS)
83
+ throw new WorkflowListScanCapExceededError(MAX_LIST_SCAN_ROWS);
84
+ const encodedWorkflowId = key.slice(prefix.length).split(":").at(-1);
85
+ if (encodedWorkflowId === void 0)
86
+ continue;
87
+ const workflowId = tryDecodeStorageKeyComponent(encodedWorkflowId);
88
+ if (workflowId !== null)
89
+ workflowIds.add(workflowId);
90
+ }
91
+ return workflowIds;
92
+ }
93
+ export async function queryScheduleRunIndex(internals, scheduleId) {
94
+ const workflowIds = new Set, prefix = KEYS.scheduleRunBySchedulePrefix(scheduleId);
95
+ for await (const [key] of internals.storage.scan(prefix)) {
96
+ if (workflowIds.size >= MAX_LIST_SCAN_ROWS)
97
+ throw new WorkflowListScanCapExceededError(MAX_LIST_SCAN_ROWS);
98
+ const encodedWorkflowId = key.slice(prefix.length), workflowId = tryDecodeStorageKeyComponent(encodedWorkflowId);
99
+ if (workflowId !== null)
100
+ workflowIds.add(workflowId);
101
+ }
102
+ return workflowIds;
103
+ }
77
104
  async function runConstrainedIdQueries(queries) {
78
- const idSets = Array.from({ length: queries.length });
79
105
  let nextIndex = 0;
80
106
  const runWorker = async () => {
107
+ const workerResults = [];
81
108
  while (nextIndex < queries.length) {
82
109
  const currentIndex = nextIndex;
83
110
  nextIndex += 1;
84
- idSets[currentIndex] = await queries[currentIndex]();
111
+ workerResults.push([currentIndex, await queries[currentIndex]()]);
85
112
  }
86
- }, workerLimit = Math.max(1, Math.min(ATTRIBUTE_SCAN_CONCURRENCY, queries.length));
87
- await Promise.all(Array.from({ length: workerLimit }, () => runWorker()));
88
- return idSets.map(requireConstrainedIdSet);
89
- }
90
- function requireConstrainedIdSet(idSet) {
91
- if (idSet === void 0)
92
- throw Error("Attribute index query did not produce a workflow ID set.");
93
- return idSet;
113
+ return workerResults;
114
+ }, workerLimit = Math.max(1, Math.min(ATTRIBUTE_SCAN_CONCURRENCY, queries.length)), completedResults = (await Promise.all(Array.from({ length: workerLimit }, () => runWorker()))).flat();
115
+ completedResults.sort(([leftIndex], [rightIndex]) => leftIndex - rightIndex);
116
+ return completedResults.map(([, idSet]) => idSet);
94
117
  }
95
118
  async function collectExactAttributeMatches(internals, filter, attributeName, ids) {
96
119
  const exactValues = attributeFilterExactValues(filter);
@@ -38,10 +38,9 @@ export declare class ScheduleFiredEvent extends Event {
38
38
  /** Wall-clock time the run was launched, from the engine's injected clock. */
39
39
  readonly firedAt: number;
40
40
  /**
41
- * The scheduled grid timestamp the occurrence was due. `undefined` for a run
42
- * that drained from the `queue` overlap policy: a queued occurrence is tracked
43
- * only as a count (`queuedRuns`), so its original due timestamp is not retained
44
- * and cannot be reported when the run finally launches.
41
+ * The scheduled grid timestamp the occurrence was due. Queue overlap retains
42
+ * this timestamp on the durable queue entry and reports it when the run drains.
43
+ * It is undefined only when an internal caller did not supply an occurrence.
45
44
  */
46
45
  readonly occurrence: number | undefined;
47
46
  constructor(scheduleId: string, workflowId: string, firedAt: number, occurrence?: number);
@@ -241,7 +241,8 @@ export declare class WorkflowRecoverySkippedEvent extends Event {
241
241
  * dead-letter horizon is reached. Per-attempt observability — fires on every retry.
242
242
  * - `'dead-lettered'`: the retry horizon was reached without success; the external
243
243
  * resource may be leaked. The durable record is `KEYS.teardownDeadLetter`, which
244
- * survives purge so the leak is auditable after the workflow record is gone.
244
+ * survives purge so `getFinalizerStatus()` remains auditable after the workflow
245
+ * record is gone.
245
246
  *
246
247
  * @example
247
248
  * ```ts
@@ -20,6 +20,9 @@ import { WeftError } from './weft-error.ts';
20
20
  export declare const listFilterObjectSchema: z.ZodObject<{
21
21
  status: z.ZodOptional<z.ZodUnion<readonly [z.ZodType<WorkflowStatus, unknown, z.core.$ZodTypeInternals<WorkflowStatus, unknown>>, z.ZodArray<z.ZodType<WorkflowStatus, unknown, z.core.$ZodTypeInternals<WorkflowStatus, unknown>>>]>>;
22
22
  type: z.ZodOptional<z.ZodString>;
23
+ scheduleId: z.ZodOptional<z.ZodString>;
24
+ parentWorkflowId: z.ZodOptional<z.ZodString>;
25
+ parentWorkflowExecutionToken: z.ZodOptional<z.ZodString>;
23
26
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
27
  attributes: z.ZodOptional<z.ZodArray<z.ZodObject<{
25
28
  key: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
@@ -35,6 +35,9 @@ const WORKFLOW_STATUSES = [
35
35
  export const listFilterObjectSchema = z.object({
36
36
  status: z.union([workflowStatusSchema, z.array(workflowStatusSchema)]).optional(),
37
37
  type: z.string().min(1).optional(),
38
+ scheduleId: z.string().min(1).optional(),
39
+ parentWorkflowId: z.string().min(1).optional(),
40
+ parentWorkflowExecutionToken: z.string().min(1).optional(),
38
41
  tags: z.array(z.string()).optional(),
39
42
  attributes: z.array(attributeFilterSchema).optional(),
40
43
  limit: z.number().int().min(1).max(1000).optional(),
@@ -70,6 +73,14 @@ export function normalizeListFilter(input) {
70
73
  const result = listFilterObjectSchema.safeParse(input ?? {});
71
74
  if (!result.success)
72
75
  throw new ListFilterValidationError(result.error.issues.map(flattenIssue));
76
+ if (result.data.parentWorkflowExecutionToken !== void 0 && result.data.parentWorkflowId === void 0)
77
+ throw new ListFilterValidationError([
78
+ {
79
+ path: ["parentWorkflowExecutionToken"],
80
+ message: "parentWorkflowExecutionToken requires parentWorkflowId",
81
+ code: "custom"
82
+ }
83
+ ]);
73
84
  return result.data;
74
85
  }
75
86
 
@@ -1,3 +1,25 @@
1
+ /**
2
+ * Pure builder that captures a snapshot of the engine's locally-registered
3
+ * workflows and activities, with their JSON Schemas. This is the data source
4
+ * behind the `GET /v1/registry` REST endpoint and (later) the MCP server.
5
+ *
6
+ * The output is a plain object designed to be safe for JSON serialization.
7
+ * Absent metadata fields are omitted (never `null`, never `{}`); converter
8
+ * exceptions surface as a typed {@link RegistrySchemaConversionError} (see
9
+ * its JSDoc for what reaches the wire vs server logs).
10
+ *
11
+ * **Ordering guarantee.** Builder code inserts workflow, activity, signal,
12
+ * update, and query keys in alphabetical (codepoint) order. Workflow and
13
+ * activity names cannot be integer-like (the name grammar requires a leading
14
+ * letter or underscore), but signal/update/query names accept any string, so
15
+ * integer-like message names could theoretically be reordered by JS engines
16
+ * despite the explicit sort. Clients that want to protect themselves from
17
+ * future registry sources should still sort `Object.keys(...)` before
18
+ * presenting or diffing snapshot entries.
19
+ *
20
+ * @module core/registry-snapshot
21
+ */
22
+ import type { ActivityMetadata } from './activity-registry.ts';
1
23
  import type { Engine } from './engine.ts';
2
24
  import { WeftError } from './weft-error.ts';
3
25
  /**
@@ -6,12 +28,20 @@ import { WeftError } from './weft-error.ts';
6
28
  * versions with a clear upgrade message.
7
29
  */
8
30
  export declare const REGISTRY_VERSION = 1;
31
+ /** Schema metadata reported for a statically registered workflow message. */
32
+ export type RegistryMessageEntry = {
33
+ inputSchema?: Record<string, unknown>;
34
+ outputSchema?: Record<string, unknown>;
35
+ };
9
36
  /** Metadata reported per workflow in a registry snapshot. */
10
37
  export type RegistryWorkflowEntry = {
11
38
  inputSchema?: Record<string, unknown>;
12
39
  outputSchema?: Record<string, unknown>;
13
40
  description?: string;
14
41
  tags?: ReadonlyArray<string>;
42
+ signals?: Record<string, RegistryMessageEntry>;
43
+ updates?: Record<string, RegistryMessageEntry>;
44
+ queries?: Record<string, RegistryMessageEntry>;
15
45
  };
16
46
  /**
17
47
  * Metadata reported per activity in a registry snapshot.
@@ -31,6 +61,8 @@ export type RegistryActivityEntry = {
31
61
  /** Engine assigns a default queue when none is specified, so this is always populated. */
32
62
  queue: string;
33
63
  description?: string;
64
+ retry?: ActivityMetadata['retry'];
65
+ timeout?: ActivityMetadata['timeout'];
34
66
  };
35
67
  /**
36
68
  * Snapshot of every locally-registered workflow and activity, suitable for