homegraph 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +131 -0
- package/LICENSE +21 -21
- package/README.md +312 -305
- package/dist/arkui/index.d.ts +4 -0
- package/dist/arkui/index.d.ts.map +1 -0
- package/dist/arkui/index.js +23 -0
- package/dist/arkui/index.js.map +1 -0
- package/dist/arkui/migrate-passage.d.ts +28 -0
- package/dist/arkui/migrate-passage.d.ts.map +1 -0
- package/dist/arkui/migrate-passage.js +310 -0
- package/dist/arkui/migrate-passage.js.map +1 -0
- package/dist/arkui/migrate-semantics.d.ts +47 -0
- package/dist/arkui/migrate-semantics.d.ts.map +1 -0
- package/dist/arkui/migrate-semantics.js +229 -0
- package/dist/arkui/migrate-semantics.js.map +1 -0
- package/dist/arkui/migrate-snapshot.d.ts +79 -0
- package/dist/arkui/migrate-snapshot.d.ts.map +1 -0
- package/dist/arkui/migrate-snapshot.js +340 -0
- package/dist/arkui/migrate-snapshot.js.map +1 -0
- package/dist/bin/fatal-handler.js +2 -2
- package/dist/bin/homegraph.js +111 -162
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/bin/node-version-check.d.ts +6 -15
- package/dist/bin/node-version-check.d.ts.map +1 -1
- package/dist/bin/node-version-check.js +8 -37
- package/dist/bin/node-version-check.js.map +1 -1
- package/dist/db/index.d.ts +36 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +124 -38
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +52 -38
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/queries.d.ts +42 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +330 -163
- package/dist/db/queries.js.map +1 -1
- package/dist/db/schema.sql +208 -203
- package/dist/db/sqlite-adapter.d.ts +8 -3
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +45 -3
- package/dist/db/sqlite-adapter.js.map +1 -1
- package/dist/directory.js +5 -5
- package/dist/extraction/extraction-version.d.ts +1 -1
- package/dist/extraction/extraction-version.js +1 -1
- package/dist/extraction/generated-detection.d.ts +42 -11
- package/dist/extraction/generated-detection.d.ts.map +1 -1
- package/dist/extraction/generated-detection.js +141 -20
- package/dist/extraction/generated-detection.js.map +1 -1
- package/dist/extraction/index.d.ts +43 -1
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +150 -43
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +70 -6
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +958 -108
- 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/extraction/wasm-runtime-flags.d.ts.map +1 -1
- package/dist/extraction/wasm-runtime-flags.js +9 -6
- package/dist/extraction/wasm-runtime-flags.js.map +1 -1
- package/dist/graph-sources.d.ts +41 -0
- package/dist/graph-sources.d.ts.map +1 -0
- package/dist/graph-sources.js +82 -0
- package/dist/graph-sources.js.map +1 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +80 -9
- package/dist/index.js.map +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +0 -48
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/instructions-template.js +9 -9
- package/dist/installer/targets/antigravity.js +1 -1
- package/dist/installer/targets/antigravity.js.map +1 -1
- package/dist/installer/targets/deveco.js +1 -1
- package/dist/installer/targets/deveco.js.map +1 -1
- package/dist/installer/targets/hermes.d.ts +1 -1
- package/dist/installer/targets/hermes.js +2 -2
- package/dist/installer/targets/hermes.js.map +1 -1
- package/dist/installer/targets/opencode.js +1 -1
- package/dist/installer/targets/opencode.js.map +1 -1
- package/dist/installer/targets/shared.js +1 -1
- package/dist/installer/targets/shared.js.map +1 -1
- package/dist/mcp/daemon-paths.d.ts +5 -4
- package/dist/mcp/daemon-paths.d.ts.map +1 -1
- package/dist/mcp/daemon-paths.js +28 -13
- package/dist/mcp/daemon-paths.js.map +1 -1
- package/dist/mcp/daemon-registry.d.ts.map +1 -1
- package/dist/mcp/daemon-registry.js +4 -1
- package/dist/mcp/daemon-registry.js.map +1 -1
- package/dist/mcp/daemon.d.ts +1 -1
- package/dist/mcp/daemon.js +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/engine.d.ts.map +1 -1
- package/dist/mcp/engine.js +12 -3
- package/dist/mcp/engine.js.map +1 -1
- package/dist/mcp/explore-dedup.d.ts +137 -0
- package/dist/mcp/explore-dedup.d.ts.map +1 -0
- package/dist/mcp/explore-dedup.js +236 -0
- package/dist/mcp/explore-dedup.js.map +1 -0
- package/dist/mcp/explore-session-state.d.ts +217 -0
- package/dist/mcp/explore-session-state.d.ts.map +1 -0
- package/dist/mcp/explore-session-state.js +322 -0
- package/dist/mcp/explore-session-state.js.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +4 -7
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/liveness-watchdog.js +51 -51
- package/dist/mcp/proxy.d.ts.map +1 -1
- package/dist/mcp/proxy.js +5 -14
- package/dist/mcp/proxy.js.map +1 -1
- package/dist/mcp/query-worker.d.ts +1 -1
- package/dist/mcp/query-worker.js +1 -1
- package/dist/mcp/server-instructions.d.ts +7 -2
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +125 -47
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/session.d.ts +14 -3
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +17 -14
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/startup-handshake.d.ts +1 -1
- package/dist/mcp/startup-handshake.js +1 -1
- package/dist/mcp/tools.d.ts +138 -12
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +3279 -476
- 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/frameworks/arkts-napi.d.ts +14 -0
- package/dist/resolution/frameworks/arkts-napi.d.ts.map +1 -1
- package/dist/resolution/frameworks/arkts-napi.js +169 -49
- package/dist/resolution/frameworks/arkts-napi.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/search/query-utils.d.ts +116 -4
- package/dist/search/query-utils.d.ts.map +1 -1
- package/dist/search/query-utils.js +869 -60
- package/dist/search/query-utils.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/types.d.ts +8 -0
- package/dist/types.d.ts.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/dist/upgrade/index.d.ts +14 -26
- package/dist/upgrade/index.d.ts.map +1 -1
- package/dist/upgrade/index.js +28 -123
- package/dist/upgrade/index.js.map +1 -1
- package/dist/upgrade/remove-binary.d.ts +19 -19
- package/dist/upgrade/remove-binary.d.ts.map +1 -1
- package/dist/upgrade/remove-binary.js +20 -21
- package/dist/upgrade/remove-binary.js.map +1 -1
- package/dist/upgrade/update-check.d.ts +4 -3
- package/dist/upgrade/update-check.d.ts.map +1 -1
- package/dist/upgrade/update-check.js +4 -3
- package/dist/upgrade/update-check.js.map +1 -1
- package/package.json +65 -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/dist/telemetry/index.d.ts +0 -143
- package/dist/telemetry/index.d.ts.map +0 -1
- package/dist/telemetry/index.js +0 -541
- package/dist/telemetry/index.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,9 +75,13 @@ 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;
|
|
83
|
+
exports.fieldDecoratorEntries = fieldDecoratorEntries;
|
|
84
|
+
exports.fieldDecoratorsEncoded = fieldDecoratorsEncoded;
|
|
81
85
|
exports.stateTransferViaForField = stateTransferViaForField;
|
|
82
86
|
exports.isUnclearArkClassSignature = isUnclearArkClassSignature;
|
|
83
87
|
exports.isUnclearArkMethodSignature = isUnclearArkMethodSignature;
|
|
@@ -99,7 +103,13 @@ exports.parseJson5Minimal = parseJson5Minimal;
|
|
|
99
103
|
exports.normalizeOhosApiVersion = normalizeOhosApiVersion;
|
|
100
104
|
exports.detectOhosCompileSdkVersion = detectOhosCompileSdkVersion;
|
|
101
105
|
exports.isOhosArktsProject = isOhosArktsProject;
|
|
106
|
+
exports.readOhosSdkPlatformVersion = readOhosSdkPlatformVersion;
|
|
107
|
+
exports.discoverLocalOhosSdkCandidates = discoverLocalOhosSdkCandidates;
|
|
108
|
+
exports.findLocalOhosSdkForVersion = findLocalOhosSdkForVersion;
|
|
109
|
+
exports.resolveOhosApiVersionForProject = resolveOhosApiVersionForProject;
|
|
110
|
+
exports.tryDownloadOhosApiDbFromNpm = tryDownloadOhosApiDbFromNpm;
|
|
102
111
|
exports.ensureOhosApiDb = ensureOhosApiDb;
|
|
112
|
+
exports.attachExistingOhosApiDbForProject = attachExistingOhosApiDbForProject;
|
|
103
113
|
exports.bindOhosApiDbForProject = bindOhosApiDbForProject;
|
|
104
114
|
exports.restoreOhosApiDbAttach = restoreOhosApiDbAttach;
|
|
105
115
|
const crypto = __importStar(require("crypto"));
|
|
@@ -115,9 +125,12 @@ const directory_1 = require("../../directory");
|
|
|
115
125
|
const project_config_1 = require("../../project-config");
|
|
116
126
|
const context_1 = require("../context");
|
|
117
127
|
const default_ignore_1 = require("../default-ignore");
|
|
128
|
+
const generated_detection_1 = require("../generated-detection");
|
|
118
129
|
const extraction_version_1 = require("../extraction-version");
|
|
119
130
|
const version_1 = require("../../mcp/version");
|
|
120
131
|
const liveness_watchdog_1 = require("../../mcp/liveness-watchdog");
|
|
132
|
+
const migrate_semantics_1 = require("../../arkui/migrate-semantics");
|
|
133
|
+
const migrate_passage_1 = require("../../arkui/migrate-passage");
|
|
121
134
|
const tree_sitter_helpers_1 = require("../tree-sitter-helpers");
|
|
122
135
|
const wasm_runtime_flags_1 = require("../wasm-runtime-flags");
|
|
123
136
|
const ignore_1 = __importDefault(require("ignore"));
|
|
@@ -473,11 +486,16 @@ function hasHarmonyProjectMarkers(rootDir) {
|
|
|
473
486
|
function normIndexPath(filePath) {
|
|
474
487
|
return filePath.replace(/\\/g, '/');
|
|
475
488
|
}
|
|
476
|
-
/**
|
|
489
|
+
/** Photos-style member calls: `sdk.Asset_setCropRect(`. */
|
|
477
490
|
const NAPI_MEMBER_CALL_RE = /\.\s*([A-Z][A-Za-z0-9_]*(?:_[A-Za-z0-9_]+)+)\s*\(/g;
|
|
491
|
+
/** `libFoo.so` module path from ArkAnalyzer import infos. */
|
|
492
|
+
const LIB_SO_MODULE_RE = /^lib[A-Za-z0-9_]+\.so$/;
|
|
478
493
|
function isNapiSymbolName(name) {
|
|
479
494
|
return /^[A-Z][A-Za-z0-9_]*_[A-Za-z0-9_]+$/.test(name);
|
|
480
495
|
}
|
|
496
|
+
function escapeRegExp(s) {
|
|
497
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
498
|
+
}
|
|
481
499
|
let persistedBatch = null;
|
|
482
500
|
/** File that triggered the most recent batch persist (for indexAll edge stats). */
|
|
483
501
|
let batchTriggerFile = null;
|
|
@@ -628,6 +646,7 @@ function persistFileResult(queries, filePath, content, stats, result, options) {
|
|
|
628
646
|
indexedAt: Date.now(),
|
|
629
647
|
nodeCount: result.nodes.length,
|
|
630
648
|
errors: result.errors.length > 0 ? result.errors : undefined,
|
|
649
|
+
generated: (0, generated_detection_1.detectGeneratedFile)(filePath, content),
|
|
631
650
|
};
|
|
632
651
|
queries.upsertFile(fileRecord);
|
|
633
652
|
}
|
|
@@ -647,6 +666,27 @@ function persistIncrementalModuleResults(rootDir, queries, index) {
|
|
|
647
666
|
persistFileResult(queries, filePath, content, stats, fileResult, { force: true });
|
|
648
667
|
}
|
|
649
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* Persist a module slice then drop it from the in-memory map (streaming modular build).
|
|
671
|
+
* Returns the number of files written.
|
|
672
|
+
*/
|
|
673
|
+
function persistAndDropModuleSlice(rootDir, queries, slice, streamedPersistedPaths, persistOrdinal) {
|
|
674
|
+
if (slice.size === 0)
|
|
675
|
+
return 0;
|
|
676
|
+
for (const [filePath, fileResult] of slice) {
|
|
677
|
+
persistOrdinal.done++;
|
|
678
|
+
reportBatchProgress('persist', Math.min(persistOrdinal.done, persistOrdinal.totalHint), persistOrdinal.totalHint, filePath);
|
|
679
|
+
const isVirtual = filePath.startsWith('@');
|
|
680
|
+
const full = path.join(rootDir, filePath);
|
|
681
|
+
const content = isVirtual ? '' : fs.readFileSync(full, 'utf-8');
|
|
682
|
+
const stats = isVirtual
|
|
683
|
+
? { size: 0, mtimeMs: Date.now() }
|
|
684
|
+
: fs.statSync(full);
|
|
685
|
+
persistFileResult(queries, filePath, content, stats, fileResult, { force: true });
|
|
686
|
+
streamedPersistedPaths.add(normIndexPath(filePath));
|
|
687
|
+
}
|
|
688
|
+
return slice.size;
|
|
689
|
+
}
|
|
650
690
|
async function persistBatchResultsAsync(rootDir, queries, index) {
|
|
651
691
|
const entries = [...index.fileResults.entries()];
|
|
652
692
|
const persistTotal = entries.length;
|
|
@@ -694,18 +734,89 @@ function persistBatchResultsSync(rootDir, queries, index) {
|
|
|
694
734
|
}
|
|
695
735
|
}
|
|
696
736
|
function commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile) {
|
|
697
|
-
|
|
737
|
+
const streamed = index.streamedPersistedPaths;
|
|
738
|
+
batchPersistedPaths =
|
|
739
|
+
streamed && streamed.size > 0
|
|
740
|
+
? new Set([...streamed].map(normIndexPath))
|
|
741
|
+
: new Set([...index.fileResults.keys()].map(normIndexPath));
|
|
698
742
|
persistedBatch = { ...index, rootDir, batchKey, etsFiles };
|
|
699
743
|
batchTriggerFile = normIndexPath(triggerFile);
|
|
700
|
-
if (index.fileResults.size > 0) {
|
|
744
|
+
if (batchPersistedPaths.size > 0 || index.fileResults.size > 0) {
|
|
701
745
|
batchBuildCommitted = true;
|
|
702
746
|
}
|
|
703
747
|
return persistedBatch;
|
|
704
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
* Drop in-memory ExtractionResults / edges after they are in SQLite.
|
|
751
|
+
* Keep path sets so `isArkTSBatchPersisted` still skips re-parse; matches the
|
|
752
|
+
* isolated-worker commit shape (empty fileResults).
|
|
753
|
+
*/
|
|
754
|
+
function releaseArkTSBatchHeavyPayload() {
|
|
755
|
+
if (!persistedBatch)
|
|
756
|
+
return;
|
|
757
|
+
persistedBatch.fileResults.clear();
|
|
758
|
+
persistedBatch.crossFileEdges = [];
|
|
759
|
+
persistedBatch.nodeIds.clear();
|
|
760
|
+
persistedBatch.errors = [];
|
|
761
|
+
}
|
|
762
|
+
/** Best-effort reclaim after Scene / batch teardown (needs `node --expose-gc`). */
|
|
763
|
+
function tryForceGc() {
|
|
764
|
+
const gc = globalThis.gc;
|
|
765
|
+
if (typeof gc === 'function') {
|
|
766
|
+
try {
|
|
767
|
+
gc();
|
|
768
|
+
}
|
|
769
|
+
catch {
|
|
770
|
+
// ignore
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Unload every module's IR then dispose Scene globals. Plain `scene.dispose()`
|
|
776
|
+
* only clears SDK helpers / moduleCache — project ArkFiles stay reachable and
|
|
777
|
+
* pin multi-GB RSS into Parsing/Resolving.
|
|
778
|
+
*/
|
|
779
|
+
function releaseArkAnalyzerScene(scene) {
|
|
780
|
+
try {
|
|
781
|
+
for (const mod of scene.getModules()) {
|
|
782
|
+
try {
|
|
783
|
+
scene.disposeModule(mod);
|
|
784
|
+
mod.setFileDepGraph(undefined);
|
|
785
|
+
mod.clearFilesMap();
|
|
786
|
+
mod.setLoadState(arkanalyzer_1.ModuleLoadState.NOT_LOADED);
|
|
787
|
+
}
|
|
788
|
+
catch {
|
|
789
|
+
// per-module best-effort
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
catch {
|
|
794
|
+
// ignore
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
scene.dispose();
|
|
798
|
+
}
|
|
799
|
+
catch {
|
|
800
|
+
// ignore
|
|
801
|
+
}
|
|
802
|
+
tryForceGc();
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* ArkAnalyzer 1.0.92 keeps INDEX hollow on {@link ModuleBuilder} as a private
|
|
806
|
+
* method (public eviction paths call it internally). HomeGraph needs the same
|
|
807
|
+
* post-ingest hollow; call through a narrow cast until a public API exists.
|
|
808
|
+
*/
|
|
809
|
+
function hollowModuleToIndex(builder, moduleId) {
|
|
810
|
+
builder.downgradeModule(moduleId, arkanalyzer_1.ModuleDepthLevel.INDEX);
|
|
811
|
+
}
|
|
705
812
|
function tryReturnCachedBatch(rootDir, triggerFile, etsFiles) {
|
|
706
813
|
const normalizedTrigger = normIndexPath(triggerFile);
|
|
814
|
+
// Only reuse when payload is still in memory. After
|
|
815
|
+
// releaseArkTSBatchHeavyPayload(), fileResults is empty — fall through to
|
|
816
|
+
// rebuild (same as isolated-worker commit) rather than return a hollow cache.
|
|
707
817
|
if (persistedBatch &&
|
|
708
818
|
persistedBatch.rootDir === rootDir &&
|
|
819
|
+
persistedBatch.fileResults.size > 0 &&
|
|
709
820
|
batchPersistedPaths.has(normalizedTrigger)) {
|
|
710
821
|
return persistedBatch;
|
|
711
822
|
}
|
|
@@ -789,7 +900,9 @@ function runArkTSBatchFullCore(rootDir, queries, triggerFile, etsFiles, batchKey
|
|
|
789
900
|
const index = buildArkTSIndex(rootDir, etsFiles);
|
|
790
901
|
if (index.fileResults.size === 0) {
|
|
791
902
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
792
|
-
|
|
903
|
+
const detail = index.errors.map((e) => `[${e.severity}] ${e.message}`).join(' | ');
|
|
904
|
+
throw new Error(fatal?.message ??
|
|
905
|
+
`ArkTS batch produced no indexed files${detail ? ` (${detail})` : ' (no Scene/adapter errors recorded)'}`);
|
|
793
906
|
}
|
|
794
907
|
queries.deleteArkTSCrossFileCallEdges();
|
|
795
908
|
persistBatchResultsSync(rootDir, queries, index);
|
|
@@ -799,6 +912,8 @@ function runArkTSBatchFullCore(rootDir, queries, triggerFile, etsFiles, batchKey
|
|
|
799
912
|
queries.insertEdges(valid);
|
|
800
913
|
}
|
|
801
914
|
}
|
|
915
|
+
// Keep payload in memory for ArkTSExtractor.extract() same-batch hits.
|
|
916
|
+
// indexAll uses primeArkTSBatch → releaseArkTSBatchHeavyPayload after persist.
|
|
802
917
|
return commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile);
|
|
803
918
|
}
|
|
804
919
|
finally {
|
|
@@ -850,6 +965,27 @@ function runArkTSBatch(rootDir, queries, triggerFile) {
|
|
|
850
965
|
if (cached) {
|
|
851
966
|
return cached;
|
|
852
967
|
}
|
|
968
|
+
// indexAll releases the in-memory payload after SQLite persist. A later
|
|
969
|
+
// ArkTSExtractor hit must NOT rebuild the entire Scene (looks like a restart).
|
|
970
|
+
if (batchBuildCommitted &&
|
|
971
|
+
persistedBatch &&
|
|
972
|
+
persistedBatch.rootDir === rootDir &&
|
|
973
|
+
batchPersistedPaths.has(normalizedTrigger)) {
|
|
974
|
+
return {
|
|
975
|
+
fileResults: new Map(),
|
|
976
|
+
crossFileEdges: [],
|
|
977
|
+
nodeIds: new Set(),
|
|
978
|
+
errors: [
|
|
979
|
+
{
|
|
980
|
+
message: 'ArkTS batch already persisted this run; in-memory payload released',
|
|
981
|
+
severity: 'warning',
|
|
982
|
+
},
|
|
983
|
+
],
|
|
984
|
+
rootDir,
|
|
985
|
+
batchKey: persistedBatch.batchKey,
|
|
986
|
+
etsFiles: persistedBatch.etsFiles,
|
|
987
|
+
};
|
|
988
|
+
}
|
|
853
989
|
const batchKey = etsFiles.length > 0 ? computeBatchKey(rootDir, etsFiles) : '';
|
|
854
990
|
return runArkTSBatchFull(rootDir, queries, normalizedTrigger, etsFiles, batchKey);
|
|
855
991
|
}
|
|
@@ -892,13 +1028,27 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
892
1028
|
(0, context_1.setArktsBatchRunning)(true);
|
|
893
1029
|
try {
|
|
894
1030
|
reportBatchProgress('scene', 0, etsFiles.length);
|
|
895
|
-
const
|
|
896
|
-
if (
|
|
1031
|
+
const streamModular = shouldUseModularArkTSBuild(rootDir);
|
|
1032
|
+
if (streamModular) {
|
|
1033
|
+
// Clear stale cross-file RTA edges before streaming module persists.
|
|
1034
|
+
queries.deleteArkTSCrossFileCallEdges();
|
|
1035
|
+
}
|
|
1036
|
+
const index = buildArkTSIndex(rootDir, etsFiles, {
|
|
1037
|
+
streamQueries: streamModular ? queries : undefined,
|
|
1038
|
+
});
|
|
1039
|
+
const streamed = index.streamedPersistedPaths?.size ?? 0;
|
|
1040
|
+
if (index.fileResults.size === 0 && streamed === 0) {
|
|
897
1041
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
898
|
-
|
|
1042
|
+
const detail = index.errors.map((e) => `[${e.severity}] ${e.message}`).join(' | ');
|
|
1043
|
+
throw new Error(fatal?.message ??
|
|
1044
|
+
`ArkTS batch produced no indexed files${detail ? ` (${detail})` : ' (no Scene/adapter errors recorded)'}`);
|
|
1045
|
+
}
|
|
1046
|
+
if (!streamModular) {
|
|
1047
|
+
queries.deleteArkTSCrossFileCallEdges();
|
|
1048
|
+
}
|
|
1049
|
+
if (index.fileResults.size > 0) {
|
|
1050
|
+
await persistBatchResultsAsync(rootDir, queries, index);
|
|
899
1051
|
}
|
|
900
|
-
queries.deleteArkTSCrossFileCallEdges();
|
|
901
|
-
await persistBatchResultsAsync(rootDir, queries, index);
|
|
902
1052
|
if (index.crossFileEdges.length > 0) {
|
|
903
1053
|
const valid = index.crossFileEdges.filter((e) => index.nodeIds.has(e.source) && index.nodeIds.has(e.target));
|
|
904
1054
|
if (valid.length > 0) {
|
|
@@ -906,6 +1056,8 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
906
1056
|
}
|
|
907
1057
|
}
|
|
908
1058
|
commitArkTSBatch(rootDir, batchKey, etsFiles, index, normalizedTrigger);
|
|
1059
|
+
releaseArkTSBatchHeavyPayload();
|
|
1060
|
+
tryForceGc();
|
|
909
1061
|
}
|
|
910
1062
|
finally {
|
|
911
1063
|
(0, context_1.setArktsBatchRunning)(false);
|
|
@@ -923,8 +1075,13 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
923
1075
|
(0, context_1.setArktsBatchRunning)(true);
|
|
924
1076
|
try {
|
|
925
1077
|
reportBatchProgress('scene', 0, etsFiles.length);
|
|
926
|
-
|
|
927
|
-
|
|
1078
|
+
queries.deleteArkTSCrossFileCallEdges();
|
|
1079
|
+
const index = buildArkTSIndexByModule(rootDir, etsFiles, {
|
|
1080
|
+
targetModuleSrcPaths: moduleSrcPaths,
|
|
1081
|
+
streamQueries: queries,
|
|
1082
|
+
});
|
|
1083
|
+
const streamed = index.streamedPersistedPaths?.size ?? 0;
|
|
1084
|
+
if (index.fileResults.size === 0 && streamed === 0) {
|
|
928
1085
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
929
1086
|
// Fall back to full project batch when the subset produced nothing.
|
|
930
1087
|
const batchKey = etsFiles.length > 0 ? computeBatchKey(rootDir, etsFiles) : '';
|
|
@@ -932,20 +1089,26 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
932
1089
|
process.stderr.write(`\n\x1b[33m ArkTS: incremental modules failed (${fatal.message}); full rebuild\x1b[0m\n`);
|
|
933
1090
|
}
|
|
934
1091
|
const full = buildArkTSIndex(rootDir, etsFiles);
|
|
935
|
-
if (full.fileResults.size === 0) {
|
|
1092
|
+
if (full.fileResults.size === 0 && (full.streamedPersistedPaths?.size ?? 0) === 0) {
|
|
936
1093
|
throw new Error(fatal?.message ?? 'ArkTS incremental batch produced no indexed files');
|
|
937
1094
|
}
|
|
938
1095
|
queries.deleteArkTSCrossFileCallEdges();
|
|
939
|
-
|
|
1096
|
+
if (full.fileResults.size > 0) {
|
|
1097
|
+
await persistBatchResultsAsync(rootDir, queries, full);
|
|
1098
|
+
}
|
|
940
1099
|
if (full.crossFileEdges.length > 0) {
|
|
941
1100
|
const valid = full.crossFileEdges.filter((e) => full.nodeIds.has(e.source) && full.nodeIds.has(e.target));
|
|
942
1101
|
if (valid.length > 0)
|
|
943
1102
|
queries.insertEdges(valid);
|
|
944
1103
|
}
|
|
945
1104
|
commitArkTSBatch(rootDir, batchKey, etsFiles, full, triggerFile);
|
|
1105
|
+
releaseArkTSBatchHeavyPayload();
|
|
1106
|
+
tryForceGc();
|
|
946
1107
|
return;
|
|
947
1108
|
}
|
|
948
|
-
|
|
1109
|
+
if (index.fileResults.size > 0) {
|
|
1110
|
+
persistIncrementalModuleResults(rootDir, queries, index);
|
|
1111
|
+
}
|
|
949
1112
|
if (index.crossFileEdges.length > 0) {
|
|
950
1113
|
const valid = index.crossFileEdges.filter((e) => index.nodeIds.has(e.source) && index.nodeIds.has(e.target));
|
|
951
1114
|
if (valid.length > 0) {
|
|
@@ -955,6 +1118,8 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
955
1118
|
// Unique key so a partial index is never mistaken for a full-project cache hit.
|
|
956
1119
|
const batchKey = `incremental:${moduleSrcPaths.join(',')}`;
|
|
957
1120
|
commitArkTSBatch(rootDir, batchKey, etsFiles, index, triggerFile);
|
|
1121
|
+
releaseArkTSBatchHeavyPayload();
|
|
1122
|
+
tryForceGc();
|
|
958
1123
|
process.stderr.write(`\n\x1b[33m ArkTS: incremental module reindex done (${moduleSrcPaths.join(', ')})\x1b[0m\n`);
|
|
959
1124
|
}
|
|
960
1125
|
finally {
|
|
@@ -965,6 +1130,10 @@ async function runArkTSBatchIncrementalModules(rootDir, queries, triggerFile, et
|
|
|
965
1130
|
function isArkTSBatchPersisted(filePath) {
|
|
966
1131
|
return batchPersistedPaths.has(normIndexPath(filePath));
|
|
967
1132
|
}
|
|
1133
|
+
/** True after a successful ArkTS batch was committed to SQLite this index run. */
|
|
1134
|
+
function isArkTSBatchCommitted() {
|
|
1135
|
+
return batchBuildCommitted;
|
|
1136
|
+
}
|
|
968
1137
|
/** Clear cached batch state (tests and full re-index). */
|
|
969
1138
|
function resetArkTSBatch() {
|
|
970
1139
|
persistedBatch = null;
|
|
@@ -973,6 +1142,16 @@ function resetArkTSBatch() {
|
|
|
973
1142
|
batchBuildCommitted = false;
|
|
974
1143
|
arktsIndexNotices = [];
|
|
975
1144
|
}
|
|
1145
|
+
/**
|
|
1146
|
+
* After parse workers no longer need skip-path metadata beyond
|
|
1147
|
+
* `batchPersistedPaths`, drop the heavy path-list / batchKey strings.
|
|
1148
|
+
*/
|
|
1149
|
+
function shrinkArkTSBatchPostParse() {
|
|
1150
|
+
if (!persistedBatch)
|
|
1151
|
+
return;
|
|
1152
|
+
persistedBatch.etsFiles = [];
|
|
1153
|
+
persistedBatch.batchKey = '';
|
|
1154
|
+
}
|
|
976
1155
|
class ArkTSExtractor {
|
|
977
1156
|
filePath;
|
|
978
1157
|
constructor(filePath, _source) {
|
|
@@ -1035,8 +1214,33 @@ const FIELD_CATEGORY = {
|
|
|
1035
1214
|
};
|
|
1036
1215
|
/** {@link ModifierType.CONST} — not re-exported from the arkanalyzer npm entry. */
|
|
1037
1216
|
const MODIFIER_CONST = 128;
|
|
1217
|
+
/**
|
|
1218
|
+
* Project-relative path for an ArkAnalyzer absolute file path.
|
|
1219
|
+
*
|
|
1220
|
+
* ArkAnalyzer returns realpath'd absolutes (macOS: `/private/var/...`), while
|
|
1221
|
+
* HomeGraph's `rootDir` from `mkdtemp` / callers is often the unresolved form
|
|
1222
|
+
* (`/var/...`). Without canonicalizing both sides, `path.relative` escapes the
|
|
1223
|
+
* project (`../../../../private/var/.../file.ets`) and every file is skipped
|
|
1224
|
+
* by the `scanned` set → empty batch / "no indexed files".
|
|
1225
|
+
*/
|
|
1038
1226
|
function normalizeRelPath(rootDir, absolutePath) {
|
|
1039
|
-
|
|
1227
|
+
const root = realpathExisting(rootDir);
|
|
1228
|
+
const abs = realpathExisting(absolutePath);
|
|
1229
|
+
return path.relative(root, abs).replace(/\\/g, '/');
|
|
1230
|
+
}
|
|
1231
|
+
/** `realpathSync` when the path (or its dirname) exists; otherwise `path.resolve`. */
|
|
1232
|
+
function realpathExisting(p) {
|
|
1233
|
+
try {
|
|
1234
|
+
return fs.realpathSync(p);
|
|
1235
|
+
}
|
|
1236
|
+
catch {
|
|
1237
|
+
try {
|
|
1238
|
+
return path.join(fs.realpathSync(path.dirname(p)), path.basename(p));
|
|
1239
|
+
}
|
|
1240
|
+
catch {
|
|
1241
|
+
return path.resolve(p);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1040
1244
|
}
|
|
1041
1245
|
/** Map ArkAnalyzer synthetic files (e.g. @dummyFile) to our virtual indexed path. */
|
|
1042
1246
|
function resolveArkanalyzerVirtualPath(arkFile) {
|
|
@@ -1492,6 +1696,52 @@ function stateDecoratorKinds(field) {
|
|
|
1492
1696
|
return [];
|
|
1493
1697
|
return [...decs].map((d) => d.getKind()).filter(Boolean);
|
|
1494
1698
|
}
|
|
1699
|
+
/** All field decorators with optional string args (Provide('theme') → theme). */
|
|
1700
|
+
function fieldDecoratorEntries(field) {
|
|
1701
|
+
const out = [];
|
|
1702
|
+
const seen = new Set();
|
|
1703
|
+
const push = (kind, arg) => {
|
|
1704
|
+
if (!kind || seen.has(kind))
|
|
1705
|
+
return;
|
|
1706
|
+
seen.add(kind);
|
|
1707
|
+
out.push(arg ? { kind, arg } : { kind });
|
|
1708
|
+
};
|
|
1709
|
+
try {
|
|
1710
|
+
for (const d of field.getDecorators?.() ?? []) {
|
|
1711
|
+
push(d.getKind(), (0, migrate_semantics_1.parseDecoratorArgFromContent)(d.getContent?.() ?? ''));
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
catch {
|
|
1715
|
+
/* ignore */
|
|
1716
|
+
}
|
|
1717
|
+
if (out.length === 0) {
|
|
1718
|
+
for (const kind of stateDecoratorKinds(field))
|
|
1719
|
+
push(kind);
|
|
1720
|
+
}
|
|
1721
|
+
return out;
|
|
1722
|
+
}
|
|
1723
|
+
/** Encoded decorators list for node.decorators (bare kinds + Kind@arg). */
|
|
1724
|
+
function fieldDecoratorsEncoded(field) {
|
|
1725
|
+
return (0, migrate_semantics_1.encodeDecoratorEntries)(fieldDecoratorEntries(field));
|
|
1726
|
+
}
|
|
1727
|
+
function modelDecoratorEntries(model) {
|
|
1728
|
+
const out = [];
|
|
1729
|
+
const seen = new Set();
|
|
1730
|
+
try {
|
|
1731
|
+
for (const d of model.getDecorators?.() ?? []) {
|
|
1732
|
+
const kind = d.getKind();
|
|
1733
|
+
if (!kind || seen.has(kind))
|
|
1734
|
+
continue;
|
|
1735
|
+
seen.add(kind);
|
|
1736
|
+
const arg = (0, migrate_semantics_1.parseDecoratorArgFromContent)(d.getContent?.() ?? '');
|
|
1737
|
+
out.push(arg ? { kind, arg } : { kind });
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
catch {
|
|
1741
|
+
/* ignore */
|
|
1742
|
+
}
|
|
1743
|
+
return out;
|
|
1744
|
+
}
|
|
1495
1745
|
/** Child @Prop / @Link — edge `via` uses the decorator name as-is. */
|
|
1496
1746
|
function stateTransferViaForField(field) {
|
|
1497
1747
|
for (const kind of stateDecoratorKinds(field)) {
|
|
@@ -1838,12 +2088,12 @@ function indexViewTreeForClass(ctx, cls, result, relativePath) {
|
|
|
1838
2088
|
if (!buildId)
|
|
1839
2089
|
return;
|
|
1840
2090
|
const seen = new Set();
|
|
1841
|
-
const link = (source, target, kind, via, line) => {
|
|
2091
|
+
const link = (source, target, kind, via, line, extraMeta) => {
|
|
1842
2092
|
const key = `${source}>${target}>${kind}>${via}`;
|
|
1843
2093
|
if (seen.has(key))
|
|
1844
2094
|
return;
|
|
1845
2095
|
seen.add(key);
|
|
1846
|
-
ctx.addEdge(result, source, target, kind, relativePath, via, line);
|
|
2096
|
+
ctx.addEdge(result, source, target, kind, relativePath, via, line, extraMeta);
|
|
1847
2097
|
};
|
|
1848
2098
|
for (const [field] of viewTree.getStateValues()) {
|
|
1849
2099
|
const fieldId = ctx.resolveFieldNodeId(field);
|
|
@@ -1911,32 +2161,62 @@ function indexViewTreeForClass(ctx, cls, result, relativePath) {
|
|
|
1911
2161
|
// Skip this node's signature edge; keep walking siblings/children.
|
|
1912
2162
|
}
|
|
1913
2163
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
2164
|
+
// Data passages for migrate snapshot (+ Prop/Link edges for explore).
|
|
2165
|
+
try {
|
|
2166
|
+
const passages = (0, migrate_passage_1.collectPassagesForViewTreeNode)(node, ctx.scene, cls);
|
|
2167
|
+
for (const p of passages) {
|
|
2168
|
+
const toId = ctx.resolveFieldNodeId(p.toField);
|
|
2169
|
+
if (!toId)
|
|
2170
|
+
continue;
|
|
2171
|
+
ctx.markFieldStateDecorators?.(p.toField, toId, result);
|
|
2172
|
+
let fromId = null;
|
|
2173
|
+
if (p.fromField) {
|
|
2174
|
+
fromId = ctx.resolveFieldNodeId(p.fromField);
|
|
2175
|
+
if (fromId)
|
|
2176
|
+
ctx.markFieldStateDecorators?.(p.fromField, fromId, result);
|
|
1927
2177
|
}
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2178
|
+
if (!fromId && p.fromIsParentComponent)
|
|
2179
|
+
fromId = classNodeId;
|
|
2180
|
+
if (!fromId)
|
|
2181
|
+
continue;
|
|
2182
|
+
const line = p.toField.getOriginFullPosition()?.getFirstLine() ?? 1;
|
|
2183
|
+
link(fromId, toId, 'references', p.via || 'data-passage', line, {
|
|
2184
|
+
passageType: p.passageType,
|
|
2185
|
+
valueType: p.valueType,
|
|
2186
|
+
forcesMigration: p.forcesMigration,
|
|
2187
|
+
parentExpression: p.parentExpression,
|
|
2188
|
+
});
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
catch {
|
|
2192
|
+
// Fall back to legacy Prop/Link-only transfer below.
|
|
2193
|
+
if (node.stateValuesTransfer) {
|
|
2194
|
+
for (const [childField, parentValue] of node.stateValuesTransfer) {
|
|
2195
|
+
const childFieldId = ctx.resolveFieldNodeId(childField);
|
|
2196
|
+
if (parentValue instanceof arkanalyzer_1.ArkField) {
|
|
2197
|
+
const via = stateTransferViaForField(childField);
|
|
2198
|
+
if (!via || !childFieldId)
|
|
2199
|
+
continue;
|
|
2200
|
+
ctx.markFieldStateDecorators?.(childField, childFieldId, result);
|
|
2201
|
+
const parentFieldId = ctx.resolveFieldNodeId(parentValue);
|
|
2202
|
+
if (parentFieldId) {
|
|
2203
|
+
ctx.markFieldStateDecorators?.(parentValue, parentFieldId, result);
|
|
2204
|
+
link(parentFieldId, childFieldId, 'references', via, childField.getOriginFullPosition()?.getFirstLine() ?? 1);
|
|
1936
2205
|
}
|
|
1937
2206
|
}
|
|
1938
|
-
if (
|
|
1939
|
-
|
|
2207
|
+
else if (parentValue instanceof arkanalyzer_1.ArkMethod) {
|
|
2208
|
+
let builderId = ctx.ensureMethodNode(parentValue, relativePath, result, classNodeId);
|
|
2209
|
+
if (!builderId) {
|
|
2210
|
+
try {
|
|
2211
|
+
builderId = ctx.resolveMethodIdBySig(parentValue.getSignature());
|
|
2212
|
+
}
|
|
2213
|
+
catch {
|
|
2214
|
+
builderId = null;
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
if (childFieldId && builderId) {
|
|
2218
|
+
link(builderId, childFieldId, 'references', 'builder-param', childField.getOriginFullPosition()?.getFirstLine() ?? 1);
|
|
2219
|
+
}
|
|
1940
2220
|
}
|
|
1941
2221
|
}
|
|
1942
2222
|
}
|
|
@@ -1992,6 +2272,12 @@ class ArkTSAdapter {
|
|
|
1992
2272
|
crossFileEdges = [];
|
|
1993
2273
|
/** Per-module `@dummyMain@…` created during analyseByModule (for scene aggregator). */
|
|
1994
2274
|
moduleDummyMains = [];
|
|
2275
|
+
/**
|
|
2276
|
+
* Local import binding names from `lib*.so` per relative file path.
|
|
2277
|
+
* Filled before method bodies are scanned so `multimodalinput.getTidByName(`
|
|
2278
|
+
* can emit NAPI call refs (not only photos-style `Class_method` names).
|
|
2279
|
+
*/
|
|
2280
|
+
soImportBindingsByFile = new Map();
|
|
1995
2281
|
constructor(rootDir, scannedFiles) {
|
|
1996
2282
|
this.rootDir = rootDir;
|
|
1997
2283
|
this.scanned = new Set(scannedFiles);
|
|
@@ -2028,6 +2314,9 @@ class ArkTSAdapter {
|
|
|
2028
2314
|
this.stitchModuleCfgCallsAndRefs(files, moduleFiles);
|
|
2029
2315
|
// Drop live IR object keys so ArkAnalyzer can GC unloaded modules; keep sig→id maps.
|
|
2030
2316
|
this.releaseLiveIrMaps();
|
|
2317
|
+
// Edges for this module are already in fileResults / crossFileEdges — drop
|
|
2318
|
+
// the string dedupe set so it does not grow across the whole project.
|
|
2319
|
+
this.emittedEdgeKeys.clear();
|
|
2031
2320
|
}
|
|
2032
2321
|
/**
|
|
2033
2322
|
* Index symbols from already-loaded non-target modules into sig/name maps only.
|
|
@@ -2466,6 +2755,30 @@ class ArkTSAdapter {
|
|
|
2466
2755
|
errors: [...errors],
|
|
2467
2756
|
};
|
|
2468
2757
|
}
|
|
2758
|
+
/**
|
|
2759
|
+
* Remove and return ExtractionResults for the given paths so callers can
|
|
2760
|
+
* persist+GC mid-Scene (streaming modular build). Keeps sig→id maps / nodeIds.
|
|
2761
|
+
*/
|
|
2762
|
+
takeFileResults(paths) {
|
|
2763
|
+
const out = new Map();
|
|
2764
|
+
for (const raw of paths) {
|
|
2765
|
+
const p = normIndexPath(raw);
|
|
2766
|
+
const result = this.fileResults.get(p);
|
|
2767
|
+
if (!result)
|
|
2768
|
+
continue;
|
|
2769
|
+
out.set(p, result);
|
|
2770
|
+
this.fileResults.delete(p);
|
|
2771
|
+
}
|
|
2772
|
+
return out;
|
|
2773
|
+
}
|
|
2774
|
+
/** Re-insert a previously taken file result (stream persist failure recovery). */
|
|
2775
|
+
putFileResult(filePath, result) {
|
|
2776
|
+
this.fileResults.set(normIndexPath(filePath), result);
|
|
2777
|
+
}
|
|
2778
|
+
/** All paths currently held in fileResults (for final flush). */
|
|
2779
|
+
fileResultPaths() {
|
|
2780
|
+
return [...this.fileResults.keys()];
|
|
2781
|
+
}
|
|
2469
2782
|
indexScannedFile(arkFile) {
|
|
2470
2783
|
const relativePath = normalizeRelPath(this.rootDir, arkFile.getFilePath());
|
|
2471
2784
|
if (!this.scanned.has(relativePath))
|
|
@@ -2498,6 +2811,70 @@ class ArkTSAdapter {
|
|
|
2498
2811
|
}
|
|
2499
2812
|
}
|
|
2500
2813
|
}
|
|
2814
|
+
this.indexObservedClassRefs();
|
|
2815
|
+
}
|
|
2816
|
+
/**
|
|
2817
|
+
* State / Param fields whose type names an @Observed / @ObservedV2 class →
|
|
2818
|
+
* `references` via `observed-ref` (arkui-migrate).
|
|
2819
|
+
*/
|
|
2820
|
+
indexObservedClassRefs() {
|
|
2821
|
+
const observedByName = new Map();
|
|
2822
|
+
for (const [, result] of this.fileResults) {
|
|
2823
|
+
for (const n of result.nodes) {
|
|
2824
|
+
if (n.kind !== 'class' && n.kind !== 'struct')
|
|
2825
|
+
continue;
|
|
2826
|
+
const decs = n.decorators ?? [];
|
|
2827
|
+
if (!decs.some((d) => migrate_semantics_1.OBSERVATION_DECORATORS.has(d.split('@')[0])))
|
|
2828
|
+
continue;
|
|
2829
|
+
const arr = observedByName.get(n.name) ?? [];
|
|
2830
|
+
arr.push(n.id);
|
|
2831
|
+
observedByName.set(n.name, arr);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
if (observedByName.size === 0)
|
|
2835
|
+
return;
|
|
2836
|
+
for (const [relativePath, result] of this.fileResults) {
|
|
2837
|
+
for (const field of result.nodes) {
|
|
2838
|
+
if (field.kind !== 'property' && field.kind !== 'field')
|
|
2839
|
+
continue;
|
|
2840
|
+
const typeStr = field.signature ?? '';
|
|
2841
|
+
const bare = typeStr.replace(/<.*>/, '').split(/[.\s|]/).filter(Boolean).pop();
|
|
2842
|
+
if (!bare)
|
|
2843
|
+
continue;
|
|
2844
|
+
const targets = observedByName.get(bare);
|
|
2845
|
+
if (!targets || targets.length === 0)
|
|
2846
|
+
continue;
|
|
2847
|
+
// Prefer unique name; if ambiguous, same-file first
|
|
2848
|
+
let targetId = targets[0];
|
|
2849
|
+
if (targets.length > 1) {
|
|
2850
|
+
const sameFile = targets.find((id) => {
|
|
2851
|
+
const n = result.nodes.find((x) => x.id === id);
|
|
2852
|
+
return n?.filePath === relativePath;
|
|
2853
|
+
});
|
|
2854
|
+
if (sameFile)
|
|
2855
|
+
targetId = sameFile;
|
|
2856
|
+
else
|
|
2857
|
+
continue; // ambiguous cross-file — skip (precision > recall)
|
|
2858
|
+
}
|
|
2859
|
+
const edgeKey = `${field.id}\0${targetId}\0observed-ref`;
|
|
2860
|
+
if (this.emittedEdgeKeys.has(edgeKey))
|
|
2861
|
+
continue;
|
|
2862
|
+
this.emittedEdgeKeys.add(edgeKey);
|
|
2863
|
+
const edge = arkEdge(field.id, targetId, 'references', {
|
|
2864
|
+
metadata: {
|
|
2865
|
+
synthesizedBy: 'arkui-migrate',
|
|
2866
|
+
via: 'observed-ref',
|
|
2867
|
+
registeredAt: `${relativePath}:${field.startLine}`,
|
|
2868
|
+
},
|
|
2869
|
+
});
|
|
2870
|
+
if (this.nodeInFile(targetId, relativePath)) {
|
|
2871
|
+
result.edges.push(edge);
|
|
2872
|
+
}
|
|
2873
|
+
else {
|
|
2874
|
+
this.crossFileEdges.push(edge);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2501
2878
|
}
|
|
2502
2879
|
/** Index ArkAnalyzer's in-scene @dummyMain (DummyMainCreater output), not a HomeGraph synthetic node. */
|
|
2503
2880
|
indexArkanalyzerDummyMain(dummyMain) {
|
|
@@ -2547,6 +2924,8 @@ class ArkTSAdapter {
|
|
|
2547
2924
|
}
|
|
2548
2925
|
const result = this.ensureFileResult(relativePath, lineCount);
|
|
2549
2926
|
const fileId = `file:${relativePath}`;
|
|
2927
|
+
// Before methods: record `lib*.so` bindings so body NAPI call scan can see them.
|
|
2928
|
+
this.collectSoImportBindings(relativePath, arkFile);
|
|
2550
2929
|
for (const ns of arkFile.getNamespaces()) {
|
|
2551
2930
|
this.indexNamespace(relativePath, language, result, ns, fileId);
|
|
2552
2931
|
}
|
|
@@ -2555,6 +2934,59 @@ class ArkTSAdapter {
|
|
|
2555
2934
|
this.indexTypeAliases(relativePath, language, result, arkFile, undefined, fileId);
|
|
2556
2935
|
this.indexModuleLocals(relativePath, language, result, arkFile, undefined, fileId);
|
|
2557
2936
|
this.indexImports(relativePath, language, result, arkFile, fileId);
|
|
2937
|
+
this.indexStorageApiKeys(relativePath, arkFile, result);
|
|
2938
|
+
}
|
|
2939
|
+
/** AppStorage / LocalStorage / … literal keys → arkui-migrate edges from each component. */
|
|
2940
|
+
indexStorageApiKeys(relativePath, arkFile, result) {
|
|
2941
|
+
let code = '';
|
|
2942
|
+
try {
|
|
2943
|
+
code = arkFile.getCode() ?? '';
|
|
2944
|
+
}
|
|
2945
|
+
catch {
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2948
|
+
if (!code)
|
|
2949
|
+
return;
|
|
2950
|
+
const hits = (0, migrate_semantics_1.scanStorageApiKeys)(code);
|
|
2951
|
+
if (hits.length === 0)
|
|
2952
|
+
return;
|
|
2953
|
+
const componentIds = result.nodes
|
|
2954
|
+
.filter((n) => n.kind === 'component' && n.filePath === relativePath)
|
|
2955
|
+
.map((n) => n.id);
|
|
2956
|
+
if (componentIds.length === 0)
|
|
2957
|
+
return;
|
|
2958
|
+
for (const hit of hits) {
|
|
2959
|
+
const line = (0, migrate_semantics_1.lineOfOffset)(code, hit.index);
|
|
2960
|
+
for (const compId of componentIds) {
|
|
2961
|
+
const edgeKey = `${compId}\0storage-api\0${hit.channel}\0${hit.key}`;
|
|
2962
|
+
if (this.emittedEdgeKeys.has(edgeKey))
|
|
2963
|
+
continue;
|
|
2964
|
+
this.emittedEdgeKeys.add(edgeKey);
|
|
2965
|
+
result.edges.push(arkEdge(compId, compId, 'references', {
|
|
2966
|
+
metadata: {
|
|
2967
|
+
synthesizedBy: 'arkui-migrate',
|
|
2968
|
+
via: 'storage-api',
|
|
2969
|
+
channel: hit.channel,
|
|
2970
|
+
key: hit.key,
|
|
2971
|
+
method: hit.method,
|
|
2972
|
+
registeredAt: `${relativePath}:${line}`,
|
|
2973
|
+
},
|
|
2974
|
+
}));
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
collectSoImportBindings(relativePath, arkFile) {
|
|
2979
|
+
const set = new Set();
|
|
2980
|
+
for (const importInfo of arkFile.getImportInfos()) {
|
|
2981
|
+
const modulePath = importInfo.getFrom();
|
|
2982
|
+
if (!modulePath || !LIB_SO_MODULE_RE.test(modulePath))
|
|
2983
|
+
continue;
|
|
2984
|
+
const clause = importInfo.getImportClauseName();
|
|
2985
|
+
if (clause)
|
|
2986
|
+
set.add(clause);
|
|
2987
|
+
}
|
|
2988
|
+
if (set.size > 0)
|
|
2989
|
+
this.soImportBindingsByFile.set(relativePath, set);
|
|
2558
2990
|
}
|
|
2559
2991
|
indexModuleLocals(relativePath, language, result, arkFile, ns, parentId) {
|
|
2560
2992
|
const declaringClass = ns ? ns.getDefaultClass() : arkFile.getDefaultClass();
|
|
@@ -2648,8 +3080,10 @@ class ArkTSAdapter {
|
|
|
2648
3080
|
const qn = buildClassQualifiedName(cls);
|
|
2649
3081
|
const kind = classNodeKind(cls);
|
|
2650
3082
|
const { line, endLine, col } = linesFromPosition(cls.getOriginFullPosition());
|
|
3083
|
+
const classDecs = (0, migrate_semantics_1.encodeDecoratorEntries)(modelDecoratorEntries(cls));
|
|
2651
3084
|
const classNode = makeNode(relativePath, language, kind, displayName, qn, line, endLine, col, {
|
|
2652
3085
|
...modelModifiersToNodeExtras(cls),
|
|
3086
|
+
...(classDecs.length > 0 ? { decorators: classDecs } : {}),
|
|
2653
3087
|
});
|
|
2654
3088
|
this.addNode(result, classNode);
|
|
2655
3089
|
this.classToId.set(cls, classNode.id);
|
|
@@ -2677,7 +3111,10 @@ class ArkTSAdapter {
|
|
|
2677
3111
|
}
|
|
2678
3112
|
}
|
|
2679
3113
|
if (cls.hasComponentDecorator()) {
|
|
2680
|
-
const componentNode = makeNode(relativePath, language, 'component', displayName, qn, line, endLine, col, {
|
|
3114
|
+
const componentNode = makeNode(relativePath, language, 'component', displayName, qn, line, endLine, col, {
|
|
3115
|
+
...modelModifiersToNodeExtras(cls),
|
|
3116
|
+
...(classDecs.length > 0 ? { decorators: classDecs } : {}),
|
|
3117
|
+
});
|
|
2681
3118
|
this.addNode(result, componentNode);
|
|
2682
3119
|
this.componentToId.set(cls, componentNode.id);
|
|
2683
3120
|
try {
|
|
@@ -2763,10 +3200,10 @@ class ArkTSAdapter {
|
|
|
2763
3200
|
return;
|
|
2764
3201
|
const { line, endLine, col } = linesFromPosition(field.getOriginFullPosition());
|
|
2765
3202
|
const qn = buildFieldQualifiedName(field);
|
|
2766
|
-
const
|
|
3203
|
+
const encodedDecs = fieldDecoratorsEncoded(field);
|
|
2767
3204
|
const fieldNode = makeNode(relativePath, language, kind, name, qn, line, endLine, col, {
|
|
2768
3205
|
signature: field.getType()?.toString(),
|
|
2769
|
-
...(
|
|
3206
|
+
...(encodedDecs.length > 0 ? { decorators: encodedDecs } : {}),
|
|
2770
3207
|
...modelModifiersToNodeExtras(field),
|
|
2771
3208
|
});
|
|
2772
3209
|
this.addNode(result, fieldNode);
|
|
@@ -2792,19 +3229,19 @@ class ArkTSAdapter {
|
|
|
2792
3229
|
classToId: this.classToId,
|
|
2793
3230
|
fieldToId: this.fieldToId,
|
|
2794
3231
|
nodeIds: this.nodeIds,
|
|
2795
|
-
addEdge: (result, source, target, kind, callerFile, via, line) => this.addViewTreeEdge(result, source, target, kind, callerFile, via, line),
|
|
3232
|
+
addEdge: (result, source, target, kind, callerFile, via, line, extraMeta) => this.addViewTreeEdge(result, source, target, kind, callerFile, via, line, extraMeta),
|
|
2796
3233
|
ensureMethodNode: (method, relativePath, result, parentId) => this.ensureMethodNode(method, relativePath, result, parentId),
|
|
2797
3234
|
resolveClassNodeId: (cls) => this.resolveClassNodeId(cls),
|
|
2798
3235
|
resolveClassIdBySig: (sig, importer) => this.resolveClassIdBySig(sig, importer),
|
|
2799
3236
|
resolveMethodIdBySig: (sig, importer) => this.resolveMethodIdBySig(sig, importer),
|
|
2800
3237
|
resolveFieldNodeId: (field) => this.resolveFieldNodeId(field),
|
|
2801
3238
|
markFieldStateDecorators: (field, fieldNodeId, result) => {
|
|
2802
|
-
const
|
|
2803
|
-
if (
|
|
3239
|
+
const encoded = fieldDecoratorsEncoded(field);
|
|
3240
|
+
if (encoded.length === 0)
|
|
2804
3241
|
return;
|
|
2805
3242
|
const node = result.nodes.find((n) => n.id === fieldNodeId);
|
|
2806
3243
|
if (node)
|
|
2807
|
-
node.decorators =
|
|
3244
|
+
node.decorators = encoded;
|
|
2808
3245
|
},
|
|
2809
3246
|
};
|
|
2810
3247
|
}
|
|
@@ -3028,7 +3465,7 @@ class ArkTSAdapter {
|
|
|
3028
3465
|
this.indexMethod(relativePath, 'arkts', result, method, parentId, kind, displayName);
|
|
3029
3466
|
return this.methodToId.get(method) ?? null;
|
|
3030
3467
|
}
|
|
3031
|
-
addViewTreeEdge(result, source, target, kind, callerFile, via, line) {
|
|
3468
|
+
addViewTreeEdge(result, source, target, kind, callerFile, via, line, extraMeta) {
|
|
3032
3469
|
const edgeKey = `${source}\0${target}\0${kind}\0${via}`;
|
|
3033
3470
|
if (this.emittedEdgeKeys.has(edgeKey))
|
|
3034
3471
|
return;
|
|
@@ -3038,6 +3475,7 @@ class ArkTSAdapter {
|
|
|
3038
3475
|
synthesizedBy: 'viewtree',
|
|
3039
3476
|
via,
|
|
3040
3477
|
registeredAt: `${callerFile}:${line}`,
|
|
3478
|
+
...(extraMeta ?? {}),
|
|
3041
3479
|
},
|
|
3042
3480
|
});
|
|
3043
3481
|
if (this.nodeInFile(target, callerFile)) {
|
|
@@ -3115,17 +3553,14 @@ class ArkTSAdapter {
|
|
|
3115
3553
|
return;
|
|
3116
3554
|
const baseLine = method.getImplOriginFullPosition()?.getFirstLine() ?? 1;
|
|
3117
3555
|
const seenAtLine = new Set();
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
const
|
|
3122
|
-
if (!isNapiSymbolName(symbol))
|
|
3123
|
-
continue;
|
|
3124
|
-
const lineOffset = code.slice(0, m.index).split('\n').length - 1;
|
|
3556
|
+
const pushCall = (symbol, matchIndex) => {
|
|
3557
|
+
if (!symbol)
|
|
3558
|
+
return;
|
|
3559
|
+
const lineOffset = code.slice(0, matchIndex).split('\n').length - 1;
|
|
3125
3560
|
const line = baseLine + lineOffset;
|
|
3126
3561
|
const dedupKey = `${symbol}:${line}`;
|
|
3127
3562
|
if (seenAtLine.has(dedupKey))
|
|
3128
|
-
|
|
3563
|
+
return;
|
|
3129
3564
|
seenAtLine.add(dedupKey);
|
|
3130
3565
|
result.unresolvedReferences.push({
|
|
3131
3566
|
fromNodeId,
|
|
@@ -3136,6 +3571,35 @@ class ArkTSAdapter {
|
|
|
3136
3571
|
filePath: relativePath,
|
|
3137
3572
|
language,
|
|
3138
3573
|
});
|
|
3574
|
+
};
|
|
3575
|
+
// Photos-style `Class_method` on any receiver.
|
|
3576
|
+
NAPI_MEMBER_CALL_RE.lastIndex = 0;
|
|
3577
|
+
let m;
|
|
3578
|
+
while ((m = NAPI_MEMBER_CALL_RE.exec(code)) !== null) {
|
|
3579
|
+
const symbol = m[1];
|
|
3580
|
+
if (!isNapiSymbolName(symbol))
|
|
3581
|
+
continue;
|
|
3582
|
+
pushCall(symbol, m.index);
|
|
3583
|
+
}
|
|
3584
|
+
// `import x from 'libFoo.so'` → `x.draw(` / `x.getTidByName(` (camelCase OK).
|
|
3585
|
+
const soBindings = this.soImportBindingsByFile.get(relativePath);
|
|
3586
|
+
if (soBindings) {
|
|
3587
|
+
for (const binding of soBindings) {
|
|
3588
|
+
const re = new RegExp(`\\b${escapeRegExp(binding)}\\s*\\.\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\(`, 'g');
|
|
3589
|
+
while ((m = re.exec(code)) !== null) {
|
|
3590
|
+
pushCall(m[1], m.index);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
// define_class / instance path: `this.inst?.addVerticalRuler(` — receiver is
|
|
3594
|
+
// not the import binding. Only in files that already import a lib*.so.
|
|
3595
|
+
const anyMemberRe = /\?\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(|\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
3596
|
+
while ((m = anyMemberRe.exec(code)) !== null) {
|
|
3597
|
+
const symbol = m[1] ?? m[2];
|
|
3598
|
+
if (!symbol)
|
|
3599
|
+
continue;
|
|
3600
|
+
// Skip photos-style names already handled above; keep camelCase / plain ids.
|
|
3601
|
+
pushCall(symbol, m.index);
|
|
3602
|
+
}
|
|
3139
3603
|
}
|
|
3140
3604
|
}
|
|
3141
3605
|
indexImports(relativePath, language, result, arkFile, fileId) {
|
|
@@ -3219,6 +3683,8 @@ function emptyArkTSBatchIndex(errors) {
|
|
|
3219
3683
|
* Runs with the #850 liveness watchdog suspended: ArkAnalyzer's native BODIES
|
|
3220
3684
|
* load between module callbacks routinely exceeds the 60s heartbeat window on
|
|
3221
3685
|
* large Harmony projects, and there is no JS yield point inside that span.
|
|
3686
|
+
* When `streamQueries` is set, each module's ExtractionResults are written to
|
|
3687
|
+
* SQLite immediately and dropped from RAM (cross-file call edges stay until end).
|
|
3222
3688
|
*/
|
|
3223
3689
|
function buildArkTSIndexByModule(rootDir, scannedFiles, options) {
|
|
3224
3690
|
return (0, liveness_watchdog_1.runWithoutLivenessWatchdog)(() => buildArkTSIndexByModuleInner(rootDir, scannedFiles, options));
|
|
@@ -3232,6 +3698,13 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3232
3698
|
let modulesIndexed = 0;
|
|
3233
3699
|
const targetSrc = options?.targetModuleSrcPaths?.map(normalizeHarmonyModuleSrcPath);
|
|
3234
3700
|
const targetSrcSet = targetSrc && targetSrc.length > 0 ? new Set(targetSrc) : null;
|
|
3701
|
+
const streamQueries = options?.streamQueries;
|
|
3702
|
+
const streamedPersistedPaths = new Set();
|
|
3703
|
+
const scannedList = [...scannedFiles].map(normIndexPath);
|
|
3704
|
+
const persistOrdinal = {
|
|
3705
|
+
done: 0,
|
|
3706
|
+
totalHint: Math.max(1, scannedList.filter((f) => isArkAnalyzerSourcePath(f)).length),
|
|
3707
|
+
};
|
|
3235
3708
|
try {
|
|
3236
3709
|
scene.config(sceneConfig);
|
|
3237
3710
|
const config = new arkanalyzer_1.ModuleAnalysisConfig();
|
|
@@ -3274,18 +3747,83 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3274
3747
|
return emptyArkTSBatchIndex(errors);
|
|
3275
3748
|
}
|
|
3276
3749
|
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`);
|
|
3750
|
+
process.stderr.write(`\n\x1b[33m ArkTS: analyseByModule incremental (${ids.length} module(s)→BODIES, deps→SIGNATURES, memoryLimitMB=${memoryLimitMB}${streamQueries ? ', streamPersist' : ''})...\x1b[0m\n`);
|
|
3278
3751
|
}
|
|
3279
3752
|
else {
|
|
3280
3753
|
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`);
|
|
3754
|
+
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
3755
|
}
|
|
3283
3756
|
scene.analyseByModule((module, scn) => {
|
|
3757
|
+
const moduleFileRels = [...module.getFilesMap().values()]
|
|
3758
|
+
.map((f) => normalizeRelPath(rootDir, f.getFilePath()))
|
|
3759
|
+
.filter((p) => p && isArkAnalyzerSourcePath(p))
|
|
3760
|
+
.map(normIndexPath);
|
|
3284
3761
|
adapter.indexModule(module, scn);
|
|
3762
|
+
// HomeGraph already captured this module's symbols/edges. Hollow every
|
|
3763
|
+
// currently-loaded module still above INDEX (target + oh_modules /
|
|
3764
|
+
// SIGNATURES deps left in ModuleCache). Topo order means PROJECT deps of
|
|
3765
|
+
// the current target are often already INDEX from their own callback, so
|
|
3766
|
+
// BFS-from-self alone left depHollowed=0; sweeping the cache catches the
|
|
3767
|
+
// fat non-target SIGNATURES that memoryLimitMB rarely evicts under a large
|
|
3768
|
+
// soft budget. INDEX keeps export-reachable shells; loadModule upgrades
|
|
3769
|
+
// when a later target needs SIGNATURES/BODIES.
|
|
3770
|
+
let depHollowed = 0;
|
|
3771
|
+
try {
|
|
3772
|
+
const builder = new arkanalyzer_1.ModuleBuilder(scn);
|
|
3773
|
+
const selfId = scn.getModuleId(module);
|
|
3774
|
+
hollowModuleToIndex(builder, selfId);
|
|
3775
|
+
const cache = scn.getModuleCache();
|
|
3776
|
+
if (cache) {
|
|
3777
|
+
for (const modId of cache.getLoadedModules()) {
|
|
3778
|
+
if (modId === selfId)
|
|
3779
|
+
continue;
|
|
3780
|
+
const depMod = builder.getModule(modId);
|
|
3781
|
+
if (!depMod || depMod.getLoadState() <= arkanalyzer_1.ModuleLoadState.INDEX)
|
|
3782
|
+
continue;
|
|
3783
|
+
hollowModuleToIndex(builder, modId);
|
|
3784
|
+
depHollowed++;
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
catch (e) {
|
|
3789
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
3790
|
+
errors.push({
|
|
3791
|
+
message: `ArkTS post-index INDEX hollow failed for ${module.getModuleName() || module.getModulePath()}: ${message}`,
|
|
3792
|
+
severity: 'warning',
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
3795
|
+
if (streamQueries) {
|
|
3796
|
+
// Persist this module's file bodies now; keep @dummyFile until finalize.
|
|
3797
|
+
const slicePaths = moduleFileRels.filter((p) => p !== ARKANALYZER_DUMMY_FILE);
|
|
3798
|
+
const slice = adapter.takeFileResults(slicePaths);
|
|
3799
|
+
try {
|
|
3800
|
+
persistAndDropModuleSlice(rootDir, streamQueries, slice, streamedPersistedPaths, persistOrdinal);
|
|
3801
|
+
}
|
|
3802
|
+
catch (e) {
|
|
3803
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
3804
|
+
errors.push({
|
|
3805
|
+
message: `ArkTS stream persist failed for ${module.getModuleName() || module.getModulePath()}: ${message}`,
|
|
3806
|
+
severity: 'error',
|
|
3807
|
+
});
|
|
3808
|
+
throw e;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3285
3811
|
modulesIndexed++;
|
|
3286
3812
|
const name = module.getModuleName() || path.basename(module.getModulePath());
|
|
3287
|
-
|
|
3813
|
+
const st = arkanalyzer_1.ModuleLoadState[module.getLoadState()] ?? String(module.getLoadState());
|
|
3814
|
+
const depNote = depHollowed > 0 ? `, hollowed ${depHollowed} deps` : '';
|
|
3815
|
+
process.stderr.write(`\x1b[33m ArkTS: indexed module ${modulesIndexed}: ${name} → ${st}${depNote}${streamQueries ? ` (streamed ${moduleFileRels.length} files)` : ''}\x1b[0m\n`);
|
|
3288
3816
|
}, config);
|
|
3817
|
+
if (process.env.HOMEGRAPH_ARKTS_DIAG === '1') {
|
|
3818
|
+
const sceneDiag = scene;
|
|
3819
|
+
const ev = typeof sceneDiag.getModuleEvictionStats === 'function'
|
|
3820
|
+
? sceneDiag.getModuleEvictionStats()
|
|
3821
|
+
: null;
|
|
3822
|
+
const mu = process.memoryUsage();
|
|
3823
|
+
const cacheN = scene.getModuleCache()?.size?.() ?? scene.getModuleCache()?.getLoadedModules?.()?.length ?? -1;
|
|
3824
|
+
process.stderr.write(`\x1b[36m ArkTS DIAG eviction: ${JSON.stringify(ev)} cacheLoaded=${cacheN} ` +
|
|
3825
|
+
`heapUsedMB=${(mu.heapUsed / 1024 / 1024).toFixed(1)} rssMB=${(mu.rss / 1024 / 1024).toFixed(1)}\x1b[0m\n`);
|
|
3826
|
+
}
|
|
3289
3827
|
if (modulesIndexed === 0) {
|
|
3290
3828
|
errors.push({
|
|
3291
3829
|
message: 'ArkTS analyseByModule found no PROJECT modules (check build-profile.json5); falling back to full Scene build',
|
|
@@ -3303,8 +3841,15 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3303
3841
|
severity: 'warning',
|
|
3304
3842
|
});
|
|
3305
3843
|
}
|
|
3844
|
+
if (streamQueries) {
|
|
3845
|
+
const leftover = adapter.takeFileResults(adapter.fileResultPaths());
|
|
3846
|
+
persistAndDropModuleSlice(rootDir, streamQueries, leftover, streamedPersistedPaths, persistOrdinal);
|
|
3847
|
+
}
|
|
3306
3848
|
const index = adapter.toBatchIndex(errors);
|
|
3307
|
-
if (
|
|
3849
|
+
if (streamQueries) {
|
|
3850
|
+
index.streamedPersistedPaths = streamedPersistedPaths;
|
|
3851
|
+
}
|
|
3852
|
+
if (index.fileResults.size === 0 && streamedPersistedPaths.size === 0) {
|
|
3308
3853
|
errors.push({
|
|
3309
3854
|
message: 'ArkTS analyseByModule indexed modules but no scanned source files matched',
|
|
3310
3855
|
severity: 'warning',
|
|
@@ -3323,7 +3868,7 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3323
3868
|
}
|
|
3324
3869
|
finally {
|
|
3325
3870
|
try {
|
|
3326
|
-
scene
|
|
3871
|
+
releaseArkAnalyzerScene(scene);
|
|
3327
3872
|
}
|
|
3328
3873
|
catch {
|
|
3329
3874
|
// ignore dispose failures
|
|
@@ -3349,13 +3894,26 @@ function buildArkTSIndexFull(rootDir, scannedFiles, priorErrors = []) {
|
|
|
3349
3894
|
allErrors.push({ message: `ArkTS adapter failed: ${message}`, severity: 'error' });
|
|
3350
3895
|
return emptyArkTSBatchIndex(allErrors);
|
|
3351
3896
|
}
|
|
3897
|
+
finally {
|
|
3898
|
+
try {
|
|
3899
|
+
releaseArkAnalyzerScene(scene);
|
|
3900
|
+
}
|
|
3901
|
+
catch {
|
|
3902
|
+
// ignore
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3352
3905
|
});
|
|
3353
3906
|
}
|
|
3354
|
-
function buildArkTSIndex(rootDir, scannedFiles) {
|
|
3907
|
+
function buildArkTSIndex(rootDir, scannedFiles, options) {
|
|
3355
3908
|
if (shouldUseModularArkTSBuild(rootDir)) {
|
|
3356
|
-
const modular = buildArkTSIndexByModule(rootDir, scannedFiles
|
|
3909
|
+
const modular = buildArkTSIndexByModule(rootDir, scannedFiles, {
|
|
3910
|
+
streamQueries: options?.streamQueries,
|
|
3911
|
+
});
|
|
3357
3912
|
const fatal = modular.errors.find((e) => e.severity === 'error');
|
|
3358
|
-
|
|
3913
|
+
const hasOutput = modular.fileResults.size > 0 ||
|
|
3914
|
+
(modular.streamedPersistedPaths?.size ?? 0) > 0 ||
|
|
3915
|
+
modular.nodeIds.size > 0;
|
|
3916
|
+
if (hasOutput && !fatal) {
|
|
3359
3917
|
return modular;
|
|
3360
3918
|
}
|
|
3361
3919
|
// Warnings like "no PROJECT modules" / modular failure → full Scene fallback.
|
|
@@ -3520,7 +4078,7 @@ function extractOhosSdkArchive(archivePath) {
|
|
|
3520
4078
|
function ohosApiDbFilename(version) {
|
|
3521
4079
|
return `ohos-api-${version}.db`;
|
|
3522
4080
|
}
|
|
3523
|
-
/**
|
|
4081
|
+
/** Metadata / publish id — one package per API version (e.g. homegraph-ohos-api-db-6.0.1). */
|
|
3524
4082
|
function ohosApiDbPackageName(version) {
|
|
3525
4083
|
return `homegraph-ohos-api-db-${version}`;
|
|
3526
4084
|
}
|
|
@@ -3668,7 +4226,7 @@ async function indexOhosApiDb(options) {
|
|
|
3668
4226
|
}
|
|
3669
4227
|
}
|
|
3670
4228
|
// =============================================================================
|
|
3671
|
-
// OHOS API db consumer: compileSdkVersion detect,
|
|
4229
|
+
// OHOS API db consumer: compileSdkVersion detect, local SDK build, ATTACH
|
|
3672
4230
|
// =============================================================================
|
|
3673
4231
|
exports.OHOS_API_FILE_PREFIX = 'ohos-sdk:';
|
|
3674
4232
|
exports.OHOS_API_VERSION_META = 'ohos_api_version';
|
|
@@ -3810,75 +4368,367 @@ function hasEtsUnder(dir, depth) {
|
|
|
3810
4368
|
}
|
|
3811
4369
|
return false;
|
|
3812
4370
|
}
|
|
3813
|
-
/**
|
|
3814
|
-
function
|
|
3815
|
-
const
|
|
3816
|
-
|
|
4371
|
+
/** Read platformVersion from sdk-pkg.json / oh-uni-package.json under an SDK home. */
|
|
4372
|
+
function readOhosSdkPlatformVersion(sdkHome) {
|
|
4373
|
+
const pkgPaths = [
|
|
4374
|
+
path.join(sdkHome, 'sdk-pkg.json'),
|
|
4375
|
+
path.join(sdkHome, 'openharmony', 'ets', 'oh-uni-package.json'),
|
|
4376
|
+
path.join(sdkHome, 'openharmony', 'oh-uni-package.json'),
|
|
4377
|
+
];
|
|
4378
|
+
for (const pkgPath of pkgPaths) {
|
|
4379
|
+
if (!fs.existsSync(pkgPath))
|
|
4380
|
+
continue;
|
|
4381
|
+
try {
|
|
4382
|
+
const raw = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
4383
|
+
const data = raw.data && typeof raw.data === 'object'
|
|
4384
|
+
? raw.data
|
|
4385
|
+
: raw;
|
|
4386
|
+
const v = normalizeOhosApiVersion(data.platformVersion) ||
|
|
4387
|
+
normalizeOhosApiVersion(data.version) ||
|
|
4388
|
+
normalizeOhosApiVersion(raw.platformVersion) ||
|
|
4389
|
+
normalizeOhosApiVersion(raw.version);
|
|
4390
|
+
if (v)
|
|
4391
|
+
return v;
|
|
4392
|
+
}
|
|
4393
|
+
catch {
|
|
4394
|
+
/* try next */
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
return normalizeOhosApiVersion(path.basename(sdkHome));
|
|
4398
|
+
}
|
|
4399
|
+
/** Expand a user/env path into concrete sdk/default-style homes that contain openharmony/ets. */
|
|
4400
|
+
function expandLocalOhosSdkHomes(input) {
|
|
4401
|
+
const resolved = path.resolve(input);
|
|
4402
|
+
if (!fs.existsSync(resolved))
|
|
4403
|
+
return [];
|
|
4404
|
+
const homes = [];
|
|
4405
|
+
const push = (p) => {
|
|
4406
|
+
if (hasOpenHarmonyEts(p))
|
|
4407
|
+
homes.push(p);
|
|
4408
|
+
};
|
|
4409
|
+
push(resolved);
|
|
4410
|
+
push(path.join(resolved, 'default'));
|
|
4411
|
+
push(path.join(resolved, 'sdk', 'default'));
|
|
4412
|
+
const viaFind = findOhosSdkHome(resolved);
|
|
4413
|
+
if (viaFind)
|
|
4414
|
+
homes.push(viaFind);
|
|
4415
|
+
let entries;
|
|
4416
|
+
try {
|
|
4417
|
+
entries = fs.readdirSync(resolved, { withFileTypes: true });
|
|
4418
|
+
}
|
|
4419
|
+
catch {
|
|
4420
|
+
return [...new Set(homes)];
|
|
4421
|
+
}
|
|
4422
|
+
for (const entry of entries) {
|
|
4423
|
+
if (!entry.isDirectory())
|
|
4424
|
+
continue;
|
|
4425
|
+
if (entry.name === 'node_modules' || entry.name === '.git')
|
|
4426
|
+
continue;
|
|
4427
|
+
const child = path.join(resolved, entry.name);
|
|
4428
|
+
push(child);
|
|
4429
|
+
push(path.join(child, 'default'));
|
|
4430
|
+
}
|
|
4431
|
+
return [...new Set(homes)];
|
|
4432
|
+
}
|
|
4433
|
+
function localOhosSdkSearchRoots() {
|
|
4434
|
+
const roots = [];
|
|
4435
|
+
for (const key of ['HOMEGRAPH_OHOS_SDK', 'OHOS_SDK_HOME', 'DEVECO_SDK_HOME', 'HOS_SDK_HOME']) {
|
|
4436
|
+
const v = process.env[key]?.trim();
|
|
4437
|
+
if (v)
|
|
4438
|
+
roots.push(v);
|
|
4439
|
+
}
|
|
4440
|
+
const home = os.homedir();
|
|
4441
|
+
if (process.platform === 'win32') {
|
|
4442
|
+
const lad = process.env.LOCALAPPDATA;
|
|
4443
|
+
if (lad) {
|
|
4444
|
+
roots.push(path.join(lad, 'OpenHarmony', 'Sdk'));
|
|
4445
|
+
roots.push(path.join(lad, 'Huawei', 'Sdk'));
|
|
4446
|
+
}
|
|
4447
|
+
roots.push(path.join('C:\\', 'Program Files', 'Huawei', 'DevEco Studio', 'sdk'));
|
|
4448
|
+
roots.push(path.join(home, 'Huawei', 'Sdk'));
|
|
4449
|
+
roots.push(path.join(home, 'OpenHarmony', 'Sdk'));
|
|
4450
|
+
}
|
|
4451
|
+
else if (process.platform === 'darwin') {
|
|
4452
|
+
roots.push(path.join(home, 'Library', 'Huawei', 'Sdk'));
|
|
4453
|
+
roots.push(path.join(home, 'Library', 'OpenHarmony', 'Sdk'));
|
|
4454
|
+
roots.push('/Applications/DevEco-Studio.app/Contents/sdk');
|
|
4455
|
+
}
|
|
4456
|
+
else {
|
|
4457
|
+
roots.push(path.join(home, 'Huawei', 'Sdk'));
|
|
4458
|
+
roots.push(path.join(home, 'OpenHarmony', 'Sdk'));
|
|
4459
|
+
}
|
|
4460
|
+
return roots;
|
|
4461
|
+
}
|
|
4462
|
+
/** Discover local DevEco / OpenHarmony SDK homes (env vars + common install paths). */
|
|
4463
|
+
function discoverLocalOhosSdkCandidates() {
|
|
4464
|
+
const out = [];
|
|
4465
|
+
const seen = new Set();
|
|
4466
|
+
for (const root of localOhosSdkSearchRoots()) {
|
|
4467
|
+
for (const sdkHome of expandLocalOhosSdkHomes(root)) {
|
|
4468
|
+
if (seen.has(sdkHome))
|
|
4469
|
+
continue;
|
|
4470
|
+
seen.add(sdkHome);
|
|
4471
|
+
out.push({ sdkHome, version: readOhosSdkPlatformVersion(sdkHome) });
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
return out;
|
|
4475
|
+
}
|
|
4476
|
+
/**
|
|
4477
|
+
* Pick a local SDK home.
|
|
4478
|
+
* When `preferredVersion` is set, require an exact platformVersion match.
|
|
4479
|
+
* When unset, return the first candidate that has a readable version (else any).
|
|
4480
|
+
*/
|
|
4481
|
+
function findLocalOhosSdkForVersion(preferredVersion) {
|
|
4482
|
+
const candidates = discoverLocalOhosSdkCandidates();
|
|
4483
|
+
if (candidates.length === 0)
|
|
4484
|
+
return null;
|
|
4485
|
+
if (preferredVersion) {
|
|
4486
|
+
return candidates.find((c) => c.version === preferredVersion) ?? null;
|
|
4487
|
+
}
|
|
4488
|
+
return candidates.find((c) => c.version) ?? candidates[0];
|
|
4489
|
+
}
|
|
4490
|
+
/**
|
|
4491
|
+
* Resolve the API db version for a project: build-profile / env first, else local SDK.
|
|
4492
|
+
*/
|
|
4493
|
+
function resolveOhosApiVersionForProject(projectRoot) {
|
|
4494
|
+
const fromProject = detectOhosCompileSdkVersion(projectRoot);
|
|
4495
|
+
if (fromProject)
|
|
4496
|
+
return fromProject;
|
|
4497
|
+
return findLocalOhosSdkForVersion(null)?.version ?? null;
|
|
4498
|
+
}
|
|
4499
|
+
function attachOhosApiDbBinding(queries, binding) {
|
|
4500
|
+
try {
|
|
4501
|
+
queries.attachOhosApiDb(binding.dbPath);
|
|
4502
|
+
queries.setMetadata(exports.OHOS_API_VERSION_META, binding.version);
|
|
4503
|
+
queries.setMetadata(exports.OHOS_API_DB_PATH_META, binding.dbPath);
|
|
4504
|
+
return binding;
|
|
4505
|
+
}
|
|
4506
|
+
catch (err) {
|
|
4507
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3817
4508
|
return {
|
|
3818
|
-
|
|
3819
|
-
dbPath
|
|
3820
|
-
packageName: ohosApiDbPackageName(version),
|
|
3821
|
-
installed: false,
|
|
4509
|
+
code: 'ohos_api_db_missing',
|
|
4510
|
+
message: `Failed to attach OHOS API db at ${binding.dbPath}: ${msg}`,
|
|
3822
4511
|
};
|
|
3823
4512
|
}
|
|
4513
|
+
}
|
|
4514
|
+
/** Last-resort prebuilt npm package when the project's API version cannot be obtained. */
|
|
4515
|
+
const OHOS_API_FALLBACK_VERSION = '6.1.0';
|
|
4516
|
+
function runNpmPack(args, cwd) {
|
|
4517
|
+
const inv = process.platform === 'win32'
|
|
4518
|
+
? { cmd: 'cmd.exe', args: ['/d', '/s', '/c', ['npm', ...args].join(' ')] }
|
|
4519
|
+
: { cmd: 'npm', args };
|
|
4520
|
+
(0, child_process_1.execFileSync)(inv.cmd, inv.args, {
|
|
4521
|
+
cwd,
|
|
4522
|
+
stdio: 'pipe',
|
|
4523
|
+
timeout: 180_000,
|
|
4524
|
+
windowsHide: true,
|
|
4525
|
+
env: process.env,
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4528
|
+
/**
|
|
4529
|
+
* Fetch `homegraph-ohos-api-db-<version>` from npm and copy the db into
|
|
4530
|
+
* `~/.homegraph/api/`. Returns null when downloads are disabled, the package
|
|
4531
|
+
* is missing, or install fails. Opt out: HOMEGRAPH_OHOS_API_NO_DOWNLOAD=1.
|
|
4532
|
+
*/
|
|
4533
|
+
function tryDownloadOhosApiDbFromNpm(version) {
|
|
4534
|
+
if (process.env.HOMEGRAPH_OHOS_API_NO_DOWNLOAD === '1')
|
|
4535
|
+
return null;
|
|
4536
|
+
const dbPath = ohosApiDbInstallPath(version);
|
|
3824
4537
|
const packageName = ohosApiDbPackageName(version);
|
|
3825
|
-
|
|
4538
|
+
if (fs.existsSync(dbPath)) {
|
|
4539
|
+
return { version, dbPath, packageName, installed: false };
|
|
4540
|
+
}
|
|
4541
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'hg-ohos-api-npm-'));
|
|
3826
4542
|
try {
|
|
3827
|
-
(
|
|
3828
|
-
|
|
4543
|
+
runNpmPack(['pack', packageName, '--silent'], tmp);
|
|
4544
|
+
const tgz = fs.readdirSync(tmp).find((f) => f.endsWith('.tgz'));
|
|
4545
|
+
if (!tgz)
|
|
4546
|
+
return null;
|
|
4547
|
+
const extractDir = path.join(tmp, 'extract');
|
|
4548
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
4549
|
+
(0, child_process_1.execFileSync)('tar', ['-xzf', path.join(tmp, tgz), '-C', extractDir], {
|
|
3829
4550
|
stdio: 'pipe',
|
|
3830
|
-
timeout:
|
|
3831
|
-
|
|
4551
|
+
timeout: 60_000,
|
|
4552
|
+
windowsHide: true,
|
|
3832
4553
|
});
|
|
4554
|
+
const dbName = ohosApiDbFilename(version);
|
|
4555
|
+
const src = [
|
|
4556
|
+
path.join(extractDir, 'package', dbName),
|
|
4557
|
+
path.join(extractDir, dbName),
|
|
4558
|
+
].find((p) => fs.existsSync(p)) ?? null;
|
|
4559
|
+
if (!src)
|
|
4560
|
+
return null;
|
|
4561
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4562
|
+
fs.copyFileSync(src, dbPath);
|
|
4563
|
+
return { version, dbPath, packageName, installed: true };
|
|
3833
4564
|
}
|
|
3834
|
-
catch
|
|
3835
|
-
|
|
3836
|
-
return {
|
|
3837
|
-
code: 'ohos_api_install_failed',
|
|
3838
|
-
message: `Could not fetch ${packageName} (${msg}). ` +
|
|
3839
|
-
'Explore will use project code only until the matching API db is available.',
|
|
3840
|
-
};
|
|
4565
|
+
catch {
|
|
4566
|
+
return null;
|
|
3841
4567
|
}
|
|
3842
4568
|
finally {
|
|
3843
|
-
|
|
4569
|
+
try {
|
|
4570
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
4571
|
+
}
|
|
4572
|
+
catch {
|
|
4573
|
+
/* ignore */
|
|
4574
|
+
}
|
|
3844
4575
|
}
|
|
3845
|
-
|
|
4576
|
+
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Build (or reuse) ~/.homegraph/api/ohos-api-<version>.db from a local SDK,
|
|
4579
|
+
* then npm prebuilts. Never throws — missing SDK / build / download failure
|
|
4580
|
+
* returns a warning.
|
|
4581
|
+
*
|
|
4582
|
+
* Order: existing db → local SDK build → npm pack for that version →
|
|
4583
|
+
* npm pack homegraph-ohos-api-db-6.1.0 fallback.
|
|
4584
|
+
*/
|
|
4585
|
+
async function ensureOhosApiDb(version, options = {}) {
|
|
4586
|
+
const dbPath = ohosApiDbInstallPath(version);
|
|
4587
|
+
const packageName = ohosApiDbPackageName(version);
|
|
4588
|
+
// 1. Local db for the requested version
|
|
4589
|
+
if (fs.existsSync(dbPath)) {
|
|
4590
|
+
return { version, dbPath, packageName, installed: false };
|
|
4591
|
+
}
|
|
4592
|
+
if (options.build === false || process.env.HOMEGRAPH_OHOS_API_SKIP === '1') {
|
|
3846
4593
|
return {
|
|
3847
4594
|
code: 'ohos_api_db_missing',
|
|
3848
|
-
message: `${
|
|
4595
|
+
message: `${ohosApiDbFilename(version)} is not under ~/.homegraph/api/ yet. ` +
|
|
4596
|
+
'Re-run `homegraph init -i` / `homegraph index` with a local OHOS SDK, or `homegraph index-api <sdk>`. ' +
|
|
3849
4597
|
'Explore will use project code only.',
|
|
3850
4598
|
};
|
|
3851
4599
|
}
|
|
3852
|
-
|
|
4600
|
+
// 2. Build from a local DevEco / OpenHarmony SDK
|
|
4601
|
+
let sdkHome = null;
|
|
4602
|
+
if (options.sdkHomeHint) {
|
|
4603
|
+
const hintHomes = expandLocalOhosSdkHomes(options.sdkHomeHint);
|
|
4604
|
+
const match = hintHomes.find((h) => readOhosSdkPlatformVersion(h) === version) ?? hintHomes[0];
|
|
4605
|
+
if (match && hasOpenHarmonyEts(match))
|
|
4606
|
+
sdkHome = match;
|
|
4607
|
+
}
|
|
4608
|
+
if (!sdkHome) {
|
|
4609
|
+
sdkHome = findLocalOhosSdkForVersion(version)?.sdkHome ?? null;
|
|
4610
|
+
}
|
|
4611
|
+
if (sdkHome) {
|
|
4612
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4613
|
+
options.onProgress?.({
|
|
4614
|
+
phase: 'ohos-api',
|
|
4615
|
+
current: 0,
|
|
4616
|
+
total: 1,
|
|
4617
|
+
currentFile: version,
|
|
4618
|
+
});
|
|
4619
|
+
try {
|
|
4620
|
+
const result = await indexOhosApiDb({
|
|
4621
|
+
sdkHome,
|
|
4622
|
+
version,
|
|
4623
|
+
outputPath: dbPath,
|
|
4624
|
+
onProgress: options.onProgress
|
|
4625
|
+
? (progress) => {
|
|
4626
|
+
options.onProgress?.({
|
|
4627
|
+
phase: 'ohos-api',
|
|
4628
|
+
current: progress.current,
|
|
4629
|
+
total: progress.total || 1,
|
|
4630
|
+
currentFile: progress.currentFile
|
|
4631
|
+
? `${version} ${path.basename(progress.currentFile)}`
|
|
4632
|
+
: version,
|
|
4633
|
+
subphase: progress.subphase,
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
: undefined,
|
|
4637
|
+
});
|
|
4638
|
+
if (result.success && fs.existsSync(dbPath)) {
|
|
4639
|
+
options.onProgress?.({
|
|
4640
|
+
phase: 'ohos-api',
|
|
4641
|
+
current: 1,
|
|
4642
|
+
total: 1,
|
|
4643
|
+
currentFile: version,
|
|
4644
|
+
});
|
|
4645
|
+
return { version, dbPath, packageName, installed: true };
|
|
4646
|
+
}
|
|
4647
|
+
try {
|
|
4648
|
+
fs.unlinkSync(dbPath);
|
|
4649
|
+
}
|
|
4650
|
+
catch {
|
|
4651
|
+
/* ignore */
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
catch {
|
|
4655
|
+
try {
|
|
4656
|
+
if (fs.existsSync(dbPath))
|
|
4657
|
+
fs.unlinkSync(dbPath);
|
|
4658
|
+
}
|
|
4659
|
+
catch {
|
|
4660
|
+
/* ignore */
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4664
|
+
// 3. Download prebuilt for the requested version from npm
|
|
4665
|
+
const fromNpm = tryDownloadOhosApiDbFromNpm(version);
|
|
4666
|
+
if (fromNpm)
|
|
4667
|
+
return fromNpm;
|
|
4668
|
+
// 4. Last resort: homegraph-ohos-api-db-6.1.0
|
|
4669
|
+
if (version !== OHOS_API_FALLBACK_VERSION) {
|
|
4670
|
+
const fallbackPath = ohosApiDbInstallPath(OHOS_API_FALLBACK_VERSION);
|
|
4671
|
+
if (fs.existsSync(fallbackPath)) {
|
|
4672
|
+
return {
|
|
4673
|
+
version: OHOS_API_FALLBACK_VERSION,
|
|
4674
|
+
dbPath: fallbackPath,
|
|
4675
|
+
packageName: ohosApiDbPackageName(OHOS_API_FALLBACK_VERSION),
|
|
4676
|
+
installed: false,
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
const fallback = tryDownloadOhosApiDbFromNpm(OHOS_API_FALLBACK_VERSION);
|
|
4680
|
+
if (fallback)
|
|
4681
|
+
return fallback;
|
|
4682
|
+
}
|
|
4683
|
+
return {
|
|
4684
|
+
code: 'ohos_api_sdk_missing',
|
|
4685
|
+
message: `OHOS API db for ${version} unavailable (no local SDK build, npm ${packageName} failed` +
|
|
4686
|
+
(version !== OHOS_API_FALLBACK_VERSION
|
|
4687
|
+
? `, and fallback ${ohosApiDbPackageName(OHOS_API_FALLBACK_VERSION)} failed`
|
|
4688
|
+
: '') +
|
|
4689
|
+
'). Set OHOS_SDK_HOME / DEVECO_SDK_HOME or install the matching DevEco SDK. ' +
|
|
4690
|
+
'SDK API lookup disabled; project indexing continues normally.',
|
|
4691
|
+
};
|
|
4692
|
+
}
|
|
4693
|
+
/**
|
|
4694
|
+
* Attach a prebuilt API db if present — never builds. Used on open / wireLayers.
|
|
4695
|
+
*/
|
|
4696
|
+
function attachExistingOhosApiDbForProject(projectRoot, queries, languages) {
|
|
4697
|
+
if (!isOhosArktsProject(projectRoot, languages))
|
|
4698
|
+
return null;
|
|
4699
|
+
const version = resolveOhosApiVersionForProject(projectRoot);
|
|
4700
|
+
if (!version)
|
|
4701
|
+
return null;
|
|
4702
|
+
const dbPath = ohosApiDbInstallPath(version);
|
|
4703
|
+
if (!fs.existsSync(dbPath))
|
|
4704
|
+
return null;
|
|
4705
|
+
return attachOhosApiDbBinding(queries, {
|
|
4706
|
+
version,
|
|
4707
|
+
dbPath,
|
|
4708
|
+
packageName: ohosApiDbPackageName(version),
|
|
4709
|
+
installed: false,
|
|
4710
|
+
});
|
|
3853
4711
|
}
|
|
3854
|
-
/**
|
|
3855
|
-
|
|
4712
|
+
/**
|
|
4713
|
+
* Detect version, build from local SDK when needed, persist metadata, ATTACH — never throws.
|
|
4714
|
+
* Pass `{ build: true }` (default) after init/index; `{ build: false }` on open.
|
|
4715
|
+
*/
|
|
4716
|
+
async function bindOhosApiDbForProject(projectRoot, queries, languages, options = {}) {
|
|
3856
4717
|
if (!isOhosArktsProject(projectRoot, languages))
|
|
3857
4718
|
return null;
|
|
3858
|
-
const version =
|
|
4719
|
+
const version = resolveOhosApiVersionForProject(projectRoot);
|
|
3859
4720
|
if (!version) {
|
|
3860
4721
|
return {
|
|
3861
4722
|
code: 'ohos_api_version_unknown',
|
|
3862
|
-
message: 'HarmonyOS project detected but compileSdkVersion not found in build-profile.json5
|
|
4723
|
+
message: 'HarmonyOS project detected but compileSdkVersion was not found in build-profile.json5 ' +
|
|
4724
|
+
'and no local OHOS SDK was discovered (set OHOS_SDK_HOME / DEVECO_SDK_HOME). ' +
|
|
3863
4725
|
'SDK API lookup disabled; project indexing continues normally.',
|
|
3864
4726
|
};
|
|
3865
4727
|
}
|
|
3866
|
-
const ensured = ensureOhosApiDb(version);
|
|
4728
|
+
const ensured = await ensureOhosApiDb(version, options);
|
|
3867
4729
|
if ('code' in ensured)
|
|
3868
4730
|
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;
|
|
4731
|
+
return attachOhosApiDbBinding(queries, ensured);
|
|
3882
4732
|
}
|
|
3883
4733
|
/** Re-ATTACH from project metadata after reopen / new QueryBuilder. */
|
|
3884
4734
|
function restoreOhosApiDbAttach(queries) {
|