@ddt-tools/cli 0.2.0 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/advise-tests-YNMKVJCD.js +87 -0
  2. package/dist/advise-tests-YNMKVJCD.js.map +1 -0
  3. package/dist/ai-NTNPYEKZ.js +86 -0
  4. package/dist/ai-NTNPYEKZ.js.map +1 -0
  5. package/dist/anonymize-LERTWUQO.js +139 -0
  6. package/dist/anonymize-LERTWUQO.js.map +1 -0
  7. package/dist/approval-GGZGKIU4.js +73 -0
  8. package/dist/approval-GGZGKIU4.js.map +1 -0
  9. package/dist/approval-chain-GWJKZHVU.js +118 -0
  10. package/dist/approval-chain-GWJKZHVU.js.map +1 -0
  11. package/dist/audit-log-2PH55BU4.js +159 -0
  12. package/dist/audit-log-2PH55BU4.js.map +1 -0
  13. package/dist/backlog-QNXGOUF4.js +76 -0
  14. package/dist/backlog-QNXGOUF4.js.map +1 -0
  15. package/dist/bisect-W3XKKRWG.js +111 -0
  16. package/dist/bisect-W3XKKRWG.js.map +1 -0
  17. package/dist/bookmarks-XVOGXGMC.js +107 -0
  18. package/dist/bookmarks-XVOGXGMC.js.map +1 -0
  19. package/dist/branch-S3I2IJGQ.js +103 -0
  20. package/dist/branch-S3I2IJGQ.js.map +1 -0
  21. package/dist/build-MP3JQEFO.js +20 -0
  22. package/dist/build-MP3JQEFO.js.map +1 -0
  23. package/dist/catalog-3J3NFNXP.js +137 -0
  24. package/dist/catalog-3J3NFNXP.js.map +1 -0
  25. package/dist/changelog-ZQAH3ULB.js +216 -0
  26. package/dist/changelog-ZQAH3ULB.js.map +1 -0
  27. package/dist/chunk-2FT6HXKS.js +55 -0
  28. package/dist/chunk-2FT6HXKS.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/chunk-DGUM43GV.js.map +1 -0
  31. package/dist/chunk-DL3V7UJ2.js +25 -0
  32. package/dist/chunk-DL3V7UJ2.js.map +1 -0
  33. package/dist/chunk-VM2H4LAO.js +15 -0
  34. package/dist/chunk-VM2H4LAO.js.map +1 -0
  35. package/dist/chunk-XFXG347C.js +40 -0
  36. package/dist/chunk-XFXG347C.js.map +1 -0
  37. package/dist/cli.js +504 -19402
  38. package/dist/cli.js.map +1 -1
  39. package/dist/compare-IOEATL6G.js +435 -0
  40. package/dist/compare-IOEATL6G.js.map +1 -0
  41. package/dist/compare-profiles-H33CXZPD.js +219 -0
  42. package/dist/compare-profiles-H33CXZPD.js.map +1 -0
  43. package/dist/completion-ZSNCQKJ2.js +89 -0
  44. package/dist/completion-ZSNCQKJ2.js.map +1 -0
  45. package/dist/connection-CDGVEFUC.js +148 -0
  46. package/dist/connection-CDGVEFUC.js.map +1 -0
  47. package/dist/cost-estimate-S2MKHT2H.js +321 -0
  48. package/dist/cost-estimate-S2MKHT2H.js.map +1 -0
  49. package/dist/data-compare-46ZI7KHL.js +128 -0
  50. package/dist/data-compare-46ZI7KHL.js.map +1 -0
  51. package/dist/data-fit-WGEPLD5S.js +127 -0
  52. package/dist/data-fit-WGEPLD5S.js.map +1 -0
  53. package/dist/deploy-status-4H5KJFRC.js +58 -0
  54. package/dist/deploy-status-4H5KJFRC.js.map +1 -0
  55. package/dist/design-ILX3ZSWW.js +135 -0
  56. package/dist/design-ILX3ZSWW.js.map +1 -0
  57. package/dist/diagnose-WPUL67E4.js +150 -0
  58. package/dist/diagnose-WPUL67E4.js.map +1 -0
  59. package/dist/discover-DEO2R5T6.js +78 -0
  60. package/dist/discover-DEO2R5T6.js.map +1 -0
  61. package/dist/docs-QNY3MUVO.js +183 -0
  62. package/dist/docs-QNY3MUVO.js.map +1 -0
  63. package/dist/drift-FDRNPWQA.js +233 -0
  64. package/dist/drift-FDRNPWQA.js.map +1 -0
  65. package/dist/drift-gate-6BWWWMHW.js +103 -0
  66. package/dist/drift-gate-6BWWWMHW.js.map +1 -0
  67. package/dist/error-lookup-4R3Y4RBC.js +56 -0
  68. package/dist/error-lookup-4R3Y4RBC.js.map +1 -0
  69. package/dist/errorReporting-LX6WT4JH.js +109 -0
  70. package/dist/errorReporting-LX6WT4JH.js.map +1 -0
  71. package/dist/exec-JOLH5LPT.js +122 -0
  72. package/dist/exec-JOLH5LPT.js.map +1 -0
  73. package/dist/explain-NS26WE2Y.js +189 -0
  74. package/dist/explain-NS26WE2Y.js.map +1 -0
  75. package/dist/explorer-GSYYYOAL.js +58 -0
  76. package/dist/explorer-GSYYYOAL.js.map +1 -0
  77. package/dist/extract-4LWEZG4O.js +152 -0
  78. package/dist/extract-4LWEZG4O.js.map +1 -0
  79. package/dist/features-KQV4OFIZ.js +54 -0
  80. package/dist/features-KQV4OFIZ.js.map +1 -0
  81. package/dist/feedback-CBLGXUEG.js +158 -0
  82. package/dist/feedback-CBLGXUEG.js.map +1 -0
  83. package/dist/find-SMXRCZ76.js +176 -0
  84. package/dist/find-SMXRCZ76.js.map +1 -0
  85. package/dist/format-HMGG6MY3.js +277 -0
  86. package/dist/format-HMGG6MY3.js.map +1 -0
  87. package/dist/generate-W7VLBDLI.js +160 -0
  88. package/dist/generate-W7VLBDLI.js.map +1 -0
  89. package/dist/graph-YYL5UYCJ.js +168 -0
  90. package/dist/graph-YYL5UYCJ.js.map +1 -0
  91. package/dist/history-GDRFP4PG.js +184 -0
  92. package/dist/history-GDRFP4PG.js.map +1 -0
  93. package/dist/hosts-DRFZTMIJ.js +45 -0
  94. package/dist/hosts-DRFZTMIJ.js.map +1 -0
  95. package/dist/impact-A4NU6CB2.js +63 -0
  96. package/dist/impact-A4NU6CB2.js.map +1 -0
  97. package/dist/import-EGOVKTLX.js +29 -0
  98. package/dist/import-EGOVKTLX.js.map +1 -0
  99. package/dist/import-script-R5RXPDH6.js +79 -0
  100. package/dist/import-script-R5RXPDH6.js.map +1 -0
  101. package/dist/index.cjs +11 -5
  102. package/dist/index.cjs.map +1 -1
  103. package/dist/index.js +8 -2
  104. package/dist/index.js.map +1 -1
  105. package/dist/init-EAOGNGXI.js +54 -0
  106. package/dist/init-EAOGNGXI.js.map +1 -0
  107. package/dist/install-hooks-G3Y5LVXK.js +109 -0
  108. package/dist/install-hooks-G3Y5LVXK.js.map +1 -0
  109. package/dist/license-Z5YSC7XQ.js +43 -0
  110. package/dist/license-Z5YSC7XQ.js.map +1 -0
  111. package/dist/lineage-C5CGVP36.js +555 -0
  112. package/dist/lineage-C5CGVP36.js.map +1 -0
  113. package/dist/lint-AQFPZ3WG.js +144 -0
  114. package/dist/lint-AQFPZ3WG.js.map +1 -0
  115. package/dist/mcp-6ZXOAF7S.js +343 -0
  116. package/dist/mcp-6ZXOAF7S.js.map +1 -0
  117. package/dist/migrate-from-dbt-K4ELOWUD.js +156 -0
  118. package/dist/migrate-from-dbt-K4ELOWUD.js.map +1 -0
  119. package/dist/migrate-platform-E7VZFPO5.js +91 -0
  120. package/dist/migrate-platform-E7VZFPO5.js.map +1 -0
  121. package/dist/optimize-WUJ5ZN5Y.js +109 -0
  122. package/dist/optimize-WUJ5ZN5Y.js.map +1 -0
  123. package/dist/perf-UULZSREY.js +200 -0
  124. package/dist/perf-UULZSREY.js.map +1 -0
  125. package/dist/pii-QHU32VML.js +146 -0
  126. package/dist/pii-QHU32VML.js.map +1 -0
  127. package/dist/pilot-BR6GVK32.js +29 -0
  128. package/dist/pilot-BR6GVK32.js.map +1 -0
  129. package/dist/pr-comment-2FOA3EXG.js +81 -0
  130. package/dist/pr-comment-2FOA3EXG.js.map +1 -0
  131. package/dist/preview-XNY422OU.js +46 -0
  132. package/dist/preview-XNY422OU.js.map +1 -0
  133. package/dist/profile-SQTBNKYS.js +98 -0
  134. package/dist/profile-SQTBNKYS.js.map +1 -0
  135. package/dist/promote-FSGUPIPD.js +417 -0
  136. package/dist/promote-FSGUPIPD.js.map +1 -0
  137. package/dist/publish-HLP3XHM5.js +766 -0
  138. package/dist/publish-HLP3XHM5.js.map +1 -0
  139. package/dist/purge-Y5IOTXKA.js +56 -0
  140. package/dist/purge-Y5IOTXKA.js.map +1 -0
  141. package/dist/query-log-SDDGMJLJ.js +112 -0
  142. package/dist/query-log-SDDGMJLJ.js.map +1 -0
  143. package/dist/refactor-TC7S43F2.js +5809 -0
  144. package/dist/refactor-TC7S43F2.js.map +1 -0
  145. package/dist/refresh-MDJYOYV5.js +39 -0
  146. package/dist/refresh-MDJYOYV5.js.map +1 -0
  147. package/dist/replay-E4664A5K.js +118 -0
  148. package/dist/replay-E4664A5K.js.map +1 -0
  149. package/dist/revert-QWQWCJJB.js +111 -0
  150. package/dist/revert-QWQWCJJB.js.map +1 -0
  151. package/dist/review-7CAVLD67.js +164 -0
  152. package/dist/review-7CAVLD67.js.map +1 -0
  153. package/dist/rollback-suggest-C6D5YFCA.js +79 -0
  154. package/dist/rollback-suggest-C6D5YFCA.js.map +1 -0
  155. package/dist/safer-alternative-QR4QEFUV.js +84 -0
  156. package/dist/safer-alternative-QR4QEFUV.js.map +1 -0
  157. package/dist/safety-OFWUFLK4.js +165 -0
  158. package/dist/safety-OFWUFLK4.js.map +1 -0
  159. package/dist/savings-MEBE4TXI.js +95 -0
  160. package/dist/savings-MEBE4TXI.js.map +1 -0
  161. package/dist/scan-secrets-XCUBMLHL.js +54 -0
  162. package/dist/scan-secrets-XCUBMLHL.js.map +1 -0
  163. package/dist/schema-7JZIG6QR.js +447 -0
  164. package/dist/schema-7JZIG6QR.js.map +1 -0
  165. package/dist/script-BMYVBHFR.js +167 -0
  166. package/dist/script-BMYVBHFR.js.map +1 -0
  167. package/dist/search-TA3C3AZT.js +151 -0
  168. package/dist/search-TA3C3AZT.js.map +1 -0
  169. package/dist/seed-W4Q3L2IU.js +101 -0
  170. package/dist/seed-W4Q3L2IU.js.map +1 -0
  171. package/dist/sketch-6B2V6FJV.js +83 -0
  172. package/dist/sketch-6B2V6FJV.js.map +1 -0
  173. package/dist/snapshot-YMVS322L.js +171 -0
  174. package/dist/snapshot-YMVS322L.js.map +1 -0
  175. package/dist/snippets-EVTN63OU.js +74 -0
  176. package/dist/snippets-EVTN63OU.js.map +1 -0
  177. package/dist/standards-FGJW3CQL.js +238 -0
  178. package/dist/standards-FGJW3CQL.js.map +1 -0
  179. package/dist/suggest-V3LVIFZ5.js +44 -0
  180. package/dist/suggest-V3LVIFZ5.js.map +1 -0
  181. package/dist/suggest-constraints-EX2FCWOQ.js +154 -0
  182. package/dist/suggest-constraints-EX2FCWOQ.js.map +1 -0
  183. package/dist/suite-YTQ3CNX5.js +85 -0
  184. package/dist/suite-YTQ3CNX5.js.map +1 -0
  185. package/dist/telemetry-KOIY3NEQ.js +90 -0
  186. package/dist/telemetry-KOIY3NEQ.js.map +1 -0
  187. package/dist/template-MUJ6X6LN.js +396 -0
  188. package/dist/template-MUJ6X6LN.js.map +1 -0
  189. package/dist/test-XFSQHR2S.js +169 -0
  190. package/dist/test-XFSQHR2S.js.map +1 -0
  191. package/dist/trial-GFTGYCR3.js +31 -0
  192. package/dist/trial-GFTGYCR3.js.map +1 -0
  193. package/dist/validate-LFDEZFFH.js +107 -0
  194. package/dist/validate-LFDEZFFH.js.map +1 -0
  195. package/dist/verify-KRDYOJCR.js +76 -0
  196. package/dist/verify-KRDYOJCR.js.map +1 -0
  197. package/dist/watch-FSG23RR3.js +80 -0
  198. package/dist/watch-FSG23RR3.js.map +1 -0
  199. package/dist/xcompare-U4TXTTIR.js +87 -0
  200. package/dist/xcompare-U4TXTTIR.js.map +1 -0
  201. package/package.json +2 -2
  202. package/dist/cli.cjs +0 -19298
  203. package/dist/cli.cjs.map +0 -1
  204. package/dist/cli.d.cts +0 -1
  205. package/dist/cli.d.ts +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport {\n CompareEngine,\n DatabricksExecutor,\n LiveSource,\n PacSource,\n ProjectSource,\n ScriptGenerator,\n ai,\n aiPreflight,\n approval,\n buildDeployManifest,\n buildExecutionPlan,\n colorizeMigrationScript,\n compileSlice,\n createConnection,\n defaultSafePlan,\n executePlan,\n getProfile,\n lintSql,\n loadProject,\n mergeDeployOptions,\n pac as pacNs,\n pacFreshness,\n protectedObjects,\n queryExecution,\n recordDeployChangelog,\n renderHtmlReport,\n resolveProfile,\n safety,\n testFramework,\n validatePlan,\n type CompareSource,\n type StepResult,\n} from '@ddt-tools/core';\nimport { addMappingFlags, buildMappingFromOptions, describeMapping } from '../util/mapping.js';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\n\n/**\n * `ddt publish` — produce a migration script that drives a target toward a\n * source's desired state. `--dry-run` (default) generates the script and\n * prints the safety assessment + default-safe plan summary.\n * `--apply` executes the plan against the named connection profile via\n * `DatabricksExecutor` + `executePlan`, with automatic rollback on\n * partial failure.\n */\nexport function publishCommand(): Command {\n const cmd = new Command('publish');\n cmd\n .description(\n 'Generate (and optionally apply) a migration script from source → target. ' +\n 'Shared form with `sdt publish`: `--source <desired> --connection <live-target>` ' +\n 'compares the desired state against the live workspace. Or pass `--target <offline>` ' +\n 'to diff against a `.ddtproj` / `.ddtpac` instead.',\n )\n .option(\n '--source <path>',\n 'Source: .ddtproj or .ddtpac (the desired state). Required for a normal publish; omit only with --restore-from-snapshot.',\n )\n .option(\n '--target <path>',\n 'Target / current state: .ddtproj or .ddtpac (offline). Omit to compare the source against the live workspace at --connection (the `sdt publish` model). One of --target or --connection is required for a normal publish.',\n )\n .option(\n '--restore-from-snapshot <batchId>',\n 'Recovery mode: skip the compare step and emit DROP/CREATE … SHALLOW CLONE 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 `ddt publish --restore-from-snapshot <id>`.\",\n )\n .option(\n '--snapshot-dir <path>',\n 'Snapshot registry directory used with --restore-from-snapshot.',\n '.ddt/snapshots',\n )\n .option(\n '--out <path>',\n 'Write the generated SQL to this path (currently honored on the --restore-from-snapshot recovery path).',\n )\n .option('--dry-run', 'Print the migration script and safety assessment without applying.', true)\n .option(\n '--apply',\n 'Apply the migration against --connection. Requires --connection. Default false.',\n false,\n )\n .option(\n '--connection <name>',\n 'Connection profile. Doubles as the live compare target when --target is omitted, and as the apply executor (required with --apply).',\n )\n .option(\n '--yes',\n 'Skip interactive confirmation when applying. Required with --apply (no TTY today).',\n false,\n )\n .option('--variables <kv>', 'Comma-separated KEY=VALUE pairs for $(VAR) substitution.')\n .option('--ignore-case', 'Compare object FQNs case-insensitively.', false)\n .option(\n '--no-rollback',\n 'Skip rollback on partial failure (leaves DIRTY state). Default rollback ON.',\n )\n .option(\n '--require-reversible',\n 'Refuse before any DDL runs if any step lacks reverse SQL.',\n false,\n )\n .option(\n '--manifest <path>',\n 'Write a JSON deploy manifest (every step + status + duration + reverse SQL) to <path>. Audit-friendly.',\n )\n .option('--report-html <path>', 'Write a self-contained HTML compare-report to <path>.')\n .option(\n '--no-lint',\n 'Skip the lint gate. Default: lint the generated script and refuse --apply on ERROR-severity findings.',\n )\n .option(\n '--webhook <url>',\n 'POST a deploy-summary JSON to this URL after --apply completes. Or set $DDT_NOTIFY_WEBHOOK.',\n )\n .option(\n '--profile <name>',\n 'Use the deploymentProfiles[<name>] overlay from --source (.ddtproj). ' +\n 'Pulls profile.connection, profile.variables, profile.deployOptions on top of project defaults.',\n )\n .option(\n '--changelog [catalog]',\n 'After --apply, append a row to <catalog>.__ddt.deploy_log on the workspace. ' +\n \"Defaults catalog to the connection profile's default catalog. Liquibase-style audit table.\",\n )\n .option(\n '--no-slice',\n \"Disable the source's Project Slice (if it has one). Default: a sliced source partitions the diff automatically and target objects outside the slice are left untouched.\",\n )\n .option(\n '--query-tag <tag>',\n 'Tag attached to every executed statement so system.query.history can attribute the deploy. ' +\n 'Prepended as `-- DDT-TAG: <tag>` comment. ' +\n \"Defaults to 'ddt:<projectName>@<projectVersion>:<unix-ms>' when omitted.\",\n )\n .option(\n '--color <mode>',\n 'Color stdout: auto | always | never. Honors NO_COLOR / DDT_NO_COLOR env vars in auto mode.',\n 'auto',\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 `ddt 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>:<catalog>.<schema>).',\n )\n .option(\n '--approvals-root <path>',\n 'Approvals directory (default: .ddt/approvals).',\n path.join('.ddt', 'approvals'),\n )\n .option(\n '--allow-protected <fqns>',\n 'Comma-separated FQNs to exempt from the .ddt-protection.json gate. Repeatable.',\n (val: string, prev: string[]) => [...prev, val],\n [] as string[],\n )\n .option(\n '--allow-all-protected',\n 'Bypass all protection gates defined in .ddt-protection.json. Use with care.',\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 --manifest recipe when tests fail.',\n );\n addMappingFlags(cmd);\n cmd.action(async (opts) => {\n // Recovery mode — branch out of the normal compare/apply flow and emit\n // DROP/CREATE … SHALLOW CLONE from a recorded snapshot batch.\n if (opts.restoreFromSnapshot) {\n await runRestoreFromSnapshot(opts);\n return;\n }\n if (!opts.source) {\n throw new Error('--source <path> is required (unless --restore-from-snapshot is given).');\n }\n if (!opts.target && !opts.connection) {\n throw new Error(\n 'Provide a current state to diff the source against: --target <.ddtproj|.ddtpac> (offline) ' +\n 'or --connection <profile> (the live workspace, mirroring `sdt publish`). With neither there is nothing to compare.',\n );\n }\n const nameMapping = await buildMappingFromOptions(opts);\n const sourcePath = String(opts.source);\n const engine = new CompareEngine();\n const src: CompareSource = sourcePath.endsWith('.ddtpac')\n ? new PacSource(sourcePath, 'source')\n : new ProjectSource(sourcePath, 'source');\n\n // Target: an offline artifact (--target) or, when omitted, the live\n // workspace at --connection. The live path mirrors `sdt publish`, which\n // always diffs the desired-state pac against the live --connection target.\n // The live target's scope (catalog / schema) is derived from the source.\n let tgt: CompareSource;\n if (opts.target) {\n const targetPath = String(opts.target);\n tgt = targetPath.endsWith('.ddtpac')\n ? new PacSource(targetPath, 'target')\n : new ProjectSource(targetPath, 'target');\n } else {\n const scope = await resolveSourceScope(sourcePath);\n const liveProfile = await getProfile(String(opts.connection));\n tgt = new LiveSource(\n createConnection(liveProfile),\n scope,\n `${String(opts.connection)} (live)`,\n );\n }\n\n // Freshness gate — only fires when source is a .ddtpac, before any network call.\n let loadedSrcPac: Awaited<ReturnType<typeof pacNs.readPac>> | undefined;\n if (sourcePath.endsWith('.ddtpac')) {\n loadedSrcPac = await pacNs.readPac(sourcePath);\n const freshnessMode = (opts.freshness as pacFreshness.FreshnessMode | undefined) ?? 'warn';\n if (freshnessMode !== 'skip') {\n const fr = pacFreshness.checkPacFreshness(loadedSrcPac.manifest.builtAt);\n if (fr.status !== 'fresh') {\n const msg = pacFreshness.formatFreshnessWarning(fr, sourcePath);\n if (freshnessMode === 'strict') {\n console.error(msg);\n process.exitCode = 1;\n return;\n } else {\n console.warn(msg);\n }\n }\n }\n }\n\n // Auto-load slice from the source. .ddtpac carries it in manifest;\n // .ddtproj has it as a top-level field. --no-slice bypasses.\n let slice: ReturnType<typeof compileSlice> | undefined;\n if (opts.slice !== false) {\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n if (srcPac.manifest.slice) slice = compileSlice(srcPac.manifest.slice);\n } else {\n const loaded = await loadProject(sourcePath);\n if (loaded.project.slice) slice = compileSlice(loaded.project.slice);\n }\n }\n\n const result = await engine.compare(src, tgt, {\n ignoreCase: !!opts.ignoreCase,\n ...(nameMapping ? { nameMapping } : {}),\n ...(slice ? { sliceFilter: slice } : {}),\n });\n const mappingSummary = describeMapping(nameMapping);\n if (mappingSummary) console.log(`Mapping: ${mappingSummary}`);\n if (slice) {\n const outside = result.outsideScope?.length ?? 0;\n const refs = result.referenced?.length ?? 0;\n console.log(\n `Slice active: ${result.objects.length} owned · ${outside} outside scope (untouched) · ${refs} referenced`,\n );\n }\n\n if (opts.reportHtml) {\n const html = renderHtmlReport(result, {\n title: `Publish ${result.source.label} → ${result.target.label}`,\n safety: safety.assess(result),\n });\n const htmlPath = path.resolve(String(opts.reportHtml));\n await fs.mkdir(path.dirname(htmlPath), { recursive: true });\n await fs.writeFile(htmlPath, html, 'utf8');\n console.log(`Wrote HTML report → ${htmlPath}`);\n }\n\n // --profile overlay (project-level deployOptions + variables + connection).\n // VARSYNTAX.2 — also reads from `.ddtpac` manifest's deploymentProfiles\n // snapshot when --source is a built pac (writable since the pac builder\n // snapshots loaded.project.deploymentProfiles into the manifest).\n let profileDeployment: ReturnType<typeof mergeDeployOptions>['deployment'] | undefined;\n let profileVariables: Record<string, string> = {};\n let profileConnection: string | undefined;\n if (opts.profile) {\n const profileName = String(opts.profile);\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n const block = srcPac.manifest.deploymentProfiles?.[profileName];\n if (!block) {\n const available = Object.keys(srcPac.manifest.deploymentProfiles ?? {});\n throw new Error(\n `--profile ${profileName}: 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 .ddtproj declare any?).'\n : `Available: ${available.join(', ')}.`),\n );\n }\n if (block.variables) profileVariables = { ...block.variables };\n if (block.connection) profileConnection = block.connection;\n // pac-snapshot path does NOT overlay profile-level deployOptions —\n // those are already merged into manifest.deployOptions at build\n // time. Leave profileDeployment undefined; the caller falls back\n // to `mergeDeployOptions().deployment`.\n console.log(\n `Profile: ${profileName}${profileConnection ? ` (connection=${profileConnection})` : ''}`,\n );\n } else {\n const loaded = await loadProject(sourcePath);\n const resolved = resolveProfile(loaded, profileName);\n profileDeployment = resolved.deployment;\n profileVariables = resolved.variables;\n profileConnection = resolved.connection;\n console.log(`Profile: ${resolved.name} (connection=${resolved.connection})`);\n }\n }\n\n const assessment = safety.assess(result);\n const deployment = profileDeployment ?? mergeDeployOptions().deployment;\n const variables = { ...profileVariables, ...(parseVariables(opts.variables) ?? {}) };\n\n // VARSYNTAX.3 — assemble the deploy context so `$(DDT_PROFILE)`,\n // `$(DDT_PROJECT_NAME)`, `$(DDT_PROJECT_VERSION)`, `$(DDT_PLATFORM)`,\n // `$(DDT_TIMESTAMP)`, `$(DDT_USER)` substitute without the operator\n // having to pass them via --variables. projectName / projectVersion\n // come from whichever source was loaded (.ddtpac manifest or\n // .ddtproj); profile defaults to undefined unless --profile was used.\n let ctxProjectName: string | undefined;\n let ctxProjectVersion: string | undefined;\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n ctxProjectName = srcPac.manifest.projectName;\n ctxProjectVersion = srcPac.manifest.projectVersion;\n } else {\n try {\n const loaded = await loadProject(sourcePath);\n ctxProjectName = loaded.project.name;\n ctxProjectVersion = loaded.project.version;\n } catch {\n // best-effort; built-in vars stay defaulted\n }\n }\n const generateContext: {\n profile?: string;\n projectName?: string;\n projectVersion?: string;\n user?: string;\n } = {\n ...(opts.profile ? { profile: String(opts.profile) } : {}),\n ...(ctxProjectName ? { projectName: ctxProjectName } : {}),\n ...(ctxProjectVersion ? { projectVersion: ctxProjectVersion } : {}),\n };\n\n const generator = new ScriptGenerator();\n const script = generator.generate(result, {\n variables,\n deployment,\n context: generateContext,\n });\n\n const plan = defaultSafePlan(result);\n const validated = validatePlan(result, plan);\n const steps = buildExecutionPlan(result, validated, { deployment });\n\n // Protection gate — block operations on objects declared in .ddt-protection.json.\n const protConfig = await protectedObjects.loadProtectionConfig(path.dirname(sourcePath));\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: string) =>\n s.split(',').map((f: string) => f.trim()),\n );\n const violations = protectedObjects.checkProtectedObjects(ops, protConfig, {\n allowedFqns,\n allowAll: Boolean(opts.allowAllProtected),\n });\n if (violations.length > 0) {\n console.error(protectedObjects.formatProtectionViolations(violations));\n process.exitCode = 1;\n return;\n }\n }\n\n printPlanReport(result.summary, script.summary, assessment, steps);\n\n // ── apply path ──────────────────────────────────────────────────────\n if (opts.apply) {\n const connectionName = opts.connection ?? profileConnection;\n if (!connectionName) {\n throw new Error(\n '--apply requires --connection <profile-name> or --profile <env> (supplies connection).',\n );\n }\n if (!opts.yes) {\n throw new Error(\n '--apply requires --yes. The CLI does not currently prompt interactively. Pass --yes to confirm.',\n );\n }\n if (assessment.blocked) {\n console.error(\n `Refusing to apply: safety classifier blocked the deploy (${assessment.blockReason}).`,\n );\n process.exitCode = 2;\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 ?? `${connectionName}:default`);\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 console.error(\n 'Approval gate blocked deploy: ' +\n (outcome.blockReason ?? 'gate not satisfied') +\n `. Record approvals with \\`ddt approval add ${deployId} --as <user>\\`.`,\n );\n process.exitCode = 2;\n return;\n }\n console.error(\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 console.error(\n '--ai-preflight needs a narrative. Provide it inline via --ai-preflight-text or via stdin.',\n );\n process.exitCode = 2;\n return;\n }\n console.log('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: script.sql,\n target: `${result.target.label}@${connectionName ?? 'unknown'}`,\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 console.log(` preflight verdict: ${verdict.verdict}`);\n if (verdict.reasoning) console.log(` preflight reasoning: ${verdict.reasoning}`);\n for (const d of verdict.discrepancies) console.log(` • ${d}`);\n\n const isBlocking =\n verdict.verdict === 'mismatch' ||\n (opts.strictAiPreflight && verdict.verdict === 'partial');\n if (isBlocking && !opts.confirmAfterPreflightMismatch) {\n console.error(\n `Refusing to apply: AI preflight returned \"${verdict.verdict}\". Re-run with --confirm-after-preflight-mismatch to override, or revise the narrative.`,\n );\n process.exitCode = 2;\n return;\n }\n if (verdict.parseFailed) {\n console.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 console.error(\n `--ai-preflight failed: ${msg}. Configure an AI provider via \\`ddt ai status\\` or omit --ai-preflight.`,\n );\n process.exitCode = 2;\n return;\n }\n }\n\n // Lint gate — refuse --apply when the generated script trips an\n // ERROR-severity lint rule (L003 ADD NOT NULL no DEFAULT, L007\n // REFUSED placeholder). Opt out with --no-lint when you've\n // reviewed the script and want to proceed.\n if (opts.lint !== false) {\n const lint = lintSql(script.sql);\n if (lint.errorCount > 0) {\n console.error('');\n console.error(\n `Refusing to apply: lint gate caught ${lint.errorCount} ERROR-severity finding(s).`,\n );\n for (const f of lint.findings) {\n if (f.severity !== 'ERROR') continue;\n console.error(` ${f.rule} line ${f.line}: ${f.message}`);\n if (f.suggestion) console.error(` → ${f.suggestion}`);\n }\n console.error('');\n console.error('Resolve the findings or re-run with --no-lint to override.');\n process.exitCode = 2;\n return;\n }\n if (lint.warningCount > 0) {\n console.log(`Lint: ${lint.warningCount} warning(s) — proceeding.`);\n }\n }\n\n const profile = await getProfile(String(connectionName));\n const conn = createConnection(profile);\n const queryTag = resolveQueryTag(\n opts.queryTag,\n sourcePath,\n result.source.label,\n ctxProjectName,\n ctxProjectVersion,\n );\n if (queryTag) console.log(`Query tag: ${queryTag}`);\n try {\n await conn.connect();\n const executor = new DatabricksExecutor(conn, {\n ...(queryTag ? { queryTag } : {}),\n onStatement: (sql) => {\n const head = sql.split('\\n')[0]?.slice(0, 100) ?? '';\n console.log(` → ${head}${sql.length > 100 ? ' …' : ''}`);\n },\n });\n console.log('');\n console.log('--- APPLYING ---');\n const report = await executePlan(steps, executor, {\n rollbackOnFailure: opts.rollback !== false,\n requireReversible: !!opts.requireReversible,\n onStepStart: (step) => {\n console.log(`▶ ${step.objectType} ${step.fqn} — ${step.description}`);\n },\n onStepEnd: (r: StepResult) => {\n const tag =\n r.status === 'SUCCESS'\n ? '✓'\n : r.status === 'ROLLED_BACK'\n ? '↩'\n : r.status === 'FAILED'\n ? '✗'\n : r.status === 'SKIPPED'\n ? '-'\n : '?';\n console.log(\n ` ${tag} ${r.status}${r.durationMs ? ` (${r.durationMs}ms)` : ''}${r.error ? `: ${r.error}` : ''}`,\n );\n },\n });\n console.log('');\n console.log('--- DEPLOYMENT REPORT ---');\n console.log(`finalState: ${report.finalState}`);\n if (report.failedStepId) console.log(`failedStepId: ${report.failedStepId}`);\n if (report.preflightErrors?.length) {\n console.log('preflightErrors:');\n for (const e of report.preflightErrors) console.log(` ${e}`);\n }\n const manifestJson = JSON.stringify(\n buildDeployManifest(report, profile.auth.host),\n null,\n 2,\n );\n if (opts.manifest) {\n const mPath = path.resolve(String(opts.manifest));\n await fs.mkdir(path.dirname(mPath), { recursive: true });\n await fs.writeFile(mPath, manifestJson + '\\n', 'utf8');\n console.log(`Wrote deploy manifest → ${mPath}`);\n }\n\n // Target-side changelog table — best-effort. Failures are reported\n // (the deploy already succeeded) but never thrown.\n if (opts.changelog) {\n const catalog = opts.changelog === true ? undefined : String(opts.changelog);\n const r = await recordDeployChangelog(\n conn,\n report,\n result.source,\n result.target,\n manifestJson,\n '0.3.0',\n catalog ? { catalog } : {},\n );\n if (r.ok) {\n console.log(`Changelog: deploy_id=${r.entry.deployId} appended.`);\n } else {\n console.error(`Changelog write failed (deploy still completed): ${r.error}`);\n }\n }\n\n // Webhook notification — fire-and-forget POST with a small JSON\n // summary. Slack / Teams / generic JSON receivers all work.\n const webhookUrl = opts.webhook ?? process.env['DDT_NOTIFY_WEBHOOK'];\n if (webhookUrl) {\n try {\n await postWebhook(String(webhookUrl), {\n workspaceHost: profile.auth.host,\n source: result.source.label,\n target: result.target.label,\n finalState: report.finalState,\n failedStepId: report.failedStepId,\n stepsTotal: report.steps.length,\n stepsSucceeded: report.steps.filter((s) => s.status === 'SUCCESS').length,\n stepsFailed: report.steps.filter((s) => s.status === 'FAILED').length,\n });\n console.log(`Webhook notified → ${webhookUrl}`);\n } catch (err) {\n console.error(\n `Webhook POST failed (deploy still completed): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (report.finalState === 'DIRTY') {\n console.error(\n 'DIRTY: rollback could not complete. Manual intervention required — see report above.',\n );\n process.exitCode = 1;\n } else if (report.finalState !== 'CLEAN_SUCCESS') {\n process.exitCode = 1;\n }\n\n if (opts.postDeployTests && report.finalState === 'CLEAN_SUCCESS') {\n const projectRoot = path.resolve(String(opts.postDeployTests));\n console.log('Running post-deploy DQ tests from ' + projectRoot + '...');\n const testFiles = await testFramework.discoverTests(projectRoot);\n if (testFiles.length === 0) {\n console.warn(\n ' POST_DEPLOY_DQ: no tests found under ' + projectRoot + '/tests/ — skipping.',\n );\n } else {\n const dqReport = await testFramework.runTestFiles(testFiles, conn);\n process.stdout.write(testFramework.renderTextReport(dqReport));\n if (dqReport.summary.blocking) {\n console.error(\n 'POST_DEPLOY_DQ_FAILED: ' +\n dqReport.summary.fail +\n ' fail / ' +\n dqReport.summary.error +\n ' error.' +\n (opts.manifest\n ? ' Recover: `ddt revert --manifest ' + String(opts.manifest) + '`'\n : ' Inspect tests/ and re-run after fixing data issues.'),\n );\n process.exitCode = 2;\n } else {\n console.log(\n ' POST_DEPLOY_DQ: ' +\n dqReport.summary.pass +\n '/' +\n dqReport.summary.total +\n ' pass.',\n );\n }\n }\n }\n } finally {\n await conn.disconnect();\n }\n return;\n }\n\n // ── dry-run path (default) ──────────────────────────────────────────\n // Colorize the script for terminal output so destructive lines and safety\n // banners are unmissable; ANSI is stripped in auto mode when stdout is\n // not a TTY (e.g. piped to a file or CI log).\n const colorMode = (opts.color as 'auto' | 'always' | 'never' | undefined) ?? 'auto';\n console.log('');\n console.log('--- MIGRATION SCRIPT ---');\n console.log(colorizeMigrationScript(script.sql, { mode: colorMode }));\n });\n attachRelatedOptions(cmd, [\n 'deployment.allowDropTable',\n 'deployment.allowDropColumn',\n 'deployment.allowUnrecoverableDrop',\n 'deployment.allowNarrowingTypes',\n 'deployment.allowTableRebuild',\n 'deployment.blockOnPossibleDataLoss',\n 'deployment.preserveTargetOnlyObjects',\n ]);\n return cmd;\n}\n\nfunction printPlanReport(\n diffSummary: { added: number; removed: number; modified: number; unchanged: number },\n scriptSummary: { destructive: number; refused: number },\n assessment: ReturnType<typeof safety.assess>,\n steps: ReturnType<typeof buildExecutionPlan>,\n): void {\n console.log('--- COMPARE SUMMARY ---');\n console.log(\n `+${diffSummary.added} -${diffSummary.removed} ~${diffSummary.modified} =${diffSummary.unchanged}` +\n (scriptSummary.destructive ? ` (destructive=${scriptSummary.destructive})` : '') +\n (scriptSummary.refused ? ` (refused=${scriptSummary.refused})` : ''),\n );\n console.log('');\n console.log('--- SAFETY ASSESSMENT ---');\n console.log(`unrecoverable=${assessment.unrecoverable.length}`);\n console.log(`destructive=${assessment.destructive.length}`);\n console.log(`expensive=${assessment.expensive.length}`);\n console.log(`warnings=${assessment.warnings.length}`);\n if (assessment.blocked) console.log(`BLOCKED: ${assessment.blockReason}`);\n console.log('');\n console.log('--- DEFAULT-SAFE PLAN ---');\n const reversible = steps.filter((st) => st.reverseSql).length;\n console.log(`${steps.length} step(s) planned (default-safe selection)`);\n console.log(`${reversible} step(s) reversible · ${steps.length - reversible} irreversible`);\n}\n\n/**\n * Derive the live-target scope (catalog / schema) from the source artifact.\n * Both `.ddtpac` (manifest.scope) and `.ddtproj` (project.scope) carry a\n * `ProjectScope`. Used to scope the live `LiveSource` when `ddt publish` is\n * run without `--target` (the `sdt publish` model — diff against live).\n */\nasync function resolveSourceScope(\n sourcePath: string,\n): Promise<{ catalog?: string; schema?: string }> {\n const scope = sourcePath.endsWith('.ddtpac')\n ? (await pacNs.readPac(sourcePath)).manifest.scope\n : (await loadProject(sourcePath)).project.scope;\n return {\n ...(scope.catalog ? { catalog: scope.catalog } : {}),\n ...(scope.schema ? { schema: scope.schema } : {}),\n };\n}\n\nasync function postWebhook(url: string, payload: unknown): Promise<void> {\n const isSlackOrTeams = /hooks\\.slack\\.com|webhook\\.office\\.com/.test(url);\n let body: string;\n if (isSlackOrTeams) {\n // Both Slack and Teams accept a simple { text: \"...\" } shape.\n const summary = payload as {\n finalState: string;\n target: string;\n stepsTotal: number;\n stepsSucceeded: number;\n stepsFailed: number;\n };\n body = JSON.stringify({\n text: `DDT publish → ${summary.target} · ${summary.finalState} · ${summary.stepsSucceeded}/${summary.stepsTotal} steps ok${summary.stepsFailed ? ` · ${summary.stepsFailed} failed` : ''}`,\n });\n } else {\n body = JSON.stringify(payload);\n }\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status} ${res.statusText}`);\n }\n}\n\n// PD7.2 — parse `--variables KEY=VALUE,KEY=VALUE,...` using indexOf split\n// so a `=` inside the value (base64, JWT, connection-string fragments) is\n// preserved instead of being treated as a second delimiter. Mirrors the SDT\n// parsePublishVariables contract.\nexport function parseVariables(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\n/**\n * Resolve the effective query tag. Honors an explicit `--query-tag`\n * flag value verbatim; otherwise auto-generates\n * `ddt:<projectName>@<projectVersion>:<unix-ms>` using the supplied\n * project context (pac manifest or .ddtproj). When projectName is\n * unknown, falls back to the source-path basename / sourceLabel; when\n * projectVersion is unknown the `@<projectVersion>` segment is omitted\n * so the tag remains valid. Returns undefined only when the user\n * explicitly passed `--query-tag ''`.\n */\nexport function resolveQueryTag(\n raw: unknown,\n sourcePath: string,\n sourceLabel: string,\n projectName: string | undefined,\n projectVersion: string | undefined,\n): string | undefined {\n if (raw !== undefined) {\n const s = String(raw).trim();\n return s.length === 0 ? undefined : s;\n }\n const fallback = sourcePath.endsWith('.ddtpac')\n ? path.basename(sourcePath, '.ddtpac')\n : sourceLabel;\n const base = projectName ?? fallback;\n const version = projectVersion ? `@${projectVersion}` : '';\n return `ddt:${base}${version}:${Date.now()}`;\n}\n\n/**\n * `ddt publish --restore-from-snapshot <batchId>` recovery path.\n *\n * Loads the batch from the registry, emits the restore SQL (DROP target +\n * CREATE … SHALLOW CLONE from snapshot, or a MANUAL hint row for\n * materialised / streaming objects), and either prints it (dry-run / no\n * --apply) or executes it against the live target (--apply --yes).\n *\n * Skips: pac freshness, compare, safety classifier, AI preflight, approval\n * gate, lint gate, 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 ?? '.ddt/snapshots'));\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 console.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 console.log(`Restoring from batch ${batch.batchId} (createdAt=${batch.createdAt})`);\n console.log(\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 console.log(`Wrote restore SQL → ${out}`);\n }\n\n // The DDT publish defaults --dry-run to true; recovery follows the same\n // convention: only --apply executes anything.\n if (!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 console.error(\n '--apply requires --yes (restore replaces target tables via DROP + SHALLOW CLONE).',\n );\n process.exitCode = 1;\n return;\n }\n if (!opts.connection) {\n console.error('--apply --restore-from-snapshot requires --connection <profile>.');\n process.exitCode = 1;\n return;\n }\n\n const profile = await getProfile(String(opts.connection));\n const conn = createConnection(profile);\n let executed = 0;\n let failed = 0;\n try {\n await conn.connect();\n for (const stmt of queryExecution.splitStatements(sql).map((s) => s.sql)) {\n try {\n await conn.executeRows(stmt);\n executed += 1;\n const head = stmt.split('\\n')[0]?.slice(0, 100) ?? '';\n console.log(' → ' + head + (stmt.length > 100 ? ' …' : ''));\n } catch (err) {\n failed += 1;\n const msg = err instanceof Error ? err.message : String(err);\n console.error(` [restore ${batch.batchId}] ${msg}\\n ${stmt}`);\n }\n }\n } finally {\n await conn.disconnect();\n }\n\n if (failed === 0) {\n console.log(`Restore complete. ${executed} statement(s) executed.`);\n } else {\n console.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"],"mappings":"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","names":[]}
