@duckcodeailabs/dql-cli 1.4.1 → 1.4.3

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 (243) hide show
  1. package/package.json +21 -28
  2. package/LICENSE +0 -123
  3. package/README.md +0 -71
  4. package/dist/apps-api.d.ts +0 -16
  5. package/dist/apps-api.d.ts.map +0 -1
  6. package/dist/apps-api.js +0 -249
  7. package/dist/apps-api.js.map +0 -1
  8. package/dist/args.d.ts +0 -30
  9. package/dist/args.d.ts.map +0 -1
  10. package/dist/args.js +0 -105
  11. package/dist/args.js.map +0 -1
  12. package/dist/args.test.d.ts +0 -2
  13. package/dist/args.test.d.ts.map +0 -1
  14. package/dist/args.test.js +0 -33
  15. package/dist/args.test.js.map +0 -1
  16. package/dist/assets/dql-notebook/assets/codemirror-DJYUkPr1.js +0 -11
  17. package/dist/assets/dql-notebook/assets/index-BJ7MV8Gv.js +0 -847
  18. package/dist/assets/dql-notebook/assets/index-DrhoZmtv.css +0 -1
  19. package/dist/assets/dql-notebook/assets/react-CRB3T2We.js +0 -32
  20. package/dist/assets/dql-notebook/index.html +0 -18
  21. package/dist/assets/notebook-browser/app.js +0 -548
  22. package/dist/assets/notebook-browser/index.html +0 -83
  23. package/dist/assets/notebook-browser/styles.css +0 -336
  24. package/dist/block-templates.d.ts +0 -8
  25. package/dist/block-templates.d.ts.map +0 -1
  26. package/dist/block-templates.js +0 -60
  27. package/dist/block-templates.js.map +0 -1
  28. package/dist/commands/agent.d.ts +0 -19
  29. package/dist/commands/agent.d.ts.map +0 -1
  30. package/dist/commands/agent.js +0 -117
  31. package/dist/commands/agent.js.map +0 -1
  32. package/dist/commands/app.d.ts +0 -32
  33. package/dist/commands/app.d.ts.map +0 -1
  34. package/dist/commands/app.js +0 -307
  35. package/dist/commands/app.js.map +0 -1
  36. package/dist/commands/build.d.ts +0 -3
  37. package/dist/commands/build.d.ts.map +0 -1
  38. package/dist/commands/build.js +0 -69
  39. package/dist/commands/build.js.map +0 -1
  40. package/dist/commands/build.test.d.ts +0 -2
  41. package/dist/commands/build.test.d.ts.map +0 -1
  42. package/dist/commands/build.test.js +0 -44
  43. package/dist/commands/build.test.js.map +0 -1
  44. package/dist/commands/certify.d.ts +0 -3
  45. package/dist/commands/certify.d.ts.map +0 -1
  46. package/dist/commands/certify.js +0 -228
  47. package/dist/commands/certify.js.map +0 -1
  48. package/dist/commands/compile.d.ts +0 -21
  49. package/dist/commands/compile.d.ts.map +0 -1
  50. package/dist/commands/compile.js +0 -198
  51. package/dist/commands/compile.js.map +0 -1
  52. package/dist/commands/compile.test.d.ts +0 -2
  53. package/dist/commands/compile.test.d.ts.map +0 -1
  54. package/dist/commands/compile.test.js +0 -115
  55. package/dist/commands/compile.test.js.map +0 -1
  56. package/dist/commands/diff.d.ts +0 -3
  57. package/dist/commands/diff.d.ts.map +0 -1
  58. package/dist/commands/diff.js +0 -52
  59. package/dist/commands/diff.js.map +0 -1
  60. package/dist/commands/doctor.d.ts +0 -3
  61. package/dist/commands/doctor.d.ts.map +0 -1
  62. package/dist/commands/doctor.js +0 -191
  63. package/dist/commands/doctor.js.map +0 -1
  64. package/dist/commands/doctor.test.d.ts +0 -2
  65. package/dist/commands/doctor.test.d.ts.map +0 -1
  66. package/dist/commands/doctor.test.js +0 -43
  67. package/dist/commands/doctor.test.js.map +0 -1
  68. package/dist/commands/fmt.d.ts +0 -3
  69. package/dist/commands/fmt.d.ts.map +0 -1
  70. package/dist/commands/fmt.js +0 -53
  71. package/dist/commands/fmt.js.map +0 -1
  72. package/dist/commands/info.d.ts +0 -3
  73. package/dist/commands/info.d.ts.map +0 -1
  74. package/dist/commands/info.js +0 -56
  75. package/dist/commands/info.js.map +0 -1
  76. package/dist/commands/init.d.ts +0 -3
  77. package/dist/commands/init.d.ts.map +0 -1
  78. package/dist/commands/init.js +0 -250
  79. package/dist/commands/init.js.map +0 -1
  80. package/dist/commands/init.test.d.ts +0 -2
  81. package/dist/commands/init.test.d.ts.map +0 -1
  82. package/dist/commands/init.test.js +0 -118
  83. package/dist/commands/init.test.js.map +0 -1
  84. package/dist/commands/lineage.d.ts +0 -24
  85. package/dist/commands/lineage.d.ts.map +0 -1
  86. package/dist/commands/lineage.js +0 -634
  87. package/dist/commands/lineage.js.map +0 -1
  88. package/dist/commands/mcp.d.ts +0 -7
  89. package/dist/commands/mcp.d.ts.map +0 -1
  90. package/dist/commands/mcp.js +0 -16
  91. package/dist/commands/mcp.js.map +0 -1
  92. package/dist/commands/migrate.d.ts +0 -12
  93. package/dist/commands/migrate.d.ts.map +0 -1
  94. package/dist/commands/migrate.js +0 -192
  95. package/dist/commands/migrate.js.map +0 -1
  96. package/dist/commands/new.d.ts +0 -3
  97. package/dist/commands/new.d.ts.map +0 -1
  98. package/dist/commands/new.js +0 -490
  99. package/dist/commands/new.js.map +0 -1
  100. package/dist/commands/new.test.d.ts +0 -2
  101. package/dist/commands/new.test.d.ts.map +0 -1
  102. package/dist/commands/new.test.js +0 -191
  103. package/dist/commands/new.test.js.map +0 -1
  104. package/dist/commands/notebook.d.ts +0 -3
  105. package/dist/commands/notebook.d.ts.map +0 -1
  106. package/dist/commands/notebook.js +0 -46
  107. package/dist/commands/notebook.js.map +0 -1
  108. package/dist/commands/parse.d.ts +0 -3
  109. package/dist/commands/parse.d.ts.map +0 -1
  110. package/dist/commands/parse.js +0 -63
  111. package/dist/commands/parse.js.map +0 -1
  112. package/dist/commands/preview.d.ts +0 -3
  113. package/dist/commands/preview.d.ts.map +0 -1
  114. package/dist/commands/preview.js +0 -42
  115. package/dist/commands/preview.js.map +0 -1
  116. package/dist/commands/schedule.d.ts +0 -3
  117. package/dist/commands/schedule.d.ts.map +0 -1
  118. package/dist/commands/schedule.js +0 -215
  119. package/dist/commands/schedule.js.map +0 -1
  120. package/dist/commands/semantic.d.ts +0 -12
  121. package/dist/commands/semantic.d.ts.map +0 -1
  122. package/dist/commands/semantic.js +0 -356
  123. package/dist/commands/semantic.js.map +0 -1
  124. package/dist/commands/serve.d.ts +0 -3
  125. package/dist/commands/serve.d.ts.map +0 -1
  126. package/dist/commands/serve.js +0 -30
  127. package/dist/commands/serve.js.map +0 -1
  128. package/dist/commands/slack.d.ts +0 -13
  129. package/dist/commands/slack.d.ts.map +0 -1
  130. package/dist/commands/slack.js +0 -53
  131. package/dist/commands/slack.js.map +0 -1
  132. package/dist/commands/sync.d.ts +0 -3
  133. package/dist/commands/sync.d.ts.map +0 -1
  134. package/dist/commands/sync.js +0 -192
  135. package/dist/commands/sync.js.map +0 -1
  136. package/dist/commands/sync.test.d.ts +0 -2
  137. package/dist/commands/sync.test.d.ts.map +0 -1
  138. package/dist/commands/sync.test.js +0 -147
  139. package/dist/commands/sync.test.js.map +0 -1
  140. package/dist/commands/test.d.ts +0 -3
  141. package/dist/commands/test.d.ts.map +0 -1
  142. package/dist/commands/test.js +0 -167
  143. package/dist/commands/test.js.map +0 -1
  144. package/dist/commands/validate.d.ts +0 -3
  145. package/dist/commands/validate.d.ts.map +0 -1
  146. package/dist/commands/validate.js +0 -116
  147. package/dist/commands/validate.js.map +0 -1
  148. package/dist/commands/verify.d.ts +0 -11
  149. package/dist/commands/verify.d.ts.map +0 -1
  150. package/dist/commands/verify.js +0 -74
  151. package/dist/commands/verify.js.map +0 -1
  152. package/dist/digest.d.ts +0 -10
  153. package/dist/digest.d.ts.map +0 -1
  154. package/dist/digest.js +0 -83
  155. package/dist/digest.js.map +0 -1
  156. package/dist/git-service.d.ts +0 -17
  157. package/dist/git-service.d.ts.map +0 -1
  158. package/dist/git-service.js +0 -54
  159. package/dist/git-service.js.map +0 -1
  160. package/dist/index.d.ts +0 -3
  161. package/dist/index.d.ts.map +0 -1
  162. package/dist/index.js.map +0 -1
  163. package/dist/llm/index.d.ts +0 -4
  164. package/dist/llm/index.d.ts.map +0 -1
  165. package/dist/llm/index.js +0 -16
  166. package/dist/llm/index.js.map +0 -1
  167. package/dist/llm/providers/claude-agent-sdk.d.ts +0 -3
  168. package/dist/llm/providers/claude-agent-sdk.d.ts.map +0 -1
  169. package/dist/llm/providers/claude-agent-sdk.js +0 -174
  170. package/dist/llm/providers/claude-agent-sdk.js.map +0 -1
  171. package/dist/llm/providers/claude-code.d.ts +0 -8
  172. package/dist/llm/providers/claude-code.d.ts.map +0 -1
  173. package/dist/llm/providers/claude-code.js +0 -171
  174. package/dist/llm/providers/claude-code.js.map +0 -1
  175. package/dist/llm/tools.d.ts +0 -9
  176. package/dist/llm/tools.d.ts.map +0 -1
  177. package/dist/llm/tools.js +0 -112
  178. package/dist/llm/tools.js.map +0 -1
  179. package/dist/llm/types.d.ts +0 -70
  180. package/dist/llm/types.d.ts.map +0 -1
  181. package/dist/llm/types.js +0 -2
  182. package/dist/llm/types.js.map +0 -1
  183. package/dist/local-runtime.d.ts +0 -142
  184. package/dist/local-runtime.d.ts.map +0 -1
  185. package/dist/local-runtime.js +0 -3836
  186. package/dist/local-runtime.js.map +0 -1
  187. package/dist/local-runtime.test.d.ts +0 -2
  188. package/dist/local-runtime.test.d.ts.map +0 -1
  189. package/dist/local-runtime.test.js +0 -241
  190. package/dist/local-runtime.test.js.map +0 -1
  191. package/dist/open-browser.d.ts +0 -2
  192. package/dist/open-browser.d.ts.map +0 -1
  193. package/dist/open-browser.js +0 -29
  194. package/dist/open-browser.js.map +0 -1
  195. package/dist/schedule/alerts.d.ts +0 -5
  196. package/dist/schedule/alerts.d.ts.map +0 -1
  197. package/dist/schedule/alerts.js +0 -54
  198. package/dist/schedule/alerts.js.map +0 -1
  199. package/dist/schedule/discovery.d.ts +0 -4
  200. package/dist/schedule/discovery.d.ts.map +0 -1
  201. package/dist/schedule/discovery.js +0 -36
  202. package/dist/schedule/discovery.js.map +0 -1
  203. package/dist/schedule/notifiers/email.d.ts +0 -3
  204. package/dist/schedule/notifiers/email.d.ts.map +0 -1
  205. package/dist/schedule/notifiers/email.js +0 -76
  206. package/dist/schedule/notifiers/email.js.map +0 -1
  207. package/dist/schedule/notifiers/file.d.ts +0 -3
  208. package/dist/schedule/notifiers/file.d.ts.map +0 -1
  209. package/dist/schedule/notifiers/file.js +0 -50
  210. package/dist/schedule/notifiers/file.js.map +0 -1
  211. package/dist/schedule/notifiers/index.d.ts +0 -10
  212. package/dist/schedule/notifiers/index.d.ts.map +0 -1
  213. package/dist/schedule/notifiers/index.js +0 -33
  214. package/dist/schedule/notifiers/index.js.map +0 -1
  215. package/dist/schedule/notifiers/slack.d.ts +0 -3
  216. package/dist/schedule/notifiers/slack.d.ts.map +0 -1
  217. package/dist/schedule/notifiers/slack.js +0 -58
  218. package/dist/schedule/notifiers/slack.js.map +0 -1
  219. package/dist/schedule/runner.d.ts +0 -11
  220. package/dist/schedule/runner.d.ts.map +0 -1
  221. package/dist/schedule/runner.js +0 -109
  222. package/dist/schedule/runner.js.map +0 -1
  223. package/dist/schedule/runs.d.ts +0 -5
  224. package/dist/schedule/runs.d.ts.map +0 -1
  225. package/dist/schedule/runs.js +0 -41
  226. package/dist/schedule/runs.js.map +0 -1
  227. package/dist/schedule/service.d.ts +0 -12
  228. package/dist/schedule/service.d.ts.map +0 -1
  229. package/dist/schedule/service.js +0 -49
  230. package/dist/schedule/service.js.map +0 -1
  231. package/dist/schedule/types.d.ts +0 -64
  232. package/dist/schedule/types.d.ts.map +0 -1
  233. package/dist/schedule/types.js +0 -2
  234. package/dist/schedule/types.js.map +0 -1
  235. package/dist/semantic-import.d.ts +0 -127
  236. package/dist/semantic-import.d.ts.map +0 -1
  237. package/dist/semantic-import.js +0 -713
  238. package/dist/semantic-import.js.map +0 -1
  239. package/dist/semantic-import.test.d.ts +0 -2
  240. package/dist/semantic-import.test.d.ts.map +0 -1
  241. package/dist/semantic-import.test.js +0 -95
  242. package/dist/semantic-import.test.js.map +0 -1
  243. /package/{dist/index.js → index.js} +0 -0
