@hediet/linkrpc-hub 0.0.1

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 (50) hide show
  1. package/README.md +101 -0
  2. package/dist/chunks/config-BCvkg7jv.d.ts +566 -0
  3. package/dist/chunks/configFile-y6Phntqu.d.ts +9 -0
  4. package/dist/chunks/connectionTokenBinder.interfaces-B-beCg06.js +40 -0
  5. package/dist/chunks/connectionTokenBinder.interfaces-B-beCg06.js.map +1 -0
  6. package/dist/chunks/connectionTokenBinder.interfaces-BhzQ1DTS.d.ts +36 -0
  7. package/dist/chunks/hubConnectionAcceptor-B5-8JsFY.js +2768 -0
  8. package/dist/chunks/hubConnectionAcceptor-B5-8JsFY.js.map +1 -0
  9. package/dist/chunks/hubConnectionAcceptor-BwydvFa4.d.ts +1560 -0
  10. package/dist/chunks/index-CLIUrV88.d.ts +481 -0
  11. package/dist/chunks/node-CTXsQ6oa.js +460 -0
  12. package/dist/chunks/node-CTXsQ6oa.js.map +1 -0
  13. package/dist/chunks/nodeTransit-CWeFnbwt.js +444 -0
  14. package/dist/chunks/nodeTransit-CWeFnbwt.js.map +1 -0
  15. package/dist/chunks/nodeTransit-cmtZgdpW.d.ts +226 -0
  16. package/dist/chunks/runHub-P3YUwwdv.js +1797 -0
  17. package/dist/chunks/runHub-P3YUwwdv.js.map +1 -0
  18. package/dist/chunks/server-BAxchQhy.js +1368 -0
  19. package/dist/chunks/server-BAxchQhy.js.map +1 -0
  20. package/dist/cli.d.ts +1 -0
  21. package/dist/cli.js +38 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/config.d.ts +2 -0
  24. package/dist/config.js +305 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/configFile.d.ts +2 -0
  27. package/dist/configFile.js +27 -0
  28. package/dist/configFile.js.map +1 -0
  29. package/dist/engine/runHub.d.ts +42 -0
  30. package/dist/engine/runHub.js +2 -0
  31. package/dist/hub/server/client.d.ts +2 -0
  32. package/dist/hub/server/client.js +2 -0
  33. package/dist/hub/server/connectionTokenBinder.d.ts +2 -0
  34. package/dist/hub/server/connectionTokenBinder.js +2 -0
  35. package/dist/hub/server/index.d.ts +5 -0
  36. package/dist/hub/server/index.js +5 -0
  37. package/dist/hub/server/node/index.d.ts +218 -0
  38. package/dist/hub/server/node/index.js +2 -0
  39. package/dist/hub/server/transit.d.ts +2 -0
  40. package/dist/hub/server/transit.js +2 -0
  41. package/dist/index.d.ts +512 -0
  42. package/dist/index.js +309 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/serve.d.ts +14 -0
  45. package/dist/serve.js +31 -0
  46. package/dist/serve.js.map +1 -0
  47. package/dist/spawn.d.ts +12 -0
  48. package/dist/spawn.js +25 -0
  49. package/dist/spawn.js.map +1 -0
  50. package/package.json +83 -0
