@tailor-platform/sdk 2.30.0 → 2.32.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 (79) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/ai/index.d.mts +112 -0
  3. package/dist/ai/index.mjs +2 -0
  4. package/dist/ai/index.mjs.map +1 -0
  5. package/dist/aigateway-DsWDjzk4.mjs.map +1 -1
  6. package/dist/application-D0xNJbag.mjs.map +1 -1
  7. package/dist/assert-WeXvmG4j.mjs.map +1 -1
  8. package/dist/authconnection-CynFBIv8.mjs.map +1 -1
  9. package/dist/brand-C8nMKhJC.mjs.map +1 -1
  10. package/dist/cli/lib.mjs +1 -1
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/cli/main.mjs +6 -6
  13. package/dist/cli/main.mjs.map +1 -1
  14. package/dist/completion/zsh-worker.zsh +6 -2
  15. package/dist/configure/index.mjs.map +1 -1
  16. package/dist/context-D0QjfxzD.mjs.map +1 -1
  17. package/dist/crashreport-BCIy33Cz.mjs.map +1 -1
  18. package/dist/date-CgP7gMG_.mjs.map +1 -1
  19. package/dist/date-representation-Cr7W7Pn2.mjs.map +1 -1
  20. package/dist/errors-BOF68LJ5.mjs.map +1 -1
  21. package/dist/es-builtins-SfvXRVoG.mjs.map +1 -1
  22. package/dist/field-parse-C41e3UAF.mjs.map +1 -1
  23. package/dist/file-DKBOj5q4.mjs.map +1 -1
  24. package/dist/globals-BCmGX05J.mjs.map +1 -1
  25. package/dist/guards-yDXyKC9F.mjs.map +1 -1
  26. package/dist/iconv-DlFMt2gW.mjs.map +1 -1
  27. package/dist/idp-G_ojPBB5.mjs.map +1 -1
  28. package/dist/interceptor-DQg3cR_9.mjs.map +1 -1
  29. package/dist/kysely/index.mjs.map +1 -1
  30. package/dist/{kysely-type-BWMfV301.mjs → kysely-type-CBwsdx16.mjs} +6 -3
  31. package/dist/kysely-type-CBwsdx16.mjs.map +1 -0
  32. package/dist/logger-DP2BjQ93.mjs.map +1 -1
  33. package/dist/logger-RgtbhJ83.mjs.map +1 -1
  34. package/dist/manager-Bk-7vkwd.mjs.map +1 -1
  35. package/dist/migration-steps-C6F3zNdQ.mjs.map +1 -1
  36. package/dist/multiline-EyzjEwn9.mjs.map +1 -1
  37. package/dist/node-builtins-DYfhPBnz.mjs.map +1 -1
  38. package/dist/package-json-C690ceex.mjs.map +1 -1
  39. package/dist/platform-serialize-DkiTdHOt.mjs.map +1 -1
  40. package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
  41. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  42. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  43. package/dist/plugin/index.mjs.map +1 -1
  44. package/dist/{register-ts-hook-CDnN_Jx2.mjs → register-ts-hook-_dHIREtU.mjs} +127 -89
  45. package/dist/register-ts-hook-_dHIREtU.mjs.map +1 -0
  46. package/dist/registry-BijIDRVA.mjs.map +1 -1
  47. package/dist/repl-editor-DGJBfIt1.mjs.map +1 -1
  48. package/dist/schema-B6hJQWp6.mjs.map +1 -1
  49. package/dist/secret-file-C9wp_FCX.mjs.map +1 -1
  50. package/dist/secretmanager-vHQoXdQz.mjs.map +1 -1
  51. package/dist/seed/index.mjs.map +1 -1
  52. package/dist/seed-Cn-3JApy.mjs.map +1 -1
  53. package/dist/service-9SBPNpK8.mjs.map +1 -1
  54. package/dist/service-9rwi_I4d.mjs.map +1 -1
  55. package/dist/service_pb-Bbu2QSIr.mjs.map +1 -1
  56. package/dist/telemetry-DfT2UfI4.mjs +2 -0
  57. package/dist/telemetry-DfT2UfI4.mjs.map +1 -0
  58. package/dist/{telemetry-DpkFGBmu.mjs → telemetry-nqOtDWNO.mjs} +1 -1
  59. package/dist/type-source--ZNcV8RJ.mjs.map +1 -1
  60. package/dist/user-agent-vdHYF3QL.mjs.map +1 -1
  61. package/dist/utils/test/index.mjs.map +1 -1
  62. package/dist/vitest/environment.mjs.map +1 -1
  63. package/dist/vitest/index.mjs.map +1 -1
  64. package/dist/vitest/setup.mjs.map +1 -1
  65. package/dist/wait-point-invoker-eiP-IIux.mjs.map +1 -1
  66. package/dist/wait-point-registry-B-ESkTZX.mjs.map +1 -1
  67. package/dist/workflow-Cs9ISw6j.mjs.map +1 -1
  68. package/dist/workspace_resource_pb-CaoM2npP.mjs.map +1 -1
  69. package/docs/ai.md +92 -0
  70. package/docs/cli/tailordb.md +17 -15
  71. package/docs/cli/workspace.md +1 -1
  72. package/docs/github-actions.md +98 -11
  73. package/docs/services/tailordb/migration.md +45 -31
  74. package/docs/testing.md +2 -1
  75. package/package.json +16 -9
  76. package/dist/kysely-type-BWMfV301.mjs.map +0 -1
  77. package/dist/register-ts-hook-CDnN_Jx2.mjs.map +0 -1
  78. package/dist/telemetry-Xk7J829P.mjs +0 -2
  79. package/dist/telemetry-Xk7J829P.mjs.map +0 -1
