rnxsim 0.1.520 → 0.1.521

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 (58) hide show
  1. package/dist-lib/agent-daemon-client.cjs +1 -1
  2. package/dist-lib/agent-events.cjs +1 -1
  3. package/dist-lib/agent-identity.cjs +1 -1
  4. package/dist-lib/agent-sessions.cjs +1 -1
  5. package/dist-lib/attached-projects.cjs +1 -1
  6. package/dist-lib/auth/shared-session.cjs +1 -1
  7. package/dist-lib/backend-origin.cjs +1 -1
  8. package/dist-lib/beta.cjs +1 -1
  9. package/dist-lib/beta.mjs +1 -1
  10. package/dist-lib/bridge-constants.cjs +1 -1
  11. package/dist-lib/bridge-contract-input.cjs +7 -2
  12. package/dist-lib/bridge-contract-input.mjs +7 -2
  13. package/dist-lib/bridge-contract.cjs +1 -1
  14. package/dist-lib/bridge-contract.mjs +1 -1
  15. package/dist-lib/capture-contract.cjs +1 -1
  16. package/dist-lib/capture-contract.mjs +1 -1
  17. package/dist-lib/cli-constants.cjs +1 -1
  18. package/dist-lib/cloud-contract.cjs +1 -1
  19. package/dist-lib/cloud-contract.mjs +1 -1
  20. package/dist-lib/cloud.cjs +18 -2
  21. package/dist-lib/cloud.mjs +18 -2
  22. package/dist-lib/config.cjs +1 -1
  23. package/dist-lib/detox/index.cjs +1 -1
  24. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  25. package/dist-lib/home-paths.cjs +1 -1
  26. package/dist-lib/host/bridge-host.cjs +1 -1
  27. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  28. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  29. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  30. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  31. package/dist-lib/host/websocket-proxy.cjs +1 -1
  32. package/dist-lib/index.cjs +3 -2
  33. package/dist-lib/jump-to-source-babel.cjs +1 -1
  34. package/dist-lib/jump-to-source-native.cjs +1 -1
  35. package/dist-lib/menu.cjs +1 -1
  36. package/dist-lib/menu.mjs +1 -1
  37. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  38. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  39. package/dist-lib/metro-production-bundle.cjs +1 -1
  40. package/dist-lib/metro-production-bundle.mjs +1 -1
  41. package/dist-lib/metro.cjs +1 -1
  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 +1 -1
  49. package/dist-lib/sdk.mjs +1 -1
  50. package/dist-lib/skills.cjs +1 -1
  51. package/dist-lib/swift.cjs +93 -13
  52. package/dist-lib/vite.cjs +79 -29
  53. package/package.json +1 -1
  54. package/src/bridge-contract-input.ts +10 -0
  55. package/src/bridge-contract.ts +5 -0
  56. package/src/cloud.ts +34 -2
  57. package/src/swift.ts +2 -0
  58. package/src/vite-plugin-swift.ts +124 -34
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -388,9 +388,14 @@ function parseResetOptions(value) {
388
388
  if (strategy !== "data" && strategy !== "full") {
389
389
  return validationFailure("reset.strategy", "must be data or full");
390
390
  }
391
+ const from = input.from;
392
+ if (from !== void 0 && (typeof from !== "string" || from.length === 0 || from.length > 64)) {
393
+ return validationFailure("reset.from", "must name a simulator");
394
+ }
391
395
  return {
392
396
  strategy,
393
- relaunch: optionalBoolean(input, "relaunch", "reset") ?? true
397
+ relaunch: optionalBoolean(input, "relaunch", "reset") ?? true,
398
+ ...from === void 0 ? {} : { from }
394
399
  };
395
400
  }
396
401
  function parseScreenshotOptions(value) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -352,9 +352,14 @@ function parseResetOptions(value) {
352
352
  if (strategy !== "data" && strategy !== "full") {
353
353
  return validationFailure("reset.strategy", "must be data or full");
354
354
  }
355
+ const from = input.from;
356
+ if (from !== void 0 && (typeof from !== "string" || from.length === 0 || from.length > 64)) {
357
+ return validationFailure("reset.from", "must name a simulator");
358
+ }
355
359
  return {
356
360
  strategy,
357
- relaunch: optionalBoolean(input, "relaunch", "reset") ?? true
361
+ relaunch: optionalBoolean(input, "relaunch", "reset") ?? true,
362
+ ...from === void 0 ? {} : { from }
358
363
  };
359
364
  }
360
365
  function parseScreenshotOptions(value) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var REFUSED_PERFORM_STEP_TYPES = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-contract.ts
4
4
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -406,7 +406,23 @@ var RemoteSimulator = class {
406
406
  }
407
407
  return result;
408
408
  },
