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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zero-query",
3
- "version": "1.2.12",
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",
@@ -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
- cb(msg);
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
- await Promise.resolve(); await Promise.resolve();
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
  });