homegraph 1.5.1 → 1.5.2
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/LICENSE +21 -21
- package/README.md +305 -305
- package/dist/bin/homegraph.js +9 -9
- package/dist/db/index.js +36 -36
- package/dist/db/migrations.js +37 -37
- package/dist/db/queries.js +156 -156
- package/dist/db/schema.sql +203 -203
- package/dist/directory.js +5 -5
- package/dist/extraction/index.d.ts +1 -1
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +54 -43
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +53 -6
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +565 -65
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/installer/instructions-template.js +9 -9
- package/dist/mcp/diff-impact.d.ts +131 -0
- package/dist/mcp/diff-impact.d.ts.map +1 -0
- package/dist/mcp/diff-impact.js +385 -0
- package/dist/mcp/diff-impact.js.map +1 -0
- package/dist/mcp/liveness-watchdog.js +51 -51
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +49 -47
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/tools.d.ts +21 -5
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +305 -55
- package/dist/mcp/tools.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts +2 -1
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +153 -53
- package/dist/resolution/callback-synthesizer.js.map +1 -1
- package/dist/resolution/frameworks/arkts-entry.d.ts.map +1 -1
- package/dist/resolution/frameworks/arkts-entry.js +20 -9
- package/dist/resolution/frameworks/arkts-entry.js.map +1 -1
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +1 -1
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/memory-budget.d.ts +33 -0
- package/dist/resolution/memory-budget.d.ts.map +1 -1
- package/dist/resolution/memory-budget.js +58 -0
- package/dist/resolution/memory-budget.js.map +1 -1
- package/dist/resolution/resolver-pool.d.ts +2 -0
- package/dist/resolution/resolver-pool.d.ts.map +1 -1
- package/dist/resolution/resolver-pool.js +4 -0
- package/dist/resolution/resolver-pool.js.map +1 -1
- package/dist/spec/db/commit-node.js +4 -4
- package/dist/spec/db/fragment-node.js +10 -10
- package/dist/spec/db/fts.js +8 -8
- package/dist/spec/db/relations.js +88 -88
- package/dist/spec/db/schema.js +6 -6
- package/dist/spec/db/schema.sql +121 -121
- package/dist/spec/db/spec-node.js +4 -4
- package/dist/spec/llm/prompts.js +53 -53
- package/dist/ui/shimmer-progress.d.ts.map +1 -1
- package/dist/ui/shimmer-progress.js +4 -1
- package/dist/ui/shimmer-progress.js.map +1 -1
- package/package.json +61 -62
- package/dist/extraction/languages/arkts-viewtree.d.ts +0 -22
- package/dist/extraction/languages/arkts-viewtree.d.ts.map +0 -1
- package/dist/extraction/languages/arkts-viewtree.js +0 -133
- package/dist/extraction/languages/arkts-viewtree.js.map +0 -1
- package/dist/reasoning/config.d.ts +0 -45
- package/dist/reasoning/config.d.ts.map +0 -1
- package/dist/reasoning/config.js +0 -171
- package/dist/reasoning/config.js.map +0 -1
- package/dist/reasoning/credentials.d.ts +0 -5
- package/dist/reasoning/credentials.d.ts.map +0 -1
- package/dist/reasoning/credentials.js +0 -83
- package/dist/reasoning/credentials.js.map +0 -1
- package/dist/reasoning/login.d.ts +0 -21
- package/dist/reasoning/login.d.ts.map +0 -1
- package/dist/reasoning/login.js +0 -85
- package/dist/reasoning/login.js.map +0 -1
- package/dist/reasoning/reasoner.d.ts +0 -43
- package/dist/reasoning/reasoner.d.ts.map +0 -1
- package/dist/reasoning/reasoner.js +0 -308
- package/dist/reasoning/reasoner.js.map +0 -1
- package/dist/spec/evolve/llm-client.d.ts +0 -50
- package/dist/spec/evolve/llm-client.d.ts.map +0 -1
- package/dist/spec/evolve/llm-client.js +0 -176
- package/dist/spec/evolve/llm-client.js.map +0 -1
- package/dist/spec/evolve/logic-checker.d.ts +0 -12
- package/dist/spec/evolve/logic-checker.d.ts.map +0 -1
- package/dist/spec/evolve/logic-checker.js +0 -24
- package/dist/spec/evolve/logic-checker.js.map +0 -1
- package/dist/spec/llm/index.d.ts +0 -3
- package/dist/spec/llm/index.d.ts.map +0 -1
- package/dist/spec/llm/index.js +0 -11
- package/dist/spec/llm/index.js.map +0 -1
- package/dist/spec/mining/diff-parser.d.ts +0 -33
- package/dist/spec/mining/diff-parser.d.ts.map +0 -1
- package/dist/spec/mining/diff-parser.js +0 -166
- package/dist/spec/mining/diff-parser.js.map +0 -1
- package/dist/spec/mining/git-scanner.d.ts +0 -103
- package/dist/spec/mining/git-scanner.d.ts.map +0 -1
- package/dist/spec/mining/git-scanner.js +0 -307
- package/dist/spec/mining/git-scanner.js.map +0 -1
- package/dist/spec/mining/pipeline.d.ts +0 -53
- package/dist/spec/mining/pipeline.d.ts.map +0 -1
- package/dist/spec/mining/pipeline.js +0 -178
- package/dist/spec/mining/pipeline.js.map +0 -1
- package/dist/spec/mining/scope-resolver.d.ts +0 -45
- package/dist/spec/mining/scope-resolver.d.ts.map +0 -1
- package/dist/spec/mining/scope-resolver.js +0 -103
- package/dist/spec/mining/scope-resolver.js.map +0 -1
- package/dist/spec/mining/spec-extractor.d.ts +0 -69
- package/dist/spec/mining/spec-extractor.d.ts.map +0 -1
- package/dist/spec/mining/spec-extractor.js +0 -369
- package/dist/spec/mining/spec-extractor.js.map +0 -1
- package/dist/spec/utils.d.ts +0 -167
- package/dist/spec/utils.d.ts.map +0 -1
- package/dist/spec/utils.js +0 -463
- package/dist/spec/utils.js.map +0 -1
- package/scripts/_tmp-cfwk-resolve.log +0 -0
- package/scripts/_tmp-cfwk-sig.log +0 -0
- package/scripts/_tmp-cfwk-vt.log +0 -0
- package/scripts/add-lang/bench.sh +0 -60
- package/scripts/add-lang/check-grammar.mjs +0 -75
- package/scripts/add-lang/dump-ast.mjs +0 -103
- package/scripts/add-lang/verify-extraction.mjs +0 -70
- package/scripts/agent-eval/ab-adoption.sh +0 -91
- package/scripts/agent-eval/ab-hook.sh +0 -86
- package/scripts/agent-eval/ab-impl.sh +0 -78
- package/scripts/agent-eval/ab-new-vs-baseline.sh +0 -102
- package/scripts/agent-eval/ab-sufficiency.sh +0 -78
- package/scripts/agent-eval/arms-F.sh +0 -21
- package/scripts/agent-eval/arms-matrix.sh +0 -37
- package/scripts/agent-eval/audit.sh +0 -68
- package/scripts/agent-eval/bench-readme.sh +0 -28
- package/scripts/agent-eval/bench-why-repo.sh +0 -22
- package/scripts/agent-eval/block-read-hook.sh +0 -19
- package/scripts/agent-eval/hook-settings.json +0 -15
- package/scripts/agent-eval/itrun.sh +0 -120
- package/scripts/agent-eval/offload-eval-3arm.sh +0 -72
- package/scripts/agent-eval/offload-eval-cost.mjs +0 -133
- package/scripts/agent-eval/offload-eval-effort.mjs +0 -108
- package/scripts/agent-eval/offload-eval-frontload-matrix.sh +0 -25
- package/scripts/agent-eval/offload-eval-frontload.sh +0 -47
- package/scripts/agent-eval/offload-eval-ground-truth.json +0 -18
- package/scripts/agent-eval/offload-eval-hook.mjs +0 -84
- package/scripts/agent-eval/offload-eval-judge.mjs +0 -103
- package/scripts/agent-eval/offload-eval-matrix.sh +0 -20
- package/scripts/agent-eval/offload-eval-metrics.mjs +0 -94
- package/scripts/agent-eval/offload-eval-refs1.sh +0 -50
- package/scripts/agent-eval/offload-eval-setup.sh +0 -24
- package/scripts/agent-eval/offload-eval-styles.sh +0 -72
- package/scripts/agent-eval/offload-eval-summarize.mjs +0 -68
- package/scripts/agent-eval/offload-eval.md +0 -76
- package/scripts/agent-eval/parse-arms.mjs +0 -116
- package/scripts/agent-eval/parse-bench-readme.mjs +0 -84
- package/scripts/agent-eval/parse-run.mjs +0 -45
- package/scripts/agent-eval/parse-session.mjs +0 -93
- package/scripts/agent-eval/probe-context.mjs +0 -21
- package/scripts/agent-eval/probe-explore.mjs +0 -40
- package/scripts/agent-eval/probe-node.mjs +0 -20
- package/scripts/agent-eval/probe-sweep.mjs +0 -119
- package/scripts/agent-eval/probe-trace.mjs +0 -20
- package/scripts/agent-eval/redirect-read-hook.sh +0 -38
- package/scripts/agent-eval/repro-concurrent-explore.mjs +0 -119
- package/scripts/agent-eval/repro-daemon-clients.mjs +0 -125
- package/scripts/agent-eval/run-agent.sh +0 -34
- package/scripts/agent-eval/run-all.sh +0 -75
- package/scripts/agent-eval/run-arms.sh +0 -56
- package/scripts/agent-eval/seq-matrix.mjs +0 -137
- package/scripts/bench-arkts-init-rss.log +0 -0
- package/scripts/build-bundle.sh +0 -123
- package/scripts/exp_boundary_eval/README.md +0 -247
- package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_all.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_all.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/setup.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/__pycache__/win_mcp_launcher.cpython-38.pyc +0 -0
- package/scripts/exp_boundary_eval/_test_mcp_chain.py +0 -78
- package/scripts/exp_boundary_eval/_test_stdin.py +0 -8
- package/scripts/exp_boundary_eval/_utils.py +0 -1116
- package/scripts/exp_boundary_eval/analyze.py +0 -1313
- package/scripts/exp_boundary_eval/deveco_arm.py +0 -519
- package/scripts/exp_boundary_eval/run_all.py +0 -378
- package/scripts/exp_boundary_eval/run_one.py +0 -165
- package/scripts/exp_boundary_eval/run_session.py +0 -158
- package/scripts/exp_boundary_eval/setup.py +0 -120
- package/scripts/exp_boundary_eval/win_mcp_launcher.py +0 -73
- package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +0 -36
- package/scripts/exp_boundary_eval/win_node_launcher.py +0 -24
- package/scripts/extract-release-notes.mjs +0 -130
- package/scripts/local-install.sh +0 -41
- package/scripts/npm-sdk.js +0 -75
- package/scripts/npm-shim.js +0 -275
- package/scripts/ohos-sdk-publish.mjs +0 -133
- package/scripts/pack-npm.sh +0 -119
- package/scripts/prepare-release.mjs +0 -270
- package/scripts/probe-arkts-mem-why-run.log +0 -0
- package/scripts/probe-banner-livecard-ir.log +0 -0
- package/scripts/probe-cfwk-ast.stderr.log +0 -0
- package/scripts/probe-cfwk-ast.stdout.log +0 -0
- package/scripts/probe-cfwk-attr-shape.log +0 -0
- package/scripts/probe-cfwk-cfgdump.stderr.log +0 -0
- package/scripts/probe-cfwk-cfgdump.stdout.log +0 -0
- package/scripts/probe-cfwk-cvc.stderr.log +0 -0
- package/scripts/probe-cfwk-cvc.stdout.log +0 -0
- package/scripts/probe-cfwk-diag2.log +0 -0
- package/scripts/probe-cfwk-diag3.log +0 -0
- package/scripts/probe-cfwk-fedbg.stderr.log +0 -0
- package/scripts/probe-cfwk-fedbg.stdout.log +0 -0
- package/scripts/probe-cfwk-fileresult.log +0 -0
- package/scripts/probe-cfwk-fix.stderr.log +0 -0
- package/scripts/probe-cfwk-fix.stdout.log +0 -0
- package/scripts/probe-cfwk-fix2.stderr.log +0 -0
- package/scripts/probe-cfwk-fix2.stdout.log +0 -0
- package/scripts/probe-cfwk-fix3.stderr.log +0 -0
- package/scripts/probe-cfwk-fix3.stdout.log +0 -0
- package/scripts/probe-cfwk-foreach.log +0 -0
- package/scripts/probe-cfwk-getmethod-throw.log +0 -0
- package/scripts/probe-cfwk-hg-extract.log +0 -0
- package/scripts/probe-cfwk-pr1003-noprior.stderr.log +0 -0
- package/scripts/probe-cfwk-pr1003-noprior.stdout.log +0 -0
- package/scripts/probe-cfwk-pr1003.stderr.log +0 -0
- package/scripts/probe-cfwk-pr1003.stdout.log +0 -0
- package/scripts/probe-cfwk-preroot-noprior.stderr.log +0 -0
- package/scripts/probe-cfwk-preroot-noprior.stdout.log +0 -0
- package/scripts/probe-cfwk-preroot-prior.stderr.log +0 -0
- package/scripts/probe-cfwk-preroot-prior.stdout.log +0 -0
- package/scripts/probe-cfwk-preroot-skipstate.stderr.log +0 -0
- package/scripts/probe-cfwk-preroot-skipstate.stdout.log +0 -0
- package/scripts/probe-cfwk-resolve-sim.log +0 -0
- package/scripts/probe-cfwk-tree-shape.log +0 -0
- package/scripts/probe-cfwk-walk-abort.log +0 -0
- package/scripts/probe-cfwk.log +0 -0
- package/scripts/probe-force-index.log +0 -0
- package/scripts/probe-no-force-index.log +0 -0
- package/scripts/probe-samefile-ir.stderr.log +0 -0
- package/scripts/probe-samefile-ir.stdout.log +0 -224
- package/scripts/probe-sdk-vs-project.log +0 -0
- package/scripts/probe-viewtree-downgrade.log +0 -0
|
@@ -75,7 +75,9 @@ exports.scanEtsFiles = scanEtsFiles;
|
|
|
75
75
|
exports.runIsolatedArkTSBatchEntry = runIsolatedArkTSBatchEntry;
|
|
76
76
|
exports.primeArkTSBatch = primeArkTSBatch;
|
|
77
77
|
exports.isArkTSBatchPersisted = isArkTSBatchPersisted;
|
|
78
|
+
exports.isArkTSBatchCommitted = isArkTSBatchCommitted;
|
|
78
79
|
exports.resetArkTSBatch = resetArkTSBatch;
|
|
80
|
+
exports.shrinkArkTSBatchPostParse = shrinkArkTSBatchPostParse;
|
|
79
81
|
exports.collectExportedRtaEntryPointsFromFiles = collectExportedRtaEntryPointsFromFiles;
|
|
80
82
|
exports.stateDecoratorKinds = stateDecoratorKinds;
|
|
81
83
|
exports.stateTransferViaForField = stateTransferViaForField;
|
|
@@ -99,7 +101,12 @@ exports.parseJson5Minimal = parseJson5Minimal;
|
|
|
99
101
|
exports.normalizeOhosApiVersion = normalizeOhosApiVersion;
|
|
100
102
|
exports.detectOhosCompileSdkVersion = detectOhosCompileSdkVersion;
|
|
101
103
|
exports.isOhosArktsProject = isOhosArktsProject;
|
|
104
|
+
exports.readOhosSdkPlatformVersion = readOhosSdkPlatformVersion;
|
|
105
|
+
exports.discoverLocalOhosSdkCandidates = discoverLocalOhosSdkCandidates;
|
|
106
|
+
exports.findLocalOhosSdkForVersion = findLocalOhosSdkForVersion;
|
|
107
|
+
exports.resolveOhosApiVersionForProject = resolveOhosApiVersionForProject;
|
|
102
108
|
exports.ensureOhosApiDb = ensureOhosApiDb;
|
|
109
|
+
exports.attachExistingOhosApiDbForProject = attachExistingOhosApiDbForProject;
|
|
103
110
|
exports.bindOhosApiDbForProject = bindOhosApiDbForProject;
|
|
104
111
|
exports.restoreOhosApiDbAttach = restoreOhosApiDbAttach;
|
|
105
112
|
const crypto = __importStar(require("crypto"));
|
|
@@ -647,6 +654,27 @@ function persistIncrementalModuleResults(rootDir, queries, index) {
|
|
|
647
654
|
persistFileResult(queries, filePath, content, stats, fileResult, { force: true });
|
|
648
655
|
}
|
|
649
656
|
}
|
|
657
|
+
/**
|
|
658
|
+
* Persist a module slice then drop it from the in-memory map (streaming modular build).
|
|
659
|
+
* Returns the number of files written.
|
|
660
|
+
*/
|
|
661
|
+
function persistAndDropModuleSlice(rootDir, queries, slice, streamedPersistedPaths, persistOrdinal) {
|
|
662
|
+
if (slice.size === 0)
|
|
663
|
+
return 0;
|
|
664
|
+
for (const [filePath, fileResult] of slice) {
|
|
665
|
+
persistOrdinal.done++;
|
|
666
|
+
reportBatchProgress('persist', Math.min(persistOrdinal.done, persistOrdinal.totalHint), persistOrdinal.totalHint, filePath);
|
|
667
|
+
const isVirtual = filePath.startsWith('@');
|
|
668
|
+
const full = path.join(rootDir, filePath);
|
|
669
|
+
const content = isVirtual ? '' : fs.readFileSync(full, 'utf-8');
|
|
670
|
+
const stats = isVirtual
|
|
671
|
+
? { size: 0, mtimeMs: Date.now() }
|
|
672
|
+
: fs.statSync(full);
|
|
673
|
+
persistFileResult(queries, filePath, content, stats, fileResult, { force: true });
|
|
674
|
+
streamedPersistedPaths.add(normIndexPath(filePath));
|
|
675
|
+
}
|
|
676
|
+
return slice.size;
|
|
677
|
+
}
|
|
650
678
|
async function persistBatchResultsAsync(rootDir, queries, index) {
|
|
651
679
|
const entries = [...index.fileResults.entries()];
|
|
652
680
|
const persistTotal = entries.length;
|
|
@@ -694,18 +722,89 @@ function persistBatchResultsSync(rootDir, queries, index) {
|
|
|
694
722
|
}
|
|
695
723
|
}
|
|
696
724
|
function commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile) {
|
|
697
|
-
|
|
725
|
+
const streamed = index.streamedPersistedPaths;
|
|
726
|
+
batchPersistedPaths =
|
|
727
|
+
streamed && streamed.size > 0
|
|
728
|
+
? new Set([...streamed].map(normIndexPath))
|
|
729
|
+
: new Set([...index.fileResults.keys()].map(normIndexPath));
|
|
698
730
|
persistedBatch = { ...index, rootDir, batchKey, etsFiles };
|
|
699
731
|
batchTriggerFile = normIndexPath(triggerFile);
|
|
700
|
-
if (index.fileResults.size > 0) {
|
|
732
|
+
if (batchPersistedPaths.size > 0 || index.fileResults.size > 0) {
|
|
701
733
|
batchBuildCommitted = true;
|
|
702
734
|
}
|
|
703
735
|
return persistedBatch;
|
|
704
736
|
}
|
|
737
|
+
/**
|
|
738
|
+
* Drop in-memory ExtractionResults / edges after they are in SQLite.
|
|
739
|
+
* Keep path sets so `isArkTSBatchPersisted` still skips re-parse; matches the
|
|
740
|
+
* isolated-worker commit shape (empty fileResults).
|
|
741
|
+
*/
|
|
742
|
+
function releaseArkTSBatchHeavyPayload() {
|
|
743
|
+
if (!persistedBatch)
|
|
744
|
+
return;
|
|
745
|
+
persistedBatch.fileResults.clear();
|
|
746
|
+
persistedBatch.crossFileEdges = [];
|
|
747
|
+
persistedBatch.nodeIds.clear();
|
|
748
|
+
persistedBatch.errors = [];
|
|
749
|
+
}
|
|
750
|
+
/** Best-effort reclaim after Scene / batch teardown (needs `node --expose-gc`). */
|
|
751
|
+
function tryForceGc() {
|
|
752
|
+
const gc = globalThis.gc;
|
|
753
|
+
if (typeof gc === 'function') {
|
|
754
|
+
try {
|
|
755
|
+
gc();
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
// ignore
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Unload every module's IR then dispose Scene globals. Plain `scene.dispose()`
|
|
764
|
+
* only clears SDK helpers / moduleCache — project ArkFiles stay reachable and
|
|
765
|
+
* pin multi-GB RSS into Parsing/Resolving.
|
|
766
|
+
*/
|
|
767
|
+
function releaseArkAnalyzerScene(scene) {
|
|
768
|
+
try {
|
|
769
|
+
for (const mod of scene.getModules()) {
|
|
770
|
+
try {
|
|
771
|
+
scene.disposeModule(mod);
|
|
772
|
+
mod.setFileDepGraph(undefined);
|
|
773
|
+
mod.clearFilesMap();
|
|
774
|
+
mod.setLoadState(arkanalyzer_1.ModuleLoadState.NOT_LOADED);
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
// per-module best-effort
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
catch {
|
|
782
|
+
// ignore
|
|
783
|
+
}
|
|
784
|
+
try {
|
|
785
|
+
scene.dispose();
|
|
786
|
+
}
|
|
787
|
+
catch {
|
|
788
|
+
// ignore
|
|
789
|
+
}
|
|
790
|
+
tryForceGc();
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* ArkAnalyzer 1.0.92 keeps INDEX hollow on {@link ModuleBuilder} as a private
|
|
794
|
+
* method (public eviction paths call it internally). HomeGraph needs the same
|
|
795
|
+
* post-ingest hollow; call through a narrow cast until a public API exists.
|
|
796
|
+
*/
|
|
797
|
+
function hollowModuleToIndex(builder, moduleId) {
|
|
798
|
+
builder.downgradeModule(moduleId, arkanalyzer_1.ModuleDepthLevel.INDEX);
|
|
799
|
+
}
|
|
705
800
|
function tryReturnCachedBatch(rootDir, triggerFile, etsFiles) {
|
|
706
801
|
const normalizedTrigger = normIndexPath(triggerFile);
|
|
802
|
+
// Only reuse when payload is still in memory. After
|
|
803
|
+
// releaseArkTSBatchHeavyPayload(), fileResults is empty — fall through to
|
|
804
|
+
// rebuild (same as isolated-worker commit) rather than return a hollow cache.
|
|
707
805
|
if (persistedBatch &&
|
|
708
806
|
persistedBatch.rootDir === rootDir &&
|
|
807
|
+
persistedBatch.fileResults.size > 0 &&
|
|
709
808
|
batchPersistedPaths.has(normalizedTrigger)) {
|
|
710
809
|
return persistedBatch;
|
|
711
810
|
}
|
|
@@ -799,6 +898,8 @@ function runArkTSBatchFullCore(rootDir, queries, triggerFile, etsFiles, batchKey
|
|
|
799
898
|
queries.insertEdges(valid);
|
|
800
899
|
}
|
|
801
900
|
}
|
|
901
|
+
// Keep payload in memory for ArkTSExtractor.extract() same-batch hits.
|
|
902
|
+
// indexAll uses primeArkTSBatch → releaseArkTSBatchHeavyPayload after persist.
|
|
802
903
|
return commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile);
|
|
803
904
|
}
|
|
804
905
|
finally {
|
|
@@ -850,6 +951,27 @@ function runArkTSBatch(rootDir, queries, triggerFile) {
|
|
|
850
951
|
if (cached) {
|
|
851
952
|
return cached;
|
|
852
953
|
}
|
|
954
|
+
// indexAll releases the in-memory payload after SQLite persist. A later
|
|
955
|
+
// ArkTSExtractor hit must NOT rebuild the entire Scene (looks like a restart).
|
|
956
|
+
if (batchBuildCommitted &&
|
|
957
|
+
persistedBatch &&
|
|
958
|
+
persistedBatch.rootDir === rootDir &&
|
|
959
|
+
batchPersistedPaths.has(normalizedTrigger)) {
|
|
960
|
+
return {
|
|
961
|
+
fileResults: new Map(),
|
|
962
|
+
crossFileEdges: [],
|
|
963
|
+
nodeIds: new Set(),
|
|
964
|
+
errors: [
|
|
965
|
+
{
|
|
966
|
+
message: 'ArkTS batch already persisted this run; in-memory payload released',
|
|
967
|
+
severity: 'warning',
|
|
968
|
+
},
|
|
969
|
+
],
|
|
970
|
+
rootDir,
|
|
971
|
+
batchKey: persistedBatch.batchKey,
|
|
972
|
+
etsFiles: persistedBatch.etsFiles,
|
|
973
|
+
};
|
|
974
|
+
}
|
|
853
975
|
const batchKey = etsFiles.length > 0 ? computeBatchKey(rootDir, etsFiles) : '';
|
|
854
976
|
return runArkTSBatchFull(rootDir, queries, normalizedTrigger, etsFiles, batchKey);
|
|
855
977
|
}
|
|
@@ -892,13 +1014,25 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
892
1014
|
(0, context_1.setArktsBatchRunning)(true);
|
|
893
1015
|
try {
|
|
894
1016
|
reportBatchProgress('scene', 0, etsFiles.length);
|
|
895
|
-
const
|
|
896
|
-
if (
|
|
1017
|
+
const streamModular = shouldUseModularArkTSBuild(rootDir);
|
|
1018
|
+
if (streamModular) {
|
|
1019
|
+
// Clear stale cross-file RTA edges before streaming module persists.
|
|
1020
|
+
queries.deleteArkTSCrossFileCallEdges();
|
|
1021
|
+
}
|
|
1022
|
+
const index = buildArkTSIndex(rootDir, etsFiles, {
|
|
1023
|
+
streamQueries: streamModular ? queries : undefined,
|
|
1024
|
+
});
|
|
1025
|
+
const streamed = index.streamedPersistedPaths?.size ?? 0;
|
|
1026
|
+
if (index.fileResults.size === 0 && streamed === 0) {
|
|
897
1027
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
898
1028
|
throw new Error(fatal?.message ?? 'ArkTS batch produced no indexed files');
|
|
899
1029
|
}
|
|
900
|
-
|
|
901
|
-
|
|
1030
|
+
if (!streamModular) {
|
|
1031
|
+
queries.deleteArkTSCrossFileCallEdges();
|
|
1032
|
+
}
|
|
1033
|
+
if (index.fileResults.size > 0) {
|
|
1034
|
+
await persistBatchResultsAsync(rootDir, queries, index);
|
|
1035
|
+
}
|
|
902
1036
|
if (index.crossFileEdges.length > 0) {
|
|
903
1037
|
const valid = index.crossFileEdges.filter((e) => index.nodeIds.has(e.source) && index.nodeIds.has(e.target));
|
|
904
1038
|
if (valid.length > 0) {
|
|
@@ -906,6 +1040,8 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
906
1040
|
}
|
|
907
1041
|
}
|
|
908
1042
|
commitArkTSBatch(rootDir, batchKey, etsFiles, index, normalizedTrigger);
|
|
1043
|
+
releaseArkTSBatchHeavyPayload();
|
|
1044
|
+
tryForceGc();
|
|
909
1045
|
}
|
|
910
1046
|
finally {
|
|
911
1047
|
(0, context_1.setArktsBatchRunning)(false);
|
|
@@ -923,8 +1059,13 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
923
1059
|
(0, context_1.setArktsBatchRunning)(true);
|
|
924
1060
|
try {
|
|
925
1061
|
reportBatchProgress('scene', 0, etsFiles.length);
|
|
926
|
-
|
|
927
|
-
|
|
1062
|
+
queries.deleteArkTSCrossFileCallEdges();
|
|
1063
|
+
const index = buildArkTSIndexByModule(rootDir, etsFiles, {
|
|
1064
|
+
targetModuleSrcPaths: moduleSrcPaths,
|
|
1065
|
+
streamQueries: queries,
|
|
1066
|
+
});
|
|
1067
|
+
const streamed = index.streamedPersistedPaths?.size ?? 0;
|
|
1068
|
+
if (index.fileResults.size === 0 && streamed === 0) {
|
|
928
1069
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
929
1070
|
// Fall back to full project batch when the subset produced nothing.
|
|
930
1071
|
const batchKey = etsFiles.length > 0 ? computeBatchKey(rootDir, etsFiles) : '';
|
|
@@ -932,20 +1073,26 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
932
1073
|
process.stderr.write(`\n\x1b[33m ArkTS: incremental modules failed (${fatal.message}); full rebuild\x1b[0m\n`);
|
|
933
1074
|
}
|
|
934
1075
|
const full = buildArkTSIndex(rootDir, etsFiles);
|
|
935
|
-
if (full.fileResults.size === 0) {
|
|
1076
|
+
if (full.fileResults.size === 0 && (full.streamedPersistedPaths?.size ?? 0) === 0) {
|
|
936
1077
|
throw new Error(fatal?.message ?? 'ArkTS incremental batch produced no indexed files');
|
|
937
1078
|
}
|
|
938
1079
|
queries.deleteArkTSCrossFileCallEdges();
|
|
939
|
-
|
|
1080
|
+
if (full.fileResults.size > 0) {
|
|
1081
|
+
await persistBatchResultsAsync(rootDir, queries, full);
|
|
1082
|
+
}
|
|
940
1083
|
if (full.crossFileEdges.length > 0) {
|
|
941
1084
|
const valid = full.crossFileEdges.filter((e) => full.nodeIds.has(e.source) && full.nodeIds.has(e.target));
|
|
942
1085
|
if (valid.length > 0)
|
|
943
1086
|
queries.insertEdges(valid);
|
|
944
1087
|
}
|
|
945
1088
|
commitArkTSBatch(rootDir, batchKey, etsFiles, full, triggerFile);
|
|
1089
|
+
releaseArkTSBatchHeavyPayload();
|
|
1090
|
+
tryForceGc();
|
|
946
1091
|
return;
|
|
947
1092
|
}
|
|
948
|
-
|
|
1093
|
+
if (index.fileResults.size > 0) {
|
|
1094
|
+
persistIncrementalModuleResults(rootDir, queries, index);
|
|
1095
|
+
}
|
|
949
1096
|
if (index.crossFileEdges.length > 0) {
|
|
950
1097
|
const valid = index.crossFileEdges.filter((e) => index.nodeIds.has(e.source) && index.nodeIds.has(e.target));
|
|
951
1098
|
if (valid.length > 0) {
|
|
@@ -955,6 +1102,8 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
955
1102
|
// Unique key so a partial index is never mistaken for a full-project cache hit.
|
|
956
1103
|
const batchKey = `incremental:${moduleSrcPaths.join(',')}`;
|
|
957
1104
|
commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile);
|
|
1105
|
+
releaseArkTSBatchHeavyPayload();
|
|
1106
|
+
tryForceGc();
|
|
958
1107
|
process.stderr.write(`\n\x1b[33m ArkTS: incremental module reindex done (${moduleSrcPaths.join(', ')})\x1b[0m\n`);
|
|
959
1108
|
}
|
|
960
1109
|
finally {
|
|
@@ -965,6 +1114,10 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
965
1114
|
function isArkTSBatchPersisted(filePath) {
|
|
966
1115
|
return batchPersistedPaths.has(normIndexPath(filePath));
|
|
967
1116
|
}
|
|
1117
|
+
/** True after a successful ArkTS batch was committed to SQLite this index run. */
|
|
1118
|
+
function isArkTSBatchCommitted() {
|
|
1119
|
+
return batchBuildCommitted;
|
|
1120
|
+
}
|
|
968
1121
|
/** Clear cached batch state (tests and full re-index). */
|
|
969
1122
|
function resetArkTSBatch() {
|
|
970
1123
|
persistedBatch = null;
|
|
@@ -973,6 +1126,16 @@ function resetArkTSBatch() {
|
|
|
973
1126
|
batchBuildCommitted = false;
|
|
974
1127
|
arktsIndexNotices = [];
|
|
975
1128
|
}
|
|
1129
|
+
/**
|
|
1130
|
+
* After parse workers no longer need skip-path metadata beyond
|
|
1131
|
+
* `batchPersistedPaths`, drop the heavy path-list / batchKey strings.
|
|
1132
|
+
*/
|
|
1133
|
+
function shrinkArkTSBatchPostParse() {
|
|
1134
|
+
if (!persistedBatch)
|
|
1135
|
+
return;
|
|
1136
|
+
persistedBatch.etsFiles = [];
|
|
1137
|
+
persistedBatch.batchKey = '';
|
|
1138
|
+
}
|
|
976
1139
|
class ArkTSExtractor {
|
|
977
1140
|
filePath;
|
|
978
1141
|
constructor(filePath, _source) {
|
|
@@ -2028,6 +2191,9 @@ class ArkTSAdapter {
|
|
|
2028
2191
|
this.stitchModuleCfgCallsAndRefs(files, moduleFiles);
|
|
2029
2192
|
// Drop live IR object keys so ArkAnalyzer can GC unloaded modules; keep sig→id maps.
|
|
2030
2193
|
this.releaseLiveIrMaps();
|
|
2194
|
+
// Edges for this module are already in fileResults / crossFileEdges — drop
|
|
2195
|
+
// the string dedupe set so it does not grow across the whole project.
|
|
2196
|
+
this.emittedEdgeKeys.clear();
|
|
2031
2197
|
}
|
|
2032
2198
|
/**
|
|
2033
2199
|
* Index symbols from already-loaded non-target modules into sig/name maps only.
|
|
@@ -2466,6 +2632,30 @@ class ArkTSAdapter {
|
|
|
2466
2632
|
errors: [...errors],
|
|
2467
2633
|
};
|
|
2468
2634
|
}
|
|
2635
|
+
/**
|
|
2636
|
+
* Remove and return ExtractionResults for the given paths so callers can
|
|
2637
|
+
* persist+GC mid-Scene (streaming modular build). Keeps sig→id maps / nodeIds.
|
|
2638
|
+
*/
|
|
2639
|
+
takeFileResults(paths) {
|
|
2640
|
+
const out = new Map();
|
|
2641
|
+
for (const raw of paths) {
|
|
2642
|
+
const p = normIndexPath(raw);
|
|
2643
|
+
const result = this.fileResults.get(p);
|
|
2644
|
+
if (!result)
|
|
2645
|
+
continue;
|
|
2646
|
+
out.set(p, result);
|
|
2647
|
+
this.fileResults.delete(p);
|
|
2648
|
+
}
|
|
2649
|
+
return out;
|
|
2650
|
+
}
|
|
2651
|
+
/** Re-insert a previously taken file result (stream persist failure recovery). */
|
|
2652
|
+
putFileResult(filePath, result) {
|
|
2653
|
+
this.fileResults.set(normIndexPath(filePath), result);
|
|
2654
|
+
}
|
|
2655
|
+
/** All paths currently held in fileResults (for final flush). */
|
|
2656
|
+
fileResultPaths() {
|
|
2657
|
+
return [...this.fileResults.keys()];
|
|
2658
|
+
}
|
|
2469
2659
|
indexScannedFile(arkFile) {
|
|
2470
2660
|
const relativePath = normalizeRelPath(this.rootDir, arkFile.getFilePath());
|
|
2471
2661
|
if (!this.scanned.has(relativePath))
|
|
@@ -3219,6 +3409,8 @@ function emptyArkTSBatchIndex(errors) {
|
|
|
3219
3409
|
* Runs with the #850 liveness watchdog suspended: ArkAnalyzer's native BODIES
|
|
3220
3410
|
* load between module callbacks routinely exceeds the 60s heartbeat window on
|
|
3221
3411
|
* large Harmony projects, and there is no JS yield point inside that span.
|
|
3412
|
+
* When `streamQueries` is set, each module's ExtractionResults are written to
|
|
3413
|
+
* SQLite immediately and dropped from RAM (cross-file call edges stay until end).
|
|
3222
3414
|
*/
|
|
3223
3415
|
function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
3224
3416
|
return (0, liveness_watchdog_1.runWithoutLivenessWatchdog)(() => buildArkTSIndexByModuleInner(rootDir, scannedFiles, options));
|
|
@@ -3232,6 +3424,13 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3232
3424
|
let modulesIndexed = 0;
|
|
3233
3425
|
const targetSrc = options?.targetModuleSrcPaths?.map(normalizeHarmonyModuleSrcPath);
|
|
3234
3426
|
const targetSrcSet = targetSrc && targetSrc.length > 0 ? new Set(targetSrc) : null;
|
|
3427
|
+
const streamQueries = options?.streamQueries;
|
|
3428
|
+
const streamedPersistedPaths = new Set();
|
|
3429
|
+
const scannedList = [...scannedFiles].map(normIndexPath);
|
|
3430
|
+
const persistOrdinal = {
|
|
3431
|
+
done: 0,
|
|
3432
|
+
totalHint: Math.max(1, scannedList.filter((f) => isArkAnalyzerSourcePath(f)).length),
|
|
3433
|
+
};
|
|
3235
3434
|
try {
|
|
3236
3435
|
scene.config(sceneConfig);
|
|
3237
3436
|
const config = new arkanalyzer_1.ModuleAnalysisConfig();
|
|
@@ -3274,18 +3473,83 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3274
3473
|
return emptyArkTSBatchIndex(errors);
|
|
3275
3474
|
}
|
|
3276
3475
|
config.setTargetModuleIds(ids);
|
|
3277
|
-
process.stderr.write(`\n\x1b[33m ArkTS: analyseByModule incremental (${ids.length} module(s)→BODIES, deps→SIGNATURES, memoryLimitMB=${memoryLimitMB})...\x1b[0m\n`);
|
|
3476
|
+
process.stderr.write(`\n\x1b[33m ArkTS: analyseByModule incremental (${ids.length} module(s)→BODIES, deps→SIGNATURES, memoryLimitMB=${memoryLimitMB}${streamQueries ? ', streamPersist' : ''})...\x1b[0m\n`);
|
|
3278
3477
|
}
|
|
3279
3478
|
else {
|
|
3280
3479
|
config.setIncludeType(arkanalyzer_1.ModuleType.PROJECT, true);
|
|
3281
|
-
process.stderr.write(`\n\x1b[33m ArkTS: analyseByModule (PROJECT→BODIES, deps→SIGNATURES, memoryLimitMB=${memoryLimitMB}, maxOldSpace=${(0, wasm_runtime_flags_1.resolveMaxOldSpaceSizeMb)()})...\x1b[0m\n`);
|
|
3480
|
+
process.stderr.write(`\n\x1b[33m ArkTS: analyseByModule (PROJECT→BODIES, deps→SIGNATURES, memoryLimitMB=${memoryLimitMB}, maxOldSpace=${(0, wasm_runtime_flags_1.resolveMaxOldSpaceSizeMb)()}${streamQueries ? ', streamPersist' : ''})...\x1b[0m\n`);
|
|
3282
3481
|
}
|
|
3283
3482
|
scene.analyseByModule((module, scn) => {
|
|
3483
|
+
const moduleFileRels = [...module.getFilesMap().values()]
|
|
3484
|
+
.map((f) => normalizeRelPath(rootDir, f.getFilePath()))
|
|
3485
|
+
.filter((p) => p && isArkAnalyzerSourcePath(p))
|
|
3486
|
+
.map(normIndexPath);
|
|
3284
3487
|
adapter.indexModule(module, scn);
|
|
3488
|
+
// HomeGraph already captured this module's symbols/edges. Hollow every
|
|
3489
|
+
// currently-loaded module still above INDEX (target + oh_modules /
|
|
3490
|
+
// SIGNATURES deps left in ModuleCache). Topo order means PROJECT deps of
|
|
3491
|
+
// the current target are often already INDEX from their own callback, so
|
|
3492
|
+
// BFS-from-self alone left depHollowed=0; sweeping the cache catches the
|
|
3493
|
+
// fat non-target SIGNATURES that memoryLimitMB rarely evicts under a large
|
|
3494
|
+
// soft budget. INDEX keeps export-reachable shells; loadModule upgrades
|
|
3495
|
+
// when a later target needs SIGNATURES/BODIES.
|
|
3496
|
+
let depHollowed = 0;
|
|
3497
|
+
try {
|
|
3498
|
+
const builder = new arkanalyzer_1.ModuleBuilder(scn);
|
|
3499
|
+
const selfId = scn.getModuleId(module);
|
|
3500
|
+
hollowModuleToIndex(builder, selfId);
|
|
3501
|
+
const cache = scn.getModuleCache();
|
|
3502
|
+
if (cache) {
|
|
3503
|
+
for (const modId of cache.getLoadedModules()) {
|
|
3504
|
+
if (modId === selfId)
|
|
3505
|
+
continue;
|
|
3506
|
+
const depMod = builder.getModule(modId);
|
|
3507
|
+
if (!depMod || depMod.getLoadState() <= arkanalyzer_1.ModuleLoadState.INDEX)
|
|
3508
|
+
continue;
|
|
3509
|
+
hollowModuleToIndex(builder, modId);
|
|
3510
|
+
depHollowed++;
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
catch (e) {
|
|
3515
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
3516
|
+
errors.push({
|
|
3517
|
+
message: `ArkTS post-index INDEX hollow failed for ${module.getModuleName() || module.getModulePath()}: ${message}`,
|
|
3518
|
+
severity: 'warning',
|
|
3519
|
+
});
|
|
3520
|
+
}
|
|
3521
|
+
if (streamQueries) {
|
|
3522
|
+
// Persist this module's file bodies now; keep @dummyFile until finalize.
|
|
3523
|
+
const slicePaths = moduleFileRels.filter((p) => p !== ARKANALYZER_DUMMY_FILE);
|
|
3524
|
+
const slice = adapter.takeFileResults(slicePaths);
|
|
3525
|
+
try {
|
|
3526
|
+
persistAndDropModuleSlice(rootDir, streamQueries, slice, streamedPersistedPaths, persistOrdinal);
|
|
3527
|
+
}
|
|
3528
|
+
catch (e) {
|
|
3529
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
3530
|
+
errors.push({
|
|
3531
|
+
message: `ArkTS stream persist failed for ${module.getModuleName() || module.getModulePath()}: ${message}`,
|
|
3532
|
+
severity: 'error',
|
|
3533
|
+
});
|
|
3534
|
+
throw e;
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3285
3537
|
modulesIndexed++;
|
|
3286
3538
|
const name = module.getModuleName() || path.basename(module.getModulePath());
|
|
3287
|
-
|
|
3539
|
+
const st = arkanalyzer_1.ModuleLoadState[module.getLoadState()] ?? String(module.getLoadState());
|
|
3540
|
+
const depNote = depHollowed > 0 ? `, hollowed ${depHollowed} deps` : '';
|
|
3541
|
+
process.stderr.write(`\x1b[33m ArkTS: indexed module ${modulesIndexed}: ${name} → ${st}${depNote}${streamQueries ? ` (streamed ${moduleFileRels.length} files)` : ''}\x1b[0m\n`);
|
|
3288
3542
|
}, config);
|
|
3543
|
+
if (process.env.HOMEGRAPH_ARKTS_DIAG === '1') {
|
|
3544
|
+
const sceneDiag = scene;
|
|
3545
|
+
const ev = typeof sceneDiag.getModuleEvictionStats === 'function'
|
|
3546
|
+
? sceneDiag.getModuleEvictionStats()
|
|
3547
|
+
: null;
|
|
3548
|
+
const mu = process.memoryUsage();
|
|
3549
|
+
const cacheN = scene.getModuleCache()?.size?.() ?? scene.getModuleCache()?.getLoadedModules?.()?.length ?? -1;
|
|
3550
|
+
process.stderr.write(`\x1b[36m ArkTS DIAG eviction: ${JSON.stringify(ev)} cacheLoaded=${cacheN} ` +
|
|
3551
|
+
`heapUsedMB=${(mu.heapUsed / 1024 / 1024).toFixed(1)} rssMB=${(mu.rss / 1024 / 1024).toFixed(1)}\x1b[0m\n`);
|
|
3552
|
+
}
|
|
3289
3553
|
if (modulesIndexed === 0) {
|
|
3290
3554
|
errors.push({
|
|
3291
3555
|
message: 'ArkTS analyseByModule found no PROJECT modules (check build-profile.json5); falling back to full Scene build',
|
|
@@ -3303,8 +3567,15 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3303
3567
|
severity: 'warning',
|
|
3304
3568
|
});
|
|
3305
3569
|
}
|
|
3570
|
+
if (streamQueries) {
|
|
3571
|
+
const leftover = adapter.takeFileResults(adapter.fileResultPaths());
|
|
3572
|
+
persistAndDropModuleSlice(rootDir, streamQueries, leftover, streamedPersistedPaths, persistOrdinal);
|
|
3573
|
+
}
|
|
3306
3574
|
const index = adapter.toBatchIndex(errors);
|
|
3307
|
-
if (
|
|
3575
|
+
if (streamQueries) {
|
|
3576
|
+
index.streamedPersistedPaths = streamedPersistedPaths;
|
|
3577
|
+
}
|
|
3578
|
+
if (index.fileResults.size === 0 && streamedPersistedPaths.size === 0) {
|
|
3308
3579
|
errors.push({
|
|
3309
3580
|
message: 'ArkTS analyseByModule indexed modules but no scanned source files matched',
|
|
3310
3581
|
severity: 'warning',
|
|
@@ -3323,7 +3594,7 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3323
3594
|
}
|
|
3324
3595
|
finally {
|
|
3325
3596
|
try {
|
|
3326
|
-
scene
|
|
3597
|
+
releaseArkAnalyzerScene(scene);
|
|
3327
3598
|
}
|
|
3328
3599
|
catch {
|
|
3329
3600
|
// ignore dispose failures
|
|
@@ -3349,13 +3620,26 @@ function buildArkTSIndexFull(rootDir, scannedFiles, priorErrors = []) {
|
|
|
3349
3620
|
allErrors.push({ message: `ArkTS adapter failed: ${message}`, severity: 'error' });
|
|
3350
3621
|
return emptyArkTSBatchIndex(allErrors);
|
|
3351
3622
|
}
|
|
3623
|
+
finally {
|
|
3624
|
+
try {
|
|
3625
|
+
releaseArkAnalyzerScene(scene);
|
|
3626
|
+
}
|
|
3627
|
+
catch {
|
|
3628
|
+
// ignore
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3352
3631
|
});
|
|
3353
3632
|
}
|
|
3354
|
-
function buildArkTSIndex(rootDir, scannedFiles) {
|
|
3633
|
+
function buildArkTSIndex(rootDir, scannedFiles, options) {
|
|
3355
3634
|
if (shouldUseModularArkTSBuild(rootDir)) {
|
|
3356
|
-
const modular = buildArkTSIndexByModule(rootDir, scannedFiles
|
|
3635
|
+
const modular = buildArkTSIndexByModule(rootDir, scannedFiles, {
|
|
3636
|
+
streamQueries: options?.streamQueries,
|
|
3637
|
+
});
|
|
3357
3638
|
const fatal = modular.errors.find((e) => e.severity === 'error');
|
|
3358
|
-
|
|
3639
|
+
const hasOutput = modular.fileResults.size > 0 ||
|
|
3640
|
+
(modular.streamedPersistedPaths?.size ?? 0) > 0 ||
|
|
3641
|
+
modular.nodeIds.size > 0;
|
|
3642
|
+
if (hasOutput && !fatal) {
|
|
3359
3643
|
return modular;
|
|
3360
3644
|
}
|
|
3361
3645
|
// Warnings like "no PROJECT modules" / modular failure → full Scene fallback.
|
|
@@ -3520,7 +3804,7 @@ function extractOhosSdkArchive(archivePath) {
|
|
|
3520
3804
|
function ohosApiDbFilename(version) {
|
|
3521
3805
|
return `ohos-api-${version}.db`;
|
|
3522
3806
|
}
|
|
3523
|
-
/**
|
|
3807
|
+
/** Metadata / publish id — one package per API version (e.g. homegraph-ohos-api-db-6.0.1). */
|
|
3524
3808
|
function ohosApiDbPackageName(version) {
|
|
3525
3809
|
return `homegraph-ohos-api-db-${version}`;
|
|
3526
3810
|
}
|
|
@@ -3668,7 +3952,7 @@ async function indexOhosApiDb(options) {
|
|
|
3668
3952
|
}
|
|
3669
3953
|
}
|
|
3670
3954
|
// =============================================================================
|
|
3671
|
-
// OHOS API db consumer: compileSdkVersion detect,
|
|
3955
|
+
// OHOS API db consumer: compileSdkVersion detect, local SDK build, ATTACH
|
|
3672
3956
|
// =============================================================================
|
|
3673
3957
|
exports.OHOS_API_FILE_PREFIX = 'ohos-sdk:';
|
|
3674
3958
|
exports.OHOS_API_VERSION_META = 'ohos_api_version';
|
|
@@ -3810,75 +4094,291 @@ function hasEtsUnder(dir, depth) {
|
|
|
3810
4094
|
}
|
|
3811
4095
|
return false;
|
|
3812
4096
|
}
|
|
3813
|
-
/**
|
|
3814
|
-
function
|
|
3815
|
-
const
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
4097
|
+
/** Read platformVersion from sdk-pkg.json / oh-uni-package.json under an SDK home. */
|
|
4098
|
+
function readOhosSdkPlatformVersion(sdkHome) {
|
|
4099
|
+
const pkgPaths = [
|
|
4100
|
+
path.join(sdkHome, 'sdk-pkg.json'),
|
|
4101
|
+
path.join(sdkHome, 'openharmony', 'ets', 'oh-uni-package.json'),
|
|
4102
|
+
path.join(sdkHome, 'openharmony', 'oh-uni-package.json'),
|
|
4103
|
+
];
|
|
4104
|
+
for (const pkgPath of pkgPaths) {
|
|
4105
|
+
if (!fs.existsSync(pkgPath))
|
|
4106
|
+
continue;
|
|
4107
|
+
try {
|
|
4108
|
+
const raw = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
4109
|
+
const data = raw.data && typeof raw.data === 'object'
|
|
4110
|
+
? raw.data
|
|
4111
|
+
: raw;
|
|
4112
|
+
const v = normalizeOhosApiVersion(data.platformVersion) ||
|
|
4113
|
+
normalizeOhosApiVersion(data.version) ||
|
|
4114
|
+
normalizeOhosApiVersion(raw.platformVersion) ||
|
|
4115
|
+
normalizeOhosApiVersion(raw.version);
|
|
4116
|
+
if (v)
|
|
4117
|
+
return v;
|
|
4118
|
+
}
|
|
4119
|
+
catch {
|
|
4120
|
+
/* try next */
|
|
4121
|
+
}
|
|
3823
4122
|
}
|
|
3824
|
-
|
|
3825
|
-
|
|
4123
|
+
return normalizeOhosApiVersion(path.basename(sdkHome));
|
|
4124
|
+
}
|
|
4125
|
+
/** Expand a user/env path into concrete sdk/default-style homes that contain openharmony/ets. */
|
|
4126
|
+
function expandLocalOhosSdkHomes(input) {
|
|
4127
|
+
const resolved = path.resolve(input);
|
|
4128
|
+
if (!fs.existsSync(resolved))
|
|
4129
|
+
return [];
|
|
4130
|
+
const homes = [];
|
|
4131
|
+
const push = (p) => {
|
|
4132
|
+
if (hasOpenHarmonyEts(p))
|
|
4133
|
+
homes.push(p);
|
|
4134
|
+
};
|
|
4135
|
+
push(resolved);
|
|
4136
|
+
push(path.join(resolved, 'default'));
|
|
4137
|
+
push(path.join(resolved, 'sdk', 'default'));
|
|
4138
|
+
const viaFind = findOhosSdkHome(resolved);
|
|
4139
|
+
if (viaFind)
|
|
4140
|
+
homes.push(viaFind);
|
|
4141
|
+
let entries;
|
|
3826
4142
|
try {
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
4143
|
+
entries = fs.readdirSync(resolved, { withFileTypes: true });
|
|
4144
|
+
}
|
|
4145
|
+
catch {
|
|
4146
|
+
return [...new Set(homes)];
|
|
4147
|
+
}
|
|
4148
|
+
for (const entry of entries) {
|
|
4149
|
+
if (!entry.isDirectory())
|
|
4150
|
+
continue;
|
|
4151
|
+
if (entry.name === 'node_modules' || entry.name === '.git')
|
|
4152
|
+
continue;
|
|
4153
|
+
const child = path.join(resolved, entry.name);
|
|
4154
|
+
push(child);
|
|
4155
|
+
push(path.join(child, 'default'));
|
|
4156
|
+
}
|
|
4157
|
+
return [...new Set(homes)];
|
|
4158
|
+
}
|
|
4159
|
+
function localOhosSdkSearchRoots() {
|
|
4160
|
+
const roots = [];
|
|
4161
|
+
for (const key of ['HOMEGRAPH_OHOS_SDK', 'OHOS_SDK_HOME', 'DEVECO_SDK_HOME', 'HOS_SDK_HOME']) {
|
|
4162
|
+
const v = process.env[key]?.trim();
|
|
4163
|
+
if (v)
|
|
4164
|
+
roots.push(v);
|
|
4165
|
+
}
|
|
4166
|
+
const home = os.homedir();
|
|
4167
|
+
if (process.platform === 'win32') {
|
|
4168
|
+
const lad = process.env.LOCALAPPDATA;
|
|
4169
|
+
if (lad) {
|
|
4170
|
+
roots.push(path.join(lad, 'OpenHarmony', 'Sdk'));
|
|
4171
|
+
roots.push(path.join(lad, 'Huawei', 'Sdk'));
|
|
4172
|
+
}
|
|
4173
|
+
roots.push(path.join('C:\\', 'Program Files', 'Huawei', 'DevEco Studio', 'sdk'));
|
|
4174
|
+
roots.push(path.join(home, 'Huawei', 'Sdk'));
|
|
4175
|
+
roots.push(path.join(home, 'OpenHarmony', 'Sdk'));
|
|
4176
|
+
}
|
|
4177
|
+
else if (process.platform === 'darwin') {
|
|
4178
|
+
roots.push(path.join(home, 'Library', 'Huawei', 'Sdk'));
|
|
4179
|
+
roots.push(path.join(home, 'Library', 'OpenHarmony', 'Sdk'));
|
|
4180
|
+
roots.push('/Applications/DevEco-Studio.app/Contents/sdk');
|
|
4181
|
+
}
|
|
4182
|
+
else {
|
|
4183
|
+
roots.push(path.join(home, 'Huawei', 'Sdk'));
|
|
4184
|
+
roots.push(path.join(home, 'OpenHarmony', 'Sdk'));
|
|
4185
|
+
}
|
|
4186
|
+
return roots;
|
|
4187
|
+
}
|
|
4188
|
+
/** Discover local DevEco / OpenHarmony SDK homes (env vars + common install paths). */
|
|
4189
|
+
function discoverLocalOhosSdkCandidates() {
|
|
4190
|
+
const out = [];
|
|
4191
|
+
const seen = new Set();
|
|
4192
|
+
for (const root of localOhosSdkSearchRoots()) {
|
|
4193
|
+
for (const sdkHome of expandLocalOhosSdkHomes(root)) {
|
|
4194
|
+
if (seen.has(sdkHome))
|
|
4195
|
+
continue;
|
|
4196
|
+
seen.add(sdkHome);
|
|
4197
|
+
out.push({ sdkHome, version: readOhosSdkPlatformVersion(sdkHome) });
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4200
|
+
return out;
|
|
4201
|
+
}
|
|
4202
|
+
/**
|
|
4203
|
+
* Pick a local SDK home.
|
|
4204
|
+
* When `preferredVersion` is set, require an exact platformVersion match.
|
|
4205
|
+
* When unset, return the first candidate that has a readable version (else any).
|
|
4206
|
+
*/
|
|
4207
|
+
function findLocalOhosSdkForVersion(preferredVersion) {
|
|
4208
|
+
const candidates = discoverLocalOhosSdkCandidates();
|
|
4209
|
+
if (candidates.length === 0)
|
|
4210
|
+
return null;
|
|
4211
|
+
if (preferredVersion) {
|
|
4212
|
+
return candidates.find((c) => c.version === preferredVersion) ?? null;
|
|
4213
|
+
}
|
|
4214
|
+
return candidates.find((c) => c.version) ?? candidates[0];
|
|
4215
|
+
}
|
|
4216
|
+
/**
|
|
4217
|
+
* Resolve the API db version for a project: build-profile / env first, else local SDK.
|
|
4218
|
+
*/
|
|
4219
|
+
function resolveOhosApiVersionForProject(projectRoot) {
|
|
4220
|
+
const fromProject = detectOhosCompileSdkVersion(projectRoot);
|
|
4221
|
+
if (fromProject)
|
|
4222
|
+
return fromProject;
|
|
4223
|
+
return findLocalOhosSdkForVersion(null)?.version ?? null;
|
|
4224
|
+
}
|
|
4225
|
+
function attachOhosApiDbBinding(queries, binding) {
|
|
4226
|
+
try {
|
|
4227
|
+
queries.attachOhosApiDb(binding.dbPath);
|
|
4228
|
+
queries.setMetadata(exports.OHOS_API_VERSION_META, binding.version);
|
|
4229
|
+
queries.setMetadata(exports.OHOS_API_DB_PATH_META, binding.dbPath);
|
|
4230
|
+
return binding;
|
|
3833
4231
|
}
|
|
3834
4232
|
catch (err) {
|
|
3835
4233
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3836
4234
|
return {
|
|
3837
|
-
code: '
|
|
3838
|
-
message: `
|
|
3839
|
-
'Explore will use project code only until the matching API db is available.',
|
|
4235
|
+
code: 'ohos_api_db_missing',
|
|
4236
|
+
message: `Failed to attach OHOS API db at ${binding.dbPath}: ${msg}`,
|
|
3840
4237
|
};
|
|
3841
4238
|
}
|
|
3842
|
-
|
|
3843
|
-
|
|
4239
|
+
}
|
|
4240
|
+
/**
|
|
4241
|
+
* Build (or reuse) ~/.homegraph/api/ohos-api-<version>.db from a local SDK.
|
|
4242
|
+
* Never throws — missing SDK / build failure returns a warning.
|
|
4243
|
+
*/
|
|
4244
|
+
async function ensureOhosApiDb(version, options = {}) {
|
|
4245
|
+
const dbPath = ohosApiDbInstallPath(version);
|
|
4246
|
+
const packageName = ohosApiDbPackageName(version);
|
|
4247
|
+
if (fs.existsSync(dbPath)) {
|
|
4248
|
+
return { version, dbPath, packageName, installed: false };
|
|
3844
4249
|
}
|
|
3845
|
-
if (
|
|
4250
|
+
if (options.build === false || process.env.HOMEGRAPH_OHOS_API_SKIP === '1') {
|
|
3846
4251
|
return {
|
|
3847
4252
|
code: 'ohos_api_db_missing',
|
|
3848
|
-
message: `${
|
|
4253
|
+
message: `${ohosApiDbFilename(version)} is not under ~/.homegraph/api/ yet. ` +
|
|
4254
|
+
'Re-run `homegraph init -i` / `homegraph index` with a local OHOS SDK, or `homegraph index-api <sdk>`. ' +
|
|
3849
4255
|
'Explore will use project code only.',
|
|
3850
4256
|
};
|
|
3851
4257
|
}
|
|
3852
|
-
|
|
4258
|
+
let sdkHome = null;
|
|
4259
|
+
if (options.sdkHomeHint) {
|
|
4260
|
+
const hintHomes = expandLocalOhosSdkHomes(options.sdkHomeHint);
|
|
4261
|
+
const match = hintHomes.find((h) => readOhosSdkPlatformVersion(h) === version) ?? hintHomes[0];
|
|
4262
|
+
if (match && hasOpenHarmonyEts(match))
|
|
4263
|
+
sdkHome = match;
|
|
4264
|
+
}
|
|
4265
|
+
if (!sdkHome) {
|
|
4266
|
+
sdkHome = findLocalOhosSdkForVersion(version)?.sdkHome ?? null;
|
|
4267
|
+
}
|
|
4268
|
+
if (!sdkHome) {
|
|
4269
|
+
return {
|
|
4270
|
+
code: 'ohos_api_sdk_missing',
|
|
4271
|
+
message: `Local OHOS SDK for API ${version} not found (set OHOS_SDK_HOME / DEVECO_SDK_HOME / HOMEGRAPH_OHOS_SDK, ` +
|
|
4272
|
+
'or install the matching DevEco SDK). SDK API lookup disabled; project indexing continues normally.',
|
|
4273
|
+
};
|
|
4274
|
+
}
|
|
4275
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4276
|
+
options.onProgress?.({
|
|
4277
|
+
phase: 'ohos-api',
|
|
4278
|
+
current: 0,
|
|
4279
|
+
total: 1,
|
|
4280
|
+
currentFile: version,
|
|
4281
|
+
});
|
|
4282
|
+
try {
|
|
4283
|
+
const result = await indexOhosApiDb({
|
|
4284
|
+
sdkHome,
|
|
4285
|
+
version,
|
|
4286
|
+
outputPath: dbPath,
|
|
4287
|
+
onProgress: options.onProgress
|
|
4288
|
+
? (progress) => {
|
|
4289
|
+
options.onProgress?.({
|
|
4290
|
+
phase: 'ohos-api',
|
|
4291
|
+
current: progress.current,
|
|
4292
|
+
total: progress.total || 1,
|
|
4293
|
+
currentFile: progress.currentFile
|
|
4294
|
+
? `${version} ${path.basename(progress.currentFile)}`
|
|
4295
|
+
: version,
|
|
4296
|
+
subphase: progress.subphase,
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4299
|
+
: undefined,
|
|
4300
|
+
});
|
|
4301
|
+
if (!result.success || !fs.existsSync(dbPath)) {
|
|
4302
|
+
try {
|
|
4303
|
+
fs.unlinkSync(dbPath);
|
|
4304
|
+
}
|
|
4305
|
+
catch {
|
|
4306
|
+
/* ignore */
|
|
4307
|
+
}
|
|
4308
|
+
const detail = result.errors
|
|
4309
|
+
.filter((e) => e.severity === 'error')
|
|
4310
|
+
.map((e) => e.message)
|
|
4311
|
+
.join('; ');
|
|
4312
|
+
return {
|
|
4313
|
+
code: 'ohos_api_build_failed',
|
|
4314
|
+
message: `Failed to build OHOS API db for ${version} from ${sdkHome}` +
|
|
4315
|
+
(detail ? ` (${detail})` : '') +
|
|
4316
|
+
'. SDK API lookup disabled; project indexing continues normally.',
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
options.onProgress?.({
|
|
4320
|
+
phase: 'ohos-api',
|
|
4321
|
+
current: 1,
|
|
4322
|
+
total: 1,
|
|
4323
|
+
currentFile: version,
|
|
4324
|
+
});
|
|
4325
|
+
return { version, dbPath, packageName, installed: true };
|
|
4326
|
+
}
|
|
4327
|
+
catch (err) {
|
|
4328
|
+
try {
|
|
4329
|
+
if (fs.existsSync(dbPath))
|
|
4330
|
+
fs.unlinkSync(dbPath);
|
|
4331
|
+
}
|
|
4332
|
+
catch {
|
|
4333
|
+
/* ignore */
|
|
4334
|
+
}
|
|
4335
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4336
|
+
return {
|
|
4337
|
+
code: 'ohos_api_build_failed',
|
|
4338
|
+
message: `Failed to build OHOS API db for ${version} from ${sdkHome} (${msg}). ` +
|
|
4339
|
+
'SDK API lookup disabled; project indexing continues normally.',
|
|
4340
|
+
};
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
/**
|
|
4344
|
+
* Attach a prebuilt API db if present — never builds. Used on open / wireLayers.
|
|
4345
|
+
*/
|
|
4346
|
+
function attachExistingOhosApiDbForProject(projectRoot, queries, languages) {
|
|
4347
|
+
if (!isOhosArktsProject(projectRoot, languages))
|
|
4348
|
+
return null;
|
|
4349
|
+
const version = resolveOhosApiVersionForProject(projectRoot);
|
|
4350
|
+
if (!version)
|
|
4351
|
+
return null;
|
|
4352
|
+
const dbPath = ohosApiDbInstallPath(version);
|
|
4353
|
+
if (!fs.existsSync(dbPath))
|
|
4354
|
+
return null;
|
|
4355
|
+
return attachOhosApiDbBinding(queries, {
|
|
4356
|
+
version,
|
|
4357
|
+
dbPath,
|
|
4358
|
+
packageName: ohosApiDbPackageName(version),
|
|
4359
|
+
installed: false,
|
|
4360
|
+
});
|
|
3853
4361
|
}
|
|
3854
|
-
/**
|
|
3855
|
-
|
|
4362
|
+
/**
|
|
4363
|
+
* Detect version, build from local SDK when needed, persist metadata, ATTACH — never throws.
|
|
4364
|
+
* Pass `{ build: true }` (default) after init/index; `{ build: false }` on open.
|
|
4365
|
+
*/
|
|
4366
|
+
async function bindOhosApiDbForProject(projectRoot, queries, languages, options = {}) {
|
|
3856
4367
|
if (!isOhosArktsProject(projectRoot, languages))
|
|
3857
4368
|
return null;
|
|
3858
|
-
const version =
|
|
4369
|
+
const version = resolveOhosApiVersionForProject(projectRoot);
|
|
3859
4370
|
if (!version) {
|
|
3860
4371
|
return {
|
|
3861
4372
|
code: 'ohos_api_version_unknown',
|
|
3862
|
-
message: 'HarmonyOS project detected but compileSdkVersion not found in build-profile.json5
|
|
4373
|
+
message: 'HarmonyOS project detected but compileSdkVersion was not found in build-profile.json5 ' +
|
|
4374
|
+
'and no local OHOS SDK was discovered (set OHOS_SDK_HOME / DEVECO_SDK_HOME). ' +
|
|
3863
4375
|
'SDK API lookup disabled; project indexing continues normally.',
|
|
3864
4376
|
};
|
|
3865
4377
|
}
|
|
3866
|
-
const ensured = ensureOhosApiDb(version);
|
|
4378
|
+
const ensured = await ensureOhosApiDb(version, options);
|
|
3867
4379
|
if ('code' in ensured)
|
|
3868
4380
|
return ensured;
|
|
3869
|
-
|
|
3870
|
-
queries.attachOhosApiDb(ensured.dbPath);
|
|
3871
|
-
queries.setMetadata(exports.OHOS_API_VERSION_META, ensured.version);
|
|
3872
|
-
queries.setMetadata(exports.OHOS_API_DB_PATH_META, ensured.dbPath);
|
|
3873
|
-
}
|
|
3874
|
-
catch (err) {
|
|
3875
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3876
|
-
return {
|
|
3877
|
-
code: 'ohos_api_db_missing',
|
|
3878
|
-
message: `Failed to attach OHOS API db at ${ensured.dbPath}: ${msg}`,
|
|
3879
|
-
};
|
|
3880
|
-
}
|
|
3881
|
-
return ensured;
|
|
4381
|
+
return attachOhosApiDbBinding(queries, ensured);
|
|
3882
4382
|
}
|
|
3883
4383
|
/** Re-ATTACH from project metadata after reopen / new QueryBuilder. */
|
|
3884
4384
|
function restoreOhosApiDbAttach(queries) {
|