peachsim 0.1.527

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 (324) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +640 -0
  3. package/cli/app-config.ts +82 -0
  4. package/cli/app-fonts.ts +388 -0
  5. package/cli/app-project.ts +59 -0
  6. package/cli/app-splash.ts +171 -0
  7. package/cli/app-state-reset.ts +26 -0
  8. package/cli/auth.ts +184 -0
  9. package/cli/bin.ts +59 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2806 -0
  12. package/cli/browser-backend.ts +44 -0
  13. package/cli/browser-evals.ts +96 -0
  14. package/cli/cli-update.ts +261 -0
  15. package/cli/cloud-client.ts +1530 -0
  16. package/cli/cloud-dispatch.ts +206 -0
  17. package/cli/cloud-session.ts +227 -0
  18. package/cli/command-contract.ts +221 -0
  19. package/cli/command-registry.ts +304 -0
  20. package/cli/commands/agent-wrapper.ts +1048 -0
  21. package/cli/commands/agent.ts +439 -0
  22. package/cli/commands/app-fonts.ts +99 -0
  23. package/cli/commands/assert.ts +542 -0
  24. package/cli/commands/auth.ts +71 -0
  25. package/cli/commands/box/bundle-plane.ts +71 -0
  26. package/cli/commands/box/checkout-plane.ts +256 -0
  27. package/cli/commands/box/plane-share.ts +208 -0
  28. package/cli/commands/box/rnx-command.ts +60 -0
  29. package/cli/commands/box/search-command.ts +157 -0
  30. package/cli/commands/box.ts +925 -0
  31. package/cli/commands/camera.ts +266 -0
  32. package/cli/commands/cleanup.ts +169 -0
  33. package/cli/commands/compat.ts +176 -0
  34. package/cli/commands/config.ts +34 -0
  35. package/cli/commands/control.ts +2154 -0
  36. package/cli/commands/cpu-profile.ts +941 -0
  37. package/cli/commands/daemon-mac-app.ts +165 -0
  38. package/cli/commands/daemon.ts +800 -0
  39. package/cli/commands/debug.ts +751 -0
  40. package/cli/commands/desktop.ts +39 -0
  41. package/cli/commands/detect.ts +201 -0
  42. package/cli/commands/detox.ts +481 -0
  43. package/cli/commands/device.ts +134 -0
  44. package/cli/commands/diagnose.ts +589 -0
  45. package/cli/commands/do-chain.ts +408 -0
  46. package/cli/commands/electron.ts +94 -0
  47. package/cli/commands/film.ts +379 -0
  48. package/cli/commands/flow.ts +1240 -0
  49. package/cli/commands/inspect/actions.ts +516 -0
  50. package/cli/commands/inspect/core.ts +2086 -0
  51. package/cli/commands/inspect/count.ts +17 -0
  52. package/cli/commands/inspect/describe.ts +119 -0
  53. package/cli/commands/inspect/env.ts +25 -0
  54. package/cli/commands/inspect/find.ts +169 -0
  55. package/cli/commands/inspect/get-layout.ts +53 -0
  56. package/cli/commands/inspect/keyboard.ts +54 -0
  57. package/cli/commands/inspect/list.ts +175 -0
  58. package/cli/commands/inspect/memory.ts +215 -0
  59. package/cli/commands/inspect/redaction.ts +39 -0
  60. package/cli/commands/inspect/resolve-target.ts +77 -0
  61. package/cli/commands/inspect/screens.ts +78 -0
  62. package/cli/commands/inspect/settle.ts +32 -0
  63. package/cli/commands/inspect/settling.ts +197 -0
  64. package/cli/commands/inspect/shared.ts +259 -0
  65. package/cli/commands/inspect/sleep.ts +15 -0
  66. package/cli/commands/inspect/tree.ts +23 -0
  67. package/cli/commands/inspect/url.ts +17 -0
  68. package/cli/commands/inspect/wait-event.ts +211 -0
  69. package/cli/commands/inspect/wait-idle.ts +25 -0
  70. package/cli/commands/inspect/wait-ready.ts +87 -0
  71. package/cli/commands/inspect/wait-selector.ts +59 -0
  72. package/cli/commands/inspect/wait.ts +32 -0
  73. package/cli/commands/inspect.ts +4738 -0
  74. package/cli/commands/install-cli.ts +275 -0
  75. package/cli/commands/install-desktop.ts +385 -0
  76. package/cli/commands/login.ts +357 -0
  77. package/cli/commands/logout.ts +24 -0
  78. package/cli/commands/maestro-generate.ts +375 -0
  79. package/cli/commands/maestro.ts +536 -0
  80. package/cli/commands/mode.ts +58 -0
  81. package/cli/commands/no-bridge-hint.ts +80 -0
  82. package/cli/commands/perf.ts +67 -0
  83. package/cli/commands/permissions.ts +203 -0
  84. package/cli/commands/platform.ts +410 -0
  85. package/cli/commands/preview.ts +103 -0
  86. package/cli/commands/profile.ts +106 -0
  87. package/cli/commands/react.ts +353 -0
  88. package/cli/commands/record.ts +1435 -0
  89. package/cli/commands/report-issue.ts +305 -0
  90. package/cli/commands/reset.ts +89 -0
  91. package/cli/commands/runtime.ts +348 -0
  92. package/cli/commands/screenshot-command.ts +106 -0
  93. package/cli/commands/screenshot-layers.ts +144 -0
  94. package/cli/commands/screenshot-mode.ts +38 -0
  95. package/cli/commands/screenshot.ts +492 -0
  96. package/cli/commands/screenshots-capture.ts +608 -0
  97. package/cli/commands/screenshots.ts +127 -0
  98. package/cli/commands/serve.ts +139 -0
  99. package/cli/commands/setup.ts +416 -0
  100. package/cli/commands/shell-boolean-mode.ts +82 -0
  101. package/cli/commands/skills.ts +468 -0
  102. package/cli/commands/slides.ts +357 -0
  103. package/cli/commands/state.ts +94 -0
  104. package/cli/commands/storage.ts +58 -0
  105. package/cli/commands/telemetry.ts +55 -0
  106. package/cli/commands/test.ts +535 -0
  107. package/cli/commands/three-mode.ts +771 -0
  108. package/cli/commands/timeline.ts +121 -0
  109. package/cli/commands/upgrade.ts +167 -0
  110. package/cli/commands/upload.ts +1203 -0
  111. package/cli/commands/version.ts +63 -0
  112. package/cli/commands/welcome.ts +17 -0
  113. package/cli/commands/what-happened.ts +328 -0
  114. package/cli/current-sim.ts +217 -0
  115. package/cli/desktop-companion.ts +315 -0
  116. package/cli/drivers/electron.ts +70 -0
  117. package/cli/drivers/index.ts +20 -0
  118. package/cli/drivers/playwright-provisioning.ts +184 -0
  119. package/cli/drivers/playwright-sim-host.ts +537 -0
  120. package/cli/drivers/playwright.ts +209 -0
  121. package/cli/drivers/registry.ts +65 -0
  122. package/cli/drivers/types.ts +102 -0
  123. package/cli/flow-export.ts +55 -0
  124. package/cli/flow-file.ts +142 -0
  125. package/cli/flow-live-status.ts +126 -0
  126. package/cli/flow-session.ts +140 -0
  127. package/cli/help-core.ts +143 -0
  128. package/cli/help.ts +44 -0
  129. package/cli/hidden-runtime-alias.ts +19 -0
  130. package/cli/hints.ts +214 -0
  131. package/cli/inspect-notice-state.ts +114 -0
  132. package/cli/internal-child.ts +176 -0
  133. package/cli/maestro-js.ts +323 -0
  134. package/cli/main.ts +704 -0
  135. package/cli/open-url.ts +8 -0
  136. package/cli/outbound-endpoints.ts +830 -0
  137. package/cli/parent-pid.ts +204 -0
  138. package/cli/parse-args.ts +189 -0
  139. package/cli/privacy.ts +53 -0
  140. package/cli/prompt.ts +56 -0
  141. package/cli/recording-access.ts +108 -0
  142. package/cli/registry.ts +2 -0
  143. package/cli/resolve-assets.ts +63 -0
  144. package/cli/rnx-in-process.ts +98 -0
  145. package/cli/run-registry.ts +226 -0
  146. package/cli/run-rnx-core.ts +148 -0
  147. package/cli/run-rnx.ts +137 -0
  148. package/cli/runtime-notes.ts +66 -0
  149. package/cli/runtime-summary.ts +25 -0
  150. package/cli/secure-file.ts +31 -0
  151. package/cli/self-invocation.ts +58 -0
  152. package/cli/send-to-box.ts +109 -0
  153. package/cli/setup-repository.ts +191 -0
  154. package/cli/shell-init.ts +98 -0
  155. package/cli/standalone.ts +4 -0
  156. package/cli/telemetry.ts +84 -0
  157. package/cli/ui.ts +68 -0
  158. package/cli/ws-bridge.ts +1020 -0
  159. package/detox/attempt-events.cjs +23 -0
  160. package/detox/colors.ts +54 -0
  161. package/detox/config-loader.ts +135 -0
  162. package/detox/element-types.ts +48 -0
  163. package/detox/expectations.ts +534 -0
  164. package/detox/gestures.ts +501 -0
  165. package/detox/index.ts +2694 -0
  166. package/detox/jest-preset.cjs +29 -0
  167. package/detox/jest-setup-after-env.cjs +22 -0
  168. package/detox/matchers.ts +30 -0
  169. package/detox/motion-change.cjs +109 -0
  170. package/detox/motion-change.d.cts +35 -0
  171. package/detox/navigation-lifecycle.ts +290 -0
  172. package/detox/navigation.ts +43 -0
  173. package/detox/proof-frame.cjs +114 -0
  174. package/detox/record-block-execution.cjs +78 -0
  175. package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
  176. package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
  177. package/detox/screenshots/color-test-rest-norngh.png +0 -0
  178. package/detox/screenshots/theme-blue-toggle.png +0 -0
  179. package/detox/screenshots/theme-blue.png +0 -0
  180. package/detox/screenshots/theme-red-toggle.png +0 -0
  181. package/detox/screenshots/theme-red.png +0 -0
  182. package/dist-lib/agent-daemon-client.cjs +502 -0
  183. package/dist-lib/agent-events.cjs +48 -0
  184. package/dist-lib/agent-identity.cjs +53 -0
  185. package/dist-lib/agent-sessions.cjs +759 -0
  186. package/dist-lib/attached-projects.cjs +526 -0
  187. package/dist-lib/auth/shared-session.cjs +208 -0
  188. package/dist-lib/backend-origin.cjs +88 -0
  189. package/dist-lib/beta.cjs +41 -0
  190. package/dist-lib/beta.mjs +13 -0
  191. package/dist-lib/bridge-constants.cjs +161 -0
  192. package/dist-lib/bridge-contract-input.cjs +451 -0
  193. package/dist-lib/bridge-contract-input.mjs +414 -0
  194. package/dist-lib/bridge-contract.cjs +130 -0
  195. package/dist-lib/bridge-contract.mjs +94 -0
  196. package/dist-lib/capture-contract.cjs +220 -0
  197. package/dist-lib/capture-contract.mjs +190 -0
  198. package/dist-lib/cli-constants.cjs +32 -0
  199. package/dist-lib/cloud-contract.cjs +194 -0
  200. package/dist-lib/cloud-contract.mjs +145 -0
  201. package/dist-lib/cloud.cjs +670 -0
  202. package/dist-lib/cloud.mjs +639 -0
  203. package/dist-lib/config.cjs +96 -0
  204. package/dist-lib/detox/index.cjs +3789 -0
  205. package/dist-lib/detox/jest-preset.cjs +29 -0
  206. package/dist-lib/dev-bundle-resolution.cjs +290 -0
  207. package/dist-lib/home-paths.cjs +638 -0
  208. package/dist-lib/host/bridge-host.cjs +10903 -0
  209. package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
  210. package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
  211. package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
  212. package/dist-lib/host/replacement-module-handler.cjs +469 -0
  213. package/dist-lib/host/websocket-proxy.cjs +298 -0
  214. package/dist-lib/index.cjs +4268 -0
  215. package/dist-lib/jump-to-source-babel.cjs +93 -0
  216. package/dist-lib/jump-to-source-native.cjs +112 -0
  217. package/dist-lib/menu.cjs +646 -0
  218. package/dist-lib/menu.mjs +604 -0
  219. package/dist-lib/metro-fingerprint-registry.cjs +186 -0
  220. package/dist-lib/metro-fingerprint-registry.mjs +156 -0
  221. package/dist-lib/metro-production-bundle.cjs +329 -0
  222. package/dist-lib/metro-production-bundle.mjs +295 -0
  223. package/dist-lib/metro.cjs +904 -0
  224. package/dist-lib/profiles.cjs +246 -0
  225. package/dist-lib/public-brand.cjs +39 -0
  226. package/dist-lib/react-native-host-modules.cjs +51 -0
  227. package/dist-lib/react-native-host-modules.mjs +25 -0
  228. package/dist-lib/render-mode.cjs +54 -0
  229. package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
  230. package/dist-lib/sdk.cjs +2433 -0
  231. package/dist-lib/sdk.mjs +2336 -0
  232. package/dist-lib/skills.cjs +39132 -0
  233. package/dist-lib/swift-submit.cjs +92 -0
  234. package/dist-lib/swift-submit.mjs +65 -0
  235. package/dist-lib/swift.cjs +1646 -0
  236. package/dist-lib/vite.cjs +1591 -0
  237. package/package.json +290 -0
  238. package/scripts/dev-server-scanner.ts +1291 -0
  239. package/scripts/optional-demo-registry.ts +113 -0
  240. package/skills/contrast/SKILL.md +165 -0
  241. package/skills/rnx-debug/SKILL.md +433 -0
  242. package/skills/rnx-perf/SKILL.md +399 -0
  243. package/skills/rnx-setup/SKILL.md +172 -0
  244. package/skills/rnx-test/SKILL.md +321 -0
  245. package/skills/rnx-visual/SKILL.md +126 -0
  246. package/src/agent-daemon-client.ts +410 -0
  247. package/src/agent-events.ts +71 -0
  248. package/src/agent-identity.ts +23 -0
  249. package/src/agent-prompt.ts +71 -0
  250. package/src/agent-sessions.ts +580 -0
  251. package/src/app-url.ts +39 -0
  252. package/src/attached-projects.ts +539 -0
  253. package/src/auth/shared-session.ts +235 -0
  254. package/src/backend-origin.ts +70 -0
  255. package/src/beta.ts +19 -0
  256. package/src/bridge-constants.ts +175 -0
  257. package/src/bridge-contract-input.ts +502 -0
  258. package/src/bridge-contract.ts +558 -0
  259. package/src/browser-cache.ts +16 -0
  260. package/src/capture-contract.ts +587 -0
  261. package/src/cli-constants.ts +1 -0
  262. package/src/cli-version.ts +40 -0
  263. package/src/cloud-contract.ts +274 -0
  264. package/src/cloud.ts +762 -0
  265. package/src/codex-client.ts +234 -0
  266. package/src/config.ts +132 -0
  267. package/src/connect.ts +101 -0
  268. package/src/dev-bundle-resolution.ts +346 -0
  269. package/src/dev-server-open.ts +198 -0
  270. package/src/disk-cleanup.ts +540 -0
  271. package/src/flow-timeout-scale.ts +15 -0
  272. package/src/home-paths.ts +806 -0
  273. package/src/host/agent-host.ts +666 -0
  274. package/src/host/bridge-host.ts +2788 -0
  275. package/src/host/fetch-proxy-handler.ts +449 -0
  276. package/src/host/fetch-proxy-overrides.ts +51 -0
  277. package/src/host/open-url.ts +89 -0
  278. package/src/host/plane-host.ts +203 -0
  279. package/src/host/replacement-module-handler.ts +158 -0
  280. package/src/host/websocket-proxy.ts +315 -0
  281. package/src/index.ts +71 -0
  282. package/src/jump-to-source-babel.ts +135 -0
  283. package/src/jump-to-source-native.ts +102 -0
  284. package/src/menu.ts +777 -0
  285. package/src/metro-fingerprint-registry.ts +302 -0
  286. package/src/metro-plugin.ts +341 -0
  287. package/src/metro-production-bundle.ts +471 -0
  288. package/src/native-dev-bundle-url.ts +118 -0
  289. package/src/native-seam-manifest.ts +341 -0
  290. package/src/playwright-host-log.ts +16 -0
  291. package/src/plist.ts +27 -0
  292. package/src/poll-until.ts +123 -0
  293. package/src/profiles.ts +179 -0
  294. package/src/public-brand.ts +9 -0
  295. package/src/react-native-host-modules.ts +26 -0
  296. package/src/render-mode.ts +27 -0
  297. package/src/rolldown-plugin-swift.ts +240 -0
  298. package/src/runtime-assets.ts +233 -0
  299. package/src/runtime-delivery.ts +54 -0
  300. package/src/screenshots/compose.ts +422 -0
  301. package/src/screenshots/frame-compose.ts +514 -0
  302. package/src/screenshots/orchestrate.ts +256 -0
  303. package/src/screenshots/org-deck.ts +91 -0
  304. package/src/screenshots/registry.ts +58 -0
  305. package/src/screenshots/schema.ts +364 -0
  306. package/src/sdk.ts +15 -0
  307. package/src/settings-bundle.ts +418 -0
  308. package/src/shell-bridge-identity.ts +15 -0
  309. package/src/sim-client.ts +697 -0
  310. package/src/skills/builtin/a11y-review.ts +132 -0
  311. package/src/skills/builtin/compat-check.ts +71 -0
  312. package/src/skills/builtin/maestro-test.ts +121 -0
  313. package/src/skills/builtin/perf-profile.ts +90 -0
  314. package/src/skills/builtin/screenshot-all.ts +52 -0
  315. package/src/skills/builtin/visual-diff.ts +100 -0
  316. package/src/skills/registry.ts +107 -0
  317. package/src/skills/types.ts +41 -0
  318. package/src/swift-submit.ts +185 -0
  319. package/src/swift.ts +7 -0
  320. package/src/team-machine-prompt.ts +73 -0
  321. package/src/vite-plugin-one.ts +264 -0
  322. package/src/vite-plugin-swift.ts +620 -0
  323. package/src/vite-plugin.ts +1946 -0
  324. package/src/worklets-babel.ts +250 -0
