kirograph 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +103 -1822
- package/dist/architecture/layers/dart.js +112 -0
- package/dist/architecture/layers/dart.js.map +7 -0
- package/dist/architecture/layers/index.js +3 -1
- package/dist/architecture/layers/index.js.map +2 -2
- package/dist/bin/banner.js +74 -6
- package/dist/bin/banner.js.map +2 -2
- package/dist/bin/commands/attack-surface.js +157 -0
- package/dist/bin/commands/attack-surface.js.map +7 -0
- package/dist/bin/commands/benchmark.js +206 -0
- package/dist/bin/commands/benchmark.js.map +7 -0
- package/dist/bin/commands/budget.js +70 -0
- package/dist/bin/commands/budget.js.map +7 -0
- package/dist/bin/commands/communities.js +77 -0
- package/dist/bin/commands/communities.js.map +7 -0
- package/dist/bin/commands/dep-confusion.js +119 -0
- package/dist/bin/commands/dep-confusion.js.map +7 -0
- package/dist/bin/commands/export.js +304 -10
- package/dist/bin/commands/export.js.map +2 -2
- package/dist/bin/commands/flows.js +97 -0
- package/dist/bin/commands/flows.js.map +7 -0
- package/dist/bin/commands/help.js +382 -101
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/install.js +90 -8
- package/dist/bin/commands/install.js.map +2 -2
- package/dist/bin/commands/licenses.js +201 -0
- package/dist/bin/commands/licenses.js.map +7 -0
- package/dist/bin/commands/reachability.js +160 -0
- package/dist/bin/commands/reachability.js.map +7 -0
- package/dist/bin/commands/read.js +100 -0
- package/dist/bin/commands/read.js.map +7 -0
- package/dist/bin/commands/refactor.js +116 -0
- package/dist/bin/commands/refactor.js.map +7 -0
- package/dist/bin/commands/remediation.js +133 -0
- package/dist/bin/commands/remediation.js.map +7 -0
- package/dist/bin/commands/sbom.js +76 -0
- package/dist/bin/commands/sbom.js.map +7 -0
- package/dist/bin/commands/security-ci-report.js +267 -0
- package/dist/bin/commands/security-ci-report.js.map +7 -0
- package/dist/bin/commands/security-export.js +1349 -0
- package/dist/bin/commands/security-export.js.map +7 -0
- package/dist/bin/commands/security-flows.js +126 -0
- package/dist/bin/commands/security-flows.js.map +7 -0
- package/dist/bin/commands/security-secrets.js +170 -0
- package/dist/bin/commands/security-secrets.js.map +7 -0
- package/dist/bin/commands/security.js +193 -0
- package/dist/bin/commands/security.js.map +7 -0
- package/dist/bin/commands/staleness.js +144 -0
- package/dist/bin/commands/staleness.js.map +7 -0
- package/dist/bin/commands/status.js +102 -2
- package/dist/bin/commands/status.js.map +2 -2
- package/dist/bin/commands/supply-chain.js +138 -0
- package/dist/bin/commands/supply-chain.js.map +7 -0
- package/dist/bin/commands/uninit.js +109 -60
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/commands/vex.js +76 -0
- package/dist/bin/commands/vex.js.map +7 -0
- package/dist/bin/commands/vuln-suppress.js +95 -0
- package/dist/bin/commands/vuln-suppress.js.map +7 -0
- package/dist/bin/commands/vulns.js +382 -0
- package/dist/bin/commands/vulns.js.map +7 -0
- package/dist/bin/installer/auto-detect.js +125 -0
- package/dist/bin/installer/auto-detect.js.map +7 -0
- package/dist/bin/installer/cli-agent.js +28 -5
- package/dist/bin/installer/cli-agent.js.map +2 -2
- package/dist/bin/installer/common.js +47 -0
- package/dist/bin/installer/common.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +12 -1
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/detect.js +309 -0
- package/dist/bin/installer/detect.js.map +7 -0
- package/dist/bin/installer/index.js +9 -1
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/instructions.js +195 -3
- package/dist/bin/installer/instructions.js.map +2 -2
- package/dist/bin/installer/steering.js +407 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.js +3 -3
- package/dist/bin/installer/targets/aider.js.map +2 -2
- package/dist/bin/installer/targets/amp.js +3 -3
- package/dist/bin/installer/targets/amp.js.map +2 -2
- package/dist/bin/installer/targets/antigravity.js +35 -6
- package/dist/bin/installer/targets/antigravity.js.map +2 -2
- package/dist/bin/installer/targets/augment.js +3 -3
- package/dist/bin/installer/targets/augment.js.map +2 -2
- package/dist/bin/installer/targets/claude.js +49 -5
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.js +32 -6
- package/dist/bin/installer/targets/cline.js.map +2 -2
- package/dist/bin/installer/targets/codex.js +64 -12
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.js +2 -2
- package/dist/bin/installer/targets/continue.js.map +2 -2
- package/dist/bin/installer/targets/copilot-cli.js +101 -0
- package/dist/bin/installer/targets/copilot-cli.js.map +7 -0
- package/dist/bin/installer/targets/copilot.js +31 -5
- package/dist/bin/installer/targets/copilot.js.map +2 -2
- package/dist/bin/installer/targets/cursor.js +4 -4
- package/dist/bin/installer/targets/cursor.js.map +2 -2
- package/dist/bin/installer/targets/devin.js +2 -2
- package/dist/bin/installer/targets/devin.js.map +2 -2
- package/dist/bin/installer/targets/gemini-cli.js +2 -2
- package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
- package/dist/bin/installer/targets/generic.js +2 -14
- package/dist/bin/installer/targets/generic.js.map +2 -2
- package/dist/bin/installer/targets/goose.js +3 -3
- package/dist/bin/installer/targets/goose.js.map +2 -2
- package/dist/bin/installer/targets/index.js +250 -0
- package/dist/bin/installer/targets/index.js.map +2 -2
- package/dist/bin/installer/targets/junie.js +2 -2
- package/dist/bin/installer/targets/junie.js.map +2 -2
- package/dist/bin/installer/targets/kilo.js +2 -2
- package/dist/bin/installer/targets/kilo.js.map +2 -2
- package/dist/bin/installer/targets/kiro.js +3 -3
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/installer/targets/opencode.js +2 -2
- package/dist/bin/installer/targets/opencode.js.map +2 -2
- package/dist/bin/installer/targets/openhands.js +3 -3
- package/dist/bin/installer/targets/openhands.js.map +2 -2
- package/dist/bin/installer/targets/qoder.js +91 -0
- package/dist/bin/installer/targets/qoder.js.map +7 -0
- package/dist/bin/installer/targets/qwen.js +96 -0
- package/dist/bin/installer/targets/qwen.js.map +7 -0
- package/dist/bin/installer/targets/replit.js +3 -3
- package/dist/bin/installer/targets/replit.js.map +2 -2
- package/dist/bin/installer/targets/roo.js +2 -2
- package/dist/bin/installer/targets/roo.js.map +2 -2
- package/dist/bin/installer/targets/tabnine.js +3 -3
- package/dist/bin/installer/targets/tabnine.js.map +2 -2
- package/dist/bin/installer/targets/trae.js +5 -5
- package/dist/bin/installer/targets/trae.js.map +2 -2
- package/dist/bin/installer/targets/warp.js +2 -2
- package/dist/bin/installer/targets/warp.js.map +2 -2
- package/dist/bin/installer/targets/windsurf.js +35 -6
- package/dist/bin/installer/targets/windsurf.js.map +2 -2
- package/dist/bin/kirograph.js +72 -6
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/compression/tracker.js +80 -0
- package/dist/compression/tracker.js.map +2 -2
- package/dist/config.js +90 -3
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +36 -2
- package/dist/core/pipeline.js.map +2 -2
- package/dist/db/database.js +56 -5
- package/dist/db/database.js.map +2 -2
- package/dist/db/memory-schema.sql +5 -1
- package/dist/db/schema.sql +3 -1
- package/dist/db/security-schema.sql +72 -0
- package/dist/extraction/extractor.js +81 -0
- package/dist/extraction/extractor.js.map +2 -2
- package/dist/extraction/grammars.js +32 -8
- package/dist/extraction/grammars.js.map +2 -2
- package/dist/extraction/languages.js +43 -1
- package/dist/extraction/languages.js.map +2 -2
- package/dist/extraction/notebook.js +300 -0
- package/dist/extraction/notebook.js.map +7 -0
- package/dist/extraction/wasm/tree-sitter-astro.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-gdscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-julia.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-perl.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-powershell.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-sql.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-verilog.wasm +0 -0
- package/dist/frameworks/flutter.js +130 -0
- package/dist/frameworks/flutter.js.map +7 -0
- package/dist/frameworks/index.js +7 -1
- package/dist/frameworks/index.js.map +3 -3
- package/dist/graph/communities.js +317 -0
- package/dist/graph/communities.js.map +7 -0
- package/dist/graph/flows.js +138 -0
- package/dist/graph/flows.js.map +7 -0
- package/dist/graph/refactor.js +155 -0
- package/dist/graph/refactor.js.map +7 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mcp/cache.js +172 -0
- package/dist/mcp/cache.js.map +7 -0
- package/dist/mcp/read-modes.js +295 -0
- package/dist/mcp/read-modes.js.map +7 -0
- package/dist/mcp/server.js +2 -1
- package/dist/mcp/server.js.map +2 -2
- package/dist/mcp/tool-names.js +21 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +1160 -1
- package/dist/mcp/tools.js.map +3 -3
- package/dist/memory/database.js +32 -2
- package/dist/memory/database.js.map +2 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/resolution/bridges/android-rn.js +298 -0
- package/dist/resolution/bridges/android-rn.js.map +7 -0
- package/dist/resolution/bridges/expo-modules.js +155 -0
- package/dist/resolution/bridges/expo-modules.js.map +7 -0
- package/dist/resolution/bridges/flutter-channel.js +385 -0
- package/dist/resolution/bridges/flutter-channel.js.map +7 -0
- package/dist/resolution/bridges/index.js +80 -0
- package/dist/resolution/bridges/index.js.map +7 -0
- package/dist/resolution/bridges/native-events.js +188 -0
- package/dist/resolution/bridges/native-events.js.map +7 -0
- package/dist/resolution/bridges/native-views.js +244 -0
- package/dist/resolution/bridges/native-views.js.map +7 -0
- package/dist/resolution/bridges/react-native.js +161 -0
- package/dist/resolution/bridges/react-native.js.map +7 -0
- package/dist/resolution/bridges/swift-objc.js +227 -0
- package/dist/resolution/bridges/swift-objc.js.map +7 -0
- package/dist/resolution/bridges/turbomodules.js +216 -0
- package/dist/resolution/bridges/turbomodules.js.map +7 -0
- package/dist/resolution/index.js +61 -2
- package/dist/resolution/index.js.map +2 -2
- package/dist/security/attack-surface.js +164 -0
- package/dist/security/attack-surface.js.map +7 -0
- package/dist/security/context-warnings.js +123 -0
- package/dist/security/context-warnings.js.map +7 -0
- package/dist/security/context-warnings.test.js +300 -0
- package/dist/security/context-warnings.test.js.map +7 -0
- package/dist/security/data-flows.js +228 -0
- package/dist/security/data-flows.js.map +7 -0
- package/dist/security/dep-confusion.js +255 -0
- package/dist/security/dep-confusion.js.map +7 -0
- package/dist/security/errors.js +51 -0
- package/dist/security/errors.js.map +7 -0
- package/dist/security/export/fix-suggestions.js +58 -0
- package/dist/security/export/fix-suggestions.js.map +7 -0
- package/dist/security/export/fix-suggestions.test.js +74 -0
- package/dist/security/export/fix-suggestions.test.js.map +7 -0
- package/dist/security/export/sbom.js +230 -0
- package/dist/security/export/sbom.js.map +7 -0
- package/dist/security/export/sbom.test.js +288 -0
- package/dist/security/export/sbom.test.js.map +7 -0
- package/dist/security/export/serialization.js +142 -0
- package/dist/security/export/serialization.js.map +7 -0
- package/dist/security/export/serialization.test.js +310 -0
- package/dist/security/export/serialization.test.js.map +7 -0
- package/dist/security/export/vex.js +220 -0
- package/dist/security/export/vex.js.map +7 -0
- package/dist/security/export/vex.test.js +278 -0
- package/dist/security/export/vex.test.js.map +7 -0
- package/dist/security/index.js +67 -0
- package/dist/security/index.js.map +7 -0
- package/dist/security/integrator-transitives.test.js +359 -0
- package/dist/security/integrator-transitives.test.js.map +7 -0
- package/dist/security/integrator.js +491 -0
- package/dist/security/integrator.js.map +7 -0
- package/dist/security/integrator.test.js +237 -0
- package/dist/security/integrator.test.js.map +7 -0
- package/dist/security/license.js +70 -0
- package/dist/security/license.js.map +7 -0
- package/dist/security/manifest/adapter.js +243 -0
- package/dist/security/manifest/adapter.js.map +7 -0
- package/dist/security/manifest/adapter.test.js +272 -0
- package/dist/security/manifest/adapter.test.js.map +7 -0
- package/dist/security/manifest/parser.js +376 -0
- package/dist/security/manifest/parser.js.map +7 -0
- package/dist/security/manifest/parser.test.js +110 -0
- package/dist/security/manifest/parser.test.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.js +233 -0
- package/dist/security/manifest/plugins/cargo.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.test.js +304 -0
- package/dist/security/manifest/plugins/cargo.test.js.map +7 -0
- package/dist/security/manifest/plugins/composer.js +152 -0
- package/dist/security/manifest/plugins/composer.js.map +7 -0
- package/dist/security/manifest/plugins/go.js +146 -0
- package/dist/security/manifest/plugins/go.js.map +7 -0
- package/dist/security/manifest/plugins/go.test.js +341 -0
- package/dist/security/manifest/plugins/go.test.js.map +7 -0
- package/dist/security/manifest/plugins/gradle.js +159 -0
- package/dist/security/manifest/plugins/gradle.js.map +7 -0
- package/dist/security/manifest/plugins/hex.js +159 -0
- package/dist/security/manifest/plugins/hex.js.map +7 -0
- package/dist/security/manifest/plugins/maven.js +128 -0
- package/dist/security/manifest/plugins/maven.js.map +7 -0
- package/dist/security/manifest/plugins/maven.test.js +389 -0
- package/dist/security/manifest/plugins/maven.test.js.map +7 -0
- package/dist/security/manifest/plugins/npm.js +284 -0
- package/dist/security/manifest/plugins/npm.js.map +7 -0
- package/dist/security/manifest/plugins/npm.test.js +298 -0
- package/dist/security/manifest/plugins/npm.test.js.map +7 -0
- package/dist/security/manifest/plugins/nuget.js +186 -0
- package/dist/security/manifest/plugins/nuget.js.map +7 -0
- package/dist/security/manifest/plugins/pip.js +123 -0
- package/dist/security/manifest/plugins/pip.js.map +7 -0
- package/dist/security/manifest/plugins/pip.test.js +208 -0
- package/dist/security/manifest/plugins/pip.test.js.map +7 -0
- package/dist/security/manifest/plugins/pubspec.js +212 -0
- package/dist/security/manifest/plugins/pubspec.js.map +7 -0
- package/dist/security/manifest/plugins/pyproject.js +261 -0
- package/dist/security/manifest/plugins/pyproject.js.map +7 -0
- package/dist/security/manifest/plugins/rubygems.js +185 -0
- package/dist/security/manifest/plugins/rubygems.js.map +7 -0
- package/dist/security/manifest/plugins/swift.js +200 -0
- package/dist/security/manifest/plugins/swift.js.map +7 -0
- package/dist/security/owasp.js +104 -0
- package/dist/security/owasp.js.map +7 -0
- package/dist/security/pipeline.js +125 -0
- package/dist/security/pipeline.js.map +7 -0
- package/dist/security/reachability.js +276 -0
- package/dist/security/reachability.js.map +7 -0
- package/dist/security/reachability.test.js +394 -0
- package/dist/security/reachability.test.js.map +7 -0
- package/dist/security/remediation.js +126 -0
- package/dist/security/remediation.js.map +7 -0
- package/dist/security/secrets.js +176 -0
- package/dist/security/secrets.js.map +7 -0
- package/dist/security/staleness.js +231 -0
- package/dist/security/staleness.js.map +7 -0
- package/dist/security/supply-chain.js +301 -0
- package/dist/security/supply-chain.js.map +7 -0
- package/dist/security/suppressions.js +106 -0
- package/dist/security/suppressions.js.map +7 -0
- package/dist/security/types.js +17 -0
- package/dist/security/types.js.map +7 -0
- package/dist/security/vuln/client.js +355 -0
- package/dist/security/vuln/client.js.map +7 -0
- package/dist/security/vuln/client.test.js +288 -0
- package/dist/security/vuln/client.test.js.map +7 -0
- package/dist/security/vuln/epss-client.js +95 -0
- package/dist/security/vuln/epss-client.js.map +7 -0
- package/dist/security/vuln/index.js +29 -0
- package/dist/security/vuln/index.js.map +7 -0
- package/dist/security/vuln/osv-adapter.js +321 -0
- package/dist/security/vuln/osv-adapter.js.map +7 -0
- package/dist/security/vuln/osv-adapter.test.js +391 -0
- package/dist/security/vuln/osv-adapter.test.js.map +7 -0
- package/dist/security/vuln/types.js +17 -0
- package/dist/security/vuln/types.js.map +7 -0
- package/dist/types.js.map +2 -2
- package/package.json +2 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var expo_modules_exports = {};
|
|
20
|
+
__export(expo_modules_exports, {
|
|
21
|
+
expoModulesBridge: () => expoModulesBridge
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(expo_modules_exports);
|
|
24
|
+
function findExpoModuleDecls(context) {
|
|
25
|
+
const decls = [];
|
|
26
|
+
const files = context.getAllFiles();
|
|
27
|
+
for (const f of files) {
|
|
28
|
+
if (!f.endsWith(".swift") && !f.endsWith(".kt")) continue;
|
|
29
|
+
const content = context.readFile(f);
|
|
30
|
+
if (!content) continue;
|
|
31
|
+
const nameMatch = content.match(/Name\s*\(\s*"(\w+)"\s*\)/);
|
|
32
|
+
if (!nameMatch) continue;
|
|
33
|
+
const moduleName = nameMatch[1];
|
|
34
|
+
const functions = /* @__PURE__ */ new Map();
|
|
35
|
+
const nodes = context.getNodesInFile(f);
|
|
36
|
+
const funcRegex = /(?:Async)?Function\s*\(\s*"(\w+)"\s*\)/g;
|
|
37
|
+
let match;
|
|
38
|
+
while ((match = funcRegex.exec(content)) !== null) {
|
|
39
|
+
const funcName = match[1];
|
|
40
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
41
|
+
const closestNode = nodes.find(
|
|
42
|
+
(n) => (n.kind === "method" || n.kind === "function") && Math.abs(n.startLine - lineNum) < 10
|
|
43
|
+
);
|
|
44
|
+
if (closestNode) {
|
|
45
|
+
functions.set(funcName, closestNode.id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const propRegex = /Property\s*\(\s*"(\w+)"\s*\)/g;
|
|
49
|
+
while ((match = propRegex.exec(content)) !== null) {
|
|
50
|
+
const propName = match[1];
|
|
51
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
52
|
+
const closestNode = nodes.find(
|
|
53
|
+
(n) => (n.kind === "property" || n.kind === "method") && Math.abs(n.startLine - lineNum) < 10
|
|
54
|
+
);
|
|
55
|
+
if (closestNode) {
|
|
56
|
+
functions.set(propName, closestNode.id);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (functions.size > 0) {
|
|
60
|
+
decls.push({ moduleName, functions, filePath: f });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return decls;
|
|
64
|
+
}
|
|
65
|
+
function findExpoModuleCalls(context) {
|
|
66
|
+
const calls = [];
|
|
67
|
+
const files = context.getAllFiles();
|
|
68
|
+
for (const f of files) {
|
|
69
|
+
if (!f.endsWith(".ts") && !f.endsWith(".tsx") && !f.endsWith(".js") && !f.endsWith(".jsx")) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const content = context.readFile(f);
|
|
73
|
+
if (!content) continue;
|
|
74
|
+
const nodes = context.getNodesInFile(f);
|
|
75
|
+
const requireRegex = /requireNativeModule\s*\(\s*['"](\w+)['"]\s*\)\.(\w+)\s*\(/g;
|
|
76
|
+
let match;
|
|
77
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
78
|
+
const moduleName = match[1];
|
|
79
|
+
const functionName = match[2];
|
|
80
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
81
|
+
const enclosing = nodes.find(
|
|
82
|
+
(n) => (n.kind === "function" || n.kind === "method") && n.startLine <= lineNum && n.endLine >= lineNum
|
|
83
|
+
);
|
|
84
|
+
if (enclosing) {
|
|
85
|
+
calls.push({ moduleName, functionName, callerNodeId: enclosing.id });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const assignRegex = /(?:const|let|var)\s+(\w+)\s*=\s*requireNativeModule\s*\(\s*['"](\w+)['"]\s*\)/g;
|
|
89
|
+
while ((match = assignRegex.exec(content)) !== null) {
|
|
90
|
+
const varName = match[1];
|
|
91
|
+
const moduleName = match[2];
|
|
92
|
+
const callOnVarRegex = new RegExp(`${varName}\\.(\\w+)\\s*\\(`, "g");
|
|
93
|
+
let callMatch;
|
|
94
|
+
while ((callMatch = callOnVarRegex.exec(content)) !== null) {
|
|
95
|
+
const functionName = callMatch[1];
|
|
96
|
+
const lineNum = content.slice(0, callMatch.index).split("\n").length;
|
|
97
|
+
const enclosing = nodes.find(
|
|
98
|
+
(n) => (n.kind === "function" || n.kind === "method") && n.startLine <= lineNum && n.endLine >= lineNum
|
|
99
|
+
);
|
|
100
|
+
if (enclosing) {
|
|
101
|
+
calls.push({ moduleName, functionName, callerNodeId: enclosing.id });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return calls;
|
|
107
|
+
}
|
|
108
|
+
const expoModulesBridge = {
|
|
109
|
+
name: "expo-modules-bridge",
|
|
110
|
+
detect(context) {
|
|
111
|
+
const packageJson = context.readFile("package.json");
|
|
112
|
+
if (packageJson) {
|
|
113
|
+
try {
|
|
114
|
+
const pkg = JSON.parse(packageJson);
|
|
115
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
116
|
+
if ("expo-modules-core" in deps || "expo" in deps) return true;
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (context.fileExists("expo-module.config.json")) return true;
|
|
121
|
+
const files = context.getAllFiles();
|
|
122
|
+
return files.some((f) => f.endsWith("expo-module.config.json"));
|
|
123
|
+
},
|
|
124
|
+
resolve(context) {
|
|
125
|
+
const edges = [];
|
|
126
|
+
const decls = findExpoModuleDecls(context);
|
|
127
|
+
const calls = findExpoModuleCalls(context);
|
|
128
|
+
for (const call of calls) {
|
|
129
|
+
for (const decl of decls) {
|
|
130
|
+
if (call.moduleName !== decl.moduleName) continue;
|
|
131
|
+
const targetNodeId = decl.functions.get(call.functionName);
|
|
132
|
+
if (!targetNodeId) continue;
|
|
133
|
+
edges.push({
|
|
134
|
+
source: call.callerNodeId,
|
|
135
|
+
target: targetNodeId,
|
|
136
|
+
kind: "calls",
|
|
137
|
+
confidence: "inferred",
|
|
138
|
+
confidenceScore: 0.9,
|
|
139
|
+
metadata: {
|
|
140
|
+
synthesizedBy: "expo-modules-bridge",
|
|
141
|
+
provenance: "heuristic",
|
|
142
|
+
moduleName: call.moduleName,
|
|
143
|
+
functionName: call.functionName
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return edges;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
152
|
+
0 && (module.exports = {
|
|
153
|
+
expoModulesBridge
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=expo-modules.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/resolution/bridges/expo-modules.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Expo Modules Bridge\n *\n * Synthesizes edges between JavaScript calls to requireNativeModule('X').fn()\n * and Swift/Kotlin module definitions using the Expo Modules DSL:\n * Module { Name(\"X\"); AsyncFunction(\"fn\") { ... } }\n */\n\nimport type { Node } from '../../types';\nimport type { ResolutionContext } from '../../frameworks/types';\nimport type { BridgeResolver, SynthesizedEdge } from './index';\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface ExpoModuleDecl {\n moduleName: string;\n functions: Map<string, string>; // functionName \u2192 nodeId\n filePath: string;\n}\n\ninterface ExpoModuleCall {\n moduleName: string;\n functionName: string;\n callerNodeId: string;\n}\n\n/**\n * Find Expo module definitions in Swift/Kotlin files.\n * Parses the Expo DSL: Module { Name(\"X\"); Function(\"fn\") { ... } }\n */\nfunction findExpoModuleDecls(context: ResolutionContext): ExpoModuleDecl[] {\n const decls: ExpoModuleDecl[] = [];\n const files = context.getAllFiles();\n\n for (const f of files) {\n if (!f.endsWith('.swift') && !f.endsWith('.kt')) continue;\n\n const content = context.readFile(f);\n if (!content) continue;\n\n // Look for Expo Module DSL pattern\n // Swift: public class MyModule: Module { ... }\n // or: Module { Name(\"ModuleName\") ... }\n const nameMatch = content.match(/Name\\s*\\(\\s*\"(\\w+)\"\\s*\\)/);\n if (!nameMatch) continue;\n\n const moduleName = nameMatch[1];\n const functions = new Map<string, string>();\n const nodes = context.getNodesInFile(f);\n\n // Find function declarations in the DSL\n // Patterns: Function(\"name\"), AsyncFunction(\"name\"), Property(\"name\")\n const funcRegex = /(?:Async)?Function\\s*\\(\\s*\"(\\w+)\"\\s*\\)/g;\n let match: RegExpExecArray | null;\n\n while ((match = funcRegex.exec(content)) !== null) {\n const funcName = match[1];\n const lineNum = content.slice(0, match.index).split('\\n').length;\n\n // Find the closest node to this declaration\n const closestNode = nodes.find(\n n => (n.kind === 'method' || n.kind === 'function') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n\n if (closestNode) {\n functions.set(funcName, closestNode.id);\n }\n }\n\n // Also look for Property declarations\n const propRegex = /Property\\s*\\(\\s*\"(\\w+)\"\\s*\\)/g;\n while ((match = propRegex.exec(content)) !== null) {\n const propName = match[1];\n const lineNum = content.slice(0, match.index).split('\\n').length;\n\n const closestNode = nodes.find(\n n => (n.kind === 'property' || n.kind === 'method') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n\n if (closestNode) {\n functions.set(propName, closestNode.id);\n }\n }\n\n if (functions.size > 0) {\n decls.push({ moduleName, functions, filePath: f });\n }\n }\n\n return decls;\n}\n\n/**\n * Find JS/TS calls to requireNativeModule('X').fn() or useModule patterns.\n */\nfunction findExpoModuleCalls(context: ResolutionContext): ExpoModuleCall[] {\n const calls: ExpoModuleCall[] = [];\n const files = context.getAllFiles();\n\n for (const f of files) {\n if (!f.endsWith('.ts') && !f.endsWith('.tsx') && !f.endsWith('.js') && !f.endsWith('.jsx')) {\n continue;\n }\n\n const content = context.readFile(f);\n if (!content) continue;\n\n const nodes = context.getNodesInFile(f);\n\n // Pattern 1: requireNativeModule('ModuleName').methodName(...)\n const requireRegex = /requireNativeModule\\s*\\(\\s*['\"](\\w+)['\"]\\s*\\)\\.(\\w+)\\s*\\(/g;\n let match: RegExpExecArray | null;\n\n while ((match = requireRegex.exec(content)) !== null) {\n const moduleName = match[1];\n const functionName = match[2];\n const lineNum = content.slice(0, match.index).split('\\n').length;\n\n const enclosing = nodes.find(\n n => (n.kind === 'function' || n.kind === 'method') &&\n n.startLine <= lineNum && n.endLine >= lineNum\n );\n\n if (enclosing) {\n calls.push({ moduleName, functionName, callerNodeId: enclosing.id });\n }\n }\n\n // Pattern 2: const module = requireNativeModule('X'); module.fn()\n const assignRegex = /(?:const|let|var)\\s+(\\w+)\\s*=\\s*requireNativeModule\\s*\\(\\s*['\"](\\w+)['\"]\\s*\\)/g;\n while ((match = assignRegex.exec(content)) !== null) {\n const varName = match[1];\n const moduleName = match[2];\n\n // Find calls on this variable\n const callOnVarRegex = new RegExp(`${varName}\\\\.(\\\\w+)\\\\s*\\\\(`, 'g');\n let callMatch: RegExpExecArray | null;\n\n while ((callMatch = callOnVarRegex.exec(content)) !== null) {\n const functionName = callMatch[1];\n const lineNum = content.slice(0, callMatch.index).split('\\n').length;\n\n const enclosing = nodes.find(\n n => (n.kind === 'function' || n.kind === 'method') &&\n n.startLine <= lineNum && n.endLine >= lineNum\n );\n\n if (enclosing) {\n calls.push({ moduleName, functionName, callerNodeId: enclosing.id });\n }\n }\n }\n }\n\n return calls;\n}\n\n// \u2500\u2500 Bridge Implementation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const expoModulesBridge: BridgeResolver = {\n name: 'expo-modules-bridge',\n\n detect(context: ResolutionContext): boolean {\n // Check for expo-modules-core in package.json\n const packageJson = context.readFile('package.json');\n if (packageJson) {\n try {\n const pkg = JSON.parse(packageJson);\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if ('expo-modules-core' in deps || 'expo' in deps) return true;\n } catch {\n // ignore parse errors\n }\n }\n\n // Check for expo-module.config.json\n if (context.fileExists('expo-module.config.json')) return true;\n\n // Check in subdirectories\n const files = context.getAllFiles();\n return files.some(f => f.endsWith('expo-module.config.json'));\n },\n\n resolve(context: ResolutionContext): SynthesizedEdge[] {\n const edges: SynthesizedEdge[] = [];\n\n const decls = findExpoModuleDecls(context);\n const calls = findExpoModuleCalls(context);\n\n // Match calls to declarations\n for (const call of calls) {\n for (const decl of decls) {\n if (call.moduleName !== decl.moduleName) continue;\n\n const targetNodeId = decl.functions.get(call.functionName);\n if (!targetNodeId) continue;\n\n edges.push({\n source: call.callerNodeId,\n target: targetNodeId,\n kind: 'calls',\n confidence: 'inferred',\n confidenceScore: 0.9,\n metadata: {\n synthesizedBy: 'expo-modules-bridge',\n provenance: 'heuristic',\n moduleName: call.moduleName,\n functionName: call.functionName,\n },\n });\n }\n }\n\n return edges;\n },\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var flutter_channel_exports = {};
|
|
30
|
+
__export(flutter_channel_exports, {
|
|
31
|
+
flutterChannelBridge: () => flutterChannelBridge
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(flutter_channel_exports);
|
|
34
|
+
var path = __toESM(require("path"));
|
|
35
|
+
function hasPubspecFlutter(context) {
|
|
36
|
+
const pubspec = context.readFile("pubspec.yaml");
|
|
37
|
+
if (!pubspec) return false;
|
|
38
|
+
return /^\s*flutter\s*:/m.test(pubspec) || /flutter\s*:/.test(pubspec);
|
|
39
|
+
}
|
|
40
|
+
function hasDartChannelUsage(context) {
|
|
41
|
+
const files = context.getAllFiles();
|
|
42
|
+
for (const f of files) {
|
|
43
|
+
if (!f.endsWith(".dart")) continue;
|
|
44
|
+
const content = context.readFile(f);
|
|
45
|
+
if (!content) continue;
|
|
46
|
+
if (content.includes("MethodChannel(") || content.includes("EventChannel(")) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function extractDartChannelDeclarations(content) {
|
|
53
|
+
const map = /* @__PURE__ */ new Map();
|
|
54
|
+
const declRegex = /(?:static\s+const|final|const|var)\s+(\w+)\s*=\s*(MethodChannel|EventChannel)\s*\(\s*['"]([^'"]+)['"]/g;
|
|
55
|
+
let match;
|
|
56
|
+
while ((match = declRegex.exec(content)) !== null) {
|
|
57
|
+
const varName = match[1];
|
|
58
|
+
const channelClass = match[2];
|
|
59
|
+
const channelName = match[3];
|
|
60
|
+
map.set(varName, {
|
|
61
|
+
channelName,
|
|
62
|
+
channelType: channelClass === "EventChannel" ? "event" : "method"
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return map;
|
|
66
|
+
}
|
|
67
|
+
function findDartChannelCalls(context) {
|
|
68
|
+
const calls = [];
|
|
69
|
+
const files = context.getAllFiles();
|
|
70
|
+
for (const f of files) {
|
|
71
|
+
if (!f.endsWith(".dart")) continue;
|
|
72
|
+
const content = context.readFile(f);
|
|
73
|
+
if (!content) continue;
|
|
74
|
+
if (!content.includes("MethodChannel") && !content.includes("EventChannel")) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const nodes = context.getNodesInFile(f);
|
|
78
|
+
const enclosingNode = (offset) => {
|
|
79
|
+
const lineNum = content.slice(0, offset).split("\n").length;
|
|
80
|
+
return nodes.find(
|
|
81
|
+
(n) => (n.kind === "function" || n.kind === "method") && n.startLine <= lineNum && n.endLine >= lineNum
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
const channelVars = extractDartChannelDeclarations(content);
|
|
85
|
+
const invokeVarRegex = /\b(\w+)\.invokeMethod\s*\(\s*['"](\w+)['"]/g;
|
|
86
|
+
let match;
|
|
87
|
+
while ((match = invokeVarRegex.exec(content)) !== null) {
|
|
88
|
+
const varName = match[1];
|
|
89
|
+
const methodName = match[2];
|
|
90
|
+
const info = channelVars.get(varName);
|
|
91
|
+
if (!info || info.channelType !== "method") continue;
|
|
92
|
+
const caller = enclosingNode(match.index);
|
|
93
|
+
if (caller) {
|
|
94
|
+
calls.push({
|
|
95
|
+
channelName: info.channelName,
|
|
96
|
+
methodName,
|
|
97
|
+
channelType: "method",
|
|
98
|
+
callerNodeId: caller.id,
|
|
99
|
+
filePath: f
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const inlineInvokeRegex = /MethodChannel\s*\(\s*['"]([^'"]+)['"]\s*\)\.invokeMethod\s*\(\s*['"](\w+)['"]/g;
|
|
104
|
+
while ((match = inlineInvokeRegex.exec(content)) !== null) {
|
|
105
|
+
const channelName = match[1];
|
|
106
|
+
const methodName = match[2];
|
|
107
|
+
const caller = enclosingNode(match.index);
|
|
108
|
+
if (caller) {
|
|
109
|
+
calls.push({
|
|
110
|
+
channelName,
|
|
111
|
+
methodName,
|
|
112
|
+
channelType: "method",
|
|
113
|
+
callerNodeId: caller.id,
|
|
114
|
+
filePath: f
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const broadcastVarRegex = /\b(\w+)\.receiveBroadcastStream\s*\(/g;
|
|
119
|
+
while ((match = broadcastVarRegex.exec(content)) !== null) {
|
|
120
|
+
const varName = match[1];
|
|
121
|
+
const info = channelVars.get(varName);
|
|
122
|
+
if (!info || info.channelType !== "event") continue;
|
|
123
|
+
const caller = enclosingNode(match.index);
|
|
124
|
+
if (caller) {
|
|
125
|
+
calls.push({
|
|
126
|
+
channelName: info.channelName,
|
|
127
|
+
methodName: null,
|
|
128
|
+
channelType: "event",
|
|
129
|
+
callerNodeId: caller.id,
|
|
130
|
+
filePath: f
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const inlineBroadcastRegex = /EventChannel\s*\(\s*['"]([^'"]+)['"]\s*\)\.receiveBroadcastStream\s*\(/g;
|
|
135
|
+
while ((match = inlineBroadcastRegex.exec(content)) !== null) {
|
|
136
|
+
const channelName = match[1];
|
|
137
|
+
const caller = enclosingNode(match.index);
|
|
138
|
+
if (caller) {
|
|
139
|
+
calls.push({
|
|
140
|
+
channelName,
|
|
141
|
+
methodName: null,
|
|
142
|
+
channelType: "event",
|
|
143
|
+
callerNodeId: caller.id,
|
|
144
|
+
filePath: f
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return calls;
|
|
150
|
+
}
|
|
151
|
+
function extractChannelNameNearOffset(content, offset, lookback = 400) {
|
|
152
|
+
const region = content.slice(Math.max(0, offset - lookback), offset + 200);
|
|
153
|
+
const channelNameRegex = /['"]([a-zA-Z0-9._-]+\/[a-zA-Z0-9._/-]+)['"]/g;
|
|
154
|
+
let best = null;
|
|
155
|
+
let bestDist = Infinity;
|
|
156
|
+
let match;
|
|
157
|
+
while ((match = channelNameRegex.exec(region)) !== null) {
|
|
158
|
+
const dist = Math.abs(match.index - lookback);
|
|
159
|
+
if (dist < bestDist) {
|
|
160
|
+
bestDist = dist;
|
|
161
|
+
best = match[1];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!best) {
|
|
165
|
+
const fallbackRegex = /['"]([a-zA-Z0-9._/-]{5,})['"]/g;
|
|
166
|
+
while ((match = fallbackRegex.exec(region)) !== null) {
|
|
167
|
+
const dist = Math.abs(match.index - lookback);
|
|
168
|
+
if (dist < bestDist) {
|
|
169
|
+
bestDist = dist;
|
|
170
|
+
best = match[1];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return best;
|
|
175
|
+
}
|
|
176
|
+
function findKotlinJavaHandlers(context) {
|
|
177
|
+
const handlers = [];
|
|
178
|
+
const projectRoot = path.resolve(context.getProjectRoot());
|
|
179
|
+
const files = context.getAllFiles().filter((f) => path.resolve(f).startsWith(projectRoot));
|
|
180
|
+
for (const f of files) {
|
|
181
|
+
if (!f.endsWith(".kt") && !f.endsWith(".java")) continue;
|
|
182
|
+
const content = context.readFile(f);
|
|
183
|
+
if (!content) continue;
|
|
184
|
+
const isFlutterNative = content.includes("setMethodCallHandler") || content.includes("setStreamHandler") || content.includes("FlutterPlugin") || content.includes("MethodChannel") || content.includes("EventChannel");
|
|
185
|
+
if (!isFlutterNative) continue;
|
|
186
|
+
const nodes = context.getNodesInFile(f);
|
|
187
|
+
const language = f.endsWith(".kt") ? "kotlin" : "java";
|
|
188
|
+
const methodHandlerRegex = /\.setMethodCallHandler\s*[\({]/g;
|
|
189
|
+
let match;
|
|
190
|
+
while ((match = methodHandlerRegex.exec(content)) !== null) {
|
|
191
|
+
const channelName = extractChannelNameNearOffset(content, match.index);
|
|
192
|
+
if (!channelName) continue;
|
|
193
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
194
|
+
const handlerNode = nodes.find(
|
|
195
|
+
(n) => (n.kind === "method" || n.kind === "function" || n.kind === "class") && Math.abs(n.startLine - lineNum) < 10
|
|
196
|
+
);
|
|
197
|
+
if (handlerNode) {
|
|
198
|
+
handlers.push({
|
|
199
|
+
channelName,
|
|
200
|
+
channelType: "method",
|
|
201
|
+
nodeId: handlerNode.id,
|
|
202
|
+
language,
|
|
203
|
+
filePath: f
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const streamHandlerRegex = /\.setStreamHandler\s*\(/g;
|
|
208
|
+
while ((match = streamHandlerRegex.exec(content)) !== null) {
|
|
209
|
+
const channelName = extractChannelNameNearOffset(content, match.index);
|
|
210
|
+
if (!channelName) continue;
|
|
211
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
212
|
+
const handlerNode = nodes.find(
|
|
213
|
+
(n) => (n.kind === "method" || n.kind === "function" || n.kind === "class") && Math.abs(n.startLine - lineNum) < 10
|
|
214
|
+
);
|
|
215
|
+
if (handlerNode) {
|
|
216
|
+
handlers.push({
|
|
217
|
+
channelName,
|
|
218
|
+
channelType: "event",
|
|
219
|
+
nodeId: handlerNode.id,
|
|
220
|
+
language,
|
|
221
|
+
filePath: f
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return handlers;
|
|
227
|
+
}
|
|
228
|
+
function findSwiftHandlers(context) {
|
|
229
|
+
const handlers = [];
|
|
230
|
+
const projectRoot = path.resolve(context.getProjectRoot());
|
|
231
|
+
const files = context.getAllFiles().filter((f) => path.resolve(f).startsWith(projectRoot));
|
|
232
|
+
for (const f of files) {
|
|
233
|
+
if (!f.endsWith(".swift")) continue;
|
|
234
|
+
const content = context.readFile(f);
|
|
235
|
+
if (!content) continue;
|
|
236
|
+
const isFlutterNative = content.includes("FlutterMethodChannel") || content.includes("FlutterEventChannel") || content.includes("setMethodCallHandler") || content.includes("setStreamHandler");
|
|
237
|
+
if (!isFlutterNative) continue;
|
|
238
|
+
const nodes = context.getNodesInFile(f);
|
|
239
|
+
const channelVarMap = /* @__PURE__ */ new Map();
|
|
240
|
+
const flutterChannelDeclRegex = /(?:let|var)\s+(\w+)\s*=\s*(FlutterMethodChannel|FlutterEventChannel)\s*\(\s*name\s*:\s*"([^"]+)"/g;
|
|
241
|
+
let match;
|
|
242
|
+
while ((match = flutterChannelDeclRegex.exec(content)) !== null) {
|
|
243
|
+
const varName = match[1];
|
|
244
|
+
const channelClass = match[2];
|
|
245
|
+
const channelName = match[3];
|
|
246
|
+
channelVarMap.set(varName, {
|
|
247
|
+
channelName,
|
|
248
|
+
channelType: channelClass === "FlutterEventChannel" ? "event" : "method"
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
const inlineMethodHandlerRegex = /FlutterMethodChannel\s*\(\s*name\s*:\s*"([^"]+)"[^)]*\)\s*\.setMethodCallHandler/g;
|
|
252
|
+
while ((match = inlineMethodHandlerRegex.exec(content)) !== null) {
|
|
253
|
+
const channelName = match[1];
|
|
254
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
255
|
+
const handlerNode = nodes.find(
|
|
256
|
+
(n) => (n.kind === "method" || n.kind === "function" || n.kind === "class") && Math.abs(n.startLine - lineNum) < 10
|
|
257
|
+
);
|
|
258
|
+
if (handlerNode) {
|
|
259
|
+
handlers.push({
|
|
260
|
+
channelName,
|
|
261
|
+
channelType: "method",
|
|
262
|
+
nodeId: handlerNode.id,
|
|
263
|
+
language: "swift",
|
|
264
|
+
filePath: f
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const namedMethodHandlerRegex = /\b(\w+)\.setMethodCallHandler\s*\{/g;
|
|
269
|
+
while ((match = namedMethodHandlerRegex.exec(content)) !== null) {
|
|
270
|
+
const varName = match[1];
|
|
271
|
+
const info = channelVarMap.get(varName);
|
|
272
|
+
if (!info || info.channelType !== "method") continue;
|
|
273
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
274
|
+
const handlerNode = nodes.find(
|
|
275
|
+
(n) => (n.kind === "method" || n.kind === "function" || n.kind === "class") && Math.abs(n.startLine - lineNum) < 10
|
|
276
|
+
);
|
|
277
|
+
if (handlerNode) {
|
|
278
|
+
handlers.push({
|
|
279
|
+
channelName: info.channelName,
|
|
280
|
+
channelType: "method",
|
|
281
|
+
nodeId: handlerNode.id,
|
|
282
|
+
language: "swift",
|
|
283
|
+
filePath: f
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const inlineStreamHandlerRegex = /FlutterEventChannel\s*\(\s*name\s*:\s*"([^"]+)"[^)]*\)\s*\.setStreamHandler/g;
|
|
288
|
+
while ((match = inlineStreamHandlerRegex.exec(content)) !== null) {
|
|
289
|
+
const channelName = match[1];
|
|
290
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
291
|
+
const handlerNode = nodes.find(
|
|
292
|
+
(n) => (n.kind === "method" || n.kind === "function" || n.kind === "class") && Math.abs(n.startLine - lineNum) < 10
|
|
293
|
+
);
|
|
294
|
+
if (handlerNode) {
|
|
295
|
+
handlers.push({
|
|
296
|
+
channelName,
|
|
297
|
+
channelType: "event",
|
|
298
|
+
nodeId: handlerNode.id,
|
|
299
|
+
language: "swift",
|
|
300
|
+
filePath: f
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const namedStreamHandlerRegex = /\b(\w+)\.setStreamHandler\s*\(/g;
|
|
305
|
+
while ((match = namedStreamHandlerRegex.exec(content)) !== null) {
|
|
306
|
+
const varName = match[1];
|
|
307
|
+
const info = channelVarMap.get(varName);
|
|
308
|
+
if (!info || info.channelType !== "event") continue;
|
|
309
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
310
|
+
const handlerNode = nodes.find(
|
|
311
|
+
(n) => (n.kind === "method" || n.kind === "function" || n.kind === "class") && Math.abs(n.startLine - lineNum) < 10
|
|
312
|
+
);
|
|
313
|
+
if (handlerNode) {
|
|
314
|
+
handlers.push({
|
|
315
|
+
channelName: info.channelName,
|
|
316
|
+
channelType: "event",
|
|
317
|
+
nodeId: handlerNode.id,
|
|
318
|
+
language: "swift",
|
|
319
|
+
filePath: f
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return handlers;
|
|
325
|
+
}
|
|
326
|
+
const flutterChannelBridge = {
|
|
327
|
+
name: "flutter-channel-bridge",
|
|
328
|
+
detect(context) {
|
|
329
|
+
if (hasPubspecFlutter(context)) return true;
|
|
330
|
+
return hasDartChannelUsage(context);
|
|
331
|
+
},
|
|
332
|
+
resolve(context) {
|
|
333
|
+
const edges = [];
|
|
334
|
+
const dartCalls = findDartChannelCalls(context);
|
|
335
|
+
if (dartCalls.length === 0) return edges;
|
|
336
|
+
const kotlinJavaHandlers = findKotlinJavaHandlers(context);
|
|
337
|
+
const swiftHandlers = findSwiftHandlers(context);
|
|
338
|
+
const allHandlers = [...kotlinJavaHandlers, ...swiftHandlers];
|
|
339
|
+
for (const call of dartCalls) {
|
|
340
|
+
const matchingHandlers = allHandlers.filter(
|
|
341
|
+
(h) => h.channelName === call.channelName && h.channelType === call.channelType
|
|
342
|
+
);
|
|
343
|
+
for (const handler of matchingHandlers) {
|
|
344
|
+
if (call.channelType === "method") {
|
|
345
|
+
edges.push({
|
|
346
|
+
source: call.callerNodeId,
|
|
347
|
+
target: handler.nodeId,
|
|
348
|
+
kind: "calls",
|
|
349
|
+
confidence: "inferred",
|
|
350
|
+
confidenceScore: 0.7,
|
|
351
|
+
metadata: {
|
|
352
|
+
synthesizedBy: "flutter-channel-bridge",
|
|
353
|
+
provenance: "heuristic",
|
|
354
|
+
channelName: call.channelName,
|
|
355
|
+
methodName: call.methodName ?? void 0,
|
|
356
|
+
channelType: "method",
|
|
357
|
+
nativeLanguage: handler.language
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
} else {
|
|
361
|
+
edges.push({
|
|
362
|
+
source: call.callerNodeId,
|
|
363
|
+
target: handler.nodeId,
|
|
364
|
+
kind: "references",
|
|
365
|
+
confidence: "inferred",
|
|
366
|
+
confidenceScore: 0.65,
|
|
367
|
+
metadata: {
|
|
368
|
+
synthesizedBy: "flutter-channel-bridge",
|
|
369
|
+
provenance: "heuristic",
|
|
370
|
+
channelName: call.channelName,
|
|
371
|
+
channelType: "event",
|
|
372
|
+
nativeLanguage: handler.language
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return edges;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
382
|
+
0 && (module.exports = {
|
|
383
|
+
flutterChannelBridge
|
|
384
|
+
});
|
|
385
|
+
//# sourceMappingURL=flutter-channel.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/resolution/bridges/flutter-channel.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Flutter Method Channel Bridge\n *\n * Synthesizes edges between Dart callers using MethodChannel.invokeMethod() /\n * EventChannel.receiveBroadcastStream() and their native handler implementations\n * in Kotlin/Java (setMethodCallHandler / setStreamHandler) or Swift/ObjC\n * (FlutterMethodChannel.setMethodCallHandler).\n *\n * The channel name string (e.g. \"com.example/camera\") is the linking key between\n * the Dart side and each native platform side.\n */\n\nimport * as path from 'path';\nimport type { ResolutionContext } from '../../frameworks/types';\nimport type { BridgeResolver, SynthesizedEdge } from './index';\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype ChannelType = 'method' | 'event';\n\ninterface DartChannelCall {\n channelName: string;\n methodName: string | null; // null for EventChannel receiveBroadcastStream\n channelType: ChannelType;\n callerNodeId: string;\n filePath: string;\n}\n\ninterface NativeChannelHandler {\n channelName: string;\n channelType: ChannelType;\n nodeId: string;\n language: 'kotlin' | 'java' | 'swift' | 'objc';\n filePath: string;\n}\n\n// \u2500\u2500 Detection helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Returns true if pubspec.yaml declares a flutter dependency, indicating\n * this is a Flutter project.\n */\nfunction hasPubspecFlutter(context: ResolutionContext): boolean {\n const pubspec = context.readFile('pubspec.yaml');\n if (!pubspec) return false;\n // Flutter apps have a \"flutter:\" section or flutter as a dependency\n return /^\\s*flutter\\s*:/m.test(pubspec) || /flutter\\s*:/.test(pubspec);\n}\n\n/**\n * Returns true if any .dart file in the project uses MethodChannel or\n * EventChannel, as a secondary detection heuristic.\n */\nfunction hasDartChannelUsage(context: ResolutionContext): boolean {\n const files = context.getAllFiles();\n for (const f of files) {\n if (!f.endsWith('.dart')) continue;\n const content = context.readFile(f);\n if (!content) continue;\n if (content.includes('MethodChannel(') || content.includes('EventChannel(')) {\n return true;\n }\n }\n return false;\n}\n\n// \u2500\u2500 Dart-side parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Extract all channel name strings declared in a Dart file.\n *\n * Handles both forms:\n * static const _channel = MethodChannel('com.example/camera');\n * final _channel = MethodChannel(\"com.example/camera\");\n *\n * Returns a map of variable name \u2192 { channelName, channelType }.\n */\nfunction extractDartChannelDeclarations(\n content: string\n): Map<string, { channelName: string; channelType: ChannelType }> {\n const map = new Map<string, { channelName: string; channelType: ChannelType }>();\n\n // MethodChannel / EventChannel variable declarations\n // Matches: [final|static const] <name> = MethodChannel('...')\n const declRegex =\n /(?:static\\s+const|final|const|var)\\s+(\\w+)\\s*=\\s*(MethodChannel|EventChannel)\\s*\\(\\s*['\"]([^'\"]+)['\"]/g;\n let match: RegExpExecArray | null;\n while ((match = declRegex.exec(content)) !== null) {\n const varName = match[1];\n const channelClass = match[2];\n const channelName = match[3];\n map.set(varName, {\n channelName,\n channelType: channelClass === 'EventChannel' ? 'event' : 'method',\n });\n }\n\n // Also handle inline: MethodChannel('com.example/camera').invokeMethod(...)\n // These are collected during call scanning below; no variable name needed.\n\n return map;\n}\n\n/**\n * Scan all .dart files for MethodChannel.invokeMethod() and\n * EventChannel.receiveBroadcastStream() call sites, then resolve them to\n * graph nodes.\n */\nfunction findDartChannelCalls(context: ResolutionContext): DartChannelCall[] {\n const calls: DartChannelCall[] = [];\n const files = context.getAllFiles();\n\n for (const f of files) {\n if (!f.endsWith('.dart')) continue;\n\n const content = context.readFile(f);\n if (!content) continue;\n\n if (!content.includes('MethodChannel') && !content.includes('EventChannel')) {\n continue;\n }\n\n const nodes = context.getNodesInFile(f);\n\n /**\n * Find the enclosing function/method node for a given character offset.\n */\n const enclosingNode = (offset: number) => {\n const lineNum = content.slice(0, offset).split('\\n').length;\n return nodes.find(\n n =>\n (n.kind === 'function' || n.kind === 'method') &&\n n.startLine <= lineNum &&\n n.endLine >= lineNum\n );\n };\n\n // Build variable name \u2192 channel info map for this file\n const channelVars = extractDartChannelDeclarations(content);\n\n // 1. invokeMethod calls on a known variable:\n // _channel.invokeMethod('takePicture', ...)\n const invokeVarRegex = /\\b(\\w+)\\.invokeMethod\\s*\\(\\s*['\"](\\w+)['\"]/g;\n let match: RegExpExecArray | null;\n while ((match = invokeVarRegex.exec(content)) !== null) {\n const varName = match[1];\n const methodName = match[2];\n const info = channelVars.get(varName);\n if (!info || info.channelType !== 'method') continue;\n\n const caller = enclosingNode(match.index);\n if (caller) {\n calls.push({\n channelName: info.channelName,\n methodName,\n channelType: 'method',\n callerNodeId: caller.id,\n filePath: f,\n });\n }\n }\n\n // 2. Inline invokeMethod: MethodChannel('name').invokeMethod('method')\n const inlineInvokeRegex =\n /MethodChannel\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)\\.invokeMethod\\s*\\(\\s*['\"](\\w+)['\"]/g;\n while ((match = inlineInvokeRegex.exec(content)) !== null) {\n const channelName = match[1];\n const methodName = match[2];\n const caller = enclosingNode(match.index);\n if (caller) {\n calls.push({\n channelName,\n methodName,\n channelType: 'method',\n callerNodeId: caller.id,\n filePath: f,\n });\n }\n }\n\n // 3. EventChannel receiveBroadcastStream on a known variable:\n // _eventChannel.receiveBroadcastStream()\n const broadcastVarRegex = /\\b(\\w+)\\.receiveBroadcastStream\\s*\\(/g;\n while ((match = broadcastVarRegex.exec(content)) !== null) {\n const varName = match[1];\n const info = channelVars.get(varName);\n if (!info || info.channelType !== 'event') continue;\n\n const caller = enclosingNode(match.index);\n if (caller) {\n calls.push({\n channelName: info.channelName,\n methodName: null,\n channelType: 'event',\n callerNodeId: caller.id,\n filePath: f,\n });\n }\n }\n\n // 4. Inline EventChannel: EventChannel('name').receiveBroadcastStream()\n const inlineBroadcastRegex =\n /EventChannel\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)\\.receiveBroadcastStream\\s*\\(/g;\n while ((match = inlineBroadcastRegex.exec(content)) !== null) {\n const channelName = match[1];\n const caller = enclosingNode(match.index);\n if (caller) {\n calls.push({\n channelName,\n methodName: null,\n channelType: 'event',\n callerNodeId: caller.id,\n filePath: f,\n });\n }\n }\n }\n\n return calls;\n}\n\n// \u2500\u2500 Native-side parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Extract a channel name from the region of source text surrounding a\n * setMethodCallHandler / setStreamHandler call.\n *\n * Searches backwards up to `lookback` characters from `offset` for a quoted\n * string that is likely the channel name argument passed to the MethodChannel /\n * EventChannel constructor.\n *\n * Returns null if no candidate string is found.\n */\nfunction extractChannelNameNearOffset(\n content: string,\n offset: number,\n lookback = 400\n): string | null {\n const region = content.slice(Math.max(0, offset - lookback), offset + 200);\n\n // Look for a quoted string that looks like a channel name (contains '/')\n // Dart convention: \"com.example/channelName\"\n const channelNameRegex = /['\"]([a-zA-Z0-9._-]+\\/[a-zA-Z0-9._/-]+)['\"]/g;\n let best: string | null = null;\n let bestDist = Infinity;\n let match: RegExpExecArray | null;\n while ((match = channelNameRegex.exec(region)) !== null) {\n const dist = Math.abs(match.index - lookback);\n if (dist < bestDist) {\n bestDist = dist;\n best = match[1];\n }\n }\n\n // Fallback: any quoted string that could be a channel name (no slash required)\n if (!best) {\n const fallbackRegex = /['\"]([a-zA-Z0-9._/-]{5,})['\"]/g;\n while ((match = fallbackRegex.exec(region)) !== null) {\n const dist = Math.abs(match.index - lookback);\n if (dist < bestDist) {\n bestDist = dist;\n best = match[1];\n }\n }\n }\n\n return best;\n}\n\n/**\n * Scan Kotlin/Java files for Flutter MethodChannel and EventChannel handler\n * registrations:\n * MethodChannel(flutterEngine.dartExecutor.binaryMessenger, \"com.example/camera\")\n * .setMethodCallHandler { ... }\n * EventChannel(..., \"com.example/stream\").setStreamHandler(...)\n */\nfunction findKotlinJavaHandlers(context: ResolutionContext): NativeChannelHandler[] {\n const handlers: NativeChannelHandler[] = [];\n const projectRoot = path.resolve(context.getProjectRoot());\n // getAllFiles() returns already-indexed paths from the project \u2014 validate they\n // are within the project root before reading to prevent path traversal.\n const files = context.getAllFiles().filter(f => path.resolve(f).startsWith(projectRoot));\n\n for (const f of files) {\n if (!f.endsWith('.kt') && !f.endsWith('.java')) continue;\n\n const content = context.readFile(f);\n if (!content) continue;\n\n const isFlutterNative =\n content.includes('setMethodCallHandler') ||\n content.includes('setStreamHandler') ||\n content.includes('FlutterPlugin') ||\n content.includes('MethodChannel') ||\n content.includes('EventChannel');\n\n if (!isFlutterNative) continue;\n\n const nodes = context.getNodesInFile(f);\n const language: 'kotlin' | 'java' = f.endsWith('.kt') ? 'kotlin' : 'java';\n\n // setMethodCallHandler { call, result -> ... } or .setMethodCallHandler(handler)\n const methodHandlerRegex = /\\.setMethodCallHandler\\s*[\\({]/g;\n let match: RegExpExecArray | null;\n while ((match = methodHandlerRegex.exec(content)) !== null) {\n const channelName = extractChannelNameNearOffset(content, match.index);\n if (!channelName) continue;\n\n const lineNum = content.slice(0, match.index).split('\\n').length;\n const handlerNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function' || n.kind === 'class') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n\n if (handlerNode) {\n handlers.push({\n channelName,\n channelType: 'method',\n nodeId: handlerNode.id,\n language,\n filePath: f,\n });\n }\n }\n\n // setStreamHandler(...) for EventChannel\n const streamHandlerRegex = /\\.setStreamHandler\\s*\\(/g;\n while ((match = streamHandlerRegex.exec(content)) !== null) {\n const channelName = extractChannelNameNearOffset(content, match.index);\n if (!channelName) continue;\n\n const lineNum = content.slice(0, match.index).split('\\n').length;\n const handlerNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function' || n.kind === 'class') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n\n if (handlerNode) {\n handlers.push({\n channelName,\n channelType: 'event',\n nodeId: handlerNode.id,\n language,\n filePath: f,\n });\n }\n }\n }\n\n return handlers;\n}\n\n/**\n * Scan Swift files for FlutterMethodChannel and FlutterEventChannel handler\n * registrations:\n * let channel = FlutterMethodChannel(name: \"com.example/camera\", binaryMessenger: ...)\n * channel.setMethodCallHandler { call, result in ... }\n *\n * FlutterEventChannel(name: \"com.example/stream\", binaryMessenger: ...)\n * .setStreamHandler(...)\n */\nfunction findSwiftHandlers(context: ResolutionContext): NativeChannelHandler[] {\n const handlers: NativeChannelHandler[] = [];\n const projectRoot = path.resolve(context.getProjectRoot());\n // Validate all paths are within the project root before reading.\n const files = context.getAllFiles().filter(f => path.resolve(f).startsWith(projectRoot));\n\n for (const f of files) {\n if (!f.endsWith('.swift')) continue;\n\n const content = context.readFile(f);\n if (!content) continue;\n\n const isFlutterNative =\n content.includes('FlutterMethodChannel') ||\n content.includes('FlutterEventChannel') ||\n content.includes('setMethodCallHandler') ||\n content.includes('setStreamHandler');\n\n if (!isFlutterNative) continue;\n\n const nodes = context.getNodesInFile(f);\n\n // Build a map of Swift variable name \u2192 channel name for this file.\n // FlutterMethodChannel(name: \"com.example/camera\", binaryMessenger: ...)\n const channelVarMap = new Map<string, { channelName: string; channelType: ChannelType }>();\n\n const flutterChannelDeclRegex =\n /(?:let|var)\\s+(\\w+)\\s*=\\s*(FlutterMethodChannel|FlutterEventChannel)\\s*\\(\\s*name\\s*:\\s*\"([^\"]+)\"/g;\n let match: RegExpExecArray | null;\n while ((match = flutterChannelDeclRegex.exec(content)) !== null) {\n const varName = match[1];\n const channelClass = match[2];\n const channelName = match[3];\n channelVarMap.set(varName, {\n channelName,\n channelType: channelClass === 'FlutterEventChannel' ? 'event' : 'method',\n });\n }\n\n // Also handle inline FlutterMethodChannel(name:...).setMethodCallHandler\n const inlineMethodHandlerRegex =\n /FlutterMethodChannel\\s*\\(\\s*name\\s*:\\s*\"([^\"]+)\"[^)]*\\)\\s*\\.setMethodCallHandler/g;\n while ((match = inlineMethodHandlerRegex.exec(content)) !== null) {\n const channelName = match[1];\n const lineNum = content.slice(0, match.index).split('\\n').length;\n const handlerNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function' || n.kind === 'class') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n if (handlerNode) {\n handlers.push({\n channelName,\n channelType: 'method',\n nodeId: handlerNode.id,\n language: 'swift',\n filePath: f,\n });\n }\n }\n\n // Named variable: channel.setMethodCallHandler { call, result in ... }\n const namedMethodHandlerRegex = /\\b(\\w+)\\.setMethodCallHandler\\s*\\{/g;\n while ((match = namedMethodHandlerRegex.exec(content)) !== null) {\n const varName = match[1];\n const info = channelVarMap.get(varName);\n if (!info || info.channelType !== 'method') continue;\n\n const lineNum = content.slice(0, match.index).split('\\n').length;\n const handlerNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function' || n.kind === 'class') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n if (handlerNode) {\n handlers.push({\n channelName: info.channelName,\n channelType: 'method',\n nodeId: handlerNode.id,\n language: 'swift',\n filePath: f,\n });\n }\n }\n\n // EventChannel: inline FlutterEventChannel(name:...).setStreamHandler(...)\n const inlineStreamHandlerRegex =\n /FlutterEventChannel\\s*\\(\\s*name\\s*:\\s*\"([^\"]+)\"[^)]*\\)\\s*\\.setStreamHandler/g;\n while ((match = inlineStreamHandlerRegex.exec(content)) !== null) {\n const channelName = match[1];\n const lineNum = content.slice(0, match.index).split('\\n').length;\n const handlerNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function' || n.kind === 'class') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n if (handlerNode) {\n handlers.push({\n channelName,\n channelType: 'event',\n nodeId: handlerNode.id,\n language: 'swift',\n filePath: f,\n });\n }\n }\n\n // Named variable: eventChannel.setStreamHandler(...)\n const namedStreamHandlerRegex = /\\b(\\w+)\\.setStreamHandler\\s*\\(/g;\n while ((match = namedStreamHandlerRegex.exec(content)) !== null) {\n const varName = match[1];\n const info = channelVarMap.get(varName);\n if (!info || info.channelType !== 'event') continue;\n\n const lineNum = content.slice(0, match.index).split('\\n').length;\n const handlerNode = nodes.find(\n n =>\n (n.kind === 'method' || n.kind === 'function' || n.kind === 'class') &&\n Math.abs(n.startLine - lineNum) < 10\n );\n if (handlerNode) {\n handlers.push({\n channelName: info.channelName,\n channelType: 'event',\n nodeId: handlerNode.id,\n language: 'swift',\n filePath: f,\n });\n }\n }\n }\n\n return handlers;\n}\n\n// \u2500\u2500 Bridge Implementation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const flutterChannelBridge: BridgeResolver = {\n name: 'flutter-channel-bridge',\n\n detect(context: ResolutionContext): boolean {\n // Primary signal: pubspec.yaml with flutter dependency\n if (hasPubspecFlutter(context)) return true;\n\n // Secondary signal: .dart files that use MethodChannel / EventChannel\n return hasDartChannelUsage(context);\n },\n\n resolve(context: ResolutionContext): SynthesizedEdge[] {\n const edges: SynthesizedEdge[] = [];\n\n const dartCalls = findDartChannelCalls(context);\n if (dartCalls.length === 0) return edges;\n\n const kotlinJavaHandlers = findKotlinJavaHandlers(context);\n const swiftHandlers = findSwiftHandlers(context);\n const allHandlers: NativeChannelHandler[] = [...kotlinJavaHandlers, ...swiftHandlers];\n\n for (const call of dartCalls) {\n const matchingHandlers = allHandlers.filter(\n h => h.channelName === call.channelName && h.channelType === call.channelType\n );\n\n for (const handler of matchingHandlers) {\n if (call.channelType === 'method') {\n // MethodChannel invokeMethod \u2192 native setMethodCallHandler\n edges.push({\n source: call.callerNodeId,\n target: handler.nodeId,\n kind: 'calls',\n confidence: 'inferred',\n confidenceScore: 0.7,\n metadata: {\n synthesizedBy: 'flutter-channel-bridge',\n provenance: 'heuristic',\n channelName: call.channelName,\n methodName: call.methodName ?? undefined,\n channelType: 'method',\n nativeLanguage: handler.language,\n },\n });\n } else {\n // EventChannel receiveBroadcastStream \u2192 native setStreamHandler\n edges.push({\n source: call.callerNodeId,\n target: handler.nodeId,\n kind: 'references',\n confidence: 'inferred',\n confidenceScore: 0.65,\n metadata: {\n synthesizedBy: 'flutter-channel-bridge',\n provenance: 'heuristic',\n channelName: call.channelName,\n channelType: 'event',\n nativeLanguage: handler.language,\n },\n });\n }\n }\n }\n\n return edges;\n },\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|