svelte-realtime 0.1.6 → 0.1.8
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/README.md +25 -2
- package/package.json +1 -1
- package/server.d.ts +37 -1
- package/server.js +13 -3
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
|
|
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
|
|
|
@@ -1025,6 +1025,21 @@ export const stats = live.stream('stats', async (ctx) => {
|
|
|
1025
1025
|
}, { merge: 'set' });
|
|
1026
1026
|
```
|
|
1027
1027
|
|
|
1028
|
+
The function receives a `ctx` object with `publish`, `throttle`, `debounce`, and `signal` -- the same helpers available in RPC handlers (minus `user` and `ws`, since cron runs outside a connection). Use `ctx.publish` for fine-grained control, e.g. publishing individual `created`/`deleted` events on a crud stream:
|
|
1029
|
+
|
|
1030
|
+
```js
|
|
1031
|
+
export const cleanup = live.cron('0 * * * *', 'boards', async (ctx) => {
|
|
1032
|
+
const stale = await listStaleBoards();
|
|
1033
|
+
for (const board of stale) {
|
|
1034
|
+
await deleteBoard(board.board_id);
|
|
1035
|
+
ctx.publish('boards', 'deleted', { board_id: board.board_id });
|
|
1036
|
+
}
|
|
1037
|
+
// returning undefined skips the automatic 'set' publish
|
|
1038
|
+
});
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
If the function returns a value, it is published as a `set` event (same as before). If it returns `undefined`, no automatic publish happens -- this lets you use `ctx.publish` exclusively without an unwanted `set` event overwriting your crud updates.
|
|
1042
|
+
|
|
1028
1043
|
Cron expressions use 5 fields: `minute hour day month weekday`. Supported syntax: `*`, single values, ranges (`9-17`), lists (`0,15,30`), and steps (`*/5`).
|
|
1029
1044
|
|
|
1030
1045
|
The platform is captured automatically from the first RPC call. If your app starts cron jobs before any WebSocket connections, call `setCronPlatform(platform)` in your `open` hook.
|
|
@@ -1511,7 +1526,7 @@ The adapter's `sendQueued()` drops the oldest item if the queue exceeds 1000. Un
|
|
|
1511
1526
|
|
|
1512
1527
|
### Batch size (max 50 calls)
|
|
1513
1528
|
|
|
1514
|
-
A single `batch()` call is limited to 50 RPC calls.
|
|
1529
|
+
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
1530
|
|
|
1516
1531
|
### ws.subscribe() vs the subscribe hook
|
|
1517
1532
|
|
|
@@ -1773,6 +1788,14 @@ What gets measured:
|
|
|
1773
1788
|
|
|
1774
1789
|
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
1790
|
|
|
1791
|
+
### Event batching (browser)
|
|
1792
|
+
|
|
1793
|
+
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.
|
|
1794
|
+
|
|
1795
|
+
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.
|
|
1796
|
+
|
|
1797
|
+
In Node/SSR (tests, `__directCall`, etc.), events apply synchronously -- no batching overhead.
|
|
1798
|
+
|
|
1776
1799
|
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
1800
|
|
|
1778
1801
|
---
|
package/package.json
CHANGED
package/server.d.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import type { Platform, WebSocket } from 'svelte-adapter-uws';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Context passed to `live.cron()` functions.
|
|
5
|
+
* No `user` or `ws` since cron jobs run outside a connection.
|
|
6
|
+
*/
|
|
7
|
+
export interface CronContext {
|
|
8
|
+
/** The platform API (publish, send, topic helpers). */
|
|
9
|
+
platform: Platform;
|
|
10
|
+
/** Shorthand for `platform.publish` -- delegates to whatever platform was passed in. */
|
|
11
|
+
publish: Platform['publish'];
|
|
12
|
+
/** Throttled publish -- sends at most once per `ms` milliseconds. */
|
|
13
|
+
throttle(topic: string, event: string, data: any, ms: number): void;
|
|
14
|
+
/** Debounced publish -- sends after `ms` milliseconds of silence. */
|
|
15
|
+
debounce(topic: string, event: string, data: any, ms: number): void;
|
|
16
|
+
/** Send a point-to-point signal to a specific user. */
|
|
17
|
+
signal(userId: string, event: string, data: any): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
3
20
|
/**
|
|
4
21
|
* Context passed to every `live()` and `live.stream()` function.
|
|
5
22
|
*/
|
|
@@ -372,18 +389,32 @@ export namespace live {
|
|
|
372
389
|
/**
|
|
373
390
|
* Create a server-side scheduled function that publishes to a topic on a cron schedule.
|
|
374
391
|
*
|
|
392
|
+
* The function receives a `ctx` object with `publish`, `throttle`, `debounce`, and `signal`.
|
|
393
|
+
* If the function returns a value, it is published as a `set` event on the topic.
|
|
394
|
+
* If the function returns `undefined`, no automatic publish happens (use `ctx.publish` instead).
|
|
395
|
+
*
|
|
375
396
|
* @param schedule - Cron expression (5 fields: minute hour day month weekday)
|
|
376
397
|
* @param topic - Topic to publish results to
|
|
377
398
|
* @param fn - Async function to run on schedule
|
|
378
399
|
*
|
|
379
400
|
* @example
|
|
380
401
|
* ```js
|
|
402
|
+
* // Return a value -- published as 'set' automatically
|
|
381
403
|
* export const refreshStats = live.cron('*\/5 * * * *', 'stats', async () => {
|
|
382
404
|
* return db.stats();
|
|
383
405
|
* });
|
|
406
|
+
*
|
|
407
|
+
* // Use ctx.publish for fine-grained control (e.g. crud streams)
|
|
408
|
+
* export const cleanup = live.cron('0 * * * *', 'boards', async (ctx) => {
|
|
409
|
+
* const stale = await listStaleBoards();
|
|
410
|
+
* for (const board of stale) {
|
|
411
|
+
* await deleteBoard(board.board_id);
|
|
412
|
+
* ctx.publish('boards', 'deleted', { board_id: board.board_id });
|
|
413
|
+
* }
|
|
414
|
+
* });
|
|
384
415
|
* ```
|
|
385
416
|
*/
|
|
386
|
-
function cron<T extends () => any>(
|
|
417
|
+
function cron<T extends ((ctx: CronContext) => any) | (() => any)>(
|
|
387
418
|
schedule: string,
|
|
388
419
|
topic: string,
|
|
389
420
|
fn: T
|
|
@@ -773,6 +804,11 @@ export function _activateDerived(platform: Platform): void;
|
|
|
773
804
|
*/
|
|
774
805
|
export function _clearCron(): void;
|
|
775
806
|
|
|
807
|
+
/**
|
|
808
|
+
* Run all matching cron jobs for the current minute. Exported for testing.
|
|
809
|
+
*/
|
|
810
|
+
export function _tickCron(): Promise<void>;
|
|
811
|
+
|
|
776
812
|
/**
|
|
777
813
|
* Set a global error handler for cron job failures.
|
|
778
814
|
* Without this, cron errors are logged in dev and silently swallowed in production.
|
package/server.js
CHANGED
|
@@ -1270,7 +1270,7 @@ export function _restoreHmr(snap) {
|
|
|
1270
1270
|
}
|
|
1271
1271
|
}
|
|
1272
1272
|
|
|
1273
|
-
async function _tickCron() {
|
|
1273
|
+
export async function _tickCron() {
|
|
1274
1274
|
if (_lazyQueue.length) await _resolveAllLazy();
|
|
1275
1275
|
const now = new Date();
|
|
1276
1276
|
const minute = now.getMinutes();
|
|
@@ -1296,8 +1296,18 @@ async function _tickCron() {
|
|
|
1296
1296
|
}
|
|
1297
1297
|
return;
|
|
1298
1298
|
}
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1299
|
+
const _h = _getCtxHelpers(_cronPlatform);
|
|
1300
|
+
const ctx = {
|
|
1301
|
+
platform: _cronPlatform,
|
|
1302
|
+
publish: _h.publish,
|
|
1303
|
+
throttle: _h.throttle,
|
|
1304
|
+
debounce: _h.debounce,
|
|
1305
|
+
signal: _h.signal
|
|
1306
|
+
};
|
|
1307
|
+
const result = await entry.fn(ctx);
|
|
1308
|
+
if (result !== undefined) {
|
|
1309
|
+
_cronPlatform.publish(entry.topic, 'set', result);
|
|
1310
|
+
}
|
|
1301
1311
|
} catch (err) {
|
|
1302
1312
|
if (_cronErrorHandler) {
|
|
1303
1313
|
_cronErrorHandler(path, err);
|