webSocketMockSource.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. "use strict";
  2. var __defProp = Object.defineProperty;
  3. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  4. var __getOwnPropNames = Object.getOwnPropertyNames;
  5. var __hasOwnProp = Object.prototype.hasOwnProperty;
  6. var __export = (target, all) => {
  7. for (var name in all)
  8. __defProp(target, name, { get: all[name], enumerable: true });
  9. };
  10. var __copyProps = (to, from, except, desc) => {
  11. if (from && typeof from === "object" || typeof from === "function") {
  12. for (let key of __getOwnPropNames(from))
  13. if (!__hasOwnProp.call(to, key) && key !== except)
  14. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  15. }
  16. return to;
  17. };
  18. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  19. var webSocketMockSource_exports = {};
  20. __export(webSocketMockSource_exports, {
  21. source: () => source
  22. });
  23. module.exports = __toCommonJS(webSocketMockSource_exports);
  24. const source = `
  25. var __commonJS = obj => {
  26. let required = false;
  27. let result;
  28. return function __require() {
  29. if (!required) {
  30. required = true;
  31. let fn;
  32. for (const name in obj) { fn = obj[name]; break; }
  33. const module = { exports: {} };
  34. fn(module.exports, module);
  35. result = module.exports;
  36. }
  37. return result;
  38. }
  39. };
  40. var __export = (target, all) => {for (var name in all) target[name] = all[name];};
  41. var __toESM = mod => ({ ...mod, 'default': mod });
  42. var __toCommonJS = mod => ({ ...mod, __esModule: true });
  43. // packages/injected/src/webSocketMock.ts
  44. var webSocketMock_exports = {};
  45. __export(webSocketMock_exports, {
  46. inject: () => inject
  47. });
  48. module.exports = __toCommonJS(webSocketMock_exports);
  49. function inject(globalThis) {
  50. if (globalThis.__pwWebSocketDispatch)
  51. return;
  52. function generateId() {
  53. const bytes = new Uint8Array(32);
  54. globalThis.crypto.getRandomValues(bytes);
  55. const hex = "0123456789abcdef";
  56. return [...bytes].map((value) => {
  57. const high = Math.floor(value / 16);
  58. const low = value % 16;
  59. return hex[high] + hex[low];
  60. }).join("");
  61. }
  62. function bufferToData(b) {
  63. let s = "";
  64. for (let i = 0; i < b.length; i++)
  65. s += String.fromCharCode(b[i]);
  66. return { data: globalThis.btoa(s), isBase64: true };
  67. }
  68. function stringToBuffer(s) {
  69. s = globalThis.atob(s);
  70. const b = new Uint8Array(s.length);
  71. for (let i = 0; i < s.length; i++)
  72. b[i] = s.charCodeAt(i);
  73. return b.buffer;
  74. }
  75. function messageToData(message, cb) {
  76. if (message instanceof globalThis.Blob)
  77. return message.arrayBuffer().then((buffer) => cb(bufferToData(new Uint8Array(buffer))));
  78. if (typeof message === "string")
  79. return cb({ data: message, isBase64: false });
  80. if (ArrayBuffer.isView(message))
  81. return cb(bufferToData(new Uint8Array(message.buffer, message.byteOffset, message.byteLength)));
  82. return cb(bufferToData(new Uint8Array(message)));
  83. }
  84. function dataToMessage(data, binaryType) {
  85. if (!data.isBase64)
  86. return data.data;
  87. const buffer = stringToBuffer(data.data);
  88. return binaryType === "arraybuffer" ? buffer : new Blob([buffer]);
  89. }
  90. const binding = globalThis.__pwWebSocketBinding;
  91. const NativeWebSocket = globalThis.WebSocket;
  92. const idToWebSocket = /* @__PURE__ */ new Map();
  93. globalThis.__pwWebSocketDispatch = (request) => {
  94. const ws = idToWebSocket.get(request.id);
  95. if (!ws)
  96. return;
  97. if (request.type === "connect")
  98. ws._apiConnect();
  99. if (request.type === "passthrough")
  100. ws._apiPassThrough();
  101. if (request.type === "ensureOpened")
  102. ws._apiEnsureOpened();
  103. if (request.type === "sendToPage")
  104. ws._apiSendToPage(dataToMessage(request.data, ws.binaryType));
  105. if (request.type === "closePage")
  106. ws._apiClosePage(request.code, request.reason, request.wasClean);
  107. if (request.type === "sendToServer")
  108. ws._apiSendToServer(dataToMessage(request.data, ws.binaryType));
  109. if (request.type === "closeServer")
  110. ws._apiCloseServer(request.code, request.reason, request.wasClean);
  111. };
  112. const _WebSocketMock = class _WebSocketMock extends EventTarget {
  113. constructor(url, protocols) {
  114. var _a, _b;
  115. super();
  116. // WebSocket.CLOSED
  117. this.CONNECTING = 0;
  118. // WebSocket.CONNECTING
  119. this.OPEN = 1;
  120. // WebSocket.OPEN
  121. this.CLOSING = 2;
  122. // WebSocket.CLOSING
  123. this.CLOSED = 3;
  124. // WebSocket.CLOSED
  125. this._oncloseListener = null;
  126. this._onerrorListener = null;
  127. this._onmessageListener = null;
  128. this._onopenListener = null;
  129. this.bufferedAmount = 0;
  130. this.extensions = "";
  131. this.protocol = "";
  132. this.readyState = 0;
  133. this._origin = "";
  134. this._passthrough = false;
  135. this._wsBufferedMessages = [];
  136. this._binaryType = "blob";
  137. this.url = new URL(url, globalThis.window.document.baseURI).href.replace(/^http/, "ws");
  138. this._origin = (_b = (_a = URL.parse(this.url)) == null ? void 0 : _a.origin) != null ? _b : "";
  139. this._protocols = protocols;
  140. this._id = generateId();
  141. idToWebSocket.set(this._id, this);
  142. binding({ type: "onCreate", id: this._id, url: this.url });
  143. }
  144. // --- native WebSocket implementation ---
  145. get binaryType() {
  146. return this._binaryType;
  147. }
  148. set binaryType(type) {
  149. this._binaryType = type;
  150. if (this._ws)
  151. this._ws.binaryType = type;
  152. }
  153. get onclose() {
  154. return this._oncloseListener;
  155. }
  156. set onclose(listener) {
  157. if (this._oncloseListener)
  158. this.removeEventListener("close", this._oncloseListener);
  159. this._oncloseListener = listener;
  160. if (this._oncloseListener)
  161. this.addEventListener("close", this._oncloseListener);
  162. }
  163. get onerror() {
  164. return this._onerrorListener;
  165. }
  166. set onerror(listener) {
  167. if (this._onerrorListener)
  168. this.removeEventListener("error", this._onerrorListener);
  169. this._onerrorListener = listener;
  170. if (this._onerrorListener)
  171. this.addEventListener("error", this._onerrorListener);
  172. }
  173. get onopen() {
  174. return this._onopenListener;
  175. }
  176. set onopen(listener) {
  177. if (this._onopenListener)
  178. this.removeEventListener("open", this._onopenListener);
  179. this._onopenListener = listener;
  180. if (this._onopenListener)
  181. this.addEventListener("open", this._onopenListener);
  182. }
  183. get onmessage() {
  184. return this._onmessageListener;
  185. }
  186. set onmessage(listener) {
  187. if (this._onmessageListener)
  188. this.removeEventListener("message", this._onmessageListener);
  189. this._onmessageListener = listener;
  190. if (this._onmessageListener)
  191. this.addEventListener("message", this._onmessageListener);
  192. }
  193. send(message) {
  194. if (this.readyState === _WebSocketMock.CONNECTING)
  195. throw new DOMException(\`Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.\`);
  196. if (this.readyState !== _WebSocketMock.OPEN)
  197. throw new DOMException(\`WebSocket is already in CLOSING or CLOSED state.\`);
  198. if (this._passthrough) {
  199. if (this._ws)
  200. this._apiSendToServer(message);
  201. } else {
  202. messageToData(message, (data) => binding({ type: "onMessageFromPage", id: this._id, data }));
  203. }
  204. }
  205. close(code, reason) {
  206. if (code !== void 0 && code !== 1e3 && (code < 3e3 || code > 4999))
  207. throw new DOMException(\`Failed to execute 'close' on 'WebSocket': The close code must be either 1000, or between 3000 and 4999. \${code} is neither.\`);
  208. if (this.readyState === _WebSocketMock.OPEN || this.readyState === _WebSocketMock.CONNECTING)
  209. this.readyState = _WebSocketMock.CLOSING;
  210. if (this._passthrough)
  211. this._apiCloseServer(code, reason, true);
  212. else
  213. binding({ type: "onClosePage", id: this._id, code, reason, wasClean: true });
  214. }
  215. // --- methods called from the routing API ---
  216. _apiEnsureOpened() {
  217. if (!this._ws)
  218. this._ensureOpened();
  219. }
  220. _apiSendToPage(message) {
  221. this._ensureOpened();
  222. if (this.readyState !== _WebSocketMock.OPEN)
  223. throw new DOMException(\`WebSocket is already in CLOSING or CLOSED state.\`);
  224. this.dispatchEvent(new MessageEvent("message", { data: message, origin: this._origin, cancelable: true }));
  225. }
  226. _apiSendToServer(message) {
  227. if (!this._ws)
  228. throw new Error("Cannot send a message before connecting to the server");
  229. if (this._ws.readyState === _WebSocketMock.CONNECTING)
  230. this._wsBufferedMessages.push(message);
  231. else
  232. this._ws.send(message);
  233. }
  234. _apiConnect() {
  235. if (this._ws)
  236. throw new Error("Can only connect to the server once");
  237. this._ws = new NativeWebSocket(this.url, this._protocols);
  238. this._ws.binaryType = this._binaryType;
  239. this._ws.onopen = () => {
  240. for (const message of this._wsBufferedMessages)
  241. this._ws.send(message);
  242. this._wsBufferedMessages = [];
  243. this._ensureOpened();
  244. };
  245. this._ws.onclose = (event) => {
  246. this._onWSClose(event.code, event.reason, event.wasClean);
  247. };
  248. this._ws.onmessage = (event) => {
  249. if (this._passthrough)
  250. this._apiSendToPage(event.data);
  251. else
  252. messageToData(event.data, (data) => binding({ type: "onMessageFromServer", id: this._id, data }));
  253. };
  254. this._ws.onerror = () => {
  255. const event = new Event("error", { cancelable: true });
  256. this.dispatchEvent(event);
  257. };
  258. }
  259. // This method connects to the server, and passes all messages through,
  260. // as if WebSocketMock was not engaged.
  261. _apiPassThrough() {
  262. this._passthrough = true;
  263. this._apiConnect();
  264. }
  265. _apiCloseServer(code, reason, wasClean) {
  266. if (!this._ws) {
  267. this._onWSClose(code, reason, wasClean);
  268. return;
  269. }
  270. if (this._ws.readyState === _WebSocketMock.CONNECTING || this._ws.readyState === _WebSocketMock.OPEN)
  271. this._ws.close(code, reason);
  272. }
  273. _apiClosePage(code, reason, wasClean) {
  274. if (this.readyState === _WebSocketMock.CLOSED)
  275. return;
  276. this.readyState = _WebSocketMock.CLOSED;
  277. this.dispatchEvent(new CloseEvent("close", { code, reason, wasClean, cancelable: true }));
  278. this._maybeCleanup();
  279. if (this._passthrough)
  280. this._apiCloseServer(code, reason, wasClean);
  281. else
  282. binding({ type: "onClosePage", id: this._id, code, reason, wasClean });
  283. }
  284. // --- internals ---
  285. _ensureOpened() {
  286. var _a;
  287. if (this.readyState !== _WebSocketMock.CONNECTING)
  288. return;
  289. this.extensions = ((_a = this._ws) == null ? void 0 : _a.extensions) || "";
  290. if (this._ws)
  291. this.protocol = this._ws.protocol;
  292. else if (Array.isArray(this._protocols))
  293. this.protocol = this._protocols[0] || "";
  294. else
  295. this.protocol = this._protocols || "";
  296. this.readyState = _WebSocketMock.OPEN;
  297. this.dispatchEvent(new Event("open", { cancelable: true }));
  298. }
  299. _onWSClose(code, reason, wasClean) {
  300. if (this._passthrough)
  301. this._apiClosePage(code, reason, wasClean);
  302. else
  303. binding({ type: "onCloseServer", id: this._id, code, reason, wasClean });
  304. if (this._ws) {
  305. this._ws.onopen = null;
  306. this._ws.onclose = null;
  307. this._ws.onmessage = null;
  308. this._ws.onerror = null;
  309. this._ws = void 0;
  310. this._wsBufferedMessages = [];
  311. }
  312. this._maybeCleanup();
  313. }
  314. _maybeCleanup() {
  315. if (this.readyState === _WebSocketMock.CLOSED && !this._ws)
  316. idToWebSocket.delete(this._id);
  317. }
  318. };
  319. _WebSocketMock.CONNECTING = 0;
  320. // WebSocket.CONNECTING
  321. _WebSocketMock.OPEN = 1;
  322. // WebSocket.OPEN
  323. _WebSocketMock.CLOSING = 2;
  324. // WebSocket.CLOSING
  325. _WebSocketMock.CLOSED = 3;
  326. let WebSocketMock = _WebSocketMock;
  327. globalThis.WebSocket = class WebSocket extends WebSocketMock {
  328. };
  329. }
  330. `;
  331. // Annotate the CommonJS export names for ESM import in node:
  332. 0 && (module.exports = {
  333. source
  334. });