@lostgradient/weft 0.23.1 → 0.24.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 (294) hide show
  1. package/README.md +72 -4
  2. package/dist/cli/codegen-emit-dedup.d.ts +81 -0
  3. package/dist/cli/codegen-emit-dedup.js +45 -0
  4. package/dist/cli/codegen-emit-registry.d.ts +90 -0
  5. package/dist/cli/codegen-emit-registry.js +50 -0
  6. package/dist/cli/codegen-emit.d.ts +26 -39
  7. package/dist/cli/codegen-emit.js +4 -27
  8. package/dist/cli/codegen-validate.d.ts +49 -0
  9. package/dist/cli/codegen-validate.js +186 -0
  10. package/dist/cli/codegen.js +6 -87
  11. package/dist/cli/generated/operation-catalog.snapshot.json +287 -3
  12. package/dist/cli/generated/operation-client.generated.d.ts +324 -10
  13. package/dist/cli/generated/operation-client.generated.js +12 -0
  14. package/dist/cli/output.js +1 -1
  15. package/dist/cli/storage-factory.js +2 -0
  16. package/dist/cli/workflow-commands.js +2 -0
  17. package/dist/cli-main.js +1 -1
  18. package/dist/core/application-payload-digest.d.ts +42 -0
  19. package/dist/core/application-payload-digest.js +76 -0
  20. package/dist/core/application-primitive-abort.d.ts +50 -0
  21. package/dist/core/application-primitive-abort.js +40 -0
  22. package/dist/core/application-primitive-attempt-registry.d.ts +74 -0
  23. package/dist/core/application-primitive-attempt-registry.js +73 -0
  24. package/dist/core/application-primitive-codec.d.ts +36 -0
  25. package/dist/core/application-primitive-codec.js +56 -0
  26. package/dist/core/application-primitive-commit.d.ts +67 -0
  27. package/dist/core/application-primitive-commit.js +66 -0
  28. package/dist/core/application-primitive-guards.d.ts +62 -0
  29. package/dist/core/application-primitive-guards.js +89 -0
  30. package/dist/core/application-primitive-payload.d.ts +29 -0
  31. package/dist/core/application-primitive-payload.js +65 -0
  32. package/dist/core/application-primitive-timing.d.ts +18 -0
  33. package/dist/core/application-primitive-timing.js +22 -0
  34. package/dist/core/atomic-state.js +2 -0
  35. package/dist/core/catalog/codec.d.ts +40 -0
  36. package/dist/core/catalog/codec.js +45 -0
  37. package/dist/core/catalog/errors.d.ts +104 -0
  38. package/dist/core/catalog/errors.js +41 -0
  39. package/dist/core/catalog/index.d.ts +25 -0
  40. package/dist/core/catalog/index.js +15 -0
  41. package/dist/core/catalog/reference-counts.d.ts +105 -0
  42. package/dist/core/catalog/reference-counts.js +28 -0
  43. package/dist/core/catalog/removal.d.ts +52 -0
  44. package/dist/core/catalog/removal.js +14 -0
  45. package/dist/core/catalog/storage-io.d.ts +78 -0
  46. package/dist/core/catalog/storage-io.js +106 -0
  47. package/dist/core/catalog/types.d.ts +108 -0
  48. package/dist/core/catalog/types.js +0 -0
  49. package/dist/core/catalog/workflow-catalog.d.ts +165 -0
  50. package/dist/core/catalog/workflow-catalog.js +198 -0
  51. package/dist/core/compare-codepoint.d.ts +13 -0
  52. package/dist/core/compare-codepoint.js +7 -0
  53. package/dist/core/compression.js +2 -0
  54. package/dist/core/context/attributes.d.ts +1 -1
  55. package/dist/core/context/durable-operations.d.ts +2 -2
  56. package/dist/core/context/index.js +5 -3
  57. package/dist/core/context/state-namespace.d.ts +1 -1
  58. package/dist/core/contract/build.d.ts +64 -0
  59. package/dist/core/contract/build.js +99 -0
  60. package/dist/core/contract/compatibility.d.ts +187 -0
  61. package/dist/core/contract/compatibility.js +37 -0
  62. package/dist/core/contract/failure.d.ts +64 -0
  63. package/dist/core/contract/failure.js +4 -0
  64. package/dist/core/contract/hash.d.ts +92 -0
  65. package/dist/core/contract/hash.js +34 -0
  66. package/dist/core/contract/index.d.ts +21 -0
  67. package/dist/core/contract/index.js +22 -0
  68. package/dist/core/contract/limits.d.ts +74 -0
  69. package/dist/core/contract/limits.js +1 -0
  70. package/dist/core/contract/manifest-parse-schema.d.ts +37 -0
  71. package/dist/core/contract/manifest-parse-schema.js +106 -0
  72. package/dist/core/contract/manifest-parse.d.ts +76 -0
  73. package/dist/core/contract/manifest-parse.js +189 -0
  74. package/dist/core/contract/manifest.d.ts +56 -0
  75. package/dist/core/contract/manifest.js +31 -0
  76. package/dist/core/contract/normalize.d.ts +97 -0
  77. package/dist/core/contract/normalize.js +127 -0
  78. package/dist/core/contract/revision.d.ts +29 -0
  79. package/dist/core/contract/revision.js +5 -0
  80. package/dist/core/contract/types.d.ts +274 -0
  81. package/dist/core/contract/types.js +1 -0
  82. package/dist/core/engine/catalog-activation.d.ts +40 -0
  83. package/dist/core/engine/catalog-activation.js +17 -0
  84. package/dist/core/engine/catalog-events.d.ts +36 -0
  85. package/dist/core/engine/catalog-events.js +15 -0
  86. package/dist/core/engine/catalog-readiness.d.ts +49 -0
  87. package/dist/core/engine/catalog-readiness.js +59 -0
  88. package/dist/core/engine/catalog-removal.d.ts +132 -0
  89. package/dist/core/engine/catalog-removal.js +70 -0
  90. package/dist/core/engine/construction.d.ts +1 -1
  91. package/dist/core/engine/construction.js +4 -2
  92. package/dist/core/engine/disposal.js +9 -0
  93. package/dist/core/engine/engine-workflows-namespace.d.ts +130 -0
  94. package/dist/core/engine/engine-workflows-namespace.js +49 -0
  95. package/dist/core/engine/finalizer-metadata.d.ts +22 -0
  96. package/dist/core/engine/finalizer-metadata.js +12 -0
  97. package/dist/core/engine/handle-result.js +2 -0
  98. package/dist/core/engine/index.d.ts +83 -0
  99. package/dist/core/engine/index.js +61 -0
  100. package/dist/core/engine/internals.d.ts +44 -1
  101. package/dist/core/engine/internals.js +6 -0
  102. package/dist/core/engine/lifecycle/start.js +3 -0
  103. package/dist/core/engine/registration.d.ts +76 -0
  104. package/dist/core/engine/registration.js +12 -6
  105. package/dist/core/engine/source-registration.d.ts +42 -0
  106. package/dist/core/engine/source-registration.js +36 -0
  107. package/dist/core/engine/source-resolution.d.ts +85 -0
  108. package/dist/core/engine/source-resolution.js +119 -0
  109. package/dist/core/engine/validation.js +2 -2
  110. package/dist/core/events/catalog-events.d.ts +117 -0
  111. package/dist/core/events/catalog-events.js +62 -0
  112. package/dist/core/events/event-map.d.ts +6 -0
  113. package/dist/core/events/index.d.ts +1 -0
  114. package/dist/core/events/index.js +1 -0
  115. package/dist/core/mailbox-admission.d.ts +26 -0
  116. package/dist/core/mailbox-admission.js +130 -0
  117. package/dist/core/mailbox-claims.d.ts +251 -0
  118. package/dist/core/mailbox-claims.js +0 -0
  119. package/dist/core/mailbox-codec.d.ts +32 -0
  120. package/dist/core/mailbox-codec.js +221 -0
  121. package/dist/core/mailbox-contract.d.ts +290 -0
  122. package/dist/core/mailbox-contract.js +0 -0
  123. package/dist/core/mailbox-delivery.d.ts +37 -0
  124. package/dist/core/mailbox-delivery.js +193 -0
  125. package/dist/core/mailbox-guards.d.ts +78 -0
  126. package/dist/core/mailbox-guards.js +58 -0
  127. package/dist/core/mailbox-index-codec.d.ts +41 -0
  128. package/dist/core/mailbox-index-codec.js +71 -0
  129. package/dist/core/mailbox-internals.d.ts +156 -0
  130. package/dist/core/mailbox-internals.js +154 -0
  131. package/dist/core/mailbox-maintenance.d.ts +29 -0
  132. package/dist/core/mailbox-maintenance.js +196 -0
  133. package/dist/core/mailbox-settlement.d.ts +69 -0
  134. package/dist/core/mailbox-settlement.js +206 -0
  135. package/dist/core/mailbox-storage.d.ts +117 -0
  136. package/dist/core/mailbox-storage.js +115 -0
  137. package/dist/core/mailbox-transition-helpers.d.ts +70 -0
  138. package/dist/core/mailbox-transition-helpers.js +43 -0
  139. package/dist/core/mailbox-transitions-recovery.d.ts +28 -0
  140. package/dist/core/mailbox-transitions-recovery.js +64 -0
  141. package/dist/core/mailbox-transitions.d.ts +138 -0
  142. package/dist/core/mailbox-transitions.js +180 -0
  143. package/dist/core/mailbox-types.d.ts +406 -0
  144. package/dist/core/mailbox-types.js +16 -0
  145. package/dist/core/mailbox-validation.d.ts +65 -0
  146. package/dist/core/mailbox-validation.js +73 -0
  147. package/dist/core/mailbox-waits.d.ts +50 -0
  148. package/dist/core/mailbox-waits.js +97 -0
  149. package/dist/core/mailbox.d.ts +163 -0
  150. package/dist/core/mailbox.js +211 -0
  151. package/dist/core/outbox-claims.d.ts +307 -0
  152. package/dist/core/outbox-claims.js +0 -0
  153. package/dist/core/outbox-codec.d.ts +24 -0
  154. package/dist/core/outbox-codec.js +257 -0
  155. package/dist/core/outbox-contract.d.ts +346 -0
  156. package/dist/core/outbox-contract.js +0 -0
  157. package/dist/core/outbox-delivery.d.ts +34 -0
  158. package/dist/core/outbox-delivery.js +149 -0
  159. package/dist/core/outbox-drain.d.ts +29 -0
  160. package/dist/core/outbox-drain.js +158 -0
  161. package/dist/core/outbox-enqueue.d.ts +24 -0
  162. package/dist/core/outbox-enqueue.js +115 -0
  163. package/dist/core/outbox-guards.d.ts +76 -0
  164. package/dist/core/outbox-guards.js +60 -0
  165. package/dist/core/outbox-index-codec.d.ts +33 -0
  166. package/dist/core/outbox-index-codec.js +71 -0
  167. package/dist/core/outbox-internals.d.ts +99 -0
  168. package/dist/core/outbox-internals.js +159 -0
  169. package/dist/core/outbox-listing.d.ts +12 -0
  170. package/dist/core/outbox-listing.js +38 -0
  171. package/dist/core/outbox-maintenance.d.ts +28 -0
  172. package/dist/core/outbox-maintenance.js +191 -0
  173. package/dist/core/outbox-operations.d.ts +18 -0
  174. package/dist/core/outbox-operations.js +46 -0
  175. package/dist/core/outbox-runner.d.ts +30 -0
  176. package/dist/core/outbox-runner.js +217 -0
  177. package/dist/core/outbox-settlement.d.ts +57 -0
  178. package/dist/core/outbox-settlement.js +183 -0
  179. package/dist/core/outbox-storage.d.ts +107 -0
  180. package/dist/core/outbox-storage.js +127 -0
  181. package/dist/core/outbox-transition-helpers.d.ts +83 -0
  182. package/dist/core/outbox-transition-helpers.js +63 -0
  183. package/dist/core/outbox-transitions-recovery.d.ts +30 -0
  184. package/dist/core/outbox-transitions-recovery.js +42 -0
  185. package/dist/core/outbox-transitions.d.ts +145 -0
  186. package/dist/core/outbox-transitions.js +236 -0
  187. package/dist/core/outbox-types.d.ts +400 -0
  188. package/dist/core/outbox-types.js +20 -0
  189. package/dist/core/outbox-validation.d.ts +101 -0
  190. package/dist/core/outbox-validation.js +127 -0
  191. package/dist/core/outbox-waits.d.ts +39 -0
  192. package/dist/core/outbox-waits.js +88 -0
  193. package/dist/core/outbox.d.ts +156 -0
  194. package/dist/core/outbox.js +234 -0
  195. package/dist/core/registry-limits.d.ts +49 -0
  196. package/dist/core/registry-limits.js +10 -0
  197. package/dist/core/registry-schema-conversion.d.ts +20 -0
  198. package/dist/core/registry-schema-conversion.js +22 -0
  199. package/dist/core/registry-snapshot.d.ts +91 -42
  200. package/dist/core/registry-snapshot.js +39 -77
  201. package/dist/core/registry-workflow-contract-draft.d.ts +32 -0
  202. package/dist/core/registry-workflow-contract-draft.js +28 -0
  203. package/dist/core/registry-workflow-manifest.d.ts +122 -0
  204. package/dist/core/registry-workflow-manifest.js +92 -0
  205. package/dist/core/source/errors.d.ts +55 -0
  206. package/dist/core/source/errors.js +13 -0
  207. package/dist/core/source/index.d.ts +19 -0
  208. package/dist/core/source/index.js +4 -0
  209. package/dist/core/source/resolvers.d.ts +45 -0
  210. package/dist/core/source/resolvers.js +9 -0
  211. package/dist/core/source/types.d.ts +211 -0
  212. package/dist/core/source/types.js +0 -0
  213. package/dist/core/source/validate.d.ts +91 -0
  214. package/dist/core/source/validate.js +62 -0
  215. package/dist/core/source/workflow-source.d.ts +43 -0
  216. package/dist/core/source/workflow-source.js +16 -0
  217. package/dist/core/types/workflow-registries.d.ts +7 -0
  218. package/dist/core/types/workflow-registry.d.ts +12 -0
  219. package/dist/core/versioning.js +2 -0
  220. package/dist/core/weft-error.d.ts +1 -1
  221. package/dist/core/weft-error.js +9 -1
  222. package/dist/http.js +2 -2
  223. package/dist/index.d.ts +23 -2
  224. package/dist/index.js +50 -0
  225. package/dist/indexeddb.js +1 -1
  226. package/dist/json-schema.js +3 -3
  227. package/dist/mcp/resources.js +2 -0
  228. package/dist/runtime/portable.d.ts +9 -1
  229. package/dist/server/fault-to-json-rpc.js +3 -1
  230. package/dist/server/handler/auth-context-principal.js +4 -0
  231. package/dist/server/json-rpc-http.js +2 -0
  232. package/dist/server/json-rpc-websocket.js +6 -4
  233. package/dist/server/operation-catalog/index.d.ts +1 -1
  234. package/dist/server/operation-catalog/pipeline-helpers.js +2 -0
  235. package/dist/server/operation-catalog/types.d.ts +17 -0
  236. package/dist/server/operation-fault.d.ts +3 -0
  237. package/dist/server/operation-fault.js +3 -1
  238. package/dist/server/operations/activate-workflow-revision.d.ts +30 -0
  239. package/dist/server/operations/activate-workflow-revision.js +69 -0
  240. package/dist/server/operations/fleet-events-subscription.js +1 -1
  241. package/dist/server/operations/fork-workflow.js +1 -1
  242. package/dist/server/operations/get-active-workflow-revision.d.ts +32 -0
  243. package/dist/server/operations/get-active-workflow-revision.js +46 -0
  244. package/dist/server/operations/get-catalog-diagnostics.d.ts +85 -0
  245. package/dist/server/operations/get-catalog-diagnostics.js +57 -0
  246. package/dist/server/operations/get-registry.js +17 -4
  247. package/dist/server/operations/get-workflow-revision.d.ts +25 -0
  248. package/dist/server/operations/get-workflow-revision.js +50 -0
  249. package/dist/server/operations/install-workflow-revision.d.ts +27 -0
  250. package/dist/server/operations/install-workflow-revision.js +46 -0
  251. package/dist/server/operations/list-workflow-revisions.d.ts +20 -0
  252. package/dist/server/operations/list-workflow-revisions.js +40 -0
  253. package/dist/server/operations/start-or-signal-workflow.js +1 -1
  254. package/dist/server/operations/start-workflow.js +1 -1
  255. package/dist/server/operations/static-registrations.js +36 -0
  256. package/dist/server/operations/storage.js +2 -2
  257. package/dist/server/operations/submit-review-decision.js +1 -1
  258. package/dist/server/operations/workflow-catalog-operation-helpers.d.ts +59 -0
  259. package/dist/server/operations/workflow-catalog-operation-helpers.js +135 -0
  260. package/dist/server/runtime/websocket-upgrade.js +2 -0
  261. package/dist/storage/bun-sql.js +49 -6
  262. package/dist/storage/catalog-keys.d.ts +45 -0
  263. package/dist/storage/catalog-keys.js +6 -0
  264. package/dist/storage/compressed-storage.js +1 -1
  265. package/dist/storage/derived-operations.js +1 -1
  266. package/dist/storage/index.d.ts +26 -1
  267. package/dist/storage/interface.d.ts +26 -1
  268. package/dist/storage/interface.js +1 -1
  269. package/dist/storage/key-encoding.d.ts +15 -0
  270. package/dist/storage/key-encoding.js +3 -0
  271. package/dist/storage/key-prefixes.d.ts +1 -1
  272. package/dist/storage/key-prefixes.js +15 -0
  273. package/dist/storage/lmdb.d.ts +26 -1
  274. package/dist/storage/lmdb.js +1 -1
  275. package/dist/storage/mailbox-keys.d.ts +80 -0
  276. package/dist/storage/mailbox-keys.js +14 -0
  277. package/dist/storage/memory.js +1 -1
  278. package/dist/storage/neon.js +2 -2
  279. package/dist/storage/node-sqlite-loader.d.ts +1 -1
  280. package/dist/storage/node-sqlite.js +49 -6
  281. package/dist/storage/outbox-keys.d.ts +77 -0
  282. package/dist/storage/outbox-keys.js +14 -0
  283. package/dist/storage/postgres.js +2 -2
  284. package/dist/storage/resolve.js +1 -1
  285. package/dist/storage/scoped-storage.js +1 -1
  286. package/dist/storage/storage-configuration.d.ts +6 -0
  287. package/dist/storage/testing.js +1 -1
  288. package/dist/storage/turso.js +2 -2
  289. package/dist/version.d.ts +1 -1
  290. package/dist/version.js +1 -1
  291. package/dist/web-extension.js +1 -1
  292. package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
  293. package/dist/worker/manifest/registry-contract-builder.js +40 -31
  294. package/package.json +1 -1
