@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.
- package/LICENSE +201 -0
- package/README.md +51 -0
- package/dist/analysis/analysis-snapshot.d.ts +145 -0
- package/dist/analysis/analysis-snapshot.js +615 -0
- package/dist/analysis/dependency-service.d.ts +56 -0
- package/dist/analysis/dependency-service.js +75 -0
- package/dist/analysis/execution-analyzer.d.ts +85 -0
- package/dist/analysis/execution-analyzer.js +245 -0
- package/dist/analysis/manifest-graph.d.ts +118 -0
- package/dist/analysis/manifest-graph.js +651 -0
- package/dist/analysis/run-results-search.d.ts +56 -0
- package/dist/analysis/run-results-search.js +127 -0
- package/dist/analysis/sql-analyzer.d.ts +30 -0
- package/dist/analysis/sql-analyzer.js +218 -0
- package/dist/browser.d.ts +11 -0
- package/dist/browser.js +17 -0
- package/dist/errors/error-handler.d.ts +26 -0
- package/dist/errors/error-handler.js +59 -0
- package/dist/formatting/field-filter.d.ts +29 -0
- package/dist/formatting/field-filter.js +112 -0
- package/dist/formatting/graph-export.d.ts +9 -0
- package/dist/formatting/graph-export.js +147 -0
- package/dist/formatting/output-formatter.d.ts +77 -0
- package/dist/formatting/output-formatter.js +160 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +38 -0
- package/dist/introspection/schema-generator.d.ts +29 -0
- package/dist/introspection/schema-generator.js +275 -0
- package/dist/io/artifact-loader.d.ts +27 -0
- package/dist/io/artifact-loader.js +142 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +2 -0
- package/dist/validation/input-validator.d.ts +39 -0
- package/dist/validation/input-validator.js +167 -0
- package/dist/version.d.ts +28 -0
- package/dist/version.js +60 -0
- package/package.json +47 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCommandSchema = getCommandSchema;
|
|
4
|
+
exports.getAllSchemas = getAllSchemas;
|
|
5
|
+
const ARG_MANIFEST_PATH = "manifest-path";
|
|
6
|
+
const OPT_TARGET_DIR = "--target-dir";
|
|
7
|
+
const OPT_JSON = "--json";
|
|
8
|
+
const OPT_NO_JSON = "--no-json";
|
|
9
|
+
const TYPE_STRING = "string";
|
|
10
|
+
const TYPE_BOOLEAN = "boolean";
|
|
11
|
+
const OUTPUT_JSON_OR_HUMAN = "json or human-readable";
|
|
12
|
+
const DESC_MANIFEST_PATH = "Path to manifest.json file (defaults to ./target/manifest.json)";
|
|
13
|
+
const DESC_TARGET_DIR = "Custom target directory (defaults to ./target)";
|
|
14
|
+
const DESC_FORCE_JSON = "Force JSON output";
|
|
15
|
+
const DESC_FORCE_HUMAN = "Force human-readable output";
|
|
16
|
+
const DESC_FIELDS = "Comma-separated list of fields to include in response (e.g., unique_id,name)";
|
|
17
|
+
const DESC_RUN_RESULTS_PATH = "Path to run_results.json file (defaults to ./target/run_results.json)";
|
|
18
|
+
const DESC_MANIFEST_OPTIONAL = "Path to manifest.json file (optional, for critical path analysis)";
|
|
19
|
+
function getSummarySchema() {
|
|
20
|
+
return {
|
|
21
|
+
command: "summary",
|
|
22
|
+
description: "Provide summary statistics for dbt manifest",
|
|
23
|
+
arguments: [
|
|
24
|
+
{
|
|
25
|
+
name: ARG_MANIFEST_PATH,
|
|
26
|
+
required: false,
|
|
27
|
+
description: DESC_MANIFEST_PATH,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
options: [
|
|
31
|
+
{
|
|
32
|
+
name: OPT_TARGET_DIR,
|
|
33
|
+
type: TYPE_STRING,
|
|
34
|
+
description: DESC_TARGET_DIR,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "--fields",
|
|
38
|
+
type: TYPE_STRING,
|
|
39
|
+
description: DESC_FIELDS,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: OPT_JSON,
|
|
43
|
+
type: TYPE_BOOLEAN,
|
|
44
|
+
description: DESC_FORCE_JSON,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: OPT_NO_JSON,
|
|
48
|
+
type: TYPE_BOOLEAN,
|
|
49
|
+
description: DESC_FORCE_HUMAN,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
output_format: OUTPUT_JSON_OR_HUMAN,
|
|
53
|
+
example: "dbt-tools summary",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function getGraphSchema() {
|
|
57
|
+
return {
|
|
58
|
+
command: "graph",
|
|
59
|
+
description: "Export dependency graph in various formats",
|
|
60
|
+
arguments: [
|
|
61
|
+
{
|
|
62
|
+
name: ARG_MANIFEST_PATH,
|
|
63
|
+
required: false,
|
|
64
|
+
description: DESC_MANIFEST_PATH,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
options: [
|
|
68
|
+
{
|
|
69
|
+
name: "--format",
|
|
70
|
+
type: "enum",
|
|
71
|
+
values: ["json", "dot", "gexf"],
|
|
72
|
+
default: "json",
|
|
73
|
+
description: "Export format",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "--output",
|
|
77
|
+
type: TYPE_STRING,
|
|
78
|
+
description: "Output file path (default: stdout)",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: OPT_TARGET_DIR,
|
|
82
|
+
type: TYPE_STRING,
|
|
83
|
+
description: DESC_TARGET_DIR,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "--fields",
|
|
87
|
+
type: TYPE_STRING,
|
|
88
|
+
description: DESC_FIELDS,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "--field-level",
|
|
92
|
+
type: TYPE_BOOLEAN,
|
|
93
|
+
description: "Include field-level (column-level) lineage",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "--catalog-path",
|
|
97
|
+
type: TYPE_STRING,
|
|
98
|
+
description: "Path to catalog.json file",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
output_format: "json, dot, or gexf",
|
|
102
|
+
example: "dbt-tools graph --format dot --output graph.dot",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function getRunReportSchema() {
|
|
106
|
+
return {
|
|
107
|
+
command: "run-report",
|
|
108
|
+
description: "Generate execution report from run_results.json",
|
|
109
|
+
arguments: [
|
|
110
|
+
{
|
|
111
|
+
name: "run-results-path",
|
|
112
|
+
required: false,
|
|
113
|
+
description: DESC_RUN_RESULTS_PATH,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: ARG_MANIFEST_PATH,
|
|
117
|
+
required: false,
|
|
118
|
+
description: DESC_MANIFEST_OPTIONAL,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
options: [
|
|
122
|
+
{
|
|
123
|
+
name: OPT_TARGET_DIR,
|
|
124
|
+
type: TYPE_STRING,
|
|
125
|
+
description: DESC_TARGET_DIR,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "--fields",
|
|
129
|
+
type: TYPE_STRING,
|
|
130
|
+
description: DESC_FIELDS,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "--bottlenecks",
|
|
134
|
+
type: TYPE_BOOLEAN,
|
|
135
|
+
description: "Include bottleneck section in report",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "--bottlenecks-top",
|
|
139
|
+
type: "number",
|
|
140
|
+
default: "10",
|
|
141
|
+
description: "Top N slowest nodes (default: 10 when --bottlenecks)",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "--bottlenecks-threshold",
|
|
145
|
+
type: "number",
|
|
146
|
+
description: "Nodes exceeding N seconds (alternative to top-N)",
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: OPT_JSON,
|
|
150
|
+
type: TYPE_BOOLEAN,
|
|
151
|
+
description: DESC_FORCE_JSON,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: OPT_NO_JSON,
|
|
155
|
+
type: TYPE_BOOLEAN,
|
|
156
|
+
description: DESC_FORCE_HUMAN,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
output_format: OUTPUT_JSON_OR_HUMAN,
|
|
160
|
+
example: "dbt-tools run-report --bottlenecks",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function getDepsSchemaOptions() {
|
|
164
|
+
return [
|
|
165
|
+
{
|
|
166
|
+
name: "--direction",
|
|
167
|
+
type: "enum",
|
|
168
|
+
values: ["upstream", "downstream"],
|
|
169
|
+
default: "downstream",
|
|
170
|
+
description: "Direction of dependency traversal",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "--manifest-path",
|
|
174
|
+
type: TYPE_STRING,
|
|
175
|
+
default: "./target/manifest.json",
|
|
176
|
+
description: "Path to manifest.json file",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: OPT_TARGET_DIR,
|
|
180
|
+
type: TYPE_STRING,
|
|
181
|
+
description: DESC_TARGET_DIR,
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "--fields",
|
|
185
|
+
type: TYPE_STRING,
|
|
186
|
+
description: DESC_FIELDS,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "--field",
|
|
190
|
+
type: TYPE_STRING,
|
|
191
|
+
description: "Specific field (column) to trace dependencies for",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "--catalog-path",
|
|
195
|
+
type: TYPE_STRING,
|
|
196
|
+
description: "Path to catalog.json file",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "--depth",
|
|
200
|
+
type: "number",
|
|
201
|
+
description: "Max traversal depth; 1 = immediate neighbors, omit for all levels",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "--format",
|
|
205
|
+
type: "enum",
|
|
206
|
+
values: ["flat", "tree"],
|
|
207
|
+
default: "tree",
|
|
208
|
+
description: "Output structure: flat list or nested tree",
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: "--build-order",
|
|
212
|
+
type: TYPE_BOOLEAN,
|
|
213
|
+
description: "Output upstream dependencies in topological build order (only with --direction upstream)",
|
|
214
|
+
},
|
|
215
|
+
{ name: OPT_JSON, type: TYPE_BOOLEAN, description: DESC_FORCE_JSON },
|
|
216
|
+
{ name: OPT_NO_JSON, type: TYPE_BOOLEAN, description: DESC_FORCE_HUMAN },
|
|
217
|
+
];
|
|
218
|
+
}
|
|
219
|
+
function getDepsSchema() {
|
|
220
|
+
return {
|
|
221
|
+
command: "deps",
|
|
222
|
+
description: "Get upstream or downstream dependencies for a dbt resource",
|
|
223
|
+
arguments: [
|
|
224
|
+
{
|
|
225
|
+
name: "resource-id",
|
|
226
|
+
required: true,
|
|
227
|
+
description: "Unique ID of the dbt resource (e.g., model.my_project.customers)",
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
options: getDepsSchemaOptions(),
|
|
231
|
+
output_format: OUTPUT_JSON_OR_HUMAN,
|
|
232
|
+
example: "dbt-tools deps model.my_project.customers --direction downstream",
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function getSchemaCommandSchema() {
|
|
236
|
+
return {
|
|
237
|
+
command: "schema",
|
|
238
|
+
description: "Get machine-readable schema for a command",
|
|
239
|
+
arguments: [
|
|
240
|
+
{
|
|
241
|
+
name: "command",
|
|
242
|
+
required: false,
|
|
243
|
+
description: "Command name (if omitted, returns all command schemas)",
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
options: [
|
|
247
|
+
{
|
|
248
|
+
name: OPT_JSON,
|
|
249
|
+
type: TYPE_BOOLEAN,
|
|
250
|
+
description: DESC_FORCE_JSON,
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
output_format: "json",
|
|
254
|
+
example: "dbt-tools schema deps",
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Get schema for a specific command
|
|
259
|
+
*/
|
|
260
|
+
function getCommandSchema(command) {
|
|
261
|
+
const schemas = getAllSchemas();
|
|
262
|
+
return schemas[command] || null;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Get all command schemas
|
|
266
|
+
*/
|
|
267
|
+
function getAllSchemas() {
|
|
268
|
+
return {
|
|
269
|
+
summary: getSummarySchema(),
|
|
270
|
+
graph: getGraphSchema(),
|
|
271
|
+
"run-report": getRunReportSchema(),
|
|
272
|
+
deps: getDepsSchema(),
|
|
273
|
+
schema: getSchemaCommandSchema(),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ParsedManifest } from "dbt-artifacts-parser/manifest";
|
|
2
|
+
import type { ParsedRunResults } from "dbt-artifacts-parser/run_results";
|
|
3
|
+
import type { ParsedCatalog } from "dbt-artifacts-parser/catalog";
|
|
4
|
+
/**
|
|
5
|
+
* Resolved artifact file paths
|
|
6
|
+
*/
|
|
7
|
+
export interface ArtifactPaths {
|
|
8
|
+
manifest: string;
|
|
9
|
+
runResults: string;
|
|
10
|
+
catalog?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve artifact paths, defaulting to ./target directory
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveArtifactPaths(manifestPath?: string, runResultsPath?: string, targetDir?: string, catalogPath?: string): ArtifactPaths;
|
|
16
|
+
/**
|
|
17
|
+
* Load and parse manifest.json file
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadManifest(manifestPath: string): ParsedManifest;
|
|
20
|
+
/**
|
|
21
|
+
* Load and parse run_results.json file
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadRunResults(runResultsPath: string): ParsedRunResults;
|
|
24
|
+
/**
|
|
25
|
+
* Load and parse catalog.json file
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadCatalog(catalogPath: string): ParsedCatalog;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resolveArtifactPaths = resolveArtifactPaths;
|
|
37
|
+
exports.loadManifest = loadManifest;
|
|
38
|
+
exports.loadRunResults = loadRunResults;
|
|
39
|
+
exports.loadCatalog = loadCatalog;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const manifest_1 = require("dbt-artifacts-parser/manifest");
|
|
43
|
+
const input_validator_1 = require("../validation/input-validator");
|
|
44
|
+
const run_results_1 = require("dbt-artifacts-parser/run_results");
|
|
45
|
+
const catalog_1 = require("dbt-artifacts-parser/catalog");
|
|
46
|
+
const DEFAULT_TARGET_DIR = "./target";
|
|
47
|
+
const MANIFEST_FILE = "manifest.json";
|
|
48
|
+
const RUN_RESULTS_FILE = "run_results.json";
|
|
49
|
+
const CATALOG_FILE = "catalog.json";
|
|
50
|
+
function resolveManifestPath(manifestPath, targetDir) {
|
|
51
|
+
if (manifestPath) {
|
|
52
|
+
if (manifestPath.endsWith(".json")) {
|
|
53
|
+
return (0, input_validator_1.resolveSafePath)(manifestPath);
|
|
54
|
+
}
|
|
55
|
+
return path.join((0, input_validator_1.resolveSafePath)(manifestPath), MANIFEST_FILE);
|
|
56
|
+
}
|
|
57
|
+
const effectiveTargetDir = targetDir || process.env.DBT_TARGET_DIR || DEFAULT_TARGET_DIR;
|
|
58
|
+
return path.join((0, input_validator_1.resolveSafePath)(effectiveTargetDir), MANIFEST_FILE);
|
|
59
|
+
}
|
|
60
|
+
function resolveRunResultsPath(runResultsPath, targetDir) {
|
|
61
|
+
if (runResultsPath) {
|
|
62
|
+
if (runResultsPath.endsWith(".json")) {
|
|
63
|
+
return (0, input_validator_1.resolveSafePath)(runResultsPath);
|
|
64
|
+
}
|
|
65
|
+
return path.join((0, input_validator_1.resolveSafePath)(runResultsPath), RUN_RESULTS_FILE);
|
|
66
|
+
}
|
|
67
|
+
const effectiveTargetDir = targetDir || process.env.DBT_TARGET_DIR || DEFAULT_TARGET_DIR;
|
|
68
|
+
return path.join((0, input_validator_1.resolveSafePath)(effectiveTargetDir), RUN_RESULTS_FILE);
|
|
69
|
+
}
|
|
70
|
+
function resolveCatalogPath(catalogPath, targetDir) {
|
|
71
|
+
if (catalogPath) {
|
|
72
|
+
if (catalogPath.endsWith(".json")) {
|
|
73
|
+
return (0, input_validator_1.resolveSafePath)(catalogPath);
|
|
74
|
+
}
|
|
75
|
+
return path.join((0, input_validator_1.resolveSafePath)(catalogPath), CATALOG_FILE);
|
|
76
|
+
}
|
|
77
|
+
const effectiveTargetDir = targetDir || process.env.DBT_TARGET_DIR || DEFAULT_TARGET_DIR;
|
|
78
|
+
return path.join((0, input_validator_1.resolveSafePath)(effectiveTargetDir), CATALOG_FILE);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve artifact paths, defaulting to ./target directory
|
|
82
|
+
*/
|
|
83
|
+
function resolveArtifactPaths(manifestPath, runResultsPath, targetDir, catalogPath) {
|
|
84
|
+
const effectiveTargetDir = targetDir || process.env.DBT_TARGET_DIR || DEFAULT_TARGET_DIR;
|
|
85
|
+
const resolved = {
|
|
86
|
+
manifest: resolveManifestPath(manifestPath, effectiveTargetDir),
|
|
87
|
+
runResults: resolveRunResultsPath(runResultsPath, effectiveTargetDir),
|
|
88
|
+
catalog: resolveCatalogPath(catalogPath, effectiveTargetDir),
|
|
89
|
+
};
|
|
90
|
+
return resolved;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Load and parse manifest.json file
|
|
94
|
+
*/
|
|
95
|
+
function loadManifest(manifestPath) {
|
|
96
|
+
const fullPath = (0, input_validator_1.resolveSafePath)(manifestPath);
|
|
97
|
+
if (!fs.existsSync(fullPath)) {
|
|
98
|
+
throw new Error(`Manifest file not found: ${fullPath}`);
|
|
99
|
+
}
|
|
100
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
101
|
+
try {
|
|
102
|
+
const manifestJson = JSON.parse(content);
|
|
103
|
+
return (0, manifest_1.parseManifest)(manifestJson);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
throw new Error(`Failed to parse manifest file ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Load and parse run_results.json file
|
|
111
|
+
*/
|
|
112
|
+
function loadRunResults(runResultsPath) {
|
|
113
|
+
const fullPath = (0, input_validator_1.resolveSafePath)(runResultsPath);
|
|
114
|
+
if (!fs.existsSync(fullPath)) {
|
|
115
|
+
throw new Error(`Run results file not found: ${fullPath}`);
|
|
116
|
+
}
|
|
117
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
118
|
+
try {
|
|
119
|
+
const runResultsJson = JSON.parse(content);
|
|
120
|
+
return (0, run_results_1.parseRunResults)(runResultsJson);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw new Error(`Failed to parse run results file ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Load and parse catalog.json file
|
|
128
|
+
*/
|
|
129
|
+
function loadCatalog(catalogPath) {
|
|
130
|
+
const fullPath = (0, input_validator_1.resolveSafePath)(catalogPath);
|
|
131
|
+
if (!fs.existsSync(fullPath)) {
|
|
132
|
+
throw new Error(`Catalog file not found: ${fullPath}`);
|
|
133
|
+
}
|
|
134
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
135
|
+
try {
|
|
136
|
+
const catalogJson = JSON.parse(content);
|
|
137
|
+
return (0, catalog_1.parseCatalog)(catalogJson);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
throw new Error(`Failed to parse catalog file ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource types in dbt manifests
|
|
3
|
+
*/
|
|
4
|
+
export type DbtResourceType = "model" | "source" | "seed" | "snapshot" | "test" | "analysis" | "macro" | "exposure" | "metric" | "semantic_model" | "unit_test" | "field" | "function";
|
|
5
|
+
/**
|
|
6
|
+
* Node attributes stored in the graph
|
|
7
|
+
*/
|
|
8
|
+
export interface GraphNodeAttributes {
|
|
9
|
+
unique_id: string;
|
|
10
|
+
resource_type: DbtResourceType;
|
|
11
|
+
name: string;
|
|
12
|
+
package_name: string;
|
|
13
|
+
path?: string;
|
|
14
|
+
original_file_path?: string;
|
|
15
|
+
tags?: string[];
|
|
16
|
+
description?: string;
|
|
17
|
+
parent_id?: string;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Edge attributes stored in the graph
|
|
22
|
+
*/
|
|
23
|
+
export interface GraphEdgeAttributes {
|
|
24
|
+
dependency_type: "node" | "macro" | "source" | "field" | "internal";
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Summary statistics about the graph
|
|
29
|
+
*/
|
|
30
|
+
export interface GraphSummary {
|
|
31
|
+
total_nodes: number;
|
|
32
|
+
nodes_by_type: Record<string, number>;
|
|
33
|
+
total_edges: number;
|
|
34
|
+
has_cycles: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Version information extracted from a manifest
|
|
38
|
+
*/
|
|
39
|
+
export interface VersionInfo {
|
|
40
|
+
schema_version: number | null;
|
|
41
|
+
dbt_version: string | null;
|
|
42
|
+
is_supported: boolean;
|
|
43
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate that a path does not contain traversal sequences
|
|
3
|
+
* Rejects paths like ../../.ssh or ..\\..\\etc
|
|
4
|
+
*/
|
|
5
|
+
export declare function validateSafePath(pathInput: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* Validate path and resolve to absolute. Use instead of path.resolve for user-controlled paths.
|
|
8
|
+
* Satisfies path-traversal audit: validation and resolve are coupled.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveSafePath(pathInput: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Ensure a resolved path is under a base directory (e.g. cwd).
|
|
13
|
+
* Call this after path.resolve() to prevent escaping the base via absolute paths or traversal.
|
|
14
|
+
*/
|
|
15
|
+
export declare function assertPathUnderBase(resolvedPath: string, baseDir: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Validate that input does not contain control characters
|
|
18
|
+
* Allows \n (0x0A), \r (0x0D), and \t (0x09)
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateNoControlChars(input: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Validate that input does not contain pre-encoded URL strings
|
|
23
|
+
* Rejects strings like %2e%2e (encoded ..)
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateNoPreEncoding(input: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Validate a dbt resource ID
|
|
28
|
+
* Rejects embedded query params, URL fragments, and pre-encoded strings
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateResourceId(id: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Validate optional depth for traversal (e.g. deps --depth).
|
|
33
|
+
* Rejects NaN, non-integers, and values < 1 when depth is provided.
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateDepth(depth: unknown): void;
|
|
36
|
+
/**
|
|
37
|
+
* Validate a general string input (combines multiple validations)
|
|
38
|
+
*/
|
|
39
|
+
export declare function validateString(input: string, allowEmpty?: boolean): void;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.validateSafePath = validateSafePath;
|
|
37
|
+
exports.resolveSafePath = resolveSafePath;
|
|
38
|
+
exports.assertPathUnderBase = assertPathUnderBase;
|
|
39
|
+
exports.validateNoControlChars = validateNoControlChars;
|
|
40
|
+
exports.validateNoPreEncoding = validateNoPreEncoding;
|
|
41
|
+
exports.validateResourceId = validateResourceId;
|
|
42
|
+
exports.validateDepth = validateDepth;
|
|
43
|
+
exports.validateString = validateString;
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
/**
|
|
46
|
+
* Validate that a path does not contain traversal sequences
|
|
47
|
+
* Rejects paths like ../../.ssh or ..\\..\\etc
|
|
48
|
+
*/
|
|
49
|
+
function validateSafePath(pathInput) {
|
|
50
|
+
if (!pathInput || typeof pathInput !== "string") {
|
|
51
|
+
throw new Error("Path must be a non-empty string");
|
|
52
|
+
}
|
|
53
|
+
// Check for path traversal patterns
|
|
54
|
+
const normalized = pathInput.replace(/\\/g, "/");
|
|
55
|
+
if (normalized.includes("../") ||
|
|
56
|
+
normalized.includes("..\\") ||
|
|
57
|
+
normalized.startsWith("../") ||
|
|
58
|
+
normalized.includes("/../") ||
|
|
59
|
+
normalized.includes("\\..\\")) {
|
|
60
|
+
throw new Error(`Path traversal detected in path: ${pathInput}. Paths must not contain ../ or ..\\`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate path and resolve to absolute. Use instead of path.resolve for user-controlled paths.
|
|
65
|
+
* Satisfies path-traversal audit: validation and resolve are coupled.
|
|
66
|
+
*/
|
|
67
|
+
function resolveSafePath(pathInput) {
|
|
68
|
+
validateSafePath(pathInput);
|
|
69
|
+
return path.resolve(pathInput);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Ensure a resolved path is under a base directory (e.g. cwd).
|
|
73
|
+
* Call this after path.resolve() to prevent escaping the base via absolute paths or traversal.
|
|
74
|
+
*/
|
|
75
|
+
function assertPathUnderBase(resolvedPath, baseDir) {
|
|
76
|
+
validateSafePath(resolvedPath);
|
|
77
|
+
const absPath = path.resolve(resolvedPath);
|
|
78
|
+
const baseAbs = path.resolve(baseDir);
|
|
79
|
+
const relative = path.relative(baseAbs, absPath);
|
|
80
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
81
|
+
throw new Error(`Path is outside allowed base: ${resolvedPath}. Paths must be under ${baseDir}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validate that input does not contain control characters
|
|
86
|
+
* Allows \n (0x0A), \r (0x0D), and \t (0x09)
|
|
87
|
+
*/
|
|
88
|
+
function validateNoControlChars(input) {
|
|
89
|
+
if (!input || typeof input !== "string") {
|
|
90
|
+
return; // Empty or non-string is fine
|
|
91
|
+
}
|
|
92
|
+
for (let i = 0; i < input.length; i++) {
|
|
93
|
+
const charCode = input.charCodeAt(i);
|
|
94
|
+
// Reject control characters (< 0x20) except \n, \r, \t
|
|
95
|
+
if (charCode < 0x20 &&
|
|
96
|
+
charCode !== 0x09 &&
|
|
97
|
+
charCode !== 0x0a &&
|
|
98
|
+
charCode !== 0x0d) {
|
|
99
|
+
throw new Error(`Control character detected at position ${i}: U+${charCode.toString(16).padStart(4, "0")}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Validate that input does not contain pre-encoded URL strings
|
|
105
|
+
* Rejects strings like %2e%2e (encoded ..)
|
|
106
|
+
*/
|
|
107
|
+
function validateNoPreEncoding(input) {
|
|
108
|
+
if (!input || typeof input !== "string") {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Check for URL encoding patterns
|
|
112
|
+
if (input.includes("%")) {
|
|
113
|
+
// Check for common encoded traversal patterns
|
|
114
|
+
const lowerInput = input.toLowerCase();
|
|
115
|
+
if (lowerInput.includes("%2e%2e") ||
|
|
116
|
+
lowerInput.includes("%2e%2e%2f") ||
|
|
117
|
+
lowerInput.includes("%2e%2e%5c")) {
|
|
118
|
+
throw new Error(`Pre-encoded path traversal detected: ${input}. Use the actual path, not an encoded version.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Validate a dbt resource ID
|
|
124
|
+
* Rejects embedded query params, URL fragments, and pre-encoded strings
|
|
125
|
+
*/
|
|
126
|
+
function validateResourceId(id) {
|
|
127
|
+
if (!id || typeof id !== "string") {
|
|
128
|
+
throw new Error("Resource ID must be a non-empty string");
|
|
129
|
+
}
|
|
130
|
+
// Reject embedded query params
|
|
131
|
+
if (id.includes("?") || id.includes("#")) {
|
|
132
|
+
throw new Error(`Resource ID contains invalid characters (?, #): ${id}`);
|
|
133
|
+
}
|
|
134
|
+
// Reject pre-encoded URLs (agents sometimes double-encode)
|
|
135
|
+
if (id.includes("%")) {
|
|
136
|
+
throw new Error(`Resource ID appears to be URL-encoded: ${id}. Use the actual resource ID, not an encoded version.`);
|
|
137
|
+
}
|
|
138
|
+
validateNoControlChars(id);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Validate optional depth for traversal (e.g. deps --depth).
|
|
142
|
+
* Rejects NaN, non-integers, and values < 1 when depth is provided.
|
|
143
|
+
*/
|
|
144
|
+
function validateDepth(depth) {
|
|
145
|
+
if (depth === undefined) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (typeof depth !== "number" ||
|
|
149
|
+
Number.isNaN(depth) ||
|
|
150
|
+
!Number.isInteger(depth) ||
|
|
151
|
+
depth < 1) {
|
|
152
|
+
throw new Error(`Invalid depth: ${depth}. Must be a positive integer`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Validate a general string input (combines multiple validations)
|
|
157
|
+
*/
|
|
158
|
+
function validateString(input, allowEmpty = false) {
|
|
159
|
+
if (!allowEmpty &&
|
|
160
|
+
(!input || typeof input !== "string" || input.trim().length === 0)) {
|
|
161
|
+
throw new Error("Input must be a non-empty string");
|
|
162
|
+
}
|
|
163
|
+
if (input) {
|
|
164
|
+
validateNoControlChars(input);
|
|
165
|
+
validateNoPreEncoding(input);
|
|
166
|
+
}
|
|
167
|
+
}
|