herdr-remote 0.2.39 → 0.3.32

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 (205) hide show
  1. package/README.md +4 -2
  2. package/README.zh-CN.md +2 -2
  3. package/dist/chunks/3FQAXVGC.js +3 -0
  4. package/dist/chunks/42KARAL4.js +31 -0
  5. package/dist/chunks/44IGVPMA.js +5 -0
  6. package/dist/chunks/5LZFJ6UZ.js +3 -0
  7. package/dist/chunks/7SMZ43LI.js +9 -0
  8. package/dist/chunks/AX4EPJCP.js +4 -0
  9. package/dist/chunks/B76DFI4H.js +16 -0
  10. package/dist/chunks/BYGDPZZD.js +3 -0
  11. package/dist/chunks/C2UUZY6J.js +2 -0
  12. package/dist/chunks/CVJ4HBMP.js +3 -0
  13. package/dist/chunks/DGUZCGQN.js +3 -0
  14. package/dist/chunks/FONUO5O5.js +4 -0
  15. package/dist/chunks/H24ZP5D5.js +16 -0
  16. package/dist/chunks/I6NRCYLI.js +3 -0
  17. package/dist/chunks/JJGEV4EZ.js +3 -0
  18. package/dist/chunks/K5RFPHSF.js +5 -0
  19. package/dist/chunks/KGOQKOHH.js +5 -0
  20. package/dist/chunks/KRVF37OW.js +3 -0
  21. package/dist/chunks/KUDCC7ZO.js +6 -0
  22. package/dist/chunks/LPR662JH.js +77 -0
  23. package/dist/chunks/LY2ZGCXN.js +4 -0
  24. package/dist/chunks/M52LJEES.js +6 -0
  25. package/dist/chunks/MCUVFSSK.js +3 -0
  26. package/dist/chunks/MEFDV375.js +10 -0
  27. package/dist/chunks/MKAEXKXG.js +10 -0
  28. package/dist/chunks/O2LNVFUT.js +3 -0
  29. package/dist/chunks/PBMBFWLQ.js +3 -0
  30. package/dist/chunks/PBRQ3HEG.js +3 -0
  31. package/dist/chunks/QCSILMFB.js +3 -0
  32. package/dist/chunks/QRGNEKME.js +5 -0
  33. package/dist/chunks/RMHH3IAX.js +63 -0
  34. package/dist/chunks/TNLCNVA2.js +3 -0
  35. package/dist/chunks/U6QAYG3E.js +3 -0
  36. package/dist/chunks/VD54DF7R.js +45 -0
  37. package/dist/chunks/WY7WHEJ4.js +3 -0
  38. package/dist/cli.js +21 -294
  39. package/dist/connector/admin-broker.js +3 -303
  40. package/dist/connector/main.js +20 -18
  41. package/dist/harfbuzz-subset.wasm +0 -0
  42. package/dist/postinstall.js +17 -0
  43. package/herdr-plugin.toml +1 -1
  44. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  45. package/node_modules/herdr-remote-relay/package.json +3 -46
  46. package/package.json +8 -27
  47. package/dist/agent-status.js +0 -130
  48. package/dist/binary-plist.js +0 -157
  49. package/dist/config.js +0 -278
  50. package/dist/connector/admin-broker-setup.js +0 -40
  51. package/dist/connector/admin-shell.js +0 -67
  52. package/dist/connector/admin-tab.js +0 -90
  53. package/dist/connector/adminBrokerEndpoint.js +0 -68
  54. package/dist/connector/agent-watch.js +0 -123
  55. package/dist/connector/elevatedPty.js +0 -141
  56. package/dist/connector/font-server.js +0 -165
  57. package/dist/connector/herdr-prefix-keys.js +0 -28
  58. package/dist/connector/host-connector.js +0 -433
  59. package/dist/connector/host-hello.js +0 -32
  60. package/dist/connector/lock.js +0 -73
  61. package/dist/connector/sessions.js +0 -381
  62. package/dist/connector/update-report.js +0 -70
  63. package/dist/connector/wire.js +0 -42
  64. package/dist/exit-codes.js +0 -13
  65. package/dist/font-subset.js +0 -116
  66. package/dist/herdr-api.js +0 -254
  67. package/dist/herdr-command.js +0 -245
  68. package/dist/herdr-plugin.js +0 -120
  69. package/dist/herdr-server.js +0 -190
  70. package/dist/i18n/en.js +0 -257
  71. package/dist/i18n/index.js +0 -48
  72. package/dist/i18n/zh.js +0 -256
  73. package/dist/keepalive/detached.js +0 -97
  74. package/dist/keepalive/environment.js +0 -44
  75. package/dist/keepalive/index.js +0 -129
  76. package/dist/keepalive/launchd.js +0 -117
  77. package/dist/keepalive/systemd.js +0 -112
  78. package/dist/keepalive/types.js +0 -1
  79. package/dist/keepalive/windows-service-setup.js +0 -138
  80. package/dist/keepalive/windows-service.js +0 -272
  81. package/dist/keepalive/windows-task.js +0 -191
  82. package/dist/lib/process.js +0 -132
  83. package/dist/lifecycle.js +0 -79
  84. package/dist/net-interfaces.js +0 -82
  85. package/dist/node-pty-loader.js +0 -80
  86. package/dist/pasted-files.js +0 -115
  87. package/dist/paths.js +0 -74
  88. package/dist/pty-session.js +0 -133
  89. package/dist/relay-client.js +0 -106
  90. package/dist/relay-urls.js +0 -99
  91. package/dist/runtime.js +0 -107
  92. package/dist/service.js +0 -267
  93. package/dist/settings-model.js +0 -298
  94. package/dist/socket-discovery.js +0 -70
  95. package/dist/supervisor.js +0 -323
  96. package/dist/terminal-font/deps.js +0 -83
  97. package/dist/terminal-font/files.js +0 -357
  98. package/dist/terminal-font/formats.js +0 -355
  99. package/dist/terminal-font/index.js +0 -187
  100. package/dist/terminal-font/readers.js +0 -373
  101. package/dist/terminal-font/terminals.js +0 -147
  102. package/dist/terminal-palette.js +0 -399
  103. package/dist/tui/App.js +0 -199
  104. package/dist/tui/api.js +0 -91
  105. package/dist/tui/components/TextField.js +0 -50
  106. package/dist/tui/components/common.js +0 -80
  107. package/dist/tui/index.js +0 -36
  108. package/dist/tui/mouse/index.js +0 -164
  109. package/dist/tui/mouse/protocol.js +0 -96
  110. package/dist/tui/mouse/source.js +0 -79
  111. package/dist/tui/screens/About.js +0 -172
  112. package/dist/tui/screens/Herdr.js +0 -75
  113. package/dist/tui/screens/Keepalive.js +0 -141
  114. package/dist/tui/screens/Overview.js +0 -36
  115. package/dist/tui/screens/Pair.js +0 -74
  116. package/dist/tui/screens/Relay.js +0 -243
  117. package/dist/tui/screens/Services.js +0 -63
  118. package/dist/tui/screens/Wizard.js +0 -241
  119. package/dist/tui/terminal.js +0 -39
  120. package/dist/tui/theme.js +0 -33
  121. package/dist/update-command.js +0 -118
  122. package/dist/updater/in-place.js +0 -83
  123. package/dist/updater/npm.js +0 -62
  124. package/dist/updater.js +0 -401
  125. package/node_modules/herdr-remote-relay/README.md +0 -76
  126. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  127. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  128. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  129. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  130. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  131. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  132. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  133. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  134. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  135. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  136. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  137. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  138. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  139. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  140. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  141. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  142. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  143. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  144. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  145. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  146. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  147. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  148. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  149. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  150. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  151. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  152. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  153. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  154. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  155. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  156. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  157. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  158. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  159. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  160. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  161. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  162. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  163. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  164. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  165. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  166. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  167. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  168. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  169. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  170. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  171. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  172. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  173. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  174. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  175. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  176. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  177. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  178. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  179. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  180. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  181. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  182. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  183. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  184. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  185. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  186. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  187. package/node_modules/ws/LICENSE +0 -20
  188. package/node_modules/ws/README.md +0 -548
  189. package/node_modules/ws/browser.js +0 -8
  190. package/node_modules/ws/index.js +0 -22
  191. package/node_modules/ws/lib/buffer-util.js +0 -131
  192. package/node_modules/ws/lib/constants.js +0 -19
  193. package/node_modules/ws/lib/event-target.js +0 -292
  194. package/node_modules/ws/lib/extension.js +0 -203
  195. package/node_modules/ws/lib/limiter.js +0 -55
  196. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  197. package/node_modules/ws/lib/receiver.js +0 -743
  198. package/node_modules/ws/lib/sender.js +0 -607
  199. package/node_modules/ws/lib/stream.js +0 -161
  200. package/node_modules/ws/lib/subprotocol.js +0 -62
  201. package/node_modules/ws/lib/validation.js +0 -152
  202. package/node_modules/ws/lib/websocket-server.js +0 -562
  203. package/node_modules/ws/lib/websocket.js +0 -1407
  204. package/node_modules/ws/package.json +0 -74
  205. package/node_modules/ws/wrapper.mjs +0 -21
