9remote 3.0.34 → 3.0.39

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.
Files changed (258) hide show
  1. package/dist/cli.cjs +144 -142
  2. package/dist/extension/background.js +55 -19
  3. package/dist/extension/manifest.json +1 -1
  4. package/dist/ptyDaemon.cjs +2 -2
  5. package/dist/server.cjs +729 -517
  6. package/dist/ui/assets/index-BKC13tXO.js +8 -0
  7. package/dist/ui/index.html +1 -1
  8. package/dist/web/404.html +1 -1
  9. package/dist/web/__next.__PAGE__.txt +2 -2
  10. package/dist/web/__next._full.txt +8 -8
  11. package/dist/web/__next._head.txt +1 -1
  12. package/dist/web/__next._index.txt +7 -7
  13. package/dist/web/__next._tree.txt +2 -2
  14. package/dist/web/_next/static/chunks/1222.16edfbb0beb9081f.js +1 -0
  15. package/dist/web/_next/static/chunks/1228.c4aa83586b3730b6.js +1 -0
  16. package/dist/web/_next/static/chunks/1243.3f23d89283c156ea.js +1 -0
  17. package/dist/web/_next/static/chunks/132.71f657e5a607b7d3.js +1 -0
  18. package/dist/web/_next/static/chunks/1750.ea89a41e86ac2790.js +1 -0
  19. package/dist/web/_next/static/chunks/2038.b77e2c3d38385a76.js +1 -0
  20. package/dist/web/_next/static/chunks/213.ae43f03862ad9cc1.js +1 -0
  21. package/dist/web/_next/static/chunks/2180.edcf5dc3d77ceee8.js +1 -0
  22. package/dist/web/_next/static/chunks/2849.d810c3fdb2054b14.js +1 -0
  23. package/dist/web/_next/static/chunks/3429.e69c6bd48e1207a9.js +1 -0
  24. package/dist/web/_next/static/chunks/3514.20bd03c8194dec25.js +1 -0
  25. package/dist/web/_next/static/chunks/{4300-7d6c9b3328075562.js → 4300-a66b52c9e1a27db3.js} +1 -1
  26. package/dist/web/_next/static/chunks/4643.1e356e007756e19d.js +1 -0
  27. package/dist/web/_next/static/chunks/5788-f0c87f9c14fa0ff9.js +1 -0
  28. package/dist/web/_next/static/chunks/6419.34fdd4aa97c5cc5d.js +1 -0
  29. package/dist/web/_next/static/chunks/674.b2f5d5fc3fb84c4a.js +1 -0
  30. package/dist/web/_next/static/chunks/6742.88fddf128fbeb43d.js +1 -0
  31. package/dist/web/_next/static/chunks/7507.77d9ac9eb03518f7.js +1 -0
  32. package/dist/web/_next/static/chunks/7960.2754ce9111bef59e.js +1 -0
  33. package/dist/web/_next/static/chunks/8467.c2c0273ac5e97e34.js +1 -0
  34. package/dist/web/_next/static/chunks/8638.f44546e4532d76e8.js +1 -0
  35. package/dist/web/_next/static/chunks/{8762-cb470d05070f5ead.js → 8762-e88a7f91d20d82bd.js} +1 -1
  36. package/dist/web/_next/static/chunks/8922-4c589850b01d2e12.js +1 -0
  37. package/dist/web/_next/static/chunks/925.7cfb725e605559a0.js +1 -0
  38. package/dist/web/_next/static/chunks/9259.97e2f3306fc149c7.js +1 -0
  39. package/dist/web/_next/static/chunks/9299.e37149b455be7e9d.js +1 -0
  40. package/dist/web/_next/static/chunks/app/dev/question/page-c7128ddf798789f1.js +1 -0
  41. package/dist/web/_next/static/chunks/app/login/{page-edc7457ddecf9262.js → page-2a046d10af50c87a.js} +1 -1
  42. package/dist/web/_next/static/chunks/app/{page-b76772608a876c23.js → page-8387dba850541259.js} +2 -2
  43. package/dist/web/_next/static/chunks/app/workspace/layout-9b28454945238595.js +20 -0
  44. package/dist/web/_next/static/chunks/webpack-beef15eb580fc66b.js +1 -0
  45. package/dist/web/_next/static/css/c48f9629be2a67b4.css +1 -0
  46. package/dist/web/_not-found/__next._full.txt +7 -7
  47. package/dist/web/_not-found/__next._head.txt +1 -1
  48. package/dist/web/_not-found/__next._index.txt +7 -7
  49. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +1 -1
  50. package/dist/web/_not-found/__next._not-found.txt +1 -1
  51. package/dist/web/_not-found/__next._tree.txt +2 -2
  52. package/dist/web/_not-found.html +1 -1
  53. package/dist/web/_not-found.txt +7 -7
  54. package/dist/web/admin/__next._full.txt +8 -8
  55. package/dist/web/admin/__next._head.txt +1 -1
  56. package/dist/web/admin/__next._index.txt +7 -7
  57. package/dist/web/admin/__next._tree.txt +2 -2
  58. package/dist/web/admin/__next.admin.__PAGE__.txt +2 -2
  59. package/dist/web/admin/__next.admin.txt +1 -1
  60. package/dist/web/admin/access/__next._full.txt +8 -8
  61. package/dist/web/admin/access/__next._head.txt +1 -1
  62. package/dist/web/admin/access/__next._index.txt +7 -7
  63. package/dist/web/admin/access/__next._tree.txt +2 -2
  64. package/dist/web/admin/access/__next.admin.access.__PAGE__.txt +2 -2
  65. package/dist/web/admin/access/__next.admin.access.txt +1 -1
  66. package/dist/web/admin/access/__next.admin.txt +1 -1
  67. package/dist/web/admin/access.html +1 -1
  68. package/dist/web/admin/access.txt +8 -8
  69. package/dist/web/admin/login/__next._full.txt +8 -8
  70. package/dist/web/admin/login/__next._head.txt +1 -1
  71. package/dist/web/admin/login/__next._index.txt +7 -7
  72. package/dist/web/admin/login/__next._tree.txt +2 -2
  73. package/dist/web/admin/login/__next.admin.login.__PAGE__.txt +2 -2
  74. package/dist/web/admin/login/__next.admin.login.txt +1 -1
  75. package/dist/web/admin/login/__next.admin.txt +1 -1
  76. package/dist/web/admin/login.html +1 -1
  77. package/dist/web/admin/login.txt +8 -8
  78. package/dist/web/admin/ota/__next._full.txt +8 -8
  79. package/dist/web/admin/ota/__next._head.txt +1 -1
  80. package/dist/web/admin/ota/__next._index.txt +7 -7
  81. package/dist/web/admin/ota/__next._tree.txt +2 -2
  82. package/dist/web/admin/ota/__next.admin.ota.__PAGE__.txt +2 -2
  83. package/dist/web/admin/ota/__next.admin.ota.txt +1 -1
  84. package/dist/web/admin/ota/__next.admin.txt +1 -1
  85. package/dist/web/admin/ota.html +1 -1
  86. package/dist/web/admin/ota.txt +8 -8
  87. package/dist/web/admin/turn/__next._full.txt +8 -8
  88. package/dist/web/admin/turn/__next._head.txt +1 -1
  89. package/dist/web/admin/turn/__next._index.txt +7 -7
  90. package/dist/web/admin/turn/__next._tree.txt +2 -2
  91. package/dist/web/admin/turn/__next.admin.turn.__PAGE__.txt +2 -2
  92. package/dist/web/admin/turn/__next.admin.turn.txt +1 -1
  93. package/dist/web/admin/turn/__next.admin.txt +1 -1
  94. package/dist/web/admin/turn.html +1 -1
  95. package/dist/web/admin/turn.txt +8 -8
  96. package/dist/web/admin.html +1 -1
  97. package/dist/web/admin.txt +8 -8
  98. package/dist/web/dev/question/__next._full.txt +8 -8
  99. package/dist/web/dev/question/__next._head.txt +1 -1
  100. package/dist/web/dev/question/__next._index.txt +7 -7
  101. package/dist/web/dev/question/__next._tree.txt +2 -2
  102. package/dist/web/dev/question/__next.dev.question.__PAGE__.txt +2 -2
  103. package/dist/web/dev/question/__next.dev.question.txt +1 -1
  104. package/dist/web/dev/question/__next.dev.txt +1 -1
  105. package/dist/web/dev/question.html +1 -1
  106. package/dist/web/dev/question.txt +8 -8
  107. package/dist/web/download/__next._full.txt +8 -8
  108. package/dist/web/download/__next._head.txt +1 -1
  109. package/dist/web/download/__next._index.txt +7 -7
  110. package/dist/web/download/__next._tree.txt +2 -2
  111. package/dist/web/download/__next.download.__PAGE__.txt +2 -2
  112. package/dist/web/download/__next.download.txt +1 -1
  113. package/dist/web/download.html +1 -1
  114. package/dist/web/download.txt +8 -8
  115. package/dist/web/index.html +1 -1
  116. package/dist/web/index.txt +8 -8
  117. package/dist/web/login/__next._full.txt +8 -8
  118. package/dist/web/login/__next._head.txt +1 -1
  119. package/dist/web/login/__next._index.txt +7 -7
  120. package/dist/web/login/__next._tree.txt +2 -2
  121. package/dist/web/login/__next.login.__PAGE__.txt +2 -2
  122. package/dist/web/login/__next.login.txt +1 -1
  123. package/dist/web/login.html +1 -1
  124. package/dist/web/login.txt +8 -8
  125. package/dist/web/workspace/__next._full.txt +8 -8
  126. package/dist/web/workspace/__next._head.txt +1 -1
  127. package/dist/web/workspace/__next._index.txt +7 -7
  128. package/dist/web/workspace/__next._tree.txt +2 -2
  129. package/dist/web/workspace/__next.workspace.__PAGE__.txt +1 -1
  130. package/dist/web/workspace/__next.workspace.txt +2 -2
  131. package/dist/web/workspace/browse/__next._full.txt +8 -8
  132. package/dist/web/workspace/browse/__next._head.txt +1 -1
  133. package/dist/web/workspace/browse/__next._index.txt +7 -7
  134. package/dist/web/workspace/browse/__next._tree.txt +2 -2
  135. package/dist/web/workspace/browse/__next.workspace.browse.__PAGE__.txt +1 -1
  136. package/dist/web/workspace/browse/__next.workspace.browse.txt +1 -1
  137. package/dist/web/workspace/browse/__next.workspace.txt +2 -2
  138. package/dist/web/workspace/browse.html +1 -1
  139. package/dist/web/workspace/browse.txt +8 -8
  140. package/dist/web/workspace/editor/__next._full.txt +8 -8
  141. package/dist/web/workspace/editor/__next._head.txt +1 -1
  142. package/dist/web/workspace/editor/__next._index.txt +7 -7
  143. package/dist/web/workspace/editor/__next._tree.txt +2 -2
  144. package/dist/web/workspace/editor/__next.workspace.editor.__PAGE__.txt +1 -1
  145. package/dist/web/workspace/editor/__next.workspace.editor.txt +1 -1
  146. package/dist/web/workspace/editor/__next.workspace.txt +2 -2
  147. package/dist/web/workspace/editor.html +1 -1
  148. package/dist/web/workspace/editor.txt +8 -8
  149. package/dist/web/workspace/files/__next._full.txt +8 -8
  150. package/dist/web/workspace/files/__next._head.txt +1 -1
  151. package/dist/web/workspace/files/__next._index.txt +7 -7
  152. package/dist/web/workspace/files/__next._tree.txt +2 -2
  153. package/dist/web/workspace/files/__next.workspace.files.__PAGE__.txt +1 -1
  154. package/dist/web/workspace/files/__next.workspace.files.txt +1 -1
  155. package/dist/web/workspace/files/__next.workspace.txt +2 -2
  156. package/dist/web/workspace/files.html +1 -1
  157. package/dist/web/workspace/files.txt +8 -8
  158. package/dist/web/workspace/git/__next._full.txt +8 -8
  159. package/dist/web/workspace/git/__next._head.txt +1 -1
  160. package/dist/web/workspace/git/__next._index.txt +7 -7
  161. package/dist/web/workspace/git/__next._tree.txt +2 -2
  162. package/dist/web/workspace/git/__next.workspace.git.__PAGE__.txt +1 -1
  163. package/dist/web/workspace/git/__next.workspace.git.txt +1 -1
  164. package/dist/web/workspace/git/__next.workspace.txt +2 -2
  165. package/dist/web/workspace/git.html +1 -1
  166. package/dist/web/workspace/git.txt +8 -8
  167. package/dist/web/workspace/mobile/__next._full.txt +8 -8
  168. package/dist/web/workspace/mobile/__next._head.txt +1 -1
  169. package/dist/web/workspace/mobile/__next._index.txt +7 -7
  170. package/dist/web/workspace/mobile/__next._tree.txt +2 -2
  171. package/dist/web/workspace/mobile/__next.workspace.mobile.__PAGE__.txt +1 -1
  172. package/dist/web/workspace/mobile/__next.workspace.mobile.txt +1 -1
  173. package/dist/web/workspace/mobile/__next.workspace.txt +2 -2
  174. package/dist/web/workspace/mobile.html +1 -1
  175. package/dist/web/workspace/mobile.txt +8 -8
  176. package/dist/web/workspace/pair/__next._full.txt +8 -8
  177. package/dist/web/workspace/pair/__next._head.txt +1 -1
  178. package/dist/web/workspace/pair/__next._index.txt +7 -7
  179. package/dist/web/workspace/pair/__next._tree.txt +2 -2
  180. package/dist/web/workspace/pair/__next.workspace.pair.__PAGE__.txt +1 -1
  181. package/dist/web/workspace/pair/__next.workspace.pair.txt +1 -1
  182. package/dist/web/workspace/pair/__next.workspace.txt +2 -2
  183. package/dist/web/workspace/pair.html +1 -1
  184. package/dist/web/workspace/pair.txt +8 -8
  185. package/dist/web/workspace/remote/__next._full.txt +8 -8
  186. package/dist/web/workspace/remote/__next._head.txt +1 -1
  187. package/dist/web/workspace/remote/__next._index.txt +7 -7
  188. package/dist/web/workspace/remote/__next._tree.txt +2 -2
  189. package/dist/web/workspace/remote/__next.workspace.remote.__PAGE__.txt +1 -1
  190. package/dist/web/workspace/remote/__next.workspace.remote.txt +1 -1
  191. package/dist/web/workspace/remote/__next.workspace.txt +2 -2
  192. package/dist/web/workspace/remote.html +1 -1
  193. package/dist/web/workspace/remote.txt +8 -8
  194. package/dist/web/workspace/terminal/default/__next._full.txt +22 -21
  195. package/dist/web/workspace/terminal/default/__next._head.txt +1 -1
  196. package/dist/web/workspace/terminal/default/__next._index.txt +7 -7
  197. package/dist/web/workspace/terminal/default/__next._tree.txt +2 -2
  198. package/dist/web/workspace/terminal/default/__next.workspace.terminal.$d$sessionId.__PAGE__.txt +1 -1
  199. package/dist/web/workspace/terminal/default/__next.workspace.terminal.$d$sessionId.txt +1 -1
  200. package/dist/web/workspace/terminal/default/__next.workspace.terminal.txt +1 -1
  201. package/dist/web/workspace/terminal/default/__next.workspace.txt +2 -2
  202. package/dist/web/workspace/terminal/default.html +1 -1
  203. package/dist/web/workspace/terminal/default.txt +22 -21
  204. package/dist/web/workspace/welcome/__next._full.txt +8 -8
  205. package/dist/web/workspace/welcome/__next._head.txt +1 -1
  206. package/dist/web/workspace/welcome/__next._index.txt +7 -7
  207. package/dist/web/workspace/welcome/__next._tree.txt +2 -2
  208. package/dist/web/workspace/welcome/__next.workspace.txt +2 -2
  209. package/dist/web/workspace/welcome/__next.workspace.welcome.__PAGE__.txt +1 -1
  210. package/dist/web/workspace/welcome/__next.workspace.welcome.txt +1 -1
  211. package/dist/web/workspace/welcome.html +1 -1
  212. package/dist/web/workspace/welcome.txt +8 -8
  213. package/dist/web/workspace/workspaces/__next._full.txt +8 -8
  214. package/dist/web/workspace/workspaces/__next._head.txt +1 -1
  215. package/dist/web/workspace/workspaces/__next._index.txt +7 -7
  216. package/dist/web/workspace/workspaces/__next._tree.txt +2 -2
  217. package/dist/web/workspace/workspaces/__next.workspace.txt +2 -2
  218. package/dist/web/workspace/workspaces/__next.workspace.workspaces.__PAGE__.txt +1 -1
  219. package/dist/web/workspace/workspaces/__next.workspace.workspaces.txt +1 -1
  220. package/dist/web/workspace/workspaces.html +1 -1
  221. package/dist/web/workspace/workspaces.txt +8 -8
  222. package/dist/web/workspace.html +1 -1
  223. package/dist/web/workspace.txt +8 -8
  224. package/package.json +1 -1
  225. package/dist/ui/assets/index-DbuftNJF.js +0 -8
  226. package/dist/web/_next/static/chunks/1222.a2ce7d7f04241e2a.js +0 -1
  227. package/dist/web/_next/static/chunks/1228.046ad5fd038f5192.js +0 -1
  228. package/dist/web/_next/static/chunks/1243.1d11020940b8a88b.js +0 -1
  229. package/dist/web/_next/static/chunks/132.1ed2fce3a4b6f7ee.js +0 -1
  230. package/dist/web/_next/static/chunks/1750.09ca4eade7a6e5d3.js +0 -1
  231. package/dist/web/_next/static/chunks/2038.b7c72b442a1b2d92.js +0 -1
  232. package/dist/web/_next/static/chunks/213.f07a816120a320ba.js +0 -1
  233. package/dist/web/_next/static/chunks/2180.23585bd3f3308f3a.js +0 -1
  234. package/dist/web/_next/static/chunks/2849.360e2c64521fa795.js +0 -1
  235. package/dist/web/_next/static/chunks/3429.98d7e79ff6d2a6ce.js +0 -1
  236. package/dist/web/_next/static/chunks/3514.9079bf49bb3e05c9.js +0 -1
  237. package/dist/web/_next/static/chunks/4643.ddf3ea3a760e73a5.js +0 -1
  238. package/dist/web/_next/static/chunks/5788-d4b8dc2ab68396b6.js +0 -1
  239. package/dist/web/_next/static/chunks/6419.99aa173d83f20370.js +0 -1
  240. package/dist/web/_next/static/chunks/674.97a7237f011fb986.js +0 -1
  241. package/dist/web/_next/static/chunks/6742.bc3783b9784ead96.js +0 -1
  242. package/dist/web/_next/static/chunks/7507.28a3807b5d02a5c1.js +0 -1
  243. package/dist/web/_next/static/chunks/7960.026e07028dd25edb.js +0 -1
  244. package/dist/web/_next/static/chunks/8467.290e9bde34b58e7c.js +0 -1
  245. package/dist/web/_next/static/chunks/8638.237712645dcdcb8a.js +0 -1
  246. package/dist/web/_next/static/chunks/8922-8660f73e9cd42406.js +0 -1
  247. package/dist/web/_next/static/chunks/925.fe600c6039a3201d.js +0 -1
  248. package/dist/web/_next/static/chunks/9259.c450f561db04037d.js +0 -1
  249. package/dist/web/_next/static/chunks/9299.83236781dfa74eb0.js +0 -1
  250. package/dist/web/_next/static/chunks/app/dev/question/page-bdedfcfce7a3c516.js +0 -1
  251. package/dist/web/_next/static/chunks/app/workspace/layout-0fd9ea03182e9c89.js +0 -15
  252. package/dist/web/_next/static/chunks/webpack-f3500fd1fae71c88.js +0 -1
  253. package/dist/web/_next/static/css/1b307dedbb687210.css +0 -1
  254. package/dist/web/proxy.html +0 -179
  255. package/dist/web/sw-site.js +0 -283
  256. package/dist/web/swBridgeClaim.js +0 -57
  257. /package/dist/web/_next/static/{DdbafQkcX_ZaKRMjMeNKp → hFRLMLaLieW6POo9cLvLQ}/_buildManifest.js +0 -0
  258. /package/dist/web/_next/static/{DdbafQkcX_ZaKRMjMeNKp → hFRLMLaLieW6POo9cLvLQ}/_ssgManifest.js +0 -0
