@tailor-platform/sdk 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/README.md +1 -1
  3. package/dist/{application-BpLeawBg.mjs → application-C8a0UJ-z.mjs} +312 -166
  4. package/dist/application-C8a0UJ-z.mjs.map +1 -0
  5. package/dist/application-D4zOiLLa.mjs +3 -0
  6. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +47 -24
  7. package/dist/cli/commands/tailordb/migrate/generate.d.mts +6 -0
  8. package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +22 -0
  9. package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +1 -1
  10. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +30 -11
  11. package/dist/cli/commands/tailordb/truncate.d.mts +2 -2
  12. package/dist/cli/lib.mjs +2 -2
  13. package/dist/cli/lib.mjs.map +1 -1
  14. package/dist/cli/main.mjs +366 -137
  15. package/dist/cli/main.mjs.map +1 -1
  16. package/dist/cli/ts-hook.mjs +55 -8
  17. package/dist/completion/zsh-worker.zsh +38 -10
  18. package/dist/configure/config/index.d.mts +1 -1
  19. package/dist/configure/index.d.mts +3 -2
  20. package/dist/configure/index.mjs +92 -25
  21. package/dist/configure/index.mjs.map +1 -1
  22. package/dist/configure/services/aigateway/index.d.mts +5 -4
  23. package/dist/configure/services/aigateway/types.d.mts +6 -1
  24. package/dist/configure/services/auth/types.d.mts +2 -2
  25. package/dist/configure/services/index.d.mts +2 -2
  26. package/dist/configure/services/tailordb/permission.d.mts +1 -1
  27. package/dist/configure/services/tailordb/schema.d.mts +1 -1
  28. package/dist/configure/services/tailordb/types.d.mts +7 -4
  29. package/dist/configure/services/workflow/index.d.mts +2 -2
  30. package/dist/configure/services/workflow/wait-point.d.mts +72 -13
  31. package/dist/configure/types/auth-namespace-name.d.mts +18 -0
  32. package/dist/{crashreport-CINZ5dVN.mjs → crashreport-Cd4TqfZq.mjs} +1 -1
  33. package/dist/{crashreport-CIIfLnoO.mjs → crashreport-Q0pDAlrj.mjs} +8 -4
  34. package/dist/{crashreport-CIIfLnoO.mjs.map → crashreport-Q0pDAlrj.mjs.map} +1 -1
  35. package/dist/{errors-CWj21238.mjs → errors-Bxut5_Sy.mjs} +17 -3
  36. package/dist/{errors-CWj21238.mjs.map → errors-Bxut5_Sy.mjs.map} +1 -1
  37. package/dist/file-BUcmVIH2.mjs.map +1 -1
  38. package/dist/kysely/index.d.mts +2 -1
  39. package/dist/kysely/index.mjs +2 -1
  40. package/dist/kysely/index.mjs.map +1 -1
  41. package/dist/{logger-DSdExfor.mjs → logger-D0bEznPv.mjs} +9 -5
  42. package/dist/{logger-DSdExfor.mjs.map → logger-D0bEznPv.mjs.map} +1 -1
  43. package/dist/package-json-8b0O9TlX.mjs.map +1 -1
  44. package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
  45. package/dist/plugin/builtin/enum-constants/index.mjs +5 -5
  46. package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
  47. package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
  48. package/dist/plugin/builtin/file-utils/index.mjs +14 -11
  49. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  50. package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
  51. package/dist/plugin/builtin/kysely-type/index.mjs +9 -9
  52. package/dist/plugin/builtin/kysely-type/index.mjs.map +1 -1
  53. package/dist/plugin/builtin/seed/index.mjs +1 -1
  54. package/dist/plugin/builtin/seed/seed-type-processor.d.mts +4 -4
  55. package/dist/plugin/get-generated-type.d.mts +4 -4
  56. package/dist/plugin/index.mjs +13 -13
  57. package/dist/plugin/index.mjs.map +1 -1
  58. package/dist/plugin/types.d.mts +2 -2
  59. package/dist/plugin/with-context.d.mts +1 -1
  60. package/dist/{register-ts-hook-CTth1eqj.mjs → register-ts-hook-Vqjza10T.mjs} +2516 -857
  61. package/dist/register-ts-hook-Vqjza10T.mjs.map +1 -0
  62. package/dist/{repl-editor-BCozyiNq.mjs → repl-editor-F9LotMNx.mjs} +3 -2
  63. package/dist/{repl-editor-BCozyiNq.mjs.map → repl-editor-F9LotMNx.mjs.map} +1 -1
  64. package/dist/runtime/file.d.mts +14 -14
  65. package/dist/{schema--xYWRGfe.mjs → schema-D4TC4BC3.mjs} +4 -3
  66. package/dist/schema-D4TC4BC3.mjs.map +1 -0
  67. package/dist/seed/index.d.mts +4 -4
  68. package/dist/seed/index.mjs +3 -3
  69. package/dist/seed/index.mjs.map +1 -1
  70. package/dist/{seed-xqM9WIHP.mjs → seed-Cibjmtry.mjs} +46 -40
  71. package/dist/seed-Cibjmtry.mjs.map +1 -0
  72. package/dist/{service-goqlJJgA.mjs → service-2grcAOCJ.mjs} +61 -27
  73. package/dist/service-2grcAOCJ.mjs.map +1 -0
  74. package/dist/service-CaBPNDIZ.mjs +3 -0
  75. package/dist/{service-CCwl3Avt.mjs → service-LTfp03ZZ.mjs} +7 -5
  76. package/dist/{service-CCwl3Avt.mjs.map → service-LTfp03ZZ.mjs.map} +1 -1
  77. package/dist/types/aigateway.generated.d.mts +2 -2
  78. package/dist/types/auth.generated.d.mts +3 -3
  79. package/dist/types/executor.generated.d.mts +1 -1
  80. package/dist/types/tailordb.generated.d.mts +12 -12
  81. package/dist/utils/test/index.d.mts +4 -4
  82. package/dist/utils/test/index.mjs +4 -4
  83. package/dist/utils/test/index.mjs.map +1 -1
  84. package/dist/vitest/index.mjs +95 -7
  85. package/dist/vitest/index.mjs.map +1 -1
  86. package/dist/vitest/mocks/workflow.d.mts +13 -1
  87. package/dist/wait-point-invoker-__oE88_P.mjs +148 -0
  88. package/dist/wait-point-invoker-__oE88_P.mjs.map +1 -0
  89. package/dist/wait-point-registry-TL99zotw.mjs +47 -0
  90. package/dist/wait-point-registry-TL99zotw.mjs.map +1 -0
  91. package/docs/cli/application.md +3 -1
  92. package/docs/cli/function.md +17 -13
  93. package/docs/cli/setup.md +14 -1
  94. package/docs/cli/tailordb.md +30 -27
  95. package/docs/cli/workspace.md +2 -2
  96. package/docs/cli-reference.md +23 -22
  97. package/docs/configuration.md +2 -2
  98. package/docs/github-actions.md +73 -28
  99. package/docs/migration/v2.md +58 -4
  100. package/docs/migration/v3.md +41 -0
  101. package/docs/quickstart.md +1 -1
  102. package/docs/services/aigateway.md +33 -9
  103. package/docs/services/executor.md +5 -1
  104. package/docs/services/tailordb-migration.md +146 -44
  105. package/docs/services/tailordb.md +7 -7
  106. package/docs/services/workflow.md +52 -2
  107. package/docs/testing.md +13 -1
  108. package/package.json +8 -8
  109. package/dist/application-BpLeawBg.mjs.map +0 -1
  110. package/dist/application-BxLLiLsr.mjs +0 -3
  111. package/dist/register-ts-hook-CTth1eqj.mjs.map +0 -1
  112. package/dist/schema--xYWRGfe.mjs.map +0 -1
  113. package/dist/seed-xqM9WIHP.mjs.map +0 -1
  114. package/dist/service-_XmjSEGr.mjs +0 -3
  115. package/dist/service-goqlJJgA.mjs.map +0 -1
  116. package/dist/test-env-key-D7UkZp99.mjs +0 -75
  117. package/dist/test-env-key-D7UkZp99.mjs.map +0 -1
@@ -1,10 +1,11 @@
1
1
  import { n as isSdkBranded } from "./brand-Eo4pLXPJ.mjs";
2
+ import { n as getScopedWaitPoints } from "./wait-point-registry-TL99zotw.mjs";
2
3
  import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
3
- import { a as parseBoolean, n as logger, r as styles } from "./logger-DSdExfor.mjs";
4
- import { a as AuthConfigSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, g as functionSchema, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema } from "./service-goqlJJgA.mjs";
4
+ import { a as parseBoolean, n as logger, r as styles } from "./logger-D0bEznPv.mjs";
5
+ import { a as AuthConfigSchema, b as functionSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema, y as importUserModule } from "./service-2grcAOCJ.mjs";
5
6
  import { t as multiline } from "./multiline-sfHpTZZK.mjs";
6
7
  import { t as userAgent } from "./user-agent-Bgsszb5I.mjs";
7
- import { t as createCLIError } from "./errors-CWj21238.mjs";
8
+ import { t as createCLIError } from "./errors-Bxut5_Sy.mjs";
8
9
  import { n as isNodeBuiltinImport, t as getNodeBuiltinMessage } from "./node-builtins-CmaL2Cbq.mjs";
9
10
  import { t as isPluginGeneratedType } from "./type-source-DH_LH20p.mjs";
10
11
  import { createRequire } from "node:module";
@@ -16,7 +17,6 @@ import { create } from "@bufbuild/protobuf";
16
17
  import * as fs from "node:fs";
17
18
  import { readFileSync } from "node:fs";
18
19
  import { MethodOptions_IdempotencyLevel } from "@bufbuild/protobuf/wkt";
19
- import { pathToFileURL } from "node:url";
20
20
  import { OAuth2Client } from "@badgateway/oauth2-client";
21
21
  import { Code, ConnectError, createClient } from "@connectrpc/connect";
22
22
  import pLimit from "p-limit";
@@ -191,7 +191,8 @@ async function initOperatorClient(accessToken, config) {
191
191
  createTracingInterceptor(),
192
192
  concurrencyLimitInterceptor()
193
193
  ];
194
- return createClient(OperatorService, await createTransport(getPlatformBaseUrl(platformConfig), interceptors));
194
+ const transport = await createTransport(getPlatformBaseUrl(platformConfig), interceptors);
195
+ return createClient(OperatorService, transport);
195
196
  }
