@tailor-platform/sdk 1.22.0 → 1.24.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 (69) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/app-config-BNKHurwr.d.mts +174 -0
  3. package/dist/{application-CTQe2HSB.mjs → application-DThE2HW7.mjs} +200 -215
  4. package/dist/application-DThE2HW7.mjs.map +1 -0
  5. package/dist/application-P1by1htu.mjs +8 -0
  6. package/dist/{brand-DyPrAzpM.mjs → brand-64NrPh_e.mjs} +1 -1
  7. package/dist/{brand-DyPrAzpM.mjs.map → brand-64NrPh_e.mjs.map} +1 -1
  8. package/dist/cli/index.d.mts +0 -1
  9. package/dist/cli/index.mjs +7 -7
  10. package/dist/cli/index.mjs.map +1 -1
  11. package/dist/cli/lib.d.mts +8 -9
  12. package/dist/cli/lib.mjs +7 -7
  13. package/dist/cli/lib.mjs.map +1 -1
  14. package/dist/cli/skills.d.mts +0 -1
  15. package/dist/configure/index.d.mts +5 -5
  16. package/dist/configure/index.mjs +3 -3
  17. package/dist/configure/index.mjs.map +1 -1
  18. package/dist/enum-constants-B5Nl-yzx.mjs.map +1 -1
  19. package/dist/env-CHwmMyfF.d.mts +32 -0
  20. package/dist/file-utils-sEOwAdJ4.mjs.map +1 -1
  21. package/dist/{index-Cwi86SUR.d.mts → index-BrIZ1rm2.d.mts} +2 -3
  22. package/dist/{index-BiutQT7m.d.mts → index-Df0aH5zp.d.mts} +55 -5
  23. package/dist/{index-DPN_P0w3.d.mts → index-DhtKJmgi.d.mts} +2 -3
  24. package/dist/{index-ClS0NClx.d.mts → index-DkRJwNw2.d.mts} +2 -3
  25. package/dist/{index-BGPX26_D.d.mts → index-zZUL7_2B.d.mts} +2 -3
  26. package/dist/{interceptor-DiARwPfw.mjs → interceptor-B-0OmiDZ.mjs} +1 -1
  27. package/dist/{interceptor-DiARwPfw.mjs.map → interceptor-B-0OmiDZ.mjs.map} +1 -1
  28. package/dist/{job-CRavYLLk.mjs → job-CnqcfVTI.mjs} +2 -2
  29. package/dist/{job-CRavYLLk.mjs.map → job-CnqcfVTI.mjs.map} +1 -1
  30. package/dist/kysely/index.d.mts +0 -1
  31. package/dist/kysely-type-CSlcwNFH.mjs.map +1 -1
  32. package/dist/{package-json-iVBhE5Ef.mjs → package-json-4G3gLWMd.mjs} +1 -1
  33. package/dist/{package-json-iVBhE5Ef.mjs.map → package-json-4G3gLWMd.mjs.map} +1 -1
  34. package/dist/package-json-BqvUKPBM.mjs +3 -0
  35. package/dist/plugin/builtin/enum-constants/index.d.mts +2 -3
  36. package/dist/plugin/builtin/file-utils/index.d.mts +2 -3
  37. package/dist/plugin/builtin/kysely-type/index.d.mts +2 -3
  38. package/dist/plugin/builtin/seed/index.d.mts +2 -3
  39. package/dist/plugin/builtin/seed/index.mjs +1 -1
  40. package/dist/plugin/index.d.mts +2 -2
  41. package/dist/plugin/index.mjs.map +1 -1
  42. package/dist/{types-bcuNRo1Y.d.mts → plugin-CE-BZZgX.d.mts} +1069 -1763
  43. package/dist/{query-Bz2oDGhw.mjs → query-CV5n7DRd.mjs} +496 -115
  44. package/dist/query-CV5n7DRd.mjs.map +1 -0
  45. package/dist/{schema-Cjm-OvPF.mjs → schema-0ByCZ2Ym.mjs} +2 -2
  46. package/dist/schema-0ByCZ2Ym.mjs.map +1 -0
  47. package/dist/{seed-CXvCW3Xc.mjs → seed-Cl5QXYsL.mjs} +13 -3
  48. package/dist/seed-Cl5QXYsL.mjs.map +1 -0
  49. package/dist/{telemetry-C46fds1l.mjs → telemetry-BPviAbME.mjs} +2 -2
  50. package/dist/{telemetry-C46fds1l.mjs.map → telemetry-BPviAbME.mjs.map} +1 -1
  51. package/dist/telemetry-Dq5FZUH0.mjs +3 -0
  52. package/dist/utils/test/index.d.mts +3 -4
  53. package/dist/utils/test/index.mjs +2 -2
  54. package/docs/cli/tailordb.md +1 -1
  55. package/docs/cli-reference.md +8 -8
  56. package/docs/services/auth.md +2 -2
  57. package/package.json +27 -25
  58. package/postinstall.mjs +4 -14
  59. package/dist/application-CTQe2HSB.mjs.map +0 -1
  60. package/dist/application-DdSu3baZ.mjs +0 -8
  61. package/dist/package-json-BI0ng3_5.mjs +0 -3
  62. package/dist/query-Bz2oDGhw.mjs.map +0 -1
  63. package/dist/schema-Cjm-OvPF.mjs.map +0 -1
  64. package/dist/seed-CXvCW3Xc.mjs.map +0 -1
  65. package/dist/telemetry-BAxP8-PR.mjs +0 -3
  66. package/dist/types-CBTSg-LK.mjs +0 -13
  67. package/dist/types-CBTSg-LK.mjs.map +0 -1
  68. package/dist/types-DVMQNdTs.d.mts +0 -246
  69. package/dist/user-defined.d.ts +0 -13
