@tailor-platform/sdk 2.0.0-next.4 → 2.0.0-next.6

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 (321) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/dist/{application-BX13RArL.mjs → application-BJXRpQj5.mjs} +602 -1842
  3. package/dist/application-BJXRpQj5.mjs.map +1 -0
  4. package/dist/application-BV-AXawv.mjs +3 -0
  5. package/dist/cli/cache/bundle-cache.d.mts +2 -1
  6. package/dist/cli/cache/store.d.mts +1 -1
  7. package/dist/cli/commands/api/index.d.mts +1 -0
  8. package/dist/cli/commands/deploy/aigateway.d.mts +5 -1
  9. package/dist/cli/commands/deploy/application.d.mts +7 -1
  10. package/dist/cli/commands/deploy/apply-phases.d.mts +14 -1
  11. package/dist/cli/commands/deploy/auth.d.mts +5 -1
  12. package/dist/cli/commands/deploy/deploy.d.mts +13 -3
  13. package/dist/cli/commands/deploy/executor.d.mts +7 -1
  14. package/dist/cli/commands/deploy/function-registry.d.mts +5 -0
  15. package/dist/cli/commands/deploy/idp.d.mts +5 -1
  16. package/dist/cli/commands/deploy/resolver.d.mts +5 -1
  17. package/dist/cli/commands/deploy/secret-manager.d.mts +5 -1
  18. package/dist/cli/commands/deploy/staticwebsite.d.mts +5 -1
  19. package/dist/cli/commands/deploy/tailordb/index.d.mts +8 -1
  20. package/dist/cli/commands/deploy/types.d.mts +3 -1
  21. package/dist/cli/commands/deploy/workflow-execution-policy.d.mts +3 -1
  22. package/dist/cli/commands/deploy/workflow.d.mts +4 -1
  23. package/dist/cli/commands/executor/get.d.mts +0 -1
  24. package/dist/cli/commands/executor/jobs.d.mts +0 -1
  25. package/dist/cli/commands/executor/list.d.mts +0 -1
  26. package/dist/cli/commands/executor/transform.d.mts +1 -0
  27. package/dist/cli/commands/executor/trigger.d.mts +0 -1
  28. package/dist/cli/commands/executor/webhook.d.mts +0 -1
  29. package/dist/cli/commands/function/get.d.mts +0 -1
  30. package/dist/cli/commands/function/list.d.mts +0 -1
  31. package/dist/cli/commands/function/transform.d.mts +1 -0
  32. package/dist/cli/commands/generate/options.d.mts +0 -1
  33. package/dist/cli/commands/generate/service.d.mts +6 -2
  34. package/dist/cli/commands/machineuser/list.d.mts +0 -1
  35. package/dist/cli/commands/machineuser/token.d.mts +0 -1
  36. package/dist/cli/commands/oauth2client/get.d.mts +0 -1
  37. package/dist/cli/commands/oauth2client/list.d.mts +0 -1
  38. package/dist/cli/commands/oauth2client/transform.d.mts +1 -0
  39. package/dist/cli/commands/organization/folder/create.d.mts +0 -1
  40. package/dist/cli/commands/organization/folder/delete.d.mts +0 -1
  41. package/dist/cli/commands/organization/folder/get.d.mts +0 -1
  42. package/dist/cli/commands/organization/folder/list.d.mts +0 -1
  43. package/dist/cli/commands/organization/folder/update.d.mts +0 -1
  44. package/dist/cli/commands/organization/get.d.mts +0 -1
  45. package/dist/cli/commands/organization/list.d.mts +0 -1
  46. package/dist/cli/commands/organization/transform.d.mts +2 -0
  47. package/dist/cli/commands/organization/tree.d.mts +0 -1
  48. package/dist/cli/commands/organization/update.d.mts +0 -1
  49. package/dist/cli/commands/remove.d.mts +0 -1
  50. package/dist/cli/commands/show.d.mts +9 -4
  51. package/dist/cli/commands/staticwebsite/deploy.d.mts +19 -0
  52. package/dist/cli/commands/tailordb/migrate/config.d.mts +0 -1
  53. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +16 -3
  54. package/dist/cli/commands/tailordb/migrate/generate.d.mts +0 -1
  55. package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +6 -0
  56. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +1 -1
  57. package/dist/cli/commands/tailordb/truncate.d.mts +0 -1
  58. package/dist/cli/commands/workflow/executions.d.mts +0 -1
  59. package/dist/cli/commands/workflow/get.d.mts +2 -1
  60. package/dist/cli/commands/workflow/list.d.mts +0 -1
  61. package/dist/cli/commands/workflow/resume.d.mts +0 -1
  62. package/dist/cli/commands/workflow/start.d.mts +0 -1
  63. package/dist/cli/commands/workflow/status.d.mts +1 -0
  64. package/dist/cli/commands/workflow/transform.d.mts +1 -0
  65. package/dist/cli/commands/workflow/wait.d.mts +0 -1
  66. package/dist/cli/commands/workflow/waiter.d.mts +1 -1
  67. package/dist/cli/commands/workspace/app/health.d.mts +0 -1
  68. package/dist/cli/commands/workspace/app/list.d.mts +0 -1
  69. package/dist/cli/commands/workspace/app/transform.d.mts +2 -0
  70. package/dist/cli/commands/workspace/create.d.mts +3 -1
  71. package/dist/cli/commands/workspace/delete.d.mts +0 -1
  72. package/dist/cli/commands/workspace/get.d.mts +0 -1
  73. package/dist/cli/commands/workspace/list.d.mts +1 -1
  74. package/dist/cli/commands/workspace/restore.d.mts +0 -1
  75. package/dist/cli/commands/workspace/transform.d.mts +4 -0
  76. package/dist/cli/commands/workspace/user/invite.d.mts +0 -1
  77. package/dist/cli/commands/workspace/user/list.d.mts +0 -1
  78. package/dist/cli/commands/workspace/user/remove.d.mts +0 -1
  79. package/dist/cli/commands/workspace/user/transform.d.mts +1 -0
  80. package/dist/cli/commands/workspace/user/update.d.mts +0 -1
  81. package/dist/cli/index.d.mts +6 -3
  82. package/dist/cli/index.mjs +871 -1577
  83. package/dist/cli/index.mjs.map +1 -1
  84. package/dist/cli/lib.d.mts +10 -4
  85. package/dist/cli/lib.mjs +50 -4
  86. package/dist/cli/lib.mjs.map +1 -1
  87. package/dist/cli/query/index.d.mts +0 -1
  88. package/dist/cli/services/application.d.mts +12 -1
  89. package/dist/cli/services/auth/service.d.mts +2 -0
  90. package/dist/cli/services/executor/service.d.mts +1 -1
  91. package/dist/cli/services/http-adapter/bundler.d.mts +3 -1
  92. package/dist/cli/services/http-adapter/service.d.mts +2 -1
  93. package/dist/cli/services/resolver/service.d.mts +1 -1
  94. package/dist/cli/services/tailordb/service.d.mts +3 -1
  95. package/dist/cli/services/workflow/bundler.d.mts +2 -1
  96. package/dist/cli/services/workflow/service.d.mts +1 -1
  97. package/dist/cli/shared/args.d.mts +1 -1
  98. package/dist/cli/shared/client.d.mts +1 -1
  99. package/dist/cli/shared/config-loader.d.mts +1 -2
  100. package/dist/cli/shared/context.d.mts +2 -1
  101. package/dist/cli/shared/readonly-guard.d.mts +25 -0
  102. package/dist/cli/shared/script-executor.d.mts +0 -1
  103. package/dist/cli/shared/seed-chunker.d.mts +7 -3
  104. package/dist/cli/shared/tailordb-namespaces.d.mts +37 -0
  105. package/dist/cli/shared/type-generator.d.mts +0 -1
  106. package/dist/cli/shared/workspace-name.d.mts +1 -0
  107. package/dist/completion/zsh-worker.zsh +15 -303
  108. package/dist/configure/config/index.d.mts +0 -1
  109. package/dist/configure/config/types.d.mts +2 -2
  110. package/dist/configure/index.d.mts +14 -4
  111. package/dist/configure/index.mjs +201 -6
  112. package/dist/configure/index.mjs.map +1 -1
  113. package/dist/configure/services/aigateway/index.d.mts +0 -1
  114. package/dist/configure/services/aigateway/types.d.mts +0 -1
  115. package/dist/configure/services/auth/index.d.mts +0 -1
  116. package/dist/configure/services/auth/types.d.mts +15 -14
  117. package/dist/configure/services/executor/executor.d.mts +2 -13
  118. package/dist/configure/services/executor/index.d.mts +1 -0
  119. package/dist/configure/services/executor/operation.d.mts +9 -4
  120. package/dist/configure/services/executor/trigger/event.d.mts +0 -1
  121. package/dist/configure/services/executor/trigger/index.d.mts +0 -1
  122. package/dist/configure/services/executor/trigger/schedule.d.mts +0 -1
  123. package/dist/configure/services/executor/trigger/webhook.d.mts +0 -1
  124. package/dist/configure/services/http-adapter/http-adapter.d.mts +1 -2
  125. package/dist/configure/services/idp/index.d.mts +1 -5
  126. package/dist/configure/services/idp/permission.d.mts +4 -5
  127. package/dist/configure/services/idp/types.d.mts +0 -1
  128. package/dist/configure/services/index.d.mts +5 -0
  129. package/dist/configure/services/resolver/resolver.d.mts +1 -0
  130. package/dist/configure/services/resolver/types.d.mts +0 -1
  131. package/dist/configure/services/secrets/index.d.mts +0 -1
  132. package/dist/configure/services/secrets/types.d.mts +0 -1
  133. package/dist/configure/services/staticwebsite/index.d.mts +0 -1
  134. package/dist/configure/services/staticwebsite/types.d.mts +0 -1
  135. package/dist/configure/services/tailordb/permission.d.mts +4 -5
  136. package/dist/configure/services/tailordb/schema.d.mts +86 -56
  137. package/dist/configure/services/tailordb/types.d.mts +66 -17
  138. package/dist/configure/services/workflow/execution-policy.d.mts +2 -3
  139. package/dist/configure/services/workflow/execution-policy.types.d.mts +0 -1
  140. package/dist/configure/services/workflow/index.d.mts +1 -0
  141. package/dist/configure/services/workflow/job.d.mts +0 -1
  142. package/dist/configure/services/workflow/wait-point.d.mts +0 -1
  143. package/dist/configure/services/workflow/workflow.d.mts +1 -1
  144. package/dist/configure/types/field.d.mts +1 -2
  145. package/dist/configure/types/field.types.d.mts +6 -45
  146. package/dist/configure/types/index.d.mts +1 -0
  147. package/dist/configure/types/type.d.mts +2 -11
  148. package/dist/{crashreport-D3RjuSYb.mjs → crashreport-0ioNRZ4K.mjs} +1 -1
  149. package/dist/{crashreport-Cg-pqRJV.mjs → crashreport-CxHne11E.mjs} +4 -3
  150. package/dist/{crashreport-Cg-pqRJV.mjs.map → crashreport-CxHne11E.mjs.map} +1 -1
  151. package/dist/errors-DfnF4ibq.mjs +100 -0
  152. package/dist/errors-DfnF4ibq.mjs.map +1 -0
  153. package/dist/{globals-BjhJJ2dN.mjs → globals-D-YbJKW-.mjs} +16 -10
  154. package/dist/globals-D-YbJKW-.mjs.map +1 -0
  155. package/dist/kysely/index.d.mts +3 -4
  156. package/dist/{kysely-type-D-zflMGf.mjs → kysely-type-DqIRgDYR.mjs} +4 -3
  157. package/dist/kysely-type-DqIRgDYR.mjs.map +1 -0
  158. package/dist/{logger-BEiZZ3qT.mjs → logger-BwS4ppwO.mjs} +3 -3
  159. package/dist/logger-BwS4ppwO.mjs.map +1 -0
  160. package/dist/parser/service/auth/index.d.mts +1 -1
  161. package/dist/parser/service/auth/schema.d.mts +2 -0
  162. package/dist/parser/service/http-adapter/index.d.mts +1 -1
  163. package/dist/parser/service/idp/types.d.mts +1 -0
  164. package/dist/parser/service/tailordb/type-source.d.mts +10 -0
  165. package/dist/parser/service/tailordb/types.d.mts +6 -1
  166. package/dist/plugin/builtin/enum-constants/index.d.mts +0 -1
  167. package/dist/plugin/builtin/file-utils/index.d.mts +0 -1
  168. package/dist/plugin/builtin/kysely-type/index.d.mts +0 -1
  169. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  170. package/dist/plugin/builtin/seed/index.d.mts +0 -1
  171. package/dist/plugin/get-generated-type.d.mts +0 -1
  172. package/dist/plugin/manager.d.mts +2 -1
  173. package/dist/plugin/types.d.mts +0 -1
  174. package/dist/plugin/with-context.d.mts +0 -1
  175. package/dist/{register-ts-hook-BM1caLT0.mjs → register-ts-hook-DL31O2W9.mjs} +3076 -1744
  176. package/dist/register-ts-hook-DL31O2W9.mjs.map +1 -0
  177. package/dist/{registry-BSbq6SPo.mjs → registry-CC3CbQiF.mjs} +3 -3
  178. package/dist/registry-CC3CbQiF.mjs.map +1 -0
  179. package/dist/runtime/aigateway.d.mts +0 -1
  180. package/dist/runtime/authconnection.d.mts +0 -1
  181. package/dist/runtime/context.d.mts +0 -1
  182. package/dist/runtime/field-parse.d.mts +10 -0
  183. package/dist/runtime/globals.d.mts +5 -3
  184. package/dist/runtime/index.d.mts +0 -1
  185. package/dist/runtime/index.mjs +1 -1
  186. package/dist/runtime/types.d.mts +9 -4
  187. package/dist/runtime/workflow.d.mts +70 -10
  188. package/dist/runtime/workflow.mjs +1 -1
  189. package/dist/{schema-C4LYbXVG.mjs → schema--xYWRGfe.mjs} +272 -51
  190. package/dist/schema--xYWRGfe.mjs.map +1 -0
  191. package/dist/{secret-file-aoNXJmKa.mjs → secret-file-C3J4Gy8r.mjs} +2 -27
  192. package/dist/secret-file-C3J4Gy8r.mjs.map +1 -0
  193. package/dist/{secretmanager-Bd45j7an.mjs → secretmanager-IY4UvinW.mjs} +42 -9
  194. package/dist/secretmanager-IY4UvinW.mjs.map +1 -0
  195. package/dist/seed/index.d.mts +3 -2
  196. package/dist/{service-ufH7sS8o.mjs → service-CnHz9rwz.mjs} +47 -20
  197. package/dist/service-CnHz9rwz.mjs.map +1 -0
  198. package/dist/{service-DeZeYa06.mjs → service-DaeJlBoH.mjs} +3 -3
  199. package/dist/{service-DeZeYa06.mjs.map → service-DaeJlBoH.mjs.map} +1 -1
  200. package/dist/service-nU6ITOHL.mjs +3 -0
  201. package/dist/service_pb-BoLM0X4K.mjs +167 -0
  202. package/dist/service_pb-BoLM0X4K.mjs.map +1 -0
  203. package/dist/{service_pb-B2FF59sP.mjs → service_pb-r1T7kpJc.mjs} +1 -1
  204. package/dist/tailor-proto/src/tailor/v1/aigateway_pb.d.mts +0 -1
  205. package/dist/tailor-proto/src/tailor/v1/aigateway_resource_pb.d.mts +0 -1
  206. package/dist/tailor-proto/src/tailor/v1/application_pb.d.mts +0 -1
  207. package/dist/tailor-proto/src/tailor/v1/application_resource_pb.d.mts +0 -1
  208. package/dist/tailor-proto/src/tailor/v1/auth_pb.d.mts +0 -1
  209. package/dist/tailor-proto/src/tailor/v1/auth_resource_pb.d.mts +0 -1
  210. package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +0 -1
  211. package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +0 -1
  212. package/dist/tailor-proto/src/tailor/v1/executor_pb.d.mts +0 -1
  213. package/dist/tailor-proto/src/tailor/v1/executor_resource_pb.d.mts +0 -1
  214. package/dist/tailor-proto/src/tailor/v1/function_pb.d.mts +0 -1
  215. package/dist/tailor-proto/src/tailor/v1/function_registry_pb.d.mts +0 -1
  216. package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +0 -1
  217. package/dist/tailor-proto/src/tailor/v1/http_adapter_resource_pb.d.mts +0 -1
  218. package/dist/tailor-proto/src/tailor/v1/idp_pb.d.mts +0 -1
  219. package/dist/tailor-proto/src/tailor/v1/idp_resource_pb.d.mts +0 -1
  220. package/dist/tailor-proto/src/tailor/v1/metadata_pb.d.mts +0 -1
  221. package/dist/tailor-proto/src/tailor/v1/metadata_resource_pb.d.mts +0 -1
  222. package/dist/tailor-proto/src/tailor/v1/meter_pb.d.mts +0 -1
  223. package/dist/tailor-proto/src/tailor/v1/pipeline_pb.d.mts +0 -1
  224. package/dist/tailor-proto/src/tailor/v1/pipeline_resource_pb.d.mts +0 -1
  225. package/dist/tailor-proto/src/tailor/v1/resource_pb.d.mts +0 -1
  226. package/dist/tailor-proto/src/tailor/v1/secret_manager_pb.d.mts +0 -1
  227. package/dist/tailor-proto/src/tailor/v1/secret_manager_resource_pb.d.mts +0 -1
  228. package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +0 -1
  229. package/dist/tailor-proto/src/tailor/v1/stateflow_pb.d.mts +0 -1
  230. package/dist/tailor-proto/src/tailor/v1/stateflow_resource_pb.d.mts +0 -1
  231. package/dist/tailor-proto/src/tailor/v1/staticwebsite_pb.d.mts +0 -1
  232. package/dist/tailor-proto/src/tailor/v1/staticwebsite_resource_pb.d.mts +0 -1
  233. package/dist/tailor-proto/src/tailor/v1/tailordb_pb.d.mts +0 -1
  234. package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +0 -1
  235. package/dist/tailor-proto/src/tailor/v1/telemetryrouter_pb.d.mts +0 -1
  236. package/dist/tailor-proto/src/tailor/v1/telemetryrouter_resource_pb.d.mts +0 -1
  237. package/dist/tailor-proto/src/tailor/v1/workflow_pb.d.mts +0 -1
  238. package/dist/tailor-proto/src/tailor/v1/workflow_resource_pb.d.mts +0 -1
  239. package/dist/tailor-proto/src/tailor/v1/workspace_pb.d.mts +0 -1
  240. package/dist/tailor-proto/src/tailor/v1/workspace_resource_pb.d.mts +0 -1
  241. package/dist/types/aigateway.generated.d.mts +5 -2
  242. package/dist/types/auth-connection.generated.d.mts +24 -11
  243. package/dist/types/auth.generated.d.mts +115 -84
  244. package/dist/types/executor.generated.d.mts +133 -123
  245. package/dist/types/helpers.d.mts +7 -6
  246. package/dist/types/http-adapter.generated.d.mts +11 -5
  247. package/dist/types/idp.generated.d.mts +536 -524
  248. package/dist/types/resolver.generated.d.mts +26 -11
  249. package/dist/types/staticwebsite.generated.d.mts +7 -3
  250. package/dist/types/tailordb.generated.d.mts +57 -29
  251. package/dist/types/workflow.generated.d.mts +9 -4
  252. package/dist/user-agent-Bgsszb5I.mjs +28 -0
  253. package/dist/user-agent-Bgsszb5I.mjs.map +1 -0
  254. package/dist/utils/test/index.d.mts +1 -2
  255. package/dist/utils/test/index.mjs +36 -10
  256. package/dist/utils/test/index.mjs.map +1 -1
  257. package/dist/utils/test/mock.d.mts +0 -1
  258. package/dist/vitest/environment.mjs +1 -1
  259. package/dist/vitest/index.d.mts +9 -9
  260. package/dist/vitest/index.mjs +591 -173
  261. package/dist/vitest/index.mjs.map +1 -1
  262. package/dist/vitest/mock-kysely.d.mts +0 -1
  263. package/dist/vitest/mock.d.mts +8 -8
  264. package/dist/vitest/mocks/aigateway.d.mts +16 -6
  265. package/dist/vitest/mocks/authconnection.d.mts +16 -6
  266. package/dist/vitest/mocks/file.d.mts +23 -6
  267. package/dist/vitest/mocks/iconv.d.mts +28 -4
  268. package/dist/vitest/mocks/idp.d.mts +26 -10
  269. package/dist/vitest/mocks/secretmanager.d.mts +16 -5
  270. package/dist/vitest/mocks/tailordb.d.mts +54 -10
  271. package/dist/vitest/mocks/workflow.d.mts +72 -21
  272. package/dist/vitest/setup.mjs +2 -2
  273. package/dist/vitest/workflow-local.d.mts +1 -0
  274. package/dist/workflow-9kHGKxF2.mjs +64 -0
  275. package/dist/workflow-9kHGKxF2.mjs.map +1 -0
  276. package/dist/{service_pb-B5w9rjPY.mjs → workspace_resource_pb-Db3fv68L.mjs} +57 -165
  277. package/dist/workspace_resource_pb-Db3fv68L.mjs.map +1 -0
  278. package/docs/cli/application.md +61 -15
  279. package/docs/cli/setup.md +1 -1
  280. package/docs/cli/tailordb.md +8 -140
  281. package/docs/cli/workspace.md +6 -4
  282. package/docs/cli-reference.md +18 -14
  283. package/docs/github-actions.md +8 -0
  284. package/docs/migration/v2.md +247 -0
  285. package/docs/runtime.md +5 -6
  286. package/docs/services/aigateway.md +2 -0
  287. package/docs/services/executor.md +5 -0
  288. package/docs/services/tailordb.md +144 -68
  289. package/docs/services/workflow.md +3 -3
  290. package/docs/testing.md +105 -89
  291. package/package.json +22 -25
  292. package/dist/application-BO4ZF8wP.mjs +0 -3
  293. package/dist/application-BX13RArL.mjs.map +0 -1
  294. package/dist/cli/erd-viewer-assets/app.js +0 -1424
  295. package/dist/cli/erd-viewer-assets/index.html +0 -77
  296. package/dist/cli/erd-viewer-assets/serve.json +0 -13
  297. package/dist/cli/erd-viewer-assets/styles.css +0 -1036
  298. package/dist/cli/shared/trigger-context.d.mts +0 -1
  299. package/dist/configure/types/field-runtime.d.mts +0 -17
  300. package/dist/errors-118wUDRr.mjs +0 -42
  301. package/dist/errors-118wUDRr.mjs.map +0 -1
  302. package/dist/field-runtime-CAnh_oow.mjs +0 -183
  303. package/dist/field-runtime-CAnh_oow.mjs.map +0 -1
  304. package/dist/globals-BjhJJ2dN.mjs.map +0 -1
  305. package/dist/kysely-type-D-zflMGf.mjs.map +0 -1
  306. package/dist/logger-BEiZZ3qT.mjs.map +0 -1
  307. package/dist/register-ts-hook-BM1caLT0.mjs.map +0 -1
  308. package/dist/registry-BSbq6SPo.mjs.map +0 -1
  309. package/dist/resource_pb-BRv7AMXK.mjs +0 -49
  310. package/dist/resource_pb-BRv7AMXK.mjs.map +0 -1
  311. package/dist/schema-C4LYbXVG.mjs.map +0 -1
  312. package/dist/secret-file-aoNXJmKa.mjs.map +0 -1
  313. package/dist/secretmanager-Bd45j7an.mjs.map +0 -1
  314. package/dist/service-CkIwEctX.mjs +0 -3
  315. package/dist/service-ufH7sS8o.mjs.map +0 -1
  316. package/dist/service_pb-B5w9rjPY.mjs.map +0 -1
  317. package/dist/types-C-lQ4se3.mjs +0 -4
  318. package/dist/types-CXF2OYdR.mjs +0 -199
  319. package/dist/types-CXF2OYdR.mjs.map +0 -1
  320. package/dist/workflow-GNuSnsTx.mjs +0 -22
  321. package/dist/workflow-GNuSnsTx.mjs.map +0 -1