196
197
  /**
197
198
  * Create a Connect transport using connect-node (HTTP/2).
@@ -253,7 +254,7 @@ function retryInterceptor() {
253
254
  return (next) => async (req) => {
254
255
  if (req.stream) return await next(req);
255
256
  let lastError;
256
- for (let i = 0; i < 3; i++) {
257
+ for (let i = 0; i < MAX_RETRY_ATTEMPTS; i++) {
257
258
  if (i > 0) await waitRetryBackoff(i);
258
259
  try {
259
260
  return await next(req);
@@ -263,7 +264,7 @@ function retryInterceptor() {
263
264
  logger.debug(`retry: ${req.method.name} returned AlreadyExists on attempt ${i + 1}; treating as success (prior attempt likely committed)`);
264
265
  return synthesizeEmptyUnaryResponse(req);
265
266
  }
266
- const { reportCrash } = await import("./crashreport-CINZ5dVN.mjs");
267
+ const { reportCrash } = await import("./crashreport-Cd4TqfZq.mjs");
267
268
  await reportCrash(error, "handledError");
268
269
  }
269
270
  if (req.method.name !== "CreateWorkspace" && isRetirable(error, req.method.idempotency)) {
@@ -397,6 +398,8 @@ function synthesizeEmptyUnaryResponse(req) {
397
398
  * response was lost), which is what triggers the `already_exists` race.
398
399
  */
399
400
  const RETRY_BASE_DELAY_MS = 500;
401
+ /** Maximum number of attempts, including the initial one, for a retried request. */
402
+ const MAX_RETRY_ATTEMPTS = 3;
400
403
  /**
401
404
  * Wait for an exponential backoff delay with jitter.
402
405
  * @param attempt - Current retry attempt number (1-based)
@@ -424,6 +427,7 @@ function isRetirable(error, idempotency) {
424
427
  }
425
428
  /**
426
429
  * Create an interceptor that enhances error messages from the Operator API.
430
+ * @internal
427
431
  * @returns Error handling interceptor
428
432
  */
429
433
  function errorHandlingInterceptor() {
@@ -433,8 +437,8 @@ function errorHandlingInterceptor() {
433
437
  } catch (error) {
434
438
  if (error instanceof ConnectError) {
435
439
  const { operation, resourceType } = parseMethodName(req.method.name);
436
- const requestParams = formatRequestParams(req.message);
437
- throw new ConnectError(`Failed to ${operation} ${resourceType}: ${error.rawMessage}\nRequest: ${requestParams}`, error.code, error.metadata);
440
+ const identity = formatRequestIdentity(req.message, req.method.name);
441
+ throw new ConnectError(`Failed to ${operation} ${resourceType}${identity}: ${error.rawMessage}`, error.code, error.metadata);
438
442
  }
439
443
  throw error;
440
444
  }
@@ -457,28 +461,45 @@ function parseMethodName(methodName) {
457
461
  resourceType: resource
458
462
  };
459
463
  }
