@sdt-tools/cli 0.2.0 → 0.2.6
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-6DRSZMBL.js +87 -0
- package/dist/advise-tests-6DRSZMBL.js.map +1 -0
- package/dist/ai-G4MJWHTM.js +89 -0
- package/dist/ai-G4MJWHTM.js.map +1 -0
- package/dist/anonymize-QR6JGXA7.js +123 -0
- package/dist/anonymize-QR6JGXA7.js.map +1 -0
- package/dist/approval-YVHYTV53.js +73 -0
- package/dist/approval-YVHYTV53.js.map +1 -0
- package/dist/approval-chain-54KKJZS3.js +120 -0
- package/dist/approval-chain-54KKJZS3.js.map +1 -0
- package/dist/audit-log-QZFH7LUX.js +159 -0
- package/dist/audit-log-QZFH7LUX.js.map +1 -0
- package/dist/backlog-V2YUIQDL.js +76 -0
- package/dist/backlog-V2YUIQDL.js.map +1 -0
- package/dist/bisect-GEVYAVL5.js +111 -0
- package/dist/bisect-GEVYAVL5.js.map +1 -0
- package/dist/bookmarks-57LKS7P6.js +107 -0
- package/dist/bookmarks-57LKS7P6.js.map +1 -0
- package/dist/branch-W2MGMPSH.js +88 -0
- package/dist/branch-W2MGMPSH.js.map +1 -0
- package/dist/build-VNIQFKSP.js +23 -0
- package/dist/build-VNIQFKSP.js.map +1 -0
- package/dist/catalog-JLB5VCEV.js +137 -0
- package/dist/catalog-JLB5VCEV.js.map +1 -0
- package/dist/changelog-M7XGDYSY.js +220 -0
- package/dist/changelog-M7XGDYSY.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-EWXM4KJN.js +25 -0
- package/dist/chunk-EWXM4KJN.js.map +1 -0
- package/dist/chunk-JP2EZLR5.js +50 -0
- package/dist/chunk-JP2EZLR5.js.map +1 -0
- package/dist/chunk-VM2H4LAO.js +15 -0
- package/dist/chunk-VM2H4LAO.js.map +1 -0
- package/dist/chunk-ZWY4ZRHL.js +44 -0
- package/dist/chunk-ZWY4ZRHL.js.map +1 -0
- package/dist/cli.js +511 -19014
- package/dist/cli.js.map +1 -1
- package/dist/compare-5O6UTWPJ.js +405 -0
- package/dist/compare-5O6UTWPJ.js.map +1 -0
- package/dist/compare-profiles-7ZSNIW7B.js +218 -0
- package/dist/compare-profiles-7ZSNIW7B.js.map +1 -0
- package/dist/completion-I5U5VVAX.js +82 -0
- package/dist/completion-I5U5VVAX.js.map +1 -0
- package/dist/connection-GNTZDHXF.js +133 -0
- package/dist/connection-GNTZDHXF.js.map +1 -0
- package/dist/cost-estimate-TJDDH6TO.js +328 -0
- package/dist/cost-estimate-TJDDH6TO.js.map +1 -0
- package/dist/data-compare-UK2UXAS3.js +134 -0
- package/dist/data-compare-UK2UXAS3.js.map +1 -0
- package/dist/data-fit-Q45ENBRL.js +125 -0
- package/dist/data-fit-Q45ENBRL.js.map +1 -0
- package/dist/deploy-status-UUHKVDTI.js +58 -0
- package/dist/deploy-status-UUHKVDTI.js.map +1 -0
- package/dist/design-PO6UPBL7.js +138 -0
- package/dist/design-PO6UPBL7.js.map +1 -0
- package/dist/diagnose-6IFMELFR.js +145 -0
- package/dist/diagnose-6IFMELFR.js.map +1 -0
- package/dist/discover-A7OSZAHK.js +78 -0
- package/dist/discover-A7OSZAHK.js.map +1 -0
- package/dist/docs-CVRKGUSW.js +177 -0
- package/dist/docs-CVRKGUSW.js.map +1 -0
- package/dist/drift-XDA3BDYN.js +226 -0
- package/dist/drift-XDA3BDYN.js.map +1 -0
- package/dist/drift-gate-V7QSIOGZ.js +94 -0
- package/dist/drift-gate-V7QSIOGZ.js.map +1 -0
- package/dist/error-lookup-7ZWCZJ44.js +56 -0
- package/dist/error-lookup-7ZWCZJ44.js.map +1 -0
- package/dist/errorReporting-AQXKKGZH.js +109 -0
- package/dist/errorReporting-AQXKKGZH.js.map +1 -0
- package/dist/exec-PKBHLI7T.js +121 -0
- package/dist/exec-PKBHLI7T.js.map +1 -0
- package/dist/explain-LWKJOTL7.js +192 -0
- package/dist/explain-LWKJOTL7.js.map +1 -0
- package/dist/explorer-QOVM6VBD.js +61 -0
- package/dist/explorer-QOVM6VBD.js.map +1 -0
- package/dist/export-IYYBZ5HE.js +42 -0
- package/dist/export-IYYBZ5HE.js.map +1 -0
- package/dist/extract-VMMVRQVT.js +102 -0
- package/dist/extract-VMMVRQVT.js.map +1 -0
- package/dist/features-LE6BDZ2S.js +59 -0
- package/dist/features-LE6BDZ2S.js.map +1 -0
- package/dist/feedback-M7DM2EQC.js +161 -0
- package/dist/feedback-M7DM2EQC.js.map +1 -0
- package/dist/find-EME2JG2I.js +176 -0
- package/dist/find-EME2JG2I.js.map +1 -0
- package/dist/format-TRLWLMGS.js +141 -0
- package/dist/format-TRLWLMGS.js.map +1 -0
- package/dist/generate-6NAZGZDV.js +152 -0
- package/dist/generate-6NAZGZDV.js.map +1 -0
- package/dist/graph-QNQDAUO7.js +161 -0
- package/dist/graph-QNQDAUO7.js.map +1 -0
- package/dist/history-RONA7ZTI.js +199 -0
- package/dist/history-RONA7ZTI.js.map +1 -0
- package/dist/hosts-YBXY2ZG5.js +49 -0
- package/dist/hosts-YBXY2ZG5.js.map +1 -0
- package/dist/impact-T2JSANHS.js +59 -0
- package/dist/impact-T2JSANHS.js.map +1 -0
- package/dist/import-AELYLY6A.js +32 -0
- package/dist/import-AELYLY6A.js.map +1 -0
- package/dist/import-script-2OF5BI6A.js +83 -0
- package/dist/import-script-2OF5BI6A.js.map +1 -0
- package/dist/index.cjs +71 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +95 -31
- package/dist/index.js.map +1 -1
- package/dist/init-SWRRJMGI.js +57 -0
- package/dist/init-SWRRJMGI.js.map +1 -0
- package/dist/install-hooks-6SIAGTAF.js +109 -0
- package/dist/install-hooks-6SIAGTAF.js.map +1 -0
- package/dist/license-OAF22PLZ.js +46 -0
- package/dist/license-OAF22PLZ.js.map +1 -0
- package/dist/lineage-EW66XJ6O.js +552 -0
- package/dist/lineage-EW66XJ6O.js.map +1 -0
- package/dist/lint-FQ2OTYTQ.js +143 -0
- package/dist/lint-FQ2OTYTQ.js.map +1 -0
- package/dist/mcp-SARDMCDV.js +344 -0
- package/dist/mcp-SARDMCDV.js.map +1 -0
- package/dist/migrate-from-dbt-JVTXPWKQ.js +156 -0
- package/dist/migrate-from-dbt-JVTXPWKQ.js.map +1 -0
- package/dist/migrate-platform-NTRTOGNR.js +91 -0
- package/dist/migrate-platform-NTRTOGNR.js.map +1 -0
- package/dist/optimize-CJYWMAWA.js +105 -0
- package/dist/optimize-CJYWMAWA.js.map +1 -0
- package/dist/perf-LL2CPCJF.js +205 -0
- package/dist/perf-LL2CPCJF.js.map +1 -0
- package/dist/pii-FBDRDQ2E.js +136 -0
- package/dist/pii-FBDRDQ2E.js.map +1 -0
- package/dist/pilot-CCQERKPH.js +29 -0
- package/dist/pilot-CCQERKPH.js.map +1 -0
- package/dist/pr-comment-S5FF4QRX.js +79 -0
- package/dist/pr-comment-S5FF4QRX.js.map +1 -0
- package/dist/preview-5U4YVCRM.js +47 -0
- package/dist/preview-5U4YVCRM.js.map +1 -0
- package/dist/profile-7VC57KD2.js +101 -0
- package/dist/profile-7VC57KD2.js.map +1 -0
- package/dist/promote-AASEFTIA.js +408 -0
- package/dist/promote-AASEFTIA.js.map +1 -0
- package/dist/publish-UMVIWH6H.js +721 -0
- package/dist/publish-UMVIWH6H.js.map +1 -0
- package/dist/purge-QMXZKCMD.js +57 -0
- package/dist/purge-QMXZKCMD.js.map +1 -0
- package/dist/query-log-6OM4GI7W.js +112 -0
- package/dist/query-log-6OM4GI7W.js.map +1 -0
- package/dist/refactor-LTZQLJ35.js +5799 -0
- package/dist/refactor-LTZQLJ35.js.map +1 -0
- package/dist/refresh-4TY2AGOU.js +38 -0
- package/dist/refresh-4TY2AGOU.js.map +1 -0
- package/dist/replay-OOC25FZN.js +117 -0
- package/dist/replay-OOC25FZN.js.map +1 -0
- package/dist/revert-ODMUVJW6.js +110 -0
- package/dist/revert-ODMUVJW6.js.map +1 -0
- package/dist/review-XXPWOBFP.js +158 -0
- package/dist/review-XXPWOBFP.js.map +1 -0
- package/dist/rollback-suggest-6G2HEKFR.js +79 -0
- package/dist/rollback-suggest-6G2HEKFR.js.map +1 -0
- package/dist/safer-alternative-QFVNLG3L.js +89 -0
- package/dist/safer-alternative-QFVNLG3L.js.map +1 -0
- package/dist/safety-7QWRSUEZ.js +168 -0
- package/dist/safety-7QWRSUEZ.js.map +1 -0
- package/dist/savings-RHIXP6IT.js +95 -0
- package/dist/savings-RHIXP6IT.js.map +1 -0
- package/dist/scan-secrets-5YCQ4UCU.js +54 -0
- package/dist/scan-secrets-5YCQ4UCU.js.map +1 -0
- package/dist/schema-CIZXCQD2.js +429 -0
- package/dist/schema-CIZXCQD2.js.map +1 -0
- package/dist/script-K7CIN2P6.js +153 -0
- package/dist/script-K7CIN2P6.js.map +1 -0
- package/dist/search-BUZ5NXZZ.js +151 -0
- package/dist/search-BUZ5NXZZ.js.map +1 -0
- package/dist/seed-76QAK276.js +96 -0
- package/dist/seed-76QAK276.js.map +1 -0
- package/dist/sketch-PTLKDIK3.js +88 -0
- package/dist/sketch-PTLKDIK3.js.map +1 -0
- package/dist/snapshot-XLPR2OZ5.js +177 -0
- package/dist/snapshot-XLPR2OZ5.js.map +1 -0
- package/dist/snippets-EK4DK5CN.js +74 -0
- package/dist/snippets-EK4DK5CN.js.map +1 -0
- package/dist/standards-7T2UY6DD.js +241 -0
- package/dist/standards-7T2UY6DD.js.map +1 -0
- package/dist/suggest-VGRYSAR6.js +39 -0
- package/dist/suggest-VGRYSAR6.js.map +1 -0
- package/dist/suggest-constraints-MY5WKUHA.js +160 -0
- package/dist/suggest-constraints-MY5WKUHA.js.map +1 -0
- package/dist/suite-TRNGZWQM.js +88 -0
- package/dist/suite-TRNGZWQM.js.map +1 -0
- package/dist/telemetry-3U2QLA2S.js +75 -0
- package/dist/telemetry-3U2QLA2S.js.map +1 -0
- package/dist/template-ZERIXVXF.js +403 -0
- package/dist/template-ZERIXVXF.js.map +1 -0
- package/dist/test-5M2ED3WT.js +169 -0
- package/dist/test-5M2ED3WT.js.map +1 -0
- package/dist/trial-U732FONV.js +31 -0
- package/dist/trial-U732FONV.js.map +1 -0
- package/dist/validate-T6D2WCOK.js +106 -0
- package/dist/validate-T6D2WCOK.js.map +1 -0
- package/dist/verify-KXVASEEG.js +76 -0
- package/dist/verify-KXVASEEG.js.map +1 -0
- package/dist/watch-I6K4BNMA.js +80 -0
- package/dist/watch-I6K4BNMA.js.map +1 -0
- package/dist/xcompare-TPFLQO6W.js +87 -0
- package/dist/xcompare-TPFLQO6W.js.map +1 -0
- package/package.json +2 -2
- package/dist/cli.cjs +0 -19040
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
import "./chunk-DGUM43GV.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/compare-profiles.ts
|
|
7
|
+
import { promises as fs } from "fs";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import { catalog, compareProfiles, pac, project } from "@sdt-tools/core";
|
|
10
|
+
function compareProfilesCommand() {
|
|
11
|
+
const cmd = new Command("compare-profiles");
|
|
12
|
+
cmd.description("Manage saved compare profiles (.sdt/compare-profiles.json).");
|
|
13
|
+
cmd.command("list").description("List every saved profile.").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--json", "Emit JSON instead of a human table.").action(async (opts) => {
|
|
14
|
+
const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
|
|
15
|
+
const all = await store.list();
|
|
16
|
+
if (opts.json) {
|
|
17
|
+
console.log(JSON.stringify(all, null, 2));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (all.length === 0) {
|
|
21
|
+
logger.dim("(no compare profiles saved yet)");
|
|
22
|
+
logger.dim(` store: ${store.path}`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
logger.success(`${all.length} compare profile(s):`);
|
|
26
|
+
for (const p of all) {
|
|
27
|
+
console.log(` ${p.id.padEnd(24)} ${p.name}`);
|
|
28
|
+
logger.dim(` source: ${p.source.kind}=${p.source.reference}`);
|
|
29
|
+
logger.dim(` target: ${p.target.kind}=${p.target.reference}`);
|
|
30
|
+
logger.dim(` mappings: ${p.mappings.length} updated: ${p.updatedAt}`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
cmd.command("show").description("Show one profile by id.").argument("<id>", "Profile id (from `compare-profiles list`).").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--json", "Emit JSON instead of a human table.").action(async (id, opts) => {
|
|
34
|
+
const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
|
|
35
|
+
const p = await store.get(String(id));
|
|
36
|
+
if (!p) {
|
|
37
|
+
logger.error(`No profile with id "${id}". Run \`sdt compare-profiles list\` to enumerate.`);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (opts.json) {
|
|
42
|
+
console.log(JSON.stringify(p, null, 2));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
logger.success(`Profile: ${p.name} (${p.id})`);
|
|
46
|
+
if (p.description) logger.dim(` ${p.description}`);
|
|
47
|
+
logger.dim(
|
|
48
|
+
` source: ${p.source.kind}=${p.source.reference}${p.source.database ? ` db=${p.source.database}` : ""}${p.source.schema ? ` schema=${p.source.schema}` : ""}`
|
|
49
|
+
);
|
|
50
|
+
logger.dim(
|
|
51
|
+
` target: ${p.target.kind}=${p.target.reference}${p.target.database ? ` db=${p.target.database}` : ""}${p.target.schema ? ` schema=${p.target.schema}` : ""}`
|
|
52
|
+
);
|
|
53
|
+
logger.dim(` case-sensitive: ${p.caseSensitive ? "yes" : "no"}`);
|
|
54
|
+
logger.dim(` rewrite-inside-strings: ${p.rewriteInsideStrings ? "yes" : "no"}`);
|
|
55
|
+
logger.dim(` updatedAt: ${p.updatedAt}`);
|
|
56
|
+
if (p.mappings.length === 0) {
|
|
57
|
+
logger.dim(" mappings: (none \u2014 identity)");
|
|
58
|
+
} else {
|
|
59
|
+
logger.dim(` mappings (${p.mappings.length}):`);
|
|
60
|
+
for (const m of p.mappings) logger.dim(` ${m.source} => ${m.target}`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
cmd.command("save").description("Upsert a profile from a JSON file or stdin.").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--json-file <path>", 'Path to a JSON file containing one profile. Use "-" for stdin.').action(async (opts) => {
|
|
64
|
+
if (!opts.jsonFile) {
|
|
65
|
+
throw new Error('--json-file is required (use a path or "-" for stdin).');
|
|
66
|
+
}
|
|
67
|
+
const raw = String(opts.jsonFile) === "-" ? await readStdin() : await fs.readFile(String(opts.jsonFile), "utf8");
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
if (!parsed?.id || !parsed?.name || !parsed?.source || !parsed?.target) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"Profile JSON must contain at minimum: id, name, source, target. mappings defaults to []."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
|
|
75
|
+
const stamped = await store.upsert({ ...parsed, mappings: parsed.mappings ?? [] });
|
|
76
|
+
logger.success(`Saved profile "${stamped.name}" (${stamped.id})`);
|
|
77
|
+
logger.dim(` store: ${store.path}`);
|
|
78
|
+
});
|
|
79
|
+
cmd.command("remove").description("Delete one profile by id.").argument("<id>", "Profile id (from `compare-profiles list`).").option("--root <path>", "Project root. Default cwd.", process.cwd()).action(async (id, opts) => {
|
|
80
|
+
const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
|
|
81
|
+
const removed = await store.remove(String(id));
|
|
82
|
+
if (removed) {
|
|
83
|
+
logger.success(`Removed profile "${id}".`);
|
|
84
|
+
} else {
|
|
85
|
+
logger.warn(`No profile with id "${id}" \u2014 nothing to remove.`);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
cmd.command("preview").description(
|
|
89
|
+
"Preview which FQNs match between the profile's source and target. Local-only \u2014 no warehouse round-trip."
|
|
90
|
+
).argument("<id>", "Profile id (from `compare-profiles list`).").option("--root <path>", "Project root. Default cwd.", process.cwd()).option(
|
|
91
|
+
"--examples <n>",
|
|
92
|
+
"Max example FQNs to show per bucket in human output. Default 5.",
|
|
93
|
+
(v) => parseInt(v, 10),
|
|
94
|
+
5
|
|
95
|
+
).option("--json", "Emit the full PreviewSummary as JSON.").action(async (id, opts) => {
|
|
96
|
+
const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
|
|
97
|
+
const profile = await store.get(String(id));
|
|
98
|
+
if (!profile) {
|
|
99
|
+
logger.error(`No profile with id "${id}". Run \`sdt compare-profiles list\` to enumerate.`);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const root = String(opts.root);
|
|
104
|
+
const source = await resolveEndpointFqns(profile.source, root);
|
|
105
|
+
const target = await resolveEndpointFqns(profile.target, root);
|
|
106
|
+
const summary = compareProfiles.previewMatch({
|
|
107
|
+
source: source.fqns,
|
|
108
|
+
target: target.fqns,
|
|
109
|
+
mappings: profile.mappings,
|
|
110
|
+
...profile.caseSensitive ? { caseSensitive: true } : {}
|
|
111
|
+
});
|
|
112
|
+
if (opts.json) {
|
|
113
|
+
console.log(
|
|
114
|
+
JSON.stringify(
|
|
115
|
+
{
|
|
116
|
+
profile: { id: profile.id, name: profile.name },
|
|
117
|
+
source: {
|
|
118
|
+
kind: profile.source.kind,
|
|
119
|
+
reference: profile.source.reference,
|
|
120
|
+
count: source.fqns.length
|
|
121
|
+
},
|
|
122
|
+
target: {
|
|
123
|
+
kind: profile.target.kind,
|
|
124
|
+
reference: profile.target.reference,
|
|
125
|
+
count: target.fqns.length
|
|
126
|
+
},
|
|
127
|
+
summary
|
|
128
|
+
},
|
|
129
|
+
null,
|
|
130
|
+
2
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
logger.success(`Preview: ${profile.name} (${profile.id})`);
|
|
136
|
+
logger.dim(
|
|
137
|
+
` source (${profile.source.kind}=${profile.source.reference}): ${source.fqns.length} object(s)${source.note ? ` \u2014 ${source.note}` : ""}`
|
|
138
|
+
);
|
|
139
|
+
logger.dim(
|
|
140
|
+
` target (${profile.target.kind}=${profile.target.reference}): ${target.fqns.length} object(s)${target.note ? ` \u2014 ${target.note}` : ""}`
|
|
141
|
+
);
|
|
142
|
+
console.log("");
|
|
143
|
+
console.log(` matched: ${summary.matchedCount}`);
|
|
144
|
+
console.log(` source-only: ${summary.sourceOnlyCount}`);
|
|
145
|
+
console.log(` target-only: ${summary.targetOnlyCount}`);
|
|
146
|
+
const exN = Math.max(0, Number(opts.examples));
|
|
147
|
+
printBucket("matched", summary.matched, exN);
|
|
148
|
+
printBucket("source-only", summary.sourceOnly, exN);
|
|
149
|
+
printBucket("target-only", summary.targetOnly, exN);
|
|
150
|
+
if (summary.matchedCount === 0 && (summary.sourceOnlyCount > 0 || summary.targetOnlyCount > 0)) {
|
|
151
|
+
logger.warn("No FQNs matched \u2014 check the profile's scope and mapping rules.");
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
return cmd;
|
|
155
|
+
}
|
|
156
|
+
async function resolveEndpointFqns(endpoint, root) {
|
|
157
|
+
if (endpoint.kind === "connection") {
|
|
158
|
+
const cache = new catalog.CatalogCache({ root, connection: endpoint.reference });
|
|
159
|
+
const snapshot = await cache.get();
|
|
160
|
+
if (snapshot.databases.length === 0) {
|
|
161
|
+
return {
|
|
162
|
+
fqns: [],
|
|
163
|
+
note: `empty catalog cache at ${cache.path} \u2014 run \`sdt catalog refresh --connection ${endpoint.reference}\` first`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return { fqns: fqnsFromSnapshot(snapshot, endpoint.database, endpoint.schema) };
|
|
167
|
+
}
|
|
168
|
+
if (endpoint.kind === "pac") {
|
|
169
|
+
const contents = await pac.readPac(endpoint.reference);
|
|
170
|
+
return { fqns: fqnsFromObjects(contents.model, endpoint.database, endpoint.schema) };
|
|
171
|
+
}
|
|
172
|
+
const loaded = await project.loadProject(endpoint.reference);
|
|
173
|
+
const model = await project.parseProjectModel(loaded);
|
|
174
|
+
return { fqns: fqnsFromObjects(model, endpoint.database, endpoint.schema) };
|
|
175
|
+
}
|
|
176
|
+
function fqnsFromSnapshot(snapshot, databaseScope, schemaScope) {
|
|
177
|
+
const sameId = (a, b) => !!a && !!b && a.toUpperCase() === b.toUpperCase();
|
|
178
|
+
const out = [];
|
|
179
|
+
for (const db of snapshot.databases) {
|
|
180
|
+
if (databaseScope && !sameId(db.database, databaseScope)) continue;
|
|
181
|
+
for (const sc of db.schemas) {
|
|
182
|
+
if (schemaScope && !sameId(sc.schema, schemaScope)) continue;
|
|
183
|
+
for (const obj of sc.objects) {
|
|
184
|
+
out.push({ database: obj.database, schema: obj.schema, name: obj.name });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
function fqnsFromObjects(model, databaseScope, schemaScope) {
|
|
191
|
+
const sameId = (a, b) => !!a && !!b && a.toUpperCase() === b.toUpperCase();
|
|
192
|
+
const out = [];
|
|
193
|
+
for (const obj of model) {
|
|
194
|
+
if (databaseScope && !sameId(obj.fqn.database, databaseScope)) continue;
|
|
195
|
+
if (schemaScope && !sameId(obj.fqn.schema, schemaScope)) continue;
|
|
196
|
+
out.push(obj.fqn);
|
|
197
|
+
}
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
function printBucket(label, items, exampleN) {
|
|
201
|
+
if (items.length === 0) return;
|
|
202
|
+
console.log("");
|
|
203
|
+
console.log(
|
|
204
|
+
` ${label} examples (showing ${Math.min(exampleN, items.length)} of ${items.length}):`
|
|
205
|
+
);
|
|
206
|
+
for (const fqn of items.slice(0, exampleN)) console.log(` ${fqn}`);
|
|
207
|
+
}
|
|
208
|
+
async function readStdin() {
|
|
209
|
+
const chunks = [];
|
|
210
|
+
for await (const chunk of process.stdin) {
|
|
211
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
212
|
+
}
|
|
213
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
214
|
+
}
|
|
215
|
+
export {
|
|
216
|
+
compareProfilesCommand
|
|
217
|
+
};
|
|
218
|
+
//# sourceMappingURL=compare-profiles-7ZSNIW7B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/compare-profiles.ts"],"sourcesContent":["/**\n * `sdt compare-profiles` — manage saved comparison profiles.\n *\n * A profile is \"Source X (connection|pac|project) → Target Y, with these\n * mapping rules\", persisted to `<cwd>/.sdt/compare-profiles.json`. The\n * eventual VS Code webview reads/writes the same store; this CLI gives\n * power-users the same affordances headlessly.\n *\n * Subcommands:\n * list — list all saved profiles (table or JSON)\n * show <id> — print one profile (table or JSON)\n * save — upsert a profile from a JSON file (--json-file <path>) or stdin\n * remove <id> — delete a profile\n * preview <id> — run `previewMatch` over the saved profile's\n * source + target endpoints and report counts +\n * examples of matched / source-only / target-only\n * FQNs. Catches typo'd schema names before a\n * full extract + diff round-trip.\n *\n * `preview` resolves each endpoint locally:\n * - kind=connection — reads the EE1 catalog cache at\n * `<root>/.sdt/cache/<reference>/catalog.msgpack` (legacy\n * `catalog.json` also accepted). Run\n * `sdt catalog refresh --connection <reference>` first.\n * - kind=pac — reads the `.sdtpac` archive at `reference`.\n * - kind=project — reads the `.sdtproj` at `reference`.\n */\nimport { promises as fs } from 'node:fs';\nimport { Command } from 'commander';\nimport { catalog, compareProfiles, pac, project } from '@sdt-tools/core';\nimport type { FullyQualifiedName, SnowflakeObject } from '@sdt-tools/core/model';\nimport { logger } from '../util/logger.js';\n\nexport function compareProfilesCommand(): Command {\n const cmd = new Command('compare-profiles');\n cmd.description('Manage saved compare profiles (.sdt/compare-profiles.json).');\n\n cmd\n .command('list')\n .description('List every saved profile.')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--json', 'Emit JSON instead of a human table.')\n .action(async (opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const all = await store.list();\n if (opts.json) {\n console.log(JSON.stringify(all, null, 2));\n return;\n }\n if (all.length === 0) {\n logger.dim('(no compare profiles saved yet)');\n logger.dim(` store: ${store.path}`);\n return;\n }\n logger.success(`${all.length} compare profile(s):`);\n for (const p of all) {\n console.log(` ${p.id.padEnd(24)} ${p.name}`);\n logger.dim(` source: ${p.source.kind}=${p.source.reference}`);\n logger.dim(` target: ${p.target.kind}=${p.target.reference}`);\n logger.dim(` mappings: ${p.mappings.length} updated: ${p.updatedAt}`);\n }\n });\n\n cmd\n .command('show')\n .description('Show one profile by id.')\n .argument('<id>', 'Profile id (from `compare-profiles list`).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--json', 'Emit JSON instead of a human table.')\n .action(async (id, opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const p = await store.get(String(id));\n if (!p) {\n logger.error(`No profile with id \"${id}\". Run \\`sdt compare-profiles list\\` to enumerate.`);\n process.exitCode = 1;\n return;\n }\n if (opts.json) {\n console.log(JSON.stringify(p, null, 2));\n return;\n }\n logger.success(`Profile: ${p.name} (${p.id})`);\n if (p.description) logger.dim(` ${p.description}`);\n logger.dim(\n ` source: ${p.source.kind}=${p.source.reference}${p.source.database ? ` db=${p.source.database}` : ''}${p.source.schema ? ` schema=${p.source.schema}` : ''}`,\n );\n logger.dim(\n ` target: ${p.target.kind}=${p.target.reference}${p.target.database ? ` db=${p.target.database}` : ''}${p.target.schema ? ` schema=${p.target.schema}` : ''}`,\n );\n logger.dim(` case-sensitive: ${p.caseSensitive ? 'yes' : 'no'}`);\n logger.dim(` rewrite-inside-strings: ${p.rewriteInsideStrings ? 'yes' : 'no'}`);\n logger.dim(` updatedAt: ${p.updatedAt}`);\n if (p.mappings.length === 0) {\n logger.dim(' mappings: (none — identity)');\n } else {\n logger.dim(` mappings (${p.mappings.length}):`);\n for (const m of p.mappings) logger.dim(` ${m.source} => ${m.target}`);\n }\n });\n\n cmd\n .command('save')\n .description('Upsert a profile from a JSON file or stdin.')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--json-file <path>', 'Path to a JSON file containing one profile. Use \"-\" for stdin.')\n .action(async (opts) => {\n if (!opts.jsonFile) {\n throw new Error('--json-file is required (use a path or \"-\" for stdin).');\n }\n const raw =\n String(opts.jsonFile) === '-'\n ? await readStdin()\n : await fs.readFile(String(opts.jsonFile), 'utf8');\n const parsed = JSON.parse(raw) as compareProfiles.CompareProfile;\n if (!parsed?.id || !parsed?.name || !parsed?.source || !parsed?.target) {\n throw new Error(\n 'Profile JSON must contain at minimum: id, name, source, target. mappings defaults to [].',\n );\n }\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const stamped = await store.upsert({ ...parsed, mappings: parsed.mappings ?? [] });\n logger.success(`Saved profile \"${stamped.name}\" (${stamped.id})`);\n logger.dim(` store: ${store.path}`);\n });\n\n cmd\n .command('remove')\n .description('Delete one profile by id.')\n .argument('<id>', 'Profile id (from `compare-profiles list`).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .action(async (id, opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const removed = await store.remove(String(id));\n if (removed) {\n logger.success(`Removed profile \"${id}\".`);\n } else {\n logger.warn(`No profile with id \"${id}\" — nothing to remove.`);\n }\n });\n\n cmd\n .command('preview')\n .description(\n \"Preview which FQNs match between the profile's source and target. Local-only — no warehouse round-trip.\",\n )\n .argument('<id>', 'Profile id (from `compare-profiles list`).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option(\n '--examples <n>',\n 'Max example FQNs to show per bucket in human output. Default 5.',\n (v) => parseInt(v, 10),\n 5,\n )\n .option('--json', 'Emit the full PreviewSummary as JSON.')\n .action(async (id, opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const profile = await store.get(String(id));\n if (!profile) {\n logger.error(`No profile with id \"${id}\". Run \\`sdt compare-profiles list\\` to enumerate.`);\n process.exitCode = 1;\n return;\n }\n\n const root = String(opts.root);\n const source = await resolveEndpointFqns(profile.source, root);\n const target = await resolveEndpointFqns(profile.target, root);\n\n const summary = compareProfiles.previewMatch({\n source: source.fqns,\n target: target.fqns,\n mappings: profile.mappings,\n ...(profile.caseSensitive ? { caseSensitive: true } : {}),\n });\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n {\n profile: { id: profile.id, name: profile.name },\n source: {\n kind: profile.source.kind,\n reference: profile.source.reference,\n count: source.fqns.length,\n },\n target: {\n kind: profile.target.kind,\n reference: profile.target.reference,\n count: target.fqns.length,\n },\n summary,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n logger.success(`Preview: ${profile.name} (${profile.id})`);\n logger.dim(\n ` source (${profile.source.kind}=${profile.source.reference}): ${source.fqns.length} object(s)${source.note ? ` — ${source.note}` : ''}`,\n );\n logger.dim(\n ` target (${profile.target.kind}=${profile.target.reference}): ${target.fqns.length} object(s)${target.note ? ` — ${target.note}` : ''}`,\n );\n console.log('');\n console.log(` matched: ${summary.matchedCount}`);\n console.log(` source-only: ${summary.sourceOnlyCount}`);\n console.log(` target-only: ${summary.targetOnlyCount}`);\n\n const exN = Math.max(0, Number(opts.examples));\n printBucket('matched', summary.matched, exN);\n printBucket('source-only', summary.sourceOnly, exN);\n printBucket('target-only', summary.targetOnly, exN);\n\n if (\n summary.matchedCount === 0 &&\n (summary.sourceOnlyCount > 0 || summary.targetOnlyCount > 0)\n ) {\n logger.warn(\"No FQNs matched — check the profile's scope and mapping rules.\");\n }\n });\n\n return cmd;\n}\n\ninterface EndpointResolution {\n fqns: FullyQualifiedName[];\n note?: string;\n}\n\nasync function resolveEndpointFqns(\n endpoint: compareProfiles.CompareProfile['source'],\n root: string,\n): Promise<EndpointResolution> {\n if (endpoint.kind === 'connection') {\n const cache = new catalog.CatalogCache({ root, connection: endpoint.reference });\n const snapshot = await cache.get();\n if (snapshot.databases.length === 0) {\n return {\n fqns: [],\n note: `empty catalog cache at ${cache.path} — run \\`sdt catalog refresh --connection ${endpoint.reference}\\` first`,\n };\n }\n return { fqns: fqnsFromSnapshot(snapshot, endpoint.database, endpoint.schema) };\n }\n if (endpoint.kind === 'pac') {\n const contents = await pac.readPac(endpoint.reference);\n return { fqns: fqnsFromObjects(contents.model, endpoint.database, endpoint.schema) };\n }\n // kind === 'project'\n const loaded = await project.loadProject(endpoint.reference);\n const model = await project.parseProjectModel(loaded);\n return { fqns: fqnsFromObjects(model, endpoint.database, endpoint.schema) };\n}\n\nfunction fqnsFromSnapshot(\n snapshot: catalog.CatalogSnapshot,\n databaseScope: string | undefined,\n schemaScope: string | undefined,\n): FullyQualifiedName[] {\n const sameId = (a: string | undefined, b: string | undefined): boolean =>\n !!a && !!b && a.toUpperCase() === b.toUpperCase();\n const out: FullyQualifiedName[] = [];\n for (const db of snapshot.databases) {\n if (databaseScope && !sameId(db.database, databaseScope)) continue;\n for (const sc of db.schemas) {\n if (schemaScope && !sameId(sc.schema, schemaScope)) continue;\n for (const obj of sc.objects) {\n out.push({ database: obj.database, schema: obj.schema, name: obj.name });\n }\n }\n }\n return out;\n}\n\nfunction fqnsFromObjects(\n model: readonly SnowflakeObject[],\n databaseScope: string | undefined,\n schemaScope: string | undefined,\n): FullyQualifiedName[] {\n const sameId = (a: string | undefined, b: string | undefined): boolean =>\n !!a && !!b && a.toUpperCase() === b.toUpperCase();\n const out: FullyQualifiedName[] = [];\n for (const obj of model) {\n if (databaseScope && !sameId(obj.fqn.database, databaseScope)) continue;\n if (schemaScope && !sameId(obj.fqn.schema, schemaScope)) continue;\n out.push(obj.fqn);\n }\n return out;\n}\n\nfunction printBucket(label: string, items: readonly string[], exampleN: number): void {\n if (items.length === 0) return;\n console.log('');\n console.log(\n ` ${label} examples (showing ${Math.min(exampleN, items.length)} of ${items.length}):`,\n );\n for (const fqn of items.slice(0, exampleN)) console.log(` ${fqn}`);\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : (chunk as Buffer));\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
import "./chunk-DGUM43GV.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/completion.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
var COMMANDS = [
|
|
9
|
+
"init",
|
|
10
|
+
"extract",
|
|
11
|
+
"build",
|
|
12
|
+
"publish",
|
|
13
|
+
"compare",
|
|
14
|
+
"drift",
|
|
15
|
+
"validate",
|
|
16
|
+
"connection",
|
|
17
|
+
"refactor",
|
|
18
|
+
"import",
|
|
19
|
+
"format",
|
|
20
|
+
"completion",
|
|
21
|
+
"telemetry",
|
|
22
|
+
"license"
|
|
23
|
+
];
|
|
24
|
+
function completionCommand() {
|
|
25
|
+
return new Command("completion").description("Print shell completion script (bash, zsh, fish, powershell).").argument("<shell>", "Shell: bash | zsh | fish | powershell").action((shell) => {
|
|
26
|
+
switch (shell) {
|
|
27
|
+
case "bash":
|
|
28
|
+
process.stdout.write(bashCompletion());
|
|
29
|
+
break;
|
|
30
|
+
case "zsh":
|
|
31
|
+
process.stdout.write(zshCompletion());
|
|
32
|
+
break;
|
|
33
|
+
case "fish":
|
|
34
|
+
process.stdout.write(fishCompletion());
|
|
35
|
+
break;
|
|
36
|
+
case "powershell":
|
|
37
|
+
case "pwsh":
|
|
38
|
+
process.stdout.write(powershellCompletion());
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
logger.error(`Unsupported shell: ${shell}. Use bash | zsh | fish | powershell.`);
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function bashCompletion() {
|
|
47
|
+
return `# sdt bash completion
|
|
48
|
+
_sdt_completion() {
|
|
49
|
+
local cur prev cmds
|
|
50
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
51
|
+
cmds="${COMMANDS.join(" ")}"
|
|
52
|
+
COMPREPLY=( $(compgen -W "$cmds" -- "$cur") )
|
|
53
|
+
}
|
|
54
|
+
complete -F _sdt_completion sdt
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
function zshCompletion() {
|
|
58
|
+
return `#compdef sdt
|
|
59
|
+
_sdt() {
|
|
60
|
+
local -a cmds
|
|
61
|
+
cmds=(${COMMANDS.map((c) => `'${c}:'`).join(" ")})
|
|
62
|
+
_describe 'sdt commands' cmds
|
|
63
|
+
}
|
|
64
|
+
_sdt "$@"
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
function fishCompletion() {
|
|
68
|
+
return COMMANDS.map((c) => `complete -c sdt -f -n '__fish_use_subcommand' -a '${c}'`).join("\n") + "\n";
|
|
69
|
+
}
|
|
70
|
+
function powershellCompletion() {
|
|
71
|
+
return `Register-ArgumentCompleter -Native -CommandName sdt -ScriptBlock {
|
|
72
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
73
|
+
@(${COMMANDS.map((c) => `'${c}'`).join(", ")}) | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
74
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
completionCommand
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=completion-I5U5VVAX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/completion.ts"],"sourcesContent":["/**\n * `sdt completion <shell>` — print shell completion script for bash | zsh | fish | powershell.\n *\n * v0.1 prints a minimal completion script that lists top-level commands. The\n * full surface (option flags, dynamic profile names from `~/.sdt/profiles.json`)\n * is wired in v0.2.\n */\nimport { Command } from 'commander';\n\nimport { logger } from '../util/logger.js';\n\nconst COMMANDS = [\n 'init',\n 'extract',\n 'build',\n 'publish',\n 'compare',\n 'drift',\n 'validate',\n 'connection',\n 'refactor',\n 'import',\n 'format',\n 'completion',\n 'telemetry',\n 'license',\n];\n\nexport function completionCommand(): Command {\n return new Command('completion')\n .description('Print shell completion script (bash, zsh, fish, powershell).')\n .argument('<shell>', 'Shell: bash | zsh | fish | powershell')\n .action((shell: string) => {\n switch (shell) {\n case 'bash':\n process.stdout.write(bashCompletion());\n break;\n case 'zsh':\n process.stdout.write(zshCompletion());\n break;\n case 'fish':\n process.stdout.write(fishCompletion());\n break;\n case 'powershell':\n case 'pwsh':\n process.stdout.write(powershellCompletion());\n break;\n default:\n logger.error(`Unsupported shell: ${shell}. Use bash | zsh | fish | powershell.`);\n process.exitCode = 1;\n }\n });\n}\n\nfunction bashCompletion(): string {\n return `# sdt bash completion\n_sdt_completion() {\n local cur prev cmds\n cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n cmds=\"${COMMANDS.join(' ')}\"\n COMPREPLY=( $(compgen -W \"$cmds\" -- \"$cur\") )\n}\ncomplete -F _sdt_completion sdt\n`;\n}\n\nfunction zshCompletion(): string {\n return `#compdef sdt\n_sdt() {\n local -a cmds\n cmds=(${COMMANDS.map((c) => `'${c}:'`).join(' ')})\n _describe 'sdt commands' cmds\n}\n_sdt \"$@\"\n`;\n}\n\nfunction fishCompletion(): string {\n return (\n COMMANDS.map((c) => `complete -c sdt -f -n '__fish_use_subcommand' -a '${c}'`).join('\\n') + '\\n'\n );\n}\n\nfunction powershellCompletion(): string {\n return `Register-ArgumentCompleter -Native -CommandName sdt -ScriptBlock {\n param($wordToComplete, $commandAst, $cursorPosition)\n @(${COMMANDS.map((c) => `'${c}'`).join(', ')}) | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }\n}\n`;\n}\n"],"mappings":";;;;;;AAOA,SAAS,eAAe;AAIxB,IAAM,WAAW;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,oBAA6B;AAC3C,SAAO,IAAI,QAAQ,YAAY,EAC5B,YAAY,8DAA8D,EAC1E,SAAS,WAAW,uCAAuC,EAC3D,OAAO,CAAC,UAAkB;AACzB,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,gBAAQ,OAAO,MAAM,eAAe,CAAC;AACrC;AAAA,MACF,KAAK;AACH,gBAAQ,OAAO,MAAM,cAAc,CAAC;AACpC;AAAA,MACF,KAAK;AACH,gBAAQ,OAAO,MAAM,eAAe,CAAC;AACrC;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,gBAAQ,OAAO,MAAM,qBAAqB,CAAC;AAC3C;AAAA,MACF;AACE,eAAO,MAAM,sBAAsB,KAAK,uCAAuC;AAC/E,gBAAQ,WAAW;AAAA,IACvB;AAAA,EACF,CAAC;AACL;AAEA,SAAS,iBAAyB;AAChC,SAAO;AAAA;AAAA;AAAA;AAAA,UAIC,SAAS,KAAK,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAK5B;AAEA,SAAS,gBAAwB;AAC/B,SAAO;AAAA;AAAA;AAAA,UAGC,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAKlD;AAEA,SAAS,iBAAyB;AAChC,SACE,SAAS,IAAI,CAAC,MAAM,qDAAqD,CAAC,GAAG,EAAE,KAAK,IAAI,IAAI;AAEhG;AAEA,SAAS,uBAA+B;AACtC,SAAO;AAAA;AAAA,MAEH,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAK9C;","names":[]}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
import "./chunk-DGUM43GV.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/connection.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import {
|
|
9
|
+
getProfile,
|
|
10
|
+
loadProfiles,
|
|
11
|
+
removeProfile,
|
|
12
|
+
SnowflakeConnection,
|
|
13
|
+
upsertProfile
|
|
14
|
+
} from "@sdt-tools/core/connection";
|
|
15
|
+
function connectionCommand() {
|
|
16
|
+
const cmd = new Command("connection");
|
|
17
|
+
cmd.description("Manage Snowflake connection profiles.");
|
|
18
|
+
cmd.command("list").description("List configured connection profiles.").action(async () => {
|
|
19
|
+
const profiles = await loadProfiles();
|
|
20
|
+
if (!profiles.length) {
|
|
21
|
+
logger.dim("(no profiles configured \u2014 run `sdt connection add` to add one)");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
for (const p of profiles) {
|
|
25
|
+
logger.info(
|
|
26
|
+
` ${p.name.padEnd(20)} ${p.account.padEnd(28)} ${p.auth.method} as ${p.auth.username}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
cmd.command("add").description("Add or replace a connection profile.").requiredOption("--name <name>", "Profile name").requiredOption("--account <account>", "Snowflake account locator (e.g. xy12345.us-east-1)").requiredOption("--user <user>", "Snowflake username").option(
|
|
31
|
+
"--auth <method>",
|
|
32
|
+
"Auth method: PASSWORD | KEY_PAIR | OAUTH | EXTERNAL_BROWSER | MFA",
|
|
33
|
+
"PASSWORD"
|
|
34
|
+
).option("--password <value>", "Password or env:VAR_NAME placeholder").option("--key <path>", "Path to RSA private key (for KEY_PAIR auth)").option("--key-passphrase <value>", "Passphrase or env:VAR_NAME placeholder").option("--role <role>", "Default role").option("--warehouse <wh>", "Default warehouse").option("--database <db>", "Default database").option("--schema <schema>", "Default schema").action(async (opts) => {
|
|
35
|
+
const auth = String(opts.auth).toUpperCase();
|
|
36
|
+
let authBlock;
|
|
37
|
+
switch (auth) {
|
|
38
|
+
case "PASSWORD":
|
|
39
|
+
if (!opts.password) throw new Error("--password is required for PASSWORD auth");
|
|
40
|
+
authBlock = {
|
|
41
|
+
method: "PASSWORD",
|
|
42
|
+
username: String(opts.user),
|
|
43
|
+
password: String(opts.password)
|
|
44
|
+
};
|
|
45
|
+
break;
|
|
46
|
+
case "KEY_PAIR":
|
|
47
|
+
if (!opts.key) throw new Error("--key is required for KEY_PAIR auth");
|
|
48
|
+
authBlock = {
|
|
49
|
+
method: "KEY_PAIR",
|
|
50
|
+
username: String(opts.user),
|
|
51
|
+
privateKeyPath: String(opts.key),
|
|
52
|
+
privateKeyPassphrase: opts.keyPassphrase ? String(opts.keyPassphrase) : void 0
|
|
53
|
+
};
|
|
54
|
+
break;
|
|
55
|
+
case "OAUTH":
|
|
56
|
+
if (!opts.password)
|
|
57
|
+
throw new Error("--password (token, or env:VAR) is required for OAUTH auth");
|
|
58
|
+
authBlock = {
|
|
59
|
+
method: "OAUTH",
|
|
60
|
+
username: String(opts.user),
|
|
61
|
+
token: String(opts.password)
|
|
62
|
+
};
|
|
63
|
+
break;
|
|
64
|
+
case "EXTERNAL_BROWSER":
|
|
65
|
+
authBlock = { method: "EXTERNAL_BROWSER", username: String(opts.user) };
|
|
66
|
+
break;
|
|
67
|
+
case "MFA":
|
|
68
|
+
if (!opts.password) throw new Error("--password is required for MFA auth");
|
|
69
|
+
authBlock = {
|
|
70
|
+
method: "MFA",
|
|
71
|
+
username: String(opts.user),
|
|
72
|
+
password: String(opts.password)
|
|
73
|
+
};
|
|
74
|
+
break;
|
|
75
|
+
default:
|
|
76
|
+
throw new Error(`Unsupported auth method: ${auth}`);
|
|
77
|
+
}
|
|
78
|
+
const profile = {
|
|
79
|
+
name: String(opts.name),
|
|
80
|
+
account: String(opts.account),
|
|
81
|
+
auth: authBlock,
|
|
82
|
+
role: opts.role ? String(opts.role) : void 0,
|
|
83
|
+
warehouse: opts.warehouse ? String(opts.warehouse) : void 0,
|
|
84
|
+
database: opts.database ? String(opts.database) : void 0,
|
|
85
|
+
schema: opts.schema ? String(opts.schema) : void 0
|
|
86
|
+
};
|
|
87
|
+
await upsertProfile(profile);
|
|
88
|
+
logger.success(`Saved profile "${profile.name}".`);
|
|
89
|
+
});
|
|
90
|
+
cmd.command("get <name>").description("Print a profile (secrets are redacted).").action(async (name) => {
|
|
91
|
+
const profile = await getProfile(String(name));
|
|
92
|
+
const redacted = { ...profile, auth: redactAuth(profile.auth) };
|
|
93
|
+
logger.info(JSON.stringify(redacted, null, 2));
|
|
94
|
+
});
|
|
95
|
+
cmd.command("remove").description("Remove a connection profile.").argument("<name>").action(async (name) => {
|
|
96
|
+
const ok = await removeProfile(String(name));
|
|
97
|
+
if (ok) logger.success(`Removed profile "${name}".`);
|
|
98
|
+
else logger.warn(`No profile named "${name}".`);
|
|
99
|
+
});
|
|
100
|
+
cmd.command("test").description("Test connectivity for a profile.").argument("<name>").action(async (name) => {
|
|
101
|
+
const profile = await getProfile(String(name));
|
|
102
|
+
const conn = new SnowflakeConnection(profile);
|
|
103
|
+
try {
|
|
104
|
+
const result = await conn.test();
|
|
105
|
+
logger.success(`OK \u2014 Snowflake ${result.version}`);
|
|
106
|
+
} finally {
|
|
107
|
+
await conn.disconnect();
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return cmd;
|
|
111
|
+
}
|
|
112
|
+
function redactAuth(auth) {
|
|
113
|
+
const isPlaceholder = (v) => v.startsWith("env:") || v.startsWith("keyring:");
|
|
114
|
+
switch (auth.method) {
|
|
115
|
+
case "PASSWORD":
|
|
116
|
+
case "MFA":
|
|
117
|
+
return auth.password && !isPlaceholder(auth.password) ? { ...auth, password: "<redacted>" } : auth;
|
|
118
|
+
case "OAUTH":
|
|
119
|
+
return auth.token && !isPlaceholder(auth.token) ? { ...auth, token: "<redacted>" } : auth;
|
|
120
|
+
case "KEY_PAIR":
|
|
121
|
+
return auth.privateKeyPassphrase && !isPlaceholder(auth.privateKeyPassphrase) ? { ...auth, privateKeyPassphrase: "<redacted>" } : auth;
|
|
122
|
+
case "EXTERNAL_BROWSER":
|
|
123
|
+
return auth;
|
|
124
|
+
default: {
|
|
125
|
+
const _exhaustive = auth;
|
|
126
|
+
return _exhaustive;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
connectionCommand
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=connection-GNTZDHXF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/connection.ts"],"sourcesContent":["import { Command } from 'commander';\nimport {\n getProfile,\n loadProfiles,\n removeProfile,\n SnowflakeConnection,\n upsertProfile,\n type ConnectionProfile,\n type SnowflakeAuth,\n} from '@sdt-tools/core/connection';\nimport { logger } from '../util/logger.js';\n\nexport function connectionCommand(): Command {\n const cmd = new Command('connection');\n cmd.description('Manage Snowflake connection profiles.');\n\n cmd\n .command('list')\n .description('List configured connection profiles.')\n .action(async () => {\n const profiles = await loadProfiles();\n if (!profiles.length) {\n logger.dim('(no profiles configured — run `sdt connection add` to add one)');\n return;\n }\n for (const p of profiles) {\n logger.info(\n ` ${p.name.padEnd(20)} ${p.account.padEnd(28)} ${p.auth.method} as ${p.auth.username}`,\n );\n }\n });\n\n cmd\n .command('add')\n .description('Add or replace a connection profile.')\n .requiredOption('--name <name>', 'Profile name')\n .requiredOption('--account <account>', 'Snowflake account locator (e.g. xy12345.us-east-1)')\n .requiredOption('--user <user>', 'Snowflake username')\n .option(\n '--auth <method>',\n 'Auth method: PASSWORD | KEY_PAIR | OAUTH | EXTERNAL_BROWSER | MFA',\n 'PASSWORD',\n )\n .option('--password <value>', 'Password or env:VAR_NAME placeholder')\n .option('--key <path>', 'Path to RSA private key (for KEY_PAIR auth)')\n .option('--key-passphrase <value>', 'Passphrase or env:VAR_NAME placeholder')\n .option('--role <role>', 'Default role')\n .option('--warehouse <wh>', 'Default warehouse')\n .option('--database <db>', 'Default database')\n .option('--schema <schema>', 'Default schema')\n .action(async (opts) => {\n const auth = String(opts.auth).toUpperCase() as ConnectionProfile['auth']['method'];\n let authBlock: ConnectionProfile['auth'];\n switch (auth) {\n case 'PASSWORD':\n if (!opts.password) throw new Error('--password is required for PASSWORD auth');\n authBlock = {\n method: 'PASSWORD',\n username: String(opts.user),\n password: String(opts.password),\n };\n break;\n case 'KEY_PAIR':\n if (!opts.key) throw new Error('--key is required for KEY_PAIR auth');\n authBlock = {\n method: 'KEY_PAIR',\n username: String(opts.user),\n privateKeyPath: String(opts.key),\n privateKeyPassphrase: opts.keyPassphrase ? String(opts.keyPassphrase) : undefined,\n };\n break;\n case 'OAUTH':\n if (!opts.password)\n throw new Error('--password (token, or env:VAR) is required for OAUTH auth');\n authBlock = {\n method: 'OAUTH',\n username: String(opts.user),\n token: String(opts.password),\n };\n break;\n case 'EXTERNAL_BROWSER':\n authBlock = { method: 'EXTERNAL_BROWSER', username: String(opts.user) };\n break;\n case 'MFA':\n if (!opts.password) throw new Error('--password is required for MFA auth');\n authBlock = {\n method: 'MFA',\n username: String(opts.user),\n password: String(opts.password),\n };\n break;\n default:\n throw new Error(`Unsupported auth method: ${auth}`);\n }\n const profile: ConnectionProfile = {\n name: String(opts.name),\n account: String(opts.account),\n auth: authBlock,\n role: opts.role ? String(opts.role) : undefined,\n warehouse: opts.warehouse ? String(opts.warehouse) : undefined,\n database: opts.database ? String(opts.database) : undefined,\n schema: opts.schema ? String(opts.schema) : undefined,\n };\n await upsertProfile(profile);\n logger.success(`Saved profile \"${profile.name}\".`);\n });\n\n cmd\n .command('get <name>')\n .description('Print a profile (secrets are redacted).')\n .action(async (name) => {\n const profile = await getProfile(String(name));\n const redacted: ConnectionProfile = { ...profile, auth: redactAuth(profile.auth) };\n logger.info(JSON.stringify(redacted, null, 2));\n });\n\n cmd\n .command('remove')\n .description('Remove a connection profile.')\n .argument('<name>')\n .action(async (name) => {\n const ok = await removeProfile(String(name));\n if (ok) logger.success(`Removed profile \"${name}\".`);\n else logger.warn(`No profile named \"${name}\".`);\n });\n\n cmd\n .command('test')\n .description('Test connectivity for a profile.')\n .argument('<name>')\n .action(async (name) => {\n const profile = await getProfile(String(name));\n const conn = new SnowflakeConnection(profile);\n try {\n const result = await conn.test();\n logger.success(`OK — Snowflake ${result.version}`);\n } finally {\n await conn.disconnect();\n }\n });\n\n return cmd;\n}\n\n/**\n * Redact inline secrets for `connection get`. `env:`/`keyring:` placeholders are\n * not secrets (they only name where the value resolves from at runtime), so they\n * are preserved; literal secret values are replaced with `<redacted>`.\n */\nfunction redactAuth(auth: SnowflakeAuth): SnowflakeAuth {\n const isPlaceholder = (v: string): boolean => v.startsWith('env:') || v.startsWith('keyring:');\n switch (auth.method) {\n case 'PASSWORD':\n case 'MFA':\n return auth.password && !isPlaceholder(auth.password)\n ? { ...auth, password: '<redacted>' }\n : auth;\n case 'OAUTH':\n return auth.token && !isPlaceholder(auth.token) ? { ...auth, token: '<redacted>' } : auth;\n case 'KEY_PAIR':\n return auth.privateKeyPassphrase && !isPlaceholder(auth.privateKeyPassphrase)\n ? { ...auth, privateKeyPassphrase: '<redacted>' }\n : auth;\n case 'EXTERNAL_BROWSER':\n return auth;\n default: {\n const _exhaustive: never = auth;\n return _exhaustive;\n }\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,eAAe;AACxB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGA,SAAS,oBAA6B;AAC3C,QAAM,MAAM,IAAI,QAAQ,YAAY;AACpC,MAAI,YAAY,uCAAuC;AAEvD,MACG,QAAQ,MAAM,EACd,YAAY,sCAAsC,EAClD,OAAO,YAAY;AAClB,UAAM,WAAW,MAAM,aAAa;AACpC,QAAI,CAAC,SAAS,QAAQ;AACpB,aAAO,IAAI,qEAAgE;AAC3E;AAAA,IACF;AACA,eAAW,KAAK,UAAU;AACxB,aAAO;AAAA,QACL,KAAK,EAAE,KAAK,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,OAAO,EAAE,KAAK,QAAQ;AAAA,MACvF;AAAA,IACF;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,KAAK,EACb,YAAY,sCAAsC,EAClD,eAAe,iBAAiB,cAAc,EAC9C,eAAe,uBAAuB,oDAAoD,EAC1F,eAAe,iBAAiB,oBAAoB,EACpD;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC,OAAO,sBAAsB,sCAAsC,EACnE,OAAO,gBAAgB,6CAA6C,EACpE,OAAO,4BAA4B,wCAAwC,EAC3E,OAAO,iBAAiB,cAAc,EACtC,OAAO,oBAAoB,mBAAmB,EAC9C,OAAO,mBAAmB,kBAAkB,EAC5C,OAAO,qBAAqB,gBAAgB,EAC5C,OAAO,OAAO,SAAS;AACtB,UAAM,OAAO,OAAO,KAAK,IAAI,EAAE,YAAY;AAC3C,QAAI;AACJ,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,YAAI,CAAC,KAAK,SAAU,OAAM,IAAI,MAAM,0CAA0C;AAC9E,oBAAY;AAAA,UACV,QAAQ;AAAA,UACR,UAAU,OAAO,KAAK,IAAI;AAAA,UAC1B,UAAU,OAAO,KAAK,QAAQ;AAAA,QAChC;AACA;AAAA,MACF,KAAK;AACH,YAAI,CAAC,KAAK,IAAK,OAAM,IAAI,MAAM,qCAAqC;AACpE,oBAAY;AAAA,UACV,QAAQ;AAAA,UACR,UAAU,OAAO,KAAK,IAAI;AAAA,UAC1B,gBAAgB,OAAO,KAAK,GAAG;AAAA,UAC/B,sBAAsB,KAAK,gBAAgB,OAAO,KAAK,aAAa,IAAI;AAAA,QAC1E;AACA;AAAA,MACF,KAAK;AACH,YAAI,CAAC,KAAK;AACR,gBAAM,IAAI,MAAM,2DAA2D;AAC7E,oBAAY;AAAA,UACV,QAAQ;AAAA,UACR,UAAU,OAAO,KAAK,IAAI;AAAA,UAC1B,OAAO,OAAO,KAAK,QAAQ;AAAA,QAC7B;AACA;AAAA,MACF,KAAK;AACH,oBAAY,EAAE,QAAQ,oBAAoB,UAAU,OAAO,KAAK,IAAI,EAAE;AACtE;AAAA,MACF,KAAK;AACH,YAAI,CAAC,KAAK,SAAU,OAAM,IAAI,MAAM,qCAAqC;AACzE,oBAAY;AAAA,UACV,QAAQ;AAAA,UACR,UAAU,OAAO,KAAK,IAAI;AAAA,UAC1B,UAAU,OAAO,KAAK,QAAQ;AAAA,QAChC;AACA;AAAA,MACF;AACE,cAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;AAAA,IACtD;AACA,UAAM,UAA6B;AAAA,MACjC,MAAM,OAAO,KAAK,IAAI;AAAA,MACtB,SAAS,OAAO,KAAK,OAAO;AAAA,MAC5B,MAAM;AAAA,MACN,MAAM,KAAK,OAAO,OAAO,KAAK,IAAI,IAAI;AAAA,MACtC,WAAW,KAAK,YAAY,OAAO,KAAK,SAAS,IAAI;AAAA,MACrD,UAAU,KAAK,WAAW,OAAO,KAAK,QAAQ,IAAI;AAAA,MAClD,QAAQ,KAAK,SAAS,OAAO,KAAK,MAAM,IAAI;AAAA,IAC9C;AACA,UAAM,cAAc,OAAO;AAC3B,WAAO,QAAQ,kBAAkB,QAAQ,IAAI,IAAI;AAAA,EACnD,CAAC;AAEH,MACG,QAAQ,YAAY,EACpB,YAAY,yCAAyC,EACrD,OAAO,OAAO,SAAS;AACtB,UAAM,UAAU,MAAM,WAAW,OAAO,IAAI,CAAC;AAC7C,UAAM,WAA8B,EAAE,GAAG,SAAS,MAAM,WAAW,QAAQ,IAAI,EAAE;AACjF,WAAO,KAAK,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,EAC/C,CAAC;AAEH,MACG,QAAQ,QAAQ,EAChB,YAAY,8BAA8B,EAC1C,SAAS,QAAQ,EACjB,OAAO,OAAO,SAAS;AACtB,UAAM,KAAK,MAAM,cAAc,OAAO,IAAI,CAAC;AAC3C,QAAI,GAAI,QAAO,QAAQ,oBAAoB,IAAI,IAAI;AAAA,QAC9C,QAAO,KAAK,qBAAqB,IAAI,IAAI;AAAA,EAChD,CAAC;AAEH,MACG,QAAQ,MAAM,EACd,YAAY,kCAAkC,EAC9C,SAAS,QAAQ,EACjB,OAAO,OAAO,SAAS;AACtB,UAAM,UAAU,MAAM,WAAW,OAAO,IAAI,CAAC;AAC7C,UAAM,OAAO,IAAI,oBAAoB,OAAO;AAC5C,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,aAAO,QAAQ,uBAAkB,OAAO,OAAO,EAAE;AAAA,IACnD,UAAE;AACA,YAAM,KAAK,WAAW;AAAA,IACxB;AAAA,EACF,CAAC;AAEH,SAAO;AACT;AAOA,SAAS,WAAW,MAAoC;AACtD,QAAM,gBAAgB,CAAC,MAAuB,EAAE,WAAW,MAAM,KAAK,EAAE,WAAW,UAAU;AAC7F,UAAQ,KAAK,QAAQ;AAAA,IACnB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,KAAK,YAAY,CAAC,cAAc,KAAK,QAAQ,IAChD,EAAE,GAAG,MAAM,UAAU,aAAa,IAClC;AAAA,IACN,KAAK;AACH,aAAO,KAAK,SAAS,CAAC,cAAc,KAAK,KAAK,IAAI,EAAE,GAAG,MAAM,OAAO,aAAa,IAAI;AAAA,IACvF,KAAK;AACH,aAAO,KAAK,wBAAwB,CAAC,cAAc,KAAK,oBAAoB,IACxE,EAAE,GAAG,MAAM,sBAAsB,aAAa,IAC9C;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,SAAS;AACP,YAAM,cAAqB;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|