@tailor-platform/sdk 2.3.0 → 2.4.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 (102) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +1 -1
  3. package/dist/{application-F-nl107y.mjs → application-C8a0UJ-z.mjs} +201 -149
  4. package/dist/application-C8a0UJ-z.mjs.map +1 -0
  5. package/dist/application-D4zOiLLa.mjs +3 -0
  6. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +4 -4
  7. package/dist/cli/commands/tailordb/migrate/generate.d.mts +3 -3
  8. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +5 -5
  9. package/dist/cli/commands/tailordb/truncate.d.mts +2 -2
  10. package/dist/cli/lib.mjs +2 -2
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/cli/main.mjs +174 -106
  13. package/dist/cli/main.mjs.map +1 -1
  14. package/dist/cli/ts-hook.mjs +3 -1
  15. package/dist/completion/zsh-worker.zsh +19 -11
  16. package/dist/configure/config/index.d.mts +1 -1
  17. package/dist/configure/index.d.mts +2 -1
  18. package/dist/configure/index.mjs +2 -2
  19. package/dist/configure/index.mjs.map +1 -1
  20. package/dist/configure/services/aigateway/index.d.mts +5 -4
  21. package/dist/configure/services/aigateway/types.d.mts +6 -1
  22. package/dist/configure/services/auth/types.d.mts +2 -2
  23. package/dist/configure/services/tailordb/permission.d.mts +1 -1
  24. package/dist/configure/services/tailordb/schema.d.mts +1 -1
  25. package/dist/configure/services/tailordb/types.d.mts +7 -4
  26. package/dist/configure/types/auth-namespace-name.d.mts +18 -0
  27. package/dist/{crashreport-CINZ5dVN.mjs → crashreport-Cd4TqfZq.mjs} +1 -1
  28. package/dist/{crashreport-CIIfLnoO.mjs → crashreport-Q0pDAlrj.mjs} +8 -4
  29. package/dist/{crashreport-CIIfLnoO.mjs.map → crashreport-Q0pDAlrj.mjs.map} +1 -1
  30. package/dist/{errors-BVb6vYGy.mjs → errors-Bxut5_Sy.mjs} +2 -2
  31. package/dist/{errors-BVb6vYGy.mjs.map → errors-Bxut5_Sy.mjs.map} +1 -1
  32. package/dist/file-BUcmVIH2.mjs.map +1 -1
  33. package/dist/kysely/index.mjs +2 -1
  34. package/dist/kysely/index.mjs.map +1 -1
  35. package/dist/{logger-DSdExfor.mjs → logger-D0bEznPv.mjs} +9 -5
  36. package/dist/{logger-DSdExfor.mjs.map → logger-D0bEznPv.mjs.map} +1 -1
  37. package/dist/package-json-8b0O9TlX.mjs.map +1 -1
  38. package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
  39. package/dist/plugin/builtin/enum-constants/index.mjs +5 -5
  40. package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
  41. package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
  42. package/dist/plugin/builtin/file-utils/index.mjs +14 -11
  43. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  44. package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
  45. package/dist/plugin/builtin/kysely-type/index.mjs +9 -9
  46. package/dist/plugin/builtin/kysely-type/index.mjs.map +1 -1
  47. package/dist/plugin/builtin/seed/index.mjs +1 -1
  48. package/dist/plugin/builtin/seed/seed-type-processor.d.mts +4 -4
  49. package/dist/plugin/get-generated-type.d.mts +4 -4
  50. package/dist/plugin/index.mjs +13 -13
  51. package/dist/plugin/index.mjs.map +1 -1
  52. package/dist/plugin/types.d.mts +2 -2
  53. package/dist/plugin/with-context.d.mts +1 -1
  54. package/dist/{register-ts-hook-ClI226n2.mjs → register-ts-hook-Vqjza10T.mjs} +552 -416
  55. package/dist/register-ts-hook-Vqjza10T.mjs.map +1 -0
  56. package/dist/{repl-editor-BCozyiNq.mjs → repl-editor-F9LotMNx.mjs} +3 -2
  57. package/dist/{repl-editor-BCozyiNq.mjs.map → repl-editor-F9LotMNx.mjs.map} +1 -1
  58. package/dist/runtime/file.d.mts +14 -14
  59. package/dist/{schema--xYWRGfe.mjs → schema-D4TC4BC3.mjs} +4 -3
  60. package/dist/schema-D4TC4BC3.mjs.map +1 -0
  61. package/dist/seed/index.d.mts +4 -4
  62. package/dist/seed/index.mjs +3 -3
  63. package/dist/seed/index.mjs.map +1 -1
  64. package/dist/{seed-xqM9WIHP.mjs → seed-Cibjmtry.mjs} +46 -40
  65. package/dist/seed-Cibjmtry.mjs.map +1 -0
  66. package/dist/{service-C_WpbKHu.mjs → service-2grcAOCJ.mjs} +24 -24
  67. package/dist/service-2grcAOCJ.mjs.map +1 -0
  68. package/dist/service-CaBPNDIZ.mjs +3 -0
  69. package/dist/{service-hZskxZmg.mjs → service-LTfp03ZZ.mjs} +7 -5
  70. package/dist/{service-hZskxZmg.mjs.map → service-LTfp03ZZ.mjs.map} +1 -1
  71. package/dist/types/aigateway.generated.d.mts +2 -2
  72. package/dist/types/auth.generated.d.mts +3 -3
  73. package/dist/types/executor.generated.d.mts +1 -1
  74. package/dist/types/tailordb.generated.d.mts +12 -12
  75. package/dist/utils/test/index.d.mts +4 -4
  76. package/dist/utils/test/index.mjs +4 -4
  77. package/dist/utils/test/index.mjs.map +1 -1
  78. package/dist/vitest/index.mjs +13 -7
  79. package/dist/vitest/index.mjs.map +1 -1
  80. package/dist/vitest/mocks/file.d.mts +1 -1
  81. package/docs/cli/application.md +3 -1
  82. package/docs/cli/function.md +17 -13
  83. package/docs/cli/tailordb.md +30 -30
  84. package/docs/cli/workspace.md +2 -2
  85. package/docs/cli-reference.md +21 -21
  86. package/docs/configuration.md +2 -2
  87. package/docs/migration/v2.md +4 -4
  88. package/docs/migration/v3.md +41 -0
  89. package/docs/quickstart.md +1 -1
  90. package/docs/services/aigateway.md +33 -9
  91. package/docs/services/executor.md +5 -1
  92. package/docs/services/tailordb-migration.md +47 -47
  93. package/docs/services/tailordb.md +7 -7
  94. package/docs/testing.md +1 -1
  95. package/package.json +6 -6
  96. package/dist/application-BZfk4HKm.mjs +0 -3
  97. package/dist/application-F-nl107y.mjs.map +0 -1
  98. package/dist/register-ts-hook-ClI226n2.mjs.map +0 -1
  99. package/dist/schema--xYWRGfe.mjs.map +0 -1
  100. package/dist/seed-xqM9WIHP.mjs.map +0 -1
  101. package/dist/service-C_WpbKHu.mjs.map +0 -1
  102. package/dist/service-D1RCdzIL.mjs +0 -3