@@ -1,283 +0,0 @@
1
- // Site proxy service worker: serves /browse/<port>/... documents via transport bridge.
2
-
3
- importScripts("/swBridgeClaim.js");
4
-
5
- const BROWSE_RE = /^\/browse\/(\d{1,5})(\/.*)?$/;
6
- const BRIDGE_TIMEOUT_MS = 60000;
7
- const BRIDGE_DISCOVER_MS = 3000;
8
- const MAX_REQUEST_BODY_BYTES = 512 * 1024;
9
- const REDIRECT_STATUSES = [301, 302, 303, 307, 308];
10
-
11
- // Restrict framing and origin communication via Content Security Policy.
12
- const SITE_CSP = [
13
- "connect-src 'self'",
14
- "form-action 'self'",
15
- `frame-ancestors 'self' ${appOriginsFor(self.location.hostname, self.location.port)}`.trim(),
16
- "base-uri 'self'"
17
- ].join("; ");
18
-
19
- let bridgeClient = null;
20
- let bridgeDiscover = null;
21
- let bridgeResolve = null;
22
- let reqCounter = 0;
23
-
24
- self.addEventListener("install", (event) => event.waitUntil(self.skipWaiting()));
25
- self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
26
-
27
- self.addEventListener("message", (event) => {
28
- const msg = event.data;
29
- if (!msg || typeof msg !== "object") return;
30
- if (msg.type === "bridge-hello") {
31
- // Verify source is shell to prevent cross-site privilege escalation.
32
- if (!canBeBridge(event.source?.url, self.location.origin)) return;
33
- bridgeClient = event.source;
34
- if (bridgeResolve) {
35
- const resolve = bridgeResolve;
36
- bridgeDiscover = null;
37
- bridgeResolve = null;
38
- resolve();
39
- }
40
- return;
41
- }
42
- });
43
-
44
- function base64ToBytes(b64) {
45
- const bin = atob(b64 || "");
46
- const out = new Uint8Array(bin.length);
47
- for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
48
- return out;
49
- }
50
-
51
- function bytesToBase64(buffer) {
52
- const bytes = new Uint8Array(buffer);
53
- let bin = "";
54
- const CHUNK = 0x8000;
55
- for (let i = 0; i < bytes.length; i += CHUNK) {
56
- bin += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
57
- }
58
- return btoa(bin);
59
- }
60
-
61
- function discoverBridge() {
62
- if (bridgeClient) return Promise.resolve();
63
- if (!bridgeDiscover) {
64
- bridgeDiscover = new Promise((resolve) => {
65
- bridgeResolve = resolve;
66
- setTimeout(() => {
67
- bridgeDiscover = null;
68
- bridgeResolve = null;
69
- resolve();
70
- }, BRIDGE_DISCOVER_MS);
71
- });
72
- self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => {
73
- for (const client of clients) {
74
- if (canBeBridge(client.url, self.location.origin)) client.postMessage({ type: "sw-hello" });
75
- }
76
- }).catch(() => {});
77
- }
78
- return bridgeDiscover;
79
- }
80
-
81
- // Injected script rewriting absolute links/forms into /browse/<port> scope.
82
- function inScopeScript(port) {
83
- return `<script data-9remote="site-proxy">
84
- (function () {
85
- if (window.__nineRemoteSiteProxy) return;
86
- window.__nineRemoteSiteProxy = true;
87
- var BASE = "/browse/${port}";
88
- function scoped(path) {
89
- if (!path || path.indexOf(BASE + "/") === 0 || path === BASE) return null;
90
- return path.charAt(0) === "/" ? BASE + path : null;
91
- }
92
- // Bubble phase allows SPA routers to handle owned routes first.
93
- document.addEventListener("click", function (e) {
94
- if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
95
- var a = e.target && e.target.closest && e.target.closest("a[href]");
96
- if (!a || a.target && a.target !== "_self" || a.hasAttribute("download")) return;
97
- var url;
98
- try { url = new URL(a.getAttribute("href"), location.href); } catch (err) { return; }
99
- if (url.origin !== location.origin) return;
100
- var next = scoped(url.pathname);
101
- if (!next) return;
102
- e.preventDefault();
103
- location.replace(next + url.search + url.hash);
104
- });
105
- document.addEventListener("submit", function (e) {
106
- if (e.defaultPrevented) return;
107
- var form = e.target;
108
- if (!form || form.tagName !== "FORM" || form.target && form.target !== "_self") return;
109
- var action = form.getAttribute("action");
110
- if (action === null) return;
111
- var url;
112
- try { url = new URL(action, location.href); } catch (err) { return; }
113
- if (url.origin !== location.origin) return;
114
- var next = scoped(url.pathname);
115
- if (next) form.setAttribute("action", next + url.search);
116
- }, true);
117
- // Downgrade pushState to replaceState so iframe history doesn't swallow parent Back.
118
- var nativeReplace = history.replaceState.bind(history);
119
- ["pushState", "replaceState"].forEach(function (name) {
120
- history[name] = function (state, title, path) {
121
- if (typeof path === "string") {
122
- try {
123
- var url = new URL(path, location.href);
124
- if (url.origin === location.origin) {
125
- var next = scoped(url.pathname);
126
- if (next) path = next + url.search + url.hash;
127
- }
128
- } catch (err) { /* leave path as-is */ }
129
- }
130
- return nativeReplace(state, title, path);
131
- };
132
- });
133
- })();
134
- </script>`;
135
- }
136
-
137
- function injectIntoHtml(html, port) {
138
- const tag = inScopeScript(port);
139
- const head = html.match(/<head[^>]*>/i);
140
- if (head) return html.replace(head[0], () => head[0] + tag);
141
- return tag + html;
142
- }
143
-
144
- async function portFromReferrer(request) {
145
- try {
146
- if (request.referrer) {
147
- const match = new URL(request.referrer).pathname.match(BROWSE_RE);
148
- if (match) return Number(match[1]);
149
- }
150
- } catch { /* bad referrer — fall through */ }
151
- const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: false });
152
- for (const client of clients) {
153
- const match = new URL(client.url).pathname.match(BROWSE_RE);
154
- if (match) return Number(match[1]);
155
- }
156
- return null;
157
- }
158
-
159
- // MessageChannel per request to keep concurrent requests isolated.
160
- function requestViaBridge(reqId, port, method, target, headers, bodyB64) {
161
- return new Promise((resolve, reject) => {
162
- const channel = new MessageChannel();
163
- const timer = setTimeout(() => {
164
- channel.port1.close();
165
- reject(new Error("bridge-timeout"));
166
- }, BRIDGE_TIMEOUT_MS);
167
- channel.port1.onmessage = (event) => {
168
- clearTimeout(timer);
169
- channel.port1.close();
170
- resolve(event.data);
171
- };
172
- bridgeClient.postMessage(
173
- { type: "http-request", reqId, port, method, target, headers, bodyB64 },
174
- [channel.port2]
175
- );
176
- });
177
- }
178
-
179
- function fail(message, status, port) {
180
- if (port) {
181
- self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => {
182
- for (const client of clients) {
183
- if (canBeBridge(client.url, self.location.origin)) client.postMessage({ type: "site-error", message, port });
184
- }
185
- }).catch(() => {});
186
- }
187
- return new Response(`9Remote: ${message}`, { status });
188
- }
189
-
190
- async function handle(request, url, inScope) {
191
- const port = inScope ? Number(inScope[1]) : await portFromReferrer(request);
192
- if (!port) return fail("no site context for this request", 503, null);
193
-
194
- // Prevent cross-site subresource access within shared origin.
195
- if (inScope && request.mode !== "navigate") {
196
- const from = await portFromReferrer(request);
197
- if (from && from !== port) {
198
- return new Response("9Remote: cross-site request refused", { status: 403 });
199
- }
200
- }
201
-
202
- await discoverBridge();
203
- if (!bridgeClient) return fail("bridge not connected — open the 9Remote app", 503, port);
204
-
205
- url.searchParams.delete("r");
206
- const path = inScope ? (inScope[2] || "/") : url.pathname;
207
- const target = path + (url.search || "");
208
-
209
- if (request.mode === "navigate") {
210
- bridgeClient.postMessage({ type: "site-nav", port, path });
211
- }
212
-
213
- const headers = {};
214
- request.headers.forEach((value, key) => {
215
- if (key.toLowerCase() !== "cookie") headers[key] = value;
216
- });
217
-
218
- let bodyB64 = null;
219
- if (request.method !== "GET" && request.method !== "HEAD") {
220
- const buf = await request.arrayBuffer();
221
- if (buf.byteLength > MAX_REQUEST_BODY_BYTES) {
222
- return new Response("9Remote: request body too large", { status: 413 });
223
- }
224
- if (buf.byteLength > 0) bodyB64 = bytesToBase64(buf);
225
- }
226
-
227
- const reqId = `sw-${Date.now()}-${++reqCounter}`;
228
- let msg;
229
- try {
230
- msg = await requestViaBridge(reqId, port, request.method, target, headers, bodyB64);
231
- } catch {
232
- return fail("request timed out", 504, port);
233
- }
234
- if (!msg || msg.error) return fail(msg?.error || "no reply from the agent", 502, port);
235
-
236
- const resHeaders = new Headers(msg.headers || {});
237
-
238
- const location = resHeaders.get("location");
239
- if (location && REDIRECT_STATUSES.includes(msg.status)) {
240
- let next = null;
241
- try {
242
- const abs = new URL(location, `http://localhost:${port}`);
243
- const sameSite = location.startsWith("/") || abs.host === `localhost:${port}`;
244
- if (sameSite) next = abs.pathname + abs.search;
245
- } catch { /* keep null */ }
246
- if (next) return Response.redirect(`/browse/${port}${next}`, msg.status);
247
- return Response.redirect(location, msg.status);
248
- }
249
-
250
- if (msg.status === 204 || msg.status === 304) {
251
- return new Response(null, { status: msg.status, headers: resHeaders });
252
- }
253
-
254
- // Enforce CSP on proxied site content.
255
- resHeaders.set("Content-Security-Policy", SITE_CSP);
256
-
257
- const bytes = base64ToBytes(msg.bodyB64);
258
- if ((resHeaders.get("content-type") || "").includes("text/html")) {
259
- const html = new TextDecoder("utf-8").decode(bytes);
260
- return new Response(injectIntoHtml(html, port), { status: msg.status, headers: resHeaders });
261
- }
262
- return new Response(bytes, { status: msg.status, headers: resHeaders });
263
- }
264
-
265
- self.addEventListener("fetch", (event) => {
266
- const request = event.request;
267
- let url;
268
- try { url = new URL(request.url); } catch { return; }
269
-
270
- if (url.origin !== self.location.origin) return;
271
- if (url.pathname === "/sw-site.js") return;
272
- if (url.pathname.startsWith("/cdn-cgi/")) return;
273
-
274
- const inScope = url.pathname.match(BROWSE_RE);
275
- if (inScope) {
276
- event.respondWith(handle(request, url, inScope));
277
- return;
278
- }
279
-
280
- // Handle root-relative subresource requests from in-scope pages.
281
- if (request.mode === "navigate") return;
282
- event.respondWith(handle(request, url, null));
283
- });
@@ -1,57 +0,0 @@
1
- // Which client may act as the bridge.
2
- //
3
- // The worker forwards every browsed request to whichever client announced
4
- // itself with "bridge-hello", and every browsed site lives on this same origin.
5
- // So a dev server rendering something the developer did not write can claim the
6
- // role, then see every other tab's requests — paths, headers, bodies — and
7
- // answer them with content of its own.
8
- //
9
- // The shell is the one page entitled to it, and the worker can tell: the shell
10
- // is served at /proxy.html, while sites are always under /browse/. Parsing the
11
- // URL rather than matching on the string is what makes /browse/3000/proxy.html
12
- // and /browse/3000/../proxy.html answer correctly.
13
- //
14
- // Loaded by sw-site.js through importScripts, so this is a classic script and
15
- // defines a global rather than exporting. The test reads and evaluates it.
16
- function canBeBridge(clientUrl, expectedOrigin) {
17
- if (typeof clientUrl !== "string" || !clientUrl) return false;
18
- let url;
19
- try {
20
- url = new URL(clientUrl);
21
- } catch {
22
- return false;
23
- }
24
- if (url.origin !== expectedOrigin) return false;
25
- // pathname is already normalised by the URL parser, so ../ is resolved and a
26
- // query or fragment cannot smuggle the shell's name into it.
27
- return url.pathname === "/proxy.html";
28
- }
29
-
30
- // The app origins that may sit above a browsed site. frame-ancestors is checked
31
- // against EVERY ancestor, not just the parent, so a site framed by the shell is
32
- // also framed by the app — naming only the shell blocks the load.
33
- //
34
- // The loopback entry is the agent's own deploy: there the app and the sites host
35
- // are two names on one server, and the app answers on any loopback name, so all
36
- // of them have to be listed. A single fixed name leaves the site unframable for
37
- // whichever spelling the app was not opened on.
38
- const APP_HOSTS_BY_SITES_HOST = {
39
- "sites.9remote.cc": ["https://9remote.cc"],
40
- "sites-dev.9remote.cc": ["https://dev.9remote.cc"],
41
- // No [::1]: CSP's frame-ancestors rejects an IPv6 literal as a source
42
- // expression — Chrome drops the whole directive over it — so a page opened on
43
- // that spelling cannot be named, and is left unsupported rather than silently
44
- // unframable. localhost and 127.0.0.1 are what the agent is actually reached on.
45
- "sites.localhost": ["http://localhost", "http://127.0.0.1"],
46
- "sites.127.0.0.1": ["http://127.0.0.1"]
47
- };
48
-
49
- // "" for an unknown host: the policy then names the shell alone, which is the
50
- // safe direction — a load fails rather than an unknown origin being allowed.
51
- // `port` is the sites origin's own port, and the app answers on the same one.
52
- function appOriginsFor(hostname, port) {
53
- const host = String(hostname || "").toLowerCase();
54
- const bases = APP_HOSTS_BY_SITES_HOST[host];
55
- if (!bases) return "";
56
- return bases.map((b) => (port ? `${b}:${port}` : b)).join(" ");
57
- }