@tailor-platform/sdk 2.26.0 → 2.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/agent-skills/tailor/SKILL.md +4 -2
  3. package/dist/application-CgJLyJaF.mjs +200 -0
  4. package/dist/application-CgJLyJaF.mjs.map +1 -0
  5. package/dist/application-CvmKh5WT.mjs +1 -0
  6. package/dist/cli/commands/deploy/deploy.d.mts +2 -0
  7. package/dist/cli/commands/deploy/tailordb/migration-validation.d.mts +2 -1
  8. package/dist/cli/commands/executor/get.d.mts +2 -1
  9. package/dist/cli/commands/executor/jobs.d.mts +2 -1
  10. package/dist/cli/commands/executor/trigger.d.mts +2 -1
  11. package/dist/cli/commands/function/get.d.mts +2 -1
  12. package/dist/cli/commands/function/list.d.mts +2 -1
  13. package/dist/cli/commands/organization/folder/create.d.mts +2 -1
  14. package/dist/cli/commands/organization/folder/delete.d.mts +2 -1
  15. package/dist/cli/commands/organization/folder/get.d.mts +2 -1
  16. package/dist/cli/commands/organization/folder/list.d.mts +2 -1
  17. package/dist/cli/commands/organization/folder/update.d.mts +2 -1
  18. package/dist/cli/commands/organization/get.d.mts +2 -1
  19. package/dist/cli/commands/organization/tree.d.mts +2 -1
  20. package/dist/cli/commands/organization/update.d.mts +2 -1
  21. package/dist/cli/commands/show.d.mts +2 -1
  22. package/dist/cli/commands/tailordb/migrate/bundler.d.mts +4 -6
  23. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +8 -1
  24. package/dist/cli/commands/tailordb/migrate/remote-state.d.mts +1 -0
  25. package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +3 -1
  26. package/dist/cli/commands/workflow/executions.d.mts +2 -1
  27. package/dist/cli/commands/workflow/get.d.mts +2 -1
  28. package/dist/cli/commands/workflow/start.d.mts +2 -1
  29. package/dist/cli/commands/workspace/app/health.d.mts +2 -1
  30. package/dist/cli/commands/workspace/app/list.d.mts +2 -1
  31. package/dist/cli/commands/workspace/create.d.mts +5 -2
  32. package/dist/cli/commands/workspace/delete.d.mts +2 -1
  33. package/dist/cli/commands/workspace/get.d.mts +2 -1
  34. package/dist/cli/commands/workspace/restore.d.mts +2 -1
  35. package/dist/cli/commands/workspace/user/invite.d.mts +2 -1
  36. package/dist/cli/commands/workspace/user/list.d.mts +2 -1
  37. package/dist/cli/commands/workspace/user/remove.d.mts +2 -1
  38. package/dist/cli/commands/workspace/user/update.d.mts +2 -1
  39. package/dist/cli/lib.d.mts +3 -3
  40. package/dist/cli/lib.mjs +1 -1
  41. package/dist/cli/lib.mjs.map +1 -1
  42. package/dist/cli/main.d.mts +2 -0
  43. package/dist/cli/main.mjs +68 -55
  44. package/dist/cli/main.mjs.map +1 -1
  45. package/dist/cli/query/index.d.mts +2 -1
  46. package/dist/cli/shared/args.d.mts +4 -2
  47. package/dist/cli/shared/client.d.mts +6 -1
  48. package/dist/cli/shared/context.d.mts +2 -1
  49. package/dist/cli/shared/logger.d.mts +5 -2
  50. package/dist/cli/shared/readonly-guard.d.mts +2 -1
  51. package/dist/cli/shared/seed-context.d.mts +5 -0
  52. package/dist/cli/shared/type-generator.d.mts +2 -1
  53. package/dist/cli/shared/workspace-name.d.mts +2 -0
  54. package/dist/completion/zsh-worker.zsh +220 -19
  55. package/dist/configure/config/types.d.mts +34 -1
  56. package/dist/configure/index.d.mts +6 -6
  57. package/dist/configure/index.mjs +1 -1
  58. package/dist/configure/index.mjs.map +1 -1
  59. package/dist/configure/services/aigateway/types.d.mts +2 -1
  60. package/dist/configure/services/auth/types.d.mts +8 -2
  61. package/dist/configure/services/executor/executor.d.mts +2 -1
  62. package/dist/configure/services/executor/operation.d.mts +2 -1
  63. package/dist/configure/services/executor/trigger/event.d.mts +3 -2
  64. package/dist/configure/services/executor/trigger/schedule.d.mts +2 -1
  65. package/dist/configure/services/executor/trigger/webhook.d.mts +1 -1
  66. package/dist/configure/services/http-adapter/http-adapter.d.mts +2 -1
  67. package/dist/configure/services/idp/permission.d.mts +4 -3
  68. package/dist/configure/services/idp/types.d.mts +2 -1
  69. package/dist/configure/services/resolver/permission.d.mts +2 -1
  70. package/dist/configure/services/resolver/resolver.d.mts +3 -2
  71. package/dist/configure/services/secrets/types.d.mts +2 -1
  72. package/dist/configure/services/staticwebsite/types.d.mts +2 -1
  73. package/dist/configure/services/tailordb/permission.d.mts +2 -1
  74. package/dist/configure/services/tailordb/schema.d.mts +5 -4
  75. package/dist/configure/services/tailordb/types.d.mts +13 -2
  76. package/dist/configure/services/workflow/execution-policy.types.d.mts +2 -1
  77. package/dist/configure/services/workflow/job.d.mts +3 -2
  78. package/dist/configure/services/workflow/wait-point.d.mts +2 -1
  79. package/dist/configure/types/field.types.d.mts +50 -12
  80. package/dist/configure/types/permission-operand.types.d.mts +2 -1
  81. package/dist/configure/types/type.d.mts +3 -2
  82. package/dist/{crashreport-DFpRn-LS.mjs → crashreport-BAG8c7hB.mjs} +4 -4
  83. package/dist/{crashreport-DFpRn-LS.mjs.map → crashreport-BAG8c7hB.mjs.map} +1 -1
  84. package/dist/crashreport-BcXuocwI.mjs +1 -0
  85. package/dist/date-CgP7gMG_.mjs +2 -0
  86. package/dist/date-CgP7gMG_.mjs.map +1 -0
  87. package/dist/{secretmanager-5olfnI1b.mjs → date-representation-Cr7W7Pn2.mjs} +2 -2
  88. package/dist/date-representation-Cr7W7Pn2.mjs.map +1 -0
  89. package/dist/{errors-CpvSChyL.mjs → errors-D3qpNHR6.mjs} +2 -2
  90. package/dist/{errors-CpvSChyL.mjs.map → errors-D3qpNHR6.mjs.map} +1 -1
  91. package/dist/{field-parse-DhnFs9WG.mjs → field-parse-C41e3UAF.mjs} +2 -2
  92. package/dist/{field-parse-DhnFs9WG.mjs.map → field-parse-C41e3UAF.mjs.map} +1 -1
  93. package/dist/idp-G_ojPBB5.mjs.map +1 -1
  94. package/dist/kysely-type-BWMfV301.mjs +48 -0
  95. package/dist/kysely-type-BWMfV301.mjs.map +1 -0
  96. package/dist/logger-DuaT2zY3.mjs +9 -0
  97. package/dist/logger-DuaT2zY3.mjs.map +1 -0
  98. package/dist/{manager-8-JOvuLl.mjs → manager-Bk-7vkwd.mjs} +2 -2
  99. package/dist/{manager-8-JOvuLl.mjs.map → manager-Bk-7vkwd.mjs.map} +1 -1
  100. package/dist/migration-steps-C6F3zNdQ.mjs +3 -0
  101. package/dist/migration-steps-C6F3zNdQ.mjs.map +1 -0
  102. package/dist/package-json-D1BRkjtr.mjs +1 -0
  103. package/dist/parser/service/tailordb/types.d.mts +3 -1
  104. package/dist/plugin/builtin/enum-constants/index.d.mts +2 -1
  105. package/dist/plugin/builtin/file-utils/index.d.mts +2 -1
  106. package/dist/plugin/builtin/kysely-type/index.d.mts +2 -1
  107. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  108. package/dist/plugin/builtin/seed/index.d.mts +11 -1
  109. package/dist/plugin/builtin/seed/index.mjs +1 -1
  110. package/dist/plugin/index.mjs +1 -1
  111. package/dist/register-ts-hook-yxshv0LV.mjs +1013 -0
  112. package/dist/register-ts-hook-yxshv0LV.mjs.map +1 -0
  113. package/dist/runtime/aigateway.mjs +1 -1
  114. package/dist/runtime/authconnection.mjs +1 -1
  115. package/dist/runtime/context.d.mts +2 -1
  116. package/dist/runtime/context.mjs +1 -1
  117. package/dist/runtime/date.d.mts +2 -1
  118. package/dist/runtime/field-parse.d.mts +2 -1
  119. package/dist/runtime/file.d.mts +2 -1
  120. package/dist/runtime/file.mjs +1 -1
  121. package/dist/runtime/globals.d.mts +2 -1
  122. package/dist/runtime/iconv.mjs +1 -1
  123. package/dist/runtime/idp.d.mts +6 -0
  124. package/dist/runtime/idp.mjs +1 -1
  125. package/dist/runtime/index.mjs +1 -1
  126. package/dist/runtime/logger.mjs +1 -1
  127. package/dist/runtime/secretmanager.mjs +1 -1
  128. package/dist/runtime/types.d.mts +7 -1
  129. package/dist/runtime/workflow.d.mts +2 -1
  130. package/dist/runtime/workflow.mjs +1 -1
  131. package/dist/{schema-CRtAmDcl.mjs → schema-B6hJQWp6.mjs} +2 -2
  132. package/dist/schema-B6hJQWp6.mjs.map +1 -0
  133. package/dist/seed/index.d.mts +19 -4
  134. package/dist/seed/index.mjs +7 -9
  135. package/dist/seed/index.mjs.map +1 -1
  136. package/dist/seed-C3P_T_Eh.mjs.map +1 -1
  137. package/dist/service-CETr0AKY.mjs +7 -0
  138. package/dist/service-CETr0AKY.mjs.map +1 -0
  139. package/dist/service-DREWLYxD.mjs +1 -0
  140. package/dist/{service-DFmt9UJP.mjs → service-b1g22NN9.mjs} +2 -2
  141. package/dist/{service-DFmt9UJP.mjs.map → service-b1g22NN9.mjs.map} +1 -1
  142. package/dist/service_pb-Bbu2QSIr.mjs +2 -0
  143. package/dist/service_pb-Bbu2QSIr.mjs.map +1 -0
  144. package/dist/service_pb-urxAIYHT.mjs +1 -0
  145. package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +53 -1
  146. package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +33 -0
  147. package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +0 -8
  148. package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +18 -1
  149. package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +4 -0
  150. package/dist/telemetry-DpkFGBmu.mjs +1 -0
  151. package/dist/{telemetry-Bklv9kQY.mjs → telemetry-Xk7J829P.mjs} +2 -2
  152. package/dist/{telemetry-Bklv9kQY.mjs.map → telemetry-Xk7J829P.mjs.map} +1 -1
  153. package/dist/types/field.generated.d.mts +2 -2
  154. package/dist/types/tailordb.generated.d.mts +2 -0
  155. package/dist/utils/test/index.mjs +1 -1
  156. package/dist/vitest/date-representation.d.mts +23 -0
  157. package/dist/vitest/index.d.mts +3 -1
  158. package/dist/vitest/index.mjs +1 -1
  159. package/dist/vitest/index.mjs.map +1 -1
  160. package/dist/vitest/migration-steps.d.mts +45 -0
  161. package/dist/vitest/mock-kysely.d.mts +2 -1
  162. package/dist/vitest/mocks/aigateway.d.mts +2 -1
  163. package/dist/vitest/mocks/authconnection.d.mts +2 -1
  164. package/dist/vitest/mocks/file.d.mts +2 -1
  165. package/dist/vitest/mocks/iconv.d.mts +2 -1
  166. package/dist/vitest/mocks/idp.d.mts +3 -1
  167. package/dist/vitest/mocks/logger.d.mts +2 -1
  168. package/dist/vitest/mocks/secretmanager.d.mts +2 -1
  169. package/dist/vitest/mocks/tailordb-pglite.d.mts +2 -1
  170. package/dist/vitest/mocks/tailordb.d.mts +2 -1
  171. package/dist/vitest/mocks/workflow.d.mts +3 -1
  172. package/dist/vitest/pglite-kysely.d.mts +2 -1
  173. package/dist/vitest/setup.d.mts +3 -1
  174. package/dist/vitest/setup.mjs +1 -1
  175. package/dist/vitest/setup.mjs.map +1 -1
  176. package/dist/vitest/workflow-local.d.mts +2 -1
  177. package/dist/{workspace_resource_pb-CxR_6fyN.mjs → workspace_resource_pb-CaoM2npP.mjs} +2 -2
  178. package/dist/workspace_resource_pb-CaoM2npP.mjs.map +1 -0
  179. package/docs/cli/application.md +37 -16
  180. package/docs/cli/data.md +105 -0
  181. package/docs/cli/function.md +2 -0
  182. package/docs/cli/secret.md +6 -6
  183. package/docs/cli/tailordb.md +18 -16
  184. package/docs/cli/user.md +37 -7
  185. package/docs/cli/workspace.md +10 -2
  186. package/docs/cli-reference.md +25 -3
  187. package/docs/configuration.md +28 -6
  188. package/docs/github-actions.md +212 -63
  189. package/docs/multi-environment.md +24 -0
  190. package/docs/plugin/custom.md +2 -1
  191. package/docs/services/auth.md +20 -0
  192. package/docs/services/executor.md +2 -2
  193. package/docs/services/idp.md +6 -1
  194. package/docs/services/resolver.md +3 -1
  195. package/docs/services/staticwebsite.md +5 -5
  196. package/docs/services/tailordb-migration.md +222 -41
  197. package/docs/services/tailordb.md +17 -4
  198. package/docs/testing.md +4 -0
  199. package/package.json +18 -18
  200. package/dist/application-ChqHuhZW.mjs +0 -1
  201. package/dist/application-DS0XKBtK.mjs +0 -200
  202. package/dist/application-DS0XKBtK.mjs.map +0 -1
  203. package/dist/crashreport-CBaRJl5h.mjs +0 -1
  204. package/dist/date-Dri6Yas-.mjs +0 -2
  205. package/dist/date-Dri6Yas-.mjs.map +0 -1
  206. package/dist/kysely-type-RDX59E1j.mjs +0 -48
  207. package/dist/kysely-type-RDX59E1j.mjs.map +0 -1
  208. package/dist/logger-Db-YRTwK.mjs +0 -9
  209. package/dist/logger-Db-YRTwK.mjs.map +0 -1
  210. package/dist/package-json-CmqhDJ__.mjs +0 -1
  211. package/dist/register-ts-hook-BuwkLuxC.mjs +0 -925
  212. package/dist/register-ts-hook-BuwkLuxC.mjs.map +0 -1
  213. package/dist/schema-CRtAmDcl.mjs.map +0 -1
  214. package/dist/secretmanager-5olfnI1b.mjs.map +0 -1
  215. package/dist/service-Bx-7Lz06.mjs +0 -1
  216. package/dist/service-CL1Bp4It.mjs +0 -7
  217. package/dist/service-CL1Bp4It.mjs.map +0 -1
  218. package/dist/service_pb-ZClWtj1X.mjs +0 -2
  219. package/dist/service_pb-ZClWtj1X.mjs.map +0 -1
  220. package/dist/service_pb-dB3RrGeh.mjs +0 -1
  221. package/dist/telemetry-DXHP3VVi.mjs +0 -1
  222. package/dist/workspace_resource_pb-CxR_6fyN.mjs.map +0 -1
