svelte-realtime 0.6.0-next.77 → 0.6.0-next.78

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": "svelte-realtime",
3
- "version": "0.6.0-next.77",
3
+ "version": "0.6.0-next.78",
4
4
  "publishConfig": {
5
5
  "tag": "next"
6
6
  },
@@ -1096,17 +1096,18 @@ function _smoothJoinSnapshot(rec, identity) {
1096
1096
  * `moved` carries the entities that changed this tick, each with the author identity
1097
1097
  * to suppress for that frame (echo suppression): on the owner that is the commanded
1098
1098
  * author under noEcho, on the non-owner it is the exclude identity the owner stamped
1099
- * onto the relay. An entity in range that did NOT move is delivered from `catalogByKey`
1100
- * (first-sight catch-up), and a subscriber's own entity is never caught up to itself
1099
+ * onto the relay. An entity in range that did NOT move is delivered via `lookup`
1100
+ * (first-sight catch-up: the owner reads the authority, the non-owner its shadow),
1101
+ * and a subscriber's own entity is never caught up to itself
1101
1102
  * under noEcho. Only a remote frame advances the send-cadence estimator (a client
1102
1103
  * discards its own entity frame before measuring its interpolation delay).
1103
1104
  * @param {any} rec
1104
1105
  * @param {Map<string, Set<string>>} relevancy identity -> entity keys to deliver
1105
- * @param {Map<string, any>} catalogByKey full per-entity state, for catch-up
1106
+ * @param {(key: string) => any} lookup current per-entity state, for catch-up
1106
1107
  * @param {Map<string, { state: any, exclude: string | undefined }>} moved entities that changed this tick
1107
1108
  * @param {number} t wall stamp for the send-cadence estimator (hitTest only)
1108
1109
  */
1109
- function _smoothDeliverCulled(rec, relevancy, catalogByKey, moved, t) {
1110
+ function _smoothDeliverCulled(rec, relevancy, lookup, moved, t) {
1110
1111
  for (const [identity, ws] of rec.registry) {
1111
1112
  const relSet = relevancy.get(identity);
1112
1113
  if (relSet === undefined) continue;
@@ -1123,7 +1124,7 @@ function _smoothDeliverCulled(rec, relevancy, catalogByKey, moved, t) {
1123
1124
  exclude = m.exclude;
1124
1125
  } else {
1125
1126
  // First-sight catch-up: in range, no update of its own this tick.
1126
- state = catalogByKey.get(key);
1127
+ state = lookup(key);
1127
1128
  exclude = rec.noEcho ? key : undefined;
1128
1129
  }
1129
1130
  if (exclude !== undefined && identity === exclude) continue;
@@ -1169,7 +1170,7 @@ function _smoothCullTick(rec) {
1169
1170
  const catalog = [];
1170
1171
  for (const [key, state] of rec.shadow) catalog.push({ key, state });
1171
1172
  const relevancy = rec.interest.compute(catalog, rec.registry.keys(), rec.interestTick++, rec.budgetOf ?? undefined);
1172
- _smoothDeliverCulled(rec, relevancy, rec.shadow, rec.pendingRelay, t);
1173
+ _smoothDeliverCulled(rec, relevancy, (key) => rec.shadow.get(key), rec.pendingRelay, t);
1173
1174
  rec.pendingRelay.clear();
1174
1175
  rec.shadowDirty = false;
1175
1176
  rec.interestDirty = false;
@@ -1309,8 +1310,9 @@ function _smoothTick(rec) {
1309
1310
  rec.interestDirty = false;
1310
1311
  // Broadcast updates collect here and flush as ONE batched fan-out after the
1311
1312
  // loop (per-entry author exclusion rides along); the relay and the cells
1312
- // routing stay per-entity inside the loop.
1313
- const broadcastUpdates = [];
1313
+ // routing stay per-entity inside the loop. Allocated on first use: the
1314
+ // interest and cells paths never push (their delivery runs elsewhere).
1315
+ let broadcastUpdates = null;
1314
1316
  for (let i = 0; i < wireUpdates.length; i++) {
1315
1317
  const u = wireUpdates[i];
1316
1318
  // Cells mode: route each changed entity to its cell topic (native shared
@@ -1343,7 +1345,7 @@ function _smoothTick(rec) {
1343
1345
  // own subscribers all see the move).
1344
1346
  if (!relevancy) {
1345
1347
  const localAuthor = rec.noEcho && u.commanded ? rec.registry.get(u.key) : undefined;
1346
- broadcastUpdates.push({ key: u.key, state: u.state, excludeWs: localAuthor });
1348
+ (broadcastUpdates ??= []).push({ key: u.key, state: u.state, excludeWs: localAuthor });
1347
1349
  }
1348
1350
  // Relay to other instances. Only a remote (surrogate) author needs a
1349
1351
  // cross-instance exclude - a local author is already excluded (below, or
@@ -1357,13 +1359,13 @@ function _smoothTick(rec) {
1357
1359
  // Interest-on: updates fan out per subscriber after this loop (the
1358
1360
  // relevancy walk below), so nothing broadcasts here.
1359
1361
  } else {
1360
- broadcastUpdates.push({ key: u.key, state: u.state, excludeWs: rec.noEcho && u.commanded ? u.ws : undefined });
1362
+ (broadcastUpdates ??= []).push({ key: u.key, state: u.state, excludeWs: rec.noEcho && u.commanded ? u.ws : undefined });
1361
1363
  }
1362
1364
  }
1363
1365
  // One tick, one fan-out: every collected update in a single batched frame
1364
1366
  // per capable viewer (per-entity on an older platform). Acks, events, and
1365
1367
  // removals keep their own frames below, exactly as before.
1366
- _smoothPublishUpdates(rec, broadcastUpdates);
1368
+ if (broadcastUpdates !== null) _smoothPublishUpdates(rec, broadcastUpdates);
1367
1369
  // Interest-on: deliver each LOCAL subscriber (single-instance, or the owner
1368
1370
  // side of a cluster) the CURRENT state of every entity the relevancy pass marked
1369
1371
  // for it this tick - the entities inside its area of interest that changed since
@@ -1377,19 +1379,22 @@ function _smoothTick(rec) {
1377
1379
  // the cursor viewport cull does. Events and removals stay on the shared broadcast
1378
1380
  // path (over-deliver rather than risk a ghost or a dropped one-shot).
1379
1381
  if (relevancy) {
1380
- const catalogByKey = new Map();
1381
- for (let i = 0; i < catalog.length; i++) catalogByKey.set(catalog[i].key, catalog[i].state);
1382
1382
  // `moved` carries each entity that changed this tick with the author to suppress
1383
1383
  // for that frame (the commanded author under noEcho). The shared delivery helper
1384
- // catches up in-range entities that did not move from `catalogByKey`. Only a
1385
- // remote frame advances the lag-comp send-cadence estimator (the client discards
1386
- // its own entity frame before measuring its interpolation delay).
1384
+ // catches up in-range entities that did not move straight from the authority
1385
+ // (`get` is O(1) and hands back the same state object the catalog carried -
1386
+ // nothing mutates entries between the post-drain snapshot and this delivery).
1387
+ // Only a remote frame advances the lag-comp send-cadence estimator (the client
1388
+ // discards its own entity frame before measuring its interpolation delay).
1387
1389
  const moved = new Map();
1388
1390
  for (let i = 0; i < wireUpdates.length; i++) {
1389
1391
  const u = wireUpdates[i];
1390
1392
  moved.set(u.key, { state: u.state, exclude: rec.noEcho && u.commanded ? u.key : undefined });
1391
1393
  }
1392
- _smoothDeliverCulled(rec, relevancy, catalogByKey, moved, t);
1394
+ _smoothDeliverCulled(rec, relevancy, (key) => {
1395
+ const e = rec.authority.get(key);
1396
+ return e === undefined ? undefined : e.state;
1397
+ }, moved, t);
1393
1398
  }
1394
1399
  for (let i = 0; i < acks.length; i++) {
1395
1400
  const a = acks[i];