@maintainer-pro/ai-bridge 0.1.13 → 0.1.15

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maintainer-pro/ai-bridge",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Local bridge daemon that pairs a machine to Maintainer Pro and configures multiple client sandboxes.",
5
5
  "keywords": [
6
6
  "maintainer-pro",
@@ -28,7 +28,7 @@
28
28
  "node": ">=22"
29
29
  },
30
30
  "dependencies": {
31
- "@maintainer-pro/ai-cli": "^0.1.7",
32
- "@maintainer-pro/ai-server": "^0.1.6"
31
+ "@maintainer-pro/ai-cli": "^0.1.8",
32
+ "@maintainer-pro/ai-server": "^0.1.7"
33
33
  }
34
34
  }
package/src/daemon.mjs CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  lookupShareResponse,
24
24
  prepareShareHttpRequest,
25
25
  processShareHttpResponse,
26
- shouldRewriteBody,
26
+ shouldProcessShareResponse,
27
27
  } from "./share-rewrite.mjs";
28
28
 
29
29
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -2565,7 +2565,7 @@ function handleProxyHttpFromAdmin(msg) {
2565
2565
  }
2566
2566
  const status = res.statusCode || 502;
2567
2567
  applyShareCacheHeaders(outHeaders, path);
2568
- if (shouldRewriteBody(outHeaders)) {
2568
+ if (shouldProcessShareResponse(outHeaders)) {
2569
2569
  /** @type {Buffer[]} */
2570
2570
  const chunks = [];
2571
2571
  res.on("data", (chunk) => {
@@ -5200,6 +5200,12 @@ async function main() {
5200
5200
 
5201
5201
  if (args.pair || !cfg.token || !cfg.adminUrl) {
5202
5202
  cfg = await pairFlow(args);
5203
+ } else if (typeof args.adminUrl === "string" && args.adminUrl.trim()) {
5204
+ const next = args.adminUrl.replace(/\/$/, "");
5205
+ if (cfg.adminUrl && cfg.adminUrl !== next) {
5206
+ log(`admin URL ${cfg.adminUrl} → ${next}`);
5207
+ }
5208
+ cfg.adminUrl = next;
5203
5209
  }
5204
5210
 
5205
5211
  cfg.noAiServer = Boolean(args.noAiServer);
@@ -5557,6 +5563,11 @@ async function main() {
5557
5563
  clearHeartbeatTimer();
5558
5564
  clearPingTimer();
5559
5565
  if (socket === ws) socket = null;
5566
+ if (Number(code) === 1008) {
5567
+ warn(
5568
+ "Admin rejected this computer’s token. This admin is not the one in ~/.maintainer-pro/bridge.json. Generate a new pair code (Admin → Bridges → Add computer) and run the pair command."
5569
+ );
5570
+ }
5560
5571
  scheduleReconnect(code, reason);
5561
5572
  };
5562
5573
 
@@ -128,6 +128,11 @@ export function shouldRewriteBody(headers) {
128
128
  );
129
129
  }
130
130
 
131
+ /** Redirects have no Content-Type, but Location still has to stay under the share prefix. */
132
+ export function shouldProcessShareResponse(headers) {
133
+ return Boolean(headerGet(headers, "location")) || shouldRewriteBody(headers);
134
+ }
135
+
131
136
  function isImmutableAssetPath(path) {
132
137
  const p = String(path || "").split("?")[0] || "";
133
138
  return (
@@ -237,17 +242,36 @@ function notModifiedResult(headers, etag) {
237
242
 
238
243
  function rewriteLocation(value, publicBase, port) {
239
244
  if (!publicBase) return value;
245
+ const base = String(publicBase).replace(/\/$/, "");
246
+ const prefix = proxyPathPrefix(publicBase);
240
247
  try {
241
248
  const u = new URL(value, `http://127.0.0.1:${Number(port) || 0}`);
242
- if (u.hostname === "127.0.0.1" || u.hostname === "localhost") {
243
- return `${String(publicBase).replace(/\/$/, "")}${u.pathname}${u.search}${u.hash}`;
249
+ const host = String(u.hostname || "").toLowerCase();
250
+ const local =
251
+ host === "127.0.0.1" ||
252
+ host === "localhost" ||
253
+ host === "::1" ||
254
+ host === "0.0.0.0";
255
+ if (!local) return value;
256
+ let path = u.pathname || "/";
257
+ if (prefix && (path === prefix || path.startsWith(`${prefix}/`))) {
258
+ path = path.slice(prefix.length) || "/";
244
259
  }
260
+ return `${base}${path}${u.search}${u.hash}`;
245
261
  } catch {
246
262
  /* keep */
247
263
  }
248
264
  return value;
249
265
  }
250
266
 
267
+ function rewriteLinkHeader(value, publicBase) {
268
+ const prefix = proxyPathPrefix(publicBase);
269
+ if (!prefix || !value) return value;
270
+ return String(value).replace(/<(\/(?!\/)[^>\s]*)>/g, (full, path) =>
271
+ alreadyPrefixed(path, prefix) ? full : `<${prefix}${path}>`
272
+ );
273
+ }
274
+
251
275
  function gzipIfRequested(payload, headers, acceptEncoding) {
252
276
  if (payload.length < 1024) return payload;
253
277
  if (!/\bgzip\b/i.test(acceptEncoding || "")) return payload;
@@ -348,26 +372,65 @@ function viteDevBases(body) {
348
372
  return bases;
349
373
  }
350
374
 
351
- function viteAssetRoots(body) {
352
- const roots = new Set([
353
- "/@vite",
354
- "/@react-refresh",
355
- "/@fs",
356
- "/@id",
357
- "/node_modules/",
358
- "/src/",
359
- "/config.json",
360
- ]);
361
- const re =
362
- /["'`](\/[^"'`]*?)(?=\/(?:@vite|@react-refresh|@fs|@id|node_modules\/|src\/))/g;
363
- let match;
364
- while ((match = re.exec(body))) {
365
- const base = match[1];
366
- if (!base || base === "/") continue;
367
- roots.add(base.endsWith("/") ? base : `${base}/`);
375
+ function inferViteBase(body) {
376
+ const fromConst = viteDevBases(body)[0];
377
+ if (fromConst) return fromConst;
378
+ const text = String(body || "");
379
+ const match = text.match(
380
+ /["'`](\/[^"'`]*?)\/(?:@vite(?:\/client)?|@react-refresh)(?:["'`?]|$)/
381
+ );
382
+ if (match?.[1] && match[1] !== "/") {
383
+ return match[1].endsWith("/") ? match[1] : `${match[1]}/`;
384
+ }
385
+ return "/";
386
+ }
387
+
388
+ /** Vite/Next module URLs that must stay on the share origin. App routes like /hub/settings are not assets. */
389
+ function isShareAssetPath(path) {
390
+ const p = String(path || "").split("?")[0];
391
+ if (!p.startsWith("/")) return false;
392
+ if (
393
+ /\/(?:_next\/|__nextjs_|@vite(?:\/|$)|@react-refresh(?:\/|$)|@fs\/|@id\/|node_modules\/)/.test(
394
+ p
395
+ )
396
+ ) {
397
+ return true;
398
+ }
399
+ if (/\/src\/[^/].+\.[A-Za-z0-9]+$/.test(p)) return true;
400
+ return /\.(?:m?[jt]sx?|cjs|mjs|css|scss|sass|less|map|wasm|vue|svelte)$/i.test(
401
+ p
402
+ );
403
+ }
404
+
405
+ function prefixQuotedAssetPaths(body, pathPrefix) {
406
+ if (!pathPrefix) return body;
407
+ return String(body).replace(/(["'`])(\/(?!\/)[^"'`]*)/g, (full, q, path) => {
408
+ if (alreadyPrefixed(path, pathPrefix)) return full;
409
+ if (!isShareAssetPath(path)) return full;
410
+ return `${q}${pathPrefix}${path}`;
411
+ });
412
+ }
413
+
414
+ function rewriteViteBaseLiterals(body, pathPrefix) {
415
+ let out = rewriteViteHmrClient(body, pathPrefix);
416
+ const bases = [...viteDevBases(body), inferViteBase(body)].filter(
417
+ (base, i, all) => base && base !== "/" && all.indexOf(base) === i
418
+ );
419
+ for (const viteBase of bases) {
420
+ const prefixed = joinProxyAndViteBase(pathPrefix, viteBase);
421
+ const withSlash = viteBase.endsWith("/") ? viteBase : `${viteBase}/`;
422
+ const escaped = withSlash.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
423
+ out = out.replace(
424
+ new RegExp(`(\\bconst base(?:\\$\\d+)?\\s*=\\s*)(["'])${escaped}\\2`, "g"),
425
+ `$1$2${prefixed}$2`
426
+ );
427
+ out = out.split(`BASE_URL:"${withSlash}"`).join(`BASE_URL:"${prefixed}"`);
428
+ out = out.split(`BASE_URL:'${withSlash}'`).join(`BASE_URL:'${prefixed}'`);
429
+ out = out
430
+ .split(`"BASE_URL":"${withSlash}"`)
431
+ .join(`"BASE_URL":"${prefixed}"`);
368
432
  }
369
- for (const base of viteDevBases(body)) roots.add(base);
370
- return [...roots];
433
+ return out;
371
434
  }
372
435
 
373
436
  function joinProxyAndViteBase(pathPrefix, viteBase) {
@@ -411,9 +474,38 @@ function alreadyPrefixed(path, pathPrefix) {
411
474
  return path === pathPrefix || path.startsWith(`${pathPrefix}/`);
412
475
  }
413
476
 
477
+ function rewriteNextFlightCanonical(body, pathPrefix) {
478
+ if (!pathPrefix || !body) return body;
479
+ const encoded = JSON.stringify(pathPrefix);
480
+ const escaped = pathPrefix.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
481
+ let out = body;
482
+ if (out.includes(`"c":["",""]`)) {
483
+ out = out.split(`"c":["",""]`).join(`"c":[${encoded}]`);
484
+ }
485
+ if (out.includes('\\"c\\":[\\"\\",\\"\\"]')) {
486
+ out = out
487
+ .split('\\"c\\":[\\"\\",\\"\\"]')
488
+ .join(`\\"c\\":[\\"${escaped}\\"]`);
489
+ }
490
+ out = out.replace(/("b":"[^"]+","p":)""/g, `$1${encoded}`);
491
+ out = out.replace(
492
+ /(\\?"b\\":\\?"[^"]+\\?",\\?"p\\":)\\?"\\?"/g,
493
+ `$1\\"${escaped}\\"`
494
+ );
495
+ if (out.includes(`"assetPrefix":""`)) {
496
+ out = out.split(`"assetPrefix":""`).join(`"assetPrefix":${encoded}`);
497
+ }
498
+ if (out.includes('\\"assetPrefix\\":\\"\\"')) {
499
+ out = out
500
+ .split('\\"assetPrefix\\":\\"\\"')
501
+ .join(`\\"assetPrefix\\":\\"${escaped}\\"`);
502
+ }
503
+ return out;
504
+ }
505
+
414
506
  function rewriteNextTurbopackBasePath(body, pathPrefix) {
415
507
  if (!pathPrefix) return body;
416
- let out = body;
508
+ let out = rewriteNextFlightCanonical(body, pathPrefix);
417
509
  if (out.includes("TURBOPACK compile-time value")) {
418
510
  const next = JSON.stringify(pathPrefix);
419
511
  out = out
@@ -454,10 +546,9 @@ function prefixRootPaths(body, publicBase, contentType = "") {
454
546
  mime === "application/xhtml+xml" ||
455
547
  (!mime && /<!doctype html|<html[\s>]/i.test(body)));
456
548
  const css = mime === "text/css" || (!isCode && !html && /css/.test(mime));
457
- const viteAndNext = ["/_next/", "/__nextjs_", ...viteAssetRoots(body)];
458
549
  if (!html && !css) {
459
- let code = rewriteViteHmrClient(body, pathPrefix);
460
- code = prefixQuotedRoots(code, pathPrefix, viteAndNext);
550
+ let code = rewriteViteBaseLiterals(body, pathPrefix);
551
+ code = prefixQuotedAssetPaths(code, pathPrefix);
461
552
  code = code.replace(
462
553
  /(?<![A-Za-z0-9])\/__nextjs_/g,
463
554
  `${pathPrefix}/__nextjs_`
@@ -472,10 +563,9 @@ function prefixRootPaths(body, publicBase, contentType = "") {
472
563
  !/<base\s/i.test(out) &&
473
564
  !isNextDocument(out)
474
565
  ) {
475
- out = out.replace(
476
- /<head([^>]*)>/i,
477
- `<head$1><base href="${pathPrefix}/">`
478
- );
566
+ const viteBase = isViteDocument(out) ? inferViteBase(out) : "/";
567
+ const href = joinProxyAndViteBase(pathPrefix, viteBase);
568
+ out = out.replace(/<head([^>]*)>/i, `<head$1><base href="${href}">`);
479
569
  }
480
570
  if (html) {
481
571
  out = out.replace(
@@ -486,6 +576,7 @@ function prefixRootPaths(body, publicBase, contentType = "") {
486
576
  out = out.replace(/\s+crossorigin(?:\s*=\s*(["']).*?\1)?/gi, "");
487
577
  }
488
578
  if (isNextDocument(out)) {
579
+ out = rewriteNextFlightCanonical(out, pathPrefix);
489
580
  out = prefixQuotedRoots(out, pathPrefix, ["/_next/", "/__nextjs_"]);
490
581
  out = out.replace(
491
582
  /(url\(\s*['"]?)(\/(?!\/)[^)"']*)/gi,
@@ -503,13 +594,7 @@ function prefixRootPaths(body, publicBase, contentType = "") {
503
594
  (full, start, path) =>
504
595
  alreadyPrefixed(path, pathPrefix) ? full : `${start}${pathPrefix}${path}`
505
596
  );
506
- for (const root of viteAndNext) {
507
- const escaped = root.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
508
- out = out.replace(
509
- new RegExp(`(?<![A-Za-z0-9])${escaped}`, "g"),
510
- `${pathPrefix}${root}`
511
- );
512
- }
597
+ out = prefixQuotedAssetPaths(out, pathPrefix);
513
598
  }
514
599
  return out;
515
600
  }
@@ -600,6 +685,179 @@ function injectMaintainerProEmbed(html, aiPublicBase) {
600
685
  return html + loader;
601
686
  }
602
687
 
688
+ /**
689
+ * In-page rewrite: navigation stays under the share prefix; fetch/XHR/WebSocket
690
+ * only touch same-origin Vite/Next assets. Other hosts are left unchanged.
691
+ */
692
+ function shareProxyShim(p) {
693
+ if (!p) return;
694
+ function collapse(path) {
695
+ var d = p + p;
696
+ while (path.indexOf(d) === 0) path = p + path.slice(d.length);
697
+ return path;
698
+ }
699
+ function isAsset(path) {
700
+ return (
701
+ /\/(?:_next\/|__nextjs_|@vite(?:\/|$)|@react-refresh(?:\/|$)|@fs\/|@id\/|node_modules\/)/.test(
702
+ path
703
+ ) ||
704
+ /\/src\/[^/].+\.[A-Za-z0-9]+(?:\?|$)/.test(path) ||
705
+ /\.(?:m?[jt]sx?|cjs|mjs|css|scss|sass|less|map|wasm|vue|svelte)(?:\?|$)/i.test(
706
+ path
707
+ )
708
+ );
709
+ }
710
+ function prefixPath(path) {
711
+ if (typeof path !== "string" || !path) return path;
712
+ if (path.charAt(0) === "/" && path.charAt(1) !== "/") {
713
+ if (path === p || path.indexOf(p + "/") === 0) return collapse(path);
714
+ var root = p.slice(0, p.lastIndexOf("/"));
715
+ if (root && (path === root || path.indexOf(root + "/") === 0)) return path;
716
+ if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return path;
717
+ return collapse(p + path);
718
+ }
719
+ return path;
720
+ }
721
+ function addNav(v) {
722
+ if (typeof v !== "string" || !v) return v;
723
+ if (v.charAt(0) === "/" && v.charAt(1) !== "/") return prefixPath(v);
724
+ try {
725
+ var nav = new URL(v, location.href);
726
+ if (nav.origin !== location.origin) return v;
727
+ nav.pathname = prefixPath(nav.pathname);
728
+ return nav.toString();
729
+ } catch (e) {}
730
+ return v;
731
+ }
732
+ function addNet(v) {
733
+ if (typeof v !== "string" || !v) return v;
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;
750
+ }
751
+ function mapSel(sel) {
752
+ if (typeof sel !== "string") return sel;
753
+ var pairs = [
754
+ ['src="/_next/', 'src="' + p + "/_next/"],
755
+ ['href="/_next/', 'href="' + p + "/_next/"],
756
+ ];
757
+ for (var i = 0; i < pairs.length; i++) {
758
+ if (sel.indexOf(pairs[i][0]) !== -1) return sel.split(pairs[i][0]).join(pairs[i][1]);
759
+ }
760
+ return sel;
761
+ }
762
+ function patchQS(proto, name) {
763
+ var orig = proto[name];
764
+ proto[name] = function (sel) {
765
+ var r = orig.call(this, sel);
766
+ if (name === "querySelector") {
767
+ if (r) return r;
768
+ } else if (r.length) return r;
769
+ var s2 = mapSel(sel);
770
+ return s2 === sel ? r : orig.call(this, s2);
771
+ };
772
+ }
773
+ patchQS(Document.prototype, "querySelector");
774
+ patchQS(Document.prototype, "querySelectorAll");
775
+ patchQS(Element.prototype, "querySelector");
776
+ patchQS(Element.prototype, "querySelectorAll");
777
+ var sa = Element.prototype.setAttribute;
778
+ Element.prototype.setAttribute = function (n, v) {
779
+ if ((n === "src" || n === "href") && typeof v === "string") v = addNav(v);
780
+ return sa.call(this, n, v);
781
+ };
782
+ function patchUrlProp(ctor, prop) {
783
+ try {
784
+ var d = Object.getOwnPropertyDescriptor(ctor.prototype, prop);
785
+ if (!d || !d.set) return;
786
+ Object.defineProperty(ctor.prototype, prop, {
787
+ configurable: true,
788
+ enumerable: true,
789
+ get: d.get,
790
+ set: function (v) {
791
+ d.set.call(this, addNav(v));
792
+ },
793
+ });
794
+ } catch (e) {}
795
+ }
796
+ patchUrlProp(HTMLScriptElement, "src");
797
+ patchUrlProp(HTMLLinkElement, "href");
798
+ var f = window.fetch;
799
+ window.fetch = function (input, init) {
800
+ if (typeof input === "string") input = addNet(input);
801
+ else if (typeof Request !== "undefined" && input instanceof Request) {
802
+ input = new Request(addNet(input.url), input);
803
+ } else if (typeof URL !== "undefined" && input instanceof URL) {
804
+ input = new URL(addNet(input.href));
805
+ }
806
+ return f.call(this, input, init);
807
+ };
808
+ var xo = XMLHttpRequest.prototype.open;
809
+ XMLHttpRequest.prototype.open = function (m, u) {
810
+ if (typeof u === "string") arguments[1] = addNet(u);
811
+ return xo.apply(this, arguments);
812
+ };
813
+ var ps = history.pushState.bind(history);
814
+ history.pushState = function (s, t, u) {
815
+ if (typeof u === "string") u = addNav(u);
816
+ return ps(s, t, u);
817
+ };
818
+ var rs = history.replaceState.bind(history);
819
+ history.replaceState = function (s, t, u) {
820
+ if (typeof u === "string") u = addNav(u);
821
+ return rs(s, t, u);
822
+ };
823
+ var WS = window.WebSocket;
824
+ function WrappedWS(url, protocols) {
825
+ if (typeof url === "string") url = addNet(url);
826
+ return protocols !== undefined ? new WS(url, protocols) : new WS(url);
827
+ }
828
+ WrappedWS.prototype = WS.prototype;
829
+ WrappedWS.CONNECTING = WS.CONNECTING;
830
+ WrappedWS.OPEN = WS.OPEN;
831
+ WrappedWS.CLOSING = WS.CLOSING;
832
+ WrappedWS.CLOSED = WS.CLOSED;
833
+ window.WebSocket = WrappedWS;
834
+ try {
835
+ var la = location.assign.bind(location);
836
+ location.assign = function (u) {
837
+ return la(addNav(String(u)));
838
+ };
839
+ var lr = location.replace.bind(location);
840
+ location.replace = function (u) {
841
+ return lr(addNav(String(u)));
842
+ };
843
+ } catch (e) {}
844
+ try {
845
+ var hd = Object.getOwnPropertyDescriptor(Location.prototype, "href");
846
+ if (hd && hd.set) {
847
+ Object.defineProperty(location, "href", {
848
+ configurable: true,
849
+ enumerable: true,
850
+ get: function () {
851
+ return hd.get.call(location);
852
+ },
853
+ set: function (v) {
854
+ hd.set.call(location, addNav(String(v)));
855
+ },
856
+ });
857
+ }
858
+ } catch (e) {}
859
+ }
860
+
603
861
  function injectNextProxyShim(html, publicBase) {
604
862
  const prefix = proxyPathPrefix(publicBase);
605
863
  if (
@@ -609,8 +867,7 @@ function injectNextProxyShim(html, publicBase) {
609
867
  ) {
610
868
  return html;
611
869
  }
612
- const p = JSON.stringify(prefix);
613
- const script = `<script data-mp-proxy-next>(function(p){if(!p)return;function add(v){if(typeof v!=="string"||!v)return v;if(v.charAt(0)==="/"&&v.charAt(1)!=="/"){if(v===p||v.indexOf(p+"/")===0)return v;var root=p.slice(0,p.lastIndexOf("/"));if(root&&(v===root||v.indexOf(root+"/")===0)return v;if(v==="/api/v1"||v.indexOf("/api/v1/")===0)return v;return p+v}try{var u=new URL(v,location.href);var host=String(u.hostname||"").toLowerCase();if(host.charAt(0)==="["&&host.charAt(host.length-1)==="]")host=host.slice(1,-1);var local=host==="localhost"||host==="127.0.0.1"||host==="::1"||host==="0.0.0.0";if(!local){if(u.pathname===p){u.pathname="/";return u.toString()}if(u.pathname.indexOf(p+"/")===0){u.pathname=u.pathname.slice(p.length)||"/";return u.toString()}return v}var samePort=String(u.port||"")===String(location.port||"");if(samePort){u.pathname=add(u.pathname);return u.toString()}if(u.pathname===p||u.pathname.indexOf(p+"/")===0){u.protocol=location.protocol;u.host=location.host;return u.toString()}}catch(e){}return v}function mapSel(sel){if(typeof sel!=="string")return sel;var pairs=[["src=\\"/_next/","src=\\""+p+"/_next/"],["href=\\"/_next/","href=\\""+p+"/_next/"]];for(var i=0;i<pairs.length;i++){if(sel.indexOf(pairs[i][0])!==-1)return sel.split(pairs[i][0]).join(pairs[i][1])}return sel}function patchQS(proto,name){var orig=proto[name];proto[name]=function(sel){var r=orig.call(this,sel);if(name==="querySelector"){if(r)return r}else if(r.length)return r;var s2=mapSel(sel);return s2===sel?r:orig.call(this,s2)}}patchQS(Document.prototype,"querySelector");patchQS(Document.prototype,"querySelectorAll");patchQS(Element.prototype,"querySelector");patchQS(Element.prototype,"querySelectorAll");var sa=Element.prototype.setAttribute;Element.prototype.setAttribute=function(n,v){if((n==="src"||n==="href")&&typeof v==="string")v=add(v);return sa.call(this,n,v)};function patchUrlProp(ctor,prop){try{var d=Object.getOwnPropertyDescriptor(ctor.prototype,prop);if(!d||!d.set)return;Object.defineProperty(ctor.prototype,prop,{configurable:true,enumerable:true,get:d.get,set:function(v){d.set.call(this,add(v))}})}catch(e){}}patchUrlProp(HTMLScriptElement,"src");patchUrlProp(HTMLLinkElement,"href");var f=window.fetch;window.fetch=function(input,init){if(typeof input==="string")input=add(input);else if(typeof Request!=="undefined"&&input instanceof Request)input=new Request(add(input.url),input);else if(typeof URL!=="undefined"&&input instanceof URL)input=new URL(add(input.href));return f.call(this,input,init)};var xo=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(m,u){if(typeof u==="string")arguments[1]=add(u);return xo.apply(this,arguments)};var ps=history.pushState.bind(history);history.pushState=function(s,t,u){if(typeof u==="string")u=add(u);return ps(s,t,u)};var rs=history.replaceState.bind(history);history.replaceState=function(s,t,u){if(typeof u==="string")u=add(u);return rs(s,t,u)};var WS=window.WebSocket;function WrappedWS(url,protocols){if(typeof url==="string")url=add(url);return protocols!==undefined?new WS(url,protocols):new WS(url)}WrappedWS.prototype=WS.prototype;WrappedWS.CONNECTING=WS.CONNECTING;WrappedWS.OPEN=WS.OPEN;WrappedWS.CLOSING=WS.CLOSING;WrappedWS.CLOSED=WS.CLOSED;window.WebSocket=WrappedWS})(${p})</script>`;
870
+ const script = `<script data-mp-proxy-next>(${shareProxyShim.toString()})(${JSON.stringify(prefix)})</script>`;
614
871
  return html.replace(/<head([^>]*)>/i, `<head$1>${script}`);
615
872
  }
616
873
 
@@ -649,6 +906,9 @@ export function processShareHttpResponse(opts) {
649
906
  if (headers.location) {
650
907
  headers.location = rewriteLocation(headers.location, publicBase, port);
651
908
  }
909
+ if (headers.link) {
910
+ headers.link = rewriteLinkHeader(headers.link, publicBase);
911
+ }
652
912
 
653
913
  const cachedKey = rewriteCacheKey(publicBase, path);
654
914
  const hit =