460
- /**
461
- * JSON.stringify replacer that converts BigInt values to strings.
462
- * @param _key - Object key (unused)
463
- * @param value - Value to serialize
464
- * @returns Serializable value
465
- */
466
- function bigIntReplacer(_key, value) {
467
- if (typeof value === "bigint") return value.toString();
468
- return value;
464
+ const IDENTITY_KEYS = /* @__PURE__ */ new Set(["name", "id"]);
465
+ const IDENTITY_KEY_SUFFIXES = [
466
+ "Name",
467
+ "Id",
468
+ "Namespace"
469
+ ];
470
+ const NESTED_IDENTITY_KEY = "name";
471
+ const METHOD_IDENTITY_KEYS = {
472
+ GetMetadata: ["trn"],
473
+ SetMetadata: ["trn"],
474
+ AddCustomDomain: ["domain"],
475
+ GetCustomDomain: ["domain"],
476
+ RemoveCustomDomain: ["domain"],
477
+ CreateWorkflowJobFunctionExecutionPolicy: ["executionPolicyKey"],
478
+ UpdateWorkflowJobFunctionExecutionPolicy: ["executionPolicyKey"],
479
+ GetWorkflowJobFunctionExecutionPolicyByKey: ["executionPolicyKey"]
480
+ };
481
+ function isIdentityKey(key, methodName) {
482
+ if (key.startsWith("$")) return false;
483
+ return IDENTITY_KEYS.has(key) || IDENTITY_KEY_SUFFIXES.some((suffix) => key.endsWith(suffix)) || (METHOD_IDENTITY_KEYS[methodName]?.includes(key) ?? false);
469
484
  }
470
485
  /**
471
- * @internal
472
- * @param message - Request message to format
473
- * @returns Pretty-printed JSON or error placeholder
474
- */
475
- function formatRequestParams(message) {
476
- try {
477
- if (message && typeof message === "object" && "toJson" in message) return JSON.stringify(message.toJson(), bigIntReplacer, 2);
478
- return JSON.stringify(message, bigIntReplacer, 2);
479
- } catch {
480
- return "(unable to serialize request)";
486
+ * Format an allowlisted identity suffix for enhanced error messages.
487
+ *
488
+ * Only resource identifiers are included — the rest of the request payload
489
+ * can carry credentials and must never reach terminal or CI logs.
490
+ * @param message - Request message to extract identifiers from
491
+ * @param methodName - RPC method name used to resolve method-scoped identifiers
492
+ * @returns Identity suffix like " (namespaceName: x, type.name: y)", or an empty string
493
+ */
494
+ function formatRequestIdentity(message, methodName) {
495
+ if (typeof message !== "object" || message === null) return "";
496
+ const parts = [];
497
+ for (const [key, value] of Object.entries(message)) if (typeof value === "string" && value !== "" && isIdentityKey(key, methodName)) parts.push(`${key}: ${value}`);
498
+ else if (!key.startsWith("$") && value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.$typeName === "string") {
499
+ const nestedName = value[NESTED_IDENTITY_KEY];
500
+ if (typeof nestedName === "string" && nestedName !== "") parts.push(`${key}.${NESTED_IDENTITY_KEY}: ${nestedName}`);
481
501
  }
502
+ return parts.length === 0 ? "" : ` (${parts.join(", ")})`;
482
503
  }
483
504
  const MAX_PAGE_SIZE = 1e3;
484
505
  /**
@@ -631,14 +652,15 @@ async function fetchMachineUserToken(url, clientId, clientSecret) {
631
652
  formData.append("grant_type", "client_credentials");
632
653
  formData.append("client_id", clientId);
633
654
  formData.append("client_secret", clientSecret);
634
- const resp = await fetch(tokenEndpoint, {
655
+ const request = {
635
656
  method: "POST",
636
657
  headers: {
637
658
  "User-Agent": await userAgent(),
638
659
  "Content-Type": "application/x-www-form-urlencoded"
639
660
  },
640
661
  body: formData
641
- });
662
+ };
663
+ const resp = await withConnectTimeoutRetry("machine user token request", () => fetch(tokenEndpoint, request));
642
664
  if (!resp.ok) {
643
665
  const body = await resp.text().catch(() => "");
644
666
  throw new Error(`Failed to fetch machine user token: ${resp.status} ${resp.statusText} ${body.slice(0, 500)}`);
@@ -650,6 +672,29 @@ async function fetchMachineUserToken(url, clientId, clientSecret) {
650
672
  expires_in: z.number()
651
673
  }).parse(rawJson);
652
674
  }
675
+ function isUndiciConnectTimeout(error) {
676
+ if (!(error instanceof TypeError)) return false;
677
+ const cause = error.cause;
678
+ return cause instanceof Error && "code" in cause && cause.code === "UND_ERR_CONNECT_TIMEOUT";
679
+ }
680
+ /**
681
+ * Retry a request that failed before the connection was established.
682
+ *
683
+ * Only `UND_ERR_CONNECT_TIMEOUT` is retried: the request provably never reached
684
+ * the server, so replaying it cannot duplicate a server-side effect.
685
+ * @param label - Request description for the retry debug log
686
+ * @param send - Sends the request; called once per attempt
687
+ * @returns The first successful result
688
+ */
689
+ async function withConnectTimeoutRetry(label, send) {
690
+ for (let attempt = 1;; attempt++) try {
691
+ return await send();
692
+ } catch (error) {
693
+ if (!isUndiciConnectTimeout(error) || attempt >= MAX_RETRY_ATTEMPTS) throw error;
694
+ logger.debug(`retry: ${label} attempt ${attempt} failed with UND_ERR_CONNECT_TIMEOUT; retrying`);
695
+ await waitRetryBackoff(attempt);
696
+ }
697
+ }
653
698
  /**
654
699
  * Fetch an OAuth2 token for a platform machine user via client_credentials grant.
655
700
  * @param clientId - Client ID for the platform machine user
@@ -658,12 +703,13 @@ async function fetchMachineUserToken(url, clientId, clientSecret) {
658
703
  * @returns OAuth2 token
659
704
  */
660
705
  async function fetchPlatformMachineUserToken(clientId, clientSecret, config) {
661
- return await new OAuth2Client({
706
+ const server = getPlatformBaseUrl(config);
707
+ return await withConnectTimeoutRetry("platform machine user token request", () => new OAuth2Client({
662
708
  clientId,
663
709
  clientSecret,
664
- server: getPlatformBaseUrl(config),
710
+ server,
665
711
  discoveryEndpoint: oauth2DiscoveryEndpoint
666
- }).clientCredentials();
712
+ }).clientCredentials());
667
713
  }
668
714
  /**
669
715
  * Close the global HTTP connection pool to prevent libuv UV_HANDLE_CLOSING
@@ -950,7 +996,7 @@ function isPluginExecutorWithFile(executor) {
950
996
  * @param executors - Array of plugin executor information
951
997
  * @param outputDir - Base output directory (e.g., .tailor)
952
998
  * @param typeGenerationResult - Result from plugin type generation (for import resolution)
953
- * @param sourceTypeInfoMap - Map of source type names to their source info
999
+ * @param sourceTypeInfoMap - Map of source table names to their source info
954
1000
  * @param configPath - Path to tailor.config.ts (used for resolving plugin import paths)
955
1001
  * @returns Array of generated file paths
956
1002
  */
@@ -971,7 +1017,7 @@ function generatePluginExecutorFiles(executors, outputDir, typeGenerationResult,
971
1017
  * @param info - Plugin executor metadata and definition
972
1018
  * @param outputDir - Base output directory (e.g., .tailor)
973
1019
  * @param typeGenerationResult - Result from plugin type generation
974
- * @param sourceTypeInfoMap - Map of source type names to their source info
1020
+ * @param sourceTypeInfoMap - Map of source table names to their source info
975
1021
  * @param baseDirs - Base directories for resolving plugin import paths
976
1022
  * @returns Absolute path to the generated file
977
1023
  */
@@ -994,7 +1040,7 @@ function generateSingleExecutorFile(info, outputDir, typeGenerationResult, sourc
994
1040
  * @param executor - Executor definition with resolve
995
1041
  * @param outputDir - Base output directory
996
1042
  * @param typeGenerationResult - Result from plugin type generation
997
- * @param sourceTypeInfoMap - Map of source type names to their source info
1043
+ * @param sourceTypeInfoMap - Map of source table names to their source info
998
1044
  * @param baseDirs - Base directories for resolving plugin import paths
999
1045
  * @returns TypeScript source code for executor file
1000
1046
  */
@@ -1029,7 +1075,7 @@ function generateExecutorFileContentNew(info, executor, outputDir, typeGeneratio
1029
1075
  * @param outputDir - Base output directory for generated files
1030
1076
  * @param pluginId - Plugin identifier used for output paths
1031
1077
  * @param typeGenerationResult - Result from plugin type generation
1032
- * @param sourceTypeInfoMap - Map of source type names to their source info
1078
+ * @param sourceTypeInfoMap - Map of source table names to their source info
1033
1079
  * @returns Map of context keys to their import information
1034
1080
  */
1035
1081
  function collectTypeImports(context, outputDir, pluginId, typeGenerationResult, sourceTypeInfoMap) {
@@ -1076,9 +1122,9 @@ function generateContextCode(context, typeImports) {
1076
1122
  return `{\n${entries.join(",\n")},\n}`;
1077
1123
  }
1078
1124
  /**
1079
- * Check if a value is a TailorDB type object.
1125
+ * Check if a value is a TailorDB table object.
1080
1126
  * @param value - Value to inspect
1081
- * @returns True if value is a type object with name and fields
1127
+ * @returns True if value is a table object with name and fields
1082
1128
  */
1083
1129
  function isTypeObject(value) {
1084
1130
  return typeof value === "object" && value !== null && "name" in value && "fields" in value && typeof value.name === "string";
@@ -1276,15 +1322,15 @@ function toKebabCase$1(str) {
1276
1322
  /**
1277
1323
  * Plugin Type Generator
1278
1324
  *
1279
- * Generates TypeScript files for plugin-generated types (TailorDB types).
1280
- * These files can be imported by plugin executors to reference generated types.
1325
+ * Generates TypeScript files for plugin-generated types (TailorDB tables).
1326
+ * These files can be imported by plugin executors to reference generated tables.
1281
1327
  */
1282
1328
  function isFieldDefinition(value) {
1283
1329
  return typeof value === "object" && value !== null;
1284
1330
  }
1285
1331
  /**
1286
- * Generate TypeScript files for plugin-generated types.
1287
- * These files export the type definition and can be imported by executor files.
1332
+ * Generate TypeScript files for plugin-generated tables.
1333
+ * These files export the table definition and can be imported by executor files.
1288
1334
  * @param types - Array of plugin type information
1289
1335
  * @param outputDir - Base output directory (e.g., .tailor)
1290
1336
  * @returns Generation result with file paths
@@ -1299,7 +1345,7 @@ function generatePluginTypeFiles(types, outputDir) {
1299
1345
  const seenTypeNames = /* @__PURE__ */ new Map();
1300
1346
  for (const info of types) {
1301
1347
  const existing = seenTypeNames.get(info.type.name);
1302
- if (existing) throw new Error(`Duplicate plugin-generated type name "${info.type.name}" detected. First: plugin "${existing.pluginId}" (kind: "${existing.kind}", source type: "${existing.sourceTypeName}"), Second: plugin "${info.pluginId}" (kind: "${info.kind}", source type: "${info.sourceTypeName}"). Plugin-generated type names must be unique.`);
1348
+ if (existing) throw new Error(`Duplicate plugin-generated table name "${info.type.name}" detected. First: plugin "${existing.pluginId}" (kind: "${existing.kind}", source: "${existing.sourceTypeName}"), Second: plugin "${info.pluginId}" (kind: "${info.kind}", source: "${info.sourceTypeName}"). Plugin-generated table names must be unique.`);
1303
1349
  seenTypeNames.set(info.type.name, info);
1304
1350
  const pluginDir = sanitizePluginId(info.pluginId);
1305
1351
  const typeOutputDir = path.join(outputDir, pluginDir, "types");
@@ -1330,8 +1376,8 @@ function generateTypeFileContent(info) {
1330
1376
  const fieldsCode = generateFieldsCode(type);
1331
1377
  return multiline`
1332
1378
  /**
1333
- * Auto-generated type by plugin: ${pluginId}
1334
- * Source type: ${sourceTypeName}
1379
+ * Auto-generated table by plugin: ${pluginId}
1380
+ * Source: ${sourceTypeName}
1335
1381
  * Kind: ${kind}
1336
1382
  *
1337
1383
  * DO NOT EDIT - This file is generated by @tailor-platform/sdk
@@ -1345,8 +1391,8 @@ function generateTypeFileContent(info) {
1345
1391
  }
1346
1392
  /**
1347
1393
  * Generate TypeScript code for field definitions.
1348
- * This creates a simplified version of the type's fields.
1349
- * @param type - TailorDB type
1394
+ * This creates a simplified version of the table's fields.
1395
+ * @param type - TailorDB table
1350
1396
  * @returns TypeScript code for fields object
1351
1397
  */
1352
1398
  function generateFieldsCode(type) {
@@ -1359,7 +1405,7 @@ function generateFieldsCode(type) {
1359
1405
  return `{\n${fieldEntries.join(",\n")},\n}`;
1360
1406
  }
1361
1407
  /**
1362
- * Map from TailorDB type to SDK method name.
1408
+ * Map from TailorDB table to SDK method name.
1363
1409
  */
1364
1410
  const typeToMethodMap = {
1365
1411
  string: "string",
@@ -1602,7 +1648,8 @@ function collectSdkBindings(program, functionName) {
1602
1648
  if (nodeType === "VariableDeclaration") {
1603
1649
  const varDecl = node;
1604
1650
  for (const decl of varDecl.declarations) {
1605
- const source = getImportSource(unwrapAwait(decl.init));
1651
+ const init = unwrapAwait(decl.init);
1652
+ const source = getImportSource(init);
1606
1653
  if (source && isTailorSdkSource(source)) {
1607
1654
  const id = decl.id;
1608
1655
  if (id.type === "Identifier") bindings.add(`__namespace__:${id.name}`);
@@ -1732,7 +1779,8 @@ function findAllWorkflows(program, _sourceText) {
1732
1779
  const args = node.arguments;
1733
1780
  const firstArg = args[0];
1734
1781
  if (args.length >= 1 && firstArg?.type === "ObjectExpression") {
1735
- const nameProp = findProperty(assertDefined(firstArg, "createWorkflow first argument missing").properties, "name");
1782
+ const configObj = assertDefined(firstArg, "createWorkflow first argument missing");
1783
+ const nameProp = findProperty(configObj.properties, "name");
1736
1784
  if (nameProp && isStringLiteral(nameProp.value)) {
1737
1785
  let exportName;
1738
1786
  let isDefaultExport = false;
@@ -2055,10 +2103,12 @@ function transformStartCalls(source, startContext, currentFilePath) {
2055
2103
  let transformedCall;
2056
2104
  if (call.kind === "workflow") {
2057
2105
  let optionsPart = "";
2058
- if (call.optionsText !== void 0) if (authNamespace) {
2059
- optionsPart = `, ${NORMALIZER_IDENTIFIER}(${call.optionsText})`;
2060
- needsNormalizerHelper = true;
2061
- } else optionsPart = `, ${call.optionsText}`;
2106
+ if (call.optionsText !== void 0) {
2107
+ if (authNamespace) {
2108
+ optionsPart = `, ${NORMALIZER_IDENTIFIER}(${call.optionsText})`;
2109
+ needsNormalizerHelper = true;
2110
+ } else optionsPart = `, ${call.optionsText}`;
2111
+ }
2062
2112
  transformedCall = `tailor.workflow.startWorkflow(${JSON.stringify(call.targetName)}, ${call.argsText || "undefined"}${optionsPart})`;
2063
2113
  } else {
2064
2114
  const optionsPart = call.optionsText !== void 0 ? `, ${call.optionsText}` : "";
@@ -2112,11 +2162,14 @@ function createBundleLog(options = {}) {
2112
2162
  }
2113
2163
  function unresolvedImportError(logs, tsconfig) {
2114
2164
  const imports = logs.map(formatUnresolvedImport);
2165
+ const message = imports.length === 1 ? `Could not resolve ${imports[0]}.` : `Could not resolve ${imports.length} imports.`;
2166
+ const details = imports.length === 1 ? void 0 : imports.map((item) => `- ${item}`).join("\n");
2167
+ const suggestion = unresolvedImportSuggestion(logs, tsconfig);
2115
2168
  return createCLIError({
2116
2169
  code: "UNRESOLVED_IMPORT",
2117
- message: imports.length === 1 ? `Could not resolve ${imports[0]}.` : `Could not resolve ${imports.length} imports.`,
2118
- details: imports.length === 1 ? void 0 : imports.map((item) => `- ${item}`).join("\n"),
2119
- suggestion: unresolvedImportSuggestion(logs, tsconfig)
2170
+ message,
2171
+ details,
2172
+ suggestion
2120
2173
  });
2121
2174
  }
2122
2175
  function unresolvedImportSuggestion(logs, tsconfig) {
@@ -2417,19 +2470,22 @@ async function bundleAuthHooks(options) {
2417
2470
  const absoluteConfigPath = path.resolve(configPath);
2418
2471
  const tsconfig = await resolveTSConfigWithFallback(baseDir);
2419
2472
  const functionName = `auth-hook--${authName}--before-login`;
2473
+ const serializedStartContext = serializeStartContext(startContext);
2474
+ const sortedEnvPrefix = JSON.stringify(Object.fromEntries(Object.entries(env).toSorted(([a], [b]) => a.localeCompare(b))));
2475
+ const contextHash = computeBundlerContextHash({
2476
+ sourceFile: absoluteConfigPath,
2477
+ extraContext: serializedStartContext,
2478
+ tsconfig,
2479
+ inlineSourcemap,
2480
+ bundleLogLevel,
2481
+ prefix: sortedEnvPrefix
2482
+ });
2420
2483
  const code = await withCache({
2421
2484
  cache,
2422
2485
  kind: "auth-hook",
2423
2486
  name: functionName,
2424
2487
  sourceFile: absoluteConfigPath,
2425
- contextHash: computeBundlerContextHash({
2426
- sourceFile: absoluteConfigPath,
2427
- extraContext: serializeStartContext(startContext),
2428
- tsconfig,
2429
- inlineSourcemap,
2430
- bundleLogLevel,
2431
- prefix: JSON.stringify(Object.fromEntries(Object.entries(env).toSorted(([a], [b]) => a.localeCompare(b))))
2432
- }),
2488
+ contextHash,
2433
2489
  async build(cachePlugins, trackDependency) {
2434
2490
  const entryContent = multiline`
2435
2491
  import _config from "${absoluteConfigPath}";
@@ -2579,6 +2635,7 @@ const tailorPrincipalMap = makePrincipalExpr({
2579
2635
  attributeList: "$raw.attributes ?? []"
2580
2636
  }
2581
2637
  });
2638
+ const PRINCIPAL_VAR = "_principal";
2582
2639
  /**
2583
2640
  * Parse `wrapped` and return the first property of the top-level parenthesized
2584
2641
  * object expression, or `undefined` if it does not parse as one.
@@ -2630,17 +2687,20 @@ function formatScriptContext(kind, context) {
2630
2687
  const convertToScriptExpr = (fn, kind, context) => {
2631
2688
  const precompiledExpr = getPrecompiledScriptExpr(fn);
2632
2689
  if (precompiledExpr) return precompiledExpr;
2633
- return assertParsableExpression(`(${stringifyFunction(fn)})(${kind === "validate" ? `{ value: _value }` : kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }`})`, formatScriptContext(kind, context));
2690
+ const normalized = stringifyFunction(fn);
2691
+ return assertParsableExpression(`(${normalized})(${kind === "validate" ? `{ value: _value }` : kind === "hooks.create" ? `{ input: _value, invoker: ${PRINCIPAL_VAR}, now: _now }` : `{ input: _value, oldValue: _oldValue, invoker: ${PRINCIPAL_VAR}, now: _now }`})`, formatScriptContext(kind, context));
2634
2692
  };
2635
2693
  const convertTypeHookToExpr = (fn) => {
2636
2694
  const precompiledExpr = getPrecompiledScriptExpr(fn);
2637
2695
  if (precompiledExpr) return precompiledExpr;
2638
- return assertParsableExpression(`(${stringifyFunction(fn)})({ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now })`, "type-hook");
2696
+ const normalized = stringifyFunction(fn);
2697
+ return assertParsableExpression(`(${normalized})({ input: _input, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR}, now: _now })`, "type-hook");
2639
2698
  };
2640
2699
  const convertTypeValidateToExpr = (fn) => {
2641
2700
  const precompiledExpr = getPrecompiledScriptExpr(fn);
2642
2701
  if (precompiledExpr) return precompiledExpr;
2643
- return assertParsableExpression(`(${stringifyFunction(fn)})({ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues)`, "type-validate");
2702
+ const normalized = stringifyFunction(fn);
2703
+ return assertParsableExpression(`(${normalized})({ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR} }, __issues)`, "type-validate");
2644
2704
  };
2645
2705
  /**
2646
2706
  * Parse TailorDBField into OperatorFieldConfig.
@@ -2653,8 +2713,8 @@ function parseFieldConfig(field, context) {
2653
2713
  const metadata = field.metadata;
2654
2714
  const fieldType = field.type;
2655
2715
  const rawRelation = field.rawRelation;
2656
- if (context && context.fieldPath.length > 1 && metadata.default !== void 0) throw new Error(`Field "${context.fieldPath.join(".")}" on type "${context.typeName}": .default() cannot be used on nested inner fields`);
2657
- if (context && context.fieldPath.length > 1 && metadata.hooks) throw new Error(`Field "${context.fieldPath.join(".")}" on type "${context.typeName}": .hooks() cannot be used on nested inner fields`);
2716
+ if (context && context.fieldPath.length > 1 && metadata.default !== void 0) throw new Error(`Field "${context.fieldPath.join(".")}" on table "${context.typeName}": .default() cannot be used on nested inner fields`);
2717
+ if (context && context.fieldPath.length > 1 && metadata.hooks) throw new Error(`Field "${context.fieldPath.join(".")}" on table "${context.typeName}": .hooks() cannot be used on nested inner fields`);
2658
2718
  const nestedFields = field.fields;
2659
2719
  return {
2660
2720
  type: fieldType,
@@ -2715,7 +2775,7 @@ function isSingleArrayConditionFormat(cond) {
2715
2775
  }
2716
2776
  /**
2717
2777
  * Normalize record-level permissions into a standard structure.
2718
- * @param permission - Tailor type permission
2778
+ * @param permission - Tailor table permission
2719
2779
  * @returns Normalized record permissions
2720
2780
  */
2721
2781
  function normalizePermission(permission) {
@@ -2817,8 +2877,8 @@ function findOmittedPermitRules(rawPermissions) {
2817
2877
  return locations;
2818
2878
  }
2819
2879
  /**
2820
- * Check whether GraphQL exposure is fully disabled for a type, given its
2821
- * effective gqlOperations (the type's own setting, falling back to the
2880
+ * Check whether GraphQL exposure is fully disabled for a table, given its
2881
+ * effective gqlOperations (the table's own setting, falling back to the
2822
2882
  * TailorDB namespace default). `undefined` means the default of all
2823
2883
  * operations enabled, so it is never considered fully disabled.
2824
2884
  * @param gqlOperations - Effective, normalized gqlOperations configuration
@@ -2829,15 +2889,15 @@ function isGqlOperationsFullyDisabled(gqlOperations) {
2829
2889
  return gqlOperations.create === false && gqlOperations.update === false && gqlOperations.delete === false && gqlOperations.read === false;
2830
2890
  }
2831
2891
  /**
2832
- * Find missing permission configuration for a TailorDB type.
2892
+ * Find missing permission configuration for a TailorDB table.
2833
2893
  *
2834
2894
  * Record-level permission is always required: TailorDB denies all record
2835
- * operations for a type without it, regardless of whether the type is
2895
+ * operations for a table without it, regardless of whether the table is
2836
2896
  * exposed via GraphQL. GraphQL permission is required whenever GraphQL
2837
- * exposure is enabled for the type (the default, unless every operation is
2897
+ * exposure is enabled for the table (the default, unless every operation is
2838
2898
  * explicitly disabled via `gqlOperations`).
2839
- * @param rawPermissions - Raw permissions definition for the type
2840
- * @param effectiveGqlOperations - The type's own gqlOperations, falling back to the namespace default
2899
+ * @param rawPermissions - Raw permissions definition for the table
2900
+ * @param effectiveGqlOperations - The table's own gqlOperations, falling back to the namespace default
2841
2901
  * @returns Which permission configuration, if any, is missing
2842
2902
  */
2843
2903
  function findMissingPermissionConfig(rawPermissions, effectiveGqlOperations) {
@@ -2850,12 +2910,12 @@ function findMissingPermissionConfig(rawPermissions, effectiveGqlOperations) {
2850
2910
  //#endregion
2851
2911
  //#region src/parser/service/tailordb/type-parser.ts
2852
2912
  /**
2853
- * Parse multiple TailorDB types, build relationships, and validate uniqueness.
2854
- * This is the main entry point for parsing TailorDB types.
2855
- * @param rawTypes - Raw TailorDB types keyed by name
2913
+ * Parse multiple TailorDB tables, build relationships, and validate uniqueness.
2914
+ * This is the main entry point for parsing TailorDB tables.
2915
+ * @param rawTypes - Raw TailorDB tables keyed by name
2856
2916
  * @param namespace - TailorDB namespace name
2857
- * @param typeSourceInfo - Optional type source information
2858
- * @returns Parsed types
2917
+ * @param typeSourceInfo - Optional table source information
2918
+ * @returns Parsed tables
2859
2919
  */
2860
2920
  function parseTypes(rawTypes, namespace, typeSourceInfo) {
2861
2921
  const types = createRecord();
@@ -2867,11 +2927,11 @@ function parseTypes(rawTypes, namespace, typeSourceInfo) {
2867
2927
  }
2868
2928
  /**
2869
2929
  * Parse a TailorDBTypeSchemaOutput into a TailorDBType.
2870
- * @param type - TailorDB type to parse
2871
- * @param allTypeNames - Set of all TailorDB type names
2872
- * @param rawTypes - All raw TailorDB types keyed by name
2873
- * @param typeSourceInfo - Optional type source information
2874
- * @returns Parsed TailorDB type
2930
+ * @param type - TailorDB table to parse
2931
+ * @param allTypeNames - Set of all TailorDB table names
2932
+ * @param rawTypes - All raw TailorDB tables keyed by name
2933
+ * @param typeSourceInfo - Optional table source information
2934
+ * @returns Parsed TailorDB table
2875
2935
  */
2876
2936
  function parseTailorDBType(type, allTypeNames, rawTypes, typeSourceInfo) {
2877
2937
  const metadata = type.metadata;
@@ -2896,12 +2956,12 @@ function parseTailorDBType(type, allTypeNames, rawTypes, typeSourceInfo) {
2896
2956
  "n-1",
2897
2957
  "manyToOne",
2898
2958
  "N-1"
2899
- ].includes(rawRelation.type) && fieldConfig.unique) throw new Error(`Field "${fieldName}" on type "${type.name}": cannot set unique on n-1 (manyToOne) relation. Use 1-1 (oneToOne) relation instead, or remove the unique constraint.`);
2959
+ ].includes(rawRelation.type) && fieldConfig.unique) throw new Error(`Field "${fieldName}" on table "${type.name}": cannot set unique on n-1 (manyToOne) relation. Use 1-1 (oneToOne) relation instead, or remove the unique constraint.`);
2900
2960
  const relationMetadata = processRelationMetadata(rawRelation, context, fieldConfig.array);
2901
2961
  fieldConfig = applyRelationMetadataToFieldConfig(fieldConfig, relationMetadata);
2902
2962
  }
2903
- if (fieldConfig.array && fieldConfig.index) throw new Error(`Field "${fieldName}" on type "${type.name}": index cannot be set on array fields`);
2904
- if (fieldConfig.array && fieldConfig.unique) throw new Error(`Field "${fieldName}" on type "${type.name}": unique cannot be set on array fields`);
2963
+ if (fieldConfig.array && fieldConfig.index) throw new Error(`Field "${fieldName}" on table "${type.name}": index cannot be set on array fields`);
2964
+ if (fieldConfig.array && fieldConfig.unique) throw new Error(`Field "${fieldName}" on table "${type.name}": unique cannot be set on array fields`);
2905
2965
  const parsedField = {
2906
2966
  name: fieldName,
2907
2967
  config: fieldConfig
@@ -2910,14 +2970,14 @@ function parseTailorDBType(type, allTypeNames, rawTypes, typeSourceInfo) {
2910
2970
  if (relationInfo) {
2911
2971
  parsedField.relation = { ...relationInfo };
2912
2972
  const forwardName = relationInfo.forwardName;
2913
- if (forwardName.length === 0) throw new Error(`Forward relation name for field "${fieldName}" on type "${type.name}"${typeLocation} cannot be empty. Use the "as" option in .relation({ toward: { as: ... } }) to specify a non-empty name.`);
2973
+ if (forwardName.length === 0) throw new Error(`Forward relation name for field "${fieldName}" on table "${type.name}"${typeLocation} cannot be empty. Use the "as" option in .relation({ toward: { as: ... } }) to specify a non-empty name.`);
2914
2974
  const existingForward = forwardRelationships[forwardName];
2915
- if (existingForward) throw new Error(`Forward relation name "${forwardName}" on type "${type.name}"${typeLocation} is duplicated between fields "${existingForward.targetField}" and "${fieldName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a unique name.`);
2975
+ if (existingForward) throw new Error(`Forward relation name "${forwardName}" on table "${type.name}"${typeLocation} is duplicated between fields "${existingForward.targetField}" and "${fieldName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a unique name.`);
2916
2976
  if (Object.hasOwn(type.fields, forwardName)) {
2917
- const message = forwardName === fieldName ? `Forward relation name "${forwardName}" on type "${type.name}"${typeLocation} is the same as its own relation field "${fieldName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a different name.` : `Forward relation name "${forwardName}" from field "${fieldName}" on type "${type.name}"${typeLocation} conflicts with existing field "${forwardName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a different name.`;
2977
+ const message = forwardName === fieldName ? `Forward relation name "${forwardName}" on table "${type.name}"${typeLocation} is the same as its own relation field "${fieldName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a different name.` : `Forward relation name "${forwardName}" from field "${fieldName}" on table "${type.name}"${typeLocation} conflicts with existing field "${forwardName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a different name.`;
2918
2978
  throw new Error(message);
2919
2979
  }
2920
- if (Object.hasOwn(metadata.files, forwardName)) throw new Error(`Forward relation name "${forwardName}" from field "${fieldName}" on type "${type.name}"${typeLocation} conflicts with files field "${forwardName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a different name.`);
2980
+ if (Object.hasOwn(metadata.files, forwardName)) throw new Error(`Forward relation name "${forwardName}" from field "${fieldName}" on table "${type.name}"${typeLocation} conflicts with files field "${forwardName}". Use the "as" option in .relation({ toward: { as: ... } }) to specify a different name.`);
2921
2981
  const targetType = rawTypes[relationInfo.targetType];
2922
2982
  forwardRelationships[forwardName] = {
2923
2983
  name: forwardName,
@@ -2949,11 +3009,11 @@ function parseTailorDBType(type, allTypeNames, rawTypes, typeSourceInfo) {
2949
3009
  };
2950
3010
  }
2951
3011
  /**
2952
- * Build backward relationships between parsed types.
2953
- * Also validates that backward relation names are unique within each type.
2954
- * @param types - Parsed types
3012
+ * Build backward relationships between parsed tables.
3013
+ * Also validates that backward relation names are unique within each table.
3014
+ * @param types - Parsed tables
2955
3015
  * @param namespace - TailorDB namespace name
2956
- * @param typeSourceInfo - Optional type source information
3016
+ * @param typeSourceInfo - Optional table source information
2957
3017
  */
2958
3018
  function buildBackwardRelationships(types, namespace, typeSourceInfo) {
2959
3019
  const backwardNameSources = Object.create(null);
@@ -2993,25 +3053,25 @@ function buildBackwardRelationships(types, namespace, typeSourceInfo) {
2993
3053
  const location = formatTypeSourceLocation(getTypeSourceInfo(typeSourceInfo, s.sourceType));
2994
3054
  return `${s.sourceType}.${s.fieldName}${location}`;
2995
3055
  }).join(", ");
2996
- errors.push(`Backward relation name "${backwardName}" on type "${targetTypeName}" is duplicated from: ${sourceList}. Use the "backward" option in .relation() to specify unique names.`);
3056
+ errors.push(`Backward relation name "${backwardName}" on table "${targetTypeName}" is duplicated from: ${sourceList}. Use the "backward" option in .relation() to specify unique names.`);
2997
3057
  }
2998
3058
  if (Object.hasOwn(targetType.fields, backwardName)) {
2999
3059
  const source = sources[0];
3000
3060
  if (source === void 0) throw new Error(`no source found for backward name: ${backwardName}`);
3001
3061
  const sourceLocation = formatTypeSourceLocation(getTypeSourceInfo(typeSourceInfo, source.sourceType));
3002
- errors.push(`Backward relation name "${backwardName}" from ${source.sourceType}.${source.fieldName}${sourceLocation} conflicts with existing field "${backwardName}" on type "${targetTypeName}"${targetLocation}. Use the "backward" option in .relation() to specify a different name.`);
3062
+ errors.push(`Backward relation name "${backwardName}" from ${source.sourceType}.${source.fieldName}${sourceLocation} conflicts with existing field "${backwardName}" on table "${targetTypeName}"${targetLocation}. Use the "backward" option in .relation() to specify a different name.`);
3003
3063
  }
3004
3064
  if (targetType.files && Object.hasOwn(targetType.files, backwardName)) {
3005
3065
  const source = sources[0];
3006
3066
  if (source === void 0) throw new Error(`no source found for backward name: ${backwardName}`);
3007
3067
  const sourceLocation = formatTypeSourceLocation(getTypeSourceInfo(typeSourceInfo, source.sourceType));
3008
- errors.push(`Backward relation name "${backwardName}" from ${source.sourceType}.${source.fieldName}${sourceLocation} conflicts with files field "${backwardName}" on type "${targetTypeName}"${targetLocation}. Use the "backward" option in .relation() to specify a different name.`);
3068
+ errors.push(`Backward relation name "${backwardName}" from ${source.sourceType}.${source.fieldName}${sourceLocation} conflicts with files field "${backwardName}" on table "${targetTypeName}"${targetLocation}. Use the "backward" option in .relation() to specify a different name.`);
3009
3069
  }
3010
3070
  if (Object.hasOwn(targetType.forwardRelationships, backwardName)) {
3011
3071
  const source = sources[0];
3012
3072
  if (source === void 0) throw new Error(`no source found for backward name: ${backwardName}`);
3013
3073
  const sourceLocation = formatTypeSourceLocation(getTypeSourceInfo(typeSourceInfo, source.sourceType));
3014
- errors.push(`Relation name "${backwardName}" on type "${targetTypeName}"${targetLocation} is used by both a forward relationship and a backward relationship from ${source.sourceType}.${source.fieldName}${sourceLocation}. Use the "as" option in .relation({ toward: { as: ... } }) or the "backward" option in .relation() to specify unique names.`);
3074
+ errors.push(`Relation name "${backwardName}" on table "${targetTypeName}"${targetLocation} is used by both a forward relationship and a backward relationship from ${source.sourceType}.${source.fieldName}${sourceLocation}. Use the "as" option in .relation({ toward: { as: ... } }) or the "backward" option in .relation() to specify unique names.`);
3015
3075
  }
3016
3076
  }
3017
3077
  }
@@ -3020,11 +3080,11 @@ function buildBackwardRelationships(types, namespace, typeSourceInfo) {
3020
3080
  /**
3021
3081
  * Validate GraphQL query field name uniqueness.
3022
3082
  * Checks for:
3023
- * 1. Each type's singular query name != plural query name
3024
- * 2. No duplicate query names across all types
3025
- * @param types - Parsed types
3083
+ * 1. Each table's singular query name != plural query name
3084
+ * 2. No duplicate query names across all tables
3085
+ * @param types - Parsed tables
3026
3086
  * @param namespace - TailorDB namespace name
3027
- * @param typeSourceInfo - Optional type source information
3087
+ * @param typeSourceInfo - Optional table source information
3028
3088
  */
3029
3089
  function validatePluralFormUniqueness(types, namespace, typeSourceInfo) {
3030
3090
  const errors = [];
@@ -3032,7 +3092,7 @@ function validatePluralFormUniqueness(types, namespace, typeSourceInfo) {
3032
3092
  const singularQuery = inflection.camelize(parsedType.name, true);
3033
3093
  if (singularQuery === inflection.camelize(parsedType.pluralForm, true)) {
3034
3094
  const location = formatTypeSourceLocation(getTypeSourceInfo(typeSourceInfo, parsedType.name));
3035
- errors.push(`Type "${parsedType.name}"${location} has identical singular and plural query names "${singularQuery}". Use db.table(["${parsedType.name}", "UniquePluralForm"], {...}) to set a unique pluralForm.`);
3095
+ errors.push(`Table "${parsedType.name}"${location} has identical singular and plural query names "${singularQuery}". Use db.table(["${parsedType.name}", "UniquePluralForm"], {...}) to set a unique pluralForm.`);
3036
3096
  }
3037
3097
  }
3038
3098
  const queryNameToSource = /* @__PURE__ */ new Map();
@@ -3106,12 +3166,12 @@ function collectBindingsFromPattern(pattern, bindings) {
3106
3166
  else collectBindingsFromPattern(prop.value, bindings);
3107
3167
  break;
3108
3168
  case "ArrayPattern":
3109
- for (const elem of pattern.elements) if (elem) if (elem.type === "RestElement") collectBindingsFromPattern(elem.argument, bindings);
3110
- else collectBindingsFromPattern(elem, bindings);
3111
- break;
3112
- case "AssignmentPattern":
3113
- collectBindingsFromPattern(pattern.left, bindings);
3169
+ for (const elem of pattern.elements) if (elem) {
3170
+ if (elem.type === "RestElement") collectBindingsFromPattern(elem.argument, bindings);
3171
+ else collectBindingsFromPattern(elem, bindings);
3172
+ }
3114
3173
  break;
3174
+ case "AssignmentPattern": collectBindingsFromPattern(pattern.left, bindings);
3115
3175
  }
3116
3176
  }
3117
3177
  /** Fields that contain TypeScript type annotations (not runtime references). */
@@ -3386,7 +3446,7 @@ async function bundleScriptTarget(args) {
3386
3446
  const context = `${kind} in ${sourceFilePath}`;
3387
3447
  const fnSource = stringifyFunction(fn);
3388
3448
  if ((kind === "typeValidate" || kind === "validate") && fn.constructor.name === "AsyncFunction") throw new Error(`${context} must be synchronous — the generated validator runs synchronously, so issues reported after an await are silently lost. Remove the async keyword.`);
3389
- const argsObject = kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "hooks.update" ? `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "validate" ? `{ value: _value }` : kind === "typeHook" ? `{ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now }` : `{ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues`;
3449
+ const argsObject = kind === "hooks.create" ? `{ input: _value, invoker: ${PRINCIPAL_VAR}, now: _now }` : kind === "hooks.update" ? `{ input: _value, oldValue: _oldValue, invoker: ${PRINCIPAL_VAR}, now: _now }` : kind === "validate" ? `{ value: _value }` : kind === "typeHook" ? `{ input: _input, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR}, now: _now }` : `{ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${PRINCIPAL_VAR} }, __issues`;
3390
3450
  const inlineExpr = assertParsableExpression(`(${fnSource})(${argsObject})`, context);
3391
3451
  const freeVars = findUndefinedReferences(`const __fn = ${fnSource};`);
3392
3452
  if (freeVars.size === 0) return inlineExpr;
@@ -3426,8 +3486,8 @@ async function bundleScriptTarget(args) {
3426
3486
  * Precompile TailorDB hooks/validators into self-contained script expressions using rolldown.
3427
3487
  * Uses oxc-parser AST walking to extract free variables from functions, then builds
3428
3488
  * minimal entry points containing only the needed imports and declarations.
3429
- * @param type - TailorDB type schema output.
3430
- * @param sourceFilePath - Source file where the type is defined.
3489
+ * @param type - TailorDB table schema output.
3490
+ * @param sourceFilePath - Source file where the table is defined.
3431
3491
  * @param tsconfig - Resolved tsconfig path, or undefined if not found.
3432
3492
  */
3433
3493
  async function precompileTailorDBTypeScripts(type, sourceFilePath, tsconfig) {
@@ -3451,8 +3511,8 @@ async function precompileTailorDBTypeScripts(type, sourceFilePath, tsconfig) {
3451
3511
  //#endregion
3452
3512
  //#region src/cli/services/tailordb/type-name-validation.ts
3453
3513
  /**
3454
- * Format a TailorDB type source for validation errors.
3455
- * @param sourceInfo - Source information captured when loading the type
3514
+ * Format a TailorDB table source for validation errors.
3515
+ * @param sourceInfo - Source information captured when loading the table
3456
3516
  * @returns Human-readable source detail
3457
3517
  */
3458
3518
  function formatTailorDBTypeSourceInfo(sourceInfo) {
@@ -3467,9 +3527,9 @@ function formatTailorDBTypeSourceInfo(sourceInfo) {
3467
3527
  return `${sourceInfo.filePath} export ${sourceInfo.exportName}`;
3468
3528
  }
3469
3529
  /**
3470
- * Collect TailorDB type-name sources from loaded local services.
3530
+ * Collect TailorDB table-name sources from loaded local services.
3471
3531
  * @param args - Collection inputs
3472
- * @returns Type-name sources for local services
3532
+ * @returns Table-name sources for local services
3473
3533
  */
3474
3534
  function collectLocalTailorDBTypeNameSources(args) {
3475
3535
  const sources = [];
@@ -3482,9 +3542,9 @@ function collectLocalTailorDBTypeNameSources(args) {
3482
3542
  return sources;
3483
3543
  }
3484
3544
  /**
3485
- * Fetch TailorDB type-name sources for external namespaces.
3545
+ * Fetch TailorDB table-name sources for external namespaces.
3486
3546
  * @param args - Fetch inputs
3487
- * @returns Type-name sources for external services
3547
+ * @returns Table-name sources for external services
3488
3548
  */
3489
3549
  async function fetchExternalTailorDBTypeNameSources(args) {
3490
3550
  return (await Promise.all(args.externalTailorDBNamespaces.map(async (namespace) => {
@@ -3512,7 +3572,7 @@ async function fetchExternalTailorDBTypeNameSources(args) {
3512
3572
  }))).flat();
3513
3573
  }
3514
3574
  /**
3515
- * Assert that TailorDB type names are unique across all supplied sources.
3575
+ * Assert that TailorDB table names are unique across all supplied sources.
3516
3576
  * @param args - Validation inputs
3517
3577
  */
3518
3578
  function assertUniqueTailorDBTypeNames(args) {
@@ -3526,20 +3586,20 @@ function assertUniqueTailorDBTypeNames(args) {
3526
3586
  for (const [typeName, sources] of sourcesByTypeName) {
3527
3587
  if (sources.length <= 1) continue;
3528
3588
  const sourceList = sources.map(formatTailorDBTypeNameSource).join(", ");
3529
- errors.push(`Type "${typeName}" is defined more than once: ${sourceList}`);
3589
+ errors.push(`Table "${typeName}" is defined more than once: ${sourceList}`);
3530
3590
  }
3531
- if (errors.length > 0) throw new Error(`Duplicate TailorDB type names detected.
3532
- ${errors.map((error) => ` - ${error}`).join("\n")}\nTailorDB type names must be unique across all TailorDB namespaces in an application.`);
3591
+ if (errors.length > 0) throw new Error(`Duplicate TailorDB table names detected.
3592
+ ${errors.map((error) => ` - ${error}`).join("\n")}\nTailorDB table names must be unique across all TailorDB namespaces in an application.`);
3533
3593
  }
3534
3594
  /**
3535
- * Assert local TailorDB type names are unique.
3595
+ * Assert local TailorDB table names are unique.
3536
3596
  * @param args - Validation inputs
3537
3597
  */
3538
3598
  function assertUniqueLocalTailorDBTypeNames(args) {
3539
3599
  assertUniqueTailorDBTypeNames({ sources: collectLocalTailorDBTypeNameSources(args) });
3540
3600
  }
3541
3601
  /**
3542
- * Assert TailorDB type names are unique across local and external namespaces.
3602
+ * Assert TailorDB table names are unique across local and external namespaces.
3543
3603
  * @param args - Validation inputs
3544
3604
  */
3545
3605
  async function assertUniqueTailorDBTypeNamesWithExternal(args) {
@@ -3587,7 +3647,7 @@ function createTailorDBService(params) {
3587
3647
  if (existingSourceInfo) {
3588
3648
  const firstSource = formatTailorDBTypeSourceInfo(existingSourceInfo) ?? "unknown source";
3589
3649
  const secondSource = formatTailorDBTypeSourceInfo(sourceInfo) ?? "unknown source";
3590
- throw new Error(`Duplicate TailorDB type name "${typeName}" detected in TailorDB service "${namespace}". First: ${firstSource}. Second: ${secondSource}. TailorDB type names must be unique across all TailorDB files in a service.`);
3650
+ throw new Error(`Duplicate TailorDB table name "${typeName}" detected in TailorDB service "${namespace}". First: ${firstSource}. Second: ${secondSource}. TailorDB table names must be unique across all TailorDB files in a service.`);
3591
3651
  }
3592
3652
  assertDefined(rawTypes[rawTypesKey], `raw types entry missing for key: ${rawTypesKey}`)[typeName] = type;
3593
3653
  typeSourceInfo[typeName] = sourceInfo;
@@ -3600,7 +3660,7 @@ function createTailorDBService(params) {
3600
3660
  const warnOmittedPermit = () => {
3601
3661
  for (const fileTypes of Object.values(rawTypes)) for (const [typeName, type] of Object.entries(fileTypes)) {
3602
3662
  const locations = findOmittedPermitRules(type.metadata.permissions);
3603
- 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.`);
3663
+ if (locations.length > 0) logger.warn(`TailorDB table "${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.`);
3604
3664
  }
3605
3665
  };
3606
3666
  const validateRequiredPermissions = () => {
@@ -3611,16 +3671,16 @@ function createTailorDBService(params) {
3611
3671
  if (!missingPermission && !missingGqlPermission) continue;
3612
3672
  const source = formatTailorDBTypeSourceInfo(typeSourceInfo[typeName]);
3613
3673
  const location = source ? ` (${source})` : "";
3614
- 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.`);
3615
- 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 } }).`);
3674
+ if (missingPermission) errors.push(`TailorDB table "${typeName}"${location} has no .permission() configured. TailorDB denies all record operations for tables without permission; call .permission(...) to grant access explicitly.`);
3675
+ if (missingGqlPermission) errors.push(`TailorDB table "${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 } }).`);
3616
3676
  }
3617
3677
  if (errors.length > 0) throw new Error(`TailorDB permission configuration errors in service "${namespace}":\n${errors.map((e) => ` - ${e}`).join("\n")}`);
3618
3678
  };
3619
3679
  /**
3620
- * Process plugins for a type and add generated types to rawTypes
3621
- * @param rawType - The raw TailorDB type being processed
3622
- * @param attachments - Plugin attachments for this type
3623
- * @param sourceFilePath - The file path where the type was loaded from
3680
+ * Process plugins for a table and add generated tables to rawTypes
3681
+ * @param rawType - The raw TailorDB table being processed
3682
+ * @param attachments - Plugin attachments for this table
3683
+ * @param sourceFilePath - The file path where the table was loaded from
3624
3684
  */
3625
3685
  const processPluginsForType = async (rawType, attachments, sourceFilePath) => {
3626
3686
  if (!pluginManager) return;
@@ -3650,7 +3710,7 @@ function createTailorDBService(params) {
3650
3710
  rawTypes[typeFile] = createRawTypesByName();
3651
3711
  const loadedTypes = createRawTypesByName();
3652
3712
  try {
3653
- const module = await import(pathToFileURL(typeFile).href);
3713
+ const module = await importUserModule(typeFile);
3654
3714
  for (const exportName of Object.keys(module)) {
3655
3715
  const exportedValue = module[exportName];
3656
3716
  const result = TailorDBTypeSchema.safeParse(stripTailorDBTypeBuilderHelpers(exportedValue));
@@ -3666,15 +3726,16 @@ function createTailorDBService(params) {
3666
3726
  filePath: typeFile,
3667
3727
  exportName
3668
3728
  });
3669
- if (exportedValue.plugins && Array.isArray(exportedValue.plugins) && exportedValue.plugins.length > 0) {
3670
- pluginAttachments.set(exportedValue.name, [...exportedValue.plugins]);
3671
- logger.log(` Plugin attachments: ${styles.info(exportedValue.plugins.map((p) => p.pluginId).join(", "))}`);
3672
- await processPluginsForType(exportedValue, exportedValue.plugins, typeFile);
3729
+ const rawType = exportedValue;
3730
+ if (rawType.plugins && Array.isArray(rawType.plugins) && rawType.plugins.length > 0) {
3731
+ pluginAttachments.set(rawType.name, [...rawType.plugins]);
3732
+ logger.log(` Plugin attachments: ${styles.info(rawType.plugins.map((p) => p.pluginId).join(", "))}`);
3733
+ await processPluginsForType(rawType, rawType.plugins, typeFile);
3673
3734
  }
3674
3735
  }
3675
3736
  } catch (error) {
3676
3737
  const relativePath = path.relative(process.cwd(), typeFile);
3677
- logger.error(`Failed to load type from ${styles.bold(relativePath)}`);
3738
+ logger.error(`Failed to load table from ${styles.bold(relativePath)}`);
3678
3739
  logger.error(String(error));
3679
3740
  throw error;
3680
3741
  }
@@ -3698,7 +3759,7 @@ function createTailorDBService(params) {
3698
3759
  const typeFiles = [...new Set(loadFilesWithIgnores(config, baseDir))];
3699
3760
  const tsconfig = await resolveTSConfigWithFallback(baseDir);
3700
3761
  logger.newline();
3701
- logger.log(`Found ${styles.highlight(typeFiles.length.toString())} type files for TailorDB service ${styles.highlight(`"${namespace}"`)}`);
3762
+ logger.log(`Found ${styles.highlight(typeFiles.length.toString())} table files for TailorDB service ${styles.highlight(`"${namespace}"`)}`);
3702
3763
  if (pluginManager) for (const typeFile of typeFiles) await loadTypeFile(typeFile, tsconfig);
3703
3764
  else await Promise.all(typeFiles.map((typeFile) => loadTypeFile(typeFile, tsconfig)));
3704
3765
  doParseTypes();
@@ -4033,7 +4094,7 @@ function buildResolverPermissionGuardExpr(permission) {
4033
4094
  }
4034
4095
  /**
4035
4096
  * Build the permission guard and input-validation statements shared by every
4036
- * resolver entry wrapper (production bundling and `function test-run`).
4097
+ * resolver entry wrapper (production bundling and `function run`).
4037
4098
  *
4038
4099
  * Kept as a single generator so a resolver-wrapping behavior (like the
4039
4100
  * permission guard) can't be added to one entry-point template and forgotten
@@ -4207,8 +4268,8 @@ const HttpAdapterConfigSchema = z.strictObject({
4207
4268
 
4208
4269
  //#endregion
4209
4270
  //#region src/cli/services/http-adapter/bundler.ts
4210
- const ADAPTER_BUNDLE_WARN_BYTES = 64 * 1024;
4211
- const ADAPTER_BUNDLE_ERROR_BYTES = 256 * 1024;
4271
+ const ADAPTER_BUNDLE_WARN_BYTES = 65536;
4272
+ const ADAPTER_BUNDLE_ERROR_BYTES = 262144;
4212
4273
  const GRAPHQL_WEB_MODULE = createRequire(import.meta.url).resolve("@0no-co/graphql.web");
4213
4274
  /**
4214
4275
  * Bundle each adapter's `input` (and `output`, if present) into a standalone
@@ -4229,12 +4290,13 @@ async function bundleHttpAdapters(adapters, baseDir, cache, bundleLogLevel = "DE
4229
4290
  logger.newline();
4230
4291
  logger.log(`Bundling ${styles.highlight(adapters.length.toString())} files for ${styles.info("\"http-adapter\"")}`);
4231
4292
  const tsconfig = await resolveTSConfigWithFallback(baseDir);
4232
- const results = await withBundleConcurrency(adapters.flatMap((adapter) => {
4293
+ const tasks = adapters.flatMap((adapter) => {
4233
4294
  return (adapter.hasOutput ? ["input", "output"] : ["input"]).map((kind) => ({
4234
4295
  adapter,
4235
4296
  kind
4236
4297
  }));
4237
- }), ({ adapter, kind }) => bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel, tsconfigCache));
4298
+ });
4299
+ const results = await withBundleConcurrency(tasks, ({ adapter, kind }) => bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel, tsconfigCache));
4238
4300
  const bundledInputs = /* @__PURE__ */ new Map();
4239
4301
  const bundledOutputs = /* @__PURE__ */ new Map();
4240
4302
  for (const [name, kind, code] of results) if (kind === "input") bundledInputs.set(name, code);
@@ -4437,7 +4499,7 @@ async function loadAdapterFiles(config, baseDir) {
4437
4499
  }
4438
4500
  async function loadAdapterFromFile(filePath) {
4439
4501
  try {
4440
- const module = await import(pathToFileURL(filePath).href);
4502
+ const module = await importUserModule(filePath);
4441
4503
  if (!isSdkBranded(module.default, "http-adapter")) {
4442
4504
  const named = Object.entries(module).find(([exportName, value]) => exportName !== "default" && isSdkBranded(value, "http-adapter"));
4443
4505
  if (named) throw new Error(`HTTP adapter must be the default export, but it is exported as \`${named[0]}\`. Re-export it: \`export default createHttpAdapter({...})\`.`);
@@ -4533,7 +4595,7 @@ const ResolverSchema = z.strictObject({
4533
4595
  * @returns Parsed resolver or null if invalid
4534
4596
  */
4535
4597
  async function loadResolver(resolverFilePath) {
4536
- const resolver = (await import(pathToFileURL(resolverFilePath).href)).default;
4598
+ const resolver = (await importUserModule(resolverFilePath)).default;
4537
4599
  const parseResult = ResolverSchema.safeParse(resolver);
4538
4600
  if (!parseResult.success) return null;
4539
4601
  return parseResult.data;
@@ -4609,16 +4671,17 @@ async function bundleSingleResolver(options) {
4609
4671
  contextHash,
4610
4672
  async build(cachePlugins, trackDependency) {
4611
4673
  const absoluteSourcePath = path.resolve(resolver.sourceFile);
4674
+ const guardAndInputCheckExpr = buildResolverPermissionAndInputCheckExpr({
4675
+ permission: resolver.permission,
4676
+ defaultPermission
4677
+ });
4612
4678
  const entryContent = multiline`
4613
4679
  import _internalResolver from "${absoluteSourcePath}";
4614
4680
  import { t } from "@tailor-platform/sdk";
4615
4681
 
4616
4682
  const $tailor_resolver_body = async (context) => {
4617
4683
  const invoker = ${INVOKER_EXPR};
4618
- ${buildResolverPermissionAndInputCheckExpr({
4619
- permission: resolver.permission,
4620
- defaultPermission
4621
- })}
4684
+ ${guardAndInputCheckExpr}
4622
4685
  return _internalResolver.body({ ...context, invoker });
4623
4686
  };
4624
4687
 
@@ -4673,7 +4736,7 @@ function parseResolverDefaultPermission(params) {
4673
4736
  /**
4674
4737
  * Find the `defaultPermission` of the resolver namespace a file belongs to.
4675
4738
  *
4676
- * `function test-run` receives a single file rather than a namespace, so the
4739
+ * `function run` receives a single file rather than a namespace, so the
4677
4740
  * namespace is recovered by matching the file against each namespace's own
4678
4741
  * patterns — otherwise a test run would skip a guard the deployed resolver has.
4679
4742
  *
@@ -4718,7 +4781,7 @@ function createResolverService(namespace, config, baseDir) {
4718
4781
  });
4719
4782
  const loadResolverForFile = async (resolverFile) => {
4720
4783
  try {
4721
- const resolverModule = await import(pathToFileURL(resolverFile).href);
4784
+ const resolverModule = await importUserModule(resolverFile);
4722
4785
  const result = ResolverSchema.safeParse(resolverModule.default);
4723
4786
  if (result.success) {
4724
4787
  const relativePath = path.relative(process.cwd(), resolverFile);
@@ -5260,7 +5323,7 @@ async function loadFileContent(filePath) {
5260
5323
  const jobs = [];
5261
5324
  let workflow = null;
5262
5325
  try {
5263
- const module = await import(pathToFileURL(filePath).href);
5326
+ const module = await importUserModule(filePath);
5264
5327
  for (const [exportName, exportValue] of Object.entries(module)) {
5265
5328
  if (exportName === "default") {
5266
5329
  const workflowResult = WorkflowSchema.safeParse(stripRuntimeStart(exportValue));
@@ -5299,6 +5362,16 @@ async function loadFileContent(filePath) {
5299
5362
  function getApplicationAuthNamespace(application) {
5300
5363
  return application.authService?.config.name ?? application.config?.auth?.name;
5301
5364
  }
5365
+ /**
5366
+ * Resolve the auth namespace configured for an application, throwing when none is configured.
5367
+ * @param application - Loaded application with local or external Auth config
5368
+ * @returns Auth namespace
5369
+ */
5370
+ function requireApplicationAuthNamespace(application) {
5371
+ const authNamespace = getApplicationAuthNamespace(application);
5372
+ if (!authNamespace) throw new Error("No Auth service configured");
5373
+ return authNamespace;
5374
+ }
5302
5375
 
5303
5376
  //#endregion
5304
5377
  //#region src/cli/shared/inline-sourcemap.ts
@@ -5331,7 +5404,7 @@ const NAME_PATTERN = /^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$/;
5331
5404
  const AUTH_NAMESPACE_PATTERN = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
5332
5405
  const AIGatewaySchema = z.strictObject({
5333
5406
  name: z.string().regex(NAME_PATTERN, "Must be 3-30 lowercase alphanumeric characters or hyphens").describe("AI Gateway name"),
5334
- authNamespace: z.string().regex(AUTH_NAMESPACE_PATTERN, "Must be 3-63 lowercase alphanumeric characters or hyphens").describe("Auth namespace used to resolve request tokens against the workspace's auth"),
5407
+ authNamespace: z.string().regex(AUTH_NAMESPACE_PATTERN, "Must be 3-63 lowercase alphanumeric characters or hyphens").optional().describe("Auth namespace used to resolve request tokens against the workspace's auth. Defaults to the application's own auth service when omitted; omitting it without an Auth service configured is rejected."),
5335
5408
  cors: z.array(z.string()).optional().describe("Allowed CORS origins for browser-based clients. Each entry is `*`, `http(s)://*`, `http(s)://*.example.com`, or `http(s)://app.example.com`, optionally with `:port`. Empty list disables cross-origin access.")
5336
5409
  }).brand("AIGatewayConfig");
5337
5410
 
@@ -5530,6 +5603,65 @@ const StaticWebsiteSchema = z.strictObject({
5530
5603
  customDomains: z.array(z.string()).optional().describe("Custom domains for the static website")
5531
5604
  }).brand("StaticWebsiteConfig");
5532
5605
 
5606
+ //#endregion
5607
+ //#region src/parser/service/workflow/wait-point-key.ts
5608
+ const KEY_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
5609
+ const LITERAL_SEGMENT_REGEX = /^[a-z0-9]+$/;
5610
+ const PARAM_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
5611
+ const MAX_KEY_LENGTH = 63;
5612
+ const KEY_GRAMMAR = "[a-z0-9-] (3-63 characters; must start and end with [a-z0-9])";
5613
+ /**
5614
+ * Check one declared wait point key against the key rules.
5615
+ *
5616
+ * The declaration matters: only `createWaitPoints`' `define` receives the key
5617
+ * before the type arguments, so only there can TypeScript read the `$params`
5618
+ * off the key literal.
5619
+ * @param waitPoint - A key as declared, with the declaration it came from
5620
+ * @returns The rule the key breaks, or undefined when the key is usable
5621
+ */
5622
+ function checkWaitPointKey(waitPoint) {
5623
+ const { key, declaredBy } = waitPoint;
5624
+ const segments = key.split("-");
5625
+ const paramSegments = segments.filter((segment) => segment.startsWith("$") && segment.length > 1);
5626
+ if (paramSegments.length > 0 && declaredBy !== "define") return declaredBy === "property" ? `Invalid wait point key "${key}": $params cannot come from a property name. Pass the key to define instead, e.g. define.for("${key}")<Payload, Result>().` : `Invalid wait point key "${key}": createWaitPoint takes its type arguments first, which stops TypeScript inferring the key as a literal, so it cannot type the $params. Declare it through createWaitPoints instead: createWaitPoints((define) => ({ myWaitPoint: define.for("${key}")<Payload, Result>() })).`;
5627
+ const seen = /* @__PURE__ */ new Set();
5628
+ let literals = 0;
5629
+ for (const segment of segments) {
5630
+ if (segment.startsWith("$")) {
5631
+ const name = segment.slice(1);
5632
+ if (!PARAM_NAME_REGEX.test(name)) return `Invalid wait point key "${key}": "${segment}" is not a usable parameter name. Use letters, digits and underscores, starting with a letter or underscore.`;
5633
+ if (seen.has(name)) return `Invalid wait point key "${key}": parameter "$${name}" appears more than once.`;
5634
+ seen.add(name);
5635
+ continue;
5636
+ }
5637
+ if (segment === "") continue;
5638
+ if (!LITERAL_SEGMENT_REGEX.test(segment)) return `Invalid wait point key "${key}": segment "${segment}" may only contain [a-z0-9]. Wait point keys accept ${KEY_GRAMMAR}, with $params standing in for runtime values.`;
5639
+ literals += 1;
5640
+ }
5641
+ if (literals === 0 && paramSegments.length > 0) return key.startsWith("-") || key.endsWith("-") ? `Invalid wait point key "${key}": must match ${KEY_GRAMMAR}.` : `Invalid wait point key "${key}": it needs at least one literal segment alongside its $params, otherwise the key carries no identity of its own and can collide with an unrelated wait point.`;
5642
+ if (paramSegments.length === 0) return KEY_REGEX.test(key) ? void 0 : `Invalid wait point key "${key}": must match ${KEY_GRAMMAR}.`;
5643
+ const shortest = segments.map((segment) => segment.startsWith("$") ? "0" : segment).join("-");
5644
+ if (shortest.length > MAX_KEY_LENGTH) return `Wait point key "${key}" cannot fit in ${MAX_KEY_LENGTH} characters: even single-character parameter values produce ${shortest.length}.`;
5645
+ return KEY_REGEX.test(shortest) ? void 0 : `Invalid wait point key "${key}": must match ${KEY_GRAMMAR}.`;
5646
+ }
5647
+ /**
5648
+ * Check every declared wait point key, reporting each broken rule once.
5649
+ * @param waitPoints - The keys declared across the project, as registered
5650
+ * @returns One message per distinct declaration that breaks a rule
5651
+ */
5652
+ function collectWaitPointKeyFailures(waitPoints) {
5653
+ const failures = [];
5654
+ const seen = /* @__PURE__ */ new Set();
5655
+ for (const waitPoint of waitPoints) {
5656
+ const identity = `${waitPoint.declaredBy} ${waitPoint.key}`;
5657
+ if (seen.has(identity)) continue;
5658
+ seen.add(identity);
5659
+ const failure = checkWaitPointKey(waitPoint);
5660
+ if (failure) failures.push(failure);
5661
+ }
5662
+ return failures;
5663
+ }
5664
+
5533
5665
  //#endregion
5534
5666
  //#region src/cli/services/application.ts
5535
5667
  function defineTailorDB(config, baseDir, pluginManager) {
@@ -5544,9 +5676,10 @@ function defineTailorDB(config, baseDir, pluginManager) {
5544
5676
  for (const [namespace, serviceConfig] of Object.entries(config)) {
5545
5677
  if ("external" in serviceConfig) externalTailorDBNamespaces.push(namespace);
5546
5678
  else {
5679
+ const parsedConfig = TailorDBServiceConfigSchema.parse(serviceConfig);
5547
5680
  const tailorDB = createTailorDBService({
5548
5681
  namespace,
5549
- config: TailorDBServiceConfigSchema.parse(serviceConfig),
5682
+ config: parsedConfig,
5550
5683
  pluginManager,
5551
5684
  baseDir
5552
5685
  });
@@ -5672,13 +5805,17 @@ function defineStaticWebsites(websites) {
5672
5805
  });
5673
5806
  return staticWebsiteServices;
5674
5807
  }
5675
- function defineAIGateways(gateways) {
5808
+ function defineAIGateways(gateways, applicationAuthNamespace) {
5676
5809
  const aiGatewayServices = [];
5677
5810
  const gatewayNames = /* @__PURE__ */ new Set();
5678
5811
  (gateways ?? []).forEach((config) => {
5679
5812
  const gateway = AIGatewaySchema.parse(config);
5680
5813
  if (gatewayNames.has(gateway.name)) throw new Error(`AI Gateway with name "${gateway.name}" already defined.`);
5681
5814
  gatewayNames.add(gateway.name);
5815
+ if (gateway.authNamespace === void 0) {
5816
+ if (!applicationAuthNamespace) throw new Error(`AI Gateway "${gateway.name}" has no "authNamespace" and no Auth service is configured to default to. Define an Auth service, or set "authNamespace" explicitly.`);
5817
+ gateway.authNamespace = applicationAuthNamespace;
5818
+ }
5682
5819
  aiGatewayServices.push(gateway);
5683
5820
  });
5684
5821
  return aiGatewayServices;
@@ -5711,7 +5848,10 @@ function defineServices(config, baseDir, pluginManager) {
5711
5848
  const idpResult = defineIdp(config.idp);
5712
5849
  const authResult = defineAuth(config.auth, tailordbResult.tailorDBServices, tailordbResult.externalTailorDBNamespaces);
5713
5850
  const staticWebsiteServices = defineStaticWebsites(config.staticWebsites);
5714
- const aiGatewayServices = defineAIGateways(config.aiGateways);
5851
+ const aiGatewayServices = defineAIGateways(config.aiGateways, getApplicationAuthNamespace({
5852
+ authService: authResult.authService,
5853
+ config
5854
+ }));
5715
5855
  const { secrets, ignoreNullishValues } = parseSecretManager(config.secrets);
5716
5856
  return {
5717
5857
  tailordbResult,
@@ -5779,9 +5919,9 @@ function defineApplication(params) {
5779
5919
  }
5780
5920
  /**
5781
5921
  * Generate plugin type and executor files if a plugin manager is provided.
5782
- * Collects source type info from TailorDB services and delegates to PluginManager.
5922
+ * Collects source table info from TailorDB services and delegates to PluginManager.
5783
5923
  * @param pluginManager - Plugin manager instance (skips if undefined)
5784
- * @param tailorDBServices - TailorDB services to collect type source info from
5924
+ * @param tailorDBServices - TailorDB services to collect table source info from
5785
5925
  * @param configPath - Path to tailor.config.ts for resolving plugin imports
5786
5926
  * @returns Generated executor file paths
5787
5927
  */
@@ -5803,6 +5943,11 @@ function generatePluginFilesIfNeeded(pluginManager, tailorDBServices, configPath
5803
5943
  executorGenerator: generatePluginExecutorFiles
5804
5944
  });
5805
5945
  }
5946
+ function assertWaitPointKeys() {
5947
+ const failures = collectWaitPointKeyFailures(getScopedWaitPoints());
5948
+ if (failures.length === 0) return;
5949
+ throw new Error(failures.join("\n"));
5950
+ }
5806
5951
  /**
5807
5952
  * Load and fully initialize a Tailor application.
5808
5953
  * This performs all I/O-heavy operations: loading types, processing plugins,
@@ -5895,6 +6040,7 @@ async function loadApplication(params) {
5895
6040
  await executorService.loadExecutors();
5896
6041
  if (pluginExecutorFiles.length > 0) await executorService.loadPluginExecutorFiles([...pluginExecutorFiles]);
5897
6042
  }
6043
+ assertWaitPointKeys();
5898
6044
  if (workflowService) workflowService.printLoadedWorkflows();
5899
6045
  if (httpAdapterService) httpAdapterService.printLoadedAdapters();
5900
6046
  logger.newline();
@@ -5921,5 +6067,5 @@ async function loadApplication(params) {
5921
6067
  }
5922
6068
 
5923
6069
  //#endregion
5924
- export { createApplyLimiter as $, getDistDir as A, fetchUserInfo as B, composeFunctionTreeshakeOptions as C, getPluginGenerationDependencies as D, createBundleLog as E, fetchAll as F, initOAuth2Client as G, getOAuth2ClientId as H, fetchAllTolerant as I, isNotFoundError as J, initOperatorClient as K, fetchMachineUserToken as L, hashFile as M, closeConnectionPool as N, hasGenerationHooks as O, defaultPlatformBaseUrl as P, byName as Q, fetchPaged as R, platformBundleDefinePlugin as S, resolveBundleLogLevel as T, getOrNull as U, getConsoleBaseUrl as V, getPlatformBaseUrl as W, rememberPlatformConfigForToken as X, normalizeBaseUrl as Y, resolveStaticWebsiteUrls as Z, assertUniqueTailorDBTypeNamesWithExternal as _, resolveInlineSourcemap as a, createTsconfigPathsPlugin as b, WorkflowJobSchema as c, HTTP_METHODS as d, LOG_LEVELS as et, INVOKER_EXPR as f, assertUniqueLocalTailorDBTypeNames as g, buildResolverPermissionAndInputCheckExpr as h, resolverBundleKey as i, hashContent as j, createBundleCache as k, resolveResolverDefaultPermissionForFile as l, buildResolverOperationHookExpr as m, generatePluginFilesIfNeeded as n, getApplicationAuthNamespace as o, buildExecutorArgsExpr as p, isDefaultPlatform as q, loadApplication as r, WorkflowJobFunctionExecutionPolicySchema as s, defineApplication as t, ResolverSchema as u, stringifyFunction as v, createLogLevelTreeshakeOptions as w, resolveTSConfigWithFallback as x, createGeneratedEntryResolverPlugin as y, fetchPlatformMachineUserToken as z };
5925
- //# sourceMappingURL=application-BpLeawBg.mjs.map
6070
+ export { rememberPlatformConfigForToken as $, getPluginGenerationDependencies as A, fetchMachineUserToken as B, createTsconfigPathsPlugin as C, createLogLevelTreeshakeOptions as D, composeFunctionTreeshakeOptions as E, hashFile as F, getOAuth2ClientId as G, fetchPlatformMachineUserToken as H, closeConnectionPool as I, initOAuth2Client as J, getOrNull as K, defaultPlatformBaseUrl as L, createBundleCache as M, getDistDir as N, resolveBundleLogLevel as O, hashContent as P, normalizeBaseUrl as Q, fetchAll as R, createGeneratedEntryResolverPlugin as S, platformBundleDefinePlugin as T, fetchUserInfo as U, fetchPaged as V, getConsoleBaseUrl as W, isDefaultPlatform as X, initOperatorClient as Y, isNotFoundError as Z, assertUniqueLocalTailorDBTypeNames as _, resolveInlineSourcemap as a, stringifyFunction as b, WorkflowJobFunctionExecutionPolicySchema as c, ResolverSchema as d, resolveStaticWebsiteUrls as et, HTTP_METHODS as f, buildResolverPermissionAndInputCheckExpr as g, buildResolverOperationHookExpr as h, resolverBundleKey as i, hasGenerationHooks as j, createBundleLog as k, WorkflowJobSchema as l, buildExecutorArgsExpr as m, generatePluginFilesIfNeeded as n, createApplyLimiter as nt, getApplicationAuthNamespace as o, INVOKER_EXPR as p, getPlatformBaseUrl as q, loadApplication as r, LOG_LEVELS as rt, requireApplicationAuthNamespace as s, defineApplication as t, byName as tt, resolveResolverDefaultPermissionForFile as u, assertUniqueTailorDBTypeNamesWithExternal as v, resolveTSConfigWithFallback as w, tailorPrincipalMap as x, PRINCIPAL_VAR as y, fetchAllTolerant as z };
6071
+ //# sourceMappingURL=application-C8a0UJ-z.mjs.map