@duckcodeailabs/dql-core 1.6.21 → 1.6.23
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/contracts/index.d.ts +1 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/registry.d.ts +25 -1
- package/dist/contracts/registry.d.ts.map +1 -1
- package/dist/contracts/registry.js +104 -12
- package/dist/contracts/registry.js.map +1 -1
- package/dist/contracts/types.d.ts +55 -0
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.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/lineage/builder.d.ts.map +1 -1
- package/dist/lineage/builder.js +12 -0
- package/dist/lineage/builder.js.map +1 -1
- package/dist/lineage/column-lineage.d.ts +27 -0
- package/dist/lineage/column-lineage.d.ts.map +1 -1
- package/dist/lineage/column-lineage.js +147 -0
- package/dist/lineage/column-lineage.js.map +1 -1
- package/dist/lineage/derivation.d.ts +111 -0
- package/dist/lineage/derivation.d.ts.map +1 -0
- package/dist/lineage/derivation.js +177 -0
- package/dist/lineage/derivation.js.map +1 -0
- package/dist/lineage/impact.d.ts +127 -0
- package/dist/lineage/impact.d.ts.map +1 -0
- package/dist/lineage/impact.js +305 -0
- package/dist/lineage/impact.js.map +1 -0
- package/dist/lineage/index.d.ts +3 -1
- package/dist/lineage/index.d.ts.map +1 -1
- package/dist/lineage/index.js +3 -1
- package/dist/lineage/index.js.map +1 -1
- package/dist/manifest/builder.d.ts +16 -0
- package/dist/manifest/builder.d.ts.map +1 -1
- package/dist/manifest/builder.js +117 -10
- package/dist/manifest/builder.js.map +1 -1
- package/dist/manifest/dbt-freshness.d.ts +65 -0
- package/dist/manifest/dbt-freshness.d.ts.map +1 -0
- package/dist/manifest/dbt-freshness.js +264 -0
- package/dist/manifest/dbt-freshness.js.map +1 -0
- package/dist/manifest/domain-writer.d.ts +66 -0
- package/dist/manifest/domain-writer.d.ts.map +1 -0
- package/dist/manifest/domain-writer.js +114 -0
- package/dist/manifest/domain-writer.js.map +1 -0
- package/dist/manifest/index.d.ts +4 -1
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +3 -0
- package/dist/manifest/index.js.map +1 -1
- package/dist/manifest/output-drift.d.ts +31 -0
- package/dist/manifest/output-drift.d.ts.map +1 -0
- package/dist/manifest/output-drift.js +97 -0
- package/dist/manifest/output-drift.js.map +1 -0
- package/dist/manifest/types.d.ts +156 -1
- package/dist/manifest/types.d.ts.map +1 -1
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +8 -0
- package/dist/parser/parser.js.map +1 -1
- package/dist/semantic/analyzer.d.ts +8 -0
- package/dist/semantic/analyzer.d.ts.map +1 -1
- package/dist/semantic/analyzer.js +8 -0
- package/dist/semantic/analyzer.js.map +1 -1
- package/dist/semantic/conflicts.d.ts +33 -0
- package/dist/semantic/conflicts.d.ts.map +1 -0
- package/dist/semantic/conflicts.js +270 -0
- package/dist/semantic/conflicts.js.map +1 -0
- package/dist/semantic/index.d.ts +1 -1
- package/dist/semantic/index.d.ts.map +1 -1
- package/dist/semantic/index.js +1 -1
- package/dist/semantic/index.js.map +1 -1
- package/dist/trust/index.d.ts +3 -0
- package/dist/trust/index.d.ts.map +1 -0
- package/dist/trust/index.js +2 -0
- package/dist/trust/index.js.map +1 -0
- package/dist/trust/labels.d.ts +122 -0
- package/dist/trust/labels.d.ts.map +1 -0
- package/dist/trust/labels.js +185 -0
- package/dist/trust/labels.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impact analysis + re-certification gating for DQL's answer layer.
|
|
3
|
+
*
|
|
4
|
+
* DQL composition is unbounded freeform `ref()`. The safety net: when a block
|
|
5
|
+
* changes, you must know what downstream *certified* work it invalidates.
|
|
6
|
+
*
|
|
7
|
+
* Given changed block name(s) — from `dql diff` or explicit input — this module:
|
|
8
|
+
* 1. walks the dependency DAG **downstream** to the full transitive set,
|
|
9
|
+
* 2. flags the cross-domain edges that get invalidated,
|
|
10
|
+
* 3. computes the resulting `domainTrust` delta,
|
|
11
|
+
* 4. produces a **required re-cert** list (certified downstream depending on
|
|
12
|
+
* a changed block's semantics).
|
|
13
|
+
*
|
|
14
|
+
* It also classifies whether a given block change is *semantic* (affects what
|
|
15
|
+
* the block computes) or purely cosmetic (description/owner/tag/viz only).
|
|
16
|
+
* The classification is **conservative**: when unsure, treat the change as
|
|
17
|
+
* semantic so re-cert is required rather than silently skipped.
|
|
18
|
+
*
|
|
19
|
+
* This is the LOCAL engine. It does not manage org-wide workflow, approvals,
|
|
20
|
+
* audit, or auto-re-certification — that is the cloud product.
|
|
21
|
+
*/
|
|
22
|
+
import { getDomainTrustOverview } from './domain-lineage.js';
|
|
23
|
+
// ---- Semantic-change classification ----
|
|
24
|
+
/**
|
|
25
|
+
* Block-diff field paths that are **non-semantic**: they describe or annotate
|
|
26
|
+
* the block but do not change what it computes. A change confined entirely to
|
|
27
|
+
* these fields does not invalidate certification.
|
|
28
|
+
*
|
|
29
|
+
* Everything else (query, params, metric refs, tests, domain, type, business
|
|
30
|
+
* rules, visualization, …) is treated as semantic. This is deliberately a
|
|
31
|
+
* deny-list of known-cosmetic fields rather than an allow-list of semantic
|
|
32
|
+
* ones, so an unrecognized/new field defaults to "semantic" → needs re-cert.
|
|
33
|
+
*/
|
|
34
|
+
const NON_SEMANTIC_BLOCK_FIELDS = new Set([
|
|
35
|
+
'description',
|
|
36
|
+
'owner',
|
|
37
|
+
'tags',
|
|
38
|
+
'businessOutcome',
|
|
39
|
+
'businessOwner',
|
|
40
|
+
'decisionUse',
|
|
41
|
+
'reviewCadence',
|
|
42
|
+
'caveats',
|
|
43
|
+
]);
|
|
44
|
+
/**
|
|
45
|
+
* Classify a single block-level `DiffChange` as semantic or non-semantic.
|
|
46
|
+
*
|
|
47
|
+
* - Added/removed blocks are always semantic (structural).
|
|
48
|
+
* - A changed block is non-semantic only when **every** changed field is in
|
|
49
|
+
* {@link NON_SEMANTIC_BLOCK_FIELDS}. A single unrecognized or semantic field
|
|
50
|
+
* makes the whole change semantic (conservative default).
|
|
51
|
+
*/
|
|
52
|
+
export function classifyBlockChange(change) {
|
|
53
|
+
switch (change.kind) {
|
|
54
|
+
case 'block-added':
|
|
55
|
+
case 'block-removed':
|
|
56
|
+
return { name: change.name, verdict: 'semantic', changedFields: [], structural: true };
|
|
57
|
+
case 'block-changed': {
|
|
58
|
+
const fields = change.fields.map((f) => f.path);
|
|
59
|
+
const allCosmetic = fields.length > 0 && fields.every((p) => isNonSemanticField(p));
|
|
60
|
+
return {
|
|
61
|
+
name: change.name,
|
|
62
|
+
verdict: allCosmetic ? 'non-semantic' : 'semantic',
|
|
63
|
+
changedFields: fields,
|
|
64
|
+
structural: false,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** A field path is non-semantic when its leading segment is a known cosmetic field. */
|
|
72
|
+
function isNonSemanticField(path) {
|
|
73
|
+
// `tags`, `description`, … are top-level; `params.region`, `tests[..]`,
|
|
74
|
+
// `visualization.title`, `query` are semantic. Only the exact known cosmetic
|
|
75
|
+
// field names count as non-semantic.
|
|
76
|
+
return NON_SEMANTIC_BLOCK_FIELDS.has(path);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Extract the set of changed blocks from a diff report's changes, classifying
|
|
80
|
+
* each as semantic or non-semantic. Non-block changes (dashboards, workbooks,
|
|
81
|
+
* cells, notebooks) are ignored here — impact is keyed off block identity.
|
|
82
|
+
*/
|
|
83
|
+
export function changedBlocksFromDiff(changes) {
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const change of changes) {
|
|
86
|
+
const classified = classifyBlockChange(change);
|
|
87
|
+
if (classified)
|
|
88
|
+
out.push(classified);
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Compute the impact of a set of changed blocks against a lineage graph.
|
|
94
|
+
*
|
|
95
|
+
* Only blocks with a **semantic** verdict propagate downstream impact; a purely
|
|
96
|
+
* cosmetic change (description/owner/tag only) produces an empty downstream set
|
|
97
|
+
* and never trips the re-cert gate.
|
|
98
|
+
*/
|
|
99
|
+
export function computeImpact(graph, changedBlocks, options = {}) {
|
|
100
|
+
const ignoreAlreadyPending = options.ignoreAlreadyPending ?? true;
|
|
101
|
+
const semantic = changedBlocks.filter((b) => b.verdict === 'semantic');
|
|
102
|
+
const semanticNames = semantic.map((b) => b.name);
|
|
103
|
+
// Resolve each changed block to its graph node id. A changed block may not
|
|
104
|
+
// exist in the graph (e.g. removed, or never compiled) — skip those for the
|
|
105
|
+
// downstream walk but keep them in the report.
|
|
106
|
+
const sourceIds = [];
|
|
107
|
+
const sourceNameById = new Map();
|
|
108
|
+
for (const block of semantic) {
|
|
109
|
+
const id = `block:${block.name}`;
|
|
110
|
+
if (graph.getNode(id)) {
|
|
111
|
+
sourceIds.push(id);
|
|
112
|
+
sourceNameById.set(id, block.name);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Full transitive downstream union. Track which changed block(s) reach each
|
|
116
|
+
// downstream node so the re-cert list can attribute invalidations precisely.
|
|
117
|
+
const downstreamById = new Map();
|
|
118
|
+
const reachedBy = new Map();
|
|
119
|
+
for (const id of sourceIds) {
|
|
120
|
+
const blockName = sourceNameById.get(id);
|
|
121
|
+
for (const node of graph.descendants(id)) {
|
|
122
|
+
if (node.type === 'domain')
|
|
123
|
+
continue;
|
|
124
|
+
downstreamById.set(node.id, node);
|
|
125
|
+
const set = reachedBy.get(node.id) ?? new Set();
|
|
126
|
+
set.add(blockName);
|
|
127
|
+
reachedBy.set(node.id, set);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const downstream = [...downstreamById.values()]
|
|
131
|
+
.map(toImpactedNode)
|
|
132
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
133
|
+
// Cross-domain edges fully contained in the invalidated zone (source = a
|
|
134
|
+
// changed block or a downstream node; target = a downstream node).
|
|
135
|
+
const zoneIds = new Set([...sourceIds, ...downstreamById.keys()]);
|
|
136
|
+
const crossDomainImpacts = collectCrossDomainImpacts(graph, zoneIds);
|
|
137
|
+
// Re-cert list: certified downstream nodes invalidated by a semantic change.
|
|
138
|
+
const requiresRecert = [];
|
|
139
|
+
for (const node of downstreamById.values()) {
|
|
140
|
+
if (node.status !== 'certified') {
|
|
141
|
+
if (!(node.status === 'pending_recertification' && !ignoreAlreadyPending))
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
requiresRecert.push({
|
|
145
|
+
id: node.id,
|
|
146
|
+
name: node.name,
|
|
147
|
+
domain: node.domain,
|
|
148
|
+
owner: node.owner,
|
|
149
|
+
invalidatedBy: [...(reachedBy.get(node.id) ?? new Set())].sort(),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
requiresRecert.sort((a, b) => a.id.localeCompare(b.id));
|
|
153
|
+
const domainTrustDelta = computeDomainTrustDelta(graph, requiresRecert);
|
|
154
|
+
return {
|
|
155
|
+
changedBlocks,
|
|
156
|
+
semanticChanges: semanticNames,
|
|
157
|
+
downstream,
|
|
158
|
+
crossDomainImpacts,
|
|
159
|
+
requiresRecert,
|
|
160
|
+
domainTrustDelta,
|
|
161
|
+
hasCertifiedInvalidation: requiresRecert.length > 0,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function toImpactedNode(node) {
|
|
165
|
+
return {
|
|
166
|
+
id: node.id,
|
|
167
|
+
type: node.type,
|
|
168
|
+
name: node.name,
|
|
169
|
+
domain: node.domain,
|
|
170
|
+
status: node.status,
|
|
171
|
+
owner: node.owner,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Collect cross-domain edges whose source and target both lie inside the
|
|
176
|
+
* invalidated zone, grouped by `from → to` domain pair.
|
|
177
|
+
*/
|
|
178
|
+
function collectCrossDomainImpacts(graph, zoneIds) {
|
|
179
|
+
const byPair = new Map();
|
|
180
|
+
for (const edge of graph.getCrossDomainEdges()) {
|
|
181
|
+
if (!zoneIds.has(edge.source) || !zoneIds.has(edge.target))
|
|
182
|
+
continue;
|
|
183
|
+
const from = edge.sourceDomain ?? graph.getNode(edge.source)?.domain ?? '(unknown)';
|
|
184
|
+
const to = edge.targetDomain ?? graph.getNode(edge.target)?.domain ?? '(unknown)';
|
|
185
|
+
const key = `${from}→${to}`;
|
|
186
|
+
let impact = byPair.get(key);
|
|
187
|
+
if (!impact) {
|
|
188
|
+
impact = { from, to, edges: [] };
|
|
189
|
+
byPair.set(key, impact);
|
|
190
|
+
}
|
|
191
|
+
impact.edges.push({ source: edge.source, target: edge.target });
|
|
192
|
+
}
|
|
193
|
+
return [...byPair.values()].sort((a, b) => `${a.from}→${a.to}`.localeCompare(`${b.from}→${b.to}`));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Compute the per-domain `domainTrust` delta that results from demoting every
|
|
197
|
+
* re-cert artifact out of `certified`. The "before" view uses the live graph;
|
|
198
|
+
* the "after" view recomputes trust as if each re-cert block were no longer
|
|
199
|
+
* certified (it would become `pending_recertification`).
|
|
200
|
+
*
|
|
201
|
+
* Only block-typed re-cert items affect trust scores, mirroring
|
|
202
|
+
* {@link getDomainTrustOverview} which counts blocks only.
|
|
203
|
+
*/
|
|
204
|
+
function computeDomainTrustDelta(graph, requiresRecert) {
|
|
205
|
+
// Map domain → set of block names losing certified status.
|
|
206
|
+
const demotedByDomain = new Map();
|
|
207
|
+
const affectedDomains = new Set();
|
|
208
|
+
for (const item of requiresRecert) {
|
|
209
|
+
const node = graph.getNode(item.id);
|
|
210
|
+
if (!node || node.type !== 'block')
|
|
211
|
+
continue;
|
|
212
|
+
const domain = node.domain ?? '(unassigned)';
|
|
213
|
+
demotedByDomain.set(domain, (demotedByDomain.get(domain) ?? 0) + 1);
|
|
214
|
+
affectedDomains.add(domain);
|
|
215
|
+
}
|
|
216
|
+
const deltas = [];
|
|
217
|
+
for (const domain of affectedDomains) {
|
|
218
|
+
const overview = getDomainTrustOverview(graph, domain);
|
|
219
|
+
const total = overview.totalBlocks;
|
|
220
|
+
const certifiedBefore = overview.certified;
|
|
221
|
+
const demoted = demotedByDomain.get(domain) ?? 0;
|
|
222
|
+
const certifiedAfter = Math.max(0, certifiedBefore - demoted);
|
|
223
|
+
const trustBefore = total > 0 ? certifiedBefore / total : 0;
|
|
224
|
+
const trustAfter = total > 0 ? certifiedAfter / total : 0;
|
|
225
|
+
deltas.push({
|
|
226
|
+
domain,
|
|
227
|
+
certifiedBefore,
|
|
228
|
+
certifiedAfter,
|
|
229
|
+
total,
|
|
230
|
+
trustBefore,
|
|
231
|
+
trustAfter,
|
|
232
|
+
delta: trustAfter - trustBefore,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return deltas.sort((a, b) => a.domain.localeCompare(b.domain));
|
|
236
|
+
}
|
|
237
|
+
// ---- Convenience: full report from a diff ----
|
|
238
|
+
/**
|
|
239
|
+
* One-shot helper: classify a diff's block changes and compute the impact
|
|
240
|
+
* report in a single call.
|
|
241
|
+
*/
|
|
242
|
+
export function computeImpactFromDiff(graph, changes, options) {
|
|
243
|
+
return computeImpact(graph, changedBlocksFromDiff(changes), options);
|
|
244
|
+
}
|
|
245
|
+
// ---- Text rendering ----
|
|
246
|
+
/** Render an impact report as human-readable text for the CLI. */
|
|
247
|
+
export function renderImpactText(report) {
|
|
248
|
+
const lines = [];
|
|
249
|
+
lines.push(' Impact Analysis');
|
|
250
|
+
lines.push(' ' + '='.repeat(50));
|
|
251
|
+
if (report.changedBlocks.length === 0) {
|
|
252
|
+
lines.push('');
|
|
253
|
+
lines.push(' No block changes detected.');
|
|
254
|
+
return lines.join('\n');
|
|
255
|
+
}
|
|
256
|
+
lines.push('');
|
|
257
|
+
lines.push(' Changed blocks:');
|
|
258
|
+
for (const block of report.changedBlocks) {
|
|
259
|
+
const tag = block.structural
|
|
260
|
+
? '(structural)'
|
|
261
|
+
: block.verdict === 'non-semantic'
|
|
262
|
+
? '(non-semantic — no re-cert)'
|
|
263
|
+
: '(semantic)';
|
|
264
|
+
lines.push(` ${block.name} ${tag}`);
|
|
265
|
+
}
|
|
266
|
+
lines.push('');
|
|
267
|
+
lines.push(` Transitive downstream affected: ${report.downstream.length}`);
|
|
268
|
+
for (const node of report.downstream) {
|
|
269
|
+
const badge = node.status === 'certified' ? ' [certified]' : node.status ? ` [${node.status}]` : '';
|
|
270
|
+
const domain = node.domain ? ` (${node.domain})` : '';
|
|
271
|
+
lines.push(` - ${node.type}:${node.name}${domain}${badge}`);
|
|
272
|
+
}
|
|
273
|
+
if (report.crossDomainImpacts.length > 0) {
|
|
274
|
+
lines.push('');
|
|
275
|
+
lines.push(' Cross-domain edges invalidated:');
|
|
276
|
+
for (const impact of report.crossDomainImpacts) {
|
|
277
|
+
lines.push(` ${impact.from} -> ${impact.to} (${impact.edges.length} edge(s))`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (report.domainTrustDelta.length > 0) {
|
|
281
|
+
lines.push('');
|
|
282
|
+
lines.push(' domainTrust delta:');
|
|
283
|
+
for (const d of report.domainTrustDelta) {
|
|
284
|
+
const before = (d.trustBefore * 100).toFixed(0);
|
|
285
|
+
const after = (d.trustAfter * 100).toFixed(0);
|
|
286
|
+
const pts = (d.delta * 100).toFixed(0);
|
|
287
|
+
lines.push(` ${d.domain}: ${d.certifiedBefore}/${d.total} -> ${d.certifiedAfter}/${d.total} certified ` +
|
|
288
|
+
`(${before}% -> ${after}%, ${pts} pts)`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
lines.push('');
|
|
292
|
+
if (report.requiresRecert.length > 0) {
|
|
293
|
+
lines.push(` Requires re-certification (${report.requiresRecert.length}):`);
|
|
294
|
+
for (const item of report.requiresRecert) {
|
|
295
|
+
const owner = item.owner ? ` — ${item.owner}` : '';
|
|
296
|
+
lines.push(` ! ${item.name}${item.domain ? ` (${item.domain})` : ''}${owner}`);
|
|
297
|
+
lines.push(` invalidated by: ${item.invalidatedBy.join(', ')}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
lines.push(' No certified downstream invalidated. Re-cert not required.');
|
|
302
|
+
}
|
|
303
|
+
return lines.join('\n');
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.js","sourceRoot":"","sources":["../../src/lineage/impact.ts"],"names":[],"mappings":"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"}
|
package/dist/lineage/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { analyzeSqlReferences, extractTablesFromSql, type SqlColumnReference, type SqlParseResult, type SqlReferenceAnalysis, } from './sql-parser.js';
|
|
2
|
-
export { extractColumnLineage, type ColumnLineageEntry, type ColumnLineageResult, type ColumnSource, } from './column-lineage.js';
|
|
2
|
+
export { extractColumnLineage, extractRefColumnUsage, type ColumnLineageEntry, type ColumnLineageResult, type ColumnSource, type RefColumnUsage, } from './column-lineage.js';
|
|
3
3
|
export { resolveDependencies, getUpstream, getDownstream, type BlockDependencyInfo, type DependencyResolutionResult, } from './dependency-resolver.js';
|
|
4
4
|
export { LineageGraph, getLayerForNodeType, type LineageNode, type LineageEdge, type LineageNodeType, type LineageEdgeType, type LineageLayer, type LineageGraphJSON, } from './lineage-graph.js';
|
|
5
5
|
export { buildLineageGraph, type LineageBlockInput, type LineageMetricInput, type LineageDimensionInput, type LineageDbtModelInput, type LineageDashboardInput, type LineageBusinessViewInput, type LineageTermInput, type LineageBuilderOptions, } from './builder.js';
|
|
6
6
|
export { queryLineage, queryBusiness360, queryCompleteLineagePaths, type LineageQuery, type LineageQueryResult, type Business360Asset, type Business360BlockContract, type Business360Edge, type Business360Gap, type Business360Options, type Business360Reusability, type Business360Result, type Business360ResultV2, type LineagePath, type CompletePathResult, type CompletePathOptions, } from './query.js';
|
|
7
7
|
export { buildTrustChain, analyzeImpact, detectDomainFlows, getDomainTrustOverview, type TrustChain, type TrustChainNode, type ImpactAnalysis, type DomainImpact, type DomainFlow, } from './domain-lineage.js';
|
|
8
|
+
export { buildDerivationWalk, type DerivationWalk, type DerivationStep, type DerivationStepKind, type DerivationFocusBlock, type BuildDerivationWalkInput, } from './derivation.js';
|
|
9
|
+
export { classifyBlockChange, changedBlocksFromDiff, computeImpact, computeImpactFromDiff, renderImpactText, type ChangedBlock, type SemanticVerdict, type ImpactReport, type ImpactedNode, type CrossDomainImpact, type DomainTrustDelta, type RecertItem, type ComputeImpactOptions, } from './impact.js';
|
|
8
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,GAChC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,GAC9B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,oBAAoB,GAC1B,MAAM,aAAa,CAAC"}
|
package/dist/lineage/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// Lineage analysis — SQL parsing, dependency resolution, graph building, and domain lineage
|
|
2
2
|
export { analyzeSqlReferences, extractTablesFromSql, } from './sql-parser.js';
|
|
3
|
-
export { extractColumnLineage, } from './column-lineage.js';
|
|
3
|
+
export { extractColumnLineage, extractRefColumnUsage, } from './column-lineage.js';
|
|
4
4
|
export { resolveDependencies, getUpstream, getDownstream, } from './dependency-resolver.js';
|
|
5
5
|
export { LineageGraph, getLayerForNodeType, } from './lineage-graph.js';
|
|
6
6
|
export { buildLineageGraph, } from './builder.js';
|
|
7
7
|
export { queryLineage, queryBusiness360, queryCompleteLineagePaths, } from './query.js';
|
|
8
8
|
export { buildTrustChain, analyzeImpact, detectDomainFlows, getDomainTrustOverview, } from './domain-lineage.js';
|
|
9
|
+
export { buildDerivationWalk, } from './derivation.js';
|
|
10
|
+
export { classifyBlockChange, changedBlocksFromDiff, computeImpact, computeImpactFromDiff, renderImpactText, } from './impact.js';
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAE5F,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAIrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAE5F,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAIrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GAKtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,aAAa,GAGd,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,YAAY,EACZ,mBAAmB,GAOpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,GASlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,GAc1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,sBAAsB,GAMvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,GAMpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,GASjB,MAAM,aAAa,CAAC"}
|
|
@@ -91,6 +91,22 @@ interface ProjectConfig {
|
|
|
91
91
|
/** Path to datalex-manifest.json, absolute or relative to projectRoot */
|
|
92
92
|
manifestPath?: string;
|
|
93
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Optional `dql propose` conventions. Refines the convention-agnostic
|
|
96
|
+
* classifier + bounded selection. All fields are optional; defaults apply.
|
|
97
|
+
*/
|
|
98
|
+
propose?: {
|
|
99
|
+
/** Folders/tags that mean business (default ["marts","core","reporting"]). */
|
|
100
|
+
businessLayers?: string[];
|
|
101
|
+
/** Folders/tags that mean plumbing (default ["staging","intermediate","base"]). */
|
|
102
|
+
excludeLayers?: string[];
|
|
103
|
+
/** Max candidates generated per domain (default 8). */
|
|
104
|
+
maxPerDomain?: number;
|
|
105
|
+
/** Minimum demand score a candidate needs to be selected (default 0). */
|
|
106
|
+
minScore?: number;
|
|
107
|
+
/** Optional AI enrichment toggle (default "auto"). */
|
|
108
|
+
aiEnrichment?: 'auto' | 'on' | 'off';
|
|
109
|
+
};
|
|
94
110
|
}
|
|
95
111
|
/**
|
|
96
112
|
* Load `dql.config.json`. Exposed so CLI commands can read the same config
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/manifest/builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/manifest/builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH,OAAO,KAAK,EACV,WAAW,EAeZ,MAAM,YAAY,CAAC;AAepB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAmDzE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,WAAW,CAgJxE;AAID,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE;QACJ,0EAA0E;QAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,6FAA6F;QAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,iGAAiG;IACjG,OAAO,CAAC,EAAE;QACR,yEAAyE;QACzE,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR,8EAA8E;QAC9E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,mFAAmF;QACnF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,uDAAuD;QACvD,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,yEAAyE;QACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,sDAAsD;QACtD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;KACtC,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAQpE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAgBf;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CAef"}
|
package/dist/manifest/builder.js
CHANGED
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
9
9
|
import { join, extname, relative } from 'node:path';
|
|
10
10
|
import { Parser } from '../parser/index.js';
|
|
11
|
-
import { analyze } from '../semantic/index.js';
|
|
11
|
+
import { analyze, detectTrustConflicts } from '../semantic/index.js';
|
|
12
12
|
import { extractTablesFromSql } from '../lineage/sql-parser.js';
|
|
13
13
|
import { extractColumnLineage } from '../lineage/column-lineage.js';
|
|
14
|
+
import { detectOutputDrift } from './output-drift.js';
|
|
14
15
|
import { buildLineageGraph } from '../lineage/builder.js';
|
|
15
16
|
import { detectDomainFlows, getDomainTrustOverview } from '../lineage/domain-lineage.js';
|
|
16
17
|
import { loadSemanticLayerFromDir } from '../semantic/index.js';
|
|
17
18
|
import { DataLexContractRegistry } from '../contracts/index.js';
|
|
18
19
|
import { loadAppDocument, findAppDocuments, findDashboardsForApp, loadDashboardDocument, extractDashboardBlockRefs, appFolderRelPath, } from '../apps/index.js';
|
|
20
|
+
import { loadDbtRunState, applyBlockDataState } from './dbt-freshness.js';
|
|
19
21
|
/**
|
|
20
22
|
* Enumerate every file whose contents could change the manifest output.
|
|
21
23
|
*
|
|
@@ -121,6 +123,14 @@ export function buildManifest(options) {
|
|
|
121
123
|
validateDomains(domains, blocks, businessViews, terms, diagnostics);
|
|
122
124
|
validateBusinessViews(businessViews, blocks, diagnostics);
|
|
123
125
|
validateTermRefs(terms, blocks, businessViews, diagnostics);
|
|
126
|
+
// Cross-artifact trust-conflict detection: two certified terms / blocks that
|
|
127
|
+
// claim the same concept/grain but disagree. Additive `kind: 'conflict'`
|
|
128
|
+
// diagnostics; conservative heuristics avoid false positives.
|
|
129
|
+
diagnostics.push(...detectTrustConflicts(terms, blocks));
|
|
130
|
+
// Output-contract drift detection: a parent block `ref()`s a child and
|
|
131
|
+
// references a column the child no longer outputs. Additive `kind: 'drift'`
|
|
132
|
+
// warnings — never fails the build; freeform composition stays unrestricted.
|
|
133
|
+
diagnostics.push(...detectOutputDrift(blocks));
|
|
124
134
|
// Load semantic layer
|
|
125
135
|
const semanticDir = resolveSemanticPath(projectRoot, config);
|
|
126
136
|
const { metrics, dimensions } = loadSemanticDefinitions(projectRoot, semanticDir);
|
|
@@ -148,11 +158,19 @@ export function buildManifest(options) {
|
|
|
148
158
|
let dbtImport;
|
|
149
159
|
if (options.dbtManifestPath) {
|
|
150
160
|
const filters = options.dbtImportFilters ?? config.dbtImport;
|
|
161
|
+
// Freshness-aware trust — read dbt's run artifacts (run_results.json + source
|
|
162
|
+
// freshness) alongside the manifest, READ-ONLY. Empty/absent → every node is
|
|
163
|
+
// `unknown` and blocks are left untouched (additive, backward compatible).
|
|
164
|
+
const runState = loadDbtRunState(options.dbtManifestPath);
|
|
151
165
|
dbtImport = importDbtManifest(options.dbtManifestPath, sources, referencedTables, {
|
|
152
166
|
maxHops: options.maxDbtHops,
|
|
153
167
|
selectiveThreshold: options.selectiveDbtThreshold ?? 200,
|
|
154
168
|
filters,
|
|
169
|
+
runState,
|
|
155
170
|
});
|
|
171
|
+
// Roll up per-model run state into each certified block's effective
|
|
172
|
+
// `dataState` via its transitive dbt upstreams.
|
|
173
|
+
applyBlockDataState(blocks, sources, dbtImport.dbtDag, runState);
|
|
156
174
|
}
|
|
157
175
|
// Apps & dashboards (consumption layer). Scanned after blocks/notebooks
|
|
158
176
|
// because dashboard refs are resolved against the block path → name map.
|
|
@@ -1039,9 +1057,10 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1039
1057
|
const database = node.database;
|
|
1040
1058
|
const dependsOn = Array.isArray(node.depends_on?.nodes) ? node.depends_on.nodes : [];
|
|
1041
1059
|
const lookupKeys = buildLookupKeys(name, schema, database);
|
|
1060
|
+
const primaryLookupKeys = buildPrimaryLookupKeys(name, schema, database);
|
|
1042
1061
|
const tags = Array.isArray(node.tags) ? node.tags : [];
|
|
1043
1062
|
const path = (node.original_file_path ?? node.path);
|
|
1044
|
-
index.set(uniqueId, { uniqueId, name, type: 'model', schema, database, dependsOn, lookupKeys, tags, path, raw: node });
|
|
1063
|
+
index.set(uniqueId, { uniqueId, name, type: 'model', schema, database, dependsOn, lookupKeys, primaryLookupKeys, tags, path, raw: node });
|
|
1045
1064
|
}
|
|
1046
1065
|
const rawSources = manifest.sources ?? {};
|
|
1047
1066
|
for (const [uniqueId, src] of Object.entries(rawSources)) {
|
|
@@ -1051,9 +1070,10 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1051
1070
|
const schema = src.schema;
|
|
1052
1071
|
const database = src.database;
|
|
1053
1072
|
const lookupKeys = buildLookupKeys(name, schema, database);
|
|
1073
|
+
const primaryLookupKeys = buildPrimaryLookupKeys(name, schema, database);
|
|
1054
1074
|
const tags = Array.isArray(src.tags) ? src.tags : [];
|
|
1055
1075
|
const path = (src.original_file_path ?? src.path);
|
|
1056
|
-
index.set(uniqueId, { uniqueId, name, type: 'source', schema, database, dependsOn: [], lookupKeys, tags, path, raw: src });
|
|
1076
|
+
index.set(uniqueId, { uniqueId, name, type: 'source', schema, database, dependsOn: [], lookupKeys, primaryLookupKeys, tags, path, raw: src });
|
|
1057
1077
|
}
|
|
1058
1078
|
const totalDbtModels = [...index.values()].filter((e) => e.type === 'model').length;
|
|
1059
1079
|
const filters = opts.filters;
|
|
@@ -1073,21 +1093,33 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1073
1093
|
// Multiple dbt nodes may share a short alias, so short-name matches are
|
|
1074
1094
|
// used only when they resolve unambiguously.
|
|
1075
1095
|
const nameToIds = new Map();
|
|
1076
|
-
const
|
|
1096
|
+
const primaryNameToIds = new Map();
|
|
1097
|
+
const addTo = (map, key, uniqueId) => {
|
|
1077
1098
|
const normalized = key.toLowerCase();
|
|
1078
|
-
const existing =
|
|
1099
|
+
const existing = map.get(normalized);
|
|
1079
1100
|
if (existing)
|
|
1080
1101
|
existing.add(uniqueId);
|
|
1081
1102
|
else
|
|
1082
|
-
|
|
1103
|
+
map.set(normalized, new Set([uniqueId]));
|
|
1083
1104
|
};
|
|
1084
1105
|
for (const entry of index.values()) {
|
|
1085
|
-
for (const key of entry.lookupKeys)
|
|
1086
|
-
|
|
1087
|
-
|
|
1106
|
+
for (const key of entry.lookupKeys)
|
|
1107
|
+
addTo(nameToIds, key, entry.uniqueId);
|
|
1108
|
+
for (const key of entry.primaryLookupKeys)
|
|
1109
|
+
addTo(primaryNameToIds, key, entry.uniqueId);
|
|
1088
1110
|
}
|
|
1089
1111
|
const resolveDbtAnchor = (ref) => {
|
|
1090
|
-
|
|
1112
|
+
const refKeys = buildReferencedTableLookupKeys(ref);
|
|
1113
|
+
// Prefer an unambiguous match on EXACT model names before falling back to
|
|
1114
|
+
// dbt role-prefix-stripped aliases — otherwise `dev.customers` is ambiguous
|
|
1115
|
+
// between the `customers` mart and the stripped alias of `stg_customers`,
|
|
1116
|
+
// and the anchor (and its whole upstream subgraph) is silently skipped.
|
|
1117
|
+
for (const key of refKeys) {
|
|
1118
|
+
const ids = primaryNameToIds.get(key);
|
|
1119
|
+
if (ids?.size === 1)
|
|
1120
|
+
return [...ids][0];
|
|
1121
|
+
}
|
|
1122
|
+
for (const key of refKeys) {
|
|
1091
1123
|
const ids = nameToIds.get(key);
|
|
1092
1124
|
if (ids?.size === 1)
|
|
1093
1125
|
return [...ids][0];
|
|
@@ -1172,6 +1204,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1172
1204
|
type: v.data_type,
|
|
1173
1205
|
}))
|
|
1174
1206
|
: undefined;
|
|
1207
|
+
const modelRunState = opts.runState?.byUniqueId.get(uid);
|
|
1175
1208
|
if (!sources[tableName]) {
|
|
1176
1209
|
sources[tableName] = { name: tableName, origin: 'dbt', referencedBy: [] };
|
|
1177
1210
|
}
|
|
@@ -1187,6 +1220,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1187
1220
|
{ name: v.name, description: v.description, type: v.data_type },
|
|
1188
1221
|
]))
|
|
1189
1222
|
: undefined,
|
|
1223
|
+
runState: modelRunState,
|
|
1190
1224
|
};
|
|
1191
1225
|
if (sources[tableName].origin === 'sql')
|
|
1192
1226
|
sources[tableName].origin = 'dbt';
|
|
@@ -1202,6 +1236,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1202
1236
|
database: entry.database,
|
|
1203
1237
|
materialized: node.config?.materialized,
|
|
1204
1238
|
description: node.description,
|
|
1239
|
+
runState: modelRunState,
|
|
1205
1240
|
});
|
|
1206
1241
|
for (const dep of selectedDeps) {
|
|
1207
1242
|
dbtDagEdges.push({ source: dep, target: uid });
|
|
@@ -1215,11 +1250,13 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1215
1250
|
if (!sources[tableName]) {
|
|
1216
1251
|
sources[tableName] = { name: tableName, origin: 'dbt', referencedBy: [] };
|
|
1217
1252
|
}
|
|
1253
|
+
const sourceRunState = opts.runState?.byUniqueId.get(uid);
|
|
1218
1254
|
sources[tableName].dbtModel = {
|
|
1219
1255
|
uniqueId: uid,
|
|
1220
1256
|
database: entry.database,
|
|
1221
1257
|
schema: entry.schema,
|
|
1222
1258
|
description: src.description,
|
|
1259
|
+
runState: sourceRunState,
|
|
1223
1260
|
};
|
|
1224
1261
|
dbtDagModels.push({
|
|
1225
1262
|
uniqueId: uid,
|
|
@@ -1229,6 +1266,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1229
1266
|
schema: entry.schema,
|
|
1230
1267
|
database: entry.database,
|
|
1231
1268
|
description: src.description,
|
|
1269
|
+
runState: sourceRunState,
|
|
1232
1270
|
});
|
|
1233
1271
|
sourcesImported++;
|
|
1234
1272
|
}
|
|
@@ -1243,9 +1281,23 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
|
|
|
1243
1281
|
maxHops: opts.maxHops,
|
|
1244
1282
|
importedAt: new Date().toISOString(),
|
|
1245
1283
|
dbtDag: { models: dbtDagModels, edges: dbtDagEdges },
|
|
1284
|
+
runResultsPath: opts.runState?.runResultsPath,
|
|
1246
1285
|
};
|
|
1247
1286
|
}
|
|
1248
1287
|
/** Build all normalised lookup keys for a dbt node name. */
|
|
1288
|
+
/**
|
|
1289
|
+
* Lookup keys from a dbt node's EXACT name (no role-prefix stripping), so a
|
|
1290
|
+
* mart `customers` and a staging `stg_customers` never collide on the same key.
|
|
1291
|
+
*/
|
|
1292
|
+
function buildPrimaryLookupKeys(name, schema, database) {
|
|
1293
|
+
const alias = name.toLowerCase();
|
|
1294
|
+
const keys = new Set([alias]);
|
|
1295
|
+
if (schema)
|
|
1296
|
+
keys.add(`${schema}.${alias}`.toLowerCase());
|
|
1297
|
+
if (schema && database)
|
|
1298
|
+
keys.add(`${database}.${schema}.${alias}`.toLowerCase());
|
|
1299
|
+
return [...keys];
|
|
1300
|
+
}
|
|
1249
1301
|
function buildLookupKeys(name, schema, database) {
|
|
1250
1302
|
const aliases = new Set([name.toLowerCase()]);
|
|
1251
1303
|
const stripped = stripDbtRolePrefix(name);
|
|
@@ -1816,8 +1868,63 @@ function blockDeclToManifestBlock(block, filePath) {
|
|
|
1816
1868
|
unresolved: c.unresolved,
|
|
1817
1869
|
}))
|
|
1818
1870
|
: undefined,
|
|
1871
|
+
outputContract: buildOutputContract(Array.isArray(block.outputs) ? block.outputs : undefined, columnLineage),
|
|
1819
1872
|
};
|
|
1820
1873
|
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Build a block's typed `outputContract` — the columns a parent that `ref()`s
|
|
1876
|
+
* this block can rely on. Additive and distinct from `outputs` (column-lineage
|
|
1877
|
+
* shaped). Source of truth, in priority order:
|
|
1878
|
+
*
|
|
1879
|
+
* 1. `declaredOutputs` (reviewer-declared field names) when present. These
|
|
1880
|
+
* are the contract the block author committed to, so they win. Column
|
|
1881
|
+
* lineage is used only to enrich `role`/`type` for matching names.
|
|
1882
|
+
* 2. Otherwise, the resolved column-lineage output columns — but only when
|
|
1883
|
+
* the projection is fully resolved (no `*`/unresolved entries). A star or
|
|
1884
|
+
* unparsed projection means the schema is open, so we emit no contract and
|
|
1885
|
+
* drift detection stays silent for this block (conservative).
|
|
1886
|
+
*
|
|
1887
|
+
* Returns undefined when neither source yields a usable schema, keeping the
|
|
1888
|
+
* field optional and backward-compatible.
|
|
1889
|
+
*/
|
|
1890
|
+
function buildOutputContract(declaredOutputs, columnLineage) {
|
|
1891
|
+
const roleByName = new Map();
|
|
1892
|
+
if (columnLineage?.parsed) {
|
|
1893
|
+
for (const col of columnLineage.columns) {
|
|
1894
|
+
if (col.isAggregate)
|
|
1895
|
+
roleByName.set(col.name.toLowerCase(), 'metric');
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
if (Array.isArray(declaredOutputs) && declaredOutputs.length > 0) {
|
|
1899
|
+
const seen = new Set();
|
|
1900
|
+
const contract = [];
|
|
1901
|
+
for (const raw of declaredOutputs) {
|
|
1902
|
+
const name = typeof raw === 'string' ? raw.trim() : '';
|
|
1903
|
+
if (!name || seen.has(name.toLowerCase()))
|
|
1904
|
+
continue;
|
|
1905
|
+
seen.add(name.toLowerCase());
|
|
1906
|
+
contract.push({ name, role: roleByName.get(name.toLowerCase()) });
|
|
1907
|
+
}
|
|
1908
|
+
return contract.length > 0 ? contract : undefined;
|
|
1909
|
+
}
|
|
1910
|
+
if (!columnLineage?.parsed || columnLineage.columns.length === 0)
|
|
1911
|
+
return undefined;
|
|
1912
|
+
// Only emit a derived contract when every output column is resolved — a star
|
|
1913
|
+
// or unresolved entry means we cannot enumerate the real output schema.
|
|
1914
|
+
if (columnLineage.columns.some((c) => c.unresolved || c.name === '*' || c.name.endsWith('.*'))) {
|
|
1915
|
+
return undefined;
|
|
1916
|
+
}
|
|
1917
|
+
const seen = new Set();
|
|
1918
|
+
const contract = [];
|
|
1919
|
+
for (const col of columnLineage.columns) {
|
|
1920
|
+
const name = col.name?.trim();
|
|
1921
|
+
if (!name || seen.has(name.toLowerCase()))
|
|
1922
|
+
continue;
|
|
1923
|
+
seen.add(name.toLowerCase());
|
|
1924
|
+
contract.push({ name, role: col.isAggregate ? 'metric' : undefined });
|
|
1925
|
+
}
|
|
1926
|
+
return contract.length > 0 ? contract : undefined;
|
|
1927
|
+
}
|
|
1821
1928
|
function domainDeclToManifestDomain(domain, filePath) {
|
|
1822
1929
|
return {
|
|
1823
1930
|
name: domain.name,
|