@@ -0,0 +1,460 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import * as path from "node:path";
3
+ import { connectNdjson, runInitializeHandshake } from "@hediet/linkrpc/node";
4
+ import * as net from "node:net";
5
+ import * as fs from "node:fs";
6
+ import * as os from "node:os";
7
+ import * as http from "node:http";
8
+ import { WebSocketServer } from "ws";
9
+ //#region src/hub/server/node/socketServer.ts
10
+ /**
11
+ * A {@link Transport} over a Node socket that still exposes the underlying
12
+ * {@link net.Socket}. Provenance providers (peercred, etc.) read `socket`;
13
+ * the hub core never does. Framing is newline-delimited JSON, and the
14
+ * connection has already completed the `hubrpc::initialize` handshake.
15
+ */
16
+ var NodeSocketTransport = class {
17
+ socket;
18
+ _inner;
19
+ topologyInfo;
20
+ _closeHandlers = [];
21
+ _closed = false;
22
+ /**
23
+ * Token presented by the peer in the `hubrpc::initialize` handshake. When
24
+ * the server was started with {@link SocketServerOptions.isTokenAccepted}
25
+ * the token has already been validated (the connection would otherwise have
26
+ * been dropped); a {@link ConnectionProvenanceProvider} can read it to
27
+ * further attest the peer (e.g. map a one-shot run token to an identity).
28
+ * `undefined` when the handshake carried no token.
29
+ */
30
+ initializeToken;
31
+ constructor(socket, _inner, topologyInfo) {
32
+ this.socket = socket;
33
+ this._inner = _inner;
34
+ this.topologyInfo = topologyInfo;
35
+ socket.on("close", () => this._fireClose());
36
+ socket.on("error", () => this.dispose());
37
+ }
38
+ send(message) {
39
+ return this._inner.send(message);
40
+ }
41
+ setListener(listener) {
42
+ this._inner.setListener(listener);
43
+ }
44
+ onDidClose(handler) {
45
+ if (this._closed) {
46
+ queueMicrotask(handler);
47
+ return;
48
+ }
49
+ this._closeHandlers.push(handler);
50
+ }
51
+ dispose() {
52
+ this._inner.dispose();
53
+ try {
54
+ this.socket.destroy();
55
+ } catch {}
56
+ }
57
+ _fireClose() {
58
+ if (this._closed) return;
59
+ this._closed = true;
60
+ for (const handler of this._closeHandlers) handler();
61
+ this._closeHandlers.length = 0;
62
+ }
63
+ };
64
+ /**
65
+ * A hub-agnostic {@link ITransportServer} over a named pipe / UDS. It listens,
66
+ * accepts, runs the `hubrpc::initialize` handshake (authentication + protocol
67
+ * negotiation), frames each socket as a {@link NodeSocketTransport}, and hands
68
+ * it to the connection handler — no hub coupling. Compose
69
+ * attestation/identity on top via `withProvenance` and
70
+ * {@link HubConnectionAcceptor}.
71
+ *
72
+ * The handshake's token is validated via
73
+ * {@link SocketServerOptions.isTokenAccepted} when provided; otherwise any
74
+ * token is accepted and merely captured for a provenance provider.
75
+ */
76
+ var SocketServer = class SocketServer {
77
+ _server;
78
+ _endpoint;
79
+ _live = /* @__PURE__ */ new Set();
80
+ _isTokenAccepted;
81
+ _handler;
82
+ _disposed = false;
83
+ constructor(endpoint, isTokenAccepted) {
84
+ this._endpoint = endpoint;
85
+ this._isTokenAccepted = isTokenAccepted ?? (async () => true);
86
+ this._server = net.createServer((socket) => this._onConnection(socket));
87
+ }
88
+ /** Bind + listen (with stale-UDS unlink retry), resolving once ready. */
89
+ static async start(options = {}) {
90
+ const server = new SocketServer(options.endpoint ?? SocketServer.allocSocketPath(), options.isTokenAccepted);
91
+ await server._listenWithRetry();
92
+ return server;
93
+ }
94
+ /** Allocate a fresh, unused socket path (named pipe on Windows, UDS elsewhere). */
95
+ static allocSocketPath() {
96
+ const id = randomUUID();
97
+ if (process.platform === "win32") return `\\\\.\\pipe\\vscode-linkrpc-${id}`;
98
+ return path.join(os.tmpdir(), `vscode-linkrpc-${id}.sock`);
99
+ }
100
+ get endpoint() {
101
+ return this._endpoint;
102
+ }
103
+ setConnectionHandler(handler) {
104
+ this._handler = handler;
105
+ }
106
+ dispose() {
107
+ if (this._disposed) return;
108
+ this._disposed = true;
109
+ for (const t of this._live) t.dispose();
110
+ this._live.clear();
111
+ this._server.close();
112
+ if (process.platform !== "win32") try {
113
+ fs.unlinkSync(this._endpoint);
114
+ } catch {}
115
+ }
116
+ _onConnection(socket) {
117
+ this._acceptConnection(socket);
118
+ }
119
+ /**
120
+ * Run the `hubrpc::initialize` handshake on the freshly accepted socket,
121
+ * then frame it as a {@link NodeSocketTransport} and hand it to the
122
+ * connection handler. A failed handshake (bad/missing token, wrong first
123
+ * message, or timeout) drops the socket without reaching the handler.
124
+ */
125
+ async _acceptConnection(socket) {
126
+ socket.on("error", () => socket.destroy());
127
+ let connected;
128
+ try {
129
+ connected = await connectNdjson({
130
+ input: socket,
131
+ output: socket,
132
+ initialize: {
133
+ kind: "server",
134
+ isTokenAccepted: this._isTokenAccepted
135
+ }
136
+ });
137
+ } catch {
138
+ socket.destroy();
139
+ return;
140
+ }
141
+ if (this._disposed) {
142
+ connected.transport.dispose();
143
+ socket.destroy();
144
+ return;
145
+ }
146
+ const transport = new NodeSocketTransport(socket, connected.transport, {
147
+ type: process.platform === "win32" ? "named-pipe" : "unix",
148
+ path: this._endpoint
149
+ });
150
+ transport.initializeToken = connected.token;
151
+ this._live.add(transport);
152
+ transport.onDidClose(() => this._live.delete(transport));
153
+ this._handler?.(transport);
154
+ }
155
+ async _listenWithRetry() {
156
+ const isWin = process.platform === "win32";
157
+ const deadline = Date.now() + 1e3;
158
+ let delay = 10;
159
+ while (true) try {
160
+ await this._listenOnce();
161
+ return;
162
+ } catch (err) {
163
+ if (err.code !== "EADDRINUSE") throw err;
164
+ if (!isWin) {
165
+ try {
166
+ fs.unlinkSync(this._endpoint);
167
+ } catch {}
168
+ await this._listenOnce();
169
+ return;
170
+ }
171
+ if (Date.now() >= deadline) throw err;
172
+ await new Promise((r) => setTimeout(r, delay));
173
+ delay = Math.min(delay * 2, 100);
174
+ }
175
+ }
176
+ _listenOnce() {
177
+ return new Promise((resolve, reject) => {
178
+ const onError = (err) => {
179
+ this._server.removeListener("error", onError);
180
+ reject(err);
181
+ };
182
+ this._server.once("error", onError);
183
+ this._server.listen(this._endpoint, () => {
184
+ this._server.removeListener("error", onError);
185
+ resolve();
186
+ });
187
+ });
188
+ }
189
+ };
190
+ //#endregion
191
+ //#region src/hub/server/node/webSocketServer.ts
192
+ /**
193
+ * A {@link Transport} over a server-side `ws` WebSocket. One JSON-RPC message
194
+ * per text frame; binary frames are decoded as UTF-8 and unparseable frames
195
+ * are dropped (the same lenient behaviour as {@link NodeSocketTransport}).
196
+ *
197
+ * The transport takes ownership of the socket: {@link dispose} closes it, and
198
+ * a peer close / error fires {@link onDidClose} handlers exactly once.
199
+ */
200
+ var NodeWebSocketTransport = class {
201
+ socket;
202
+ topologyInfo;
203
+ _listener;
204
+ _buffer = [];
205
+ _closeHandlers = [];
206
+ _closed = false;
207
+ /**
208
+ * Token presented by the peer in the `hubrpc::initialize` handshake, when
209
+ * the server ran one. A {@link ConnectionProvenanceProvider} can read it to
210
+ * attest the peer. `undefined` when the handshake carried no token.
211
+ */
212
+ initializeToken;
213
+ constructor(socket, topologyInfo) {
214
+ this.socket = socket;
215
+ this.topologyInfo = topologyInfo;
216
+ socket.on("message", (data, isBinary) => {
217
+ const text = isBinary ? Buffer.isBuffer(data) ? data.toString("utf8") : Array.isArray(data) ? Buffer.concat(data).toString("utf8") : Buffer.from(data).toString("utf8") : data.toString();
218
+ for (const line of text.split("\n")) {
219
+ const trimmed = line.trim();
220
+ if (!trimmed) continue;
221
+ let parsed;
222
+ try {
223
+ parsed = JSON.parse(trimmed);
224
+ } catch {
225
+ continue;
226
+ }
227
+ this._deliver(parsed);
228
+ }
229
+ });
230
+ const onEnd = () => {
231
+ if (this._closed) return;
232
+ this._closed = true;
233
+ this._fireClose();
234
+ };
235
+ socket.on("close", onEnd);
236
+ socket.on("error", onEnd);
237
+ }
238
+ send(message) {
239
+ if (this._closed) return;
240
+ if (this.socket.readyState !== 1) return;
241
+ this.socket.send(JSON.stringify(message));
242
+ }
243
+ setListener(listener) {
244
+ this._listener = listener;
245
+ if (listener) while (this._buffer.length > 0 && this._listener) {
246
+ const m = this._buffer.shift();
247
+ this._listener(m);
248
+ }
249
+ }
250
+ onDidClose(handler) {
251
+ if (this._closed) {
252
+ queueMicrotask(handler);
253
+ return;
254
+ }
255
+ this._closeHandlers.push(handler);
256
+ }
257
+ dispose() {
258
+ if (this._closed) return;
259
+ this._closed = true;
260
+ try {
261
+ this.socket.close();
262
+ } catch {}
263
+ this._fireClose();
264
+ }
265
+ _fireClose() {
266
+ const handlers = this._closeHandlers.splice(0);
267
+ for (const h of handlers) try {
268
+ h();
269
+ } catch {}
270
+ }
271
+ _deliver(m) {
272
+ if (this._listener) this._listener(m);
273
+ else this._buffer.push(m);
274
+ }
275
+ };
276
+ /**
277
+ * A hub-agnostic {@link ITransportServer} over WebSocket. It owns an
278
+ * `http.Server`, gates each upgrade by origin, runs the `hubrpc::initialize`
279
+ * handshake (authentication + protocol negotiation) over each accepted socket,
280
+ * and frames it as a {@link NodeWebSocketTransport}. Compose identity/claim
281
+ * policy on top via {@link HubConnectionAcceptor} — this server never couples
282
+ * to the hub.
283
+ *
284
+ * Sibling of {@link SocketServer} (named pipe / UDS) for reaching the hub from
285
+ * outside the host over `ws://` / `wss://`.
286
+ */
287
+ var WebSocketServer$1 = class WebSocketServer$1 {
288
+ _opts;
289
+ _httpServer;
290
+ _wss;
291
+ _path;
292
+ _attached = /* @__PURE__ */ new Set();
293
+ _sockets = /* @__PURE__ */ new Set();
294
+ _handler;
295
+ _disposed = false;
296
+ constructor(_opts) {
297
+ this._opts = _opts;
298
+ this._path = _opts.path ?? "/";
299
+ if (!_opts.allowAnonymous && !_opts.isTokenAccepted) throw new Error("WebSocketServer: either `isTokenAccepted` must be provided or `allowAnonymous` must be true");
300
+ this._httpServer = http.createServer(_opts.requestListener ?? ((_req, res) => {
301
+ res.writeHead(426, { "content-type": "text/plain" });
302
+ res.end("upgrade required");
303
+ }));
304
+ this._wss = new WebSocketServer({
305
+ noServer: true,
306
+ maxPayload: _opts.maxPayload ?? 16777216
307
+ });
308
+ this._httpServer.on("upgrade", (req, socket, head) => this._onUpgrade(req, socket, head));
309
+ this._wss.on("connection", (ws, req) => this._onConnection(ws, req));
310
+ }
311
+ /** Bind + listen, resolving once the port is open. */
312
+ static start(options = {}) {
313
+ const server = new WebSocketServer$1(options);
314
+ return new Promise((resolve, reject) => {
315
+ const onError = (err) => reject(err);
316
+ server._httpServer.once("error", onError);
317
+ server._httpServer.listen(options.port ?? 0, options.host, () => {
318
+ server._httpServer.removeListener("error", onError);
319
+ resolve(server);
320
+ });
321
+ });
322
+ }
323
+ /** The bound port (resolved even when started with port `0`). */
324
+ get port() {
325
+ const addr = this._httpServer.address();
326
+ if (addr && typeof addr === "object") return addr.port;
327
+ return 0;
328
+ }
329
+ setConnectionHandler(handler) {
330
+ this._handler = handler;
331
+ }
332
+ dispose() {
333
+ if (this._disposed) return;
334
+ this._disposed = true;
335
+ for (const t of this._attached) t.dispose();
336
+ this._attached.clear();
337
+ for (const s of this._sockets) try {
338
+ s.terminate();
339
+ } catch {}
340
+ this._sockets.clear();
341
+ this._wss.close();
342
+ this._httpServer.close();
343
+ }
344
+ _onUpgrade(req, socket, head) {
345
+ if (new URL(req.url ?? "/", "http://localhost").pathname !== this._path) {
346
+ this._reject(socket, 404, "not found", req);
347
+ return;
348
+ }
349
+ const allowedOrigins = this._opts.allowedOrigins;
350
+ if (allowedOrigins && allowedOrigins.length > 0) {
351
+ const origin = req.headers.origin;
352
+ if (origin && !allowedOrigins.includes(origin)) {
353
+ this._reject(socket, 403, "origin not allowed", req);
354
+ return;
355
+ }
356
+ }
357
+ this._wss.handleUpgrade(req, socket, head, (ws) => {
358
+ this._wss.emit("connection", ws, req);
359
+ });
360
+ }
361
+ _onConnection(ws, req) {
362
+ this._acceptConnection(ws, req);
363
+ }
364
+ /**
365
+ * Frame the upgraded socket, run the `hubrpc::initialize` handshake
366
+ * (authentication + protocol negotiation), then hand the transport to the
367
+ * connection handler. A failed handshake (bad/missing token, wrong first
368
+ * message, or timeout) drops the socket without reaching the handler.
369
+ */
370
+ async _acceptConnection(ws, req) {
371
+ if (this._disposed) {
372
+ try {
373
+ ws.terminate();
374
+ } catch {}
375
+ return;
376
+ }
377
+ this._sockets.add(ws);
378
+ ws.once("close", () => this._sockets.delete(ws));
379
+ const transport = new NodeWebSocketTransport(ws, _webSocketTopologyInfo(req, this._path));
380
+ const isTokenAccepted = this._opts.isTokenAccepted ?? (async () => true);
381
+ let token;
382
+ try {
383
+ ({token} = await runInitializeHandshake(transport, {
384
+ kind: "server",
385
+ isTokenAccepted
386
+ }));
387
+ } catch {
388
+ transport.dispose();
389
+ this._opts.onEvent?.({
390
+ kind: "rejected",
391
+ reason: "401 unauthorized",
392
+ remoteAddress: _remoteAddr(req)
393
+ });
394
+ return;
395
+ }
396
+ if (this._disposed) {
397
+ transport.dispose();
398
+ return;
399
+ }
400
+ transport.initializeToken = token;
401
+ transport.onDidClose(() => {
402
+ this._attached.delete(transport);
403
+ this._opts.onEvent?.({
404
+ kind: "closed",
405
+ remoteAddress: _remoteAddr(req)
406
+ });
407
+ });
408
+ this._attached.add(transport);
409
+ this._handler?.(transport);
410
+ this._opts.onEvent?.({
411
+ kind: "accepted",
412
+ remoteAddress: _remoteAddr(req)
413
+ });
414
+ }
415
+ _reject(socket, status, message, req) {
416
+ this._opts.onEvent?.({
417
+ kind: "rejected",
418
+ reason: `${status} ${message}`,
419
+ remoteAddress: _remoteAddr(req)
420
+ });
421
+ try {
422
+ socket.write(`HTTP/1.1 ${status} ${message}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n`);
423
+ } catch {}
424
+ try {
425
+ socket.destroy();
426
+ } catch {}
427
+ }
428
+ };
429
+ function _remoteAddr(req) {
430
+ return _forwardedFor(req) ?? req.socket.remoteAddress ?? void 0;
431
+ }
432
+ function _webSocketTopologyInfo(req, path) {
433
+ const forwardedFor = _forwardedFor(req);
434
+ const origin = req.headers.origin;
435
+ return {
436
+ type: "websocket",
437
+ path,
438
+ ...req.socket.localAddress !== void 0 || req.socket.localPort !== void 0 ? { local: {
439
+ ...req.socket.localAddress !== void 0 ? { address: req.socket.localAddress } : {},
440
+ ...req.socket.localPort !== void 0 ? { port: req.socket.localPort } : {}
441
+ } } : {},
442
+ ...req.socket.remoteAddress !== void 0 || req.socket.remotePort !== void 0 ? { remote: {
443
+ ...req.socket.remoteAddress !== void 0 ? { address: req.socket.remoteAddress } : {},
444
+ ...req.socket.remotePort !== void 0 ? { port: req.socket.remotePort } : {}
445
+ } } : {},
446
+ ...forwardedFor !== void 0 || origin !== void 0 ? { metadata: {
447
+ ...forwardedFor !== void 0 ? { forwardedFor } : {},
448
+ ...origin !== void 0 ? { origin } : {}
449
+ } } : {}
450
+ };
451
+ }
452
+ function _forwardedFor(req) {
453
+ const value = req.headers["x-forwarded-for"];
454
+ if (typeof value !== "string") return void 0;
455
+ return value.split(",")[0]?.trim() || void 0;
456
+ }
457
+ //#endregion
458
+ export { SocketServer as i, WebSocketServer$1 as n, NodeSocketTransport as r, NodeWebSocketTransport as t };
459
+
460
+ //# sourceMappingURL=node-CTXsQ6oa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-CTXsQ6oa.js","names":["WebSocketServer","WsServer"],"sources":["../../src/hub/server/node/socketServer.ts","../../src/hub/server/node/webSocketServer.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as net from 'node:net';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { connectNdjson } from '@hediet/linkrpc/node';\nimport type { IMessageTransport, JsonRpcMessage } from '@hediet/linkrpc';\nimport type {\n ITransportServer,\n Transport,\n} from '@hediet/linkrpc/hub/common';\nimport type { TopologyTransportInfo } from '@hediet/linkrpc/inspection';\n\n/**\n * A {@link Transport} over a Node socket that still exposes the underlying\n * {@link net.Socket}. Provenance providers (peercred, etc.) read `socket`;\n * the hub core never does. Framing is newline-delimited JSON, and the\n * connection has already completed the `hubrpc::initialize` handshake.\n */\nexport class NodeSocketTransport implements Transport {\n private readonly _closeHandlers: (() => void)[] = [];\n private _closed = false;\n\n /**\n * Token presented by the peer in the `hubrpc::initialize` handshake. When\n * the server was started with {@link SocketServerOptions.isTokenAccepted}\n * the token has already been validated (the connection would otherwise have\n * been dropped); a {@link ConnectionProvenanceProvider} can read it to\n * further attest the peer (e.g. map a one-shot run token to an identity).\n * `undefined` when the handshake carried no token.\n */\n public initializeToken: string | undefined;\n\n constructor(\n public readonly socket: net.Socket,\n private readonly _inner: IMessageTransport,\n public readonly topologyInfo?: TopologyTransportInfo,\n ) {\n socket.on('close', () => this._fireClose());\n socket.on('error', () => this.dispose());\n }\n\n public send(message: JsonRpcMessage): void | Promise<void> {\n return this._inner.send(message);\n }\n\n public setListener(listener: ((message: JsonRpcMessage) => void) | undefined): void {\n this._inner.setListener(listener);\n }\n\n public onDidClose(handler: () => void): void {\n if (this._closed) {\n queueMicrotask(handler);\n return;\n }\n this._closeHandlers.push(handler);\n }\n\n public dispose(): void {\n this._inner.dispose();\n try {\n this.socket.destroy();\n } catch { /* ignore */ }\n }\n\n private _fireClose(): void {\n if (this._closed) return;\n this._closed = true;\n for (const handler of this._closeHandlers) {\n handler();\n }\n this._closeHandlers.length = 0;\n }\n}\n\nexport interface SocketServerOptions {\n /**\n * Named pipe (Windows) / UDS path (Unix) to listen on. Defaults to a\n * per-instance computed path.\n */\n readonly endpoint?: string;\n /**\n * Validate the token presented in the client's `hubrpc::initialize`\n * handshake. Resolve `true` to admit the connection, `false` to reject it\n * (the socket is dropped before it reaches the connection handler). Called\n * live on every connection, so a dynamic allow-list (e.g. one-shot run\n * tokens added for the lifetime of a spawned child) is re-read per connect.\n *\n * Omit to accept any token: the handshake is still required and the token\n * is captured onto {@link NodeSocketTransport.initializeToken} for a\n * provenance provider, but it is not gated.\n */\n readonly isTokenAccepted?: (token: string | undefined) => Promise<boolean>;\n}\n\n/**\n * A hub-agnostic {@link ITransportServer} over a named pipe / UDS. It listens,\n * accepts, runs the `hubrpc::initialize` handshake (authentication + protocol\n * negotiation), frames each socket as a {@link NodeSocketTransport}, and hands\n * it to the connection handler — no hub coupling. Compose\n * attestation/identity on top via `withProvenance` and\n * {@link HubConnectionAcceptor}.\n *\n * The handshake's token is validated via\n * {@link SocketServerOptions.isTokenAccepted} when provided; otherwise any\n * token is accepted and merely captured for a provenance provider.\n */\nexport class SocketServer implements ITransportServer<NodeSocketTransport> {\n private readonly _server: net.Server;\n private readonly _endpoint: string;\n private readonly _live = new Set<NodeSocketTransport>();\n private readonly _isTokenAccepted: (token: string | undefined) => Promise<boolean>;\n private _handler: ((transport: NodeSocketTransport) => void) | undefined;\n private _disposed = false;\n\n private constructor(\n endpoint: string,\n isTokenAccepted: ((token: string | undefined) => Promise<boolean>) | undefined,\n ) {\n this._endpoint = endpoint;\n this._isTokenAccepted = isTokenAccepted ?? (async () => true);\n this._server = net.createServer((socket) => this._onConnection(socket));\n }\n\n /** Bind + listen (with stale-UDS unlink retry), resolving once ready. */\n public static async start(options: SocketServerOptions = {}): Promise<SocketServer> {\n const server = new SocketServer(\n options.endpoint ?? SocketServer.allocSocketPath(),\n options.isTokenAccepted,\n );\n await server._listenWithRetry();\n return server;\n }\n\n /** Allocate a fresh, unused socket path (named pipe on Windows, UDS elsewhere). */\n public static allocSocketPath(): string {\n const id = randomUUID();\n if (process.platform === 'win32') {\n return `\\\\\\\\.\\\\pipe\\\\vscode-linkrpc-${id}`;\n }\n return path.join(os.tmpdir(), `vscode-linkrpc-${id}.sock`);\n }\n\n public get endpoint(): string {\n return this._endpoint;\n }\n\n public setConnectionHandler(handler: (transport: NodeSocketTransport) => void): void {\n this._handler = handler;\n }\n\n public dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n for (const t of this._live) {\n t.dispose();\n }\n this._live.clear();\n this._server.close();\n if (process.platform !== 'win32') {\n try {\n fs.unlinkSync(this._endpoint);\n } catch { /* ignore */ }\n }\n }\n\n private _onConnection(socket: net.Socket): void {\n void this._acceptConnection(socket);\n }\n\n /**\n * Run the `hubrpc::initialize` handshake on the freshly accepted socket,\n * then frame it as a {@link NodeSocketTransport} and hand it to the\n * connection handler. A failed handshake (bad/missing token, wrong first\n * message, or timeout) drops the socket without reaching the handler.\n */\n private async _acceptConnection(socket: net.Socket): Promise<void> {\n socket.on('error', () => socket.destroy());\n let connected: { transport: IMessageTransport; token?: string; };\n try {\n connected = await connectNdjson({\n input: socket,\n output: socket,\n initialize: { kind: 'server', isTokenAccepted: this._isTokenAccepted },\n });\n } catch {\n socket.destroy();\n return;\n }\n if (this._disposed) {\n connected.transport.dispose();\n socket.destroy();\n return;\n }\n const transport = new NodeSocketTransport(\n socket,\n connected.transport,\n {\n type: process.platform === 'win32' ? 'named-pipe' : 'unix',\n path: this._endpoint,\n },\n );\n transport.initializeToken = connected.token;\n this._live.add(transport);\n transport.onDidClose(() => this._live.delete(transport));\n this._handler?.(transport);\n }\n\n private async _listenWithRetry(): Promise<void> { // On Unix, EADDRINUSE on our deterministic path means a stale UDS from\n // a crashed instance — unlink and retry once. On Windows, named pipes\n // are kernel-managed and may linger briefly after close — retry with\n // backoff over a bounded window.\n const isWin = process.platform === 'win32';\n const deadline = Date.now() + 1000;\n let delay = 10;\n while (true) {\n try {\n await this._listenOnce();\n return;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw err;\n if (!isWin) {\n try {\n fs.unlinkSync(this._endpoint);\n } catch { /* ignore */ }\n await this._listenOnce();\n return;\n }\n if (Date.now() >= deadline) throw err;\n await new Promise((r) => setTimeout(r, delay));\n delay = Math.min(delay * 2, 100);\n }\n }\n }\n\n private _listenOnce(): Promise<void> {\n return new Promise((resolve, reject) => {\n const onError = (err: Error) => {\n this._server.removeListener('error', onError);\n reject(err);\n };\n this._server.once('error', onError);\n this._server.listen(this._endpoint, () => {\n this._server.removeListener('error', onError);\n resolve();\n });\n });\n }\n}\n","import * as http from 'node:http';\nimport { type WebSocket, WebSocketServer as WsServer } from 'ws';\nimport type { JsonRpcMessage } from '@hediet/linkrpc';\nimport { runInitializeHandshake } from '@hediet/linkrpc/node';\nimport type {\n ITransportServer,\n Transport,\n} from '@hediet/linkrpc/hub/common';\nimport type { TopologyTransportInfo } from '@hediet/linkrpc/inspection';\n\n/**\n * A {@link Transport} over a server-side `ws` WebSocket. One JSON-RPC message\n * per text frame; binary frames are decoded as UTF-8 and unparseable frames\n * are dropped (the same lenient behaviour as {@link NodeSocketTransport}).\n *\n * The transport takes ownership of the socket: {@link dispose} closes it, and\n * a peer close / error fires {@link onDidClose} handlers exactly once.\n */\nexport class NodeWebSocketTransport implements Transport {\n private _listener: ((m: JsonRpcMessage) => void) | undefined;\n private readonly _buffer: JsonRpcMessage[] = [];\n private readonly _closeHandlers: (() => void)[] = [];\n private _closed = false;\n\n /**\n * Token presented by the peer in the `hubrpc::initialize` handshake, when\n * the server ran one. A {@link ConnectionProvenanceProvider} can read it to\n * attest the peer. `undefined` when the handshake carried no token.\n */\n public initializeToken: string | undefined;\n\n constructor(\n public readonly socket: WebSocket,\n public readonly topologyInfo?: TopologyTransportInfo,\n ) {\n socket.on('message', (data, isBinary) => {\n const text = isBinary ?\n Buffer.isBuffer(data) ?\n data.toString('utf8') :\n Array.isArray(data) ?\n Buffer.concat(data).toString('utf8') :\n Buffer.from(data as ArrayBuffer).toString('utf8') :\n data.toString();\n for (const line of text.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n let parsed: JsonRpcMessage;\n try {\n parsed = JSON.parse(trimmed) as JsonRpcMessage;\n } catch {\n continue;\n }\n this._deliver(parsed);\n }\n });\n const onEnd = (): void => {\n if (this._closed) return;\n this._closed = true;\n this._fireClose();\n };\n socket.on('close', onEnd);\n socket.on('error', onEnd);\n }\n\n public send(message: JsonRpcMessage): void {\n if (this._closed) return;\n if (this.socket.readyState !== 1 /* OPEN */) return;\n this.socket.send(JSON.stringify(message));\n }\n\n public setListener(listener: ((m: JsonRpcMessage) => void) | undefined): void {\n this._listener = listener;\n if (listener) {\n while (this._buffer.length > 0 && this._listener) {\n const m = this._buffer.shift()!;\n this._listener(m);\n }\n }\n }\n\n public onDidClose(handler: () => void): void {\n if (this._closed) {\n queueMicrotask(handler);\n return;\n }\n this._closeHandlers.push(handler);\n }\n\n public dispose(): void {\n if (this._closed) return;\n this._closed = true;\n try {\n this.socket.close();\n } catch { /* ignore */ }\n this._fireClose();\n }\n\n private _fireClose(): void {\n const handlers = this._closeHandlers.splice(0);\n for (const h of handlers) {\n try {\n h();\n } catch { /* ignore */ }\n }\n }\n\n private _deliver(m: JsonRpcMessage): void {\n if (this._listener) this._listener(m);\n else this._buffer.push(m);\n }\n}\n\nexport type WebSocketServerEvent =\n | { readonly kind: 'accepted'; readonly remoteAddress: string | undefined; }\n | { readonly kind: 'rejected'; readonly reason: string; readonly remoteAddress: string | undefined; }\n | { readonly kind: 'closed'; readonly remoteAddress: string | undefined; };\n\nexport interface WebSocketServerOptions {\n /** TCP port to listen on. Defaults to an OS-assigned free port (`0`). */\n readonly port?: number;\n /** Host/interface to bind. Defaults to all interfaces. */\n readonly host?: string;\n /**\n * Validate the token presented in the client's `hubrpc::initialize`\n * handshake. Resolve `true` to admit the connection, `false` to reject it.\n * Required unless {@link allowAnonymous} is set. Browsers cannot set the\n * `Authorization` header on the `WebSocket` constructor, so auth happens\n * over the in-band handshake rather than the HTTP upgrade.\n */\n readonly isTokenAccepted?: (token: string | undefined) => Promise<boolean>;\n /**\n * Allow connections without validating a token: the `hubrpc::initialize`\n * handshake still runs (and its token is captured), but any token is\n * accepted. Use ONLY behind a trusted reverse proxy that performs auth\n * itself, or on a private interface.\n */\n readonly allowAnonymous?: boolean;\n /**\n * Optional `Origin` allow-list for browser clients. When set, the upgrade\n * is rejected if `Origin` is present and not in the list. Non-browser\n * clients (which send no `Origin`) are unaffected.\n */\n readonly allowedOrigins?: readonly string[];\n /**\n * URL path the WebSocket endpoint is mounted at. Defaults to `/`. Any\n * other path gets a `404`, so the same server can host health checks.\n */\n readonly path?: string;\n /** Bytes. Frames larger than this close the connection. Defaults to 16 MiB. */\n readonly maxPayload?: number;\n /** Optional logger called on accept / reject / close. */\n readonly onEvent?: (event: WebSocketServerEvent) => void;\n /**\n * Optional handler for ordinary (non-upgrade) HTTP requests on the same\n * port. An Express app is a valid {@link http.RequestListener}, so this\n * lets the WebSocket endpoint and e.g. a `/health` route share one port.\n * Defaults to replying `426 upgrade required` to every request.\n */\n readonly requestListener?: http.RequestListener;\n}\n\n/**\n * A hub-agnostic {@link ITransportServer} over WebSocket. It owns an\n * `http.Server`, gates each upgrade by origin, runs the `hubrpc::initialize`\n * handshake (authentication + protocol negotiation) over each accepted socket,\n * and frames it as a {@link NodeWebSocketTransport}. Compose identity/claim\n * policy on top via {@link HubConnectionAcceptor} — this server never couples\n * to the hub.\n *\n * Sibling of {@link SocketServer} (named pipe / UDS) for reaching the hub from\n * outside the host over `ws://` / `wss://`.\n */\nexport class WebSocketServer implements ITransportServer<NodeWebSocketTransport> {\n private readonly _httpServer: http.Server;\n private readonly _wss: WsServer;\n private readonly _path: string;\n private readonly _attached = new Set<NodeWebSocketTransport>();\n private readonly _sockets = new Set<WebSocket>();\n private _handler: ((transport: NodeWebSocketTransport) => void) | undefined;\n private _disposed = false;\n\n private constructor(private readonly _opts: WebSocketServerOptions) {\n this._path = _opts.path ?? '/';\n if (!_opts.allowAnonymous && !_opts.isTokenAccepted) {\n throw new Error(\n 'WebSocketServer: either `isTokenAccepted` must be provided or `allowAnonymous` must be true',\n );\n }\n this._httpServer = http.createServer(\n _opts.requestListener ??\n ((_req, res) => {\n res.writeHead(426, { 'content-type': 'text/plain' });\n res.end('upgrade required');\n }),\n );\n this._wss = new WsServer({\n noServer: true,\n maxPayload: _opts.maxPayload ?? 16 * 1024 * 1024,\n });\n this._httpServer.on('upgrade', (req, socket, head) => this._onUpgrade(req, socket, head));\n this._wss.on('connection', (ws, req) => this._onConnection(ws, req));\n }\n\n /** Bind + listen, resolving once the port is open. */\n public static start(options: WebSocketServerOptions = {}): Promise<WebSocketServer> {\n const server = new WebSocketServer(options);\n return new Promise<WebSocketServer>((resolve, reject) => {\n const onError = (err: Error): void => reject(err);\n server._httpServer.once('error', onError);\n server._httpServer.listen(options.port ?? 0, options.host, () => {\n server._httpServer.removeListener('error', onError);\n resolve(server);\n });\n });\n }\n\n /** The bound port (resolved even when started with port `0`). */\n public get port(): number {\n const addr = this._httpServer.address();\n if (addr && typeof addr === 'object') return addr.port;\n return 0;\n }\n\n public setConnectionHandler(handler: (transport: NodeWebSocketTransport) => void): void {\n this._handler = handler;\n }\n\n public dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n for (const t of this._attached) {\n t.dispose();\n }\n this._attached.clear();\n for (const s of this._sockets) {\n try {\n s.terminate();\n } catch { /* ignore */ }\n }\n this._sockets.clear();\n this._wss.close();\n this._httpServer.close();\n }\n\n private _onUpgrade(\n req: http.IncomingMessage,\n socket: NodeJS.WritableStream & { destroy: () => void; },\n head: Buffer,\n ): void {\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.pathname !== this._path) {\n this._reject(socket, 404, 'not found', req);\n return;\n }\n const allowedOrigins = this._opts.allowedOrigins;\n if (allowedOrigins && allowedOrigins.length > 0) {\n const origin = req.headers.origin;\n if (origin && !allowedOrigins.includes(origin)) {\n this._reject(socket, 403, 'origin not allowed', req);\n return;\n }\n }\n this._wss.handleUpgrade(req, socket as never, head, (ws) => {\n this._wss.emit('connection', ws, req);\n });\n }\n\n private _onConnection(ws: WebSocket, req: http.IncomingMessage): void {\n void this._acceptConnection(ws, req);\n }\n\n /**\n * Frame the upgraded socket, run the `hubrpc::initialize` handshake\n * (authentication + protocol negotiation), then hand the transport to the\n * connection handler. A failed handshake (bad/missing token, wrong first\n * message, or timeout) drops the socket without reaching the handler.\n */\n private async _acceptConnection(ws: WebSocket, req: http.IncomingMessage): Promise<void> {\n if (this._disposed) {\n try {\n ws.terminate();\n } catch { /* ignore */ }\n return;\n }\n this._sockets.add(ws);\n ws.once('close', () => this._sockets.delete(ws));\n\n const transport = new NodeWebSocketTransport(\n ws,\n _webSocketTopologyInfo(req, this._path),\n );\n const isTokenAccepted = this._opts.isTokenAccepted ?? (async () => true);\n let token: string | undefined;\n try {\n ({ token } = await runInitializeHandshake(transport, { kind: 'server', isTokenAccepted }));\n } catch {\n transport.dispose();\n this._opts.onEvent?.({ kind: 'rejected', reason: '401 unauthorized', remoteAddress: _remoteAddr(req) });\n return;\n }\n if (this._disposed) {\n transport.dispose();\n return;\n }\n transport.initializeToken = token;\n transport.onDidClose(() => {\n this._attached.delete(transport);\n this._opts.onEvent?.({ kind: 'closed', remoteAddress: _remoteAddr(req) });\n });\n this._attached.add(transport);\n this._handler?.(transport);\n this._opts.onEvent?.({ kind: 'accepted', remoteAddress: _remoteAddr(req) });\n }\n\n private _reject(\n socket: NodeJS.WritableStream & { destroy: () => void; },\n status: number,\n message: string,\n req: http.IncomingMessage,\n ): void {\n this._opts.onEvent?.({ kind: 'rejected', reason: `${status} ${message}`, remoteAddress: _remoteAddr(req) });\n try {\n socket.write(\n `HTTP/1.1 ${status} ${message}\\r\\n` +\n `Content-Length: 0\\r\\n` +\n `Connection: close\\r\\n` +\n `\\r\\n`,\n );\n } catch { /* ignore */ }\n try {\n socket.destroy();\n } catch { /* ignore */ }\n }\n}\n\nfunction _remoteAddr(req: http.IncomingMessage): string | undefined {\n return _forwardedFor(req) ?? req.socket.remoteAddress ?? undefined;\n}\n\nfunction _webSocketTopologyInfo(\n req: http.IncomingMessage,\n path: string,\n): TopologyTransportInfo {\n const forwardedFor = _forwardedFor(req);\n const origin = req.headers.origin;\n return {\n type: 'websocket',\n path,\n ...(req.socket.localAddress !== undefined || req.socket.localPort !== undefined\n ? {\n local: {\n ...(req.socket.localAddress !== undefined\n ? { address: req.socket.localAddress }\n : {}),\n ...(req.socket.localPort !== undefined ? { port: req.socket.localPort } : {}),\n },\n }\n : {}),\n ...(req.socket.remoteAddress !== undefined || req.socket.remotePort !== undefined\n ? {\n remote: {\n ...(req.socket.remoteAddress !== undefined\n ? { address: req.socket.remoteAddress }\n : {}),\n ...(req.socket.remotePort !== undefined ? { port: req.socket.remotePort } : {}),\n },\n }\n : {}),\n ...(forwardedFor !== undefined || origin !== undefined\n ? {\n metadata: {\n ...(forwardedFor !== undefined ? { forwardedFor } : {}),\n ...(origin !== undefined ? { origin } : {}),\n },\n }\n : {}),\n };\n}\n\nfunction _forwardedFor(req: http.IncomingMessage): string | undefined {\n const value = req.headers['x-forwarded-for'];\n if (typeof value !== 'string') return undefined;\n return value.split(',')[0]?.trim() || undefined;\n}\n"],"mappings":"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"}