@tailor-platform/sdk 2.3.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 (102) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +1 -1
  3. package/dist/{application-F-nl107y.mjs → application-C8a0UJ-z.mjs} +201 -149
  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 +4 -4
  7. package/dist/cli/commands/tailordb/migrate/generate.d.mts +3 -3
  8. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +5 -5
  9. package/dist/cli/commands/tailordb/truncate.d.mts +2 -2
  10. package/dist/cli/lib.mjs +2 -2
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/cli/main.mjs +174 -106
  13. package/dist/cli/main.mjs.map +1 -1
  14. package/dist/cli/ts-hook.mjs +3 -1
  15. package/dist/completion/zsh-worker.zsh +19 -11
  16. package/dist/configure/config/index.d.mts +1 -1
  17. package/dist/configure/index.d.mts +2 -1
  18. package/dist/configure/index.mjs +2 -2
  19. package/dist/configure/index.mjs.map +1 -1
  20. package/dist/configure/services/aigateway/index.d.mts +5 -4
  21. package/dist/configure/services/aigateway/types.d.mts +6 -1
  22. package/dist/configure/services/auth/types.d.mts +2 -2
  23. package/dist/configure/services/tailordb/permission.d.mts +1 -1
  24. package/dist/configure/services/tailordb/schema.d.mts +1 -1
  25. package/dist/configure/services/tailordb/types.d.mts +7 -4
  26. package/dist/configure/types/auth-namespace-name.d.mts +18 -0
  27. package/dist/{crashreport-CINZ5dVN.mjs → crashreport-Cd4TqfZq.mjs} +1 -1
  28. package/dist/{crashreport-CIIfLnoO.mjs → crashreport-Q0pDAlrj.mjs} +8 -4
  29. package/dist/{crashreport-CIIfLnoO.mjs.map → crashreport-Q0pDAlrj.mjs.map} +1 -1
  30. package/dist/{errors-BVb6vYGy.mjs → errors-Bxut5_Sy.mjs} +2 -2
  31. package/dist/{errors-BVb6vYGy.mjs.map → errors-Bxut5_Sy.mjs.map} +1 -1
  32. package/dist/file-BUcmVIH2.mjs.map +1 -1
  33. package/dist/kysely/index.mjs +2 -1
  34. package/dist/kysely/index.mjs.map +1 -1
  35. package/dist/{logger-DSdExfor.mjs → logger-D0bEznPv.mjs} +9 -5
  36. package/dist/{logger-DSdExfor.mjs.map → logger-D0bEznPv.mjs.map} +1 -1
  37. package/dist/package-json-8b0O9TlX.mjs.map +1 -1
  38. package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
  39. package/dist/plugin/builtin/enum-constants/index.mjs +5 -5
  40. package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
  41. package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
  42. package/dist/plugin/builtin/file-utils/index.mjs +14 -11
  43. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  44. package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
  45. package/dist/plugin/builtin/kysely-type/index.mjs +9 -9
  46. package/dist/plugin/builtin/kysely-type/index.mjs.map +1 -1
  47. package/dist/plugin/builtin/seed/index.mjs +1 -1
  48. package/dist/plugin/builtin/seed/seed-type-processor.d.mts +4 -4
  49. package/dist/plugin/get-generated-type.d.mts +4 -4
  50. package/dist/plugin/index.mjs +13 -13
  51. package/dist/plugin/index.mjs.map +1 -1
  52. package/dist/plugin/types.d.mts +2 -2
  53. package/dist/plugin/with-context.d.mts +1 -1
  54. package/dist/{register-ts-hook-ClI226n2.mjs → register-ts-hook-Vqjza10T.mjs} +552 -416
  55. package/dist/register-ts-hook-Vqjza10T.mjs.map +1 -0
  56. package/dist/{repl-editor-BCozyiNq.mjs → repl-editor-F9LotMNx.mjs} +3 -2
  57. package/dist/{repl-editor-BCozyiNq.mjs.map → repl-editor-F9LotMNx.mjs.map} +1 -1
  58. package/dist/runtime/file.d.mts +14 -14
  59. package/dist/{schema--xYWRGfe.mjs → schema-D4TC4BC3.mjs} +4 -3
  60. package/dist/schema-D4TC4BC3.mjs.map +1 -0
  61. package/dist/seed/index.d.mts +4 -4
  62. package/dist/seed/index.mjs +3 -3
  63. package/dist/seed/index.mjs.map +1 -1
  64. package/dist/{seed-xqM9WIHP.mjs → seed-Cibjmtry.mjs} +46 -40
  65. package/dist/seed-Cibjmtry.mjs.map +1 -0
  66. package/dist/{service-C_WpbKHu.mjs → service-2grcAOCJ.mjs} +24 -24
  67. package/dist/service-2grcAOCJ.mjs.map +1 -0
  68. package/dist/service-CaBPNDIZ.mjs +3 -0
  69. package/dist/{service-hZskxZmg.mjs → service-LTfp03ZZ.mjs} +7 -5
  70. package/dist/{service-hZskxZmg.mjs.map → service-LTfp03ZZ.mjs.map} +1 -1
  71. package/dist/types/aigateway.generated.d.mts +2 -2
  72. package/dist/types/auth.generated.d.mts +3 -3
  73. package/dist/types/executor.generated.d.mts +1 -1
  74. package/dist/types/tailordb.generated.d.mts +12 -12
  75. package/dist/utils/test/index.d.mts +4 -4
  76. package/dist/utils/test/index.mjs +4 -4
  77. package/dist/utils/test/index.mjs.map +1 -1
  78. package/dist/vitest/index.mjs +13 -7
  79. package/dist/vitest/index.mjs.map +1 -1
  80. package/dist/vitest/mocks/file.d.mts +1 -1
  81. package/docs/cli/application.md +3 -1
  82. package/docs/cli/function.md +17 -13
  83. package/docs/cli/tailordb.md +30 -30
  84. package/docs/cli/workspace.md +2 -2
  85. package/docs/cli-reference.md +21 -21
  86. package/docs/configuration.md +2 -2
  87. package/docs/migration/v2.md +4 -4
  88. package/docs/migration/v3.md +41 -0
  89. package/docs/quickstart.md +1 -1
  90. package/docs/services/aigateway.md +33 -9
  91. package/docs/services/executor.md +5 -1
  92. package/docs/services/tailordb-migration.md +47 -47
  93. package/docs/services/tailordb.md +7 -7
  94. package/docs/testing.md +1 -1
  95. package/package.json +6 -6
  96. package/dist/application-BZfk4HKm.mjs +0 -3
  97. package/dist/application-F-nl107y.mjs.map +0 -1
  98. package/dist/register-ts-hook-ClI226n2.mjs.map +0 -1
  99. package/dist/schema--xYWRGfe.mjs.map +0 -1
  100. package/dist/seed-xqM9WIHP.mjs.map +0 -1
  101. package/dist/service-C_WpbKHu.mjs.map +0 -1
  102. package/dist/service-D1RCdzIL.mjs +0 -3
@@ -1,5 +1,6 @@
1
- import { AIGatewayInput } from "../../../types/aigateway.generated.mjs";
2
- import { AIGatewayConfig, AIGatewayDefinitionBrand } from "./types.mjs";
1
+ import { AuthNamespaceName } from "../../types/auth-namespace-name.mjs";
2
+ import { AIGatewayConfig, AIGatewayDefinitionBrand, AIGatewayServiceInput } from "./types.mjs";
3
+ import "../../index.mjs";
3
4
  //#region src/configure/services/aigateway/index.d.ts
4
5
  /**
5
6
  * Define an AI Gateway configuration for the Tailor SDK.
@@ -7,10 +8,10 @@ import { AIGatewayConfig, AIGatewayDefinitionBrand } from "./types.mjs";
7
8
  * @param config - AI Gateway configuration
8
9
  * @returns Defined AI Gateway
9
10
  */
