execbro 2.9.3 → 2.9.4

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.
Files changed (86) hide show
  1. package/README.md +12 -8
  2. package/build/__tests__/unit/passiveConnectionBanner.test.d.ts +2 -0
  3. package/build/__tests__/unit/passiveConnectionBanner.test.d.ts.map +1 -0
  4. package/build/__tests__/unit/passiveConnectionBanner.test.js +49 -0
  5. package/build/__tests__/unit/passiveConnectionBanner.test.js.map +1 -0
  6. package/build/__tests__/unit/pressablesSwitch.test.d.ts +2 -0
  7. package/build/__tests__/unit/pressablesSwitch.test.d.ts.map +1 -0
  8. package/build/__tests__/unit/pressablesSwitch.test.js +86 -0
  9. package/build/__tests__/unit/pressablesSwitch.test.js.map +1 -0
  10. package/build/__tests__/unit/reduxBatchDispatch.test.d.ts +2 -0
  11. package/build/__tests__/unit/reduxBatchDispatch.test.d.ts.map +1 -0
  12. package/build/__tests__/unit/reduxBatchDispatch.test.js +51 -0
  13. package/build/__tests__/unit/reduxBatchDispatch.test.js.map +1 -0
  14. package/build/__tests__/unit/requestDetailsInclude.test.d.ts +2 -0
  15. package/build/__tests__/unit/requestDetailsInclude.test.d.ts.map +1 -0
  16. package/build/__tests__/unit/requestDetailsInclude.test.js +46 -0
  17. package/build/__tests__/unit/requestDetailsInclude.test.js.map +1 -0
  18. package/build/__tests__/unit/screenStateSwitch.test.d.ts +2 -0
  19. package/build/__tests__/unit/screenStateSwitch.test.d.ts.map +1 -0
  20. package/build/__tests__/unit/screenStateSwitch.test.js +34 -0
  21. package/build/__tests__/unit/screenStateSwitch.test.js.map +1 -0
  22. package/build/__tests__/unit/sheetOffset.test.d.ts +2 -0
  23. package/build/__tests__/unit/sheetOffset.test.d.ts.map +1 -0
  24. package/build/__tests__/unit/sheetOffset.test.js +47 -0
  25. package/build/__tests__/unit/sheetOffset.test.js.map +1 -0
  26. package/build/core/connection.d.ts +1 -0
  27. package/build/core/connection.d.ts.map +1 -1
  28. package/build/core/connection.js +9 -0
  29. package/build/core/connection.js.map +1 -1
  30. package/build/core/guides.d.ts.map +1 -1
  31. package/build/core/guides.js +38 -3
  32. package/build/core/guides.js.map +1 -1
  33. package/build/core/index.d.ts +1 -1
  34. package/build/core/index.d.ts.map +1 -1
  35. package/build/core/index.js +1 -1
  36. package/build/core/index.js.map +1 -1
  37. package/build/core/injected/sheetOffset.d.ts +13 -0
  38. package/build/core/injected/sheetOffset.d.ts.map +1 -0
  39. package/build/core/injected/sheetOffset.js +37 -0
  40. package/build/core/injected/sheetOffset.js.map +1 -0
  41. package/build/core/inspector.d.ts.map +1 -1
  42. package/build/core/inspector.js +55 -3
  43. package/build/core/inspector.js.map +1 -1
  44. package/build/core/measureComponent.d.ts.map +1 -1
  45. package/build/core/measureComponent.js +49 -13
  46. package/build/core/measureComponent.js.map +1 -1
  47. package/build/core/network.d.ts +2 -0
  48. package/build/core/network.d.ts.map +1 -1
  49. package/build/core/network.js +23 -8
  50. package/build/core/network.js.map +1 -1
  51. package/build/core/pressables.d.ts.map +1 -1
  52. package/build/core/pressables.js +113 -10
  53. package/build/core/pressables.js.map +1 -1
  54. package/build/core/redux.d.ts +1 -1
  55. package/build/core/redux.d.ts.map +1 -1
  56. package/build/core/redux.js +4 -3
  57. package/build/core/redux.js.map +1 -1
  58. package/build/core/screenLayout.d.ts.map +1 -1
  59. package/build/core/screenLayout.js +50 -0
  60. package/build/core/screenLayout.js.map +1 -1
  61. package/build/core/screenState.d.ts +2 -0
  62. package/build/core/screenState.d.ts.map +1 -1
  63. package/build/core/screenState.js +111 -7
  64. package/build/core/screenState.js.map +1 -1
  65. package/build/core/swipeDiagnosis.d.ts.map +1 -1
  66. package/build/core/swipeDiagnosis.js +31 -2
  67. package/build/core/swipeDiagnosis.js.map +1 -1
  68. package/build/pro/tap.d.ts +14 -0
  69. package/build/pro/tap.d.ts.map +1 -1
  70. package/build/pro/tap.js +54 -1
  71. package/build/pro/tap.js.map +1 -1
  72. package/build/tools/componentTools.js +1 -1
  73. package/build/tools/componentTools.js.map +1 -1
  74. package/build/tools/interactionTools.js +1 -1
  75. package/build/tools/interactionTools.js.map +1 -1
  76. package/build/tools/logTools.d.ts.map +1 -1
  77. package/build/tools/logTools.js +3 -9
  78. package/build/tools/logTools.js.map +1 -1
  79. package/build/tools/networkTools.d.ts.map +1 -1
  80. package/build/tools/networkTools.js +11 -12
  81. package/build/tools/networkTools.js.map +1 -1
  82. package/build/tools/reduxTools.d.ts.map +1 -1
  83. package/build/tools/reduxTools.js +5 -3
  84. package/build/tools/reduxTools.js.map +1 -1
  85. package/package.json +14 -3
  86. package/tools.json +1 -0
