@duckcodeailabs/dql-cli 1.6.4 → 1.6.5

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.
@@ -143,6 +143,19 @@ export declare function createAppPackage(projectRoot: string, input: AppCreateRe
143
143
  ok: false;
144
144
  error: string;
145
145
  };
146
+ declare function selectedBlockContext(context: unknown): Record<string, unknown> | null;
147
+ declare function buildPreviewMetricSnapshot(preview: Record<string, unknown>, fallbackTitle?: string): Record<string, unknown>;
148
+ declare function buildPreviewDriverCards(preview: Record<string, unknown>, intent: LocalAppInvestigationIntent): Array<Record<string, unknown>>;
149
+ declare function buildDeterministicInvestigationSql(projectRoot: string, input: {
150
+ question: string;
151
+ intent: LocalAppInvestigationIntent;
152
+ selected: Record<string, unknown> | null;
153
+ sourceBlockId?: string;
154
+ }): {
155
+ sql: string;
156
+ sourceBlockPath: string;
157
+ sourceBlockName: string;
158
+ } | undefined;
146
159
  declare function loadAppById(projectRoot: string, id: string): {
147
160
  app: AppDocument;
148
161
  dashboards: Array<{
@@ -187,5 +200,11 @@ export declare function previewNotebookForApp(projectRoot: string, appId: string
187
200
  status: number;
188
201
  error: string;
189
202
  };
203
+ export declare const __test__: {
204
+ buildPreviewDriverCards: typeof buildPreviewDriverCards;
205
+ buildPreviewMetricSnapshot: typeof buildPreviewMetricSnapshot;
206
+ buildDeterministicInvestigationSql: typeof buildDeterministicInvestigationSql;
207
+ selectedBlockContext: typeof selectedBlockContext;
208
+ };
190
209
  export {};
191
210
  //# sourceMappingURL=apps-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apps-api.d.ts","sourceRoot":"","sources":["../src/apps-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apps-api.d.ts","sourceRoot":"","sources":["../src/apps-api.ts"],"names":[],"mappings":"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"}
package/dist/apps-api.js CHANGED
@@ -1063,13 +1063,24 @@ async function runAppInvestigation(ctx, storage, investigation, input = {}) {
1063
1063
  const previews = buildContextPreviews(selected);
1064
1064
  let metricSnapshot = buildMetricSnapshot(selected);
1065
1065
  let driverCards = buildDriverCards(selected, intent);
1066
+ const sourceTileId = investigation.sourceTileId ?? selectedContextString(context, 'tileId');
1067
+ const sourceBlockId = investigation.sourceBlockId ?? selectedContextString(context, 'blockId');
1068
+ const deterministicGeneration = generatedSql
1069
+ ? undefined
1070
+ : buildDeterministicInvestigationSql(ctx.projectRoot, {
1071
+ question,
1072
+ intent,
1073
+ selected,
1074
+ sourceBlockId,
1075
+ });
1076
+ generatedSql = generatedSql || deterministicGeneration?.sql;
1066
1077
  const agentGeneration = generatedSql
1067
1078
  ? undefined
1068
1079
  : await generateInvestigationSql(ctx, {
1069
1080
  appId: investigation.appId,
1070
1081
  dashboardId: investigation.dashboardId ?? selectedString(context, 'dashboardId'),
1071
- sourceTileId: investigation.sourceTileId ?? selectedContextString(context, 'tileId'),
1072
- sourceBlockId: investigation.sourceBlockId ?? selectedContextString(context, 'blockId'),
1082
+ sourceTileId,
1083
+ sourceBlockId,
1073
1084
  title: investigation.title,
1074
1085
  question,
1075
1086
  intent,
@@ -1095,6 +1106,9 @@ async function runAppInvestigation(ctx, storage, investigation, input = {}) {
1095
1106
  generatedSql: generatedSql || undefined,
1096
1107
  sqlExecuted: Boolean(sqlEvidence.preview),
1097
1108
  sqlError,
1109
+ generationSource: deterministicGeneration ? 'selected_block_metadata' : agentGeneration?.providerUsed ? 'ai_provider' : generatedSql ? 'provided_sql' : 'context_only',
1110
+ sourceBlockPath: deterministicGeneration?.sourceBlockPath,
1111
+ sourceBlockName: deterministicGeneration?.sourceBlockName,
1098
1112
  providerUsed: agentGeneration?.providerUsed,
1099
1113
  },
1100
1114
  certifiedContext: {
@@ -1102,8 +1116,9 @@ async function runAppInvestigation(ctx, storage, investigation, input = {}) {
1102
1116
  appName: appInfo?.app.name,
1103
1117
  dashboardId: investigation.dashboardId,
1104
1118
  dashboardTitle: selectedString(context, 'dashboardTitle'),
1105
- sourceTileId: investigation.sourceTileId ?? selectedContextString(context, 'tileId'),
1106
- sourceBlockId: investigation.sourceBlockId ?? selectedContextString(context, 'blockId'),
1119
+ sourceTileId,
1120
+ sourceBlockId,
1121
+ sourceBlockPath: deterministicGeneration?.sourceBlockPath ?? selectedString(selected, 'blockPath'),
1107
1122
  certificationStatus: selectedString(selected, 'certificationStatus'),
1108
1123
  },
1109
1124
  assumptions: investigationAssumptions(intent, selected, generatedSql, sqlError),
@@ -1174,7 +1189,14 @@ function safeIntentContext(context) {
1174
1189
  }
1175
1190
  function selectedBlockContext(context) {
1176
1191
  const root = asRecord(context);
1177
- return asRecord(root?.selectedBlock);
1192
+ const selected = asRecord(root?.selectedBlock);
1193
+ if (selected)
1194
+ return selected;
1195
+ if (!root)
1196
+ return null;
1197
+ const hasSelectedTileContext = ['blockId', 'blockPath', 'tileId', 'certificationStatus', 'resultSample', 'rowCount']
1198
+ .some((key) => root[key] !== undefined && root[key] !== null);
1199
+ return hasSelectedTileContext ? root : null;
1178
1200
  }
1179
1201
  function selectedContextString(context, key) {
1180
1202
  return selectedString(selectedBlockContext(context), key);
@@ -1274,7 +1296,8 @@ function buildPreviewMetricSnapshot(preview, fallbackTitle) {
1274
1296
  };
1275
1297
  }
1276
1298
  const currentColumn = pickColumn(numericColumns, [/^current_/i, /current.*(revenue|value|amount|total|orders?)/i])
1277
- ?? pickColumn(numericColumns, [/(revenue|value|amount|total|orders?|count)$/i])
1299
+ ?? pickColumn(numericColumns, [/^total_/i, /(revenue|value|amount|total|orders?|points?|goals?|assists?|rebounds?|score|games_played)$/i])
1300
+ ?? pickColumn(numericColumns, [/(count|row_count)$/i])
1278
1301
  ?? numericColumns[0];
1279
1302
  const baselineColumn = pickColumn(numericColumns, [/^baseline_/i, /baseline.*(revenue|value|amount|total|orders?)/i]);
1280
1303
  const deltaColumn = pickColumn(numericColumns, [/(delta|change|variance|diff|contribution)/i]);
@@ -1302,7 +1325,8 @@ function buildPreviewDriverCards(preview, intent) {
1302
1325
  }
1303
1326
  const numericColumns = columns.filter((column) => rows.some((row) => typeofNumber(row[column]) !== null));
1304
1327
  const contributionColumn = pickColumn(numericColumns, [/(delta|change|variance|diff|contribution)/i])
1305
- ?? pickColumn(numericColumns, [/^current_/i, /(revenue|value|amount|total|orders?|count)$/i])
1328
+ ?? pickColumn(numericColumns, [/^current_/i, /^total_/i, /(revenue|value|amount|total|orders?|points?|goals?|assists?|rebounds?|score|games_played)$/i])
1329
+ ?? pickColumn(numericColumns, [/(count|row_count)$/i])
1306
1330
  ?? numericColumns[0];
1307
1331
  const dimensionColumn = columns.find((column) => column !== contributionColumn && rows.some((row) => typeof row[column] === 'string'));
1308
1332
  if (!contributionColumn) {
@@ -1355,6 +1379,229 @@ function sumNumericRows(rows, column) {
1355
1379
  }
1356
1380
  return found ? total : null;
1357
1381
  }
1382
+ function buildDeterministicInvestigationSql(projectRoot, input) {
1383
+ if (input.intent === 'trust_gap_review')
1384
+ return undefined;
1385
+ const block = resolveSelectedBlock(projectRoot, input.selected, input.sourceBlockId);
1386
+ if (!block)
1387
+ return undefined;
1388
+ const source = readFileSync(join(projectRoot, block.path), 'utf-8');
1389
+ const blockSql = extractDqlQuery(source);
1390
+ if (!blockSql || /\{\{/.test(blockSql) || !isReadOnlySql(blockSql))
1391
+ return undefined;
1392
+ const rows = selectedRows(input.selected);
1393
+ const columns = selectedColumns(input.selected, rows);
1394
+ const sourceSql = stripTopLevelOrderAndLimit(blockSql);
1395
+ const profile = profileResultColumns(columns, rows);
1396
+ const measure = chooseMeasureColumn(profile);
1397
+ if (!measure)
1398
+ return undefined;
1399
+ const dimension = chooseDimensionColumn(input.question, profile, input.intent);
1400
+ const sourceCte = `WITH dql_source AS (\n${sourceSql}\n)`;
1401
+ if (input.intent === 'entity_drilldown') {
1402
+ const entity = inferEntityFilter(input.question, profile, rows);
1403
+ const orderBy = `ORDER BY ${quoteSqlIdentifier(measure.name)} DESC`;
1404
+ const where = entity ? `\nWHERE ${quoteSqlIdentifier(entity.column)} IS NOT NULL AND LOWER(CAST(${quoteSqlIdentifier(entity.column)} AS VARCHAR)) LIKE ${sqlStringLiteral(`%${entity.value.toLowerCase()}%`)}` : '';
1405
+ return {
1406
+ sql: `${sourceCte}\nSELECT *\nFROM dql_source${where}\n${orderBy}\nLIMIT 100`,
1407
+ sourceBlockPath: block.path,
1408
+ sourceBlockName: block.name,
1409
+ };
1410
+ }
1411
+ if (input.intent === 'anomaly_investigation') {
1412
+ const timeDimension = chooseTimeDimension(profile) ?? dimension;
1413
+ const rankExpr = `${measureAgg(measure)}(${quoteSqlIdentifier(measure.name)})`;
1414
+ if (timeDimension) {
1415
+ return {
1416
+ sql: [
1417
+ sourceCte,
1418
+ ', dql_trend AS (',
1419
+ ` SELECT ${quoteSqlIdentifier(timeDimension.name)} AS ${quoteSqlIdentifier(timeDimension.name)}, ${rankExpr} AS ${quoteSqlIdentifier(measure.name)}`,
1420
+ ' FROM dql_source',
1421
+ ` GROUP BY ${quoteSqlIdentifier(timeDimension.name)}`,
1422
+ '), dql_deltas AS (',
1423
+ ` SELECT ${quoteSqlIdentifier(timeDimension.name)}, ${quoteSqlIdentifier(measure.name)}, LAG(${quoteSqlIdentifier(measure.name)}) OVER (ORDER BY ${quoteSqlIdentifier(timeDimension.name)}) AS baseline_${safeAlias(measure.name)}`,
1424
+ ' FROM dql_trend',
1425
+ ')',
1426
+ `SELECT *, ${quoteSqlIdentifier(measure.name)} - baseline_${safeAlias(measure.name)} AS delta_${safeAlias(measure.name)}`,
1427
+ 'FROM dql_deltas',
1428
+ `ORDER BY ABS(COALESCE(delta_${safeAlias(measure.name)}, 0)) DESC`,
1429
+ 'LIMIT 20',
1430
+ ].join('\n'),
1431
+ sourceBlockPath: block.path,
1432
+ sourceBlockName: block.name,
1433
+ };
1434
+ }
1435
+ }
1436
+ if (!dimension)
1437
+ return undefined;
1438
+ const aggregate = `${measureAgg(measure)}(${quoteSqlIdentifier(measure.name)})`;
1439
+ const label = quoteSqlIdentifier(dimension.name);
1440
+ return {
1441
+ sql: [
1442
+ sourceCte,
1443
+ `SELECT ${label} AS ${label}, ${aggregate} AS ${quoteSqlIdentifier(measure.name)}, COUNT(*) AS ${quoteSqlIdentifier('row_count')}`,
1444
+ 'FROM dql_source',
1445
+ `GROUP BY ${label}`,
1446
+ `ORDER BY ABS(COALESCE(${quoteSqlIdentifier(measure.name)}, 0)) DESC`,
1447
+ 'LIMIT 20',
1448
+ ].join('\n'),
1449
+ sourceBlockPath: block.path,
1450
+ sourceBlockName: block.name,
1451
+ };
1452
+ }
1453
+ function resolveSelectedBlock(projectRoot, selected, sourceBlockId) {
1454
+ const selectedPath = selectedString(selected, 'blockPath');
1455
+ const candidates = collectBlockCandidates(projectRoot);
1456
+ if (selectedPath) {
1457
+ const normalizedPath = selectedPath.replace(/^\/+/, '');
1458
+ const found = candidates.find((block) => block.path === normalizedPath);
1459
+ if (found)
1460
+ return found;
1461
+ if (normalizedPath.startsWith('blocks/') && existsSync(join(projectRoot, normalizedPath))) {
1462
+ const source = readFileSync(join(projectRoot, normalizedPath), 'utf-8');
1463
+ const name = matchString(source, /block\s+"([^"]+)"/) ?? titleFromPath(normalizedPath);
1464
+ return {
1465
+ id: name,
1466
+ name,
1467
+ domain: matchString(source, /domain\s*=\s*"([^"]+)"/) ?? 'uncategorized',
1468
+ status: matchString(source, /status\s*=\s*"([^"]+)"/) ?? 'draft',
1469
+ owner: matchString(source, /owner\s*=\s*"([^"]+)"/),
1470
+ tags: matchArray(source, /tags\s*=\s*\[([^\]]*)\]/),
1471
+ path: normalizedPath,
1472
+ lastModified: statSyncSafe(join(projectRoot, normalizedPath))?.mtime.toISOString() ?? new Date(0).toISOString(),
1473
+ description: matchString(source, /description\s*=\s*"((?:[^"\\]|\\.)*)"/) ?? '',
1474
+ llmContext: matchString(source, /llmContext\s*=\s*"((?:[^"\\]|\\.)*)"/),
1475
+ chartType: matchString(source, /chart\s*=\s*"([^"]+)"/) ?? undefined,
1476
+ score: 0,
1477
+ reasons: [],
1478
+ };
1479
+ }
1480
+ }
1481
+ const id = cleanString(sourceBlockId) || selectedString(selected, 'blockId');
1482
+ if (!id)
1483
+ return undefined;
1484
+ return candidates.find((block) => block.id === id || block.name === id || block.path === id);
1485
+ }
1486
+ function profileResultColumns(columns, rows) {
1487
+ return columns.map((name) => {
1488
+ const lower = name.toLowerCase();
1489
+ const numeric = rows.length === 0 ? !isLikelyTextColumn(lower) : rows.some((row) => typeofNumber(row[name]) !== null);
1490
+ const text = rows.some((row) => typeof row[name] === 'string' && String(row[name]).trim().length > 0);
1491
+ const time = /\b(season|year|month|week|quarter|date|day)\b/i.test(lower);
1492
+ const identifier = /\b(id|key|uuid|number)\b/i.test(lower) && !time;
1493
+ const measureName = /\b(total|sum|amount|revenue|sales|points?|goals?|assists?|rebounds?|count|avg|average|rate|pct|percent|score|value|delta|change|variance)\b/i.test(lower);
1494
+ const dimensionName = /\b(name|type|segment|region|market|category|status|player|customer|account|team|season|year|month|week|quarter|date)\b/i.test(lower);
1495
+ const measure = numeric && measureName && !identifier && !time;
1496
+ const dimension = !measure && (text || time || dimensionName || !numeric);
1497
+ return { name, lower, numeric, text, dimension, measure, time };
1498
+ });
1499
+ }
1500
+ function chooseMeasureColumn(columns) {
1501
+ const candidates = columns.filter((column) => column.measure);
1502
+ return candidates.find((column) => /\b(delta|change|variance|contribution)\b/i.test(column.lower))
1503
+ ?? candidates.find((column) => /\b(total_points|total_revenue|total_amount|total|revenue|amount|sales|points|goals)\b/i.test(column.lower))
1504
+ ?? candidates.find((column) => /\b(count|value|score|avg|average|rate|pct|percent)\b/i.test(column.lower))
1505
+ ?? columns.find((column) => column.numeric && !column.dimension);
1506
+ }
1507
+ function chooseDimensionColumn(question, columns, intent) {
1508
+ const dimensions = columns.filter((column) => column.dimension);
1509
+ const questionTokens = new Set(question.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean));
1510
+ const mentioned = dimensions.find((column) => column.lower.split(/[^a-z0-9]+/).some((token) => questionTokens.has(token)));
1511
+ if (mentioned)
1512
+ return mentioned;
1513
+ const timeDimension = chooseTimeDimension(columns);
1514
+ if ((intent === 'diagnose_change' || intent === 'segment_compare' || intent === 'anomaly_investigation') && timeDimension)
1515
+ return timeDimension;
1516
+ return dimensions.find((column) => column.text && !column.time)
1517
+ ?? timeDimension
1518
+ ?? dimensions[0];
1519
+ }
1520
+ function chooseTimeDimension(columns) {
1521
+ return columns.find((column) => column.time);
1522
+ }
1523
+ function inferEntityFilter(question, columns, rows) {
1524
+ const textDimensions = columns.filter((column) => column.dimension && (column.text || /\b(name|player|customer|account|team)\b/i.test(column.lower)));
1525
+ const lowerQuestion = question.toLowerCase();
1526
+ for (const column of textDimensions) {
1527
+ for (const row of rows) {
1528
+ const value = cleanString(row[column.name]);
1529
+ if (value && lowerQuestion.includes(value.toLowerCase()))
1530
+ return { column: column.name, value };
1531
+ }
1532
+ }
1533
+ const named = question.match(/\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b/);
1534
+ const value = named?.[1]?.trim();
1535
+ const column = textDimensions[0];
1536
+ return value && column ? { column: column.name, value } : undefined;
1537
+ }
1538
+ function measureAgg(column) {
1539
+ return /\b(avg|average|rate|pct|percent|per_)\b/i.test(column.lower) ? 'AVG' : 'SUM';
1540
+ }
1541
+ function extractDqlQuery(source) {
1542
+ const tripleQuoteMatch = source.match(/query\s*=\s*"""([\s\S]*?)"""/i);
1543
+ if (tripleQuoteMatch)
1544
+ return tripleQuoteMatch[1].trim() || null;
1545
+ const singleQuoteMatch = source.match(/query\s*=\s*"((?:[^"\\]|\\.)*)"/i);
1546
+ if (singleQuoteMatch)
1547
+ return singleQuoteMatch[1].replace(/\\"/g, '"').trim() || null;
1548
+ return null;
1549
+ }
1550
+ function stripTopLevelOrderAndLimit(sql) {
1551
+ let next = sql.trim().replace(/;+\s*$/g, '');
1552
+ const limitIndex = findLastTopLevelKeyword(next, 'limit');
1553
+ if (limitIndex >= 0 && /^\s+limit\s+\d+\s*$/i.test(next.slice(limitIndex))) {
1554
+ next = next.slice(0, limitIndex).trim();
1555
+ }
1556
+ const orderIndex = findLastTopLevelKeyword(next, 'order by');
1557
+ if (orderIndex >= 0)
1558
+ next = next.slice(0, orderIndex).trim();
1559
+ return next;
1560
+ }
1561
+ function findLastTopLevelKeyword(sql, keyword) {
1562
+ const lower = sql.toLowerCase();
1563
+ const target = keyword.toLowerCase();
1564
+ let depth = 0;
1565
+ let quote = null;
1566
+ let last = -1;
1567
+ for (let i = 0; i < lower.length; i += 1) {
1568
+ const char = lower[i];
1569
+ if (quote) {
1570
+ if (char === quote && lower[i - 1] !== '\\')
1571
+ quote = null;
1572
+ continue;
1573
+ }
1574
+ if (char === '"' || char === "'" || char === '`') {
1575
+ quote = char;
1576
+ continue;
1577
+ }
1578
+ if (char === '(')
1579
+ depth += 1;
1580
+ if (char === ')')
1581
+ depth = Math.max(0, depth - 1);
1582
+ if (depth === 0 && lower.startsWith(target, i) && isKeywordBoundary(lower, i - 1) && isKeywordBoundary(lower, i + target.length)) {
1583
+ last = i;
1584
+ }
1585
+ }
1586
+ return last;
1587
+ }
1588
+ function isKeywordBoundary(value, index) {
1589
+ if (index < 0 || index >= value.length)
1590
+ return true;
1591
+ return /[^a-z0-9_]/i.test(value[index]);
1592
+ }
1593
+ function quoteSqlIdentifier(identifier) {
1594
+ return `"${identifier.replace(/"/g, '""')}"`;
1595
+ }
1596
+ function sqlStringLiteral(value) {
1597
+ return `'${value.replace(/'/g, "''")}'`;
1598
+ }
1599
+ function safeAlias(identifier) {
1600
+ return identifier.replace(/[^a-z0-9_]+/gi, '_').replace(/^_+|_+$/g, '') || 'value';
1601
+ }
1602
+ function isLikelyTextColumn(value) {
1603
+ return /\b(name|type|segment|region|market|category|status|player|customer|account|team)\b/i.test(value);
1604
+ }
1358
1605
  async function generateInvestigationSql(ctx, input) {
1359
1606
  if (!ctx.generateInvestigationSql)
1360
1607
  return undefined;
@@ -1432,13 +1679,26 @@ function buildGeneratedSqlPreview(result, generatedSql) {
1432
1679
  };
1433
1680
  }
