@tailor-platform/sdk 2.0.0-next.5 → 2.0.0-next.6

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 (118) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/{application-Bf8wxOi6.mjs → application-BJXRpQj5.mjs} +190 -1258
  3. package/dist/application-BJXRpQj5.mjs.map +1 -0
  4. package/dist/application-BV-AXawv.mjs +3 -0
  5. package/dist/cli/commands/deploy/deploy.d.mts +8 -3
  6. package/dist/cli/commands/generate/options.d.mts +0 -1
  7. package/dist/cli/commands/generate/service.d.mts +1 -1
  8. package/dist/cli/commands/staticwebsite/deploy.d.mts +19 -0
  9. package/dist/cli/commands/workspace/create.d.mts +3 -0
  10. package/dist/cli/commands/workspace/list.d.mts +1 -0
  11. package/dist/cli/commands/workspace/transform.d.mts +2 -0
  12. package/dist/cli/index.mjs +776 -1551
  13. package/dist/cli/index.mjs.map +1 -1
  14. package/dist/cli/lib.d.mts +7 -3
  15. package/dist/cli/lib.mjs +49 -3
  16. package/dist/cli/lib.mjs.map +1 -1
  17. package/dist/cli/shared/config-loader.d.mts +1 -1
  18. package/dist/cli/shared/readonly-guard.d.mts +25 -0
  19. package/dist/cli/shared/tailordb-namespaces.d.mts +37 -0
  20. package/dist/cli/shared/workspace-name.d.mts +1 -0
  21. package/dist/completion/zsh-worker.zsh +15 -303
  22. package/dist/configure/index.mjs +7 -6
  23. package/dist/configure/index.mjs.map +1 -1
  24. package/dist/configure/services/executor/executor.d.mts +2 -13
  25. package/dist/configure/services/executor/operation.d.mts +7 -2
  26. package/dist/configure/services/tailordb/schema.d.mts +1 -7
  27. package/dist/configure/services/tailordb/types.d.mts +1 -1
  28. package/dist/configure/services/workflow/execution-policy.d.mts +2 -2
  29. package/dist/configure/types/type.d.mts +1 -9
  30. package/dist/{crashreport-D3RjuSYb.mjs → crashreport-0ioNRZ4K.mjs} +1 -1
  31. package/dist/{crashreport-Cg-pqRJV.mjs → crashreport-CxHne11E.mjs} +4 -3
  32. package/dist/{crashreport-Cg-pqRJV.mjs.map → crashreport-CxHne11E.mjs.map} +1 -1
  33. package/dist/errors-DfnF4ibq.mjs +100 -0
  34. package/dist/errors-DfnF4ibq.mjs.map +1 -0
  35. package/dist/{globals-BjhJJ2dN.mjs → globals-D-YbJKW-.mjs} +16 -10
  36. package/dist/globals-D-YbJKW-.mjs.map +1 -0
  37. package/dist/{logger-BEiZZ3qT.mjs → logger-BwS4ppwO.mjs} +3 -3
  38. package/dist/logger-BwS4ppwO.mjs.map +1 -0
  39. package/dist/parser/service/tailordb/type-source.d.mts +10 -0
  40. package/dist/{register-ts-hook-BhzVkej0.mjs → register-ts-hook-DL31O2W9.mjs} +2623 -1645
  41. package/dist/register-ts-hook-DL31O2W9.mjs.map +1 -0
  42. package/dist/{registry-BSbq6SPo.mjs → registry-CC3CbQiF.mjs} +3 -3
  43. package/dist/registry-CC3CbQiF.mjs.map +1 -0
  44. package/dist/runtime/field-parse.d.mts +10 -0
  45. package/dist/runtime/globals.d.mts +5 -2
  46. package/dist/runtime/index.mjs +1 -1
  47. package/dist/runtime/workflow.d.mts +70 -10
  48. package/dist/runtime/workflow.mjs +1 -1
  49. package/dist/{schema-iCd1ugGp.mjs → schema--xYWRGfe.mjs} +42 -24
  50. package/dist/schema--xYWRGfe.mjs.map +1 -0
  51. package/dist/{secret-file-aoNXJmKa.mjs → secret-file-C3J4Gy8r.mjs} +2 -27
  52. package/dist/secret-file-C3J4Gy8r.mjs.map +1 -0
  53. package/dist/{secretmanager-Bd45j7an.mjs → secretmanager-IY4UvinW.mjs} +42 -9
  54. package/dist/secretmanager-IY4UvinW.mjs.map +1 -0
  55. package/dist/{service-CzZEVrp9.mjs → service-CnHz9rwz.mjs} +34 -12
  56. package/dist/service-CnHz9rwz.mjs.map +1 -0
  57. package/dist/{service-DeZeYa06.mjs → service-DaeJlBoH.mjs} +3 -3
  58. package/dist/{service-DeZeYa06.mjs.map → service-DaeJlBoH.mjs.map} +1 -1
  59. package/dist/service-nU6ITOHL.mjs +3 -0
  60. package/dist/service_pb-BoLM0X4K.mjs +167 -0
  61. package/dist/service_pb-BoLM0X4K.mjs.map +1 -0
  62. package/dist/{service_pb-DZkfjB_6.mjs → service_pb-r1T7kpJc.mjs} +1 -1
  63. package/dist/types/executor.generated.d.mts +60 -42
  64. package/dist/user-agent-Bgsszb5I.mjs +28 -0
  65. package/dist/user-agent-Bgsszb5I.mjs.map +1 -0
  66. package/dist/vitest/environment.mjs +1 -1
  67. package/dist/vitest/index.d.mts +8 -8
  68. package/dist/vitest/index.mjs +591 -173
  69. package/dist/vitest/index.mjs.map +1 -1
  70. package/dist/vitest/mock.d.mts +8 -8
  71. package/dist/vitest/mocks/aigateway.d.mts +14 -4
  72. package/dist/vitest/mocks/authconnection.d.mts +14 -4
  73. package/dist/vitest/mocks/file.d.mts +23 -6
  74. package/dist/vitest/mocks/iconv.d.mts +28 -4
  75. package/dist/vitest/mocks/idp.d.mts +25 -9
  76. package/dist/vitest/mocks/secretmanager.d.mts +13 -4
  77. package/dist/vitest/mocks/tailordb.d.mts +48 -7
  78. package/dist/vitest/mocks/workflow.d.mts +68 -22
  79. package/dist/vitest/setup.mjs +2 -2
  80. package/dist/workflow-9kHGKxF2.mjs +64 -0
  81. package/dist/workflow-9kHGKxF2.mjs.map +1 -0
  82. package/dist/{service_pb-Bk7dL-yT.mjs → workspace_resource_pb-Db3fv68L.mjs} +47 -165
  83. package/dist/workspace_resource_pb-Db3fv68L.mjs.map +1 -0
  84. package/docs/cli/application.md +61 -15
  85. package/docs/cli/tailordb.md +8 -140
  86. package/docs/cli/workspace.md +6 -4
  87. package/docs/cli-reference.md +11 -8
  88. package/docs/github-actions.md +8 -0
  89. package/docs/migration/v2.md +41 -0
  90. package/docs/runtime.md +5 -6
  91. package/docs/services/executor.md +5 -0
  92. package/docs/services/tailordb.md +44 -0
  93. package/docs/services/workflow.md +2 -2
  94. package/docs/testing.md +105 -89
  95. package/package.json +2 -5
  96. package/dist/application-Bf8wxOi6.mjs.map +0 -1
  97. package/dist/application-CUkjzfij.mjs +0 -3
  98. package/dist/cli/erd-viewer-assets/app.js +0 -1424
  99. package/dist/cli/erd-viewer-assets/index.html +0 -77
  100. package/dist/cli/erd-viewer-assets/serve.json +0 -13
  101. package/dist/cli/erd-viewer-assets/styles.css +0 -1036
  102. package/dist/configure/types/field-runtime.d.mts +0 -16
  103. package/dist/errors-118wUDRr.mjs +0 -42
  104. package/dist/errors-118wUDRr.mjs.map +0 -1
  105. package/dist/globals-BjhJJ2dN.mjs.map +0 -1
  106. package/dist/logger-BEiZZ3qT.mjs.map +0 -1
  107. package/dist/register-ts-hook-BhzVkej0.mjs.map +0 -1
  108. package/dist/registry-BSbq6SPo.mjs.map +0 -1
  109. package/dist/resource_pb-BRv7AMXK.mjs +0 -49
  110. package/dist/resource_pb-BRv7AMXK.mjs.map +0 -1
  111. package/dist/schema-iCd1ugGp.mjs.map +0 -1
  112. package/dist/secret-file-aoNXJmKa.mjs.map +0 -1
  113. package/dist/secretmanager-Bd45j7an.mjs.map +0 -1
  114. package/dist/service-BT0GPd01.mjs +0 -3
  115. package/dist/service-CzZEVrp9.mjs.map +0 -1
  116. package/dist/service_pb-Bk7dL-yT.mjs.map +0 -1
  117. package/dist/workflow-GNuSnsTx.mjs +0 -22
  118. package/dist/workflow-GNuSnsTx.mjs.map +0 -1
@@ -1,291 +1,31 @@
1
1
  import { n as isSdkBranded } from "./brand-Eo4pLXPJ.mjs";
2
- import { i as PageDirection } from "./resource_pb-BRv7AMXK.mjs";
3
2
  import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
4
- import { a as parseBoolean, n as logger, r as styles } from "./logger-BEiZZ3qT.mjs";
5
- 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-CzZEVrp9.mjs";
3
+ import { a as parseBoolean, n as logger, r as styles } from "./logger-BwS4ppwO.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-CnHz9rwz.mjs";
6
5
  import { t as multiline } from "./multiline-sfHpTZZK.mjs";
7
- import { t as readPackageJson } from "./package-json-8b0O9TlX.mjs";
8
- import { i as userAgent, n as tightenSecretFilePermissions, r as writeSecretFile } from "./secret-file-aoNXJmKa.mjs";
9
- import { t as createCLIError } from "./errors-118wUDRr.mjs";
6
+ import { t as userAgent } from "./user-agent-Bgsszb5I.mjs";
10
7
  import { t as isPluginGeneratedType } from "./type-source-DH_LH20p.mjs";
11
8
  import { builtinModules, createRequire } from "node:module";
12
9
  import * as path from "pathe";
13
- import { join, resolve } from "pathe";
10
+ import { resolve } from "pathe";
14
11
  import { resolveTSConfig } from "pkg-types";
15
- import { arg } from "politty";
16
12
  import { z } from "zod";
17
13
  import { create } from "@bufbuild/protobuf";
18
- import * as fs$1 from "node:fs";
19
- import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
20
- import { parseEnv } from "node:util";
14
+ import * as fs from "node:fs";
15
+ import { readFileSync } from "node:fs";
21
16
  import { MethodOptions_IdempotencyLevel } from "@bufbuild/protobuf/wkt";
22
17
  import { pathToFileURL } from "node:url";
23
- import { parseYAML, stringifyYAML } from "confbox";
24
- import { findUpSync } from "find-up-simple";
25
- import { lt } from "semver";
26
- import { xdgConfig } from "xdg-basedir";
27
18
  import { OAuth2Client } from "@badgateway/oauth2-client";
28
19
  import { Code, ConnectError, createClient } from "@connectrpc/connect";
29
20
  import { getGlobalDispatcher } from "undici";
30
21
  import pLimit from "p-limit";
31
22
  import * as crypto from "node:crypto";
32
23
  import * as rolldown from "rolldown";
33
- import * as fs from "node:fs/promises";
34
24
  import { parseSync } from "oxc-parser";
35
25
  import * as inflection from "inflection";
36
26
  import * as globals from "globals";
37
27
  import * as os from "node:os";
38
28
 
