@tailor-platform/sdk 2.29.2 → 2.32.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 (95) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/ai/index.d.mts +112 -0
  3. package/dist/ai/index.mjs +2 -0
  4. package/dist/ai/index.mjs.map +1 -0
  5. package/dist/{application-Dm_21z-i.mjs → application-C2bPmmbh.mjs} +1 -1
  6. package/dist/{application-D8jruzwo.mjs → application-D0xNJbag.mjs} +32 -32
  7. package/dist/{application-D8jruzwo.mjs.map → application-D0xNJbag.mjs.map} +1 -1
  8. package/dist/cli/commands/machineuser/list.d.mts +2 -1
  9. package/dist/cli/lib.mjs +1 -1
  10. package/dist/cli/main.mjs +17 -17
  11. package/dist/cli/main.mjs.map +1 -1
  12. package/dist/cli/shared/error-json.d.mts +2 -0
  13. package/dist/completion/zsh-worker.zsh +6 -2
  14. package/dist/{crashreport-BAG8c7hB.mjs → crashreport-BCIy33Cz.mjs} +2 -2
  15. package/dist/{crashreport-BAG8c7hB.mjs.map → crashreport-BCIy33Cz.mjs.map} +1 -1
  16. package/dist/crashreport-IN6H7GX3.mjs +1 -0
  17. package/dist/errors-BOF68LJ5.mjs +5 -0
  18. package/dist/errors-BOF68LJ5.mjs.map +1 -0
  19. package/dist/{kysely-type-BWMfV301.mjs → kysely-type-CBwsdx16.mjs} +6 -3
  20. package/dist/kysely-type-CBwsdx16.mjs.map +1 -0
  21. package/dist/{logger-DuaT2zY3.mjs → logger-RgtbhJ83.mjs} +3 -3
  22. package/dist/logger-RgtbhJ83.mjs.map +1 -0
  23. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  24. package/dist/plugin/builtin/seed/index.mjs +1 -1
  25. package/dist/register-ts-hook-_dHIREtU.mjs +1050 -0
  26. package/dist/register-ts-hook-_dHIREtU.mjs.map +1 -0
  27. package/dist/seed/idp-user-password.d.mts +15 -0
  28. package/dist/seed/index.d.mts +19 -1
  29. package/dist/seed/index.mjs +6 -6
  30. package/dist/seed/index.mjs.map +1 -1
  31. package/dist/{seed-C3P_T_Eh.mjs → seed-Cn-3JApy.mjs} +12 -8
  32. package/dist/seed-Cn-3JApy.mjs.map +1 -0
  33. package/dist/{service-l_tEI8sS.mjs → service-9SBPNpK8.mjs} +2 -2
  34. package/dist/{service-l_tEI8sS.mjs.map → service-9SBPNpK8.mjs.map} +1 -1
  35. package/dist/{service-b1g22NN9.mjs → service-9rwi_I4d.mjs} +2 -2
  36. package/dist/{service-b1g22NN9.mjs.map → service-9rwi_I4d.mjs.map} +1 -1
  37. package/dist/service-Ca1WwyIt.mjs +1 -0
  38. package/dist/telemetry-DfT2UfI4.mjs +2 -0
  39. package/dist/telemetry-DfT2UfI4.mjs.map +1 -0
  40. package/dist/{telemetry-DpkFGBmu.mjs → telemetry-nqOtDWNO.mjs} +1 -1
  41. package/docs/ai.md +92 -0
  42. package/docs/cli/application.md +4 -0
  43. package/docs/cli/auth.md +4 -0
  44. package/docs/cli/completion.md +4 -0
  45. package/docs/cli/crashreport.md +4 -0
  46. package/docs/cli/data.md +4 -0
  47. package/docs/cli/executor.md +4 -0
  48. package/docs/cli/function.md +4 -0
  49. package/docs/cli/organization.md +4 -0
  50. package/docs/cli/plugin.md +4 -0
  51. package/docs/cli/query.md +4 -0
  52. package/docs/cli/secret.md +4 -0
  53. package/docs/cli/skills.md +4 -0
  54. package/docs/cli/staticwebsite.md +4 -0
  55. package/docs/cli/tailordb.md +21 -15
  56. package/docs/cli/upgrade.md +4 -0
  57. package/docs/cli/user.md +4 -0
  58. package/docs/cli/workflow.md +4 -0
  59. package/docs/cli/workspace.md +5 -1
  60. package/docs/cli-reference.md +4 -0
  61. package/docs/configuration.md +4 -0
  62. package/docs/data-clone.md +4 -0
  63. package/docs/github-actions.md +108 -11
  64. package/docs/migration/v2.md +4 -0
  65. package/docs/multi-environment.md +4 -0
  66. package/docs/plugin/custom.md +4 -0
  67. package/docs/plugin/frontend.md +4 -0
  68. package/docs/plugin/index.md +4 -0
  69. package/docs/quickstart.md +4 -0
  70. package/docs/runtime.md +4 -0
  71. package/docs/services/aigateway.md +4 -0
  72. package/docs/services/auth.md +4 -0
  73. package/docs/services/executor.md +4 -0
  74. package/docs/services/http-adapter.md +4 -0
  75. package/docs/services/idp.md +4 -0
  76. package/docs/services/resolver.md +4 -0
  77. package/docs/services/secret.md +4 -0
  78. package/docs/services/staticwebsite.md +4 -0
  79. package/docs/services/tailordb/index.md +4 -0
  80. package/docs/services/tailordb/migration.md +49 -31
  81. package/docs/services/workflow.md +4 -0
  82. package/docs/testing.md +6 -1
  83. package/package.json +16 -9
  84. package/dist/crashreport-BcXuocwI.mjs +0 -1
  85. package/dist/errors-D3qpNHR6.mjs +0 -7
  86. package/dist/errors-D3qpNHR6.mjs.map +0 -1
  87. package/dist/kysely-type-BWMfV301.mjs.map +0 -1
  88. package/dist/logger-DuaT2zY3.mjs.map +0 -1
  89. package/dist/register-ts-hook-B5ZbbJwS.mjs +0 -1012
  90. package/dist/register-ts-hook-B5ZbbJwS.mjs.map +0 -1
  91. package/dist/seed-C3P_T_Eh.mjs.map +0 -1
  92. package/dist/service-C_CogLQr.mjs +0 -1
  93. package/dist/telemetry-Xk7J829P.mjs +0 -2
  94. package/dist/telemetry-Xk7J829P.mjs.map +0 -1
  95. package/docs/migration/v3.md +0 -277
