@shipfox/api-definitions 2.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 (401) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +215 -0
  5. package/LICENSE +21 -0
  6. package/dist/config.d.ts +10 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +35 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/core/entities/index.d.ts +5 -0
  11. package/dist/core/entities/index.d.ts.map +1 -0
  12. package/dist/core/entities/index.js +6 -0
  13. package/dist/core/entities/index.js.map +1 -0
  14. package/dist/core/entities/integration-context.d.ts +11 -0
  15. package/dist/core/entities/integration-context.d.ts.map +1 -0
  16. package/dist/core/entities/integration-context.js +3 -0
  17. package/dist/core/entities/integration-context.js.map +1 -0
  18. package/dist/core/entities/sync-state.d.ts +19 -0
  19. package/dist/core/entities/sync-state.d.ts.map +1 -0
  20. package/dist/core/entities/sync-state.js +20 -0
  21. package/dist/core/entities/sync-state.js.map +1 -0
  22. package/dist/core/entities/workflow-definition.d.ts +35 -0
  23. package/dist/core/entities/workflow-definition.d.ts.map +1 -0
  24. package/dist/core/entities/workflow-definition.js +3 -0
  25. package/dist/core/entities/workflow-definition.js.map +1 -0
  26. package/dist/core/entities/workflow-model.d.ts +138 -0
  27. package/dist/core/entities/workflow-model.d.ts.map +1 -0
  28. package/dist/core/entities/workflow-model.js +3 -0
  29. package/dist/core/entities/workflow-model.js.map +1 -0
  30. package/dist/core/errors.d.ts +10 -0
  31. package/dist/core/errors.d.ts.map +1 -0
  32. package/dist/core/errors.js +14 -0
  33. package/dist/core/errors.js.map +1 -0
  34. package/dist/core/has-agent-step-integrations.d.ts +3 -0
  35. package/dist/core/has-agent-step-integrations.d.ts.map +1 -0
  36. package/dist/core/has-agent-step-integrations.js +5 -0
  37. package/dist/core/has-agent-step-integrations.js.map +1 -0
  38. package/dist/core/index.d.ts +9 -0
  39. package/dist/core/index.d.ts.map +1 -0
  40. package/dist/core/index.js +9 -0
  41. package/dist/core/index.js.map +1 -0
  42. package/dist/core/parse-definition.d.ts +9 -0
  43. package/dist/core/parse-definition.d.ts.map +1 -0
  44. package/dist/core/parse-definition.js +17 -0
  45. package/dist/core/parse-definition.js.map +1 -0
  46. package/dist/core/sync-definitions.d.ts +44 -0
  47. package/dist/core/sync-definitions.d.ts.map +1 -0
  48. package/dist/core/sync-definitions.js +140 -0
  49. package/dist/core/sync-definitions.js.map +1 -0
  50. package/dist/core/validate-definition.d.ts +20 -0
  51. package/dist/core/validate-definition.d.ts.map +1 -0
  52. package/dist/core/validate-definition.js +61 -0
  53. package/dist/core/validate-definition.js.map +1 -0
  54. package/dist/core/workflow-model/constants.d.ts +2 -0
  55. package/dist/core/workflow-model/constants.d.ts.map +1 -0
  56. package/dist/core/workflow-model/constants.js +3 -0
  57. package/dist/core/workflow-model/constants.js.map +1 -0
  58. package/dist/core/workflow-model/cron-trigger.d.ts +16 -0
  59. package/dist/core/workflow-model/cron-trigger.d.ts.map +1 -0
  60. package/dist/core/workflow-model/cron-trigger.js +85 -0
  61. package/dist/core/workflow-model/cron-trigger.js.map +1 -0
  62. package/dist/core/workflow-model/index.d.ts +4 -0
  63. package/dist/core/workflow-model/index.d.ts.map +1 -0
  64. package/dist/core/workflow-model/index.js +5 -0
  65. package/dist/core/workflow-model/index.js.map +1 -0
  66. package/dist/core/workflow-model/invalid-workflow-model-error.d.ts +15 -0
  67. package/dist/core/workflow-model/invalid-workflow-model-error.d.ts.map +1 -0
  68. package/dist/core/workflow-model/invalid-workflow-model-error.js +9 -0
  69. package/dist/core/workflow-model/invalid-workflow-model-error.js.map +1 -0
  70. package/dist/core/workflow-model/map-job-ids.d.ts +4 -0
  71. package/dist/core/workflow-model/map-job-ids.d.ts.map +1 -0
  72. package/dist/core/workflow-model/map-job-ids.js +33 -0
  73. package/dist/core/workflow-model/map-job-ids.js.map +1 -0
  74. package/dist/core/workflow-model/normalize-agent-integrations.d.ts +12 -0
  75. package/dist/core/workflow-model/normalize-agent-integrations.d.ts.map +1 -0
  76. package/dist/core/workflow-model/normalize-agent-integrations.js +157 -0
  77. package/dist/core/workflow-model/normalize-agent-integrations.js.map +1 -0
  78. package/dist/core/workflow-model/normalize-dependencies.d.ts +6 -0
  79. package/dist/core/workflow-model/normalize-dependencies.d.ts.map +1 -0
  80. package/dist/core/workflow-model/normalize-dependencies.js +119 -0
  81. package/dist/core/workflow-model/normalize-dependencies.js.map +1 -0
  82. package/dist/core/workflow-model/normalize-env.d.ts +17 -0
  83. package/dist/core/workflow-model/normalize-env.d.ts.map +1 -0
  84. package/dist/core/workflow-model/normalize-env.js +40 -0
  85. package/dist/core/workflow-model/normalize-env.js.map +1 -0
  86. package/dist/core/workflow-model/normalize-if-condition.d.ts +14 -0
  87. package/dist/core/workflow-model/normalize-if-condition.d.ts.map +1 -0
  88. package/dist/core/workflow-model/normalize-if-condition.js +75 -0
  89. package/dist/core/workflow-model/normalize-if-condition.js.map +1 -0
  90. package/dist/core/workflow-model/normalize-job-checkout.d.ts +5 -0
  91. package/dist/core/workflow-model/normalize-job-checkout.d.ts.map +1 -0
  92. package/dist/core/workflow-model/normalize-job-checkout.js +16 -0
  93. package/dist/core/workflow-model/normalize-job-checkout.js.map +1 -0
  94. package/dist/core/workflow-model/normalize-job-listening.d.ts +10 -0
  95. package/dist/core/workflow-model/normalize-job-listening.d.ts.map +1 -0
  96. package/dist/core/workflow-model/normalize-job-listening.js +125 -0
  97. package/dist/core/workflow-model/normalize-job-listening.js.map +1 -0
  98. package/dist/core/workflow-model/normalize-job-success.d.ts +11 -0
  99. package/dist/core/workflow-model/normalize-job-success.d.ts.map +1 -0
  100. package/dist/core/workflow-model/normalize-job-success.js +23 -0
  101. package/dist/core/workflow-model/normalize-job-success.js.map +1 -0
  102. package/dist/core/workflow-model/normalize-jobs.d.ts +12 -0
  103. package/dist/core/workflow-model/normalize-jobs.d.ts.map +1 -0
  104. package/dist/core/workflow-model/normalize-jobs.js +935 -0
  105. package/dist/core/workflow-model/normalize-jobs.js.map +1 -0
  106. package/dist/core/workflow-model/normalize-needs.d.ts +2 -0
  107. package/dist/core/workflow-model/normalize-needs.d.ts.map +1 -0
  108. package/dist/core/workflow-model/normalize-needs.js +16 -0
  109. package/dist/core/workflow-model/normalize-needs.js.map +1 -0
  110. package/dist/core/workflow-model/normalize-step-gate.d.ts +15 -0
  111. package/dist/core/workflow-model/normalize-step-gate.d.ts.map +1 -0
  112. package/dist/core/workflow-model/normalize-step-gate.js +89 -0
  113. package/dist/core/workflow-model/normalize-step-gate.js.map +1 -0
  114. package/dist/core/workflow-model/normalize-step-outputs.d.ts +10 -0
  115. package/dist/core/workflow-model/normalize-step-outputs.d.ts.map +1 -0
  116. package/dist/core/workflow-model/normalize-step-outputs.js +32 -0
  117. package/dist/core/workflow-model/normalize-step-outputs.js.map +1 -0
  118. package/dist/core/workflow-model/normalize-triggers.d.ts +11 -0
  119. package/dist/core/workflow-model/normalize-triggers.d.ts.map +1 -0
  120. package/dist/core/workflow-model/normalize-triggers.js +128 -0
  121. package/dist/core/workflow-model/normalize-triggers.js.map +1 -0
  122. package/dist/core/workflow-model/normalize-workflow-document.d.ts +11 -0
  123. package/dist/core/workflow-model/normalize-workflow-document.d.ts.map +1 -0
  124. package/dist/core/workflow-model/normalize-workflow-document.js +43 -0
  125. package/dist/core/workflow-model/normalize-workflow-document.js.map +1 -0
  126. package/dist/core/workflow-model/parse-duration-ms.d.ts +11 -0
  127. package/dist/core/workflow-model/parse-duration-ms.d.ts.map +1 -0
  128. package/dist/core/workflow-model/parse-duration-ms.js +47 -0
  129. package/dist/core/workflow-model/parse-duration-ms.js.map +1 -0
  130. package/dist/core/workflow-model/parse-interpolation-field.d.ts +14 -0
  131. package/dist/core/workflow-model/parse-interpolation-field.d.ts.map +1 -0
  132. package/dist/core/workflow-model/parse-interpolation-field.js +377 -0
  133. package/dist/core/workflow-model/parse-interpolation-field.js.map +1 -0
  134. package/dist/core/workflow-model/stable-id.d.ts +2 -0
  135. package/dist/core/workflow-model/stable-id.d.ts.map +1 -0
  136. package/dist/core/workflow-model/stable-id.js +8 -0
  137. package/dist/core/workflow-model/stable-id.js.map +1 -0
  138. package/dist/core/workflow-model/validate-job-references.d.ts +10 -0
  139. package/dist/core/workflow-model/validate-job-references.d.ts.map +1 -0
  140. package/dist/core/workflow-model/validate-job-references.js +42 -0
  141. package/dist/core/workflow-model/validate-job-references.js.map +1 -0
  142. package/dist/core/workflow-model/validate-predicate-expression.d.ts +14 -0
  143. package/dist/core/workflow-model/validate-predicate-expression.d.ts.map +1 -0
  144. package/dist/core/workflow-model/validate-predicate-expression.js +273 -0
  145. package/dist/core/workflow-model/validate-predicate-expression.js.map +1 -0
  146. package/dist/core/workflow-model/validation-issue.d.ts +8 -0
  147. package/dist/core/workflow-model/validation-issue.d.ts.map +1 -0
  148. package/dist/core/workflow-model/validation-issue.js +17 -0
  149. package/dist/core/workflow-model/validation-issue.js.map +1 -0
  150. package/dist/core/workflow-model/workflow-field-label.d.ts +3 -0
  151. package/dist/core/workflow-model/workflow-field-label.d.ts.map +1 -0
  152. package/dist/core/workflow-model/workflow-field-label.js +47 -0
  153. package/dist/core/workflow-model/workflow-field-label.js.map +1 -0
  154. package/dist/core/workflow-yaml/index.d.ts +3 -0
  155. package/dist/core/workflow-yaml/index.d.ts.map +1 -0
  156. package/dist/core/workflow-yaml/index.js +4 -0
  157. package/dist/core/workflow-yaml/index.js.map +1 -0
  158. package/dist/core/workflow-yaml/invalid-workflow-yaml-error.d.ts +16 -0
  159. package/dist/core/workflow-yaml/invalid-workflow-yaml-error.d.ts.map +1 -0
  160. package/dist/core/workflow-yaml/invalid-workflow-yaml-error.js +12 -0
  161. package/dist/core/workflow-yaml/invalid-workflow-yaml-error.js.map +1 -0
  162. package/dist/core/workflow-yaml/parse-workflow-yaml.d.ts +9 -0
  163. package/dist/core/workflow-yaml/parse-workflow-yaml.d.ts.map +1 -0
  164. package/dist/core/workflow-yaml/parse-workflow-yaml.js +52 -0
  165. package/dist/core/workflow-yaml/parse-workflow-yaml.js.map +1 -0
  166. package/dist/core/workflow-yaml/source-locations.d.ts +3 -0
  167. package/dist/core/workflow-yaml/source-locations.d.ts.map +1 -0
  168. package/dist/core/workflow-yaml/source-locations.js +48 -0
  169. package/dist/core/workflow-yaml/source-locations.js.map +1 -0
  170. package/dist/db/db.d.ts +1277 -0
  171. package/dist/db/db.d.ts.map +1 -0
  172. package/dist/db/db.js +22 -0
  173. package/dist/db/db.js.map +1 -0
  174. package/dist/db/definition-triggers.d.ts +4 -0
  175. package/dist/db/definition-triggers.d.ts.map +1 -0
  176. package/dist/db/definition-triggers.js +17 -0
  177. package/dist/db/definition-triggers.js.map +1 -0
  178. package/dist/db/definitions.d.ts +63 -0
  179. package/dist/db/definitions.d.ts.map +1 -0
  180. package/dist/db/definitions.js +208 -0
  181. package/dist/db/definitions.js.map +1 -0
  182. package/dist/db/index.d.ts +8 -0
  183. package/dist/db/index.d.ts.map +1 -0
  184. package/dist/db/index.js +10 -0
  185. package/dist/db/index.js.map +1 -0
  186. package/dist/db/schema/common.d.ts +2 -0
  187. package/dist/db/schema/common.d.ts.map +1 -0
  188. package/dist/db/schema/common.js +4 -0
  189. package/dist/db/schema/common.js.map +1 -0
  190. package/dist/db/schema/definitions.d.ts +237 -0
  191. package/dist/db/schema/definitions.d.ts.map +1 -0
  192. package/dist/db/schema/definitions.js +63 -0
  193. package/dist/db/schema/definitions.js.map +1 -0
  194. package/dist/db/schema/outbox.d.ts +195 -0
  195. package/dist/db/schema/outbox.d.ts.map +1 -0
  196. package/dist/db/schema/outbox.js +5 -0
  197. package/dist/db/schema/outbox.js.map +1 -0
  198. package/dist/db/schema/sync-states.d.ts +218 -0
  199. package/dist/db/schema/sync-states.d.ts.map +1 -0
  200. package/dist/db/schema/sync-states.js +68 -0
  201. package/dist/db/schema/sync-states.js.map +1 -0
  202. package/dist/db/sync-states.d.ts +21 -0
  203. package/dist/db/sync-states.d.ts.map +1 -0
  204. package/dist/db/sync-states.js +47 -0
  205. package/dist/db/sync-states.js.map +1 -0
  206. package/dist/index.d.ts +14 -0
  207. package/dist/index.d.ts.map +1 -0
  208. package/dist/index.js +63 -0
  209. package/dist/index.js.map +1 -0
  210. package/dist/presentation/dto/definition.d.ts +6 -0
  211. package/dist/presentation/dto/definition.d.ts.map +1 -0
  212. package/dist/presentation/dto/definition.js +35 -0
  213. package/dist/presentation/dto/definition.js.map +1 -0
  214. package/dist/presentation/dto/index.d.ts +2 -0
  215. package/dist/presentation/dto/index.d.ts.map +1 -0
  216. package/dist/presentation/dto/index.js +3 -0
  217. package/dist/presentation/dto/index.js.map +1 -0
  218. package/dist/presentation/index.d.ts +2 -0
  219. package/dist/presentation/index.d.ts.map +1 -0
  220. package/dist/presentation/index.js +3 -0
  221. package/dist/presentation/index.js.map +1 -0
  222. package/dist/presentation/routes/create-definition.d.ts +9 -0
  223. package/dist/presentation/routes/create-definition.d.ts.map +1 -0
  224. package/dist/presentation/routes/create-definition.js +76 -0
  225. package/dist/presentation/routes/create-definition.js.map +1 -0
  226. package/dist/presentation/routes/get-definition.d.ts +2 -0
  227. package/dist/presentation/routes/get-definition.d.ts.map +1 -0
  228. package/dist/presentation/routes/get-definition.js +43 -0
  229. package/dist/presentation/routes/get-definition.js.map +1 -0
  230. package/dist/presentation/routes/index.d.ts +7 -0
  231. package/dist/presentation/routes/index.d.ts.map +1 -0
  232. package/dist/presentation/routes/index.js +22 -0
  233. package/dist/presentation/routes/index.js.map +1 -0
  234. package/dist/presentation/routes/list-definitions.d.ts +2 -0
  235. package/dist/presentation/routes/list-definitions.d.ts.map +1 -0
  236. package/dist/presentation/routes/list-definitions.js +56 -0
  237. package/dist/presentation/routes/list-definitions.js.map +1 -0
  238. package/dist/presentation/routes/validate-definition.d.ts +2 -0
  239. package/dist/presentation/routes/validate-definition.d.ts.map +1 -0
  240. package/dist/presentation/routes/validate-definition.js +43 -0
  241. package/dist/presentation/routes/validate-definition.js.map +1 -0
  242. package/dist/presentation/subscribers/index.d.ts +3 -0
  243. package/dist/presentation/subscribers/index.d.ts.map +1 -0
  244. package/dist/presentation/subscribers/index.js +4 -0
  245. package/dist/presentation/subscribers/index.js.map +1 -0
  246. package/dist/presentation/subscribers/on-project-source-bound.d.ts +3 -0
  247. package/dist/presentation/subscribers/on-project-source-bound.d.ts.map +1 -0
  248. package/dist/presentation/subscribers/on-project-source-bound.js +11 -0
  249. package/dist/presentation/subscribers/on-project-source-bound.js.map +1 -0
  250. package/dist/presentation/subscribers/on-project-source-commit-observed.d.ts +3 -0
  251. package/dist/presentation/subscribers/on-project-source-commit-observed.d.ts.map +1 -0
  252. package/dist/presentation/subscribers/on-project-source-commit-observed.js +13 -0
  253. package/dist/presentation/subscribers/on-project-source-commit-observed.js.map +1 -0
  254. package/dist/presentation/subscribers/start-definition-sync.d.ts +10 -0
  255. package/dist/presentation/subscribers/start-definition-sync.d.ts.map +1 -0
  256. package/dist/presentation/subscribers/start-definition-sync.js +26 -0
  257. package/dist/presentation/subscribers/start-definition-sync.js.map +1 -0
  258. package/dist/temporal/activities/index.d.ts +3 -0
  259. package/dist/temporal/activities/index.d.ts.map +1 -0
  260. package/dist/temporal/activities/index.js +3 -0
  261. package/dist/temporal/activities/index.js.map +1 -0
  262. package/dist/temporal/activities/sync-activities.d.ts +47 -0
  263. package/dist/temporal/activities/sync-activities.d.ts.map +1 -0
  264. package/dist/temporal/activities/sync-activities.js +128 -0
  265. package/dist/temporal/activities/sync-activities.js.map +1 -0
  266. package/dist/temporal/constants.d.ts +3 -0
  267. package/dist/temporal/constants.d.ts.map +1 -0
  268. package/dist/temporal/constants.js +4 -0
  269. package/dist/temporal/constants.js.map +1 -0
  270. package/dist/temporal/index.d.ts +3 -0
  271. package/dist/temporal/index.d.ts.map +1 -0
  272. package/dist/temporal/index.js +4 -0
  273. package/dist/temporal/index.js.map +1 -0
  274. package/dist/temporal/workflows/definition-sync.d.ts +20 -0
  275. package/dist/temporal/workflows/definition-sync.d.ts.map +1 -0
  276. package/dist/temporal/workflows/definition-sync.js +95 -0
  277. package/dist/temporal/workflows/definition-sync.js.map +1 -0
  278. package/dist/temporal/workflows/index.d.ts +2 -0
  279. package/dist/temporal/workflows/index.d.ts.map +1 -0
  280. package/dist/temporal/workflows/index.js +3 -0
  281. package/dist/temporal/workflows/index.js.map +1 -0
  282. package/dist/tsconfig.test.tsbuildinfo +1 -0
  283. package/drizzle/0000_initial.sql +64 -0
  284. package/drizzle/0001_definition_list_pagination.sql +1 -0
  285. package/drizzle/meta/0000_snapshot.json +527 -0
  286. package/drizzle/meta/_journal.json +20 -0
  287. package/drizzle.config.ts +11 -0
  288. package/package.json +89 -0
  289. package/src/config.test.ts +23 -0
  290. package/src/config.ts +50 -0
  291. package/src/core/entities/index.ts +4 -0
  292. package/src/core/entities/integration-context.ts +21 -0
  293. package/src/core/entities/sync-state.ts +40 -0
  294. package/src/core/entities/workflow-definition.ts +38 -0
  295. package/src/core/entities/workflow-model.ts +162 -0
  296. package/src/core/errors.ts +21 -0
  297. package/src/core/has-agent-step-integrations.ts +7 -0
  298. package/src/core/index.ts +23 -0
  299. package/src/core/parse-definition.test.ts +200 -0
  300. package/src/core/parse-definition.ts +28 -0
  301. package/src/core/sync-definitions.test.ts +390 -0
  302. package/src/core/sync-definitions.ts +216 -0
  303. package/src/core/validate-definition.test.ts +152 -0
  304. package/src/core/validate-definition.ts +72 -0
  305. package/src/core/workflow-model/README.md +129 -0
  306. package/src/core/workflow-model/constants.ts +1 -0
  307. package/src/core/workflow-model/cron-trigger.test.ts +29 -0
  308. package/src/core/workflow-model/cron-trigger.ts +83 -0
  309. package/src/core/workflow-model/index.ts +9 -0
  310. package/src/core/workflow-model/invalid-workflow-model-error.ts +71 -0
  311. package/src/core/workflow-model/map-job-ids.ts +33 -0
  312. package/src/core/workflow-model/normalize-agent-integrations.ts +208 -0
  313. package/src/core/workflow-model/normalize-dependencies.ts +139 -0
  314. package/src/core/workflow-model/normalize-env.ts +48 -0
  315. package/src/core/workflow-model/normalize-if-condition.ts +126 -0
  316. package/src/core/workflow-model/normalize-job-checkout.ts +19 -0
  317. package/src/core/workflow-model/normalize-job-listening.ts +121 -0
  318. package/src/core/workflow-model/normalize-job-success.ts +28 -0
  319. package/src/core/workflow-model/normalize-jobs.ts +1010 -0
  320. package/src/core/workflow-model/normalize-needs.ts +12 -0
  321. package/src/core/workflow-model/normalize-step-gate.ts +90 -0
  322. package/src/core/workflow-model/normalize-step-outputs.ts +36 -0
  323. package/src/core/workflow-model/normalize-triggers.ts +130 -0
  324. package/src/core/workflow-model/normalize-workflow-document.test.ts +4463 -0
  325. package/src/core/workflow-model/normalize-workflow-document.ts +63 -0
  326. package/src/core/workflow-model/parse-duration-ms.ts +58 -0
  327. package/src/core/workflow-model/parse-interpolation-field.ts +566 -0
  328. package/src/core/workflow-model/stable-id.ts +12 -0
  329. package/src/core/workflow-model/validate-job-references.ts +54 -0
  330. package/src/core/workflow-model/validate-predicate-expression.test.ts +161 -0
  331. package/src/core/workflow-model/validate-predicate-expression.ts +406 -0
  332. package/src/core/workflow-model/validation-issue.ts +27 -0
  333. package/src/core/workflow-model/workflow-field-label.ts +50 -0
  334. package/src/core/workflow-yaml/README.md +77 -0
  335. package/src/core/workflow-yaml/index.ts +11 -0
  336. package/src/core/workflow-yaml/invalid-workflow-yaml-error.ts +23 -0
  337. package/src/core/workflow-yaml/parse-workflow-yaml.test.ts +71 -0
  338. package/src/core/workflow-yaml/parse-workflow-yaml.ts +74 -0
  339. package/src/core/workflow-yaml/source-locations.ts +62 -0
  340. package/src/db/db.ts +18 -0
  341. package/src/db/definition-triggers.test.ts +95 -0
  342. package/src/db/definition-triggers.ts +17 -0
  343. package/src/db/definitions.test.ts +1309 -0
  344. package/src/db/definitions.ts +357 -0
  345. package/src/db/index.ts +31 -0
  346. package/src/db/schema/common.ts +3 -0
  347. package/src/db/schema/definitions.test.ts +36 -0
  348. package/src/db/schema/definitions.ts +76 -0
  349. package/src/db/schema/outbox.ts +4 -0
  350. package/src/db/schema/sync-states.ts +79 -0
  351. package/src/db/sync-states.test.ts +90 -0
  352. package/src/db/sync-states.ts +86 -0
  353. package/src/index.ts +104 -0
  354. package/src/presentation/dto/definition.test.ts +59 -0
  355. package/src/presentation/dto/definition.ts +41 -0
  356. package/src/presentation/dto/index.ts +1 -0
  357. package/src/presentation/index.ts +1 -0
  358. package/src/presentation/routes/create-definition.test.ts +356 -0
  359. package/src/presentation/routes/create-definition.ts +101 -0
  360. package/src/presentation/routes/get-definition.test.ts +79 -0
  361. package/src/presentation/routes/get-definition.ts +37 -0
  362. package/src/presentation/routes/index.test.ts +41 -0
  363. package/src/presentation/routes/index.ts +28 -0
  364. package/src/presentation/routes/list-definitions.test.ts +195 -0
  365. package/src/presentation/routes/list-definitions.ts +49 -0
  366. package/src/presentation/routes/validate-definition.test.ts +63 -0
  367. package/src/presentation/routes/validate-definition.ts +46 -0
  368. package/src/presentation/subscribers/index.ts +2 -0
  369. package/src/presentation/subscribers/on-project-source-bound.test.ts +65 -0
  370. package/src/presentation/subscribers/on-project-source-bound.ts +11 -0
  371. package/src/presentation/subscribers/on-project-source-commit-observed.test.ts +56 -0
  372. package/src/presentation/subscribers/on-project-source-commit-observed.ts +15 -0
  373. package/src/presentation/subscribers/start-definition-sync.ts +38 -0
  374. package/src/temporal/activities/index.ts +10 -0
  375. package/src/temporal/activities/sync-activities.test.ts +337 -0
  376. package/src/temporal/activities/sync-activities.ts +206 -0
  377. package/src/temporal/constants.ts +2 -0
  378. package/src/temporal/index.ts +2 -0
  379. package/src/temporal/workflows/definition-sync.test.ts +59 -0
  380. package/src/temporal/workflows/definition-sync.ts +116 -0
  381. package/src/temporal/workflows/index.ts +1 -0
  382. package/test/env.ts +7 -0
  383. package/test/factories/definition.ts +66 -0
  384. package/test/fixtures/invalid-cycle.yml +13 -0
  385. package/test/fixtures/invalid-missing-name.yml +4 -0
  386. package/test/fixtures/invalid-yaml-syntax.yml +6 -0
  387. package/test/fixtures/valid-dag.yml +25 -0
  388. package/test/fixtures/valid-listening-job.yml +21 -0
  389. package/test/fixtures/valid-simple.yml +18 -0
  390. package/test/fixtures/workflow-yaml/invalid/invalid-yaml-syntax.yaml +6 -0
  391. package/test/fixtures/workflow-yaml/invalid/missing-step-run.yaml +5 -0
  392. package/test/fixtures/workflow-yaml/valid/simple-build.document.json +26 -0
  393. package/test/fixtures/workflow-yaml/valid/simple-build.yaml +14 -0
  394. package/test/globalSetup.ts +25 -0
  395. package/test/index.ts +1 -0
  396. package/test/setup.ts +17 -0
  397. package/tsconfig.build.json +9 -0
  398. package/tsconfig.build.tsbuildinfo +1 -0
  399. package/tsconfig.json +3 -0
  400. package/tsconfig.test.json +8 -0
  401. package/vitest.config.ts +11 -0