package/docs/ai.md ADDED
@@ -0,0 +1,92 @@
1
+ ---
2
+ description: "Make choice, boolean, and ordered score decisions from application state through AI Gateway with the System One client from @tailor-platform/sdk/ai."
3
+ ---
4
+
5
+ # System One
6
+
7
+ System One makes semantic decisions from application state, a question, and a fixed answer space. It returns the selected answer and its probability without requiring prompt construction or structured-output parsing.
8
+
9
+ Create a client with the URL the platform assigns to your deployed [AI Gateway](services/aigateway.md). Inside a Tailor Function, `aigateway.get()` returns it as `url`. Omit `token`; the platform authenticates the request:
10
+
11
+ ```typescript
12
+ import { createSystemOne } from "@tailor-platform/sdk/ai";
13
+ import { aigateway } from "@tailor-platform/sdk/runtime";
14
+
15
+ const { url } = await aigateway.get("my-aigateway");
16
+ const systemOne = createSystemOne({ baseURL: url });
17
+ ```
18
+
19
+ External clients pass the same URL, which `tailor show` lists, directly with an access token issued by the AI Gateway's auth namespace:
20
+
21
+ ```typescript
22
+ import { createSystemOne } from "@tailor-platform/sdk/ai";
23
+
24
+ const systemOne = createSystemOne({
25
+ baseURL: "https://my-aigateway-{WORKSPACE_HASH}.ai.erp.dev",
26
+ token: accessToken,
27
+ });
28
+ ```
29
+
30
+ ## Choice decisions
31
+
32
+ Use `choice()` to select one value from an answer space. Literal types are inferred without `as const`:
33
+
34
+ ```typescript
35
+ const result = await systemOne.choice({
36
+ state: { ticket, customer },
37
+ question: "Which department should handle this ticket?",
38
+ choices: ["billing", "sales", "support"],
39
+ });
40
+
41
+ // result.value: "billing" | "sales" | "support"
42
+ // result.probabilities: Record<"billing" | "sales" | "support", number>
43
+ ```
44
+
45
+ ## Boolean decisions
46
+
47
+ Use `boolean()` for a binary decision. `value` is `true` when the probability of "yes" is at least `0.5`, and `probability` is the probability of the selected answer:
48
+
49
+ ```typescript
50
+ const result = await systemOne.boolean({
51
+ state: invoice,
52
+ question: "Is this invoice a duplicate?",
53
+ });
54
+
55
+ if (result.value && result.probability > 0.95) {
56
+ // Apply the appropriate business rule.
57
+ }
58
+ ```
59
+
60
+ ## Ordered scores
61
+
62
+ Use `score()` when the answer space has a meaningful order. Levels are ordered from lowest to highest, and `value` is the level with the highest probability:
63
+
64
+ ```typescript
65
+ const result = await systemOne.score({
66
+ state: transaction,
67
+ question: "Assess the risk of this transaction.",
68
+ levels: ["low", "medium", "high"],
69
+ });
70
+ ```
71
+
72
+ ## Models
73
+
74
+ Decisions use `jev-1.13.0` unless a call passes `model`. `jev-latest` always points to the newest model, so its probabilities can shift when a new model is released. Use it only when your logic does not compare probabilities against fixed thresholds:
75
+
76
+ ```typescript
77
+ const result = await systemOne.boolean({
78
+ state: invoice,
79
+ question: "Is this invoice a duplicate?",
80
+ model: "jev-latest",
81
+ });
82
+ ```
83
+
84
+ ## Errors
85
+
86
+ Failures throw `SystemOneError` with a provider-independent `code`. Applications can handle stable codes such as `UNAUTHORIZED`, `RATE_LIMITED`, `PROVIDER_TIMEOUT`, and `PROVIDER_UNAVAILABLE` without depending on a model provider.
87
+
88
+ A decision waits until AI Gateway responds. To fail with `PROVIDER_TIMEOUT` instead, pass `timeoutMs`:
89
+
90
+ ```typescript
91
+ const systemOne = createSystemOne({ baseURL: url, timeoutMs: 10_000 });
92
+ ```
@@ -328,26 +328,28 @@ tailor tailordb migration test [options]
328
328
 
329
329
  **Options**
330
330
 
