@ceraph/react-native-mcp 0.3.2 → 0.4.5

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 (132) hide show
  1. package/README.md +335 -68
  2. package/dist/babel-plugin/index.cjs +1 -0
  3. package/dist/babel-plugin/index.js +1 -0
  4. package/dist/cli.d.ts +3 -1
  5. package/dist/cli.js +1 -47
  6. package/dist/index.d.ts +106 -1
  7. package/dist/index.js +2 -1651
  8. package/dist/shim/async-storage-ops.d.ts +26 -0
  9. package/dist/shim/async-storage-ops.js +1 -0
  10. package/dist/shim/boot.d.ts +9 -0
  11. package/dist/shim/boot.js +1 -141
  12. package/dist/shim/camera.js +1 -62
  13. package/dist/shim/command-poll.d.ts +18 -0
  14. package/dist/shim/command-poll.js +1 -0
  15. package/dist/shim/config.js +1 -56
  16. package/dist/shim/console-capture.d.ts +16 -0
  17. package/dist/shim/console-capture.js +1 -0
  18. package/dist/shim/deep-link.js +1 -25
  19. package/dist/shim/dev-guard.js +1 -3
  20. package/dist/shim/dev-host.d.ts +1 -0
  21. package/dist/shim/dev-host.js +1 -0
  22. package/dist/shim/error-handler.js +1 -66
  23. package/dist/shim/fetch-interceptor.js +1 -93
  24. package/dist/shim/index.d.ts +3 -0
  25. package/dist/shim/index.js +1 -6
  26. package/dist/shim/keep-awake.js +1 -118
  27. package/dist/shim/network-ownership.d.ts +4 -0
  28. package/dist/shim/network-ownership.js +1 -0
  29. package/dist/shim/optimistic-observer.d.ts +29 -0
  30. package/dist/shim/optimistic-observer.js +1 -0
  31. package/dist/shim/reload.js +1 -76
  32. package/dist/shim/reset.d.ts +30 -0
  33. package/dist/shim/reset.js +1 -0
  34. package/dist/shim/signal-capture.d.ts +8 -0
  35. package/dist/shim/signal-capture.js +1 -15
  36. package/dist/shim/signal-transport.d.ts +14 -1
  37. package/dist/shim/signal-transport.js +1 -43
  38. package/dist/shim/xhr-interceptor.d.ts +39 -0
  39. package/dist/shim/xhr-interceptor.js +1 -0
  40. package/package.json +41 -15
  41. package/dist/app-lifecycle.d.ts +0 -50
  42. package/dist/app-lifecycle.js +0 -487
  43. package/dist/camera-image-writer.d.ts +0 -43
  44. package/dist/camera-image-writer.js +0 -280
  45. package/dist/camera-registry-sync.d.ts +0 -18
  46. package/dist/camera-registry-sync.js +0 -117
  47. package/dist/device-autonomy.d.ts +0 -30
  48. package/dist/device-autonomy.js +0 -117
  49. package/dist/error-parser.d.ts +0 -51
  50. package/dist/error-parser.js +0 -275
  51. package/dist/expo-manager.d.ts +0 -62
  52. package/dist/expo-manager.js +0 -447
  53. package/dist/init/ast-camera.d.ts +0 -29
  54. package/dist/init/ast-camera.js +0 -267
  55. package/dist/init/ast-layout.d.ts +0 -15
  56. package/dist/init/ast-layout.js +0 -167
  57. package/dist/init/claude-hook-constants.d.ts +0 -9
  58. package/dist/init/claude-hook-constants.js +0 -91
  59. package/dist/init/lan-ip.d.ts +0 -11
  60. package/dist/init/lan-ip.js +0 -51
  61. package/dist/init/monorepo.d.ts +0 -13
  62. package/dist/init/monorepo.js +0 -185
  63. package/dist/init/oauth.d.ts +0 -52
  64. package/dist/init/oauth.js +0 -220
  65. package/dist/init/package-manager.d.ts +0 -11
  66. package/dist/init/package-manager.js +0 -60
  67. package/dist/init/prompt.d.ts +0 -12
  68. package/dist/init/prompt.js +0 -68
  69. package/dist/init/shell-profile.d.ts +0 -22
  70. package/dist/init/shell-profile.js +0 -85
  71. package/dist/init/steps.d.ts +0 -135
  72. package/dist/init/steps.js +0 -399
  73. package/dist/init/url-scheme.d.ts +0 -42
  74. package/dist/init/url-scheme.js +0 -187
  75. package/dist/init/walkthrough.d.ts +0 -76
  76. package/dist/init/walkthrough.js +0 -340
  77. package/dist/init.d.ts +0 -8
  78. package/dist/init.js +0 -395
  79. package/dist/iproxy-manager.d.ts +0 -32
  80. package/dist/iproxy-manager.js +0 -216
  81. package/dist/mac-caffeinate.d.ts +0 -10
  82. package/dist/mac-caffeinate.js +0 -56
  83. package/dist/permission-interceptor.d.ts +0 -29
  84. package/dist/permission-interceptor.js +0 -185
  85. package/dist/prebuild-detector.d.ts +0 -19
  86. package/dist/prebuild-detector.js +0 -174
  87. package/dist/preflight.d.ts +0 -34
  88. package/dist/preflight.js +0 -847
  89. package/dist/screen.d.ts +0 -184
  90. package/dist/screen.js +0 -931
  91. package/dist/signal-listener.d.ts +0 -27
  92. package/dist/signal-listener.js +0 -135
  93. package/dist/simulator-boot.d.ts +0 -52
  94. package/dist/simulator-boot.js +0 -227
  95. package/dist/target.d.ts +0 -48
  96. package/dist/target.js +0 -267
  97. package/dist/uninstall/cli-runner.d.ts +0 -32
  98. package/dist/uninstall/cli-runner.js +0 -223
  99. package/dist/uninstall/footprint.d.ts +0 -40
  100. package/dist/uninstall/footprint.js +0 -288
  101. package/dist/uninstall/mcp-tools.d.ts +0 -14
  102. package/dist/uninstall/mcp-tools.js +0 -175
  103. package/dist/uninstall/revert-auth.d.ts +0 -22
  104. package/dist/uninstall/revert-auth.js +0 -31
  105. package/dist/uninstall/revert-boot.d.ts +0 -24
  106. package/dist/uninstall/revert-boot.js +0 -242
  107. package/dist/uninstall/revert-camera.d.ts +0 -12
  108. package/dist/uninstall/revert-camera.js +0 -199
  109. package/dist/uninstall/revert-ceraph-dir.d.ts +0 -27
  110. package/dist/uninstall/revert-ceraph-dir.js +0 -38
  111. package/dist/uninstall/revert-claude-hooks.d.ts +0 -19
  112. package/dist/uninstall/revert-claude-hooks.js +0 -191
  113. package/dist/uninstall/revert-gitignore.d.ts +0 -17
  114. package/dist/uninstall/revert-gitignore.js +0 -43
  115. package/dist/uninstall/revert-mcp-clients.d.ts +0 -57
  116. package/dist/uninstall/revert-mcp-clients.js +0 -194
  117. package/dist/uninstall/revert-package.d.ts +0 -34
  118. package/dist/uninstall/revert-package.js +0 -98
  119. package/dist/uninstall/revert-scheme.d.ts +0 -36
  120. package/dist/uninstall/revert-scheme.js +0 -139
  121. package/dist/uninstall/revert-signal-host-env.d.ts +0 -31
  122. package/dist/uninstall/revert-signal-host-env.js +0 -61
  123. package/dist/uninstall/walkthrough.d.ts +0 -80
  124. package/dist/uninstall/walkthrough.js +0 -1244
  125. package/dist/utils/atomic-write.d.ts +0 -1
  126. package/dist/utils/atomic-write.js +0 -30
  127. package/dist/wait-for-device.d.ts +0 -68
  128. package/dist/wait-for-device.js +0 -368
  129. package/dist/wda-manager.d.ts +0 -38
  130. package/dist/wda-manager.js +0 -186
  131. package/dist/wda-simulator.d.ts +0 -28
  132. package/dist/wda-simulator.js +0 -257
package/README.md CHANGED
@@ -1,46 +1,140 @@
1
1
  # @ceraph/react-native-mcp
2
2
 