@@ -0,0 +1,63 @@
1
+ import {
2
+ DEFAULT_HARNESS_TOOL_DEPLOYMENT_CONFIG,
3
+ type HarnessToolDeploymentConfig,
4
+ } from '@shipfox/api-agent-dto';
5
+ import {canonicalizeLabels} from '@shipfox/runner-labels';
6
+ import type {WorkflowDocument} from '@shipfox/workflow-document';
7
+ import type {IntegrationValidationContext} from '../entities/integration-context.js';
8
+ import type {WorkflowModel, WorkflowStepSourceLocationMap} from '../entities/workflow-model.js';
9
+ import {
10
+ InvalidWorkflowModelError,
11
+ type WorkflowModelValidationIssue,
12
+ } from './invalid-workflow-model-error.js';
13
+ import {mapJobIds} from './map-job-ids.js';
14
+ import {normalizeDependencies, validateCycles} from './normalize-dependencies.js';
15
+ import {normalizeEnv} from './normalize-env.js';
16
+ import {normalizeJobs} from './normalize-jobs.js';
17
+ import {normalizeTriggers} from './normalize-triggers.js';
18
+
19
+ export function normalizeWorkflowDocument(
20
+ document: WorkflowDocument,
21
+ options: {
22
+ defaultRunnerLabels?: readonly string[] | undefined;
23
+ harnessToolDeploymentConfig?: HarnessToolDeploymentConfig | undefined;
24
+ integrationValidationContext?: IntegrationValidationContext | undefined;
25
+ stepSourceLocations?: WorkflowStepSourceLocationMap | undefined;
26
+ } = {},
27
+ ): WorkflowModel {
28
+ const issues: WorkflowModelValidationIssue[] = [];
29
+ const defaultRunnerLabels = canonicalizeLabels(options.defaultRunnerLabels);
30
+ const harnessToolDeploymentConfig =
31
+ options.harnessToolDeploymentConfig ?? DEFAULT_HARNESS_TOOL_DEPLOYMENT_CONFIG;
32
+ const context = {
33
+ defaultRunnerLabels,
34
+ harnessToolDeploymentConfig,
35
+ integrationValidationContext: options.integrationValidationContext,
36
+ };
37
+ const jobIdBySourceName = mapJobIds(document, issues);
38
+ const triggers = normalizeTriggers(document, issues);
39
+ const jobs = normalizeJobs(
40
+ document,
41
+ jobIdBySourceName,
42
+ issues,
43
+ options.stepSourceLocations,
44
+ context,
45
+ );
46
+ const dependencies = normalizeDependencies(document.jobs, jobIdBySourceName, issues);
47
+ const workflowEnv = normalizeEnv({env: document.env, path: ['env'], issues});
48
+
49
+ validateCycles(document.jobs, jobIdBySourceName, issues);
50
+
51
+ if (issues.length > 0) {
52
+ throw new InvalidWorkflowModelError(issues);
53
+ }
54
+
55
+ return {
56
+ kind: 'workflow',
57
+ name: document.name,
58
+ ...workflowEnv,
59
+ triggers,
60
+ jobs,
61
+ dependencies,
62
+ };
63
+ }
@@ -0,0 +1,58 @@
1
+ import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
2
+ import {issue} from './validation-issue.js';
3
+
4
+ const UNIT_MS = {
5
+ ms: 1,
6
+ s: 1000,
7
+ m: 60 * 1000,
8
+ h: 60 * 60 * 1000,
9
+ d: 24 * 60 * 60 * 1000,
10
+ } as const;
11
+
12
+ const MIN_DURATION_MS = 1000;
13
+ const MAX_DURATION_MS = 24 * 60 * 60 * 1000;
14
+ const DURATION_PATTERN = /^(\d+)(ms|s|m|h|d)$/;
15
+
16
+ export function parseDurationMs(params: {
17
+ source: string | undefined;
18
+ path: Array<string | number>;
19
+ issues: WorkflowModelValidationIssue[];
20
+ minMs?: number | undefined;
21
+ maxMs?: number | undefined;
22
+ outOfRangeCode?: 'invalid-duration' | 'listening-timeout-exceeds-run-timeout' | undefined;
23
+ outOfRangeMessage?: string | undefined;
24
+ }): number | undefined {
25
+ if (params.source === undefined) return undefined;
26
+
27
+ const match = DURATION_PATTERN.exec(params.source.trim());
28
+ if (!match) {
29
+ params.issues.push(
30
+ issue({
31
+ code: 'invalid-duration',
32
+ message: 'Duration must be an integer followed by ms, s, m, h, or d.',
33
+ path: params.path,
34
+ details: {source: params.source},
35
+ }),
36
+ );
37
+ return undefined;
38
+ }
39
+
40
+ const value = Number(match[1]);
41
+ const unit = match[2] as keyof typeof UNIT_MS;
42
+ const ms = value * UNIT_MS[unit];
43
+ const minMs = params.minMs ?? MIN_DURATION_MS;
44
+ const maxMs = params.maxMs ?? MAX_DURATION_MS;
45
+ if (!Number.isSafeInteger(ms) || ms < minMs || ms > maxMs) {
46
+ params.issues.push(
47
+ issue({
48
+ code: params.outOfRangeCode ?? 'invalid-duration',
49
+ message: params.outOfRangeMessage ?? 'Duration must be between 1s and 24h.',
50
+ path: params.path,
51
+ details: {source: params.source, min_ms: minMs, max_ms: maxMs},
52
+ }),
53
+ );
54
+ return undefined;
55
+ }
56
+
57
+ return ms;
58
+ }
@@ -0,0 +1,566 @@
1
+ import {secretKeySchema, secretStoreSchema} from '@shipfox/api-secrets-dto';
2
+ import {
3
+ type AvailabilitySite,
4
+ analyzeContextKeyAccess,
5
+ createWorkflowExpression,
6
+ type ExpressionTypeEnvironment,
7
+ extractCelUntrustedPathAccesses,
8
+ getWorkflowContextAvailability,
9
+ getWorkflowContextDefinition,
10
+ getWorkflowContextHost,
11
+ getWorkflowContextTypeEnvironment,
12
+ getWorkflowContextUntrustedPaths,
13
+ InvalidWorkflowExpressionError,
14
+ InvalidWorkflowTemplateError,
15
+ type PlanViolation,
16
+ parseWorkflowTemplate,
17
+ planInterpolationField,
18
+ unavailableRootsAt,
19
+ type WorkflowContextName,
20
+ type WorkflowInterpolationField,
21
+ type WorkflowTemplateExprSegment,
22
+ type WorkflowTemplateSegment,
23
+ workflowContextNames,
24
+ workflowInterpolationFieldAcceptsContext,
25
+ workflowInterpolationFieldAcceptsHost,
26
+ workflowInterpolationFieldAcceptsTrustTier,
27
+ } from '@shipfox/expression';
28
+ import type {WorkflowFieldTemplate} from '../entities/workflow-model.js';
29
+ import type {
30
+ WorkflowModelValidationIssue,
31
+ WorkflowModelValidationIssueCode,
32
+ WorkflowModelValidationIssuePathSegment,
33
+ } from './invalid-workflow-model-error.js';
34
+ import {validateDirectJobReferences} from './validate-job-references.js';
35
+
36
+ export type StoredInterpolationField =
37
+ | 'run'
38
+ | 'env.value'
39
+ | 'agent.prompt'
40
+ | 'agent.model'
41
+ | 'agent.provider'
42
+ | 'job.outputs'
43
+ | 'job.name'
44
+ | 'job.runner'
45
+ | 'step.name'
46
+ | 'step.feedback';
47
+
48
+ export function parseInterpolationField(params: {
49
+ field: StoredInterpolationField;
50
+ source: string;
51
+ path: readonly WorkflowModelValidationIssuePathSegment[];
52
+ issues: WorkflowModelValidationIssue[];
53
+ fillSite?: AvailabilitySite;
54
+ allowedJobReferences?: ReadonlySet<string>;
55
+ typeOverlay?: ExpressionTypeEnvironment | undefined;
56
+ }): WorkflowFieldTemplate | undefined {
57
+ const segments = parseTemplate(params);
58
+ if (segments === undefined) return undefined;
59
+
60
+ const expressionSegments = segments.filter(isExpressionSegment);
61
+ if (expressionSegments.length === 0) return undefined;
62
+
63
+ const checkedSegments = segments.map((segment) => {
64
+ if (segment.kind === 'literal') return segment;
65
+
66
+ const validatedSegment = validateExpressionSegment({...params, segment});
67
+ return validatedSegment ?? segment;
68
+ });
69
+
70
+ const plan = planInterpolationField({field: params.field, segments: checkedSegments});
71
+ if (!plan.ok) {
72
+ params.issues.push(
73
+ ...plan.violations.map((violation) => planViolationIssue(params, violation)),
74
+ );
75
+ return undefined;
76
+ }
77
+
78
+ return plan.plan.field.segments;
79
+ }
80
+
81
+ function parseTemplate(params: {
82
+ field: WorkflowInterpolationField;
83
+ source: string;
84
+ path: readonly WorkflowModelValidationIssuePathSegment[];
85
+ issues: WorkflowModelValidationIssue[];
86
+ }): WorkflowTemplateSegment[] | undefined {
87
+ try {
88
+ return parseWorkflowTemplate(params.source);
89
+ } catch (error) {
90
+ params.issues.push(
91
+ issue({
92
+ code: 'invalid-interpolation-template',
93
+ message: `${fieldLabel(params.field)} must use valid $${'{{ }}'} interpolation syntax.`,
94
+ path: params.path,
95
+ details: {
96
+ field: params.field,
97
+ source: params.source,
98
+ reason:
99
+ error instanceof InvalidWorkflowTemplateError
100
+ ? error.reason
101
+ : 'Template source did not parse.',
102
+ },
103
+ }),
104
+ );
105
+ return undefined;
106
+ }
107
+ }
108
+
109
+ function validateExpressionSegment(params: {
110
+ field: StoredInterpolationField;
111
+ source: string;
112
+ path: readonly WorkflowModelValidationIssuePathSegment[];
113
+ issues: WorkflowModelValidationIssue[];
114
+ segment: WorkflowTemplateExprSegment;
115
+ fillSite?: AvailabilitySite;
116
+ allowedJobReferences?: ReadonlySet<string>;
117
+ typeOverlay?: ExpressionTypeEnvironment | undefined;
118
+ }): WorkflowTemplateExprSegment | undefined {
119
+ const contextRoots = uniqueStrings(params.segment.contextRoots);
120
+ const knownRoots = contextRoots.filter(isWorkflowContextName);
121
+ const unknownRoots = contextRoots.filter((root) => !isWorkflowContextName(root));
122
+
123
+ const rejectedRoots = knownRoots.filter(
124
+ (root) => !workflowInterpolationFieldAcceptsContext(params.field, root),
125
+ );
126
+ if (rejectedRoots.length > 0) {
127
+ params.issues.push(untrustedContextIssue({...params, contextRoots, rejectedRoots}));
128
+ return undefined;
129
+ }
130
+
131
+ const rejectedHostRoots = knownRoots.filter(
132
+ (root) => !workflowInterpolationFieldAcceptsHost(params.field, getWorkflowContextHost(root)),
133
+ );
134
+ if (rejectedHostRoots.length > 0) {
135
+ params.issues.push(runnerContextInFieldIssue({...params, contextRoots, rejectedHostRoots}));
136
+ return undefined;
137
+ }
138
+
139
+ const keyAccess = analyzeContextKeyAccess(params.segment.expression);
140
+ if (keyAccess.violations.length > 0) {
141
+ params.issues.push(
142
+ ...keyAccess.violations.map((violation) =>
143
+ computedContextKeyIssue({
144
+ ...params,
145
+ contextRoots,
146
+ root: violation.root,
147
+ expression: violation.source,
148
+ }),
149
+ ),
150
+ );
151
+ return undefined;
152
+ }
153
+
154
+ const invalidReferenceIssue = validateContextKeyReferences({
155
+ ...params,
156
+ contextRoots,
157
+ references: keyAccess.references,
158
+ });
159
+ if (invalidReferenceIssue !== undefined) {
160
+ params.issues.push(invalidReferenceIssue);
161
+ return undefined;
162
+ }
163
+
164
+ const invalidJobReferenceIssue =
165
+ params.allowedJobReferences === undefined
166
+ ? undefined
167
+ : validateDirectJobReferences({
168
+ source: params.source,
169
+ expression: params.segment.expression,
170
+ field: params.field,
171
+ path: params.path,
172
+ allowedJobReferences: params.allowedJobReferences,
173
+ });
174
+ if (invalidJobReferenceIssue !== undefined) {
175
+ params.issues.push(invalidJobReferenceIssue);
176
+ return undefined;
177
+ }
178
+
179
+ const rejectedPathRoots = findUntrustedPathRoots(params.segment, params.field, knownRoots);
180
+ if (rejectedPathRoots.length > 0) {
181
+ params.issues.push(
182
+ untrustedContextIssue({...params, contextRoots, rejectedRoots: rejectedPathRoots}),
183
+ );
184
+ return undefined;
185
+ }
186
+
187
+ if (unknownRoots.length > 0) {
188
+ params.issues.push(
189
+ issue({
190
+ code: 'unknown-interpolation-context',
191
+ message: `${fieldLabel(params.field)} interpolation references unknown context ${formatList(
192
+ unknownRoots,
193
+ )}.`,
194
+ path: params.path,
195
+ details: {
196
+ field: params.field,
197
+ source: params.source,
198
+ expression: params.segment.expression.source,
199
+ contextRoots,
200
+ unknownRoots,
201
+ },
202
+ }),
203
+ );
204
+ return undefined;
205
+ }
206
+
207
+ const fillSite = params.fillSite;
208
+ if (fillSite !== undefined) {
209
+ const serverRoots = knownRoots.filter((root) => getWorkflowContextHost(root) === 'server');
210
+ const unavailableRoots = unavailableRootsAt(serverRoots, fillSite);
211
+ if (unavailableRoots.length > 0) {
212
+ params.issues.push(
213
+ unavailableContextIssue({...params, contextRoots, unavailableRoots, fillSite}),
214
+ );
215
+ return undefined;
216
+ }
217
+ }
218
+
219
+ if (
220
+ params.typeOverlay === undefined &&
221
+ (knownRoots.length === 0 || knownRoots.some((root) => hasSyntaxOnlyCheckMode(root)))
222
+ ) {
223
+ return params.segment;
224
+ }
225
+
226
+ try {
227
+ return {
228
+ ...params.segment,
229
+ expression: createWorkflowExpression({
230
+ source: params.segment.expression.source,
231
+ check: {
232
+ mode: 'typed',
233
+ // `undefined` preserves the legacy syntax-only path above. `{}` means
234
+ // callers intentionally requested typed checking with the standard roots.
235
+ typeEnvironment: mergeTypeEnvironments(knownRoots, params.typeOverlay),
236
+ },
237
+ }),
238
+ };
239
+ } catch (error) {
240
+ params.issues.push(
241
+ issue({
242
+ code: 'invalid-interpolation-expression',
243
+ message: `${fieldLabel(params.field)} interpolation expression did not type-check.`,
244
+ path: params.path,
245
+ details: {
246
+ field: params.field,
247
+ source: params.source,
248
+ expression: params.segment.expression.source,
249
+ contextRoots,
250
+ reason:
251
+ error instanceof InvalidWorkflowExpressionError
252
+ ? error.reason
253
+ : 'Expression source did not type-check.',
254
+ },
255
+ }),
256
+ );
257
+ return undefined;
258
+ }
259
+ }
260
+
261
+ function findUntrustedPathRoots(
262
+ segment: WorkflowTemplateExprSegment,
263
+ field: StoredInterpolationField,
264
+ knownRoots: readonly WorkflowContextName[],
265
+ ): readonly WorkflowContextName[] {
266
+ if (!isTrustedOnlyField(field)) return [];
267
+
268
+ const untrustedPathsByRoot = new Map<string, readonly string[]>();
269
+ for (const root of knownRoots) {
270
+ const paths = getWorkflowContextUntrustedPaths(root);
271
+ if (paths === undefined || paths.length === 0) continue;
272
+ untrustedPathsByRoot.set(root, paths);
273
+ }
274
+ if (knownRoots.includes('steps')) {
275
+ untrustedPathsByRoot.set('steps', [
276
+ ...new Set([...(untrustedPathsByRoot.get('steps') ?? []), 'outputs']),
277
+ ]);
278
+ }
279
+ if (untrustedPathsByRoot.size === 0) return [];
280
+
281
+ return extractCelUntrustedPathAccesses({
282
+ source: segment.expression.source,
283
+ untrustedPathsByRoot,
284
+ }).filter(isWorkflowContextName);
285
+ }
286
+
287
+ function isTrustedOnlyField(field: StoredInterpolationField): boolean {
288
+ return !workflowInterpolationFieldAcceptsTrustTier(field, 'untrusted');
289
+ }
290
+
291
+ function untrustedContextIssue(params: {
292
+ field: WorkflowInterpolationField;
293
+ source: string;
294
+ path: readonly WorkflowModelValidationIssuePathSegment[];
295
+ contextRoots: readonly string[];
296
+ rejectedRoots: readonly WorkflowContextName[];
297
+ }): WorkflowModelValidationIssue {
298
+ return issue({
299
+ code: 'untrusted-context-in-field',
300
+ message:
301
+ params.field === 'run'
302
+ ? `Run command interpolation cannot use untrusted context ${formatList(
303
+ params.rejectedRoots,
304
+ )}. Bind untrusted values to env and reference the shell variable from run instead.`
305
+ : `${fieldLabel(params.field)} interpolation cannot use untrusted context ${formatList(
306
+ params.rejectedRoots,
307
+ )}.`,
308
+ path: params.path,
309
+ details: {
310
+ field: params.field,
311
+ source: params.source,
312
+ contextRoots: params.contextRoots,
313
+ rejectedRoots: params.rejectedRoots,
314
+ },
315
+ });
316
+ }
317
+
318
+ function runnerContextInFieldIssue(params: {
319
+ field: WorkflowInterpolationField;
320
+ source: string;
321
+ path: readonly WorkflowModelValidationIssuePathSegment[];
322
+ contextRoots: readonly string[];
323
+ rejectedHostRoots: readonly WorkflowContextName[];
324
+ }): WorkflowModelValidationIssue {
325
+ return issue({
326
+ code: 'runner-context-in-field',
327
+ message: `${fieldLabel(params.field)} interpolation cannot use runner context ${formatList(
328
+ params.rejectedHostRoots,
329
+ )}. Bind secrets to a run-step env value instead.`,
330
+ path: params.path,
331
+ details: {
332
+ field: params.field,
333
+ source: params.source,
334
+ contextRoots: params.contextRoots,
335
+ rejectedRoots: params.rejectedHostRoots,
336
+ },
337
+ });
338
+ }
339
+
340
+ function computedContextKeyIssue(params: {
341
+ field: WorkflowInterpolationField;
342
+ source: string;
343
+ path: readonly WorkflowModelValidationIssuePathSegment[];
344
+ contextRoots: readonly string[];
345
+ root: string;
346
+ expression: string;
347
+ }): WorkflowModelValidationIssue {
348
+ return issue({
349
+ code: 'computed-context-key',
350
+ message: `${fieldLabel(params.field)} interpolation must reference ${params.root} with a literal dot key.`,
351
+ path: params.path,
352
+ details: {
353
+ field: params.field,
354
+ source: params.source,
355
+ expression: params.expression,
356
+ contextRoots: params.contextRoots,
357
+ root: params.root,
358
+ },
359
+ });
360
+ }
361
+
362
+ function validateContextKeyReferences(params: {
363
+ field: WorkflowInterpolationField;
364
+ source: string;
365
+ path: readonly WorkflowModelValidationIssuePathSegment[];
366
+ contextRoots: readonly string[];
367
+ segment: WorkflowTemplateExprSegment;
368
+ references: ReturnType<typeof analyzeContextKeyAccess>['references'];
369
+ }): WorkflowModelValidationIssue | undefined {
370
+ for (const reference of params.references) {
371
+ if (!secretKeySchema.safeParse(reference.key).success) {
372
+ return computedContextKeyIssue({
373
+ ...params,
374
+ root: reference.root,
375
+ expression: params.segment.expression.source,
376
+ });
377
+ }
378
+
379
+ if (reference.root !== 'secrets' || reference.store === undefined) continue;
380
+ if (secretStoreSchema.safeParse(reference.store).success) continue;
381
+
382
+ return issue({
383
+ code: 'unknown-secret-store',
384
+ message: `${fieldLabel(params.field)} interpolation references unknown secret store "${reference.store}".`,
385
+ path: params.path,
386
+ details: {
387
+ field: params.field,
388
+ source: params.source,
389
+ expression: params.segment.expression.source,
390
+ contextRoots: params.contextRoots,
391
+ store: reference.store,
392
+ },
393
+ });
394
+ }
395
+
396
+ return undefined;
397
+ }
398
+
399
+ const availabilitySiteLabels = {
400
+ ingest: 'ingest',
401
+ 'run-creation': 'run creation',
402
+ 'execution-creation': 'execution creation',
403
+ 'job-activation': 'job activation',
404
+ 'step-dispatch': 'step dispatch',
405
+ 'step-report': 'step reporting',
406
+ 'execution-resolution': 'execution resolution',
407
+ 'job-resolution': 'job resolution',
408
+ } as const satisfies Record<AvailabilitySite, string>;
409
+
410
+ function unavailableContextIssue(params: {
411
+ field: WorkflowInterpolationField;
412
+ source: string;
413
+ path: readonly WorkflowModelValidationIssuePathSegment[];
414
+ segment: WorkflowTemplateExprSegment;
415
+ contextRoots: readonly string[];
416
+ unavailableRoots: readonly WorkflowContextName[];
417
+ fillSite: AvailabilitySite;
418
+ }): WorkflowModelValidationIssue {
419
+ return issue({
420
+ code: 'context-unavailable-at-fill-site',
421
+ message: `${fieldLabel(params.field)} interpolation references ${contextNoun(
422
+ params.unavailableRoots,
423
+ )} ${formatList(params.unavailableRoots)} that ${availabilityVerb(
424
+ params.unavailableRoots,
425
+ )} not available at ${describeAvailabilitySite(params.fillSite)}. ${params.unavailableRoots
426
+ .map(unavailableRootAvailabilityMessage)
427
+ .join(' ')}`,
428
+ path: params.path,
429
+ details: {
430
+ field: params.field,
431
+ source: params.source,
432
+ expression: params.segment.expression.source,
433
+ contextRoots: params.contextRoots,
434
+ unavailableRoots: params.unavailableRoots,
435
+ fillSite: params.fillSite,
436
+ },
437
+ });
438
+ }
439
+
440
+ function planViolationIssue(
441
+ params: {
442
+ field: WorkflowInterpolationField;
443
+ source: string;
444
+ path: readonly WorkflowModelValidationIssuePathSegment[];
445
+ },
446
+ violation: PlanViolation,
447
+ ): WorkflowModelValidationIssue {
448
+ if (violation.reason === 'computed-context-key') {
449
+ return issue({
450
+ code: 'computed-context-key',
451
+ message: `${fieldLabel(params.field)} interpolation must reference ${formatList(
452
+ violation.contextRoots ?? [],
453
+ )} with a literal dot key.`,
454
+ path: params.path,
455
+ details: {
456
+ field: params.field,
457
+ source: params.source,
458
+ expression: violation.source,
459
+ contextRoots: violation.contextRoots,
460
+ },
461
+ });
462
+ }
463
+
464
+ return issue({
465
+ code: 'runner-context-not-bare',
466
+ message: `${fieldLabel(params.field)} interpolation references runner context in a larger expression; ${violation.hint}.`,
467
+ path: params.path,
468
+ details: {
469
+ field: params.field,
470
+ source: params.source,
471
+ expression: violation.source,
472
+ runnerRoots: violation.runnerRoots ?? [],
473
+ },
474
+ });
475
+ }
476
+
477
+ function contextNoun(roots: readonly WorkflowContextName[]): 'context' | 'contexts' {
478
+ return roots.length === 1 ? 'context' : 'contexts';
479
+ }
480
+
481
+ function availabilityVerb(roots: readonly WorkflowContextName[]): 'is' | 'are' {
482
+ return roots.length === 1 ? 'is' : 'are';
483
+ }
484
+
485
+ function unavailableRootAvailabilityMessage(root: WorkflowContextName): string {
486
+ const availability = getWorkflowContextAvailability(root);
487
+ if (availability === undefined) return `"${root}" is not available at any server site.`;
488
+ return `"${root}" becomes available at ${describeAvailabilitySite(availability)}.`;
489
+ }
490
+
491
+ function describeAvailabilitySite(site: AvailabilitySite): string {
492
+ return availabilitySiteLabels[site];
493
+ }
494
+
495
+ function hasSyntaxOnlyCheckMode(root: WorkflowContextName): boolean {
496
+ return getWorkflowContextDefinition(root).checkMode === 'syntax';
497
+ }
498
+
499
+ function mergeTypeEnvironments(
500
+ roots: readonly WorkflowContextName[],
501
+ typeOverlay?: ExpressionTypeEnvironment,
502
+ ): ExpressionTypeEnvironment {
503
+ const typeEnvironment: Record<string, ExpressionTypeEnvironment[string]> = {};
504
+
505
+ for (const root of roots) {
506
+ const overlayType = typeOverlay?.[root];
507
+ if (overlayType !== undefined) {
508
+ typeEnvironment[root] = overlayType;
509
+ continue;
510
+ }
511
+
512
+ const contextTypeEnvironment = getWorkflowContextTypeEnvironment(root);
513
+ if (contextTypeEnvironment === undefined) {
514
+ if (typeOverlay !== undefined) typeEnvironment[root] = {kind: 'map'};
515
+ continue;
516
+ }
517
+
518
+ Object.assign(typeEnvironment, contextTypeEnvironment);
519
+ }
520
+
521
+ return typeEnvironment;
522
+ }
523
+
524
+ function isExpressionSegment(
525
+ segment: WorkflowTemplateSegment,
526
+ ): segment is WorkflowTemplateExprSegment {
527
+ return segment.kind === 'expr';
528
+ }
529
+
530
+ function isWorkflowContextName(root: string): root is WorkflowContextName {
531
+ return (workflowContextNames as readonly string[]).includes(root);
532
+ }
533
+
534
+ function uniqueStrings(values: readonly string[]): readonly string[] {
535
+ return [...new Set(values)];
536
+ }
537
+
538
+ function formatList(values: readonly string[]): string {
539
+ return values.map((value) => `"${value}"`).join(', ');
540
+ }
541
+
542
+ function fieldLabel(field: WorkflowInterpolationField): string {
543
+ return `Workflow ${field}`;
544
+ }
545
+
546
+ function issue(params: {
547
+ code: WorkflowModelValidationIssueCode;
548
+ message: string;
549
+ path: readonly WorkflowModelValidationIssuePathSegment[];
550
+ details?: Readonly<Record<string, unknown>>;
551
+ }): WorkflowModelValidationIssue {
552
+ if (params.details === undefined) {
553
+ return {
554
+ code: params.code,
555
+ message: params.message,
556
+ path: params.path,
557
+ };
558
+ }
559
+
560
+ return {
561
+ code: params.code,
562
+ message: params.message,
563
+ path: params.path,
564
+ details: params.details,
565
+ };
566
+ }
@@ -0,0 +1,12 @@
1
+ const nonStableIdPattern = /[^a-z0-9]+/g;
2
+ const edgeDashPattern = /^-+|-+$/g;
3
+
4
+ export function stableId(sourceName: string): string {
5
+ const id = sourceName
6
+ .trim()
7
+ .toLowerCase()
8
+ .replace(nonStableIdPattern, '-')
9
+ .replace(edgeDashPattern, '');
10
+
11
+ return id.length === 0 ? 'unnamed' : id;
12
+ }