@duckcodeailabs/dql-core 1.6.34 → 1.7.1
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/apps/app-document.d.ts +3 -1
- package/dist/apps/app-document.d.ts.map +1 -1
- package/dist/apps/app-document.js +26 -11
- package/dist/apps/app-document.js.map +1 -1
- package/dist/apps/dashboard-document.d.ts +74 -0
- package/dist/apps/dashboard-document.d.ts.map +1 -1
- package/dist/apps/dashboard-document.js +101 -2
- package/dist/apps/dashboard-document.js.map +1 -1
- package/dist/apps/index.d.ts +3 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -0
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/product-domain-context.d.ts +23 -0
- package/dist/apps/product-domain-context.d.ts.map +1 -0
- package/dist/apps/product-domain-context.js +25 -0
- package/dist/apps/product-domain-context.js.map +1 -0
- package/dist/artifacts/dql-artifact.d.ts +13 -0
- package/dist/artifacts/dql-artifact.d.ts.map +1 -1
- package/dist/artifacts/dql-artifact.js +69 -0
- package/dist/artifacts/dql-artifact.js.map +1 -1
- package/dist/artifacts/index.d.ts +1 -1
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/ast/index.d.ts +1 -1
- package/dist/ast/index.d.ts.map +1 -1
- package/dist/ast/index.js.map +1 -1
- package/dist/ast/nodes.d.ts +10 -1
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/ast/printer.js +4 -0
- package/dist/ast/printer.js.map +1 -1
- package/dist/blocks/index.d.ts +2 -0
- package/dist/blocks/index.d.ts.map +1 -0
- package/dist/blocks/index.js +2 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/parameters.d.ts +55 -0
- package/dist/blocks/parameters.d.ts.map +1 -0
- package/dist/blocks/parameters.js +160 -0
- package/dist/blocks/parameters.js.map +1 -0
- package/dist/format/diff.d.ts.map +1 -1
- package/dist/format/diff.js +19 -11
- package/dist/format/diff.js.map +1 -1
- package/dist/format/notebook.d.ts +1 -1
- package/dist/format/notebook.js +2 -2
- package/dist/formatter/formatter.js +23 -1
- package/dist/formatter/formatter.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/manifest/builder.d.ts +17 -0
- package/dist/manifest/builder.d.ts.map +1 -1
- package/dist/manifest/builder.js +143 -9
- package/dist/manifest/builder.js.map +1 -1
- package/dist/manifest/datalex-migration.d.ts +68 -0
- package/dist/manifest/datalex-migration.d.ts.map +1 -0
- package/dist/manifest/datalex-migration.js +369 -0
- package/dist/manifest/datalex-migration.js.map +1 -0
- package/dist/manifest/dbt-first-authoring.d.ts +210 -0
- package/dist/manifest/dbt-first-authoring.d.ts.map +1 -0
- package/dist/manifest/dbt-first-authoring.js +510 -0
- package/dist/manifest/dbt-first-authoring.js.map +1 -0
- package/dist/manifest/dbt-first-modeling.d.ts +34 -0
- package/dist/manifest/dbt-first-modeling.d.ts.map +1 -0
- package/dist/manifest/dbt-first-modeling.js +931 -0
- package/dist/manifest/dbt-first-modeling.js.map +1 -0
- package/dist/manifest/domain-discovery.d.ts +98 -0
- package/dist/manifest/domain-discovery.d.ts.map +1 -0
- package/dist/manifest/domain-discovery.js +503 -0
- package/dist/manifest/domain-discovery.js.map +1 -0
- package/dist/manifest/domain-package-registry.d.ts +20 -0
- package/dist/manifest/domain-package-registry.d.ts.map +1 -0
- package/dist/manifest/domain-package-registry.js +265 -0
- package/dist/manifest/domain-package-registry.js.map +1 -0
- package/dist/manifest/domain-writer.d.ts +4 -0
- package/dist/manifest/domain-writer.d.ts.map +1 -1
- package/dist/manifest/domain-writer.js +3 -0
- package/dist/manifest/domain-writer.js.map +1 -1
- package/dist/manifest/index.d.ts +7 -1
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +6 -0
- package/dist/manifest/index.js.map +1 -1
- package/dist/manifest/project-snapshot.d.ts +30 -0
- package/dist/manifest/project-snapshot.d.ts.map +1 -0
- package/dist/manifest/project-snapshot.js +68 -0
- package/dist/manifest/project-snapshot.js.map +1 -0
- package/dist/manifest/types.d.ts +289 -3
- package/dist/manifest/types.d.ts.map +1 -1
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +40 -6
- package/dist/parser/parser.js.map +1 -1
- package/dist/semantic/analyzer.js +1 -1
- package/dist/semantic/analyzer.js.map +1 -1
- package/dist/semantic/providers/repo-resolver.d.ts.map +1 -1
- package/dist/semantic/providers/repo-resolver.js +14 -4
- package/dist/semantic/providers/repo-resolver.js.map +1 -1
- package/dist/semantic/semantic-layer.d.ts +6 -0
- package/dist/semantic/semantic-layer.d.ts.map +1 -1
- package/dist/semantic/semantic-layer.js +68 -2
- package/dist/semantic/semantic-layer.js.map +1 -1
- package/package.json +1 -1
package/dist/manifest/builder.js
CHANGED
|
@@ -16,8 +16,12 @@ import { buildLineageGraph } from '../lineage/builder.js';
|
|
|
16
16
|
import { detectDomainFlows, getDomainTrustOverview } from '../lineage/domain-lineage.js';
|
|
17
17
|
import { loadSemanticLayerFromDir } from '../semantic/index.js';
|
|
18
18
|
import { DataLexContractRegistry } from '../contracts/index.js';
|
|
19
|
-
import { loadAppDocument, findAppDocuments, findDashboardsForApp, loadDashboardDocument, extractDashboardBlockRefs, appFolderRelPath, } from '../apps/index.js';
|
|
19
|
+
import { loadAppDocument, findAppDocuments, findDashboardsForApp, loadDashboardDocument, extractDashboardBlockRefs, appFolderRelPath, normalizeProductDomainContext, } from '../apps/index.js';
|
|
20
20
|
import { loadDbtRunState, applyBlockDataState } from './dbt-freshness.js';
|
|
21
|
+
import { blockParameterDefinitions } from '../blocks/parameters.js';
|
|
22
|
+
import { loadDbtFirstModeling, siblingDbtArtifact } from './dbt-first-modeling.js';
|
|
23
|
+
import { domainFolderSlug } from './domain-writer.js';
|
|
24
|
+
import { loadDomainPackageRegistry } from './domain-package-registry.js';
|
|
21
25
|
/**
|
|
22
26
|
* Enumerate every file whose contents could change the manifest output.
|
|
23
27
|
*
|
|
@@ -53,7 +57,7 @@ export function collectInputFiles(options) {
|
|
|
53
57
|
for (const f of scanFilesRecursive(join(projectRoot, dir), ['.dql']))
|
|
54
58
|
files.add(f);
|
|
55
59
|
}
|
|
56
|
-
const notebookDirs = ['notebooks', 'blocks', 'dashboards', 'workbooks', ...(options.extraNotebookDirs ?? [])];
|
|
60
|
+
const notebookDirs = ['notebooks', 'domains', 'blocks', 'dashboards', 'workbooks', ...(options.extraNotebookDirs ?? [])];
|
|
57
61
|
for (const dir of notebookDirs) {
|
|
58
62
|
for (const f of scanFilesRecursive(join(projectRoot, dir), ['.dqlnb']))
|
|
59
63
|
files.add(f);
|
|
@@ -65,6 +69,16 @@ export function collectInputFiles(options) {
|
|
|
65
69
|
}
|
|
66
70
|
if (options.dbtManifestPath && existsSync(options.dbtManifestPath)) {
|
|
67
71
|
files.add(options.dbtManifestPath);
|
|
72
|
+
const catalogPath = siblingDbtArtifact(options.dbtManifestPath, 'catalog.json');
|
|
73
|
+
const semanticManifestPath = siblingDbtArtifact(options.dbtManifestPath, 'semantic_manifest.json');
|
|
74
|
+
if (catalogPath)
|
|
75
|
+
files.add(catalogPath);
|
|
76
|
+
if (semanticManifestPath)
|
|
77
|
+
files.add(semanticManifestPath);
|
|
78
|
+
}
|
|
79
|
+
if (config.manifestVersion === 3 && config.modeling?.mode === 'dbt-first') {
|
|
80
|
+
for (const f of scanFilesRecursive(join(projectRoot, 'domains'), ['.yaml', '.yml']))
|
|
81
|
+
files.add(f);
|
|
68
82
|
}
|
|
69
83
|
// Apps & dashboards. Manifests live at apps/<id>/dql.app.json; dashboards
|
|
70
84
|
// live under apps/<id>/dashboards/*.dqld.
|
|
@@ -82,6 +96,14 @@ export function buildManifest(options) {
|
|
|
82
96
|
// Load project config
|
|
83
97
|
const config = loadProjectConfig(projectRoot);
|
|
84
98
|
const projectName = config.project ?? 'dql-project';
|
|
99
|
+
const dbtFirstV3 = config.manifestVersion === 3 && config.modeling?.mode === 'dbt-first';
|
|
100
|
+
if ((config.manifestVersion === 3 || config.modeling?.mode === 'dbt-first') && !dbtFirstV3) {
|
|
101
|
+
diagnostics.push({
|
|
102
|
+
kind: 'config',
|
|
103
|
+
severity: 'error',
|
|
104
|
+
message: 'manifest v3 requires both `manifestVersion: 3` and `modeling.mode: "dbt-first"`; compiling with manifest v2 compatibility defaults',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
85
107
|
const datalexManifestPath = resolveDataLexManifestPath(projectRoot, options.datalexManifestPath, config);
|
|
86
108
|
const datalexRegistry = datalexManifestPath
|
|
87
109
|
? new DataLexContractRegistry({ manifestPath: datalexManifestPath })
|
|
@@ -96,8 +118,12 @@ export function buildManifest(options) {
|
|
|
96
118
|
}
|
|
97
119
|
// Scan first-class business domains
|
|
98
120
|
const domainDirs = ['domains', 'blocks', 'terms', 'business-views', ...(options.extraBlockDirs ?? [])];
|
|
99
|
-
const
|
|
100
|
-
|
|
121
|
+
const packageRegistry = dbtFirstV3 ? loadDomainPackageRegistry(projectRoot) : undefined;
|
|
122
|
+
const domains = packageRegistry
|
|
123
|
+
? Object.fromEntries(packageRegistry.values().map((pkg) => [pkg.id, pkg.domain]))
|
|
124
|
+
: scanDomains(projectRoot, domainDirs, diagnostics);
|
|
125
|
+
if (!packageRegistry)
|
|
126
|
+
validateDomainHierarchy(domains, diagnostics);
|
|
101
127
|
// Scan blocks
|
|
102
128
|
const blockDirs = ['blocks', 'domains', 'dashboards', 'workbooks', ...(options.extraBlockDirs ?? [])];
|
|
103
129
|
const blocks = scanBlocks(projectRoot, blockDirs, diagnostics, datalexRegistry);
|
|
@@ -173,15 +199,33 @@ export function buildManifest(options) {
|
|
|
173
199
|
// `dataState` via its transitive dbt upstreams.
|
|
174
200
|
applyBlockDataState(blocks, sources, dbtImport.dbtDag, runState);
|
|
175
201
|
}
|
|
202
|
+
let dbtFirstModeling;
|
|
203
|
+
if (dbtFirstV3) {
|
|
204
|
+
if (!options.dbtManifestPath || !existsSync(options.dbtManifestPath)) {
|
|
205
|
+
diagnostics.push({
|
|
206
|
+
kind: 'config',
|
|
207
|
+
severity: 'error',
|
|
208
|
+
message: 'manifest v3 dbt-first modeling requires a readable dbt manifest.json',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
dbtFirstModeling = loadDbtFirstModeling(projectRoot, options.dbtManifestPath);
|
|
213
|
+
diagnostics.push(...dbtFirstModeling.diagnostics);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
176
216
|
// Apps & dashboards (consumption layer). Scanned after blocks/notebooks
|
|
177
217
|
// because dashboard refs are resolved against the block path → name map.
|
|
178
218
|
const { apps, dashboards } = scanAppsAndDashboards(projectRoot, blocks, diagnostics);
|
|
179
219
|
// Build lineage
|
|
180
|
-
const lineage = buildManifestLineage(domains, blocks, metrics, dimensions, notebooks, dbtImport, apps, dashboards, businessViews, terms);
|
|
220
|
+
const lineage = buildManifestLineage(domains, blocks, metrics, dimensions, notebooks, dbtFirstV3 ? stripDbtImportDetails(dbtImport) : dbtImport, apps, dashboards, businessViews, terms);
|
|
221
|
+
if (dbtFirstModeling)
|
|
222
|
+
appendDbtFirstModelingLineage(lineage, dbtFirstModeling.modeling);
|
|
181
223
|
return {
|
|
182
|
-
manifestVersion: 2,
|
|
224
|
+
manifestVersion: dbtFirstV3 ? 3 : 2,
|
|
183
225
|
dqlVersion,
|
|
184
|
-
|
|
226
|
+
// v3 is a reproducible policy artifact. Runtime build time lives in the
|
|
227
|
+
// local cache, not in the canonical manifest content.
|
|
228
|
+
generatedAt: dbtFirstV3 ? new Date(0).toISOString() : new Date().toISOString(),
|
|
185
229
|
project: projectName,
|
|
186
230
|
projectRoot,
|
|
187
231
|
domains,
|
|
@@ -191,11 +235,13 @@ export function buildManifest(options) {
|
|
|
191
235
|
notebooks,
|
|
192
236
|
metrics,
|
|
193
237
|
dimensions,
|
|
194
|
-
sources,
|
|
238
|
+
sources: dbtFirstV3 ? stripDbtOwnedSourceDetails(sources) : sources,
|
|
195
239
|
apps: Object.keys(apps).length > 0 ? apps : undefined,
|
|
196
240
|
dashboards: Object.keys(dashboards).length > 0 ? dashboards : undefined,
|
|
197
241
|
lineage,
|
|
198
|
-
dbtImport,
|
|
242
|
+
dbtImport: dbtFirstV3 ? undefined : dbtImport,
|
|
243
|
+
dbtProvenance: dbtFirstModeling?.provenance,
|
|
244
|
+
modeling: dbtFirstModeling?.modeling,
|
|
199
245
|
diagnostics,
|
|
200
246
|
};
|
|
201
247
|
}
|
|
@@ -265,6 +311,75 @@ export function resolveDataLexManifestPath(projectRoot, explicit, loadedConfig)
|
|
|
265
311
|
function isAbsPath(p) {
|
|
266
312
|
return p.startsWith('/') || /^[A-Za-z]:[\\/]/.test(p);
|
|
267
313
|
}
|
|
314
|
+
/** Remove physical dbt payloads before serializing a v3 lineage graph. */
|
|
315
|
+
function stripDbtImportDetails(dbtImport) {
|
|
316
|
+
if (!dbtImport?.dbtDag)
|
|
317
|
+
return dbtImport;
|
|
318
|
+
return {
|
|
319
|
+
...dbtImport,
|
|
320
|
+
dbtDag: {
|
|
321
|
+
edges: dbtImport.dbtDag.edges,
|
|
322
|
+
models: dbtImport.dbtDag.models.map(({ columns: _columns, description: _description, ...model }) => model),
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/** Keep source names/usage but never serialize dbt-owned schema content in v3. */
|
|
327
|
+
function stripDbtOwnedSourceDetails(sources) {
|
|
328
|
+
return Object.fromEntries(Object.entries(sources).map(([name, source]) => {
|
|
329
|
+
const { dbtModel: _dbtModel, ...sparseSource } = source;
|
|
330
|
+
return [name, sparseSource];
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Add DQL-owned analytical bindings and relationship proof to lineage without
|
|
335
|
+
* mutating or reclassifying dbt transformation edges. A relationship edge is
|
|
336
|
+
* intentionally distinct from dbt `depends_on`: it is a reviewed analytical
|
|
337
|
+
* join policy, not inferred DAG evidence.
|
|
338
|
+
*/
|
|
339
|
+
function appendDbtFirstModelingLineage(lineage, modeling) {
|
|
340
|
+
const existingNodeIds = new Set(lineage.nodes.map((node) => node.id));
|
|
341
|
+
const existingEdges = new Set(lineage.edges.map((edge) => `${edge.source}:${edge.target}:${edge.type}`));
|
|
342
|
+
const dbtNodeByUniqueId = new Map();
|
|
343
|
+
for (const node of lineage.nodes) {
|
|
344
|
+
const uniqueId = typeof node.metadata?.uniqueId === 'string' ? node.metadata.uniqueId : undefined;
|
|
345
|
+
if (uniqueId)
|
|
346
|
+
dbtNodeByUniqueId.set(uniqueId, node.id);
|
|
347
|
+
}
|
|
348
|
+
const addEdge = (source, target, type) => {
|
|
349
|
+
const key = `${source}:${target}:${type}`;
|
|
350
|
+
if (!existingEdges.has(key)) {
|
|
351
|
+
lineage.edges.push({ source, target, type });
|
|
352
|
+
existingEdges.add(key);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
for (const entity of Object.values(modeling.entities)) {
|
|
356
|
+
const entityId = `dql_entity:${entity.id}`;
|
|
357
|
+
if (!existingNodeIds.has(entityId)) {
|
|
358
|
+
lineage.nodes.push({
|
|
359
|
+
id: entityId,
|
|
360
|
+
type: 'dql_entity',
|
|
361
|
+
name: entity.id,
|
|
362
|
+
layer: 'answer',
|
|
363
|
+
domain: entity.domain,
|
|
364
|
+
filePath: entity.sourcePath,
|
|
365
|
+
metadata: {
|
|
366
|
+
dbtUniqueId: entity.dbtUniqueId,
|
|
367
|
+
grain: entity.grain,
|
|
368
|
+
keys: entity.keys,
|
|
369
|
+
identityFingerprint: entity.identityFingerprint,
|
|
370
|
+
provenance: 'dql sparse overlay',
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
existingNodeIds.add(entityId);
|
|
374
|
+
}
|
|
375
|
+
const dbtNode = dbtNodeByUniqueId.get(entity.dbtUniqueId);
|
|
376
|
+
if (dbtNode)
|
|
377
|
+
addEdge(dbtNode, entityId, 'binds_dbt_model');
|
|
378
|
+
}
|
|
379
|
+
for (const relationship of Object.values(modeling.relationships)) {
|
|
380
|
+
addEdge(`dql_entity:${relationship.from}`, `dql_entity:${relationship.to}`, 'governed_relationship');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
268
383
|
function resolveSemanticPath(projectRoot, config) {
|
|
269
384
|
const customPath = config.semanticLayer?.path;
|
|
270
385
|
if (customPath)
|
|
@@ -882,9 +997,11 @@ function scanNotebooks(projectRoot, dirs, diagnostics, datalexRegistry) {
|
|
|
882
997
|
});
|
|
883
998
|
}
|
|
884
999
|
if (cells.length > 0) {
|
|
1000
|
+
const productContext = normalizeProductDomainContext(doc.metadata ?? {}, stringOrUndefined(doc.metadata?.domain));
|
|
885
1001
|
notebooks[relPath] = {
|
|
886
1002
|
title: doc.metadata?.title ?? doc.title ?? relPath,
|
|
887
1003
|
filePath: relPath,
|
|
1004
|
+
...productContext,
|
|
888
1005
|
cells,
|
|
889
1006
|
};
|
|
890
1007
|
}
|
|
@@ -902,6 +1019,15 @@ function scanNotebooks(projectRoot, dirs, diagnostics, datalexRegistry) {
|
|
|
902
1019
|
}
|
|
903
1020
|
return notebooks;
|
|
904
1021
|
}
|
|
1022
|
+
function stringOrUndefined(value) {
|
|
1023
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
1024
|
+
}
|
|
1025
|
+
function stringArrayValue(value) {
|
|
1026
|
+
if (!Array.isArray(value))
|
|
1027
|
+
return undefined;
|
|
1028
|
+
const items = [...new Set(value.filter((item) => typeof item === 'string').map((item) => item.trim()).filter(Boolean))];
|
|
1029
|
+
return items.length > 0 ? items : undefined;
|
|
1030
|
+
}
|
|
905
1031
|
/** Extract blocks declared inside notebook DQL cells into the blocks map. */
|
|
906
1032
|
function extractNotebookBlocks(notebooks) {
|
|
907
1033
|
const blocks = {};
|
|
@@ -1541,6 +1667,11 @@ function appDocumentToManifest(app, filePath, dashboardIds) {
|
|
|
1541
1667
|
businessRules: app.businessRules,
|
|
1542
1668
|
caveats: app.caveats,
|
|
1543
1669
|
domain: app.domain,
|
|
1670
|
+
ownerDomain: app.ownerDomain ?? app.domain,
|
|
1671
|
+
usesDomains: app.usesDomains,
|
|
1672
|
+
purpose: app.purpose,
|
|
1673
|
+
requiredExports: app.requiredExports,
|
|
1674
|
+
classification: app.classification,
|
|
1544
1675
|
subdomain: app.subdomain,
|
|
1545
1676
|
groups: app.groups ?? [],
|
|
1546
1677
|
audience: app.audience,
|
|
@@ -1896,6 +2027,7 @@ function blockDeclToManifestBlock(block, filePath) {
|
|
|
1896
2027
|
policy: entry.policy,
|
|
1897
2028
|
}))
|
|
1898
2029
|
: undefined,
|
|
2030
|
+
parameters: blockParameterDefinitions(block),
|
|
1899
2031
|
filterBindings: Array.isArray(block.filterBindings)
|
|
1900
2032
|
? block.filterBindings.map((entry) => ({
|
|
1901
2033
|
filter: entry.filter,
|
|
@@ -1984,6 +2116,7 @@ function buildOutputContract(declaredOutputs, columnLineage) {
|
|
|
1984
2116
|
}
|
|
1985
2117
|
function domainDeclToManifestDomain(domain, filePath) {
|
|
1986
2118
|
return {
|
|
2119
|
+
id: typeof domain.id === 'string' ? domain.id : domainFolderSlug(domain.name),
|
|
1987
2120
|
name: domain.name,
|
|
1988
2121
|
filePath,
|
|
1989
2122
|
parent: typeof domain.parent === 'string' ? domain.parent : undefined,
|
|
@@ -2003,6 +2136,7 @@ function domainDeclToManifestDomain(domain, filePath) {
|
|
|
2003
2136
|
dbtTags: Array.isArray(domain.dbtTags) ? domain.dbtTags : undefined,
|
|
2004
2137
|
semanticDomains: Array.isArray(domain.semanticDomains) ? domain.semanticDomains : undefined,
|
|
2005
2138
|
semanticTags: Array.isArray(domain.semanticTags) ? domain.semanticTags : undefined,
|
|
2139
|
+
exports: Array.isArray(domain.exports) ? domain.exports : undefined,
|
|
2006
2140
|
};
|
|
2007
2141
|
}
|
|
2008
2142
|
function termDeclToManifestTerm(term, filePath) {
|