zero-query 1.2.13 → 1.2.15

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.13",
3
+ "version": "1.2.15",
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",
@@ -253,11 +253,12 @@ export class Peer {
253
253
  this.pc.onicecandidate = (event) => {
254
254
  if (this.closed) return;
255
255
  const candidate = event && event.candidate;
256
- // End-of-candidates marker (null) -> always forward.
257
- if (!candidate) {
258
- this.signaling.send('ice', { target: this.id, candidate: null });
259
- return;
260
- }
256
+ // End-of-candidates: the @zero-server/webrtc SignalingHub rejects
257
+ // any ice frame whose `candidate` is not a non-empty string
258
+ // (BAD_FRAME -> counts toward the protocol-error budget that can
259
+ // disconnect the peer). Browsers infer end-of-candidates from
260
+ // iceGatheringState anyway, so we simply drop the marker.
261
+ if (!candidate) return;
261
262
  const cand = typeof candidate === 'string' ? candidate : candidate.candidate;
262
263
  if (!cand) return;
263
264
  // NOTE: do NOT filter mDNS (.local) candidates. Firefox emits them by
@@ -362,12 +363,17 @@ export class Peer {
362
363
  }
363
364
  // Older zero-query clients relayed only the bare `a=candidate:` string,
364
365
  // which browsers reject with "missing values for both sdpMid and
365
- // sdpMLineIndex". Newer clients send a full init dict - normalize both.
366
+ // sdpMLineIndex". Pass sdpMLineIndex:0 and leave sdpMid null so
367
+ // the browser binds the candidate to the first (and, under
368
+ // bundlePolicy:'max-bundle', only) m-section. Passing sdpMid:''
369
+ // is NOT equivalent to omitting it - the browser treats empty
370
+ // string as a real mid and fails with "Failed to execute
371
+ // 'addIceCandidate'" because no m-section has mid="".
366
372
  const init = (typeof candidate === 'string')
367
- ? { candidate, sdpMid: '', sdpMLineIndex: 0 }
373
+ ? { candidate, sdpMid: null, sdpMLineIndex: 0 }
368
374
  : {
369
375
  candidate: candidate.candidate,
370
- sdpMid: candidate.sdpMid != null ? candidate.sdpMid : '',
376
+ sdpMid: candidate.sdpMid != null ? candidate.sdpMid : null,
371
377
  sdpMLineIndex: candidate.sdpMLineIndex != null ? candidate.sdpMLineIndex : 0,
372
378
  usernameFragment: candidate.usernameFragment != null ? candidate.usernameFragment : undefined,
373
379
  };
@@ -130,26 +130,23 @@ describe('Peer (perfect negotiation)', () => {
130
130
  // Outbound: ICE trickle
131
131
  // -----------------------------------------------------------------------
132
132
 
133
- it('forwards trickled ICE candidates as `ice` frames addressed to peerId', async () => {
133
+ it('forwards trickled ICE candidates as `ice` frames addressed to peerId, dropping the null EOC marker', async () => {
134
134
  const sig = await makeOpenSignaling();
135
135
  const peer = new Peer('peer_a', sig, { RTCPeerConnection: FakeRTCPeerConnection });
136
136
 
137
137
  lastPc().fakeIceCandidate('candidate:1 1 udp 2122260223 192.0.2.1 5000 typ host');
138
138
  lastPc().fakeIceCandidate('candidate:2 1 udp 1686052607 198.51.100.1 5001 typ srflx');
139
- lastPc().fakeIceCandidate(null); // end-of-candidates
140
-
141
- // Bypass coalesce window by reading the queued frames directly: we know
142
- // signaling.js batches ICE per 200ms - flush by advancing real time...
143
- // ... or simpler: assert the queue has populated by reading send calls
144
- // once the timer flushes. Use synchronous expectation on _iceQueue.
145
- // The SignalingClient buffers ice frames; we don't want to wait timers
146
- // here, so just verify a deterministic side-effect: at minimum the
147
- // queue depth matches the candidate count we trickled.
148
- expect(sig._iceQueue.length).toBe(3);
139
+ lastPc().fakeIceCandidate(null); // end-of-candidates - dropped on the floor
140
+
141
+ // The SignalingHub rejects ice frames whose candidate is not a
142
+ // non-empty string (BAD_FRAME, counted against the protocol-error
143
+ // budget), so the EOC marker must NOT reach the wire. Browsers infer
144
+ // end-of-candidates from iceGatheringState anyway.
145
+ expect(sig._iceQueue.length).toBe(2);
149
146
  expect(sig._iceQueue[0].target).toBe('peer_a');
150
147
  expect(typeof sig._iceQueue[0].candidate).toBe('string');
151
148
  expect(sig._iceQueue[0].candidate).toContain('typ host');
152
- expect(sig._iceQueue[2].candidate).toBeNull();
149
+ expect(sig._iceQueue[1].candidate).toContain('typ srflx');
153
150
  peer.close();
154
151
  });
155
152
 
@@ -175,13 +172,12 @@ describe('Peer (perfect negotiation)', () => {
175
172
  for (let i = 0; i < 10; i++) {
176
173
  lastPc().fakeIceCandidate(`candidate:${i} 1 udp 2122260223 192.0.2.${i} 5000 typ host`);
177
174
  }
178
- // EOC marker is always allowed past the cap.
175
+ // EOC marker is dropped (server rejects null candidates as BAD_FRAME).
179
176
  lastPc().fakeIceCandidate(null);
180
177
 
181
178
  const iceFrames = sig._iceQueue;
182
- const nonNull = iceFrames.filter((f) => f.candidate !== null);
183
- expect(nonNull.length).toBe(3);
184
- expect(iceFrames.some((f) => f.candidate === null)).toBe(true);
179
+ expect(iceFrames.length).toBe(3);
180
+ expect(iceFrames.every((f) => typeof f.candidate === 'string' && f.candidate.length > 0)).toBe(true);
185
181
  peer.close();
186
182
  });
187
183
 
@@ -237,7 +233,7 @@ describe('Peer (perfect negotiation)', () => {
237
233
  expect(lastPc().addIceCandidateCalls).toHaveLength(2);
238
234
  expect(lastPc().addIceCandidateCalls[0]).toEqual({
239
235
  candidate: 'candidate:7 1 udp 1 192.0.2.7 5000 typ host',
240
- sdpMid: '',
236
+ sdpMid: null,
241
237
  sdpMLineIndex: 0,
242
238
  });
243
239
  expect(lastPc().addIceCandidateCalls[1]).toBeNull();