@xemahq/dsl 0.1.1

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 (288) hide show
  1. package/LICENSE +201 -0
  2. package/dist/deliverable-spec/index.d.ts +3 -0
  3. package/dist/deliverable-spec/index.d.ts.map +1 -0
  4. package/dist/deliverable-spec/index.js +19 -0
  5. package/dist/deliverable-spec/index.js.map +1 -0
  6. package/dist/deliverable-spec/lib/schema.d.ts +151 -0
  7. package/dist/deliverable-spec/lib/schema.d.ts.map +1 -0
  8. package/dist/deliverable-spec/lib/schema.js +139 -0
  9. package/dist/deliverable-spec/lib/schema.js.map +1 -0
  10. package/dist/deliverable-spec/lib/types.d.ts +8 -0
  11. package/dist/deliverable-spec/lib/types.d.ts.map +1 -0
  12. package/dist/deliverable-spec/lib/types.js +3 -0
  13. package/dist/deliverable-spec/lib/types.js.map +1 -0
  14. package/dist/payload-codec/index.d.ts +8 -0
  15. package/dist/payload-codec/index.d.ts.map +1 -0
  16. package/dist/payload-codec/index.js +27 -0
  17. package/dist/payload-codec/index.js.map +1 -0
  18. package/dist/payload-codec/lib/blob-store.d.ts +37 -0
  19. package/dist/payload-codec/lib/blob-store.d.ts.map +1 -0
  20. package/dist/payload-codec/lib/blob-store.js +0 -0
  21. package/dist/payload-codec/lib/blob-store.js.map +1 -0
  22. package/dist/payload-codec/lib/codec-context.d.ts +6 -0
  23. package/dist/payload-codec/lib/codec-context.d.ts.map +1 -0
  24. package/dist/payload-codec/lib/codec-context.js +16 -0
  25. package/dist/payload-codec/lib/codec-context.js.map +1 -0
  26. package/dist/payload-codec/lib/codec.d.ts +51 -0
  27. package/dist/payload-codec/lib/codec.d.ts.map +1 -0
  28. package/dist/payload-codec/lib/codec.js +330 -0
  29. package/dist/payload-codec/lib/codec.js.map +1 -0
  30. package/dist/payload-codec/lib/enums.d.ts +18 -0
  31. package/dist/payload-codec/lib/enums.d.ts.map +1 -0
  32. package/dist/payload-codec/lib/enums.js +23 -0
  33. package/dist/payload-codec/lib/enums.js.map +1 -0
  34. package/dist/payload-codec/lib/errors.d.ts +18 -0
  35. package/dist/payload-codec/lib/errors.d.ts.map +1 -0
  36. package/dist/payload-codec/lib/errors.js +39 -0
  37. package/dist/payload-codec/lib/errors.js.map +1 -0
  38. package/dist/payload-codec/lib/http-blob-store.d.ts +21 -0
  39. package/dist/payload-codec/lib/http-blob-store.d.ts.map +1 -0
  40. package/dist/payload-codec/lib/http-blob-store.js +139 -0
  41. package/dist/payload-codec/lib/http-blob-store.js.map +1 -0
  42. package/dist/payload-codec/lib/lru-cache.d.ts +12 -0
  43. package/dist/payload-codec/lib/lru-cache.d.ts.map +1 -0
  44. package/dist/payload-codec/lib/lru-cache.js +59 -0
  45. package/dist/payload-codec/lib/lru-cache.js.map +1 -0
  46. package/dist/schema/action.schema.json +181 -0
  47. package/dist/schema/reusable-workflow.schema.json +46 -0
  48. package/dist/schema/workflow.schema.json +373 -0
  49. package/dist/workflow/index.d.ts +14 -0
  50. package/dist/workflow/index.d.ts.map +1 -0
  51. package/dist/workflow/index.js +49 -0
  52. package/dist/workflow/index.js.map +1 -0
  53. package/dist/workflow/lib/action-input-validator.d.ts +10 -0
  54. package/dist/workflow/lib/action-input-validator.d.ts.map +1 -0
  55. package/dist/workflow/lib/action-input-validator.js +69 -0
  56. package/dist/workflow/lib/action-input-validator.js.map +1 -0
  57. package/dist/workflow/lib/compiler/action-shape.d.ts +5 -0
  58. package/dist/workflow/lib/compiler/action-shape.d.ts.map +1 -0
  59. package/dist/workflow/lib/compiler/action-shape.js +43 -0
  60. package/dist/workflow/lib/compiler/action-shape.js.map +1 -0
  61. package/dist/workflow/lib/compiler/canonical-json.d.ts +3 -0
  62. package/dist/workflow/lib/compiler/canonical-json.d.ts.map +1 -0
  63. package/dist/workflow/lib/compiler/canonical-json.js +45 -0
  64. package/dist/workflow/lib/compiler/canonical-json.js.map +1 -0
  65. package/dist/workflow/lib/compiler/compile.d.ts +4 -0
  66. package/dist/workflow/lib/compiler/compile.d.ts.map +1 -0
  67. package/dist/workflow/lib/compiler/compile.js +794 -0
  68. package/dist/workflow/lib/compiler/compile.js.map +1 -0
  69. package/dist/workflow/lib/compiler/concurrency.d.ts +5 -0
  70. package/dist/workflow/lib/compiler/concurrency.d.ts.map +1 -0
  71. package/dist/workflow/lib/compiler/concurrency.js +104 -0
  72. package/dist/workflow/lib/compiler/concurrency.js.map +1 -0
  73. package/dist/workflow/lib/compiler/dag.d.ts +10 -0
  74. package/dist/workflow/lib/compiler/dag.d.ts.map +1 -0
  75. package/dist/workflow/lib/compiler/dag.js +74 -0
  76. package/dist/workflow/lib/compiler/dag.js.map +1 -0
  77. package/dist/workflow/lib/compiler/index.d.ts +6 -0
  78. package/dist/workflow/lib/compiler/index.d.ts.map +1 -0
  79. package/dist/workflow/lib/compiler/index.js +14 -0
  80. package/dist/workflow/lib/compiler/index.js.map +1 -0
  81. package/dist/workflow/lib/compiler/inputs.d.ts +4 -0
  82. package/dist/workflow/lib/compiler/inputs.d.ts.map +1 -0
  83. package/dist/workflow/lib/compiler/inputs.js +108 -0
  84. package/dist/workflow/lib/compiler/inputs.js.map +1 -0
  85. package/dist/workflow/lib/compiler/installation-resource-validator.d.ts +9 -0
  86. package/dist/workflow/lib/compiler/installation-resource-validator.d.ts.map +1 -0
  87. package/dist/workflow/lib/compiler/installation-resource-validator.js +76 -0
  88. package/dist/workflow/lib/compiler/installation-resource-validator.js.map +1 -0
  89. package/dist/workflow/lib/compiler/manifest-source.d.ts +4 -0
  90. package/dist/workflow/lib/compiler/manifest-source.d.ts.map +1 -0
  91. package/dist/workflow/lib/compiler/manifest-source.js +100 -0
  92. package/dist/workflow/lib/compiler/manifest-source.js.map +1 -0
  93. package/dist/workflow/lib/compiler/matrix.d.ts +4 -0
  94. package/dist/workflow/lib/compiler/matrix.d.ts.map +1 -0
  95. package/dist/workflow/lib/compiler/matrix.js +76 -0
  96. package/dist/workflow/lib/compiler/matrix.js.map +1 -0
  97. package/dist/workflow/lib/compiler/mount-plan.d.ts +4 -0
  98. package/dist/workflow/lib/compiler/mount-plan.d.ts.map +1 -0
  99. package/dist/workflow/lib/compiler/mount-plan.js +96 -0
  100. package/dist/workflow/lib/compiler/mount-plan.js.map +1 -0
  101. package/dist/workflow/lib/compiler/payload-reach-in.d.ts +14 -0
  102. package/dist/workflow/lib/compiler/payload-reach-in.d.ts.map +1 -0
  103. package/dist/workflow/lib/compiler/payload-reach-in.js +273 -0
  104. package/dist/workflow/lib/compiler/payload-reach-in.js.map +1 -0
  105. package/dist/workflow/lib/compiler/permissions.d.ts +6 -0
  106. package/dist/workflow/lib/compiler/permissions.d.ts.map +1 -0
  107. package/dist/workflow/lib/compiler/permissions.js +43 -0
  108. package/dist/workflow/lib/compiler/permissions.js.map +1 -0
  109. package/dist/workflow/lib/compiler/retry-timeout.d.ts +6 -0
  110. package/dist/workflow/lib/compiler/retry-timeout.d.ts.map +1 -0
  111. package/dist/workflow/lib/compiler/retry-timeout.js +64 -0
  112. package/dist/workflow/lib/compiler/retry-timeout.js.map +1 -0
  113. package/dist/workflow/lib/compiler/review-step.d.ts +18 -0
  114. package/dist/workflow/lib/compiler/review-step.d.ts.map +1 -0
  115. package/dist/workflow/lib/compiler/review-step.js +247 -0
  116. package/dist/workflow/lib/compiler/review-step.js.map +1 -0
  117. package/dist/workflow/lib/compiler/types.d.ts +42 -0
  118. package/dist/workflow/lib/compiler/types.d.ts.map +1 -0
  119. package/dist/workflow/lib/compiler/types.js +3 -0
  120. package/dist/workflow/lib/compiler/types.js.map +1 -0
  121. package/dist/workflow/lib/compiler/variable-requirements.d.ts +5 -0
  122. package/dist/workflow/lib/compiler/variable-requirements.d.ts.map +1 -0
  123. package/dist/workflow/lib/compiler/variable-requirements.js +119 -0
  124. package/dist/workflow/lib/compiler/variable-requirements.js.map +1 -0
  125. package/dist/workflow/lib/deliverable-spec-keys.d.ts +3 -0
  126. package/dist/workflow/lib/deliverable-spec-keys.d.ts.map +1 -0
  127. package/dist/workflow/lib/deliverable-spec-keys.js +90 -0
  128. package/dist/workflow/lib/deliverable-spec-keys.js.map +1 -0
  129. package/dist/workflow/lib/dispatch-inputs/index.d.ts +23 -0
  130. package/dist/workflow/lib/dispatch-inputs/index.d.ts.map +1 -0
  131. package/dist/workflow/lib/dispatch-inputs/index.js +106 -0
  132. package/dist/workflow/lib/dispatch-inputs/index.js.map +1 -0
  133. package/dist/workflow/lib/dispatch-inputs/to-json-schema.d.ts +3 -0
  134. package/dist/workflow/lib/dispatch-inputs/to-json-schema.d.ts.map +1 -0
  135. package/dist/workflow/lib/dispatch-inputs/to-json-schema.js +43 -0
  136. package/dist/workflow/lib/dispatch-inputs/to-json-schema.js.map +1 -0
  137. package/dist/workflow/lib/duration.d.ts +2 -0
  138. package/dist/workflow/lib/duration.d.ts.map +1 -0
  139. package/dist/workflow/lib/duration.js +26 -0
  140. package/dist/workflow/lib/duration.js.map +1 -0
  141. package/dist/workflow/lib/errors.d.ts +9 -0
  142. package/dist/workflow/lib/errors.d.ts.map +1 -0
  143. package/dist/workflow/lib/errors.js +28 -0
  144. package/dist/workflow/lib/errors.js.map +1 -0
  145. package/dist/workflow/lib/expression/ast.d.ts +61 -0
  146. package/dist/workflow/lib/expression/ast.d.ts.map +1 -0
  147. package/dist/workflow/lib/expression/ast.js +34 -0
  148. package/dist/workflow/lib/expression/ast.js.map +1 -0
  149. package/dist/workflow/lib/expression/context.d.ts +63 -0
  150. package/dist/workflow/lib/expression/context.d.ts.map +1 -0
  151. package/dist/workflow/lib/expression/context.js +32 -0
  152. package/dist/workflow/lib/expression/context.js.map +1 -0
  153. package/dist/workflow/lib/expression/evaluator.d.ts +5 -0
  154. package/dist/workflow/lib/expression/evaluator.d.ts.map +1 -0
  155. package/dist/workflow/lib/expression/evaluator.js +291 -0
  156. package/dist/workflow/lib/expression/evaluator.js.map +1 -0
  157. package/dist/workflow/lib/expression/index.d.ts +9 -0
  158. package/dist/workflow/lib/expression/index.d.ts.map +1 -0
  159. package/dist/workflow/lib/expression/index.js +26 -0
  160. package/dist/workflow/lib/expression/index.js.map +1 -0
  161. package/dist/workflow/lib/expression/interpolation.d.ts +9 -0
  162. package/dist/workflow/lib/expression/interpolation.d.ts.map +1 -0
  163. package/dist/workflow/lib/expression/interpolation.js +51 -0
  164. package/dist/workflow/lib/expression/interpolation.js.map +1 -0
  165. package/dist/workflow/lib/expression/parser.d.ts +4 -0
  166. package/dist/workflow/lib/expression/parser.d.ts.map +1 -0
  167. package/dist/workflow/lib/expression/parser.js +203 -0
  168. package/dist/workflow/lib/expression/parser.js.map +1 -0
  169. package/dist/workflow/lib/expression/template.d.ts +18 -0
  170. package/dist/workflow/lib/expression/template.d.ts.map +1 -0
  171. package/dist/workflow/lib/expression/template.js +63 -0
  172. package/dist/workflow/lib/expression/template.js.map +1 -0
  173. package/dist/workflow/lib/expression/tokenizer.d.ts +3 -0
  174. package/dist/workflow/lib/expression/tokenizer.d.ts.map +1 -0
  175. package/dist/workflow/lib/expression/tokenizer.js +153 -0
  176. package/dist/workflow/lib/expression/tokenizer.js.map +1 -0
  177. package/dist/workflow/lib/expression/tokens.d.ts +25 -0
  178. package/dist/workflow/lib/expression/tokens.d.ts.map +1 -0
  179. package/dist/workflow/lib/expression/tokens.js +24 -0
  180. package/dist/workflow/lib/expression/tokens.js.map +1 -0
  181. package/dist/workflow/lib/expression/walk-artifact-refs.d.ts +5 -0
  182. package/dist/workflow/lib/expression/walk-artifact-refs.d.ts.map +1 -0
  183. package/dist/workflow/lib/expression/walk-artifact-refs.js +138 -0
  184. package/dist/workflow/lib/expression/walk-artifact-refs.js.map +1 -0
  185. package/dist/workflow/lib/installation-resource-kind.d.ts +14 -0
  186. package/dist/workflow/lib/installation-resource-kind.d.ts.map +1 -0
  187. package/dist/workflow/lib/installation-resource-kind.js +59 -0
  188. package/dist/workflow/lib/installation-resource-kind.js.map +1 -0
  189. package/dist/workflow/lib/schemas-loader.d.ts +4 -0
  190. package/dist/workflow/lib/schemas-loader.d.ts.map +1 -0
  191. package/dist/workflow/lib/schemas-loader.js +36 -0
  192. package/dist/workflow/lib/schemas-loader.js.map +1 -0
  193. package/dist/workflow/lib/serializer.d.ts +3 -0
  194. package/dist/workflow/lib/serializer.d.ts.map +1 -0
  195. package/dist/workflow/lib/serializer.js +15 -0
  196. package/dist/workflow/lib/serializer.js.map +1 -0
  197. package/dist/workflow/lib/types.d.ts +179 -0
  198. package/dist/workflow/lib/types.d.ts.map +1 -0
  199. package/dist/workflow/lib/types.js +3 -0
  200. package/dist/workflow/lib/types.js.map +1 -0
  201. package/dist/workflow/lib/validate.d.ts +8 -0
  202. package/dist/workflow/lib/validate.d.ts.map +1 -0
  203. package/dist/workflow/lib/validate.js +119 -0
  204. package/dist/workflow/lib/validate.js.map +1 -0
  205. package/dist/workspace-manifest/index.d.ts +6 -0
  206. package/dist/workspace-manifest/index.d.ts.map +1 -0
  207. package/dist/workspace-manifest/index.js +22 -0
  208. package/dist/workspace-manifest/index.js.map +1 -0
  209. package/dist/workspace-manifest/lib/compile.d.ts +8 -0
  210. package/dist/workspace-manifest/lib/compile.d.ts.map +1 -0
  211. package/dist/workspace-manifest/lib/compile.js +439 -0
  212. package/dist/workspace-manifest/lib/compile.js.map +1 -0
  213. package/dist/workspace-manifest/lib/interpolate.d.ts +12 -0
  214. package/dist/workspace-manifest/lib/interpolate.d.ts.map +1 -0
  215. package/dist/workspace-manifest/lib/interpolate.js +81 -0
  216. package/dist/workspace-manifest/lib/interpolate.js.map +1 -0
  217. package/dist/workspace-manifest/lib/resolve-extends.d.ts +10 -0
  218. package/dist/workspace-manifest/lib/resolve-extends.d.ts.map +1 -0
  219. package/dist/workspace-manifest/lib/resolve-extends.js +108 -0
  220. package/dist/workspace-manifest/lib/resolve-extends.js.map +1 -0
  221. package/dist/workspace-manifest/lib/schema.d.ts +710 -0
  222. package/dist/workspace-manifest/lib/schema.d.ts.map +1 -0
  223. package/dist/workspace-manifest/lib/schema.js +355 -0
  224. package/dist/workspace-manifest/lib/schema.js.map +1 -0
  225. package/dist/workspace-manifest/lib/types.d.ts +153 -0
  226. package/dist/workspace-manifest/lib/types.d.ts.map +1 -0
  227. package/dist/workspace-manifest/lib/types.js +10 -0
  228. package/dist/workspace-manifest/lib/types.js.map +1 -0
  229. package/package.json +79 -0
  230. package/schema/action.schema.json +181 -0
  231. package/schema/reusable-workflow.schema.json +46 -0
  232. package/schema/workflow.schema.json +373 -0
  233. package/src/deliverable-spec/index.ts +19 -0
  234. package/src/deliverable-spec/lib/schema.ts +248 -0
  235. package/src/deliverable-spec/lib/types.ts +26 -0
  236. package/src/payload-codec/index.ts +40 -0
  237. package/src/payload-codec/lib/blob-store.ts +0 -0
  238. package/src/payload-codec/lib/codec-context.ts +38 -0
  239. package/src/payload-codec/lib/codec.ts +593 -0
  240. package/src/payload-codec/lib/enums.ts +58 -0
  241. package/src/payload-codec/lib/errors.ts +54 -0
  242. package/src/payload-codec/lib/http-blob-store.ts +257 -0
  243. package/src/payload-codec/lib/lru-cache.ts +81 -0
  244. package/src/workflow/index.ts +98 -0
  245. package/src/workflow/lib/action-input-validator.ts +160 -0
  246. package/src/workflow/lib/compiler/action-shape.ts +71 -0
  247. package/src/workflow/lib/compiler/canonical-json.ts +53 -0
  248. package/src/workflow/lib/compiler/compile.ts +1518 -0
  249. package/src/workflow/lib/compiler/concurrency.ts +223 -0
  250. package/src/workflow/lib/compiler/dag.ts +108 -0
  251. package/src/workflow/lib/compiler/index.ts +10 -0
  252. package/src/workflow/lib/compiler/inputs.ts +199 -0
  253. package/src/workflow/lib/compiler/installation-resource-validator.ts +114 -0
  254. package/src/workflow/lib/compiler/manifest-source.ts +176 -0
  255. package/src/workflow/lib/compiler/matrix.ts +135 -0
  256. package/src/workflow/lib/compiler/mount-plan.ts +202 -0
  257. package/src/workflow/lib/compiler/payload-reach-in.ts +497 -0
  258. package/src/workflow/lib/compiler/permissions.ts +64 -0
  259. package/src/workflow/lib/compiler/retry-timeout.ts +105 -0
  260. package/src/workflow/lib/compiler/review-step.ts +517 -0
  261. package/src/workflow/lib/compiler/types.ts +170 -0
  262. package/src/workflow/lib/compiler/variable-requirements.ts +208 -0
  263. package/src/workflow/lib/deliverable-spec-keys.ts +109 -0
  264. package/src/workflow/lib/dispatch-inputs/index.ts +160 -0
  265. package/src/workflow/lib/dispatch-inputs/to-json-schema.ts +60 -0
  266. package/src/workflow/lib/duration.ts +48 -0
  267. package/src/workflow/lib/errors.ts +37 -0
  268. package/src/workflow/lib/expression/ast.ts +108 -0
  269. package/src/workflow/lib/expression/context.ts +148 -0
  270. package/src/workflow/lib/expression/evaluator.ts +492 -0
  271. package/src/workflow/lib/expression/index.ts +28 -0
  272. package/src/workflow/lib/expression/interpolation.ts +84 -0
  273. package/src/workflow/lib/expression/parser.ts +264 -0
  274. package/src/workflow/lib/expression/template.ts +117 -0
  275. package/src/workflow/lib/expression/tokenizer.ts +200 -0
  276. package/src/workflow/lib/expression/tokens.ts +30 -0
  277. package/src/workflow/lib/expression/walk-artifact-refs.ts +232 -0
  278. package/src/workflow/lib/installation-resource-kind.ts +107 -0
  279. package/src/workflow/lib/schemas-loader.ts +64 -0
  280. package/src/workflow/lib/serializer.ts +30 -0
  281. package/src/workflow/lib/types.ts +361 -0
  282. package/src/workflow/lib/validate.ts +199 -0
  283. package/src/workspace-manifest/index.ts +27 -0
  284. package/src/workspace-manifest/lib/compile.ts +608 -0
  285. package/src/workspace-manifest/lib/interpolate.ts +140 -0
  286. package/src/workspace-manifest/lib/resolve-extends.ts +260 -0
  287. package/src/workspace-manifest/lib/schema.ts +612 -0
  288. package/src/workspace-manifest/lib/types.ts +392 -0
