@tailor-platform/sdk 1.71.0 → 1.73.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 (382) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/aigateway-B3oZZE6v.mjs +15 -0
  3. package/dist/aigateway-B3oZZE6v.mjs.map +1 -0
  4. package/dist/application-BhRnH42h.mjs +3 -0
  5. package/dist/{application-DmjIKQ-a.mjs → application-Dxhdq_nu.mjs} +21 -19
  6. package/dist/application-Dxhdq_nu.mjs.map +1 -0
  7. package/dist/{assert-CKfwrmCV.mjs → assert-DBxo8jPo.mjs} +1 -2
  8. package/dist/{assert-CKfwrmCV.mjs.map → assert-DBxo8jPo.mjs.map} +1 -1
  9. package/dist/{authconnection-D7lsvsKY.mjs → authconnection-BGQM8FZI.mjs} +2 -3
  10. package/dist/{authconnection-D7lsvsKY.mjs.map → authconnection-BGQM8FZI.mjs.map} +1 -1
  11. package/dist/{brand-DlnJ375c.mjs → brand-Eo4pLXPJ.mjs} +1 -2
  12. package/dist/{brand-DlnJ375c.mjs.map → brand-Eo4pLXPJ.mjs.map} +1 -1
  13. package/dist/cli/cache/bundle-cache.d.mts +1 -0
  14. package/dist/cli/cache/store.d.mts +1 -0
  15. package/dist/cli/cache/types.d.mts +1 -0
  16. package/dist/cli/commands/api/api-call.d.mts +19 -0
  17. package/dist/cli/commands/api/index.d.mts +3 -0
  18. package/dist/cli/commands/deploy/aigateway.d.mts +1 -0
  19. package/dist/cli/commands/deploy/application.d.mts +1 -0
  20. package/dist/cli/commands/deploy/auth.d.mts +1 -0
  21. package/dist/cli/commands/deploy/deploy.d.mts +24 -0
  22. package/dist/cli/commands/deploy/executor.d.mts +1 -0
  23. package/dist/cli/commands/deploy/function-registry-types.d.mts +12 -0
  24. package/dist/cli/commands/deploy/function-registry.d.mts +2 -0
  25. package/dist/cli/commands/deploy/idp.d.mts +1 -0
  26. package/dist/cli/commands/deploy/resolver.d.mts +1 -0
  27. package/dist/cli/commands/deploy/secret-manager.d.mts +1 -0
  28. package/dist/cli/commands/deploy/staticwebsite.d.mts +1 -0
  29. package/dist/cli/commands/deploy/tailordb/index.d.mts +1 -0
  30. package/dist/cli/commands/deploy/types.d.mts +1 -0
  31. package/dist/cli/commands/deploy/workflow.d.mts +1 -0
  32. package/dist/cli/commands/executor/get.d.mts +29 -0
  33. package/dist/cli/commands/executor/jobs.d.mts +123 -0
  34. package/dist/cli/commands/executor/list.d.mts +19 -0
  35. package/dist/cli/commands/executor/transform.d.mts +41 -0
  36. package/dist/cli/commands/executor/trigger.d.mts +41 -0
  37. package/dist/cli/commands/executor/webhook.d.mts +22 -0
  38. package/dist/cli/commands/function/get.d.mts +18 -0
  39. package/dist/cli/commands/function/list.d.mts +22 -0
  40. package/dist/cli/commands/function/transform.d.mts +11 -0
  41. package/dist/cli/commands/generate/options.d.mts +7 -0
  42. package/dist/cli/commands/generate/seed/bundler.d.mts +26 -0
  43. package/dist/cli/commands/generate/service.d.mts +12 -0
  44. package/dist/cli/commands/generate/types.d.mts +126 -0
  45. package/dist/cli/commands/machineuser/list.d.mts +27 -0
  46. package/dist/cli/commands/machineuser/token.d.mts +22 -0
  47. package/dist/cli/commands/oauth2client/get.d.mts +18 -0
  48. package/dist/cli/commands/oauth2client/list.d.mts +20 -0
  49. package/dist/cli/commands/oauth2client/transform.d.mts +20 -0
  50. package/dist/cli/commands/organization/folder/create.d.mts +18 -0
  51. package/dist/cli/commands/organization/folder/delete.d.mts +16 -0
  52. package/dist/cli/commands/organization/folder/get.d.mts +17 -0
  53. package/dist/cli/commands/organization/folder/list.d.mts +22 -0
  54. package/dist/cli/commands/organization/folder/update.d.mts +18 -0
  55. package/dist/cli/commands/organization/get.d.mts +16 -0
  56. package/dist/cli/commands/organization/list.d.mts +15 -0
  57. package/dist/cli/commands/organization/transform.d.mts +27 -0
  58. package/dist/cli/commands/organization/tree.d.mts +25 -0
  59. package/dist/cli/commands/organization/update.d.mts +17 -0
  60. package/dist/cli/commands/remove.d.mts +16 -0
  61. package/dist/cli/commands/show.d.mts +34 -0
  62. package/dist/cli/commands/tailordb/migrate/bundler.d.mts +28 -0
  63. package/dist/cli/commands/tailordb/migrate/config.d.mts +19 -0
  64. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +211 -0
  65. package/dist/cli/commands/tailordb/migrate/generate.d.mts +17 -0
  66. package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +202 -0
  67. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +113 -0
  68. package/dist/cli/commands/tailordb/migrate/types.d.mts +7 -0
  69. package/dist/cli/commands/tailordb/truncate.d.mts +19 -0
  70. package/dist/cli/commands/workflow/executions.d.mts +75 -0
  71. package/dist/cli/commands/workflow/get.d.mts +29 -0
  72. package/dist/cli/commands/workflow/list.d.mts +19 -0
  73. package/dist/cli/commands/workflow/resume.d.mts +23 -0
  74. package/dist/cli/commands/workflow/start.d.mts +63 -0
  75. package/dist/cli/commands/workflow/status.d.mts +5 -0
  76. package/dist/cli/commands/workflow/transform.d.mts +33 -0
  77. package/dist/cli/commands/workflow/wait.d.mts +16 -0
  78. package/dist/cli/commands/workflow/waiter.d.mts +23 -0
  79. package/dist/cli/commands/workspace/app/health.d.mts +18 -0
  80. package/dist/cli/commands/workspace/app/list.d.mts +22 -0
  81. package/dist/cli/commands/workspace/app/transform.d.mts +18 -0
  82. package/dist/cli/commands/workspace/create.d.mts +25 -0
  83. package/dist/cli/commands/workspace/delete.d.mts +15 -0
  84. package/dist/cli/commands/workspace/get.d.mts +17 -0
  85. package/dist/cli/commands/workspace/list.d.mts +17 -0
  86. package/dist/cli/commands/workspace/restore.d.mts +15 -0
  87. package/dist/cli/commands/workspace/transform.d.mts +16 -0
  88. package/dist/cli/commands/workspace/user/invite.d.mts +22 -0
  89. package/dist/cli/commands/workspace/user/list.d.mts +22 -0
  90. package/dist/cli/commands/workspace/user/remove.d.mts +17 -0
  91. package/dist/cli/commands/workspace/user/transform.d.mts +8 -0
  92. package/dist/cli/commands/workspace/user/update.d.mts +22 -0
  93. package/dist/cli/index.d.mts +1 -2
  94. package/dist/cli/index.mjs +801 -206
  95. package/dist/cli/index.mjs.map +1 -1
  96. package/dist/cli/lib.d.mts +80 -21402
  97. package/dist/cli/lib.mjs +8 -9
  98. package/dist/cli/lib.mjs.map +1 -1
  99. package/dist/cli/query/index.d.mts +35 -0
  100. package/dist/cli/services/application.d.mts +1 -0
  101. package/dist/cli/services/auth/service.d.mts +1 -0
  102. package/dist/cli/services/executor/service.d.mts +1 -0
  103. package/dist/cli/services/http-adapter/bundler.d.mts +1 -0
  104. package/dist/cli/services/http-adapter/service.d.mts +1 -0
  105. package/dist/cli/services/resolver/service.d.mts +1 -0
  106. package/dist/cli/services/tailordb/service.d.mts +1 -0
  107. package/dist/cli/services/workflow/bundler.d.mts +1 -0
  108. package/dist/cli/services/workflow/service.d.mts +1 -0
  109. package/dist/cli/shared/args.d.mts +13 -0
  110. package/dist/cli/shared/client.d.mts +1273 -0
  111. package/dist/cli/shared/config-loader.d.mts +44 -0
  112. package/dist/cli/shared/context.d.mts +28 -0
  113. package/dist/cli/shared/script-executor.d.mts +74 -0
  114. package/dist/cli/shared/seed-chunker.d.mts +46 -0
  115. package/dist/cli/shared/trigger-context.d.mts +1 -0
  116. package/dist/cli/shared/type-generator.d.mts +20 -0
  117. package/dist/cli/skills.mjs +1 -2
  118. package/dist/cli/skills.mjs.map +1 -1
  119. package/dist/completion/zsh-worker.zsh +124 -25
  120. package/dist/configure/config/index.d.mts +29 -0
  121. package/dist/configure/config/types.d.mts +103 -0
  122. package/dist/configure/index.d.mts +125 -7
  123. package/dist/configure/index.mjs +5 -6
  124. package/dist/configure/index.mjs.map +1 -1
  125. package/dist/configure/services/aigateway/index.d.mts +17 -0
  126. package/dist/configure/services/aigateway/types.d.mts +11 -0
  127. package/dist/configure/services/auth/index.d.mts +43 -0
  128. package/dist/{types-v_ErzNUF.d.mts → configure/services/auth/types.d.mts} +7 -172
  129. package/dist/configure/services/executor/executor.d.mts +82 -0
  130. package/dist/configure/services/executor/index.d.mts +8 -0
  131. package/dist/configure/services/executor/operation.d.mts +46 -0
  132. package/dist/configure/services/executor/trigger/event.d.mts +272 -0
  133. package/dist/configure/services/executor/trigger/index.d.mts +8 -0
  134. package/dist/configure/services/executor/trigger/schedule.d.mts +25 -0
  135. package/dist/configure/services/executor/trigger/webhook.d.mts +44 -0
  136. package/dist/configure/services/executor/types.d.mts +2 -0
  137. package/dist/configure/services/http-adapter/http-adapter.d.mts +125 -0
  138. package/dist/configure/services/http-adapter/index.d.mts +2 -0
  139. package/dist/configure/services/idp/index.d.mts +64 -0
  140. package/dist/configure/services/idp/permission.d.mts +80 -0
  141. package/dist/configure/services/idp/types.d.mts +20 -0
  142. package/dist/configure/services/index.d.mts +33 -0
  143. package/dist/configure/services/resolver/index.d.mts +5 -0
  144. package/dist/configure/services/resolver/resolver.d.mts +77 -0
  145. package/dist/configure/services/resolver/types.d.mts +6 -0
  146. package/dist/configure/services/secrets/index.d.mts +38 -0
  147. package/dist/configure/services/secrets/types.d.mts +11 -0
  148. package/dist/configure/services/staticwebsite/index.d.mts +19 -0
  149. package/dist/configure/services/staticwebsite/types.d.mts +11 -0
  150. package/dist/configure/services/tailordb/index.d.mts +4 -0
  151. package/dist/configure/services/tailordb/permission.d.mts +119 -0
  152. package/dist/configure/services/tailordb/schema.d.mts +451 -0
  153. package/dist/configure/services/tailordb/types.d.mts +149 -0
  154. package/dist/configure/services/workflow/index.d.mts +7 -0
  155. package/dist/configure/services/workflow/job.d.mts +81 -0
  156. package/dist/configure/services/workflow/test-env-key.d.mts +9 -0
  157. package/dist/configure/services/workflow/types.d.mts +2 -0
  158. package/dist/configure/services/workflow/wait-point.d.mts +73 -0
  159. package/dist/configure/services/workflow/workflow.d.mts +55 -0
  160. package/dist/configure/types/aigateway-name.d.mts +12 -0
  161. package/dist/configure/types/connection-name.d.mts +12 -0
  162. package/dist/configure/types/field.d.mts +9 -0
  163. package/dist/configure/types/field.types.d.mts +109 -0
  164. package/dist/configure/types/idp-name.d.mts +12 -0
  165. package/dist/configure/types/index.d.mts +4 -0
  166. package/dist/configure/types/machine-user.d.mts +12 -0
  167. package/dist/configure/types/type.d.mts +99 -0
  168. package/dist/configure/user.d.mts +7 -0
  169. package/dist/{context-CDQqIv4u.mjs → context-Bd266-ru.mjs} +2 -3
  170. package/dist/{context-CDQqIv4u.mjs.map → context-Bd266-ru.mjs.map} +1 -1
  171. package/dist/{crashreport-BwF8cHF0.mjs → crashreport-Dy4ZzSkd.mjs} +1 -2
  172. package/dist/{crashreport-BqyvFk-_.mjs → crashreport-ayWANEZ6.mjs} +4 -5
  173. package/dist/{crashreport-BqyvFk-_.mjs.map → crashreport-ayWANEZ6.mjs.map} +1 -1
  174. package/dist/{enum-constants-C7DaWeQo.mjs → enum-constants-j9QBF0cB.mjs} +1 -2
  175. package/dist/{enum-constants-C7DaWeQo.mjs.map → enum-constants-j9QBF0cB.mjs.map} +1 -1
  176. package/dist/{errors-EsY4XO6O.mjs → errors-Dtf2WPaW.mjs} +1 -2
  177. package/dist/{errors-EsY4XO6O.mjs.map → errors-Dtf2WPaW.mjs.map} +1 -1
  178. package/dist/{field-C4zdJLW5.mjs → field-DOsJCPFa.mjs} +1 -2
  179. package/dist/{field-C4zdJLW5.mjs.map → field-DOsJCPFa.mjs.map} +1 -1
  180. package/dist/{file-BkxupbYP.mjs → file-_oUZo76X.mjs} +2 -3
  181. package/dist/{file-BkxupbYP.mjs.map → file-_oUZo76X.mjs.map} +1 -1
  182. package/dist/{file-utils-BHPxPXmn.mjs → file-utils-DcyIPFQh.mjs} +2 -3
  183. package/dist/{file-utils-BHPxPXmn.mjs.map → file-utils-DcyIPFQh.mjs.map} +1 -1
  184. package/dist/{globals-ByrCoDip.mjs → globals-B8XX5TRB.mjs} +3 -3
  185. package/dist/globals-B8XX5TRB.mjs.map +1 -0
  186. package/dist/{iconv-D1zmPjvi.mjs → iconv-D2vi8G36.mjs} +2 -3
  187. package/dist/{iconv-D1zmPjvi.mjs.map → iconv-D2vi8G36.mjs.map} +1 -1
  188. package/dist/{idp-ynUfzwpz.mjs → idp-Bx25ZBdN.mjs} +2 -3
  189. package/dist/{idp-ynUfzwpz.mjs.map → idp-Bx25ZBdN.mjs.map} +1 -1
  190. package/dist/{interceptor-DOqRkCya.mjs → interceptor-D-q1rvRl.mjs} +1 -2
  191. package/dist/{interceptor-DOqRkCya.mjs.map → interceptor-D-q1rvRl.mjs.map} +1 -1
  192. package/dist/{job-BpsFXPbi.mjs → job-CtU73PGa.mjs} +3 -4
  193. package/dist/{job-BpsFXPbi.mjs.map → job-CtU73PGa.mjs.map} +1 -1
  194. package/dist/kysely/index.d.mts +2 -3
  195. package/dist/kysely/index.mjs +0 -1
  196. package/dist/kysely/index.mjs.map +1 -1
  197. package/dist/{kysely-type-CSoZxVKN.mjs → kysely-type-D-zflMGf.mjs} +2 -3
  198. package/dist/{kysely-type-CSoZxVKN.mjs.map → kysely-type-D-zflMGf.mjs.map} +1 -1
  199. package/dist/{logger-DKF-JsAK.mjs → logger-BEiZZ3qT.mjs} +1 -2
  200. package/dist/{logger-DKF-JsAK.mjs.map → logger-BEiZZ3qT.mjs.map} +1 -1
  201. package/dist/{mock-wf5qeZLi.mjs → mock-Ce1GCi4-.mjs} +60 -5
  202. package/dist/mock-Ce1GCi4-.mjs.map +1 -0
  203. package/dist/{multiline-Cf9ODpr1.mjs → multiline-sfHpTZZK.mjs} +1 -2
  204. package/dist/{multiline-Cf9ODpr1.mjs.map → multiline-sfHpTZZK.mjs.map} +1 -1
  205. package/dist/{package-json-DcQApfPQ.mjs → package-json-8b0O9TlX.mjs} +1 -2
  206. package/dist/{package-json-DcQApfPQ.mjs.map → package-json-8b0O9TlX.mjs.map} +1 -1
  207. package/dist/package-json-Cv2Z-TqQ.mjs +3 -0
  208. package/dist/parser/generator-config/schema.d.mts +1 -0
  209. package/dist/parser/service/auth/index.d.mts +1 -0
  210. package/dist/parser/service/auth/schema.d.mts +1 -0
  211. package/dist/parser/service/http-adapter/index.d.mts +1 -0
  212. package/dist/parser/service/http-adapter/schema.d.mts +1 -0
  213. package/dist/parser/service/idp/types.d.mts +4 -0
  214. package/dist/parser/service/tailordb/types.d.mts +126 -0
  215. package/dist/plugin/builtin/enum-constants/index.d.mts +16 -1
  216. package/dist/plugin/builtin/enum-constants/index.mjs +1 -2
  217. package/dist/plugin/builtin/file-utils/index.d.mts +16 -1
  218. package/dist/plugin/builtin/file-utils/index.mjs +1 -2
  219. package/dist/plugin/builtin/kysely-type/index.d.mts +16 -1
  220. package/dist/plugin/builtin/kysely-type/index.mjs +1 -2
  221. package/dist/plugin/builtin/seed/index.d.mts +45 -1
  222. package/dist/plugin/builtin/seed/index.mjs +1 -2
  223. package/dist/plugin/get-generated-type.d.mts +17 -0
  224. package/dist/plugin/index.d.mts +3 -118
  225. package/dist/plugin/index.mjs +0 -1
  226. package/dist/plugin/index.mjs.map +1 -1
  227. package/dist/plugin/manager.d.mts +1 -0
  228. package/dist/plugin/types.d.mts +245 -0
  229. package/dist/plugin/with-context.d.mts +102 -0
  230. package/dist/{registry-D0uB0OrK.mjs → registry-BozuxbPp.mjs} +1 -2
  231. package/dist/{registry-D0uB0OrK.mjs.map → registry-BozuxbPp.mjs.map} +1 -1
  232. package/dist/{repl-editor-DD5YP5mt.mjs → repl-editor-BmQOyZJd.mjs} +2 -3
  233. package/dist/{repl-editor-DD5YP5mt.mjs.map → repl-editor-BmQOyZJd.mjs.map} +1 -1
  234. package/dist/{rolldown-runtime-BkoGK1jX.mjs → rolldown-runtime-DXywRVcq.mjs} +0 -1
  235. package/dist/runtime/aigateway.d.mts +35 -0
  236. package/dist/runtime/aigateway.mjs +3 -0
  237. package/dist/runtime/authconnection.d.mts +31 -2
  238. package/dist/runtime/authconnection.mjs +1 -2
  239. package/dist/runtime/context.d.mts +67 -2
  240. package/dist/runtime/context.mjs +1 -2
  241. package/dist/runtime/file.d.mts +249 -2
  242. package/dist/runtime/file.mjs +1 -2
  243. package/dist/runtime/globals.d.mts +31 -20
  244. package/dist/runtime/globals.mjs +0 -1
  245. package/dist/runtime/iconv.d.mts +121 -2
  246. package/dist/runtime/iconv.mjs +1 -2
  247. package/dist/runtime/idp.d.mts +186 -2
  248. package/dist/runtime/idp.mjs +1 -2
  249. package/dist/runtime/index.d.mts +47 -9
  250. package/dist/runtime/index.mjs +9 -9
  251. package/dist/runtime/secretmanager.d.mts +54 -2
  252. package/dist/runtime/secretmanager.mjs +1 -2
  253. package/dist/{types-DTcZ2Yax.d.mts → runtime/types.d.mts} +1 -2
  254. package/dist/runtime/workflow.d.mts +107 -2
  255. package/dist/runtime/workflow.mjs +2 -3
  256. package/dist/{runtime-Cd375HD-.mjs → runtime-CEFC05qX.mjs} +81 -130
  257. package/dist/runtime-CEFC05qX.mjs.map +1 -0
  258. package/dist/{schema-C4fkpWV_.mjs → schema-DAdZSFOd.mjs} +3 -4
  259. package/dist/{schema-C4fkpWV_.mjs.map → schema-DAdZSFOd.mjs.map} +1 -1
  260. package/dist/{secret-file-eB3R3Fil.mjs → secret-file-VSVGy1V0.mjs} +2 -3
  261. package/dist/{secret-file-eB3R3Fil.mjs.map → secret-file-VSVGy1V0.mjs.map} +1 -1
  262. package/dist/{secretmanager-h3tBJV8f.mjs → secretmanager-BVxw3ih_.mjs} +2 -3
  263. package/dist/{secretmanager-h3tBJV8f.mjs.map → secretmanager-BVxw3ih_.mjs.map} +1 -1
  264. package/dist/seed/index.d.mts +1 -2
  265. package/dist/seed/index.mjs +0 -1
  266. package/dist/seed/index.mjs.map +1 -1
  267. package/dist/{seed-YAbtMy65.mjs → seed-DoMTMLcm.mjs} +3 -4
  268. package/dist/{seed-YAbtMy65.mjs.map → seed-DoMTMLcm.mjs.map} +1 -1
  269. package/dist/service-BXmdFwSH.mjs +3 -0
  270. package/dist/{service-DDWgZL_L2.mjs → service-DBFZQloR.mjs} +3 -4
  271. package/dist/service-DBFZQloR.mjs.map +1 -0
  272. package/dist/{service-B2Jd9CxS.mjs → service-DiEtAsvc.mjs} +3 -4
  273. package/dist/{service-B2Jd9CxS.mjs.map → service-DiEtAsvc.mjs.map} +1 -1
  274. package/dist/{service_pb-BsBR6yW5.mjs → service_pb-CIrhGwHk.mjs} +1 -2
  275. package/dist/{service_pb-BsBR6yW5.mjs.map → service_pb-CIrhGwHk.mjs.map} +1 -1
  276. package/dist/{service_pb-BJ9GsAnW.mjs → service_pb-DjwIn4jO.mjs} +1 -2
  277. package/dist/tailor-proto/src/tailor/v1/aigateway_pb.d.mts +204 -0
  278. package/dist/tailor-proto/src/tailor/v1/aigateway_resource_pb.d.mts +58 -0
  279. package/dist/tailor-proto/src/tailor/v1/application_pb.d.mts +316 -0
  280. package/dist/tailor-proto/src/tailor/v1/application_resource_pb.d.mts +161 -0
  281. package/dist/tailor-proto/src/tailor/v1/auth_pb.d.mts +2070 -0
  282. package/dist/tailor-proto/src/tailor/v1/auth_resource_pb.d.mts +861 -0
  283. package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +112 -0
  284. package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +69 -0
  285. package/dist/tailor-proto/src/tailor/v1/executor_pb.d.mts +439 -0
  286. package/dist/tailor-proto/src/tailor/v1/executor_resource_pb.d.mts +564 -0
  287. package/dist/tailor-proto/src/tailor/v1/function_pb.d.mts +145 -0
  288. package/dist/tailor-proto/src/tailor/v1/function_registry_pb.d.mts +451 -0
  289. package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +192 -0
  290. package/dist/tailor-proto/src/tailor/v1/http_adapter_resource_pb.d.mts +50 -0
  291. package/dist/tailor-proto/src/tailor/v1/idp_pb.d.mts +407 -0
  292. package/dist/tailor-proto/src/tailor/v1/idp_resource_pb.d.mts +430 -0
  293. package/dist/tailor-proto/src/tailor/v1/metadata_pb.d.mts +73 -0
  294. package/dist/tailor-proto/src/tailor/v1/metadata_resource_pb.d.mts +17 -0
  295. package/dist/tailor-proto/src/tailor/v1/meter_pb.d.mts +309 -0
  296. package/dist/tailor-proto/src/tailor/v1/pipeline_pb.d.mts +591 -0
  297. package/dist/tailor-proto/src/tailor/v1/pipeline_resource_pb.d.mts +330 -0
  298. package/dist/tailor-proto/src/tailor/v1/resource_pb.d.mts +113 -0
  299. package/dist/tailor-proto/src/tailor/v1/secret_manager_pb.d.mts +346 -0
  300. package/dist/tailor-proto/src/tailor/v1/secret_manager_resource_pb.d.mts +54 -0
  301. package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +3576 -0
  302. package/dist/tailor-proto/src/tailor/v1/stateflow_pb.d.mts +187 -0
  303. package/dist/tailor-proto/src/tailor/v1/stateflow_resource_pb.d.mts +29 -0
  304. package/dist/tailor-proto/src/tailor/v1/staticwebsite_pb.d.mts +429 -0
  305. package/dist/tailor-proto/src/tailor/v1/staticwebsite_resource_pb.d.mts +90 -0
  306. package/dist/tailor-proto/src/tailor/v1/tailordb_pb.d.mts +684 -0
  307. package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +797 -0
  308. package/dist/tailor-proto/src/tailor/v1/telemetryrouter_pb.d.mts +330 -0
  309. package/dist/tailor-proto/src/tailor/v1/telemetryrouter_resource_pb.d.mts +183 -0
  310. package/dist/tailor-proto/src/tailor/v1/workflow_pb.d.mts +842 -0
  311. package/dist/tailor-proto/src/tailor/v1/workflow_resource_pb.d.mts +276 -0
  312. package/dist/tailor-proto/src/tailor/v1/workspace_pb.d.mts +2015 -0
  313. package/dist/tailor-proto/src/tailor/v1/workspace_resource_pb.d.mts +483 -0
  314. package/dist/{telemetry-BQbbVo2t.mjs → telemetry-CdqJEzkj.mjs} +2 -3
  315. package/dist/{telemetry-BQbbVo2t.mjs.map → telemetry-CdqJEzkj.mjs.map} +1 -1
  316. package/dist/telemetry-ClwW5ohF.mjs +3 -0
  317. package/dist/types/aigateway.generated.d.mts +9 -0
  318. package/dist/types/app-config.generated.d.mts +4 -0
  319. package/dist/types/auth-connection.generated.d.mts +20 -0
  320. package/dist/types/auth.generated.d.mts +150 -0
  321. package/dist/types/executor.generated.d.mts +177 -0
  322. package/dist/types/generator-config.generated.d.mts +28 -0
  323. package/dist/types/helpers.d.mts +39 -0
  324. package/dist/types/http-adapter.generated.d.mts +17 -0
  325. package/dist/types/idp.generated.d.mts +584 -0
  326. package/dist/types/resolver.generated.d.mts +38 -0
  327. package/dist/types/secrets.generated.d.mts +14 -0
  328. package/dist/types/staticwebsite.generated.d.mts +10 -0
  329. package/dist/{types-rj8YJcEe.d.mts → types/tailordb.generated.d.mts} +2 -290
  330. package/dist/types/workflow.generated.d.mts +12 -0
  331. package/dist/types-qhFZ8y9B.mjs +4 -0
  332. package/dist/{types-D4QMmNWh.mjs → types-ywCGpzDZ.mjs} +2 -3
  333. package/dist/{types-D4QMmNWh.mjs.map → types-ywCGpzDZ.mjs.map} +1 -1
  334. package/dist/utils/test/index.d.mts +6 -89
  335. package/dist/utils/test/index.mjs +1 -2
  336. package/dist/utils/test/index.mjs.map +1 -1
  337. package/dist/utils/test/mock.d.mts +87 -0
  338. package/dist/vitest/environment.d.mts +1 -2
  339. package/dist/vitest/environment.mjs +1 -2
  340. package/dist/vitest/environment.mjs.map +1 -1
  341. package/dist/vitest/index.d.mts +4 -392
  342. package/dist/vitest/index.mjs +4 -5
  343. package/dist/vitest/index.mjs.map +1 -1
  344. package/dist/vitest/mock-kysely.d.mts +62 -0
  345. package/dist/vitest/mock.d.mts +364 -0
  346. package/dist/vitest/setup.d.mts +1 -2
  347. package/dist/vitest/setup.mjs +2 -3
  348. package/dist/vitest/setup.mjs.map +1 -1
  349. package/dist/{workflow-C8I7shjM.mjs → workflow-DSwnYPPP.mjs} +10 -4
  350. package/dist/workflow-DSwnYPPP.mjs.map +1 -0
  351. package/docs/cli/setup.md +129 -19
  352. package/docs/cli-reference.md +9 -4
  353. package/docs/runtime.md +5 -1
  354. package/docs/services/aigateway.md +14 -0
  355. package/docs/testing.md +48 -2
  356. package/package.json +6 -1
  357. package/dist/application-DmjIKQ-a.mjs.map +0 -1
  358. package/dist/application-a_ApFuPh.mjs +0 -4
  359. package/dist/authconnection-CqfaJfrC.d.mts +0 -32
  360. package/dist/context-CUBwSBq4.d.mts +0 -68
  361. package/dist/file-BzK8z3X-.d.mts +0 -250
  362. package/dist/globals-ByrCoDip.mjs.map +0 -1
  363. package/dist/iconv-kwrmd1U_.d.mts +0 -122
  364. package/dist/idp-BmYwCXnJ.d.mts +0 -187
  365. package/dist/index-BDBU7x-i.d.mts +0 -18
  366. package/dist/index-BSlv_YrG.d.mts +0 -46
  367. package/dist/index-BzGqTY5x.d.mts +0 -1315
  368. package/dist/index-D3_Au3wu.d.mts +0 -47
  369. package/dist/index-DtFRlrww.d.mts +0 -18
  370. package/dist/index-jeE3UvF2.d.mts +0 -18
  371. package/dist/mock-wf5qeZLi.mjs.map +0 -1
  372. package/dist/package-json-wzO6nV9O.mjs +0 -4
  373. package/dist/runtime-Cd375HD-.mjs.map +0 -1
  374. package/dist/secretmanager-CKLB3wAQ.d.mts +0 -55
  375. package/dist/service-CRaa4Joe.mjs +0 -4
  376. package/dist/service-DDWgZL_L2.mjs.map +0 -1
  377. package/dist/telemetry-w92bvGdC.mjs +0 -4
  378. package/dist/types-32lUMToj.mjs +0 -5
  379. package/dist/types-aqnqTCfO.d.mts +0 -602
  380. package/dist/workflow-C8I7shjM.mjs.map +0 -1
  381. package/dist/workflow-CMamswkK.d.mts +0 -96
  382. package/dist/workflow.generated-Da1zECDw.d.mts +0 -1348
