@shipfox/api-definitions 11.0.0 → 12.0.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 (148) hide show
  1. package/.turbo/turbo-build.log +13 -12
  2. package/CHANGELOG.md +81 -0
  3. package/dist/core/entities/index.d.ts +1 -0
  4. package/dist/core/entities/index.d.ts.map +1 -1
  5. package/dist/core/entities/index.js +1 -0
  6. package/dist/core/entities/index.js.map +1 -1
  7. package/dist/core/entities/sync-state.d.ts +4 -0
  8. package/dist/core/entities/sync-state.d.ts.map +1 -1
  9. package/dist/core/entities/sync-state.js +10 -0
  10. package/dist/core/entities/sync-state.js.map +1 -1
  11. package/dist/core/entities/validation-warning.d.ts +6 -0
  12. package/dist/core/entities/validation-warning.d.ts.map +1 -0
  13. package/dist/core/entities/validation-warning.js +3 -0
  14. package/dist/core/entities/validation-warning.js.map +1 -0
  15. package/dist/core/parse-definition.d.ts +9 -7
  16. package/dist/core/parse-definition.d.ts.map +1 -1
  17. package/dist/core/parse-definition.js +14 -2
  18. package/dist/core/parse-definition.js.map +1 -1
  19. package/dist/core/sync-definitions.d.ts +2 -0
  20. package/dist/core/sync-definitions.d.ts.map +1 -1
  21. package/dist/core/sync-definitions.js +6 -5
  22. package/dist/core/sync-definitions.js.map +1 -1
  23. package/dist/core/validate-definition.d.ts +9 -5
  24. package/dist/core/validate-definition.d.ts.map +1 -1
  25. package/dist/core/validate-definition.js +23 -2
  26. package/dist/core/validate-definition.js.map +1 -1
  27. package/dist/core/workflow-model/index.d.ts +1 -1
  28. package/dist/core/workflow-model/index.d.ts.map +1 -1
  29. package/dist/core/workflow-model/index.js.map +1 -1
  30. package/dist/core/workflow-model/invalid-workflow-model-error.d.ts +3 -1
  31. package/dist/core/workflow-model/invalid-workflow-model-error.d.ts.map +1 -1
  32. package/dist/core/workflow-model/invalid-workflow-model-error.js.map +1 -1
  33. package/dist/core/workflow-model/normalize-if-condition.d.ts +0 -1
  34. package/dist/core/workflow-model/normalize-if-condition.d.ts.map +1 -1
  35. package/dist/core/workflow-model/normalize-if-condition.js +0 -1
  36. package/dist/core/workflow-model/normalize-if-condition.js.map +1 -1
  37. package/dist/core/workflow-model/normalize-job-checkout.d.ts +15 -3
  38. package/dist/core/workflow-model/normalize-job-checkout.d.ts.map +1 -1
  39. package/dist/core/workflow-model/normalize-job-checkout.js +83 -4
  40. package/dist/core/workflow-model/normalize-job-checkout.js.map +1 -1
  41. package/dist/core/workflow-model/normalize-job-listening.js +0 -1
  42. package/dist/core/workflow-model/normalize-job-listening.js.map +1 -1
  43. package/dist/core/workflow-model/normalize-job-success.d.ts.map +1 -1
  44. package/dist/core/workflow-model/normalize-job-success.js +0 -1
  45. package/dist/core/workflow-model/normalize-job-success.js.map +1 -1
  46. package/dist/core/workflow-model/normalize-jobs.d.ts.map +1 -1
  47. package/dist/core/workflow-model/normalize-jobs.js +310 -45
  48. package/dist/core/workflow-model/normalize-jobs.js.map +1 -1
  49. package/dist/core/workflow-model/normalize-step-gate.js +0 -1
  50. package/dist/core/workflow-model/normalize-step-gate.js.map +1 -1
  51. package/dist/core/workflow-model/normalize-triggers.d.ts.map +1 -1
  52. package/dist/core/workflow-model/normalize-triggers.js +0 -1
  53. package/dist/core/workflow-model/normalize-triggers.js.map +1 -1
  54. package/dist/core/workflow-model/normalize-workflow-document.d.ts +3 -0
  55. package/dist/core/workflow-model/normalize-workflow-document.d.ts.map +1 -1
  56. package/dist/core/workflow-model/normalize-workflow-document.js +34 -4
  57. package/dist/core/workflow-model/normalize-workflow-document.js.map +1 -1
  58. package/dist/core/workflow-model/parse-interpolation-field.d.ts +1 -1
  59. package/dist/core/workflow-model/parse-interpolation-field.d.ts.map +1 -1
  60. package/dist/core/workflow-model/parse-interpolation-field.js +27 -57
  61. package/dist/core/workflow-model/parse-interpolation-field.js.map +1 -1
  62. package/dist/core/workflow-model/validate-literal-name.d.ts +12 -0
  63. package/dist/core/workflow-model/validate-literal-name.d.ts.map +1 -0
  64. package/dist/core/workflow-model/validate-literal-name.js +36 -0
  65. package/dist/core/workflow-model/validate-literal-name.js.map +1 -0
  66. package/dist/core/workflow-model/validate-predicate-expression.d.ts +1 -2
  67. package/dist/core/workflow-model/validate-predicate-expression.d.ts.map +1 -1
  68. package/dist/core/workflow-model/validate-predicate-expression.js +31 -90
  69. package/dist/core/workflow-model/validate-predicate-expression.js.map +1 -1
  70. package/dist/core/workflow-model/validation-issue.d.ts +2 -1
  71. package/dist/core/workflow-model/validation-issue.d.ts.map +1 -1
  72. package/dist/core/workflow-model/validation-issue.js +8 -2
  73. package/dist/core/workflow-model/validation-issue.js.map +1 -1
  74. package/dist/core/workflow-model/workflow-field-label.d.ts.map +1 -1
  75. package/dist/core/workflow-model/workflow-field-label.js +16 -2
  76. package/dist/core/workflow-model/workflow-field-label.js.map +1 -1
  77. package/dist/db/db.d.ts +38 -0
  78. package/dist/db/db.d.ts.map +1 -1
  79. package/dist/db/schema/sync-states.d.ts +19 -0
  80. package/dist/db/schema/sync-states.d.ts.map +1 -1
  81. package/dist/db/schema/sync-states.js +3 -1
  82. package/dist/db/schema/sync-states.js.map +1 -1
  83. package/dist/db/sync-states.d.ts +2 -1
  84. package/dist/db/sync-states.d.ts.map +1 -1
  85. package/dist/db/sync-states.js +4 -0
  86. package/dist/db/sync-states.js.map +1 -1
  87. package/dist/presentation/dto/definition.d.ts.map +1 -1
  88. package/dist/presentation/dto/definition.js +2 -1
  89. package/dist/presentation/dto/definition.js.map +1 -1
  90. package/dist/presentation/routes/project-access.d.ts +1 -0
  91. package/dist/presentation/routes/project-access.d.ts.map +1 -1
  92. package/dist/presentation/routes/validate-definition.d.ts.map +1 -1
  93. package/dist/presentation/routes/validate-definition.js +5 -3
  94. package/dist/presentation/routes/validate-definition.js.map +1 -1
  95. package/dist/temporal/activities/sync-activities.d.ts +6 -2
  96. package/dist/temporal/activities/sync-activities.d.ts.map +1 -1
  97. package/dist/temporal/activities/sync-activities.js +8 -1
  98. package/dist/temporal/activities/sync-activities.js.map +1 -1
  99. package/dist/temporal/workflows/definition-sync.js +4 -1
  100. package/dist/temporal/workflows/definition-sync.js.map +1 -1
  101. package/dist/temporal/workflows/index.bundle.js +9029 -3
  102. package/dist/tsconfig.test.tsbuildinfo +1 -1
  103. package/drizzle/0002_lumpy_ben_parker.sql +1 -0
  104. package/drizzle/meta/0002_snapshot.json +534 -0
  105. package/drizzle/meta/_journal.json +7 -0
  106. package/package.json +14 -14
  107. package/src/core/entities/index.ts +1 -0
  108. package/src/core/entities/sync-state.ts +23 -0
  109. package/src/core/entities/validation-warning.ts +5 -0
  110. package/src/core/parse-definition-warnings.test.ts +33 -0
  111. package/src/core/parse-definition.test.ts +7 -1
  112. package/src/core/parse-definition.ts +28 -10
  113. package/src/core/sync-definitions.test.ts +39 -0
  114. package/src/core/sync-definitions.ts +14 -4
  115. package/src/core/validate-definition.test.ts +128 -0
  116. package/src/core/validate-definition.ts +39 -8
  117. package/src/core/workflow-model/README.md +2 -2
  118. package/src/core/workflow-model/index.ts +1 -0
  119. package/src/core/workflow-model/invalid-workflow-model-error.ts +8 -2
  120. package/src/core/workflow-model/normalize-if-condition.ts +0 -2
  121. package/src/core/workflow-model/normalize-job-checkout.ts +105 -8
  122. package/src/core/workflow-model/normalize-job-listening.ts +0 -1
  123. package/src/core/workflow-model/normalize-job-success.ts +0 -1
  124. package/src/core/workflow-model/normalize-jobs.ts +330 -49
  125. package/src/core/workflow-model/normalize-step-gate.ts +0 -1
  126. package/src/core/workflow-model/normalize-triggers.ts +0 -1
  127. package/src/core/workflow-model/normalize-workflow-document.test.ts +732 -185
  128. package/src/core/workflow-model/normalize-workflow-document.ts +29 -4
  129. package/src/core/workflow-model/parse-interpolation-field.ts +50 -79
  130. package/src/core/workflow-model/validate-literal-name.ts +64 -0
  131. package/src/core/workflow-model/validate-predicate-expression.test.ts +153 -15
  132. package/src/core/workflow-model/validate-predicate-expression.ts +52 -120
  133. package/src/core/workflow-model/validation-issue.ts +4 -0
  134. package/src/core/workflow-model/workflow-field-label.ts +16 -2
  135. package/src/core/workflow-yaml/README.md +2 -2
  136. package/src/db/schema/sync-states.ts +4 -1
  137. package/src/db/sync-states.test.ts +57 -0
  138. package/src/db/sync-states.ts +6 -0
  139. package/src/presentation/dto/definition.ts +1 -0
  140. package/src/presentation/routes/list-definitions.test.ts +1 -0
  141. package/src/presentation/routes/validate-definition.test.ts +1 -0
  142. package/src/presentation/routes/validate-definition.ts +7 -1
  143. package/src/temporal/activities/sync-activities.test.ts +16 -0
  144. package/src/temporal/activities/sync-activities.ts +18 -3
  145. package/src/temporal/workflows/definition-sync.ts +1 -1
  146. package/test/agent-validation-catalog.ts +17 -0
  147. package/test/fixtures/valid-listening-job.yml +2 -1
  148. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,5 +1,5 @@
