@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 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport { Command } from 'commander';\nimport { readPac } from '@sdt-tools/core/pac';\nimport {\n CompareEngine,\n LiveSource,\n buildExecutionPlan,\n defaultSafePlan,\n validatePlan,\n type CompareSource,\n} from '@sdt-tools/core/compare';\nimport {\n ScriptGenerator,\n SnowflakeExecutor,\n buildDeployManifest,\n executePlan,\n resolveDeployScripts,\n formatDeployScript,\n colorizeMigrationScript,\n splitStatements as coreSplitStatements,\n type DeployScript,\n type DeploymentReport,\n type StepResult,\n} from '@sdt-tools/core/deploy';\nimport { compileSlice, mergeDeployOptions, type SdtDeployOptions } from '@sdt-tools/core/project';\nimport {\n ai,\n aiPreflight,\n approval,\n pacFreshness,\n protectedObjects,\n safety,\n license,\n testFramework,\n} from '@sdt-tools/core';\nimport { executeDcmProjectDeploy, mapDcmResultToDeploymentReport } from '@sdt-tools/core/exporters';\nimport { assessDcmCompatibility, formatDcmCompatibilityReport } from '@sdt-tools/core/safety';\nimport { getProfile, SnowflakeConnection } from '@sdt-tools/core/connection';\nimport { logger } from '../util/logger.js';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\nimport { addMappingFlags, buildMappingFromOptions } from '../util/mapping.js';\n\nclass ModelSource implements CompareSource {\n readonly kind = 'pac' as const;\n readonly platform = 'Snowflake' as const;\n readonly label: string;\n private readonly model: Awaited<ReturnType<typeof readPac>>['model'];\n constructor(label: string, model: Awaited<ReturnType<typeof readPac>>['model']) {\n this.label = label;\n this.model = model;\n }\n async load() {\n return this.model;\n }\n}\n\n/**\n * Split a multi-statement SQL block into individual statements. Thin\n * wrapper around `@sdt-tools/core/deploy.splitStatements` so callsites here\n * keep their `string[]` shape.\n */\nfunction splitStatements(sql: string): string[] {\n return coreSplitStatements(sql).map((s) => s.sql);\n}\n\nexport function publishCommand(): Command {\n const cmd = new Command('publish');\n cmd\n .description(\n 'Compare a .sdtpac (the desired state) to a live Snowflake target and apply (or dry-run) the migration. Shared form with `ddt publish`: `--source <desired> --connection <live-target>`.',\n )\n .option(\n '--source <path>',\n 'Path to the desired-state .sdtpac (canonical name; the same flag `ddt publish` uses). Required for a normal publish; omit only with --restore-from-snapshot.',\n )\n .option(\n '--pac <path>',\n 'Back-compat alias for --source (the desired-state .sdtpac). Prefer --source for cross-platform parity with `ddt publish`.',\n )\n .requiredOption('-c, --connection <profile>', 'Connection profile name (the live target).')\n .option(\n '--restore-from-snapshot <batchId>',\n 'Recovery mode: skip the compare step and emit ALTER TABLE … SWAP WITH against the snapshot batch <batchId> from the registry. ' +\n 'Dry-run by default; pass --apply --yes to execute. ' +\n \"This is the command printed by TRUST.4's post-deploy-smoke + TRUST.8's restore-hint when a deploy fails — they tell the operator to run `sdt publish --restore-from-snapshot <id>`.\",\n )\n .option(\n '--snapshot-dir <path>',\n 'Snapshot registry directory used with --restore-from-snapshot.',\n '.sdt/snapshots',\n )\n .option('--db <database>', 'Target database (defaults to pac scope.database)')\n .option('--schema <schema>', 'Target schema (defaults to pac scope.schema)')\n .option('--dry-run', 'Print the migration script without executing it', false)\n .option('--apply', 'Execute the migration against the live target (gated by safety)', false)\n .option('--out <path>', 'Write the generated migration script to this path')\n .option(\n '--color <mode>',\n 'Color stdout: auto | always | never. Honors NO_COLOR / SDT_NO_COLOR env vars in auto mode.',\n 'auto',\n )\n .option('--no-pre-scripts', 'Skip pre-deploy scripts even if the pac configures them')\n .option('--no-post-scripts', 'Skip post-deploy scripts even if the pac configures them')\n .option(\n '--no-slice',\n \"Disable the pac's Project Slice (if it has one). Default: the slice baked into the pac is applied automatically and target objects outside the slice are left untouched.\",\n )\n .option('--yes', 'Confirm REQUIRE_YES_FLAG safety gate (required for --apply)', false)\n .option(\n '--confirm-production',\n 'Confirm REQUIRE_CONFIRM_PRODUCTION gate (required for prod-named profiles with destructive changes)',\n false,\n )\n .option(\n '--query-tag <tag>',\n \"Set Snowflake QUERY_TAG for this deploy (audit / observability). The CLI runs ALTER SESSION SET QUERY_TAG='<tag>' before any migration statement so QUERY_HISTORY can attribute the deploy. Defaults to 'sdt:<projectName>@<projectVersion>:<unix-ms>'.\",\n )\n .option(\n '--manifest <path>',\n 'After --apply, write a JSON deploy manifest (every step + status + duration + reverse SQL) to <path>. Replayable via `sdt revert --manifest <path>`.',\n )\n .option(\n '--ai-preflight',\n \"Before --apply, ask AI to grade the operator's plain-language deploy description against the actual diff + safety findings. Blocks on 'mismatch' unless --confirm-after-preflight-mismatch is also passed.\",\n false,\n )\n .option(\n '--ai-preflight-text <narrative>',\n 'Non-interactive form of --ai-preflight. Supplies the narrative directly instead of reading from stdin.',\n )\n .option(\n '--strict-ai-preflight',\n \"Also block on 'partial' (not just 'mismatch'). Useful for production deploys.\",\n false,\n )\n .option(\n '--confirm-after-preflight-mismatch',\n 'Proceed even when --ai-preflight returns a mismatch verdict. Acknowledges that the AI grader flagged a discrepancy.',\n false,\n )\n .option(\n '--require-approvals <n>',\n 'Multi-approver gate: refuse --apply until <n> distinct approvers have signed off via `sdt approval add <deploy-id> --as <user>`. Team-tier.',\n )\n .option(\n '--approver <ids>',\n 'Comma-separated allow-list of approver IDs honoured by --require-approvals. Empty = any approver counts.',\n )\n .option(\n '--deploy-id <id>',\n 'Stable deploy identifier paired with --require-approvals (default: <connection>:<db>.<schema>).',\n )\n .option(\n '--approvals-root <path>',\n 'Approvals directory (default: .sdt/approvals).',\n path.join('.sdt', 'approvals'),\n )\n .option(\n '--allow-protected <fqns>',\n 'Comma-separated FQNs to allow despite .sdt-protection.json protection. Repeatable.',\n (val: string, prev: string[]) => [...prev, val],\n [] as string[],\n )\n .option(\n '--allow-all-protected',\n 'Override all protection config entries (caution: disables DSR.4 guardrails).',\n false,\n )\n .option(\n '--freshness <mode>',\n 'Pac age check: warn (log if stale, continue), strict (block if stale), skip (no check). Default warn.',\n 'warn',\n )\n .option(\n '--post-deploy-tests <project>',\n 'After a successful --apply, discover YAML tests under <project>/tests/ and run them against the same connection. Exits 2 on any blocking (error-severity) DQ failure. Emits POST_DEPLOY_DQ_FAILED hint with --restore-from-snapshot recipe when tests fail.',\n )\n .option(\n '--via-dcm <stage>',\n 'Enterprise: delegate execution to Snowflake DCM Projects. <stage> is the DCM project FQN (e.g. @MY_DB.PUBLIC.MY_STAGE/my_project). SDT runs its safety pre-flight and DCM compatibility check, then executes EXECUTE DCM PROJECT DEPLOY. DCM must have been populated via `sdt export --dcm` first.',\n )\n .option(\n '--profile <name>',\n 'VARSYNTAX.2 — look up `deploymentProfiles[<name>].variables` from the pac manifest and substitute `$(VAR)` references in the generated migration script. Errors if the named profile is absent from the pac (e.g. pac built before VARSYNTAX.2, or profile not declared in .sdtproj). Distinct from `--connection`, which selects the Snowflake credential profile.',\n )\n .option(\n '--variables <kv>',\n 'VARSYNTAX.2 — comma-separated KEY=VALUE pairs for `$(VAR)` substitution. Merged on top of `--profile <name>` variables (CLI values win on key collision).',\n )\n .action(async (opts) => {\n if (opts.apply && opts.dryRun) {\n logger.error('--apply and --dry-run are mutually exclusive.');\n process.exitCode = 1;\n return;\n }\n\n // Recovery mode — branch out of the normal compare/apply flow and\n // emit ALTER TABLE … SWAP WITH from a recorded snapshot batch.\n if (opts.restoreFromSnapshot) {\n await runRestoreFromSnapshot(opts);\n return;\n }\n\n const pacRef = opts.source ?? opts.pac;\n if (!pacRef) {\n logger.error(\n '--source <path> (or its alias --pac) is required (unless --restore-from-snapshot is given).',\n );\n process.exitCode = 1;\n return;\n }\n const pacPath = path.resolve(String(pacRef));\n const pac = await readPac(pacPath);\n\n // Freshness gate — runs before any network call so stale artifacts\n // are caught fast, before an expensive live-extract round trip.\n const freshnessMode = (opts.freshness as pacFreshness.FreshnessMode | undefined) ?? 'warn';\n if (freshnessMode !== 'skip') {\n const fr = pacFreshness.checkPacFreshness(pac.manifest.builtAt);\n if (fr.status !== 'fresh') {\n const msg = pacFreshness.formatFreshnessWarning(fr, pacPath);\n if (freshnessMode === 'strict') {\n logger.error(msg);\n process.exitCode = 1;\n return;\n } else {\n logger.warn(msg);\n }\n }\n }\n\n const profileName = String(opts.connection);\n const profile = await getProfile(profileName);\n const conn = new SnowflakeConnection(profile);\n const db = opts.db ? String(opts.db) : pac.manifest.scope.database;\n const schema = opts.schema ? String(opts.schema) : pac.manifest.scope.schema;\n const live = new LiveSource(conn, { database: db, schema });\n const source = new ModelSource(pacPath, pac.model);\n\n const baseline: SdtDeployOptions | undefined = pac.manifest.deployOptions;\n const merged = mergeDeployOptions(baseline);\n // Apply the production safety floor: if the profile name looks like\n // production, force preDeployClone=true regardless of what the user\n // authored. Documented in docs/UX_PLAYBOOK.md §\"Per-profile safety\n // floor\". Compose by mutating the `deployment` block in place.\n const isProd = safety.looksLikeProduction(profileName);\n const resolved = {\n ...merged,\n deployment: safety.applyProductionSafetyFloor(merged.deployment, isProd),\n };\n if (isProd && merged.deployment.preDeployClone !== true) {\n logger.info(\n ' Profile \"' +\n profileName +\n '\" looks like production — forcing deployOptions.deployment.preDeployClone=true (safety floor).',\n );\n }\n const preScripts: DeployScript[] =\n opts.preScripts === false\n ? []\n : resolveDeployScripts(pac.source, resolved.deployment.preDeployScriptPatterns);\n const postScripts: DeployScript[] =\n opts.postScripts === false\n ? []\n : resolveDeployScripts(pac.source, resolved.deployment.postDeployScriptPatterns);\n\n // Logical-name mapping (`--map`, `--map-file`) — rewrites pac-side FQNs\n // so a pac built against DB_A can publish into DB_B. See\n // docs/LOGICAL_NAME_MAPPING.md. Mirrors `ddt publish`.\n const nameMapping = await buildMappingFromOptions(opts);\n const engine = new CompareEngine();\n logger.step('Comparing pac to live...');\n\n // Apply the pac's Slice (if any) so target objects outside the slice\n // are partitioned into `outsideScope` rather than treated as removals.\n // --no-slice on the publish command bypasses this.\n const sliceConfig = opts.slice === false ? undefined : pac.manifest.slice;\n const slice = sliceConfig ? compileSlice(sliceConfig) : undefined;\n\n const result = await engine.compare(source, live, {\n ...(nameMapping ? { nameMapping } : {}),\n ...(slice ? { sliceFilter: slice } : {}),\n });\n if (slice) {\n const outside = result.outsideScope?.length ?? 0;\n const refs = result.referenced?.length ?? 0;\n logger.info(\n ' Slice active: ' +\n result.objects.length +\n ' owned · ' +\n outside +\n ' outside scope (untouched) · ' +\n refs +\n ' referenced',\n );\n }\n logger.info(\n ' +' +\n result.summary.added +\n ' -' +\n result.summary.removed +\n ' ~' +\n result.summary.modified +\n ' =' +\n result.summary.unchanged,\n );\n\n // Protection check — refuse to touch protected objects unless overridden.\n const protConfig = await protectedObjects.loadProtectionConfig(path.dirname(pacPath));\n if (protConfig) {\n const ops = result.objects\n .filter((o) => o.kind !== 'unchanged' && o.kind !== 'added')\n .map((o) => ({\n fqn: o.identity.fqn,\n objectType: String(o.identity.objectType),\n kind: o.kind,\n }));\n const allowedFqns = ((opts.allowProtected as string[]) ?? []).flatMap((s) =>\n s.split(',').map((f) => f.trim()),\n );\n const violations = protectedObjects.checkProtectedObjects(ops, protConfig, {\n allowedFqns,\n allowAll: Boolean(opts.allowAllProtected),\n });\n if (violations.length > 0) {\n logger.error(protectedObjects.formatProtectionViolations(violations));\n process.exitCode = 1;\n await conn.disconnect();\n return;\n }\n }\n\n // VARSYNTAX.2 — resolve `$(VAR)` variables from the pac's\n // deployment-profile snapshot (when `--profile <name>` is given)\n // merged with `--variables KEY=VALUE` (CLI wins on key collision).\n // Absent / empty leaves `$(VAR)` references literal so the prior\n // behavior is preserved when no flag is passed.\n const deploymentProfileName = opts.profile ? String(opts.profile) : undefined;\n let publishVariables: Record<string, string> | undefined;\n if (deploymentProfileName) {\n const profileBlock = pac.manifest.deploymentProfiles?.[deploymentProfileName];\n if (!profileBlock) {\n const available = Object.keys(pac.manifest.deploymentProfiles ?? {});\n logger.error(\n `--profile ${deploymentProfileName}: no deploymentProfile by that name in the pac manifest. ` +\n (available.length === 0\n ? 'The pac carries no deploymentProfiles (was it built before VARSYNTAX.2, or does the .sdtproj declare any?).'\n : `Available: ${available.join(', ')}.`),\n );\n process.exitCode = 1;\n await conn.disconnect();\n return;\n }\n if (profileBlock.variables) publishVariables = { ...profileBlock.variables };\n }\n const cliVariables = parsePublishVariables(opts.variables);\n if (cliVariables) {\n publishVariables = { ...(publishVariables ?? {}), ...cliVariables };\n }\n\n const gen = new ScriptGenerator();\n // VARSYNTAX.3 — supply the deploy context so `$(SDT_PROFILE)`,\n // `$(SDT_PROJECT_NAME)`, `$(SDT_PROJECT_VERSION)`, `$(SDT_PLATFORM)`,\n // `$(SDT_TIMESTAMP)`, `$(SDT_USER)` substitute without the operator\n // having to pass them via `--variables`.\n const generateContext: {\n profile?: string;\n projectName?: string;\n projectVersion?: string;\n user?: string;\n } = {\n projectName: pac.manifest.projectName,\n projectVersion: pac.manifest.projectVersion,\n ...(deploymentProfileName ? { profile: deploymentProfileName } : {}),\n ...(profile.auth?.username ? { user: profile.auth.username } : {}),\n };\n const body = gen.generate(result, {\n banner: 'SDT publish: ' + pac.manifest.projectName + ' v' + pac.manifest.projectVersion,\n deployment: resolved.deployment,\n ...(publishVariables ? { variables: publishVariables } : {}),\n context: generateContext,\n });\n\n const sections: string[] = [];\n if (preScripts.length > 0) {\n sections.push('-- ' + '='.repeat(68));\n sections.push('-- PRE-DEPLOY (' + preScripts.length + ' script(s))');\n sections.push('-- ' + '='.repeat(68));\n for (const s of preScripts) sections.push(formatDeployScript(s, 'pre'));\n }\n sections.push(body.sql);\n if (postScripts.length > 0) {\n sections.push('');\n sections.push('-- ' + '='.repeat(68));\n sections.push('-- POST-DEPLOY (' + postScripts.length + ' script(s))');\n sections.push('-- ' + '='.repeat(68));\n for (const s of postScripts) sections.push(formatDeployScript(s, 'post'));\n }\n const fullScript = sections.join('\\n');\n\n logger.info(\n ' pre-deploy: ' +\n preScripts.length +\n ' script(s); post-deploy: ' +\n postScripts.length +\n ' script(s)',\n );\n\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, fullScript);\n logger.success('Wrote migration script to ' + out);\n }\n\n if (opts.dryRun || !opts.apply) {\n const colorMode = (opts.color as 'auto' | 'always' | 'never' | undefined) ?? 'auto';\n process.stdout.write(colorizeMigrationScript(fullScript, { mode: colorMode }) + '\\n');\n await conn.disconnect();\n return;\n }\n\n // --apply path: run safety classifier and check gates.\n const assessment = safety.assess(result, { deployment: resolved.deployment });\n logger.info(\n ' safety: ' +\n assessment.unrecoverable.length +\n ' unrecoverable, ' +\n assessment.destructive.length +\n ' destructive, ' +\n assessment.expensive.length +\n ' expensive, ' +\n assessment.warnings.length +\n ' warnings',\n );\n if (assessment.blocked) {\n logger.error('Deploy refused: ' + assessment.blockReason);\n await conn.disconnect();\n process.exitCode = 1;\n return;\n }\n\n const satisfied = safety.satisfiedGates({\n yes: Boolean(opts.yes),\n confirmProduction:\n Boolean(opts.confirmProduction) || !safety.looksLikeProduction(profileName),\n allowDropTable: resolved.deployment.allowDropTable,\n allowDropColumn: resolved.deployment.allowDropColumn,\n allowNarrowing: resolved.deployment.allowNarrowingTypes,\n allowTableRebuild: resolved.deployment.allowTableRebuild,\n allowUnrecoverableDrop: resolved.deployment.allowUnrecoverableDrop,\n });\n const outstanding = safety.outstandingGates(assessment, satisfied);\n if (outstanding.length > 0) {\n logger.error(\n 'Outstanding safety gate(s): ' +\n outstanding.join(', ') +\n '. Pass --yes / --confirm-production or set the matching deployOptions.deployment.* flag in the profile.',\n );\n await conn.disconnect();\n process.exitCode = 1;\n return;\n }\n\n // Approval gate (Team-tier): refuse to apply until N distinct\n // approvers have recorded approval tokens for this deploy id.\n if (opts.requireApprovals !== undefined && opts.requireApprovals !== false) {\n const required = Number(opts.requireApprovals) || 0;\n const allowed = opts.approver\n ? String(opts.approver)\n .split(',')\n .map((s: string) => s.trim())\n .filter(Boolean)\n : [];\n const deployId = String(opts.deployId ?? `${profileName}:${db}.${schema}`);\n const approvalsRoot = String(opts.approvalsRoot);\n const file = await approval.readApprovalFile(approvalsRoot, deployId);\n const outcome = approval.evaluateApprovalGate(\n { deployId, required, allowedApprovers: allowed },\n file.approvals,\n );\n if (!outcome.satisfied) {\n logger.error(\n 'Approval gate blocked deploy: ' +\n (outcome.blockReason ?? 'gate not satisfied') +\n `. Record approvals with \\`sdt approval add ${deployId} --as <user>\\`.`,\n );\n await conn.disconnect();\n process.exitCode = 1;\n return;\n }\n logger.info(\n ` approvals: ${outcome.satisfiedBy.length}/${required} ` +\n `(${outcome.satisfiedBy.join(', ')})`,\n );\n }\n\n // AI preflight grader: ask the operator to type what they expect\n // this deploy to do, then let an AI compare that narrative to the\n // actual SQL + safety findings. Blocks deploy on `mismatch`.\n if (opts.aiPreflight) {\n const narrative = await readPreflightNarrative(opts.aiPreflightText);\n if (!narrative) {\n logger.error(\n '--ai-preflight needs a narrative. Provide it inline via --ai-preflight-text or via stdin.',\n );\n await conn.disconnect();\n process.exitCode = 1;\n return;\n }\n logger.step('Grading deploy narrative with AI...');\n try {\n const findings = [\n ...assessment.unrecoverable.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ...assessment.destructive.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ...assessment.expensive.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ];\n const compareSummary = `added ${result.summary.added}, modified ${result.summary.modified}, removed ${result.summary.removed}, unchanged ${result.summary.unchanged}`;\n const verdict = await aiPreflight.gradePreflight(\n {\n narrative,\n compareSummary,\n safetyFindings: findings,\n diffSampleDdl: fullScript,\n target: `${db}.${schema}@${profileName}`,\n },\n {\n completeFn: async (prompt) => {\n const r = await ai.complete([{ role: 'user', content: prompt }], {\n feature: 'ai-preflight',\n });\n return r.text;\n },\n },\n );\n logger.info(' preflight verdict: ' + verdict.verdict);\n if (verdict.reasoning) logger.info(' preflight reasoning: ' + verdict.reasoning);\n for (const d of verdict.discrepancies) logger.info(' • ' + d);\n\n const isBlocking =\n verdict.verdict === 'mismatch' ||\n (opts.strictAiPreflight && verdict.verdict === 'partial');\n if (isBlocking && !opts.confirmAfterPreflightMismatch) {\n logger.error(\n `Deploy refused: AI preflight returned \"${verdict.verdict}\". Re-run with --confirm-after-preflight-mismatch to override, or revise the narrative.`,\n );\n await conn.disconnect();\n process.exitCode = 1;\n return;\n }\n if (verdict.parseFailed) {\n logger.warn(\n ' preflight verdict could not be parsed — continuing without a clean alignment signal.',\n );\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `--ai-preflight failed: ${msg}. Configure an AI provider via \\`sdt ai status\\` or omit --ai-preflight.`,\n );\n await conn.disconnect();\n process.exitCode = 1;\n return;\n }\n }\n\n // ── DCM path — replaces the standard executor when --via-dcm is set ──\n if (opts.viaDcm) {\n const dcmStage = String(opts.viaDcm);\n\n // Enterprise license check (warn mode for v0.1 — feature is allowed with warning if no license).\n const lic = await license.loadLicense();\n const licCheck = license.checkProFeature(lic, 'dcm-deploy');\n if (licCheck.warning) logger.warn('[Enterprise] ' + licCheck.warning);\n\n // DCM compatibility pre-flight — always runs, even for --dry-run.\n const dcmCompatReport = assessDcmCompatibility(result);\n if (dcmCompatReport.blocked) {\n logger.error('--via-dcm blocked:\\n' + formatDcmCompatibilityReport(dcmCompatReport));\n await conn.disconnect();\n process.exitCode = 1;\n return;\n }\n if (dcmCompatReport.findings.length > 0) {\n logger.warn(\n 'DCM compatibility warnings:\\n' + formatDcmCompatibilityReport(dcmCompatReport),\n );\n }\n\n if (opts.dryRun || !opts.apply) {\n logger.info(\n 'DCM compatibility check passed (' +\n dcmCompatReport.findings.length +\n ' warning(s)). Use --apply to execute via DCM.',\n );\n await conn.disconnect();\n return;\n }\n\n // Execute via DCM.\n logger.step('Executing via DCM: EXECUTE DCM PROJECT ' + dcmStage + ' DEPLOY');\n const dcmResult = await executeDcmProjectDeploy(conn, dcmStage);\n const bodyReport = mapDcmResultToDeploymentReport(dcmResult);\n\n if (dcmResult.overallStatus === 'SUCCESS') {\n logger.success(\n 'DCM deploy complete. ' + dcmResult.objects.length + ' object(s) processed.',\n );\n } else {\n logger.error(\n 'DCM deploy finished with failures: ' +\n dcmResult.failures.length +\n ' failed, ' +\n dcmResult.objects.length +\n ' total.',\n );\n process.exitCode = 1;\n }\n\n if (opts.manifest) {\n const m = buildDeployManifest(bodyReport, profile.account);\n const mPath = path.resolve(String(opts.manifest));\n await fs.mkdir(path.dirname(mPath), { recursive: true });\n await fs.writeFile(mPath, JSON.stringify(m, null, 2) + '\\n', 'utf8');\n logger.success('Wrote deploy manifest → ' + mPath);\n }\n\n await conn.disconnect();\n return;\n }\n // ── End DCM path ────────────────────────────────────────────────────────\n\n // Set the Snowflake QUERY_TAG before executing any migration\n // statement. Threading the tag into QUERY_HISTORY is what makes a\n // deploy auditable after the fact (\"which statements ran as part of\n // sdt-vX.Y.Z published at 13:42?\"). Default tag encodes the project\n // identity + a millisecond timestamp so two back-to-back applies are\n // distinguishable.\n const queryTag =\n typeof opts.queryTag === 'string' && opts.queryTag.length > 0\n ? String(opts.queryTag)\n : `sdt:${pac.manifest.projectName}@${pac.manifest.projectVersion}:${Date.now()}`;\n try {\n await conn.query(`ALTER SESSION SET QUERY_TAG = '${queryTag.replace(/'/g, \"''\")}'`);\n logger.info(' query_tag: ' + queryTag);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n 'Failed to set QUERY_TAG: ' +\n msg +\n '. Continuing without tag — audit attribution will be missing.',\n );\n }\n\n // Execute. Statements run sequentially. Snowflake DDL auto-commits per\n // statement, so partial deploys do not roll back automatically; if\n // preDeployClone was enabled, use UNDROP DATABASE or clone-swap to\n // recover. The main body runs through executePlan + SnowflakeExecutor\n // so every step's reverseSql is captured for `sdt revert`. Pre/post\n // scripts are file-based (no emitter, no reverseSql) so they keep the\n // simpler sequential-query path.\n logger.step('Applying migration...');\n let executed = 0;\n let failed = 0;\n const runOne = async (sql: string, label: string): Promise<boolean> => {\n const stmts = splitStatements(sql);\n for (const stmt of stmts) {\n try {\n await conn.query(stmt);\n executed += 1;\n } catch (err) {\n failed += 1;\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(' [' + label + '] failed: ' + msg + '\\n ' + stmt);\n return false;\n }\n }\n return true;\n };\n\n let ok = true;\n for (const s of preScripts) {\n logger.info(' pre: ' + s.path);\n ok = await runOne(s.sql, 'pre/' + s.path);\n if (!ok) break;\n }\n\n let bodyReport: DeploymentReport | undefined;\n if (ok) {\n const plan = defaultSafePlan(result);\n const validated = validatePlan(result, plan);\n const steps = buildExecutionPlan(result, validated, {\n deployment: resolved.deployment,\n });\n if (steps.length > 0) {\n const executor = new SnowflakeExecutor(conn, {\n onStatement: (sql) => {\n executed += 1;\n const head = sql.split('\\n')[0]?.slice(0, 100) ?? '';\n logger.info(' → ' + head + (sql.length > 100 ? ' …' : ''));\n },\n });\n bodyReport = await executePlan(steps, executor, {\n rollbackOnFailure: false,\n onStepEnd: (r: StepResult) => {\n if (r.status === 'FAILED') {\n failed += 1;\n logger.error(\n ' [' +\n r.step.fqn +\n '] failed: ' +\n (r.error ?? 'unknown') +\n '\\n ' +\n r.step.forwardSql,\n );\n }\n },\n });\n if (bodyReport.finalState !== 'CLEAN_SUCCESS') ok = false;\n }\n }\n\n if (ok) {\n for (const s of postScripts) {\n logger.info(' post: ' + s.path);\n ok = await runOne(s.sql, 'post/' + s.path);\n if (!ok) break;\n }\n }\n\n // Write deploy manifest if requested. The manifest captures every\n // body step (forwardSql + reverseSql + status + duration) and is\n // replayable via `sdt revert --manifest <path>`. Pre/post scripts are\n // not in the manifest (they have no inverse).\n if (opts.manifest && bodyReport) {\n const m = buildDeployManifest(bodyReport, profile.account);\n const mPath = path.resolve(String(opts.manifest));\n await fs.mkdir(path.dirname(mPath), { recursive: true });\n await fs.writeFile(mPath, JSON.stringify(m, null, 2) + '\\n', 'utf8');\n logger.success('Wrote deploy manifest → ' + mPath);\n }\n\n if (ok) {\n logger.success('Deploy complete. ' + executed + ' statement(s) executed.');\n\n // Post-deploy DQ tests (DQ.6).\n if (opts.postDeployTests) {\n const projectRoot = path.resolve(String(opts.postDeployTests));\n logger.step('Running post-deploy DQ tests from ' + projectRoot + '...');\n const testFiles = await testFramework.discoverTests(projectRoot);\n if (testFiles.length === 0) {\n logger.warn(\n ' POST_DEPLOY_DQ: no tests found under ' + projectRoot + '/tests/ — skipping.',\n );\n } else {\n const report = await testFramework.runTestFiles(testFiles, conn);\n process.stdout.write(testFramework.renderTextReport(report));\n if (report.summary.blocking) {\n logger.error(\n 'POST_DEPLOY_DQ_FAILED: ' +\n report.summary.fail +\n ' fail / ' +\n report.summary.error +\n ' error.' +\n (opts.manifest\n ? ' Recover: `sdt revert --manifest ' + String(opts.manifest) + '`'\n : ' Consider re-deploying prior version or using `sdt revert --manifest` if --manifest was used.'),\n );\n process.exitCode = 2;\n } else {\n logger.success(\n ' POST_DEPLOY_DQ: ' + report.summary.pass + '/' + report.summary.total + ' pass.',\n );\n }\n }\n }\n } else {\n logger.error(\n 'Deploy aborted after ' +\n executed +\n ' statement(s); ' +\n failed +\n ' failure(s). Recover via UNDROP / clone-swap if preDeployClone was set.',\n );\n process.exitCode = 1;\n }\n await conn.disconnect();\n });\n addMappingFlags(cmd);\n attachRelatedOptions(cmd, [\n 'deployment.allowDropTable',\n 'deployment.allowDropColumn',\n 'deployment.allowUnrecoverableDrop',\n 'deployment.allowNarrowingTypes',\n 'deployment.preDeployClone',\n 'deployment.blockOnPossibleDataLoss',\n 'deployment.preserveTargetOnlyObjects',\n ]);\n return cmd;\n}\n\n/**\n * `sdt publish --restore-from-snapshot <batchId>` recovery path.\n *\n * Loads the batch from the registry, emits the restore SQL (one\n * `ALTER TABLE … SWAP WITH` per source object, or a MANUAL hint row for\n * materialised objects), and either prints it (dry-run / no --apply) or\n * executes it against the live target (--apply --yes, --confirm-production\n * for prod-named profiles).\n *\n * Skips: pac freshness, compare, safety classifier, AI preflight, approval\n * gate, DCM path, manifest writing. Recovery is an emergency lever — the\n * gates that protect the forward path don't apply when we're restoring a\n * known-good snapshot the safety classifier itself authored.\n */\nasync function runRestoreFromSnapshot(opts: Record<string, unknown>): Promise<void> {\n const batchId = String(opts.restoreFromSnapshot);\n const registryDir = path.resolve(String(opts.snapshotDir ?? '.sdt/snapshots'));\n const profileName = String(opts.connection);\n\n let batch: safety.SnapshotBatch;\n try {\n batch = await safety.readSnapshotBatch(registryDir, batchId);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(`Snapshot batch \"${batchId}\" not found in ${registryDir}: ${msg}`);\n process.exitCode = 1;\n return;\n }\n\n const sqlLines = safety.emitRestoreFromSnapshotSql(batch);\n const sql = sqlLines.join('\\n');\n\n logger.info(`Restoring from batch ${batch.batchId} (createdAt=${batch.createdAt})`);\n logger.info(\n ` ${batch.entries.length} object(s) recorded; ${sqlLines.filter((l) => !l.startsWith('--')).length} statement(s) will run.`,\n );\n\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, sql + '\\n', 'utf8');\n logger.success(`Wrote restore SQL → ${out}`);\n }\n\n if (opts.dryRun || !opts.apply) {\n const colorMode = (opts.color as 'auto' | 'always' | 'never' | undefined) ?? 'auto';\n process.stdout.write(colorizeMigrationScript(sql, { mode: colorMode }) + '\\n');\n return;\n }\n\n if (!opts.yes) {\n logger.error('--apply requires --yes (restore replaces production tables via SWAP WITH).');\n process.exitCode = 1;\n return;\n }\n if (safety.looksLikeProduction(profileName) && !opts.confirmProduction) {\n logger.error(\n `Profile \"${profileName}\" looks like production — also pass --confirm-production to restore.`,\n );\n process.exitCode = 1;\n return;\n }\n\n const profile = await getProfile(profileName);\n const conn = new SnowflakeConnection(profile);\n let executed = 0;\n let failed = 0;\n try {\n for (const stmt of coreSplitStatements(sql).map((s) => s.sql)) {\n try {\n await conn.query(stmt);\n executed += 1;\n const head = stmt.split('\\n')[0]?.slice(0, 100) ?? '';\n logger.info(' → ' + head + (stmt.length > 100 ? ' …' : ''));\n } catch (err) {\n failed += 1;\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(` [restore ${batch.batchId}] ${msg}\\n ${stmt}`);\n }\n }\n } finally {\n await conn.disconnect();\n }\n\n if (failed === 0) {\n logger.success(`Restore complete. ${executed} statement(s) executed.`);\n } else {\n logger.error(\n `Restore finished with failures: ${executed} ok, ${failed} failed. ` +\n `Inspect the snapshot tables under ${registryDir} and recover by hand.`,\n );\n process.exitCode = 1;\n }\n}\n\n/**\n * Read the AI-preflight narrative from --ai-preflight-text (preferred for CI)\n * or from stdin (interactive). Returns trimmed text, or undefined when no\n * narrative was supplied.\n */\nasync function readPreflightNarrative(inlineText: unknown): Promise<string | undefined> {\n if (typeof inlineText === 'string' && inlineText.trim().length > 0) {\n return inlineText.trim();\n }\n if (process.stdin.isTTY) {\n process.stdout.write(\n '\\n--ai-preflight is active. In one paragraph, describe what you expect this deploy to do.\\nEnd with an empty line (or Ctrl-D):\\n> ',\n );\n }\n process.stdin.setEncoding('utf8');\n let buffer = '';\n await new Promise<void>((resolve) => {\n const onData = (chunk: string): void => {\n buffer += chunk;\n if (/\\n\\s*\\n/.test(buffer)) {\n process.stdin.removeListener('data', onData);\n process.stdin.removeListener('end', onEnd);\n resolve();\n }\n };\n const onEnd = (): void => {\n process.stdin.removeListener('data', onData);\n resolve();\n };\n process.stdin.on('data', onData);\n process.stdin.on('end', onEnd);\n });\n const trimmed = buffer.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\n/**\n * VARSYNTAX.2 — parse the `--variables KEY=VALUE,KEY=VALUE,...` flag.\n * Returns undefined when nothing is parsed (no KV pairs, missing values,\n * empty string) so callers can `...(parsed ? { variables: parsed } : {})`\n * without leaking an empty object.\n */\nexport function parsePublishVariables(raw: unknown): Record<string, string> | undefined {\n if (!raw) return undefined;\n const out: Record<string, string> = {};\n for (const pair of String(raw).split(',')) {\n const eq = pair.indexOf('=');\n if (eq <= 0) continue;\n const k = pair.slice(0, eq).trim();\n const v = pair.slice(eq + 1).trim();\n if (k.length > 0) out[k] = v;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n"],"mappings":"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","names":["bodyReport"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/purge.ts
|
|
4
|
+
import { promises as fs } from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { compare, purge } from "@sdt-tools/core";
|
|
8
|
+
function purgeCommand() {
|
|
9
|
+
return new Command("purge").description(
|
|
10
|
+
"Generate a DROP script for every object in the project (bulk teardown). Requires --confirm-production. Honours all per-type drop gates \u2014 relax with --allow-* flags."
|
|
11
|
+
).requiredOption("--source <path>", ".sdtproj or .sdtpac to purge from the target.").option(
|
|
12
|
+
"--confirm-production",
|
|
13
|
+
"Explicit acknowledgement that this drops every project object.",
|
|
14
|
+
false
|
|
15
|
+
).option("--allow-drop-table", "Permit DROP TABLE on regular tables (default refuses).", false).option(
|
|
16
|
+
"--allow-unrecoverable-drop",
|
|
17
|
+
"Permit drops of UNRECOVERABLE types (STREAM/PIPE/TASK).",
|
|
18
|
+
false
|
|
19
|
+
).option("--allow-narrowing-types", "Permit narrowing type changes.", false).option("--allow-table-rebuild", "Permit table rebuilds.", false).option("-o, --output <path>", "Write script to a file instead of stdout.").action(async (opts) => {
|
|
20
|
+
purge.assertPurgeConfirmation({ confirmProduction: opts.confirmProduction === true });
|
|
21
|
+
const sourcePath = path.resolve(String(opts.source));
|
|
22
|
+
const source = new compare.ProjectSource(sourcePath);
|
|
23
|
+
const model = await source.load();
|
|
24
|
+
const result = purge.generatePurgeScript(model, {
|
|
25
|
+
deployOptions: {
|
|
26
|
+
deployment: {
|
|
27
|
+
allowDropTable: opts.allowDropTable === true,
|
|
28
|
+
allowUnrecoverableDrop: opts.allowUnrecoverableDrop === true,
|
|
29
|
+
allowNarrowingTypes: opts.allowNarrowingTypes === true,
|
|
30
|
+
allowTableRebuild: opts.allowTableRebuild === true,
|
|
31
|
+
// Purge means we WANT drops — clear the default skip-list.
|
|
32
|
+
doNotDropObjectTypes: [],
|
|
33
|
+
preserveTargetOnlyObjects: false,
|
|
34
|
+
blockOnPossibleDataLoss: false
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
targetLabel: "target (purge)"
|
|
38
|
+
});
|
|
39
|
+
const header = `-- PURGE script generated by sdt \u2014 drops every object in ${path.basename(sourcePath)} from the target.
|
|
40
|
+
-- ${result.summary.objects} object(s) considered \xB7 types: ${result.summary.droppedObjectTypes.join(", ") || "(none)"}
|
|
41
|
+
-- skipped=${result.summary.skipped} refused=${result.summary.refused}
|
|
42
|
+
|
|
43
|
+
`;
|
|
44
|
+
const out = header + result.sql;
|
|
45
|
+
if (opts.output) {
|
|
46
|
+
const outPath = path.resolve(String(opts.output));
|
|
47
|
+
await fs.writeFile(outPath, out, "utf8");
|
|
48
|
+
console.error(`purge: wrote ${result.summary.objects} object(s) to ${outPath}`);
|
|
49
|
+
} else {
|
|
50
|
+
process.stdout.write(out);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
purgeCommand
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=purge-QMXZKCMD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/purge.ts"],"sourcesContent":["/**\n * `sdt purge` — DCM compatibility item 2.\n *\n * Generates a DROP script for every object in the project. The operator\n * runs the script explicitly — purge is destructive enough that v1\n * does not auto-execute.\n *\n * Mirrors DCM's `EXECUTE DCM PROJECT <n> PURGE` semantic. Honours every\n * per-type drop gate already in the safety classifier — set them via\n * --allow-* flags to relax. Default: refuses everything until each\n * relevant gate is flipped.\n */\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { compare, purge } from '@sdt-tools/core';\n\nexport function purgeCommand(): Command {\n return new Command('purge')\n .description(\n 'Generate a DROP script for every object in the project (bulk teardown). ' +\n 'Requires --confirm-production. Honours all per-type drop gates — relax with --allow-* flags.',\n )\n .requiredOption('--source <path>', '.sdtproj or .sdtpac to purge from the target.')\n .option(\n '--confirm-production',\n 'Explicit acknowledgement that this drops every project object.',\n false,\n )\n .option('--allow-drop-table', 'Permit DROP TABLE on regular tables (default refuses).', false)\n .option(\n '--allow-unrecoverable-drop',\n 'Permit drops of UNRECOVERABLE types (STREAM/PIPE/TASK).',\n false,\n )\n .option('--allow-narrowing-types', 'Permit narrowing type changes.', false)\n .option('--allow-table-rebuild', 'Permit table rebuilds.', false)\n .option('-o, --output <path>', 'Write script to a file instead of stdout.')\n .action(async (opts: Record<string, unknown>) => {\n purge.assertPurgeConfirmation({ confirmProduction: opts.confirmProduction === true });\n const sourcePath = path.resolve(String(opts.source));\n const source = new compare.ProjectSource(sourcePath);\n const model = await source.load();\n const result = purge.generatePurgeScript(model, {\n deployOptions: {\n deployment: {\n allowDropTable: opts.allowDropTable === true,\n allowUnrecoverableDrop: opts.allowUnrecoverableDrop === true,\n allowNarrowingTypes: opts.allowNarrowingTypes === true,\n allowTableRebuild: opts.allowTableRebuild === true,\n // Purge means we WANT drops — clear the default skip-list.\n doNotDropObjectTypes: [],\n preserveTargetOnlyObjects: false,\n blockOnPossibleDataLoss: false,\n },\n },\n targetLabel: 'target (purge)',\n });\n const header =\n `-- PURGE script generated by sdt — drops every object in ${path.basename(sourcePath)} from the target.\\n` +\n `-- ${result.summary.objects} object(s) considered · types: ${result.summary.droppedObjectTypes.join(', ') || '(none)'}\\n` +\n `-- skipped=${result.summary.skipped} refused=${result.summary.refused}\\n\\n`;\n const out = header + result.sql;\n if (opts.output) {\n const outPath = path.resolve(String(opts.output));\n await fs.writeFile(outPath, out, 'utf8');\n console.error(`purge: wrote ${result.summary.objects} object(s) to ${outPath}`);\n } else {\n process.stdout.write(out);\n }\n });\n}\n"],"mappings":";;;AAYA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,SAAS,SAAS,aAAa;AAExB,SAAS,eAAwB;AACtC,SAAO,IAAI,QAAQ,OAAO,EACvB;AAAA,IACC;AAAA,EAEF,EACC,eAAe,mBAAmB,+CAA+C,EACjF;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC,OAAO,sBAAsB,0DAA0D,KAAK,EAC5F;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC,OAAO,2BAA2B,kCAAkC,KAAK,EACzE,OAAO,yBAAyB,0BAA0B,KAAK,EAC/D,OAAO,uBAAuB,2CAA2C,EACzE,OAAO,OAAO,SAAkC;AAC/C,UAAM,wBAAwB,EAAE,mBAAmB,KAAK,sBAAsB,KAAK,CAAC;AACpF,UAAM,aAAa,KAAK,QAAQ,OAAO,KAAK,MAAM,CAAC;AACnD,UAAM,SAAS,IAAI,QAAQ,cAAc,UAAU;AACnD,UAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,UAAM,SAAS,MAAM,oBAAoB,OAAO;AAAA,MAC9C,eAAe;AAAA,QACb,YAAY;AAAA,UACV,gBAAgB,KAAK,mBAAmB;AAAA,UACxC,wBAAwB,KAAK,2BAA2B;AAAA,UACxD,qBAAqB,KAAK,wBAAwB;AAAA,UAClD,mBAAmB,KAAK,sBAAsB;AAAA;AAAA,UAE9C,sBAAsB,CAAC;AAAA,UACvB,2BAA2B;AAAA,UAC3B,yBAAyB;AAAA,QAC3B;AAAA,MACF;AAAA,MACA,aAAa;AAAA,IACf,CAAC;AACD,UAAM,SACJ,iEAA4D,KAAK,SAAS,UAAU,CAAC;AAAA,KAC/E,OAAO,QAAQ,OAAO,qCAAkC,OAAO,QAAQ,mBAAmB,KAAK,IAAI,KAAK,QAAQ;AAAA,aACxG,OAAO,QAAQ,OAAO,YAAY,OAAO,QAAQ,OAAO;AAAA;AAAA;AACxE,UAAM,MAAM,SAAS,OAAO;AAC5B,QAAI,KAAK,QAAQ;AACf,YAAM,UAAU,KAAK,QAAQ,OAAO,KAAK,MAAM,CAAC;AAChD,YAAM,GAAG,UAAU,SAAS,KAAK,MAAM;AACvC,cAAQ,MAAM,gBAAgB,OAAO,QAAQ,OAAO,iBAAiB,OAAO,EAAE;AAAA,IAChF,OAAO;AACL,cAAQ,OAAO,MAAM,GAAG;AAAA,IAC1B;AAAA,EACF,CAAC;AACL;","names":[]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/query-log.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { promises as fs } from "fs";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { queryLog } from "@sdt-tools/core";
|
|
9
|
+
var DEFAULT_STORE = path.join(os.homedir(), ".sdt", "query-log.json");
|
|
10
|
+
async function loadStore(storePath) {
|
|
11
|
+
try {
|
|
12
|
+
const raw = await fs.readFile(storePath, "utf8");
|
|
13
|
+
return queryLog.QueryLogStore.deserialize(JSON.parse(raw));
|
|
14
|
+
} catch {
|
|
15
|
+
return new queryLog.QueryLogStore();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function saveStore(storePath, store) {
|
|
19
|
+
await fs.mkdir(path.dirname(storePath), { recursive: true });
|
|
20
|
+
await fs.writeFile(storePath, JSON.stringify(store.serialize(), null, 2), "utf8");
|
|
21
|
+
}
|
|
22
|
+
function queryLogCommand() {
|
|
23
|
+
const cmd = new Command("query-log");
|
|
24
|
+
cmd.description("Browse and manage the local query log from the EE3 query window (AUTH.5).");
|
|
25
|
+
cmd.command("list").description("List recent query log entries (newest first).").option("--profile <p>", "Filter to a specific connection profile.").option("--limit <n>", "Maximum entries to show (default 100).", "100").option("--format <fmt>", "text | json (default text).", "text").option("--store <path>", "Path to the query log JSON file.", DEFAULT_STORE).action(async (opts) => {
|
|
26
|
+
const store = await loadStore(opts.store);
|
|
27
|
+
const entries = store.listEntries({
|
|
28
|
+
profile: opts.profile,
|
|
29
|
+
limit: parseInt(String(opts.limit ?? "100"), 10)
|
|
30
|
+
});
|
|
31
|
+
const fmt = String(opts.format ?? "text").toLowerCase();
|
|
32
|
+
if (fmt === "json") {
|
|
33
|
+
process.stdout.write(JSON.stringify(entries, null, 2) + "\n");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (entries.length === 0) {
|
|
37
|
+
process.stdout.write("No query log entries.\n");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
for (const e of entries) {
|
|
41
|
+
const when = e.executedAt.slice(0, 19).replace("T", " ");
|
|
42
|
+
const dur = e.durationMs !== void 0 ? ` ${e.durationMs}ms` : "";
|
|
43
|
+
const rows = e.rowCount !== void 0 ? ` ${e.rowCount} rows` : "";
|
|
44
|
+
const status = e.status === "error" ? " \u2717" : " \u2713";
|
|
45
|
+
const sql = e.sql.replace(/\s+/g, " ").slice(0, 80);
|
|
46
|
+
process.stdout.write(`[${e.id}] ${when} [${e.profile}]${status}${dur}${rows} ${sql}
|
|
47
|
+
`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
cmd.command("show <id>").description("Show a single query log entry by id.").option("--format <fmt>", "text | json | sql (default text).", "text").option("--store <path>", "Path to the query log JSON file.", DEFAULT_STORE).action(async (id, opts) => {
|
|
51
|
+
const store = await loadStore(opts.store);
|
|
52
|
+
const entry = store.getEntry(id);
|
|
53
|
+
if (!entry) throw new Error(`No entry with id '${id}'.`);
|
|
54
|
+
const fmt = String(opts.format ?? "text").toLowerCase();
|
|
55
|
+
if (fmt === "sql") {
|
|
56
|
+
process.stdout.write(entry.sql + "\n");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (fmt === "json") {
|
|
60
|
+
process.stdout.write(JSON.stringify(entry, null, 2) + "\n");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
process.stdout.write(`id: ${entry.id}
|
|
64
|
+
`);
|
|
65
|
+
process.stdout.write(`profile: ${entry.profile}
|
|
66
|
+
`);
|
|
67
|
+
process.stdout.write(`executed: ${entry.executedAt}
|
|
68
|
+
`);
|
|
69
|
+
process.stdout.write(`status: ${entry.status}
|
|
70
|
+
`);
|
|
71
|
+
if (entry.durationMs !== void 0) process.stdout.write(`duration: ${entry.durationMs}ms
|
|
72
|
+
`);
|
|
73
|
+
if (entry.rowCount !== void 0) process.stdout.write(`rows: ${entry.rowCount}
|
|
74
|
+
`);
|
|
75
|
+
if (entry.error) process.stdout.write(`error: ${entry.error}
|
|
76
|
+
`);
|
|
77
|
+
process.stdout.write(`
|
|
78
|
+
${entry.sql}
|
|
79
|
+
`);
|
|
80
|
+
});
|
|
81
|
+
cmd.command("add <sql>").description("Manually append a query to the log (used by EE3 and for testing).").requiredOption("--profile <p>", "Connection profile the query was run against.").option("--status <s>", "success | error (default success).", "success").option("--store <path>", "Path to the query log JSON file.", DEFAULT_STORE).action(async (sql, opts) => {
|
|
82
|
+
if (opts.status !== "success" && opts.status !== "error") {
|
|
83
|
+
throw new Error(`--status must be success or error, got: ${opts.status}`);
|
|
84
|
+
}
|
|
85
|
+
const store = await loadStore(opts.store);
|
|
86
|
+
const entry = store.appendEntry({
|
|
87
|
+
profile: opts.profile,
|
|
88
|
+
sql,
|
|
89
|
+
executedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90
|
+
status: opts.status
|
|
91
|
+
});
|
|
92
|
+
await saveStore(opts.store, store);
|
|
93
|
+
process.stdout.write(`Added entry ${entry.id}.
|
|
94
|
+
`);
|
|
95
|
+
});
|
|
96
|
+
cmd.command("clear").description("Remove query log entries.").option("--profile <p>", "Remove only entries for this profile (omit for all).").option("--yes", "Skip confirmation prompt.").option("--store <path>", "Path to the query log JSON file.", DEFAULT_STORE).action(async (opts) => {
|
|
97
|
+
if (!opts.yes) {
|
|
98
|
+
const target = opts.profile ? `profile '${opts.profile}'` : "all profiles";
|
|
99
|
+
throw new Error(`Pass --yes to confirm clearing the query log for ${target}.`);
|
|
100
|
+
}
|
|
101
|
+
const store = await loadStore(opts.store);
|
|
102
|
+
const removed = store.clearEntries(opts.profile);
|
|
103
|
+
await saveStore(opts.store, store);
|
|
104
|
+
process.stdout.write(`Cleared ${removed} entries.
|
|
105
|
+
`);
|
|
106
|
+
});
|
|
107
|
+
return cmd;
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
queryLogCommand
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=query-log-6OM4GI7W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/query-log.ts"],"sourcesContent":["/**\n * `sdt query-log` — AUTH.5 local query log CLI.\n *\n * Subcommands:\n * sdt query-log list [--profile <p>] [--limit N] [--format text|json]\n * sdt query-log show <id> [--format text|json|sql]\n * sdt query-log add <sql> --profile <p> [--status success|error] [--store <path>]\n * sdt query-log clear [--profile <p>] [--yes]\n *\n * The log is stored at `~/.sdt/query-log.json` (override with --store).\n * EE3 will call `appendEntry()` on the substrate directly when a query\n * is executed. This CLI surfaces it for ad-hoc review + rerun.\n *\n * Mirrors `Databricks/packages/cli/src/commands/query-log.ts`.\n */\nimport { Command } from 'commander';\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { queryLog } from '@sdt-tools/core';\n\nconst DEFAULT_STORE = path.join(os.homedir(), '.sdt', 'query-log.json');\n\nasync function loadStore(storePath: string): Promise<queryLog.QueryLogStore> {\n try {\n const raw = await fs.readFile(storePath, 'utf8');\n return queryLog.QueryLogStore.deserialize(JSON.parse(raw));\n } catch {\n return new queryLog.QueryLogStore();\n }\n}\n\nasync function saveStore(storePath: string, store: queryLog.QueryLogStore): Promise<void> {\n await fs.mkdir(path.dirname(storePath), { recursive: true });\n await fs.writeFile(storePath, JSON.stringify(store.serialize(), null, 2), 'utf8');\n}\n\nexport function queryLogCommand(): Command {\n const cmd = new Command('query-log');\n cmd.description('Browse and manage the local query log from the EE3 query window (AUTH.5).');\n\n cmd\n .command('list')\n .description('List recent query log entries (newest first).')\n .option('--profile <p>', 'Filter to a specific connection profile.')\n .option('--limit <n>', 'Maximum entries to show (default 100).', '100')\n .option('--format <fmt>', 'text | json (default text).', 'text')\n .option('--store <path>', 'Path to the query log JSON file.', DEFAULT_STORE)\n .action(async (opts) => {\n const store = await loadStore(opts.store as string);\n const entries = store.listEntries({\n profile: opts.profile as string | undefined,\n limit: parseInt(String(opts.limit ?? '100'), 10),\n });\n const fmt = String(opts.format ?? 'text').toLowerCase();\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(entries, null, 2) + '\\n');\n return;\n }\n if (entries.length === 0) {\n process.stdout.write('No query log entries.\\n');\n return;\n }\n for (const e of entries) {\n const when = e.executedAt.slice(0, 19).replace('T', ' ');\n const dur = e.durationMs !== undefined ? ` ${e.durationMs}ms` : '';\n const rows = e.rowCount !== undefined ? ` ${e.rowCount} rows` : '';\n const status = e.status === 'error' ? ' ✗' : ' ✓';\n const sql = e.sql.replace(/\\s+/g, ' ').slice(0, 80);\n process.stdout.write(`[${e.id}] ${when} [${e.profile}]${status}${dur}${rows} ${sql}\\n`);\n }\n });\n\n cmd\n .command('show <id>')\n .description('Show a single query log entry by id.')\n .option('--format <fmt>', 'text | json | sql (default text).', 'text')\n .option('--store <path>', 'Path to the query log JSON file.', DEFAULT_STORE)\n .action(async (id: string, opts) => {\n const store = await loadStore(opts.store as string);\n const entry = store.getEntry(id);\n if (!entry) throw new Error(`No entry with id '${id}'.`);\n const fmt = String(opts.format ?? 'text').toLowerCase();\n if (fmt === 'sql') {\n process.stdout.write(entry.sql + '\\n');\n return;\n }\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(entry, null, 2) + '\\n');\n return;\n }\n process.stdout.write(`id: ${entry.id}\\n`);\n process.stdout.write(`profile: ${entry.profile}\\n`);\n process.stdout.write(`executed: ${entry.executedAt}\\n`);\n process.stdout.write(`status: ${entry.status}\\n`);\n if (entry.durationMs !== undefined) process.stdout.write(`duration: ${entry.durationMs}ms\\n`);\n if (entry.rowCount !== undefined) process.stdout.write(`rows: ${entry.rowCount}\\n`);\n if (entry.error) process.stdout.write(`error: ${entry.error}\\n`);\n process.stdout.write(`\\n${entry.sql}\\n`);\n });\n\n cmd\n .command('add <sql>')\n .description('Manually append a query to the log (used by EE3 and for testing).')\n .requiredOption('--profile <p>', 'Connection profile the query was run against.')\n .option('--status <s>', 'success | error (default success).', 'success')\n .option('--store <path>', 'Path to the query log JSON file.', DEFAULT_STORE)\n .action(async (sql: string, opts) => {\n if (opts.status !== 'success' && opts.status !== 'error') {\n throw new Error(`--status must be success or error, got: ${opts.status}`);\n }\n const store = await loadStore(opts.store as string);\n const entry = store.appendEntry({\n profile: opts.profile as string,\n sql,\n executedAt: new Date().toISOString(),\n status: opts.status as 'success' | 'error',\n });\n await saveStore(opts.store as string, store);\n process.stdout.write(`Added entry ${entry.id}.\\n`);\n });\n\n cmd\n .command('clear')\n .description('Remove query log entries.')\n .option('--profile <p>', 'Remove only entries for this profile (omit for all).')\n .option('--yes', 'Skip confirmation prompt.')\n .option('--store <path>', 'Path to the query log JSON file.', DEFAULT_STORE)\n .action(async (opts) => {\n if (!opts.yes) {\n const target = opts.profile ? `profile '${opts.profile as string}'` : 'all profiles';\n throw new Error(`Pass --yes to confirm clearing the query log for ${target}.`);\n }\n const store = await loadStore(opts.store as string);\n const removed = store.clearEntries(opts.profile as string | undefined);\n await saveStore(opts.store as string, store);\n process.stdout.write(`Cleared ${removed} entries.\\n`);\n });\n\n return cmd;\n}\n"],"mappings":"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","names":[]}
|