10
- declare function defineAIGateway(name: string, config: Omit<AIGatewayInput, "name">): {
11
+ declare function defineAIGateway(name: string, config: Omit<AIGatewayServiceInput, "name">): {
11
12
  readonly name: string;
12
13
  readonly cors?: string[] | undefined;
13
- readonly authNamespace: string;
14
+ readonly authNamespace?: AuthNamespaceName;
14
15
  } & AIGatewayDefinitionBrand;
15
16
  //#endregion
16
17
  export { type AIGatewayConfig, defineAIGateway };
@@ -1,10 +1,15 @@
1
+ import { AuthNamespaceName } from "../../types/auth-namespace-name.mjs";
1
2
  import { AIGatewayInput } from "../../../types/aigateway.generated.mjs";
2
3
  //#region src/configure/services/aigateway/types.d.ts
3
4
  declare const aiGatewayDefinitionBrand: unique symbol;
4
5
  type AIGatewayDefinitionBrand = {
5
6
  readonly [aiGatewayDefinitionBrand]: true;
6
7
  };
8
+ /** AI Gateway configuration accepted by `defineAIGateway()`. */
9
+ type AIGatewayServiceInput = Omit<AIGatewayInput, "authNamespace"> & {
10
+ authNamespace?: AuthNamespaceName;
11
+ };
7
12
  /** Type accepted by `AppConfig.aiGateways`. Only values returned by `defineAIGateway()` satisfy this. */
8
13
  type AIGatewayConfig = AIGatewayInput & AIGatewayDefinitionBrand;
9
14
  //#endregion
10
- export { AIGatewayConfig, AIGatewayDefinitionBrand };
15
+ export { AIGatewayConfig, AIGatewayDefinitionBrand, AIGatewayServiceInput };
@@ -49,10 +49,10 @@ type AttributeListToTuple<User extends TailorDBInstance, AttributeList extends r
49
49
  type SelectedAttributeKeys<User extends TailorDBInstance, Attributes extends UserAttributes<User>> = Extract<{ [K in keyof Attributes]-?: undefined extends Attributes[K] ? never : K; }[keyof Attributes], UserAttributeKey<User>>;
50
50
  type UserProfile<User extends TailorDBInstance, Attributes extends UserAttributes<User>, AttributeList extends UserAttributeListKey<User>[]> = {
51
51
  /**
52
- * TailorDB namespace where the user type is defined.
52
+ * TailorDB namespace where the user table is defined.
53
53
  *
54
54
  * Usually auto-resolved, so you don't need to specify this.
55
- * Required only when multiple TailorDBs exist and the type is in an external TailorDB.
55
+ * Required only when multiple TailorDBs exist and the table is in an external TailorDB.
56
56
  */
57
57
  namespace?: string;
58
58
  type: User;
@@ -2,7 +2,7 @@ import { InferredAttributes } from "../../../runtime/types.mjs";
2
2
  import { UserBooleanArrayOperand, UserBooleanOperand, UserStringArrayOperand, UserStringOperand } from "../../types/permission-operand.types.mjs";
3
3
  //#region src/configure/services/tailordb/permission.d.ts
4
4
  /**
5
- * Record-level permission configuration for a TailorDB type.
5
+ * Record-level permission configuration for a TailorDB table.
6
6
  * Defines create, read, update, and delete permissions.
7
7
  *
8
8
  * Prefer object format with explicit `conditions` and `permit` for readability.
@@ -209,7 +209,7 @@ interface TailorDBField<Defined extends DefinedDBFieldMetadata = DefinedDBFieldM
209
209
  */
210
210
  description: DBFieldDescriptionMethod<Defined, Output, Nested>;
211
211
  /**
212
- * Define a relation to another type.
212
+ * Define a relation to another table.
213
213
  */
214
214
  relation: DBFieldRelationMethod<Defined, Output, Nested>;
215
215
  /**
@@ -185,17 +185,20 @@ type ExcludeDefaultedDBFields<T extends Record<string, TailorAnyDBField>> = { [K
185
185
  default: true;
186
186
  }, any> ? never : T[K]; };
187
187
  interface TypeFeatures {
188
+ /** Custom plural form of the table name for GraphQL */
188
189
  pluralForm?: string;
190
+ /** Enable aggregation queries for this table */
189
191
  aggregation?: true;
192
+ /** Enable bulk upsert mutation for this table */
190
193
  bulkUpsert?: true;
191
- /** Configure GraphQL operations for this type. Use "query" for read-only mode, or an object for granular control. */
194
+ /** Configure GraphQL operations for this table. Use "query" for read-only mode, or an object for granular control. */
192
195
  gqlOperations?: GqlOperationsConfig;
193
196
  /**
194
- * Enable publishing events for this type.
197
+ * Enable publishing events for this table.
195
198
  * When enabled, record creation/update/deletion events are published.
196
- * If not specified, this is automatically set to true when an executor uses this type
199
+ * If not specified, this is automatically set to true when an executor uses this table
197
200
  * with recordCreated/recordUpdated/recordDeleted triggers. If explicitly set to false
198
- * while an executor uses this type, an error will be thrown during apply.
201
+ * while an executor uses this table, an error will be thrown during apply.
199
202
  */
200
203
  publishEvents?: boolean;
201
204
  }
@@ -0,0 +1,18 @@
1
+ //#region src/configure/types/auth-namespace-name.d.ts
2
+ interface AuthNamespaceNameRegistry {}
3
+ /**
4
+ * Auth namespace name.
5
+ *
6
+ * When `tailor.d.ts` is generated (via `tailor deploy`/`generate`), this is narrowed
7
+ * to the application's own auth namespace name (from `defineAuth()`, local or
8
+ * external) — the common case, since an `AIGatewayConfig` usually authenticates
9
+ * against its own app's auth. When no auth namespace is registered yet, falls back
10
+ * to `string` to avoid blocking editing before the first generate run.
11
+ *
12
+ * To authenticate against an auth namespace owned by a different application in the
13
+ * workspace, extend the registry: `declare module "@tailor-platform/sdk" { interface
14
+ * AuthNamespaceNameRegistry { "other-app-auth": true } }`.
15
+ */
16
+ type AuthNamespaceName = keyof AuthNamespaceNameRegistry extends never ? string : keyof AuthNamespaceNameRegistry & string;
17
+ //#endregion
18
+ export { AuthNamespaceName, AuthNamespaceNameRegistry };
@@ -1,3 +1,3 @@
1
- import { n as reportCrash, t as initCrashReporting } from "./crashreport-CIIfLnoO.mjs";
1
+ import { n as reportCrash, t as initCrashReporting } from "./crashreport-Q0pDAlrj.mjs";
2
2
 
3
3
  export { reportCrash };
@@ -1,4 +1,4 @@
1
- import { n as logger } from "./logger-DSdExfor.mjs";
1
+ import { n as logger } from "./logger-D0bEznPv.mjs";
2
2
  import { t as readPackageJson } from "./package-json-8b0O9TlX.mjs";
3
3
  import { n as userAgentFromVersion } from "./user-agent-Bgsszb5I.mjs";
4
4
  import { r as writeSecretFile, t as ensureSecretDir } from "./secret-file-C3J4Gy8r.mjs";
@@ -106,7 +106,8 @@ function writeCrashReport(report, dir) {
106
106
  ensureSecretDir(dir);
107
107
  const filename = generateFilename(report);
108
108
  const filePath = path.join(dir, filename);
109
- writeSecretFile(filePath, formatCrashReport(report));
109
+ const content = formatCrashReport(report);
110
+ writeSecretFile(filePath, content);
110
111
  cleanupOldFiles(dir);
111
112
  return filePath;
112
113
  } catch {
@@ -396,7 +397,10 @@ async function reportCrash(error, errorType) {
396
397
  ` tailor crashreport send --file "${filePath}"`
397
398
  ].join("\n"));
398
399
  }
399
- if (config.remoteEnabled) await sendCrashReport(report, userAgentFromVersion(sdkVersion));
400
+ if (config.remoteEnabled) {
401
+ const ua = userAgentFromVersion(sdkVersion);
402
+ await sendCrashReport(report, ua);
403
+ }
400
404
  } catch {}
401
405
  }
402
406
  /**
@@ -420,4 +424,4 @@ function initCrashReporting() {
420
424
 
421
425
  //#endregion
422
426
  export { JSON_FOOTER_MARKER as a, CRASH_LOG_EXTENSION as i, reportCrash as n, parseCrashReportConfig as o, sendCrashReport as r, initCrashReporting as t };
423
- //# sourceMappingURL=crashreport-CIIfLnoO.mjs.map
427
+ //# sourceMappingURL=crashreport-Q0pDAlrj.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"crashreport-CIIfLnoO.mjs","names":[],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"#/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .toSorted()\n .toReversed();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(parseCommand()),\n argv: sanitizeArgv(process.argv),\n errorName,\n errorMessage: sanitizeMessage(rawMessage),\n stackTrace: sanitizeStackTrace(rawStack),\n errorType,\n userId: currentUser?.id ?? null,\n userEmail: currentUser?.email ?? null,\n };\n}\n\ntype CurrentUser = {\n id: string;\n email: string | null;\n};\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user ID and email, or null if unavailable\n */\nfunction readCurrentUser(): CurrentUser | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as {\n current_user?: string | null;\n users?: Record<string, { email?: unknown } | undefined>;\n };\n // parseYAML returns null for empty documents\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n const currentUser = raw?.current_user ?? null;\n if (!currentUser) return null;\n const email = raw.users?.[currentUser]?.email;\n return {\n id: currentUser,\n email: typeof email === \"string\" ? email : legacyEmail(currentUser),\n };\n } catch {\n return null;\n }\n}\n\nfunction legacyEmail(user: string): string | null {\n return user.includes(\"@\") ? user : null;\n}\n","import { logger } from \"#/cli/shared/logger\";\nimport { readPackageJson } from \"#/cli/shared/package-json\";\nimport { userAgentFromVersion } from \"#/cli/shared/user-agent\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n const ua = userAgentFromVersion(sdkVersion);\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"crashreport-Q0pDAlrj.mjs","names":[],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"#/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .toSorted()\n .toReversed();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(parseCommand()),\n argv: sanitizeArgv(process.argv),\n errorName,\n errorMessage: sanitizeMessage(rawMessage),\n stackTrace: sanitizeStackTrace(rawStack),\n errorType,\n userId: currentUser?.id ?? null,\n userEmail: currentUser?.email ?? null,\n };\n}\n\ntype CurrentUser = {\n id: string;\n email: string | null;\n};\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user ID and email, or null if unavailable\n */\nfunction readCurrentUser(): CurrentUser | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as {\n current_user?: string | null;\n users?: Record<string, { email?: unknown } | undefined>;\n };\n // parseYAML returns null for empty documents\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n const currentUser = raw?.current_user ?? null;\n if (!currentUser) return null;\n const email = raw.users?.[currentUser]?.email;\n return {\n id: currentUser,\n email: typeof email === \"string\" ? email : legacyEmail(currentUser),\n };\n } catch {\n return null;\n }\n}\n\nfunction legacyEmail(user: string): string | null {\n return user.includes(\"@\") ? user : null;\n}\n","import { logger } from \"#/cli/shared/logger\";\nimport { readPackageJson } from \"#/cli/shared/package-json\";\nimport { userAgentFromVersion } from \"#/cli/shared/user-agent\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n const ua = userAgentFromVersion(sdkVersion);\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { r as styles } from "./logger-DSdExfor.mjs";
1
+ import { r as styles } from "./logger-D0bEznPv.mjs";
2
2
 
3
3
  //#region src/cli/shared/errors.ts
