@ddt-tools/cli 0.2.0 → 0.2.5
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.
- package/dist/advise-tests-YNMKVJCD.js +87 -0
- package/dist/advise-tests-YNMKVJCD.js.map +1 -0
- package/dist/ai-NTNPYEKZ.js +86 -0
- package/dist/ai-NTNPYEKZ.js.map +1 -0
- package/dist/anonymize-LERTWUQO.js +139 -0
- package/dist/anonymize-LERTWUQO.js.map +1 -0
- package/dist/approval-GGZGKIU4.js +73 -0
- package/dist/approval-GGZGKIU4.js.map +1 -0
- package/dist/approval-chain-GWJKZHVU.js +118 -0
- package/dist/approval-chain-GWJKZHVU.js.map +1 -0
- package/dist/audit-log-2PH55BU4.js +159 -0
- package/dist/audit-log-2PH55BU4.js.map +1 -0
- package/dist/backlog-QNXGOUF4.js +76 -0
- package/dist/backlog-QNXGOUF4.js.map +1 -0
- package/dist/bisect-W3XKKRWG.js +111 -0
- package/dist/bisect-W3XKKRWG.js.map +1 -0
- package/dist/bookmarks-XVOGXGMC.js +107 -0
- package/dist/bookmarks-XVOGXGMC.js.map +1 -0
- package/dist/branch-S3I2IJGQ.js +103 -0
- package/dist/branch-S3I2IJGQ.js.map +1 -0
- package/dist/build-MP3JQEFO.js +20 -0
- package/dist/build-MP3JQEFO.js.map +1 -0
- package/dist/catalog-3J3NFNXP.js +137 -0
- package/dist/catalog-3J3NFNXP.js.map +1 -0
- package/dist/changelog-ZQAH3ULB.js +216 -0
- package/dist/changelog-ZQAH3ULB.js.map +1 -0
- package/dist/chunk-2FT6HXKS.js +55 -0
- package/dist/chunk-2FT6HXKS.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-DL3V7UJ2.js +25 -0
- package/dist/chunk-DL3V7UJ2.js.map +1 -0
- package/dist/chunk-VM2H4LAO.js +15 -0
- package/dist/chunk-VM2H4LAO.js.map +1 -0
- package/dist/chunk-XFXG347C.js +40 -0
- package/dist/chunk-XFXG347C.js.map +1 -0
- package/dist/cli.js +504 -19402
- package/dist/cli.js.map +1 -1
- package/dist/compare-IOEATL6G.js +435 -0
- package/dist/compare-IOEATL6G.js.map +1 -0
- package/dist/compare-profiles-H33CXZPD.js +219 -0
- package/dist/compare-profiles-H33CXZPD.js.map +1 -0
- package/dist/completion-ZSNCQKJ2.js +89 -0
- package/dist/completion-ZSNCQKJ2.js.map +1 -0
- package/dist/connection-CDGVEFUC.js +148 -0
- package/dist/connection-CDGVEFUC.js.map +1 -0
- package/dist/cost-estimate-S2MKHT2H.js +321 -0
- package/dist/cost-estimate-S2MKHT2H.js.map +1 -0
- package/dist/data-compare-46ZI7KHL.js +128 -0
- package/dist/data-compare-46ZI7KHL.js.map +1 -0
- package/dist/data-fit-WGEPLD5S.js +127 -0
- package/dist/data-fit-WGEPLD5S.js.map +1 -0
- package/dist/deploy-status-4H5KJFRC.js +58 -0
- package/dist/deploy-status-4H5KJFRC.js.map +1 -0
- package/dist/design-ILX3ZSWW.js +135 -0
- package/dist/design-ILX3ZSWW.js.map +1 -0
- package/dist/diagnose-WPUL67E4.js +150 -0
- package/dist/diagnose-WPUL67E4.js.map +1 -0
- package/dist/discover-DEO2R5T6.js +78 -0
- package/dist/discover-DEO2R5T6.js.map +1 -0
- package/dist/docs-QNY3MUVO.js +183 -0
- package/dist/docs-QNY3MUVO.js.map +1 -0
- package/dist/drift-FDRNPWQA.js +233 -0
- package/dist/drift-FDRNPWQA.js.map +1 -0
- package/dist/drift-gate-6BWWWMHW.js +103 -0
- package/dist/drift-gate-6BWWWMHW.js.map +1 -0
- package/dist/error-lookup-4R3Y4RBC.js +56 -0
- package/dist/error-lookup-4R3Y4RBC.js.map +1 -0
- package/dist/errorReporting-LX6WT4JH.js +109 -0
- package/dist/errorReporting-LX6WT4JH.js.map +1 -0
- package/dist/exec-JOLH5LPT.js +122 -0
- package/dist/exec-JOLH5LPT.js.map +1 -0
- package/dist/explain-NS26WE2Y.js +189 -0
- package/dist/explain-NS26WE2Y.js.map +1 -0
- package/dist/explorer-GSYYYOAL.js +58 -0
- package/dist/explorer-GSYYYOAL.js.map +1 -0
- package/dist/extract-4LWEZG4O.js +152 -0
- package/dist/extract-4LWEZG4O.js.map +1 -0
- package/dist/features-KQV4OFIZ.js +54 -0
- package/dist/features-KQV4OFIZ.js.map +1 -0
- package/dist/feedback-CBLGXUEG.js +158 -0
- package/dist/feedback-CBLGXUEG.js.map +1 -0
- package/dist/find-SMXRCZ76.js +176 -0
- package/dist/find-SMXRCZ76.js.map +1 -0
- package/dist/format-HMGG6MY3.js +277 -0
- package/dist/format-HMGG6MY3.js.map +1 -0
- package/dist/generate-W7VLBDLI.js +160 -0
- package/dist/generate-W7VLBDLI.js.map +1 -0
- package/dist/graph-YYL5UYCJ.js +168 -0
- package/dist/graph-YYL5UYCJ.js.map +1 -0
- package/dist/history-GDRFP4PG.js +184 -0
- package/dist/history-GDRFP4PG.js.map +1 -0
- package/dist/hosts-DRFZTMIJ.js +45 -0
- package/dist/hosts-DRFZTMIJ.js.map +1 -0
- package/dist/impact-A4NU6CB2.js +63 -0
- package/dist/impact-A4NU6CB2.js.map +1 -0
- package/dist/import-EGOVKTLX.js +29 -0
- package/dist/import-EGOVKTLX.js.map +1 -0
- package/dist/import-script-R5RXPDH6.js +79 -0
- package/dist/import-script-R5RXPDH6.js.map +1 -0
- package/dist/index.cjs +11 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/init-EAOGNGXI.js +54 -0
- package/dist/init-EAOGNGXI.js.map +1 -0
- package/dist/install-hooks-G3Y5LVXK.js +109 -0
- package/dist/install-hooks-G3Y5LVXK.js.map +1 -0
- package/dist/license-Z5YSC7XQ.js +43 -0
- package/dist/license-Z5YSC7XQ.js.map +1 -0
- package/dist/lineage-C5CGVP36.js +555 -0
- package/dist/lineage-C5CGVP36.js.map +1 -0
- package/dist/lint-AQFPZ3WG.js +144 -0
- package/dist/lint-AQFPZ3WG.js.map +1 -0
- package/dist/mcp-6ZXOAF7S.js +343 -0
- package/dist/mcp-6ZXOAF7S.js.map +1 -0
- package/dist/migrate-from-dbt-K4ELOWUD.js +156 -0
- package/dist/migrate-from-dbt-K4ELOWUD.js.map +1 -0
- package/dist/migrate-platform-E7VZFPO5.js +91 -0
- package/dist/migrate-platform-E7VZFPO5.js.map +1 -0
- package/dist/optimize-WUJ5ZN5Y.js +109 -0
- package/dist/optimize-WUJ5ZN5Y.js.map +1 -0
- package/dist/perf-UULZSREY.js +200 -0
- package/dist/perf-UULZSREY.js.map +1 -0
- package/dist/pii-QHU32VML.js +146 -0
- package/dist/pii-QHU32VML.js.map +1 -0
- package/dist/pilot-BR6GVK32.js +29 -0
- package/dist/pilot-BR6GVK32.js.map +1 -0
- package/dist/pr-comment-2FOA3EXG.js +81 -0
- package/dist/pr-comment-2FOA3EXG.js.map +1 -0
- package/dist/preview-XNY422OU.js +46 -0
- package/dist/preview-XNY422OU.js.map +1 -0
- package/dist/profile-SQTBNKYS.js +98 -0
- package/dist/profile-SQTBNKYS.js.map +1 -0
- package/dist/promote-FSGUPIPD.js +417 -0
- package/dist/promote-FSGUPIPD.js.map +1 -0
- package/dist/publish-HLP3XHM5.js +766 -0
- package/dist/publish-HLP3XHM5.js.map +1 -0
- package/dist/purge-Y5IOTXKA.js +56 -0
- package/dist/purge-Y5IOTXKA.js.map +1 -0
- package/dist/query-log-SDDGMJLJ.js +112 -0
- package/dist/query-log-SDDGMJLJ.js.map +1 -0
- package/dist/refactor-TC7S43F2.js +5809 -0
- package/dist/refactor-TC7S43F2.js.map +1 -0
- package/dist/refresh-MDJYOYV5.js +39 -0
- package/dist/refresh-MDJYOYV5.js.map +1 -0
- package/dist/replay-E4664A5K.js +118 -0
- package/dist/replay-E4664A5K.js.map +1 -0
- package/dist/revert-QWQWCJJB.js +111 -0
- package/dist/revert-QWQWCJJB.js.map +1 -0
- package/dist/review-7CAVLD67.js +164 -0
- package/dist/review-7CAVLD67.js.map +1 -0
- package/dist/rollback-suggest-C6D5YFCA.js +79 -0
- package/dist/rollback-suggest-C6D5YFCA.js.map +1 -0
- package/dist/safer-alternative-QR4QEFUV.js +84 -0
- package/dist/safer-alternative-QR4QEFUV.js.map +1 -0
- package/dist/safety-OFWUFLK4.js +165 -0
- package/dist/safety-OFWUFLK4.js.map +1 -0
- package/dist/savings-MEBE4TXI.js +95 -0
- package/dist/savings-MEBE4TXI.js.map +1 -0
- package/dist/scan-secrets-XCUBMLHL.js +54 -0
- package/dist/scan-secrets-XCUBMLHL.js.map +1 -0
- package/dist/schema-7JZIG6QR.js +447 -0
- package/dist/schema-7JZIG6QR.js.map +1 -0
- package/dist/script-BMYVBHFR.js +167 -0
- package/dist/script-BMYVBHFR.js.map +1 -0
- package/dist/search-TA3C3AZT.js +151 -0
- package/dist/search-TA3C3AZT.js.map +1 -0
- package/dist/seed-W4Q3L2IU.js +101 -0
- package/dist/seed-W4Q3L2IU.js.map +1 -0
- package/dist/sketch-6B2V6FJV.js +83 -0
- package/dist/sketch-6B2V6FJV.js.map +1 -0
- package/dist/snapshot-YMVS322L.js +171 -0
- package/dist/snapshot-YMVS322L.js.map +1 -0
- package/dist/snippets-EVTN63OU.js +74 -0
- package/dist/snippets-EVTN63OU.js.map +1 -0
- package/dist/standards-FGJW3CQL.js +238 -0
- package/dist/standards-FGJW3CQL.js.map +1 -0
- package/dist/suggest-V3LVIFZ5.js +44 -0
- package/dist/suggest-V3LVIFZ5.js.map +1 -0
- package/dist/suggest-constraints-EX2FCWOQ.js +154 -0
- package/dist/suggest-constraints-EX2FCWOQ.js.map +1 -0
- package/dist/suite-YTQ3CNX5.js +85 -0
- package/dist/suite-YTQ3CNX5.js.map +1 -0
- package/dist/telemetry-KOIY3NEQ.js +90 -0
- package/dist/telemetry-KOIY3NEQ.js.map +1 -0
- package/dist/template-MUJ6X6LN.js +396 -0
- package/dist/template-MUJ6X6LN.js.map +1 -0
- package/dist/test-XFSQHR2S.js +169 -0
- package/dist/test-XFSQHR2S.js.map +1 -0
- package/dist/trial-GFTGYCR3.js +31 -0
- package/dist/trial-GFTGYCR3.js.map +1 -0
- package/dist/validate-LFDEZFFH.js +107 -0
- package/dist/validate-LFDEZFFH.js.map +1 -0
- package/dist/verify-KRDYOJCR.js +76 -0
- package/dist/verify-KRDYOJCR.js.map +1 -0
- package/dist/watch-FSG23RR3.js +80 -0
- package/dist/watch-FSG23RR3.js.map +1 -0
- package/dist/xcompare-U4TXTTIR.js +87 -0
- package/dist/xcompare-U4TXTTIR.js.map +1 -0
- package/package.json +2 -2
- package/dist/cli.cjs +0 -19298
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/error-lookup.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { errorCatalog } from "@ddt-tools/core";
|
|
6
|
+
function errorLookupCommand() {
|
|
7
|
+
const cmd = new Command("error-lookup");
|
|
8
|
+
cmd.description("Look up a failure in the known-error catalog by code, fingerprint, or message.").option("--code <code>", "Adapter error code (e.g. PERMISSION_DENIED).").option("--fingerprint <hex>", "16-char fingerprint from a prior failure.").option("--message <text>", "Free-text error message \u2014 falls back to regex match.").option("--list", "List every entry in the catalog (with code/key/cause).", false).option("--format <fmt>", "text | json. Default text.", "text").action(
|
|
9
|
+
(opts) => {
|
|
10
|
+
const fmt = (opts.format ?? "text").toLowerCase();
|
|
11
|
+
if (opts.list) {
|
|
12
|
+
const all = errorCatalog.DEFAULT_KNOWN_ERRORS;
|
|
13
|
+
if (fmt === "json") {
|
|
14
|
+
process.stdout.write(JSON.stringify(all, null, 2) + "\n");
|
|
15
|
+
} else {
|
|
16
|
+
for (const entry of all) {
|
|
17
|
+
const codeList = entry.codes?.join(", ") ?? "\u2014";
|
|
18
|
+
process.stdout.write(
|
|
19
|
+
`${entry.key}
|
|
20
|
+
codes: ${codeList}
|
|
21
|
+
cause: ${entry.causeSummary}
|
|
22
|
+
|
|
23
|
+
`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!opts.code && !opts.fingerprint && !opts.message) {
|
|
30
|
+
process.stderr.write("error-lookup: pass --code, --fingerprint, --message, or --list\n");
|
|
31
|
+
process.exitCode = 2;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const match = errorCatalog.lookupKnownError({
|
|
35
|
+
code: opts.code,
|
|
36
|
+
fingerprint: opts.fingerprint,
|
|
37
|
+
message: opts.message
|
|
38
|
+
});
|
|
39
|
+
if (!match) {
|
|
40
|
+
process.stderr.write("No catalog entry matched.\n");
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (fmt === "json") {
|
|
45
|
+
process.stdout.write(JSON.stringify(match, null, 2) + "\n");
|
|
46
|
+
} else {
|
|
47
|
+
process.stdout.write(errorCatalog.formatCatalogMatch(match) + "\n");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
return cmd;
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
errorLookupCommand
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=error-lookup-4R3Y4RBC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/error-lookup.ts"],"sourcesContent":["/**\n * `ddt error-lookup` — look up a failure in the known-error catalog (DSR.2).\n *\n * Usage:\n * ddt error-lookup --code PERMISSION_DENIED\n * ddt error-lookup --fingerprint abcd1234deadbeef\n * ddt error-lookup --message \"Permission denied\"\n * ddt error-lookup --list\n *\n * Exit codes:\n * 0 — match found, or `--list` ran\n * 1 — no match found for the supplied input\n * 2 — usage error (no --code / --fingerprint / --message / --list passed)\n *\n * Mirrors `sdt error-lookup`.\n */\nimport { Command } from 'commander';\nimport { errorCatalog } from '@ddt-tools/core';\n\nexport function errorLookupCommand(): Command {\n const cmd = new Command('error-lookup');\n cmd\n .description('Look up a failure in the known-error catalog by code, fingerprint, or message.')\n .option('--code <code>', 'Adapter error code (e.g. PERMISSION_DENIED).')\n .option('--fingerprint <hex>', '16-char fingerprint from a prior failure.')\n .option('--message <text>', 'Free-text error message — falls back to regex match.')\n .option('--list', 'List every entry in the catalog (with code/key/cause).', false)\n .option('--format <fmt>', 'text | json. Default text.', 'text')\n .action(\n (opts: {\n code?: string;\n fingerprint?: string;\n message?: string;\n list?: boolean;\n format?: string;\n }) => {\n const fmt = (opts.format ?? 'text').toLowerCase();\n if (opts.list) {\n const all = errorCatalog.DEFAULT_KNOWN_ERRORS;\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(all, null, 2) + '\\n');\n } else {\n for (const entry of all) {\n const codeList = entry.codes?.join(', ') ?? '—';\n process.stdout.write(\n `${entry.key}\\n codes: ${codeList}\\n cause: ${entry.causeSummary}\\n\\n`,\n );\n }\n }\n return;\n }\n if (!opts.code && !opts.fingerprint && !opts.message) {\n process.stderr.write('error-lookup: pass --code, --fingerprint, --message, or --list\\n');\n process.exitCode = 2;\n return;\n }\n const match = errorCatalog.lookupKnownError({\n code: opts.code,\n fingerprint: opts.fingerprint,\n message: opts.message,\n });\n if (!match) {\n process.stderr.write('No catalog entry matched.\\n');\n process.exitCode = 1;\n return;\n }\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(match, null, 2) + '\\n');\n } else {\n process.stdout.write(errorCatalog.formatCatalogMatch(match) + '\\n');\n }\n },\n );\n return cmd;\n}\n"],"mappings":";;;AAgBA,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAEtB,SAAS,qBAA8B;AAC5C,QAAM,MAAM,IAAI,QAAQ,cAAc;AACtC,MACG,YAAY,gFAAgF,EAC5F,OAAO,iBAAiB,8CAA8C,EACtE,OAAO,uBAAuB,2CAA2C,EACzE,OAAO,oBAAoB,2DAAsD,EACjF,OAAO,UAAU,0DAA0D,KAAK,EAChF,OAAO,kBAAkB,8BAA8B,MAAM,EAC7D;AAAA,IACC,CAAC,SAMK;AACJ,YAAM,OAAO,KAAK,UAAU,QAAQ,YAAY;AAChD,UAAI,KAAK,MAAM;AACb,cAAM,MAAM,aAAa;AACzB,YAAI,QAAQ,QAAQ;AAClB,kBAAQ,OAAO,MAAM,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAAA,QAC1D,OAAO;AACL,qBAAW,SAAS,KAAK;AACvB,kBAAM,WAAW,MAAM,OAAO,KAAK,IAAI,KAAK;AAC5C,oBAAQ,OAAO;AAAA,cACb,GAAG,MAAM,GAAG;AAAA,WAAc,QAAQ;AAAA,WAAc,MAAM,YAAY;AAAA;AAAA;AAAA,YACpE;AAAA,UACF;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,eAAe,CAAC,KAAK,SAAS;AACpD,gBAAQ,OAAO,MAAM,kEAAkE;AACvF,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,YAAM,QAAQ,aAAa,iBAAiB;AAAA,QAC1C,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,SAAS,KAAK;AAAA,MAChB,CAAC;AACD,UAAI,CAAC,OAAO;AACV,gBAAQ,OAAO,MAAM,6BAA6B;AAClD,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,UAAI,QAAQ,QAAQ;AAClB,gBAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,IAAI;AAAA,MAC5D,OAAO;AACL,gBAAQ,OAAO,MAAM,aAAa,mBAAmB,KAAK,IAAI,IAAI;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AACF,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
import "./chunk-DGUM43GV.js";
|
|
5
|
+
|
|
6
|
+
// src/util/errorReporting.ts
|
|
7
|
+
import { createInterface } from "readline";
|
|
8
|
+
import * as errorReport from "@ddt-tools/core/errorReport";
|
|
9
|
+
var EXEMPT_COMMANDS = /* @__PURE__ */ new Set(["telemetry", "feedback", "help", "completion"]);
|
|
10
|
+
var activeConsent = "unset";
|
|
11
|
+
var uninstallHooks = null;
|
|
12
|
+
async function setupErrorReporting(commandName) {
|
|
13
|
+
if (EXEMPT_COMMANDS.has(commandName)) return;
|
|
14
|
+
const stored = await errorReport.readConsent();
|
|
15
|
+
activeConsent = stored.consent;
|
|
16
|
+
if (shouldPromptFirstRun(stored.consent) && process.stdout.isTTY && process.stdin.isTTY && !isCi()) {
|
|
17
|
+
printBetaNotice();
|
|
18
|
+
activeConsent = await promptFirstRunConsent() ? "on" : "off";
|
|
19
|
+
await errorReport.writeConsent(activeConsent === "on" ? "on" : "off");
|
|
20
|
+
}
|
|
21
|
+
if (activeConsent !== "off") {
|
|
22
|
+
uninstallHooks = errorReport.installProcessHooks();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function finishErrorReporting(productVersion, transport = {}) {
|
|
26
|
+
await errorReport.flushErrorEvents();
|
|
27
|
+
await errorReport.sendUsagePing({
|
|
28
|
+
product: "ddt",
|
|
29
|
+
version: productVersion,
|
|
30
|
+
surface: "cli",
|
|
31
|
+
...transport.fetchImpl ? { fetchImpl: transport.fetchImpl } : {}
|
|
32
|
+
}).catch(() => void 0);
|
|
33
|
+
if (!errorReport.isErrorReportingEnabled(activeConsent)) return;
|
|
34
|
+
const spooled = await errorReport.listSpooled(transport.dir);
|
|
35
|
+
if (spooled.length === 0) return;
|
|
36
|
+
const result = await errorReport.drainSpool({ productVersion }, transport);
|
|
37
|
+
if (result.sent > 0) {
|
|
38
|
+
logger.dim(` (${result.sent} error report${result.sent === 1 ? "" : "s"} sent \u2014 thank you)`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function reportCliFailure(err, productVersion, transport = {}) {
|
|
42
|
+
errorReport.reportError(err, "handled", "cli:main");
|
|
43
|
+
await errorReport.flushErrorEvents(transport.dir);
|
|
44
|
+
if (errorReport.isErrorReportingEnabled(activeConsent)) {
|
|
45
|
+
await errorReport.drainSpool({ productVersion }, transport);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY || isCi()) return;
|
|
49
|
+
const yes = await promptYesNo("Report this error to the DDT team? [y/N] ", false);
|
|
50
|
+
if (!yes) return;
|
|
51
|
+
const result = await errorReport.drainSpool({ productVersion }, transport);
|
|
52
|
+
if (result.sent > 0) logger.dim(" Report sent \u2014 thank you.");
|
|
53
|
+
else logger.dim(" Could not reach the error endpoint; the report is queued locally.");
|
|
54
|
+
}
|
|
55
|
+
function teardownErrorReporting() {
|
|
56
|
+
uninstallHooks?.();
|
|
57
|
+
uninstallHooks = null;
|
|
58
|
+
activeConsent = "unset";
|
|
59
|
+
}
|
|
60
|
+
function activeConsentForTests() {
|
|
61
|
+
return activeConsent;
|
|
62
|
+
}
|
|
63
|
+
var BETA_VERSION = "0.2.5";
|
|
64
|
+
function shouldPromptFirstRun(consent) {
|
|
65
|
+
return consent === "unset";
|
|
66
|
+
}
|
|
67
|
+
function printBetaNotice() {
|
|
68
|
+
const lines = [
|
|
69
|
+
"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
70
|
+
`\u2502 DDT ${BETA_VERSION} \u2014 Public Beta \u2502`,
|
|
71
|
+
"\u2502 \u2022 All features are free during the beta. \u2502",
|
|
72
|
+
"\u2502 \u2022 After the beta: core features stay free forever; \u2502",
|
|
73
|
+
"\u2502 Pro features keep working and show license notices. \u2502",
|
|
74
|
+
"\u2502 \u2022 AI features use your own API key (never ours). \u2502",
|
|
75
|
+
'\u2502 \u2022 Found a bug? Run: ddt feedback "<what happened>" \u2502',
|
|
76
|
+
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
|
|
77
|
+
];
|
|
78
|
+
for (const line of lines) logger.info(line);
|
|
79
|
+
}
|
|
80
|
+
async function promptFirstRunConsent() {
|
|
81
|
+
logger.info("DDT can report errors automatically (sanitized diagnostics + OS context,");
|
|
82
|
+
logger.info("never your SQL, identifiers, or credentials) so they get fixed fast.");
|
|
83
|
+
logger.info(errorReport.CONSENT_WARNING);
|
|
84
|
+
return promptYesNo("Enable automatic error reporting? [Y/n] ", true);
|
|
85
|
+
}
|
|
86
|
+
function promptYesNo(question, defaultYes) {
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
89
|
+
rl.question(question, (answer) => {
|
|
90
|
+
rl.close();
|
|
91
|
+
const normalized = answer.trim().toLowerCase();
|
|
92
|
+
if (normalized === "") resolve(defaultYes);
|
|
93
|
+
else resolve(normalized === "y" || normalized === "yes");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function isCi() {
|
|
98
|
+
return process.env["CI"] === "true";
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
activeConsentForTests,
|
|
102
|
+
finishErrorReporting,
|
|
103
|
+
printBetaNotice,
|
|
104
|
+
reportCliFailure,
|
|
105
|
+
setupErrorReporting,
|
|
106
|
+
shouldPromptFirstRun,
|
|
107
|
+
teardownErrorReporting
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=errorReporting-LX6WT4JH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/errorReporting.ts"],"sourcesContent":["/**\n * CLI error-reporting wiring (ERR.2).\n *\n * Lazily imported from `cli.ts` hooks so the cold-start path pays zero cost\n * until a command actually runs (preAction) or fails (catch handler).\n *\n * Responsibilities:\n * - first-run consent prompt (TTY only, default Yes, one keystroke)\n * - crash-hook installation when consent allows capture\n * - end-of-run drain: spool → `POST /errors` when consent is `on`\n * - manual \"Report this error? [y/N]\" prompt when consent is NOT `on`\n * and a command fails\n *\n * @see @ddt-tools/core/errorReport (capture substrate + consent + transport)\n */\nimport { createInterface } from 'node:readline';\n// Subpath import, NOT the `@ddt-tools/core` barrel — this module runs in the\n// preAction hook of every command; the barrel would drag ~85 core modules in.\nimport * as errorReport from '@ddt-tools/core/errorReport';\nimport { logger } from './logger.js';\n\n/** Commands that must never trigger the consent prompt or auto-drain. */\nconst EXEMPT_COMMANDS = new Set(['telemetry', 'feedback', 'help', 'completion']);\n\nlet activeConsent: errorReport.ErrorReportConsent = 'unset';\nlet uninstallHooks: (() => void) | null = null;\n\n/**\n * preAction hook body. Reads (and on first run, prompts for) consent, then\n * installs crash capture unless the user opted out.\n */\nexport async function setupErrorReporting(commandName: string): Promise<void> {\n if (EXEMPT_COMMANDS.has(commandName)) return;\n const stored = await errorReport.readConsent();\n activeConsent = stored.consent;\n\n // First-run consent prompt — explicit, one keystroke, default Yes.\n // Skipped when not interactive (CI, pipes) so scripted runs never block.\n if (\n shouldPromptFirstRun(stored.consent) &&\n process.stdout.isTTY &&\n process.stdin.isTTY &&\n !isCi()\n ) {\n // Beta install-time messaging — shown ONCE, immediately before the very\n // first consent question (true first run, consent still `unset`).\n printBetaNotice();\n activeConsent = (await promptFirstRunConsent()) ? 'on' : 'off';\n await errorReport.writeConsent(activeConsent === 'on' ? 'on' : 'off');\n }\n\n // Crash capture is installed unless the user said no. Capture is local-only;\n // nothing leaves the machine without `isErrorReportingEnabled` saying so.\n if (activeConsent !== 'off') {\n uninstallHooks = errorReport.installProcessHooks();\n }\n}\n\n/**\n * postAction hook body. Flushes buffered events and, when consent is `on`,\n * drains the spool to the Worker. No-ops fast when there is nothing to send.\n * `transport` is injectable for tests.\n */\nexport async function finishErrorReporting(\n productVersion: string,\n transport: errorReport.TransportOptions = {},\n): Promise<void> {\n await errorReport.flushErrorEvents();\n\n // Anonymous usage ping (opt-in) — gated internally by the SAME consent +\n // env opt-outs as error reporting and throttled to once per 24h. It is\n // awaited here (not detached) so the postAction lifecycle has a\n // deterministic completion point: `sendUsagePing` is self-bounded (3s hard\n // timeout) and never throws, so awaiting it can block command exit by at\n // most that timeout — the same bound the spool drain below already imposes.\n // Detaching it left the send racing process teardown, which both dropped\n // pings in practice and made the wiring untestable without a sleep.\n // `transport.fetchImpl` is threaded through so tests can intercept it.\n await errorReport\n .sendUsagePing({\n product: 'ddt',\n version: productVersion,\n surface: 'cli',\n ...(transport.fetchImpl ? { fetchImpl: transport.fetchImpl } : {}),\n })\n .catch(() => undefined);\n\n if (!errorReport.isErrorReportingEnabled(activeConsent)) return;\n const spooled = await errorReport.listSpooled(transport.dir);\n if (spooled.length === 0) return;\n const result = await errorReport.drainSpool({ productVersion }, transport);\n if (result.sent > 0) {\n logger.dim(` (${result.sent} error report${result.sent === 1 ? '' : 's'} sent — thank you)`);\n }\n}\n\n/**\n * Top-level command-failure handler. Reports the error as `handled` (real\n * crashes go through `uncaughtExceptionMonitor`), then either auto-sends\n * (consent `on`) or offers a one-keystroke manual report.\n * `transport` is injectable for tests.\n */\nexport async function reportCliFailure(\n err: unknown,\n productVersion: string,\n transport: errorReport.TransportOptions = {},\n): Promise<void> {\n errorReport.reportError(err, 'handled', 'cli:main');\n await errorReport.flushErrorEvents(transport.dir);\n\n if (errorReport.isErrorReportingEnabled(activeConsent)) {\n await errorReport.drainSpool({ productVersion }, transport);\n return;\n }\n\n // Manual push path — only when interactive.\n if (!process.stdout.isTTY || !process.stdin.isTTY || isCi()) return;\n const yes = await promptYesNo('Report this error to the DDT team? [y/N] ', false);\n if (!yes) return;\n const result = await errorReport.drainSpool({ productVersion }, transport);\n if (result.sent > 0) logger.dim(' Report sent — thank you.');\n else logger.dim(' Could not reach the error endpoint; the report is queued locally.');\n}\n\n/** Uninstall crash hooks (tests). */\nexport function teardownErrorReporting(): void {\n uninstallHooks?.();\n uninstallHooks = null;\n activeConsent = 'unset';\n}\n\n/** Current consent as seen by the hooks (tests). */\nexport function activeConsentForTests(): errorReport.ErrorReportConsent {\n return activeConsent;\n}\n\n/** DDT version surfaced in the first-run beta notice. */\nconst BETA_VERSION = '0.2.5';\n\n/**\n * Whether the first-run flow (beta notice + consent prompt) should fire.\n * True ONLY on true first run — when consent has never been decided\n * (`unset`). Exported so the \"first run only\" contract is unit-testable\n * without faking a TTY.\n */\nexport function shouldPromptFirstRun(consent: errorReport.ErrorReportConsent): boolean {\n return consent === 'unset';\n}\n\n/**\n * Beta install-time messaging — printed ONCE, on true first run (consent\n * `unset`), immediately before the consent question. Tells the user, at\n * install time: it's a 30-day public beta with all features free; what\n * happens after the beta (core stays free forever, Pro features keep\n * working but show license notices); that AI features are bring-your-own\n * key; and how to report a bug. Plain ASCII box to match CLI output style.\n */\nexport function printBetaNotice(): void {\n const lines = [\n '┌─────────────────────────────────────────────────────────┐',\n `│ DDT ${BETA_VERSION} — Public Beta │`,\n '│ • All features are free during the beta. │',\n '│ • After the beta: core features stay free forever; │',\n '│ Pro features keep working and show license notices. │',\n '│ • AI features use your own API key (never ours). │',\n '│ • Found a bug? Run: ddt feedback \"<what happened>\" │',\n '└─────────────────────────────────────────────────────────┘',\n ];\n for (const line of lines) logger.info(line);\n}\n\nasync function promptFirstRunConsent(): Promise<boolean> {\n logger.info('DDT can report errors automatically (sanitized diagnostics + OS context,');\n logger.info('never your SQL, identifiers, or credentials) so they get fixed fast.');\n logger.info(errorReport.CONSENT_WARNING);\n return promptYesNo('Enable automatic error reporting? [Y/n] ', true);\n}\n\nfunction promptYesNo(question: string, defaultYes: boolean): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(question, (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n if (normalized === '') resolve(defaultYes);\n else resolve(normalized === 'y' || normalized === 'yes');\n });\n });\n}\n\nfunction isCi(): boolean {\n return process.env['CI'] === 'true';\n}\n"],"mappings":"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","names":[]}
|
|
@@ -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":[]}
|