@@ -1,17 +1,16 @@
1
1
  #!/usr/bin/env node
2
-
3
- import { C as CustomDomainStatus, R as FunctionExecution_Type, mt as AuthInvokerSchema, xt as PATScope } from "../service_pb-BsBR6yW5.mjs";
4
- import { t as assertDefined } from "../assert-CKfwrmCV.mjs";
5
- import { n as logger, r as styles } from "../logger-DKF-JsAK.mjs";
6
- import { $t as generateAllTypeManifestsFromSnapshot, A as listCommand$12, An as apiCommand, Bn as paginationArgs, C as listCommand$13, Cn as getNamespacesWithMigrations, Dn as PluginManager, Dt as listCommand$6, E as waitCommand, En as sdkNameLabelKey, F as generateCommand$1, Fn as configArg, G as removeCommand$1, Gt as deploy, H as extractOwnedNamespaces, Hn as workspaceArgs, Ht as formatKeyValueTable, I as generateMigrationScript, In as confirmationArgs, It as getCommand$6, K as updateCommand$3, Kt as executeScript, L as writeDbTypesFile, Ln as deploymentArgs, Mn as assertWritable, N as truncateCommand, Nn as defineAppCommand, O as resumeCommand, On as generateUserTypes, Pn as commonArgs, Pt as startCommand, Q as getCommand$5, Qt as compareSnapshotWithRemote, R as getConfiguredEditorCommand, Rn as isVerbose, Rt as executionsCommand, Sn as hasChanges, T as healthCommand, Tn as resourceTrn, Tt as triggerCommand, U as logBetaWarning, Ut as getCommand$1, V as showCommand, Vn as toPageDirection, Vt as functionExecutionStatusToString, X as listCommand$11, Xt as handleOptionalToRequiredError, Y as treeCommand, Yt as MIGRATION_LABEL_KEY, Zt as parseMigrationLabelNumber, _n as reconstructSnapshotFromMigrations, b as createCommand$4, c as listCommand$14, ct as createCommand$3, en as protoGqlPermission, et as updateCommand$2, f as restoreCommand, fn as getMigrationFilePath, ft as getCommand$3, g as getCommand$7, gn as loadDiff, gt as listCommand$8, hn as isValidMigrationNumber, ht as tokenCommand, i as updateCommand$4, it as getCommand$4, jt as jobsCommand, kn as prompt, ln as createSnapshotFromLocalTypes, m as listCommand$15, nt as listCommand$10, o as removeCommand, on as assertValidMigrationFiles, ot as deleteCommand$3, pn as getMigrationFiles, r as queryCommand, rn as INITIAL_SCHEMA_NUMBER, sn as compareLocalTypesWithSnapshot, t as isNativeTypeScriptRuntime, u as inviteCommand, un as getLatestMigrationNumber, ut as listCommand$9, v as deleteCommand$4, vn as formatMigrationNumber, vt as generate, wn as ensureConfigId, wt as webhookCommand, xn as formatMigrationDiff, xt as getCommand$2, yn as parseMigrationNumberArg, yt as listCommand$7, z as openInConfiguredEditor, zn as pagedLogArgs } from "../runtime-Cd375HD-.mjs";
7
- import { $ as initOperatorClient, A as loadAccessToken, B as saveUserTokens, C as hashContent$1, D as fetchLatestToken, E as deleteUserTokens, F as loadStoredUserTokens, H as closeConnectionPool, I as loadWorkspaceId, J as fetchUserInfo, K as fetchPaged, L as platformConfigFromProfile, M as loadConsoleBaseUrl, N as loadMachineUserName, O as hasAnyUserTokenEntry, Q as initOAuth2Client, R as readPlatformConfig, S as getDistDir, T as loadConfig, U as defaultPlatformBaseUrl, V as writePlatformConfig, W as fetchAll, _ as createLogLevelTreeshakeOptions, a as WorkflowJobSchema, c as INVOKER_EXPR, et as isDefaultPlatform, g as composeFunctionTreeshakeOptions, h as platformBundleDefinePlugin, i as resolveInlineSourcemap, k as hasUserTokenEntry, o as ResolverSchema, q as fetchPlatformMachineUserToken, t as defineApplication, v as resolveBundleLogLevel, z as resolveUserTokenKey } from "../application-DmjIKQ-a.mjs";
8
- import { n as ExecutorSchema } from "../service-B2Jd9CxS.mjs";
9
- import { t as multiline } from "../multiline-Cf9ODpr1.mjs";
10
- import { r as isPluginGeneratedType } from "../seed-YAbtMy65.mjs";
11
- import { t as readPackageJson } from "../package-json-DcQApfPQ.mjs";
12
- import { i as userAgent } from "../secret-file-eB3R3Fil.mjs";
13
- import { n as isCLIError } from "../errors-EsY4XO6O.mjs";
14
- import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-BqyvFk-_.mjs";
2
+ import { C as CustomDomainStatus, R as FunctionExecution_Type, mt as AuthInvokerSchema, xt as PATScope } from "../service_pb-CIrhGwHk.mjs";
3
+ import { t as assertDefined } from "../assert-DBxo8jPo.mjs";
4
+ import { n as logger, r as styles } from "../logger-BEiZZ3qT.mjs";
5
+ import { $t as generateAllTypeManifestsFromSnapshot, A as listCommand$12, An as apiCommand, Bn as paginationArgs, C as listCommand$13, Cn as getNamespacesWithMigrations, Dn as PluginManager, Dt as listCommand$6, E as waitCommand, En as sdkNameLabelKey, F as generateCommand$1, Fn as configArg, G as removeCommand$1, Gt as deploy, H as extractOwnedNamespaces, Hn as workspaceArgs, Ht as formatKeyValueTable, I as generateMigrationScript, In as confirmationArgs, It as getCommand$6, K as updateCommand$3, Kt as executeScript, L as writeDbTypesFile, Ln as deploymentArgs, Mn as assertWritable, N as truncateCommand, Nn as defineAppCommand, O as resumeCommand, On as generateUserTypes, Pn as commonArgs, Pt as startCommand, Q as getCommand$5, Qt as compareSnapshotWithRemote, R as getConfiguredEditorCommand, Rn as isVerbose, Rt as executionsCommand, Sn as hasChanges, T as healthCommand, Tn as resourceTrn, Tt as triggerCommand, U as logBetaWarning, Ut as getCommand$1, V as showCommand, Vn as toPageDirection, Vt as functionExecutionStatusToString, X as listCommand$11, Xt as handleOptionalToRequiredError, Y as treeCommand, Yt as MIGRATION_LABEL_KEY, Zt as parseMigrationLabelNumber, _n as reconstructSnapshotFromMigrations, b as createCommand$4, c as listCommand$14, ct as createCommand$3, en as protoGqlPermission, et as updateCommand$2, f as restoreCommand, fn as getMigrationFilePath, ft as getCommand$3, g as getCommand$7, gn as loadDiff, gt as listCommand$8, hn as isValidMigrationNumber, ht as tokenCommand, i as updateCommand$4, it as getCommand$4, jt as jobsCommand, kn as prompt, ln as createSnapshotFromLocalTypes, m as listCommand$15, nt as listCommand$10, o as removeCommand, on as assertValidMigrationFiles, ot as deleteCommand$3, pn as getMigrationFiles, r as queryCommand, rn as INITIAL_SCHEMA_NUMBER, sn as compareLocalTypesWithSnapshot, t as isNativeTypeScriptRuntime, u as inviteCommand, un as getLatestMigrationNumber, ut as listCommand$9, v as deleteCommand$4, vn as formatMigrationNumber, vt as generate, wn as ensureConfigId, wt as webhookCommand, xn as formatMigrationDiff, xt as getCommand$2, yn as parseMigrationNumberArg, yt as listCommand$7, z as openInConfiguredEditor, zn as pagedLogArgs } from "../runtime-CEFC05qX.mjs";
6
+ import { $ as initOperatorClient, A as loadAccessToken, B as saveUserTokens, C as hashContent$1, D as fetchLatestToken, E as deleteUserTokens, F as loadStoredUserTokens, H as closeConnectionPool, I as loadWorkspaceId, J as fetchUserInfo, K as fetchPaged, L as platformConfigFromProfile, M as loadConsoleBaseUrl, N as loadMachineUserName, O as hasAnyUserTokenEntry, Q as initOAuth2Client, R as readPlatformConfig, S as getDistDir, T as loadConfig, U as defaultPlatformBaseUrl, V as writePlatformConfig, W as fetchAll, _ as createLogLevelTreeshakeOptions, a as WorkflowJobSchema, c as INVOKER_EXPR, et as isDefaultPlatform, g as composeFunctionTreeshakeOptions, h as platformBundleDefinePlugin, i as resolveInlineSourcemap, k as hasUserTokenEntry, o as ResolverSchema, q as fetchPlatformMachineUserToken, t as defineApplication, v as resolveBundleLogLevel, z as resolveUserTokenKey } from "../application-Dxhdq_nu.mjs";
7
+ import { n as ExecutorSchema } from "../service-DiEtAsvc.mjs";
8
+ import { t as multiline } from "../multiline-sfHpTZZK.mjs";
9
+ import { r as isPluginGeneratedType } from "../seed-DoMTMLcm.mjs";
10
+ import { t as readPackageJson } from "../package-json-8b0O9TlX.mjs";
11
+ import { i as userAgent } from "../secret-file-VSVGy1V0.mjs";
12
+ import { n as isCLIError } from "../errors-Dtf2WPaW.mjs";
13
+ import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-ayWANEZ6.mjs";
15
14
  import { arg, defineCommand, runCommand, runMain } from "politty";