@@ -1,8 +1,7 @@
1
- import { t as db } from "./schema-Cjm-OvPF.mjs";
2
- import { $ as AuthSCIMAttribute_Mutability, A as platformBaseUrl, B as TailorDBType_Permission_Permit, C as readPlatformConfig, E as fetchMachineUserToken, F as WorkflowJobExecution_Status, H as PipelineResolver_OperationType, I as TailorDBGQLPermission_Action, J as ExecutorTriggerType, K as ExecutorJobStatus, L as TailorDBGQLPermission_Operator, M as userAgent, N as WorkspacePlatformUserRole, P as WorkflowExecution_Status, Q as AuthOAuth2Client_GrantType, R as TailorDBGQLPermission_Permit, S as loadWorkspaceId, T as fetchAll, U as IdPLang, V as TailorDBType_PermitAction, W as FunctionExecution_Status, X as AuthInvokerSchema, Y as AuthIDPConfig_AuthType, Z as AuthOAuth2Client_ClientType, _ as hashFile, a as loadConfig, at as UserProfileProviderConfig_UserProfileProviderType, b as loadFolderId, ct as Condition_Operator, d as TailorDBTypeSchema, dt as ApplicationSchemaUpdateAttemptStatus, et as AuthSCIMAttribute_Type, f as stringifyFunction, ft as Subgraph_ServiceType, g as getDistDir, h as createBundleCache, ht as symbols, it as TenantProviderConfig_TenantProviderType, j as resolveStaticWebsiteUrls, k as initOperatorClient, l as OAuth2ClientSchema, lt as FilterSchema, m as loadFilesWithIgnores, mt as styles, n as generatePluginFilesIfNeeded, nt as AuthSCIMConfig_AuthorizationType, ot as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, p as tailorUserMap, pt as logger, q as ExecutorTargetType, r as loadApplication, s as createExecutorService, st as ConditionSchema, t as defineApplication, tt as AuthSCIMAttribute_Uniqueness, ut as PageDirection, w as writePlatformConfig, x as loadOrganizationId, y as loadAccessToken, z as TailorDBType_Permission_Operator } from "./application-CTQe2HSB.mjs";
3
- import { t as readPackageJson } from "./package-json-iVBhE5Ef.mjs";
4
- import { r as withSpan } from "./telemetry-C46fds1l.mjs";
5
- import { createRequire } from "node:module";
1
+ import { t as db } from "./schema-0ByCZ2Ym.mjs";
2
+ import { $ as AuthSCIMAttribute_Mutability, A as platformBaseUrl, B as TailorDBType_Permission_Permit, C as readPlatformConfig, E as fetchMachineUserToken, F as WorkflowJobExecution_Status, H as PipelineResolver_OperationType, I as TailorDBGQLPermission_Action, J as ExecutorTriggerType, K as ExecutorJobStatus, L as TailorDBGQLPermission_Operator, M as userAgent, N as WorkspacePlatformUserRole, P as WorkflowExecution_Status, Q as AuthOAuth2Client_GrantType, R as TailorDBGQLPermission_Permit, S as loadWorkspaceId, T as fetchAll, U as IdPLang, V as TailorDBType_PermitAction, W as FunctionExecution_Status, X as AuthInvokerSchema, Y as AuthIDPConfig_AuthType, Z as AuthOAuth2Client_ClientType, _ as hashFile, a as loadConfig, at as UserProfileProviderConfig_UserProfileProviderType, b as loadFolderId, ct as Condition_Operator, d as TailorDBTypeSchema, dt as ApplicationSchemaUpdateAttemptStatus, et as AuthSCIMAttribute_Type, f as stringifyFunction, ft as Subgraph_ServiceType, g as getDistDir, h as createBundleCache, ht as symbols, it as TenantProviderConfig_TenantProviderType, j as resolveStaticWebsiteUrls, k as initOperatorClient, l as OAuth2ClientSchema, lt as FilterSchema, m as loadFilesWithIgnores, mt as styles, n as generatePluginFilesIfNeeded, nt as AuthSCIMConfig_AuthorizationType, ot as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, p as tailorUserMap, pt as logger, q as ExecutorTargetType, r as loadApplication, s as createExecutorService, st as ConditionSchema, t as defineApplication, tt as AuthSCIMAttribute_Uniqueness, ut as PageDirection, w as writePlatformConfig, x as loadOrganizationId, y as loadAccessToken, z as TailorDBType_Permission_Operator } from "./application-DThE2HW7.mjs";
3
+ import { t as readPackageJson } from "./package-json-4G3gLWMd.mjs";
4
+ import { r as withSpan } from "./telemetry-BPviAbME.mjs";
6
5
  import { arg, defineCommand, runCommand } from "politty";
7
6
  import { z } from "zod";
8
7
  import * as fs$1 from "node:fs";
@@ -14,6 +13,7 @@ import { getBorderCharacters, table } from "table";
14
13
  import { ValueSchema, timestampDate } from "@bufbuild/protobuf/wkt";
15
14
  import { Code, ConnectError } from "@connectrpc/connect";
16
15
  import { resolveTSConfig } from "pkg-types";
16
+ import { tmpdir } from "node:os";
17
17
  import { findUpSync } from "find-up-simple";
18
18
  import ml from "multiline-ts";
19
19
  import * as crypto from "node:crypto";
@@ -28,7 +28,9 @@ import { setTimeout as setTimeout$1 } from "timers/promises";
28
28
  import { spawn } from "node:child_process";
29
29
  import { watch } from "chokidar";
30
30
  import * as madgeModule from "madge";
31
+ import { createInterface } from "node:readline/promises";
31
32
  import { astVisitor, parse, toSql } from "pgsql-ast-parser";
33
+ import { parse as parse$1 } from "@0no-co/graphql.web";
32
34
 
33
35
  //#region src/cli/shared/errors.ts
