peachsim 0.1.527
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/LICENSE +22 -0
- package/README.md +640 -0
- package/cli/app-config.ts +82 -0
- package/cli/app-fonts.ts +388 -0
- package/cli/app-project.ts +59 -0
- package/cli/app-splash.ts +171 -0
- package/cli/app-state-reset.ts +26 -0
- package/cli/auth.ts +184 -0
- package/cli/bin.ts +59 -0
- package/cli/bridge-diagnostics.ts +226 -0
- package/cli/bridge-flow-runner.ts +2806 -0
- package/cli/browser-backend.ts +44 -0
- package/cli/browser-evals.ts +96 -0
- package/cli/cli-update.ts +261 -0
- package/cli/cloud-client.ts +1530 -0
- package/cli/cloud-dispatch.ts +206 -0
- package/cli/cloud-session.ts +227 -0
- package/cli/command-contract.ts +221 -0
- package/cli/command-registry.ts +304 -0
- package/cli/commands/agent-wrapper.ts +1048 -0
- package/cli/commands/agent.ts +439 -0
- package/cli/commands/app-fonts.ts +99 -0
- package/cli/commands/assert.ts +542 -0
- package/cli/commands/auth.ts +71 -0
- package/cli/commands/box/bundle-plane.ts +71 -0
- package/cli/commands/box/checkout-plane.ts +256 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +60 -0
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +925 -0
- package/cli/commands/camera.ts +266 -0
- package/cli/commands/cleanup.ts +169 -0
- package/cli/commands/compat.ts +176 -0
- package/cli/commands/config.ts +34 -0
- package/cli/commands/control.ts +2154 -0
- package/cli/commands/cpu-profile.ts +941 -0
- package/cli/commands/daemon-mac-app.ts +165 -0
- package/cli/commands/daemon.ts +800 -0
- package/cli/commands/debug.ts +751 -0
- package/cli/commands/desktop.ts +39 -0
- package/cli/commands/detect.ts +201 -0
- package/cli/commands/detox.ts +481 -0
- package/cli/commands/device.ts +134 -0
- package/cli/commands/diagnose.ts +589 -0
- package/cli/commands/do-chain.ts +408 -0
- package/cli/commands/electron.ts +94 -0
- package/cli/commands/film.ts +379 -0
- package/cli/commands/flow.ts +1240 -0
- package/cli/commands/inspect/actions.ts +516 -0
- package/cli/commands/inspect/core.ts +2086 -0
- package/cli/commands/inspect/count.ts +17 -0
- package/cli/commands/inspect/describe.ts +119 -0
- package/cli/commands/inspect/env.ts +25 -0
- package/cli/commands/inspect/find.ts +169 -0
- package/cli/commands/inspect/get-layout.ts +53 -0
- package/cli/commands/inspect/keyboard.ts +54 -0
- package/cli/commands/inspect/list.ts +175 -0
- package/cli/commands/inspect/memory.ts +215 -0
- package/cli/commands/inspect/redaction.ts +39 -0
- package/cli/commands/inspect/resolve-target.ts +77 -0
- package/cli/commands/inspect/screens.ts +78 -0
- package/cli/commands/inspect/settle.ts +32 -0
- package/cli/commands/inspect/settling.ts +197 -0
- package/cli/commands/inspect/shared.ts +259 -0
- package/cli/commands/inspect/sleep.ts +15 -0
- package/cli/commands/inspect/tree.ts +23 -0
- package/cli/commands/inspect/url.ts +17 -0
- package/cli/commands/inspect/wait-event.ts +211 -0
- package/cli/commands/inspect/wait-idle.ts +25 -0
- package/cli/commands/inspect/wait-ready.ts +87 -0
- package/cli/commands/inspect/wait-selector.ts +59 -0
- package/cli/commands/inspect/wait.ts +32 -0
- package/cli/commands/inspect.ts +4738 -0
- package/cli/commands/install-cli.ts +275 -0
- package/cli/commands/install-desktop.ts +385 -0
- package/cli/commands/login.ts +357 -0
- package/cli/commands/logout.ts +24 -0
- package/cli/commands/maestro-generate.ts +375 -0
- package/cli/commands/maestro.ts +536 -0
- package/cli/commands/mode.ts +58 -0
- package/cli/commands/no-bridge-hint.ts +80 -0
- package/cli/commands/perf.ts +67 -0
- package/cli/commands/permissions.ts +203 -0
- package/cli/commands/platform.ts +410 -0
- package/cli/commands/preview.ts +103 -0
- package/cli/commands/profile.ts +106 -0
- package/cli/commands/react.ts +353 -0
- package/cli/commands/record.ts +1435 -0
- package/cli/commands/report-issue.ts +305 -0
- package/cli/commands/reset.ts +89 -0
- package/cli/commands/runtime.ts +348 -0
- package/cli/commands/screenshot-command.ts +106 -0
- package/cli/commands/screenshot-layers.ts +144 -0
- package/cli/commands/screenshot-mode.ts +38 -0
- package/cli/commands/screenshot.ts +492 -0
- package/cli/commands/screenshots-capture.ts +608 -0
- package/cli/commands/screenshots.ts +127 -0
- package/cli/commands/serve.ts +139 -0
- package/cli/commands/setup.ts +416 -0
- package/cli/commands/shell-boolean-mode.ts +82 -0
- package/cli/commands/skills.ts +468 -0
- package/cli/commands/slides.ts +357 -0
- package/cli/commands/state.ts +94 -0
- package/cli/commands/storage.ts +58 -0
- package/cli/commands/telemetry.ts +55 -0
- package/cli/commands/test.ts +535 -0
- package/cli/commands/three-mode.ts +771 -0
- package/cli/commands/timeline.ts +121 -0
- package/cli/commands/upgrade.ts +167 -0
- package/cli/commands/upload.ts +1203 -0
- package/cli/commands/version.ts +63 -0
- package/cli/commands/welcome.ts +17 -0
- package/cli/commands/what-happened.ts +328 -0
- package/cli/current-sim.ts +217 -0
- package/cli/desktop-companion.ts +315 -0
- package/cli/drivers/electron.ts +70 -0
- package/cli/drivers/index.ts +20 -0
- package/cli/drivers/playwright-provisioning.ts +184 -0
- package/cli/drivers/playwright-sim-host.ts +537 -0
- package/cli/drivers/playwright.ts +209 -0
- package/cli/drivers/registry.ts +65 -0
- package/cli/drivers/types.ts +102 -0
- package/cli/flow-export.ts +55 -0
- package/cli/flow-file.ts +142 -0
- package/cli/flow-live-status.ts +126 -0
- package/cli/flow-session.ts +140 -0
- package/cli/help-core.ts +143 -0
- package/cli/help.ts +44 -0
- package/cli/hidden-runtime-alias.ts +19 -0
- package/cli/hints.ts +214 -0
- package/cli/inspect-notice-state.ts +114 -0
- package/cli/internal-child.ts +176 -0
- package/cli/maestro-js.ts +323 -0
- package/cli/main.ts +704 -0
- package/cli/open-url.ts +8 -0
- package/cli/outbound-endpoints.ts +830 -0
- package/cli/parent-pid.ts +204 -0
- package/cli/parse-args.ts +189 -0
- package/cli/privacy.ts +53 -0
- package/cli/prompt.ts +56 -0
- package/cli/recording-access.ts +108 -0
- package/cli/registry.ts +2 -0
- package/cli/resolve-assets.ts +63 -0
- package/cli/rnx-in-process.ts +98 -0
- package/cli/run-registry.ts +226 -0
- package/cli/run-rnx-core.ts +148 -0
- package/cli/run-rnx.ts +137 -0
- package/cli/runtime-notes.ts +66 -0
- package/cli/runtime-summary.ts +25 -0
- package/cli/secure-file.ts +31 -0
- package/cli/self-invocation.ts +58 -0
- package/cli/send-to-box.ts +109 -0
- package/cli/setup-repository.ts +191 -0
- package/cli/shell-init.ts +98 -0
- package/cli/standalone.ts +4 -0
- package/cli/telemetry.ts +84 -0
- package/cli/ui.ts +68 -0
- package/cli/ws-bridge.ts +1020 -0
- package/detox/attempt-events.cjs +23 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +48 -0
- package/detox/expectations.ts +534 -0
- package/detox/gestures.ts +501 -0
- package/detox/index.ts +2694 -0
- package/detox/jest-preset.cjs +29 -0
- package/detox/jest-setup-after-env.cjs +22 -0
- package/detox/matchers.ts +30 -0
- package/detox/motion-change.cjs +109 -0
- package/detox/motion-change.d.cts +35 -0
- package/detox/navigation-lifecycle.ts +290 -0
- package/detox/navigation.ts +43 -0
- package/detox/proof-frame.cjs +114 -0
- package/detox/record-block-execution.cjs +78 -0
- package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
- package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
- package/detox/screenshots/color-test-rest-norngh.png +0 -0
- package/detox/screenshots/theme-blue-toggle.png +0 -0
- package/detox/screenshots/theme-blue.png +0 -0
- package/detox/screenshots/theme-red-toggle.png +0 -0
- package/detox/screenshots/theme-red.png +0 -0
- package/dist-lib/agent-daemon-client.cjs +502 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +759 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +208 -0
- package/dist-lib/backend-origin.cjs +88 -0
- package/dist-lib/beta.cjs +41 -0
- package/dist-lib/beta.mjs +13 -0
- package/dist-lib/bridge-constants.cjs +161 -0
- package/dist-lib/bridge-contract-input.cjs +451 -0
- package/dist-lib/bridge-contract-input.mjs +414 -0
- package/dist-lib/bridge-contract.cjs +130 -0
- package/dist-lib/bridge-contract.mjs +94 -0
- package/dist-lib/capture-contract.cjs +220 -0
- package/dist-lib/capture-contract.mjs +190 -0
- package/dist-lib/cli-constants.cjs +32 -0
- package/dist-lib/cloud-contract.cjs +194 -0
- package/dist-lib/cloud-contract.mjs +145 -0
- package/dist-lib/cloud.cjs +670 -0
- package/dist-lib/cloud.mjs +639 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +3789 -0
- package/dist-lib/detox/jest-preset.cjs +29 -0
- package/dist-lib/dev-bundle-resolution.cjs +290 -0
- package/dist-lib/home-paths.cjs +638 -0
- package/dist-lib/host/bridge-host.cjs +10903 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
- package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
- package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
- package/dist-lib/host/replacement-module-handler.cjs +469 -0
- package/dist-lib/host/websocket-proxy.cjs +298 -0
- package/dist-lib/index.cjs +4268 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/jump-to-source-native.cjs +112 -0
- package/dist-lib/menu.cjs +646 -0
- package/dist-lib/menu.mjs +604 -0
- package/dist-lib/metro-fingerprint-registry.cjs +186 -0
- package/dist-lib/metro-fingerprint-registry.mjs +156 -0
- package/dist-lib/metro-production-bundle.cjs +329 -0
- package/dist-lib/metro-production-bundle.mjs +295 -0
- package/dist-lib/metro.cjs +904 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/react-native-host-modules.cjs +51 -0
- package/dist-lib/react-native-host-modules.mjs +25 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
- package/dist-lib/sdk.cjs +2433 -0
- package/dist-lib/sdk.mjs +2336 -0
- package/dist-lib/skills.cjs +39132 -0
- package/dist-lib/swift-submit.cjs +92 -0
- package/dist-lib/swift-submit.mjs +65 -0
- package/dist-lib/swift.cjs +1646 -0
- package/dist-lib/vite.cjs +1591 -0
- package/package.json +290 -0
- package/scripts/dev-server-scanner.ts +1291 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/skills/contrast/SKILL.md +165 -0
- package/skills/rnx-debug/SKILL.md +433 -0
- package/skills/rnx-perf/SKILL.md +399 -0
- package/skills/rnx-setup/SKILL.md +172 -0
- package/skills/rnx-test/SKILL.md +321 -0
- package/skills/rnx-visual/SKILL.md +126 -0
- package/src/agent-daemon-client.ts +410 -0
- package/src/agent-events.ts +71 -0
- package/src/agent-identity.ts +23 -0
- package/src/agent-prompt.ts +71 -0
- package/src/agent-sessions.ts +580 -0
- package/src/app-url.ts +39 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +235 -0
- package/src/backend-origin.ts +70 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +175 -0
- package/src/bridge-contract-input.ts +502 -0
- package/src/bridge-contract.ts +558 -0
- package/src/browser-cache.ts +16 -0
- package/src/capture-contract.ts +587 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +40 -0
- package/src/cloud-contract.ts +274 -0
- package/src/cloud.ts +762 -0
- package/src/codex-client.ts +234 -0
- package/src/config.ts +132 -0
- package/src/connect.ts +101 -0
- package/src/dev-bundle-resolution.ts +346 -0
- package/src/dev-server-open.ts +198 -0
- package/src/disk-cleanup.ts +540 -0
- package/src/flow-timeout-scale.ts +15 -0
- package/src/home-paths.ts +806 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2788 -0
- package/src/host/fetch-proxy-handler.ts +449 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/plane-host.ts +203 -0
- package/src/host/replacement-module-handler.ts +158 -0
- package/src/host/websocket-proxy.ts +315 -0
- package/src/index.ts +71 -0
- package/src/jump-to-source-babel.ts +135 -0
- package/src/jump-to-source-native.ts +102 -0
- package/src/menu.ts +777 -0
- package/src/metro-fingerprint-registry.ts +302 -0
- package/src/metro-plugin.ts +341 -0
- package/src/metro-production-bundle.ts +471 -0
- package/src/native-dev-bundle-url.ts +118 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/playwright-host-log.ts +16 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +123 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/react-native-host-modules.ts +26 -0
- package/src/render-mode.ts +27 -0
- package/src/rolldown-plugin-swift.ts +240 -0
- package/src/runtime-assets.ts +233 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +514 -0
- package/src/screenshots/orchestrate.ts +256 -0
- package/src/screenshots/org-deck.ts +91 -0
- package/src/screenshots/registry.ts +58 -0
- package/src/screenshots/schema.ts +364 -0
- package/src/sdk.ts +15 -0
- package/src/settings-bundle.ts +418 -0
- package/src/shell-bridge-identity.ts +15 -0
- package/src/sim-client.ts +697 -0
- package/src/skills/builtin/a11y-review.ts +132 -0
- package/src/skills/builtin/compat-check.ts +71 -0
- package/src/skills/builtin/maestro-test.ts +121 -0
- package/src/skills/builtin/perf-profile.ts +90 -0
- package/src/skills/builtin/screenshot-all.ts +52 -0
- package/src/skills/builtin/visual-diff.ts +100 -0
- package/src/skills/registry.ts +107 -0
- package/src/skills/types.ts +41 -0
- package/src/swift-submit.ts +185 -0
- package/src/swift.ts +7 -0
- package/src/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +264 -0
- package/src/vite-plugin-swift.ts +620 -0
- package/src/vite-plugin.ts +1946 -0
- package/src/worklets-babel.ts +250 -0
|
@@ -0,0 +1,1591 @@
|
|
|
1
|
+
/*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
|
+
"use strict";
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
+
for (let key of __getOwnPropNames(from))
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
+
mod
|
|
29
|
+
));
|
|
30
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
|
+
|
|
32
|
+
// src/vite-plugin-one.ts
|
|
33
|
+
var vite_plugin_one_exports = {};
|
|
34
|
+
__export(vite_plugin_one_exports, {
|
|
35
|
+
default: () => vite_plugin_one_default,
|
|
36
|
+
rnxPlugin: () => rnxPlugin,
|
|
37
|
+
sootsimPlugin: () => sootsimPlugin,
|
|
38
|
+
swiftPlugin: () => swiftPlugin,
|
|
39
|
+
swiftRemotePlugin: () => swiftRemotePlugin
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(vite_plugin_one_exports);
|
|
42
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
43
|
+
var import_node_crypto3 = require("node:crypto");
|
|
44
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
45
|
+
|
|
46
|
+
// src/bridge-constants.ts
|
|
47
|
+
var DEFAULT_SOOTSIM_BRIDGE_PORT = 7668;
|
|
48
|
+
var DEFAULT_SOOTSIM_BRIDGE_URL = `http://localhost:${DEFAULT_SOOTSIM_BRIDGE_PORT}/`;
|
|
49
|
+
|
|
50
|
+
// src/dev-server-open.ts
|
|
51
|
+
var import_node_child_process = require("node:child_process");
|
|
52
|
+
var import_node_crypto = require("node:crypto");
|
|
53
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
54
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
55
|
+
var import_node_url = require("node:url");
|
|
56
|
+
function resolveRnxDesktopAppName(open, projectRoot) {
|
|
57
|
+
if (!open) return null;
|
|
58
|
+
if (typeof open === "object") {
|
|
59
|
+
const appName = open.appName.trim();
|
|
60
|
+
if (!appName) throw new Error("rnx: open.appName must not be empty");
|
|
61
|
+
return appName;
|
|
62
|
+
}
|
|
63
|
+
const packageJsonPath = import_node_path.default.join(projectRoot, "package.json");
|
|
64
|
+
const packageJson = JSON.parse(import_node_fs.default.readFileSync(packageJsonPath, "utf8"));
|
|
65
|
+
const packageName = packageJson && typeof packageJson === "object" ? Reflect.get(packageJson, "name") : void 0;
|
|
66
|
+
if (typeof packageName !== "string" || !packageName.trim()) {
|
|
67
|
+
throw new Error(`rnx: ${packageJsonPath} must declare a package name`);
|
|
68
|
+
}
|
|
69
|
+
return packageName.trim();
|
|
70
|
+
}
|
|
71
|
+
function tagRnxBundleForPlugin(bundleUrl, replacementOrigin, replacementToken, projectRoot, appName) {
|
|
72
|
+
const absolute = /^[a-z][a-z\d+.-]*:\/\//i.test(bundleUrl);
|
|
73
|
+
const url = new URL(bundleUrl, absolute ? void 0 : "http://rnx.local");
|
|
74
|
+
if (appName) {
|
|
75
|
+
url.searchParams.set("rnxAppName", appName);
|
|
76
|
+
url.searchParams.set(
|
|
77
|
+
"rnxProjectId",
|
|
78
|
+
(0, import_node_crypto.createHash)("sha256").update(import_node_fs.default.realpathSync(projectRoot)).digest("hex").slice(0, 32)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
url.searchParams.set("rnxReplacementOrigin", replacementOrigin);
|
|
82
|
+
url.searchParams.set("rnxReplacementToken", replacementToken);
|
|
83
|
+
return absolute ? url.toString() : `${url.pathname}${url.search}${url.hash}`;
|
|
84
|
+
}
|
|
85
|
+
function resolveRnxDesktopLaunchTarget(port, bundleUrl) {
|
|
86
|
+
return new URL(bundleUrl, `http://localhost:${port}`).toString();
|
|
87
|
+
}
|
|
88
|
+
function buildRnxDesktopOpenArgs(bundleUrl) {
|
|
89
|
+
return ["open", bundleUrl, "--new", "--driver", "electron", "--no-describe"];
|
|
90
|
+
}
|
|
91
|
+
function observeRnxDesktopLaunch(child, appName) {
|
|
92
|
+
let stderr = "";
|
|
93
|
+
let spawnFailed = false;
|
|
94
|
+
child.stderr?.setEncoding("utf8");
|
|
95
|
+
child.stderr?.on("data", (chunk) => {
|
|
96
|
+
stderr = `${stderr}${chunk}`.slice(-16384);
|
|
97
|
+
});
|
|
98
|
+
child.once("error", (error) => {
|
|
99
|
+
spawnFailed = true;
|
|
100
|
+
console.error(`[rnx] could not open ${appName}: ${error.message}`);
|
|
101
|
+
});
|
|
102
|
+
child.once("close", (code, signal) => {
|
|
103
|
+
if (spawnFailed) return;
|
|
104
|
+
if (code === 0) {
|
|
105
|
+
console.log(`[rnx] opened ${appName} in the desktop simulator`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const detail = stderr.trim() || (signal ? `signal ${signal}` : `exit ${code}`);
|
|
109
|
+
console.error(`[rnx] could not open ${appName}: ${detail}`);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function launchRnxDesktop(bundleUrl, appName) {
|
|
113
|
+
const packageRoot = import_node_path.default.resolve(import_node_path.default.dirname((0, import_node_url.fileURLToPath)(__sootsim_import_meta_url)), "..");
|
|
114
|
+
const binPath = import_node_path.default.join(packageRoot, "dist-cli", "bin.js");
|
|
115
|
+
console.log(`[rnx] opening ${appName} in the desktop simulator`);
|
|
116
|
+
const child = (0, import_node_child_process.spawn)(
|
|
117
|
+
process.execPath,
|
|
118
|
+
[binPath, ...buildRnxDesktopOpenArgs(bundleUrl)],
|
|
119
|
+
{
|
|
120
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
121
|
+
windowsHide: true
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
observeRnxDesktopLaunch(child, appName);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/home-paths.ts
|
|
128
|
+
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
129
|
+
var import_node_os = require("node:os");
|
|
130
|
+
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
131
|
+
var RNX_HOME_ENV = "RNX_HOME";
|
|
132
|
+
var ACTIVE_RUNTIME_FILE = "active";
|
|
133
|
+
var DAEMON_LOCKFILE = "daemon.json";
|
|
134
|
+
var DAEMON_HEARTBEAT_STALE_MS = 3e4;
|
|
135
|
+
function rnxHomeDir() {
|
|
136
|
+
const override = process.env[RNX_HOME_ENV];
|
|
137
|
+
if (override && override.length > 0) return import_node_path2.default.resolve(override);
|
|
138
|
+
return import_node_path2.default.join((0, import_node_os.homedir)(), ".rnx");
|
|
139
|
+
}
|
|
140
|
+
function runtimesDir() {
|
|
141
|
+
return import_node_path2.default.join(rnxHomeDir(), "runtimes");
|
|
142
|
+
}
|
|
143
|
+
function runtimeDir(version) {
|
|
144
|
+
return import_node_path2.default.join(runtimesDir(), version);
|
|
145
|
+
}
|
|
146
|
+
function activeRuntimeFile() {
|
|
147
|
+
return import_node_path2.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
|
|
148
|
+
}
|
|
149
|
+
function daemonLockfilePath() {
|
|
150
|
+
return import_node_path2.default.join(rnxHomeDir(), DAEMON_LOCKFILE);
|
|
151
|
+
}
|
|
152
|
+
function readActiveRuntime() {
|
|
153
|
+
try {
|
|
154
|
+
const value = import_node_fs2.default.readFileSync(activeRuntimeFile(), "utf8").trim();
|
|
155
|
+
return value.length > 0 ? value : null;
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
|
|
161
|
+
function readDaemonLockfile() {
|
|
162
|
+
try {
|
|
163
|
+
const fd = import_node_fs2.default.openSync(daemonLockfilePath(), "r");
|
|
164
|
+
try {
|
|
165
|
+
const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
|
|
166
|
+
const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
|
|
167
|
+
const raw = buf.subarray(0, bytesRead).toString("utf8");
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
if (parsed && parsed.schema === 1 && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
|
|
170
|
+
return parsed;
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
} finally {
|
|
174
|
+
import_node_fs2.default.closeSync(fd);
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function isDaemonLockfileFresh(lock, now = Date.now()) {
|
|
181
|
+
if (!lock) return false;
|
|
182
|
+
if (now - lock.heartbeatAt > DAEMON_HEARTBEAT_STALE_MS) return false;
|
|
183
|
+
try {
|
|
184
|
+
process.kill(lock.pid, 0);
|
|
185
|
+
return true;
|
|
186
|
+
} catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/host/replacement-module-handler.ts
|
|
192
|
+
var import_promises = __toESM(require("node:fs/promises"), 1);
|
|
193
|
+
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
194
|
+
|
|
195
|
+
// ../contrast-bundler/src/replacementModule.ts
|
|
196
|
+
var import_dist = require("sucrase/dist/index.js");
|
|
197
|
+
var import_parser = require("sucrase/dist/parser/index.js");
|
|
198
|
+
var import_types = require("sucrase/dist/parser/tokenizer/types.js");
|
|
199
|
+
|
|
200
|
+
// ../contrast-bundler/src/posixPath.ts
|
|
201
|
+
var sep = "/";
|
|
202
|
+
var delimiter = ":";
|
|
203
|
+
function isAbsolute(p) {
|
|
204
|
+
return p.charCodeAt(0) === 47;
|
|
205
|
+
}
|
|
206
|
+
function normalizeSegments(parts, allowAboveRoot) {
|
|
207
|
+
const out = [];
|
|
208
|
+
for (const part of parts) {
|
|
209
|
+
if (part === "" || part === ".") continue;
|
|
210
|
+
if (part === "..") {
|
|
211
|
+
if (out.length > 0 && out[out.length - 1] !== "..") out.pop();
|
|
212
|
+
else if (allowAboveRoot) out.push("..");
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
out.push(part);
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
function normalize(p) {
|
|
220
|
+
if (p.length === 0) return ".";
|
|
221
|
+
const absolute = isAbsolute(p);
|
|
222
|
+
const trailingSlash = p.charCodeAt(p.length - 1) === 47;
|
|
223
|
+
const segments = normalizeSegments(p.split("/"), !absolute);
|
|
224
|
+
let joined = segments.join("/");
|
|
225
|
+
if (joined.length === 0) {
|
|
226
|
+
if (absolute) return "/";
|
|
227
|
+
return trailingSlash ? "./" : ".";
|
|
228
|
+
}
|
|
229
|
+
if (trailingSlash) joined += "/";
|
|
230
|
+
return absolute ? "/" + joined : joined;
|
|
231
|
+
}
|
|
232
|
+
function join(...parts) {
|
|
233
|
+
const present = parts.filter((part) => part.length > 0);
|
|
234
|
+
if (present.length === 0) return ".";
|
|
235
|
+
return normalize(present.join("/"));
|
|
236
|
+
}
|
|
237
|
+
function resolve(...parts) {
|
|
238
|
+
let resolved = "";
|
|
239
|
+
let absolute = false;
|
|
240
|
+
for (let i = parts.length - 1; i >= 0 && !absolute; i--) {
|
|
241
|
+
const part = parts[i];
|
|
242
|
+
if (part.length === 0) continue;
|
|
243
|
+
resolved = resolved.length > 0 ? `${part}/${resolved}` : part;
|
|
244
|
+
absolute = isAbsolute(part);
|
|
245
|
+
}
|
|
246
|
+
if (!absolute)
|
|
247
|
+
throw new Error(
|
|
248
|
+
`posixPath.resolve needs an absolute segment, got: ${parts.join(", ")}`
|
|
249
|
+
);
|
|
250
|
+
const normalized = normalizeSegments(resolved.split("/"), false).join("/");
|
|
251
|
+
return normalized.length > 0 ? "/" + normalized : "/";
|
|
252
|
+
}
|
|
253
|
+
function dirname(p) {
|
|
254
|
+
const idx = p.lastIndexOf("/");
|
|
255
|
+
if (idx < 0) return ".";
|
|
256
|
+
if (idx === 0) return "/";
|
|
257
|
+
return p.slice(0, idx);
|
|
258
|
+
}
|
|
259
|
+
function basename(p, ext) {
|
|
260
|
+
const idx = p.lastIndexOf("/");
|
|
261
|
+
const base = idx < 0 ? p : p.slice(idx + 1);
|
|
262
|
+
if (ext != null && ext.length < base.length && base.endsWith(ext)) {
|
|
263
|
+
return base.slice(0, base.length - ext.length);
|
|
264
|
+
}
|
|
265
|
+
return base;
|
|
266
|
+
}
|
|
267
|
+
function extname(p) {
|
|
268
|
+
const base = basename(p);
|
|
269
|
+
const idx = base.lastIndexOf(".");
|
|
270
|
+
if (idx <= 0) return "";
|
|
271
|
+
return base.slice(idx);
|
|
272
|
+
}
|
|
273
|
+
function relative(from, to) {
|
|
274
|
+
if (from === to) return "";
|
|
275
|
+
const fromParts = normalizeSegments(from.split("/"), false);
|
|
276
|
+
const toParts = normalizeSegments(to.split("/"), false);
|
|
277
|
+
let shared = 0;
|
|
278
|
+
while (shared < fromParts.length && shared < toParts.length && fromParts[shared] === toParts[shared]) {
|
|
279
|
+
shared++;
|
|
280
|
+
}
|
|
281
|
+
const up = new Array(fromParts.length - shared).fill("..");
|
|
282
|
+
return [...up, ...toParts.slice(shared)].join("/");
|
|
283
|
+
}
|
|
284
|
+
var posix = {
|
|
285
|
+
sep,
|
|
286
|
+
delimiter,
|
|
287
|
+
isAbsolute,
|
|
288
|
+
normalize,
|
|
289
|
+
join,
|
|
290
|
+
resolve,
|
|
291
|
+
dirname,
|
|
292
|
+
basename,
|
|
293
|
+
extname,
|
|
294
|
+
relative
|
|
295
|
+
};
|
|
296
|
+
var posixPath_default = { ...posix, posix };
|
|
297
|
+
|
|
298
|
+
// ../contrast-bundler/src/replacementModule.ts
|
|
299
|
+
var REPLACEMENT_MODULE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"];
|
|
300
|
+
var IMPORT_KEYWORD = import_types.TokenType["_import"];
|
|
301
|
+
var EXPORT_KEYWORD = import_types.TokenType["_export"];
|
|
302
|
+
var REACT_BRIDGE = "globalThis.__sootsim_react_bridge";
|
|
303
|
+
var REACT_NATIVE_BRIDGE = "globalThis.__sootsim_components__";
|
|
304
|
+
var PACKAGE_BRIDGES = "globalThis.__sootsim_config_package_modules";
|
|
305
|
+
var JSX_RUNTIME_SPECIFIERS = /* @__PURE__ */ new Set(["react/jsx-runtime", "react/jsx-dev-runtime"]);
|
|
306
|
+
function transformsFor(filePath) {
|
|
307
|
+
switch (extname(filePath).toLowerCase()) {
|
|
308
|
+
case ".ts":
|
|
309
|
+
return ["typescript"];
|
|
310
|
+
case ".tsx":
|
|
311
|
+
return ["typescript", "jsx"];
|
|
312
|
+
case ".js":
|
|
313
|
+
case ".jsx":
|
|
314
|
+
return ["jsx"];
|
|
315
|
+
default:
|
|
316
|
+
throw new Error(
|
|
317
|
+
`replacement files must be ${REPLACEMENT_MODULE_EXTENSIONS.join(", ")}`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function isContrastKitSpecifier(specifier) {
|
|
322
|
+
return specifier === "contrast-kit" || specifier.startsWith("contrast-kit/");
|
|
323
|
+
}
|
|
324
|
+
function unsupportedSpecifier(specifier) {
|
|
325
|
+
return new Error(
|
|
326
|
+
`replacement modules may only import react, react-native, contrast-kit, and the packages rnx stubs for this app; '${specifier}' is none of those. vendor the code into the replacement file, or add the package to rnx.config so the simulator bridges it.`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
function bridgeDeclaration(name, specifier) {
|
|
330
|
+
const notReady = (what) => JSON.stringify(`[rnx] replacement ${what} bridge is not ready`);
|
|
331
|
+
if (specifier === "react") {
|
|
332
|
+
return `const ${name} = ${REACT_BRIDGE}; if (!${name}) throw new Error(${notReady("React")});`;
|
|
333
|
+
}
|
|
334
|
+
if (specifier === "react-native") {
|
|
335
|
+
return `const ${name} = ${REACT_NATIVE_BRIDGE}; if (!${name}) throw new Error(${notReady("React Native")});`;
|
|
336
|
+
}
|
|
337
|
+
if (isContrastKitSpecifier(specifier)) {
|
|
338
|
+
return `const ${name} = (globalThis.__contrast_kit_modules && globalThis.__contrast_kit_modules[${JSON.stringify(specifier)}]) || (${PACKAGE_BRIDGES} || {})[${JSON.stringify(specifier)}]; if (!${name}) throw new Error(${JSON.stringify(`[rnx] replacement package bridge ${specifier} is not ready`)});`;
|
|
339
|
+
}
|
|
340
|
+
if (JSX_RUNTIME_SPECIFIERS.has(specifier)) {
|
|
341
|
+
return `const ${name} = (() => { const react = ${REACT_BRIDGE}; if (!react) throw new Error(${notReady("React")}); const jsx = (type, props, key) => react.createElement(type, key === undefined ? props : { ...props, key }); return { Fragment: react.Fragment, jsx, jsxs: jsx, jsxDEV: jsx }; })();`;
|
|
342
|
+
}
|
|
343
|
+
return `const ${name} = (${PACKAGE_BRIDGES} || {})[${JSON.stringify(specifier)}]; if (!${name}) throw new Error(${JSON.stringify(`[rnx] replacement package bridge ${specifier} is not ready`)});`;
|
|
344
|
+
}
|
|
345
|
+
var DEFAULT_INTEROP = "__rnxReplacementDefault";
|
|
346
|
+
var DEFAULT_INTEROP_DECLARATION = `const ${DEFAULT_INTEROP} = (bridge) => (bridge && bridge.__esModule ? bridge.default : bridge);`;
|
|
347
|
+
function compileReplacementModule(input) {
|
|
348
|
+
const transformed = (0, import_dist.transform)(input.source, {
|
|
349
|
+
transforms: transformsFor(input.filePath),
|
|
350
|
+
filePath: input.filePath,
|
|
351
|
+
// classic runtime, matching what the app's own Metro build does for a file
|
|
352
|
+
// that has no automatic-runtime pragma: the file imports React itself.
|
|
353
|
+
jsxRuntime: "classic",
|
|
354
|
+
jsxPragma: "React.createElement",
|
|
355
|
+
jsxFragmentPragma: "React.Fragment",
|
|
356
|
+
production: true,
|
|
357
|
+
sourceMapOptions: { compiledFilename: input.filePath }
|
|
358
|
+
});
|
|
359
|
+
const code = transformed.code;
|
|
360
|
+
const sourceMap = transformed.sourceMap;
|
|
361
|
+
if (!sourceMap) throw new Error("sucrase compiled the replacement with no source map");
|
|
362
|
+
const { tokens } = (0, import_parser.parse)(code, false, false, false);
|
|
363
|
+
const read = (token) => code.slice(token.start, token.end);
|
|
364
|
+
const readSpecifier = (token) => {
|
|
365
|
+
if (token.type !== import_types.TokenType.string) {
|
|
366
|
+
throw new Error("replacement module import has no string specifier");
|
|
367
|
+
}
|
|
368
|
+
return code.slice(token.start + 1, token.end - 1);
|
|
369
|
+
};
|
|
370
|
+
const bridgeNames = /* @__PURE__ */ new Map();
|
|
371
|
+
const declarations = [];
|
|
372
|
+
let needsDefaultInterop = false;
|
|
373
|
+
const bridgeFor = (specifier) => {
|
|
374
|
+
const existing = bridgeNames.get(specifier);
|
|
375
|
+
if (existing) return existing;
|
|
376
|
+
const name = `__rnxReplacementBridge${bridgeNames.size}`;
|
|
377
|
+
bridgeNames.set(specifier, name);
|
|
378
|
+
declarations.push(bridgeDeclaration(name, specifier));
|
|
379
|
+
return name;
|
|
380
|
+
};
|
|
381
|
+
const edits = [];
|
|
382
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
383
|
+
const token = tokens[index];
|
|
384
|
+
if (token.type === EXPORT_KEYWORD) {
|
|
385
|
+
const after2 = tokens[index + 1];
|
|
386
|
+
let fromAt = -1;
|
|
387
|
+
if (after2?.type === import_types.TokenType.star) {
|
|
388
|
+
fromAt = read(tokens[index + 2]) === "from" ? index + 2 : index + 4;
|
|
389
|
+
} else if (after2?.type === import_types.TokenType.braceL) {
|
|
390
|
+
let depth = 1;
|
|
391
|
+
let cursor2 = index + 2;
|
|
392
|
+
while (cursor2 < tokens.length && depth > 0) {
|
|
393
|
+
if (tokens[cursor2].type === import_types.TokenType.braceL) depth++;
|
|
394
|
+
else if (tokens[cursor2].type === import_types.TokenType.braceR) depth--;
|
|
395
|
+
cursor2++;
|
|
396
|
+
}
|
|
397
|
+
fromAt = cursor2;
|
|
398
|
+
}
|
|
399
|
+
if (fromAt > 0 && tokens[fromAt] && read(tokens[fromAt]) === "from" && tokens[fromAt + 1]?.type === import_types.TokenType.string) {
|
|
400
|
+
const specifier2 = readSpecifier(tokens[fromAt + 1]);
|
|
401
|
+
throw new Error(
|
|
402
|
+
`a replacement module cannot re-export from another module, and this one re-exports from '${specifier2}'. import what you need and export the binding.`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (token.type !== IMPORT_KEYWORD) continue;
|
|
408
|
+
const after = tokens[index + 1];
|
|
409
|
+
if (!after || after.type === import_types.TokenType.parenL || after.type === import_types.TokenType.dot) continue;
|
|
410
|
+
const bindings = [];
|
|
411
|
+
let cursor = index + 1;
|
|
412
|
+
if (after.type !== import_types.TokenType.string) {
|
|
413
|
+
while (cursor < tokens.length) {
|
|
414
|
+
const clause = tokens[cursor];
|
|
415
|
+
if (clause.type === import_types.TokenType.name && read(clause) === "from") break;
|
|
416
|
+
if (clause.type === import_types.TokenType.comma) {
|
|
417
|
+
cursor++;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (clause.type === import_types.TokenType.star) {
|
|
421
|
+
bindings.push({ local: read(tokens[cursor + 2]), imported: null });
|
|
422
|
+
cursor += 3;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (clause.type === import_types.TokenType.braceL) {
|
|
426
|
+
cursor++;
|
|
427
|
+
while (cursor < tokens.length && tokens[cursor].type !== import_types.TokenType.braceR) {
|
|
428
|
+
if (tokens[cursor].type === import_types.TokenType.comma) {
|
|
429
|
+
cursor++;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const nameToken = tokens[cursor];
|
|
433
|
+
const imported = nameToken.type === import_types.TokenType.string ? readSpecifier(nameToken) : read(nameToken);
|
|
434
|
+
let local = imported;
|
|
435
|
+
if (tokens[cursor + 1] && read(tokens[cursor + 1]) === "as") {
|
|
436
|
+
local = read(tokens[cursor + 2]);
|
|
437
|
+
cursor += 2;
|
|
438
|
+
}
|
|
439
|
+
bindings.push({ local, imported });
|
|
440
|
+
cursor++;
|
|
441
|
+
}
|
|
442
|
+
cursor++;
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
bindings.push({ local: read(clause), imported: "default" });
|
|
446
|
+
cursor++;
|
|
447
|
+
}
|
|
448
|
+
cursor++;
|
|
449
|
+
}
|
|
450
|
+
const specifierToken = tokens[cursor];
|
|
451
|
+
if (!specifierToken) throw new Error("replacement module import is unterminated");
|
|
452
|
+
const specifier = readSpecifier(specifierToken);
|
|
453
|
+
if (specifier !== "react" && specifier !== "react-native" && !JSX_RUNTIME_SPECIFIERS.has(specifier) && !isContrastKitSpecifier(specifier) && !input.runtimePackages.has(specifier)) {
|
|
454
|
+
throw unsupportedSpecifier(specifier);
|
|
455
|
+
}
|
|
456
|
+
const bridge = bindings.length > 0 ? bridgeFor(specifier) : null;
|
|
457
|
+
const replacement = bindings.map((binding) => {
|
|
458
|
+
if (binding.imported === null) return `const ${binding.local} = ${bridge};`;
|
|
459
|
+
if (binding.imported === "default") {
|
|
460
|
+
needsDefaultInterop = true;
|
|
461
|
+
return `const ${binding.local} = ${DEFAULT_INTEROP}(${bridge});`;
|
|
462
|
+
}
|
|
463
|
+
return `const ${binding.local} = ${bridge}[${JSON.stringify(binding.imported)}];`;
|
|
464
|
+
}).join(" ");
|
|
465
|
+
const start = token.start;
|
|
466
|
+
const end = specifierToken.end;
|
|
467
|
+
const newlines = code.slice(start, end).split("\n").length - 1;
|
|
468
|
+
edits.push({ start, end, text: replacement + "\n".repeat(newlines) });
|
|
469
|
+
}
|
|
470
|
+
let out = code;
|
|
471
|
+
for (const edit of edits.reverse()) {
|
|
472
|
+
out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
|
|
473
|
+
}
|
|
474
|
+
if (needsDefaultInterop) declarations.unshift(DEFAULT_INTEROP_DECLARATION);
|
|
475
|
+
const preamble = declarations.join(" ");
|
|
476
|
+
return {
|
|
477
|
+
code: preamble ? `${preamble} ${out}` : out,
|
|
478
|
+
sourceMap: { ...sourceMap, sourcesContent: [input.source] }
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/backend-origin.ts
|
|
483
|
+
function normalizeHostname(hostname) {
|
|
484
|
+
return hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
485
|
+
}
|
|
486
|
+
function isLoopbackHost(hostname) {
|
|
487
|
+
const normalized = normalizeHostname(hostname);
|
|
488
|
+
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/host/replacement-module-handler.ts
|
|
492
|
+
var REPLACEMENT_MODULE_ROUTE = "/__sootsim-replacement-module";
|
|
493
|
+
function sendJavaScript(req, res, source) {
|
|
494
|
+
const origin = req.headers.origin;
|
|
495
|
+
let allowedOrigin;
|
|
496
|
+
if (origin) {
|
|
497
|
+
try {
|
|
498
|
+
if (isLoopbackHost(new URL(origin).hostname)) allowedOrigin = origin;
|
|
499
|
+
} catch {
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
res.writeHead(200, {
|
|
503
|
+
"Content-Type": "application/javascript; charset=utf-8",
|
|
504
|
+
"Cache-Control": "no-cache",
|
|
505
|
+
...allowedOrigin ? { "Access-Control-Allow-Origin": allowedOrigin, Vary: "Origin" } : {}
|
|
506
|
+
});
|
|
507
|
+
res.end(req.method === "HEAD" ? void 0 : source);
|
|
508
|
+
}
|
|
509
|
+
async function handleReplacementModuleRequest(req, res, options = {}) {
|
|
510
|
+
const rawUrl = req.url || "";
|
|
511
|
+
let url;
|
|
512
|
+
try {
|
|
513
|
+
url = new URL(rawUrl, "http://rnx.local");
|
|
514
|
+
} catch {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
if (options.requireLoopbackOrigin) {
|
|
518
|
+
const origin = req.headers.origin;
|
|
519
|
+
const remoteAddress = req.socket.remoteAddress?.replace(/^::ffff:/, "");
|
|
520
|
+
let allowed = Boolean(remoteAddress && isLoopbackHost(remoteAddress));
|
|
521
|
+
if (allowed && origin) {
|
|
522
|
+
try {
|
|
523
|
+
allowed = isLoopbackHost(new URL(origin).hostname);
|
|
524
|
+
} catch {
|
|
525
|
+
allowed = false;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (!allowed) {
|
|
529
|
+
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
|
|
530
|
+
res.end("replacement modules are available only to loopback origins");
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (url.pathname !== REPLACEMENT_MODULE_ROUTE) return false;
|
|
535
|
+
if (options.requestToken && url.searchParams.get("token") !== options.requestToken) {
|
|
536
|
+
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
|
|
537
|
+
res.end("replacement module request token is invalid");
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
const targetPath = url.searchParams.get("path");
|
|
541
|
+
if (!targetPath) {
|
|
542
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
|
|
543
|
+
res.end("missing path param");
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
const extension = import_node_path3.default.extname(targetPath).toLowerCase();
|
|
547
|
+
if (!REPLACEMENT_MODULE_EXTENSIONS.some((allowed) => allowed === extension)) {
|
|
548
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
|
|
549
|
+
res.end(`replacement files must be ${REPLACEMENT_MODULE_EXTENSIONS.join(", ")}`);
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
if (!import_node_path3.default.isAbsolute(targetPath)) {
|
|
553
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
|
|
554
|
+
res.end("replacement file path must be absolute");
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
const runtimePackages = new Set(url.searchParams.getAll("runtimePackage"));
|
|
558
|
+
let resolvedProjectRoot;
|
|
559
|
+
if (options.projectRoot) {
|
|
560
|
+
const lexicalProjectRoot = import_node_path3.default.resolve(options.projectRoot);
|
|
561
|
+
resolvedProjectRoot = await import_promises.default.realpath(options.projectRoot);
|
|
562
|
+
const lexicalTarget = import_node_path3.default.relative(lexicalProjectRoot, targetPath);
|
|
563
|
+
if (lexicalTarget.startsWith("..") || import_node_path3.default.isAbsolute(lexicalTarget)) {
|
|
564
|
+
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
|
|
565
|
+
res.end("replacement file must be inside the project root");
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
let resolvedTargetPath;
|
|
570
|
+
try {
|
|
571
|
+
resolvedTargetPath = await import_promises.default.realpath(targetPath);
|
|
572
|
+
} catch (error) {
|
|
573
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
574
|
+
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
575
|
+
res.end(`replacement file not found: ${message}`);
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
if (resolvedProjectRoot) {
|
|
579
|
+
const relativeTarget = import_node_path3.default.relative(resolvedProjectRoot, resolvedTargetPath);
|
|
580
|
+
if (relativeTarget.startsWith("..") || import_node_path3.default.isAbsolute(relativeTarget)) {
|
|
581
|
+
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
|
|
582
|
+
res.end("replacement file must be inside the project root");
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
const compiled = compileReplacementModule({
|
|
588
|
+
filePath: resolvedTargetPath,
|
|
589
|
+
source: await import_promises.default.readFile(resolvedTargetPath, "utf8"),
|
|
590
|
+
runtimePackages
|
|
591
|
+
});
|
|
592
|
+
const map = Buffer.from(JSON.stringify(compiled.sourceMap), "utf8").toString("base64");
|
|
593
|
+
sendJavaScript(
|
|
594
|
+
req,
|
|
595
|
+
res,
|
|
596
|
+
`${compiled.code}
|
|
597
|
+
//# sourceMappingURL=data:application/json;base64,${map}
|
|
598
|
+
`
|
|
599
|
+
);
|
|
600
|
+
} catch (error) {
|
|
601
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
602
|
+
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
603
|
+
res.end(`replacement compile failed: ${message}`);
|
|
604
|
+
}
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/runtime-assets.ts
|
|
609
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
610
|
+
var import_path = __toESM(require("path"), 1);
|
|
611
|
+
var import_url = require("url");
|
|
612
|
+
|
|
613
|
+
// ../contrast-runtime-delivery/src/product.ts
|
|
614
|
+
function isValidRuntimeVersion(value) {
|
|
615
|
+
return value.length <= 100 && /^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(value);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/runtime-assets.ts
|
|
619
|
+
var SOOTSIM_RUNTIME_MISSING_MESSAGE = "[rnx] no engine runtime installed. run `rnx runtime install`";
|
|
620
|
+
var _monorepoRootCache;
|
|
621
|
+
function findMonorepoRoot() {
|
|
622
|
+
if (_monorepoRootCache !== void 0) return _monorepoRootCache;
|
|
623
|
+
let dir = import_path.default.dirname((0, import_url.fileURLToPath)(__sootsim_import_meta_url));
|
|
624
|
+
for (let i = 0; i < 12 && dir !== import_path.default.dirname(dir); i++) {
|
|
625
|
+
if (import_fs.default.existsSync(import_path.default.join(dir, "packages/sootsim-engine/package.json")) && import_fs.default.existsSync(import_path.default.join(dir, "public/sootsim"))) {
|
|
626
|
+
_monorepoRootCache = dir;
|
|
627
|
+
return dir;
|
|
628
|
+
}
|
|
629
|
+
dir = import_path.default.dirname(dir);
|
|
630
|
+
}
|
|
631
|
+
_monorepoRootCache = null;
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
function resolveDevCheckoutRuntimeRoot() {
|
|
635
|
+
if (process.env.RNX_HOME && readActiveRuntime()) return null;
|
|
636
|
+
const root = findMonorepoRoot();
|
|
637
|
+
if (!root) return null;
|
|
638
|
+
const devBuild = import_path.default.join(root, "public/sootsim");
|
|
639
|
+
if (import_fs.default.existsSync(import_path.default.join(devBuild, "index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine/index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.js"))) {
|
|
640
|
+
return devBuild;
|
|
641
|
+
}
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
var MIME_TYPES = {
|
|
645
|
+
".js": "application/javascript",
|
|
646
|
+
".mjs": "application/javascript",
|
|
647
|
+
".css": "text/css",
|
|
648
|
+
".html": "text/html",
|
|
649
|
+
".wasm": "application/wasm",
|
|
650
|
+
".json": "application/json",
|
|
651
|
+
".jpg": "image/jpeg",
|
|
652
|
+
".jpeg": "image/jpeg",
|
|
653
|
+
".png": "image/png",
|
|
654
|
+
".svg": "image/svg+xml",
|
|
655
|
+
".webp": "image/webp",
|
|
656
|
+
".glb": "model/gltf-binary",
|
|
657
|
+
".ttf": "font/ttf",
|
|
658
|
+
".otf": "font/otf",
|
|
659
|
+
".woff": "font/woff",
|
|
660
|
+
".woff2": "font/woff2",
|
|
661
|
+
".mp3": "audio/mpeg",
|
|
662
|
+
".wav": "audio/wav"
|
|
663
|
+
};
|
|
664
|
+
var ROOT_RUNTIME_PATHS = [
|
|
665
|
+
"/assets/",
|
|
666
|
+
"/engine/",
|
|
667
|
+
"/engine-tenant/",
|
|
668
|
+
"/photos/",
|
|
669
|
+
"/three-mode/",
|
|
670
|
+
"/fonts/",
|
|
671
|
+
"/icons/",
|
|
672
|
+
"/sounds/",
|
|
673
|
+
"/spike/",
|
|
674
|
+
"/test-wallpaper.jpg",
|
|
675
|
+
"/preview-sw.js",
|
|
676
|
+
"/sootsim.svg"
|
|
677
|
+
];
|
|
678
|
+
function resolveActiveRuntimeRoot() {
|
|
679
|
+
const devRoot = resolveDevCheckoutRuntimeRoot();
|
|
680
|
+
if (devRoot) return devRoot;
|
|
681
|
+
const active = readActiveRuntime();
|
|
682
|
+
if (!active) return null;
|
|
683
|
+
const dir = runtimeDir(active);
|
|
684
|
+
if (!import_fs.default.existsSync(import_path.default.join(dir, "index.html"))) return null;
|
|
685
|
+
return dir;
|
|
686
|
+
}
|
|
687
|
+
var RUNTIME_VERSION_HOSTNAME_SUFFIX = ".runtime.localhost";
|
|
688
|
+
function isRuntimeVersionHostname(hostname) {
|
|
689
|
+
return hostname.toLowerCase().endsWith(RUNTIME_VERSION_HOSTNAME_SUFFIX);
|
|
690
|
+
}
|
|
691
|
+
function runtimeVersionFromHostname(hostname) {
|
|
692
|
+
if (!isRuntimeVersionHostname(hostname)) return null;
|
|
693
|
+
const labels = hostname.slice(0, -RUNTIME_VERSION_HOSTNAME_SUFFIX.length).split(".");
|
|
694
|
+
if (labels.length === 0 || labels.some((label) => !/^[0-9a-f]{1,60}$/i.test(label))) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
const encoded = labels.join("");
|
|
698
|
+
if (encoded.length % 2 !== 0) return null;
|
|
699
|
+
try {
|
|
700
|
+
const version = Buffer.from(encoded, "hex").toString("utf8");
|
|
701
|
+
return isValidRuntimeVersion(version) ? version : null;
|
|
702
|
+
} catch {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
function resolveInstalledRuntimeRoot(version) {
|
|
707
|
+
const dir = runtimeDir(version);
|
|
708
|
+
if (!import_fs.default.existsSync(import_path.default.join(dir, "index.html"))) return null;
|
|
709
|
+
return dir;
|
|
710
|
+
}
|
|
711
|
+
function resolveRuntimeRootForHostname(hostname) {
|
|
712
|
+
if (!isRuntimeVersionHostname(hostname)) return resolveActiveRuntimeRoot();
|
|
713
|
+
const version = runtimeVersionFromHostname(hostname);
|
|
714
|
+
return version ? resolveInstalledRuntimeRoot(version) : null;
|
|
715
|
+
}
|
|
716
|
+
function resolveRuntimeRootForRequestHost(host) {
|
|
717
|
+
if (!host) return resolveActiveRuntimeRoot();
|
|
718
|
+
try {
|
|
719
|
+
return resolveRuntimeRootForHostname(new URL(`http://${host}`).hostname);
|
|
720
|
+
} catch {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function isRootRuntimeAssetPath(pathname) {
|
|
725
|
+
return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p));
|
|
726
|
+
}
|
|
727
|
+
function resolveRuntimeFilePath(runtimeRoot, pathname) {
|
|
728
|
+
if (!pathname.startsWith("/")) return null;
|
|
729
|
+
if (pathname.includes("\0") || pathname.includes("\\")) return null;
|
|
730
|
+
for (const segment of pathname.split("/")) {
|
|
731
|
+
if (segment === "..") return null;
|
|
732
|
+
}
|
|
733
|
+
const fullPath = import_path.default.resolve(runtimeRoot, pathname.replace(/^\/+/, ""));
|
|
734
|
+
const rootWithSep = runtimeRoot.endsWith(import_path.default.sep) ? runtimeRoot : runtimeRoot + import_path.default.sep;
|
|
735
|
+
if (!fullPath.startsWith(rootWithSep) && fullPath !== runtimeRoot) return null;
|
|
736
|
+
if (!import_fs.default.existsSync(fullPath) || !import_fs.default.statSync(fullPath).isFile()) return null;
|
|
737
|
+
return fullPath;
|
|
738
|
+
}
|
|
739
|
+
function serveRuntimeFile(req, res, fullPath) {
|
|
740
|
+
const ext = import_path.default.extname(fullPath);
|
|
741
|
+
const stat = import_fs.default.statSync(fullPath);
|
|
742
|
+
const lastModified = stat.mtime.toUTCString();
|
|
743
|
+
res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
|
|
744
|
+
if (/^canvaskit-graphite-pipelines-[0-9a-f]{8}\.bin$/.test(import_path.default.basename(fullPath))) {
|
|
745
|
+
res.setHeader("cache-control", "public, max-age=31536000, immutable");
|
|
746
|
+
import_fs.default.createReadStream(fullPath).pipe(res);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
res.setHeader("cache-control", "no-cache");
|
|
750
|
+
res.setHeader("last-modified", lastModified);
|
|
751
|
+
if (req?.headers?.["if-modified-since"] === lastModified) {
|
|
752
|
+
res.statusCode = 304;
|
|
753
|
+
res.end();
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
import_fs.default.createReadStream(fullPath).pipe(res);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// src/vite-plugin-swift.ts
|
|
760
|
+
var import_node_child_process2 = require("node:child_process");
|
|
761
|
+
var import_node_crypto2 = require("node:crypto");
|
|
762
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
763
|
+
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
764
|
+
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
765
|
+
var import_node_util = require("node:util");
|
|
766
|
+
|
|
767
|
+
// ../sootsim-swift/src/bundleAssets.ts
|
|
768
|
+
var RASTER_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
769
|
+
".avif",
|
|
770
|
+
".bmp",
|
|
771
|
+
".gif",
|
|
772
|
+
".ico",
|
|
773
|
+
".jpeg",
|
|
774
|
+
".jpg",
|
|
775
|
+
".png",
|
|
776
|
+
".webp"
|
|
777
|
+
]);
|
|
778
|
+
function extOf(file) {
|
|
779
|
+
const dot = file.lastIndexOf(".");
|
|
780
|
+
return dot < 0 ? "" : file.slice(dot).toLowerCase();
|
|
781
|
+
}
|
|
782
|
+
function baseOf(file) {
|
|
783
|
+
const slash = file.lastIndexOf("/");
|
|
784
|
+
return slash < 0 ? file : file.slice(slash + 1);
|
|
785
|
+
}
|
|
786
|
+
function pickImagesetFile(images) {
|
|
787
|
+
const rasters = images.filter(
|
|
788
|
+
(image) => typeof image.filename === "string" && RASTER_EXTENSIONS.has(extOf(image.filename))
|
|
789
|
+
);
|
|
790
|
+
if (rasters.length === 0) return null;
|
|
791
|
+
const rank = (scale) => scale === "2x" ? 0 : scale === "3x" ? 1 : scale === "1x" ? 2 : 3;
|
|
792
|
+
return [...rasters].sort((a, b) => rank(a.scale) - rank(b.scale))[0].filename;
|
|
793
|
+
}
|
|
794
|
+
function imagesetName(contentsPath) {
|
|
795
|
+
const dir = contentsPath.split("/").slice(0, -1).join("/");
|
|
796
|
+
const leaf = baseOf(dir);
|
|
797
|
+
if (!leaf.endsWith(".imageset")) return null;
|
|
798
|
+
return leaf.slice(0, -".imageset".length) || null;
|
|
799
|
+
}
|
|
800
|
+
function isFlatResourceImage(path8) {
|
|
801
|
+
return path8.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path8));
|
|
802
|
+
}
|
|
803
|
+
function enumerateBundleImages(paths, readText) {
|
|
804
|
+
const table = [];
|
|
805
|
+
const seen = /* @__PURE__ */ new Set();
|
|
806
|
+
const catalogs = paths.filter((path8) => baseOf(path8) === "Contents.json");
|
|
807
|
+
for (const contentsPath of catalogs) {
|
|
808
|
+
const name = imagesetName(contentsPath);
|
|
809
|
+
if (name === null || seen.has(name)) continue;
|
|
810
|
+
let parsed;
|
|
811
|
+
try {
|
|
812
|
+
const text = readText(contentsPath);
|
|
813
|
+
if (text === null) continue;
|
|
814
|
+
parsed = JSON.parse(text);
|
|
815
|
+
} catch {
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
const images = parsed?.images;
|
|
819
|
+
if (!Array.isArray(images)) continue;
|
|
820
|
+
const dir = contentsPath.split("/").slice(0, -1).join("/");
|
|
821
|
+
const file = pickImagesetFile(images);
|
|
822
|
+
if (file === null || file.includes("/") || file.includes("\\")) continue;
|
|
823
|
+
seen.add(name);
|
|
824
|
+
table.push({ name, file: `${dir}/${file}` });
|
|
825
|
+
}
|
|
826
|
+
for (const path8 of paths) {
|
|
827
|
+
if (!isFlatResourceImage(path8)) continue;
|
|
828
|
+
const name = baseOf(path8).slice(0, -extOf(path8).length);
|
|
829
|
+
if (name === "" || seen.has(name)) continue;
|
|
830
|
+
seen.add(name);
|
|
831
|
+
table.push({ name, file: path8 });
|
|
832
|
+
}
|
|
833
|
+
return table;
|
|
834
|
+
}
|
|
835
|
+
function enumerateBundleJson(paths) {
|
|
836
|
+
const found = [];
|
|
837
|
+
const seen = /* @__PURE__ */ new Set();
|
|
838
|
+
const ordered = [...paths].filter(
|
|
839
|
+
(path8) => path8.split("/").includes("Resources") && path8.split("/").every((segment) => segment !== "" && segment !== "..") && extOf(path8) === ".json"
|
|
840
|
+
).sort((a, b) => {
|
|
841
|
+
const aDepth = a.split("/").length;
|
|
842
|
+
const bDepth = b.split("/").length;
|
|
843
|
+
return aDepth - bDepth || (a < b ? -1 : a > b ? 1 : 0);
|
|
844
|
+
});
|
|
845
|
+
for (const file of ordered) {
|
|
846
|
+
const base = baseOf(file);
|
|
847
|
+
if (seen.has(base)) continue;
|
|
848
|
+
seen.add(base);
|
|
849
|
+
found.push({ base, file });
|
|
850
|
+
}
|
|
851
|
+
return found;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// ../sootsim-swift/toolchain.json
|
|
855
|
+
var toolchain_default = {
|
|
856
|
+
release: "swift-6.3-RELEASE",
|
|
857
|
+
wasmSdkChecksum: "9fa4016ee632c7e9e906608ec3b55cf13dfc4dff44e47574c5af58064dc33fd9"
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
// src/vite-plugin-swift.ts
|
|
861
|
+
var run = (0, import_node_util.promisify)(import_node_child_process2.execFile);
|
|
862
|
+
var TOOLCHAIN_BIN = import_node_path4.default.join(
|
|
863
|
+
import_node_os2.default.homedir(),
|
|
864
|
+
`Library/Developer/Toolchains/${toolchain_default.release}.xctoolchain/usr/bin`
|
|
865
|
+
);
|
|
866
|
+
var WASM_SDK = `${toolchain_default.release}_wasm`;
|
|
867
|
+
var WASM_TRIPLE = "wasm32-unknown-wasip1";
|
|
868
|
+
var ARTIFACT_PATH = "/__rnx/swift/";
|
|
869
|
+
var RESOURCE_PATH = "/__rnx/swift-res/";
|
|
870
|
+
var VIRTUAL_PREFIX = "\0rnx-swift:";
|
|
871
|
+
var projects = /* @__PURE__ */ new Map();
|
|
872
|
+
var devServer;
|
|
873
|
+
function projectFor(packagePath) {
|
|
874
|
+
const existing = projects.get(packagePath);
|
|
875
|
+
if (existing) return existing;
|
|
876
|
+
const created = {
|
|
877
|
+
packagePath,
|
|
878
|
+
artifact: null,
|
|
879
|
+
building: null,
|
|
880
|
+
resourceFiles: /* @__PURE__ */ new Map()
|
|
881
|
+
};
|
|
882
|
+
projects.set(packagePath, created);
|
|
883
|
+
return created;
|
|
884
|
+
}
|
|
885
|
+
function sourceFiles(root) {
|
|
886
|
+
const found = [];
|
|
887
|
+
const walk = (dir) => {
|
|
888
|
+
for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
|
|
889
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
890
|
+
const full = import_node_path4.default.join(dir, entry.name);
|
|
891
|
+
if (entry.isDirectory()) {
|
|
892
|
+
if (full !== root && import_node_fs3.default.existsSync(import_node_path4.default.join(full, "Package.swift"))) continue;
|
|
893
|
+
walk(full);
|
|
894
|
+
} else if (entry.name.endsWith(".swift")) found.push(full);
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
walk(root);
|
|
898
|
+
return found;
|
|
899
|
+
}
|
|
900
|
+
function resourceCandidateFiles(root) {
|
|
901
|
+
const found = [];
|
|
902
|
+
const walk = (dir) => {
|
|
903
|
+
for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
|
|
904
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === ".build")
|
|
905
|
+
continue;
|
|
906
|
+
const full = import_node_path4.default.join(dir, entry.name);
|
|
907
|
+
if (entry.isDirectory()) {
|
|
908
|
+
if (full !== root && import_node_fs3.default.existsSync(import_node_path4.default.join(full, "Package.swift"))) continue;
|
|
909
|
+
walk(full);
|
|
910
|
+
} else {
|
|
911
|
+
found.push(full);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
walk(root);
|
|
916
|
+
return found;
|
|
917
|
+
}
|
|
918
|
+
var RESOURCE_CONTENT_TYPES = {
|
|
919
|
+
".avif": "image/avif",
|
|
920
|
+
".bmp": "image/bmp",
|
|
921
|
+
".gif": "image/gif",
|
|
922
|
+
".ico": "image/x-icon",
|
|
923
|
+
".jpeg": "image/jpeg",
|
|
924
|
+
".jpg": "image/jpeg",
|
|
925
|
+
".png": "image/png",
|
|
926
|
+
".webp": "image/webp"
|
|
927
|
+
};
|
|
928
|
+
function buildSwiftResourceTable(project, baseUrl) {
|
|
929
|
+
const absolute = resourceCandidateFiles(project.packagePath);
|
|
930
|
+
const relative2 = absolute.map(
|
|
931
|
+
(file) => import_node_path4.default.relative(project.packagePath, file).split(import_node_path4.default.sep).join(import_node_path4.default.posix.sep)
|
|
932
|
+
);
|
|
933
|
+
const byRelative = new Map(absolute.map((file, index) => [relative2[index], file]));
|
|
934
|
+
const found = enumerateBundleImages(relative2, (candidate) => {
|
|
935
|
+
const file = byRelative.get(candidate);
|
|
936
|
+
if (!file || import_node_path4.default.basename(file) !== "Contents.json") return null;
|
|
937
|
+
try {
|
|
938
|
+
return import_node_fs3.default.readFileSync(file, "utf8");
|
|
939
|
+
} catch {
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
const table = {};
|
|
944
|
+
const watched = [];
|
|
945
|
+
const registered = /* @__PURE__ */ new Map();
|
|
946
|
+
for (const { name, file } of found) {
|
|
947
|
+
const disk = byRelative.get(file);
|
|
948
|
+
if (!disk) continue;
|
|
949
|
+
let bytes;
|
|
950
|
+
try {
|
|
951
|
+
bytes = import_node_fs3.default.readFileSync(disk);
|
|
952
|
+
} catch {
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 16);
|
|
956
|
+
const ext = import_node_path4.default.posix.extname(file).toLowerCase();
|
|
957
|
+
table[name] = `${baseUrl}${RESOURCE_PATH}${hash}${ext}`;
|
|
958
|
+
watched.push(file);
|
|
959
|
+
const contents = file.replace(/\/[^/]+$/, "/Contents.json");
|
|
960
|
+
if (byRelative.has(contents)) watched.push(contents);
|
|
961
|
+
registered.set(`${hash}${ext}`, disk);
|
|
962
|
+
}
|
|
963
|
+
project.resourceFiles = registered;
|
|
964
|
+
return { table, watched };
|
|
965
|
+
}
|
|
966
|
+
var BUNDLE_MAX_FILES = 128;
|
|
967
|
+
var BUNDLE_MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
968
|
+
var BUNDLE_MAX_TOTAL_BYTES = 8 * 1024 * 1024;
|
|
969
|
+
function buildSwiftBundleFiles(packagePath) {
|
|
970
|
+
const relative2 = resourceCandidateFiles(packagePath).map(
|
|
971
|
+
(file) => import_node_path4.default.relative(packagePath, file).split(import_node_path4.default.sep).join(import_node_path4.default.posix.sep)
|
|
972
|
+
);
|
|
973
|
+
const files = {};
|
|
974
|
+
const watched = [];
|
|
975
|
+
const skipped = [];
|
|
976
|
+
let total = 0;
|
|
977
|
+
for (const { base, file } of enumerateBundleJson(relative2)) {
|
|
978
|
+
watched.push(file);
|
|
979
|
+
let bytes;
|
|
980
|
+
try {
|
|
981
|
+
bytes = import_node_fs3.default.readFileSync(import_node_path4.default.join(packagePath, ...file.split("/")));
|
|
982
|
+
} catch {
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
if (bytes.length > BUNDLE_MAX_FILE_BYTES) {
|
|
986
|
+
skipped.push(`${file} (${bytes.length} bytes past the per-file cap)`);
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
if (Object.keys(files).length >= BUNDLE_MAX_FILES || total + bytes.length > BUNDLE_MAX_TOTAL_BYTES) {
|
|
990
|
+
skipped.push(
|
|
991
|
+
`${file} (pack caps: ${BUNDLE_MAX_FILES} files / ${BUNDLE_MAX_TOTAL_BYTES} bytes)`
|
|
992
|
+
);
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
files[base] = bytes.toString("base64");
|
|
996
|
+
total += bytes.length;
|
|
997
|
+
}
|
|
998
|
+
return { files, watched, skipped };
|
|
999
|
+
}
|
|
1000
|
+
function serveSwiftResource(project, key) {
|
|
1001
|
+
const disk = project.resourceFiles.get(key);
|
|
1002
|
+
if (!disk) return null;
|
|
1003
|
+
try {
|
|
1004
|
+
return {
|
|
1005
|
+
bytes: import_node_fs3.default.readFileSync(disk),
|
|
1006
|
+
contentType: RESOURCE_CONTENT_TYPES[import_node_path4.default.extname(disk).toLowerCase()] ?? "application/octet-stream"
|
|
1007
|
+
};
|
|
1008
|
+
} catch {
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
function linkedModuleRoots(packagePath) {
|
|
1013
|
+
let manifest;
|
|
1014
|
+
try {
|
|
1015
|
+
manifest = import_node_fs3.default.readFileSync(import_node_path4.default.join(packagePath, "Package.swift"), "utf8");
|
|
1016
|
+
} catch {
|
|
1017
|
+
return [];
|
|
1018
|
+
}
|
|
1019
|
+
const roots = /* @__PURE__ */ new Set();
|
|
1020
|
+
const dependency = /\.package\(\s*path:\s*"([^"]+)"\s*\)/g;
|
|
1021
|
+
for (const match of manifest.replace(/^\s*\/\/.*$/gm, "").matchAll(dependency)) {
|
|
1022
|
+
const root = import_node_path4.default.resolve(packagePath, match[1]);
|
|
1023
|
+
if (import_node_fs3.default.existsSync(import_node_path4.default.join(root, "Package.swift"))) roots.add(root);
|
|
1024
|
+
}
|
|
1025
|
+
return [...roots].sort();
|
|
1026
|
+
}
|
|
1027
|
+
function sourceStamp(packagePath) {
|
|
1028
|
+
const hash = (0, import_node_crypto2.createHash)("sha256");
|
|
1029
|
+
for (const root of [packagePath, ...linkedModuleRoots(packagePath)]) {
|
|
1030
|
+
hash.update(root);
|
|
1031
|
+
for (const file of sourceFiles(root).sort()) {
|
|
1032
|
+
hash.update(import_node_path4.default.relative(root, file));
|
|
1033
|
+
hash.update(String(import_node_fs3.default.statSync(file).mtimeMs));
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return hash.digest("hex").slice(0, 16);
|
|
1037
|
+
}
|
|
1038
|
+
function entryRootId(packagePath, file) {
|
|
1039
|
+
const relative2 = import_node_path4.default.relative(packagePath, file).split(import_node_path4.default.sep).join(import_node_path4.default.posix.sep);
|
|
1040
|
+
return `${import_node_path4.default.basename(packagePath)}${import_node_path4.default.posix.sep}${relative2}`;
|
|
1041
|
+
}
|
|
1042
|
+
function serverOrigin() {
|
|
1043
|
+
const address = devServer?.httpServer?.address();
|
|
1044
|
+
const port = address && typeof address === "object" ? address.port : devServer?.config.server.port;
|
|
1045
|
+
if (!port) throw new Error("the swift urls need the dev server port");
|
|
1046
|
+
return `http://localhost:${port}`;
|
|
1047
|
+
}
|
|
1048
|
+
function artifactUrl(project, artifact) {
|
|
1049
|
+
return `${serverOrigin()}${ARTIFACT_PATH}${artifact.hash}.wasm`;
|
|
1050
|
+
}
|
|
1051
|
+
function builtArtifact(packagePath) {
|
|
1052
|
+
const binDir = import_node_path4.default.join(packagePath, ".build", WASM_TRIPLE, "release");
|
|
1053
|
+
const artifacts = import_node_fs3.default.existsSync(binDir) ? import_node_fs3.default.readdirSync(binDir).filter((name) => name.endsWith(".wasm")).sort() : [];
|
|
1054
|
+
if (artifacts.length !== 1) {
|
|
1055
|
+
throw new Error(
|
|
1056
|
+
`swift project ${packagePath} built ${artifacts.length} wasm artifacts; exactly one executable is the app`
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
return import_node_path4.default.join(binDir, artifacts[0]);
|
|
1060
|
+
}
|
|
1061
|
+
function streamText(value) {
|
|
1062
|
+
if (typeof value === "string") return value.trim();
|
|
1063
|
+
if (Buffer.isBuffer(value)) return value.toString("utf8").trim();
|
|
1064
|
+
return "";
|
|
1065
|
+
}
|
|
1066
|
+
async function compile(packagePath, stamp) {
|
|
1067
|
+
const swift = import_node_path4.default.join(TOOLCHAIN_BIN, "swift");
|
|
1068
|
+
if (!import_node_fs3.default.existsSync(swift)) {
|
|
1069
|
+
throw new Error(
|
|
1070
|
+
`no swift.org toolchain at ${TOOLCHAIN_BIN}. install it and the ${WASM_SDK} sdk; see packages/sootsim-swift/README.md`
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
try {
|
|
1074
|
+
await run(
|
|
1075
|
+
swift,
|
|
1076
|
+
[
|
|
1077
|
+
"build",
|
|
1078
|
+
"--package-path",
|
|
1079
|
+
packagePath,
|
|
1080
|
+
"--swift-sdk",
|
|
1081
|
+
WASM_SDK,
|
|
1082
|
+
"-c",
|
|
1083
|
+
"release",
|
|
1084
|
+
"-Xswiftc",
|
|
1085
|
+
"-Osize"
|
|
1086
|
+
],
|
|
1087
|
+
{ maxBuffer: 64 * 1024 * 1024 }
|
|
1088
|
+
);
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
const streams = error && typeof error === "object" ? [
|
|
1091
|
+
streamText(Reflect.get(error, "stdout")),
|
|
1092
|
+
streamText(Reflect.get(error, "stderr"))
|
|
1093
|
+
].filter(Boolean) : [];
|
|
1094
|
+
const detail = streams.length > 0 ? streams.join("\n") : error instanceof Error ? error.message : String(error);
|
|
1095
|
+
throw new Error(`swift build failed for ${packagePath}
|
|
1096
|
+
${detail}`);
|
|
1097
|
+
}
|
|
1098
|
+
const bytes = import_node_fs3.default.readFileSync(builtArtifact(packagePath));
|
|
1099
|
+
return {
|
|
1100
|
+
hash: (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 16),
|
|
1101
|
+
bytes,
|
|
1102
|
+
stamp
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
function artifactFor(project) {
|
|
1106
|
+
const stamp = sourceStamp(project.packagePath);
|
|
1107
|
+
if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact);
|
|
1108
|
+
if (project.building) {
|
|
1109
|
+
const running = project.building;
|
|
1110
|
+
if (running.stamp === stamp) return running.promise;
|
|
1111
|
+
const again = () => artifactFor(project);
|
|
1112
|
+
return running.promise.then(again, again);
|
|
1113
|
+
}
|
|
1114
|
+
const promise = compile(project.packagePath, stamp).then(
|
|
1115
|
+
(artifact) => {
|
|
1116
|
+
project.artifact = artifact;
|
|
1117
|
+
project.building = null;
|
|
1118
|
+
return artifact;
|
|
1119
|
+
},
|
|
1120
|
+
(error) => {
|
|
1121
|
+
project.building = null;
|
|
1122
|
+
throw error;
|
|
1123
|
+
}
|
|
1124
|
+
);
|
|
1125
|
+
project.building = { stamp, promise };
|
|
1126
|
+
return promise;
|
|
1127
|
+
}
|
|
1128
|
+
function packageFor(file) {
|
|
1129
|
+
let dir = import_node_path4.default.dirname(file);
|
|
1130
|
+
const stop = devServer?.config.root;
|
|
1131
|
+
for (; ; ) {
|
|
1132
|
+
if (import_node_fs3.default.existsSync(import_node_path4.default.join(dir, "Package.swift"))) return dir;
|
|
1133
|
+
if (stop !== void 0 && dir === stop || import_node_path4.default.dirname(dir) === dir) break;
|
|
1134
|
+
dir = import_node_path4.default.dirname(dir);
|
|
1135
|
+
}
|
|
1136
|
+
throw new Error(
|
|
1137
|
+
`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`
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
function swiftPlugin(options = {}) {
|
|
1141
|
+
return {
|
|
1142
|
+
name: "rnx-swift",
|
|
1143
|
+
enforce: "pre",
|
|
1144
|
+
async resolveId(source, importer) {
|
|
1145
|
+
if (source.startsWith(VIRTUAL_PREFIX)) return source;
|
|
1146
|
+
if (source.startsWith("/@id/")) {
|
|
1147
|
+
const decoded = source.slice("/@id/".length).replace(/^__x00__/, "\0");
|
|
1148
|
+
return decoded.startsWith(VIRTUAL_PREFIX) ? decoded : null;
|
|
1149
|
+
}
|
|
1150
|
+
if (!source.endsWith(".swift")) return null;
|
|
1151
|
+
const resolved = await this.resolve(source, importer, { skipSelf: true });
|
|
1152
|
+
if (!resolved) throw new Error(`cannot resolve swift source ${source}`);
|
|
1153
|
+
return VIRTUAL_PREFIX + resolved.id;
|
|
1154
|
+
},
|
|
1155
|
+
async load(id) {
|
|
1156
|
+
if (!id.startsWith(VIRTUAL_PREFIX)) return null;
|
|
1157
|
+
const file = id.slice(VIRTUAL_PREFIX.length);
|
|
1158
|
+
if (!import_node_fs3.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
|
|
1159
|
+
const packagePath = options.packagePath ?? packageFor(file);
|
|
1160
|
+
const project = projectFor(packagePath);
|
|
1161
|
+
for (const source of sourceFiles(packagePath)) this.addWatchFile(source);
|
|
1162
|
+
for (const root of linkedModuleRoots(packagePath)) {
|
|
1163
|
+
for (const source of sourceFiles(root)) this.addWatchFile(source);
|
|
1164
|
+
}
|
|
1165
|
+
let artifact;
|
|
1166
|
+
try {
|
|
1167
|
+
artifact = await artifactFor(project);
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
if (!project.artifact) throw error;
|
|
1170
|
+
this.warn(error instanceof Error ? error.message : String(error));
|
|
1171
|
+
artifact = project.artifact;
|
|
1172
|
+
}
|
|
1173
|
+
let resources = {};
|
|
1174
|
+
try {
|
|
1175
|
+
const built = buildSwiftResourceTable(project, serverOrigin());
|
|
1176
|
+
resources = built.table;
|
|
1177
|
+
for (const watched of built.watched) this.addWatchFile(watched);
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
this.warn(
|
|
1180
|
+
`swift resources for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
let bundleFiles = {};
|
|
1184
|
+
try {
|
|
1185
|
+
const packed = buildSwiftBundleFiles(packagePath);
|
|
1186
|
+
bundleFiles = packed.files;
|
|
1187
|
+
for (const watched of packed.watched) {
|
|
1188
|
+
this.addWatchFile(import_node_path4.default.join(packagePath, ...watched.split(import_node_path4.default.posix.sep)));
|
|
1189
|
+
}
|
|
1190
|
+
if (packed.skipped.length > 0) {
|
|
1191
|
+
this.warn(
|
|
1192
|
+
`swift bundle files skipped for ${packagePath}: ${packed.skipped.join("; ")}`
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
this.warn(
|
|
1197
|
+
`swift bundle files for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
return [
|
|
1201
|
+
`import { createSwiftEntry } from 'rnxsim/swift'`,
|
|
1202
|
+
`export default createSwiftEntry({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)}, rootId: ${JSON.stringify(entryRootId(packagePath, file))}, resources: ${JSON.stringify(resources)}, bundleFiles: ${JSON.stringify(bundleFiles)} })`,
|
|
1203
|
+
""
|
|
1204
|
+
].join("\n");
|
|
1205
|
+
},
|
|
1206
|
+
configureServer(server) {
|
|
1207
|
+
devServer = server;
|
|
1208
|
+
server.middlewares.use((req, res, next) => {
|
|
1209
|
+
const url = (req.url ?? "").split("?")[0];
|
|
1210
|
+
if (url.startsWith(RESOURCE_PATH)) {
|
|
1211
|
+
for (const project of projects.values()) {
|
|
1212
|
+
const served = serveSwiftResource(project, url.slice(RESOURCE_PATH.length));
|
|
1213
|
+
if (served) {
|
|
1214
|
+
res.setHeader("access-control-allow-origin", "*");
|
|
1215
|
+
res.setHeader("content-type", served.contentType);
|
|
1216
|
+
res.end(served.bytes);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
res.statusCode = 404;
|
|
1221
|
+
res.end("swift resource is stale");
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (!url.startsWith(ARTIFACT_PATH)) {
|
|
1225
|
+
next();
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
for (const project of projects.values()) {
|
|
1229
|
+
const artifact = project.artifact;
|
|
1230
|
+
if (artifact && url === `${ARTIFACT_PATH}${artifact.hash}.wasm`) {
|
|
1231
|
+
res.setHeader("access-control-allow-origin", "*");
|
|
1232
|
+
res.setHeader("content-type", "application/wasm");
|
|
1233
|
+
res.end(artifact.bytes);
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
res.statusCode = 404;
|
|
1238
|
+
res.end("swift artifact is stale");
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// src/rolldown-plugin-swift.ts
|
|
1245
|
+
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
1246
|
+
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
1247
|
+
|
|
1248
|
+
// src/swift-submit.ts
|
|
1249
|
+
var SWIFT_COMPILE_PATH = "/api/swift-compile";
|
|
1250
|
+
var POLL_INTERVAL_MS = 1500;
|
|
1251
|
+
function httpSwiftCompileSubmit(context) {
|
|
1252
|
+
const { assetReadToken, endpoint } = context;
|
|
1253
|
+
if (!assetReadToken) return void 0;
|
|
1254
|
+
const url = endpoint ? `${endpoint.replace(/\/+$/, "")}${SWIFT_COMPILE_PATH}` : SWIFT_COMPILE_PATH;
|
|
1255
|
+
return async (work) => {
|
|
1256
|
+
let response;
|
|
1257
|
+
try {
|
|
1258
|
+
response = await fetch(url, {
|
|
1259
|
+
method: "POST",
|
|
1260
|
+
headers: { "content-type": "application/json" },
|
|
1261
|
+
body: JSON.stringify({ ...work, token: assetReadToken })
|
|
1262
|
+
});
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
throw new Error(
|
|
1265
|
+
`contrast-bundler: fetch failed for ${url} while compiling ${work.entry}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1266
|
+
{ cause: error }
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
if (!response.ok) {
|
|
1270
|
+
const detail = (await response.text()).trim();
|
|
1271
|
+
throw new Error(
|
|
1272
|
+
`contrast-bundler: the swift compile for ${work.entry} answered ${response.status}${detail ? `: ${detail}` : ""}`
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
return await response.json();
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
function resolveImageUrls(urls, origin) {
|
|
1279
|
+
const resolved = {};
|
|
1280
|
+
for (const [name, url] of Object.entries(urls)) {
|
|
1281
|
+
resolved[name] = new URL(url, origin).href;
|
|
1282
|
+
}
|
|
1283
|
+
return resolved;
|
|
1284
|
+
}
|
|
1285
|
+
async function compileSwiftProject(submit, work, origin) {
|
|
1286
|
+
while (true) {
|
|
1287
|
+
const outcome = await submit(work);
|
|
1288
|
+
switch (outcome.status) {
|
|
1289
|
+
case "published":
|
|
1290
|
+
return {
|
|
1291
|
+
url: new URL(outcome.artifactUrl, origin).href,
|
|
1292
|
+
hash: outcome.hash,
|
|
1293
|
+
...outcome.imageUrls === void 0 ? {} : { imageUrls: resolveImageUrls(outcome.imageUrls, origin) }
|
|
1294
|
+
};
|
|
1295
|
+
case "compiling":
|
|
1296
|
+
await new Promise((resolve2) => setTimeout(resolve2, POLL_INTERVAL_MS));
|
|
1297
|
+
continue;
|
|
1298
|
+
case "refused":
|
|
1299
|
+
case "failed":
|
|
1300
|
+
throw new Error(
|
|
1301
|
+
`contrast-bundler: the swift sources for ${work.entry} cannot be compiled: ${outcome.detail}`
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// src/rolldown-plugin-swift.ts
|
|
1308
|
+
var VIRTUAL_PREFIX2 = "\0rnx-swift-remote:";
|
|
1309
|
+
var projects2 = /* @__PURE__ */ new Map();
|
|
1310
|
+
function projectFor2(packagePath) {
|
|
1311
|
+
const existing = projects2.get(packagePath);
|
|
1312
|
+
if (existing) return existing;
|
|
1313
|
+
const created = { packagePath, address: null };
|
|
1314
|
+
projects2.set(packagePath, created);
|
|
1315
|
+
return created;
|
|
1316
|
+
}
|
|
1317
|
+
function packageFiles(root) {
|
|
1318
|
+
const found = [];
|
|
1319
|
+
const walk = (dir) => {
|
|
1320
|
+
for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
|
|
1321
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === ".build")
|
|
1322
|
+
continue;
|
|
1323
|
+
const full = import_node_path5.default.join(dir, entry.name);
|
|
1324
|
+
if (entry.isDirectory()) {
|
|
1325
|
+
if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
|
|
1326
|
+
walk(full);
|
|
1327
|
+
} else {
|
|
1328
|
+
found.push(import_node_path5.default.relative(root, full).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep));
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
walk(root);
|
|
1333
|
+
return found;
|
|
1334
|
+
}
|
|
1335
|
+
function sourceFiles2(packagePath) {
|
|
1336
|
+
const files = {};
|
|
1337
|
+
for (const relative2 of packageFiles(packagePath)) {
|
|
1338
|
+
if (!relative2.endsWith(".swift")) continue;
|
|
1339
|
+
files[relative2] = import_node_fs4.default.readFileSync(
|
|
1340
|
+
import_node_path5.default.join(packagePath, ...relative2.split("/")),
|
|
1341
|
+
"utf8"
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
return files;
|
|
1345
|
+
}
|
|
1346
|
+
function bundlePack(packagePath) {
|
|
1347
|
+
const relative2 = packageFiles(packagePath);
|
|
1348
|
+
const disk = (file) => import_node_path5.default.join(packagePath, ...file.split("/"));
|
|
1349
|
+
const resources = {};
|
|
1350
|
+
const watched = [];
|
|
1351
|
+
for (const { base, file } of enumerateBundleJson(relative2)) {
|
|
1352
|
+
resources[base] = import_node_fs4.default.readFileSync(disk(file)).toString("base64");
|
|
1353
|
+
watched.push(file);
|
|
1354
|
+
}
|
|
1355
|
+
const images = {};
|
|
1356
|
+
const byRelative = new Map(relative2.map((file) => [file, disk(file)]));
|
|
1357
|
+
for (const { name, file } of enumerateBundleImages(relative2, (candidate) => {
|
|
1358
|
+
const absolute = byRelative.get(candidate);
|
|
1359
|
+
if (!absolute || import_node_path5.default.basename(absolute) !== "Contents.json") return null;
|
|
1360
|
+
try {
|
|
1361
|
+
return import_node_fs4.default.readFileSync(absolute, "utf8");
|
|
1362
|
+
} catch {
|
|
1363
|
+
return null;
|
|
1364
|
+
}
|
|
1365
|
+
})) {
|
|
1366
|
+
const absolute = byRelative.get(file);
|
|
1367
|
+
if (!absolute) continue;
|
|
1368
|
+
images[name] = {
|
|
1369
|
+
file: import_node_path5.default.posix.basename(file),
|
|
1370
|
+
data: import_node_fs4.default.readFileSync(absolute).toString("base64")
|
|
1371
|
+
};
|
|
1372
|
+
watched.push(file);
|
|
1373
|
+
}
|
|
1374
|
+
return { resources, images, watched };
|
|
1375
|
+
}
|
|
1376
|
+
function packageFor2(file, stop) {
|
|
1377
|
+
let dir = import_node_path5.default.dirname(file);
|
|
1378
|
+
for (; ; ) {
|
|
1379
|
+
if (import_node_fs4.default.existsSync(import_node_path5.default.join(dir, "Package.swift"))) return dir;
|
|
1380
|
+
if (stop !== void 0 && dir === stop || import_node_path5.default.dirname(dir) === dir) break;
|
|
1381
|
+
dir = import_node_path5.default.dirname(dir);
|
|
1382
|
+
}
|
|
1383
|
+
throw new Error(
|
|
1384
|
+
`no Package.swift above ${file}; a .swift import needs a swiftpm package like the one in packages/sootsim-swift/example`
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
function swiftRemotePlugin(options) {
|
|
1388
|
+
const submit = httpSwiftCompileSubmit({
|
|
1389
|
+
assetReadToken: options.assetReadToken,
|
|
1390
|
+
endpoint: options.endpoint
|
|
1391
|
+
});
|
|
1392
|
+
if (!submit) {
|
|
1393
|
+
throw new Error(
|
|
1394
|
+
"swiftRemotePlugin needs an assetReadToken: a build without a compile credential cannot compile .swift sources"
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
return {
|
|
1398
|
+
name: "rnx-swift-remote",
|
|
1399
|
+
enforce: "pre",
|
|
1400
|
+
async resolveId(source, importer) {
|
|
1401
|
+
if (source.startsWith(VIRTUAL_PREFIX2)) return source;
|
|
1402
|
+
if (!source.endsWith(".swift")) return null;
|
|
1403
|
+
const resolved = await this.resolve(source, importer, { skipSelf: true });
|
|
1404
|
+
if (!resolved) throw new Error(`cannot resolve swift source ${source}`);
|
|
1405
|
+
return VIRTUAL_PREFIX2 + resolved.id;
|
|
1406
|
+
},
|
|
1407
|
+
async load(id) {
|
|
1408
|
+
if (!id.startsWith(VIRTUAL_PREFIX2)) return null;
|
|
1409
|
+
const file = id.slice(VIRTUAL_PREFIX2.length);
|
|
1410
|
+
if (!import_node_fs4.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
|
|
1411
|
+
const packagePath = options.packagePath ?? packageFor2(file);
|
|
1412
|
+
const project = projectFor2(packagePath);
|
|
1413
|
+
const files = sourceFiles2(packagePath);
|
|
1414
|
+
const entry = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
|
|
1415
|
+
for (const source of Object.keys(files)) {
|
|
1416
|
+
this.addWatchFile(import_node_path5.default.join(packagePath, ...source.split("/")));
|
|
1417
|
+
}
|
|
1418
|
+
const pack = bundlePack(packagePath);
|
|
1419
|
+
for (const watched of pack.watched) {
|
|
1420
|
+
this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split("/")));
|
|
1421
|
+
}
|
|
1422
|
+
try {
|
|
1423
|
+
project.address = await compileSwiftProject(
|
|
1424
|
+
submit,
|
|
1425
|
+
{ files, resources: pack.resources, images: pack.images, entry },
|
|
1426
|
+
options.endpoint
|
|
1427
|
+
);
|
|
1428
|
+
} catch (error) {
|
|
1429
|
+
if (!project.address) throw error;
|
|
1430
|
+
this.warn(error instanceof Error ? error.message : String(error));
|
|
1431
|
+
}
|
|
1432
|
+
const address = project.address;
|
|
1433
|
+
return [
|
|
1434
|
+
`import { createSwiftEntry } from 'rnxsim/swift'`,
|
|
1435
|
+
`export default createSwiftEntry({ url: ${JSON.stringify(address.url)}, hash: ${JSON.stringify(address.hash)}, rootId: ${JSON.stringify(entryRootId(packagePath, file))}, resources: ${JSON.stringify(address.imageUrls ?? {})}, bundleFiles: ${JSON.stringify(pack.resources)} })`,
|
|
1436
|
+
""
|
|
1437
|
+
].join("\n");
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// src/vite-plugin-one.ts
|
|
1443
|
+
function rnxPlugin(options = {}) {
|
|
1444
|
+
const prefix = options.prefix || "/__soot";
|
|
1445
|
+
return {
|
|
1446
|
+
name: "sootsim-one",
|
|
1447
|
+
// use-latest-callback ships a CJS main (module.exports, no default) with
|
|
1448
|
+
// an esm.mjs shim that re-imports it. react-native-bottom-tabs
|
|
1449
|
+
// default-imports it from source served live, so vite hands the browser
|
|
1450
|
+
// the raw CJS file and the import fails with 'does not provide an export
|
|
1451
|
+
// named default' before anything mounts. forcing it through dep
|
|
1452
|
+
// optimization lets esbuild's CJS interop answer the default import, and
|
|
1453
|
+
// every bare import of it (including the nested copy's) resolves to the
|
|
1454
|
+
// one pre-bundled instance.
|
|
1455
|
+
config() {
|
|
1456
|
+
return { optimizeDeps: { include: ["use-latest-callback"] } };
|
|
1457
|
+
},
|
|
1458
|
+
configureServer(server) {
|
|
1459
|
+
if (options.enabled === false) return;
|
|
1460
|
+
const bundleUrl = options.bundleUrl || "/node_modules/one/metro-entry.bundle?platform=ios&dev=true&minify=false";
|
|
1461
|
+
const appName = resolveRnxDesktopAppName(options.open, server.config.root);
|
|
1462
|
+
const replacementToken = (0, import_node_crypto3.randomUUID)();
|
|
1463
|
+
const configuredPort = server.config.server.port || 8081;
|
|
1464
|
+
const listeningPort = () => {
|
|
1465
|
+
const address = server.httpServer?.address();
|
|
1466
|
+
return address && typeof address === "object" ? address.port : configuredPort;
|
|
1467
|
+
};
|
|
1468
|
+
const bundleForPort = (port2) => tagRnxBundleForPlugin(
|
|
1469
|
+
bundleUrl,
|
|
1470
|
+
`http://localhost:${port2}`,
|
|
1471
|
+
replacementToken,
|
|
1472
|
+
server.config.root,
|
|
1473
|
+
appName
|
|
1474
|
+
);
|
|
1475
|
+
const buildShellHtml = (runtimeRoot) => {
|
|
1476
|
+
const html = import_fs2.default.readFileSync(import_path2.default.join(runtimeRoot, "index.html"), "utf8");
|
|
1477
|
+
const lock = readDaemonLockfile();
|
|
1478
|
+
const bridgePort = lock && isDaemonLockfileFresh(lock) && lock.bridgePort > 0 ? lock.bridgePort : DEFAULT_SOOTSIM_BRIDGE_PORT;
|
|
1479
|
+
const inject = `<script>(function(){var u=new URL(location.href);if(!u.searchParams.has('bundle'))u.searchParams.set('bundle',${JSON.stringify(bundleForPort(listeningPort()))});window.__sootsimBridgePort=${bridgePort};history.replaceState(null,'','${prefix}/'+u.search)})()</script>`;
|
|
1480
|
+
return html.replace("</head>", inject + "</head>");
|
|
1481
|
+
};
|
|
1482
|
+
server.middlewares.use((req, res, next) => {
|
|
1483
|
+
const origWriteHead = res.writeHead.bind(res);
|
|
1484
|
+
res.writeHead = function(statusCode, ...args) {
|
|
1485
|
+
const setCookie = res.getHeader("set-cookie");
|
|
1486
|
+
if (setCookie) {
|
|
1487
|
+
const value = Array.isArray(setCookie) ? setCookie.join(", ") : String(setCookie);
|
|
1488
|
+
res.setHeader("x-sootsim-set-cookie", value);
|
|
1489
|
+
res.setHeader("access-control-expose-headers", "x-sootsim-set-cookie");
|
|
1490
|
+
}
|
|
1491
|
+
return origWriteHead(
|
|
1492
|
+
statusCode,
|
|
1493
|
+
...args
|
|
1494
|
+
);
|
|
1495
|
+
};
|
|
1496
|
+
next();
|
|
1497
|
+
});
|
|
1498
|
+
server.middlewares.use((req, res, next) => {
|
|
1499
|
+
if ((req.url || "").split("?")[0] !== "/__server-scan") return next();
|
|
1500
|
+
const scanPort = listeningPort();
|
|
1501
|
+
const pluginBundleUrl = bundleForPort(scanPort);
|
|
1502
|
+
const absoluteBundleUrl = pluginBundleUrl.startsWith("http") ? pluginBundleUrl : `http://localhost:${scanPort}${pluginBundleUrl}`;
|
|
1503
|
+
res.setHeader("content-type", "application/json");
|
|
1504
|
+
res.end(
|
|
1505
|
+
JSON.stringify([
|
|
1506
|
+
{
|
|
1507
|
+
port: scanPort,
|
|
1508
|
+
framework: "one",
|
|
1509
|
+
bundleUrl: absoluteBundleUrl,
|
|
1510
|
+
...appName ? { projectName: appName } : {},
|
|
1511
|
+
lastSeen: Date.now()
|
|
1512
|
+
}
|
|
1513
|
+
])
|
|
1514
|
+
);
|
|
1515
|
+
});
|
|
1516
|
+
server.middlewares.use((req, res, next) => {
|
|
1517
|
+
void handleReplacementModuleRequest(req, res, {
|
|
1518
|
+
projectRoot: server.config.root,
|
|
1519
|
+
requestToken: replacementToken,
|
|
1520
|
+
requireLoopbackOrigin: true
|
|
1521
|
+
}).then((handled) => {
|
|
1522
|
+
if (!handled) next();
|
|
1523
|
+
});
|
|
1524
|
+
});
|
|
1525
|
+
server.middlewares.use((req, res, next) => {
|
|
1526
|
+
const url = req.url || "";
|
|
1527
|
+
const pathname = url.split("?")[0];
|
|
1528
|
+
const runtimeRoot = resolveRuntimeRootForRequestHost(req.headers.host);
|
|
1529
|
+
if (!runtimeRoot) {
|
|
1530
|
+
if (pathname === prefix || pathname === prefix + "/" || pathname.startsWith(prefix + "/")) {
|
|
1531
|
+
res.statusCode = 500;
|
|
1532
|
+
res.end(SOOTSIM_RUNTIME_MISSING_MESSAGE);
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
next();
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
if (isRootRuntimeAssetPath(pathname)) {
|
|
1539
|
+
const fullPath = resolveRuntimeFilePath(runtimeRoot, pathname);
|
|
1540
|
+
if (fullPath) {
|
|
1541
|
+
serveRuntimeFile(req, res, fullPath);
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
if (pathname === prefix || pathname === prefix + "/") {
|
|
1546
|
+
res.setHeader("content-type", "text/html");
|
|
1547
|
+
res.end(buildShellHtml(runtimeRoot));
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
if (pathname.startsWith(prefix + "/")) {
|
|
1551
|
+
const fullPath = resolveRuntimeFilePath(
|
|
1552
|
+
runtimeRoot,
|
|
1553
|
+
pathname.slice(prefix.length)
|
|
1554
|
+
);
|
|
1555
|
+
if (fullPath) {
|
|
1556
|
+
serveRuntimeFile(req, res, fullPath);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const ext = import_path2.default.extname(pathname);
|
|
1560
|
+
if (!ext) {
|
|
1561
|
+
res.setHeader("content-type", "text/html");
|
|
1562
|
+
res.end(buildShellHtml(runtimeRoot));
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
next();
|
|
1567
|
+
});
|
|
1568
|
+
const port = configuredPort;
|
|
1569
|
+
const sootsimUrl = `http://localhost:${port}${prefix}/`;
|
|
1570
|
+
console.log(`[rnx] serving at ${sootsimUrl}`);
|
|
1571
|
+
if (appName && !process.env.CI && !process.env.RNX_NO_OPEN) {
|
|
1572
|
+
server.httpServer?.once("listening", () => {
|
|
1573
|
+
const openPort = listeningPort();
|
|
1574
|
+
launchRnxDesktop(
|
|
1575
|
+
resolveRnxDesktopLaunchTarget(openPort, bundleForPort(openPort)),
|
|
1576
|
+
appName
|
|
1577
|
+
);
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
var sootsimPlugin = rnxPlugin;
|
|
1584
|
+
var vite_plugin_one_default = rnxPlugin;
|
|
1585
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1586
|
+
0 && (module.exports = {
|
|
1587
|
+
rnxPlugin,
|
|
1588
|
+
sootsimPlugin,
|
|
1589
|
+
swiftPlugin,
|
|
1590
|
+
swiftRemotePlugin
|
|
1591
|
+
});
|