rnxsim 0.1.363 → 0.1.365

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 (176) hide show
  1. package/cli/outbound-endpoints.ts +2 -6
  2. package/dist-cli/bin.js +5 -5
  3. package/dist-cli/chunks/{agent-EN7USQWO.js → agent-BVMNQFKF.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-4K2P2EBF.js → agent-wrapper-B6YFXGC3.js} +2 -2
  5. package/dist-cli/chunks/app-fonts-MEI6R6XR.js +4 -0
  6. package/dist-cli/chunks/{assert-WEQDRDJ2.js → assert-JGHI53HV.js} +2 -2
  7. package/dist-cli/chunks/{auth-U5GBY5SE.js → auth-STEOXUS7.js} +2 -2
  8. package/dist-cli/chunks/{beta-WOKLQLOJ.js → beta-HT3FLS5T.js} +2 -2
  9. package/dist-cli/chunks/{camera-GRMP2HT3.js → camera-ZCLFSLON.js} +2 -2
  10. package/dist-cli/chunks/{chunk-TIZP26F4.js → chunk-25BTVHK2.js} +2 -2
  11. package/dist-cli/chunks/{chunk-YFF6YFYJ.js → chunk-2CXHDTQ5.js} +1 -1
  12. package/dist-cli/chunks/{chunk-TK4ZQQAL.js → chunk-2JFZ5Z7Q.js} +1 -1
  13. package/dist-cli/chunks/{chunk-QVUS7OHN.js → chunk-2SFMW5K6.js} +1 -1
  14. package/dist-cli/chunks/{chunk-6BGR523F.js → chunk-4WFN4DFR.js} +2 -2
  15. package/dist-cli/chunks/{chunk-4QK74NWP.js → chunk-52ES4API.js} +2 -2
  16. package/dist-cli/chunks/{chunk-FFWDCXTD.js → chunk-5AS7S6HY.js} +2 -2
  17. package/dist-cli/chunks/{chunk-SIKPB3GL.js → chunk-5VHWQGGF.js} +1 -1
  18. package/dist-cli/chunks/{chunk-GLYEL265.js → chunk-622E4JET.js} +1 -1
  19. package/dist-cli/chunks/{chunk-DK764ET6.js → chunk-67ATAITF.js} +2 -2
  20. package/dist-cli/chunks/{chunk-2AELGO56.js → chunk-6BAF6GFS.js} +1 -1
  21. package/dist-cli/chunks/{chunk-ZSFETJZV.js → chunk-BGRGP3QR.js} +1 -1
  22. package/dist-cli/chunks/{chunk-7CT75XQF.js → chunk-CYIE65QN.js} +1 -1
  23. package/dist-cli/chunks/{chunk-PDGEVGHU.js → chunk-DBJZUCFP.js} +2 -2
  24. package/dist-cli/chunks/{chunk-KCHXGTYQ.js → chunk-DQFOHIRF.js} +1 -1
  25. package/dist-cli/chunks/{chunk-MOC4QX3B.js → chunk-EAFKG5R3.js} +2 -2
  26. package/dist-cli/chunks/{chunk-DFL5AISK.js → chunk-EFL2ASZO.js} +1 -1
  27. package/dist-cli/chunks/{chunk-GMW6T2K6.js → chunk-EFXNSJ55.js} +2 -2
  28. package/dist-cli/chunks/{chunk-NUU3SWLR.js → chunk-EVBQDQVY.js} +1 -1
  29. package/dist-cli/chunks/{chunk-X2ITJOHZ.js → chunk-FHWZPUGC.js} +1 -1
  30. package/dist-cli/chunks/{chunk-23MIH7SN.js → chunk-FIXRGPUT.js} +1 -1
  31. package/dist-cli/chunks/{chunk-EAXYIY5U.js → chunk-GK7PGYFH.js} +1 -1
  32. package/dist-cli/chunks/{chunk-BPYGMBOL.js → chunk-GZEUDDAW.js} +2 -2
  33. package/dist-cli/chunks/{chunk-WIV5YAQT.js → chunk-HIDS3PYC.js} +2 -2
  34. package/dist-cli/chunks/{chunk-WYD5DEEU.js → chunk-IWPS3EKJ.js} +2 -2
  35. package/dist-cli/chunks/{chunk-5N5BWVZK.js → chunk-J66US2OZ.js} +1 -1
  36. package/dist-cli/chunks/{chunk-MM2XAXTN.js → chunk-JAFF4RAU.js} +1 -1
  37. package/dist-cli/chunks/{chunk-GKKO7RE2.js → chunk-JRPU5TFI.js} +3 -3
  38. package/dist-cli/chunks/{chunk-ZSLOAO3P.js → chunk-M5KJCLUW.js} +3 -3
  39. package/dist-cli/chunks/{chunk-4ZGUXZ6U.js → chunk-MACI34ST.js} +3 -3
  40. package/dist-cli/chunks/chunk-MCHKBFUL.js +4 -0
  41. package/dist-cli/chunks/{chunk-42O6RKCS.js → chunk-MR3YPNUI.js} +2 -2
  42. package/dist-cli/chunks/{chunk-QXBFDUCQ.js → chunk-MRNW4GVL.js} +2 -2
  43. package/dist-cli/chunks/{chunk-VXN2HAHP.js → chunk-NEH4T67X.js} +2 -2
  44. package/dist-cli/chunks/{chunk-ZCHCE6WJ.js → chunk-O2ORLK6W.js} +2 -2
  45. package/dist-cli/chunks/{chunk-KWFRIVXW.js → chunk-O6P4N2JD.js} +2 -2
  46. package/dist-cli/chunks/{chunk-BWTDQWRB.js → chunk-OSZ5HZSI.js} +2 -2
  47. package/dist-cli/chunks/{chunk-HJN6LXR5.js → chunk-QIQL7Y5V.js} +2 -2
  48. package/dist-cli/chunks/{chunk-LV4BJLQU.js → chunk-R4FQZXJC.js} +2 -2
  49. package/dist-cli/chunks/{chunk-74RRQIWF.js → chunk-RI3NS77J.js} +1 -1
  50. package/dist-cli/chunks/{chunk-BAPVTWIM.js → chunk-TKCGP3DD.js} +2 -2
  51. package/dist-cli/chunks/{chunk-S6CNNXSR.js → chunk-TULWPOAY.js} +2 -2
  52. package/dist-cli/chunks/{chunk-FADHHMJU.js → chunk-UR345KM3.js} +2 -2
  53. package/dist-cli/chunks/{chunk-IY2WWLFH.js → chunk-W3UIDI3W.js} +2 -2
  54. package/dist-cli/chunks/{chunk-FNVWTY57.js → chunk-WGNDJARS.js} +1 -1
  55. package/dist-cli/chunks/{chunk-XYPCBRGY.js → chunk-X5ZOZVHC.js} +1 -1
  56. package/dist-cli/chunks/{chunk-HGPDQPIG.js → chunk-XKRUNS7A.js} +2 -2
  57. package/dist-cli/chunks/{chunk-FAWMDCMJ.js → chunk-XPOTXF5Q.js} +2 -2
  58. package/dist-cli/chunks/{chunk-WPXQ7CNQ.js → chunk-XR4KID25.js} +2 -2
  59. package/dist-cli/chunks/{chunk-AGUR7LE7.js → chunk-XRO5WQXP.js} +1 -1
  60. package/dist-cli/chunks/{chunk-YSLE3K2S.js → chunk-XXNPISER.js} +2 -2
  61. package/dist-cli/chunks/{chunk-PH4W36KI.js → chunk-YE3M3YD4.js} +3 -3
  62. package/dist-cli/chunks/{chunk-46BMZYP7.js → chunk-ZCP4CRP6.js} +2 -2
  63. package/dist-cli/chunks/{chunk-KJ5SZIXX.js → chunk-ZGD7GZRI.js} +2 -2
  64. package/dist-cli/chunks/{chunk-2IVWNQDE.js → chunk-ZGH5U4AJ.js} +2 -2
  65. package/dist-cli/chunks/{chunk-4CMRJ7UX.js → chunk-ZTWUNK45.js} +2 -2
  66. package/dist-cli/chunks/{cleanup-ZVQLJLTY.js → cleanup-G4TEYVKY.js} +2 -2
  67. package/dist-cli/chunks/cli-version-ZM3PVKQ5.js +4 -0
  68. package/dist-cli/chunks/{compat-7T2VZZDL.js → compat-EZ5NP32J.js} +2 -2
  69. package/dist-cli/chunks/{config-E7OULENQ.js → config-27M2DGCS.js} +2 -2
  70. package/dist-cli/chunks/{control-X5O5UXPP.js → control-4QR43FXV.js} +2 -2
  71. package/dist-cli/chunks/daemon-2U4CJ6AT.js +4 -0
  72. package/dist-cli/chunks/{debug-LK6GRYHR.js → debug-EXU3D7PC.js} +3 -3
  73. package/dist-cli/chunks/{desktop-2LAHHUP6.js → desktop-SHHPZAWQ.js} +3 -3
  74. package/dist-cli/chunks/{detox-7GF6V6E6.js → detox-RQ6FFSBS.js} +2 -2
  75. package/dist-cli/chunks/{device-G3IF4IRA.js → device-4NI26HG6.js} +2 -2
  76. package/dist-cli/chunks/{diagnose-6G2RJWVP.js → diagnose-P4SFXH6J.js} +2 -2
  77. package/dist-cli/chunks/{disk-cleanup-PRAJROKV.js → disk-cleanup-BUAKWYMY.js} +2 -2
  78. package/dist-cli/chunks/drivers-OVCUPQTM.js +4 -0
  79. package/dist-cli/chunks/{film-CGVAWXPR.js → film-Y2BAQINB.js} +3 -3
  80. package/dist-cli/chunks/flow-ELTLKKZ3.js +4 -0
  81. package/dist-cli/chunks/help-CDEZAG2L.js +4 -0
  82. package/dist-cli/chunks/{hidden-runtime-alias-FCDWZQM3.js → hidden-runtime-alias-U2MAY2XV.js} +2 -2
  83. package/dist-cli/chunks/{home-paths-JEUBFYPY.js → home-paths-PAJMZCOS.js} +2 -2
  84. package/dist-cli/chunks/inspect-VVQRPJLS.js +4 -0
  85. package/dist-cli/chunks/install-desktop-YXSRKR35.js +4 -0
  86. package/dist-cli/chunks/{login-HBPZL3QX.js → login-D53XKBEO.js} +2 -2
  87. package/dist-cli/chunks/{logout-SVBZGGEI.js → logout-HZQKLNUJ.js} +2 -2
  88. package/dist-cli/chunks/{maestro-445CMWSH.js → maestro-XSS2KX6L.js} +3 -3
  89. package/dist-cli/chunks/{maestro-generate-4JWKONR2.js → maestro-generate-DNDQX3JQ.js} +3 -3
  90. package/dist-cli/chunks/{mode-M6GVNTDZ.js → mode-LEVGOKI3.js} +2 -2
  91. package/dist-cli/chunks/{optional-demo-registry-Q5CATNHD.js → optional-demo-registry-2XZIJNDG.js} +2 -2
  92. package/dist-cli/chunks/{perf-MBNYDE6X.js → perf-OKMAXDLB.js} +2 -2
  93. package/dist-cli/chunks/{permissions-HNLQIZJD.js → permissions-VG5TBUTN.js} +2 -2
  94. package/dist-cli/chunks/privacy-MNRHREME.js +4 -0
  95. package/dist-cli/chunks/{record-MH3BLHON.js → record-BQYGZOMV.js} +3 -3
  96. package/dist-cli/chunks/{report-issue-6HQHCFBG.js → report-issue-HCPZESWE.js} +2 -2
  97. package/dist-cli/chunks/reset-R4E4W4YI.js +4 -0
  98. package/dist-cli/chunks/runtime-XRQZ7W34.js +4 -0
  99. package/dist-cli/chunks/{screenshot-command-D6VS6GDA.js → screenshot-command-DWEYPJE7.js} +3 -3
  100. package/dist-cli/chunks/{screenshot-layers-MC7JHOO2.js → screenshot-layers-5GMBXLO7.js} +2 -2
  101. package/dist-cli/chunks/{screenshots-capture-PUQTDG5I.js → screenshots-capture-5KCI7IZD.js} +2 -2
  102. package/dist-cli/chunks/serve-QNKTA2HR.js +56 -0
  103. package/dist-cli/chunks/setup-RNMM5GO5.js +4 -0
  104. package/dist-cli/chunks/{skills-IJDXM6IC.js → skills-DOEALD3X.js} +2 -2
  105. package/dist-cli/chunks/{state-KCBXBFZZ.js → state-3HH2IHIF.js} +2 -2
  106. package/dist-cli/chunks/{storage-RBYDWRJC.js → storage-E7WUWN3E.js} +2 -2
  107. package/dist-cli/chunks/store-HUPZUCFG.js +4 -0
  108. package/dist-cli/chunks/telemetry-IJ53XR2Y.js +4 -0
  109. package/dist-cli/chunks/{timeline-RPEEEB3A.js → timeline-IKNUGQ2A.js} +2 -2
  110. package/dist-cli/chunks/{upgrade-TFLWCW3P.js → upgrade-VS3OKXE2.js} +2 -2
  111. package/dist-cli/chunks/upload-A57NAAHF.js +4 -0
  112. package/dist-cli/chunks/{version-LHPE2VT4.js → version-F4XHNTBZ.js} +2 -2
  113. package/dist-cli/chunks/{web-QJ74GCNZ.js → web-F72724QL.js} +2 -2
  114. package/dist-cli/chunks/welcome-B72RRMBW.js +5 -0
  115. package/dist-cli/chunks/{what-happened-JXBBC5U7.js → what-happened-APK7YKOV.js} +2 -2
  116. package/dist-lib/agent-daemon-client.cjs +1 -1
  117. package/dist-lib/agent-events.cjs +1 -1
  118. package/dist-lib/agent-identity.cjs +1 -1
  119. package/dist-lib/agent-sessions.cjs +1 -1
  120. package/dist-lib/attached-projects.cjs +1 -1
  121. package/dist-lib/auth/shared-session.cjs +1 -1
  122. package/dist-lib/backend-origin.cjs +1 -1
  123. package/dist-lib/beta.cjs +1 -1
  124. package/dist-lib/beta.mjs +1 -1
  125. package/dist-lib/bridge-constants.cjs +1 -1
  126. package/dist-lib/bridge-contract.cjs +1 -1
  127. package/dist-lib/cli-constants.cjs +1 -1
  128. package/dist-lib/config.cjs +1 -1
  129. package/dist-lib/detox/index.cjs +1 -1
  130. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  131. package/dist-lib/home-paths.cjs +1 -1
  132. package/dist-lib/host/bridge-host.cjs +89 -154
  133. package/dist-lib/host/fetch-proxy-handler.cjs +36 -3
  134. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  135. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  136. package/dist-lib/host/replacement-module-handler.cjs +60 -107
  137. package/dist-lib/host/websocket-proxy.cjs +1 -1
  138. package/dist-lib/index.cjs +60 -105
  139. package/dist-lib/jump-to-source-babel.cjs +1 -1
  140. package/dist-lib/menu.cjs +1 -1
  141. package/dist-lib/menu.mjs +1 -1
  142. package/dist-lib/metro-production-bundle.cjs +1 -1
  143. package/dist-lib/metro-production-bundle.mjs +1 -1
  144. package/dist-lib/metro.cjs +60 -105
  145. package/dist-lib/profiles.cjs +1 -1
  146. package/dist-lib/public-brand.cjs +1 -1
  147. package/dist-lib/react-native-host-modules.cjs +1 -1
  148. package/dist-lib/react-native-host-modules.mjs +1 -1
  149. package/dist-lib/render-mode.cjs +1 -1
  150. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  151. package/dist-lib/sdk.cjs +1 -1
  152. package/dist-lib/sdk.mjs +1 -1
  153. package/dist-lib/skills.cjs +126 -189
  154. package/dist-lib/vite.cjs +59 -104
  155. package/package.json +1 -1
  156. package/src/host/bridge-host.ts +2 -50
  157. package/src/host/fetch-proxy-handler.ts +40 -3
  158. package/src/host/replacement-module-handler.ts +65 -117
  159. package/dist-cli/chunks/app-fonts-7B5E7UDM.js +0 -4
  160. package/dist-cli/chunks/chunk-FQGTATLA.js +0 -4
  161. package/dist-cli/chunks/cli-version-KAVMACHT.js +0 -4
  162. package/dist-cli/chunks/daemon-GEDFARBC.js +0 -4
  163. package/dist-cli/chunks/drivers-4WSF4T4I.js +0 -4
  164. package/dist-cli/chunks/flow-MEDCS46T.js +0 -4
  165. package/dist-cli/chunks/help-KOAHGOH4.js +0 -4
  166. package/dist-cli/chunks/inspect-IU5HFVKF.js +0 -4
  167. package/dist-cli/chunks/install-desktop-5Q2WB4QO.js +0 -4
  168. package/dist-cli/chunks/privacy-UL6QT67W.js +0 -4
  169. package/dist-cli/chunks/reset-GHLOGMF3.js +0 -4
  170. package/dist-cli/chunks/runtime-JK43NPS6.js +0 -4
  171. package/dist-cli/chunks/serve-4UDVILST.js +0 -118
  172. package/dist-cli/chunks/setup-5IHFDPBA.js +0 -4
  173. package/dist-cli/chunks/store-O2PYGAXL.js +0 -4
  174. package/dist-cli/chunks/telemetry-B2CPDQID.js +0 -4
  175. package/dist-cli/chunks/upload-2GJWZG6R.js +0 -4
  176. package/dist-cli/chunks/welcome-5XJPBRJD.js +0 -5
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.363 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.365 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -202,7 +202,6 @@ function isLoopbackHost(hostname) {
202
202
 
203
203
  // src/host/replacement-module-handler.ts
204
204
  var REPLACEMENT_MODULE_ROUTE = "/__sootsim-replacement-module";
205
- var REPLACEMENT_RUNTIME_PREFIX = "/__sootsim-replacement-runtime";
206
205
  function getReplacementLoader(filePath) {
207
206
  const ext = import_node_path3.default.extname(filePath).toLowerCase();
208
207
  switch (ext) {
@@ -218,88 +217,6 @@ function getReplacementLoader(filePath) {
218
217
  return null;
219
218
  }
220
219
  }
221
- function reactRuntimeSource(reactBridgeUrl) {
222
- if (reactBridgeUrl) {
223
- return `export * from ${JSON.stringify(reactBridgeUrl)};
224
- import React from ${JSON.stringify(reactBridgeUrl)};
225
- export default React;
226
- `;
227
- }
228
- return `const bridge = globalThis.__sootsim_react_bridge
229
- if (!bridge) throw new Error('[rnx] replacement React bridge is not ready')
230
- export default bridge.default
231
- export const Activity = bridge.Activity
232
- export const Children = bridge.Children
233
- export const Component = bridge.Component
234
- export const Fragment = bridge.Fragment
235
- export const Profiler = bridge.Profiler
236
- export const PureComponent = bridge.PureComponent
237
- export const StrictMode = bridge.StrictMode
238
- export const Suspense = bridge.Suspense
239
- export const __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = bridge.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
240
- export const __COMPILER_RUNTIME = bridge.__COMPILER_RUNTIME
241
- export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = bridge.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
242
- export const act = bridge.act
243
- export const cache = bridge.cache
244
- export const cacheSignal = bridge.cacheSignal
245
- export const captureOwnerStack = bridge.captureOwnerStack
246
- export const cloneElement = bridge.cloneElement
247
- export const createContext = bridge.createContext
248
- export const createElement = bridge.createElement
249
- export const createRef = bridge.createRef
250
- export const forwardRef = bridge.forwardRef
251
- export const isValidElement = bridge.isValidElement
252
- export const lazy = bridge.lazy
253
- export const memo = bridge.memo
254
- export const startTransition = bridge.startTransition
255
- export const unstable_useCacheRefresh = bridge.unstable_useCacheRefresh
256
- export const use = bridge.use
257
- export const useActionState = bridge.useActionState
258
- export const useCallback = bridge.useCallback
259
- export const useContext = bridge.useContext
260
- export const useDebugValue = bridge.useDebugValue
261
- export const useDeferredValue = bridge.useDeferredValue
262
- export const useEffect = bridge.useEffect
263
- export const useEffectEvent = bridge.useEffectEvent
264
- export const useId = bridge.useId
265
- export const useImperativeHandle = bridge.useImperativeHandle
266
- export const useInsertionEffect = bridge.useInsertionEffect
267
- export const useLayoutEffect = bridge.useLayoutEffect
268
- export const useMemo = bridge.useMemo
269
- export const useOptimistic = bridge.useOptimistic
270
- export const useReducer = bridge.useReducer
271
- export const useRef = bridge.useRef
272
- export const useState = bridge.useState
273
- export const useSyncExternalStore = bridge.useSyncExternalStore
274
- export const useTransition = bridge.useTransition
275
- export const version = bridge.version
276
- `;
277
- }
278
- function reactNativeRuntimeSource() {
279
- return `const rn = globalThis.__sootsim_components__ || {}
280
- export default rn
281
- export const View = rn.View
282
- export const Text = rn.Text
283
- export const Image = rn.Image
284
- export const ScrollView = rn.ScrollView
285
- export const FlatList = rn.FlatList
286
- export const Pressable = rn.Pressable
287
- export const TouchableOpacity = rn.TouchableOpacity
288
- export const TouchableWithoutFeedback = rn.TouchableWithoutFeedback
289
- export const TouchableHighlight = rn.TouchableHighlight
290
- export const TextInput = rn.TextInput
291
- export const ActivityIndicator = rn.ActivityIndicator
292
- export const KeyboardAvoidingView = rn.KeyboardAvoidingView
293
- export const Button = rn.Button
294
- export const Modal = rn.Modal
295
- export const Switch = rn.Switch
296
- export const StyleSheet = rn.StyleSheet
297
- export const Platform = rn.Platform
298
- export const Keyboard = rn.Keyboard
299
- export const Dimensions = rn.Dimensions
300
- export const Appearance = rn.Appearance
301
- `;
302
- }
303
220
  function sendJavaScript(req, res, source) {
304
221
  const origin = req.headers.origin;
305
222
  let allowedOrigin;
@@ -341,14 +258,6 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
341
258
  return true;
342
259
  }
343
260
  }
344
- if (url.pathname === `${REPLACEMENT_RUNTIME_PREFIX}/react`) {
345
- sendJavaScript(req, res, reactRuntimeSource(options.reactBridgeUrl));
346
- return true;
347
- }
348
- if (url.pathname === `${REPLACEMENT_RUNTIME_PREFIX}/react-native`) {
349
- sendJavaScript(req, res, reactNativeRuntimeSource());
350
- return true;
351
- }
352
261
  if (url.pathname !== REPLACEMENT_MODULE_ROUTE) return false;
353
262
  if (options.requestToken && url.searchParams.get("token") !== options.requestToken) {
354
263
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
@@ -372,6 +281,7 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
372
281
  res.end("replacement file path must be absolute");
373
282
  return true;
374
283
  }
284
+ const runtimePackages = new Set(url.searchParams.getAll("runtimePackage"));
375
285
  let resolvedProjectRoot;
376
286
  if (options.projectRoot) {
377
287
  const lexicalProjectRoot = import_node_path3.default.resolve(options.projectRoot);
@@ -414,19 +324,64 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
414
324
  tsconfigRaw: { compilerOptions: { jsx: "react" } },
415
325
  sourcemap: "inline",
416
326
  logLevel: "silent",
417
- external: ["react", "react-native"]
327
+ plugins: [
328
+ {
329
+ name: "sootsim-replacement-runtime",
330
+ setup(runtimeBuild) {
331
+ runtimeBuild.onResolve(
332
+ {
333
+ filter: /^(react|react-native|react\/jsx-runtime|react\/jsx-dev-runtime)$/
334
+ },
335
+ (args) => ({ path: args.path, namespace: "sootsim-replacement-runtime" })
336
+ );
337
+ runtimeBuild.onResolve(
338
+ { filter: /.*/ },
339
+ (args) => runtimePackages.has(args.path) ? { path: args.path, namespace: "sootsim-replacement-runtime" } : void 0
340
+ );
341
+ runtimeBuild.onLoad(
342
+ { filter: /.*/, namespace: "sootsim-replacement-runtime" },
343
+ (args) => {
344
+ if (args.path === "react-native") {
345
+ return {
346
+ contents: `const bridge = globalThis.__sootsim_components__
347
+ if (!bridge) throw new Error('[rnx] replacement React Native bridge is not ready')
348
+ module.exports = bridge`,
349
+ loader: "js"
350
+ };
351
+ }
352
+ if (args.path === "react") {
353
+ return {
354
+ contents: `const bridge = globalThis.__sootsim_react_bridge
355
+ if (!bridge) throw new Error('[rnx] replacement React bridge is not ready')
356
+ module.exports = bridge`,
357
+ loader: "js"
358
+ };
359
+ }
360
+ if (args.path !== "react/jsx-runtime" && args.path !== "react/jsx-dev-runtime") {
361
+ return {
362
+ contents: `const packages = globalThis.__sootsim_config_package_modules
363
+ const bridge = packages?.[${JSON.stringify(args.path)}]
364
+ if (!bridge) throw new Error(${JSON.stringify(`[rnx] replacement package bridge ${args.path} is not ready`)})
365
+ module.exports = bridge`,
366
+ loader: "js"
367
+ };
368
+ }
369
+ return {
370
+ contents: `const React = globalThis.__sootsim_react_bridge
371
+ if (!React) throw new Error('[rnx] replacement React bridge is not ready')
372
+ exports.Fragment = React.Fragment
373
+ exports.jsx = (type, props, key) => React.createElement(type, key === undefined ? props : { ...props, key })
374
+ exports.jsxs = exports.jsx
375
+ exports.jsxDEV = exports.jsx`,
376
+ loader: "js"
377
+ };
378
+ }
379
+ );
380
+ }
381
+ }
382
+ ]
418
383
  });
