@fairfox/polly 0.56.0 → 0.58.0

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.
@@ -277,17 +277,43 @@ export interface SlotInitiationDecision {
277
277
  * the adapter.
278
278
  * - `firstOutboundSendAt`: when polly first dispatched bytes through
279
279
  * {@link MeshWebRTCAdapter.send} for this peer. If
280
- * `peerCandidateEmittedAt` is set but this is still `undefined`, the
281
- * wrapper above polly (Automerge's NetworkSubsystem, MeshNetworkAdapter)
282
- * never asked the adapter to send anything; that's a failure
283
- * upstream of polly.
280
+ * `peerCandidateEmittedAt` is set but this is still `undefined`,
281
+ * Automerge's NetworkSubsystem has not asked the adapter to send
282
+ * anything. The polly#106 ladder named "no handle locally" as the
283
+ * typical cause for this rung; the polly#107 failing-shape evidence
284
+ * (fourteen `$meshState` handles pre-warmed in `ready` state,
285
+ * `firstOutboundSendAt` still undefined long after peer-candidate
286
+ * fires) shows that's a misleading ladder entry. Revised:
287
+ *
288
+ * - If `repo.handles[docId]` is undefined or in a non-`ready`
289
+ * state for every doc this peer should sync, the consumer
290
+ * fix is real — pre-warm the handles via the documented
291
+ * `$meshState(key, initial)` factory before the slot opens.
292
+ *
293
+ * - If `repo.handles[docId]` is `ready` for every doc AND
294
+ * `getPeerStateSnapshot().peers[…].slot.handles[docId]
295
+ * .announcedToPeer` is `false`, the gate is BETWEEN Automerge's
296
+ * NetworkSubsystem and the adapter — not on the consumer's
297
+ * handle-construction path. This is the polly#107 surface;
298
+ * post-#107 the mesh client hooks `peer-candidate` to invoke
299
+ * the synchronizer's `reevaluateDocumentShare` so the
300
+ * `addPeer`/`addDocument` ordering race that leaves a handle
301
+ * un-announced gets closed by an idempotent re-evaluation.
302
+ *
303
+ * - If `announcedToPeer` becomes `true` for every relevant doc
304
+ * and `firstOutboundSendAt` is still undefined, the gap is in
305
+ * polly's own send path — that's a polly bug, not an
306
+ * Automerge or consumer one.
307
+ *
284
308
  * - `firstInboundMessageAt`: when polly first emitted a `message` event
285
309
  * for this peer. If `peerCandidateEmittedAt` is set on the remote and
286
310
  * `firstOutboundSendAt` is set on the remote but this is `undefined`
287
311
  * locally, bytes were sent across the wire but never decoded — that
288
312
  * points at the crypto envelope or the wire fragmenter.
289
313
  *
290
- * Polly issue #106 item 7. */
314
+ * Polly issue #106 item 7; polly#107 revised the
315
+ * `firstOutboundSendAt` rung to point at the polly⇄Automerge gate
316
+ * rather than the consumer. */
291
317
  export interface SyncHandshakeAttemptSnapshot {
292
318
  dataChannelOpenedAt: number | undefined;
293
319
  peerCandidateEmittedAt: number | undefined;
@@ -337,6 +363,47 @@ export interface InFlightSyncSnapshot {
337
363
  * and this stays 0. */
338
364
  applyBacklog: number;
339
365
  }
366
+ /** Per-handle per-peer sync bookkeeping. Closes the diagnostic gap
367
+ * between "handle exists in repo" (observable today via `repo.handles`)
368
+ * and "Automerge's NetworkSubsystem has actually told this peer about
369
+ * this handle" — the latter being the load-bearing question for the
370
+ * polly#107 failing shape, where fourteen pre-warmed handles sit in
371
+ * `ready` state and the peer slot is fully connected, yet Automerge
372
+ * has not asked the adapter to send a single sync message.
373
+ *
374
+ * Each field is stamped from the adapter's own wire path: `out` from
375
+ * {@link MeshWebRTCAdapter.send}, `in` from {@link dispatchMessage}'s
376
+ * deserialised view. Combined with the consumer's view of
377
+ * `repo.handles[documentId].state`, the mesh client's
378
+ * {@link createMeshClient}-returned `getPeerStateSnapshot` produces the
379
+ * full per-handle observability the polly#107 ticket asks for in
380
+ * item 7. */
381
+ export interface HandleSyncSnapshot {
382
+ /** `performance.now()` of the most recent send through
383
+ * {@link MeshWebRTCAdapter.send} carrying this `documentId` for this
384
+ * peer. `undefined` means Automerge has never asked the adapter to
385
+ * send a sync message for this document — i.e. the handle is NOT
386
+ * "announced to peer" in the polly#107 sense, regardless of whether
387
+ * the handle is `ready` in `repo.handles`. */
388
+ lastSyncMessageOutAt: number | undefined;
389
+ /** `performance.now()` of the most recent inbound message dispatched
390
+ * upward for this `documentId` from this peer. `undefined` means we
391
+ * have never received a sync message for this document from this
392
+ * peer — they have not announced their copy of this handle to us. */
393
+ lastSyncMessageInAt: number | undefined;
394
+ /** Byte length of the most recent outbound sync message (the raw
395
+ * `RTCDataChannel.send` payload, which is the crypto-wrapped envelope
396
+ * — not the inner Automerge sync size). `undefined` until the first
397
+ * outbound send. Used by the polly#107 example's wire-level
398
+ * transcript to distinguish "Automerge generated an empty sync
399
+ * message" from "Automerge generated nothing at all". */
400
+ lastSyncMessageOutSize: number | undefined;
401
+ /** Type field of the most recent outbound message for this document
402
+ * to this peer. Typically `"sync"` once handshake has begun, or
403
+ * `"request"` while the local side is still asking. `undefined`
404
+ * until the first outbound send. */
405
+ lastSyncMessageOutType: string | undefined;
406
+ }
340
407
  /**
341
408
  * Automerge-Repo NetworkAdapter backed by real WebRTC data channels.
342
409
  * Manages one RTCPeerConnection per remote peer and uses a supplied
@@ -475,6 +542,7 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
475
542
  inFlightSync: InFlightSyncSnapshot | undefined;
476
543
  transport: TransportSnapshot | undefined;
477
544
  lastSyncHandshakeAttempt: SyncHandshakeAttemptSnapshot;
545
+ handles: Record<string, HandleSyncSnapshot>;
478
546
  };
479
547
  }>;
480
548
  };
@@ -736,6 +804,10 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
736
804
  * observability for {@link SyncHandshakeAttemptSnapshot}; does not
737
805
  * affect dispatch. */
738
806
  private stampFirstInboundMessage;
807
+ /** Stamp `handles[documentId].lastSyncMessageInAt` for the
808
+ * per-handle observability layer polly#107 adds. Pure observability;
809
+ * does not affect dispatch. */
810
+ private stampHandleInbound;
739
811
  private finishInFlightSyncApply;
740
812
  private emitSyncProgress;
741
813
  private handleSyncFragment;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fairfox/polly",
3
- "version": "0.56.0",
3
+ "version": "0.58.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Multi-execution-context framework with reactive state and cross-context messaging for Chrome extensions, PWAs, and worker-based applications",