@@ -153,6 +153,10 @@ or end with a hyphen. It is used for the generated file name, the workflow
153
153
  select which workspace gets deployed (see
154
154
  [Targeting a workspace](#targeting-a-workspace)).
155
155
 
156
+ For a preview target, the name can be at most 50 characters: each pull request
157
+ gets its own workspace named `<name>-pr-<number>`, which must fit in 63
158
+ characters.
159
+
156
160
  ## Targeting a workspace
157
161
 
158
162
  The generated `plan` and `deploy` jobs target a workspace by **id**, read from
@@ -190,6 +194,26 @@ the `plan` job as well as `deploy`, because `plan` must enter the environment to
190
194
  read the variable. (A token-based read that lets `plan` run without entering the
191
195
  environment is planned.)
192
196
 
197
+ ## Package manager
198
+
199
+ The generated workflow sets up your package manager, installs dependencies with
200
+ it, and runs `tailor` through it. `setup ci` detects the package manager from the
201
+ lockfile at the repository root (`pnpm-lock.yaml`, `yarn.lock`, `bun.lock`,
202
+ `bun.lockb`, `package-lock.json`, or `npm-shrinkwrap.json`), or, when there is
203
+ none, from the `packageManager` or `devEngines.packageManager` field of the root
204
+ `package.json`. When neither names one, `setup ci` stops instead of guessing.
205
+
206
+ Pass `--package-manager` (`pnpm`, `npm`, `yarn`, or `bun`) to choose it
207
+ yourself, for example to generate the workflow before the lockfile exists:
208
+
209
+ ```bash
210
+ tailor setup ci branch --name my-app-stg --package-manager pnpm
211
+ ```
212
+
213
+ The workflow installs dependencies at the repository root, so commit the
214
+ lockfile there before it runs. `tailor setup update` keeps a package manager
215
+ chosen with `--package-manager` and detects it again otherwise.
216
+
193
217
  ## Generated files
194
218
 
195
219
  Running a workflow setup subcommand creates or updates:
@@ -213,9 +237,8 @@ for what you can edit.
213
237
  ### Customizing the generated workflow
214
238
 
215
239
  The SDK owns the jobs and steps whose `id` starts with `tailor-`, and the
216
- top-level keys it writes (`name:`, `on:`, and `permissions:` in a workflow; the
217
- metadata, inputs, and outputs of a composite action). Do not edit or rename
218
- them. Everything else is yours, and re-running `setup` keeps it:
240
+ top-level `name:`, `on:`, and `permissions:`. Do not edit or rename them.
241
+ Everything else is yours, and re-running `setup` keeps it:
219
242
 
220
243
  - **Your own jobs and steps.** Add them anywhere, with an `id` (if any) that
221
244
  does not start with `tailor-`: the prefix is reserved for the SDK, even in a
@@ -225,19 +248,27 @@ them. Everything else is yours, and re-running `setup` keeps it:
225
248
  (such as `playwright install`) _after_ it. A job of your own can depend on a
226
249
  managed job with `needs: tailor-deploy`.
227
250
  - **Your own top-level keys**, such as a workflow-level `env:` or `defaults:`.
251
+ - **The top-level `concurrency:` of a preview workflow.** The SDK writes a
252
+ default, and re-running `setup` keeps your edits to it. If you delete it,
253
+ re-running `setup` adds the default back.
228
254
  - **Runtime settings of managed jobs:** `runs-on`, `timeout-minutes`,
229
- `container`, and `env`. On a managed job generated without an
230
- `environment:` (such as `tailor-tag-guard` or `tailor-erd-preview`), you can
231
- also add one, for example so a step you added there can read that GitHub
232
- Environment's secrets. The `environment:` of `tailor-plan`, `tailor-deploy`,
233
- and the preview jobs comes from `--environment`, so change it with that
234
- option instead.
255
+ `container`, and `env`. If every managed job that re-running `setup` keeps
256
+ uses the same `runs-on` you set, such as self-hosted runners, a managed job
257
+ it adds (for example, the ERD preview jobs when you add `--erd-preview`)
258
+ uses it too; otherwise, set the new job's `runs-on` yourself. On a managed
259
+ job generated without an `environment:` (such as `tailor-tag-guard` or
260
+ `tailor-erd-preview`), you can also add one, for example so a step you added
261
+ there can read that GitHub Environment's secrets. The `environment:` of
262
+ `tailor-plan`, `tailor-deploy`, and the preview jobs comes from
263
+ `--environment`, so change it with that option instead.
235
264
  - **These inputs of managed steps:** `ignore` on `tailor-generate-check` and
236
265
  `tailor-drift-check`, `fail-on-drift` on `tailor-drift-check`,
237
266
  `install-command` on `tailor-install`, `node-version-file` on
238
267
  `tailor-setup`, `label` on `tailor-plan`, and `user-mapping` on
239
- `tailor-notify` and on a coordinator's steps that call an app action.
240
- - **The `run:` command of the `tailor-build-site` step** in a composite action.
268
+ `tailor-notify`.
269
+ - **Your own jobs in the `needs` of `tailor-result` and
270
+ `tailor-preview-result`**, so they count toward the workflow's result (see
271
+ [Requiring the workflow in branch protection](#requiring-the-workflow-in-branch-protection)).
241
272
 
242
273
  In a preview workflow, the `tailor-preview-deploy` job exposes the per-PR
243
274
  workspace as the outputs `workspace-id`, `workspace-name`, and `app-url`, so a
@@ -250,7 +281,7 @@ deploy-assets:
250
281
  environment: my-app
251
282
  env:
252
283
  TAILOR_PLATFORM_WORKSPACE_ID: ${{ needs.tailor-preview-deploy.outputs.workspace-id }}
253
- TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}
284
+ TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID: ${{ vars.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID || secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}
254
285
  TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}
255
286
  steps:
256
287
  # checkout, dependency installation, and your tailor commands
@@ -265,6 +296,14 @@ The job is skipped whenever `tailor-preview-deploy` is skipped: when a pull
265
296
  request is closed, for draft and fork pull requests, and for unlabeled ones with
266
297
  `--require-preview-label`.
267
298
 
299
+ Each new preview workflow run for a pull request, such as after a push or when
300
+ the pull request is closed, cancels the run still in progress for it, including
301
+ your jobs, so they do not keep running against a workspace that the new run
302
+ may redeploy or delete. Events on a pull request that stays closed, such as a
303
+ label added after merging, do not cancel anything. A job with `if: always()`
304
+ still runs when its run is cancelled, and the new run waits for it to finish;
305
+ use `if: ${{ !cancelled() }}` instead so it stops with the run.
306
+
268
307
  Likewise, the `tailor-deploy` job of a branch or tag workflow exposes the
269
308
  deployed workspace as the outputs `workspace-id` and `app-url` to a job with
270
309
  `needs: tailor-deploy`. Reading them does not require the target's GitHub
@@ -299,6 +338,48 @@ Files generated by an older SDK version are compared as a whole, so the first
299
338
  re-run after upgrading stops if you edited the file in any way. Re-run once with
300
339
  `--force` to switch it to this model; your own jobs and steps are kept.
301
340
 
341
+ ### Requiring the workflow in branch protection
342
+
343
+ Branch and preview workflows end with a job that reports the result of the
344
+ whole workflow: `tailor-result` in a branch workflow and
345
+ `tailor-preview-result` in a preview workflow. Its check is named after the
346
+ workspace, such as `tailor-result (my-app)`, so each workflow in the repository
347
+ has its own. Require that one check in branch protection instead of listing
348
+ the jobs one by one.
349
+
350
+ The job runs after every other SDK-managed job of the workflow, even when one
351
+ fails or the run is cancelled. It fails when a job in its `needs` failed or was
352
+ cancelled, and passes when they succeeded or were skipped, for example when the
353
+ preview deploy is skipped for a fork pull request. When a newer run cancels a
354
+ job of an older one, such as the preview deploy of a commit you pushed over,
355
+ the older run's check fails; the newer run reports its own.
356
+
357
+ To make jobs of your own count, add them to its `needs`. A matrix job is listed
358
+ once by its job id, however many combinations it expands to:
359
+
360
+ ```yaml
361
+ tailor-preview-result:
362
+ name: tailor-preview-result (my-app)
363
+ needs:
364
+ - tailor-preview-deploy
365
+ - tailor-preview-cleanup
366
+ - seed
367
+ - e2e
368
+ ```
369
+
370
+ Re-running `setup` keeps the jobs you add there and updates the `tailor-`
371
+ entries to the jobs the SDK generates. A job skipped because a job it needs
372
+ failed also counts as passing, so list every job of yours whose failure should
373
+ block merging, not only the last one: above, `seed` is listed because `e2e`
374
+ needs it.
375
+
376
+ With `--require-preview-label`, the preview workflow does not start when a pull
377
+ request is opened, so its check is not reported until the pull request is
378
+ labeled, reopened, or pushed to. A draft pull request skips the preview deploy,
379
+ and marking it ready for review does not start the workflow, so the check that
380
+ passed while it was a draft stays until the next push. Tag workflows have no
381
+ such job: they run on tags, not on pull requests.
382
+
302
383
  ### `.github/tailor.lock`
303
384
 
304
385
  A machine-owned JSON file that tracks which files the SDK manages, the inputs
@@ -353,23 +434,33 @@ the move.
353
434
 
354
435
  ## Secrets
355
436
 
356
- The generated workflow requires two secrets (the optional Slack token is listed in
437
+ The generated workflow requires one secret (the optional Slack token is listed in
357
438
  [Setting secrets and variables](#setting-secrets-and-variables)):
358
439
 
359
440
  | Secret | Description |
360
441
  | -------------------------------------------- | -------------------------- |
361
- | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` | Machine user client ID |
362
442
  | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` | Machine user client secret |
363
443
 
364
- Set them on the target GitHub Environment (the `--environment` value, or the
365
- workspace name when omitted); `tailor setup ci env` prints the commands (see
444
+ The machine user client ID is a variable, not a secret: it cannot authenticate
445
+ without the client secret. The generated workflow reads
446
+ `vars.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` and falls back to
447
+ `secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID`, so a repository that already
448
+ stores the client ID as a secret keeps working unchanged. Storing the client ID
449
+ as a secret is deprecated, and the fallback will be removed in a future release,
450
+ so move it to a variable (`gh variable set TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID`)
451
+ and delete the secret.
452
+
453
+ Set the client secret as a secret and the client ID as a variable on the target
454
+ GitHub Environment (the `--environment` value, or the workspace name when
455
+ omitted); `tailor setup ci env` prints the commands (see
366
456
  [Setting secrets and variables](#setting-secrets-and-variables)).
367
457
 
368
458
  Setting them at the environment level isolates each target's credentials and
369
459
  keeps them alongside that environment's `TAILOR_PLATFORM_WORKSPACE_ID`
370
- variable. You can also set them as repository-level secrets if every target
371
- shares one machine user, but then any workflow on any branch can read them, so
372
- the environment's protection rules no longer guard your deploys.
460
+ variable. You can also set the client secret as a repository-level secret and the
461
+ client ID as a repository-level variable if every target shares one machine user,
462
+ but then any workflow on any branch can read them, so the environment's
463
+ protection rules no longer guard your deploys.
373
464
 
374
465
  ### Setting secrets and variables
375
466
 
@@ -389,24 +480,23 @@ tailor setup ci env --environment my-app-stg # only one environment (repeat for
389
480
 
390
481
  The list follows what each generated workflow actually reads:
391
482
 
392
- | Name | Kind | Targets | Required | Where the value comes from |
393
- | -------------------------------------------- | -------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
394
- | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` | secret | all | yes | Client ID of the platform machine user CI signs in as; it needs an editor or admin role on the organization or folder that holds the workspace. Contact [Tailor support](https://docs.tailor.tech/administration/support) to get one |
395
- | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` | secret | all | yes | Client secret of the same platform machine user |
396
- | `TAILOR_PLATFORM_WORKSPACE_ID` | variable | branch, tag, coordinate | yes | `id` printed by `tailor workspace create`, or listed by `tailor workspace list` |
397
- | `TAILOR_PLATFORM_ORGANIZATION_ID` | variable | preview | yes | Organization to create the per-PR workspaces in (a machine user cannot create a workspace without one): `organizationId` listed by `tailor organization list` |
398
- | `TAILOR_PLATFORM_FOLDER_ID` | variable | preview | no | Folder to create the per-PR workspaces in: `id` listed by `tailor organization folder list -o <organization id>`. When unset they go directly under the organization, which needs the machine user's role on the organization itself |
399
- | `TAILOR_PLATFORM_FAIL_ON_DRIFT` | variable | all | no | `true` to fail the drift check when it finds drift |
400
- | `TAILOR_SLACK_BOT_TOKEN` | secret | branch, tag, coordinate | no | Bot User OAuth Token (`xoxb-...`) of a Slack app with the `chat:write` scope |
401
- | `TAILOR_SLACK_CHANNEL_ID` | variable | branch, tag, coordinate | no | Channel ID (`C...`) from the channel details in Slack; invite the bot to the channel |
402
- | `TAILOR_SLACK_USER_MAPPING` | variable | branch, tag | no | JSON object mapping GitHub usernames to Slack member IDs (for example `{"alice":"U0123456"}`) so notifications mention the actor; read only after you uncomment the `user-mapping` input of the `tailor-notify` step |
483
+ | Name | Kind | Targets | Required | Where the value comes from |
484
+ | -------------------------------------------- | -------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
485
+ | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` | variable | all | yes | Client ID of the platform machine user CI signs in as (an existing secret of the same name still works as a deprecated fallback); it needs an editor or admin role on the organization or folder that holds the workspace. An organization or folder admin creates one in the Tailor Console and grants it a role; without the required permission you cannot view or create machine users |
486
+ | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` | secret | all | yes | Client secret of the same platform machine user |
487
+ | `TAILOR_PLATFORM_WORKSPACE_ID` | variable | branch, tag | yes | `id` printed by `tailor workspace create`, or listed by `tailor workspace list` |
488
+ | `TAILOR_PLATFORM_ORGANIZATION_ID` | variable | preview | yes | Organization to create the per-PR workspaces in (a machine user cannot create a workspace without one): `organizationId` listed by `tailor organization list` |
489
+ | `TAILOR_PLATFORM_FOLDER_ID` | variable | preview | no | Folder to create the per-PR workspaces in: `id` listed by `tailor organization folder list -o <organization id>`. When unset they go directly under the organization, which needs the machine user's role on the organization itself |
490
+ | `TAILOR_PLATFORM_FAIL_ON_DRIFT` | variable | all | no | `true` to fail the drift check when it finds drift |
491
+ | `TAILOR_SLACK_BOT_TOKEN` | secret | branch, tag | no | Bot User OAuth Token (`xoxb-...`) of a Slack app with the `chat:write` scope |
492
+ | `TAILOR_SLACK_CHANNEL_ID` | variable | branch, tag | no | Channel ID (`C...`) from the channel details in Slack; invite the bot to the channel |
493
+ | `TAILOR_SLACK_USER_MAPPING` | variable | branch, tag | no | JSON object mapping GitHub usernames to Slack member IDs (for example `{"alice":"U0123456"}`) so notifications mention the actor; read only after you uncomment the `user-mapping` input of the `tailor-notify` step |
403
494
 
404
495
  See [Account management](https://docs.tailor.tech/administration/account-management)
405
496
  for how organizations, folders, workspaces, and machine users relate.
406
497
 
407
- Composite actions (`setup ci action`) read nothing themselves; the coordinator
408
- that calls them does. Set `TAILOR_SLACK_BOT_TOKEN` and `TAILOR_SLACK_CHANNEL_ID`
409
- together to enable Slack deploy notifications.
498
+ Set `TAILOR_SLACK_BOT_TOKEN` and `TAILOR_SLACK_CHANNEL_ID` together to enable
499
+ Slack deploy notifications.
410
500
 
411
501
  The `gh` output creates an environment only when GitHub reports it missing and
412
502
  leaves optional entries commented out. Run the commands one at a time: each
@@ -463,8 +553,7 @@ dispatches, so you can deploy any commit regardless of branch membership.
463
553
 
464
554
  ### Restricting manual deploys
465
555
 
466
- Pass `--restrict-dispatch` to `setup ci branch`, `setup ci tag`, or
467
- `setup ci coordinate` to limit what a manual dispatch with `dry-run` off can
556
+ Pass `--restrict-dispatch` to `setup ci branch` or `setup ci tag` to limit what a manual dispatch with `dry-run` off can
468
557
  deploy:
469
558
 
470
559
  - **Branch target:** only the target branch. Dispatching another branch skips
@@ -484,6 +573,67 @@ plan job also enters the environment, allow `refs/pull/*/merge` as well so pull
484
573
  request plans keep running, and note that dry runs from other branches are then
485
574
  rejected too.
486
575
 
576
+ ## Seeding the workspace after deploy
577
+
578
+ Pass `--seed` to `setup ci branch`, `setup ci tag`, or `setup ci preview` to seed
579
+ the workspace with `tailor seed apply` right after each deploy. Add
580
+ `--seed-dataset <name>` (repeatable, implies `--seed`) to apply
581
+ [datasets](https://github.com/tailor-platform/sdk/tree/main/packages/sdk-plugin-seed#datasets)
582
+ on top of the base seed data:
583
+
584
+ ```bash
585
+ tailor setup ci branch --seed-dataset stg
586
+ tailor setup ci preview --region us-west --seed-dataset preview
587
+ ```
588
+
589
+ The deploy job gains a `tailor-seed` step after the deploy step. Branch targets
590
+ validate seed data before deploy and also in the PR or dry-run plan job; tag
591
+ targets validate it in the plan job before deploy. Both use
592
+ `tailor seed validate --upsert` with the selected datasets to check the `id`
593
+ and required fields that `seed apply --upsert` needs. Preview targets validate
594
+ and apply the data after deploy.
595
+
596
+ | Target | Seeds with | Effect |
597
+ | ----------- | ------------------------------------ | ----------------------------------------------------------------------------- |
598
+ | branch, tag | `tailor seed apply --upsert` | The fields of each committed seed row are written; other rows are kept |
599
+ | preview | `tailor seed apply --truncate --yes` | The preview workspace is emptied, IdP users included, and seeded from scratch |
600
+
601
+ Setup stops unless the config uses `seedPlugin` with `machineUserName`, which
602
+ names the machine user that seeds, and an existing `<distPath>/data/` directory.
603
+ Every `--seed-dataset` must name an entry of
604
+ the `seedPlugin` `datasets` option or a directory under `<distPath>/data/`.
605
+ Commit the dataset directories the workflow seeds: a gitignored dataset such as
606
+ a per-developer `local/` is not in the CI checkout.
607
+ Install `@tailor-platform/sdk-plugin-seed` so the `tailor seed` commands resolve
608
+ in CI. For branch and tag targets, use a version that supports
609
+ `tailor seed validate --upsert`; update the setup and seed plugins together.
610
+ Give every TailorDB seed row an `id` and
611
+ every field its table requires: `--upsert` matches rows by id, so run
612
+ `tailor seed fill` and commit the result.
613
+ Seeding is available for a target that deploys one app.
614
+
615
+ Before enabling it, note that:
616
+
617
+ - Every branch or tag deploy writes the fields of the committed seed rows over
618
+ the workspace, `_User` rows and their passwords included, so an edit to one of
619
+ those fields in the workspace is undone. Each row is updated like any other update: update hooks
620
+ and validation run, `updatedAt` changes, and executors with
621
+ `recordUpdatedTrigger` fire once per seeded row on every deploy.
622
+ - Every preview deploy deletes the data reviewers added to the preview
623
+ workspace. That is every push, and with `--require-preview-label` also every
624
+ label added to the pull request, since each one redeploys the preview.
625
+ - A seed failure fails the deploy job and its notification, although the deploy
626
+ itself went through.
627
+ - The step shares the deploy job's timeout. IdP users take about a second each.
628
+ - Steps you added after the deploy step stay right after it, so they run before
629
+ `tailor-seed`. Move a step below `tailor-seed` when it needs the seeded data,
630
+ and move it again if you regenerate without `--seed` and then with it.
631
+ - `tailor-seed` is an SDK-managed step. To seed another way (a different mode,
632
+ `--skip-idp`, a condition), leave `--seed` off and add your own step.
633
+
634
+ Re-run `setup ci` with the flags each time you regenerate the workflow; `setup
635
+ update` keeps them.
636
+
487
637
  ## Monorepo setup
488
638
 
489
639
  For a monorepo where your SDK app lives in a subdirectory, pass `--dir`:
@@ -493,12 +643,14 @@ tailor setup ci branch --name my-app --dir apps/backend
493
643
  ```
494
644
 
495
645
  The `working-directory` for SDK commands is set accordingly, and the workflow
496
- only plans and deploys when that subdirectory changes. The workflow itself
497
- starts on every pull request and push: a `tailor-changes` job checks whether
498
- the change touches `apps/backend/**`, and the plan, deploy, and ERD preview jobs
499
- are skipped when it does not. A skipped job reports success, so you can make
500
- these checks required in branch protection; a workflow that a `paths` trigger
501
- filter never started would leave them pending instead. If the `tailor-changes` job
646
+ only plans and deploys when that subdirectory or the generated workflow file
647
+ changes. The workflow itself starts on every pull request and push: a
648
+ `tailor-changes` job checks whether the change touches `apps/backend/**` or the
649
+ generated workflow file itself, and the plan, deploy, and ERD preview jobs are
650
+ skipped when it does not. A skipped job reports success, so you can require the
651
+ workflow's [result check](#requiring-the-workflow-in-branch-protection) in
652
+ branch protection; a workflow that a `paths` trigger filter never started would
653
+ leave it pending instead. If the `tailor-changes` job
502
654
  itself fails (for example, on a GitHub API error), the plan, deploy, and ERD
503
655
  preview jobs fail too instead of being skipped, so a required check blocks
504
656
  merging and nothing is deployed. Re-run the failed jobs once the cause is gone.
@@ -539,8 +691,13 @@ tailor setup ci preview --name erp --region asia-northeast \
539
691
  --paths "apps/*/frontend/**" --paths "modules/**" --paths pnpm-lock.yaml
540
692
  ```
541
693
 
542
- The patterns are checked after the app directories, in order, and the last
543
- pattern that matches a changed file decides whether it counts. `*` matches
694
+ The patterns are checked after the app directories and the generated workflow
695
+ file, in order, and the last pattern that matches a changed file decides whether
696
+ it counts. A change to the workflow file alone runs the generated plan, deploy,
697
+ and preview jobs, and with them any job of yours that needs them, in the pull
698
+ request or push that makes the change; to skip the jobs for such a change,
699
+ exclude the file, for example `--paths '!.github/workflows/tailor-erp.yml'`
700
+ (`tailor-erp-preview.yml` for the preview workflow). `*` matches
544
701
  within one path segment, `**` matches any number of segments, and a pattern
545
702
  starting with `!` excludes matching paths, so it can also exclude files inside
546
703
  an app directory, for example `--paths '!apps/erp/backend/**/*.md'`. Other glob
@@ -597,8 +754,9 @@ a branch target deploys only its target branch, so use Option 1 there.
597
754
  - **Schema and data are not rolled back.** If the older config expects a schema
598
755
  state that no longer exists, the plan may show errors. In that case, review
599
756
  the diff carefully before proceeding.
600
- - **Seed data** is not part of the deployment pipeline and is unaffected by
601
- rollbacks.
757
+ - **Seed data** is not rolled back. Without `--seed`, the pipeline does not
758
+ touch it; with `--seed`, deploying an older commit seeds the rows committed
759
+ there on top of the current data.
602
760
  - **Static websites** are not yet integrated into the generated pipeline.
603
761
  Static asset rollbacks must be performed manually.
604
762
 
@@ -675,14 +833,12 @@ finding names a stable rule key for future suppression. Run
675
833
  [`tailor setup update`](#updating-the-generated-workflow) to regenerate the
676
834
  targets it reports.
677
835
 
678
- Workflows generated by `setup ci branch`, `setup ci tag`, `setup ci preview`, and
679
- `setup ci coordinate` self-audit: each contains a `tailor-drift-check` step that
836
+ Workflows generated by `setup ci branch`, `setup ci tag`, and `setup ci preview`
837
+ self-audit: each contains a `tailor-drift-check` step that
680
838
  runs the check in CI. Preview workflows run it alongside each preview deploy,
681
839
  so pull requests that deploy no preview (drafts, fork PRs, and unlabeled PRs
682
840
  in label-triggered mode) skip the check. A single run audits every target
683
- recorded in `.github/tailor.lock`, so per-app composite actions generated by
684
- `setup ci action` are covered by their coordinator's step and do not carry one
685
- of their own.
841
+ recorded in `.github/tailor.lock`.
686
842
 
687
843
  Drift findings are advisory by default. Set the repository variable
688
844
  `TAILOR_PLATFORM_FAIL_ON_DRIFT` to `true` to make unsuppressed findings fail
@@ -702,28 +858,21 @@ pick up template improvements:
702
858
  tailor setup update
703
859
  ```
704
860
 
705
- It regenerates every workflow and composite action recorded in
861
+ It regenerates every workflow recorded in
706
862
  `.github/tailor.lock` with the flags each one was generated with, so you do not
707
863
  have to re-type `setup ci branch`, `setup ci tag`, and the rest one by one.
708
864
  Your own jobs, steps, and settings are kept (see
709
865
  [Customizing the generated workflow](#customizing-the-generated-workflow)). A
710
- branch that was detected from the repository default branch is detected again,
711
- and the parts that follow your config — the migration drift check, seed
712
- validation, static website builds, and ERD preview namespaces — are derived
713
- from the current `tailor.config.ts`.
866
+ branch that was detected from the repository default branch and a
867
+ [package manager](#package-manager) that was detected from the repository root
868
+ are detected again, and the parts that follow your config — the migration drift
869
+ check, seed validation, and ERD preview namespaces — are derived from the
870
+ current `tailor.config.ts`.
714
871
 
715
872
  A target that cannot be regenerated, for example because you edited a managed
716
873
  part, does not stop the others: `update` regenerates the rest, then lists the
717
874
  targets it could not update and exits non-zero. Revert the edit, or run
718
875
  `tailor setup update --force` to reset the managed parts of every target.
719
876
 
720
- Coordinators generated by an older plugin version did not record how their
721
- `--action` values were grouped, so `update` lists them instead of guessing
722
- which apps deploy together. Re-run `tailor setup ci coordinate` once with all
723
- of its original flags and its original `--action` groups. The message `update`
724
- prints fills in the recorded flags (`--tag`, `--branch`, `--environment`,
725
- `--restrict-dispatch`), so you only add the `--action` values. Later updates
726
- pick the grouping up from the lock.
727
-
728
877
  To change a flag for one target, re-run its `setup ci` subcommand with the new
729
878
  flags. `.github/tailor.lock` records them, and later updates reuse them.
@@ -96,3 +96,27 @@ defineStaticWebSite("my-website", {
96
96
  customDomains: process.env.APP_ENV === "production" ? ["app.example.com"] : undefined,
97
97
  });
98
98
  ```
99
+
100
+ ## Seed data per environment and per scenario
101
+
102
+ `seedPlugin` seeds every environment from the JSONL files in `<distPath>/data/`. Rows that only some environments or scenarios need go into **datasets** — directories under `<distPath>/data/` that hold JSONL files for some of the tables — and are applied on top of the base seed data with `tailor seed apply --dataset <name>`:
103
+
104
+ ```
105
+ seed/data/
106
+ *.schema.ts, *.jsonl # base seed data, applied everywhere
107
+ cogs/Item.jsonl # scenario: rows only a costing demo needs
108
+ dev/Account.jsonl # environment: the dev variant of a table
109
+ stg/Account.jsonl # environment: the staging variant of the same table
110
+ local/_User.jsonl # per developer, gitignored
111
+ ```
112
+
113
+ A table whose rows differ per environment lives only in the per-environment datasets, since a row may be defined in only one of the selected files. Name the combinations each environment uses in the `datasets` option of `seedPlugin`, so a deploy pipeline runs `tailor seed apply --dataset preview` instead of repeating the list:
114
+
115
+ ```typescript
116
+ seedPlugin({
117
+ distPath: "./seed",
118
+ datasets: { preview: ["cogs", "dev"] },
119
+ });
120
+ ```
121
+
122
+ The `tailor seed` commands come from the `@tailor-platform/sdk-plugin-seed` CLI plugin; see its [Datasets](https://github.com/tailor-platform/sdk/tree/main/packages/sdk-plugin-seed#datasets) guide for validating, filling, and dumping datasets.
@@ -272,7 +272,8 @@ same fields. OAuth client secrets are never included.
272
272
  `ctx.configPath` is the absolute path of the registering config. `ctx.workspaceId`
273
273
  and `ctx.pluginConfig` identify the deployment workspace and the plugin's options.
274
274
  Use `ctx.logger.info`, `warn`, and `success` for diagnostics; they write to stderr
275
- and preserve deploy's JSON stdout.
275
+ and preserve deploy's JSON stdout. A hook that writes to stdout itself, such as
276
+ with `console.log`, makes `tailor deploy --json` output unparseable.
276
277
 
277
278
  ```typescript
278
279
  import { definePlugins, type Plugin } from "@tailor-platform/sdk";
@@ -526,6 +526,26 @@ hooks: {
526
526
  }
527
527
  ```
528
528
 
529
+ ### Password age claim
530
+
531
+ For a user with a Built-in IdP password, `claims.password_updated_at` holds when the password was last set, in seconds since the Unix epoch. It is `undefined` for users without a password. Throw from the handler to reject a sign-in whose password is older than your policy allows.
532
+
533
+ ```typescript
534
+ const MAX_PASSWORD_AGE_SECONDS = 90 * 24 * 60 * 60;
535
+
536
+ hooks: {
537
+ beforeLogin: {
538
+ handler: async ({ claims }) => {
539
+ const updatedAt = claims.password_updated_at;
540
+ if (updatedAt !== undefined && Date.now() / 1000 - updatedAt > MAX_PASSWORD_AGE_SECONDS) {
541
+ throw new Error("Password expired");
542
+ }
543
+ },
544
+ invoker: "hook-invoker",
545
+ },
546
+ }
547
+ ```
548
+
529
549
  ## CLI Commands
530
550
 
531
551
  Manage Auth resources using the CLI:
@@ -278,7 +278,7 @@ Executor callbacks receive the trigger args, including `env` from `defineConfig(
278
278
 
279
279
  ### Job Function Operation
280
280
 
281
- For long-running operations, use `jobFunction` which runs asynchronously and supports extended execution times. See [Job Function Operation](https://docs.tailor.tech/guides/executor/job-function-operation) for details.
281
+ For work that needs more than the 60-second `function` limit, use `jobFunction`, which can run for up to 5 minutes. See [Job Function Operation](https://docs.tailor.tech/guides/executor/job-function-operation) for details.
282
282
 
283
283
  ```typescript
284
284
  import { createExecutor, scheduleTrigger } from "@tailor-platform/sdk";
@@ -292,7 +292,7 @@ export default createExecutor({
292
292
  kind: "jobFunction",
293
293
  body: async () => {
294
294
  const db = getDB("tailordb");
295
- // Long-running report generation logic
295
+ // Report generation logic (runs up to 5 minutes)
296
296
  const records = await db.selectFrom("Order").selectAll().execute();
297
297
  // Process records...
298
298
  },
@@ -105,6 +105,9 @@ defineIdp("my-idp", {
105
105
 
106
106
  **Operators:** `"="`, `"!="`, `"in"`, `"not in"`
107
107
 
108
+ > [!WARNING]
109
+ > A policy of `[{ user: "_loggedIn" }, "=", true]` permits every authenticated user to run the operation against any IdP user. For `read` and `update` this exposes and lets them change other users' login information (such as email addresses), and for `sendPasswordResetEmail` it lets them send reset emails to anyone. Use it only when every user who can sign in to the project is trusted. For an IdP used by external users (such as business partners), restrict each operation to an administrator role as shown above. `tailor deploy` warns about each rule whose only condition is `_loggedIn`.
110
+
108
111
  **Helper:** `unsafeAllowAllIdPPermission` grants full access without conditions. Intended only for development and testing.
109
112
 
110
113
  ```typescript
@@ -161,6 +164,8 @@ defineIdp("my-idp", {
161
164
  - `passwordMinLength` - Minimum password length. Must be between 6 and 30. Default `6`.
162
165
  - `passwordMaxLength` - Maximum password length. Must be between 6 and 4096. Default `4096`.
163
166
 
167
+ **Password expiration:** the IdP does not expire passwords on its own, but it records when each user's password was last set so you can enforce your own policy. The `User` records returned by `idp.Client` expose it as `passwordUpdatedAt` (an ISO 8601 string, or `null` for users without a password), for example to find expired passwords in a scheduled executor. To block sign-in with an expired password, read `claims.password_updated_at` in the Auth `beforeLogin` hook (see [Auth](./auth.md#password-age-claim)).
168
+
164
169
  **Email domains and social login:**
165
170
 
166
171
  - `allowedEmailDomains` - Restrict registration to these email domains. Each entry is a hostname (`example.com`), matched exactly and case-insensitively, so `example.com` does not cover `sub.example.com`. Up to 100 entries.
@@ -189,7 +194,7 @@ defineIdp("my-idp", {
189
194
  read: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
190
195
  update: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
191
196
  delete: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
192
- sendPasswordResetEmail: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
197
+ sendPasswordResetEmail: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
193
198
  unenrollMfa: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
194
199
  },
195
200
  });
@@ -141,7 +141,7 @@ GraphQL still accepts and returns `YYYY-MM-DD` strings. The SDK converts input t
141
141
 
142
142
  This option also works in nested objects and with `array: true` or `optional: true`. Input must be a valid calendar date, and output must be a valid `Date` with a 4-digit UTC year (0000-9999). Both deployed resolvers and `tailor function run` perform these conversions.
143
143
 
144
- A resolver bundle only includes the conversion code for the representations (`as: "date"` or `as: "temporal"`) its `input` and `output` use. To parse other values with such fields inside `body`, for example an external API response, use `parseDateFields` instead of `.parse()`. It takes the same arguments and returns the same result as `.parse()`. Calling `.parse()` on a field whose representation the bundle leaves out throws an error that points to `parseDateFields`.
144
+ A resolver bundle only includes the conversion code for the representations (`as: "date"` or `as: "temporal"`) its `input` and `output` use; with [`defaultDateRepresentation: "temporal"`](../configuration.md#application-settings), the Temporal conversion is always included. To parse other values with such fields inside `body`, for example an external API response, use `parseDateFields` instead of `.parse()`. It takes the same arguments and returns the same result as `.parse()`. Calling `.parse()` on a field whose representation the bundle leaves out throws an error that points to `parseDateFields`.
145
145
 
146
146
  ```typescript
147
147
  import { parseDateFields } from "@tailor-platform/sdk/runtime";
@@ -204,6 +204,8 @@ createResolver({
204
204
 
205
205
  All three field types support `array`, `optional`, nested objects, and input validators with the selected representation. Both deployed resolvers and `tailor function run` convert input and output. Executors using `resolverExecutedTrigger` receive date, datetime, and time results as strings.
206
206
 
207
+ To use Temporal values without writing `as: "temporal"` on every field, set [`defaultDateRepresentation: "temporal"`](../configuration.md#application-settings) in `defineConfig()`. Fields that omit `as` then use `Temporal.PlainDate`, `Temporal.Instant`, and `Temporal.PlainTime`, and `as: "string"` opts a single field back to strings. `defaultDateRepresentation: "date"` does the same for `Date` values. Run `tailor generate` after changing the setting so `tailor.d.ts` updates the field types.
208
+
207
209
  The SDK supplies Temporal types, so existing projects can use `as: "temporal"` without changing `compilerOptions.lib`. To construct values or name their types, import `Temporal` from the SDK:
208
210
 
209
211
  ```typescript
@@ -145,11 +145,11 @@ const website = defineStaticWebSite("my-frontend", {
145
145
  const idp = defineIdp("my-idp", {
146
146
  clients: ["default-client"],
147
147
  permission: {
148
- create: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
149
- read: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
150
- update: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
151
- delete: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
152
- sendPasswordResetEmail: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
148
+ create: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
149
+ read: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
150
+ update: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
151
+ delete: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
152
+ sendPasswordResetEmail: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
153
153
  },
154
154
  });
155
155