3
- MCP server for React Native and Expo development. Automatic build error capture, console monitoring, reliable screen interactions, prebuild detection, and iOS Simulator support.
3
+ MCP server for React Native and Expo development. Automatic build error capture, console monitoring, reliable screen interactions, and prebuild detection.
4
4
 
5
5
  Works with any MCP client: Claude Code, Cursor, Codex, Windsurf, and others.
6
6
 
7
7
  > ⚠️ **Ignore the `npm i` line above.** Run this once instead:
8
8
  >
9
9
  > ```bash
10
- > npx @ceraph/react-native-mcp init
10
+ > npx @ceraph/react-native-mcp@latest init
11
11
  > ```
12
12
 
13
+ ## Platform support
14
+
15
+ This MCP is **iOS-only**. Both a real iOS device (the original supported configuration) and a booted iOS Simulator now work. The runtime path (devicectl / idb / WebDriverAgent / simctl) is Apple-specific.
16
+
17
+ | | Status | Notes |
18
+ |---|---|---|
19
+ | iOS device (USB) | Supported | WebDriverAgent installs through Xcode (one-time); WDA is reached at `localhost:8100`. Default — no env var needed. |
20
+ | iOS Simulator | Supported | `ceraph_start` installs the optional `appium-webdriveragent` dep and builds + launches the WDA automatically the first time you target a simulator. See [Simulator setup](#simulator-setup) below. |
21
+ | Android (device or emulator) | Not supported | |
22
+ | Linux / Windows host | Not supported | |
23
+ | Expo Go | Not supported | Needs dev client or prebuilt app. |
24
+
25
+ If you don't have a Mac with Xcode, stop here — nothing in this package will work for you yet.
26
+
27
+ ### Choosing device vs simulator
28
+
29
+ By default the MCP auto-detects: if a real device is connected, it uses the device; otherwise it uses a booted simulator. Override explicitly:
30
+
31
+ ```bash
32
+ export CERAPH_TARGET=device # always use real device
33
+ export CERAPH_TARGET=simulator # always use simulator
34
+ export CERAPH_TARGET=auto # default behavior (also: unset)
35
+ ```
36
+
37
+ The `rn_target_status` tool reports which target is currently active and the resolved WDA base URL.
38
+
13
39
  ## Requirements
14
40
 
15
41
  - macOS with Xcode 14+ (Command Line Tools installed)
16
- - iOS 16+ device on USB **OR** a booted iOS Simulator
42
+ - iOS 16+ device connected to the Mac over USB and unlocked **OR** a booted iOS Simulator
17
43
  - Node.js 18+
18
- - [`mobile-mcp`](https://github.com/mobile-next/mobile-mcp) for screenshots, swipe, and device management
44
+ - [WebDriverAgent](https://github.com/appium/WebDriverAgent) running and reachable. On a real device, this is `localhost:8100` (port-forwarded from the device by Xcode / `iproxy`). On a simulator, the port is captured at runtime from `rn_wda_start` — see [Simulator setup](#simulator-setup).
19
45
  - Expo dev client or prebuilt app (Expo Go is not supported)
20
46
 
21
- iOS-only. Android, Linux, and Windows hosts are not supported.
47
+ > **Multi-developer Macs:** The runtime signal listener binds to `localhost:8101` (WDA uses 8100, Metro uses 8081). Only one developer on a given Mac can run the MCP at a time without a port collision. If you share a Mac and need parallel sessions, override the port via `CERAPH_SIGNAL_PORT` when starting the MCP server.
48
+
49
+ ### Environment variables
50
+
51
+ | Variable | Default | Purpose |
52
+ |---|---|---|
53
+ | `CERAPH_SIGNAL_HOST` | auto-detected | Optional override for the host the on-device signal capture posts to. It auto-discovers the Mac's address from the Metro dev-server URL at runtime (`SourceCode.scriptURL` — `localhost` on a simulator, the LAN IP on a real device), and the MCP server re-detects the LAN IP at startup for its own listener binding. You only set this for unusual network setups where the dev-server host isn't reachable for the signal channel (e.g. `192.168.1.x`). Leave it unset for normal use. |
54
+ | `CERAPH_SIGNAL_PORT` | `8101` | Port the signal listener binds to on the Mac. Change when 8101 is taken. |
55
+ | `CERAPH_TARGET` | `auto` | Which iOS runtime to drive. `device` forces the real-device path even if a simulator is booted; `simulator` forces simctl/sim-WDA even if a device is connected; `auto` prefers device when both are available. |
56
+
57
+ ## Simulator setup
58
+
59
+ On a simulator, the WDA needs the optional `appium-webdriveragent` package — `ceraph_start` (and `rn_wda_start`) install it automatically the first time you target a simulator, then build and launch the WDA for you. The first build is a one-time ~1 minute; later runs reuse the cache at `<project>/.ceraph/wda-derived/`. It's an optional dep, so device-only setups skip the ~200 MB download.
60
+
61
+ Boot any iPhone simulator (iOS ≥ 16) and run `ceraph_start`:
62
+
63
+ ```bash
64
+ open -a Simulator
65
+ # or: xcrun simctl boot <UDID>
66
+ ```
67
+
68
+ `xcrun simctl list devices booted` shows what's currently up.
69
+
70
+ Once WDA is up, every `screen_*`, `app_*`, and `rn_test_*` tool routes to the simulator automatically. Call `rn_wda_stop` to tear it down (or just exit the MCP — shutdown stops the xcodebuild child cleanly).
71
+
72
+ **Concurrent device + simulator:** The auto-detection prefers a connected device. To test on the simulator while a phone is plugged in, set `CERAPH_TARGET=simulator` in the MCP process environment.
22
73
 
23
74
  ## Quick Setup
24
75
 
25
76
  Run this from your project root:
26
77
 
27
78
  ```bash
28
- npx @ceraph/react-native-mcp init
79
+ npx @ceraph/react-native-mcp@latest init
29
80
  ```
30
81
 
31
- For testing on a simulator, also run `npm i -D appium-webdriveragent` and call `rn_wda_start` once from your MCP client (first WDA build takes 60–120s and may need an Xcode signing prompt; subsequent starts are ~5–10s via cache).
82
+ This automatically:
83
+ - Configures MCP servers for all detected clients (Claude Code, Cursor, Codex, VS Code, Windsurf, Antigravity)
84
+ - Installs a Claude Code hook that injects runtime errors into your conversation automatically
85
+ - Adds `.rn-errors.json` to your `.gitignore`
86
+ - Signs you in (browser OAuth) so the autonomous flow tools can reach the Ceraph server
32
87
 
33
- ## Uninstalling
88
+ ## Uninstalling Ceraph
34
89
 
35
90
  ```bash
36
91
  npx @ceraph/react-native-mcp uninstall
37
92
  ```
38
93
 
39
- Flags: `--yes` (skip prompts), `--purge-images` (also delete `.ceraph/`), `--global` (also delete `~/.ceraph/auth.json`), `--dry-run` (preview only).
94
+ Non-destructive by default: it reverses what `init` did and reports a per-step status, leaving your camera test images and OAuth token in place so you can re-install without losing state. Only `@ceraph/react-native-mcp` is removed from `package.json`.
95
+
96
+ ### Flags
97
+
98
+ | Flag | Effect |
99
+ | --- | --- |
100
+ | `-y`, `--yes` | Skip confirmation prompts. |
101
+ | `--purge-images` | Also delete `.ceraph/` (camera images, design snapshots, run artefacts). |
102
+ | `--global` | Also delete `~/.ceraph/auth.json`, signing you out of every Ceraph install on the machine. |
103
+ | `--dry-run` | Preview the step list without writing anything. |
104
+ | `--project-dir <path>` | Operate against a specific subpackage. Required when a monorepo has more than one React Native app. |
105
+
106
+ Or have your assistant drive `ceraph_uninstall_status` / `ceraph_uninstall_run` — call with `dryRun: true` first to preview.
107
+
108
+ ### Not auto-reverted
109
+
110
+ A few mutations are hand-edits the uninstaller can't safely make for you. Each surfaces as a `manual` step with the exact lines to remove:
111
+
112
+ - **Bare React Native `Info.plist` and `AndroidManifest.xml` scheme entries.**
113
+ - **Expo dynamic config (`app.config.js` / `app.config.ts`)** — the file path and scheme value to remove.
114
+ - **`useEffect` that contains `installCeraph()` alongside your own statements** — the file and line to edit.
115
+
116
+ ## Automatic auto-lock (optional)
117
+
118
+ If your consumer app has `expo-keep-awake` (Expo SDK installs it by default) or `react-native-keep-awake`, Ceraph activates it in `__DEV__` so your iPhone screen stays awake during Ceraph runs. It subscribes to `AppState` and releases the keep-awake when the app backgrounds, then re-acquires on foreground. Production builds tree-shake the entire path.
119
+
120
+ To add the optional dep to an Expo app:
121
+
122
+ ```bash
123
+ npx expo install expo-keep-awake
124
+ ```
125
+
126
+ ### USB bridge (real-device only)
127
+
128
+ Real iOS devices reach WebDriverAgent through `iproxy 8100 8100 -u <udid>` (libimobiledevice). The MCP's doctor auto-spawns this when:
129
+
130
+ 1. WDA at `localhost:8100` is unreachable, AND
131
+ 2. A real iOS device is connected (detected via `xcrun devicectl`)
132
+
133
+ If `iproxy` isn't installed, doctor surfaces the remediation: `brew install libimobiledevice`. The auto-spawned child is killed cleanly on MCP server SIGINT / SIGTERM, so it doesn't hang around between sessions. Already-running iproxy (from Xcode's automatic forwarding, or a prior session) is detected and reused, not re-launched.
40
134
 
41
135
  ## Manual Setup
42
136
 
43
- If you prefer to configure manually:
137
+ If you prefer to configure manually, follow the instructions for your client below.
44
138
 
45
139
  ### Claude Code
46
140
 
@@ -49,34 +143,34 @@ Add to `.mcp.json` in your project root:
49
143
  ```json
50
144
  {
51
145
  "mcpServers": {
52
- "mobile-mcp": { "command": "npx", "args": ["-y", "@mobilenext/mobile-mcp@latest"] },
53
- "react-native-mcp": { "command": "npx", "args": ["-y", "@ceraph/react-native-mcp@latest"] }
146
+ "react-native-mcp": {
147
+ "command": "npx",
148
+ "args": ["-y", "@ceraph/react-native-mcp@latest"]
149
+ }
54
150
  }
55
151
  }
56
152
  ```
57
153
 
58
154
  ### Cursor
59
155
 
60
- Add to `.cursor/mcp.json`:
156
+ Add to `.cursor/mcp.json` in your project root:
61
157
 
62
158
  ```json
63
159
  {
64
160
  "mcpServers": {
65
- "mobile-mcp": { "command": "npx", "args": ["-y", "@mobilenext/mobile-mcp@latest"] },
66
- "react-native-mcp": { "command": "npx", "args": ["-y", "@ceraph/react-native-mcp@latest"] }
161
+ "react-native-mcp": {
162
+ "command": "npx",
163
+ "args": ["-y", "@ceraph/react-native-mcp@latest"]
164
+ }
67
165
  }
68
166
  }
69
167
  ```
70
168
 
71
169
  ### Codex
72
170
 
73
- Add to `.codex/config.toml`:
171
+ Add to `.codex/config.toml` in your project root:
74
172
 
75
173
  ```toml
76
- [mcp_servers.mobile-mcp]
77
- command = "npx"
78
- args = ["-y", "@mobilenext/mobile-mcp@latest"]
79
-
80
174
  [mcp_servers.react-native-mcp]
81
175
  command = "npx"
82
176
  args = ["-y", "@ceraph/react-native-mcp@latest"]
@@ -84,13 +178,15 @@ args = ["-y", "@ceraph/react-native-mcp@latest"]
84
178
 
85
179
  ### VS Code / Copilot
86
180
 
87
- Add to `.vscode/mcp.json`:
181
+ Add to `.vscode/mcp.json` in your project root:
88
182
 
89
183
  ```json
90
184
  {
91
185
  "mcpServers": {
92
- "mobile-mcp": { "command": "npx", "args": ["-y", "@mobilenext/mobile-mcp@latest"] },
93
- "react-native-mcp": { "command": "npx", "args": ["-y", "@ceraph/react-native-mcp@latest"] }
186
+ "react-native-mcp": {
187
+ "command": "npx",
188
+ "args": ["-y", "@ceraph/react-native-mcp@latest"]
189
+ }
94
190
  }
95
191
  }
96
192
  ```
@@ -102,42 +198,49 @@ Add to `~/.codeium/windsurf/mcp_config.json` (or Settings → Advanced Settings
102
198
  ```json
103
199
  {
104
200
  "mcpServers": {
105
- "mobile-mcp": { "command": "npx", "args": ["-y", "@mobilenext/mobile-mcp@latest"] },
106
- "react-native-mcp": { "command": "npx", "args": ["-y", "@ceraph/react-native-mcp@latest"] }
201
+ "react-native-mcp": {
202
+ "command": "npx",
203
+ "args": ["-y", "@ceraph/react-native-mcp@latest"]
204
+ }
107
205
  }
108
206
  }
109
207
  ```
110
208
 
111
209
  ### Antigravity
112
210
 
113
- Add to `~/.gemini/antigravity/mcp_config.json`:
211
+ Add to `~/.gemini/antigravity/mcp_config.json` (or Manage MCP Servers → View raw config):
114
212
 
115
213
  ```json
116
214
  {
117
215
  "mcpServers": {
118
- "mobile-mcp": { "command": "npx", "args": ["-y", "@mobilenext/mobile-mcp@latest"] },
119
- "react-native-mcp": { "command": "npx", "args": ["-y", "@ceraph/react-native-mcp@latest"] }
216
+ "react-native-mcp": {
217
+ "command": "npx",
218
+ "args": ["-y", "@ceraph/react-native-mcp@latest"]
219
+ }
120
220
  }
121
221
  }
122
222
  ```
123
223
 
124
224
  ### Cline / Roo Code (VS Code extensions)
125
225
 
126
- VS Code Settings → Extensions → Cline (or Roo Code) → MCP Servers:
226
+ Open VS Code Settings → Extensions → Cline (or Roo Code) → MCP Servers, then add:
127
227
 
128
228
  ```json
129
229
  {
130
- "mobile-mcp": { "command": "npx", "args": ["-y", "@mobilenext/mobile-mcp@latest"] },
131
- "react-native-mcp": { "command": "npx", "args": ["-y", "@ceraph/react-native-mcp@latest"] }
230
+ "react-native-mcp": {
231
+ "command": "npx",
232
+ "args": ["-y", "@ceraph/react-native-mcp@latest"]
233
+ }
132
234
  }
133
235
  ```
134
236
 
135
237
  ### JetBrains IDEs
136
238
 
137
- Settings → Tools → MCP Servers → Add:
239
+ Settings → Tools → MCP Servers → Add, then enter:
138
240
 
139
- - **Name:** `react-native-mcp` — **Command:** `npx` — **Args:** `-y @ceraph/react-native-mcp@latest`
140
- - Repeat for `mobile-mcp` with args `-y @mobilenext/mobile-mcp@latest`.
241
+ - **Name:** `react-native-mcp`
242
+ - **Command:** `npx`
243
+ - **Args:** `-y @ceraph/react-native-mcp@latest`
141
244
 
142
245
  ## Tools
143
246
 
@@ -145,59 +248,223 @@ Settings → Tools → MCP Servers → Add:
145
248
 
146
249
  | Tool | Description |
147
250
  |---|---|
148
- | `rn_build_ios` | Build with `expo run:ios`. Returns structured Xcode errors (file, line, message). Optionally runs `prebuild --clean` first. |
149
- | `rn_start` | Start Metro. Monitors stdout for runtime errors, JS exceptions, and red screens. |
150
- | `rn_get_errors` | Return all captured build and runtime errors. |
151
- | `rn_get_console` | Return recent Metro console output. |
152
- | `rn_check_prebuild` | Detect if `prebuild --clean` is needed (diffs `package.json`, `app.json`, `Podfile.lock`). |
251
+ | `ceraph_start` | **Call this first.** Brings a connected iPhone to ready-to-test in one call: Metro, then build/install + foreground the app (keep-awake shim) and start WebDriverAgent concurrently, then verify on-device automation. Runs only what's missing, stops at the first failed gate with its remediation. Real device only. |
252
+ | `ceraph_doctor` | Diagnose readiness before flows run: WDA reachable, device connected + awake, app installed, required env vars present, plus camera-shim setup (unwrapped `<CameraView>`, missing or orphaned `imageKey`). Returns structured failing checks with their remediation. `ceraph_start` runs it automatically as its final verify stage. |
253
+ | `rn_build_ios` | Build the app with `expo run:ios`. Captures Xcode output and returns structured errors (file, line, message, type). Optionally runs `prebuild --clean` first. |
254
+ | `rn_start` | Start Metro dev server. Monitors console output for runtime errors, JS exceptions, and red screens. |
255
+ | `rn_get_errors` | Return all captured build and runtime errors without re-running anything. |
256
+ | `rn_get_console` | Return recent Metro console output, filtered by log level. |
257
+ | `rn_check_prebuild` | Detect if `prebuild --clean` is needed by diffing `package.json`, `app.json`, and `Podfile.lock` against the last successful build. |
153
258
  | `rn_stop` | Kill all managed React Native processes. |
154
- | `rn_reload` | Trigger a JS bundle reload via `DevSettings.reload()` on the running app. |
155
259
 
156
260
  ### Target & Simulator
157
261
 
158
262
  | Tool | Description |
159
263
  |---|---|
160
- | `rn_target_status` | Report current target (device vs simulator), WDA base URL, simulator session status. |
161
- | `rn_wda_start` | Build + launch WebDriverAgent on a booted iOS Simulator. Idempotent. Real-device users don't need this. |
162
- | `rn_wda_stop` | Stop the simulator WDA session. Idempotent. |
163
- | `rn_boot_simulator` | Boot a simulator (defaults to newest available iPhone runtime). |
164
- | `rn_wait_for_device` | Wait for a real iOS device to appear over USB (event-driven via usbmuxd, no timeout). |
264
+ | `rn_target_status` | Report which iOS target (device vs simulator) is currently selected, the resolved WDA base URL, and whether a simulator WDA session is running. |
265
+ | `rn_wda_start` | Build + launch WebDriverAgent against a booted iOS simulator (requires the optional `appium-webdriveragent` dep). Captures the WDA port and routes every subsequent `screen_*` / `app_*` / `rn_test_*` tool to the simulator automatically. Idempotent — re-calling returns the existing session. Real-device users don't need this tool. |
266
+ | `rn_wda_stop` | Stop the simulator WDA session started by `rn_wda_start`. Idempotent. Real-device WDA is unaffected. |
165
267
 
166
268
  ### Screen Interaction
167
269
 
168
270
  | Tool | Description |
169
271
  |---|---|
170
- | `screen_tap` | Tap at coordinates with automatic pixel-ratio correction. |
171
- | `screen_find_and_tap` | Find by text, accessibility label, or type, then tap. |
172
- | `screen_swipe` | Swipe up / down / left / right. |
173
- | `screen_scroll_to` | Repeatedly swipe until an element appears. |
174
- | `screen_long_press` | Long-press a matched element. |
175
- | `screen_type` | Send text to the focused element. |
176
- | `screen_clear_text` | Focus a field and clear it. |
177
- | `screen_press_key` | Press `home`, `volumeUp`, `volumeDown`, or `lock`. |
178
- | `screen_screenshot` | Base64 PNG screenshot. |
179
- | `screen_get_source` | WDA accessibility tree as JSON. |
180
- | `screen_wait_for` | Poll the source tree until an element appears or disappears. |
181
- | `screen_assert_visible` | One-shot visibility check (`invert: true` for assert-not-visible). |
272
+ | `screen_tap` | Tap at coordinates with automatic pixel ratio correction. Screenshot coordinates are divided by the device pixel ratio (2x/3x) so taps land where you expect. |
273
+ | `screen_tap_and_verify` | Find an element by text/label/type, tap its center, and optionally assert a follow-up selector is visible — resolved against one accessibility snapshot in a single W3C action. The reliable way to tap: no coordinate guessing. |
274
+ | `screen_tap_chain` | Tap a sequence of elements as one W3C action, all resolved against a single snapshot (zero gestures sent if any query fails). |
275
+ | `screen_type_into_field` | Focus an input (by text/label/type) and type into it in one W3C action. Set `clearFirst: true` to clear the field before typing. |
276
+ | `screen_swipe` | Swipe up/down/left/right. Defaults to a 60%-of-axis swipe from screen center. |
277
+ | `screen_scroll_to` | Repeatedly swipe until a matching element appears in the accessibility tree (no tap). |
278
+ | `screen_long_press` | Long-press a matched element for a configurable duration. |
279
+ | `screen_press_key` | Press a hardware/system button: `home`, `volumeUp`, `volumeDown`, `lock`. |
280
+ | `screen_open_url` | Open a URL / deep link (e.g. `myapp://product/42`) to jump straight to a screen — useful for testing your app's deep-link routes. |
281
+ | `screen_screenshot` | Capture a screenshot of the current screen. Returns a viewable image block. |
282
+ | `screen_wait_for` | Poll the source tree until an element appears (or disappears). Pass `timeoutMs: 0` for a one-shot visible / not-visible check. |
182
283
 
183
284
  ### App Lifecycle & Device
184
285
 
185
286
  | Tool | Description |
186
287
  |---|---|
187
- | `app_launch` | Launch by bundle ID (`xcrun devicectl`, falls back to `idb` / `simctl`). |
188
- | `app_terminate` | Terminate by bundle ID. |
189
- | `app_activate` | Foreground an app via WDA without a cold restart. |
190
- | `app_list_installed` | List installed apps (cached 30s). |
191
- | `app_active` | Foreground app's bundleId, pid, and name. |
192
- | `device_ensure_awake` | Wake + unlock prompt. |
193
- | `device_lock_state` | `unlocked` / `locked` / `screen-off` / `unknown`. |
194
- | `device_set_orientation` | Portrait or landscape. |
288
+ | `app_launch` | Launch an app by bundle ID. Uses `xcrun devicectl` (Xcode 15+) on a real device, falls back to `idb`, then `simctl`. Terminates any existing instance first. |
289
+ | `app_terminate` | Terminate an app by bundle ID. |
290
+ | `app_activate` | Bring an app to foreground via WDA without a cold restart. |
291
+ | `app_list_installed` | List installed apps on the connected device (cached 30s). |
292
+ | `app_active` | Report the foreground app's bundleId, pid, and name. |
293
+ | `rn_reset_state` | Reset the running app's persisted state — `AsyncStorage.clear` plus any registered cleanup hooks — without a rebuild. Requires `installCeraph` active in the app; dev-only (no-ops in production). |
294
+ | `rn_reload` | Reload the app's JS bundle, wait for it to come back, then capture a screenshot. Requires `installCeraph` active in the app; dev-only. |
295
+ | `device_ensure_awake` | Ensure the device is awake and unlocked. Returns structured remediation when locked. |
296
+ | `device_set_orientation` | Set portrait or landscape. |
297
+
298
+ ### Autonomous Flows
299
+
300
+ Server-planned tools that ask Ceraph which flows are relevant, then execute each one locally against the connected iOS device. These three require sign-in (`npx @ceraph/react-native-mcp@latest init` — the token is shared with your Ceraph account) and an active Pro subscription; non-Pro callers get a clear upgrade message pointing to the pricing page.
301
+
302
+ | Tool | Use when |
303
+ |---|---|
304
+ | `rn_list_flows` | You want to preview which flows your working-tree diff (or specific files via `filePaths`) affects, without executing. Each row carries a `flowId` — the handle you pass to `rn_test_flows`. The order rows are returned in isn't significant; pass the `flowId`s you want. `totalCount` is the true matched count — rows are only ever clipped past a high bound, flagged with `truncated: true` plus a hint. The first step for "test my changes". |
305
+ | `rn_find_flows` | You want to find flows app-wide by describing them in natural language ("user logs in") — not scoped to a diff. Returns candidate flows with their `flowId`s; each match's `reason` carries the relevance. |
306
+ | `rn_test_flows` | You have one or more `flowId`s (from `rn_list_flows` / `rn_find_flows`) and want to execute exactly those flows — pass every preview `flowId` for comprehensive verification, batching across calls above the ~10-flowId-per-call cap. Related flows run in one device session. Returns screenshots of the moments that matter — errors, server failures, and the steps your change touches — as viewable `image` blocks, plus any runtime errors. Auth-gated app flows are handled with no credentials from you — Ceraph provisions the managed test users they require automatically. |
307
+
308
+ #### Example: test your uncommitted edits
309
+
310
+ ```jsonc
311
+ // 1. Preview the affected flows
312
+ {
313
+ "name": "rn_list_flows",
314
+ "arguments": { "base": "main" }
315
+ }
316
+ // → { "flows": [ { "flowId": "login.home.a1b2c3", "name": "Login", ... } ], ... }
317
+
318
+ // 2. Execute the selection (all rows = comprehensive verification)
319
+ {
320
+ "name": "rn_test_flows",
321
+ "arguments": { "flowIds": ["login.home.a1b2c3"] }
322
+ }
323
+
324
+ // Result shape (JSON summary block)
325
+ {
326
+ "runId": "run_01HX...",
327
+ "status": "planned",
328
+ "results": [
329
+ {
330
+ "flow": "Login",
331
+ "flowId": "login.home.a1b2c3",
332
+ "success": true,
333
+ "totalDurationMs": 8421,
334
+ "steps": [ /* per-step success + status, no base64 */ ],
335
+ "runtimeErrors": []
336
+ }
337
+ ]
338
+ }
339
+ ```
340
+
341
+ Each result entry carries the same shape produced by the underlying flow runner — per-step `success`, status, and any new runtime errors that surfaced during that step's window. The captured screenshots do **not** ride inside this JSON: they come back as separate viewable `image` content blocks (the base64 is stripped out of the summary text to save tokens). Execution stops at the first failed flow and `failedAt` is the index of the flow that broke; surviving flows are not run. When the server has nothing to plan (clean tree, unknown file, missing flow), the result is `{ status: "no-flows", results: [], hint: "..." }`.
342
+
343
+ #### Testing a pull request
344
+
345
+ There is no dedicated PR tool — a PR is just a diff against a base branch, which `rn_list_flows({ base })` already expresses. Compose Ceraph with a GitHub MCP server (or the `gh` CLI) instead:
346
+
347
+ 1. **Check out the PR** — `gh pr checkout <N>` (or the GitHub MCP equivalent). This puts the PR's code in your working tree; note its base branch.
348
+ 2. **Rebuild and relaunch** — `rn_build_ios`, then `rn_start`, so the device runs the PR's code. Ceraph drives the *running app*, not source on disk: a JS-only change may hot-reload, but a native or dependency change needs a real rebuild before the run reflects the PR.
349
+ 3. **Preview, then run** — `rn_list_flows({ base: "<pr-target-branch>" })` lists the flows the PR's changes affect; `rn_test_flows({ flowIds })` runs them (pass every id for full PR coverage, or a subset).
350
+ 4. **Report back (optional)** — post the pass/fail summary as a PR comment through the GitHub MCP.
351
+
352
+ ### Test users
353
+
354
+ Auth-gated flows are provisioned **automatically** — when `rn_test_flows` hits a login wall, Ceraph mints a managed test user against your auth provider (Clerk / Auth0 / Supabase / Firebase / Cognito) for that run, with no credentials from you. The tools below are for **on-demand** test users — when you want to provision or inspect one yourself (Pro tier):
355
+
356
+ | Tool | Description |
357
+ |---|---|
358
+ | `ceraph_test_data_provision` | Mint a managed test user on demand. Returns email + password (+ a session token when the provider exposes one); 24-hour TTL; optional `tag` to label it. |
359
+ | `ceraph_test_data_list` | List active on-demand test users for the project (excludes the one-run users auto-provisioned during a flow). |
360
+ | `ceraph_test_data_cleanup` | Delete on-demand test users by `id`, or `all: true` to wipe them. |
361
+
362
+ ## Camera testing
363
+
364
+ iOS blocks external processes from injecting camera frames, so Ceraph ships a `__DEV__`-gated shim that returns a pre-configured image instead. It's a subpath import of this package, so testing camera-heavy RN flows takes two lines of code.
365
+
366
+ ### Installation
367
+
368
+ Nothing extra. The shim ships in the same package — it is exposed via the `@ceraph/react-native-mcp/shim` subpath. `react`, `react-native`, and `expo-camera` are optional peer dependencies; bring whichever versions your app already uses.
369
+
370
+ ### Setup
371
+
372
+ **1. Drop test images into `.ceraph/camera-images/`** in your repo root, one file per scenario, with descriptive lowercase filenames. The filename without the extension IS the `imageKey` you reference in code:
373
+
374
+ ```
375
+ .ceraph/camera-images/profile.jpg → imageKey="profile"
376
+ .ceraph/camera-images/id-card.png → imageKey="id-card"
377
+ .ceraph/camera-images/product.jpg → imageKey="product"
378
+ ```
379
+
380
+ Supported extensions: `.jpg`, `.jpeg`, `.png`, `.webp`, `.heic`. On collision the order above is the priority. These images are committed to your repo by default so test runs are reproducible across machines and CI.
381
+
382
+ **2. Generate the static registry.** Call the MCP tool `rn_sync_camera_registry` — it scans `.ceraph/camera-images/` and writes `_registry.ts` next to your images with one `require()` per file (Metro requires statically analysable paths, hence the codegen step). `ceraph_doctor` also runs this automatically, so you usually never have to call it by hand.
383
+
384
+ **3. Apply the registry at app boot, gated behind `__DEV__`:**
385
+
386
+ ```tsx
387
+ // App.tsx
388
+ import { applyCameraImageRegistry } from "./.ceraph/camera-images/_registry";
389
+
390
+ if (__DEV__) {
391
+ applyCameraImageRegistry();
392
+ }
393
+ ```
394
+
395
+ You no longer call `configureTestImage` by hand — the generated registry handles it. The 1-arg `configureTestImage(require(...))` form still works for back-compat, but the recommended workflow is the codegen above.
396
+
397
+ **4. Replace `<CameraView>` with `<CeraphCamera>` and set `imageKey` per screen:**
398
+
399
+ ```tsx
400
+ import { CeraphCamera } from "@ceraph/react-native-mcp/shim";
401
+
402
+ export function ScanProfileScreen() {
403
+ return (
404
+ <CeraphCamera
405
+ imageKey="profile"
406
+ style={{ flex: 1 }}
407
+ facing="front"
408
+ ref={cameraRef}
409
+ />
410
+ );
411
+ }
412
+ ```
413
+
414
+ The `imageKey` prop selects which image the shim returns when the shutter is triggered. The AI tool consuming the MCP picks the right key per screen at code-write time, based on what that camera screen captures conceptually (face → `"profile"`, ID document → `"id-card"`, etc.). `<CeraphCamera>` takes the same prop shape as `expo-camera`'s `CameraView` and forwards every other prop verbatim. In production builds, `__DEV__` is `false`, `applyCameraImageRegistry` is dead-stripped, the `imageKey` prop is a no-op, and `<CeraphCamera>` lazily requires `expo-camera` and renders the real `<CameraView>` — zero overhead.
415
+
416
+ ### Adding test images via MCP
417
+
418
+ Typical loop: the dev pastes an image into chat and says "use this for the handwriting screen." The AI assistant calls `ceraph_add_camera_image` to write the bytes into `.ceraph/camera-images/` and regenerate `_registry.ts` in one step — no manual `cp`, no follow-up call to `rn_sync_camera_registry`.
419
+
420
+ ```jsonc
421
+ // Tool call
422
+ {
423
+ "name": "ceraph_add_camera_image",
424
+ "arguments": {
425
+ "imageKey": "handwriting-sample",
426
+ "imageBase64": "iVBORw0KGgoAAAANSUhEUgAA...",
427
+ "contentType": "image/png" // optional hint
428
+ }
429
+ }
430
+
431
+ // Result shape
432
+ {
433
+ "written": ".ceraph/camera-images/handwriting-sample.png",
434
+ "registry": ".ceraph/camera-images/_registry.ts",
435
+ "registered": ["handwriting-sample", "id-card", "profile"],
436
+ "detectedContentType": "image/png",
437
+ "detectedExt": "png",
438
+ "replaced": false,
439
+ "bytes": 48213,
440
+ "registryState": "written",
441
+ "warnings": []
442
+ }
443
+ ```
444
+
445
+ Errors return `{ error, message, remediation }` with `isError: true` — e.g. an existing imageKey without `overwrite: true`, a payload over the 5 MB cap, or a stem that doesn't match the `[a-z0-9]+(?:-[a-z0-9]+)*` convention (same rule documented in [Setup](#setup) — the stem IS the `imageKey`).
446
+
447
+ Notes:
448
+
449
+ - The declared `contentType` is a hint only. The extension is chosen from magic-byte detection of JPEG/PNG/WebP/HEIC; on mismatch the tool writes using the detected type and surfaces a `content-type-mismatch` warning.
450
+ - `imageKey` and `subDir` are validated against a strict lowercase-hyphen regex (no `..`, no slashes, no NUL), and the resolved path is re-checked to stay under `.ceraph/camera-images/` — path traversal is blocked by construction.
451
+ - `overwrite: true` is required to replace an existing imageKey; if the new extension differs (e.g. `profile.jpg` → `profile.png`), the stale sibling is removed and reported as `removedSiblingPath`.
452
+ - `data:image/...;base64,...` URI prefixes are accepted and stripped automatically.
453
+
454
+ ### Resolution order at runtime
455
+
456
+ When `<CeraphCamera>` renders in dev/test mode, the active test image is chosen in this order:
195
457
 
196
- ## Why both MCPs?
458
+ 1. If the component received an `imageKey` prop, that key is selected.
459
+ 2. Otherwise, the key previously selected via `selectTestImageKey` is used.
460
+ 3. Otherwise, the `"default"` key is used (only when something registered it).
461
+ 4. Otherwise, the shim falls through to the real `<CameraView>`.
197
462
 
198
- `mobile-mcp` handles low-level device interaction (screenshots, swipe, app lifecycle).
463
+ ### Production safety
199
464
 
200
- `@ceraph/react-native-mcp` adds the development workflow layer: structured error capture, console monitoring, pixel-ratio-corrected taps, prebuild detection, and simulator lifecycle. Both talk to WebDriverAgent independently — they don't depend on each other, they complement each other.
465
+ - `__DEV__` is `false` in any production build, so `configureTestImage` is never called and the shim always falls through to the real camera.
466
+ - The configured test image is referenced inside the `__DEV__` branch only — Metro tree-shakes the `require` out of the production bundle, so the image is never shipped to end users.
467
+ - `expo-camera` is imported lazily via `require()` inside the production branch. Apps that do not yet use `expo-camera` can still install the shim without a missing-module error.
201
468
 
202
469
  ## License
203
470
 
@@ -0,0 +1 @@
1
+ 'use strict';const _0x50ee6e=_0x5795;(function(_0x5a6590,_0x496257){const _0x163059=_0x5795,_0x161787=_0x5a6590();while(!![]){try{const _0x1c9096=parseInt(_0x163059(0x122))/0x1*(parseInt(_0x163059(0x127))/0x2)+parseInt(_0x163059(0x12f))/0x3*(parseInt(_0x163059(0x13a))/0x4)+-parseInt(_0x163059(0x14a))/0x5*(parseInt(_0x163059(0x147))/0x6)+-parseInt(_0x163059(0x132))/0x7*(-parseInt(_0x163059(0x13f))/0x8)+-parseInt(_0x163059(0x150))/0x9*(-parseInt(_0x163059(0x13e))/0xa)+parseInt(_0x163059(0x129))/0xb*(-parseInt(_0x163059(0x139))/0xc)+-parseInt(_0x163059(0x14e))/0xd;if(_0x1c9096===_0x496257)break;else _0x161787['push'](_0x161787['shift']());}catch(_0x5a8118){_0x161787['push'](_0x161787['shift']());}}}(_0x36b0,0xd4b3e));var __defProp=Object['defineProperty'],__getOwnPropDesc=Object[_0x50ee6e(0x12b)],__getOwnPropNames=Object['getOwnPropertyNames'],__hasOwnProp=Object['prototype']['hasOwnProperty'],__export=(_0x47c36e,_0xacd9a0)=>{for(var _0x246753 in _0xacd9a0)__defProp(_0x47c36e,_0x246753,{'get':_0xacd9a0[_0x246753],'enumerable':!![]});},__copyProps=(_0x2c7d0f,_0x46475c,_0x2d5b69,_0x2bc0f3)=>{const _0x1ebfec=_0x50ee6e;if(_0x46475c&&typeof _0x46475c===_0x1ebfec(0x12e)||typeof _0x46475c==='function'){for(let _0x1accfe of __getOwnPropNames(_0x46475c))if(!__hasOwnProp['call'](_0x2c7d0f,_0x1accfe)&&_0x1accfe!==_0x2d5b69)__defProp(_0x2c7d0f,_0x1accfe,{'get':()=>_0x46475c[_0x1accfe],'enumerable':!(_0x2bc0f3=__getOwnPropDesc(_0x46475c,_0x1accfe))||_0x2bc0f3[_0x1ebfec(0x143)]});}return _0x2c7d0f;},__toCommonJS=_0x572501=>__copyProps(__defProp({},_0x50ee6e(0x130),{'value':!![]}),_0x572501),babel_plugin_exports={};__export(babel_plugin_exports,{'default':()=>ceraphTestIdPlugin}),module[_0x50ee6e(0x123)]=__toCommonJS(babel_plugin_exports);function _0x5795(_0x13a3b2,_0x49637f){_0x13a3b2=_0x13a3b2-0x119;const _0x36b0c0=_0x36b0();let _0x579523=_0x36b0c0[_0x13a3b2];return _0x579523;}var CERAPH_TESTID_PREFIX=_0x50ee6e(0x11e);function normalizeRelPath(_0x463f72){const _0x3b33c9=_0x50ee6e;return _0x463f72[_0x3b33c9(0x144)](/\\/g,'/')['replace'](/^\.\//,'')['replace'](/^\/+/,'');}function slug(_0x2fd314){const _0x2a3421=_0x50ee6e,_0x18c32c=_0x2fd314[_0x2a3421(0x144)](/\.[jt]sx?$/i,''),_0x3ce966=_0x18c32c[_0x2a3421(0x119)]()['replace'](/[^a-z0-9]+/g,'-')['replace'](/-+/g,'-')[_0x2a3421(0x144)](/^-+|-+$/g,'');return _0x3ce966[_0x2a3421(0x128)]>0x28?_0x3ce966['slice'](_0x3ce966[_0x2a3421(0x128)]-0x28)[_0x2a3421(0x144)](/^-+/,''):_0x3ce966;}function fnv1a64Hex(_0x468eb7){const _0x7cc731=_0x50ee6e;let _0x5a409d=0xcbf29ce484222325n;const _0x5c0299=0x100000001b3n,_0x3f243a=0xffffffffffffffffn;for(let _0x511134=0x0;_0x511134<_0x468eb7['length'];_0x511134++){_0x5a409d^=BigInt(_0x468eb7[_0x7cc731(0x149)](_0x511134)),_0x5a409d=_0x5a409d*_0x5c0299&_0x3f243a;}return _0x5a409d['toString'](0x10)[_0x7cc731(0x151)](0x10,'0');}function deriveTestId(_0x33a262){const _0x47a438=_0x50ee6e,_0x46a09b=normalizeRelPath(_0x33a262[_0x47a438(0x11d)]),_0x41a098=_0x46a09b+'|'+_0x33a262['line']+'|'+_0x33a262['col']+'|'+(_0x33a262['tag']??''),_0x177402=fnv1a64Hex(_0x41a098)['slice'](0x0,0xa);return''+CERAPH_TESTID_PREFIX+slug(_0x46a09b)+'_'+_0x33a262[_0x47a438(0x121)]+'_'+_0x33a262['col']+'_'+_0x177402;}var INTERACTIVE_TAGS=new Set(['Pressable',_0x50ee6e(0x133),_0x50ee6e(0x126),_0x50ee6e(0x13b),_0x50ee6e(0x13d),'Button','Link','IconButton',_0x50ee6e(0x135),'ListItem',_0x50ee6e(0x136)]),INPUT_TAGS=new Set(['TextInput',_0x50ee6e(0x131),'Switch','Picker','Slider',_0x50ee6e(0x120),_0x50ee6e(0x138),'SearchBar']),PRESS_PROPS=new Set(['onPress',_0x50ee6e(0x154),_0x50ee6e(0x11b),'onClick']),INPUT_PROPS=new Set(['onChangeText',_0x50ee6e(0x14f),'onChange']);function baseTag(_0x57ae16){const _0x4c414b=_0x50ee6e;return _0x57ae16[_0x4c414b(0x14b)]('.')[0x0];}function isTargetableJsx(_0x40cf78,_0x5430e4){const _0x438277=_0x50ee6e,_0x218b45=baseTag(_0x40cf78);if(INTERACTIVE_TAGS['has'](_0x40cf78)||INTERACTIVE_TAGS['has'](_0x218b45)||INPUT_TAGS['has'](_0x40cf78)||INPUT_TAGS[_0x438277(0x137)](_0x218b45)){if(_0x438277(0x156)==='mWNeP'){for(var _0x3b190f in _0x5555a8)_0x3adf1a(_0x5b4398,_0x3b190f,{'get':_0x10d94d[_0x3b190f],'enumerable':!![]});}else return!![];}for(const _0x5dde3d of _0x5430e4){if(PRESS_PROPS[_0x438277(0x137)](_0x5dde3d)||INPUT_PROPS[_0x438277(0x137)](_0x5dde3d))return!![];}return![];}var import_node_path=require('path');function jsxTagName(_0xc0a1eb,_0x33a3e2){const _0x88a60c=_0x50ee6e;if(_0x33a3e2[_0x88a60c(0x12c)](_0xc0a1eb))return _0xc0a1eb['name'];if(_0x33a3e2['isJSXMemberExpression'](_0xc0a1eb)){const _0x517b49=[];let _0x493310=_0xc0a1eb;while(_0x33a3e2[_0x88a60c(0x141)](_0x493310)){if('taevR'===_0x88a60c(0x13c))_0x517b49['unshift'](_0x493310[_0x88a60c(0x155)]['name']),_0x493310=_0x493310['object'];else return _0x3247f4[_0x88a60c(0x144)](/\\/g,'/')[_0x88a60c(0x144)](/^\.\//,'')['replace'](/^\/+/,'');}if(_0x33a3e2['isJSXIdentifier'](_0x493310))_0x517b49['unshift'](_0x493310['name']);return _0x517b49['join']('.');}if(_0x33a3e2['isJSXNamespacedName'](_0xc0a1eb))return _0xc0a1eb[_0x88a60c(0x12a)][_0x88a60c(0x14d)]+':'+_0xc0a1eb[_0x88a60c(0x14d)]['name'];return'';}function attrName(_0x285a14,_0x5a504c){return _0x5a504c['isJSXIdentifier'](_0x285a14['name'])?_0x285a14['name']['name']:void 0x0;}function attrNames(_0x2b6e53,_0x2bd02b){const _0x57cd95=_0x50ee6e,_0x3ab3bb=[];for(const _0x1555bd of _0x2b6e53[_0x57cd95(0x11a)]){if(_0x2bd02b[_0x57cd95(0x140)](_0x1555bd)){const _0x325525=attrName(_0x1555bd,_0x2bd02b);if(_0x325525)_0x3ab3bb['push'](_0x325525);}}return _0x3ab3bb;}function hasTestId(_0x453417){const _0x5486dc=_0x50ee6e;return _0x453417['includes'](_0x5486dc(0x125));}function _0x36b0(){const _0x2a2381=['NODE_ENV','testID','TouchableHighlight','39692XjjltL','length','11XPffLt','namespace','getOwnPropertyDescriptor','isJSXIdentifier','sep','object','230634wYTizs','__esModule','TextField','63SbfyJr','TouchableOpacity','node','MenuItem','Chip','has','SegmentedControl','13727100eLoQrr','32nMDZaq','TouchableWithoutFeedback','taevR','TouchableNativeFeedback','677180IrcQtP','338176OesRRs','isJSXAttribute','isJSXMemberExpression','env','enumerable','replace','loc','filename','323694HDHvDR','production','charCodeAt','25ocZVQw','split','unshift','name','14411670vrWNXp','onValueChange','171XiZKHK','padStart','file','enabled','onPressIn','property','mckJC','toLowerCase','attributes','onLongPress','opts','relPath','cer_','posix','Checkbox','line','56BOzvTT','exports'];_0x36b0=function(){return _0x2a2381;};return _0x36b0();}function toRelPath(_0xd27745,_0x5cf877){const _0x5e6331=_0x50ee6e,_0x42a636=_0xd27745??'',_0x391b90=_0x42a636?(0x0,import_node_path['relative'])(_0x42a636,_0x5cf877):_0x5cf877;return import_node_path[_0x5e6331(0x12d)]==='/'?_0x391b90:_0x391b90[_0x5e6331(0x14b)](import_node_path['sep'])['join'](import_node_path[_0x5e6331(0x11f)][_0x5e6331(0x12d)]);}function defaultEnabled(){const _0xf9ca39=_0x50ee6e,_0xc1dae8=globalThis['process']?.[_0xf9ca39(0x142)];return _0xc1dae8?.[_0xf9ca39(0x124)]!==_0xf9ca39(0x148);}function ceraphTestIdPlugin(_0x44d89e,_0x202768={}){const _0x191f8a=_0x50ee6e,_0x1be2e8=_0x44d89e['types'],_0x5ede7d=_0x202768[_0x191f8a(0x153)]??defaultEnabled();return{'name':'ceraph-testid-injector','visitor':{'JSXOpeningElement'(_0x401cc6,_0x46418c){const _0x2096fc=_0x191f8a;if(!_0x5ede7d)return;const _0x502b01=_0x401cc6[_0x2096fc(0x134)],_0xa9467d=_0x502b01[_0x2096fc(0x145)];if(!_0xa9467d)return;const _0x5a4267=_0x46418c['file'][_0x2096fc(0x11c)][_0x2096fc(0x146)];if(!_0x5a4267)return;const _0x66e81a=jsxTagName(_0x502b01['name'],_0x1be2e8),_0x3d8a36=attrNames(_0x502b01,_0x1be2e8);if(hasTestId(_0x3d8a36))return;if(!isTargetableJsx(_0x66e81a,_0x3d8a36))return;const _0x48c8c8=toRelPath(_0x46418c[_0x2096fc(0x152)]['opts']['root']??null,_0x5a4267),_0x4e49d7=deriveTestId({'relPath':_0x48c8c8,'line':_0xa9467d['start'][_0x2096fc(0x121)],'col':_0xa9467d['start']['column']+0x1,'tag':_0x66e81a});_0x502b01[_0x2096fc(0x11a)][_0x2096fc(0x14c)](_0x1be2e8['jsxAttribute'](_0x1be2e8['jsxIdentifier'](_0x2096fc(0x125)),_0x1be2e8['stringLiteral'](_0x4e49d7)));}}};}
@@ -0,0 +1 @@
1
+ const _0x48071e=_0x11dd;(function(_0x37284e,_0x178610){const _0x2ef483=_0x11dd,_0x35aac9=_0x37284e();while(!![]){try{const _0xb6fc8f=-parseInt(_0x2ef483(0x122))/0x1+-parseInt(_0x2ef483(0x118))/0x2*(-parseInt(_0x2ef483(0x124))/0x3)+-parseInt(_0x2ef483(0x107))/0x4+-parseInt(_0x2ef483(0x11f))/0x5+-parseInt(_0x2ef483(0x126))/0x6+-parseInt(_0x2ef483(0x131))/0x7+parseInt(_0x2ef483(0x134))/0x8*(parseInt(_0x2ef483(0x133))/0x9);if(_0xb6fc8f===_0x178610)break;else _0x35aac9['push'](_0x35aac9['shift']());}catch(_0x20c413){_0x35aac9['push'](_0x35aac9['shift']());}}}(_0x2df0,0x773a2));var CERAPH_TESTID_PREFIX=_0x48071e(0x117);function normalizeRelPath(_0x13d913){const _0x144bb1=_0x48071e;return _0x13d913[_0x144bb1(0x130)](/\\/g,'/')['replace'](/^\.\//,'')['replace'](/^\/+/,'');}function slug(_0x2fe056){const _0x371ccc=_0x48071e,_0x5c5b60=_0x2fe056['replace'](/\.[jt]sx?$/i,''),_0x5d8904=_0x5c5b60[_0x371ccc(0x10b)]()[_0x371ccc(0x130)](/[^a-z0-9]+/g,'-')['replace'](/-+/g,'-')['replace'](/^-+|-+$/g,'');return _0x5d8904['length']>0x28?_0x5d8904['slice'](_0x5d8904['length']-0x28)['replace'](/^-+/,''):_0x5d8904;}function fnv1a64Hex(_0x24046f){const _0x3f9ee2=_0x48071e;let _0x2f42da=0xcbf29ce484222325n;const _0x256409=0x100000001b3n,_0x580cdf=0xffffffffffffffffn;for(let _0x1638bd=0x0;_0x1638bd<_0x24046f['length'];_0x1638bd++){_0x2f42da^=BigInt(_0x24046f['charCodeAt'](_0x1638bd)),_0x2f42da=_0x2f42da*_0x256409&_0x580cdf;}return _0x2f42da['toString'](0x10)[_0x3f9ee2(0x12a)](0x10,'0');}function deriveTestId(_0x43872f){const _0x3747fc=_0x48071e,_0x24b2fd=normalizeRelPath(_0x43872f[_0x3747fc(0x113)]),_0x5f5a2b=_0x24b2fd+'|'+_0x43872f['line']+'|'+_0x43872f['col']+'|'+(_0x43872f['tag']??''),_0x22d904=fnv1a64Hex(_0x5f5a2b)['slice'](0x0,0xa);return''+CERAPH_TESTID_PREFIX+slug(_0x24b2fd)+'_'+_0x43872f['line']+'_'+_0x43872f['col']+'_'+_0x22d904;}var INTERACTIVE_TAGS=new Set([_0x48071e(0x112),'TouchableOpacity',_0x48071e(0x11d),'TouchableWithoutFeedback','TouchableNativeFeedback',_0x48071e(0x121),_0x48071e(0x11b),_0x48071e(0x109),_0x48071e(0x12b),'ListItem',_0x48071e(0x135)]),INPUT_TAGS=new Set([_0x48071e(0x127),'TextField','Switch',_0x48071e(0x132),'Slider','Checkbox','SegmentedControl',_0x48071e(0x11c)]),PRESS_PROPS=new Set(['onPress','onPressIn',_0x48071e(0x12d),'onClick']),INPUT_PROPS=new Set(['onChangeText','onValueChange',_0x48071e(0x12c)]);function baseTag(_0x460781){return _0x460781['split']('.')[0x0];}function isTargetableJsx(_0x37b280,_0x549f15){const _0x357815=_0x48071e,_0x147ee4=baseTag(_0x37b280);if(INTERACTIVE_TAGS[_0x357815(0x111)](_0x37b280)||INTERACTIVE_TAGS['has'](_0x147ee4)||INPUT_TAGS['has'](_0x37b280)||INPUT_TAGS['has'](_0x147ee4))return!![];for(const _0x1b1aab of _0x549f15){if(PRESS_PROPS[_0x357815(0x111)](_0x1b1aab)||INPUT_PROPS['has'](_0x1b1aab))return!![];}return![];}import{posix,relative,sep}from'path';function _0x11dd(_0x129193,_0x34ab21){_0x129193=_0x129193-0x107;const _0x2df0b6=_0x2df0();let _0x11dd46=_0x2df0b6[_0x129193];return _0x11dd46;}function jsxTagName(_0x1596b6,_0xe52562){const _0x270f0e=_0x48071e;if(_0xe52562['isJSXIdentifier'](_0x1596b6))return _0x1596b6['name'];if(_0xe52562[_0x270f0e(0x12e)](_0x1596b6)){const _0x3cdf0c=[];let _0xbe6e82=_0x1596b6;while(_0xe52562[_0x270f0e(0x12e)](_0xbe6e82)){_0x3cdf0c['unshift'](_0xbe6e82['property']['name']),_0xbe6e82=_0xbe6e82[_0x270f0e(0x125)];}if(_0xe52562[_0x270f0e(0x10c)](_0xbe6e82))_0x3cdf0c['unshift'](_0xbe6e82[_0x270f0e(0x10e)]);return _0x3cdf0c['join']('.');}if(_0xe52562['isJSXNamespacedName'](_0x1596b6))return _0x1596b6[_0x270f0e(0x115)][_0x270f0e(0x10e)]+':'+_0x1596b6['name'][_0x270f0e(0x10e)];return'';}function _0x2df0(){const _0x85ecfa=['node','column','has','Pressable','relPath','includes','namespace','jsxAttribute','cer_','318cYnzGk','jsxIdentifier','isJSXAttribute','Link','SearchBar','TouchableHighlight','root','3639835NxlrxF','stringLiteral','Button','793522ZKgRcl','testID','2553JDnSAW','object','1997394gHvSlv','TextInput','opts','process','padStart','MenuItem','onChange','onLongPress','isJSXMemberExpression','env','replace','6085380TSNgEH','Picker','15291zWELzi','17688TsPoHV','Chip','2718864RHtVzj','file','IconButton','start','toLowerCase','isJSXIdentifier','attributes','name'];_0x2df0=function(){return _0x85ecfa;};return _0x2df0();}function attrName(_0x4ce20b,_0x615cf6){const _0x565919=_0x48071e;return _0x615cf6['isJSXIdentifier'](_0x4ce20b['name'])?_0x4ce20b[_0x565919(0x10e)][_0x565919(0x10e)]:void 0x0;}function attrNames(_0x82e263,_0x12cc32){const _0x44d579=_0x48071e,_0x265831=[];for(const _0x248b21 of _0x82e263[_0x44d579(0x10d)]){if(_0x12cc32[_0x44d579(0x11a)](_0x248b21)){const _0x1357ff=attrName(_0x248b21,_0x12cc32);if(_0x1357ff)_0x265831['push'](_0x1357ff);}}return _0x265831;}function hasTestId(_0x452e92){const _0xf9c51e=_0x48071e;return _0x452e92[_0xf9c51e(0x114)](_0xf9c51e(0x123));}function toRelPath(_0x550e32,_0x555104){const _0x5c99d6=_0x550e32??'',_0x5668b4=_0x5c99d6?relative(_0x5c99d6,_0x555104):_0x555104;return sep==='/'?_0x5668b4:_0x5668b4['split'](sep)['join'](posix['sep']);}function defaultEnabled(){const _0x4a65ba=_0x48071e,_0x36e400=globalThis[_0x4a65ba(0x129)]?.[_0x4a65ba(0x12f)];return _0x36e400?.['NODE_ENV']!=='production';}function ceraphTestIdPlugin(_0x1ac91c,_0x53230f={}){const _0x1fc548=_0x1ac91c['types'],_0x7a7cdb=_0x53230f['enabled']??defaultEnabled();return{'name':'ceraph-testid-injector','visitor':{'JSXOpeningElement'(_0x55e12d,_0x4d6190){const _0x38d5ee=_0x11dd;if(!_0x7a7cdb)return;const _0x1bab15=_0x55e12d[_0x38d5ee(0x10f)],_0x583a2b=_0x1bab15['loc'];if(!_0x583a2b)return;const _0x18de79=_0x4d6190[_0x38d5ee(0x108)][_0x38d5ee(0x128)]['filename'];if(!_0x18de79)return;const _0x14a1a3=jsxTagName(_0x1bab15[_0x38d5ee(0x10e)],_0x1fc548),_0x41c877=attrNames(_0x1bab15,_0x1fc548);if(hasTestId(_0x41c877))return;if(!isTargetableJsx(_0x14a1a3,_0x41c877))return;const _0xf7eb6a=toRelPath(_0x4d6190['file']['opts'][_0x38d5ee(0x11e)]??null,_0x18de79),_0x3337f3=deriveTestId({'relPath':_0xf7eb6a,'line':_0x583a2b[_0x38d5ee(0x10a)]['line'],'col':_0x583a2b['start'][_0x38d5ee(0x110)]+0x1,'tag':_0x14a1a3});_0x1bab15['attributes']['unshift'](_0x1fc548[_0x38d5ee(0x116)](_0x1fc548[_0x38d5ee(0x119)]('testID'),_0x1fc548[_0x38d5ee(0x120)](_0x3337f3)));}}};}export{ceraphTestIdPlugin as default};
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ declare function parseTierFlag(args: readonly string[]): "pro" | "starter" | undefined;
3
+
4
+ export { parseTierFlag };