409
- reset: () => this.sendCommand({ type: "reset" }),
409
+ reset: (options) => {
410
+ if (options?.strategy !== void 0 && options.strategy !== "data" && options.strategy !== "full") {
411
+ throw new Error("RNX Cloud reset strategy must be data or full");
412
+ }
413
+ if (options?.from !== void 0 && (typeof options.from !== "string" || options.from.length === 0)) {
414
+ throw new Error("RNX Cloud reset from must name a simulator");
415
+ }
416
+ return this.sendCommand(
417
+ options === void 0 ? { type: "reset" } : {
418
+ type: "reset",
419
+ resetOptions: {
420
+ ...options.strategy === void 0 ? {} : { strategy: options.strategy },
421
+ ...options.from === void 0 ? {} : { from: options.from }
422
+ }
423
+ }
424
+ );
425
+ },
410
426
  state: (options) => this.sendCommand(
411
427
  options === void 0 ? { type: "state" } : { type: "state", stateOptions: options }
412
428
  )
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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);
@@ -376,7 +376,23 @@ var RemoteSimulator = class {
376
376
  }
377
377
  return result;
378
378
  },
379
- reset: () => this.sendCommand({ type: "reset" }),
379
+ reset: (options) => {
380
+ if (options?.strategy !== void 0 && options.strategy !== "data" && options.strategy !== "full") {
381
+ throw new Error("RNX Cloud reset strategy must be data or full");
382
+ }
383
+ if (options?.from !== void 0 && (typeof options.from !== "string" || options.from.length === 0)) {
384
+ throw new Error("RNX Cloud reset from must name a simulator");
385
+ }
386
+ return this.sendCommand(
387
+ options === void 0 ? { type: "reset" } : {
388
+ type: "reset",
389
+ resetOptions: {
390
+ ...options.strategy === void 0 ? {} : { strategy: options.strategy },
391
+ ...options.from === void 0 ? {} : { from: options.from }
392
+ }
393
+ }
394
+ );
395
+ },
380
396
  state: (options) => this.sendCommand(
381
397
  options === void 0 ? { type: "state" } : { type: "state", stateOptions: options }
382
398
  )
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -3832,7 +3832,8 @@ var import_node_util = require("node:util");
3832
3832
 
3833
3833
  // ../sootsim-swift/toolchain.json
3834
3834
  var toolchain_default = {
3835
- release: "swift-6.3-RELEASE"
3835
+ release: "swift-6.3-RELEASE",
3836
+ wasmSdkChecksum: "9fa4016ee632c7e9e906608ec3b55cf13dfc4dff44e47574c5af58064dc33fd9"
3836
3837
  };
3837
3838
 
3838
3839
  // src/vite-plugin-swift.ts
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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/menu.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "rnx";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-fingerprint-registry.ts
4
4
  var RNX_METRO_FINGERPRINT_SCHEMA_VERSION = 2;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-production-bundle.ts
4
4
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -32,7 +32,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  // src/swift.ts
33
33
  var swift_exports = {};
34
34
  __export(swift_exports, {
35
- createSwiftModule: () => createSwiftModule
35
+ createSwiftModule: () => createSwiftModule,
36
+ createSwiftView: () => createSwiftView
36
37
  });
37
38
  module.exports = __toCommonJS(swift_exports);
38
39
 
@@ -189,6 +190,20 @@ function pointerExport(exports2, name) {
189
190
  return result;
190
191
  };
191
192
  }
