herdr-remote 0.2.39 → 0.3.31

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 (203) hide show
  1. package/dist/chunks/353DMT35.js +3 -0
  2. package/dist/chunks/42KARAL4.js +31 -0
  3. package/dist/chunks/7AUQHB3O.js +10 -0
  4. package/dist/chunks/7SMZ43LI.js +9 -0
  5. package/dist/chunks/AM3OEMFK.js +16 -0
  6. package/dist/chunks/ANNJESOL.js +10 -0
  7. package/dist/chunks/AWB4A65P.js +63 -0
  8. package/dist/chunks/C2UUZY6J.js +2 -0
  9. package/dist/chunks/DGUZCGQN.js +3 -0
  10. package/dist/chunks/H24ZP5D5.js +16 -0
  11. package/dist/chunks/I6NRCYLI.js +3 -0
  12. package/dist/chunks/JJGEV4EZ.js +3 -0
  13. package/dist/chunks/KRSURB5L.js +5 -0
  14. package/dist/chunks/KRVF37OW.js +3 -0
  15. package/dist/chunks/KUDCC7ZO.js +6 -0
  16. package/dist/chunks/M3NRPJK2.js +77 -0
  17. package/dist/chunks/M4AFZHKH.js +3 -0
  18. package/dist/chunks/M52LJEES.js +6 -0
  19. package/dist/chunks/O2LNVFUT.js +3 -0
  20. package/dist/chunks/OJPBI7KB.js +3 -0
  21. package/dist/chunks/OMN6C7J6.js +3 -0
  22. package/dist/chunks/PBMBFWLQ.js +3 -0
  23. package/dist/chunks/PBRQ3HEG.js +3 -0
  24. package/dist/chunks/PFFVGFPL.js +45 -0
  25. package/dist/chunks/QRGNEKME.js +5 -0
  26. package/dist/chunks/TNLCNVA2.js +3 -0
  27. package/dist/chunks/U27JJKJ6.js +3 -0
  28. package/dist/chunks/U6QAYG3E.js +3 -0
  29. package/dist/chunks/UEOQE77N.js +4 -0
  30. package/dist/chunks/WSACSE45.js +4 -0
  31. package/dist/chunks/WY7WHEJ4.js +3 -0
  32. package/dist/chunks/XJKWLK43.js +4 -0
  33. package/dist/chunks/XN6D5S55.js +3 -0
  34. package/dist/chunks/YD3MIP7B.js +5 -0
  35. package/dist/chunks/YXDQ62BB.js +4 -0
  36. package/dist/cli.js +21 -294
  37. package/dist/connector/admin-broker.js +3 -303
  38. package/dist/connector/main.js +20 -18
  39. package/dist/harfbuzz-subset.wasm +0 -0
  40. package/dist/postinstall.js +17 -0
  41. package/herdr-plugin.toml +1 -1
  42. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  43. package/node_modules/herdr-remote-relay/package.json +3 -46
  44. package/package.json +8 -27
  45. package/dist/agent-status.js +0 -130
  46. package/dist/binary-plist.js +0 -157
  47. package/dist/config.js +0 -278
  48. package/dist/connector/admin-broker-setup.js +0 -40
  49. package/dist/connector/admin-shell.js +0 -67
  50. package/dist/connector/admin-tab.js +0 -90
  51. package/dist/connector/adminBrokerEndpoint.js +0 -68
  52. package/dist/connector/agent-watch.js +0 -123
  53. package/dist/connector/elevatedPty.js +0 -141
  54. package/dist/connector/font-server.js +0 -165
  55. package/dist/connector/herdr-prefix-keys.js +0 -28
  56. package/dist/connector/host-connector.js +0 -433
  57. package/dist/connector/host-hello.js +0 -32
  58. package/dist/connector/lock.js +0 -73
  59. package/dist/connector/sessions.js +0 -381
  60. package/dist/connector/update-report.js +0 -70
  61. package/dist/connector/wire.js +0 -42
  62. package/dist/exit-codes.js +0 -13
  63. package/dist/font-subset.js +0 -116
  64. package/dist/herdr-api.js +0 -254
  65. package/dist/herdr-command.js +0 -245
  66. package/dist/herdr-plugin.js +0 -120
  67. package/dist/herdr-server.js +0 -190
  68. package/dist/i18n/en.js +0 -257
  69. package/dist/i18n/index.js +0 -48
  70. package/dist/i18n/zh.js +0 -256
  71. package/dist/keepalive/detached.js +0 -97
  72. package/dist/keepalive/environment.js +0 -44
  73. package/dist/keepalive/index.js +0 -129
  74. package/dist/keepalive/launchd.js +0 -117
  75. package/dist/keepalive/systemd.js +0 -112
  76. package/dist/keepalive/types.js +0 -1
  77. package/dist/keepalive/windows-service-setup.js +0 -138
  78. package/dist/keepalive/windows-service.js +0 -272
  79. package/dist/keepalive/windows-task.js +0 -191
  80. package/dist/lib/process.js +0 -132
  81. package/dist/lifecycle.js +0 -79
  82. package/dist/net-interfaces.js +0 -82
  83. package/dist/node-pty-loader.js +0 -80
  84. package/dist/pasted-files.js +0 -115
  85. package/dist/paths.js +0 -74
  86. package/dist/pty-session.js +0 -133
  87. package/dist/relay-client.js +0 -106
  88. package/dist/relay-urls.js +0 -99
  89. package/dist/runtime.js +0 -107
  90. package/dist/service.js +0 -267
  91. package/dist/settings-model.js +0 -298
  92. package/dist/socket-discovery.js +0 -70
  93. package/dist/supervisor.js +0 -323
  94. package/dist/terminal-font/deps.js +0 -83
  95. package/dist/terminal-font/files.js +0 -357
  96. package/dist/terminal-font/formats.js +0 -355
  97. package/dist/terminal-font/index.js +0 -187
  98. package/dist/terminal-font/readers.js +0 -373
  99. package/dist/terminal-font/terminals.js +0 -147
  100. package/dist/terminal-palette.js +0 -399
  101. package/dist/tui/App.js +0 -199
  102. package/dist/tui/api.js +0 -91
  103. package/dist/tui/components/TextField.js +0 -50
  104. package/dist/tui/components/common.js +0 -80
  105. package/dist/tui/index.js +0 -36
  106. package/dist/tui/mouse/index.js +0 -164
  107. package/dist/tui/mouse/protocol.js +0 -96
  108. package/dist/tui/mouse/source.js +0 -79
  109. package/dist/tui/screens/About.js +0 -172
  110. package/dist/tui/screens/Herdr.js +0 -75
  111. package/dist/tui/screens/Keepalive.js +0 -141
  112. package/dist/tui/screens/Overview.js +0 -36
  113. package/dist/tui/screens/Pair.js +0 -74
  114. package/dist/tui/screens/Relay.js +0 -243
  115. package/dist/tui/screens/Services.js +0 -63
  116. package/dist/tui/screens/Wizard.js +0 -241
  117. package/dist/tui/terminal.js +0 -39
  118. package/dist/tui/theme.js +0 -33
  119. package/dist/update-command.js +0 -118
  120. package/dist/updater/in-place.js +0 -83
  121. package/dist/updater/npm.js +0 -62
  122. package/dist/updater.js +0 -401
  123. package/node_modules/herdr-remote-relay/README.md +0 -76
  124. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  125. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  126. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  127. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  128. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  129. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  130. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  131. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  132. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  133. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  134. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  135. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  136. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  137. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  138. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  139. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  140. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  141. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  142. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  143. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  144. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  145. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  146. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  147. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  148. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  149. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  150. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  151. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  152. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  153. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  154. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  155. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  156. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  157. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  158. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  159. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  160. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  161. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  162. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  163. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  164. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  165. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  166. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  167. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  168. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  169. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  170. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  171. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  172. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  173. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  174. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  175. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  176. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  177. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  178. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  179. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  180. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  181. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  182. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  183. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  184. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  185. package/node_modules/ws/LICENSE +0 -20
  186. package/node_modules/ws/README.md +0 -548
  187. package/node_modules/ws/browser.js +0 -8
  188. package/node_modules/ws/index.js +0 -22
  189. package/node_modules/ws/lib/buffer-util.js +0 -131
  190. package/node_modules/ws/lib/constants.js +0 -19
  191. package/node_modules/ws/lib/event-target.js +0 -292
  192. package/node_modules/ws/lib/extension.js +0 -203
  193. package/node_modules/ws/lib/limiter.js +0 -55
  194. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  195. package/node_modules/ws/lib/receiver.js +0 -743
  196. package/node_modules/ws/lib/sender.js +0 -607
  197. package/node_modules/ws/lib/stream.js +0 -161
  198. package/node_modules/ws/lib/subprotocol.js +0 -62
  199. package/node_modules/ws/lib/validation.js +0 -152
  200. package/node_modules/ws/lib/websocket-server.js +0 -562
  201. package/node_modules/ws/lib/websocket.js +0 -1407
  202. package/node_modules/ws/package.json +0 -74
  203. 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;