34
36
  /**
@@ -211,7 +213,7 @@ const withCommonArgs = (handler) => async (args) => {
211
213
  try {
212
214
  if ("json" in args && typeof args.json === "boolean") logger.jsonMode = args.json;
213
215
  loadEnvFiles(args["env-file"], args["env-file-if-exists"]);
214
- const { initTelemetry } = await import("./telemetry-BAxP8-PR.mjs");
216
+ const { initTelemetry } = await import("./telemetry-Dq5FZUH0.mjs");
215
217
  await initTelemetry();
216
218
  await handler(args);
217
219
  } catch (error) {
@@ -224,7 +226,7 @@ const withCommonArgs = (handler) => async (args) => {
224
226
  } else logger.error(`Unknown error: ${error}`);
225
227
  process.exit(1);
226
228
  } finally {
227
- const { shutdownTelemetry } = await import("./telemetry-BAxP8-PR.mjs");
229
+ const { shutdownTelemetry } = await import("./telemetry-Dq5FZUH0.mjs");
228
230
  await shutdownTelemetry();
229
231
  }
230
232
  process.exit(0);
@@ -519,7 +521,6 @@ export {};
519
521
 
520
522
  `;
521
523
  }
522
- const require = createRequire(import.meta.url);
523
524
  function collectAttributesFromConfig(config) {
524
525
  const auth = config.auth;
525
526
  if (!auth || typeof auth !== "object") return {};
@@ -562,12 +563,7 @@ function collectAttributesFromConfig(config) {
562
563
  * @returns Absolute path to the type definition file
563
564
  */
564
565
  function resolveTypeDefinitionPath(configPath) {
565
- const typePath = process.env.TAILOR_PLATFORM_SDK_TYPE_PATH;
566
- if (typePath) return path.resolve(process.cwd(), typePath);
567
- const configDir = path.dirname(path.resolve(configPath));
568
- const packageDir = resolvePackageDirectory(configDir);
569
- if (!packageDir) return path.join(configDir, "node_modules", "@tailor-platform", "sdk", "dist", "user-defined.d.ts");
570
- return path.join(packageDir, "dist", "user-defined.d.ts");
566
+ return path.join(path.dirname(path.resolve(configPath)), "tailor.d.ts");
571
567
  }
572
568
  /**
573
569
  * Generate user type definitions from the app config and write them to disk.
@@ -595,33 +591,16 @@ async function generateUserTypes(options) {
595
591
  logger.error(String(error));
596
592
  }
597
593
  }
598
- function resolvePackageDirectory(startDir) {
599
- let currentDir = startDir;
600
- const root = path.parse(currentDir).root;
601
- while (true) {
602
- const candidate = path.join(currentDir, "node_modules", "@tailor-platform", "sdk");
603
- const packageJsonPath = path.join(candidate, "package.json");
604
- if (fs$1.existsSync(packageJsonPath)) return candidate;
605
- if (currentDir === root) break;
606
- currentDir = path.dirname(currentDir);
607
- }
608
- try {
609
- const resolved = require.resolve("@tailor-platform/sdk/package.json", { paths: [startDir] });
610
- return path.dirname(resolved);
611
- } catch {
612
- return null;
613
- }
614
- }
615
594
 
616
595
  //#endregion
617
- //#region src/parser/plugin-config/generation-types.ts
596
+ //#region src/types/plugin-generation.ts
618
597
  /**
619
598
  * Derives generation-time dependency set from hook presence on a plugin.
620
- * @param plugin - Plugin to check for generation hooks
621
- * @param plugin.onTailorDBReady - TailorDB phase-complete hook
622
- * @param plugin.onResolverReady - Resolver phase-complete hook
623
- * @param plugin.onExecutorReady - Executor phase-complete hook
624
- * @returns Set of dependency kinds based on which hooks are implemented
599
+ * @param plugin - The plugin object to inspect.
600
+ * @param plugin.onTailorDBReady - Hook for TailorDB readiness.
601
+ * @param plugin.onResolverReady - Hook for resolver readiness.
602
+ * @param plugin.onExecutorReady - Hook for executor readiness.
603
+ * @returns Set of dependency kinds required by the plugin.
625
604
  */
