@ddt-tools/cli 0.2.0 → 0.2.4

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 (203) hide show
  1. package/dist/advise-tests-YNMKVJCD.js +87 -0
  2. package/dist/advise-tests-YNMKVJCD.js.map +1 -0
  3. package/dist/ai-NTNPYEKZ.js +86 -0
  4. package/dist/ai-NTNPYEKZ.js.map +1 -0
  5. package/dist/anonymize-LERTWUQO.js +139 -0
  6. package/dist/anonymize-LERTWUQO.js.map +1 -0
  7. package/dist/approval-GGZGKIU4.js +73 -0
  8. package/dist/approval-GGZGKIU4.js.map +1 -0
  9. package/dist/approval-chain-GWJKZHVU.js +118 -0
  10. package/dist/approval-chain-GWJKZHVU.js.map +1 -0
  11. package/dist/audit-log-2PH55BU4.js +159 -0
  12. package/dist/audit-log-2PH55BU4.js.map +1 -0
  13. package/dist/backlog-QNXGOUF4.js +76 -0
  14. package/dist/backlog-QNXGOUF4.js.map +1 -0
  15. package/dist/bisect-W3XKKRWG.js +111 -0
  16. package/dist/bisect-W3XKKRWG.js.map +1 -0
  17. package/dist/bookmarks-XVOGXGMC.js +107 -0
  18. package/dist/bookmarks-XVOGXGMC.js.map +1 -0
  19. package/dist/branch-S3I2IJGQ.js +103 -0
  20. package/dist/branch-S3I2IJGQ.js.map +1 -0
  21. package/dist/build-MP3JQEFO.js +20 -0
  22. package/dist/build-MP3JQEFO.js.map +1 -0
  23. package/dist/catalog-3J3NFNXP.js +137 -0
  24. package/dist/catalog-3J3NFNXP.js.map +1 -0
  25. package/dist/changelog-ZQAH3ULB.js +216 -0
  26. package/dist/changelog-ZQAH3ULB.js.map +1 -0
  27. package/dist/chunk-2FT6HXKS.js +55 -0
  28. package/dist/chunk-2FT6HXKS.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/chunk-DGUM43GV.js.map +1 -0
  31. package/dist/chunk-DL3V7UJ2.js +25 -0
  32. package/dist/chunk-DL3V7UJ2.js.map +1 -0
  33. package/dist/chunk-VM2H4LAO.js +15 -0
  34. package/dist/chunk-VM2H4LAO.js.map +1 -0
  35. package/dist/chunk-XFXG347C.js +40 -0
  36. package/dist/chunk-XFXG347C.js.map +1 -0
  37. package/dist/cli.js +499 -19402
  38. package/dist/cli.js.map +1 -1
  39. package/dist/compare-P7JOV76O.js +379 -0
  40. package/dist/compare-P7JOV76O.js.map +1 -0
  41. package/dist/compare-profiles-H33CXZPD.js +219 -0
  42. package/dist/compare-profiles-H33CXZPD.js.map +1 -0
  43. package/dist/completion-ZSNCQKJ2.js +89 -0
  44. package/dist/completion-ZSNCQKJ2.js.map +1 -0
  45. package/dist/connection-CDGVEFUC.js +148 -0
  46. package/dist/connection-CDGVEFUC.js.map +1 -0
  47. package/dist/cost-estimate-S2MKHT2H.js +321 -0
  48. package/dist/cost-estimate-S2MKHT2H.js.map +1 -0
  49. package/dist/data-compare-46ZI7KHL.js +128 -0
  50. package/dist/data-compare-46ZI7KHL.js.map +1 -0
  51. package/dist/data-fit-WGEPLD5S.js +127 -0
  52. package/dist/data-fit-WGEPLD5S.js.map +1 -0
  53. package/dist/deploy-status-4H5KJFRC.js +58 -0
  54. package/dist/deploy-status-4H5KJFRC.js.map +1 -0
  55. package/dist/design-ILX3ZSWW.js +135 -0
  56. package/dist/design-ILX3ZSWW.js.map +1 -0
  57. package/dist/diagnose-WPUL67E4.js +150 -0
  58. package/dist/diagnose-WPUL67E4.js.map +1 -0
  59. package/dist/discover-DEO2R5T6.js +78 -0
  60. package/dist/discover-DEO2R5T6.js.map +1 -0
  61. package/dist/docs-QNY3MUVO.js +183 -0
  62. package/dist/docs-QNY3MUVO.js.map +1 -0
  63. package/dist/drift-FDRNPWQA.js +233 -0
  64. package/dist/drift-FDRNPWQA.js.map +1 -0
  65. package/dist/drift-gate-6BWWWMHW.js +103 -0
  66. package/dist/drift-gate-6BWWWMHW.js.map +1 -0
  67. package/dist/error-lookup-4R3Y4RBC.js +56 -0
  68. package/dist/error-lookup-4R3Y4RBC.js.map +1 -0
  69. package/dist/errorReporting-3LPE2IJY.js +109 -0
  70. package/dist/errorReporting-3LPE2IJY.js.map +1 -0
  71. package/dist/exec-JOLH5LPT.js +122 -0
  72. package/dist/exec-JOLH5LPT.js.map +1 -0
  73. package/dist/explain-NS26WE2Y.js +189 -0
  74. package/dist/explain-NS26WE2Y.js.map +1 -0
  75. package/dist/explorer-GSYYYOAL.js +58 -0
  76. package/dist/explorer-GSYYYOAL.js.map +1 -0
  77. package/dist/extract-4LWEZG4O.js +152 -0
  78. package/dist/extract-4LWEZG4O.js.map +1 -0
  79. package/dist/features-KQV4OFIZ.js +54 -0
  80. package/dist/features-KQV4OFIZ.js.map +1 -0
  81. package/dist/feedback-CBLGXUEG.js +158 -0
  82. package/dist/feedback-CBLGXUEG.js.map +1 -0
  83. package/dist/find-SMXRCZ76.js +176 -0
  84. package/dist/find-SMXRCZ76.js.map +1 -0
  85. package/dist/format-HMGG6MY3.js +277 -0
  86. package/dist/format-HMGG6MY3.js.map +1 -0
  87. package/dist/generate-W7VLBDLI.js +160 -0
  88. package/dist/generate-W7VLBDLI.js.map +1 -0
  89. package/dist/graph-YYL5UYCJ.js +168 -0
  90. package/dist/graph-YYL5UYCJ.js.map +1 -0
  91. package/dist/history-GDRFP4PG.js +184 -0
  92. package/dist/history-GDRFP4PG.js.map +1 -0
  93. package/dist/hosts-DRFZTMIJ.js +45 -0
  94. package/dist/hosts-DRFZTMIJ.js.map +1 -0
  95. package/dist/impact-A4NU6CB2.js +63 -0
  96. package/dist/impact-A4NU6CB2.js.map +1 -0
  97. package/dist/import-2RNYDL4E.js +79 -0
  98. package/dist/import-2RNYDL4E.js.map +1 -0
  99. package/dist/index.cjs +11 -5
  100. package/dist/index.cjs.map +1 -1
  101. package/dist/index.js +8 -2
  102. package/dist/index.js.map +1 -1
  103. package/dist/init-EAOGNGXI.js +54 -0
  104. package/dist/init-EAOGNGXI.js.map +1 -0
  105. package/dist/install-hooks-G3Y5LVXK.js +109 -0
  106. package/dist/install-hooks-G3Y5LVXK.js.map +1 -0
  107. package/dist/license-Z5YSC7XQ.js +43 -0
  108. package/dist/license-Z5YSC7XQ.js.map +1 -0
  109. package/dist/lineage-C5CGVP36.js +555 -0
  110. package/dist/lineage-C5CGVP36.js.map +1 -0
  111. package/dist/lint-AQFPZ3WG.js +144 -0
  112. package/dist/lint-AQFPZ3WG.js.map +1 -0
  113. package/dist/mcp-F7FND5X7.js +343 -0
  114. package/dist/mcp-F7FND5X7.js.map +1 -0
  115. package/dist/migrate-from-dbt-K4ELOWUD.js +156 -0
  116. package/dist/migrate-from-dbt-K4ELOWUD.js.map +1 -0
  117. package/dist/migrate-platform-E7VZFPO5.js +91 -0
  118. package/dist/migrate-platform-E7VZFPO5.js.map +1 -0
  119. package/dist/optimize-WUJ5ZN5Y.js +109 -0
  120. package/dist/optimize-WUJ5ZN5Y.js.map +1 -0
  121. package/dist/perf-UULZSREY.js +200 -0
  122. package/dist/perf-UULZSREY.js.map +1 -0
  123. package/dist/pii-QHU32VML.js +146 -0
  124. package/dist/pii-QHU32VML.js.map +1 -0
  125. package/dist/pilot-BR6GVK32.js +29 -0
  126. package/dist/pilot-BR6GVK32.js.map +1 -0
  127. package/dist/pr-comment-2FOA3EXG.js +81 -0
  128. package/dist/pr-comment-2FOA3EXG.js.map +1 -0
  129. package/dist/preview-XNY422OU.js +46 -0
  130. package/dist/preview-XNY422OU.js.map +1 -0
  131. package/dist/profile-SQTBNKYS.js +98 -0
  132. package/dist/profile-SQTBNKYS.js.map +1 -0
  133. package/dist/promote-FSGUPIPD.js +417 -0
  134. package/dist/promote-FSGUPIPD.js.map +1 -0
  135. package/dist/publish-AYCRMCE2.js +739 -0
  136. package/dist/publish-AYCRMCE2.js.map +1 -0
  137. package/dist/purge-Y5IOTXKA.js +56 -0
  138. package/dist/purge-Y5IOTXKA.js.map +1 -0
  139. package/dist/query-log-SDDGMJLJ.js +112 -0
  140. package/dist/query-log-SDDGMJLJ.js.map +1 -0
  141. package/dist/refactor-TC7S43F2.js +5809 -0
  142. package/dist/refactor-TC7S43F2.js.map +1 -0
  143. package/dist/refresh-MDJYOYV5.js +39 -0
  144. package/dist/refresh-MDJYOYV5.js.map +1 -0
  145. package/dist/replay-E4664A5K.js +118 -0
  146. package/dist/replay-E4664A5K.js.map +1 -0
  147. package/dist/revert-QWQWCJJB.js +111 -0
  148. package/dist/revert-QWQWCJJB.js.map +1 -0
  149. package/dist/review-7CAVLD67.js +164 -0
  150. package/dist/review-7CAVLD67.js.map +1 -0
  151. package/dist/rollback-suggest-C6D5YFCA.js +79 -0
  152. package/dist/rollback-suggest-C6D5YFCA.js.map +1 -0
  153. package/dist/safer-alternative-QR4QEFUV.js +84 -0
  154. package/dist/safer-alternative-QR4QEFUV.js.map +1 -0
  155. package/dist/safety-OFWUFLK4.js +165 -0
  156. package/dist/safety-OFWUFLK4.js.map +1 -0
  157. package/dist/savings-MEBE4TXI.js +95 -0
  158. package/dist/savings-MEBE4TXI.js.map +1 -0
  159. package/dist/scan-secrets-XCUBMLHL.js +54 -0
  160. package/dist/scan-secrets-XCUBMLHL.js.map +1 -0
  161. package/dist/schema-7JZIG6QR.js +447 -0
  162. package/dist/schema-7JZIG6QR.js.map +1 -0
  163. package/dist/script-BMYVBHFR.js +167 -0
  164. package/dist/script-BMYVBHFR.js.map +1 -0
  165. package/dist/search-TA3C3AZT.js +151 -0
  166. package/dist/search-TA3C3AZT.js.map +1 -0
  167. package/dist/seed-W4Q3L2IU.js +101 -0
  168. package/dist/seed-W4Q3L2IU.js.map +1 -0
  169. package/dist/sketch-6B2V6FJV.js +83 -0
  170. package/dist/sketch-6B2V6FJV.js.map +1 -0
  171. package/dist/snapshot-YMVS322L.js +171 -0
  172. package/dist/snapshot-YMVS322L.js.map +1 -0
  173. package/dist/snippets-EVTN63OU.js +74 -0
  174. package/dist/snippets-EVTN63OU.js.map +1 -0
  175. package/dist/standards-FGJW3CQL.js +238 -0
  176. package/dist/standards-FGJW3CQL.js.map +1 -0
  177. package/dist/suggest-V3LVIFZ5.js +44 -0
  178. package/dist/suggest-V3LVIFZ5.js.map +1 -0
  179. package/dist/suggest-constraints-EX2FCWOQ.js +154 -0
  180. package/dist/suggest-constraints-EX2FCWOQ.js.map +1 -0
  181. package/dist/suite-YTQ3CNX5.js +85 -0
  182. package/dist/suite-YTQ3CNX5.js.map +1 -0
  183. package/dist/telemetry-KOIY3NEQ.js +90 -0
  184. package/dist/telemetry-KOIY3NEQ.js.map +1 -0
  185. package/dist/template-MUJ6X6LN.js +396 -0
  186. package/dist/template-MUJ6X6LN.js.map +1 -0
  187. package/dist/test-XFSQHR2S.js +169 -0
  188. package/dist/test-XFSQHR2S.js.map +1 -0
  189. package/dist/trial-GFTGYCR3.js +31 -0
  190. package/dist/trial-GFTGYCR3.js.map +1 -0
  191. package/dist/validate-LFDEZFFH.js +107 -0
  192. package/dist/validate-LFDEZFFH.js.map +1 -0
  193. package/dist/verify-KRDYOJCR.js +76 -0
  194. package/dist/verify-KRDYOJCR.js.map +1 -0
  195. package/dist/watch-FSG23RR3.js +80 -0
  196. package/dist/watch-FSG23RR3.js.map +1 -0
  197. package/dist/xcompare-U4TXTTIR.js +87 -0
  198. package/dist/xcompare-U4TXTTIR.js.map +1 -0
  199. package/package.json +2 -2
  200. package/dist/cli.cjs +0 -19298
  201. package/dist/cli.cjs.map +0 -1
  202. package/dist/cli.d.cts +0 -1
  203. package/dist/cli.d.ts +0 -1
