@fitzzero/quickdraw-core 5.1.2 → 5.1.3

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 (124) hide show
  1. package/README.md +793 -57
  2. package/dist/{admin-DWWrT1h6.d.ts → admin-CS7JB_UH.d.ts} +2 -2
  3. package/dist/{adminSchemas-D4bFKsrb.d.ts → adminSchemas-29cGgrg5.d.ts} +1 -1
  4. package/dist/{auth-DXiSJZaf.d.ts → auth-DtdYjrZ9.d.ts} +11 -7
  5. package/dist/{caller-BT-Mv4bD.d.ts → caller-BTwGl7vm.d.ts} +183 -15
  6. package/dist/chunk-2FDSKLBO.js +82 -0
  7. package/dist/chunk-2FDSKLBO.js.map +1 -0
  8. package/dist/{chunk-GSIPUNJL.js → chunk-64I6MYBC.js} +139 -792
  9. package/dist/chunk-64I6MYBC.js.map +1 -0
  10. package/dist/chunk-7473HK24.js +17 -0
  11. package/dist/chunk-7473HK24.js.map +1 -0
  12. package/dist/chunk-7Y5ROPXL.js +33 -0
  13. package/dist/chunk-7Y5ROPXL.js.map +1 -0
  14. package/dist/chunk-A2RBQD66.js +7 -0
  15. package/dist/chunk-A2RBQD66.js.map +1 -0
  16. package/dist/chunk-ASAMUGHG.js +651 -0
  17. package/dist/chunk-ASAMUGHG.js.map +1 -0
  18. package/dist/{chunk-B2P7AZ25.js → chunk-CYE4UBOB.js} +6 -4
  19. package/dist/chunk-CYE4UBOB.js.map +1 -0
  20. package/dist/chunk-DNP65TD5.js +832 -0
  21. package/dist/chunk-DNP65TD5.js.map +1 -0
  22. package/dist/{chunk-5CGO426C.js → chunk-GYLWEJHW.js} +5 -1
  23. package/dist/chunk-GYLWEJHW.js.map +1 -0
  24. package/dist/{chunk-AONAFOYU.js → chunk-I3JCFS7H.js} +25 -13
  25. package/dist/chunk-I3JCFS7H.js.map +1 -0
  26. package/dist/{chunk-X5CWPX7H.js → chunk-IIQ3WMJK.js} +13 -2
  27. package/dist/chunk-IIQ3WMJK.js.map +1 -0
  28. package/dist/{chunk-6K5BY47X.js → chunk-JVTONQFI.js} +1277 -277
  29. package/dist/chunk-JVTONQFI.js.map +1 -0
  30. package/dist/{chunk-RL4VKLBX.js → chunk-KKQANRLG.js} +2 -2
  31. package/dist/{chunk-RL4VKLBX.js.map → chunk-KKQANRLG.js.map} +1 -1
  32. package/dist/{chunk-GKH3LJ7N.js → chunk-MPQAT55F.js} +38 -205
  33. package/dist/chunk-MPQAT55F.js.map +1 -0
  34. package/dist/chunk-NX2V6SIL.js +202 -0
  35. package/dist/chunk-NX2V6SIL.js.map +1 -0
  36. package/dist/{chunk-7QCUX4TK.js → chunk-NZHA4TZD.js} +72 -175
  37. package/dist/chunk-NZHA4TZD.js.map +1 -0
  38. package/dist/{chunk-MBH6IDSM.js → chunk-OCZJ4EYV.js} +40 -17
  39. package/dist/chunk-OCZJ4EYV.js.map +1 -0
  40. package/dist/{chunk-RJ5I2VO3.js → chunk-PHCURHC7.js} +6 -6
  41. package/dist/chunk-PHCURHC7.js.map +1 -0
  42. package/dist/{chunk-4YO5H5ON.js → chunk-Q4OGQM5I.js} +4 -10
  43. package/dist/chunk-Q4OGQM5I.js.map +1 -0
  44. package/dist/chunk-QSU47XEX.js +13 -0
  45. package/dist/chunk-QSU47XEX.js.map +1 -0
  46. package/dist/{chunk-LCC7HNFP.js → chunk-RZXJTDH5.js} +2 -2
  47. package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
  48. package/dist/chunk-SN3OZVEQ.js.map +1 -0
  49. package/dist/{chunk-Z5ILULF2.js → chunk-SQ5VUG77.js} +4 -3
  50. package/dist/{chunk-Z5ILULF2.js.map → chunk-SQ5VUG77.js.map} +1 -1
  51. package/dist/chunk-STH5DLQP.js +1192 -0
  52. package/dist/chunk-STH5DLQP.js.map +1 -0
  53. package/dist/{chunk-NMXYENH4.js → chunk-VZFJTKIW.js} +6 -6
  54. package/dist/{chunk-753VJIUP.js → chunk-X4ZU4QH5.js} +43 -9
  55. package/dist/chunk-X4ZU4QH5.js.map +1 -0
  56. package/dist/chunk-XD7SRDUY.js +188 -0
  57. package/dist/chunk-XD7SRDUY.js.map +1 -0
  58. package/dist/chunk-Z7I6SONO.js +825 -0
  59. package/dist/chunk-Z7I6SONO.js.map +1 -0
  60. package/dist/cli/quickdraw-docs.js +131 -113
  61. package/dist/cli/quickdraw-docs.js.map +1 -1
  62. package/dist/client/core.d.ts +387 -0
  63. package/dist/client/core.js +109 -0
  64. package/dist/client/core.js.map +1 -0
  65. package/dist/client/index.d.ts +30 -193
  66. package/dist/client/index.js +56 -217
  67. package/dist/client/index.js.map +1 -1
  68. package/dist/{clientTypes-hWcPUVS9.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
  69. package/dist/{connection-CgijdGpT.d.ts → connection-2EeQ_Et0.d.ts} +16 -9
  70. package/dist/{createServer-Cp-Wk500.d.ts → createServer-Bx5HOcYK.d.ts} +37 -9
  71. package/dist/{createTestApp-BB_PkqQ7.d.ts → createTestApp-DAMANT79.d.ts} +36 -9
  72. package/dist/{envelope-DzWyIXjj.d.ts → envelope-BMBaxppV.d.ts} +200 -8
  73. package/dist/{http-DotQ5bj8.d.ts → http-YzLAar3w.d.ts} +15 -50
  74. package/dist/index.d.ts +7 -6
  75. package/dist/index.js +61 -28
  76. package/dist/index.js.map +1 -1
  77. package/dist/{infer-BmoZsL9a.d.ts → infer-Br62cnyW.d.ts} +39 -1
  78. package/dist/{metrics-DW80Exve.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
  79. package/dist/parser.js +2 -1
  80. package/dist/prisma/index.d.ts +13 -6
  81. package/dist/prisma/index.js +155 -9
  82. package/dist/prisma/index.js.map +1 -1
  83. package/dist/remote-N7OUAGOW.js +15 -0
  84. package/dist/remote-N7OUAGOW.js.map +1 -0
  85. package/dist/server/auth/index.d.ts +3 -3
  86. package/dist/server/auth/index.js +5 -3
  87. package/dist/server/auth/index.js.map +1 -1
  88. package/dist/server/index.d.ts +140 -36
  89. package/dist/server/index.js +162 -55
  90. package/dist/server/index.js.map +1 -1
  91. package/dist/server/mcp/index.d.ts +34 -18
  92. package/dist/server/mcp/index.js +29 -12
  93. package/dist/server/mcp/index.js.map +1 -1
  94. package/dist/server/otel.d.ts +3 -3
  95. package/dist/{sharing-UDLn1049.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
  96. package/dist/testing/client.d.ts +13 -13
  97. package/dist/testing/client.js +17 -10
  98. package/dist/testing/client.js.map +1 -1
  99. package/dist/testing/index.d.ts +49 -12
  100. package/dist/testing/index.js +102 -19
  101. package/dist/testing/index.js.map +1 -1
  102. package/dist/testing/mock.d.ts +5 -5
  103. package/dist/testing/mock.js +15 -9
  104. package/dist/tracked-BR5b2O-U.d.ts +43 -0
  105. package/dist/{types-CYliTP65.d.ts → types-CLRCQGnO.d.ts} +4 -3
  106. package/dist/types-tMagHbjy.d.ts +151 -0
  107. package/dist/utils/index.d.ts +16 -4
  108. package/dist/utils/index.js +6 -3
  109. package/package.json +5 -1
  110. package/dist/chunk-4YO5H5ON.js.map +0 -1
  111. package/dist/chunk-5CGO426C.js.map +0 -1
  112. package/dist/chunk-6K5BY47X.js.map +0 -1
  113. package/dist/chunk-753VJIUP.js.map +0 -1
  114. package/dist/chunk-7QCUX4TK.js.map +0 -1
  115. package/dist/chunk-AONAFOYU.js.map +0 -1
  116. package/dist/chunk-B2P7AZ25.js.map +0 -1
  117. package/dist/chunk-GKH3LJ7N.js.map +0 -1
  118. package/dist/chunk-GSIPUNJL.js.map +0 -1
  119. package/dist/chunk-MBH6IDSM.js.map +0 -1
  120. package/dist/chunk-RJ5I2VO3.js.map +0 -1
  121. package/dist/chunk-X5CWPX7H.js.map +0 -1
  122. package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
  123. /package/dist/{chunk-LCC7HNFP.js.map → chunk-RZXJTDH5.js.map} +0 -0
  124. /package/dist/{chunk-NMXYENH4.js.map → chunk-VZFJTKIW.js.map} +0 -0
@@ -0,0 +1,832 @@
1
+ import {
2
+ createLinkCore,
3
+ throwLater
4
+ } from "./chunk-ASAMUGHG.js";
5
+ import {
6
+ isPeerLabel,
7
+ isRoomName,
8
+ isSocketId,
9
+ readSignal
10
+ } from "./chunk-2FDSKLBO.js";
11
+ import {
12
+ CLIENT_EVENTS,
13
+ MAX_PEER_LINKS,
14
+ MAX_PEER_MESSAGE_BYTES,
15
+ PEER_RELAY_BURST,
16
+ PEER_RELAY_BYTES_BURST,
17
+ PEER_RELAY_BYTES_PER_SECOND,
18
+ PEER_RELAY_FAST_BACKLOG,
19
+ PEER_RELAY_RATE,
20
+ SERVER_EVENTS
21
+ } from "./chunk-X4ZU4QH5.js";
22
+ import {
23
+ utf8ByteLength
24
+ } from "./chunk-7Y5ROPXL.js";
25
+ import {
26
+ isRecord
27
+ } from "./chunk-QSU47XEX.js";
28
+
29
+ // src/client/peer/rtc.ts
30
+ var DISCONNECTED_GRACE_MS = 5e3;
31
+ var FAST_BACKLOG_BYTES = 32768;
32
+ var MAX_EARLY_CANDIDATES = 64;
33
+ function candidateOf(candidate) {
34
+ const { sdpMid, sdpMLineIndex, usernameFragment } = candidate;
35
+ return {
36
+ candidate: candidate.candidate,
37
+ ...sdpMid === void 0 ? {} : { sdpMid },
38
+ ...sdpMLineIndex === void 0 ? {} : { sdpMLineIndex },
39
+ ...usernameFragment === void 0 ? {} : { usernameFragment }
40
+ };
41
+ }
42
+ function shut(state) {
43
+ state.closed = true;
44
+ state.open = false;
45
+ clearTimeout(state.grace);
46
+ state.rtc.onicecandidate = null;
47
+ state.rtc.oniceconnectionstatechange = null;
48
+ for (const channel of Object.values(state.channels)) {
49
+ channel.onopen = null;
50
+ channel.onclose = null;
51
+ channel.onmessage = null;
52
+ channel.close();
53
+ }
54
+ state.rtc.close();
55
+ }
56
+ function fail(state, closed2 = false) {
57
+ if (!state.closed) {
58
+ shut(state);
59
+ state.events.failed(closed2);
60
+ }
61
+ }
62
+ function step(state, work) {
63
+ work().catch(() => {
64
+ fail(state);
65
+ });
66
+ }
67
+ function setRemote(state, description) {
68
+ return state.rtc.setRemoteDescription(description).then(() => {
69
+ state.remoteSet = true;
70
+ for (const ice of state.early.splice(0)) {
71
+ state.rtc.addIceCandidate(ice).catch(() => void 0);
72
+ }
73
+ });
74
+ }
75
+ async function describe(state, kind) {
76
+ const description = await (kind === "offer" ? state.rtc.createOffer() : state.rtc.createAnswer());
77
+ if (state.closed) {
78
+ return;
79
+ }
80
+ await state.rtc.setLocalDescription(description);
81
+ if (!state.closed) {
82
+ state.events.signal({ sdp: { type: kind, sdp: description.sdp ?? "" } });
83
+ }
84
+ }
85
+ function watch(state) {
86
+ const { rtc, channels, events } = state;
87
+ for (const label of ["rel", "fast"]) {
88
+ const channel = channels[label];
89
+ channel.binaryType = "arraybuffer";
90
+ channel.onopen = () => {
91
+ if (!state.open && channels.rel.readyState === "open" && channels.fast.readyState === "open") {
92
+ state.open = true;
93
+ events.opened();
94
+ }
95
+ };
96
+ channel.onclose = () => {
97
+ fail(state, true);
98
+ };
99
+ channel.onmessage = (event) => {
100
+ events.message(event.data, label);
101
+ };
102
+ }
103
+ rtc.onicecandidate = (event) => {
104
+ if (event.candidate !== null) {
105
+ events.signal({ ice: candidateOf(event.candidate) });
106
+ }
107
+ };
108
+ rtc.oniceconnectionstatechange = () => {
109
+ const ice = rtc.iceConnectionState;
110
+ if (ice === "failed" || ice === "closed") {
111
+ fail(state);
112
+ } else if (ice === "disconnected") {
113
+ state.grace ??= setTimeout(() => {
114
+ fail(state);
115
+ }, DISCONNECTED_GRACE_MS);
116
+ } else if (ice === "connected" || ice === "completed") {
117
+ clearTimeout(state.grace);
118
+ state.grace = void 0;
119
+ }
120
+ };
121
+ }
122
+ function createDirectPath(rtc, events) {
123
+ const state = {
124
+ rtc,
125
+ channels: {
126
+ rel: rtc.createDataChannel("rel", { negotiated: true, id: 0, ordered: true }),
127
+ fast: rtc.createDataChannel("fast", {
128
+ negotiated: true,
129
+ id: 1,
130
+ ordered: false,
131
+ maxRetransmits: 0
132
+ })
133
+ },
134
+ events,
135
+ early: [],
136
+ open: false,
137
+ closed: false,
138
+ remoteSet: false,
139
+ grace: void 0
140
+ };
141
+ watch(state);
142
+ return {
143
+ get open() {
144
+ return state.open;
145
+ },
146
+ offer: () => {
147
+ step(state, () => describe(state, "offer"));
148
+ },
149
+ answer: (offer) => {
150
+ step(state, async () => {
151
+ await setRemote(state, offer);
152
+ await describe(state, "answer");
153
+ });
154
+ },
155
+ accept: (answer) => {
156
+ step(state, () => setRemote(state, answer));
157
+ },
158
+ candidate(ice) {
159
+ if (state.closed) {
160
+ return;
161
+ }
162
+ if (!state.remoteSet) {
163
+ if (state.early.length < MAX_EARLY_CANDIDATES) {
164
+ state.early.push(ice);
165
+ }
166
+ return;
167
+ }
168
+ rtc.addIceCandidate(ice).catch(() => void 0);
169
+ },
170
+ send(label, text) {
171
+ const channel = state.channels[label];
172
+ if (!state.open || channel.readyState !== "open") {
173
+ return false;
174
+ }
175
+ if (label === "fast" && channel.bufferedAmount > FAST_BACKLOG_BYTES) {
176
+ return false;
177
+ }
178
+ try {
179
+ channel.send(text);
180
+ return true;
181
+ } catch {
182
+ return false;
183
+ }
184
+ },
185
+ close: () => {
186
+ if (!state.closed) {
187
+ shut(state);
188
+ }
189
+ }
190
+ };
191
+ }
192
+
193
+ // src/client/peer/hub.ts
194
+ var PENDING_MS = 15e3;
195
+ var MAX_PENDING = 64;
196
+ var MAX_PENDING_SIGNALS = 64;
197
+ var MAX_PENDING_CHARS = 65536;
198
+ var MAX_PENDING_TOTAL_CHARS = 1048576;
199
+ var SIGNAL_TIMEOUT_MS = 1e4;
200
+ var RATE_SHARE = 0.9;
201
+ var BURST_SHARE = 0.5;
202
+ var DEFAULT_PEER_LIMITS = Object.freeze({
203
+ maxLinks: MAX_PEER_LINKS,
204
+ maxMessageBytes: MAX_PEER_MESSAGE_BYTES,
205
+ relayRate: PEER_RELAY_RATE,
206
+ relayBurst: PEER_RELAY_BURST,
207
+ relayBytesPerSecond: PEER_RELAY_BYTES_PER_SECOND,
208
+ relayBytesBurst: PEER_RELAY_BYTES_BURST
209
+ });
210
+ function charsOf(sig) {
211
+ if (sig.sdp !== void 0) {
212
+ return sig.sdp.sdp.length;
213
+ }
214
+ const { ice } = sig;
215
+ return ice === void 0 ? 1 : ice.candidate.length + (ice.sdpMid?.length ?? 0) + (ice.usernameFragment?.length ?? 0);
216
+ }
217
+ var keyOf = (room, peer) => `${room}\0${peer}`;
218
+ var now = () => performance.now();
219
+ function take(bucket, rate, burst, cost = 1) {
220
+ const at = now();
221
+ bucket.tokens = Math.min(burst, bucket.tokens + (at - bucket.refilledAt) / 1e3 * rate);
222
+ bucket.refilledAt = at;
223
+ if (bucket.tokens < cost) {
224
+ return false;
225
+ }
226
+ bucket.tokens -= cost;
227
+ return true;
228
+ }
229
+ function servedLimits(connection) {
230
+ const { hello } = connection.getState();
231
+ return hello?.features.includes("peer") === true ? hello.limits.peers ?? DEFAULT_PEER_LIMITS : void 0;
232
+ }
233
+ function readForward(frame) {
234
+ if (!isRecord(frame) || !isRoomName(frame.room) || !isSocketId(frame.from)) {
235
+ return void 0;
236
+ }
237
+ const sig = readSignal(frame.sig);
238
+ const { userId } = frame;
239
+ return sig === void 0 || typeof userId !== "string" || userId === "" ? void 0 : { room: frame.room, from: frame.from, userId, sig };
240
+ }
241
+ function dropPending(state, key) {
242
+ const entry = state.pending.get(key);
243
+ if (entry !== void 0) {
244
+ state.pending.delete(key);
245
+ state.pendingChars -= entry.chars;
246
+ }
247
+ return entry;
248
+ }
249
+ function keep(state, entry, sig) {
250
+ const chars = charsOf(sig);
251
+ if (entry.signals.length >= MAX_PENDING_SIGNALS || entry.chars + chars > MAX_PENDING_CHARS || state.pendingChars + chars > MAX_PENDING_TOTAL_CHARS) {
252
+ return false;
253
+ }
254
+ entry.signals.push(sig);
255
+ entry.chars += chars;
256
+ state.pendingChars += chars;
257
+ return true;
258
+ }
259
+ function receiveSignal(state, frame) {
260
+ const forward = readForward(frame);
261
+ if (forward === void 0) {
262
+ return;
263
+ }
264
+ const key = keyOf(forward.room, forward.from);
265
+ const link = state.links.get(key);
266
+ if (link?.signal(forward.sig, forward.userId) === true) {
267
+ return;
268
+ }
269
+ if (forward.sig.bye === true) {
270
+ dropPending(state, key);
271
+ return;
272
+ }
273
+ const at = now();
274
+ for (const [old, entry2] of state.pending) {
275
+ if (at - entry2.at > PENDING_MS) {
276
+ dropPending(state, old);
277
+ }
278
+ }
279
+ const entry = state.pending.get(key);
280
+ if (entry !== void 0) {
281
+ keep(state, entry, forward.sig);
282
+ return;
283
+ }
284
+ const { room, from, userId } = forward;
285
+ const waiting = { room, from, userId, signals: [], at, chars: 0 };
286
+ if (state.pending.size >= MAX_PENDING || !keep(state, waiting, forward.sig)) {
287
+ return;
288
+ }
289
+ state.pending.set(key, waiting);
290
+ for (const listener of [...state.listeners]) {
291
+ try {
292
+ listener({ room, from, userId });
293
+ } catch (error) {
294
+ throwLater(error);
295
+ }
296
+ }
297
+ }
298
+ function receiveRelay(state, frame) {
299
+ if (!Array.isArray(frame)) {
300
+ return;
301
+ }
302
+ const [room, from, label, text] = frame;
303
+ if (isRoomName(room) && isSocketId(from) && isPeerLabel(label) && typeof text === "string") {
304
+ state.links.get(keyOf(room, from))?.relay(label, text);
305
+ }
306
+ }
307
+ function receivePresence(state, frame) {
308
+ if (!isRecord(frame) || !isRoomName(frame.room)) {
309
+ return;
310
+ }
311
+ const { users } = frame;
312
+ if (!Array.isArray(users) || users.length > 0) {
313
+ return;
314
+ }
315
+ const prefix = keyOf(frame.room, "");
316
+ for (const [key, link] of [...state.links]) {
317
+ if (key.startsWith(prefix)) {
318
+ link.left();
319
+ }
320
+ }
321
+ }
322
+ function listen(state) {
323
+ const { socket } = state.connection;
324
+ socket.on(SERVER_EVENTS.peer, (frame) => {
325
+ receiveSignal(state, frame);
326
+ });
327
+ socket.on(SERVER_EVENTS.relay, (frame) => {
328
+ receiveRelay(state, frame);
329
+ });
330
+ socket.on(SERVER_EVENTS.presence, (frame) => {
331
+ receivePresence(state, frame);
332
+ });
333
+ socket.on("disconnect", () => {
334
+ state.pending.clear();
335
+ state.pendingChars = 0;
336
+ for (const link of [...state.links.values()]) {
337
+ link.disconnected();
338
+ }
339
+ });
340
+ }
341
+ function signalOf(state, room, to, sig) {
342
+ const { socket } = state.connection;
343
+ if (!socket.connected) {
344
+ return Promise.resolve({ ok: false, e: { code: "INTERNAL", message: "Not connected" } });
345
+ }
346
+ return socket.timeout(SIGNAL_TIMEOUT_MS).emitWithAck(CLIENT_EVENTS.peer, { room, to, sig }).catch(
347
+ () => ({ ok: false, e: { code: "TIMEOUT", message: "No answer to a peer signal" } })
348
+ );
349
+ }
350
+ function backedUp(socket) {
351
+ const engine = socket.io?.engine;
352
+ if ((engine?.writeBuffer?.length ?? 0) >= PEER_RELAY_FAST_BACKLOG) {
353
+ return true;
354
+ }
355
+ const buffered = engine?.transport?.ws?.bufferedAmount;
356
+ return typeof buffered === "number" && buffered > FAST_BACKLOG_BYTES;
357
+ }
358
+ function relayOf(state, frame, pair) {
359
+ const { socket } = state.connection;
360
+ const limits = servedLimits(state.connection);
361
+ if (!socket.connected || limits === void 0) {
362
+ return false;
363
+ }
364
+ if (frame[2] === "fast" && backedUp(socket)) {
365
+ return false;
366
+ }
367
+ if (!take(pair, limits.relayRate * RATE_SHARE, limits.relayBurst * BURST_SHARE)) {
368
+ return false;
369
+ }
370
+ const bytes = utf8ByteLength(frame[3]);
371
+ const budget = limits.relayBytesPerSecond * RATE_SHARE;
372
+ if (!take(state.bytes, budget, limits.relayBytesBurst * BURST_SHARE, bytes)) {
373
+ pair.tokens += 1;
374
+ return false;
375
+ }
376
+ socket.emit(CLIENT_EVENTS.relay, frame);
377
+ return true;
378
+ }
379
+ function whenReady(connection, ready, gone) {
380
+ let stop;
381
+ const check = () => {
382
+ const { status, hello } = connection.getState();
383
+ if (status === "connected" && hello !== null) {
384
+ stop?.();
385
+ ready(servedLimits(connection));
386
+ return true;
387
+ }
388
+ if (status === "idle" || status === "disconnected" || status === "refused") {
389
+ stop?.();
390
+ gone();
391
+ return true;
392
+ }
393
+ return false;
394
+ };
395
+ queueMicrotask(() => {
396
+ if (!check()) {
397
+ stop = connection.subscribe(() => {
398
+ check();
399
+ });
400
+ }
401
+ });
402
+ }
403
+ function createHub(connection) {
404
+ const state = {
405
+ connection,
406
+ links: /* @__PURE__ */ new Map(),
407
+ pending: /* @__PURE__ */ new Map(),
408
+ pendingChars: 0,
409
+ listeners: /* @__PURE__ */ new Set(),
410
+ bytes: { tokens: 0, refilledAt: now() }
411
+ };
412
+ state.bytes.tokens = DEFAULT_PEER_LIMITS.relayBytesBurst * BURST_SHARE;
413
+ listen(state);
414
+ const hub = {
415
+ connection,
416
+ add(room, peer, handle) {
417
+ const key = keyOf(room, peer);
418
+ if (state.links.has(key)) {
419
+ throw new TypeError(
420
+ `createPeerLink: this connection has a link to ${peer} in room "${room}" already; close it first`
421
+ );
422
+ }
423
+ state.links.set(key, handle);
424
+ return () => {
425
+ if (state.links.get(key) === handle) {
426
+ state.links.delete(key);
427
+ }
428
+ };
429
+ },
430
+ take(room, peer) {
431
+ const entry = dropPending(state, keyOf(room, peer));
432
+ const fresh = entry !== void 0 && now() - entry.at <= PENDING_MS;
433
+ return {
434
+ room,
435
+ from: peer,
436
+ userId: entry?.userId ?? "",
437
+ signals: fresh ? entry.signals : []
438
+ };
439
+ },
440
+ forget(room, peer) {
441
+ dropPending(state, keyOf(room, peer));
442
+ },
443
+ signal: (room, to, sig) => signalOf(state, room, to, sig),
444
+ relay: (room, to, label, text, pair) => relayOf(state, [room, to, label, text], pair),
445
+ pairBucket: () => ({
446
+ tokens: (servedLimits(connection) ?? DEFAULT_PEER_LIMITS).relayBurst * BURST_SHARE,
447
+ refilledAt: now()
448
+ }),
449
+ requests(listener) {
450
+ state.listeners.add(listener);
451
+ return () => {
452
+ state.listeners.delete(listener);
453
+ };
454
+ },
455
+ whenReady: (ready, gone) => {
456
+ whenReady(connection, ready, gone);
457
+ },
458
+ limits: () => servedLimits(connection) ?? DEFAULT_PEER_LIMITS
459
+ };
460
+ return Object.freeze(hub);
461
+ }
462
+ var hubs = /* @__PURE__ */ new WeakMap();
463
+ function hubOf(connection) {
464
+ let hub = hubs.get(connection);
465
+ if (hub === void 0) {
466
+ hub = createHub(connection);
467
+ hubs.set(connection, hub);
468
+ }
469
+ return hub;
470
+ }
471
+
472
+ // src/client/peer/link.ts
473
+ var DEFAULT_ICE_SERVERS = Object.freeze([
474
+ { urls: "stun:stun.l.google.com:19302" },
475
+ { urls: "stun:stun.cloudflare.com:3478" }
476
+ ]);
477
+ var OPEN_TIMEOUT_MS = 15e3;
478
+ var DIRECT_TIMEOUT_MS = 5e3;
479
+ var BYE_WAIT_MS = 2e3;
480
+ function platformFactory() {
481
+ const Rtc = globalThis.RTCPeerConnection;
482
+ return Rtc === void 0 ? void 0 : (configuration) => new Rtc(configuration);
483
+ }
484
+ function canDirect(run) {
485
+ return run.relay !== "always" && run.factory !== void 0;
486
+ }
487
+ function retryDelay(data) {
488
+ const wait = data?.retryAfterMs;
489
+ return typeof wait === "number" && wait > 0 ? Math.min(wait, OPEN_TIMEOUT_MS) : 100;
490
+ }
491
+ function signal(run, sig, attempt = 0) {
492
+ void run.hub.signal(run.room, run.peer, sig).then((answer) => {
493
+ if (answer.ok || run.core.link.state === "closed" || sig.ice !== void 0) {
494
+ return;
495
+ }
496
+ const { code, data } = answer.e;
497
+ if (code === "RATE_LIMITED" && attempt < 3) {
498
+ const retry = setTimeout(() => {
499
+ run.retries.delete(retry);
500
+ signal(run, sig, attempt + 1);
501
+ }, retryDelay(data));
502
+ run.retries.add(retry);
503
+ } else if (code !== "TIMEOUT") {
504
+ run.core.fail("refused");
505
+ }
506
+ });
507
+ }
508
+ function closeDirect(run, after) {
509
+ clearTimeout(run.directTimer);
510
+ const { direct } = run;
511
+ run.direct = void 0;
512
+ if (after === void 0) {
513
+ direct?.close();
514
+ } else {
515
+ const close = () => {
516
+ direct?.close();
517
+ };
518
+ void after.then(close, close);
519
+ }
520
+ }
521
+ function moveToRelay(run) {
522
+ clearTimeout(run.byeTimer);
523
+ if (run.relay === "never") {
524
+ run.core.fail("failed");
525
+ return;
526
+ }
527
+ closeDirect(run);
528
+ if (!run.relaySent) {
529
+ run.relaySent = true;
530
+ signal(run, { relay: true });
531
+ }
532
+ if (run.relayHeard || run.core.link.state === "open") {
533
+ run.core.carry("relay");
534
+ }
535
+ }
536
+ function directFailed(run) {
537
+ if (run.relay === "never") {
538
+ run.core.fail(run.core.link.state === "open" ? "lost" : "failed");
539
+ } else {
540
+ moveToRelay(run);
541
+ }
542
+ }
543
+ function awaitBye(run) {
544
+ clearTimeout(run.byeTimer);
545
+ run.byeTimer = setTimeout(() => {
546
+ run.byeTimer = void 0;
547
+ if (run.direct === void 0) {
548
+ directFailed(run);
549
+ }
550
+ }, BYE_WAIT_MS);
551
+ }
552
+ function discard(rtc) {
553
+ try {
554
+ rtc?.close();
555
+ } catch {
556
+ }
557
+ }
558
+ function openDirect(run, factory) {
559
+ let rtc;
560
+ let direct;
561
+ try {
562
+ rtc = factory({ iceServers: run.iceServers });
563
+ direct = createDirectPath(rtc, {
564
+ signal: (sig) => {
565
+ if (run.direct === direct) {
566
+ signal(run, sig);
567
+ }
568
+ },
569
+ opened: () => {
570
+ clearTimeout(run.directTimer);
571
+ run.core.carry("direct");
572
+ },
573
+ failed: (channelClosed) => {
574
+ if (run.direct !== direct) {
575
+ return;
576
+ }
577
+ run.direct = void 0;
578
+ if (channelClosed) {
579
+ clearTimeout(run.directTimer);
580
+ awaitBye(run);
581
+ } else {
582
+ directFailed(run);
583
+ }
584
+ },
585
+ message: (data, label) => {
586
+ run.core.receive(data, label);
587
+ }
588
+ });
589
+ } catch {
590
+ discard(rtc);
591
+ return void 0;
592
+ }
593
+ run.direct = direct;
594
+ for (const ice of run.early.splice(0)) {
595
+ direct.candidate(ice);
596
+ }
597
+ if (run.relay !== "never") {
598
+ run.directTimer = setTimeout(() => {
599
+ if (run.direct === direct && !direct.open) {
600
+ directFailed(run);
601
+ }
602
+ }, DIRECT_TIMEOUT_MS);
603
+ }
604
+ return direct;
605
+ }
606
+ function yields(run) {
607
+ const self = run.hub.connection.socket.id;
608
+ return !run.relaySent && self !== void 0 && self > run.peer;
609
+ }
610
+ function described(run, sdp) {
611
+ if (sdp.type === "answer") {
612
+ if (run.offerer) {
613
+ run.direct?.accept(sdp);
614
+ }
615
+ return;
616
+ }
617
+ if (run.offerer) {
618
+ if (!yields(run)) {
619
+ return;
620
+ }
621
+ closeDirect(run);
622
+ run.offerer = false;
623
+ run.heardOffer = true;
624
+ }
625
+ if (run.relaySent || run.direct !== void 0) {
626
+ return;
627
+ }
628
+ const direct = canDirect(run) && run.factory !== void 0 ? openDirect(run, run.factory) : void 0;
629
+ if (direct === void 0) {
630
+ moveToRelay(run);
631
+ } else {
632
+ direct.answer(sdp);
633
+ }
634
+ }
635
+ var MAX_EARLY_CANDIDATES2 = 64;
636
+ function restarts(run, sig) {
637
+ if (run.offerer) {
638
+ return false;
639
+ }
640
+ if (sig.relay === true) {
641
+ return run.relayHeard;
642
+ }
643
+ if (sig.sdp?.type !== "offer") {
644
+ return false;
645
+ }
646
+ const again = run.heardOffer || run.relayHeard;
647
+ run.heardOffer = true;
648
+ return again;
649
+ }
650
+ function onSignal(run, sig, userId) {
651
+ if (!run.started) {
652
+ run.queued.push([sig, userId]);
653
+ return true;
654
+ }
655
+ if (restarts(run, sig)) {
656
+ run.core.end("bye");
657
+ return false;
658
+ }
659
+ if (userId !== "") {
660
+ run.core.identify(userId);
661
+ }
662
+ if (sig.bye === true) {
663
+ run.core.end("bye");
664
+ } else if (sig.relay === true) {
665
+ run.relayHeard = true;
666
+ moveToRelay(run);
667
+ } else if (sig.sdp !== void 0) {
668
+ described(run, sig.sdp);
669
+ } else if (sig.ice !== void 0) {
670
+ if (run.direct !== void 0) {
671
+ run.direct.candidate(sig.ice);
672
+ } else if (!run.relaySent && run.early.length < MAX_EARLY_CANDIDATES2) {
673
+ run.early.push(sig.ice);
674
+ }
675
+ }
676
+ return true;
677
+ }
678
+ function start(run, limits) {
679
+ if (run.core.link.state === "closed") {
680
+ return;
681
+ }
682
+ if (limits === void 0 || !canDirect(run) && run.relay === "never") {
683
+ run.core.fail("unsupported");
684
+ return;
685
+ }
686
+ run.started = true;
687
+ run.pair = run.hub.pairBucket();
688
+ run.openTimer = setTimeout(() => {
689
+ if (run.core.link.state === "connecting") {
690
+ run.core.fail("timeout");
691
+ }
692
+ }, OPEN_TIMEOUT_MS);
693
+ const waiting = run.hub.take(run.room, run.peer);
694
+ if (run.offerer) {
695
+ const direct = canDirect(run) && run.factory !== void 0 ? openDirect(run, run.factory) : void 0;
696
+ if (direct === void 0) {
697
+ moveToRelay(run);
698
+ } else {
699
+ direct.offer();
700
+ }
701
+ }
702
+ for (const [sig, userId] of [
703
+ ...waiting.signals.map((waited) => [waited, waiting.userId]),
704
+ ...run.queued.splice(0)
705
+ ]) {
706
+ if (!onSignal(run, sig, userId)) {
707
+ return;
708
+ }
709
+ }
710
+ }
711
+ function sendOn(run, label, text) {
712
+ if (run.direct?.open === true) {
713
+ return run.direct.send(label, text);
714
+ }
715
+ return run.relaySent && run.hub.relay(run.room, run.peer, label, text, run.pair);
716
+ }
717
+ function closed(run, fromHere) {
718
+ clearTimeout(run.openTimer);
719
+ clearTimeout(run.byeTimer);
720
+ for (const retry of run.retries) {
721
+ clearTimeout(retry);
722
+ }
723
+ run.retries.clear();
724
+ run.remove();
725
+ const bye = fromHere && run.started ? run.hub.signal(run.room, run.peer, { bye: true }) : void 0;
726
+ closeDirect(run, bye);
727
+ }
728
+ var RELAY_MODES = ["auto", "always", "never"];
729
+ function checkOptions(options) {
730
+ if (!isRoomName(options.room)) {
731
+ throw new TypeError("createPeerLink: room must name an app room, of 1 to 256 characters");
732
+ }
733
+ if (!isSocketId(options.to)) {
734
+ throw new TypeError("createPeerLink: to must be the peer's socket id");
735
+ }
736
+ if (typeof options.offerer !== "boolean") {
737
+ throw new TypeError(
738
+ "createPeerLink: offerer must be true on the side that offers, false on the other"
739
+ );
740
+ }
741
+ if (options.relay !== void 0 && !RELAY_MODES.includes(options.relay)) {
742
+ throw new TypeError('createPeerLink: relay must be "auto", "always" or "never"');
743
+ }
744
+ if (options.peerConnection !== void 0 && typeof options.peerConnection !== "function") {
745
+ throw new TypeError("createPeerLink: peerConnection must make an RTCPeerConnection");
746
+ }
747
+ }
748
+ function createPeerLink(connection, options) {
749
+ checkOptions(options);
750
+ const hub = hubOf(connection);
751
+ const run = {
752
+ hub,
753
+ room: options.room,
754
+ peer: options.to,
755
+ offerer: options.offerer,
756
+ relay: options.relay ?? "auto",
757
+ factory: options.peerConnection ?? platformFactory(),
758
+ iceServers: (options.iceServers ?? DEFAULT_ICE_SERVERS).map((server) => ({ ...server })),
759
+ core: createLinkCore({
760
+ room: options.room,
761
+ peer: options.to,
762
+ maxMessageBytes: () => hub.limits().maxMessageBytes,
763
+ hooks: {
764
+ send: (label, text) => sendOn(run, label, text),
765
+ closed: (_reason, fromHere) => {
766
+ closed(run, fromHere);
767
+ }
768
+ }
769
+ }),
770
+ pair: hub.pairBucket(),
771
+ started: false,
772
+ queued: [],
773
+ early: [],
774
+ heardOffer: false,
775
+ direct: void 0,
776
+ relaySent: false,
777
+ relayHeard: false,
778
+ openTimer: void 0,
779
+ directTimer: void 0,
780
+ byeTimer: void 0,
781
+ retries: /* @__PURE__ */ new Set(),
782
+ remove: () => void 0
783
+ };
784
+ run.remove = hub.add(run.room, run.peer, {
785
+ signal: (sig, userId) => onSignal(run, sig, userId),
786
+ relay: (label, text) => {
787
+ run.core.receive(text, label);
788
+ },
789
+ disconnected: () => {
790
+ run.core.end("disconnected");
791
+ },
792
+ left: () => {
793
+ run.core.end("left");
794
+ }
795
+ });
796
+ hub.whenReady(
797
+ (limits) => {
798
+ start(run, limits);
799
+ },
800
+ () => {
801
+ run.core.end("disconnected");
802
+ }
803
+ );
804
+ return run.core.link;
805
+ }
806
+ function onPeerRequest(connection, listener) {
807
+ const hub = hubOf(connection);
808
+ return hub.requests((incoming) => {
809
+ listener(
810
+ Object.freeze({
811
+ ...incoming,
812
+ accept: (options = {}) => createPeerLink(connection, {
813
+ ...options,
814
+ room: incoming.room,
815
+ to: incoming.from,
816
+ offerer: false
817
+ }),
818
+ reject: () => {
819
+ hub.forget(incoming.room, incoming.from);
820
+ void hub.signal(incoming.room, incoming.from, { bye: true });
821
+ }
822
+ })
823
+ );
824
+ });
825
+ }
826
+
827
+ export {
828
+ DEFAULT_ICE_SERVERS,
829
+ createPeerLink,
830
+ onPeerRequest
831
+ };
832
+ //# sourceMappingURL=chunk-DNP65TD5.js.map