@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,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
+ }
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
+ }