@@ -16,6 +16,8 @@ export declare function errorToJson(error: unknown, options?: ErrorToJsonOptions
16
16
  /**
17
17
  * Serialize a CLI failure into the stable JSON error envelope.
18
18
  *
19
+ * String values are written without colors.
20
+ *
19
21
  * Redacts registered secrets from every string, number, boolean, and `null` *value* in the
20
22
  * envelope, however deeply nested, before this function's own `JSON.stringify` call, rather
21
23
  * than relying only on the redaction `logger.log()` does on the final string: an upstream
@@ -1,5 +1,5 @@
1
1
  # politty-completion-version: 1
2
- # politty-bin-sig: 1792460218
2
+ # politty-bin-sig: 1792653623
3
3
  # politty-bin-path: /home/runner/work/sdk/sdk/node_modules/.bin/tailor
4
4
  # program: tailor
5
5
  # shell: zsh
@@ -785,6 +785,8 @@ __tailor_worker_opt_takes_value() {
785
785
  tailordb:migration:test:--config|tailordb:migration:test:-c|tailordb:migration:test:--c) return 0 ;;
786
786
  tailordb:migration:test:--data) return 0 ;;
787
787
  tailordb:migration:test:--target-workspace-id|tailordb:migration:test:--targetWorkspaceId) return 0 ;;
788
+ tailordb:migration:test:--organization-id|tailordb:migration:test:--organizationId) return 0 ;;
789
+ tailordb:migration:test:--folder-id|tailordb:migration:test:--folderId) return 0 ;;
788
790
  tailordb:migration:test:--assert) return 0 ;;
789
791
  tailordb:migration:test:--assert-namespace|tailordb:migration:test:--assertNamespace) return 0 ;;
790
792
  tailordb:migration:test:--keep-ttl|tailordb:migration:test:--keepTtl) return 0 ;;
@@ -3892,6 +3894,8 @@ __tailor_worker_complete_tailordb_migration_test() {
3892
3894
  __tailor_worker_not_used "--data" && _opts+=("--data:Data source for the migration test (seed or clone)")
3893
3895
  __tailor_worker_not_used "--target-workspace-id" "--targetWorkspaceId" && _opts+=("--target-workspace-id:Existing throwaway workspace to retain after the test (requires --yes)")
3894
3896
  __tailor_worker_not_used "--keep" && _opts+=("--keep:Keep the automatically created workspace after the test")
3897
+ __tailor_worker_not_used "--organization-id" "--organizationId" && _opts+=("--organization-id:Organization to create the temporary workspace in, at its root unless --folder-id is given (default\: the source workspace's organization and folder). With --target-workspace-id, must match the target's organization")
3898
+ __tailor_worker_not_used "--folder-id" "--folderId" && _opts+=("--folder-id:Folder to create the temporary workspace in (default\: the source workspace's folder). Requires --organization-id when the source workspace is not in an organization. With --target-workspace-id, must match the target's folder")
3895
3899
  __tailor_worker_not_used "--assert" && _opts+=("--assert:Path to a TypeScript assertion script to run after migrations")
3896
3900
  __tailor_worker_not_used "--assert-namespace" "--assertNamespace" && _opts+=("--assert-namespace:TailorDB namespace exposed to the assertion script")
3897
3901
  __tailor_worker_not_used "--keep-ttl" "--keepTtl" && _opts+=("--keep-ttl:Record an expiry on the kept workspace, e.g. 24h or 7d, so workspace prune --expired can delete it (requires --keep)")
@@ -4580,7 +4584,7 @@ __tailor_worker_complete_workspace_create() {
4580
4584
  if [[ "${words[CURRENT]}" == -* ]]; then
4581
4585
  local -a _opts=()
4582
4586
  __tailor_worker_not_used "--name" "-n" "--n" && _opts+=("--name:Workspace name")
4583
- __tailor_worker_not_used "--region" "-r" "--r" && _opts+=("--region:Workspace region (asia-northeast, us-west)")
4587
+ __tailor_worker_not_used "--region" "-r" "--r" && _opts+=("--region:Workspace region (asia-northeast, us-west, europe-west)")
4584
4588
  __tailor_worker_not_used "--delete-protection" "--deleteProtection" "-d" "--d" && _opts+=("--delete-protection:Enable delete protection")
4585
4589
  __tailor_worker_not_used "--organization-id" "--organizationId" "-o" "--o" && _opts+=("--organization-id:Organization ID to workspace associate with")
4586
4590
  __tailor_worker_not_used "--folder-id" "--folderId" "-f" "--f" && _opts+=("--folder-id:Folder ID to workspace associate with")
@@ -1,4 +1,4 @@
1
- import{a as e,o as t}from"./logger-DuaT2zY3.mjs";import{t as n}from"./package-json-C690ceex.mjs";import{n as r}from"./user-agent-vdHYF3QL.mjs";import{r as i,t as a}from"./secret-file-C9wp_FCX.mjs";import*as o from"pathe";import*as s from"node:fs";import{isCI as c}from"std-env";import{parseYAML as l}from"confbox";import{xdgConfig as u}from"xdg-basedir";import*as d from"node:crypto";import*as f from"node:os";function parseCrashReportConfig(){if(c)return{localEnabled:!1,remoteEnabled:!1,localDir:``};let e=(process.env.TAILOR_CRASH_REPORTS_LOCAL??`on`).toLowerCase()!==`off`,t=(process.env.TAILOR_CRASH_REPORTS_REMOTE??`off`).toLowerCase()===`on`,n=u?o.join(u,`tailor-platform`,`crash-reports`):``;return{localEnabled:e&&n!==``,remoteEnabled:t,localDir:n}}const p=`--- JSON ---`,m=`.crash.log`;function formatCrashReport(e){return[`Crash Report: ${e.id}`,`Timestamp: ${e.timestamp}`,`Error Type: ${e.errorType}`,``,`--- Environment ---`,`SDK Version: ${e.sdkVersion}`,`Node Version: ${e.nodeVersion}`,`OS: ${e.osPlatform} ${e.osRelease}`,`Arch: ${e.arch}`,``,`--- Command ---`,`Command: ${e.command}`,`Arguments: ${JSON.stringify(e.argv)}`,``,`--- Error ---`,`Name: ${e.errorName}`,`Message: ${e.errorMessage}`,``,`--- Stack Trace ---`,e.stackTrace||`(no stack trace available)`,``,p,JSON.stringify(e),``].join(`
1
+ import{a as e,s as t}from"./logger-RgtbhJ83.mjs";import{t as n}from"./package-json-C690ceex.mjs";import{n as r}from"./user-agent-vdHYF3QL.mjs";import{r as i,t as a}from"./secret-file-C9wp_FCX.mjs";import*as o from"pathe";import*as s from"node:fs";import{isCI as c}from"std-env";import{parseYAML as l}from"confbox";import{xdgConfig as u}from"xdg-basedir";import*as d from"node:crypto";import*as f from"node:os";function parseCrashReportConfig(){if(c)return{localEnabled:!1,remoteEnabled:!1,localDir:``};let e=(process.env.TAILOR_CRASH_REPORTS_LOCAL??`on`).toLowerCase()!==`off`,t=(process.env.TAILOR_CRASH_REPORTS_REMOTE??`off`).toLowerCase()===`on`,n=u?o.join(u,`tailor-platform`,`crash-reports`):``;return{localEnabled:e&&n!==``,remoteEnabled:t,localDir:n}}const p=`--- JSON ---`,m=`.crash.log`;function formatCrashReport(e){return[`Crash Report: ${e.id}`,`Timestamp: ${e.timestamp}`,`Error Type: ${e.errorType}`,``,`--- Environment ---`,`SDK Version: ${e.sdkVersion}`,`Node Version: ${e.nodeVersion}`,`OS: ${e.osPlatform} ${e.osRelease}`,`Arch: ${e.arch}`,``,`--- Command ---`,`Command: ${e.command}`,`Arguments: ${JSON.stringify(e.argv)}`,``,`--- Error ---`,`Name: ${e.errorName}`,`Message: ${e.errorMessage}`,``,`--- Stack Trace ---`,e.stackTrace||`(no stack trace available)`,``,p,JSON.stringify(e),``].join(`
2
2
  `)}function generateFilename(e){return`${e.timestamp.replace(/[:.]/g,`-`)}-${e.id.slice(0,8)}${m}`}function cleanupOldFiles(e){try{let t=s.readdirSync(e).filter(e=>e.endsWith(m)).toSorted().toReversed();for(let n of t.slice(10))s.unlinkSync(o.join(e,n))}catch{}}function writeCrashReport(e,t){try{a(t);let n=generateFilename(e),r=o.join(t,n),s=formatCrashReport(e);return i(r,s),cleanupOldFiles(t),r}catch{return}}async function sendCrashReport(e,t){try{let n=process.env.TAILOR_CRASH_REPORT_ENDPOINT||`https://sdk-error-tracking-926vh9t4cl.erp.dev/query`,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`,"User-Agent":t},body:JSON.stringify({query:`
3
3
  mutation SubmitCrashReport(
4
4
  $id: String!
@@ -39,4 +39,4 @@ mutation SubmitCrashReport(
39
39
  }`,variables:e}),signal:AbortSignal.timeout(5e3)});if(!r.ok)return!1;let i=await r.json();return!i.errors?.length&&i.data?.submitCrashReport.success===!0}catch{return!1}}const h=f.homedir(),g=/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi,_=/\b[0-9a-fA-F]{32,}\b/g,v=/\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g,y=/(?:\/(?:[\w.@\- ]+\/)+[\w.@\- ]+)/g,b=/(?:[A-Za-z]:\\(?:[\w.@\- ]+\\)+[\w.@\- ]+)/g,x=/[?&][^?\s]*/g,S=/\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/,C=/^[A-Za-z]:\\/,w=`packages/sdk/`;function lastSegment(e,t){return e.split(t).pop()??e}function sanitizeStackTrace(e){let t=e.search(/\n\s+at /),n;return n=t===-1?sanitizeMessage(e):sanitizeMessage(e.slice(0,t))+e.slice(t),n=n.replace(y,e=>{let t=e.indexOf(w);return t===-1?e.startsWith(h)?`~/<redacted>/${lastSegment(e,`/`)}`:`<external>/${lastSegment(e,`/`)}`:e.slice(t)}),n=n.replace(b,e=>{let t=e.replace(/\\/g,`/`),n=t.indexOf(w);return n===-1?`<external>/${lastSegment(e,`\\`)}`:t.slice(n)}),n}function sanitizeMessage(e){let t=e;return t=t.replace(/\nRequest:\s*[\s\S]*$/,`
40
40
  Request: <redacted>`),t=t.replace(g,`<uuid>`),t=t.replace(_,`<redacted>`),t=t.replace(v,`<email>`),t=t.replace(x,`?<redacted>`),t=t.replace(y,e=>`<path>/${lastSegment(e,`/`)}`),t=t.replace(b,e=>`<path>/${lastSegment(e,`\\`)}`),t}function sanitizeArgv(e){let t=[],n=!1;for(let r of e){if(n){if(!r.startsWith(`-`)){t.push(`<redacted>`),n=!1;continue}n=!1}if(r.startsWith(`-`)){let e=r.indexOf(`=`);if(e!==-1){t.push(`${r.slice(0,e)}=<redacted>`);continue}t.push(r),n=!0}else r.startsWith(`/`)&&r.includes(`/`,1)||C.test(r)?t.push(`<path>`):S.test(r)?t.push(`<email>`):t.push(r)}return t}function parseCommand(){let e=process.argv.slice(2),t=[];for(let n of e){if(n.startsWith(`-`)||t.length>=3)break;t.push(n)}return t.join(` `)||`<unknown>`}function buildCrashReport(e){let{error:n,sdkVersion:r,errorType:i}=e,a=n instanceof Error,o=a?n.message:String(n),s=a&&n.stack?n.stack:``,c=a?n.name:`UnknownError`,l=readCurrentUser();return{id:d.randomUUID(),timestamp:(/* @__PURE__ */ new Date()).toISOString(),sdkVersion:r,nodeVersion:process.version,osPlatform:process.platform,osRelease:f.release(),arch:process.arch,command:sanitizeMessage(t(parseCommand())),argv:sanitizeArgv(process.argv.map(t)),errorName:t(c),errorMessage:sanitizeMessage(t(o)),stackTrace:sanitizeStackTrace(t(s)),errorType:i,userId:l?.id?t(l.id):null,userEmail:l?.email?t(l.email):null}}function readCurrentUser(){try{if(!u)return null;let e=o.join(u,`tailor-platform`,`config.yaml`);if(!s.existsSync(e))return null;let t=l(s.readFileSync(e,`utf-8`)),n=t?.current_user??null;if(!n)return null;let r=t.users?.[n]?.email;return{id:n,email:typeof r==`string`?r:legacyEmail(n)}}catch{return null}}function legacyEmail(e){return e.includes(`@`)?e:null}async function reportCrash(t,i){try{let a=parseCrashReportConfig();if(!a.localEnabled&&!a.remoteEnabled)return;let o=(await n()).version??`unknown`,s=buildCrashReport({error:t,sdkVersion:o,errorType:i});if(a.localEnabled){let t=writeCrashReport(s,a.localDir);t&&e.log([``,`An unexpected error occurred. A crash report has been saved to:`,` ${t}`,``,`To submit this report:`,` tailor crashreport send --file "${t}"`].join(`
41
41
  `))}a.remoteEnabled&&await sendCrashReport(s,r(o))}catch{}}function initCrashReporting(){let t=parseCrashReportConfig();if(!t.localEnabled&&!t.remoteEnabled)return;let handleFatal=(t,n)=>{let r=t instanceof Error?t.message:String(t);e.error(r),reportCrash(t,n).finally(()=>{process.exit(1)})};process.on(`uncaughtException`,e=>handleFatal(e,`uncaughtException`)),process.on(`unhandledRejection`,e=>handleFatal(e,`unhandledRejection`))}export{p as a,m as i,reportCrash as n,parseCrashReportConfig as o,sendCrashReport as r,initCrashReporting as t};
42
- //# sourceMappingURL=crashreport-BAG8c7hB.mjs.map
42
+ //# sourceMappingURL=crashreport-BCIy33Cz.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"crashreport-BAG8c7hB.mjs","names":["logger","redactSecrets","readPackageJson","userAgentFromVersion","writeSecretFile","ensureSecretDir","path","fs","crypto","os","isCI","localEnabled","remoteEnabled","localDir","xdgConfig","JSON_FOOTER_MARKER","CRASH_LOG_EXTENSION","report","files","dir","f","file","filename","filePath","content","endpoint","response","ua","data","HOME_DIR","UUID_PATTERN","LONG_HEX_PATTERN","EMAIL_PATTERN","ABSOLUTE_PATH_PATTERN","WINDOWS_PATH_PATTERN","URL_QUERY_PATTERN","EMAIL_TEST_PATTERN","WINDOWS_DRIVE_TEST_PATTERN","SDK_PACKAGE_MARKER","separator","firstFrameIndex","stack","result","sdkIndex","match","normalized","message","redactNext","arg","argv","eqIndex","args","commandParts","options","isError","error","rawMessage","rawStack","errorName","currentUser","configPath","raw","parseYAML","email","user","config","sdkVersion","errorType","reason"],"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 { redactSecrets } from \"#/cli/shared/logger\";\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: `redactSecrets` masks every registered\n * secret first, then the pattern-based sanitizers below strip known-shape values (UUIDs,\n * long hex, emails, paths) from what's left. `redactSecrets` must run first — the path\n * sanitizer intentionally keeps a path's basename (e.g. `/home/user/.../key.json` becomes\n * `<path>/key.json`), so a registered secret shaped like a path would have that basename\n * survive if the pattern sanitizer ran on it first. This report is written to a local file\n * (and optionally sent remotely) outside the CLI's normal stderr path.\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(redactSecrets(parseCommand())),\n argv: sanitizeArgv(process.argv.map(redactSecrets)),\n errorName: redactSecrets(errorName),\n errorMessage: sanitizeMessage(redactSecrets(rawMessage)),\n stackTrace: sanitizeStackTrace(redactSecrets(rawStack)),\n errorType,\n userId: currentUser?.id ? redactSecrets(currentUser.id) : null,\n userEmail: currentUser?.email ? redactSecrets(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-BCIy33Cz.mjs","names":["logger","redactSecrets","readPackageJson","userAgentFromVersion","writeSecretFile","ensureSecretDir","path","fs","crypto","os","isCI","localEnabled","remoteEnabled","localDir","xdgConfig","JSON_FOOTER_MARKER","CRASH_LOG_EXTENSION","report","files","dir","f","file","filename","filePath","content","endpoint","response","ua","data","HOME_DIR","UUID_PATTERN","LONG_HEX_PATTERN","EMAIL_PATTERN","ABSOLUTE_PATH_PATTERN","WINDOWS_PATH_PATTERN","URL_QUERY_PATTERN","EMAIL_TEST_PATTERN","WINDOWS_DRIVE_TEST_PATTERN","SDK_PACKAGE_MARKER","separator","firstFrameIndex","stack","result","sdkIndex","match","normalized","message","redactNext","arg","argv","eqIndex","args","commandParts","options","isError","error","rawMessage","rawStack","errorName","currentUser","configPath","raw","parseYAML","email","user","config","sdkVersion","errorType","reason"],"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 { redactSecrets } from \"#/cli/shared/logger\";\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: `redactSecrets` masks every registered\n * secret first, then the pattern-based sanitizers below strip known-shape values (UUIDs,\n * long hex, emails, paths) from what's left. `redactSecrets` must run first — the path\n * sanitizer intentionally keeps a path's basename (e.g. `/home/user/.../key.json` becomes\n * `<path>/key.json`), so a registered secret shaped like a path would have that basename\n * survive if the pattern sanitizer ran on it first. This report is written to a local file\n * (and optionally sent remotely) outside the CLI's normal stderr path.\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(redactSecrets(parseCommand())),\n argv: sanitizeArgv(process.argv.map(redactSecrets)),\n errorName: redactSecrets(errorName),\n errorMessage: sanitizeMessage(redactSecrets(rawMessage)),\n stackTrace: sanitizeStackTrace(redactSecrets(rawStack)),\n errorType,\n userId: currentUser?.id ? redactSecrets(currentUser.id) : null,\n userEmail: currentUser?.email ? redactSecrets(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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"}
@@ -0,0 +1 @@
1
+ export{n as reportCrash}from"./crashreport-BCIy33Cz.mjs";
@@ -0,0 +1,5 @@
1
+ import{s as e,u as t}from"./logger-RgtbhJ83.mjs";const n=/^[A-Za-z0-9_./:=@+-]+$/,r=/^[A-Za-z0-9_./:=@+\\-]+$/,i=/[%$"`“”„\p{Cc}]|\\$|^~(?:[\\/]|$)/u,a=/"|\s.*\\$|^~(?:[\\/]|$)/;function quotePosixArg(e){return n.test(e)?e:`'${e.replaceAll(`'`,`'"'"'`)}'`}function isPowerShellBare(e){return r.test(e)&&!/^(@|[+-]?\.?\d)/.test(e)&&e!==`--`&&!/^-[^-].*[.:]/.test(e)}function quotePowerShellArg(e){return isPowerShellBare(e)?e:`'${e.replace(/['‘’‚‛]/g,`$&$&`)}'`}function quoteCmdArg(e){if(r.test(e)&&!e.endsWith(`\\`))return e;let t=`"`,n=0;for(let r of e)r===`\\`?n++:(t+=r===`"`?`${`\\`.repeat(n*2)}""`:`${`\\`.repeat(n)}${r===`%`?`%%cd:~,%`:r}`,n=0);return`${t}${`\\`.repeat(n*2)}"`}function formatWindowsCommandLines(e){if(e.every(e=>isPowerShellBare(e)||e!==``&&!i.test(e)))return{kind:`shared`,commandLine:e.map(e=>isPowerShellBare(e)?e:`"${e}"`).join(` `)};let t=e.map(quoteCmdArg).join(` `);return{kind:`perShell`,powershell:e.some(e=>e===``||a.test(e))?`cmd /d /s /c ${quotePowerShellArg(t)}`:e.map(quotePowerShellArg).join(` `),cmd:t}}function formatShellCommandLines(e){return process.platform===`win32`?formatWindowsCommandLines(e):{kind:`shared`,commandLine:e.map(quotePosixArg).join(` `)}}function formatCommandHint(t){let n=formatShellCommandLines([t.command,...t.args].map(t=>e(t)));return n.kind===`shared`?`\`${n.commandLine}\``:`\`${n.powershell}\` in PowerShell or \`${n.cmd}\` in cmd.exe`}function redactAndIndent(t){return e(t).split(`
2
+ `).join(`
3
+ `)}function formatError(e){let n=[t.error(`Error${e.code?` [${e.code}]`:``}: ${e.message}`)];return e.details&&n.push(`\n ${t.dim(`Details:`)} ${redactAndIndent(e.details)}`),e.suggestion&&n.push(`\n ${t.info(`Suggestion:`)} ${redactAndIndent(e.suggestion)}`),e.command&&n.push(`\n ${t.dim(`Help:`)} Run \`tailor ${e.command} --help\` for usage information.`),e.next&&n.push(`\n ${t.info(`Next:`)} Run ${formatCommandHint(e.next)}.`),n.join(``)}function CLIError(e){let t=Error(e.message,e.cause===void 0?void 0:{cause:e.cause});return t.name=`CLIError`,t.code=e.code,t.details=e.details,t.suggestion=e.suggestion,t.command=e.command,t.next=e.next,t.context=e.context,t.format=()=>formatError(t),t}function isCLIError(e){return e instanceof Error&&e.name===`CLIError`}function errorSummary(e){return isCLIError(e)?[e.message,e.details,e.suggestion].filter(Boolean).join(`
4
+ `):e instanceof Error?e.message:String(e)}function internalError(e,t){return Error(e,t)}function toError(e){return e instanceof Error?e:Error(String(e),{cause:e})}const o=/does not provide an export named '(?!default')([^']+)'/;function typeOnlyImportHint(e){if(!(e instanceof SyntaxError))return;let t=o.exec(e.message)?.[1];if(t)return`If '${t}' is a type, import it with \`import type\` (or the inline \`type\` modifier). The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. Set "verbatimModuleSyntax": true in tsconfig.json to catch this at typecheck.`}export{isCLIError as a,internalError as i,errorSummary as n,toError as o,formatCommandHint as r,typeOnlyImportHint as s,CLIError as t};
5
+ //# sourceMappingURL=errors-BOF68LJ5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-BOF68LJ5.mjs","names":["redactSecrets","styles","POSIX_BARE_ARG","CMD_BARE_ARG","WINDOWS_DOUBLE_QUOTE_UNSAFE","POWERSHELL_VERSION_SENSITIVE","value","quoted","backslashes","char","argv","cmd","commandLines","action","arg","text","parts","error","options","message","MISSING_NAMED_EXPORT_PATTERN","name"],"sources":["../src/cli/shared/shell-quote.ts","../src/cli/shared/errors.ts"],"sourcesContent":["/**\n * Command lines for a suggested command: one line when every supported shell reads it the same\n * way, otherwise one line per Windows shell.\n */\nexport type ShellCommandLines =\n | { kind: \"shared\"; commandLine: string }\n | { kind: \"perShell\"; powershell: string; cmd: string };\n\nconst POSIX_BARE_ARG = /^[A-Za-z0-9_./:=@+-]+$/;\nconst CMD_BARE_ARG = /^[A-Za-z0-9_./:=@+\\\\-]+$/;\n// PowerShell expands `$` and backtick escapes inside double quotes and reads `“ ” „` as `\"`,\n// cmd.exe expands `%` even inside quotes, a `\"` or a trailing backslash changes where the\n// quoted argument ends, and a leading `~` is version-sensitive (below).\nconst WINDOWS_DOUBLE_QUOTE_UNSAFE = /[%$\"`“”„\\p{Cc}]|\\\\$|^~(?:[\\\\/]|$)/u;\n// PowerShell versions pass these to programs differently: Windows PowerShell 5.1 and 7.3+\n// disagree on quotes and trailing backslashes, and 7.6+ expands a leading `~` inside the `.ps1`\n// shim however it was quoted.\nconst POWERSHELL_VERSION_SENSITIVE = /\"|\\s.*\\\\$|^~(?:[\\\\/]|$)/;\n\nfunction quotePosixArg(value: string): string {\n if (POSIX_BARE_ARG.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction isPowerShellBare(value: string): boolean {\n // A leading `@` splats, a lone `--` ends parameters, a leading digit reads as a number, and a\n // single-dash token with `.` or `:` reads as parameter syntax.\n return (\n CMD_BARE_ARG.test(value) &&\n !/^(@|[+-]?\\.?\\d)/.test(value) &&\n value !== \"--\" &&\n !/^-[^-].*[.:]/.test(value)\n );\n}\n\nfunction quotePowerShellArg(value: string): string {\n if (isPowerShellBare(value)) return value;\n return `'${value.replace(/['‘’‚‛]/g, \"$&$&\")}'`;\n}\n\nfunction quoteCmdArg(value: string): string {\n // Quoting a trailing backslash keeps a command routed through `cmd /c` from ending in one,\n // which PowerShell 7 doubles when it quotes the line.\n if (CMD_BARE_ARG.test(value) && !value.endsWith(\"\\\\\")) return value;\n let quoted = '\"';\n let backslashes = 0;\n for (const char of value) {\n if (char === \"\\\\\") {\n backslashes++;\n continue;\n }\n if (char === '\"') {\n quoted += `${\"\\\\\".repeat(backslashes * 2)}\"\"`;\n } else {\n // `%%cd:~,%` reads back as a literal `%` without cmd.exe expanding a `%NAME%` around it.\n quoted += `${\"\\\\\".repeat(backslashes)}${char === \"%\" ? \"%%cd:~,%\" : char}`;\n }\n backslashes = 0;\n }\n return `${quoted}${\"\\\\\".repeat(backslashes * 2)}\"`;\n}\n\nfunction formatWindowsCommandLines(argv: readonly string[]): ShellCommandLines {\n if (\n argv.every(\n (value) =>\n isPowerShellBare(value) || (value !== \"\" && !WINDOWS_DOUBLE_QUOTE_UNSAFE.test(value)),\n )\n ) {\n return {\n kind: \"shared\",\n commandLine: argv.map((value) => (isPowerShellBare(value) ? value : `\"${value}\"`)).join(\" \"),\n };\n }\n const cmd = argv.map(quoteCmdArg).join(\" \");\n // Both PowerShell versions pass a `cmd` argument the same legacy way, so routing the cmd.exe\n // line through `cmd /c` keeps the arguments identical where their native quoting differs.\n const powershell = argv.some((value) => value === \"\" || POWERSHELL_VERSION_SENSITIVE.test(value))\n ? `cmd /d /s /c ${quotePowerShellArg(cmd)}`\n : argv.map(quotePowerShellArg).join(\" \");\n return { kind: \"perShell\", powershell, cmd };\n}\n\n/**\n * Render an argv array as command lines that the current platform's shells run with every\n * argument delivered unchanged\n * @param {readonly string[]} argv - Executable name followed by its arguments\n * @returns {ShellCommandLines} One shared command line, or one per Windows shell\n */\nexport function formatShellCommandLines(argv: readonly string[]): ShellCommandLines {\n if (process.platform === \"win32\") return formatWindowsCommandLines(argv);\n return { kind: \"shared\", commandLine: argv.map(quotePosixArg).join(\" \") };\n}\n","import { redactSecrets, styles } from \"./logger\";\nimport { formatShellCommandLines } from \"./shell-quote\";\nimport type { Jsonifiable } from \"type-fest\";\n\n/**\n * Options for creating a CLI error\n */\ninterface CLIErrorOptions {\n /** Stable machine-readable failure code, such as `WORKSPACE_NOT_FOUND`. */\n code: string;\n message: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n cause?: unknown;\n}\n\nexport type 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, Jsonifiable | undefined>>;\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, Jsonifiable | undefined>>;\n format(): string;\n};\n\n/**\n * Render a command as a user-facing hint that can be copied into the current platform's shells:\n * one backticked command line, or on Windows, when no single quoting suits both shells, a\n * PowerShell and a cmd.exe line (`` `…` in PowerShell or `…` in cmd.exe ``).\n * Registered secrets are redacted before quoting, which could otherwise split them.\n * @param {CLIErrorNextAction} action - Executable and arguments to suggest\n * @returns {string} The command part of the hint, to follow a verb such as \"Run\"\n */\nexport function formatCommandHint(action: CLIErrorNextAction): string {\n const commandLines = formatShellCommandLines(\n [action.command, ...action.args].map((arg) => redactSecrets(arg)),\n );\n if (commandLines.kind === \"shared\") {\n return `\\`${commandLines.commandLine}\\``;\n }\n return `\\`${commandLines.powershell}\\` in PowerShell or \\`${commandLines.cmd}\\` in cmd.exe`;\n}\n\n/**\n * Redact registered secrets, then indent every line after the first.\n * Redacting first keeps the indent from splitting a multi-line secret.\n * @param text - Details or suggestion text\n * @returns Redacted text with its continuation lines indented\n */\nfunction redactAndIndent(text: string): string {\n return redactSecrets(text).split(\"\\n\").join(\"\\n \");\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:\")} ${redactAndIndent(error.details)}`);\n }\n\n if (error.suggestion) {\n parts.push(`\\n ${styles.info(\"Suggestion:\")} ${redactAndIndent(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 ${formatCommandHint(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 */\nexport function CLIError(options: CLIErrorOptions): CLIError {\n const error = new Error(\n options.message,\n options.cause === undefined ? undefined : { cause: options.cause },\n ) 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\n/**\n * Summarize a failure as plain text for reports that carry a single error string.\n * CLI errors contribute their details and suggestion so the remediation survives.\n * @param error - Failure to summarize\n * @returns Message followed by any details and suggestion, one per line\n */\nexport function errorSummary(error: unknown): string {\n if (isCLIError(error)) {\n return [error.message, error.details, error.suggestion].filter(Boolean).join(\"\\n\");\n }\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Create an error for an SDK invariant violation that no user action can fix.\n * The result is a plain Error, so JSON output reports it as `UNEXPECTED_ERROR`\n * and crash reporting treats it like any other unexpected failure.\n * @param message - Description of the violated invariant\n * @param options - Standard Error options, such as `cause`\n * @returns Plain Error instance\n */\nexport function internalError(message: string, options?: ErrorOptions): Error {\n return new Error(message, options);\n}\n\n/**\n * Convert a caught value into an Error, keeping Error instances as-is\n * @param value - Caught value\n * @returns The value itself when it is an Error, otherwise an Error of its string form with the value as `cause`\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value), { cause: value });\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"],"mappings":"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"}
@@ -1,5 +1,8 @@
1
- import{t as e}from"./multiline-EyzjEwn9.mjs";import{resolve as t}from"pathe";const n=new TextEncoder;function mapFieldTypeToPostgresType(e){switch(e){case`uuid`:return`uuid`;case`string`:case`enum`:return`text`;case`boolean`:case`bool`:return`boolean`;case`integer`:return`integer`;case`float`:return`double precision`;case`decimal`:return`numeric`;case`date`:return`date`;case`datetime`:return`timestamptz`;case`time`:return`time`;case`nested`:return`jsonb`;default:throw Error(`Field type "${e}" has no PostgreSQL column type.`)}}function identifier(e){return`"${e.replaceAll(`"`,`""`)}"`}function fnv1aHex(e){let t=2166136261;for(let r of n.encode(e))t=Math.imul(t^r,16777619)>>>0;return t.toString(16).padStart(8,`0`)}function derivedIdentifier(e,t,r){let i=`_${fnv1aHex(JSON.stringify([e,t,r]))}_${r}`,a=`${e}_${t}`;for(;n.encode(a+i).length>63;)a=a.slice(0,-1);return identifier(a+i)}function stringLiteral(e){return`'${e.replaceAll(`'`,`''`)}'`}function columnType(e){let t=e.type===`decimal`?`numeric(1000, ${e.scale??6})`:mapFieldTypeToPostgresType(e.type);return e.array&&e.type!==`nested`?`${t}[]`:t}function scalarLiteral(e,t,n){let r=mapFieldTypeToPostgresType(t.type);if(e instanceof Date){if(Number.isNaN(e.getTime()))throw Error(`Default of field ${n} is an invalid Date.`);return`${stringLiteral(e.toISOString())}::${r}`}switch(typeof e){case`string`:return r===`text`?stringLiteral(e):`${stringLiteral(e)}::${r}`;case`number`:if(!Number.isFinite(e))throw Error(`Default of field ${n} is not a finite number.`);return String(e);case`boolean`:return e?`TRUE`:`FALSE`;default:throw Error(`Default of field ${n} cannot be rendered as a SQL literal.`)}}const r=/* @__PURE__ */ new Map([[`datetime`,`now()`],[`date`,`CURRENT_DATE`],[`time`,`LOCALTIME`]]);function defaultExpression(e,t){let n=e.default,i=r.get(e.type);if(n===`now`&&i!==void 0)return i;if(e.array){if(!Array.isArray(n))throw Error(`Default of array field ${t} must be an array.`);let r=columnType(e);return n.length===0?`'{}'::${r}`:`ARRAY[${n.map(n=>scalarLiteral(n,e,t)).join(`, `)}]::${r}`}return scalarLiteral(n,e,t)}function parseSerialFormat(e,t){let n=/^(?<prefix>[^%]*)%(?<zero>0?)(?<width>\d*)(?<conversion>[a-zA-Z])(?<suffix>[^%]*)$/.exec(e)?.groups,r=n?.conversion;if(!n||r!==`d`&&r!==`x`&&r!==`X`)throw Error(`Serial format "${e}" of field ${t} is not supported; use a single %d, %x, or %X specifier with an optional width (e.g. "INV-%05d").`);return{prefix:n.prefix??``,zeroPad:n.zero===`0`,width:n.width?Number(n.width):0,conversion:r,suffix:n.suffix??``}}function sequenceName(e,t){return derivedIdentifier(e,t,`seq`)}function sequenceRange(e){let t=[`START WITH ${e.start}`,`MINVALUE ${e.start}`];return e.maxValue!==void 0&&t.push(`MAXVALUE ${e.maxValue}`),t.join(` `)}const i={d:e=>e,x:e=>`to_hex(${e})`,X:e=>`upper(to_hex(${e}))`};function serialStringDefault(e,t,n){let r=`nextval(${stringLiteral(e)})`;if(t===void 0)return`(${r}::text)`;let{prefix:a,width:o,zeroPad:s,conversion:c,suffix:l}=parseSerialFormat(t,n),u=i[c](r),d=c===`d`?`${u}::text`:u;return o>0&&(d=`format('%${o}s', ${u})`,s&&(d=`translate(${d}, ' ', '0')`)),`(${[...a?[stringLiteral(a)]:[],d,...l?[stringLiteral(l)]:[]].join(` || `)})`}function columnDefinition(e,t,n){let r=identifier(t),i=`${identifier(e)}.${r}`,a=[r,columnType(n)];if(n.serial)return n.type===`integer`?(a.push(`GENERATED BY DEFAULT AS IDENTITY (${sequenceRange(n.serial)})`),n.unique&&a.push(`UNIQUE`),a.join(` `)):(n.required&&a.push(`NOT NULL`),n.unique&&a.push(`UNIQUE`),a.push(`DEFAULT ${serialStringDefault(sequenceName(e,t),n.serial.format,i)}`),a.join(` `));let o=n.default!==void 0,s=n.hooks?.create!==void 0||n.optionalOnCreate===!0;return n.required&&(o||!s)&&a.push(`NOT NULL`),n.unique&&a.push(`UNIQUE`),o&&a.push(`DEFAULT ${defaultExpression(n,i)}`),a.join(` `)}function generateTableDDL(e){let t=identifier(e.name),n=[],r=[],i=[` "id" uuid PRIMARY KEY DEFAULT gen_random_uuid()`];for(let[a,o]of Object.entries(e.fields))if(a!==`id`){if(o.serial&&o.type!==`integer`){let i=sequenceName(e.name,a);n.push(`CREATE SEQUENCE IF NOT EXISTS ${i} ${sequenceRange(o.serial)}`),r.push(`ALTER SEQUENCE ${i} OWNED BY ${t}.${identifier(a)}`)}i.push(` ${columnDefinition(e.name,a,o)}`)}let a=Object.entries(e.indexes??{}).filter(([,e])=>e.unique).map(([n,r])=>`CREATE UNIQUE INDEX IF NOT EXISTS ${derivedIdentifier(e.name,n,`idx`)} ON ${t} (${r.fields.map(identifier).join(`, `)})`);return[...n,`CREATE TABLE IF NOT EXISTS ${t} (\n${i.join(`,
2
- `)}\n)`,...r,...a]}function generateSchemaDDL(e){return e.map(e=>generateTableDDL(e).map(e=>`${e};`).join(`
1
+ import{t as e}from"./multiline-EyzjEwn9.mjs";import{resolve as t}from"pathe";const n=new TextEncoder;function mapFieldTypeToPostgresType(e){switch(e){case`uuid`:return`uuid`;case`string`:case`enum`:return`text`;case`boolean`:case`bool`:return`boolean`;case`integer`:return`integer`;case`float`:return`double precision`;case`decimal`:return`numeric`;case`date`:return`date`;case`datetime`:return`timestamptz`;case`time`:return`time`;case`nested`:return`jsonb`;default:throw Error(`Field type "${e}" has no PostgreSQL column type.`)}}function identifier(e){return`"${e.replaceAll(`"`,`""`)}"`}function fnv1aHex(e){let t=2166136261;for(let r of n.encode(e))t=Math.imul(t^r,16777619)>>>0;return t.toString(16).padStart(8,`0`)}function derivedName(e,t,r){let i=`_${fnv1aHex(JSON.stringify([e,t,r]))}_${r}`,a=`${e}_${t}`;for(;n.encode(a+i).length>63;)a=a.slice(0,-1);return a+i}function derivedIdentifier(e,t,n){return identifier(derivedName(e,t,n))}function dollarQuoted(e){let t=`$fk$`;for(;e.includes(t);)t=`${t.slice(0,-1)}_$`;return`${t}\n${e}\n${t}`}function stringLiteral(e){return`'${e.replaceAll(`'`,`''`)}'`}function columnType(e){let t=e.type===`decimal`?`numeric(1000, ${e.scale??6})`:mapFieldTypeToPostgresType(e.type);return e.array&&e.type!==`nested`?`${t}[]`:t}function scalarLiteral(e,t,n){let r=mapFieldTypeToPostgresType(t.type);if(e instanceof Date){if(Number.isNaN(e.getTime()))throw Error(`Default of field ${n} is an invalid Date.`);return`${stringLiteral(e.toISOString())}::${r}`}switch(typeof e){case`string`:return r===`text`?stringLiteral(e):`${stringLiteral(e)}::${r}`;case`number`:if(!Number.isFinite(e))throw Error(`Default of field ${n} is not a finite number.`);return String(e);case`boolean`:return e?`TRUE`:`FALSE`;default:throw Error(`Default of field ${n} cannot be rendered as a SQL literal.`)}}const r=/* @__PURE__ */ new Map([[`datetime`,`now()`],[`date`,`CURRENT_DATE`],[`time`,`LOCALTIME`]]);function defaultExpression(e,t){let n=e.default,i=r.get(e.type);if(n===`now`&&i!==void 0)return i;if(e.array){if(!Array.isArray(n))throw Error(`Default of array field ${t} must be an array.`);let r=columnType(e);return n.length===0?`'{}'::${r}`:`ARRAY[${n.map(n=>scalarLiteral(n,e,t)).join(`, `)}]::${r}`}return scalarLiteral(n,e,t)}function parseSerialFormat(e,t){let n=/^(?<prefix>[^%]*)%(?<zero>0?)(?<width>\d*)(?<conversion>[a-zA-Z])(?<suffix>[^%]*)$/.exec(e)?.groups,r=n?.conversion;if(!n||r!==`d`&&r!==`x`&&r!==`X`)throw Error(`Serial format "${e}" of field ${t} is not supported; use a single %d, %x, or %X specifier with an optional width (e.g. "INV-%05d").`);return{prefix:n.prefix??``,zeroPad:n.zero===`0`,width:n.width?Number(n.width):0,conversion:r,suffix:n.suffix??``}}function sequenceName(e,t){return derivedIdentifier(e,t,`seq`)}function sequenceRange(e){let t=[`START WITH ${e.start}`,`MINVALUE ${e.start}`];return e.maxValue!==void 0&&t.push(`MAXVALUE ${e.maxValue}`),t.join(` `)}const i={d:e=>e,x:e=>`to_hex(${e})`,X:e=>`upper(to_hex(${e}))`};function serialStringDefault(e,t,n){let r=`nextval(${stringLiteral(e)})`;if(t===void 0)return`(${r}::text)`;let{prefix:a,width:o,zeroPad:s,conversion:c,suffix:l}=parseSerialFormat(t,n),u=i[c](r),d=c===`d`?`${u}::text`:u;return o>0&&(d=`format('%${o}s', ${u})`,s&&(d=`translate(${d}, ' ', '0')`)),`(${[...a?[stringLiteral(a)]:[],d,...l?[stringLiteral(l)]:[]].join(` || `)})`}function columnDefinition(e,t,n){let r=identifier(t),i=`${identifier(e)}.${r}`,a=[r,columnType(n)];if(n.serial)return n.type===`integer`?(a.push(`GENERATED BY DEFAULT AS IDENTITY (${sequenceRange(n.serial)})`),n.unique&&a.push(`UNIQUE`),a.join(` `)):(n.required&&a.push(`NOT NULL`),n.unique&&a.push(`UNIQUE`),a.push(`DEFAULT ${serialStringDefault(sequenceName(e,t),n.serial.format,i)}`),a.join(` `));let o=n.default!==void 0,s=n.hooks?.create!==void 0||n.optionalOnCreate===!0;return n.required&&(o||!s)&&a.push(`NOT NULL`),n.unique&&a.push(`UNIQUE`),o&&a.push(`DEFAULT ${defaultExpression(n,i)}`),a.join(` `)}function jsonPathKey(e){return`"${e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`)}"`}function referencesWithin(e,t,n){let r=t.array?`${n}[*]`:n;return t.type===`nested`?Object.entries(t.fields??{}).flatMap(([t,n])=>referencesWithin(e,n,`${r}.${jsonPathKey(t)}`)):!t.foreignKey||t.foreignKeyType===void 0?[]:[{column:e,path:r,table:t.foreignKeyType,field:t.foreignKeyField??`id`,type:t.type}]}function tableReferences(e){return Object.entries(e.fields).filter(([e])=>e!==`id`).flatMap(([e,t])=>referencesWithin(e,t,`$`))}function isConstraintReference(e,t){if(e.path!==`$`)return!1;let n=e.field===`id`?`uuid`:t.get(e.table)?.fields[e.field]?.type;return n===void 0||mapFieldTypeToPostgresType(n)===mapFieldTypeToPostgresType(e.type)}function referenceClause(e){return`REFERENCES ${identifier(e.table)} (${identifier(e.field)})`}function declaredType(e){return e===`bool`?`boolean`:e}function referencedColumnType(e,t,n){let r=t.get(e.table);if(!r)throw Error(`Field ${n} references unknown table "${e.table}".`);let i=e.field===`id`?{type:`uuid`,unique:!0}:r.fields[e.field];if(!i)throw Error(`Field ${n} references unknown field "${e.field}" of table "${e.table}".`);let a=e.field===`id`||declaredType(i.type)===declaredType(e.type);if(!i.unique||!a)throw Error(`Field ${n} references "${e.table}"."${e.field}", which must be unique and of the same type as the referencing field.`);return mapFieldTypeToPostgresType(i.type)}function referenceCheck(e,t,n){let r=referencedColumnType(e,t,n),i=r===mapFieldTypeToPostgresType(e.type)?`parent.${identifier(e.field)} = (fk_check.v #>> '{}')::${r}`:`parent.${identifier(e.field)}::text = (fk_check.v #>> '{}')`,a=`violates foreign key constraints: % not found in ${`${identifier(e.table)}.${identifier(e.field)}`.replaceAll(`%`,`%%`)}`;return[` FOR v IN SELECT jsonb_path_query(to_jsonb(NEW.${identifier(e.column)}), ${stringLiteral(e.path)}) LOOP`,` IF jsonb_typeof(fk_check.v) <> 'null' AND NOT EXISTS (SELECT 1 FROM ${identifier(e.table)} AS parent WHERE ${i}) THEN`,` RAISE EXCEPTION ${stringLiteral(a)}, fk_check.v #>> '{}' USING ERRCODE = 'foreign_key_violation';`,` END IF;`,` END LOOP;`].join(`
2
+ `)}function referenceTrigger(e,t,n){if(t.length===0)return[];let r=derivedIdentifier(e.name,`references`,`fk`);return[`CREATE OR REPLACE FUNCTION ${r}() RETURNS trigger LANGUAGE plpgsql AS ${dollarQuoted([`<<fk_check>>`,`DECLARE v jsonb;`,`BEGIN`,...t.map(t=>referenceCheck(t,n,`${e.name}.${t.column}`)),` RETURN NULL;`,`END`].join(`
3
+ `))}`,`CREATE OR REPLACE TRIGGER ${r} AFTER INSERT OR UPDATE ON ${identifier(e.name)} FOR EACH ROW EXECUTE FUNCTION ${r}()`]}function generateTableDDL(e,t={}){let n=identifier(e.name),r=new Map((t.tables??[e]).map(e=>[e.name,e])),i=[],a=[],o=[` "id" uuid PRIMARY KEY DEFAULT gen_random_uuid()`],s=tableReferences(e),c=new Map(s.filter(e=>isConstraintReference(e,r)).map(e=>[e.column,e]));for(let[r,s]of Object.entries(e.fields)){if(r===`id`)continue;if(s.serial&&s.type!==`integer`){let t=sequenceName(e.name,r);i.push(`CREATE SEQUENCE IF NOT EXISTS ${t} ${sequenceRange(s.serial)}`),a.push(`ALTER SEQUENCE ${t} OWNED BY ${n}.${identifier(r)}`)}let l=c.get(r),u=l&&!t.deferredReferences?.has(r)?` ${referenceClause(l)}`:``;o.push(` ${columnDefinition(e.name,r,s)}${u}`)}let l=Object.entries(e.indexes??{}).filter(([,e])=>e.unique).map(([t,r])=>`CREATE UNIQUE INDEX IF NOT EXISTS ${derivedIdentifier(e.name,t,`idx`)} ON ${n} (${r.fields.map(identifier).join(`, `)})`);for(let t of c.values())referencedColumnType(t,r,`${e.name}.${t.column}`);return[...i,`CREATE TABLE IF NOT EXISTS ${n} (\n${o.join(`,
4
+ `)}\n)`,...a,...l,...referenceTrigger(e,s.filter(e=>!isConstraintReference(e,r)),r)]}function addForeignKey(e,t){let n=derivedName(e.name,t.column,`fk`),r=identifier(e.name);return`DO ${dollarQuoted([`BEGIN`,`IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = ${stringLiteral(n)} AND conrelid = ${stringLiteral(r)}::regclass) THEN`,`ALTER TABLE ${r} ADD CONSTRAINT ${identifier(n)} FOREIGN KEY (${identifier(t.column)}) ${referenceClause(t)};`,`END IF;`,`END`].join(`
5
+ `))}`}function planCreation(e){let t=new Set(e.map(e=>e.name)),n=new Map(e.map(e=>[e.name,e])),r=/* @__PURE__ */ new Set,i=[...e],a=[],o=/* @__PURE__ */ new Map,pendingOf=e=>tableReferences(e).filter(e=>isConstraintReference(e,n)).filter(n=>t.has(n.table)&&n.table!==e.name&&!r.has(n.table));for(;i.length>0;){let e=i.findIndex(e=>pendingOf(e).length===0),[t]=i.splice(Math.max(e,0),1),n=pendingOf(t);n.length>0&&o.set(t,n),r.add(t.name),a.push(t)}return{ordered:a,deferred:o}}function generateSchemaDDL(e){let{ordered:t,deferred:n}=planCreation(e),r=t.map(t=>generateTableDDL(t,{tables:e,deferredReferences:new Set(n.get(t)?.map(e=>e.column))})),i=t.flatMap(e=>(n.get(e)??[]).map(t=>addForeignKey(e,t)));return[...r,i].filter(e=>e.length>0).map(e=>e.map(e=>`${e};`).join(`
3
6
  `)).join(`
4
7
 
5
8
  `)}function templateLiteral(e){return`\`${e.replaceAll(`\\`,`\\\\`).replaceAll("`","\\`").replaceAll("${","\\${").replaceAll(`\r`,`\\r`)}\``}function generatePgliteSchemaModule(e,t){let n=e.map(({namespace:e,tables:t})=>` ${JSON.stringify(e)}: ${templateLiteral(generateSchemaDDL(t))},`);return[`/**`,` * Auto-generated PGlite schema for TailorDB tables.`," * Create a namespace's tables in a test with `pglite.exec(pgliteSchema[namespace])`.",` *`,` * DO NOT EDIT - This file is auto-generated by ${t.generatedBy}.`,` */`,``,`export const pgliteSchema = {`,...n,`} as const;`,``].join(`
@@ -45,4 +48,4 @@ import{t as e}from"./multiline-EyzjEwn9.mjs";import{resolve as t}from"pathe";con
45
48
 
46
49
  `)+`
47
50
  `}const o=`@tailor-platform/kysely-type`,s=`./generated/tailordb.ts`;function kyselyTypePlugin(e){return{id:o,description:`Generates Kysely type definitions for TailorDB tables`,pluginConfig:e,async onTailorDBReady(e){let{distPath:n,pgliteSchemaPath:r,temporal:i}=e.pluginConfig;if(r&&t(n)===t(r))throw Error(`distPath and pgliteSchemaPath must resolve to different files.`);let a=[];for(let t of e.tailordb){let e=[];for(let n of Object.values(t.tables)){let t=await processKyselyType(n,i);e.push(t)}e.length!==0&&a.push({namespace:t.namespace,types:e})}let o=[];if(a.length>0){let t=generateUnifiedKyselyTypes(a,i);o.push({path:e.pluginConfig.distPath,content:t}),e.pluginConfig.pgliteSchemaPath&&o.push({path:e.pluginConfig.pgliteSchemaPath,content:generatePGliteSchemaModule(e.tailordb.filter(e=>Object.keys(e.tables).length>0))})}return{files:o}}}}export{processKyselyFields as a,generatePgliteSchemaModule as c,generateUnifiedKyselyTypes as i,o as n,a as o,kyselyTypePlugin as r,mapFieldTypeToColumnType as s,s as t};
48
- //# sourceMappingURL=kysely-type-BWMfV301.mjs.map
51
+ //# sourceMappingURL=kysely-type-CBwsdx16.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-type-CBwsdx16.mjs","names":["multiline","utf8","fieldType","name","hash","byte","value","suffix","tableName","memberName","kind","head","tag","body","base","field","pgType","label","CURRENT_TIME_EXPRESSIONS","currentTime","v","groups","format","conversion","fieldName","range","serial","SERIAL_CONVERSIONS","nextval","sequence","number","width","zeroPad","prefix","column","parts","hasDefault","filledOnCreate","key","here","path","child","table","reference","targetType","tables","type","target","targetField","sameType","comparison","message","references","tableIdentifier","options","t","sequences","ownerships","columns","constraintReferences","r","constraint","inline","indexes","index","names","byName","created","remaining","ordered","deferred","pending","statements","constraints","group","statement","entries","namespaces","namespace","temporal","COLUMN_TYPE_ALIASES","a","b","usedUtilityTypes","allowedValues","fieldConfig","fields","fieldResults","config","result","aggregatedUtilityTypes","acc","fieldTypes","obj","hasOptionalFields","hasGeneratedFields","nested","baseTypeResult","isArray","isNullable","isColumnTypeBase","finalType","fieldLines","parsedField","namespaceData","globalUsedUtilityTypes","ns","utilityTypeImports","types","line","KyselyGeneratorID","DEFAULT_KYSELY_TYPES_DIST_PATH","ctx","pgliteSchemaPath","resolve","distPath","allNamespaceData","typeMetadataList","metadata","files","content"],"sources":["../src/utils/tailordb-ddl.ts","../src/plugin/builtin/kysely-type/pglite-schema.ts","../src/utils/field-column-type.ts","../src/plugin/builtin/kysely-type/type-processor.ts","../src/plugin/builtin/kysely-type/index.ts"],"sourcesContent":["/**\n * PostgreSQL DDL derived from TailorDB table definitions, for creating the\n * tables a PGlite-backed test needs. The columns match the flat shape the\n * Kysely type generators emit, not TailorDB's storage layout.\n */\n\n/** The field shape the DDL generator reads; parsed and snapshot field configs are both assignable. */\nexport interface DDLFieldConfig {\n type: string;\n required?: boolean;\n array?: boolean;\n unique?: boolean;\n serial?: { start: number; maxValue?: number; format?: string };\n scale?: number;\n default?: unknown;\n optionalOnCreate?: boolean;\n foreignKey?: boolean;\n foreignKeyType?: string;\n foreignKeyField?: string;\n hooks?: { create?: unknown; update?: unknown };\n fields?: Record<string, DDLFieldConfig>;\n}\n\n/** A table to emit DDL for. */\nexport interface DDLTableConfig {\n name: string;\n fields: Record<string, DDLFieldConfig>;\n indexes?: Record<string, { fields: string[]; unique?: boolean }>;\n}\n\nconst MAX_IDENTIFIER_BYTES = 63;\n// The platform rounds decimals to their scale (6 unless configured); numeric\n// needs a precision to carry a scale, and the maximum leaves it unconstrained.\nconst DECIMAL_PRECISION = 1000;\nconst DEFAULT_DECIMAL_SCALE = 6;\nconst utf8 = new TextEncoder();\n\n/**\n * Map a TailorDB field type to the PostgreSQL column type PGlite tests use for it.\n * @param fieldType - TailorDB field type name\n * @returns The PostgreSQL type name\n * @throws If the type is not a TailorDB field type\n */\nexport function mapFieldTypeToPostgresType(fieldType: string): string {\n switch (fieldType) {\n case \"uuid\":\n return \"uuid\";\n case \"string\":\n case \"enum\":\n return \"text\";\n case \"boolean\":\n case \"bool\":\n return \"boolean\";\n case \"integer\":\n return \"integer\";\n case \"float\":\n return \"double precision\";\n case \"decimal\":\n return \"numeric\";\n case \"date\":\n return \"date\";\n case \"datetime\":\n return \"timestamptz\";\n case \"time\":\n return \"time\";\n case \"nested\":\n return \"jsonb\";\n default:\n throw new Error(`Field type \"${fieldType}\" has no PostgreSQL column type.`);\n }\n}\n\nfunction identifier(name: string): string {\n return `\"${name.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction fnv1aHex(value: string): string {\n let hash = 0x811c9dc5;\n for (const byte of utf8.encode(value)) {\n hash = Math.imul(hash ^ byte, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n\nfunction derivedName(tableName: string, memberName: string, kind: \"seq\" | \"idx\" | \"fk\"): string {\n const suffix = `_${fnv1aHex(JSON.stringify([tableName, memberName, kind]))}_${kind}`;\n let head = `${tableName}_${memberName}`;\n while (utf8.encode(head + suffix).length > MAX_IDENTIFIER_BYTES) head = head.slice(0, -1);\n return head + suffix;\n}\n\nfunction derivedIdentifier(\n tableName: string,\n memberName: string,\n kind: \"seq\" | \"idx\" | \"fk\",\n): string {\n return identifier(derivedName(tableName, memberName, kind));\n}\n\nfunction dollarQuoted(body: string): string {\n let tag = \"$fk$\";\n while (body.includes(tag)) tag = `${tag.slice(0, -1)}_$`;\n return `${tag}\\n${body}\\n${tag}`;\n}\n\nfunction stringLiteral(value: string): string {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction columnType(field: DDLFieldConfig): string {\n const base =\n field.type === \"decimal\"\n ? `numeric(${DECIMAL_PRECISION}, ${field.scale ?? DEFAULT_DECIMAL_SCALE})`\n : mapFieldTypeToPostgresType(field.type);\n return field.array && field.type !== \"nested\" ? `${base}[]` : base;\n}\n\nfunction scalarLiteral(value: unknown, field: DDLFieldConfig, label: string): string {\n const pgType = mapFieldTypeToPostgresType(field.type);\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) {\n throw new Error(`Default of field ${label} is an invalid Date.`);\n }\n return `${stringLiteral(value.toISOString())}::${pgType}`;\n }\n switch (typeof value) {\n case \"string\":\n return pgType === \"text\" ? stringLiteral(value) : `${stringLiteral(value)}::${pgType}`;\n case \"number\":\n if (!Number.isFinite(value)) {\n throw new Error(`Default of field ${label} is not a finite number.`);\n }\n return String(value);\n case \"boolean\":\n return value ? \"TRUE\" : \"FALSE\";\n default:\n throw new Error(`Default of field ${label} cannot be rendered as a SQL literal.`);\n }\n}\n\nconst CURRENT_TIME_EXPRESSIONS = new Map([\n [\"datetime\", \"now()\"],\n [\"date\", \"CURRENT_DATE\"],\n [\"time\", \"LOCALTIME\"],\n]);\n\nfunction defaultExpression(field: DDLFieldConfig, label: string): string {\n const value = field.default;\n const currentTime = CURRENT_TIME_EXPRESSIONS.get(field.type);\n if (value === \"now\" && currentTime !== undefined) {\n return currentTime;\n }\n if (field.array) {\n if (!Array.isArray(value)) {\n throw new Error(`Default of array field ${label} must be an array.`);\n }\n const pgType = columnType(field);\n if (value.length === 0) return `'{}'::${pgType}`;\n return `ARRAY[${value.map((v) => scalarLiteral(v, field, label)).join(\", \")}]::${pgType}`;\n }\n return scalarLiteral(value, field, label);\n}\n\ninterface SerialFormat {\n prefix: string;\n width: number;\n zeroPad: boolean;\n conversion: \"d\" | \"x\" | \"X\";\n suffix: string;\n}\n\n// printf-style with exactly one conversion specifier; octal has no Postgres\n// formatting function to stand in for it.\nfunction parseSerialFormat(format: string, label: string): SerialFormat {\n const match =\n /^(?<prefix>[^%]*)%(?<zero>0?)(?<width>\\d*)(?<conversion>[a-zA-Z])(?<suffix>[^%]*)$/.exec(\n format,\n );\n const groups = match?.groups;\n const conversion = groups?.conversion;\n if (!groups || (conversion !== \"d\" && conversion !== \"x\" && conversion !== \"X\")) {\n throw new Error(\n `Serial format \"${format}\" of field ${label} is not supported; use a single %d, %x, or %X specifier with an optional width (e.g. \"INV-%05d\").`,\n );\n }\n return {\n prefix: groups.prefix ?? \"\",\n zeroPad: groups.zero === \"0\",\n width: groups.width ? Number(groups.width) : 0,\n conversion,\n suffix: groups.suffix ?? \"\",\n };\n}\n\nfunction sequenceName(tableName: string, fieldName: string): string {\n return derivedIdentifier(tableName, fieldName, \"seq\");\n}\n\n// A sequence's minimum defaults to 1, which rejects a start of 0.\nfunction sequenceRange(serial: NonNullable<DDLFieldConfig[\"serial\"]>): string {\n const range = [`START WITH ${serial.start}`, `MINVALUE ${serial.start}`];\n if (serial.maxValue !== undefined) range.push(`MAXVALUE ${serial.maxValue}`);\n return range.join(\" \");\n}\n\nconst SERIAL_CONVERSIONS: Record<SerialFormat[\"conversion\"], (nextval: string) => string> = {\n d: (nextval) => nextval,\n x: (nextval) => `to_hex(${nextval})`,\n X: (nextval) => `upper(to_hex(${nextval}))`,\n};\n\nfunction serialStringDefault(sequence: string, format: string | undefined, label: string): string {\n const nextval = `nextval(${stringLiteral(sequence)})`;\n if (format === undefined) return `(${nextval}::text)`;\n const { prefix, width, zeroPad, conversion, suffix } = parseSerialFormat(format, label);\n const number = SERIAL_CONVERSIONS[conversion](nextval);\n let value = conversion === \"d\" ? `${number}::text` : number;\n if (width > 0) {\n value = `format('%${width}s', ${number})`;\n if (zeroPad) value = `translate(${value}, ' ', '0')`;\n }\n const parts = [\n ...(prefix ? [stringLiteral(prefix)] : []),\n value,\n ...(suffix ? [stringLiteral(suffix)] : []),\n ];\n return `(${parts.join(\" || \")})`;\n}\n\nfunction columnDefinition(tableName: string, fieldName: string, field: DDLFieldConfig): string {\n const column = identifier(fieldName);\n const label = `${identifier(tableName)}.${column}`;\n const parts = [column, columnType(field)];\n\n if (field.serial) {\n if (field.type === \"integer\") {\n parts.push(`GENERATED BY DEFAULT AS IDENTITY (${sequenceRange(field.serial)})`);\n if (field.unique) parts.push(\"UNIQUE\");\n return parts.join(\" \");\n }\n if (field.required) parts.push(\"NOT NULL\");\n if (field.unique) parts.push(\"UNIQUE\");\n parts.push(\n `DEFAULT ${serialStringDefault(sequenceName(tableName, fieldName), field.serial.format, label)}`,\n );\n return parts.join(\" \");\n }\n\n const hasDefault = field.default !== undefined;\n const filledOnCreate = field.hooks?.create !== undefined || field.optionalOnCreate === true;\n if (field.required && (hasDefault || !filledOnCreate)) parts.push(\"NOT NULL\");\n if (field.unique) parts.push(\"UNIQUE\");\n if (hasDefault) parts.push(`DEFAULT ${defaultExpression(field, label)}`);\n return parts.join(\" \");\n}\n\ninterface Reference {\n /** Column holding the reference */\n column: string;\n /** JSONPath from the column value to each referencing value; `$` for a scalar column */\n path: string;\n table: string;\n field: string;\n /** TailorDB type of the referencing value */\n type: string;\n}\n\nfunction jsonPathKey(key: string): string {\n return `\"${key.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"')}\"`;\n}\n\nfunction referencesWithin(column: string, field: DDLFieldConfig, path: string): Reference[] {\n const here = field.array ? `${path}[*]` : path;\n if (field.type === \"nested\") {\n return Object.entries(field.fields ?? {}).flatMap(([key, child]) =>\n referencesWithin(column, child, `${here}.${jsonPathKey(key)}`),\n );\n }\n if (!field.foreignKey || field.foreignKeyType === undefined) return [];\n return [\n {\n column,\n path: here,\n table: field.foreignKeyType,\n field: field.foreignKeyField ?? \"id\",\n type: field.type,\n },\n ];\n}\n\nfunction tableReferences(table: DDLTableConfig): Reference[] {\n return Object.entries(table.fields)\n .filter(([fieldName]) => fieldName !== \"id\")\n .flatMap(([fieldName, field]) => referencesWithin(fieldName, field, \"$\"));\n}\n\n// Postgres cannot reference from an array or jsonb column, nor from a column\n// whose type differs from the referenced one (a string field related to id),\n// so only a plain column of the same storage type becomes a FOREIGN KEY constraint.\nfunction isConstraintReference(\n reference: Reference,\n tables: ReadonlyMap<string, DDLTableConfig>,\n): boolean {\n if (reference.path !== \"$\") return false;\n const targetType =\n reference.field === \"id\" ? \"uuid\" : tables.get(reference.table)?.fields[reference.field]?.type;\n return (\n targetType === undefined ||\n mapFieldTypeToPostgresType(targetType) === mapFieldTypeToPostgresType(reference.type)\n );\n}\n\nfunction referenceClause(reference: Reference): string {\n return `REFERENCES ${identifier(reference.table)} (${identifier(reference.field)})`;\n}\n\nfunction declaredType(type: string): string {\n return type === \"bool\" ? \"boolean\" : type;\n}\n\nfunction referencedColumnType(\n reference: Reference,\n tables: ReadonlyMap<string, DDLTableConfig>,\n label: string,\n): string {\n const target = tables.get(reference.table);\n if (!target) throw new Error(`Field ${label} references unknown table \"${reference.table}\".`);\n const targetField =\n reference.field === \"id\" ? { type: \"uuid\", unique: true } : target.fields[reference.field];\n if (!targetField) {\n throw new Error(\n `Field ${label} references unknown field \"${reference.field}\" of table \"${reference.table}\".`,\n );\n }\n const sameType =\n reference.field === \"id\" || declaredType(targetField.type) === declaredType(reference.type);\n if (!targetField.unique || !sameType) {\n throw new Error(\n `Field ${label} references \"${reference.table}\".\"${reference.field}\", which must be unique and of the same type as the referencing field.`,\n );\n }\n return mapFieldTypeToPostgresType(targetField.type);\n}\n\nfunction referenceCheck(\n reference: Reference,\n tables: ReadonlyMap<string, DDLTableConfig>,\n label: string,\n): string {\n const pgType = referencedColumnType(reference, tables, label);\n const comparison =\n pgType === mapFieldTypeToPostgresType(reference.type)\n ? `parent.${identifier(reference.field)} = (fk_check.v #>> '{}')::${pgType}`\n : `parent.${identifier(reference.field)}::text = (fk_check.v #>> '{}')`;\n const target = `${identifier(reference.table)}.${identifier(reference.field)}`;\n const message = `violates foreign key constraints: % not found in ${target.replaceAll(\"%\", \"%%\")}`;\n return [\n ` FOR v IN SELECT jsonb_path_query(to_jsonb(NEW.${identifier(reference.column)}), ${stringLiteral(reference.path)}) LOOP`,\n ` IF jsonb_typeof(fk_check.v) <> 'null' AND NOT EXISTS (SELECT 1 FROM ${identifier(reference.table)} AS parent WHERE ${comparison}) THEN`,\n ` RAISE EXCEPTION ${stringLiteral(message)}, fk_check.v #>> '{}' USING ERRCODE = 'foreign_key_violation';`,\n \" END IF;\",\n \" END LOOP;\",\n ].join(\"\\n\");\n}\n\nfunction referenceTrigger(\n table: DDLTableConfig,\n references: readonly Reference[],\n tables: ReadonlyMap<string, DDLTableConfig>,\n): string[] {\n if (references.length === 0) return [];\n const name = derivedIdentifier(table.name, \"references\", \"fk\");\n const checks = references.map((reference) =>\n referenceCheck(reference, tables, `${table.name}.${reference.column}`),\n );\n const body = [\n \"<<fk_check>>\",\n \"DECLARE v jsonb;\",\n \"BEGIN\",\n ...checks,\n \" RETURN NULL;\",\n \"END\",\n ].join(\"\\n\");\n return [\n `CREATE OR REPLACE FUNCTION ${name}() RETURNS trigger LANGUAGE plpgsql AS ${dollarQuoted(body)}`,\n `CREATE OR REPLACE TRIGGER ${name} AFTER INSERT OR UPDATE ON ${identifier(table.name)} FOR EACH ROW EXECUTE FUNCTION ${name}()`,\n ];\n}\n\n/** What {@link generateTableDDL} needs to know about the other tables of the schema. */\nexport interface TableDDLOptions {\n /** Every table of the schema, to resolve the types referenced columns point at */\n tables?: readonly DDLTableConfig[];\n /** Columns whose FOREIGN KEY is added after all tables exist, because the target is created later */\n deferredReferences?: ReadonlySet<string>;\n}\n\n/**\n * DDL statements that create one table: any sequences its string serial\n * fields draw from, the table, the sequences' ownership of their columns,\n * its unique indexes, then a trigger checking the references Postgres cannot\n * express as a FOREIGN KEY (inside array and nested columns). Every statement\n * is `IF NOT EXISTS` or otherwise idempotent, so re-applying the script on a\n * database that already has the table is a no-op. Owning a sequence makes\n * `DROP TABLE` drop it and `TRUNCATE ... RESTART IDENTITY` reset it to the\n * configured start. Index names end in `_idx` so they cannot take the\n * `<table>_<column>_key` name Postgres gives a UNIQUE column, which\n * `IF NOT EXISTS` would otherwise silently skip.\n * @param table - Table name, fields, and indexes\n * @param options - The schema's other tables, and references to leave to later statements\n * @returns Statements in execution order, without trailing semicolons\n * @throws If a field type, default, or serial format cannot be expressed\n */\nexport function generateTableDDL(table: DDLTableConfig, options: TableDDLOptions = {}): string[] {\n const tableIdentifier = identifier(table.name);\n const tables = new Map((options.tables ?? [table]).map((t) => [t.name, t]));\n const sequences: string[] = [];\n const ownerships: string[] = [];\n const columns = [` \"id\" uuid PRIMARY KEY DEFAULT gen_random_uuid()`];\n const references = tableReferences(table);\n const constraintReferences = new Map(\n references.filter((r) => isConstraintReference(r, tables)).map((r) => [r.column, r]),\n );\n\n for (const [fieldName, field] of Object.entries(table.fields)) {\n if (fieldName === \"id\") continue;\n if (field.serial && field.type !== \"integer\") {\n const sequence = sequenceName(table.name, fieldName);\n sequences.push(`CREATE SEQUENCE IF NOT EXISTS ${sequence} ${sequenceRange(field.serial)}`);\n ownerships.push(\n `ALTER SEQUENCE ${sequence} OWNED BY ${tableIdentifier}.${identifier(fieldName)}`,\n );\n }\n const constraint = constraintReferences.get(fieldName);\n const inline =\n constraint && !options.deferredReferences?.has(fieldName)\n ? ` ${referenceClause(constraint)}`\n : \"\";\n columns.push(` ${columnDefinition(table.name, fieldName, field)}${inline}`);\n }\n\n const indexes = Object.entries(table.indexes ?? {})\n .filter(([, index]) => index.unique)\n .map(\n ([name, index]) =>\n `CREATE UNIQUE INDEX IF NOT EXISTS ${derivedIdentifier(table.name, name, \"idx\")} ON ${tableIdentifier} (${index.fields.map(identifier).join(\", \")})`,\n );\n\n for (const reference of constraintReferences.values()) {\n referencedColumnType(reference, tables, `${table.name}.${reference.column}`);\n }\n\n return [\n ...sequences,\n `CREATE TABLE IF NOT EXISTS ${tableIdentifier} (\\n${columns.join(\",\\n\")}\\n)`,\n ...ownerships,\n ...indexes,\n ...referenceTrigger(\n table,\n references.filter((r) => !isConstraintReference(r, tables)),\n tables,\n ),\n ];\n}\n\nfunction addForeignKey(table: DDLTableConfig, reference: Reference): string {\n const name = derivedName(table.name, reference.column, \"fk\");\n const tableIdentifier = identifier(table.name);\n const body = [\n \"BEGIN\",\n `IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = ${stringLiteral(name)} AND conrelid = ${stringLiteral(tableIdentifier)}::regclass) THEN`,\n `ALTER TABLE ${tableIdentifier} ADD CONSTRAINT ${identifier(name)} FOREIGN KEY (${identifier(reference.column)}) ${referenceClause(reference)};`,\n \"END IF;\",\n \"END\",\n ].join(\"\\n\");\n return `DO ${dollarQuoted(body)}`;\n}\n\n// Tables come after the tables they reference. Where references form a cycle,\n// the first table of the cycle is created without the constraint that points\n// at a table not yet created, and the constraint is added once all tables exist.\nfunction planCreation(tables: readonly DDLTableConfig[]): {\n ordered: DDLTableConfig[];\n deferred: Map<DDLTableConfig, Reference[]>;\n} {\n const names = new Set(tables.map((table) => table.name));\n const byName = new Map(tables.map((table) => [table.name, table]));\n const created = new Set<string>();\n const remaining = [...tables];\n const ordered: DDLTableConfig[] = [];\n const deferred = new Map<DDLTableConfig, Reference[]>();\n const pendingOf = (table: DDLTableConfig) =>\n tableReferences(table)\n .filter((r) => isConstraintReference(r, byName))\n .filter((r) => names.has(r.table) && r.table !== table.name && !created.has(r.table));\n while (remaining.length > 0) {\n const index = remaining.findIndex((table) => pendingOf(table).length === 0);\n const [table] = remaining.splice(Math.max(index, 0), 1) as [DDLTableConfig];\n const pending = pendingOf(table);\n if (pending.length > 0) deferred.set(table, pending);\n created.add(table.name);\n ordered.push(table);\n }\n return { ordered, deferred };\n}\n\n/**\n * A single SQL script creating every given table, for `pglite.exec()`. Tables\n * are created after the tables they reference.\n * @param tables - Tables to create\n * @returns The script, empty when there are no tables\n * @throws If a field references a table or field that is not among the tables\n */\nexport function generateSchemaDDL(tables: readonly DDLTableConfig[]): string {\n const { ordered, deferred } = planCreation(tables);\n const statements = ordered.map((table) =>\n generateTableDDL(table, {\n tables,\n deferredReferences: new Set(deferred.get(table)?.map((r) => r.column)),\n }),\n );\n const constraints = ordered.flatMap((table) =>\n (deferred.get(table) ?? []).map((reference) => addForeignKey(table, reference)),\n );\n return [...statements, constraints]\n .filter((group) => group.length > 0)\n .map((group) => group.map((statement) => `${statement};`).join(\"\\n\"))\n .join(\"\\n\\n\");\n}\n\n/** One namespace's tables, as the schema module groups them. */\nexport interface PgliteSchemaNamespace {\n namespace: string;\n tables: readonly DDLTableConfig[];\n}\n\n/** Options for {@link generatePgliteSchemaModule}. */\nexport interface PgliteSchemaModuleOptions {\n /** Named in the generated header as the producer users must not edit around. */\n generatedBy: string;\n}\n\n// A template literal keeps line feeds but folds a carriage return into one.\nfunction templateLiteral(value: string): string {\n const escaped = value\n .replaceAll(\"\\\\\", \"\\\\\\\\\")\n .replaceAll(\"`\", \"\\\\`\")\n .replaceAll(\"${\", \"\\\\${\")\n .replaceAll(\"\\r\", \"\\\\r\");\n return `\\`${escaped}\\``;\n}\n\n/**\n * Render the module that exports each namespace's `CREATE TABLE` script for\n * `pglite.exec()`.\n * @param namespaces - Namespaces with the tables to create\n * @param options - Header wording\n * @returns TypeScript source of the schema module\n */\nexport function generatePgliteSchemaModule(\n namespaces: readonly PgliteSchemaNamespace[],\n options: PgliteSchemaModuleOptions,\n): string {\n const entries = namespaces.map(\n ({ namespace, tables }) =>\n ` ${JSON.stringify(namespace)}: ${templateLiteral(generateSchemaDDL(tables))},`,\n );\n return [\n \"/**\",\n \" * Auto-generated PGlite schema for TailorDB tables.\",\n \" * Create a namespace's tables in a test with `pglite.exec(pgliteSchema[namespace])`.\",\n \" *\",\n ` * DO NOT EDIT - This file is auto-generated by ${options.generatedBy}.`,\n \" */\",\n \"\",\n \"export const pgliteSchema = {\",\n ...entries,\n \"} as const;\",\n \"\",\n ].join(\"\\n\");\n}\n","import { generatePgliteSchemaModule, type DDLTableConfig } from \"#/utils/tailordb-ddl\";\nimport type { TailorDBType } from \"#/parser/service/tailordb/types\";\n\n/** Tables of one namespace, as the schema module groups them. */\nexport interface PGliteSchemaNamespace {\n namespace: string;\n tables: Record<string, TailorDBType>;\n}\n\n/**\n * Reduce parsed tables to what the DDL generator reads.\n * @param tables - Parsed TailorDB tables keyed by name\n * @returns DDL table configs in the same order\n */\nexport function toDDLTables(tables: Record<string, TailorDBType>): DDLTableConfig[] {\n return Object.values(tables).map((type) => ({\n name: type.name,\n fields: Object.fromEntries(\n Object.entries(type.fields).map(([fieldName, field]) => [fieldName, field.config]),\n ),\n indexes: type.indexes,\n }));\n}\n\n/**\n * Render the module that exports each namespace's `CREATE TABLE` script for\n * `pglite.exec()`.\n * @param namespaces - Namespaces with their parsed tables\n * @returns TypeScript source of the schema module\n */\nexport function generatePGliteSchemaModule(namespaces: readonly PGliteSchemaNamespace[]): string {\n return generatePgliteSchemaModule(\n namespaces.map(({ namespace, tables }) => ({ namespace, tables: toDDLTables(tables) })),\n { generatedBy: \"the kysely-type plugin\" },\n );\n}\n","/** The Kysely column type a TailorDB field maps to, before array/null modifiers. */\nexport type FieldColumnType =\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"Timestamp\"\n | \"TemporalDate\"\n | \"TemporalInstant\"\n | \"TemporalTime\";\n\n/**\n * Map a scalar TailorDB field type to the column type generated code uses for it.\n *\n * With `temporal: false` (the default), `date`/`datetime` resolve to `Timestamp` and\n * `time` to `string`, because that's what the function runtime hands back for them by\n * default. With `temporal: true`, `date`/`datetime`/`time` resolve to their\n * `Temporal.PlainDate`/`Temporal.Instant`/`Temporal.PlainTime` counterparts instead,\n * matching a `getDB` created with `createGetDB({ temporal: true })`.\n *\n * `enum` and `nested` carry their own shape, so each generator resolves them\n * before reaching here; passing either is a caller bug rather than a `string`\n * column.\n * @param fieldType - TailorDB scalar field type name\n * @param temporal - Whether to map date/datetime/time fields to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @returns The column type for the field, defaulting to `string`\n * @throws If given `enum` or `nested`\n */\nexport function mapFieldTypeToColumnType(fieldType: string, temporal = false): FieldColumnType {\n switch (fieldType) {\n case \"uuid\":\n case \"string\":\n case \"decimal\":\n return \"string\";\n case \"integer\":\n case \"float\":\n return \"number\";\n case \"date\":\n return temporal ? \"TemporalDate\" : \"Timestamp\";\n case \"datetime\":\n return temporal ? \"TemporalInstant\" : \"Timestamp\";\n case \"time\":\n return temporal ? \"TemporalTime\" : \"string\";\n case \"bool\":\n case \"boolean\":\n return \"boolean\";\n case \"enum\":\n case \"nested\":\n throw new Error(\n `Field type \"${fieldType}\" has no scalar column type; resolve it before mapping.`,\n );\n default:\n return \"string\";\n }\n}\n\n/** The select and write types a `ColumnType`-shaped alias expands to. */\nexport type ColumnTypeAliasExpansion = {\n /** Type the alias reads back as. */\n readonly select: string;\n /** Type the alias accepts on insert and update. */\n readonly write: string;\n};\n\n/**\n * Column types whose alias expands to a `ColumnType`, mapped to that expansion.\n *\n * Kysely only unwraps a `ColumnType` at the top level of a table property, so an\n * array of one of these stays wrapped in `ArrayColumnType<...>` rather than taking\n * a `[]` suffix, which would nest the `ColumnType` out of Kysely's reach. Where a\n * generator has to spell out the slots itself, it reads the expansion from here\n * rather than inlining the alias, which would nest just the same.\n *\n * Both type generators track alias usage per alias to decide which declarations to\n * emit, so adding an entry here also means teaching them to report the new alias.\n */\nexport const COLUMN_TYPE_ALIASES: ReadonlyMap<string, ColumnTypeAliasExpansion> = new Map<\n FieldColumnType,\n ColumnTypeAliasExpansion\n>([\n [\"Timestamp\", { select: \"Date\", write: \"Date | string\" }],\n [\"TemporalDate\", { select: \"Temporal.PlainDate\", write: \"Temporal.PlainDate | string\" }],\n [\"TemporalInstant\", { select: \"Temporal.Instant\", write: \"Temporal.Instant | string\" }],\n [\"TemporalTime\", { select: \"Temporal.PlainTime\", write: \"Temporal.PlainTime | string\" }],\n]);\n","import { COLUMN_TYPE_ALIASES, mapFieldTypeToColumnType } from \"#/utils/field-column-type\";\nimport multiline from \"#/utils/multiline\";\nimport {\n type KyselyFieldConfig,\n type KyselyNamespaceMetadata,\n type KyselyTypeMetadata,\n type UsedUtilityTypes,\n} from \"./types\";\nimport type { TailorDBType } from \"#/parser/service/tailordb/types\";\n\ntype FieldTypeResult = {\n type: string;\n usedUtilityTypes: UsedUtilityTypes;\n};\n\nfunction emptyUsedUtilityTypes(): UsedUtilityTypes {\n return {\n Timestamp: false,\n TemporalDate: false,\n TemporalInstant: false,\n TemporalTime: false,\n Serial: false,\n ObjectColumnType: false,\n ArrayColumnType: false,\n };\n}\n\nfunction mergeUsedUtilityTypes(a: UsedUtilityTypes, b: UsedUtilityTypes): UsedUtilityTypes {\n return {\n Timestamp: a.Timestamp || b.Timestamp,\n TemporalDate: a.TemporalDate || b.TemporalDate,\n TemporalInstant: a.TemporalInstant || b.TemporalInstant,\n TemporalTime: a.TemporalTime || b.TemporalTime,\n Serial: a.Serial || b.Serial,\n ObjectColumnType: a.ObjectColumnType || b.ObjectColumnType,\n ArrayColumnType: a.ArrayColumnType || b.ArrayColumnType,\n };\n}\n\n/**\n * Whether a utility-type usage record uses any `ColumnType`-shaped alias.\n * @param usedUtilityTypes - Utility-type usage record to check\n * @returns Whether any `ColumnType`-shaped alias is used\n */\nfunction usesColumnTypeAlias(usedUtilityTypes: UsedUtilityTypes): boolean {\n return (\n usedUtilityTypes.Timestamp ||\n usedUtilityTypes.TemporalDate ||\n usedUtilityTypes.TemporalInstant ||\n usedUtilityTypes.TemporalTime\n );\n}\n\n/**\n * Get the enum type definition.\n * @param fieldConfig - The field configuration\n * @returns The enum type as a string union\n */\nfunction getEnumType(fieldConfig: KyselyFieldConfig): string {\n const allowedValues = fieldConfig.allowedValues;\n\n if (allowedValues && Array.isArray(allowedValues)) {\n return allowedValues\n .map((v: string | { value: string }) => {\n const value = typeof v === \"string\" ? v : v.value;\n return `\"${value}\"`;\n })\n .join(\" | \");\n }\n return \"string\";\n}\n\n/**\n * Get the nested object type definition.\n * @param fieldConfig - The field configuration\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @returns The nested type with used utility types\n */\nfunction getNestedType(fieldConfig: KyselyFieldConfig, temporal: boolean): FieldTypeResult {\n const fields = fieldConfig.fields;\n if (!fields || typeof fields !== \"object\") {\n return {\n type: \"string\",\n usedUtilityTypes: emptyUsedUtilityTypes(),\n };\n }\n\n const fieldResults = Object.entries(fields).map(([fieldName, config]) => {\n const result = generateFieldType(config, temporal, true);\n const optional = config.required !== true ? \"?\" : \"\";\n return {\n fieldType: `${fieldName}${optional}: ${result.type}`,\n usedUtilityTypes: result.usedUtilityTypes,\n };\n });\n\n const aggregatedUtilityTypes = fieldResults.reduce(\n (acc, result) => mergeUsedUtilityTypes(acc, result.usedUtilityTypes),\n emptyUsedUtilityTypes(),\n );\n\n const fieldTypes = fieldResults.map((r) => r.fieldType);\n const obj = `{\\n ${fieldTypes.join(\";\\n \")}${fieldTypes.length > 0 ? \";\" : \"\"}\\n}`;\n\n const hasOptionalFields = Object.values(fields).some((config) => config.required !== true);\n const hasGeneratedFields = Object.values(fields).some(\n (config) =>\n config.hooks?.create || config.default !== undefined || config.optionalOnCreate === true,\n );\n if (usesColumnTypeAlias(aggregatedUtilityTypes) || hasOptionalFields || hasGeneratedFields) {\n return {\n type: `ObjectColumnType<${obj}>`,\n usedUtilityTypes: { ...aggregatedUtilityTypes, ObjectColumnType: true },\n };\n }\n return { type: obj, usedUtilityTypes: aggregatedUtilityTypes };\n}\n\n/**\n * Get the base Kysely type for a field (without array/null modifiers).\n * @param fieldConfig - The field configuration\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @param nested - Whether the field is inside an object\n * @returns The base type with used utility types\n */\nfunction getBaseType(\n fieldConfig: KyselyFieldConfig,\n temporal: boolean,\n nested: boolean,\n): FieldTypeResult {\n const fieldType = fieldConfig.type;\n const usedUtilityTypes = emptyUsedUtilityTypes();\n\n if (fieldType === \"enum\") {\n return { type: getEnumType(fieldConfig), usedUtilityTypes };\n }\n if (fieldType === \"nested\") {\n return getNestedType(fieldConfig, temporal);\n }\n\n const type = mapFieldTypeToColumnType(fieldType, temporal && !(nested && fieldType === \"time\"));\n switch (type) {\n case \"Timestamp\":\n usedUtilityTypes.Timestamp = true;\n break;\n case \"TemporalDate\":\n usedUtilityTypes.TemporalDate = true;\n break;\n case \"TemporalInstant\":\n usedUtilityTypes.TemporalInstant = true;\n break;\n case \"TemporalTime\":\n usedUtilityTypes.TemporalTime = true;\n break;\n case \"string\":\n case \"number\":\n case \"boolean\":\n break;\n }\n\n return { type, usedUtilityTypes };\n}\n\n/**\n * Generate the complete field type including array and null modifiers.\n * @param fieldConfig - The field configuration\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @param nested - Whether the field is inside an object\n * @returns The complete field type with used utility types\n */\nfunction generateFieldType(\n fieldConfig: KyselyFieldConfig,\n temporal: boolean,\n nested = false,\n): FieldTypeResult {\n const baseTypeResult = getBaseType(fieldConfig, temporal, nested);\n const usedUtilityTypes = { ...baseTypeResult.usedUtilityTypes };\n\n const isArray = fieldConfig.array === true;\n const isNullable = fieldConfig.required !== true;\n\n // A ColumnType-shaped alias and ObjectColumnType cannot be wrapped with [] for\n // arrays, because Kysely only resolves ColumnType at the top-level table\n // property. Use ArrayColumnType to keep the ColumnType at the top level.\n const isColumnTypeBase = COLUMN_TYPE_ALIASES.has(baseTypeResult.type);\n\n let finalType = baseTypeResult.type;\n if (isArray) {\n if (isColumnTypeBase || finalType.startsWith(\"ObjectColumnType<\")) {\n finalType = `ArrayColumnType<${baseTypeResult.type}>`;\n usedUtilityTypes.ArrayColumnType = true;\n } else {\n const needsParens = fieldConfig.type === \"enum\";\n finalType = needsParens ? `(${baseTypeResult.type})[]` : `${baseTypeResult.type}[]`;\n }\n }\n if (isNullable) {\n finalType = `${finalType} | null`;\n }\n\n if (fieldConfig.serial) {\n usedUtilityTypes.Serial = true;\n finalType = `Serial<${finalType}>`;\n }\n if (\n fieldConfig.hooks?.create ||\n fieldConfig.default !== undefined ||\n fieldConfig.optionalOnCreate === true\n ) {\n finalType = `Generated<${finalType}>`;\n }\n\n return { type: finalType, usedUtilityTypes };\n}\n\n/**\n * Generate the table interface.\n * @param name - Table name\n * @param fields - Field configurations keyed by field name\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @returns The type definition and used utility types\n */\nfunction generateTableInterface(\n name: string,\n fields: Record<string, KyselyFieldConfig>,\n temporal: boolean,\n): {\n typeDef: string;\n usedUtilityTypes: UsedUtilityTypes;\n} {\n const fieldEntries = Object.entries(fields).filter(([fieldName]) => fieldName !== \"id\");\n\n const fieldResults = fieldEntries.map(([fieldName, fieldConfig]) => ({\n fieldName,\n ...generateFieldType(fieldConfig, temporal),\n }));\n\n const fieldLines = [\n \"id: Generated<string>;\",\n ...fieldResults.map((result) => `${result.fieldName}: ${result.type};`),\n ];\n\n const aggregatedUtilityTypes = fieldResults.reduce(\n (acc, result) => mergeUsedUtilityTypes(acc, result.usedUtilityTypes),\n emptyUsedUtilityTypes(),\n );\n\n const typeDef = multiline /* ts */ `\n ${name}: {\n ${fieldLines.join(\"\\n\")}\n }\n `;\n\n return { typeDef, usedUtilityTypes: aggregatedUtilityTypes };\n}\n\n/**\n * Generate KyselyTypeMetadata from field configurations.\n * @param name - Table name\n * @param fields - Field configurations keyed by field name\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults. Defaults to `false`.\n * @returns Generated Kysely type metadata\n */\nexport function processKyselyFields(\n name: string,\n fields: Record<string, KyselyFieldConfig>,\n temporal = false,\n): KyselyTypeMetadata {\n const result = generateTableInterface(name, fields, temporal);\n\n return {\n name,\n typeDef: result.typeDef,\n usedUtilityTypes: result.usedUtilityTypes,\n };\n}\n\n/**\n * Convert a TailorDBType into KyselyTypeMetadata.\n * @param type - Parsed TailorDB table\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults. Defaults to `false`.\n * @returns Generated Kysely type metadata\n */\nexport async function processKyselyType(\n type: TailorDBType,\n temporal = false,\n): Promise<KyselyTypeMetadata> {\n return processKyselyFields(\n type.name,\n Object.fromEntries(\n Object.entries(type.fields).map(([fieldName, parsedField]) => [\n fieldName,\n parsedField.config,\n ]),\n ),\n temporal,\n );\n}\n\n/**\n * Generate unified types file from multiple namespaces.\n * @param namespaceData - Namespace metadata\n * @param temporal - Whether `kyselyTypePlugin` was configured with `{ temporal: true }`;\n * passed to `createGetDB` so the generated `getDB` always reads back the Temporal values\n * the tables above are typed with. Defaults to `false`.\n * @returns Generated types file contents\n */\nexport function generateUnifiedKyselyTypes(\n namespaceData: KyselyNamespaceMetadata[],\n temporal = false,\n): string {\n if (namespaceData.length === 0) {\n return \"\";\n }\n\n // Aggregate used utility types from all namespaces\n const globalUsedUtilityTypes = namespaceData\n .flatMap((ns) => ns.types)\n .reduce(\n (acc, type) => mergeUsedUtilityTypes(acc, type.usedUtilityTypes),\n emptyUsedUtilityTypes(),\n );\n\n const utilityTypeImports: string[] = [\"type Generated\"];\n if (globalUsedUtilityTypes.Timestamp) {\n utilityTypeImports.push(\"type Timestamp\");\n }\n if (globalUsedUtilityTypes.TemporalDate) {\n utilityTypeImports.push(\"type TemporalDate\");\n }\n if (globalUsedUtilityTypes.TemporalInstant) {\n utilityTypeImports.push(\"type TemporalInstant\");\n }\n if (globalUsedUtilityTypes.TemporalTime) {\n utilityTypeImports.push(\"type TemporalTime\");\n }\n if (globalUsedUtilityTypes.ObjectColumnType) {\n utilityTypeImports.push(\"type ObjectColumnType\");\n }\n if (globalUsedUtilityTypes.ArrayColumnType) {\n utilityTypeImports.push(\"type ArrayColumnType\");\n }\n if (globalUsedUtilityTypes.Serial) {\n utilityTypeImports.push(\"type Serial\");\n }\n\n const importsSection = multiline /* ts */ `\n import {\n createGetDB,\n ${utilityTypeImports.join(\",\\n\")},\n type NamespaceDB,\n type NamespaceInsertable,\n type NamespaceSelectable,\n type NamespaceTable,\n type NamespaceTableName,\n type NamespaceTransaction,\n type NamespaceUpdateable,\n } from \"@tailor-platform/sdk/kysely\";\n `;\n\n // Generate Namespace interface with multiple namespaces\n const namespaceInterfaces = namespaceData\n .map(({ namespace, types }) => {\n const typeDefsWithIndent = types\n .map((type) => {\n return type.typeDef\n .split(\"\\n\")\n .map((line) => (line.trim() ? ` ${line}` : \"\"))\n .join(\"\\n\");\n })\n .join(\"\\n\\n\");\n\n return ` \"${namespace}\": {\\n${typeDefsWithIndent}\\n }`;\n })\n .join(\",\\n\");\n\n const namespaceInterface = `export interface Namespace {\\n${namespaceInterfaces}\\n}`;\n\n const getDBFunction = multiline /* ts */ `\n export const getDB = createGetDB<Namespace>(${temporal ? \"{ temporal: true }\" : \"\"});\n\n export type DB<N extends keyof Namespace = keyof Namespace> = NamespaceDB<Namespace, N>;\n `;\n\n const utilityTypeExports = multiline /* ts */ `\n export type Transaction<K extends keyof Namespace | DB = keyof Namespace> =\n NamespaceTransaction<Namespace, K>;\n\n type TableName = NamespaceTableName<Namespace>;\n export type Table<T extends TableName> = NamespaceTable<Namespace, T>;\n\n export type Insertable<T extends TableName> = NamespaceInsertable<Namespace, T>;\n export type Selectable<T extends TableName> = NamespaceSelectable<Namespace, T>;\n export type Updateable<T extends TableName> = NamespaceUpdateable<Namespace, T>;\n `;\n\n return (\n [importsSection, namespaceInterface, getDBFunction, utilityTypeExports].join(\"\\n\\n\") + \"\\n\"\n );\n}\n","import { resolve } from \"pathe\";\nimport { generatePGliteSchemaModule } from \"./pglite-schema\";\nimport { processKyselyType, generateUnifiedKyselyTypes } from \"./type-processor\";\nimport type { Plugin, GeneratorResult, TailorDBReadyContext } from \"#/plugin/types\";\nimport type { KyselyTypeMetadata, KyselyNamespaceMetadata } from \"./types\";\n\n/** Unique identifier for the Kysely type generator plugin. */\nexport const KyselyGeneratorID = \"@tailor-platform/kysely-type\";\n\ntype KyselyTypePluginOptions = {\n distPath: string;\n pgliteSchemaPath?: string;\n /**\n * When `true`, generated tables map date/datetime fields and top-level time fields to\n * `Temporal.PlainDate`/`Temporal.Instant`/`Temporal.PlainTime` columns instead of\n * their `Date`/`string` defaults, and the generated `getDB` reads them back as those\n * Temporal values. Nested time fields remain strings. Defaults to `false`.\n */\n temporal?: boolean;\n};\n\n// Register this plugin's config type under its own id, via the package's\n// real public specifier, so callers can resolve it type-safely from a\n// `Plugin[]` array (see plugin/get-plugin-config.ts's resolvePluginConfig)\n// without importing KyselyTypePluginOptions, which stays unexported.\ndeclare module \"@tailor-platform/sdk/plugin\" {\n interface PluginConfigRegistry {\n \"@tailor-platform/kysely-type\": KyselyTypePluginOptions;\n }\n}\n\n/** Conventional output path used when `kyselyTypePlugin` has no `distPath` configured. */\nexport const DEFAULT_KYSELY_TYPES_DIST_PATH = \"./generated/tailordb.ts\";\n\n/**\n * Plugin that generates Kysely type definitions for TailorDB tables.\n * @param options - Plugin options\n * @param options.distPath - Output file path for generated types\n * @param options.pgliteSchemaPath - Output file path for the PGlite `CREATE TABLE` script module; omit to skip it\n * @param options.temporal - When `true`, generated tables map date/datetime fields and\n * top-level time fields to their `Temporal.PlainDate`/`Temporal.Instant`/\n * `Temporal.PlainTime` counterparts instead of `Date`/`string`. Nested time fields\n * remain strings. Defaults to `false`.\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function kyselyTypePlugin(\n options: KyselyTypePluginOptions,\n): Plugin<unknown, KyselyTypePluginOptions> {\n return {\n id: KyselyGeneratorID,\n description: \"Generates Kysely type definitions for TailorDB tables\",\n pluginConfig: options,\n\n async onTailorDBReady(\n ctx: TailorDBReadyContext<KyselyTypePluginOptions>,\n ): Promise<GeneratorResult> {\n const { distPath, pgliteSchemaPath, temporal } = ctx.pluginConfig;\n if (pgliteSchemaPath && resolve(distPath) === resolve(pgliteSchemaPath)) {\n throw new Error(\"distPath and pgliteSchemaPath must resolve to different files.\");\n }\n\n const allNamespaceData: KyselyNamespaceMetadata[] = [];\n\n for (const ns of ctx.tailordb) {\n const typeMetadataList: KyselyTypeMetadata[] = [];\n\n for (const type of Object.values(ns.tables)) {\n const metadata = await processKyselyType(type, temporal);\n typeMetadataList.push(metadata);\n }\n\n if (typeMetadataList.length === 0) continue;\n\n allNamespaceData.push({\n namespace: ns.namespace,\n types: typeMetadataList,\n });\n }\n\n const files: GeneratorResult[\"files\"] = [];\n if (allNamespaceData.length > 0) {\n const content = generateUnifiedKyselyTypes(allNamespaceData, temporal);\n files.push({\n path: ctx.pluginConfig.distPath,\n content,\n });\n if (ctx.pluginConfig.pgliteSchemaPath) {\n files.push({\n path: ctx.pluginConfig.pgliteSchemaPath,\n content: generatePGliteSchemaModule(\n ctx.tailordb.filter((ns) => Object.keys(ns.tables).length > 0),\n ),\n });\n }\n }\n\n return { files };\n },\n };\n}\n"],"mappings":"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"}
@@ -2,8 +2,8 @@ import{formatWithOptions as e,stripVTControlCharacters as t,styleText as n}from"
2
2
  `?!1:t<=31||t===127}function normalizeControlCharacters(e){let t=e.match(s)??[];return e.split(s).map(e=>{let t=[];for(let n of e)n===` `?t.push(` `.repeat(8)):isStrippableControlCharacter(n)||t.push(n);return t.join(``)}).reduce((e,n,r)=>e+n+(t[r]??``),``)}function sanitizeCell(e){return normalizeControlCharacters(String(e??``).replace(c,`
3
3
  `))}const u=/^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Format}|\p{Nonspacing_Mark}|\p{Enclosing_Mark}|\p{Surrogate})+$/v,d=/^\p{RGI_Emoji}$/v;function displayWidth(e){let t=0;for(let{segment:n}of l.segment(e.replace(s,``))){if(u.test(n))continue;if(d.test(n)){t+=2;continue}let e=n.codePointAt(0);t+=e===void 0?0:a(e)}return t}function toCellLines(e){return e.split(`
4
4
  `).map(e=>({text:e,width:displayWidth(e)}))}function padCell(e,t){return e.text+` `.repeat(Math.max(0,t-e.width))}function renderBorder(e,t,n,r){return e+r.map(e=>`─`.repeat(e+2)).join(t)+n}function validateConsistentColumnCount(e){let t=e[0]?.length;t!==void 0&&e.forEach((e,n)=>{if(e.length!==t)throw Error(`renderTable: all rows must have the same number of columns (expected ${t}, row ${n} has ${e.length}).`)})}function renderTable(e,t={}){let n=e.map(e=>e.map(sanitizeCell));validateConsistentColumnCount(n);let r=n.length,i=n[0]?.length??0;if(r===0||i===0)return``;let a=n.map(e=>Array.from({length:i},(t,n)=>toCellLines(e[n]??``))),o=Array.from({length:i},(e,t)=>maxOf(a.map(e=>maxOf((e[t]??[]).map(e=>e.width))))),shouldDrawLine=e=>t.singleLine?e===0||e===r:(t.drawHorizontalLine??(()=>!0))(e,r),s=[];return shouldDrawLine(0)&&s.push(renderBorder(`┌`,`┬`,`┐`,o)),a.forEach((e,t)=>{let n=maxOf(e.map(e=>e.length),1);for(let t=0;t<n;t++){let n=e.map((e,n)=>padCell(e[t]??{text:``,width:0},o[n]??0));s.push(`│ ${n.join(` │ `)} │`)}t<r-1&&shouldDrawLine(t+1)&&s.push(renderBorder(`├`,`┼`,`┤`,o))}),shouldDrawLine(r)&&s.push(renderBorder(`└`,`┴`,`┘`,o)),`${s.join(`
5
- `)}\n`}const f=/* @__PURE__ */ new Set([`true`,`t`,`yes`,`y`,`on`,`1`]),p=/* @__PURE__ */ new Set([`false`,`f`,`no`,`n`,`off`,`0`]);function parseBoolean(e){if(e===void 0)return;let t=e.trim().toLowerCase();if(t!==``){if(f.has(t))return!0;if(p.has(t))return!1}}const m=`TAILOR_JSON_OUTPUT`;function interactiveTerminal(){return!i&&process.stdin.isTTY===!0&&process.stdout.isTTY===!0}function jsonModeRemedy(e){let t=y;switch(e){case`agent`:return` JSON output is enabled because an AI coding agent${t?` (${t})`:``} was detected; pass --json=false or set ${m}=false to restore prompts.`;case`flag`:return t?` JSON output is enabled by --json; pass --json=false instead to restore prompts.`:` JSON output is enabled by --json; drop it to restore prompts.`;case`env`:return t?` JSON output is enabled by ${m}; set it to false to restore prompts.`:` JSON output is enabled by ${m}; unset it to restore prompts.`;case`both`:return t?` JSON output is enabled by --json and ${m}; pass --json=false instead of the flag to restore prompts.`:` JSON output is enabled by --json and ${m}; drop the flag and unset the variable to restore prompts.`}}var CIPromptError=class extends Error{constructor(e){let t=_&&v!==void 0&&interactiveTerminal()?jsonModeRemedy(v):``;super((e??`Interactive prompts are not available in this environment. Provide the required options explicitly.`)+t),this.name=`CIPromptError`}};const h={success:o.green,error:o.red,warning:o.yellow,info:o.cyan,create:o.green,update:o.yellow,delete:o.red,replace:o.magenta,unchanged:o.gray,bold:o.bold,dim:o.gray,highlight:o.cyanBright,successBright:o.greenBright,errorBright:o.redBright,resourceType:o.bold,resourceName:o.cyan,path:o.cyan,value:o.white,placeholder:e=>o.italic(o.gray(e))},g={success:h.success(`✓`),error:h.error(`✖`),warning:h.warning(`⚠`),info:h.info(`i`),create:h.create(`+`),update:h.update(`~`),delete:h.delete(`-`),replace:h.replace(`±`),bullet:h.dim(`•`),arrow:h.dim(`→`)};let _=!1,v,y,b=!1;const x=/* @__PURE__ */ new Set,S=`<redacted>`;function buildAutomaton(e){let t={children:/* @__PURE__ */ new Map,fail:void 0,outputs:[]};t.fail=t;for(let n of e){let e=t;for(let r=0;r<n.length;r++){let i=n[r],a=e.children.get(i);a||(a={children:/* @__PURE__ */ new Map,fail:t,outputs:[]},e.children.set(i,a)),e=a}e.outputs.push(n)}let n=[...t.children.values()];for(let e=0;e<n.length;e++){let r=n[e];for(let[e,i]of r.children){let a=r.fail;for(;a!==t&&!a.children.has(e);)a=a.fail;i.fail=a.children.get(e)??t,i.outputs=i.outputs.concat(i.fail.outputs),n.push(i)}}return t}let C=null;function getAutomaton(){return C??=buildAutomaton(x),C}function findSecretSpans(e){let t=[],n=getAutomaton(),r=n;for(let i=0;i<e.length;i++){let a=e[i];for(;r!==n&&!r.children.has(a);)r=r.fail;r=r.children.get(a)??n;for(let e of r.outputs)t.push([i+1-e.length,i+1])}return t}function redactSecrets(e){if(x.size===0)return e;let t=[];for(let n=0,r;(r=e.indexOf(S,n))!==-1;)t.push([r,r+10]),n=r+10;let n=findSecretSpans(e).filter(([e,n])=>!t.some(([t,r])=>e>=t&&n<=r));if(n.length===0)return e;n.sort(([e],[t])=>e-t);let r=[];for(let e of n){let t=r.at(-1);t&&e[0]<=t[1]?t[1]=Math.max(t[1],e[1]):r.push(e)}let i=``,a=0;for(let[t,n]of r)i+=e.slice(a,t)+S,a=n;return i+e.slice(a)}const w={info:`ℹ`,success:`✔`,warn:`⚠`,error:`✖`,debug:`⚙`,trace:`→`,log:``},T={info:h.info,success:h.success,warn:h.warning,error:h.error,debug:h.dim,trace:h.dim,log:e=>e};function formatLogLine(e){let{mode:t,indent:n,type:r,message:i,timestamp:a}=e,o=n>0?` `.repeat(n):``,s=T[r]||(e=>e);if(t===`plain`)return`${o}${s(i)}\n`;let c=w[r]||``,l=s(`${c?`${c} `:``}${i}`);return`${o}${a??``}${l}\n`}function writeLog(t,n,r){let i=r?.mode??`default`,a=r?.indent??0,o={breakLength:process.stdout.columns||80},s=formatLogLine({mode:i,indent:a,type:t,message:e(o,n),timestamp:i===`stream`?`${(/* @__PURE__ */ new Date()).toLocaleTimeString()} `:``});process.stderr.write(renderFor(process.stderr,redactSecrets(s)))}function setJsonDefaultAgent(e){y=e}function jsonDefaultAgent(){return y}const E={get jsonMode(){return _},set jsonMode(e){_=e,e||(v=void 0)},setJsonMode(e,t){_=e,v=e?t:void 0},get verbose(){return b||parseBoolean(process.env.DEBUG)===!0||process.env.RUNNER_DEBUG===`1`},set verbose(e){b=e},info(e,t){writeLog(`info`,e,t)},success(e,t){writeLog(`success`,e,t)},warn(e,t){writeLog(`warn`,e,t)},error(e,t){writeLog(`error`,e,t)},log(e){writeLog(`log`,e,{mode:`plain`})},newline(){process.stderr.write(`
5
+ `)}\n`}const f=/* @__PURE__ */ new Set([`true`,`t`,`yes`,`y`,`on`,`1`]),p=/* @__PURE__ */ new Set([`false`,`f`,`no`,`n`,`off`,`0`]);function parseBoolean(e){if(e===void 0)return;let t=e.trim().toLowerCase();if(t!==``){if(f.has(t))return!0;if(p.has(t))return!1}}const m=`TAILOR_JSON_OUTPUT`;function interactiveTerminal(){return!i&&process.stdin.isTTY===!0&&process.stdout.isTTY===!0}function jsonModeRemedy(e){let t=y;switch(e){case`agent`:return` JSON output is enabled because an AI coding agent${t?` (${t})`:``} was detected; pass --json=false or set ${m}=false to restore prompts.`;case`flag`:return t?` JSON output is enabled by --json; pass --json=false instead to restore prompts.`:` JSON output is enabled by --json; drop it to restore prompts.`;case`env`:return t?` JSON output is enabled by ${m}; set it to false to restore prompts.`:` JSON output is enabled by ${m}; unset it to restore prompts.`;case`both`:return t?` JSON output is enabled by --json and ${m}; pass --json=false instead of the flag to restore prompts.`:` JSON output is enabled by --json and ${m}; drop the flag and unset the variable to restore prompts.`}}var CIPromptError=class extends Error{constructor(e){let t=_&&v!==void 0&&interactiveTerminal()?jsonModeRemedy(v):``;super((e??`Interactive prompts are not available in this environment. Provide the required options explicitly.`)+t),this.name=`CIPromptError`}};const h={success:o.green,error:o.red,warning:o.yellow,info:o.cyan,create:o.green,update:o.yellow,delete:o.red,replace:o.magenta,unchanged:o.gray,bold:o.bold,dim:o.gray,highlight:o.cyanBright,successBright:o.greenBright,errorBright:o.redBright,resourceType:o.bold,resourceName:o.cyan,path:o.cyan,value:o.white,placeholder:e=>o.italic(o.gray(e))},g={success:h.success(`✓`),error:h.error(`✖`),warning:h.warning(`⚠`),info:h.info(`i`),create:h.create(`+`),update:h.update(`~`),delete:h.delete(`-`),replace:h.replace(`±`),bullet:h.dim(`•`),arrow:h.dim(`→`)};let _=!1,v,y,b=!1;const x=/* @__PURE__ */ new Set,S=`<redacted>`;function buildAutomaton(e){let t={children:/* @__PURE__ */ new Map,fail:void 0,outputs:[]};t.fail=t;for(let n of e){let e=t;for(let r=0;r<n.length;r++){let i=n[r],a=e.children.get(i);a||(a={children:/* @__PURE__ */ new Map,fail:t,outputs:[]},e.children.set(i,a)),e=a}e.outputs.push(n)}let n=[...t.children.values()];for(let e=0;e<n.length;e++){let r=n[e];for(let[e,i]of r.children){let a=r.fail;for(;a!==t&&!a.children.has(e);)a=a.fail;i.fail=a.children.get(e)??t,i.outputs=i.outputs.concat(i.fail.outputs),n.push(i)}}return t}let C=null;function getAutomaton(){return C??=buildAutomaton(x),C}function findSecretSpans(e){let t=[],n=getAutomaton(),r=n;for(let i=0;i<e.length;i++){let a=e[i];for(;r!==n&&!r.children.has(a);)r=r.fail;r=r.children.get(a)??n;for(let e of r.outputs)t.push([i+1-e.length,i+1])}return t}function redactSecrets(e){if(x.size===0)return e;let t=[];for(let n=0,r;(r=e.indexOf(S,n))!==-1;)t.push([r,r+10]),n=r+10;let n=findSecretSpans(e).filter(([e,n])=>!t.some(([t,r])=>e>=t&&n<=r));if(n.length===0)return e;n.sort(([e],[t])=>e-t);let r=[];for(let e of n){let t=r.at(-1);t&&e[0]<=t[1]?t[1]=Math.max(t[1],e[1]):r.push(e)}let i=``,a=0;for(let[t,n]of r)i+=e.slice(a,t)+S,a=n;return i+e.slice(a)}function renderWithRedaction(e,n){let r=n(redactSecrets(e)),i=t(r),a=redactSecrets(i);return a===i?r:a}function renderRedacted(e,t){return renderWithRedaction(t,t=>renderFor(e,t))}function plainRedacted(e){return renderWithRedaction(e,t)}const w={info:`ℹ`,success:`✔`,warn:`⚠`,error:`✖`,debug:`⚙`,trace:`→`,log:``},T={info:h.info,success:h.success,warn:h.warning,error:h.error,debug:h.dim,trace:h.dim,log:e=>e};function formatLogLine(e){let{mode:t,indent:n,type:r,message:i,timestamp:a}=e,o=n>0?` `.repeat(n):``,s=T[r]||(e=>e);if(t===`plain`)return`${o}${s(i)}\n`;let c=w[r]||``,l=s(`${c?`${c} `:``}${i}`);return`${o}${a??``}${l}\n`}function writeLog(t,n,r){let i=r?.mode??`default`,a=r?.indent??0,o={breakLength:process.stdout.columns||80},s=formatLogLine({mode:i,indent:a,type:t,message:e(o,n),timestamp:i===`stream`?`${(/* @__PURE__ */ new Date()).toLocaleTimeString()} `:``});process.stderr.write(renderRedacted(process.stderr,s))}function setJsonDefaultAgent(e){y=e}function jsonDefaultAgent(){return y}const E={get jsonMode(){return _},set jsonMode(e){_=e,e||(v=void 0)},setJsonMode(e,t){_=e,v=e?t:void 0},get verbose(){return b||parseBoolean(process.env.DEBUG)===!0||process.env.RUNNER_DEBUG===`1`},set verbose(e){b=e},info(e,t){writeLog(`info`,e,t)},success(e,t){writeLog(`success`,e,t)},warn(e,t){writeLog(`warn`,e,t)},error(e,t){writeLog(`error`,e,t)},log(e){writeLog(`log`,e,{mode:`plain`})},newline(){process.stderr.write(`
6
6
  `)},debug(e){E.verbose&&writeLog(`log`,h.dim(e),{mode:`plain`})},registerSecret(e){if(typeof e!=`string`||[...e].length<4)return;let t=x.size;x.add(e);let n=JSON.stringify(e).slice(1,-1);n!==e&&x.add(n);let r=new URLSearchParams([[`v`,e]]).toString().slice(2);r!==e&&x.add(r),x.size!==t&&(C=null)},out(e,t){if(typeof e==`string`){process.stdout.write(renderFor(process.stdout,e.endsWith(`
7
7
  `)?e:e+`
8
- `));return}if(this.jsonMode){console.log(JSON.stringify(e));return}let n=t?.display,formatValue=(e,n=!1)=>t?.showNull&&e===null?`NULL`:e==null?`N/A`:e instanceof Date?r(e,{addSuffix:!0}):typeof e==`object`?n?JSON.stringify(e,null,2):JSON.stringify(e):String(e),isExcluded=e=>n!==void 0&&e in n&&n[e]===null,transformValue=(e,t,r,i=!1)=>{if(n&&e in n){let i=n[e];if(i)return i(t,r)}return formatValue(t,i)};if(!Array.isArray(e)){let t=renderTable(Object.entries(e).filter(([e])=>!isExcluded(e)).map(([t,n])=>[t,transformValue(t,n,e,!0)]),{singleLine:!1});process.stdout.write(renderFor(process.stdout,t));return}if(e.length===0)return;let i=Array.from(new Set(e.flatMap(e=>Object.keys(e)))).filter(e=>!isExcluded(e));if(i.length===0)return;let a=renderTable([i,...e.map(e=>i.map(t=>transformValue(t,e[t],e)))],{drawHorizontalLine:(e,t)=>e===0||e===1||e===t});process.stdout.write(renderFor(process.stdout,a))}};export{E as a,h as c,renderTable as d,renderFor as f,jsonDefaultAgent as i,g as l,m as n,redactSecrets as o,interactiveTerminal as r,setJsonDefaultAgent as s,CIPromptError as t,parseBoolean as u};
9
- //# sourceMappingURL=logger-DuaT2zY3.mjs.map
8
+ `));return}if(this.jsonMode){console.log(JSON.stringify(e));return}let n=t?.display,formatValue=(e,n=!1)=>t?.showNull&&e===null?`NULL`:e==null?`N/A`:e instanceof Date?r(e,{addSuffix:!0}):typeof e==`object`?n?JSON.stringify(e,null,2):JSON.stringify(e):String(e),isExcluded=e=>n!==void 0&&e in n&&n[e]===null,transformValue=(e,t,r,i=!1)=>{if(n&&e in n){let i=n[e];if(i)return i(t,r)}return formatValue(t,i)};if(!Array.isArray(e)){let t=renderTable(Object.entries(e).filter(([e])=>!isExcluded(e)).map(([t,n])=>[t,transformValue(t,n,e,!0)]),{singleLine:!1});process.stdout.write(renderFor(process.stdout,t));return}if(e.length===0)return;let i=Array.from(new Set(e.flatMap(e=>Object.keys(e)))).filter(e=>!isExcluded(e));if(i.length===0)return;let a=renderTable([i,...e.map(e=>i.map(t=>transformValue(t,e[t],e)))],{drawHorizontalLine:(e,t)=>e===0||e===1||e===t});process.stdout.write(renderFor(process.stdout,a))}};export{E as a,renderRedacted as c,g as d,parseBoolean as f,jsonDefaultAgent as i,setJsonDefaultAgent as l,m as n,plainRedacted as o,renderTable as p,interactiveTerminal as r,redactSecrets as s,CIPromptError as t,h as u};
9
+ //# sourceMappingURL=logger-RgtbhJ83.mjs.map