homegraph 1.5.0 → 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/dist/bin/command-supervision.d.ts.map +1 -1
- package/dist/bin/command-supervision.js +7 -4
- package/dist/bin/command-supervision.js.map +1 -1
- 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 +69 -12
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +739 -86
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- 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.d.ts +18 -0
- package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
- package/dist/mcp/liveness-watchdog.js +134 -22
- package/dist/mcp/liveness-watchdog.js.map +1 -1
- 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 +2 -0
- 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/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 +4 -5
- package/dist/arkts/ohos-api-index.d.ts +0 -15
- package/dist/arkts/ohos-api-index.d.ts.map +0 -1
- package/dist/arkts/ohos-api-index.js +0 -190
- package/dist/arkts/ohos-api-index.js.map +0 -1
- package/dist/arkts/ohos-sdk-input.d.ts +0 -36
- package/dist/arkts/ohos-sdk-input.d.ts.map +0 -1
- package/dist/arkts/ohos-sdk-input.js +0 -214
- package/dist/arkts/ohos-sdk-input.js.map +0 -1
- package/dist/extraction/languages/arkts-state-decorators.d.ts +0 -13
- package/dist/extraction/languages/arkts-state-decorators.d.ts.map +0 -1
- package/dist/extraction/languages/arkts-state-decorators.js +0 -26
- package/dist/extraction/languages/arkts-state-decorators.js.map +0 -1
- package/dist/extraction/languages/arkts-viewtree.d.ts +0 -24
- package/dist/extraction/languages/arkts-viewtree.d.ts.map +0 -1
- package/dist/extraction/languages/arkts-viewtree.js +0 -148
- package/dist/extraction/languages/arkts-viewtree.js.map +0 -1
- package/dist/extraction/languages/ohos-api-consumer.d.ts +0 -34
- package/dist/extraction/languages/ohos-api-consumer.d.ts.map +0 -1
- package/dist/extraction/languages/ohos-api-consumer.js +0 -283
- package/dist/extraction/languages/ohos-api-consumer.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/build/git-scanner.d.ts +0 -93
- package/dist/spec/build/git-scanner.d.ts.map +0 -1
- package/dist/spec/build/git-scanner.js +0 -254
- package/dist/spec/build/git-scanner.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/git-utils.d.ts +0 -8
- package/dist/spec/git-utils.d.ts.map +0 -1
- package/dist/spec/git-utils.js +0 -14
- package/dist/spec/git-utils.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/mine/clusterer.d.ts +0 -63
- package/dist/spec/mine/clusterer.d.ts.map +0 -1
- package/dist/spec/mine/clusterer.js +0 -904
- package/dist/spec/mine/clusterer.js.map +0 -1
- package/dist/spec/mine/progress-handler.d.ts +0 -22
- package/dist/spec/mine/progress-handler.d.ts.map +0 -1
- package/dist/spec/mine/progress-handler.js +0 -108
- package/dist/spec/mine/progress-handler.js.map +0 -1
- package/dist/spec/mine/progress.d.ts +0 -23
- package/dist/spec/mine/progress.d.ts.map +0 -1
- package/dist/spec/mine/progress.js +0 -12
- package/dist/spec/mine/progress.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 -155
- package/dist/spec/utils.d.ts.map +0 -1
- package/dist/spec/utils.js +0 -411
- package/dist/spec/utils.js.map +0 -1
- 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/build-bundle.sh +0 -123
- package/scripts/exp_boundary_eval/README.md +0 -247
- 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
|
@@ -11,11 +11,14 @@
|
|
|
11
11
|
* HarmonyOS multi-module projects (`build-profile.json5`) use
|
|
12
12
|
* `Scene.analyseByModule` so each HAP/HSP/HAR is the atomic unit (like a file
|
|
13
13
|
* for tree-sitter languages): symbols + ViewTree + **intra-module RTA** while
|
|
14
|
-
* the module has BODIES.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* the module has BODIES. Per-module RTA entries = DummyMain (Ability/Component
|
|
15
|
+
* lifecycle) **plus the module export surface** (exported functions and methods
|
|
16
|
+
* of exported classes), so non-UI libraries still grow reachable call edges.
|
|
17
|
+
* CFG does **not** stitch same-module calls (that densified past unlimited RTA).
|
|
18
|
+
* Cross-module edges come from RTA with an exact MethodSignature map, plus a
|
|
19
|
+
* CFG `%unk` bridge that recovers Class.method / free functions from stmt text,
|
|
20
|
+
* UnclearReferenceType names, or PascalCase base locals (import + unique target
|
|
21
|
+
* + fan-out blocklist). Deps load to SIGNATURES.
|
|
19
22
|
*
|
|
20
23
|
* Sync can re-index a **dirty module subset** (`primeArkTSBatch` + `changedFiles`)
|
|
21
24
|
* via `ModuleAnalysisConfig.setTargetModuleIds`, instead of rebuilding every
|
|
@@ -72,7 +75,10 @@ exports.scanEtsFiles = scanEtsFiles;
|
|
|
72
75
|
exports.runIsolatedArkTSBatchEntry = runIsolatedArkTSBatchEntry;
|
|
73
76
|
exports.primeArkTSBatch = primeArkTSBatch;
|
|
74
77
|
exports.isArkTSBatchPersisted = isArkTSBatchPersisted;
|
|
78
|
+
exports.isArkTSBatchCommitted = isArkTSBatchCommitted;
|
|
75
79
|
exports.resetArkTSBatch = resetArkTSBatch;
|
|
80
|
+
exports.shrinkArkTSBatchPostParse = shrinkArkTSBatchPostParse;
|
|
81
|
+
exports.collectExportedRtaEntryPointsFromFiles = collectExportedRtaEntryPointsFromFiles;
|
|
76
82
|
exports.stateDecoratorKinds = stateDecoratorKinds;
|
|
77
83
|
exports.stateTransferViaForField = stateTransferViaForField;
|
|
78
84
|
exports.isUnclearArkClassSignature = isUnclearArkClassSignature;
|
|
@@ -95,7 +101,12 @@ exports.parseJson5Minimal = parseJson5Minimal;
|
|
|
95
101
|
exports.normalizeOhosApiVersion = normalizeOhosApiVersion;
|
|
96
102
|
exports.detectOhosCompileSdkVersion = detectOhosCompileSdkVersion;
|
|
97
103
|
exports.isOhosArktsProject = isOhosArktsProject;
|
|
104
|
+
exports.readOhosSdkPlatformVersion = readOhosSdkPlatformVersion;
|
|
105
|
+
exports.discoverLocalOhosSdkCandidates = discoverLocalOhosSdkCandidates;
|
|
106
|
+
exports.findLocalOhosSdkForVersion = findLocalOhosSdkForVersion;
|
|
107
|
+
exports.resolveOhosApiVersionForProject = resolveOhosApiVersionForProject;
|
|
98
108
|
exports.ensureOhosApiDb = ensureOhosApiDb;
|
|
109
|
+
exports.attachExistingOhosApiDbForProject = attachExistingOhosApiDbForProject;
|
|
99
110
|
exports.bindOhosApiDbForProject = bindOhosApiDbForProject;
|
|
100
111
|
exports.restoreOhosApiDbAttach = restoreOhosApiDbAttach;
|
|
101
112
|
const crypto = __importStar(require("crypto"));
|
|
@@ -113,6 +124,7 @@ const context_1 = require("../context");
|
|
|
113
124
|
const default_ignore_1 = require("../default-ignore");
|
|
114
125
|
const extraction_version_1 = require("../extraction-version");
|
|
115
126
|
const version_1 = require("../../mcp/version");
|
|
127
|
+
const liveness_watchdog_1 = require("../../mcp/liveness-watchdog");
|
|
116
128
|
const tree_sitter_helpers_1 = require("../tree-sitter-helpers");
|
|
117
129
|
const wasm_runtime_flags_1 = require("../wasm-runtime-flags");
|
|
118
130
|
const ignore_1 = __importDefault(require("ignore"));
|
|
@@ -642,6 +654,27 @@ function persistIncrementalModuleResults(rootDir, queries, index) {
|
|
|
642
654
|
persistFileResult(queries, filePath, content, stats, fileResult, { force: true });
|
|
643
655
|
}
|
|
644
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
|
+
}
|
|
645
678
|
async function persistBatchResultsAsync(rootDir, queries, index) {
|
|
646
679
|
const entries = [...index.fileResults.entries()];
|
|
647
680
|
const persistTotal = entries.length;
|
|
@@ -689,18 +722,89 @@ function persistBatchResultsSync(rootDir, queries, index) {
|
|
|
689
722
|
}
|
|
690
723
|
}
|
|
691
724
|
function commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile) {
|
|
692
|
-
|
|
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));
|
|
693
730
|
persistedBatch = { ...index, rootDir, batchKey, etsFiles };
|
|
694
731
|
batchTriggerFile = normIndexPath(triggerFile);
|
|
695
|
-
if (index.fileResults.size > 0) {
|
|
732
|
+
if (batchPersistedPaths.size > 0 || index.fileResults.size > 0) {
|
|
696
733
|
batchBuildCommitted = true;
|
|
697
734
|
}
|
|
698
735
|
return persistedBatch;
|
|
699
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
|
+
}
|
|
700
800
|
function tryReturnCachedBatch(rootDir, triggerFile, etsFiles) {
|
|
701
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.
|
|
702
805
|
if (persistedBatch &&
|
|
703
806
|
persistedBatch.rootDir === rootDir &&
|
|
807
|
+
persistedBatch.fileResults.size > 0 &&
|
|
704
808
|
batchPersistedPaths.has(normalizedTrigger)) {
|
|
705
809
|
return persistedBatch;
|
|
706
810
|
}
|
|
@@ -794,6 +898,8 @@ function runArkTSBatchFullCore(rootDir, queries, triggerFile, etsFiles, batchKey
|
|
|
794
898
|
queries.insertEdges(valid);
|
|
795
899
|
}
|
|
796
900
|
}
|
|
901
|
+
// Keep payload in memory for ArkTSExtractor.extract() same-batch hits.
|
|
902
|
+
// indexAll uses primeArkTSBatch → releaseArkTSBatchHeavyPayload after persist.
|
|
797
903
|
return commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile);
|
|
798
904
|
}
|
|
799
905
|
finally {
|
|
@@ -845,6 +951,27 @@ function runArkTSBatch(rootDir, queries, triggerFile) {
|
|
|
845
951
|
if (cached) {
|
|
846
952
|
return cached;
|
|
847
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
|
+
}
|
|
848
975
|
const batchKey = etsFiles.length > 0 ? computeBatchKey(rootDir, etsFiles) : '';
|
|
849
976
|
return runArkTSBatchFull(rootDir, queries, normalizedTrigger, etsFiles, batchKey);
|
|
850
977
|
}
|
|
@@ -887,13 +1014,25 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
887
1014
|
(0, context_1.setArktsBatchRunning)(true);
|
|
888
1015
|
try {
|
|
889
1016
|
reportBatchProgress('scene', 0, etsFiles.length);
|
|
890
|
-
const
|
|
891
|
-
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) {
|
|
892
1027
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
893
1028
|
throw new Error(fatal?.message ?? 'ArkTS batch produced no indexed files');
|
|
894
1029
|
}
|
|
895
|
-
|
|
896
|
-
|
|
1030
|
+
if (!streamModular) {
|
|
1031
|
+
queries.deleteArkTSCrossFileCallEdges();
|
|
1032
|
+
}
|
|
1033
|
+
if (index.fileResults.size > 0) {
|
|
1034
|
+
await persistBatchResultsAsync(rootDir, queries, index);
|
|
1035
|
+
}
|
|
897
1036
|
if (index.crossFileEdges.length > 0) {
|
|
898
1037
|
const valid = index.crossFileEdges.filter((e) => index.nodeIds.has(e.source) && index.nodeIds.has(e.target));
|
|
899
1038
|
if (valid.length > 0) {
|
|
@@ -901,6 +1040,8 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
901
1040
|
}
|
|
902
1041
|
}
|
|
903
1042
|
commitArkTSBatch(rootDir, batchKey, etsFiles, index, normalizedTrigger);
|
|
1043
|
+
releaseArkTSBatchHeavyPayload();
|
|
1044
|
+
tryForceGc();
|
|
904
1045
|
}
|
|
905
1046
|
finally {
|
|
906
1047
|
(0, context_1.setArktsBatchRunning)(false);
|
|
@@ -918,8 +1059,13 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
918
1059
|
(0, context_1.setArktsBatchRunning)(true);
|
|
919
1060
|
try {
|
|
920
1061
|
reportBatchProgress('scene', 0, etsFiles.length);
|
|
921
|
-
|
|
922
|
-
|
|
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) {
|
|
923
1069
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
924
1070
|
// Fall back to full project batch when the subset produced nothing.
|
|
925
1071
|
const batchKey = etsFiles.length > 0 ? computeBatchKey(rootDir, etsFiles) : '';
|
|
@@ -927,20 +1073,26 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
927
1073
|
process.stderr.write(`\n\x1b[33m ArkTS: incremental modules failed (${fatal.message}); full rebuild\x1b[0m\n`);
|
|
928
1074
|
}
|
|
929
1075
|
const full = buildArkTSIndex(rootDir, etsFiles);
|
|
930
|
-
if (full.fileResults.size === 0) {
|
|
1076
|
+
if (full.fileResults.size === 0 && (full.streamedPersistedPaths?.size ?? 0) === 0) {
|
|
931
1077
|
throw new Error(fatal?.message ?? 'ArkTS incremental batch produced no indexed files');
|
|
932
1078
|
}
|
|
933
1079
|
queries.deleteArkTSCrossFileCallEdges();
|
|
934
|
-
|
|
1080
|
+
if (full.fileResults.size > 0) {
|
|
1081
|
+
await persistBatchResultsAsync(rootDir, queries, full);
|
|
1082
|
+
}
|
|
935
1083
|
if (full.crossFileEdges.length > 0) {
|
|
936
1084
|
const valid = full.crossFileEdges.filter((e) => full.nodeIds.has(e.source) && full.nodeIds.has(e.target));
|
|
937
1085
|
if (valid.length > 0)
|
|
938
1086
|
queries.insertEdges(valid);
|
|
939
1087
|
}
|
|
940
1088
|
commitArkTSBatch(rootDir, batchKey, etsFiles, full, triggerFile);
|
|
1089
|
+
releaseArkTSBatchHeavyPayload();
|
|
1090
|
+
tryForceGc();
|
|
941
1091
|
return;
|
|
942
1092
|
}
|
|
943
|
-
|
|
1093
|
+
if (index.fileResults.size > 0) {
|
|
1094
|
+
persistIncrementalModuleResults(rootDir, queries, index);
|
|
1095
|
+
}
|
|
944
1096
|
if (index.crossFileEdges.length > 0) {
|
|
945
1097
|
const valid = index.crossFileEdges.filter((e) => index.nodeIds.has(e.source) && index.nodeIds.has(e.target));
|
|
946
1098
|
if (valid.length > 0) {
|
|
@@ -950,6 +1102,8 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
950
1102
|
// Unique key so a partial index is never mistaken for a full-project cache hit.
|
|
951
1103
|
const batchKey = `incremental:${moduleSrcPaths.join(',')}`;
|
|
952
1104
|
commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile);
|
|
1105
|
+
releaseArkTSBatchHeavyPayload();
|
|
1106
|
+
tryForceGc();
|
|
953
1107
|
process.stderr.write(`\n\x1b[33m ArkTS: incremental module reindex done (${moduleSrcPaths.join(', ')})\x1b[0m\n`);
|
|
954
1108
|
}
|
|
955
1109
|
finally {
|
|
@@ -960,6 +1114,10 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
960
1114
|
function isArkTSBatchPersisted(filePath) {
|
|
961
1115
|
return batchPersistedPaths.has(normIndexPath(filePath));
|
|
962
1116
|
}
|
|
1117
|
+
/** True after a successful ArkTS batch was committed to SQLite this index run. */
|
|
1118
|
+
function isArkTSBatchCommitted() {
|
|
1119
|
+
return batchBuildCommitted;
|
|
1120
|
+
}
|
|
963
1121
|
/** Clear cached batch state (tests and full re-index). */
|
|
964
1122
|
function resetArkTSBatch() {
|
|
965
1123
|
persistedBatch = null;
|
|
@@ -968,6 +1126,16 @@ function resetArkTSBatch() {
|
|
|
968
1126
|
batchBuildCommitted = false;
|
|
969
1127
|
arktsIndexNotices = [];
|
|
970
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
|
+
}
|
|
971
1139
|
class ArkTSExtractor {
|
|
972
1140
|
filePath;
|
|
973
1141
|
constructor(filePath, _source) {
|
|
@@ -1303,6 +1471,140 @@ function collectNonUiRtaEntryPointsFromFiles(files) {
|
|
|
1303
1471
|
}
|
|
1304
1472
|
return entries;
|
|
1305
1473
|
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Methods another module can call into: module `export` surface as RTA entries.
|
|
1476
|
+
* - exported function/method → itself
|
|
1477
|
+
* - exported class/struct → all of its non-skip methods (with body)
|
|
1478
|
+
* Skips `export … from` re-exports (not this module's IR).
|
|
1479
|
+
*/
|
|
1480
|
+
function collectExportedRtaEntryPointsFromFiles(files) {
|
|
1481
|
+
const seen = new Set();
|
|
1482
|
+
const out = [];
|
|
1483
|
+
const addMethod = (method) => {
|
|
1484
|
+
if (shouldSkipArkMethod(method))
|
|
1485
|
+
return;
|
|
1486
|
+
try {
|
|
1487
|
+
if (!method.getCfg())
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
catch {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
try {
|
|
1494
|
+
const sig = method.getSignature();
|
|
1495
|
+
const key = sig.toString();
|
|
1496
|
+
if (seen.has(key))
|
|
1497
|
+
return;
|
|
1498
|
+
seen.add(key);
|
|
1499
|
+
out.push(sig);
|
|
1500
|
+
}
|
|
1501
|
+
catch {
|
|
1502
|
+
// signature unavailable
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
const addClass = (cls) => {
|
|
1506
|
+
try {
|
|
1507
|
+
for (const method of cls.getMethods(true)) {
|
|
1508
|
+
addMethod(method);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
catch {
|
|
1512
|
+
// class IR unavailable
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const visitExportInfos = (infos) => {
|
|
1516
|
+
for (const info of infos) {
|
|
1517
|
+
try {
|
|
1518
|
+
if (info.getFrom?.())
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
catch {
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
let arkExport;
|
|
1525
|
+
try {
|
|
1526
|
+
arkExport = info.getArkExport?.() ?? null;
|
|
1527
|
+
}
|
|
1528
|
+
catch {
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
if (!arkExport || typeof arkExport !== 'object')
|
|
1532
|
+
continue;
|
|
1533
|
+
const exp = arkExport;
|
|
1534
|
+
// Prefer ExportType when present (METHOD=2, CLASS=1, NAME_SPACE=0).
|
|
1535
|
+
let exportType;
|
|
1536
|
+
try {
|
|
1537
|
+
exportType = exp.getExportType?.();
|
|
1538
|
+
}
|
|
1539
|
+
catch {
|
|
1540
|
+
exportType = undefined;
|
|
1541
|
+
}
|
|
1542
|
+
if (exportType === 2 || (exportType === undefined && typeof exp.getDeclaringArkClass === 'function' && typeof exp.getCfg === 'function')) {
|
|
1543
|
+
addMethod(arkExport);
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (exportType === 1 || (exportType === undefined && typeof exp.getMethods === 'function')) {
|
|
1547
|
+
addClass(arkExport);
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
if (exportType === 0 || typeof exp.getClasses === 'function') {
|
|
1551
|
+
try {
|
|
1552
|
+
for (const cls of exp.getClasses?.() ?? []) {
|
|
1553
|
+
addClass(cls);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
catch {
|
|
1557
|
+
// ignore
|
|
1558
|
+
}
|
|
1559
|
+
const nested = exp.getExportInfos?.();
|
|
1560
|
+
if (nested)
|
|
1561
|
+
visitExportInfos(nested);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
for (const file of files) {
|
|
1566
|
+
try {
|
|
1567
|
+
visitExportInfos(file.getExportInfos());
|
|
1568
|
+
}
|
|
1569
|
+
catch {
|
|
1570
|
+
// ignore
|
|
1571
|
+
}
|
|
1572
|
+
try {
|
|
1573
|
+
for (const ns of file.getNamespaces()) {
|
|
1574
|
+
try {
|
|
1575
|
+
visitExportInfos(ns.getExportInfos());
|
|
1576
|
+
}
|
|
1577
|
+
catch {
|
|
1578
|
+
// ignore
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
catch {
|
|
1583
|
+
// ignore
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return out;
|
|
1587
|
+
}
|
|
1588
|
+
/** Merge RTA entry signatures, preserving order and dropping duplicates. */
|
|
1589
|
+
function mergeRtaEntryPoints(...groups) {
|
|
1590
|
+
const seen = new Set();
|
|
1591
|
+
const out = [];
|
|
1592
|
+
for (const group of groups) {
|
|
1593
|
+
for (const sig of group) {
|
|
1594
|
+
try {
|
|
1595
|
+
const key = sig.toString();
|
|
1596
|
+
if (seen.has(key))
|
|
1597
|
+
continue;
|
|
1598
|
+
seen.add(key);
|
|
1599
|
+
out.push(sig);
|
|
1600
|
+
}
|
|
1601
|
+
catch {
|
|
1602
|
+
// ignore
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return out;
|
|
1607
|
+
}
|
|
1306
1608
|
function resolveRtaEntryPoints(scene) {
|
|
1307
1609
|
return resolveRtaEntryPointsFromFiles(scene, scene.getFiles());
|
|
1308
1610
|
}
|
|
@@ -1889,6 +2191,9 @@ class ArkTSAdapter {
|
|
|
1889
2191
|
this.stitchModuleCfgCallsAndRefs(files, moduleFiles);
|
|
1890
2192
|
// Drop live IR object keys so ArkAnalyzer can GC unloaded modules; keep sig→id maps.
|
|
1891
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();
|
|
1892
2197
|
}
|
|
1893
2198
|
/**
|
|
1894
2199
|
* Index symbols from already-loaded non-target modules into sig/name maps only.
|
|
@@ -2054,6 +2359,10 @@ class ArkTSAdapter {
|
|
|
2054
2359
|
if (dummyMain) {
|
|
2055
2360
|
this.indexArkanalyzerDummyMain(dummyMain);
|
|
2056
2361
|
}
|
|
2362
|
+
// Export surface = cross-module call-ins; seed them as RTA entries so
|
|
2363
|
+
// non-UI libraries (Manager.getInstance → …) grow intra-module edges
|
|
2364
|
+
// without waiting for a Component DummyMain to reach them.
|
|
2365
|
+
entryPoints = mergeRtaEntryPoints(entryPoints, collectExportedRtaEntryPointsFromFiles(files));
|
|
2057
2366
|
if (entryPoints.length === 0)
|
|
2058
2367
|
return;
|
|
2059
2368
|
let callGraph;
|
|
@@ -2323,6 +2632,30 @@ class ArkTSAdapter {
|
|
|
2323
2632
|
errors: [...errors],
|
|
2324
2633
|
};
|
|
2325
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
|
+
}
|
|
2326
2659
|
indexScannedFile(arkFile) {
|
|
2327
2660
|
const relativePath = normalizeRelPath(this.rootDir, arkFile.getFilePath());
|
|
2328
2661
|
if (!this.scanned.has(relativePath))
|
|
@@ -3072,8 +3405,17 @@ function emptyArkTSBatchIndex(errors) {
|
|
|
3072
3405
|
* HomeGraph structures in the callback, let ArkAnalyzer evict under memoryLimitMB.
|
|
3073
3406
|
* When `targetModuleSrcPaths` is set, only those modules are BODIES targets
|
|
3074
3407
|
* (deps still load at SIGNATURES) — used for incremental sync.
|
|
3408
|
+
*
|
|
3409
|
+
* Runs with the #850 liveness watchdog suspended: ArkAnalyzer's native BODIES
|
|
3410
|
+
* load between module callbacks routinely exceeds the 60s heartbeat window on
|
|
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).
|
|
3075
3414
|
*/
|
|
3076
3415
|
function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
3416
|
+
return (0, liveness_watchdog_1.runWithoutLivenessWatchdog)(() => buildArkTSIndexByModuleInner(rootDir, scannedFiles, options));
|
|
3417
|
+
}
|
|
3418
|
+
function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
3077
3419
|
const errors = [];
|
|
3078
3420
|
const memoryLimitMB = resolveArkTSSceneMemoryLimitMB();
|
|
3079
3421
|
const sceneConfig = buildSceneConfig(rootDir, { memoryLimitMB });
|
|
@@ -3082,6 +3424,13 @@ function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
|
3082
3424
|
let modulesIndexed = 0;
|
|
3083
3425
|
const targetSrc = options?.targetModuleSrcPaths?.map(normalizeHarmonyModuleSrcPath);
|
|
3084
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
|
+
};
|
|
3085
3434
|
try {
|
|
3086
3435
|
scene.config(sceneConfig);
|
|
3087
3436
|
const config = new arkanalyzer_1.ModuleAnalysisConfig();
|
|
@@ -3124,18 +3473,83 @@ function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
|
3124
3473
|
return emptyArkTSBatchIndex(errors);
|
|
3125
3474
|
}
|
|
3126
3475
|
config.setTargetModuleIds(ids);
|
|
3127
|
-
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`);
|
|
3128
3477
|
}
|
|
3129
3478
|
else {
|
|
3130
3479
|
config.setIncludeType(arkanalyzer_1.ModuleType.PROJECT, true);
|
|
3131
|
-
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`);
|
|
3132
3481
|
}
|
|
3133
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);
|
|
3134
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
|
+
}
|
|
3135
3537
|
modulesIndexed++;
|
|
3136
3538
|
const name = module.getModuleName() || path.basename(module.getModulePath());
|
|
3137
|
-
|
|
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`);
|
|
3138
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
|
+
}
|
|
3139
3553
|
if (modulesIndexed === 0) {
|
|
3140
3554
|
errors.push({
|
|
3141
3555
|
message: 'ArkTS analyseByModule found no PROJECT modules (check build-profile.json5); falling back to full Scene build',
|
|
@@ -3153,8 +3567,15 @@ function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
|
3153
3567
|
severity: 'warning',
|
|
3154
3568
|
});
|
|
3155
3569
|
}
|
|
3570
|
+
if (streamQueries) {
|
|
3571
|
+
const leftover = adapter.takeFileResults(adapter.fileResultPaths());
|
|
3572
|
+
persistAndDropModuleSlice(rootDir, streamQueries, leftover, streamedPersistedPaths, persistOrdinal);
|
|
3573
|
+
}
|
|
3156
3574
|
const index = adapter.toBatchIndex(errors);
|
|
3157
|
-
if (
|
|
3575
|
+
if (streamQueries) {
|
|
3576
|
+
index.streamedPersistedPaths = streamedPersistedPaths;
|
|
3577
|
+
}
|
|
3578
|
+
if (index.fileResults.size === 0 && streamedPersistedPaths.size === 0) {
|
|
3158
3579
|
errors.push({
|
|
3159
3580
|
message: 'ArkTS analyseByModule indexed modules but no scanned source files matched',
|
|
3160
3581
|
severity: 'warning',
|
|
@@ -3173,7 +3594,7 @@ function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
|
3173
3594
|
}
|
|
3174
3595
|
finally {
|
|
3175
3596
|
try {
|
|
3176
|
-
scene
|
|
3597
|
+
releaseArkAnalyzerScene(scene);
|
|
3177
3598
|
}
|
|
3178
3599
|
catch {
|
|
3179
3600
|
// ignore dispose failures
|
|
@@ -3181,28 +3602,44 @@ function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
|
3181
3602
|
}
|
|
3182
3603
|
}
|
|
3183
3604
|
function buildArkTSIndexFull(rootDir, scannedFiles, priorErrors = []) {
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3605
|
+
// Full Scene build is the same opaque native stall as analyseByModule.
|
|
3606
|
+
return (0, liveness_watchdog_1.runWithoutLivenessWatchdog)(() => {
|
|
3607
|
+
const { scene, errors } = tryBuildArkScene(rootDir);
|
|
3608
|
+
const allErrors = [...priorErrors, ...errors];
|
|
3609
|
+
if (!scene) {
|
|
3610
|
+
return emptyArkTSBatchIndex(allErrors);
|
|
3611
|
+
}
|
|
3612
|
+
try {
|
|
3613
|
+
const adapter = new ArkTSAdapter(rootDir, scannedFiles);
|
|
3614
|
+
const index = adapter.build(scene);
|
|
3615
|
+
index.errors.push(...allErrors);
|
|
3616
|
+
return index;
|
|
3617
|
+
}
|
|
3618
|
+
catch (e) {
|
|
3619
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
3620
|
+
allErrors.push({ message: `ArkTS adapter failed: ${message}`, severity: 'error' });
|
|
3621
|
+
return emptyArkTSBatchIndex(allErrors);
|
|
3622
|
+
}
|
|
3623
|
+
finally {
|
|
3624
|
+
try {
|
|
3625
|
+
releaseArkAnalyzerScene(scene);
|
|
3626
|
+
}
|
|
3627
|
+
catch {
|
|
3628
|
+
// ignore
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
});
|
|
3200
3632
|
}
|
|
3201
|
-
function buildArkTSIndex(rootDir, scannedFiles) {
|
|
3633
|
+
function buildArkTSIndex(rootDir, scannedFiles, options) {
|
|
3202
3634
|
if (shouldUseModularArkTSBuild(rootDir)) {
|
|
3203
|
-
const modular = buildArkTSIndexByModule(rootDir, scannedFiles
|
|
3635
|
+
const modular = buildArkTSIndexByModule(rootDir, scannedFiles, {
|
|
3636
|
+
streamQueries: options?.streamQueries,
|
|
3637
|
+
});
|
|
3204
3638
|
const fatal = modular.errors.find((e) => e.severity === 'error');
|
|
3205
|
-
|
|
3639
|
+
const hasOutput = modular.fileResults.size > 0 ||
|
|
3640
|
+
(modular.streamedPersistedPaths?.size ?? 0) > 0 ||
|
|
3641
|
+
modular.nodeIds.size > 0;
|
|
3642
|
+
if (hasOutput && !fatal) {
|
|
3206
3643
|
return modular;
|
|
3207
3644
|
}
|
|
3208
3645
|
// Warnings like "no PROJECT modules" / modular failure → full Scene fallback.
|
|
@@ -3367,7 +3804,7 @@ function extractOhosSdkArchive(archivePath) {
|
|
|
3367
3804
|
function ohosApiDbFilename(version) {
|
|
3368
3805
|
return `ohos-api-${version}.db`;
|
|
3369
3806
|
}
|
|
3370
|
-
/**
|
|
3807
|
+
/** Metadata / publish id — one package per API version (e.g. homegraph-ohos-api-db-6.0.1). */
|
|
3371
3808
|
function ohosApiDbPackageName(version) {
|
|
3372
3809
|
return `homegraph-ohos-api-db-${version}`;
|
|
3373
3810
|
}
|
|
@@ -3515,7 +3952,7 @@ async function indexOhosApiDb(options) {
|
|
|
3515
3952
|
}
|
|
3516
3953
|
}
|
|
3517
3954
|
// =============================================================================
|
|
3518
|
-
// OHOS API db consumer: compileSdkVersion detect,
|
|
3955
|
+
// OHOS API db consumer: compileSdkVersion detect, local SDK build, ATTACH
|
|
3519
3956
|
// =============================================================================
|
|
3520
3957
|
exports.OHOS_API_FILE_PREFIX = 'ohos-sdk:';
|
|
3521
3958
|
exports.OHOS_API_VERSION_META = 'ohos_api_version';
|
|
@@ -3657,75 +4094,291 @@ function hasEtsUnder(dir, depth) {
|
|
|
3657
4094
|
}
|
|
3658
4095
|
return false;
|
|
3659
4096
|
}
|
|
3660
|
-
/**
|
|
3661
|
-
function
|
|
3662
|
-
const
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
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
|
+
}
|
|
3670
4122
|
}
|
|
3671
|
-
|
|
3672
|
-
|
|
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;
|
|
3673
4142
|
try {
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
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;
|
|
3680
4231
|
}
|
|
3681
4232
|
catch (err) {
|
|
3682
4233
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3683
4234
|
return {
|
|
3684
|
-
code: '
|
|
3685
|
-
message: `
|
|
3686
|
-
'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}`,
|
|
3687
4237
|
};
|
|
3688
4238
|
}
|
|
3689
|
-
|
|
3690
|
-
|
|
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 };
|
|
3691
4249
|
}
|
|
3692
|
-
if (
|
|
4250
|
+
if (options.build === false || process.env.HOMEGRAPH_OHOS_API_SKIP === '1') {
|
|
3693
4251
|
return {
|
|
3694
4252
|
code: 'ohos_api_db_missing',
|
|
3695
|
-
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>`. ' +
|
|
3696
4255
|
'Explore will use project code only.',
|
|
3697
4256
|
};
|
|
3698
4257
|
}
|
|
3699
|
-
|
|
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
|
+
}
|
|
3700
4342
|
}
|
|
3701
|
-
/**
|
|
3702
|
-
|
|
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
|
+
});
|
|
4361
|
+
}
|
|
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 = {}) {
|
|
3703
4367
|
if (!isOhosArktsProject(projectRoot, languages))
|
|
3704
4368
|
return null;
|
|
3705
|
-
const version =
|
|
4369
|
+
const version = resolveOhosApiVersionForProject(projectRoot);
|
|
3706
4370
|
if (!version) {
|
|
3707
4371
|
return {
|
|
3708
4372
|
code: 'ohos_api_version_unknown',
|
|
3709
|
-
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). ' +
|
|
3710
4375
|
'SDK API lookup disabled; project indexing continues normally.',
|
|
3711
4376
|
};
|
|
3712
4377
|
}
|
|
3713
|
-
const ensured = ensureOhosApiDb(version);
|
|
4378
|
+
const ensured = await ensureOhosApiDb(version, options);
|
|
3714
4379
|
if ('code' in ensured)
|
|
3715
4380
|
return ensured;
|
|
3716
|
-
|
|
3717
|
-
queries.attachOhosApiDb(ensured.dbPath);
|
|
3718
|
-
queries.setMetadata(exports.OHOS_API_VERSION_META, ensured.version);
|
|
3719
|
-
queries.setMetadata(exports.OHOS_API_DB_PATH_META, ensured.dbPath);
|
|
3720
|
-
}
|
|
3721
|
-
catch (err) {
|
|
3722
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3723
|
-
return {
|
|
3724
|
-
code: 'ohos_api_db_missing',
|
|
3725
|
-
message: `Failed to attach OHOS API db at ${ensured.dbPath}: ${msg}`,
|
|
3726
|
-
};
|
|
3727
|
-
}
|
|
3728
|
-
return ensured;
|
|
4381
|
+
return attachOhosApiDbBinding(queries, ensured);
|
|
3729
4382
|
}
|
|
3730
4383
|
/** Re-ATTACH from project metadata after reopen / new QueryBuilder. */
|
|
3731
4384
|
function restoreOhosApiDbAttach(queries) {
|