@@ -59,7 +59,7 @@ If your local tables and remote schema have **diverged**, reconcile them before
59
59
 
60
60
  ### Resetting
61
61
 
62
- `tailor tailordb migration generate --init` deletes the existing `migrations/` directory and creates `0000` from the current local types. Use it only before the project is deployed. For a deployed migration history, use [`migration rebaseline`](#re-baselining-a-deployed-migration-history), which verifies the history and connected workspace before replacing any files.
62
+ `tailor tailordb migration generate --init` deletes the existing `migrations/` directory and creates `0000` from the current local tables. Use it only before the project is deployed. For a deployed migration history, use [`migration rebaseline`](#re-baselining-a-deployed-migration-history), which verifies the history and connected workspace before replacing any files.
63
63
 
64
64
  ## Migration Workflow
65
65
 
@@ -141,9 +141,9 @@ In an interactive session, `migration generate` offers to record the reason on t
141
141
 
142
142
  ### Renaming a field
143
143
 
144
- Renaming a field in a type definition looks like a removal plus an addition to the diff engine. Left as-is, that combination silently drops the old field's data: the removal is only a warning, so nothing forces a data copy.
144
+ Renaming a field in a table definition looks like a removal plus an addition to the diff engine. Left as-is, that combination silently drops the old field's data: the removal is only a warning, so nothing forces a data copy.
145
145
 
146
- To prevent that, when `migration generate` finds a removed field and an added field in the same type whose stored values can be copied without changing their meaning, it asks whether the change is a rename. Serial fields are never rename candidates, and an enum field only qualifies when it keeps every value of the removed field:
146
+ To prevent that, when `migration generate` finds a removed field and an added field in the same table whose stored values can be copied without changing their meaning, it asks whether the change is a rename. Serial fields are never rename candidates, and an enum field only qualifies when it keeps every value of the removed field:
147
147
 
148
148
  ```
149
149
  ? User.fullName was removed and displayName was added with a compatible type. Was it renamed to displayName? (Y/n)
@@ -166,30 +166,30 @@ If you decline the prompt (or confirm the removal with `--drop`), the change sta
166
166
 
167
167
  Renaming a member inside a **nested field** is not detected, and it is quieter: `User.address.zip` → `zipCode` becomes a single `field_modified` on `address` with no breaking change, no warning, and no generated script, so the member's values are not carried over. Copy them with a custom `tailordb migration script` if they must survive.
168
168
 
169
- ### Renaming a type
169
+ ### Renaming a table
170
170
 
171
- Renaming a whole type is detected the same way: when `migration generate` finds a removed type and an added type with a matching shape, it asks whether the change is a rename:
171
+ Renaming a whole table is detected the same way: when `migration generate` finds a removed table and an added table with a matching shape, it asks whether the change is a rename:
172
172
 
173
173
  ```
174
174
  ? User was removed and Person was added with a compatible schema. Was it renamed to Person? (Y/n)
175
175
  ```
176
176
 
177
- In non-interactive environments the command fails while a candidate is left unresolved, exactly like field renames. Resolve it with the type forms of the same flags (a value without a `.` targets a type):
177
+ In non-interactive environments the command fails while a candidate is left unresolved, exactly like field renames. Resolve it with the table forms of the same flags (a value without a `.` targets a table):
178
178
 
179
179
  ```bash
180
180
  tailor tailordb migration generate --rename "User:Person"
181
181
  tailor tailordb migration generate --drop "User"
182
182
  ```
183
183
 
184
- Two types qualify as a rename pair only when copying every row preserves the data: every field must keep its name, type, array-ness, required/unique constraints, foreign key target, and decimal scale; enum fields may gain values but not lose them; indexes must match. A self-referential foreign key is compared against the new type name and must be optional. Types with serial fields (their values cannot be written by a script) or file fields (file contents are not copied) are never candidates. Name-derived and data-independent settings — `pluralForm`, description, type settings, permissions, hooks, and validations — may differ.
184
+ Two tables qualify as a rename pair only when copying every row preserves the data: every field must keep its name, type, array-ness, required/unique constraints, foreign key target, and decimal scale; enum fields may gain values but not lose them; indexes must match. A self-referential foreign key is compared against the new table name and must be optional. Tables with serial fields (their values cannot be written by a script) or file fields (file contents are not copied) are never candidates. Name-derived and data-independent settings — `pluralForm`, description, table settings, permissions, hooks, and validations — may differ.
185
185
 
186
- A confirmed rename is recorded as a single `table_renamed` change and treated as **breaking** for two reasons: existing records must be copied by the migration script, and the type's GraphQL API names (derived from the type name and `pluralForm`) change, which breaks API clients. The generated `migrate.ts` copies every row from the old type into the new one in id-ordered batches, preserving ids so stored foreign key references stay valid, and the generated `db.ts` exposes both the old table (readable) and the new table (writable). Self-referential foreign keys are inserted as null and backfilled after every row exists, so a reference to a row in a later batch cannot fail the copy.
186
+ A confirmed rename is recorded as a single `table_renamed` change and treated as **breaking** for two reasons: existing records must be copied by the migration script, and the table's GraphQL API names (derived from the table name and `pluralForm`) change, which breaks API clients. The generated `migrate.ts` copies every row from the old table into the new one in id-ordered batches, preserving ids so stored foreign key references stay valid, and the generated `db.ts` exposes both the old table (readable) and the new table (writable). Self-referential foreign keys are inserted as null and backfilled after every row exists, so a reference to a row in a later batch cannot fail the copy.
187
187
 
188
- Two caveats apply to the copy. The old type is not write-protected: rows written to it after the script's transaction commits — and before post-migration cleanup drops it — are not carried over, so pause writers to the renamed type for the duration of the deploy. And platform-managed record metadata (creation/update timestamps and actors) cannot be written by the script, so the new type's records carry the migration run's metadata instead of the original values.
188
+ Two caveats apply to the copy. The old table is not write-protected: rows written to it after the script's transaction commits — and before post-migration cleanup drops it — are not carried over, so pause writers to the renamed table for the duration of the deploy. And platform-managed record metadata (creation/update timestamps and actors) cannot be written by the script, so the new table's records carry the migration run's metadata instead of the original values.
189
189
 
190
- Fields on other types that reference the renamed type via `foreignKeyType` must be retargeted at the new name in the same change. That retarget is recognized as part of the rename: it is not flagged as a breaking foreign-key change and needs no reference fixup, because record ids are preserved by the copy.
190
+ Fields on other tables that reference the renamed table via `foreignKeyType` must be retargeted at the new name in the same change. That retarget is recognized as part of the rename: it is not flagged as a breaking foreign-key change and needs no reference fixup, because record ids are preserved by the copy.
191
191
 
192
- During deploy, the pre-migration phase creates the new type with its full constraints while the old type stays on the namespace, the script copies the rows, and the old type is dropped in post-migration cleanup after the checkpoint advances — all within a single `tailor deploy`.
192
+ During deploy, the pre-migration phase creates the new table with its full constraints while the old table stays on the namespace, the script copies the rows, and the old table is dropped in post-migration cleanup after the checkpoint advances — all within a single `tailor deploy`.
193
193
 
194
194
  ### Breaking changes without a script
195
195
 
@@ -320,31 +320,31 @@ The `env` values are injected at bundle time (the same mechanism as resolvers/ex
320
320
 
321
321
  ## Supported Schema Changes
322
322
 
323
- | Change Type | Breaking? | Migration Script? | Notes |
324
- | --------------------------------- | --------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
325
- | Add optional field | No | No | Schema change only |
326
- | Add required field | Yes | Yes | Script populates default values |
327
- | Remove field | No | Optional | Warning tier — no script is auto-generated, but you can add one with `tailordb migration script` to preserve or clear data before the field leaves the active schema. The field stays readable from `migrate.ts` during Pre-migration. |
328
- | Rename field | Yes | Yes | Confirmed interactively at generate time or via `--rename "Type.old:new"` — see [Renaming a field](#renaming-a-field). Auto-generated script copies values from the old field to the new one; both fields coexist during Pre-migration. |
329
- | Change optional → required | Yes | Yes | Script sets defaults for null values |
330
- | Change required → optional | No | No | Schema change only |
331
- | Add index (non-unique) | No | No | Schema change only |
332
- | Add unique index | Yes | Yes | Script must resolve duplicate value combinations across the index fields |
333
- | Change unique index fields | Yes | Yes | Treated like adding a new unique constraint over the new field set |
334
- | Remove index | No | No | Schema change only (removing the unique constraint from an index is also non-breaking) |
335
- | Add unique constraint | Yes | Yes | Script must resolve duplicate values |
336
- | Remove unique constraint | No | No | Schema change only |
337
- | Change decimal scale | Yes | Yes | Auto-generated script re-saves existing rows under the new scale. Decreasing scale rounds values half-up and can lose precision. If the same change adds a unique constraint, duplicate handling runs after re-saving. |
338
- | Add enum value | No | No | Schema change only |
339
- | Remove enum value | Yes | Yes | Script migrates records with removed values |
340
- | Add table | No | No | Schema change only |
341
- | Remove table | No | Optional | Warning tier — no script is auto-generated, but you can add one with `tailordb migration script` to preserve data before the table leaves the active schema. The table stays readable from `migrate.ts` during Pre-migration. |
342
- | Rename table | Yes | Yes | Confirmed interactively at generate time or via `--rename "OldType:NewType"` — see [Renaming a type](#renaming-a-type). Auto-generated script copies all rows preserving ids; both tables coexist until post-migration cleanup. |
343
- | Change foreign key target table | Yes | Yes | Script updates references to the new target |
344
- | Change field type (verified pair) | Yes | Yes | In-place for the pairs listed under [Field type changes](#field-type-changes); review the generated normalization scaffold and customize it only when existing values need transformation |
345
- | Change field type (other pair) | Yes | Yes | Two migrations, generated together after you confirm — see [Converting a field type](#converting-a-field-type). Edit the conversion in the first; the second needs no changes. |
346
- | Change array → single value | - | - | **Not supported** — see [Converting a field type](#converting-a-field-type) |
347
- | Change single value → array | - | - | **Not supported** — see [Converting a field type](#converting-a-field-type) |
323
+ | Change Type | Breaking? | Migration Script? | Notes |
324
+ | --------------------------------- | --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
325
+ | Add optional field | No | No | Schema change only |
326
+ | Add required field | Yes | Yes | Script populates default values |
327
+ | Remove field | No | Optional | Warning tier — no script is auto-generated, but you can add one with `tailordb migration script` to preserve or clear data before the field leaves the active schema. The field stays readable from `migrate.ts` during Pre-migration. |
328
+ | Rename field | Yes | Yes | Confirmed interactively at generate time or via `--rename "Table.oldField:newField"` — see [Renaming a field](#renaming-a-field). Auto-generated script copies values from the old field to the new one; both fields coexist during Pre-migration. |
329
+ | Change optional → required | Yes | Yes | Script sets defaults for null values |
330
+ | Change required → optional | No | No | Schema change only |
331
+ | Add index (non-unique) | No | No | Schema change only |
332
+ | Add unique index | Yes | Yes | Script must resolve duplicate value combinations across the index fields |
333
+ | Change unique index fields | Yes | Yes | Treated like adding a new unique constraint over the new field set |
334
+ | Remove index | No | No | Schema change only (removing the unique constraint from an index is also non-breaking) |
335
+ | Add unique constraint | Yes | Yes | Script must resolve duplicate values |
336
+ | Remove unique constraint | No | No | Schema change only |
337
+ | Change decimal scale | Yes | Yes | Auto-generated script re-saves existing rows under the new scale. Decreasing scale rounds values half-up and can lose precision. If the same change adds a unique constraint, duplicate handling runs after re-saving. |
338
+ | Add enum value | No | No | Schema change only |
339
+ | Remove enum value | Yes | Yes | Script migrates records with removed values |
340
+ | Add table | No | No | Schema change only |
341
+ | Remove table | No | Optional | Warning tier — no script is auto-generated, but you can add one with `tailordb migration script` to preserve data before the table leaves the active schema. The table stays readable from `migrate.ts` during Pre-migration. |
342
+ | Rename table | Yes | Yes | Confirmed interactively at generate time or via `--rename "OldTable:NewTable"` — see [Renaming a table](#renaming-a-table). Auto-generated script copies all rows preserving ids; both tables coexist until post-migration cleanup. |
343
+ | Change foreign key target table | Yes | Yes | Script updates references to the new target |
344
+ | Change field type (verified pair) | Yes | Yes | In-place for the pairs listed under [Field type changes](#field-type-changes); review the generated normalization scaffold and customize it only when existing values need transformation |
345
+ | Change field type (other pair) | Yes | Yes | Two migrations, generated together after you confirm — see [Converting a field type](#converting-a-field-type). Edit the conversion in the first; the second needs no changes. |
346
+ | Change array → single value | - | - | **Not supported** — see [Converting a field type](#converting-a-field-type) |
347
+ | Change single value → array | - | - | **Not supported** — see [Converting a field type](#converting-a-field-type) |
348
348
 
349
349
  ### Field type changes
350
350
 
@@ -449,7 +449,7 @@ Without the flag the command fails rather than converting anything, so a scripte
449
449
  Some changes are still rejected and need a temporary field you add yourself — add the new field, write a script that fills it and clears the old one, then remove the old field and rename the temporary one in a later migration:
450
450
 
451
451
  - Array-to-scalar and scalar-to-array, since collapsing an array has no answer the generated script could choose for you.
452
- - A field that is unique, or that an index, relationship, permission, or type-level script names. Those keep pointing at the original name, which the conversion removes.
452
+ - A field that is unique, or that an index, relationship, permission, or table-level script names. Those keep pointing at the original name, which the conversion removes.
453
453
 
454
454
  ## Testing Pending Migrations
455
455
 
@@ -465,25 +465,25 @@ The command performs the following sequence:
465
465
  6. Optionally runs an assertion script against the migrated data.
466
466
  7. Deletes an automatically-created workspace after success or failure.
467
467
 
468
- Both the pre-migration and final TailorDB schemas come from committed migration snapshots. Ungenerated changes in the current type source are not included in the rehearsal.
468
+ Both the pre-migration and final TailorDB schemas come from committed migration snapshots. Ungenerated changes in the current table definitions are not included in the rehearsal.
469
469
 
470
470
  Executors are omitted from the baseline deployment so loading fixture or cloned records cannot trigger current event handlers against the older schema. Auth user profiles are also deferred until the pending migrations finish, while configured machine users remain available to run seed and migration scripts. The final deployment restores the configured executors and user profiles. Static websites are deployed so configuration references to their URLs resolve, but their workspace-bound custom domains are omitted from migration-test deployments.
471
471
 
472
472
  ### Seed mode
473
473
 
474
- Seed mode is the default and uses the JSONL files produced by the configured `seedPlugin`. Run `tailor generate` after adding the plugin or changing seed types, then populate its `data/*.jsonl` files:
474
+ Seed mode is the default and uses the JSONL files produced by the configured `seedPlugin`. Run `tailor generate` after adding the plugin or changing seed tables, then populate its `data/*.jsonl` files:
475
475
 
476
476
  ```bash
477
477
  tailor tailordb migration test --data seed
478
478
  ```
479
479
 
480
- Rows are loaded only for types present in the deployed pre-migration snapshots (and current schemas without migrations), in foreign-key dependency order. Fields introduced by pending migrations, including timestamp and nested fields, are removed before insertion so current fixtures can be loaded into the baseline schema. Missing type files are treated as empty. IdP `_User` fixtures are not loaded by this command.
480
+ Rows are loaded only for tables present in the deployed pre-migration snapshots (and current schemas without migrations), in foreign-key dependency order. Fields introduced by pending migrations, including timestamp and nested fields, are removed before insertion so current fixtures can be loaded into the baseline schema. Missing table files are treated as empty. IdP `_User` fixtures are not loaded by this command.
481
481
 
482
482
  Use `--machine-user` to override the seed plugin's `machineUserName`, the namespace migration setting, and the first configured Auth machine user for seed and assertion execution.
483
483
 
484
484
  ### Clone mode
485
485
 
486
- Clone mode copies TailorDB records from the source workspace after the identical application, namespace names, and pre-migration schemas exist in the target. For namespaces without migration history, the command reproduces the deployed source schema rather than uncommitted local type changes:
486
+ Clone mode copies TailorDB records from the source workspace after the identical application, namespace names, and pre-migration schemas exist in the target. For namespaces without migration history, the command reproduces the deployed source schema rather than uncommitted local table changes:
487
487
 
488
488
  ```bash
489
489
  tailor tailordb migration test --data clone
@@ -530,10 +530,10 @@ When you run `tailor deploy`, the SDK detects pending migrations (anything past
530
530
 
531
531
  For each pending migration:
532
532
 
533
- 1. **Pre-migration**: Schema changes that would be breaking are applied in a relaxed form first. A verified in-place field type change keeps its complete previous field contract until Post-migration, including field and type-level hooks or validators changed by the same migration. Newly-required fields are added as optional; fields whose `optional → required` transition is breaking are temporarily kept optional. Fields that are being removed in this migration are temporarily kept on the table so that `migrate.ts` can still read them (for example, to `innerJoin` through a foreign key that is about to be dropped). For a renamed field, the old field is kept and the new field is added with its constraints relaxed, so the script can read the old field and write the new one. For a renamed type, the new type is created with its full constraints while the old type stays on the namespace until post-migration cleanup, so the script can copy rows between them. Breaking type-level index changes are relaxed the same way: a newly-added unique index is withheld, and an index gaining a unique constraint (or a unique index changing its field set) keeps its previous definition, so `migrate.ts` can resolve duplicates first. Non-breaking changes that are part of the same migration are also applied here.
533
+ 1. **Pre-migration**: Schema changes that would be breaking are applied in a relaxed form first. A verified in-place field type change keeps its complete previous field contract until Post-migration, including field and table-level hooks or validators changed by the same migration. Newly-required fields are added as optional; fields whose `optional → required` transition is breaking are temporarily kept optional. Fields that are being removed in this migration are temporarily kept on the table so that `migrate.ts` can still read them (for example, to `innerJoin` through a foreign key that is about to be dropped). For a renamed field, the old field is kept and the new field is added with its constraints relaxed, so the script can read the old field and write the new one. For a renamed table, the new table is created with its full constraints while the old table stays on the namespace until post-migration cleanup, so the script can copy rows between them. Breaking table-level index changes are relaxed the same way: a newly-added unique index is withheld, and an index gaining a unique constraint (or a unique index changing its field set) keeps its previous definition, so `migrate.ts` can resolve duplicates first. Non-breaking changes that are part of the same migration are also applied here.
534
534
  2. **Script execution**: If `migrate.ts` exists on disk for this migration, it is bundled and sent to the platform via the script execution API and runs as the configured machine user inside a transaction. The script is hard-required for breaking changes (`diff.requiresMigrationScript`) — deploy fails if the file is missing, unless a `--no-script` acknowledgment was recorded (see [Breaking changes without a script](#breaking-changes-without-a-script)). It is also executed when present for warning-tier diffs — see [Warnings and optional migration scripts](#warnings-and-optional-migration-scripts).
535
535
  3. **Post-migration schema**: Required constraints and the target field definitions are applied. Do not assume that removing a field clears its underlying stored JSON value.
536
- 4. **Checkpoint and cleanup**: The `sdk-migration` label is bumped to this migration's number, then removed GQL permissions and tables — including a renamed type's old table — are deleted. Advancing the checkpoint first prevents a failed checkpoint write from requiring the SDK to recreate irreversibly deleted records.
536
+ 4. **Checkpoint and cleanup**: The `sdk-migration` label is bumped to this migration's number, then removed GQL permissions and tables — including the old table left behind by a rename — are deleted. Advancing the checkpoint first prevents a failed checkpoint write from requiring the SDK to recreate irreversibly deleted records.
537
537
 
538
538
  This split is what allows existing rows to be backfilled before the database starts rejecting nulls, and what lets `migrate.ts` traverse foreign-key fields that the same migration removes.
539
539
 
@@ -551,7 +551,7 @@ On drift you'll see something like:
551
551
  Namespace: tailordb
552
552
  Remote migration: 0007
553
553
  Differences:
554
- Type 'User':
554
+ Table 'User':
555
555
  - Field 'email': required: remote=false, expected=true
556
556
  ```
557
557
 
@@ -565,7 +565,7 @@ tailor tailordb migration validate
565
565
 
566
566
  It reports issues per namespace, exits with a non-zero code when any check fails, and supports `--json` for machine-readable output.
567
567
 
568
- With `--strict`, validation additionally fails when a migration not yet applied to the remote has data-loss warnings (see [Warnings and optional migration scripts](#warnings-and-optional-migration-scripts)) but neither a `migrate.ts` nor a recorded `--no-script` acknowledgment. The failure names the affected type and field and prints the exact command to record the acknowledgment.
568
+ With `--strict`, validation additionally fails when a migration not yet applied to the remote has data-loss warnings (see [Warnings and optional migration scripts](#warnings-and-optional-migration-scripts)) but neither a `migrate.ts` nor a recorded `--no-script` acknowledgment. The failure names the affected table and field and prints the exact command to record the acknowledgment.
569
569
 
570
570
  To bypass both checks during deploy (not recommended outside of recovery scenarios):
571
571
 
@@ -595,7 +595,7 @@ Before running it:
595
595
 
596
596
  1. Apply the latest migration to every environment. The CLI verifies the connected workspace, but it cannot inspect other workspaces.
597
597
  2. Commit or otherwise preserve the existing migration history. Files after `0000`, including `migrate.ts` and `db.ts`, disappear from the working tree; Git history retains committed files.
598
- 3. Make sure local type changes have been captured with `tailor tailordb migration generate`.
598
+ 3. Make sure local table changes have been captured with `tailor tailordb migration generate`.
599
599
 
600
600
  Then re-baseline one namespace:
601
601
 
@@ -603,7 +603,7 @@ Then re-baseline one namespace:
603
603
  tailor tailordb migration rebaseline --namespace tailordb
604
604
  ```
605
605
 
606
- The command validates the migration files, verifies that replaying the latest migration exactly reproduces the local types, and checks that the connected workspace is at that latest migration with no schema drift. After confirmation, it replaces the local history with the reconstructed baseline, records a new migration history ID in both `0000/schema.json` and remote metadata, and resets the connected workspace's `sdk-migration` label to `0000`. Use `--yes` only after arranging the same operational preconditions in non-interactive automation.
606
+ The command validates the migration files, verifies that replaying the latest migration exactly reproduces the local table definitions, and checks that the connected workspace is at that latest migration with no schema drift. After confirmation, it replaces the local history with the reconstructed baseline, records a new migration history ID in both `0000/schema.json` and remote metadata, and resets the connected workspace's `sdk-migration` label to `0000`. Use `--yes` only after arranging the same operational preconditions in non-interactive automation.
607
607
 
608
608
  Commit the resulting `migrations/` change before generating any new migrations. For another environment still carrying the exact checkpoint and history ID that the new baseline replaced, the next `tailor deploy` checks whether its remote schema exactly matches the new `0000`. If it does, deploy offers to reset the checkpoint to `0000` and move the environment to the new history ID before applying any later local migrations. A markerless history is eligible only for the first rebaseline, at the exact migration recorded as replaced. Any other checkpoint or history ID is rejected without changing remote metadata, even if its schema happens to match the baseline.
609
609
 
@@ -309,11 +309,11 @@ db.string().hooks({
309
309
  });
310
310
  ```
311
311
 
312
- Field-level hooks operate on a single field and cannot access other fields. Use type-level hooks for cross-field logic.
312
+ Field-level hooks operate on a single field and cannot access other fields. Use table-level hooks for cross-field logic.
313
313
 
314
- #### Type-level Hooks
314
+ #### Table-level Hooks
315
315
 
316
- Set hooks across multiple fields using `db.table().hooks()`. The hook returns an object with the fields to override. When both field-level and type-level hooks exist for the same field, type-level hooks take priority.
316
+ Set hooks across multiple fields using `db.table().hooks()`. The hook returns an object with the fields to override. When both field-level and table-level hooks exist for the same field, table-level hooks take priority.
317
317
 
318
318
  Create hooks receive:
319
319
 
@@ -356,7 +356,7 @@ export const order = db
356
356
  });
357
357
  ```
358
358
 
359
- **Note:** `.hooks()` can only be called once on a table. Duplicate type-level calls fail at compile time and throw at runtime.
359
+ **Note:** `.hooks()` can only be called once on a table. Duplicate table-level calls fail at compile time and throw at runtime.
360
360
 
361
361
  ### Validation
362
362
 
@@ -380,7 +380,7 @@ db.string().validate(
380
380
  );
381
381
  ```
382
382
 
383
- #### Type-level Validation
383
+ #### Table-level Validation
384
384
 
385
385
  Set a validator across all fields using `db.table().validate()`. The validator receives `{ newRecord, oldRecord, invoker }` and an `issues()` callback to report errors per field:
386
386
 
@@ -415,7 +415,7 @@ For datetime/date/time fields, pass `"now"` to use the operation timestamp:
415
415
  db.datetime().default("now");
416
416
  ```
417
417
 
418
- **Note:** `.validate()` can only be called once on a table. Duplicate type-level calls fail at compile time and throw at runtime.
418
+ **Note:** `.validate()` can only be called once on a table. Duplicate table-level calls fail at compile time and throw at runtime.
419
419
 
420
420
  ### Vector Search
421
421
 
@@ -450,7 +450,7 @@ export const user = db.table("User", {
450
450
 
451
451
  ## Table Modifiers
452
452
 
453
- Table builder methods that set one type-level configuration can be called only once on the same table. Duplicate calls fail at compile time and throw at runtime. This applies to `.description()`, `.hooks()`, `.validate()`, `.features()`, `.indexes()`, `.files()`, `.permission()`, and `.gqlPermission()`.
453
+ Table builder methods that set one table-level configuration can be called only once on the same table. Duplicate calls fail at compile time and throw at runtime. This applies to `.description()`, `.hooks()`, `.validate()`, `.features()`, `.indexes()`, `.files()`, `.permission()`, and `.gqlPermission()`.
454
454
 
455
455
  Conditional assignment is still supported when only one branch calls the method:
456
456
 
package/docs/testing.md CHANGED
@@ -661,7 +661,7 @@ describe("retryFailedWorkflow resolver", () => {
661
661
 
662
662
  ### Testing Executors
663
663
 
664
- Function-kind executors expose their handler as `executor.operation.body(args)`. The shape of `args` is determined by the trigger — for example, `recordCreatedTrigger({ type: user })` produces `{ newRecord }` typed against the type's output, plus runtime fields such as `env`, `actor`, and `invoker`. GraphQL, webhook, and workflow operation kinds are declarative and don't expose a user-authored body to test.
664
+ Function-kind executors expose their handler as `executor.operation.body(args)`. The shape of `args` is determined by the trigger — for example, `recordCreatedTrigger({ type: user })` produces `{ newRecord }` typed against the table's output, plus runtime fields such as `env`, `actor`, and `invoker`. GraphQL, webhook, and workflow operation kinds are declarative and don't expose a user-authored body to test.
665
665
 
666
666
  The `executor` template extracts shared DB access into a helper (`shared.ts`) and tests the helper directly against a mocked `tailordb.Client` (same TailorDB-mocking pattern as the resolver section). Executor handlers themselves stay thin and can be tested by spying on the helper:
667
667
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/sdk",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -169,7 +169,7 @@
169
169
  "@secretlint/secretlint-rule-preset-recommend": "13.0.4",
170
170
  "@standard-schema/spec": "1.1.0",
171
171
  "@tailor-platform/function-kysely-tailordb": "0.1.3",
172
- "@toiroakr/lines-db": "0.12.0",
172
+ "@toiroakr/lines-db": "0.12.2",
173
173
  "@toiroakr/read-multiline": "0.4.1",
174
174
  "@urql/core": "6.0.3",
175
175
  "amaro": "1.1.11",
@@ -191,7 +191,7 @@
191
191
  "pgsql-ast-parser": "12.0.2",
192
192
  "pkg-types": "2.3.1",
193
193
  "politty": "0.11.6",
194
- "rolldown": "1.2.0",
194
+ "rolldown": "1.2.4",
195
195
  "semver": "7.8.5",
196
196
  "sql-highlight": "6.1.0",
197
197
  "std-env": "4.2.0",
@@ -207,7 +207,7 @@
207
207
  "@types/semver": "7.8.0",
208
208
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
209
209
  "@vitest/coverage-v8": "4.1.10",
210
- "eslint-plugin-zod": "4.9.0",
210
+ "eslint-plugin-zod": "4.9.1",
211
211
  "oxfmt": "0.61.0",
212
212
  "oxlint": "1.76.0",
213
213
  "oxlint-tsgolint": "7.0.2001",
@@ -215,7 +215,7 @@
215
215
  "tsdown": "0.22.14",
216
216
  "typescript": "6.0.3",
217
217
  "vitest": "4.1.10",
218
- "zinfer": "0.2.7",
218
+ "zinfer": "0.2.8",
219
219
  "@tailor-platform/shared": "^0.0.0",
220
220
  "@tailor-platform/tailor-proto": "^0.0.1"
221
221
  },
@@ -237,7 +237,7 @@
237
237
  },
238
238
  "engines": {
239
239
  "bun": ">=1.2.0",
240
- "node": ">=22.15.0"
240
+ "node": ">=22.18.0"
241
241
  },
242
242
  "scripts": {
243
243
  "test": "vitest",
@@ -1,3 +0,0 @@
1
- import { n as generatePluginFilesIfNeeded, r as loadApplication, t as defineApplication } from "./application-F-nl107y.mjs";
2
-
3
- export { defineApplication, generatePluginFilesIfNeeded };