193
+ function maybePointerExport(exports2, name) {
194
+ const value = exports2[name];
195
+ if (value === void 0) return void 0;
196
+ if (typeof value !== "function") {
197
+ throw new Error(`swift export ${name} is not a function`);
198
+ }
199
+ return (...args) => {
200
+ const result = Reflect.apply(value, void 0, args);
201
+ if (typeof result !== "number") {
202
+ throw new Error(`swift export ${name} returned ${typeof result}`);
203
+ }
204
+ return result;
205
+ };
206
+ }
192
207
  function voidExport(exports2, name) {
193
208
  const fn = rawExport(exports2, name);
194
209
  return (...args) => {
@@ -221,6 +236,8 @@ function instantiate(module2) {
221
236
  const initialize = voidExport(instance.exports, "_initialize");
222
237
  const mainArgv = voidExport(instance.exports, "__main_argc_argv");
223
238
  const mount = pointerExport(instance.exports, "rnx_mount");
239
+ const mountViewExport = maybePointerExport(instance.exports, "rnx_mount_view");
240
+ const hasViewExport = maybePointerExport(instance.exports, "rnx_has_view");
224
241
  const event = pointerExport(instance.exports, "rnx_event");
225
242
  const stateExport = pointerExport(instance.exports, "rnx_state_export");
226
243
  const stateImport = voidExport(instance.exports, "rnx_state_import");
@@ -244,7 +261,30 @@ function instantiate(module2) {
244
261
  initialize();
245
262
  mainArgv(0, 0);
246
263
  return {
247
- mount: () => readTree(readBuffer(mount())),
264
+ mount: () => {
265
+ if ((hasViewExport?.() ?? 0) === 1) {
266
+ throw new Error(
267
+ "swift package has an RNXPackage entry, not an App entry; mount it as a view or make its @main type conform to App"
268
+ );
269
+ }
270
+ return readTree(readBuffer(mount()));
271
+ },
272
+ mountView: (props) => {
273
+ if (!mountViewExport) {
274
+ throw new Error(
275
+ "swift artifact has no rnx_mount_view export; rebuild the swift package"
276
+ );
277
+ }
278
+ if ((hasViewExport?.() ?? 0) !== 1) {
279
+ throw new Error(
280
+ "swift package has no RNXPackage entry; its @main type must conform to RNXPackage to mount as a view"
281
+ );
282
+ }
283
+ return readTree(
284
+ readBuffer(withPayload(props, (ptr, length) => mountViewExport(ptr, length)))
285
+ );
286
+ },
287
+ hasView: () => (hasViewExport?.() ?? 0) === 1,
248
288
  deliver: (handlerId2, payload) => readTree(
249
289
  readBuffer(
250
290
  withPayload(
@@ -628,8 +668,10 @@ function nodeProps(node, deliver) {
628
668
  }
629
669
  case "BottomSheet": {
630
670
  const onDismiss = handlerId(node, "onDismiss");
671
+ const detents = (node.c ?? []).flatMap((child) => nodeModifiers(child) ?? []).filter((modifier) => modifier.$type === "presentationDetents");
631
672
  return {
632
673
  isOpened: props.isOpened === true,
674
+ ...detents.length === 0 ? {} : { modifiers: detents },
633
675
  ...onDismiss === void 0 ? {} : { onDismiss: () => deliver(onDismiss, null) }
634
676
  };
635
677
  }
@@ -1099,9 +1141,18 @@ function rootRegistry() {
1099
1141
  Reflect.set(globalThis, ROOT_KEY, created);
1100
1142
  return created;
1101
1143
  }
1102
- function createRoot() {
1144
+ function slotFor(rootId, mountTree, props) {
1145
+ const registry = rootRegistry();
1146
+ const existing = registry.get(rootId);
1147
+ if (existing) return existing;
1148
+ const created = { root: createRoot(mountTree, props), props };
1149
+ registry.set(rootId, created);
1150
+ return created;
1151
+ }
1152
+ function createRoot(mountTree, props) {
1103
1153
  let instance = null;
1104
1154
  let source = null;
1155
+ let refresh;
1105
1156
  let state = { tree: null, error: null };
1106
1157
  let swap = Promise.resolve();
1107
1158
  const listeners = /* @__PURE__ */ new Set();
@@ -1116,7 +1167,7 @@ function createRoot() {
1116
1167
  if (carried !== null) created.importState(carried);
1117
1168
  if (source !== options) return;
1118
1169
  instance = created;
1119
- publish({ tree: created.mount(), error: null });
1170
+ publish({ tree: mountTree(created, props.current), error: null });
1120
1171
  };
1121
1172
  const component = function SwiftRoot() {
1122
1173
  const rendered = (0, import_react.useSyncExternalStore)(
@@ -1136,10 +1187,11 @@ function createRoot() {
1136
1187
  };
1137
1188
  return {
1138
1189
  component,
1139
- use(options) {
1140
- const changed = source === null || source.hash !== options.hash || source.url !== options.url;
1190
+ use(options, refreshKey) {
1191
+ const changed = source === null || source.hash !== options.hash || source.url !== options.url || refreshKey !== void 0 && refreshKey !== refresh;
1141
1192
  if (changed) {
1142
1193
  source = options;
1194
+ refresh = refreshKey;
1143
1195
  swap = swap.then(
1144
1196
  () => apply(options),
1145
1197
  () => apply(options)
@@ -1157,13 +1209,41 @@ function createRoot() {
1157
1209
  }
1158
1210
  function createSwiftModule(options) {
1159
1211
  const rootId = options.rootId ?? "root";
1160
- const registry = rootRegistry();
1161
- const existing = registry.get(rootId);
1162
- const root = existing ?? createRoot();
1163
- if (!existing) registry.set(rootId, root);
1164
- return root.use(options);
1212
+ const slot = slotFor(rootId, (instance) => instance.mount(), { current: {} });
1213
+ return slot.root.use(options);
1214
+ }
1215
+ function propsSignature(props) {
1216
+ try {
1217
+ return JSON.stringify(props, (_key, value) => {
1218
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
1219
+ throw new Error(`swift view props must be json; found ${typeof value}`);
1220
+ }
1221
+ return value;
1222
+ }) ?? "null";
1223
+ } catch (error) {
1224
+ if (error instanceof Error && error.message.startsWith("swift view props"))
1225
+ throw error;
1226
+ throw new Error(
1227
+ `swift view props must be json: ${error instanceof Error ? error.message : String(error)}`
1228
+ );
1229
+ }
1230
+ }
1231
+ function createSwiftView(options) {
1232
+ const rootId = options.rootId ?? "view";
1233
+ const slot = slotFor(rootId, (instance, props) => instance.mountView(props), {
1234
+ current: {}
1235
+ });
1236
+ return function SwiftView(props) {
1237
+ if (props.children !== void 0) {
1238
+ throw new Error("swift views take data props, not children");
1239
+ }
1240
+ slot.props.current = props;
1241
+ const component = slot.root.use(options, propsSignature(props));
1242
+ return (0, import_react.createElement)(component);
1243
+ };
1165
1244
  }
1166
1245
  // Annotate the CommonJS export names for ESM import in node:
1167
1246
  0 && (module.exports = {
1168
- createSwiftModule
1247
+ createSwiftModule,
1248
+ createSwiftView
1169
1249
  });
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -765,7 +765,8 @@ var import_node_util = require("node:util");
765
765
 
766
766
  // ../sootsim-swift/toolchain.json
767
767
  var toolchain_default = {
768
- release: "swift-6.3-RELEASE"
768
+ release: "swift-6.3-RELEASE",
769
+ wasmSdkChecksum: "9fa4016ee632c7e9e906608ec3b55cf13dfc4dff44e47574c5af58064dc33fd9"
769
770
  };
770
771
 
771
772
  // src/vite-plugin-swift.ts
@@ -787,30 +788,50 @@ function projectFor(packagePath) {
787
788
  projects.set(packagePath, created);
788
789
  return created;
789
790
  }
790
- function rootFor(file) {
791
- return devServer?.config.root ?? import_node_path4.default.dirname(file);
792
- }
793
- function sourceFiles(packagePath) {
791
+ function sourceFiles(root) {
794
792
  const found = [];
795
793
  const walk = (dir) => {
796
794
  for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
797
795
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
798
796
  const full = import_node_path4.default.join(dir, entry.name);
799
- if (entry.isDirectory()) walk(full);
800
- else if (entry.name.endsWith(".swift")) found.push(full);
797
+ if (entry.isDirectory()) {
798
+ if (full !== root && import_node_fs3.default.existsSync(import_node_path4.default.join(full, "Package.swift"))) continue;
799
+ walk(full);
800
+ } else if (entry.name.endsWith(".swift")) found.push(full);
801
801
  }
802
802
  };
803
- walk(packagePath);
803
+ walk(root);
804
804
  return found;
805
805
  }
806
+ function linkedModuleRoots(packagePath) {
807
+ let manifest;
808
+ try {
809
+ manifest = import_node_fs3.default.readFileSync(import_node_path4.default.join(packagePath, "Package.swift"), "utf8");
810
+ } catch {
811
+ return [];
812
+ }
813
+ const roots = /* @__PURE__ */ new Set();
814
+ const dependency = /\.package\(\s*path:\s*"([^"]+)"\s*\)/g;
815
+ for (const match of manifest.replace(/^\s*\/\/.*$/gm, "").matchAll(dependency)) {
816
+ const root = import_node_path4.default.resolve(packagePath, match[1]);
817
+ if (import_node_fs3.default.existsSync(import_node_path4.default.join(root, "Package.swift"))) roots.add(root);
818
+ }
819
+ return [...roots].sort();
820
+ }
806
821
  function sourceStamp(packagePath) {
807
822
  const hash = (0, import_node_crypto2.createHash)("sha256");
808
- for (const file of sourceFiles(packagePath).sort()) {
809
- hash.update(import_node_path4.default.relative(packagePath, file));
810
- hash.update(String(import_node_fs3.default.statSync(file).mtimeMs));
823
+ for (const root of [packagePath, ...linkedModuleRoots(packagePath)]) {
824
+ hash.update(root);
825
+ for (const file of sourceFiles(root).sort()) {
826
+ hash.update(import_node_path4.default.relative(root, file));
827
+ hash.update(String(import_node_fs3.default.statSync(file).mtimeMs));
828
+ }
811
829
  }
812
830
  return hash.digest("hex").slice(0, 16);
813
831
  }
832
+ function viewRootId(packagePath, file) {
833
+ return import_node_path4.default.join(packagePath, import_node_path4.default.relative(packagePath, file));
834
+ }
814
835
  function artifactUrl(project, artifact) {
815
836
  const address = devServer?.httpServer?.address();
816
837
  const port = address && typeof address === "object" ? address.port : devServer?.config.server.port;
@@ -879,8 +900,27 @@ function artifactFor(project) {
879
900
  );
880
901
  return project.building;
881
902
  }
903
+ function packageFor(file) {
904
+ let dir = import_node_path4.default.dirname(file);
905
+ const stop = devServer?.config.root;
906
+ for (; ; ) {
907
+ if (import_node_fs3.default.existsSync(import_node_path4.default.join(dir, "Package.swift"))) return dir;
908
+ if (stop !== void 0 && dir === stop || import_node_path4.default.dirname(dir) === dir) break;
909
+ dir = import_node_path4.default.dirname(dir);
910
+ }
911
+ throw new Error(
912
+ `no Package.swift above ${file}${stop === void 0 ? "" : ` (searched up to ${stop})`}; a .swift import needs a swiftpm package like the one in packages/sootsim-swift/example`
913
+ );
914
+ }
915
+ function isViewPackage(packagePath) {
916
+ const main = /@main\s+(?:\w+\s+)*(?:struct|class|enum|actor)\s+\w+[^{]*:\s*[^{]*\bRNXPackage\b/;
917
+ for (const file of sourceFiles(packagePath)) {
918
+ const text = import_node_fs3.default.readFileSync(file, "utf8").replace(/^\s*\/\/.*$/gm, "");
919
+ if (main.test(text)) return true;
920
+ }
921
+ return false;
922
+ }
882
923
  function swiftPlugin(options = {}) {
883
- const packagePathFor = (file) => options.packagePath ?? rootFor(file);
884
924
  return {
885
925
  name: "rnx-swift",
886
926
  enforce: "pre",
@@ -897,9 +937,13 @@ function swiftPlugin(options = {}) {
897
937
  },
898
938
  async load(id) {
899
939
  if (!id.startsWith(VIRTUAL_PREFIX)) return null;
900
- const packagePath = packagePathFor(id.slice(VIRTUAL_PREFIX.length));
940
+ const file = id.slice(VIRTUAL_PREFIX.length);
941
+ const packagePath = options.packagePath ?? packageFor(file);
901
942
  const project = projectFor(packagePath);
902
943
  for (const source of sourceFiles(packagePath)) this.addWatchFile(source);
944
+ for (const root of linkedModuleRoots(packagePath)) {
945
+ for (const source of sourceFiles(root)) this.addWatchFile(source);
946
+ }
903
947
  let artifact;
904
948
  try {
905
949
  artifact = await artifactFor(project);
@@ -908,6 +952,14 @@ function swiftPlugin(options = {}) {
908
952
  this.warn(error instanceof Error ? error.message : String(error));
909
953
  artifact = project.artifact;
910
954
  }
955
+ if (isViewPackage(packagePath)) {
956
+ const rootId = viewRootId(packagePath, file);
957
+ return [
958
+ `import { createSwiftView } from 'rnxsim/swift'`,
959
+ `export default createSwiftView({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)}, rootId: ${JSON.stringify(rootId)} })`,
960
+ ""
961
+ ].join("\n");
962
+ }
911
963
  return [
912
964
  `import { createSwiftModule } from 'rnxsim/swift'`,
913
965
  `export default createSwiftModule({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)} })`,
@@ -916,30 +968,28 @@ function swiftPlugin(options = {}) {
916
968
  },
917
969
  configureServer(server) {
918
970
  devServer = server;
919
- const packagePath = packagePathFor("");
920
971
  server.middlewares.use((req, res, next) => {
921
972
  const url = (req.url ?? "").split("?")[0];
922
973
  if (!url.startsWith(ARTIFACT_PATH)) {
923
974
  next();
924
975
  return;
925
976
  }
926
- const project = projectFor(packagePath);
927
- void artifactFor(project).then(
928
- (artifact) => {
929
- if (url !== `${ARTIFACT_PATH}${artifact.hash}.wasm`) {
930
- res.statusCode = 404;
931
- res.end("swift artifact is stale");
977
+ void (async () => {
978
+ for (const project of projects.values()) {
979
+ const artifact = await artifactFor(project);
980
+ if (url === `${ARTIFACT_PATH}${artifact.hash}.wasm`) {
981
+ res.setHeader("access-control-allow-origin", "*");
982
+ res.setHeader("content-type", "application/wasm");
983
+ res.end(artifact.bytes);
932
984
  return;
933
985
  }
934
- res.setHeader("access-control-allow-origin", "*");
935
- res.setHeader("content-type", "application/wasm");
936
- res.end(artifact.bytes);
937
- },
938
- (error) => {
939
- res.statusCode = 500;
940
- res.end(error instanceof Error ? error.message : String(error));
941
986
  }
942
- );
987
+ res.statusCode = 404;
988
+ res.end("swift artifact is stale");
989
+ })().catch((error) => {
990
+ res.statusCode = 500;
991
+ res.end(error instanceof Error ? error.message : String(error));
992
+ });
943
993
  });
944
994
  }
945
995
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnxsim",
3
- "version": "0.1.520",
3
+ "version": "0.1.521",
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",
@@ -435,9 +435,19 @@ export function parseResetOptions(value: unknown): ResetOptions {
435
435
  if (strategy !== 'data' && strategy !== 'full') {
436
436
  return validationFailure('reset.strategy', 'must be data or full')
437
437
  }
438
+ // the shape check only; the simulator service re-validates the id against
439
+ // its own strict pattern and the caller's account before touching storage.
440
+ const from = input.from
441
+ if (
442
+ from !== undefined &&
443
+ (typeof from !== 'string' || from.length === 0 || from.length > 64)
444
+ ) {
445
+ return validationFailure('reset.from', 'must name a simulator')
446
+ }
438
447
  return {
439
448
  strategy,
440
449
  relaunch: optionalBoolean(input, 'relaunch', 'reset') ?? true,
450
+ ...(from === undefined ? {} : { from }),
441
451
  }
442
452
  }
443
453
 
@@ -502,6 +502,11 @@ export interface ResetOptions extends Pick<
502
502
  strategy?: ResetStrategy
503
503
  // relaunch the app after wiping. default true.
504
504
  relaunch?: boolean
505
+ // boot the wiped simulator on a live copy of another simulator's storage
506
+ // instead of empty stores: the cheap clean-but-not-cold seam. the id names
507
+ // a simulator on the same account; runtimes that cannot reach another
508
+ // simulator's storage refuse it by name rather than wiping without forking.
509
+ from?: string
505
510
  }
506
511
 
507
512
  export interface ResetResult {
package/src/cloud.ts CHANGED
@@ -93,7 +93,12 @@ export interface RnxCommands {
93
93
  // a `data:image/png;base64,` url, assembled by the service from the frame
94
94
  screenshot(options?: { layers?: 'full' | 'tenant' | 'shell' }): Promise<string>
95
95
  capture(): Promise<RnxScreenCapture>
96
- reset(): Promise<unknown>
96
+ // data wipes the app's stores and cold-remounts, keeping install-scoped
97
+ // credentials; full clears those too. from boots the wiped simulator on a
98
+ // wholesale live copy of another simulator's storage instead of empty
99
+ // stores. the bundle itself is never reinstalled: artifacts are immutable
100
+ // and sha-addressed, so there is no install to redo.
101
+ reset(options?: { strategy?: 'data' | 'full'; from?: string }): Promise<unknown>
97
102
  // the one-call read: screenshot, tree, route, and recent errors together.
98
103
  state(options?: SimStateOptions): Promise<unknown>
99
104
  }
@@ -433,7 +438,34 @@ export class RemoteSimulator {
433
438
  }
434
439
  return result
435
440
  },
436
- reset: () => this.sendCommand({ type: 'reset' }),
441
+ reset: (options) => {
442
+ if (
443
+ options?.strategy !== undefined &&
444
+ options.strategy !== 'data' &&
445
+ options.strategy !== 'full'
446
+ ) {
447
+ throw new Error('RNX Cloud reset strategy must be data or full')
448
+ }
449
+ if (
450
+ options?.from !== undefined &&
451
+ (typeof options.from !== 'string' || options.from.length === 0)
452
+ ) {
453
+ throw new Error('RNX Cloud reset from must name a simulator')
454
+ }
455
+ return this.sendCommand(
456
+ options === undefined
457
+ ? { type: 'reset' }
458
+ : {
459
+ type: 'reset',
460
+ resetOptions: {
461
+ ...(options.strategy === undefined
462
+ ? {}
463
+ : { strategy: options.strategy }),
464
+ ...(options.from === undefined ? {} : { from: options.from }),
465
+ },
466
+ },
467
+ )
468
+ },
437
469
  state: (options) =>
438
470
  this.sendCommand(
439
471
  options === undefined
package/src/swift.ts CHANGED
@@ -2,5 +2,7 @@
2
2
  // implementation lives beside the toolchain it drives, in sootsim-swift.
3
3
  export {
4
4
  createSwiftModule,
5
+ createSwiftView,
5
6
  type SwiftModuleOptions,
7
+ type SwiftViewOptions,
6
8
  } from '../../sootsim-swift/src/mount.tsx'
@@ -63,35 +63,71 @@ function projectFor(packagePath: string): SwiftProject {
63
63
  return created
64
64
  }
65
65
 
66
- function rootFor(file: string): string {
67
- return devServer?.config.root ?? path.dirname(file)
68
- }
69
-
70
66
  // the compile unit is the whole package: `swift build` compiles every source
71
- // in it, so one file's edit can change the artifact for all of them.
72
- function sourceFiles(packagePath: string): string[] {
67
+ // in it, so one file's edit can change the artifact for all of them. also
68
+ // used for a linked module below, whose own nested packages are separate
69
+ // units by the same rule.
70
+ function sourceFiles(root: string): string[] {
73
71
  const found: string[] = []
74
72
  const walk = (dir: string): void => {
75
73
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
76
74
  if (entry.name.startsWith('.') || entry.name === 'node_modules') continue
77
75
  const full = path.join(dir, entry.name)
78
- if (entry.isDirectory()) walk(full)
79
- else if (entry.name.endsWith('.swift')) found.push(full)
76
+ // a nested Package.swift is a separate package with its own compile:
77
+ // its sources belong to that unit's stamp, not this one's
78
+ if (entry.isDirectory()) {
79
+ if (full !== root && fs.existsSync(path.join(full, 'Package.swift'))) continue
80
+ walk(full)
81
+ } else if (entry.name.endsWith('.swift')) found.push(full)
80
82
  }
81
83
  }
82
- walk(packagePath)
84
+ walk(root)
83
85
  return found
84
86
  }
85
87
 
86
- function sourceStamp(packagePath: string): string {
88
+ // the SwiftUI module the project links: its manifest names it as a path
89
+ // dependency, and `swift build` compiles it with the project, so an edit
90
+ // there changes the artifact without moving any of the project's own mtimes.
91
+ export function linkedModuleRoots(packagePath: string): string[] {
92
+ let manifest: string
93
+ try {
94
+ manifest = fs.readFileSync(path.join(packagePath, 'Package.swift'), 'utf8')
95
+ } catch {
96
+ return []
97
+ }
98
+ const roots = new Set<string>()
99
+ const dependency = /\.package\(\s*path:\s*"([^"]+)"\s*\)/g
100
+ for (const match of manifest.replace(/^\s*\/\/.*$/gm, '').matchAll(dependency)) {
101
+ const root = path.resolve(packagePath, match[1])
102
+ if (fs.existsSync(path.join(root, 'Package.swift'))) roots.add(root)
103
+ }
104
+ return [...roots].sort()
105
+ }
106
+
107
+ // the stamp covers the project plus the modules it links: a running dev
108
+ // server rebuilds only when this moves, so leaving the linked sources out
109
+ // would keep serving the artifact built before a SwiftUI module edit for as
110
+ // long as the project's own sources stayed the same. mirrors the compile
111
+ // service's artifact hash, which names the same closure.
112
+ export function sourceStamp(packagePath: string): string {
87
113
  const hash = createHash('sha256')
88
- for (const file of sourceFiles(packagePath).sort()) {
89
- hash.update(path.relative(packagePath, file))
90
- hash.update(String(fs.statSync(file).mtimeMs))
114
+ for (const root of [packagePath, ...linkedModuleRoots(packagePath)]) {
115
+ hash.update(root)
116
+ for (const file of sourceFiles(root).sort()) {
117
+ hash.update(path.relative(root, file))
118
+ hash.update(String(fs.statSync(file).mtimeMs))
119
+ }
91
120
  }
92
121
  return hash.digest('hex').slice(0, 16)
93
122
  }
94
123
 
124
+ // the registry slot for a view: the owning package plus the source within
125
+ // it. two packages in one app can hold the same filename, and a bare
126
+ // relative path would share one slot, one instance, and its state.
127
+ export function viewRootId(packagePath: string, file: string): string {
128
+ return path.join(packagePath, path.relative(packagePath, file))
129
+ }
130
+
95
131
  // the app fetches the artifact from a worker whose origin is the shell, so the
96
132
  // url has to name this dev server rather than resolve relative to the shell.
97
133
  function artifactUrl(project: SwiftProject, artifact: SwiftArtifact): string {
@@ -173,15 +209,47 @@ function artifactFor(project: SwiftProject): Promise<SwiftArtifact> {
173
209
  return project.building
174
210
  }
175
211
 
212
+ // the swiftpm package that owns an imported file: the nearest directory at
213
+ // or above the file that holds a Package.swift, stopping at the vite root
214
+ // once the dev server has named it. a swift-only project keeps its sources at
215
+ // the root so that is its package; a React Native app keeps its in a
216
+ // subdirectory (a `native/` package beside the js), and each such package
217
+ // compiles on its own.
218
+ function packageFor(file: string): string {
219
+ let dir = path.dirname(file)
220
+ const stop = devServer?.config.root
221
+ for (;;) {
222
+ if (fs.existsSync(path.join(dir, 'Package.swift'))) return dir
223
+ if ((stop !== undefined && dir === stop) || path.dirname(dir) === dir) break
224
+ dir = path.dirname(dir)
225
+ }
226
+ throw new Error(
227
+ `no Package.swift above ${file}${stop === undefined ? '' : ` (searched up to ${stop})`}; a .swift import needs a swiftpm package like the one in packages/sootsim-swift/example`,
228
+ )
229
+ }
230
+
231
+ // whether the package mounts as a view: its @main type conforms to
232
+ // RNXPackage instead of to App. the symbol graph is the exact read once the
233
+ // transform emits one; until then the conformance on the @main declaration is
234
+ // unambiguous, comments aside.
235
+ function isViewPackage(packagePath: string): boolean {
236
+ const main =
237
+ /@main\s+(?:\w+\s+)*(?:struct|class|enum|actor)\s+\w+[^{]*:\s*[^{]*\bRNXPackage\b/
238
+ for (const file of sourceFiles(packagePath)) {
239
+ const text = fs.readFileSync(file, 'utf8').replace(/^\s*\/\/.*$/gm, '')
240
+ if (main.test(text)) return true
241
+ }
242
+ return false
243
+ }
244
+
176
245
  export interface SwiftPluginOptions {
177
246
  // swiftpm package that owns the project's .swift sources. defaults to the
178
- // vite root, which is the layout a swift-only project uses.
247
+ // nearest Package.swift above each imported file, which is the vite root
248
+ // for the layout a swift-only project uses.
179
249
  packagePath?: string
180
250
  }
181
251
 
182
252
  export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
183
- const packagePathFor = (file: string): string => options.packagePath ?? rootFor(file)
184
-
185
253
  return {
186
254
  name: 'rnx-swift',
187
255
  enforce: 'pre',
@@ -205,13 +273,21 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
205
273
 
206
274
  async load(id) {
207
275
  if (!id.startsWith(VIRTUAL_PREFIX)) return null
208
- const packagePath = packagePathFor(id.slice(VIRTUAL_PREFIX.length))
276
+ const file = id.slice(VIRTUAL_PREFIX.length)
277
+ const packagePath = options.packagePath ?? packageFor(file)
209
278
  const project = projectFor(packagePath)
210
279
  // every source in the package is a dependency of this module, so a
211
280
  // bundler watching this module learns about a `.swift` edit and re-runs
212
281
  // load. that is the whole hot reload: the new hash in the emitted module
213
282
  // is what tells the mount it is looking at a different app.
214
283
  for (const source of sourceFiles(packagePath)) this.addWatchFile(source)
284
+ // an edit to the linked SwiftUI module rebuilds the artifact without
285
+ // touching the project's own sources, so those files gate the reload
286
+ // too: the new hash in the emitted module is what tells the mount it
287
+ // is looking at a different app.
288
+ for (const root of linkedModuleRoots(packagePath)) {
289
+ for (const source of sourceFiles(root)) this.addWatchFile(source)
290
+ }
215
291
  let artifact: SwiftArtifact
216
292
  try {
217
293
  artifact = await artifactFor(project)
@@ -225,6 +301,18 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
225
301
  this.warn(error instanceof Error ? error.message : String(error))
226
302
  artifact = project.artifact
227
303
  }
304
+ // a view package mounts into its importer: the default export is the
305
+ // view component, rooted by its package and source path so a hot reload
306
+ // finds the same instance. an app package keeps the root component it
307
+ // always had.
308
+ if (isViewPackage(packagePath)) {
309
+ const rootId = viewRootId(packagePath, file)
310
+ return [
311
+ `import { createSwiftView } from 'rnxsim/swift'`,
312
+ `export default createSwiftView({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)}, rootId: ${JSON.stringify(rootId)} })`,
313
+ '',
314
+ ].join('\n')
315
+ }
228
316
  return [
229
317
  `import { createSwiftModule } from 'rnxsim/swift'`,
230
318
  `export default createSwiftModule({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)} })`,
@@ -234,7 +322,6 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
234
322
 
235
323
  configureServer(server) {
236
324
  devServer = server
237
- const packagePath = packagePathFor('')
238
325
 
239
326
  server.middlewares.use((req, res, next) => {
240
327
  const url = (req.url ?? '').split('?')[0]
@@ -242,24 +329,27 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
242
329
  next()
243
330
  return
244
331
  }
245
- const project = projectFor(packagePath)
246
- void artifactFor(project).then(
247
- (artifact) => {
248
- if (url !== `${ARTIFACT_PATH}${artifact.hash}.wasm`) {
249
- res.statusCode = 404
250
- res.end('swift artifact is stale')
332
+ // the url names a content hash, not a package: the request is answered
333
+ // by whichever known project built it, so a React Native app whose
334
+ // swift package sits below its root serves the same path its imports
335
+ // compiled.
336
+ void (async () => {
337
+ for (const project of projects.values()) {
338
+ const artifact = await artifactFor(project)
339
+ if (url === `${ARTIFACT_PATH}${artifact.hash}.wasm`) {
340
+ // the tenant worker's origin is the shell, never this dev server
341
+ res.setHeader('access-control-allow-origin', '*')
342
+ res.setHeader('content-type', 'application/wasm')
343
+ res.end(artifact.bytes)
251
344
  return
252
345
  }
253
- // the tenant worker's origin is the shell, never this dev server
254
- res.setHeader('access-control-allow-origin', '*')
255
- res.setHeader('content-type', 'application/wasm')
256
- res.end(artifact.bytes)
257
- },
258
- (error: unknown) => {
259
- res.statusCode = 500
260
- res.end(error instanceof Error ? error.message : String(error))
261
- },
262
- )
346
+ }
347
+ res.statusCode = 404
348
+ res.end('swift artifact is stale')
349
+ })().catch((error: unknown) => {
350
+ res.statusCode = 500
351
+ res.end(error instanceof Error ? error.message : String(error))
352
+ })
263
353
  })
264
354
  },
265
355
  }