@veewo/gitnexus 1.5.0-rc.4 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/benchmark/agent-context/runner.js +3 -0
- package/dist/benchmark/agent-context/runner.test.js +22 -0
- package/dist/benchmark/agent-context/tool-runner.d.ts +7 -6
- package/dist/benchmark/agent-safe-query-context/io.d.ts +2 -0
- package/dist/benchmark/agent-safe-query-context/io.js +86 -0
- package/dist/benchmark/agent-safe-query-context/io.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/io.test.js +13 -0
- package/dist/benchmark/agent-safe-query-context/report.d.ts +57 -0
- package/dist/benchmark/agent-safe-query-context/report.js +159 -0
- package/dist/benchmark/agent-safe-query-context/report.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/report.test.js +362 -0
- package/dist/benchmark/agent-safe-query-context/runner.d.ts +44 -0
- package/dist/benchmark/agent-safe-query-context/runner.js +406 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.js +290 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.d.ts +20 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.js +225 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.js +122 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.d.ts +47 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.js +128 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.js +155 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.d.ts +9 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.js +77 -0
- package/dist/benchmark/agent-safe-query-context/types.d.ts +61 -0
- package/dist/benchmark/agent-safe-query-context/types.js +8 -0
- package/dist/benchmark/analyze-runner.d.ts +1 -1
- package/dist/benchmark/analyze-runner.js +4 -3
- package/dist/benchmark/analyze-runner.test.js +7 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.d.ts +47 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.js +89 -0
- package/dist/benchmark/runtime-poc/runner.d.ts +31 -0
- package/dist/benchmark/runtime-poc/runner.js +163 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.d.ts +8 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.js +21 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.d.ts +0 -1
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.js +53 -51
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.test.js +0 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +1 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +82 -18
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +1 -2
- package/dist/benchmark/u2-e2e/retrieval-runner.js +15 -7
- package/dist/benchmark/u2-e2e/retrieval-runner.test.js +46 -0
- package/dist/cli/ai-context.d.ts +0 -1
- package/dist/cli/ai-context.js +5 -6
- package/dist/cli/ai-context.test.js +8 -0
- package/dist/cli/analyze-options.js +58 -34
- package/dist/cli/analyze-options.test.js +57 -0
- package/dist/cli/analyze-runtime-summary.js +2 -0
- package/dist/cli/analyze-runtime-summary.test.js +12 -0
- package/dist/cli/analyze-summary.d.ts +4 -0
- package/dist/cli/analyze-summary.js +43 -0
- package/dist/cli/analyze-summary.test.js +65 -1
- package/dist/cli/analyze.d.ts +11 -0
- package/dist/cli/analyze.js +34 -5
- package/dist/cli/analyze.test.d.ts +1 -0
- package/dist/cli/analyze.test.js +25 -0
- package/dist/cli/benchmark-agent-context.js +1 -1
- package/dist/cli/benchmark-agent-safe-query-context.d.ts +20 -0
- package/dist/cli/benchmark-agent-safe-query-context.js +39 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.d.ts +1 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.js +271 -0
- package/dist/cli/benchmark-unity.js +1 -1
- package/dist/cli/benchmark-unity.test.js +5 -1
- package/dist/cli/benchmark.d.ts +29 -0
- package/dist/cli/benchmark.js +55 -0
- package/dist/cli/index.js +27 -2
- package/dist/cli/rule-lab.d.ts +3 -7
- package/dist/cli/rule-lab.js +13 -22
- package/dist/cli/rule-lab.test.js +23 -3
- package/dist/cli/scope-manifest-config.d.ts +9 -0
- package/dist/cli/scope-manifest-config.js +37 -0
- package/dist/cli/setup.js +40 -41
- package/dist/cli/setup.test.js +14 -14
- package/dist/cli/sync-manifest.d.ts +27 -0
- package/dist/cli/sync-manifest.js +200 -0
- package/dist/cli/sync-manifest.test.d.ts +1 -0
- package/dist/cli/sync-manifest.test.js +88 -0
- package/dist/cli/tool.d.ts +2 -0
- package/dist/cli/tool.js +2 -0
- package/dist/core/config/unity-config.d.ts +1 -1
- package/dist/core/config/unity-config.js +1 -1
- package/dist/core/ingestion/call-processor.d.ts +2 -1
- package/dist/core/ingestion/call-processor.js +28 -6
- package/dist/core/ingestion/heritage-processor.d.ts +2 -1
- package/dist/core/ingestion/heritage-processor.js +30 -7
- package/dist/core/ingestion/import-processor.d.ts +2 -1
- package/dist/core/ingestion/import-processor.js +28 -6
- package/dist/core/ingestion/parsing-processor.d.ts +5 -3
- package/dist/core/ingestion/parsing-processor.js +46 -13
- package/dist/core/ingestion/pipeline.js +100 -19
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.js +18 -20
- package/dist/core/ingestion/unity-parity-seed.d.ts +2 -1
- package/dist/core/ingestion/unity-parity-seed.js +8 -0
- package/dist/core/ingestion/unity-resource-processor.d.ts +11 -0
- package/dist/core/ingestion/unity-resource-processor.js +102 -0
- package/dist/core/ingestion/unity-resource-processor.test.js +449 -0
- package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +16 -1
- package/dist/core/ingestion/unity-runtime-binding-rules.js +193 -42
- package/dist/core/ingestion/workers/parse-worker.d.ts +2 -0
- package/dist/core/ingestion/workers/parse-worker.js +50 -6
- package/dist/core/lbug/csv-generator.test.js +2 -2
- package/dist/core/tree-sitter/csharp-define-profile.d.ts +6 -0
- package/dist/core/tree-sitter/csharp-define-profile.js +43 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.d.ts +14 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.js +261 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +10 -0
- package/dist/core/tree-sitter/parser-loader.js +19 -0
- package/dist/core/unity/doc-contract.test.d.ts +1 -0
- package/dist/core/unity/doc-contract.test.js +30 -0
- package/dist/core/unity/prefab-source-scan.d.ts +25 -0
- package/dist/core/unity/prefab-source-scan.js +152 -0
- package/dist/core/unity/prefab-source-scan.test.d.ts +1 -0
- package/dist/core/unity/prefab-source-scan.test.js +70 -0
- package/dist/core/unity/scan-context.d.ts +12 -0
- package/dist/core/unity/scan-context.js +50 -2
- package/dist/core/unity/scan-context.test.js +74 -0
- package/dist/mcp/local/agent-safe-response.d.ts +10 -0
- package/dist/mcp/local/agent-safe-response.js +639 -0
- package/dist/mcp/local/derived-process-reader.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +18 -1
- package/dist/mcp/local/local-backend.js +319 -125
- package/dist/mcp/local/process-confidence.d.ts +1 -2
- package/dist/mcp/local/process-confidence.js +0 -3
- package/dist/mcp/local/process-confidence.test.js +4 -2
- package/dist/mcp/local/process-evidence.d.ts +1 -8
- package/dist/mcp/local/process-evidence.js +1 -23
- package/dist/mcp/local/process-evidence.test.js +2 -16
- package/dist/mcp/local/process-ref.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-closure-evaluator.d.ts +33 -0
- package/dist/mcp/local/runtime-chain-closure-evaluator.js +273 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.d.ts +23 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.js +131 -0
- package/dist/mcp/local/runtime-chain-verify.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-verify.js +149 -138
- package/dist/mcp/local/runtime-chain-verify.test.js +126 -68
- package/dist/mcp/local/runtime-claim-rule-registry.d.ts +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.js +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.test.js +37 -4
- package/dist/mcp/local/runtime-claim.d.ts +11 -0
- package/dist/mcp/local/runtime-claim.js +28 -0
- package/dist/mcp/local/unity-evidence-view.d.ts +1 -1
- package/dist/mcp/local/unity-evidence-view.js +1 -1
- package/dist/mcp/local/unity-evidence-view.test.js +22 -0
- package/dist/mcp/tools.js +51 -21
- package/dist/rule-lab/analyze.d.ts +2 -1
- package/dist/rule-lab/analyze.js +94 -59
- package/dist/rule-lab/analyze.test.js +238 -20
- package/dist/rule-lab/curate.d.ts +2 -1
- package/dist/rule-lab/curate.js +24 -3
- package/dist/rule-lab/curate.test.js +65 -0
- package/dist/rule-lab/curation-input-builder.d.ts +45 -0
- package/dist/rule-lab/curation-input-builder.js +133 -0
- package/dist/rule-lab/promote.js +80 -7
- package/dist/rule-lab/promote.test.js +150 -0
- package/dist/rule-lab/review-pack.d.ts +3 -0
- package/dist/rule-lab/review-pack.js +41 -1
- package/dist/rule-lab/review-pack.test.js +67 -0
- package/dist/rule-lab/types.d.ts +29 -0
- package/dist/types/pipeline.d.ts +16 -0
- package/package.json +14 -13
- package/scripts/check-sync-manifest-traceability.mjs +203 -0
- package/scripts/run-node-tests.mjs +61 -0
- package/scripts/tree-sitter-audit-classify.mjs +172 -0
- package/skills/_shared/unity-rule-authoring-contract.md +64 -0
- package/skills/_shared/unity-runtime-process-contract.md +16 -0
- package/skills/gitnexus-cli.md +44 -4
- package/skills/gitnexus-debugging.md +9 -0
- package/skills/gitnexus-exploring.md +66 -18
- package/skills/gitnexus-guide.md +42 -3
- package/skills/gitnexus-impact-analysis.md +8 -0
- package/skills/gitnexus-pr-review.md +8 -0
- package/skills/gitnexus-refactoring.md +8 -0
- package/skills/gitnexus-unity-rule-gen.md +66 -312
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import Parser from 'tree-sitter';
|
|
2
|
-
import { loadParser, loadLanguage, isLanguageAvailable } from '../tree-sitter/parser-loader.js';
|
|
2
|
+
import { loadParser, loadLanguage, isLanguageAvailable, parseContent } from '../tree-sitter/parser-loader.js';
|
|
3
3
|
import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
|
|
4
4
|
import { generateId } from '../../lib/utils.js';
|
|
5
5
|
import { getLanguageFromFilename, yieldToEventLoop, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature } from './utils.js';
|
|
6
6
|
import { isNodeExported } from './export-detection.js';
|
|
7
7
|
import { detectFrameworkFromAST } from './framework-detection.js';
|
|
8
8
|
import { typeConfigs } from './type-extractors/index.js';
|
|
9
|
-
import {
|
|
9
|
+
import { TREE_SITTER_MAX_BUFFER } from './constants.js';
|
|
10
10
|
// isNodeExported imported from ./export-detection.js (shared module)
|
|
11
11
|
// Re-export for backward compatibility with any external consumers
|
|
12
12
|
export { isNodeExported } from './export-detection.js';
|
|
13
13
|
// ============================================================================
|
|
14
14
|
// Worker-based parallel parsing
|
|
15
15
|
// ============================================================================
|
|
16
|
-
const processParsingWithWorkers = async (graph, files, symbolTable, astCache, workerPool, onFileProgress) => {
|
|
16
|
+
const processParsingWithWorkers = async (graph, files, symbolTable, astCache, workerPool, onFileProgress, onRawFallbackParse) => {
|
|
17
17
|
// Filter to parseable files only
|
|
18
18
|
const parseableFiles = [];
|
|
19
19
|
for (const file of files) {
|
|
20
20
|
const lang = getLanguageFromFilename(file.path);
|
|
21
|
-
if (lang)
|
|
22
|
-
parseableFiles.push({
|
|
21
|
+
if (lang) {
|
|
22
|
+
parseableFiles.push({
|
|
23
|
+
path: file.path,
|
|
24
|
+
content: file.content,
|
|
25
|
+
...(file.rawContent ? { rawContent: file.rawContent } : {}),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
23
28
|
}
|
|
24
29
|
if (parseableFiles.length === 0)
|
|
25
30
|
return { imports: [], calls: [], heritage: [], routes: [], constructorBindings: [] };
|
|
@@ -34,6 +39,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
|
|
|
34
39
|
const allHeritage = [];
|
|
35
40
|
const allRoutes = [];
|
|
36
41
|
const allConstructorBindings = [];
|
|
42
|
+
let rawFallbackCount = 0;
|
|
37
43
|
for (const result of chunkResults) {
|
|
38
44
|
for (const node of result.nodes) {
|
|
39
45
|
graph.addNode({
|
|
@@ -57,7 +63,10 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
|
|
|
57
63
|
allHeritage.push(...result.heritage);
|
|
58
64
|
allRoutes.push(...result.routes);
|
|
59
65
|
allConstructorBindings.push(...result.constructorBindings);
|
|
66
|
+
rawFallbackCount += result.csharpPreprocFallbackFiles;
|
|
60
67
|
}
|
|
68
|
+
if (rawFallbackCount > 0)
|
|
69
|
+
onRawFallbackParse?.(rawFallbackCount);
|
|
61
70
|
// Merge and log skipped languages from workers
|
|
62
71
|
const skippedLanguages = new Map();
|
|
63
72
|
for (const result of chunkResults) {
|
|
@@ -78,7 +87,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
|
|
|
78
87
|
// ============================================================================
|
|
79
88
|
// Sequential fallback (original implementation)
|
|
80
89
|
// ============================================================================
|
|
81
|
-
const processParsingSequential = async (graph, files, symbolTable, astCache, onFileProgress) => {
|
|
90
|
+
const processParsingSequential = async (graph, files, symbolTable, astCache, onFileProgress, onRawFallbackParse) => {
|
|
82
91
|
const parser = await loadParser();
|
|
83
92
|
const total = files.length;
|
|
84
93
|
const skippedLanguages = new Map();
|
|
@@ -106,11 +115,35 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
|
|
|
106
115
|
}
|
|
107
116
|
let tree;
|
|
108
117
|
try {
|
|
109
|
-
tree =
|
|
118
|
+
tree = parseContent(file.content);
|
|
110
119
|
}
|
|
111
|
-
catch
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
catch {
|
|
121
|
+
if (file.rawContent && file.rawContent !== file.content) {
|
|
122
|
+
try {
|
|
123
|
+
tree = parseContent(file.rawContent);
|
|
124
|
+
onRawFallbackParse?.(1);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
console.warn(`Skipping unparseable file: ${file.path}`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.warn(`Skipping unparseable file: ${file.path}`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (file.rawContent && file.rawContent !== file.content && tree.rootNode?.hasError) {
|
|
137
|
+
try {
|
|
138
|
+
const rawTree = parseContent(file.rawContent);
|
|
139
|
+
if (!rawTree.rootNode?.hasError) {
|
|
140
|
+
tree = rawTree;
|
|
141
|
+
onRawFallbackParse?.(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Keep normalized parse result when raw fallback fails
|
|
146
|
+
}
|
|
114
147
|
}
|
|
115
148
|
astCache.set(file.path, tree);
|
|
116
149
|
const queryString = LANGUAGE_QUERIES[language];
|
|
@@ -274,16 +307,16 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
|
|
|
274
307
|
// ============================================================================
|
|
275
308
|
// Public API
|
|
276
309
|
// ============================================================================
|
|
277
|
-
export const processParsing = async (graph, files, symbolTable, astCache, onFileProgress, workerPool) => {
|
|
310
|
+
export const processParsing = async (graph, files, symbolTable, astCache, onFileProgress, workerPool, onRawFallbackParse) => {
|
|
278
311
|
if (workerPool) {
|
|
279
312
|
try {
|
|
280
|
-
return await processParsingWithWorkers(graph, files, symbolTable, astCache, workerPool, onFileProgress);
|
|
313
|
+
return await processParsingWithWorkers(graph, files, symbolTable, astCache, workerPool, onFileProgress, onRawFallbackParse);
|
|
281
314
|
}
|
|
282
315
|
catch (err) {
|
|
283
316
|
console.warn('Worker pool parsing failed, falling back to sequential:', err instanceof Error ? err.message : err);
|
|
284
317
|
}
|
|
285
318
|
}
|
|
286
319
|
// Fallback: sequential parsing (no pre-extracted data)
|
|
287
|
-
await processParsingSequential(graph, files, symbolTable, astCache, onFileProgress);
|
|
320
|
+
await processParsingSequential(graph, files, symbolTable, astCache, onFileProgress, onRawFallbackParse);
|
|
288
321
|
return null;
|
|
289
322
|
};
|
|
@@ -11,11 +11,14 @@ import { processUnityResources } from './unity-resource-processor.js';
|
|
|
11
11
|
import { applyUnityLifecycleSyntheticCalls } from './unity-lifecycle-synthetic-calls.js';
|
|
12
12
|
import { applyUnityRuntimeBindingRules } from './unity-runtime-binding-rules.js';
|
|
13
13
|
import { resolveUnityConfig } from '../config/unity-config.js';
|
|
14
|
+
import { loadCSharpDefineProfileFromCsproj } from '../tree-sitter/csharp-define-profile.js';
|
|
15
|
+
import { normalizeCSharpPreprocessorBranches } from '../tree-sitter/csharp-preproc-normalizer.js';
|
|
14
16
|
import { loadAnalyzeRules } from '../../mcp/local/runtime-claim-rule-registry.js';
|
|
15
17
|
import { createResolutionContext } from './resolution-context.js';
|
|
16
18
|
import { createASTCache } from './ast-cache.js';
|
|
17
19
|
import { walkRepositoryPaths, readFileContents, walkUnityResourcePaths } from './filesystem-walker.js';
|
|
18
20
|
import { getLanguageFromFilename } from './utils.js';
|
|
21
|
+
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
19
22
|
import { isLanguageAvailable } from '../tree-sitter/parser-loader.js';
|
|
20
23
|
import { createWorkerPool } from './workers/worker-pool.js';
|
|
21
24
|
import { selectEntriesByScopeRules } from './scope-filter.js';
|
|
@@ -39,7 +42,24 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
39
42
|
astCache.clear();
|
|
40
43
|
ctx.clear();
|
|
41
44
|
};
|
|
45
|
+
let csharpDefineSymbols;
|
|
46
|
+
const csharpUndefinedSymbols = new Set();
|
|
47
|
+
let csharpPreprocDiagnostics;
|
|
42
48
|
try {
|
|
49
|
+
if (options?.csharpDefineCsproj) {
|
|
50
|
+
const defineProfile = await loadCSharpDefineProfileFromCsproj(options.csharpDefineCsproj);
|
|
51
|
+
csharpDefineSymbols = defineProfile.symbols;
|
|
52
|
+
csharpPreprocDiagnostics = {
|
|
53
|
+
enabled: true,
|
|
54
|
+
sourcePath: defineProfile.sourcePath,
|
|
55
|
+
defineSymbolCount: defineProfile.symbols.size,
|
|
56
|
+
normalizedFiles: 0,
|
|
57
|
+
fallbackFiles: 0,
|
|
58
|
+
skippedFiles: 0,
|
|
59
|
+
expressionErrors: 0,
|
|
60
|
+
undefinedSymbols: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
43
63
|
// ── Phase 1: Scan paths only (no content read) ─────────────────────
|
|
44
64
|
onProgress({
|
|
45
65
|
phase: 'extracting',
|
|
@@ -184,7 +204,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
184
204
|
// Calls/heritage use the symbol table built so far (symbols from earlier chunks
|
|
185
205
|
// are already registered). This trades ~5% cross-chunk resolution accuracy for
|
|
186
206
|
// 200-400MB less memory — critical for Linux-kernel-scale repos.
|
|
187
|
-
const
|
|
207
|
+
const sequentialChunkFiles = [];
|
|
188
208
|
try {
|
|
189
209
|
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
|
|
190
210
|
const chunkPaths = chunks[chunkIdx];
|
|
@@ -192,7 +212,27 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
192
212
|
const chunkContents = await readFileContents(repoPath, chunkPaths);
|
|
193
213
|
const chunkFiles = chunkPaths
|
|
194
214
|
.filter(p => chunkContents.has(p))
|
|
195
|
-
.map(p =>
|
|
215
|
+
.map((p) => {
|
|
216
|
+
const originalContent = chunkContents.get(p);
|
|
217
|
+
if (!csharpDefineSymbols || getLanguageFromFilename(p) !== SupportedLanguages.CSharp) {
|
|
218
|
+
return { path: p, content: originalContent };
|
|
219
|
+
}
|
|
220
|
+
const normalized = normalizeCSharpPreprocessorBranches(originalContent, csharpDefineSymbols);
|
|
221
|
+
csharpPreprocDiagnostics.expressionErrors += normalized.diagnostics.expressionErrors;
|
|
222
|
+
for (const symbol of normalized.diagnostics.undefinedSymbols) {
|
|
223
|
+
csharpUndefinedSymbols.add(symbol);
|
|
224
|
+
}
|
|
225
|
+
if (!normalized.changed) {
|
|
226
|
+
csharpPreprocDiagnostics.skippedFiles += 1;
|
|
227
|
+
return { path: p, content: originalContent };
|
|
228
|
+
}
|
|
229
|
+
csharpPreprocDiagnostics.normalizedFiles += 1;
|
|
230
|
+
return {
|
|
231
|
+
path: p,
|
|
232
|
+
content: normalized.normalizedText,
|
|
233
|
+
rawContent: originalContent,
|
|
234
|
+
};
|
|
235
|
+
});
|
|
196
236
|
// Parse this chunk (workers or sequential fallback)
|
|
197
237
|
const chunkWorkerData = await processParsing(graph, chunkFiles, symbolTable, astCache, (current, _total, filePath) => {
|
|
198
238
|
const globalCurrent = filesParsedSoFar + current;
|
|
@@ -204,7 +244,10 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
204
244
|
detail: filePath,
|
|
205
245
|
stats: { filesProcessed: globalCurrent, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
|
|
206
246
|
});
|
|
207
|
-
}, workerPool)
|
|
247
|
+
}, workerPool, (count) => {
|
|
248
|
+
if (csharpPreprocDiagnostics)
|
|
249
|
+
csharpPreprocDiagnostics.fallbackFiles += count;
|
|
250
|
+
});
|
|
208
251
|
const chunkBasePercent = 20 + ((filesParsedSoFar / totalParseable) * 62);
|
|
209
252
|
if (chunkWorkerData) {
|
|
210
253
|
// Imports
|
|
@@ -251,8 +294,11 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
251
294
|
]);
|
|
252
295
|
}
|
|
253
296
|
else {
|
|
254
|
-
await processImports(graph, chunkFiles, astCache, ctx, undefined, repoPath, allPaths)
|
|
255
|
-
|
|
297
|
+
await processImports(graph, chunkFiles, astCache, ctx, undefined, repoPath, allPaths, (count) => {
|
|
298
|
+
if (csharpPreprocDiagnostics)
|
|
299
|
+
csharpPreprocDiagnostics.fallbackFiles += count;
|
|
300
|
+
});
|
|
301
|
+
sequentialChunkFiles.push(chunkFiles);
|
|
256
302
|
}
|
|
257
303
|
filesParsedSoFar += chunkFiles.length;
|
|
258
304
|
// Clear AST cache between chunks to free memory
|
|
@@ -263,15 +309,17 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
263
309
|
finally {
|
|
264
310
|
await workerPool?.terminate();
|
|
265
311
|
}
|
|
266
|
-
// Sequential fallback chunks:
|
|
267
|
-
for (const
|
|
268
|
-
const chunkContents = await readFileContents(repoPath, chunkPaths);
|
|
269
|
-
const chunkFiles = chunkPaths
|
|
270
|
-
.filter(p => chunkContents.has(p))
|
|
271
|
-
.map(p => ({ path: p, content: chunkContents.get(p) }));
|
|
312
|
+
// Sequential fallback chunks: use the same normalized-or-raw source for call/heritage resolution
|
|
313
|
+
for (const chunkFiles of sequentialChunkFiles) {
|
|
272
314
|
astCache = createASTCache(chunkFiles.length);
|
|
273
|
-
const rubyHeritage = await processCalls(graph, chunkFiles, astCache, ctx)
|
|
274
|
-
|
|
315
|
+
const rubyHeritage = await processCalls(graph, chunkFiles, astCache, ctx, undefined, (count) => {
|
|
316
|
+
if (csharpPreprocDiagnostics)
|
|
317
|
+
csharpPreprocDiagnostics.fallbackFiles += count;
|
|
318
|
+
});
|
|
319
|
+
await processHeritage(graph, chunkFiles, astCache, ctx, undefined, (count) => {
|
|
320
|
+
if (csharpPreprocDiagnostics)
|
|
321
|
+
csharpPreprocDiagnostics.fallbackFiles += count;
|
|
322
|
+
});
|
|
275
323
|
if (rubyHeritage.length > 0) {
|
|
276
324
|
await processHeritageFromExtracted(graph, rubyHeritage, ctx);
|
|
277
325
|
}
|
|
@@ -293,6 +341,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
293
341
|
let communityResult;
|
|
294
342
|
let processResult;
|
|
295
343
|
let unityResult;
|
|
344
|
+
let unityRuleBindingResult;
|
|
296
345
|
if (!options?.skipGraphPhases) {
|
|
297
346
|
// ── Phase 4.5: Method Resolution Order ──────────────────────────────
|
|
298
347
|
onProgress({
|
|
@@ -358,7 +407,9 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
358
407
|
// ── Phase 5.6: Unity lifecycle synthetic calls (auto-detect) ────────
|
|
359
408
|
const isUnityProject = allPaths.some(p => p.startsWith('Assets/') && p.endsWith('.cs'));
|
|
360
409
|
const unityConfig = resolveUnityConfig();
|
|
361
|
-
|
|
410
|
+
// Persistence is coupled to the Unity resource-binding indexing flow:
|
|
411
|
+
// if Unity project auto-detection is active, persist lifecycle metadata.
|
|
412
|
+
const persistLifecycleProcessMetadata = isUnityProject;
|
|
362
413
|
const unityLifecycleSyntheticResult = isUnityProject
|
|
363
414
|
? applyUnityLifecycleSyntheticCalls(graph, {
|
|
364
415
|
maxSyntheticEdgesPerClass: unityConfig.config.maxSyntheticEdgesPerClass,
|
|
@@ -374,16 +425,41 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
374
425
|
// Phase 5.7: rule-driven binding injection (Phase 3)
|
|
375
426
|
try {
|
|
376
427
|
const analyzeRules = await loadAnalyzeRules(repoPath);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
428
|
+
const bindingResult = applyUnityRuntimeBindingRules(graph, analyzeRules, unityConfig.config);
|
|
429
|
+
unityRuleBindingResult = bindingResult;
|
|
430
|
+
if (isDev && bindingResult.edgesInjected > 0) {
|
|
431
|
+
console.log(`[UnityRuleBinding] injected ${bindingResult.edgesInjected} edges from ${analyzeRules.length} rule(s)`);
|
|
382
432
|
}
|
|
383
433
|
}
|
|
384
434
|
catch (err) {
|
|
385
435
|
// rule catalog missing or invalid — skip silently
|
|
386
436
|
console.warn(`[UnityRuleBinding] failed to load or apply analyze rules: ${err instanceof Error ? err.message : String(err)}`);
|
|
437
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
438
|
+
unityRuleBindingResult = {
|
|
439
|
+
edgesInjected: 0,
|
|
440
|
+
ruleResults: [],
|
|
441
|
+
diagnostics: {
|
|
442
|
+
rulesEvaluated: 0,
|
|
443
|
+
bindingsEvaluated: 0,
|
|
444
|
+
bindingsByKind: {},
|
|
445
|
+
methodLookupCalls: 0,
|
|
446
|
+
methodLookupCacheHits: 0,
|
|
447
|
+
sceneRuntimeTraversalCalls: 0,
|
|
448
|
+
sceneRuntimeTraversalCacheHits: 0,
|
|
449
|
+
sceneRuntimeResourcesVisited: 0,
|
|
450
|
+
anomalies: [`failed to load/apply analyze rules: ${reason}`],
|
|
451
|
+
shouldAgentReport: true,
|
|
452
|
+
agentReportReason: 'failed to load/apply analyze rules',
|
|
453
|
+
summary: [
|
|
454
|
+
'rule_binding.summary: rules=0, bindings=0, edges=0',
|
|
455
|
+
'rule_binding.bindings_by_kind: none',
|
|
456
|
+
'rule_binding.lookup: method_calls=0, cache_hits=0',
|
|
457
|
+
'rule_binding.scene_closure: traversals=0, cache_hits=0, visited_resources=0',
|
|
458
|
+
'rule_binding.agent_report: should_report=true reason="failed to load/apply analyze rules"',
|
|
459
|
+
`rule_binding.anomaly: failed to load/apply analyze rules: ${reason}`,
|
|
460
|
+
],
|
|
461
|
+
},
|
|
462
|
+
};
|
|
387
463
|
}
|
|
388
464
|
// ── Phase 6: Processes ─────────────────────────────────────────────
|
|
389
465
|
onProgress({
|
|
@@ -459,6 +535,9 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
459
535
|
},
|
|
460
536
|
});
|
|
461
537
|
astCache.clear();
|
|
538
|
+
if (csharpPreprocDiagnostics) {
|
|
539
|
+
csharpPreprocDiagnostics.undefinedSymbols = [...csharpUndefinedSymbols].sort();
|
|
540
|
+
}
|
|
462
541
|
return {
|
|
463
542
|
graph,
|
|
464
543
|
repoPath,
|
|
@@ -466,7 +545,9 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
466
545
|
communityResult,
|
|
467
546
|
processResult,
|
|
468
547
|
unityResult,
|
|
548
|
+
unityRuleBindingResult,
|
|
469
549
|
scopeDiagnostics: scopeSelection.diagnostics,
|
|
550
|
+
csharpPreprocDiagnostics,
|
|
470
551
|
};
|
|
471
552
|
}
|
|
472
553
|
catch (error) {
|
|
@@ -245,7 +245,7 @@ test('detects Unity hosts through transitive inheritance chains', () => {
|
|
|
245
245
|
assert.equal(gunGraphHost.baseType, 'ScriptableObject');
|
|
246
246
|
assert.ok(gunGraphHost.methods.length > 0);
|
|
247
247
|
});
|
|
248
|
-
test('
|
|
248
|
+
test('respects global lifecycle edge budget without emitting loader bridges', () => {
|
|
249
249
|
const graph = createKnowledgeGraph();
|
|
250
250
|
for (let i = 0; i < 6; i += 1) {
|
|
251
251
|
addClass(graph, {
|
|
@@ -275,12 +275,13 @@ test('prioritizes gameplay lifecycle hosts when synthetic edge budget is tight',
|
|
|
275
275
|
maxSyntheticEdgesPerClass: 4,
|
|
276
276
|
maxSyntheticEdgesTotal: 8,
|
|
277
277
|
});
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
const targets = new Set(runtimeEdges.map((edge) => edge.targetId));
|
|
278
|
+
const lifecycleEdges = [...graph.iterRelationships()].filter((edge) => edge.type === 'CALLS' && edge.reason === 'unity-lifecycle-synthetic');
|
|
279
|
+
const loaderEdges = [...graph.iterRelationships()].filter((edge) => edge.type === 'CALLS' && edge.reason === 'unity-runtime-loader-synthetic');
|
|
281
280
|
assert.equal(result.syntheticEdgeCount, 8);
|
|
282
|
-
assert.equal(
|
|
283
|
-
assert.equal(
|
|
281
|
+
assert.equal(result.lifecycleEdgeCount, 8);
|
|
282
|
+
assert.equal(result.loaderEdgeCount, 0);
|
|
283
|
+
assert.equal(lifecycleEdges.length, 8);
|
|
284
|
+
assert.equal(loaderEdges.length, 0);
|
|
284
285
|
});
|
|
285
286
|
test('resolves named class inheritance targets when direct target node lookup is ambiguous', () => {
|
|
286
287
|
const graph = createKnowledgeGraph();
|
|
@@ -336,7 +337,7 @@ test('resolves named class inheritance targets when direct target node lookup is
|
|
|
336
337
|
assert.ok(gunGraphHost);
|
|
337
338
|
assert.equal(gunGraphHost.baseType, 'ScriptableObject');
|
|
338
339
|
});
|
|
339
|
-
test('
|
|
340
|
+
test('lifecycle phase does not emit runtime-loader bridge edges', () => {
|
|
340
341
|
const graph = createKnowledgeGraph();
|
|
341
342
|
addClass(graph, {
|
|
342
343
|
className: 'GunGraphMB',
|
|
@@ -429,16 +430,15 @@ test('emits deterministic runtime loader bridge chain after pre-bridge budget is
|
|
|
429
430
|
maxSyntheticEdgesPerClass: 12,
|
|
430
431
|
maxSyntheticEdgesTotal: 10,
|
|
431
432
|
});
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
assert.equal(result.syntheticEdgeCount,
|
|
435
|
-
assert.equal(
|
|
436
|
-
assert.equal(
|
|
437
|
-
assert.equal(
|
|
438
|
-
assert.equal(
|
|
439
|
-
assert.equal(syntheticPairs.has(`${generateId('Method', 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs:ReloadBase.CheckReload')}->${generateId('Method', 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/Reload.cs:Reload.ReloadRoutine')}`), true);
|
|
433
|
+
const syntheticLoaderEdges = [...graph.iterRelationships()].filter((edge) => edge.type === 'CALLS' && edge.reason === 'unity-runtime-loader-synthetic');
|
|
434
|
+
const lifecycleEdges = [...graph.iterRelationships()].filter((edge) => edge.type === 'CALLS' && edge.reason === 'unity-lifecycle-synthetic');
|
|
435
|
+
assert.equal(result.syntheticEdgeCount, 3);
|
|
436
|
+
assert.equal(result.lifecycleEdgeCount, 3);
|
|
437
|
+
assert.equal(result.loaderEdgeCount, 0);
|
|
438
|
+
assert.equal(lifecycleEdges.length, 3);
|
|
439
|
+
assert.equal(syntheticLoaderEdges.length, 0);
|
|
440
440
|
});
|
|
441
|
-
test('
|
|
441
|
+
test('lifecycle edge cap does not rely on runtime-loader bridge injection', () => {
|
|
442
442
|
const graph = createKnowledgeGraph();
|
|
443
443
|
for (let i = 0; i < 20; i += 1) {
|
|
444
444
|
addClass(graph, {
|
|
@@ -534,8 +534,6 @@ test('preserves bridge budget when accepted host count exceeds synthetic edge ca
|
|
|
534
534
|
maxSyntheticEdgesPerClass: 12,
|
|
535
535
|
maxSyntheticEdgesTotal: 12,
|
|
536
536
|
});
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
.map((edge) => `${edge.sourceId}->${edge.targetId}`));
|
|
540
|
-
assert.equal(syntheticPairs.has(`${generateId('Method', 'Assets/NEON/Code/Game/Core/GunGraphMB.cs:GunGraphMB.RegisterGraphEvents')}->${generateId('Method', 'Assets/NEON/Code/Game/Graph/Graphs/GunGraph.cs:GunGraph.RegisterEvents')}`), true);
|
|
537
|
+
const loaderEdges = [...graph.iterRelationships()].filter((edge) => edge.type === 'CALLS' && edge.reason === 'unity-runtime-loader-synthetic');
|
|
538
|
+
assert.equal(loaderEdges.length, 0);
|
|
541
539
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { UnityScanContext } from '../unity/scan-context.js';
|
|
1
|
+
import type { UnityPrefabSourceRef, UnityScanContext } from '../unity/scan-context.js';
|
|
2
2
|
export interface UnityParitySeed {
|
|
3
3
|
version: 1;
|
|
4
4
|
symbolToScriptPath: Record<string, string>;
|
|
5
5
|
scriptPathToGuid: Record<string, string>;
|
|
6
6
|
guidToResourcePaths: Record<string, string[]>;
|
|
7
7
|
assetGuidToPath?: Record<string, string>;
|
|
8
|
+
prefabSourceRefs?: UnityPrefabSourceRef[];
|
|
8
9
|
}
|
|
9
10
|
export declare function buildUnityParitySeed(scanContext: UnityScanContext): UnityParitySeed;
|
|
@@ -59,6 +59,14 @@ export function buildUnityParitySeed(scanContext) {
|
|
|
59
59
|
scriptPathToGuid: sortRecord(scriptPathToGuid),
|
|
60
60
|
guidToResourcePaths: sortRecord(guidToResourcePaths),
|
|
61
61
|
assetGuidToPath: Object.keys(assetGuidToPath).length > 0 ? sortRecord(assetGuidToPath) : undefined,
|
|
62
|
+
prefabSourceRefs: scanContext.prefabSourceRefs?.map((row) => ({
|
|
63
|
+
sourceResourcePath: normalizePath(row.sourceResourcePath),
|
|
64
|
+
targetGuid: String(row.targetGuid || '').trim().toLowerCase(),
|
|
65
|
+
targetResourcePath: normalizePath(row.targetResourcePath || '') || undefined,
|
|
66
|
+
fileId: String(row.fileId || '').trim() || undefined,
|
|
67
|
+
fieldName: 'm_SourcePrefab',
|
|
68
|
+
sourceLayer: row.sourceLayer === 'scene' ? 'scene' : 'prefab',
|
|
69
|
+
})),
|
|
62
70
|
};
|
|
63
71
|
}
|
|
64
72
|
function normalizePath(input) {
|
|
@@ -7,6 +7,7 @@ export interface UnityResourceProcessingResult {
|
|
|
7
7
|
bindingCount: number;
|
|
8
8
|
componentCount: number;
|
|
9
9
|
diagnostics: string[];
|
|
10
|
+
prefabSourceStats: PrefabSourcePassStats;
|
|
10
11
|
paritySeed?: UnityParitySeed;
|
|
11
12
|
timingsMs: {
|
|
12
13
|
scanContext: number;
|
|
@@ -25,4 +26,14 @@ export interface UnityResourceProcessingDeps {
|
|
|
25
26
|
buildScanContext?: typeof buildUnityScanContext;
|
|
26
27
|
resolveBindings?: typeof resolveUnityBindings;
|
|
27
28
|
}
|
|
29
|
+
interface PrefabSourcePassStats {
|
|
30
|
+
rowsParsed: number;
|
|
31
|
+
rowsFilteredZeroGuid: number;
|
|
32
|
+
rowsFilteredPlaceholder: number;
|
|
33
|
+
rowsFilteredUnresolved: number;
|
|
34
|
+
rowsDeduped: number;
|
|
35
|
+
rowsEmitted: number;
|
|
36
|
+
fileErrors: number;
|
|
37
|
+
}
|
|
28
38
|
export declare function processUnityResources(graph: KnowledgeGraph, options: UnityResourceProcessingOptions, deps?: UnityResourceProcessingDeps): Promise<UnityResourceProcessingResult>;
|
|
39
|
+
export {};
|
|
@@ -5,6 +5,7 @@ import { resolveUnityBindings } from '../unity/resolver.js';
|
|
|
5
5
|
import { buildUnityParitySeed } from './unity-parity-seed.js';
|
|
6
6
|
import { resolveUnityConfig } from '../config/unity-config.js';
|
|
7
7
|
const UNITY_DIAGNOSTIC_SAMPLE_LIMIT = 3;
|
|
8
|
+
const PREFAB_SOURCE_PASS_DISABLE_ENV = 'GITNEXUS_DISABLE_PREFAB_SOURCE_PASS';
|
|
8
9
|
export async function processUnityResources(graph, options, deps) {
|
|
9
10
|
const tStart = performance.now();
|
|
10
11
|
const buildScanContextFn = deps?.buildScanContext || buildUnityScanContext;
|
|
@@ -36,6 +37,7 @@ export async function processUnityResources(graph, options, deps) {
|
|
|
36
37
|
let scanContextMs = 0;
|
|
37
38
|
let resolveMs = 0;
|
|
38
39
|
let graphWriteMs = 0;
|
|
40
|
+
let prefabSourceStats = initPrefabSourcePassStats();
|
|
39
41
|
try {
|
|
40
42
|
const tScanContextStart = performance.now();
|
|
41
43
|
scanContext = await buildScanContextFn({
|
|
@@ -51,6 +53,22 @@ export async function processUnityResources(graph, options, deps) {
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
diagnostics.push(`scanContext: scripts=${scanContext.symbolToScriptPath.size}, guids=${scanContext.scriptPathToGuid.size}, resources=${uniqueResourcePaths.size}`);
|
|
56
|
+
if (isPrefabSourcePassDisabledByEnv()) {
|
|
57
|
+
diagnostics.push(`prefab-source: skipped (env ${PREFAB_SOURCE_PASS_DISABLE_ENV}=1)`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const tPrefabSourceStart = performance.now();
|
|
61
|
+
prefabSourceStats = await emitPrefabSourceGuidRefsFromScanContext(graph, scanContext);
|
|
62
|
+
graphWriteMs += performance.now() - tPrefabSourceStart;
|
|
63
|
+
diagnostics.push(`prefab-source: emitted=${prefabSourceStats.rowsEmitted}`);
|
|
64
|
+
diagnostics.push(`prefab_source.rows_parsed=${prefabSourceStats.rowsParsed}`);
|
|
65
|
+
diagnostics.push(`prefab_source.rows_filtered_zero_guid=${prefabSourceStats.rowsFilteredZeroGuid}`);
|
|
66
|
+
diagnostics.push(`prefab_source.rows_filtered_placeholder=${prefabSourceStats.rowsFilteredPlaceholder}`);
|
|
67
|
+
diagnostics.push(`prefab_source.rows_filtered_unresolved=${prefabSourceStats.rowsFilteredUnresolved}`);
|
|
68
|
+
diagnostics.push(`prefab_source.rows_deduped=${prefabSourceStats.rowsDeduped}`);
|
|
69
|
+
diagnostics.push(`prefab_source.rows_emitted=${prefabSourceStats.rowsEmitted}`);
|
|
70
|
+
diagnostics.push(`prefab_source.file_errors=${prefabSourceStats.fileErrors}`);
|
|
71
|
+
}
|
|
54
72
|
symbolsWithResourceHits = collectSymbolsWithResourceHits(scanContext);
|
|
55
73
|
}
|
|
56
74
|
catch (error) {
|
|
@@ -228,6 +246,7 @@ export async function processUnityResources(graph, options, deps) {
|
|
|
228
246
|
bindingCount,
|
|
229
247
|
componentCount,
|
|
230
248
|
diagnostics,
|
|
249
|
+
prefabSourceStats,
|
|
231
250
|
paritySeed: scanContext ? buildUnityParitySeed(scanContext) : undefined,
|
|
232
251
|
timingsMs: {
|
|
233
252
|
scanContext: roundMs(scanContextMs),
|
|
@@ -460,3 +479,86 @@ function classifyUnityDiagnostic(message) {
|
|
|
460
479
|
}
|
|
461
480
|
return 'other';
|
|
462
481
|
}
|
|
482
|
+
async function emitPrefabSourceGuidRefsFromScanContext(graph, scanContext) {
|
|
483
|
+
const stats = initPrefabSourcePassStats();
|
|
484
|
+
const dedupeBySource = new Map();
|
|
485
|
+
const iterable = typeof scanContext.streamPrefabSourceRefs === 'function'
|
|
486
|
+
? scanContext.streamPrefabSourceRefs({
|
|
487
|
+
hooks: {
|
|
488
|
+
onFileError: () => {
|
|
489
|
+
stats.fileErrors += 1;
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
})
|
|
493
|
+
: (async function* () {
|
|
494
|
+
for (const row of scanContext.prefabSourceRefs || []) {
|
|
495
|
+
yield row;
|
|
496
|
+
}
|
|
497
|
+
})();
|
|
498
|
+
for await (const row of iterable) {
|
|
499
|
+
stats.rowsParsed += 1;
|
|
500
|
+
const source = normalizePath(String(row.sourceResourcePath || '').trim());
|
|
501
|
+
const guid = String(row.targetGuid || '').trim().toLowerCase();
|
|
502
|
+
if (!guid || guid === '00000000000000000000000000000000') {
|
|
503
|
+
stats.rowsFilteredZeroGuid += 1;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
const hintedTarget = normalizePath(String(row.targetResourcePath || '').trim());
|
|
507
|
+
if (hintedTarget === '__PLACEHOLDER__') {
|
|
508
|
+
stats.rowsFilteredPlaceholder += 1;
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
const resolvedTarget = hintedTarget
|
|
512
|
+
|| normalizePath(scanContext.assetGuidToPath?.get(guid) || scanContext.assetGuidToPath?.get(guid.toLowerCase()) || '');
|
|
513
|
+
if (!resolvedTarget || !resolvedTarget.endsWith('.prefab')) {
|
|
514
|
+
stats.rowsFilteredUnresolved += 1;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (!source) {
|
|
518
|
+
stats.rowsFilteredUnresolved += 1;
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
const perSourceDedupe = dedupeBySource.get(source) || new Set();
|
|
522
|
+
const dedupeKey = `${resolvedTarget}|m_SourcePrefab|${guid}|${String(row.fileId || '').trim()}|${row.sourceLayer === 'scene' ? 'scene' : 'prefab'}`;
|
|
523
|
+
if (perSourceDedupe.has(dedupeKey)) {
|
|
524
|
+
stats.rowsDeduped += 1;
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
perSourceDedupe.add(dedupeKey);
|
|
528
|
+
dedupeBySource.set(source, perSourceDedupe);
|
|
529
|
+
const sourceFileId = ensureResourceFileNode(graph, source);
|
|
530
|
+
const targetFileId = ensureResourceFileNode(graph, resolvedTarget);
|
|
531
|
+
graph.addRelationship({
|
|
532
|
+
id: generateId('UNITY_ASSET_GUID_REF', `${sourceFileId}->${targetFileId}:m_SourcePrefab:${guid}:${String(row.fileId || '')}`),
|
|
533
|
+
type: 'UNITY_ASSET_GUID_REF',
|
|
534
|
+
sourceId: sourceFileId,
|
|
535
|
+
targetId: targetFileId,
|
|
536
|
+
confidence: 1.0,
|
|
537
|
+
reason: JSON.stringify({
|
|
538
|
+
resourcePath: source,
|
|
539
|
+
targetResourcePath: resolvedTarget,
|
|
540
|
+
guid,
|
|
541
|
+
fileId: String(row.fileId || ''),
|
|
542
|
+
fieldName: 'm_SourcePrefab',
|
|
543
|
+
sourceLayer: row.sourceLayer === 'scene' ? 'scene' : 'prefab',
|
|
544
|
+
}),
|
|
545
|
+
});
|
|
546
|
+
stats.rowsEmitted += 1;
|
|
547
|
+
}
|
|
548
|
+
return stats;
|
|
549
|
+
}
|
|
550
|
+
function initPrefabSourcePassStats() {
|
|
551
|
+
return {
|
|
552
|
+
rowsParsed: 0,
|
|
553
|
+
rowsFilteredZeroGuid: 0,
|
|
554
|
+
rowsFilteredPlaceholder: 0,
|
|
555
|
+
rowsFilteredUnresolved: 0,
|
|
556
|
+
rowsDeduped: 0,
|
|
557
|
+
rowsEmitted: 0,
|
|
558
|
+
fileErrors: 0,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function isPrefabSourcePassDisabledByEnv() {
|
|
562
|
+
const value = String(process.env[PREFAB_SOURCE_PASS_DISABLE_ENV] || '').trim().toLowerCase();
|
|
563
|
+
return value === '1' || value === 'true' || value === 'yes' || value === 'on';
|
|
564
|
+
}
|