@tailor-platform/sdk 2.14.1 → 2.15.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.
- package/CHANGELOG.md +44 -0
- package/bin/tailor.mjs +2 -2
- package/dist/{application-CfevPO-k.mjs → application-BpoMu4Af.mjs} +18 -18
- package/dist/application-BpoMu4Af.mjs.map +1 -0
- package/dist/application-fIhVKX78.mjs +1 -0
- package/dist/cli/commands/deploy/app-id-lock.d.mts +97 -0
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +15 -1
- package/dist/cli/commands/tailordb/migrate/generate.d.mts +5 -2
- package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +13 -0
- package/dist/cli/commands/tailordb/migrate/snapshot-comparison.d.mts +7 -1
- package/dist/cli/commands/workflow/executions.d.mts +2 -0
- package/dist/cli/lib.d.mts +4 -2
- package/dist/cli/lib.mjs +1 -1
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/main.d.mts +8 -8
- package/dist/cli/main.mjs +42 -44
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/shared/command.d.mts +1 -1
- package/dist/cli/shared/function-execution.d.mts +14 -0
- package/dist/completion/zsh-worker.zsh +8 -3
- package/dist/configure/config/types.d.mts +23 -6
- package/dist/configure/index.mjs +1 -1
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/executor/trigger/event.d.mts +5 -2
- package/dist/date-CGBZbMW5.mjs +2 -0
- package/dist/date-CGBZbMW5.mjs.map +1 -0
- package/dist/file-DKBOj5q4.mjs +2 -0
- package/dist/file-DKBOj5q4.mjs.map +1 -0
- package/dist/plugin/builtin/file-utils/index.mjs +27 -1
- package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
- package/dist/register-ts-hook-CAUcxCt4.mjs +711 -0
- package/dist/register-ts-hook-CAUcxCt4.mjs.map +1 -0
- package/dist/runtime/file.d.mts +46 -2
- package/dist/runtime/file.mjs +1 -1
- package/dist/runtime/index.mjs +1 -1
- package/dist/{schema-DMlLMsWA.mjs → schema-BTioi2dP.mjs} +2 -2
- package/dist/{schema-DMlLMsWA.mjs.map → schema-BTioi2dP.mjs.map} +1 -1
- package/dist/service_pb-DKrsO1_u.mjs +1 -0
- package/dist/service_pb-DprmLNsq.mjs +2 -0
- package/dist/{service_pb-CxHztLk1.mjs.map → service_pb-DprmLNsq.mjs.map} +1 -1
- package/dist/tailor-proto/src/tailor/v1/idp_resource_pb.d.mts +5 -1
- package/dist/types/auth.generated.d.mts +1 -19
- package/dist/types/executor.generated.d.mts +5 -3
- package/dist/types/field.generated.d.mts +40 -0
- package/dist/types/helpers.d.mts +9 -1
- package/dist/types/resolver.generated.d.mts +5 -40
- package/dist/vitest/index.d.mts +2 -3
- package/dist/vitest/index.mjs +1 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mocks/file.d.mts +2 -2
- package/dist/vitest/setup.d.mts +0 -37
- package/dist/vitest/setup.mjs +1 -1
- package/dist/vitest/setup.mjs.map +1 -1
- package/dist/{workspace_resource_pb-u9bj6p3i.mjs → workspace_resource_pb-BOoRts_z.mjs} +2 -2
- package/dist/{workspace_resource_pb-u9bj6p3i.mjs.map → workspace_resource_pb-BOoRts_z.mjs.map} +1 -1
- package/docs/cli/application.md +1 -1
- package/docs/cli/function.md +19 -6
- package/docs/cli/tailordb.md +11 -11
- package/docs/configuration.md +20 -3
- package/docs/github-actions.md +45 -13
- package/docs/migration/v3.md +38 -0
- package/docs/runtime.md +39 -0
- package/docs/services/resolver.md +3 -1
- package/docs/services/tailordb-migration.md +25 -5
- package/docs/testing.md +3 -7
- package/package.json +7 -7
- package/dist/application-BWOMdL3D.mjs +0 -1
- package/dist/application-CfevPO-k.mjs.map +0 -1
- package/dist/date-DrUO8rOJ.mjs +0 -2
- package/dist/date-DrUO8rOJ.mjs.map +0 -1
- package/dist/file-COPYfju_.mjs +0 -2
- package/dist/file-COPYfju_.mjs.map +0 -1
- package/dist/register-ts-hook-DZ1Kpt4s.mjs +0 -642
- package/dist/register-ts-hook-DZ1Kpt4s.mjs.map +0 -1
- package/dist/service_pb-CFVlGYdr.mjs +0 -1
- package/dist/service_pb-CxHztLk1.mjs +0 -2
package/docs/cli/application.md
CHANGED
|
@@ -118,7 +118,7 @@ create one explicitly before requesting a deployment plan.
|
|
|
118
118
|
|
|
119
119
|
**Config File Modification:**
|
|
120
120
|
|
|
121
|
-
On first run, `deploy`
|
|
121
|
+
On the first local run, `deploy` assigns your application a stable id (a UUID) so the SDK can recognize ownership across renames. Projects that use `tailor setup` get it recorded in `.github/tailor.lock` under `appIds`; other projects get an `id: "<uuid>"` field written into the `defineConfig({...})` call in `tailor.config.ts`. Commit the file that received the id. See [Configuration](../configuration.md#application-settings) for details.
|
|
122
122
|
|
|
123
123
|
**Multiple Config Deploys:**
|
|
124
124
|
|
package/docs/cli/function.md
CHANGED
|
@@ -83,12 +83,15 @@ tailor function logs [options] [execution-id]
|
|
|
83
83
|
|
|
84
84
|
**Options**
|
|
85
85
|
|
|
86
|
-
| Option | Alias | Description
|
|
87
|
-
| ------------------------------- | ----- |
|
|
88
|
-
| `--workspace-id <WORKSPACE_ID>` | `-w` | Workspace ID
|
|
89
|
-
| `--profile <PROFILE>` | `-p` | Workspace profile
|
|
90
|
-
| `--order <ORDER>` | - | Sort order (asc or desc)
|
|
91
|
-
| `--limit <LIMIT>` | `-l` | Maximum number of items to return (0: unlimited)
|
|
86
|
+
| Option | Alias | Description | Required | Default | Env |
|
|
87
|
+
| ------------------------------- | ----- | -------------------------------------------------------------------------------------------- | -------- | -------- | ------------------------------ |
|
|
88
|
+
| `--workspace-id <WORKSPACE_ID>` | `-w` | Workspace ID | No | - | `TAILOR_PLATFORM_WORKSPACE_ID` |
|
|
89
|
+
| `--profile <PROFILE>` | `-p` | Workspace profile | No | - | `TAILOR_PLATFORM_PROFILE` |
|
|
90
|
+
| `--order <ORDER>` | - | Sort order (asc or desc) | No | `"desc"` | - |
|
|
91
|
+
| `--limit <LIMIT>` | `-l` | Maximum number of items to return (0: unlimited) | No | `50` | - |
|
|
92
|
+
| `--follow` | `-f` | Keep polling a running execution and print new log entries as they arrive (detail mode only) | No | `false` | - |
|
|
93
|
+
| `--interval <INTERVAL>` | `-i` | Polling interval for --follow (e.g., '3s', '500ms', '1m') | No | `"3s"` | - |
|
|
94
|
+
| `--timeout <TIMEOUT>` | `-t` | Maximum time to keep following (e.g., '30s', '10m'); unbounded by default | No | - | - |
|
|
92
95
|
|
|
93
96
|
See [Global Options](../cli-reference.md#global-options) for options available to all commands.
|
|
94
97
|
|
|
@@ -118,8 +121,18 @@ $ tailor function logs --json
|
|
|
118
121
|
$ tailor function logs <execution-id> --json
|
|
119
122
|
```
|
|
120
123
|
|
|
124
|
+
**Stream log entries of a running execution until it completes**
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
$ tailor function logs <execution-id> --follow
|
|
128
|
+
```
|
|
129
|
+
|
|
121
130
|
**Notes**
|
|
122
131
|
|
|
132
|
+
Execution details include `logEntries`, the structured log lines (message, severity, timestamp) recorded while the function ran. They are available while the execution is still running, whereas the flat `logs` string is filled in only after completion. The human-readable view shows the structured entries when present and falls back to `logs` otherwise.
|
|
133
|
+
|
|
134
|
+
Use `--follow` to keep polling a running execution and print new log entries as they arrive until it completes. Polling continues while the execution is suspended at a wait point, and indefinitely unless `--timeout` is set. On environments where no structured entries are returned, `--follow` shows the flat `logs` string once the execution completes. With `--json`, `--follow` waits for completion and then emits the final execution details once.
|
|
135
|
+
|
|
123
136
|
When viewing a specific execution that failed, the command displays error details with the stack trace mapped back to your original source files (clickable file links and code snippets, matching `function run` output).
|
|
124
137
|
|
|
125
138
|
Stack traces are mapped only when the execution includes a content hash for the exact build that ran. If the content hash is missing or the build is no longer available, the command falls back to a plain-text error display.
|
package/docs/cli/tailordb.md
CHANGED
|
@@ -119,17 +119,17 @@ tailor tailordb migration generate [options]
|
|
|
119
119
|
|
|
120
120
|
**Options**
|
|
121
121
|
|
|
122
|
-
| Option | Alias | Description
|
|
123
|
-
| ------------------------------------- | ----- |
|
|
124
|
-
| `--yes` | `-y` | Skip confirmation prompts
|
|
125
|
-
| `--config <CONFIG>` | `-c` | Path to Tailor config file
|
|
126
|
-
| `--name <NAME>` | `-n` | Optional description for the migration
|
|
127
|
-
| `--init` | - | Delete existing migrations and start fresh
|
|
128
|
-
| `--data-only` | - | Create a migration with no schema changes whose migration script runs a standalone data transformation
|
|
129
|
-
| `--namespace <NAMESPACE>` | - | Target TailorDB namespace for --data-only (required if multiple namespaces exist)
|
|
130
|
-
| `--rename <RENAME>` | - | Record a field or
|
|
131
|
-
| `--drop <DROP>` | - | Confirm that a removed field or
|
|
132
|
-
| `--expand-contract <EXPAND_CONTRACT>` | - | Convert a field type through a temporary field (format: "Table.field"; repeatable). Generates two migrations.
|
|
122
|
+
| Option | Alias | Description | Required | Default | Env |
|
|
123
|
+
| ------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------- | -------------------- |
|
|
124
|
+
| `--yes` | `-y` | Skip confirmation prompts | No | `false` | - |
|
|
125
|
+
| `--config <CONFIG>` | `-c` | Path to Tailor config file | No | `"tailor.config.ts"` | `TAILOR_CONFIG_PATH` |
|
|
126
|
+
| `--name <NAME>` | `-n` | Optional description for the migration | No | - | - |
|
|
127
|
+
| `--init` | - | Delete existing migrations and start fresh | No | `false` | - |
|
|
128
|
+
| `--data-only` | - | Create a migration with no schema changes whose migration script runs a standalone data transformation | No | `false` | - |
|
|
129
|
+
| `--namespace <NAMESPACE>` | - | Target TailorDB namespace for --data-only (required if multiple namespaces exist) | No | - | - |
|
|
130
|
+
| `--rename <RENAME>` | - | Record a field, table, or nested member rename instead of remove + add (format: "Table.oldField:newField", "OldTable:NewTable", or "Table.field.oldMember:newMember"; repeatable). Renames require a migration script that copies the data. | No | - | - |
|
|
131
|
+
| `--drop <DROP>` | - | Confirm that a removed field, table, or nested member is a genuine removal, not a rename (format: "Table.field", "Table", or "Table.field.member"; repeatable). Required in non-interactive runs for a removal with rename candidates. | No | - | - |
|
|
132
|
+
| `--expand-contract <EXPAND_CONTRACT>` | - | Convert a field type through a temporary field (format: "Table.field"; repeatable). Generates two migrations. | No | - | - |
|
|
133
133
|
|
|
134
134
|
See [Global Options](../cli-reference.md#global-options) for options available to all commands.
|
|
135
135
|
|
package/docs/configuration.md
CHANGED
|
@@ -21,19 +21,23 @@ To deploy the same config to multiple workspaces with per-environment values, se
|
|
|
21
21
|
import { defineConfig } from "@tailor-platform/sdk";
|
|
22
22
|
|
|
23
23
|
export default defineConfig({
|
|
24
|
-
// SDK-managed app id — do not edit, except when copying this config to a separate app.
|
|
25
|
-
// id: "<uuid>" — written here automatically on first run
|
|
26
24
|
name: "my-app",
|
|
27
25
|
cors: ["https://example.com"],
|
|
28
26
|
allowedIpAddresses: ["192.168.1.0/24"],
|
|
29
27
|
disableIntrospection: false,
|
|
30
28
|
logLevel: process.env.TAILOR_APP_LOG_LEVEL ?? "DEBUG",
|
|
29
|
+
metadata: { "erp-kit-version": "v1-2-3" },
|
|
31
30
|
});
|
|
32
31
|
```
|
|
33
32
|
|
|
34
33
|
**Name**: Set the application name.
|
|
35
34
|
|
|
36
|
-
**Id (auto-managed)**: A stable identifier used to recognize resources managed by the SDK across renames.
|
|
35
|
+
**Id (auto-managed)**: A stable identifier used to recognize resources managed by the SDK across renames. The SDK assigns it on the first local `deploy` and keeps it under version control for you; do not edit it by hand.
|
|
36
|
+
|
|
37
|
+
- Projects that use [`tailor setup`](./github-actions.md#app-id) keep the id in `.github/tailor.lock`, under `appIds`, keyed by the config file's path. `tailor.config.ts` itself carries no id, so a config copied inside the repository gets its own id, and configs that re-export another file work as well.
|
|
38
|
+
- Other projects get an `id: "<uuid>"` field written into the `defineConfig({...})` call. Delete it only if you want the SDK to assign a new id on the next `deploy` — typically when `tailor.config.ts` was copied from another project and the new application should not share the original's id. Writing the field requires `defineConfig({...})` to be called with an inline object literal: if the argument is a separate variable (e.g. `defineConfig(config)`), or if `tailor.config.ts` re-exports a config from another file, add the `id` field manually to the file that contains the actual `defineConfig({...})` object literal.
|
|
39
|
+
|
|
40
|
+
When a project starts using `tailor setup`, the next local `deploy` or `setup` moves the id from `tailor.config.ts` into the lock and removes it from the config. `AppConfig.id` stays supported: a config `id` that agrees with the lock is accepted (a local `deploy` moves it into the lock; `deploy --dry-run`, `remove`, and CI remind you to remove it), and one that disagrees stops the command so you can decide which value to keep.
|
|
37
41
|
|
|
38
42
|
**CORS**: Specify CORS settings as an array. You can also include Static Website URL references (e.g. `website.url`) in this array; see [Static Website](./services/staticwebsite.md).
|
|
39
43
|
|
|
@@ -41,6 +45,19 @@ export default defineConfig({
|
|
|
41
45
|
|
|
42
46
|
**Disable Introspection**: Disable GraphQL introspection. Default is `false`.
|
|
43
47
|
|
|
48
|
+
**Metadata**: Extra labels written to the deployed application's metadata on `deploy`, alongside the labels the SDK writes itself. Use it to record information that tooling reads back from the platform, such as the version of a framework the config is generated from. Keys must match `^[a-z][a-z0-9_-]{0,62}$` and must not start with `sdk-`; values must be empty or match the same pattern, so a version like `1.2.3` is written as `v1-2-3`. At most 17 entries can be set:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
const erpKitVersion = "1.2.3";
|
|
52
|
+
|
|
53
|
+
export default defineConfig({
|
|
54
|
+
name: "my-app",
|
|
55
|
+
metadata: { "erp-kit-version": `v${erpKitVersion.replace(/\./g, "-")}` },
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Entries are only added or overwritten. An entry removed from the config keeps its last deployed value on the platform, and labels the config does not name are left untouched. Because those retained labels count towards the platform's limit of 20 labels per resource, `deploy` reports the overflow and stops before changing the application when the labels it would leave behind exceed that limit. The labels are written when the application itself is deployed, so a config with no TailorDB, Resolver, IdP, or Auth service has no application to carry them.
|
|
60
|
+
|
|
44
61
|
**Log Level**: Controls which `console.*` and `logger.*` (from `@tailor-platform/sdk/runtime`) calls are kept when deployment functions are bundled. Supported values are `"DEBUG"`, `"INFO"`, `"WARN"`, `"ERROR"`, and `"SILENT"`. The default is `"DEBUG"` and keeps all calls. `console.log` is treated as a DEBUG-level call (matching the platform's OpenTelemetry severity mapping), so it is dropped at `"INFO"` and above, alongside `console.debug` and `logger.debug`. `logger.setAttributes` has no severity and is never dropped, regardless of `logLevel`. For production deployments, use `"WARN"` to keep warn/error calls while dropping debug, log, and info calls:
|
|
45
62
|
|
|
46
63
|
```typescript
|
package/docs/github-actions.md
CHANGED
|
@@ -208,22 +208,54 @@ planned.)
|
|
|
208
208
|
### `.github/tailor.lock`
|
|
209
209
|
|
|
210
210
|
A machine-owned JSON file that tracks which files the SDK manages, the inputs
|
|
211
|
-
they were generated from, and their content hashes
|
|
212
|
-
|
|
213
|
-
detect hand edits.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
they were generated from, and their content hashes, plus the id of every app in
|
|
212
|
+
the repository (see [App id](#app-id)). **Commit this file.** The SDK uses it to
|
|
213
|
+
recognize its own files on re-runs and to detect hand edits. The only part
|
|
214
|
+
meant for hand editing is `appIds`, and only to re-key an entry after moving an
|
|
215
|
+
app directory or to delete the entry of a removed app.
|
|
216
|
+
|
|
217
|
+
### App id
|
|
218
|
+
|
|
219
|
+
Every application has a stable id (a UUID) that the SDK uses to recognize the
|
|
220
|
+
resources it owns across renames. In a repository set up with `tailor setup`,
|
|
221
|
+
the id lives in `.github/tailor.lock` under `appIds`, keyed by the config
|
|
222
|
+
file's repository-relative path:
|
|
223
|
+
|
|
224
|
+
```jsonc
|
|
225
|
+
{
|
|
226
|
+
"version": 2,
|
|
227
|
+
"targets": [/* generated workflows */],
|
|
228
|
+
"appIds": {
|
|
229
|
+
"apps/order/tailor.config.ts": "d0a3398a-…",
|
|
230
|
+
"apps/billing/tailor.config.ts": "7f21c4e0-…",
|
|
231
|
+
},
|
|
232
|
+
}
|
|
233
|
+
```
|
|
222
234
|
|
|
235
|
+
`setup` records the id when it generates a workflow, and a local `tailor
|
|
236
|
+
deploy` records one for any config that has none yet. Because the key is the
|
|
237
|
+
config path, renaming the app keeps its id, and copying a config to a new
|
|
238
|
+
directory gives the copy a fresh id instead of the original's. If
|
|
239
|
+
`tailor.config.ts` still has an `id` field from before, `setup` or a local
|
|
240
|
+
`deploy` moves it into the lock and removes it from the config; a config `id`
|
|
241
|
+
that disagrees with the lock stops the command so you can decide which value
|
|
242
|
+
to keep.
|
|
243
|
+
|
|
244
|
+
In CI, `tailor deploy` never assigns an id — if one were assigned fresh on
|
|
245
|
+
each run, every deploy would create a brand-new application and lose ownership
|
|
246
|
+
of previously deployed resources. A config without a recorded id fails the
|
|
247
|
+
plan job with instructions to run `tailor deploy` locally and commit the lock.
|
|
223
248
|
If your pipeline intentionally deploys a fresh, throwaway application on every
|
|
224
249
|
run (for example an end-to-end test harness that creates and deletes its own
|
|
225
|
-
workspace), set `TAILOR_CI_ALLOW_ID_INJECTION=true` to
|
|
226
|
-
|
|
250
|
+
workspace), set `TAILOR_CI_ALLOW_ID_INJECTION=true` to let CI assign one.
|
|
251
|
+
|
|
252
|
+
When you move an app directory, its `appIds` entry still points at the old
|
|
253
|
+
path. Locally, `deploy` and `setup` notice the single unmatched entry and ask
|
|
254
|
+
whether the app was moved; answering yes re-keys the entry so the app keeps
|
|
255
|
+
its id. In CI, or when more than one entry is unmatched, the command stops and
|
|
256
|
+
asks you to re-key the entry (or delete entries of removed apps) by hand.
|
|
257
|
+
Re-run the relevant `setup` subcommand as well so the workflow's paths follow
|
|
258
|
+
the move.
|
|
227
259
|
|
|
228
260
|
## Secrets
|
|
229
261
|
|
package/docs/migration/v3.md
CHANGED
|
@@ -160,3 +160,41 @@ property, which is the relation's cardinality (e.g. "n-1", "1-1",
|
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
</details>
|
|
163
|
+
|
|
164
|
+
## String file uploads → explicit encoding
|
|
165
|
+
|
|
166
|
+
**Migration:** Manual
|
|
167
|
+
|
|
168
|
+
String inputs to the SDK `file.upload` and generated `uploadFile` require an explicit `encoding` in v3. Add `encoding: "utf8"` to preserve the previous behavior; use `"base64"` only when decoding the input is intended. Byte inputs are unchanged. This migration requires checking the input type and regenerating file helpers, so it provides review guidance instead of rewriting calls automatically.
|
|
169
|
+
|
|
170
|
+
Before:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
await file.upload(ns, table, field, id, text, { contentType: "text/plain" });
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
After:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
await file.upload(ns, table, field, id, text, { contentType: "text/plain", encoding: "utf8" });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
<details>
|
|
183
|
+
<summary>Prompt for an AI agent (to perform this migration)</summary>
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
Inspect calls to the imported SDK file.upload (including aliases and destructured upload)
|
|
187
|
+
and generated uploadFile helpers, including shared wrappers and option objects.
|
|
188
|
+
For string inputs, add encoding: utf8 to preserve existing text storage behavior.
|
|
189
|
+
Choose encoding: base64 only if decoding is explicitly intended; never infer it from
|
|
190
|
+
the string contents or contentType. Preserve all existing upload options.
|
|
191
|
+
Leave byte-only inputs unchanged. For string | byte unions, supplying encoding: utf8
|
|
192
|
+
preserves behavior because byte input ignores encoding. Narrow shared options types
|
|
193
|
+
so TypeScript can see that encoding is present for strings.
|
|
194
|
+
Run tailor generate to regenerate uploadFile helpers instead of editing generated files.
|
|
195
|
+
The global tailordb.file.upload does not support this encoding option; switch to the
|
|
196
|
+
file import from @tailor-platform/sdk/runtime before using it.
|
|
197
|
+
Do not change unrelated upload APIs or already explicit encodings.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
</details>
|
package/docs/runtime.md
CHANGED
|
@@ -43,6 +43,45 @@ const { url } = await aigateway.get("my-aigateway");
|
|
|
43
43
|
logger.info("order processed", { orderId: "o-1", total: 99.5 });
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
## Uploading files
|
|
47
|
+
|
|
48
|
+
Pass bytes directly to `file.upload`. For strings, specify how to interpret the input:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { file } from "@tailor-platform/sdk/runtime";
|
|
52
|
+
|
|
53
|
+
await file.upload("my-namespace", "Document", "attachment", recordId, text, {
|
|
54
|
+
encoding: "utf8",
|
|
55
|
+
contentType: "text/plain",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await file.upload("my-namespace", "Document", "attachment", recordId, imageBase64, {
|
|
59
|
+
encoding: "base64",
|
|
60
|
+
contentType: "image/png",
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`encoding` controls the input string's interpretation; `contentType` describes the stored file.
|
|
65
|
+
Setting `contentType: "image/png"` does not decode Base64. Byte arrays and buffers are uploaded
|
|
66
|
+
unchanged, even when `encoding` is supplied. Omitting `contentType` with `encoding: "utf8"`
|
|
67
|
+
stores the file as `text/plain; charset=utf-8`; omitting it with `"base64"` leaves the content
|
|
68
|
+
type unset, unless the Base64 string is a `data:<contentType>;base64,<data>` URL, in which case
|
|
69
|
+
`<contentType>` is used. An explicit `contentType` option always takes precedence over one
|
|
70
|
+
found in a data URL.
|
|
71
|
+
|
|
72
|
+
Base64 input may omit padding and contain ASCII whitespace. Invalid characters and invalid
|
|
73
|
+
padding are rejected with `TypeError` before upload. Decoding Base64 does not validate the
|
|
74
|
+
resulting file's format.
|
|
75
|
+
|
|
76
|
+
Uploading a string without `encoding` still stores it as text, but that overload is deprecated
|
|
77
|
+
and will be removed in v3. Add `encoding: "utf8"` to preserve existing behavior, or choose
|
|
78
|
+
`"base64"` when decoding is intended. Calls with byte arrays or buffers are not deprecated.
|
|
79
|
+
The generated `uploadFile` helper supports the same options; run `tailor generate` to update it.
|
|
80
|
+
|
|
81
|
+
The encoding option is available on the imported SDK `file.upload` and generated helpers.
|
|
82
|
+
For code using the global `tailordb.file.upload`, switch to the imported `file.upload` before
|
|
83
|
+
using this option.
|
|
84
|
+
|
|
46
85
|
## Subpath imports
|
|
47
86
|
|
|
48
87
|
Each namespace can also be imported individually so you only pull what you need:
|
|
@@ -139,7 +139,9 @@ createResolver({
|
|
|
139
139
|
|
|
140
140
|
GraphQL still accepts and returns `YYYY-MM-DD` strings. The SDK converts input to a `Date` at midnight UTC and formats output using its UTC year, month, and day. Use UTC getters and setters for date arithmetic; local getters and setters depend on the runtime's timezone. Any time component in the returned `Date` is discarded according to UTC, so `new Date("2026-09-07T00:00:00+09:00")` returns `"2026-09-06"`.
|
|
141
141
|
|
|
142
|
-
This option also works in nested objects and with `array: true` or `optional: true`. Input must be a valid calendar date, and output must be a valid `Date` with a UTC year
|
|
142
|
+
This option also works in nested objects and with `array: true` or `optional: true`. Input must be a valid calendar date, and output must be a valid `Date` with a 4-digit UTC year (0000-9999). Both deployed resolvers and `tailor function run` perform these conversions.
|
|
143
|
+
|
|
144
|
+
An executor subscribing to the resolver with `resolverExecutedTrigger` receives the event as JSON, so `result` holds the `YYYY-MM-DD` string rather than a `Date`.
|
|
143
145
|
|
|
144
146
|
### Custom Type Name (`typeName`)
|
|
145
147
|
|
|
@@ -164,15 +164,33 @@ During deploy, the pre-migration phase keeps the old field and adds the new fiel
|
|
|
164
164
|
|
|
165
165
|
If you decline the prompt (or confirm the removal with `--drop`), the change stays a plain removal + addition with the usual data-loss warning.
|
|
166
166
|
|
|
167
|
-
Renaming a member inside a
|
|
167
|
+
#### Renaming a member inside a nested field
|
|
168
|
+
|
|
169
|
+
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.
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
? User.address.zip was removed and zipCode was added with a compatible type. Was it renamed to zipCode? (Y/n)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
In non-interactive environments the command fails while a candidate is left unresolved, exactly like field renames. Resolve it with the nested member forms of the same flags (a value with two or more dots before the `:` targets a member; deeper members use their dotted path, and the new name is a single segment under the same parent):
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
tailor tailordb migration generate --rename "User.address.zip:zipCode"
|
|
179
|
+
tailor tailordb migration generate --rename "User.address.geo.lat:latitude"
|
|
180
|
+
tailor tailordb migration generate --drop "User.address.zip"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
A confirmed rename is recorded on the nested field's `field_modified` change as `memberRenames` and treated as **breaking**, so a migration script is required. The generated `migrate.ts` reads every row's nested value, stores each renamed member under its new name (descending into arrays at every level), and writes the value back; the old member is kept in the written value because it stays on the schema until the post-migration phase drops it. During deploy, the pre-migration phase keeps the old member on the nested field and adds the new member as optional, the script copies the values, and the post-migration phase drops the old member and enforces the new member's requiredness.
|
|
184
|
+
|
|
185
|
+
A member removed without a confirmed rename stays a data-loss warning, which `migration validate --strict` picks up like any other warning; when a compatible sibling was added, the warning names it and the `--rename` value that confirms the rename:
|
|
168
186
|
|
|
169
187
|
```
|
|
170
188
|
Warning: data loss possible:
|
|
171
189
|
|
|
172
|
-
- User.address.zip: Nested member removed (existing values will no longer be accessible through the schema). Possibly renamed to zipCode:
|
|
190
|
+
- 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
|
|
173
191
|
```
|
|
174
192
|
|
|
175
|
-
|
|
193
|
+
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).
|
|
176
194
|
|
|
177
195
|
### Renaming a table
|
|
178
196
|
|
|
@@ -261,7 +279,7 @@ export default defineConfig({
|
|
|
261
279
|
|
|
262
280
|
### Migration file format compatibility
|
|
263
281
|
|
|
264
|
-
Migration files are versioned independently of the SDK package. This SDK writes format version `
|
|
282
|
+
Migration files are versioned independently of the SDK package. This SDK writes format version `6` and reads versions `1` through `6`. It normalizes supported older formats in memory; it never rewrites applied migration files on disk. Format version `6` records renames of members inside nested fields (`memberRenames`); older SDK versions refuse to read it rather than deploying such a migration without the copy step.
|
|
265
283
|
|
|
266
284
|
If a future SDK can no longer replay an old migration format, re-baseline while using an SDK version that still supports the complete history, commit the new baseline, deploy it to every environment, and then upgrade the SDK. A file from a newer unsupported format instead requires upgrading the SDK first. The CLI rejects both cases with guidance rather than attempting a best-effort replay.
|
|
267
285
|
|
|
@@ -348,6 +366,8 @@ The `env` values are injected at bundle time (the same mechanism as resolvers/ex
|
|
|
348
366
|
| Add required field | Yes | Yes | Script populates default values |
|
|
349
367
|
| 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. |
|
|
350
368
|
| 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. |
|
|
369
|
+
| 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. |
|
|
370
|
+
| 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. |
|
|
351
371
|
| Change optional → required | Yes | Yes | Script sets defaults for null values |
|
|
352
372
|
| Change required → optional | No | No | Schema change only |
|
|
353
373
|
| Add index (non-unique) | No | No | Schema change only |
|
|
@@ -552,7 +572,7 @@ When you run `tailor deploy`, the SDK detects pending migrations (anything past
|
|
|
552
572
|
|
|
553
573
|
For each pending migration:
|
|
554
574
|
|
|
555
|
-
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. 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.
|
|
575
|
+
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.
|
|
556
576
|
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).
|
|
557
577
|
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.
|
|
558
578
|
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
|
@@ -59,7 +59,7 @@ export default defineConfig({
|
|
|
59
59
|
`tailorRuntime()` provides:
|
|
60
60
|
|
|
61
61
|
1. **Node.js module blocking** — `import { randomBytes } from "node:crypto"` in production code throws an error with a suggestion for the Web Standard API alternative (`globalThis.crypto`). Test files (`*.test.ts`, `*.spec.ts`) are exempt.
|
|
62
|
-
2. **Node.js globals removal** — Only globals available in the platform runtime are kept (whitelist). `Buffer`, `global`, `setImmediate`, `__dirname`, `__filename`,
|
|
62
|
+
2. **Node.js globals removal** — Only globals available in the platform runtime are kept (whitelist). `Buffer`, `global`, `setImmediate`, `__dirname`, `__filename`, and others are removed.
|
|
63
63
|
3. **Platform API mocks** — the platform error classes (`TailorErrors`, `TailorDBFileError`) and `tailor.context` are always available. The other namespaces (`tailordb.Client`, `tailor.workflow`, `tailor.secretmanager`, …) are mocked when you acquire the corresponding `mockX()` — see below.
|
|
64
64
|
|
|
65
65
|
### Acquiring mocks with `using`
|
|
@@ -452,12 +452,7 @@ export default defineConfig({
|
|
|
452
452
|
plugins: [tailorRuntime()],
|
|
453
453
|
test: {
|
|
454
454
|
projects: [
|
|
455
|
-
// `extends: true` is required so each project inherits the root-level
|
|
456
|
-
// `tailorRuntime()` plugin (transform hook + injected setup file).
|
|
457
|
-
// Without it, only the environment name rewrite applies — node:* import
|
|
458
|
-
// blocking and per-test global cleanup will silently not run.
|
|
459
455
|
{
|
|
460
|
-
extends: true,
|
|
461
456
|
test: {
|
|
462
457
|
name: "unit",
|
|
463
458
|
environment: "tailor-runtime",
|
|
@@ -465,7 +460,6 @@ export default defineConfig({
|
|
|
465
460
|
},
|
|
466
461
|
},
|
|
467
462
|
{
|
|
468
|
-
extends: true,
|
|
469
463
|
test: {
|
|
470
464
|
name: "e2e",
|
|
471
465
|
include: ["e2e/**/*.test.ts"],
|
|
@@ -477,6 +471,8 @@ export default defineConfig({
|
|
|
477
471
|
});
|
|
478
472
|
```
|
|
479
473
|
|
|
474
|
+
Inline projects inherit the root-level `tailorRuntime()` plugin by default on Vitest 5. On Vitest 4, add `extends: true` to each project; without it, `node:*` import blocking silently does not run.
|
|
475
|
+
|
|
480
476
|
### Known Limitations
|
|
481
477
|
|
|
482
478
|
- **`process` and `require`** are not removed or blocked. Vitest's internal runner depends on them extensively. On the real platform runtime, they do not exist.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailor-platform/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -165,6 +165,7 @@
|
|
|
165
165
|
"@opentelemetry/sdk-trace-node": "2.11.0",
|
|
166
166
|
"@opentelemetry/semantic-conventions": "1.43.0",
|
|
167
167
|
"@oxc-project/types": "0.148.0",
|
|
168
|
+
"@politty/zod": "0.2.1",
|
|
168
169
|
"@secretlint/core": "13.0.5",
|
|
169
170
|
"@secretlint/secretlint-rule-preset-recommend": "13.0.5",
|
|
170
171
|
"@standard-schema/spec": "1.1.0",
|
|
@@ -173,13 +174,13 @@
|
|
|
173
174
|
"@toiroakr/read-multiline": "0.4.1",
|
|
174
175
|
"@urql/core": "6.0.3",
|
|
175
176
|
"amaro": "1.1.11",
|
|
176
|
-
"confbox": "0.
|
|
177
|
+
"confbox": "0.3.1",
|
|
177
178
|
"date-fns": "4.4.0",
|
|
178
179
|
"es-toolkit": "1.52.0",
|
|
179
180
|
"find-up-simple": "1.0.1",
|
|
180
181
|
"get-east-asian-width": "1.6.0",
|
|
181
182
|
"get-tsconfig": "4.14.3",
|
|
182
|
-
"globals": "17.
|
|
183
|
+
"globals": "17.12.0",
|
|
183
184
|
"graphql": "17.0.2",
|
|
184
185
|
"inflection": "3.0.2",
|
|
185
186
|
"kysely": "0.29.5",
|
|
@@ -190,7 +191,6 @@
|
|
|
190
191
|
"pathe": "2.0.3",
|
|
191
192
|
"pgsql-ast-parser": "12.0.2",
|
|
192
193
|
"pkg-types": "2.3.2",
|
|
193
|
-
"politty": "0.11.9",
|
|
194
194
|
"rolldown": "1.2.7",
|
|
195
195
|
"semver": "7.8.5",
|
|
196
196
|
"sql-highlight": "6.1.0",
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
"@types/node": "24.13.3",
|
|
209
209
|
"@types/semver": "7.8.0",
|
|
210
210
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
211
|
-
"@vitest/coverage-v8": "
|
|
211
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
212
212
|
"eslint-plugin-zod": "4.12.0",
|
|
213
213
|
"oxfmt": "0.66.0",
|
|
214
214
|
"oxlint": "1.81.0",
|
|
@@ -216,8 +216,8 @@
|
|
|
216
216
|
"sonda": "0.14.0",
|
|
217
217
|
"tsdown": "0.23.0",
|
|
218
218
|
"typescript": "6.0.3",
|
|
219
|
-
"vitest": "
|
|
220
|
-
"zinfer": "0.
|
|
219
|
+
"vitest": "5.0.0",
|
|
220
|
+
"zinfer": "0.4.5"
|
|
221
221
|
},
|
|
222
222
|
"peerDependencies": {
|
|
223
223
|
"@electric-sql/pglite": ">=0.2.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,t}from"./application-CfevPO-k.mjs";export{t as defineApplication,e as generatePluginFilesIfNeeded};
|