rollback-netcode 0.0.4
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.
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/dist/debug.d.ts +29 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +56 -0
- package/dist/debug.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/encoding.d.ts +80 -0
- package/dist/protocol/encoding.d.ts.map +1 -0
- package/dist/protocol/encoding.js +992 -0
- package/dist/protocol/encoding.js.map +1 -0
- package/dist/protocol/messages.d.ts +271 -0
- package/dist/protocol/messages.d.ts.map +1 -0
- package/dist/protocol/messages.js +114 -0
- package/dist/protocol/messages.js.map +1 -0
- package/dist/rollback/engine.d.ts +261 -0
- package/dist/rollback/engine.d.ts.map +1 -0
- package/dist/rollback/engine.js +543 -0
- package/dist/rollback/engine.js.map +1 -0
- package/dist/rollback/input-buffer.d.ts +225 -0
- package/dist/rollback/input-buffer.d.ts.map +1 -0
- package/dist/rollback/input-buffer.js +483 -0
- package/dist/rollback/input-buffer.js.map +1 -0
- package/dist/rollback/snapshot-buffer.d.ts +119 -0
- package/dist/rollback/snapshot-buffer.d.ts.map +1 -0
- package/dist/rollback/snapshot-buffer.js +256 -0
- package/dist/rollback/snapshot-buffer.js.map +1 -0
- package/dist/session/desync-manager.d.ts +106 -0
- package/dist/session/desync-manager.d.ts.map +1 -0
- package/dist/session/desync-manager.js +136 -0
- package/dist/session/desync-manager.js.map +1 -0
- package/dist/session/lag-monitor.d.ts +69 -0
- package/dist/session/lag-monitor.d.ts.map +1 -0
- package/dist/session/lag-monitor.js +74 -0
- package/dist/session/lag-monitor.js.map +1 -0
- package/dist/session/message-builders.d.ts +86 -0
- package/dist/session/message-builders.d.ts.map +1 -0
- package/dist/session/message-builders.js +199 -0
- package/dist/session/message-builders.js.map +1 -0
- package/dist/session/message-router.d.ts +61 -0
- package/dist/session/message-router.d.ts.map +1 -0
- package/dist/session/message-router.js +105 -0
- package/dist/session/message-router.js.map +1 -0
- package/dist/session/player-manager.d.ts +100 -0
- package/dist/session/player-manager.d.ts.map +1 -0
- package/dist/session/player-manager.js +160 -0
- package/dist/session/player-manager.js.map +1 -0
- package/dist/session/session.d.ts +379 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +1294 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/topology.d.ts +66 -0
- package/dist/session/topology.d.ts.map +1 -0
- package/dist/session/topology.js +72 -0
- package/dist/session/topology.js.map +1 -0
- package/dist/transport/adapter.d.ts +99 -0
- package/dist/transport/adapter.d.ts.map +1 -0
- package/dist/transport/adapter.js +8 -0
- package/dist/transport/adapter.js.map +1 -0
- package/dist/transport/local.d.ts +192 -0
- package/dist/transport/local.d.ts.map +1 -0
- package/dist/transport/local.js +435 -0
- package/dist/transport/local.js.map +1 -0
- package/dist/transport/transforming.d.ts +177 -0
- package/dist/transport/transforming.d.ts.map +1 -0
- package/dist/transport/transforming.js +407 -0
- package/dist/transport/transforming.js.map +1 -0
- package/dist/transport/webrtc.d.ts +285 -0
- package/dist/transport/webrtc.d.ts.map +1 -0
- package/dist/transport/webrtc.js +734 -0
- package/dist/transport/webrtc.js.map +1 -0
- package/dist/types.d.ts +394 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +256 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +59 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +93 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransformingTransport - Wraps a transport with compression and segmentation.
|
|
3
|
+
*
|
|
4
|
+
* Provides transparent compression (using pako/gzip) and segmentation for large
|
|
5
|
+
* messages that exceed WebRTC DataChannel limits (~16KB).
|
|
6
|
+
*/
|
|
7
|
+
import * as pako from "pako";
|
|
8
|
+
/**
|
|
9
|
+
* Default configuration for TransformingTransport.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_TRANSFORMING_TRANSPORT_CONFIG = {
|
|
12
|
+
compression: "auto",
|
|
13
|
+
compressionThreshold: 128,
|
|
14
|
+
segmentation: true,
|
|
15
|
+
maxSegmentSize: 16000,
|
|
16
|
+
reassemblyTimeout: 5000,
|
|
17
|
+
};
|
|
18
|
+
/** Header byte indicating uncompressed data */
|
|
19
|
+
const COMPRESSION_HEADER_RAW = 0x00;
|
|
20
|
+
/** Header byte indicating gzip-compressed data */
|
|
21
|
+
const COMPRESSION_HEADER_GZIP = 0x01;
|
|
22
|
+
/** Size of the compression header (1 byte) */
|
|
23
|
+
const COMPRESSION_HEADER_SIZE = 1;
|
|
24
|
+
/** Size of the segment header (msgId:u32 + idx:u16 + total:u16 = 8 bytes) */
|
|
25
|
+
const SEGMENT_HEADER_SIZE = 8;
|
|
26
|
+
/** Maximum segments allowed per message (prevents DoS with absurd total values) */
|
|
27
|
+
const MAX_SEGMENTS_PER_MESSAGE = 4096; // ~64MB at 16KB segments
|
|
28
|
+
/**
|
|
29
|
+
* Wraps a transport with transparent compression and segmentation.
|
|
30
|
+
*
|
|
31
|
+
* Send path: compress -> segment -> send via inner transport
|
|
32
|
+
* Receive path: reassemble -> decompress -> deliver to callback
|
|
33
|
+
*/
|
|
34
|
+
export class TransformingTransport {
|
|
35
|
+
onMessage = null;
|
|
36
|
+
onConnect = null;
|
|
37
|
+
onDisconnect = null;
|
|
38
|
+
onError = null;
|
|
39
|
+
inner;
|
|
40
|
+
config;
|
|
41
|
+
/** Per-peer message ID counters for outgoing messages */
|
|
42
|
+
messageIdCounters = new Map();
|
|
43
|
+
/** Per-peer reassembly buffers for incoming messages */
|
|
44
|
+
reassemblyBuffers = new Map();
|
|
45
|
+
/** Timer ID for periodic cleanup */
|
|
46
|
+
cleanupTimerId = null;
|
|
47
|
+
/**
|
|
48
|
+
* Create a new TransformingTransport.
|
|
49
|
+
*
|
|
50
|
+
* @param inner - The underlying transport to wrap
|
|
51
|
+
* @param config - Optional configuration (uses defaults for missing values)
|
|
52
|
+
*/
|
|
53
|
+
constructor(inner, config) {
|
|
54
|
+
this.inner = inner;
|
|
55
|
+
this.config = {
|
|
56
|
+
...DEFAULT_TRANSFORMING_TRANSPORT_CONFIG,
|
|
57
|
+
...config,
|
|
58
|
+
};
|
|
59
|
+
// Wire up inner transport callbacks
|
|
60
|
+
this.inner.onMessage = (peerId, message) => {
|
|
61
|
+
this.handleIncomingMessage(peerId, message);
|
|
62
|
+
};
|
|
63
|
+
this.inner.onConnect = (peerId) => {
|
|
64
|
+
this.onConnect?.(peerId);
|
|
65
|
+
};
|
|
66
|
+
this.inner.onDisconnect = (peerId) => {
|
|
67
|
+
this.cleanupPeerState(peerId);
|
|
68
|
+
this.onDisconnect?.(peerId);
|
|
69
|
+
};
|
|
70
|
+
// Forward errors from inner transport
|
|
71
|
+
if (this.inner.onError !== undefined) {
|
|
72
|
+
this.inner.onError = (peerId, error, context) => {
|
|
73
|
+
this.onError?.(peerId, error, context);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// Start periodic cleanup for timed-out reassembly buffers
|
|
77
|
+
this.startCleanupTimer();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the set of connected peer IDs.
|
|
81
|
+
*/
|
|
82
|
+
get connectedPeers() {
|
|
83
|
+
return this.inner.connectedPeers;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the local peer's ID.
|
|
87
|
+
*/
|
|
88
|
+
get localPeerId() {
|
|
89
|
+
return this.inner.localPeerId;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Connect to a peer.
|
|
93
|
+
*/
|
|
94
|
+
async connect(peerId) {
|
|
95
|
+
return this.inner.connect(peerId);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Disconnect from a peer.
|
|
99
|
+
*/
|
|
100
|
+
disconnect(peerId) {
|
|
101
|
+
this.cleanupPeerState(peerId);
|
|
102
|
+
this.inner.disconnect(peerId);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Disconnect from all peers.
|
|
106
|
+
*/
|
|
107
|
+
disconnectAll() {
|
|
108
|
+
// Clean up all state
|
|
109
|
+
this.messageIdCounters.clear();
|
|
110
|
+
this.reassemblyBuffers.clear();
|
|
111
|
+
this.inner.disconnectAll();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Send a message to a specific peer.
|
|
115
|
+
* Applies compression and segmentation as configured.
|
|
116
|
+
*/
|
|
117
|
+
send(peerId, message, reliable) {
|
|
118
|
+
const transformed = this.transformOutgoing(message);
|
|
119
|
+
const segments = this.segmentMessage(peerId, transformed);
|
|
120
|
+
for (const segment of segments) {
|
|
121
|
+
this.inner.send(peerId, segment, reliable);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Broadcast a message to all connected peers.
|
|
126
|
+
* Each peer receives independently segmented messages with unique message IDs.
|
|
127
|
+
*/
|
|
128
|
+
broadcast(message, reliable) {
|
|
129
|
+
const transformed = this.transformOutgoing(message);
|
|
130
|
+
for (const peerId of this.connectedPeers) {
|
|
131
|
+
const segments = this.segmentMessage(peerId, transformed);
|
|
132
|
+
for (const segment of segments) {
|
|
133
|
+
this.inner.send(peerId, segment, reliable);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get connection quality metrics for a peer.
|
|
139
|
+
*/
|
|
140
|
+
getConnectionMetrics(peerId) {
|
|
141
|
+
return this.inner.getConnectionMetrics?.(peerId) ?? null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Stop the cleanup timer and dispose of the inner transport.
|
|
145
|
+
* Call this when disposing of the transport.
|
|
146
|
+
*/
|
|
147
|
+
dispose() {
|
|
148
|
+
this.stopCleanupTimer();
|
|
149
|
+
this.messageIdCounters.clear();
|
|
150
|
+
this.reassemblyBuffers.clear();
|
|
151
|
+
// Dispose of the wrapped transport to prevent memory leaks
|
|
152
|
+
this.inner.dispose?.();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Apply compression to outgoing message data.
|
|
156
|
+
* Returns data with a 1-byte compression header.
|
|
157
|
+
*/
|
|
158
|
+
transformOutgoing(message) {
|
|
159
|
+
if (this.config.compression === "never") {
|
|
160
|
+
return this.addCompressionHeader(message, false);
|
|
161
|
+
}
|
|
162
|
+
// Check if message is above threshold
|
|
163
|
+
if (this.config.compression === "auto" &&
|
|
164
|
+
message.length < this.config.compressionThreshold) {
|
|
165
|
+
return this.addCompressionHeader(message, false);
|
|
166
|
+
}
|
|
167
|
+
// Attempt compression
|
|
168
|
+
const compressed = pako.deflate(message);
|
|
169
|
+
// In auto mode, only use compression if it actually reduces size
|
|
170
|
+
if (this.config.compression === "auto" &&
|
|
171
|
+
compressed.length >= message.length) {
|
|
172
|
+
return this.addCompressionHeader(message, false);
|
|
173
|
+
}
|
|
174
|
+
return this.addCompressionHeader(compressed, true);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Add compression header to message data.
|
|
178
|
+
*/
|
|
179
|
+
addCompressionHeader(data, isCompressed) {
|
|
180
|
+
const result = new Uint8Array(COMPRESSION_HEADER_SIZE + data.length);
|
|
181
|
+
result[0] = isCompressed ? COMPRESSION_HEADER_GZIP : COMPRESSION_HEADER_RAW;
|
|
182
|
+
result.set(data, COMPRESSION_HEADER_SIZE);
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Segment a transformed message for sending.
|
|
187
|
+
* Returns an array of segments, each with an 8-byte header.
|
|
188
|
+
*/
|
|
189
|
+
segmentMessage(peerId, data) {
|
|
190
|
+
// Calculate max payload size per segment
|
|
191
|
+
const maxPayloadSize = this.config.maxSegmentSize - SEGMENT_HEADER_SIZE;
|
|
192
|
+
if (!this.config.segmentation || data.length <= maxPayloadSize) {
|
|
193
|
+
// Single segment
|
|
194
|
+
return [this.createSegment(peerId, data, 0, 1)];
|
|
195
|
+
}
|
|
196
|
+
// Multiple segments
|
|
197
|
+
const segments = [];
|
|
198
|
+
const totalSegments = Math.ceil(data.length / maxPayloadSize);
|
|
199
|
+
const messageId = this.getNextMessageId(peerId);
|
|
200
|
+
for (let i = 0; i < totalSegments; i++) {
|
|
201
|
+
const start = i * maxPayloadSize;
|
|
202
|
+
const end = Math.min(start + maxPayloadSize, data.length);
|
|
203
|
+
const payload = data.slice(start, end);
|
|
204
|
+
segments.push(this.createSegmentWithId(messageId, payload, i, totalSegments));
|
|
205
|
+
}
|
|
206
|
+
return segments;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Create a segment with auto-generated message ID.
|
|
210
|
+
*/
|
|
211
|
+
createSegment(peerId, payload, index, total) {
|
|
212
|
+
const messageId = this.getNextMessageId(peerId);
|
|
213
|
+
return this.createSegmentWithId(messageId, payload, index, total);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Create a segment with a specific message ID.
|
|
217
|
+
*/
|
|
218
|
+
createSegmentWithId(messageId, payload, index, total) {
|
|
219
|
+
const segment = new Uint8Array(SEGMENT_HEADER_SIZE + payload.length);
|
|
220
|
+
const view = new DataView(segment.buffer);
|
|
221
|
+
// Write header: msgId:u32, idx:u16, total:u16
|
|
222
|
+
view.setUint32(0, messageId, true);
|
|
223
|
+
view.setUint16(4, index, true);
|
|
224
|
+
view.setUint16(6, total, true);
|
|
225
|
+
// Write payload
|
|
226
|
+
segment.set(payload, SEGMENT_HEADER_SIZE);
|
|
227
|
+
return segment;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get the next message ID for a peer.
|
|
231
|
+
*
|
|
232
|
+
* Note: Wraps at 2^32 (~4 billion). Collisions after wrap-around are handled by:
|
|
233
|
+
* 1. The total-mismatch check in bufferSegment() (rejects segments with wrong total)
|
|
234
|
+
* 2. The reassembly timeout cleanup (removes stale entries after 5s)
|
|
235
|
+
* 3. Practical impossibility: at 60 msg/sec, wrap-around takes ~2.2 years
|
|
236
|
+
*/
|
|
237
|
+
getNextMessageId(peerId) {
|
|
238
|
+
const current = this.messageIdCounters.get(peerId) ?? 0;
|
|
239
|
+
const next = (current + 1) >>> 0; // Keep as unsigned 32-bit
|
|
240
|
+
this.messageIdCounters.set(peerId, next);
|
|
241
|
+
return current;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Handle an incoming message from the inner transport.
|
|
245
|
+
*/
|
|
246
|
+
handleIncomingMessage(peerId, data) {
|
|
247
|
+
if (data.length < SEGMENT_HEADER_SIZE) {
|
|
248
|
+
// Invalid segment, ignore
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// Parse segment header
|
|
252
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
253
|
+
const messageId = view.getUint32(0, true);
|
|
254
|
+
const index = view.getUint16(4, true);
|
|
255
|
+
const total = view.getUint16(6, true);
|
|
256
|
+
// Extract payload
|
|
257
|
+
const payload = data.slice(SEGMENT_HEADER_SIZE);
|
|
258
|
+
if (total === 0) {
|
|
259
|
+
// Invalid total, ignore
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (total === 1) {
|
|
263
|
+
// Fast path: single segment message
|
|
264
|
+
this.deliverMessage(peerId, payload);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
// Multi-segment message: buffer and reassemble
|
|
268
|
+
this.bufferSegment(peerId, messageId, index, total, payload);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Buffer a segment and attempt reassembly.
|
|
272
|
+
*/
|
|
273
|
+
bufferSegment(peerId, messageId, index, total, payload) {
|
|
274
|
+
// Validate segment header values
|
|
275
|
+
if (total > MAX_SEGMENTS_PER_MESSAGE || index >= total) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
// Get or create peer state
|
|
279
|
+
let peerState = this.reassemblyBuffers.get(peerId);
|
|
280
|
+
if (!peerState) {
|
|
281
|
+
peerState = { messages: new Map() };
|
|
282
|
+
this.reassemblyBuffers.set(peerId, peerState);
|
|
283
|
+
}
|
|
284
|
+
// Get or create message state
|
|
285
|
+
let messageState = peerState.messages.get(messageId);
|
|
286
|
+
if (!messageState) {
|
|
287
|
+
messageState = {
|
|
288
|
+
total,
|
|
289
|
+
segments: new Map(),
|
|
290
|
+
startTime: Date.now(),
|
|
291
|
+
};
|
|
292
|
+
peerState.messages.set(messageId, messageState);
|
|
293
|
+
}
|
|
294
|
+
// Validate total matches
|
|
295
|
+
if (messageState.total !== total) {
|
|
296
|
+
// Mismatched total, discard segment
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
// Ignore duplicate segments
|
|
300
|
+
if (messageState.segments.has(index)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
// Store segment
|
|
304
|
+
messageState.segments.set(index, payload);
|
|
305
|
+
// Check if complete
|
|
306
|
+
if (messageState.segments.size === total) {
|
|
307
|
+
// Reassemble in order
|
|
308
|
+
const reassembled = this.reassembleMessage(messageState);
|
|
309
|
+
peerState.messages.delete(messageId);
|
|
310
|
+
this.deliverMessage(peerId, reassembled);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Reassemble a complete message from segments.
|
|
315
|
+
*/
|
|
316
|
+
reassembleMessage(state) {
|
|
317
|
+
// Calculate total size
|
|
318
|
+
let totalSize = 0;
|
|
319
|
+
for (const segment of state.segments.values()) {
|
|
320
|
+
totalSize += segment.length;
|
|
321
|
+
}
|
|
322
|
+
// Reassemble in order
|
|
323
|
+
const result = new Uint8Array(totalSize);
|
|
324
|
+
let offset = 0;
|
|
325
|
+
for (let i = 0; i < state.total; i++) {
|
|
326
|
+
const segment = state.segments.get(i);
|
|
327
|
+
if (segment) {
|
|
328
|
+
result.set(segment, offset);
|
|
329
|
+
offset += segment.length;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Decompress and deliver a reassembled message.
|
|
336
|
+
*/
|
|
337
|
+
deliverMessage(peerId, data) {
|
|
338
|
+
if (data.length < COMPRESSION_HEADER_SIZE) {
|
|
339
|
+
// Invalid message, ignore
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const compressionByte = data[0];
|
|
343
|
+
const payload = data.slice(COMPRESSION_HEADER_SIZE);
|
|
344
|
+
let decompressed;
|
|
345
|
+
if (compressionByte === COMPRESSION_HEADER_GZIP) {
|
|
346
|
+
try {
|
|
347
|
+
decompressed = pako.inflate(payload);
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// Decompression failed, ignore message
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
// Raw data
|
|
356
|
+
decompressed = payload;
|
|
357
|
+
}
|
|
358
|
+
this.onMessage?.(peerId, decompressed);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Clean up state for a disconnected peer.
|
|
362
|
+
*/
|
|
363
|
+
cleanupPeerState(peerId) {
|
|
364
|
+
this.messageIdCounters.delete(peerId);
|
|
365
|
+
this.reassemblyBuffers.delete(peerId);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Start the periodic cleanup timer.
|
|
369
|
+
*/
|
|
370
|
+
startCleanupTimer() {
|
|
371
|
+
// Run cleanup every second
|
|
372
|
+
this.cleanupTimerId = setInterval(() => {
|
|
373
|
+
this.cleanupTimedOutMessages();
|
|
374
|
+
}, 1000);
|
|
375
|
+
// Allow process to exit even if timer is running
|
|
376
|
+
if (this.cleanupTimerId.unref) {
|
|
377
|
+
this.cleanupTimerId.unref();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Stop the cleanup timer.
|
|
382
|
+
*/
|
|
383
|
+
stopCleanupTimer() {
|
|
384
|
+
if (this.cleanupTimerId !== null) {
|
|
385
|
+
clearInterval(this.cleanupTimerId);
|
|
386
|
+
this.cleanupTimerId = null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Remove incomplete messages that have timed out.
|
|
391
|
+
*/
|
|
392
|
+
cleanupTimedOutMessages() {
|
|
393
|
+
const now = Date.now();
|
|
394
|
+
for (const [peerId, peerState] of this.reassemblyBuffers) {
|
|
395
|
+
for (const [messageId, messageState] of peerState.messages) {
|
|
396
|
+
if (now - messageState.startTime > this.config.reassemblyTimeout) {
|
|
397
|
+
peerState.messages.delete(messageId);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
// Remove peer state if empty
|
|
401
|
+
if (peerState.messages.size === 0) {
|
|
402
|
+
this.reassemblyBuffers.delete(peerId);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=transforming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transforming.js","sourceRoot":"","sources":["../../src/transport/transforming.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AA4C7B;;GAEG;AACH,MAAM,CAAC,MAAM,qCAAqC,GACjD;IACC,WAAW,EAAE,MAAM;IACnB,oBAAoB,EAAE,GAAG;IACzB,YAAY,EAAE,IAAI;IAClB,cAAc,EAAE,KAAK;IACrB,iBAAiB,EAAE,IAAI;CACvB,CAAC;AAEH,+CAA+C;AAC/C,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC,kDAAkD;AAClD,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAErC,8CAA8C;AAC9C,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAElC,6EAA6E;AAC7E,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B,mFAAmF;AACnF,MAAM,wBAAwB,GAAG,IAAI,CAAC,CAAC,yBAAyB;AAwBhE;;;;;GAKG;AACH,MAAM,OAAO,qBAAqB;IAC1B,SAAS,GACf,IAAI,CAAC;IACC,SAAS,GAAsC,IAAI,CAAC;IACpD,YAAY,GAAsC,IAAI,CAAC;IACvD,OAAO,GAEJ,IAAI,CAAC;IAEE,KAAK,CAAmB;IACxB,MAAM,CAA8B;IAErD,yDAAyD;IACxC,iBAAiB,GAAwB,IAAI,GAAG,EAAE,CAAC;IAEpE,wDAAwD;IACvC,iBAAiB,GACjC,IAAI,GAAG,EAAE,CAAC;IAEX,oCAAoC;IAC5B,cAAc,GAA0C,IAAI,CAAC;IAErE;;;;;OAKG;IACH,YACC,KAAuB,EACvB,MAA6C;QAE7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG;YACb,GAAG,qCAAqC;YACxC,GAAG,MAAM;SACT,CAAC;QAEF,oCAAoC;QACpC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;YAC1C,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC7C,CAAC,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,EAAE;YACjC,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,MAAM,EAAE,EAAE;YACpC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;YAC9B,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC,CAAC;QAEF,sCAAsC;QACtC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAC/C,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YACxC,CAAC,CAAC;QACH,CAAC;QAED,0DAA0D;QAC1D,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,MAAc;QAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,MAAc;QACxB,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,aAAa;QACZ,qBAAqB;QACrB,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,MAAc,EAAE,OAAmB,EAAE,QAAiB;QAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAE1D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5C,CAAC;IACF,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,OAAmB,EAAE,QAAiB;QAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAEpD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;YAC1D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC5C,CAAC;QACF,CAAC;IACF,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAE,MAAc;QACnC,OAAO,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,OAAO;QACN,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,2DAA2D;QAC3D,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IACxB,CAAC;IAED;;;OAGG;IACK,iBAAiB,CAAC,OAAmB;QAC5C,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QAED,sCAAsC;QACtC,IACC,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM;YAClC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAChD,CAAC;YACF,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QAED,sBAAsB;QACtB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEzC,iEAAiE;QACjE,IACC,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM;YAClC,UAAU,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,EAClC,CAAC;YACF,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,IAAI,CAAC,oBAAoB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACK,oBAAoB,CAC3B,IAAgB,EAChB,YAAqB;QAErB,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,uBAAuB,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,MAAM,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,sBAAsB,CAAC;QAC5E,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;QAC1C,OAAO,MAAM,CAAC;IACf,CAAC;IAED;;;OAGG;IACK,cAAc,CAAC,MAAc,EAAE,IAAgB;QACtD,yCAAyC;QACzC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,GAAG,mBAAmB,CAAC;QAExE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC,MAAM,IAAI,cAAc,EAAE,CAAC;YAChE,iBAAiB;YACjB,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,oBAAoB;QACpB,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;QAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAEhD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,CAAC,GAAG,cAAc,CAAC;YACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAEvC,QAAQ,CAAC,IAAI,CACZ,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,aAAa,CAAC,CAC9D,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,aAAa,CACpB,MAAc,EACd,OAAmB,EACnB,KAAa,EACb,KAAa;QAEb,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACnE,CAAC;IAED;;OAEG;IACK,mBAAmB,CAC1B,SAAiB,EACjB,OAAmB,EACnB,KAAa,EACb,KAAa;QAEb,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACrE,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE1C,8CAA8C;QAC9C,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAE/B,gBAAgB;QAChB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAE1C,OAAO,OAAO,CAAC;IAChB,CAAC;IAED;;;;;;;OAOG;IACK,gBAAgB,CAAC,MAAc;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,0BAA0B;QAC5D,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACzC,OAAO,OAAO,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,MAAc,EAAE,IAAgB;QAC7D,IAAI,IAAI,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;YACvC,0BAA0B;YAC1B,OAAO;QACR,CAAC;QAED,uBAAuB;QACvB,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAEtC,kBAAkB;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAEhD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACjB,wBAAwB;YACxB,OAAO;QACR,CAAC;QAED,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACjB,oCAAoC;YACpC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACrC,OAAO;QACR,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACK,aAAa,CACpB,MAAc,EACd,SAAiB,EACjB,KAAa,EACb,KAAa,EACb,OAAmB;QAEnB,iCAAiC;QACjC,IAAI,KAAK,GAAG,wBAAwB,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;YACxD,OAAO;QACR,CAAC;QAED,2BAA2B;QAC3B,IAAI,SAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,SAAS,GAAG,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC/C,CAAC;QAED,8BAA8B;QAC9B,IAAI,YAAY,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,YAAY,EAAE,CAAC;YACnB,YAAY,GAAG;gBACd,KAAK;gBACL,QAAQ,EAAE,IAAI,GAAG,EAAE;gBACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACrB,CAAC;YACF,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QACjD,CAAC;QAED,yBAAyB;QACzB,IAAI,YAAY,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YAClC,oCAAoC;YACpC,OAAO;QACR,CAAC;QAED,4BAA4B;QAC5B,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO;QACR,CAAC;QAED,gBAAgB;QAChB,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAE1C,oBAAoB;QACpB,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC1C,sBAAsB;YACtB,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;YACzD,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAErC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAC1C,CAAC;IACF,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,KAA6B;QACtD,uBAAuB;QACvB,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC/C,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;QAC7B,CAAC;QAED,sBAAsB;QACtB,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,OAAO,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC5B,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;YAC1B,CAAC;QACF,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,MAAc,EAAE,IAAgB;QACtD,IAAI,IAAI,CAAC,MAAM,GAAG,uBAAuB,EAAE,CAAC;YAC3C,0BAA0B;YAC1B,OAAO;QACR,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAEpD,IAAI,YAAwB,CAAC;QAE7B,IAAI,eAAe,KAAK,uBAAuB,EAAE,CAAC;YACjD,IAAI,CAAC;gBACJ,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACR,uCAAuC;gBACvC,OAAO;YACR,CAAC;QACF,CAAC;aAAM,CAAC;YACP,WAAW;YACX,YAAY,GAAG,OAAO,CAAC;QACxB,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,MAAc;QACtC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,iBAAiB;QACxB,2BAA2B;QAC3B,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAChC,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,iDAAiD;QACjD,IAAI,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;YAC/B,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC7B,CAAC;IACF,CAAC;IAED;;OAEG;IACK,gBAAgB;QACvB,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YAClC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC5B,CAAC;IACF,CAAC;IAED;;OAEG;IACK,uBAAuB;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,KAAK,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC1D,KAAK,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;gBAC5D,IAAI,GAAG,GAAG,YAAY,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;oBAClE,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACtC,CAAC;YACF,CAAC;YAED,6BAA6B;YAC7B,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvC,CAAC;QACF,CAAC;IACF,CAAC;CACD"}
|