1
1
  import { secretKeySchema, secretStoreSchema } from '@shipfox/api-secrets-dto';
2
- import { analyzeContextKeyAccess, createWorkflowExpression, extractCelUntrustedPathAccesses, getWorkflowContextAvailability, getWorkflowContextDefinition, getWorkflowContextHost, getWorkflowContextTypeEnvironment, getWorkflowContextUntrustedPaths, InvalidWorkflowExpressionError, InvalidWorkflowTemplateError, parseWorkflowTemplate, planInterpolationField, unavailableRootsAt, workflowContextNames, workflowInterpolationFieldAcceptsContext, workflowInterpolationFieldAcceptsHost, workflowInterpolationFieldAcceptsTrustTier } from '@shipfox/expression';
2
+ import { analyzeContextKeyAccess, analyzeContextRootKeyAccess, createWorkflowExpression, getWorkflowContextAvailability, getWorkflowContextDefinition, getWorkflowContextHost, getWorkflowContextTypeEnvironment, getWorkflowInterpolationFieldSelfReference, InvalidWorkflowExpressionError, InvalidWorkflowTemplateError, parseWorkflowTemplate, planInterpolationField, unavailableRootsAt, workflowContextNames, workflowInterpolationFieldAcceptsHost } from '@shipfox/expression';
3
3
  import { validateDirectJobReferences } from './validate-job-references.js';
4
4
  export function parseInterpolationField(params) {
5
5
  const segments = parseTemplate(params);
@@ -24,6 +24,27 @@ export function parseInterpolationField(params) {
24
24
  }
25
25
  return plan.plan.field.segments;
26
26
  }
