@lostgradient/weft 0.23.0 → 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 (295) 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-loader.js +2 -2
  281. package/dist/storage/node-sqlite.js +51 -8
  282. package/dist/storage/outbox-keys.d.ts +77 -0
  283. package/dist/storage/outbox-keys.js +14 -0
  284. package/dist/storage/postgres.js +2 -2
  285. package/dist/storage/resolve.js +1 -1
  286. package/dist/storage/scoped-storage.js +1 -1
  287. package/dist/storage/storage-configuration.d.ts +6 -0
  288. package/dist/storage/testing.js +1 -1
  289. package/dist/storage/turso.js +2 -2
  290. package/dist/version.d.ts +1 -1
  291. package/dist/version.js +1 -1
  292. package/dist/web-extension.js +1 -1
  293. package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
  294. package/dist/worker/manifest/registry-contract-builder.js +40 -31
  295. package/package.json +1 -1
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Canonical, normalized workflow contract vocabulary — the single input to
3
+ * both TypeScript code generation and `contractHash()`, plus the typed
4
+ * `WorkflowRevisionManifest` and its hostile-input parser.
5
+ *
6
+ * @module core/contract
7
+ */
8
+ export { MAX_CONTRACT_IDENTIFIER_BYTES, MAX_CONTRACT_MESSAGE_COUNT, MAX_CONTRACT_SCHEMA_DEPTH, MAX_NORMALIZED_CONTRACT_BYTES, } from './limits.ts';
9
+ export { WORKFLOW_CONTRACT_VERSION, WORKFLOW_REVISION_MANIFEST_VERSION } from './types.ts';
10
+ export type { WorkflowActivityContract, WorkflowContract, WorkflowContractActivitySource, WorkflowContractMessageSource, WorkflowContractSource, WorkflowMessageContract, WorkflowRevisionManifest, } from './types.ts';
11
+ export { WorkflowContractConversionError, buildWorkflowContract } from './build.ts';
12
+ export { canonicalWorkflowContractJson, normalizeWorkflowContract } from './normalize.ts';
13
+ export { WORKFLOW_CONTRACT_DIGEST_ALGORITHM, activityContractHash, contractHash, digestCanonicalWorkflowContract, } from './hash.ts';
14
+ export { deriveWorkflowRevision } from './revision.ts';
15
+ export { buildWorkflowRevisionManifest } from './manifest.ts';
16
+ export type { BuildWorkflowRevisionManifestOptions } from './manifest.ts';
17
+ export type { WorkflowRevisionManifestRejectionReason, WorkflowRevisionManifestValidationFailure, } from './failure.ts';
18
+ export { parseWorkflowRevisionManifest } from './manifest-parse.ts';
19
+ export type { WorkflowRevisionManifestParseResult, WorkflowRevisionManifestParseSuccess, } from './manifest-parse.ts';
20
+ export { DEFAULT_WORKFLOW_COMPATIBILITY_POLICY, checkWorkflowCompatibility, } from './compatibility.ts';
21
+ export type { WorkflowCompatibilityPolicy, WorkflowCompatibilityReason, WorkflowCompatibilityVerdict, } from './compatibility.ts';
@@ -0,0 +1,22 @@
1
+ export {
2
+ MAX_CONTRACT_IDENTIFIER_BYTES,
3
+ MAX_CONTRACT_MESSAGE_COUNT,
4
+ MAX_CONTRACT_SCHEMA_DEPTH,
5
+ MAX_NORMALIZED_CONTRACT_BYTES
6
+ } from "./limits.js";
7
+ export { WORKFLOW_CONTRACT_VERSION, WORKFLOW_REVISION_MANIFEST_VERSION } from "./types.js";
8
+ export { WorkflowContractConversionError, buildWorkflowContract } from "./build.js";
9
+ export { canonicalWorkflowContractJson, normalizeWorkflowContract } from "./normalize.js";
10
+ export {
11
+ WORKFLOW_CONTRACT_DIGEST_ALGORITHM,
12
+ activityContractHash,
13
+ contractHash,
14
+ digestCanonicalWorkflowContract
15
+ } from "./hash.js";
16
+ export { deriveWorkflowRevision } from "./revision.js";
17
+ export { buildWorkflowRevisionManifest } from "./manifest.js";
18
+ export { parseWorkflowRevisionManifest } from "./manifest-parse.js";
19
+ export {
20
+ DEFAULT_WORKFLOW_COMPATIBILITY_POLICY,
21
+ checkWorkflowCompatibility
22
+ } from "./compatibility.js";
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Semantic bounds applied to a workflow contract before it is normalized,
3
+ * hashed, or admitted as a {@link WorkflowRevisionManifest}.
4
+ *
5
+ * Sized analogously to `src/worker/manifest/limits.ts` — the same
6
+ * placement rationale applies: the type declarations, the hostile-input
7
+ * manifest parser, and the canonical serializer can all depend on these
8
+ * limits without forming an import cycle.
9
+ *
10
+ * @module core/contract/limits
11
+ */
12
+ /**
13
+ * Maximum UTF-8 byte length of any single identifier-shaped contract string —
14
+ * workflow/signal/update/query/activity name, `workflowVersion`, or an
15
+ * explicitly supplied `revision`.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { MAX_CONTRACT_IDENTIFIER_BYTES } from '@lostgradient/weft';
20
+ *
21
+ * const name = 'checkout';
22
+ * const fits = new TextEncoder().encode(name).byteLength <= MAX_CONTRACT_IDENTIFIER_BYTES;
23
+ * console.log(fits); // true
24
+ * ```
25
+ */
26
+ export declare const MAX_CONTRACT_IDENTIFIER_BYTES = 512;
27
+ /**
28
+ * Maximum number of entries in any one of a contract's `signals`, `updates`,
29
+ * `queries`, or `activities` records.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { MAX_CONTRACT_MESSAGE_COUNT } from '@lostgradient/weft';
34
+ *
35
+ * const signals = ['approved', 'rejected'];
36
+ * console.log(signals.length <= MAX_CONTRACT_MESSAGE_COUNT); // true
37
+ * ```
38
+ */
39
+ export declare const MAX_CONTRACT_MESSAGE_COUNT = 512;
40
+ /**
41
+ * Maximum nesting depth permitted inside one schema fragment
42
+ * (`inputSchema`/`outputSchema`).
43
+ *
44
+ * Mirrors `codegen-emit.ts`'s `MAX_RECURSION_DEPTH`. `isJSONValue()` and
45
+ * `canonicalJsonStringify()` are both unbounded recursion over their input,
46
+ * so {@link parseWorkflowRevisionManifest} enforces this bound itself,
47
+ * iteratively, before either ever walks an untrusted schema fragment.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * import { MAX_CONTRACT_SCHEMA_DEPTH } from '@lostgradient/weft';
52
+ *
53
+ * console.log(MAX_CONTRACT_SCHEMA_DEPTH >= 16); // true
54
+ * ```
55
+ */
56
+ export declare const MAX_CONTRACT_SCHEMA_DEPTH = 64;
57
+ /**
58
+ * Maximum UTF-8 byte length of the canonical, normalized contract JSON.
59
+ *
60
+ * Checked against the canonical serialization — not the received bytes — so
61
+ * a compact payload cannot evade the bound and then expand under
62
+ * normalization.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import { buildWorkflowContract, canonicalWorkflowContractJson, MAX_NORMALIZED_CONTRACT_BYTES } from '@lostgradient/weft';
67
+ *
68
+ * const canonical = canonicalWorkflowContractJson(
69
+ * buildWorkflowContract({ name: 'checkout', version: '2.1.0' }),
70
+ * );
71
+ * console.log(canonical.length <= MAX_NORMALIZED_CONTRACT_BYTES); // true
72
+ * ```
73
+ */
74
+ export declare const MAX_NORMALIZED_CONTRACT_BYTES = 262144;
@@ -0,0 +1 @@
1
+ export const MAX_CONTRACT_IDENTIFIER_BYTES = 512, MAX_CONTRACT_MESSAGE_COUNT = 512, MAX_CONTRACT_SCHEMA_DEPTH = 64, MAX_NORMALIZED_CONTRACT_BYTES = 262144;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Hostile-input validation for the schema-fragment-shaped parts of an
3
+ * untrusted {@link WorkflowRevisionManifest}: `inputSchema`/`outputSchema`
4
+ * pairs, and the `signals`/`updates`/`queries`/`activities` records that
5
+ * carry them.
6
+ *
7
+ * Split out of `manifest-parse.ts` to keep each file under the repository's
8
+ * implementation-file-size ceiling and so the depth/JSON-safety walk that
9
+ * every schema fragment goes through has one place to audit.
10
+ *
11
+ * @module core/contract/manifest-parse-schema
12
+ */
13
+ import { type NameKind } from '../types/name-grammar.ts';
14
+ import { type WorkflowRevisionManifestValidationFailure } from './failure.ts';
15
+ import type { WorkflowActivityContract, WorkflowMessageContract } from './types.ts';
16
+ type ParseSuccess<T> = {
17
+ ok: true;
18
+ value: T;
19
+ };
20
+ type ParseOutcome<T> = ParseSuccess<T> | WorkflowRevisionManifestValidationFailure;
21
+ /** Validate a `{ inputSchema?, outputSchema? }`-shaped entry from untrusted input. */
22
+ export declare function parseSchemaPair(value: unknown, path: string): ParseOutcome<WorkflowMessageContract | WorkflowActivityContract>;
23
+ /**
24
+ * Validate one record key: present, non-empty, within the identifier byte
25
+ * ceiling and, when `kind` is given, wire-safe per
26
+ * `validateWorkflowOrActivityName`. Signal/update/query names pass `kind:
27
+ * undefined` — those names are deliberately unconstrained, matching
28
+ * `SignalDefinition`/`UpdateDefinition`/`QueryDefinition` at the type level.
29
+ */
30
+ export declare function checkContractKey(key: string, kind: NameKind | undefined, path: string): WorkflowRevisionManifestValidationFailure | undefined;
31
+ /**
32
+ * Validate a `signals`/`updates`/`queries`/`activities` record from
33
+ * untrusted input: bounded entry count, bounded/validated keys, and every
34
+ * entry parsed as a schema pair.
35
+ */
36
+ export declare function parseContractRecord(value: unknown, path: string, keyKind: NameKind | undefined): ParseOutcome<Readonly<Record<string, WorkflowMessageContract | WorkflowActivityContract>>>;
37
+ export {};
@@ -0,0 +1,106 @@
1
+ import { isRecord } from "../../worker/manifest/is-record.js";
2
+ import { utf8ByteLength } from "../../worker/manifest/utf8.js";
3
+ import { isJSONValue } from "../json.js";
4
+ import { validateWorkflowOrActivityName } from "../types/name-grammar.js";
5
+ import {
6
+ workflowRevisionManifestFailure
7
+ } from "./failure.js";
8
+ import {
9
+ MAX_CONTRACT_IDENTIFIER_BYTES,
10
+ MAX_CONTRACT_MESSAGE_COUNT,
11
+ MAX_CONTRACT_SCHEMA_DEPTH
12
+ } from "./limits.js";
13
+ function withinDepth(value, maxDepth) {
14
+ const stack = [{ value, depth: 1 }];
15
+ while (stack.length > 0) {
16
+ const frame = stack.pop();
17
+ if (frame === void 0)
18
+ break;
19
+ if (frame.depth > maxDepth)
20
+ return !1;
21
+ const current = frame.value;
22
+ if (current === null || typeof current !== "object")
23
+ continue;
24
+ const nextDepth = frame.depth + 1;
25
+ if (nextDepth > maxDepth)
26
+ return !1;
27
+ if (Array.isArray(current))
28
+ for (const item of current)
29
+ stack.push({ value: item, depth: nextDepth });
30
+ else
31
+ for (const item of Object.values(current))
32
+ stack.push({ value: item, depth: nextDepth });
33
+ }
34
+ return !0;
35
+ }
36
+ function cloneJsonSafe(value) {
37
+ if (value === null || typeof value !== "object")
38
+ return value;
39
+ if (Array.isArray(value))
40
+ return value.map((entry) => cloneJsonSafe(entry));
41
+ const record = value, cloned = Object.create(null);
42
+ for (const key of Object.keys(record))
43
+ cloned[key] = cloneJsonSafe(record[key]);
44
+ return cloned;
45
+ }
46
+ function parseSchemaFragment(value, path) {
47
+ if (!isRecord(value))
48
+ return workflowRevisionManifestFailure("invalid-field", "must be a JSON object", path);
49
+ if (!withinDepth(value, MAX_CONTRACT_SCHEMA_DEPTH))
50
+ return workflowRevisionManifestFailure("invalid-field", `nests deeper than the maximum schema depth of ${MAX_CONTRACT_SCHEMA_DEPTH}`, path);
51
+ if (!isJSONValue(value))
52
+ return workflowRevisionManifestFailure("invalid-field", "must be a JSON-safe object", path);
53
+ return { ok: !0, value: cloneJsonSafe(value) };
54
+ }
55
+ export function parseSchemaPair(value, path) {
56
+ if (!isRecord(value))
57
+ return workflowRevisionManifestFailure("invalid-field", "must be a JSON object", path);
58
+ const entry = {};
59
+ if (value.inputSchema !== void 0) {
60
+ const inputSchema = parseSchemaFragment(value.inputSchema, `${path}.inputSchema`);
61
+ if (!inputSchema.ok)
62
+ return inputSchema;
63
+ entry.inputSchema = inputSchema.value;
64
+ }
65
+ if (value.outputSchema !== void 0) {
66
+ const outputSchema = parseSchemaFragment(value.outputSchema, `${path}.outputSchema`);
67
+ if (!outputSchema.ok)
68
+ return outputSchema;
69
+ entry.outputSchema = outputSchema.value;
70
+ }
71
+ return { ok: !0, value: entry };
72
+ }
73
+ export function checkContractKey(key, kind, path) {
74
+ if (kind !== void 0 && key.length === 0)
75
+ return workflowRevisionManifestFailure("invalid-field", "must not be an empty string", path);
76
+ if (kind !== void 0) {
77
+ const bytes = utf8ByteLength(key);
78
+ if (bytes > MAX_CONTRACT_IDENTIFIER_BYTES)
79
+ return workflowRevisionManifestFailure("identifier-too-long", `is ${bytes} bytes, exceeding the maximum identifier size of ${MAX_CONTRACT_IDENTIFIER_BYTES}`, path);
80
+ try {
81
+ validateWorkflowOrActivityName(key, kind);
82
+ } catch (error) {
83
+ const message = (error instanceof Error ? error.message : "is not a wire-safe name").replaceAll(key, JSON.stringify(key));
84
+ return workflowRevisionManifestFailure("invalid-field", message, path);
85
+ }
86
+ }
87
+ return;
88
+ }
89
+ export function parseContractRecord(value, path, keyKind) {
90
+ if (!isRecord(value))
91
+ return workflowRevisionManifestFailure("invalid-field", "must be a JSON object", path);
92
+ const names = Object.keys(value);
93
+ if (names.length > MAX_CONTRACT_MESSAGE_COUNT)
94
+ return workflowRevisionManifestFailure("too-many-entries", `declares ${names.length} entries, exceeding the maximum of ${MAX_CONTRACT_MESSAGE_COUNT}`, path);
95
+ const built = Object.create(null);
96
+ for (const name of names) {
97
+ const keyFailure = checkContractKey(name, keyKind, `${path} key ${JSON.stringify(name)}`);
98
+ if (keyFailure !== void 0)
99
+ return keyFailure;
100
+ const entry = parseSchemaPair(value[name], `${path}.${name}`);
101
+ if (!entry.ok)
102
+ return entry;
103
+ built[name] = entry.value;
104
+ }
105
+ return { ok: !0, value: built };
106
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Hostile-input parsing for workflow revision manifests.
3
+ *
4
+ * A manifest arrives from a source this module does not control — persisted
5
+ * storage read by an older or differently-configured build, a wire payload,
6
+ * an operator-supplied fixture. Every field is proven from `unknown` rather
7
+ * than asserted, every open-ended collection is bounded before it is walked,
8
+ * and `contractHash` is always recomputed and compared rather than trusted
9
+ * as supplied — a caller cannot assert a hash it did not earn.
10
+ *
11
+ * @module core/contract/manifest-parse
12
+ */
13
+ import { type WorkflowRevisionManifestValidationFailure } from './failure.ts';
14
+ import type { WorkflowRevisionManifest } from './types.ts';
15
+ /**
16
+ * A successfully validated manifest, paired with the canonical serialization
17
+ * of its (normalized) contract, so a caller does not have to re-derive it.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import {
22
+ * buildWorkflowContract,
23
+ * buildWorkflowRevisionManifest,
24
+ * parseWorkflowRevisionManifest,
25
+ * type WorkflowRevisionManifestParseSuccess,
26
+ * } from '@lostgradient/weft';
27
+ *
28
+ * const manifest = await buildWorkflowRevisionManifest(buildWorkflowContract({ name: 'checkout' }));
29
+ * const result = await parseWorkflowRevisionManifest(manifest);
30
+ * if (result.ok) {
31
+ * const accepted: WorkflowRevisionManifestParseSuccess = result;
32
+ * console.log(accepted.canonicalJson.length > 0);
33
+ * }
34
+ * ```
35
+ */
36
+ export type WorkflowRevisionManifestParseSuccess = Readonly<{
37
+ ok: true;
38
+ /** The validated, normalized manifest. */
39
+ manifest: WorkflowRevisionManifest;
40
+ /** Canonical serialization of `manifest.contract` — `canonicalWorkflowContractJson(manifest.contract)`. */
41
+ canonicalJson: string;
42
+ }>;
43
+ /**
44
+ * Outcome of validating an untrusted workflow revision manifest.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import { parseWorkflowRevisionManifest, type WorkflowRevisionManifestParseResult } from '@lostgradient/weft';
49
+ *
50
+ * const result: WorkflowRevisionManifestParseResult = await parseWorkflowRevisionManifest({});
51
+ * console.log(result.ok);
52
+ * ```
53
+ */
54
+ export type WorkflowRevisionManifestParseResult = WorkflowRevisionManifestParseSuccess | WorkflowRevisionManifestValidationFailure;
55
+ /**
56
+ * Validate an untrusted workflow revision manifest.
57
+ *
58
+ * The returned manifest is already normalized, `contractHash` is always
59
+ * recomputed from the (normalized) contract and compared against the
60
+ * supplied value — a mismatch is rejected with `'contract-hash-mismatch'`
61
+ * regardless of what the caller asserted — and `manifest.name`/
62
+ * `manifest.workflowVersion` must agree with `manifest.contract.name`/
63
+ * `manifest.contract.workflowVersion`. `revision` is validated (bounded,
64
+ * non-empty) but never recomputed: it is an opaque label the parser trusts
65
+ * once it is well-formed.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { buildWorkflowContract, buildWorkflowRevisionManifest, parseWorkflowRevisionManifest } from '@lostgradient/weft';
70
+ *
71
+ * const manifest = await buildWorkflowRevisionManifest(buildWorkflowContract({ name: 'checkout' }));
72
+ * const result = await parseWorkflowRevisionManifest(manifest);
73
+ * console.log(result.ok ? result.manifest.name : result.reason);
74
+ * ```
75
+ */
76
+ export declare function parseWorkflowRevisionManifest(value: unknown): Promise<WorkflowRevisionManifestParseResult>;
@@ -0,0 +1,189 @@
1
+ import { isRecord } from "../../worker/manifest/is-record.js";
2
+ import { utf8ByteLength } from "../../worker/manifest/utf8.js";
3
+ import {
4
+ workflowRevisionManifestFailure
5
+ } from "./failure.js";
6
+ import { contractHash } from "./hash.js";
7
+ import {
8
+ MAX_CONTRACT_IDENTIFIER_BYTES,
9
+ MAX_CONTRACT_MESSAGE_COUNT,
10
+ MAX_NORMALIZED_CONTRACT_BYTES
11
+ } from "./limits.js";
12
+ import { checkContractKey, parseContractRecord, parseSchemaPair } from "./manifest-parse-schema.js";
13
+ import { canonicalWorkflowContractJson, normalizeWorkflowContract } from "./normalize.js";
14
+ import { WORKFLOW_REVISION_MANIFEST_VERSION } from "./types.js";
15
+ function parseIdentifier(value, path) {
16
+ if (typeof value !== "string" || value.length === 0)
17
+ return workflowRevisionManifestFailure("invalid-field", "must be a non-empty string", path);
18
+ const bytes = utf8ByteLength(value);
19
+ if (bytes > MAX_CONTRACT_IDENTIFIER_BYTES)
20
+ return workflowRevisionManifestFailure("identifier-too-long", `is ${bytes} bytes, exceeding the maximum identifier size of ${MAX_CONTRACT_IDENTIFIER_BYTES}`, path);
21
+ return { ok: !0, value };
22
+ }
23
+ function parseDescription(value, path) {
24
+ if (typeof value !== "string")
25
+ return workflowRevisionManifestFailure("invalid-field", "must be a string", path);
26
+ return { ok: !0, value };
27
+ }
28
+ function parseTag(value, path) {
29
+ if (typeof value !== "string")
30
+ return workflowRevisionManifestFailure("invalid-field", "must be a string", path);
31
+ return { ok: !0, value };
32
+ }
33
+ function parseTags(value, path) {
34
+ if (value === void 0)
35
+ return { ok: !0, value: void 0 };
36
+ if (!Array.isArray(value))
37
+ return workflowRevisionManifestFailure("invalid-field", "must be an array of strings", path);
38
+ if (value.length > MAX_CONTRACT_MESSAGE_COUNT)
39
+ return workflowRevisionManifestFailure("too-many-entries", `declares ${value.length} entries, exceeding the maximum of ${MAX_CONTRACT_MESSAGE_COUNT}`, path);
40
+ const tags = [];
41
+ let accumulatedBytes = 0;
42
+ for (const [index, entry] of value.entries()) {
43
+ const tag = parseTag(entry, `${path}[${index}]`);
44
+ if (!tag.ok)
45
+ return tag;
46
+ accumulatedBytes += utf8ByteLength(tag.value);
47
+ if (accumulatedBytes > MAX_NORMALIZED_CONTRACT_BYTES)
48
+ return workflowRevisionManifestFailure("manifest-too-large", `accumulated tag bytes exceed the maximum of ${MAX_NORMALIZED_CONTRACT_BYTES}`, path);
49
+ tags.push(tag.value);
50
+ }
51
+ return { ok: !0, value: tags.length === 0 ? void 0 : tags };
52
+ }
53
+ function parseContractIdentity(value, path) {
54
+ const name = parseIdentifier(value.name, `${path}.name`);
55
+ if (!name.ok)
56
+ return name;
57
+ const nameKeyFailure = checkContractKey(name.value, "workflow", `${path}.name`);
58
+ if (nameKeyFailure !== void 0)
59
+ return nameKeyFailure;
60
+ const workflowVersion = parseIdentifier(value.workflowVersion, `${path}.workflowVersion`);
61
+ if (!workflowVersion.ok)
62
+ return workflowVersion;
63
+ const identity = {
64
+ name: name.value,
65
+ workflowVersion: workflowVersion.value
66
+ };
67
+ if (value.description !== void 0) {
68
+ const description = parseDescription(value.description, `${path}.description`);
69
+ if (!description.ok)
70
+ return description;
71
+ identity.description = description.value;
72
+ }
73
+ const tags = parseTags(value.tags, `${path}.tags`);
74
+ if (!tags.ok)
75
+ return tags;
76
+ if (tags.value !== void 0)
77
+ identity.tags = tags.value;
78
+ return { ok: !0, value: identity };
79
+ }
80
+ const NAMED_RECORD_FIELDS = [
81
+ ["signals", void 0],
82
+ ["updates", void 0],
83
+ ["queries", void 0],
84
+ ["activities", "activity"]
85
+ ];
86
+ function parseNamedRecordFields(value, path, collections) {
87
+ for (const [field, keyKind] of NAMED_RECORD_FIELDS) {
88
+ if (value[field] === void 0)
89
+ continue;
90
+ const parsed = parseContractRecord(value[field], `${path}.${field}`, keyKind);
91
+ if (!parsed.ok)
92
+ return parsed;
93
+ collections[field] = parsed.value;
94
+ }
95
+ return;
96
+ }
97
+ function parseContractCollections(value, path) {
98
+ const collections = {};
99
+ if (value.inputSchema !== void 0 || value.outputSchema !== void 0) {
100
+ const pair = parseSchemaPair({ inputSchema: value.inputSchema, outputSchema: value.outputSchema }, path);
101
+ if (!pair.ok)
102
+ return pair;
103
+ if (pair.value.inputSchema !== void 0)
104
+ collections.inputSchema = pair.value.inputSchema;
105
+ if (pair.value.outputSchema !== void 0)
106
+ collections.outputSchema = pair.value.outputSchema;
107
+ }
108
+ const recordFailure = parseNamedRecordFields(value, path, collections);
109
+ if (recordFailure !== void 0)
110
+ return recordFailure;
111
+ if (value.finalizer !== void 0) {
112
+ const finalizer = parseSchemaPair(value.finalizer, `${path}.finalizer`);
113
+ if (!finalizer.ok)
114
+ return finalizer;
115
+ collections.finalizer = finalizer.value;
116
+ }
117
+ return { ok: !0, value: collections };
118
+ }
119
+ function parseContract(value, path) {
120
+ if (!isRecord(value))
121
+ return workflowRevisionManifestFailure("invalid-field", "must be a JSON object", path);
122
+ const identity = parseContractIdentity(value, path);
123
+ if (!identity.ok)
124
+ return identity;
125
+ const collections = parseContractCollections(value, path);
126
+ if (!collections.ok)
127
+ return collections;
128
+ return { ok: !0, value: { ...identity.value, ...collections.value } };
129
+ }
130
+ const TOP_LEVEL_IDENTIFIER_FIELDS = [
131
+ ["name", "manifest.name"],
132
+ ["workflowVersion", "manifest.workflowVersion"],
133
+ ["revision", "manifest.revision"],
134
+ ["contractHash", "manifest.contractHash"]
135
+ ];
136
+ function parseTopLevelIdentifiers(value) {
137
+ const parsed = {};
138
+ for (const [field, path] of TOP_LEVEL_IDENTIFIER_FIELDS) {
139
+ const result = parseIdentifier(value[field], path);
140
+ if (!result.ok)
141
+ return result;
142
+ parsed[field] = result.value;
143
+ }
144
+ return { ok: !0, value: parsed };
145
+ }
146
+ function checkIdentityAgreement(identifiers, contract) {
147
+ if (identifiers.name !== contract.name)
148
+ return workflowRevisionManifestFailure("invalid-field", "must equal manifest.contract.name", "manifest.name");
149
+ if (identifiers.workflowVersion !== contract.workflowVersion)
150
+ return workflowRevisionManifestFailure("invalid-field", "must equal manifest.contract.workflowVersion", "manifest.workflowVersion");
151
+ return;
152
+ }
153
+ async function finalizeManifest(identifiers, normalized) {
154
+ const canonicalJson = canonicalWorkflowContractJson(normalized), canonicalBytes = utf8ByteLength(canonicalJson);
155
+ if (canonicalBytes > MAX_NORMALIZED_CONTRACT_BYTES)
156
+ return workflowRevisionManifestFailure("manifest-too-large", `contract normalizes to ${canonicalBytes} bytes, exceeding the maximum of ${MAX_NORMALIZED_CONTRACT_BYTES}`, "manifest.contract");
157
+ const recomputedHash = await contractHash(normalized);
158
+ if (recomputedHash !== identifiers.contractHash)
159
+ return workflowRevisionManifestFailure("contract-hash-mismatch", `recomputed contractHash ${recomputedHash} does not match the supplied value`, "manifest.contractHash");
160
+ return {
161
+ ok: !0,
162
+ manifest: {
163
+ manifestVersion: WORKFLOW_REVISION_MANIFEST_VERSION,
164
+ name: identifiers.name,
165
+ workflowVersion: identifiers.workflowVersion,
166
+ revision: identifiers.revision,
167
+ contractHash: recomputedHash,
168
+ contract: normalized
169
+ },
170
+ canonicalJson
171
+ };
172
+ }
173
+ export async function parseWorkflowRevisionManifest(value) {
174
+ if (!isRecord(value))
175
+ return workflowRevisionManifestFailure("not-an-object", "manifest must be a JSON object");
176
+ if (value.manifestVersion !== WORKFLOW_REVISION_MANIFEST_VERSION)
177
+ return workflowRevisionManifestFailure("manifest-version-unsupported", `manifest.manifestVersion must be ${String(WORKFLOW_REVISION_MANIFEST_VERSION)}`);
178
+ const identifiers = parseTopLevelIdentifiers(value);
179
+ if (!identifiers.ok)
180
+ return identifiers;
181
+ const contract = parseContract(value.contract, "manifest.contract");
182
+ if (!contract.ok)
183
+ return contract;
184
+ const agreementFailure = checkIdentityAgreement(identifiers.value, contract.value);
185
+ if (agreementFailure !== void 0)
186
+ return agreementFailure;
187
+ const normalized = normalizeWorkflowContract(contract.value);
188
+ return finalizeManifest(identifiers.value, normalized);
189
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Build a {@link WorkflowRevisionManifest} from a normalized contract.
3
+ *
4
+ * @module core/contract/manifest
5
+ */
6
+ import type { WorkflowContract, WorkflowRevisionManifest } from './types.ts';
7
+ /**
8
+ * Options accepted by {@link buildWorkflowRevisionManifest}.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import type { BuildWorkflowRevisionManifestOptions } from '@lostgradient/weft';
13
+ *
14
+ * const options: BuildWorkflowRevisionManifestOptions = { revision: 'deploy-2026.09.01' };
15
+ * console.log(options.revision);
16
+ * ```
17
+ */
18
+ export interface BuildWorkflowRevisionManifestOptions {
19
+ /**
20
+ * Explicit opaque revision identity. When omitted, `revision` is derived
21
+ * from the full contract via {@link deriveWorkflowRevision}. An empty
22
+ * string, or a string exceeding `MAX_CONTRACT_IDENTIFIER_BYTES`, is
23
+ * rejected rather than silently falling back to derivation.
24
+ */
25
+ revision?: string;
26
+ }
27
+ /**
28
+ * Build a {@link WorkflowRevisionManifest} for a workflow contract.
29
+ *
30
+ * Normalizes the contract, computes `contractHash`, and either derives
31
+ * `revision` (the default) or validates and preserves an explicitly
32
+ * supplied one.
33
+ *
34
+ * Throws a plain `Error` when `options.revision` is present but empty, or
35
+ * exceeds `MAX_CONTRACT_IDENTIFIER_BYTES` — a caller-supplied identity is
36
+ * validated the same way any other contract identifier is, since it becomes
37
+ * part of the manifest a consumer will trust.
38
+ *
39
+ * **Round-trips through {@link parseWorkflowRevisionManifest} before
40
+ * returning.** A manifest this function builds but its own parser would
41
+ * reject (`identifier-too-long`, `too-many-entries`, `manifest-too-large`)
42
+ * is a build-time bug, not a runtime condition to defer to whoever later
43
+ * reads the manifest back — see WFT-5 PR #943 review thread
44
+ * PRRT_kwDORwthfM6eWFwv. Throws a plain `Error` describing the rejection
45
+ * reason when that happens.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { buildWorkflowContract, buildWorkflowRevisionManifest } from '@lostgradient/weft';
50
+ *
51
+ * const contract = buildWorkflowContract({ name: 'checkout', version: '2.1.0' });
52
+ * const manifest = await buildWorkflowRevisionManifest(contract);
53
+ * console.log(manifest.name, manifest.contractHash.startsWith('sha256:'));
54
+ * ```
55
+ */
56
+ export declare function buildWorkflowRevisionManifest(contract: WorkflowContract, options?: BuildWorkflowRevisionManifestOptions): Promise<WorkflowRevisionManifest>;
@@ -0,0 +1,31 @@
1
+ import { utf8ByteLength } from "../../worker/manifest/utf8.js";
2
+ import { contractHash } from "./hash.js";
3
+ import { MAX_CONTRACT_IDENTIFIER_BYTES } from "./limits.js";
4
+ import { parseWorkflowRevisionManifest } from "./manifest-parse.js";
5
+ import { normalizeWorkflowContract } from "./normalize.js";
6
+ import { deriveWorkflowRevision } from "./revision.js";
7
+ import { WORKFLOW_REVISION_MANIFEST_VERSION } from "./types.js";
8
+ export async function buildWorkflowRevisionManifest(contract, options) {
9
+ const normalized = normalizeWorkflowContract(contract), hash = await contractHash(normalized);
10
+ let revision;
11
+ if (options?.revision !== void 0) {
12
+ if (options.revision.length === 0)
13
+ throw Error("buildWorkflowRevisionManifest: options.revision must not be an empty string");
14
+ const bytes = utf8ByteLength(options.revision);
15
+ if (bytes > MAX_CONTRACT_IDENTIFIER_BYTES)
16
+ throw Error(`buildWorkflowRevisionManifest: options.revision is ${bytes} bytes, exceeding the maximum identifier size of ${MAX_CONTRACT_IDENTIFIER_BYTES}`);
17
+ revision = options.revision;
18
+ } else
19
+ revision = await deriveWorkflowRevision(normalized);
20
+ const manifest = {
21
+ manifestVersion: WORKFLOW_REVISION_MANIFEST_VERSION,
22
+ name: normalized.name,
23
+ workflowVersion: normalized.workflowVersion,
24
+ revision,
25
+ contractHash: hash,
26
+ contract: normalized
27
+ }, parsed = await parseWorkflowRevisionManifest(manifest);
28
+ if (!parsed.ok)
29
+ throw Error(`buildWorkflowRevisionManifest: built manifest fails parseWorkflowRevisionManifest validation (${parsed.reason}${parsed.path === void 0 ? "" : ` at ${parsed.path}`}): ` + parsed.message);
30
+ return parsed.manifest;
31
+ }