rnxsim 0.1.520 → 0.1.522

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 +7 -3
  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 +7 -3
  51. package/dist-lib/swift.cjs +119 -14
  52. package/dist-lib/vite.cjs +95 -34
  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 +164 -38
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.520 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.522 | (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
  }
@@ -1033,6 +1075,26 @@ function NavigationStackHost({ node, deliver }) {
1033
1075
  screen.id
1034
1076
  )) });
1035
1077
  }
1078
+ var REGULAR_WIDTH = 768;
1079
+ function SplitViewHost({
1080
+ node,
1081
+ deliver,
1082
+ cache
1083
+ }) {
1084
+ const { width } = (0, import_react_native.useWindowDimensions)();
1085
+ const screens = (node.c ?? []).filter((child) => child.t === "NavigationScreen");
1086
+ if (width < REGULAR_WIDTH) {
1087
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: NAVIGATOR_FILL, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationStackHost, { node, deliver }) });
1088
+ }
1089
+ const sidebar = screens[0];
1090
+ const pushed = screens.slice(1);
1091
+ const detailProp = node.p?.detail;
1092
+ const detail = isRecord(detailProp) && detailProp.t === "SplitDetail" ? readNodes(detailProp.c) : [];
1093
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: { flex: 1, flexDirection: "row", alignSelf: "stretch" }, children: [
1094
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: { width: 320 }, children: sidebar ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationStackHost, { node: { ...node, c: [sidebar] }, deliver }) : null }),
1095
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: { flex: 1 }, children: pushed.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationStackHost, { node: { ...node, c: pushed }, deliver }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: detail.map((child) => renderNode(child, deliver, cache)) }) })
1096
+ ] });
1097
+ }
1036
1098
  function renderNode(node, deliver, cache) {
1037
1099
  const cached = cache?.entries.get(node.id);
1038
1100
  if (cached && sameNode(cached.node, node)) return cached.element;
@@ -1048,6 +1110,9 @@ function buildNode(node, deliver, cache) {
1048
1110
  if (node.t === "NavigationStack") {
1049
1111
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: NAVIGATOR_FILL, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationStackHost, { node, deliver }) }, node.id);
1050
1112
  }
1113
+ if (node.t === "NavigationSplitView") {
1114
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SplitViewHost, { node, deliver, cache }, node.id);
1115
+ }
1051
1116
  if (node.t === "NavigationLink") {
1052
1117
  return (0, import_react.createElement)(
1053
1118
  import_ui.Button,
@@ -1085,7 +1150,9 @@ function SwiftTree({
1085
1150
  import_ui.Host,
1086
1151
  {
1087
1152
  style: { flex: 1 },
1088
- ignoreSafeArea: tree.some((node) => node.t === "NavigationStack"),
1153
+ ignoreSafeArea: tree.some(
1154
+ (node) => node.t === "NavigationStack" || node.t === "NavigationSplitView"
1155
+ ),
1089
1156
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_native.NavigationIndependentTree, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_native.NavigationContainer, { children: tree.map((node) => renderNode(node, deliver, cache)) }) })
1090
1157
  }
1091
1158
  )
@@ -1099,9 +1166,18 @@ function rootRegistry() {
1099
1166
  Reflect.set(globalThis, ROOT_KEY, created);
1100
1167
  return created;
1101
1168
  }
