svelte-realtime 0.1.5 → 0.1.7

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.
Files changed (3) hide show
  1. package/README.md +10 -2
  2. package/client.js +50 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -647,7 +647,7 @@ const [board, column] = await batch(() => [
647
647
  ], { sequential: true });
648
648
  ```
649
649
 
650
- Each call resolves or rejects independently -- one failure does not cancel the others. Batches are limited to 50 calls per frame.
650
+ Each call resolves or rejects independently -- one failure does not cancel the others. Batches are limited to 50 calls -- enforced both client-side (rejects before sending) and server-side.
651
651
 
652
652
  ---
653
653
 
@@ -1511,7 +1511,7 @@ The adapter's `sendQueued()` drops the oldest item if the queue exceeds 1000. Un
1511
1511
 
1512
1512
  ### Batch size (max 50 calls)
1513
1513
 
1514
- A single `batch()` call is limited to 50 RPC calls. If exceeded, the server rejects the entire batch. Split into multiple `batch()` calls if you need more.
1514
+ A single `batch()` call is limited to 50 RPC calls. The client rejects before sending if the limit is exceeded, and the server enforces the same limit as a safety net. Split into multiple `batch()` calls if you need more.
1515
1515
 
1516
1516
  ### ws.subscribe() vs the subscribe hook
1517
1517
 
@@ -1773,6 +1773,14 @@ What gets measured:
1773
1773
 
1774
1774
  Merge strategies use an internal `Map<key, index>` for O(1) lookups instead of linear scans. Updates and upserts on keyed strategies (crud, presence, cursor) are constant-time regardless of array size. Deletes and prepends require an index rebuild (linear), which matches the cost of the delete itself.
1775
1775
 
1776
+ ### Event batching (browser)
1777
+
1778
+ In the browser, incoming pub/sub events are queued and flushed once per `requestAnimationFrame` instead of triggering a Svelte store update per event. This is automatic -- no configuration needed.
1779
+
1780
+ With high-frequency streams (e.g. 1000 cursors at 20 updates/sec), this reduces reactive store updates from ~20,000/sec to ~60/sec (one per frame). All merge operations still run, but Svelte only diffs and re-renders once per frame.
1781
+
1782
+ In Node/SSR (tests, `__directCall`, etc.), events apply synchronously -- no batching overhead.
1783
+
1776
1784
  These benchmarks run in-process with mock objects (no real network). They isolate the framework overhead from network latency. See [bench/rpc.js](bench/rpc.js) for the full source.
1777
1785
 
1778
1786
  ---
package/client.js CHANGED
@@ -4,6 +4,14 @@ import { writable } from 'svelte/store';
4
4
 
5
5
  const _textEncoder = new TextEncoder();
6
6
 
7
+ /**
8
+ * RAF-based event batching for high-frequency streams (cursors, presence).
9
+ * In the browser, incoming pub/sub events are queued and flushed once per
10
+ * animation frame, reducing Svelte reactive updates from N-per-event to
11
+ * 1-per-frame. In Node/SSR, events apply synchronously (no DOM to protect).
12
+ */
13
+ const _useRAF = typeof window !== 'undefined' && typeof requestAnimationFrame === 'function';
14
+
7
15
  /**
8
16
  * Typed error for RPC failures.
9
17
  */
@@ -535,18 +543,49 @@ function _createStream(path, options, dynamicArgs) {
535
543
  return false;
536
544
  }
537
545
 
546
+ /** @type {Array<{ event: string, data: any }>} Queued events waiting for next animation frame */
547
+ let _eventQueue = [];
548
+
549
+ /** @type {number | null} */
550
+ let _rafId = null;
551
+
538
552
  /**
539
- * Apply a single pub/sub event to the current value using the merge strategy.
540
- * Creates a new array reference for Svelte reactivity only when needed.
541
- * @param {{ event: string, data: any }} envelope
553
+ * Flush all queued events in a single batch, then update the store once.
554
+ * Reduces reactive updates from N-per-event to 1-per-frame.
542
555
  */
543
- function applyEvent(envelope) {
544
- const replaced = _applyMerge(envelope);
545
- if (!replaced && Array.isArray(currentValue)) currentValue = currentValue.slice();
556
+ function _flushEvents() {
557
+ _rafId = null;
558
+ if (_eventQueue.length === 0) return;
559
+ const queue = _eventQueue;
560
+ _eventQueue = [];
561
+ for (let i = 0; i < queue.length; i++) {
562
+ _applyMerge(queue[i]);
563
+ }
564
+ if (Array.isArray(currentValue)) currentValue = currentValue.slice();
546
565
  store.set(currentValue);
547
566
  _recordHistory();
548
567
  }
549
568
 
569
+ /**
570
+ * Apply a pub/sub event to the store. In the browser, events are queued
571
+ * and flushed once per animation frame to reduce reactive updates from
572
+ * N-per-event to 1-per-frame. In Node/SSR, events apply immediately.
573
+ * @param {{ event: string, data: any }} envelope
574
+ */
575
+ function applyEvent(envelope) {
576
+ if (_useRAF) {
577
+ _eventQueue.push(envelope);
578
+ if (_rafId === null) {
579
+ _rafId = requestAnimationFrame(_flushEvents);
580
+ }
581
+ } else {
582
+ const replaced = _applyMerge(envelope);
583
+ if (!replaced && Array.isArray(currentValue)) currentValue = currentValue.slice();
584
+ store.set(currentValue);
585
+ _recordHistory();
586
+ }
587
+ }
588
+
550
589
  /**
551
590
  * Fetch initial data and subscribe to live updates.
552
591
  */
@@ -716,6 +755,11 @@ function _createStream(path, options, dynamicArgs) {
716
755
  clearTimeout(_reconnectTimer);
717
756
  _reconnectTimer = null;
718
757
  }
758
+ if (_rafId !== null) {
759
+ cancelAnimationFrame(_rafId);
760
+ _rafId = null;
761
+ }
762
+ _eventQueue = [];
719
763
  topic = null;
720
764
  initialLoaded = false;
721
765
  fetching = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-realtime",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Realtime RPC and reactive subscriptions for SvelteKit, built on svelte-adapter-uws",
5
5
  "author": "Kevin Radziszewski",
6
6
  "license": "MIT",