331
- | Option | Alias | Description | Required | Default | Env |
332
- | --------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------- | -------- | -------------------- | ------------------------------ |
333
- | `--workspace-id <WORKSPACE_ID>` | `-w` | Workspace ID | No | - | `TAILOR_PLATFORM_WORKSPACE_ID` |
334
- | `--profile <PROFILE>` | `-p` | Workspace profile | No | - | `TAILOR_PLATFORM_PROFILE` |
335
- | `--config <CONFIG>` | `-c` | Path to Tailor config file | No | `"tailor.config.ts"` | `TAILOR_CONFIG_PATH` |
336
- | `--yes` | `-y` | Acknowledge that a designated target workspace may be overwritten | No | `false` | - |
337
- | `--data <DATA>` | - | Data source for the migration test (seed or clone) | No | `"seed"` | - |
338
- | `--target-workspace-id <TARGET_WORKSPACE_ID>` | - | Existing throwaway workspace to retain after the test (requires --yes) | No | - | - |
339
- | `--keep` | - | Keep the automatically created workspace after the test | No | `false` | - |
340
- | `--assert <ASSERT>` | - | Path to a TypeScript assertion script to run after migrations | No | - | - |
341
- | `--assert-namespace <ASSERT_NAMESPACE>` | - | TailorDB namespace exposed to the assertion script | No | - | - |
342
- | `--keep-ttl <KEEP_TTL>` | - | Record an expiry on the kept workspace, e.g. 24h or 7d, so workspace prune --expired can delete it (requires --keep) | No | - | - |
343
- | `--clone-timeout <CLONE_TIMEOUT>` | - | How long to wait for the clone to finish with --data clone, e.g. 30s or 10m (default: 30m) | No | - | - |
344
- | `--machine-user <MACHINE_USER>` | - | Machine user for seed and assertion script execution | No | - | - |
331
+ | Option | Alias | Description | Required | Default | Env |
332
+ | --------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------- | --------------------------------- |
333
+ | `--workspace-id <WORKSPACE_ID>` | `-w` | Workspace ID | No | - | `TAILOR_PLATFORM_WORKSPACE_ID` |
334
+ | `--profile <PROFILE>` | `-p` | Workspace profile | No | - | `TAILOR_PLATFORM_PROFILE` |
335
+ | `--config <CONFIG>` | `-c` | Path to Tailor config file | No | `"tailor.config.ts"` | `TAILOR_CONFIG_PATH` |
336
+ | `--yes` | `-y` | Acknowledge that a designated target workspace may be overwritten | No | `false` | - |
337
+ | `--data <DATA>` | - | Data source for the migration test (seed or clone) | No | `"seed"` | - |
338
+ | `--target-workspace-id <TARGET_WORKSPACE_ID>` | - | Existing throwaway workspace to retain after the test (requires --yes) | No | - | - |
339
+ | `--keep` | - | Keep the automatically created workspace after the test | No | `false` | - |
340
+ | `--organization-id <ORGANIZATION_ID>` | - | Organization to create the temporary workspace in, at its root unless --folder-id is given (default: the source workspace's organization and folder). With --target-workspace-id, must match the target's organization | No | - | `TAILOR_PLATFORM_ORGANIZATION_ID` |
341
+ | `--folder-id <FOLDER_ID>` | - | Folder to create the temporary workspace in (default: the source workspace's folder). Requires --organization-id when the source workspace is not in an organization. With --target-workspace-id, must match the target's folder | No | - | `TAILOR_PLATFORM_FOLDER_ID` |
342
+ | `--assert <ASSERT>` | - | Path to a TypeScript assertion script to run after migrations | No | - | - |
343
+ | `--assert-namespace <ASSERT_NAMESPACE>` | - | TailorDB namespace exposed to the assertion script | No | - | - |
344
+ | `--keep-ttl <KEEP_TTL>` | - | Record an expiry on the kept workspace, e.g. 24h or 7d, so workspace prune --expired can delete it (requires --keep) | No | - | - |
345
+ | `--clone-timeout <CLONE_TIMEOUT>` | - | How long to wait for the clone to finish with --data clone, e.g. 30s or 10m (default: 30m) | No | - | - |
346
+ | `--machine-user <MACHINE_USER>` | - | Machine user for seed and assertion script execution | No | - | - |
345
347
 
346
348
  See [Global Options](../cli-reference.md#global-options) for options available to all commands.
347
349
 
348
350
  **Notes**
349
351
 
350
- The source workspace is read-only. Without --target-workspace-id, the command creates a workspace in the source workspace's region and deletes it after success or failure; pass --keep to retain it for inspection. A designated target is retained and requires --yes. Clone mode copies TailorDB records only; it does not copy IdP users or file blobs. With --keep-ttl, the kept workspace records an expiry; it is not deleted automatically, but workspace prune --expired removes it once the expiry has passed.
352
+ The source workspace is read-only. Without --target-workspace-id, the command creates a workspace in the source workspace's region and deletes it after success or failure; pass --keep to retain it for inspection. Right after creating the workspace it records a 24h expiry (unless --keep is given without --keep-ttl), so if the run is interrupted before the command can delete the workspace, workspace prune --expired removes it later. A designated target is retained and requires --yes. Clone mode copies TailorDB records only; it does not copy IdP users or file blobs. With --keep-ttl, the kept workspace records an expiry; it is not deleted automatically, but workspace prune --expired removes it once the expiry has passed. Organization and folder IDs loaded by --env-file are used the same way as the options; command-line options take precedence, and empty env-file values are ignored.
351
353
 
352
354
  #### tailordb migration validate
353
355
 
@@ -107,7 +107,7 @@ tailor workspace create [options]
107
107
  | Option | Alias | Description | Required | Default | Env |
108
108
  | ------------------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | --------------------------------- |
109
109
  | `--name <NAME>` | `-n` | Workspace name | Yes | - | - |
110
- | `--region <REGION>` | `-r` | Workspace region (asia-northeast, us-west) | Yes | - | - |
110
+ | `--region <REGION>` | `-r` | Workspace region (asia-northeast, us-west, europe-west) | Yes | - | - |
111
111
  | `--delete-protection` | `-d` | Enable delete protection | No | `false` | - |
112
112
  | `--organization-id <ORGANIZATION_ID>` | `-o` | Organization ID to workspace associate with | No | - | `TAILOR_PLATFORM_ORGANIZATION_ID` |
113
113
  | `--folder-id <FOLDER_ID>` | `-f` | Folder ID to workspace associate with | No | - | `TAILOR_PLATFORM_FOLDER_ID` |
@@ -117,6 +117,92 @@ all-added or all-removed viewer artifacts. Re-run `setup ci branch` after adding
117
117
  removing TailorDB namespaces. `setup check` reports drift when the recorded ERD
118
118
  preview namespaces no longer match the current config.
119
119
 
120
+ #### Migration test on a labeled pull request
121
+
122
+ Pass `--migration-test` on a branch target to add a job that runs
123
+ [`tailor tailordb migration test`](./cli/tailordb.md) against a pull request on
124
+ demand. It is opt-in and requires a config with TailorDB migrations.
125
+
126
+ ```bash
127
+ tailor setup ci branch --name my-app-stg --migration-test
128
+ ```
129
+
130
+ The job runs only when someone adds the `tailor:migration-test` label to a pull
131
+ request from the same repository (change the label with
132
+ `--migration-test-label`). It runs the pending migrations in a temporary
133
+ workspace created next to the source workspace, writes the `--json` result to
134
+ the job summary, and removes the label as its last step, whether the test
135
+ succeeded, failed, or was cancelled while running. To run it again, add the label
136
+ again. If a run is cancelled before the job starts (for example, a reviewer
137
+ rejects the environment's approval), the label stays on the pull request: remove
138
+ it and add it again.
139
+ Anyone who can label pull requests in your repository can start the job, and
140
+ each run creates and deletes a workspace. Runs for one pull request never
141
+ cancel each other.
142
+
143
+ The generated workflow also lists `labeled` in its `pull_request` types. The
144
+ plan and ERD preview jobs skip label events, so adding an unrelated label does
145
+ not re-run them.
146
+
147
+ Runs started by a label report their result job as `tailor-result (<name>) [label]`
148
+ instead of `tailor-result (<name>)`. A label event therefore never replaces the
149
+ result of the check you require for branch protection. The migration test result
150
+ appears only under the `[label]` name, so it does not block merging through the
151
+ check you require.
152
+
153
+ The job clones the source workspace's TailorDB records and passes
154
+ `--clone-timeout 30m`, which bounds only the wait for the data clone. If the clone takes longer, the command
155
+ fails with the clone's operation ID in the error and removes its temporary
156
+ workspace, while the clone itself keeps running on the platform.
157
+
158
+ The migration test step is limited to 50 minutes and the job to 60, so the label
159
+ is still removed when the step times out. A step that is stopped at its limit
160
+ (or a job cancelled at the job limit) gives the command no chance to clean up:
161
+ delete the leftover temporary workspace with `tailor workspace delete`.
162
+
163
+ ##### Choosing the source workspace
164
+
165
+ `migration test` reads the source workspace (it is not modified), builds a
166
+ temporary workspace from it in the same organization and folder, and deletes
167
+ that workspace afterwards. Because the machine user creates and deletes that
168
+ workspace, it needs an editor or admin role on the organization or folder that
169
+ holds the source workspace; a viewer role is not enough. The clone also needs the
170
+ same role on the source side. The clone copies TailorDB records only: IdP users
171
+ and file blobs are not copied.
172
+
173
+ Two setups are supported:
174
+
175
+ - **Source is the plan/deploy workspace (default).** The job uses the same GitHub
176
+ Environment as the plan and deploy jobs, so it reads the same
177
+ `TAILOR_PLATFORM_WORKSPACE_ID` variable and machine-user secrets. Nothing
178
+ extra to configure. If that workspace holds too little data to be a useful
179
+ test, point the job at a workspace that does with the next setup.
180
+ - **Source is a different workspace, such as production.** Pass
181
+ `--migration-test-environment <env>`. The job then uses that GitHub
182
+ Environment and reads the source workspace from its
183
+ `TAILOR_PLATFORM_MIGRATION_TEST_SOURCE_WORKSPACE_ID` variable, together with the
184
+ machine-user secrets set on that same environment. Give this environment a
185
+ machine user whose role you are willing to hold on the source workspace's
186
+ organization or folder, since it needs the editor-or-admin role described
187
+ above. Pending migrations run from the source workspace's migration number up
188
+ to the latest, so a source that is behind the deploy target replays more
189
+ migrations, which is closer to what a production deploy will do.
190
+
191
+ A job can declare only one GitHub Environment, and a repository secret or
192
+ variable is read from that environment, which is why a different source needs its
193
+ own environment. `tailor setup ci env` prints what each environment needs,
194
+ including the dedicated one. Keep the dedicated environment separate from your
195
+ production deploy environment: a pull request job runs on the pull request merge
196
+ ref (`refs/pull/<number>/merge`), so if an environment restricts which branches
197
+ may deploy or requires reviewers, check how that affects the pull request job
198
+ before pointing the migration test at it. A required-reviewers rule makes the
199
+ job wait for approval before it can read the environment's secrets. The job runs
200
+ the pull request's own code with that environment's credentials, so add one when
201
+ the source workspace holds production data.
202
+
203
+ Fork pull requests cannot read secrets, so the job is skipped for them.
204
+ `--migration-test` is not available together with several `--dir` values.
205
+
120
206
  ### Tag target (recommended for production)
121
207
 
122
208
  The tag target fires when a tag matching `--tag-pattern` (default `v*`) is
@@ -501,17 +587,18 @@ tailor setup ci env --check # check what is registered in GitHu
501
587
 
502
588
  The list follows what each generated workflow actually reads:
503
589
 
504
- | Name | Kind | Targets | Required | Where the value comes from |
505
- | -------------------------------------------- | -------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
506
- | `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 |
507
- | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` | secret | all | yes | Client secret of the same platform machine user |
508
- | `TAILOR_PLATFORM_WORKSPACE_ID` | variable | branch, tag | yes | `id` printed by `tailor workspace create`, or listed by `tailor workspace list` |
509
- | `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` |
510
- | `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 |
511
- | `TAILOR_PLATFORM_FAIL_ON_DRIFT` | variable | all | no | `true` to fail the drift check when it finds drift |
512
- | `TAILOR_SLACK_BOT_TOKEN` | secret | branch, tag | no | Bot User OAuth Token (`xoxb-...`) of a Slack app with the `chat:write` scope |
513
- | `TAILOR_SLACK_CHANNEL_ID` | variable | branch, tag | no | Channel ID (`C...`) from the channel details in Slack; invite the bot to the channel |
514
- | `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 |
590
+ | Name | Kind | Targets | Required | Where the value comes from |
591
+ | ---------------------------------------------------- | -------- | ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
592
+ | `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 |
593
+ | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` | secret | all | yes | Client secret of the same platform machine user |
594
+ | `TAILOR_PLATFORM_WORKSPACE_ID` | variable | branch, tag | yes | `id` printed by `tailor workspace create`, or listed by `tailor workspace list` |
595
+ | `TAILOR_PLATFORM_MIGRATION_TEST_SOURCE_WORKSPACE_ID` | variable | branch (migration test environment) | yes | ID of the workspace the migration test starts from, set on the environment passed to `--migration-test-environment`; it is only read to build the temporary workspace. Needs the machine user's editor or admin role on that workspace's organization or folder |
596
+ | `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` |
597
+ | `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 |
598
+ | `TAILOR_PLATFORM_FAIL_ON_DRIFT` | variable | all | no | `true` to fail the drift check when it finds drift |
599
+ | `TAILOR_SLACK_BOT_TOKEN` | secret | branch, tag | no | Bot User OAuth Token (`xoxb-...`) of a Slack app with the `chat:write` scope |
600
+ | `TAILOR_SLACK_CHANNEL_ID` | variable | branch, tag | no | Channel ID (`C...`) from the channel details in Slack; invite the bot to the channel |
601
+ | `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 |
515
602
 
516
603
  See [Account management](https://docs.tailor.tech/administration/account-management)
517
604
  for how organizations, folders, workspaces, and machine users relate.
@@ -173,9 +173,17 @@ During deploy, the pre-migration phase keeps the old field and adds the new fiel
173
173
 
174
174
  If you decline the prompt (or confirm the removal with `--drop`), the change stays a plain removal + addition with the usual data-loss warning.
175
175
 
176
+ #### Changing nested member constraints
177
+
178
+ Adding a required member, making an existing member required, or removing enum values inside a nested field requires a migration script, including inside arrays of objects. During Pre-migration, added or newly required members are optional and removed enum values remain allowed. Post-migration enforces the target constraints after your script fills missing values or replaces removed enum values. A newly added optional object may keep required children: those children are required only when the object is present.
179
+
180
+ The generated script includes a `TODO` for each nested constraint change. Replace these with updates to the containing nested field, preserving its other members and handling objects inside arrays. A confirmed member rename generates its copy step alongside these TODOs.
181
+
182
+ Changing a nested member's type, array-ness, decimal scale, or foreign key target is unsupported in a single migration. Use separate migrations to add a member with the target shape, copy or transform values in a custom script, and then remove the old member.
183
+
176
184
  #### Renaming a member inside a nested field
177
185
 
178
- Members inside a **nested field** (`db.object(...)`) are detected the same way: when `migration generate` finds a member removed from a nested field and a compatible member added under the same parent, it asks whether the member was renamed. Two members qualify only when copying the value preserves it exactly, because nested member constraints other than the new member's requiredness and unique constraint are not relaxed: the type, array-ness, requiredness, foreign key target, decimal scale, hooks, and validations must match (index, unique, and vector may differ, as for a top-level rename), enum values may be added but not removed, an object-typed member must keep the same members recursively, and serial members never qualify.
186
+ Members inside a **nested field** (`db.object(...)`) are detected the same way: when `migration generate` finds a member removed from a nested field and a compatible member added under the same parent, it asks whether the member was renamed. Two members qualify only when copying the value preserves it exactly, because a rename copies existing values without transforming them: the type, array-ness, requiredness, foreign key target, decimal scale, hooks, and validations must match (index, unique, and vector may differ, as for a top-level rename), enum values may be added but not removed, an object-typed member must keep the same members recursively, and serial members never qualify.
179
187
 
180
188
  ```
181
189
  ? User.address.zip was removed and zipCode was added with a compatible type. Was it renamed to zipCode? (Y/n)
@@ -199,7 +207,7 @@ Warning: data loss possible:
199
207
  - User.address.zip: Nested member removed (existing values will no longer be accessible through the schema). Possibly renamed to zipCode: confirm it with --rename "User.address.zip:<newName>" to scaffold a copy script, or keep the removal and copy the values yourself
200
208
  ```
201
209
 
202
- The pre-migration phase keeps a removed member on the nested field until the script finishes, exactly like a removed top-level field, so a custom script can still read it. Nested fields reach the script as objects. Renaming a nested member cannot be combined in one migration with renaming its table or the nested field itself, and an object-typed member cannot be renamed in the same migration as one of its own members; split such changes into separate migrations (rename the object first, then its member).
210
+ The pre-migration phase keeps a removed member on the nested field until the script finishes, exactly like a removed top-level field, so a custom script can still read it. Nested fields reach the script as objects. Renaming a nested member requires every ancestor, including the top-level nested field, to keep its nested type and array-ness. It cannot be combined in one migration with renaming its table or the nested field itself, and an object-typed member cannot be renamed in the same migration as one of its own members; split such changes into separate migrations (rename the object first, then its member).
203
211
 
204
212
  ### Renaming a table
205
213
 
@@ -465,33 +473,35 @@ Tables that the failed migration removes are deleted with their data under `--co
465
473
 
466
474
  ## Supported Schema Changes
467
475
 
468
- | Change Type | Breaking? | Migration Script? | Notes |
469
- | --------------------------------- | --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
470
- | Add optional field | No | No | Schema change only |
471
- | Add required field | Yes | Yes | Script populates default values |
472
- | 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. |
473
- | 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. |
474
- | Remove nested member | No | Optional | Warning tier — see [Renaming a member inside a nested field](#renaming-a-member-inside-a-nested-field). The member stays readable from `migrate.ts` during Pre-migration. |
475
- | Rename nested member | Yes | Yes | Confirmed interactively at generate time or via `--rename "Table.field.oldMember:newMember"`. Auto-generated script rewrites each row's nested value; the old member stays readable and the new member is optional during Pre-migration. |
476
- | Change optional → required | Yes | Yes | Script sets defaults for null values |
477
- | Change required → optional | No | No | Schema change only |
478
- | Add index (non-unique) | No | No | Schema change only |
479
- | Add unique index | Yes | Yes | Script must resolve duplicate value combinations across the index fields |
480
- | Change unique index fields | Yes | Yes | Treated like adding a new unique constraint over the new field set |
481
- | Remove index | No | No | Schema change only (removing the unique constraint from an index is also non-breaking) |
482
- | Add unique constraint | Yes | Yes | Script must resolve duplicate values |
483
- | Remove unique constraint | No | No | Schema change only |
484
- | 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. |
485
- | Add enum value | No | No | Schema change only |
486
- | Remove enum value | Yes | Yes | Script migrates records with removed values |
487
- | Add table | No | No | Schema change only |
488
- | 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. |
489
- | 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. |
490
- | Change foreign key target table | Yes | Yes | Script updates references to the new target |
491
- | 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 |
492
- | 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. |
493
- | Change array → single value | - | - | **Not supported** — see [Converting a field type](#converting-a-field-type) |
494
- | Change single value → array | Yes | Yes | Two migrations, generated together after you confirm — see [Converting a field type](#converting-a-field-type). Each stored value becomes a one-element array, so the conversion needs no edits. |
476
+ | Change Type | Breaking? | Migration Script? | Notes |
477
+ | ----------------------------------------------------------------------------- | --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
478
+ | Add optional field | No | No | Schema change only |
479
+ | Add required field | Yes | Yes | Script populates default values |
480
+ | 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. |
481
+ | 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. |
482
+ | Remove nested member | No | Optional | Warning tier — see [Renaming a member inside a nested field](#renaming-a-member-inside-a-nested-field). The member stays readable from `migrate.ts` during Pre-migration. |
483
+ | Add required nested member / make member required / remove nested enum values | Yes | Yes | Pre-migration relaxes the member constraints. Complete the generated TODOs to transform the nested values before Post-migration. |
484
+ | Change nested member type / array-ness / decimal scale / foreign key target | Rejected | — | Add a new member, transform and copy values, then remove the old member in separate migrations. |
485
+ | Rename nested member | Yes | Yes | Confirmed interactively at generate time or via `--rename "Table.field.oldMember:newMember"`. Auto-generated script rewrites each row's nested value; the old member stays readable and the new member is optional during Pre-migration. |
486
+ | Change optional → required | Yes | Yes | Script sets defaults for null values |
487
+ | Change required → optional | No | No | Schema change only |
488
+ | Add index (non-unique) | No | No | Schema change only |
489
+ | Add unique index | Yes | Yes | Script must resolve duplicate value combinations across the index fields |
490
+ | Change unique index fields | Yes | Yes | Treated like adding a new unique constraint over the new field set |
491
+ | Remove index | No | No | Schema change only (removing the unique constraint from an index is also non-breaking) |
492
+ | Add unique constraint | Yes | Yes | Script must resolve duplicate values |
493
+ | Remove unique constraint | No | No | Schema change only |
494
+ | 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. |
495
+ | Add enum value | No | No | Schema change only |
496
+ | Remove enum value | Yes | Yes | Script migrates records with removed values |
497
+ | Add table | No | No | Schema change only |
498
+ | 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. |
499
+ | 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. |
500
+ | Change foreign key target table | Yes | Yes | Script updates references to the new target |
501
+ | 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 |
502
+ | 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. |
503
+ | Change array → single value | - | - | **Not supported** — see [Converting a field type](#converting-a-field-type) |
504
+ | Change single value → array | Yes | Yes | Two migrations, generated together after you confirm — see [Converting a field type](#converting-a-field-type). Each stored value becomes a one-element array, so the conversion needs no edits. |
495
505
 
496
506
  ### Field type changes
497
507
 
@@ -611,13 +621,15 @@ Some changes are still rejected and need a temporary field you add yourself —
611
621
  The command performs the following sequence:
612
622
 
613
623
  1. Reads each migration-enabled namespace's `sdk-migration` checkpoint from the source workspace and reconstructs that exact snapshot from local migration history. A deployed namespace that has no checkpoint is treated as migration 0, as `tailor deploy` does, but only when its schema matches the `0000` snapshot; otherwise the command fails and lists the differences. A namespace that was never deployed also fails.
614
- 2. Creates a temporary workspace in the same region, organization, and folder as the source, unless `--target-workspace-id` names an existing throwaway workspace.
624
+ 2. Creates a temporary workspace in the same region as the source, unless `--target-workspace-id` names an existing throwaway workspace. By default it is created in the source's organization and folder. `--organization-id` places it at the root of that organization, and `--folder-id` places it in that folder (together with `--organization-id` when the folder is in another organization or the source is not in an organization). With `--target-workspace-id`, the given values must match where that workspace already is.
615
625
  3. Deploys the checkpoint snapshots and writes their checkpoint labels.
616
626
  4. Loads fixture data or clones source records.
617
627
  5. Runs the normal deployment pipeline, including every pending pre-migration, `migrate.ts`, and post-migration phase.
618
628
  6. Optionally runs an assertion script against the migrated data.
619
629
  7. Deletes an automatically-created workspace after success or failure.
620
630
 
631
+ Right after creating an automatically-created workspace (step 2), the command records a 24-hour expiry on it. If the run is interrupted before step 7 — for example, a CI job is cancelled or reaches its time limit — the command cannot delete the workspace, so it stays. Once the 24 hours have passed, `tailor workspace prune --expired` (with a location option such as `--organization-root`, `--folder-id`, or `--personal`) deletes it. A workspace given with `--target-workspace-id` is not given an expiry. If recording the expiry fails, the command warns and continues the test.
632
+
621
633
  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.
622
634
 
623
635
  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.
@@ -667,6 +679,8 @@ To inspect the result after a run, pass `--keep` so the automatically created wo
667
679
  tailor tailordb migration test --keep
668
680
  ```
669
681
 
682
+ A kept workspace records no expiry unless you also pass `--keep-ttl <age>` (for example `--keep-ttl 7d`), so `tailor workspace prune --expired` does not delete it.
683
+
670
684
  Alternatively, provide an empty designated throwaway workspace. This mode never deletes the target and requires explicit acknowledgment:
671
685
 
672
686
  ```bash
@@ -685,7 +699,7 @@ When you run `tailor deploy`, the SDK detects pending migrations (anything past
685
699
 
686
700
  For each pending migration:
687
701
 
688
- 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); members removed from a nested field are kept the same way, and the new member of a confirmed nested rename is added as optional and non-unique. 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.
702
+ 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); members removed from a nested field are kept the same way, and the new member of a confirmed nested rename is added as optional and non-unique. Added or newly required nested members are optional and removed nested enum values remain allowed until Post-migration. 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.
689
703
  2. **Script execution**: If `migrate.ts` exists on disk for this migration, it is bundled and runs on the platform as the configured machine user inside a transaction — or, for a script that exports [`steps`](#splitting-a-migration-into-steps), one transaction per step. 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).
690
704
  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.
691
705
  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.
package/docs/testing.md CHANGED
@@ -209,11 +209,12 @@ test("upserts against real rows", async () => {
209
209
  });
210
210
  ```
211
211
 
212
- The generated columns follow the Kysely types, not TailorDB's storage: `text` for string and enum fields, `timestamptz` for datetime, `date` and `time` for date and time, `numeric` for decimal, rounded to the configured scale and read back with exactly that many fractional digits, `jsonb` for nested objects (and arrays of them), Postgres arrays for other array fields. `id` is a generated `uuid` primary key, `.unique()` fields and unique `.indexes()` are enforced, so `ON CONFLICT` upserts behave, and `.default()` values become column defaults (`"now"` becomes the current time). `.serial()` fields are assigned by the database from the configured `start`, `maxValue`, and format. Relations are not enforced.
212
+ The generated columns follow the Kysely types, not TailorDB's storage: `text` for string and enum fields, `timestamptz` for datetime, `date` and `time` for date and time, `numeric` for decimal, rounded to the configured scale and read back with exactly that many fractional digits, `jsonb` for nested objects (and arrays of them), Postgres arrays for other array fields. `id` is a generated `uuid` primary key, `.unique()` fields and unique `.indexes()` are enforced, so `ON CONFLICT` upserts behave, and `.default()` values become column defaults (`"now"` becomes the current time). `.serial()` fields are assigned by the database from the configured `start`, `maxValue`, and format. Relations are enforced as the platform does: a row that points at a parent that does not exist is rejected with an error containing `violates foreign key constraint`, so create parent rows before their children. This includes relations on array fields. A relation declared on a field inside a nested object is not enforced, as on the platform.
213
213
 
214
214
  What the script cannot reproduce:
215
215
 
216
216
  - Hooks, validations, and permissions do not run. A required field whose value only its own field-level create hook supplies is created nullable, so inserts that omit it succeed; give it a `.default()` if the test reads it back. A field filled by a table-level hook stays `NOT NULL`, as its Kysely type still requires it on insert.
217
+ - Relations on array fields, and relations from a field whose type differs from the field it points at (such as a `string` field related to `id`), are checked only when a row is written. Deleting a parent that such a row still points at is rejected by the platform but not by PGlite, and changing the parent's `id` is not checked either, so a test must not expect PGlite to reject them.
217
218
  - Serial formats are reproduced for a single `%d`, `%x`, or `%X` specifier with an optional zero-padded width; an octal `%o` format fails generation with an error naming the field.
218
219
  - `gapless` is not reproduced: the numbers come from a sequence, so an insert that fails still uses up its number.
219
220
  - A date or datetime inside a nested object reads back as a string from `jsonb`, not a `Date`, or a `Temporal.PlainDate` or `Temporal.Instant` under `kyselyTypePlugin({ temporal: true })`, although the platform returns those types.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/sdk",
3
- "version": "2.30.0",
3
+ "version": "2.32.0",
4
4
  "description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -82,6 +82,11 @@
82
82
  "import": "./dist/seed/index.mjs",
83
83
  "default": "./dist/seed/index.mjs"
84
84
  },
85
+ "./ai": {
86
+ "types": "./dist/ai/index.d.mts",
87
+ "import": "./dist/ai/index.mjs",
88
+ "default": "./dist/ai/index.mjs"
89
+ },
85
90
  "./vitest": {
86
91
  "types": "./dist/vitest/index.d.mts",
87
92
  "import": "./dist/vitest/index.mjs",
@@ -160,9 +165,10 @@
160
165
  "@jridgewell/trace-mapping": "0.3.31",
161
166
  "@napi-rs/keyring": "2.1.1",
162
167
  "@opentelemetry/api": "1.9.1",
168
+ "@opentelemetry/context-async-hooks": "2.12.0",
163
169
  "@opentelemetry/exporter-trace-otlp-proto": "0.223.0",
164
170
  "@opentelemetry/resources": "2.12.0",
165
- "@opentelemetry/sdk-trace-node": "2.12.0",
171
+ "@opentelemetry/sdk-trace": "2.12.0",
166
172
  "@opentelemetry/semantic-conventions": "1.43.0",
167
173
  "@oxc-project/types": "0.154.0",
168
174
  "@politty/zod": "0.5.2",
@@ -182,7 +188,7 @@
182
188
  "globals": "17.13.0",
183
189
  "graphql": "17.0.2",
184
190
  "inflection": "3.0.2",
185
- "kysely": "0.29.6",
191
+ "kysely": "0.29.7",
186
192
  "lines-db": "0.18.0",
187
193
  "mime-types": "3.0.2",
188
194
  "open": "11.0.4",
@@ -191,7 +197,7 @@
191
197
  "pathe": "2.0.3",
192
198
  "pgsql-ast-parser": "12.0.2",
193
199
  "pkg-types": "2.3.3",
194
- "rolldown": "1.2.13",
200
+ "rolldown": "1.2.14",
195
201
  "semver": "7.8.5",
196
202
  "sql-highlight": "6.1.0",
197
203
  "std-env": "4.3.0",
@@ -203,21 +209,22 @@
203
209
  "zod": "4.6.7"
204
210
  },
205
211
  "devDependencies": {
212
+ "@ark/attest": "0.57.0",
206
213
  "@electric-sql/pglite": "0.5.10",
207
- "@opentelemetry/sdk-trace-base": "2.12.0",
208
214
  "@tailor-platform/shared": "^0.0.0",
209
215
  "@tailor-platform/tailor-proto": "^0.0.1",
210
216
  "@types/mime-types": "3.0.1",
211
217
  "@types/node": "24.19.2",
212
218
  "@types/semver": "7.8.0",
213
- "@typescript/native-preview": "7.0.0-dev.20260707.2",
214
219
  "@vitest/coverage-v8": "5.0.5",
215
- "eslint-plugin-zod": "5.0.0",
220
+ "eslint-plugin-zod": "5.2.0",
216
221
  "oxfmt": "0.73.0",
217
222
  "oxlint": "1.88.0",
218
223
  "oxlint-tsgolint": "7.0.2004",
219
224
  "sonda": "0.14.0",
220
225
  "tsdown": "0.23.1",
226
+ "tsrs": "0.16.0-ts7.1.0-dev.20260929",
227
+ "tsx": "4.23.15",
221
228
  "typescript": "6.0.3",
222
229
  "vitest": "5.0.5",
223
230
  "zinfer": "0.4.7"
@@ -256,8 +263,8 @@
256
263
  "check:import-cycles": "node --experimental-strip-types scripts/check-import-cycles.ts",
257
264
  "lint:fix": "oxlint --type-aware . --fix",
258
265
  "typecheck": "tsc --noEmit",
259
- "typecheck:go": "tsgo --tsBuildInfoFile ./.tsgo.tsbuildinfo",
260
- "perf:typecheck": "bash scripts/perf/exec.sh",
266
+ "typecheck:rs": "node scripts/typecheck-native.ts",
267
+ "perf:attest": "tsx scripts/perf/attest/run.ts",
261
268
  "perf:runtime": "bash scripts/perf/runtime-perf.sh",
262
269
  "postinstall": "node postinstall.mjs",
263
270
  "measure:bundle": "node ../../scripts/measure-bundle-size-sonda.js",