agent-yes 1.195.0 → 1.196.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.
@@ -1,210 +1,258 @@
1
- function k() {
1
+ // src/shared/signaling.ts
2
+ var CLIENT_WIRE_ROLE = "viewer";
3
+ function newPeerId() {
2
4
  return crypto.randomUUID();
3
5
  }
4
- var g = 60000,
5
- u = 1e4,
6
- L = 1000,
7
- b = 120000,
8
- O = 25000;
9
- class j {
6
+
7
+ // src/shared/signaling-client.ts
8
+ var STABLE_MS = 60000;
9
+ var CONNECT_TIMEOUT_MS = 1e4;
10
+ var RECONNECT_MIN_MS = 1000;
11
+ var RECONNECT_MAX_MS = 120000;
12
+ var HEARTBEAT_MS = 25000;
13
+
14
+ class SignalingClient {
10
15
  opts;
11
16
  peerId;
12
17
  ws = null;
13
- closed = !1;
14
- reconnectDelay = L;
18
+ closed = false;
19
+ reconnectDelay = RECONNECT_MIN_MS;
15
20
  reconnectTimer = null;
16
- dormant = !1;
21
+ dormant = false;
17
22
  heartbeat = null;
18
23
  stableTimer = null;
19
24
  openedAt = 0;
20
- constructor(z) {
21
- this.opts = z;
22
- this.peerId = z.peerId ?? k();
25
+ constructor(opts) {
26
+ this.opts = opts;
27
+ this.peerId = opts.peerId ?? newPeerId();
23
28
  }
24
29
  connect() {
25
- ((this.closed = !1), this.attachWakeListeners(), this.open());
30
+ this.closed = false;
31
+ this.attachWakeListeners();
32
+ this.open();
26
33
  }
27
34
  onWake = () => {
28
- if (this.closed) return;
29
- let z = this.ws?.readyState;
30
- if (z === 1) return;
31
- if (z === 0) {
35
+ if (this.closed)
36
+ return;
37
+ const state = this.ws?.readyState;
38
+ if (state === 1)
39
+ return;
40
+ if (state === 0) {
32
41
  try {
33
42
  this.ws?.close();
34
43
  } catch {}
35
44
  return;
36
45
  }
37
- if (this.dormant || this.reconnectTimer != null)
38
- ((this.dormant = !1), this.clearReconnectTimer(), this.open());
46
+ if (this.dormant || this.reconnectTimer != null) {
47
+ this.dormant = false;
48
+ this.clearReconnectTimer();
49
+ this.open();
50
+ }
39
51
  };
40
52
  hidden() {
41
- return globalThis.document?.visibilityState === "hidden";
53
+ const doc = globalThis.document;
54
+ return doc?.visibilityState === "hidden";
42
55
  }
43
56
  attachWakeListeners() {
44
- globalThis.document?.addEventListener("visibilitychange", this.onWake);
45
- let Q = globalThis.window;
46
- (Q?.addEventListener("focus", this.onWake), Q?.addEventListener("online", this.onWake));
57
+ const doc = globalThis.document;
58
+ doc?.addEventListener("visibilitychange", this.onWake);
59
+ const win = globalThis.window;
60
+ win?.addEventListener("focus", this.onWake);
61
+ win?.addEventListener("online", this.onWake);
47
62
  }
48
63
  detachWakeListeners() {
49
- globalThis.document?.removeEventListener("visibilitychange", this.onWake);
50
- let Q = globalThis.window;
51
- (Q?.removeEventListener("focus", this.onWake), Q?.removeEventListener("online", this.onWake));
64
+ const doc = globalThis.document;
65
+ doc?.removeEventListener("visibilitychange", this.onWake);
66
+ const win = globalThis.window;
67
+ win?.removeEventListener("focus", this.onWake);
68
+ win?.removeEventListener("online", this.onWake);
52
69
  }
53
70
  roomUrl() {
54
- return `${this.opts.url.replace(/\/+$/, "")}/room/${encodeURIComponent(this.opts.token)}`;
71
+ const base = this.opts.url.replace(/\/+$/, "");
72
+ return `${base}/room/${encodeURIComponent(this.opts.token)}`;
55
73
  }
56
74
  open() {
57
- let z = new WebSocket(this.roomUrl());
58
- this.ws = z;
59
- let Q = setTimeout(() => {
60
- if (z.readyState === 0)
75
+ const ws = new WebSocket(this.roomUrl());
76
+ this.ws = ws;
77
+ const connectTimer = setTimeout(() => {
78
+ if (ws.readyState === 0) {
61
79
  try {
62
- z.close();
80
+ ws.close();
63
81
  } catch {}
64
- }, u);
65
- ((z.onopen = () => {
66
- (clearTimeout(Q),
67
- (this.openedAt = Date.now()),
68
- this.clearStableTimer(),
69
- (this.stableTimer = setTimeout(() => {
70
- this.reconnectDelay = L;
71
- }, g)));
72
- let Y = {
82
+ }
83
+ }, CONNECT_TIMEOUT_MS);
84
+ ws.onopen = () => {
85
+ clearTimeout(connectTimer);
86
+ this.openedAt = Date.now();
87
+ this.clearStableTimer();
88
+ this.stableTimer = setTimeout(() => {
89
+ this.reconnectDelay = RECONNECT_MIN_MS;
90
+ }, STABLE_MS);
91
+ const hello = {
73
92
  type: "hello",
74
93
  role: this.opts.role,
75
94
  peerId: this.peerId,
76
- ...(this.opts.meta ? { meta: this.opts.meta } : {}),
95
+ ...this.opts.meta ? { meta: this.opts.meta } : {}
77
96
  };
78
- (z.send(JSON.stringify(Y)), this.startHeartbeat(), this.opts.onOpen?.());
79
- }),
80
- (z.onmessage = (Y) => {
81
- let Z;
82
- try {
83
- Z = JSON.parse(String(Y.data));
84
- } catch {
85
- return;
86
- }
87
- if (Z.type === "peers") this.opts.onPeers?.(Z.peers);
88
- else if (Z.type === "signal") this.opts.onSignal?.(Z.from, Z.data);
89
- }),
90
- (z.onclose = (Y) => {
91
- (clearTimeout(Q), this.clearStableTimer(), this.stopHeartbeat());
92
- let Z = this.openedAt ? Date.now() - this.openedAt : 0;
93
- if (
94
- ((this.openedAt = 0),
95
- this.opts.onClose?.({ code: Y?.code ?? 0, reason: Y?.reason ?? "", ms: Z }),
96
- !this.closed)
97
- )
98
- this.scheduleReconnect();
99
- }),
100
- (z.onerror = () => {
101
- try {
102
- z.close();
103
- } catch {}
104
- }));
97
+ ws.send(JSON.stringify(hello));
98
+ this.startHeartbeat();
99
+ this.opts.onOpen?.();
100
+ };
101
+ ws.onmessage = (ev) => {
102
+ let msg;
103
+ try {
104
+ msg = JSON.parse(String(ev.data));
105
+ } catch {
106
+ return;
107
+ }
108
+ if (msg.type === "peers")
109
+ this.opts.onPeers?.(msg.peers, msg.now);
110
+ else if (msg.type === "signal")
111
+ this.opts.onSignal?.(msg.from, msg.data);
112
+ };
113
+ ws.onclose = (ev) => {
114
+ clearTimeout(connectTimer);
115
+ this.clearStableTimer();
116
+ this.stopHeartbeat();
117
+ const ms = this.openedAt ? Date.now() - this.openedAt : 0;
118
+ this.openedAt = 0;
119
+ this.opts.onClose?.({ code: ev?.code ?? 0, reason: ev?.reason ?? "", ms });
120
+ if (!this.closed)
121
+ this.scheduleReconnect();
122
+ };
123
+ ws.onerror = () => {
124
+ try {
125
+ ws.close();
126
+ } catch {}
127
+ };
105
128
  }
106
129
  startHeartbeat() {
107
- (this.stopHeartbeat(),
108
- (this.heartbeat = setInterval(() => {
109
- try {
110
- this.ws?.send(JSON.stringify({ type: "ping" }));
111
- } catch {}
112
- }, O)));
130
+ this.stopHeartbeat();
131
+ this.heartbeat = setInterval(() => {
132
+ try {
133
+ this.ws?.send(JSON.stringify({ type: "ping" }));
134
+ } catch {}
135
+ }, HEARTBEAT_MS);
113
136
  }
114
137
  stopHeartbeat() {
115
- if (this.heartbeat != null) (clearInterval(this.heartbeat), (this.heartbeat = null));
138
+ if (this.heartbeat != null) {
139
+ clearInterval(this.heartbeat);
140
+ this.heartbeat = null;
141
+ }
116
142
  }
117
143
  clearStableTimer() {
118
- if (this.stableTimer != null) (clearTimeout(this.stableTimer), (this.stableTimer = null));
144
+ if (this.stableTimer != null) {
145
+ clearTimeout(this.stableTimer);
146
+ this.stableTimer = null;
147
+ }
119
148
  }
120
149
  scheduleReconnect() {
121
150
  if (this.hidden()) {
122
- this.dormant = !0;
151
+ this.dormant = true;
123
152
  return;
124
153
  }
125
- let z = Math.round(this.reconnectDelay * (0.75 + Math.random() * 0.5));
126
- ((this.reconnectDelay = Math.min(this.reconnectDelay * 2, b)),
127
- this.clearReconnectTimer(),
128
- (this.reconnectTimer = setTimeout(() => {
129
- if (((this.reconnectTimer = null), this.closed)) return;
130
- if (this.hidden()) {
131
- this.dormant = !0;
132
- return;
133
- }
134
- this.open();
135
- }, z)));
154
+ const delay = Math.round(this.reconnectDelay * (0.75 + Math.random() * 0.5));
155
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, RECONNECT_MAX_MS);
156
+ this.clearReconnectTimer();
157
+ this.reconnectTimer = setTimeout(() => {
158
+ this.reconnectTimer = null;
159
+ if (this.closed)
160
+ return;
161
+ if (this.hidden()) {
162
+ this.dormant = true;
163
+ return;
164
+ }
165
+ this.open();
166
+ }, delay);
136
167
  }
137
168
  clearReconnectTimer() {
138
- if (this.reconnectTimer != null)
139
- (clearTimeout(this.reconnectTimer), (this.reconnectTimer = null));
169
+ if (this.reconnectTimer != null) {
170
+ clearTimeout(this.reconnectTimer);
171
+ this.reconnectTimer = null;
172
+ }
140
173
  }
141
- sendSignal(z, Q) {
142
- let Y = { type: "signal", to: z, data: Q };
143
- this.ws?.send(JSON.stringify(Y));
174
+ sendSignal(to, data) {
175
+ const msg = { type: "signal", to, data };
176
+ this.ws?.send(JSON.stringify(msg));
144
177
  }
145
- updateMeta(z) {
146
- if (((this.opts.meta = z), this.ws?.readyState === 1)) {
147
- let Q = { type: "meta", meta: z };
148
- this.ws.send(JSON.stringify(Q));
178
+ updateMeta(meta) {
179
+ this.opts.meta = meta;
180
+ if (this.ws?.readyState === 1) {
181
+ const msg = { type: "meta", meta };
182
+ this.ws.send(JSON.stringify(msg));
149
183
  }
150
184
  }
151
185
  close() {
152
- ((this.closed = !0),
153
- (this.dormant = !1),
154
- this.detachWakeListeners(),
155
- this.clearReconnectTimer(),
156
- this.stopHeartbeat(),
157
- this.clearStableTimer());
186
+ this.closed = true;
187
+ this.dormant = false;
188
+ this.detachWakeListeners();
189
+ this.clearReconnectTimer();
190
+ this.stopHeartbeat();
191
+ this.clearStableTimer();
158
192
  try {
159
193
  this.ws?.close();
160
194
  } catch {}
161
195
  }
162
196
  }
163
- var A = ["stun:stun.l.google.com:19302", "stun:stun1.l.google.com:19302"],
164
- N = "codehost",
165
- _ = "codehost-bulk";
166
- class B {
197
+
198
+ // src/shared/rtc.ts
199
+ var ICE_SERVERS = [
200
+ "stun:stun.l.google.com:19302",
201
+ "stun:stun1.l.google.com:19302"
202
+ ];
203
+ var CHANNEL_LABEL = "codehost";
204
+ var BULK_CHANNEL_LABEL = "codehost-bulk";
205
+
206
+ // src/web/rtc-client.ts
207
+ class RtcClient {
167
208
  opts;
168
209
  pc;
169
210
  channel = null;
170
211
  bulk = null;
171
- constructor(z) {
172
- this.opts = z;
173
- ((this.pc = new RTCPeerConnection({ iceServers: A.map((Q) => ({ urls: Q })) })),
174
- (this.pc.onicecandidate = (Q) => {
175
- if (Q.candidate)
176
- this.opts.sendSignal({
177
- kind: "candidate",
178
- candidate: Q.candidate.candidate,
179
- mid: Q.candidate.sdpMid ?? "0",
180
- });
181
- }),
182
- (this.pc.onconnectionstatechange = () => {
183
- this.opts.onState?.(this.pc.connectionState);
184
- }));
212
+ constructor(opts) {
213
+ this.opts = opts;
214
+ this.pc = new RTCPeerConnection({
215
+ iceServers: ICE_SERVERS.map((urls) => ({ urls }))
216
+ });
217
+ this.pc.onicecandidate = (ev) => {
218
+ if (ev.candidate) {
219
+ this.opts.sendSignal({
220
+ kind: "candidate",
221
+ candidate: ev.candidate.candidate,
222
+ mid: ev.candidate.sdpMid ?? "0"
223
+ });
224
+ }
225
+ };
226
+ this.pc.onconnectionstatechange = () => {
227
+ this.opts.onState?.(this.pc.connectionState);
228
+ };
185
229
  }
186
230
  async start() {
187
- let z = this.pc.createDataChannel(N, { ordered: !0 });
188
- ((z.binaryType = "arraybuffer"),
189
- (this.channel = z),
190
- (z.onopen = () => this.opts.onOpen?.(z)),
191
- (z.onclose = () => this.opts.onClose?.()));
192
- let Q = this.pc.createDataChannel(_, { ordered: !0 });
193
- ((Q.binaryType = "arraybuffer"), (this.bulk = Q));
194
- let Y = await this.pc.createOffer();
195
- (await this.pc.setLocalDescription(Y),
196
- this.opts.sendSignal({ kind: "offer", type: "offer", sdp: Y.sdp ?? "" }));
197
- }
198
- async handleSignal(z) {
199
- let Q = z;
200
- if (!Q || typeof Q !== "object") return;
201
- if (Q.kind === "answer") await this.pc.setRemoteDescription({ type: "answer", sdp: Q.sdp });
202
- else if (Q.kind === "candidate")
231
+ const channel = this.pc.createDataChannel(CHANNEL_LABEL, { ordered: true });
232
+ channel.binaryType = "arraybuffer";
233
+ this.channel = channel;
234
+ channel.onopen = () => this.opts.onOpen?.(channel);
235
+ channel.onclose = () => this.opts.onClose?.();
236
+ const bulk = this.pc.createDataChannel(BULK_CHANNEL_LABEL, { ordered: true });
237
+ bulk.binaryType = "arraybuffer";
238
+ this.bulk = bulk;
239
+ const offer = await this.pc.createOffer();
240
+ await this.pc.setLocalDescription(offer);
241
+ this.opts.sendSignal({ kind: "offer", type: "offer", sdp: offer.sdp ?? "" });
242
+ }
243
+ async handleSignal(data) {
244
+ const sig = data;
245
+ if (!sig || typeof sig !== "object")
246
+ return;
247
+ if (sig.kind === "answer") {
248
+ await this.pc.setRemoteDescription({ type: "answer", sdp: sig.sdp });
249
+ } else if (sig.kind === "candidate") {
203
250
  try {
204
- await this.pc.addIceCandidate({ candidate: Q.candidate, sdpMid: Q.mid });
205
- } catch (Y) {
206
- console.error("[rtc] addIceCandidate failed:", Y);
251
+ await this.pc.addIceCandidate({ candidate: sig.candidate, sdpMid: sig.mid });
252
+ } catch (err) {
253
+ console.error("[rtc] addIceCandidate failed:", err);
207
254
  }
255
+ }
208
256
  }
209
257
  get dataChannel() {
210
258
  return this.channel;
@@ -214,35 +262,33 @@ class B {
214
262
  }
215
263
  async selectedPath() {
216
264
  try {
217
- let z = await this.pc.getStats(),
218
- Q = null;
219
- z.forEach((q) => {
220
- if (q.type === "transport" && q.selectedCandidatePairId) Q = q.selectedCandidatePairId;
265
+ const stats = await this.pc.getStats();
266
+ let pairId = null;
267
+ stats.forEach((s) => {
268
+ if (s.type === "transport" && s.selectedCandidatePairId)
269
+ pairId = s.selectedCandidatePairId;
221
270
  });
222
- let Y = null;
223
- if (
224
- (z.forEach((q) => {
225
- if (
226
- Q ? q.id === Q : q.type === "candidate-pair" && q.state === "succeeded" && q.nominated
227
- )
228
- Y = q;
229
- }),
230
- !Y)
231
- )
271
+ let pair = null;
272
+ stats.forEach((s) => {
273
+ if (pairId ? s.id === pairId : s.type === "candidate-pair" && s.state === "succeeded" && s.nominated) {
274
+ pair = s;
275
+ }
276
+ });
277
+ if (!pair)
232
278
  return null;
233
- let { localCandidateId: Z, remoteCandidateId: $ } = Y,
234
- G = !0,
235
- V = 0;
236
- if (
237
- (z.forEach((q) => {
238
- if (q.id === Z || q.id === $) {
239
- if ((V++, q.candidateType !== "host")) G = !1;
240
- }
241
- }),
242
- V < 2)
243
- )
279
+ const { localCandidateId, remoteCandidateId } = pair;
280
+ let lan = true;
281
+ let found = 0;
282
+ stats.forEach((s) => {
283
+ if (s.id === localCandidateId || s.id === remoteCandidateId) {
284
+ found++;
285
+ if (s.candidateType !== "host")
286
+ lan = false;
287
+ }
288
+ });
289
+ if (found < 2)
244
290
  return null;
245
- return G ? "lan" : "p2p";
291
+ return lan ? "lan" : "p2p";
246
292
  } catch {
247
293
  return null;
248
294
  }
@@ -259,280 +305,390 @@ class B {
259
305
  } catch {}
260
306
  }
261
307
  }
262
- var y = new TextEncoder(),
263
- S = new TextDecoder();
264
- function P(z, Q, Y) {
265
- let Z = Y?.byteLength ?? 0,
266
- $ = new Uint8Array(5 + Z);
267
- if ((($[0] = z), new DataView($.buffer).setUint32(1, Q >>> 0, !1), Y && Z)) $.set(Y, 5);
268
- return $;
269
- }
270
- function J(z, Q, Y) {
271
- return P(z, Q, y.encode(JSON.stringify(Y)));
308
+
309
+ // src/tunnel/protocol.ts
310
+ var FRAME_HEADER = 5;
311
+ var MAX_FRAME = 64 * 1024;
312
+ var MAX_CHUNK = MAX_FRAME - FRAME_HEADER;
313
+ var enc = new TextEncoder;
314
+ var dec = new TextDecoder;
315
+ function encodeFrame(op, streamId, payload) {
316
+ const len = payload?.byteLength ?? 0;
317
+ const buf = new Uint8Array(5 + len);
318
+ buf[0] = op;
319
+ new DataView(buf.buffer).setUint32(1, streamId >>> 0, false);
320
+ if (payload && len)
321
+ buf.set(payload, 5);
322
+ return buf;
272
323
  }
273
- function T(z) {
274
- let Q = z instanceof Uint8Array ? z : new Uint8Array(z),
275
- Y = Q[0],
276
- Z = new DataView(Q.buffer, Q.byteOffset, Q.byteLength).getUint32(1, !1),
277
- $ = Q.subarray(5);
278
- return { op: Y, streamId: Z, payload: $ };
324
+ function encodeJson(op, streamId, obj) {
325
+ return encodeFrame(op, streamId, enc.encode(JSON.stringify(obj)));
279
326
  }
280
- function U(z) {
281
- return JSON.parse(S.decode(z));
327
+ function decodeFrame(data) {
328
+ const u8 = data instanceof Uint8Array ? data : new Uint8Array(data);
329
+ const op = u8[0];
330
+ const streamId = new DataView(u8.buffer, u8.byteOffset, u8.byteLength).getUint32(1, false);
331
+ const payload = u8.subarray(5);
332
+ return { op, streamId, payload };
282
333
  }
283
- function E(z) {
284
- return S.decode(z);
334
+ function payloadJson(payload) {
335
+ return JSON.parse(dec.decode(payload));
285
336
  }
286
- function* C(z) {
287
- for (let Q = 0; Q < z.byteLength; Q += 65531) yield z.slice(Q, Math.min(Q + 65531, z.byteLength));
337
+ function payloadText(payload) {
338
+ return dec.decode(payload);
288
339
  }
289
- function f(z) {
290
- if (z.length === 1) return z[0];
291
- let Q = z.reduce(($, G) => $ + G.byteLength, 0),
292
- Y = new Uint8Array(Q),
293
- Z = 0;
294
- for (let $ of z) (Y.set($, Z), (Z += $.byteLength));
295
- return Y;
340
+ function* chunk(body) {
341
+ for (let off = 0;off < body.byteLength; off += MAX_CHUNK) {
342
+ yield body.slice(off, Math.min(off + MAX_CHUNK, body.byteLength));
343
+ }
296
344
  }
297
- function* H(z, Q, Y) {
298
- let Z = 0;
299
- while (Y.byteLength - Z > 65531) (yield P(13, Q, Y.subarray(Z, Z + 65531)), (Z += 65531));
300
- yield P(z, Q, Y.subarray(Z));
345
+ function concatBytes(parts) {
346
+ if (parts.length === 1)
347
+ return parts[0];
348
+ const total = parts.reduce((n, p) => n + p.byteLength, 0);
349
+ const out = new Uint8Array(total);
350
+ let off = 0;
351
+ for (const p of parts) {
352
+ out.set(p, off);
353
+ off += p.byteLength;
354
+ }
355
+ return out;
301
356
  }
302
- class F {
303
- pending = new Map();
304
- cont(z, Q) {
305
- let Y = this.pending.get(z);
306
- if (Y) Y.push(Q.slice());
307
- else this.pending.set(z, [Q.slice()]);
308
- }
309
- finish(z, Q) {
310
- let Y = this.pending.get(z);
311
- if (!Y) return Q;
312
- return (this.pending.delete(z), Y.push(Q), f(Y));
357
+ function* wsMessageFrames(terminal, streamId, payload) {
358
+ let off = 0;
359
+ while (payload.byteLength - off > MAX_CHUNK) {
360
+ yield encodeFrame(13 /* WsCont */, streamId, payload.subarray(off, off + MAX_CHUNK));
361
+ off += MAX_CHUNK;
313
362
  }
314
- drop(z) {
315
- this.pending.delete(z);
363
+ yield encodeFrame(terminal, streamId, payload.subarray(off));
364
+ }
365
+
366
+ class WsReassembler {
367
+ pending = new Map;
368
+ cont(streamId, payload) {
369
+ const buf = this.pending.get(streamId);
370
+ if (buf)
371
+ buf.push(payload.slice());
372
+ else
373
+ this.pending.set(streamId, [payload.slice()]);
374
+ }
375
+ finish(streamId, payload) {
376
+ const buf = this.pending.get(streamId);
377
+ if (!buf)
378
+ return payload;
379
+ this.pending.delete(streamId);
380
+ buf.push(payload);
381
+ return concatBytes(buf);
382
+ }
383
+ drop(streamId) {
384
+ this.pending.delete(streamId);
316
385
  }
317
386
  }
318
- class R {
319
- channel;
387
+
388
+ // src/tunnel/client.ts
389
+ class TunnelClient {
390
+ transport;
320
391
  bulk;
321
392
  nextStreamId = 1;
322
- https = new Map();
323
- wss = new Map();
324
- wsRx = new F();
325
- textEncoder = new TextEncoder();
326
- constructor(z, Q = null) {
327
- this.channel = z;
328
- this.bulk = Q;
329
- if (
330
- ((z.binaryType = "arraybuffer"),
331
- z.addEventListener("message", (Y) => this.onFrame(Y.data)),
332
- Q)
333
- )
334
- ((Q.binaryType = "arraybuffer"), Q.addEventListener("message", (Y) => this.onFrame(Y.data)));
393
+ https = new Map;
394
+ httpLane = new Map;
395
+ wss = new Map;
396
+ wsRx = new WsReassembler;
397
+ textEncoder = new TextEncoder;
398
+ constructor(transport, bulk = null) {
399
+ this.transport = transport;
400
+ this.bulk = bulk;
401
+ transport.onFrame((data) => this.onFrame(data));
402
+ bulk?.onFrame((data) => this.onFrame(data));
403
+ transport.onClose(() => this.failLane(transport, true));
404
+ bulk?.onClose(() => this.failLane(bulk, false));
405
+ }
406
+ failLane(lane, interactive) {
407
+ for (const [streamId, waiter] of [...this.https]) {
408
+ if ((this.httpLane.get(streamId) ?? this.transport) !== lane)
409
+ continue;
410
+ this.https.delete(streamId);
411
+ this.httpLane.delete(streamId);
412
+ waiter.onError("tunnel closed");
413
+ }
414
+ if (interactive) {
415
+ for (const [streamId, handlers] of [...this.wss]) {
416
+ this.wss.delete(streamId);
417
+ this.wsRx.drop(streamId);
418
+ handlers.onClose(1006, "tunnel closed");
419
+ }
420
+ }
335
421
  }
336
422
  allocId() {
337
- let z = this.nextStreamId;
338
- return ((this.nextStreamId = (this.nextStreamId + 1) >>> 0 || 1), z);
339
- }
340
- onFrame(z) {
341
- if (typeof z === "string") return;
342
- let { op: Q, streamId: Y, payload: Z } = T(z);
343
- switch (Q) {
344
- case 4:
345
- this.https.get(Y)?.onHead(U(Z));
423
+ const id = this.nextStreamId;
424
+ this.nextStreamId = this.nextStreamId + 1 >>> 0 || 1;
425
+ return id;
426
+ }
427
+ onFrame(data) {
428
+ const { op, streamId, payload } = decodeFrame(data);
429
+ switch (op) {
430
+ case 4 /* HttpResHead */:
431
+ this.https.get(streamId)?.onHead(payloadJson(payload));
346
432
  break;
347
- case 5:
348
- this.https.get(Y)?.onBody(Z.slice());
433
+ case 5 /* HttpResBody */:
434
+ this.https.get(streamId)?.onBody(payload.slice());
349
435
  break;
350
- case 6:
351
- (this.https.get(Y)?.onEnd(), this.https.delete(Y));
436
+ case 6 /* HttpResEnd */:
437
+ this.https.get(streamId)?.onEnd();
438
+ this.https.delete(streamId);
439
+ this.httpLane.delete(streamId);
352
440
  break;
353
- case 12: {
354
- let $ = this.https.get(Y);
355
- if ($) ($.onError(U(Z).message), this.https.delete(Y));
441
+ case 12 /* Error */: {
442
+ const waiter = this.https.get(streamId);
443
+ if (waiter) {
444
+ waiter.onError(payloadJson(payload).message);
445
+ this.https.delete(streamId);
446
+ this.httpLane.delete(streamId);
447
+ }
356
448
  break;
357
449
  }
358
- case 8: {
359
- let $ = U(Z);
360
- this.wss.get(Y)?.onOpenAck($.ok, $.protocol);
450
+ case 8 /* WsOpenAck */: {
451
+ const info = payloadJson(payload);
452
+ this.wss.get(streamId)?.onOpenAck(info.ok, info.protocol);
361
453
  break;
362
454
  }
363
- case 13:
364
- this.wsRx.cont(Y, Z);
455
+ case 13 /* WsCont */:
456
+ this.wsRx.cont(streamId, payload);
365
457
  break;
366
- case 9:
367
- this.wss.get(Y)?.onText(E(this.wsRx.finish(Y, Z)));
458
+ case 9 /* WsText */:
459
+ this.wss.get(streamId)?.onText(payloadText(this.wsRx.finish(streamId, payload)));
368
460
  break;
369
- case 10:
370
- this.wss.get(Y)?.onBin(this.wsRx.finish(Y, Z).slice());
461
+ case 10 /* WsBin */:
462
+ this.wss.get(streamId)?.onBin(this.wsRx.finish(streamId, payload).slice());
371
463
  break;
372
- case 11: {
373
- let $ = U(Z);
374
- (this.wsRx.drop(Y),
375
- this.wss.get(Y)?.onClose($.code ?? 1000, $.reason ?? ""),
376
- this.wss.delete(Y));
464
+ case 11 /* WsClose */: {
465
+ const info = payloadJson(payload);
466
+ this.wsRx.drop(streamId);
467
+ this.wss.get(streamId)?.onClose(info.code ?? 1000, info.reason ?? "");
468
+ this.wss.delete(streamId);
377
469
  break;
378
470
  }
379
471
  }
380
472
  }
381
- fetch(z, Q, Y, Z) {
382
- let $ = this.allocId();
383
- return new Promise((G, V) => {
384
- let q = null,
385
- D = null,
386
- X = new ReadableStream({
387
- start: (K) => {
388
- D = K;
389
- },
390
- }),
391
- v = typeof DecompressionStream < "u" ? { ...Y, "x-codehost-accept-gzip": "1" } : Y;
392
- this.https.set($, {
393
- onHead: (K) => {
394
- q = K;
395
- let x = new Headers(K.headers),
396
- M = X;
397
- if (x.get("content-encoding") === "gzip")
398
- ((M = X.pipeThrough(new DecompressionStream("gzip"))),
399
- x.delete("content-encoding"),
400
- x.delete("content-length"));
401
- G(
402
- new Response(M, {
403
- status: K.status === 204 || K.status === 304 ? K.status : K.status,
404
- statusText: K.statusText,
405
- headers: x,
406
- }),
407
- );
473
+ fetch(method, path, headers, body) {
474
+ const streamId = this.allocId();
475
+ return new Promise((resolve, reject) => {
476
+ let head = null;
477
+ let controller = null;
478
+ const stream = new ReadableStream({
479
+ start: (c) => {
480
+ controller = c;
481
+ }
482
+ });
483
+ const reqHeaders = typeof DecompressionStream !== "undefined" ? { ...headers, "x-codehost-accept-gzip": "1" } : headers;
484
+ this.https.set(streamId, {
485
+ onHead: (h) => {
486
+ head = h;
487
+ const resHeaders = new Headers(h.headers);
488
+ let bodyStream = stream;
489
+ if (resHeaders.get("content-encoding") === "gzip") {
490
+ bodyStream = stream.pipeThrough(new DecompressionStream("gzip"));
491
+ resHeaders.delete("content-encoding");
492
+ resHeaders.delete("content-length");
493
+ }
494
+ resolve(new Response(bodyStream, {
495
+ status: h.status === 204 || h.status === 304 ? h.status : h.status,
496
+ statusText: h.statusText,
497
+ headers: resHeaders
498
+ }));
408
499
  },
409
- onBody: (K) => {
500
+ onBody: (b) => {
410
501
  try {
411
- D?.enqueue(K);
502
+ controller?.enqueue(b);
412
503
  } catch {}
413
504
  },
414
505
  onEnd: () => {
415
506
  try {
416
- D?.close();
507
+ controller?.close();
417
508
  } catch {}
418
- if (!q) V(Error("stream ended before head"));
509
+ if (!head)
510
+ reject(new Error("stream ended before head"));
419
511
  },
420
- onError: (K) => {
512
+ onError: (msg) => {
421
513
  try {
422
- D?.error(Error(K));
514
+ controller?.error(new Error(msg));
423
515
  } catch {}
424
- if (!q) V(Error(K));
425
- },
516
+ if (!head)
517
+ reject(new Error(msg));
518
+ }
426
519
  });
427
- let W = this.bulk?.readyState === "open" ? this.bulk : this.channel;
428
- if ((this.sendOn(W, J(1, $, { method: z, path: Q, headers: v })), Z && Z.byteLength))
429
- for (let K of C(Z)) this.sendOn(W, P(2, $, K));
430
- this.sendOn(W, P(3, $));
520
+ const lane = this.bulk?.isOpen() ? this.bulk : this.transport;
521
+ this.httpLane.set(streamId, lane);
522
+ this.sendOn(lane, encodeJson(1 /* HttpReq */, streamId, { method, path, headers: reqHeaders }));
523
+ if (body && body.byteLength) {
524
+ for (const part of chunk(body))
525
+ this.sendOn(lane, encodeFrame(2 /* HttpReqBody */, streamId, part));
526
+ }
527
+ this.sendOn(lane, encodeFrame(3 /* HttpReqEnd */, streamId));
431
528
  });
432
529
  }
433
- openWs(z, Q, Y) {
434
- let Z = this.allocId();
435
- return (
436
- this.wss.set(Z, Y),
437
- this.send(J(7, Z, { path: z, protocols: Q })),
438
- {
439
- sendText: ($) => {
440
- for (let G of H(9, Z, this.textEncoder.encode($))) this.send(G);
441
- },
442
- sendBin: ($) => {
443
- for (let G of H(10, Z, $)) this.send(G);
444
- },
445
- close: ($, G) => {
446
- (this.send(J(11, Z, { code: $, reason: G })), this.wss.delete(Z));
447
- },
530
+ openWs(path, protocols, handlers) {
531
+ const streamId = this.allocId();
532
+ this.wss.set(streamId, handlers);
533
+ this.send(encodeJson(7 /* WsOpen */, streamId, { path, protocols }));
534
+ return {
535
+ sendText: (text) => {
536
+ for (const f of wsMessageFrames(9 /* WsText */, streamId, this.textEncoder.encode(text)))
537
+ this.send(f);
538
+ },
539
+ sendBin: (data) => {
540
+ for (const f of wsMessageFrames(10 /* WsBin */, streamId, data))
541
+ this.send(f);
542
+ },
543
+ close: (code, reason) => {
544
+ this.send(encodeJson(11 /* WsClose */, streamId, { code, reason }));
545
+ this.wss.delete(streamId);
448
546
  }
449
- );
547
+ };
450
548
  }
451
- send(z) {
452
- this.sendOn(this.channel, z);
549
+ send(frame) {
550
+ this.sendOn(this.transport, frame);
453
551
  }
454
- sendOn(z, Q) {
455
- if (z.readyState === "open") {
456
- let Y = new Uint8Array(Q.byteLength);
457
- (Y.set(Q), z.send(Y.buffer));
458
- }
552
+ sendOn(t, frame) {
553
+ if (t.isOpen())
554
+ t.send(frame);
459
555
  }
460
556
  get ready() {
461
- return this.channel.readyState === "open";
557
+ return this.transport.isOpen();
462
558
  }
463
559
  }
464
- var I = "wss://signal.codehost.dev",
465
- h = 1e4;
466
- class w {
560
+
561
+ // src/tunnel/rtc-datachannel.ts
562
+ function rtcDataChannelTransport(channel) {
563
+ channel.binaryType = "arraybuffer";
564
+ return {
565
+ send(frame) {
566
+ const copy = new Uint8Array(frame.byteLength);
567
+ copy.set(frame);
568
+ channel.send(copy.buffer);
569
+ },
570
+ isOpen: () => channel.readyState === "open",
571
+ bufferedAmount: () => channel.bufferedAmount,
572
+ setBufferedAmountLow(bytes, cb) {
573
+ channel.bufferedAmountLowThreshold = bytes;
574
+ channel.addEventListener("bufferedamountlow", cb);
575
+ },
576
+ onFrame(cb) {
577
+ channel.addEventListener("message", (ev) => {
578
+ if (typeof ev.data === "string")
579
+ return;
580
+ cb(new Uint8Array(ev.data));
581
+ });
582
+ },
583
+ onClose(cb) {
584
+ channel.addEventListener("close", cb);
585
+ }
586
+ };
587
+ }
588
+
589
+ // src/web/tunnel-client.ts
590
+ class TunnelClient2 extends TunnelClient {
591
+ constructor(channel, bulk = null) {
592
+ super(rtcDataChannelTransport(channel), bulk ? rtcDataChannelTransport(bulk) : null);
593
+ }
594
+ }
595
+
596
+ // src/web/room-client.ts
597
+ var DEFAULT_SIGNAL_URL = "wss://signal.codehost.dev";
598
+ var DIAL_FAIL_COOLDOWN_MS = 1e4;
599
+
600
+ class CodehostRoom {
467
601
  peers = [];
468
602
  signaling;
469
- rtcs = new Map();
470
- tunnels = new Map();
471
- dialFailedAt = new Map();
472
- closed = !1;
473
- constructor(z) {
474
- ((this.signaling = new j({
475
- url: z.signalUrl ?? I,
476
- token: z.token,
477
- role: "viewer",
478
- onOpen: () => z.onStatus?.(!0),
479
- onClose: () => z.onStatus?.(!1),
480
- onPeers: (Q) => {
481
- ((this.peers = Q.filter((Y) => Y.role === "server")), z.onPeers?.(this.peers));
603
+ rtcs = new Map;
604
+ tunnels = new Map;
605
+ dialFailedAt = new Map;
606
+ closed = false;
607
+ constructor(opts) {
608
+ this.signaling = new SignalingClient({
609
+ url: opts.signalUrl ?? DEFAULT_SIGNAL_URL,
610
+ token: opts.token,
611
+ role: CLIENT_WIRE_ROLE,
612
+ onOpen: () => opts.onStatus?.(true),
613
+ onClose: () => opts.onStatus?.(false),
614
+ onPeers: (peers) => {
615
+ this.peers = peers.filter((p) => p.role === "server");
616
+ opts.onPeers?.(this.peers);
482
617
  },
483
- onSignal: (Q, Y) => void this.rtcs.get(Q)?.handleSignal(Y),
484
- })),
485
- this.signaling.connect());
486
- }
487
- async fetch(z, Q, Y, Z = {}) {
488
- let $ = await this.dial(z),
489
- G = typeof Z.body === "string" ? new TextEncoder().encode(Z.body) : Z.body;
490
- return $.fetch(Q, Y, Z.headers ?? {}, G);
491
- }
492
- dial(z) {
493
- let Q = this.tunnels.get(z);
494
- if (Q) return Q;
495
- let Y = this.dialFailedAt.get(z);
496
- if (Y != null && Date.now() - Y < h)
497
- return Promise.reject(Error("dial failed recently; cooling down"));
498
- let Z = () => {
499
- (this.tunnels.delete(z), this.rtcs.get(z)?.close(), this.rtcs.delete(z));
500
- },
501
- $ = new Promise((G, V) => {
502
- let q = setTimeout(() => {
503
- (Z(), V(Error("dial timed out")));
504
- }, 15000),
505
- D = new B({
506
- sendSignal: (X) => this.signaling.sendSignal(z, X),
507
- onOpen: (X) => {
508
- (clearTimeout(q), this.dialFailedAt.delete(z), G(new R(X, D.bulkChannel)));
509
- },
510
- onClose: Z,
511
- onState: (X) => {
512
- if (X === "failed" || X === "disconnected") Z();
513
- },
514
- });
515
- (this.rtcs.set(z, D),
516
- D.start().catch((X) => {
517
- (clearTimeout(q), Z(), V(X));
518
- }));
618
+ onSignal: (from, data) => void this.rtcs.get(from)?.handleSignal(data)
619
+ });
620
+ this.signaling.connect();
621
+ }
622
+ async fetch(peerId, method, path, init = {}) {
623
+ const tunnel = await this.dial(peerId);
624
+ const body = typeof init.body === "string" ? new TextEncoder().encode(init.body) : init.body;
625
+ return tunnel.fetch(method, path, init.headers ?? {}, body);
626
+ }
627
+ async openWs(peerId, path, protocols, handlers) {
628
+ const tunnel = await this.dial(peerId);
629
+ return tunnel.openWs(path, protocols, handlers);
630
+ }
631
+ dial(peerId) {
632
+ const existing = this.tunnels.get(peerId);
633
+ if (existing)
634
+ return existing;
635
+ const failedAt = this.dialFailedAt.get(peerId);
636
+ if (failedAt != null && Date.now() - failedAt < DIAL_FAIL_COOLDOWN_MS) {
637
+ return Promise.reject(new Error("dial failed recently; cooling down"));
638
+ }
639
+ const drop = () => {
640
+ this.tunnels.delete(peerId);
641
+ this.rtcs.get(peerId)?.close();
642
+ this.rtcs.delete(peerId);
643
+ };
644
+ const dialing = new Promise((resolve, reject) => {
645
+ const timer = setTimeout(() => {
646
+ drop();
647
+ reject(new Error("dial timed out"));
648
+ }, 15000);
649
+ const rtc = new RtcClient({
650
+ sendSignal: (data) => this.signaling.sendSignal(peerId, data),
651
+ onOpen: (channel) => {
652
+ clearTimeout(timer);
653
+ this.dialFailedAt.delete(peerId);
654
+ resolve(new TunnelClient2(channel, rtc.bulkChannel));
655
+ },
656
+ onClose: drop,
657
+ onState: (state) => {
658
+ if (state === "failed" || state === "disconnected")
659
+ drop();
660
+ }
519
661
  });
520
- return (
521
- this.tunnels.set(z, $),
522
- $.catch(() => {
523
- (this.dialFailedAt.set(z, Date.now()), this.tunnels.delete(z));
524
- }),
525
- $
526
- );
662
+ this.rtcs.set(peerId, rtc);
663
+ rtc.start().catch((err) => {
664
+ clearTimeout(timer);
665
+ drop();
666
+ reject(err);
667
+ });
668
+ });
669
+ this.tunnels.set(peerId, dialing);
670
+ dialing.catch(() => {
671
+ this.dialFailedAt.set(peerId, Date.now());
672
+ this.tunnels.delete(peerId);
673
+ });
674
+ return dialing;
527
675
  }
528
676
  close() {
529
- if (this.closed) return;
530
- this.closed = !0;
531
- for (let z of this.rtcs.values()) z.close();
532
- (this.rtcs.clear(), this.tunnels.clear(), this.signaling.close());
677
+ if (this.closed)
678
+ return;
679
+ this.closed = true;
680
+ for (const rtc of this.rtcs.values())
681
+ rtc.close();
682
+ this.rtcs.clear();
683
+ this.tunnels.clear();
684
+ this.signaling.close();
533
685
  }
534
686
  }
535
- function zz(z) {
536
- return new w(z);
687
+ function joinRoom(opts) {
688
+ return new CodehostRoom(opts);
537
689
  }
538
- export { zz as joinRoom, I as DEFAULT_SIGNAL_URL, w as CodehostRoom };
690
+ export {
691
+ joinRoom,
692
+ DEFAULT_SIGNAL_URL,
693
+ CodehostRoom
694
+ };