@@ -1,634 +0,0 @@
1
- /**
2
- * `dql lineage` — Answer-layer lineage analysis.
3
- *
4
- * Shows how data flows from source tables through blocks, semantic metrics,
5
- * domains, and charts. Tracks trust chains and cross-domain impacts.
6
- *
7
- * Usage:
8
- * dql lineage [path] Show full lineage graph summary
9
- * dql lineage <name> [path] Show upstream/downstream for a block, table, or metric
10
- * dql lineage --table <name> [path] Show lineage for a specific source table
11
- * dql lineage --metric <name> [path] Show lineage for a specific metric
12
- * dql lineage --domain <name> [path] Show lineage within a domain
13
- * dql lineage --impact <name> [path] Impact analysis: what breaks if this node changes?
14
- * dql lineage --trust-chain <from> <to> Show trust chain between two blocks
15
- * dql lineage --search <term> [path] Search lineage nodes by name
16
- * dql lineage --focus <name> [path] Show a focused lineage subgraph
17
- * dql lineage --dashboard <name> [path] Show lineage for a dashboard/notebook
18
- * dql lineage --dbt [path] Show the dbt portion of lineage
19
- * dql lineage --export [path] Export lineage as JSON
20
- * dql lineage --no-manifest Force live scan (skip dql-manifest.json)
21
- */
22
- import { readdirSync, readFileSync, existsSync } from 'node:fs';
23
- import { join, extname, resolve } from 'node:path';
24
- import { Parser, loadSemanticLayerFromDir, buildLineageGraph, buildManifest, analyzeImpact, buildTrustChain, detectDomainFlows, getDomainTrustOverview, LineageGraph, queryLineage, } from '@duckcodeailabs/dql-core';
25
- export async function runLineage(blockNameOrPath, rest, flags) {
26
- // Collect all args and separate flags from positional args
27
- const allArgs = [...(blockNameOrPath ? [blockNameOrPath] : []), ...rest];
28
- const noManifest = allArgs.includes('--no-manifest');
29
- const positionalArgs = allArgs.filter((a) => !a.startsWith('-'));
30
- const flagArgs = allArgs.filter((a) => a.startsWith('-'));
31
- // Re-derive blockNameOrPath from the first non-flag positional arg
32
- const effectiveBlockArg = positionalArgs[0] ?? null;
33
- // Determine project root — check for dql.config.json
34
- const candidateRoot = resolve(positionalArgs.find((r) => existsSync(join(resolve(r), 'dql.config.json')))
35
- ?? '.');
36
- const projectRoot = existsSync(join(candidateRoot, 'dql.config.json'))
37
- ? candidateRoot
38
- : resolve('.');
39
- if (!existsSync(join(projectRoot, 'dql.config.json'))) {
40
- console.error('No DQL project found (missing dql.config.json). Run from a project root or pass a project path.');
41
- process.exitCode = 1;
42
- return;
43
- }
44
- // Build the lineage graph — prefer manifest if available
45
- const graph = noManifest
46
- ? buildProjectLineage(projectRoot)
47
- : loadFromManifestOrScan(projectRoot);
48
- if (graph.nodeCount === 0) {
49
- console.log('\n No lineage data found.');
50
- console.log(' Run `dql compile` to generate the project manifest, or add blocks in blocks/.\n');
51
- return;
52
- }
53
- // Route to the right subcommand based on flags
54
- if (flags.format === 'json' || allArgs.includes('--export')) {
55
- console.log(JSON.stringify(graph.toJSON(), null, 2));
56
- return;
57
- }
58
- // --impact <name>
59
- const impactIdx = allArgs.indexOf('--impact');
60
- if (impactIdx >= 0 && allArgs[impactIdx + 1]) {
61
- const nodeId = resolveNodeId(graph, allArgs[impactIdx + 1]);
62
- if (!nodeId) {
63
- console.error(`"${allArgs[impactIdx + 1]}" not found in lineage graph.`);
64
- process.exitCode = 1;
65
- return;
66
- }
67
- return printImpactAnalysis(graph, nodeId, flags);
68
- }
69
- // --trust-chain <from> <to>
70
- const trustIdx = allArgs.indexOf('--trust-chain');
71
- if (trustIdx >= 0 && allArgs[trustIdx + 1] && allArgs[trustIdx + 2]) {
72
- return printTrustChain(graph, allArgs[trustIdx + 1], allArgs[trustIdx + 2], flags);
73
- }
74
- const searchIdx = allArgs.indexOf('--search');
75
- if (searchIdx >= 0 && allArgs[searchIdx + 1]) {
76
- return printSearchResults(graph, allArgs[searchIdx + 1]);
77
- }
78
- const focusIdx = allArgs.indexOf('--focus');
79
- if (focusIdx >= 0 && allArgs[focusIdx + 1]) {
80
- return printFocusedLineage(graph, allArgs[focusIdx + 1]);
81
- }
82
- const dashboardIdx = allArgs.indexOf('--dashboard');
83
- if (dashboardIdx >= 0 && allArgs[dashboardIdx + 1]) {
84
- return printFocusedLineage(graph, `dashboard:${allArgs[dashboardIdx + 1]}`);
85
- }
86
- if (allArgs.includes('--dbt')) {
87
- const result = queryLineage(graph, { types: ['dbt_model', 'dbt_source'] });
88
- console.log(JSON.stringify(result.graph, null, 2));
89
- return;
90
- }
91
- // --domain <name>
92
- if (flags.domain) {
93
- return printDomainLineage(graph, flags.domain, flags);
94
- }
95
- // --table <name>
96
- const tableIdx = allArgs.indexOf('--table');
97
- if (tableIdx >= 0 && allArgs[tableIdx + 1]) {
98
- return printNodeLineage(graph, `table:${allArgs[tableIdx + 1]}`, flags);
99
- }
100
- // --metric <name>
101
- const metricIdx = allArgs.indexOf('--metric');
102
- if (metricIdx >= 0 && allArgs[metricIdx + 1]) {
103
- return printNodeLineage(graph, `metric:${allArgs[metricIdx + 1]}`, flags);
104
- }
105
- // Specific node — smart lookup: try block, then table, then metric
106
- if (effectiveBlockArg && resolve(effectiveBlockArg) !== projectRoot) {
107
- const nodeId = resolveNodeId(graph, effectiveBlockArg);
108
- if (!nodeId) {
109
- console.error(`"${effectiveBlockArg}" not found in lineage graph.`);
110
- console.error(' Hint: use --table <name> or --metric <name> for explicit lookup.');
111
- process.exitCode = 1;
112
- return;
113
- }
114
- return printNodeLineage(graph, nodeId, flags);
115
- }
116
- // Default: show full summary
117
- return printSummary(graph, flags);
118
- }
119
- /** Load lineage from dql-manifest.json if available, otherwise scan live. */
120
- function loadFromManifestOrScan(projectRoot) {
121
- const manifestPath = join(projectRoot, 'dql-manifest.json');
122
- if (existsSync(manifestPath)) {
123
- try {
124
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
125
- if (manifest.lineage?.nodes && manifest.lineage?.edges) {
126
- return LineageGraph.fromJSON({
127
- nodes: manifest.lineage.nodes,
128
- edges: manifest.lineage.edges,
129
- });
130
- }
131
- }
132
- catch {
133
- // Fall through to live scan
134
- }
135
- }
136
- return buildProjectLineage(projectRoot);
137
- }
138
- /** Discover all blocks and semantic layer definitions and build the lineage graph. */
139
- function buildProjectLineage(projectRoot) {
140
- const dbtManifestPath = resolveDbtManifestPath(projectRoot);
141
- try {
142
- const manifest = buildManifest({ projectRoot, dbtManifestPath });
143
- return LineageGraph.fromJSON({
144
- nodes: manifest.lineage.nodes,
145
- edges: manifest.lineage.edges,
146
- });
147
- }
148
- catch {
149
- const blocks = [];
150
- const metrics = [];
151
- const dimensions = [];
152
- const dirs = ['blocks', 'dashboards', 'workbooks'];
153
- for (const dir of dirs) {
154
- const dirPath = join(projectRoot, dir);
155
- if (!existsSync(dirPath))
156
- continue;
157
- for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
158
- if (!entry.isFile() || extname(entry.name) !== '.dql')
159
- continue;
160
- const filePath = join(dirPath, entry.name);
161
- try {
162
- const source = readFileSync(filePath, 'utf-8');
163
- const parser = new Parser(source, `${dir}/${entry.name}`);
164
- const ast = parser.parse();
165
- for (const stmt of ast.statements) {
166
- const block = stmt;
167
- if (block.kind !== 'BlockDecl')
168
- continue;
169
- blocks.push({
170
- name: block.name,
171
- sql: block.query?.rawSQL ?? '',
172
- domain: extractBlockProperty(block, 'domain'),
173
- owner: extractBlockProperty(block, 'owner'),
174
- status: extractBlockProperty(block, 'status'),
175
- blockType: block.blockType,
176
- metricRef: block.metricRef,
177
- chartType: extractVisualizationChart(block),
178
- });
179
- }
180
- }
181
- catch {
182
- // Skip unparseable files
183
- }
184
- }
185
- }
186
- const semanticDir = join(projectRoot, 'semantic-layer');
187
- if (existsSync(semanticDir)) {
188
- try {
189
- const layer = loadSemanticLayerFromDir(semanticDir);
190
- for (const metric of layer.listMetrics()) {
191
- metrics.push({
192
- name: metric.name,
193
- table: metric.table,
194
- domain: metric.domain,
195
- type: metric.type,
196
- });
197
- }
198
- for (const dim of layer.listDimensions()) {
199
- dimensions.push({
200
- name: dim.name,
201
- table: dim.table,
202
- });
203
- }
204
- }
205
- catch {
206
- // Non-fatal
207
- }
208
- }
209
- return buildLineageGraph(blocks, metrics, dimensions);
210
- }
211
- }
212
- function resolveDbtManifestPath(projectRoot) {
213
- const candidate = join(projectRoot, 'target', 'manifest.json');
214
- return existsSync(candidate) ? candidate : undefined;
215
- }
216
- /** Extract a property value from a block — checks direct AST fields first, then properties array. */
217
- function extractBlockProperty(block, propName) {
218
- // The parser puts well-known fields (domain, owner, type) directly on the AST node
219
- if (block[propName] !== undefined && block[propName] !== null) {
220
- return String(block[propName]);
221
- }
222
- // Fall back to properties array for custom/extended properties
223
- if (!block.properties)
224
- return undefined;
225
- for (const prop of block.properties) {
226
- if (prop.key === propName && prop.value?.kind === 'Literal') {
227
- return String(prop.value.value);
228
- }
229
- }
230
- return undefined;
231
- }
232
- /** Extract chart type from a block's visualization config. */
233
- function extractVisualizationChart(block) {
234
- if (!block.visualization)
235
- return undefined;
236
- for (const prop of block.visualization.properties ?? []) {
237
- if (prop.key === 'chart' && prop.value?.kind === 'Literal') {
238
- return String(prop.value.value);
239
- }
240
- }
241
- return undefined;
242
- }
243
- // ---- Output formatters ----
244
- function printSummary(graph, _flags) {
245
- const nodes = graph.getAllNodes();
246
- const edges = graph.getAllEdges();
247
- const domains = graph.getDomains();
248
- const sourceTables = graph.getNodesByType('source_table');
249
- const blocks = graph.getNodesByType('block');
250
- const metrics = graph.getNodesByType('metric');
251
- const dimensions = graph.getNodesByType('dimension');
252
- const charts = graph.getNodesByType('chart');
253
- console.log('\n DQL Lineage Summary');
254
- console.log(' ' + '='.repeat(50));
255
- // Overview counts
256
- console.log(`\n ${nodes.length} nodes, ${edges.length} edges, ${domains.length} domain(s)`);
257
- // Source Tables
258
- if (sourceTables.length > 0) {
259
- console.log(`\n Source Tables (${sourceTables.length}):`);
260
- for (const t of sourceTables.sort((a, b) => a.name.localeCompare(b.name))) {
261
- const downstream = graph.getOutgoingEdges(t.id);
262
- const targets = [...new Set(downstream.map((e) => graph.getNode(e.target)?.name).filter(Boolean))];
263
- const arrow = targets.length > 0 ? ` -> ${targets.join(', ')}` : '';
264
- console.log(` ${t.name}${arrow}`);
265
- }
266
- }
267
- // Blocks
268
- if (blocks.length > 0) {
269
- console.log(`\n Blocks (${blocks.length}):`);
270
- for (const b of blocks.sort((a, b) => a.name.localeCompare(b.name))) {
271
- const parts = [];
272
- if (b.domain)
273
- parts.push(`domain: ${b.domain}`);
274
- if (b.owner)
275
- parts.push(`owner: ${b.owner}`);
276
- if (b.status)
277
- parts.push(b.status);
278
- const meta = parts.length > 0 ? ` (${parts.join(', ')})` : '';
279
- console.log(` ${b.name}${meta}`);
280
- // Show what this block reads from (upstream) — deduplicate by node id
281
- const incoming = graph.getIncomingEdges(b.id);
282
- const upstreamNames = [...new Set(incoming
283
- .map((e) => graph.getNode(e.source))
284
- .filter((n) => n !== undefined && n.type !== 'domain')
285
- .map((n) => n.name))];
286
- if (upstreamNames.length > 0) {
287
- console.log(` reads from: ${upstreamNames.join(', ')}`);
288
- }
289
- // Show what this block feeds into (downstream) — deduplicate by node id
290
- const outgoing = graph.getOutgoingEdges(b.id);
291
- const downstreamNames = [...new Set(outgoing
292
- .map((e) => graph.getNode(e.target))
293
- .filter((n) => n !== undefined && n.type !== 'domain')
294
- .map((n) => n.name))];
295
- if (downstreamNames.length > 0) {
296
- console.log(` feeds into: ${downstreamNames.join(', ')}`);
297
- }
298
- }
299
- }
300
- // Metrics
301
- if (metrics.length > 0) {
302
- console.log(`\n Metrics (${metrics.length}):`);
303
- for (const m of metrics.sort((a, b) => a.name.localeCompare(b.name))) {
304
- const incoming = graph.getIncomingEdges(m.id);
305
- const sources = incoming.map((e) => graph.getNode(e.source)?.name).filter(Boolean);
306
- const from = sources.length > 0 ? ` <- ${sources.join(', ')}` : '';
307
- const domain = m.domain ? ` (${m.domain})` : '';
308
- console.log(` ${m.name}${domain}${from}`);
309
- }
310
- }
311
- // Dimensions
312
- if (dimensions.length > 0) {
313
- console.log(`\n Dimensions (${dimensions.length}):`);
314
- for (const d of dimensions.sort((a, b) => a.name.localeCompare(b.name))) {
315
- console.log(` ${d.name}`);
316
- }
317
- }
318
- // Charts
319
- if (charts.length > 0) {
320
- console.log(`\n Charts (${charts.length}):`);
321
- for (const c of charts.sort((a, b) => a.name.localeCompare(b.name))) {
322
- const incoming = graph.getIncomingEdges(c.id);
323
- const sources = incoming.map((e) => graph.getNode(e.source)?.name).filter(Boolean);
324
- const from = sources.length > 0 ? ` <- ${sources.join(', ')}` : '';
325
- console.log(` ${c.name}${from}`);
326
- }
327
- }
328
- // Shared Table Correlation — blocks reading the same tables
329
- const tableReaders = new Map();
330
- for (const t of sourceTables) {
331
- const downstream = graph.getOutgoingEdges(t.id)
332
- .map((e) => graph.getNode(e.target))
333
- .filter((n) => n !== undefined && n.type === 'block');
334
- if (downstream.length >= 2) {
335
- tableReaders.set(t.name, downstream.map((n) => n.name));
336
- }
337
- }
338
- if (tableReaders.size > 0) {
339
- console.log(`\n Shared Tables (${tableReaders.size}):`);
340
- for (const [table, readers] of [...tableReaders.entries()].sort()) {
341
- console.log(` ${table} <- ${readers.join(', ')}`);
342
- }
343
- }
344
- // Data Flow DAG
345
- console.log('\n Data Flow:');
346
- console.log(' ' + '-'.repeat(50));
347
- // Find root nodes (no incoming edges, excluding domain nodes)
348
- const roots = nodes.filter((n) => n.type !== 'domain' && (graph.getIncomingEdges(n.id).length === 0));
349
- const printed = new Set();
350
- for (const root of roots.sort((a, b) => a.name.localeCompare(b.name))) {
351
- printDAGNode(graph, root, 0, printed);
352
- }
353
- // Print any remaining nodes not reachable from roots (cycles or disconnected)
354
- for (const node of nodes) {
355
- if (!printed.has(node.id) && node.type !== 'domain') {
356
- printDAGNode(graph, node, 0, printed);
357
- }
358
- }
359
- // Cross-domain flows
360
- const flows = detectDomainFlows(graph);
361
- if (flows.length > 0) {
362
- console.log('\n Cross-Domain Flows:');
363
- for (const flow of flows) {
364
- console.log(` ${flow.from} -> ${flow.to} (${flow.edges.length} edge(s))`);
365
- }
366
- }
367
- // Domain trust overview
368
- if (domains.length > 0) {
369
- console.log('\n Domain Trust:');
370
- for (const domain of domains.sort()) {
371
- const overview = getDomainTrustOverview(graph, domain);
372
- if (overview.totalBlocks === 0)
373
- continue;
374
- const score = (overview.trustScore * 100).toFixed(0);
375
- console.log(` ${domain}: ${overview.certified}/${overview.totalBlocks} certified (${score}% trust)`);
376
- }
377
- }
378
- console.log('');
379
- }
380
- /** Recursively print DAG tree from a node. */
381
- function printDAGNode(graph, node, depth, printed) {
382
- if (printed.has(node.id))
383
- return;
384
- printed.add(node.id);
385
- const indent = ' ' + ' '.repeat(depth);
386
- const prefix = depth === 0 ? '' : '└── ';
387
- const typeLabel = node.type === 'source_table' ? 'table' : node.type;
388
- const badge = node.status === 'certified' ? ' ✓' : '';
389
- const domain = node.domain ? ` [${node.domain}]` : '';
390
- console.log(`${indent}${prefix}${typeLabel}:${node.name}${domain}${badge}`);
391
- // Get non-domain downstream
392
- const outgoing = graph.getOutgoingEdges(node.id);
393
- const children = outgoing
394
- .map((e) => graph.getNode(e.target))
395
- .filter((n) => n !== undefined && n.type !== 'domain' && !printed.has(n.id));
396
- for (const child of children) {
397
- printDAGNode(graph, child, depth + 1, printed);
398
- }
399
- }
400
- /**
401
- * Resolve a user-provided name to a node ID by trying multiple type prefixes.
402
- * Priority: block > source_table > metric > dimension > chart > exact match.
403
- */
404
- function resolveNodeId(graph, name) {
405
- // If the name already contains a colon, try it as-is
406
- if (name.includes(':') && graph.getNode(name))
407
- return name;
408
- // Try common type prefixes in priority order
409
- const prefixes = ['block', 'dashboard', 'dbt_model', 'dbt_source', 'table', 'metric', 'dimension', 'chart', 'domain'];
410
- for (const prefix of prefixes) {
411
- const id = `${prefix}:${name}`;
412
- if (graph.getNode(id))
413
- return id;
414
- }
415
- // Fuzzy match: search all nodes for a name match
416
- for (const node of graph.getAllNodes()) {
417
- if (node.name === name)
418
- return node.id;
419
- }
420
- return null;
421
- }
422
- function printSearchResults(graph, term) {
423
- const result = queryLineage(graph, { search: term });
424
- console.log(`\n Search: ${term}`);
425
- console.log(' ' + '='.repeat(50));
426
- if (!result.matches?.length) {
427
- console.log(' No lineage nodes matched.\n');
428
- return;
429
- }
430
- for (const match of result.matches) {
431
- const node = match.node;
432
- console.log(` ${node.id}${node.domain ? ` [${node.domain}]` : ''}`);
433
- }
434
- console.log('');
435
- }
436
- function printFocusedLineage(graph, focus) {
437
- const result = queryLineage(graph, { focus });
438
- if (!result.focalNode) {
439
- console.error(`"${focus}" not found in lineage graph.`);
440
- process.exitCode = 1;
441
- return;
442
- }
443
- console.log(`\n Focused Lineage: ${result.focalNode.name}`);
444
- console.log(' ' + '='.repeat(50));
445
- for (const node of result.graph.nodes.sort((a, b) => a.id.localeCompare(b.id))) {
446
- console.log(` ${node.id}${node.domain ? ` [${node.domain}]` : ''}`);
447
- }
448
- if (result.graph.edges.length > 0) {
449
- console.log('\n Edges:');
450
- for (const edge of result.graph.edges) {
451
- console.log(` ${edge.source} -${edge.type}-> ${edge.target}`);
452
- }
453
- }
454
- console.log('');
455
- }
456
- function printNodeLineage(graph, nodeId, _flags) {
457
- const node = graph.getNode(nodeId);
458
- if (!node) {
459
- console.error(`Node "${nodeId}" not found in lineage graph.`);
460
- process.exitCode = 1;
461
- return;
462
- }
463
- const typeLabel = node.type === 'source_table' ? 'Table' : node.type.charAt(0).toUpperCase() + node.type.slice(1);
464
- const ancestors = graph.ancestors(nodeId);
465
- const descendants = graph.descendants(nodeId);
466
- console.log(`\n ${typeLabel} Lineage: ${node.name}`);
467
- console.log(' ' + '='.repeat(50));
468
- // Metadata
469
- const meta = [];
470
- if (node.type !== 'source_table')
471
- meta.push(`Type: ${node.type}`);
472
- if (node.domain)
473
- meta.push(`Domain: ${node.domain}`);
474
- if (node.status)
475
- meta.push(`Status: ${node.status}`);
476
- if (node.owner)
477
- meta.push(`Owner: ${node.owner}`);
478
- if (meta.length > 0) {
479
- for (const m of meta)
480
- console.log(` ${m}`);
481
- }
482
- // Direct connections (immediate neighbors)
483
- const inEdges = graph.getIncomingEdges(nodeId);
484
- const outEdges = graph.getOutgoingEdges(nodeId);
485
- const directUpstream = [...new Set(inEdges
486
- .map((e) => graph.getNode(e.source))
487
- .filter((n) => n !== undefined && n.type !== 'domain'))];
488
- const directDownstream = [...new Set(outEdges
489
- .map((e) => graph.getNode(e.target))
490
- .filter((n) => n !== undefined && n.type !== 'domain'))];
491
- if (directUpstream.length > 0) {
492
- console.log(`\n Direct Upstream (${directUpstream.length}):`);
493
- for (const n of directUpstream) {
494
- const badge = n.status === 'certified' ? ' [certified]' : '';
495
- const typePfx = n.type === 'source_table' ? 'table' : n.type;
496
- console.log(` ${typePfx}:${n.name}${badge}${n.domain ? ` (${n.domain})` : ''}`);
497
- }
498
- }
499
- if (directDownstream.length > 0) {
500
- console.log(`\n Direct Downstream (${directDownstream.length}):`);
501
- for (const n of directDownstream) {
502
- const badge = n.status === 'certified' ? ' [certified]' : '';
503
- const typePfx = n.type === 'source_table' ? 'table' : n.type;
504
- console.log(` ${typePfx}:${n.name}${badge}${n.domain ? ` (${n.domain})` : ''}`);
505
- }
506
- }
507
- // Full transitive upstream/downstream (if different from direct)
508
- const transitiveUp = ancestors.filter((n) => n.type !== 'domain');
509
- const transitiveDown = descendants.filter((n) => n.type !== 'domain');
510
- if (transitiveUp.length > directUpstream.length) {
511
- console.log(`\n All Upstream (${transitiveUp.length}):`);
512
- for (const a of transitiveUp) {
513
- const badge = a.status === 'certified' ? ' [certified]' : '';
514
- const typePfx = a.type === 'source_table' ? 'table' : a.type;
515
- const direct = directUpstream.some((d) => d.id === a.id) ? ' *' : '';
516
- console.log(` ${typePfx}:${a.name}${badge}${a.domain ? ` (${a.domain})` : ''}${direct}`);
517
- }
518
- }
519
- if (transitiveDown.length > directDownstream.length) {
520
- console.log(`\n All Downstream (${transitiveDown.length}):`);
521
- for (const d of transitiveDown) {
522
- const badge = d.status === 'certified' ? ' [certified]' : '';
523
- const typePfx = d.type === 'source_table' ? 'table' : d.type;
524
- const direct = directDownstream.some((dd) => dd.id === d.id) ? ' *' : '';
525
- console.log(` ${typePfx}:${d.name}${badge}${d.domain ? ` (${d.domain})` : ''}${direct}`);
526
- }
527
- }
528
- // Data flow tree from this node
529
- if (transitiveDown.length > 0) {
530
- console.log('\n Data Flow:');
531
- console.log(' ' + '-'.repeat(50));
532
- const printed = new Set();
533
- printDAGNode(graph, node, 0, printed);
534
- }
535
- console.log('');
536
- }
537
- function printDomainLineage(graph, domain, _flags) {
538
- const nodes = graph.getNodesByDomain(domain);
539
- const overview = getDomainTrustOverview(graph, domain);
540
- const flows = detectDomainFlows(graph);
541
- console.log(`\n Domain Lineage: ${domain}`);
542
- console.log(' ' + '='.repeat(40));
543
- console.log(`\n Blocks: ${overview.totalBlocks}`);
544
- console.log(` Certified: ${overview.certified}`);
545
- console.log(` Trust Score: ${(overview.trustScore * 100).toFixed(0)}%`);
546
- console.log(`\n Nodes in domain (${nodes.length}):`);
547
- for (const node of nodes) {
548
- const badge = node.status === 'certified' ? ' [certified]' : '';
549
- console.log(` ${node.type}:${node.name}${badge}`);
550
- }
551
- const inFlows = flows.filter((f) => f.to === domain);
552
- const outFlows = flows.filter((f) => f.from === domain);
553
- if (inFlows.length > 0) {
554
- console.log('\n Data flows IN from:');
555
- for (const f of inFlows) {
556
- console.log(` ${f.from} (${f.edges.length} edge(s))`);
557
- }
558
- }
559
- if (outFlows.length > 0) {
560
- console.log('\n Data flows OUT to:');
561
- for (const f of outFlows) {
562
- console.log(` ${f.to} (${f.edges.length} edge(s))`);
563
- }
564
- }
565
- console.log('');
566
- }
567
- function printImpactAnalysis(graph, nodeId, _flags) {
568
- const node = graph.getNode(nodeId);
569
- if (!node) {
570
- console.error(`"${nodeId}" not found.`);
571
- process.exitCode = 1;
572
- return;
573
- }
574
- const impact = analyzeImpact(graph, nodeId);
575
- console.log(`\n Impact Analysis: ${node.name}`);
576
- console.log(' ' + '='.repeat(40));
577
- console.log(`\n Total downstream affected: ${impact.totalAffected}`);
578
- if (impact.domainImpacts.length > 0) {
579
- console.log('\n By domain:');
580
- for (const di of impact.domainImpacts) {
581
- console.log(` ${di.domain}: ${di.affectedNodes.length} node(s), ${di.certifiedBlocksAffected} certified`);
582
- for (const n of di.affectedNodes) {
583
- const badge = n.status === 'certified' ? ' [certified]' : '';
584
- console.log(` - ${n.name}${badge}`);
585
- }
586
- }
587
- }
588
- if (impact.domainCrossings.length > 0) {
589
- console.log('\n Domain boundaries crossed:');
590
- for (const dc of impact.domainCrossings) {
591
- console.log(` ${dc.from} -> ${dc.to} (${dc.edgeCount} edge(s))`);
592
- }
593
- }
594
- console.log('');
595
- }
596
- function printTrustChain(graph, fromBlock, toBlock, _flags) {
597
- const fromId = `block:${fromBlock}`;
598
- const toId = `block:${toBlock}`;
599
- if (!graph.getNode(fromId)) {
600
- console.error(`Block "${fromBlock}" not found.`);
601
- process.exitCode = 1;
602
- return;
603
- }
604
- if (!graph.getNode(toId)) {
605
- console.error(`Block "${toBlock}" not found.`);
606
- process.exitCode = 1;
607
- return;
608
- }
609
- const chain = buildTrustChain(graph, fromId, toId);
610
- if (!chain) {
611
- console.log(`\n No path found from "${fromBlock}" to "${toBlock}".`);
612
- return;
613
- }
614
- console.log(`\n Trust Chain: ${fromBlock} -> ${toBlock}`);
615
- console.log(' ' + '='.repeat(40));
616
- console.log(`\n Trust Score: ${(chain.trustScore * 100).toFixed(0)}% (${chain.certifiedCount}/${chain.nodes.length} certified)`);
617
- console.log('\n Chain:');
618
- for (let i = 0; i < chain.nodes.length; i++) {
619
- const n = chain.nodes[i];
620
- const icon = n.isTrustCheckpoint ? '[CERTIFIED]' : '[ ]';
621
- const domain = n.domain ? ` (${n.domain})` : '';
622
- const owner = n.owner ? ` — ${n.owner}` : '';
623
- const prefix = i === 0 ? ' ' : ' -> ';
624
- console.log(` ${prefix}${icon} ${n.name}${domain}${owner}`);
625
- }
626
- if (chain.domainCrossings.length > 0) {
627
- console.log('\n Domain boundaries:');
628
- for (const dc of chain.domainCrossings) {
629
- console.log(` ${dc.from} -> ${dc.to}`);
630
- }
631
- }
632
- console.log('');
633
- }
634
- //# sourceMappingURL=lineage.js.map