@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,152 @@
1
+ import {validateDefinition} from './validate-definition.js';
2
+
3
+ describe('validateDefinition', () => {
4
+ test('valid YAML returns { valid: true, definition }', () => {
5
+ const yaml = `
6
+ name: Test
7
+ runner: ubuntu-latest
8
+ jobs:
9
+ build:
10
+ steps:
11
+ - run: echo hello
12
+ `;
13
+
14
+ const result = validateDefinition(yaml);
15
+
16
+ expect(result.valid).toBe(true);
17
+ if (result.valid) {
18
+ expect(result.definition.document.name).toBe('Test');
19
+ expect(result.definition.document.jobs.build?.steps).toHaveLength(1);
20
+ expect(result.definition.model.jobs[0]?.id).toBe('build');
21
+ }
22
+ });
23
+
24
+ test('invalid YAML syntax returns { valid: false, errors }', () => {
25
+ const result = validateDefinition('name: Bad\n invalid:\nindentation');
26
+
27
+ expect(result.valid).toBe(false);
28
+ if (!result.valid) {
29
+ expect(result.errors).toHaveLength(1);
30
+ expect(result.errors[0]?.message).toContain('Invalid workflow YAML syntax');
31
+ }
32
+ });
33
+
34
+ test('non-object YAML returns { valid: false, errors }', () => {
35
+ const stringResult = validateDefinition('just a string');
36
+ expect(stringResult.valid).toBe(false);
37
+
38
+ const nullResult = validateDefinition('');
39
+ expect(nullResult.valid).toBe(false);
40
+
41
+ const arrayResult = validateDefinition('- item1\n- item2');
42
+ expect(arrayResult.valid).toBe(false);
43
+ });
44
+
45
+ test('invalid document returns { valid: false, errors with details }', () => {
46
+ const yaml = `
47
+ jobs:
48
+ build:
49
+ steps:
50
+ - run: echo hello
51
+ `;
52
+
53
+ const result = validateDefinition(yaml);
54
+
55
+ expect(result.valid).toBe(false);
56
+ if (!result.valid) {
57
+ expect(result.errors.length).toBeGreaterThan(0);
58
+ expect(result.errors[0]?.message).toBeDefined();
59
+ }
60
+ });
61
+
62
+ test('cyclic DAG returns { valid: false, errors with cycle info }', () => {
63
+ const yaml = `
64
+ name: Cyclic
65
+ runner: ubuntu-latest
66
+ jobs:
67
+ a:
68
+ needs: b
69
+ steps:
70
+ - run: echo a
71
+ b:
72
+ needs: a
73
+ steps:
74
+ - run: echo b
75
+ `;
76
+
77
+ const result = validateDefinition(yaml);
78
+
79
+ expect(result.valid).toBe(false);
80
+ if (!result.valid) {
81
+ expect(result.errors[0]?.message).toContain('Circular dependency');
82
+ }
83
+ });
84
+
85
+ test('runner-less YAML returns a validation path for the missing runner', () => {
86
+ const yaml = `
87
+ name: Missing runner
88
+ jobs:
89
+ build:
90
+ steps:
91
+ - run: echo hello
92
+ `;
93
+
94
+ const result = validateDefinition(yaml);
95
+
96
+ expect(result.valid).toBe(false);
97
+ if (!result.valid) {
98
+ expect(result.errors).toContainEqual(expect.objectContaining({path: 'jobs.build.runner'}));
99
+ }
100
+ });
101
+
102
+ test('default runner labels allow runner-less YAML', () => {
103
+ const yaml = `
104
+ name: Default runner
105
+ jobs:
106
+ build:
107
+ steps:
108
+ - run: echo hello
109
+ `;
110
+
111
+ const result = validateDefinition(yaml, {defaultRunnerLabels: ['ubuntu-latest']});
112
+
113
+ expect(result.valid).toBe(true);
114
+ if (result.valid) {
115
+ expect(result.definition.model.jobs[0]?.runner).toEqual(['ubuntu-latest']);
116
+ }
117
+ });
118
+
119
+ test('disabled Pi search tools return precise definition validation paths', () => {
120
+ const yaml = `
121
+ name: Pi tools
122
+ runner: ubuntu-latest
123
+ jobs:
124
+ inspect:
125
+ steps:
126
+ - harness: pi
127
+ prompt: Fetch without search.
128
+ tools: [fetch_content, web_search, get_search_content]
129
+ `;
130
+
131
+ const result = validateDefinition(yaml, {
132
+ harnessToolDeploymentConfig: {
133
+ pi: {enabledToolPackages: ['pi-web-access'], webSearchEnabled: false},
134
+ claude: {enabledToolPackages: []},
135
+ },
136
+ });
137
+
138
+ expect(result.valid).toBe(false);
139
+ if (!result.valid) {
140
+ expect(result.errors).toEqual([
141
+ expect.objectContaining({
142
+ path: 'jobs.inspect.steps.0.tools.1',
143
+ message: expect.stringContaining('web_search'),
144
+ }),
145
+ expect.objectContaining({
146
+ path: 'jobs.inspect.steps.0.tools.2',
147
+ message: expect.stringContaining('get_search_content'),
148
+ }),
149
+ ]);
150
+ }
151
+ });
152
+ });
@@ -0,0 +1,72 @@
1
+ import type {HarnessToolDeploymentConfig} from '@shipfox/api-agent-dto';
2
+ import {InvalidWorkflowDocumentError} from '@shipfox/workflow-document';
3
+ import {definitionDefaultRunnerLabels, definitionHarnessToolDeploymentConfig} from '../config.js';
4
+ import type {IntegrationValidationContext} from './entities/integration-context.js';
5
+ import type {WorkflowDefinitionPayload} from './entities/workflow-definition.js';
6
+ import {InvalidWorkflowModelError, normalizeWorkflowDocument} from './workflow-model/index.js';
7
+ import {InvalidWorkflowYamlError, parseWorkflowYamlWithLocations} from './workflow-yaml/index.js';
8
+
9
+ export type ValidationError = {message: string; path?: string | undefined};
10
+
11
+ export type ValidationResult =
12
+ | {valid: true; definition: WorkflowDefinitionPayload}
13
+ | {valid: false; errors: ValidationError[]};
14
+
15
+ export function validateDefinition(
16
+ yamlContent: string,
17
+ options: {
18
+ defaultRunnerLabels?: readonly string[];
19
+ harnessToolDeploymentConfig?: HarnessToolDeploymentConfig;
20
+ integrationValidationContext?: IntegrationValidationContext;
21
+ } = {},
22
+ ): ValidationResult {
23
+ try {
24
+ const {document, stepSourceLocations} = parseWorkflowYamlWithLocations(yamlContent);
25
+ const model = normalizeWorkflowDocument(document, {
26
+ defaultRunnerLabels: options.defaultRunnerLabels ?? definitionDefaultRunnerLabels,
27
+ harnessToolDeploymentConfig:
28
+ options.harnessToolDeploymentConfig ?? definitionHarnessToolDeploymentConfig,
29
+ integrationValidationContext: options.integrationValidationContext,
30
+ stepSourceLocations,
31
+ });
32
+ return {valid: true, definition: {document, model}};
33
+ } catch (error) {
34
+ return {valid: false, errors: validationErrorsFor(error)};
35
+ }
36
+ }
37
+
38
+ function validationErrorsFor(error: unknown): ValidationError[] {
39
+ if (error instanceof InvalidWorkflowYamlError) {
40
+ return [
41
+ validationError({
42
+ message: error.message,
43
+ path:
44
+ error.location === undefined
45
+ ? undefined
46
+ : `${error.location.line}:${error.location.column}`,
47
+ }),
48
+ ];
49
+ }
50
+
51
+ if (error instanceof InvalidWorkflowDocumentError) {
52
+ return error.validationError.issues.map((issue) =>
53
+ validationError({
54
+ message: issue.message,
55
+ path: issue.path.join('.') || undefined,
56
+ }),
57
+ );
58
+ }
59
+
60
+ if (error instanceof InvalidWorkflowModelError) {
61
+ return error.issues.map((issue) =>
62
+ validationError({message: issue.message, path: issue.path.join('.')}),
63
+ );
64
+ }
65
+
66
+ throw error;
67
+ }
68
+
69
+ function validationError(params: {message: string; path?: string | undefined}): ValidationError {
70
+ if (params.path === undefined) return {message: params.message};
71
+ return {message: params.message, path: params.path};
72
+ }
@@ -0,0 +1,129 @@
1
+ # Workflow Model
2
+
3
+ Code that turns a checked workflow document into the model used by definitions.
4
+
5
+ ## What it does
6
+
7
+ - **`WorkflowModel`**: Defines the stable workflow graph used after document
8
+ parsing.
9
+ - **`normalizeWorkflowDocument(document)`**: Expands shorthand fields, assigns
10
+ stable ids, validates explicit providers, and builds graph edges.
11
+ - **Step gates**: Parse run-step `gate.success` as a server-evaluated CEL
12
+ predicate against context roots available at step reporting, and check
13
+ `gate.on_failure.restart_from` against earlier named steps in the same job.
14
+ - **`InvalidWorkflowModelError`**: Reports semantic workflow errors found during
15
+ normalization.
16
+
17
+ Use this module after a workflow document has passed the shared document checks.
18
+ It gives later code one shape for jobs, steps, runners, and graph edges. This is
19
+ useful even when YAML is the only file format. YAML is for people to write. The
20
+ model is for Shipfox code to use.
21
+
22
+ This split keeps each step clear. The document package checks the public shape.
23
+ This module decides what that shape means for jobs and run order. Later code can
24
+ use the model without asking how the workflow was written.
25
+
26
+ Think of this as the hand off from what a person wrote to what the system can
27
+ run. A person may use short forms and names that are easy to read. The system
28
+ needs one clear form. This module makes that form once, so the rest of the code
29
+ does not have to guess.
30
+
31
+ This is why the module is here. It gives the rest of the app a clear way to do
32
+ its work. A test can set up one case, call one thing, and check what comes back.
33
+ That keeps the next step easy to read.
34
+ It is small.
35
+
36
+ ## Installation / Setup
37
+
38
+ This code is part of `@shipfox/api-definitions`. It is not a standalone package.
39
+
40
+ ```json
41
+ {
42
+ "dependencies": {
43
+ "@shipfox/api-agent-dto": "workspace:*",
44
+ "@shipfox/workflow-document": "workspace:*"
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ```ts
52
+ import {normalizeWorkflowDocument} from '#core/workflow-model/index.js';
53
+
54
+ const model = normalizeWorkflowDocument({
55
+ name: 'simple build',
56
+ runner: 'ubuntu-latest',
57
+ jobs: {
58
+ build: {
59
+ steps: [{run: 'npm test'}],
60
+ },
61
+ },
62
+ });
63
+
64
+ console.log(model.jobs[0]?.steps[0]?.command.value);
65
+ ```
66
+
67
+ ## Behavior Notes
68
+
69
+ The model does not own project ids, commit shas, refs, dates, database rows, or
70
+ file paths. Those fields belong to `WorkflowDefinition`.
71
+
72
+ Trigger filters stay as source strings in this module until event schemas define
73
+ the expression context needed to type-check them.
74
+
75
+ Event selectors use one document shape, `{source, event, with, filter}`, in
76
+ three roles. Workflow `triggers` start a run. Job `listening.on` fires job
77
+ executions. Job `listening.until` resolves the listening job.
78
+
79
+ The persisted `workflow_document` keeps author-provided runner labels exactly as
80
+ written. Canonical and defaulted labels live on `workflow_model.jobs[].runner`;
81
+ downstream consumers should read the model, not `document.runner`.
82
+
83
+ Run-step gate expressions are different. They are server-evaluated predicates,
84
+ so this module parses them against the shared context registry, rejects
85
+ runner-host roots, and checks that each root is available at step reporting. The
86
+ accepted model stores a `WorkflowExpression` with `language: 'cel'`, the
87
+ original source string, and the strongest check mode the referenced roots allow.
88
+
89
+ Agent steps keep omitted `provider`, `model`, and `thinking` fields omitted in
90
+ the definition model. Run materialization resolves runner-ready catalog defaults
91
+ before execution. Explicit providers are checked against the shared provider
92
+ catalog; explicit model ids are preserved because the shared seed catalog does
93
+ not yet carry each provider's full model list.
94
+
95
+ For now, run-step gates can use typed roots such as `step.exit_code`,
96
+ `step.status`, open output fields such as `step.outputs.pass`, and other server
97
+ roots that are available by step reporting. Guard optional output keys with
98
+ `has(step.outputs.pass)` before reading them so missing keys fail as a normal
99
+ false predicate instead of an uncheckable evaluation error.
100
+
101
+ `on_failure.restart_from` must name an earlier step in the same job. The runtime
102
+ host does not execute restart semantics yet. This module only records the
103
+ accepted meaning in the model.
104
+
105
+ If a rule needs project data or database state, put that rule in another layer.
106
+ This module should stay pure and easy to test.
107
+
108
+ If a rule can be checked from the document alone, it can live here. If a rule
109
+ needs a project, a user, a commit, or a database row, it should live with that
110
+ data.
111
+
112
+ Run this step before saving a new definition. That way bad links, bad ids, and
113
+ bad gate rules stop early, while the caller still knows which file field caused
114
+ the problem.
115
+
116
+ This also makes the next step simpler. The next step can read one clear shape
117
+ instead of checking many short forms again.
118
+
119
+ ## Development
120
+
121
+ ```sh
122
+ turbo check --filter=@shipfox/api-definitions
123
+ turbo type --filter=@shipfox/api-definitions
124
+ turbo test --filter=@shipfox/api-definitions
125
+ ```
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1 @@
1
+ export const DEFAULT_RUN_TIMEOUT_MS = 30 * 24 * 60 * 60 * 1000;
@@ -0,0 +1,29 @@
1
+ import {isValidCronExpression, isValidTimezone} from './cron-trigger.js';
2
+
3
+ describe('isValidCronExpression', () => {
4
+ it.each([['0 2 * * *'], ['*/15 9-17 * * 1-5'], ['0 2 * * MON']])('accepts %s', (expression) => {
5
+ const result = isValidCronExpression(expression);
6
+
7
+ expect(result).toBe(true);
8
+ });
9
+
10
+ it.each([['0 0 2 * * *'], ['@daily'], [' '], ['not a cron']])('rejects %s', (expression) => {
11
+ const result = isValidCronExpression(expression);
12
+
13
+ expect(result).toBe(false);
14
+ });
15
+ });
16
+
17
+ describe('isValidTimezone', () => {
18
+ it.each(['UTC', 'Europe/Paris', 'US/Pacific'])('accepts %s', (timezone) => {
19
+ const result = isValidTimezone(timezone);
20
+
21
+ expect(result).toBe(true);
22
+ });
23
+
24
+ it('rejects an unknown timezone', () => {
25
+ const result = isValidTimezone('Not/A/Zone');
26
+
27
+ expect(result).toBe(false);
28
+ });
29
+ });
@@ -0,0 +1,83 @@
1
+ import {CronExpressionParser} from 'cron-parser';
2
+ import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
3
+ import {issue} from './validation-issue.js';
4
+
5
+ export const cronTriggerDefaultTimezone = 'UTC';
6
+ const cronFieldSeparatorRegex = /\s+/;
7
+
8
+ export function validateCronTrigger(params: {
9
+ readonly sourceKey: string;
10
+ readonly trigger: {
11
+ readonly event: string;
12
+ };
13
+ readonly config: {
14
+ readonly schedule?: string | undefined;
15
+ readonly timezone?: string | undefined;
16
+ };
17
+ readonly issues: WorkflowModelValidationIssue[];
18
+ }): void {
19
+ const {sourceKey, trigger, config, issues} = params;
20
+
21
+ if (trigger.event !== 'tick') {
22
+ issues.push(
23
+ issue({
24
+ code: 'invalid-cron-event',
25
+ message: `A cron trigger must use event "tick"; found "${trigger.event}".`,
26
+ path: ['triggers', sourceKey, 'event'],
27
+ details: {event: trigger.event},
28
+ }),
29
+ );
30
+ }
31
+
32
+ if (config.schedule === undefined) {
33
+ issues.push(
34
+ issue({
35
+ code: 'missing-cron-schedule',
36
+ message: 'A cron trigger requires a schedule.',
37
+ path: ['triggers', sourceKey, 'config', 'schedule'],
38
+ }),
39
+ );
40
+ } else if (!isValidCronExpression(config.schedule)) {
41
+ issues.push(
42
+ issue({
43
+ code: 'invalid-cron-schedule',
44
+ message: 'Cron trigger schedule must be a valid 5-field cron expression.',
45
+ path: ['triggers', sourceKey, 'config', 'schedule'],
46
+ details: {schedule: config.schedule},
47
+ }),
48
+ );
49
+ }
50
+
51
+ if (config.timezone !== undefined && !isValidTimezone(config.timezone)) {
52
+ issues.push(
53
+ issue({
54
+ code: 'invalid-cron-timezone',
55
+ message: 'Cron trigger timezone must be a valid IANA time zone.',
56
+ path: ['triggers', sourceKey, 'config', 'timezone'],
57
+ details: {timezone: config.timezone},
58
+ }),
59
+ );
60
+ }
61
+ }
62
+
63
+ export function isValidCronExpression(expression: string): boolean {
64
+ const trimmedExpression = expression.trim();
65
+ if (trimmedExpression.length === 0) return false;
66
+ if (trimmedExpression.split(cronFieldSeparatorRegex).length !== 5) return false;
67
+
68
+ try {
69
+ CronExpressionParser.parse(trimmedExpression);
70
+ return true;
71
+ } catch {
72
+ return false;
73
+ }
74
+ }
75
+
76
+ export function isValidTimezone(timezone: string): boolean {
77
+ try {
78
+ new Intl.DateTimeFormat('en-US', {timeZone: timezone});
79
+ return true;
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
@@ -0,0 +1,9 @@
1
+ export {DEFAULT_RUN_TIMEOUT_MS} from './constants.js';
2
+ export {
3
+ InvalidWorkflowModelError,
4
+ invalidWorkflowModelErrorCode,
5
+ type WorkflowModelValidationIssue,
6
+ type WorkflowModelValidationIssueCode,
7
+ type WorkflowModelValidationIssuePathSegment,
8
+ } from './invalid-workflow-model-error.js';
9
+ export {normalizeWorkflowDocument} from './normalize-workflow-document.js';
@@ -0,0 +1,71 @@
1
+ export const invalidWorkflowModelErrorCode = 'invalid-workflow-model';
2
+
3
+ export type WorkflowModelValidationIssueCode =
4
+ | 'context-unavailable-at-fill-site'
5
+ | 'context-unavailable-at-predicate-site'
6
+ | 'computed-context-key'
7
+ | 'duplicate-job-id'
8
+ | 'duplicate-step-id'
9
+ | 'duplicate-trigger-id'
10
+ | 'harness-provider-incompatible'
11
+ | 'harness-thinking-incompatible'
12
+ | 'harness-tool-incompatible'
13
+ | 'integration-connection-not-capable'
14
+ | 'integration-connection-not-found'
15
+ | 'integration-write-not-allowed'
16
+ | 'invalid-cron-event'
17
+ | 'invalid-cron-schedule'
18
+ | 'invalid-cron-timezone'
19
+ | 'invalid-provider'
20
+ | 'invalid-trigger-filter'
21
+ | 'invalid-interpolation-expression'
22
+ | 'invalid-interpolation-template'
23
+ | 'invalid-duration'
24
+ | 'invalid-listener-filter'
25
+ | 'invalid-job-if'
26
+ | 'invalid-job-output'
27
+ | 'invalid-job-success'
28
+ | 'invalid-output-schema'
29
+ | 'invalid-runner-label'
30
+ | 'invalid-step-gate-restart-from'
31
+ | 'invalid-step-gate-success'
32
+ | 'invalid-step-if'
33
+ | 'job-dependency-cycle'
34
+ | 'listening-job-missing-resolution-source'
35
+ | 'listening-timeout-exceeds-run-timeout'
36
+ | 'missing-harness-for-tools'
37
+ | 'missing-connection-for-integration'
38
+ | 'missing-job-needs-edge'
39
+ | 'missing-cron-schedule'
40
+ | 'missing-runner-label'
41
+ | 'multiple-manual-triggers'
42
+ | 'runner-context-not-bare'
43
+ | 'runner-context-in-field'
44
+ | 'runner-context-in-server-predicate'
45
+ | 'self-job-dependency'
46
+ | 'too-many-runner-labels'
47
+ | 'unknown-secret-store'
48
+ | 'unknown-interpolation-context'
49
+ | 'unknown-integration-method'
50
+ | 'unknown-integration-tool'
51
+ | 'unknown-job-dependency'
52
+ | 'untrusted-context-in-field'
53
+ | 'vars-context-in-server-predicate';
54
+
55
+ export type WorkflowModelValidationIssuePathSegment = string | number;
56
+
57
+ export interface WorkflowModelValidationIssue {
58
+ readonly code: WorkflowModelValidationIssueCode;
59
+ readonly message: string;
60
+ readonly path: readonly WorkflowModelValidationIssuePathSegment[];
61
+ readonly details?: Readonly<Record<string, unknown>>;
62
+ }
63
+
64
+ export class InvalidWorkflowModelError extends Error {
65
+ readonly code = invalidWorkflowModelErrorCode;
66
+
67
+ constructor(readonly issues: readonly WorkflowModelValidationIssue[]) {
68
+ super('Invalid workflow model');
69
+ this.name = 'InvalidWorkflowModelError';
70
+ }
71
+ }
@@ -0,0 +1,33 @@
1
+ import type {WorkflowDocument} from '@shipfox/workflow-document';
2
+ import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
3
+ import {stableId} from './stable-id.js';
4
+ import {issue} from './validation-issue.js';
5
+
6
+ export function mapJobIds(
7
+ document: WorkflowDocument,
8
+ issues: WorkflowModelValidationIssue[],
9
+ ): ReadonlyMap<string, string> {
10
+ const jobIdBySourceName = new Map<string, string>();
11
+ const usedJobIds = new Map<string, string>();
12
+
13
+ for (const sourceKey of Object.keys(document.jobs)) {
14
+ const id = stableId(sourceKey);
15
+ const existingSourceKey = usedJobIds.get(id);
16
+ if (existingSourceKey !== undefined) {
17
+ issues.push(
18
+ issue({
19
+ code: 'duplicate-job-id',
20
+ message: `Job keys "${existingSourceKey}" and "${sourceKey}" resolve to the same stable id "${id}".`,
21
+ path: ['jobs', sourceKey],
22
+ details: {id, sourceKeys: [existingSourceKey, sourceKey]},
23
+ }),
24
+ );
25
+ continue;
26
+ }
27
+
28
+ usedJobIds.set(id, sourceKey);
29
+ jobIdBySourceName.set(sourceKey, id);
30
+ }
31
+
32
+ return jobIdBySourceName;
33
+ }