1434
1681
  function isReadOnlySql(sql) {
1435
- const trimmed = sql.trim().replace(/;+\s*$/g, '');
1682
+ const trimmed = stripLeadingSqlComments(sql).replace(/;+\s*$/g, '');
1436
1683
  if (!/^(select|with)\b/i.test(trimmed))
1437
1684
  return false;
1438
1685
  if (/;\s*\S/.test(trimmed))
1439
1686
  return false;
1440
1687
  return !/\b(insert|update|delete|merge|drop|alter|create|truncate|copy|grant|revoke|call|execute|attach|detach)\b/i.test(trimmed);
1441
1688
  }
1689
+ function stripLeadingSqlComments(sql) {
1690
+ let next = sql.trim();
1691
+ while (next.startsWith('--') || next.startsWith('/*')) {
1692
+ if (next.startsWith('--')) {
1693
+ const lineEnd = next.indexOf('\n');
1694
+ next = lineEnd >= 0 ? next.slice(lineEnd + 1).trimStart() : '';
1695
+ continue;
1696
+ }
1697
+ const blockEnd = next.indexOf('*/');
1698
+ next = blockEnd >= 0 ? next.slice(blockEnd + 2).trimStart() : '';
1699
+ }
1700
+ return next;
1701
+ }
1442
1702
  function boundedPreviewSql(sql) {
1443
1703
  return `SELECT * FROM (${sql.trim().replace(/;+\s*$/g, '')}) AS dql_research_preview LIMIT 100`;
1444
1704
  }
