execbro 1.11.0 → 2.0.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.
Files changed (250) hide show
  1. package/README.md +4 -4
  2. package/build/__tests__/unit/appContext.test.d.ts +2 -0
  3. package/build/__tests__/unit/appContext.test.d.ts.map +1 -0
  4. package/build/__tests__/unit/appContext.test.js +70 -0
  5. package/build/__tests__/unit/appContext.test.js.map +1 -0
  6. package/build/__tests__/unit/appRequest.test.d.ts +2 -0
  7. package/build/__tests__/unit/appRequest.test.d.ts.map +1 -0
  8. package/build/__tests__/unit/appRequest.test.js +179 -0
  9. package/build/__tests__/unit/appRequest.test.js.map +1 -0
  10. package/build/__tests__/unit/contextWiring.test.d.ts +2 -0
  11. package/build/__tests__/unit/contextWiring.test.d.ts.map +1 -0
  12. package/build/__tests__/unit/contextWiring.test.js +28 -0
  13. package/build/__tests__/unit/contextWiring.test.js.map +1 -0
  14. package/build/__tests__/unit/epochRender.test.d.ts +2 -0
  15. package/build/__tests__/unit/epochRender.test.d.ts.map +1 -0
  16. package/build/__tests__/unit/epochRender.test.js +60 -0
  17. package/build/__tests__/unit/epochRender.test.js.map +1 -0
  18. package/build/__tests__/unit/executeBudget.test.d.ts +2 -0
  19. package/build/__tests__/unit/executeBudget.test.d.ts.map +1 -0
  20. package/build/__tests__/unit/executeBudget.test.js +27 -0
  21. package/build/__tests__/unit/executeBudget.test.js.map +1 -0
  22. package/build/__tests__/unit/executor.test.js +48 -6
  23. package/build/__tests__/unit/executor.test.js.map +1 -1
  24. package/build/__tests__/unit/getLogsEmptyContract.test.js +4 -3
  25. package/build/__tests__/unit/getLogsEmptyContract.test.js.map +1 -1
  26. package/build/__tests__/unit/getLogsNativeSource.test.js +7 -8
  27. package/build/__tests__/unit/getLogsNativeSource.test.js.map +1 -1
  28. package/build/__tests__/unit/inspectorHarvest.test.js +1 -21
  29. package/build/__tests__/unit/inspectorHarvest.test.js.map +1 -1
  30. package/build/__tests__/unit/inspectorSource.test.js +1 -38
  31. package/build/__tests__/unit/inspectorSource.test.js.map +1 -1
  32. package/build/__tests__/unit/jsLogEvents.test.js +1 -0
  33. package/build/__tests__/unit/jsLogEvents.test.js.map +1 -1
  34. package/build/__tests__/unit/logBufferEpoch.test.d.ts +2 -0
  35. package/build/__tests__/unit/logBufferEpoch.test.d.ts.map +1 -0
  36. package/build/__tests__/unit/logBufferEpoch.test.js +63 -0
  37. package/build/__tests__/unit/logBufferEpoch.test.js.map +1 -0
  38. package/build/__tests__/unit/metro.test.js +18 -1
  39. package/build/__tests__/unit/metro.test.js.map +1 -1
  40. package/build/__tests__/unit/moduleRegistry.test.d.ts +2 -0
  41. package/build/__tests__/unit/moduleRegistry.test.d.ts.map +1 -0
  42. package/build/__tests__/unit/moduleRegistry.test.js +62 -0
  43. package/build/__tests__/unit/moduleRegistry.test.js.map +1 -0
  44. package/build/__tests__/unit/navigateVerify.test.d.ts +2 -0
  45. package/build/__tests__/unit/navigateVerify.test.d.ts.map +1 -0
  46. package/build/__tests__/unit/navigateVerify.test.js +30 -0
  47. package/build/__tests__/unit/navigateVerify.test.js.map +1 -0
  48. package/build/__tests__/unit/navigation.test.d.ts +2 -0
  49. package/build/__tests__/unit/navigation.test.d.ts.map +1 -0
  50. package/build/__tests__/unit/navigation.test.js +254 -0
  51. package/build/__tests__/unit/navigation.test.js.map +1 -0
  52. package/build/__tests__/unit/network.test.js +1 -0
  53. package/build/__tests__/unit/network.test.js.map +1 -1
  54. package/build/__tests__/unit/networkBufferEpoch.test.d.ts +2 -0
  55. package/build/__tests__/unit/networkBufferEpoch.test.d.ts.map +1 -0
  56. package/build/__tests__/unit/networkBufferEpoch.test.js +52 -0
  57. package/build/__tests__/unit/networkBufferEpoch.test.js.map +1 -0
  58. package/build/__tests__/unit/networkInterceptor.test.js +7 -7
  59. package/build/__tests__/unit/networkInterceptor.test.js.map +1 -1
  60. package/build/__tests__/unit/promiseHandles.test.d.ts +2 -0
  61. package/build/__tests__/unit/promiseHandles.test.d.ts.map +1 -0
  62. package/build/__tests__/unit/promiseHandles.test.js +61 -0
  63. package/build/__tests__/unit/promiseHandles.test.js.map +1 -0
  64. package/build/__tests__/unit/resolveBufferOffline.test.d.ts +2 -0
  65. package/build/__tests__/unit/resolveBufferOffline.test.d.ts.map +1 -0
  66. package/build/__tests__/unit/resolveBufferOffline.test.js +25 -0
  67. package/build/__tests__/unit/resolveBufferOffline.test.js.map +1 -0
  68. package/build/__tests__/unit/sdkMirrorPoller.test.d.ts +2 -0
  69. package/build/__tests__/unit/sdkMirrorPoller.test.d.ts.map +1 -0
  70. package/build/__tests__/unit/sdkMirrorPoller.test.js +118 -0
  71. package/build/__tests__/unit/sdkMirrorPoller.test.js.map +1 -0
  72. package/build/__tests__/unit/sessionEpoch.test.d.ts +2 -0
  73. package/build/__tests__/unit/sessionEpoch.test.d.ts.map +1 -0
  74. package/build/__tests__/unit/sessionEpoch.test.js +41 -0
  75. package/build/__tests__/unit/sessionEpoch.test.js.map +1 -0
  76. package/build/__tests__/unit/truncate.test.d.ts +2 -0
  77. package/build/__tests__/unit/truncate.test.d.ts.map +1 -0
  78. package/build/__tests__/unit/truncate.test.js +58 -0
  79. package/build/__tests__/unit/truncate.test.js.map +1 -0
  80. package/build/core/appContext.d.ts +53 -0
  81. package/build/core/appContext.d.ts.map +1 -0
  82. package/build/core/appContext.js +172 -0
  83. package/build/core/appContext.js.map +1 -0
  84. package/build/core/appRequest.d.ts +21 -0
  85. package/build/core/appRequest.d.ts.map +1 -0
  86. package/build/core/appRequest.js +95 -0
  87. package/build/core/appRequest.js.map +1 -0
  88. package/build/core/bufferConfig.d.ts +5 -0
  89. package/build/core/bufferConfig.d.ts.map +1 -0
  90. package/build/core/bufferConfig.js +20 -0
  91. package/build/core/bufferConfig.js.map +1 -0
  92. package/build/core/connection.d.ts.map +1 -1
  93. package/build/core/connection.js +29 -9
  94. package/build/core/connection.js.map +1 -1
  95. package/build/core/debugGlobals.d.ts.map +1 -1
  96. package/build/core/debugGlobals.js +13 -0
  97. package/build/core/debugGlobals.js.map +1 -1
  98. package/build/core/epochRender.d.ts +19 -0
  99. package/build/core/epochRender.d.ts.map +1 -0
  100. package/build/core/epochRender.js +38 -0
  101. package/build/core/epochRender.js.map +1 -0
  102. package/build/core/guides.d.ts.map +1 -1
  103. package/build/core/guides.js +36 -91
  104. package/build/core/guides.js.map +1 -1
  105. package/build/core/index.d.ts +2 -3
  106. package/build/core/index.d.ts.map +1 -1
  107. package/build/core/index.js +4 -4
  108. package/build/core/index.js.map +1 -1
  109. package/build/core/inspector.d.ts +0 -39
  110. package/build/core/inspector.d.ts.map +1 -1
  111. package/build/core/inspector.js +1 -358
  112. package/build/core/inspector.js.map +1 -1
  113. package/build/core/ios.js +2 -2
  114. package/build/core/ios.js.map +1 -1
  115. package/build/core/jsExecute.d.ts +15 -0
  116. package/build/core/jsExecute.d.ts.map +1 -1
  117. package/build/core/jsExecute.js +136 -29
  118. package/build/core/jsExecute.js.map +1 -1
  119. package/build/core/logs.d.ts +12 -3
  120. package/build/core/logs.d.ts.map +1 -1
  121. package/build/core/logs.js +28 -5
  122. package/build/core/logs.js.map +1 -1
  123. package/build/core/metro.d.ts +2 -0
  124. package/build/core/metro.d.ts.map +1 -1
  125. package/build/core/metro.js +16 -8
  126. package/build/core/metro.js.map +1 -1
  127. package/build/core/moduleRegistry.d.ts +19 -0
  128. package/build/core/moduleRegistry.d.ts.map +1 -0
  129. package/build/core/moduleRegistry.js +67 -0
  130. package/build/core/moduleRegistry.js.map +1 -0
  131. package/build/core/nativeOnlyHints.d.ts.map +1 -1
  132. package/build/core/nativeOnlyHints.js +0 -2
  133. package/build/core/nativeOnlyHints.js.map +1 -1
  134. package/build/core/navigate.d.ts +46 -0
  135. package/build/core/navigate.d.ts.map +1 -0
  136. package/build/core/navigate.js +119 -0
  137. package/build/core/navigate.js.map +1 -0
  138. package/build/core/navigation.d.ts +66 -0
  139. package/build/core/navigation.d.ts.map +1 -0
  140. package/build/core/navigation.js +269 -0
  141. package/build/core/navigation.js.map +1 -0
  142. package/build/core/network.d.ts +12 -2
  143. package/build/core/network.d.ts.map +1 -1
  144. package/build/core/network.js +52 -12
  145. package/build/core/network.js.map +1 -1
  146. package/build/core/networkInterceptor.d.ts +1 -1
  147. package/build/core/networkInterceptor.d.ts.map +1 -1
  148. package/build/core/networkInterceptor.js +3 -2
  149. package/build/core/networkInterceptor.js.map +1 -1
  150. package/build/core/promiseHandles.d.ts +19 -0
  151. package/build/core/promiseHandles.d.ts.map +1 -0
  152. package/build/core/promiseHandles.js +58 -0
  153. package/build/core/promiseHandles.js.map +1 -0
  154. package/build/core/register.d.ts.map +1 -1
  155. package/build/core/register.js +1 -4
  156. package/build/core/register.js.map +1 -1
  157. package/build/core/sdkMirrorPoller.d.ts +31 -0
  158. package/build/core/sdkMirrorPoller.d.ts.map +1 -0
  159. package/build/core/sdkMirrorPoller.js +210 -0
  160. package/build/core/sdkMirrorPoller.js.map +1 -0
  161. package/build/core/state.d.ts +14 -0
  162. package/build/core/state.d.ts.map +1 -1
  163. package/build/core/state.js +48 -2
  164. package/build/core/state.js.map +1 -1
  165. package/build/core/toolHelpers.d.ts.map +1 -1
  166. package/build/core/toolHelpers.js +42 -10
  167. package/build/core/toolHelpers.js.map +1 -1
  168. package/build/core/truncate.d.ts +42 -0
  169. package/build/core/truncate.d.ts.map +1 -0
  170. package/build/core/truncate.js +169 -0
  171. package/build/core/truncate.js.map +1 -0
  172. package/build/core/types.d.ts +3 -0
  173. package/build/core/types.d.ts.map +1 -1
  174. package/build/index.d.ts.map +1 -1
  175. package/build/index.js +4 -2
  176. package/build/index.js.map +1 -1
  177. package/build/tools/_deviceArg.d.ts +17 -1
  178. package/build/tools/_deviceArg.d.ts.map +1 -1
  179. package/build/tools/_deviceArg.js +17 -1
  180. package/build/tools/_deviceArg.js.map +1 -1
  181. package/build/tools/bundleTools.d.ts.map +1 -1
  182. package/build/tools/bundleTools.js +20 -33
  183. package/build/tools/bundleTools.js.map +1 -1
  184. package/build/tools/componentTools.d.ts.map +1 -1
  185. package/build/tools/componentTools.js +41 -219
  186. package/build/tools/componentTools.js.map +1 -1
  187. package/build/tools/connectionTools.d.ts.map +1 -1
  188. package/build/tools/connectionTools.js +17 -22
  189. package/build/tools/connectionTools.js.map +1 -1
  190. package/build/tools/deviceTools.d.ts.map +1 -1
  191. package/build/tools/deviceTools.js +13 -167
  192. package/build/tools/deviceTools.js.map +1 -1
  193. package/build/tools/executionTools.d.ts.map +1 -1
  194. package/build/tools/executionTools.js +63 -18
  195. package/build/tools/executionTools.js.map +1 -1
  196. package/build/tools/interactionTools.d.ts.map +1 -1
  197. package/build/tools/interactionTools.js +13 -45
  198. package/build/tools/interactionTools.js.map +1 -1
  199. package/build/tools/logTools.d.ts.map +1 -1
  200. package/build/tools/logTools.js +61 -80
  201. package/build/tools/logTools.js.map +1 -1
  202. package/build/tools/metaTools.js +1 -1
  203. package/build/tools/metaTools.js.map +1 -1
  204. package/build/tools/navigationTools.d.ts +3 -0
  205. package/build/tools/navigationTools.d.ts.map +1 -0
  206. package/build/tools/navigationTools.js +60 -0
  207. package/build/tools/navigationTools.js.map +1 -0
  208. package/build/tools/networkTools.d.ts.map +1 -1
  209. package/build/tools/networkTools.js +44 -202
  210. package/build/tools/networkTools.js.map +1 -1
  211. package/build/tools/reduxTools.d.ts.map +1 -1
  212. package/build/tools/reduxTools.js +3 -2
  213. package/build/tools/reduxTools.js.map +1 -1
  214. package/build/tools/requestTools.d.ts +3 -0
  215. package/build/tools/requestTools.d.ts.map +1 -0
  216. package/build/tools/requestTools.js +79 -0
  217. package/build/tools/requestTools.js.map +1 -0
  218. package/build/tools/screenshotTools.d.ts.map +1 -1
  219. package/build/tools/screenshotTools.js +9 -13
  220. package/build/tools/screenshotTools.js.map +1 -1
  221. package/package.json +2 -1
  222. package/tools.json +22 -78
  223. package/build/__tests__/unit/flowpoints.test.d.ts +0 -2
  224. package/build/__tests__/unit/flowpoints.test.d.ts.map +0 -1
  225. package/build/__tests__/unit/flowpoints.test.js +0 -126
  226. package/build/__tests__/unit/flowpoints.test.js.map +0 -1
  227. package/build/__tests__/unit/flowpointsQuery.test.d.ts +0 -2
  228. package/build/__tests__/unit/flowpointsQuery.test.d.ts.map +0 -1
  229. package/build/__tests__/unit/flowpointsQuery.test.js +0 -91
  230. package/build/__tests__/unit/flowpointsQuery.test.js.map +0 -1
  231. package/build/__tests__/unit/selectionPoller.test.d.ts +0 -2
  232. package/build/__tests__/unit/selectionPoller.test.d.ts.map +0 -1
  233. package/build/__tests__/unit/selectionPoller.test.js +0 -109
  234. package/build/__tests__/unit/selectionPoller.test.js.map +0 -1
  235. package/build/__tests__/unit/verifyFlow.test.d.ts +0 -2
  236. package/build/__tests__/unit/verifyFlow.test.d.ts.map +0 -1
  237. package/build/__tests__/unit/verifyFlow.test.js +0 -45
  238. package/build/__tests__/unit/verifyFlow.test.js.map +0 -1
  239. package/build/core/selectionPoller.d.ts +0 -13
  240. package/build/core/selectionPoller.d.ts.map +0 -1
  241. package/build/core/selectionPoller.js +0 -102
  242. package/build/core/selectionPoller.js.map +0 -1
  243. package/build/pro/flowpointTools.d.ts +0 -4
  244. package/build/pro/flowpointTools.d.ts.map +0 -1
  245. package/build/pro/flowpointTools.js +0 -266
  246. package/build/pro/flowpointTools.js.map +0 -1
  247. package/build/pro/flowpoints.d.ts +0 -86
  248. package/build/pro/flowpoints.d.ts.map +0 -1
  249. package/build/pro/flowpoints.js +0 -283
  250. package/build/pro/flowpoints.js.map +0 -1
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # ExecBro
2
2
 
