@tailor-platform/sdk 2.37.0 → 2.38.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 (93) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/{application-83WVdH85.mjs → application-Bz80INH4.mjs} +1 -1
  3. package/dist/application-CeqD1OK-.mjs +207 -0
  4. package/dist/application-CeqD1OK-.mjs.map +1 -0
  5. package/dist/cli/commands/deploy/deploy.d.mts +1 -0
  6. package/dist/cli/commands/deploy/tailordb/migration-validation.d.mts +2 -0
  7. package/dist/cli/lib.d.mts +2 -2
  8. package/dist/cli/lib.mjs +1 -1
  9. package/dist/cli/lib.mjs.map +1 -1
  10. package/dist/cli/main.mjs +66 -60
  11. package/dist/cli/main.mjs.map +1 -1
  12. package/dist/cli/shared/args.d.mts +14 -5
  13. package/dist/cli/shared/client.d.mts +14 -3
  14. package/dist/cli/shared/error-json.d.mts +12 -26
  15. package/dist/client-CHXix4LD.mjs +1 -0
  16. package/dist/client-DOQv-BP8.mjs +2 -0
  17. package/dist/client-DOQv-BP8.mjs.map +1 -0
  18. package/dist/completion/zsh-worker.zsh +133 -69
  19. package/dist/configure/services/executor/operation.d.mts +5 -4
  20. package/dist/configure/services/http-adapter/http-adapter.d.mts +1 -1
  21. package/dist/configure/services/index.d.mts +0 -1
  22. package/dist/configure/services/tailordb/types.d.mts +9 -0
  23. package/dist/context-DSi5kfqr.mjs +23 -0
  24. package/dist/context-DSi5kfqr.mjs.map +1 -0
  25. package/dist/context-OOASEQLA.mjs +1 -0
  26. package/dist/{crashreport-D_wf4GZh.mjs → crashreport-CQTAlnbA.mjs} +2 -2
  27. package/dist/{crashreport-D_wf4GZh.mjs.map → crashreport-CQTAlnbA.mjs.map} +1 -1
  28. package/dist/crashreport-GRnWyDnF.mjs +1 -0
  29. package/dist/{errors-Dmdnq0zi.mjs → errors-9OOL3xOM.mjs} +6 -5
  30. package/dist/errors-9OOL3xOM.mjs.map +1 -0
  31. package/dist/field_behavior_pb-B196sUx6.mjs +2 -0
  32. package/dist/field_behavior_pb-B196sUx6.mjs.map +1 -0
  33. package/dist/{kysely-type-DVL90_oX.mjs → kysely-type-DhAZqdQg.mjs} +14 -10
  34. package/dist/kysely-type-DhAZqdQg.mjs.map +1 -0
  35. package/dist/notification_resource_pb-BZ0hCA01.mjs +1 -0
  36. package/dist/notification_resource_pb-Dwzah7gO.mjs +2 -0
  37. package/dist/notification_resource_pb-Dwzah7gO.mjs.map +1 -0
  38. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  39. package/dist/register-ts-hook-CXYYYgCl.mjs +1094 -0
  40. package/dist/register-ts-hook-CXYYYgCl.mjs.map +1 -0
  41. package/dist/repl-editor-Bposemei.mjs +2 -0
  42. package/dist/repl-editor-Bposemei.mjs.map +1 -0
  43. package/dist/resource_pb-elY668ZB.mjs +2 -0
  44. package/dist/resource_pb-elY668ZB.mjs.map +1 -0
  45. package/dist/{secret-file-DiuEcLFP.mjs → secret-file-CSXzcg4c.mjs} +2 -2
  46. package/dist/{secret-file-DiuEcLFP.mjs.map → secret-file-CSXzcg4c.mjs.map} +1 -1
  47. package/dist/service-B2vx6CGM.mjs +2 -0
  48. package/dist/{service-DDrbdyd-.mjs.map → service-B2vx6CGM.mjs.map} +1 -1
  49. package/dist/service-DB-RHn25.mjs +9 -0
  50. package/dist/service-DB-RHn25.mjs.map +1 -0
  51. package/dist/service-DsAWXbVX.mjs +1 -0
  52. package/dist/service_pb-Bethc2NW.mjs +1 -0
  53. package/dist/service_pb-CfpSriCy.mjs +2 -0
  54. package/dist/service_pb-CfpSriCy.mjs.map +1 -0
  55. package/dist/validation-errors-DqCeUW4L.mjs +3 -0
  56. package/dist/validation-errors-DqCeUW4L.mjs.map +1 -0
  57. package/{LICENSE → dist/vendor/graphql/LICENSE} +6 -6
  58. package/dist/vendor/graphql/language/KindTypeMap.d.mts +3 -0
  59. package/dist/vendor/graphql/language/ast.d.mts +784 -0
  60. package/dist/vendor/graphql/language/kinds_.d.mts +203 -0
  61. package/dist/vendor/graphql/language/source.d.mts +46 -0
  62. package/dist/vendor/graphql/language/tokenKind.d.mts +35 -0
  63. package/dist/workspace_resource_pb-Cow7mrJY.mjs +2 -0
  64. package/dist/workspace_resource_pb-Cow7mrJY.mjs.map +1 -0
  65. package/docs/cli/application.md +13 -9
  66. package/docs/cli/notification.md +128 -0
  67. package/docs/cli/tailordb.md +26 -57
  68. package/docs/cli/workspace.md +1 -1
  69. package/docs/cli-reference.md +228 -48
  70. package/docs/github-actions.md +162 -26
  71. package/docs/services/tailordb/index.md +6 -2
  72. package/docs/services/tailordb/migration.md +53 -56
  73. package/docs/testing.md +0 -1
  74. package/package.json +14 -10
  75. package/dist/application-Cida-j1g.mjs +0 -207
  76. package/dist/application-Cida-j1g.mjs.map +0 -1
  77. package/dist/configure/services/http-adapter/index.d.mts +0 -2
  78. package/dist/crashreport-C2AcQNTe.mjs +0 -1
  79. package/dist/errors-Dmdnq0zi.mjs.map +0 -1
  80. package/dist/kysely-type-DVL90_oX.mjs.map +0 -1
  81. package/dist/register-ts-hook-Ht4zCseV.mjs +0 -1115
  82. package/dist/register-ts-hook-Ht4zCseV.mjs.map +0 -1
  83. package/dist/repl-editor-DkrCZmmg.mjs +0 -2
  84. package/dist/repl-editor-DkrCZmmg.mjs.map +0 -1
  85. package/dist/service-CZ0EdD2-.mjs +0 -10
  86. package/dist/service-CZ0EdD2-.mjs.map +0 -1
  87. package/dist/service-DDrbdyd-.mjs +0 -2
  88. package/dist/service-WhSyhaho.mjs +0 -1
  89. package/dist/service_pb-BMHhVot2.mjs +0 -1
  90. package/dist/service_pb-BxMSK0gg.mjs +0 -2
  91. package/dist/service_pb-BxMSK0gg.mjs.map +0 -1
  92. package/dist/workspace_resource_pb-7smKERdZ.mjs +0 -2
  93. package/dist/workspace_resource_pb-7smKERdZ.mjs.map +0 -1