1102
- function createRoot() {
1169
+ function slotFor(rootId, mountTree, props) {
1170
+ const registry = rootRegistry();
1171
+ const existing = registry.get(rootId);
1172
+ if (existing) return existing;
1173
+ const created = { root: createRoot(mountTree, props), props };
1174
+ registry.set(rootId, created);
1175
+ return created;
1176
+ }
1177
+ function createRoot(mountTree, props) {
1103
1178
  let instance = null;
1104
1179
  let source = null;
1180
+ let refresh;
1105
1181
  let state = { tree: null, error: null };
1106
1182
  let swap = Promise.resolve();
1107
1183
  const listeners = /* @__PURE__ */ new Set();
@@ -1116,7 +1192,7 @@ function createRoot() {
1116
1192
  if (carried !== null) created.importState(carried);
1117
1193
  if (source !== options) return;
1118
1194
  instance = created;
1119
- publish({ tree: created.mount(), error: null });
1195
+ publish({ tree: mountTree(created, props.current), error: null });
1120
1196
  };
1121
1197
  const component = function SwiftRoot() {
1122
1198
  const rendered = (0, import_react.useSyncExternalStore)(
@@ -1136,10 +1212,11 @@ function createRoot() {
1136
1212
  };
1137
1213
  return {
1138
1214
  component,
1139
- use(options) {
1140
- const changed = source === null || source.hash !== options.hash || source.url !== options.url;
1215
+ use(options, refreshKey) {
1216
+ const changed = source === null || source.hash !== options.hash || source.url !== options.url || refreshKey !== void 0 && refreshKey !== refresh;
1141
1217
  if (changed) {
1142
1218
  source = options;
1219
+ refresh = refreshKey;
1143
1220
  swap = swap.then(
1144
1221
  () => apply(options),
1145
1222
  () => apply(options)
@@ -1157,13 +1234,41 @@ function createRoot() {
1157
1234
  }
1158
1235
  function createSwiftModule(options) {
1159
1236
  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);
1237
+ const slot = slotFor(rootId, (instance) => instance.mount(), { current: {} });
1238
+ return slot.root.use(options);
1239
+ }
1240
+ function propsSignature(props) {
1241
+ try {
1242
+ return JSON.stringify(props, (_key, value) => {
1243
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
1244
+ throw new Error(`swift view props must be json; found ${typeof value}`);
1245
+ }
1246
+ return value;
1247
+ }) ?? "null";
1248
+ } catch (error) {
1249
+ if (error instanceof Error && error.message.startsWith("swift view props"))
1250
+ throw error;
1251
+ throw new Error(
1252
+ `swift view props must be json: ${error instanceof Error ? error.message : String(error)}`
1253
+ );
1254
+ }
1255
+ }
1256
+ function createSwiftView(options) {
1257
+ const rootId = options.rootId ?? "view";
1258
+ const slot = slotFor(rootId, (instance, props) => instance.mountView(props), {
1259
+ current: {}
1260
+ });
1261
+ return function SwiftView(props) {
1262
+ if (props.children !== void 0) {
1263
+ throw new Error("swift views take data props, not children");
1264
+ }
1265
+ slot.props.current = props;
1266
+ const component = slot.root.use(options, propsSignature(props));
1267
+ return (0, import_react.createElement)(component);
1268
+ };
1165
1269
  }
1166
1270
  // Annotate the CommonJS export names for ESM import in node:
1167
1271
  0 && (module.exports = {
1168
- createSwiftModule
1272
+ createSwiftModule,
1273
+ createSwiftView
1169
1274
  });
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.522 | (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;
@@ -827,6 +848,11 @@ function builtArtifact(packagePath) {
827
848
  }
828
849
  return import_node_path4.default.join(binDir, artifacts[0]);
829
850
  }
851
+ function streamText(value) {
852
+ if (typeof value === "string") return value.trim();
853
+ if (Buffer.isBuffer(value)) return value.toString("utf8").trim();
854
+ return "";
855
+ }
830
856
  async function compile(packagePath, stamp) {
831
857
  const swift = import_node_path4.default.join(TOOLCHAIN_BIN, "swift");
832
858
  if (!import_node_fs3.default.existsSync(swift)) {
@@ -851,10 +877,13 @@ async function compile(packagePath, stamp) {
851
877
  { maxBuffer: 64 * 1024 * 1024 }
852
878
  );
853
879
  } catch (error) {
854
- throw new Error(
855
- `swift build failed for ${packagePath}
856
- ${error instanceof Error ? error.message : String(error)}`
857
- );
880
+ const streams = error && typeof error === "object" ? [
881
+ streamText(Reflect.get(error, "stdout")),
882
+ streamText(Reflect.get(error, "stderr"))
883
+ ].filter(Boolean) : [];
884
+ const detail = streams.length > 0 ? streams.join("\n") : error instanceof Error ? error.message : String(error);
885
+ throw new Error(`swift build failed for ${packagePath}
886
+ ${detail}`);
858
887
  }
859
888
  const bytes = import_node_fs3.default.readFileSync(builtArtifact(packagePath));
860
889
  return {
@@ -866,7 +895,13 @@ ${error instanceof Error ? error.message : String(error)}`
866
895
  function artifactFor(project) {
867
896
  const stamp = sourceStamp(project.packagePath);
868
897
  if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact);
869
- project.building ??= compile(project.packagePath, stamp).then(
898
+ if (project.building) {
899
+ const running = project.building;
900
+ if (running.stamp === stamp) return running.promise;
901
+ const again = () => artifactFor(project);
902
+ return running.promise.then(again, again);
903
+ }
904
+ const promise = compile(project.packagePath, stamp).then(
870
905
  (artifact) => {
871
906
  project.artifact = artifact;
872
907
  project.building = null;
@@ -877,10 +912,30 @@ function artifactFor(project) {
877
912
  throw error;
878
913
  }
879
914
  );
880
- return project.building;
915
+ project.building = { stamp, promise };
916
+ return promise;
917
+ }
918
+ function packageFor(file) {
919
+ let dir = import_node_path4.default.dirname(file);
920
+ const stop = devServer?.config.root;
921
+ for (; ; ) {
922
+ if (import_node_fs3.default.existsSync(import_node_path4.default.join(dir, "Package.swift"))) return dir;
923
+ if (stop !== void 0 && dir === stop || import_node_path4.default.dirname(dir) === dir) break;
924
+ dir = import_node_path4.default.dirname(dir);
925
+ }
926
+ throw new Error(
927
+ `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`
928
+ );
929
+ }
930
+ function isViewPackage(packagePath) {
931
+ const main = /@main\s+(?:\w+\s+)*(?:struct|class|enum|actor)\s+\w+[^{]*:\s*[^{]*\bRNXPackage\b/;
932
+ for (const file of sourceFiles(packagePath)) {
933
+ const text = import_node_fs3.default.readFileSync(file, "utf8").replace(/^\s*\/\/.*$/gm, "");
934
+ if (main.test(text)) return true;
935
+ }
936
+ return false;
881
937
  }
882
938
  function swiftPlugin(options = {}) {
883
- const packagePathFor = (file) => options.packagePath ?? rootFor(file);
884
939
  return {
885
940
  name: "rnx-swift",
886
941
  enforce: "pre",
@@ -897,9 +952,14 @@ function swiftPlugin(options = {}) {
897
952
  },
898
953
  async load(id) {
899
954
  if (!id.startsWith(VIRTUAL_PREFIX)) return null;
900
- const packagePath = packagePathFor(id.slice(VIRTUAL_PREFIX.length));
955
+ const file = id.slice(VIRTUAL_PREFIX.length);
956
+ if (!import_node_fs3.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
957
+ const packagePath = options.packagePath ?? packageFor(file);
901
958
  const project = projectFor(packagePath);
902
959
  for (const source of sourceFiles(packagePath)) this.addWatchFile(source);
960
+ for (const root of linkedModuleRoots(packagePath)) {
961
+ for (const source of sourceFiles(root)) this.addWatchFile(source);
962
+ }
903
963
  let artifact;
904
964
  try {
905
965
  artifact = await artifactFor(project);
@@ -908,6 +968,14 @@ function swiftPlugin(options = {}) {
908
968
  this.warn(error instanceof Error ? error.message : String(error));
909
969
  artifact = project.artifact;
910
970
  }
971
+ if (isViewPackage(packagePath)) {
972
+ const rootId = viewRootId(packagePath, file);
973
+ return [
974
+ `import { createSwiftView } from 'rnxsim/swift'`,
975
+ `export default createSwiftView({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)}, rootId: ${JSON.stringify(rootId)} })`,
976
+ ""
977
+ ].join("\n");
978
+ }
911
979
  return [
912
980
  `import { createSwiftModule } from 'rnxsim/swift'`,
913
981
  `export default createSwiftModule({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)} })`,
@@ -916,30 +984,23 @@ function swiftPlugin(options = {}) {
916
984
  },
917
985
  configureServer(server) {
918
986
  devServer = server;
919
- const packagePath = packagePathFor("");
920
987
  server.middlewares.use((req, res, next) => {
921
988
  const url = (req.url ?? "").split("?")[0];
922
989
  if (!url.startsWith(ARTIFACT_PATH)) {
923
990
  next();
924
991
  return;
925
992
  }
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");
932
- return;
933
- }
993
+ for (const project of projects.values()) {
994
+ const artifact = project.artifact;
995
+ if (artifact && url === `${ARTIFACT_PATH}${artifact.hash}.wasm`) {
934
996
  res.setHeader("access-control-allow-origin", "*");
935
997
  res.setHeader("content-type", "application/wasm");
936
998
  res.end(artifact.bytes);
937
- },
938
- (error) => {
939
- res.statusCode = 500;
940
- res.end(error instanceof Error ? error.message : String(error));
999
+ return;
941
1000
  }
942
- );
1001
+ }
1002
+ res.statusCode = 404;
1003
+ res.end("swift artifact is stale");
943
1004
  });
944
1005
  }
945
1006
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnxsim",
3
- "version": "0.1.520",
3
+ "version": "0.1.522",
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'