39
- //#region src/cli/shared/args.ts
40
- const unitToMs = {
41
- ms: 1,
42
- s: 1e3,
43
- m: 60 * 1e3
44
- };
45
- const durationPattern = /^(\d+)(ms|s|m)$/;
46
- /**
47
- * Schema for duration string validation (e.g., "3s", "500ms", "1m")
48
- * Only validates format; use parseDuration() to convert to milliseconds
49
- */
50
- const durationArg = z.string().refine((val) => durationPattern.test(val), { message: "Invalid duration format. Expected format: '3s', '500ms', '1m'" }).refine((val) => {
51
- const match = val.match(durationPattern);
52
- if (!match) return false;
53
- const digits = match[1];
54
- return digits !== void 0 && parseInt(digits, 10) > 0;
55
- }, { message: "Duration must be greater than 0" });
56
- /**
57
- * Parse a validated duration string into milliseconds
58
- * @param duration - Duration string (e.g., "3s", "500ms", "1m")
59
- * @returns Duration in milliseconds
60
- */
61
- function parseDuration(duration) {
62
- const match = assertDefined(duration.match(durationPattern), `invalid duration format: ${duration}`);
63
- const value = parseInt(assertDefined(match[1], "duration digits group missing"), 10);
64
- const unit = assertDefined(match[2], "duration unit group missing");
65
- return value * unitToMs[unit];
66
- }
67
- /**
68
- * Schema for positive integer validation (from string input)
69
- * Transforms the string to a number
70
- */
71
- const positiveIntArg = z.coerce.number().int().positive();
72
- /**
73
- * Schema for non-negative integer validation (from string input).
74
- * Accepts 0 (used for `--limit 0` to disable the limit).
75
- */
76
- const nonNegativeIntArg = z.coerce.number().int().nonnegative();
77
- /**
78
- * Schema for sort order (`asc` or `desc`).
79
- */
80
- const orderArg = z.enum(["asc", "desc"]);
81
- /**
82
- * Translate a CLI `--order` value into the proto `PageDirection` enum.
83
- * Returns `undefined` when the user did not specify an order so that
84
- * callers can omit the field and fall back to the server default.
85
- * @param order - Order string from CLI args (`"asc"` | `"desc"` | undefined)
86
- * @returns PageDirection, or undefined when `order` is undefined
87
- */
88
- function toPageDirection(order) {
89
- if (order === void 0) return void 0;
90
- return order === "asc" ? PageDirection.ASC : PageDirection.DESC;
91
- }
92
- function hasMachineUserFlag(argv) {
93
- return argv.slice(0, argv.indexOf("--") === -1 ? argv.length : argv.indexOf("--")).some((token) => token === "-m" || token.startsWith("-m=") || token === "--machine-user" || token.startsWith("--machine-user=") || token === "--machineUser" || token.startsWith("--machineUser=") || token === "--machineuser" || token.startsWith("--machineuser="));
94
- }
95
- /**
96
- * Resolve whether a parsed machine user value came from an explicit CLI option or env fallback.
97
- * @param machineUser - Parsed machine user value
98
- * @param argv - Raw CLI argv, excluding the executable and script path
99
- * @param options - Source resolution options
100
- * @returns Machine user input source, or undefined when no value was parsed
101
- */
102
- function resolveMachineUserInputSource(machineUser, argv = process.argv.slice(2), options = {}) {
103
- if (machineUser === void 0) return void 0;
104
- if (options.valueIsExplicit) return "option";
105
- if (hasMachineUserFlag(argv)) return "option";
106
- return process.env.TAILOR_PLATFORM_MACHINE_USER_NAME === machineUser ? "env" : "option";
107
- }
108
- /**
109
- * Load env files from parsed arguments.
110
- * Processes --env-file first, then --env-file-if-exists.
111
- *
112
- * Follows Node.js --env-file behavior:
113
- * - Variables already set in the environment are NOT overwritten
114
- * - Variables from later files override those from earlier files
115
- * @param envFiles - Required env file path(s) that must exist
116
- * @param envFilesIfExists - Optional env file path(s) that are loaded if they exist
117
- */
118
- function loadEnvFiles(envFiles, envFilesIfExists) {
119
- const originalEnvKeys = new Set(Object.keys(process.env));
120
- const load = (files, required) => {
121
- for (const file of [files ?? []].flat()) {
122
- const envPath = path.resolve(process.cwd(), file);
123
- if (!fs$1.existsSync(envPath)) {
124
- if (required) throw new Error(`Environment file not found: ${envPath}`);
125
- continue;
126
- }
127
- const parsed = parseEnv(fs$1.readFileSync(envPath, "utf-8"));
128
- for (const [key, value] of Object.entries(parsed)) {
129
- if (originalEnvKeys.has(key)) continue;
130
- process.env[key] = value;
131
- }
132
- }
133
- };
134
- load(envFiles, true);
135
- load(envFilesIfExists, false);
136
- }
137
- /**
138
- * Common arguments for all CLI commands
139
- *
140
- * NOTE: --env-file and --env-file-if-exists collide with Node.js flags due to a bug
141
- * (https://github.com/nodejs/node/issues/54232). Node.js parses these even after the
142
- * script path, causing warnings (twice due to tsx loader).
143
- */
144
- const commonArgs = {
145
- "env-file": arg(z.string().optional(), {
146
- alias: "e",
147
- description: "Path to the environment file (error if not found)",
148
- completion: {
149
- type: "file",
150
- matcher: [".env.*", ".env"]
151
- }
152
- }),
153
- "env-file-if-exists": arg(z.string().optional(), {
154
- description: "Path to the environment file (ignored if not found)",
155
- completion: {
156
- type: "file",
157
- matcher: [".env.*", ".env"]
158
- },
159
- effect: (_value, { args }) => {
160
- loadEnvFiles(args["env-file"], args["env-file-if-exists"]);
161
- }
162
- }),
163
- verbose: arg(z.boolean().default(false), {
164
- description: "Enable verbose logging",
165
- effect: (value) => {
166
- verboseMode = value;
167
- }
168
- }),
169
- json: arg(z.boolean().default(false), {
170
- alias: "j",
171
- description: "Output as JSON",
172
- effect: (value) => {
173
- logger.jsonMode = value;
174
- }
175
- })
176
- };
177
- /**
178
- * Arguments for commands that require workspace context
179
- */
180
- const workspaceArgs = {
181
- "workspace-id": arg(z.string().optional(), {
182
- alias: "w",
183
- description: "Workspace ID",
184
- env: "TAILOR_PLATFORM_WORKSPACE_ID",
185
- completion: { type: "none" }
186
- }),
187
- profile: arg(z.string().optional(), {
188
- alias: "p",
189
- description: "Workspace profile",
190
- env: "TAILOR_PLATFORM_PROFILE",
191
- completion: { type: "none" }
192
- })
193
- };
194
- /**
195
- * Shared config arg for commands that accept a config file path
196
- */
197
- const configArg = { config: arg(z.string().default("tailor.config.ts"), {
198
- alias: "c",
199
- description: "Path to Tailor config file",
200
- env: "TAILOR_CONFIG_PATH",
201
- completion: {
202
- type: "file",
203
- extensions: ["ts"]
204
- }
205
- }) };
206
- /**
207
- * Shared config arg for commands that accept one or more comma-separated config file paths
208
- */
209
- const multiConfigArg = { config: arg(z.string().default("tailor.config.ts"), {
210
- alias: "c",
211
- description: "Path to SDK config file. Use comma-separated paths to deploy multiple apps together.",
212
- env: "TAILOR_PLATFORM_SDK_CONFIG_PATH",
213
- completion: {
214
- type: "file",
215
- extensions: ["ts"]
216
- }
217
- }) };
218
- /**
219
- * Arguments for commands that interact with deployed resources (includes config)
220
- */
221
- const deploymentArgs = {
222
- ...workspaceArgs,
223
- ...configArg
224
- };
225
- /**
226
- * Arguments for commands that require confirmation
227
- */
228
- const confirmationArgs = { yes: arg(z.boolean().default(false), {
229
- alias: "y",
230
- description: "Skip confirmation prompts"
231
- }) };
232
- /**
233
- * Arguments for commands that require organization context
234
- */
235
- const organizationArgs = { "organization-id": arg(z.string(), {
236
- alias: "o",
237
- description: "Organization ID",
238
- env: "TAILOR_PLATFORM_ORGANIZATION_ID",
239
- completion: { type: "none" }
240
- }) };
241
- /**
242
- * Arguments for list commands that accept `--order` / `--limit`. Sort
243
- * order defaults to `desc` (newest first) because most callers want the
244
- * latest items; pass `--order asc` to opt in to ascending order. The
245
- * limit is unbounded by default so existing invocations keep returning
246
- * every item; pass `--limit N` to cap the result size.
247
- * @param defaultOrder - Default value for `--order` (defaults to `"desc"`)
248
- * @returns Argument shape suitable for spreading into a command schema
249
- */
250
- const paginationArgs = (defaultOrder = "desc") => ({
251
- order: arg(orderArg.default(defaultOrder), { description: "Sort order (asc or desc)" }),
252
- limit: arg(nonNegativeIntArg.optional(), {
253
- alias: "l",
254
- description: "Maximum number of items to return (0 or omit: unlimited)"
255
- })
256
- });
257
- /**
258
- * Arguments for time-series log list commands. Defaults to newest-first
259
- * (`desc`) and a 50-item cap so that listing stays responsive on busy
260
- * workspaces. Pass `--limit 0` to disable the cap and fetch all entries.
261
- */
262
- const pagedLogArgs = {
263
- order: arg(orderArg.default("desc"), { description: "Sort order (asc or desc)" }),
264
- limit: arg(nonNegativeIntArg.default(50), {
265
- alias: "l",
266
- description: "Maximum number of items to return (0: unlimited)"
267
- })
268
- };
269
- /**
270
- * Arguments for commands that require folder context
271
- */
272
- const folderArgs = { "folder-id": arg(z.string(), {
273
- alias: "f",
274
- description: "Folder ID",
275
- env: "TAILOR_PLATFORM_FOLDER_ID",
276
- completion: { type: "none" }
277
- }) };
278
- let verboseMode = false;
279
- /**
280
- * Returns whether verbose mode is enabled.
281
- * Used by the global cleanup handler which doesn't have access to parsed args.
282
- * @returns Whether verbose mode is enabled
283
- */
284
- function isVerbose() {
285
- return verboseMode;
286
- }
287
-
288
- //#endregion
289
29
  //#region src/parser/app-config/log-level.ts