@@ -1,291 +1,31 @@
1
1
  import { n as isSdkBranded } from "./brand-Eo4pLXPJ.mjs";
2
- import { i as PageDirection } from "./resource_pb-BRv7AMXK.mjs";
3
2
  import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
4
- import { a as parseBoolean, n as logger, r as styles } from "./logger-BEiZZ3qT.mjs";
5
- import { a as AuthConfigSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, g as functionSchema, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema } from "./service-ufH7sS8o.mjs";
3
+ import { a as parseBoolean, n as logger, r as styles } from "./logger-BwS4ppwO.mjs";
4
+ import { a as AuthConfigSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, g as functionSchema, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema } from "./service-CnHz9rwz.mjs";
6
5
  import { t as multiline } from "./multiline-sfHpTZZK.mjs";
7
- import { t as readPackageJson } from "./package-json-8b0O9TlX.mjs";
8
- import { i as userAgent, n as tightenSecretFilePermissions, r as writeSecretFile } from "./secret-file-aoNXJmKa.mjs";
9
- import { t as createCLIError } from "./errors-118wUDRr.mjs";
6
+ import { t as userAgent } from "./user-agent-Bgsszb5I.mjs";
10
7
  import { t as isPluginGeneratedType } from "./type-source-DH_LH20p.mjs";
11
8
  import { builtinModules, createRequire } from "node:module";
12
9
  import * as path from "pathe";
13
- import { join, resolve } from "pathe";
10
+ import { resolve } from "pathe";
14
11
  import { resolveTSConfig } from "pkg-types";
15
- import { arg } from "politty";
16
12
  import { z } from "zod";
17
13
  import { create } from "@bufbuild/protobuf";
18
- import * as fs$1 from "node:fs";
19
- import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
20
- import { parseEnv } from "node:util";
14
+ import * as fs from "node:fs";
15
+ import { readFileSync } from "node:fs";
21
16
  import { MethodOptions_IdempotencyLevel } from "@bufbuild/protobuf/wkt";
22
17
  import { pathToFileURL } from "node:url";
23
- import { parseYAML, stringifyYAML } from "confbox";
24
- import { findUpSync } from "find-up-simple";
25
- import { lt } from "semver";
26
- import { xdgConfig } from "xdg-basedir";
27
18
  import { OAuth2Client } from "@badgateway/oauth2-client";
28
19
  import { Code, ConnectError, createClient } from "@connectrpc/connect";
29
20
  import { getGlobalDispatcher } from "undici";
30
21
  import pLimit from "p-limit";
31
22
  import * as crypto from "node:crypto";
32
23
  import * as rolldown from "rolldown";
33
- import * as fs from "node:fs/promises";
34
24
  import { parseSync } from "oxc-parser";
35
25
  import * as inflection from "inflection";
36
26
  import * as globals from "globals";
37
27
  import * as os from "node:os";
38
28
 
39
- //#region src/cli/shared/args.ts
40
- const unitToMs = {
41
- ms: 1,
42
- s: 1e3,
43
- m: 60 * 1e3
44
- };
45
- const durationPattern = /^(\d+)(ms|s|m)$/;
46
- /**
47
- * Schema for duration string validation (e.g., "3s", "500ms", "1m")
48
- * Only validates format; use parseDuration() to convert to milliseconds
49
- */
50
- const durationArg = z.string().refine((val) => durationPattern.test(val), { message: "Invalid duration format. Expected format: '3s', '500ms', '1m'" }).refine((val) => {
51
- const match = val.match(durationPattern);
52
- if (!match) return false;
53
- const digits = match[1];
54
- return digits !== void 0 && parseInt(digits, 10) > 0;
55
- }, { message: "Duration must be greater than 0" });
56
- /**
57
- * Parse a validated duration string into milliseconds
58
- * @param duration - Duration string (e.g., "3s", "500ms", "1m")
59
- * @returns Duration in milliseconds
60
- */
61
- function parseDuration(duration) {
62
- const match = assertDefined(duration.match(durationPattern), `invalid duration format: ${duration}`);
63
- const value = parseInt(assertDefined(match[1], "duration digits group missing"), 10);
64
- const unit = assertDefined(match[2], "duration unit group missing");
65
- return value * unitToMs[unit];
66
- }
67
- /**
68
- * Schema for positive integer validation (from string input)
69
- * Transforms the string to a number
70
- */
71
- const positiveIntArg = z.coerce.number().int().positive();
72
- /**
73
- * Schema for non-negative integer validation (from string input).
74
- * Accepts 0 (used for `--limit 0` to disable the limit).
75
- */
76
- const nonNegativeIntArg = z.coerce.number().int().nonnegative();
77
- /**
78
- * Schema for sort order (`asc` or `desc`).
79
- */
80
- const orderArg = z.enum(["asc", "desc"]);
81
- /**
82
- * Translate a CLI `--order` value into the proto `PageDirection` enum.
83
- * Returns `undefined` when the user did not specify an order so that
84
- * callers can omit the field and fall back to the server default.
85
- * @param order - Order string from CLI args (`"asc"` | `"desc"` | undefined)
86
- * @returns PageDirection, or undefined when `order` is undefined
87
- */
88
- function toPageDirection(order) {
89
- if (order === void 0) return void 0;
90
- return order === "asc" ? PageDirection.ASC : PageDirection.DESC;
91
- }
92
- function hasMachineUserFlag(argv) {
93
- return argv.slice(0, argv.indexOf("--") === -1 ? argv.length : argv.indexOf("--")).some((token) => token === "-m" || token.startsWith("-m=") || token === "--machine-user" || token.startsWith("--machine-user=") || token === "--machineUser" || token.startsWith("--machineUser=") || token === "--machineuser" || token.startsWith("--machineuser="));
94
- }
95
- /**
96
- * Resolve whether a parsed machine user value came from an explicit CLI option or env fallback.
97
- * @param machineUser - Parsed machine user value
98
- * @param argv - Raw CLI argv, excluding the executable and script path
99
- * @param options - Source resolution options
100
- * @returns Machine user input source, or undefined when no value was parsed
101
- */
102
- function resolveMachineUserInputSource(machineUser, argv = process.argv.slice(2), options = {}) {
103
- if (machineUser === void 0) return void 0;
104
- if (options.valueIsExplicit) return "option";
105
- if (hasMachineUserFlag(argv)) return "option";
106
- return process.env.TAILOR_PLATFORM_MACHINE_USER_NAME === machineUser ? "env" : "option";
107
- }
108
- /**
109
- * Load env files from parsed arguments.
110
- * Processes --env-file first, then --env-file-if-exists.
111
- *
112
- * Follows Node.js --env-file behavior:
113
- * - Variables already set in the environment are NOT overwritten
114
- * - Variables from later files override those from earlier files
115
- * @param envFiles - Required env file path(s) that must exist
116
- * @param envFilesIfExists - Optional env file path(s) that are loaded if they exist
117
- */
118
- function loadEnvFiles(envFiles, envFilesIfExists) {
119
- const originalEnvKeys = new Set(Object.keys(process.env));
120
- const load = (files, required) => {
121
- for (const file of [files ?? []].flat()) {
122
- const envPath = path.resolve(process.cwd(), file);
123
- if (!fs$1.existsSync(envPath)) {
124
- if (required) throw new Error(`Environment file not found: ${envPath}`);
125
- continue;
126
- }
127
- const parsed = parseEnv(fs$1.readFileSync(envPath, "utf-8"));
128
- for (const [key, value] of Object.entries(parsed)) {
129
- if (originalEnvKeys.has(key)) continue;
130
- process.env[key] = value;
131
- }
132
- }
133
- };
134
- load(envFiles, true);
135
- load(envFilesIfExists, false);
136
- }
137
- /**
138
- * Common arguments for all CLI commands
139
- *
140
- * NOTE: --env-file and --env-file-if-exists collide with Node.js flags due to a bug
141
- * (https://github.com/nodejs/node/issues/54232). Node.js parses these even after the
142
- * script path, causing warnings (twice due to tsx loader).
143
- */
144
- const commonArgs = {
145
- "env-file": arg(z.string().optional(), {
146
- alias: "e",
147
- description: "Path to the environment file (error if not found)",
148
- completion: {
149
- type: "file",
150
- matcher: [".env.*", ".env"]
151
- }
152
- }),
153
- "env-file-if-exists": arg(z.string().optional(), {
154
- description: "Path to the environment file (ignored if not found)",
155
- completion: {
156
- type: "file",
157
- matcher: [".env.*", ".env"]
158
- },
159
- effect: (_value, { args }) => {
160
- loadEnvFiles(args["env-file"], args["env-file-if-exists"]);
161
- }
162
- }),
163
- verbose: arg(z.boolean().default(false), {
164
- description: "Enable verbose logging",
165
- effect: (value) => {
166
- verboseMode = value;
167
- }
168
- }),
169
- json: arg(z.boolean().default(false), {
170
- alias: "j",
171
- description: "Output as JSON",
172
- effect: (value) => {
173
- logger.jsonMode = value;
174
- }
175
- })
176
- };
177
- /**
178
- * Arguments for commands that require workspace context
179
- */
180
- const workspaceArgs = {
181
- "workspace-id": arg(z.string().optional(), {
182
- alias: "w",
183
- description: "Workspace ID",
184
- env: "TAILOR_PLATFORM_WORKSPACE_ID",
185
- completion: { type: "none" }
186
- }),
187
- profile: arg(z.string().optional(), {
188
- alias: "p",
189
- description: "Workspace profile",
190
- env: "TAILOR_PLATFORM_PROFILE",
191
- completion: { type: "none" }
192
- })
193
- };
194
- /**
195
- * Shared config arg for commands that accept a config file path
196
- */
197
- const configArg = { config: arg(z.string().default("tailor.config.ts"), {
198
- alias: "c",
199
- description: "Path to Tailor config file",
200
- env: "TAILOR_CONFIG_PATH",
201
- completion: {
202
- type: "file",
203
- extensions: ["ts"]
204
- }
205
- }) };
206
- /**
207
- * Shared config arg for commands that accept one or more comma-separated config file paths
208
- */
209
- const multiConfigArg = { config: arg(z.string().default("tailor.config.ts"), {
210
- alias: "c",
211
- description: "Path to SDK config file. Use comma-separated paths to deploy multiple apps together.",
212
- env: "TAILOR_PLATFORM_SDK_CONFIG_PATH",
213
- completion: {
214
- type: "file",
215
- extensions: ["ts"]
216
- }
217
- }) };
218
- /**
219
- * Arguments for commands that interact with deployed resources (includes config)
220
- */
221
- const deploymentArgs = {
222
- ...workspaceArgs,
223
- ...configArg
224
- };
225
- /**
226
- * Arguments for commands that require confirmation
227
- */
228
- const confirmationArgs = { yes: arg(z.boolean().default(false), {
229
- alias: "y",
230
- description: "Skip confirmation prompts"
231
- }) };
232
- /**
233
- * Arguments for commands that require organization context
234
- */
235
- const organizationArgs = { "organization-id": arg(z.string(), {
236
- alias: "o",
237
- description: "Organization ID",
238
- env: "TAILOR_PLATFORM_ORGANIZATION_ID",
239
- completion: { type: "none" }
240
- }) };
241
- /**
242
- * Arguments for list commands that accept `--order` / `--limit`. Sort
243
- * order defaults to `desc` (newest first) because most callers want the
244
- * latest items; pass `--order asc` to opt in to ascending order. The
245
- * limit is unbounded by default so existing invocations keep returning
246
- * every item; pass `--limit N` to cap the result size.
247
- * @param defaultOrder - Default value for `--order` (defaults to `"desc"`)
248
- * @returns Argument shape suitable for spreading into a command schema
249
- */
250
- const paginationArgs = (defaultOrder = "desc") => ({
251
- order: arg(orderArg.default(defaultOrder), { description: "Sort order (asc or desc)" }),
252
- limit: arg(nonNegativeIntArg.optional(), {
253
- alias: "l",
254
- description: "Maximum number of items to return (0 or omit: unlimited)"
255
- })
256
- });
257
- /**
258
- * Arguments for time-series log list commands. Defaults to newest-first
259
- * (`desc`) and a 50-item cap so that listing stays responsive on busy
260
- * workspaces. Pass `--limit 0` to disable the cap and fetch all entries.
261
- */
262
- const pagedLogArgs = {
263
- order: arg(orderArg.default("desc"), { description: "Sort order (asc or desc)" }),
264
- limit: arg(nonNegativeIntArg.default(50), {
265
- alias: "l",
266
- description: "Maximum number of items to return (0: unlimited)"
267
- })
268
- };
269
- /**
270
- * Arguments for commands that require folder context
271
- */
272
- const folderArgs = { "folder-id": arg(z.string(), {
273
- alias: "f",
274
- description: "Folder ID",
275
- env: "TAILOR_PLATFORM_FOLDER_ID",
276
- completion: { type: "none" }
277
- }) };
278
- let verboseMode = false;
279
- /**
280
- * Returns whether verbose mode is enabled.
281
- * Used by the global cleanup handler which doesn't have access to parsed args.
282
- * @returns Whether verbose mode is enabled
283
- */
284
- function isVerbose() {
285
- return verboseMode;
286
- }
287
-
288
- //#endregion
289
29
  //#region src/parser/app-config/log-level.ts