@@ -0,0 +1,232 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // ── Runtime walker — find every AST sub-expression that resolves to an
3
+ // ArtifactRef the runtime must pre-fetch (plan §C.1 / §C.2). ──
4
+ //
5
+ // Two flavours of pre-fetch target:
6
+ //
7
+ // 1. Payload reach-in — `needs.<X>.outputs.<name>.<payload-field>` where
8
+ // `<payload-field>` is NOT an ArtifactRef envelope field. The
9
+ // evaluator reads the field via `ctx.payloadCache.get(versionId)`,
10
+ // so the runtime must resolve the BASE expression
11
+ // (`needs.<X>.outputs.<name>` or its indexed / keyed variants) to a
12
+ // concrete `ArtifactRef` and pre-fetch the payload.
13
+ //
14
+ // 2. `fromJSON(<expr>)` — the argument expression should evaluate to
15
+ // an ArtifactRef whose payload the evaluator returns via
16
+ // `payloadCache.get(versionId)`. The runtime pre-fetches the same
17
+ // way; the difference from #1 is that `fromJSON` is the documented
18
+ // escape hatch (compile-time skipped) so the walker just yields
19
+ // the argument node verbatim.
20
+ //
21
+ // The walker yields BASE NODES. The caller (worker prefetch helper)
22
+ // evaluates each yielded node against `ExpressionContext`, normalizes
23
+ // the result to `ArtifactRef[]`, dedupes by `versionId`, and fetches.
24
+ //
25
+ // Workflow-sandbox-safe: zero imports beyond pure AST types. Lives in
26
+ // the `expression/` subpath so workflow code (which can't import the
27
+ // compiler graph) can call it directly.
28
+ // ═══════════════════════════════════════════════════════════════════════════
29
+
30
+ import { isArtifactRefEnvelopeField } from '@xemahq/kernel-contracts/workflow';
31
+
32
+ import {
33
+ ExpressionFunction,
34
+ ExpressionNodeKind,
35
+ type ExpressionNode,
36
+ } from './ast';
37
+
38
+ /**
39
+ * Yield every sub-expression in `node` that should resolve to an
40
+ * ArtifactRef (or array of refs) for runtime pre-fetch. The caller is
41
+ * responsible for evaluating each yielded node against an
42
+ * `ExpressionContext` and shaping the result.
43
+ *
44
+ * Important: the walker also recurses INTO yielded matches so a nested
45
+ * reach-in inside e.g. `byKey[fromJSON(needs.X.outputs.foo).key]` still
46
+ * surfaces both the outer reach-in's base AND the inner `fromJSON` arg.
47
+ */
48
+ export function walkRuntimeArtifactRefTargets(
49
+ node: ExpressionNode,
50
+ visit: (baseNode: ExpressionNode) => void,
51
+ ): void {
52
+ walk(node, visit);
53
+ }
54
+
55
+ function walk(
56
+ node: ExpressionNode,
57
+ visit: (baseNode: ExpressionNode) => void,
58
+ ): void {
59
+ // Reach-in: `<base>.<field>` where `<base>` resolves to a `needs.X.outputs.<name>`
60
+ // chain (with optional `[N]` / `byKey['k']` indexing) and `<field>` is NOT an
61
+ // envelope field. The base node IS the artifact-ref expression.
62
+ if (node.kind === ExpressionNodeKind.MEMBER) {
63
+ if (!isArtifactRefEnvelopeField(node.property)) {
64
+ const base = node.target;
65
+ if (resolvesToOutputsRef(base)) {
66
+ visit(base);
67
+ }
68
+ }
69
+ }
70
+
71
+ // `fromJSON(<expr>)` — escape hatch. The argument expression must
72
+ // evaluate to an ArtifactRef at runtime; pre-fetch its payload.
73
+ // (String-arg `fromJSON(...)` is also valid GitHub-Actions semantics
74
+ // for parsing literal JSON; the prefetch helper detects non-ref
75
+ // results and skips them — no pre-fetch needed.)
76
+ if (
77
+ node.kind === ExpressionNodeKind.CALL &&
78
+ node.callee === ExpressionFunction.FROM_JSON
79
+ ) {
80
+ for (const arg of node.args) {
81
+ visit(arg);
82
+ }
83
+ }
84
+
85
+ // Recurse — same shape the compile-time walker uses, but WITHOUT
86
+ // skipping fromJSON args (the compiler skips them as the documented
87
+ // escape hatch; the runtime needs them to fetch).
88
+ switch (node.kind) {
89
+ case ExpressionNodeKind.MEMBER:
90
+ walk(node.target, visit);
91
+ return;
92
+ case ExpressionNodeKind.INDEX:
93
+ walk(node.target, visit);
94
+ walk(node.index, visit);
95
+ return;
96
+ case ExpressionNodeKind.CALL:
97
+ for (const arg of node.args) walk(arg, visit);
98
+ return;
99
+ case ExpressionNodeKind.UNARY_NOT:
100
+ walk(node.operand, visit);
101
+ return;
102
+ case ExpressionNodeKind.BINARY_EQ:
103
+ case ExpressionNodeKind.BINARY_NEQ:
104
+ case ExpressionNodeKind.BINARY_AND:
105
+ case ExpressionNodeKind.BINARY_OR:
106
+ walk(node.left, visit);
107
+ walk(node.right, visit);
108
+ return;
109
+ case ExpressionNodeKind.LITERAL:
110
+ case ExpressionNodeKind.IDENTIFIER:
111
+ return;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Does the AST node resolve (structurally) to one of:
117
+ *
118
+ * - `needs.<X>.outputs.<name>` (single output)
119
+ * - `needs.<X>.outputs[<N>]` (indexed positional)
120
+ * - `needs.<X>.outputs.byKey['<k>']` (keyed matrix access)
121
+ *
122
+ * Returning true means evaluating this node against `ctx` is EXPECTED
123
+ * to yield an ArtifactRef (or ArtifactRef[]); the prefetch helper
124
+ * checks the actual shape at runtime and silently skips non-refs (a
125
+ * mid-chain result that's a plain object means the author wrote
126
+ * something the evaluator will reject when it actually runs — let the
127
+ * evaluator surface the error there, not at prefetch time).
128
+ */
129
+ function resolvesToOutputsRef(node: ExpressionNode): boolean {
130
+ // Form A: needs.<X>.outputs.<name>
131
+ if (node.kind === ExpressionNodeKind.MEMBER) {
132
+ if (node.property === 'byKey') {
133
+ // `outputs.byKey` by itself isn't a ref — only `outputs.byKey['k']`
134
+ // (the INDEX node) is. Return false; the INDEX form is matched below.
135
+ return false;
136
+ }
137
+ return isNeedsOutputsRoot(node.target);
138
+ }
139
+
140
+ // Form B: needs.<X>.outputs[<N>] OR needs.<X>.outputs.byKey['<k>']
141
+ if (node.kind === ExpressionNodeKind.INDEX) {
142
+ const indexed = node.target;
143
+ if (isNeedsOutputsRoot(indexed)) {
144
+ return true;
145
+ }
146
+ if (
147
+ indexed.kind === ExpressionNodeKind.MEMBER &&
148
+ indexed.property === 'byKey' &&
149
+ isNeedsOutputsRoot(indexed.target)
150
+ ) {
151
+ return true;
152
+ }
153
+ }
154
+
155
+ return false;
156
+ }
157
+
158
+ /** Match `needs.<X>.outputs`. */
159
+ function isNeedsOutputsRoot(node: ExpressionNode): boolean {
160
+ if (node.kind !== ExpressionNodeKind.MEMBER) return false;
161
+ if (node.property !== 'outputs') return false;
162
+ const needsRoot = node.target;
163
+ if (needsRoot.kind !== ExpressionNodeKind.MEMBER) return false;
164
+ const ident = needsRoot.target;
165
+ if (ident.kind !== ExpressionNodeKind.IDENTIFIER) return false;
166
+ if (ident.name !== 'needs') return false;
167
+ return true;
168
+ }
169
+
170
+ /**
171
+ * Extract `<X>` from a base node whose shape is
172
+ * `needs.<X>.outputs.<name>` (or its indexed / keyed variants). Returns
173
+ * `null` when the base doesn't structurally resolve to a needs-outputs
174
+ * chain — `fromJSON(...)` arguments and dynamically-shaped refs land
175
+ * here.
176
+ */
177
+ export function extractUpstreamJobKey(node: ExpressionNode): string | null {
178
+ // Form A: needs.<X>.outputs.<name>
179
+ if (node.kind === ExpressionNodeKind.MEMBER) {
180
+ const outputsNode = node.target;
181
+ return extractFromOutputsRoot(outputsNode);
182
+ }
183
+ // Form B: needs.<X>.outputs[N] OR needs.<X>.outputs.byKey['k']
184
+ if (node.kind === ExpressionNodeKind.INDEX) {
185
+ const indexed = node.target;
186
+ const fromOutputs = extractFromOutputsRoot(indexed);
187
+ if (fromOutputs !== null) return fromOutputs;
188
+ if (
189
+ indexed.kind === ExpressionNodeKind.MEMBER &&
190
+ indexed.property === 'byKey'
191
+ ) {
192
+ return extractFromOutputsRoot(indexed.target);
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+
198
+ /**
199
+ * Extract the upstream output name from a base node, when the shape is
200
+ * `needs.<X>.outputs.<name>` (Form A). Returns `null` for indexed /
201
+ * keyed access (Forms B) because the output name isn't statically
202
+ * known there.
203
+ *
204
+ * Pair with `extractUpstreamJobKey` to resolve per-output spec pins at
205
+ * runtime (plan §B). Falling back to `null` is the cue the prefetch
206
+ * helper uses to consult the job-level pin instead.
207
+ */
208
+ export function extractUpstreamOutputName(
209
+ node: ExpressionNode,
210
+ ): string | null {
211
+ if (node.kind !== ExpressionNodeKind.MEMBER) return null;
212
+ const outputsNode = node.target;
213
+ if (outputsNode.kind !== ExpressionNodeKind.MEMBER) return null;
214
+ if (outputsNode.property !== 'outputs') return null;
215
+ const needsRoot = outputsNode.target;
216
+ if (needsRoot.kind !== ExpressionNodeKind.MEMBER) return null;
217
+ const ident = needsRoot.target;
218
+ if (ident.kind !== ExpressionNodeKind.IDENTIFIER) return null;
219
+ if (ident.name !== 'needs') return null;
220
+ return node.property;
221
+ }
222
+
223
+ function extractFromOutputsRoot(node: ExpressionNode): string | null {
224
+ if (node.kind !== ExpressionNodeKind.MEMBER) return null;
225
+ if (node.property !== 'outputs') return null;
226
+ const needsRoot = node.target;
227
+ if (needsRoot.kind !== ExpressionNodeKind.MEMBER) return null;
228
+ const ident = needsRoot.target;
229
+ if (ident.kind !== ExpressionNodeKind.IDENTIFIER) return null;
230
+ if (ident.name !== 'needs') return null;
231
+ return needsRoot.property;
232
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Closed enum of installation-resource kinds the compile-time validator
3
+ * understands. Mirrored on the JSON Schema custom keyword `x-installation-resource`
4
+ * (see action-input-validator.ts) and on the workflow-engine compiler's
5
+ * installation-resource resolver.
6
+ *
7
+ * Adding a kind here is a 3-step change:
8
+ * 1. Add the enum value below.
9
+ * 2. Extend `addKeyword.metaSchema.properties.kind.enum` in
10
+ * action-input-validator.ts.
11
+ * 3. Teach the compiler-side `InstallationResourceResolverService`
12
+ * how to query bound resources of that kind from biome-host-api.
13
+ */
14
+ export enum InstallationResourceKind {
15
+ /** SMTP / IMAP wallet — `OrgIntegrationCredential` row attached to the installation. */
16
+ Wallet = 'wallet',
17
+ /** SCM repository binding on the installation. */
18
+ Repo = 'repo',
19
+ /** Tracker (Jira / Linear) project binding. */
20
+ Project = 'project',
21
+ /** Messaging (Slack / Discord) channel binding. */
22
+ Channel = 'channel',
23
+ /** Documentation (Confluence / KB) space binding. */
24
+ Space = 'space',
25
+ }
26
+
27
+ /**
28
+ * Per-field hint extracted from an action manifest's `spec.inputs` JSON
29
+ * Schema. The compiler-side validator walks the schema, finds fields
30
+ * carrying `x-installation-resource`, and produces one of these for
31
+ * each hit. Path is dotted (`walletId`, `defaults.repoId`, …) and
32
+ * matches the layout of the caller's `with:` block.
33
+ */
34
+ export interface InstallationResourceFieldHint {
35
+ readonly path: string;
36
+ readonly kind: InstallationResourceKind;
37
+ /** When true, an empty / missing value at this path is allowed. */
38
+ readonly optional: boolean;
39
+ }
40
+
41
+ /**
42
+ * Walks a JSON Schema object and collects every field annotated with
43
+ * `x-installation-resource: { kind: ... }`. Object + array properties
44
+ * are descended into; oneOf / anyOf / allOf branches are NOT — actions
45
+ * that need a wallet input should declare it on the top-level shape,
46
+ * not behind a union. Refs aren't followed either (action manifests
47
+ * are pinned at compile time and use inline schemas).
48
+ *
49
+ * The traversal is pure and deterministic — call it once per
50
+ * `ActionRef.inputsSchema` and cache the result alongside the schema.
51
+ */
52
+ export function collectInstallationResourceHints(
53
+ schema: unknown,
54
+ ): InstallationResourceFieldHint[] {
55
+ const out: InstallationResourceFieldHint[] = [];
56
+ walk(schema, '', new Set<string>(), out);
57
+ return out;
58
+ }
59
+
60
+ function walk(
61
+ node: unknown,
62
+ path: string,
63
+ required: ReadonlySet<string>,
64
+ out: InstallationResourceFieldHint[],
65
+ ): void {
66
+ if (!node || typeof node !== 'object') {return;}
67
+ const obj = node as Record<string, unknown>;
68
+ const hint = obj['x-installation-resource'];
69
+ if (hint && typeof hint === 'object') {
70
+ const kind = (hint as Record<string, unknown>)['kind'];
71
+ if (typeof kind === 'string' && isInstallationResourceKind(kind)) {
72
+ out.push({
73
+ path,
74
+ kind,
75
+ optional: !required.has(lastSegment(path)),
76
+ });
77
+ }
78
+ }
79
+ const properties = obj['properties'];
80
+ if (properties && typeof properties === 'object') {
81
+ const reqList = Array.isArray(obj['required'])
82
+ ? new Set(obj['required'].filter((x): x is string => typeof x === 'string'))
83
+ : new Set<string>();
84
+ for (const [key, child] of Object.entries(properties as Record<string, unknown>)) {
85
+ walk(child, path === '' ? key : `${path}.${key}`, reqList, out);
86
+ }
87
+ }
88
+ const items = obj['items'];
89
+ if (items) {
90
+ walk(items, path === '' ? '[*]' : `${path}[*]`, new Set(), out);
91
+ }
92
+ }
93
+
94
+ function lastSegment(path: string): string {
95
+ const dot = path.lastIndexOf('.');
96
+ return dot >= 0 ? path.slice(dot + 1) : path;
97
+ }
98
+
99
+ function isInstallationResourceKind(value: string): value is InstallationResourceKind {
100
+ return (
101
+ value === InstallationResourceKind.Wallet ||
102
+ value === InstallationResourceKind.Repo ||
103
+ value === InstallationResourceKind.Project ||
104
+ value === InstallationResourceKind.Channel ||
105
+ value === InstallationResourceKind.Space
106
+ );
107
+ }
@@ -0,0 +1,64 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { WorkflowErrorCode } from '@xemahq/kernel-contracts/workflow';
4
+ import { WorkflowDslError } from './errors';
5
+
6
+ /**
7
+ * Resolves the schema/ directory relative to this compiled file. Module
8
+ * load MUST succeed if the package is installed correctly; missing
9
+ * schemas are a packaging bug (see scripts/copy-schemas.mjs).
10
+ *
11
+ * We intentionally resolve at module-load time and read the JSON files
12
+ * synchronously — Ajv compiles once at module load and every subsequent
13
+ * validate() call is lock-free. Package is published as CommonJS (see
14
+ * tsconfig.base.json module:commonjs), so `__dirname` is always defined.
15
+ */
16
+ function schemasRoot(): string {
17
+ // This module lives at `<sub>/lib/schemas-loader` inside the @xemahq/dsl
18
+ // bundle (compiled `dist/workflow/lib/...`, source `src/workflow/lib/...`).
19
+ // Resolution order (first reachable wins):
20
+ // 1. `dist/schema/` — install-time, copied by scripts/copy-schemas.mjs
21
+ // into the package-root `dist/schema` (two levels up from dist/workflow/lib).
22
+ // 2. package-root `schema/` — the authored source schemas (three levels up
23
+ // from either dist/workflow/lib or src/workflow/lib, i.e. xema-source dev mode).
24
+ // If neither is reachable, fail fast — the package is malformed.
25
+ const distSchema = join(__dirname, '..', '..', 'schema');
26
+ const packageRootSchema = join(__dirname, '..', '..', '..', 'schema');
27
+ const candidates = [distSchema, packageRootSchema];
28
+ for (const candidate of candidates) {
29
+ try {
30
+ readFileSync(join(candidate, 'workflow.schema.json'), 'utf8');
31
+ return candidate;
32
+ } catch {
33
+ continue;
34
+ }
35
+ }
36
+ throw new WorkflowDslError(
37
+ WorkflowErrorCode.COMPILE_INTERNAL,
38
+ '@xemahq/dsl/workflow: schema/ directory not found. Package is malformed.',
39
+ { tried: candidates },
40
+ );
41
+ }
42
+
43
+ function readJsonSchema(fileName: string): Readonly<Record<string, unknown>> {
44
+ const filePath = join(schemasRoot(), fileName);
45
+ const raw = readFileSync(filePath, 'utf8');
46
+ try {
47
+ return JSON.parse(raw) as Readonly<Record<string, unknown>>;
48
+ } catch (err) {
49
+ throw new WorkflowDslError(
50
+ WorkflowErrorCode.COMPILE_INTERNAL,
51
+ `Failed to parse ${fileName}: ${(err as Error).message}`,
52
+ { fileName },
53
+ );
54
+ }
55
+ }
56
+
57
+ /** Schema object for the canonical Workflow document. */
58
+ export const WORKFLOW_SCHEMA = readJsonSchema('workflow.schema.json');
59
+
60
+ /** Schema object for a reusable workflow (must declare `on.workflow_call`). */
61
+ export const REUSABLE_WORKFLOW_SCHEMA = readJsonSchema('reusable-workflow.schema.json');
62
+
63
+ /** Schema object for a single action manifest. */
64
+ export const ACTION_SCHEMA = readJsonSchema('action.schema.json');
@@ -0,0 +1,30 @@
1
+ import { dump as dumpYaml } from 'js-yaml';
2
+
3
+ import type { WorkflowDocument } from './types';
4
+
5
+ /**
6
+ * Render a validated `WorkflowDocument` back to YAML.
7
+ *
8
+ * Note on comment preservation: this serializer does NOT carry over
9
+ * source comments from the original YAML — it dumps the AST verbatim.
10
+ * Use it when the canonical representation lives in the AST (e.g. the
11
+ * canvas editor in PR 5.x) and the YAML view is a derived presentation.
12
+ * If you need to apply a single mutation to existing hand-authored YAML
13
+ * while keeping comments + key ordering intact, use the `yaml`
14
+ * package's CST/Document API directly (follow-up; not in this PR).
15
+ *
16
+ * Output style is tuned for human-readable diffs:
17
+ * - block scalars (flow disabled),
18
+ * - 2-space indent,
19
+ * - long lines unwrapped so URLs / expressions don't fold mid-token.
20
+ */
21
+ export function serializeWorkflowDocument(doc: WorkflowDocument): string {
22
+ return dumpYaml(doc as unknown as Record<string, unknown>, {
23
+ indent: 2,
24
+ lineWidth: 120,
25
+ noRefs: true,
26
+ sortKeys: false,
27
+ quotingType: '"',
28
+ forceQuotes: false,
29
+ });
30
+ }