@@ -0,0 +1,122 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/exec.ts
4
+ import { Command } from "commander";
5
+ import { promises as fs } from "fs";
6
+ import { getProfile, createConnection } from "@ddt-tools/core/connection";
7
+ import { queryExecution } from "@ddt-tools/core";
8
+ var PROD_PATTERN = /\bprod(uction)?\b/i;
9
+ function isProductionProfile(name) {
10
+ return PROD_PATTERN.test(name);
11
+ }
12
+ async function runOnProfile(execFn, profile, sql, timeoutMs) {
13
+ const start = Date.now();
14
+ try {
15
+ const res = await execFn(profile, sql, timeoutMs);
16
+ return { ...res, profile, durationMs: Date.now() - start };
17
+ } catch (err) {
18
+ return {
19
+ profile,
20
+ status: "error",
21
+ durationMs: Date.now() - start,
22
+ error: err instanceof Error ? err.message : String(err)
23
+ };
24
+ }
25
+ }
26
+ function renderText(results) {
27
+ const lines = [];
28
+ for (const r of results) {
29
+ if (r.status === "success") {
30
+ const rows = r.rowsAffected !== void 0 ? ` (${r.rowsAffected} rows)` : "";
31
+ lines.push(` ${r.profile}: \u2713${rows} \u2014 ${(r.durationMs / 1e3).toFixed(1)}s`);
32
+ } else {
33
+ lines.push(` ${r.profile}: \u2717 ${r.error ?? "unknown error"}`);
34
+ }
35
+ }
36
+ const total = results.length;
37
+ const succeeded = results.filter((r) => r.status === "success").length;
38
+ lines.push(`
39
+ ${succeeded}/${total} profiles succeeded.`);
40
+ return lines.join("\n");
41
+ }
42
+ async function defaultExecFn(profile, sql, timeoutMs) {
43
+ const profileObj = await getProfile(profile);
44
+ const conn = createConnection(profileObj);
45
+ await conn.connect();
46
+ try {
47
+ const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1e3));
48
+ const runner = {
49
+ executeStatement: async (statement) => {
50
+ const result2 = await conn.query(statement, { timeoutSeconds });
51
+ return {
52
+ rows: result2.rows,
53
+ durationMs: result2.durationMs
54
+ };
55
+ }
56
+ };
57
+ const result = await queryExecution.executeStatements(sql, runner, {
58
+ failFast: false,
59
+ splitOptions: { dialect: "databricks" },
60
+ toolName: "ddt"
61
+ });
62
+ const firstError = result.statements.find((s) => s.error);
63
+ if (firstError && firstError.error) {
64
+ throw new Error(firstError.error.message);
65
+ }
66
+ const rowsAffected = result.statements.reduce((acc, s) => acc + (s.rowCount ?? 0), 0);
67
+ return { status: "success", rowsAffected };
68
+ } finally {
69
+ await conn.disconnect().catch(() => {
70
+ });
71
+ }
72
+ }
73
+ function execCommand(execFn = defaultExecFn) {
74
+ const cmd = new Command("exec");
75
+ cmd.description("Run a SQL script on one or more connection profiles in parallel.").argument("<file>", "Path to the .sql file to execute.").requiredOption("--profiles <list>", "Comma-separated connection profile names.").option("--yes", "Confirm execution against production profiles without prompting.").option("--format <fmt>", "text | json (default text).", "text").option("--timeout <ms>", "Per-profile timeout in milliseconds.", "30000").action(async (file, opts) => {
76
+ const profiles = opts.profiles.split(",").map((p) => p.trim()).filter(Boolean);
77
+ if (profiles.length === 0) throw new Error("--profiles must list at least one profile.");
78
+ const prodProfiles = profiles.filter(isProductionProfile);
79
+ if (prodProfiles.length > 0 && !opts.yes) {
80
+ throw new Error(
81
+ `Profile(s) look like production: ${prodProfiles.join(", ")}. Pass --yes to confirm.`
82
+ );
83
+ }
84
+ let sql;
85
+ try {
86
+ sql = await fs.readFile(file, "utf8");
87
+ } catch {
88
+ throw new Error(`Cannot read file: ${file}`);
89
+ }
90
+ if (!sql.trim()) throw new Error(`File is empty: ${file}`);
91
+ const timeoutMs = parseInt(String(opts.timeout ?? "30000"), 10);
92
+ const results = await Promise.all(
93
+ profiles.map((p) => runOnProfile(execFn, p, sql, timeoutMs))
94
+ );
95
+ const fmt = String(opts.format ?? "text").toLowerCase();
96
+ if (fmt === "json") {
97
+ const failed = results.filter((r) => r.status === "error").length;
98
+ process.stdout.write(
99
+ JSON.stringify(
100
+ {
101
+ results,
102
+ summary: {
103
+ total: results.length,
104
+ succeeded: results.length - failed,
105
+ failed
106
+ }
107
+ },
108
+ null,
109
+ 2
110
+ ) + "\n"
111
+ );
112
+ return;
113
+ }
114
+ if (fmt !== "text") throw new Error(`Unknown --format: ${opts.format}. Use text | json.`);
115
+ process.stdout.write(renderText(results) + "\n");
116
+ });
117
+ return cmd;
118
+ }
119
+ export {
120
+ execCommand
121
+ };
122
+ //# sourceMappingURL=exec-JOLH5LPT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/exec.ts"],"sourcesContent":["/**\n * `ddt exec <file.sql> --profiles <list>` — AUTH.3.\n *\n * Runs a SQL / DeltaSQL script on one or more connection profiles in\n * parallel and prints a merged report. Guarded against any profile\n * whose name matches \"prod\" or \"production\" (case-insensitive) unless\n * `--yes` is passed.\n *\n * Mirrors `Snowflake/packages/cli/src/commands/exec.ts`.\n */\nimport { Command } from 'commander';\nimport { promises as fs } from 'node:fs';\nimport { getProfile, createConnection } from '@ddt-tools/core/connection';\nimport { queryExecution } from '@ddt-tools/core';\n\nexport interface ExecResult {\n profile: string;\n status: 'success' | 'error';\n rowsAffected?: number;\n durationMs: number;\n error?: string;\n}\n\nexport type ExecFn = (\n profile: string,\n sql: string,\n timeoutMs: number,\n) => Promise<Omit<ExecResult, 'profile' | 'durationMs'>>;\n\nconst PROD_PATTERN = /\\bprod(uction)?\\b/i;\n\nfunction isProductionProfile(name: string): boolean {\n return PROD_PATTERN.test(name);\n}\n\nasync function runOnProfile(\n execFn: ExecFn,\n profile: string,\n sql: string,\n timeoutMs: number,\n): Promise<ExecResult> {\n const start = Date.now();\n try {\n const res = await execFn(profile, sql, timeoutMs);\n return { ...res, profile, durationMs: Date.now() - start };\n } catch (err) {\n return {\n profile,\n status: 'error',\n durationMs: Date.now() - start,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nfunction renderText(results: ExecResult[]): string {\n const lines: string[] = [];\n for (const r of results) {\n if (r.status === 'success') {\n const rows = r.rowsAffected !== undefined ? ` (${r.rowsAffected} rows)` : '';\n lines.push(` ${r.profile}: ✓${rows} — ${(r.durationMs / 1000).toFixed(1)}s`);\n } else {\n lines.push(` ${r.profile}: ✗ ${r.error ?? 'unknown error'}`);\n }\n }\n const total = results.length;\n const succeeded = results.filter((r) => r.status === 'success').length;\n lines.push(`\\n${succeeded}/${total} profiles succeeded.`);\n return lines.join('\\n');\n}\n\n/**\n * Wire to the real Databricks connection layer:\n * 1. Resolve the named profile (`~/.ddt/profiles.json`).\n * 2. Open a connection (PAT bearer or OAuth, per profile.auth).\n * 3. Split the SQL via the shared `queryExecution.splitStatements`\n * (Databricks dialect: backtick identifiers, no dollar-quotes)\n * and run each statement sequentially through\n * `DatabricksConnection.query`. We do NOT `failFast` so a script\n * with N statements surfaces every error.\n * 4. Aggregate: success iff every statement succeeded; the first\n * error's message becomes the profile-level error. Total\n * `rowsAffected` is the sum of rowCount across statements.\n *\n * Injectable for tests via the `execFn` parameter on `execCommand`.\n */\nasync function defaultExecFn(\n profile: string,\n sql: string,\n timeoutMs: number,\n): Promise<Omit<ExecResult, 'profile' | 'durationMs'>> {\n const profileObj = await getProfile(profile);\n const conn = createConnection(profileObj);\n await conn.connect();\n try {\n const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));\n const runner: queryExecution.QueryRunner = {\n executeStatement: async (statement: string) => {\n const result = await conn.query(statement, { timeoutSeconds });\n return {\n rows: result.rows as Record<string, unknown>[],\n durationMs: result.durationMs,\n };\n },\n };\n const result = await queryExecution.executeStatements(sql, runner, {\n failFast: false,\n splitOptions: { dialect: 'databricks' },\n toolName: 'ddt',\n });\n const firstError = result.statements.find((s) => s.error);\n if (firstError && firstError.error) {\n throw new Error(firstError.error.message);\n }\n const rowsAffected = result.statements.reduce((acc, s) => acc + (s.rowCount ?? 0), 0);\n return { status: 'success', rowsAffected };\n } finally {\n await conn.disconnect().catch(() => {\n /* best-effort cleanup */\n });\n }\n}\n\nexport function execCommand(execFn: ExecFn = defaultExecFn): Command {\n const cmd = new Command('exec');\n cmd\n .description('Run a SQL script on one or more connection profiles in parallel.')\n .argument('<file>', 'Path to the .sql file to execute.')\n .requiredOption('--profiles <list>', 'Comma-separated connection profile names.')\n .option('--yes', 'Confirm execution against production profiles without prompting.')\n .option('--format <fmt>', 'text | json (default text).', 'text')\n .option('--timeout <ms>', 'Per-profile timeout in milliseconds.', '30000')\n .action(async (file: string, opts) => {\n const profiles = (opts.profiles as string)\n .split(',')\n .map((p) => p.trim())\n .filter(Boolean);\n if (profiles.length === 0) throw new Error('--profiles must list at least one profile.');\n\n const prodProfiles = profiles.filter(isProductionProfile);\n if (prodProfiles.length > 0 && !opts.yes) {\n throw new Error(\n `Profile(s) look like production: ${prodProfiles.join(', ')}. ` +\n `Pass --yes to confirm.`,\n );\n }\n\n let sql: string;\n try {\n sql = await fs.readFile(file, 'utf8');\n } catch {\n throw new Error(`Cannot read file: ${file}`);\n }\n if (!sql.trim()) throw new Error(`File is empty: ${file}`);\n\n const timeoutMs = parseInt(String(opts.timeout ?? '30000'), 10);\n const results = await Promise.all(\n profiles.map((p) => runOnProfile(execFn, p, sql, timeoutMs)),\n );\n\n const fmt = String(opts.format ?? 'text').toLowerCase();\n if (fmt === 'json') {\n const failed = results.filter((r) => r.status === 'error').length;\n process.stdout.write(\n JSON.stringify(\n {\n results,\n summary: {\n total: results.length,\n succeeded: results.length - failed,\n failed,\n },\n },\n null,\n 2,\n ) + '\\n',\n );\n return;\n }\n if (fmt !== 'text') throw new Error(`Unknown --format: ${opts.format}. Use text | json.`);\n process.stdout.write(renderText(results) + '\\n');\n });\n return cmd;\n}\n"],"mappings":"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","names":["result"]}
@@ -0,0 +1,189 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/explain.ts
4
+ import { Command } from "commander";
5
+ import { options as optionsApi } from "@ddt-tools/core";
6
+ function explainCommand() {
7
+ const cmd = new Command("explain");
8
+ cmd.description(
9
+ "Search the options catalog. Explains what an option does, when to use it, and what it pairs with."
10
+ ).argument("[query...]", "Free-form search \u2014 flag name, alias, or text.").option(
11
+ "--tag <tag>",
12
+ "Filter by tag (e.g. compare, deployment, safety, drop, column, streaming, governance)."
13
+ ).option(
14
+ "--safety <tier>",
15
+ "Filter by safety tier (safe, review, data-impacting, destructive, unrecoverable)."
16
+ ).option("--limit <n>", "Max results to show.", "8").option("--list", "List all options grouped by tag.").option("--format <fmt>", "Output format: text | json | markdown. Default text.", "text").action(async (queryParts, opts) => {
17
+ const query = (queryParts ?? []).join(" ").trim();
18
+ const limit = Number.parseInt(String(opts.limit ?? "8"), 10) || 8;
19
+ const safety = opts.safety;
20
+ const tag = opts.tag;
21
+ const fmt = String(opts.format ?? "text").toLowerCase();
22
+ if (opts.list) {
23
+ const all = optionsApi.listOptions({ tag, safety });
24
+ if (fmt === "json") {
25
+ console.log(JSON.stringify(all, null, 2));
26
+ } else if (fmt === "markdown") {
27
+ console.log(renderMarkdownList(all));
28
+ } else {
29
+ printSummaryList(all);
30
+ }
31
+ return;
32
+ }
33
+ if (!query && !tag && !safety) {
34
+ cmd.outputHelp();
35
+ return;
36
+ }
37
+ const hits = optionsApi.searchOptions(query, { tag, safety, limit });
38
+ if (hits.length === 0) {
39
+ if (fmt === "json") {
40
+ console.log("[]");
41
+ return;
42
+ }
43
+ console.warn(`No matches for: "${query || `${tag ?? ""} ${safety ?? ""}`.trim()}"`);
44
+ console.warn(" Try: ddt explain --list");
45
+ console.warn(" Try: ddt explain --tag safety");
46
+ return;
47
+ }
48
+ if (fmt === "json") {
49
+ console.log(
50
+ JSON.stringify(
51
+ hits.map((h) => h.entry),
52
+ null,
53
+ 2
54
+ )
55
+ );
56
+ return;
57
+ }
58
+ if (fmt === "markdown") {
59
+ for (const [i, hit] of hits.entries()) {
60
+ if (i > 0) console.log("");
61
+ console.log(renderMarkdownEntry(hit.entry));
62
+ }
63
+ return;
64
+ }
65
+ for (const [i, hit] of hits.entries()) {
66
+ if (i > 0) console.log("");
67
+ printEntry(hit.entry);
68
+ }
69
+ });
70
+ return cmd;
71
+ }
72
+ function renderMarkdownEntry(entry) {
73
+ const lines = [];
74
+ const badge = entry.safety ? ` _(safety: ${entry.safety})_` : "";
75
+ lines.push(`### \`${entry.name}\`${badge}`);
76
+ lines.push("");
77
+ lines.push(`**${entry.summary}**`);
78
+ lines.push("");
79
+ lines.push(`- **short**: ${entry.short}`);
80
+ if (entry.aliases.length > 0)
81
+ lines.push(`- **aliases**: ${entry.aliases.map((a) => `\`${a}\``).join(", ")}`);
82
+ lines.push(`- **type**: \`${entry.type}\``);
83
+ lines.push(`- **default**: \`${entry.defaultValue}\``);
84
+ if (entry.enumValues)
85
+ lines.push(`- **values**: ${entry.enumValues.map((v) => `\`${v}\``).join(", ")}`);
86
+ lines.push(`- **path**: \`${entry.path}\``);
87
+ lines.push(`- **tags**: ${entry.tags.map((t) => `\`${t}\``).join(", ")}`);
88
+ if (entry.example) {
89
+ lines.push("");
90
+ lines.push("```");
91
+ lines.push(entry.example);
92
+ lines.push("```");
93
+ }
94
+ if (entry.details) {
95
+ lines.push("");
96
+ lines.push("**Details**:");
97
+ lines.push("");
98
+ for (const line of entry.details.split(/\r?\n/)) lines.push(`> ${line}`);
99
+ }
100
+ if (entry.worksWellWith && entry.worksWellWith.length > 0) {
101
+ lines.push("");
102
+ lines.push("**Works well with**:");
103
+ for (const ref of entry.worksWellWith) lines.push(`- \`${ref}\``);
104
+ }
105
+ if (entry.conflictsWith && entry.conflictsWith.length > 0) {
106
+ lines.push("");
107
+ lines.push("**Conflicts with**:");
108
+ for (const ref of entry.conflictsWith) lines.push(`- \`${ref}\``);
109
+ }
110
+ return lines.join("\n");
111
+ }
112
+ function renderMarkdownList(entries) {
113
+ const grouped = /* @__PURE__ */ new Map();
114
+ for (const entry of entries) {
115
+ const tag = entry.tags[0] ?? "misc";
116
+ const arr = grouped.get(tag) ?? [];
117
+ arr.push(entry);
118
+ grouped.set(tag, arr);
119
+ }
120
+ const lines = [];
121
+ lines.push("# Options catalog");
122
+ lines.push("");
123
+ for (const tag of [...grouped.keys()].sort()) {
124
+ lines.push(`## ${tag} (${grouped.get(tag).length})`);
125
+ lines.push("");
126
+ for (const e of grouped.get(tag)) {
127
+ const badge = e.safety ? ` _(${e.safety})_` : "";
128
+ lines.push(`- \`${e.short}\`${badge} \u2014 ${e.summary}`);
129
+ }
130
+ lines.push("");
131
+ }
132
+ return lines.join("\n");
133
+ }
134
+ function printEntry(entry) {
135
+ const tags = entry.tags.join(", ");
136
+ const safetyBadge = entry.safety ? ` [safety: ${entry.safety}]` : "";
137
+ console.log(`${entry.name}${safetyBadge}`);
138
+ console.log(` short: ${entry.short}`);
139
+ if (entry.aliases.length > 0) {
140
+ console.log(` aliases: ${entry.aliases.join(", ")}`);
141
+ }
142
+ console.log(` type: ${entry.type}`);
143
+ console.log(` default: ${entry.defaultValue}`);
144
+ if (entry.enumValues) console.log(` values: ${entry.enumValues.join(", ")}`);
145
+ console.log(` path: ${entry.path}`);
146
+ console.log(` tags: ${tags}`);
147
+ console.log(` summary: ${entry.summary}`);
148
+ console.log(` details:`);
149
+ for (const line of entry.details.split(/\r?\n/)) {
150
+ console.log(` ${line}`);
151
+ }
152
+ if (entry.example) {
153
+ console.log(` example: ${entry.example}`);
154
+ }
155
+ if (entry.worksWellWith && entry.worksWellWith.length > 0) {
156
+ console.log(` works well with:`);
157
+ for (const ref of entry.worksWellWith) console.log(` - ${ref}`);
158
+ }
159
+ if (entry.conflictsWith && entry.conflictsWith.length > 0) {
160
+ console.log(` conflicts with:`);
161
+ for (const ref of entry.conflictsWith) console.log(` - ${ref}`);
162
+ }
163
+ if (entry.seeAlso && entry.seeAlso.length > 0) {
164
+ console.log(` see also: ${entry.seeAlso.join(", ")}`);
165
+ }
166
+ }
167
+ function printSummaryList(entries) {
168
+ const grouped = /* @__PURE__ */ new Map();
169
+ for (const entry of entries) {
170
+ const tag = entry.tags[0] ?? "misc";
171
+ const arr = grouped.get(tag) ?? [];
172
+ arr.push(entry);
173
+ grouped.set(tag, arr);
174
+ }
175
+ const tagOrder = [...grouped.keys()].sort();
176
+ for (const tag of tagOrder) {
177
+ console.log(`# ${tag} (${grouped.get(tag).length})`);
178
+ for (const e of grouped.get(tag)) {
179
+ const badge = e.safety ? ` [${e.safety}]` : "";
180
+ console.log(` ${e.short}${badge}`);
181
+ console.log(` ${e.summary}`);
182
+ }
183
+ console.log("");
184
+ }
185
+ }
186
+ export {
187
+ explainCommand
188
+ };
189
+ //# sourceMappingURL=explain-NS26WE2Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/explain.ts"],"sourcesContent":["/**\n * `ddt explain <query>` — search the options catalog and print details.\n *\n * Examples:\n * ddt explain drop table\n * ddt explain allowNarrowingTypes\n * ddt explain --tag safety\n * ddt explain --safety destructive\n *\n * See `docs/UX_PLAYBOOK.md` §2 for the full design.\n */\nimport { Command } from 'commander';\nimport { options as optionsApi, type options as optionsNs } from '@ddt-tools/core';\n\ntype OptionEntry = optionsNs.OptionEntry;\ntype SafetyTier = optionsNs.SafetyTier;\n\nexport function explainCommand(): Command {\n const cmd = new Command('explain');\n cmd\n .description(\n 'Search the options catalog. Explains what an option does, when to use it, and what it pairs with.',\n )\n .argument('[query...]', 'Free-form search — flag name, alias, or text.')\n .option(\n '--tag <tag>',\n 'Filter by tag (e.g. compare, deployment, safety, drop, column, streaming, governance).',\n )\n .option(\n '--safety <tier>',\n 'Filter by safety tier (safe, review, data-impacting, destructive, unrecoverable).',\n )\n .option('--limit <n>', 'Max results to show.', '8')\n .option('--list', 'List all options grouped by tag.')\n .option('--format <fmt>', 'Output format: text | json | markdown. Default text.', 'text')\n .action(async (queryParts: string[], opts) => {\n const query = (queryParts ?? []).join(' ').trim();\n const limit = Number.parseInt(String(opts.limit ?? '8'), 10) || 8;\n const safety = opts.safety as SafetyTier | undefined;\n const tag = opts.tag as string | undefined;\n const fmt = String(opts.format ?? 'text').toLowerCase();\n\n if (opts.list) {\n const all = optionsApi.listOptions({ tag, safety });\n if (fmt === 'json') {\n console.log(JSON.stringify(all, null, 2));\n } else if (fmt === 'markdown') {\n console.log(renderMarkdownList(all));\n } else {\n printSummaryList(all);\n }\n return;\n }\n\n if (!query && !tag && !safety) {\n cmd.outputHelp();\n return;\n }\n\n const hits = optionsApi.searchOptions(query, { tag, safety, limit });\n if (hits.length === 0) {\n if (fmt === 'json') {\n console.log('[]');\n return;\n }\n console.warn(`No matches for: \"${query || `${tag ?? ''} ${safety ?? ''}`.trim()}\"`);\n console.warn(' Try: ddt explain --list');\n console.warn(' Try: ddt explain --tag safety');\n return;\n }\n\n if (fmt === 'json') {\n console.log(\n JSON.stringify(\n hits.map((h) => h.entry),\n null,\n 2,\n ),\n );\n return;\n }\n if (fmt === 'markdown') {\n for (const [i, hit] of hits.entries()) {\n if (i > 0) console.log('');\n console.log(renderMarkdownEntry(hit.entry));\n }\n return;\n }\n\n for (const [i, hit] of hits.entries()) {\n if (i > 0) console.log('');\n printEntry(hit.entry);\n }\n });\n return cmd;\n}\n\nfunction renderMarkdownEntry(entry: OptionEntry): string {\n const lines: string[] = [];\n const badge = entry.safety ? ` _(safety: ${entry.safety})_` : '';\n lines.push(`### \\`${entry.name}\\`${badge}`);\n lines.push('');\n lines.push(`**${entry.summary}**`);\n lines.push('');\n lines.push(`- **short**: ${entry.short}`);\n if (entry.aliases.length > 0)\n lines.push(`- **aliases**: ${entry.aliases.map((a) => `\\`${a}\\``).join(', ')}`);\n lines.push(`- **type**: \\`${entry.type}\\``);\n lines.push(`- **default**: \\`${entry.defaultValue}\\``);\n if (entry.enumValues)\n lines.push(`- **values**: ${entry.enumValues.map((v) => `\\`${v}\\``).join(', ')}`);\n lines.push(`- **path**: \\`${entry.path}\\``);\n lines.push(`- **tags**: ${entry.tags.map((t) => `\\`${t}\\``).join(', ')}`);\n if (entry.example) {\n lines.push('');\n lines.push('```');\n lines.push(entry.example);\n lines.push('```');\n }\n if (entry.details) {\n lines.push('');\n lines.push('**Details**:');\n lines.push('');\n for (const line of entry.details.split(/\\r?\\n/)) lines.push(`> ${line}`);\n }\n if (entry.worksWellWith && entry.worksWellWith.length > 0) {\n lines.push('');\n lines.push('**Works well with**:');\n for (const ref of entry.worksWellWith) lines.push(`- \\`${ref}\\``);\n }\n if (entry.conflictsWith && entry.conflictsWith.length > 0) {\n lines.push('');\n lines.push('**Conflicts with**:');\n for (const ref of entry.conflictsWith) lines.push(`- \\`${ref}\\``);\n }\n return lines.join('\\n');\n}\n\nfunction renderMarkdownList(entries: readonly OptionEntry[]): string {\n const grouped = new Map<string, OptionEntry[]>();\n for (const entry of entries) {\n const tag = entry.tags[0] ?? 'misc';\n const arr = grouped.get(tag) ?? [];\n arr.push(entry);\n grouped.set(tag, arr);\n }\n const lines: string[] = [];\n lines.push('# Options catalog');\n lines.push('');\n for (const tag of [...grouped.keys()].sort()) {\n lines.push(`## ${tag} (${grouped.get(tag)!.length})`);\n lines.push('');\n for (const e of grouped.get(tag)!) {\n const badge = e.safety ? ` _(${e.safety})_` : '';\n lines.push(`- \\`${e.short}\\`${badge} — ${e.summary}`);\n }\n lines.push('');\n }\n return lines.join('\\n');\n}\n\nfunction printEntry(entry: OptionEntry): void {\n const tags = entry.tags.join(', ');\n const safetyBadge = entry.safety ? ` [safety: ${entry.safety}]` : '';\n console.log(`${entry.name}${safetyBadge}`);\n console.log(` short: ${entry.short}`);\n if (entry.aliases.length > 0) {\n console.log(` aliases: ${entry.aliases.join(', ')}`);\n }\n console.log(` type: ${entry.type}`);\n console.log(` default: ${entry.defaultValue}`);\n if (entry.enumValues) console.log(` values: ${entry.enumValues.join(', ')}`);\n console.log(` path: ${entry.path}`);\n console.log(` tags: ${tags}`);\n console.log(` summary: ${entry.summary}`);\n console.log(` details:`);\n for (const line of entry.details.split(/\\r?\\n/)) {\n console.log(` ${line}`);\n }\n if (entry.example) {\n console.log(` example: ${entry.example}`);\n }\n if (entry.worksWellWith && entry.worksWellWith.length > 0) {\n console.log(` works well with:`);\n for (const ref of entry.worksWellWith) console.log(` - ${ref}`);\n }\n if (entry.conflictsWith && entry.conflictsWith.length > 0) {\n console.log(` conflicts with:`);\n for (const ref of entry.conflictsWith) console.log(` - ${ref}`);\n }\n if (entry.seeAlso && entry.seeAlso.length > 0) {\n console.log(` see also: ${entry.seeAlso.join(', ')}`);\n }\n}\n\nfunction printSummaryList(entries: readonly OptionEntry[]): void {\n const grouped = new Map<string, OptionEntry[]>();\n for (const entry of entries) {\n const tag = entry.tags[0] ?? 'misc';\n const arr = grouped.get(tag) ?? [];\n arr.push(entry);\n grouped.set(tag, arr);\n }\n const tagOrder = [...grouped.keys()].sort();\n for (const tag of tagOrder) {\n console.log(`# ${tag} (${grouped.get(tag)!.length})`);\n for (const e of grouped.get(tag)!) {\n const badge = e.safety ? ` [${e.safety}]` : '';\n console.log(` ${e.short}${badge}`);\n console.log(` ${e.summary}`);\n }\n console.log('');\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,58 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/explorer.ts
4
+ import { Command } from "commander";
5
+ import { catalog, objectExplorer } from "@ddt-tools/core";
6
+ function explorerCommand() {
7
+ const cmd = new Command("explorer");
8
+ cmd.description(
9
+ "ASCII tree dump of the cached catalog for a connection. Run `ddt catalog refresh` first to populate."
10
+ ).requiredOption("--connection <name>", "Connection profile name (the cache subfolder).").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--filter <query>", "Typeahead substring filter (case-insensitive).").option(
11
+ "--depth <n>",
12
+ "Truncate tree at depth N (0 = root only, 1 = +catalogs, 2 = +schemas, 3 = +object-groups, 4 = +objects). Default unlimited.",
13
+ (v) => parseInt(v, 10)
14
+ ).option("--json", "Emit tree as JSON instead of ASCII.").action(async (opts) => {
15
+ const cache = new catalog.CatalogCache({
16
+ root: String(opts.root),
17
+ connection: String(opts.connection)
18
+ });
19
+ const snapshot = await cache.get();
20
+ let tree = objectExplorer.treeForSnapshot(snapshot);
21
+ if (opts.filter) tree = objectExplorer.filterTree(tree, String(opts.filter));
22
+ if (typeof opts.depth === "number") tree = objectExplorer.truncateTree(tree, opts.depth);
23
+ if (snapshot.catalogs.length === 0) {
24
+ console.warn(`Catalog cache is empty for connection "${opts.connection}".`);
25
+ console.warn(` Cache file: ${cache.path}`);
26
+ console.warn(` Run \`ddt catalog refresh --connection ${opts.connection}\` to populate.`);
27
+ return;
28
+ }
29
+ if (opts.json) {
30
+ console.log(JSON.stringify(tree, null, 2));
31
+ return;
32
+ }
33
+ renderAsciiTree(tree);
34
+ });
35
+ return cmd;
36
+ }
37
+ function renderAsciiTree(root) {
38
+ const desc = root.description ? ` (${root.description})` : "";
39
+ console.log(`${root.label}${desc}`);
40
+ const children = root.children ?? [];
41
+ for (let i = 0; i < children.length; i++) {
42
+ renderChild(children[i], "", i === children.length - 1);
43
+ }
44
+ }
45
+ function renderChild(node, prefix, isLast) {
46
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
47
+ const desc = node.description ? ` (${node.description})` : "";
48
+ console.log(`${prefix}${connector}${node.label}${desc}`);
49
+ const children = node.children ?? [];
50
+ const childPrefix = prefix + (isLast ? " " : "\u2502 ");
51
+ for (let i = 0; i < children.length; i++) {
52
+ renderChild(children[i], childPrefix, i === children.length - 1);
53
+ }
54
+ }
55
+ export {
56
+ explorerCommand
57
+ };
58
+ //# sourceMappingURL=explorer-GSYYYOAL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/explorer.ts"],"sourcesContent":["/**\n * `ddt explorer` — ASCII tree dump of the live catalog/schema/object\n * hierarchy for a connected target, read from the EE1 catalog cache.\n *\n * The eventual VS Code TreeDataProvider walks the same `treeForSnapshot`\n * shape; this CLI gives non-VS-Code users the same browsable view of the\n * cached catalog. No live queries are issued — the cache is read-only.\n * Run `ddt catalog refresh --connection <name>` first to populate it.\n *\n * Inputs:\n * --connection <name> Required. Connection profile name to load the cache for.\n * --root <path> Project root (where .ddt/cache/ lives). Default cwd.\n * --filter <query> Typeahead filter; case-insensitive substring match.\n * Keeps every ancestor of a leaf match.\n * --json Emit the tree as JSON instead of an ASCII tree.\n */\nimport { Command } from 'commander';\nimport { catalog, objectExplorer } from '@ddt-tools/core';\n\nexport function explorerCommand(): Command {\n const cmd = new Command('explorer');\n cmd\n .description(\n 'ASCII tree dump of the cached catalog for a connection. Run `ddt catalog refresh` first to populate.',\n )\n .requiredOption('--connection <name>', 'Connection profile name (the cache subfolder).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--filter <query>', 'Typeahead substring filter (case-insensitive).')\n .option(\n '--depth <n>',\n 'Truncate tree at depth N (0 = root only, 1 = +catalogs, 2 = +schemas, 3 = +object-groups, 4 = +objects). Default unlimited.',\n (v) => parseInt(v, 10),\n )\n .option('--json', 'Emit tree as JSON instead of ASCII.')\n .action(async (opts) => {\n const cache = new catalog.CatalogCache({\n root: String(opts.root),\n connection: String(opts.connection),\n });\n const snapshot = await cache.get();\n let tree = objectExplorer.treeForSnapshot(snapshot);\n if (opts.filter) tree = objectExplorer.filterTree(tree, String(opts.filter));\n if (typeof opts.depth === 'number') tree = objectExplorer.truncateTree(tree, opts.depth);\n\n if (snapshot.catalogs.length === 0) {\n console.warn(`Catalog cache is empty for connection \"${opts.connection}\".`);\n console.warn(` Cache file: ${cache.path}`);\n console.warn(` Run \\`ddt catalog refresh --connection ${opts.connection}\\` to populate.`);\n return;\n }\n\n if (opts.json) {\n console.log(JSON.stringify(tree, null, 2));\n return;\n }\n\n renderAsciiTree(tree);\n });\n return cmd;\n}\n\n/**\n * Render the explorer tree as an ASCII tree (├──, │, └──). Mirrors the\n * GNU `tree`(1) glyph set for familiarity. Root prints without a\n * connector; descendants get the standard branch glyphs.\n */\nfunction renderAsciiTree(root: objectExplorer.ExplorerNode): void {\n const desc = root.description ? ` (${root.description})` : '';\n console.log(`${root.label}${desc}`);\n const children = root.children ?? [];\n for (let i = 0; i < children.length; i++) {\n renderChild(children[i]!, '', i === children.length - 1);\n }\n}\n\nfunction renderChild(node: objectExplorer.ExplorerNode, prefix: string, isLast: boolean): void {\n const connector = isLast ? '└── ' : '├── ';\n const desc = node.description ? ` (${node.description})` : '';\n console.log(`${prefix}${connector}${node.label}${desc}`);\n const children = node.children ?? [];\n const childPrefix = prefix + (isLast ? ' ' : '│ ');\n for (let i = 0; i < children.length; i++) {\n renderChild(children[i]!, childPrefix, i === children.length - 1);\n }\n}\n"],"mappings":";;;AAgBA,SAAS,eAAe;AACxB,SAAS,SAAS,sBAAsB;AAEjC,SAAS,kBAA2B;AACzC,QAAM,MAAM,IAAI,QAAQ,UAAU;AAClC,MACG;AAAA,IACC;AAAA,EACF,EACC,eAAe,uBAAuB,gDAAgD,EACtF,OAAO,iBAAiB,8BAA8B,QAAQ,IAAI,CAAC,EACnE,OAAO,oBAAoB,gDAAgD,EAC3E;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,MAAM,SAAS,GAAG,EAAE;AAAA,EACvB,EACC,OAAO,UAAU,qCAAqC,EACtD,OAAO,OAAO,SAAS;AACtB,UAAM,QAAQ,IAAI,QAAQ,aAAa;AAAA,MACrC,MAAM,OAAO,KAAK,IAAI;AAAA,MACtB,YAAY,OAAO,KAAK,UAAU;AAAA,IACpC,CAAC;AACD,UAAM,WAAW,MAAM,MAAM,IAAI;AACjC,QAAI,OAAO,eAAe,gBAAgB,QAAQ;AAClD,QAAI,KAAK,OAAQ,QAAO,eAAe,WAAW,MAAM,OAAO,KAAK,MAAM,CAAC;AAC3E,QAAI,OAAO,KAAK,UAAU,SAAU,QAAO,eAAe,aAAa,MAAM,KAAK,KAAK;AAEvF,QAAI,SAAS,SAAS,WAAW,GAAG;AAClC,cAAQ,KAAK,0CAA0C,KAAK,UAAU,IAAI;AAC1E,cAAQ,KAAK,iBAAiB,MAAM,IAAI,EAAE;AAC1C,cAAQ,KAAK,4CAA4C,KAAK,UAAU,iBAAiB;AACzF;AAAA,IACF;AAEA,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACzC;AAAA,IACF;AAEA,oBAAgB,IAAI;AAAA,EACtB,CAAC;AACH,SAAO;AACT;AAOA,SAAS,gBAAgB,MAAyC;AAChE,QAAM,OAAO,KAAK,cAAc,MAAM,KAAK,WAAW,MAAM;AAC5D,UAAQ,IAAI,GAAG,KAAK,KAAK,GAAG,IAAI,EAAE;AAClC,QAAM,WAAW,KAAK,YAAY,CAAC;AACnC,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAY,SAAS,CAAC,GAAI,IAAI,MAAM,SAAS,SAAS,CAAC;AAAA,EACzD;AACF;AAEA,SAAS,YAAY,MAAmC,QAAgB,QAAuB;AAC7F,QAAM,YAAY,SAAS,wBAAS;AACpC,QAAM,OAAO,KAAK,cAAc,MAAM,KAAK,WAAW,MAAM;AAC5D,UAAQ,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,KAAK,GAAG,IAAI,EAAE;AACvD,QAAM,WAAW,KAAK,YAAY,CAAC;AACnC,QAAM,cAAc,UAAU,SAAS,SAAS;AAChD,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAY,SAAS,CAAC,GAAI,aAAa,MAAM,SAAS,SAAS,CAAC;AAAA,EAClE;AACF;","names":[]}
@@ -0,0 +1,152 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/extract.ts
4
+ import path from "path";
5
+ import { promises as fs } from "fs";
6
+ import { Command } from "commander";
7
+ import {
8
+ AccountExtractor,
9
+ catalog,
10
+ createConnection,
11
+ defaultExtractors,
12
+ getProfile,
13
+ pac
14
+ } from "@ddt-tools/core";
15
+ function extractCommand() {
16
+ const cmd = new Command("extract");
17
+ cmd.description(
18
+ "Extract live UC objects from a workspace into a project tree (--output) or a .ddtpac (--out-pac)."
19
+ ).requiredOption(
20
+ "-c, --connection <name>",
21
+ "Connection profile name (see `ddt connection list`)."
22
+ ).option("--catalog <catalog>", "Limit extraction to a single catalog.").option("--schema <schema>", "Limit extraction to a single schema (requires --catalog).").option("--output <dir>", "Write a project tree at <dir> (catalogs/.../<NAME>.sql).").option("--out-pac <file>", "Write a .ddtpac at <file> (artifact mode).").option(
23
+ "--project-name <name>",
24
+ 'Project name stamped into manifest.json. Default: derived from --catalog or "ExtractedProject".'
25
+ ).option(
26
+ "--write-catalog-cache",
27
+ "Also write the extracted model to the EE1 catalog cache (.ddt/cache/<connection>/catalog.msgpack) so subsequent editor sessions have a populated cache without an extra `catalog refresh` round-trip.",
28
+ false
29
+ ).option(
30
+ "--use-catalog-cache",
31
+ "Read object metadata from the on-disk EE1 catalog cache instead of querying Databricks. Fast / offline \u2014 no live connection required. Writes a catalog-snapshot.json to the output dir. Requires a prior `ddt catalog refresh` or `ddt extract --write-catalog-cache` run.",
32
+ false
33
+ ).action(async (opts) => {
34
+ if (opts.useCatalogCache) {
35
+ const out = path.resolve(String(opts.output ?? opts.outPac ?? "."));
36
+ await fs.mkdir(out, { recursive: true });
37
+ const cache = new catalog.CatalogCache({ root: out, connection: String(opts.connection) });
38
+ const snapshot = await cache.get();
39
+ if (snapshot.catalogs.length === 0 && snapshot.fingerprint === null) {
40
+ console.error(
41
+ `No catalog cache found at ${cache.path}. Run "ddt catalog refresh -c ${String(opts.connection)}" or "ddt extract --write-catalog-cache" first.`
42
+ );
43
+ process.exitCode = 1;
44
+ return;
45
+ }
46
+ const outPath = path.join(out, ".ddt-cache", "catalog-snapshot.json");
47
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
48
+ await fs.writeFile(outPath, JSON.stringify(snapshot, null, 2));
49
+ const objCount = snapshot.catalogs.flatMap(
50
+ (c) => c.schemas.flatMap((s) => s.objects)
51
+ ).length;
52
+ console.log(
53
+ `Loaded ${objCount} object(s) from catalog cache (${snapshot.catalogs.length} catalog(s)) \u2192 ${outPath}`
54
+ );
55
+ console.log(
56
+ `Cache was snapshotted at ${snapshot.snapshotAt}. Use "ddt catalog refresh" to update it.`
57
+ );
58
+ return;
59
+ }
60
+ const hasOutput = !!opts.output;
61
+ const hasPac = !!opts.outPac;
62
+ if (!hasOutput && !hasPac) {
63
+ throw new Error("Specify --output <dir> and/or --out-pac <file>.");
64
+ }
65
+ if (opts.schema && !opts.catalog) {
66
+ throw new Error("--schema requires --catalog.");
67
+ }
68
+ const profile = await getProfile(String(opts.connection));
69
+ const conn = createConnection(profile);
70
+ try {
71
+ await conn.connect();
72
+ const accountExtractor = new AccountExtractor(defaultExtractors());
73
+ const scope = {
74
+ ...opts.catalog ? { catalog: String(opts.catalog) } : {},
75
+ ...opts.schema ? { schema: String(opts.schema) } : {}
76
+ };
77
+ const t0 = Date.now();
78
+ const model = await accountExtractor.extract(conn, scope);
79
+ const elapsedSec = ((Date.now() - t0) / 1e3).toFixed(1);
80
+ console.log(
81
+ `Extracted ${model.length} object(s) from ${profile.auth.host} in ${elapsedSec}s.`
82
+ );
83
+ if (hasOutput) {
84
+ const root = path.resolve(String(opts.output));
85
+ await fs.mkdir(root, { recursive: true });
86
+ await writeProjectTree(root, model);
87
+ console.log(` \u2192 wrote project tree to ${root}`);
88
+ }
89
+ if (opts.writeCatalogCache) {
90
+ const cacheRoot = opts.output ? path.resolve(String(opts.output)) : path.dirname(path.resolve(String(opts.outPac)));
91
+ const snapshot = catalog.snapshotFromObjects(model, {
92
+ connection: String(opts.connection)
93
+ });
94
+ const cache = new catalog.CatalogCache({
95
+ root: cacheRoot,
96
+ connection: String(opts.connection)
97
+ });
98
+ await cache.set(snapshot);
99
+ console.log(
100
+ ` \u2192 wrote catalog cache to ${cache.path} (${snapshot.catalogs.length} catalog(s), ${model.length} object(s)).`
101
+ );
102
+ }
103
+ if (hasPac) {
104
+ const projectName = String(opts.projectName ?? "") || (opts.catalog ? `${String(opts.catalog)}-extract` : "ExtractedProject");
105
+ const outPac = path.resolve(String(opts.outPac));
106
+ await fs.mkdir(path.dirname(outPac), { recursive: true });
107
+ await pac.writePac(outPac, {
108
+ manifest: {
109
+ formatVersion: 1,
110
+ projectName,
111
+ projectVersion: "0.0.0",
112
+ scope: opts.schema ? { type: "schema", catalog: String(opts.catalog), schema: String(opts.schema) } : opts.catalog ? { type: "catalog", catalog: String(opts.catalog) } : { type: "metastore" },
113
+ targetPlatform: { platform: "Databricks" },
114
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
115
+ builtBy: { name: "ddt", version: "0.3.0" },
116
+ objectCount: model.length
117
+ },
118
+ model,
119
+ source: {},
120
+ checksums: pac.computeChecksums(model)
121
+ });
122
+ console.log(` \u2192 wrote .ddtpac to ${outPac}`);
123
+ }
124
+ } finally {
125
+ await conn.disconnect();
126
+ }
127
+ });
128
+ return cmd;
129
+ }
130
+ async function writeProjectTree(root, model) {
131
+ const { objectFilePath } = await import("@ddt-tools/core");
132
+ for (const obj of model) {
133
+ let target;
134
+ try {
135
+ target = objectFilePath(root, obj.objectType, {
136
+ ...obj.fqn.database ? { database: obj.fqn.database } : {},
137
+ ...obj.fqn.schema ? { schema: obj.fqn.schema } : {},
138
+ name: obj.fqn.name
139
+ });
140
+ } catch {
141
+ target = path.join(root, "_unsorted", `${obj.objectType}__${obj.fqn.name}.sql`);
142
+ }
143
+ await fs.mkdir(path.dirname(target), { recursive: true });
144
+ const ddl = "ddl" in obj && typeof obj.ddl === "string" ? obj.ddl : `-- ${obj.objectType} ${[obj.fqn.database, obj.fqn.schema, obj.fqn.name].filter(Boolean).join(".")}
145
+ -- (no DDL captured \u2014 extractor returned model-only)`;
146
+ await fs.writeFile(target, ddl.trim() + "\n", "utf8");
147
+ }
148
+ }
149
+ export {
150
+ extractCommand
151
+ };
152
+ //# sourceMappingURL=extract-4LWEZG4O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/extract.ts"],"sourcesContent":["import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport { Command } from 'commander';\nimport {\n AccountExtractor,\n catalog,\n createConnection,\n defaultExtractors,\n getProfile,\n pac,\n type DatabricksObject,\n} from '@ddt-tools/core';\n\n/**\n * `ddt extract` — read live workspace state into a `.ddtpac` artifact (or\n * onto disk as a project tree).\n *\n * Walks the configured scope (catalog / schema) via `AccountExtractor`,\n * hits the Statement Execution API + workspace REST for every UC object\n * type the registry knows about, and writes the result as either:\n * - `--output <dir>` → unzipped project tree under `<dir>/catalogs/...`\n * - `--out-pac <file>` → packed `.ddtpac` artifact\n *\n * The two modes are mutually exclusive; at least one is required.\n */\nexport function extractCommand(): Command {\n const cmd = new Command('extract');\n cmd\n .description(\n 'Extract live UC objects from a workspace into a project tree (--output) or a .ddtpac (--out-pac).',\n )\n .requiredOption(\n '-c, --connection <name>',\n 'Connection profile name (see `ddt connection list`).',\n )\n .option('--catalog <catalog>', 'Limit extraction to a single catalog.')\n .option('--schema <schema>', 'Limit extraction to a single schema (requires --catalog).')\n .option('--output <dir>', 'Write a project tree at <dir> (catalogs/.../<NAME>.sql).')\n .option('--out-pac <file>', 'Write a .ddtpac at <file> (artifact mode).')\n .option(\n '--project-name <name>',\n 'Project name stamped into manifest.json. Default: derived from --catalog or \"ExtractedProject\".',\n )\n .option(\n '--write-catalog-cache',\n 'Also write the extracted model to the EE1 catalog cache (.ddt/cache/<connection>/catalog.msgpack) so subsequent editor sessions have a populated cache without an extra `catalog refresh` round-trip.',\n false,\n )\n .option(\n '--use-catalog-cache',\n 'Read object metadata from the on-disk EE1 catalog cache instead of querying Databricks. Fast / offline — no live connection required. Writes a catalog-snapshot.json to the output dir. Requires a prior `ddt catalog refresh` or `ddt extract --write-catalog-cache` run.',\n false,\n )\n .action(async (opts) => {\n if (opts.useCatalogCache) {\n const out = path.resolve(String(opts.output ?? opts.outPac ?? '.'));\n await fs.mkdir(out, { recursive: true });\n const cache = new catalog.CatalogCache({ root: out, connection: String(opts.connection) });\n const snapshot = await cache.get();\n if (snapshot.catalogs.length === 0 && snapshot.fingerprint === null) {\n console.error(\n `No catalog cache found at ${cache.path}. Run \"ddt catalog refresh -c ${String(opts.connection)}\" or \"ddt extract --write-catalog-cache\" first.`,\n );\n process.exitCode = 1;\n return;\n }\n const outPath = path.join(out, '.ddt-cache', 'catalog-snapshot.json');\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, JSON.stringify(snapshot, null, 2));\n const objCount = snapshot.catalogs.flatMap((c) =>\n c.schemas.flatMap((s) => s.objects),\n ).length;\n console.log(\n `Loaded ${objCount} object(s) from catalog cache (${snapshot.catalogs.length} catalog(s)) → ${outPath}`,\n );\n console.log(\n `Cache was snapshotted at ${snapshot.snapshotAt}. Use \"ddt catalog refresh\" to update it.`,\n );\n return;\n }\n\n const hasOutput = !!opts.output;\n const hasPac = !!opts.outPac;\n if (!hasOutput && !hasPac) {\n throw new Error('Specify --output <dir> and/or --out-pac <file>.');\n }\n if (opts.schema && !opts.catalog) {\n throw new Error('--schema requires --catalog.');\n }\n\n const profile = await getProfile(String(opts.connection));\n const conn = createConnection(profile);\n try {\n await conn.connect();\n const accountExtractor = new AccountExtractor(defaultExtractors());\n const scope = {\n ...(opts.catalog ? { catalog: String(opts.catalog) } : {}),\n ...(opts.schema ? { schema: String(opts.schema) } : {}),\n };\n const t0 = Date.now();\n const model = await accountExtractor.extract(conn, scope);\n const elapsedSec = ((Date.now() - t0) / 1000).toFixed(1);\n console.log(\n `Extracted ${model.length} object(s) from ${profile.auth.host} in ${elapsedSec}s.`,\n );\n\n if (hasOutput) {\n const root = path.resolve(String(opts.output));\n await fs.mkdir(root, { recursive: true });\n await writeProjectTree(root, model);\n console.log(` → wrote project tree to ${root}`);\n }\n if (opts.writeCatalogCache) {\n // Honour --output as the cache root when set; otherwise --out-pac's\n // parent dir. Either way the cache lands at <root>/.ddt/cache/<conn>/.\n const cacheRoot = opts.output\n ? path.resolve(String(opts.output))\n : path.dirname(path.resolve(String(opts.outPac)));\n const snapshot = catalog.snapshotFromObjects(model, {\n connection: String(opts.connection),\n });\n const cache = new catalog.CatalogCache({\n root: cacheRoot,\n connection: String(opts.connection),\n });\n await cache.set(snapshot);\n console.log(\n ` → wrote catalog cache to ${cache.path} (${snapshot.catalogs.length} catalog(s), ${model.length} object(s)).`,\n );\n }\n if (hasPac) {\n const projectName =\n String(opts.projectName ?? '') ||\n (opts.catalog ? `${String(opts.catalog)}-extract` : 'ExtractedProject');\n const outPac = path.resolve(String(opts.outPac));\n await fs.mkdir(path.dirname(outPac), { recursive: true });\n await pac.writePac(outPac, {\n manifest: {\n formatVersion: 1,\n projectName,\n projectVersion: '0.0.0',\n scope: opts.schema\n ? { type: 'schema', catalog: String(opts.catalog), schema: String(opts.schema) }\n : opts.catalog\n ? { type: 'catalog', catalog: String(opts.catalog) }\n : { type: 'metastore' },\n targetPlatform: { platform: 'Databricks' },\n builtAt: new Date().toISOString(),\n builtBy: { name: 'ddt', version: '0.3.0' },\n objectCount: model.length,\n },\n model,\n source: {},\n checksums: pac.computeChecksums(model),\n });\n console.log(` → wrote .ddtpac to ${outPac}`);\n }\n } finally {\n await conn.disconnect();\n }\n });\n return cmd;\n}\n\nasync function writeProjectTree(root: string, model: DatabricksObject[]): Promise<void> {\n const { objectFilePath } = await import('@ddt-tools/core');\n for (const obj of model) {\n let target: string;\n try {\n target = objectFilePath(root, obj.objectType, {\n ...(obj.fqn.database ? { database: obj.fqn.database } : {}),\n ...(obj.fqn.schema ? { schema: obj.fqn.schema } : {}),\n name: obj.fqn.name,\n });\n } catch {\n // No folder mapping for this type — drop to a generic _unsorted/ bucket.\n target = path.join(root, '_unsorted', `${obj.objectType}__${obj.fqn.name}.sql`);\n }\n await fs.mkdir(path.dirname(target), { recursive: true });\n const ddl =\n 'ddl' in obj && typeof (obj as { ddl?: unknown }).ddl === 'string'\n ? ((obj as { ddl: string }).ddl as string)\n : `-- ${obj.objectType} ${[obj.fqn.database, obj.fqn.schema, obj.fqn.name].filter(Boolean).join('.')}\\n-- (no DDL captured — extractor returned model-only)`;\n await fs.writeFile(target, ddl.trim() + '\\n', 'utf8');\n }\n}\n"],"mappings":"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","names":[]}