3
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-io.github.igorzheludkov%2Fexecbro-6E56CF)](https://registry.modelcontextprotocol.io/?q=io.github.igorzheludkov%2Fexecbro)
4
+
3
5
  **Give your AI assistant eyes and hands into your running React Native app.** Like Chrome DevTools — but for AI agents.
4
6
 
5
7
  Build, debug, and verify features end-to-end — without leaving the chat.
@@ -42,7 +44,7 @@ Linking your installation here is also **required to unlock [ExecBro Pro](#prici
42
44
  - **Android Devices** - Screenshots, app install/launch, package management (via ADB)
43
45
  - **Unified Tap** - Single `tap` tool with automatic fallback chain: fiber tree → accessibility → OCR → coordinates. Auto-detects platform, accepts pixels from screenshots. Returns post-tap screenshot and verifies visual change by default
44
46
  - **Unified Swipe** - Single `swipe` tool that auto-routes to iOS or Android based on the connected device. Accepts screenshot pixel coordinates, handles per-platform conversion, and returns a `verification.meaningful` signal so agents detect end-of-list, non-scrollable surfaces, and missed coordinates. Essential for scrolling virtualized lists (FlatList/SectionList) where off-screen items aren't in the fiber tree
45
- - **UI Automation** - Swipe, long press, key events, and text input on both platforms. On Bridgeless/Fabric apps, `clear_focused_input` and `dismiss_keyboard` operate on whatever has focus, and `ios_input_text` / `android_input_text` accept `replace:true` to overwrite pre-filled values — all three update React state through `onChangeText` so controlled components (Formik, react-hook-form, useState) stay consistent
47
+ - **UI Automation** - Swipe, long press, key events, and text input on both platforms. On Bridgeless/Fabric apps, `dismiss_keyboard` operates on whatever has focus, and `ios_input_text` / `android_input_text` accept `replace:true` to overwrite pre-filled values — updating React state through `onChangeText` so controlled components (Formik, react-hook-form, useState) stay consistent
46
48
  - **Accessibility Inspection** - Query UI hierarchy to find elements by text, label, or resource ID
47
49
  - **OCR Text Extraction** - Extract visible text with tap-ready coordinates via Google Cloud Vision (works on any screen content)
48
50
 
@@ -108,11 +110,9 @@ See the [full tool reference](docs/tools.md) for all tools with descriptions. Ke
108
110
  | `scan_metro` | **Start here** — scan for Metro servers and auto-connect |
109
111
  | `get_logs` / `search_logs` | Capture and search console logs with filtering and summaries |
110
112
  | `get_network_requests` | Monitor HTTP requests with method/status filtering |
111
- | `get_flowpoints` / `verify_flow` | Flow tracing + factual verification: query, wait on, and assert `flowpoint()` breadcrumbs (SDK) |
112
113
  | `get_screen_layout` | Screen map of visible components with positions, sizes, and text content |
113
114
  | `tap` | **Unified tap** — auto-detects platform, tries fiber → accessibility → OCR → coordinates |
114
115
  | `ios_input_text` / `android_input_text` | Type text into the focused field. `replace:true` clears pre-filled values first (Fabric) |
115
- | `clear_focused_input` | Clear the focused TextInput via React `onChangeText`, keeping controlled state in sync |
116
116
  | `dismiss_keyboard` | Blur the focused input and close the on-screen keyboard |
117
117
  | `execute_in_app` | Run JS expressions in the app runtime (REPL-style) |
118
118
  | `ios_screenshot` / `android_screenshot` | Take device screenshots |
@@ -145,7 +145,7 @@ See the [full tool reference](docs/tools.md) for all tools with descriptions. Ke
145
145
  The list won't scroll — scroll it down and check what's going on
146
146
  ```
147
147
  ```
148
- Instrument the checkout flow with flowpoints, run it, and verify the steps fire in order
148
+ Why does this button look wrong — what component renders it and where?
149
149
  ```
150
150
 
151
151
  The agent connects to Metro, reads logs and network, inspects the screen, and drives the UI as needed to answer.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=appContext.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appContext.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/appContext.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { buildContextPreamble, CONTEXT_BINDINGS } from "../../core/appContext.js";
3
+ describe("buildContextPreamble", () => {
4
+ const source = buildContextPreamble();
5
+ function evaluateWith(globalStub, expr) {
6
+ const run = new Function("globalThis", source + "; return (" + expr + ");");
7
+ return run(globalStub);
8
+ }
9
+ it("emits Hermes-compatible ES5 only", () => {
10
+ expect(source).not.toMatch(/\bconst\b|\blet\b/);
11
+ expect(source).not.toMatch(/=>/);
12
+ expect(source).not.toMatch(/\basync\b/);
13
+ expect(source).not.toContain("`");
14
+ });
15
+ it("declares every advertised binding", () => {
16
+ for (const name of CONTEXT_BINDINGS) {
17
+ expect(source).toContain(name);
18
+ }
19
+ });
20
+ it("defers the expensive bindings behind functions", () => {
21
+ // cache() must not run extract() at preamble time — it costs 17ms and
22
+ // can materialise 1.6MB.
23
+ expect(source).toMatch(/function cache\(/);
24
+ });
25
+ it("resolves everything to null when nothing is available", () => {
26
+ const out = evaluateWith({}, "{ store: store, state: state, router: router, apollo: apollo }");
27
+ expect(out.store).toBeNull();
28
+ expect(out.state).toBeNull();
29
+ expect(out.router).toBeNull();
30
+ expect(out.apollo).toBeNull();
31
+ });
32
+ it("resolves a redux store from a known global when the fiber walk finds nothing", () => {
33
+ const store = {
34
+ getState: () => ({ auth: { id: 1 } }),
35
+ dispatch: () => undefined,
36
+ subscribe: () => undefined
37
+ };
38
+ const out = evaluateWith({ __REDUX_STORE__: store }, "{ store: store, state: state }");
39
+ expect(out.store).toBe(store);
40
+ expect(out.state.auth.id).toBe(1);
41
+ });
42
+ it("falls back to the SDK store registry", () => {
43
+ const store = { getState: () => ({ ok: true }), dispatch: () => undefined, subscribe: () => undefined };
44
+ const out = evaluateWith({ __RN_AI_DEVTOOLS__: { stores: { redux: store } } }, "{ store: store, state: state }");
45
+ expect(out.store).toBe(store);
46
+ expect(out.state.ok).toBe(true);
47
+ });
48
+ it("derefs an Apollo normalized reference and passes plain values through", () => {
49
+ const cacheObj = { "Entity:1": { id: "1", name: "Acme" } };
50
+ const deref = evaluateWith({}, "deref");
51
+ expect(deref({ __ref: "Entity:1" }, cacheObj)).toEqual({ id: "1", name: "Acme" });
52
+ expect(deref({ plain: true }, cacheObj)).toEqual({ plain: true });
53
+ expect(deref(null, cacheObj)).toBeNull();
54
+ });
55
+ it("summary reports per-key type and byte size, heaviest first", () => {
56
+ const summary = evaluateWith({}, "summary");
57
+ const rows = summary({ small: { a: 1 }, big: { pad: "x".repeat(500) } });
58
+ expect(rows[0].key).toBe("big");
59
+ expect(rows[0].type).toBe("object");
60
+ expect(rows[0].bytes).toBeGreaterThan(rows[1].bytes);
61
+ });
62
+ it("summary survives a non-serialisable value instead of throwing", () => {
63
+ const summary = evaluateWith({}, "summary");
64
+ const cyclic = {};
65
+ cyclic.self = cyclic;
66
+ const rows = summary({ cyclic, fine: 1 });
67
+ expect(rows.find((r) => r.key === "cyclic")?.bytes).toBe(-1);
68
+ });
69
+ });
70
+ //# sourceMappingURL=appContext.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appContext.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/appContext.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=appRequest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appRequest.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/appRequest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,179 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { buildRequestExpression } from "../../core/appRequest.js";
3
+ describe("buildRequestExpression", () => {
4
+ it("emits Hermes-compatible ES5 only", () => {
5
+ const src = buildRequestExpression({ method: "GET", url: "https://example.com/x" });
6
+ expect(src).not.toMatch(/=>|\bconst\b|\blet\b|\basync\b/);
7
+ });
8
+ it("embeds method and url safely as JSON literals", () => {
9
+ const src = buildRequestExpression({ method: "DELETE", url: "https://api.test/user/17?q=a&b=c" });
10
+ expect(src).toContain('"DELETE"');
11
+ expect(src).toContain('"https://api.test/user/17?q=a&b=c"');
12
+ });
13
+ it("upper-cases the method", () => {
14
+ const src = buildRequestExpression({ method: "post", url: "https://api.test/x" });
15
+ expect(src).toContain('"POST"');
16
+ });
17
+ it("never embeds a token — auth resolves in-app", () => {
18
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
19
+ expect(src).toContain("accessToken");
20
+ expect(src).not.toMatch(/eyJ[A-Za-z0-9_-]{10,}/);
21
+ });
22
+ it("omits the Authorization header when auth is none", () => {
23
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/public", auth: "none" });
24
+ // The response still reports an `authorized` flag, so assert on the
25
+ // header actually being set rather than on the word appearing at all.
26
+ expect(src).not.toContain("'Bearer '");
27
+ expect(src).not.toContain("accessToken");
28
+ });
29
+ it("serialises a JSON body and sets the content type", () => {
30
+ const src = buildRequestExpression({ method: "POST", url: "https://api.test/x", body: { name: "Test" } });
31
+ expect(src).toContain('"application/json"');
32
+ expect(src).toContain('{\\"name\\":\\"Test\\"}');
33
+ });
34
+ it("escapes a url containing a quote rather than breaking the expression", () => {
35
+ const src = buildRequestExpression({ method: "GET", url: 'https://api.test/a"b' });
36
+ expect(src).toContain('\\"');
37
+ // The generated source must still be parseable.
38
+ expect(() => new Function("return " + src.replace(/^\(function/, "(function"))).not.toThrow();
39
+ });
40
+ it("passes explicit headers through", () => {
41
+ const src = buildRequestExpression({
42
+ method: "GET",
43
+ url: "https://api.test/x",
44
+ headers: { "x-lang": "en" }
45
+ });
46
+ expect(src).toContain('"x-lang"');
47
+ expect(src).toContain('"en"');
48
+ });
49
+ });
50
+ describe("buildRequestExpression — unresolved auth", () => {
51
+ it("warns when auth=auto is requested (the note is emitted, gated at runtime)", () => {
52
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
53
+ expect(src).toContain("UNAUTHENTICATED");
54
+ expect(src).toContain("no captured request carried an Authorization header");
55
+ });
56
+ it("does not emit the warning when auth is explicitly none", () => {
57
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/x", auth: "none" });
58
+ expect(src).toContain("var authNote = null;");
59
+ expect(src).not.toContain("UNAUTHENTICATED");
60
+ });
61
+ });
62
+ describe("buildRequestExpression — generated source behaviour", () => {
63
+ function runGenerated(src, stateStub, globalStub = {}) {
64
+ let captured;
65
+ const fetchStub = (url, init) => {
66
+ captured = { url, init };
67
+ return Promise.resolve({
68
+ status: 200,
69
+ ok: true,
70
+ text: () => Promise.resolve(JSON.stringify({ hello: "world" }))
71
+ });
72
+ };
73
+ const run = new Function("state", "fetch", "globalThis", "return " + src + ";");
74
+ return run(stateStub, fetchStub, globalStub).then((result) => ({
75
+ captured: captured,
76
+ result
77
+ }));
78
+ }
79
+ it("sets a bearer header from state.user.accessToken", async () => {
80
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
81
+ const { captured, result } = await runGenerated(src, { user: { accessToken: "tok-123" } });
82
+ expect(captured.init.headers.Authorization).toBe("Bearer tok-123");
83
+ expect(result.authorized).toBe(true);
84
+ expect(result.authNote).toBeUndefined();
85
+ });
86
+ it("falls back to state.auth.accessToken", async () => {
87
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
88
+ const { captured } = await runGenerated(src, { auth: { accessToken: "tok-abc" } });
89
+ expect(captured.init.headers.Authorization).toBe("Bearer tok-abc");
90
+ });
91
+ it("warns and sends unauthenticated when no token is present", async () => {
92
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
93
+ const { captured, result } = await runGenerated(src, { auth: { isAuthenticated: true } });
94
+ expect(captured.init.headers.Authorization).toBeUndefined();
95
+ expect(result.authorized).toBe(false);
96
+ expect(String(result.authNote)).toContain("UNAUTHENTICATED");
97
+ });
98
+ it("lets an explicit Authorization header win over auth=auto", async () => {
99
+ const src = buildRequestExpression({
100
+ method: "GET",
101
+ url: "https://api.test/me",
102
+ auth: "auto",
103
+ headers: { Authorization: "Bearer explicit" }
104
+ });
105
+ const { captured, result } = await runGenerated(src, { user: { accessToken: "tok-123" } });
106
+ expect(captured.init.headers.Authorization).toBe("Bearer explicit");
107
+ expect(result.authNote).toBeUndefined();
108
+ });
109
+ it("sends the body and parses a JSON response", async () => {
110
+ const src = buildRequestExpression({ method: "POST", url: "https://api.test/x", body: { a: 1 }, auth: "none" });
111
+ const { captured, result } = await runGenerated(src, null);
112
+ expect(captured.init.method).toBe("POST");
113
+ expect(captured.init.body).toBe('{"a":1}');
114
+ expect(captured.init.headers["Content-Type"]).toBe("application/json");
115
+ expect(result.body).toEqual({ hello: "world" });
116
+ });
117
+ });
118
+ describe("buildRequestExpression — token from captured requests (Apollo apps)", () => {
119
+ function runWith(src, stateStub, globalStub) {
120
+ let captured;
121
+ const fetchStub = (url, init) => {
122
+ captured = { url, init };
123
+ return Promise.resolve({ status: 200, ok: true, text: () => Promise.resolve("{}") });
124
+ };
125
+ const run = new Function("state", "fetch", "globalThis", "return " + src + ";");
126
+ return run(stateStub, fetchStub, globalStub).then((result) => ({
127
+ captured: captured,
128
+ result
129
+ }));
130
+ }
131
+ const sdkWith = (headers) => ({
132
+ __RN_AI_DEVTOOLS__: {
133
+ getNetworkEntries: () => headers.map((h) => ({ requestHeaders: h }))
134
+ }
135
+ });
136
+ it("reuses the Authorization header from the most recent captured request", async () => {
137
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
138
+ const { captured, result } = await runWith(src, { auth: { isAuthenticated: true } }, sdkWith([
139
+ { Authorization: "Bearer old-token" },
140
+ { "content-type": "application/json" },
141
+ { authorization: "Bearer newest-token" }
142
+ ]));
143
+ expect(captured.init.headers.Authorization).toBe("Bearer newest-token");
144
+ expect(result.authSource).toBe("captured-request");
145
+ expect(result.authNote).toBeUndefined();
146
+ });
147
+ it("prefers redux over a captured header when both exist", async () => {
148
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
149
+ const { captured, result } = await runWith(src, { user: { accessToken: "redux-tok" } }, sdkWith([
150
+ { Authorization: "Bearer captured-tok" }
151
+ ]));
152
+ expect(captured.init.headers.Authorization).toBe("Bearer redux-tok");
153
+ expect(result.authSource).toBe("redux");
154
+ });
155
+ it("still warns when neither redux nor any captured request has a token", async () => {
156
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
157
+ const { result } = await runWith(src, null, sdkWith([{ "content-type": "application/json" }]));
158
+ expect(result.authorized).toBe(false);
159
+ expect(String(result.authNote)).toContain("UNAUTHENTICATED");
160
+ });
161
+ it("degrades quietly when the SDK is absent", async () => {
162
+ const src = buildRequestExpression({ method: "GET", url: "https://api.test/me", auth: "auto" });
163
+ const { result } = await runWith(src, null, {});
164
+ expect(result.authorized).toBe(false);
165
+ expect(String(result.authNote)).toContain("UNAUTHENTICATED");
166
+ });
167
+ it("reports an explicitly-passed header as the source", async () => {
168
+ const src = buildRequestExpression({
169
+ method: "GET",
170
+ url: "https://api.test/me",
171
+ auth: "auto",
172
+ headers: { Authorization: "Bearer explicit" }
173
+ });
174
+ const { captured, result } = await runWith(src, { user: { accessToken: "redux-tok" } }, sdkWith([]));
175
+ expect(captured.init.headers.Authorization).toBe("Bearer explicit");
176
+ expect(result.authSource).toBe("explicit");
177
+ });
178
+ });
179
+ //# sourceMappingURL=appRequest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appRequest.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/appRequest.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contextWiring.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextWiring.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/contextWiring.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,28 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { buildEvaluationSource } from "../../core/executor.js";
3
+ describe("buildEvaluationSource", () => {
4
+ it("puts the polyfill first, then the context, then the caller's expression", () => {
5
+ const src = buildEvaluationSource("state.auth");
6
+ expect(src.indexOf("var global =")).toBeLessThan(src.indexOf("var store ="));
7
+ expect(src.indexOf("var store =")).toBeLessThan(src.lastIndexOf("state.auth"));
8
+ expect(src.endsWith("state.auth")).toBe(true);
9
+ });
10
+ it("declares context bindings with var so the caller's expression can see them", () => {
11
+ const src = buildEvaluationSource("1");
12
+ expect(src).toContain("var store =");
13
+ expect(src).toContain("var state =");
14
+ expect(src).not.toContain("const store");
15
+ });
16
+ it("does not wrap the caller's expression", () => {
17
+ // PR #5 (merged 2026-03-02) reverted blanket IIFE wrapping: it wrapped
18
+ // without a `return`, so every result came back undefined, and tools
19
+ // building their own IIFE were double-wrapped.
20
+ const src = buildEvaluationSource("2 + 2");
21
+ expect(src).not.toContain("(function(){ 2 + 2 })()");
22
+ expect(src.endsWith("2 + 2")).toBe(true);
23
+ });
24
+ it("is stable across calls so the preamble is built once", () => {
25
+ expect(buildEvaluationSource("1")).toBe(buildEvaluationSource("1"));
26
+ });
27
+ });
28
+ //# sourceMappingURL=contextWiring.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextWiring.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/contextWiring.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QAC/E,MAAM,GAAG,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;QAC7E,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC;QAC/E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QAClF,MAAM,GAAG,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC7C,uEAAuE;QACvE,qEAAqE;QACrE,+CAA+C;QAC/C,MAAM,GAAG,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=epochRender.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epochRender.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/epochRender.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ import { withRestartDividers, evictionNotice, resolveEpochFilter } from "../../core/epochRender.js";
2
+ import { bumpEpoch, resetEpochs, getLogBuffer, logBuffers, networkBuffers } from "../../core/state.js";
3
+ describe("withRestartDividers", () => {
4
+ it("inserts a divider where the epoch changes", () => {
5
+ const entries = [
6
+ { epoch: 1, msg: "a" },
7
+ { epoch: 1, msg: "b" },
8
+ { epoch: 2, msg: "c" },
9
+ ];
10
+ expect(withRestartDividers(entries, e => e.msg)).toBe("a\nb\n── app restarted (epoch 2) ──\nc");
11
+ });
12
+ it("emits no divider for a single epoch", () => {
13
+ expect(withRestartDividers([{ epoch: 1, msg: "a" }], e => e.msg)).toBe("a");
14
+ });
15
+ it("handles an empty list", () => {
16
+ expect(withRestartDividers([], e => e.msg)).toBe("");
17
+ });
18
+ });
19
+ describe("evictionNotice", () => {
20
+ it("is empty when nothing was dropped", () => {
21
+ expect(evictionNotice(0, "EXECBRO_LOG_BUFFER_SIZE")).toBe("");
22
+ });
23
+ it("reports the count with a thousands separator", () => {
24
+ expect(evictionNotice(3412, "EXECBRO_LOG_BUFFER_SIZE")).toBe("\n\n[3,412 older entries evicted — raise EXECBRO_LOG_BUFFER_SIZE to retain more]");
25
+ });
26
+ });
27
+ describe("resolveEpochFilter", () => {
28
+ beforeEach(() => {
29
+ resetEpochs();
30
+ logBuffers.clear();
31
+ networkBuffers.clear();
32
+ });
33
+ it("defaults to no filter", () => {
34
+ expect(resolveEpochFilter(undefined, "dev")).toBeUndefined();
35
+ expect(resolveEpochFilter("all", "dev")).toBeUndefined();
36
+ });
37
+ it("resolves 'current' to the device's epoch", () => {
38
+ getLogBuffer("dev");
39
+ bumpEpoch("dev");
40
+ expect(resolveEpochFilter("current", "dev")).toBe(2);
41
+ });
42
+ it("resolves 'current' from a device SUBSTRING, not the raw argument", () => {
43
+ // The tool argument is a substring ("iPhone"); the buffer key is the
44
+ // full name. Looking the epoch up under the raw argument returns 1 and
45
+ // serves pre-restart data labelled as current.
46
+ getLogBuffer("iPhone 17 Pro");
47
+ bumpEpoch("iPhone 17 Pro");
48
+ expect(resolveEpochFilter("current", "iPhone")).toBe(2);
49
+ });
50
+ it("resolves 'current' to the newest run when no device is given", () => {
51
+ getLogBuffer("iPhone 17 Pro");
52
+ getLogBuffer("sdk_gphone64_arm64");
53
+ bumpEpoch("sdk_gphone64_arm64");
54
+ expect(resolveEpochFilter("current", undefined)).toBe(2);
55
+ });
56
+ it("passes a numeric epoch through", () => {
57
+ expect(resolveEpochFilter(1, "dev")).toBe(1);
58
+ });
59
+ });
60
+ //# sourceMappingURL=epochRender.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epochRender.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/epochRender.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=executeBudget.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executeBudget.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/executeBudget.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { applyResultBudget } from "../../core/truncate.js";
3
+ describe("applyResultBudget", () => {
4
+ it("passes a small string result through unchanged", () => {
5
+ const out = applyResultBudget("2", 25000);
6
+ expect(out.text).toBe("2");
7
+ expect(out.budget.truncated).toBe(false);
8
+ });
9
+ it("bounds an oversized JSON string result and reports the reduction", () => {
10
+ const big = JSON.stringify({ rows: Array.from({ length: 4000 }, (_, i) => ({ i, pad: "xxxxxxxxxx" })) });
11
+ const out = applyResultBudget(big, 2000);
12
+ expect(out.budget.truncated).toBe(true);
13
+ expect(out.text.length).toBeLessThanOrEqual(2000);
14
+ expect(out.budget.originalBytes).toBeGreaterThan(2000);
15
+ });
16
+ it("leaves a non-JSON string alone rather than mangling it", () => {
17
+ const out = applyResultBudget("restored", 25000);
18
+ expect(out.text).toBe("restored");
19
+ expect(out.budget.truncated).toBe(false);
20
+ });
21
+ it("hard-clips a non-JSON string that exceeds the budget", () => {
22
+ const out = applyResultBudget("y".repeat(9000), 500);
23
+ expect(out.text.length).toBeLessThanOrEqual(500);
24
+ expect(out.budget.truncated).toBe(true);
25
+ });
26
+ });
27
+ //# sourceMappingURL=executeBudget.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executeBudget.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/executeBudget.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,MAAM,GAAG,GAAG,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QACxE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACzG,MAAM,GAAG,GAAG,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAC9D,MAAM,GAAG,GAAG,iBAAiB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC5D,MAAM,GAAG,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
- import { describe, it, expect } from "@jest/globals";
2
- import { containsProblematicUnicode, stripLeadingComments, validateAndPreprocessExpression, formatScreenLayoutTree, } from "../../core/executor.js";
1
+ import { describe, it, expect, beforeEach } from "@jest/globals";
2
+ import { containsProblematicUnicode, stripLeadingComments, validateAndPreprocessExpression, formatScreenLayoutTree, looksLikeAsyncFunction, markAsyncEvalUnsupported, isAsyncEvalUnsupported, resetAsyncEvalSupport, } from "../../core/executor.js";
3
3
  describe("containsProblematicUnicode", () => {
4
4
  it("returns false for ASCII text", () => {
5
5
  expect(containsProblematicUnicode("hello world")).toBe(false);
@@ -64,11 +64,14 @@ describe("validateAndPreprocessExpression", () => {
64
64
  const result = validateAndPreprocessExpression("(async () => { await fetch() })()");
65
65
  expect(result.valid).toBe(true);
66
66
  });
67
- it("points require() callers at Metro's __r registry", () => {
67
+ it("accepts require() — the injected context now provides it", () => {
68
+ // Superseded behaviour: this used to be rejected pre-flight with a
69
+ // pointer at Metro's __r registry, which was the second-largest
70
+ // production failure class (236 events). The context defines `require`
71
+ // over that registry, so the caller no longer has to hand-roll it.
68
72
  const result = validateAndPreprocessExpression("require('react-native').Dimensions.get('window')");
69
- expect(result.valid).toBe(false);
70
- expect(result.error).toContain("require()");
71
- expect(result.error).toContain("__r");
73
+ expect(result.valid).toBe(true);
74
+ expect(result.error).toBeUndefined();
72
75
  });
73
76
  it("strips comments and validates remaining expression", () => {
74
77
  const result = validateAndPreprocessExpression("// setup\n__DEV__");
@@ -253,4 +256,43 @@ describe("formatScreenLayoutTree off-screen summary", () => {
253
256
  expect(lines[summaryLineIdx - 1]).toBe("");
254
257
  });
255
258
  });
259
+ describe("async-function capability tracking (Hermes engine-dependent)", () => {
260
+ beforeEach(() => resetAsyncEvalSupport());
261
+ it("detects async function syntax in its common forms", () => {
262
+ expect(looksLikeAsyncFunction("(async () => { return 1; })()")).toBe(true);
263
+ expect(looksLikeAsyncFunction("async function f(){ return 1 }")).toBe(true);
264
+ expect(looksLikeAsyncFunction("async x => x + 1")).toBe(true);
265
+ });
266
+ it("does not flag promise chains or unrelated identifiers", () => {
267
+ expect(looksLikeAsyncFunction("Promise.resolve(1).then(function(x){ return x + 1 })")).toBe(false);
268
+ expect(looksLikeAsyncFunction("state.asyncStorage")).toBe(false);
269
+ expect(looksLikeAsyncFunction("foo.async")).toBe(false);
270
+ });
271
+ it("does not flag async-looking text inside string literals or comments", () => {
272
+ // Regression: an over-eager probe here reintroduces the pre-flight
273
+ // rejection of legitimate calls that f6fb8a0 removed.
274
+ expect(looksLikeAsyncFunction('(function(){ return "async (not really)"; })()')).toBe(false);
275
+ expect(looksLikeAsyncFunction("({ label: 'async () => {}' })")).toBe(false);
276
+ expect(looksLikeAsyncFunction("`async function x(){}`")).toBe(false);
277
+ expect(looksLikeAsyncFunction("// async () => {}\n1 + 1")).toBe(false);
278
+ expect(looksLikeAsyncFunction("/* async function f(){} */ 42")).toBe(false);
279
+ });
280
+ it("still flags real async syntax that follows a string literal", () => {
281
+ expect(looksLikeAsyncFunction('var label = "async"; (async () => 1)()')).toBe(true);
282
+ });
283
+ it("starts with every device assumed capable", () => {
284
+ expect(isAsyncEvalUnsupported("iPhone Air")).toBe(false);
285
+ });
286
+ it("records the capability per device, not globally", () => {
287
+ markAsyncEvalUnsupported("iPhone Air");
288
+ expect(isAsyncEvalUnsupported("iPhone Air")).toBe(true);
289
+ // A second device may run a Hermes build that does compile async.
290
+ expect(isAsyncEvalUnsupported("sdk_gphone16k_arm64")).toBe(false);
291
+ });
292
+ it("resets cleanly between sessions", () => {
293
+ markAsyncEvalUnsupported("iPhone Air");
294
+ resetAsyncEvalSupport();
295
+ expect(isAsyncEvalUnsupported("iPhone Air")).toBe(false);
296
+ });
297
+ });
256
298
  //# sourceMappingURL=executor.test.js.map