PageSourceSearch

https://et.topease.net/shared-websocket-worker.js

js topease.net collected 2026-09-24 13:01:25 UTC 4,105 bytes, 172 lines download raw bytes

1/**
2 * 多页签共享 WebSocket 连接 Worker
3 * 所有同源页签连接到同一个 SharedWorker,共享一个 WebSocket 连接
4 */
5(function () {
6  'use strict';
7
8  var ws = null;
9  var currentUrl = null;
10  var heartbeatInterval = 30000;
11  var heartbeatTimer = null;
12
13  /** @type {Set<MessagePort>} */
14  var ports = new Set();
15  /** @type {Map<MessagePort, boolean>} */
16  var portInterests = new Map();
17
18  // ======================== WebSocket 管理 ========================
19
20  function connect(url) {
21    if (url === currentUrl && ws && ws.readyState === WebSocket.OPEN) {
22      return;
23    }
24
25    if (ws) {
26      closeWebSocket();
27    }
28
29    currentUrl = url;
30
31    try {
32      ws = new WebSocket(url);
33      setupEventListeners();
34    } catch (error) {
35      broadcast({ type: 'error', message: 'WebSocket创建失败: ' + error.message });
36    }
37  }
38
39  function setupEventListeners() {
40    if (!ws) return;
41
42    ws.onopen = function () {
43      startHeartbeat();
44      broadcast({ type: 'open' });
45    };
46
47    ws.onmessage = function (event) {
48      if (event.data === 'pong') return;
49
50      try {
51        var data = JSON.parse(event.data);
52        broadcast({ type: 'message', data: data });
53      } catch (_) {
54        broadcast({ type: 'message', data: event.data });
55      }
56    };
57
58    ws.onerror = function () {
59      broadcast({ type: 'error', message: 'WebSocket连接错误' });
60    };
61
62    ws.onclose = function (event) {
63      stopHeartbeat();
64
65      if (event.code === 3000) {
66        broadcast({ type: 'error', message: '连接被新连接替换' });
67      }
68
69      broadcast({ type: 'close', code: event.code, reason: event.reason });
70    };
71  }
72
73  function startHeartbeat() {
74    stopHeartbeat();
75    heartbeatTimer = setInterval(function () {
76      if (ws && ws.readyState === WebSocket.OPEN) {
77        ws.send(JSON.stringify({ type: 'ping' }));
78      }
79    }, heartbeatInterval);
80  }
81
82  function stopHeartbeat() {
83    if (heartbeatTimer) {
84      clearInterval(heartbeatTimer);
85      heartbeatTimer = null;
86    }
87  }
88
89  function closeWebSocket() {
90    stopHeartbeat();
91    if (ws) {
92      ws.close();
93      ws = null;
94    }
95    currentUrl = null;
96  }
97
98  function sendData(data) {
99    if (!ws || ws.readyState !== WebSocket.OPEN) {
100      return false;
101    }
102    try {
103      var message = typeof data === 'object' ? JSON.stringify(data) : data;
104      ws.send(message);
105      return true;
106    } catch (error) {
107      broadcast({ type: 'error', message: '发送消息失败: ' + error.message });
108      return false;
109    }
110  }
111
112  // ======================== 端口管理 ========================
113
114  function broadcast(message) {
115    ports.forEach(function (port) {
116      try {
117        port.postMessage(message);
118      } catch (_) {
119        ports.delete(port);
120      }
121    });
122  }
123
124  function shouldClose() {
125    var anyInterested = false;
126    portInterests.forEach(function (interested) {
127      if (interested) anyInterested = true;
128    });
129    if (!anyInterested) {
130      closeWebSocket();
131    }
132  }
133
134  // ======================== SharedWorker 入口 ========================
135
136  self.onconnect = function (event) {
137    var port = event.ports[0];
138    ports.add(port);
139    portInterests.set(port, true);
140
141    port.addEventListener('message', function (e) {
142      var msg = e.data;
143
144      switch (msg.type) {
145        case 'connect':
146          portInterests.set(port, true);
147          connect(msg.url);
148          // 如果连接已处于 OPEN(同 URL 复用),立即向该 port 单独回复当前状态
149          // 否则调用方的 Promise 会因收不到 open 事件而永久挂起
150          if (ws && ws.readyState === WebSocket.OPEN) {
151            port.postMessage({ type: 'open' });
152          }
153          break;
154        case 'disconnect':
155          portInterests.set(port, false);
156          shouldClose();
157          break;
158        case 'send':
159          sendData(msg.data);
160          break;
161      }
162    });
163
164    port.start();
165
166    port.addEventListener('close', function () {
167      ports.delete(port);
168      portInterests.delete(port);
169      shouldClose();
170    });
171  };
172})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.