rnxsim 0.0.0 → 0.1.313
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 +21 -0
- package/README.md +512 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +39 -0
- package/detox/expectations.ts +537 -0
- package/detox/gestures.ts +480 -0
- package/detox/index.ts +2118 -0
- package/detox/jest-preset.cjs +51 -0
- package/detox/jest-setup-after-env.cjs +18 -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.ts +43 -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-cli/bin.js +15 -0
- package/dist-cli/chunks/agent-XZ2KTPCU.js +48 -0
- package/dist-cli/chunks/agent-wrapper-JJYYW2WH.js +17 -0
- package/dist-cli/chunks/app-fonts-IXRNQG6B.js +4 -0
- package/dist-cli/chunks/assert-54T5SK5F.js +49 -0
- package/dist-cli/chunks/auth-FI5UDI45.js +12 -0
- package/dist-cli/chunks/beta-JV6UKADW.js +4 -0
- package/dist-cli/chunks/camera-VL73YIKP.js +22 -0
- package/dist-cli/chunks/chunk-2D2UPBBR.js +4 -0
- package/dist-cli/chunks/chunk-2YR5BGA5.js +5 -0
- package/dist-cli/chunks/chunk-3NV2NCNX.js +4 -0
- package/dist-cli/chunks/chunk-46EUUFJ5.js +4 -0
- package/dist-cli/chunks/chunk-4NPPOV2N.js +5 -0
- package/dist-cli/chunks/chunk-5DHC6KHQ.js +7 -0
- package/dist-cli/chunks/chunk-5TEF3ET3.js +74 -0
- package/dist-cli/chunks/chunk-5TPRP5QT.js +4 -0
- package/dist-cli/chunks/chunk-5YJCOWCH.js +4 -0
- package/dist-cli/chunks/chunk-7GN3LVWB.js +4 -0
- package/dist-cli/chunks/chunk-BBULZ7CG.js +568 -0
- package/dist-cli/chunks/chunk-BTWORNNG.js +71 -0
- package/dist-cli/chunks/chunk-D4FFVGI5.js +5 -0
- package/dist-cli/chunks/chunk-DCEMHR2Y.js +4 -0
- package/dist-cli/chunks/chunk-DZS6WPUI.js +4 -0
- package/dist-cli/chunks/chunk-F5ZRSS3C.js +3 -0
- package/dist-cli/chunks/chunk-FSUYIVJ6.js +9 -0
- package/dist-cli/chunks/chunk-G2WW6L2C.js +23 -0
- package/dist-cli/chunks/chunk-GASE6UBA.js +4 -0
- package/dist-cli/chunks/chunk-GGRX24GF.js +142 -0
- package/dist-cli/chunks/chunk-HI5TFJWN.js +5 -0
- package/dist-cli/chunks/chunk-IJ5CAZZC.js +5 -0
- package/dist-cli/chunks/chunk-IJO63TDP.js +4 -0
- package/dist-cli/chunks/chunk-KTHV3RUS.js +26 -0
- package/dist-cli/chunks/chunk-LF2ZVT7O.js +6 -0
- package/dist-cli/chunks/chunk-MJRLLB4R.js +38 -0
- package/dist-cli/chunks/chunk-NFK7T35W.js +4 -0
- package/dist-cli/chunks/chunk-NMF2ZMZQ.js +15 -0
- package/dist-cli/chunks/chunk-OHAZNXLK.js +4 -0
- package/dist-cli/chunks/chunk-OVFJFXUD.js +101 -0
- package/dist-cli/chunks/chunk-OZSSI4WN.js +28 -0
- package/dist-cli/chunks/chunk-QKDWYITG.js +25 -0
- package/dist-cli/chunks/chunk-RSZWCKNT.js +30 -0
- package/dist-cli/chunks/chunk-RTN5C5RL.js +4 -0
- package/dist-cli/chunks/chunk-TIVZIMMW.js +4 -0
- package/dist-cli/chunks/chunk-TZFFR3SD.js +7 -0
- package/dist-cli/chunks/chunk-UC6U3MML.js +5 -0
- package/dist-cli/chunks/chunk-VFCMSYZK.js +6 -0
- package/dist-cli/chunks/chunk-VNQEB4L7.js +5 -0
- package/dist-cli/chunks/chunk-VZXWHRUZ.js +983 -0
- package/dist-cli/chunks/chunk-W6K4EFPH.js +107 -0
- package/dist-cli/chunks/chunk-WEXDAC74.js +4 -0
- package/dist-cli/chunks/chunk-WF3T4SVI.js +14 -0
- package/dist-cli/chunks/chunk-WINYQ44O.js +4 -0
- package/dist-cli/chunks/chunk-WMIIKMGK.js +10 -0
- package/dist-cli/chunks/chunk-WUSWBCWA.js +507 -0
- package/dist-cli/chunks/chunk-WWZIXIRD.js +4 -0
- package/dist-cli/chunks/chunk-XEVZYVIW.js +766 -0
- package/dist-cli/chunks/chunk-YDGQTMQL.js +4 -0
- package/dist-cli/chunks/chunk-YIFT42WN.js +4 -0
- package/dist-cli/chunks/chunk-ZMJD5GEC.js +125 -0
- package/dist-cli/chunks/cleanup-P27PA6JI.js +25 -0
- package/dist-cli/chunks/cli-version-WWLPBDQ7.js +4 -0
- package/dist-cli/chunks/compat-ZD65FED3.js +20 -0
- package/dist-cli/chunks/config-XMJRNM2A.js +13 -0
- package/dist-cli/chunks/control-KMIQT3QP.js +4 -0
- package/dist-cli/chunks/daemon-G2ME7NLB.js +4 -0
- package/dist-cli/chunks/debug-PT4HOP7N.js +200 -0
- package/dist-cli/chunks/desktop-S3FG72AK.js +31 -0
- package/dist-cli/chunks/detox-B3D4IFCN.js +52 -0
- package/dist-cli/chunks/device-XBNDSB2R.js +18 -0
- package/dist-cli/chunks/diagnose-HMQXJE5N.js +43 -0
- package/dist-cli/chunks/disk-cleanup-BLCZ5BSZ.js +4 -0
- package/dist-cli/chunks/drivers-LDECZGP2.js +4 -0
- package/dist-cli/chunks/film-BJGTBYZB.js +74 -0
- package/dist-cli/chunks/flow-UEQNVTU7.js +4 -0
- package/dist-cli/chunks/help-T5FYSVGB.js +4 -0
- package/dist-cli/chunks/hidden-runtime-alias-ANOYADHM.js +4 -0
- package/dist-cli/chunks/home-paths-GT3LFNOR.js +4 -0
- package/dist-cli/chunks/inspect-ZA6XF5LD.js +4 -0
- package/dist-cli/chunks/install-desktop-TIMUDHPL.js +4 -0
- package/dist-cli/chunks/login-FJ737MWG.js +28 -0
- package/dist-cli/chunks/logout-ZCNMMHMY.js +4 -0
- package/dist-cli/chunks/maestro-SZTNKLDF.js +104 -0
- package/dist-cli/chunks/maestro-generate-DCFAIZ4H.js +36 -0
- package/dist-cli/chunks/mode-GRMQCRXR.js +26 -0
- package/dist-cli/chunks/optional-demo-registry-W36EWFFB.js +4 -0
- package/dist-cli/chunks/perf-QYBAAUZG.js +48 -0
- package/dist-cli/chunks/permissions-3QCQ6VF4.js +13 -0
- package/dist-cli/chunks/record-QPPC2S4E.js +72 -0
- package/dist-cli/chunks/report-issue-7NMFP4HK.js +29 -0
- package/dist-cli/chunks/runtime-XOAXMSTU.js +4 -0
- package/dist-cli/chunks/screenshot-command-67AECJFB.js +159 -0
- package/dist-cli/chunks/screenshot-layers-ASWBYPJL.js +20 -0
- package/dist-cli/chunks/screenshots-capture-PXA3HFQK.js +42 -0
- package/dist-cli/chunks/serve-BI2NBAXG.js +44 -0
- package/dist-cli/chunks/setup-7DWPMRSB.js +32 -0
- package/dist-cli/chunks/skills-S3Y22TUA.js +50 -0
- package/dist-cli/chunks/storage-XUIMJWAJ.js +37 -0
- package/dist-cli/chunks/store-JTHEJLAZ.js +4 -0
- package/dist-cli/chunks/telemetry-ZYJGD2DB.js +4 -0
- package/dist-cli/chunks/timeline-TMPLQPSP.js +24 -0
- package/dist-cli/chunks/upgrade-7HDSIM7K.js +7 -0
- package/dist-cli/chunks/upload-GMSZPWM6.js +4 -0
- package/dist-cli/chunks/version-HOCHZ37L.js +6 -0
- package/dist-cli/chunks/web-DG3WBYD3.js +4 -0
- package/dist-cli/chunks/what-happened-XFVUTZR7.js +25 -0
- package/dist-lib/agent-daemon-client.cjs +468 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +725 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +195 -0
- package/dist-lib/backend-origin.cjs +70 -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/cli-constants.cjs +32 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +2863 -0
- package/dist-lib/detox/jest-preset.cjs +51 -0
- package/dist-lib/dev-bundle-resolution.cjs +223 -0
- package/dist-lib/home-paths.cjs +421 -0
- package/dist-lib/host/bridge-host.cjs +5600 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +373 -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/websocket-proxy.cjs +214 -0
- package/dist-lib/index.cjs +513 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/menu.cjs +620 -0
- package/dist-lib/menu.mjs +581 -0
- package/dist-lib/metro.cjs +297 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1034 -0
- package/dist-lib/sdk.cjs +2333 -0
- package/dist-lib/sdk.mjs +2240 -0
- package/dist-lib/skills.cjs +23922 -0
- package/dist-lib/vite.cjs +358 -0
- package/package.json +189 -18
- package/scripts/dev-server-scanner.ts +941 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/scripts/postinstall.cjs +3 -0
- package/scripts/preuninstall.cjs +118 -0
- package/skills/contrast/SKILL.md +171 -0
- package/skills/rnx-debug/SKILL.md +425 -0
- package/skills/rnx-perf/SKILL.md +295 -0
- package/skills/rnx-setup/SKILL.md +160 -0
- package/skills/rnx-test/SKILL.md +292 -0
- package/skills/rnx-visual/SKILL.md +122 -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 +573 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +204 -0
- package/src/backend-origin.ts +49 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +171 -0
- package/src/browser-cache.ts +16 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +32 -0
- package/src/codex-client.ts +215 -0
- package/src/config.ts +134 -0
- package/src/dev-bundle-resolution.ts +236 -0
- package/src/disk-cleanup.ts +466 -0
- package/src/home-paths.ts +552 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2626 -0
- package/src/host/fetch-proxy-handler.ts +397 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/websocket-proxy.ts +210 -0
- package/src/index.ts +12 -705
- package/src/jump-to-source-babel.ts +135 -0
- package/src/menu.ts +734 -0
- package/src/metro-plugin.ts +139 -0
- package/src/native-dev-bundle-url.ts +96 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +92 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/render-mode.ts +27 -0
- package/src/runtime-assets.ts +228 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +454 -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 +7 -0
- package/src/settings-bundle.ts +418 -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/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +190 -0
- package/src/vite-plugin.ts +1750 -0
- package/src/worklets-babel.ts +134 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/*! rnx v0.1.313 | (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/host/fetch-proxy-handler.ts
|
|
33
|
+
var fetch_proxy_handler_exports = {};
|
|
34
|
+
__export(fetch_proxy_handler_exports, {
|
|
35
|
+
buildAppApiProxyHeaders: () => buildAppApiProxyHeaders,
|
|
36
|
+
buildFetchProxyHeaders: () => buildFetchProxyHeaders,
|
|
37
|
+
handleAppApiRequest: () => handleAppApiRequest,
|
|
38
|
+
handleFetchProxyRequest: () => handleFetchProxyRequest,
|
|
39
|
+
isAppApiRequestUrl: () => isAppApiRequestUrl,
|
|
40
|
+
isFetchProxyRequestUrl: () => isFetchProxyRequestUrl
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(fetch_proxy_handler_exports);
|
|
43
|
+
var import_http = __toESM(require("http"), 1);
|
|
44
|
+
var import_https = __toESM(require("https"), 1);
|
|
45
|
+
var import_promises = require("stream/promises");
|
|
46
|
+
|
|
47
|
+
// src/backend-origin.ts
|
|
48
|
+
function normalizeHostname(hostname) {
|
|
49
|
+
return hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
50
|
+
}
|
|
51
|
+
function isLoopbackHost(hostname) {
|
|
52
|
+
const normalized = normalizeHostname(hostname);
|
|
53
|
+
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/host/fetch-proxy-overrides.ts
|
|
57
|
+
var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
58
|
+
var HOST_HEADER_OVERRIDES = [
|
|
59
|
+
{
|
|
60
|
+
hostSuffix: "uniswap.org",
|
|
61
|
+
headers: {
|
|
62
|
+
origin: "https://app.uniswap.org",
|
|
63
|
+
referer: "https://app.uniswap.org/"
|
|
64
|
+
},
|
|
65
|
+
// uniswap's native gateway accepts the app origin, but forcing a desktop
|
|
66
|
+
// browser ua on native api posts returns a bogus 414/tcp-stack response.
|
|
67
|
+
suppressBrowserUserAgent: true
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
function headerOverridesFor(hostname) {
|
|
71
|
+
const host = hostname.toLowerCase();
|
|
72
|
+
for (const override of HOST_HEADER_OVERRIDES) {
|
|
73
|
+
if (host === override.hostSuffix || host.endsWith(`.${override.hostSuffix}`)) {
|
|
74
|
+
return override;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
function getFetchProxyTargetHeaders(targetUrl) {
|
|
80
|
+
const hostOverrides = headerOverridesFor(targetUrl.hostname);
|
|
81
|
+
return {
|
|
82
|
+
"accept-encoding": "identity",
|
|
83
|
+
...hostOverrides?.suppressBrowserUserAgent ? {} : { "user-agent": FETCH_PROXY_BROWSER_USER_AGENT },
|
|
84
|
+
...hostOverrides?.headers
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/host/fetch-proxy-handler.ts
|
|
89
|
+
var STRIP_FETCH_PROXY_HEADERS = /* @__PURE__ */ new Set([
|
|
90
|
+
"host",
|
|
91
|
+
"origin",
|
|
92
|
+
"referer",
|
|
93
|
+
"user-agent",
|
|
94
|
+
"accept-encoding",
|
|
95
|
+
"cookie",
|
|
96
|
+
"connection",
|
|
97
|
+
"keep-alive",
|
|
98
|
+
"transfer-encoding",
|
|
99
|
+
"upgrade",
|
|
100
|
+
"content-length",
|
|
101
|
+
"sec-fetch-site",
|
|
102
|
+
"sec-fetch-mode",
|
|
103
|
+
"sec-fetch-dest",
|
|
104
|
+
"sec-ch-ua",
|
|
105
|
+
"sec-ch-ua-mobile",
|
|
106
|
+
"sec-ch-ua-platform"
|
|
107
|
+
]);
|
|
108
|
+
var FETCH_PROXY_CORS_HEADERS = {
|
|
109
|
+
"access-control-allow-origin": "*",
|
|
110
|
+
"access-control-allow-methods": "GET,POST,PUT,DELETE,PATCH,OPTIONS",
|
|
111
|
+
"access-control-allow-headers": "*",
|
|
112
|
+
"access-control-expose-headers": "*",
|
|
113
|
+
"access-control-max-age": "3600"
|
|
114
|
+
};
|
|
115
|
+
var APP_API_HEADER_REWRITES = /* @__PURE__ */ new Set([
|
|
116
|
+
"host",
|
|
117
|
+
"origin",
|
|
118
|
+
"referer",
|
|
119
|
+
"sec-fetch-site",
|
|
120
|
+
"sec-fetch-mode",
|
|
121
|
+
"sec-fetch-dest"
|
|
122
|
+
]);
|
|
123
|
+
function loopbackTlsOptions(targetUrl) {
|
|
124
|
+
if (targetUrl.protocol !== "https:") return {};
|
|
125
|
+
return isLoopbackHost(targetUrl.hostname) ? { rejectUnauthorized: false } : {};
|
|
126
|
+
}
|
|
127
|
+
function applyFetchProxyCors(res) {
|
|
128
|
+
for (const [key, value] of Object.entries(FETCH_PROXY_CORS_HEADERS)) {
|
|
129
|
+
res.setHeader(key, value);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function formatFetchProxyError(targetUrl, err) {
|
|
133
|
+
const details = [];
|
|
134
|
+
const error = err;
|
|
135
|
+
if (error?.code) details.push(error.code);
|
|
136
|
+
if (error?.message) details.push(error.message);
|
|
137
|
+
if (error?.cause?.code) details.push(error.cause.code);
|
|
138
|
+
if (error?.cause?.message) details.push(error.cause.message);
|
|
139
|
+
const uniqueDetails = [...new Set(details.filter(Boolean))];
|
|
140
|
+
const message = uniqueDetails.join(" | ") || String(err);
|
|
141
|
+
if (targetUrl.includes("stored-in-.env.local")) {
|
|
142
|
+
return `${message} | upstream url still contains placeholder env values`;
|
|
143
|
+
}
|
|
144
|
+
return message;
|
|
145
|
+
}
|
|
146
|
+
function buildFetchProxyHeaders(reqHeaders, targetUrl) {
|
|
147
|
+
const headers = {};
|
|
148
|
+
for (const [key, value] of Object.entries(reqHeaders)) {
|
|
149
|
+
if (!value) continue;
|
|
150
|
+
if (STRIP_FETCH_PROXY_HEADERS.has(key.toLowerCase())) continue;
|
|
151
|
+
headers[key] = Array.isArray(value) ? value.join(", ") : value;
|
|
152
|
+
}
|
|
153
|
+
Object.assign(
|
|
154
|
+
headers,
|
|
155
|
+
targetUrl ? getFetchProxyTargetHeaders(targetUrl) : { "user-agent": FETCH_PROXY_BROWSER_USER_AGENT }
|
|
156
|
+
);
|
|
157
|
+
return headers;
|
|
158
|
+
}
|
|
159
|
+
function buildAppApiProxyHeaders(reqHeaders, targetUrl) {
|
|
160
|
+
const headers = {};
|
|
161
|
+
for (const [key, value] of Object.entries(reqHeaders)) {
|
|
162
|
+
if (!value) continue;
|
|
163
|
+
if (APP_API_HEADER_REWRITES.has(key.toLowerCase())) continue;
|
|
164
|
+
headers[key] = value;
|
|
165
|
+
}
|
|
166
|
+
headers.host = targetUrl.host;
|
|
167
|
+
headers.origin = targetUrl.origin;
|
|
168
|
+
headers.referer = `${targetUrl.origin}/`;
|
|
169
|
+
return headers;
|
|
170
|
+
}
|
|
171
|
+
function isFetchProxyRequestUrl(rawUrl) {
|
|
172
|
+
return rawUrl?.startsWith("/__fetch-proxy?") || rawUrl?.startsWith("/__proxy?") || false;
|
|
173
|
+
}
|
|
174
|
+
function isAppApiRequestUrl(rawUrl) {
|
|
175
|
+
if (!rawUrl) return false;
|
|
176
|
+
if (rawUrl.startsWith("/__app-api?")) return true;
|
|
177
|
+
if (rawUrl.startsWith("/__app-api/")) return true;
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
async function handleFetchProxyRequest(req, res) {
|
|
181
|
+
if (req.method === "OPTIONS") {
|
|
182
|
+
applyFetchProxyCors(res);
|
|
183
|
+
res.writeHead(204);
|
|
184
|
+
res.end();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const params = new URLSearchParams((req.url || "").split("?")[1] || "");
|
|
188
|
+
const targetUrl = params.get("url");
|
|
189
|
+
if (!targetUrl) {
|
|
190
|
+
applyFetchProxyCors(res);
|
|
191
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
192
|
+
res.end("missing url param");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
let upstreamUrl;
|
|
196
|
+
try {
|
|
197
|
+
upstreamUrl = new URL(targetUrl);
|
|
198
|
+
} catch {
|
|
199
|
+
applyFetchProxyCors(res);
|
|
200
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
201
|
+
res.end("invalid url param");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
let body;
|
|
205
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
206
|
+
const chunks = [];
|
|
207
|
+
for await (const chunk of req) {
|
|
208
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
209
|
+
}
|
|
210
|
+
if (chunks.length > 0) {
|
|
211
|
+
body = Buffer.concat(chunks);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const originalMethod = req.method || "GET";
|
|
215
|
+
const proxy = async (targetUrl2, method, nextBody, redirects) => {
|
|
216
|
+
const transport = targetUrl2.protocol === "https:" ? import_https.default : import_http.default;
|
|
217
|
+
const targetHeaders = buildFetchProxyHeaders(req.headers, targetUrl2);
|
|
218
|
+
if (targetUrl2.origin !== upstreamUrl.origin) {
|
|
219
|
+
delete targetHeaders.authorization;
|
|
220
|
+
delete targetHeaders["proxy-authorization"];
|
|
221
|
+
}
|
|
222
|
+
if (method === "GET" || method === "HEAD") {
|
|
223
|
+
delete targetHeaders["content-length"];
|
|
224
|
+
delete targetHeaders["content-type"];
|
|
225
|
+
} else if (nextBody) {
|
|
226
|
+
targetHeaders["content-length"] = String(nextBody.byteLength);
|
|
227
|
+
}
|
|
228
|
+
const proxyRes = await new Promise((resolve, reject) => {
|
|
229
|
+
const proxyReq = transport.request(
|
|
230
|
+
{
|
|
231
|
+
hostname: targetUrl2.hostname,
|
|
232
|
+
port: targetUrl2.port || (targetUrl2.protocol === "https:" ? 443 : 80),
|
|
233
|
+
path: targetUrl2.pathname + targetUrl2.search,
|
|
234
|
+
method,
|
|
235
|
+
headers: targetHeaders,
|
|
236
|
+
...loopbackTlsOptions(targetUrl2)
|
|
237
|
+
},
|
|
238
|
+
resolve
|
|
239
|
+
);
|
|
240
|
+
const abort = () => proxyReq.destroy(new Error("fetch proxy client disconnected"));
|
|
241
|
+
req.once("aborted", abort);
|
|
242
|
+
proxyReq.once("close", () => req.off("aborted", abort));
|
|
243
|
+
proxyReq.once("error", reject);
|
|
244
|
+
if (nextBody === void 0) proxyReq.end();
|
|
245
|
+
else proxyReq.end(nextBody);
|
|
246
|
+
});
|
|
247
|
+
const status = proxyRes.statusCode ?? 502;
|
|
248
|
+
const location = proxyRes.headers.location;
|
|
249
|
+
if (location && (status === 301 || status === 302 || status === 303 || status === 307 || status === 308)) {
|
|
250
|
+
if (redirects >= 10) {
|
|
251
|
+
proxyRes.destroy();
|
|
252
|
+
throw new Error("too many redirects");
|
|
253
|
+
}
|
|
254
|
+
const switchToGet = (status === 301 || status === 302) && method === "POST" || status === 303 && method !== "GET" && method !== "HEAD";
|
|
255
|
+
const redirectUrl = new URL(location, targetUrl2);
|
|
256
|
+
proxyRes.resume();
|
|
257
|
+
await (0, import_promises.finished)(proxyRes);
|
|
258
|
+
await proxy(
|
|
259
|
+
redirectUrl,
|
|
260
|
+
switchToGet ? "GET" : method,
|
|
261
|
+
switchToGet ? void 0 : nextBody,
|
|
262
|
+
redirects + 1
|
|
263
|
+
);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
267
|
+
const lowerKey = key.toLowerCase();
|
|
268
|
+
if (value === void 0 || lowerKey === "set-cookie" || lowerKey === "connection" || lowerKey === "keep-alive" || lowerKey === "proxy-authenticate" || lowerKey === "proxy-authorization" || lowerKey === "te" || lowerKey === "trailer" || lowerKey === "transfer-encoding" || lowerKey === "upgrade" || lowerKey.startsWith("access-control-")) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
res.setHeader(key, value);
|
|
272
|
+
}
|
|
273
|
+
applyFetchProxyCors(res);
|
|
274
|
+
const setCookie = proxyRes.headers["set-cookie"] ?? [];
|
|
275
|
+
if (setCookie.length > 0) {
|
|
276
|
+
res.setHeader(
|
|
277
|
+
"x-sootsim-set-cookie",
|
|
278
|
+
(Array.isArray(setCookie) ? setCookie : [setCookie]).join(", ")
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
res.statusCode = status;
|
|
282
|
+
await (0, import_promises.pipeline)(proxyRes, res);
|
|
283
|
+
};
|
|
284
|
+
try {
|
|
285
|
+
await proxy(upstreamUrl, originalMethod, body, 0);
|
|
286
|
+
} catch (err) {
|
|
287
|
+
if (res.headersSent) {
|
|
288
|
+
res.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
applyFetchProxyCors(res);
|
|
292
|
+
res.writeHead(502, { "Content-Type": "text/plain" });
|
|
293
|
+
res.end(`fetch proxy error: ${formatFetchProxyError(upstreamUrl.href, err)}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function handleAppApiRequest(req, res) {
|
|
297
|
+
const reqUrl = req.url || "";
|
|
298
|
+
let targetPath = "";
|
|
299
|
+
let targetOrigin = "";
|
|
300
|
+
if (reqUrl.startsWith("/__app-api?")) {
|
|
301
|
+
const parsed = new URL(reqUrl, "http://sootsim.local");
|
|
302
|
+
targetPath = parsed.searchParams.get("path") || "";
|
|
303
|
+
targetOrigin = parsed.searchParams.get("origin")?.trim() || "";
|
|
304
|
+
} else if (reqUrl.startsWith("/__app-api/")) {
|
|
305
|
+
targetPath = reqUrl.slice("/__app-api".length);
|
|
306
|
+
} else {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
if (!targetOrigin) {
|
|
310
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
311
|
+
res.end("app-api: missing origin query param");
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
if (req.method === "OPTIONS") {
|
|
315
|
+
res.writeHead(204, {
|
|
316
|
+
"Access-Control-Allow-Origin": req.headers.origin || "*",
|
|
317
|
+
"Access-Control-Allow-Methods": "GET,POST,PUT,PATCH,DELETE,OPTIONS",
|
|
318
|
+
"Access-Control-Allow-Headers": req.headers["access-control-request-headers"] || "*",
|
|
319
|
+
"Access-Control-Allow-Credentials": "true",
|
|
320
|
+
"Access-Control-Max-Age": "86400"
|
|
321
|
+
});
|
|
322
|
+
res.end();
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
let targetUrl;
|
|
326
|
+
try {
|
|
327
|
+
targetUrl = new URL(targetPath, targetOrigin);
|
|
328
|
+
} catch {
|
|
329
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
330
|
+
res.end("app-api: invalid origin or path");
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
const transport = targetUrl.protocol === "https:" ? import_https.default : import_http.default;
|
|
334
|
+
const fwdHeaders = buildAppApiProxyHeaders(req.headers, targetUrl);
|
|
335
|
+
const proxyReq = transport.request(
|
|
336
|
+
{
|
|
337
|
+
hostname: targetUrl.hostname,
|
|
338
|
+
port: targetUrl.port || (targetUrl.protocol === "https:" ? 443 : 80),
|
|
339
|
+
path: targetUrl.pathname + targetUrl.search,
|
|
340
|
+
method: req.method,
|
|
341
|
+
headers: fwdHeaders,
|
|
342
|
+
...loopbackTlsOptions(targetUrl)
|
|
343
|
+
},
|
|
344
|
+
(proxyRes) => {
|
|
345
|
+
const exposedHeaders = Object.keys(proxyRes.headers).filter((name) => {
|
|
346
|
+
const lower = name.toLowerCase();
|
|
347
|
+
return !lower.startsWith("access-control-") && lower !== "set-cookie";
|
|
348
|
+
}).join(", ");
|
|
349
|
+
res.writeHead(proxyRes.statusCode ?? 502, {
|
|
350
|
+
...proxyRes.headers,
|
|
351
|
+
"access-control-allow-origin": req.headers.origin || "*",
|
|
352
|
+
"access-control-allow-credentials": "true",
|
|
353
|
+
"access-control-expose-headers": exposedHeaders
|
|
354
|
+
});
|
|
355
|
+
proxyRes.pipe(res);
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
proxyReq.on("error", (err) => {
|
|
359
|
+
res.statusCode = 502;
|
|
360
|
+
res.end(`app proxy error: ${err.message}`);
|
|
361
|
+
});
|
|
362
|
+
req.pipe(proxyReq);
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
366
|
+
0 && (module.exports = {
|
|
367
|
+
buildAppApiProxyHeaders,
|
|
368
|
+
buildFetchProxyHeaders,
|
|
369
|
+
handleAppApiRequest,
|
|
370
|
+
handleFetchProxyRequest,
|
|
371
|
+
isAppApiRequestUrl,
|
|
372
|
+
isFetchProxyRequestUrl
|
|
373
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*! rnx v0.1.313 | (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 __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/host/fetch-proxy-overrides.ts
|
|
23
|
+
var fetch_proxy_overrides_exports = {};
|
|
24
|
+
__export(fetch_proxy_overrides_exports, {
|
|
25
|
+
FETCH_PROXY_BROWSER_USER_AGENT: () => FETCH_PROXY_BROWSER_USER_AGENT,
|
|
26
|
+
getFetchProxyTargetHeaders: () => getFetchProxyTargetHeaders
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(fetch_proxy_overrides_exports);
|
|
29
|
+
var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
30
|
+
var HOST_HEADER_OVERRIDES = [
|
|
31
|
+
{
|
|
32
|
+
hostSuffix: "uniswap.org",
|
|
33
|
+
headers: {
|
|
34
|
+
origin: "https://app.uniswap.org",
|
|
35
|
+
referer: "https://app.uniswap.org/"
|
|
36
|
+
},
|
|
37
|
+
// uniswap's native gateway accepts the app origin, but forcing a desktop
|
|
38
|
+
// browser ua on native api posts returns a bogus 414/tcp-stack response.
|
|
39
|
+
suppressBrowserUserAgent: true
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
function headerOverridesFor(hostname) {
|
|
43
|
+
const host = hostname.toLowerCase();
|
|
44
|
+
for (const override of HOST_HEADER_OVERRIDES) {
|
|
45
|
+
if (host === override.hostSuffix || host.endsWith(`.${override.hostSuffix}`)) {
|
|
46
|
+
return override;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
function getFetchProxyTargetHeaders(targetUrl) {
|
|
52
|
+
const hostOverrides = headerOverridesFor(targetUrl.hostname);
|
|
53
|
+
return {
|
|
54
|
+
"accept-encoding": "identity",
|
|
55
|
+
...hostOverrides?.suppressBrowserUserAgent ? {} : { "user-agent": FETCH_PROXY_BROWSER_USER_AGENT },
|
|
56
|
+
...hostOverrides?.headers
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
60
|
+
0 && (module.exports = {
|
|
61
|
+
FETCH_PROXY_BROWSER_USER_AGENT,
|
|
62
|
+
getFetchProxyTargetHeaders
|
|
63
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*! rnx v0.1.313 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
|
|
3
|
+
// src/host/fetch-proxy-overrides.ts
|
|
4
|
+
var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
5
|
+
var HOST_HEADER_OVERRIDES = [
|
|
6
|
+
{
|
|
7
|
+
hostSuffix: "uniswap.org",
|
|
8
|
+
headers: {
|
|
9
|
+
origin: "https://app.uniswap.org",
|
|
10
|
+
referer: "https://app.uniswap.org/"
|
|
11
|
+
},
|
|
12
|
+
// uniswap's native gateway accepts the app origin, but forcing a desktop
|
|
13
|
+
// browser ua on native api posts returns a bogus 414/tcp-stack response.
|
|
14
|
+
suppressBrowserUserAgent: true
|
|
15
|
+
}
|
|
16
|
+
];
|
|
17
|
+
function headerOverridesFor(hostname) {
|
|
18
|
+
const host = hostname.toLowerCase();
|
|
19
|
+
for (const override of HOST_HEADER_OVERRIDES) {
|
|
20
|
+
if (host === override.hostSuffix || host.endsWith(`.${override.hostSuffix}`)) {
|
|
21
|
+
return override;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
function getFetchProxyTargetHeaders(targetUrl) {
|
|
27
|
+
const hostOverrides = headerOverridesFor(targetUrl.hostname);
|
|
28
|
+
return {
|
|
29
|
+
"accept-encoding": "identity",
|
|
30
|
+
...hostOverrides?.suppressBrowserUserAgent ? {} : { "user-agent": FETCH_PROXY_BROWSER_USER_AGENT },
|
|
31
|
+
...hostOverrides?.headers
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
FETCH_PROXY_BROWSER_USER_AGENT,
|
|
36
|
+
getFetchProxyTargetHeaders
|
|
37
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/*! rnx v0.1.313 | (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 __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/host/websocket-proxy.ts
|
|
23
|
+
var websocket_proxy_exports = {};
|
|
24
|
+
__export(websocket_proxy_exports, {
|
|
25
|
+
WEBSOCKET_PROXY_PATH: () => WEBSOCKET_PROXY_PATH,
|
|
26
|
+
handleWebSocketProxyUpgrade: () => handleWebSocketProxyUpgrade,
|
|
27
|
+
isWebSocketProxyRequestUrl: () => isWebSocketProxyRequestUrl
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(websocket_proxy_exports);
|
|
30
|
+
var import_ws = require("ws");
|
|
31
|
+
var WEBSOCKET_PROXY_PATH = "/__websocket-proxy";
|
|
32
|
+
var STRIP_UPSTREAM_HEADERS = /* @__PURE__ */ new Set([
|
|
33
|
+
"host",
|
|
34
|
+
"connection",
|
|
35
|
+
"upgrade",
|
|
36
|
+
"transfer-encoding",
|
|
37
|
+
"content-length",
|
|
38
|
+
"sec-websocket-accept",
|
|
39
|
+
"sec-websocket-extensions",
|
|
40
|
+
"sec-websocket-key",
|
|
41
|
+
"sec-websocket-protocol",
|
|
42
|
+
"sec-websocket-version"
|
|
43
|
+
]);
|
|
44
|
+
function rejectUpgrade(socket, status, message) {
|
|
45
|
+
try {
|
|
46
|
+
socket.write(
|
|
47
|
+
`HTTP/1.1 ${status} ${message}\r
|
|
48
|
+
Connection: close\r
|
|
49
|
+
Content-Type: text/plain\r
|
|
50
|
+
Content-Length: ${message.length}\r
|
|
51
|
+
\r
|
|
52
|
+
${message}`
|
|
53
|
+
);
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
socket.destroy();
|
|
57
|
+
}
|
|
58
|
+
function isSameOriginUpgrade(req) {
|
|
59
|
+
const origin = req.headers.origin;
|
|
60
|
+
const host = req.headers.host;
|
|
61
|
+
if (!origin || !host) return false;
|
|
62
|
+
try {
|
|
63
|
+
return new URL(origin).host === host;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function decodeProxyHeaders(encoded) {
|
|
69
|
+
if (!encoded) return {};
|
|
70
|
+
const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
71
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
72
|
+
const parsed = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
73
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
74
|
+
const headers = {};
|
|
75
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
76
|
+
if (value == null) continue;
|
|
77
|
+
if (STRIP_UPSTREAM_HEADERS.has(key.toLowerCase())) continue;
|
|
78
|
+
headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
79
|
+
}
|
|
80
|
+
return headers;
|
|
81
|
+
}
|
|
82
|
+
function getDefaultWebSocketOrigin(targetUrl) {
|
|
83
|
+
const origin = new URL(targetUrl.href);
|
|
84
|
+
origin.protocol = targetUrl.protocol === "wss:" ? "https:" : "http:";
|
|
85
|
+
return origin.origin;
|
|
86
|
+
}
|
|
87
|
+
function getRequestedProtocols(req) {
|
|
88
|
+
const header = req.headers["sec-websocket-protocol"];
|
|
89
|
+
const value = Array.isArray(header) ? header.join(",") : header || "";
|
|
90
|
+
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
91
|
+
}
|
|
92
|
+
function safeClose(ws, code, reason) {
|
|
93
|
+
if (ws.readyState === import_ws.WebSocket.CLOSED || ws.readyState === import_ws.WebSocket.CLOSING) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
ws.close(code, reason);
|
|
98
|
+
} catch {
|
|
99
|
+
ws.terminate();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function connectProxyPair(clientWs, upstream) {
|
|
103
|
+
let closing = false;
|
|
104
|
+
const closeBoth = (source, target, code, reason) => {
|
|
105
|
+
if (closing) return;
|
|
106
|
+
closing = true;
|
|
107
|
+
safeClose(target, code, reason.toString());
|
|
108
|
+
if (source.readyState === import_ws.WebSocket.OPEN) {
|
|
109
|
+
safeClose(source, code, reason.toString());
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
clientWs.on("message", (data, isBinary) => {
|
|
113
|
+
if (upstream.readyState === import_ws.WebSocket.OPEN) {
|
|
114
|
+
upstream.send(data, { binary: isBinary });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
upstream.on("message", (data, isBinary) => {
|
|
118
|
+
if (clientWs.readyState === import_ws.WebSocket.OPEN) {
|
|
119
|
+
clientWs.send(data, { binary: isBinary });
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
clientWs.on("close", (code, reason) => closeBoth(clientWs, upstream, code, reason));
|
|
123
|
+
upstream.on("close", (code, reason) => closeBoth(upstream, clientWs, code, reason));
|
|
124
|
+
clientWs.on("error", () => safeClose(upstream, 1011, "proxy client error"));
|
|
125
|
+
upstream.on("error", () => safeClose(clientWs, 1011, "upstream websocket error"));
|
|
126
|
+
}
|
|
127
|
+
function createUpstreamWebSocket(targetUrl, protocols, headers, forwardOrigin) {
|
|
128
|
+
const upstreamHeaders = { ...headers };
|
|
129
|
+
const originKeys = Object.keys(upstreamHeaders).filter(
|
|
130
|
+
(key) => key.toLowerCase() === "origin"
|
|
131
|
+
);
|
|
132
|
+
if (forwardOrigin && originKeys.length === 0) {
|
|
133
|
+
upstreamHeaders.origin = getDefaultWebSocketOrigin(targetUrl);
|
|
134
|
+
} else if (!forwardOrigin) {
|
|
135
|
+
for (const key of originKeys) delete upstreamHeaders[key];
|
|
136
|
+
}
|
|
137
|
+
if (Object.keys(upstreamHeaders).length === 0)
|
|
138
|
+
return new import_ws.WebSocket(targetUrl.href, protocols);
|
|
139
|
+
return new import_ws.WebSocket(targetUrl.href, protocols, {
|
|
140
|
+
headers: upstreamHeaders
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function isWebSocketProxyRequestUrl(rawUrl) {
|
|
144
|
+
if (!rawUrl) return false;
|
|
145
|
+
try {
|
|
146
|
+
return new URL(rawUrl, "http://localhost").pathname === WEBSOCKET_PROXY_PATH;
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function handleWebSocketProxyUpgrade(req, socket, head, forwardOrigin = true) {
|
|
152
|
+
if (!isWebSocketProxyRequestUrl(req.url)) return false;
|
|
153
|
+
if (!isSameOriginUpgrade(req)) {
|
|
154
|
+
rejectUpgrade(socket, 403, "forbidden websocket proxy origin");
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
let targetUrl;
|
|
158
|
+
let headers;
|
|
159
|
+
try {
|
|
160
|
+
const requestUrl = new URL(req.url || "/", "http://localhost");
|
|
161
|
+
const target = requestUrl.searchParams.get("url");
|
|
162
|
+
if (!target) {
|
|
163
|
+
rejectUpgrade(socket, 400, "missing websocket proxy url");
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
targetUrl = new URL(target);
|
|
167
|
+
if (targetUrl.protocol !== "ws:" && targetUrl.protocol !== "wss:") {
|
|
168
|
+
rejectUpgrade(socket, 400, "invalid websocket proxy protocol");
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
headers = decodeProxyHeaders(requestUrl.searchParams.get("headers"));
|
|
172
|
+
} catch {
|
|
173
|
+
rejectUpgrade(socket, 400, "invalid websocket proxy request");
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
const protocols = getRequestedProtocols(req);
|
|
177
|
+
const upstream = createUpstreamWebSocket(targetUrl, protocols, headers, forwardOrigin);
|
|
178
|
+
let completed = false;
|
|
179
|
+
socket.once("close", () => {
|
|
180
|
+
if (!completed) upstream.terminate();
|
|
181
|
+
});
|
|
182
|
+
upstream.once("open", () => {
|
|
183
|
+
if (completed) return;
|
|
184
|
+
completed = true;
|
|
185
|
+
const selectedProtocol = upstream.protocol;
|
|
186
|
+
const proxyServer = new import_ws.WebSocketServer({
|
|
187
|
+
noServer: true,
|
|
188
|
+
clientTracking: false,
|
|
189
|
+
handleProtocols(requestedProtocols) {
|
|
190
|
+
return selectedProtocol || requestedProtocols.values().next().value || false;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
proxyServer.handleUpgrade(req, socket, head, (clientWs) => {
|
|
194
|
+
connectProxyPair(clientWs, upstream);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
upstream.once("error", () => {
|
|
198
|
+
if (completed) return;
|
|
199
|
+
completed = true;
|
|
200
|
+
rejectUpgrade(socket, 502, "upstream websocket error");
|
|
201
|
+
});
|
|
202
|
+
upstream.once("close", () => {
|
|
203
|
+
if (completed) return;
|
|
204
|
+
completed = true;
|
|
205
|
+
rejectUpgrade(socket, 502, "upstream websocket closed");
|
|
206
|
+
});
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
210
|
+
0 && (module.exports = {
|
|
211
|
+
WEBSOCKET_PROXY_PATH,
|
|
212
|
+
handleWebSocketProxyUpgrade,
|
|
213
|
+
isWebSocketProxyRequestUrl
|
|
214
|
+
});
|