rnxsim 0.1.393 → 0.1.395

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 (69) hide show
  1. package/README.md +12 -5
  2. package/cli/cloud-client.ts +74 -52
  3. package/cli/cloud-dispatch.ts +2 -1
  4. package/cli/commands/inspect/core.ts +33 -2
  5. package/cli/commands/platform.ts +34 -9
  6. package/dist-lib/agent-daemon-client.cjs +1 -1
  7. package/dist-lib/agent-events.cjs +1 -1
  8. package/dist-lib/agent-identity.cjs +1 -1
  9. package/dist-lib/agent-sessions.cjs +1 -1
  10. package/dist-lib/attached-projects.cjs +1 -1
  11. package/dist-lib/auth/shared-session.cjs +1 -1
  12. package/dist-lib/backend-origin.cjs +1 -1
  13. package/dist-lib/beta.cjs +1 -1
  14. package/dist-lib/beta.mjs +1 -1
  15. package/dist-lib/bridge-constants.cjs +1 -1
  16. package/dist-lib/bridge-contract-input.cjs +12 -1
  17. package/dist-lib/bridge-contract-input.mjs +10 -1
  18. package/dist-lib/bridge-contract.cjs +7 -1
  19. package/dist-lib/bridge-contract.mjs +5 -1
  20. package/dist-lib/capture-contract.cjs +1 -1
  21. package/dist-lib/capture-contract.mjs +1 -1
  22. package/dist-lib/cli-constants.cjs +1 -1
  23. package/dist-lib/cloud-contract.cjs +29 -3
  24. package/dist-lib/cloud-contract.mjs +26 -2
  25. package/dist-lib/config.cjs +1 -1
  26. package/dist-lib/detox/index.cjs +1 -1
  27. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  28. package/dist-lib/home-paths.cjs +1 -1
  29. package/dist-lib/host/bridge-host.cjs +1 -1
  30. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  31. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  32. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  33. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  34. package/dist-lib/host/websocket-proxy.cjs +1 -1
  35. package/dist-lib/index.cjs +25 -55
  36. package/dist-lib/jump-to-source-babel.cjs +1 -1
  37. package/dist-lib/menu.cjs +1 -1
  38. package/dist-lib/menu.mjs +1 -1
  39. package/dist-lib/metro-production-bundle.cjs +132 -8
  40. package/dist-lib/metro-production-bundle.mjs +129 -6
  41. package/dist-lib/metro.cjs +3 -55
  42. package/dist-lib/profiles.cjs +1 -1
  43. package/dist-lib/public-brand.cjs +1 -1
  44. package/dist-lib/react-native-host-modules.cjs +1 -1
  45. package/dist-lib/react-native-host-modules.mjs +1 -1
  46. package/dist-lib/render-mode.cjs +1 -1
  47. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  48. package/dist-lib/sdk.cjs +24 -3
  49. package/dist-lib/sdk.mjs +24 -3
  50. package/dist-lib/skills.cjs +28 -25
  51. package/dist-lib/vite.cjs +1 -1
  52. package/package.json +1 -1
  53. package/src/bridge-contract-input.ts +12 -0
  54. package/src/bridge-contract.ts +7 -0
  55. package/src/cloud-contract.ts +29 -0
  56. package/src/metro-plugin.ts +17 -98
  57. package/src/metro-production-bundle.ts +204 -3
  58. package/dist-lib/metro-host-modules/react-dom.js +0 -6
  59. package/dist-lib/metro-host-modules/react-native-web.js +0 -6
  60. package/dist-lib/metro-host-modules/react-native.js +0 -6
  61. package/dist-lib/metro-host-modules/react.js +0 -6
  62. package/dist-lib/metro-host-modules/react__jsx-dev-runtime.js +0 -6
  63. package/dist-lib/metro-host-modules/react__jsx-runtime.js +0 -6
  64. package/src/metro-host-modules/react-dom.js +0 -6
  65. package/src/metro-host-modules/react-native-web.js +0 -6
  66. package/src/metro-host-modules/react-native.js +0 -6
  67. package/src/metro-host-modules/react.js +0 -6
  68. package/src/metro-host-modules/react__jsx-dev-runtime.js +0 -6
  69. package/src/metro-host-modules/react__jsx-runtime.js +0 -6
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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;
@@ -410,7 +410,6 @@ exports.jsxDEV = exports.jsx`,
410
410
  }
411
411
 
412
412
  // src/metro-production-bundle.ts
413
- var RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION = "rnxsim";
414
413
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
415
414
  function toRNXProductionBundleUrl(bundleUrl) {
416
415
  const relative = bundleUrl.startsWith("/");
@@ -418,8 +417,6 @@ function toRNXProductionBundleUrl(bundleUrl) {
418
417
  url.searchParams.set("dev", "false");
419
418
  url.searchParams.set("hot", "false");
420
419
  url.searchParams.set("minify", "true");
421
- url.searchParams.set(`resolver.${RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION}`, "true");
422
- url.searchParams.set(`transform.${RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION}`, "true");
423
420
  return relative ? `${url.pathname}${url.search}${url.hash}` : url.toString();
424
421
  }
425
422
  function createRNXMetroModulePathsFooter(modulePaths) {
@@ -606,35 +603,6 @@ function serveRuntimeFile(req, res, fullPath) {
606
603
 
607
604
  // src/metro-plugin.ts
608
605
  var prefix = "/__soot";
609
- var hostModuleSpecifiers = [
610
- "react",
611
- "react-dom",
612
- "react/jsx-runtime",
613
- "react/jsx-dev-runtime",
614
- "react-native",
615
- "react-native-web"
616
- ];
617
- function isRNXProductionBundle(graph) {
618
- return graph.transformOptions?.dev === false && graph.transformOptions.customTransformOptions?.[RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION] === "true";
619
- }
620
- function assertProductionEnvironment() {
621
- if (process.env.NODE_ENV === "production") return;
622
- throw new Error(
623
- "rnxsim: production bundle requests require NODE_ENV=production; restart Metro with NODE_ENV=production"
624
- );
625
- }
626
- function hostModuleFileName(specifier) {
627
- return `${specifier.replaceAll("/", "__")}.js`;
628
- }
629
- function resolveHostModules() {
630
- const root = import_path2.default.join(__dirname, "metro-host-modules");
631
- const files = /* @__PURE__ */ new Map();
632
- for (const specifier of hostModuleSpecifiers) {
633
- const filePath = import_path2.default.join(root, hostModuleFileName(specifier));
634
- files.set(specifier, filePath);
635
- }
636
- return { root, files };
637
- }
638
606
  function readCallableMetroExport(moduleValue, label) {
639
607
  const callable = typeof moduleValue === "function" ? moduleValue : typeof moduleValue === "object" && moduleValue !== null ? Reflect.get(moduleValue, "default") : void 0;
640
608
  if (typeof callable !== "function") {
@@ -664,7 +632,7 @@ function createMetroDefaultSerializer(projectRoot) {
664
632
  return code;
665
633
  };
666
634
  }
667
- function prependRNXModulePaths(result, graph, options, projectRoot) {
635
+ function appendRNXModulePaths(result, graph, options, projectRoot) {
668
636
  const dependencies = graph.dependencies;
669
637
  const createModuleId = options.createModuleId;
670
638
  if (!dependencies || !createModuleId) {
@@ -697,44 +665,24 @@ function withRNX(config, options = {}) {
697
665
  cfgProjectRoot,
698
666
  appName
699
667
  );
700
- const cfgResolver = config.resolver;
701
668
  const cfgSerializer = config.serializer;
702
669
  const existingEnhance = cfgServer?.enhanceMiddleware;
703
- const existingResolveRequest = cfgResolver?.resolveRequest;
704
670
  const existingSerializer = cfgSerializer?.customSerializer;
705
671
  let productionConfig = {};
706
672
  if (options.productionBundles) {
707
- const hostModules = resolveHostModules();
708
673
  let defaultSerializer;
709
674
  productionConfig = {
710
- watchFolders: [
711
- ...config.watchFolders || [],
712
- hostModules.root
713
- ],
714
- resolver: {
715
- ...cfgResolver,
716
- resolveRequest: (context, moduleName, platform) => {
717
- if (context.customResolverOptions?.[RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION] === "true") {
718
- assertProductionEnvironment();
719
- const hostModule = hostModules.files.get(moduleName);
720
- if (hostModule) return { type: "sourceFile", filePath: hostModule };
721
- }
722
- return existingResolveRequest ? existingResolveRequest(context, moduleName, platform) : context.resolveRequest(context, moduleName, platform);
723
- }
724
- },
725
675
  serializer: {
726
676
  ...cfgSerializer,
727
677
  customSerializer: async (entryPoint, preModules, graph, serializerOptions) => {
728
678
  const serializer = existingSerializer || (defaultSerializer ||= createMetroDefaultSerializer(cfgProjectRoot));
729
- const productionBundle = isRNXProductionBundle(graph);
730
- if (productionBundle) assertProductionEnvironment();
731
679
  const result = await serializer(
732
680
  entryPoint,
733
681
  preModules,
734
682
  graph,
735
683
  serializerOptions
736
684
  );
737
- return productionBundle ? prependRNXModulePaths(result, graph, serializerOptions, cfgProjectRoot) : result;
685
+ return graph.transformOptions?.dev === false ? appendRNXModulePaths(result, graph, serializerOptions, cfgProjectRoot) : result;
738
686
  }
739
687
  }
740
688
  };
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/react-native-host-modules.ts
4
4
  var REACT_NATIVE_PASSTHROUGH_SPECIFIERS = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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;
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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 __defProp = Object.defineProperty;
@@ -1402,8 +1402,9 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
1402
1402
  };
1403
1403
  }
1404
1404
  const probe = result.probe;
1405
+ const externalError = probe?.externalError ?? "";
1405
1406
  const status = {
1406
- ready: result.matched,
1407
+ ready: result.matched && !externalError,
1407
1408
  elapsedMs: result.elapsedMs,
1408
1409
  nodes: probe?.nodes ?? 0,
1409
1410
  targets: probe?.targets ?? 0,
@@ -1414,7 +1415,7 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
1414
1415
  loadingText: probe?.loadingText ?? "",
1415
1416
  externalReady: probe?.externalReady ?? null,
1416
1417
  externalStatus: probe?.externalStatus ?? "",
1417
- externalError: probe?.externalError ?? "",
1418
+ externalError,
1418
1419
  suppressedEntryError: probe?.suppressedEntryError ?? "",
1419
1420
  errors: probe?.errors ?? 0,
1420
1421
  bridgeError: result.error ?? ""
@@ -1783,6 +1784,26 @@ async function setDebugChannels(bridge, action, channels) {
1783
1784
  });
1784
1785
  }
1785
1786
  async function inspectMemory(bridge) {
1787
+ if (bridge.plane === "cloud") {
1788
+ const value = await bridge.send({ type: "memory" });
1789
+ const rawWasmHeapBytes = typeof value === "object" && value !== null ? Reflect.get(value, "wasmHeapBytes") : null;
1790
+ const wasmHeapBytes = typeof rawWasmHeapBytes === "number" ? rawWasmHeapBytes : null;
1791
+ return {
1792
+ tenant: {
1793
+ nodesRegistered: null,
1794
+ nodesDetached: null,
1795
+ detachedTypes: null,
1796
+ objects: null,
1797
+ activeNativeAnimations: null,
1798
+ imageLoader: null,
1799
+ workerHeap: null,
1800
+ wasmHeapBytes
1801
+ },
1802
+ shell: null,
1803
+ compositor: null,
1804
+ hostHeap: null
1805
+ };
1806
+ }
1786
1807
  const result = await bridge.send({
1787
1808
  type: "evaluate",
1788
1809
  code: `(async () => {
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -1307,8 +1307,9 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
1307
1307
  };
1308
1308
  }