@@ -0,0 +1,7 @@
1
+ export function compareCodepoint(a, b) {
2
+ if (a < b)
3
+ return -1;
4
+ if (a > b)
5
+ return 1;
6
+ return 0;
7
+ }
@@ -38,6 +38,8 @@ export function createCompressor(algorithm) {
38
38
  return data;
39
39
  }
40
40
  };
41
+ default:
42
+ throw Error(`Unknown compression algorithm: ${JSON.stringify(algorithm)}`);
41
43
  }
42
44
  }
43
45
  export async function compressPayload(data, compressor, threshold) {
@@ -3,5 +3,5 @@ import type { ContextInternals } from './internals.ts';
3
3
  export declare function validateAttribute(internals: ContextInternals, key: string, value: SearchAttributeValue): void;
4
4
  export declare function setAttribute(internals: ContextInternals, key: string, value: SearchAttributeValue): void;
5
5
  export declare function setAttributes(internals: ContextInternals, attributes: Record<string, SearchAttributeValue>): void;
6
- export declare function getAttribute<T extends SearchAttributeValue = SearchAttributeValue>(internals: ContextInternals, key: string): T | undefined;
6
+ export declare function getAttribute(internals: ContextInternals, key: string): SearchAttributeValue | undefined;
7
7
  export declare function getAttributes(internals: ContextInternals): Readonly<Record<string, SearchAttributeValue>>;
@@ -43,8 +43,8 @@ export declare function readOrInitConditionDeadline(internals: ContextInternals,
43
43
  */
44
44
  export declare function waitUntil(context: Context, internals: ContextInternals, predicate: () => boolean, timeout?: Duration): Generator<ContextOperationRequest, boolean | void, unknown>;
45
45
  export declare function getVersion(_context: Context, internals: ContextInternals, changeId: string, minSupported: number, maxSupported: number): Generator<ContextOperationRequest, number, unknown>;
46
- export declare function waitForUpdate<T = unknown>(context: Context, internals: ContextInternals, name: string): Generator<ContextOperationRequest, {
47
- payload: T;
46
+ export declare function waitForUpdate(context: Context, internals: ContextInternals, name: string): Generator<ContextOperationRequest, {
47
+ payload: unknown;
48
48
  respond: (result: unknown) => void;
49
49
  }, unknown>;
50
50
  export declare function review(context: Context, internals: ContextInternals, options: HumanReviewOptions): Generator<ContextOperationRequest, HumanReviewResult, unknown>;
@@ -114,9 +114,11 @@ export class Context {
114
114
  if (prepared.cached)
115
115
  return;
116
116
  const execute = () => durableOperations.completePreparedSleepOperation(this, prepared);
117
- if (!internals.workflowInterceptor)
118
- return yield* execute();
119
- return yield* internals.workflowInterceptor.sleep({
117
+ if (!internals.workflowInterceptor) {
118
+ yield* execute();
119
+ return;
120
+ }
121
+ yield* internals.workflowInterceptor.sleep({
120
122
  workflowId: this.workflowId,
121
123
  duration: prepared.milliseconds,
122
124
  headers: new Map
@@ -6,7 +6,7 @@ import type { ContextOperationRequest } from './operation-request.ts';
6
6
  interface WorkflowAtomicStateOperationCache {
7
7
  nextStep(): number;
8
8
  has(step: number): boolean;
9
- get<TResult>(step: number): TResult;
9
+ get(step: number): unknown;
10
10
  set(step: number, value: unknown): void;
11
11
  }
12
12
  export declare function createStateNamespace(context: Context, internals: ContextInternals): WorkflowStateNamespace;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Convert an authoring-time workflow definition into a {@link WorkflowContract}.
3
+ *
4
+ * Every `DefinitionSchema` (workflow input/output, every signal/update/query,
5
+ * every activity, the finalizer) is converted through the existing
6
+ * `definitionSchemaToJsonSchema()` — this module does not reimplement schema
7
+ * conversion, and it does not reimplement the "unsupported construct emits
8
+ * `unknown`" behavior, which lives entirely in `weft codegen`'s emitter.
9
+ *
10
+ * @module core/contract/build
11
+ */
12
+ import { WeftError } from '../weft-error.ts';
13
+ import type { WorkflowContract, WorkflowContractSource } from './types.ts';
14
+ /** Discriminates which part of a workflow contract source failed conversion. */
15
+ type ContractEntityKind = 'workflow' | 'signal' | 'update' | 'query' | 'activity' | 'finalizer';
16
+ /**
17
+ * Thrown when {@link buildWorkflowContract} cannot convert a registered
18
+ * `DefinitionSchema` to JSON Schema — no built-in vendor adapter and no
19
+ * structural `~standard.jsonSchema` converter. Mirrors
20
+ * `RegistrySchemaConversionError`'s `entityKind`/`entityName`/`direction`
21
+ * fields; not root-exported, matching that error's precedent (build-tooling
22
+ * errors surfaced to the caller of the builder, not part of the public error
23
+ * vocabulary).
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { WorkflowContractConversionError } from '@lostgradient/weft';
28
+ *
29
+ * try {
30
+ * throw new WorkflowContractConversionError('activity', 'charge', 'inputSchema', new Error('boom'));
31
+ * } catch (error) {
32
+ * console.log(error instanceof WorkflowContractConversionError); // true
33
+ * }
34
+ * ```
35
+ */
36
+ export declare class WorkflowContractConversionError extends WeftError<'WorkflowContractConversionError'> {
37
+ readonly entityKind: ContractEntityKind;
38
+ readonly entityName: string;
39
+ readonly direction: 'inputSchema' | 'outputSchema';
40
+ constructor(entityKind: ContractEntityKind, entityName: string, direction: 'inputSchema' | 'outputSchema', cause: unknown);
41
+ }
42
+ /**
43
+ * Build a normalized {@link WorkflowContract} from an authoring-time workflow
44
+ * definition.
45
+ *
46
+ * Accepts anything structurally matching {@link WorkflowContractSource} —
47
+ * both `WorkflowDefinition` and `BuiltWorkflowDefinition` (the return type
48
+ * of `workflow({...}).execute(fn)`) satisfy it directly, with no cast.
49
+ *
50
+ * Throws {@link WorkflowContractConversionError} if any declared schema
51
+ * (workflow input/output, a signal/update/query input/output, an activity
52
+ * input/output, or the finalizer's input/output) cannot be converted to JSON
53
+ * Schema.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * import { buildWorkflowContract } from '@lostgradient/weft';
58
+ *
59
+ * const contract = buildWorkflowContract({ name: 'checkout', version: '2.1.0' });
60
+ * console.log(contract.name, contract.workflowVersion); // checkout 2.1.0
61
+ * ```
62
+ */
63
+ export declare function buildWorkflowContract(source: WorkflowContractSource): WorkflowContract;
64
+ export {};
@@ -0,0 +1,99 @@
1
+ import { definitionSchemaToJsonSchema } from "../types/definition-schema-to-json.js";
2
+ import { DEFAULT_WORKFLOW_VERSION } from "../versioning.js";
3
+ import { WeftError } from "../weft-error.js";
4
+ import { normalizeWorkflowContract } from "./normalize.js";
5
+
6
+ export class WorkflowContractConversionError extends WeftError {
7
+ entityKind;
8
+ entityName;
9
+ direction;
10
+ constructor(entityKind, entityName, direction, cause) {
11
+ const causeMessage = cause instanceof Error ? cause.message : String(cause);
12
+ super("WorkflowContractConversionError", `Failed to convert ${direction} for ${entityKind} "${entityName}": ${causeMessage}`, { cause });
13
+ this.entityKind = entityKind;
14
+ this.entityName = entityName;
15
+ this.direction = direction;
16
+ }
17
+ }
18
+ function convertSchema(entityKind, entityName, direction, schema) {
19
+ try {
20
+ return definitionSchemaToJsonSchema(schema, direction === "inputSchema" ? "input" : "output");
21
+ } catch (cause) {
22
+ throw new WorkflowContractConversionError(entityKind, entityName, direction, cause);
23
+ }
24
+ }
25
+ function buildMessageContract(entityKind, entityName, source) {
26
+ const entry = {};
27
+ if (source.inputSchema !== void 0)
28
+ entry.inputSchema = convertSchema(entityKind, entityName, "inputSchema", source.inputSchema);
29
+ if (source.outputSchema !== void 0)
30
+ entry.outputSchema = convertSchema(entityKind, entityName, "outputSchema", source.outputSchema);
31
+ return entry;
32
+ }
33
+ function buildActivityContract(workflowName, activityName, source) {
34
+ return buildMessageContract("activity", `${workflowName}.${activityName}`, source);
35
+ }
36
+ function buildMessageRecord(workflowName, entityKind, sources) {
37
+ if (sources === void 0)
38
+ return;
39
+ const localKeys = Object.keys(sources);
40
+ if (localKeys.length === 0)
41
+ return;
42
+ const built = Object.create(null);
43
+ for (const localKey of localKeys) {
44
+ const source = sources[localKey], wireName = source.name;
45
+ if (Object.hasOwn(built, wireName))
46
+ throw Error(`Duplicate ${entityKind} runtime name "${wireName}" in workflow "${workflowName}"`);
47
+ built[wireName] = buildMessageContract(entityKind, `${workflowName}.${entityKind}.${wireName}`, source);
48
+ }
49
+ return built;
50
+ }
51
+ function buildActivityRecord(workflowName, sources) {
52
+ if (sources === void 0)
53
+ return;
54
+ const names = Object.keys(sources);
55
+ if (names.length === 0)
56
+ return;
57
+ const built = Object.create(null);
58
+ for (const name of names)
59
+ built[name] = buildActivityContract(workflowName, name, sources[name]);
60
+ return built;
61
+ }
62
+ function applyDescriptionAndTags(draft, source) {
63
+ if (source.description !== void 0)
64
+ draft.description = source.description;
65
+ if (source.tags !== void 0 && source.tags.length > 0)
66
+ draft.tags = [...source.tags];
67
+ }
68
+ function applySchemas(draft, source) {
69
+ if (source.inputSchema !== void 0)
70
+ draft.inputSchema = convertSchema("workflow", source.name, "inputSchema", source.inputSchema);
71
+ if (source.outputSchema !== void 0)
72
+ draft.outputSchema = convertSchema("workflow", source.name, "outputSchema", source.outputSchema);
73
+ }
74
+ function applyMessageRecords(draft, source) {
75
+ const signals = buildMessageRecord(source.name, "signal", source.signals);
76
+ if (signals !== void 0)
77
+ draft.signals = signals;
78
+ const updates = buildMessageRecord(source.name, "update", source.updates);
79
+ if (updates !== void 0)
80
+ draft.updates = updates;
81
+ const queries = buildMessageRecord(source.name, "query", source.queries);
82
+ if (queries !== void 0)
83
+ draft.queries = queries;
84
+ }
85
+ export function buildWorkflowContract(source) {
86
+ const draft = {
87
+ name: source.name,
88
+ workflowVersion: source.version ?? DEFAULT_WORKFLOW_VERSION
89
+ };
90
+ applyDescriptionAndTags(draft, source);
91
+ applySchemas(draft, source);
92
+ applyMessageRecords(draft, source);
93
+ const activities = buildActivityRecord(source.name, source.activities);
94
+ if (activities !== void 0)
95
+ draft.activities = activities;
96
+ if (source.finalizer !== void 0)
97
+ draft.finalizer = buildMessageContract("finalizer", `${source.name}.finalizer`, source.finalizer);
98
+ return normalizeWorkflowContract(draft);
99
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Structured compatibility verdicts between two workflow revision manifests.
3
+ *
4
+ * `checkWorkflowCompatibility()` answers "may `candidate` automatically
5
+ * activate in place of `current`" as a bounded, machine-readable
6
+ * {@link WorkflowCompatibilityVerdict} rather than a thrown error or a bare
7
+ * boolean—a catalog or refresh orchestrator can report every reason a
8
+ * candidate revision was rejected, but never override one during automatic
9
+ * activation (the reasons are policy-fixed except for one deliberate knob,
10
+ * see {@link WorkflowCompatibilityPolicy}).
11
+ *
12
+ * The five reasons this module can report mirror, and in one case literally
13
+ * call, checks that already exist elsewhere in the codebase rather than
14
+ * reinventing them:
15
+ *
16
+ * - `workflow-version-incompatible` calls {@link checkVersionCompatibility}
17
+ * from `core/versioning.ts`—the exact primitive
18
+ * `derivePreparedExecutionState()` (`core/engine/lifecycle/persist.ts`)
19
+ * already uses to reject a stored checkpoint against a re-registered
20
+ * workflow. This module never modifies that primitive; it reuses it as-is
21
+ * so the two answers can never disagree.
22
+ * - `contract-hash-mismatch` and `manifest-version-unsupported` reuse the
23
+ * same names `parseWorkflowRevisionManifest()`
24
+ * (`WorkflowRevisionManifestRejectionReason`, `./failure.ts`) already uses
25
+ * for structurally similar situations—hostile-input rejection there,
26
+ * activation compatibility here. The two unions are intentionally
27
+ * independent; the shared literal is vocabulary reuse, not a coupling.
28
+ * `manifest-version-unsupported` specifically is unreachable through this
29
+ * module's own type-safe surface today—`WorkflowRevisionManifest.manifestVersion`
30
+ * is typed as the single supported literal, and every in-repo producer
31
+ * ({@link buildWorkflowRevisionManifest}, {@link parseWorkflowRevisionManifest})
32
+ * guarantees it—so it costs nothing to check here and exists for the
33
+ * consumer this module is exported for but does not yet have: a future
34
+ * catalog or refresh system comparing a manifest it read from storage or
35
+ * the wire, which may not have round-tripped through
36
+ * `parseWorkflowRevisionManifest()` immediately before the comparison.
37
+ * Widening the parameter type to accept that case is deferred to whichever
38
+ * catalog work (WFT-9) first needs a type-safe way to trigger it.
39
+ *
40
+ * `checkWorkflowCompatibility()` is pure and synchronous: both manifests
41
+ * already carry their computed `contractHash`/`revision`, so no hashing
42
+ * happens here.
43
+ *
44
+ * @module core/contract/compatibility
45
+ */
46
+ import type { WorkflowRevisionManifest } from './types.ts';
47
+ /**
48
+ * One bounded, machine-readable reason `candidate` is not compatible with
49
+ * `current`. Closed union, safe to use as a metric label—exactly the five
50
+ * reasons named by the Activation Compatibility issue, in the fixed order
51
+ * {@link checkWorkflowCompatibility} evaluates and reports them.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import type { WorkflowCompatibilityReason } from '@lostgradient/weft';
56
+ *
57
+ * const counts = new Map<WorkflowCompatibilityReason, number>();
58
+ * counts.set('contract-hash-mismatch', 1);
59
+ * console.log(counts.get('contract-hash-mismatch'));
60
+ * ```
61
+ */
62
+ export type WorkflowCompatibilityReason = 'name-mismatch' | 'manifest-version-unsupported' | 'contract-hash-mismatch' | 'workflow-version-incompatible' | 'artifact-revision-mismatch';
63
+ /**
64
+ * The structured result of {@link checkWorkflowCompatibility}: either fully
65
+ * compatible, or incompatible with the complete, ordered list of every
66
+ * applicable {@link WorkflowCompatibilityReason}—never just the first one
67
+ * found. A refresh orchestrator may report every reason here; it may never
68
+ * treat an incompatible verdict as compatible during automatic activation.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * import type { WorkflowCompatibilityVerdict } from '@lostgradient/weft';
73
+ *
74
+ * const verdict: WorkflowCompatibilityVerdict = {
75
+ * compatible: false,
76
+ * reasons: ['contract-hash-mismatch'],
77
+ * };
78
+ * console.log(verdict.compatible ? 'ok' : verdict.reasons.join(', '));
79
+ * ```
80
+ */
81
+ export type WorkflowCompatibilityVerdict = Readonly<{
82
+ compatible: true;
83
+ }> | Readonly<{
84
+ compatible: false;
85
+ reasons: readonly WorkflowCompatibilityReason[];
86
+ }>;
87
+ /**
88
+ * Declared compatibility policy accepted by {@link checkWorkflowCompatibility}.
89
+ *
90
+ * `requireExactRevision` is the *only* tunable axis. `name-mismatch`,
91
+ * `manifest-version-unsupported`, `contract-hash-mismatch`, and
92
+ * `workflow-version-incompatible` can never be loosened by policy—a
93
+ * refresh system may report those reasons but may not override them during
94
+ * automatic activation, which is the literal mechanism the Activation
95
+ * Compatibility issue asks for.
96
+ *
97
+ * - `true` (the strict default): a candidate whose `revision` differs from
98
+ * `current`'s—even when `contractHash` is identical—reports
99
+ * `artifact-revision-mismatch` and is not compatible.
100
+ * - `false`: a `revision`-only difference is tolerated; `artifact-revision-mismatch`
101
+ * is never reported. What that difference *means* depends on how `revision`
102
+ * was produced. Under the default content-derived revision
103
+ * ({@link deriveWorkflowRevision}), a `revision`-only difference is always a
104
+ * documentation-only edit (`contract.description` or `contract.tags`, since
105
+ * `contractHash` deliberately excludes both). Under a caller-supplied
106
+ * revision (`buildWorkflowRevisionManifest(contract, { revision })`), a
107
+ * `revision`-only difference can be any artifact-identity change the caller
108
+ * chose to encode there (a build id, a deployment tag)—`false` tolerates
109
+ * that too, since this module has no way to distinguish an opaque supplied
110
+ * revision from a derived one by inspecting the manifest alone. Do not set
111
+ * `false` when your manifests use explicit revisions unless you intend to
112
+ * accept any `revision` change as compatible. Under the default
113
+ * content-derived revision, a `contractHash` difference always implies a
114
+ * `revision` difference too (`revision`'s full-contract digest is a strict
115
+ * superset of what `contractHash` covers); under a caller-supplied
116
+ * revision, two manifests can share the same `revision` string despite
117
+ * different `contractHash` values, since the caller controls that string
118
+ * independently of contract content. Either way,
119
+ * `contract-hash-mismatch` is unaffected by this setting and still blocks
120
+ * activation on its own.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * import type { WorkflowCompatibilityPolicy } from '@lostgradient/weft';
125
+ *
126
+ * const lenient: WorkflowCompatibilityPolicy = { requireExactRevision: false };
127
+ * console.log(lenient.requireExactRevision);
128
+ * ```
129
+ */
130
+ export interface WorkflowCompatibilityPolicy {
131
+ /**
132
+ * Whether an exact `revision` match is required for compatibility, on top
133
+ * of the four never-tunable reasons. Defaults to `true` when omitted or
134
+ * when the policy object itself omits the field.
135
+ */
136
+ requireExactRevision?: boolean;
137
+ }
138
+ /**
139
+ * The strict default policy `checkWorkflowCompatibility` uses when no
140
+ * policy argument is supplied. Exported so a catalog can name the default
141
+ * explicitly—in a log line, a configuration default, a test fixture—rather
142
+ * than relying on an implicit fallback. Frozen: `checkWorkflowCompatibility`
143
+ * consults this exact object as the default argument for every call that
144
+ * omits a policy, so a caller that mutated it would silently change the
145
+ * default for every other caller in the process.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * import { DEFAULT_WORKFLOW_COMPATIBILITY_POLICY } from '@lostgradient/weft';
150
+ *
151
+ * console.log(DEFAULT_WORKFLOW_COMPATIBILITY_POLICY.requireExactRevision); // true
152
+ * ```
153
+ */
154
+ export declare const DEFAULT_WORKFLOW_COMPATIBILITY_POLICY: Readonly<Required<WorkflowCompatibilityPolicy>>;
155
+ /**
156
+ * Compare two workflow revision manifests and report whether `candidate` is
157
+ * compatible with `current`—the pure comparison behind automatic
158
+ * activation. Symmetric: `checkWorkflowCompatibility(a, b, policy)` and
159
+ * `checkWorkflowCompatibility(b, a, policy)` always agree, since every
160
+ * underlying check is an equality or bounded-value comparison with no
161
+ * directionality.
162
+ *
163
+ * Every applicable reason is collected and returned in the fixed order
164
+ * `name-mismatch`, `manifest-version-unsupported`, `contract-hash-mismatch`,
165
+ * `workflow-version-incompatible`, `artifact-revision-mismatch`—the
166
+ * function never short-circuits on the first reason found.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * import {
171
+ * buildWorkflowContract,
172
+ * buildWorkflowRevisionManifest,
173
+ * checkWorkflowCompatibility,
174
+ * } from '@lostgradient/weft';
175
+ *
176
+ * const current = await buildWorkflowRevisionManifest(
177
+ * buildWorkflowContract({ name: 'checkout', version: '1.0.0' }),
178
+ * );
179
+ * const candidate = await buildWorkflowRevisionManifest(
180
+ * buildWorkflowContract({ name: 'checkout', version: '2.0.0' }),
181
+ * );
182
+ *
183
+ * const verdict = checkWorkflowCompatibility(current, candidate);
184
+ * console.log(verdict.compatible); // false
185
+ * ```
186
+ */
187
+ export declare function checkWorkflowCompatibility(current: WorkflowRevisionManifest, candidate: WorkflowRevisionManifest, policy?: WorkflowCompatibilityPolicy): WorkflowCompatibilityVerdict;
@@ -0,0 +1,37 @@
1
+ import { checkVersionCompatibility } from "../versioning.js";
2
+ import { WORKFLOW_REVISION_MANIFEST_VERSION } from "./types.js";
3
+ export const DEFAULT_WORKFLOW_COMPATIBILITY_POLICY = Object.freeze({
4
+ requireExactRevision: !0
5
+ });
6
+ function isSupportedWorkflowRevisionManifestVersion(version) {
7
+ return version === WORKFLOW_REVISION_MANIFEST_VERSION;
8
+ }
9
+ function isNameMismatch(current, candidate) {
10
+ return current.name !== candidate.name;
11
+ }
12
+ function isManifestVersionUnsupported(current, candidate) {
13
+ return !isSupportedWorkflowRevisionManifestVersion(current.manifestVersion) || !isSupportedWorkflowRevisionManifestVersion(candidate.manifestVersion);
14
+ }
15
+ function isContractHashMismatch(current, candidate) {
16
+ return current.contractHash !== candidate.contractHash;
17
+ }
18
+ function isWorkflowVersionIncompatible(current, candidate) {
19
+ return checkVersionCompatibility(current.workflowVersion, candidate.workflowVersion) === "incompatible";
20
+ }
21
+ function isArtifactRevisionMismatch(current, candidate) {
22
+ return current.revision !== candidate.revision;
23
+ }
24
+ export function checkWorkflowCompatibility(current, candidate, policy = DEFAULT_WORKFLOW_COMPATIBILITY_POLICY) {
25
+ const requireExactRevision = policy.requireExactRevision ?? !0, reasons = [];
26
+ if (isNameMismatch(current, candidate))
27
+ reasons.push("name-mismatch");
28
+ if (isManifestVersionUnsupported(current, candidate))
29
+ reasons.push("manifest-version-unsupported");
30
+ if (isContractHashMismatch(current, candidate))
31
+ reasons.push("contract-hash-mismatch");
32
+ if (isWorkflowVersionIncompatible(current, candidate))
33
+ reasons.push("workflow-version-incompatible");
34
+ if (requireExactRevision && isArtifactRevisionMismatch(current, candidate))
35
+ reasons.push("artifact-revision-mismatch");
36
+ return reasons.length === 0 ? { compatible: !0 } : { compatible: !1, reasons };
37
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Bounded failure vocabulary for workflow revision manifest validation.
3
+ *
4
+ * Mirrors `src/worker/manifest/failure.ts`: rejections are returned rather
5
+ * than thrown, `reason` is a closed union safe to use as a metric label, and
6
+ * `message`/`path` are diagnostics only.
7
+ *
8
+ * @module core/contract/failure
9
+ */
10
+ /**
11
+ * Why an untrusted {@link WorkflowRevisionManifest} was rejected.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import type { WorkflowRevisionManifestRejectionReason } from '@lostgradient/weft';
16
+ *
17
+ * const counts = new Map<WorkflowRevisionManifestRejectionReason, number>();
18
+ * counts.set('contract-hash-mismatch', 1);
19
+ * console.log(counts.get('contract-hash-mismatch'));
20
+ * ```
21
+ */
22
+ export type WorkflowRevisionManifestRejectionReason = 'manifest-version-unsupported' | 'contract-hash-mismatch' | 'not-an-object' | 'invalid-field' | 'identifier-too-long' | 'too-many-entries' | 'manifest-too-large';
23
+ /**
24
+ * A rejected workflow revision manifest, with enough detail to fix the
25
+ * caller's input and not so much that it leaks contract content into logs.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { parseWorkflowRevisionManifest, type WorkflowRevisionManifestValidationFailure } from '@lostgradient/weft';
30
+ *
31
+ * const result = await parseWorkflowRevisionManifest({ manifestVersion: 99 });
32
+ * if (!result.ok) {
33
+ * const rejection: WorkflowRevisionManifestValidationFailure = result;
34
+ * console.log(rejection.reason); // 'manifest-version-unsupported'
35
+ * }
36
+ * ```
37
+ */
38
+ export type WorkflowRevisionManifestValidationFailure = Readonly<{
39
+ ok: false;
40
+ /** Bounded rejection reason, safe as a metric label. */
41
+ reason: WorkflowRevisionManifestRejectionReason;
42
+ /** Human-readable diagnostic. Never use as a metric label. */
43
+ message: string;
44
+ /** Dotted path to the offending field, when one applies. */
45
+ path?: string;
46
+ }>;
47
+ /**
48
+ * Build a validation failure whose message reads as one sentence about the
49
+ * offending path — `manifest.contract.workflowVersion must be a non-empty
50
+ * string`.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import { workflowRevisionManifestFailure } from '@lostgradient/weft';
55
+ *
56
+ * const failure = workflowRevisionManifestFailure(
57
+ * 'invalid-field',
58
+ * 'must be a non-empty string',
59
+ * 'manifest.name',
60
+ * );
61
+ * console.log(failure.message);
62
+ * ```
63
+ */
64
+ export declare function workflowRevisionManifestFailure(reason: WorkflowRevisionManifestRejectionReason, message: string, path?: string): WorkflowRevisionManifestValidationFailure;
@@ -0,0 +1,4 @@
1
+ export function workflowRevisionManifestFailure(reason, message, path) {
2
+ const described = path === void 0 ? message : `${path} ${message}`;
3
+ return path === void 0 ? { ok: !1, reason, message: described } : { ok: !1, reason, message: described, path };
4
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Content-addressed digests of a workflow contract's public payload.
3
+ *
4
+ * `contractHash()` answers "which public payload contract does this
5
+ * revision implement" — it excludes `name`, `workflowVersion`,
6
+ * `description`, and `tags`, which identify the workflow and describe it in
7
+ * prose rather than describe what callers may send and get back. That
8
+ * exclusion is what lets a documentation edit change `deriveWorkflowRevision()`'s
9
+ * broader identity (see `revision.ts`) without changing `contractHash()`.
10
+ *
11
+ * `digestCanonicalWorkflowContract()` is a generic "digest this canonical
12
+ * JSON text" primitive shared by both identities: which identity a caller
13
+ * gets back depends entirely on which canonical serializer produced the
14
+ * text handed to it — `canonicalWorkflowContractJson()`'s full-identity
15
+ * output yields the same digest `deriveWorkflowRevision()` returns; this
16
+ * module's internal payload-only serializer is what `contractHash()` feeds
17
+ * it. Treat it as a shared digesting primitive, not as itself a payload-only
18
+ * function.
19
+ *
20
+ * SHA-256 rather than the repository's FNV-1a helpers, matching the worker
21
+ * manifest digest's choice: `contractHash` is compared for compatibility
22
+ * decisions, so a collision would let two different contracts pass for one
23
+ * another, ruling out a cache-key-quality hash.
24
+ *
25
+ * @module core/contract/hash
26
+ */
27
+ import type { WorkflowActivityContract, WorkflowContract } from './types.ts';
28
+ /**
29
+ * Algorithm tag prefixed to every digest {@link contractHash} and
30
+ * {@link activityContractHash} produce.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { WORKFLOW_CONTRACT_DIGEST_ALGORITHM } from '@lostgradient/weft';
35
+ *
36
+ * const hash = 'sha256:2b1f0c9d';
37
+ * console.log(hash.startsWith(`${WORKFLOW_CONTRACT_DIGEST_ALGORITHM}:`)); // true
38
+ * ```
39
+ */
40
+ export declare const WORKFLOW_CONTRACT_DIGEST_ALGORITHM = "sha256";
41
+ /**
42
+ * Digest canonical contract JSON text.
43
+ *
44
+ * Separate from {@link contractHash} and `deriveWorkflowRevision` so a
45
+ * caller that already holds a canonical serialization does not serialize
46
+ * the contract a second time. Which identity the result represents depends
47
+ * entirely on which canonical serializer produced `canonicalJson` — see this
48
+ * module's JSDoc.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { canonicalWorkflowContractJson, digestCanonicalWorkflowContract } from '@lostgradient/weft';
53
+ *
54
+ * const canonical = canonicalWorkflowContractJson({ name: 'checkout', workflowVersion: '2.1.0' });
55
+ * console.log((await digestCanonicalWorkflowContract(canonical)).startsWith('sha256:'));
56
+ * ```
57
+ */
58
+ export declare function digestCanonicalWorkflowContract(canonicalJson: string): Promise<string>;
59
+ /**
60
+ * Compute a workflow contract's payload-only content identity.
61
+ *
62
+ * Determined entirely by the payload fields (schemas, signals, updates,
63
+ * queries, activities, finalizer) plus the `WORKFLOW_CONTRACT_VERSION`
64
+ * domain separator — two contracts that differ only in `name`,
65
+ * `workflowVersion`, `description`, or `tags` hash identically, and two
66
+ * contracts that differ only in key order also hash identically.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { contractHash } from '@lostgradient/weft';
71
+ *
72
+ * const left = await contractHash({ name: 'checkout', workflowVersion: '1.0.0' });
73
+ * const right = await contractHash({ name: 'checkout', workflowVersion: '2.0.0' });
74
+ * console.log(left === right); // true — name/workflowVersion are excluded
75
+ * ```
76
+ */
77
+ export declare function contractHash(contract: WorkflowContract): Promise<string>;
78
+ /**
79
+ * Compute one activity's payload-only content identity, independent of the
80
+ * workflow contract it is declared under.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { activityContractHash } from '@lostgradient/weft';
85
+ *
86
+ * const hash = await activityContractHash({
87
+ * inputSchema: { type: 'object', properties: { amount: { type: 'number' } } },
88
+ * });
89
+ * console.log(hash.startsWith('sha256:')); // true
90
+ * ```
91
+ */
92
+ export declare function activityContractHash(activity: WorkflowActivityContract): Promise<string>;
@@ -0,0 +1,34 @@
1
+ import { sha256Hex } from "../../worker/manifest/content-digest.js";
2
+ import {
3
+ appendContractRecordField,
4
+ appendSchemaField,
5
+ canonicalMessageContractJson
6
+ } from "./normalize.js";
7
+ import { WORKFLOW_CONTRACT_VERSION } from "./types.js";
8
+ export const WORKFLOW_CONTRACT_DIGEST_ALGORITHM = "sha256";
9
+ function canonicalWorkflowContractPayloadJson(contract) {
10
+ const fields = [`"contractVersion":${JSON.stringify(WORKFLOW_CONTRACT_VERSION)}`];
11
+ appendSchemaField(fields, "inputSchema", contract.inputSchema);
12
+ appendSchemaField(fields, "outputSchema", contract.outputSchema);
13
+ appendContractRecordField(fields, "signals", contract.signals);
14
+ appendContractRecordField(fields, "updates", contract.updates);
15
+ appendContractRecordField(fields, "queries", contract.queries);
16
+ appendContractRecordField(fields, "activities", contract.activities);
17
+ if (contract.finalizer !== void 0)
18
+ fields.push(`"finalizer":${canonicalMessageContractJson(contract.finalizer)}`);
19
+ return `{${fields.join(",")}}`;
20
+ }
21
+ export async function digestCanonicalWorkflowContract(canonicalJson) {
22
+ return sha256Hex(canonicalJson);
23
+ }
24
+ export async function contractHash(contract) {
25
+ return digestCanonicalWorkflowContract(canonicalWorkflowContractPayloadJson(contract));
26
+ }
27
+ export async function activityContractHash(activity) {
28
+ const versionField = `"contractVersion":${JSON.stringify(WORKFLOW_CONTRACT_VERSION)}`, schemaFields = canonicalActivityContractSchemaFields(activity);
29
+ return digestCanonicalWorkflowContract(`{${[versionField, ...schemaFields].join(",")}}`);
30
+ }
31
+ function canonicalActivityContractSchemaFields(entry) {
32
+ const stripped = canonicalMessageContractJson(entry).slice(1, -1);
33
+ return stripped.length === 0 ? [] : [stripped];
34
+ }