@tailor-platform/sdk 2.2.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 (55) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/application-BZfk4HKm.mjs +3 -0
  3. package/dist/{application-BpLeawBg.mjs → application-F-nl107y.mjs} +118 -24
  4. package/dist/application-F-nl107y.mjs.map +1 -0
  5. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +43 -20
  6. package/dist/cli/commands/tailordb/migrate/generate.d.mts +6 -0
  7. package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +22 -0
  8. package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +1 -1
  9. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +26 -7
  10. package/dist/cli/lib.mjs +2 -2
  11. package/dist/cli/main.mjs +203 -42
  12. package/dist/cli/main.mjs.map +1 -1
  13. package/dist/cli/ts-hook.mjs +52 -7
  14. package/dist/completion/zsh-worker.zsh +23 -3
  15. package/dist/configure/index.d.mts +2 -2
  16. package/dist/configure/index.mjs +90 -23
  17. package/dist/configure/index.mjs.map +1 -1
  18. package/dist/configure/services/index.d.mts +2 -2
  19. package/dist/configure/services/workflow/index.d.mts +2 -2
  20. package/dist/configure/services/workflow/wait-point.d.mts +72 -13
  21. package/dist/{errors-CWj21238.mjs → errors-BVb6vYGy.mjs} +16 -2
  22. package/dist/{errors-CWj21238.mjs.map → errors-BVb6vYGy.mjs.map} +1 -1
  23. package/dist/kysely/index.d.mts +2 -1
  24. package/dist/kysely/index.mjs.map +1 -1
  25. package/dist/{register-ts-hook-CTth1eqj.mjs → register-ts-hook-ClI226n2.mjs} +2034 -511
  26. package/dist/register-ts-hook-ClI226n2.mjs.map +1 -0
  27. package/dist/{service-goqlJJgA.mjs → service-C_WpbKHu.mjs} +38 -4
  28. package/dist/service-C_WpbKHu.mjs.map +1 -0
  29. package/dist/service-D1RCdzIL.mjs +3 -0
  30. package/dist/{service-CCwl3Avt.mjs → service-hZskxZmg.mjs} +2 -2
  31. package/dist/{service-CCwl3Avt.mjs.map → service-hZskxZmg.mjs.map} +1 -1
  32. package/dist/vitest/index.mjs +83 -1
  33. package/dist/vitest/index.mjs.map +1 -1
  34. package/dist/vitest/mocks/file.d.mts +1 -1
  35. package/dist/vitest/mocks/workflow.d.mts +13 -1
  36. package/dist/wait-point-invoker-__oE88_P.mjs +148 -0
  37. package/dist/wait-point-invoker-__oE88_P.mjs.map +1 -0
  38. package/dist/wait-point-registry-TL99zotw.mjs +47 -0
  39. package/dist/wait-point-registry-TL99zotw.mjs.map +1 -0
  40. package/docs/cli/setup.md +14 -1
  41. package/docs/cli/tailordb.md +9 -6
  42. package/docs/cli-reference.md +2 -1
  43. package/docs/github-actions.md +73 -28
  44. package/docs/migration/v2.md +54 -0
  45. package/docs/services/tailordb-migration.md +137 -35
  46. package/docs/services/workflow.md +52 -2
  47. package/docs/testing.md +12 -0
  48. package/package.json +4 -4
  49. package/dist/application-BpLeawBg.mjs.map +0 -1
  50. package/dist/application-BxLLiLsr.mjs +0 -3
  51. package/dist/register-ts-hook-CTth1eqj.mjs.map +0 -1
  52. package/dist/service-_XmjSEGr.mjs +0 -3
  53. package/dist/service-goqlJJgA.mjs.map +0 -1
  54. package/dist/test-env-key-D7UkZp99.mjs +0 -75
  55. package/dist/test-env-key-D7UkZp99.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,120 @@
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
+
3
118
  ## 2.2.0
4
119
 
5
120
  ### Minor Changes
@@ -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
4
  import { a as parseBoolean, n as logger, r as styles } from "./logger-DSdExfor.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-goqlJJgA.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-CWj21238.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";
@@ -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);
@@ -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-BpLeawBg.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