@tailor-platform/sdk 2.1.0 → 2.3.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 (98) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/{dist/cli/index.mjs → bin/tailor.mjs} +1 -1
  3. package/dist/application-BZfk4HKm.mjs +3 -0
  4. package/dist/{application-D77KJFKD.mjs → application-F-nl107y.mjs} +121 -27
  5. package/dist/application-F-nl107y.mjs.map +1 -0
  6. package/dist/cli/commands/deploy/tailordb/index.d.mts +2 -1
  7. package/dist/cli/commands/deploy/types.d.mts +2 -1
  8. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +52 -22
  9. package/dist/cli/commands/tailordb/migrate/file-state.d.mts +1 -0
  10. package/dist/cli/commands/tailordb/migrate/generate.d.mts +6 -0
  11. package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +22 -0
  12. package/dist/cli/commands/tailordb/migrate/schema-checks.d.mts +2 -2
  13. package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +9 -2
  14. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +30 -9
  15. package/dist/cli/lib.d.mts +4 -4
  16. package/dist/cli/lib.mjs +3 -482
  17. package/dist/cli/lib.mjs.map +1 -1
  18. package/dist/cli/main.mjs +1151 -208
  19. package/dist/cli/main.mjs.map +1 -1
  20. package/dist/cli/ts-hook.mjs +52 -7
  21. package/dist/completion/zsh-worker.zsh +167 -4
  22. package/dist/configure/index.d.mts +3 -3
  23. package/dist/configure/index.mjs +90 -23
  24. package/dist/configure/index.mjs.map +1 -1
  25. package/dist/configure/services/index.d.mts +3 -2
  26. package/dist/configure/services/tailordb/index.d.mts +2 -1
  27. package/dist/configure/services/tailordb/schema.d.mts +46 -46
  28. package/dist/configure/services/tailordb/types.d.mts +30 -4
  29. package/dist/configure/services/workflow/index.d.mts +2 -2
  30. package/dist/configure/services/workflow/wait-point.d.mts +72 -13
  31. package/dist/{crashreport-B5LqCTfF.mjs → crashreport-CIIfLnoO.mjs} +2 -2
  32. package/dist/{crashreport-B5LqCTfF.mjs.map → crashreport-CIIfLnoO.mjs.map} +1 -1
  33. package/dist/{crashreport-eI7ty7ok.mjs → crashreport-CINZ5dVN.mjs} +1 -1
  34. package/dist/errors-BVb6vYGy.mjs +89 -0
  35. package/dist/errors-BVb6vYGy.mjs.map +1 -0
  36. package/dist/kysely/index.d.mts +53 -5
  37. package/dist/kysely/index.mjs +7 -2
  38. package/dist/kysely/index.mjs.map +1 -1
  39. package/dist/{logger-CXQq9YIp.mjs → logger-DSdExfor.mjs} +83 -42
  40. package/dist/logger-DSdExfor.mjs.map +1 -0
  41. package/dist/plugin/builtin/seed/index.mjs +1 -1
  42. package/dist/{register-ts-hook-BU18uU44.mjs → register-ts-hook-ClI226n2.mjs} +3541 -703
  43. package/dist/register-ts-hook-ClI226n2.mjs.map +1 -0
  44. package/dist/schema--xYWRGfe.mjs.map +1 -1
  45. package/dist/seed/index.d.mts +52 -3
  46. package/dist/seed/index.mjs +200 -44
  47. package/dist/seed/index.mjs.map +1 -1
  48. package/dist/{seed-B-yckgQY.mjs → seed-xqM9WIHP.mjs} +8 -8
  49. package/dist/seed-xqM9WIHP.mjs.map +1 -0
  50. package/dist/{service-k1xy_A5G.mjs → service-C_WpbKHu.mjs} +39 -5
  51. package/dist/service-C_WpbKHu.mjs.map +1 -0
  52. package/dist/service-D1RCdzIL.mjs +3 -0
  53. package/dist/{service-B5WynBHQ.mjs → service-hZskxZmg.mjs} +3 -3
  54. package/dist/{service-B5WynBHQ.mjs.map → service-hZskxZmg.mjs.map} +1 -1
  55. package/dist/{service_pb-D5--My_M.mjs → service_pb-BEWj1pOV.mjs} +2 -2
  56. package/dist/{service_pb-D5--My_M.mjs.map → service_pb-BEWj1pOV.mjs.map} +1 -1
  57. package/dist/{service_pb-i8CmnQP4.mjs → service_pb-BjlhvK9E.mjs} +1 -1
  58. package/dist/utils/test/index.d.mts +6 -3
  59. package/dist/utils/test/index.mjs +25 -13
  60. package/dist/utils/test/index.mjs.map +1 -1
  61. package/dist/vitest/index.d.mts +2 -1
  62. package/dist/vitest/index.mjs +167 -3
  63. package/dist/vitest/index.mjs.map +1 -1
  64. package/dist/vitest/mocks/workflow.d.mts +13 -1
  65. package/dist/vitest/pglite-kysely.d.mts +43 -0
  66. package/dist/wait-point-invoker-__oE88_P.mjs +148 -0
  67. package/dist/wait-point-invoker-__oE88_P.mjs.map +1 -0
  68. package/dist/wait-point-registry-TL99zotw.mjs +47 -0
  69. package/dist/wait-point-registry-TL99zotw.mjs.map +1 -0
  70. package/dist/{workspace_resource_pb-DXnO2AT-.mjs → workspace_resource_pb-HNIL_Vfj.mjs} +10 -2
  71. package/dist/{workspace_resource_pb-DXnO2AT-.mjs.map → workspace_resource_pb-HNIL_Vfj.mjs.map} +1 -1
  72. package/docs/cli/setup.md +14 -1
  73. package/docs/cli/tailordb.md +96 -28
  74. package/docs/cli-reference.md +15 -12
  75. package/docs/configuration.md +1 -1
  76. package/docs/github-actions.md +73 -28
  77. package/docs/migration/v2.md +55 -1
  78. package/docs/plugin/custom.md +48 -48
  79. package/docs/plugin/index.md +24 -24
  80. package/docs/services/auth.md +10 -10
  81. package/docs/services/executor.md +1 -1
  82. package/docs/services/resolver.md +2 -2
  83. package/docs/services/tailordb-migration.md +485 -84
  84. package/docs/services/tailordb.md +28 -28
  85. package/docs/services/workflow.md +52 -2
  86. package/docs/testing.md +14 -0
  87. package/package.json +12 -7
  88. package/dist/application-Af1zIqSI.mjs +0 -3
  89. package/dist/application-D77KJFKD.mjs.map +0 -1
  90. package/dist/errors-D7583Zz7.mjs +0 -58
  91. package/dist/errors-D7583Zz7.mjs.map +0 -1
  92. package/dist/logger-CXQq9YIp.mjs.map +0 -1
  93. package/dist/register-ts-hook-BU18uU44.mjs.map +0 -1
  94. package/dist/seed-B-yckgQY.mjs.map +0 -1
  95. package/dist/service-M7OYziQP.mjs +0 -3
  96. package/dist/service-k1xy_A5G.mjs.map +0 -1
  97. package/dist/test-env-key-D7UkZp99.mjs +0 -75
  98. package/dist/test-env-key-D7UkZp99.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,204 @@
1
1
  # @tailor-platform/sdk
2
2
 
