@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,208 @@
1
+ import type {AgentToolSelector} from '@shipfox/api-integration-core-dto';
2
+ import type {WorkflowDocumentStepIntegration} from '@shipfox/workflow-document';
3
+ import type {IntegrationValidationContext} from '../entities/integration-context.js';
4
+ import type {WorkflowModelStepIntegration} from '../entities/workflow-model.js';
5
+ import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
6
+ import {issue} from './validation-issue.js';
7
+
8
+ export function normalizeAgentIntegrations(params: {
9
+ integrations: readonly WorkflowDocumentStepIntegration[] | undefined;
10
+ sourceName: string;
11
+ stepIndex: number;
12
+ issues: WorkflowModelValidationIssue[];
13
+ integrationValidationContext?: IntegrationValidationContext | undefined;
14
+ }): readonly WorkflowModelStepIntegration[] | undefined {
15
+ if (params.integrations === undefined) return undefined;
16
+
17
+ return params.integrations.map((integration, integrationIndex) => {
18
+ const normalized = normalizeIntegration(integration);
19
+ validateIntegration({...params, integration, normalized, integrationIndex});
20
+ return normalized;
21
+ });
22
+ }
23
+
24
+ function normalizeIntegration(
25
+ integration: WorkflowDocumentStepIntegration,
26
+ ): WorkflowModelStepIntegration {
27
+ return {
28
+ ...(integration.connection === undefined ? {} : {connection: integration.connection}),
29
+ include: dedupe(integration.include),
30
+ ...(integration.exclude === undefined ? {} : {exclude: dedupe(integration.exclude)}),
31
+ allowWrite: integration.allow_write ?? false,
32
+ };
33
+ }
34
+
35
+ function validateIntegration(params: {
36
+ integration: WorkflowDocumentStepIntegration;
37
+ normalized: WorkflowModelStepIntegration;
38
+ sourceName: string;
39
+ stepIndex: number;
40
+ integrationIndex: number;
41
+ issues: WorkflowModelValidationIssue[];
42
+ integrationValidationContext?: IntegrationValidationContext | undefined;
43
+ }): void {
44
+ const context = params.integrationValidationContext;
45
+ if (context === undefined) return;
46
+
47
+ const connectionSlug = params.normalized.connection ?? context.defaultConnectionSlug;
48
+ if (connectionSlug === undefined) {
49
+ params.issues.push(
50
+ issue({
51
+ code: 'missing-connection-for-integration',
52
+ message: 'Agent step integration requires a connection or a default source connection.',
53
+ path: integrationPath(params),
54
+ details: {integrationIndex: params.integrationIndex},
55
+ }),
56
+ );
57
+ return;
58
+ }
59
+
60
+ const connection = context.workspaceConnectionSnapshot.get(connectionSlug);
61
+ if (connection === undefined) {
62
+ params.issues.push(
63
+ issue({
64
+ code: 'integration-connection-not-found',
65
+ message: `Integration connection "${connectionSlug}" was not found in the workspace.`,
66
+ path: integrationConnectionPath(params),
67
+ details: {
68
+ connection: connectionSlug,
69
+ integrationIndex: params.integrationIndex,
70
+ },
71
+ }),
72
+ );
73
+ return;
74
+ }
75
+
76
+ const catalog = context.agentToolSelectionCatalogs.get(connection.provider);
77
+ if (catalog === undefined || !connection.capabilities.includes('agent_tools')) {
78
+ params.issues.push(
79
+ issue({
80
+ code: 'integration-connection-not-capable',
81
+ message: `Integration connection "${connectionSlug}" does not support agent tools.`,
82
+ path: integrationConnectionPath(params),
83
+ details: {
84
+ connection: connectionSlug,
85
+ provider: connection.provider,
86
+ capabilities: connection.capabilities,
87
+ integrationIndex: params.integrationIndex,
88
+ },
89
+ }),
90
+ );
91
+ return;
92
+ }
93
+
94
+ const selectorsByToken = new Map(catalog.selectors.map((selector) => [selector.token, selector]));
95
+ validateSelection({
96
+ ...params,
97
+ field: 'include',
98
+ tokens: params.integration.include,
99
+ selectorsByToken,
100
+ });
101
+ if (params.integration.exclude !== undefined) {
102
+ validateSelection({
103
+ ...params,
104
+ field: 'exclude',
105
+ tokens: params.integration.exclude,
106
+ selectorsByToken,
107
+ });
108
+ }
109
+ validateWriteSelection({...params, selectorsByToken});
110
+ }
111
+
112
+ function validateSelection(params: {
113
+ field: 'include' | 'exclude';
114
+ tokens: readonly string[];
115
+ selectorsByToken: ReadonlyMap<string, AgentToolSelector>;
116
+ sourceName: string;
117
+ stepIndex: number;
118
+ integrationIndex: number;
119
+ issues: WorkflowModelValidationIssue[];
120
+ }): void {
121
+ params.tokens.forEach((token, tokenIndex) => {
122
+ if (params.selectorsByToken.has(token)) return;
123
+
124
+ const code = classifyUnknownSelection(token, params.selectorsByToken);
125
+ params.issues.push(
126
+ issue({
127
+ code,
128
+ message:
129
+ code === 'unknown-integration-method'
130
+ ? `Unknown integration tool method: ${token}.`
131
+ : `Unknown integration tool: ${token}.`,
132
+ path: [...integrationPath(params), params.field, tokenIndex],
133
+ details: {token},
134
+ }),
135
+ );
136
+ });
137
+ }
138
+
139
+ function validateWriteSelection(params: {
140
+ integration: WorkflowDocumentStepIntegration;
141
+ normalized: WorkflowModelStepIntegration;
142
+ selectorsByToken: ReadonlyMap<string, AgentToolSelector>;
143
+ sourceName: string;
144
+ stepIndex: number;
145
+ integrationIndex: number;
146
+ issues: WorkflowModelValidationIssue[];
147
+ }): void {
148
+ if (params.normalized.allowWrite) return;
149
+
150
+ const writeTokens = dedupe(
151
+ params.integration.include.filter(
152
+ (token) => params.selectorsByToken.get(token)?.sensitivity === 'write',
153
+ ),
154
+ );
155
+ if (writeTokens.length === 0) return;
156
+
157
+ params.issues.push(
158
+ issue({
159
+ code: 'integration-write-not-allowed',
160
+ message: `Integration selection includes write-capable tools but allow_write is not true: ${writeTokens.join(', ')}.`,
161
+ path: [...integrationPath(params), 'include'],
162
+ details: {tokens: writeTokens},
163
+ }),
164
+ );
165
+ }
166
+
167
+ function classifyUnknownSelection(
168
+ token: string,
169
+ selectorsByToken: ReadonlyMap<string, AgentToolSelector>,
170
+ ): 'unknown-integration-method' | 'unknown-integration-tool' {
171
+ const dotIndex = token.indexOf('.');
172
+ if (dotIndex < 1) return 'unknown-integration-tool';
173
+
174
+ const family = token.slice(0, dotIndex);
175
+ return selectorsByToken.get(family)?.kind === 'family'
176
+ ? 'unknown-integration-method'
177
+ : 'unknown-integration-tool';
178
+ }
179
+
180
+ function integrationConnectionPath(params: {
181
+ sourceName: string;
182
+ stepIndex: number;
183
+ integrationIndex: number;
184
+ integration: WorkflowDocumentStepIntegration;
185
+ }): readonly (string | number)[] {
186
+ const path = integrationPath(params);
187
+ if (params.integration.connection === undefined) return path;
188
+ return [...path, 'connection'];
189
+ }
190
+
191
+ function integrationPath(params: {
192
+ sourceName: string;
193
+ stepIndex: number;
194
+ integrationIndex: number;
195
+ }): readonly (string | number)[] {
196
+ return [
197
+ 'jobs',
198
+ params.sourceName,
199
+ 'steps',
200
+ params.stepIndex,
201
+ 'integrations',
202
+ params.integrationIndex,
203
+ ];
204
+ }
205
+
206
+ function dedupe(values: readonly string[]): readonly string[] {
207
+ return [...new Set(values)];
208
+ }
@@ -0,0 +1,139 @@
1
+ import type {WorkflowDocumentJob} from '@shipfox/workflow-document';
2
+ import type {WorkflowModelDependency} from '../entities/workflow-model.js';
3
+ import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
4
+ import {normalizeNeeds} from './normalize-needs.js';
5
+ import {issue} from './validation-issue.js';
6
+
7
+ export function normalizeDependencies(
8
+ jobs: Readonly<Record<string, WorkflowDocumentJob>>,
9
+ jobIdBySourceName: ReadonlyMap<string, string>,
10
+ issues: WorkflowModelValidationIssue[],
11
+ ): readonly WorkflowModelDependency[] {
12
+ const dependencies: WorkflowModelDependency[] = [];
13
+
14
+ for (const [sourceName, job] of Object.entries(jobs)) {
15
+ const to = jobIdBySourceName.get(sourceName);
16
+ if (to === undefined) continue;
17
+
18
+ for (const dependencySourceName of normalizeNeeds(job.needs)) {
19
+ if (!jobIdBySourceName.has(dependencySourceName)) {
20
+ issues.push(
21
+ issue({
22
+ code: 'unknown-job-dependency',
23
+ message: `Job "${sourceName}" depends on unknown job "${dependencySourceName}".`,
24
+ path: ['jobs', sourceName, 'needs'],
25
+ details: {job: sourceName, dependency: dependencySourceName},
26
+ }),
27
+ );
28
+ continue;
29
+ }
30
+
31
+ if (dependencySourceName === sourceName) {
32
+ issues.push(
33
+ issue({
34
+ code: 'self-job-dependency',
35
+ message: `Job "${sourceName}" depends on itself.`,
36
+ path: ['jobs', sourceName, 'needs'],
37
+ details: {job: sourceName},
38
+ }),
39
+ );
40
+ continue;
41
+ }
42
+
43
+ dependencies.push({from: jobIdBySourceName.get(dependencySourceName) as string, to});
44
+ }
45
+ }
46
+
47
+ return dependencies;
48
+ }
49
+
50
+ export function validateCycles(
51
+ jobs: Readonly<Record<string, WorkflowDocumentJob>>,
52
+ jobIdBySourceName: ReadonlyMap<string, string>,
53
+ issues: WorkflowModelValidationIssue[],
54
+ ): void {
55
+ const jobNames = Object.keys(jobs);
56
+ const adjacency = new Map<string, string[]>();
57
+
58
+ for (const name of jobNames) {
59
+ adjacency.set(name, []);
60
+ }
61
+
62
+ for (const [name, job] of Object.entries(jobs)) {
63
+ for (const dependency of normalizeNeeds(job.needs)) {
64
+ if (!jobIdBySourceName.has(dependency) || dependency === name) continue;
65
+
66
+ adjacency.get(dependency)?.push(name);
67
+ }
68
+ }
69
+
70
+ const cyclicNames = findCyclicSourceNames(jobNames, adjacency);
71
+ if (cyclicNames.length > 0) {
72
+ issues.push(
73
+ issue({
74
+ code: 'job-dependency-cycle',
75
+ message: `Circular dependency detected among jobs: ${cyclicNames.join(', ')}.`,
76
+ path: ['jobs'],
77
+ details: {
78
+ cycleSourceNames: cyclicNames,
79
+ cycleJobIds: cyclicNames.flatMap((name) => {
80
+ const id = jobIdBySourceName.get(name);
81
+ return id === undefined ? [] : [id];
82
+ }),
83
+ },
84
+ }),
85
+ );
86
+ }
87
+ }
88
+
89
+ function findCyclicSourceNames(
90
+ jobNames: readonly string[],
91
+ adjacency: ReadonlyMap<string, readonly string[]>,
92
+ ): readonly string[] {
93
+ const indexes = new Map<string, number>();
94
+ const lowLinks = new Map<string, number>();
95
+ const stack: string[] = [];
96
+ const onStack = new Set<string>();
97
+ const cyclicNames = new Set<string>();
98
+ let index = 0;
99
+
100
+ const visit = (node: string) => {
101
+ indexes.set(node, index);
102
+ lowLinks.set(node, index);
103
+ index += 1;
104
+ stack.push(node);
105
+ onStack.add(node);
106
+
107
+ for (const neighbor of adjacency.get(node) ?? []) {
108
+ if (!indexes.has(neighbor)) {
109
+ visit(neighbor);
110
+ lowLinks.set(
111
+ node,
112
+ Math.min(lowLinks.get(node) as number, lowLinks.get(neighbor) as number),
113
+ );
114
+ } else if (onStack.has(neighbor)) {
115
+ lowLinks.set(node, Math.min(lowLinks.get(node) as number, indexes.get(neighbor) as number));
116
+ }
117
+ }
118
+
119
+ if (lowLinks.get(node) !== indexes.get(node)) return;
120
+
121
+ const component: string[] = [];
122
+ while (stack.length > 0) {
123
+ const member = stack.pop() as string;
124
+ onStack.delete(member);
125
+ component.push(member);
126
+ if (member === node) break;
127
+ }
128
+
129
+ if (component.length > 1) {
130
+ for (const member of component) cyclicNames.add(member);
131
+ }
132
+ };
133
+
134
+ for (const name of jobNames) {
135
+ if (!indexes.has(name)) visit(name);
136
+ }
137
+
138
+ return jobNames.filter((name) => cyclicNames.has(name));
139
+ }
@@ -0,0 +1,48 @@
1
+ import type {AvailabilitySite, ExpressionTypeEnvironment} from '@shipfox/expression';
2
+ import type {WorkflowEnvTemplates, WorkflowFieldTemplate} from '../entities/workflow-model.js';
3
+ import type {
4
+ WorkflowModelValidationIssue,
5
+ WorkflowModelValidationIssuePathSegment,
6
+ } from './invalid-workflow-model-error.js';
7
+ import {parseInterpolationField} from './parse-interpolation-field.js';
8
+
9
+ export function normalizeEnv(params: {
10
+ env: Readonly<Record<string, string | number | boolean>> | undefined;
11
+ path: readonly WorkflowModelValidationIssuePathSegment[];
12
+ issues: WorkflowModelValidationIssue[];
13
+ fillSite?: AvailabilitySite;
14
+ allowedJobReferences?: ReadonlySet<string>;
15
+ typeOverlay?: ExpressionTypeEnvironment | undefined;
16
+ }): {env?: Readonly<Record<string, string>>; templates?: {env: WorkflowEnvTemplates}} {
17
+ const env = params.env;
18
+ if (env === undefined || Object.keys(env).length === 0) return {};
19
+
20
+ const normalizedEnv: Record<string, string> = Object.create(null) as Record<string, string>;
21
+ const templates: Record<string, WorkflowFieldTemplate> = Object.create(null) as Record<
22
+ string,
23
+ WorkflowFieldTemplate
24
+ >;
25
+
26
+ for (const [key, value] of Object.entries(env)) {
27
+ normalizedEnv[key] = String(value);
28
+ if (typeof value !== 'string') continue;
29
+
30
+ const template = parseInterpolationField({
31
+ field: 'env.value',
32
+ source: value,
33
+ path: [...params.path, key],
34
+ issues: params.issues,
35
+ ...(params.fillSite === undefined ? {} : {fillSite: params.fillSite}),
36
+ ...(params.allowedJobReferences === undefined
37
+ ? {}
38
+ : {allowedJobReferences: params.allowedJobReferences}),
39
+ ...(params.typeOverlay === undefined ? {} : {typeOverlay: params.typeOverlay}),
40
+ });
41
+ if (template !== undefined) templates[key] = template;
42
+ }
43
+
44
+ return {
45
+ env: normalizedEnv,
46
+ ...(Object.keys(templates).length === 0 ? {} : {templates: {env: templates}}),
47
+ };
48
+ }
@@ -0,0 +1,126 @@
1
+ import {
2
+ type ExpressionTypeEnvironment,
3
+ extractExactContextRoots,
4
+ InvalidWorkflowTemplateError,
5
+ parseWorkflowTemplate,
6
+ type WorkflowExpression,
7
+ type WorkflowPredicateField,
8
+ type WorkflowTemplateExprSegment,
9
+ } from '@shipfox/expression';
10
+ import type {
11
+ WorkflowModelValidationIssue,
12
+ WorkflowModelValidationIssueCode,
13
+ WorkflowModelValidationIssuePathSegment,
14
+ } from './invalid-workflow-model-error.js';
15
+ import {validatePredicateExpression} from './validate-predicate-expression.js';
16
+ import {issue} from './validation-issue.js';
17
+ import {workflowFieldLabel} from './workflow-field-label.js';
18
+
19
+ const ifExpressionSyntax = `\${{ }}`;
20
+
21
+ export function normalizeIfCondition(params: {
22
+ readonly field: Extract<WorkflowPredicateField, 'job.if' | 'step.if'>;
23
+ readonly source: string | undefined;
24
+ readonly site: 'job-activation' | 'step-dispatch';
25
+ readonly path: readonly WorkflowModelValidationIssuePathSegment[];
26
+ readonly invalidCode: Extract<
27
+ WorkflowModelValidationIssueCode,
28
+ 'invalid-job-if' | 'invalid-step-if'
29
+ >;
30
+ readonly invalidMessage: string;
31
+ readonly issues: WorkflowModelValidationIssue[];
32
+ readonly allowedJobReferences: ReadonlySet<string>;
33
+ readonly typeOverlay: ExpressionTypeEnvironment;
34
+ }): WorkflowExpression | undefined {
35
+ if (params.source === undefined) return undefined;
36
+ const source = params.source;
37
+
38
+ const expressionSegment = parseSingleExpression({...params, source});
39
+ if (expressionSegment === undefined) return undefined;
40
+
41
+ const roots = extractExactContextRoots(expressionSegment.expression.source);
42
+ if (roots.includes('needs') && params.allowedJobReferences.size === 0) {
43
+ params.issues.push(
44
+ issue({
45
+ code: params.invalidCode,
46
+ message: `${workflowFieldLabel(params.field)} cannot reference needs because the job has no direct needs.`,
47
+ path: params.path,
48
+ details: {
49
+ field: params.field,
50
+ source: params.source,
51
+ contextRoots: roots,
52
+ rejectedRoots: ['needs'],
53
+ },
54
+ }),
55
+ );
56
+ return undefined;
57
+ }
58
+
59
+ return validatePredicateExpression({
60
+ field: params.field,
61
+ source: expressionSegment.expression.source,
62
+ site: params.site,
63
+ path: params.path,
64
+ invalidCode: params.invalidCode,
65
+ invalidMessage: params.invalidMessage,
66
+ issues: params.issues,
67
+ allowedJobReferences: params.allowedJobReferences,
68
+ typeOverlay: params.typeOverlay,
69
+ });
70
+ }
71
+
72
+ function parseSingleExpression(params: {
73
+ readonly field: WorkflowPredicateField;
74
+ readonly source: string;
75
+ readonly path: readonly WorkflowModelValidationIssuePathSegment[];
76
+ readonly invalidCode: WorkflowModelValidationIssueCode;
77
+ readonly invalidMessage: string;
78
+ readonly issues: WorkflowModelValidationIssue[];
79
+ }): WorkflowTemplateExprSegment | undefined {
80
+ let segments: ReturnType<typeof parseWorkflowTemplate>;
81
+ try {
82
+ segments = parseWorkflowTemplate(params.source);
83
+ } catch (error) {
84
+ params.issues.push(
85
+ invalidIfIssue({
86
+ ...params,
87
+ reason:
88
+ error instanceof InvalidWorkflowTemplateError
89
+ ? error.reason
90
+ : 'Template source did not parse.',
91
+ }),
92
+ );
93
+ return undefined;
94
+ }
95
+
96
+ const [segment] = segments;
97
+ if (segment?.kind === 'expr' && segments.length === 1) return segment;
98
+
99
+ params.issues.push(
100
+ invalidIfIssue({
101
+ ...params,
102
+ reason: `${workflowFieldLabel(params.field)} must be exactly one ${ifExpressionSyntax} expression.`,
103
+ }),
104
+ );
105
+ return undefined;
106
+ }
107
+
108
+ function invalidIfIssue(params: {
109
+ readonly field: WorkflowPredicateField;
110
+ readonly source: string;
111
+ readonly path: readonly WorkflowModelValidationIssuePathSegment[];
112
+ readonly invalidCode: WorkflowModelValidationIssueCode;
113
+ readonly invalidMessage: string;
114
+ readonly reason: string;
115
+ }): WorkflowModelValidationIssue {
116
+ return issue({
117
+ code: params.invalidCode,
118
+ message: params.invalidMessage,
119
+ path: params.path,
120
+ details: {
121
+ field: params.field,
122
+ source: params.source,
123
+ reason: params.reason,
124
+ },
125
+ });
126
+ }
@@ -0,0 +1,19 @@
1
+ import type {WorkflowDocumentJobCheckout} from '@shipfox/workflow-document';
2
+ import type {WorkflowModelJobCheckout} from '../entities/workflow-model.js';
3
+
4
+ export const DEFAULT_JOB_CHECKOUT: WorkflowModelJobCheckout = {
5
+ permissions: {contents: 'read'},
6
+ persistCredentials: true,
7
+ };
8
+
9
+ export function normalizeJobCheckout(
10
+ checkout: WorkflowDocumentJobCheckout | undefined,
11
+ ): WorkflowModelJobCheckout {
12
+ return {
13
+ permissions: {
14
+ contents: checkout?.permissions?.contents ?? DEFAULT_JOB_CHECKOUT.permissions.contents,
15
+ },
16
+ persistCredentials:
17
+ checkout?.['persist-credentials'] ?? DEFAULT_JOB_CHECKOUT.persistCredentials,
18
+ };
19
+ }
@@ -0,0 +1,121 @@
1
+ import type {WorkflowDocumentJob} from '@shipfox/workflow-document';
2
+ import type {WorkflowModelJobListening} from '../entities/workflow-model.js';
3
+ import {DEFAULT_RUN_TIMEOUT_MS} from './constants.js';
4
+ import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
5
+ import {normalizeTriggerEntry} from './normalize-triggers.js';
6
+ import {parseDurationMs} from './parse-duration-ms.js';
7
+ import {validatePredicateExpression} from './validate-predicate-expression.js';
8
+ import {issue} from './validation-issue.js';
9
+
10
+ export function normalizeJobListening(params: {
11
+ job: WorkflowDocumentJob;
12
+ sourceName: string;
13
+ issues: WorkflowModelValidationIssue[];
14
+ allowedJobReferences: ReadonlySet<string>;
15
+ }): WorkflowModelJobListening | undefined {
16
+ const path = ['jobs', params.sourceName] as const;
17
+ const listening = params.job.listening;
18
+
19
+ if (listening === undefined) {
20
+ return undefined;
21
+ }
22
+
23
+ const timeoutMs = parseDurationMs({
24
+ source: listening.timeout,
25
+ path: [...path, 'listening', 'timeout'],
26
+ issues: params.issues,
27
+ maxMs: DEFAULT_RUN_TIMEOUT_MS,
28
+ outOfRangeCode: 'listening-timeout-exceeds-run-timeout',
29
+ outOfRangeMessage: 'Listening job timeout must be between 1s and the workflow run timeout.',
30
+ });
31
+ const debounceMs = parseDurationMs({
32
+ source: listening.batch?.debounce,
33
+ path: [...path, 'listening', 'batch', 'debounce'],
34
+ issues: params.issues,
35
+ });
36
+ const maxWaitMs = parseDurationMs({
37
+ source: listening.batch?.max_wait,
38
+ path: [...path, 'listening', 'batch', 'max_wait'],
39
+ issues: params.issues,
40
+ });
41
+
42
+ if (
43
+ listening.until === undefined &&
44
+ listening.timeout === undefined &&
45
+ listening.max_executions === undefined
46
+ ) {
47
+ params.issues.push(
48
+ issue({
49
+ code: 'listening-job-missing-resolution-source',
50
+ message: `Listening job "${params.sourceName}" must declare until, timeout, or max_executions.`,
51
+ path: [...path, 'listening'],
52
+ }),
53
+ );
54
+ }
55
+
56
+ const batch =
57
+ debounceMs === undefined && listening.batch?.max_size === undefined && maxWaitMs === undefined
58
+ ? undefined
59
+ : {
60
+ ...(debounceMs === undefined ? {} : {debounceMs}),
61
+ ...(listening.batch?.max_size === undefined ? {} : {maxSize: listening.batch.max_size}),
62
+ ...(maxWaitMs === undefined ? {} : {maxWaitMs}),
63
+ };
64
+
65
+ return {
66
+ on: listening.on.map((trigger, index) =>
67
+ normalizeListeningTrigger({
68
+ trigger,
69
+ field: 'listener.on',
70
+ path: [...path, 'listening', 'on', index, 'filter'],
71
+ issues: params.issues,
72
+ allowedJobReferences: params.allowedJobReferences,
73
+ }),
74
+ ),
75
+ ...(listening.until === undefined
76
+ ? {}
77
+ : {
78
+ until: listening.until.map((trigger, index) =>
79
+ normalizeListeningTrigger({
80
+ trigger,
81
+ field: 'listener.until',
82
+ path: [...path, 'listening', 'until', index, 'filter'],
83
+ issues: params.issues,
84
+ allowedJobReferences: params.allowedJobReferences,
85
+ }),
86
+ ),
87
+ }),
88
+ ...(timeoutMs === undefined ? {} : {timeoutMs}),
89
+ ...(listening.max_executions === undefined ? {} : {maxExecutions: listening.max_executions}),
90
+ ...(batch === undefined ? {} : {batch}),
91
+ onResolve: listening.on_resolve ?? 'finish',
92
+ };
93
+ }
94
+
95
+ function normalizeListeningTrigger(params: {
96
+ trigger: {
97
+ readonly source: string;
98
+ readonly event: string;
99
+ readonly with?: Readonly<Record<string, unknown>> | undefined;
100
+ readonly filter?: string | undefined;
101
+ };
102
+ field: 'listener.on' | 'listener.until';
103
+ path: readonly (string | number)[];
104
+ issues: WorkflowModelValidationIssue[];
105
+ allowedJobReferences: ReadonlySet<string>;
106
+ }): WorkflowModelJobListening['on'][number] {
107
+ if (params.trigger.filter !== undefined) {
108
+ validatePredicateExpression({
109
+ field: params.field,
110
+ source: params.trigger.filter,
111
+ site: 'job-activation',
112
+ path: params.path,
113
+ invalidCode: 'invalid-listener-filter',
114
+ invalidMessage: `${params.field === 'listener.on' ? 'Listener on' : 'Listener until'} filter must be a valid CEL boolean expression.`,
115
+ issues: params.issues,
116
+ allowedJobReferences: params.allowedJobReferences,
117
+ });
118
+ }
119
+
120
+ return normalizeTriggerEntry(params.trigger);
121
+ }
@@ -0,0 +1,28 @@
1
+ import type {ExpressionTypeEnvironment} from '@shipfox/expression';
2
+ import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
3
+ import {validatePredicateExpression} from './validate-predicate-expression.js';
4
+
5
+ export const DEFAULT_JOB_SUCCESS = "!executions.exists(e, e.status == 'failed')";
6
+
7
+ export function normalizeJobSuccess(params: {
8
+ source: string | undefined;
9
+ sourceName: string;
10
+ issues: WorkflowModelValidationIssue[];
11
+ allowedJobReferences: ReadonlySet<string>;
12
+ typeOverlay?: ExpressionTypeEnvironment | undefined;
13
+ }): string | undefined {
14
+ if (params.source === undefined) return undefined;
15
+
16
+ const expression = validatePredicateExpression({
17
+ field: 'job.success',
18
+ source: params.source,
19
+ site: 'job-resolution',
20
+ path: ['jobs', params.sourceName, 'success'],
21
+ invalidCode: 'invalid-job-success',
22
+ invalidMessage: 'Job success must be a valid CEL boolean expression.',
23
+ issues: params.issues,
24
+ allowedJobReferences: params.allowedJobReferences,
25
+ typeOverlay: params.typeOverlay,
26
+ });
27
+ return expression?.source;
28
+ }