gitnexus 1.6.7-rc.1 → 1.6.7-rc.3
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/_shared/graph/types.d.ts +18 -2
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +2 -2
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +10 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/cli/analyze.js +7 -5
- package/dist/cli/optional-grammars.d.ts +18 -4
- package/dist/cli/optional-grammars.js +51 -8
- package/dist/core/ingestion/languages/dart/query.js +11 -3
- package/dist/core/ingestion/languages/kotlin/query.js +11 -3
- package/dist/core/ingestion/languages/swift/query.js +11 -3
- package/dist/core/ingestion/model/registration-table.js +3 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/index.js +1 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +11 -3
- package/dist/core/ingestion/pipeline-phases/registry.d.ts +56 -0
- package/dist/core/ingestion/pipeline-phases/registry.js +48 -0
- package/dist/core/ingestion/pipeline.d.ts +21 -0
- package/dist/core/ingestion/pipeline.js +28 -20
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +11 -0
- package/dist/core/ingestion/taint/source-sink-config.d.ts +36 -0
- package/dist/core/ingestion/taint/source-sink-config.js +16 -0
- package/dist/core/ingestion/taint/source-sink-registry.d.ts +27 -0
- package/dist/core/ingestion/taint/source-sink-registry.js +35 -0
- package/dist/core/ingestion/workers/parse-worker.js +13 -0
- package/dist/core/lbug/csv-generator.js +14 -0
- package/dist/core/lbug/lbug-adapter.js +12 -0
- package/dist/core/lbug/schema.d.ts +2 -1
- package/dist/core/lbug/schema.js +22 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +8 -0
- package/dist/core/tree-sitter/parser-loader.js +70 -0
- package/dist/server/api.d.ts +1 -0
- package/dist/server/api.js +17 -2
- package/package.json +1 -1
- package/scripts/spikes/s1-reaching-def-index-bench.ts +151 -0
- package/scripts/spikes/s2-postdom-prototype.ts +162 -0
- package/web/assets/{agent-EkX7webX.js → agent-CKPMqImC.js} +1 -1
- package/web/assets/{architectureDiagram-UL44E2DR-BoczH8cv.js → architectureDiagram-UL44E2DR-D1EXI0zA.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-By5HB3b-.js → chunk-LCXTWHL2-Dfmux4m1.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-BQE_DkpL.js → chunk-RG4AUYOV-CAkzcoRj.js} +1 -1
- package/web/assets/{classDiagram-KGZ6W3CR-Dj_02GUH.js → classDiagram-KGZ6W3CR-Bv93af_b.js} +1 -1
- package/web/assets/{classDiagram-v2-72OJOZXJ-D-NArMpf.js → classDiagram-v2-72OJOZXJ-CLfEqHUa.js} +1 -1
- package/web/assets/{diagram-3NCE3AQN-VXtXDO95.js → diagram-3NCE3AQN-BqAtKUpW.js} +1 -1
- package/web/assets/{diagram-GF46GFSD-B1qab2Ah.js → diagram-GF46GFSD-CaBG6n6o.js} +1 -1
- package/web/assets/{diagram-QXG6HAR7-BSCHGzzT.js → diagram-QXG6HAR7-CZ-O3rcV.js} +1 -1
- package/web/assets/{diagram-WEQXMOUZ-DHtknmNT.js → diagram-WEQXMOUZ-P4lSL4GH.js} +1 -1
- package/web/assets/{erDiagram-L5TCEMPS-Db-IYTmm.js → erDiagram-L5TCEMPS-QN2eEP1e.js} +1 -1
- package/web/assets/{flowDiagram-H6V6AXG4-BHDoyVoW.js → flowDiagram-H6V6AXG4-CXbXImlN.js} +1 -1
- package/web/assets/{index-BR0IhgbR.js → index-CG6q8eTs.js} +6 -6
- package/web/assets/{infoDiagram-3YFTVSEB-6h8wbWEe.js → infoDiagram-3YFTVSEB-DmLICZx1.js} +1 -1
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-Dx2OkPk1.js → ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-CIIEZfqV.js → kanban-definition-75IXJCU3-DgDi9oJT.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-DUAqydRq.js → mindmap-definition-2TDM6QVE-CwR5sBB-.js} +1 -1
- package/web/assets/{pieDiagram-CU6KROY3-BntYuYZa.js → pieDiagram-CU6KROY3-By8g6f6B.js} +1 -1
- package/web/assets/{requirementDiagram-JXO7QTGE-QCD6I29n.js → requirementDiagram-JXO7QTGE-7oDcJ1_J.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-DScFwLz2.js → sequenceDiagram-VS2MUI6T-FySeKCUy.js} +1 -1
- package/web/assets/{stateDiagram-7D4R322I-AkRAm6Hl.js → stateDiagram-7D4R322I-CB2nABwH.js} +1 -1
- package/web/assets/{stateDiagram-v2-36443NZ5-BCK1vigR.js → stateDiagram-v2-36443NZ5-COBGd2RL.js} +1 -1
- package/web/assets/{timeline-definition-O6YCAMPW-BvpIVXjX.js → timeline-definition-O6YCAMPW-Ds2CnVZK.js} +1 -1
- package/web/assets/{vennDiagram-MWXL3ELB-BYGWk6dK.js → vennDiagram-MWXL3ELB-DUIEwXWp.js} +1 -1
- package/web/assets/{wardleyDiagram-CUQ6CDDI-Dk1z7H2T.js → wardleyDiagram-CUQ6CDDI-DEiFPQih.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-2N6NCSWN.js → xychartDiagram-N2JHSOCM-BUbayhST.js} +1 -1
- package/web/index.html +1 -1
|
@@ -27,6 +27,7 @@ import { getPhaseOutput } from '../../pipeline-phases/types.js';
|
|
|
27
27
|
import { getLanguageFromFilename } from '../../../../_shared/index.js';
|
|
28
28
|
import { readFileContents } from '../../filesystem-walker.js';
|
|
29
29
|
import { runScopeResolution } from './run.js';
|
|
30
|
+
import { isLanguageAvailable } from '../../../tree-sitter/parser-loader.js';
|
|
30
31
|
import { buildGraphNodeLookup } from '../graph-bridge/node-lookup.js';
|
|
31
32
|
import { SCOPE_RESOLVERS } from './registry.js';
|
|
32
33
|
import { isDev, isSemanticModelValidatorEnabled } from '../../utils/env.js';
|
|
@@ -120,6 +121,16 @@ export const scopeResolutionPhase = {
|
|
|
120
121
|
const fileLang = getLanguageFromFilename(f.path);
|
|
121
122
|
if (fileLang === null)
|
|
122
123
|
continue;
|
|
124
|
+
// Skip files whose grammar isn't available (optional grammars like
|
|
125
|
+
// swift/dart/kotlin on an install where the binding is absent or the
|
|
126
|
+
// user set GITNEXUS_SKIP_OPTIONAL_GRAMMARS). The parse phase already
|
|
127
|
+
// excluded and warned about these (parse-impl.ts); without this guard the
|
|
128
|
+
// file would fall through to the main-thread re-extract in run.ts and
|
|
129
|
+
// throw "Unsupported language" (caught, but noisy, and it needlessly
|
|
130
|
+
// loads the grammar on the main thread). `isLanguageAvailable` is
|
|
131
|
+
// memoized, so this stays O(1) per language. (#2091, #2093)
|
|
132
|
+
if (!isLanguageAvailable(fileLang))
|
|
133
|
+
continue;
|
|
123
134
|
let bucket = filesByLang.get(fileLang);
|
|
124
135
|
if (bucket === undefined) {
|
|
125
136
|
bucket = [];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source/sink/sanitizer config model (issue #2080, taint/PDG substrate M0).
|
|
3
|
+
*
|
|
4
|
+
* The per-language taint configuration *shape*. M0 ships only the type and an
|
|
5
|
+
* (empty) registry seam — no analysis consumes it yet. M3 (#2083, intra-proc
|
|
6
|
+
* taint) populates per-language specs and reads them when emitting TAINTED /
|
|
7
|
+
* SANITIZES edges.
|
|
8
|
+
*
|
|
9
|
+
* Kept deliberately minimal: enough for M3 to express "callable X is a
|
|
10
|
+
* source / sink / sanitizer, optionally for argument position N" without M0
|
|
11
|
+
* committing to matcher semantics it cannot yet validate. The shape is
|
|
12
|
+
* expected to grow (e.g. sanitizer escape conditions, return-position taint)
|
|
13
|
+
* when M3 makes contact with real flows; that is a forward-declared-interface
|
|
14
|
+
* design choice, not a finished contract.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Identifies a callable that participates in taint flow. `name` is matched
|
|
18
|
+
* against a resolved callable (simple or qualified name — exact matching
|
|
19
|
+
* semantics are M3's call). `args` optionally narrows to specific 0-based
|
|
20
|
+
* argument positions that carry taint (for a source/sink) or clear it (for a
|
|
21
|
+
* sanitizer); omit to mean "unspecified / all".
|
|
22
|
+
*/
|
|
23
|
+
export interface TaintCallableMatcher {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly args?: readonly number[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The taint configuration for a single language: which callables introduce
|
|
29
|
+
* taint (sources), which are dangerous to reach with tainted input (sinks),
|
|
30
|
+
* and which clear taint (sanitizers).
|
|
31
|
+
*/
|
|
32
|
+
export interface SourceSinkSanitizerSpec {
|
|
33
|
+
readonly sources: readonly TaintCallableMatcher[];
|
|
34
|
+
readonly sinks: readonly TaintCallableMatcher[];
|
|
35
|
+
readonly sanitizers: readonly TaintCallableMatcher[];
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source/sink/sanitizer config model (issue #2080, taint/PDG substrate M0).
|
|
3
|
+
*
|
|
4
|
+
* The per-language taint configuration *shape*. M0 ships only the type and an
|
|
5
|
+
* (empty) registry seam — no analysis consumes it yet. M3 (#2083, intra-proc
|
|
6
|
+
* taint) populates per-language specs and reads them when emitting TAINTED /
|
|
7
|
+
* SANITIZES edges.
|
|
8
|
+
*
|
|
9
|
+
* Kept deliberately minimal: enough for M3 to express "callable X is a
|
|
10
|
+
* source / sink / sanitizer, optionally for argument position N" without M0
|
|
11
|
+
* committing to matcher semantics it cannot yet validate. The shape is
|
|
12
|
+
* expected to grow (e.g. sanitizer escape conditions, return-position taint)
|
|
13
|
+
* when M3 makes contact with real flows; that is a forward-declared-interface
|
|
14
|
+
* design choice, not a finished contract.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-language source/sink/sanitizer registry seam (issue #2080).
|
|
3
|
+
*
|
|
4
|
+
* A keyed registry of {@link SourceSinkSanitizerSpec} by language id. M0 stands
|
|
5
|
+
* up the empty seam — no language is registered and nothing in the pipeline
|
|
6
|
+
* reads it. M3 (#2083) registers per-language specs and queries this registry
|
|
7
|
+
* when emitting taint edges.
|
|
8
|
+
*
|
|
9
|
+
* The store is module-level (matching the codebase's other per-language
|
|
10
|
+
* registries). {@link clearSourceSinkRegistry} resets it for test isolation.
|
|
11
|
+
*/
|
|
12
|
+
import type { SourceSinkSanitizerSpec } from './source-sink-config.js';
|
|
13
|
+
/**
|
|
14
|
+
* Register the taint config for a language. Last-write-wins: re-registering
|
|
15
|
+
* the same `languageId` overwrites the previous spec (so M3 can override a
|
|
16
|
+
* built-in default). Returns nothing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function registerSourceSinkConfig(languageId: string, spec: SourceSinkSanitizerSpec): void;
|
|
19
|
+
/**
|
|
20
|
+
* Look up the taint config for a language. Returns `undefined` when no spec is
|
|
21
|
+
* registered (the M0 default for every language) — never throws.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getSourceSinkConfig(languageId: string): SourceSinkSanitizerSpec | undefined;
|
|
24
|
+
/** Language ids that currently have a registered spec. Empty in M0. */
|
|
25
|
+
export declare function registeredTaintLanguages(): string[];
|
|
26
|
+
/** Reset the registry. Primarily for test isolation. */
|
|
27
|
+
export declare function clearSourceSinkRegistry(): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-language source/sink/sanitizer registry seam (issue #2080).
|
|
3
|
+
*
|
|
4
|
+
* A keyed registry of {@link SourceSinkSanitizerSpec} by language id. M0 stands
|
|
5
|
+
* up the empty seam — no language is registered and nothing in the pipeline
|
|
6
|
+
* reads it. M3 (#2083) registers per-language specs and queries this registry
|
|
7
|
+
* when emitting taint edges.
|
|
8
|
+
*
|
|
9
|
+
* The store is module-level (matching the codebase's other per-language
|
|
10
|
+
* registries). {@link clearSourceSinkRegistry} resets it for test isolation.
|
|
11
|
+
*/
|
|
12
|
+
const registry = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* Register the taint config for a language. Last-write-wins: re-registering
|
|
15
|
+
* the same `languageId` overwrites the previous spec (so M3 can override a
|
|
16
|
+
* built-in default). Returns nothing.
|
|
17
|
+
*/
|
|
18
|
+
export function registerSourceSinkConfig(languageId, spec) {
|
|
19
|
+
registry.set(languageId, spec);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Look up the taint config for a language. Returns `undefined` when no spec is
|
|
23
|
+
* registered (the M0 default for every language) — never throws.
|
|
24
|
+
*/
|
|
25
|
+
export function getSourceSinkConfig(languageId) {
|
|
26
|
+
return registry.get(languageId);
|
|
27
|
+
}
|
|
28
|
+
/** Language ids that currently have a registered spec. Empty in M0. */
|
|
29
|
+
export function registeredTaintLanguages() {
|
|
30
|
+
return [...registry.keys()];
|
|
31
|
+
}
|
|
32
|
+
/** Reset the registry. Primarily for test isolation. */
|
|
33
|
+
export function clearSourceSinkRegistry() {
|
|
34
|
+
registry.clear();
|
|
35
|
+
}
|
|
@@ -18,6 +18,19 @@ import { getProvider } from '../languages/index.js';
|
|
|
18
18
|
import { getTreeSitterBufferSize, getTreeSitterContentByteLength, TREE_SITTER_MAX_BUFFER, } from '../constants.js';
|
|
19
19
|
import { ARRAY_METHOD_HOC_BLOCKLIST_SET, DEFAULT_EXPORT_IDENTIFIER_BLOCKLIST_SET, deriveDefaultExportHocName, } from '../ts-js-hoc-utils.js';
|
|
20
20
|
import { parseSourceSafe } from '../../tree-sitter/safe-parse.js';
|
|
21
|
+
// ── Worker grammar loading — enforcement boundary (#2091/#2093, #2101) ───────
|
|
22
|
+
// The worker maintains its own grammar table (the guarded `_require`s below +
|
|
23
|
+
// `languageMap`) and intentionally does NOT consult the runtime
|
|
24
|
+
// `GITNEXUS_SKIP_OPTIONAL_GRAMMARS` opt-out. It does not need to: the MAIN
|
|
25
|
+
// THREAD's `parseableScanned` filter (pipeline-phases/parse-impl.ts, gated on
|
|
26
|
+
// `parser-loader.isLanguageAvailable`, which honors the runtime opt-out and a
|
|
27
|
+
// genuinely-absent binding alike) excludes files of an unavailable/opted-out
|
|
28
|
+
// language BEFORE any chunk is dispatched, so the worker never receives them.
|
|
29
|
+
// That main-thread filter is the single enforcement point. Any future change
|
|
30
|
+
// that dispatches files to the worker WITHOUT first passing them through
|
|
31
|
+
// `isLanguageAvailable` must re-introduce the gate here. (The cleaner end-state
|
|
32
|
+
// — routing this table through `parser-loader.getLanguageGrammar` so there is
|
|
33
|
+
// one loader — is the deferred Tier-1 consolidation.)
|
|
21
34
|
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
22
35
|
const _require = createRequire(import.meta.url);
|
|
23
36
|
let Swift = null;
|
|
@@ -232,6 +232,9 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
232
232
|
const routeWriter = new BufferedCSVWriter(path.join(csvDir, 'route.csv'), 'id,name,filePath,responseKeys,errorKeys,middleware');
|
|
233
233
|
// Tool nodes for MCP tool definitions
|
|
234
234
|
const toolWriter = new BufferedCSVWriter(path.join(csvDir, 'tool.csv'), 'id,name,filePath,description');
|
|
235
|
+
// BasicBlock nodes — taint/PDG substrate (issue #2080). No `name` column;
|
|
236
|
+
// blocks are identified by id + source span. Emitted by no phase yet.
|
|
237
|
+
const basicBlockWriter = new BufferedCSVWriter(path.join(csvDir, 'basicblock.csv'), 'id,filePath,startLine,endLine,text');
|
|
235
238
|
// Multi-language node types share the same CSV shape (no isExported column)
|
|
236
239
|
const multiLangHeader = 'id,name,filePath,startLine,endLine,content,description';
|
|
237
240
|
const MULTI_LANG_TYPES = [
|
|
@@ -380,6 +383,15 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
380
383
|
escapeCSVField(node.properties.description || ''),
|
|
381
384
|
].join(','));
|
|
382
385
|
break;
|
|
386
|
+
case 'BasicBlock':
|
|
387
|
+
await basicBlockWriter.addRow([
|
|
388
|
+
escapeCSVField(node.id),
|
|
389
|
+
escapeCSVField(node.properties.filePath || ''),
|
|
390
|
+
escapeCSVNumber(node.properties.startLine, -1),
|
|
391
|
+
escapeCSVNumber(node.properties.endLine, -1),
|
|
392
|
+
escapeCSVField(node.properties.text || ''),
|
|
393
|
+
].join(','));
|
|
394
|
+
break;
|
|
383
395
|
default: {
|
|
384
396
|
// Code element nodes (Function, Class, Interface, CodeElement)
|
|
385
397
|
const writer = codeWriterMap[node.label];
|
|
@@ -433,6 +445,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
433
445
|
sectionWriter,
|
|
434
446
|
routeWriter,
|
|
435
447
|
toolWriter,
|
|
448
|
+
basicBlockWriter,
|
|
436
449
|
...multiLangWriters.values(),
|
|
437
450
|
];
|
|
438
451
|
await Promise.all(allWriters.map((w) => w.finish()));
|
|
@@ -465,6 +478,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
|
|
|
465
478
|
['Section', sectionWriter],
|
|
466
479
|
['Route', routeWriter],
|
|
467
480
|
['Tool', toolWriter],
|
|
481
|
+
['BasicBlock', basicBlockWriter],
|
|
468
482
|
...Array.from(multiLangWriters.entries()).map(([name, w]) => [name, w]),
|
|
469
483
|
];
|
|
470
484
|
for (const [name, writer] of tableMap) {
|
|
@@ -962,6 +962,10 @@ const getCopyQuery = (table, filePath) => {
|
|
|
962
962
|
if (table === 'Tool') {
|
|
963
963
|
return `COPY ${t}(id, name, filePath, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
964
964
|
}
|
|
965
|
+
if (table === 'BasicBlock') {
|
|
966
|
+
// Taint/PDG substrate (issue #2080) — no name column.
|
|
967
|
+
return `COPY ${t}(id, filePath, startLine, endLine, text) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
968
|
+
}
|
|
965
969
|
if (table === 'Method') {
|
|
966
970
|
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
967
971
|
}
|
|
@@ -1011,6 +1015,10 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
|
|
|
1011
1015
|
: '';
|
|
1012
1016
|
query = `CREATE (n:Section {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, level: ${properties.level || 1}, content: ${escapeValue(properties.content || '')}${descPart}})`;
|
|
1013
1017
|
}
|
|
1018
|
+
else if (label === 'BasicBlock') {
|
|
1019
|
+
// Taint/PDG substrate (issue #2080) — no name column.
|
|
1020
|
+
query = `CREATE (n:BasicBlock {id: ${escapeValue(properties.id)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, text: ${escapeValue(properties.text || '')}})`;
|
|
1021
|
+
}
|
|
1014
1022
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
1015
1023
|
const descPart = properties.description
|
|
1016
1024
|
? `, description: ${escapeValue(properties.description)}`
|
|
@@ -1094,6 +1102,10 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
|
|
|
1094
1102
|
: '';
|
|
1095
1103
|
query = `MERGE (n:Section {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.level = ${properties.level || 1}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
|
|
1096
1104
|
}
|
|
1105
|
+
else if (label === 'BasicBlock') {
|
|
1106
|
+
// Taint/PDG substrate (issue #2080) — no name column.
|
|
1107
|
+
query = `MERGE (n:BasicBlock {id: ${escapeValue(properties.id)}}) SET n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.text = ${escapeValue(properties.text || '')}`;
|
|
1108
|
+
}
|
|
1097
1109
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
1098
1110
|
const descPart = properties.description
|
|
1099
1111
|
? `, n.description = ${escapeValue(properties.description)}`
|
|
@@ -42,7 +42,8 @@ export declare const MODULE_SCHEMA: string;
|
|
|
42
42
|
export declare const ROUTE_SCHEMA = "\nCREATE NODE TABLE Route (\n id STRING,\n name STRING,\n filePath STRING,\n responseKeys STRING[],\n errorKeys STRING[],\n middleware STRING[],\n PRIMARY KEY (id)\n)";
|
|
43
43
|
export declare const TOOL_SCHEMA = "\nCREATE NODE TABLE Tool (\n id STRING,\n name STRING,\n filePath STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
|
|
44
44
|
export declare const SECTION_SCHEMA = "\nCREATE NODE TABLE Section (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n level INT64,\n content STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
|
|
45
|
-
export declare const
|
|
45
|
+
export declare const BASICBLOCK_SCHEMA = "\nCREATE NODE TABLE BasicBlock (\n id STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n text STRING,\n PRIMARY KEY (id)\n)";
|
|
46
|
+
export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Variable`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM File TO Section,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Function TO `Property`,\n FROM Function TO CodeElement,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Class TO `Property`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM Method TO `Property`,\n FROM Method TO CodeElement,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM Section TO Section,\n FROM Section TO File,\n FROM File TO Route,\n FROM Function TO Route,\n FROM Method TO Route,\n FROM File TO Tool,\n FROM Function TO Tool,\n FROM Method TO Tool,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM Interface TO `Property`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Struct` TO Interface,\n FROM `Struct` TO `Constructor`,\n FROM `Struct` TO `Property`,\n FROM `Enum` TO `Enum`,\n FROM `Enum` TO Community,\n FROM `Enum` TO Class,\n FROM `Enum` TO Interface,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Namespace` TO `Struct`,\n FROM `Trait` TO Method,\n FROM `Trait` TO `Constructor`,\n FROM `Trait` TO `Property`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Method,\n FROM `Impl` TO `Constructor`,\n FROM `Impl` TO `Property`,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `TypeAlias` TO Class,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Variable` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Method,\n FROM `Record` TO `Constructor`,\n FROM `Record` TO `Property`,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Constructor` TO `Property`,\n FROM `Constructor` TO `Typedef`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Variable` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n FROM Route TO Process,\n FROM Tool TO Process,\n FROM BasicBlock TO BasicBlock,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
|
|
46
47
|
export declare const EMBEDDING_DIMS: number;
|
|
47
48
|
/** HNSW vector index name for the CodeEmbedding table. */
|
|
48
49
|
export declare const EMBEDDING_INDEX_NAME = "code_embedding_idx";
|
package/dist/core/lbug/schema.js
CHANGED
|
@@ -201,6 +201,22 @@ CREATE NODE TABLE Section (
|
|
|
201
201
|
description STRING,
|
|
202
202
|
PRIMARY KEY (id)
|
|
203
203
|
)`;
|
|
204
|
+
// Taint/PDG substrate (issue #2080) — intra-procedural control-flow node.
|
|
205
|
+
// Emitted by no phase yet; M1 (#2081) populates these behind an opt-in.
|
|
206
|
+
// REACHING_DEF carries its variable name in the relation's existing `reason`
|
|
207
|
+
// column (see RELATION_SCHEMA) — LadybugDB has no secondary index on rel
|
|
208
|
+
// properties, so a dedicated indexed column would buy nothing for the
|
|
209
|
+
// variable-filtered path query (M0/S1 verdict). No `name` column: blocks are
|
|
210
|
+
// identified by id + source span, not a symbol name.
|
|
211
|
+
export const BASICBLOCK_SCHEMA = `
|
|
212
|
+
CREATE NODE TABLE BasicBlock (
|
|
213
|
+
id STRING,
|
|
214
|
+
filePath STRING,
|
|
215
|
+
startLine INT64,
|
|
216
|
+
endLine INT64,
|
|
217
|
+
text STRING,
|
|
218
|
+
PRIMARY KEY (id)
|
|
219
|
+
)`;
|
|
204
220
|
// ============================================================================
|
|
205
221
|
// RELATION TABLE SCHEMA
|
|
206
222
|
// Single table with 'type' property - connects all node tables
|
|
@@ -410,6 +426,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
|
|
|
410
426
|
FROM CodeElement TO Process,
|
|
411
427
|
FROM Route TO Process,
|
|
412
428
|
FROM Tool TO Process,
|
|
429
|
+
FROM BasicBlock TO BasicBlock,
|
|
413
430
|
type STRING,
|
|
414
431
|
confidence DOUBLE,
|
|
415
432
|
reason STRING,
|
|
@@ -490,6 +507,11 @@ export const NODE_SCHEMA_QUERIES = [
|
|
|
490
507
|
ROUTE_SCHEMA,
|
|
491
508
|
// MCP tools
|
|
492
509
|
TOOL_SCHEMA,
|
|
510
|
+
// Taint/PDG substrate (issue #2080) — must be appended here, not just
|
|
511
|
+
// declared above: SCHEMA_QUERIES (the list initLbug actually runs) is built
|
|
512
|
+
// from NODE_SCHEMA_QUERIES. Omitting this leaves the BasicBlock table
|
|
513
|
+
// uncreated and the bulk-COPY round-trip fails with "table does not exist".
|
|
514
|
+
BASICBLOCK_SCHEMA,
|
|
493
515
|
];
|
|
494
516
|
export const REL_SCHEMA_QUERIES = [RELATION_SCHEMA];
|
|
495
517
|
export const SCHEMA_QUERIES = [...NODE_SCHEMA_QUERIES, ...REL_SCHEMA_QUERIES, EMBEDDING_SCHEMA];
|
|
@@ -15,6 +15,14 @@ export interface GrammarSourceDescriptor {
|
|
|
15
15
|
export declare const listGrammarSources: () => GrammarSourceDescriptor[];
|
|
16
16
|
export declare const resolveLanguageKey: (language: SupportedLanguages, filePath?: string) => string;
|
|
17
17
|
export declare const isLanguageAvailable: (language: SupportedLanguages, filePath?: string) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* True when `language`'s grammar is being treated as unavailable specifically
|
|
20
|
+
* because of the runtime GITNEXUS_SKIP_OPTIONAL_GRAMMARS opt-out — as opposed
|
|
21
|
+
* to a genuinely-missing/broken native binding. Lets callers surface an
|
|
22
|
+
* accurate "skipped on purpose" message instead of a spurious "npm rebuild"
|
|
23
|
+
* recovery hint. Returns false for required grammars and for an absent env.
|
|
24
|
+
*/
|
|
25
|
+
export declare const isGrammarRuntimeSkipped: (language: SupportedLanguages, filePath?: string) => boolean;
|
|
18
26
|
export declare const getLanguageGrammar: (language: SupportedLanguages, filePath?: string) => unknown;
|
|
19
27
|
export declare const loadParser: () => Promise<Parser>;
|
|
20
28
|
export declare const loadLanguage: (language: SupportedLanguages, filePath?: string) => Promise<void>;
|
|
@@ -86,6 +86,7 @@ const SOURCES = {
|
|
|
86
86
|
[SupportedLanguages.Swift]: {
|
|
87
87
|
load: () => _require('tree-sitter-swift'),
|
|
88
88
|
optional: true,
|
|
89
|
+
userSkippable: true,
|
|
89
90
|
unavailableNote: 'Swift parsing disabled: vendored `tree-sitter-swift` (under ' +
|
|
90
91
|
'`gitnexus/vendor/tree-sitter-swift`) failed to load. ' +
|
|
91
92
|
'Likely cause: no prebuilt `.node` for this platform/architecture. ' +
|
|
@@ -94,6 +95,7 @@ const SOURCES = {
|
|
|
94
95
|
[SupportedLanguages.Dart]: {
|
|
95
96
|
load: () => _require('tree-sitter-dart'),
|
|
96
97
|
optional: true,
|
|
98
|
+
userSkippable: true,
|
|
97
99
|
unavailableNote: 'Dart parsing disabled: vendored `tree-sitter-dart` (under ' +
|
|
98
100
|
'`gitnexus/vendor/tree-sitter-dart`) failed to load. ' +
|
|
99
101
|
'Likely cause: native compile failed at install (missing python3/make/g++). ' +
|
|
@@ -102,6 +104,7 @@ const SOURCES = {
|
|
|
102
104
|
[SupportedLanguages.Kotlin]: {
|
|
103
105
|
load: () => _require('tree-sitter-kotlin'),
|
|
104
106
|
optional: true,
|
|
107
|
+
userSkippable: true,
|
|
105
108
|
unavailableNote: 'Kotlin parsing disabled: `tree-sitter-kotlin` is an optionalDependency ' +
|
|
106
109
|
'and is not installed (or its native binding failed to build).',
|
|
107
110
|
},
|
|
@@ -112,6 +115,43 @@ export const listGrammarSources = () => Object.entries(SOURCES).map(([key, sourc
|
|
|
112
115
|
}));
|
|
113
116
|
const loadCache = new Map();
|
|
114
117
|
const logged = new Set();
|
|
118
|
+
// Parsed form of GITNEXUS_SKIP_OPTIONAL_GRAMMARS, resolved lazily ONCE per
|
|
119
|
+
// process. The env is set before analyze runs, so re-reading + re-allocating a
|
|
120
|
+
// Set on every call was wasted work (and a latent trap for any future per-file
|
|
121
|
+
// caller). `vi.resetModules()` gives the unit tests a fresh module — and thus a
|
|
122
|
+
// fresh memo — per case, so this stays test-friendly.
|
|
123
|
+
// 'all' → every userSkippable grammar; Set → only the named ids
|
|
124
|
+
// (and `tree-sitter-<id>` spellings); null → env unset/empty (nothing).
|
|
125
|
+
let _skipDirective;
|
|
126
|
+
const skipDirective = () => {
|
|
127
|
+
if (_skipDirective !== undefined)
|
|
128
|
+
return _skipDirective;
|
|
129
|
+
const raw = (process.env.GITNEXUS_SKIP_OPTIONAL_GRAMMARS ?? '').trim().toLowerCase();
|
|
130
|
+
if (raw === '')
|
|
131
|
+
return (_skipDirective = null);
|
|
132
|
+
if (raw === '1' || raw === 'true' || raw === 'all' || raw === '*')
|
|
133
|
+
return (_skipDirective = 'all');
|
|
134
|
+
return (_skipDirective = new Set(raw
|
|
135
|
+
.split(',')
|
|
136
|
+
.map((s) => s.trim())
|
|
137
|
+
.filter(Boolean)
|
|
138
|
+
.flatMap((s) => [s, s.replace(/^tree-sitter-/, '')])));
|
|
139
|
+
};
|
|
140
|
+
const isRuntimeSkippedGrammar = (key, source) => {
|
|
141
|
+
// Only grammars explicitly flagged user-skippable (swift/dart/kotlin) — never
|
|
142
|
+
// required deps that use the optional machinery for ABI safety (C carries no
|
|
143
|
+
// `userSkippable`).
|
|
144
|
+
if (source.userSkippable !== true)
|
|
145
|
+
return false;
|
|
146
|
+
const directive = skipDirective();
|
|
147
|
+
if (directive === null)
|
|
148
|
+
return false;
|
|
149
|
+
if (directive === 'all')
|
|
150
|
+
return true;
|
|
151
|
+
// `key` is the SupportedLanguages value (e.g. `swift`); the directive Set
|
|
152
|
+
// already holds both the bare id and the `tree-sitter-<id>` spelling.
|
|
153
|
+
return directive.has(key) || directive.has(`tree-sitter-${key}`);
|
|
154
|
+
};
|
|
115
155
|
const logFailure = (key, result) => {
|
|
116
156
|
if (result.ok === true)
|
|
117
157
|
return;
|
|
@@ -148,6 +188,24 @@ const loadGrammar = (key) => {
|
|
|
148
188
|
loadCache.set(key, result);
|
|
149
189
|
return result;
|
|
150
190
|
}
|
|
191
|
+
// Runtime opt-out: treat a user-skipped optional grammar exactly like an
|
|
192
|
+
// absent binding (non-fatal unavailable + one warning), without attempting
|
|
193
|
+
// the native load. See `isRuntimeSkippedGrammar`.
|
|
194
|
+
if (isRuntimeSkippedGrammar(key, source)) {
|
|
195
|
+
// Deliberate opt-out: emit an accurate "disabled on purpose" note rather
|
|
196
|
+
// than `source.unavailableNote` (which blames a missing/unbuilt binding and
|
|
197
|
+
// would mislead a user who set the env intentionally — #2101 review).
|
|
198
|
+
const result = {
|
|
199
|
+
ok: false,
|
|
200
|
+
error: new Error('runtime opt-out'),
|
|
201
|
+
note: `${key} parsing disabled via GITNEXUS_SKIP_OPTIONAL_GRAMMARS (unset it to re-enable).`,
|
|
202
|
+
fatal: false,
|
|
203
|
+
severity: 'warn',
|
|
204
|
+
};
|
|
205
|
+
loadCache.set(key, result);
|
|
206
|
+
logFailure(key, result);
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
151
209
|
let result;
|
|
152
210
|
try {
|
|
153
211
|
result = { ok: true, grammar: source.load() };
|
|
@@ -168,6 +226,18 @@ const loadGrammar = (key) => {
|
|
|
168
226
|
return result;
|
|
169
227
|
};
|
|
170
228
|
export const isLanguageAvailable = (language, filePath) => loadGrammar(resolveLanguageKey(language, filePath)).ok;
|
|
229
|
+
/**
|
|
230
|
+
* True when `language`'s grammar is being treated as unavailable specifically
|
|
231
|
+
* because of the runtime GITNEXUS_SKIP_OPTIONAL_GRAMMARS opt-out — as opposed
|
|
232
|
+
* to a genuinely-missing/broken native binding. Lets callers surface an
|
|
233
|
+
* accurate "skipped on purpose" message instead of a spurious "npm rebuild"
|
|
234
|
+
* recovery hint. Returns false for required grammars and for an absent env.
|
|
235
|
+
*/
|
|
236
|
+
export const isGrammarRuntimeSkipped = (language, filePath) => {
|
|
237
|
+
const key = resolveLanguageKey(language, filePath);
|
|
238
|
+
const source = SOURCES[key];
|
|
239
|
+
return source !== undefined && isRuntimeSkippedGrammar(key, source);
|
|
240
|
+
};
|
|
171
241
|
export const getLanguageGrammar = (language, filePath) => {
|
|
172
242
|
const key = resolveLanguageKey(language, filePath);
|
|
173
243
|
const result = loadGrammar(key);
|
package/dist/server/api.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ export declare const landingPageHtml: () => string;
|
|
|
47
47
|
export declare const staticCacheControlSetHeaders: (res: express.Response, filePath: string) => void;
|
|
48
48
|
export declare const registerWebUI: (app: express.Express, staticDir: string | null) => void;
|
|
49
49
|
export declare const writeNdjsonRecord: (res: express.Response, record: GraphStreamRecord, signal?: AbortSignal) => Promise<void>;
|
|
50
|
+
export declare const getNodeQuery: (table: string, includeContent: boolean) => string;
|
|
50
51
|
export declare const streamGraphNdjson: (res: express.Response, includeContent?: boolean, signal?: AbortSignal) => Promise<void>;
|
|
51
52
|
/**
|
|
52
53
|
* Handle a GET /api/file request body. Extracted from createServer's route
|
package/dist/server/api.js
CHANGED
|
@@ -290,8 +290,16 @@ const buildGraph = async (includeContent = false) => {
|
|
|
290
290
|
const GRAPH_RELATIONSHIP_QUERY = `MATCH (a)-[r:CodeRelation]->(b) RETURN a.id AS sourceId, b.id AS targetId, ` +
|
|
291
291
|
`r.type AS type, r.confidence AS confidence, r.reason AS reason, r.step AS step`;
|
|
292
292
|
const quoteNodeTable = (table) => `\`${table.replace(/`/g, '``')}\``;
|
|
293
|
-
const getNodeQuery = (table, includeContent) => {
|
|
293
|
+
export const getNodeQuery = (table, includeContent) => {
|
|
294
294
|
const tableLabel = quoteNodeTable(table);
|
|
295
|
+
if (table === 'BasicBlock') {
|
|
296
|
+
// Taint/PDG substrate (issue #2080) — BasicBlock has no name/content
|
|
297
|
+
// columns. Project only its declared columns: a default `n.name`
|
|
298
|
+
// projection raises a Ladybug "Cannot find property name" binder error
|
|
299
|
+
// (not matched by isIgnorableGraphQueryError), which would 500 the graph
|
|
300
|
+
// endpoint the moment BasicBlock joins NODE_TABLES, even on an empty table.
|
|
301
|
+
return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine, n.text AS text`;
|
|
302
|
+
}
|
|
295
303
|
if (table === 'File') {
|
|
296
304
|
return includeContent
|
|
297
305
|
? `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.content AS content`
|
|
@@ -320,10 +328,17 @@ const mapGraphNodeRow = (table, row, includeContent) => ({
|
|
|
320
328
|
id: row.id ?? row[0],
|
|
321
329
|
label: table,
|
|
322
330
|
properties: {
|
|
323
|
-
|
|
331
|
+
// `?? ''` keeps NodeProperties.name a `string` even for label rows that
|
|
332
|
+
// project no name/label column (BasicBlock — taint/PDG substrate #2080).
|
|
333
|
+
// Without it, BasicBlock rows carry name:undefined (masked by the cast
|
|
334
|
+
// below) and the web layer (Header search, circles/tree layout) derefs
|
|
335
|
+
// `.name` unguarded → TypeError once M1 emits blocks. `row.text` gives a
|
|
336
|
+
// BasicBlock a sensible fallback name before the empty-string floor.
|
|
337
|
+
name: row.name ?? row.label ?? row.text ?? row[1] ?? '',
|
|
324
338
|
filePath: row.filePath ?? row[2],
|
|
325
339
|
startLine: row.startLine,
|
|
326
340
|
endLine: row.endLine,
|
|
341
|
+
text: row.text,
|
|
327
342
|
content: includeContent ? row.content : undefined,
|
|
328
343
|
responseKeys: row.responseKeys,
|
|
329
344
|
errorKeys: row.errorKeys,
|
package/package.json
CHANGED