zero-query 1.2.12 → 1.2.14
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/cli/scaffold/webrtc/app/app.js +1 -1
- package/cli/scaffold/webrtc/app/components/video-room.js +64 -45
- package/cli/scaffold/webrtc/global.css +244 -61
- package/cli/scaffold/webrtc/index.html +14 -1
- package/cli/scaffold/webrtc/server/index.js +1 -1
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +11 -4
- package/dist/zquery.min.js +3 -3
- package/package.json +1 -1
- package/src/webrtc/peer.js +8 -1
- package/tests/webrtc/peer.test.js +11 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zero-query",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.14",
|
|
4
4
|
"description": "Lightweight modern frontend library - jQuery-like selectors, reactive components, SPA router, and state management with zero dependencies.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
package/src/webrtc/peer.js
CHANGED
|
@@ -104,6 +104,11 @@ export class Peer {
|
|
|
104
104
|
this._maxIceCandidates = options.maxIceCandidates || DEFAULT_MAX_ICE_CANDIDATES;
|
|
105
105
|
this._sentCandidates = 0;
|
|
106
106
|
this._sigUnsub = [];
|
|
107
|
+
// Serialize incoming remote events (offer/answer/ice) so an ICE frame
|
|
108
|
+
// can never call addIceCandidate() while a setRemoteDescription() is
|
|
109
|
+
// still in flight - that race throws "The remote description was null"
|
|
110
|
+
// and leaves the PeerConnection stuck in `have-local-offer` forever.
|
|
111
|
+
this._opChain = Promise.resolve();
|
|
107
112
|
|
|
108
113
|
this._attachPc();
|
|
109
114
|
this._attachSignaling();
|
|
@@ -294,7 +299,9 @@ export class Peer {
|
|
|
294
299
|
const guard = (cb) => (msg) => {
|
|
295
300
|
if (this.closed) return;
|
|
296
301
|
if (!msg || msg.from !== this.id) return;
|
|
297
|
-
|
|
302
|
+
// Chain every remote event behind any prior in-flight op so SDP
|
|
303
|
+
// and ICE never race against each other.
|
|
304
|
+
this._opChain = this._opChain.then(() => cb(msg)).catch(() => {});
|
|
298
305
|
};
|
|
299
306
|
this._sigUnsub.push(
|
|
300
307
|
this.signaling.on('offer', guard((m) => this._onRemoteDescription('offer', m.sdp))),
|
|
@@ -196,7 +196,7 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
196
196
|
fakeSockets[0].fakeMessage({ type: 'offer', from: 'peer_a', sdp: 'remote-sdp-blob' });
|
|
197
197
|
// setRemoteDescription + setLocalDescription are awaited inside the
|
|
198
198
|
// handler; yield two microtasks.
|
|
199
|
-
await Promise.resolve(); await Promise.resolve();
|
|
199
|
+
await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve();
|
|
200
200
|
|
|
201
201
|
expect(lastPc().setRemoteCalls).toHaveLength(1);
|
|
202
202
|
expect(lastPc().setRemoteCalls[0]).toEqual({ type: 'offer', sdp: 'remote-sdp-blob' });
|
|
@@ -214,7 +214,7 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
214
214
|
// Pretend we previously offered.
|
|
215
215
|
lastPc().signalingState = 'have-local-offer';
|
|
216
216
|
fakeSockets[0].fakeMessage({ type: 'answer', from: 'peer_a', sdp: 'remote-answer-sdp' });
|
|
217
|
-
await Promise.resolve(); await Promise.resolve();
|
|
217
|
+
await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve();
|
|
218
218
|
|
|
219
219
|
expect(lastPc().setRemoteCalls).toHaveLength(1);
|
|
220
220
|
expect(lastPc().setRemoteCalls[0].type).toBe('answer');
|
|
@@ -229,7 +229,10 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
229
229
|
|
|
230
230
|
fakeSockets[0].fakeMessage({ type: 'ice', from: 'peer_a', candidate: 'candidate:7 1 udp 1 192.0.2.7 5000 typ host' });
|
|
231
231
|
fakeSockets[0].fakeMessage({ type: 'ice', from: 'peer_a', candidate: null });
|
|
232
|
-
|
|
232
|
+
// The peer serializes remote events through an internal microtask chain;
|
|
233
|
+
// drain it before asserting.
|
|
234
|
+
await peer._opChain;
|
|
235
|
+
await peer._opChain;
|
|
233
236
|
|
|
234
237
|
expect(lastPc().addIceCandidateCalls).toHaveLength(2);
|
|
235
238
|
expect(lastPc().addIceCandidateCalls[0]).toEqual({
|
|
@@ -247,7 +250,7 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
247
250
|
|
|
248
251
|
fakeSockets[0].fakeMessage({ type: 'offer', from: 'peer_b', sdp: 'other' });
|
|
249
252
|
fakeSockets[0].fakeMessage({ type: 'ice', from: 'peer_b', candidate: 'foo' });
|
|
250
|
-
await Promise.resolve(); await Promise.resolve();
|
|
253
|
+
await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve();
|
|
251
254
|
|
|
252
255
|
expect(lastPc().setRemoteCalls).toHaveLength(0);
|
|
253
256
|
expect(lastPc().addIceCandidateCalls).toHaveLength(0);
|
|
@@ -268,7 +271,7 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
268
271
|
// Simulate an in-flight local offer.
|
|
269
272
|
peer.makingOffer = true;
|
|
270
273
|
fakeSockets[0].fakeMessage({ type: 'offer', from: 'peer_a', sdp: 'collide' });
|
|
271
|
-
await Promise.resolve(); await Promise.resolve();
|
|
274
|
+
await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve();
|
|
272
275
|
|
|
273
276
|
expect(peer.ignoreOffer).toBe(true);
|
|
274
277
|
expect(lastPc().setRemoteCalls).toHaveLength(0);
|
|
@@ -285,7 +288,7 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
285
288
|
|
|
286
289
|
peer.makingOffer = true;
|
|
287
290
|
fakeSockets[0].fakeMessage({ type: 'offer', from: 'peer_a', sdp: 'collide' });
|
|
288
|
-
await Promise.resolve(); await Promise.resolve();
|
|
291
|
+
await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve();
|
|
289
292
|
|
|
290
293
|
expect(peer.ignoreOffer).toBe(false);
|
|
291
294
|
expect(lastPc().setRemoteCalls).toHaveLength(1);
|
|
@@ -305,7 +308,7 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
305
308
|
peer.ignoreOffer = true;
|
|
306
309
|
lastPc().failNextAddIce = new Error('stale');
|
|
307
310
|
fakeSockets[0].fakeMessage({ type: 'ice', from: 'peer_a', candidate: 'candidate:foo' });
|
|
308
|
-
await Promise.resolve(); await Promise.resolve();
|
|
311
|
+
await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve();
|
|
309
312
|
|
|
310
313
|
expect(errors).toHaveLength(0);
|
|
311
314
|
peer.close();
|
|
@@ -372,7 +375,7 @@ describe('Peer (perfect negotiation)', () => {
|
|
|
372
375
|
|
|
373
376
|
// Further inbound frames must not touch the (closed) PC.
|
|
374
377
|
fakeSockets[0].fakeMessage({ type: 'offer', from: 'peer_a', sdp: 'late' });
|
|
375
|
-
await Promise.resolve(); await Promise.resolve();
|
|
378
|
+
await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve();
|
|
376
379
|
expect(lastPc().setRemoteCalls).toHaveLength(0);
|
|
377
380
|
});
|
|
378
381
|
});
|