@maintainer-pro/ai-bridge 0.1.16 → 0.1.19
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/package.json +2 -2
- package/src/daemon.mjs +386 -74
- package/src/share-rewrite.mjs +529 -61
package/src/share-rewrite.mjs
CHANGED
|
@@ -79,7 +79,8 @@ export function prepareShareHttpRequest(
|
|
|
79
79
|
headers,
|
|
80
80
|
publicBase,
|
|
81
81
|
acceptEncodingHint,
|
|
82
|
-
path
|
|
82
|
+
path,
|
|
83
|
+
portUrls
|
|
83
84
|
) {
|
|
84
85
|
const out = { ...headers };
|
|
85
86
|
const acceptEncoding = String(
|
|
@@ -116,6 +117,13 @@ export function prepareShareHttpRequest(
|
|
|
116
117
|
/* ignore */
|
|
117
118
|
}
|
|
118
119
|
}
|
|
120
|
+
if (portUrls && typeof portUrls === "object") {
|
|
121
|
+
for (const key of Object.keys(out)) {
|
|
122
|
+
const lower = key.toLowerCase();
|
|
123
|
+
if (lower === "host" || lower === "content-length") continue;
|
|
124
|
+
out[key] = rewriteMappedLocalUrls(out[key], portUrls);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
119
127
|
return { headers: out, acceptEncoding, ifNoneMatch };
|
|
120
128
|
}
|
|
121
129
|
|
|
@@ -123,14 +131,23 @@ export function shouldRewriteBody(headers) {
|
|
|
123
131
|
const enc = headerGet(headers, "content-encoding").toLowerCase();
|
|
124
132
|
if (enc && enc !== "identity") return false;
|
|
125
133
|
const ct = headerGet(headers, "content-type").toLowerCase();
|
|
126
|
-
|
|
134
|
+
if (/event-stream|octet-stream|image\/|audio\/|video\/|font\/|wasm/.test(ct)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return /html|javascript|ecmascript|css|json|svg|xml|text\/|urlencoded|x-component|x-ref|\brsc\b/.test(
|
|
127
138
|
ct
|
|
128
139
|
);
|
|
129
140
|
}
|
|
130
141
|
|
|
131
142
|
/** Redirects have no Content-Type, but Location still has to stay under the share prefix. */
|
|
132
143
|
export function shouldProcessShareResponse(headers) {
|
|
133
|
-
|
|
144
|
+
if (headerGet(headers, "location")) return true;
|
|
145
|
+
if (shouldRewriteBody(headers)) return true;
|
|
146
|
+
const ct = headerGet(headers, "content-type").toLowerCase();
|
|
147
|
+
if (ct) return false;
|
|
148
|
+
const len = Number(headerGet(headers, "content-length") || 0);
|
|
149
|
+
if (len > 2 * 1024 * 1024) return false;
|
|
150
|
+
return true;
|
|
134
151
|
}
|
|
135
152
|
|
|
136
153
|
function isImmutableAssetPath(path) {
|
|
@@ -155,7 +172,8 @@ function isNoStorePath(path) {
|
|
|
155
172
|
/\/@vite(?:\/|$)/.test(p) ||
|
|
156
173
|
/\/@react-refresh/.test(p) ||
|
|
157
174
|
/\/@fs\//.test(p) ||
|
|
158
|
-
/\/@id\//.test(p)
|
|
175
|
+
/\/@id\//.test(p) ||
|
|
176
|
+
/\/__mp\//.test(p)
|
|
159
177
|
);
|
|
160
178
|
}
|
|
161
179
|
|
|
@@ -240,30 +258,274 @@ function notModifiedResult(headers, etag) {
|
|
|
240
258
|
return { status: 304, headers: out, body: Buffer.alloc(0) };
|
|
241
259
|
}
|
|
242
260
|
|
|
243
|
-
function
|
|
244
|
-
|
|
245
|
-
|
|
261
|
+
function isLoopbackHost(host) {
|
|
262
|
+
const name = String(host || "").toLowerCase().replace(/^\[|\]$/g, "");
|
|
263
|
+
return (
|
|
264
|
+
name === "127.0.0.1" ||
|
|
265
|
+
name === "localhost" ||
|
|
266
|
+
name === "::1" ||
|
|
267
|
+
name === "0.0.0.0"
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function mappedUrlForPort(portUrls, port) {
|
|
272
|
+
if (!portUrls || !port) return "";
|
|
273
|
+
const dest = portUrls[port] || portUrls[String(port)];
|
|
274
|
+
return dest ? String(dest).replace(/\/$/, "") : "";
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isProbablyUtf8Text(buf) {
|
|
278
|
+
if (!Buffer.isBuffer(buf) || !buf.length || buf.length > 8 * 1024 * 1024) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
const sample = buf.subarray(0, Math.min(buf.length, 4096));
|
|
282
|
+
if (sample.includes(0)) return false;
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Replace app-list origins (`localhost:8080`, LAN IPs, hostnames) with the
|
|
288
|
+
* share URL for that port. Self-contained so the browser shim/SW can embed it.
|
|
289
|
+
*
|
|
290
|
+
* @param {string} text
|
|
291
|
+
* @param {Record<string, string> | null | undefined} portUrls
|
|
292
|
+
*/
|
|
293
|
+
export function rewriteMappedLocalUrls(text, portUrls) {
|
|
294
|
+
if (!text || !portUrls) return text;
|
|
295
|
+
const ports = Object.keys(portUrls)
|
|
296
|
+
.map((key) => Number(key))
|
|
297
|
+
.filter((port) => Number.isInteger(port) && port > 0 && port <= 65535)
|
|
298
|
+
.sort((a, b) => b - a);
|
|
299
|
+
if (!ports.length) return text;
|
|
300
|
+
const localHost =
|
|
301
|
+
"(?:localhost|127\\.0\\.0\\.1|\\[::1\\]|::1|0\\.0\\.0\\.0|" +
|
|
302
|
+
"10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
303
|
+
"192\\.168\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
304
|
+
"172\\.(?:1[6-9]|2\\d|3[01])\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
305
|
+
"169\\.254\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
306
|
+
"[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.(?:local|lan|internal|home))?)";
|
|
307
|
+
const anyHost = "(?:[^\\s'\"\\\\/<>@:]+|\\[[0-9a-fA-F:]+\\])";
|
|
308
|
+
let out = String(text);
|
|
309
|
+
for (let i = 0; i < ports.length; i++) {
|
|
310
|
+
const port = ports[i];
|
|
311
|
+
const dest = String(portUrls[port] || portUrls[String(port)] || "").replace(
|
|
312
|
+
/\/$/,
|
|
313
|
+
""
|
|
314
|
+
);
|
|
315
|
+
if (!dest) continue;
|
|
316
|
+
const destWs = dest.replace(/^http/i, "ws");
|
|
317
|
+
const destEsc = dest.replace(/\//g, "\\/");
|
|
318
|
+
const destWsEsc = destWs.replace(/\//g, "\\/");
|
|
319
|
+
const tail = "(?!\\d)";
|
|
320
|
+
const swap = function (matched, replacement) {
|
|
321
|
+
if (!matched) return matched;
|
|
322
|
+
if (dest.indexOf(matched) === 0 || destWs.indexOf(matched) === 0) {
|
|
323
|
+
return matched;
|
|
324
|
+
}
|
|
325
|
+
return replacement;
|
|
326
|
+
};
|
|
327
|
+
out = out.replace(new RegExp("https?://" + localHost + ":" + port + tail, "gi"), function (m) {
|
|
328
|
+
return swap(m, dest);
|
|
329
|
+
});
|
|
330
|
+
out = out.replace(new RegExp("wss?://" + localHost + ":" + port + tail, "gi"), function (m) {
|
|
331
|
+
return swap(m, destWs);
|
|
332
|
+
});
|
|
333
|
+
out = out.replace(new RegExp("https?:\\\\/\\\\/" + localHost + ":" + port + tail, "gi"), function (m) {
|
|
334
|
+
return swap(m, destEsc);
|
|
335
|
+
});
|
|
336
|
+
out = out.replace(new RegExp("wss?:\\\\/\\\\/" + localHost + ":" + port + tail, "gi"), function (m) {
|
|
337
|
+
return swap(m, destWsEsc);
|
|
338
|
+
});
|
|
339
|
+
out = out.replace(new RegExp("//" + localHost + ":" + port + tail, "gi"), function (m) {
|
|
340
|
+
return swap(m, dest.replace(/^https?:/i, ""));
|
|
341
|
+
});
|
|
342
|
+
out = out.replace(new RegExp("http://" + anyHost + ":" + port + tail, "gi"), function (m) {
|
|
343
|
+
return swap(m, dest);
|
|
344
|
+
});
|
|
345
|
+
out = out.replace(new RegExp("ws://" + anyHost + ":" + port + tail, "gi"), function (m) {
|
|
346
|
+
return swap(m, destWs);
|
|
347
|
+
});
|
|
348
|
+
out = out.replace(
|
|
349
|
+
new RegExp("(?<![\\w./])" + localHost + ":" + port + tail, "gi"),
|
|
350
|
+
function (m) {
|
|
351
|
+
return swap(m, dest);
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
return out;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* @param {Buffer | string | null | undefined} body
|
|
360
|
+
* @param {Record<string, string>} headers
|
|
361
|
+
* @param {Record<string, string> | null | undefined} portUrls
|
|
362
|
+
*/
|
|
363
|
+
export function rewriteShareRequestBody(body, headers, portUrls, publicBase) {
|
|
364
|
+
if (!body) return body;
|
|
365
|
+
const buf = Buffer.isBuffer(body) ? body : Buffer.from(body);
|
|
366
|
+
if (!buf.length) return buf;
|
|
367
|
+
if (!shouldRewriteBody(headers) && !isProbablyUtf8Text(buf)) return buf;
|
|
368
|
+
let text = buf.toString("utf8");
|
|
369
|
+
let next = text;
|
|
370
|
+
if (portUrls && Object.keys(portUrls).length) {
|
|
371
|
+
next = rewriteMappedLocalUrls(next, portUrls);
|
|
372
|
+
}
|
|
373
|
+
if (publicBase) next = rewriteShareOriginPaths(next, publicBase);
|
|
374
|
+
if (next === text) return buf;
|
|
375
|
+
return Buffer.from(next, "utf8");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function shareTokenRoot(publicBase) {
|
|
246
379
|
const prefix = proxyPathPrefix(publicBase);
|
|
380
|
+
if (!prefix) return "";
|
|
381
|
+
const parts = prefix.split("/").filter(Boolean);
|
|
382
|
+
if (parts[0] === "p" && parts[1]) return `/${parts[0]}/${parts[1]}/`;
|
|
383
|
+
return prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function shareShimScript(pathPrefix, portUrls) {
|
|
387
|
+
return `(${shareProxyShim.toString()})(${JSON.stringify(pathPrefix || "")},${JSON.stringify(portUrls || {})},${rewriteMappedLocalUrls.toString()});`;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function shareServiceWorkerScript(portUrls, tokenRoot) {
|
|
391
|
+
return `(${shareServiceWorkerMain.toString()})(${JSON.stringify(portUrls || {})},${JSON.stringify(tokenRoot || "/")},${rewriteMappedLocalUrls.toString()});`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function rewriteHeaderUrls(headers, publicBase, port, portUrls) {
|
|
395
|
+
if (headers.location) {
|
|
396
|
+
headers.location = rewriteLocation(
|
|
397
|
+
headers.location,
|
|
398
|
+
publicBase,
|
|
399
|
+
port,
|
|
400
|
+
portUrls
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
if (headers.link) {
|
|
404
|
+
headers.link = rewriteLinkHeader(headers.link, publicBase);
|
|
405
|
+
}
|
|
406
|
+
if (headers["access-control-allow-origin"]) {
|
|
407
|
+
headers["access-control-allow-origin"] = rewriteAllowOrigin(
|
|
408
|
+
headers["access-control-allow-origin"],
|
|
409
|
+
publicBase
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
if (portUrls && Object.keys(portUrls).length) {
|
|
413
|
+
for (const key of Object.keys(headers)) {
|
|
414
|
+
const lower = key.toLowerCase();
|
|
415
|
+
if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
headers[key] = rewriteMappedLocalUrls(headers[key], portUrls);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (publicBase) {
|
|
422
|
+
for (const key of Object.keys(headers)) {
|
|
423
|
+
const lower = key.toLowerCase();
|
|
424
|
+
if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
headers[key] = rewriteShareOriginPaths(headers[key], publicBase);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Apps that honor X-Forwarded-Host emit https://admin-host/oauth2 instead of
|
|
434
|
+
* localhost:8080/oauth2. Put those back under /p/{token}/{slug} so the token
|
|
435
|
+
* and app route stay on the URL.
|
|
436
|
+
*
|
|
437
|
+
* @param {string} value
|
|
438
|
+
* @param {string} publicBase
|
|
439
|
+
*/
|
|
440
|
+
export function prefixShareOriginUrl(value, publicBase) {
|
|
441
|
+
if (!value || !publicBase) return value;
|
|
442
|
+
try {
|
|
443
|
+
const base = new URL(String(publicBase).replace(/\/$/, ""));
|
|
444
|
+
const prefix = base.pathname.replace(/\/$/, "");
|
|
445
|
+
if (!prefix || prefix.indexOf("/p/") !== 0) return value;
|
|
446
|
+
const u = new URL(String(value), base);
|
|
447
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return value;
|
|
448
|
+
if (String(u.hostname || "").toLowerCase() !== String(base.hostname || "").toLowerCase()) {
|
|
449
|
+
return value;
|
|
450
|
+
}
|
|
451
|
+
const reqPort = u.port || (u.protocol === "https:" ? "443" : "80");
|
|
452
|
+
const basePort = base.port || (base.protocol === "https:" ? "443" : "80");
|
|
453
|
+
if (reqPort !== basePort) return value;
|
|
454
|
+
const path = u.pathname || "/";
|
|
455
|
+
if (path === prefix || path.indexOf(prefix + "/") === 0) return u.toString();
|
|
456
|
+
if (path.indexOf("/p/") === 0) return u.toString();
|
|
457
|
+
u.pathname = path === "/" ? prefix : prefix + path;
|
|
458
|
+
return u.toString();
|
|
459
|
+
} catch {
|
|
460
|
+
return value;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export function rewriteShareOriginPaths(text, publicBase) {
|
|
465
|
+
if (!text || !publicBase) return text;
|
|
466
|
+
let origin = "";
|
|
467
|
+
try {
|
|
468
|
+
origin = new URL(String(publicBase).replace(/\/$/, "")).origin;
|
|
469
|
+
} catch {
|
|
470
|
+
return text;
|
|
471
|
+
}
|
|
472
|
+
if (!origin) return text;
|
|
473
|
+
const originEsc = origin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
474
|
+
const originSlashEsc = origin
|
|
475
|
+
.replace(/\//g, "\\/")
|
|
476
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
477
|
+
let out = String(text);
|
|
478
|
+
out = out.replace(new RegExp(originEsc + "(?:/[^\\s'\"<>\\\\]*)?", "gi"), function (matched) {
|
|
479
|
+
return prefixShareOriginUrl(matched, publicBase);
|
|
480
|
+
});
|
|
481
|
+
out = out.replace(
|
|
482
|
+
new RegExp(originSlashEsc + "(?:\\\\/[^\\s'\"<>]*)?", "gi"),
|
|
483
|
+
function (matched) {
|
|
484
|
+
const unescaped = matched.replace(/\\\//g, "/");
|
|
485
|
+
return prefixShareOriginUrl(unescaped, publicBase).replace(/\//g, "\\/");
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
return out;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function rewriteLocation(value, publicBase, port, portUrls) {
|
|
492
|
+
if (!publicBase && !(portUrls && Object.keys(portUrls).length)) return value;
|
|
247
493
|
try {
|
|
248
494
|
const u = new URL(value, `http://127.0.0.1:${Number(port) || 0}`);
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
495
|
+
if (isLoopbackHost(u.hostname)) {
|
|
496
|
+
const locPort = Number(u.port) || Number(port) || 0;
|
|
497
|
+
const mapped =
|
|
498
|
+
mappedUrlForPort(portUrls, locPort) ||
|
|
499
|
+
String(publicBase || "").replace(/\/$/, "");
|
|
500
|
+
if (!mapped) return value;
|
|
501
|
+
const prefix = proxyPathPrefix(mapped);
|
|
502
|
+
let path = u.pathname || "/";
|
|
503
|
+
if (prefix && (path === prefix || path.startsWith(`${prefix}/`))) {
|
|
504
|
+
path = path.slice(prefix.length) || "/";
|
|
505
|
+
}
|
|
506
|
+
if (path === "/") return `${mapped}${u.search}${u.hash}`;
|
|
507
|
+
return `${mapped}${path}${u.search}${u.hash}`;
|
|
508
|
+
}
|
|
509
|
+
const fromPorts = rewriteMappedLocalUrls(String(value), portUrls);
|
|
510
|
+
if (fromPorts !== String(value)) return fromPorts;
|
|
511
|
+
if (publicBase) return prefixShareOriginUrl(String(value), publicBase);
|
|
261
512
|
} catch {
|
|
262
513
|
/* keep */
|
|
263
514
|
}
|
|
264
515
|
return value;
|
|
265
516
|
}
|
|
266
517
|
|
|
518
|
+
function rewriteAllowOrigin(value, publicBase) {
|
|
519
|
+
if (!value || value === "*" || !publicBase) return value;
|
|
520
|
+
try {
|
|
521
|
+
const u = new URL(value);
|
|
522
|
+
if (!isLoopbackHost(u.hostname)) return value;
|
|
523
|
+
return new URL(publicBase).origin;
|
|
524
|
+
} catch {
|
|
525
|
+
return value;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
267
529
|
function rewriteLinkHeader(value, publicBase) {
|
|
268
530
|
const prefix = proxyPathPrefix(publicBase);
|
|
269
531
|
if (!prefix || !value) return value;
|
|
@@ -686,11 +948,22 @@ function injectMaintainerProEmbed(html, aiPublicBase) {
|
|
|
686
948
|
}
|
|
687
949
|
|
|
688
950
|
/**
|
|
689
|
-
*
|
|
690
|
-
* only touch same-origin Vite/Next assets. Other hosts are left unchanged.
|
|
951
|
+
* Intercept every browser request and map listed app ports onto share URLs.
|
|
691
952
|
*/
|
|
692
|
-
function shareProxyShim(p) {
|
|
693
|
-
if (!p) return;
|
|
953
|
+
function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
|
|
954
|
+
if (!p || window.__MP_SHARE_SHIM__) return;
|
|
955
|
+
window.__MP_SHARE_SHIM__ = 1;
|
|
956
|
+
window.__MP_PORT_MAP__ = portMap || {};
|
|
957
|
+
function rewriteText(text) {
|
|
958
|
+
if (typeof text !== "string" || !text || typeof rewriteMappedLocalUrls !== "function") {
|
|
959
|
+
return text;
|
|
960
|
+
}
|
|
961
|
+
try {
|
|
962
|
+
return rewriteMappedLocalUrls(text, portMap);
|
|
963
|
+
} catch (e) {
|
|
964
|
+
return text;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
694
967
|
function collapse(path) {
|
|
695
968
|
var d = p + p;
|
|
696
969
|
while (path.indexOf(d) === 0) path = p + path.slice(d.length);
|
|
@@ -713,13 +986,40 @@ function shareProxyShim(p) {
|
|
|
713
986
|
if (path === p || path.indexOf(p + "/") === 0) return collapse(path);
|
|
714
987
|
var root = p.slice(0, p.lastIndexOf("/"));
|
|
715
988
|
if (root && (path === root || path.indexOf(root + "/") === 0)) return path;
|
|
989
|
+
if (path.indexOf("/p/") === 0) return path;
|
|
716
990
|
if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return path;
|
|
717
991
|
return collapse(p + path);
|
|
718
992
|
}
|
|
719
993
|
return path;
|
|
720
994
|
}
|
|
995
|
+
function mapLocal(v) {
|
|
996
|
+
if (typeof v !== "string" || !v) return v;
|
|
997
|
+
if (
|
|
998
|
+
v.indexOf("blob:") === 0 ||
|
|
999
|
+
v.indexOf("data:") === 0 ||
|
|
1000
|
+
v.indexOf("javascript:") === 0
|
|
1001
|
+
) {
|
|
1002
|
+
return v;
|
|
1003
|
+
}
|
|
1004
|
+
var raw = v;
|
|
1005
|
+
if (
|
|
1006
|
+
v.indexOf("://") < 0 &&
|
|
1007
|
+
/^(localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0):\d+/i.test(v)
|
|
1008
|
+
) {
|
|
1009
|
+
raw = "http://" + v;
|
|
1010
|
+
}
|
|
1011
|
+
var next = rewriteText(raw);
|
|
1012
|
+
if (next !== raw) return next;
|
|
1013
|
+
try {
|
|
1014
|
+
var u = new URL(v, location.href);
|
|
1015
|
+
var resolved = rewriteText(u.href);
|
|
1016
|
+
if (resolved !== u.href) return resolved;
|
|
1017
|
+
} catch (e) {}
|
|
1018
|
+
return v;
|
|
1019
|
+
}
|
|
721
1020
|
function addNav(v) {
|
|
722
1021
|
if (typeof v !== "string" || !v) return v;
|
|
1022
|
+
v = mapLocal(v);
|
|
723
1023
|
if (v.charAt(0) === "/" && v.charAt(1) !== "/") return prefixPath(v);
|
|
724
1024
|
try {
|
|
725
1025
|
var nav = new URL(v, location.href);
|
|
@@ -730,23 +1030,7 @@ function shareProxyShim(p) {
|
|
|
730
1030
|
return v;
|
|
731
1031
|
}
|
|
732
1032
|
function addNet(v) {
|
|
733
|
-
|
|
734
|
-
try {
|
|
735
|
-
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(v) || v.indexOf("//") === 0) {
|
|
736
|
-
var net = new URL(v, location.href);
|
|
737
|
-
if (net.origin !== location.origin) return v;
|
|
738
|
-
if (net.pathname === p || net.pathname.indexOf(p + "/") === 0) return v;
|
|
739
|
-
if (!isAsset(net.pathname)) return v;
|
|
740
|
-
net.pathname = prefixPath(net.pathname);
|
|
741
|
-
return net.toString();
|
|
742
|
-
}
|
|
743
|
-
} catch (e) {}
|
|
744
|
-
if (v.charAt(0) === "/" && v.charAt(1) !== "/") {
|
|
745
|
-
if (v === p || v.indexOf(p + "/") === 0) return collapse(v);
|
|
746
|
-
if (!isAsset(v)) return v;
|
|
747
|
-
return prefixPath(v);
|
|
748
|
-
}
|
|
749
|
-
return v;
|
|
1033
|
+
return addNav(v);
|
|
750
1034
|
}
|
|
751
1035
|
function mapSel(sel) {
|
|
752
1036
|
if (typeof sel !== "string") return sel;
|
|
@@ -776,11 +1060,22 @@ function shareProxyShim(p) {
|
|
|
776
1060
|
patchQS(Element.prototype, "querySelectorAll");
|
|
777
1061
|
var sa = Element.prototype.setAttribute;
|
|
778
1062
|
Element.prototype.setAttribute = function (n, v) {
|
|
779
|
-
|
|
1063
|
+
var name = String(n || "").toLowerCase();
|
|
1064
|
+
if (
|
|
1065
|
+
(name === "src" ||
|
|
1066
|
+
name === "href" ||
|
|
1067
|
+
name === "action" ||
|
|
1068
|
+
name === "poster" ||
|
|
1069
|
+
name === "data-src") &&
|
|
1070
|
+
typeof v === "string"
|
|
1071
|
+
) {
|
|
1072
|
+
v = addNav(v);
|
|
1073
|
+
}
|
|
780
1074
|
return sa.call(this, n, v);
|
|
781
1075
|
};
|
|
782
1076
|
function patchUrlProp(ctor, prop) {
|
|
783
1077
|
try {
|
|
1078
|
+
if (!ctor || !ctor.prototype) return;
|
|
784
1079
|
var d = Object.getOwnPropertyDescriptor(ctor.prototype, prop);
|
|
785
1080
|
if (!d || !d.set) return;
|
|
786
1081
|
Object.defineProperty(ctor.prototype, prop, {
|
|
@@ -795,6 +1090,13 @@ function shareProxyShim(p) {
|
|
|
795
1090
|
}
|
|
796
1091
|
patchUrlProp(HTMLScriptElement, "src");
|
|
797
1092
|
patchUrlProp(HTMLLinkElement, "href");
|
|
1093
|
+
patchUrlProp(HTMLAnchorElement, "href");
|
|
1094
|
+
patchUrlProp(HTMLImageElement, "src");
|
|
1095
|
+
patchUrlProp(HTMLIFrameElement, "src");
|
|
1096
|
+
patchUrlProp(HTMLFormElement, "action");
|
|
1097
|
+
patchUrlProp(HTMLSourceElement, "src");
|
|
1098
|
+
patchUrlProp(HTMLVideoElement, "src");
|
|
1099
|
+
patchUrlProp(HTMLAudioElement, "src");
|
|
798
1100
|
var f = window.fetch;
|
|
799
1101
|
window.fetch = function (input, init) {
|
|
800
1102
|
if (typeof input === "string") input = addNet(input);
|
|
@@ -803,6 +1105,9 @@ function shareProxyShim(p) {
|
|
|
803
1105
|
} else if (typeof URL !== "undefined" && input instanceof URL) {
|
|
804
1106
|
input = new URL(addNet(input.href));
|
|
805
1107
|
}
|
|
1108
|
+
if (init && typeof init.body === "string") {
|
|
1109
|
+
init = Object.assign({}, init, { body: rewriteText(init.body) });
|
|
1110
|
+
}
|
|
806
1111
|
return f.call(this, input, init);
|
|
807
1112
|
};
|
|
808
1113
|
var xo = XMLHttpRequest.prototype.open;
|
|
@@ -810,6 +1115,11 @@ function shareProxyShim(p) {
|
|
|
810
1115
|
if (typeof u === "string") arguments[1] = addNet(u);
|
|
811
1116
|
return xo.apply(this, arguments);
|
|
812
1117
|
};
|
|
1118
|
+
var xs = XMLHttpRequest.prototype.send;
|
|
1119
|
+
XMLHttpRequest.prototype.send = function (body) {
|
|
1120
|
+
if (typeof body === "string") arguments[0] = rewriteText(body);
|
|
1121
|
+
return xs.apply(this, arguments);
|
|
1122
|
+
};
|
|
813
1123
|
var ps = history.pushState.bind(history);
|
|
814
1124
|
history.pushState = function (s, t, u) {
|
|
815
1125
|
if (typeof u === "string") u = addNav(u);
|
|
@@ -823,6 +1133,7 @@ function shareProxyShim(p) {
|
|
|
823
1133
|
var WS = window.WebSocket;
|
|
824
1134
|
function WrappedWS(url, protocols) {
|
|
825
1135
|
if (typeof url === "string") url = addNet(url);
|
|
1136
|
+
else if (typeof URL !== "undefined" && url instanceof URL) url = addNet(url.href);
|
|
826
1137
|
return protocols !== undefined ? new WS(url, protocols) : new WS(url);
|
|
827
1138
|
}
|
|
828
1139
|
WrappedWS.prototype = WS.prototype;
|
|
@@ -831,6 +1142,30 @@ function shareProxyShim(p) {
|
|
|
831
1142
|
WrappedWS.CLOSING = WS.CLOSING;
|
|
832
1143
|
WrappedWS.CLOSED = WS.CLOSED;
|
|
833
1144
|
window.WebSocket = WrappedWS;
|
|
1145
|
+
if (window.EventSource) {
|
|
1146
|
+
var ES = window.EventSource;
|
|
1147
|
+
function WrappedES(url, config) {
|
|
1148
|
+
if (typeof url === "string") url = addNet(url);
|
|
1149
|
+
return config !== undefined ? new ES(url, config) : new ES(url);
|
|
1150
|
+
}
|
|
1151
|
+
WrappedES.prototype = ES.prototype;
|
|
1152
|
+
WrappedES.CONNECTING = ES.CONNECTING;
|
|
1153
|
+
WrappedES.OPEN = ES.OPEN;
|
|
1154
|
+
WrappedES.CLOSED = ES.CLOSED;
|
|
1155
|
+
window.EventSource = WrappedES;
|
|
1156
|
+
}
|
|
1157
|
+
if (navigator.sendBeacon) {
|
|
1158
|
+
var sb = navigator.sendBeacon.bind(navigator);
|
|
1159
|
+
navigator.sendBeacon = function (url, data) {
|
|
1160
|
+
if (typeof data === "string") data = rewriteText(data);
|
|
1161
|
+
return sb(addNet(String(url)), data);
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
var wo = window.open;
|
|
1165
|
+
window.open = function (url) {
|
|
1166
|
+
if (typeof url === "string") arguments[0] = addNav(url);
|
|
1167
|
+
return wo.apply(this, arguments);
|
|
1168
|
+
};
|
|
834
1169
|
try {
|
|
835
1170
|
var la = location.assign.bind(location);
|
|
836
1171
|
location.assign = function (u) {
|
|
@@ -856,19 +1191,141 @@ function shareProxyShim(p) {
|
|
|
856
1191
|
});
|
|
857
1192
|
}
|
|
858
1193
|
} catch (e) {}
|
|
1194
|
+
try {
|
|
1195
|
+
var tokenRoot = p.replace(/\/[^/]+$/, "/");
|
|
1196
|
+
if (navigator.serviceWorker && tokenRoot.indexOf("/p/") === 0) {
|
|
1197
|
+
navigator.serviceWorker.register(p + "/__mp/sw.js", { scope: tokenRoot });
|
|
1198
|
+
}
|
|
1199
|
+
} catch (e) {}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
|
|
1203
|
+
self.addEventListener("install", function (event) {
|
|
1204
|
+
event.waitUntil(self.skipWaiting());
|
|
1205
|
+
});
|
|
1206
|
+
self.addEventListener("activate", function (event) {
|
|
1207
|
+
event.waitUntil(self.clients.claim());
|
|
1208
|
+
});
|
|
1209
|
+
function rewriteText(text) {
|
|
1210
|
+
if (typeof text !== "string" || !text) return text;
|
|
1211
|
+
try {
|
|
1212
|
+
return rewriteMappedLocalUrls(text, portMap);
|
|
1213
|
+
} catch (e) {
|
|
1214
|
+
return text;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
function appPrefixFromHref(href) {
|
|
1218
|
+
try {
|
|
1219
|
+
var u = new URL(String(href || ""));
|
|
1220
|
+
var parts = u.pathname.split("/").filter(Boolean);
|
|
1221
|
+
if (parts[0] === "p" && parts[1] && parts[2]) {
|
|
1222
|
+
return "/" + parts[0] + "/" + parts[1] + "/" + parts[2];
|
|
1223
|
+
}
|
|
1224
|
+
} catch (e) {}
|
|
1225
|
+
return "";
|
|
1226
|
+
}
|
|
1227
|
+
function prefixWith(url, prefix) {
|
|
1228
|
+
if (!prefix) return url;
|
|
1229
|
+
try {
|
|
1230
|
+
var u = new URL(url, self.location.href);
|
|
1231
|
+
if (u.origin !== self.location.origin) return url;
|
|
1232
|
+
if (u.pathname.indexOf("/p/") === 0) return url;
|
|
1233
|
+
if (u.pathname === prefix || u.pathname.indexOf(prefix + "/") === 0) return url;
|
|
1234
|
+
u.pathname = u.pathname === "/" ? prefix : prefix + u.pathname;
|
|
1235
|
+
return u.toString();
|
|
1236
|
+
} catch (e) {}
|
|
1237
|
+
return url;
|
|
1238
|
+
}
|
|
1239
|
+
function mapUrl(v, prefix) {
|
|
1240
|
+
if (typeof v !== "string" || !v) return v;
|
|
1241
|
+
if (v.indexOf("/__mp/") !== -1) return v;
|
|
1242
|
+
return prefixWith(rewriteText(v), prefix);
|
|
1243
|
+
}
|
|
1244
|
+
self.addEventListener("fetch", function (event) {
|
|
1245
|
+
var req = event.request;
|
|
1246
|
+
if (req.url.indexOf("/__mp/") !== -1) return;
|
|
1247
|
+
event.respondWith(
|
|
1248
|
+
(async function () {
|
|
1249
|
+
var client = null;
|
|
1250
|
+
try {
|
|
1251
|
+
if (event.clientId) client = await self.clients.get(event.clientId);
|
|
1252
|
+
} catch (e) {}
|
|
1253
|
+
var prefix =
|
|
1254
|
+
appPrefixFromHref(client && client.url) ||
|
|
1255
|
+
appPrefixFromHref(req.referrer) ||
|
|
1256
|
+
appPrefixFromHref(self.registration && self.registration.scope);
|
|
1257
|
+
var url = mapUrl(req.url, prefix);
|
|
1258
|
+
var method = req.method;
|
|
1259
|
+
var headers = new Headers(req.headers);
|
|
1260
|
+
var body;
|
|
1261
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
1262
|
+
try {
|
|
1263
|
+
var ct = headers.get("content-type") || "";
|
|
1264
|
+
if (/json|text|xml|urlencoded|javascript/.test(ct) || !ct) {
|
|
1265
|
+
var text = await req.clone().text();
|
|
1266
|
+
var next = rewriteText(text);
|
|
1267
|
+
if (next !== text) body = next;
|
|
1268
|
+
}
|
|
1269
|
+
} catch (e) {}
|
|
1270
|
+
}
|
|
1271
|
+
var init = {
|
|
1272
|
+
method: method,
|
|
1273
|
+
headers: headers,
|
|
1274
|
+
credentials: req.credentials,
|
|
1275
|
+
redirect: req.redirect,
|
|
1276
|
+
referrer: req.referrer,
|
|
1277
|
+
};
|
|
1278
|
+
if (req.mode && req.mode !== "navigate") init.mode = req.mode;
|
|
1279
|
+
if (body !== undefined) init.body = body;
|
|
1280
|
+
else if (method !== "GET" && method !== "HEAD") {
|
|
1281
|
+
try {
|
|
1282
|
+
init.body = await req.blob();
|
|
1283
|
+
} catch (e) {}
|
|
1284
|
+
}
|
|
1285
|
+
var res = await fetch(new Request(url, init));
|
|
1286
|
+
var outHeaders = new Headers(res.headers);
|
|
1287
|
+
var loc = outHeaders.get("location");
|
|
1288
|
+
if (loc) outHeaders.set("location", mapUrl(loc, prefix));
|
|
1289
|
+
var resType = res.headers.get("content-type") || "";
|
|
1290
|
+
if (
|
|
1291
|
+
!/html|javascript|ecmascript|json|css|xml|svg|text|urlencoded/.test(resType)
|
|
1292
|
+
) {
|
|
1293
|
+
if (loc && outHeaders.get("location") !== loc) {
|
|
1294
|
+
return new Response(res.body, {
|
|
1295
|
+
status: res.status,
|
|
1296
|
+
statusText: res.statusText,
|
|
1297
|
+
headers: outHeaders,
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
return res;
|
|
1301
|
+
}
|
|
1302
|
+
var resText = await res.text();
|
|
1303
|
+
var mapped = rewriteText(resText);
|
|
1304
|
+
outHeaders.delete("content-length");
|
|
1305
|
+
outHeaders.delete("content-encoding");
|
|
1306
|
+
return new Response(mapped, {
|
|
1307
|
+
status: res.status,
|
|
1308
|
+
statusText: res.statusText,
|
|
1309
|
+
headers: outHeaders,
|
|
1310
|
+
});
|
|
1311
|
+
})()
|
|
1312
|
+
);
|
|
1313
|
+
});
|
|
859
1314
|
}
|
|
860
1315
|
|
|
861
1316
|
function injectNextProxyShim(html, publicBase) {
|
|
862
1317
|
const prefix = proxyPathPrefix(publicBase);
|
|
863
|
-
if (
|
|
864
|
-
!prefix ||
|
|
865
|
-
html.includes("data-mp-proxy-next") ||
|
|
866
|
-
(!isNextDocument(html) && !isViteDocument(html))
|
|
867
|
-
) {
|
|
1318
|
+
if (!prefix || html.includes("data-mp-proxy-next")) {
|
|
868
1319
|
return html;
|
|
869
1320
|
}
|
|
870
|
-
const script = `<script data-mp-proxy-next
|
|
871
|
-
|
|
1321
|
+
const script = `<script data-mp-proxy-next src="${prefix}/__mp/shim.js"><\/script>`;
|
|
1322
|
+
if (/<head[^>]*>/i.test(html)) {
|
|
1323
|
+
return html.replace(/<head([^>]*)>/i, `<head$1>${script}`);
|
|
1324
|
+
}
|
|
1325
|
+
if (/<body[^>]*>/i.test(html)) {
|
|
1326
|
+
return html.replace(/<body([^>]*)>/i, `<body$1>${script}`);
|
|
1327
|
+
}
|
|
1328
|
+
return `${script}${html}`;
|
|
872
1329
|
}
|
|
873
1330
|
|
|
874
1331
|
/**
|
|
@@ -885,6 +1342,7 @@ function injectNextProxyShim(html, publicBase) {
|
|
|
885
1342
|
* acceptEncoding?: string,
|
|
886
1343
|
* ifNoneMatch?: string,
|
|
887
1344
|
* port?: number,
|
|
1345
|
+
* portUrls?: Record<string, string>,
|
|
888
1346
|
* }} opts
|
|
889
1347
|
* @returns {{ status: number, headers: Record<string, string>, body: Buffer }}
|
|
890
1348
|
*/
|
|
@@ -896,6 +1354,8 @@ export function processShareHttpResponse(opts) {
|
|
|
896
1354
|
const acceptEncoding = String(opts?.acceptEncoding || "");
|
|
897
1355
|
const ifNoneMatch = String(opts?.ifNoneMatch || "");
|
|
898
1356
|
const port = Number(opts?.port) || 0;
|
|
1357
|
+
const portUrls =
|
|
1358
|
+
opts?.portUrls && typeof opts.portUrls === "object" ? opts.portUrls : {};
|
|
899
1359
|
let status =
|
|
900
1360
|
typeof opts?.status === "number" && opts.status >= 100 ? opts.status : 200;
|
|
901
1361
|
const headers = lowerHeaders(opts?.headers || {});
|
|
@@ -903,12 +1363,7 @@ export function processShareHttpResponse(opts) {
|
|
|
903
1363
|
? opts.body
|
|
904
1364
|
: Buffer.from(opts?.body || "");
|
|
905
1365
|
|
|
906
|
-
|
|
907
|
-
headers.location = rewriteLocation(headers.location, publicBase, port);
|
|
908
|
-
}
|
|
909
|
-
if (headers.link) {
|
|
910
|
-
headers.link = rewriteLinkHeader(headers.link, publicBase);
|
|
911
|
-
}
|
|
1366
|
+
rewriteHeaderUrls(headers, publicBase, port, portUrls);
|
|
912
1367
|
|
|
913
1368
|
const cachedKey = rewriteCacheKey(publicBase, path);
|
|
914
1369
|
const hit =
|
|
@@ -933,10 +1388,27 @@ export function processShareHttpResponse(opts) {
|
|
|
933
1388
|
|
|
934
1389
|
let text = payload.toString("utf8");
|
|
935
1390
|
let mutated = false;
|
|
936
|
-
|
|
1391
|
+
const rewriteTextBody =
|
|
1392
|
+
shouldRewriteBody(headers) ||
|
|
1393
|
+
(!headerGet(headers, "content-type") && isProbablyUtf8Text(payload));
|
|
1394
|
+
if (publicBase && rewriteTextBody) {
|
|
937
1395
|
text = prefixRootPaths(text, publicBase, headers["content-type"] || "");
|
|
938
1396
|
mutated = true;
|
|
939
1397
|
}
|
|
1398
|
+
if (Object.keys(portUrls).length && rewriteTextBody) {
|
|
1399
|
+
const mapped = rewriteMappedLocalUrls(text, portUrls);
|
|
1400
|
+
if (mapped !== text) {
|
|
1401
|
+
text = mapped;
|
|
1402
|
+
mutated = true;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
if (publicBase && rewriteTextBody) {
|
|
1406
|
+
const prefixed = rewriteShareOriginPaths(text, publicBase);
|
|
1407
|
+
if (prefixed !== text) {
|
|
1408
|
+
text = prefixed;
|
|
1409
|
+
mutated = true;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
940
1412
|
if (aiPublicBase) {
|
|
941
1413
|
const withShareUrls = rewriteLocalSidecarUrls(text, aiPublicBase, publicBase);
|
|
942
1414
|
if (withShareUrls !== text) {
|
|
@@ -951,12 +1423,8 @@ export function processShareHttpResponse(opts) {
|
|
|
951
1423
|
mutated = true;
|
|
952
1424
|
}
|
|
953
1425
|
}
|
|
954
|
-
if (
|
|
955
|
-
publicBase
|
|
956
|
-
isHtmlDocument(headers, text) &&
|
|
957
|
-
(isNextDocument(text) || isViteDocument(text))
|
|
958
|
-
) {
|
|
959
|
-
const next = injectNextProxyShim(text, publicBase);
|
|
1426
|
+
if (publicBase && isHtmlDocument(headers, text)) {
|
|
1427
|
+
const next = injectNextProxyShim(text, publicBase, portUrls);
|
|
960
1428
|
if (next !== text) {
|
|
961
1429
|
text = next;
|
|
962
1430
|
mutated = true;
|