27
+ function dynamicNameSelfReference(params) {
28
+ const target = getWorkflowInterpolationFieldSelfReference(params.field);
29
+ if (target === undefined) return undefined;
30
+ const access = analyzeContextRootKeyAccess(params.segment.expression, [
31
+ target.root
32
+ ]);
33
+ const reference = access.references.find((entry)=>entry.key === target.key);
34
+ const computedReference = access.violations.find((violation)=>violation.root === target.root && (violation.key === undefined || violation.key === target.key));
35
+ if (reference === undefined && computedReference === undefined) return undefined;
36
+ return issue({
37
+ code: 'dynamic-name-self-reference',
38
+ message: `${fieldLabel(params.field)} interpolation cannot reference its own dynamic name.`,
39
+ path: params.path,
40
+ details: {
41
+ field: params.field,
42
+ source: params.source,
43
+ expression: params.segment.expression.source,
44
+ reference: reference === undefined ? `${target.root}[computed]` : `${reference.root}.${reference.key}`
45
+ }
46
+ });
47
+ }
27
48
  function parseTemplate(params) {
28
49
  try {
29
50
  return parseWorkflowTemplate(params.source);
@@ -45,15 +66,6 @@ function validateExpressionSegment(params) {
45
66
  const contextRoots = uniqueStrings(params.segment.contextRoots);
46
67
  const knownRoots = contextRoots.filter(isWorkflowContextName);
47
68
  const unknownRoots = contextRoots.filter((root)=>!isWorkflowContextName(root));
48
- const rejectedRoots = knownRoots.filter((root)=>!workflowInterpolationFieldAcceptsContext(params.field, root));
49
- if (rejectedRoots.length > 0) {
50
- params.issues.push(untrustedContextIssue({
51
- ...params,
52
- contextRoots,
53
- rejectedRoots
54
- }));
55
- return undefined;
56
- }
57
69
  const rejectedHostRoots = knownRoots.filter((root)=>!workflowInterpolationFieldAcceptsHost(params.field, getWorkflowContextHost(root)));
58
70
  if (rejectedHostRoots.length > 0) {
59
71
  params.issues.push(runnerContextInFieldIssue({
@@ -73,6 +85,11 @@ function validateExpressionSegment(params) {
73
85
  })));
74
86
  return undefined;
75
87
  }
88
+ const selfReference = dynamicNameSelfReference(params);
89
+ if (selfReference !== undefined) {
90
+ params.issues.push(selfReference);
91
+ return undefined;
92
+ }
76
93
  const invalidReferenceIssue = validateContextKeyReferences({
77
94
  ...params,
78
95
  contextRoots,
@@ -93,15 +110,6 @@ function validateExpressionSegment(params) {
93
110
  params.issues.push(invalidJobReferenceIssue);
94
111
  return undefined;
95
112
  }
96
- const rejectedPathRoots = findUntrustedPathRoots(params.segment, params.field, knownRoots);
97
- if (rejectedPathRoots.length > 0) {
98
- params.issues.push(untrustedContextIssue({
99
- ...params,
100
- contextRoots,
101
- rejectedRoots: rejectedPathRoots
102
- }));
103
- return undefined;
104
- }
105
113
  if (unknownRoots.length > 0) {
106
114
  params.issues.push(issue({
107
115
  code: 'unknown-interpolation-context',
@@ -163,44 +171,6 @@ function validateExpressionSegment(params) {
163
171
  return undefined;
164
172
  }
165
173
  }
166
- function findUntrustedPathRoots(segment, field, knownRoots) {
167
- if (!isTrustedOnlyField(field)) return [];
168
- const untrustedPathsByRoot = new Map();
169
- for (const root of knownRoots){
170
- const paths = getWorkflowContextUntrustedPaths(root);
171
- if (paths === undefined || paths.length === 0) continue;
172
- untrustedPathsByRoot.set(root, paths);
173
- }
174
- if (knownRoots.includes('steps')) {
175
- untrustedPathsByRoot.set('steps', [
176
- ...new Set([
177
- ...untrustedPathsByRoot.get('steps') ?? [],
178
- 'outputs'
179
- ])
180
- ]);
181
- }
182
- if (untrustedPathsByRoot.size === 0) return [];
183
- return extractCelUntrustedPathAccesses({
184
- source: segment.expression.source,
185
- untrustedPathsByRoot
186
- }).filter(isWorkflowContextName);
187
- }
188
- function isTrustedOnlyField(field) {
189
- return !workflowInterpolationFieldAcceptsTrustTier(field, 'untrusted');
190
- }
191
- function untrustedContextIssue(params) {
192
- return issue({
193
- code: 'untrusted-context-in-field',
194
- message: params.field === 'run' ? `Run command interpolation cannot use untrusted context ${formatList(params.rejectedRoots)}. Bind untrusted values to env and reference the shell variable from run instead.` : `${fieldLabel(params.field)} interpolation cannot use untrusted context ${formatList(params.rejectedRoots)}.`,
195
- path: params.path,
196
- details: {
197
- field: params.field,
198
- source: params.source,
199
- contextRoots: params.contextRoots,
200
- rejectedRoots: params.rejectedRoots
201
- }
202
- });
203
- }
204
174
  function runnerContextInFieldIssue(params) {
205
175
  return issue({
206
176
  code: 'runner-context-in-field',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/workflow-model/parse-interpolation-field.ts"],"sourcesContent":["import {secretKeySchema, secretStoreSchema} from '@shipfox/api-secrets-dto';\nimport {\n type AvailabilitySite,\n analyzeContextKeyAccess,\n createWorkflowExpression,\n type ExpressionTypeEnvironment,\n extractCelUntrustedPathAccesses,\n getWorkflowContextAvailability,\n getWorkflowContextDefinition,\n getWorkflowContextHost,\n getWorkflowContextTypeEnvironment,\n getWorkflowContextUntrustedPaths,\n InvalidWorkflowExpressionError,\n InvalidWorkflowTemplateError,\n type PlanViolation,\n parseWorkflowTemplate,\n planInterpolationField,\n unavailableRootsAt,\n type WorkflowContextName,\n type WorkflowInterpolationField,\n type WorkflowTemplateExprSegment,\n type WorkflowTemplateSegment,\n workflowContextNames,\n workflowInterpolationFieldAcceptsContext,\n workflowInterpolationFieldAcceptsHost,\n workflowInterpolationFieldAcceptsTrustTier,\n} from '@shipfox/expression';\nimport type {WorkflowFieldTemplate} from '../entities/workflow-model.js';\nimport type {\n WorkflowModelValidationIssue,\n WorkflowModelValidationIssueCode,\n WorkflowModelValidationIssuePathSegment,\n} from './invalid-workflow-model-error.js';\nimport {validateDirectJobReferences} from './validate-job-references.js';\n\nexport type StoredInterpolationField =\n | 'run'\n | 'env.value'\n | 'agent.prompt'\n | 'agent.model'\n | 'agent.provider'\n | 'job.outputs'\n | 'job.name'\n | 'job.runner'\n | 'step.name'\n | 'step.feedback';\n\nexport function parseInterpolationField(params: {\n field: StoredInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n issues: WorkflowModelValidationIssue[];\n fillSite?: AvailabilitySite;\n allowedJobReferences?: ReadonlySet<string>;\n typeOverlay?: ExpressionTypeEnvironment | undefined;\n}): WorkflowFieldTemplate | undefined {\n const segments = parseTemplate(params);\n if (segments === undefined) return undefined;\n\n const expressionSegments = segments.filter(isExpressionSegment);\n if (expressionSegments.length === 0) return undefined;\n\n const checkedSegments = segments.map((segment) => {\n if (segment.kind === 'literal') return segment;\n\n const validatedSegment = validateExpressionSegment({...params, segment});\n return validatedSegment ?? segment;\n });\n\n const plan = planInterpolationField({field: params.field, segments: checkedSegments});\n if (!plan.ok) {\n params.issues.push(\n ...plan.violations.map((violation) => planViolationIssue(params, violation)),\n );\n return undefined;\n }\n\n return plan.plan.field.segments;\n}\n\nfunction parseTemplate(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n issues: WorkflowModelValidationIssue[];\n}): WorkflowTemplateSegment[] | undefined {\n try {\n return parseWorkflowTemplate(params.source);\n } catch (error) {\n params.issues.push(\n issue({\n code: 'invalid-interpolation-template',\n message: `${fieldLabel(params.field)} must use valid $${'{{ }}'} interpolation syntax.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n reason:\n error instanceof InvalidWorkflowTemplateError\n ? error.reason\n : 'Template source did not parse.',\n },\n }),\n );\n return undefined;\n }\n}\n\nfunction validateExpressionSegment(params: {\n field: StoredInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n issues: WorkflowModelValidationIssue[];\n segment: WorkflowTemplateExprSegment;\n fillSite?: AvailabilitySite;\n allowedJobReferences?: ReadonlySet<string>;\n typeOverlay?: ExpressionTypeEnvironment | undefined;\n}): WorkflowTemplateExprSegment | undefined {\n const contextRoots = uniqueStrings(params.segment.contextRoots);\n const knownRoots = contextRoots.filter(isWorkflowContextName);\n const unknownRoots = contextRoots.filter((root) => !isWorkflowContextName(root));\n\n const rejectedRoots = knownRoots.filter(\n (root) => !workflowInterpolationFieldAcceptsContext(params.field, root),\n );\n if (rejectedRoots.length > 0) {\n params.issues.push(untrustedContextIssue({...params, contextRoots, rejectedRoots}));\n return undefined;\n }\n\n const rejectedHostRoots = knownRoots.filter(\n (root) => !workflowInterpolationFieldAcceptsHost(params.field, getWorkflowContextHost(root)),\n );\n if (rejectedHostRoots.length > 0) {\n params.issues.push(runnerContextInFieldIssue({...params, contextRoots, rejectedHostRoots}));\n return undefined;\n }\n\n const keyAccess = analyzeContextKeyAccess(params.segment.expression);\n if (keyAccess.violations.length > 0) {\n params.issues.push(\n ...keyAccess.violations.map((violation) =>\n computedContextKeyIssue({\n ...params,\n contextRoots,\n root: violation.root,\n expression: violation.source,\n }),\n ),\n );\n return undefined;\n }\n\n const invalidReferenceIssue = validateContextKeyReferences({\n ...params,\n contextRoots,\n references: keyAccess.references,\n });\n if (invalidReferenceIssue !== undefined) {\n params.issues.push(invalidReferenceIssue);\n return undefined;\n }\n\n const invalidJobReferenceIssue =\n params.allowedJobReferences === undefined\n ? undefined\n : validateDirectJobReferences({\n source: params.source,\n expression: params.segment.expression,\n field: params.field,\n path: params.path,\n allowedJobReferences: params.allowedJobReferences,\n });\n if (invalidJobReferenceIssue !== undefined) {\n params.issues.push(invalidJobReferenceIssue);\n return undefined;\n }\n\n const rejectedPathRoots = findUntrustedPathRoots(params.segment, params.field, knownRoots);\n if (rejectedPathRoots.length > 0) {\n params.issues.push(\n untrustedContextIssue({...params, contextRoots, rejectedRoots: rejectedPathRoots}),\n );\n return undefined;\n }\n\n if (unknownRoots.length > 0) {\n params.issues.push(\n issue({\n code: 'unknown-interpolation-context',\n message: `${fieldLabel(params.field)} interpolation references unknown context ${formatList(\n unknownRoots,\n )}.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots,\n unknownRoots,\n },\n }),\n );\n return undefined;\n }\n\n const fillSite = params.fillSite;\n if (fillSite !== undefined) {\n const serverRoots = knownRoots.filter((root) => getWorkflowContextHost(root) === 'server');\n const unavailableRoots = unavailableRootsAt(serverRoots, fillSite);\n if (unavailableRoots.length > 0) {\n params.issues.push(\n unavailableContextIssue({...params, contextRoots, unavailableRoots, fillSite}),\n );\n return undefined;\n }\n }\n\n if (\n params.typeOverlay === undefined &&\n (knownRoots.length === 0 || knownRoots.some((root) => hasSyntaxOnlyCheckMode(root)))\n ) {\n return params.segment;\n }\n\n try {\n return {\n ...params.segment,\n expression: createWorkflowExpression({\n source: params.segment.expression.source,\n check: {\n mode: 'typed',\n // `undefined` preserves the legacy syntax-only path above. `{}` means\n // callers intentionally requested typed checking with the standard roots.\n typeEnvironment: mergeTypeEnvironments(knownRoots, params.typeOverlay),\n },\n }),\n };\n } catch (error) {\n params.issues.push(\n issue({\n code: 'invalid-interpolation-expression',\n message: `${fieldLabel(params.field)} interpolation expression did not type-check.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots,\n reason:\n error instanceof InvalidWorkflowExpressionError\n ? error.reason\n : 'Expression source did not type-check.',\n },\n }),\n );\n return undefined;\n }\n}\n\nfunction findUntrustedPathRoots(\n segment: WorkflowTemplateExprSegment,\n field: StoredInterpolationField,\n knownRoots: readonly WorkflowContextName[],\n): readonly WorkflowContextName[] {\n if (!isTrustedOnlyField(field)) return [];\n\n const untrustedPathsByRoot = new Map<string, readonly string[]>();\n for (const root of knownRoots) {\n const paths = getWorkflowContextUntrustedPaths(root);\n if (paths === undefined || paths.length === 0) continue;\n untrustedPathsByRoot.set(root, paths);\n }\n if (knownRoots.includes('steps')) {\n untrustedPathsByRoot.set('steps', [\n ...new Set([...(untrustedPathsByRoot.get('steps') ?? []), 'outputs']),\n ]);\n }\n if (untrustedPathsByRoot.size === 0) return [];\n\n return extractCelUntrustedPathAccesses({\n source: segment.expression.source,\n untrustedPathsByRoot,\n }).filter(isWorkflowContextName);\n}\n\nfunction isTrustedOnlyField(field: StoredInterpolationField): boolean {\n return !workflowInterpolationFieldAcceptsTrustTier(field, 'untrusted');\n}\n\nfunction untrustedContextIssue(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n contextRoots: readonly string[];\n rejectedRoots: readonly WorkflowContextName[];\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'untrusted-context-in-field',\n message:\n params.field === 'run'\n ? `Run command interpolation cannot use untrusted context ${formatList(\n params.rejectedRoots,\n )}. Bind untrusted values to env and reference the shell variable from run instead.`\n : `${fieldLabel(params.field)} interpolation cannot use untrusted context ${formatList(\n params.rejectedRoots,\n )}.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n contextRoots: params.contextRoots,\n rejectedRoots: params.rejectedRoots,\n },\n });\n}\n\nfunction runnerContextInFieldIssue(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n contextRoots: readonly string[];\n rejectedHostRoots: readonly WorkflowContextName[];\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'runner-context-in-field',\n message: `${fieldLabel(params.field)} interpolation cannot use runner context ${formatList(\n params.rejectedHostRoots,\n )}. Bind secrets to a run-step env value instead.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n contextRoots: params.contextRoots,\n rejectedRoots: params.rejectedHostRoots,\n },\n });\n}\n\nfunction computedContextKeyIssue(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n contextRoots: readonly string[];\n root: string;\n expression: string;\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'computed-context-key',\n message: `${fieldLabel(params.field)} interpolation must reference ${params.root} with a literal dot key.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.expression,\n contextRoots: params.contextRoots,\n root: params.root,\n },\n });\n}\n\nfunction validateContextKeyReferences(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n contextRoots: readonly string[];\n segment: WorkflowTemplateExprSegment;\n references: ReturnType<typeof analyzeContextKeyAccess>['references'];\n}): WorkflowModelValidationIssue | undefined {\n for (const reference of params.references) {\n if (!secretKeySchema.safeParse(reference.key).success) {\n return computedContextKeyIssue({\n ...params,\n root: reference.root,\n expression: params.segment.expression.source,\n });\n }\n\n if (reference.root !== 'secrets' || reference.store === undefined) continue;\n if (secretStoreSchema.safeParse(reference.store).success) continue;\n\n return issue({\n code: 'unknown-secret-store',\n message: `${fieldLabel(params.field)} interpolation references unknown secret store \"${reference.store}\".`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots: params.contextRoots,\n store: reference.store,\n },\n });\n }\n\n return undefined;\n}\n\nconst availabilitySiteLabels = {\n ingest: 'ingest',\n 'run-creation': 'run creation',\n 'execution-creation': 'execution creation',\n 'job-activation': 'job activation',\n 'step-dispatch': 'step dispatch',\n 'step-report': 'step reporting',\n 'execution-resolution': 'execution resolution',\n 'job-resolution': 'job resolution',\n} as const satisfies Record<AvailabilitySite, string>;\n\nfunction unavailableContextIssue(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n segment: WorkflowTemplateExprSegment;\n contextRoots: readonly string[];\n unavailableRoots: readonly WorkflowContextName[];\n fillSite: AvailabilitySite;\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'context-unavailable-at-fill-site',\n message: `${fieldLabel(params.field)} interpolation references ${contextNoun(\n params.unavailableRoots,\n )} ${formatList(params.unavailableRoots)} that ${availabilityVerb(\n params.unavailableRoots,\n )} not available at ${describeAvailabilitySite(params.fillSite)}. ${params.unavailableRoots\n .map(unavailableRootAvailabilityMessage)\n .join(' ')}`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots: params.contextRoots,\n unavailableRoots: params.unavailableRoots,\n fillSite: params.fillSite,\n },\n });\n}\n\nfunction planViolationIssue(\n params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n },\n violation: PlanViolation,\n): WorkflowModelValidationIssue {\n if (violation.reason === 'computed-context-key') {\n return issue({\n code: 'computed-context-key',\n message: `${fieldLabel(params.field)} interpolation must reference ${formatList(\n violation.contextRoots ?? [],\n )} with a literal dot key.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: violation.source,\n contextRoots: violation.contextRoots,\n },\n });\n }\n\n return issue({\n code: 'runner-context-not-bare',\n message: `${fieldLabel(params.field)} interpolation references runner context in a larger expression; ${violation.hint}.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: violation.source,\n runnerRoots: violation.runnerRoots ?? [],\n },\n });\n}\n\nfunction contextNoun(roots: readonly WorkflowContextName[]): 'context' | 'contexts' {\n return roots.length === 1 ? 'context' : 'contexts';\n}\n\nfunction availabilityVerb(roots: readonly WorkflowContextName[]): 'is' | 'are' {\n return roots.length === 1 ? 'is' : 'are';\n}\n\nfunction unavailableRootAvailabilityMessage(root: WorkflowContextName): string {\n const availability = getWorkflowContextAvailability(root);\n if (availability === undefined) return `\"${root}\" is not available at any server site.`;\n return `\"${root}\" becomes available at ${describeAvailabilitySite(availability)}.`;\n}\n\nfunction describeAvailabilitySite(site: AvailabilitySite): string {\n return availabilitySiteLabels[site];\n}\n\nfunction hasSyntaxOnlyCheckMode(root: WorkflowContextName): boolean {\n return getWorkflowContextDefinition(root).checkMode === 'syntax';\n}\n\nfunction mergeTypeEnvironments(\n roots: readonly WorkflowContextName[],\n typeOverlay?: ExpressionTypeEnvironment,\n): ExpressionTypeEnvironment {\n const typeEnvironment: Record<string, ExpressionTypeEnvironment[string]> = {};\n\n for (const root of roots) {\n const overlayType = typeOverlay?.[root];\n if (overlayType !== undefined) {\n typeEnvironment[root] = overlayType;\n continue;\n }\n\n const contextTypeEnvironment = getWorkflowContextTypeEnvironment(root);\n if (contextTypeEnvironment === undefined) {\n if (typeOverlay !== undefined) typeEnvironment[root] = {kind: 'map'};\n continue;\n }\n\n Object.assign(typeEnvironment, contextTypeEnvironment);\n }\n\n return typeEnvironment;\n}\n\nfunction isExpressionSegment(\n segment: WorkflowTemplateSegment,\n): segment is WorkflowTemplateExprSegment {\n return segment.kind === 'expr';\n}\n\nfunction isWorkflowContextName(root: string): root is WorkflowContextName {\n return (workflowContextNames as readonly string[]).includes(root);\n}\n\nfunction uniqueStrings(values: readonly string[]): readonly string[] {\n return [...new Set(values)];\n}\n\nfunction formatList(values: readonly string[]): string {\n return values.map((value) => `\"${value}\"`).join(', ');\n}\n\nfunction fieldLabel(field: WorkflowInterpolationField): string {\n return `Workflow ${field}`;\n}\n\nfunction issue(params: {\n code: WorkflowModelValidationIssueCode;\n message: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n details?: Readonly<Record<string, unknown>>;\n}): WorkflowModelValidationIssue {\n if (params.details === undefined) {\n return {\n code: params.code,\n message: params.message,\n path: params.path,\n };\n }\n\n return {\n code: params.code,\n message: params.message,\n path: params.path,\n details: params.details,\n };\n}\n"],"names":["secretKeySchema","secretStoreSchema","analyzeContextKeyAccess","createWorkflowExpression","extractCelUntrustedPathAccesses","getWorkflowContextAvailability","getWorkflowContextDefinition","getWorkflowContextHost","getWorkflowContextTypeEnvironment","getWorkflowContextUntrustedPaths","InvalidWorkflowExpressionError","InvalidWorkflowTemplateError","parseWorkflowTemplate","planInterpolationField","unavailableRootsAt","workflowContextNames","workflowInterpolationFieldAcceptsContext","workflowInterpolationFieldAcceptsHost","workflowInterpolationFieldAcceptsTrustTier","validateDirectJobReferences","parseInterpolationField","params","segments","parseTemplate","undefined","expressionSegments","filter","isExpressionSegment","length","checkedSegments","map","segment","kind","validatedSegment","validateExpressionSegment","plan","field","ok","issues","push","violations","violation","planViolationIssue","source","error","issue","code","message","fieldLabel","path","details","reason","contextRoots","uniqueStrings","knownRoots","isWorkflowContextName","unknownRoots","root","rejectedRoots","untrustedContextIssue","rejectedHostRoots","runnerContextInFieldIssue","keyAccess","expression","computedContextKeyIssue","invalidReferenceIssue","validateContextKeyReferences","references","invalidJobReferenceIssue","allowedJobReferences","rejectedPathRoots","findUntrustedPathRoots","formatList","fillSite","serverRoots","unavailableRoots","unavailableContextIssue","typeOverlay","some","hasSyntaxOnlyCheckMode","check","mode","typeEnvironment","mergeTypeEnvironments","isTrustedOnlyField","untrustedPathsByRoot","Map","paths","set","includes","Set","get","size","reference","safeParse","key","success","store","availabilitySiteLabels","ingest","contextNoun","availabilityVerb","describeAvailabilitySite","unavailableRootAvailabilityMessage","join","hint","runnerRoots","roots","availability","site","checkMode","overlayType","contextTypeEnvironment","Object","assign","values","value"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/workflow-model/parse-interpolation-field.ts"],"sourcesContent":["import {secretKeySchema, secretStoreSchema} from '@shipfox/api-secrets-dto';\nimport {\n type AvailabilitySite,\n analyzeContextKeyAccess,\n analyzeContextRootKeyAccess,\n createWorkflowExpression,\n type ExpressionTypeEnvironment,\n getWorkflowContextAvailability,\n getWorkflowContextDefinition,\n getWorkflowContextHost,\n getWorkflowContextTypeEnvironment,\n getWorkflowInterpolationFieldSelfReference,\n InvalidWorkflowExpressionError,\n InvalidWorkflowTemplateError,\n type PlanViolation,\n parseWorkflowTemplate,\n planInterpolationField,\n unavailableRootsAt,\n type WorkflowContextName,\n type WorkflowInterpolationField,\n type WorkflowTemplateExprSegment,\n type WorkflowTemplateSegment,\n workflowContextNames,\n workflowInterpolationFieldAcceptsHost,\n} from '@shipfox/expression';\nimport type {WorkflowFieldTemplate} from '../entities/workflow-model.js';\nimport type {\n WorkflowModelValidationIssue,\n WorkflowModelValidationIssueCode,\n WorkflowModelValidationIssuePathSegment,\n} from './invalid-workflow-model-error.js';\nimport {validateDirectJobReferences} from './validate-job-references.js';\n\nexport type StoredInterpolationField =\n | 'run'\n | 'env.value'\n | 'agent.prompt'\n | 'agent.model'\n | 'agent.provider'\n | 'agent.thinking'\n | 'job.outputs'\n | 'workflow.run_name'\n | 'job.execution_name'\n | 'job.runner'\n | 'step.name'\n | 'step.working_directory'\n | 'step.feedback'\n | 'checkout.project'\n | 'checkout.connection'\n | 'checkout.repository'\n | 'checkout.ref'\n | 'checkout.path';\n\nexport function parseInterpolationField(params: {\n field: StoredInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n issues: WorkflowModelValidationIssue[];\n fillSite?: AvailabilitySite;\n allowedJobReferences?: ReadonlySet<string>;\n typeOverlay?: ExpressionTypeEnvironment | undefined;\n}): WorkflowFieldTemplate | undefined {\n const segments = parseTemplate(params);\n if (segments === undefined) return undefined;\n\n const expressionSegments = segments.filter(isExpressionSegment);\n if (expressionSegments.length === 0) return undefined;\n\n const checkedSegments = segments.map((segment) => {\n if (segment.kind === 'literal') return segment;\n\n const validatedSegment = validateExpressionSegment({...params, segment});\n return validatedSegment ?? segment;\n });\n\n const plan = planInterpolationField({field: params.field, segments: checkedSegments});\n if (!plan.ok) {\n params.issues.push(\n ...plan.violations.map((violation) => planViolationIssue(params, violation)),\n );\n return undefined;\n }\n\n return plan.plan.field.segments;\n}\n\nfunction dynamicNameSelfReference(params: {\n field: StoredInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n segment: WorkflowTemplateExprSegment;\n}): WorkflowModelValidationIssue | undefined {\n const target = getWorkflowInterpolationFieldSelfReference(params.field);\n if (target === undefined) return undefined;\n\n const access = analyzeContextRootKeyAccess(params.segment.expression, [target.root]);\n const reference = access.references.find((entry) => entry.key === target.key);\n const computedReference = access.violations.find(\n (violation) =>\n violation.root === target.root &&\n (violation.key === undefined || violation.key === target.key),\n );\n if (reference === undefined && computedReference === undefined) return undefined;\n\n return issue({\n code: 'dynamic-name-self-reference',\n message: `${fieldLabel(params.field)} interpolation cannot reference its own dynamic name.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n reference:\n reference === undefined ? `${target.root}[computed]` : `${reference.root}.${reference.key}`,\n },\n });\n}\n\nfunction parseTemplate(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n issues: WorkflowModelValidationIssue[];\n}): WorkflowTemplateSegment[] | undefined {\n try {\n return parseWorkflowTemplate(params.source);\n } catch (error) {\n params.issues.push(\n issue({\n code: 'invalid-interpolation-template',\n message: `${fieldLabel(params.field)} must use valid $${'{{ }}'} interpolation syntax.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n reason:\n error instanceof InvalidWorkflowTemplateError\n ? error.reason\n : 'Template source did not parse.',\n },\n }),\n );\n return undefined;\n }\n}\n\nfunction validateExpressionSegment(params: {\n field: StoredInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n issues: WorkflowModelValidationIssue[];\n segment: WorkflowTemplateExprSegment;\n fillSite?: AvailabilitySite;\n allowedJobReferences?: ReadonlySet<string>;\n typeOverlay?: ExpressionTypeEnvironment | undefined;\n}): WorkflowTemplateExprSegment | undefined {\n const contextRoots = uniqueStrings(params.segment.contextRoots);\n const knownRoots = contextRoots.filter(isWorkflowContextName);\n const unknownRoots = contextRoots.filter((root) => !isWorkflowContextName(root));\n\n const rejectedHostRoots = knownRoots.filter(\n (root) => !workflowInterpolationFieldAcceptsHost(params.field, getWorkflowContextHost(root)),\n );\n if (rejectedHostRoots.length > 0) {\n params.issues.push(runnerContextInFieldIssue({...params, contextRoots, rejectedHostRoots}));\n return undefined;\n }\n\n const keyAccess = analyzeContextKeyAccess(params.segment.expression);\n if (keyAccess.violations.length > 0) {\n params.issues.push(\n ...keyAccess.violations.map((violation) =>\n computedContextKeyIssue({\n ...params,\n contextRoots,\n root: violation.root,\n expression: violation.source,\n }),\n ),\n );\n return undefined;\n }\n\n const selfReference = dynamicNameSelfReference(params);\n if (selfReference !== undefined) {\n params.issues.push(selfReference);\n return undefined;\n }\n\n const invalidReferenceIssue = validateContextKeyReferences({\n ...params,\n contextRoots,\n references: keyAccess.references,\n });\n if (invalidReferenceIssue !== undefined) {\n params.issues.push(invalidReferenceIssue);\n return undefined;\n }\n\n const invalidJobReferenceIssue =\n params.allowedJobReferences === undefined\n ? undefined\n : validateDirectJobReferences({\n source: params.source,\n expression: params.segment.expression,\n field: params.field,\n path: params.path,\n allowedJobReferences: params.allowedJobReferences,\n });\n if (invalidJobReferenceIssue !== undefined) {\n params.issues.push(invalidJobReferenceIssue);\n return undefined;\n }\n\n if (unknownRoots.length > 0) {\n params.issues.push(\n issue({\n code: 'unknown-interpolation-context',\n message: `${fieldLabel(params.field)} interpolation references unknown context ${formatList(\n unknownRoots,\n )}.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots,\n unknownRoots,\n },\n }),\n );\n return undefined;\n }\n\n const fillSite = params.fillSite;\n if (fillSite !== undefined) {\n const serverRoots = knownRoots.filter((root) => getWorkflowContextHost(root) === 'server');\n const unavailableRoots = unavailableRootsAt(serverRoots, fillSite);\n if (unavailableRoots.length > 0) {\n params.issues.push(\n unavailableContextIssue({...params, contextRoots, unavailableRoots, fillSite}),\n );\n return undefined;\n }\n }\n\n if (\n params.typeOverlay === undefined &&\n (knownRoots.length === 0 || knownRoots.some((root) => hasSyntaxOnlyCheckMode(root)))\n ) {\n return params.segment;\n }\n\n try {\n return {\n ...params.segment,\n expression: createWorkflowExpression({\n source: params.segment.expression.source,\n check: {\n mode: 'typed',\n // `undefined` preserves the legacy syntax-only path above. `{}` means\n // callers intentionally requested typed checking with the standard roots.\n typeEnvironment: mergeTypeEnvironments(knownRoots, params.typeOverlay),\n },\n }),\n };\n } catch (error) {\n params.issues.push(\n issue({\n code: 'invalid-interpolation-expression',\n message: `${fieldLabel(params.field)} interpolation expression did not type-check.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots,\n reason:\n error instanceof InvalidWorkflowExpressionError\n ? error.reason\n : 'Expression source did not type-check.',\n },\n }),\n );\n return undefined;\n }\n}\n\nfunction runnerContextInFieldIssue(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n contextRoots: readonly string[];\n rejectedHostRoots: readonly WorkflowContextName[];\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'runner-context-in-field',\n message: `${fieldLabel(params.field)} interpolation cannot use runner context ${formatList(\n params.rejectedHostRoots,\n )}. Bind secrets to a run-step env value instead.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n contextRoots: params.contextRoots,\n rejectedRoots: params.rejectedHostRoots,\n },\n });\n}\n\nfunction computedContextKeyIssue(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n contextRoots: readonly string[];\n root: string;\n expression: string;\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'computed-context-key',\n message: `${fieldLabel(params.field)} interpolation must reference ${params.root} with a literal dot key.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.expression,\n contextRoots: params.contextRoots,\n root: params.root,\n },\n });\n}\n\nfunction validateContextKeyReferences(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n contextRoots: readonly string[];\n segment: WorkflowTemplateExprSegment;\n references: ReturnType<typeof analyzeContextKeyAccess>['references'];\n}): WorkflowModelValidationIssue | undefined {\n for (const reference of params.references) {\n if (!secretKeySchema.safeParse(reference.key).success) {\n return computedContextKeyIssue({\n ...params,\n root: reference.root,\n expression: params.segment.expression.source,\n });\n }\n\n if (reference.root !== 'secrets' || reference.store === undefined) continue;\n if (secretStoreSchema.safeParse(reference.store).success) continue;\n\n return issue({\n code: 'unknown-secret-store',\n message: `${fieldLabel(params.field)} interpolation references unknown secret store \"${reference.store}\".`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots: params.contextRoots,\n store: reference.store,\n },\n });\n }\n\n return undefined;\n}\n\nconst availabilitySiteLabels = {\n ingest: 'ingest',\n 'run-creation': 'run creation',\n 'execution-creation': 'execution creation',\n 'job-activation': 'job activation',\n 'step-dispatch': 'step dispatch',\n 'step-report': 'step reporting',\n 'execution-resolution': 'execution resolution',\n 'job-resolution': 'job resolution',\n} as const satisfies Record<AvailabilitySite, string>;\n\nfunction unavailableContextIssue(params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n segment: WorkflowTemplateExprSegment;\n contextRoots: readonly string[];\n unavailableRoots: readonly WorkflowContextName[];\n fillSite: AvailabilitySite;\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'context-unavailable-at-fill-site',\n message: `${fieldLabel(params.field)} interpolation references ${contextNoun(\n params.unavailableRoots,\n )} ${formatList(params.unavailableRoots)} that ${availabilityVerb(\n params.unavailableRoots,\n )} not available at ${describeAvailabilitySite(params.fillSite)}. ${params.unavailableRoots\n .map(unavailableRootAvailabilityMessage)\n .join(' ')}`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: params.segment.expression.source,\n contextRoots: params.contextRoots,\n unavailableRoots: params.unavailableRoots,\n fillSite: params.fillSite,\n },\n });\n}\n\nfunction planViolationIssue(\n params: {\n field: WorkflowInterpolationField;\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n },\n violation: PlanViolation,\n): WorkflowModelValidationIssue {\n if (violation.reason === 'computed-context-key') {\n return issue({\n code: 'computed-context-key',\n message: `${fieldLabel(params.field)} interpolation must reference ${formatList(\n violation.contextRoots ?? [],\n )} with a literal dot key.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: violation.source,\n contextRoots: violation.contextRoots,\n },\n });\n }\n\n return issue({\n code: 'runner-context-not-bare',\n message: `${fieldLabel(params.field)} interpolation references runner context in a larger expression; ${violation.hint}.`,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n expression: violation.source,\n runnerRoots: violation.runnerRoots ?? [],\n },\n });\n}\n\nfunction contextNoun(roots: readonly WorkflowContextName[]): 'context' | 'contexts' {\n return roots.length === 1 ? 'context' : 'contexts';\n}\n\nfunction availabilityVerb(roots: readonly WorkflowContextName[]): 'is' | 'are' {\n return roots.length === 1 ? 'is' : 'are';\n}\n\nfunction unavailableRootAvailabilityMessage(root: WorkflowContextName): string {\n const availability = getWorkflowContextAvailability(root);\n if (availability === undefined) return `\"${root}\" is not available at any server site.`;\n return `\"${root}\" becomes available at ${describeAvailabilitySite(availability)}.`;\n}\n\nfunction describeAvailabilitySite(site: AvailabilitySite): string {\n return availabilitySiteLabels[site];\n}\n\nfunction hasSyntaxOnlyCheckMode(root: WorkflowContextName): boolean {\n return getWorkflowContextDefinition(root).checkMode === 'syntax';\n}\n\nfunction mergeTypeEnvironments(\n roots: readonly WorkflowContextName[],\n typeOverlay?: ExpressionTypeEnvironment,\n): ExpressionTypeEnvironment {\n const typeEnvironment: Record<string, ExpressionTypeEnvironment[string]> = {};\n\n for (const root of roots) {\n const overlayType = typeOverlay?.[root];\n if (overlayType !== undefined) {\n typeEnvironment[root] = overlayType;\n continue;\n }\n\n const contextTypeEnvironment = getWorkflowContextTypeEnvironment(root);\n if (contextTypeEnvironment === undefined) {\n if (typeOverlay !== undefined) typeEnvironment[root] = {kind: 'map'};\n continue;\n }\n\n Object.assign(typeEnvironment, contextTypeEnvironment);\n }\n\n return typeEnvironment;\n}\n\nfunction isExpressionSegment(\n segment: WorkflowTemplateSegment,\n): segment is WorkflowTemplateExprSegment {\n return segment.kind === 'expr';\n}\n\nfunction isWorkflowContextName(root: string): root is WorkflowContextName {\n return (workflowContextNames as readonly string[]).includes(root);\n}\n\nfunction uniqueStrings(values: readonly string[]): readonly string[] {\n return [...new Set(values)];\n}\n\nfunction formatList(values: readonly string[]): string {\n return values.map((value) => `\"${value}\"`).join(', ');\n}\n\nfunction fieldLabel(field: WorkflowInterpolationField): string {\n return `Workflow ${field}`;\n}\n\nfunction issue(params: {\n code: WorkflowModelValidationIssueCode;\n message: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n details?: Readonly<Record<string, unknown>>;\n}): WorkflowModelValidationIssue {\n if (params.details === undefined) {\n return {\n code: params.code,\n message: params.message,\n path: params.path,\n };\n }\n\n return {\n code: params.code,\n message: params.message,\n path: params.path,\n details: params.details,\n };\n}\n"],"names":["secretKeySchema","secretStoreSchema","analyzeContextKeyAccess","analyzeContextRootKeyAccess","createWorkflowExpression","getWorkflowContextAvailability","getWorkflowContextDefinition","getWorkflowContextHost","getWorkflowContextTypeEnvironment","getWorkflowInterpolationFieldSelfReference","InvalidWorkflowExpressionError","InvalidWorkflowTemplateError","parseWorkflowTemplate","planInterpolationField","unavailableRootsAt","workflowContextNames","workflowInterpolationFieldAcceptsHost","validateDirectJobReferences","parseInterpolationField","params","segments","parseTemplate","undefined","expressionSegments","filter","isExpressionSegment","length","checkedSegments","map","segment","kind","validatedSegment","validateExpressionSegment","plan","field","ok","issues","push","violations","violation","planViolationIssue","dynamicNameSelfReference","target","access","expression","root","reference","references","find","entry","key","computedReference","issue","code","message","fieldLabel","path","details","source","error","reason","contextRoots","uniqueStrings","knownRoots","isWorkflowContextName","unknownRoots","rejectedHostRoots","runnerContextInFieldIssue","keyAccess","computedContextKeyIssue","selfReference","invalidReferenceIssue","validateContextKeyReferences","invalidJobReferenceIssue","allowedJobReferences","formatList","fillSite","serverRoots","unavailableRoots","unavailableContextIssue","typeOverlay","some","hasSyntaxOnlyCheckMode","check","mode","typeEnvironment","mergeTypeEnvironments","rejectedRoots","safeParse","success","store","availabilitySiteLabels","ingest","contextNoun","availabilityVerb","describeAvailabilitySite","unavailableRootAvailabilityMessage","join","hint","runnerRoots","roots","availability","site","checkMode","overlayType","contextTypeEnvironment","Object","assign","includes","values","Set","value"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { WorkflowModelValidationIssue, WorkflowModelValidationIssuePathSegment } from './invalid-workflow-model-error.js';
2
+ /** Defense-in-depth for typed callers that bypass workflowDocumentSchema. */
3
+ export declare function validateLiteralName(params: {
4
+ field: 'workflow.name' | 'job.name';
5
+ dynamicField: 'run_name' | 'execution_name';
6
+ source: string;
7
+ path: readonly WorkflowModelValidationIssuePathSegment[];
8
+ message: string;
9
+ issues: WorkflowModelValidationIssue[];
10
+ }): void;
11
+ export declare function unescapeLiteralName(source: string): string;
12
+ //# sourceMappingURL=validate-literal-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-literal-name.d.ts","sourceRoot":"","sources":["../../../src/core/workflow-model/validate-literal-name.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,4BAA4B,EAC5B,uCAAuC,EACxC,MAAM,mCAAmC,CAAC;AAG3C,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,KAAK,EAAE,eAAe,GAAG,UAAU,CAAC;IACpC,YAAY,EAAE,UAAU,GAAG,gBAAgB,CAAC;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,uCAAuC,EAAE,CAAC;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,4BAA4B,EAAE,CAAC;CACxC,GAAG,IAAI,CAuBP;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D"}
@@ -0,0 +1,36 @@
1
+ import { InvalidWorkflowTemplateError, parseWorkflowTemplate } from '@shipfox/expression';
2
+ import { issue } from './validation-issue.js';
3
+ /** Defense-in-depth for typed callers that bypass workflowDocumentSchema. */ export function validateLiteralName(params) {
4
+ try {
5
+ const segments = parseWorkflowTemplate(params.source);
6
+ if (!segments.some((segment)=>segment.kind === 'expr')) return;
7
+ } catch (error) {
8
+ params.issues.push(literalNameIssue({
9
+ ...params,
10
+ reason: error instanceof InvalidWorkflowTemplateError ? error.reason : 'Template source did not parse.'
11
+ }));
12
+ return;
13
+ }
14
+ params.issues.push(literalNameIssue({
15
+ ...params,
16
+ reason: `Static names cannot contain interpolation expressions. Use ${params.dynamicField} for runtime interpolation.`
17
+ }));
18
+ }
19
+ export function unescapeLiteralName(source) {
20
+ return source.replaceAll('$${{', '${{');
21
+ }
22
+ function literalNameIssue(params) {
23
+ return issue({
24
+ code: 'invalid-interpolation-template',
25
+ message: params.message,
26
+ path: params.path,
27
+ details: {
28
+ field: params.field,
29
+ source: params.source,
30
+ dynamicField: params.dynamicField,
31
+ reason: params.reason
32
+ }
33
+ });
34
+ }
35
+
36
+ //# sourceMappingURL=validate-literal-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/workflow-model/validate-literal-name.ts"],"sourcesContent":["import {InvalidWorkflowTemplateError, parseWorkflowTemplate} from '@shipfox/expression';\nimport type {\n WorkflowModelValidationIssue,\n WorkflowModelValidationIssuePathSegment,\n} from './invalid-workflow-model-error.js';\nimport {issue} from './validation-issue.js';\n\n/** Defense-in-depth for typed callers that bypass workflowDocumentSchema. */\nexport function validateLiteralName(params: {\n field: 'workflow.name' | 'job.name';\n dynamicField: 'run_name' | 'execution_name';\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n message: string;\n issues: WorkflowModelValidationIssue[];\n}): void {\n try {\n const segments = parseWorkflowTemplate(params.source);\n if (!segments.some((segment) => segment.kind === 'expr')) return;\n } catch (error) {\n params.issues.push(\n literalNameIssue({\n ...params,\n reason:\n error instanceof InvalidWorkflowTemplateError\n ? error.reason\n : 'Template source did not parse.',\n }),\n );\n return;\n }\n\n params.issues.push(\n literalNameIssue({\n ...params,\n reason: `Static names cannot contain interpolation expressions. Use ${params.dynamicField} for runtime interpolation.`,\n }),\n );\n}\n\nexport function unescapeLiteralName(source: string): string {\n return source.replaceAll('$${{', '${{');\n}\n\nfunction literalNameIssue(params: {\n field: 'workflow.name' | 'job.name';\n dynamicField: 'run_name' | 'execution_name';\n source: string;\n path: readonly WorkflowModelValidationIssuePathSegment[];\n message: string;\n reason: string;\n}): WorkflowModelValidationIssue {\n return issue({\n code: 'invalid-interpolation-template',\n message: params.message,\n path: params.path,\n details: {\n field: params.field,\n source: params.source,\n dynamicField: params.dynamicField,\n reason: params.reason,\n },\n });\n}\n"],"names":["InvalidWorkflowTemplateError","parseWorkflowTemplate","issue","validateLiteralName","params","segments","source","some","segment","kind","error","issues","push","literalNameIssue","reason","dynamicField","unescapeLiteralName","replaceAll","code","message","path","details","field"],"mappings":"AAAA,SAAQA,4BAA4B,EAAEC,qBAAqB,QAAO,sBAAsB;AAKxF,SAAQC,KAAK,QAAO,wBAAwB;AAE5C,2EAA2E,GAC3E,OAAO,SAASC,oBAAoBC,MAOnC;IACC,IAAI;QACF,MAAMC,WAAWJ,sBAAsBG,OAAOE,MAAM;QACpD,IAAI,CAACD,SAASE,IAAI,CAAC,CAACC,UAAYA,QAAQC,IAAI,KAAK,SAAS;IAC5D,EAAE,OAAOC,OAAO;QACdN,OAAOO,MAAM,CAACC,IAAI,CAChBC,iBAAiB;YACf,GAAGT,MAAM;YACTU,QACEJ,iBAAiBV,+BACbU,MAAMI,MAAM,GACZ;QACR;QAEF;IACF;IAEAV,OAAOO,MAAM,CAACC,IAAI,CAChBC,iBAAiB;QACf,GAAGT,MAAM;QACTU,QAAQ,CAAC,2DAA2D,EAAEV,OAAOW,YAAY,CAAC,2BAA2B,CAAC;IACxH;AAEJ;AAEA,OAAO,SAASC,oBAAoBV,MAAc;IAChD,OAAOA,OAAOW,UAAU,CAAC,QAAQ;AACnC;AAEA,SAASJ,iBAAiBT,MAOzB;IACC,OAAOF,MAAM;QACXgB,MAAM;QACNC,SAASf,OAAOe,OAAO;QACvBC,MAAMhB,OAAOgB,IAAI;QACjBC,SAAS;YACPC,OAAOlB,OAAOkB,KAAK;YACnBhB,QAAQF,OAAOE,MAAM;YACrBS,cAAcX,OAAOW,YAAY;YACjCD,QAAQV,OAAOU,MAAM;QACvB;IACF;AACF"}
@@ -1,9 +1,8 @@
1
- import { type AvailabilitySite, type ExpressionTypeEnvironment, type WorkflowExpression, type WorkflowPredicateField } from '@shipfox/expression';
1
+ import { type ExpressionTypeEnvironment, type WorkflowExpression, type WorkflowPredicateField } from '@shipfox/expression';
2
2
  import type { WorkflowModelValidationIssue, WorkflowModelValidationIssueCode, WorkflowModelValidationIssuePathSegment } from './invalid-workflow-model-error.js';
3
3
  export declare function validatePredicateExpression(params: {
4
4
  field: WorkflowPredicateField;
5
5
  source: string;
6
- site: AvailabilitySite;
7
6
  path: readonly WorkflowModelValidationIssuePathSegment[];
8
7
  invalidCode: WorkflowModelValidationIssueCode;
9
8
  invalidMessage: string;
@@ -1 +1 @@
1
- {"version":3,"file":"validate-predicate-expression.d.ts","sourceRoot":"","sources":["../../../src/core/workflow-model/validate-predicate-expression.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAIrB,KAAK,yBAAyB,EAW9B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAE5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,4BAA4B,EAC5B,gCAAgC,EAChC,uCAAuC,EACxC,MAAM,mCAAmC,CAAC;AAK3C,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,KAAK,EAAE,sBAAsB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,SAAS,uCAAuC,EAAE,CAAC;IACzD,WAAW,EAAE,gCAAgC,CAAC;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,4BAA4B,EAAE,CAAC;IACvC,oBAAoB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;CACrD,GAAG,kBAAkB,GAAG,SAAS,CAqHjC"}
1
+ {"version":3,"file":"validate-predicate-expression.d.ts","sourceRoot":"","sources":["../../../src/core/workflow-model/validate-predicate-expression.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,yBAAyB,EAW9B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAE5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,4BAA4B,EAC5B,gCAAgC,EAChC,uCAAuC,EACxC,MAAM,mCAAmC,CAAC;AAK3C,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,KAAK,EAAE,sBAAsB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,uCAAuC,EAAE,CAAC;IACzD,WAAW,EAAE,gCAAgC,CAAC;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,4BAA4B,EAAE,CAAC;IACvC,oBAAoB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;CACrD,GAAG,kBAAkB,GAAG,SAAS,CA0GjC"}
@@ -1,8 +1,9 @@
1
- import { analyzeContextRootKeyAccess, availabilitySites, createWorkflowExpression, extractExactContextRoots, getWorkflowContextDefinition, getWorkflowContextTypeEnvironment, InvalidWorkflowExpressionError, predicateSourceIsBooleanShaped, resolveContextRootAvailability, resolveContextRootHost, routeExpression, validateServerEvaluable, workflowContextNames } from '@shipfox/expression';
1
+ import { createWorkflowExpression, extractExactContextRoots, getWorkflowContextDefinition, getWorkflowPredicateContextRoots, getWorkflowPredicateFieldMinimumFillTarget, getWorkflowPredicateFieldTypeEnvironment, InvalidWorkflowExpressionError, predicateSourceIsBooleanShaped, resolveContextRootHost, validateServerEvaluable, workflowContextNames } from '@shipfox/expression';
2
2
  import { validateDirectJobReferences } from './validate-job-references.js';
3
3
  import { issue } from './validation-issue.js';
4
4
  import { workflowFieldLabel } from './workflow-field-label.js';
5
5
  export function validatePredicateExpression(params) {
6
+ const site = getWorkflowPredicateFieldMinimumFillTarget(params.field);
6
7
  const syntaxExpression = createSyntaxExpression(params);
7
8
  if (syntaxExpression === undefined) return undefined;
8
9
  const contextRoots = extractExactContextRoots(syntaxExpression.source);
@@ -19,42 +20,23 @@ export function validatePredicateExpression(params) {
19
20
  if (!serverEvaluability.ok) {
20
21
  params.issues.push(...serverEvaluability.violations.map((violation)=>runnerContextInServerPredicateIssue({
21
22
  ...params,
23
+ site,
22
24
  contextRoots,
23
25
  runnerRoots: violation.runnerRoots
24
26
  })));
25
27
  return undefined;
26
28
  }
27
- const varsRoots = knownRoots.filter((root)=>root === 'vars');
28
- if (varsRoots.length > 0) {
29
- params.issues.push(varsContextInServerPredicateIssue({
30
- ...params,
31
- contextRoots
32
- }));
33
- return undefined;
34
- }
35
- const unavailableRoots = knownRoots.filter((root)=>!isRootAvailableAt(root, params.site));
29
+ const predicateContextRoots = getWorkflowPredicateContextRoots(params.field);
30
+ const unavailableRoots = knownRoots.filter((root)=>!predicateContextRoots.includes(root));
36
31
  if (unavailableRoots.length > 0) {
37
32
  params.issues.push(unavailablePredicateContextIssue({
38
33
  ...params,
34
+ site,
39
35
  contextRoots,
40
36
  unavailableRoots
41
37
  }));
42
38
  return undefined;
43
39
  }
44
- const route = routeExpression(syntaxExpression);
45
- if (route.fillTarget !== 'runner-fill' && !isSiteAtOrAfter(params.site, route.fillTarget)) {
46
- const unavailableRoots = unavailableRouteReferences(syntaxExpression);
47
- params.issues.push(unavailableRoots.length > 0 ? unavailablePredicateContextIssue({
48
- ...params,
49
- contextRoots,
50
- unavailableRoots
51
- }) : routeUnavailablePredicateContextIssue({
52
- ...params,
53
- contextRoots,
54
- fillTarget: route.fillTarget
55
- }));
56
- return undefined;
57
- }
58
40
  const invalidJobReferenceIssue = params.allowedJobReferences === undefined ? undefined : validateDirectJobReferences({
59
41
  source: params.source,
60
42
  expression: syntaxExpression,
@@ -66,7 +48,7 @@ export function validatePredicateExpression(params) {
66
48
  params.issues.push(invalidJobReferenceIssue);
67
49
  return undefined;
68
50
  }
69
- if (params.typeOverlay === undefined && knownRoots.some((root)=>hasSyntaxOnlyCheckMode(root))) {
51
+ if (params.typeOverlay === undefined && knownRoots.length > 0 && knownRoots.every((root)=>hasSyntaxOnlyCheckMode(root))) {
70
52
  if (isWorkflowFilterPredicateField(params.field) && !predicateSourceIsBooleanShaped(syntaxExpression.source)) {
71
53
  params.issues.push(invalidPredicateIssue({
72
54
  ...params,
@@ -84,7 +66,7 @@ export function validatePredicateExpression(params) {
84
66
  mode: 'typed',
85
67
  // `undefined` preserves the legacy syntax-only path above. `{}` means
86
68
  // callers intentionally requested typed checking with the standard roots.
87
- typeEnvironment: mergeTypeEnvironments(knownRoots, params.typeOverlay),
69
+ typeEnvironment: mergeTypeEnvironments(params.field, knownRoots, params.typeOverlay),
88
70
  expectedResultType: 'bool'
89
71
  }
90
72
  });
@@ -151,26 +133,10 @@ function runnerContextInServerPredicateIssue(params) {
151
133
  }
152
134
  });
153
135
  }
154
- function varsContextInServerPredicateIssue(params) {
155
- return issue({
156
- code: 'vars-context-in-server-predicate',
157
- message: `${fieldLabel(params.field)} cannot reference vars because predicate evaluation does not include workflow variables.`,
158
- path: params.path,
159
- details: {
160
- field: params.field,
161
- source: params.source,
162
- contextRoots: params.contextRoots,
163
- rejectedRoots: [
164
- 'vars'
165
- ],
166
- site: params.site
167
- }
168
- });
169
- }
170
136
  function unavailablePredicateContextIssue(params) {
171
137
  return issue({
172
138
  code: 'context-unavailable-at-predicate-site',
173
- message: `${fieldLabel(params.field)} references ${contextNoun(params.unavailableRoots)} ${formatList(params.unavailableRoots)} that ${availabilityVerb(params.unavailableRoots)} not available at ${describeAvailabilitySite(params.site)}. ${params.unavailableRoots.map(unavailableRootAvailabilityMessage).join(' ')}`,
139
+ message: `${fieldLabel(params.field)} references ${contextNoun(params.unavailableRoots)} ${formatList(params.unavailableRoots)} that ${availabilityVerb(params.unavailableRoots)} not supplied when ${fieldLabel(params.field)} is evaluated at ${describeAvailabilitySite(params.site)}.`,
174
140
  path: params.path,
175
141
  details: {
176
142
  field: params.field,
@@ -181,70 +147,48 @@ function unavailablePredicateContextIssue(params) {
181
147
  }
182
148
  });
183
149
  }
184
- function routeUnavailablePredicateContextIssue(params) {
185
- return issue({
186
- code: 'context-unavailable-at-predicate-site',
187
- message: `${fieldLabel(params.field)} requires context filled at ${describeAvailabilitySite(params.fillTarget)}, but it is evaluated at ${describeAvailabilitySite(params.site)}.`,
188
- path: params.path,
189
- details: {
190
- field: params.field,
191
- source: params.source,
192
- contextRoots: params.contextRoots,
193
- fillTarget: params.fillTarget,
194
- site: params.site
195
- }
196
- });
197
- }
198
- function isRootAvailableAt(root, site) {
199
- const availability = resolveContextRootAvailability(root);
200
- if (availability === undefined) return false;
201
- return availabilitySites.indexOf(availability) <= availabilitySites.indexOf(site);
202
- }
203
- function unavailableRootAvailabilityMessage(root) {
204
- if (root === 'execution.failed') {
205
- return '"execution.failed" becomes available at step dispatch.';
206
- }
207
- const availability = resolveContextRootAvailability(root);
208
- if (availability === undefined) return `"${root}" is not available at any server site.`;
209
- return `"${root}" becomes available at ${describeAvailabilitySite(availability)}.`;
210
- }
211
- function unavailableRouteReferences(expression) {
212
- const access = analyzeContextRootKeyAccess(expression, [
213
- 'execution'
214
- ]);
215
- return access.references.some((reference)=>reference.key === 'failed') ? [
216
- 'execution.failed'
217
- ] : [];
218
- }
219
150
  function hasSyntaxOnlyCheckMode(root) {
220
151
  if (!isWorkflowContextName(root)) return resolveContextRootHost(root) === 'server';
221
152
  return isWorkflowContextName(root) && getWorkflowContextDefinition(root).checkMode === 'syntax';
222
153
  }
223
- function mergeTypeEnvironments(roots, typeOverlay) {
154
+ function mergeTypeEnvironments(field, roots, typeOverlay) {
224
155
  const typeEnvironment = {};
225
156
  for (const root of roots){
226
157
  const overlayType = typeOverlay?.[root];
227
- if (overlayType !== undefined) {
228
- typeEnvironment[root] = overlayType;
229
- continue;
230
- }
231
158
  if (!isWorkflowContextName(root)) {
232
- if (typeOverlay !== undefined) typeEnvironment[root] = {
159
+ if (overlayType !== undefined) typeEnvironment[root] = overlayType;
160
+ else typeEnvironment[root] = {
233
161
  kind: 'map'
234
162
  };
235
163
  continue;
236
164
  }
237
- const contextTypeEnvironment = getWorkflowContextTypeEnvironment(root);
165
+ const contextTypeEnvironment = getWorkflowPredicateFieldTypeEnvironment(field, root);
238
166
  if (contextTypeEnvironment === undefined) {
239
- if (typeOverlay !== undefined) typeEnvironment[root] = {
167
+ if (overlayType !== undefined) typeEnvironment[root] = overlayType;
168
+ else typeEnvironment[root] = {
240
169
  kind: 'map'
241
170
  };
242
171
  continue;
243
172
  }
244
- Object.assign(typeEnvironment, contextTypeEnvironment);
173
+ const contextType = contextTypeEnvironment[root];
174
+ if (contextType === undefined) continue;
175
+ typeEnvironment[root] = overlayType === undefined ? contextType : overlayKnownFields(contextType, overlayType);
245
176
  }
246
177
  return typeEnvironment;
247
178
  }
179
+ function overlayKnownFields(base, overlay) {
180
+ if (typeof base === 'string' || typeof overlay === 'string') return overlay;
181
+ if (base.kind !== 'object' || overlay.kind !== 'object') return overlay;
182
+ // Preserve output specialization without reintroducing properties absent from this field's
183
+ // runtime shape.
184
+ return {
185
+ kind: 'object',
186
+ fields: Object.fromEntries(Object.entries(base.fields).map(([key, fieldType])=>[
187
+ key,
188
+ overlay.fields[key] ?? fieldType
189
+ ]))
190
+ };
191
+ }
248
192
  function isWorkflowContextName(root) {
249
193
  return workflowContextNames.includes(root);
250
194
  }
@@ -263,9 +207,6 @@ function availabilityVerb(roots) {
263
207
  function describeAvailabilitySite(site) {
264
208
  return availabilitySiteLabels[site];
265
209
  }
266
- function isSiteAtOrAfter(site, fillTarget) {
267
- return availabilitySites.indexOf(site) >= availabilitySites.indexOf(fillTarget);
268
- }
269
210
  function formatList(values) {
270
211
  return values.map((value)=>`"${value}"`).join(', ');
271
212
  }