@@ -0,0 +1,302 @@
1
+ export const RNX_METRO_FINGERPRINT_SCHEMA_VERSION = 2
2
+ export const RNX_METRO_FINGERPRINT_POINTER_PATH = '/compile/metro-fingerprints.json'
3
+ export const RNX_METRO_FINGERPRINT_MAX_POINTER_BYTES = 16 * 1024
4
+ export const RNX_METRO_FINGERPRINT_MAX_PAYLOAD_BYTES = 8 * 1024 * 1024
5
+
6
+ export type RNXMetroFingerprintPlatform = 'ios' | 'android'
7
+
8
+ export interface RNXMetroFingerprintAtom {
9
+ key: string
10
+ exports: string[]
11
+ literals: string[]
12
+ dependencyArity: number
13
+ factoryDigest: string
14
+ }
15
+
16
+ export interface RNXMetroFingerprintNode {
17
+ atom: number
18
+ internalDependencies: Array<{ position: number; node: number }>
19
+ landmarkPath?: string
20
+ }
21
+
22
+ export interface RNXMetroFingerprintRoot {
23
+ node: number
24
+ }
25
+
26
+ export interface RNXMetroFingerprintTemplate {
27
+ packageName: string
28
+ packageVersion: string
29
+ sourceIntegrity: string
30
+ profile: string
31
+ platform: RNXMetroFingerprintPlatform
32
+ nodes: RNXMetroFingerprintNode[]
33
+ roots: RNXMetroFingerprintRoot[]
34
+ }
35
+
36
+ export interface RNXMetroFingerprintCandidate {
37
+ template: number
38
+ root: number
39
+ }
40
+
41
+ export interface RNXMetroFingerprintRegistry {
42
+ schemaVersion: typeof RNX_METRO_FINGERPRINT_SCHEMA_VERSION
43
+ revision: string
44
+ atoms: RNXMetroFingerprintAtom[]
45
+ templates: RNXMetroFingerprintTemplate[]
46
+ candidatesByFactoryDigest: Record<string, RNXMetroFingerprintCandidate[]>
47
+ candidatesByDiscriminator: Record<string, RNXMetroFingerprintCandidate[]>
48
+ }
49
+
50
+ export interface RNXMetroFingerprintPointer {
51
+ schemaVersion: typeof RNX_METRO_FINGERPRINT_SCHEMA_VERSION
52
+ revision: string
53
+ integrity: string
54
+ bytes: number
55
+ payloadUrl: string
56
+ }
57
+
58
+ function isRecord(value: unknown): value is Record<string, unknown> {
59
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
60
+ }
61
+
62
+ function isSha256(value: unknown): value is string {
63
+ return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value)
64
+ }
65
+
66
+ function readStringArray(value: unknown): string[] | null {
67
+ if (!Array.isArray(value) || !value.every((entry) => typeof entry === 'string')) {
68
+ return null
69
+ }
70
+ const entries = [...value]
71
+ if (
72
+ new Set(entries).size !== entries.length ||
73
+ entries.some((entry, index) => index > 0 && entries[index - 1] >= entry)
74
+ ) {
75
+ return null
76
+ }
77
+ return entries
78
+ }
79
+
80
+ export function parseRNXMetroFingerprintPointer(
81
+ value: unknown,
82
+ ): RNXMetroFingerprintPointer | null {
83
+ if (!isRecord(value)) return null
84
+ const { schemaVersion, revision, integrity, bytes, payloadUrl } = value
85
+ if (
86
+ schemaVersion !== RNX_METRO_FINGERPRINT_SCHEMA_VERSION ||
87
+ !isSha256(revision) ||
88
+ !isSha256(integrity) ||
89
+ typeof bytes !== 'number' ||
90
+ !Number.isSafeInteger(bytes) ||
91
+ bytes < 1 ||
92
+ typeof payloadUrl !== 'string' ||
93
+ payloadUrl.length === 0
94
+ ) {
95
+ return null
96
+ }
97
+ return { schemaVersion, revision, integrity, bytes, payloadUrl }
98
+ }
99
+
100
+ export function parseRNXMetroFingerprintRegistry(
101
+ value: unknown,
102
+ ): RNXMetroFingerprintRegistry | null {
103
+ if (!isRecord(value)) return null
104
+ const { schemaVersion, revision } = value
105
+ if (
106
+ schemaVersion !== RNX_METRO_FINGERPRINT_SCHEMA_VERSION ||
107
+ !isSha256(revision) ||
108
+ !Array.isArray(value.atoms) ||
109
+ !Array.isArray(value.templates) ||
110
+ !isRecord(value.candidatesByFactoryDigest) ||
111
+ !isRecord(value.candidatesByDiscriminator)
112
+ ) {
113
+ return null
114
+ }
115
+
116
+ const atoms: RNXMetroFingerprintAtom[] = []
117
+ for (const atomValue of value.atoms) {
118
+ if (!isRecord(atomValue)) return null
119
+ const exports = readStringArray(atomValue.exports)
120
+ const literals = readStringArray(atomValue.literals)
121
+ if (
122
+ typeof atomValue.key !== 'string' ||
123
+ atomValue.key.length === 0 ||
124
+ !exports ||
125
+ !literals ||
126
+ typeof atomValue.dependencyArity !== 'number' ||
127
+ !Number.isSafeInteger(atomValue.dependencyArity) ||
128
+ atomValue.dependencyArity < 0 ||
129
+ !isSha256(atomValue.factoryDigest)
130
+ ) {
131
+ return null
132
+ }
133
+ atoms.push({
134
+ key: atomValue.key,
135
+ exports,
136
+ literals,
137
+ dependencyArity: atomValue.dependencyArity,
138
+ factoryDigest: atomValue.factoryDigest,
139
+ })
140
+ }
141
+
142
+ const templates: RNXMetroFingerprintTemplate[] = []
143
+ for (const templateValue of value.templates) {
144
+ if (
145
+ !isRecord(templateValue) ||
146
+ typeof templateValue.packageName !== 'string' ||
147
+ templateValue.packageName.length === 0 ||
148
+ typeof templateValue.packageVersion !== 'string' ||
149
+ templateValue.packageVersion.length === 0 ||
150
+ !isSha256(templateValue.sourceIntegrity) ||
151
+ typeof templateValue.profile !== 'string' ||
152
+ templateValue.profile.length === 0 ||
153
+ (templateValue.platform !== 'ios' && templateValue.platform !== 'android') ||
154
+ !Array.isArray(templateValue.nodes) ||
155
+ templateValue.nodes.length === 0 ||
156
+ !Array.isArray(templateValue.roots) ||
157
+ templateValue.roots.length === 0
158
+ ) {
159
+ return null
160
+ }
161
+ const nodes: RNXMetroFingerprintNode[] = []
162
+ for (const nodeValue of templateValue.nodes) {
163
+ if (
164
+ !isRecord(nodeValue) ||
165
+ typeof nodeValue.atom !== 'number' ||
166
+ !Number.isSafeInteger(nodeValue.atom) ||
167
+ nodeValue.atom < 0 ||
168
+ nodeValue.atom >= atoms.length ||
169
+ !Array.isArray(nodeValue.internalDependencies) ||
170
+ (nodeValue.landmarkPath !== undefined &&
171
+ (typeof nodeValue.landmarkPath !== 'string' ||
172
+ nodeValue.landmarkPath.length === 0))
173
+ ) {
174
+ return null
175
+ }
176
+ const internalDependencies: Array<{ position: number; node: number }> = []
177
+ const positions = new Set<number>()
178
+ for (const edgeValue of nodeValue.internalDependencies) {
179
+ if (
180
+ !isRecord(edgeValue) ||
181
+ typeof edgeValue.position !== 'number' ||
182
+ !Number.isSafeInteger(edgeValue.position) ||
183
+ edgeValue.position < 0 ||
184
+ positions.has(edgeValue.position) ||
185
+ typeof edgeValue.node !== 'number' ||
186
+ !Number.isSafeInteger(edgeValue.node) ||
187
+ edgeValue.node < 0 ||
188
+ edgeValue.node >= templateValue.nodes.length
189
+ ) {
190
+ return null
191
+ }
192
+ positions.add(edgeValue.position)
193
+ internalDependencies.push({
194
+ position: edgeValue.position,
195
+ node: edgeValue.node,
196
+ })
197
+ }
198
+ internalDependencies.sort((left, right) => left.position - right.position)
199
+ nodes.push({
200
+ atom: nodeValue.atom,
201
+ internalDependencies,
202
+ ...(typeof nodeValue.landmarkPath === 'string'
203
+ ? { landmarkPath: nodeValue.landmarkPath }
204
+ : {}),
205
+ })
206
+ }
207
+ const roots: RNXMetroFingerprintRoot[] = []
208
+ for (const rootValue of templateValue.roots) {
209
+ if (
210
+ !isRecord(rootValue) ||
211
+ typeof rootValue.node !== 'number' ||
212
+ !Number.isSafeInteger(rootValue.node) ||
213
+ rootValue.node < 0 ||
214
+ rootValue.node >= nodes.length ||
215
+ !nodes[rootValue.node].landmarkPath
216
+ ) {
217
+ return null
218
+ }
219
+ roots.push({ node: rootValue.node })
220
+ }
221
+ templates.push({
222
+ packageName: templateValue.packageName,
223
+ packageVersion: templateValue.packageVersion,
224
+ sourceIntegrity: templateValue.sourceIntegrity,
225
+ profile: templateValue.profile,
226
+ platform: templateValue.platform,
227
+ nodes,
228
+ roots,
229
+ })
230
+ }
231
+
232
+ const candidatesByFactoryDigest: Record<string, RNXMetroFingerprintCandidate[]> = {}
233
+ for (const [factoryDigest, candidateValues] of Object.entries(
234
+ value.candidatesByFactoryDigest,
235
+ )) {
236
+ if (!isSha256(factoryDigest) || !Array.isArray(candidateValues)) return null
237
+ const candidates: RNXMetroFingerprintCandidate[] = []
238
+ for (const candidateValue of candidateValues) {
239
+ if (
240
+ !isRecord(candidateValue) ||
241
+ typeof candidateValue.template !== 'number' ||
242
+ !Number.isSafeInteger(candidateValue.template) ||
243
+ candidateValue.template < 0 ||
244
+ candidateValue.template >= templates.length ||
245
+ typeof candidateValue.root !== 'number' ||
246
+ !Number.isSafeInteger(candidateValue.root) ||
247
+ candidateValue.root < 0 ||
248
+ candidateValue.root >= templates[candidateValue.template].roots.length
249
+ ) {
250
+ return null
251
+ }
252
+ const template = templates[candidateValue.template]
253
+ const root = template.roots[candidateValue.root]
254
+ if (atoms[template.nodes[root.node].atom].factoryDigest !== factoryDigest) {
255
+ return null
256
+ }
257
+ candidates.push({
258
+ template: candidateValue.template,
259
+ root: candidateValue.root,
260
+ })
261
+ }
262
+ candidatesByFactoryDigest[factoryDigest] = candidates
263
+ }
264
+
265
+ const candidatesByDiscriminator: Record<string, RNXMetroFingerprintCandidate[]> = {}
266
+ for (const [discriminator, candidateValues] of Object.entries(
267
+ value.candidatesByDiscriminator,
268
+ )) {
269
+ if (!/^(?:e|l|ea|la|el|ela):[a-f0-9]{16}$/.test(discriminator)) return null
270
+ if (!Array.isArray(candidateValues) || candidateValues.length === 0) return null
271
+ const candidates: RNXMetroFingerprintCandidate[] = []
272
+ for (const candidateValue of candidateValues) {
273
+ if (
274
+ !isRecord(candidateValue) ||
275
+ typeof candidateValue.template !== 'number' ||
276
+ !Number.isSafeInteger(candidateValue.template) ||
277
+ candidateValue.template < 0 ||
278
+ candidateValue.template >= templates.length ||
279
+ typeof candidateValue.root !== 'number' ||
280
+ !Number.isSafeInteger(candidateValue.root) ||
281
+ candidateValue.root < 0 ||
282
+ candidateValue.root >= templates[candidateValue.template].roots.length
283
+ ) {
284
+ return null
285
+ }
286
+ candidates.push({
287
+ template: candidateValue.template,
288
+ root: candidateValue.root,
289
+ })
290
+ }
291
+ candidatesByDiscriminator[discriminator] = candidates
292
+ }
293
+
294
+ return {
295
+ schemaVersion,
296
+ revision,
297
+ atoms,
298
+ templates,
299
+ candidatesByFactoryDigest,
300
+ candidatesByDiscriminator,
301
+ }
302
+ }
@@ -0,0 +1,341 @@
1
+ // rnx metro plugin for dev shell and bundle module identity annotation.
2
+ // shell assets come from an engine runtime under ~/.rnx/runtimes/, fetched
3
+ // by `rnx runtime install`. ordinary localhost requests
4
+ // follow active; a version-owned localhost origin stays on its exact directory
5
+ // when active changes, so one loaded shell cannot mix runtime chunk trees.
6
+ //
7
+ // usage in metro.config.js:
8
+ // const { withRNX } = require('rnxsim/metro')
9
+ // module.exports = withRNX(getDefaultConfig(__dirname))
10
+
11
+ import fs from 'fs'
12
+ import { createRequire } from 'module'
13
+ import { randomUUID } from 'node:crypto'
14
+ import path from 'path'
15
+ import { tagRnxBundleForPlugin } from './dev-server-open'
16
+ import {
17
+ handleReplacementModuleRequest,
18
+ isReplacementModuleRequestUrl,
19
+ } from './host/replacement-module-handler'
20
+ import {
21
+ appendRNXMetroModuleIdentityFooter,
22
+ RNX_METRO_MODULE_IDENTITY_VERSION,
23
+ toRNXDevelopmentBundleUrl,
24
+ toRNXProductionBundleUrl,
25
+ type RNXMetroModuleIdentityMetadata,
26
+ } from './metro-production-bundle'
27
+ import {
28
+ isRootRuntimeAssetPath,
29
+ resolveRuntimeFilePath,
30
+ resolveRuntimeRootForRequestHost,
31
+ serveRuntimeFile,
32
+ SOOTSIM_RUNTIME_MISSING_MESSAGE,
33
+ } from './runtime-assets'
34
+
35
+ const prefix = '/__soot'
36
+ export { toRNXDevelopmentBundleUrl, toRNXProductionBundleUrl }
37
+
38
+ type MetroSerializerModule = {
39
+ path: string
40
+ dependencies?: ReadonlyMap<
41
+ string,
42
+ {
43
+ absolutePath?: string
44
+ data?: { name?: string }
45
+ }
46
+ >
47
+ }
48
+
49
+ type MetroSerializerGraph = {
50
+ dependencies?: ReadonlyMap<unknown, MetroSerializerModule>
51
+ }
52
+
53
+ type MetroSerializerOptions = {
54
+ createModuleId?: (modulePath: string) => number
55
+ dev?: boolean
56
+ [key: string]: unknown
57
+ }
58
+
59
+ type MetroSerializerResult = string | { code: string; map: string }
60
+
61
+ type MetroSerializer = (
62
+ entryPoint: string,
63
+ preModules: readonly unknown[],
64
+ graph: MetroSerializerGraph,
65
+ options: MetroSerializerOptions,
66
+ ) => MetroSerializerResult | Promise<MetroSerializerResult>
67
+
68
+ type MetroConfigWithServer = {
69
+ projectRoot?: string
70
+ serializer?: {
71
+ customSerializer?: MetroSerializer
72
+ [key: string]: unknown
73
+ }
74
+ server?: {
75
+ enhanceMiddleware?: (middleware: unknown, server: unknown) => unknown
76
+ port?: number
77
+ [key: string]: unknown
78
+ }
79
+ }
80
+
81
+ function readCallableMetroExport(
82
+ moduleValue: unknown,
83
+ label: string,
84
+ ): (...args: readonly unknown[]) => unknown {
85
+ const callable =
86
+ typeof moduleValue === 'function'
87
+ ? moduleValue
88
+ : typeof moduleValue === 'object' && moduleValue !== null
89
+ ? Reflect.get(moduleValue, 'default')
90
+ : undefined
91
+ if (typeof callable !== 'function') {
92
+ throw new Error(`rnxsim: Metro ${label} module did not export a function`)
93
+ }
94
+ return (...args) => Reflect.apply(callable, undefined, args)
95
+ }
96
+
97
+ function createMetroDefaultSerializer(projectRoot: string): MetroSerializer {
98
+ const projectRequire = createRequire(path.join(projectRoot, 'package.json'))
99
+ const metroRoot = path.dirname(projectRequire.resolve('metro/package.json'))
100
+ const baseJSBundle = readCallableMetroExport(
101
+ projectRequire(path.join(metroRoot, 'src/DeltaBundler/Serializers/baseJSBundle.js')),
102
+ 'baseJSBundle',
103
+ )
104
+ const bundleToString = readCallableMetroExport(
105
+ projectRequire(path.join(metroRoot, 'src/lib/bundleToString.js')),
106
+ 'bundleToString',
107
+ )
108
+
109
+ return (entryPoint, preModules, graph, options) => {
110
+ const serialized = bundleToString(
111
+ baseJSBundle(entryPoint, preModules, graph, options),
112
+ )
113
+ const code =
114
+ typeof serialized === 'object' && serialized !== null
115
+ ? Reflect.get(serialized, 'code')
116
+ : undefined
117
+ if (typeof code !== 'string') {
118
+ throw new Error('rnxsim: Metro bundleToString did not return bundle code')
119
+ }
120
+ return code
121
+ }
122
+ }
123
+
124
+ // the only thing rnx adds to an ordinary Metro bundle. production needs the
125
+ // footer because Metro drops the fourth `__d` argument when `dev` is false;
126
+ // development keeps that verbose name and receives the same identity without
127
+ // changing resolution, transforms, or the trailing source map.
128
+ function appendRNXModulePaths(
129
+ result: MetroSerializerResult,
130
+ graph: MetroSerializerGraph,
131
+ options: MetroSerializerOptions,
132
+ projectRoot: string,
133
+ ): MetroSerializerResult {
134
+ const dependencies = graph.dependencies
135
+ const createModuleId = options.createModuleId
136
+ if (!dependencies || !createModuleId) {
137
+ return result
138
+ }
139
+
140
+ const modulePaths: Record<string, string> = {}
141
+ for (const module of dependencies.values()) {
142
+ modulePaths[String(createModuleId(module.path))] = path
143
+ .relative(projectRoot, module.path)
144
+ .replaceAll(path.sep, '/')
145
+ }
146
+ if (Object.keys(modulePaths).length !== dependencies.size) {
147
+ throw new Error('rnxsim: bundle module IDs were not unique')
148
+ }
149
+
150
+ const specifiersByModuleId = new Map<string, Set<string>>()
151
+ for (const module of dependencies.values()) {
152
+ for (const [dependencyKey, dependency] of module.dependencies ?? []) {
153
+ const specifier = dependency.data?.name ?? dependencyKey
154
+ if (
155
+ specifier.length === 0 ||
156
+ specifier.startsWith('.') ||
157
+ specifier.startsWith('/') ||
158
+ typeof dependency.absolutePath !== 'string'
159
+ ) {
160
+ continue
161
+ }
162
+ const moduleId = String(createModuleId(dependency.absolutePath))
163
+ if (!Object.prototype.hasOwnProperty.call(modulePaths, moduleId)) continue
164
+ const found = specifiersByModuleId.get(moduleId)
165
+ if (found) found.add(specifier)
166
+ else specifiersByModuleId.set(moduleId, new Set([specifier]))
167
+ }
168
+ }
169
+ const logicalSpecifiers = Object.fromEntries(
170
+ [...specifiersByModuleId]
171
+ .sort(([left], [right]) => Number(left) - Number(right))
172
+ .map(([moduleId, specifiers]) => [moduleId, [...specifiers].sort()]),
173
+ )
174
+ const identity: RNXMetroModuleIdentityMetadata = {
175
+ version: RNX_METRO_MODULE_IDENTITY_VERSION,
176
+ identitySource: 'rnx-plugin',
177
+ modulePaths,
178
+ logicalSpecifiers,
179
+ }
180
+ return typeof result === 'string'
181
+ ? appendRNXMetroModuleIdentityFooter(result, identity)
182
+ : { ...result, code: appendRNXMetroModuleIdentityFooter(result.code, identity) }
183
+ }
184
+
185
+ export function withRNX<T extends Record<string, unknown>>(config: T): T {
186
+ const cfgProjectRoot = (config as MetroConfigWithServer).projectRoot || process.cwd()
187
+ const configuredBundleUrl = '/index.bundle?platform=ios&dev=true&hot=true&minify=false'
188
+
189
+ const cfgServer = (config as MetroConfigWithServer).server
190
+ const port = resolveMetroPort(cfgServer)
191
+ const metroServerId = randomUUID()
192
+ const bundleUrl = tagRnxBundleForPlugin(
193
+ configuredBundleUrl,
194
+ `http://localhost:${port}`,
195
+ metroServerId,
196
+ cfgProjectRoot,
197
+ )
198
+ const cfgSerializer = (config as MetroConfigWithServer).serializer
199
+ const existingEnhance = cfgServer?.enhanceMiddleware
200
+ const existingSerializer = cfgSerializer?.customSerializer
201
+
202
+ let defaultSerializer: MetroSerializer | undefined
203
+
204
+ const serializerConfig = {
205
+ serializer: {
206
+ ...cfgSerializer,
207
+ customSerializer: async (
208
+ entryPoint: string,
209
+ preModules: readonly unknown[],
210
+ graph: MetroSerializerGraph,
211
+ serializerOptions: MetroSerializerOptions,
212
+ ) => {
213
+ const serializer =
214
+ existingSerializer ||
215
+ (defaultSerializer ||= createMetroDefaultSerializer(cfgProjectRoot))
216
+ const result = await serializer(entryPoint, preModules, graph, serializerOptions)
217
+ return appendRNXModulePaths(result, graph, serializerOptions, cfgProjectRoot)
218
+ },
219
+ },
220
+ }
221
+
222
+ const serverConfig = {
223
+ ...cfgServer,
224
+ enhanceMiddleware: (metroMiddleware: any, metroServer: any) => {
225
+ let middleware = metroMiddleware
226
+ if (existingEnhance) {
227
+ middleware = existingEnhance(metroMiddleware, metroServer)
228
+ }
229
+
230
+ let connect: any
231
+ try {
232
+ connect = require('connect')
233
+ } catch {
234
+ console.warn('[rnx] connect not available')
235
+ return middleware
236
+ }
237
+
238
+ const server = connect()
239
+
240
+ server.use((req: any, res: any, next: any) => {
241
+ if (isReplacementModuleRequestUrl(req.url)) {
242
+ void handleReplacementModuleRequest(req, res, {
243
+ projectRoot: cfgProjectRoot,
244
+ requestToken: metroServerId,
245
+ requireLoopbackOrigin: true,
246
+ }).then((handled) => {
247
+ if (!handled) next()
248
+ })
249
+ return
250
+ }
251
+
252
+ const url = req.url || ''
253
+ const pathname = url.split('?')[0]
254
+
255
+ const runtimeRoot = resolveRuntimeRootForRequestHost(req.headers?.host)
256
+
257
+ if (!runtimeRoot) {
258
+ if (pathname === prefix || pathname === prefix + '/') {
259
+ res.statusCode = 500
260
+ res.end(SOOTSIM_RUNTIME_MISSING_MESSAGE)
261
+ return
262
+ }
263
+ next()
264
+ return
265
+ }
266
+
267
+ // root-relative runtime assets emitted by the shell and engine vite
268
+ // builds. if a user app has its own /assets/foo request, we only
269
+ // intercept when that exact file exists in the runtime; otherwise
270
+ // metro handles it normally.
271
+ if (isRootRuntimeAssetPath(pathname)) {
272
+ const fullPath = resolveRuntimeFilePath(runtimeRoot, pathname)
273
+ if (fullPath) {
274
+ serveRuntimeFile(req, res, fullPath)
275
+ return
276
+ }
277
+ }
278
+
279
+ // /__soot/* — serve runtime assets
280
+ if (pathname.startsWith(prefix + '/')) {
281
+ const fullPath = resolveRuntimeFilePath(
282
+ runtimeRoot,
283
+ pathname.slice(prefix.length),
284
+ )
285
+ if (fullPath) {
286
+ serveRuntimeFile(req, res, fullPath)
287
+ return
288
+ }
289
+ }
290
+
291
+ // serve HTML shell for /__soot and extensionless in-shell routes.
292
+ if (
293
+ pathname === prefix ||
294
+ pathname === prefix + '/' ||
295
+ pathname.startsWith(prefix + '/')
296
+ ) {
297
+ const ext = path.extname(pathname)
298
+ if (!ext) {
299
+ const htmlPath = path.join(runtimeRoot, 'index.html')
300
+ let html = fs.readFileSync(htmlPath, 'utf8')
301
+ const script = `<script>(function(){var u=new URL(location.href);if(!u.searchParams.has('bundle'))u.searchParams.set('bundle',${JSON.stringify(bundleUrl)});history.replaceState(null,'','${prefix}/'+u.search)})()</script>`
302
+ html = html.includes('</head>')
303
+ ? html.replace('</head>', `${script}</head>`)
304
+ : `${html}${script}`
305
+ res.setHeader('content-type', 'text/html')
306
+ res.end(html)
307
+ return
308
+ }
309
+ }
310
+
311
+ next()
312
+ })
313
+
314
+ server.use(middleware)
315
+
316
+ console.log(`[rnx] serving at ${prefix}/`)
317
+ return server
318
+ },
319
+ }
320
+
321
+ return {
322
+ ...config,
323
+ ...serializerConfig,
324
+ server: serverConfig,
325
+ } as T
326
+ }
327
+
328
+ function resolveMetroPort(server: MetroConfigWithServer['server']): number {
329
+ const inlinePort = process.argv.find((arg) => arg.startsWith('--port='))?.slice(7)
330
+ const portIndex = process.argv.indexOf('--port')
331
+ const splitPort = portIndex >= 0 ? process.argv[portIndex + 1] : undefined
332
+ const rawPort =
333
+ inlinePort || splitPort || process.env.RCT_METRO_PORT || server?.port || 8081
334
+ const port = Number(rawPort)
335
+ if (!Number.isInteger(port) || port <= 0) {
336
+ throw new Error(`rnx: Metro port must be a positive integer, got ${rawPort}`)
337
+ }
338
+ return port
339
+ }
340
+
341
+ export default withRNX