@duckcodeailabs/dql-cli 1.5.2 → 1.6.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/dist/args.d.ts +6 -0
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +9 -0
- package/dist/args.js.map +1 -1
- package/dist/assets/dql-notebook/assets/index-B5jI3I8Q.js +869 -0
- package/dist/assets/dql-notebook/assets/index-cv-O4BEj.css +1 -0
- package/dist/assets/dql-notebook/index.html +2 -2
- package/dist/commands/certify.d.ts.map +1 -1
- package/dist/commands/certify.js +20 -0
- package/dist/commands/certify.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.d.ts +46 -0
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +147 -12
- package/dist/local-runtime.js.map +1 -1
- package/dist/local-runtime.test.js +61 -2
- package/dist/local-runtime.test.js.map +1 -1
- package/dist/package.json +44 -0
- package/dist/promote-from-draft.d.ts +40 -0
- package/dist/promote-from-draft.d.ts.map +1 -0
- package/dist/promote-from-draft.js +164 -0
- package/dist/promote-from-draft.js.map +1 -0
- package/dist/promote-from-draft.test.d.ts +2 -0
- package/dist/promote-from-draft.test.d.ts.map +1 -0
- package/dist/promote-from-draft.test.js +149 -0
- package/dist/promote-from-draft.test.js.map +1 -0
- package/package.json +12 -12
- package/dist/assets/dql-notebook/assets/index-BZX1UCr2.js +0 -863
- package/dist/assets/dql-notebook/assets/index-R3UrqjLQ.css +0 -1
package/dist/local-runtime.js
CHANGED
|
@@ -1043,7 +1043,7 @@ export async function startLocalServer(opts) {
|
|
|
1043
1043
|
if (req.method === 'POST' && path === '/api/blocks') {
|
|
1044
1044
|
try {
|
|
1045
1045
|
const body = await readJSON(req);
|
|
1046
|
-
const { name, domain, content, description, tags, metricRefs, template, } = body;
|
|
1046
|
+
const { name, domain, content, description, tags, metricRefs, template, blockType, } = body;
|
|
1047
1047
|
if (!name || typeof name !== 'string') {
|
|
1048
1048
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1049
1049
|
res.end(serializeJSON({ error: 'Missing block name' }));
|
|
@@ -1057,6 +1057,7 @@ export async function startLocalServer(opts) {
|
|
|
1057
1057
|
tags,
|
|
1058
1058
|
metricRefs,
|
|
1059
1059
|
template,
|
|
1060
|
+
blockType,
|
|
1060
1061
|
});
|
|
1061
1062
|
res.writeHead(201, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1062
1063
|
res.end(serializeJSON(created));
|
|
@@ -1637,6 +1638,18 @@ export async function startLocalServer(opts) {
|
|
|
1637
1638
|
}
|
|
1638
1639
|
return;
|
|
1639
1640
|
}
|
|
1641
|
+
if (req.method === 'GET' && path === '/api/block-studio/dbt-status') {
|
|
1642
|
+
try {
|
|
1643
|
+
const status = buildDbtStatus(projectRoot, projectConfig, semanticLastSyncTime);
|
|
1644
|
+
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1645
|
+
res.end(serializeJSON(status));
|
|
1646
|
+
}
|
|
1647
|
+
catch (error) {
|
|
1648
|
+
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
1649
|
+
res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
|
|
1650
|
+
}
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1640
1653
|
if (req.method === 'GET' && path === '/api/block-studio/open') {
|
|
1641
1654
|
try {
|
|
1642
1655
|
const relativePath = url.searchParams.get('path');
|
|
@@ -4466,17 +4479,25 @@ export function createBlockArtifacts(projectRoot, options) {
|
|
|
4466
4479
|
? listBlockTemplates().find((template) => template.id === options.template)?.content
|
|
4467
4480
|
: undefined;
|
|
4468
4481
|
const relativePath = safeDomain ? `blocks/${safeDomain}/${slug}.dql` : `blocks/${slug}.dql`;
|
|
4469
|
-
const fileContent = canonicalizeSafe(
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4482
|
+
const fileContent = canonicalizeSafe(options.blockType === 'semantic' && !options.content?.trim() && !templateContent
|
|
4483
|
+
? buildBlankSemanticBlockContent({
|
|
4484
|
+
name: options.name,
|
|
4485
|
+
domain: safeDomain || 'uncategorized',
|
|
4486
|
+
owner: options.owner,
|
|
4487
|
+
description: options.description,
|
|
4488
|
+
tags: options.tags,
|
|
4489
|
+
})
|
|
4490
|
+
: normalizeBlockStudioContent({
|
|
4491
|
+
name: options.name,
|
|
4492
|
+
domain: safeDomain || 'uncategorized',
|
|
4493
|
+
owner: options.owner,
|
|
4494
|
+
description: options.description,
|
|
4495
|
+
tags: options.tags,
|
|
4496
|
+
llmContext: options.llmContext,
|
|
4497
|
+
examples: options.examples,
|
|
4498
|
+
invariants: options.invariants,
|
|
4499
|
+
content: options.content?.trim() || templateContent,
|
|
4500
|
+
}));
|
|
4480
4501
|
writeFileSync(blockPath, fileContent, 'utf-8');
|
|
4481
4502
|
const companionPath = writeBlockCompanionFile(projectRoot, {
|
|
4482
4503
|
slug,
|
|
@@ -4777,6 +4798,25 @@ function buildBlankBlockContent(options) {
|
|
|
4777
4798
|
lines.push('}');
|
|
4778
4799
|
return lines.join('\n') + '\n';
|
|
4779
4800
|
}
|
|
4801
|
+
function buildBlankSemanticBlockContent(options) {
|
|
4802
|
+
const lines = [
|
|
4803
|
+
`block "${escapeDqlString(options.name)}" {`,
|
|
4804
|
+
` domain = "${escapeDqlString(options.domain)}"`,
|
|
4805
|
+
' type = "semantic"',
|
|
4806
|
+
' status = "draft"',
|
|
4807
|
+
` description = "${escapeDqlString(options.description?.trim() || options.name)}"`,
|
|
4808
|
+
` owner = "${escapeDqlString(options.owner?.trim() ?? '')}"`,
|
|
4809
|
+
` tags = [${(options.tags ?? []).map((tag) => `"${escapeDqlString(tag)}"`).join(', ')}]`,
|
|
4810
|
+
' metric = ""',
|
|
4811
|
+
' dimensions = []',
|
|
4812
|
+
'',
|
|
4813
|
+
' visualization {',
|
|
4814
|
+
' chart = "table"',
|
|
4815
|
+
' }',
|
|
4816
|
+
'}',
|
|
4817
|
+
];
|
|
4818
|
+
return lines.join('\n') + '\n';
|
|
4819
|
+
}
|
|
4780
4820
|
function parseYamlScalar(value) {
|
|
4781
4821
|
const trimmed = value.trim();
|
|
4782
4822
|
if (!trimmed)
|
|
@@ -4905,6 +4945,101 @@ function resolveDbtManifestPath(projectRoot) {
|
|
|
4905
4945
|
const candidate = join(projectRoot, 'target', 'manifest.json');
|
|
4906
4946
|
return existsSync(candidate) ? candidate : undefined;
|
|
4907
4947
|
}
|
|
4948
|
+
export function buildDbtStatus(projectRoot, projectConfig, lastSyncTime) {
|
|
4949
|
+
const configuredDbtDir = projectConfig.dbt?.projectDir
|
|
4950
|
+
? resolve(projectRoot, projectConfig.dbt.projectDir)
|
|
4951
|
+
: undefined;
|
|
4952
|
+
const semanticDbtDir = projectConfig.semanticLayer?.provider === 'dbt' && projectConfig.semanticLayer.projectPath
|
|
4953
|
+
? resolve(projectRoot, projectConfig.semanticLayer.projectPath)
|
|
4954
|
+
: undefined;
|
|
4955
|
+
const candidateDirs = [
|
|
4956
|
+
configuredDbtDir,
|
|
4957
|
+
semanticDbtDir,
|
|
4958
|
+
projectRoot,
|
|
4959
|
+
resolve(projectRoot, '..'),
|
|
4960
|
+
resolve(projectRoot, '../dbt'),
|
|
4961
|
+
resolve(projectRoot, '../../dbt'),
|
|
4962
|
+
].filter((value) => Boolean(value));
|
|
4963
|
+
const dbtProjectPath = candidateDirs.find((dir, index, list) => list.indexOf(dir) === index && existsSync(join(dir, 'dbt_project.yml'))) ?? configuredDbtDir ?? semanticDbtDir ?? projectRoot;
|
|
4964
|
+
const configuredManifest = projectConfig.dbt?.manifestPath ?? 'target/manifest.json';
|
|
4965
|
+
const manifestPath = resolve(dbtProjectPath, configuredManifest);
|
|
4966
|
+
const catalogPath = resolve(dbtProjectPath, 'target/catalog.json');
|
|
4967
|
+
const semanticManifestPath = resolve(dbtProjectPath, 'target/semantic_manifest.json');
|
|
4968
|
+
const runResultsPath = resolve(dbtProjectPath, 'target/run_results.json');
|
|
4969
|
+
const manifest = readJsonFile(manifestPath);
|
|
4970
|
+
const semanticManifest = readJsonFile(semanticManifestPath);
|
|
4971
|
+
const projectName = typeof manifest?.metadata?.project_name === 'string'
|
|
4972
|
+
? manifest.metadata.project_name
|
|
4973
|
+
: null;
|
|
4974
|
+
const nodes = manifest && typeof manifest === 'object' && manifest.nodes && typeof manifest.nodes === 'object'
|
|
4975
|
+
? Object.values(manifest.nodes)
|
|
4976
|
+
: [];
|
|
4977
|
+
const modelCount = nodes.filter((node) => node?.resource_type === 'model').length;
|
|
4978
|
+
const sourceCount = manifest?.sources && typeof manifest.sources === 'object'
|
|
4979
|
+
? Object.keys(manifest.sources).length
|
|
4980
|
+
: 0;
|
|
4981
|
+
const manifestMetricCount = manifest?.metrics && typeof manifest.metrics === 'object'
|
|
4982
|
+
? Object.keys(manifest.metrics).length
|
|
4983
|
+
: 0;
|
|
4984
|
+
const semanticMetricCount = Array.isArray(semanticManifest?.metrics)
|
|
4985
|
+
? semanticManifest.metrics.length
|
|
4986
|
+
: manifestMetricCount;
|
|
4987
|
+
const semanticModelCount = Array.isArray(semanticManifest?.semantic_models)
|
|
4988
|
+
? semanticManifest.semantic_models.length
|
|
4989
|
+
: 0;
|
|
4990
|
+
const savedQueryCount = Array.isArray(semanticManifest?.saved_queries)
|
|
4991
|
+
? semanticManifest.saved_queries.length
|
|
4992
|
+
: 0;
|
|
4993
|
+
const configured = existsSync(join(dbtProjectPath, 'dbt_project.yml')) || Boolean(configuredDbtDir || semanticDbtDir);
|
|
4994
|
+
const manifestExists = existsSync(manifestPath);
|
|
4995
|
+
const semanticExists = existsSync(semanticManifestPath);
|
|
4996
|
+
const setupHint = !configured
|
|
4997
|
+
? 'No dbt project detected. Start without dbt or run DQL from a repo with dbt_project.yml.'
|
|
4998
|
+
: !manifestExists
|
|
4999
|
+
? 'Run `dbt parse`, `dbt compile`, or `dbt build`, then run `dql sync dbt`.'
|
|
5000
|
+
: !semanticExists
|
|
5001
|
+
? 'dbt manifest is ready. Run `dbt parse` or `dbt build` if you use dbt Semantic Layer metrics.'
|
|
5002
|
+
: 'dbt artifacts are ready. Build SQL blocks from models or semantic blocks from metrics.';
|
|
5003
|
+
return {
|
|
5004
|
+
configured,
|
|
5005
|
+
provider: projectConfig.semanticLayer?.provider ?? null,
|
|
5006
|
+
projectPath: dbtProjectPath,
|
|
5007
|
+
projectName,
|
|
5008
|
+
artifacts: {
|
|
5009
|
+
manifest: describeArtifact(manifestPath, modelCount + sourceCount, manifest?.metadata?.generated_at),
|
|
5010
|
+
catalog: describeArtifact(catalogPath),
|
|
5011
|
+
semanticManifest: describeArtifact(semanticManifestPath, semanticMetricCount + semanticModelCount + savedQueryCount, semanticManifest?.metadata?.generated_at),
|
|
5012
|
+
runResults: describeArtifact(runResultsPath),
|
|
5013
|
+
},
|
|
5014
|
+
counts: {
|
|
5015
|
+
models: modelCount,
|
|
5016
|
+
sources: sourceCount,
|
|
5017
|
+
metrics: semanticMetricCount,
|
|
5018
|
+
semanticModels: semanticModelCount,
|
|
5019
|
+
savedQueries: savedQueryCount,
|
|
5020
|
+
},
|
|
5021
|
+
lastSyncTime,
|
|
5022
|
+
setupHint,
|
|
5023
|
+
};
|
|
5024
|
+
}
|
|
5025
|
+
function describeArtifact(path, count, generatedAt) {
|
|
5026
|
+
return {
|
|
5027
|
+
path,
|
|
5028
|
+
exists: existsSync(path),
|
|
5029
|
+
count,
|
|
5030
|
+
generatedAt: generatedAt ?? null,
|
|
5031
|
+
};
|
|
5032
|
+
}
|
|
5033
|
+
function readJsonFile(path) {
|
|
5034
|
+
if (!existsSync(path))
|
|
5035
|
+
return null;
|
|
5036
|
+
try {
|
|
5037
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
5038
|
+
}
|
|
5039
|
+
catch {
|
|
5040
|
+
return null;
|
|
5041
|
+
}
|
|
5042
|
+
}
|
|
4908
5043
|
function resolveLineageNode(graph, rawNodeId) {
|
|
4909
5044
|
if (graph.getNode(rawNodeId))
|
|
4910
5045
|
return graph.getNode(rawNodeId);
|