4
4
  function shellQuote(value) {
@@ -86,4 +86,4 @@ function typeOnlyImportHint(error) {
86
86
 
87
87
  //#endregion
88
88
  export { typeOnlyImportHint as a, isCLIError as i, formatCopyableCommand as n, formatNextAction as r, createCLIError as t };
89
- //# sourceMappingURL=errors-BVb6vYGy.mjs.map
89
+ //# sourceMappingURL=errors-Bxut5_Sy.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors-BVb6vYGy.mjs","names":[],"sources":["../src/cli/shared/errors.ts"],"sourcesContent":["import { styles } from \"./logger\";\n\n/**\n * Options for creating a CLI error\n */\nexport interface CLIErrorOptions {\n message: string;\n details?: string;\n suggestion?: string;\n command?: string;\n code?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, unknown>>;\n}\n\nexport interface CLIErrorNextAction {\n /** Executable name, such as `tailor`. */\n command: string;\n /** Arguments passed directly to the executable. */\n args: readonly string[];\n}\n\n/**\n * CLI error interface with formatted output\n */\nexport interface CLIError extends Error {\n readonly code?: string;\n readonly details?: string;\n readonly suggestion?: string;\n readonly command?: string;\n readonly next?: CLIErrorNextAction;\n readonly context?: Readonly<Record<string, unknown>>;\n format(): string;\n}\n\ntype CLIErrorInternal = Error & {\n code?: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, unknown>>;\n format(): string;\n};\n\nfunction shellQuote(value: string): string {\n if (process.platform === \"win32\") {\n if (/^[A-Za-z0-9_./:=@+\\\\-]+$/.test(value)) return value;\n return `\"${value.replaceAll('\"', '\\\\\"')}\"`;\n }\n if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction needsArgvRendering(argv: readonly string[]): boolean {\n // cmd.exe/PowerShell expand %, $, and ! even inside double quotes, so no\n // quoting can keep such values literal on Windows.\n return process.platform === \"win32\" && argv.some((value) => /[%$!]/.test(value));\n}\n\n/**\n * Render an argv array as a copyable command line for the current platform's shell\n * @param {readonly string[]} argv - Executable name followed by its arguments\n * @returns {string} A shell-quoted command line, or an `argv [...]` JSON rendering when the platform shell cannot keep a value literal\n */\nexport function formatCopyableCommand(argv: readonly string[]): string {\n if (needsArgvRendering(argv)) {\n return `argv ${JSON.stringify(argv)}`;\n }\n return argv.map(shellQuote).join(\" \");\n}\n\n/**\n * Format an executable and argv as a shell-safe user-facing command.\n * @param next - Executable and arguments to format\n * @returns Shell command, or an argv representation when shell quoting is unsafe\n */\nexport function formatNextAction(next: CLIErrorNextAction): string {\n const argv = [next.command, ...next.args];\n const rendered = formatCopyableCommand(argv);\n return needsArgvRendering(argv) ? `with ${rendered}` : `\\`${rendered}\\``;\n}\n\n/**\n * Format CLI error for output\n * @param error - CLIError instance to format\n * @returns Formatted error message\n */\nfunction formatError(error: CLIError): string {\n const parts: string[] = [\n styles.error(`Error${error.code ? ` [${error.code}]` : \"\"}: ${error.message}`),\n ];\n\n if (error.details) {\n parts.push(`\\n ${styles.dim(\"Details:\")} ${error.details}`);\n }\n\n if (error.suggestion) {\n parts.push(`\\n ${styles.info(\"Suggestion:\")} ${error.suggestion}`);\n }\n\n if (error.command) {\n parts.push(\n `\\n ${styles.dim(\"Help:\")} Run \\`tailor ${error.command} --help\\` for usage information.`,\n );\n }\n\n if (error.next) {\n parts.push(`\\n ${styles.info(\"Next:\")} Run ${formatNextAction(error.next)}.`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Create a CLI error with formatted output\n * @param options - Options to construct a CLIError\n * @returns Constructed CLIError instance\n */\nfunction createCLIError(options: CLIErrorOptions): CLIError {\n const error = new Error(options.message) as CLIErrorInternal;\n error.name = \"CLIError\";\n error.code = options.code;\n error.details = options.details;\n error.suggestion = options.suggestion;\n error.command = options.command;\n error.next = options.next;\n error.context = options.context;\n error.format = () => formatError(error);\n return error;\n}\n\n/**\n * Type guard to check if an error is a CLIError\n * @param error - Error to check\n * @returns True if the error is a CLIError\n */\nexport function isCLIError(error: unknown): error is CLIError {\n return error instanceof Error && error.name === \"CLIError\";\n}\n\nconst MISSING_NAMED_EXPORT_PATTERN = /does not provide an export named '(?!default')([^']+)'/;\n\n/**\n * Suggest `import type` when importing user code fails on a missing named\n * export. The CLI strips types from each file in isolation, so a type-only\n * export does not exist at runtime and a plain import of it fails to link.\n * @param error - Error thrown while importing user modules\n * @returns Suggestion text, or undefined when the error is not that failure\n */\nexport function typeOnlyImportHint(error: unknown): string | undefined {\n if (!(error instanceof SyntaxError)) return undefined;\n const name = MISSING_NAMED_EXPORT_PATTERN.exec(error.message)?.[1];\n if (!name) return undefined;\n return (\n `If '${name}' is a type, import it with \\`import type\\` (or the inline \\`type\\` modifier). ` +\n \"The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. \" +\n 'Set \"verbatimModuleSyntax\": true in tsconfig.json to catch this at typecheck.'\n );\n}\n\n// Re-export createCLIError as CLIError for backward compatibility\nexport { createCLIError as CLIError };\n"],"mappings":";;;AA6CA,SAAS,WAAW,OAAuB;CACzC,IAAI,QAAQ,aAAa,SAAS;EAChC,IAAI,2BAA2B,KAAK,KAAK,GAAG,OAAO;EACnD,OAAO,IAAI,MAAM,WAAW,MAAK,MAAK,EAAE;CAC1C;CACA,IAAI,yBAAyB,KAAK,KAAK,GAAG,OAAO;CACjD,OAAO,IAAI,MAAM,WAAW,KAAK,OAAO,EAAE;AAC5C;AAEA,SAAS,mBAAmB,MAAkC;CAG5D,OAAO,QAAQ,aAAa,WAAW,KAAK,MAAM,UAAU,QAAQ,KAAK,KAAK,CAAC;AACjF;;;;;;AAOA,SAAgB,sBAAsB,MAAiC;CACrE,IAAI,mBAAmB,IAAI,GACzB,OAAO,QAAQ,KAAK,UAAU,IAAI;CAEpC,OAAO,KAAK,IAAI,UAAU,CAAC,CAAC,KAAK,GAAG;AACtC;;;;;;AAOA,SAAgB,iBAAiB,MAAkC;CACjE,MAAM,OAAO,CAAC,KAAK,SAAS,GAAG,KAAK,IAAI;CACxC,MAAM,WAAW,sBAAsB,IAAI;CAC3C,OAAO,mBAAmB,IAAI,IAAI,QAAQ,aAAa,KAAK,SAAS;AACvE;;;;;;AAOA,SAAS,YAAY,OAAyB;CAC5C,MAAM,QAAkB,CACtB,OAAO,MAAM,QAAQ,MAAM,OAAO,KAAK,MAAM,KAAK,KAAK,GAAG,IAAI,MAAM,SAAS,CAC/E;CAEA,IAAI,MAAM,SACR,MAAM,KAAK,OAAO,OAAO,IAAI,UAAU,EAAE,GAAG,MAAM,SAAS;CAG7D,IAAI,MAAM,YACR,MAAM,KAAK,OAAO,OAAO,KAAK,aAAa,EAAE,GAAG,MAAM,YAAY;CAGpE,IAAI,MAAM,SACR,MAAM,KACJ,OAAO,OAAO,IAAI,OAAO,EAAE,gBAAgB,MAAM,QAAQ,iCAC3D;CAGF,IAAI,MAAM,MACR,MAAM,KAAK,OAAO,OAAO,KAAK,OAAO,EAAE,OAAO,iBAAiB,MAAM,IAAI,EAAE,EAAE;CAG/E,OAAO,MAAM,KAAK,EAAE;AACtB;;;;;;AAOA,SAAS,eAAe,SAAoC;CAC1D,MAAM,QAAQ,IAAI,MAAM,QAAQ,OAAO;CACvC,MAAM,OAAO;CACb,MAAM,OAAO,QAAQ;CACrB,MAAM,UAAU,QAAQ;CACxB,MAAM,aAAa,QAAQ;CAC3B,MAAM,UAAU,QAAQ;CACxB,MAAM,OAAO,QAAQ;CACrB,MAAM,UAAU,QAAQ;CACxB,MAAM,eAAe,YAAY,KAAK;CACtC,OAAO;AACT;;;;;;AAOA,SAAgB,WAAW,OAAmC;CAC5D,OAAO,iBAAiB,SAAS,MAAM,SAAS;AAClD;AAEA,MAAM,+BAA+B;;;;;;;;AASrC,SAAgB,mBAAmB,OAAoC;CACrE,IAAI,EAAE,iBAAiB,cAAc,OAAO;CAC5C,MAAM,OAAO,6BAA6B,KAAK,MAAM,OAAO,CAAC,GAAG;CAChE,IAAI,CAAC,MAAM,OAAO;CAClB,OACE,OAAO,KAAK;AAIhB"}
1
+ {"version":3,"file":"errors-Bxut5_Sy.mjs","names":[],"sources":["../src/cli/shared/errors.ts"],"sourcesContent":["import { styles } from \"./logger\";\n\n/**\n * Options for creating a CLI error\n */\nexport interface CLIErrorOptions {\n message: string;\n details?: string;\n suggestion?: string;\n command?: string;\n code?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, unknown>>;\n}\n\nexport interface CLIErrorNextAction {\n /** Executable name, such as `tailor`. */\n command: string;\n /** Arguments passed directly to the executable. */\n args: readonly string[];\n}\n\n/**\n * CLI error interface with formatted output\n */\nexport interface CLIError extends Error {\n readonly code?: string;\n readonly details?: string;\n readonly suggestion?: string;\n readonly command?: string;\n readonly next?: CLIErrorNextAction;\n readonly context?: Readonly<Record<string, unknown>>;\n format(): string;\n}\n\ntype CLIErrorInternal = Error & {\n code?: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, unknown>>;\n format(): string;\n};\n\nfunction shellQuote(value: string): string {\n if (process.platform === \"win32\") {\n if (/^[A-Za-z0-9_./:=@+\\\\-]+$/.test(value)) return value;\n return `\"${value.replaceAll('\"', '\\\\\"')}\"`;\n }\n if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction needsArgvRendering(argv: readonly string[]): boolean {\n // cmd.exe/PowerShell expand %, $, and ! even inside double quotes, so no\n // quoting can keep such values literal on Windows.\n return process.platform === \"win32\" && argv.some((value) => /[%$!]/.test(value));\n}\n\n/**\n * Render an argv array as a copyable command line for the current platform's shell\n * @param {readonly string[]} argv - Executable name followed by its arguments\n * @returns {string} A shell-quoted command line, or an `argv [...]` JSON rendering when the platform shell cannot keep a value literal\n */\nexport function formatCopyableCommand(argv: readonly string[]): string {\n if (needsArgvRendering(argv)) {\n return `argv ${JSON.stringify(argv)}`;\n }\n return argv.map(shellQuote).join(\" \");\n}\n\n/**\n * Format an executable and argv as a shell-safe user-facing command.\n * @param next - Executable and arguments to format\n * @returns Shell command, or an argv representation when shell quoting is unsafe\n */\nexport function formatNextAction(next: CLIErrorNextAction): string {\n const argv = [next.command, ...next.args];\n const rendered = formatCopyableCommand(argv);\n return needsArgvRendering(argv) ? `with ${rendered}` : `\\`${rendered}\\``;\n}\n\n/**\n * Format CLI error for output\n * @param error - CLIError instance to format\n * @returns Formatted error message\n */\nfunction formatError(error: CLIError): string {\n const parts: string[] = [\n styles.error(`Error${error.code ? ` [${error.code}]` : \"\"}: ${error.message}`),\n ];\n\n if (error.details) {\n parts.push(`\\n ${styles.dim(\"Details:\")} ${error.details}`);\n }\n\n if (error.suggestion) {\n parts.push(`\\n ${styles.info(\"Suggestion:\")} ${error.suggestion}`);\n }\n\n if (error.command) {\n parts.push(\n `\\n ${styles.dim(\"Help:\")} Run \\`tailor ${error.command} --help\\` for usage information.`,\n );\n }\n\n if (error.next) {\n parts.push(`\\n ${styles.info(\"Next:\")} Run ${formatNextAction(error.next)}.`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Create a CLI error with formatted output\n * @param options - Options to construct a CLIError\n * @returns Constructed CLIError instance\n */\nfunction createCLIError(options: CLIErrorOptions): CLIError {\n const error = new Error(options.message) as CLIErrorInternal;\n error.name = \"CLIError\";\n error.code = options.code;\n error.details = options.details;\n error.suggestion = options.suggestion;\n error.command = options.command;\n error.next = options.next;\n error.context = options.context;\n error.format = () => formatError(error);\n return error;\n}\n\n/**\n * Type guard to check if an error is a CLIError\n * @param error - Error to check\n * @returns True if the error is a CLIError\n */\nexport function isCLIError(error: unknown): error is CLIError {\n return error instanceof Error && error.name === \"CLIError\";\n}\n\nconst MISSING_NAMED_EXPORT_PATTERN = /does not provide an export named '(?!default')([^']+)'/;\n\n/**\n * Suggest `import type` when importing user code fails on a missing named\n * export. The CLI strips types from each file in isolation, so a type-only\n * export does not exist at runtime and a plain import of it fails to link.\n * @param error - Error thrown while importing user modules\n * @returns Suggestion text, or undefined when the error is not that failure\n */\nexport function typeOnlyImportHint(error: unknown): string | undefined {\n if (!(error instanceof SyntaxError)) return undefined;\n const name = MISSING_NAMED_EXPORT_PATTERN.exec(error.message)?.[1];\n if (!name) return undefined;\n return (\n `If '${name}' is a type, import it with \\`import type\\` (or the inline \\`type\\` modifier). ` +\n \"The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. \" +\n 'Set \"verbatimModuleSyntax\": true in tsconfig.json to catch this at typecheck.'\n );\n}\n\n// Re-export createCLIError as CLIError for backward compatibility\nexport { createCLIError as CLIError };\n"],"mappings":";;;AA6CA,SAAS,WAAW,OAAuB;CACzC,IAAI,QAAQ,aAAa,SAAS;EAChC,IAAI,2BAA2B,KAAK,KAAK,GAAG,OAAO;EACnD,OAAO,IAAI,MAAM,WAAW,MAAK,MAAK,EAAE;CAC1C;CACA,IAAI,yBAAyB,KAAK,KAAK,GAAG,OAAO;CACjD,OAAO,IAAI,MAAM,WAAW,KAAK,OAAO,EAAE;AAC5C;AAEA,SAAS,mBAAmB,MAAkC;CAG5D,OAAO,QAAQ,aAAa,WAAW,KAAK,MAAM,UAAU,QAAQ,KAAK,KAAK,CAAC;AACjF;;;;;;AAOA,SAAgB,sBAAsB,MAAiC;CACrE,IAAI,mBAAmB,IAAI,GACzB,OAAO,QAAQ,KAAK,UAAU,IAAI;CAEpC,OAAO,KAAK,IAAI,UAAU,CAAC,CAAC,KAAK,GAAG;AACtC;;;;;;AAOA,SAAgB,iBAAiB,MAAkC;CACjE,MAAM,OAAO,CAAC,KAAK,SAAS,GAAG,KAAK,IAAI;CACxC,MAAM,WAAW,sBAAsB,IAAI;CAC3C,OAAO,mBAAmB,IAAI,IAAI,QAAQ,aAAa,KAAK,SAAS;AACvE;;;;;;AAOA,SAAS,YAAY,OAAyB;CAC5C,MAAM,QAAkB,CACtB,OAAO,MAAM,QAAQ,MAAM,OAAO,KAAK,MAAM,KAAK,KAAK,GAAG,IAAI,MAAM,SAAS,CAC/E;CAEA,IAAI,MAAM,SACR,MAAM,KAAK,OAAO,OAAO,IAAI,UAAU,EAAE,GAAG,MAAM,SAAS;CAG7D,IAAI,MAAM,YACR,MAAM,KAAK,OAAO,OAAO,KAAK,aAAa,EAAE,GAAG,MAAM,YAAY;CAGpE,IAAI,MAAM,SACR,MAAM,KACJ,OAAO,OAAO,IAAI,OAAO,EAAE,gBAAgB,MAAM,QAAQ,iCAC3D;CAGF,IAAI,MAAM,MACR,MAAM,KAAK,OAAO,OAAO,KAAK,OAAO,EAAE,OAAO,iBAAiB,MAAM,IAAI,EAAE,EAAE;CAG/E,OAAO,MAAM,KAAK,EAAE;AACtB;;;;;;AAOA,SAAS,eAAe,SAAoC;CAC1D,MAAM,QAAQ,IAAI,MAAM,QAAQ,OAAO;CACvC,MAAM,OAAO;CACb,MAAM,OAAO,QAAQ;CACrB,MAAM,UAAU,QAAQ;CACxB,MAAM,aAAa,QAAQ;CAC3B,MAAM,UAAU,QAAQ;CACxB,MAAM,OAAO,QAAQ;CACrB,MAAM,UAAU,QAAQ;CACxB,MAAM,eAAe,YAAY,KAAK;CACtC,OAAO;AACT;;;;;;AAOA,SAAgB,WAAW,OAAmC;CAC5D,OAAO,iBAAiB,SAAS,MAAM,SAAS;AAClD;AAEA,MAAM,+BAA+B;;;;;;;;AASrC,SAAgB,mBAAmB,OAAoC;CACrE,IAAI,EAAE,iBAAiB,cAAc,OAAO;CAC5C,MAAM,OAAO,6BAA6B,KAAK,MAAM,OAAO,CAAC,GAAG;CAChE,IAAI,CAAC,MAAM,OAAO;CAClB,OACE,OAAO,KAAK;AAIhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-BUcmVIH2.mjs","names":[],"sources":["../src/runtime/file.ts"],"sourcesContent":["/**\n * TailorDB file (BLOB) utilities.\n *\n * Thin typed wrapper around the platform-provided `tailordb.file` runtime API.\n * At runtime this delegates to `globalThis.tailordb.file`. Use `mockFile` from\n * `@tailor-platform/sdk/vitest` to mock these calls in unit tests.\n * @example\n * import { file } from \"@tailor-platform/sdk/runtime\";\n *\n * const { metadata } = await file.upload(\n * \"my-namespace\",\n * \"Document\",\n * \"attachment\",\n * recordId,\n * bytes,\n * );\n */\n\n/** Upload response metadata. */\nexport interface UploadMetadata {\n fileSize: number;\n sha256sum: string;\n}\n\n/** Download response metadata. */\nexport interface DownloadMetadata {\n contentType: string;\n fileSize: number;\n sha256sum: string;\n lastUploadedAt: string;\n}\n\n/** File metadata (for {@link TailorDBFileAPI.getMetadata}). */\nexport interface FileMetadata {\n contentType: string;\n fileSize: number;\n sha256sum: string;\n urlPath: string;\n lastUploadedAt?: string;\n}\n\n/** Upload options. */\nexport interface FileUploadOptions {\n contentType?: string;\n}\n\n/** Upload stream options. */\nexport interface FileUploadStreamOptions {\n contentType?: string;\n fileSize?: number;\n}\n\n/** Upload response. */\nexport interface FileUploadResponse {\n metadata: UploadMetadata;\n}\n\n/** Download response. */\nexport interface FileDownloadResponse {\n data: Uint8Array;\n metadata: DownloadMetadata;\n}\n\n/** Download-as-Base64 response. */\nexport interface FileDownloadAsBase64Response {\n data: string;\n metadata: DownloadMetadata;\n}\n\n/** Download stream response. */\nexport interface FileDownloadStreamResponse {\n body: ReadableStream<Uint8Array>;\n metadata: DownloadMetadata;\n}\n\n/** Error code emitted by {@link TailorDBFileError}. */\nexport type TailorDBFileErrorCode =\n | \"INVALID_PARAMS\"\n | \"INVALID_DATA_TYPE\"\n | \"OPERATION_FAILED\"\n | \"DELETE_FAILED\"\n | \"STREAM_OPEN_FAILED\"\n | \"STREAM_READ_ERROR\"\n | \"STREAM_ERROR\"\n | \"FILE_TOO_LARGE\";\n\n/**\n * Type-only shape of the `TailorDBFileError` runtime class. The class itself\n * is provided by the platform runtime (and by `injectMocks` in tests); this\n * interface mirrors it so callers can `import type { TailorDBFileError }` from\n * the wrapper module without depending on any ambient declaration.\n */\nexport interface TailorDBFileError extends Error {\n name: \"TailorDBFileError\";\n code?: TailorDBFileErrorCode;\n cause?: unknown;\n}\n\n/**\n * Platform API surface for `tailordb.file`. Describes the shape the platform\n * runtime injects on `globalThis.tailordb.file`.\n */\nexport interface TailorDBFileAPI {\n /**\n * Upload a file to TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @param data - File contents\n * @param options - Upload options (e.g. `contentType`)\n * @returns Upload response containing the file metadata\n */\n upload(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n data: string | ArrayBuffer | Uint8Array | number[],\n options?: FileUploadOptions,\n ): Promise<FileUploadResponse>;\n\n /**\n * Download a file from TailorDB.\n *\n * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n * exceeds 10MB — use {@link downloadStream} for large files.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Bytes and metadata for the file\n */\n download(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadResponse>;\n\n /**\n * Download a file from TailorDB as a Base64-encoded string.\n *\n * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n * exceeds 10MB — use {@link downloadStream} for large files.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Base64-encoded contents and metadata for the file\n */\n downloadAsBase64(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadAsBase64Response>;\n\n /**\n * Delete a file from TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Resolves once the file has been deleted\n */\n delete(namespace: string, typeName: string, fieldName: string, recordId: string): Promise<void>;\n\n /**\n * Get file metadata from TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Metadata for the stored file\n */\n getMetadata(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileMetadata>;\n\n /**\n * Download a file as a ReadableStream.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns ReadableStream body and metadata for the file\n */\n downloadStream(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadStreamResponse>;\n\n /**\n * Upload a file using a ReadableStream.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @param readableStream - ReadableStream providing the file data\n * @param options - Upload stream options (e.g. `contentType`, `fileSize`)\n * @returns Upload response containing the file metadata\n */\n uploadStream(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n readableStream: ReadableStream<Uint8Array | ArrayBuffer>,\n options?: FileUploadStreamOptions,\n ): Promise<FileUploadResponse>;\n}\n\nconst api = (): TailorDBFileAPI =>\n (globalThis as unknown as { tailordb: { file: TailorDBFileAPI } }).tailordb.file;\n\n/**\n * See {@link TailorDBFileAPI.upload}.\n * @param args - Forwarded to {@link TailorDBFileAPI.upload}\n * @returns Upload response containing the file metadata\n */\nconst upload: TailorDBFileAPI[\"upload\"] = (...args) => api().upload(...args);\n\n/**\n * See {@link TailorDBFileAPI.download}.\n * @param args - Forwarded to {@link TailorDBFileAPI.download}\n * @returns Bytes and metadata for the file\n */\nconst download: TailorDBFileAPI[\"download\"] = (...args) => api().download(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadAsBase64}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadAsBase64}\n * @returns Base64-encoded contents and metadata for the file\n */\nconst downloadAsBase64: TailorDBFileAPI[\"downloadAsBase64\"] = (...args) =>\n api().downloadAsBase64(...args);\n\n/**\n * See {@link TailorDBFileAPI.delete}.\n * @param args - Forwarded to {@link TailorDBFileAPI.delete}\n * @returns Resolves once the file has been deleted\n */\nconst deleteFile: TailorDBFileAPI[\"delete\"] = (...args) => api().delete(...args);\n\n/**\n * See {@link TailorDBFileAPI.getMetadata}.\n * @param args - Forwarded to {@link TailorDBFileAPI.getMetadata}\n * @returns Metadata for the stored file\n */\nconst getMetadata: TailorDBFileAPI[\"getMetadata\"] = (...args) => api().getMetadata(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadStream}\n * @returns ReadableStream body and metadata for the file\n */\nconst downloadStream: TailorDBFileAPI[\"downloadStream\"] = (...args) =>\n api().downloadStream(...args);\n\n/**\n * See {@link TailorDBFileAPI.uploadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.uploadStream}\n * @returns Upload response containing the file metadata\n */\nconst uploadStream: TailorDBFileAPI[\"uploadStream\"] = (...args) => api().uploadStream(...args);\n\n/** Runtime wrapper namespace for `tailordb.file`. */\nexport const file = {\n upload,\n download,\n downloadAsBase64,\n delete: deleteFile,\n getMetadata,\n downloadStream,\n uploadStream,\n} as const satisfies TailorDBFileAPI;\n"],"mappings":";AA0NA,MAAM,YACH,WAAkE,SAAS;;;;;;AAO9E,MAAM,UAAqC,GAAG,SAAS,IAAI,CAAC,CAAC,OAAO,GAAG,IAAI;;;;;;AAO3E,MAAM,YAAyC,GAAG,SAAS,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI;;;;;;AAOjF,MAAM,oBAAyD,GAAG,SAChE,IAAI,CAAC,CAAC,iBAAiB,GAAG,IAAI;;;;;;AAOhC,MAAM,cAAyC,GAAG,SAAS,IAAI,CAAC,CAAC,OAAO,GAAG,IAAI;;;;;;AAO/E,MAAM,eAA+C,GAAG,SAAS,IAAI,CAAC,CAAC,YAAY,GAAG,IAAI;;;;;;AAO1F,MAAM,kBAAqD,GAAG,SAC5D,IAAI,CAAC,CAAC,eAAe,GAAG,IAAI;;;;;;AAO9B,MAAM,gBAAiD,GAAG,SAAS,IAAI,CAAC,CAAC,aAAa,GAAG,IAAI;;AAG7F,MAAa,OAAO;CAClB;CACA;CACA;CACA,QAAQ;CACR;CACA;CACA;AACF"}
1
+ {"version":3,"file":"file-BUcmVIH2.mjs","names":[],"sources":["../src/runtime/file.ts"],"sourcesContent":["/**\n * TailorDB file (BLOB) utilities.\n *\n * Thin typed wrapper around the platform-provided `tailordb.file` runtime API.\n * At runtime this delegates to `globalThis.tailordb.file`. Use `mockFile` from\n * `@tailor-platform/sdk/vitest` to mock these calls in unit tests.\n * @example\n * import { file } from \"@tailor-platform/sdk/runtime\";\n *\n * const { metadata } = await file.upload(\n * \"my-namespace\",\n * \"Document\",\n * \"attachment\",\n * recordId,\n * bytes,\n * );\n */\n\n/** Upload response metadata. */\nexport interface UploadMetadata {\n fileSize: number;\n sha256sum: string;\n}\n\n/** Download response metadata. */\nexport interface DownloadMetadata {\n contentType: string;\n fileSize: number;\n sha256sum: string;\n lastUploadedAt: string;\n}\n\n/** File metadata (for {@link TailorDBFileAPI.getMetadata}). */\nexport interface FileMetadata {\n contentType: string;\n fileSize: number;\n sha256sum: string;\n urlPath: string;\n lastUploadedAt?: string;\n}\n\n/** Upload options. */\nexport interface FileUploadOptions {\n contentType?: string;\n}\n\n/** Upload stream options. */\nexport interface FileUploadStreamOptions {\n contentType?: string;\n fileSize?: number;\n}\n\n/** Upload response. */\nexport interface FileUploadResponse {\n metadata: UploadMetadata;\n}\n\n/** Download response. */\nexport interface FileDownloadResponse {\n data: Uint8Array;\n metadata: DownloadMetadata;\n}\n\n/** Download-as-Base64 response. */\nexport interface FileDownloadAsBase64Response {\n data: string;\n metadata: DownloadMetadata;\n}\n\n/** Download stream response. */\nexport interface FileDownloadStreamResponse {\n body: ReadableStream<Uint8Array>;\n metadata: DownloadMetadata;\n}\n\n/** Error code emitted by {@link TailorDBFileError}. */\nexport type TailorDBFileErrorCode =\n | \"INVALID_PARAMS\"\n | \"INVALID_DATA_TYPE\"\n | \"OPERATION_FAILED\"\n | \"DELETE_FAILED\"\n | \"STREAM_OPEN_FAILED\"\n | \"STREAM_READ_ERROR\"\n | \"STREAM_ERROR\"\n | \"FILE_TOO_LARGE\";\n\n/**\n * Type-only shape of the `TailorDBFileError` runtime class. The class itself\n * is provided by the platform runtime (and by `injectMocks` in tests); this\n * interface mirrors it so callers can `import type { TailorDBFileError }` from\n * the wrapper module without depending on any ambient declaration.\n */\nexport interface TailorDBFileError extends Error {\n name: \"TailorDBFileError\";\n code?: TailorDBFileErrorCode;\n cause?: unknown;\n}\n\n/**\n * Platform API surface for `tailordb.file`. Describes the shape the platform\n * runtime injects on `globalThis.tailordb.file`.\n */\nexport interface TailorDBFileAPI {\n /**\n * Upload a file to TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @param data - File contents\n * @param options - Upload options (e.g. `contentType`)\n * @returns Upload response containing the file metadata\n */\n upload(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n data: string | ArrayBuffer | Uint8Array | number[],\n options?: FileUploadOptions,\n ): Promise<FileUploadResponse>;\n\n /**\n * Download a file from TailorDB.\n *\n * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n * exceeds 10MB — use {@link downloadStream} for large files.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @returns Bytes and metadata for the file\n */\n download(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadResponse>;\n\n /**\n * Download a file from TailorDB as a Base64-encoded string.\n *\n * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n * exceeds 10MB — use {@link downloadStream} for large files.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @returns Base64-encoded contents and metadata for the file\n */\n downloadAsBase64(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadAsBase64Response>;\n\n /**\n * Delete a file from TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @returns Resolves once the file has been deleted\n */\n delete(namespace: string, typeName: string, fieldName: string, recordId: string): Promise<void>;\n\n /**\n * Get file metadata from TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @returns Metadata for the stored file\n */\n getMetadata(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileMetadata>;\n\n /**\n * Download a file as a ReadableStream.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @returns ReadableStream body and metadata for the file\n */\n downloadStream(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadStreamResponse>;\n\n /**\n * Upload a file using a ReadableStream.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @param readableStream - ReadableStream providing the file data\n * @param options - Upload stream options (e.g. `contentType`, `fileSize`)\n * @returns Upload response containing the file metadata\n */\n uploadStream(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n readableStream: ReadableStream<Uint8Array | ArrayBuffer>,\n options?: FileUploadStreamOptions,\n ): Promise<FileUploadResponse>;\n}\n\nconst api = (): TailorDBFileAPI =>\n (globalThis as unknown as { tailordb: { file: TailorDBFileAPI } }).tailordb.file;\n\n/**\n * See {@link TailorDBFileAPI.upload}.\n * @param args - Forwarded to {@link TailorDBFileAPI.upload}\n * @returns Upload response containing the file metadata\n */\nconst upload: TailorDBFileAPI[\"upload\"] = (...args) => api().upload(...args);\n\n/**\n * See {@link TailorDBFileAPI.download}.\n * @param args - Forwarded to {@link TailorDBFileAPI.download}\n * @returns Bytes and metadata for the file\n */\nconst download: TailorDBFileAPI[\"download\"] = (...args) => api().download(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadAsBase64}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadAsBase64}\n * @returns Base64-encoded contents and metadata for the file\n */\nconst downloadAsBase64: TailorDBFileAPI[\"downloadAsBase64\"] = (...args) =>\n api().downloadAsBase64(...args);\n\n/**\n * See {@link TailorDBFileAPI.delete}.\n * @param args - Forwarded to {@link TailorDBFileAPI.delete}\n * @returns Resolves once the file has been deleted\n */\nconst deleteFile: TailorDBFileAPI[\"delete\"] = (...args) => api().delete(...args);\n\n/**\n * See {@link TailorDBFileAPI.getMetadata}.\n * @param args - Forwarded to {@link TailorDBFileAPI.getMetadata}\n * @returns Metadata for the stored file\n */\nconst getMetadata: TailorDBFileAPI[\"getMetadata\"] = (...args) => api().getMetadata(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadStream}\n * @returns ReadableStream body and metadata for the file\n */\nconst downloadStream: TailorDBFileAPI[\"downloadStream\"] = (...args) =>\n api().downloadStream(...args);\n\n/**\n * See {@link TailorDBFileAPI.uploadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.uploadStream}\n * @returns Upload response containing the file metadata\n */\nconst uploadStream: TailorDBFileAPI[\"uploadStream\"] = (...args) => api().uploadStream(...args);\n\n/** Runtime wrapper namespace for `tailordb.file`. */\nexport const file = {\n upload,\n download,\n downloadAsBase64,\n delete: deleteFile,\n getMetadata,\n downloadStream,\n uploadStream,\n} as const satisfies TailorDBFileAPI;\n"],"mappings":";AA0NA,MAAM,YACH,WAAkE,SAAS;;;;;;AAO9E,MAAM,UAAqC,GAAG,SAAS,IAAI,CAAC,CAAC,OAAO,GAAG,IAAI;;;;;;AAO3E,MAAM,YAAyC,GAAG,SAAS,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI;;;;;;AAOjF,MAAM,oBAAyD,GAAG,SAChE,IAAI,CAAC,CAAC,iBAAiB,GAAG,IAAI;;;;;;AAOhC,MAAM,cAAyC,GAAG,SAAS,IAAI,CAAC,CAAC,OAAO,GAAG,IAAI;;;;;;AAO/E,MAAM,eAA+C,GAAG,SAAS,IAAI,CAAC,CAAC,YAAY,GAAG,IAAI;;;;;;AAO1F,MAAM,kBAAqD,GAAG,SAC5D,IAAI,CAAC,CAAC,eAAe,GAAG,IAAI;;;;;;AAO9B,MAAM,gBAAiD,GAAG,SAAS,IAAI,CAAC,CAAC,aAAa,GAAG,IAAI;;AAG7F,MAAa,OAAO;CAClB;CACA;CACA;CACA,QAAQ;CACR;CACA;CACA;AACF"}
@@ -20,8 +20,9 @@ import { Kysely, Kysely as Kysely$1, sql } from "kysely";
20
20
  */
21
21
  function createGetDB() {
22
22
  return function getDB(namespace, config) {
23
+ const client = new tailordb.Client({ namespace });
23
24
  return new Kysely$1({
24
- dialect: new TailordbDialect$1(new tailordb.Client({ namespace })),
25
+ dialect: new TailordbDialect$1(client),
25
26
  ...config
26
27
  });
27
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["Kysely","TailordbDialect"],"sources":["../../src/kysely/index.ts"],"sourcesContent":["/**\n * Kysely integration module for generated TailorDB code.\n *\n * Re-exports kysely and function-kysely-tailordb types through a single import path\n * to avoid phantom dependency issues with pnpm, and provides the namespace-aware\n * utility types and factory functions the code generator emits against.\n *\n * It also holds the `TailorDB*` input types, which hand-written code uses to derive\n * create/read/update shapes straight from a table or field collection. They live here\n * rather than in the main entry point because they are built out of kysely's column\n * types, which the main entry point does not otherwise depend on.\n */\n\nimport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\nimport {\n type ColumnType,\n Kysely,\n type Insertable,\n type KyselyConfig,\n type Selectable,\n type Transaction as KyselyTransaction,\n type Updateable,\n} from \"kysely\";\nimport type {\n IsAutoFilledDBField,\n IsReadOnlyDBField,\n TailorAnyDBField,\n TailorAnyDBType,\n} from \"#/configure/services/tailordb/types\";\nimport type { output, TypeLevelError } from \"#/types/helpers\";\n\nexport {\n type ColumnType,\n Kysely,\n type KyselyConfig,\n type Transaction,\n type Insertable,\n type Selectable,\n sql,\n type Updateable,\n} from \"kysely\";\n\nexport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\n\nexport type Timestamp = ColumnType<Date, Date | string, Date | string>;\ntype ResolveSelect<T> = T extends ColumnType<infer S, unknown, unknown> ? S : T;\ntype ResolveInsert<T> = T extends ColumnType<unknown, infer I, unknown> ? I : T;\ntype ResolveUpdate<T> = T extends ColumnType<unknown, unknown, infer U> ? U : T;\nexport type ObjectColumnType<T> = ColumnType<\n { [K in keyof T]-?: Exclude<ResolveSelect<T[K]>, undefined> },\n { [K in keyof T]: ResolveInsert<T[K]> },\n { [K in keyof T]: ResolveUpdate<T[K]> }\n>;\nexport type ArrayColumnType<T> = ColumnType<\n ResolveSelect<T>[],\n ResolveInsert<T>[],\n ResolveUpdate<T>[]\n>;\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\n// The insert/update types carry the reason so that supplying a value fails with it,\n// instead of the bare \"does not exist\" an absent key produces. The named alias is\n// what survives in nested positions (values()/set()), where tsc elides the marker's\n// type argument and only the name reaches the user.\n// `| undefined` is what makes the column omittable; Kysely drops undefined columns\n// from the statement, so passing it explicitly is the same as leaving it out.\ntype SerialColumnMustBeOmitted = TypeLevelError<\"assigned by .serial(); remove it from the input\">;\nexport type Serial<T = string | number> = ColumnType<\n T,\n SerialColumnMustBeOmitted | undefined,\n SerialColumnMustBeOmitted | undefined\n>;\n\n// Kysely composes its input types out of intersections. Flattening them keeps the\n// shape readable in assignability errors and editor tooltips.\ntype FlattenColumns<T> = { [K in keyof T]: T[K] } & {};\n\nexport type TailordbKysely<DB> = Kysely<DB>;\nexport type NamespaceDB<NS, N extends keyof NS = keyof NS> = TailordbKysely<NS[N]>;\n\n/**\n * Create a namespace-aware getDB function for generated code.\n * @returns A getDB function that creates Kysely instances for specific namespaces\n */\nexport function createGetDB<NS>() {\n return function getDB<const N extends keyof NS & string>(\n namespace: N,\n config?: Omit<KyselyConfig, \"dialect\">,\n ): TailordbKysely<NS[N]> {\n const client = new tailordb.Client({ namespace });\n return new Kysely<NS[N]>({\n dialect: new TailordbDialect(client),\n ...config,\n });\n };\n}\n\nexport type NamespaceTransaction<NS, K extends keyof NS | TailordbKysely<NS[keyof NS]> = keyof NS> =\n K extends TailordbKysely<infer DB>\n ? KyselyTransaction<DB>\n : K extends keyof NS\n ? KyselyTransaction<NS[K]>\n : never;\n\nexport type NamespaceTableName<NS> = {\n [N in keyof NS]: keyof NS[N];\n}[keyof NS];\n\nexport type NamespaceTable<NS, T extends NamespaceTableName<NS>> = {\n [N in keyof NS]: T extends keyof NS[N] ? NS[N][T] : never;\n}[keyof NS];\n\nexport type NamespaceInsertable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Insertable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceSelectable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Selectable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceUpdateable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Updateable<NamespaceTable<NS, T>>\n>;\n\n/** What the derived input types accept: a table (`typeof myTable`) or a field collection. */\ntype TailorDBColumnsSource = TailorAnyDBType | Record<string, TailorAnyDBField>;\n\ntype DBFieldsOf<T> = T extends TailorAnyDBType ? T[\"fields\"] : T;\n\n// The column mapping below mirrors the one `kyselyTypePlugin` applies when it writes a\n// table interface (`plugin/builtin/kysely-type/type-processor.ts`), so both surfaces\n// resolve a field to the same column type — including inside nested objects, where the\n// function runtime also hands back a Date for a date/datetime.\n// `example/tests/kysely-parity.ts` pins the two against each other on real generator output.\ntype DBFieldType<F> = F extends TailorAnyDBField ? F[\"_defined\"][\"type\"] : never;\ntype IsArrayDBField<F> = F extends TailorAnyDBField\n ? F[\"_defined\"][\"array\"] extends true\n ? true\n : false\n : false;\n\ntype Unwrapped<F> = Exclude<output<F>, null>;\ntype ElementOutput<F> =\n IsArrayDBField<F> extends true\n ? Unwrapped<F> extends readonly (infer E)[]\n ? E\n : Unwrapped<F>\n : Unwrapped<F>;\n\ntype NestedFieldsOf<F> = F extends TailorAnyDBField ? F[\"fields\"] : never;\n\n// Nested props carry the same column mapping as top-level ones, so a datetime inside an\n// object also resolves to Timestamp. The optional marker matches what the output type\n// gives them, which is what the generator emits too.\n// `-readonly` because a field collection is inferred with `const`, and the generated\n// table interfaces declare their nested props mutable.\ntype NestedProps<Fields> = {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? never : K]: DBColumn<Fields[K]>;\n} & {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? K : never]?: DBColumn<Fields[K]>;\n};\n\n// The generator reaches for ObjectColumnType only when the object holds something whose\n// select and insert types differ: a timestamp, an optional prop, or a filled-in one.\ntype NestedNeedsColumnType<Fields> = true extends {\n [K in keyof Fields]: DBFieldType<Fields[K]> extends \"date\" | \"datetime\"\n ? true\n : null extends output<Fields[K]>\n ? true\n : DBFieldType<Fields[K]> extends \"nested\"\n ? NestedNeedsColumnType<NestedFieldsOf<Fields[K]>>\n : Fields[K] extends TailorAnyDBField\n ? IsAutoFilledDBField<Fields[K]>\n : false;\n}[keyof Fields]\n ? true\n : false;\n\ntype NestedColumn<F> =\n NestedNeedsColumnType<NestedFieldsOf<F>> extends true\n ? ObjectColumnType<FlattenColumns<NestedProps<NestedFieldsOf<F>>>>\n : FlattenColumns<NestedProps<NestedFieldsOf<F>>>;\n\ntype ElementColumn<F> =\n DBFieldType<F> extends \"date\" | \"datetime\"\n ? Timestamp\n : DBFieldType<F> extends \"nested\"\n ? NestedColumn<F>\n : ElementOutput<F>;\n\n// A ColumnType cannot sit inside an array — Kysely only unwraps it at the top level of a\n// table property — so an array of them keeps the ColumnType outermost.\ntype ArrayedColumn<F> =\n IsArrayDBField<F> extends true\n ? ElementColumn<F> extends ColumnType<unknown, unknown, unknown>\n ? ArrayColumnType<ElementColumn<F>>\n : ElementColumn<F>[]\n : ElementColumn<F>;\n\ntype NullableColumn<F> = null extends output<F> ? ArrayedColumn<F> | null : ArrayedColumn<F>;\n\ntype DBColumn<F> = F extends TailorAnyDBField\n ? IsReadOnlyDBField<F> extends true\n ? Serial<NullableColumn<F>>\n : IsAutoFilledDBField<F> extends true\n ? Generated<NullableColumn<F>>\n : NullableColumn<F>\n : never;\n\n// The column map the three derived types are built from. Not exported: it is how the\n// mapping is expressed, not something callers need to name.\ntype TailorDBColumns<T extends TailorDBColumnsSource> = {\n [K in keyof DBFieldsOf<T>]: K extends \"id\"\n ? Generated<output<DBFieldsOf<T>[K]>>\n : DBColumn<DBFieldsOf<T>[K]>;\n};\n\n/**\n * Create input derived from a TailorDB table (`typeof myTable`) or a field collection.\n *\n * Each field resolves to the column type `kyselyTypePlugin` writes for it, so this and\n * the generated table types agree: `.serial()` fields are never caller-supplied,\n * `.default()` / `.hooks({ create })` fields may be omitted, optional fields stay\n * optional, `id` is platform-generated, and a date or datetime reads back as `Date`.\n *\n * Pass a field collection when the fields are a type parameter — a shared module that\n * lets each project extend a table with its own fields cannot name a generated table\n * type. Declare it as `<const F extends Record<string, TailorAnyDBField>>` so the field\n * types are inferred; a bare `Record<string, TailorAnyDBField>` erases them and nothing\n * is checked.\n *\n * Hand the result to callers rather than consuming it inside the generic that declares\n * it: while `F` is still an unresolved type parameter the mapping stays deferred, so\n * assigning to it inside the function body reports the unevaluated conditional rather\n * than a readable shape.\n * @example\n * function createInput<const F extends Record<string, TailorAnyDBField>>(fields: F) {\n * return (input: TailorDBInsertable<F>) => { ... };\n * }\n */\nexport type TailorDBInsertable<T extends TailorDBColumnsSource> = FlattenColumns<\n Insertable<TailorDBColumns<T>>\n>;\n\n/** Read shape of a TailorDB table or field collection. See {@link TailorDBInsertable}. */\nexport type TailorDBSelectable<T extends TailorDBColumnsSource> = FlattenColumns<\n Selectable<TailorDBColumns<T>>\n>;\n\n/** Update input for a TailorDB table or field collection. See {@link TailorDBInsertable}. */\nexport type TailorDBUpdateable<T extends TailorDBColumnsSource> = FlattenColumns<\n Updateable<TailorDBColumns<T>>\n>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsFA,SAAgB,cAAkB;CAChC,OAAO,SAAS,MACd,WACA,QACuB;EAEvB,OAAO,IAAIA,SAAc;GACvB,SAAS,IAAIC,kBAAgB,IAFZ,SAAS,OAAO,EAAE,UAAU,CAEX,CAAC;GACnC,GAAG;EACL,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"index.mjs","names":["Kysely","TailordbDialect"],"sources":["../../src/kysely/index.ts"],"sourcesContent":["/**\n * Kysely integration module for generated TailorDB code.\n *\n * Re-exports kysely and function-kysely-tailordb types through a single import path\n * to avoid phantom dependency issues with pnpm, and provides the namespace-aware\n * utility types and factory functions the code generator emits against.\n *\n * It also holds the `TailorDB*` input types, which hand-written code uses to derive\n * create/read/update shapes straight from a table or field collection. They live here\n * rather than in the main entry point because they are built out of kysely's column\n * types, which the main entry point does not otherwise depend on.\n */\n\nimport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\nimport {\n type ColumnType,\n Kysely,\n type Insertable,\n type KyselyConfig,\n type Selectable,\n type Transaction as KyselyTransaction,\n type Updateable,\n} from \"kysely\";\nimport type {\n IsAutoFilledDBField,\n IsReadOnlyDBField,\n TailorAnyDBField,\n TailorAnyDBType,\n} from \"#/configure/services/tailordb/types\";\nimport type { output, TypeLevelError } from \"#/types/helpers\";\n\nexport {\n type ColumnType,\n Kysely,\n type KyselyConfig,\n type Transaction,\n type Insertable,\n type Selectable,\n sql,\n type Updateable,\n} from \"kysely\";\n\nexport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\n\nexport type Timestamp = ColumnType<Date, Date | string, Date | string>;\ntype ResolveSelect<T> = T extends ColumnType<infer S, unknown, unknown> ? S : T;\ntype ResolveInsert<T> = T extends ColumnType<unknown, infer I, unknown> ? I : T;\ntype ResolveUpdate<T> = T extends ColumnType<unknown, unknown, infer U> ? U : T;\nexport type ObjectColumnType<T> = ColumnType<\n { [K in keyof T]-?: Exclude<ResolveSelect<T[K]>, undefined> },\n { [K in keyof T]: ResolveInsert<T[K]> },\n { [K in keyof T]: ResolveUpdate<T[K]> }\n>;\nexport type ArrayColumnType<T> = ColumnType<\n ResolveSelect<T>[],\n ResolveInsert<T>[],\n ResolveUpdate<T>[]\n>;\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\n// The insert/update types carry the reason so that supplying a value fails with it,\n// instead of the bare \"does not exist\" an absent key produces. The named alias is\n// what survives in nested positions (values()/set()), where tsc elides the marker's\n// type argument and only the name reaches the user.\n// `| undefined` is what makes the column omittable; Kysely drops undefined columns\n// from the statement, so passing it explicitly is the same as leaving it out.\ntype SerialColumnMustBeOmitted = TypeLevelError<\"assigned by .serial(); remove it from the input\">;\nexport type Serial<T = string | number> = ColumnType<\n T,\n SerialColumnMustBeOmitted | undefined,\n SerialColumnMustBeOmitted | undefined\n>;\n\n// Kysely composes its input types out of intersections. Flattening them keeps the\n// shape readable in assignability errors and editor tooltips.\ntype FlattenColumns<T> = { [K in keyof T]: T[K] } & {};\n\nexport type TailordbKysely<DB> = Kysely<DB>;\nexport type NamespaceDB<NS, N extends keyof NS = keyof NS> = TailordbKysely<NS[N]>;\n\n/**\n * Create a namespace-aware getDB function for generated code.\n * @returns A getDB function that creates Kysely instances for specific namespaces\n */\nexport function createGetDB<NS>() {\n return function getDB<const N extends keyof NS & string>(\n namespace: N,\n config?: Omit<KyselyConfig, \"dialect\">,\n ): TailordbKysely<NS[N]> {\n const client = new tailordb.Client({ namespace });\n return new Kysely<NS[N]>({\n dialect: new TailordbDialect(client),\n ...config,\n });\n };\n}\n\nexport type NamespaceTransaction<NS, K extends keyof NS | TailordbKysely<NS[keyof NS]> = keyof NS> =\n K extends TailordbKysely<infer DB>\n ? KyselyTransaction<DB>\n : K extends keyof NS\n ? KyselyTransaction<NS[K]>\n : never;\n\nexport type NamespaceTableName<NS> = {\n [N in keyof NS]: keyof NS[N];\n}[keyof NS];\n\nexport type NamespaceTable<NS, T extends NamespaceTableName<NS>> = {\n [N in keyof NS]: T extends keyof NS[N] ? NS[N][T] : never;\n}[keyof NS];\n\nexport type NamespaceInsertable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Insertable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceSelectable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Selectable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceUpdateable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Updateable<NamespaceTable<NS, T>>\n>;\n\n/** What the derived input types accept: a table (`typeof myTable`) or a field collection. */\ntype TailorDBColumnsSource = TailorAnyDBType | Record<string, TailorAnyDBField>;\n\ntype DBFieldsOf<T> = T extends TailorAnyDBType ? T[\"fields\"] : T;\n\n// The column mapping below mirrors the one `kyselyTypePlugin` applies when it writes a\n// table interface (`plugin/builtin/kysely-type/type-processor.ts`), so both surfaces\n// resolve a field to the same column type — including inside nested objects, where the\n// function runtime also hands back a Date for a date/datetime.\n// `example/tests/kysely-parity.ts` pins the two against each other on real generator output.\ntype DBFieldType<F> = F extends TailorAnyDBField ? F[\"_defined\"][\"type\"] : never;\ntype IsArrayDBField<F> = F extends TailorAnyDBField\n ? F[\"_defined\"][\"array\"] extends true\n ? true\n : false\n : false;\n\ntype Unwrapped<F> = Exclude<output<F>, null>;\ntype ElementOutput<F> =\n IsArrayDBField<F> extends true\n ? Unwrapped<F> extends readonly (infer E)[]\n ? E\n : Unwrapped<F>\n : Unwrapped<F>;\n\ntype NestedFieldsOf<F> = F extends TailorAnyDBField ? F[\"fields\"] : never;\n\n// Nested props carry the same column mapping as top-level ones, so a datetime inside an\n// object also resolves to Timestamp. The optional marker matches what the output type\n// gives them, which is what the generator emits too.\n// `-readonly` because a field collection is inferred with `const`, and the generated\n// table interfaces declare their nested props mutable.\ntype NestedProps<Fields> = {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? never : K]: DBColumn<Fields[K]>;\n} & {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? K : never]?: DBColumn<Fields[K]>;\n};\n\n// The generator reaches for ObjectColumnType only when the object holds something whose\n// select and insert types differ: a timestamp, an optional prop, or a filled-in one.\ntype NestedNeedsColumnType<Fields> = true extends {\n [K in keyof Fields]: DBFieldType<Fields[K]> extends \"date\" | \"datetime\"\n ? true\n : null extends output<Fields[K]>\n ? true\n : DBFieldType<Fields[K]> extends \"nested\"\n ? NestedNeedsColumnType<NestedFieldsOf<Fields[K]>>\n : Fields[K] extends TailorAnyDBField\n ? IsAutoFilledDBField<Fields[K]>\n : false;\n}[keyof Fields]\n ? true\n : false;\n\ntype NestedColumn<F> =\n NestedNeedsColumnType<NestedFieldsOf<F>> extends true\n ? ObjectColumnType<FlattenColumns<NestedProps<NestedFieldsOf<F>>>>\n : FlattenColumns<NestedProps<NestedFieldsOf<F>>>;\n\ntype ElementColumn<F> =\n DBFieldType<F> extends \"date\" | \"datetime\"\n ? Timestamp\n : DBFieldType<F> extends \"nested\"\n ? NestedColumn<F>\n : ElementOutput<F>;\n\n// A ColumnType cannot sit inside an array — Kysely only unwraps it at the top level of a\n// table property — so an array of them keeps the ColumnType outermost.\ntype ArrayedColumn<F> =\n IsArrayDBField<F> extends true\n ? ElementColumn<F> extends ColumnType<unknown, unknown, unknown>\n ? ArrayColumnType<ElementColumn<F>>\n : ElementColumn<F>[]\n : ElementColumn<F>;\n\ntype NullableColumn<F> = null extends output<F> ? ArrayedColumn<F> | null : ArrayedColumn<F>;\n\ntype DBColumn<F> = F extends TailorAnyDBField\n ? IsReadOnlyDBField<F> extends true\n ? Serial<NullableColumn<F>>\n : IsAutoFilledDBField<F> extends true\n ? Generated<NullableColumn<F>>\n : NullableColumn<F>\n : never;\n\n// The column map the three derived types are built from. Not exported: it is how the\n// mapping is expressed, not something callers need to name.\ntype TailorDBColumns<T extends TailorDBColumnsSource> = {\n [K in keyof DBFieldsOf<T>]: K extends \"id\"\n ? Generated<output<DBFieldsOf<T>[K]>>\n : DBColumn<DBFieldsOf<T>[K]>;\n};\n\n/**\n * Create input derived from a TailorDB table (`typeof myTable`) or a field collection.\n *\n * Each field resolves to the column type `kyselyTypePlugin` writes for it, so this and\n * the generated table types agree: `.serial()` fields are never caller-supplied,\n * `.default()` / `.hooks({ create })` fields may be omitted, optional fields stay\n * optional, `id` is platform-generated, and a date or datetime reads back as `Date`.\n *\n * Pass a field collection when the fields are a type parameter — a shared module that\n * lets each project extend a table with its own fields cannot name a generated table\n * type. Declare it as `<const F extends Record<string, TailorAnyDBField>>` so the field\n * types are inferred; a bare `Record<string, TailorAnyDBField>` erases them and nothing\n * is checked.\n *\n * Hand the result to callers rather than consuming it inside the generic that declares\n * it: while `F` is still an unresolved type parameter the mapping stays deferred, so\n * assigning to it inside the function body reports the unevaluated conditional rather\n * than a readable shape.\n * @example\n * function createInput<const F extends Record<string, TailorAnyDBField>>(fields: F) {\n * return (input: TailorDBInsertable<F>) => { ... };\n * }\n */\nexport type TailorDBInsertable<T extends TailorDBColumnsSource> = FlattenColumns<\n Insertable<TailorDBColumns<T>>\n>;\n\n/** Read shape of a TailorDB table or field collection. See {@link TailorDBInsertable}. */\nexport type TailorDBSelectable<T extends TailorDBColumnsSource> = FlattenColumns<\n Selectable<TailorDBColumns<T>>\n>;\n\n/** Update input for a TailorDB table or field collection. See {@link TailorDBInsertable}. */\nexport type TailorDBUpdateable<T extends TailorDBColumnsSource> = FlattenColumns<\n Updateable<TailorDBColumns<T>>\n>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsFA,SAAgB,cAAkB;CAChC,OAAO,SAAS,MACd,WACA,QACuB;EACvB,MAAM,SAAS,IAAI,SAAS,OAAO,EAAE,UAAU,CAAC;EAChD,OAAO,IAAIA,SAAc;GACvB,SAAS,IAAIC,kBAAgB,MAAM;GACnC,GAAG;EACL,CAAC;CACH;AACF"}
@@ -274,11 +274,13 @@ function formatLogLine(opts) {
274
274
  */
275
275
  function writeLog(type, message, opts) {
276
276
  const mode = opts?.mode ?? "default";
277
+ const indent = opts?.indent ?? 0;
278
+ const inspectOpts = { breakLength: process.stdout.columns || 80 };
277
279
  const output = formatLogLine({
278
280
  mode,
279
- indent: opts?.indent ?? 0,
281
+ indent,
280
282
  type,
281
- message: formatWithOptions({ breakLength: process.stdout.columns || 80 }, message),
283
+ message: formatWithOptions(inspectOpts, message),
282
284
  timestamp: mode === "stream" ? `${(/* @__PURE__ */ new Date()).toLocaleTimeString()} ` : ""
283
285
  });
284
286
  process.stderr.write(renderFor(process.stderr, output));
@@ -339,14 +341,16 @@ const logger = {
339
341
  return formatValue(value, pretty);
340
342
  };
341
343
  if (!Array.isArray(data)) {
342
- const t = renderTable(Object.entries(data).filter(([key]) => !isExcluded(key)).map(([key, value]) => [key, transformValue(key, value, data, true)]), { singleLine: false });
344
+ const formattedEntries = Object.entries(data).filter(([key]) => !isExcluded(key)).map(([key, value]) => [key, transformValue(key, value, data, true)]);
345
+ const t = renderTable(formattedEntries, { singleLine: false });
343
346
  process.stdout.write(renderFor(process.stdout, t));
344
347
  return;
345
348
  }
346
349
  if (data.length === 0) return;
347
350
  const headers = Array.from(new Set(data.flatMap((item) => Object.keys(item)))).filter((h) => !isExcluded(h));
348
351
  if (headers.length === 0) return;
349
- const t = renderTable([headers, ...data.map((item) => headers.map((header) => transformValue(header, item[header], item)))], { drawHorizontalLine: (lineIndex, rowCount) => {
352
+ const rows = data.map((item) => headers.map((header) => transformValue(header, item[header], item)));
353
+ const t = renderTable([headers, ...rows], { drawHorizontalLine: (lineIndex, rowCount) => {
350
354
  return lineIndex === 0 || lineIndex === 1 || lineIndex === rowCount;
351
355
  } });
352
356
  process.stdout.write(renderFor(process.stdout, t));
@@ -355,4 +359,4 @@ const logger = {
355
359
 
356
360
  //#endregion
357
361
  export { parseBoolean as a, symbols as i, logger as n, renderTable as o, styles as r, renderFor as s, CIPromptError as t };
358
- //# sourceMappingURL=logger-DSdExfor.mjs.map
362
+ //# sourceMappingURL=logger-D0bEznPv.mjs.map