290
30
  const LOG_LEVELS = [
291
31
  "DEBUG",
@@ -298,64 +38,6 @@ function isLogLevel(value) {
298
38
  return LOG_LEVELS.includes(value);
299
39
  }
300
40
 
301
- //#endregion
302
- //#region src/parser/app-config/schema.ts
303
- const envValueSchema = z.union([
304
- z.string(),
305
- z.number(),
306
- z.boolean()
307
- ]);
308
- const LogLevelSchema = z.enum(LOG_LEVELS);
309
- const logLevelSchema = z.string().refine((value) => LogLevelSchema.safeParse(value.trim().toUpperCase()).success, { message: `'logLevel' must be one of: ${LOG_LEVELS.join(", ")}.` });
310
- /**
311
- * Structural validation schema for `defineConfig({...})`. Validates only
312
- * top-level fields with platform-side constraints (notably `id`); fields
313
- * that carry SDK builder objects (`auth`, `idp`, `db`, ...) are accepted
314
- * as opaque values, since their internal shapes are validated by their
315
- * own factory functions and parser-level schemas.
316
- *
317
- * The `id` is auto-managed by `deploy` and stored as a plain UUID. A
318
- * label-compatible prefix is added at the metadata boundary, so user-facing
319
- * configs only need to carry a UUID.
320
- */
321
- const AppConfigSchema = z.strictObject({
322
- id: z.uuid({ message: "'id' must be a UUID." }).optional(),
323
- name: z.string().min(1, { message: "'name' must be a non-empty string." }),
324
- env: z.record(z.string(), envValueSchema).optional(),
325
- cors: z.array(z.string()).optional(),
326
- allowedIpAddresses: z.array(z.string()).optional(),
327
- disableIntrospection: z.boolean().optional(),
328
- inlineSourcemap: z.boolean().optional(),
329
- logLevel: logLevelSchema.optional(),
330
- db: z.unknown().optional(),
331
- resolver: z.unknown().optional(),
332
- idp: z.unknown().optional(),
333
- auth: z.unknown().optional(),
334
- executor: z.unknown().optional(),
335
- workflow: z.unknown().optional(),
336
- httpAdapter: z.unknown().optional(),
337
- staticWebsites: z.unknown().optional(),
338
- aiGateways: z.unknown().optional(),
339
- secrets: z.unknown().optional()
340
- });
341
-
342
- //#endregion
343
- //#region src/parser/plugin-config/schema.ts
344
- const PluginConfigSchema = z.looseObject({
345
- id: z.string(),
346
- description: z.string(),
347
- importPath: z.string().optional(),
348
- pluginConfig: z.unknown().optional(),
349
- typeConfigRequired: z.union([z.boolean(), functionSchema]).optional(),
350
- onTypeLoaded: functionSchema.optional(),
351
- onNamespaceLoaded: functionSchema.optional(),
352
- onTailorDBReady: functionSchema.optional(),
353
- onResolverReady: functionSchema.optional(),
354
- onExecutorReady: functionSchema.optional()
355
- }).refine((p) => {
356
- return !(p.onTypeLoaded || p.onNamespaceLoaded) || !!p.importPath;
357
- }, { message: "importPath is required when plugin has definition-time hooks (onTypeLoaded/onNamespaceLoaded)" }).transform((plugin) => plugin);
358
-
359
41
  //#endregion
360
42
  //#region src/cli/shared/apply-concurrency.ts
361
43
  /**
@@ -498,7 +180,7 @@ function initOAuth2Client(config) {
498
180
  */
499
181
  async function initOperatorClient(accessToken, config) {
500
182
  const platformConfig = config ?? getPlatformConfigForToken(accessToken);
501
- const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-DZkfjB_6.mjs")]);
183
+ const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-r1T7kpJc.mjs")]);
502
184
  const interceptors = [
503
185
  await userAgentInterceptor(),
504
186
  await bearerTokenInterceptor(accessToken),
@@ -551,9 +233,9 @@ async function bearerTokenInterceptor(accessToken) {
551
233
  /**
552
234
  * Create an interceptor that retries failed unary requests with backoff.
553
235
  *
554
- * Retries any unary method on `Unavailable`/`ResourceExhausted`, and `Internal`
555
- * only for methods declared idempotent, up to 3 attempts (despite the historical
556
- * "idempotent" naming, the first two codes are retried regardless of idempotency).
236
+ * Retries unary methods on `Unavailable`/`ResourceExhausted`, and `Internal`
237
+ * only for methods declared idempotent, up to 3 attempts. Workspace creation is
238
+ * excluded because it has no idempotency key and a lost response is ambiguous.
557
239
  * As a targeted exception for the deploy/apply flow, a post-retry `AlreadyExists`
558
240
  * from an allowlisted Create (see `RETRY_SAFE_CREATE_METHODS`) is treated as
559
241
  * success, since it means a prior attempt already committed the resource
@@ -578,10 +260,10 @@ function retryInterceptor() {
578
260
  logger.debug(`retry: ${req.method.name} returned AlreadyExists on attempt ${i + 1}; treating as success (prior attempt likely committed)`);
579
261
  return synthesizeEmptyUnaryResponse(req);
580
262
  }
581
- const { reportCrash } = await import("./crashreport-D3RjuSYb.mjs");
263
+ const { reportCrash } = await import("./crashreport-0ioNRZ4K.mjs");
582
264
  await reportCrash(error, "handledError");
583
265
  }
584
- if (isRetirable(error, req.method.idempotency)) {
266
+ if (req.method.name !== "CreateWorkspace" && isRetirable(error, req.method.idempotency)) {
585
267
  lastError = error;
586
268
  logger.debug(`retry: ${req.method.name} attempt ${i + 1} failed with ${connectCodeName(error)}; retrying`);
587
269
  continue;
@@ -986,806 +668,6 @@ async function closeConnectionPool() {
986
668
  await getGlobalDispatcher().close();
987
669
  }
988
670
 
989
- //#endregion
990
- //#region src/cli/shared/token-store.ts
991
- const SERVICE_NAME = "tailor-platform-cli";
992
- let entryClass;
993
- async function getEntryClass() {
994
- if (entryClass !== void 0) return entryClass;
995
- try {
996
- const mod = await import("@napi-rs/keyring");
997
- const probe = new mod.Entry(SERVICE_NAME, "__probe__");
998
- probe.setPassword("probe");
999
- probe.deletePassword();
1000
- entryClass = mod.Entry;
1001
- } catch {
1002
- logger.warn("System keyring is not available. Tokens will be stored in the config file. Set TAILOR_PLATFORM_TOKEN environment variable for CI environments.");
1003
- entryClass = false;
1004
- }
1005
- return entryClass;
1006
- }
1007
- /**
1008
- * Check whether the OS keyring is available and functional.
1009
- * @returns true if keyring is available
1010
- */
1011
- async function isKeyringAvailable() {
1012
- return await getEntryClass() !== false;
1013
- }
1014
- /**
1015
- * Load tokens from the OS keyring for a given account.
1016
- * @param account - User identifier (e.g. email or client ID)
1017
- * @returns Token data or undefined if not found or keyring unavailable
1018
- */
1019
- async function loadKeyringTokens(account) {
1020
- const Entry = await getEntryClass();
1021
- if (!Entry) return void 0;
1022
- try {
1023
- const raw = new Entry(SERVICE_NAME, account).getPassword();
1024
- if (raw === null) return void 0;
1025
- return JSON.parse(raw);
1026
- } catch {
1027
- return;
1028
- }
1029
- }
1030
- /**
1031
- * Save tokens to the OS keyring for a given account.
1032
- * @param account - User identifier (e.g. email or client ID)
1033
- * @param tokens - Token data to store
1034
- */
1035
- async function saveKeyringTokens(account, tokens) {
1036
- const Entry = await getEntryClass();
1037
- if (!Entry) return;
1038
- new Entry(SERVICE_NAME, account).setPassword(JSON.stringify(tokens));
1039
- }
1040
- /**
1041
- * Delete tokens from the OS keyring for a given account.
1042
- * @param account - User identifier (e.g. email or client ID)
1043
- */
1044
- async function deleteKeyringTokens(account) {
1045
- const Entry = await getEntryClass();
1046
- if (!Entry) return;
1047
- try {
1048
- new Entry(SERVICE_NAME, account).deletePassword();
1049
- } catch {}
1050
- }
1051
-
1052
- //#endregion
1053
- //#region src/cli/shared/context.ts
1054
- const pfProfileSchema = z.object({
1055
- user: z.string(),
1056
- workspace_id: z.string(),
1057
- readonly: z.boolean().optional(),
1058
- machine_user: z.string().optional(),
1059
- machine_user_override: z.enum(["allow", "deny"]).optional(),
1060
- platform_url: z.url().optional(),
1061
- oauth2_client_id: z.string().optional(),
1062
- console_url: z.url().optional()
1063
- });
1064
- const pfUserSchemaV1 = z.object({
1065
- access_token: z.string(),
1066
- refresh_token: z.string().optional(),
1067
- token_expires_at: z.string()
1068
- });
1069
- const pfUserKeyringSchema = z.object({
1070
- storage: z.literal("keyring"),
1071
- token_expires_at: z.string()
1072
- });
1073
- const pfUserFileSchema = z.object({
1074
- storage: z.literal("file"),
1075
- token_expires_at: z.string(),
1076
- access_token: z.string(),
1077
- refresh_token: z.string().optional()
1078
- });
1079
- const pfUserSchemaV2 = z.discriminatedUnion("storage", [pfUserKeyringSchema, pfUserFileSchema]);
1080
- const pfUserKeyringSchemaV3 = pfUserKeyringSchema.extend({ email: z.string().optional() });
1081
- const pfUserFileSchemaV3 = pfUserFileSchema.extend({ email: z.string().optional() });
1082
- const pfUserSchemaV3 = z.discriminatedUnion("storage", [pfUserKeyringSchemaV3, pfUserFileSchemaV3]);
1083
- const pfConfigSchemaV1 = z.object({
1084
- version: z.literal(1),
1085
- users: z.partialRecord(z.string(), pfUserSchemaV1),
1086
- profiles: z.partialRecord(z.string(), pfProfileSchema),
1087
- current_user: z.string().nullable()
1088
- });
1089
- const V2_CONFIG_VERSION = 2;
1090
- const LATEST_CONFIG_VERSION = 3;
1091
- const V2_MIN_SDK_VERSION = "1.29.0";
1092
- const V3_MIN_SDK_VERSION = "2.0.0";
1093
- const semverSchema = z.templateLiteral([
1094
- z.number().int(),
1095
- ".",
1096
- z.number().int(),
1097
- ".",
1098
- z.number().int()
1099
- ]);
1100
- const pfConfigSchemaV2 = z.object({
1101
- version: z.literal(V2_CONFIG_VERSION),
1102
- min_sdk_version: semverSchema,
1103
- latest_version: z.number().int().optional(),
1104
- latest_min_sdk_version: semverSchema.optional(),
1105
- users: z.partialRecord(z.string(), pfUserSchemaV2),
1106
- profiles: z.partialRecord(z.string(), pfProfileSchema),
1107
- current_user: z.string().nullable()
1108
- });
1109
- const pfConfigSchemaV3 = z.object({
1110
- version: z.literal(LATEST_CONFIG_VERSION),
1111
- min_sdk_version: semverSchema,
1112
- latest_version: z.number().int().optional(),
1113
- latest_min_sdk_version: semverSchema.optional(),
1114
- users: z.partialRecord(z.string(), pfUserSchemaV3),
1115
- profiles: z.partialRecord(z.string(), pfProfileSchema),
1116
- current_user: z.string().nullable()
1117
- });
1118
- function platformConfigPath() {
1119
- if (!xdgConfig) throw new Error("User home directory not found");
1120
- return path.join(xdgConfig, "tailor-platform", "config.yaml");
1121
- }
1122
- /**
1123
- * Convert stored profile platform fields to platform client settings.
1124
- * @param profile - Profile platform settings
1125
- * @returns Platform client settings
1126
- */
1127
- function platformConfigFromProfile(profile) {
1128
- const config = {
1129
- ...profile?.platform_url ? { platformUrl: profile.platform_url } : {},
1130
- ...profile?.oauth2_client_id ? { oauth2ClientId: profile.oauth2_client_id } : {},
1131
- ...profile?.console_url ? { consoleUrl: profile.console_url } : {}
1132
- };
1133
- return Object.keys(config).length > 0 ? config : void 0;
1134
- }
1135
- function platformUserKey(user, config) {
1136
- const platformUrl = getPlatformBaseUrl(config);
1137
- if (platformUrl === normalizeBaseUrl("https://api.tailor.tech")) return user;
1138
- return `${platformUrl}|${user}`;
1139
- }
1140
- function userFromPlatformUserKey(userKey, config) {
1141
- const platformPrefix = `${getPlatformBaseUrl(config)}|`;
1142
- return userKey.startsWith(platformPrefix) ? userKey.slice(platformPrefix.length) : userKey;
1143
- }
1144
- function canUseLegacyUserKey(platformUrl) {
1145
- try {
1146
- return getPlatformBaseUrl() === platformUrl;
1147
- } catch {
1148
- return false;
1149
- }
1150
- }
1151
- function findUserByEmail(users, email, platformConfig) {
1152
- const platformUrl = getPlatformBaseUrl(platformConfig);
1153
- const platformPrefix = `${platformUrl}|`;
1154
- const defaultPlatform = platformUrl === normalizeBaseUrl(defaultPlatformBaseUrl);
1155
- return Object.entries(users).find(([key, entry]) => {
1156
- if (entry?.email !== email) return false;
1157
- return defaultPlatform ? !key.includes("|") : key.startsWith(platformPrefix);
1158
- });
1159
- }
1160
- function findUserEntry(config, user, platformConfig, opts = {}) {
1161
- const userKey = platformUserKey(user, platformConfig);
1162
- const userEntry = config.users[userKey];
1163
- if (userEntry) return {
1164
- userKey,
1165
- userEntry
1166
- };
1167
- const emailMatch = findUserByEmail(config.users, user, platformConfig);
1168
- if (emailMatch?.[1]) return {
1169
- userKey: emailMatch[0],
1170
- userEntry: emailMatch[1]
1171
- };
1172
- const platformUrl = getPlatformBaseUrl(platformConfig);
1173
- if (userKey !== user && (opts.allowLegacyUserKey === false || !canUseLegacyUserKey(platformUrl))) return {
1174
- userKey,
1175
- userEntry
1176
- };
1177
- const legacyEntry = config.users[user];
1178
- return legacyEntry ? {
1179
- userKey: user,
1180
- userEntry: legacyEntry
1181
- } : {
1182
- userKey,
1183
- userEntry
1184
- };
1185
- }
1186
- /**
1187
- * Resolve the config user key that would be used for a user on the selected platform.
1188
- * @param config - Platform config
1189
- * @param user - User name
1190
- * @param platformConfig - Optional platform connection settings
1191
- * @param opts - Token lookup options
1192
- * @returns Resolved config user key
1193
- */
1194
- function resolveUserTokenKey(config, user, platformConfig, opts) {
1195
- return findUserEntry(config, user, platformConfig, opts).userKey;
1196
- }
1197
- function resolveConfigUser(config, user, platformConfig, opts) {
1198
- const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
1199
- return userEntry ? userFromPlatformUserKey(userKey, platformConfig) : void 0;
1200
- }
1201
- /**
1202
- * Check whether tokens are registered for a user on the selected platform.
1203
- * @param config - Platform config
1204
- * @param user - User name
1205
- * @param platformConfig - Optional platform connection settings
1206
- * @param opts - Token lookup options
1207
- * @returns True when the user has a registered token entry
1208
- */
1209
- function hasUserTokenEntry(config, user, platformConfig, opts) {
1210
- return findUserEntry(config, user, platformConfig, opts).userEntry !== void 0;
1211
- }
1212
- function hasUserKeyForName(users, user) {
1213
- return users[user] !== void 0 || Object.keys(users).some((key) => key.endsWith(`|${user}`));
1214
- }
1215
- function hasUserEmailEntry(users, user) {
1216
- return Object.values(users).some((entry) => entry?.email === user);
1217
- }
1218
- /**
1219
- * Check whether any platform has tokens registered for a user.
1220
- * @param config - Platform config
1221
- * @param user - User name
1222
- * @returns True when the user has a token entry for any platform
1223
- */
1224
- function hasAnyUserTokenEntry(config, user) {
1225
- return hasUserKeyForName(config.users, user) || hasUserEmailEntry(config.users, user);
1226
- }
1227
- function hasCurrentUserEntry(users, currentUser) {
1228
- return hasUserKeyForName(users, currentUser);
1229
- }
1230
- /**
1231
- * Migrate a v1 config to v2.
1232
- * Tokens are kept in the config file (storage: "file") during migration.
1233
- * They will be moved to the OS keyring on next login or token refresh.
1234
- * @param v1Config - v1 configuration to migrate
1235
- * @returns Migrated v2 configuration
1236
- */
1237
- function migrateV1ToV2(v1Config) {
1238
- const users = {};
1239
- for (const [name, v1User] of Object.entries(v1Config.users)) {
1240
- if (!v1User) continue;
1241
- users[name] = {
1242
- access_token: v1User.access_token,
1243
- refresh_token: v1User.refresh_token,
1244
- token_expires_at: v1User.token_expires_at,
1245
- storage: "file"
1246
- };
1247
- }
1248
- return {
1249
- version: V2_CONFIG_VERSION,
1250
- min_sdk_version: V2_MIN_SDK_VERSION,
1251
- users,
1252
- profiles: v1Config.profiles,
1253
- current_user: v1Config.current_user
1254
- };
1255
- }
1256
- function inferEmailFromUserId(user) {
1257
- return z.email().safeParse(user).success ? user : void 0;
1258
- }
1259
- function migrateV2ToV3(v2Config) {
1260
- const users = {};
1261
- for (const [user, entry] of Object.entries(v2Config.users)) {
1262
- if (!entry) continue;
1263
- const email = inferEmailFromUserId(user);
1264
- users[user] = {
1265
- ...entry,
1266
- ...email ? { email } : {}
1267
- };
1268
- }
1269
- return {
1270
- version: LATEST_CONFIG_VERSION,
1271
- min_sdk_version: V3_MIN_SDK_VERSION,
1272
- users,
1273
- profiles: v2Config.profiles,
1274
- current_user: v2Config.current_user
1275
- };
1276
- }
1277
- function migrateV1ToV3(v1Config) {
1278
- return migrateV2ToV3(migrateV1ToV2(v1Config));
1279
- }
1280
- function formatUnknownError(error) {
1281
- return error instanceof Error ? error.message : String(error);
1282
- }
1283
- async function trySaveTokensInKeyring(user, tokens) {
1284
- if (!await isKeyringAvailable()) return false;
1285
- try {
1286
- await saveKeyringTokens(user, tokens);
1287
- return true;
1288
- } catch (error) {
1289
- logger.warn(`System keyring failed to store credentials. Tokens will be stored in the config file. ${formatUnknownError(error)}`);
1290
- return false;
1291
- }
1292
- }
1293
- async function warnIfNewerConfigAvailable(config) {
1294
- if (!config.latest_min_sdk_version) return;
1295
- if (lt((await readPackageJson()).version ?? "0.0.0", config.latest_min_sdk_version)) logger.warn(multiline`
1296
- A newer config version (${String(config.latest_version)}) is available.
1297
- Please update your SDK to >= ${config.latest_min_sdk_version}: pnpm update @tailor-platform/sdk
1298
- `);
1299
- }
1300
- /**
1301
- * Read Tailor Platform CLI configuration, migrating from v1 if necessary.
1302
- * @returns Parsed platform configuration
1303
- */
1304
- async function readPlatformConfig() {
1305
- const configPath = platformConfigPath();
1306
- if (!fs$1.existsSync(configPath)) {
1307
- const config = {
1308
- version: LATEST_CONFIG_VERSION,
1309
- min_sdk_version: V3_MIN_SDK_VERSION,
1310
- users: {},
1311
- profiles: {},
1312
- current_user: null
1313
- };
1314
- writePlatformConfig(config);
1315
- return config;
1316
- }
1317
- const rawConfig = parseYAML(fs$1.readFileSync(configPath, "utf-8"));
1318
- tightenSecretFilePermissions(configPath);
1319
- const version = rawConfig != null && typeof rawConfig === "object" && "version" in rawConfig ? rawConfig.version : void 0;
1320
- if (typeof version === "number" && version > LATEST_CONFIG_VERSION) {
1321
- const minSdk = "min_sdk_version" in rawConfig ? String(rawConfig.min_sdk_version) : void 0;
1322
- const updateHint = minSdk ? `Please update your SDK to >= ${minSdk}: pnpm update @tailor-platform/sdk` : "Please update your SDK: pnpm update @tailor-platform/sdk";
1323
- throw new Error(multiline`
1324
- Config file uses version ${String(version)}, but this SDK only supports up to version ${String(LATEST_CONFIG_VERSION)}.
1325
- ${updateHint}
1326
- `);
1327
- }
1328
- const v3Result = pfConfigSchemaV3.safeParse(rawConfig);
1329
- if (v3Result.success) {
1330
- await warnIfNewerConfigAvailable(v3Result.data);
1331
- return v3Result.data;
1332
- }
1333
- const v2Result = pfConfigSchemaV2.safeParse(rawConfig);
1334
- if (v2Result.success) {
1335
- await warnIfNewerConfigAvailable(v2Result.data);
1336
- return migrateV2ToV3(v2Result.data);
1337
- }
1338
- const v1Result = pfConfigSchemaV1.safeParse(rawConfig);
1339
- if (v1Result.success) return migrateV1ToV3(v1Result.data);
1340
- throw new Error(multiline`
1341
- Failed to parse config file at ${configPath}.
1342
- The file may be corrupted or created by an incompatible SDK version.
1343
- `);
1344
- }
1345
- function toV1ForDisk(config) {
1346
- const users = {};
1347
- for (const [name, entry] of Object.entries(config.users)) {
1348
- if (!entry || entry.storage === "keyring") continue;
1349
- users[name] = {
1350
- access_token: entry.access_token,
1351
- refresh_token: entry.refresh_token,
1352
- token_expires_at: entry.token_expires_at
1353
- };
1354
- }
1355
- const currentUser = config.current_user && hasCurrentUserEntry(users, config.current_user) ? config.current_user : null;
1356
- return {
1357
- version: 1,
1358
- users,
1359
- profiles: config.profiles,
1360
- current_user: currentUser
1361
- };
1362
- }
1363
- function hasProfilePlatformSettings(config) {
1364
- return Object.values(config.profiles).some((profile) => profile?.platform_url !== void 0 || profile?.oauth2_client_id !== void 0 || profile?.console_url !== void 0);
1365
- }
1366
- function hasScopedUserKeys(config) {
1367
- return Object.keys(config.users).some((userKey) => userKey.includes("|"));
1368
- }
1369
- function hasUserEmailMetadata(config) {
1370
- return Object.values(config.users).some((user) => user != null && "email" in user && user.email !== void 0);
1371
- }
1372
- function toLatestForDisk(config) {
1373
- return {
1374
- ...config.version === 1 ? migrateV1ToV2(config) : config,
1375
- version: LATEST_CONFIG_VERSION,
1376
- min_sdk_version: V3_MIN_SDK_VERSION
1377
- };
1378
- }
1379
- /**
1380
- * Write Tailor Platform CLI configuration to disk.
1381
- * By default, file-backed configs without newer fields are converted to V1 for
1382
- * backward compatibility, so an older SDK can still read the file. Configs
1383
- * containing a keyring user are kept in V2 or later because the keyring storage
1384
- * variant is not representable in V1. Configs containing profile-level Platform
1385
- * settings, platform-scoped user tokens, canonical user IDs, or email metadata
1386
- * are written in the latest min-SDK-gated format because older SDKs would
1387
- * silently drop or misread those settings.
1388
- *
1389
- * The config file may contain access/refresh tokens when the OS keyring is
1390
- * unavailable, so it is written via {@link writeSecretFile} so other users
1391
- * on the host cannot read it.
1392
- * @param config - Platform configuration to write
1393
- */
1394
- function writePlatformConfig(config) {
1395
- const configPath = platformConfigPath();
1396
- const hasKeyringUser = config.version !== 1 && Object.values(config.users).some((u) => u?.storage === "keyring");
1397
- writeSecretFile(configPath, stringifyYAML(config.version === LATEST_CONFIG_VERSION || hasProfilePlatformSettings(config) || hasScopedUserKeys(config) || hasUserEmailMetadata(config) ? toLatestForDisk(config) : config.version === V2_CONFIG_VERSION && !hasKeyringUser ? toV1ForDisk(config) : config));
1398
- }
1399
- function validateUUID(value, source) {
1400
- const result = z.uuid().safeParse(value);
1401
- if (!result.success) throw new Error(`Invalid value from ${source}: must be a valid UUID`);
1402
- return result.data;
1403
- }
1404
- /**
1405
- * Load workspace ID from command options, environment variables, or platform config.
1406
- * In CLI context, env fallback is also handled by politty's arg env option.
1407
- * Priority: opts/workspaceId > env/workspaceId > opts/profile > error
1408
- * @param opts - Workspace and profile options
1409
- * @returns Resolved workspace ID
1410
- */
1411
- async function loadWorkspaceId(opts) {
1412
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1413
- if (opts?.workspaceId) return validateUUID(opts.workspaceId, "--workspace-id option");
1414
- if (process.env.TAILOR_PLATFORM_WORKSPACE_ID) return validateUUID(process.env.TAILOR_PLATFORM_WORKSPACE_ID, "TAILOR_PLATFORM_WORKSPACE_ID environment variable");
1415
- if (profile) {
1416
- const wsId = (await readPlatformConfig()).profiles[profile]?.workspace_id;
1417
- if (!wsId) throw new Error(`Profile "${profile}" not found`);
1418
- return validateUUID(wsId, `profile "${profile}"`);
1419
- }
1420
- throw new Error(multiline`
1421
- Workspace ID not found.
1422
- Please specify workspace ID via --workspace-id option or TAILOR_PLATFORM_WORKSPACE_ID environment variable.
1423
- `);
1424
- }
1425
- /**
1426
- * Load machine user name from command options, environment variables, or platform config.
1427
- * In CLI context, env fallback is also handled by politty's arg env option.
1428
- * Priority: opts/machineUser > env/TAILOR_PLATFORM_MACHINE_USER_NAME > opts/profile (profile default) > undefined.
1429
- * An explicitly empty `opts.machineUser` is rejected with a CLIError (`MACHINE_USER_NAME_EMPTY`) rather than falling back to the env var or profile default.
1430
- * When the active profile has `machine_user_override: "deny"`, an explicit value that differs from the profile's machine user throws a CLIError with code `PROFILE_MACHINE_USER_OVERRIDE_DENIED`.
1431
- * @param opts - Machine user and profile options
1432
- * @returns Resolved machine user name, or undefined if not set
1433
- */
1434
- async function loadMachineUserName(opts) {
1435
- if (opts?.machineUser === "") throw createCLIError({
1436
- code: "MACHINE_USER_NAME_EMPTY",
1437
- message: "Machine user name cannot be empty.",
1438
- suggestion: "Pass a non-empty machine user name, or omit the option to use the environment variable or profile default."
1439
- });
1440
- const envMachineUser = process.env.TAILOR_PLATFORM_MACHINE_USER_NAME || void 0;
1441
- const explicitMachineUser = opts?.machineUser ? {
1442
- source: opts.machineUserSource ?? "option",
1443
- value: opts.machineUser
1444
- } : envMachineUser ? {
1445
- source: "env",
1446
- value: envMachineUser
1447
- } : void 0;
1448
- const explicit = explicitMachineUser?.value;
1449
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1450
- if (!profile) return explicit;
1451
- const entry = (await readPlatformConfig()).profiles[profile];
1452
- if (!entry) {
1453
- if (explicit) return explicit;
1454
- throw new Error(`Profile "${profile}" not found`);
1455
- }
1456
- if (entry.machine_user && entry.machine_user_override === "deny") {
1457
- if (explicit && explicit !== entry.machine_user) {
1458
- const details = explicitMachineUser.source === "env" ? `The machine user is being set to "${explicit}" via the TAILOR_PLATFORM_MACHINE_USER_NAME environment variable, which conflicts with this profile's pinned machine user "${entry.machine_user}".` : `This profile fixes the machine user to "${entry.machine_user}" for application-data commands.`;
1459
- throw createCLIError({
1460
- code: "PROFILE_MACHINE_USER_OVERRIDE_DENIED",
1461
- message: `Profile "${profile}" denies overriding the machine user.`,
1462
- details,
1463
- suggestion: `Omit the machine user option, unset TAILOR_PLATFORM_MACHINE_USER_NAME, or run 'tailor profile update ${profile} --machine-user-override allow'.`
1464
- });
1465
- }
1466
- return entry.machine_user;
1467
- }
1468
- return explicit || entry.machine_user;
1469
- }
1470
- /**
1471
- * Load access token from environment variables, command options, or platform config.
1472
- * In CLI context, profile env fallback is also handled by politty's arg env option.
1473
- * Priority: env/TAILOR_PLATFORM_TOKEN > env/TAILOR_TOKEN (deprecated) > opts/profile > env/profile > config/currentUser > error
1474
- * @param opts - Profile options
1475
- * @returns Resolved access token
1476
- */
1477
- async function loadAccessToken(opts) {
1478
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1479
- const envToken = process.env.TAILOR_PLATFORM_TOKEN ?? process.env.TAILOR_TOKEN;
1480
- if (envToken && !process.env.TAILOR_PLATFORM_TOKEN) logger.warn("TAILOR_TOKEN is deprecated. Please use TAILOR_PLATFORM_TOKEN instead.");
1481
- if (envToken) {
1482
- rememberPlatformConfigForToken(envToken, await loadPlatformClientConfig({
1483
- profile,
1484
- allowMissingProfile: true
1485
- }));
1486
- return envToken;
1487
- }
1488
- const pfConfig = await readPlatformConfig();
1489
- const profileEntry = profile ? pfConfig.profiles[profile] : void 0;
1490
- if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
1491
- const user = profileEntry?.user ?? pfConfig.current_user;
1492
- if (!user) throw new Error(multiline`
1493
- Tailor Platform token not found.
1494
- Please specify token via TAILOR_PLATFORM_TOKEN environment variable or login using 'tailor login' command.
1495
- `);
1496
- return (await fetchLatestToken(pfConfig, user, profileEntry ? platformConfigFromProfile(profileEntry) : void 0)).accessToken;
1497
- }
1498
- /**
1499
- * Load platform connection settings from the active profile.
1500
- * @param opts - Profile options
1501
- * @returns Resolved platform connection settings, or undefined for the default environment
1502
- */
1503
- async function loadPlatformClientConfig(opts) {
1504
- const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
1505
- if (!profile) return;
1506
- let pfConfig;
1507
- try {
1508
- pfConfig = await readPlatformConfig();
1509
- } catch (error) {
1510
- if (opts?.allowMissingProfile) return;
1511
- throw error;
1512
- }
1513
- const profileEntry = pfConfig.profiles[profile];
1514
- if (!profileEntry) {
1515
- if (opts?.allowMissingProfile) return;
1516
- throw new Error(`Profile "${profile}" not found`);
1517
- }
1518
- return platformConfigFromProfile(profileEntry);
1519
- }
1520
- /**
1521
- * Load the Tailor Platform Console base URL from environment variables or the active profile.
1522
- * @param opts - Profile options
1523
- * @returns Resolved Console base URL
1524
- */
1525
- async function loadConsoleBaseUrl(opts) {
1526
- return getConsoleBaseUrl(await loadPlatformClientConfig(opts));
1527
- }
1528
- /**
1529
- * Resolve the actual token values for a user, reading from keyring or config as appropriate.
1530
- * @param userEntry - User entry from the config
1531
- * @param user - User identifier
1532
- * @param label - User-facing identifier used in error messages
1533
- * @returns Access token and optional refresh token
1534
- */
1535
- async function resolveTokens(userEntry, user, label = user) {
1536
- if (userEntry.storage === "keyring") {
1537
- const tokens = await loadKeyringTokens(user);
1538
- if (!tokens) throw new Error(multiline`
1539
- Credentials not found in OS keyring for "${label}".
1540
- Please run 'tailor login' and try again.
1541
- `);
1542
- return tokens;
1543
- }
1544
- return {
1545
- accessToken: userEntry.access_token,
1546
- refreshToken: userEntry.refresh_token
1547
- };
1548
- }
1549
- /**
1550
- * Save tokens for a user, writing to keyring by default when available.
1551
- * @param config - Platform config
1552
- * @param user - User identifier
1553
- * @param tokens - Token data to save
1554
- * @param tokens.accessToken - Access token to save
1555
- * @param tokens.refreshToken - Optional refresh token to save
1556
- * @param expiresAt - Token expiration date
1557
- * @param opts - Optional platform and user metadata
1558
- */
1559
- async function saveUserTokens(config, user, tokens, expiresAt, opts = {}) {
1560
- const userKey = platformUserKey(user, opts.platformConfig);
1561
- const email = opts.email ?? config.users[userKey]?.email;
1562
- if (await trySaveTokensInKeyring(userKey, tokens)) config.users[userKey] = {
1563
- token_expires_at: expiresAt,
1564
- storage: "keyring",
1565
- ...email ? { email } : {}
1566
- };
1567
- else {
1568
- if (config.users[userKey]?.storage === "keyring") await deleteKeyringTokens(userKey);
1569
- config.users[userKey] = {
1570
- access_token: tokens.accessToken,
1571
- refresh_token: tokens.refreshToken,
1572
- token_expires_at: expiresAt,
1573
- storage: "file",
1574
- ...email ? { email } : {}
1575
- };
1576
- }
1577
- }
1578
- /**
1579
- * Delete tokens for a user from keyring if applicable.
1580
- * @param config - Platform config
1581
- * @param user - User identifier
1582
- * @param platformConfig - Optional platform connection settings
1583
- * @param opts - Token lookup options
1584
- */
1585
- async function deleteUserTokens(config, user, platformConfig, opts) {
1586
- const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
1587
- if (userEntry?.storage === "keyring") await deleteKeyringTokens(userKey);
1588
- delete config.users[userKey];
1589
- }
1590
- /**
1591
- * Resolve stored tokens for a user on the selected platform.
1592
- * @param config - Platform config
1593
- * @param user - User name
1594
- * @param platformConfig - Optional platform connection settings
1595
- * @param opts - Token lookup options
1596
- * @returns Stored user entry and token values, or undefined when the user is not logged in
1597
- */
1598
- async function loadStoredUserTokens(config, user, platformConfig, opts) {
1599
- const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
1600
- if (!userEntry) return void 0;
1601
- return {
1602
- userEntry,
1603
- ...await resolveTokens(userEntry, userKey, user)
1604
- };
1605
- }
1606
- function updateUserReferences(config, fromUser, toUser) {
1607
- if (fromUser === toUser) return;
1608
- if (config.current_user === fromUser) config.current_user = toUser;
1609
- for (const profile of Object.values(config.profiles)) if (profile?.user === fromUser) profile.user = toUser;
1610
- }
1611
- /**
1612
- * Remove a legacy alias after a canonical user ID has been written.
1613
- * @param config - Platform config
1614
- * @param legacyUser - Previous user key
1615
- * @param canonicalUser - Canonical user key
1616
- * @param platformConfig - Platform settings for scoped token storage
1617
- */
1618
- async function removeLegacyUserAlias(config, legacyUser, canonicalUser, platformConfig) {
1619
- if (legacyUser === canonicalUser) return;
1620
- updateUserReferences(config, legacyUser, canonicalUser);
1621
- const canonicalKey = platformUserKey(canonicalUser, platformConfig);
1622
- const legacyKeys = /* @__PURE__ */ new Set([legacyUser, platformUserKey(legacyUser, platformConfig)]);
1623
- for (const legacyKey of legacyKeys) {
1624
- if (legacyKey === canonicalKey) continue;
1625
- if (config.users[legacyKey]?.storage === "keyring") await deleteKeyringTokens(legacyKey);
1626
- delete config.users[legacyKey];
1627
- }
1628
- }
1629
- function shouldResolveSubjectOnRefresh(user, userEntry) {
1630
- return Boolean(userEntry.email || inferEmailFromUserId(user));
1631
- }
1632
- /**
1633
- * Fetch the latest access token, refreshing if necessary.
1634
- * @param config - Platform config
1635
- * @param user - User identifier
1636
- * @param platformConfig - Optional platform connection settings
1637
- * @returns Latest access token and the canonical user ID it is stored under
1638
- * (the resolved subject when a legacy email key was migrated during refresh,
1639
- * otherwise the matching config user)
1640
- */
1641
- async function fetchLatestToken(config, user, platformConfig) {
1642
- const { userKey: storedUser, userEntry } = findUserEntry(config, user, platformConfig);
1643
- if (!userEntry) throw new Error(multiline`
1644
- User "${user}" not found.
1645
- Please verify your user name and login using 'tailor login' command.
1646
- `);
1647
- const storedConfigUser = userFromPlatformUserKey(storedUser, platformConfig);
1648
- const tokens = await resolveTokens(userEntry, storedUser, user);
1649
- if (new Date(userEntry.token_expires_at) > /* @__PURE__ */ new Date()) {
1650
- rememberPlatformConfigForToken(tokens.accessToken, platformConfig);
1651
- return {
1652
- accessToken: tokens.accessToken,
1653
- user: storedConfigUser
1654
- };
1655
- }
1656
- if (!tokens.refreshToken) throw new Error(multiline`
1657
- Token expired.
1658
- Please run 'tailor login' and try again.
1659
- `);
1660
- const client = initOAuth2Client(platformConfig);
1661
- let resp;
1662
- try {
1663
- resp = await client.refreshToken({
1664
- accessToken: tokens.accessToken,
1665
- refreshToken: tokens.refreshToken,
1666
- expiresAt: Date.parse(userEntry.token_expires_at)
1667
- });
1668
- } catch {
1669
- throw new Error(multiline`
1670
- Failed to refresh token. Your session may have expired.
1671
- Please run 'tailor login' and try again.
1672
- `);
1673
- }
1674
- const newExpiresAt = new Date(assertDefined(resp.expiresAt, "token refresh response missing expiresAt")).toISOString();
1675
- let resolvedUser = storedConfigUser;
1676
- const previousEmail = userEntry.email ?? inferEmailFromUserId(user) ?? inferEmailFromUserId(storedConfigUser);
1677
- let email = previousEmail;
1678
- if (shouldResolveSubjectOnRefresh(user, userEntry) || shouldResolveSubjectOnRefresh(storedConfigUser, userEntry)) try {
1679
- const userInfo = await fetchUserInfo(resp.accessToken, platformConfig);
1680
- resolvedUser = userInfo.sub;
1681
- email = userInfo.email;
1682
- } catch (error) {
1683
- logger.debug(`Failed to resolve refreshed token user info: ${String(error)}`);
1684
- }
1685
- await saveUserTokens(config, resolvedUser, {
1686
- accessToken: resp.accessToken,
1687
- refreshToken: resp.refreshToken ?? void 0
1688
- }, newExpiresAt, {
1689
- platformConfig,
1690
- email
1691
- });
1692
- await removeLegacyUserAlias(config, user, resolvedUser, platformConfig);
1693
- if (storedUser !== platformUserKey(resolvedUser, platformConfig)) {
1694
- if (config.users[storedUser]?.storage === "keyring") await deleteKeyringTokens(storedUser);
1695
- delete config.users[storedUser];
1696
- }
1697
- if (previousEmail && email && previousEmail !== email) logger.info(`Updated local user email from "${previousEmail}" to "${email}".`);
1698
- writePlatformConfig(config);
1699
- rememberPlatformConfigForToken(resp.accessToken, platformConfig);
1700
- return {
1701
- accessToken: resp.accessToken,
1702
- user: resolvedUser
1703
- };
1704
- }
1705
- const DEFAULT_CONFIG_FILENAME = "tailor.config.ts";
1706
- /**
1707
- * Load config path from command options, environment variables, or search parent directories.
1708
- * In CLI context, env fallback is also handled by politty's arg env option.
1709
- * Priority: opts/config > env/config > search parent directories
1710
- * @param configPath - Optional explicit config path
1711
- * @returns Resolved config path or undefined
1712
- */
1713
- function loadConfigPath(configPath) {
1714
- if (configPath) return configPath;
1715
- if (process.env.TAILOR_CONFIG_PATH) return process.env.TAILOR_CONFIG_PATH;
1716
- return findUpSync(DEFAULT_CONFIG_FILENAME);
1717
- }
1718
-
1719
- //#endregion
1720
- //#region src/cli/shared/mock.ts
1721
- /**
1722
- * Install a stub `globalThis.tailordb` so that user code loaded by the CLI
1723
- * (e.g. via `createGetDB` in `@tailor-platform/sdk/kysely`) can reference
1724
- * `tailordb.Client` without hitting a `ReferenceError`. The CLI never
1725
- * actually executes the user code paths that issue queries, so a no-op
1726
- * client suffices.
1727
- *
1728
- * Exposed as a function (rather than a top-level statement) so that
1729
- * `package.json#sideEffects` can keep the file marked side-effect-free
1730
- * without bundlers eliminating the install step.
1731
- */
1732
- function installCliTailordbStub() {
1733
- globalThis.tailordb = { Client: class {
1734
- constructor(_config) {}
1735
- async connect() {}
1736
- async end() {}
1737
- async queryObject() {
1738
- return {};
1739
- }
1740
- } };
1741
- }
1742
-
1743
- //#endregion
1744
- //#region src/cli/shared/config-loader.ts
1745
- /**
1746
- * Load Tailor configuration file and associated plugins.
1747
- * @param configPath - Optional explicit config path
1748
- * @param options - Optional module import behavior.
1749
- * @returns Loaded config, plugins, and config path
1750
- */
1751
- async function loadConfig(configPath, options = {}) {
1752
- installCliTailordbStub();
1753
- const foundPath = loadConfigPath(configPath);
1754
- if (!foundPath) throw new Error("Configuration file not found: tailor.config.ts not found in current or parent directories");
1755
- const resolvedPath = path.resolve(process.cwd(), foundPath);
1756
- if (!fs$1.existsSync(resolvedPath)) throw new Error(`Configuration file not found: ${configPath}`);
1757
- const configUrl = pathToFileURL(resolvedPath);
1758
- if (options.importNonce) configUrl.searchParams.set("tailorImportNonce", options.importNonce);
1759
- const configModule = await import(configUrl.href);
1760
- if (!configModule || !configModule.default) throw new Error("Invalid Tailor config module: default export not found");
1761
- const validated = AppConfigSchema.safeParse(configModule.default);
1762
- if (!validated.success) {
1763
- const issues = validated.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
1764
- throw new Error(`Invalid Tailor config in ${resolvedPath}:\n${issues}`);
1765
- }
1766
- const allPlugins = [];
1767
- for (const value of Object.values(configModule)) if (Array.isArray(value)) {
1768
- const pluginParsed = value.reduce((acc, item) => {
1769
- if (!acc.success) return acc;
1770
- const result = PluginConfigSchema.safeParse(item);
1771
- if (result.success) acc.items.push(result.data);
1772
- else acc.success = false;
1773
- return acc;
1774
- }, {
1775
- success: true,
1776
- items: []
1777
- });
1778
- if (pluginParsed.success && pluginParsed.items.length > 0) allPlugins.push(...pluginParsed.items);
1779
- }
1780
- return {
1781
- config: {
1782
- ...configModule.default,
1783
- path: resolvedPath
1784
- },
1785
- plugins: allPlugins
1786
- };
1787
- }
1788
-
1789
671
  //#endregion
1790
672
  //#region src/cli/cache/hasher.ts
1791
673
  /**
@@ -1802,7 +684,7 @@ function hashContent(content) {
1802
684
  * @returns Hex-encoded SHA-256 hash of the file content
1803
685
  */
1804
686
  function hashFile(filePath) {
1805
- const content = fs$1.readFileSync(filePath);
687
+ const content = fs.readFileSync(filePath);
1806
688
  return crypto.createHash("sha256").update(content).digest("hex");
1807
689
  }
1808
690
  /**
@@ -1931,7 +813,10 @@ function createBundleCache(store) {
1931
813
  return;
1932
814
  }
1933
815
  if (currentHash !== entry.inputHash) return;
1934
- return store.restoreBundleContent(cacheKey);
816
+ const content = store.restoreBundleContent(cacheKey);
817
+ const output = entry.outputFiles.find((file) => file.outputPath === cacheKey);
818
+ if (content === void 0 || !output || hashContent(content) !== output.contentHash) return;
819
+ return content;
1935
820
  }
1936
821
  function save(params) {
1937
822
  const { kind, namespace, name, sourceFile, content, dependencyPaths, contextHash } = params;
@@ -1978,7 +863,7 @@ function resolveRelativePluginImportPath(pluginImportPath, baseDirs) {
1978
863
  if (!pluginImportPath.startsWith(".")) return null;
1979
864
  for (const baseDir of baseDirs) {
1980
865
  const absolutePath = path.resolve(baseDir, pluginImportPath);
1981
- if (fs$1.existsSync(absolutePath)) return absolutePath;
866
+ if (fs.existsSync(absolutePath)) return absolutePath;
1982
867
  }
1983
868
  return null;
1984
869
  }
@@ -2062,13 +947,13 @@ function generatePluginExecutorFiles(executors, outputDir, typeGenerationResult,
2062
947
  function generateSingleExecutorFile(info, outputDir, typeGenerationResult, sourceTypeInfoMap, baseDirs = []) {
2063
948
  const pluginDir = sanitizePluginId$1(info.pluginId);
2064
949
  const executorOutputDir = path.join(outputDir, pluginDir, "executors");
2065
- fs$1.mkdirSync(executorOutputDir, { recursive: true });
950
+ fs.mkdirSync(executorOutputDir, { recursive: true });
2066
951
  const fileName = sanitizeExecutorFileName(info.executor.name);
2067
952
  const filePath = path.join(executorOutputDir, `${fileName}.ts`);
2068
953
  let content;
2069
954
  if (isPluginExecutorWithFile(info.executor)) content = generateExecutorFileContentNew(info, info.executor, outputDir, typeGenerationResult, sourceTypeInfoMap, baseDirs);
2070
955
  else content = generateExecutorFileContentLegacy(info.executor);
2071
- fs$1.writeFileSync(filePath, content);
956
+ fs.writeFileSync(filePath, content);
2072
957
  return filePath;
2073
958
  }
2074
959
  /**
@@ -2300,7 +1185,7 @@ function extractDynamicImportSpecifier(resolve) {
2300
1185
  function resolvePluginBaseDir(pluginImportPath, baseDirs) {
2301
1186
  if (pluginImportPath.startsWith(".")) {
2302
1187
  const resolvedPath = resolveRelativePluginImportPath(pluginImportPath, baseDirs) ?? path.resolve(baseDirs[0] ?? process.cwd(), pluginImportPath);
2303
- if (fs$1.existsSync(resolvedPath)) return fs$1.statSync(resolvedPath).isDirectory() ? resolvedPath : path.dirname(resolvedPath);
1188
+ if (fs.existsSync(resolvedPath)) return fs.statSync(resolvedPath).isDirectory() ? resolvedPath : path.dirname(resolvedPath);
2304
1189
  return path.extname(resolvedPath) ? path.dirname(resolvedPath) : resolvedPath;
2305
1190
  }
2306
1191
  for (const baseDir of baseDirs) try {
@@ -2387,11 +1272,11 @@ function generatePluginTypeFiles(types, outputDir) {
2387
1272
  seenTypeNames.set(info.type.name, info);
2388
1273
  const pluginDir = sanitizePluginId(info.pluginId);
2389
1274
  const typeOutputDir = path.join(outputDir, pluginDir, "types");
2390
- fs$1.mkdirSync(typeOutputDir, { recursive: true });
1275
+ fs.mkdirSync(typeOutputDir, { recursive: true });
2391
1276
  const fileName = `${toKebabCase(info.type.name)}.ts`;
2392
1277
  const filePath = path.join(typeOutputDir, fileName);
2393
1278
  const content = generateTypeFileContent(info);
2394
- fs$1.writeFileSync(filePath, content);
1279
+ fs.writeFileSync(filePath, content);
2395
1280
  generatedFiles.push(filePath);
2396
1281
  const relativePath = path.relative(outputDir, filePath);
2397
1282
  typeFilePaths.set(info.type.name, relativePath);
@@ -2512,20 +1397,6 @@ function toCamelCase(str) {
2512
1397
  return result.charAt(0).toLowerCase() + result.slice(1);
2513
1398
  }
2514
1399
 
2515
- //#endregion
2516
- //#region src/cli/services/stale-cleanup.ts
2517
- /**
2518
- * Remove stale `.entry.js` files from the output directory.
2519
- *
2520
- * Must be called before parallel bundling; concurrent builds
2521
- * sharing the same output directory would otherwise conflict.
2522
- * @param outputDir - Directory to clean
2523
- */
2524
- async function removeStaleEntryFiles(outputDir) {
2525
- const files = await fs.readdir(outputDir);
2526
- await Promise.all(files.filter((file) => file.endsWith(".entry.js")).map((file) => fs.rm(path.join(outputDir, file), { force: true })));
2527
- }
2528
-
2529
1400
  //#endregion
2530
1401
  //#region src/cli/services/workflow/ast-utils.ts
2531
1402
  /**
@@ -2934,7 +1805,7 @@ async function buildTriggerContext(workflowConfig, authNamespace) {
2934
1805
  authNamespace
2935
1806
  };
2936
1807
  for (const file of loadFilesWithIgnores(workflowConfig)) try {
2937
- const source = await fs$1.promises.readFile(file, "utf-8");
1808
+ const source = await fs.promises.readFile(file, "utf-8");
2938
1809
  const { program } = parseSync("input.ts", source);
2939
1810
  modules.set(normalizeFilePath(file), createModuleBindings(program, source));
2940
1811
  } catch (error) {
@@ -3264,13 +2135,39 @@ const platformBundleDefinePlugin = {
3264
2135
  }
3265
2136
  };
3266
2137
 
2138
+ //#endregion
2139
+ //#region src/cli/shared/virtual-entry.ts
2140
+ /**
2141
+ * Create an in-memory rolldown entry module with a deterministic ID.
2142
+ * @param name - Logical entry name
2143
+ * @param code - Entry module source
2144
+ * @param sourceType - Parser type for the generated module
2145
+ * @returns Rolldown input and plugin for loading the entry
2146
+ */
2147
+ function createVirtualEntry(name, code, sourceType = "js") {
2148
+ const input = `tailor-sdk-entry:${name}.${sourceType}`;
2149
+ const resolvedId = `\0${input}`;
2150
+ return {
2151
+ input,
2152
+ plugin: {
2153
+ name: "tailor-sdk-virtual-entry",
2154
+ resolveId(source, importer) {
2155
+ return source === input && importer === void 0 ? resolvedId : null;
2156
+ },
2157
+ load(id) {
2158
+ return id === resolvedId ? code : null;
2159
+ }
2160
+ }
2161
+ };
2162
+ }
2163
+
3267
2164
  //#endregion
3268
2165
  //#region src/cli/services/auth/bundler.ts
3269
2166
  /**
3270
2167
  * Bundle a single auth hook handler.
3271
2168
  *
3272
2169
  * Follows the same pattern as the executor bundler:
3273
- * 1. Generate an entry file that re-exports the handler as `main`
2170
+ * 1. Generate an in-memory entry module that re-exports the handler as `main`
3274
2171
  * 2. Bundle with rolldown + tree-shaking
3275
2172
  * @param options - Bundle options
3276
2173
  * @returns Map of function name to bundled code
@@ -3279,9 +2176,6 @@ async function bundleAuthHooks(options) {
3279
2176
  const { configPath, authName, handlerAccessPath, env = {}, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG" } = options;
3280
2177
  logger.newline();
3281
2178
  logger.log(`Bundling auth hook for ${styles.info(`"${authName}"`)}`);
3282
- const outputDir = path.resolve(getDistDir(), "auth-hooks");
3283
- fs$1.mkdirSync(outputDir, { recursive: true });
3284
- await removeStaleEntryFiles(outputDir);
3285
2179
  let tsconfig;
3286
2180
  try {
3287
2181
  tsconfig = await resolveTSConfig();
@@ -3306,7 +2200,6 @@ async function bundleAuthHooks(options) {
3306
2200
  prefix: sortedEnvPrefix
3307
2201
  }),
3308
2202
  async build(cachePlugins) {
3309
- const entryPath = path.join(outputDir, `${functionName}.entry.js`);
3310
2203
  const entryContent = multiline`
3311
2204
  import _config from "${absoluteConfigPath}";
3312
2205
  const __auth_hook_function = _config.${handlerAccessPath};
@@ -3315,12 +2208,13 @@ async function bundleAuthHooks(options) {
3315
2208
  return await __auth_hook_function({ ...args, env });
3316
2209
  }
3317
2210
  `;
3318
- fs$1.writeFileSync(entryPath, entryContent);
2211
+ const entry = createVirtualEntry(`auth-hook:${functionName}`, entryContent);
3319
2212
  const triggerPlugin = createTriggerTransformPlugin(triggerContext);
3320
- const plugins = triggerPlugin ? [triggerPlugin] : [];
2213
+ const plugins = [entry.plugin];
2214
+ if (triggerPlugin) plugins.push(triggerPlugin);
3321
2215
  plugins.push(platformBundleDefinePlugin, ...cachePlugins);
3322
2216
  return (await rolldown.build({
3323
- input: entryPath,
2217
+ input: entry.input,
3324
2218
  write: false,
3325
2219
  output: {
3326
2220
  format: "esm",
@@ -3685,6 +2579,36 @@ function findOmittedPermitRules(rawPermissions) {
3685
2579
  });
3686
2580
  return locations;
3687
2581
  }
2582
+ /**
2583
+ * Check whether GraphQL exposure is fully disabled for a type, given its
2584
+ * effective gqlOperations (the type's own setting, falling back to the
2585
+ * TailorDB namespace default). `undefined` means the default of all
2586
+ * operations enabled, so it is never considered fully disabled.
2587
+ * @param gqlOperations - Effective, normalized gqlOperations configuration
2588
+ * @returns Whether create, update, delete, and read are all explicitly disabled
2589
+ */
2590
+ function isGqlOperationsFullyDisabled(gqlOperations) {
2591
+ if (!gqlOperations) return false;
2592
+ return gqlOperations.create === false && gqlOperations.update === false && gqlOperations.delete === false && gqlOperations.read === false;
2593
+ }
2594
+ /**
2595
+ * Find missing permission configuration for a TailorDB type.
2596
+ *
2597
+ * Record-level permission is always required: TailorDB denies all record
2598
+ * operations for a type without it, regardless of whether the type is
2599
+ * exposed via GraphQL. GraphQL permission is required whenever GraphQL
2600
+ * exposure is enabled for the type (the default, unless every operation is
2601
+ * explicitly disabled via `gqlOperations`).
2602
+ * @param rawPermissions - Raw permissions definition for the type
2603
+ * @param effectiveGqlOperations - The type's own gqlOperations, falling back to the namespace default
2604
+ * @returns Which permission configuration, if any, is missing
2605
+ */
2606
+ function findMissingPermissionConfig(rawPermissions, effectiveGqlOperations) {
2607
+ return {
2608
+ missingPermission: !rawPermissions.record,
2609
+ missingGqlPermission: !rawPermissions.gql && !isGqlOperationsFullyDisabled(effectiveGqlOperations)
2610
+ };
2611
+ }
3688
2612
 
3689
2613
  //#endregion
3690
2614
  //#region src/parser/service/tailordb/type-parser.ts
@@ -4221,7 +3145,7 @@ function buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFi
4221
3145
  ].join("\n");
4222
3146
  }
4223
3147
  async function bundleScriptTarget(args) {
4224
- const { fn, kind, sourceFilePath, sourceBindings, tempDir, targetIndex, tsconfig } = args;
3148
+ const { fn, kind, sourceFilePath, sourceBindings, typeName, targetIndex, tsconfig } = args;
4225
3149
  const context = `${kind} in ${sourceFilePath}`;
4226
3150
  const fnSource = stringifyFunction(fn);
4227
3151
  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.`);
@@ -4233,11 +3157,10 @@ async function bundleScriptTarget(args) {
4233
3157
  if (unresolved.length > 0) throw new Error(`${context} captures unresolvable variables (${unresolved.join(", ")}). Hooks and validators must not reference variables that cannot be resolved from the source file.
4234
3158
  ${kind}: ${fnSource}`);
4235
3159
  const entryContent = buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath, kind === "typeValidate");
4236
- const entryPath = join(tempDir, `tailordb-script-${targetIndex}.entry.ts`);
4237
- writeFileSync(entryPath, entryContent);
3160
+ const entry = createVirtualEntry(`tailordb-script:${typeName}:${targetIndex}`, entryContent, "ts");
4238
3161
  const bundledCode = (await rolldown.build({
4239
- plugins: [platformBundleDefinePlugin],
4240
- input: entryPath,
3162
+ plugins: [entry.plugin, platformBundleDefinePlugin],
3163
+ input: entry.input,
4241
3164
  write: false,
4242
3165
  output: {
4243
3166
  format: "cjs",
@@ -4267,27 +3190,18 @@ async function precompileTailorDBTypeScripts(type, sourceFilePath, tsconfig) {
4267
3190
  const targets = collectScriptTargets(type);
4268
3191
  if (targets.length === 0) return;
4269
3192
  const sourceBindings = collectSourceBindings(sourceFilePath);
4270
- const tempDir = resolve(getDistDir(), "hooks-validate-scripts", type.name);
4271
- mkdirSync(tempDir, { recursive: true });
4272
- try {
4273
- const results = await Promise.allSettled(targets.map((target, index) => bundleScriptTarget({
4274
- fn: target.fn,
4275
- kind: target.kind,
4276
- sourceFilePath,
4277
- sourceBindings,
4278
- tempDir,
4279
- targetIndex: index,
4280
- tsconfig
4281
- })));
4282
- const firstError = results.find((r) => r.status === "rejected");
4283
- if (firstError) throw firstError.reason;
4284
- for (const [index, result] of results.entries()) if (result.status === "fulfilled") setPrecompiledScriptExpr(assertDefined(targets[index], `bundle target at index ${index} missing`).fn, result.value);
4285
- } finally {
4286
- rmSync(tempDir, {
4287
- recursive: true,
4288
- force: true
4289
- });
4290
- }
3193
+ const results = await Promise.allSettled(targets.map((target, index) => bundleScriptTarget({
3194
+ fn: target.fn,
3195
+ kind: target.kind,
3196
+ sourceFilePath,
3197
+ sourceBindings,
3198
+ typeName: type.name,
3199
+ targetIndex: index,
3200
+ tsconfig
3201
+ })));
3202
+ const firstError = results.find((r) => r.status === "rejected");
3203
+ if (firstError) throw firstError.reason;
3204
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") setPrecompiledScriptExpr(assertDefined(targets[index], `bundle target at index ${index} missing`).fn, result.value);
4291
3205
  }
4292
3206
 
4293
3207
  //#endregion
@@ -4445,6 +3359,19 @@ function createTailorDBService(params) {
4445
3359
  if (locations.length > 0) logger.warn(`TailorDB type "${typeName}" has permission rule(s) ${locations.join(", ")} in object form without an explicit "permit"; they default to "deny". Set permit: true (allow) or permit: false (deny) to silence this warning.`);
4446
3360
  }
4447
3361
  };
3362
+ const validateRequiredPermissions = () => {
3363
+ const errors = [];
3364
+ for (const fileTypes of Object.values(rawTypes)) for (const [typeName, type] of Object.entries(fileTypes)) {
3365
+ const effectiveGqlOperations = type.metadata.settings?.gqlOperations ?? config.gqlOperations;
3366
+ const { missingPermission, missingGqlPermission } = findMissingPermissionConfig(type.metadata.permissions, effectiveGqlOperations);
3367
+ if (!missingPermission && !missingGqlPermission) continue;
3368
+ const source = formatTailorDBTypeSourceInfo(typeSourceInfo[typeName]);
3369
+ const location = source ? ` (${source})` : "";
3370
+ if (missingPermission) errors.push(`TailorDB type "${typeName}"${location} has no .permission() configured. TailorDB denies all record operations for types without permission; call .permission(...) to grant access explicitly.`);
3371
+ if (missingGqlPermission) errors.push(`TailorDB type "${typeName}"${location} has no .gqlPermission() configured, but GraphQL operations are enabled for it. Call .gqlPermission(...) to grant GraphQL access explicitly, or disable GraphQL exposure with .features({ gqlOperations: { create: false, update: false, delete: false, read: false } }).`);
3372
+ }
3373
+ if (errors.length > 0) throw new Error(`TailorDB permission configuration errors in service "${namespace}":\n${errors.map((e) => ` - ${e}`).join("\n")}`);
3374
+ };
4448
3375
  /**
4449
3376
  * Process plugins for a type and add generated types to rawTypes
4450
3377
  * @param rawType - The raw TailorDB type being processed
@@ -4537,6 +3464,7 @@ function createTailorDBService(params) {
4537
3464
  else await Promise.all(typeFiles.map((typeFile) => loadTypeFile(typeFile, tsconfig)));
4538
3465
  doParseTypes();
4539
3466
  warnOmittedPermit();
3467
+ validateRequiredPermissions();
4540
3468
  return types;
4541
3469
  })();
4542
3470
  return loadPromise;
@@ -4578,7 +3506,10 @@ function createTailorDBService(params) {
4578
3506
  hasGeneratedTypes = true;
4579
3507
  logger.log(` Generated: ${styles.success(generatedType.name)} by namespace plugin ${styles.info(pluginId)}`);
4580
3508
  }
4581
- if (hasGeneratedTypes || hadPreviousGeneratedTypes) doParseTypes();
3509
+ if (hasGeneratedTypes || hadPreviousGeneratedTypes) {
3510
+ doParseTypes();
3511
+ validateRequiredPermissions();
3512
+ }
4582
3513
  }
4583
3514
  };
4584
3515
  }
@@ -4665,9 +3596,31 @@ function resolveBundleConcurrency() {
4665
3596
  * @param worker - Async worker function
4666
3597
  * @returns Worker results in input order
4667
3598
  */
4668
- function withBundleConcurrency(items, worker) {
4669
- const limit = pLimit(resolveBundleConcurrency());
4670
- return Promise.all(items.map((item) => limit(() => worker(item))));
3599
+ async function withBundleConcurrency(items, worker) {
3600
+ const resultCount = items.length;
3601
+ const workItems = items.flatMap((item, index) => [{
3602
+ index,
3603
+ item
3604
+ }]);
3605
+ const results = [];
3606
+ results.length = resultCount;
3607
+ let nextWorkIndex = 0;
3608
+ let rejection;
3609
+ const runWorker = async () => {
3610
+ while (!rejection) {
3611
+ const workItem = workItems[nextWorkIndex++];
3612
+ if (workItem === void 0) return;
3613
+ try {
3614
+ results[workItem.index] = await worker(workItem.item);
3615
+ } catch (reason) {
3616
+ rejection ??= { reason };
3617
+ }
3618
+ }
3619
+ };
3620
+ const workerCount = Math.min(resolveBundleConcurrency(), workItems.length);
3621
+ await Promise.all(Array.from({ length: workerCount }, runWorker));
3622
+ if (rejection) throw rejection.reason;
3623
+ return results;
4671
3624
  }
4672
3625
 
4673
3626
  //#endregion
@@ -4678,7 +3631,7 @@ function withBundleConcurrency(items, worker) {
4678
3631
  * Two delivery paths:
4679
3632
  * - Apply config: shipped with apply and evaluated by the platform before
4680
3633
  * invoking user code.
4681
- * - Bundle inline: interpolated into the generated `.entry.js` wrapper and
3634
+ * - Bundle inline: interpolated into the generated entry module and
4682
3635
  * evaluated inside the bundled script at function entry.
4683
3636
  *
4684
3637
  * The principal field mapping (server → SDK) shared across services is built by
@@ -4768,7 +3721,7 @@ function buildResolverOperationHookExpr(env) {
4768
3721
  * Bundle executors from the specified configuration
4769
3722
  *
4770
3723
  * This function:
4771
- * 1. Creates entry file that extracts operation.body
3724
+ * 1. Creates an in-memory entry module that extracts operation.body
4772
3725
  * 2. Bundles in a single step with tree-shaking
4773
3726
  * @param options - Bundle executor options
4774
3727
  * @returns Map of executor name to bundled code
@@ -4803,21 +3756,18 @@ async function bundleExecutors(options) {
4803
3756
  logger.debug(" No function executors to bundle");
4804
3757
  return bundledCode;
4805
3758
  }
4806
- const outputDir = path.resolve(getDistDir(), "executors");
4807
- fs$1.mkdirSync(outputDir, { recursive: true });
4808
- await removeStaleEntryFiles(outputDir);
4809
3759
  let tsconfig;
4810
3760
  try {
4811
3761
  tsconfig = await resolveTSConfig();
4812
3762
  } catch {
4813
3763
  tsconfig = void 0;
4814
3764
  }
4815
- const results = await withBundleConcurrency(executors, (executor) => bundleSingleExecutor(executor, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
3765
+ const results = await withBundleConcurrency(executors, (executor) => bundleSingleExecutor(executor, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
4816
3766
  for (const [name, code] of results) bundledCode.set(name, code);
4817
3767
  logger.log(`${styles.success("Bundled")} ${styles.info("\"executor\"")}`);
4818
3768
  return bundledCode;
4819
3769
  }
4820
- async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
3770
+ async function bundleSingleExecutor(executor, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
4821
3771
  const serializedTriggerContext = serializeTriggerContext(triggerContext);
4822
3772
  const contextHash = computeBundlerContextHash({
4823
3773
  sourceFile: executor.sourceFile,
@@ -4833,7 +3783,6 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContex
4833
3783
  sourceFile: executor.sourceFile,
4834
3784
  contextHash,
4835
3785
  async build(cachePlugins) {
4836
- const entryPath = path.join(outputDir, `${executor.name}.entry.js`);
4837
3786
  const entryContent = multiline`
4838
3787
  import _internalExecutor from "${path.resolve(executor.sourceFile)}";
4839
3788
 
@@ -4844,12 +3793,13 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContex
4844
3793
 
4845
3794
  export { __executor_function as main };
4846
3795
  `;
4847
- fs$1.writeFileSync(entryPath, entryContent);
3796
+ const entry = createVirtualEntry(`executor:${executor.name}`, entryContent);
4848
3797
  const triggerPlugin = createTriggerTransformPlugin(triggerContext);
4849
- const plugins = triggerPlugin ? [triggerPlugin] : [];
3798
+ const plugins = [entry.plugin];
3799
+ if (triggerPlugin) plugins.push(triggerPlugin);
4850
3800
  plugins.push(platformBundleDefinePlugin, ...cachePlugins);
4851
3801
  return (await rolldown.build({
4852
- input: entryPath,
3802
+ input: entry.input,
4853
3803
  write: false,
4854
3804
  output: {
4855
3805
  format: "esm",
@@ -4932,8 +3882,6 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
4932
3882
  };
4933
3883
  logger.newline();
4934
3884
  logger.log(`Bundling ${styles.highlight(adapters.length.toString())} files for ${styles.info("\"http-adapter\"")}`);
4935
- const outputDir = path.resolve(getDistDir(), "http-adapters");
4936
- fs$1.mkdirSync(outputDir, { recursive: true });
4937
3885
  let tsconfig;
4938
3886
  try {
4939
3887
  tsconfig = await resolveTSConfig();
@@ -4945,7 +3893,7 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
4945
3893
  adapter,
4946
3894
  kind
4947
3895
  }));
4948
- }), ({ adapter, kind }) => bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bundleLogLevel));
3896
+ }), ({ adapter, kind }) => bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel));
4949
3897
  const bundledInputs = /* @__PURE__ */ new Map();
4950
3898
  const bundledOutputs = /* @__PURE__ */ new Map();
4951
3899
  for (const [name, kind, code] of results) if (kind === "input") bundledInputs.set(name, code);
@@ -4956,7 +3904,7 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
4956
3904
  bundledOutputs
4957
3905
  };
4958
3906
  }
4959
- async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bundleLogLevel = "DEBUG") {
3907
+ async function bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel = "DEBUG") {
4960
3908
  const contextHash = computeBundlerContextHash({
4961
3909
  sourceFile: adapter.sourceFile,
4962
3910
  serializedTriggerContext: kind === "input" ? adapter.methods.join(",") : "",
@@ -4972,9 +3920,11 @@ async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bu
4972
3920
  sourceFile: adapter.sourceFile,
4973
3921
  contextHash,
4974
3922
  async build(cachePlugins) {
4975
- const entryPath = path.join(outputDir, `${adapter.name}.${kind}.entry.js`);
4976
3923
  const absoluteSourcePath = path.resolve(adapter.sourceFile);
3924
+ const entryContent = kind === "input" ? buildInputEntry(absoluteSourcePath, adapter.methods, GRAPHQL_WEB_MODULE) : buildOutputEntry(absoluteSourcePath);
3925
+ const entry = createVirtualEntry(`http-adapter:${adapter.name}:${kind}`, entryContent);
4977
3926
  const plugins = [
3927
+ entry.plugin,
4978
3928
  {
4979
3929
  name: "http-adapter-reject-node-imports",
4980
3930
  resolveId(source) {
@@ -4998,30 +3948,21 @@ async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bu
4998
3948
  },
4999
3949
  ...cachePlugins
5000
3950
  ];
5001
- let bundled;
5002
- try {
5003
- const entryContent = kind === "input" ? buildInputEntry(absoluteSourcePath, adapter.methods, GRAPHQL_WEB_MODULE) : buildOutputEntry(absoluteSourcePath);
5004
- fs$1.writeFileSync(entryPath, entryContent);
5005
- bundled = (await rolldown.build({
5006
- input: entryPath,
5007
- write: false,
5008
- output: {
5009
- format: "iife",
5010
- sourcemap: false,
5011
- minify: true,
5012
- codeSplitting: false
5013
- },
5014
- tsconfig,
5015
- plugins,
5016
- transform: { target: "es2017" },
5017
- treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
5018
- logLevel: "silent"
5019
- })).output[0].code;
5020
- } finally {
5021
- try {
5022
- fs$1.rmSync(entryPath, { force: true });
5023
- } catch {}
5024
- }
3951
+ const bundled = (await rolldown.build({
3952
+ input: entry.input,
3953
+ write: false,
3954
+ output: {
3955
+ format: "iife",
3956
+ sourcemap: false,
3957
+ minify: true,
3958
+ codeSplitting: false
3959
+ },
3960
+ tsconfig,
3961
+ plugins,
3962
+ transform: { target: "es2017" },
3963
+ treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
3964
+ logLevel: "silent"
3965
+ })).output[0].code;
5025
3966
  const byteLength = Buffer.byteLength(bundled, "utf8");
5026
3967
  if (byteLength > ADAPTER_BUNDLE_ERROR_BYTES) throw new Error(`HTTP adapter "${adapter.name}" ${kind} script is ${byteLength} bytes, exceeding the ${ADAPTER_BUNDLE_ERROR_BYTES} byte limit`);
5027
3968
  if (byteLength > ADAPTER_BUNDLE_WARN_BYTES) logger.warn(`HTTP adapter "${adapter.name}" ${kind} script is ${byteLength} bytes, larger than the recommended ${ADAPTER_BUNDLE_WARN_BYTES} byte limit`);
@@ -5220,7 +4161,7 @@ async function loadResolver(resolverFilePath) {
5220
4161
  *
5221
4162
  * This function:
5222
4163
  * 1. Uses a transform plugin to add validation wrapper during bundling
5223
- * 2. Creates entry file
4164
+ * 2. Creates an in-memory entry module
5224
4165
  * 3. Bundles in a single step with tree-shaking
5225
4166
  * @param namespace - Resolver namespace name
5226
4167
  * @param config - Resolver file loading configuration
@@ -5251,21 +4192,18 @@ async function bundleResolvers(namespace, config, triggerContext, cache, inlineS
5251
4192
  sourceFile: file
5252
4193
  });
5253
4194
  }
5254
- const outputDir = path.resolve(getDistDir(), "resolvers");
5255
- fs$1.mkdirSync(outputDir, { recursive: true });
5256
- await removeStaleEntryFiles(outputDir);
5257
4195
  let tsconfig;
5258
4196
  try {
5259
4197
  tsconfig = await resolveTSConfig();
5260
4198
  } catch {
5261
4199
  tsconfig = void 0;
5262
4200
  }
5263
- const results = await withBundleConcurrency(resolvers, (resolver) => bundleSingleResolver(namespace, resolver, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
4201
+ const results = await withBundleConcurrency(resolvers, (resolver) => bundleSingleResolver(namespace, resolver, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
5264
4202
  for (const [name, code] of results) bundledCode.set(name, code);
5265
4203
  logger.log(`${styles.success("Bundled")} ${styles.info(`"${namespace}"`)}`);
5266
4204
  return bundledCode;
5267
4205
  }
5268
- async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
4206
+ async function bundleSingleResolver(namespace, resolver, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
5269
4207
  const serializedTriggerContext = serializeTriggerContext(triggerContext);
5270
4208
  const contextHash = computeBundlerContextHash({
5271
4209
  sourceFile: resolver.sourceFile,
@@ -5282,7 +4220,6 @@ async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, tr
5282
4220
  sourceFile: resolver.sourceFile,
5283
4221
  contextHash,
5284
4222
  async build(cachePlugins) {
5285
- const entryPath = path.join(outputDir, `${resolver.name}.entry.js`);
5286
4223
  const entryContent = multiline`
5287
4224
  import _internalResolver from "${path.resolve(resolver.sourceFile)}";
5288
4225
  import { t } from "@tailor-platform/sdk";
@@ -5309,12 +4246,13 @@ async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, tr
5309
4246
 
5310
4247
  export { $tailor_resolver_body as main };
5311
4248
  `;
5312
- fs$1.writeFileSync(entryPath, entryContent);
4249
+ const entry = createVirtualEntry(`resolver:${resolver.name}`, entryContent);
5313
4250
  const triggerPlugin = createTriggerTransformPlugin(triggerContext);
5314
- const plugins = triggerPlugin ? [triggerPlugin] : [];
4251
+ const plugins = [entry.plugin];
4252
+ if (triggerPlugin) plugins.push(triggerPlugin);
5315
4253
  plugins.push(platformBundleDefinePlugin, ...cachePlugins);
5316
4254
  return (await rolldown.build({
5317
- input: entryPath,
4255
+ input: entry.input,
5318
4256
  write: false,
5319
4257
  output: {
5320
4258
  format: "esm",
@@ -5502,7 +4440,7 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
5502
4440
  function safeRealpath(p) {
5503
4441
  const resolved = path.resolve(p);
5504
4442
  try {
5505
- return fs$1.realpathSync(resolved);
4443
+ return fs.realpathSync(resolved);
5506
4444
  } catch (e) {
5507
4445
  logger.debug(`realpathSync failed for ${resolved}: ${e instanceof Error ? e.message : e}`);
5508
4446
  return resolved;
@@ -5514,7 +4452,7 @@ function safeRealpath(p) {
5514
4452
  * This function:
5515
4453
  * 1. Detects which jobs are actually used (mainJobs + their dependencies)
5516
4454
  * 2. Uses a transform plugin to transform trigger calls during bundling
5517
- * 3. Creates entry file and bundles with tree-shaking
4455
+ * 3. Creates an in-memory entry module and bundles with tree-shaking
5518
4456
  *
5519
4457
  * Returns metadata about which jobs each workflow uses.
5520
4458
  * @param allJobs - All available job infos
@@ -5538,19 +4476,13 @@ async function bundleWorkflowJobs(allJobs, mainJobNames, env = {}, triggerContex
5538
4476
  const { usedJobs, mainJobDeps } = await filterUsedJobs(allJobs, mainJobNames, triggerContext);
5539
4477
  logger.newline();
5540
4478
  logger.log(`Bundling ${styles.highlight(usedJobs.length.toString())} files for ${styles.info("\"workflow-job\"")}`);
5541
- const outputDir = path.resolve(getDistDir(), "workflow-jobs");
5542
- fs$1.mkdirSync(outputDir, { recursive: true });
5543
- const currentJobNames = new Set(usedJobs.map((j) => j.name));
5544
- const existingFiles = fs$1.readdirSync(outputDir);
5545
- for (const file of existingFiles) if (file.endsWith(".js") && !currentJobNames.has(path.basename(file, ".js"))) fs$1.rmSync(path.join(outputDir, file), { force: true });
5546
- else if (file.endsWith(".js.map") && !currentJobNames.has(path.basename(file, ".js.map"))) fs$1.rmSync(path.join(outputDir, file), { force: true });
5547
4479
  let tsconfig;
5548
4480
  try {
5549
4481
  tsconfig = await resolveTSConfig();
5550
4482
  } catch {
5551
4483
  tsconfig = void 0;
5552
4484
  }
5553
- const results = await withBundleConcurrency(usedJobs, (job) => bundleSingleJob(job, usedJobs, outputDir, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel));
4485
+ const results = await withBundleConcurrency(usedJobs, (job) => bundleSingleJob(job, usedJobs, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel));
5554
4486
  const bundledCode = /* @__PURE__ */ new Map();
5555
4487
  for (const [name, code] of results) bundledCode.set(name, code);
5556
4488
  logger.log(`${styles.success("Bundled")} ${styles.info("\"workflow-job\"")}`);
@@ -5586,7 +4518,7 @@ async function filterUsedJobs(allJobs, mainJobNames, triggerContext) {
5586
4518
  const dependencies = /* @__PURE__ */ new Map();
5587
4519
  const fileResults = await Promise.all(Array.from(jobsBySourceFile.entries()).map(async ([sourceFile, jobs]) => {
5588
4520
  try {
5589
- const source = await fs$1.promises.readFile(sourceFile, "utf-8");
4521
+ const source = await fs.promises.readFile(sourceFile, "utf-8");
5590
4522
  const { program } = parseSync("input.ts", source);
5591
4523
  const detectedJobs = findAllJobs(program, source);
5592
4524
  const triggerCalls = detectResolvedTriggerCalls(program, source, triggerContext, sourceFile);
@@ -5626,7 +4558,7 @@ async function filterUsedJobs(allJobs, mainJobNames, triggerContext) {
5626
4558
  mainJobDeps
5627
4559
  };
5628
4560
  }
5629
- async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
4561
+ async function bundleSingleJob(job, allJobs, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
5630
4562
  const serializedTriggerContext = serializeTriggerContext(triggerContext);
5631
4563
  const sortedEnvPrefix = JSON.stringify(Object.fromEntries(Object.entries(env).toSorted(([a], [b]) => a.localeCompare(b))));
5632
4564
  const contextHash = computeBundlerContextHash({
@@ -5644,7 +4576,6 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
5644
4576
  sourceFile: job.sourceFile,
5645
4577
  contextHash,
5646
4578
  async build(cachePlugins) {
5647
- const entryPath = path.join(outputDir, `${job.name}.entry.js`);
5648
4579
  const absoluteSourcePath = path.resolve(job.sourceFile);
5649
4580
  const entryContent = multiline`
5650
4581
  import { ${job.exportName} } from "${absoluteSourcePath}";
@@ -5655,10 +4586,11 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
5655
4586
  return await ${job.exportName}.body(input, { env, invoker });
5656
4587
  }
5657
4588
  `;
5658
- fs$1.writeFileSync(entryPath, entryContent);
4589
+ const entry = createVirtualEntry(`workflow-job:${job.name}`, entryContent);
5659
4590
  const resolvedSourceFile = safeRealpath(job.sourceFile);
5660
4591
  const otherJobExportNames = allJobs.filter((candidate) => candidate.name !== job.name && safeRealpath(candidate.sourceFile) === resolvedSourceFile).map((j) => j.exportName);
5661
4592
  const plugins = [
4593
+ entry.plugin,
5662
4594
  {
5663
4595
  name: "workflow-transform",
5664
4596
  transform: {
@@ -5677,7 +4609,7 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
5677
4609
  ...cachePlugins
5678
4610
  ];
5679
4611
  return (await rolldown.build({
5680
- input: entryPath,
4612
+ input: entry.input,
5681
4613
  write: false,
5682
4614
  output: {
5683
4615
  format: "esm",
@@ -5736,7 +4668,7 @@ const RetryPolicySchema = z.strictObject({
5736
4668
  });
5737
4669
  const ConcurrencyPolicySchema = z.strictObject({ maxConcurrentExecutions: z.number().int().min(1).max(1e3).describe("Maximum number of concurrent executions (1-1000)") });
5738
4670
  const ExecutionPolicyNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/, "Invalid execution policy name: must match [a-z0-9-] (3-63 chars; must start and end with [a-z0-9])").describe("Workspace-unique execution policy name embedded in the resource TRN");
5739
- const ExecutionPolicyKeySchema = z.string().regex(/^[a-z0-9][a-z0-9_:.-]{0,62}[a-z0-9*]$/, "Invalid execution policy key: must match [a-z0-9_:.-] (2-64 chars; must start with [a-z0-9] and end with [a-z0-9] or a trailing '*')").describe("Execution policy key passed to triggerJobFunction's executionPolicyKey option");
4671
+ const ExecutionPolicyKeySchema = z.string().regex(/^[a-z0-9][a-z0-9_:.-]{0,62}[a-z0-9*]$/, "Invalid execution policy key: must match [a-z0-9_:.-] (2-64 chars; must start with [a-z0-9] and end with [a-z0-9] or a trailing '*')").describe("Execution policy key passed to startJobFunction's (or its frozen alias triggerJobFunction's) executionPolicyKey option");
5740
4672
  const WorkflowJobFunctionExecutionPolicySchema = z.strictObject({
5741
4673
  name: ExecutionPolicyNameSchema,
5742
4674
  key: ExecutionPolicyKeySchema,
@@ -6493,5 +5425,5 @@ async function loadApplication(params) {
6493
5425
  }
6494
5426
 
6495
5427
  //#endregion
6496
- export { fetchUserInfo as $, hasAnyUserTokenEntry as A, readPlatformConfig as B, createBundleCache as C, positiveIntArg as Ct, loadConfig as D, hashFile as E, workspaceArgs as Et, loadMachineUserName as F, writePlatformConfig as G, resolveConfigUser as H, loadPlatformClientConfig as I, fetchAll as J, closeConnectionPool as K, loadStoredUserTokens as L, loadAccessToken as M, loadConfigPath as N, deleteUserTokens as O, loadConsoleBaseUrl as P, fetchPlatformMachineUserToken as Q, loadWorkspaceId as R, hasGenerationHooks as S, parseDuration as St, hashContent as T, toPageDirection as Tt, resolveUserTokenKey as U, removeLegacyUserAlias as V, saveUserTokens as W, fetchMachineUserToken as X, fetchAllTolerant as Y, fetchPaged as Z, platformBundleDefinePlugin as _, nonNegativeIntArg as _t, resolveInlineSourcemap as a, initOperatorClient as at, resolveBundleLogLevel as b, pagedLogArgs as bt, WorkflowJobSchema as c, byName as ct, INVOKER_EXPR as d, confirmationArgs as dt, getConsoleBaseUrl as et, buildExecutorArgsExpr as f, deploymentArgs as ft, stringifyFunction as g, multiConfigArg as gt, assertUniqueTailorDBTypeNamesWithExternal as h, isVerbose as ht, resolverBundleKey as i, initOAuth2Client as it, hasUserTokenEntry as j, fetchLatestToken as k, ResolverSchema as l, commonArgs as lt, assertUniqueLocalTailorDBTypeNames as m, folderArgs as mt, generatePluginFilesIfNeeded as n, getOrNull as nt, getApplicationAuthNamespace as o, isDefaultPlatform as ot, buildResolverOperationHookExpr as p, durationArg as pt, defaultPlatformBaseUrl as q, loadApplication as r, getPlatformBaseUrl as rt, WorkflowJobFunctionExecutionPolicySchema as s, resolveStaticWebsiteUrls as st, defineApplication as t, getOAuth2ClientId as tt, HTTP_METHODS as u, configArg as ut, composeFunctionTreeshakeOptions as v, orderArg as vt, getDistDir as w, resolveMachineUserInputSource as wt, getPluginGenerationDependencies as x, paginationArgs as xt, createLogLevelTreeshakeOptions as y, organizationArgs as yt, platformConfigFromProfile as z };
6497
- //# sourceMappingURL=application-Bf8wxOi6.mjs.map
5428
+ export { fetchAllTolerant as A, initOperatorClient as B, createBundleCache as C, closeConnectionPool as D, hashFile as E, getConsoleBaseUrl as F, byName as G, normalizeBaseUrl as H, getOAuth2ClientId as I, LOG_LEVELS as K, getOrNull as L, fetchPaged as M, fetchPlatformMachineUserToken as N, defaultPlatformBaseUrl as O, fetchUserInfo as P, getPlatformBaseUrl as R, hasGenerationHooks as S, hashContent as T, rememberPlatformConfigForToken as U, isDefaultPlatform as V, resolveStaticWebsiteUrls as W, platformBundleDefinePlugin as _, resolveInlineSourcemap as a, resolveBundleLogLevel as b, WorkflowJobSchema as c, INVOKER_EXPR as d, buildExecutorArgsExpr as f, stringifyFunction as g, assertUniqueTailorDBTypeNamesWithExternal as h, resolverBundleKey as i, fetchMachineUserToken as j, fetchAll as k, ResolverSchema as l, assertUniqueLocalTailorDBTypeNames as m, generatePluginFilesIfNeeded as n, getApplicationAuthNamespace as o, buildResolverOperationHookExpr as p, loadApplication as r, WorkflowJobFunctionExecutionPolicySchema as s, defineApplication as t, HTTP_METHODS as u, composeFunctionTreeshakeOptions as v, getDistDir as w, getPluginGenerationDependencies as x, createLogLevelTreeshakeOptions as y, initOAuth2Client as z };
5429
+ //# sourceMappingURL=application-BJXRpQj5.mjs.map