3
+ ## 2.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#2041](https://github.com/tailor-platform/sdk/pull/2041) [`0d2d542`](https://github.com/tailor-platform/sdk/commit/0d2d5420935a5f1f43f367f58837943fb49ad2dc) Thanks [@dqn](https://github.com/dqn)! - Add `tailor setup renovate` to generate a Renovate config that extends Tailor's shared preset without overwriting existing or customized configuration.
8
+
9
+ - [#2043](https://github.com/tailor-platform/sdk/pull/2043) [`2524e2f`](https://github.com/tailor-platform/sdk/commit/2524e2f0fff830c3ae8978d3136d6a7f0d22e0fe) Thanks [@dqn](https://github.com/dqn)! - Allow generated GitHub workflows to fail on unsuppressed setup drift when the `TAILOR_PLATFORM_FAIL_ON_DRIFT` repository variable is set to `true`. Execution and configuration errors in the drift check now fail regardless of this variable. Re-run the relevant `tailor setup` command after upgrading to regenerate the workflow.
10
+
11
+ - [#2035](https://github.com/tailor-platform/sdk/pull/2035) [`a928959`](https://github.com/tailor-platform/sdk/commit/a9289591359449da4157eb98a28c4beef14bc121) Thanks [@dqn](https://github.com/dqn)! - Convert a TailorDB field to a type that cannot be applied in place without writing the migrations by hand. `tailor tailordb migration generate` now offers to carry the values through a temporary field and writes both migrations for you; you supply the conversion expression. Non-interactive runs opt in per field with `--expand-contract "Type.field"`, and still fail without it.
12
+
13
+ Writes that land on the field while the conversion runs are dropped rather than converted, so stop writing to it first on a live workspace. Array-cardinality changes, unique fields, and fields named by an index, relationship, permission, or type-level script remain manual.
14
+
15
+ - [#2033](https://github.com/tailor-platform/sdk/pull/2033) [`d5801fc`](https://github.com/tailor-platform/sdk/commit/d5801fc94419359cfea2c3973b0d43333324510d) Thanks [@dqn](https://github.com/dqn)! - Support more TailorDB field type changes as single in-place migrations: `integer` now converts to `string` and `decimal`, `float` to `string` and `decimal`, `decimal` to `float`, and `boolean` to `string`. A `float` field that is already unique keeps to the 3-step migration when converting to `decimal`, because rounding to the target scale can merge distinct values.
16
+
17
+ - [#1991](https://github.com/tailor-platform/sdk/pull/1991) [`dcc66a6`](https://github.com/tailor-platform/sdk/commit/dcc66a61861cf4d94c6081e46d8fd9053d9f81e5) Thanks [@dqn](https://github.com/dqn)! - Renaming a TailorDB field no longer silently drops the field's data: `tailordb migration generate` now detects rename candidates and records a confirmed rename as a single breaking `field_renamed` change with a scaffolded data-copy script, instead of decomposing it into a removal plus an addition. Note: older SDK versions cannot read a `diff.json` that contains a `field_renamed` change and stop with a validation error.
18
+
19
+ - [#2026](https://github.com/tailor-platform/sdk/pull/2026) [`1346b75`](https://github.com/tailor-platform/sdk/commit/1346b75c1f3e2dd41297db18c6e7b8e676e01f77) Thanks [@dqn](https://github.com/dqn)! - Renaming a TailorDB type no longer silently drops its records: `tailordb migration generate` now detects type rename candidates and records a confirmed rename as a single breaking `type_renamed` change with a scaffolded id-preserving data-copy script, instead of decomposing it into a removal plus an addition (confirm interactively, or via `--rename "OldType:NewType"` / `--drop "Type"` in non-interactive runs). Note: older SDK versions cannot read a `diff.json` that contains a `type_renamed` change and stop with a validation error.
20
+
21
+ - [#1933](https://github.com/tailor-platform/sdk/pull/1933) [`89b5647`](https://github.com/tailor-platform/sdk/commit/89b5647c21624b3782af4e8bf3a0de39b10b1ed0) Thanks [@toiroakr](https://github.com/toiroakr)! - Support wait point keys with runtime values. Write `$paramName` in a key passed to `createWaitPoints`' `define` and the param names become the argument of `.with()`, which builds the concrete key:
22
+
23
+ ```typescript
24
+ export const { lineApproval } = createWaitPoints((define) => ({
25
+ lineApproval: define.for("line-approval-$lineId")<{ message: string }, { approved: boolean }>(),
26
+ }));
27
+
28
+ await lineApproval.with({ lineId: line.id }).wait({ message: "Please approve" });
29
+ ```
30
+
31
+ This makes it possible for one execution to suspend on the same logical wait point more than once at a time — one approval per order line, one per approver — which previously failed because a suspension with that key was already pending. A parameterized wait point exposes only `.with()`, so the unsubstituted key can never be waited on. `mockWorkflow().waitPointWith(definition, params)` gives typed mocks for one binding.
32
+
33
+ The key has to come before the `Payload` / `Result` type arguments, because TypeScript stops inferring it as a literal type once those are given explicitly, and the param names can only be read off a literal. `createWaitPoint` takes its type arguments first, so it cannot type `$params`; `deploy` rejects such a key and points at `createWaitPoints`. Its own signature is unchanged.
34
+
35
+ Wait point keys are now checked by `deploy` instead of failing when the job creates the suspension. Keys must match `[a-z0-9-]`, be 3 to 63 characters long, and start and end with `[a-z0-9]`. A key that never worked — a camelCase `createWaitPoints` property name, say — was rejected by the platform once the job ran; `deploy` now reports it before anything is deployed. Inside `createWaitPoints`, pass a valid key to `define` to keep the property name you read at the call site:
36
+
37
+ ```typescript
38
+ managerApproval: define.for("manager-approval")<{ amount: number }, { approved: boolean }>(),
39
+ ```
40
+
41
+ This works for a key without `$params` too, which is the way to keep a property name that reads well at the call site while the key stays within the platform grammar.
42
+
43
+ ### Patch Changes
44
+
45
+ - [#2025](https://github.com/tailor-platform/sdk/pull/2025) [`2574bdd`](https://github.com/tailor-platform/sdk/commit/2574bdd69947b0f6e050c3c0af224fc3f18c6435) Thanks [@dqn](https://github.com/dqn)! - Treat `migrate.test.ts` as a migration artifact: `tailordb migration rebaseline` now removes a test-only migration directory together with the history it belongs to, and aborts when one is added while its confirmation prompt is open.
46
+
47
+ - [#2012](https://github.com/tailor-platform/sdk/pull/2012) [`8826675`](https://github.com/tailor-platform/sdk/commit/88266754543c0975609274f0905c3a5b19cb9538) Thanks [@renovate](https://github.com/apps/renovate)! - chore(deps): lock file maintenance
48
+
49
+ - [#2014](https://github.com/tailor-platform/sdk/pull/2014) [`3852f3a`](https://github.com/tailor-platform/sdk/commit/3852f3a71dc5235c979b596cf23f1b252e3a3532) Thanks [@renovate](https://github.com/apps/renovate)! - chore(deps): update dependency tsx to v4.23.10
50
+
51
+ - [#2018](https://github.com/tailor-platform/sdk/pull/2018) [`6d13888`](https://github.com/tailor-platform/sdk/commit/6d13888b0fa07d078076ad3df9f3992f61419d6b) Thanks [@renovate](https://github.com/apps/renovate)! - fix(deps): update dependency @toiroakr/lines-db to v0.12.0
52
+
53
+ - [#2042](https://github.com/tailor-platform/sdk/pull/2042) [`6ee6584`](https://github.com/tailor-platform/sdk/commit/6ee65841670880986b392dabc69c06b1a2f3988f) Thanks [@dqn](https://github.com/dqn)! - Retry machine-user access token requests after transient connection timeouts.
54
+
55
+ - [#2032](https://github.com/tailor-platform/sdk/pull/2032) [`b173a0d`](https://github.com/tailor-platform/sdk/commit/b173a0de53c5235b8c201bc8c671ebf812953d1c) Thanks [@dqn](https://github.com/dqn)! - Workflows generated by `tailor setup preview` now include a `tailor-drift-check` step, so preview-only repositories audit their generated workflows in CI like branch, tag, and coordinator workflows already do. The check is warning-only and never fails the job. `tailor setup check` reports targets generated with an older template as outdated; re-run the matching `tailor setup` subcommand to regenerate them.
56
+
57
+ - [#2034](https://github.com/tailor-platform/sdk/pull/2034) [`413f6cb`](https://github.com/tailor-platform/sdk/commit/413f6cbf8faefefb155869f14e0ea76e939c1b41) Thanks [@dqn](https://github.com/dqn)! - Name the serial-column constraint in `db.insertInto(...).values({ ... })` and `.set({ ... })` errors, where TypeScript previously truncated the explanation away
58
+
59
+ - [#2051](https://github.com/tailor-platform/sdk/pull/2051) [`a3b980d`](https://github.com/tailor-platform/sdk/commit/a3b980da849c28b5faf985469fdde4a193ec269f) Thanks [@toiroakr](https://github.com/toiroakr)! - Fix `tailordb deploy`/`migrate` failing with `type_hook.create.expr: must be at most 10000 characters` on TailorDB types with many field hooks. The generated hook scripts are now optimized to avoid duplication.
60
+
61
+ Since generated hook/validate script text changes for any type with hooks, the first `tailordb migration generate` after upgrading will report a script update for those types even without any code changes on your side — this is expected and required for the fix to take effect.
62
+
63
+ - [#2049](https://github.com/tailor-platform/sdk/pull/2049) [`b3b2aae`](https://github.com/tailor-platform/sdk/commit/b3b2aae05a63e2514cdd796d9a8b16bf458f1fcd) Thanks [@dqn](https://github.com/dqn)! - TailorDB schema snapshots now keep their tables under a `tables` key instead of `types`, completing the `db.type()` → `db.table()` rename. Committed migration histories keep replaying: a legacy `types` key in `schema.json` is still read and moved on load, so no migration files need editing. Migration file format version is now 5 and this SDK reads versions 1 through 5.
64
+
65
+ Code that imports `SchemaSnapshot` or `NormalizedSchemaSnapshot` from `@tailor-platform/sdk/cli` and reads `snapshot.types` no longer compiles:
66
+
67
+ ```ts
68
+ Object.keys(snapshot.types); // before
69
+ Object.keys(snapshot.tables); // after
70
+ ```
71
+
72
+ This also applies to the snapshots returned by `compareSnapshots`, `createSnapshotFromLocalTypes`, `reconstructSnapshotFromMigrations`, and `compareLocalTypesWithSnapshot`.
73
+
74
+ The `types` key on parsed TailorDB service config is a different thing and is unchanged.
75
+
76
+ - [#2044](https://github.com/tailor-platform/sdk/pull/2044) [`8a5cc3f`](https://github.com/tailor-platform/sdk/commit/8a5cc3fe9a5f53f0128857f095c9b995f86c7408) Thanks [@dqn](https://github.com/dqn)! - TailorDB migration diffs now describe table-level changes as `table_*` instead of `type_*`, completing the `db.type()` → `db.table()` rename. Migration histories written by earlier versions keep working: `type_*` change kinds in committed `diff.json` files are still read and normalized on load, so no migration files need editing. Migration file format version is now 3 and this SDK reads versions 1 through 3. CLI diff output reads `[Table]` and `table(s) added` where it previously read `[Type]` and `type(s) added`.
77
+
78
+ Code that imports `MigrationDiff` / `DiffChange` from `@tailor-platform/sdk/cli` and compares `change.kind` against a renamed spelling no longer compiles, because the discriminant literal is gone from the union:
79
+
80
+ | Old spelling | New spelling |
81
+ | ------------------------ | ------------------------- |
82
+ | `type_added` | `table_added` |
83
+ | `type_removed` | `table_removed` |
84
+ | `type_renamed` | `table_renamed` |
85
+ | `type_modified` | `table_modified` |
86
+ | `type_settings_modified` | `table_settings_modified` |
87
+ | `type_scripts_modified` | `table_scripts_modified` |
88
+
89
+ Update those comparisons to the new spelling. Reading `diff.json` files is unaffected.
90
+
91
+ - [#2045](https://github.com/tailor-platform/sdk/pull/2045) [`c468a23`](https://github.com/tailor-platform/sdk/commit/c468a2327025dea9c3b5f8d0554010f5efba9211) Thanks [@dqn](https://github.com/dqn)! - TailorDB migration diffs now name the table they describe with `tableName` instead of `typeName`, and a rename records `previousTableName` instead of `previousTypeName`, continuing the `db.type()` → `db.table()` rename. Migration histories written by earlier versions keep replaying: the legacy field names in committed `diff.json` files are still read and normalized on load, across change entries, breaking changes, and warnings, so no migration files need editing. Migration file format version is now 4 and this SDK reads versions 1 through 4.
92
+
93
+ Code that imports `MigrationDiff` / `DiffChange` / `BreakingChangeInfo` from `@tailor-platform/sdk/cli` and reads the renamed properties no longer compiles:
94
+
95
+ | Old spelling | New spelling |
96
+ | ---------------------------- | ----------------------------- |
97
+ | `change.typeName` | `change.tableName` |
98
+ | `change.previousTypeName` | `change.previousTableName` |
99
+ | `breakingChanges[].typeName` | `breakingChanges[].tableName` |
100
+ | `warnings[].typeName` | `warnings[].tableName` |
101
+
102
+ Update those reads to the new spelling. `tailor tailordb migration validate --json` renames the same key wherever it appears in its output, so anything parsing that JSON needs the same update.
103
+
104
+ The `typeName` field on executor record triggers, the `.typeName()` field builder, and the file-runtime `typeName` argument are unrelated and unchanged.
105
+
106
+ - [#2052](https://github.com/tailor-platform/sdk/pull/2052) [`a31d362`](https://github.com/tailor-platform/sdk/commit/a31d362d4d7386a2f3dcce06f3c53c78537ffcec) Thanks [@toiroakr](https://github.com/toiroakr)! - Isolate the SDK test suite (every vitest project except e2e) from TAILOR_\* environment variables exported by the developer's shell, so tests pass regardless of local CLI configuration.
107
+
108
+ - [#2022](https://github.com/tailor-platform/sdk/pull/2022) [`4eb5302`](https://github.com/tailor-platform/sdk/commit/4eb530251e2480cd8179325fe43950c5cab5b954) Thanks [@dqn](https://github.com/dqn)! - Fail `deploy` and `tailordb migration validate` when a migration has both a recorded `--no-script` acknowledgment and a `migrate.ts`, instead of warning and running the script against the committed record. The error names the two ways out: rerun `tailordb migration script <n>` to clear the stale acknowledgment (the script then runs on the next deploy), or delete `migrate.ts` to keep the skip.
109
+
110
+ - [#2027](https://github.com/tailor-platform/sdk/pull/2027) [`794bab7`](https://github.com/tailor-platform/sdk/commit/794bab7ffec4c63f30a8b7a305bb34443573fbec) Thanks [@dqn](https://github.com/dqn)! - Reload user modules (config, tailordb types, workflows, resolvers, executors, HTTP adapters) on every `deploy()` run, so calling the programmatic `deploy()` repeatedly in one Node process picks up file changes and re-registers wait point keys instead of silently reusing modules cached by an earlier run.
111
+
112
+ - [#2023](https://github.com/tailor-platform/sdk/pull/2023) [`9dc826f`](https://github.com/tailor-platform/sdk/commit/9dc826fc0a83aa926de5b07245513f67c1ace877) Thanks [@dqn](https://github.com/dqn)! - Guide users through the v2 type-only import requirement. The CLI loads TypeScript by stripping types from each file in isolation, so a plain import of a type-only export fails at load time with `SyntaxError: ... does not provide an export named '<name>'` and no indication that the import form is the cause.
113
+
114
+ - The CLI now appends a suggestion to that error: import the name with `import type` and set `"verbatimModuleSyntax": true` in tsconfig.json to catch violations at typecheck.
115
+ - Projects scaffolded by `tailor init` now enable `verbatimModuleSyntax` in their tsconfig.json, so new projects catch violations at typecheck instead of at load time.
116
+ - The v2 migration guide gains a `v2/type-only-imports` entry documenting the requirement, the failure mode, and the migration steps, offered by `tailor upgrade` when crossing the v2 boundary.
117
+
118
+ ## 2.2.0
119
+
120
+ ### Minor Changes
121
+
122
+ - [#1992](https://github.com/tailor-platform/sdk/pull/1992) [`6e67451`](https://github.com/tailor-platform/sdk/commit/6e6745147d07d6d1a26291f3d2cc8510170ae407) Thanks [@dqn](https://github.com/dqn)! - TailorDB migration scripts can now be unit-tested before deploying:
123
+
124
+ - The generated `db.ts` exports the `Database` interface, so `createKyselyMock<Database>()` from `@tailor-platform/sdk/vitest` types queries against the migration's schema state.
125
+ - `tailor tailordb migration script <number> --with-test` scaffolds a ready-to-fill `migrate.test.ts` next to the migration script. When `migrate.ts` already exists (e.g. auto-generated for a breaking change), the flag adds only the test file.
126
+ - `createKyselyPGlite<Database>(new PGlite())` from `@tailor-platform/sdk/vitest` runs a migration script against an in-memory Postgres (`@electric-sql/pglite`, installed separately) to verify what it does to real rows.
127
+ - The migration guide's Testing section now documents the workflow.
128
+
129
+ - [#1975](https://github.com/tailor-platform/sdk/pull/1975) [`b5ca25b`](https://github.com/tailor-platform/sdk/commit/b5ca25b796e8a5e4262ea2e3527973d77cab766a) Thanks [@toiroakr](https://github.com/toiroakr)! - Add `tailor seed fill` to fill in the values a record gets on create for the JSONL seed data rows that are missing them. It fills `id` by default, so rows can reference each other by id, and `--fields` names any other create-time field:
130
+
131
+ ```bash
132
+ # ./seed/data/Customer.jsonl: {"name":"Acme Corporation"}
133
+ tailor seed fill
134
+ # ./seed/data/Customer.jsonl: {"id":"0b6b6f5e-...","name":"Acme Corporation"}
135
+
136
+ # also stamp a creation time on rows that have none
137
+ tailor seed fill --fields id,createdAt
138
+ ```
139
+
140
+ The values come from the type itself — its `id`, its field defaults, its create hooks — applied to each row on its own. Nothing is validated, so a row can be filled while a required field is still missing or while another file references an id that does not exist yet; that is the point, since the ids are what you need in order to write the rows that reference them. Run `tailor seed validate` when the data is ready.
141
+
142
+ Only the named fields are written, and only into a row that has no value for them, so a value already in the file is never replaced. A line that gains nothing is left byte for byte as it was; a line that takes a value is written with its keys in the order the type declares its fields, so a filled-in `id` lands at the front. A field the type gives no value to is skipped, so `--fields id` covers a whole data directory and leaves the IdP `_User` data alone, and naming a field the platform assigns — a `serial` field, for instance — fills nothing and says so.
143
+
144
+ `tailor seed apply --upsert` now points at the command when a row has no `id`, since that is the run it blocks, and newly scaffolded projects get a `seed:fill` script next to `seed:validate`.
145
+
146
+ The generated seed schema files now export the type's create hook, which is where the values come from. Run `tailor generate` after upgrading; until then `tailor seed fill` reports which file needs regenerating.
147
+
148
+ `createTailorDBHook` from `@tailor-platform/sdk/test` no longer runs the type's own `validate`. Computing the values a record gets on create and deciding whether a record is acceptable are separate jobs, and the second one now sits where the field-level validation already was: `createStandardSchema` takes the type as a third argument and reports type-level issues through its result.
149
+
150
+ `tailor seed validate` also stops printing two markers on the header of a failed run (`\u2716 \u2717 Found 2 error(s) in ...`), now that it hands the CLI a report that is already formatted.
151
+
152
+ That also fixes how `tailor seed validate` reports them. A type-level `validate` failure used to end the run at the first offending row with a bare message; it now lands in the same report as every other issue, naming the file and every row that fails.
153
+
154
+ A test calling `createTailorDBHook` directly to assert a type-level `validate` throws needs to go through `createStandardSchema` instead.
155
+
156
+ The same operation is available as `fillSeedData` from `@tailor-platform/sdk/seed`.
157
+
158
+ - [#1979](https://github.com/tailor-platform/sdk/pull/1979) [`07c50a6`](https://github.com/tailor-platform/sdk/commit/07c50a6d5728827b937160a97182c3996c00baad) Thanks [@toiroakr](https://github.com/toiroakr)! - Add `TailorDBColumns` / `TailorDBInsertable` / `TailorDBSelectable` / `TailorDBUpdateable` to `@tailor-platform/sdk/kysely`. They accept either a table (`typeof myTable`) or a bare field collection, so code that is generic over the fields can derive create/read/update inputs the same way the generated table types do: `.serial()` fields are never caller-supplied, `.default()` and `.hooks({ create })` fields may be omitted on create, optional fields stay optional, and `id` is generated. `IsReadOnlyDBField` and `IsAutoFilledDBField` are also exported from `@tailor-platform/sdk` for asking whether callers can never write a single field, or may omit it on create.
159
+
160
+ Supplying a value for a `.serial()` column now fails with the reason instead of `'<column>' does not exist in type ...`, which read as a typo:
161
+
162
+ ```
163
+ Type 'string' is not assignable to type 'TypeLevelError<"assigned by .serial(); remove it from the input">'.
164
+ ```
165
+
166
+ This applies to the tables `kyselyTypePlugin` generates as well, because it comes from `Serial`. A serial column is now an omittable key on `Insertable`/`Updateable` rather than an absent one, so `keyof Insertable<Table<"MyType">>` includes it, and copying a whole record into a create input (`{ ...row }`) now reports the serial column instead of silently dropping it. Assigning the same values as before still compiles, and passing `undefined` is equivalent to omitting the column.
167
+
168
+ `Insertable`, `Selectable` and `Updateable` from the generated `Namespace` also print as a flat object rather than an intersection, so assignability errors name the shape directly.
169
+
170
+ `TailorDBField` gains an optional third type parameter carrying a nested object's own fields, so `db.object({ at: db.datetime() })` keeps that shape through the builder chain. Without it a date or datetime nested in an object resolved to `string | Date` (or `string`) while the runtime hands back a `Date`. Writing `TailorDBField<Defined, Output>` still works.
171
+
172
+ - [#1988](https://github.com/tailor-platform/sdk/pull/1988) [`f45ed5b`](https://github.com/tailor-platform/sdk/commit/f45ed5b380620d58f332fd2dd8fc5bb92ea28ecd) Thanks [@dqn](https://github.com/dqn)! - Add `tailor tailordb migration test` to rehearse pending migrations in an isolated workspace. The command can load generated seed fixtures or clone source TailorDB records, run the real migration phases, optionally execute a Kysely assertion script, and deletes workspaces it creates unless `--keep` is passed.
173
+
174
+ - [#1984](https://github.com/tailor-platform/sdk/pull/1984) [`2f8457d`](https://github.com/tailor-platform/sdk/commit/2f8457d433b583769b136ece9a0c483cfccf6cdd) Thanks [@dqn](https://github.com/dqn)! - Add `--strict` to `tailordb migration validate`: validation additionally fails when a migration not yet applied to the remote has data-loss warnings (e.g. a removed field or type) but neither a `migrate.ts` nor a recorded `--no-script` acknowledgment. The failure names the affected type and field and prints the exact command to record the acknowledgment, so CI can require destructive changes to be explicitly acknowledged before merge.
175
+
176
+ To support this, `tailordb migration script <number> --no-script --reason "..."` now accepts warning-tier migrations (previously it was rejected unless the migration required a script), and `migration generate` offers to record the reason interactively when it detects warnings. Additionally, running `migration script <number>` when `migrate.ts` already exists now clears a stale `--no-script` acknowledgment from `diff.json` instead of failing, resolving the previously unclearable state after hand-adding a script.
177
+
178
+ `diff.json` files written before data-loss warnings existed now have their warnings reconstructed from the recorded field/type removals when loaded, so `validate --strict` and `--no-script` acknowledgments treat them the same as newly generated migrations.
179
+
180
+ - [#1993](https://github.com/tailor-platform/sdk/pull/1993) [`ecff3b2`](https://github.com/tailor-platform/sdk/commit/ecff3b2267f666de0ef83aef6b7727913e724a35) Thanks [@dqn](https://github.com/dqn)! - Add generated in-place TailorDB migrations for verified field type changes. Migration scripts normalize existing values while the previous field contract remains active, and the target type is applied in the post-migration phase. Generated scripts intentionally fail TypeScript checks until their normalization logic is reviewed.
181
+
182
+ - [#1990](https://github.com/tailor-platform/sdk/pull/1990) [`36715da`](https://github.com/tailor-platform/sdk/commit/36715da7a125863c9dadd99cdcbb5fd45a51b4c9) Thanks [@dqn](https://github.com/dqn)! - Add a safe `tailordb migration rebaseline` workflow, let checked deployments adopt the new baseline, keep migration history IDs aligned across recovery commands, and reject unsupported migration file formats with upgrade guidance
183
+
184
+ ### Patch Changes
185
+
186
+ - [#1960](https://github.com/tailor-platform/sdk/pull/1960) [`50743ad`](https://github.com/tailor-platform/sdk/commit/50743ad4cb38dc8f237934d3f9c905f2512ff6e4) Thanks [@toiroakr](https://github.com/toiroakr)! - Resolve the `tailor`, `tailor-seed`, and `tailor-tailordb-erd` executables through committed compile-cache launchers. The `tailor seed` command is now available as soon as the plugin is installed, and the seed and ERD plugin CLIs reuse Node's on-disk compile cache for faster warm starts.
187
+
188
+ - [#1934](https://github.com/tailor-platform/sdk/pull/1934) [`2a3ec7b`](https://github.com/tailor-platform/sdk/commit/2a3ec7b108275410bf5b35d23784b07aa147c5ea) Thanks [@toiroakr](https://github.com/toiroakr)! - Drop the `chalk` dependency in favor of Node's built-in styling, and decide color support per output stream. Diagnostics on stderr now keep their colors when you redirect stdout (`tailor executor list > out.txt`), and stop writing escape codes into the file when you redirect stderr (`tailor deploy 2> log.txt`). `NO_COLOR`, `FORCE_COLOR` and non-TTY detection keep working as before.
189
+
190
+ `@tailor-platform/sdk-codemod`, `@tailor-platform/sdk-plugin-seed` and `@tailor-platform/sdk-plugin-tailordb-erd` now declare the Node and Bun versions they need (`node >=22.15.0`, `bun >=1.2.0`), matching `@tailor-platform/sdk`. Installing them on an older runtime reports the mismatch instead of failing once the CLI runs.
191
+
192
+ - [#1985](https://github.com/tailor-platform/sdk/pull/1985) [`ab23d97`](https://github.com/tailor-platform/sdk/commit/ab23d97e79eb2be6fb3f9d7ddd21515c1bf1c244) Thanks [@dqn](https://github.com/dqn)! - Copyable migration-script hint commands — from `migration generate`, `migration validate --strict`, and deploy's missing-script error — are now rendered by one shell-aware formatter. Values are quoted for the platform shell, and on Windows a value containing `%`, `$`, or `!` (which cmd.exe/PowerShell expand even inside double quotes) switches the hint to an argv rendering instead of a command line that would resolve to a different path. The deploy hint now also pads the migration number, binds `--config` with the `=` form, and omits it for the default config path.
193
+
194
+ - [#1999](https://github.com/tailor-platform/sdk/pull/1999) [`4364357`](https://github.com/tailor-platform/sdk/commit/436435740510accbdb3aa627fe4439bd1656bb96) Thanks [@renovate](https://github.com/apps/renovate)! - fix(deps): update dependency globals to v17.9.0
195
+
196
+ - [#1994](https://github.com/tailor-platform/sdk/pull/1994) [`7e804d4`](https://github.com/tailor-platform/sdk/commit/7e804d464e40a73fb524338cf7eda42e88f5ede8) Thanks [@dqn](https://github.com/dqn)! - Make `tailor tailordb migration validate` reject generated field type normalization scaffolds until their review marker is removed.
197
+
198
+ - [#2008](https://github.com/tailor-platform/sdk/pull/2008) [`f2135ab`](https://github.com/tailor-platform/sdk/commit/f2135ab6dd3d130d88803b9829a26024de930ed3) Thanks [@toiroakr](https://github.com/toiroakr)! - Consistently call a TailorDB schema definition a "table" instead of a "type" across the docs, matching the `db.type()` → `db.table()` rename. Also fix three leftover `db.type(...)` code samples in `docs/services/tailordb.md` that should have read `db.table(...)`.
199
+
200
+ Update the `v2/idp-publish-events-rename` codemod registry description to say "tables" instead of "types", matching the same wording fix.
201
+
3
202
  ## 2.1.0
4
203
 
5
204
  ### Minor Changes
@@ -11,4 +11,4 @@ try {
11
11
  // politty is not resolvable from here (e.g. a fully bundled CLI) —
12
12
  // start without the compile cache rather than failing the CLI.
13
13
  }
14
- await import("./main.mjs");
14
+ await import("../dist/cli/main.mjs");
@@ -0,0 +1,3 @@
1
+ import { n as generatePluginFilesIfNeeded, r as loadApplication, t as defineApplication } from "./application-F-nl107y.mjs";
2
+
3
+ export { defineApplication, generatePluginFilesIfNeeded };
@@ -1,10 +1,11 @@
1
1
  import { n as isSdkBranded } from "./brand-Eo4pLXPJ.mjs";
2
+ import { n as getScopedWaitPoints } from "./wait-point-registry-TL99zotw.mjs";
2
3
  import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
3
- import { a as parseBoolean, n as logger, r as styles } from "./logger-CXQq9YIp.mjs";
4
- import { a as AuthConfigSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, g as functionSchema, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema } from "./service-k1xy_A5G.mjs";
4
+ import { a as parseBoolean, n as logger, r as styles } from "./logger-DSdExfor.mjs";
5
+ import { a as AuthConfigSchema, b as functionSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema, y as importUserModule } from "./service-C_WpbKHu.mjs";
5
6
  import { t as multiline } from "./multiline-sfHpTZZK.mjs";
6
7
  import { t as userAgent } from "./user-agent-Bgsszb5I.mjs";
7
- import { t as createCLIError } from "./errors-D7583Zz7.mjs";
8
+ import { t as createCLIError } from "./errors-BVb6vYGy.mjs";
8
9
  import { n as isNodeBuiltinImport, t as getNodeBuiltinMessage } from "./node-builtins-CmaL2Cbq.mjs";
9
10
  import { t as isPluginGeneratedType } from "./type-source-DH_LH20p.mjs";
10
11
  import { createRequire } from "node:module";
@@ -16,7 +17,6 @@ import { create } from "@bufbuild/protobuf";
16
17
  import * as fs from "node:fs";
17
18
  import { readFileSync } from "node:fs";
18
19
  import { MethodOptions_IdempotencyLevel } from "@bufbuild/protobuf/wkt";
19
- import { pathToFileURL } from "node:url";
20
20
  import { OAuth2Client } from "@badgateway/oauth2-client";
21
21
  import { Code, ConnectError, createClient } from "@connectrpc/connect";
22
22
  import pLimit from "p-limit";
@@ -182,7 +182,7 @@ function initOAuth2Client(config) {
182
182
  */
183
183
  async function initOperatorClient(accessToken, config) {
184
184
  const platformConfig = config ?? getPlatformConfigForToken(accessToken);
185
- const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-i8CmnQP4.mjs")]);
185
+ const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-BjlhvK9E.mjs")]);
186
186
  const interceptors = [
187
187
  await userAgentInterceptor(),
188
188
  await bearerTokenInterceptor(accessToken),
@@ -253,7 +253,7 @@ function retryInterceptor() {
253
253
  return (next) => async (req) => {
254
254
  if (req.stream) return await next(req);
255
255
  let lastError;
256
- for (let i = 0; i < 3; i++) {
256
+ for (let i = 0; i < MAX_RETRY_ATTEMPTS; i++) {
257
257
  if (i > 0) await waitRetryBackoff(i);
258
258
  try {
259
259
  return await next(req);
@@ -263,7 +263,7 @@ function retryInterceptor() {
263
263
  logger.debug(`retry: ${req.method.name} returned AlreadyExists on attempt ${i + 1}; treating as success (prior attempt likely committed)`);
264
264
  return synthesizeEmptyUnaryResponse(req);
265
265
  }
266
- const { reportCrash } = await import("./crashreport-eI7ty7ok.mjs");
266
+ const { reportCrash } = await import("./crashreport-CINZ5dVN.mjs");
267
267
  await reportCrash(error, "handledError");
268
268
  }
269
269
  if (req.method.name !== "CreateWorkspace" && isRetirable(error, req.method.idempotency)) {
@@ -397,6 +397,8 @@ function synthesizeEmptyUnaryResponse(req) {
397
397
  * response was lost), which is what triggers the `already_exists` race.
398
398
  */
399
399
  const RETRY_BASE_DELAY_MS = 500;
400
+ /** Maximum number of attempts, including the initial one, for a retried request. */
401
+ const MAX_RETRY_ATTEMPTS = 3;
400
402
  /**
401
403
  * Wait for an exponential backoff delay with jitter.
402
404
  * @param attempt - Current retry attempt number (1-based)
@@ -631,14 +633,15 @@ async function fetchMachineUserToken(url, clientId, clientSecret) {
631
633
  formData.append("grant_type", "client_credentials");
632
634
  formData.append("client_id", clientId);
633
635
  formData.append("client_secret", clientSecret);
634
- const resp = await fetch(tokenEndpoint, {
636
+ const request = {
635
637
  method: "POST",
636
638
  headers: {
637
639
  "User-Agent": await userAgent(),
638
640
  "Content-Type": "application/x-www-form-urlencoded"
639
641
  },
640
642
  body: formData
641
- });
643
+ };
644
+ const resp = await withConnectTimeoutRetry("machine user token request", () => fetch(tokenEndpoint, request));
642
645
  if (!resp.ok) {
643
646
  const body = await resp.text().catch(() => "");
644
647
  throw new Error(`Failed to fetch machine user token: ${resp.status} ${resp.statusText} ${body.slice(0, 500)}`);
@@ -650,6 +653,29 @@ async function fetchMachineUserToken(url, clientId, clientSecret) {
650
653
  expires_in: z.number()
651
654
  }).parse(rawJson);
652
655
  }
656
+ function isUndiciConnectTimeout(error) {
657
+ if (!(error instanceof TypeError)) return false;
658
+ const cause = error.cause;
659
+ return cause instanceof Error && "code" in cause && cause.code === "UND_ERR_CONNECT_TIMEOUT";
660
+ }
661
+ /**
662
+ * Retry a request that failed before the connection was established.
663
+ *
664
+ * Only `UND_ERR_CONNECT_TIMEOUT` is retried: the request provably never reached
665
+ * the server, so replaying it cannot duplicate a server-side effect.
666
+ * @param label - Request description for the retry debug log
667
+ * @param send - Sends the request; called once per attempt
668
+ * @returns The first successful result
669
+ */
670
+ async function withConnectTimeoutRetry(label, send) {
671
+ for (let attempt = 1;; attempt++) try {
672
+ return await send();
673
+ } catch (error) {
674
+ if (!isUndiciConnectTimeout(error) || attempt >= MAX_RETRY_ATTEMPTS) throw error;
675
+ logger.debug(`retry: ${label} attempt ${attempt} failed with UND_ERR_CONNECT_TIMEOUT; retrying`);
676
+ await waitRetryBackoff(attempt);
677
+ }
678
+ }
653
679
  /**
654
680
  * Fetch an OAuth2 token for a platform machine user via client_credentials grant.
655
681
  * @param clientId - Client ID for the platform machine user
@@ -658,12 +684,13 @@ async function fetchMachineUserToken(url, clientId, clientSecret) {
658
684
  * @returns OAuth2 token
659
685
  */
660
686
  async function fetchPlatformMachineUserToken(clientId, clientSecret, config) {
661
- return await new OAuth2Client({
687
+ const server = getPlatformBaseUrl(config);
688
+ return await withConnectTimeoutRetry("platform machine user token request", () => new OAuth2Client({
662
689
  clientId,
663
690
  clientSecret,
664
- server: getPlatformBaseUrl(config),
691
+ server,
665
692
  discoveryEndpoint: oauth2DiscoveryEndpoint
666
- }).clientCredentials();
693
+ }).clientCredentials());
667
694
  }
668
695
  /**
669
696
  * Close the global HTTP connection pool to prevent libuv UV_HANDLE_CLOSING
@@ -2579,6 +2606,7 @@ const tailorPrincipalMap = makePrincipalExpr({
2579
2606
  attributeList: "$raw.attributes ?? []"
2580
2607
  }
2581
2608
  });
2609
+ const PRINCIPAL_VAR = "_principal";
2582
2610
  /**
2583
2611
  * Parse `wrapped` and return the first property of the top-level parenthesized
2584
2612
  * object expression, or `undefined` if it does not parse as one.
@@ -2630,17 +2658,17 @@ function formatScriptContext(kind, context) {
2630
2658
  const convertToScriptExpr = (fn, kind, context) => {
2631
2659
  const precompiledExpr = getPrecompiledScriptExpr(fn);
2632
2660
  if (precompiledExpr) return precompiledExpr;
2633
- return assertParsableExpression(`(${stringifyFunction(fn)})(${kind === "validate" ? `{ value: _value }` : kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }`})`, formatScriptContext(kind, context));
2661
+ return assertParsableExpression(`(${stringifyFunction(fn)})(${kind === "validate" ? `{ value: _value }` : kind === "hooks.create" ? `{ input: _value, invoker: ${PRINCIPAL_VAR}, now: _now }` : `{ input: _value, oldValue: _oldValue, invoker: ${PRINCIPAL_VAR}, now: _now }`})`, formatScriptContext(kind, context));
2634
2662
  };
2635
2663
  const convertTypeHookToExpr = (fn) => {
2636
2664
  const precompiledExpr = getPrecompiledScriptExpr(fn);
2637
2665
  if (precompiledExpr) return precompiledExpr;
2638
- return assertParsableExpression(`(${stringifyFunction(fn)})({ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now })`, "type-hook");
2666
+ return assertParsableExpression(`(${stringifyFunction(fn)})({ input: _input, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR}, now: _now })`, "type-hook");
2639
2667
  };
2640
2668
  const convertTypeValidateToExpr = (fn) => {
2641
2669
  const precompiledExpr = getPrecompiledScriptExpr(fn);
2642
2670
  if (precompiledExpr) return precompiledExpr;
2643
- return assertParsableExpression(`(${stringifyFunction(fn)})({ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues)`, "type-validate");
2671
+ return assertParsableExpression(`(${stringifyFunction(fn)})({ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR} }, __issues)`, "type-validate");
2644
2672
  };
2645
2673
  /**
2646
2674
  * Parse TailorDBField into OperatorFieldConfig.
@@ -3386,7 +3414,7 @@ async function bundleScriptTarget(args) {
3386
3414
  const context = `${kind} in ${sourceFilePath}`;
3387
3415
  const fnSource = stringifyFunction(fn);
3388
3416
  if ((kind === "typeValidate" || kind === "validate") && fn.constructor.name === "AsyncFunction") throw new Error(`${context} must be synchronous — the generated validator runs synchronously, so issues reported after an await are silently lost. Remove the async keyword.`);
3389
- const argsObject = kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "hooks.update" ? `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "validate" ? `{ value: _value }` : kind === "typeHook" ? `{ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now }` : `{ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues`;
3417
+ const argsObject = kind === "hooks.create" ? `{ input: _value, invoker: ${PRINCIPAL_VAR}, now: _now }` : kind === "hooks.update" ? `{ input: _value, oldValue: _oldValue, invoker: ${PRINCIPAL_VAR}, now: _now }` : kind === "validate" ? `{ value: _value }` : kind === "typeHook" ? `{ input: _input, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR}, now: _now }` : `{ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR} }, __issues`;
3390
3418
  const inlineExpr = assertParsableExpression(`(${fnSource})(${argsObject})`, context);
3391
3419
  const freeVars = findUndefinedReferences(`const __fn = ${fnSource};`);
3392
3420
  if (freeVars.size === 0) return inlineExpr;
@@ -3650,7 +3678,7 @@ function createTailorDBService(params) {
3650
3678
  rawTypes[typeFile] = createRawTypesByName();
3651
3679
  const loadedTypes = createRawTypesByName();
3652
3680
  try {
3653
- const module = await import(pathToFileURL(typeFile).href);
3681
+ const module = await importUserModule(typeFile);
3654
3682
  for (const exportName of Object.keys(module)) {
3655
3683
  const exportedValue = module[exportName];
3656
3684
  const result = TailorDBTypeSchema.safeParse(stripTailorDBTypeBuilderHelpers(exportedValue));
@@ -3666,10 +3694,11 @@ function createTailorDBService(params) {
3666
3694
  filePath: typeFile,
3667
3695
  exportName
3668
3696
  });
3669
- if (exportedValue.plugins && Array.isArray(exportedValue.plugins) && exportedValue.plugins.length > 0) {
3670
- pluginAttachments.set(exportedValue.name, [...exportedValue.plugins]);
3671
- logger.log(` Plugin attachments: ${styles.info(exportedValue.plugins.map((p) => p.pluginId).join(", "))}`);
3672
- await processPluginsForType(exportedValue, exportedValue.plugins, typeFile);
3697
+ const rawType = exportedValue;
3698
+ if (rawType.plugins && Array.isArray(rawType.plugins) && rawType.plugins.length > 0) {
3699
+ pluginAttachments.set(rawType.name, [...rawType.plugins]);
3700
+ logger.log(` Plugin attachments: ${styles.info(rawType.plugins.map((p) => p.pluginId).join(", "))}`);
3701
+ await processPluginsForType(rawType, rawType.plugins, typeFile);
3673
3702
  }
3674
3703
  }
3675
3704
  } catch (error) {
@@ -4437,7 +4466,7 @@ async function loadAdapterFiles(config, baseDir) {
4437
4466
  }
4438
4467
  async function loadAdapterFromFile(filePath) {
4439
4468
  try {
4440
- const module = await import(pathToFileURL(filePath).href);
4469
+ const module = await importUserModule(filePath);
4441
4470
  if (!isSdkBranded(module.default, "http-adapter")) {
4442
4471
  const named = Object.entries(module).find(([exportName, value]) => exportName !== "default" && isSdkBranded(value, "http-adapter"));
4443
4472
  if (named) throw new Error(`HTTP adapter must be the default export, but it is exported as \`${named[0]}\`. Re-export it: \`export default createHttpAdapter({...})\`.`);
@@ -4533,7 +4562,7 @@ const ResolverSchema = z.strictObject({
4533
4562
  * @returns Parsed resolver or null if invalid
4534
4563
  */
4535
4564
  async function loadResolver(resolverFilePath) {
4536
- const resolver = (await import(pathToFileURL(resolverFilePath).href)).default;
4565
+ const resolver = (await importUserModule(resolverFilePath)).default;
4537
4566
  const parseResult = ResolverSchema.safeParse(resolver);
4538
4567
  if (!parseResult.success) return null;
4539
4568
  return parseResult.data;
@@ -4718,7 +4747,7 @@ function createResolverService(namespace, config, baseDir) {
4718
4747
  });
4719
4748
  const loadResolverForFile = async (resolverFile) => {
4720
4749
  try {
4721
- const resolverModule = await import(pathToFileURL(resolverFile).href);
4750
+ const resolverModule = await importUserModule(resolverFile);
4722
4751
  const result = ResolverSchema.safeParse(resolverModule.default);
4723
4752
  if (result.success) {
4724
4753
  const relativePath = path.relative(process.cwd(), resolverFile);
@@ -5260,7 +5289,7 @@ async function loadFileContent(filePath) {
5260
5289
  const jobs = [];
5261
5290
  let workflow = null;
5262
5291
  try {
5263
- const module = await import(pathToFileURL(filePath).href);
5292
+ const module = await importUserModule(filePath);
5264
5293
  for (const [exportName, exportValue] of Object.entries(module)) {
5265
5294
  if (exportName === "default") {
5266
5295
  const workflowResult = WorkflowSchema.safeParse(stripRuntimeStart(exportValue));
@@ -5530,6 +5559,65 @@ const StaticWebsiteSchema = z.strictObject({
5530
5559
  customDomains: z.array(z.string()).optional().describe("Custom domains for the static website")
5531
5560
  }).brand("StaticWebsiteConfig");
5532
5561
 
5562
+ //#endregion
5563
+ //#region src/parser/service/workflow/wait-point-key.ts
5564
+ const KEY_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
5565
+ const LITERAL_SEGMENT_REGEX = /^[a-z0-9]+$/;
5566
+ const PARAM_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
5567
+ const MAX_KEY_LENGTH = 63;
5568
+ const KEY_GRAMMAR = "[a-z0-9-] (3-63 characters; must start and end with [a-z0-9])";
5569
+ /**
5570
+ * Check one declared wait point key against the key rules.
5571
+ *
5572
+ * The declaration matters: only `createWaitPoints`' `define` receives the key
5573
+ * before the type arguments, so only there can TypeScript read the `$params`
5574
+ * off the key literal.
5575
+ * @param waitPoint - A key as declared, with the declaration it came from
5576
+ * @returns The rule the key breaks, or undefined when the key is usable
5577
+ */
5578
+ function checkWaitPointKey(waitPoint) {
5579
+ const { key, declaredBy } = waitPoint;
5580
+ const segments = key.split("-");
5581
+ const paramSegments = segments.filter((segment) => segment.startsWith("$") && segment.length > 1);
5582
+ if (paramSegments.length > 0 && declaredBy !== "define") return declaredBy === "property" ? `Invalid wait point key "${key}": $params cannot come from a property name. Pass the key to define instead, e.g. define.for("${key}")<Payload, Result>().` : `Invalid wait point key "${key}": createWaitPoint takes its type arguments first, which stops TypeScript inferring the key as a literal, so it cannot type the $params. Declare it through createWaitPoints instead: createWaitPoints((define) => ({ myWaitPoint: define.for("${key}")<Payload, Result>() })).`;
5583
+ const seen = /* @__PURE__ */ new Set();
5584
+ let literals = 0;
5585
+ for (const segment of segments) {
5586
+ if (segment.startsWith("$")) {
5587
+ const name = segment.slice(1);
5588
+ if (!PARAM_NAME_REGEX.test(name)) return `Invalid wait point key "${key}": "${segment}" is not a usable parameter name. Use letters, digits and underscores, starting with a letter or underscore.`;
5589
+ if (seen.has(name)) return `Invalid wait point key "${key}": parameter "$${name}" appears more than once.`;
5590
+ seen.add(name);
5591
+ continue;
5592
+ }
5593
+ if (segment === "") continue;
5594
+ if (!LITERAL_SEGMENT_REGEX.test(segment)) return `Invalid wait point key "${key}": segment "${segment}" may only contain [a-z0-9]. Wait point keys accept ${KEY_GRAMMAR}, with $params standing in for runtime values.`;
5595
+ literals += 1;
5596
+ }
5597
+ if (literals === 0 && paramSegments.length > 0) return key.startsWith("-") || key.endsWith("-") ? `Invalid wait point key "${key}": must match ${KEY_GRAMMAR}.` : `Invalid wait point key "${key}": it needs at least one literal segment alongside its $params, otherwise the key carries no identity of its own and can collide with an unrelated wait point.`;
5598
+ if (paramSegments.length === 0) return KEY_REGEX.test(key) ? void 0 : `Invalid wait point key "${key}": must match ${KEY_GRAMMAR}.`;
5599
+ const shortest = segments.map((segment) => segment.startsWith("$") ? "0" : segment).join("-");
5600
+ if (shortest.length > MAX_KEY_LENGTH) return `Wait point key "${key}" cannot fit in ${MAX_KEY_LENGTH} characters: even single-character parameter values produce ${shortest.length}.`;
5601
+ return KEY_REGEX.test(shortest) ? void 0 : `Invalid wait point key "${key}": must match ${KEY_GRAMMAR}.`;
5602
+ }
5603
+ /**
5604
+ * Check every declared wait point key, reporting each broken rule once.
5605
+ * @param waitPoints - The keys declared across the project, as registered
5606
+ * @returns One message per distinct declaration that breaks a rule
5607
+ */
5608
+ function collectWaitPointKeyFailures(waitPoints) {
5609
+ const failures = [];
5610
+ const seen = /* @__PURE__ */ new Set();
5611
+ for (const waitPoint of waitPoints) {
5612
+ const identity = `${waitPoint.declaredBy} ${waitPoint.key}`;
5613
+ if (seen.has(identity)) continue;
5614
+ seen.add(identity);
5615
+ const failure = checkWaitPointKey(waitPoint);
5616
+ if (failure) failures.push(failure);
5617
+ }
5618
+ return failures;
5619
+ }
5620
+
5533
5621
  //#endregion
5534
5622
  //#region src/cli/services/application.ts
5535
5623
  function defineTailorDB(config, baseDir, pluginManager) {
@@ -5803,6 +5891,11 @@ function generatePluginFilesIfNeeded(pluginManager, tailorDBServices, configPath
5803
5891
  executorGenerator: generatePluginExecutorFiles
5804
5892
  });
5805
5893
  }
5894
+ function assertWaitPointKeys() {
5895
+ const failures = collectWaitPointKeyFailures(getScopedWaitPoints());
5896
+ if (failures.length === 0) return;
5897
+ throw new Error(failures.join("\n"));
5898
+ }
5806
5899
  /**
5807
5900
  * Load and fully initialize a Tailor application.
5808
5901
  * This performs all I/O-heavy operations: loading types, processing plugins,
@@ -5895,6 +5988,7 @@ async function loadApplication(params) {
5895
5988
  await executorService.loadExecutors();
5896
5989
  if (pluginExecutorFiles.length > 0) await executorService.loadPluginExecutorFiles([...pluginExecutorFiles]);
5897
5990
  }
5991
+ assertWaitPointKeys();
5898
5992
  if (workflowService) workflowService.printLoadedWorkflows();
5899
5993
  if (httpAdapterService) httpAdapterService.printLoadedAdapters();
5900
5994
  logger.newline();
@@ -5921,5 +6015,5 @@ async function loadApplication(params) {
5921
6015
  }
5922
6016
 
5923
6017
  //#endregion
5924
- export { createApplyLimiter as $, getDistDir as A, fetchUserInfo as B, composeFunctionTreeshakeOptions as C, getPluginGenerationDependencies as D, createBundleLog as E, fetchAll as F, initOAuth2Client as G, getOAuth2ClientId as H, fetchAllTolerant as I, isNotFoundError as J, initOperatorClient as K, fetchMachineUserToken as L, hashFile as M, closeConnectionPool as N, hasGenerationHooks as O, defaultPlatformBaseUrl as P, byName as Q, fetchPaged as R, platformBundleDefinePlugin as S, resolveBundleLogLevel as T, getOrNull as U, getConsoleBaseUrl as V, getPlatformBaseUrl as W, rememberPlatformConfigForToken as X, normalizeBaseUrl as Y, resolveStaticWebsiteUrls as Z, assertUniqueTailorDBTypeNamesWithExternal as _, resolveInlineSourcemap as a, createTsconfigPathsPlugin as b, WorkflowJobSchema as c, HTTP_METHODS as d, LOG_LEVELS as et, INVOKER_EXPR as f, assertUniqueLocalTailorDBTypeNames as g, buildResolverPermissionAndInputCheckExpr as h, resolverBundleKey as i, hashContent as j, createBundleCache as k, resolveResolverDefaultPermissionForFile as l, buildResolverOperationHookExpr as m, generatePluginFilesIfNeeded as n, getApplicationAuthNamespace as o, buildExecutorArgsExpr as p, isDefaultPlatform as q, loadApplication as r, WorkflowJobFunctionExecutionPolicySchema as s, defineApplication as t, ResolverSchema as u, stringifyFunction as v, createLogLevelTreeshakeOptions as w, resolveTSConfigWithFallback as x, createGeneratedEntryResolverPlugin as y, fetchPlatformMachineUserToken as z };
5925
- //# sourceMappingURL=application-D77KJFKD.mjs.map
6018
+ export { resolveStaticWebsiteUrls as $, hasGenerationHooks as A, fetchPaged as B, resolveTSConfigWithFallback as C, resolveBundleLogLevel as D, createLogLevelTreeshakeOptions as E, closeConnectionPool as F, getOrNull as G, fetchUserInfo as H, defaultPlatformBaseUrl as I, initOperatorClient as J, getPlatformBaseUrl as K, fetchAll as L, getDistDir as M, hashContent as N, createBundleLog as O, hashFile as P, rememberPlatformConfigForToken as Q, fetchAllTolerant as R, createTsconfigPathsPlugin as S, composeFunctionTreeshakeOptions as T, getConsoleBaseUrl as U, fetchPlatformMachineUserToken as V, getOAuth2ClientId as W, isNotFoundError as X, isDefaultPlatform as Y, normalizeBaseUrl as Z, assertUniqueTailorDBTypeNamesWithExternal as _, resolveInlineSourcemap as a, tailorPrincipalMap as b, WorkflowJobSchema as c, HTTP_METHODS as d, byName as et, INVOKER_EXPR as f, assertUniqueLocalTailorDBTypeNames as g, buildResolverPermissionAndInputCheckExpr as h, resolverBundleKey as i, createBundleCache as j, getPluginGenerationDependencies as k, resolveResolverDefaultPermissionForFile as l, buildResolverOperationHookExpr as m, generatePluginFilesIfNeeded as n, LOG_LEVELS as nt, getApplicationAuthNamespace as o, buildExecutorArgsExpr as p, initOAuth2Client as q, loadApplication as r, WorkflowJobFunctionExecutionPolicySchema as s, defineApplication as t, createApplyLimiter as tt, ResolverSchema as u, PRINCIPAL_VAR as v, platformBundleDefinePlugin as w, createGeneratedEntryResolverPlugin as x, stringifyFunction as y, fetchMachineUserToken as z };
6019
+ //# sourceMappingURL=application-F-nl107y.mjs.map