1309
1309
  const probe = result.probe;
1310
+ const externalError = probe?.externalError ?? "";
1310
1311
  const status = {
1311
- ready: result.matched,
1312
+ ready: result.matched && !externalError,
1312
1313
  elapsedMs: result.elapsedMs,
1313
1314
  nodes: probe?.nodes ?? 0,
1314
1315
  targets: probe?.targets ?? 0,
@@ -1319,7 +1320,7 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
1319
1320
  loadingText: probe?.loadingText ?? "",
1320
1321
  externalReady: probe?.externalReady ?? null,
1321
1322
  externalStatus: probe?.externalStatus ?? "",
1322
- externalError: probe?.externalError ?? "",
1323
+ externalError,
1323
1324
  suppressedEntryError: probe?.suppressedEntryError ?? "",
1324
1325
  errors: probe?.errors ?? 0,
1325
1326
  bridgeError: result.error ?? ""
@@ -1688,6 +1689,26 @@ async function setDebugChannels(bridge, action, channels) {
1688
1689
  });
1689
1690
  }
1690
1691
  async function inspectMemory(bridge) {
1692
+ if (bridge.plane === "cloud") {
1693
+ const value = await bridge.send({ type: "memory" });
1694
+ const rawWasmHeapBytes = typeof value === "object" && value !== null ? Reflect.get(value, "wasmHeapBytes") : null;
1695
+ const wasmHeapBytes = typeof rawWasmHeapBytes === "number" ? rawWasmHeapBytes : null;
1696
+ return {
1697
+ tenant: {
1698
+ nodesRegistered: null,
1699
+ nodesDetached: null,
1700
+ detachedTypes: null,
1701
+ objects: null,
1702
+ activeNativeAnimations: null,
1703
+ imageLoader: null,
1704
+ workerHeap: null,
1705
+ wasmHeapBytes
1706
+ },
1707
+ shell: null,
1708
+ compositor: null,
1709
+ hostHeap: null
1710
+ };
1711
+ }
1691
1712
  const result = await bridge.send({
1692
1713
  type: "evaluate",
1693
1714
  code: `(async () => {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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;
@@ -21396,12 +21396,34 @@ var init_cli_constants = __esm({
21396
21396
  });
21397
21397
 
21398
21398
  // src/cloud-contract.ts
21399
- var RNX_CLOUD_MAX_ARTIFACT_BYTES, RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES;
21399
+ function isRnxCloudCommandType(value) {
21400
+ return RNX_CLOUD_COMMAND_TYPE_SET.has(value);
21401
+ }
21402
+ var RNX_CLOUD_MAX_ARTIFACT_BYTES, RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES, RNX_CLOUD_COMMAND_TYPES, RNX_CLOUD_COMMAND_TYPE_SET;
21400
21403
  var init_cloud_contract = __esm({
21401
21404
  "src/cloud-contract.ts"() {
21402
21405
  "use strict";
21403
21406
  RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
21404
21407
  RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
21408
+ RNX_CLOUD_COMMAND_TYPES = Object.freeze([
21409
+ "state",
21410
+ "query",
21411
+ "resolve",
21412
+ "perform",
21413
+ "waitFor",
21414
+ "reset",
21415
+ "screenshot",
21416
+ "capture",
21417
+ "captureRegions",
21418
+ "settle",
21419
+ "setAppearance",
21420
+ "openUrl",
21421
+ "tree",
21422
+ "tap",
21423
+ "longPress",
21424
+ "memory"
21425
+ ]);
21426
+ RNX_CLOUD_COMMAND_TYPE_SET = new Set(RNX_CLOUD_COMMAND_TYPES);
21405
21427
  }
21406
21428
  });
21407
21429
 
@@ -21520,27 +21542,7 @@ function cloudCommand(command) {
21520
21542
  for (const [key, value] of Object.entries(command)) {
21521
21543
  if (key !== "id" && key !== "simId") clean[key] = value;
21522
21544
  }
21523
- if (command.type === "query") {
21524
- const query = command.query;
21525
- if (!isRecord4(query) || query.kind !== "tree" && query.kind !== "find" && query.kind !== "count") {
21526
- throw new Error("RNX Cloud supports only structured tree, node, and count queries");
21527
- }
21528
- return clean;
21529
- }
21530
- if (command.type === "waitFor") {
21531
- const options = command.waitForOptions;
21532
- const condition = isRecord4(options) ? options.condition : null;
21533
- if (!isRecord4(condition) || condition.type !== "ready" && condition.type !== "present" && condition.type !== "absent") {
21534
- throw new Error("RNX Cloud supports only ready and selector waits");
21535
- }
21536
- return clean;
21537
- }
21538
- if (command.type === "state") return clean;
21539
- if (command.type === "capture" || command.type === "captureRegions") return clean;
21540
- if (command.type === "setAppearance" || command.type === "openUrl") return clean;
21541
- if (command.type === "screenshot" && command.crop === void 0 && command.captureFilter === void 0 && (command.layers === void 0 || command.layers === "tenant")) {
21542
- return clean;
21543
- }
21545
+ if (isRnxCloudCommandType(command.type)) return clean;
21544
21546
  throw new Error(`RNX Cloud does not support bridge command type ${command.type}`);
21545
21547
  }
21546
21548
  function createCloudBridgeForParsed(parsed) {
@@ -23604,8 +23606,9 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
23604
23606
  };
23605
23607
  }
23606
23608
  const probe3 = result.probe;
23609
+ const externalError = probe3?.externalError ?? "";
23607
23610
  const status = {
23608
- ready: result.matched,
23611
+ ready: result.matched && !externalError,
23609
23612
  elapsedMs: result.elapsedMs,
23610
23613
  nodes: probe3?.nodes ?? 0,
23611
23614
  targets: probe3?.targets ?? 0,
@@ -23616,7 +23619,7 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
23616
23619
  loadingText: probe3?.loadingText ?? "",
23617
23620
  externalReady: probe3?.externalReady ?? null,
23618
23621
  externalStatus: probe3?.externalStatus ?? "",
23619
- externalError: probe3?.externalError ?? "",
23622
+ externalError,
23620
23623
  suppressedEntryError: probe3?.suppressedEntryError ?? "",
23621
23624
  errors: probe3?.errors ?? 0,
23622
23625
  bridgeError: result.error ?? ""
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.393 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.395 | (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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnxsim",
3
- "version": "0.1.393",
3
+ "version": "0.1.395",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for rnx.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -3,6 +3,7 @@
3
3
  // WebSocket routes) validates its request bodies here rather than growing a
4
4
  // second reader for the same shapes.
5
5
 
6
+ import { SIM_LONG_PRESS_MAX_MS } from './bridge-contract'
6
7
  import type {
7
8
  ResetOptions,
8
9
  SemanticWaitOptions,
@@ -130,6 +131,17 @@ export function parsePerformStep(value: unknown, index: number): SimPerformStep
130
131
  }
131
132
  case 'longPress': {
132
133
  const durationMs = optionalNumber(step, 'durationMs', path)
134
+ if (
135
+ durationMs !== undefined &&
136
+ (!Number.isInteger(durationMs) ||
137
+ durationMs < 1 ||
138
+ durationMs > SIM_LONG_PRESS_MAX_MS)
139
+ ) {
140
+ validationFailure(
141
+ `${path}.durationMs`,
142
+ `must be an integer between 1 and ${SIM_LONG_PRESS_MAX_MS}`,
143
+ )
144
+ }
133
145
  return {
134
146
  type,
135
147
  x: numberValue(step.x, `${path}.x`),
@@ -22,6 +22,7 @@ export type BridgeCommandType =
22
22
  | 'settle'
23
23
  | 'setAppearance'
24
24
  | 'openUrl'
25
+ | 'memory'
25
26
  | 'tap'
26
27
  | 'longPress'
27
28
  | 'keyboard'
@@ -216,6 +217,12 @@ export interface SemanticReadyProbe {
216
217
  suppressedEntryError: string
217
218
  }
218
219
 
220
+ // react native's own Pressable/Touchable delayLongPress default, and the
221
+ // ceiling any transport accepts for a press-and-hold. a driver that holds
222
+ // longer than this is describing a drag, not a long press.
223
+ export const SIM_LONG_PRESS_DEFAULT_MS = 500
224
+ export const SIM_LONG_PRESS_MAX_MS = 5_000
225
+
219
226
  export const SEMANTIC_READY_CONTENT_NODE_FLOOR = 100
220
227
  export const SEMANTIC_READY_NODE_STABLE_MS = 750
221
228
  export const SEMANTIC_READY_POLL_MS = 150
@@ -1,8 +1,37 @@
1
+ import type { BridgeCommandType } from './bridge-contract'
2
+
1
3
  export const RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024
2
4
  export const RNX_CLOUD_DESIGN_PARALLEL_JOB_LIMIT = 16
3
5
  export const RNX_CLOUD_DESIGN_MAX_STORAGE_ENTRIES = 256
4
6
  export const RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024
5
7
 
8
+ export const RNX_CLOUD_COMMAND_TYPES = Object.freeze([
9
+ 'state',
10
+ 'query',
11
+ 'resolve',
12
+ 'perform',
13
+ 'waitFor',
14
+ 'reset',
15
+ 'screenshot',
16
+ 'capture',
17
+ 'captureRegions',
18
+ 'settle',
19
+ 'setAppearance',
20
+ 'openUrl',
21
+ 'tree',
22
+ 'tap',
23
+ 'longPress',
24
+ 'memory',
25
+ ]) satisfies readonly BridgeCommandType[]
26
+
27
+ const RNX_CLOUD_COMMAND_TYPE_SET = new Set<string>(RNX_CLOUD_COMMAND_TYPES)
28
+
29
+ export function isRnxCloudCommandType(
30
+ value: string,
31
+ ): value is (typeof RNX_CLOUD_COMMAND_TYPES)[number] {
32
+ return RNX_CLOUD_COMMAND_TYPE_SET.has(value)
33
+ }
34
+
6
35
  export interface RnxCloudClaim {
7
36
  id: string
8
37
  expiresAt: number
@@ -1,4 +1,4 @@
1
- // rnx metro plugin for opt-in dev shell and production bundle integration.
1
+ // rnx metro plugin for opt-in dev shell and production bundle annotation.
2
2
  // shell assets come from an engine runtime under ~/.rnx/runtimes/, fetched
3
3
  // by `rnx runtime install`. ordinary localhost requests
4
4
  // follow active; a version-owned localhost origin stays on its exact directory
@@ -24,7 +24,6 @@ import {
24
24
  isReplacementModuleRequestUrl,
25
25
  } from './host/replacement-module-handler'
26
26
  import {
27
- RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION,
28
27
  createRNXMetroModulePathsFooter,
29
28
  toRNXProductionBundleUrl,
30
29
  } from './metro-production-bundle'
@@ -39,37 +38,10 @@ import {
39
38
  const prefix = '/__soot'
40
39
  export { toRNXProductionBundleUrl }
41
40
 
42
- const hostModuleSpecifiers = [
43
- 'react',
44
- 'react-dom',
45
- 'react/jsx-runtime',
46
- 'react/jsx-dev-runtime',
47
- 'react-native',
48
- 'react-native-web',
49
- ] as const
50
-
51
- type MetroResolverContext = {
52
- customResolverOptions?: Record<string, unknown>
53
- resolveRequest: MetroResolver
54
- }
55
-
56
- type MetroResolution = {
57
- type: string
58
- filePath?: string
59
- [key: string]: unknown
60
- }
61
-
62
- type MetroResolver = (
63
- context: MetroResolverContext,
64
- moduleName: string,
65
- platform: string | null,
66
- ) => MetroResolution
67
-
68
41
  type MetroSerializerGraph = {
69
42
  dependencies?: ReadonlyMap<unknown, { path: string }>
70
43
  transformOptions?: {
71
44
  dev?: boolean
72
- customTransformOptions?: Record<string, unknown>
73
45
  }
74
46
  }
75
47
 
@@ -92,6 +64,11 @@ export interface RNXMetroOptions {
92
64
  bundleUrl?: string
93
65
  devServer?: boolean
94
66
  open?: RnxDesktopOpenOption
67
+ /**
68
+ * append the `moduleId -> sourcePath` identity footer to every production
69
+ * bundle. resolution is untouched, so the annotated artifact is the same
70
+ * bundle the app ships and rnx can load it directly.
71
+ */
95
72
  productionBundles?: boolean
96
73
  }
97
74
 
@@ -99,11 +76,6 @@ export interface RNXMetroOptions {
99
76
  // shape consumers pass while letting us extend `server.enhanceMiddleware`.
100
77
  type MetroConfigWithServer = {
101
78
  projectRoot?: string
102
- watchFolders?: readonly string[]
103
- resolver?: {
104
- resolveRequest?: MetroResolver
105
- [key: string]: unknown
106
- }
107
79
  serializer?: {
108
80
  customSerializer?: MetroSerializer
109
81
  [key: string]: unknown
@@ -115,38 +87,6 @@ type MetroConfigWithServer = {
115
87
  }
116
88
  }
117
89
 
118
- function isRNXProductionBundle(graph: MetroSerializerGraph): boolean {
119
- return (
120
- graph.transformOptions?.dev === false &&
121
- graph.transformOptions.customTransformOptions?.[
122
- RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION
123
- ] === 'true'
124
- )
125
- }
126
-
127
- function assertProductionEnvironment(): void {
128
- if (process.env.NODE_ENV === 'production') return
129
- throw new Error(
130
- 'rnxsim: production bundle requests require NODE_ENV=production; restart Metro with NODE_ENV=production',
131
- )
132
- }
133
-
134
- function hostModuleFileName(specifier: (typeof hostModuleSpecifiers)[number]): string {
135
- return `${specifier.replaceAll('/', '__')}.js`
136
- }
137
-
138
- function resolveHostModules(): { root: string; files: ReadonlyMap<string, string> } {
139
- const root = path.join(__dirname, 'metro-host-modules')
140
- const files = new Map<string, string>()
141
-
142
- for (const specifier of hostModuleSpecifiers) {
143
- const filePath = path.join(root, hostModuleFileName(specifier))
144
- files.set(specifier, filePath)
145
- }
146
-
147
- return { root, files }
148
- }
149
-
150
90
  function readCallableMetroExport(
151
91
  moduleValue: unknown,
152
92
  label: string,
@@ -190,7 +130,12 @@ function createMetroDefaultSerializer(projectRoot: string): MetroSerializer {
190
130
  }
191
131
  }
192
132
 
193
- function prependRNXModulePaths(
133
+ // the only thing rnx adds to a production bundle. Metro drops the fourth `__d`
134
+ // argument when `dev` is false, so nothing in the artifact names its modules;
135
+ // the engine's `__d` trap needs those names to swap react, react-native, and
136
+ // the native-seam packages for its own. the footer restores exactly that, and
137
+ // is inert everywhere else — on a device it assigns one unused global.
138
+ function appendRNXModulePaths(
194
139
  result: MetroSerializerResult,
195
140
  graph: MetroSerializerGraph,
196
141
  options: MetroSerializerOptions,
@@ -241,42 +186,15 @@ export function withRNX<T extends Record<string, unknown>>(
241
186
  cfgProjectRoot,
242
187
  appName,
243
188
  )
244
- const cfgResolver = (config as MetroConfigWithServer).resolver
245
189
  const cfgSerializer = (config as MetroConfigWithServer).serializer
246
190
  const existingEnhance = cfgServer?.enhanceMiddleware
247
- const existingResolveRequest = cfgResolver?.resolveRequest
248
191
  const existingSerializer = cfgSerializer?.customSerializer
249
192
  let productionConfig: Partial<MetroConfigWithServer> = {}
250
193
 
251
194
  if (options.productionBundles) {
252
- const hostModules = resolveHostModules()
253
195
  let defaultSerializer: MetroSerializer | undefined
254
196
 
255
197
  productionConfig = {
256
- watchFolders: [
257
- ...((config as MetroConfigWithServer).watchFolders || []),
258
- hostModules.root,
259
- ],
260
- resolver: {
261
- ...cfgResolver,
262
- resolveRequest: (
263
- context: MetroResolverContext,
264
- moduleName: string,
265
- platform: string | null,
266
- ) => {
267
- if (
268
- context.customResolverOptions?.[RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION] ===
269
- 'true'
270
- ) {
271
- assertProductionEnvironment()
272
- const hostModule = hostModules.files.get(moduleName)
273
- if (hostModule) return { type: 'sourceFile', filePath: hostModule }
274
- }
275
- return existingResolveRequest
276
- ? existingResolveRequest(context, moduleName, platform)
277
- : context.resolveRequest(context, moduleName, platform)
278
- },
279
- },
280
198
  serializer: {
281
199
  ...cfgSerializer,
282
200
  customSerializer: async (
@@ -288,16 +206,17 @@ export function withRNX<T extends Record<string, unknown>>(
288
206
  const serializer =
289
207
  existingSerializer ||
290
208
  (defaultSerializer ||= createMetroDefaultSerializer(cfgProjectRoot))
291
- const productionBundle = isRNXProductionBundle(graph)
292
- if (productionBundle) assertProductionEnvironment()
293
209
  const result = await serializer(
294
210
  entryPoint,
295
211
  preModules,
296
212
  graph,
297
213
  serializerOptions,
298
214
  )
299
- return productionBundle
300
- ? prependRNXModulePaths(result, graph, serializerOptions, cfgProjectRoot)
215
+ // every production build gets the footer, so the artifact the app
216
+ // already ships is the one rnx loads. a development build keeps
217
+ // Metro's fourth `__d` argument and needs nothing.
218
+ return graph.transformOptions?.dev === false
219
+ ? appendRNXModulePaths(result, graph, serializerOptions, cfgProjectRoot)
301
220
  : result
302
221
  },
303
222
  },