@@ -1525,7 +1785,11 @@ function titleFromInvestigation(question, selected) {
1525
1785
  return base.replace(/\s+/g, ' ').slice(0, 90);
1526
1786
  }
1527
1787
  function selectedRows(selected) {
1528
- const rows = Array.isArray(selected?.sampleRows) ? selected?.sampleRows : selected?.rows;
1788
+ const rows = Array.isArray(selected?.sampleRows)
1789
+ ? selected?.sampleRows
1790
+ : Array.isArray(selected?.resultSample)
1791
+ ? selected?.resultSample
1792
+ : selected?.rows;
1529
1793
  if (!Array.isArray(rows))
1530
1794
  return [];
1531
1795
  return rows.map(asRecord).filter((row) => Boolean(row)).slice(0, 100);
@@ -2209,6 +2473,12 @@ async function readJson(req) {
2209
2473
  req.on('error', reject);
2210
2474
  });
2211
2475
  }
2476
+ export const __test__ = {
2477
+ buildPreviewDriverCards,
2478
+ buildPreviewMetricSnapshot,
2479
+ buildDeterministicInvestigationSql,
2480
+ selectedBlockContext,
2481
+ };
2212
2482
  // reference unused parseAppDocument/readFileSync to keep import stable for forward use
2213
2483
  void parseAppDocument;
2214
2484
  void readFileSync;