@@ -173,8 +173,8 @@ and file blobs are not copied.
173
173
 
174
174
  Two setups are supported:
175
175
 
176
- - **Source is the plan/deploy workspace (default).** The job uses the same GitHub
177
- Environment as the plan and deploy jobs, so it reads the same
176
+ - **Source is the deploy workspace (default).** The job uses the same GitHub
177
+ Environment as the deploy job, so it reads the same
178
178
  `TAILOR_PLATFORM_WORKSPACE_ID` variable and machine-user secrets. Nothing
179
179
  extra to configure. If that workspace holds too little data to be a useful
180
180
  test, point the job at a workspace that does with the next setup.
@@ -204,6 +204,51 @@ the source workspace holds production data.
204
204
  Fork pull requests cannot read secrets, so the job is skipped for them.
205
205
  `--migration-test` is not available together with several `--dir` values.
206
206
 
207
+ #### Running the plan job in its own environment
208
+
209
+ The plan job runs on pull requests, and a pull request from a branch of the same
210
+ repository can change the workflow it runs, so its code can read every secret
211
+ and variable of the environment the plan job enters. When plan and deploy share
212
+ one environment, that includes the deploy machine user. Pass
213
+ `--split-plan-environment` on a branch target to run the plan job in an
214
+ environment of its own:
215
+
216
+ ```bash
217
+ tailor setup ci branch --name my-app-stg --split-plan-environment
218
+ ```
219
+
220
+ The plan job then enters an environment named after the deploy environment with
221
+ `-plan` appended (`my-app-stg-plan` here). The deploy job, and the migration test job unless you
222
+ pass `--migration-test-environment`, stay in the deploy environment. Set these
223
+ on the plan environment:
224
+
225
+ - `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` and
226
+ `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` of a machine user of its own. The
227
+ plan only reads, so a viewer role on the organization or folder that holds the
228
+ workspace is enough. Set them on the plan environment itself: otherwise the job
229
+ reads the ones set on the repository or organization, and
230
+ `tailor setup ci env --check` reports them as missing.
231
+ - `TAILOR_PLATFORM_WORKSPACE_ID`, the same workspace the deploy environment
232
+ deploys to.
233
+
234
+ Keep the deploy machine user off the plan environment. `tailor setup ci env`
235
+ lists what each of the two environments needs.
236
+
237
+ The flag alone does not keep the deploy credentials from a pull request, which
238
+ can point the plan job back at the deploy environment. Under **Settings →
239
+ Environments**, limit the deploy environment's **Deployment branches and tags**
240
+ to the target branch, so that GitHub refuses a pull request job in it. With that
241
+ limit in place:
242
+
243
+ - The migration test job also runs on pull requests, so give it its own
244
+ environment with `--migration-test-environment` (see
245
+ [Choosing the source workspace](#choosing-the-source-workspace)).
246
+ - The jobs of a preview target run on pull requests too, so pass
247
+ `setup ci preview` an `--environment` other than the limited one.
248
+
249
+ `setup update` keeps the flag, but re-running `setup ci branch` does not carry it
250
+ over: pass it each time, or the plan job moves back into the deploy environment.
251
+
207
252
  ### Tag target (recommended for production)
208
253
 
209
254
  The tag target fires when a tag matching `--tag-pattern` (default `v*`) is
@@ -259,6 +304,35 @@ that workspace when the pull request is closed:
259
304
  tailor setup ci preview --name my-app --region asia-northeast
260
305
  ```
261
306
 
307
+ It writes two workflows. `tailor-my-app-preview.yml` deploys the pull request
308
+ when it is opened or pushed to, and `tailor-my-app-preview-cleanup.yml` deletes
309
+ the workspace when the pull request is closed. The cleanup workflow starts on
310
+ `pull_request_target`, which GitHub also triggers for a pull request with merge
311
+ conflicts, where workflows on `pull_request` do not start. A
312
+ `pull_request_target` workflow can read the repository's secrets whoever opened
313
+ the pull request, so GitHub runs it from the default branch: the workflow file
314
+ and the code it checks out are the default branch's, never the pull request's.
315
+ Therefore:
316
+
317
+ - The cleanup workflow runs only once it is on the default branch, and it
318
+ installs dependencies and runs `tailor` from the default branch's checkout.
319
+ If the preview target deploys pull requests into another branch
320
+ (`--branch`), commit the cleanup workflow, the lockfile, and the app's
321
+ `tailor.config.ts` to the default branch too.
322
+ - If the target's GitHub Environment restricts which branches can deploy,
323
+ allow the default branch: GitHub checks the cleanup job against it, and the
324
+ deploy job against the pull request's merge ref.
325
+
326
+ When `tailor setup update` regenerates a preview workflow generated before the
327
+ cleanup workflow existed, it moves the `tailor-preview-cleanup` job into the
328
+ cleanup workflow, together with the steps and settings you added to it, and
329
+ copies the top-level `env`, `defaults`, and `concurrency` of the preview
330
+ workflow into the cleanup workflow. Your steps then run from the default branch
331
+ as described above. A job of yours in the preview workflow that runs when the
332
+ pull request is closed no longer runs, because that workflow no longer starts
333
+ then; move it into the cleanup workflow. `setup` warns about each job of yours
334
+ in the preview workflow whose `if` contains an `== 'closed'` comparison.
335
+
262
336
  Fork pull requests are skipped. Every other pull request gets a preview, draft
263
337
  pull requests included. `--require` narrows that to one of:
264
338
 
@@ -349,7 +423,9 @@ environment before the first deploy.
349
423
 
350
424
  Because the variable is scoped to a GitHub Environment, both the `plan` and
351
425
  `deploy` jobs declare `environment:` so the value resolves. When you omit
352
- `--environment`, the environment name defaults to the workspace name.
426
+ `--environment`, the environment name defaults to the workspace name. With
427
+ `--split-plan-environment`, set the variable on the plan environment as well (see
428
+ [Running the plan job in its own environment](#running-the-plan-job-in-its-own-environment)).
353
429
 
354
430
  1. Provision the workspace (once per environment) and obtain its id. For now
355
431
  this is a manual step:
@@ -374,8 +450,9 @@ provisioned yet instead of running a dry-run.
374
450
 
375
451
  If the target environment has required reviewers, that approval gate applies to
376
452
  the `plan` job as well as `deploy`, because `plan` must enter the environment to
377
- read the variable. (A token-based read that lets `plan` run without entering the
378
- environment is planned.)
453
+ read the variable, unless a branch target runs `plan` in its own environment with
454
+ `--split-plan-environment`. (A token-based read that lets `plan` run without
455
+ entering the environment is planned.)
379
456
 
380
457
  ## Package manager
381
458
 
@@ -437,9 +514,9 @@ Everything else is yours, and re-running `setup` keeps it:
437
514
  (such as `playwright install`) _after_ it. A job of your own can depend on a
438
515
  managed job with `needs: tailor-deploy`.
439
516
  - **Your own top-level keys**, such as a workflow-level `env:` or `defaults:`.
440
- - **The top-level `concurrency:` of a preview workflow.** The SDK writes a
441
- default, and re-running `setup` keeps your edits to it. If you delete it,
442
- re-running `setup` adds the default back.
517
+ - **The top-level `concurrency:` of the preview and cleanup workflows.** The
518
+ SDK writes a default, and re-running `setup` keeps your edits to it. If you
519
+ delete it, re-running `setup` adds the default back.
443
520
  - **Runtime settings of managed jobs:** `runs-on`, `timeout-minutes`,
444
521
  `container`, and `env`. If every managed job that re-running `setup` keeps
445
522
  uses the same `runs-on` you set, such as self-hosted runners, a managed job
@@ -481,9 +558,9 @@ A job that runs `tailor` against the workspace needs the machine-user
481
558
  (the `--environment` value, or the workspace name when omitted) as above. A job
482
559
  that only uses `app-url`, such as end-to-end tests, does not.
483
560
 
484
- The job is skipped whenever `tailor-preview-deploy` is skipped: when a pull
485
- request is closed, for fork pull requests, for draft pull requests with
486
- `--require ready`, and for unlabeled ones with `--require label`.
561
+ The job is skipped whenever `tailor-preview-deploy` is skipped: for fork pull
562
+ requests, for draft pull requests with `--require ready`, and for unlabeled
563
+ ones with `--require label`.
487
564
 
488
565
  A preview workspace expires 7 days (`ttl: 7d` on `tailor-preview-deploy`) after
489
566
  the last push to its pull request. When a pull request is closed,
@@ -496,13 +573,32 @@ open; the next push creates a fresh one. Change `ttl` to keep previews longer or
496
573
  shorter. Workflows generated before this was added get these settings the next
497
574
  time you run `tailor setup update`.
498
575
 
499
- Each new preview workflow run for a pull request, such as after a push or when
500
- the pull request is closed, cancels the run still in progress for it, including
501
- your jobs, so they do not keep running against a workspace that the new run
502
- may redeploy or delete. Events on a pull request that stays closed, such as a
503
- label added after merging, do not cancel anything. A job with `if: always()`
504
- still runs when its run is cancelled, and the new run waits for it to finish;
505
- use `if: ${{ !cancelled() }}` instead so it stops with the run.
576
+ A preview workspace last deployed before `ttl` was added to its workflow records
577
+ no expiry, so this sweep never deletes it. To find such leftovers, list the
578
+ preview workspaces created more than 7 days ago:
579
+
580
+ ```bash
581
+ tailor workspace prune --name 'my-app-pr-[0-9]+' --older-than 7d \
582
+ --folder-id <folder id> --limit 0 --dry-run
583
+ ```
584
+
585
+ Pass `--organization-root <organization id>` instead of `--folder-id` when
586
+ `TAILOR_PLATFORM_FOLDER_ID` is unset. The list can include the previews of pull
587
+ requests that are still open: keep each of them with
588
+ `--exclude my-app-pr-<number>`, then run the command again without `--dry-run`.
589
+
590
+ Each new preview workflow run for a pull request, such as after a push, cancels
591
+ the run still in progress for it, including your jobs, so they do not keep
592
+ running against a workspace that the new run may redeploy. Closing the pull
593
+ request cancels it too, because the cleanup workflow runs in the same
594
+ concurrency group. If you change the group in one of the two workflows, change
595
+ it in the other, and build it only from values that are the same in both, such
596
+ as `github.event.pull_request.number`: `github.ref`, `github.workflow`, and
597
+ `github.event_name` differ between them, and `github.ref` is the default branch
598
+ for every pull request in the cleanup workflow. Events on a pull request that
599
+ stays closed, such as a label added after merging, do not cancel anything. A job with `if: always()` still
600
+ runs when its run is cancelled, and the new run waits for it to finish; use
601
+ `if: ${{ !cancelled() }}` instead so it stops with the run.
506
602
 
507
603
  Likewise, the `tailor-deploy` job of a branch or tag workflow exposes the
508
604
  deployed workspace as the outputs `workspace-id` and `app-url` to a job with
@@ -528,7 +624,9 @@ job back. To start over from a clean template, delete the file and re-run
528
624
  When a template update removes a managed job that contains steps of yours, or
529
625
  a job of yours `needs` a removed job, `setup` stops and names them. Move those
530
626
  steps into a job of your own (or update the `needs`) and re-run. `--force` drops
531
- steps left inside a removed job.
627
+ steps left inside a removed job. The `tailor-preview-cleanup` job, which moved
628
+ into its own workflow, keeps your steps instead (see
629
+ [Preview target](#preview-target-per-pull-request-workspace)).
532
630
 
533
631
  `setup check` reports a job or step of yours whose `id` starts with `tailor-`,
534
632
  and re-running `setup` stops on it, naming the id. Rename it; `--force` does
@@ -562,7 +660,6 @@ tailor-preview-result:
562
660
  name: tailor-preview-result (my-app)
563
661
  needs:
564
662
  - tailor-preview-deploy
565
- - tailor-preview-cleanup
566
663
  - seed
567
664
  - e2e
568
665
  ```
@@ -660,7 +757,13 @@ keeps them alongside that environment's `TAILOR_PLATFORM_WORKSPACE_ID`
660
757
  variable. You can also set the client secret as a repository-level secret and the
661
758
  client ID as a repository-level variable if every target shares one machine user,
662
759
  but then any workflow on any branch can read them, so the environment's
663
- protection rules no longer guard your deploys.
760
+ protection rules no longer guard your deploys, and `tailor setup ci env --check`
761
+ warns about the client secret (see
762
+ [Checking what is registered](#checking-what-is-registered)). To share one machine
763
+ user and keep that protection, set the same client secret on each environment
764
+ instead. The plan environment of `--split-plan-environment` is the exception: give
765
+ it a machine user of its own (see
766
+ [Running the plan job in its own environment](#running-the-plan-job-in-its-own-environment)).
664
767
 
665
768
  ### Setting secrets and variables
666
769
 
@@ -704,6 +807,16 @@ The autofix workflow uses no GitHub Environment, so its two entries are set on
704
807
  the repository; the output lists them under the repository instead of an
705
808
  environment.
706
809
 
810
+ A branch target generated with `--split-plan-environment` reads from two
811
+ environments. The plan environment needs its own
812
+ `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` and
813
+ `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET`, for which a viewer role is enough,
814
+ and `TAILOR_PLATFORM_WORKSPACE_ID`; of the optional entries, only the plan job's
815
+ `TAILOR_PLATFORM_FAIL_ON_DRIFT` and `SEED_USER_PASSWORD` apply to it. A manual dry
816
+ run reads `SEED_USER_PASSWORD` from there, and pull requests can read it too, like
817
+ everything on that environment. The deploy environment needs the rest, without
818
+ `TAILOR_PLATFORM_FAIL_ON_DRIFT`.
819
+
707
820
  See [Account management](https://docs.tailor.tech/administration/account-management)
708
821
  for how organizations, folders, workspaces, and machine users relate.
709
822
 
@@ -735,13 +848,29 @@ to manage those settings in Terraform instead.
735
848
  environment and for the repository entries of the autofix workflow, which of
736
849
  these entries are registered. It fails when a required
737
850
  entry is missing, reports missing optional entries without failing, and prints
738
- the `gh` commands that set only the missing entries, with where each value comes
739
- from. It changes nothing, and it checks names only, since GitHub never returns
740
- secret values.
851
+ the `gh` commands that set the missing entries, with where each value comes
852
+ from, and that move the client secret onto its environment (see below). It
853
+ changes nothing, and it checks names only, since GitHub never returns secret
854
+ values.
741
855
 
742
856
  - An entry set on the repository, or on the organization and shared with the
743
857
  repository, counts as registered, because the workflow reads it from there when
744
858
  the environment does not set it. The report says where each entry was found.
859
+ - `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` set on the repository or the
860
+ organization is reported with ⚠, even when the environment also sets it: any
861
+ workflow on any branch can read it there, so the environment's protection rules
862
+ do not guard it. The check still passes. The printed commands set it on the
863
+ environment, with the value you set on the repository or organization, and list
864
+ a commented-out `gh secret delete` for the repository copy: run it once every
865
+ environment and workflow that reads that secret has its own. An organization
866
+ owner has to stop sharing an organization secret with the repository. The
867
+ warning stops once the client secret is set only on the environments; when
868
+ every target shares one machine user, set the same client secret on each of
869
+ them.
870
+ - On the plan environment of `--split-plan-environment`, the machine user's
871
+ client ID and client secret count only when set on that environment, since
872
+ the plan job would otherwise sign in as the machine user set on the repository
873
+ or organization.
745
874
  - An entry registered as the other kind, such as `TAILOR_PLATFORM_WORKSPACE_ID`
746
875
  set as a secret, is reported as missing, because the workflow does not read it.
747
876
  `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` set as a secret still counts, with a
@@ -773,7 +902,10 @@ environment-scoped secrets there.
773
902
 
774
903
  Required reviewers gate the `plan` job as well, because `plan` must enter the
775
904
  environment to read `TAILOR_PLATFORM_WORKSPACE_ID`. (A token-based read that
776
- lets `plan` run without entering the environment is planned.)
905
+ lets `plan` run without entering the environment is planned.) On a branch target,
906
+ `--split-plan-environment` moves `plan` into an environment of its own, whose
907
+ settings then apply to it instead (see
908
+ [Running the plan job in its own environment](#running-the-plan-job-in-its-own-environment)).
777
909
 
778
910
  ## Manual runs and dry-run
779
911
 
@@ -808,7 +940,11 @@ can deploy, configure **Deployment branches and tags** on the target's GitHub
808
940
  Environment and keep the machine-user secrets on that environment. Because the
809
941
  plan job also enters the environment, allow `refs/pull/*/merge` as well so pull
810
942
  request plans keep running, and note that dry runs from other branches are then
811
- rejected too.
943
+ rejected too. On a branch target, `--split-plan-environment` moves the plan job,
944
+ dry runs included, into an environment of its own instead, so the deploy
945
+ environment can allow the target branch alone once the migration test job, if
946
+ any, has its own environment as well (see
947
+ [Running the plan job in its own environment](#running-the-plan-job-in-its-own-environment)).
812
948
 
813
949
  ## Seeding the workspace after deploy
814
950
 
@@ -226,7 +226,9 @@ const user = db.table("User", {
226
226
  });
227
227
  ```
228
228
 
229
- A relation can also be set on a field inside `db.object()`. The inner field gets the foreign key constraint only: no index, no unique constraint, and no forward or backward relationship, as with `type: "keyOnly"`.
229
+ A `keyOnly` relation does not make the field unique; add `.unique()` to the field to make it unique.
230
+
231
+ A relation can also be set on a field inside `db.object()`, where it adds only the foreign key constraint to the inner field, and `.unique()` and `.index()` set on that field work as described in [Index / Unique](#index--unique).
230
232
 
231
233
  ```typescript
232
234
  const order = db.table("Order", {
@@ -477,6 +479,8 @@ db.string().serial({
477
479
  });
478
480
  ```
479
481
 
482
+ `format` holds exactly one of `%d` (decimal), `%o` (octal), `%x` (lowercase hexadecimal), or `%X` (uppercase hexadecimal), optionally with a width: `%5d` pads with spaces and `%05d` with zeros, to at least 5 characters. Write a literal `%` as `%%`. The format is at most 32 characters.
483
+
480
484
  A serial field cannot be used inside a `db.object()` field.
481
485
 
482
486
  > **Preview:** The `gapless` option is a preview feature. Its behavior and configuration may change before general availability.
@@ -571,7 +575,7 @@ db.table("User", {
571
575
  - When not specified, `deploy` sets it from the executors taking part in the same run: `true` while one of them uses this table with `recordCreatedTrigger`, `recordUpdatedTrigger`, or `recordDeletedTrigger`, and `false` once none does. Removing the last such trigger turns publishing back off on the next `deploy`
572
576
  - When explicitly set to `false` while an executor taking part in the same run uses this table, `deploy` fails
573
577
  - An executor declared with `disabled: true` never runs, so it does not count as using the table
574
- - While a `deploy` applies pending migrations with [maintenance mode](./migration.md#maintenance-mode) on, every table in the migrating namespace is restricted: the GraphQL create, update, and delete operations and bulk upsert are disabled, and the table's `read` setting is not switched off for the migration (a table the migration creates uses its configured setting), so nothing writes an intermediate schema and readable tables are not shut down. Record event publishing is also switched off so a migration script's writes do not reach executors from the previous deploy. With `maintenanceMode: false`, these restrictions are not applied. After success, the configured settings take effect. If a later migration fails, settings from the last confirmed checkpoint are restored; an uncommitted migration restores existing tables' prior settings and leaves its newly created tables restricted until a successful retry. A table that already refuses create, update, delete, and bulk upsert and publishes no record events is left as it is. Restoration is skipped if the checkpoint number or migration history changed concurrently, or if checkpoint ownership cannot be verified; the deploy then reports the namespace whose tables stay restricted and points to `tailor tailordb migration status --namespace <namespace>`. When a failed migration or a failed restoration leaves tables restricted, the deploy lists their names, and deploying again releases them once the migration completes. A table that a failed rollback could not delete or revert is the exception: the deploy also lists it, but the next deploy stops with remote schema drift until you delete or repair that table by hand. A table left behind by a failed post-checkpoint deletion also remains read-only for manual recovery, and the deploy does not list it by name
578
+ - While a `deploy` applies pending migrations with [maintenance mode](./migration.md#maintenance-mode) on, every table in the migrating namespace is restricted: the GraphQL create, update, and delete operations and bulk upsert are disabled, and the table's `read` setting is not switched off for the migration (a table the migration creates uses its configured setting), so nothing writes an intermediate schema and readable tables are not shut down. Record event publishing is also switched off so a migration script's writes do not reach executors from the previous deploy. With `maintenanceMode: false`, these restrictions are not applied. After success, the configured settings take effect. If a later migration fails, settings from the last confirmed checkpoint are restored; an uncommitted migration restores existing tables' prior settings and leaves its newly created tables restricted until a successful retry. A table that already refuses create, update, delete, and bulk upsert and publishes no record events is left as it is. Restoration is skipped if the checkpoint number or migration history changed concurrently, or if checkpoint ownership cannot be verified; the deploy then reports the namespace whose tables stay restricted and points to `tailor tailordb migration status --namespace <namespace>`. When a failed migration or a failed restoration leaves tables restricted, the deploy lists their names, and deploying again releases them once the migration completes. A table that a failed rollback could not delete or revert is also listed: when it still has the migration's pre-migration schema, deploying again runs the migration again; otherwise the next deploy stops with remote schema drift until you delete or repair that table by hand. A table left behind by a failed post-checkpoint deletion also remains read-only, and the deploy does not list it by name; the next deploy deletes it
575
579
 
576
580
  **Use cases:**
577
581