290
30
  const LOG_LEVELS = [
291
31
  "DEBUG",
@@ -298,64 +38,6 @@ function isLogLevel(value) {
298
38
  return LOG_LEVELS.includes(value);
299
39
  }
300
40
 
301
- //#endregion
302
- //#region src/parser/app-config/schema.ts
303
- const envValueSchema = z.union([
304
- z.string(),
305
- z.number(),
306
- z.boolean()
307
- ]);
308
- const LogLevelSchema = z.enum(LOG_LEVELS);
309
- const logLevelSchema = z.string().refine((value) => LogLevelSchema.safeParse(value.trim().toUpperCase()).success, { message: `'logLevel' must be one of: ${LOG_LEVELS.join(", ")}.` });
310
- /**
311
- * Structural validation schema for `defineConfig({...})`. Validates only
312
- * top-level fields with platform-side constraints (notably `id`); fields
313
- * that carry SDK builder objects (`auth`, `idp`, `db`, ...) are accepted
314
- * as opaque values, since their internal shapes are validated by their
315
- * own factory functions and parser-level schemas.
316
- *
317
- * The `id` is auto-managed by `deploy` and stored as a plain UUID. A
318
- * label-compatible prefix is added at the metadata boundary, so user-facing
319
- * configs only need to carry a UUID.
320
- */
321
- const AppConfigSchema = z.strictObject({
322
- id: z.uuid({ message: "'id' must be a UUID." }).optional(),
323
- name: z.string().min(1, { message: "'name' must be a non-empty string." }),
324
- env: z.record(z.string(), envValueSchema).optional(),
325
- cors: z.array(z.string()).optional(),
326
- allowedIpAddresses: z.array(z.string()).optional(),
327
- disableIntrospection: z.boolean().optional(),
328
- inlineSourcemap: z.boolean().optional(),
329
- logLevel: logLevelSchema.optional(),
330
- db: z.unknown().optional(),
331
- resolver: z.unknown().optional(),
332
- idp: z.unknown().optional(),
333
- auth: z.unknown().optional(),
334
- executor: z.unknown().optional(),
335
- workflow: z.unknown().optional(),
336
- httpAdapter: z.unknown().optional(),
337
- staticWebsites: z.unknown().optional(),
338
- aiGateways: z.unknown().optional(),
339
- secrets: z.unknown().optional()
340
- });
341
-
342
- //#endregion
343
- //#region src/parser/plugin-config/schema.ts
344
- const PluginConfigSchema = z.looseObject({
345
- id: z.string(),
346
- description: z.string(),
347
- importPath: z.string().optional(),
348
- pluginConfig: z.unknown().optional(),
349
- typeConfigRequired: z.union([z.boolean(), functionSchema]).optional(),
350
- onTypeLoaded: functionSchema.optional(),
351
- onNamespaceLoaded: functionSchema.optional(),
352
- onTailorDBReady: functionSchema.optional(),
353
- onResolverReady: functionSchema.optional(),
354
- onExecutorReady: functionSchema.optional()
355
- }).refine((p) => {
356
- return !(p.onTypeLoaded || p.onNamespaceLoaded) || !!p.importPath;
357
- }, { message: "importPath is required when plugin has definition-time hooks (onTypeLoaded/onNamespaceLoaded)" }).transform((plugin) => plugin);
358
-
359
41
  //#endregion
360
42
  //#region src/cli/shared/apply-concurrency.ts
361
43
  /**
@@ -498,7 +180,7 @@ function initOAuth2Client(config) {
498
180
  */
499
181
  async function initOperatorClient(accessToken, config) {
500
182
  const platformConfig = config ?? getPlatformConfigForToken(accessToken);
501
- const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-B2FF59sP.mjs")]);
183
+ const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-r1T7kpJc.mjs")]);
502
184
  const interceptors = [
503
185
  await userAgentInterceptor(),
504
186
  await bearerTokenInterceptor(accessToken),
@@ -551,9 +233,9 @@ async function bearerTokenInterceptor(accessToken) {
551
233
  /**
552
234
  * Create an interceptor that retries failed unary requests with backoff.
553
235
  *
554
- * Retries any unary method on `Unavailable`/`ResourceExhausted`, and `Internal`
555
- * only for methods declared idempotent, up to 3 attempts (despite the historical
556
- * "idempotent" naming, the first two codes are retried regardless of idempotency).
236
+ * Retries unary methods on `Unavailable`/`ResourceExhausted`, and `Internal`
237
+ * only for methods declared idempotent, up to 3 attempts. Workspace creation is
238
+ * excluded because it has no idempotency key and a lost response is ambiguous.
557
239
  * As a targeted exception for the deploy/apply flow, a post-retry `AlreadyExists`
558
240
  * from an allowlisted Create (see `RETRY_SAFE_CREATE_METHODS`) is treated as
559
241
  * success, since it means a prior attempt already committed the resource
@@ -578,10 +260,10 @@ function retryInterceptor() {
578
260
  logger.debug(`retry: ${req.method.name} returned AlreadyExists on attempt ${i + 1}; treating as success (prior attempt likely committed)`);
579
261
  return synthesizeEmptyUnaryResponse(req);
580
262
  }
581
- const { reportCrash } = await import("./crashreport-D3RjuSYb.mjs");
263
+ const { reportCrash } = await import("./crashreport-0ioNRZ4K.mjs");
582
264
  await reportCrash(error, "handledError");
583
265
  }
584
- if (isRetirable(error, req.method.idempotency)) {
266
+ if (req.method.name !== "CreateWorkspace" && isRetirable(error, req.method.idempotency)) {
585
267
  lastError = error;
586
268
  logger.debug(`retry: ${req.method.name} attempt ${i + 1} failed with ${connectCodeName(error)}; retrying`);
587
269
  continue;
@@ -986,806 +668,6 @@ async function closeConnectionPool() {
986
668
  await getGlobalDispatcher().close();
987
669
  }
988
670
 
989
- //#endregion
990
- //#region src/cli/shared/token-store.ts
991
- const SERVICE_NAME = "tailor-platform-cli";
992
- let entryClass;
993
- async function getEntryClass() {
994
- if (entryClass !== void 0) return entryClass;
995
- try {
996
- const mod = await import("@napi-rs/keyring");
997
- const probe = new mod.Entry(SERVICE_NAME, "__probe__");
998
- probe.setPassword("probe");
999
- probe.deletePassword();
1000
- entryClass = mod.Entry;
1001
- } catch {
1002
- logger.warn("System keyring is not available. Tokens will be stored in the config file. Set TAILOR_PLATFORM_TOKEN environment variable for CI environments.");
1003
- entryClass = false;
1004
- }
1005
- return entryClass;
1006
- }
1007
- /**
1008
- * Check whether the OS keyring is available and functional.
1009
- * @returns true if keyring is available
1010
- */
1011
- async function isKeyringAvailable() {
1012
- return await getEntryClass() !== false;
1013
- }
1014
- /**
1015
- * Load tokens from the OS keyring for a given account.
1016
- * @param account - User identifier (e.g. email or client ID)
1017
- * @returns Token data or undefined if not found or keyring unavailable
1018
- */
1019
- async function loadKeyringTokens(account) {
1020
- const Entry = await getEntryClass();
1021
- if (!Entry) return void 0;
1022
- try {
1023
- const raw = new Entry(SERVICE_NAME, account).getPassword();
1024
- if (raw === null) return void 0;
1025
- return JSON.parse(raw);
1026
- } catch {
1027
- return;
1028
- }
1029
- }
1030
- /**
1031
- * Save tokens to the OS keyring for a given account.
1032
- * @param account - User identifier (e.g. email or client ID)
1033
- * @param tokens - Token data to store
1034
- */
1035
- async function saveKeyringTokens(account, tokens) {
1036
- const Entry = await getEntryClass();
1037
- if (!Entry) return;
1038
- new Entry(SERVICE_NAME, account).setPassword(JSON.stringify(tokens));
1039
- }
1040
- /**
1041
- * Delete tokens from the OS keyring for a given account.
1042
- * @param account - User identifier (e.g. email or client ID)
1043
- */
1044
- async function deleteKeyringTokens(account) {
1045
- const Entry = await getEntryClass();
1046
- if (!Entry) return;
1047
- try {
1048
- new Entry(SERVICE_NAME, account).deletePassword();
1049
- } catch {}
1050
- }
1051
-
1052
- //#endregion
1053
- //#region src/cli/shared/context.ts
1054
- const pfProfileSchema = z.object({
1055
- user: z.string(),
1056
- workspace_id: z.string(),
1057
- readonly: z.boolean().optional(),
1058
- machine_user: z.string().optional(),
1059
- machine_user_override: z.enum(["allow", "deny"]).optional(),
1060
- platform_url: z.url().optional(),
1061
- oauth2_client_id: z.string().optional(),
1062
- console_url: z.url().optional()
1063
- });
1064
- const pfUserSchemaV1 = z.object({
1065
- access_token: z.string(),
1066
- refresh_token: z.string().optional(),
1067
- token_expires_at: z.string()
1068
- });
1069
- const pfUserKeyringSchema = z.object({
1070
- storage: z.literal("keyring"),
1071
- token_expires_at: z.string()
1072
- });
1073
- const pfUserFileSchema = z.object({
1074
- storage: z.literal("file"),
1075
- token_expires_at: z.string(),
1076
- access_token: z.string(),
1077
- refresh_token: z.string().optional()
1078
- });
1079
- const pfUserSchemaV2 = z.discriminatedUnion("storage", [pfUserKeyringSchema, pfUserFileSchema]);
1080
- const pfUserKeyringSchemaV3 = pfUserKeyringSchema.extend({ email: z.string().optional() });
1081
- const pfUserFileSchemaV3 = pfUserFileSchema.extend({ email: z.string().optional() });
1082
- const pfUserSchemaV3 = z.discriminatedUnion("storage", [pfUserKeyringSchemaV3, pfUserFileSchemaV3]);
1083
- const pfConfigSchemaV1 = z.object({
1084
- version: z.literal(1),
1085
- users: z.partialRecord(z.string(), pfUserSchemaV1),
1086
- profiles: z.partialRecord(z.string(), pfProfileSchema),
1087
- current_user: z.string().nullable()
1088
- });
1089
- const V2_CONFIG_VERSION = 2;
1090
- const LATEST_CONFIG_VERSION = 3;
1091
- const V2_MIN_SDK_VERSION = "1.29.0";
1092
- const V3_MIN_SDK_VERSION = "2.0.0";
1093
- const semverSchema = z.templateLiteral([
1094
- z.number().int(),
1095
- ".",
1096
- z.number().int(),
1097
- ".",
1098
- z.number().int()
1099
- ]);
1100
- const pfConfigSchemaV2 = z.object({
1101
- version: z.literal(V2_CONFIG_VERSION),
1102
- min_sdk_version: semverSchema,
1103
- latest_version: z.number().int().optional(),
1104
- latest_min_sdk_version: semverSchema.optional(),
1105
- users: z.partialRecord(z.string(), pfUserSchemaV2),
1106
- profiles: z.partialRecord(z.string(), pfProfileSchema),
1107
- current_user: z.string().nullable()
1108
- });
1109
- const pfConfigSchemaV3 = z.object({
1110
- version: z.literal(LATEST_CONFIG_VERSION),
1111
- min_sdk_version: semverSchema,
1112
- latest_version: z.number().int().optional(),
1113
- latest_min_sdk_version: semverSchema.optional(),
1114
- users: z.partialRecord(z.string(), pfUserSchemaV3),
1115
- profiles: z.partialRecord(z.string(), pfProfileSchema),
1116
- current_user: z.string().nullable()
1117
- });
1118
- function platformConfigPath() {
1119
- if (!xdgConfig) throw new Error("User home directory not found");
1120
- return path.join(xdgConfig, "tailor-platform", "config.yaml");
1121
- }
1122
- /**
1123
- * Convert stored profile platform fields to platform client settings.
1124
- * @param profile - Profile platform settings
1125
- * @returns Platform client settings
1126
- */
1127
- function platformConfigFromProfile(profile) {
1128
- const config = {
1129
- ...profile?.platform_url ? { platformUrl: profile.platform_url } : {},
1130
- ...profile?.oauth2_client_id ? { oauth2ClientId: profile.oauth2_client_id } : {},
1131
- ...profile?.console_url ? { consoleUrl: profile.console_url } : {}
1132
- };
1133
- return Object.keys(config).length > 0 ? config : void 0;
1134
- }
1135
- function platformUserKey(user, config) {
1136
- const platformUrl = getPlatformBaseUrl(config);
1137
- if (platformUrl === normalizeBaseUrl("https://api.tailor.tech")) return user;
1138
- return `${platformUrl}|${user}`;
1139
- }
1140
- function userFromPlatformUserKey(userKey, config) {
1141
- const platformPrefix = `${getPlatformBaseUrl(config)}|`;
1142
- return userKey.startsWith(platformPrefix) ? userKey.slice(platformPrefix.length) : userKey;
1143
- }
1144
- function canUseLegacyUserKey(platformUrl) {
1145
- try {
1146
- return getPlatformBaseUrl() === platformUrl;
1147
- } catch {
1148
- return false;
1149
- }
1150
- }
1151
- function findUserByEmail(users, email, platformConfig) {
1152
- const platformUrl = getPlatformBaseUrl(platformConfig);
1153
- const platformPrefix = `${platformUrl}|`;
1154
- const defaultPlatform = platformUrl === normalizeBaseUrl(defaultPlatformBaseUrl);
1155
- return Object.entries(users).find(([key, entry]) => {
1156
- if (entry?.email !== email) return false;
1157
- return defaultPlatform ? !key.includes("|") : key.startsWith(platformPrefix);
1158
- });
1159
- }
1160
- function findUserEntry(config, user, platformConfig, opts = {}) {
1161
- const userKey = platformUserKey(user, platformConfig);
1162
- const userEntry = config.users[userKey];
1163
- if (userEntry) return {
1164
- userKey,
1165
- userEntry
1166
- };
1167
- const emailMatch = findUserByEmail(config.users, user, platformConfig);
1168
- if (emailMatch?.[1]) return {
1169
- userKey: emailMatch[0],
1170
- userEntry: emailMatch[1]
1171
- };
1172
- const platformUrl = getPlatformBaseUrl(platformConfig);
1173
- if (userKey !== user && (opts.allowLegacyUserKey === false || !canUseLegacyUserKey(platformUrl))) return {
1174
- userKey,
1175
- userEntry
1176
- };
1177
- const legacyEntry = config.users[user];
1178
- return legacyEntry ? {
1179
- userKey: user,
1180
- userEntry: legacyEntry
1181
- } : {
1182
- userKey,
1183
- userEntry
1184
- };
1185
- }
1186
- /**
1187
- * Resolve the config user key that would be used for a user on the selected platform.
1188
- * @param config - Platform config
1189
- * @param user - User name
1190
- * @param platformConfig - Optional platform connection settings
1191
- * @param opts - Token lookup options
1192
- * @returns Resolved config user key
1193
- */
1194
- function resolveUserTokenKey(config, user, platformConfig, opts) {
1195
- return findUserEntry(config, user, platformConfig, opts).userKey;
1196
- }
1197
- function resolveConfigUser(config, user, platformConfig, opts) {
1198
- const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
1199
- return userEntry ? userFromPlatformUserKey(userKey, platformConfig) : void 0;
1200
- }
1201
- /**
1202
- * Check whether tokens are registered for a user on the selected platform.
1203
- * @param config - Platform config
1204
- * @param user - User name
1205
- * @param platformConfig - Optional platform connection settings
1206
- * @param opts - Token lookup options
1207
- * @returns True when the user has a registered token entry
1208
- */
1209
- function hasUserTokenEntry(config, user, platformConfig, opts) {
1210
- return findUserEntry(config, user, platformConfig, opts).userEntry !== void 0;
1211
- }
1212
- function hasUserKeyForName(users, user) {
1213
- return users[user] !== void 0 || Object.keys(users).some((key) => key.endsWith(`|${user}`));
1214
- }
1215
- function hasUserEmailEntry(users, user) {
1216
- return Object.values(users).some((entry) => entry?.email === user);
1217
- }
1218
- /**
1219
- * Check whether any platform has tokens registered for a user.
1220
- * @param config - Platform config
1221
- * @param user - User name
1222
- * @returns True when the user has a token entry for any platform
1223
- */
1224
- function hasAnyUserTokenEntry(config, user) {
1225
- return hasUserKeyForName(config.users, user) || hasUserEmailEntry(config.users, user);
1226
- }
1227
- function hasCurrentUserEntry(users, currentUser) {
1228
- return hasUserKeyForName(users, currentUser);
1229
- }
1230
- /**
1231
- * Migrate a v1 config to v2.
1232
- * Tokens are kept in the config file (storage: "file") during migration.
1233
- * They will be moved to the OS keyring on next login or token refresh.
1234
- * @param v1Config - v1 configuration to migrate
1235
- * @returns Migrated v2 configuration
1236
- */
1237
- function migrateV1ToV2(v1Config) {
1238
- const users = {};
1239
- for (const [name, v1User] of Object.entries(v1Config.users)) {
1240
- if (!v1User) continue;
1241
- users[name] = {
1242
- access_token: v1User.access_token,
1243
- refresh_token: v1User.refresh_token,
1244
- token_expires_at: v1User.token_expires_at,
1245
- storage: "file"
1246
- };
1247
- }
1248
- return {
1249
- version: V2_CONFIG_VERSION,
1250
- min_sdk_version: V2_MIN_SDK_VERSION,
1251
- users,
1252
- profiles: v1Config.profiles,
1253
- current_user: v1Config.current_user
1254
- };
1255
- }
1256
- function inferEmailFromUserId(user) {
1257
- return z.email().safeParse(user).success ? user : void 0;
1258
- }
1259
- function migrateV2ToV3(v2Config) {
1260
- const users = {};
1261
- for (const [user, entry] of Object.entries(v2Config.users)) {
1262
- if (!entry) continue;
1263
- const email = inferEmailFromUserId(user);
1264
- users[user] = {
1265
- ...entry,
1266
- ...email ? { email } : {}
1267
- };
1268
- }
1269
- return {
1270
- version: LATEST_CONFIG_VERSION,
1271
- min_sdk_version: V3_MIN_SDK_VERSION,
1272
- users,
1273
- profiles: v2Config.profiles,
1274
- current_user: v2Config.current_user
1275
- };
1276
- }
1277
- function migrateV1ToV3(v1Config) {
1278
- return migrateV2ToV3(migrateV1ToV2(v1Config));
1279
- }
1280
- function formatUnknownError(error) {
1281
- return error instanceof Error ? error.message : String(error);
1282
- }
1283
- async function trySaveTokensInKeyring(user, tokens) {
1284
- if (!await isKeyringAvailable()) return false;
1285
- try {
1286
- await saveKeyringTokens(user, tokens);
1287
- return true;
1288
- } catch (error) {
1289
- logger.warn(`System keyring failed to store credentials. Tokens will be stored in the config file. ${formatUnknownError(error)}`);
1290
- return false;
1291
- }
1292
- }
1293
- async function warnIfNewerConfigAvailable(config) {
1294
- if (!config.latest_min_sdk_version) return;
1295
- if (lt((await readPackageJson()).version ?? "0.0.0", config.latest_min_sdk_version)) logger.warn(multiline`
1296
- A newer config version (${String(config.latest_version)}) is available.
1297
- Please update your SDK to >= ${config.latest_min_sdk_version}: pnpm update @tailor-platform/sdk
1298
- `);
1299
- }
1300
- /**
1301
- * Read Tailor Platform CLI configuration, migrating from v1 if necessary.
1302
- * @returns Parsed platform configuration
1303
- */
1304
- async function readPlatformConfig() {
1305
- const configPath = platformConfigPath();
1306
- if (!fs$1.existsSync(configPath)) {
1307
- const config = {
1308
- version: LATEST_CONFIG_VERSION,
1309
- min_sdk_version: V3_MIN_SDK_VERSION,
1310
- users: {},
1311
- profiles: {},
1312
- current_user: null
1313
- };
1314
- writePlatformConfig(config);
1315
- return config;
1316
- }
1317
- const rawConfig = parseYAML(fs$1.readFileSync(configPath, "utf-8"));
1318
- tightenSecretFilePermissions(configPath);
1319
- const version = rawConfig != null && typeof rawConfig === "object" && "version" in rawConfig ? rawConfig.version : void 0;
1320
- if (typeof version === "number" && version > LATEST_CONFIG_VERSION) {
1321
- const minSdk = "min_sdk_version" in rawConfig ? String(rawConfig.min_sdk_version) : void 0;
1322
- const updateHint = minSdk ? `Please update your SDK to >= ${minSdk}: pnpm update @tailor-platform/sdk` : "Please update your SDK: pnpm update @tailor-platform/sdk";
1323
- throw new Error(multiline`
1324
- Config file uses version ${String(version)}, but this SDK only supports up to version ${String(LATEST_CONFIG_VERSION)}.
1325
- ${updateHint}
1326
- `);
1327
- }
1328
- const v3Result = pfConfigSchemaV3.safeParse(rawConfig);
1329
- if (v3Result.success) {
1330
- await warnIfNewerConfigAvailable(v3Result.data);
1331
- return v3Result.data;
1332
- }
1333
- const v2Result = pfConfigSchemaV2.safeParse(rawConfig);
1334
- if (v2Result.success) {
1335
- await warnIfNewerConfigAvailable(v2Result.data);
1336
- return migrateV2ToV3(v2Result.data);
1337
- }
1338
- const v1Result = pfConfigSchemaV1.safeParse(rawConfig);
1339
- if (v1Result.success) return migrateV1ToV3(v1Result.data);
1340
- throw new Error(multiline`
1341
- Failed to parse config file at ${configPath}.
1342
- The file may be corrupted or created by an incompatible SDK version.
1343
- `);
1344
- }
1345
- function toV1ForDisk(config) {
1346
- const users = {};
1347
- for (const [name, entry] of Object.entries(config.users)) {
1348
- if (!entry || entry.storage === "keyring") continue;
1349
- users[name] = {
1350
- access_token: entry.access_token,
1351
- refresh_token: entry.refresh_token,
1352
- token_expires_at: entry.token_expires_at
1353
- };
1354
- }
1355
- const currentUser = config.current_user && hasCurrentUserEntry(users, config.current_user) ? config.current_user : null;
1356
- return {
1357
- version: 1,
1358
- users,
1359
- profiles: config.profiles,
1360
- current_user: currentUser
1361
- };
1362
- }
1363
- function hasProfilePlatformSettings(config) {
1364
- return Object.values(config.profiles).some((profile) => profile?.platform_url !== void 0 || profile?.oauth2_client_id !== void 0 || profile?.console_url !== void 0);
1365
- }
1366
- function hasScopedUserKeys(config) {
1367
- return Object.keys(config.users).some((userKey) => userKey.includes("|"));
1368
- }
1369
- function hasUserEmailMetadata(config) {
1370
- return Object.values(config.users).some((user) => user != null && "email" in user && user.email !== void 0);
1371
- }
1372
- function toLatestForDisk(config) {
1373
- return {
1374
- ...config.version === 1 ? migrateV1ToV2(config) : config,
1375
- version: LATEST_CONFIG_VERSION,
1376
- min_sdk_version: V3_MIN_SDK_VERSION
1377
- };
1378
- }
1379
- /**
1380
- * Write Tailor Platform CLI configuration to disk.
1381
- * By default, file-backed configs without newer fields are converted to V1 for
1382
- * backward compatibility, so an older SDK can still read the file. Configs
1383
- * containing a keyring user are kept in V2 or later because the keyring storage
1384
- * variant is not representable in V1. Configs containing profile-level Platform
1385
- * settings, platform-scoped user tokens, canonical user IDs, or email metadata
1386
- * are written in the latest min-SDK-gated format because older SDKs would
1387
- * silently drop or misread those settings.
1388
- *
1389
- * The config file may contain access/refresh tokens when the OS keyring is
1390
- * unavailable, so it is written via {@link writeSecretFile} so other users
1391
- * on the host cannot read it.
1392
- * @param config - Platform configuration to write
1393
- */
1394
- function writePlatformConfig(config) {
1395
- const configPath = platformConfigPath();
1396
- const hasKeyringUser = config.version !== 1 && Object.values(config.users).some((u) => u?.storage === "keyring");
1397
- writeSecretFile(configPath, stringifyYAML(config.version === LATEST_CONFIG_VERSION || hasProfilePlatformSettings(config) || hasScopedUserKeys(config) || hasUserEmailMetadata(config) ? toLatestForDisk(config) : config.version === V2_CONFIG_VERSION && !hasKeyringUser ? toV1ForDisk(config) : config));
1398
- }
1399
- function validateUUID(value, source) {
1400
- const result = z.uuid().safeParse(value);
1401
- if (!result.success) throw new Error(`Invalid value from ${source}: must be a valid UUID`);
1402
- return result.data;
1403
- }
1404
- /**
1405
- * Load workspace ID from command options, environment variables, or platform config.
1406
- * In CLI context, env fallback is also handled by politty's arg env option.
1407
- * Priority: opts/workspaceId > env/workspaceId > opts/profile > error
1408
- * @param opts - Workspace and profile options
1409
- * @returns Resolved workspace ID
1410
- */
1411
- async function loadWorkspaceId(opts) {
1412
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1413
- if (opts?.workspaceId) return validateUUID(opts.workspaceId, "--workspace-id option");
1414
- if (process.env.TAILOR_PLATFORM_WORKSPACE_ID) return validateUUID(process.env.TAILOR_PLATFORM_WORKSPACE_ID, "TAILOR_PLATFORM_WORKSPACE_ID environment variable");
1415
- if (profile) {
1416
- const wsId = (await readPlatformConfig()).profiles[profile]?.workspace_id;
1417
- if (!wsId) throw new Error(`Profile "${profile}" not found`);
1418
- return validateUUID(wsId, `profile "${profile}"`);
1419
- }
1420
- throw new Error(multiline`
1421
- Workspace ID not found.
1422
- Please specify workspace ID via --workspace-id option or TAILOR_PLATFORM_WORKSPACE_ID environment variable.
1423
- `);
1424
- }
1425
- /**
1426
- * Load machine user name from command options, environment variables, or platform config.
1427
- * In CLI context, env fallback is also handled by politty's arg env option.
1428
- * Priority: opts/machineUser > env/TAILOR_PLATFORM_MACHINE_USER_NAME > opts/profile (profile default) > undefined.
1429
- * An explicitly empty `opts.machineUser` is rejected with a CLIError (`MACHINE_USER_NAME_EMPTY`) rather than falling back to the env var or profile default.
1430
- * When the active profile has `machine_user_override: "deny"`, an explicit value that differs from the profile's machine user throws a CLIError with code `PROFILE_MACHINE_USER_OVERRIDE_DENIED`.
1431
- * @param opts - Machine user and profile options
1432
- * @returns Resolved machine user name, or undefined if not set
1433
- */
1434
- async function loadMachineUserName(opts) {
1435
- if (opts?.machineUser === "") throw createCLIError({
1436
- code: "MACHINE_USER_NAME_EMPTY",
1437
- message: "Machine user name cannot be empty.",
1438
- suggestion: "Pass a non-empty machine user name, or omit the option to use the environment variable or profile default."
1439
- });
1440
- const envMachineUser = process.env.TAILOR_PLATFORM_MACHINE_USER_NAME || void 0;
1441
- const explicitMachineUser = opts?.machineUser ? {
1442
- source: opts.machineUserSource ?? "option",
1443
- value: opts.machineUser
1444
- } : envMachineUser ? {
1445
- source: "env",
1446
- value: envMachineUser
1447
- } : void 0;
1448
- const explicit = explicitMachineUser?.value;
1449
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1450
- if (!profile) return explicit;
1451
- const entry = (await readPlatformConfig()).profiles[profile];
1452
- if (!entry) {
1453
- if (explicit) return explicit;
1454
- throw new Error(`Profile "${profile}" not found`);
1455
- }
1456
- if (entry.machine_user && entry.machine_user_override === "deny") {
1457
- if (explicit && explicit !== entry.machine_user) {
1458
- const details = explicitMachineUser.source === "env" ? `The machine user is being set to "${explicit}" via the TAILOR_PLATFORM_MACHINE_USER_NAME environment variable, which conflicts with this profile's pinned machine user "${entry.machine_user}".` : `This profile fixes the machine user to "${entry.machine_user}" for application-data commands.`;
1459
- throw createCLIError({
1460
- code: "PROFILE_MACHINE_USER_OVERRIDE_DENIED",
1461
- message: `Profile "${profile}" denies overriding the machine user.`,
1462
- details,
1463
- suggestion: `Omit the machine user option, unset TAILOR_PLATFORM_MACHINE_USER_NAME, or run 'tailor profile update ${profile} --machine-user-override allow'.`
1464
- });
1465
- }
1466
- return entry.machine_user;
1467
- }
1468
- return explicit || entry.machine_user;
1469
- }
1470
- /**
1471
- * Load access token from environment variables, command options, or platform config.
1472
- * In CLI context, profile env fallback is also handled by politty's arg env option.
1473
- * Priority: env/TAILOR_PLATFORM_TOKEN > env/TAILOR_TOKEN (deprecated) > opts/profile > env/profile > config/currentUser > error
1474
- * @param opts - Profile options
1475
- * @returns Resolved access token
1476
- */
1477
- async function loadAccessToken(opts) {
1478
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1479
- const envToken = process.env.TAILOR_PLATFORM_TOKEN ?? process.env.TAILOR_TOKEN;
1480
- if (envToken && !process.env.TAILOR_PLATFORM_TOKEN) logger.warn("TAILOR_TOKEN is deprecated. Please use TAILOR_PLATFORM_TOKEN instead.");
1481
- if (envToken) {
1482
- rememberPlatformConfigForToken(envToken, await loadPlatformClientConfig({
1483
- profile,
1484
- allowMissingProfile: true
1485
- }));
1486
- return envToken;
1487
- }
1488
- const pfConfig = await readPlatformConfig();
1489
- const profileEntry = profile ? pfConfig.profiles[profile] : void 0;
1490
- if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
1491
- const user = profileEntry?.user ?? pfConfig.current_user;
1492
- if (!user) throw new Error(multiline`
1493
- Tailor Platform token not found.
1494
- Please specify token via TAILOR_PLATFORM_TOKEN environment variable or login using 'tailor login' command.
1495
- `);
1496
- return (await fetchLatestToken(pfConfig, user, profileEntry ? platformConfigFromProfile(profileEntry) : void 0)).accessToken;
1497
- }
1498
- /**
1499
- * Load platform connection settings from the active profile.
1500
- * @param opts - Profile options
1501
- * @returns Resolved platform connection settings, or undefined for the default environment
1502
- */
1503
- async function loadPlatformClientConfig(opts) {
1504
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1505
- if (!profile) return;
1506
- let pfConfig;
1507
- try {
1508
- pfConfig = await readPlatformConfig();
1509
- } catch (error) {
1510
- if (opts?.allowMissingProfile) return;
1511
- throw error;
1512
- }
1513
- const profileEntry = pfConfig.profiles[profile];
1514
- if (!profileEntry) {
1515
- if (opts?.allowMissingProfile) return;
1516
- throw new Error(`Profile "${profile}" not found`);
1517
- }
1518
- return platformConfigFromProfile(profileEntry);
1519
- }
1520
- /**
1521
- * Load the Tailor Platform Console base URL from environment variables or the active profile.
1522
- * @param opts - Profile options
1523
- * @returns Resolved Console base URL
1524
- */
1525
- async function loadConsoleBaseUrl(opts) {
1526
- return getConsoleBaseUrl(await loadPlatformClientConfig(opts));
1527
- }
1528
- /**
1529
- * Resolve the actual token values for a user, reading from keyring or config as appropriate.
1530
- * @param userEntry - User entry from the config
1531
- * @param user - User identifier
1532
- * @param label - User-facing identifier used in error messages
1533
- * @returns Access token and optional refresh token
1534
- */
1535
- async function resolveTokens(userEntry, user, label = user) {
1536
- if (userEntry.storage === "keyring") {
1537
- const tokens = await loadKeyringTokens(user);
1538
- if (!tokens) throw new Error(multiline`
1539
- Credentials not found in OS keyring for "${label}".
1540
- Please run 'tailor login' and try again.
1541
- `);
1542
- return tokens;
1543
- }
1544
- return {
1545
- accessToken: userEntry.access_token,
1546
- refreshToken: userEntry.refresh_token
1547
- };
1548
- }
1549
- /**
1550
- * Save tokens for a user, writing to keyring by default when available.
1551
- * @param config - Platform config
1552
- * @param user - User identifier
1553
- * @param tokens - Token data to save
1554
- * @param tokens.accessToken - Access token to save
1555
- * @param tokens.refreshToken - Optional refresh token to save
1556
- * @param expiresAt - Token expiration date
1557
- * @param opts - Optional platform and user metadata
1558
- */
1559
- async function saveUserTokens(config, user, tokens, expiresAt, opts = {}) {
1560
- const userKey = platformUserKey(user, opts.platformConfig);
1561
- const email = opts.email ?? config.users[userKey]?.email;
1562
- if (await trySaveTokensInKeyring(userKey, tokens)) config.users[userKey] = {
1563
- token_expires_at: expiresAt,
1564
- storage: "keyring",
1565
- ...email ? { email } : {}
1566
- };
1567
- else {
1568
- if (config.users[userKey]?.storage === "keyring") await deleteKeyringTokens(userKey);
1569
- config.users[userKey] = {
1570
- access_token: tokens.accessToken,
1571
- refresh_token: tokens.refreshToken,
1572
- token_expires_at: expiresAt,
1573
- storage: "file",
1574
- ...email ? { email } : {}
1575
- };
1576
- }
1577
- }
1578
- /**
1579
- * Delete tokens for a user from keyring if applicable.
1580
- * @param config - Platform config
1581
- * @param user - User identifier
1582
- * @param platformConfig - Optional platform connection settings
1583
- * @param opts - Token lookup options
1584
- */
1585
- async function deleteUserTokens(config, user, platformConfig, opts) {
1586
- const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
1587
- if (userEntry?.storage === "keyring") await deleteKeyringTokens(userKey);
1588
- delete config.users[userKey];
1589
- }
1590
- /**
1591
- * Resolve stored tokens for a user on the selected platform.
1592
- * @param config - Platform config
1593
- * @param user - User name
1594
- * @param platformConfig - Optional platform connection settings
1595
- * @param opts - Token lookup options
1596
- * @returns Stored user entry and token values, or undefined when the user is not logged in
1597
- */
1598
- async function loadStoredUserTokens(config, user, platformConfig, opts) {
1599
- const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
1600
- if (!userEntry) return void 0;
1601
- return {
1602
- userEntry,
1603
- ...await resolveTokens(userEntry, userKey, user)
1604
- };
1605
- }
1606
- function updateUserReferences(config, fromUser, toUser) {
1607
- if (fromUser === toUser) return;
1608
- if (config.current_user === fromUser) config.current_user = toUser;
1609
- for (const profile of Object.values(config.profiles)) if (profile?.user === fromUser) profile.user = toUser;
1610
- }
1611
- /**
1612
- * Remove a legacy alias after a canonical user ID has been written.
1613
- * @param config - Platform config
1614
- * @param legacyUser - Previous user key
1615
- * @param canonicalUser - Canonical user key
1616
- * @param platformConfig - Platform settings for scoped token storage
1617
- */
1618
- async function removeLegacyUserAlias(config, legacyUser, canonicalUser, platformConfig) {
1619
- if (legacyUser === canonicalUser) return;
1620
- updateUserReferences(config, legacyUser, canonicalUser);
1621
- const canonicalKey = platformUserKey(canonicalUser, platformConfig);
1622
- const legacyKeys = /* @__PURE__ */ new Set([legacyUser, platformUserKey(legacyUser, platformConfig)]);
1623
- for (const legacyKey of legacyKeys) {
1624
- if (legacyKey === canonicalKey) continue;
1625
- if (config.users[legacyKey]?.storage === "keyring") await deleteKeyringTokens(legacyKey);
1626
- delete config.users[legacyKey];
1627
- }
1628
- }
1629
- function shouldResolveSubjectOnRefresh(user, userEntry) {
1630
- return Boolean(userEntry.email || inferEmailFromUserId(user));
1631
- }
1632
- /**
1633
- * Fetch the latest access token, refreshing if necessary.
1634
- * @param config - Platform config
1635
- * @param user - User identifier
1636
- * @param platformConfig - Optional platform connection settings
1637
- * @returns Latest access token and the canonical user ID it is stored under
1638
- * (the resolved subject when a legacy email key was migrated during refresh,
1639
- * otherwise the matching config user)
1640
- */
1641
- async function fetchLatestToken(config, user, platformConfig) {
1642
- const { userKey: storedUser, userEntry } = findUserEntry(config, user, platformConfig);
1643
- if (!userEntry) throw new Error(multiline`
1644
- User "${user}" not found.
1645
- Please verify your user name and login using 'tailor login' command.
1646
- `);
1647
- const storedConfigUser = userFromPlatformUserKey(storedUser, platformConfig);
1648
- const tokens = await resolveTokens(userEntry, storedUser, user);
1649
- if (new Date(userEntry.token_expires_at) > /* @__PURE__ */ new Date()) {
1650
- rememberPlatformConfigForToken(tokens.accessToken, platformConfig);
1651
- return {
1652
- accessToken: tokens.accessToken,
1653
- user: storedConfigUser
1654
- };
1655
- }
1656
- if (!tokens.refreshToken) throw new Error(multiline`
1657
- Token expired.
1658
- Please run 'tailor login' and try again.
1659
- `);
1660
- const client = initOAuth2Client(platformConfig);
1661
- let resp;
1662
- try {
1663
- resp = await client.refreshToken({
1664
- accessToken: tokens.accessToken,
1665
- refreshToken: tokens.refreshToken,
1666
- expiresAt: Date.parse(userEntry.token_expires_at)
1667
- });
1668
- } catch {
1669
- throw new Error(multiline`
1670
- Failed to refresh token. Your session may have expired.
1671
- Please run 'tailor login' and try again.
1672
- `);
1673
- }
1674
- const newExpiresAt = new Date(assertDefined(resp.expiresAt, "token refresh response missing expiresAt")).toISOString();
1675
- let resolvedUser = storedConfigUser;
1676
- const previousEmail = userEntry.email ?? inferEmailFromUserId(user) ?? inferEmailFromUserId(storedConfigUser);
1677
- let email = previousEmail;
1678
- if (shouldResolveSubjectOnRefresh(user, userEntry) || shouldResolveSubjectOnRefresh(storedConfigUser, userEntry)) try {
1679
- const userInfo = await fetchUserInfo(resp.accessToken, platformConfig);
1680
- resolvedUser = userInfo.sub;
1681
- email = userInfo.email;
1682
- } catch (error) {
1683
- logger.debug(`Failed to resolve refreshed token user info: ${String(error)}`);
1684
- }
1685
- await saveUserTokens(config, resolvedUser, {
1686
- accessToken: resp.accessToken,
1687
- refreshToken: resp.refreshToken ?? void 0
1688
- }, newExpiresAt, {
1689
- platformConfig,
1690
- email
1691
- });
1692
- await removeLegacyUserAlias(config, user, resolvedUser, platformConfig);
1693
- if (storedUser !== platformUserKey(resolvedUser, platformConfig)) {
1694
- if (config.users[storedUser]?.storage === "keyring") await deleteKeyringTokens(storedUser);
1695
- delete config.users[storedUser];
1696
- }
1697
- if (previousEmail && email && previousEmail !== email) logger.info(`Updated local user email from "${previousEmail}" to "${email}".`);
1698
- writePlatformConfig(config);
1699
- rememberPlatformConfigForToken(resp.accessToken, platformConfig);
1700
- return {
1701
- accessToken: resp.accessToken,
1702
- user: resolvedUser
1703
- };
1704
- }
1705
- const DEFAULT_CONFIG_FILENAME = "tailor.config.ts";
1706
- /**
1707
- * Load config path from command options, environment variables, or search parent directories.
1708
- * In CLI context, env fallback is also handled by politty's arg env option.
1709
- * Priority: opts/config > env/config > search parent directories
1710
- * @param configPath - Optional explicit config path
1711
- * @returns Resolved config path or undefined
1712
- */
1713
- function loadConfigPath(configPath) {
1714
- if (configPath) return configPath;
1715
- if (process.env.TAILOR_CONFIG_PATH) return process.env.TAILOR_CONFIG_PATH;
1716
- return findUpSync(DEFAULT_CONFIG_FILENAME);
1717
- }
1718
-
1719
- //#endregion
1720
- //#region src/cli/shared/mock.ts
1721
- /**
1722
- * Install a stub `globalThis.tailordb` so that user code loaded by the CLI
1723
- * (e.g. via `createGetDB` in `@tailor-platform/sdk/kysely`) can reference
1724
- * `tailordb.Client` without hitting a `ReferenceError`. The CLI never
1725
- * actually executes the user code paths that issue queries, so a no-op
1726
- * client suffices.
1727
- *
1728
- * Exposed as a function (rather than a top-level statement) so that
1729
- * `package.json#sideEffects` can keep the file marked side-effect-free
1730
- * without bundlers eliminating the install step.
1731
- */
1732
- function installCliTailordbStub() {
1733
- globalThis.tailordb = { Client: class {
1734
- constructor(_config) {}
1735
- async connect() {}
1736
- async end() {}
1737
- async queryObject() {
1738
- return {};
1739
- }
1740
- } };
1741
- }
1742
-
1743
- //#endregion
1744
- //#region src/cli/shared/config-loader.ts
1745
- /**
1746
- * Load Tailor configuration file and associated plugins.
1747
- * @param configPath - Optional explicit config path
1748
- * @param options - Optional module import behavior.
1749
- * @returns Loaded config, plugins, and config path
1750
- */
1751
- async function loadConfig(configPath, options = {}) {
1752
- installCliTailordbStub();
1753
- const foundPath = loadConfigPath(configPath);
1754
- if (!foundPath) throw new Error("Configuration file not found: tailor.config.ts not found in current or parent directories");
1755
- const resolvedPath = path.resolve(process.cwd(), foundPath);
1756
- if (!fs$1.existsSync(resolvedPath)) throw new Error(`Configuration file not found: ${configPath}`);
1757
- const configUrl = pathToFileURL(resolvedPath);
1758
- if (options.importNonce) configUrl.searchParams.set("tailorImportNonce", options.importNonce);
1759
- const configModule = await import(configUrl.href);
1760
- if (!configModule || !configModule.default) throw new Error("Invalid Tailor config module: default export not found");
1761
- const validated = AppConfigSchema.safeParse(configModule.default);
1762
- if (!validated.success) {
1763
- const issues = validated.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
1764
- throw new Error(`Invalid Tailor config in ${resolvedPath}:\n${issues}`);
1765
- }
1766
- const allPlugins = [];
1767
- for (const value of Object.values(configModule)) if (Array.isArray(value)) {
1768
- const pluginParsed = value.reduce((acc, item) => {
1769
- if (!acc.success) return acc;
1770
- const result = PluginConfigSchema.safeParse(item);
1771
- if (result.success) acc.items.push(result.data);
1772
- else acc.success = false;
1773
- return acc;
1774
- }, {
1775
- success: true,
1776
- items: []
1777
- });
1778
- if (pluginParsed.success && pluginParsed.items.length > 0) allPlugins.push(...pluginParsed.items);
1779
- }
1780
- return {
1781
- config: {
1782
- ...configModule.default,
1783
- path: resolvedPath
1784
- },
1785
- plugins: allPlugins
1786
- };
1787
- }
1788
-
1789
671
  //#endregion
1790
672
  //#region src/cli/cache/hasher.ts
1791
673
  /**
@@ -1802,7 +684,7 @@ function hashContent(content) {
1802
684
  * @returns Hex-encoded SHA-256 hash of the file content
1803
685
  */
1804
686
  function hashFile(filePath) {
1805
- const content = fs$1.readFileSync(filePath);
687
+ const content = fs.readFileSync(filePath);
1806
688
  return crypto.createHash("sha256").update(content).digest("hex");
1807
689
  }
1808
690
  /**
@@ -1931,7 +813,10 @@ function createBundleCache(store) {
1931
813
  return;
1932
814
  }
1933
815
  if (currentHash !== entry.inputHash) return;
1934
- return store.restoreBundleContent(cacheKey);
816
+ const content = store.restoreBundleContent(cacheKey);
817
+ const output = entry.outputFiles.find((file) => file.outputPath === cacheKey);
818
+ if (content === void 0 || !output || hashContent(content) !== output.contentHash) return;
819
+ return content;
1935
820
  }
1936
821
  function save(params) {
1937
822
  const { kind, namespace, name, sourceFile, content, dependencyPaths, contextHash } = params;
@@ -1978,7 +863,7 @@ function resolveRelativePluginImportPath(pluginImportPath, baseDirs) {
1978
863
  if (!pluginImportPath.startsWith(".")) return null;
1979
864
  for (const baseDir of baseDirs) {
1980
865
  const absolutePath = path.resolve(baseDir, pluginImportPath);
1981
- if (fs$1.existsSync(absolutePath)) return absolutePath;
866
+ if (fs.existsSync(absolutePath)) return absolutePath;
1982
867
  }
1983
868
  return null;
1984
869
  }
@@ -2062,13 +947,13 @@ function generatePluginExecutorFiles(executors, outputDir, typeGenerationResult,
2062
947
  function generateSingleExecutorFile(info, outputDir, typeGenerationResult, sourceTypeInfoMap, baseDirs = []) {
2063
948
  const pluginDir = sanitizePluginId$1(info.pluginId);
2064
949
  const executorOutputDir = path.join(outputDir, pluginDir, "executors");
2065
- fs$1.mkdirSync(executorOutputDir, { recursive: true });
950
+ fs.mkdirSync(executorOutputDir, { recursive: true });
2066
951
  const fileName = sanitizeExecutorFileName(info.executor.name);
2067
952
  const filePath = path.join(executorOutputDir, `${fileName}.ts`);
2068
953
  let content;
2069
954
  if (isPluginExecutorWithFile(info.executor)) content = generateExecutorFileContentNew(info, info.executor, outputDir, typeGenerationResult, sourceTypeInfoMap, baseDirs);
2070
955
  else content = generateExecutorFileContentLegacy(info.executor);
2071
- fs$1.writeFileSync(filePath, content);
956
+ fs.writeFileSync(filePath, content);
2072
957
  return filePath;
2073
958
  }
2074
959
  /**
@@ -2300,7 +1185,7 @@ function extractDynamicImportSpecifier(resolve) {
2300
1185
  function resolvePluginBaseDir(pluginImportPath, baseDirs) {
2301
1186
  if (pluginImportPath.startsWith(".")) {
2302
1187
  const resolvedPath = resolveRelativePluginImportPath(pluginImportPath, baseDirs) ?? path.resolve(baseDirs[0] ?? process.cwd(), pluginImportPath);
2303
- if (fs$1.existsSync(resolvedPath)) return fs$1.statSync(resolvedPath).isDirectory() ? resolvedPath : path.dirname(resolvedPath);
1188
+ if (fs.existsSync(resolvedPath)) return fs.statSync(resolvedPath).isDirectory() ? resolvedPath : path.dirname(resolvedPath);
2304
1189
  return path.extname(resolvedPath) ? path.dirname(resolvedPath) : resolvedPath;
2305
1190
  }
2306
1191
  for (const baseDir of baseDirs) try {
@@ -2387,11 +1272,11 @@ function generatePluginTypeFiles(types, outputDir) {
2387
1272
  seenTypeNames.set(info.type.name, info);
2388
1273
  const pluginDir = sanitizePluginId(info.pluginId);
2389
1274
  const typeOutputDir = path.join(outputDir, pluginDir, "types");
2390
- fs$1.mkdirSync(typeOutputDir, { recursive: true });
1275
+ fs.mkdirSync(typeOutputDir, { recursive: true });
2391
1276
  const fileName = `${toKebabCase(info.type.name)}.ts`;
2392
1277
  const filePath = path.join(typeOutputDir, fileName);
2393
1278
  const content = generateTypeFileContent(info);
2394
- fs$1.writeFileSync(filePath, content);
1279
+ fs.writeFileSync(filePath, content);
2395
1280
  generatedFiles.push(filePath);
2396
1281
  const relativePath = path.relative(outputDir, filePath);
2397
1282
  typeFilePaths.set(info.type.name, relativePath);
@@ -2513,112 +1398,18 @@ function toCamelCase(str) {
2513
1398
  }
2514
1399
 
2515
1400
  //#endregion
2516
- //#region src/cli/services/stale-cleanup.ts
1401
+ //#region src/cli/services/workflow/ast-utils.ts
2517
1402
  /**
2518
- * Remove stale `.entry.js` files from the output directory.
2519
- *
2520
- * Must be called before parallel bundling; concurrent builds
2521
- * sharing the same output directory would otherwise conflict.
2522
- * @param outputDir - Directory to clean
1403
+ * Read an import or export name from an OXC identifier or string-literal node.
1404
+ * @param node - Import/export name node
1405
+ * @returns Module binding name, or undefined for an unsupported node
2523
1406
  */
2524
- async function removeStaleEntryFiles(outputDir) {
2525
- const files = await fs.readdir(outputDir);
2526
- await Promise.all(files.filter((file) => file.endsWith(".entry.js")).map((file) => fs.rm(path.join(outputDir, file), { force: true })));
1407
+ function getModuleExportName(node) {
1408
+ if (!node || typeof node !== "object") return void 0;
1409
+ const exportName = node;
1410
+ if (exportName.name) return exportName.name;
1411
+ return typeof exportName.value === "string" ? exportName.value : void 0;
2527
1412
  }
2528
-
2529
- //#endregion
2530
- //#region src/cli/shared/bundle-log-level.ts
2531
- const INFO_LEVEL_CONSOLE_METHODS = [
2532
- "console.info",
2533
- "console.table",
2534
- "console.dir",
2535
- "console.dirxml",
2536
- "console.count",
2537
- "console.countReset",
2538
- "console.time",
2539
- "console.timeLog",
2540
- "console.timeEnd",
2541
- "console.group",
2542
- "console.groupCollapsed",
2543
- "console.groupEnd",
2544
- "console.clear"
2545
- ];
2546
- const DEBUG_LEVEL_CONSOLE_METHODS = [
2547
- "console.debug",
2548
- "console.log",
2549
- "console.trace"
2550
- ];
2551
- const WARN_LEVEL_CONSOLE_METHODS = ["console.warn"];
2552
- const ERROR_LEVEL_CONSOLE_METHODS = ["console.error"];
2553
- const MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL = {
2554
- DEBUG: [],
2555
- INFO: DEBUG_LEVEL_CONSOLE_METHODS,
2556
- WARN: [...DEBUG_LEVEL_CONSOLE_METHODS, ...INFO_LEVEL_CONSOLE_METHODS],
2557
- ERROR: [
2558
- ...DEBUG_LEVEL_CONSOLE_METHODS,
2559
- ...INFO_LEVEL_CONSOLE_METHODS,
2560
- ...WARN_LEVEL_CONSOLE_METHODS
2561
- ],
2562
- SILENT: [
2563
- ...DEBUG_LEVEL_CONSOLE_METHODS,
2564
- ...INFO_LEVEL_CONSOLE_METHODS,
2565
- ...WARN_LEVEL_CONSOLE_METHODS,
2566
- ...ERROR_LEVEL_CONSOLE_METHODS
2567
- ]
2568
- };
2569
- function normalizeBundleLogLevel(value) {
2570
- const normalized = value.trim().toUpperCase();
2571
- return isLogLevel(normalized) ? normalized : void 0;
2572
- }
2573
- function resolveBundleLogLevel(configValue) {
2574
- if (configValue === void 0) return "DEBUG";
2575
- const resolved = normalizeBundleLogLevel(configValue);
2576
- if (resolved) return resolved;
2577
- throw new Error(`Invalid logLevel "${configValue}". Expected one of: ${LOG_LEVELS.join(", ")}`);
2578
- }
2579
- function manualPureFunctionsForLogLevel(logLevel) {
2580
- return MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL[logLevel];
2581
- }
2582
- function createLogLevelTreeshakeOptions(logLevel) {
2583
- const manualPureFunctions = manualPureFunctionsForLogLevel(logLevel);
2584
- return manualPureFunctions.length > 0 ? { manualPureFunctions } : {};
2585
- }
2586
-
2587
- //#endregion
2588
- //#region src/cli/shared/function-treeshake.ts
2589
- const BASE_FUNCTION_TREESHAKE_OPTIONS = {
2590
- moduleSideEffects: false,
2591
- annotations: true,
2592
- unknownGlobalSideEffects: false
2593
- };
2594
- function mergeFunctionTreeshakeOptions(fragments) {
2595
- const merged = {};
2596
- const manualPureFunctions = /* @__PURE__ */ new Set();
2597
- for (const fragment of fragments) {
2598
- Object.assign(merged, fragment);
2599
- for (const name of fragment.manualPureFunctions ?? []) manualPureFunctions.add(name);
2600
- }
2601
- if (manualPureFunctions.size > 0) merged.manualPureFunctions = [...manualPureFunctions];
2602
- else delete merged.manualPureFunctions;
2603
- return merged;
2604
- }
2605
- function composeFunctionTreeshakeOptions(fragments = []) {
2606
- return mergeFunctionTreeshakeOptions([BASE_FUNCTION_TREESHAKE_OPTIONS, ...fragments]);
2607
- }
2608
-
2609
- //#endregion
2610
- //#region src/cli/shared/platform-bundle-plugin.ts
2611
- const GATE = /(?<![\w$.])process\.env\.__TAILOR_PLATFORM_BUNDLE\b/g;
2612
- const platformBundleDefinePlugin = {
2613
- name: "tailor-platform-bundle-define",
2614
- transform(code) {
2615
- if (!code.includes("process.env.__TAILOR_PLATFORM_BUNDLE")) return null;
2616
- return { code: code.replace(GATE, "true") };
2617
- }
2618
- };
2619
-
2620
- //#endregion
2621
- //#region src/cli/services/workflow/ast-utils.ts
2622
1413
  /**
2623
1414
  * Check if a module source is from the Tailor SDK package (including subpaths)
2624
1415
  * @param source - Module source string
@@ -2662,7 +1453,7 @@ function getTriggerCallInfo(node, sourceText) {
2662
1453
  const callee = callExpr.callee;
2663
1454
  if (callee.type !== "MemberExpression") return null;
2664
1455
  const memberExpr = callee;
2665
- if (memberExpr.computed || memberExpr.object.type !== "Identifier" || memberExpr.property.name !== "trigger") return null;
1456
+ if (memberExpr.computed || memberExpr.property.name !== "trigger" || memberExpr.object.type !== "Identifier") return null;
2666
1457
  return {
2667
1458
  identifierName: memberExpr.object.name,
2668
1459
  callRange: {
@@ -2749,20 +1540,6 @@ function findStatementEnd(source, position) {
2749
1540
  if (i < source.length && source[i] === "\n") i++;
2750
1541
  return i;
2751
1542
  }
2752
- /**
2753
- * Resolve a relative path from a base directory
2754
- * Simple implementation that handles ./ and ../ prefixes
2755
- * @param baseDir - Base directory
2756
- * @param relativePath - Relative path to resolve
2757
- * @returns Resolved absolute path
2758
- */
2759
- function resolvePath(baseDir, relativePath) {
2760
- const parts = relativePath.replace(/\\/g, "/").split("/");
2761
- const baseParts = baseDir.replace(/\\/g, "/").split("/");
2762
- for (const part of parts) if (part === ".") {} else if (part === "..") baseParts.pop();
2763
- else baseParts.push(part);
2764
- return baseParts.join("/");
2765
- }
2766
1543
 
2767
1544
  //#endregion
2768
1545
  //#region src/cli/services/workflow/sdk-binding-collector.ts
@@ -2906,43 +1683,6 @@ function findAllJobs(program, _sourceText) {
2906
1683
  walk(program);
2907
1684
  return jobs;
2908
1685
  }
2909
- /**
2910
- * Build a map from export name to job name from detected jobs
2911
- * @param jobs - Detected job locations
2912
- * @returns Map from export name to job name
2913
- */
2914
- function buildJobNameMap(jobs) {
2915
- const map = /* @__PURE__ */ new Map();
2916
- for (const job of jobs) if (job.exportName) map.set(job.exportName, job.name);
2917
- return map;
2918
- }
2919
- /**
2920
- * Detect all .trigger() calls in the source code
2921
- * Returns information about each trigger call for transformation
2922
- * @param program - Parsed TypeScript program
2923
- * @param sourceText - Source code text
2924
- * @returns Detected trigger calls
2925
- */
2926
- function detectTriggerCalls(program, sourceText) {
2927
- const calls = [];
2928
- function walk(node) {
2929
- if (!node || typeof node !== "object") return;
2930
- const triggerCall = getTriggerCallInfo(node, sourceText);
2931
- if (triggerCall) calls.push({
2932
- identifierName: triggerCall.identifierName,
2933
- callRange: triggerCall.callRange,
2934
- argsText: triggerCall.argsText,
2935
- optionsText: triggerCall.optionsText
2936
- });
2937
- for (const key of Object.keys(node)) {
2938
- const child = node[key];
2939
- if (Array.isArray(child)) child.forEach((c) => walk(c));
2940
- else if (child && typeof child === "object") walk(child);
2941
- }
2942
- }
2943
- walk(program);
2944
- return calls;
2945
- }
2946
1686
 
2947
1687
  //#endregion
2948
1688
  //#region src/cli/services/workflow/workflow-detector.ts
@@ -2994,51 +1734,113 @@ function findAllWorkflows(program, _sourceText) {
2994
1734
  walk(program);
2995
1735
  return workflows;
2996
1736
  }
1737
+
1738
+ //#endregion
1739
+ //#region src/cli/shared/trigger-context.ts
2997
1740
  /**
2998
- * Build a map from export name to workflow name from detected workflows
2999
- * @param workflows - Detected workflows
3000
- * @returns Map from export name to workflow name
3001
- */
3002
- function buildWorkflowNameMap(workflows) {
3003
- const map = /* @__PURE__ */ new Map();
3004
- for (const workflow of workflows) if (workflow.exportName) map.set(workflow.exportName, workflow.name);
3005
- return map;
3006
- }
3007
- /**
3008
- * Detect default imports in a source file and return a map from local name to import source
3009
- * @param program - Parsed TypeScript program
3010
- * @returns Map from local name to import source
1741
+ * Normalize a source module path for trigger binding lookup.
1742
+ * @param filePath - Source file path or extensionless relative import path
1743
+ * @returns Absolute path without a JavaScript or TypeScript extension
3011
1744
  */
3012
- function detectDefaultImports(program) {
3013
- const imports = /* @__PURE__ */ new Map();
3014
- function walk(node) {
3015
- if (!node || typeof node !== "object") return;
3016
- if (node.type === "ImportDeclaration") {
3017
- const importDecl = node;
3018
- const source = importDecl.source.value;
3019
- if (typeof source === "string") {
3020
- for (const specifier of importDecl.specifiers) if (specifier.type === "ImportDefaultSpecifier") {
3021
- const spec = specifier;
3022
- if (spec.local.name) imports.set(spec.local.name, source);
3023
- }
1745
+ function normalizeFilePath(filePath) {
1746
+ return path.resolve(filePath.replace(/[?#].*$/, "")).replace(/\.(ts|mts|cts|js|mjs|cjs)$/, "");
1747
+ }
1748
+ function createModuleBindings(program, source) {
1749
+ const localBindings = /* @__PURE__ */ new Map();
1750
+ const exports = /* @__PURE__ */ new Map();
1751
+ for (const workflow of findAllWorkflows(program, source)) {
1752
+ const target = {
1753
+ kind: "workflow",
1754
+ name: workflow.name
1755
+ };
1756
+ if (workflow.exportName) localBindings.set(workflow.exportName, target);
1757
+ if (workflow.isDefaultExport) exports.set("default", target);
1758
+ }
1759
+ for (const job of findAllJobs(program, source)) if (job.exportName) localBindings.set(job.exportName, {
1760
+ kind: "job",
1761
+ name: job.name
1762
+ });
1763
+ for (const statement of program.body) {
1764
+ if (statement.type === "ExportDefaultDeclaration") {
1765
+ const declaration = statement.declaration;
1766
+ if (declaration?.type === "Identifier") {
1767
+ const target = localBindings.get(declaration.name);
1768
+ if (target) exports.set("default", target);
3024
1769
  }
1770
+ continue;
3025
1771
  }
3026
- for (const key of Object.keys(node)) {
3027
- const child = node[key];
3028
- if (Array.isArray(child)) child.forEach((c) => walk(c));
3029
- else if (child && typeof child === "object") walk(child);
1772
+ if (statement.type !== "ExportNamedDeclaration") continue;
1773
+ const declaration = statement.declaration;
1774
+ if (declaration?.type === "VariableDeclaration") for (const declarator of declaration.declarations) {
1775
+ const id = declarator.id;
1776
+ if (id?.type !== "Identifier") continue;
1777
+ const localName = id.name;
1778
+ const target = localBindings.get(localName);
1779
+ if (target) exports.set(localName, target);
1780
+ }
1781
+ if (statement.source) continue;
1782
+ for (const specifier of statement.specifiers ?? []) {
1783
+ const localName = getModuleExportName(specifier.local);
1784
+ const exportedName = getModuleExportName(specifier.exported);
1785
+ if (!localName || !exportedName) continue;
1786
+ const target = localBindings.get(localName);
1787
+ if (target) exports.set(exportedName, target);
3030
1788
  }
3031
1789
  }
3032
- walk(program);
3033
- return imports;
1790
+ return {
1791
+ localBindings,
1792
+ exports
1793
+ };
1794
+ }
1795
+ /**
1796
+ * Build trigger context from configured workflow source files.
1797
+ * @param workflowConfig - Workflow file loading configuration
1798
+ * @param authNamespace - Auth service namespace (optional, used for string-literal invoker expansion)
1799
+ * @returns Module-local workflow and job binding metadata
1800
+ */
1801
+ async function buildTriggerContext(workflowConfig, authNamespace) {
1802
+ const modules = /* @__PURE__ */ new Map();
1803
+ if (!workflowConfig) return {
1804
+ modules,
1805
+ authNamespace
1806
+ };
1807
+ for (const file of loadFilesWithIgnores(workflowConfig)) try {
1808
+ const source = await fs.promises.readFile(file, "utf-8");
1809
+ const { program } = parseSync("input.ts", source);
1810
+ modules.set(normalizeFilePath(file), createModuleBindings(program, source));
1811
+ } catch (error) {
1812
+ const errorMessage = error instanceof Error ? error.message : String(error);
1813
+ logger.warn(`Failed to process workflow file ${file}: ${errorMessage}`, { mode: "stream" });
1814
+ }
1815
+ return {
1816
+ modules,
1817
+ authNamespace
1818
+ };
1819
+ }
1820
+ function sortedTargets(bindings) {
1821
+ return [...bindings].toSorted(([a], [b]) => a.localeCompare(b)).map(([binding, target]) => [
1822
+ binding,
1823
+ target.kind,
1824
+ target.name
1825
+ ]);
1826
+ }
1827
+ /**
1828
+ * Serialize trigger context to a deterministic cache input.
1829
+ * @param context - Trigger context to serialize
1830
+ * @returns Deterministic string, or an empty string when context is absent
1831
+ */
1832
+ function serializeTriggerContext(context) {
1833
+ if (!context) return "";
1834
+ const modules = [...context.modules].toSorted(([a], [b]) => a.localeCompare(b)).map(([file, bindings]) => [
1835
+ file,
1836
+ sortedTargets(bindings.localBindings),
1837
+ sortedTargets(bindings.exports)
1838
+ ]);
1839
+ return JSON.stringify(modules) + (context.authNamespace ?? "");
3034
1840
  }
3035
1841
 
3036
1842
  //#endregion
3037
1843
  //#region src/cli/services/workflow/trigger-transformer.ts
3038
- /**
3039
- * Name of the injected runtime normalizer helper. Chosen to be unique enough
3040
- * to avoid collisions with user code.
3041
- */
3042
1844
  const NORMALIZER_IDENTIFIER = "__tailor_normalizeTriggerOptions";
3043
1845
  /**
3044
1846
  * Build the source text of the injected normalizer helper.
@@ -3054,336 +1856,306 @@ const NORMALIZER_IDENTIFIER = "__tailor_normalizeTriggerOptions";
3054
1856
  function buildNormalizerHelperSource(authNamespace) {
3055
1857
  return `const ${NORMALIZER_IDENTIFIER} = (o) => { if (!o) return o; const { invoker, ...rest } = o; return typeof invoker === "string" ? { ...rest, authInvoker: { namespace: ${JSON.stringify(authNamespace)}, machineUserName: invoker } } : typeof invoker === "object" ? { ...rest, authInvoker: invoker } : o; };\n`;
3056
1858
  }
3057
- /**
3058
- * Check if an AST binding pattern (parameter, catch clause, etc.) contains an Identifier with the given name.
3059
- * @param node - AST node to inspect
3060
- * @param name - Identifier name to look for
3061
- * @returns True if the binding pattern contains the name
3062
- */
3063
- function containsBindingName(node, name) {
3064
- if (!node || typeof node !== "object") return false;
3065
- if (node.type === "Identifier" && node.name === name) return true;
3066
- for (const key of Object.keys(node)) {
3067
- const child = node[key];
3068
- if (Array.isArray(child)) {
3069
- if (child.some((c) => containsBindingName(c, name))) return true;
3070
- } else if (child && typeof child === "object") {
3071
- if (containsBindingName(child, name)) return true;
3072
- }
1859
+ function collectBindingNames(node, names) {
1860
+ if (!node || typeof node !== "object") return;
1861
+ switch (node.type) {
1862
+ case "Identifier":
1863
+ names.add(node.name);
1864
+ return;
1865
+ case "ObjectPattern":
1866
+ for (const property of node.properties) collectBindingNames(property.type === "RestElement" ? property.argument : property.value, names);
1867
+ return;
1868
+ case "ArrayPattern":
1869
+ for (const element of node.elements) collectBindingNames(element, names);
1870
+ return;
1871
+ case "AssignmentPattern":
1872
+ collectBindingNames(node.left, names);
1873
+ return;
1874
+ case "RestElement":
1875
+ collectBindingNames(node.argument, names);
1876
+ return;
1877
+ case "TSParameterProperty": collectBindingNames(node.parameter, names);
3073
1878
  }
3074
- return false;
3075
1879
  }
3076
- /**
3077
- * Build a map of reference counts for multiple identifiers in a single AST pass.
3078
- * Scope-aware: references inside functions or catch clauses that shadow the name
3079
- * via parameters are excluded, so only references to the original import binding
3080
- * are counted.
3081
- * Excludes import declarations and property names in non-computed member expressions.
3082
- * @param program - The parsed AST program
3083
- * @param names - Set of identifier names to count
3084
- * @returns Map from identifier name to reference count
3085
- */
3086
- function buildReferenceCountMap(program, names) {
3087
- if (names.size === 0) return /* @__PURE__ */ new Map();
3088
- const counts = /* @__PURE__ */ new Map();
3089
- const shadowDepth = /* @__PURE__ */ new Map();
3090
- for (const name of names) {
3091
- counts.set(name, 0);
3092
- shadowDepth.set(name, 0);
3093
- }
3094
- function walk(node, parentNode, parentKey) {
3095
- if (!node || typeof node !== "object") return;
3096
- const nodeType = node.type;
3097
- if (nodeType === "ImportDeclaration") return;
3098
- const shadowedHere = [];
3099
- if (nodeType === "FunctionDeclaration" || nodeType === "FunctionExpression" || nodeType === "ArrowFunctionExpression") {
3100
- const params = node.params;
3101
- if (params) {
3102
- for (const name of names) if (params.some((p) => containsBindingName(p, name))) {
3103
- shadowDepth.set(name, (shadowDepth.get(name) ?? 0) + 1);
3104
- shadowedHere.push(name);
3105
- }
3106
- }
3107
- }
3108
- if (nodeType === "CatchClause") {
3109
- const param = node.param;
3110
- if (param) {
3111
- for (const name of names) if (containsBindingName(param, name)) {
3112
- shadowDepth.set(name, (shadowDepth.get(name) ?? 0) + 1);
3113
- shadowedHere.push(name);
3114
- }
3115
- }
3116
- }
3117
- if (nodeType === "Identifier") {
3118
- const identName = node.name;
3119
- if (identName && names.has(identName) && (shadowDepth.get(identName) ?? 0) === 0) {
3120
- const isMemberProperty = parentNode && parentNode.type === "MemberExpression" && parentKey === "property" && !parentNode.computed;
3121
- const isObjectPropertyKey = parentNode && parentNode.type === "Property" && parentKey === "key" && !parentNode.shorthand && !parentNode.computed;
3122
- if (!isMemberProperty && !isObjectPropertyKey) counts.set(identName, (counts.get(identName) ?? 0) + 1);
3123
- }
3124
- }
3125
- for (const key of Object.keys(node)) {
3126
- const child = node[key];
3127
- if (Array.isArray(child)) child.forEach((c) => walk(c, node, key));
3128
- else if (child && typeof child === "object") walk(child, node, key);
3129
- }
3130
- for (const name of shadowedHere) shadowDepth.set(name, (shadowDepth.get(name) ?? 0) - 1);
3131
- }
3132
- walk(program);
3133
- return counts;
1880
+ function declarationNode(statement) {
1881
+ return statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
3134
1882
  }
3135
- /**
3136
- * Find the text range to remove for a dead default import.
3137
- *
3138
- * - Default-only import (`import wf from "..."`): returns the full declaration range.
3139
- * - Mixed import (`import wf, { helper } from "..."`): returns the range covering
3140
- * the default specifier and trailing comma/whitespace so the result becomes
3141
- * `import { helper } from "..."`.
3142
- * @param program - The parsed AST program
3143
- * @param localName - The local name of the default import
3144
- * @param source - The source code text (used to locate the `{` in mixed imports)
3145
- * @returns Range to remove, or null if the import was not found
3146
- */
3147
- function findDefaultImportRemovalRange(program, localName, source) {
3148
- for (const statement of program.body) {
3149
- if (statement.type !== "ImportDeclaration") continue;
3150
- const importDecl = statement;
3151
- const specifiers = importDecl.specifiers;
3152
- for (const spec of specifiers) {
3153
- if (spec.type !== "ImportDefaultSpecifier") continue;
3154
- const defaultSpec = spec;
3155
- if (defaultSpec.local.name !== localName) continue;
3156
- if (specifiers.length === 1) return {
3157
- start: importDecl.start,
3158
- end: importDecl.end,
3159
- isFullDeclaration: true
3160
- };
3161
- const braceIndex = source.indexOf("{", defaultSpec.end);
3162
- if (braceIndex !== -1) return {
3163
- start: defaultSpec.start,
3164
- end: braceIndex,
3165
- isFullDeclaration: false
3166
- };
3167
- return null;
3168
- }
1883
+ function collectBlockBindings(statements, names) {
1884
+ for (const statement of statements) {
1885
+ const declaration = declarationNode(statement);
1886
+ if (!declaration) continue;
1887
+ if (declaration.type === "VariableDeclaration") {
1888
+ if (declaration.kind === "var") continue;
1889
+ for (const declarator of declaration.declarations) collectBindingNames(declarator.id, names);
1890
+ } else if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") collectBindingNames(declaration.id, names);
3169
1891
  }
3170
- return null;
3171
1892
  }
3172
- /**
3173
- * Detect .trigger() calls for known workflows and jobs
3174
- * Only detects calls where the identifier is in workflowNames or jobNames
3175
- * @param program - The parsed AST program
3176
- * @param sourceText - The source code text
3177
- * @param workflowNames - Set of known workflow identifier names
3178
- * @param jobNames - Set of known job identifier names
3179
- * @returns Detected trigger call metadata
3180
- */
3181
- function detectExtendedTriggerCalls(program, sourceText, workflowNames, jobNames) {
3182
- const calls = [];
1893
+ function collectFunctionVarBindings(root, names) {
3183
1894
  function walk(node) {
3184
1895
  if (!node || typeof node !== "object") return;
3185
- const triggerCall = getTriggerCallInfo(node, sourceText);
3186
- if (triggerCall) {
3187
- const isWorkflow = workflowNames.has(triggerCall.identifierName);
3188
- const isJob = jobNames.has(triggerCall.identifierName);
3189
- if (isWorkflow) calls.push({
3190
- kind: "workflow",
3191
- identifierName: triggerCall.identifierName,
3192
- callRange: triggerCall.callRange,
3193
- argsText: triggerCall.argsText,
3194
- optionsText: triggerCall.optionsText
3195
- });
3196
- else if (isJob) calls.push({
3197
- kind: "job",
3198
- identifierName: triggerCall.identifierName,
3199
- callRange: triggerCall.callRange,
3200
- argsText: triggerCall.argsText,
3201
- optionsText: triggerCall.optionsText
3202
- });
1896
+ if (node !== root && (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "StaticBlock")) return;
1897
+ if (node.type === "VariableDeclaration" && node.kind === "var") for (const declarator of node.declarations) collectBindingNames(declarator.id, names);
1898
+ for (const key of Object.keys(node)) {
1899
+ if (key === "parent") continue;
1900
+ const child = node[key];
1901
+ if (Array.isArray(child)) for (const item of child) walk(item);
1902
+ else if (child && typeof child === "object") walk(child);
3203
1903
  }
1904
+ }
1905
+ walk(root);
1906
+ }
1907
+ function collectScopeBindings(node) {
1908
+ const names = /* @__PURE__ */ new Set();
1909
+ if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
1910
+ collectBindingNames(node.id, names);
1911
+ for (const parameter of node.params) collectBindingNames(parameter, names);
1912
+ collectFunctionVarBindings(node, names);
1913
+ return names;
1914
+ }
1915
+ if (node.type === "BlockStatement") {
1916
+ collectBlockBindings(node.body, names);
1917
+ return names;
1918
+ }
1919
+ if (node.type === "CatchClause") {
1920
+ collectBindingNames(node.param, names);
1921
+ return names;
1922
+ }
1923
+ if (node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement") {
1924
+ const declaration = node.init ?? node.left;
1925
+ if (declaration?.type === "VariableDeclaration" && declaration.kind !== "var") for (const declarator of declaration.declarations) collectBindingNames(declarator.id, names);
1926
+ return names;
1927
+ }
1928
+ }
1929
+ function addShadowedBindings(shadowedNames, bindings, targetNames) {
1930
+ if (!bindings) return shadowedNames;
1931
+ const relevantBindings = [...bindings].filter((name) => targetNames.has(name));
1932
+ return relevantBindings.length === 0 ? shadowedNames : /* @__PURE__ */ new Set([...shadowedNames, ...relevantBindings]);
1933
+ }
1934
+ function walkBindingAware(program, targetNames, visitor) {
1935
+ function walk(node, shadowedNames, parentNode, parentKey) {
1936
+ if (!node || typeof node !== "object" || node.type === "ImportDeclaration") return;
1937
+ const nestedShadowedNames = node.type === "Program" ? shadowedNames : addShadowedBindings(shadowedNames, collectScopeBindings(node), targetNames);
1938
+ visitor(node, nestedShadowedNames, parentNode, parentKey);
3204
1939
  for (const key of Object.keys(node)) {
1940
+ if (key === "parent") continue;
3205
1941
  const child = node[key];
3206
- if (Array.isArray(child)) child.forEach((c) => walk(c));
3207
- else if (child && typeof child === "object") walk(child);
1942
+ if (Array.isArray(child)) for (const item of child) walk(item, nestedShadowedNames, node, key);
1943
+ else if (child && typeof child === "object") walk(child, nestedShadowedNames, node, key);
3208
1944
  }
3209
1945
  }
3210
- walk(program);
3211
- return calls;
1946
+ walk(program, /* @__PURE__ */ new Set());
3212
1947
  }
3213
- /**
3214
- * Transform trigger calls for resolver/executor/workflow functions
3215
- * Handles job.trigger() and workflow.trigger() calls
3216
- * @param source - The source code to transform
3217
- * @param workflowNameMap - Map from variable name to workflow name
3218
- * @param jobNameMap - Map from variable name to job name
3219
- * @param workflowFileMap - Map from file path (without extension) to workflow name for default exports
3220
- * @param currentFilePath - Path of the current file being transformed (for resolving relative imports)
3221
- * @param authNamespace - Auth service namespace used to expand string-literal `invoker` to object form
3222
- * @returns Transformed source code with trigger calls rewritten
3223
- */
3224
- function transformFunctionTriggers(source, workflowNameMap, jobNameMap, workflowFileMap, currentFilePath, authNamespace) {
3225
- const { program } = parseSync("input.ts", source);
3226
- const localWorkflowNameMap = new Map(workflowNameMap);
3227
- const workflowDefaultImportNames = /* @__PURE__ */ new Set();
3228
- if (workflowFileMap && currentFilePath) {
3229
- const defaultImports = detectDefaultImports(program);
3230
- const currentDir = currentFilePath.replace(/[/\\][^/\\]+$/, "");
3231
- for (const [localName, importSource] of defaultImports) {
3232
- if (!importSource.startsWith(".")) continue;
3233
- const resolvedPath = resolvePath(currentDir, importSource).replace(/\.(ts|mts|cts|js|mjs|cjs)$/, "");
3234
- const workflowName = workflowFileMap.get(resolvedPath);
3235
- if (workflowName) {
3236
- localWorkflowNameMap.set(localName, workflowName);
3237
- workflowDefaultImportNames.add(localName);
3238
- }
1948
+ function resolveRelativeImport(context, currentFilePath, importSource) {
1949
+ if (!importSource.startsWith(".")) return void 0;
1950
+ const currentDirectory = path.dirname(currentFilePath.replace(/[?#].*$/, ""));
1951
+ const modulePath = normalizeFilePath(path.resolve(currentDirectory, importSource));
1952
+ return context.modules.get(modulePath) ?? context.modules.get(path.join(modulePath, "index"));
1953
+ }
1954
+ function collectLocalTargets(program, context, currentFilePath) {
1955
+ const targets = /* @__PURE__ */ new Map();
1956
+ const currentModule = context.modules.get(normalizeFilePath(currentFilePath));
1957
+ if (currentModule) for (const [localName, target] of currentModule.localBindings) targets.set(localName, target);
1958
+ for (const statement of program.body) {
1959
+ if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
1960
+ const importSource = statement.source.value;
1961
+ if (typeof importSource !== "string") continue;
1962
+ const importedModule = resolveRelativeImport(context, currentFilePath, importSource);
1963
+ if (!importedModule) continue;
1964
+ for (const specifier of statement.specifiers) {
1965
+ if (specifier.type === "ImportNamespaceSpecifier") continue;
1966
+ if (specifier.type === "ImportSpecifier" && specifier.importKind === "type") continue;
1967
+ const importedName = specifier.type === "ImportDefaultSpecifier" ? "default" : getModuleExportName(specifier.imported);
1968
+ if (!importedName) continue;
1969
+ const target = importedModule.exports.get(importedName);
1970
+ if (!target) continue;
1971
+ targets.set(specifier.local.name, target);
3239
1972
  }
3240
1973
  }
3241
- const allTriggerCalls = detectExtendedTriggerCalls(program, source, new Set(localWorkflowNameMap.keys()), new Set(jobNameMap.keys()));
1974
+ return targets;
1975
+ }
1976
+ function detectTriggerCallsWithTargets(program, sourceText, targets) {
1977
+ const calls = [];
1978
+ walkBindingAware(program, new Set(targets.keys()), (node, shadowedNames) => {
1979
+ const triggerCall = getTriggerCallInfo(node, sourceText);
1980
+ if (!triggerCall || shadowedNames.has(triggerCall.identifierName)) return;
1981
+ const target = targets.get(triggerCall.identifierName);
1982
+ if (target) calls.push({
1983
+ ...triggerCall,
1984
+ kind: target.kind,
1985
+ targetName: target.name
1986
+ });
1987
+ });
1988
+ return calls;
1989
+ }
1990
+ function detectResolvedTriggerCalls(program, sourceText, context, currentFilePath) {
1991
+ return detectTriggerCallsWithTargets(program, sourceText, collectLocalTargets(program, context, currentFilePath));
1992
+ }
1993
+ function transformFunctionTriggers(source, triggerContext, currentFilePath) {
1994
+ const { program } = parseSync("input.ts", source);
1995
+ const localTargets = collectLocalTargets(program, triggerContext, currentFilePath);
1996
+ const { authNamespace } = triggerContext;
1997
+ const allTriggerCalls = detectTriggerCallsWithTargets(program, source, localTargets);
3242
1998
  const nestedTriggerCalls = [];
3243
1999
  const triggerCalls = allTriggerCalls.filter((call) => {
3244
2000
  const parent = allTriggerCalls.find((other) => other !== call && other.callRange.start <= call.callRange.start && call.callRange.end <= other.callRange.end);
3245
- if (parent) {
3246
- nestedTriggerCalls.push({
3247
- call,
3248
- parent
3249
- });
3250
- return false;
3251
- }
3252
- return true;
2001
+ if (!parent) return true;
2002
+ nestedTriggerCalls.push({
2003
+ call,
2004
+ parent
2005
+ });
2006
+ return false;
3253
2007
  });
3254
2008
  for (const { call, parent } of nestedTriggerCalls) logger.warn(`Nested trigger call "${call.identifierName}.trigger(...)" inside "${parent.identifierName}.trigger(...)" cannot be converted. Move it to a separate statement and pass the result instead.`);
3255
2009
  const replacements = [];
3256
2010
  let needsNormalizerHelper = false;
3257
- const transformedCallsPerIdentifier = /* @__PURE__ */ new Map();
3258
- for (const call of triggerCalls) if (call.kind === "workflow") {
3259
- const workflowName = localWorkflowNameMap.get(call.identifierName);
3260
- if (workflowName) {
2011
+ for (const call of triggerCalls) {
2012
+ let transformedCall;
2013
+ if (call.kind === "workflow") {
3261
2014
  let optionsPart = "";
3262
2015
  if (call.optionsText !== void 0) if (authNamespace) {
3263
2016
  optionsPart = `, ${NORMALIZER_IDENTIFIER}(${call.optionsText})`;
3264
2017
  needsNormalizerHelper = true;
3265
2018
  } else optionsPart = `, ${call.optionsText}`;
3266
- const transformedCall = `tailor.workflow.triggerWorkflow("${workflowName}", ${call.argsText || "undefined"}${optionsPart})`;
3267
- replacements.push({
3268
- start: call.callRange.start,
3269
- end: call.callRange.end,
3270
- text: transformedCall
3271
- });
3272
- transformedCallsPerIdentifier.set(call.identifierName, (transformedCallsPerIdentifier.get(call.identifierName) ?? 0) + 1);
3273
- }
3274
- } else {
3275
- const jobName = jobNameMap.get(call.identifierName);
3276
- if (jobName) {
2019
+ transformedCall = `tailor.workflow.triggerWorkflow(${JSON.stringify(call.targetName)}, ${call.argsText || "undefined"}${optionsPart})`;
2020
+ } else {
3277
2021
  const optionsPart = call.optionsText !== void 0 ? `, ${call.optionsText}` : "";
3278
- const transformedCall = `tailor.workflow.triggerJobFunction("${jobName}", ${call.argsText || "undefined"}${optionsPart})`;
3279
- replacements.push({
3280
- start: call.callRange.start,
3281
- end: call.callRange.end,
3282
- text: transformedCall
3283
- });
3284
- transformedCallsPerIdentifier.set(call.identifierName, (transformedCallsPerIdentifier.get(call.identifierName) ?? 0) + 1);
3285
- }
3286
- }
3287
- const refCounts = buildReferenceCountMap(program, workflowDefaultImportNames);
3288
- for (const localName of workflowDefaultImportNames) {
3289
- const transformedCount = transformedCallsPerIdentifier.get(localName) ?? 0;
3290
- const refCount = refCounts.get(localName) ?? 0;
3291
- if (refCount === 0 || transformedCount >= refCount) {
3292
- const removal = findDefaultImportRemovalRange(program, localName, source);
3293
- if (removal) replacements.push({
3294
- start: removal.start,
3295
- end: removal.isFullDeclaration ? findStatementEnd(source, removal.end) : removal.end,
3296
- text: ""
3297
- });
2022
+ transformedCall = `tailor.workflow.triggerJobFunction(${JSON.stringify(call.targetName)}, ${call.argsText || "undefined"}${optionsPart})`;
3298
2023
  }
2024
+ replacements.push({
2025
+ start: call.callRange.start,
2026
+ end: call.callRange.end,
2027
+ text: transformedCall
2028
+ });
3299
2029
  }
3300
2030
  const transformed = applyReplacements(source, replacements);
3301
- if (needsNormalizerHelper && authNamespace) return buildNormalizerHelperSource(authNamespace) + transformed;
3302
- return transformed;
2031
+ return needsNormalizerHelper && authNamespace ? buildNormalizerHelperSource(authNamespace) + transformed : transformed;
2032
+ }
2033
+ function createTriggerTransformPlugin(triggerContext) {
2034
+ if (!triggerContext) return void 0;
2035
+ return {
2036
+ name: "trigger-transform",
2037
+ transform: {
2038
+ filter: { id: { include: [/\.(ts|mts|cts|js|mjs|cjs)$/] } },
2039
+ handler(code, id) {
2040
+ if (!code.includes(".trigger(")) return null;
2041
+ return { code: transformFunctionTriggers(code, triggerContext, id) };
2042
+ }
2043
+ }
2044
+ };
3303
2045
  }
3304
2046
 
3305
2047
  //#endregion
3306
- //#region src/cli/shared/trigger-context.ts
3307
- /**
3308
- * Normalize a file path by removing extension and resolving to absolute path
3309
- * @param filePath - File path to normalize
3310
- * @returns Normalized absolute path without extension
3311
- */
3312
- function normalizeFilePath(filePath) {
3313
- const absolutePath = path.resolve(filePath);
3314
- const ext = path.extname(absolutePath);
3315
- return absolutePath.slice(0, -ext.length);
2048
+ //#region src/cli/shared/bundle-log-level.ts
2049
+ const INFO_LEVEL_CONSOLE_METHODS = [
2050
+ "console.info",
2051
+ "console.table",
2052
+ "console.dir",
2053
+ "console.dirxml",
2054
+ "console.count",
2055
+ "console.countReset",
2056
+ "console.time",
2057
+ "console.timeLog",
2058
+ "console.timeEnd",
2059
+ "console.group",
2060
+ "console.groupCollapsed",
2061
+ "console.groupEnd",
2062
+ "console.clear"
2063
+ ];
2064
+ const DEBUG_LEVEL_CONSOLE_METHODS = [
2065
+ "console.debug",
2066
+ "console.log",
2067
+ "console.trace"
2068
+ ];
2069
+ const WARN_LEVEL_CONSOLE_METHODS = ["console.warn"];
2070
+ const ERROR_LEVEL_CONSOLE_METHODS = ["console.error"];
2071
+ const MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL = {
2072
+ DEBUG: [],
2073
+ INFO: DEBUG_LEVEL_CONSOLE_METHODS,
2074
+ WARN: [...DEBUG_LEVEL_CONSOLE_METHODS, ...INFO_LEVEL_CONSOLE_METHODS],
2075
+ ERROR: [
2076
+ ...DEBUG_LEVEL_CONSOLE_METHODS,
2077
+ ...INFO_LEVEL_CONSOLE_METHODS,
2078
+ ...WARN_LEVEL_CONSOLE_METHODS
2079
+ ],
2080
+ SILENT: [
2081
+ ...DEBUG_LEVEL_CONSOLE_METHODS,
2082
+ ...INFO_LEVEL_CONSOLE_METHODS,
2083
+ ...WARN_LEVEL_CONSOLE_METHODS,
2084
+ ...ERROR_LEVEL_CONSOLE_METHODS
2085
+ ]
2086
+ };
2087
+ function normalizeBundleLogLevel(value) {
2088
+ const normalized = value.trim().toUpperCase();
2089
+ return isLogLevel(normalized) ? normalized : void 0;
3316
2090
  }
3317
- /**
3318
- * Build trigger context from workflow configuration
3319
- * Scans workflow files to collect workflow and job mappings
3320
- * @param workflowConfig - Workflow file loading configuration
3321
- * @param authNamespace - Auth service namespace (optional, used for string-literal invoker expansion)
3322
- * @returns Trigger context built from workflow sources
3323
- */
3324
- async function buildTriggerContext(workflowConfig, authNamespace) {
3325
- const workflowNameMap = /* @__PURE__ */ new Map();
3326
- const jobNameMap = /* @__PURE__ */ new Map();
3327
- const workflowFileMap = /* @__PURE__ */ new Map();
3328
- if (!workflowConfig) return {
3329
- workflowNameMap,
3330
- jobNameMap,
3331
- workflowFileMap,
3332
- authNamespace
3333
- };
3334
- const workflowFiles = loadFilesWithIgnores(workflowConfig);
3335
- for (const file of workflowFiles) try {
3336
- const source = await fs$1.promises.readFile(file, "utf-8");
3337
- const { program } = parseSync("input.ts", source);
3338
- const workflows = findAllWorkflows(program, source);
3339
- const workflowMap = buildWorkflowNameMap(workflows);
3340
- for (const [exportName, workflowName] of workflowMap) workflowNameMap.set(exportName, workflowName);
3341
- for (const workflow of workflows) if (workflow.isDefaultExport) {
3342
- const normalizedPath = normalizeFilePath(file);
3343
- workflowFileMap.set(normalizedPath, workflow.name);
3344
- }
3345
- const jobMap = buildJobNameMap(findAllJobs(program, source));
3346
- for (const [exportName, jobName] of jobMap) jobNameMap.set(exportName, jobName);
3347
- } catch (error) {
3348
- const errorMessage = error instanceof Error ? error.message : String(error);
3349
- logger.warn(`Failed to process workflow file ${file}: ${errorMessage}`, { mode: "stream" });
3350
- continue;
3351
- }
3352
- return {
3353
- workflowNameMap,
3354
- jobNameMap,
3355
- workflowFileMap,
3356
- authNamespace
3357
- };
2091
+ function resolveBundleLogLevel(configValue) {
2092
+ if (configValue === void 0) return "DEBUG";
2093
+ const resolved = normalizeBundleLogLevel(configValue);
2094
+ if (resolved) return resolved;
2095
+ throw new Error(`Invalid logLevel "${configValue}". Expected one of: ${LOG_LEVELS.join(", ")}`);
3358
2096
  }
3359
- function sortedMapToJson(m) {
3360
- return JSON.stringify([...m.entries()].toSorted(([a], [b]) => a.localeCompare(b)));
2097
+ function manualPureFunctionsForLogLevel(logLevel) {
2098
+ return MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL[logLevel];
3361
2099
  }
3362
- /**
3363
- * Serialize trigger context to a deterministic string for cache hashing.
3364
- * Returns an empty string if no context is provided.
3365
- * @param ctx - Trigger context to serialize
3366
- * @returns Deterministic string representation
3367
- */
3368
- function serializeTriggerContext(ctx) {
3369
- if (!ctx) return "";
3370
- return sortedMapToJson(ctx.workflowNameMap) + sortedMapToJson(ctx.jobNameMap) + sortedMapToJson(ctx.workflowFileMap) + (ctx.authNamespace ?? "");
2100
+ function createLogLevelTreeshakeOptions(logLevel) {
2101
+ const manualPureFunctions = manualPureFunctionsForLogLevel(logLevel);
2102
+ return manualPureFunctions.length > 0 ? { manualPureFunctions } : {};
3371
2103
  }
2104
+
2105
+ //#endregion
2106
+ //#region src/cli/shared/function-treeshake.ts
2107
+ const BASE_FUNCTION_TREESHAKE_OPTIONS = {
2108
+ moduleSideEffects: false,
2109
+ annotations: true,
2110
+ unknownGlobalSideEffects: false
2111
+ };
2112
+ function mergeFunctionTreeshakeOptions(fragments) {
2113
+ const merged = {};
2114
+ const manualPureFunctions = /* @__PURE__ */ new Set();
2115
+ for (const fragment of fragments) {
2116
+ Object.assign(merged, fragment);
2117
+ for (const name of fragment.manualPureFunctions ?? []) manualPureFunctions.add(name);
2118
+ }
2119
+ if (manualPureFunctions.size > 0) merged.manualPureFunctions = [...manualPureFunctions];
2120
+ else delete merged.manualPureFunctions;
2121
+ return merged;
2122
+ }
2123
+ function composeFunctionTreeshakeOptions(fragments = []) {
2124
+ return mergeFunctionTreeshakeOptions([BASE_FUNCTION_TREESHAKE_OPTIONS, ...fragments]);
2125
+ }
2126
+
2127
+ //#endregion
2128
+ //#region src/cli/shared/platform-bundle-plugin.ts
2129
+ const GATE = /(?<![\w$.])process\.env\.__TAILOR_PLATFORM_BUNDLE\b/g;
2130
+ const platformBundleDefinePlugin = {
2131
+ name: "tailor-platform-bundle-define",
2132
+ transform(code) {
2133
+ if (!code.includes("process.env.__TAILOR_PLATFORM_BUNDLE")) return null;
2134
+ return { code: code.replace(GATE, "true") };
2135
+ }
2136
+ };
2137
+
2138
+ //#endregion
2139
+ //#region src/cli/shared/virtual-entry.ts
3372
2140
  /**
3373
- * Create a rolldown plugin for transforming trigger calls
3374
- * Returns undefined if no trigger context is provided
3375
- * @param triggerContext - Trigger context to use for transformations
3376
- * @returns Rolldown plugin or undefined when no context
2141
+ * Create an in-memory rolldown entry module with a deterministic ID.
2142
+ * @param name - Logical entry name
2143
+ * @param code - Entry module source
2144
+ * @param sourceType - Parser type for the generated module
2145
+ * @returns Rolldown input and plugin for loading the entry
3377
2146
  */
3378
- function createTriggerTransformPlugin(triggerContext) {
3379
- if (!triggerContext) return;
2147
+ function createVirtualEntry(name, code, sourceType = "js") {
2148
+ const input = `tailor-sdk-entry:${name}.${sourceType}`;
2149
+ const resolvedId = `\0${input}`;
3380
2150
  return {
3381
- name: "trigger-transform",
3382
- transform: {
3383
- filter: { id: { include: [/\.(ts|mts|cts|js|mjs|cjs)$/] } },
3384
- handler(code, id) {
3385
- if (!code.includes(".trigger(")) return null;
3386
- return { code: transformFunctionTriggers(code, triggerContext.workflowNameMap, triggerContext.jobNameMap, triggerContext.workflowFileMap, id, triggerContext.authNamespace) };
2151
+ input,
2152
+ plugin: {
2153
+ name: "tailor-sdk-virtual-entry",
2154
+ resolveId(source, importer) {
2155
+ return source === input && importer === void 0 ? resolvedId : null;
2156
+ },
2157
+ load(id) {
2158
+ return id === resolvedId ? code : null;
3387
2159
  }
3388
2160
  }
3389
2161
  };
@@ -3395,7 +2167,7 @@ function createTriggerTransformPlugin(triggerContext) {
3395
2167
  * Bundle a single auth hook handler.
3396
2168
  *
3397
2169
  * Follows the same pattern as the executor bundler:
3398
- * 1. Generate an entry file that re-exports the handler as `main`
2170
+ * 1. Generate an in-memory entry module that re-exports the handler as `main`
3399
2171
  * 2. Bundle with rolldown + tree-shaking
3400
2172
  * @param options - Bundle options
3401
2173
  * @returns Map of function name to bundled code
@@ -3404,9 +2176,6 @@ async function bundleAuthHooks(options) {
3404
2176
  const { configPath, authName, handlerAccessPath, env = {}, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG" } = options;
3405
2177
  logger.newline();
3406
2178
  logger.log(`Bundling auth hook for ${styles.info(`"${authName}"`)}`);
3407
- const outputDir = path.resolve(getDistDir(), "auth-hooks");
3408
- fs$1.mkdirSync(outputDir, { recursive: true });
3409
- await removeStaleEntryFiles(outputDir);
3410
2179
  let tsconfig;
3411
2180
  try {
3412
2181
  tsconfig = await resolveTSConfig();
@@ -3431,7 +2200,6 @@ async function bundleAuthHooks(options) {
3431
2200
  prefix: sortedEnvPrefix
3432
2201
  }),
3433
2202
  async build(cachePlugins) {
3434
- const entryPath = path.join(outputDir, `${functionName}.entry.js`);
3435
2203
  const entryContent = multiline`
3436
2204
  import _config from "${absoluteConfigPath}";
3437
2205
  const __auth_hook_function = _config.${handlerAccessPath};
@@ -3440,12 +2208,13 @@ async function bundleAuthHooks(options) {
3440
2208
  return await __auth_hook_function({ ...args, env });
3441
2209
  }
3442
2210
  `;
3443
- fs$1.writeFileSync(entryPath, entryContent);
2211
+ const entry = createVirtualEntry(`auth-hook:${functionName}`, entryContent);
3444
2212
  const triggerPlugin = createTriggerTransformPlugin(triggerContext);
3445
- const plugins = triggerPlugin ? [triggerPlugin] : [];
2213
+ const plugins = [entry.plugin];
2214
+ if (triggerPlugin) plugins.push(triggerPlugin);
3446
2215
  plugins.push(platformBundleDefinePlugin, ...cachePlugins);
3447
2216
  return (await rolldown.build({
3448
- input: entryPath,
2217
+ input: entry.input,
3449
2218
  write: false,
3450
2219
  output: {
3451
2220
  format: "esm",
@@ -3624,7 +2393,17 @@ function formatScriptContext(kind, context) {
3624
2393
  const convertToScriptExpr = (fn, kind, context) => {
3625
2394
  const precompiledExpr = getPrecompiledScriptExpr(fn);
3626
2395
  if (precompiledExpr) return precompiledExpr;
3627
- return assertParsableExpression(`(${stringifyFunction(fn)})({ value: _value, data: _data, invoker: ${tailorPrincipalMap} })`, formatScriptContext(kind, context));
2396
+ return assertParsableExpression(`(${stringifyFunction(fn)})(${kind === "validate" ? `{ value: _value }` : kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }`})`, formatScriptContext(kind, context));
2397
+ };
2398
+ const convertTypeHookToExpr = (fn) => {
2399
+ const precompiledExpr = getPrecompiledScriptExpr(fn);
2400
+ if (precompiledExpr) return precompiledExpr;
2401
+ return assertParsableExpression(`(${stringifyFunction(fn)})({ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now })`, "type-hook");
2402
+ };
2403
+ const convertTypeValidateToExpr = (fn) => {
2404
+ const precompiledExpr = getPrecompiledScriptExpr(fn);
2405
+ if (precompiledExpr) return precompiledExpr;
2406
+ return assertParsableExpression(`(${stringifyFunction(fn)})({ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues)`, "type-validate");
3628
2407
  };
3629
2408
  /**
3630
2409
  * Parse TailorDBField into OperatorFieldConfig.
@@ -3637,6 +2416,8 @@ function parseFieldConfig(field, context) {
3637
2416
  const metadata = field.metadata;
3638
2417
  const fieldType = field.type;
3639
2418
  const rawRelation = field.rawRelation;
2419
+ if (context && context.fieldPath.length > 1 && metadata.default !== void 0) throw new Error(`Field "${context.fieldPath.join(".")}" on type "${context.typeName}": .default() cannot be used on nested inner fields`);
2420
+ if (context && context.fieldPath.length > 1 && metadata.hooks) throw new Error(`Field "${context.fieldPath.join(".")}" on type "${context.typeName}": .hooks() cannot be used on nested inner fields`);
3640
2421
  const nestedFields = field.fields;
3641
2422
  return {
3642
2423
  type: fieldType,
@@ -3649,19 +2430,10 @@ function parseFieldConfig(field, context) {
3649
2430
  });
3650
2431
  return acc;
3651
2432
  }, {}) } : {},
3652
- validate: metadata.validate?.map((v) => {
3653
- const { fn, message } = typeof v === "function" ? {
3654
- fn: v,
3655
- message: `failed by \`${v.toString().trim()}\``
3656
- } : {
3657
- fn: v[0],
3658
- message: v[1]
3659
- };
3660
- return {
3661
- script: { expr: convertToScriptExpr(fn, "validate", context) },
3662
- errorMessage: message
3663
- };
3664
- }),
2433
+ validate: metadata.validate?.map((fn) => ({
2434
+ script: { expr: convertToScriptExpr(fn, "validate", context) },
2435
+ errorMessage: ""
2436
+ })),
3665
2437
  hooks: metadata.hooks ? {
3666
2438
  create: metadata.hooks.create ? { expr: convertToScriptExpr(metadata.hooks.create, "hooks.create", context) } : void 0,
3667
2439
  update: metadata.hooks.update ? { expr: convertToScriptExpr(metadata.hooks.update, "hooks.update", context) } : void 0
@@ -3807,6 +2579,36 @@ function findOmittedPermitRules(rawPermissions) {
3807
2579
  });
3808
2580
  return locations;
3809
2581
  }
2582
+ /**
2583
+ * Check whether GraphQL exposure is fully disabled for a type, given its
2584
+ * effective gqlOperations (the type's own setting, falling back to the
2585
+ * TailorDB namespace default). `undefined` means the default of all
2586
+ * operations enabled, so it is never considered fully disabled.
2587
+ * @param gqlOperations - Effective, normalized gqlOperations configuration
2588
+ * @returns Whether create, update, delete, and read are all explicitly disabled
2589
+ */
2590
+ function isGqlOperationsFullyDisabled(gqlOperations) {
2591
+ if (!gqlOperations) return false;
2592
+ return gqlOperations.create === false && gqlOperations.update === false && gqlOperations.delete === false && gqlOperations.read === false;
2593
+ }
2594
+ /**
2595
+ * Find missing permission configuration for a TailorDB type.
2596
+ *
2597
+ * Record-level permission is always required: TailorDB denies all record
2598
+ * operations for a type without it, regardless of whether the type is
2599
+ * exposed via GraphQL. GraphQL permission is required whenever GraphQL
2600
+ * exposure is enabled for the type (the default, unless every operation is
2601
+ * explicitly disabled via `gqlOperations`).
2602
+ * @param rawPermissions - Raw permissions definition for the type
2603
+ * @param effectiveGqlOperations - The type's own gqlOperations, falling back to the namespace default
2604
+ * @returns Which permission configuration, if any, is missing
2605
+ */
2606
+ function findMissingPermissionConfig(rawPermissions, effectiveGqlOperations) {
2607
+ return {
2608
+ missingPermission: !rawPermissions.record,
2609
+ missingGqlPermission: !rawPermissions.gql && !isGqlOperationsFullyDisabled(effectiveGqlOperations)
2610
+ };
2611
+ }
3810
2612
 
3811
2613
  //#endregion
3812
2614
  //#region src/parser/service/tailordb/type-parser.ts
@@ -3901,7 +2703,12 @@ function parseTailorDBType(type, allTypeNames, rawTypes, typeSourceInfo) {
3901
2703
  settings: metadata.settings ?? {},
3902
2704
  permissions: parsePermissions(metadata.permissions),
3903
2705
  indexes: metadata.indexes,
3904
- files: metadata.files
2706
+ files: metadata.files,
2707
+ ...metadata.typeHook && { typeHookExpr: {
2708
+ ...typeof metadata.typeHook.create === "function" && { create: convertTypeHookToExpr(metadata.typeHook.create) },
2709
+ ...typeof metadata.typeHook.update === "function" && { update: convertTypeHookToExpr(metadata.typeHook.update) }
2710
+ } },
2711
+ ...typeof metadata.typeValidate === "function" && { typeValidateExpr: convertTypeValidateToExpr(metadata.typeValidate) }
3905
2712
  };
3906
2713
  }
3907
2714
  /**
@@ -4092,29 +2899,35 @@ function collectScriptTargets(type) {
4092
2899
  const createHook = toScriptFunction(metadata.hooks?.create);
4093
2900
  if (createHook) targets.push({
4094
2901
  fn: createHook,
4095
- kind: "hooks"
2902
+ kind: "hooks.create"
4096
2903
  });
4097
2904
  const updateHook = toScriptFunction(metadata.hooks?.update);
4098
2905
  if (updateHook) targets.push({
4099
2906
  fn: updateHook,
4100
- kind: "hooks"
2907
+ kind: "hooks.update"
4101
2908
  });
4102
- for (const validateInput of metadata.validate ?? []) if (typeof validateInput === "function") {
2909
+ for (const validateInput of metadata.validate ?? []) {
4103
2910
  const validateFn = toScriptFunction(validateInput);
4104
2911
  if (validateFn) targets.push({
4105
2912
  fn: validateFn,
4106
2913
  kind: "validate"
4107
2914
  });
4108
- } else {
4109
- const validateFn = toScriptFunction(validateInput[0]);
4110
- if (validateFn) targets.push({
4111
- fn: validateFn,
4112
- kind: "validate"
4113
- });
4114
2915
  }
4115
2916
  if (field.type === "nested" && field.fields) for (const nestedField of Object.values(field.fields)) collectFieldTargets(nestedField);
4116
2917
  };
4117
2918
  for (const field of Object.values(type.fields)) collectFieldTargets(field);
2919
+ if (type.metadata.typeHook) for (const op of ["create", "update"]) {
2920
+ const fn = toScriptFunction(type.metadata.typeHook[op]);
2921
+ if (fn) targets.push({
2922
+ fn,
2923
+ kind: "typeHook"
2924
+ });
2925
+ }
2926
+ const typeValidateFn = toScriptFunction(type.metadata.typeValidate);
2927
+ if (typeValidateFn) targets.push({
2928
+ fn: typeValidateFn,
2929
+ kind: "typeValidate"
2930
+ });
4118
2931
  return targets;
4119
2932
  }
4120
2933
  /**
@@ -4308,11 +3121,11 @@ function resolveNeededBindings(freeVars, sourceBindings) {
4308
3121
  unresolved
4309
3122
  };
4310
3123
  }
4311
- function buildPrecompiledExpr(bundleCode) {
3124
+ function buildPrecompiledExpr(bundleCode, argsObject) {
4312
3125
  return `(() => {
4313
3126
  const module = { exports: {} };
4314
3127
  const exports = module.exports;
4315
- ${bundleCode}\n return module.exports.main({ value: _value, data: _data, invoker: ${tailorPrincipalMap} });\n})()`;
3128
+ ${bundleCode}\n return module.exports.main(${argsObject});\n})()`;
4316
3129
  }
4317
3130
  /**
4318
3131
  * Build entry file content from already-resolved imports and declarations.
@@ -4320,32 +3133,34 @@ ${bundleCode}\n return module.exports.main({ value: _value, data: _data, invoke
4320
3133
  * @param declarations - Declaration statement texts.
4321
3134
  * @param fnSource - The function source code.
4322
3135
  * @param sourceFilePath - Path to the source file for resolving relative imports.
3136
+ * @param multiArg - Whether the function accepts multiple arguments (spread via `...args`).
4323
3137
  * @returns Entry file content string.
4324
3138
  */
4325
- function buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath) {
3139
+ function buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath, multiArg = false) {
4326
3140
  const sourceDir = resolve(sourceFilePath, "..").replace(/\\/g, "/");
4327
3141
  return [
4328
3142
  ...imports.map((imp) => imp.replace(/from\s+["'](\.[^"']+)["']/g, (_match, relPath) => `from "${resolve(sourceDir, relPath).replace(/\\/g, "/")}"`)),
4329
3143
  ...declarations,
4330
- `export function main(input) { return (${fnSource})(input); }`
3144
+ multiArg ? `export function main(...args) { return (${fnSource})(...args); }` : `export function main(input) { return (${fnSource})(input); }`
4331
3145
  ].join("\n");
4332
3146
  }
4333
3147
  async function bundleScriptTarget(args) {
4334
- const { fn, kind, sourceFilePath, sourceBindings, tempDir, targetIndex, tsconfig } = args;
3148
+ const { fn, kind, sourceFilePath, sourceBindings, typeName, targetIndex, tsconfig } = args;
4335
3149
  const context = `${kind} in ${sourceFilePath}`;
4336
3150
  const fnSource = stringifyFunction(fn);
4337
- const inlineExpr = assertParsableExpression(`(${fnSource})({ value: _value, data: _data, invoker: ${tailorPrincipalMap} })`, context);
3151
+ if ((kind === "typeValidate" || kind === "validate") && fn.constructor.name === "AsyncFunction") throw new Error(`${context} must be synchronous — the generated validator runs synchronously, so issues reported after an await are silently lost. Remove the async keyword.`);
3152
+ const argsObject = kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "hooks.update" ? `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "validate" ? `{ value: _value }` : kind === "typeHook" ? `{ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now }` : `{ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues`;
3153
+ const inlineExpr = assertParsableExpression(`(${fnSource})(${argsObject})`, context);
4338
3154
  const freeVars = findUndefinedReferences(`const __fn = ${fnSource};`);
4339
3155
  if (freeVars.size === 0) return inlineExpr;
4340
3156
  const { imports, declarations, unresolved } = resolveNeededBindings(freeVars, sourceBindings);
4341
3157
  if (unresolved.length > 0) throw new Error(`${context} captures unresolvable variables (${unresolved.join(", ")}). Hooks and validators must not reference variables that cannot be resolved from the source file.
4342
3158
  ${kind}: ${fnSource}`);
4343
- const entryContent = buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath);
4344
- const entryPath = join(tempDir, `tailordb-script-${targetIndex}.entry.ts`);
4345
- writeFileSync(entryPath, entryContent);
3159
+ const entryContent = buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath, kind === "typeValidate");
3160
+ const entry = createVirtualEntry(`tailordb-script:${typeName}:${targetIndex}`, entryContent, "ts");
4346
3161
  const bundledCode = (await rolldown.build({
4347
- plugins: [platformBundleDefinePlugin],
4348
- input: entryPath,
3162
+ plugins: [entry.plugin, platformBundleDefinePlugin],
3163
+ input: entry.input,
4349
3164
  write: false,
4350
3165
  output: {
4351
3166
  format: "cjs",
@@ -4361,7 +3176,7 @@ async function bundleScriptTarget(args) {
4361
3176
  },
4362
3177
  logLevel: "silent"
4363
3178
  })).output[0].code;
4364
- return assertParsableExpression(buildPrecompiledExpr(bundledCode), context);
3179
+ return assertParsableExpression(buildPrecompiledExpr(bundledCode, argsObject), context);
4365
3180
  }
4366
3181
  /**
4367
3182
  * Precompile TailorDB hooks/validators into self-contained script expressions using rolldown.
@@ -4375,27 +3190,18 @@ async function precompileTailorDBTypeScripts(type, sourceFilePath, tsconfig) {
4375
3190
  const targets = collectScriptTargets(type);
4376
3191
  if (targets.length === 0) return;
4377
3192
  const sourceBindings = collectSourceBindings(sourceFilePath);
4378
- const tempDir = resolve(getDistDir(), "hooks-validate-scripts", type.name);
4379
- mkdirSync(tempDir, { recursive: true });
4380
- try {
4381
- const results = await Promise.allSettled(targets.map((target, index) => bundleScriptTarget({
4382
- fn: target.fn,
4383
- kind: target.kind,
4384
- sourceFilePath,
4385
- sourceBindings,
4386
- tempDir,
4387
- targetIndex: index,
4388
- tsconfig
4389
- })));
4390
- const firstError = results.find((r) => r.status === "rejected");
4391
- if (firstError) throw firstError.reason;
4392
- for (const [index, result] of results.entries()) if (result.status === "fulfilled") setPrecompiledScriptExpr(assertDefined(targets[index], `bundle target at index ${index} missing`).fn, result.value);
4393
- } finally {
4394
- rmSync(tempDir, {
4395
- recursive: true,
4396
- force: true
4397
- });
4398
- }
3193
+ const results = await Promise.allSettled(targets.map((target, index) => bundleScriptTarget({
3194
+ fn: target.fn,
3195
+ kind: target.kind,
3196
+ sourceFilePath,
3197
+ sourceBindings,
3198
+ typeName: type.name,
3199
+ targetIndex: index,
3200
+ tsconfig
3201
+ })));
3202
+ const firstError = results.find((r) => r.status === "rejected");
3203
+ if (firstError) throw firstError.reason;
3204
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") setPrecompiledScriptExpr(assertDefined(targets[index], `bundle target at index ${index} missing`).fn, result.value);
4399
3205
  }
4400
3206
 
4401
3207
  //#endregion
@@ -4553,6 +3359,19 @@ function createTailorDBService(params) {
4553
3359
  if (locations.length > 0) logger.warn(`TailorDB type "${typeName}" has permission rule(s) ${locations.join(", ")} in object form without an explicit "permit"; they default to "deny". Set permit: true (allow) or permit: false (deny) to silence this warning.`);
4554
3360
  }
4555
3361
  };
3362
+ const validateRequiredPermissions = () => {
3363
+ const errors = [];
3364
+ for (const fileTypes of Object.values(rawTypes)) for (const [typeName, type] of Object.entries(fileTypes)) {
3365
+ const effectiveGqlOperations = type.metadata.settings?.gqlOperations ?? config.gqlOperations;
3366
+ const { missingPermission, missingGqlPermission } = findMissingPermissionConfig(type.metadata.permissions, effectiveGqlOperations);
3367
+ if (!missingPermission && !missingGqlPermission) continue;
3368
+ const source = formatTailorDBTypeSourceInfo(typeSourceInfo[typeName]);
3369
+ const location = source ? ` (${source})` : "";
3370
+ if (missingPermission) errors.push(`TailorDB type "${typeName}"${location} has no .permission() configured. TailorDB denies all record operations for types without permission; call .permission(...) to grant access explicitly.`);
3371
+ if (missingGqlPermission) errors.push(`TailorDB type "${typeName}"${location} has no .gqlPermission() configured, but GraphQL operations are enabled for it. Call .gqlPermission(...) to grant GraphQL access explicitly, or disable GraphQL exposure with .features({ gqlOperations: { create: false, update: false, delete: false, read: false } }).`);
3372
+ }
3373
+ if (errors.length > 0) throw new Error(`TailorDB permission configuration errors in service "${namespace}":\n${errors.map((e) => ` - ${e}`).join("\n")}`);
3374
+ };
4556
3375
  /**
4557
3376
  * Process plugins for a type and add generated types to rawTypes
4558
3377
  * @param rawType - The raw TailorDB type being processed
@@ -4645,6 +3464,7 @@ function createTailorDBService(params) {
4645
3464
  else await Promise.all(typeFiles.map((typeFile) => loadTypeFile(typeFile, tsconfig)));
4646
3465
  doParseTypes();
4647
3466
  warnOmittedPermit();
3467
+ validateRequiredPermissions();
4648
3468
  return types;
4649
3469
  })();
4650
3470
  return loadPromise;
@@ -4686,7 +3506,10 @@ function createTailorDBService(params) {
4686
3506
  hasGeneratedTypes = true;
4687
3507
  logger.log(` Generated: ${styles.success(generatedType.name)} by namespace plugin ${styles.info(pluginId)}`);
4688
3508
  }
4689
- if (hasGeneratedTypes || hadPreviousGeneratedTypes) doParseTypes();
3509
+ if (hasGeneratedTypes || hadPreviousGeneratedTypes) {
3510
+ doParseTypes();
3511
+ validateRequiredPermissions();
3512
+ }
4690
3513
  }
4691
3514
  };
4692
3515
  }
@@ -4773,9 +3596,31 @@ function resolveBundleConcurrency() {
4773
3596
  * @param worker - Async worker function
4774
3597
  * @returns Worker results in input order
4775
3598
  */
4776
- function withBundleConcurrency(items, worker) {
4777
- const limit = pLimit(resolveBundleConcurrency());
4778
- return Promise.all(items.map((item) => limit(() => worker(item))));
3599
+ async function withBundleConcurrency(items, worker) {
3600
+ const resultCount = items.length;
3601
+ const workItems = items.flatMap((item, index) => [{
3602
+ index,
3603
+ item
3604
+ }]);
3605
+ const results = [];
3606
+ results.length = resultCount;
3607
+ let nextWorkIndex = 0;
3608
+ let rejection;
3609
+ const runWorker = async () => {
3610
+ while (!rejection) {
3611
+ const workItem = workItems[nextWorkIndex++];
3612
+ if (workItem === void 0) return;
3613
+ try {
3614
+ results[workItem.index] = await worker(workItem.item);
3615
+ } catch (reason) {
3616
+ rejection ??= { reason };
3617
+ }
3618
+ }
3619
+ };
3620
+ const workerCount = Math.min(resolveBundleConcurrency(), workItems.length);
3621
+ await Promise.all(Array.from({ length: workerCount }, runWorker));
3622
+ if (rejection) throw rejection.reason;
3623
+ return results;
4779
3624
  }
4780
3625
 
4781
3626
  //#endregion
@@ -4786,7 +3631,7 @@ function withBundleConcurrency(items, worker) {
4786
3631
  * Two delivery paths:
4787
3632
  * - Apply config: shipped with apply and evaluated by the platform before
4788
3633
  * invoking user code.
4789
- * - Bundle inline: interpolated into the generated `.entry.js` wrapper and
3634
+ * - Bundle inline: interpolated into the generated entry module and
4790
3635
  * evaluated inside the bundled script at function entry.
4791
3636
  *
4792
3637
  * The principal field mapping (server → SDK) shared across services is built by
@@ -4876,7 +3721,7 @@ function buildResolverOperationHookExpr(env) {
4876
3721
  * Bundle executors from the specified configuration
4877
3722
  *
4878
3723
  * This function:
4879
- * 1. Creates entry file that extracts operation.body
3724
+ * 1. Creates an in-memory entry module that extracts operation.body
4880
3725
  * 2. Bundles in a single step with tree-shaking
4881
3726
  * @param options - Bundle executor options
4882
3727
  * @returns Map of executor name to bundled code
@@ -4911,21 +3756,18 @@ async function bundleExecutors(options) {
4911
3756
  logger.debug(" No function executors to bundle");
4912
3757
  return bundledCode;
4913
3758
  }
4914
- const outputDir = path.resolve(getDistDir(), "executors");
4915
- fs$1.mkdirSync(outputDir, { recursive: true });
4916
- await removeStaleEntryFiles(outputDir);
4917
3759
  let tsconfig;
4918
3760
  try {
4919
3761
  tsconfig = await resolveTSConfig();
4920
3762
  } catch {
4921
3763
  tsconfig = void 0;
4922
3764
  }
4923
- const results = await withBundleConcurrency(executors, (executor) => bundleSingleExecutor(executor, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
3765
+ const results = await withBundleConcurrency(executors, (executor) => bundleSingleExecutor(executor, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
4924
3766
  for (const [name, code] of results) bundledCode.set(name, code);
4925
3767
  logger.log(`${styles.success("Bundled")} ${styles.info("\"executor\"")}`);
4926
3768
  return bundledCode;
4927
3769
  }
4928
- async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
3770
+ async function bundleSingleExecutor(executor, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
4929
3771
  const serializedTriggerContext = serializeTriggerContext(triggerContext);
4930
3772
  const contextHash = computeBundlerContextHash({
4931
3773
  sourceFile: executor.sourceFile,
@@ -4941,7 +3783,6 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContex
4941
3783
  sourceFile: executor.sourceFile,
4942
3784
  contextHash,
4943
3785
  async build(cachePlugins) {
4944
- const entryPath = path.join(outputDir, `${executor.name}.entry.js`);
4945
3786
  const entryContent = multiline`
4946
3787
  import _internalExecutor from "${path.resolve(executor.sourceFile)}";
4947
3788
 
@@ -4952,12 +3793,13 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContex
4952
3793
 
4953
3794
  export { __executor_function as main };
4954
3795
  `;
4955
- fs$1.writeFileSync(entryPath, entryContent);
3796
+ const entry = createVirtualEntry(`executor:${executor.name}`, entryContent);
4956
3797
  const triggerPlugin = createTriggerTransformPlugin(triggerContext);
4957
- const plugins = triggerPlugin ? [triggerPlugin] : [];
3798
+ const plugins = [entry.plugin];
3799
+ if (triggerPlugin) plugins.push(triggerPlugin);
4958
3800
  plugins.push(platformBundleDefinePlugin, ...cachePlugins);
4959
3801
  return (await rolldown.build({
4960
- input: entryPath,
3802
+ input: entry.input,
4961
3803
  write: false,
4962
3804
  output: {
4963
3805
  format: "esm",
@@ -5040,8 +3882,6 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
5040
3882
  };
5041
3883
  logger.newline();
5042
3884
  logger.log(`Bundling ${styles.highlight(adapters.length.toString())} files for ${styles.info("\"http-adapter\"")}`);
5043
- const outputDir = path.resolve(getDistDir(), "http-adapters");
5044
- fs$1.mkdirSync(outputDir, { recursive: true });
5045
3885
  let tsconfig;
5046
3886
  try {
5047
3887
  tsconfig = await resolveTSConfig();
@@ -5053,7 +3893,7 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
5053
3893
  adapter,
5054
3894
  kind
5055
3895
  }));
5056
- }), ({ adapter, kind }) => bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bundleLogLevel));
3896
+ }), ({ adapter, kind }) => bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel));
5057
3897
  const bundledInputs = /* @__PURE__ */ new Map();
5058
3898
  const bundledOutputs = /* @__PURE__ */ new Map();
5059
3899
  for (const [name, kind, code] of results) if (kind === "input") bundledInputs.set(name, code);
@@ -5064,7 +3904,7 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
5064
3904
  bundledOutputs
5065
3905
  };
5066
3906
  }
5067
- async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bundleLogLevel = "DEBUG") {
3907
+ async function bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel = "DEBUG") {
5068
3908
  const contextHash = computeBundlerContextHash({
5069
3909
  sourceFile: adapter.sourceFile,
5070
3910
  serializedTriggerContext: kind === "input" ? adapter.methods.join(",") : "",
@@ -5080,9 +3920,11 @@ async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bu
5080
3920
  sourceFile: adapter.sourceFile,
5081
3921
  contextHash,
5082
3922
  async build(cachePlugins) {
5083
- const entryPath = path.join(outputDir, `${adapter.name}.${kind}.entry.js`);
5084
3923
  const absoluteSourcePath = path.resolve(adapter.sourceFile);
3924
+ const entryContent = kind === "input" ? buildInputEntry(absoluteSourcePath, adapter.methods, GRAPHQL_WEB_MODULE) : buildOutputEntry(absoluteSourcePath);
3925
+ const entry = createVirtualEntry(`http-adapter:${adapter.name}:${kind}`, entryContent);
5085
3926
  const plugins = [
3927
+ entry.plugin,
5086
3928
  {
5087
3929
  name: "http-adapter-reject-node-imports",
5088
3930
  resolveId(source) {
@@ -5106,30 +3948,21 @@ async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bu
5106
3948
  },
5107
3949
  ...cachePlugins
5108
3950
  ];
5109
- let bundled;
5110
- try {
5111
- const entryContent = kind === "input" ? buildInputEntry(absoluteSourcePath, adapter.methods, GRAPHQL_WEB_MODULE) : buildOutputEntry(absoluteSourcePath);
5112
- fs$1.writeFileSync(entryPath, entryContent);
5113
- bundled = (await rolldown.build({
5114
- input: entryPath,
5115
- write: false,
5116
- output: {
5117
- format: "iife",
5118
- sourcemap: false,
5119
- minify: true,
5120
- codeSplitting: false
5121
- },
5122
- tsconfig,
5123
- plugins,
5124
- transform: { target: "es2017" },
5125
- treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
5126
- logLevel: "silent"
5127
- })).output[0].code;
5128
- } finally {
5129
- try {
5130
- fs$1.rmSync(entryPath, { force: true });
5131
- } catch {}
5132
- }
3951
+ const bundled = (await rolldown.build({
3952
+ input: entry.input,
3953
+ write: false,
3954
+ output: {
3955
+ format: "iife",
3956
+ sourcemap: false,
3957
+ minify: true,
3958
+ codeSplitting: false
3959
+ },
3960
+ tsconfig,
3961
+ plugins,
3962
+ transform: { target: "es2017" },
3963
+ treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
3964
+ logLevel: "silent"
3965
+ })).output[0].code;
5133
3966
  const byteLength = Buffer.byteLength(bundled, "utf8");
5134
3967
  if (byteLength > ADAPTER_BUNDLE_ERROR_BYTES) throw new Error(`HTTP adapter "${adapter.name}" ${kind} script is ${byteLength} bytes, exceeding the ${ADAPTER_BUNDLE_ERROR_BYTES} byte limit`);
5135
3968
  if (byteLength > ADAPTER_BUNDLE_WARN_BYTES) logger.warn(`HTTP adapter "${adapter.name}" ${kind} script is ${byteLength} bytes, larger than the recommended ${ADAPTER_BUNDLE_WARN_BYTES} byte limit`);
@@ -5328,7 +4161,7 @@ async function loadResolver(resolverFilePath) {
5328
4161
  *
5329
4162
  * This function:
5330
4163
  * 1. Uses a transform plugin to add validation wrapper during bundling
5331
- * 2. Creates entry file
4164
+ * 2. Creates an in-memory entry module
5332
4165
  * 3. Bundles in a single step with tree-shaking
5333
4166
  * @param namespace - Resolver namespace name
5334
4167
  * @param config - Resolver file loading configuration
@@ -5359,21 +4192,18 @@ async function bundleResolvers(namespace, config, triggerContext, cache, inlineS
5359
4192
  sourceFile: file
5360
4193
  });
5361
4194
  }
5362
- const outputDir = path.resolve(getDistDir(), "resolvers");
5363
- fs$1.mkdirSync(outputDir, { recursive: true });
5364
- await removeStaleEntryFiles(outputDir);
5365
4195
  let tsconfig;
5366
4196
  try {
5367
4197
  tsconfig = await resolveTSConfig();
5368
4198
  } catch {
5369
4199
  tsconfig = void 0;
5370
4200
  }
5371
- const results = await withBundleConcurrency(resolvers, (resolver) => bundleSingleResolver(namespace, resolver, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
4201
+ const results = await withBundleConcurrency(resolvers, (resolver) => bundleSingleResolver(namespace, resolver, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
5372
4202
  for (const [name, code] of results) bundledCode.set(name, code);
5373
4203
  logger.log(`${styles.success("Bundled")} ${styles.info(`"${namespace}"`)}`);
5374
4204
  return bundledCode;
5375
4205
  }
5376
- async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
4206
+ async function bundleSingleResolver(namespace, resolver, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
5377
4207
  const serializedTriggerContext = serializeTriggerContext(triggerContext);
5378
4208
  const contextHash = computeBundlerContextHash({
5379
4209
  sourceFile: resolver.sourceFile,
@@ -5390,7 +4220,6 @@ async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, tr
5390
4220
  sourceFile: resolver.sourceFile,
5391
4221
  contextHash,
5392
4222
  async build(cachePlugins) {
5393
- const entryPath = path.join(outputDir, `${resolver.name}.entry.js`);
5394
4223
  const entryContent = multiline`
5395
4224
  import _internalResolver from "${path.resolve(resolver.sourceFile)}";
5396
4225
  import { t } from "@tailor-platform/sdk";
@@ -5417,12 +4246,13 @@ async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, tr
5417
4246
 
5418
4247
  export { $tailor_resolver_body as main };
5419
4248
  `;
5420
- fs$1.writeFileSync(entryPath, entryContent);
4249
+ const entry = createVirtualEntry(`resolver:${resolver.name}`, entryContent);
5421
4250
  const triggerPlugin = createTriggerTransformPlugin(triggerContext);
5422
- const plugins = triggerPlugin ? [triggerPlugin] : [];
4251
+ const plugins = [entry.plugin];
4252
+ if (triggerPlugin) plugins.push(triggerPlugin);
5423
4253
  plugins.push(platformBundleDefinePlugin, ...cachePlugins);
5424
4254
  return (await rolldown.build({
5425
- input: entryPath,
4255
+ input: entry.input,
5426
4256
  write: false,
5427
4257
  output: {
5428
4258
  format: "esm",
@@ -5511,36 +4341,15 @@ function assertUniqueResolverNames(resolvers, namespace) {
5511
4341
  */
5512
4342
  function findVariableDeclarationsByName(program) {
5513
4343
  const declarations = /* @__PURE__ */ new Map();
5514
- function walk(node) {
5515
- if (!node || typeof node !== "object") return;
5516
- const nodeType = node.type;
5517
- if (nodeType === "VariableDeclaration") {
5518
- const varDecl = node;
5519
- for (const decl of varDecl.declarations) if (decl.id.type === "Identifier" && decl.id.name) {
5520
- if (!declarations.has(decl.id.name)) declarations.set(decl.id.name, {
5521
- start: varDecl.start,
5522
- end: varDecl.end
5523
- });
5524
- }
5525
- }
5526
- if (nodeType === "ExportNamedDeclaration") {
5527
- const exportDecl = node;
5528
- const declaration = exportDecl.declaration;
5529
- if (declaration?.type === "VariableDeclaration") {
5530
- const varDecl = declaration;
5531
- for (const decl of varDecl.declarations) if (decl.id.type === "Identifier" && decl.id.name) declarations.set(decl.id.name, {
5532
- start: exportDecl.start,
5533
- end: exportDecl.end
5534
- });
5535
- }
5536
- }
5537
- for (const key of Object.keys(node)) {
5538
- const child = node[key];
5539
- if (Array.isArray(child)) child.forEach((c) => walk(c));
5540
- else if (child && typeof child === "object") walk(child);
5541
- }
4344
+ for (const statement of program.body) {
4345
+ const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
4346
+ if (declaration?.type !== "VariableDeclaration") continue;
4347
+ const variableDeclaration = declaration;
4348
+ for (const declarator of variableDeclaration.declarations) if (declarator.id.type === "Identifier") declarations.set(declarator.id.name, {
4349
+ start: statement.start,
4350
+ end: statement.end
4351
+ });
5542
4352
  }
5543
- walk(program);
5544
4353
  return declarations;
5545
4354
  }
5546
4355
  /**
@@ -5567,48 +4376,32 @@ function findWorkflowDefaultExport(program) {
5567
4376
  }
5568
4377
  /**
5569
4378
  * Transform workflow source code
5570
- * - Transform .trigger() calls to tailor.workflow.triggerJobFunction()
5571
4379
  * - Other jobs: remove entire variable declaration
5572
4380
  * @param source - The source code to transform
5573
4381
  * @param targetJobName - The name of the target job (from job config)
5574
4382
  * @param targetJobExportName - The export name of the target job (optional, for enhanced detection)
5575
4383
  * @param otherJobExportNames - Export names of other jobs to remove (optional, for enhanced detection)
5576
- * @param allJobsMap - Map from export name to job name for trigger transformation (optional)
5577
4384
  * @returns Transformed workflow source code
5578
4385
  */
5579
- function transformWorkflowSource(source, targetJobName, targetJobExportName, otherJobExportNames, allJobsMap) {
4386
+ function transformWorkflowSource(source, targetJobName, targetJobExportName, otherJobExportNames) {
5580
4387
  const { program } = parseSync("input.ts", source);
5581
4388
  const detectedJobs = findAllJobs(program, source);
5582
4389
  if (!detectedJobs.some((j) => j.name === targetJobName)) return source;
5583
- const jobNameMap = allJobsMap ?? buildJobNameMap(detectedJobs);
5584
4390
  const allDeclarations = findVariableDeclarationsByName(program);
5585
- const triggerCalls = detectTriggerCalls(program, source);
5586
4391
  const replacements = [];
5587
- const removedRanges = [];
5588
- const isInsideRemovedRange = (pos) => {
5589
- return removedRanges.some((r) => pos >= r.start && pos < r.end);
5590
- };
5591
- const isAlreadyMarkedForRemoval = (start) => {
5592
- return removedRanges.some((r) => r.start === start);
5593
- };
4392
+ const removedStarts = /* @__PURE__ */ new Set();
5594
4393
  for (const job of detectedJobs) {
5595
4394
  if (job.name === targetJobName) continue;
5596
- if (job.statementRange && !isAlreadyMarkedForRemoval(job.statementRange.start)) {
4395
+ if (job.statementRange && !removedStarts.has(job.statementRange.start)) {
5597
4396
  const endPos = findStatementEnd(source, job.statementRange.end);
5598
- removedRanges.push({
5599
- start: job.statementRange.start,
5600
- end: endPos
5601
- });
4397
+ removedStarts.add(job.statementRange.start);
5602
4398
  replacements.push({
5603
4399
  start: job.statementRange.start,
5604
4400
  end: endPos,
5605
4401
  text: ""
5606
4402
  });
5607
4403
  } else if (!job.statementRange) {
5608
- removedRanges.push({
5609
- start: job.bodyValueRange.start,
5610
- end: job.bodyValueRange.end
5611
- });
4404
+ removedStarts.add(job.bodyValueRange.start);
5612
4405
  replacements.push({
5613
4406
  start: job.bodyValueRange.start,
5614
4407
  end: job.bodyValueRange.end,
@@ -5619,12 +4412,9 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
5619
4412
  if (otherJobExportNames) for (const exportName of otherJobExportNames) {
5620
4413
  if (exportName === targetJobExportName) continue;
5621
4414
  const declRange = allDeclarations.get(exportName);
5622
- if (declRange && !isAlreadyMarkedForRemoval(declRange.start)) {
4415
+ if (declRange && !removedStarts.has(declRange.start)) {
5623
4416
  const endPos = findStatementEnd(source, declRange.end);
5624
- removedRanges.push({
5625
- start: declRange.start,
5626
- end: endPos
5627
- });
4417
+ removedStarts.add(declRange.start);
5628
4418
  replacements.push({
5629
4419
  start: declRange.start,
5630
4420
  end: endPos,
@@ -5633,31 +4423,15 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
5633
4423
  }
5634
4424
  }
5635
4425
  const workflowExport = findWorkflowDefaultExport(program);
5636
- if (workflowExport && !isAlreadyMarkedForRemoval(workflowExport.start)) {
4426
+ if (workflowExport && !removedStarts.has(workflowExport.start)) {
5637
4427
  const endPos = findStatementEnd(source, workflowExport.end);
5638
- removedRanges.push({
5639
- start: workflowExport.start,
5640
- end: endPos
5641
- });
4428
+ removedStarts.add(workflowExport.start);
5642
4429
  replacements.push({
5643
4430
  start: workflowExport.start,
5644
4431
  end: endPos,
5645
4432
  text: ""
5646
4433
  });
5647
4434
  }
5648
- for (const call of triggerCalls) {
5649
- if (isInsideRemovedRange(call.callRange.start)) continue;
5650
- const jobName = jobNameMap.get(call.identifierName);
5651
- if (jobName) {
5652
- const optionsPart = call.optionsText !== void 0 ? `, ${call.optionsText}` : "";
5653
- const transformedCall = `tailor.workflow.triggerJobFunction("${jobName}", ${call.argsText || "undefined"}${optionsPart})`;
5654
- replacements.push({
5655
- start: call.callRange.start,
5656
- end: call.callRange.end,
5657
- text: transformedCall
5658
- });
5659
- }
5660
- }
5661
4435
  return applyReplacements(source, replacements);
5662
4436
  }
5663
4437
 
@@ -5666,7 +4440,7 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
5666
4440
  function safeRealpath(p) {
5667
4441
  const resolved = path.resolve(p);
5668
4442
  try {
5669
- return fs$1.realpathSync(resolved);
4443
+ return fs.realpathSync(resolved);
5670
4444
  } catch (e) {
5671
4445
  logger.debug(`realpathSync failed for ${resolved}: ${e instanceof Error ? e.message : e}`);
5672
4446
  return resolved;
@@ -5678,7 +4452,7 @@ function safeRealpath(p) {
5678
4452
  * This function:
5679
4453
  * 1. Detects which jobs are actually used (mainJobs + their dependencies)
5680
4454
  * 2. Uses a transform plugin to transform trigger calls during bundling
5681
- * 3. Creates entry file and bundles with tree-shaking
4455
+ * 3. Creates an in-memory entry module and bundles with tree-shaking
5682
4456
  *
5683
4457
  * Returns metadata about which jobs each workflow uses.
5684
4458
  * @param allJobs - All available job infos
@@ -5699,22 +4473,16 @@ async function bundleWorkflowJobs(allJobs, mainJobNames, env = {}, triggerContex
5699
4473
  bundledCode: /* @__PURE__ */ new Map()
5700
4474
  };
5701
4475
  }
5702
- const { usedJobs, mainJobDeps } = await filterUsedJobs(allJobs, mainJobNames);
4476
+ const { usedJobs, mainJobDeps } = await filterUsedJobs(allJobs, mainJobNames, triggerContext);
5703
4477
  logger.newline();
5704
4478
  logger.log(`Bundling ${styles.highlight(usedJobs.length.toString())} files for ${styles.info("\"workflow-job\"")}`);
5705
- const outputDir = path.resolve(getDistDir(), "workflow-jobs");
5706
- fs$1.mkdirSync(outputDir, { recursive: true });
5707
- const currentJobNames = new Set(usedJobs.map((j) => j.name));
5708
- const existingFiles = fs$1.readdirSync(outputDir);
5709
- for (const file of existingFiles) if (file.endsWith(".js") && !currentJobNames.has(path.basename(file, ".js"))) fs$1.rmSync(path.join(outputDir, file), { force: true });
5710
- else if (file.endsWith(".js.map") && !currentJobNames.has(path.basename(file, ".js.map"))) fs$1.rmSync(path.join(outputDir, file), { force: true });
5711
4479
  let tsconfig;
5712
4480
  try {
5713
4481
  tsconfig = await resolveTSConfig();
5714
4482
  } catch {
5715
4483
  tsconfig = void 0;
5716
4484
  }
5717
- const results = await withBundleConcurrency(usedJobs, (job) => bundleSingleJob(job, usedJobs, outputDir, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel));
4485
+ const results = await withBundleConcurrency(usedJobs, (job) => bundleSingleJob(job, usedJobs, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel));
5718
4486
  const bundledCode = /* @__PURE__ */ new Map();
5719
4487
  for (const [name, code] of results) bundledCode.set(name, code);
5720
4488
  logger.log(`${styles.success("Bundled")} ${styles.info("\"workflow-job\"")}`);
@@ -5733,9 +4501,10 @@ async function bundleWorkflowJobs(allJobs, mainJobNames, env = {}, triggerContex
5733
4501
  * Also returns a map of mainJob -> all jobs it depends on (for metadata).
5734
4502
  * @param allJobs - All available job infos
5735
4503
  * @param mainJobNames - Names of main jobs
4504
+ * @param triggerContext - Module binding metadata for resolving trigger targets
5736
4505
  * @returns Used jobs and main job dependency map
5737
4506
  */
5738
- async function filterUsedJobs(allJobs, mainJobNames) {
4507
+ async function filterUsedJobs(allJobs, mainJobNames, triggerContext) {
5739
4508
  if (allJobs.length === 0 || mainJobNames.length === 0) return {
5740
4509
  usedJobs: [],
5741
4510
  mainJobDeps: {}
@@ -5746,26 +4515,19 @@ async function filterUsedJobs(allJobs, mainJobNames) {
5746
4515
  existing.push(job);
5747
4516
  jobsBySourceFile.set(job.sourceFile, existing);
5748
4517
  }
5749
- const exportNameToJobName = /* @__PURE__ */ new Map();
5750
- for (const job of allJobs) exportNameToJobName.set(job.exportName, job.name);
5751
4518
  const dependencies = /* @__PURE__ */ new Map();
5752
4519
  const fileResults = await Promise.all(Array.from(jobsBySourceFile.entries()).map(async ([sourceFile, jobs]) => {
5753
4520
  try {
5754
- const source = await fs$1.promises.readFile(sourceFile, "utf-8");
4521
+ const source = await fs.promises.readFile(sourceFile, "utf-8");
5755
4522
  const { program } = parseSync("input.ts", source);
5756
4523
  const detectedJobs = findAllJobs(program, source);
5757
- const localExportNameToJobName = /* @__PURE__ */ new Map();
5758
- for (const detected of detectedJobs) if (detected.exportName) localExportNameToJobName.set(detected.exportName, detected.name);
5759
- const triggerCalls = detectTriggerCalls(program, source);
4524
+ const triggerCalls = detectResolvedTriggerCalls(program, source, triggerContext, sourceFile);
5760
4525
  const jobDependencies = [];
5761
4526
  for (const job of jobs) {
5762
4527
  const detectedJob = detectedJobs.find((d) => d.name === job.name);
5763
4528
  if (!detectedJob) continue;
5764
4529
  const jobDeps = /* @__PURE__ */ new Set();
5765
- for (const call of triggerCalls) if (call.callRange.start >= detectedJob.bodyValueRange.start && call.callRange.end <= detectedJob.bodyValueRange.end) {
5766
- const triggeredJobName = localExportNameToJobName.get(call.identifierName) || exportNameToJobName.get(call.identifierName);
5767
- if (triggeredJobName) jobDeps.add(triggeredJobName);
5768
- }
4530
+ for (const call of triggerCalls) if (call.kind === "job" && call.callRange.start >= detectedJob.bodyValueRange.start && call.callRange.end <= detectedJob.bodyValueRange.end) jobDeps.add(call.targetName);
5769
4531
  if (jobDeps.size > 0) jobDependencies.push({
5770
4532
  jobName: job.name,
5771
4533
  deps: jobDeps
@@ -5796,7 +4558,7 @@ async function filterUsedJobs(allJobs, mainJobNames) {
5796
4558
  mainJobDeps
5797
4559
  };
5798
4560
  }
5799
- async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
4561
+ async function bundleSingleJob(job, allJobs, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
5800
4562
  const serializedTriggerContext = serializeTriggerContext(triggerContext);
5801
4563
  const sortedEnvPrefix = JSON.stringify(Object.fromEntries(Object.entries(env).toSorted(([a], [b]) => a.localeCompare(b))));
5802
4564
  const contextHash = computeBundlerContextHash({
@@ -5814,7 +4576,6 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
5814
4576
  sourceFile: job.sourceFile,
5815
4577
  contextHash,
5816
4578
  async build(cachePlugins) {
5817
- const entryPath = path.join(outputDir, `${job.name}.entry.js`);
5818
4579
  const absoluteSourcePath = path.resolve(job.sourceFile);
5819
4580
  const entryContent = multiline`
5820
4581
  import { ${job.exportName} } from "${absoluteSourcePath}";
@@ -5825,12 +4586,11 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
5825
4586
  return await ${job.exportName}.body(input, { env, invoker });
5826
4587
  }
5827
4588
  `;
5828
- fs$1.writeFileSync(entryPath, entryContent);
5829
- const otherJobExportNames = allJobs.filter((j) => j.name !== job.name).map((j) => j.exportName);
5830
- const allJobsMap = /* @__PURE__ */ new Map();
5831
- for (const j of allJobs) allJobsMap.set(j.exportName, j.name);
4589
+ const entry = createVirtualEntry(`workflow-job:${job.name}`, entryContent);
5832
4590
  const resolvedSourceFile = safeRealpath(job.sourceFile);
4591
+ const otherJobExportNames = allJobs.filter((candidate) => candidate.name !== job.name && safeRealpath(candidate.sourceFile) === resolvedSourceFile).map((j) => j.exportName);
5833
4592
  const plugins = [
4593
+ entry.plugin,
5834
4594
  {
5835
4595
  name: "workflow-transform",
5836
4596
  transform: {
@@ -5838,8 +4598,8 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
5838
4598
  handler(code, id) {
5839
4599
  if (!code.includes("createWorkflowJob") && !code.includes("createWorkflow") && !code.includes(".trigger(")) return null;
5840
4600
  let transformed = code;
5841
- if (safeRealpath(id) === resolvedSourceFile) transformed = transformWorkflowSource(code, job.name, job.exportName, otherJobExportNames, allJobsMap);
5842
- if (triggerContext && transformed.includes(".trigger(")) transformed = transformFunctionTriggers(transformed, triggerContext.workflowNameMap, triggerContext.jobNameMap, triggerContext.workflowFileMap, id, triggerContext.authNamespace);
4601
+ if (safeRealpath(id) === resolvedSourceFile) transformed = transformWorkflowSource(code, job.name, job.exportName, otherJobExportNames);
4602
+ if (transformed.includes(".trigger(")) transformed = transformFunctionTriggers(transformed, triggerContext, id);
5843
4603
  if (transformed === code) return null;
5844
4604
  return { code: transformed };
5845
4605
  }
@@ -5849,7 +4609,7 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
5849
4609
  ...cachePlugins
5850
4610
  ];
5851
4611
  return (await rolldown.build({
5852
- input: entryPath,
4612
+ input: entry.input,
5853
4613
  write: false,
5854
4614
  output: {
5855
4615
  format: "esm",
@@ -5908,7 +4668,7 @@ const RetryPolicySchema = z.strictObject({
5908
4668
  });
5909
4669
  const ConcurrencyPolicySchema = z.strictObject({ maxConcurrentExecutions: z.number().int().min(1).max(1e3).describe("Maximum number of concurrent executions (1-1000)") });
5910
4670
  const ExecutionPolicyNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/, "Invalid execution policy name: must match [a-z0-9-] (3-63 chars; must start and end with [a-z0-9])").describe("Workspace-unique execution policy name embedded in the resource TRN");
5911
- const ExecutionPolicyKeySchema = z.string().regex(/^[a-z0-9][a-z0-9_:.-]{0,62}[a-z0-9*]$/, "Invalid execution policy key: must match [a-z0-9_:.-] (2-64 chars; must start with [a-z0-9] and end with [a-z0-9] or a trailing '*')").describe("Execution policy key passed to triggerJobFunction's executionPolicyKey option");
4671
+ const ExecutionPolicyKeySchema = z.string().regex(/^[a-z0-9][a-z0-9_:.-]{0,62}[a-z0-9*]$/, "Invalid execution policy key: must match [a-z0-9_:.-] (2-64 chars; must start with [a-z0-9] and end with [a-z0-9] or a trailing '*')").describe("Execution policy key passed to startJobFunction's (or its frozen alias triggerJobFunction's) executionPolicyKey option");
5912
4672
  const WorkflowJobFunctionExecutionPolicySchema = z.strictObject({
5913
4673
  name: ExecutionPolicyNameSchema,
5914
4674
  key: ExecutionPolicyKeySchema,
@@ -6665,5 +5425,5 @@ async function loadApplication(params) {
6665
5425
  }
6666
5426
 
6667
5427
  //#endregion
6668
- export { fetchUserInfo as $, hasAnyUserTokenEntry as A, readPlatformConfig as B, createBundleCache as C, positiveIntArg as Ct, loadConfig as D, hashFile as E, workspaceArgs as Et, loadMachineUserName as F, writePlatformConfig as G, resolveConfigUser as H, loadPlatformClientConfig as I, fetchAll as J, closeConnectionPool as K, loadStoredUserTokens as L, loadAccessToken as M, loadConfigPath as N, deleteUserTokens as O, loadConsoleBaseUrl as P, fetchPlatformMachineUserToken as Q, loadWorkspaceId as R, hasGenerationHooks as S, parseDuration as St, hashContent as T, toPageDirection as Tt, resolveUserTokenKey as U, removeLegacyUserAlias as V, saveUserTokens as W, fetchMachineUserToken as X, fetchAllTolerant as Y, fetchPaged as Z, platformBundleDefinePlugin as _, nonNegativeIntArg as _t, resolveInlineSourcemap as a, initOperatorClient as at, resolveBundleLogLevel as b, pagedLogArgs as bt, WorkflowJobSchema as c, byName as ct, INVOKER_EXPR as d, confirmationArgs as dt, getConsoleBaseUrl as et, buildExecutorArgsExpr as f, deploymentArgs as ft, stringifyFunction as g, multiConfigArg as gt, assertUniqueTailorDBTypeNamesWithExternal as h, isVerbose as ht, resolverBundleKey as i, initOAuth2Client as it, hasUserTokenEntry as j, fetchLatestToken as k, ResolverSchema as l, commonArgs as lt, assertUniqueLocalTailorDBTypeNames as m, folderArgs as mt, generatePluginFilesIfNeeded as n, getOrNull as nt, getApplicationAuthNamespace as o, isDefaultPlatform as ot, buildResolverOperationHookExpr as p, durationArg as pt, defaultPlatformBaseUrl as q, loadApplication as r, getPlatformBaseUrl as rt, WorkflowJobFunctionExecutionPolicySchema as s, resolveStaticWebsiteUrls as st, defineApplication as t, getOAuth2ClientId as tt, HTTP_METHODS as u, configArg as ut, composeFunctionTreeshakeOptions as v, orderArg as vt, getDistDir as w, resolveMachineUserInputSource as wt, getPluginGenerationDependencies as x, paginationArgs as xt, createLogLevelTreeshakeOptions as y, organizationArgs as yt, platformConfigFromProfile as z };
6669
- //# sourceMappingURL=application-BX13RArL.mjs.map
5428
+ export { fetchAllTolerant as A, initOperatorClient as B, createBundleCache as C, closeConnectionPool as D, hashFile as E, getConsoleBaseUrl as F, byName as G, normalizeBaseUrl as H, getOAuth2ClientId as I, LOG_LEVELS as K, getOrNull as L, fetchPaged as M, fetchPlatformMachineUserToken as N, defaultPlatformBaseUrl as O, fetchUserInfo as P, getPlatformBaseUrl as R, hasGenerationHooks as S, hashContent as T, rememberPlatformConfigForToken as U, isDefaultPlatform as V, resolveStaticWebsiteUrls as W, platformBundleDefinePlugin as _, resolveInlineSourcemap as a, resolveBundleLogLevel as b, WorkflowJobSchema as c, INVOKER_EXPR as d, buildExecutorArgsExpr as f, stringifyFunction as g, assertUniqueTailorDBTypeNamesWithExternal as h, resolverBundleKey as i, fetchMachineUserToken as j, fetchAll as k, ResolverSchema as l, assertUniqueLocalTailorDBTypeNames as m, generatePluginFilesIfNeeded as n, getApplicationAuthNamespace as o, buildResolverOperationHookExpr as p, loadApplication as r, WorkflowJobFunctionExecutionPolicySchema as s, defineApplication as t, HTTP_METHODS as u, composeFunctionTreeshakeOptions as v, getDistDir as w, getPluginGenerationDependencies as x, createLogLevelTreeshakeOptions as y, initOAuth2Client as z };
5429
+ //# sourceMappingURL=application-BJXRpQj5.mjs.map