@tailor-platform/sdk 2.28.0 → 2.29.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.
@@ -35,7 +35,8 @@ tailor setup ci tag --name my-app-prod \
35
35
 
36
36
  After running the command, follow the **Next steps** printed to the terminal:
37
37
  run `tailor setup ci env` to get the commands that set the secrets and
38
- variables each GitHub Environment needs (see
38
+ variables each GitHub Environment needs, and `tailor setup ci env --check` to
39
+ confirm none is missing (see
39
40
  [Setting secrets and variables](#setting-secrets-and-variables)), then commit
40
41
  the generated files.
41
42
 
@@ -98,7 +99,8 @@ The generated workflow builds one self-contained ERD viewer HTML file for each
98
99
  owned TailorDB namespace in `tailor.config.ts`. The viewer compares the pull
99
100
  request merge result with the base branch, can switch between the current schema
100
101
  and highlighted diff, uploads the HTML files as unarchived Actions artifacts,
101
- and upserts a PR comment with artifact links.
102
+ and upserts a PR comment with artifact links. On GitHub Enterprise Server, also
103
+ pass `--ghes` (see [GitHub Enterprise Server](#github-enterprise-server)).
102
104
 
103
105
  ERD preview does not use Tailor Platform credentials. Fork pull requests still
104
106
  build artifacts, but the comment step is skipped because fork tokens cannot
@@ -264,8 +266,8 @@ Everything else is yours, and re-running `setup` keeps it:
264
266
  - **These inputs of managed steps:** `ignore` on `tailor-generate-check` and
265
267
  `tailor-drift-check`, `fail-on-drift` on `tailor-drift-check`,
266
268
  `install-command` on `tailor-install`, `node-version-file` on
267
- `tailor-setup`, `label` on `tailor-plan`, and `user-mapping` on
268
- `tailor-notify`.
269
+ `tailor-setup`, `label` on `tailor-plan`, `ttl` on `tailor-preview-deploy`,
270
+ and `user-mapping` on `tailor-notify`.
269
271
  - **Your own jobs in the `needs` of `tailor-result` and
270
272
  `tailor-preview-result`**, so they count toward the workflow's result (see
271
273
  [Requiring the workflow in branch protection](#requiring-the-workflow-in-branch-protection)).
@@ -296,6 +298,17 @@ The job is skipped whenever `tailor-preview-deploy` is skipped: when a pull
296
298
  request is closed, for draft and fork pull requests, and for unlabeled ones with
297
299
  `--require-preview-label`.
298
300
 
301
+ A preview workspace expires 7 days (`ttl: 7d` on `tailor-preview-deploy`) after
302
+ the last push to its pull request. When a pull request is closed,
303
+ `tailor-preview-cleanup` deletes its workspace and also deletes the other
304
+ expired preview workspaces of the same workspace name in the folder
305
+ (`TAILOR_PLATFORM_FOLDER_ID`, or directly under the organization when unset), so
306
+ a workspace whose own cleanup never ran does not stay forever. A pull request
307
+ that stays idle past the `ttl` can have its workspace deleted while it is still
308
+ open; the next push creates a fresh one. Change `ttl` to keep previews longer or
309
+ shorter. Workflows generated before this was added get these settings the next
310
+ time you run `tailor setup update`.
311
+
299
312
  Each new preview workflow run for a pull request, such as after a push or when
300
313
  the pull request is closed, cancels the run still in progress for it, including
301
314
  your jobs, so they do not keep running against a workspace that the new run
@@ -468,14 +481,18 @@ protection rules no longer guard your deploys.
468
481
  Environment the generated workflows use, the commands that create the
469
482
  environment (only when it does not exist yet) and set its secrets and
470
483
  variables. It is read-only, so re-run it whenever you add a target. When the
471
- `origin` remote points at github.com, the output names that repository, so the
472
- commands work from any directory; otherwise `gh` resolves the repository from
473
- the current directory and the Terraform output leaves it as a placeholder.
484
+ `origin` remote points at github.com, the output names that repository;
485
+ otherwise it names the repository `gh repo view` reports for the current
486
+ directory, with its host on GitHub Enterprise Server. Either way the commands
487
+ work from any directory. When neither names a repository, `gh` resolves it from
488
+ the current directory when you run the commands, and the Terraform output leaves
489
+ it as a placeholder.
474
490
 
475
491
  ```bash
476
492
  tailor setup ci env # gh CLI commands (default)
477
493
  tailor setup ci env --format terraform # Terraform for the integrations/github provider
478
494
  tailor setup ci env --environment my-app-stg # only one environment (repeat for several)
495
+ tailor setup ci env --check # check what is registered in GitHub (see below)
479
496
  ```
480
497
 
481
498
  The list follows what each generated workflow actually reads:
@@ -517,6 +534,30 @@ protection settings (reviewers, wait timer, branch policy), so importing an
517
534
  environment keeps the approval gate you configured; remove the `lifecycle` block
518
535
  to manage those settings in Terraform instead.
519
536
 
537
+ ### Checking what is registered
538
+
539
+ `tailor setup ci env --check` reads GitHub with `gh` and reports, for each
540
+ environment, which of these entries are registered. It fails when a required
541
+ entry is missing, reports missing optional entries without failing, and prints
542
+ the `gh` commands that set only the missing entries, with where each value comes
543
+ from. It changes nothing, and it checks names only, since GitHub never returns
544
+ secret values.
545
+
546
+ - An entry set on the repository, or on the organization and shared with the
547
+ repository, counts as registered, because the workflow reads it from there when
548
+ the environment does not set it. The report says where each entry was found.
549
+ - An entry registered as the other kind, such as `TAILOR_PLATFORM_WORKSPACE_ID`
550
+ set as a secret, is reported as missing, because the workflow does not read it.
551
+ `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` set as a secret still counts, with a
552
+ reminder to move it to a variable.
553
+ - `gh` must be installed and logged in (`gh auth login`) as an account with write
554
+ access to the repository, which listing secrets and variables requires. GitHub
555
+ answers 404 for a private repository the account cannot see, so the check
556
+ reports that as an access problem, not as missing entries.
557
+
558
+ `--environment` narrows the check the same way. `--check` cannot be combined with
559
+ `--format terraform`.
560
+
520
561
  ## GitHub Environments (approval gate)
521
562
 
522
563
  Both the `plan` and `deploy` jobs are associated with a GitHub Environment — the
@@ -784,6 +825,48 @@ tailor setup ci env
784
825
 
785
826
  Commit both workflow files and `.github/tailor.lock`.
786
827
 
828
+ ## GitHub Enterprise Server
829
+
830
+ The generated workflows also run on GitHub Enterprise Server. Prepare the
831
+ server and its runners first:
832
+
833
+ - **Runners:** GitHub Enterprise Server has no GitHub-hosted runners. Change
834
+ `runs-on` on the managed jobs to the labels of your self-hosted runners;
835
+ re-running `setup` keeps it (see
836
+ [Customizing the generated workflow](#customizing-the-generated-workflow)).
837
+ The runners need Actions runner 2.327.1 or later, which runs the Node 24
838
+ actions the workflows use, and `gh` and `jq` on `PATH` (plus `node` with
839
+ `--erd-preview`).
840
+ - **Actions from github.com:** the workflows use `tailor-platform/actions`,
841
+ `actions/checkout`, `actions/setup-node`, `pnpm/action-setup`, and
842
+ `oven-sh/setup-bun` (and `actions/upload-artifact` with `--erd-preview`).
843
+ Make them available on the server with GitHub Connect or `actions-sync`. The
844
+ server ships its own copies of the `actions/*` ones, which may not include
845
+ the pinned versions: update them with `actions-sync`, or, with GitHub
846
+ Connect, delete them from the server's `actions` organization (see GitHub's
847
+ [Using the latest version of the official bundled actions](https://docs.github.com/en/enterprise-server@latest/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/using-the-latest-version-of-the-official-bundled-actions)).
848
+ - **Environment settings:** sign `gh` in to the server
849
+ (`gh auth login --hostname <host>`) before you run `tailor setup ci env` in a
850
+ clone of the repository, so that the `gh` commands it prints and its Terraform
851
+ output target the server. When `gh` cannot tell the repository, the output
852
+ says to export `GH_HOST=<host>` before running the `gh` commands, or
853
+ `GITHUB_BASE_URL=https://<host>/` before running Terraform, so that they
854
+ call the server instead of github.com.
855
+
856
+ With `--erd-preview`, also pass `--ghes`:
857
+
858
+ ```bash
859
+ tailor setup ci branch --name my-app-stg --erd-preview --ghes
860
+ ```
861
+
862
+ GitHub Enterprise Server does not support `actions/upload-artifact` v4 or
863
+ later, which the workflow uses to upload each ERD viewer unzipped. With
864
+ `--ghes`, the workflow uploads it with v3 instead, so each viewer is a zip
865
+ file, and the PR comment links to the workflow run, which lists the artifacts.
866
+ `setup update` keeps `--ghes`; pass it again whenever you re-run
867
+ `setup ci branch`, or the workflow switches back to the upload that GitHub
868
+ Enterprise Server does not support.
869
+
787
870
  ## Keeping dependencies and actions updated
788
871
 
789
872
  Run this command once from the repository root to add Renovate configuration:
@@ -516,9 +516,9 @@ export default createSoftDeletePlugin();
516
516
  ```typescript
517
517
  // plugins/soft-delete/executors/on-delete.ts
518
518
  import { createExecutor, recordDeletedTrigger } from "@tailor-platform/sdk";
519
- import type { TailorAnyDBType } from "@tailor-platform/sdk";
520
519
  import { withPluginContext } from "@tailor-platform/sdk/plugin";
521
520
  import { getDB } from "generated/tailordb";
521
+ import type { TailorAnyDBType } from "@tailor-platform/sdk";
522
522
 
523
523
  interface SoftDeleteContext {
524
524
  sourceTable: TailorAnyDBType;
@@ -11,64 +11,7 @@ Resolvers provide:
11
11
  - Access to TailorDB via Kysely query builder
12
12
  - User context for authentication/authorization
13
13
 
14
- ## Comparison with Tailor Platform Pipeline Resolver
15
-
16
- The SDK's Resolver is a simplified version of Tailor Platform's [Pipeline Resolver](https://docs.tailor.tech/guides/resolver).
17
-
18
- | Pipeline Resolver | SDK Resolver |
19
- | ---------------------------------------- | --------------------------------- |
20
- | Multiple steps with different operations | Single `body` function |
21
- | Declarative step configuration | Imperative TypeScript code |
22
- | Built-in TailorDB/GraphQL steps | Direct database access via Kysely |
23
- | CEL expressions for data transformation | Native TypeScript transformations |
24
-
25
- ### Example Comparison
26
-
27
- **Pipeline Resolver (Tailor Platform native):**
28
-
29
- ```yaml
30
- steps:
31
- - name: getUser
32
- operation: tailordb.query
33
- params:
34
- type: User
35
- filter:
36
- email: { eq: "{{ input.email }}" }
37
- - name: updateAge
38
- operation: tailordb.mutation
39
- params:
40
- type: User
41
- id: "{{ steps.getUser.id }}"
42
- input:
43
- age: "{{ steps.getUser.age + 1 }}"
44
- ```
45
-
46
- **Resolver (SDK):**
47
-
48
- ```typescript
49
- createResolver({
50
- name: "incrementUserAge",
51
- operation: "mutation",
52
- input: { email: t.string() },
53
- body: async (context) => {
54
- const db = getDB("tailordb");
55
- const user = await db
56
- .selectFrom("User")
57
- .selectAll()
58
- .where("email", "=", context.input.email)
59
- .executeTakeFirstOrThrow();
60
-
61
- await db
62
- .updateTable("User")
63
- .set({ age: user.age + 1 })
64
- .where("id", "=", user.id)
65
- .execute();
66
-
67
- return { oldAge: user.age, newAge: user.age + 1 };
68
- },
69
- output: t.object({ oldAge: t.int(), newAge: t.int() }),
70
- });
71
- ```
14
+ For the official Tailor Platform documentation, see [Resolver Guide](https://docs.tailor.tech/guides/resolver).
72
15
 
73
16
  ## Creating a Resolver
74
17
 
@@ -425,7 +425,7 @@ Because steps commit separately, a failure does not undo the steps that already
425
425
  - If the steps changed since the earlier run, or that run is no longer available, every step runs again from the beginning. See [Editing steps of a migration in progress](#editing-steps-of-a-migration-in-progress).
426
426
  - If every step completed but the post-migration schema changes fail (for example, a field the migration makes required still has empty values), the migration also stays in progress with its pre-migration schema, and the next deploy retries those schema changes without running the steps again.
427
427
  - To run a completed step again (because it had a bug, or to fix rows the post-migration schema changes reject), change the steps: rename that step and update the `dependsOn` lists that name it, or add a step that fixes the rows. Every step then runs again from the beginning, so this relies on every step being safe to run again; to keep the other completed steps from running again, skip them as described below.
428
- - While a migration is in progress, `migration set`, `migration sync`, and `migration rebaseline` refuse to run, and `migration test` refuses to use that workspace as its source. Finish the migration with `tailor deploy` first. The one exception is `migration set <N>` with the in-progress migration's own number: it marks that migration as completed and clears its in-progress record. Use it only when the migration's schema changes are already applied, for example after the deploy reported that the checkpoint write could not be confirmed.
428
+ - While a migration is in progress, `migration set`, `migration sync`, and `migration rebaseline` refuse to run, and `migration test` refuses to use that workspace as its source. Finish the migration with `tailor deploy` first, or settle it with `migration resolve` (see [Settling a migration that stays in progress](#settling-a-migration-that-stays-in-progress)). The one exception is `migration set <N>` with the in-progress migration's own number: it marks that migration as completed and clears its in-progress record. Use it only when the migration's schema changes are already applied, for example after the deploy reported that the checkpoint write could not be confirmed.
429
429
 
430
430
  #### Editing steps of a migration in progress
431
431
 
@@ -450,6 +450,15 @@ tailor deploy --migration-skip-steps main-db/backfillUser,main-db/backfillInvoic
450
450
 
451
451
  Write every step so that running it again is safe: besides the cases above, a step can run a second time when a deploy is interrupted right after it commits. Use `where` clauses that skip rows a step already migrated, as in the example. Rows written while a migration is in progress are not seen by steps that already completed. [Maintenance mode](#maintenance-mode) blocks GraphQL writes but not the writes your resolvers, executors, or workflows make through Kysely, and without it GraphQL clients can write too, so stop such writers until the migration finishes or write steps that tolerate them.
452
452
 
453
+ ### Settling a migration that stays in progress
454
+
455
+ When you do not want to resume the migration with another deploy — you abandoned it, or you repaired the data by hand — settle it with `tailor tailordb migration resolve`. Pass exactly one of two options:
456
+
457
+ - `--rollback` sends the schema of the migration before the failed one, clears the in-progress record, and leaves the checkpoint where it was. The next `tailor deploy` runs the failed migration again from its first step, so every step must be safe to run again. Data changes made by steps that already completed are not undone, and tables that exist only in the failed migration's schema are deleted with their data.
458
+ - `--complete` sends the schema of the failed migration itself, clears the in-progress record, and moves the checkpoint to that migration, without running its `migrate.ts` again. Repair the data first, for example with `tailor query`, and make sure no record lacks a value for a field the migration makes required: the update is rejected while such a record exists, and the migration stays in progress.
459
+
460
+ Tables that the failed migration removes are deleted with their data under `--complete`. The command refuses to run while an execution of the failed migration is still running; wait for it to finish first. Both options take the namespace's tables out of [maintenance mode](#maintenance-mode) and remove the temporary resources of the failed run. The command shows what it will change and asks for confirmation (`--yes` skips it). As with `migration sync`, the local migration history must reproduce the current table definitions, so settle the migration before you change the tables. If sending the schema stops partway, run the same command again.
461
+
453
462
  ## Supported Schema Changes
454
463
 
455
464
  | Change Type | Breaking? | Migration Script? | Notes |
@@ -976,8 +985,8 @@ When `migrate.ts` already exists (the usual case for breaking changes, where `mi
976
985
  // migrations/0005/migrate.test.ts
977
986
  import { createKyselyMock } from "@tailor-platform/sdk/vitest";
978
987
  import { describe, expect, test } from "vitest";
979
- import type { Database } from "./db";
980
988
  import { main } from "./migrate";
989
+ import type { Database } from "./db";
981
990
 
982
991
  describe("0005 add required email", () => {
983
992
  test("backfills null emails", async () => {
@@ -1013,9 +1022,9 @@ The generated `db.pglite.ts` exports the `CREATE TABLE` script for the same sche
1013
1022
  import { PGlite } from "@electric-sql/pglite";
1014
1023
  import { createKyselyPGlite, type Unmigrated } from "@tailor-platform/sdk/vitest";
1015
1024
  import { afterAll, beforeAll, describe, expect, test } from "vitest";
1016
- import type { Database } from "./db";
1017
1025
  import { pgliteSchema } from "./db.pglite";
1018
1026
  import { main } from "./migrate";
1027
+ import type { Database } from "./db";
1019
1028
 
1020
1029
  const pglite = new PGlite();
1021
1030
  const db = createKyselyPGlite<Unmigrated<Database>>(pglite);
@@ -488,9 +488,9 @@ Pass the runtime key through the `executionPolicyKey` option on `job.start()`. F
488
488
 
489
489
  ```typescript
490
490
  import { createWorkflowJob } from "@tailor-platform/sdk";
491
- import { executionPolicies } from "./policies";
492
- import { sendNotification } from "./jobs/send-notification";
493
491
  import { fetchTenant } from "./jobs/fetch-tenant";
492
+ import { sendNotification } from "./jobs/send-notification";
493
+ import { executionPolicies } from "./policies";
494
494
 
495
495
  export const mainJob = createWorkflowJob({
496
496
  name: "main-job",
package/docs/testing.md CHANGED
@@ -44,9 +44,9 @@ The Tailor Platform function runtime only provides Web Standard APIs. Node.js bu
44
44
  ### Setup
45
45
 
46
46
  ```typescript
47
+ import { tailorRuntime } from "@tailor-platform/sdk/vitest";
47
48
  // vitest.config.ts
48
49
  import { defineConfig } from "vitest/config";
49
- import { tailorRuntime } from "@tailor-platform/sdk/vitest";
50
50
 
51
51
  export default defineConfig({
52
52
  plugins: [tailorRuntime()],
@@ -590,9 +590,9 @@ describe("incrementUserAge resolver", () => {
590
590
  Once the logic gets more involved, mocking raw SQL calls becomes brittle. Push database access behind a `DbOperations` interface and test the pure function that uses it:
591
591
 
592
592
  ```typescript
593
+ import type { Namespace } from "../generated/db";
593
594
  // src/resolver/decrementUserAge.ts
594
595
  import type { Selectable } from "@tailor-platform/sdk/kysely";
595
- import type { Namespace } from "../generated/db";
596
596
 
597
597
  export interface DbOperations {
598
598
  transaction: <T>(fn: (ops: DbOperations) => Promise<T>) => Promise<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/sdk",
3
- "version": "2.28.0",
3
+ "version": "2.29.0",
4
4
  "description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -160,17 +160,16 @@
160
160
  "@jridgewell/trace-mapping": "0.3.31",
161
161
  "@napi-rs/keyring": "2.1.1",
162
162
  "@opentelemetry/api": "1.9.1",
163
- "@opentelemetry/exporter-trace-otlp-proto": "0.222.0",
164
- "@opentelemetry/resources": "2.11.0",
165
- "@opentelemetry/sdk-trace-node": "2.11.0",
163
+ "@opentelemetry/exporter-trace-otlp-proto": "0.223.0",
164
+ "@opentelemetry/resources": "2.12.0",
165
+ "@opentelemetry/sdk-trace-node": "2.12.0",
166
166
  "@opentelemetry/semantic-conventions": "1.43.0",
167
- "@oxc-project/types": "0.152.0",
168
- "@politty/zod": "0.4.3",
167
+ "@oxc-project/types": "0.154.0",
168
+ "@politty/zod": "0.5.1",
169
169
  "@secretlint/core": "13.0.7",
170
170
  "@secretlint/secretlint-rule-preset-recommend": "13.0.7",
171
171
  "@standard-schema/spec": "1.1.0",
172
172
  "@tailor-platform/function-kysely-tailordb": "0.1.3",
173
- "@toiroakr/lines-db": "0.15.0",
174
173
  "@toiroakr/read-multiline": "0.4.1",
175
174
  "@urql/core": "6.0.3",
176
175
  "amaro": "1.2.1",
@@ -184,9 +183,10 @@
184
183
  "graphql": "17.0.2",
185
184
  "inflection": "3.0.2",
186
185
  "kysely": "0.29.6",
186
+ "lines-db": "0.15.0",
187
187
  "mime-types": "3.0.2",
188
188
  "open": "11.0.4",
189
- "oxc-parser": "0.152.0",
189
+ "oxc-parser": "0.154.0",
190
190
  "p-limit": "7.3.3",
191
191
  "pathe": "2.0.3",
192
192
  "pgsql-ast-parser": "12.0.2",
@@ -200,26 +200,26 @@
200
200
  "ts-cron-validator": "1.1.5",
201
201
  "type-fest": "5.10.0",
202
202
  "xdg-basedir": "5.1.0",
203
- "zod": "4.6.5"
203
+ "zod": "4.6.7"
204
204
  },
205
205
  "devDependencies": {
206
- "@electric-sql/pglite": "0.5.8",
207
- "@opentelemetry/sdk-trace-base": "2.11.0",
206
+ "@electric-sql/pglite": "0.5.9",
207
+ "@opentelemetry/sdk-trace-base": "2.12.0",
208
208
  "@tailor-platform/shared": "^0.0.0",
209
209
  "@tailor-platform/tailor-proto": "^0.0.1",
210
210
  "@types/mime-types": "3.0.1",
211
211
  "@types/node": "24.19.2",
212
212
  "@types/semver": "7.8.0",
213
213
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
214
- "@vitest/coverage-v8": "5.0.3",
214
+ "@vitest/coverage-v8": "5.0.4",
215
215
  "eslint-plugin-zod": "5.0.0",
216
- "oxfmt": "0.71.0",
217
- "oxlint": "1.86.0",
218
- "oxlint-tsgolint": "7.0.2003",
216
+ "oxfmt": "0.73.0",
217
+ "oxlint": "1.88.0",
218
+ "oxlint-tsgolint": "7.0.2004",
219
219
  "sonda": "0.14.0",
220
220
  "tsdown": "0.23.1",
221
221
  "typescript": "6.0.3",
222
- "vitest": "5.0.3",
222
+ "vitest": "5.0.4",
223
223
  "zinfer": "0.4.7"
224
224
  },
225
225
  "peerDependencies": {