@claryai/cli 0.1.0
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 +25 -0
- package/README.md +197 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/ajv.d.ts +3 -0
- package/dist/ajv.d.ts.map +1 -0
- package/dist/ajv.js +13 -0
- package/dist/analytics/analytics.d.ts +370 -0
- package/dist/analytics/analytics.d.ts.map +1 -0
- package/dist/analytics/analytics.js +143 -0
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +134 -0
- package/dist/dbt/context.d.ts +14 -0
- package/dist/dbt/context.d.ts.map +1 -0
- package/dist/dbt/context.js +76 -0
- package/dist/dbt/context.test.d.ts +2 -0
- package/dist/dbt/context.test.d.ts.map +1 -0
- package/dist/dbt/context.test.js +152 -0
- package/dist/dbt/manifest.d.ts +7 -0
- package/dist/dbt/manifest.d.ts.map +1 -0
- package/dist/dbt/manifest.js +23 -0
- package/dist/dbt/models.d.ts +43 -0
- package/dist/dbt/models.d.ts.map +1 -0
- package/dist/dbt/models.js +256 -0
- package/dist/dbt/models.test.d.ts +2 -0
- package/dist/dbt/models.test.d.ts.map +1 -0
- package/dist/dbt/models.test.js +19 -0
- package/dist/dbt/profile.d.ts +9 -0
- package/dist/dbt/profile.d.ts.map +1 -0
- package/dist/dbt/profile.js +86 -0
- package/dist/dbt/profiles.test.d.ts +2 -0
- package/dist/dbt/profiles.test.d.ts.map +1 -0
- package/dist/dbt/profiles.test.js +50 -0
- package/dist/dbt/schema.d.ts +31 -0
- package/dist/dbt/schema.d.ts.map +1 -0
- package/dist/dbt/schema.js +49 -0
- package/dist/dbt/targets/Bigquery/index.d.ts +18 -0
- package/dist/dbt/targets/Bigquery/index.d.ts.map +1 -0
- package/dist/dbt/targets/Bigquery/index.js +105 -0
- package/dist/dbt/targets/Bigquery/oauth.d.ts +2 -0
- package/dist/dbt/targets/Bigquery/oauth.d.ts.map +1 -0
- package/dist/dbt/targets/Bigquery/oauth.js +43 -0
- package/dist/dbt/targets/Bigquery/serviceAccount.d.ts +35 -0
- package/dist/dbt/targets/Bigquery/serviceAccount.d.ts.map +1 -0
- package/dist/dbt/targets/Bigquery/serviceAccount.js +149 -0
- package/dist/dbt/targets/Databricks/oauth.d.ts +21 -0
- package/dist/dbt/targets/Databricks/oauth.d.ts.map +1 -0
- package/dist/dbt/targets/Databricks/oauth.js +184 -0
- package/dist/dbt/targets/athena.d.ts +21 -0
- package/dist/dbt/targets/athena.d.ts.map +1 -0
- package/dist/dbt/targets/athena.js +91 -0
- package/dist/dbt/targets/athena.test.d.ts +2 -0
- package/dist/dbt/targets/athena.test.d.ts.map +1 -0
- package/dist/dbt/targets/athena.test.js +60 -0
- package/dist/dbt/targets/clickhouse.d.ts +24 -0
- package/dist/dbt/targets/clickhouse.d.ts.map +1 -0
- package/dist/dbt/targets/clickhouse.js +90 -0
- package/dist/dbt/targets/databricks.d.ts +27 -0
- package/dist/dbt/targets/databricks.d.ts.map +1 -0
- package/dist/dbt/targets/databricks.js +138 -0
- package/dist/dbt/targets/duckdb.d.ts +16 -0
- package/dist/dbt/targets/duckdb.d.ts.map +1 -0
- package/dist/dbt/targets/duckdb.js +63 -0
- package/dist/dbt/targets/duckdb.test.d.ts +2 -0
- package/dist/dbt/targets/duckdb.test.d.ts.map +1 -0
- package/dist/dbt/targets/duckdb.test.js +37 -0
- package/dist/dbt/targets/postgres.d.ts +26 -0
- package/dist/dbt/targets/postgres.d.ts.map +1 -0
- package/dist/dbt/targets/postgres.js +142 -0
- package/dist/dbt/targets/redshift.d.ts +23 -0
- package/dist/dbt/targets/redshift.d.ts.map +1 -0
- package/dist/dbt/targets/redshift.js +96 -0
- package/dist/dbt/targets/snowflake.d.ts +4 -0
- package/dist/dbt/targets/snowflake.d.ts.map +1 -0
- package/dist/dbt/targets/snowflake.js +134 -0
- package/dist/dbt/targets/trino.d.ts +16 -0
- package/dist/dbt/targets/trino.d.ts.map +1 -0
- package/dist/dbt/targets/trino.js +65 -0
- package/dist/dbt/templating.d.ts +15 -0
- package/dist/dbt/templating.d.ts.map +1 -0
- package/dist/dbt/templating.js +50 -0
- package/dist/dbt/templating.test.d.ts +2 -0
- package/dist/dbt/templating.test.d.ts.map +1 -0
- package/dist/dbt/templating.test.js +51 -0
- package/dist/dbt/types.d.ts +17 -0
- package/dist/dbt/types.d.ts.map +1 -0
- package/dist/dbt/types.js +2 -0
- package/dist/dbt/validation.d.ts +9 -0
- package/dist/dbt/validation.d.ts.map +1 -0
- package/dist/dbt/validation.js +54 -0
- package/dist/env.d.ts +12 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +40 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +12 -0
- package/dist/globalState.d.ts +29 -0
- package/dist/globalState.d.ts.map +1 -0
- package/dist/globalState.js +67 -0
- package/dist/handlers/asyncQuery.d.ts +7 -0
- package/dist/handlers/asyncQuery.d.ts.map +1 -0
- package/dist/handlers/asyncQuery.js +50 -0
- package/dist/handlers/compile.d.ts +16 -0
- package/dist/handlers/compile.d.ts.map +1 -0
- package/dist/handlers/compile.js +277 -0
- package/dist/handlers/compile.test.d.ts +2 -0
- package/dist/handlers/compile.test.d.ts.map +1 -0
- package/dist/handlers/compile.test.js +201 -0
- package/dist/handlers/createProject.d.ts +37 -0
- package/dist/handlers/createProject.d.ts.map +1 -0
- package/dist/handlers/createProject.js +272 -0
- package/dist/handlers/dbt/apiClient.d.ts +14 -0
- package/dist/handlers/dbt/apiClient.d.ts.map +1 -0
- package/dist/handlers/dbt/apiClient.js +167 -0
- package/dist/handlers/dbt/compile.d.ts +35 -0
- package/dist/handlers/dbt/compile.d.ts.map +1 -0
- package/dist/handlers/dbt/compile.js +220 -0
- package/dist/handlers/dbt/getDbtProfileTargetName.d.ts +9 -0
- package/dist/handlers/dbt/getDbtProfileTargetName.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtProfileTargetName.js +44 -0
- package/dist/handlers/dbt/getDbtVersion.d.ts +16 -0
- package/dist/handlers/dbt/getDbtVersion.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtVersion.js +141 -0
- package/dist/handlers/dbt/getDbtVersion.mocks.d.ts +11 -0
- package/dist/handlers/dbt/getDbtVersion.mocks.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtVersion.mocks.js +70 -0
- package/dist/handlers/dbt/getDbtVersion.test.d.ts +2 -0
- package/dist/handlers/dbt/getDbtVersion.test.d.ts.map +1 -0
- package/dist/handlers/dbt/getDbtVersion.test.js +97 -0
- package/dist/handlers/dbt/getWarehouseClient.d.ts +24 -0
- package/dist/handlers/dbt/getWarehouseClient.d.ts.map +1 -0
- package/dist/handlers/dbt/getWarehouseClient.js +312 -0
- package/dist/handlers/dbt/refresh.d.ts +11 -0
- package/dist/handlers/dbt/refresh.d.ts.map +1 -0
- package/dist/handlers/dbt/refresh.js +114 -0
- package/dist/handlers/dbt/run.d.ts +14 -0
- package/dist/handlers/dbt/run.d.ts.map +1 -0
- package/dist/handlers/dbt/run.js +67 -0
- package/dist/handlers/deploy.d.ts +26 -0
- package/dist/handlers/deploy.d.ts.map +1 -0
- package/dist/handlers/deploy.js +377 -0
- package/dist/handlers/diagnostics.d.ts +11 -0
- package/dist/handlers/diagnostics.d.ts.map +1 -0
- package/dist/handlers/diagnostics.js +194 -0
- package/dist/handlers/download.d.ts +29 -0
- package/dist/handlers/download.d.ts.map +1 -0
- package/dist/handlers/download.js +955 -0
- package/dist/handlers/exportChartImage.d.ts +7 -0
- package/dist/handlers/exportChartImage.d.ts.map +1 -0
- package/dist/handlers/exportChartImage.js +33 -0
- package/dist/handlers/generate.d.ts +13 -0
- package/dist/handlers/generate.d.ts.map +1 -0
- package/dist/handlers/generate.js +159 -0
- package/dist/handlers/generateExposures.d.ts +8 -0
- package/dist/handlers/generateExposures.d.ts.map +1 -0
- package/dist/handlers/generateExposures.js +100 -0
- package/dist/handlers/getProject.d.ts +6 -0
- package/dist/handlers/getProject.d.ts.map +1 -0
- package/dist/handlers/getProject.js +43 -0
- package/dist/handlers/installSkills.d.ts +12 -0
- package/dist/handlers/installSkills.d.ts.map +1 -0
- package/dist/handlers/installSkills.js +321 -0
- package/dist/handlers/lint/ajvToSarif.d.ts +66 -0
- package/dist/handlers/lint/ajvToSarif.d.ts.map +1 -0
- package/dist/handlers/lint/ajvToSarif.js +222 -0
- package/dist/handlers/lint/sarifFormatter.d.ts +14 -0
- package/dist/handlers/lint/sarifFormatter.d.ts.map +1 -0
- package/dist/handlers/lint/sarifFormatter.js +111 -0
- package/dist/handlers/lint.d.ts +8 -0
- package/dist/handlers/lint.d.ts.map +1 -0
- package/dist/handlers/lint.js +308 -0
- package/dist/handlers/listProjects.d.ts +6 -0
- package/dist/handlers/listProjects.d.ts.map +1 -0
- package/dist/handlers/listProjects.js +53 -0
- package/dist/handlers/login/oauth.d.ts +2 -0
- package/dist/handlers/login/oauth.d.ts.map +1 -0
- package/dist/handlers/login/oauth.js +27 -0
- package/dist/handlers/login/pat.d.ts +2 -0
- package/dist/handlers/login/pat.d.ts.map +1 -0
- package/dist/handlers/login/pat.js +31 -0
- package/dist/handlers/login.d.ts +15 -0
- package/dist/handlers/login.d.ts.map +1 -0
- package/dist/handlers/login.js +239 -0
- package/dist/handlers/metadataFile.d.ts +9 -0
- package/dist/handlers/metadataFile.d.ts.map +1 -0
- package/dist/handlers/metadataFile.js +34 -0
- package/dist/handlers/oauthLogin.d.ts +6 -0
- package/dist/handlers/oauthLogin.d.ts.map +1 -0
- package/dist/handlers/oauthLogin.js +191 -0
- package/dist/handlers/preview.d.ts +29 -0
- package/dist/handlers/preview.d.ts.map +1 -0
- package/dist/handlers/preview.js +415 -0
- package/dist/handlers/renameHandler.d.ts +16 -0
- package/dist/handlers/renameHandler.d.ts.map +1 -0
- package/dist/handlers/renameHandler.js +160 -0
- package/dist/handlers/runChart.d.ts +10 -0
- package/dist/handlers/runChart.d.ts.map +1 -0
- package/dist/handlers/runChart.js +105 -0
- package/dist/handlers/selectProject.d.ts +20 -0
- package/dist/handlers/selectProject.d.ts.map +1 -0
- package/dist/handlers/selectProject.js +91 -0
- package/dist/handlers/setProject.d.ts +14 -0
- package/dist/handlers/setProject.d.ts.map +1 -0
- package/dist/handlers/setProject.js +131 -0
- package/dist/handlers/setWarehouse.d.ts +14 -0
- package/dist/handlers/setWarehouse.d.ts.map +1 -0
- package/dist/handlers/setWarehouse.js +94 -0
- package/dist/handlers/sql.d.ts +9 -0
- package/dist/handlers/sql.d.ts.map +1 -0
- package/dist/handlers/sql.js +89 -0
- package/dist/handlers/utils.d.ts +11 -0
- package/dist/handlers/utils.d.ts.map +1 -0
- package/dist/handlers/utils.js +36 -0
- package/dist/handlers/validate.d.ts +22 -0
- package/dist/handlers/validate.d.ts.map +1 -0
- package/dist/handlers/validate.js +201 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +581 -0
- package/dist/lightdash/loader.d.ts +21 -0
- package/dist/lightdash/loader.d.ts.map +1 -0
- package/dist/lightdash/loader.js +122 -0
- package/dist/lightdash/projectType.d.ts +84 -0
- package/dist/lightdash/projectType.d.ts.map +1 -0
- package/dist/lightdash/projectType.js +75 -0
- package/dist/lightdash-config/index.d.ts +2 -0
- package/dist/lightdash-config/index.d.ts.map +1 -0
- package/dist/lightdash-config/index.js +41 -0
- package/dist/lightdash-config/lightdash-config.test.d.ts +2 -0
- package/dist/lightdash-config/lightdash-config.test.d.ts.map +1 -0
- package/dist/lightdash-config/lightdash-config.test.js +70 -0
- package/dist/styles.d.ts +10 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +14 -0
- package/entitlements.plist +33 -0
- package/package.json +71 -0
- package/track.sh +116 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatSarifForCli = formatSarifForCli;
|
|
4
|
+
exports.getSarifSummary = getSarifSummary;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
8
|
+
const path = tslib_1.__importStar(require("path"));
|
|
9
|
+
/**
|
|
10
|
+
* Get a snippet of the file around the error location
|
|
11
|
+
*/
|
|
12
|
+
function getCodeSnippet(filePath, line, contextLines = 2) {
|
|
13
|
+
try {
|
|
14
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
15
|
+
const lines = content.split('\n');
|
|
16
|
+
const startLine = Math.max(0, line - 1 - contextLines);
|
|
17
|
+
const endLine = Math.min(lines.length, line + contextLines);
|
|
18
|
+
return lines
|
|
19
|
+
.slice(startLine, endLine)
|
|
20
|
+
.map((lineText, idx) => {
|
|
21
|
+
const lineNum = startLine + idx + 1;
|
|
22
|
+
const prefix = lineNum === line ? '→ ' : ' ';
|
|
23
|
+
return `${prefix}${lineNum
|
|
24
|
+
.toString()
|
|
25
|
+
.padStart(3, ' ')} | ${lineText}`;
|
|
26
|
+
})
|
|
27
|
+
.join('\n');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Format SARIF results for CLI output
|
|
35
|
+
*/
|
|
36
|
+
function formatSarifForCli(sarifLog, searchPath) {
|
|
37
|
+
const output = [];
|
|
38
|
+
if (!sarifLog.runs || sarifLog.runs.length === 0) {
|
|
39
|
+
return chalk_1.default.green('\n✓ All Clary Code files are valid!\n');
|
|
40
|
+
}
|
|
41
|
+
const run = sarifLog.runs[0];
|
|
42
|
+
const results = run.results || [];
|
|
43
|
+
if (results.length === 0) {
|
|
44
|
+
return chalk_1.default.green('\n✓ All Clary Code files are valid!\n');
|
|
45
|
+
}
|
|
46
|
+
// Group results by file
|
|
47
|
+
const resultsByFile = new Map();
|
|
48
|
+
for (const result of results) {
|
|
49
|
+
const location = result.locations?.[0];
|
|
50
|
+
const uri = location?.physicalLocation?.artifactLocation?.uri;
|
|
51
|
+
if (uri) {
|
|
52
|
+
if (!resultsByFile.has(uri)) {
|
|
53
|
+
resultsByFile.set(uri, []);
|
|
54
|
+
}
|
|
55
|
+
resultsByFile.get(uri).push(result);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Count stats
|
|
59
|
+
const totalFiles = resultsByFile.size;
|
|
60
|
+
output.push(chalk_1.default.red('\nValidation Errors:\n'));
|
|
61
|
+
// Format each file's errors
|
|
62
|
+
for (const [fileUri, fileResults] of resultsByFile.entries()) {
|
|
63
|
+
const relativePath = path.relative(searchPath, fileUri);
|
|
64
|
+
output.push(chalk_1.default.red.bold(`\n✗ ${relativePath}`));
|
|
65
|
+
for (const result of fileResults) {
|
|
66
|
+
const message = result.message.text;
|
|
67
|
+
const location = result.locations?.[0]?.physicalLocation;
|
|
68
|
+
const line = location?.region?.startLine;
|
|
69
|
+
const column = location?.region?.startColumn;
|
|
70
|
+
output.push(chalk_1.default.red(`\n ${message}${line ? ` (line ${line})` : ''}`));
|
|
71
|
+
// Add clickable file link for IDEs
|
|
72
|
+
if (line) {
|
|
73
|
+
// Encode the URI properly for file:// protocol to handle special characters (#, ?, &, spaces, etc.)
|
|
74
|
+
const encodedPath = encodeURI(fileUri);
|
|
75
|
+
const fileLink = `file://${encodedPath}:${line}${column ? `:${column}` : ''}`;
|
|
76
|
+
output.push(chalk_1.default.dim(` ${fileLink}`));
|
|
77
|
+
}
|
|
78
|
+
if (line && fs.existsSync(fileUri)) {
|
|
79
|
+
const snippet = getCodeSnippet(fileUri, line);
|
|
80
|
+
if (snippet) {
|
|
81
|
+
output.push(chalk_1.default.dim(`\n${snippet}\n`));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
output.push(chalk_1.default.red(`\nValidation failed for ${totalFiles} file${totalFiles > 1 ? 's' : ''}`));
|
|
87
|
+
return output.join('\n');
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get summary statistics from SARIF log
|
|
91
|
+
*/
|
|
92
|
+
function getSarifSummary(sarifLog) {
|
|
93
|
+
if (!sarifLog.runs || sarifLog.runs.length === 0) {
|
|
94
|
+
return { totalFiles: 0, totalErrors: 0, hasErrors: false };
|
|
95
|
+
}
|
|
96
|
+
const run = sarifLog.runs[0];
|
|
97
|
+
const results = run.results || [];
|
|
98
|
+
const uniqueFiles = new Set();
|
|
99
|
+
for (const result of results) {
|
|
100
|
+
const location = result.locations?.[0];
|
|
101
|
+
const uri = location?.physicalLocation?.artifactLocation?.uri;
|
|
102
|
+
if (uri) {
|
|
103
|
+
uniqueFiles.add(uri);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
totalFiles: uniqueFiles.size,
|
|
108
|
+
totalErrors: results.length,
|
|
109
|
+
hasErrors: results.length > 0,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/handlers/lint.ts"],"names":[],"mappings":"AAiBA,KAAK,WAAW,GAAG;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B,CAAC;AAsOF,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiJrE"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lintHandler = lintHandler;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@lightdash/common");
|
|
6
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
8
|
+
const path = tslib_1.__importStar(require("path"));
|
|
9
|
+
const uuid_1 = require("uuid");
|
|
10
|
+
const YAML = tslib_1.__importStar(require("yaml"));
|
|
11
|
+
const ajv_1 = require("../ajv");
|
|
12
|
+
const analytics_1 = require("../analytics/analytics");
|
|
13
|
+
const ajvToSarif_1 = require("./lint/ajvToSarif");
|
|
14
|
+
const sarifFormatter_1 = require("./lint/sarifFormatter");
|
|
15
|
+
const validateChartSchema = ajv_1.ajv.compile(common_1.chartAsCodeSchema);
|
|
16
|
+
const validateDashboardSchema = ajv_1.ajv.compile(common_1.dashboardAsCodeSchema);
|
|
17
|
+
const validateModelSchema = ajv_1.ajv.compile(common_1.modelAsCodeSchema);
|
|
18
|
+
/**
|
|
19
|
+
* Find all YAML and JSON files in a path (file or directory).
|
|
20
|
+
* If a file path is provided, returns it if it's a .yml/.yaml/.json file.
|
|
21
|
+
* If a directory path is provided, recursively searches for all such files.
|
|
22
|
+
*/
|
|
23
|
+
function findLightdashCodeFiles(inputPath) {
|
|
24
|
+
const files = [];
|
|
25
|
+
// Check if the path is a file or directory
|
|
26
|
+
const stats = fs.statSync(inputPath);
|
|
27
|
+
if (stats.isFile()) {
|
|
28
|
+
// Single file case - check if it's a valid extension
|
|
29
|
+
const isYaml = inputPath.endsWith('.yml') || inputPath.endsWith('.yaml');
|
|
30
|
+
const isJson = inputPath.endsWith('.json');
|
|
31
|
+
if (isYaml || isJson) {
|
|
32
|
+
files.push(inputPath);
|
|
33
|
+
}
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
// Directory case - walk recursively
|
|
37
|
+
function walk(currentPath) {
|
|
38
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
41
|
+
if (entry.isDirectory()) {
|
|
42
|
+
// Skip node_modules, .git, etc.
|
|
43
|
+
if (!entry.name.startsWith('.') &&
|
|
44
|
+
entry.name !== 'node_modules' &&
|
|
45
|
+
entry.name !== 'target') {
|
|
46
|
+
walk(fullPath);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (entry.isFile()) {
|
|
50
|
+
const isYaml = entry.name.endsWith('.yml') || entry.name.endsWith('.yaml');
|
|
51
|
+
const isJson = entry.name.endsWith('.json');
|
|
52
|
+
if (isYaml || isJson) {
|
|
53
|
+
files.push(fullPath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
walk(inputPath);
|
|
59
|
+
return files;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Build a map of JSON paths to their line/column positions in the source YAML/JSON file.
|
|
63
|
+
*
|
|
64
|
+
* This creates a Map<string, {line, column}> by traversing the YAML Abstract Syntax Tree (AST).
|
|
65
|
+
* For each YAML node encountered, we store its location keyed by its JSON path (e.g., '/metricQuery/filters').
|
|
66
|
+
*
|
|
67
|
+
* IMPORTANT: The map stores locations for ACTUAL YAML KEYS that exist in the file.
|
|
68
|
+
* It does NOT contain entries for:
|
|
69
|
+
* - Root path '/' (there's no root key in YAML)
|
|
70
|
+
* - Missing required properties that don't exist in the file
|
|
71
|
+
*
|
|
72
|
+
* @param fileContent - The raw YAML or JSON file content
|
|
73
|
+
* @param isJson - Whether the file is JSON (true) or YAML (false)
|
|
74
|
+
* @returns Object containing parsed data and the location map
|
|
75
|
+
*/
|
|
76
|
+
function buildLocationMap(fileContent, isJson) {
|
|
77
|
+
const locationMap = new Map();
|
|
78
|
+
if (isJson) {
|
|
79
|
+
// For JSON, parse normally (location map not populated - could be enhanced later)
|
|
80
|
+
const data = JSON.parse(fileContent);
|
|
81
|
+
return { data, locationMap };
|
|
82
|
+
}
|
|
83
|
+
// Parse YAML with the 'yaml' package to access the Abstract Syntax Tree (AST)
|
|
84
|
+
const doc = YAML.parseDocument(fileContent);
|
|
85
|
+
function traverse(node, jsonPath) {
|
|
86
|
+
if (!node)
|
|
87
|
+
return;
|
|
88
|
+
// Store location for this node
|
|
89
|
+
if (node.range) {
|
|
90
|
+
const [start] = node.range;
|
|
91
|
+
const lines = fileContent.substring(0, start).split('\n');
|
|
92
|
+
const line = lines.length;
|
|
93
|
+
const column = lines[lines.length - 1].length + 1;
|
|
94
|
+
locationMap.set(jsonPath, { line, column });
|
|
95
|
+
}
|
|
96
|
+
if (YAML.isMap(node)) {
|
|
97
|
+
for (const pair of node.items) {
|
|
98
|
+
if (YAML.isScalar(pair.key)) {
|
|
99
|
+
const key = String(pair.key.value);
|
|
100
|
+
const childPath = jsonPath
|
|
101
|
+
? `${jsonPath}/${key}`
|
|
102
|
+
: `/${key}`;
|
|
103
|
+
traverse(pair.value, childPath);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (YAML.isSeq(node)) {
|
|
108
|
+
for (let i = 0; i < node.items.length; i += 1) {
|
|
109
|
+
const childPath = `${jsonPath}/${i}`;
|
|
110
|
+
traverse(node.items[i], childPath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
traverse(doc.contents, '');
|
|
115
|
+
// Convert to plain JS object for validation
|
|
116
|
+
const data = doc.toJS();
|
|
117
|
+
return { data, locationMap };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Validate a single YAML or JSON file
|
|
121
|
+
*/
|
|
122
|
+
function validateFile(filePath) {
|
|
123
|
+
try {
|
|
124
|
+
// Read and parse file
|
|
125
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
126
|
+
const isJson = filePath.endsWith('.json');
|
|
127
|
+
const { data, locationMap } = buildLocationMap(fileContent, isJson);
|
|
128
|
+
if (!data || typeof data !== 'object') {
|
|
129
|
+
return {
|
|
130
|
+
filePath,
|
|
131
|
+
valid: false,
|
|
132
|
+
// Skip non-object files as they're not clary code
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const dataObj = data;
|
|
136
|
+
// Check if this is a model (has type: "model", "model/v1beta", or "model/v1")
|
|
137
|
+
if (dataObj.type === 'model' ||
|
|
138
|
+
dataObj.type === 'model/v1beta' ||
|
|
139
|
+
dataObj.type === 'model/v1') {
|
|
140
|
+
const valid = validateModelSchema(data);
|
|
141
|
+
if (!valid && validateModelSchema.errors) {
|
|
142
|
+
return {
|
|
143
|
+
filePath,
|
|
144
|
+
valid: false,
|
|
145
|
+
errors: validateModelSchema.errors,
|
|
146
|
+
fileContent,
|
|
147
|
+
locationMap,
|
|
148
|
+
type: 'model',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return { filePath, valid: true, type: 'model' };
|
|
152
|
+
}
|
|
153
|
+
// Check if this is a chart (has version and metricQuery)
|
|
154
|
+
if (dataObj.version === 1 && dataObj.metricQuery && !dataObj.tiles) {
|
|
155
|
+
const valid = validateChartSchema(data);
|
|
156
|
+
if (!valid && validateChartSchema.errors) {
|
|
157
|
+
return {
|
|
158
|
+
filePath,
|
|
159
|
+
valid: false,
|
|
160
|
+
errors: validateChartSchema.errors,
|
|
161
|
+
fileContent,
|
|
162
|
+
locationMap,
|
|
163
|
+
type: 'chart',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return { filePath, valid: true, type: 'chart' };
|
|
167
|
+
}
|
|
168
|
+
// Check if this is a dashboard (has version and tiles)
|
|
169
|
+
if (dataObj.version === 1 && dataObj.tiles) {
|
|
170
|
+
const valid = validateDashboardSchema(data);
|
|
171
|
+
if (!valid && validateDashboardSchema.errors) {
|
|
172
|
+
return {
|
|
173
|
+
filePath,
|
|
174
|
+
valid: false,
|
|
175
|
+
errors: validateDashboardSchema.errors,
|
|
176
|
+
fileContent,
|
|
177
|
+
locationMap,
|
|
178
|
+
type: 'dashboard',
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return { filePath, valid: true, type: 'dashboard' };
|
|
182
|
+
}
|
|
183
|
+
// Not a clary code file
|
|
184
|
+
return {
|
|
185
|
+
filePath,
|
|
186
|
+
valid: true, // Don't report non-lightdash files as errors
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
// Parsing error - skip this file
|
|
191
|
+
return {
|
|
192
|
+
filePath,
|
|
193
|
+
valid: false,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function lintHandler(options) {
|
|
198
|
+
const executionId = (0, uuid_1.v4)();
|
|
199
|
+
const startTime = Date.now();
|
|
200
|
+
const searchPath = path.resolve(options.path || process.cwd());
|
|
201
|
+
const outputFormat = options.format || 'cli';
|
|
202
|
+
let shouldExitWithError = false;
|
|
203
|
+
try {
|
|
204
|
+
// Check if path exists
|
|
205
|
+
if (!fs.existsSync(searchPath)) {
|
|
206
|
+
throw new Error(`Path does not exist: ${searchPath}`);
|
|
207
|
+
}
|
|
208
|
+
if (outputFormat === 'cli') {
|
|
209
|
+
console.log(chalk_1.default.dim(`Searching for Clary Code files in: ${searchPath}\n`));
|
|
210
|
+
}
|
|
211
|
+
// Find all YAML/JSON files
|
|
212
|
+
const codeFiles = findLightdashCodeFiles(searchPath);
|
|
213
|
+
const results = [];
|
|
214
|
+
if (codeFiles.length === 0) {
|
|
215
|
+
if (outputFormat === 'cli') {
|
|
216
|
+
console.log(chalk_1.default.yellow('No YAML/JSON files found in the specified path.'));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
if (options.verbose && outputFormat === 'cli') {
|
|
221
|
+
console.log(chalk_1.default.dim(`Found ${codeFiles.length} YAML/JSON files\n`));
|
|
222
|
+
}
|
|
223
|
+
// Validate each file
|
|
224
|
+
for (const file of codeFiles) {
|
|
225
|
+
const result = validateFile(file);
|
|
226
|
+
// Only track Clary Code files (models, charts, dashboards)
|
|
227
|
+
if (result.type) {
|
|
228
|
+
results.push(result);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (results.length === 0) {
|
|
232
|
+
if (outputFormat === 'cli') {
|
|
233
|
+
console.log(chalk_1.default.yellow('No Clary Code files found.'));
|
|
234
|
+
console.log(chalk_1.default.dim('Models must have type: model (or model/v1, model/v1beta), charts must have version: 1 + metricQuery, dashboards must have version: 1 + tiles'));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
// Convert to SARIF format
|
|
239
|
+
const invalidResults = results.filter((r) => !r.valid);
|
|
240
|
+
const validCount = results.length - invalidResults.length;
|
|
241
|
+
// Build SARIF report from invalid results
|
|
242
|
+
const sarifResults = invalidResults
|
|
243
|
+
.filter((r) => r.errors && r.fileContent && r.type)
|
|
244
|
+
.map((r) => ({
|
|
245
|
+
filePath: r.filePath,
|
|
246
|
+
errors: r.errors,
|
|
247
|
+
fileContent: r.fileContent,
|
|
248
|
+
locationMap: r.locationMap,
|
|
249
|
+
schemaType: r.type,
|
|
250
|
+
}));
|
|
251
|
+
const sarifLog = (0, ajvToSarif_1.createSarifReport)(sarifResults);
|
|
252
|
+
// Output based on format
|
|
253
|
+
if (outputFormat === 'json') {
|
|
254
|
+
console.log(JSON.stringify(sarifLog, null, 2));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// CLI format
|
|
258
|
+
const summary = (0, sarifFormatter_1.getSarifSummary)(sarifLog);
|
|
259
|
+
if (!summary.hasErrors) {
|
|
260
|
+
console.log(chalk_1.default.green('\n✓ All Clary Code files are valid!\n'));
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
// Show summary
|
|
264
|
+
console.log(chalk_1.default.bold(`\nValidated ${results.length} Clary Code files:`));
|
|
265
|
+
console.log(chalk_1.default.green(` ✓ ${validCount} valid`));
|
|
266
|
+
console.log(chalk_1.default.red(` ✗ ${summary.totalFiles} invalid`));
|
|
267
|
+
// Show formatted errors (starts with newline, so we don't need extra spacing)
|
|
268
|
+
console.log((0, sarifFormatter_1.formatSarifForCli)(sarifLog, searchPath));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (invalidResults.length > 0) {
|
|
272
|
+
shouldExitWithError = true;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const invalidCount = results.filter((r) => !r.valid).length;
|
|
277
|
+
await analytics_1.LightdashAnalytics.track({
|
|
278
|
+
event: 'lint.completed',
|
|
279
|
+
properties: {
|
|
280
|
+
executionId,
|
|
281
|
+
filesScanned: codeFiles.length,
|
|
282
|
+
lightdashFilesFound: results.length,
|
|
283
|
+
validFiles: results.length - invalidCount,
|
|
284
|
+
invalidFiles: invalidCount,
|
|
285
|
+
chartFiles: results.filter((r) => r.type === 'chart').length,
|
|
286
|
+
dashboardFiles: results.filter((r) => r.type === 'dashboard')
|
|
287
|
+
.length,
|
|
288
|
+
modelFiles: results.filter((r) => r.type === 'model').length,
|
|
289
|
+
outputFormat,
|
|
290
|
+
durationMs: Date.now() - startTime,
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
await analytics_1.LightdashAnalytics.track({
|
|
296
|
+
event: 'lint.error',
|
|
297
|
+
properties: {
|
|
298
|
+
executionId,
|
|
299
|
+
error: (0, common_1.getErrorMessage)(e),
|
|
300
|
+
errorCategory: (0, analytics_1.categorizeError)(e),
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
throw e;
|
|
304
|
+
}
|
|
305
|
+
if (shouldExitWithError) {
|
|
306
|
+
process.exit(2);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listProjects.d.ts","sourceRoot":"","sources":["../../src/handlers/listProjects.ts"],"names":[],"mappings":"AAMA,KAAK,mBAAmB,GAAG;IACvB,OAAO,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,SAAS,mBAAmB,kBAkDrE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listProjectsHandler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@lightdash/common");
|
|
6
|
+
const analytics_1 = require("../analytics/analytics");
|
|
7
|
+
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
8
|
+
const styles = tslib_1.__importStar(require("../styles"));
|
|
9
|
+
const apiClient_1 = require("./dbt/apiClient");
|
|
10
|
+
const listProjectsHandler = async (options) => {
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
let success = true;
|
|
13
|
+
globalState_1.default.setVerbose(options.verbose);
|
|
14
|
+
try {
|
|
15
|
+
const projects = await (0, apiClient_1.lightdashApi)({
|
|
16
|
+
method: 'GET',
|
|
17
|
+
url: `/api/v1/org/projects`,
|
|
18
|
+
body: undefined,
|
|
19
|
+
});
|
|
20
|
+
globalState_1.default.debug(`> List projects returned response: ${JSON.stringify(projects)}`);
|
|
21
|
+
// Filter out preview projects
|
|
22
|
+
const filteredProjects = projects.filter((project) => project.type !== common_1.ProjectType.PREVIEW);
|
|
23
|
+
if (filteredProjects.length === 0) {
|
|
24
|
+
console.error(styles.warning('No projects found.'));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.error(styles.bold(`\nProjects (${filteredProjects.length}):\n`));
|
|
28
|
+
filteredProjects.forEach((project) => {
|
|
29
|
+
console.error(` ${styles.bold(project.name)}`);
|
|
30
|
+
console.error(` UUID: ${project.projectUuid}`);
|
|
31
|
+
if (project.warehouseType) {
|
|
32
|
+
console.error(` Warehouse: ${project.warehouseType}`);
|
|
33
|
+
}
|
|
34
|
+
console.error('');
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
success = false;
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await analytics_1.LightdashAnalytics.track({
|
|
44
|
+
event: 'command.executed',
|
|
45
|
+
properties: {
|
|
46
|
+
command: 'list-projects',
|
|
47
|
+
durationMs: Date.now() - startTime,
|
|
48
|
+
success,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
exports.listProjectsHandler = listProjectsHandler;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../src/handlers/login/oauth.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,IAAI,CAc3D,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Helper function to open browser
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.openBrowser = void 0;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const util_1 = require("util");
|
|
8
|
+
const globalState_1 = tslib_1.__importDefault(require("../../globalState"));
|
|
9
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
10
|
+
const openBrowser = async (url) => {
|
|
11
|
+
try {
|
|
12
|
+
const { platform } = process;
|
|
13
|
+
if (platform === 'darwin') {
|
|
14
|
+
await execAsync(`open "${url}"`);
|
|
15
|
+
}
|
|
16
|
+
else if (platform === 'win32') {
|
|
17
|
+
await execAsync(`start "${url}"`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await execAsync(`xdg-open "${url}"`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
globalState_1.default.debug(`> Could not open browser automatically: ${error}`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
exports.openBrowser = openBrowser;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pat.d.ts","sourceRoot":"","sources":["../../../src/handlers/login/pat.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,2BAA2B,GACpC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,KAAK,MAAM,EACX,gBAAe,MAAY,KAC5B,OAAO,CAAC,MAAM,CAyBhB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generatePersonalAccessToken = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@lightdash/common");
|
|
6
|
+
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
7
|
+
const generatePersonalAccessToken = async (headers, url, hoursToExpire = 720) => {
|
|
8
|
+
const patUrl = new URL(`/api/v1/user/me/personal-access-tokens`, url).href;
|
|
9
|
+
const now = new Date();
|
|
10
|
+
const createToken = {
|
|
11
|
+
expiresAt: new Date(now.getTime() + hoursToExpire * 60 * 60 * 1000),
|
|
12
|
+
description: `Generated by OAuth on ${(0, common_1.formatDate)(now)}`,
|
|
13
|
+
autoGenerated: true,
|
|
14
|
+
};
|
|
15
|
+
const body = JSON.stringify(createToken);
|
|
16
|
+
const patResponse = await (0, node_fetch_1.default)(patUrl, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
body,
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
...headers,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
if (!patResponse.ok) {
|
|
25
|
+
throw new common_1.AuthorizationError(`Failed to create PAT: ${patResponse.status} ${patResponse.statusText}`);
|
|
26
|
+
}
|
|
27
|
+
const patResponseBody = await patResponse.json();
|
|
28
|
+
const { token } = patResponseBody.results;
|
|
29
|
+
return token;
|
|
30
|
+
};
|
|
31
|
+
exports.generatePersonalAccessToken = generatePersonalAccessToken;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type LoginOptions = {
|
|
2
|
+
/** Associated with a Personal Access Token or Service Account Token */
|
|
3
|
+
token?: string;
|
|
4
|
+
/** Email for password-based login */
|
|
5
|
+
email?: string;
|
|
6
|
+
/** Project UUID to select after login */
|
|
7
|
+
project?: string;
|
|
8
|
+
/** Port for the local OAuth callback server */
|
|
9
|
+
oauthPort?: number;
|
|
10
|
+
interactive?: boolean;
|
|
11
|
+
verbose: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare const login: (urlInput: string | undefined, options: LoginOptions) => Promise<void>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/handlers/login.ts"],"names":[],"mappings":"AAkBA,KAAK,YAAY,GAAG;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CACjB,CAAC;AAwJF,eAAO,MAAM,KAAK,GACjB,UAAU,MAAM,GAAG,SAAS,EAC5B,SAAS,YAAY,kBAiJrB,CAAC"}
|