@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,223 @@
1
+ import {
2
+ WorkflowErrorCode,
3
+ WorkflowTriggerKind,
4
+ type ConcurrencyGroup,
5
+ type TriggerPayload,
6
+ } from '@xemahq/kernel-contracts/workflow';
7
+ import { WorkflowDslError } from '../errors';
8
+ import type { WorkflowConcurrencyDeclaration } from '../types';
9
+ import {
10
+ EMPTY_CONTEXT_SEEDS,
11
+ XemaActorKind,
12
+ compileExpression,
13
+ evaluateExpression,
14
+ parseTemplate,
15
+ TemplateSegmentKind,
16
+ type ExpressionContext,
17
+ type ExpressionNode,
18
+ type TemplateSegment,
19
+ } from '../expression';
20
+
21
+ /**
22
+ * Deterministically expand the authored concurrency `group` template into a
23
+ * concrete lease key. Two passes:
24
+ *
25
+ * 1. **Compile time** (`compileConcurrencyGroupTemplate`): tokenize and
26
+ * pre-compile every embedded expression. Authoring errors fire here so
27
+ * they block snapshot creation, not dispatch.
28
+ * 2. **Dispatch time** (`expandCompiledConcurrencyTemplate`): evaluate each
29
+ * pre-compiled expression against the trigger / inputs / vars context
30
+ * and stitch the literal + evaluated segments back together.
31
+ *
32
+ * Concurrency `group` is the one DSL field that opts in to partial
33
+ * interpolation (`product-development:${{ inputs.projectId }}`). Every
34
+ * other authored expression site enforces the "fully wrapped or pure
35
+ * literal" policy through `extractInterpolations` — see `compile.ts`.
36
+ */
37
+
38
+ interface CompiledTemplateExpressionSegment {
39
+ readonly kind: TemplateSegmentKind.Expression;
40
+ readonly source: string;
41
+ readonly node: ExpressionNode;
42
+ }
43
+
44
+ interface CompiledTemplateLiteralSegment {
45
+ readonly kind: TemplateSegmentKind.Literal;
46
+ readonly value: string;
47
+ }
48
+
49
+ type CompiledTemplateSegment =
50
+ | CompiledTemplateExpressionSegment
51
+ | CompiledTemplateLiteralSegment;
52
+
53
+ /**
54
+ * Tokenize and pre-compile every interpolation in the authored template.
55
+ * Pure function: same input always yields the same compiled segments.
56
+ */
57
+ function compileConcurrencyGroupTemplate(
58
+ authored: string,
59
+ ): readonly CompiledTemplateSegment[] {
60
+ const segments = parseTemplate(authored, ['concurrency', 'group']);
61
+ return segments.map((segment) => compileSegment(segment));
62
+ }
63
+
64
+ function compileSegment(segment: TemplateSegment): CompiledTemplateSegment {
65
+ if (segment.kind === TemplateSegmentKind.Literal) {
66
+ return { kind: TemplateSegmentKind.Literal, value: segment.value };
67
+ }
68
+ return {
69
+ kind: TemplateSegmentKind.Expression,
70
+ source: segment.source,
71
+ node: compileExpression(segment.source),
72
+ };
73
+ }
74
+
75
+ function expandCompiledConcurrencyTemplate(
76
+ authored: string,
77
+ segments: readonly CompiledTemplateSegment[],
78
+ ctx: ExpressionContext,
79
+ ): string {
80
+ const out: string[] = [];
81
+ for (const segment of segments) {
82
+ if (segment.kind === TemplateSegmentKind.Literal) {
83
+ out.push(segment.value);
84
+ continue;
85
+ }
86
+ const value = evaluateExpression(segment.node, ctx);
87
+ out.push(stringifyConcurrencyValue(authored, segment.source, value));
88
+ }
89
+ return out.join('');
90
+ }
91
+
92
+ function stringifyConcurrencyValue(
93
+ authored: string,
94
+ expressionSource: string,
95
+ value: unknown,
96
+ ): string {
97
+ if (value === null || value === undefined) {
98
+ throw new WorkflowDslError(
99
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
100
+ `Concurrency group expression '${expressionSource}' resolved to ${value === null ? 'null' : 'undefined'} — refuse to build an ambiguous lease key.`,
101
+ { authored, expression: expressionSource },
102
+ );
103
+ }
104
+ if (
105
+ typeof value !== 'string' &&
106
+ typeof value !== 'number' &&
107
+ typeof value !== 'boolean'
108
+ ) {
109
+ throw new WorkflowDslError(
110
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
111
+ `Concurrency group expression '${expressionSource}' must resolve to string/number/boolean; got ${typeof value}.`,
112
+ { authored, expression: expressionSource, resultKind: typeof value },
113
+ );
114
+ }
115
+ return String(value);
116
+ }
117
+
118
+ function buildConcurrencyContext(
119
+ trigger: TriggerPayload,
120
+ inputs: Readonly<Record<string, unknown>>,
121
+ vars: Readonly<Record<string, unknown>>,
122
+ ): ExpressionContext {
123
+ // Concurrency runs before any job: no `needs`, no matrix, no `job`.
124
+ // `xema.*` is seeded with the run-level identity available at this
125
+ // point (org/project/correlation/actor) so authors may key concurrency
126
+ // groups off `xema.org.id` or `xema.project.id` without reaching into
127
+ // trigger metadata.
128
+ return {
129
+ needs: EMPTY_CONTEXT_SEEDS.needs,
130
+ matrix: EMPTY_CONTEXT_SEEDS.matrix,
131
+ inputs,
132
+ vars,
133
+ secrets: EMPTY_CONTEXT_SEEDS.secrets,
134
+ trigger,
135
+ job: EMPTY_CONTEXT_SEEDS.job,
136
+ payloadCache: EMPTY_CONTEXT_SEEDS.payloadCache,
137
+ xema: {
138
+ org: { id: trigger.orgId },
139
+ project: { id: trigger.projectId },
140
+ run: {
141
+ id: '',
142
+ attempt: 0,
143
+ startedAtIso: trigger.triggeredAt,
144
+ correlationId: trigger.correlationId,
145
+ actor: {
146
+ subject: trigger.actorSubject,
147
+ kind: actorKindFromTrigger(trigger),
148
+ },
149
+ },
150
+ workflow: { key: '', version: '' },
151
+ job: { key: '', attempt: 0 },
152
+ },
153
+ };
154
+ }
155
+
156
+ function actorKindFromTrigger(trigger: TriggerPayload): XemaActorKind {
157
+ if (trigger.actorSubject) return XemaActorKind.User;
158
+ switch (trigger.kind) {
159
+ case WorkflowTriggerKind.WEBHOOK:
160
+ return XemaActorKind.Webhook;
161
+ case WorkflowTriggerKind.SCHEDULE:
162
+ return XemaActorKind.Schedule;
163
+ default:
164
+ return XemaActorKind.System;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Compile + expand the authored concurrency declaration into the runtime
170
+ * `ConcurrencyGroup`. Returns `null` when the workflow declares no
171
+ * concurrency at all.
172
+ *
173
+ * Compile-time errors (malformed template, malformed expression body) and
174
+ * dispatch-time errors (expression evaluates to null/undefined or a
175
+ * non-stringifiable type, expanded key is empty) all surface as typed
176
+ * `WorkflowDslError`s — never silently degraded to a default group.
177
+ */
178
+ export function compileConcurrency(
179
+ declaration: WorkflowConcurrencyDeclaration | undefined,
180
+ trigger: TriggerPayload,
181
+ inputs: Readonly<Record<string, unknown>>,
182
+ vars: Readonly<Record<string, unknown>>,
183
+ ): ConcurrencyGroup | null {
184
+ if (!declaration) return null;
185
+
186
+ const compiledSegments = compileConcurrencyGroupTemplate(declaration.group);
187
+ const ctx = buildConcurrencyContext(trigger, inputs, vars);
188
+ const leaseKey = expandCompiledConcurrencyTemplate(
189
+ declaration.group,
190
+ compiledSegments,
191
+ ctx,
192
+ );
193
+
194
+ if (leaseKey.length === 0) {
195
+ throw new WorkflowDslError(
196
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
197
+ `Concurrency group expression '${declaration.group}' expanded to an empty string.`,
198
+ { authored: declaration.group },
199
+ );
200
+ }
201
+
202
+ return {
203
+ leaseKey,
204
+ mode: declaration.mode,
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Compile-time-only validation hook: parse and pre-compile the concurrency
210
+ * template so authoring errors block snapshot creation. Used by
211
+ * `validateAllAuthoredExpressions` in the main compiler pass; the result is
212
+ * discarded because the dispatch path recompiles from the same source
213
+ * (determinism is preserved by the canonical workflow document hash).
214
+ */
215
+ export function validateConcurrencyGroupTemplate(
216
+ declaration: WorkflowConcurrencyDeclaration | undefined,
217
+ ): void {
218
+ if (!declaration) return;
219
+ // Parse + compile every embedded expression; throw on any syntax error.
220
+ // The returned segments are intentionally discarded — the dispatch path
221
+ // recompiles from the same source string (deterministic).
222
+ compileConcurrencyGroupTemplate(declaration.group);
223
+ }
@@ -0,0 +1,108 @@
1
+ import { WorkflowErrorCode } from '@xemahq/kernel-contracts/workflow';
2
+ import { WorkflowDslError } from '../errors';
3
+
4
+ /**
5
+ * Topological sort for the job DAG. Returns jobs in a stable order where
6
+ * every job appears after all its `needs` predecessors.
7
+ *
8
+ * Cycle detection: Kahn's algorithm. If any node remains after the queue
9
+ * drains, there's a cycle. Stable order: within each ready set, jobs are
10
+ * visited in input order (so authoring order drives UI layout).
11
+ *
12
+ * Missing-need detection: if any `needs` entry references a jobKey that
13
+ * isn't in the graph, fail fast (can't be silently dropped — would hide
14
+ * a copy-paste bug).
15
+ */
16
+ export function topologicalSort<T>(
17
+ nodes: readonly { key: string; payload: T; needs: readonly string[] }[],
18
+ ): readonly { key: string; payload: T; needs: readonly string[] }[] {
19
+ const byKey = new Map<string, { key: string; payload: T; needs: readonly string[] }>();
20
+ const inputOrder = new Map<string, number>();
21
+ nodes.forEach((node, i) => {
22
+ if (byKey.has(node.key)) {
23
+ throw new WorkflowDslError(
24
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
25
+ `Duplicate job key: ${node.key}`,
26
+ { key: node.key },
27
+ );
28
+ }
29
+ byKey.set(node.key, node);
30
+ inputOrder.set(node.key, i);
31
+ });
32
+
33
+ // Validate needs references before building indegree — unknown refs are
34
+ // a structural error, not a cycle error.
35
+ for (const node of nodes) {
36
+ for (const needKey of node.needs) {
37
+ if (!byKey.has(needKey)) {
38
+ throw new WorkflowDslError(
39
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
40
+ `Job '${node.key}' declares needs: [${needKey}] but no such job exists.`,
41
+ { jobKey: node.key, missingNeed: needKey },
42
+ );
43
+ }
44
+ if (needKey === node.key) {
45
+ throw new WorkflowDslError(
46
+ WorkflowErrorCode.DSL_CYCLE_DETECTED,
47
+ `Job '${node.key}' cannot depend on itself.`,
48
+ { jobKey: node.key },
49
+ );
50
+ }
51
+ }
52
+ }
53
+
54
+ const indegree = new Map<string, number>();
55
+ const dependents = new Map<string, string[]>();
56
+ for (const node of nodes) {
57
+ indegree.set(node.key, node.needs.length);
58
+ dependents.set(node.key, []);
59
+ }
60
+ for (const node of nodes) {
61
+ for (const needKey of node.needs) {
62
+ dependents.get(needKey)!.push(node.key);
63
+ }
64
+ }
65
+
66
+ const ready: string[] = [];
67
+ for (const [key, d] of indegree) {
68
+ if (d === 0) ready.push(key);
69
+ }
70
+ stableSortByInputOrder(ready, inputOrder);
71
+
72
+ const sorted: { key: string; payload: T; needs: readonly string[] }[] = [];
73
+ while (ready.length > 0) {
74
+ const key = ready.shift()!;
75
+ sorted.push(byKey.get(key)!);
76
+ const children = dependents.get(key)!;
77
+ const newlyReady: string[] = [];
78
+ for (const child of children) {
79
+ const next = indegree.get(child)! - 1;
80
+ indegree.set(child, next);
81
+ if (next === 0) newlyReady.push(child);
82
+ }
83
+ stableSortByInputOrder(newlyReady, inputOrder);
84
+ ready.push(...newlyReady);
85
+ stableSortByInputOrder(ready, inputOrder);
86
+ }
87
+
88
+ if (sorted.length !== nodes.length) {
89
+ const stuck = [...indegree.entries()]
90
+ .filter(([, d]) => d > 0)
91
+ .map(([k]) => k);
92
+ throw new WorkflowDslError(
93
+ WorkflowErrorCode.DSL_CYCLE_DETECTED,
94
+ `Workflow DAG contains a cycle. Jobs in cycle or downstream of one: ${stuck.join(', ')}`,
95
+ { stuck },
96
+ );
97
+ }
98
+
99
+ return sorted;
100
+ }
101
+
102
+ function stableSortByInputOrder(keys: string[], inputOrder: Map<string, number>): void {
103
+ keys.sort((a, b) => {
104
+ const ai = inputOrder.get(a)!;
105
+ const bi = inputOrder.get(b)!;
106
+ return ai - bi;
107
+ });
108
+ }
@@ -0,0 +1,10 @@
1
+ export { compileWorkflow } from './compile';
2
+ export { canonicalJsonSha256, canonicalJsonStringify } from './canonical-json';
3
+ export { compileManifestSource } from './manifest-source';
4
+ export { getWorkspaceManifestContract, isAgentShapedAction } from './action-shape';
5
+ export type {
6
+ CompileInput,
7
+ ResolvedAgent,
8
+ ResolvedDeliverableSpec,
9
+ ResolvedRef,
10
+ } from './types';
@@ -0,0 +1,199 @@
1
+ import {
2
+ WorkflowErrorCode,
3
+ WorkflowTriggerKind,
4
+ type TriggerPayload,
5
+ } from '@xemahq/kernel-contracts/workflow';
6
+ import { WorkflowDslError } from '../errors';
7
+ import type { WorkflowDocument, WorkflowInputDeclaration } from '../types';
8
+
9
+ /**
10
+ * Bind trigger inputs against the workflow's declared input schema(s),
11
+ * applying declared defaults. Missing-required / type-mismatch is a
12
+ * compile error — never a silent zero/null substitution (rule 2).
13
+ *
14
+ * We use the trigger kind to pick which declaration block governs:
15
+ * - workflow_dispatch → on.workflow_dispatch.inputs
16
+ * - schedule → on.schedule[*].inputs (pre-bound on the schedule itself)
17
+ * - workflow_call → on.workflow_call.inputs
18
+ * - webhook → no input declaration; payload is the whole envelope
19
+ *
20
+ * Scheduled triggers carry their pre-bound inputs in `trigger.inputs`; we
21
+ * validate against the workflow_dispatch declaration if present, or pass
22
+ * through un-validated (schedule-authored inputs are trusted) — the engine
23
+ * validates schedule-level inputs at schedule-creation time instead.
24
+ */
25
+ export function bindTriggerInputs(
26
+ workflow: WorkflowDocument,
27
+ trigger: TriggerPayload,
28
+ previewMode = false,
29
+ ): Readonly<Record<string, unknown>> {
30
+ switch (trigger.kind) {
31
+ case WorkflowTriggerKind.WORKFLOW_DISPATCH: {
32
+ if (workflow.on.workflow_dispatch === undefined) {
33
+ throw new WorkflowDslError(
34
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
35
+ `Workflow does not declare an 'on.workflow_dispatch' trigger and cannot be dispatched. Declared triggers: [${Object.keys(workflow.on).sort((a, b) => a.localeCompare(b)).join(', ') || '(none)'}].`,
36
+ { triggerKind: 'workflow_dispatch', declaredTriggers: Object.keys(workflow.on) },
37
+ );
38
+ }
39
+ const decl = workflow.on.workflow_dispatch.inputs ?? {};
40
+ return bindInputs(decl, trigger.inputs, 'workflow_dispatch', previewMode);
41
+ }
42
+ case WorkflowTriggerKind.WORKFLOW_CALL: {
43
+ if (workflow.on.workflow_call === undefined) {
44
+ throw new WorkflowDslError(
45
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
46
+ `Workflow does not declare an 'on.workflow_call' trigger and cannot be invoked from another workflow. Declared triggers: [${Object.keys(workflow.on).sort((a, b) => a.localeCompare(b)).join(', ') || '(none)'}].`,
47
+ { triggerKind: 'workflow_call', declaredTriggers: Object.keys(workflow.on) },
48
+ );
49
+ }
50
+ const decl = workflow.on.workflow_call.inputs ?? {};
51
+ return bindInputs(decl, trigger.inputs, 'workflow_call', previewMode);
52
+ }
53
+ case WorkflowTriggerKind.SCHEDULE: {
54
+ // Schedule inputs were validated against workflow_dispatch.inputs at
55
+ // schedule-create time by workflow-engine-api. Here we just validate
56
+ // the resulting bound payload against the same schema (defense in
57
+ // depth; cheap).
58
+ const decl = workflow.on.workflow_dispatch?.inputs ?? {};
59
+ return bindInputs(decl, trigger.inputs, 'schedule', previewMode);
60
+ }
61
+ case WorkflowTriggerKind.WEBHOOK: {
62
+ // Webhook does not declare typed inputs at the workflow level —
63
+ // validation of the canonical envelope happens at integration-adapters-api.
64
+ return trigger.payload;
65
+ }
66
+ }
67
+ }
68
+
69
+ function bindInputs(
70
+ declarations: Readonly<Record<string, WorkflowInputDeclaration>>,
71
+ received: Readonly<Record<string, unknown>>,
72
+ label: string,
73
+ previewMode = false,
74
+ ): Readonly<Record<string, unknown>> {
75
+ const out: Record<string, unknown> = {};
76
+ const seen = new Set<string>();
77
+
78
+ for (const [name, decl] of Object.entries(declarations)) {
79
+ seen.add(name);
80
+ const hasValue = Object.hasOwn(received, name);
81
+ if (!hasValue) {
82
+ handleMissingInput(out, name, decl, label, previewMode);
83
+ continue;
84
+ }
85
+ const value = received[name];
86
+ assertInputType(value, decl, name, label);
87
+ out[name] = value;
88
+ }
89
+
90
+ // Reject unknown inputs — fail-fast beats silent pass-through.
91
+ for (const name of Object.keys(received)) {
92
+ if (!seen.has(name)) {
93
+ throw new WorkflowDslError(
94
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
95
+ `${label} trigger received unknown input '${name}'. Declared inputs: [${Object.keys(declarations).sort((a, b) => a.localeCompare(b)).join(', ') || '(none)'}].`,
96
+ { label, inputName: name },
97
+ );
98
+ }
99
+ }
100
+
101
+ return out;
102
+ }
103
+
104
+ function handleMissingInput(
105
+ out: Record<string, unknown>,
106
+ name: string,
107
+ decl: WorkflowInputDeclaration,
108
+ label: string,
109
+ previewMode: boolean,
110
+ ): void {
111
+ if (decl.required !== true) {
112
+ // Optional inputs are always materialized in the bound payload so
113
+ // `${{ inputs.<name> }}` resolves deterministically — declared default
114
+ // when present, otherwise null. Without this, expressions referencing
115
+ // a not-provided optional input throw `Unknown property` at runtime.
116
+ out[name] = decl.default === undefined ? null : decl.default;
117
+ return;
118
+ }
119
+ if (previewMode) {
120
+ // In preview mode, substitute a type-appropriate sentinel so the
121
+ // compiler can validate structure (DAG, expressions, matrix)
122
+ // without requiring real input values. Never used on real runs.
123
+ out[name] = previewSentinel(decl.type);
124
+ return;
125
+ }
126
+ throw new WorkflowDslError(
127
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
128
+ `${label} trigger is missing required input '${name}'.`,
129
+ { label, inputName: name },
130
+ );
131
+ }
132
+
133
+ /**
134
+ * Returns a type-appropriate sentinel value for use in preview mode when
135
+ * a required input is missing. The sentinel must pass `assertInputType`
136
+ * for its declared type — it is never used outside of preview compilation.
137
+ */
138
+ function previewSentinel(type: WorkflowInputDeclaration['type']): unknown {
139
+ switch (type) {
140
+ case 'string':
141
+ return '';
142
+ case 'number':
143
+ return 0;
144
+ case 'integer':
145
+ return 0;
146
+ case 'boolean':
147
+ return false;
148
+ case 'object':
149
+ return {};
150
+ case 'array':
151
+ return [];
152
+ }
153
+ }
154
+
155
+ function assertInputType(
156
+ value: unknown,
157
+ decl: WorkflowInputDeclaration,
158
+ name: string,
159
+ label: string,
160
+ ): void {
161
+ const received = classify(value);
162
+ if (received !== decl.type) {
163
+ throw new WorkflowDslError(
164
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
165
+ `${label} trigger input '${name}' expected ${decl.type}, received ${received}.`,
166
+ { label, inputName: name, expected: decl.type, received },
167
+ );
168
+ }
169
+ if (decl.enum !== undefined) {
170
+ if (!decl.enum.includes(value)) {
171
+ throw new WorkflowDslError(
172
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
173
+ `${label} trigger input '${name}' value not in enum.`,
174
+ { label, inputName: name, enum: decl.enum },
175
+ );
176
+ }
177
+ }
178
+ }
179
+
180
+ function classify(value: unknown): WorkflowInputDeclaration['type'] {
181
+ if (Array.isArray(value)) return 'array';
182
+ if (value === null) return 'object'; // null treated as absence; Ajv doesn't emit null type for us
183
+ switch (typeof value) {
184
+ case 'string':
185
+ return 'string';
186
+ case 'boolean':
187
+ return 'boolean';
188
+ case 'number':
189
+ return Number.isInteger(value) ? 'integer' : 'number';
190
+ case 'object':
191
+ return 'object';
192
+ default:
193
+ throw new WorkflowDslError(
194
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
195
+ `Unsupported input value kind: ${typeof value}`,
196
+ { valueKind: typeof value },
197
+ );
198
+ }
199
+ }
@@ -0,0 +1,114 @@
1
+ import { WorkflowErrorCode } from '@xemahq/kernel-contracts/workflow';
2
+
3
+ import { WorkflowDslError } from '../errors';
4
+ import {
5
+ collectInstallationResourceHints,
6
+ type InstallationResourceFieldHint,
7
+ } from '../installation-resource-kind';
8
+
9
+ import type { InstallationCompileScope } from './types';
10
+
11
+ /**
12
+ * Compile-time check that every `x-installation-resource` field on the
13
+ * action manifest's `inputs:` schema points at a resource bound to the
14
+ * calling biome installation.
15
+ *
16
+ * Cases handled:
17
+ * - Expression-shaped value (`${{ ... }}`): the literal isn't known
18
+ * yet, so we skip the binding check. Runtime auth lives at the
19
+ * activity layer (`AuthFailed` from credentials/resolve).
20
+ * - Literal string: must be in `boundResources[kind]`. Missing →
21
+ * DSL error with `inputs.<path>` pointer.
22
+ * - Array of strings (rare): every element must be bound.
23
+ * - Undefined + optional: no-op.
24
+ *
25
+ * Skipped entirely when `installationScope` is undefined (system /
26
+ * org-wide dispatch) — only biome-installed runs get this gate.
27
+ */
28
+ export function validateInstallationResourceBindings(input: {
29
+ jobKey: string;
30
+ actionId: string;
31
+ inputsSchema: unknown;
32
+ withValue: Readonly<Record<string, unknown>>;
33
+ scope: InstallationCompileScope | undefined;
34
+ }): void {
35
+ if (!input.scope) {return;}
36
+ if (!input.inputsSchema) {return;}
37
+ const hints = collectInstallationResourceHints(input.inputsSchema);
38
+ if (hints.length === 0) {return;}
39
+ for (const hint of hints) {
40
+ const value = readPath(input.withValue, hint.path);
41
+ if (value === undefined || value === null || value === '') {
42
+ if (hint.optional) {continue;}
43
+ throw new WorkflowDslError(
44
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
45
+ `jobs.${input.jobKey}.with.${hint.path} is required (action ${input.actionId} declares x-installation-resource kind="${hint.kind}") but the with-block omitted it`,
46
+ );
47
+ }
48
+ const ctx = {
49
+ jobKey: input.jobKey,
50
+ actionId: input.actionId,
51
+ scope: input.scope,
52
+ };
53
+ if (typeof value === 'string') {
54
+ assertBound(ctx, hint, value);
55
+ continue;
56
+ }
57
+ if (Array.isArray(value)) {
58
+ for (const item of value) {
59
+ if (typeof item === 'string') {
60
+ assertBound(ctx, hint, item);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ function assertBound(
68
+ ctx: {
69
+ jobKey: string;
70
+ actionId: string;
71
+ scope: InstallationCompileScope;
72
+ },
73
+ hint: InstallationResourceFieldHint,
74
+ literal: string,
75
+ ): void {
76
+ // Expression-shaped values are deferred to runtime — the literal
77
+ // string the compiler sees here is the expression body itself
78
+ // (`${{ inputs.x }}`), not a resolvable id.
79
+ if (literal.startsWith('${{') || literal.includes('${{')) {return;}
80
+ const bound = ctx.scope.boundResources[hint.kind] ?? [];
81
+ if (bound.length === 0) {
82
+ throw new WorkflowDslError(
83
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
84
+ `jobs.${ctx.jobKey}.with.${hint.path} references ${hint.kind}="${literal}" but installation ${ctx.scope.installationId} has no ${hint.kind} resources bound — connect one before dispatching`,
85
+ );
86
+ }
87
+ if (!bound.includes(literal)) {
88
+ throw new WorkflowDslError(
89
+ WorkflowErrorCode.DSL_SEMANTIC_INVALID,
90
+ `jobs.${ctx.jobKey}.with.${hint.path} references ${hint.kind}="${literal}" which is NOT bound to installation ${ctx.scope.installationId} (action ${ctx.actionId} declares x-installation-resource kind="${hint.kind}"). Bound: [${bound.join(', ')}]`,
91
+ );
92
+ }
93
+ }
94
+
95
+ function readPath(
96
+ obj: Readonly<Record<string, unknown>>,
97
+ path: string,
98
+ ): unknown {
99
+ if (path === '') {return obj;}
100
+ const segments = path.split('.');
101
+ let cursor: unknown = obj;
102
+ for (const seg of segments) {
103
+ if (!cursor || typeof cursor !== 'object') {return undefined;}
104
+ // Array-element path uses `[*]` — treat as "any element", returning
105
+ // the array itself so the caller iterates. The hint walker emits
106
+ // these for `items.x-installation-resource` shapes.
107
+ if (seg === '[*]') {
108
+ if (!Array.isArray(cursor)) {return undefined;}
109
+ return cursor;
110
+ }
111
+ cursor = (cursor as Record<string, unknown>)[seg];
112
+ }
113
+ return cursor;
114
+ }