@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.
- package/CHANGELOG.md +115 -0
- package/dist/application-BZfk4HKm.mjs +3 -0
- package/dist/{application-BpLeawBg.mjs → application-F-nl107y.mjs} +118 -24
- package/dist/application-F-nl107y.mjs.map +1 -0
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +43 -20
- package/dist/cli/commands/tailordb/migrate/generate.d.mts +6 -0
- package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +22 -0
- package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +1 -1
- package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +26 -7
- package/dist/cli/lib.mjs +2 -2
- package/dist/cli/main.mjs +203 -42
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/ts-hook.mjs +52 -7
- package/dist/completion/zsh-worker.zsh +23 -3
- package/dist/configure/index.d.mts +2 -2
- package/dist/configure/index.mjs +90 -23
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/index.d.mts +2 -2
- package/dist/configure/services/workflow/index.d.mts +2 -2
- package/dist/configure/services/workflow/wait-point.d.mts +72 -13
- package/dist/{errors-CWj21238.mjs → errors-BVb6vYGy.mjs} +16 -2
- package/dist/{errors-CWj21238.mjs.map → errors-BVb6vYGy.mjs.map} +1 -1
- package/dist/kysely/index.d.mts +2 -1
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/{register-ts-hook-CTth1eqj.mjs → register-ts-hook-ClI226n2.mjs} +2034 -511
- package/dist/register-ts-hook-ClI226n2.mjs.map +1 -0
- package/dist/{service-goqlJJgA.mjs → service-C_WpbKHu.mjs} +38 -4
- package/dist/service-C_WpbKHu.mjs.map +1 -0
- package/dist/service-D1RCdzIL.mjs +3 -0
- package/dist/{service-CCwl3Avt.mjs → service-hZskxZmg.mjs} +2 -2
- package/dist/{service-CCwl3Avt.mjs.map → service-hZskxZmg.mjs.map} +1 -1
- package/dist/vitest/index.mjs +83 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mocks/file.d.mts +1 -1
- package/dist/vitest/mocks/workflow.d.mts +13 -1
- package/dist/wait-point-invoker-__oE88_P.mjs +148 -0
- package/dist/wait-point-invoker-__oE88_P.mjs.map +1 -0
- package/dist/wait-point-registry-TL99zotw.mjs +47 -0
- package/dist/wait-point-registry-TL99zotw.mjs.map +1 -0
- package/docs/cli/setup.md +14 -1
- package/docs/cli/tailordb.md +9 -6
- package/docs/cli-reference.md +2 -1
- package/docs/github-actions.md +73 -28
- package/docs/migration/v2.md +54 -0
- package/docs/services/tailordb-migration.md +137 -35
- package/docs/services/workflow.md +52 -2
- package/docs/testing.md +12 -0
- package/package.json +4 -4
- package/dist/application-BpLeawBg.mjs.map +0 -1
- package/dist/application-BxLLiLsr.mjs +0 -3
- package/dist/register-ts-hook-CTth1eqj.mjs.map +0 -1
- package/dist/service-_XmjSEGr.mjs +0 -3
- package/dist/service-goqlJJgA.mjs.map +0 -1
- package/dist/test-env-key-D7UkZp99.mjs +0 -75
- 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
|
|
@@ -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,
|
|
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-
|
|
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 <
|
|
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
|
|
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
|
-
|
|
687
|
+
const server = getPlatformBaseUrl(config);
|
|
688
|
+
return await withConnectTimeoutRetry("platform machine user token request", () => new OAuth2Client({
|
|
662
689
|
clientId,
|
|
663
690
|
clientSecret,
|
|
664
|
-
server
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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
|
|
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
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
5925
|
-
//# sourceMappingURL=application-
|
|
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
|