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.
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +7 -2
- package/dist-lib/bridge-contract-input.mjs +7 -2
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud.cjs +18 -2
- package/dist-lib/cloud.mjs +18 -2
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +7 -3
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +3 -2
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +7 -3
- package/dist-lib/swift.cjs +119 -14
- package/dist-lib/vite.cjs +95 -34
- package/package.json +1 -1
- package/src/bridge-contract-input.ts +10 -0
- package/src/bridge-contract.ts +5 -0
- package/src/cloud.ts +34 -2
- package/src/swift.ts +2 -0
- package/src/vite-plugin-swift.ts +164 -38
package/dist-lib/swift.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
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: () =>
|
|
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(
|
|
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
|
|
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.
|
|
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
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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.
|
|
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
|
|
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())
|
|
800
|
-
|
|
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(
|
|
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
|
|
809
|
-
hash.update(
|
|
810
|
-
|
|
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
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
927
|
-
|
|
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
|
@@ -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
|
|
package/src/bridge-contract.ts
CHANGED
|
@@ -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
|
-
|
|
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: () =>
|
|
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
|