@dbt-tools/core 0.3.2

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 (37) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +51 -0
  3. package/dist/analysis/analysis-snapshot.d.ts +145 -0
  4. package/dist/analysis/analysis-snapshot.js +615 -0
  5. package/dist/analysis/dependency-service.d.ts +56 -0
  6. package/dist/analysis/dependency-service.js +75 -0
  7. package/dist/analysis/execution-analyzer.d.ts +85 -0
  8. package/dist/analysis/execution-analyzer.js +245 -0
  9. package/dist/analysis/manifest-graph.d.ts +118 -0
  10. package/dist/analysis/manifest-graph.js +651 -0
  11. package/dist/analysis/run-results-search.d.ts +56 -0
  12. package/dist/analysis/run-results-search.js +127 -0
  13. package/dist/analysis/sql-analyzer.d.ts +30 -0
  14. package/dist/analysis/sql-analyzer.js +218 -0
  15. package/dist/browser.d.ts +11 -0
  16. package/dist/browser.js +17 -0
  17. package/dist/errors/error-handler.d.ts +26 -0
  18. package/dist/errors/error-handler.js +59 -0
  19. package/dist/formatting/field-filter.d.ts +29 -0
  20. package/dist/formatting/field-filter.js +112 -0
  21. package/dist/formatting/graph-export.d.ts +9 -0
  22. package/dist/formatting/graph-export.js +147 -0
  23. package/dist/formatting/output-formatter.d.ts +77 -0
  24. package/dist/formatting/output-formatter.js +160 -0
  25. package/dist/index.d.ts +15 -0
  26. package/dist/index.js +38 -0
  27. package/dist/introspection/schema-generator.d.ts +29 -0
  28. package/dist/introspection/schema-generator.js +275 -0
  29. package/dist/io/artifact-loader.d.ts +27 -0
  30. package/dist/io/artifact-loader.js +142 -0
  31. package/dist/types.d.ts +43 -0
  32. package/dist/types.js +2 -0
  33. package/dist/validation/input-validator.d.ts +39 -0
  34. package/dist/validation/input-validator.js +167 -0
  35. package/dist/version.d.ts +28 -0
  36. package/dist/version.js +60 -0
  37. package/package.json +47 -0
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DependencyService = void 0;
4
+ const field_filter_1 = require("../formatting/field-filter");
5
+ /**
6
+ * DependencyService wraps ManifestGraph dependency methods with formatting
7
+ * and field filtering capabilities.
8
+ */
9
+ class DependencyService {
10
+ /**
11
+ * Get dependencies for a resource with optional field filtering, depth limit, output format, and build order.
12
+ * @param depth - Optional max traversal depth; 1 = immediate neighbors, undefined = all levels
13
+ * @param format - Output structure: flat list or nested tree
14
+ * @param buildOrder - When true and direction is upstream, return dependencies in topological build order
15
+ */
16
+ static getDependencies(graph, resourceId, direction, fields, depth, format, buildOrder) {
17
+ if (format === "tree" && !(direction === "upstream" && buildOrder)) {
18
+ return this.getDependenciesTree(graph, resourceId, direction, fields, depth);
19
+ }
20
+ // Flat format (or tree with buildOrder: use flat + build order)
21
+ const dependencyEntries = direction === "upstream" && buildOrder
22
+ ? graph.getUpstreamBuildOrder(resourceId, depth)
23
+ : direction === "upstream"
24
+ ? graph.getUpstream(resourceId, depth)
25
+ : graph.getDownstream(resourceId, depth);
26
+ const graphologyGraph = graph.getGraph();
27
+ const dependencies = dependencyEntries.map(({ nodeId, depth: depDepth }) => {
28
+ const attributes = graphologyGraph.getNodeAttributes(nodeId);
29
+ return Object.assign(Object.assign({}, attributes), { unique_id: nodeId, resource_type: attributes.resource_type || "unknown", name: attributes.name || nodeId, package_name: attributes.package_name || "", depth: depDepth });
30
+ });
31
+ let filteredDependencies = dependencies;
32
+ if (fields) {
33
+ filteredDependencies = field_filter_1.FieldFilter.filterArrayFields(dependencies, fields);
34
+ }
35
+ return Object.assign(Object.assign({ resource_id: resourceId, direction }, (buildOrder && direction === "upstream" && { build_order: true })), { dependencies: filteredDependencies, count: filteredDependencies.length });
36
+ }
37
+ /**
38
+ * Build nested tree from BFS entries with parent tracking
39
+ */
40
+ static getDependenciesTree(graph, resourceId, direction, fields, depth) {
41
+ var _a, _b;
42
+ const entries = direction === "upstream"
43
+ ? graph.getUpstreamWithParents(resourceId, depth)
44
+ : graph.getDownstreamWithParents(resourceId, depth);
45
+ const graphologyGraph = graph.getGraph();
46
+ // Build parent -> children map (parentId is the BFS predecessor)
47
+ const childrenByParent = new Map();
48
+ for (const { nodeId, depth: depDepth, parentId } of entries) {
49
+ const existing = (_a = childrenByParent.get(parentId)) !== null && _a !== void 0 ? _a : [];
50
+ existing.push({ nodeId, depth: depDepth });
51
+ childrenByParent.set(parentId, existing);
52
+ }
53
+ const buildNode = (nodeId, depDepth) => {
54
+ var _a;
55
+ const attributes = graphologyGraph.getNodeAttributes(nodeId);
56
+ const childEntries = (_a = childrenByParent.get(nodeId)) !== null && _a !== void 0 ? _a : [];
57
+ const childNodes = childEntries.map(({ nodeId: cId, depth: cDepth }) => buildNode(cId, cDepth));
58
+ const node = Object.assign(Object.assign({}, attributes), { unique_id: nodeId, resource_type: attributes.resource_type || "unknown", name: attributes.name || nodeId, package_name: attributes.package_name || "", depth: depDepth, dependencies: childNodes });
59
+ if (fields) {
60
+ const filtered = field_filter_1.FieldFilter.filterFields(node, fields);
61
+ return Object.assign(Object.assign({}, filtered), { dependencies: node.dependencies });
62
+ }
63
+ return node;
64
+ };
65
+ const rootChildren = (_b = childrenByParent.get(resourceId)) !== null && _b !== void 0 ? _b : [];
66
+ const dependencyTrees = rootChildren.map(({ nodeId, depth: d }) => buildNode(nodeId, d));
67
+ return {
68
+ resource_id: resourceId,
69
+ direction,
70
+ dependencies: dependencyTrees,
71
+ count: entries.length,
72
+ };
73
+ }
74
+ }
75
+ exports.DependencyService = DependencyService;
@@ -0,0 +1,85 @@
1
+ import type { ParsedRunResults } from "dbt-artifacts-parser/run_results";
2
+ import type { ManifestGraph } from "./manifest-graph";
3
+ /**
4
+ * Execution timing information for a single node
5
+ */
6
+ export interface NodeExecution {
7
+ unique_id: string;
8
+ status: string;
9
+ execution_time: number;
10
+ started_at?: string;
11
+ completed_at?: string;
12
+ thread_id?: string;
13
+ }
14
+ /**
15
+ * Critical path analysis result
16
+ */
17
+ export interface CriticalPath {
18
+ path: string[];
19
+ total_time: number;
20
+ }
21
+ /**
22
+ * Execution summary statistics
23
+ */
24
+ export interface ExecutionSummary {
25
+ total_execution_time: number;
26
+ total_nodes: number;
27
+ nodes_by_status: Record<string, number>;
28
+ critical_path?: CriticalPath;
29
+ node_executions: NodeExecution[];
30
+ }
31
+ /**
32
+ * ExecutionAnalyzer processes run_results.json to extract timing information
33
+ * and correlate it with the dependency graph.
34
+ */
35
+ export declare class ExecutionAnalyzer {
36
+ private runResults;
37
+ private graph;
38
+ constructor(runResults: ParsedRunResults, graph: ManifestGraph);
39
+ /**
40
+ * Get execution summary with statistics
41
+ */
42
+ getSummary(): ExecutionSummary;
43
+ /**
44
+ * Extract execution information for each node
45
+ */
46
+ getNodeExecutions(): NodeExecution[];
47
+ /**
48
+ * Calculate the critical path (longest path through the dependency graph)
49
+ */
50
+ calculateCriticalPath(nodeExecutions: NodeExecution[]): CriticalPath | undefined;
51
+ /**
52
+ * Find the longest path from a node to root (nodes with no dependencies)
53
+ */
54
+ private findLongestPathToRoot;
55
+ /**
56
+ * Calculate total execution time for a path
57
+ */
58
+ private calculatePathTime;
59
+ /**
60
+ * Returns the absolute epoch-ms timestamp of the earliest executed node,
61
+ * or null if no timing data is available. Useful for converting relative
62
+ * Gantt offsets to wall-clock timestamps.
63
+ */
64
+ getRunStartedAt(): number | null;
65
+ private parseTimingInterval;
66
+ /**
67
+ * Wall-clock span and optional compile/execute intervals (epoch ms).
68
+ */
69
+ private wallClockFromResult;
70
+ /**
71
+ * Get Gantt chart data for visualization
72
+ */
73
+ getGanttData(): Array<{
74
+ unique_id: string;
75
+ name: string;
76
+ start: number;
77
+ end: number;
78
+ duration: number;
79
+ status: string;
80
+ compileStart: number | null;
81
+ compileEnd: number | null;
82
+ executeStart: number | null;
83
+ executeEnd: number | null;
84
+ }>;
85
+ }
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExecutionAnalyzer = void 0;
4
+ /**
5
+ * ExecutionAnalyzer processes run_results.json to extract timing information
6
+ * and correlate it with the dependency graph.
7
+ */
8
+ class ExecutionAnalyzer {
9
+ constructor(runResults, graph) {
10
+ this.runResults = runResults;
11
+ this.graph = graph;
12
+ }
13
+ /**
14
+ * Get execution summary with statistics
15
+ */
16
+ getSummary() {
17
+ const nodeExecutions = this.getNodeExecutions();
18
+ const nodesByStatus = {};
19
+ let totalExecutionTime = 0;
20
+ for (const execution of nodeExecutions) {
21
+ // Count by status
22
+ const status = execution.status || "unknown";
23
+ nodesByStatus[status] = (nodesByStatus[status] || 0) + 1;
24
+ // Sum execution time
25
+ totalExecutionTime += execution.execution_time || 0;
26
+ }
27
+ // Calculate critical path
28
+ const criticalPath = this.calculateCriticalPath(nodeExecutions);
29
+ return {
30
+ total_execution_time: totalExecutionTime,
31
+ total_nodes: nodeExecutions.length,
32
+ nodes_by_status: nodesByStatus,
33
+ critical_path: criticalPath,
34
+ node_executions: nodeExecutions,
35
+ };
36
+ }
37
+ /**
38
+ * Extract execution information for each node
39
+ */
40
+ getNodeExecutions() {
41
+ if (!this.runResults.results || !Array.isArray(this.runResults.results)) {
42
+ return [];
43
+ }
44
+ const results = this.runResults.results;
45
+ return results.map((result) => {
46
+ var _a, _b, _c;
47
+ // Find execute timing (most relevant for execution time)
48
+ const timingArray = result.timing || [];
49
+ const executeTiming = timingArray.find((t) => t.name === "execute");
50
+ const compileTiming = timingArray.find((t) => t.name === "compile");
51
+ // Use execute timing if available, otherwise fall back to compile
52
+ const timing = executeTiming || compileTiming || timingArray[0];
53
+ return {
54
+ unique_id: result.unique_id || "",
55
+ status: result.status || "unknown",
56
+ execution_time: result.execution_time || 0,
57
+ started_at: (_a = timing === null || timing === void 0 ? void 0 : timing.started_at) !== null && _a !== void 0 ? _a : undefined,
58
+ completed_at: (_b = timing === null || timing === void 0 ? void 0 : timing.completed_at) !== null && _b !== void 0 ? _b : undefined,
59
+ thread_id: (_c = result.thread_id) !== null && _c !== void 0 ? _c : undefined,
60
+ };
61
+ });
62
+ }
63
+ /**
64
+ * Calculate the critical path (longest path through the dependency graph)
65
+ */
66
+ calculateCriticalPath(nodeExecutions) {
67
+ // Create a map of node executions by unique_id
68
+ const executionMap = new Map();
69
+ for (const exec of nodeExecutions) {
70
+ executionMap.set(exec.unique_id, exec);
71
+ }
72
+ // Find all leaf nodes (nodes with no downstream dependents)
73
+ const leafNodes = [];
74
+ const graph = this.graph.getGraph();
75
+ graph.forEachNode((nodeId) => {
76
+ const outboundNeighbors = graph.outboundNeighbors(nodeId);
77
+ if (outboundNeighbors.length === 0 && executionMap.has(nodeId)) {
78
+ leafNodes.push(nodeId);
79
+ }
80
+ });
81
+ if (leafNodes.length === 0) {
82
+ return undefined;
83
+ }
84
+ // For each leaf node, find the longest path from root
85
+ let maxPath = [];
86
+ let maxTime = 0;
87
+ for (const leafNode of leafNodes) {
88
+ const path = this.findLongestPathToRoot(leafNode, executionMap);
89
+ const pathTime = this.calculatePathTime(path, executionMap);
90
+ if (pathTime > maxTime) {
91
+ maxTime = pathTime;
92
+ maxPath = path;
93
+ }
94
+ }
95
+ if (maxPath.length === 0) {
96
+ return undefined;
97
+ }
98
+ return {
99
+ path: maxPath,
100
+ total_time: maxTime,
101
+ };
102
+ }
103
+ /**
104
+ * Find the longest path from a node to root (nodes with no dependencies)
105
+ */
106
+ findLongestPathToRoot(startNode, executionMap) {
107
+ const graph = this.graph.getGraph();
108
+ const visited = new Set();
109
+ let longestPath = [];
110
+ const dfs = (currentNode, currentPath) => {
111
+ if (visited.has(currentNode)) {
112
+ return;
113
+ }
114
+ visited.add(currentNode);
115
+ const newPath = [...currentPath, currentNode];
116
+ // Update longest path if this is longer
117
+ if (newPath.length > longestPath.length) {
118
+ longestPath = newPath;
119
+ }
120
+ // Traverse upstream (inbound neighbors)
121
+ const inboundNeighbors = graph.inboundNeighbors(currentNode);
122
+ for (const neighbor of inboundNeighbors) {
123
+ if (executionMap.has(neighbor)) {
124
+ dfs(neighbor, newPath);
125
+ }
126
+ }
127
+ visited.delete(currentNode);
128
+ };
129
+ dfs(startNode, []);
130
+ return longestPath.reverse(); // Reverse to get root-to-leaf order
131
+ }
132
+ /**
133
+ * Calculate total execution time for a path
134
+ */
135
+ calculatePathTime(path, executionMap) {
136
+ let totalTime = 0;
137
+ for (const nodeId of path) {
138
+ const exec = executionMap.get(nodeId);
139
+ if (exec) {
140
+ totalTime += exec.execution_time || 0;
141
+ }
142
+ }
143
+ return totalTime;
144
+ }
145
+ /**
146
+ * Returns the absolute epoch-ms timestamp of the earliest executed node,
147
+ * or null if no timing data is available. Useful for converting relative
148
+ * Gantt offsets to wall-clock timestamps.
149
+ */
150
+ getRunStartedAt() {
151
+ const executions = this.getNodeExecutions();
152
+ const timestamps = executions
153
+ .map((exec) => exec.started_at ? new Date(exec.started_at).getTime() : null)
154
+ .filter((t) => t !== null);
155
+ if (timestamps.length === 0)
156
+ return null;
157
+ return Math.min(...timestamps);
158
+ }
159
+ parseTimingInterval(timing) {
160
+ if (!timing)
161
+ return null;
162
+ const started = timing.started_at;
163
+ const completed = timing.completed_at;
164
+ if (!started || !completed)
165
+ return null;
166
+ const startMs = new Date(started).getTime();
167
+ const endMs = new Date(completed).getTime();
168
+ if (Number.isNaN(startMs) || Number.isNaN(endMs))
169
+ return null;
170
+ return { start: startMs, end: endMs };
171
+ }
172
+ /**
173
+ * Wall-clock span and optional compile/execute intervals (epoch ms).
174
+ */
175
+ wallClockFromResult(result) {
176
+ const timingArray = result.timing || [];
177
+ const executeTiming = timingArray.find((t) => t.name === "execute");
178
+ const compileTiming = timingArray.find((t) => t.name === "compile");
179
+ const compile = this.parseTimingInterval(compileTiming);
180
+ const execute = this.parseTimingInterval(executeTiming);
181
+ const starts = [];
182
+ const ends = [];
183
+ if (compile) {
184
+ starts.push(compile.start);
185
+ ends.push(compile.end);
186
+ }
187
+ if (execute) {
188
+ starts.push(execute.start);
189
+ ends.push(execute.end);
190
+ }
191
+ if (starts.length === 0)
192
+ return null;
193
+ return {
194
+ wallStart: Math.min(...starts),
195
+ wallEnd: Math.max(...ends),
196
+ compile,
197
+ execute,
198
+ };
199
+ }
200
+ /**
201
+ * Get Gantt chart data for visualization
202
+ */
203
+ getGanttData() {
204
+ if (!this.runResults.results || !Array.isArray(this.runResults.results)) {
205
+ return [];
206
+ }
207
+ const graphologyGraph = this.graph.getGraph();
208
+ const rows = [];
209
+ for (const result of this.runResults
210
+ .results) {
211
+ const uniqueId = result.unique_id || "";
212
+ if (!uniqueId)
213
+ continue;
214
+ const wall = this.wallClockFromResult(result);
215
+ if (!wall)
216
+ continue;
217
+ const nodeAttributes = graphologyGraph.hasNode(uniqueId)
218
+ ? graphologyGraph.getNodeAttributes(uniqueId)
219
+ : undefined;
220
+ const name = (nodeAttributes === null || nodeAttributes === void 0 ? void 0 : nodeAttributes.name) || uniqueId;
221
+ const status = result.status || "unknown";
222
+ rows.push({ unique_id: uniqueId, name, status, wall });
223
+ }
224
+ if (rows.length === 0)
225
+ return [];
226
+ const minStart = Math.min(...rows.map((r) => r.wall.wallStart));
227
+ return rows.map((row) => {
228
+ const { wall } = row;
229
+ const rel = (t) => t - minStart;
230
+ return {
231
+ unique_id: row.unique_id,
232
+ name: row.name,
233
+ start: rel(wall.wallStart),
234
+ end: rel(wall.wallEnd),
235
+ duration: wall.wallEnd - wall.wallStart,
236
+ status: row.status,
237
+ compileStart: wall.compile ? rel(wall.compile.start) : null,
238
+ compileEnd: wall.compile ? rel(wall.compile.end) : null,
239
+ executeStart: wall.execute ? rel(wall.execute.start) : null,
240
+ executeEnd: wall.execute ? rel(wall.execute.end) : null,
241
+ };
242
+ });
243
+ }
244
+ }
245
+ exports.ExecutionAnalyzer = ExecutionAnalyzer;
@@ -0,0 +1,118 @@
1
+ import { DirectedGraph } from "graphology";
2
+ import type { ParsedManifest } from "dbt-artifacts-parser/manifest";
3
+ import type { ParsedCatalog } from "dbt-artifacts-parser/catalog";
4
+ import type { GraphNodeAttributes, GraphEdgeAttributes, GraphSummary } from "../types";
5
+ import type { ColumnDependencyMap } from "./sql-analyzer";
6
+ /**
7
+ * ManifestGraph builds and manages a directed graph from a dbt manifest.
8
+ *
9
+ * This class transforms dbt artifacts into a graphology graph, enabling
10
+ * efficient graph operations like cycle detection, path finding, and traversal.
11
+ */
12
+ export declare class ManifestGraph {
13
+ private graph;
14
+ private relationMap;
15
+ constructor(manifest: ParsedManifest);
16
+ /**
17
+ * Build the graph from manifest data
18
+ */
19
+ private buildGraph;
20
+ /**
21
+ * Add nodes from manifest to the graph
22
+ */
23
+ /** Map relation_name (lowercase) to unique_id when present on manifest entries. */
24
+ private registerRelationName;
25
+ private addNodes;
26
+ private addNodeEntries;
27
+ private addSourceEntries;
28
+ private addMacroEntries;
29
+ private addExposureEntries;
30
+ private addMetricEntries;
31
+ private addSemanticModelEntries;
32
+ private addUnitTestEntries;
33
+ /**
34
+ * Add edges based on dependencies
35
+ */
36
+ private addEdges;
37
+ private addEdgesFromParentMap;
38
+ private addEdgesFromDependsOn;
39
+ private addEdgesFromNodeDependsOn;
40
+ private addEdgesFromExposureDependsOn;
41
+ private addEdgesFromMetricDependsOn;
42
+ /**
43
+ * Extract resource type from manifest node
44
+ */
45
+ private extractResourceType;
46
+ /**
47
+ * Infer dependency type from node ID
48
+ */
49
+ private inferDependencyType;
50
+ /**
51
+ * Get the underlying graphology graph
52
+ */
53
+ getGraph(): DirectedGraph<GraphNodeAttributes, GraphEdgeAttributes>;
54
+ /**
55
+ * Get summary statistics about the graph
56
+ */
57
+ getSummary(): GraphSummary;
58
+ /**
59
+ * Get all upstream dependencies of a node using BFS.
60
+ * @param nodeId - The node to find upstream dependencies for
61
+ * @param maxDepth - Optional limit; 1 = immediate neighbors only, undefined = all levels
62
+ * @returns Array of { nodeId, depth } where depth is the shortest distance from the node
63
+ */
64
+ getUpstream(nodeId: string, maxDepth?: number): Array<{
65
+ nodeId: string;
66
+ depth: number;
67
+ }>;
68
+ /**
69
+ * Get all downstream dependents of a node using BFS.
70
+ * @param nodeId - The node to find downstream dependents for
71
+ * @param maxDepth - Optional limit; 1 = immediate neighbors only, undefined = all levels
72
+ * @returns Array of { nodeId, depth } where depth is the shortest distance from the node
73
+ */
74
+ getDownstream(nodeId: string, maxDepth?: number): Array<{
75
+ nodeId: string;
76
+ depth: number;
77
+ }>;
78
+ /**
79
+ * Get upstream dependencies with parent info for tree construction.
80
+ * @returns Array of { nodeId, depth, parentId } where parentId is the BFS predecessor
81
+ */
82
+ getUpstreamWithParents(nodeId: string, maxDepth?: number): Array<{
83
+ nodeId: string;
84
+ depth: number;
85
+ parentId: string;
86
+ }>;
87
+ /**
88
+ * Get upstream dependencies in build order (topological sort).
89
+ * Sources and root models first, then models that depend on them.
90
+ * @param nodeId - The node to find upstream dependencies for
91
+ * @param maxDepth - Optional limit; 1 = immediate neighbors only, undefined = all levels
92
+ * @returns Array of { nodeId, depth } in build order
93
+ */
94
+ getUpstreamBuildOrder(nodeId: string, maxDepth?: number): Array<{
95
+ nodeId: string;
96
+ depth: number;
97
+ }>;
98
+ /**
99
+ * Get downstream dependents with parent info for tree construction.
100
+ * @returns Array of { nodeId, depth, parentId } where parentId is the BFS predecessor
101
+ */
102
+ getDownstreamWithParents(nodeId: string, maxDepth?: number): Array<{
103
+ nodeId: string;
104
+ depth: number;
105
+ parentId: string;
106
+ }>;
107
+ /**
108
+ * Add field nodes from catalog metadata
109
+ */
110
+ addFieldNodes(catalog: ParsedCatalog): void;
111
+ private processCatalogColumns;
112
+ /**
113
+ * Add field-to-field edges based on SQL analysis
114
+ */
115
+ addFieldEdges(childNodeId: string, dependencies: ColumnDependencyMap): void;
116
+ private ensureFieldNode;
117
+ private resolveRelationToUniqueId;
118
+ }