@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.
Files changed (99) hide show
  1. package/dist/apps/app-document.d.ts +3 -1
  2. package/dist/apps/app-document.d.ts.map +1 -1
  3. package/dist/apps/app-document.js +26 -11
  4. package/dist/apps/app-document.js.map +1 -1
  5. package/dist/apps/dashboard-document.d.ts +74 -0
  6. package/dist/apps/dashboard-document.d.ts.map +1 -1
  7. package/dist/apps/dashboard-document.js +101 -2
  8. package/dist/apps/dashboard-document.js.map +1 -1
  9. package/dist/apps/index.d.ts +3 -1
  10. package/dist/apps/index.d.ts.map +1 -1
  11. package/dist/apps/index.js +1 -0
  12. package/dist/apps/index.js.map +1 -1
  13. package/dist/apps/product-domain-context.d.ts +23 -0
  14. package/dist/apps/product-domain-context.d.ts.map +1 -0
  15. package/dist/apps/product-domain-context.js +25 -0
  16. package/dist/apps/product-domain-context.js.map +1 -0
  17. package/dist/artifacts/dql-artifact.d.ts +13 -0
  18. package/dist/artifacts/dql-artifact.d.ts.map +1 -1
  19. package/dist/artifacts/dql-artifact.js +69 -0
  20. package/dist/artifacts/dql-artifact.js.map +1 -1
  21. package/dist/artifacts/index.d.ts +1 -1
  22. package/dist/artifacts/index.d.ts.map +1 -1
  23. package/dist/ast/index.d.ts +1 -1
  24. package/dist/ast/index.d.ts.map +1 -1
  25. package/dist/ast/index.js.map +1 -1
  26. package/dist/ast/nodes.d.ts +10 -1
  27. package/dist/ast/nodes.d.ts.map +1 -1
  28. package/dist/ast/printer.js +4 -0
  29. package/dist/ast/printer.js.map +1 -1
  30. package/dist/blocks/index.d.ts +2 -0
  31. package/dist/blocks/index.d.ts.map +1 -0
  32. package/dist/blocks/index.js +2 -0
  33. package/dist/blocks/index.js.map +1 -0
  34. package/dist/blocks/parameters.d.ts +55 -0
  35. package/dist/blocks/parameters.d.ts.map +1 -0
  36. package/dist/blocks/parameters.js +160 -0
  37. package/dist/blocks/parameters.js.map +1 -0
  38. package/dist/format/diff.d.ts.map +1 -1
  39. package/dist/format/diff.js +19 -11
  40. package/dist/format/diff.js.map +1 -1
  41. package/dist/format/notebook.d.ts +1 -1
  42. package/dist/format/notebook.js +2 -2
  43. package/dist/formatter/formatter.js +23 -1
  44. package/dist/formatter/formatter.js.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/manifest/builder.d.ts +17 -0
  50. package/dist/manifest/builder.d.ts.map +1 -1
  51. package/dist/manifest/builder.js +143 -9
  52. package/dist/manifest/builder.js.map +1 -1
  53. package/dist/manifest/datalex-migration.d.ts +68 -0
  54. package/dist/manifest/datalex-migration.d.ts.map +1 -0
  55. package/dist/manifest/datalex-migration.js +369 -0
  56. package/dist/manifest/datalex-migration.js.map +1 -0
  57. package/dist/manifest/dbt-first-authoring.d.ts +210 -0
  58. package/dist/manifest/dbt-first-authoring.d.ts.map +1 -0
  59. package/dist/manifest/dbt-first-authoring.js +510 -0
  60. package/dist/manifest/dbt-first-authoring.js.map +1 -0
  61. package/dist/manifest/dbt-first-modeling.d.ts +34 -0
  62. package/dist/manifest/dbt-first-modeling.d.ts.map +1 -0
  63. package/dist/manifest/dbt-first-modeling.js +931 -0
  64. package/dist/manifest/dbt-first-modeling.js.map +1 -0
  65. package/dist/manifest/domain-discovery.d.ts +98 -0
  66. package/dist/manifest/domain-discovery.d.ts.map +1 -0
  67. package/dist/manifest/domain-discovery.js +503 -0
  68. package/dist/manifest/domain-discovery.js.map +1 -0
  69. package/dist/manifest/domain-package-registry.d.ts +20 -0
  70. package/dist/manifest/domain-package-registry.d.ts.map +1 -0
  71. package/dist/manifest/domain-package-registry.js +265 -0
  72. package/dist/manifest/domain-package-registry.js.map +1 -0
  73. package/dist/manifest/domain-writer.d.ts +4 -0
  74. package/dist/manifest/domain-writer.d.ts.map +1 -1
  75. package/dist/manifest/domain-writer.js +3 -0
  76. package/dist/manifest/domain-writer.js.map +1 -1
  77. package/dist/manifest/index.d.ts +7 -1
  78. package/dist/manifest/index.d.ts.map +1 -1
  79. package/dist/manifest/index.js +6 -0
  80. package/dist/manifest/index.js.map +1 -1
  81. package/dist/manifest/project-snapshot.d.ts +30 -0
  82. package/dist/manifest/project-snapshot.d.ts.map +1 -0
  83. package/dist/manifest/project-snapshot.js +68 -0
  84. package/dist/manifest/project-snapshot.js.map +1 -0
  85. package/dist/manifest/types.d.ts +289 -3
  86. package/dist/manifest/types.d.ts.map +1 -1
  87. package/dist/parser/parser.d.ts.map +1 -1
  88. package/dist/parser/parser.js +40 -6
  89. package/dist/parser/parser.js.map +1 -1
  90. package/dist/semantic/analyzer.js +1 -1
  91. package/dist/semantic/analyzer.js.map +1 -1
  92. package/dist/semantic/providers/repo-resolver.d.ts.map +1 -1
  93. package/dist/semantic/providers/repo-resolver.js +14 -4
  94. package/dist/semantic/providers/repo-resolver.js.map +1 -1
  95. package/dist/semantic/semantic-layer.d.ts +6 -0
  96. package/dist/semantic/semantic-layer.d.ts.map +1 -1
  97. package/dist/semantic/semantic-layer.js +68 -2
  98. package/dist/semantic/semantic-layer.js.map +1 -1
  99. package/package.json +1 -1
@@ -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 domains = scanDomains(projectRoot, domainDirs, diagnostics);
100
- validateDomainHierarchy(domains, diagnostics);
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
- generatedAt: new Date().toISOString(),
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) {