package/README.md CHANGED
@@ -17,7 +17,7 @@ ExecBro is the runtime bridge between your AI coding assistant and your running
17
17
  1. [Setup ExecBro as an MCP server for your agent of choice](#setup)
18
18
  2. [Setup UI automation helpers](docs/setup.md#ios-simulator--ui-automation-setup)
19
19
  3. [Install the SDK in your React Native app](#install-the-sdk-recommended) — optional, but recommended for the most robust log, network, and state experience
20
- 4. [Authorize and link to the web dashboard](#see-your-usage--execbrocom) — optional for usage stats and session history, **required to unlock unlimited usage via ExecBro Pro**
20
+ 4. [Authorize and link to the web dashboard](#see-your-usage--execbrocom) — optional, for usage stats and session history
21
21
  5. Start your React Native app (`npm start` / `expo start`)
22
22
  6. [Ask your AI assistant](#usage) in plain language — e.g. "Connect to the simulator and investigate what caused the error on the screen"
23
23
 
@@ -25,7 +25,7 @@ ExecBro is the runtime bridge between your AI coding assistant and your running
25
25
 
26
26
  Log in at **[execbro.com](https://execbro.com)** to see your ExecBro activity rendered back to you: which tools you use most, tool **error rates**, and your **session history** — so you can spot flaky tools, track usage over time, and understand how your agent drives the app across sessions. It's built from the same anonymous telemetry described in [Telemetry & Privacy](#telemetry--privacy), tied to your installation ID.
27
27
 
28
- Linking your installation here is also **required to unlock [ExecBro Pro](#pricing)** — signing in and authorizing your installation is what lets the unlimited-usage plan take effect; without it, the free tier's monthly cap applies even after subscribing.
28
+ Linking is also how [ExecBro Pro](#pricing) attaches to your installation, if you ever subscribe.
29
29
 
30
30
  ## Features
31
31
 
@@ -70,11 +70,13 @@ Linking your installation here is also **required to unlock [ExecBro Pro](#prici
70
70
  Add ExecBro to Claude Code in one command — no installation, `npx` fetches the latest version on demand:
71
71
 
72
72
  ```bash
73
- claude mcp add execbro --scope user -- npx -y execbro@latest
73
+ claude mcp add execbro --scope project -- npx -y execbro@latest
74
74
  ```
75
75
 
76
76
  Then fully restart the client (quit and relaunch) so it picks up the new server.
77
77
 
78
+ Project scope writes a `.mcp.json` you can commit, so the whole team picks ExecBro up in this repo and nowhere else. Swap `project` for `user` to register it globally instead — convenient if you work in React Native all day, but it starts ExecBro in every session you open, including repos with no Metro server to connect to.
79
+
78
80
  **Using a different client or need platform setup?** The [full setup guide](docs/setup.md) covers Claude Desktop, Codex CLI, Cursor, VS Code Copilot, Windsurf, Zed, and Gemini CLI, plus [Android](docs/setup.md#android) and [iOS simulator UI automation](docs/setup.md#ios-simulator--ui-automation-setup) requirements.
79
81
 
80
82
  ## Install the SDK (recommended)
@@ -204,12 +206,14 @@ See the [full tool reference](docs/tools.md) for all tools with descriptions. Ke
204
206
 
205
207
  ## Telemetry & Privacy
206
208
 
207
- ExecBro sends two separate signals, not one:
209
+ **No source code, file paths, or app content is ever sent.** Your app and your repo stay on your machine.
210
+
211
+ ExecBro sends two small signals:
208
212
 
209
- - **Usage metering** (required, always on) — a counted tool-call signal that powers the free tier and enforces its monthly cap. This stays on whenever you're using the free tier; it cannot be disabled and still use ExecBro's free tools.
210
- - **Product analytics** (optional) — tool names, success/failure, and durations, used to improve the product. **No source code, file paths, or app content is ever sent.** This is what powers your [usage dashboard](#see-your-usage--execbrocom). Disable it with `EXECBRO_TELEMETRY=false` in your MCP server config. Doing so turns off analytics only — it does **not** disable usage metering, which keeps counting tool calls against the free-tier cap regardless.
213
+ - **Product analytics** (optional) — tool names, success/failure, and durations. This is what powers your [usage dashboard](#see-your-usage--execbrocom) and tells me which tools are flaky and worth fixing. Turn it off any time with `EXECBRO_TELEMETRY=false` in your MCP server config.
214
+ - **Usage count** — a running tally of how many tool calls you've made, so the free tier knows where you stand against its monthly allowance. It's a number, not content, and it's the one signal that keeps running when analytics is off.
211
215
 
212
- See the [Telemetry & Data Collection guide](docs/telemetry.md) for the full breakdown of what's collected, auto-registration, and how to opt out, and [PRIVACY.md](./PRIVACY.md) for the complete privacy policy.
216
+ See the [Telemetry & Data Collection guide](docs/telemetry.md) for the full field-by-field breakdown, and [PRIVACY.md](./PRIVACY.md) for the complete privacy policy.
213
217
 
214
218
  ## Supported React Native Versions
215
219
 
@@ -223,7 +227,7 @@ See the [Telemetry & Data Collection guide](docs/telemetry.md) for the full brea
223
227
 
224
228
  ## Pricing
225
229
 
226
- ExecBro is **free to use** — every feature, fully intelligent — up to **600 tool calls per month**. That covers evaluation and everyday debugging. Heavy users can remove the cap with **ExecBro Pro** ($8.99/mo) for unlimited calls and every future hosted feature. See [execbro.com/pricing](https://execbro.com/pricing).
230
+ ExecBro is **free** — every tool, full power, no feature gates, no account needed to start. The free tier covers **600 tool calls a month**, which is evaluation plus everyday debugging; most people never reach it. If you drive your agent hard all day, **ExecBro Pro** ($8.99/mo) lifts the cap and includes every hosted feature as it ships. See [execbro.com/pricing](https://execbro.com/pricing).
227
231
 
228
232
  > [!NOTE]
229
233
  > **Grandfather notice:** the 600-call monthly cap starts applying **2026-08-01** for new installs. If you already had ExecBro installed before then, you get a free month on top — your cap doesn't start until **2026-08-31**. You'll also see this as an in-app notice. No action needed unless you want to [link your account and upgrade to Pro](#see-your-usage--execbrocom) ahead of time. Questions? [zigor535@gmail.com](mailto:zigor535@gmail.com).
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=passiveConnectionBanner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passiveConnectionBanner.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/passiveConnectionBanner.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
2
+ import { connectedApps, clearAllCDPMessageTimes, updateLastCDPMessageTime } from "../../core/state.js";
3
+ import { markContextStale, initContextHealth, clearContextHealth } from "../../core/connectionState.js";
4
+ import { passiveConnectionBanner } from "../../core/connection.js";
5
+ const APP_KEY = "8081-device1";
6
+ function connect() {
7
+ connectedApps.set(APP_KEY, {
8
+ ws: { readyState: 1 },
9
+ port: 8081,
10
+ platform: "ios",
11
+ deviceInfo: { id: "device1", title: "iPhone", deviceName: "iPhone" }
12
+ });
13
+ initContextHealth(APP_KEY);
14
+ }
15
+ describe("passiveConnectionBanner", () => {
16
+ beforeEach(() => {
17
+ connectedApps.clear();
18
+ clearAllCDPMessageTimes();
19
+ clearContextHealth(APP_KEY);
20
+ });
21
+ afterEach(() => {
22
+ connectedApps.clear();
23
+ clearAllCDPMessageTimes();
24
+ });
25
+ it("says nothing while the connection is healthy", () => {
26
+ connect();
27
+ updateLastCDPMessageTime(APP_KEY, new Date());
28
+ expect(passiveConnectionBanner()).toBe("");
29
+ });
30
+ it("reports a genuinely absent socket as disconnected", () => {
31
+ expect(passiveConnectionBanner()).toContain("Disconnected");
32
+ });
33
+ it("does not claim disconnection from an inferred stale context", () => {
34
+ connect();
35
+ updateLastCDPMessageTime(APP_KEY, new Date());
36
+ markContextStale(APP_KEY);
37
+ const banner = passiveConnectionBanner();
38
+ expect(banner).not.toContain("Disconnected");
39
+ expect(banner).toContain("Status unknown");
40
+ expect(banner).toContain("ensure_connection");
41
+ });
42
+ it("does not claim disconnection merely because no CDP traffic was recorded", () => {
43
+ connect();
44
+ const banner = passiveConnectionBanner();
45
+ expect(banner).not.toContain("Disconnected");
46
+ expect(banner).toContain("no_activity");
47
+ });
48
+ });
49
+ //# sourceMappingURL=passiveConnectionBanner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passiveConnectionBanner.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/passiveConnectionBanner.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACvG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxG,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAWnE,MAAM,OAAO,GAAG,cAAc,CAAC;AAE/B,SAAS,OAAO;IACZ,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE;QACvB,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE;QACrB,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE;KAC5C,CAAC,CAAC;IAC9B,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACrC,UAAU,CAAC,GAAG,EAAE;QACZ,aAAa,CAAC,KAAK,EAAE,CAAC;QACtB,uBAAuB,EAAE,CAAC;QAC1B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACX,aAAa,CAAC,KAAK,EAAE,CAAC;QACtB,uBAAuB,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACpD,OAAO,EAAE,CAAC;QACV,wBAAwB,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,uBAAuB,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,uBAAuB,EAAE,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACnE,OAAO,EAAE,CAAC;QACV,wBAAwB,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAE1B,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QAC/E,OAAO,EAAE,CAAC;QAEV,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pressablesSwitch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pressablesSwitch.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/pressablesSwitch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,86 @@
1
+ import { describe, it, expect, jest, beforeEach } from "@jest/globals";
2
+ const evaluated = [];
3
+ let evalResult = null;
4
+ jest.unstable_mockModule("../../core/jsExecute.js", () => ({
5
+ executeInApp: async (expression) => {
6
+ evaluated.push(expression);
7
+ return { success: true, result: JSON.stringify(evalResult) };
8
+ },
9
+ delay: async () => { }
10
+ }));
11
+ const { pressElement } = await import("../../core/pressables.js");
12
+ function fiber(props, type = "View", children = []) {
13
+ const node = {
14
+ type,
15
+ memoizedProps: props,
16
+ stateNode: { measureInWindow: (cb) => cb(10, 20, 100, 50) },
17
+ child: null,
18
+ sibling: null,
19
+ return: null
20
+ };
21
+ let prev = null;
22
+ for (const c of children) {
23
+ c.return = node;
24
+ if (prev)
25
+ prev.sibling = c;
26
+ else
27
+ node.child = c;
28
+ prev = c;
29
+ }
30
+ return node;
31
+ }
32
+ async function search(options, root) {
33
+ evaluated.length = 0;
34
+ evalResult = { dispatched: 1 };
35
+ await pressElement(options);
36
+ const globalThisStub = {
37
+ __REACT_DEVTOOLS_GLOBAL_HOOK__: {
38
+ getFiberRoots: () => new Set([{ current: root }]),
39
+ renderers: new Map()
40
+ }
41
+ };
42
+ let last = null;
43
+ for (const expression of evaluated) {
44
+ const fn = new Function("globalThis", `return (${expression.trim()});`);
45
+ last = fn(globalThisStub);
46
+ }
47
+ return typeof last === "string" ? JSON.parse(last) : last;
48
+ }
49
+ describe("fiber search and Switch elements", () => {
50
+ beforeEach(() => {
51
+ evaluated.length = 0;
52
+ });
53
+ const switchTree = (value) => fiber({}, "View", [
54
+ fiber({ children: "Push notifications" }, "Text"),
55
+ fiber({ testID: "settings-push", onValueChange: () => { }, value }, "Switch", [
56
+ fiber({}, "RCTSwitch")
57
+ ])
58
+ ]);
59
+ it("resolves a switch by testID even though it has no onPress", async () => {
60
+ const out = await search({ testID: "settings-push" }, switchTree(false));
61
+ expect(out.needsNativeTap).toBe(true);
62
+ expect(out.switchValue).toBe(false);
63
+ });
64
+ it("reports the value the caller is about to change", async () => {
65
+ const out = await search({ testID: "settings-push" }, switchTree(true));
66
+ expect(out.switchValue).toBe(true);
67
+ });
68
+ it("resolves a Switch that carries no handler at all", async () => {
69
+ const root = fiber({}, "View", [
70
+ fiber({ children: "Sponsor" }, "Text"),
71
+ fiber({ value: false }, { name: "Switch" }, [fiber({}, "RCTSwitch")])
72
+ ]);
73
+ const out = await search({ component: "Switch" }, root);
74
+ expect(out.needsNativeTap).toBe(true);
75
+ expect(out.switchValue).toBe(false);
76
+ });
77
+ it("leaves switchValue null for an ordinary button", async () => {
78
+ const root = fiber({}, "View", [
79
+ fiber({ testID: "save", onPress: () => { } }, "View", [fiber({ children: "Save" }, "Text")])
80
+ ]);
81
+ const out = await search({ testID: "save" }, root);
82
+ expect(out.needsNativeTap).toBe(true);
83
+ expect(out.switchValue).toBeNull();
84
+ });
85
+ });
86
+ //# sourceMappingURL=pressablesSwitch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pressablesSwitch.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/pressablesSwitch.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=reduxBatchDispatch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduxBatchDispatch.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/reduxBatchDispatch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import { describe, it, expect, jest } from "@jest/globals";
2
+ const evaluated = [];
3
+ jest.unstable_mockModule("../../core/executor.js", () => ({
4
+ executeInApp: async (expression) => {
5
+ evaluated.push(expression);
6
+ return { success: true, result: run(expression) };
7
+ }
8
+ }));
9
+ const dispatched = [];
10
+ const store = {
11
+ dispatch: (a) => dispatched.push(a),
12
+ getState: () => ({ settings: { a: 1 } }),
13
+ subscribe: () => { }
14
+ };
15
+ function run(expression) {
16
+ const globalThisStub = {
17
+ __REACT_DEVTOOLS_GLOBAL_HOOK__: {
18
+ renderers: new Map([[1, {}]]),
19
+ getFiberRoots: () => new Set([{ current: { type: { name: "Provider" }, memoizedProps: { store } } }])
20
+ }
21
+ };
22
+ return new Function("globalThis", `return (${expression.trim()});`)(globalThisStub);
23
+ }
24
+ const { reduxDispatch } = await import("../../core/redux.js");
25
+ describe("redux_dispatch batching", () => {
26
+ it("dispatches an array in order in a single round trip", async () => {
27
+ dispatched.length = 0;
28
+ evaluated.length = 0;
29
+ const result = await reduxDispatch({
30
+ action: [
31
+ { type: "settings/setA", payload: false },
32
+ { type: "settings/setB", payload: true }
33
+ ],
34
+ returnPath: "settings"
35
+ });
36
+ expect(evaluated).toHaveLength(1);
37
+ expect(dispatched).toEqual([
38
+ { type: "settings/setA", payload: false },
39
+ { type: "settings/setB", payload: true }
40
+ ]);
41
+ expect(result.success).toBe(true);
42
+ expect(result.state).toEqual({ a: 1 });
43
+ });
44
+ it("still accepts a single action object", async () => {
45
+ dispatched.length = 0;
46
+ const result = await reduxDispatch({ action: { type: "app/setIsLoading", payload: true } });
47
+ expect(dispatched).toEqual([{ type: "app/setIsLoading", payload: true }]);
48
+ expect(result.previousAction).toEqual({ type: "app/setIsLoading", payload: true });
49
+ });
50
+ });
51
+ //# sourceMappingURL=reduxBatchDispatch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduxBatchDispatch.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/reduxBatchDispatch.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=requestDetailsInclude.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestDetailsInclude.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/requestDetailsInclude.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { formatRequestDetails, redactHeaderValue } from "../../core/network.js";
3
+ const JWT = "Bearer " + "e".repeat(1500);
4
+ function request(overrides = {}) {
5
+ return {
6
+ requestId: "js-1",
7
+ url: "https://api.example.com/graphql",
8
+ method: "POST",
9
+ headers: { authorization: JWT, accept: "application/json" },
10
+ postData: JSON.stringify({ query: "query GetThings { things { id } }" }),
11
+ responseBody: JSON.stringify({ data: { things: [{ id: "t1" }] } }),
12
+ timestamp: new Date("2026-08-25T10:00:00Z"),
13
+ epoch: 1,
14
+ completed: true,
15
+ status: 200,
16
+ ...overrides
17
+ };
18
+ }
19
+ describe("get_request_details request/response split", () => {
20
+ it("keeps a live token out of the transcript unless verbose asks for it", () => {
21
+ const out = formatRequestDetails(request(), { include: "request" });
22
+ expect(out).not.toContain(JWT);
23
+ expect(out).toContain("Bearer [redacted, 1507 chars");
24
+ expect(formatRequestDetails(request(), { include: "request", verbose: true })).toContain(JWT);
25
+ expect(redactHeaderValue("accept", "application/json")).toBe("application/json");
26
+ });
27
+ it("renders only the queried side, so narrowing does not re-dump the request", () => {
28
+ const out = formatRequestDetails(request(), { query: "data.things[0].id" });
29
+ expect(out).toContain("t1");
30
+ expect(out).not.toContain("Request Headers");
31
+ expect(out).not.toContain("Request Body");
32
+ expect(out).not.toContain("Response Headers");
33
+ expect(out).toContain('include:"both"');
34
+ });
35
+ it("still shows both sides when there is no query", () => {
36
+ const out = formatRequestDetails(request());
37
+ expect(out).toContain("Request Headers");
38
+ expect(out).toContain("Response Body");
39
+ });
40
+ it("honours an explicit include over the query default", () => {
41
+ const out = formatRequestDetails(request(), { query: "data.things[0].id", include: "both" });
42
+ expect(out).toContain("Request Headers");
43
+ expect(out).toContain("t1");
44
+ });
45
+ });
46
+ //# sourceMappingURL=requestDetailsInclude.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestDetailsInclude.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/requestDetailsInclude.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=screenStateSwitch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenStateSwitch.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/screenStateSwitch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { formatScreenStateSummary } from "../../core/screenState.js";
3
+ function toggle(over = {}) {
4
+ return {
5
+ label: "Push notifications",
6
+ component: "Switch",
7
+ center: { x: 775, y: 320 },
8
+ bounds: { x: 745, y: 305, width: 60, height: 30 },
9
+ testID: "settings-push",
10
+ ...over
11
+ };
12
+ }
13
+ const screen = (pressables) => ({ route: null, overlays: [], pressables, texts: [], images: [] });
14
+ describe("get_screen_state switch reporting", () => {
15
+ it("lists a switch with its current value and a tappable testID", () => {
16
+ const out = formatScreenStateSummary(screen([toggle({ switchValue: true })]), undefined, {
17
+ pressablesOnly: true
18
+ });
19
+ expect(out).toContain("[switch:ON]");
20
+ expect(out).toContain('testID="settings-push"');
21
+ expect(out).toContain("(775, 320)");
22
+ });
23
+ it("distinguishes off from on", () => {
24
+ const out = formatScreenStateSummary(screen([toggle({ switchValue: false })]), undefined, {
25
+ pressablesOnly: true
26
+ });
27
+ expect(out).toContain("[switch:OFF]");
28
+ });
29
+ it("says nothing about elements that are not switches", () => {
30
+ const out = formatScreenStateSummary(screen([toggle({ component: "Button", switchValue: undefined })]), undefined, { pressablesOnly: true });
31
+ expect(out).not.toContain("[switch:");
32
+ });
33
+ });
34
+ //# sourceMappingURL=screenStateSwitch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenStateSwitch.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/screenStateSwitch.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAA+C,MAAM,2BAA2B,CAAC;AAQlH,SAAS,MAAM,CAAC,OAAsC,EAAE;IACpD,OAAO;QACH,KAAK,EAAE,oBAAoB;QAC3B,SAAS,EAAE,QAAQ;QACnB,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;QAC1B,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QACjD,MAAM,EAAE,eAAe;QACvB,GAAG,IAAI;KACV,CAAC;AACN,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,UAAkC,EAAe,EAAE,CAC/D,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;AAEvE,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACnE,MAAM,GAAG,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE;YACrF,cAAc,EAAE,IAAI;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACjC,MAAM,GAAG,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE;YACtF,cAAc,EAAE,IAAI;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QACzD,MAAM,GAAG,GAAG,wBAAwB,CAChC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EACjE,SAAS,EACT,EAAE,cAAc,EAAE,IAAI,EAAE,CAC3B,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sheetOffset.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheetOffset.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/sheetOffset.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { sheetShiftY, SHEET_HELPERS_JS } from "../../core/injected/sheetOffset.js";
3
+ const WINDOW = { width: 420, height: 912 };
4
+ describe("sheetShiftY", () => {
5
+ it("derives the measured inset from the sheet's own height", () => {
6
+ expect(sheetShiftY({ x: 0, y: 0, width: 420, height: 844 }, WINDOW)).toBe(68);
7
+ });
8
+ it("is zero for a full-screen presentation", () => {
9
+ expect(sheetShiftY({ x: 0, y: 0, width: 420, height: 912 }, WINDOW)).toBe(0);
10
+ });
11
+ it("is zero when the platform already reports absolute coordinates", () => {
12
+ expect(sheetShiftY({ x: 0, y: 68, width: 420, height: 844 }, WINDOW)).toBe(0);
13
+ });
14
+ it("scales with a detented half sheet, which is still bottom-anchored", () => {
15
+ expect(sheetShiftY({ x: 0, y: 0, width: 420, height: 456 }, WINDOW)).toBe(456);
16
+ });
17
+ it("refuses to guess when the sheet is not full-width", () => {
18
+ expect(sheetShiftY({ x: 60, y: 0, width: 300, height: 700 }, WINDOW)).toBe(0);
19
+ });
20
+ it("returns 0 rather than throwing on missing or degenerate input", () => {
21
+ expect(sheetShiftY(null, WINDOW)).toBe(0);
22
+ expect(sheetShiftY(undefined, WINDOW)).toBe(0);
23
+ expect(sheetShiftY({ x: 0, y: 0, width: 0, height: 0 }, WINDOW)).toBe(0);
24
+ expect(sheetShiftY({ x: 0, y: 0, width: 420, height: 844 }, null)).toBe(0);
25
+ expect(sheetShiftY({ x: 0, y: 0, width: 420, height: 1000 }, WINDOW)).toBe(0);
26
+ });
27
+ });
28
+ describe("SHEET_HELPERS_JS", () => {
29
+ it("evaluates to the same function the Node side uses", () => {
30
+ const fn = new Function(`${SHEET_HELPERS_JS} return sheetShiftY;`)();
31
+ expect(fn({ x: 0, y: 0, width: 420, height: 844 }, WINDOW)).toBe(68);
32
+ expect(fn({ x: 0, y: 0, width: 420, height: 912 }, WINDOW)).toBe(0);
33
+ });
34
+ it("walks up to the modal boundary and stops at ordinary hosts", () => {
35
+ const ctx = new Function(`${SHEET_HELPERS_JS} return { modalBoundaryOf: modalBoundaryOf, shiftRect: shiftRect };`)();
36
+ const sheet = { type: "RNSModalScreen", memoizedProps: {}, return: null };
37
+ const view = { type: "RCTView", memoizedProps: {}, return: sheet };
38
+ const leaf = { type: "RCTSwitch", memoizedProps: {}, return: view };
39
+ expect(ctx.modalBoundaryOf(leaf)).toBe(sheet);
40
+ const pushed = { type: "RNSScreen", memoizedProps: { stackPresentation: "push" }, return: null };
41
+ expect(ctx.modalBoundaryOf({ type: "RCTView", memoizedProps: {}, return: pushed })).toBeNull();
42
+ const legacy = { type: "RNSScreen", memoizedProps: { stackPresentation: "formSheet" }, return: null };
43
+ expect(ctx.modalBoundaryOf({ type: "RCTView", memoizedProps: {}, return: legacy })).toBe(legacy);
44
+ expect(ctx.shiftRect({ x: 1, y: 10, width: 2, height: 3 }, 68).y).toBe(78);
45
+ });
46
+ });
47
+ //# sourceMappingURL=sheetOffset.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheetOffset.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/sheetOffset.test.ts"],"names":[],"mappings":"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"}
@@ -59,5 +59,6 @@ export interface PassiveConnectionStatus {
59
59
  reason: "ok" | "no_connection" | "context_stale" | "no_activity" | "activity_stale";
60
60
  }
61
61
  export declare function getPassiveConnectionStatus(targetAppKey?: string): PassiveConnectionStatus;
62
+ export declare function passiveConnectionBanner(targetAppKey?: string): string;
62
63
  export declare function checkAndEnsureConnection(device?: string): Promise<ConnectionCheckResult>;
63
64
  //# sourceMappingURL=connection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/core/connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/core/connection.ts"],"names":[],"mappings":"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"}
@@ -1502,6 +1502,15 @@ export function getPassiveConnectionStatus(targetAppKey) {
1502
1502
  }
1503
1503
  return { connected: true, needsPing: false, reason: "ok" };
1504
1504
  }
1505
+ export function passiveConnectionBanner(targetAppKey) {
1506
+ const passive = getPassiveConnectionStatus(targetAppKey);
1507
+ if (passive.connected)
1508
+ return "";
1509
+ if (passive.reason === "no_connection") {
1510
+ return "\n\n[CONNECTION] Disconnected. Showing cached data. New data is not being captured.";
1511
+ }
1512
+ return `\n\n[CONNECTION] Status unknown for this read (${passive.reason}) — inferred, not measured. The data above is real; if you suspect capture stopped, call ensure_connection({healthCheck:true}) rather than scan_metro.`;
1513
+ }
1505
1514
  export async function checkAndEnsureConnection(device) {
1506
1515
  let targetAppKey;
1507
1516
  let targetApp = null;