16
15
  import { withCompletionCommand } from "politty/completion";
17
16
  import { z } from "zod";
@@ -488,7 +487,7 @@ const deployCommand$1 = defineAppCommand({
488
487
  }).strict(),
489
488
  run: async (args) => {
490
489
  await assertWritable({ profile: args.profile });
491
- const { initTelemetry } = await import("../telemetry-w92bvGdC.mjs");
490
+ const { initTelemetry } = await import("../telemetry-ClwW5ohF.mjs");
492
491
  await initTelemetry();
493
492
  await deploy({
494
493
  workspaceId: args["workspace-id"],
@@ -1288,7 +1287,7 @@ async function detectFunctionType(options) {
1288
1287
  const rawInput = module.default.input;
1289
1288
  let inputSchema;
1290
1289
  if (rawInput) {
1291
- const { t } = await import("../types-32lUMToj.mjs");
1290
+ const { t } = await import("../types-qhFZ8y9B.mjs");
1292
1291
  inputSchema = t.object(rawInput);
1293
1292
  }
1294
1293
  return {
@@ -1673,7 +1672,7 @@ const generateCommand = defineAppCommand({
1673
1672
  })
1674
1673
  }).strict(),
1675
1674
  run: async (args) => {
1676
- const { initTelemetry } = await import("../telemetry-w92bvGdC.mjs");
1675
+ const { initTelemetry } = await import("../telemetry-ClwW5ohF.mjs");
1677
1676
  await initTelemetry();
1678
1677
  await generate({
1679
1678
  configPath: args.config,
@@ -2820,13 +2819,25 @@ function findTarget(lock, kind, workspaceName) {
2820
2819
  return lock?.targets.find((t) => t.kind === kind && t.workspaceName === workspaceName);
2821
2820
  }
2822
2821
 
2822
+ //#endregion
2823
+ //#region src/cli/commands/setup/action.yml
2824
+ var action_default = "# __HEADER__\nname: __WORKSPACE_NAME__\ndescription: >-\n Deploy __WORKSPACE_NAME__ to Tailor Platform.\n Checkout and dependency installation are expected to be handled by the caller.\n\ninputs:\n workspace-id:\n description: Tailor Platform workspace id\n required: true\n name:\n description: Workspace name (used in Slack notifications)\n required: true\n working-directory:\n description: App directory relative to the repository root\n required: false\n default: \".\"\n platform-client-id:\n description: Machine-user client id\n required: true\n platform-client-secret:\n description: Machine-user client secret\n required: true\n slack-token:\n description: Slack bot token\n required: false\n slack-channel-id:\n description: Slack channel id\n required: false\n user-mapping:\n description: JSON string mapping GitHub usernames to Slack user IDs\n required: false\n package-manager:\n description: Package manager used for dependency installation (pnpm, npm, yarn, bun)\n required: false\n default: pnpm\n\noutputs:\n app-url:\n description: Deployed application URL\n value: ${{ steps.tailor-apply.outputs.app-url }}\n\nruns:\n using: composite\n steps:\n # __STATIC_WEBSITE_BUILD_START__\n - id: build-site\n shell: bash\n run: |\n # Replace with your static website build command.\n # Example: pnpm --filter <package-name> run build:static\n # Note: this run: command is user-editable. Custom build commands are preserved across\n # reruns and are not treated as drift by setup check. Other edits to this file are flagged\n # as drift and will be overwritten if you re-run setup action with --force.\n true\n # __STATIC_WEBSITE_BUILD_END__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-id: ${{ inputs.workspace-id }}\n working-directory: ${{ inputs.working-directory }}\n package-manager: ${{ inputs.package-manager }}\n platform-client-id: ${{ inputs.platform-client-id }}\n platform-client-secret: ${{ inputs.platform-client-secret }}\n - id: tailor-notify\n if: failure() && inputs.slack-token != '' && inputs.slack-channel-id != ''\n uses: tailor-platform/actions/notify@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n provider: slack\n status: failure\n workspace-name: ${{ inputs.name }}\n slack-token: ${{ inputs.slack-token }}\n slack-channel-id: ${{ inputs.slack-channel-id }}\n user-mapping: ${{ inputs.user-mapping }}\n";
2825
+
2823
2826
  //#endregion
2824
2827
  //#region src/cli/commands/setup/branch.workflow.yml
2825
- var branch_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n # __PULL_REQUEST_END__\n push:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n workflow_dispatch:\n # __DISPATCH_INPUTS_START__\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n # __DISPATCH_INPUTS_END__\n\npermissions:\n contents: read\n\njobs:\n # __PLAN_JOB_START__\n tailor-plan:\n if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-plan\n # Fork PRs cannot read secrets; the checks above still run for them.\n if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork\n uses: tailor-platform/actions/plan@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n # __PLAN_JOB_END__\n # __ERD_PREVIEW_JOB_START__\n tailor-erd-preview-matrix:\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n contents: read\n outputs:\n namespaces: ${{ steps.tailor-erd-preview-matrix.outputs.namespaces }}\n base-app-dir: ${{ steps.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-erd-preview-matrix\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n WORKSPACE_NAME: \"__WORKSPACE_NAME__\"\n run: |\n set -euo pipefail\n node <<'NODE'\n const fs = require(\"node:fs\");\n\n const appDir = process.env.APP_DIR || \".\";\n const workspaceName = process.env.WORKSPACE_NAME;\n const output = process.env.GITHUB_OUTPUT;\n const dirPattern = /^[A-Za-z0-9._/-]+$/;\n const namespacePattern = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\n function lockTarget(file) {\n try {\n const lock = JSON.parse(fs.readFileSync(file, \"utf8\"));\n return lock.targets?.find(\n (candidate) =>\n candidate.kind === \"branch\" &&\n candidate.workspaceName === workspaceName,\n );\n } catch (error) {\n if (error && error.code === \"ENOENT\") return undefined;\n throw error;\n }\n }\n\n function namespacesFromTarget(target, label) {\n const namespaces =\n target?.inputs?.erdPreview === true && Array.isArray(target.inputs?.erdNamespaces)\n ? target.inputs.erdNamespaces\n : [];\n for (const namespace of namespaces) {\n if (typeof namespace !== \"string\" || !namespacePattern.test(namespace)) {\n throw new Error(`Invalid ERD namespace in ${label}: ${String(namespace)}`);\n }\n }\n return namespaces;\n }\n\n function normalizeDir(value, label) {\n const raw = typeof value === \"string\" && value.length > 0 ? value : appDir;\n const normalized =\n raw.replaceAll(\"\\\\\", \"/\").replace(/\\/{2,}/g, \"/\").replace(/^\\.\\//, \"\").replace(/\\/$/, \"\") ||\n \".\";\n if (\n !dirPattern.test(normalized) ||\n normalized.startsWith(\"/\") ||\n normalized.split(\"/\").includes(\"..\")\n ) {\n throw new Error(`Invalid ERD app dir in ${label}: ${raw}`);\n }\n return normalized;\n }\n\n const headTarget = lockTarget(\".github/tailor-sdk.lock\");\n const baseTarget = lockTarget(\".tailor-erd-base/.github/tailor-sdk.lock\");\n const baseAppDir = normalizeDir(baseTarget?.inputs?.dir, \"base setup lock\");\n const namespaces = [\n ...new Set([\n ...namespacesFromTarget(headTarget, \"head setup lock\"),\n ...namespacesFromTarget(baseTarget, \"base setup lock\"),\n ]),\n ].sort((a, b) => a.localeCompare(b));\n\n if (namespaces.length === 0) {\n console.error(\"No ERD preview namespaces found in the head or base setup lock.\");\n process.exit(1);\n }\n\n fs.appendFileSync(output, `namespaces=${JSON.stringify(namespaces)}\\n`);\n fs.appendFileSync(output, `base-app-dir=${baseAppDir}\\n`);\n NODE\n\n tailor-erd-preview:\n needs: tailor-erd-preview-matrix\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 15\n permissions:\n contents: read\n strategy:\n fail-fast: false\n matrix:\n namespace: ${{ fromJSON(needs.tailor-erd-preview-matrix.outputs.namespaces) }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-detect-base-package-manager\n shell: bash\n run: |\n set -euo pipefail\n cd .tailor-erd-base\n base_package_manager=\"$(\n if [ -f pnpm-lock.yaml ]; then\n echo pnpm\n elif [ -f yarn.lock ]; then\n echo yarn\n elif [ -f bun.lockb ] || [ -f bun.lock ]; then\n echo bun\n elif [ -f package-lock.json ]; then\n echo npm\n else\n echo \"__PACKAGE_MANAGER__\"\n fi\n )\"\n echo \"package-manager=$base_package_manager\" >> \"$GITHUB_OUTPUT\"\n if [ \"$base_package_manager\" = \"pnpm\" ]; then\n base_pnpm_version=\"$(node <<'NODE'\n const fs = require(\"node:fs\");\n\n const fallback = \"10\";\n\n function readManifest(file) {\n try {\n return JSON.parse(fs.readFileSync(file, \"utf8\"));\n } catch {\n return {};\n }\n }\n\n function pnpmVersion(manifest) {\n const packageManager = manifest.packageManager;\n if (typeof packageManager === \"string\" && packageManager.startsWith(\"pnpm@\")) {\n return packageManager.slice(\"pnpm@\".length).split(\"+\")[0];\n }\n\n const devPackageManager = manifest.devEngines?.packageManager;\n if (devPackageManager?.name === \"pnpm\" && devPackageManager.version) {\n return devPackageManager.version;\n }\n\n return undefined;\n }\n\n process.stdout.write(\n pnpmVersion(readManifest(\"package.json\")) ?? fallback,\n );\n NODE\n )\"\n echo \"pnpm-version=$base_pnpm_version\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-setup-base-pnpm\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'pnpm'\n uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9\n with:\n version: ${{ steps.tailor-detect-base-package-manager.outputs['pnpm-version'] }}\n package_json_file: .tailor-erd-base/package.json\n run_install: false\n - id: tailor-setup-base-node\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] != 'bun'\n uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\n with:\n node-version-file: .tailor-erd-base/package.json\n - id: tailor-setup-base-yarn\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'yarn'\n shell: bash\n working-directory: .tailor-erd-base\n run: |\n set -euo pipefail\n corepack enable\n - id: tailor-setup-base-bun\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'bun'\n uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0\n - id: tailor-install-base\n shell: bash\n working-directory: .tailor-erd-base\n env:\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n run: |\n set -euo pipefail\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) pnpm install --frozen-lockfile ;;\n npm) npm ci ;;\n yarn)\n if grep -q '^__metadata:' yarn.lock; then\n yarn install --immutable\n else\n yarn install --frozen-lockfile\n fi\n ;;\n bun) bun install --frozen-lockfile ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n - id: tailor-restore-head-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-build-erd-preview\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n BASE_APP_DIR: ${{ needs.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n NAMESPACE: ${{ matrix.namespace }}\n run: |\n set -euo pipefail\n\n run_tailor_sdk() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec tailor-sdk \"$@\" ;;\n npm) npx tailor-sdk \"$@\" ;;\n yarn) yarn tailor-sdk \"$@\" ;;\n bun) bunx tailor-sdk \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n run_head_node() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec node \"$@\" ;;\n npm) node \"$@\" ;;\n yarn) yarn node \"$@\" ;;\n bun) node \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n tailor_sdk_bin=\"$(\n run_head_node - <<'NODE'\n const path = require(\"node:path\");\n const { createRequire } = require(\"node:module\");\n const appDir = process.env.APP_DIR || \".\";\n const githubWorkspace = process.env.GITHUB_WORKSPACE;\n if (!githubWorkspace) {\n throw new Error(\"GITHUB_WORKSPACE is required.\");\n }\n\n let lastError;\n for (const manifestPath of [\n path.join(githubWorkspace, appDir, \"package.json\"),\n path.join(githubWorkspace, \"package.json\"),\n ]) {\n try {\n const requireFromManifest = createRequire(manifestPath);\n process.stdout.write(\n path.join(\n path.dirname(requireFromManifest.resolve(\"@tailor-platform/sdk/cli\")),\n \"index.mjs\",\n ),\n );\n process.exit(0);\n } catch (error) {\n lastError = error;\n }\n }\n throw lastError || new Error(\"Failed to resolve @tailor-platform/sdk/cli.\");\n NODE\n )\"\n\n run_head_tailor_sdk_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_SDK_BIN=\"$tailor_sdk_bin\" TAILOR_SDK_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$GITHUB_WORKSPACE\" || exit 1\n case \"__PACKAGE_MANAGER__\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n run_base_tailor_sdk_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_SDK_BIN=\"$tailor_sdk_bin\" TAILOR_SDK_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$command_cwd\" || exit 1\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n head_output=\"$RUNNER_TEMP/tailor-erd/head\"\n base_output=\"$RUNNER_TEMP/tailor-erd/base\"\n preview_output=\"$RUNNER_TEMP/tailor-erd/preview\"\n dts_output=\"$RUNNER_TEMP/tailor-erd/dts\"\n head_cli_runner=\"$RUNNER_TEMP/tailor-erd/run-head-tailor-sdk.mjs\"\n head_log=\"$RUNNER_TEMP/tailor-erd/head-$NAMESPACE.log\"\n base_log=\"$RUNNER_TEMP/tailor-erd/base-$NAMESPACE.log\"\n rm -rf \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n mkdir -p \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n cat > \"$head_cli_runner\" <<'NODE'\n import { pathToFileURL } from \"node:url\";\n\n const cliBin = process.env.TAILOR_SDK_BIN;\n const commandCwd = process.env.TAILOR_SDK_CWD;\n if (!cliBin || !commandCwd) {\n throw new Error(\"TAILOR_SDK_BIN and TAILOR_SDK_CWD are required.\");\n }\n\n process.chdir(commandCwd);\n process.argv = [process.argv[0], cliBin, ...process.argv.slice(2)];\n await import(pathToFileURL(cliBin).href);\n NODE\n\n head_config=\"$GITHUB_WORKSPACE/$APP_DIR/tailor.config.ts\"\n base_config=\"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR/tailor.config.ts\"\n head_html=\"$head_output/$NAMESPACE/dist/index.html\"\n base_html=\"$base_output/$NAMESPACE/dist/index.html\"\n preview_html=\"$preview_output/$NAMESPACE-viewer.html\"\n\n head_missing=\"false\"\n if [ ! -f \"$head_config\" ]; then\n echo \"Head ERD config not found; rendering base objects as removed.\"\n head_missing=\"true\"\n elif ! (\n cd \"$GITHUB_WORKSPACE/$APP_DIR\" || exit 1\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/head-$NAMESPACE.d.ts\" \\\n run_tailor_sdk tailordb erd export --config \"$head_config\" --namespace \"$NAMESPACE\" --output \"$head_output\"\n ) >\"$head_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$head_log\"; then\n cat \"$head_log\"\n echo \"Head ERD namespace '$NAMESPACE' not found; rendering base objects as removed.\"\n head_missing=\"true\"\n else\n cat \"$head_log\"\n exit 1\n fi\n elif [ -s \"$head_log\" ]; then\n cat \"$head_log\"\n fi\n\n base_missing=\"false\"\n if [ ! -f \"$base_config\" ]; then\n echo \"Base ERD config not found; rendering current objects as added.\"\n base_missing=\"true\"\n elif ! (\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/base-$NAMESPACE.d.ts\" \\\n run_base_tailor_sdk_bin \"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR\" tailordb erd export --config \"$base_config\" --namespace \"$NAMESPACE\" --output \"$base_output\"\n ) >\"$base_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$base_log\"; then\n cat \"$base_log\"\n echo \"Base ERD namespace '$NAMESPACE' not found; rendering current objects as added.\"\n base_missing=\"true\"\n else\n cat \"$base_log\"\n exit 1\n fi\n elif [ -s \"$base_log\" ]; then\n cat \"$base_log\"\n fi\n\n if [ \"$head_missing\" = \"true\" ] && [ \"$base_missing\" = \"true\" ]; then\n echo \"::error::ERD namespace '$NAMESPACE' was not found in head or base.\"\n exit 1\n fi\n if [ \"$head_missing\" = \"false\" ] && [ ! -f \"$head_html\" ]; then\n echo \"::error::Head ERD viewer was not generated at $head_html\"\n exit 1\n fi\n if [ \"$base_missing\" = \"false\" ] && [ ! -f \"$base_html\" ]; then\n echo \"::error::Base ERD viewer was not generated at $base_html\"\n exit 1\n fi\n\n diff_args=(--namespace \"$NAMESPACE\" --output \"$preview_html\")\n if [ \"$head_missing\" = \"false\" ]; then\n diff_args+=(--head-html \"$head_html\")\n fi\n if [ \"$base_missing\" = \"false\" ]; then\n diff_args+=(--base-html \"$base_html\")\n fi\n run_head_tailor_sdk_bin \"$GITHUB_WORKSPACE\" tailordb erd diff \"${diff_args[@]}\"\n - id: tailor-upload-erd-viewer\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n with:\n name: ${{ matrix.namespace }}-viewer.html\n path: ${{ runner.temp }}/tailor-erd/preview/${{ matrix.namespace }}-viewer.html\n if-no-files-found: error\n archive: false\n retention-days: 7\n\n # __ERD_PREVIEW_JOB_END__\n # __ERD_PREVIEW_COMMENT_JOB_START__\n tailor-erd-preview-comment:\n needs: tailor-erd-preview\n if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n actions: read\n pull-requests: write\n steps:\n - id: tailor-comment-erd-preview\n env:\n GH_TOKEN: ${{ github.token }}\n REPO: ${{ github.repository }}\n RUN_ID: ${{ github.run_id }}\n SERVER: ${{ github.server_url }}\n PR_NUMBER: ${{ github.event.pull_request.number }}\n BASE_REF: ${{ github.event.pull_request.base.ref }}\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\n MARKER: \"<!-- tailor-erd-preview: __WORKSPACE_NAME__ -->\"\n run: |\n set -euo pipefail\n\n current_head=$(gh api \"repos/$REPO/pulls/$PR_NUMBER\" --jq '.head.sha')\n if [ \"$current_head\" != \"$HEAD_SHA\" ]; then\n echo \"Skipping stale ERD preview comment for $HEAD_SHA; current head is $current_head.\"\n exit 0\n fi\n\n body=\"$MARKER\"$'\\n'\"### TailorDB ERD preview (__WORKSPACE_NAME__)\"$'\\n\\n'\n body+=\"Self-contained ERD viewer HTML artifacts for this pull request. Each viewer can switch between the current schema and a diff against base branch \\`$BASE_REF\\`.\"$'\\n'\n\n while IFS=$'\\t' read -r name id; do\n [ -z \"$name\" ] && continue\n if [[ \"$name\" != *-viewer.html ]]; then\n continue\n fi\n ns=${name%-viewer.html}\n label=\"$ns viewer\"\n body+=$'\\n'\"- **$label**: [$name]($SERVER/$REPO/actions/runs/$RUN_ID/artifacts/$id)\"\n done < <(gh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts\" --paginate \\\n --jq '.artifacts[] | select(.name | endswith(\"-viewer.html\")) | [.name, (.id|tostring)] | @tsv' | sort)\n\n existing=$(gh api \"repos/$REPO/issues/$PR_NUMBER/comments\" --paginate \\\n --jq 'map(select(.body | contains(env.MARKER))) | .[0].id // empty')\n if [ -n \"$existing\" ]; then\n gh api --method PATCH \"repos/$REPO/issues/comments/$existing\" -f body=\"$body\" >/dev/null\n else\n gh api --method POST \"repos/$REPO/issues/$PR_NUMBER/comments\" -f body=\"$body\" >/dev/null\n fi\n\n # __ERD_PREVIEW_COMMENT_JOB_END__\n tailor-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n environment: __ENVIRONMENT__\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n";
2828
+ var branch_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n # __PULL_REQUEST_END__\n push:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n workflow_dispatch:\n # __DISPATCH_INPUTS_START__\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n # __DISPATCH_INPUTS_END__\n\npermissions:\n contents: read\n\njobs:\n # __PLAN_JOB_START__\n tailor-plan:\n if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_START__\n - id: tailor-seed-validate\n uses: tailor-platform/actions/seed-validate@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_END__\n - id: tailor-drift-check\n uses: tailor-platform/actions/drift-check@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __MIGRATION_DRIFT_CHECK_START__\n - id: tailor-migration-drift-check\n uses: tailor-platform/actions/migration-drift-check@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __MIGRATION_DRIFT_CHECK_END__\n - id: tailor-plan\n # Fork PRs cannot read secrets; the checks above still run for them.\n if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork\n uses: tailor-platform/actions/plan@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n # __PLAN_JOB_END__\n # __ERD_PREVIEW_JOB_START__\n tailor-erd-preview-matrix:\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n contents: read\n outputs:\n namespaces: ${{ steps.tailor-erd-preview-matrix.outputs.namespaces }}\n base-app-dir: ${{ steps.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-erd-preview-matrix\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n WORKSPACE_NAME: \"__WORKSPACE_NAME__\"\n run: |\n set -euo pipefail\n node <<'NODE'\n const fs = require(\"node:fs\");\n\n const appDir = process.env.APP_DIR || \".\";\n const workspaceName = process.env.WORKSPACE_NAME;\n const output = process.env.GITHUB_OUTPUT;\n const dirPattern = /^[A-Za-z0-9._/-]+$/;\n const namespacePattern = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\n function lockTarget(file) {\n try {\n const lock = JSON.parse(fs.readFileSync(file, \"utf8\"));\n return lock.targets?.find(\n (candidate) =>\n candidate.kind === \"branch\" &&\n candidate.workspaceName === workspaceName,\n );\n } catch (error) {\n if (error && error.code === \"ENOENT\") return undefined;\n throw error;\n }\n }\n\n function namespacesFromTarget(target, label) {\n const namespaces =\n target?.inputs?.erdPreview === true && Array.isArray(target.inputs?.erdNamespaces)\n ? target.inputs.erdNamespaces\n : [];\n for (const namespace of namespaces) {\n if (typeof namespace !== \"string\" || !namespacePattern.test(namespace)) {\n throw new Error(`Invalid ERD namespace in ${label}: ${String(namespace)}`);\n }\n }\n return namespaces;\n }\n\n function normalizeDir(value, label) {\n const raw = typeof value === \"string\" && value.length > 0 ? value : appDir;\n const normalized =\n raw.replaceAll(\"\\\\\", \"/\").replace(/\\/{2,}/g, \"/\").replace(/^\\.\\//, \"\").replace(/\\/$/, \"\") ||\n \".\";\n if (\n !dirPattern.test(normalized) ||\n normalized.startsWith(\"/\") ||\n normalized.split(\"/\").includes(\"..\")\n ) {\n throw new Error(`Invalid ERD app dir in ${label}: ${raw}`);\n }\n return normalized;\n }\n\n const headTarget = lockTarget(\".github/tailor-sdk.lock\");\n const baseTarget = lockTarget(\".tailor-erd-base/.github/tailor-sdk.lock\");\n const baseAppDir = normalizeDir(baseTarget?.inputs?.dir, \"base setup lock\");\n const namespaces = [\n ...new Set([\n ...namespacesFromTarget(headTarget, \"head setup lock\"),\n ...namespacesFromTarget(baseTarget, \"base setup lock\"),\n ]),\n ].sort((a, b) => a.localeCompare(b));\n\n if (namespaces.length === 0) {\n console.error(\"No ERD preview namespaces found in the head or base setup lock.\");\n process.exit(1);\n }\n\n fs.appendFileSync(output, `namespaces=${JSON.stringify(namespaces)}\\n`);\n fs.appendFileSync(output, `base-app-dir=${baseAppDir}\\n`);\n NODE\n\n tailor-erd-preview:\n needs: tailor-erd-preview-matrix\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 15\n permissions:\n contents: read\n strategy:\n fail-fast: false\n matrix:\n namespace: ${{ fromJSON(needs.tailor-erd-preview-matrix.outputs.namespaces) }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-detect-base-package-manager\n shell: bash\n run: |\n set -euo pipefail\n cd .tailor-erd-base\n base_package_manager=\"$(\n if [ -f pnpm-lock.yaml ]; then\n echo pnpm\n elif [ -f yarn.lock ]; then\n echo yarn\n elif [ -f bun.lockb ] || [ -f bun.lock ]; then\n echo bun\n elif [ -f package-lock.json ]; then\n echo npm\n else\n echo \"__PACKAGE_MANAGER__\"\n fi\n )\"\n echo \"package-manager=$base_package_manager\" >> \"$GITHUB_OUTPUT\"\n if [ \"$base_package_manager\" = \"pnpm\" ]; then\n base_pnpm_version=\"$(node <<'NODE'\n const fs = require(\"node:fs\");\n\n const fallback = \"10\";\n\n function readManifest(file) {\n try {\n return JSON.parse(fs.readFileSync(file, \"utf8\"));\n } catch {\n return {};\n }\n }\n\n function pnpmVersion(manifest) {\n const packageManager = manifest.packageManager;\n if (typeof packageManager === \"string\" && packageManager.startsWith(\"pnpm@\")) {\n return packageManager.slice(\"pnpm@\".length).split(\"+\")[0];\n }\n\n const devPackageManager = manifest.devEngines?.packageManager;\n if (devPackageManager?.name === \"pnpm\" && devPackageManager.version) {\n return devPackageManager.version;\n }\n\n return undefined;\n }\n\n process.stdout.write(\n pnpmVersion(readManifest(\"package.json\")) ?? fallback,\n );\n NODE\n )\"\n echo \"pnpm-version=$base_pnpm_version\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-setup-base-pnpm\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'pnpm'\n uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9\n with:\n version: ${{ steps.tailor-detect-base-package-manager.outputs['pnpm-version'] }}\n package_json_file: .tailor-erd-base/package.json\n run_install: false\n - id: tailor-setup-base-node\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] != 'bun'\n uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\n with:\n node-version-file: .tailor-erd-base/package.json\n - id: tailor-setup-base-yarn\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'yarn'\n shell: bash\n working-directory: .tailor-erd-base\n run: |\n set -euo pipefail\n corepack enable\n - id: tailor-setup-base-bun\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'bun'\n uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0\n - id: tailor-install-base\n shell: bash\n working-directory: .tailor-erd-base\n env:\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n run: |\n set -euo pipefail\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) pnpm install --frozen-lockfile ;;\n npm) npm ci ;;\n yarn)\n if grep -q '^__metadata:' yarn.lock; then\n yarn install --immutable\n else\n yarn install --frozen-lockfile\n fi\n ;;\n bun) bun install --frozen-lockfile ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n - id: tailor-restore-head-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-build-erd-preview\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n BASE_APP_DIR: ${{ needs.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n NAMESPACE: ${{ matrix.namespace }}\n run: |\n set -euo pipefail\n\n run_tailor_sdk() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec tailor-sdk \"$@\" ;;\n npm) npx tailor-sdk \"$@\" ;;\n yarn) yarn tailor-sdk \"$@\" ;;\n bun) bunx tailor-sdk \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n run_head_node() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec node \"$@\" ;;\n npm) node \"$@\" ;;\n yarn) yarn node \"$@\" ;;\n bun) node \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n tailor_sdk_bin=\"$(\n run_head_node - <<'NODE'\n const path = require(\"node:path\");\n const { createRequire } = require(\"node:module\");\n const appDir = process.env.APP_DIR || \".\";\n const githubWorkspace = process.env.GITHUB_WORKSPACE;\n if (!githubWorkspace) {\n throw new Error(\"GITHUB_WORKSPACE is required.\");\n }\n\n let lastError;\n for (const manifestPath of [\n path.join(githubWorkspace, appDir, \"package.json\"),\n path.join(githubWorkspace, \"package.json\"),\n ]) {\n try {\n const requireFromManifest = createRequire(manifestPath);\n process.stdout.write(\n path.join(\n path.dirname(requireFromManifest.resolve(\"@tailor-platform/sdk/cli\")),\n \"index.mjs\",\n ),\n );\n process.exit(0);\n } catch (error) {\n lastError = error;\n }\n }\n throw lastError || new Error(\"Failed to resolve @tailor-platform/sdk/cli.\");\n NODE\n )\"\n\n run_head_tailor_sdk_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_SDK_BIN=\"$tailor_sdk_bin\" TAILOR_SDK_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$GITHUB_WORKSPACE\" || exit 1\n case \"__PACKAGE_MANAGER__\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n run_base_tailor_sdk_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_SDK_BIN=\"$tailor_sdk_bin\" TAILOR_SDK_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$command_cwd\" || exit 1\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n head_output=\"$RUNNER_TEMP/tailor-erd/head\"\n base_output=\"$RUNNER_TEMP/tailor-erd/base\"\n preview_output=\"$RUNNER_TEMP/tailor-erd/preview\"\n dts_output=\"$RUNNER_TEMP/tailor-erd/dts\"\n head_cli_runner=\"$RUNNER_TEMP/tailor-erd/run-head-tailor-sdk.mjs\"\n head_log=\"$RUNNER_TEMP/tailor-erd/head-$NAMESPACE.log\"\n base_log=\"$RUNNER_TEMP/tailor-erd/base-$NAMESPACE.log\"\n rm -rf \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n mkdir -p \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n cat > \"$head_cli_runner\" <<'NODE'\n import { pathToFileURL } from \"node:url\";\n\n const cliBin = process.env.TAILOR_SDK_BIN;\n const commandCwd = process.env.TAILOR_SDK_CWD;\n if (!cliBin || !commandCwd) {\n throw new Error(\"TAILOR_SDK_BIN and TAILOR_SDK_CWD are required.\");\n }\n\n process.chdir(commandCwd);\n process.argv = [process.argv[0], cliBin, ...process.argv.slice(2)];\n await import(pathToFileURL(cliBin).href);\n NODE\n\n head_config=\"$GITHUB_WORKSPACE/$APP_DIR/tailor.config.ts\"\n base_config=\"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR/tailor.config.ts\"\n head_html=\"$head_output/$NAMESPACE/dist/index.html\"\n base_html=\"$base_output/$NAMESPACE/dist/index.html\"\n preview_html=\"$preview_output/$NAMESPACE-viewer.html\"\n\n head_missing=\"false\"\n if [ ! -f \"$head_config\" ]; then\n echo \"Head ERD config not found; rendering base objects as removed.\"\n head_missing=\"true\"\n elif ! (\n cd \"$GITHUB_WORKSPACE/$APP_DIR\" || exit 1\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/head-$NAMESPACE.d.ts\" \\\n run_tailor_sdk tailordb erd export --config \"$head_config\" --namespace \"$NAMESPACE\" --output \"$head_output\"\n ) >\"$head_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$head_log\"; then\n cat \"$head_log\"\n echo \"Head ERD namespace '$NAMESPACE' not found; rendering base objects as removed.\"\n head_missing=\"true\"\n else\n cat \"$head_log\"\n exit 1\n fi\n elif [ -s \"$head_log\" ]; then\n cat \"$head_log\"\n fi\n\n base_missing=\"false\"\n if [ ! -f \"$base_config\" ]; then\n echo \"Base ERD config not found; rendering current objects as added.\"\n base_missing=\"true\"\n elif ! (\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/base-$NAMESPACE.d.ts\" \\\n run_base_tailor_sdk_bin \"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR\" tailordb erd export --config \"$base_config\" --namespace \"$NAMESPACE\" --output \"$base_output\"\n ) >\"$base_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$base_log\"; then\n cat \"$base_log\"\n echo \"Base ERD namespace '$NAMESPACE' not found; rendering current objects as added.\"\n base_missing=\"true\"\n else\n cat \"$base_log\"\n exit 1\n fi\n elif [ -s \"$base_log\" ]; then\n cat \"$base_log\"\n fi\n\n if [ \"$head_missing\" = \"true\" ] && [ \"$base_missing\" = \"true\" ]; then\n echo \"::error::ERD namespace '$NAMESPACE' was not found in head or base.\"\n exit 1\n fi\n if [ \"$head_missing\" = \"false\" ] && [ ! -f \"$head_html\" ]; then\n echo \"::error::Head ERD viewer was not generated at $head_html\"\n exit 1\n fi\n if [ \"$base_missing\" = \"false\" ] && [ ! -f \"$base_html\" ]; then\n echo \"::error::Base ERD viewer was not generated at $base_html\"\n exit 1\n fi\n\n diff_args=(--namespace \"$NAMESPACE\" --output \"$preview_html\")\n if [ \"$head_missing\" = \"false\" ]; then\n diff_args+=(--head-html \"$head_html\")\n fi\n if [ \"$base_missing\" = \"false\" ]; then\n diff_args+=(--base-html \"$base_html\")\n fi\n run_head_tailor_sdk_bin \"$GITHUB_WORKSPACE\" tailordb erd diff \"${diff_args[@]}\"\n - id: tailor-upload-erd-viewer\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n with:\n name: ${{ matrix.namespace }}-viewer.html\n path: ${{ runner.temp }}/tailor-erd/preview/${{ matrix.namespace }}-viewer.html\n if-no-files-found: error\n archive: false\n retention-days: 7\n\n # __ERD_PREVIEW_JOB_END__\n # __ERD_PREVIEW_COMMENT_JOB_START__\n tailor-erd-preview-comment:\n needs: tailor-erd-preview\n if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n actions: read\n pull-requests: write\n steps:\n - id: tailor-comment-erd-preview\n env:\n GH_TOKEN: ${{ github.token }}\n REPO: ${{ github.repository }}\n RUN_ID: ${{ github.run_id }}\n SERVER: ${{ github.server_url }}\n PR_NUMBER: ${{ github.event.pull_request.number }}\n BASE_REF: ${{ github.event.pull_request.base.ref }}\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\n MARKER: \"<!-- tailor-erd-preview: __WORKSPACE_NAME__ -->\"\n run: |\n set -euo pipefail\n\n current_head=$(gh api \"repos/$REPO/pulls/$PR_NUMBER\" --jq '.head.sha')\n if [ \"$current_head\" != \"$HEAD_SHA\" ]; then\n echo \"Skipping stale ERD preview comment for $HEAD_SHA; current head is $current_head.\"\n exit 0\n fi\n\n body=\"$MARKER\"$'\\n'\"### TailorDB ERD preview (__WORKSPACE_NAME__)\"$'\\n\\n'\n body+=\"Self-contained ERD viewer HTML artifacts for this pull request. Each viewer can switch between the current schema and a diff against base branch \\`$BASE_REF\\`.\"$'\\n'\n\n while IFS=$'\\t' read -r name id; do\n [ -z \"$name\" ] && continue\n if [[ \"$name\" != *-viewer.html ]]; then\n continue\n fi\n ns=${name%-viewer.html}\n label=\"$ns viewer\"\n body+=$'\\n'\"- **$label**: [$name]($SERVER/$REPO/actions/runs/$RUN_ID/artifacts/$id)\"\n done < <(gh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts\" --paginate \\\n --jq '.artifacts[] | select(.name | endswith(\"-viewer.html\")) | [.name, (.id|tostring)] | @tsv' | sort)\n\n existing=$(gh api \"repos/$REPO/issues/$PR_NUMBER/comments\" --paginate \\\n --jq 'map(select(.body | contains(env.MARKER))) | .[0].id // empty')\n if [ -n \"$existing\" ]; then\n gh api --method PATCH \"repos/$REPO/issues/comments/$existing\" -f body=\"$body\" >/dev/null\n else\n gh api --method POST \"repos/$REPO/issues/$PR_NUMBER/comments\" -f body=\"$body\" >/dev/null\n fi\n\n # __ERD_PREVIEW_COMMENT_JOB_END__\n tailor-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n environment: __ENVIRONMENT__\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n # __SEED_DATA_START__\n - id: seed-data\n if: false\n shell: bash\n # __WORKING_DIRECTORY__\n run: |\n # Uncomment and implement to seed data on deploy.\n # Run after apply so the schema is ready.\n # Example: node scripts/seed.mjs\n true\n # __SEED_DATA_END__\n - id: slack-prereq\n if: always()\n shell: bash\n env:\n SLACK_TOKEN: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n SLACK_CHANNEL_ID: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n run: |\n if [[ -n \"$SLACK_TOKEN\" && -n \"$SLACK_CHANNEL_ID\" ]]; then\n echo \"ready=true\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-notify\n if: always() && steps.slack-prereq.outputs.ready == 'true'\n uses: tailor-platform/actions/notify@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n provider: slack\n status: ${{ job.status }}\n workspace-name: __WORKSPACE_NAME__\n slack-token: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n slack-channel-id: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n # user-mapping: ${{ vars.TAILOR_SLACK_USER_MAPPING }}\n";
2829
+
2830
+ //#endregion
2831
+ //#region src/cli/commands/setup/coordinate.workflow.yml
2832
+ var coordinate_workflow_default = "# __HEADER__\nname: Tailor Coordinator (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PULL_REQUEST_END__\n push:\n # __PUSH_BRANCHES_START__\n branches: [\"__BRANCH__\"]\n # __PUSH_BRANCHES_END__\n # __PUSH_TAGS_START__\n tags: [__TAG_PATTERN__]\n # __PUSH_TAGS_END__\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n fetch-depth: 0\n - id: tailor-tag-guard\n uses: tailor-platform/actions/tag-guard@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n target-branch: __BRANCH__\n # __TAG_GUARD_JOB_END__\n\n tailor-plan:\n # __PLAN_NEEDS__\n # __PLAN_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 60\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: ./.github/actions/tailor-setup\n # __PER_APP_PLAN_STEPS__\n\n tailor-deploy:\n # __DEPLOY_NEEDS__\n # __DEPLOY_IF__\n # __DEPLOY_ENVIRONMENT__\n runs-on: ubuntu-latest\n timeout-minutes: 60\n permissions:\n contents: read\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: ./.github/actions/tailor-setup\n # __PER_APP_DEPLOY_STEPS__\n";
2833
+
2834
+ //#endregion
2835
+ //#region src/cli/commands/setup/preview.workflow.yml
2836
+ var preview_workflow_default = "# __HEADER__\nname: Tailor Preview (__WORKSPACE_NAME__)\n\non:\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PR_TYPES__\n # To deploy only for PRs that carry the `tailor:preview` label instead of all PRs,\n # re-run: tailor-sdk setup --require-preview-label\n # __PATHS__\n\npermissions:\n contents: read\n pull-requests: write\n\njobs:\n tailor-preview-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-preview-__WORKSPACE_NAME__-${{ github.event.pull_request.number }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-preview-deploy\n uses: tailor-platform/actions/preview-deploy@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-name-prefix: __WORKSPACE_NAME__\n region: \"__REGION__\"\n organization-id: ${{ vars.TAILOR_PLATFORM_ORGANIZATION_ID }}\n folder-id: ${{ vars.TAILOR_PLATFORM_FOLDER_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n - id: tailor-preview-comment\n if: always()\n uses: tailor-platform/actions/preview-comment@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-id: ${{ steps.tailor-preview-deploy.outputs.workspace-id }}\n workspace-name: ${{ steps.tailor-preview-deploy.outputs.workspace-name }}\n status: ${{ job.status == 'success' && 'success' || 'failure' }}\n app-url: ${{ steps.tailor-preview-deploy.outputs.app-url }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n\n tailor-preview-cleanup:\n # __CLEANUP_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 15\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-preview-cleanup\n uses: tailor-platform/actions/preview-cleanup@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-name-prefix: __WORKSPACE_NAME__\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n";
2826
2837
 
2827
2838
  //#endregion
2828
2839
  //#region src/cli/commands/setup/tag.workflow.yml
2829
- var tag_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n push:\n tags: [\"__TAG_PATTERN__\"]\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n fetch-depth: 0\n - id: tailor-tag-guard\n uses: tailor-platform/actions/tag-guard@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n target-branch: \"__BRANCH__\"\n # __TAG_GUARD_JOB_END__\n tailor-plan:\n # __PLAN_NEEDS__\n # __PLAN_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-plan\n uses: tailor-platform/actions/plan@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n\n tailor-deploy:\n needs: tailor-plan\n if: ${{ !(github.event_name == 'workflow_dispatch' && inputs['dry-run']) }}\n environment: __ENVIRONMENT__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n";
2840
+ var tag_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n push:\n tags: [\"__TAG_PATTERN__\"]\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n fetch-depth: 0\n - id: tailor-tag-guard\n uses: tailor-platform/actions/tag-guard@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n target-branch: \"__BRANCH__\"\n # __TAG_GUARD_JOB_END__\n tailor-plan:\n # __PLAN_NEEDS__\n # __PLAN_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_START__\n - id: tailor-seed-validate\n uses: tailor-platform/actions/seed-validate@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_END__\n - id: tailor-drift-check\n uses: tailor-platform/actions/drift-check@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __MIGRATION_DRIFT_CHECK_START__\n - id: tailor-migration-drift-check\n uses: tailor-platform/actions/migration-drift-check@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __MIGRATION_DRIFT_CHECK_END__\n - id: tailor-plan\n uses: tailor-platform/actions/plan@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n\n tailor-deploy:\n needs: tailor-plan\n if: ${{ !(github.event_name == 'workflow_dispatch' && inputs['dry-run']) }}\n environment: __ENVIRONMENT__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n # __SEED_DATA_START__\n - id: seed-data\n if: false\n shell: bash\n # __WORKING_DIRECTORY__\n run: |\n # Uncomment and implement to seed data on deploy.\n # Run after apply so the schema is ready.\n # Example: node scripts/seed.mjs\n true\n # __SEED_DATA_END__\n - id: slack-prereq\n if: always()\n shell: bash\n env:\n SLACK_TOKEN: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n SLACK_CHANNEL_ID: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n run: |\n if [[ -n \"$SLACK_TOKEN\" && -n \"$SLACK_CHANNEL_ID\" ]]; then\n echo \"ready=true\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-notify\n if: always() && steps.slack-prereq.outputs.ready == 'true'\n uses: tailor-platform/actions/notify@d203a37c87b7a69c6040611c33e9e50c1d564ed1 # v1.5.0\n with:\n provider: slack\n status: ${{ job.status }}\n workspace-name: __WORKSPACE_NAME__\n slack-token: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n slack-channel-id: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n # user-mapping: ${{ vars.TAILOR_SLACK_USER_MAPPING }}\n";
2830
2841
 
2831
2842
  //#endregion
2832
2843
  //#region src/cli/commands/setup/templates.ts
@@ -2870,29 +2881,39 @@ function applyCommon(content, params) {
2870
2881
  }
2871
2882
  /**
2872
2883
  * Render the branch-target deploy workflow.
2873
- *
2874
- * When `plan` is false the plan job, the pull_request trigger, and the
2875
- * workflow_dispatch dry-run input are all removed (a plan-less workflow has no
2876
- * meaningful dry-run), and the deploy job runs unconditionally.
2877
2884
  * @param params - Workspace and rendering configuration
2878
2885
  * @returns Rendered YAML and the list of managed job/step ids
2879
2886
  */
2880
2887
  function renderBranchWorkflow(params) {
2881
- const { branch, plan } = params;
2882
- const erdPreview = plan ? params.erdPreview : null;
2888
+ const { branch } = params;
2889
+ const seedValidate = params.seedValidate ?? false;
2890
+ const migrationDriftCheck = params.migrationDriftCheck ?? false;
2891
+ const erdPreview = params.erdPreview;
2883
2892
  let out = branch_workflow_default;
2884
- out = block(out, "PLAN_JOB", plan);
2893
+ out = block(out, "PLAN_JOB", true);
2885
2894
  out = block(out, "ERD_PREVIEW_JOB", erdPreview !== null);
2886
2895
  out = block(out, "ERD_PREVIEW_COMMENT_JOB", erdPreview !== null);
2887
- out = block(out, "PULL_REQUEST", plan);
2888
- out = block(out, "DISPATCH_INPUTS", plan);
2889
- out = line(out, "DEPLOY_IF", plan ? `if: >-\n github.event_name == 'push' ||\n (github.event_name == 'workflow_dispatch' && !inputs['dry-run'])` : void 0);
2896
+ out = block(out, "PULL_REQUEST", true);
2897
+ out = block(out, "DISPATCH_INPUTS", true);
2898
+ out = block(out, "SEED_VALIDATE", seedValidate);
2899
+ out = block(out, "MIGRATION_DRIFT_CHECK", migrationDriftCheck);
2900
+ out = block(out, "SEED_DATA", false);
2901
+ out = line(out, "DEPLOY_IF", `if: >-\n github.event_name == 'push' ||\n (github.event_name == 'workflow_dispatch' && !inputs['dry-run'])`);
2890
2902
  out = line(out, "PATHS", params.workingDirectory ? `paths: ["${params.workingDirectory}/**"]` : void 0);
2891
2903
  out = applyCommon(out, params).replaceAll("__BRANCH__", () => branch);
2892
- const generatedIds = [];
2893
- if (plan) generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-generate-check", "tailor-plan/tailor-plan");
2904
+ const generatedIds = [
2905
+ "tailor-plan",
2906
+ "tailor-plan/tailor-checkout",
2907
+ "tailor-plan/tailor-setup",
2908
+ "tailor-plan/tailor-install",
2909
+ "tailor-plan/tailor-generate-check"
2910
+ ];
2911
+ if (seedValidate) generatedIds.push("tailor-plan/tailor-seed-validate");
2912
+ generatedIds.push("tailor-plan/tailor-drift-check");
2913
+ if (migrationDriftCheck) generatedIds.push("tailor-plan/tailor-migration-drift-check");
2914
+ generatedIds.push("tailor-plan/tailor-plan");
2894
2915
  if (erdPreview) generatedIds.push("tailor-erd-preview-matrix", "tailor-erd-preview-matrix/tailor-checkout", "tailor-erd-preview-matrix/tailor-checkout-base", "tailor-erd-preview-matrix/tailor-erd-preview-matrix", "tailor-erd-preview", "tailor-erd-preview/tailor-checkout", "tailor-erd-preview/tailor-setup", "tailor-erd-preview/tailor-checkout-base", "tailor-erd-preview/tailor-detect-base-package-manager", "tailor-erd-preview/tailor-setup-base-pnpm", "tailor-erd-preview/tailor-setup-base-node", "tailor-erd-preview/tailor-setup-base-yarn", "tailor-erd-preview/tailor-setup-base-bun", "tailor-erd-preview/tailor-install-base", "tailor-erd-preview/tailor-restore-head-setup", "tailor-erd-preview/tailor-build-erd-preview", "tailor-erd-preview/tailor-upload-erd-viewer", "tailor-erd-preview-comment", "tailor-erd-preview-comment/tailor-comment-erd-preview");
2895
- generatedIds.push("tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup", "tailor-deploy/tailor-apply");
2916
+ generatedIds.push("tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup", "tailor-deploy/tailor-install", "tailor-deploy/tailor-apply", "tailor-deploy/tailor-notify");
2896
2917
  return {
2897
2918
  content: out,
2898
2919
  generatedIds
@@ -2909,139 +2930,208 @@ function renderBranchWorkflow(params) {
2909
2930
  function renderTagWorkflow(params) {
2910
2931
  const { tagPattern, branch } = params;
2911
2932
  const hasGuard = branch !== void 0;
2933
+ const seedValidate = params.seedValidate ?? false;
2934
+ const migrationDriftCheck = params.migrationDriftCheck ?? false;
2912
2935
  let out = tag_workflow_default;
2913
2936
  out = block(out, "TAG_GUARD_JOB", hasGuard);
2937
+ out = block(out, "SEED_VALIDATE", seedValidate);
2938
+ out = block(out, "MIGRATION_DRIFT_CHECK", migrationDriftCheck);
2939
+ out = block(out, "SEED_DATA", false);
2914
2940
  out = line(out, "PLAN_NEEDS", hasGuard ? "needs: tailor-tag-guard" : void 0);
2915
2941
  out = line(out, "PLAN_IF", hasGuard ? `if: >-\n github.event_name == 'workflow_dispatch' ||\n needs.tailor-tag-guard.outputs.on-branch == 'true'` : void 0);
2916
2942
  out = applyCommon(out, params).replaceAll("__TAG_PATTERN__", () => tagPattern);
2917
2943
  if (hasGuard) out = out.replaceAll("__BRANCH__", () => branch);
2918
2944
  const generatedIds = [];
2919
2945
  if (hasGuard) generatedIds.push("tailor-tag-guard", "tailor-tag-guard/tailor-checkout", "tailor-tag-guard/tailor-tag-guard");
2920
- generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-generate-check", "tailor-plan/tailor-plan", "tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup", "tailor-deploy/tailor-apply");
2946
+ generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-install", "tailor-plan/tailor-generate-check");
2947
+ if (seedValidate) generatedIds.push("tailor-plan/tailor-seed-validate");
2948
+ generatedIds.push("tailor-plan/tailor-drift-check");
2949
+ if (migrationDriftCheck) generatedIds.push("tailor-plan/tailor-migration-drift-check");
2950
+ generatedIds.push("tailor-plan/tailor-plan", "tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup", "tailor-deploy/tailor-install", "tailor-deploy/tailor-apply", "tailor-deploy/tailor-notify");
2921
2951
  return {
2922
2952
  content: out,
2923
2953
  generatedIds
2924
2954
  };
2925
2955
  }
2926
-
2927
- //#endregion
2928
- //#region src/cli/commands/setup/check.ts
2929
2956
  /**
2930
- * Compute drift findings for one target by comparing its recorded lock state
2931
- * against the current repository/config state.
2932
- * @param target - The lock target being audited
2933
- * @param state - Current repository/config state for this target
2934
- * @returns Drift findings (empty when the target is in sync)
2957
+ * Render the preview workflow (PR open/sync/close triggers).
2958
+ *
2959
+ * The deploy job runs on opened/synchronize/reopened events; the cleanup job
2960
+ * runs on the closed event. Both jobs run in the same workflow so PR number
2961
+ * context is always available.
2962
+ * @param params - Workspace and rendering configuration
2963
+ * @returns Rendered YAML and the list of managed job/step ids
2935
2964
  */
2936
- function findTargetDrift(target, state) {
2937
- const id = `${target.kind} ${target.workspaceName}`;
2938
- const findings = [];
2939
- if (!state.fileExists) findings.push({
2940
- target: id,
2941
- rule: "missing-file",
2942
- message: `${target.file} is missing or unreadable. Re-run setup to restore it.`
2943
- });
2944
- else if (state.currentHash !== null && state.currentHash !== target.contentHash) findings.push({
2945
- target: id,
2946
- rule: "hand-edit",
2947
- message: `${target.file} was edited by hand since it was generated. Re-run setup with --force to regenerate, or keep customizations in your own jobs/steps.`
2948
- });
2949
- if (target.templateVersion < state.templateVersion) findings.push({
2950
- target: id,
2951
- rule: "template-version",
2952
- message: `A newer workflow template is available (generated with v${String(target.templateVersion)}, current v${String(state.templateVersion)}). Re-run setup to update.`
2953
- });
2954
- if (!state.configExists) findings.push({
2955
- target: id,
2956
- rule: "config-dir",
2957
- message: `tailor.config.ts not found under "${target.inputs.dir}". The app directory may have moved; re-run setup with the correct --dir.`
2958
- });
2959
- if (target.kind === "branch" && target.inputs.branchAutoDetected !== false && state.defaultBranch !== null && target.inputs.branch !== null && target.inputs.branch !== state.defaultBranch) findings.push({
2960
- target: id,
2961
- rule: "default-branch",
2962
- message: `The workflow triggers on "${target.inputs.branch}" but the repository default branch is now "${state.defaultBranch}". If this is intentional, ignore this; otherwise re-run setup so the trigger matches the default branch.`
2963
- });
2964
- if (target.kind === "branch" && target.inputs.erdPreview && state.configExists) {
2965
- const recorded = [...target.inputs.erdNamespaces ?? []].toSorted((a, b) => a.localeCompare(b));
2966
- const current = state.erdNamespaces?.toSorted((a, b) => a.localeCompare(b)) ?? null;
2967
- if (current === null || recorded.length !== current.length || recorded.some((namespace, index) => namespace !== current[index])) findings.push({
2968
- target: id,
2969
- rule: "erd-namespaces",
2970
- message: "TailorDB namespaces for ERD preview changed. Re-run setup so the ERD preview matrix is regenerated."
2971
- });
2972
- }
2973
- return findings;
2974
- }
2975
- function detectDefaultBranchSafe(cwd, run) {
2976
- try {
2977
- return detectDefaultBranch(cwd, run);
2978
- } catch {
2979
- return null;
2980
- }
2981
- }
2982
- function escapesRoot$1(rel) {
2983
- return path.isAbsolute(rel) || rel === ".." || rel.startsWith(`..${path.sep}`) || rel.startsWith("../");
2965
+ function renderPreviewWorkflow(params) {
2966
+ const { branch } = params;
2967
+ const requirePreviewLabel = params.requirePreviewLabel ?? false;
2968
+ let out = preview_workflow_default;
2969
+ out = line(out, "PATHS", params.workingDirectory ? `paths: ["${params.workingDirectory}/**"]` : void 0);
2970
+ out = line(out, "PR_TYPES", requirePreviewLabel ? "types: [labeled, synchronize, reopened, closed]" : "types: [opened, synchronize, reopened, closed]");
2971
+ out = line(out, "DEPLOY_IF", requirePreviewLabel ? `if: >-\n contains(github.event.pull_request.labels.*.name, 'tailor:preview') &&\n github.event.action != 'closed' &&\n !github.event.pull_request.draft &&\n !github.event.pull_request.head.repo.fork` : `if: >-\n github.event.action != 'closed' &&\n !github.event.pull_request.draft &&\n !github.event.pull_request.head.repo.fork`);
2972
+ out = line(out, "CLEANUP_IF", `if: >-\n github.event.action == 'closed' &&\n !github.event.pull_request.head.repo.fork`);
2973
+ out = applyCommon(out, params).replaceAll("__BRANCH__", () => branch).replaceAll("__REGION__", () => params.region);
2974
+ return {
2975
+ content: out,
2976
+ generatedIds: [
2977
+ "tailor-preview-deploy",
2978
+ "tailor-preview-deploy/tailor-checkout",
2979
+ "tailor-preview-deploy/tailor-setup",
2980
+ "tailor-preview-deploy/tailor-install",
2981
+ "tailor-preview-deploy/tailor-generate-check",
2982
+ "tailor-preview-deploy/tailor-preview-deploy",
2983
+ "tailor-preview-deploy/tailor-preview-comment",
2984
+ "tailor-preview-cleanup",
2985
+ "tailor-preview-cleanup/tailor-checkout",
2986
+ "tailor-preview-cleanup/tailor-setup",
2987
+ "tailor-preview-cleanup/tailor-install",
2988
+ "tailor-preview-cleanup/tailor-preview-cleanup"
2989
+ ]
2990
+ };
2984
2991
  }
2985
- function resolveWithinRoot(outputDir, relPath) {
2986
- if (path.isAbsolute(relPath)) return null;
2987
- const abs = path.join(outputDir, relPath);
2988
- if (escapesRoot$1(path.relative(outputDir, abs))) return null;
2989
- try {
2990
- if (escapesRoot$1(path.relative(fs$1.realpathSync(outputDir), fs$1.realpathSync(abs)))) return null;
2991
- } catch {}
2992
- return abs;
2992
+ /**
2993
+ * Render the per-app composite action.
2994
+ *
2995
+ * The action contains only the deploy step and a failure notification.
2996
+ * Checkout, setup, and install are the caller's responsibility (coordinator workflow).
2997
+ * @param params - Workspace and rendering configuration
2998
+ * @returns Rendered YAML and the list of managed step ids
2999
+ */
3000
+ function renderActionWorkflow(params) {
3001
+ let out = action_default;
3002
+ out = line(out, "HEADER", HEADER);
3003
+ out = block(out, "STATIC_WEBSITE_BUILD", params.hasStaticWebsites ?? false);
3004
+ out = out.replaceAll("__WORKSPACE_NAME__", () => params.workspaceName);
3005
+ return {
3006
+ content: out,
3007
+ generatedIds: ["tailor-apply", "tailor-notify"]
3008
+ };
2993
3009
  }
2994
- function readHash(absFile) {
2995
- try {
2996
- return hashContent(fs$1.readFileSync(absFile, "utf-8"));
2997
- } catch {
2998
- return null;
3010
+ const ACTIONS_SHA = "d203a37c87b7a69c6040611c33e9e50c1d564ed1";
3011
+ /**
3012
+ * Render the coordinator workflow that orchestrates per-app composite actions.
3013
+ *
3014
+ * Generates per-app plan steps (generate-check, drift-check, plan) and per-app
3015
+ * deploy steps (calling each app's composite action at .github/actions/tailor-<name>).
3016
+ * Checkout, setup, and install are done once per job using the local tailor-setup action.
3017
+ * @param params - Coordinator and app configuration
3018
+ * @returns Rendered YAML and the list of managed job/step ids
3019
+ */
3020
+ function renderCoordinateWorkflow(params) {
3021
+ const { coordinatorName, kind, apps, environment, packageManager } = params;
3022
+ const isTag = kind === "tag";
3023
+ const branch = params.branch;
3024
+ const tagPattern = params.tagPattern ?? "v*";
3025
+ let out = coordinate_workflow_default;
3026
+ out = line(out, "HEADER", HEADER);
3027
+ out = block(out, "PULL_REQUEST", !isTag);
3028
+ out = block(out, "PUSH_BRANCHES", !isTag);
3029
+ out = block(out, "PUSH_TAGS", isTag);
3030
+ out = block(out, "TAG_GUARD_JOB", isTag && branch !== void 0);
3031
+ if (isTag && branch !== void 0) {
3032
+ out = line(out, "PLAN_NEEDS", "needs: tailor-tag-guard");
3033
+ out = line(out, "PLAN_IF", `if: >-\n github.event_name == 'workflow_dispatch' ||\n needs.tailor-tag-guard.outputs.on-branch == 'true'`);
3034
+ } else if (isTag) {
3035
+ out = line(out, "PLAN_NEEDS", void 0);
3036
+ out = line(out, "PLAN_IF", void 0);
3037
+ } else {
3038
+ out = line(out, "PLAN_NEEDS", void 0);
3039
+ out = line(out, "PLAN_IF", `if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])`);
2999
3040
  }
3000
- }
3001
- async function defaultLoadErdNamespaces$1(configPath) {
3002
- const { config } = await loadConfig(configPath);
3003
- return extractOwnedNamespaces(config);
3041
+ if (isTag) {
3042
+ out = line(out, "DEPLOY_NEEDS", "needs: tailor-plan");
3043
+ out = line(out, "DEPLOY_IF", `if: \${{ !(github.event_name == 'workflow_dispatch' && inputs['dry-run']) }}`);
3044
+ out = line(out, "DEPLOY_ENVIRONMENT", `environment: ${environment}`);
3045
+ } else {
3046
+ out = line(out, "DEPLOY_NEEDS", void 0);
3047
+ out = line(out, "DEPLOY_IF", `if: >-\n github.event_name == 'push' ||\n (github.event_name == 'workflow_dispatch' && !inputs['dry-run'])`);
3048
+ out = line(out, "DEPLOY_ENVIRONMENT", `environment: ${environment}`);
3049
+ }
3050
+ const driftCheckStep = [
3051
+ `- id: tailor-drift-check`,
3052
+ ` uses: tailor-platform/actions/drift-check@${ACTIONS_SHA} # v1.5.0`,
3053
+ ` with:`,
3054
+ ` package-manager: ${packageManager}`
3055
+ ].join("\n");
3056
+ const perAppPlanSteps = apps.flatMap((app) => {
3057
+ const wd = app.dir !== "." ? app.dir : void 0;
3058
+ const wdLine = wd ? `\n working-directory: ${wd}` : "";
3059
+ return [
3060
+ `- id: tailor-generate-check-${app.name}`,
3061
+ ` uses: tailor-platform/actions/generate-check@${ACTIONS_SHA} # v1.5.0`,
3062
+ ` with:`,
3063
+ ` package-manager: ${packageManager}${wdLine}`,
3064
+ `- id: tailor-plan-${app.name}`,
3065
+ ` if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork`,
3066
+ ` uses: tailor-platform/actions/plan@${ACTIONS_SHA} # v1.5.0`,
3067
+ ` with:`,
3068
+ ` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
3069
+ ` package-manager: ${packageManager}${wdLine}`,
3070
+ ` label: ${coordinatorName}/${app.name}`,
3071
+ ` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
3072
+ ` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
3073
+ ` github-token: \${{ secrets.GITHUB_TOKEN }}`
3074
+ ];
3075
+ }).join("\n");
3076
+ out = line(out, "PER_APP_PLAN_STEPS", `${driftCheckStep}\n${perAppPlanSteps}`);
3077
+ const deploySteps = apps.flatMap((app) => [
3078
+ `- id: tailor-deploy-${app.name}`,
3079
+ ` uses: ./.github/actions/tailor-${app.name}`,
3080
+ ` with:`,
3081
+ ` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
3082
+ ` name: ${app.name}`,
3083
+ ` working-directory: ${app.dir}`,
3084
+ ` package-manager: ${packageManager}`,
3085
+ ` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
3086
+ ` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
3087
+ ` slack-token: \${{ secrets.TAILOR_SLACK_BOT_TOKEN }}`,
3088
+ ` slack-channel-id: \${{ vars.TAILOR_SLACK_CHANNEL_ID }}`,
3089
+ ` # editable: api-url:`
3090
+ ]).join("\n");
3091
+ out = line(out, "PER_APP_DEPLOY_STEPS", deploySteps);
3092
+ out = out.replaceAll("__WORKSPACE_NAME__", () => coordinatorName);
3093
+ out = out.replaceAll("__ENVIRONMENT__", () => environment);
3094
+ if (branch !== void 0) out = out.replaceAll("__BRANCH__", () => branch);
3095
+ if (isTag) out = out.replaceAll("__TAG_PATTERN__", () => tagPattern);
3096
+ const generatedIds = [];
3097
+ if (isTag && branch !== void 0) generatedIds.push("tailor-tag-guard", "tailor-tag-guard/tailor-checkout", "tailor-tag-guard/tailor-tag-guard");
3098
+ generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-drift-check");
3099
+ for (const app of apps) generatedIds.push(`tailor-plan/tailor-generate-check-${app.name}`, `tailor-plan/tailor-plan-${app.name}`);
3100
+ generatedIds.push("tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup");
3101
+ for (const app of apps) generatedIds.push(`tailor-deploy/tailor-deploy-${app.name}`);
3102
+ return {
3103
+ content: out,
3104
+ generatedIds
3105
+ };
3004
3106
  }
3005
3107
  /**
3006
- * Audit the generated workflows for drift against the current config/repo
3007
- * state. Read-only: never writes files, the lock, or the config.
3108
+ * Render the content of the user-owned .github/actions/tailor-setup/action.yml local action.
3008
3109
  *
3009
- * Throws when drift is found (so it composes like the other `:check`
3010
- * commands). The workflow drift-check step layers advisory behaviour on top
3011
- * (per-rule ignore / continue-on-error); the CLI itself reports via exit code.
3012
- * @param options - Check options
3110
+ * This file is generated once by `setup coordinate` and never overwritten.
3111
+ * It wraps the setup and install actions with the package manager baked in.
3112
+ * @param params - Package manager configuration
3113
+ * @returns File content string
3013
3114
  */
3014
- async function checkGitHub(options) {
3015
- logBetaWarning("setup");
3016
- const { outputDir } = options;
3017
- const lock = readLock(outputDir);
3018
- if (!lock || lock.targets.length === 0) throw new Error("No managed workflows found (.github/tailor-sdk.lock is missing or empty). Run `tailor-sdk setup` first.");
3019
- const exists = options.configExistsAt ?? ((p) => fs$1.existsSync(p));
3020
- const defaultBranch = detectDefaultBranchSafe(outputDir, options.gitRunner);
3021
- const loadErdNamespaces = options.loadErdNamespaces ?? defaultLoadErdNamespaces$1;
3022
- const findings = [];
3023
- for (const target of lock.targets) {
3024
- const absFile = resolveWithinRoot(outputDir, target.file);
3025
- const currentHash = absFile === null ? null : readHash(absFile);
3026
- const configAbs = resolveWithinRoot(outputDir, path.join(target.inputs.dir, "tailor.config.ts"));
3027
- const configExists = configAbs !== null && exists(configAbs);
3028
- const erdNamespaces = target.kind === "branch" && target.inputs.erdPreview && configAbs !== null && configExists ? await loadErdNamespaces(configAbs) : null;
3029
- findings.push(...findTargetDrift(target, {
3030
- fileExists: currentHash !== null,
3031
- currentHash,
3032
- configExists,
3033
- defaultBranch,
3034
- templateVersion: 5,
3035
- erdNamespaces
3036
- }));
3037
- }
3038
- const count = lock.targets.length;
3039
- if (findings.length === 0) {
3040
- logger.success(`No drift detected across ${String(count)} target(s).`);
3041
- return;
3042
- }
3043
- for (const finding of findings) logger.warn(`[${finding.target}] ${finding.message} (ignore key: ${finding.rule})`);
3044
- throw new Error(`Detected ${String(findings.length)} drift finding(s) across ${String(count)} target(s). Re-run \`tailor-sdk setup\` to regenerate, or address each finding above.`);
3115
+ function renderTailorSetupAction(params) {
3116
+ const { packageManager } = params;
3117
+ return [
3118
+ `# This file is user-owned. Generated once by \`tailor-sdk setup coordinate\` and never overwritten.`,
3119
+ `# Customize freely: add pre/post steps, change install flags, etc.`,
3120
+ `name: Tailor Setup`,
3121
+ `description: Install dependencies and run tailor-sdk setup for composite action callers.`,
3122
+ `runs:`,
3123
+ ` using: composite`,
3124
+ ` steps:`,
3125
+ ` - uses: tailor-platform/actions/setup@${ACTIONS_SHA} # v1.5.0`,
3126
+ ` with:`,
3127
+ ` package-manager: ${packageManager}`,
3128
+ ` # Add custom steps here (e.g. private registry authentication)`,
3129
+ ` - uses: tailor-platform/actions/install@${ACTIONS_SHA} # v1.5.0`,
3130
+ ` with:`,
3131
+ ` package-manager: ${packageManager}`,
3132
+ ` # editable: install-command:`,
3133
+ ``
3134
+ ].join("\n");
3045
3135
  }
3046
3136
 
3047
3137
  //#endregion
@@ -3050,13 +3140,25 @@ async function defaultLoadConfigName(configPath) {
3050
3140
  const { config } = await loadConfig(configPath);
3051
3141
  return config.name;
3052
3142
  }
3053
- async function defaultLoadErdNamespaces(configPath) {
3143
+ async function defaultLoadErdNamespaces$1(configPath) {
3054
3144
  const { config } = await loadConfig(configPath);
3055
3145
  return extractOwnedNamespaces(config);
3056
3146
  }
3147
+ async function defaultLoadHasMigrations$1(configPath) {
3148
+ const { config } = await loadConfig(configPath);
3149
+ return getNamespacesWithMigrations(config, path.dirname(configPath)).length > 0;
3150
+ }
3151
+ async function defaultLoadHasSeeds$1(configPath) {
3152
+ const { plugins } = await loadConfig(configPath);
3153
+ return plugins.some((p) => p.id === "@tailor-platform/seed");
3154
+ }
3155
+ async function defaultLoadHasStaticWebsites$1(configPath) {
3156
+ const { config } = await loadConfig(configPath);
3157
+ return (config.staticWebsites?.length ?? 0) > 0;
3158
+ }
3057
3159
  const WORKSPACE_NAME_RE = /^[a-z0-9-]+$/;
3058
3160
  function validateWorkspaceName(name) {
3059
- if (name.length < 3 || name.length > 63 || !WORKSPACE_NAME_RE.test(name) || name.startsWith("-") || name.endsWith("-")) throw new Error(`Invalid workspace name "${name}". Names must be 3-63 characters of lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen. Pass a valid name with --workspace-name.`);
3161
+ if (name.length < 3 || name.length > 63 || !WORKSPACE_NAME_RE.test(name) || name.startsWith("-") || name.endsWith("-")) throw new Error(`Invalid workspace name "${name}". Names must be 3-63 characters of lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen. Pass a valid name with --name.`);
3060
3162
  }
3061
3163
  const BRANCH_RE = /^[A-Za-z0-9._/-]+$/;
3062
3164
  const TAG_PATTERN_RE = /^[A-Za-z0-9._/*?![\]-]+$/;
@@ -3070,6 +3172,10 @@ const ENVIRONMENT_RE = /^[A-Za-z0-9._/-]+$/;
3070
3172
  function validateEnvironment(environment) {
3071
3173
  if (!ENVIRONMENT_RE.test(environment)) throw new Error(`Invalid environment name "${environment}". Only letters, numbers, ".", "_", "/", and "-" are supported.`);
3072
3174
  }
3175
+ const REGION_RE = /^[A-Za-z0-9._-]+$/;
3176
+ function validateRegion(region) {
3177
+ if (!REGION_RE.test(region)) throw new Error(`Invalid region "${region}". Only letters, numbers, ".", "_", and "-" are supported.`);
3178
+ }
3073
3179
  const DIR_RE = /^[A-Za-z0-9._/-]+$/;
3074
3180
  function validateDir(dir) {
3075
3181
  if (!DIR_RE.test(dir)) throw new Error(`Invalid --dir "${dir}". Only letters, numbers, ".", "_", "/", and "-" are supported.`);
@@ -3078,7 +3184,7 @@ const ERD_NAMESPACE_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
3078
3184
  function validateErdNamespaces(namespaces) {
3079
3185
  for (const namespace of namespaces) if (!ERD_NAMESPACE_RE.test(namespace)) throw new Error(`TailorDB namespace "${namespace}" cannot be used in --erd-preview. Only letters, numbers, '.', '_', and '-' are supported, and the name must start with a letter or number.`);
3080
3186
  }
3081
- function escapesRoot(rel) {
3187
+ function escapesRoot$1(rel) {
3082
3188
  return rel === ".." || rel.startsWith(`..${path.sep}`) || rel.startsWith("../") || path.isAbsolute(rel);
3083
3189
  }
3084
3190
  /**
@@ -3094,11 +3200,11 @@ function escapesRoot(rel) {
3094
3200
  function resolveConfigPath(outputDir, dir) {
3095
3201
  const appDir = path.resolve(outputDir, dir);
3096
3202
  const rel = path.relative(outputDir, appDir);
3097
- if (path.isAbsolute(dir) || escapesRoot(rel)) throw new Error(`--dir must be a relative path inside the repository (got "${dir}").`);
3203
+ if (path.isAbsolute(dir) || escapesRoot$1(rel)) throw new Error(`--dir must be a relative path inside the repository (got "${dir}").`);
3098
3204
  if (fs$1.existsSync(appDir)) {
3099
3205
  const realAppDir = path.normalize(fs$1.realpathSync(appDir));
3100
3206
  const realOutputDir = path.normalize(fs$1.realpathSync(outputDir));
3101
- if (escapesRoot(path.relative(realOutputDir, realAppDir))) throw new Error(`--dir must resolve to a directory inside the repository (got "${dir}", which links outside it).`);
3207
+ if (escapesRoot$1(path.relative(realOutputDir, realAppDir))) throw new Error(`--dir must resolve to a directory inside the repository (got "${dir}", which links outside it).`);
3102
3208
  }
3103
3209
  const configPath = path.join(appDir, "tailor.config.ts");
3104
3210
  if (!fs$1.existsSync(configPath)) throw new Error(`tailor.config.ts not found at ${configPath}. Run this from your SDK project root, or pass the app directory with --dir.`);
@@ -3110,17 +3216,15 @@ function resolveConfigPath(outputDir, dir) {
3110
3216
  * @returns Resolved target metadata and rendered content
3111
3217
  */
3112
3218
  async function resolve$1(options) {
3113
- if (options.tag && !options.plan) throw new Error("--no-plan cannot be combined with --tag (tag targets always run plan before deploy). Drop --no-plan or use a branch target.");
3114
- if (options.erdPreview && (options.tag || !options.plan)) throw new Error("--erd-preview requires a branch target with plan enabled.");
3115
3219
  const dir = options.dir.replaceAll("\\", "/").replace(/\/{2,}/g, "/").replace(/^\.\//, "").replace(/\/$/, "") || ".";
3116
3220
  validateDir(dir);
3117
3221
  const workingDirectory = dir !== "." ? dir : void 0;
3118
3222
  const configPath = resolveConfigPath(options.outputDir, dir);
3119
3223
  const loadName = options.loadConfigName ?? defaultLoadConfigName;
3120
3224
  const workspaceName = options.workspaceName ?? await loadName(configPath);
3121
- if (!workspaceName) throw new Error("Could not determine the workspace name. Pass --workspace-name, or set 'name' in tailor.config.ts.");
3225
+ if (!workspaceName) throw new Error("Could not determine the workspace name. Pass --name, or set 'name' in tailor.config.ts.");
3122
3226
  validateWorkspaceName(workspaceName);
3123
- const kind = options.tag ? "tag" : "branch";
3227
+ const { kind } = options;
3124
3228
  const packageManager = detectPackageManager(options.outputDir);
3125
3229
  const environment = options.environment ?? workspaceName;
3126
3230
  validateEnvironment(environment);
@@ -3129,47 +3233,85 @@ async function resolve$1(options) {
3129
3233
  let branchAutoDetected = false;
3130
3234
  let render;
3131
3235
  let erdNamespaces = [];
3132
- if (options.erdPreview) {
3133
- erdNamespaces = await (options.loadErdNamespaces ?? defaultLoadErdNamespaces)(configPath);
3134
- if (erdNamespaces.length === 0) throw new Error("No TailorDB namespaces found for --erd-preview. Define owned db namespaces in tailor.config.ts.");
3135
- validateErdNamespaces(erdNamespaces);
3136
- }
3236
+ let hasMigrations = false;
3237
+ let hasSeeds = false;
3238
+ let hasStaticWebsites = false;
3239
+ const loadHasMigrations = options.loadHasMigrations ?? defaultLoadHasMigrations$1;
3240
+ const loadHasSeeds = options.loadHasSeeds ?? defaultLoadHasSeeds$1;
3241
+ const loadHasStaticWebsites = options.loadHasStaticWebsites ?? defaultLoadHasStaticWebsites$1;
3137
3242
  if (kind === "branch") {
3243
+ if (options.erdPreview) {
3244
+ erdNamespaces = await (options.loadErdNamespaces ?? defaultLoadErdNamespaces$1)(configPath);
3245
+ if (erdNamespaces.length === 0) throw new Error("No TailorDB namespaces found for --erd-preview. Define owned db namespaces in tailor.config.ts.");
3246
+ validateErdNamespaces(erdNamespaces);
3247
+ }
3138
3248
  branchAutoDetected = options.branch === void 0;
3139
3249
  branch = options.branch ?? detectDefaultBranch(options.outputDir, options.gitRunner);
3140
3250
  validateBranch(branch);
3251
+ hasMigrations = await loadHasMigrations(configPath);
3252
+ hasSeeds = await loadHasSeeds(configPath);
3141
3253
  render = renderBranchWorkflow({
3142
3254
  workspaceName,
3143
3255
  branch,
3144
3256
  workingDirectory,
3145
3257
  environment,
3146
3258
  packageManager,
3147
- plan: options.plan,
3148
- erdPreview: options.erdPreview ? { namespaces: erdNamespaces } : null
3259
+ erdPreview: options.erdPreview ? { namespaces: erdNamespaces } : null,
3260
+ migrationDriftCheck: hasMigrations,
3261
+ seedValidate: hasSeeds
3149
3262
  });
3150
- } else {
3263
+ } else if (kind === "tag") {
3151
3264
  branch = options.branch ?? null;
3152
3265
  if (branch !== null) validateBranch(branch);
3266
+ hasMigrations = await loadHasMigrations(configPath);
3267
+ hasSeeds = await loadHasSeeds(configPath);
3153
3268
  render = renderTagWorkflow({
3154
3269
  workspaceName,
3155
3270
  tagPattern: options.tagPattern,
3156
3271
  branch: options.branch,
3157
3272
  workingDirectory,
3158
3273
  environment,
3159
- packageManager
3274
+ packageManager,
3275
+ migrationDriftCheck: hasMigrations,
3276
+ seedValidate: hasSeeds
3277
+ });
3278
+ } else if (kind === "preview") {
3279
+ branchAutoDetected = options.branch === void 0;
3280
+ branch = options.branch ?? detectDefaultBranch(options.outputDir, options.gitRunner);
3281
+ validateBranch(branch);
3282
+ validateRegion(options.region);
3283
+ render = renderPreviewWorkflow({
3284
+ workspaceName,
3285
+ branch,
3286
+ workingDirectory,
3287
+ environment,
3288
+ packageManager,
3289
+ region: options.region,
3290
+ requirePreviewLabel: options.requirePreviewLabel ?? false
3291
+ });
3292
+ } else {
3293
+ hasStaticWebsites = await loadHasStaticWebsites(configPath);
3294
+ render = renderActionWorkflow({
3295
+ workspaceName,
3296
+ workingDirectory,
3297
+ hasStaticWebsites
3160
3298
  });
3161
3299
  }
3162
- const file = `.github/workflows/tailor-${workspaceName}.yml`;
3300
+ const file = kind === "action" ? `.github/actions/tailor-${workspaceName}/action.yml` : `.github/workflows/tailor-${workspaceName}${kind === "tag" ? "-tag" : kind === "preview" ? "-preview" : ""}.yml`;
3163
3301
  const inputs = {
3164
- branch,
3165
- branchAutoDetected: kind === "branch" ? branchAutoDetected : void 0,
3302
+ branch: kind === "action" ? null : branch,
3303
+ branchAutoDetected: kind === "branch" || kind === "preview" ? branchAutoDetected : void 0,
3166
3304
  tagPattern: kind === "tag" ? options.tagPattern : null,
3167
3305
  environment,
3168
3306
  dir,
3169
3307
  packageManager,
3170
- plan: kind === "branch" ? options.plan : true,
3308
+ region: kind === "preview" ? options.region : void 0,
3309
+ requirePreviewLabel: kind === "preview" ? options.requirePreviewLabel ?? false : void 0,
3171
3310
  erdPreview: kind === "branch" ? options.erdPreview : false,
3172
- erdNamespaces: kind === "branch" && options.erdPreview ? erdNamespaces : void 0
3311
+ erdNamespaces: kind === "branch" && options.erdPreview ? erdNamespaces : void 0,
3312
+ migrationDriftCheck: kind === "branch" || kind === "tag" ? hasMigrations : void 0,
3313
+ seedValidate: kind === "branch" || kind === "tag" ? hasSeeds : void 0,
3314
+ hasStaticWebsites: kind === "action" ? hasStaticWebsites : void 0
3173
3315
  };
3174
3316
  return {
3175
3317
  kind,
@@ -3184,6 +3326,40 @@ async function resolve$1(options) {
3184
3326
  configPath
3185
3327
  };
3186
3328
  }
3329
+ const BUILD_SITE_RUN_RE = /([ \t]*- id: build-site\n[ \t]+shell: bash\n[ \t]+run: \|)([\s\S]*?)(\n[ \t]*- |\n*$)/;
3330
+ /**
3331
+ * Strip the mutable run: body of the build-site step before hashing.
3332
+ * When hasStaticWebsites is true the step is present; users are expected to
3333
+ * replace the placeholder command with their actual build command. We only
3334
+ * hash the structural parts (step id, shell declaration, run: key) so that
3335
+ * editing the build command does not look like unintended drift.
3336
+ * When hasStaticWebsites is false the step is absent, so the regex is a no-op.
3337
+ * @param content - Raw action workflow content
3338
+ * @returns Normalised content with the build-site run body replaced by a placeholder
3339
+ */
3340
+ function normalizeActionContent(content) {
3341
+ return content.replace(BUILD_SITE_RUN_RE, (_, header, _body, tail) => `${header}\n true${tail}`);
3342
+ }
3343
+ /**
3344
+ * Extract the run: body of the build-site step from action content.
3345
+ * Returns null when the step is absent (hasStaticWebsites is false).
3346
+ * @param content - Action workflow content
3347
+ * @returns The captured run body string (includes leading newline), or null
3348
+ */
3349
+ function extractBuildSiteRunBody(content) {
3350
+ const m = BUILD_SITE_RUN_RE.exec(content);
3351
+ return m ? m[2] ?? null : null;
3352
+ }
3353
+ /**
3354
+ * Replace the run: body of the build-site step in action content.
3355
+ * No-op when the step is absent.
3356
+ * @param content - Target action workflow content
3357
+ * @param body - Run body to inject (includes leading newline and indentation)
3358
+ * @returns Content with the run body replaced
3359
+ */
3360
+ function injectBuildSiteRunBody(content, body) {
3361
+ return content.replace(BUILD_SITE_RUN_RE, (_, header, _placeholder, tail) => `${header}${body}${tail}`);
3362
+ }
3187
3363
  /**
3188
3364
  * Decide how to reconcile a target with the on-disk file and lock state.
3189
3365
  * @param obj - Decision inputs
@@ -3191,10 +3367,11 @@ async function resolve$1(options) {
3191
3367
  * @param obj.fileExists - Whether the workflow file is present on disk
3192
3368
  * @param obj.currentContent - On-disk content when present
3193
3369
  * @param obj.force - Whether --force was passed
3370
+ * @param obj.normalize - Optional content normaliser applied before hashing
3194
3371
  * @returns The reconciliation action
3195
3372
  */
3196
3373
  function decideAction(obj) {
3197
- const { existing, fileExists, currentContent, force } = obj;
3374
+ const { existing, fileExists, currentContent, force, normalize } = obj;
3198
3375
  if (!existing) {
3199
3376
  if (!fileExists) return { action: "create" };
3200
3377
  if (force) return { action: "regenerate" };
@@ -3204,7 +3381,9 @@ function decideAction(obj) {
3204
3381
  };
3205
3382
  }
3206
3383
  if (!fileExists) return { action: "restore" };
3207
- if (currentContent !== null && hashContent(currentContent) === existing.contentHash) return { action: "regenerate" };
3384
+ if (currentContent !== null) {
3385
+ if (hashContent(normalize ? normalize(currentContent) : currentContent) === existing.contentHash) return { action: "regenerate" };
3386
+ }
3208
3387
  if (force) return { action: "regenerate" };
3209
3388
  return {
3210
3389
  action: "conflict",
@@ -3222,7 +3401,7 @@ function decideAction(obj) {
3222
3401
  function assertNoKindCollision(obj) {
3223
3402
  const { lock, kind, workspaceName, file } = obj;
3224
3403
  const collision = lock?.targets.find((t) => t.file === file && !(t.kind === kind && t.workspaceName === workspaceName));
3225
- if (collision) throw new Error(`A ${collision.kind} target already owns ${file}, which conflicts with this ${kind} target. Pass a different name with --workspace-name to generate a separate workflow.`);
3404
+ if (collision) throw new Error(`A ${collision.kind} target already owns ${file}, which conflicts with this ${kind} target. Pass a different name with --name to generate a separate workflow.`);
3226
3405
  }
3227
3406
  /**
3228
3407
  * Print next-step guidance after generating workflow files.
@@ -3249,11 +3428,10 @@ function printNextSteps(obj) {
3249
3428
  if (idInjected) logger.log(" - tailor.config.ts (app id was added)");
3250
3429
  }
3251
3430
  /**
3252
- * Generate the GitHub Actions workflow for a deploy target and reconcile it
3253
- * with the lock file.
3431
+ * Generate a deploy target workflow and reconcile it with the lock file.
3254
3432
  * @param options - Setup options
3255
3433
  */
3256
- async function setupGitHub(options) {
3434
+ async function setupTarget(options) {
3257
3435
  logBetaWarning("setup");
3258
3436
  const resolved = await resolve$1(options);
3259
3437
  const lock = readLock(options.outputDir);
@@ -3266,27 +3444,35 @@ async function setupGitHub(options) {
3266
3444
  });
3267
3445
  const existing = findTarget(lock, resolved.kind, resolved.workspaceName);
3268
3446
  const fileExists = fs$1.existsSync(absFile);
3447
+ const currentContent = fileExists ? fs$1.readFileSync(absFile, "utf-8") : null;
3448
+ const normalize = resolved.kind === "action" ? normalizeActionContent : void 0;
3269
3449
  const decision = decideAction({
3270
3450
  existing,
3271
3451
  fileExists,
3272
- currentContent: fileExists ? fs$1.readFileSync(absFile, "utf-8") : null,
3273
- force: options.force
3452
+ currentContent,
3453
+ force: options.force,
3454
+ normalize
3274
3455
  });
3275
3456
  if (decision.action === "conflict") throw new Error(`${resolved.file}: ${decision.reason}`);
3276
3457
  const idResult = await ensureConfigId(resolved.configPath);
3277
3458
  if (idResult === null) logger.warn("Could not find a defineConfig() call to confirm an app id. The CI deploy will fail unless your config resolves to one with an 'id'.");
3278
3459
  const idInjected = idResult?.injected ?? false;
3460
+ let contentToWrite = resolved.render.content;
3461
+ if (decision.action === "regenerate" && normalize && currentContent !== null) {
3462
+ const existingBody = extractBuildSiteRunBody(currentContent);
3463
+ if (existingBody !== null) contentToWrite = injectBuildSiteRunBody(contentToWrite, existingBody);
3464
+ }
3279
3465
  fs$1.mkdirSync(path.dirname(absFile), { recursive: true });
3280
- fs$1.writeFileSync(absFile, resolved.render.content, "utf-8");
3466
+ fs$1.writeFileSync(absFile, contentToWrite, "utf-8");
3281
3467
  const newTarget = {
3282
3468
  kind: resolved.kind,
3283
3469
  workspaceName: resolved.workspaceName,
3284
3470
  file: resolved.file,
3285
- templateVersion: 5,
3471
+ templateVersion: 6,
3286
3472
  inputs: resolved.inputs,
3287
3473
  generatedIds: resolved.render.generatedIds,
3288
3474
  ejectedIds: existing?.ejectedIds ?? [],
3289
- contentHash: hashContent(resolved.render.content)
3475
+ contentHash: hashContent(normalize ? normalize(resolved.render.content) : resolved.render.content)
3290
3476
  };
3291
3477
  const targets = [...lock?.targets ?? []];
3292
3478
  const index = targets.findIndex((t) => t.kind === newTarget.kind && t.workspaceName === newTarget.workspaceName);
@@ -3299,39 +3485,379 @@ async function setupGitHub(options) {
3299
3485
  if (decision.action === "restore") logger.success(`Regenerated ${styles.path(resolved.file)} (was missing on disk)`);
3300
3486
  else if (decision.action === "regenerate") logger.success(`Regenerated ${styles.path(resolved.file)}`);
3301
3487
  else logger.success(`Generated ${styles.path(resolved.file)}`);
3302
- printNextSteps({
3488
+ if (resolved.kind === "action") {
3489
+ logger.newline();
3490
+ logger.info("Next steps:");
3491
+ logger.newline();
3492
+ logger.log(`The composite action has been generated at ${styles.path(resolved.file)}.`);
3493
+ logger.log("Use `tailor-sdk setup coordinate` to generate a coordinator workflow that orchestrates this action.");
3494
+ } else printNextSteps({
3303
3495
  environment: resolved.environment,
3304
3496
  idInjected
3305
3497
  });
3306
3498
  }
3499
+ /**
3500
+ * Generate the coordinator workflow that orchestrates per-app composite actions.
3501
+ *
3502
+ * Unlike `setupTarget`, this function does not read a Tailor config. The coordinator
3503
+ * name is required via `--name`. App working directories are resolved from
3504
+ * the lock file entries created by `setup action`.
3505
+ * @param options - Coordinate setup options
3506
+ */
3507
+ async function setupCoordinate(options) {
3508
+ logBetaWarning("setup");
3509
+ const { coordinatorName, coordinateKind, actions, force, outputDir } = options;
3510
+ validateWorkspaceName(coordinatorName);
3511
+ if (actions.length === 0) throw new Error("At least one --action is required. Run `tailor-sdk setup action --dir <app-dir>` for each app first.");
3512
+ const environment = options.environment ?? coordinatorName;
3513
+ validateEnvironment(environment);
3514
+ let branch = null;
3515
+ let branchAutoDetected = false;
3516
+ if (coordinateKind === "branch") {
3517
+ branchAutoDetected = options.branch === void 0;
3518
+ branch = options.branch ?? detectDefaultBranch(outputDir, options.gitRunner);
3519
+ validateBranch(branch);
3520
+ } else if (options.branch !== void 0) {
3521
+ validateBranch(options.branch);
3522
+ branch = options.branch;
3523
+ }
3524
+ const tagPattern = options.tagPattern ?? "v*";
3525
+ if (coordinateKind === "tag") validateTagPattern(tagPattern);
3526
+ const packageManager = detectPackageManager(outputDir);
3527
+ const lock = readLock(outputDir);
3528
+ if (!lock) throw new Error(".github/tailor-sdk.lock not found. Run `tailor-sdk setup action --name <name>` for each app before running setup coordinate.");
3529
+ const seenNames = /* @__PURE__ */ new Set();
3530
+ const apps = actions.map((actionName) => {
3531
+ const name = actionName.startsWith("tailor-") ? actionName.slice(7) : actionName;
3532
+ if (seenNames.has(name)) throw new Error(`Duplicate --action "${name}". Each composite action can only appear once in a coordinator.`);
3533
+ seenNames.add(name);
3534
+ const entry = lock.targets.find((t) => t.kind === "action" && t.workspaceName === name);
3535
+ if (!entry) throw new Error(`Action target "${name}" not found in .github/tailor-sdk.lock. Run \`tailor-sdk setup action --name ${name}\` first.`);
3536
+ validateDir(entry.inputs.dir);
3537
+ return {
3538
+ name,
3539
+ dir: entry.inputs.dir
3540
+ };
3541
+ });
3542
+ const render = renderCoordinateWorkflow({
3543
+ coordinatorName,
3544
+ kind: coordinateKind,
3545
+ apps,
3546
+ branch: branch ?? void 0,
3547
+ tagPattern: coordinateKind === "tag" ? tagPattern : void 0,
3548
+ environment,
3549
+ packageManager
3550
+ });
3551
+ const file = `.github/workflows/tailor-coordinate-${coordinatorName}${coordinateKind === "tag" ? "-tag" : ""}.yml`;
3552
+ const absFile = path.join(outputDir, file);
3553
+ assertNoKindCollision({
3554
+ lock,
3555
+ kind: "coordinate",
3556
+ workspaceName: coordinatorName,
3557
+ file
3558
+ });
3559
+ const existing = findTarget(lock, "coordinate", coordinatorName);
3560
+ const fileExists = fs$1.existsSync(absFile);
3561
+ const decision = decideAction({
3562
+ existing,
3563
+ fileExists,
3564
+ currentContent: fileExists ? fs$1.readFileSync(absFile, "utf-8") : null,
3565
+ force
3566
+ });
3567
+ if (decision.action === "conflict") throw new Error(`${file}: ${decision.reason}`);
3568
+ fs$1.mkdirSync(path.dirname(absFile), { recursive: true });
3569
+ fs$1.writeFileSync(absFile, render.content, "utf-8");
3570
+ const tailorSetupFile = ".github/actions/tailor-setup/action.yml";
3571
+ const absTailorSetupFile = path.join(outputDir, tailorSetupFile);
3572
+ if (!fs$1.existsSync(absTailorSetupFile)) {
3573
+ fs$1.mkdirSync(path.dirname(absTailorSetupFile), { recursive: true });
3574
+ fs$1.writeFileSync(absTailorSetupFile, renderTailorSetupAction({ packageManager }), "utf-8");
3575
+ logger.success(`Generated ${styles.path(tailorSetupFile)}`);
3576
+ }
3577
+ const newTarget = {
3578
+ kind: "coordinate",
3579
+ workspaceName: coordinatorName,
3580
+ file,
3581
+ templateVersion: 6,
3582
+ inputs: {
3583
+ branch,
3584
+ branchAutoDetected: coordinateKind === "branch" ? branchAutoDetected : branch !== null ? false : void 0,
3585
+ tagPattern: coordinateKind === "tag" ? tagPattern : null,
3586
+ environment,
3587
+ dir: ".",
3588
+ packageManager,
3589
+ actionDirs: apps.map((a) => a.dir)
3590
+ },
3591
+ generatedIds: render.generatedIds,
3592
+ ejectedIds: existing?.ejectedIds ?? [],
3593
+ contentHash: hashContent(render.content)
3594
+ };
3595
+ const targets = [...lock.targets];
3596
+ const idx = targets.findIndex((t) => t.kind === newTarget.kind && t.workspaceName === newTarget.workspaceName);
3597
+ if (idx === -1) targets.push(newTarget);
3598
+ else targets[idx] = newTarget;
3599
+ writeLock(outputDir, {
3600
+ version: 1,
3601
+ targets
3602
+ });
3603
+ if (decision.action === "restore") logger.success(`Regenerated ${styles.path(file)} (was missing on disk)`);
3604
+ else if (decision.action === "regenerate") logger.success(`Regenerated ${styles.path(file)}`);
3605
+ else logger.success(`Generated ${styles.path(file)}`);
3606
+ logger.newline();
3607
+ logger.info("Next steps:");
3608
+ logger.newline();
3609
+ logger.log(`1. Set the machine-user credentials as secrets on the "${environment}" environment:`);
3610
+ logger.log(` gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID --env ${environment}`);
3611
+ logger.log(` gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET --env ${environment}`);
3612
+ logger.newline();
3613
+ logger.log(`2. Provision each workspace and set TAILOR_PLATFORM_WORKSPACE_ID on the "${environment}" environment:`);
3614
+ logger.log(" tailor-sdk workspace create # one per app; copy the id");
3615
+ logger.log(` gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env ${environment}`);
3616
+ logger.newline();
3617
+ logger.log("3. Commit the generated files:");
3618
+ logger.log(` - ${file}`);
3619
+ logger.log(` - ${tailorSetupFile} (if newly created)`);
3620
+ logger.log(" - .github/tailor-sdk.lock");
3621
+ }
3622
+
3623
+ //#endregion
3624
+ //#region src/cli/commands/setup/check.ts
3625
+ /**
3626
+ * Compute drift findings for one target by comparing its recorded lock state
3627
+ * against the current repository/config state.
3628
+ * @param target - The lock target being audited
3629
+ * @param state - Current repository/config state for this target
3630
+ * @returns Drift findings (empty when the target is in sync)
3631
+ */
3632
+ function findTargetDrift(target, state) {
3633
+ const id = `${target.kind} ${target.workspaceName}`;
3634
+ const findings = [];
3635
+ if (!state.fileExists) findings.push({
3636
+ target: id,
3637
+ rule: "missing-file",
3638
+ message: `${target.file} is missing or unreadable. Re-run setup to restore it.`
3639
+ });
3640
+ else if (state.currentHash !== null && state.currentHash !== target.contentHash) findings.push({
3641
+ target: id,
3642
+ rule: "hand-edit",
3643
+ message: `${target.file} was edited by hand since it was generated. Re-run setup with --force to regenerate, or keep customizations in your own jobs/steps.`
3644
+ });
3645
+ if (target.templateVersion < state.templateVersion) findings.push({
3646
+ target: id,
3647
+ rule: "template-version",
3648
+ message: `A newer workflow template is available (generated with v${String(target.templateVersion)}, current v${String(state.templateVersion)}). Re-run setup to update.`
3649
+ });
3650
+ if (!state.configExists) findings.push({
3651
+ target: id,
3652
+ rule: "config-dir",
3653
+ message: `tailor.config.ts not found under "${target.inputs.dir}". The app directory may have moved; re-run setup with the correct --dir.`
3654
+ });
3655
+ if ((target.kind === "branch" || target.kind === "coordinate" || target.kind === "preview") && target.inputs.branchAutoDetected !== false && state.defaultBranch !== null && target.inputs.branch !== null && target.inputs.branch !== state.defaultBranch) findings.push({
3656
+ target: id,
3657
+ rule: "default-branch",
3658
+ message: `The workflow triggers on "${target.inputs.branch}" but the repository default branch is now "${state.defaultBranch}". If this is intentional, ignore this; otherwise re-run setup so the trigger matches the default branch.`
3659
+ });
3660
+ if (target.kind === "branch" && target.inputs.erdPreview && state.configExists) {
3661
+ const recorded = [...target.inputs.erdNamespaces ?? []].toSorted((a, b) => a.localeCompare(b));
3662
+ const current = state.erdNamespaces?.toSorted((a, b) => a.localeCompare(b)) ?? null;
3663
+ if (current === null || recorded.length !== current.length || recorded.some((namespace, index) => namespace !== current[index])) findings.push({
3664
+ target: id,
3665
+ rule: "erd-namespaces",
3666
+ message: "TailorDB namespaces for ERD preview changed. Re-run setup so the ERD preview matrix is regenerated."
3667
+ });
3668
+ }
3669
+ if ((target.kind === "branch" || target.kind === "tag") && target.inputs.migrationDriftCheck === false && state.hasMigrations === true) findings.push({
3670
+ target: id,
3671
+ rule: "migration-drift",
3672
+ message: "TailorDB namespaces with migrations were added to the config. Re-run setup so the tailor-migration-drift-check step is included in the plan job."
3673
+ });
3674
+ if ((target.kind === "branch" || target.kind === "tag") && target.inputs.seedValidate === false && state.hasSeeds === true) findings.push({
3675
+ target: id,
3676
+ rule: "seed-validate",
3677
+ message: "Seed plugin detected in the current config. Re-run setup so the tailor-seed-validate step is included in the plan job."
3678
+ });
3679
+ if (target.kind === "action" && target.inputs.hasStaticWebsites === false && state.hasStaticWebsites === true) findings.push({
3680
+ target: id,
3681
+ rule: "static-websites",
3682
+ message: "Static websites were added to the config. Re-run setup so the build-site slot is included in the composite action."
3683
+ });
3684
+ return findings;
3685
+ }
3686
+ function detectDefaultBranchSafe(cwd, run) {
3687
+ try {
3688
+ return detectDefaultBranch(cwd, run);
3689
+ } catch {
3690
+ return null;
3691
+ }
3692
+ }
3693
+ function escapesRoot(rel) {
3694
+ return path.isAbsolute(rel) || rel === ".." || rel.startsWith(`..${path.sep}`) || rel.startsWith("../");
3695
+ }
3696
+ function resolveWithinRoot(outputDir, relPath) {
3697
+ if (path.isAbsolute(relPath)) return null;
3698
+ const abs = path.join(outputDir, relPath);
3699
+ if (escapesRoot(path.relative(outputDir, abs))) return null;
3700
+ try {
3701
+ if (escapesRoot(path.relative(fs$1.realpathSync(outputDir), fs$1.realpathSync(abs)))) return null;
3702
+ } catch {}
3703
+ return abs;
3704
+ }
3705
+ function readHash(absFile, normalize) {
3706
+ try {
3707
+ const content = fs$1.readFileSync(absFile, "utf-8");
3708
+ return hashContent(normalize ? normalize(content) : content);
3709
+ } catch {
3710
+ return null;
3711
+ }
3712
+ }
3713
+ async function defaultLoadErdNamespaces(configPath) {
3714
+ const { config } = await loadConfig(configPath);
3715
+ return extractOwnedNamespaces(config);
3716
+ }
3717
+ async function defaultLoadHasMigrations(configPath) {
3718
+ const { config } = await loadConfig(configPath);
3719
+ return getNamespacesWithMigrations(config, path.dirname(configPath)).length > 0;
3720
+ }
3721
+ async function defaultLoadHasSeeds(configPath) {
3722
+ const { plugins } = await loadConfig(configPath);
3723
+ return plugins.some((p) => p.id === "@tailor-platform/seed");
3724
+ }
3725
+ async function defaultLoadHasStaticWebsites(configPath) {
3726
+ const { config } = await loadConfig(configPath);
3727
+ return (config.staticWebsites?.length ?? 0) > 0;
3728
+ }
3729
+ /**
3730
+ * Audit the generated workflows for drift against the current config/repo
3731
+ * state. Read-only: never writes files, the lock, or the config.
3732
+ *
3733
+ * Throws when drift is found (so it composes like the other `:check`
3734
+ * commands). The workflow drift-check step layers advisory behaviour on top
3735
+ * (per-rule ignore / continue-on-error); the CLI itself reports via exit code.
3736
+ * @param options - Check options
3737
+ */
3738
+ async function checkGitHub(options) {
3739
+ logBetaWarning("setup");
3740
+ const { outputDir } = options;
3741
+ const lock = readLock(outputDir);
3742
+ if (!lock || lock.targets.length === 0) throw new Error("No managed workflows found (.github/tailor-sdk.lock is missing or empty). Run `tailor-sdk setup branch` (or another setup subcommand) first.");
3743
+ if (!options.ci) {
3744
+ if (lock.targets.some((t) => t.kind === "branch" || t.kind === "tag" || t.kind === "coordinate") && !process.env["TAILOR_PLATFORM_WORKSPACE_ID"]) throw new Error("TAILOR_PLATFORM_WORKSPACE_ID is not set. Provision the workspace and set the variable:\n tailor-sdk workspace create # if it does not exist yet; copy the id\n gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env <environment>");
3745
+ }
3746
+ const slackToken = Boolean(process.env["TAILOR_SLACK_BOT_TOKEN"]);
3747
+ if (slackToken !== Boolean(process.env["TAILOR_SLACK_CHANNEL_ID"])) throw new Error(`Slack is partially configured: ${slackToken ? "TAILOR_SLACK_BOT_TOKEN is set" : "TAILOR_SLACK_CHANNEL_ID is set"} but not both. TAILOR_SLACK_BOT_TOKEN (secret) and TAILOR_SLACK_CHANNEL_ID (variable) must be set together, or neither should be set.`);
3748
+ const exists = options.configExistsAt ?? ((p) => fs$1.existsSync(p));
3749
+ const defaultBranch = detectDefaultBranchSafe(outputDir, options.gitRunner);
3750
+ const loadErdNamespaces = options.loadErdNamespaces ?? defaultLoadErdNamespaces;
3751
+ const loadHasMigrations = options.loadHasMigrations ?? defaultLoadHasMigrations;
3752
+ const loadHasSeeds = options.loadHasSeeds ?? defaultLoadHasSeeds;
3753
+ const loadHasStaticWebsites = options.loadHasStaticWebsites ?? defaultLoadHasStaticWebsites;
3754
+ const findings = [];
3755
+ for (const target of lock.targets) {
3756
+ const absFile = resolveWithinRoot(outputDir, target.file);
3757
+ const currentHash = absFile === null ? null : readHash(absFile, target.kind === "action" ? normalizeActionContent : void 0);
3758
+ const configAbs = target.kind === "coordinate" ? null : resolveWithinRoot(outputDir, path.join(target.inputs.dir, "tailor.config.ts"));
3759
+ const configExists = target.kind === "coordinate" || configAbs !== null && exists(configAbs);
3760
+ const erdNamespaces = target.kind === "branch" && target.inputs.erdPreview && configAbs !== null && configExists ? await loadErdNamespaces(configAbs) : null;
3761
+ const hasMigrations = (target.kind === "branch" || target.kind === "tag") && target.inputs.migrationDriftCheck !== void 0 && configAbs !== null && configExists ? await loadHasMigrations(configAbs) : void 0;
3762
+ const hasSeeds = (target.kind === "branch" || target.kind === "tag") && target.inputs.seedValidate !== void 0 && configAbs !== null && configExists ? await loadHasSeeds(configAbs) : void 0;
3763
+ const hasStaticWebsites = target.kind === "action" && target.inputs.hasStaticWebsites !== void 0 && configAbs !== null && configExists ? await loadHasStaticWebsites(configAbs) : void 0;
3764
+ findings.push(...findTargetDrift(target, {
3765
+ fileExists: currentHash !== null,
3766
+ currentHash,
3767
+ configExists,
3768
+ defaultBranch,
3769
+ templateVersion: 6,
3770
+ erdNamespaces,
3771
+ hasMigrations,
3772
+ hasSeeds,
3773
+ hasStaticWebsites
3774
+ }));
3775
+ }
3776
+ const count = lock.targets.length;
3777
+ if (findings.length === 0) {
3778
+ logger.success(`No drift detected across ${String(count)} target(s).`);
3779
+ return;
3780
+ }
3781
+ for (const finding of findings) logger.warn(`[${finding.target}] ${finding.message} (ignore key: ${finding.rule})`);
3782
+ throw new Error(`Detected ${String(findings.length)} drift finding(s) across ${String(count)} target(s). Re-run \`tailor-sdk setup\` to regenerate, or address each finding above.`);
3783
+ }
3307
3784
 
3308
3785
  //#endregion
3309
3786
  //#region src/cli/commands/setup/index.ts
3310
3787
  const checkCommand = defineAppCommand({
3311
3788
  name: "check",
3312
3789
  description: "Audit generated workflows for drift against the current config/repo (read-only).",
3313
- args: z.object({}).strict(),
3314
- run: async () => {
3315
- await checkGitHub({ outputDir: process.cwd() });
3790
+ args: z.object({ ci: arg(z.boolean().default(false), { description: "Run in CI mode: skip checks that are handled by the runtime (e.g. TAILOR_PLATFORM_WORKSPACE_ID)." }) }).strict(),
3791
+ run: async (args) => {
3792
+ await checkGitHub({
3793
+ outputDir: process.cwd(),
3794
+ ci: args.ci
3795
+ });
3316
3796
  }
3317
3797
  });
3318
- const setupCommand = defineAppCommand({
3319
- name: "setup",
3320
- description: "Generate a CI deploy workflow for your project. (beta)",
3798
+ const coordinateCommand = defineAppCommand({
3799
+ name: "coordinate",
3800
+ description: "Generate a coordinator workflow that orchestrates multiple --action-generated composite actions.",
3321
3801
  args: z.object({
3322
- provider: arg(z.enum(["github"], { message: "Only the 'github' provider is supported." }).default("github"), {
3323
- alias: "p",
3324
- description: "CI provider to generate for (only 'github' is supported)"
3802
+ name: arg(z.string().min(1), {
3803
+ alias: "n",
3804
+ description: "Coordinator name (used in the generated workflow file name and job names)"
3325
3805
  }),
3326
- "workspace-name": arg(z.string().min(1).optional(), {
3806
+ action: arg(z.array(z.string().min(1)).min(1), { description: "Composite action to include (can be specified multiple times). tailor- prefix optional." }),
3807
+ branch: arg(z.string().min(1).optional(), { description: "Branch target: deploy trigger branch (defaults to the detected default branch)" }),
3808
+ tag: arg(z.boolean().default(false), { description: "Generate a tag target coordinator" }),
3809
+ environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs" }),
3810
+ force: arg(z.boolean().default(false), { description: "Discard hand edits and regenerate" })
3811
+ }).strict(),
3812
+ run: async (args) => {
3813
+ const coordinateKind = args.tag ? "tag" : "branch";
3814
+ await setupCoordinate({
3815
+ coordinatorName: args.name,
3816
+ coordinateKind,
3817
+ actions: args.action,
3818
+ branch: args.branch,
3819
+ environment: args.environment,
3820
+ force: args.force,
3821
+ outputDir: process.cwd()
3822
+ });
3823
+ }
3824
+ });
3825
+ const actionCommand = defineAppCommand({
3826
+ name: "action",
3827
+ description: "Generate a per-app composite action for use with setup coordinate (monorepo multi-app deploys).",
3828
+ args: z.object({
3829
+ name: arg(z.string().min(1).optional(), {
3327
3830
  alias: "n",
3328
- description: "Workspace name (defaults to the config 'name')"
3831
+ description: "Name (defaults to the config 'name')"
3832
+ }),
3833
+ dir: arg(z.string().min(1).default("."), {
3834
+ alias: "d",
3835
+ description: "App directory"
3329
3836
  }),
3330
- branch: arg(z.string().min(1).optional(), { description: "Branch target: deploy trigger branch (defaults to the detected default branch). Tag target: tag-reachability guard branch (no guard when omitted)" }),
3331
- tag: arg(z.boolean().default(false), { description: "Generate a tag target (deploy on tag push)" }),
3332
- "tag-pattern": arg(z.string().min(1).optional(), { description: "Tag glob to match (requires --tag; defaults to v*)" }),
3837
+ environment: arg(z.string().min(1).optional(), { description: "GitHub Environment (defaults to the workspace name)" }),
3838
+ force: arg(z.boolean().default(false), { description: "Discard hand edits and regenerate" })
3839
+ }).strict(),
3840
+ run: async (args) => {
3841
+ await setupTarget({
3842
+ kind: "action",
3843
+ workspaceName: args.name,
3844
+ dir: args.dir,
3845
+ environment: args.environment,
3846
+ force: args.force,
3847
+ outputDir: process.cwd()
3848
+ });
3849
+ }
3850
+ });
3851
+ const branchCommand = defineAppCommand({
3852
+ name: "branch",
3853
+ description: "Generate a branch-target deploy workflow (push to branch triggers deploy).",
3854
+ args: z.object({
3855
+ name: arg(z.string().min(1).optional(), {
3856
+ alias: "n",
3857
+ description: "Name (defaults to the config 'name')"
3858
+ }),
3859
+ branch: arg(z.string().min(1).optional(), { description: "Deploy trigger branch (defaults to the detected default branch)" }),
3333
3860
  environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs (defaults to the workspace name)" }),
3334
- "no-plan": arg(z.boolean().default(false), { description: "Disable the plan job for a branch target (cannot be combined with --tag)" }),
3335
3861
  "erd-preview": arg(z.boolean().default(false), { description: "Add PR ERD viewer artifacts with current/diff previews for TailorDB namespaces" }),
3336
3862
  dir: arg(z.string().min(1).default("."), {
3337
3863
  alias: "d",
@@ -3339,17 +3865,12 @@ const setupCommand = defineAppCommand({
3339
3865
  }),
3340
3866
  force: arg(z.boolean().default(false), { description: "Discard hand edits / take over unmanaged files and regenerate" })
3341
3867
  }).strict(),
3342
- subCommands: { check: checkCommand },
3343
3868
  run: async (args) => {
3344
- if (args["tag-pattern"] !== void 0 && !args.tag) throw new Error("--tag-pattern requires --tag.");
3345
- if (args["no-plan"] && args.tag) throw new Error("--no-plan cannot be combined with --tag.");
3346
- await setupGitHub({
3347
- workspaceName: args["workspace-name"],
3869
+ await setupTarget({
3870
+ kind: "branch",
3871
+ workspaceName: args.name,
3348
3872
  branch: args.branch,
3349
- tag: args.tag,
3350
- tagPattern: args["tag-pattern"] ?? "v*",
3351
3873
  environment: args.environment,
3352
- plan: !args["no-plan"],
3353
3874
  erdPreview: args["erd-preview"],
3354
3875
  dir: args.dir,
3355
3876
  force: args.force,
@@ -3357,6 +3878,80 @@ const setupCommand = defineAppCommand({
3357
3878
  });
3358
3879
  }
3359
3880
  });
3881
+ const tagCommand = defineAppCommand({
3882
+ name: "tag",
3883
+ description: "Generate a tag-target deploy workflow (tag push triggers deploy).",
3884
+ args: z.object({
3885
+ name: arg(z.string().min(1).optional(), {
3886
+ alias: "n",
3887
+ description: "Name (defaults to the config 'name')"
3888
+ }),
3889
+ "tag-pattern": arg(z.string().min(1).default("v*"), { description: "Tag glob to match (defaults to v*)" }),
3890
+ branch: arg(z.string().min(1).optional(), { description: "Tag-reachability guard branch (no guard when omitted)" }),
3891
+ environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs (defaults to the workspace name)" }),
3892
+ dir: arg(z.string().min(1).default("."), {
3893
+ alias: "d",
3894
+ description: "App directory (for monorepo setups)"
3895
+ }),
3896
+ force: arg(z.boolean().default(false), { description: "Discard hand edits / take over unmanaged files and regenerate" })
3897
+ }).strict(),
3898
+ run: async (args) => {
3899
+ await setupTarget({
3900
+ kind: "tag",
3901
+ workspaceName: args.name,
3902
+ tagPattern: args["tag-pattern"],
3903
+ branch: args.branch,
3904
+ environment: args.environment,
3905
+ dir: args.dir,
3906
+ force: args.force,
3907
+ outputDir: process.cwd()
3908
+ });
3909
+ }
3910
+ });
3911
+ const previewCommand = defineAppCommand({
3912
+ name: "preview",
3913
+ description: "Generate a preview workflow (PR open/sync triggers deploy to a per-PR workspace).",
3914
+ args: z.object({
3915
+ name: arg(z.string().min(1).optional(), {
3916
+ alias: "n",
3917
+ description: "Name (defaults to the config 'name')"
3918
+ }),
3919
+ branch: arg(z.string().min(1).optional(), { description: "Branch to filter PRs by (defaults to the detected default branch)" }),
3920
+ region: arg(z.string().min(1), { description: "Workspace region for preview workspace creation (e.g. us-west). Required." }),
3921
+ "require-preview-label": arg(z.boolean().default(false), { description: "Deploy preview only for PRs labeled `tailor:preview` instead of all PRs." }),
3922
+ environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the preview jobs (defaults to the workspace name)" }),
3923
+ dir: arg(z.string().min(1).default("."), {
3924
+ alias: "d",
3925
+ description: "App directory (for monorepo setups)"
3926
+ }),
3927
+ force: arg(z.boolean().default(false), { description: "Discard hand edits / take over unmanaged files and regenerate" })
3928
+ }).strict(),
3929
+ run: async (args) => {
3930
+ await setupTarget({
3931
+ kind: "preview",
3932
+ workspaceName: args.name,
3933
+ branch: args.branch,
3934
+ region: args.region,
3935
+ requirePreviewLabel: args["require-preview-label"],
3936
+ environment: args.environment,
3937
+ dir: args.dir,
3938
+ force: args.force,
3939
+ outputDir: process.cwd()
3940
+ });
3941
+ }
3942
+ });
3943
+ const setupCommand = defineCommand({
3944
+ name: "setup",
3945
+ description: "Generate CI deploy workflows for your project. (beta)",
3946
+ subCommands: {
3947
+ branch: branchCommand,
3948
+ tag: tagCommand,
3949
+ preview: previewCommand,
3950
+ action: actionCommand,
3951
+ coordinate: coordinateCommand,
3952
+ check: checkCommand
3953
+ }
3954
+ });
3360
3955
 
3361
3956
  //#endregion
3362
3957
  //#region src/cli/shared/skills-installer.ts
@@ -5391,7 +5986,7 @@ async function fetchRemoteTypes(client, workspaceId, namespace) {
5391
5986
  async function assertMigrationsReproduceLocalTypes(loaded, target) {
5392
5987
  const { config, plugins } = loaded;
5393
5988
  const pluginManager = plugins.length > 0 ? new PluginManager(plugins) : void 0;
5394
- const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-a_ApFuPh.mjs");
5989
+ const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-BhRnH42h.mjs");
5395
5990
  const application = defineApplication({
5396
5991
  config,
5397
5992
  pluginManager
@@ -5403,7 +5998,7 @@ async function assertMigrationsReproduceLocalTypes(loaded, target) {
5403
5998
  await service.processNamespacePlugins();
5404
5999
  }
5405
6000
  const pluginExecutorFiles = generatePluginFilesIfNeeded(pluginManager, application.tailorDBServices, config.path);
5406
- const executorService = application.executorService ?? (pluginExecutorFiles.length > 0 ? (await import("../service-CRaa4Joe.mjs")).createExecutorService({ config: { files: [] } }) : void 0);
6001
+ const executorService = application.executorService ?? (pluginExecutorFiles.length > 0 ? (await import("../service-BXmdFwSH.mjs")).createExecutorService({ config: { files: [] } }) : void 0);
5407
6002
  await executorService?.loadExecutors();
5408
6003
  if (pluginExecutorFiles.length > 0) await executorService?.loadPluginExecutorFiles([...pluginExecutorFiles]);
5409
6004
  const executorUsedTypes = /* @__PURE__ */ new Set();
@@ -5678,9 +6273,9 @@ const upgradeCommand = defineAppCommand({
5678
6273
  })
5679
6274
  }).strict(),
5680
6275
  run: async (args) => {
5681
- const { initTelemetry } = await import("../telemetry-w92bvGdC.mjs");
6276
+ const { initTelemetry } = await import("../telemetry-ClwW5ohF.mjs");
5682
6277
  await initTelemetry();
5683
- const { upgrade } = await import("../service-DDWgZL_L2.mjs");
6278
+ const { upgrade } = await import("../service-DBFZQloR.mjs");
5684
6279
  await upgrade({
5685
6280
  from: args.from,
5686
6281
  dryRun: args["dry-run"],
@@ -6140,11 +6735,11 @@ runMain(mainCommand, {
6140
6735
  if (isVerbose() && error.stack) logger.debug(`\nStack trace:\n${error.stack}`);
6141
6736
  } else logger.error(`Unknown error: ${error}`);
6142
6737
  if (!isCLIError(error) && (!(error instanceof Error) || error instanceof TypeError || error instanceof RangeError)) {
6143
- const { reportCrash } = await import("../crashreport-BwF8cHF0.mjs");
6738
+ const { reportCrash } = await import("../crashreport-Dy4ZzSkd.mjs");
6144
6739
  await reportCrash(error, "handledError");
6145
6740
  }
6146
6741
  }
6147
- const { shutdownTelemetry } = await import("../telemetry-w92bvGdC.mjs");
6742
+ const { shutdownTelemetry } = await import("../telemetry-ClwW5ohF.mjs");
6148
6743
  await shutdownTelemetry();
6149
6744
  }
6150
6745
  });