@@ -1,196 +0,0 @@
1
- // The relay: one HTTP server that answers the API, serves the web app and
2
- // upgrades /ws/host and /ws/client. The work itself lives in ./server/.
3
- import http from 'node:http';
4
- import path from 'node:path';
5
- import { URL } from 'node:url';
6
- import { WebSocketServer } from 'ws';
7
- import { AuthStore } from './auth-store.js';
8
- import { RelayMetrics } from './metrics.js';
9
- import { PROTOCOL_VERSION, WS_CLIENT_PATH, WS_HOST_PATH } from './protocol/index.js';
10
- import { DEFAULTS, defaultStateDir, loadRelayConfig } from './relay-config.js';
11
- import { handleClientConnection } from './server/client-channel.js';
12
- import { handleHostConnection } from './server/host-channel.js';
13
- import { handleHttp, PUBLIC_DIR } from './server/http-api.js';
14
- import { heartbeat, sweep } from './server/maintenance.js';
15
- import { isAllowedOrigin } from './server/requests.js';
16
- import { detachClient, detachHost } from './server/sessions.js';
17
- import { VERSION } from './server/version.js';
18
- import { ensureDir } from './state.js';
19
- class RelayServer {
20
- config;
21
- relayMode;
22
- hosts = new Map();
23
- clients = new Map();
24
- streams = new Map();
25
- pairAttempts = new Map();
26
- clientHandshakeAttempts = new Map();
27
- hostHandshakeAttempts = new Map();
28
- /** Sockets that are upgraded but have not finished their hello. */
29
- pendingHandshakes = new Set();
30
- startedAt = Date.now();
31
- metrics;
32
- stateFile;
33
- password;
34
- adminToken;
35
- trustProxy;
36
- devLatencyMs;
37
- devDelayMs;
38
- publicDir;
39
- sendQueues = new WeakMap();
40
- activeDelayTimers = new Set();
41
- auth;
42
- server;
43
- wss;
44
- heartbeatTimer = null;
45
- cleanupTimer = null;
46
- constructor(config = loadRelayConfig().config, options = {}) {
47
- this.config = {
48
- relay: { ...DEFAULTS.relay, ...config.relay },
49
- auth: { ...DEFAULTS.auth, ...config.auth },
50
- cleanup: { ...DEFAULTS.cleanup, ...config.cleanup },
51
- };
52
- const { relay, auth } = this.config;
53
- this.relayMode = relay.mode === 'local' ? 'local' : 'remote';
54
- this.metrics =
55
- options.metrics || new RelayMetrics({ version: VERSION, protocolVersion: PROTOCOL_VERSION });
56
- this.stateFile =
57
- options.stateFile || auth.stateFile || path.join(defaultStateDir(), 'relay-auth.json');
58
- this.password = options.password ?? auth.password ?? null;
59
- this.adminToken = options.adminToken ?? auth.adminToken ?? null;
60
- this.trustProxy = Boolean(options.trustProxy ?? relay.trustProxy);
61
- this.publicDir = path.resolve(options.publicDir ?? PUBLIC_DIR);
62
- // Development-only artificial latency switch for local responsiveness profiling.
63
- // When unset or 0, this incurs zero overhead and avoids entering the delayed path.
64
- // RELAY_DEV_LATENCY_MS specifies round-trip delay, so each one-way leg
65
- // (host -> browser and browser -> host) is delayed by half.
66
- const devLatencyRaw = options.devLatencyMs ?? relay.devLatencyMs ?? process.env.RELAY_DEV_LATENCY_MS;
67
- this.devLatencyMs = devLatencyRaw
68
- ? Math.max(0, Number.parseInt(String(devLatencyRaw), 10) || 0)
69
- : 0;
70
- this.devDelayMs = this.devLatencyMs > 0 ? Math.round(this.devLatencyMs / 2) : 0;
71
- this.auth =
72
- options.auth ||
73
- new AuthStore({
74
- stateFile: this.stateFile,
75
- pairingTtlMs: auth.pairingTtlMs,
76
- deviceTtlMs: auth.deviceTtlMs,
77
- maxDevices: auth.maxDevices,
78
- password: this.password,
79
- });
80
- this.server = http.createServer((req, res) => handleHttp(this, req, res));
81
- this.wss = new WebSocketServer({
82
- noServer: true,
83
- clientTracking: false,
84
- maxPayload: relay.maxPayloadBytes,
85
- // Frames below 1024 bytes bypass compression completely, ensuring single
86
- // keystrokes and small echoes incur zero CPU and zero buffering delay.
87
- // Keeping context across messages (NoContextTakeover=false) maximizes
88
- // compression ratios on highly repetitive full-screen ratatui/ANSI redraws;
89
- // level 3 provides low CPU cost and low latency over peak compression.
90
- perMessageDeflate: {
91
- threshold: 1024,
92
- zlibDeflateOptions: { level: 3 },
93
- serverNoContextTakeover: false,
94
- clientNoContextTakeover: false,
95
- concurrencyLimit: 10,
96
- },
97
- });
98
- this.server.on('upgrade', (req, socket, head) => this.handleUpgrade(req, socket, head));
99
- }
100
- listen(port = this.config.relay.port, host = this.config.relay.host) {
101
- ensureDir(path.dirname(this.stateFile));
102
- return new Promise((resolve, reject) => {
103
- const onError = (error) => {
104
- this.server.off('listening', onListening);
105
- reject(error);
106
- };
107
- const onListening = () => {
108
- this.server.off('error', onError);
109
- this.heartbeatTimer = setInterval(() => heartbeat(this), this.config.cleanup.heartbeatIntervalMs);
110
- this.cleanupTimer = setInterval(() => sweep(this), this.config.cleanup.intervalMs);
111
- resolve(this.address());
112
- };
113
- this.server.once('error', onError);
114
- this.server.once('listening', onListening);
115
- this.server.listen(port, host);
116
- });
117
- }
118
- address() {
119
- const address = this.server.address();
120
- if (!address)
121
- return null;
122
- return typeof address === 'string'
123
- ? { path: address }
124
- : { host: address.address, port: address.port };
125
- }
126
- async close() {
127
- if (this.heartbeatTimer)
128
- clearInterval(this.heartbeatTimer);
129
- if (this.cleanupTimer)
130
- clearInterval(this.cleanupTimer);
131
- this.heartbeatTimer = null;
132
- this.cleanupTimer = null;
133
- for (const timer of this.activeDelayTimers)
134
- clearTimeout(timer);
135
- this.activeDelayTimers.clear();
136
- for (const client of [...this.clients.values()])
137
- detachClient(this, client, { notify: false });
138
- for (const host of [...this.hosts.values()])
139
- detachHost(this, host, { notify: false });
140
- this.streams.clear();
141
- this.pairAttempts.clear();
142
- this.clientHandshakeAttempts.clear();
143
- this.hostHandshakeAttempts.clear();
144
- this.pendingHandshakes.clear();
145
- this.metrics.close();
146
- await new Promise((resolve) => {
147
- if (!this.server.listening)
148
- return resolve();
149
- this.server.close(() => resolve());
150
- });
151
- }
152
- handleUpgrade(req, socket, head) {
153
- const pathname = (() => {
154
- try {
155
- return new URL(req.url || '', 'http://localhost').pathname;
156
- }
157
- catch {
158
- return '';
159
- }
160
- })();
161
- if (![WS_HOST_PATH, WS_CLIENT_PATH].includes(pathname) ||
162
- !isAllowedOrigin(this, req.headers.origin, req)) {
163
- socket.write('HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n');
164
- socket.destroy();
165
- return;
166
- }
167
- if (this.pendingHandshakes.size >= this.config.relay.maxPendingHandshakes) {
168
- socket.write('HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\n\r\n');
169
- socket.destroy();
170
- return;
171
- }
172
- // Small ANSI/input frames should not wait behind Nagle's timer. This is
173
- // safe for both plain HTTP and TLS sockets and also benefits a relay behind
174
- // a reverse proxy by keeping the relay leg immediately writable.
175
- try {
176
- const tcp = socket;
177
- tcp.setNoDelay(true);
178
- tcp.setKeepAlive?.(true, this.config.cleanup.heartbeatIntervalMs);
179
- }
180
- catch {
181
- // Not a TCP socket (tests, proxies); these are only latency and liveness tweaks.
182
- }
183
- this.wss.handleUpgrade(req, socket, head, (ws) => {
184
- this.pendingHandshakes.add(ws);
185
- ws.once('close', () => this.finishHandshake(ws));
186
- if (pathname === WS_HOST_PATH)
187
- handleHostConnection(this, ws, req);
188
- else
189
- handleClientConnection(this, ws, req);
190
- });
191
- }
192
- finishHandshake(ws) {
193
- this.pendingHandshakes.delete(ws);
194
- }
195
- }
196
- export { RelayServer, PROTOCOL_VERSION, VERSION };
@@ -1,17 +0,0 @@
1
- /**
2
- * Wheel-report recognition for read-only clients.
3
- *
4
- * A viewer holds no control lease and may not type into the terminal. It may
5
- * still scroll, for the same reason `resize` does not require the lease: every
6
- * client drives its *own* PTY stream (`session_start` is emitted per client
7
- * with `streamId: client.id`), so a wheel report moves only that viewer's own
8
- * screen and cannot disturb the controller.
9
- *
10
- * This module is the trust boundary for that allowance, so the match is exact
11
- * rather than heuristic. A frame is forwarded only when it consists entirely
12
- * of wheel reports in the encodings a terminal emits; a single stray byte —
13
- * a keystroke, a click, a drag — rejects the whole frame.
14
- */
15
- /** Whether every byte of `payload`, a raw client input frame, belongs to a wheel report. */
16
- declare function isWheelOnlyInput(payload: Uint8Array | ArrayBuffer | null | undefined): boolean;
17
- export { isWheelOnlyInput };
@@ -1,124 +0,0 @@
1
- /**
2
- * Wheel-report recognition for read-only clients.
3
- *
4
- * A viewer holds no control lease and may not type into the terminal. It may
5
- * still scroll, for the same reason `resize` does not require the lease: every
6
- * client drives its *own* PTY stream (`session_start` is emitted per client
7
- * with `streamId: client.id`), so a wheel report moves only that viewer's own
8
- * screen and cannot disturb the controller.
9
- *
10
- * This module is the trust boundary for that allowance, so the match is exact
11
- * rather than heuristic. A frame is forwarded only when it consists entirely
12
- * of wheel reports in the encodings a terminal emits; a single stray byte —
13
- * a keystroke, a click, a drag — rejects the whole frame.
14
- */
15
- const ESC = 0x1b;
16
- const LEFT_BRACKET = 0x5b; // [
17
- const LESS_THAN = 0x3c; // <
18
- const SEMICOLON = 0x3b; // ;
19
- const UPPER_M = 0x4d; // M
20
- const ZERO = 0x30;
21
- const NINE = 0x39;
22
- /**
23
- * The wheel sets bit 64 of the event code; bit 1 is up/down and bits 4/8/16
24
- * are shift/alt/ctrl. Every other bit belongs to something that is not a
25
- * wheel: bit 32 marks motion, bits 2 and 128 select other buttons.
26
- */
27
- const WHEEL_BASE = 64;
28
- const WHEEL_CODE_MASK = ~(1 | 4 | 8 | 16);
29
- /** Single-byte (X10) parameters are biased by 32 and cannot exceed one byte. */
30
- const X10_PARAM_BIAS = 32;
31
- const X10_PARAM_MAX = 255;
32
- function isWheelCode(code) {
33
- return Number.isInteger(code) && (code & WHEEL_CODE_MASK) === WHEEL_BASE;
34
- }
35
- function isDigit(byte) {
36
- return byte >= ZERO && byte <= NINE;
37
- }
38
- /** Reads one decimal parameter. */
39
- function readNumber(bytes, start) {
40
- let index = start;
41
- let value = 0;
42
- while (index < bytes.length && isDigit(bytes[index])) {
43
- value = value * 10 + (bytes[index] - ZERO);
44
- // A parameter long enough to overflow is not something a terminal emits.
45
- if (value > 0xffff)
46
- return null;
47
- index += 1;
48
- }
49
- if (index === start)
50
- return null;
51
- return { value, next: index };
52
- }
53
- /**
54
- * Matches `ESC [ < Pb ; Px ; Py M`, the SGR and SGR-pixels encodings.
55
- * @returns offset after the report, or -1
56
- */
57
- function matchSgrWheel(bytes, start) {
58
- if (bytes[start] !== ESC || bytes[start + 1] !== LEFT_BRACKET || bytes[start + 2] !== LESS_THAN) {
59
- return -1;
60
- }
61
- const code = readNumber(bytes, start + 3);
62
- if (!code || !isWheelCode(code.value) || bytes[code.next] !== SEMICOLON)
63
- return -1;
64
- const first = readNumber(bytes, code.next + 1);
65
- if (!first || bytes[first.next] !== SEMICOLON)
66
- return -1;
67
- const second = readNumber(bytes, first.next + 1);
68
- if (!second)
69
- return -1;
70
- // A wheel is never released, so the terminator is always `M`. Accepting `m`
71
- // here would admit button releases, which are not scrolling.
72
- if (bytes[second.next] !== UPPER_M)
73
- return -1;
74
- return second.next + 1;
75
- }
76
- /**
77
- * Matches `ESC [ M Pb Px Py`, the default single-byte encoding.
78
- * @returns offset after the report, or -1
79
- */
80
- function matchX10Wheel(bytes, start) {
81
- if (bytes[start] !== ESC || bytes[start + 1] !== LEFT_BRACKET || bytes[start + 2] !== UPPER_M) {
82
- return -1;
83
- }
84
- if (start + 6 > bytes.length)
85
- return -1;
86
- const code = bytes[start + 3] - X10_PARAM_BIAS;
87
- const column = bytes[start + 4];
88
- const row = bytes[start + 5];
89
- if (!isWheelCode(code))
90
- return -1;
91
- if (column < X10_PARAM_BIAS || column > X10_PARAM_MAX)
92
- return -1;
93
- if (row < X10_PARAM_BIAS || row > X10_PARAM_MAX)
94
- return -1;
95
- return start + 6;
96
- }
97
- /** Whether every byte of `payload`, a raw client input frame, belongs to a wheel report. */
98
- function isWheelOnlyInput(payload) {
99
- if (!payload)
100
- return false;
101
- const bytes = payload instanceof Uint8Array
102
- ? payload
103
- : payload instanceof ArrayBuffer
104
- ? new Uint8Array(payload)
105
- : null;
106
- if (!bytes || bytes.length === 0)
107
- return false;
108
- let offset = 0;
109
- while (offset < bytes.length) {
110
- const sgr = matchSgrWheel(bytes, offset);
111
- if (sgr > offset) {
112
- offset = sgr;
113
- continue;
114
- }
115
- const x10 = matchX10Wheel(bytes, offset);
116
- if (x10 > offset) {
117
- offset = x10;
118
- continue;
119
- }
120
- return false;
121
- }
122
- return true;
123
- }
124
- export { isWheelOnlyInput };
@@ -1,3 +0,0 @@
1
- import type { IncomingMessage } from 'node:http';
2
- import type { RelayContext, RelaySocket } from './types.js';
3
- export declare function handleClientConnection(relay: RelayContext, ws: RelaySocket, req: IncomingMessage): void;