@@ -0,0 +1,56 @@
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 { purge, ProjectSource } from "@ddt-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."
11
+ ).requiredOption("--source <path>", ".ddtproj or .ddtpac 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 (STREAMING_TABLE/PIPELINE).",
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 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
+ doNotDropObjectTypes: [],
32
+ preserveTargetOnlyObjects: false,
33
+ blockOnPossibleDataLoss: false
34
+ }
35
+ },
36
+ targetLabel: "target (purge)"
37
+ });
38
+ const header = `-- PURGE script generated by ddt \u2014 drops every object in ${path.basename(sourcePath)} from the target.
39
+ -- ${result.summary.objects} object(s) considered \xB7 types: ${result.summary.droppedObjectTypes.join(", ") || "(none)"}
40
+ -- skipped=${result.summary.skipped} refused=${result.summary.refused}
41
+
42
+ `;
43
+ const out = header + result.sql;
44
+ if (opts.output) {
45
+ const outPath = path.resolve(String(opts.output));
46
+ await fs.writeFile(outPath, out, "utf8");
47
+ console.error(`purge: wrote ${result.summary.objects} object(s) to ${outPath}`);
48
+ } else {
49
+ process.stdout.write(out);
50
+ }
51
+ });
52
+ }
53
+ export {
54
+ purgeCommand
55
+ };
56
+ //# sourceMappingURL=purge-Y5IOTXKA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/purge.ts"],"sourcesContent":["/**\n * `ddt purge` — DCM compatibility item 2 (mirrors `sdt purge`).\n */\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { purge, ProjectSource } from '@ddt-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.',\n )\n .requiredOption('--source <path>', '.ddtproj or .ddtpac 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 (STREAMING_TABLE/PIPELINE).',\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 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 doNotDropObjectTypes: [],\n preserveTargetOnlyObjects: false,\n blockOnPossibleDataLoss: false,\n },\n },\n targetLabel: 'target (purge)',\n });\n const header =\n `-- PURGE script generated by ddt — 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":";;;AAGA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,SAAS,OAAO,qBAAqB;AAE9B,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,cAAc,UAAU;AAC3C,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,UAC9C,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 "@ddt-tools/core";
9
+ var DEFAULT_STORE = path.join(os.homedir(), ".ddt", "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-SDDGMJLJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/query-log.ts"],"sourcesContent":["/**\n * `ddt query-log` — AUTH.5 local query log CLI.\n *\n * Subcommands:\n * ddt query-log list [--profile <p>] [--limit N] [--format text|json]\n * ddt query-log show <id> [--format text|json|sql]\n * ddt query-log add <sql> --profile <p> [--status success|error] [--store <path>]\n * ddt query-log clear [--profile <p>] [--yes]\n *\n * The log is stored at `~/.ddt/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 `Snowflake/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 '@ddt-tools/core';\n\nconst DEFAULT_STORE = path.join(os.homedir(), '.ddt', '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":[]}