626
605
  function getPluginGenerationDependencies(plugin) {
627
606
  const deps = /* @__PURE__ */ new Set();
@@ -632,11 +611,11 @@ function getPluginGenerationDependencies(plugin) {
632
611
  }
633
612
  /**
634
613
  * Checks if a plugin has any generation-time hooks.
635
- * @param plugin - Plugin to check
636
- * @param plugin.onTailorDBReady - TailorDB phase-complete hook
637
- * @param plugin.onResolverReady - Resolver phase-complete hook
638
- * @param plugin.onExecutorReady - Executor phase-complete hook
639
- * @returns True if the plugin has at least one generation hook
614
+ * @param plugin - The plugin object to inspect.
615
+ * @param plugin.onTailorDBReady - Hook for TailorDB readiness.
616
+ * @param plugin.onResolverReady - Hook for resolver readiness.
617
+ * @param plugin.onExecutorReady - Hook for executor readiness.
618
+ * @returns True if the plugin has at least one generation hook.
640
619
  */
641
620
  function hasGenerationHooks(plugin) {
642
621
  return !!(plugin.onTailorDBReady || plugin.onResolverReady || plugin.onExecutorReady);
@@ -3694,7 +3673,7 @@ function createSnapshotFieldConfig(field) {
3694
3673
  }
3695
3674
  /**
3696
3675
  * Create a snapshot field config from an OperatorFieldConfig (for nested fields)
3697
- * @param {import("@/parser/service/tailordb/types").OperatorFieldConfig} fieldConfig - Field configuration
3676
+ * @param {import("@/types/tailordb").OperatorFieldConfig} fieldConfig - Field configuration
3698
3677
  * @returns {SnapshotFieldConfig} Snapshot field configuration
3699
3678
  */
3700
3679
  function createSnapshotFieldConfigFromOperatorConfig(fieldConfig) {
@@ -9549,6 +9528,70 @@ function logBetaWarning(featureName) {
9549
9528
  logger.newline();
9550
9529
  }
9551
9530
 
9531
+ //#endregion
9532
+ //#region src/cli/shared/editor.ts
9533
+ const DEFAULT_EDITOR = "editor";
9534
+ function normalizeEditorCommand(editor) {
9535
+ const normalized = editor?.trim();
9536
+ return normalized && normalized.length > 0 ? normalized : void 0;
9537
+ }
9538
+ /**
9539
+ * Resolve an editor command only from explicit environment variables.
9540
+ * @returns Configured editor command, if any
9541
+ */
9542
+ function getConfiguredEditorCommand() {
9543
+ return normalizeEditorCommand(process.env.VISUAL) ?? normalizeEditorCommand(process.env.EDITOR);
9544
+ }
9545
+ /**
9546
+ * Resolve the editor command used for interactive file editing.
9547
+ * @returns Configured editor command or the system default fallback
9548
+ */
9549
+ function getEditorCommand() {
9550
+ return getConfiguredEditorCommand() ?? DEFAULT_EDITOR;
9551
+ }
9552
+ function parseEditorCommand(editor) {
9553
+ const [command, ...args] = editor.trim().split(/\s+/);
9554
+ if (!command) throw new Error("Editor command is empty.");
9555
+ return {
9556
+ command,
9557
+ args
9558
+ };
9559
+ }
9560
+ /**
9561
+ * Open a file in the resolved editor and wait for the process to exit.
9562
+ * @param filePath - File path to open
9563
+ * @param editor - Editor command string
9564
+ * @returns Whether an editor process was launched
9565
+ */
9566
+ async function openInEditor(filePath, editor = getEditorCommand()) {
9567
+ const { command, args } = parseEditorCommand(editor);
9568
+ await new Promise((resolve$1, reject) => {
9569
+ const child = spawn(command, [...args, filePath], {
9570
+ stdio: "inherit",
9571
+ detached: false
9572
+ });
9573
+ child.once("error", (error) => reject(error));
9574
+ child.once("close", (code) => {
9575
+ if (code == null || code === 0) {
9576
+ resolve$1();
9577
+ return;
9578
+ }
9579
+ reject(/* @__PURE__ */ new Error(`Editor exited with code ${code}.`));
9580
+ });
9581
+ });
9582
+ return true;
9583
+ }
9584
+ /**
9585
+ * Open a file only when an editor is explicitly configured in the environment.
9586
+ * @param filePath - File path to open
9587
+ * @returns Whether an editor process was launched
9588
+ */
9589
+ async function openInConfiguredEditor(filePath) {
9590
+ const editor = getConfiguredEditorCommand();
9591
+ if (!editor) return false;
9592
+ return await openInEditor(filePath, editor);
9593
+ }
9594
+
9552
9595
  //#endregion
9553
9596
  //#region src/cli/commands/tailordb/migrate/db-types-generator.ts
9554
9597
  /**
@@ -10058,7 +10101,7 @@ async function generate(options) {
10058
10101
  if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
10059
10102
  let pluginManager;
10060
10103
  if (plugins.length > 0) pluginManager = new PluginManager(plugins);
10061
- const { defineApplication: defineApplication$1 } = await import("./application-DdSu3baZ.mjs");
10104
+ const { defineApplication: defineApplication$1 } = await import("./application-P1by1htu.mjs");
10062
10105
  const application = defineApplication$1({
10063
10106
  config,
10064
10107
  pluginManager
@@ -10157,33 +10200,21 @@ async function generateDiffFromSnapshot(previousSnapshot, currentSnapshot, migra
10157
10200
  logger.newline();
10158
10201
  logger.log("A migration script was generated for breaking changes.");
10159
10202
  logger.log("Please review and edit the script before running 'tailor-sdk apply'.");
10160
- await openInEditor(result.migrateFilePath);
10161
- }
10162
- }
10163
- /**
10164
- * Open a file in the user's preferred editor
10165
- * @param {string} filePath - Path to file to open
10166
- * @returns {Promise<void>} Promise that resolves when editor closes
10167
- */
10168
- async function openInEditor(filePath) {
10169
- const editor = process.env.EDITOR;
10170
- if (!editor) return;
10171
- try {
10172
- await fs.access(filePath);
10173
- } catch {
10174
- return;
10203
+ const editor = getConfiguredEditorCommand();
10204
+ if (!editor) return;
10205
+ try {
10206
+ await fs.access(result.migrateFilePath);
10207
+ } catch {
10208
+ return;
10209
+ }
10210
+ logger.newline();
10211
+ logger.info(`Opening ${path.basename(result.migrateFilePath)} in ${editor}...`);
10212
+ try {
10213
+ await openInConfiguredEditor(result.migrateFilePath);
10214
+ } catch {
10215
+ return;
10216
+ }
10175
10217
  }
10176
- const [command, ...args] = editor.trim().split(/\s+/);
10177
- logger.newline();
10178
- logger.info(`Opening ${path.basename(filePath)} in ${editor}...`);
10179
- const child = spawn(command, [...args, filePath], {
10180
- stdio: "inherit",
10181
- detached: false
10182
- });
10183
- await new Promise((resolve$1) => {
10184
- child.on("close", () => resolve$1());
10185
- child.on("error", () => resolve$1());
10186
- });
10187
10218
  }
10188
10219
  /**
10189
10220
  * CLI command definition for generate
@@ -11451,6 +11482,118 @@ function mapQueryExecutionError(args) {
11451
11482
  return args.error instanceof Error ? args.error : new Error(message);
11452
11483
  }
11453
11484
 
11485
+ //#endregion
11486
+ //#region src/cli/query/graphql-repl.ts
11487
+ /**
11488
+ * Return true when the buffered GraphQL input parses as a complete document.
11489
+ * @param input - Buffered GraphQL input
11490
+ * @returns True when the GraphQL document is complete and ready to execute
11491
+ */
11492
+ function isGraphQLInputComplete(input) {
11493
+ if (input.trim().length === 0) return false;
11494
+ try {
11495
+ parse$1(input);
11496
+ return true;
11497
+ } catch {
11498
+ return false;
11499
+ }
11500
+ }
11501
+
11502
+ //#endregion
11503
+ //#region src/cli/query/sql-repl.ts
11504
+ /**
11505
+ * Return true when the buffered SQL input ends with a real statement terminator.
11506
+ * @param input - Buffered SQL input
11507
+ * @returns True when the SQL statement is complete and ready to execute
11508
+ */
11509
+ function isSqlInputComplete(input) {
11510
+ let inSingleQuote = false;
11511
+ let inDoubleQuote = false;
11512
+ let inLineComment = false;
11513
+ let blockCommentDepth = 0;
11514
+ let dollarQuoteTag = null;
11515
+ let lastSignificantTokenWasSemicolon = false;
11516
+ for (let i = 0; i < input.length; i += 1) {
11517
+ const char = input[i];
11518
+ const next = input[i + 1];
11519
+ if (inLineComment) {
11520
+ if (char === "\n") inLineComment = false;
11521
+ continue;
11522
+ }
11523
+ if (blockCommentDepth > 0) {
11524
+ if (char === "/" && next === "*") {
11525
+ blockCommentDepth += 1;
11526
+ i += 1;
11527
+ continue;
11528
+ }
11529
+ if (char === "*" && next === "/") {
11530
+ blockCommentDepth -= 1;
11531
+ i += 1;
11532
+ }
11533
+ continue;
11534
+ }
11535
+ if (dollarQuoteTag != null) {
11536
+ if (input.startsWith(dollarQuoteTag, i)) {
11537
+ i += dollarQuoteTag.length - 1;
11538
+ dollarQuoteTag = null;
11539
+ }
11540
+ continue;
11541
+ }
11542
+ if (inSingleQuote) {
11543
+ if (char === "'" && next === "'") {
11544
+ i += 1;
11545
+ continue;
11546
+ }
11547
+ if (char === "'") inSingleQuote = false;
11548
+ continue;
11549
+ }
11550
+ if (inDoubleQuote) {
11551
+ if (char === "\"" && next === "\"") {
11552
+ i += 1;
11553
+ continue;
11554
+ }
11555
+ if (char === "\"") inDoubleQuote = false;
11556
+ continue;
11557
+ }
11558
+ if (char === "-" && next === "-") {
11559
+ inLineComment = true;
11560
+ i += 1;
11561
+ continue;
11562
+ }
11563
+ if (char === "/" && next === "*") {
11564
+ blockCommentDepth = 1;
11565
+ i += 1;
11566
+ continue;
11567
+ }
11568
+ if (char === "'") {
11569
+ lastSignificantTokenWasSemicolon = false;
11570
+ inSingleQuote = true;
11571
+ continue;
11572
+ }
11573
+ if (char === "\"") {
11574
+ lastSignificantTokenWasSemicolon = false;
11575
+ inDoubleQuote = true;
11576
+ continue;
11577
+ }
11578
+ if (char === "$") {
11579
+ const rest = input.slice(i);
11580
+ const match = rest.match(/^\$[A-Za-z_][A-Za-z0-9_]*\$/) ?? rest.match(/^\$\$/);
11581
+ if (match != null) {
11582
+ lastSignificantTokenWasSemicolon = false;
11583
+ dollarQuoteTag = match[0];
11584
+ i += match[0].length - 1;
11585
+ continue;
11586
+ }
11587
+ }
11588
+ if (char === ";") {
11589
+ lastSignificantTokenWasSemicolon = true;
11590
+ continue;
11591
+ }
11592
+ if (!/\s/.test(char)) lastSignificantTokenWasSemicolon = false;
11593
+ }
11594
+ return lastSignificantTokenWasSemicolon && !inSingleQuote && !inDoubleQuote && blockCommentDepth === 0 && dollarQuoteTag == null;
11595
+ }
11596
+
11454
11597
  //#endregion
11455
11598
  //#region src/cli/query/sql-type-extractor.ts
11456
11599
  /**
@@ -11560,14 +11703,14 @@ async function loadTypeFieldOrder(config, namespace) {
11560
11703
  //#endregion
11561
11704
  //#region src/cli/query/index.ts
11562
11705
  const queryEngineSchema = z.enum(["sql", "gql"]);
11563
- const queryOptionsSchema = z.object({
11706
+ const queryBaseOptionsSchema = z.object({
11564
11707
  workspaceId: z.string().optional(),
11565
11708
  profile: z.string().optional(),
11566
11709
  configPath: z.string().optional(),
11567
11710
  engine: queryEngineSchema,
11568
- query: z.string(),
11569
11711
  machineUser: z.string()
11570
11712
  });
11713
+ const queryOptionsSchema = queryBaseOptionsSchema.extend({ query: z.string() });
11571
11714
  async function getNamespaceFromSqlQuery(workspaceId, query$1, client, namespaces) {
11572
11715
  if (namespaces.length === 0) throw new Error("No namespaces found in configuration.");
11573
11716
  if (namespaces.length === 1) return namespaces[0];
@@ -11586,7 +11729,7 @@ async function getNamespaceFromSqlQuery(workspaceId, query$1, client, namespaces
11586
11729
  throw new Error(`Query references types from multiple namespaces: ${[...namespacesFromTypes].join(", ")}.`);
11587
11730
  }
11588
11731
  async function loadOptions(options) {
11589
- const result = queryOptionsSchema.safeParse(options);
11732
+ const result = queryBaseOptionsSchema.safeParse(options);
11590
11733
  if (!result.success) throw new Error(result.error.issues[0].message);
11591
11734
  const client = await initOperatorClient(await loadAccessToken({
11592
11735
  useProfile: true,
@@ -11609,23 +11752,14 @@ async function loadOptions(options) {
11609
11752
  name: result.data.machineUser
11610
11753
  });
11611
11754
  if (!machineUserResource) throw new Error(`Machine user ${result.data.machineUser} not found.`);
11612
- if (options.engine === "gql") return {
11613
- engine: options.engine,
11614
- client,
11615
- workspaceId,
11616
- config,
11617
- application,
11618
- machineUserResource
11619
- };
11620
- const namespace = await getNamespaceFromSqlQuery(workspaceId, result.data.query, client, namespaces);
11621
11755
  return {
11622
- engine: options.engine,
11756
+ engine: result.data.engine,
11623
11757
  client,
11624
11758
  workspaceId,
11625
11759
  config,
11626
11760
  application,
11627
11761
  machineUserResource,
11628
- namespace
11762
+ namespaces
11629
11763
  };
11630
11764
  }
11631
11765
  async function sqlQuery(client, invoker, args) {
@@ -11679,40 +11813,242 @@ function parseExecutionResult(result) {
11679
11813
  }
11680
11814
  }
11681
11815
  /**
11816
+ * Resolve query input mode from CLI args.
11817
+ * @param args - Query input flags
11818
+ * @param args.query - Direct query string
11819
+ * @param args.file - File path containing query text
11820
+ * @param args.edit - Open a query editor instead of REPL
11821
+ * @param args.engine - Query engine used to choose temp file extension
11822
+ * @returns Normalized input mode
11823
+ */
11824
+ async function resolveQueryCommandInput(args) {
11825
+ if (args.query != null) return {
11826
+ mode: "query",
11827
+ query: args.query
11828
+ };
11829
+ if (args.file != null) return {
11830
+ mode: "query",
11831
+ query: await fs.readFile(args.file, "utf-8")
11832
+ };
11833
+ if (args.edit) return await resolveEditedQueryInput(args.engine);
11834
+ return { mode: "repl" };
11835
+ }
11836
+ async function resolveEditedQueryInput(engine) {
11837
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Non-interactive terminals are not supported. Pass -q/--query or -f/--file to run a query.");
11838
+ const editor = getEditorCommand();
11839
+ const tempDir = await fs.mkdtemp(path.join(tmpdir(), "tailor-query-"));
11840
+ const fileExtension = engine === "sql" ? "sql" : "graphql";
11841
+ const filePath = path.join(tempDir, `query.${fileExtension}`);
11842
+ const initialQuery = "";
11843
+ try {
11844
+ await fs.writeFile(filePath, initialQuery, "utf-8");
11845
+ try {
11846
+ await openInEditor(filePath, editor);
11847
+ } catch (error) {
11848
+ throw new Error(`Failed to open query editor "${editor}": ${error instanceof Error ? error.message : String(error)}`);
11849
+ }
11850
+ const editedQuery = await fs.readFile(filePath, "utf-8");
11851
+ if (editedQuery.trim().length === 0 || editedQuery === initialQuery) return { mode: "abort" };
11852
+ return {
11853
+ mode: "query",
11854
+ query: editedQuery
11855
+ };
11856
+ } finally {
11857
+ await fs.rm(tempDir, {
11858
+ recursive: true,
11859
+ force: true
11860
+ });
11861
+ }
11862
+ }
11863
+ /**
11682
11864
  * Dispatch query execution.
11683
11865
  * @param options - Query command options
11684
11866
  * @returns Dispatch result
11685
11867
  */
11686
11868
  async function query(options) {
11687
- const { client, workspaceId, config, application, machineUserResource, engine, namespace } = await loadOptions(options);
11688
- try {
11689
- const bundledCode = await bundleQueryScript(engine);
11690
- const invoker = create(AuthInvokerSchema, {
11691
- namespace: application.authNamespace,
11692
- machineUserName: machineUserResource.name
11693
- });
11694
- switch (engine) {
11695
- case "sql": return reorderSqlColumns(await sqlQuery(client, invoker, {
11696
- workspaceId,
11869
+ const result = queryOptionsSchema.safeParse(options);
11870
+ if (!result.success) throw new Error(result.error.issues[0].message);
11871
+ return await (await prepareQueryExecutor(result.data))(result.data.query);
11872
+ }
11873
+ async function prepareQueryExecutor(options) {
11874
+ const { client, workspaceId, config, application, machineUserResource, engine, namespaces } = await loadOptions(options);
11875
+ const bundledCode = await bundleQueryScript(engine);
11876
+ const invoker = create(AuthInvokerSchema, {
11877
+ namespace: application.authNamespace,
11878
+ machineUserName: machineUserResource.name
11879
+ });
11880
+ return async (queryString) => {
11881
+ let namespace;
11882
+ try {
11883
+ switch (engine) {
11884
+ case "sql":
11885
+ namespace = await getNamespaceFromSqlQuery(workspaceId, queryString, client, namespaces);
11886
+ return reorderSqlColumns(await sqlQuery(client, invoker, {
11887
+ workspaceId,
11888
+ namespace,
11889
+ bundledCode,
11890
+ query: queryString
11891
+ }), config, namespace, queryString);
11892
+ case "gql": return await gqlQuery(client, invoker, application, machineUserResource, {
11893
+ workspaceId,
11894
+ bundledCode,
11895
+ query: queryString
11896
+ });
11897
+ default: throw new Error(`Unsupported query engine: ${engine}`);
11898
+ }
11899
+ } catch (error) {
11900
+ throw mapQueryExecutionError({
11901
+ error,
11902
+ engine,
11697
11903
  namespace,
11698
- bundledCode,
11699
- query: options.query
11700
- }), config, namespace, options.query);
11701
- case "gql": return await gqlQuery(client, invoker, application, machineUserResource, {
11702
- workspaceId,
11703
- bundledCode,
11704
- query: options.query
11904
+ machineUser: options.machineUser
11705
11905
  });
11706
- default: throw new Error(`Unsupported query engine: ${engine}`);
11707
11906
  }
11708
- } catch (error) {
11709
- throw mapQueryExecutionError({
11710
- error,
11711
- engine,
11712
- namespace,
11713
- machineUser: options.machineUser
11714
- });
11907
+ };
11908
+ }
11909
+ function isReadlineTerminationError(error) {
11910
+ if (!(error instanceof Error) || !("code" in error)) return false;
11911
+ return error.code === "ABORT_ERR" || error.code === "ERR_USE_AFTER_CLOSE";
11912
+ }
11913
+ /**
11914
+ * Resolve a backslash REPL command into its normalized action.
11915
+ * @param input - Raw user input
11916
+ * @returns Normalized REPL command, or null for non-command input
11917
+ */
11918
+ function resolveReplCommand(input) {
11919
+ const trimmed = input.trim();
11920
+ if (!trimmed.startsWith("\\")) return null;
11921
+ if (trimmed === "\\q" || trimmed === "\\quit") return "quit";
11922
+ if (trimmed === "\\help" || trimmed === "\\h" || trimmed === "\\?") return "help";
11923
+ if (trimmed === "\\clear" || trimmed === "\\c") return "clear";
11924
+ return "unknown";
11925
+ }
11926
+ /**
11927
+ * Decide how REPL should react to Ctrl+C based on current buffered input.
11928
+ * @param bufferedLines - Previously accepted lines in the current statement buffer
11929
+ * @param currentLine - In-progress line currently being edited
11930
+ * @returns Whether to clear the buffer or exit the REPL
11931
+ */
11932
+ function resolveReplInterruptAction(bufferedLines, currentLine) {
11933
+ if (bufferedLines.length === 0 && currentLine.length === 0) return "exit";
11934
+ return "clear";
11935
+ }
11936
+ /**
11937
+ * Clear the interactive terminal screen and move the cursor to the top-left.
11938
+ */
11939
+ function clearReplScreen() {
11940
+ process.stdout.write("\x1Bc");
11941
+ }
11942
+ async function runRepl(options) {
11943
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Non-interactive terminals are not supported. Pass -q/--query or -f/--file to run a query.");
11944
+ const execute = await prepareQueryExecutor(options);
11945
+ const rl = createInterface({
11946
+ input: process.stdin,
11947
+ output: process.stdout
11948
+ });
11949
+ logger.info(`Entering ${options.engine.toUpperCase()} REPL mode.`);
11950
+ logger.info("Type \\help for usage, \\q to quit.");
11951
+ const lines = [];
11952
+ try {
11953
+ while (true) {
11954
+ const prompt = lines.length === 0 ? `${options.engine}> ` : " ";
11955
+ let line;
11956
+ let interruptAction = null;
11957
+ const controller = new AbortController();
11958
+ const handleSigint = () => {
11959
+ interruptAction = resolveReplInterruptAction(lines, rl.line);
11960
+ if (interruptAction === "clear") {
11961
+ lines.length = 0;
11962
+ rl.write(null, {
11963
+ ctrl: true,
11964
+ name: "u"
11965
+ });
11966
+ process.stdout.write("\n");
11967
+ } else rl.close();
11968
+ controller.abort();
11969
+ };
11970
+ rl.once("SIGINT", handleSigint);
11971
+ try {
11972
+ line = await rl.question(prompt, { signal: controller.signal });
11973
+ } catch (error) {
11974
+ rl.off("SIGINT", handleSigint);
11975
+ if (controller.signal.aborted) {
11976
+ if (interruptAction === "exit") return;
11977
+ continue;
11978
+ }
11979
+ if (isReadlineTerminationError(error)) return;
11980
+ throw error;
11981
+ } finally {
11982
+ rl.off("SIGINT", handleSigint);
11983
+ }
11984
+ const trimmed = line.trim();
11985
+ if (lines.length === 0 && trimmed === "") continue;
11986
+ if (lines.length === 0) {
11987
+ const command = resolveReplCommand(trimmed);
11988
+ if (command === "quit") return;
11989
+ if (command === "help") {
11990
+ printReplHelp(options.engine);
11991
+ continue;
11992
+ }
11993
+ if (command === "clear") {
11994
+ clearReplScreen();
11995
+ continue;
11996
+ }
11997
+ if (command === "unknown") {
11998
+ logger.warn(`Unknown command: ${trimmed}`);
11999
+ continue;
12000
+ }
12001
+ }
12002
+ lines.push(line);
12003
+ if (options.engine === "sql") {
12004
+ if (!isSqlInputComplete(lines.join("\n"))) continue;
12005
+ } else if (!isGraphQLInputComplete(lines.join("\n"))) continue;
12006
+ const statement = getReplStatement(lines, options.engine);
12007
+ lines.length = 0;
12008
+ if (statement.length === 0) continue;
12009
+ try {
12010
+ if (options.engine === "sql") {
12011
+ const result$1 = await execute(statement);
12012
+ if (result$1.engine !== "sql") throw new Error(`Expected sql engine result but got: ${result$1.engine}`);
12013
+ printSqlResult(result$1, { json: options.json });
12014
+ continue;
12015
+ }
12016
+ const result = await execute(statement);
12017
+ if (result.engine !== "gql") throw new Error(`Expected gql engine result but got: ${result.engine}`);
12018
+ printGqlResult(result, { json: options.json });
12019
+ } catch (error) {
12020
+ if (isCLIError(error)) {
12021
+ logger.log(error.format());
12022
+ continue;
12023
+ }
12024
+ if (error instanceof Error) {
12025
+ logger.error(error.message);
12026
+ continue;
12027
+ }
12028
+ logger.error(String(error));
12029
+ }
12030
+ }
12031
+ } finally {
12032
+ rl.close();
12033
+ }
12034
+ }
12035
+ function getReplStatement(lines, engine) {
12036
+ if (engine === "sql") return lines.join("\n").trim();
12037
+ let end = lines.length;
12038
+ while (end > 0 && lines[end - 1].trim() === "") end -= 1;
12039
+ return lines.slice(0, end).join("\n").trim();
12040
+ }
12041
+ function printReplHelp(engine) {
12042
+ logger.log("REPL commands:");
12043
+ logger.log(" \\help, \\h, \\? Show this help");
12044
+ logger.log(" Ctrl+C Clear current input");
12045
+ logger.log(" \\q, \\quit, Ctrl+D Exit REPL");
12046
+ logger.log(" \\clear, \\c Clear the screen");
12047
+ if (engine === "sql") {
12048
+ logger.log("SQL execution: statement ending with ';' runs immediately.");
12049
+ return;
11715
12050
  }
12051
+ logger.log("GraphQL execution: a complete GraphQL document runs immediately.");
11716
12052
  }
11717
12053
  /**
11718
12054
  * Execute SQL query directly.
@@ -11793,28 +12129,73 @@ const queryCommand = defineCommand({
11793
12129
  ...jsonArgs,
11794
12130
  ...deploymentArgs,
11795
12131
  engine: arg(queryEngineSchema, { description: "Query engine (sql or gql)" }),
11796
- query: arg(z.string(), {
12132
+ query: arg(z.string().optional(), {
11797
12133
  alias: "q",
11798
- description: "Query string to execute directly"
12134
+ description: "Query string to execute directly; omit to start REPL mode"
11799
12135
  }),
12136
+ file: arg(z.string().optional(), {
12137
+ alias: "f",
12138
+ description: "Read query string from file; omit to start REPL mode"
12139
+ }),
12140
+ edit: arg(z.boolean().default(false), { description: "Open a temporary file in your editor; omit to start REPL mode" }),
11800
12141
  machineuser: arg(z.string(), {
11801
12142
  alias: "m",
11802
12143
  description: "Machine user name for query execution"
11803
12144
  })
12145
+ }).superRefine((args, ctx) => {
12146
+ if (args.query != null && args.file != null) ctx.addIssue({
12147
+ code: "custom",
12148
+ path: ["file"],
12149
+ message: "Pass either -q/--query or -f/--file, not both."
12150
+ });
12151
+ if (args.edit && args.query != null) ctx.addIssue({
12152
+ code: "custom",
12153
+ path: ["edit"],
12154
+ message: "Pass only one of --edit, -q/--query, or -f/--file."
12155
+ });
12156
+ if (args.edit && args.file != null) ctx.addIssue({
12157
+ code: "custom",
12158
+ path: ["edit"],
12159
+ message: "Pass only one of --edit, -q/--query, or -f/--file."
12160
+ });
11804
12161
  }).strict(),
11805
12162
  run: withCommonArgs(async (args) => {
12163
+ const mode = await resolveQueryCommandInput({
12164
+ query: args.query,
12165
+ file: args.file,
12166
+ edit: args.edit,
12167
+ engine: args.engine
12168
+ });
11806
12169
  const sharedOptions = {
11807
12170
  workspaceId: args["workspace-id"],
11808
12171
  profile: args.profile,
11809
12172
  configPath: args.config,
11810
- query: args.query,
12173
+ engine: args.engine,
11811
12174
  machineUser: args.machineuser
11812
12175
  };
12176
+ if (mode.mode === "abort") {
12177
+ logger.info("Editor closed without a query. Nothing was executed.");
12178
+ return;
12179
+ }
12180
+ if (mode.mode === "repl") {
12181
+ await runRepl({
12182
+ ...sharedOptions,
12183
+ json: args.json
12184
+ });
12185
+ return;
12186
+ }
12187
+ const directQuery = mode.query;
11813
12188
  if (args.engine === "sql") {
11814
- printSqlResult(await querySql(sharedOptions), { json: args.json });
12189
+ printSqlResult(await querySql({
12190
+ ...sharedOptions,
12191
+ query: directQuery
12192
+ }), { json: args.json });
11815
12193
  return;
11816
12194
  }
11817
- printGqlResult(await queryGql(sharedOptions), { json: args.json });
12195
+ printGqlResult(await queryGql({
12196
+ ...sharedOptions,
12197
+ query: directQuery
12198
+ }), { json: args.json });
11818
12199
  })
11819
12200
  });
11820
12201
  function isSQLExecutionResult(value) {
@@ -11894,4 +12275,4 @@ function printGqlResult(result, options = {}) {
11894
12275
 
11895
12276
  //#endregion
11896
12277
  export { getExecutorJob as $, truncateCommand as A, getMigrationDirPath as At, getCommand$1 as B, getNamespacesWithMigrations as Bt, getAppHealth as C, MIGRATE_FILE_NAME as Ct, listCommand$3 as D, createSnapshotFromLocalTypes as Dt, resumeWorkflow as E, compareSnapshots as Et, showCommand as F, loadDiff as Ft, listMachineUsers as G, commonArgs as Gt, getMachineUserToken as H, generateUserTypes as Ht, remove as I, reconstructSnapshotFromMigrations as It, webhookCommand as J, jsonArgs as Jt, generate$1 as K, confirmationArgs as Kt, removeCommand$1 as L, formatDiffSummary as Lt, generateCommand as M, getMigrationFiles as Mt, logBetaWarning as N, getNextMigrationNumber as Nt, listWorkflows as O, formatMigrationNumber as Ot, show as P, isValidMigrationNumber as Pt, listExecutors as Q, listCommand$4 as R, formatMigrationDiff as Rt, listCommand$2 as S, INITIAL_SCHEMA_NUMBER as St, resumeCommand as T, compareLocalTypesWithSnapshot as Tt, tokenCommand as U, apiCall as Ut, getOAuth2Client as V, trnPrefix as Vt, listCommand$5 as W, apiCommand as Wt, triggerExecutor as X, workspaceArgs as Xt, triggerCommand as Y, withCommonArgs as Yt, listCommand$6 as Z, deleteCommand as _, MIGRATION_LABEL_KEY as _t, removeCommand as a, getCommand$2 as at, createWorkspace as b, DB_TYPES_FILE_NAME as bt, listUsers as c, getWorkflowExecution as ct, restoreCommand as d, formatKeyValueTable as dt, jobsCommand as et, restoreWorkspace as f, getCommand$3 as ft, getWorkspace as g, waitForExecution$1 as gt, getCommand as h, executeScript as ht, updateUser as i, startWorkflow as it, generate as j, getMigrationFilePath as jt, truncate as k, getLatestMigrationNumber as kt, inviteCommand as l, listWorkflowExecutions as lt, listWorkspaces as m, apply as mt, queryCommand as n, watchExecutorJob as nt, removeUser as o, getWorkflow as ot, listCommand$1 as p, getExecutor as pt, listWebhookExecutors as q, deploymentArgs as qt, updateCommand as r, startCommand as rt, listCommand as s, executionsCommand as st, query as t, listExecutorJobs as tt, inviteUser as u, functionExecutionStatusToString as ut, deleteWorkspace as v, parseMigrationLabelNumber as vt, healthCommand as w, SCHEMA_FILE_NAME as wt, listApps as x, DIFF_FILE_NAME as xt, createCommand as y, bundleMigrationScript as yt, listOAuth2Clients as z, hasChanges as zt };
11897
- //# sourceMappingURL=query-Bz2oDGhw.mjs.map
12278
+ //# sourceMappingURL=query-CV5n7DRd.mjs.map