419
- const bundledCode = result.outputFiles[0]?.text ?? "";
420
- const hasReactBinding = /\bimport\s+(?:\*\s+as\s+React|React(?:\s*,|\s+from)|\{[^}]*\bdefault\s+as\s+React\b)/.test(
421
- bundledCode
422
- );
423
- const reactImport = /\bReact\.(?:createElement|Fragment)\b/.test(bundledCode) && !hasReactBinding ? `import * as React from '${REPLACEMENT_RUNTIME_PREFIX}/react';
424
- ` : "";
425
- const runtimeCode = (reactImport + bundledCode).replace(
426
- /from\s*["']react-native["']/g,
427
- `from '${REPLACEMENT_RUNTIME_PREFIX}/react-native'`
428
- ).replace(/from\s*["']react["']/g, `from '${REPLACEMENT_RUNTIME_PREFIX}/react'`);
429
- sendJavaScript(req, res, runtimeCode);
384
+ sendJavaScript(req, res, result.outputFiles[0]?.text ?? "");
430
385
  } catch (error) {
431
386
  const message = error instanceof Error ? error.message : String(error);
432
387
  res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnxsim",
3
- "version": "0.1.363",
3
+ "version": "0.1.365",
4
4
  "description": "rnx CLI, Vite and Metro plugins, and agent skills for driving React Native apps in the browser.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -7,7 +7,6 @@ import {
7
7
  scanDevServers,
8
8
  type DiscoveredServer,
9
9
  } from '../../scripts/dev-server-scanner.ts'
10
- import { isLoopbackHost } from '../backend-origin.ts'
11
10
  import {
12
11
  DEFAULT_SOOTSIM_BRIDGE_PORT,
13
12
  SOOTSIM_BRIDGE_SIM_CLOSE_CODE,
@@ -44,6 +43,7 @@ import { sootsimRuntime } from '../runtime-delivery.ts'
44
43
  import { AgentHost, type AgentHostOptions } from './agent-host.ts'
45
44
  import {
46
45
  handleAppApiRequest,
46
+ handleBundleProxyRequest,
47
47
  handleFetchProxyRequest,
48
48
  isAppApiRequestUrl,
49
49
  isFetchProxyRequestUrl,
@@ -1669,55 +1669,7 @@ export class SootSimBridgeHost {
1669
1669
  // cross-origin URL without CORS headers, and metro doesn't set them.
1670
1670
  // we proxy the request and stream the response back.
1671
1671
  if (url.pathname === '/__bundle-proxy') {
1672
- const target = url.searchParams.get('url')
1673
- if (!target) {
1674
- res.writeHead(400, { 'Content-Type': 'text/plain' })
1675
- res.end('bundle-proxy: missing url query param')
1676
- return
1677
- }
1678
- let parsedTarget: URL
1679
- try {
1680
- parsedTarget = new URL(target)
1681
- } catch {
1682
- res.writeHead(400, { 'Content-Type': 'text/plain' })
1683
- res.end('bundle-proxy: invalid url')
1684
- return
1685
- }
1686
- // limit to local loopback targets — preventing the daemon from being
1687
- // turned into an open proxy for arbitrary internet fetches.
1688
- if (!isLoopbackHost(parsedTarget.hostname)) {
1689
- res.writeHead(403, { 'Content-Type': 'text/plain' })
1690
- res.end('bundle-proxy: only loopback targets allowed')
1691
- return
1692
- }
1693
- void (async () => {
1694
- try {
1695
- const upstream = await fetch(parsedTarget.toString(), {
1696
- redirect: 'follow',
1697
- })
1698
- const headers: Record<string, string> = {}
1699
- const ct = upstream.headers.get('content-type')
1700
- if (ct) headers['Content-Type'] = ct
1701
- headers['Cache-Control'] = 'no-store'
1702
- res.writeHead(upstream.status, headers)
1703
- if (!upstream.body) {
1704
- res.end()
1705
- return
1706
- }
1707
- const reader = upstream.body.getReader()
1708
- while (true) {
1709
- const { done, value } = await reader.read()
1710
- if (done) break
1711
- res.write(Buffer.from(value))
1712
- }
1713
- res.end()
1714
- } catch (err) {
1715
- res.writeHead(502, { 'Content-Type': 'text/plain' })
1716
- res.end(
1717
- `bundle-proxy: upstream fetch failed: ${err instanceof Error ? err.message : String(err)}`,
1718
- )
1719
- }
1720
- })()
1672
+ void handleBundleProxyRequest(req, res)
1721
1673
  return
1722
1674
  }
1723
1675
 
@@ -26,7 +26,7 @@
26
26
  import http, { type IncomingMessage, type ServerResponse } from 'http'
27
27
  import https from 'https'
28
28
  import { finished, pipeline } from 'stream/promises'
29
- import { isLoopbackHost } from '../backend-origin.ts'
29
+ import { isLoopbackHost, normalizeHostname } from '../backend-origin.ts'
30
30
  import {
31
31
  FETCH_PROXY_BROWSER_USER_AGENT,
32
32
  getFetchProxyTargetHeaders,
@@ -149,6 +149,10 @@ export function isFetchProxyRequestUrl(rawUrl: string | undefined): boolean {
149
149
  return rawUrl?.startsWith('/__fetch-proxy?') || rawUrl?.startsWith('/__proxy?') || false
150
150
  }
151
151
 
152
+ export function isBundleProxyRequestUrl(rawUrl: string | undefined): boolean {
153
+ return rawUrl?.startsWith('/__bundle-proxy?') ?? false
154
+ }
155
+
152
156
  export function isAppApiRequestUrl(rawUrl: string | undefined): boolean {
153
157
  if (!rawUrl) return false
154
158
  if (rawUrl.startsWith('/__app-api?')) return true
@@ -220,7 +224,7 @@ export async function handleFetchProxyRequest(
220
224
  const proxyRes = await new Promise<IncomingMessage>((resolve, reject) => {
221
225
  const proxyReq = transport.request(
222
226
  {
223
- hostname: targetUrl.hostname,
227
+ hostname: normalizeHostname(targetUrl.hostname),
224
228
  port: targetUrl.port || (targetUrl.protocol === 'https:' ? 443 : 80),
225
229
  path: targetUrl.pathname + targetUrl.search,
226
230
  method,
@@ -310,6 +314,39 @@ export async function handleFetchProxyRequest(
310
314
  }
311
315
  }
312
316
 
317
+ export async function handleBundleProxyRequest(
318
+ req: IncomingMessage,
319
+ res: ServerResponse,
320
+ ): Promise<void> {
321
+ const params = new URLSearchParams((req.url || '').split('?')[1] || '')
322
+ const target = params.get('url')
323
+ if (!target) {
324
+ applyFetchProxyCors(res)
325
+ res.writeHead(400, { 'Content-Type': 'text/plain' })
326
+ res.end('bundle-proxy: missing url query param')
327
+ return
328
+ }
329
+
330
+ let targetUrl: URL
331
+ try {
332
+ targetUrl = new URL(target)
333
+ } catch {
334
+ applyFetchProxyCors(res)
335
+ res.writeHead(400, { 'Content-Type': 'text/plain' })
336
+ res.end('bundle-proxy: invalid url')
337
+ return
338
+ }
339
+
340
+ if (!isLoopbackHost(targetUrl.hostname)) {
341
+ applyFetchProxyCors(res)
342
+ res.writeHead(403, { 'Content-Type': 'text/plain' })
343
+ res.end('bundle-proxy: only loopback targets allowed')
344
+ return
345
+ }
346
+
347
+ await handleFetchProxyRequest(req, res)
348
+ }
349
+
313
350
  export function handleAppApiRequest(req: IncomingMessage, res: ServerResponse): boolean {
314
351
  // returns true if it handled the request, false if the caller should fall
315
352
  // through. /__app-api requires `origin` query param for stateless mode;
@@ -365,7 +402,7 @@ export function handleAppApiRequest(req: IncomingMessage, res: ServerResponse):
365
402
 
366
403
  const proxyReq = transport.request(
367
404
  {
368
- hostname: targetUrl.hostname,
405
+ hostname: normalizeHostname(targetUrl.hostname),
369
406
  port: targetUrl.port || (targetUrl.protocol === 'https:' ? 443 : 80),
370
407
  path: targetUrl.pathname + targetUrl.search,
371
408
  method: req.method,
@@ -5,10 +5,8 @@ import { isLoopbackHost } from '../backend-origin.ts'
5
5
  import type { IncomingMessage, ServerResponse } from 'node:http'
6
6
 
7
7
  export const REPLACEMENT_MODULE_ROUTE = '/__sootsim-replacement-module'
8
- export const REPLACEMENT_RUNTIME_PREFIX = '/__sootsim-replacement-runtime'
9
8
 
10
9
  export interface ReplacementModuleHandlerOptions {
11
- reactBridgeUrl?: string
12
10
  projectRoot?: string
13
11
  requireLoopbackOrigin?: boolean
14
12
  requestToken?: string
@@ -30,92 +28,6 @@ function getReplacementLoader(filePath: string): 'js' | 'jsx' | 'ts' | 'tsx' | n
30
28
  }
31
29
  }
32
30
 
33
- function reactRuntimeSource(reactBridgeUrl?: string): string {
34
- if (reactBridgeUrl) {
35
- return (
36
- `export * from ${JSON.stringify(reactBridgeUrl)};\n` +
37
- `import React from ${JSON.stringify(reactBridgeUrl)};\n` +
38
- `export default React;\n`
39
- )
40
- }
41
-
42
- return `const bridge = globalThis.__sootsim_react_bridge
43
- if (!bridge) throw new Error('[rnx] replacement React bridge is not ready')
44
- export default bridge.default
45
- export const Activity = bridge.Activity
46
- export const Children = bridge.Children
47
- export const Component = bridge.Component
48
- export const Fragment = bridge.Fragment
49
- export const Profiler = bridge.Profiler
50
- export const PureComponent = bridge.PureComponent
51
- export const StrictMode = bridge.StrictMode
52
- export const Suspense = bridge.Suspense
53
- export const __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = bridge.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
54
- export const __COMPILER_RUNTIME = bridge.__COMPILER_RUNTIME
55
- export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = bridge.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
56
- export const act = bridge.act
57
- export const cache = bridge.cache
58
- export const cacheSignal = bridge.cacheSignal
59
- export const captureOwnerStack = bridge.captureOwnerStack
60
- export const cloneElement = bridge.cloneElement
61
- export const createContext = bridge.createContext
62
- export const createElement = bridge.createElement
63
- export const createRef = bridge.createRef
64
- export const forwardRef = bridge.forwardRef
65
- export const isValidElement = bridge.isValidElement
66
- export const lazy = bridge.lazy
67
- export const memo = bridge.memo
68
- export const startTransition = bridge.startTransition
69
- export const unstable_useCacheRefresh = bridge.unstable_useCacheRefresh
70
- export const use = bridge.use
71
- export const useActionState = bridge.useActionState
72
- export const useCallback = bridge.useCallback
73
- export const useContext = bridge.useContext
74
- export const useDebugValue = bridge.useDebugValue
75
- export const useDeferredValue = bridge.useDeferredValue
76
- export const useEffect = bridge.useEffect
77
- export const useEffectEvent = bridge.useEffectEvent
78
- export const useId = bridge.useId
79
- export const useImperativeHandle = bridge.useImperativeHandle
80
- export const useInsertionEffect = bridge.useInsertionEffect
81
- export const useLayoutEffect = bridge.useLayoutEffect
82
- export const useMemo = bridge.useMemo
83
- export const useOptimistic = bridge.useOptimistic
84
- export const useReducer = bridge.useReducer
85
- export const useRef = bridge.useRef
86
- export const useState = bridge.useState
87
- export const useSyncExternalStore = bridge.useSyncExternalStore
88
- export const useTransition = bridge.useTransition
89
- export const version = bridge.version
90
- `
91
- }
92
-
93
- function reactNativeRuntimeSource(): string {
94
- return `const rn = globalThis.__sootsim_components__ || {}
95
- export default rn
96
- export const View = rn.View
97
- export const Text = rn.Text
98
- export const Image = rn.Image
99
- export const ScrollView = rn.ScrollView
100
- export const FlatList = rn.FlatList
101
- export const Pressable = rn.Pressable
102
- export const TouchableOpacity = rn.TouchableOpacity
103
- export const TouchableWithoutFeedback = rn.TouchableWithoutFeedback
104
- export const TouchableHighlight = rn.TouchableHighlight
105
- export const TextInput = rn.TextInput
106
- export const ActivityIndicator = rn.ActivityIndicator
107
- export const KeyboardAvoidingView = rn.KeyboardAvoidingView
108
- export const Button = rn.Button
109
- export const Modal = rn.Modal
110
- export const Switch = rn.Switch
111
- export const StyleSheet = rn.StyleSheet
112
- export const Platform = rn.Platform
113
- export const Keyboard = rn.Keyboard
114
- export const Dimensions = rn.Dimensions
115
- export const Appearance = rn.Appearance
116
- `
117
- }
118
-
119
31
  function sendJavaScript(req: IncomingMessage, res: ServerResponse, source: string): void {
120
32
  const origin = req.headers.origin
121
33
  let allowedOrigin: string | undefined
@@ -138,10 +50,7 @@ export function isReplacementModuleRequestUrl(rawUrl?: string): boolean {
138
50
  if (!rawUrl) return false
139
51
  try {
140
52
  const pathname = new URL(rawUrl, 'http://rnx.local').pathname
141
- return (
142
- pathname === REPLACEMENT_MODULE_ROUTE ||
143
- pathname.startsWith(`${REPLACEMENT_RUNTIME_PREFIX}/`)
144
- )
53
+ return pathname === REPLACEMENT_MODULE_ROUTE
145
54
  } catch {
146
55
  return false
147
56
  }
@@ -178,14 +87,6 @@ export async function handleReplacementModuleRequest(
178
87
  }
179
88
  }
180
89
 
181
- if (url.pathname === `${REPLACEMENT_RUNTIME_PREFIX}/react`) {
182
- sendJavaScript(req, res, reactRuntimeSource(options.reactBridgeUrl))
183
- return true
184
- }
185
- if (url.pathname === `${REPLACEMENT_RUNTIME_PREFIX}/react-native`) {
186
- sendJavaScript(req, res, reactNativeRuntimeSource())
187
- return true
188
- }
189
90
  if (url.pathname !== REPLACEMENT_MODULE_ROUTE) return false
190
91
 
191
92
  if (options.requestToken && url.searchParams.get('token') !== options.requestToken) {
@@ -214,6 +115,8 @@ export async function handleReplacementModuleRequest(
214
115
  return true
215
116
  }
216
117
 
118
+ const runtimePackages = new Set(url.searchParams.getAll('runtimePackage'))
119
+
217
120
  let resolvedProjectRoot: string | undefined
218
121
  if (options.projectRoot) {
219
122
  const lexicalProjectRoot = path.resolve(options.projectRoot)
@@ -259,24 +162,69 @@ export async function handleReplacementModuleRequest(
259
162
  tsconfigRaw: { compilerOptions: { jsx: 'react' } },
260
163
  sourcemap: 'inline',
261
164
  logLevel: 'silent',
262
- external: ['react', 'react-native'],
165
+ plugins: [
166
+ {
167
+ name: 'sootsim-replacement-runtime',
168
+ setup(runtimeBuild) {
169
+ runtimeBuild.onResolve(
170
+ {
171
+ filter:
172
+ /^(react|react-native|react\/jsx-runtime|react\/jsx-dev-runtime)$/,
173
+ },
174
+ (args) => ({ path: args.path, namespace: 'sootsim-replacement-runtime' }),
175
+ )
176
+ runtimeBuild.onResolve({ filter: /.*/ }, (args) =>
177
+ runtimePackages.has(args.path)
178
+ ? { path: args.path, namespace: 'sootsim-replacement-runtime' }
179
+ : undefined,
180
+ )
181
+ runtimeBuild.onLoad(
182
+ { filter: /.*/, namespace: 'sootsim-replacement-runtime' },
183
+ (args) => {
184
+ if (args.path === 'react-native') {
185
+ return {
186
+ contents: `const bridge = globalThis.__sootsim_components__
187
+ if (!bridge) throw new Error('[rnx] replacement React Native bridge is not ready')
188
+ module.exports = bridge`,
189
+ loader: 'js',
190
+ }
191
+ }
192
+ if (args.path === 'react') {
193
+ return {
194
+ contents: `const bridge = globalThis.__sootsim_react_bridge
195
+ if (!bridge) throw new Error('[rnx] replacement React bridge is not ready')
196
+ module.exports = bridge`,
197
+ loader: 'js',
198
+ }
199
+ }
200
+ if (
201
+ args.path !== 'react/jsx-runtime' &&
202
+ args.path !== 'react/jsx-dev-runtime'
203
+ ) {
204
+ return {
205
+ contents: `const packages = globalThis.__sootsim_config_package_modules
206
+ const bridge = packages?.[${JSON.stringify(args.path)}]
207
+ if (!bridge) throw new Error(${JSON.stringify(`[rnx] replacement package bridge ${args.path} is not ready`)})
208
+ module.exports = bridge`,
209
+ loader: 'js',
210
+ }
211
+ }
212
+ return {
213
+ contents: `const React = globalThis.__sootsim_react_bridge
214
+ if (!React) throw new Error('[rnx] replacement React bridge is not ready')
215
+ exports.Fragment = React.Fragment
216
+ exports.jsx = (type, props, key) => React.createElement(type, key === undefined ? props : { ...props, key })
217
+ exports.jsxs = exports.jsx
218
+ exports.jsxDEV = exports.jsx`,
219
+ loader: 'js',
220
+ }
221
+ },
222
+ )
223
+ },
224
+ },
225
+ ],
263
226
  })
264
- const bundledCode = result.outputFiles[0]?.text ?? ''
265
- const hasReactBinding =
266
- /\bimport\s+(?:\*\s+as\s+React|React(?:\s*,|\s+from)|\{[^}]*\bdefault\s+as\s+React\b)/.test(
267
- bundledCode,
268
- )
269
- const reactImport =
270
- /\bReact\.(?:createElement|Fragment)\b/.test(bundledCode) && !hasReactBinding
271
- ? `import * as React from '${REPLACEMENT_RUNTIME_PREFIX}/react';\n`
272
- : ''
273
- const runtimeCode = (reactImport + bundledCode)
274
- .replace(
275
- /from\s*["']react-native["']/g,
276
- `from '${REPLACEMENT_RUNTIME_PREFIX}/react-native'`,
277
- )
278
- .replace(/from\s*["']react["']/g, `from '${REPLACEMENT_RUNTIME_PREFIX}/react'`)
279
- sendJavaScript(req, res, runtimeCode)
227
+ sendJavaScript(req, res, result.outputFiles[0]?.text ?? '')
280
228
  } catch (error) {
281
229
  const message = error instanceof Error ? error.message : String(error)
282
230
  res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' })
@@ -1,4 +0,0 @@
1
- /*! rnx v0.1.363 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import { createRequire as __sootsimCreateRequire } from 'node:module'
3
- var require = __sootsimCreateRequire(import.meta.url)
4
- import{o as p}from"./chunk-4ZGUXZ6U.js";import"./chunk-XYPCBRGY.js";import"./chunk-YFF6YFYJ.js";import"./chunk-NUU3SWLR.js";import{existsSync as h,mkdirSync as d,readFileSync as y,writeFileSync as g}from"node:fs";import{join as m}from"node:path";import{gzipSync as w}from"node:zlib";function f(t,e){let o=t.indexOf(`--${e}`);if(o!==-1&&o+1<t.length)return t[o+1];let r=t.find(i=>i.startsWith(`--${e}=`));return r?r.slice(e.length+3):void 0}async function x(t){let e=f(t,"wire")||"",o=f(t,"out-dir"),r=f(t,"manifest");if((!o||!r)&&(console.error(" usage: rnx app-fonts stage --wire <wire> --out-dir <dir> --manifest <path>"),process.exit(1)),!e.trim()){console.log("no app fonts declared \u2014 nothing to stage");return}d(o,{recursive:!0});let i=await p(e,{onStaged:({url:n,byteLength:s})=>console.log(` staged app font: ${n} (${(s/1024).toFixed(1)} KiB)`),onError:(n,s)=>console.error(` warning: failed to fetch app font ${n}: ${s instanceof Error?s.message:s}`)}),a=[];for(let n of i){let s=w(n.bytes);g(m(o,n.urlhash),s),a.push({url:n.url,urlhash:n.urlhash,contentType:n.contentType,encoding:"gzip",sizeBytes:s.length,rawBytes:n.bytes.byteLength})}let c=[];if(h(r))try{let n=JSON.parse(y(r,"utf8"));Array.isArray(n)&&(c=n)}catch{}let u=new Set(c.map(n=>n.urlhash)),l=[...c,...a.filter(n=>!u.has(n.urlhash))];g(r,JSON.stringify(l)),console.log(`staged ${a.length} app font(s); manifest now has ${l.length} file(s)`)}async function E(t){let e=t[0];if(e==="stage"){await x(t.slice(1));return}console.error(` unknown app-fonts subcommand: ${e??"(none)"}`),console.error(" available: stage"),process.exit(1)}export{E as runAppFonts};