@spooky-sync/core 0.0.1-canary.331 → 0.0.1-canary.332
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/AGENTS.md +1 -1
- package/dist/index.d.ts +19 -4
- package/dist/index.js +150 -40
- package/dist/otel/index.d.ts +1 -1
- package/dist/pure.d.ts +16 -0
- package/dist/types.d.ts +29 -11
- package/package.json +3 -3
- package/src/client/router.ts +1 -1
- package/src/client/services.ts +4 -3
- package/src/kernel/events.ts +2 -2
- package/src/kernel/interpreter.ts +3 -3
- package/src/modules/crdt/crdt.test.ts +52 -6
- package/src/modules/crdt/index.ts +59 -23
- package/src/query/membership.saga.test.ts +73 -1
- package/src/query/membership.saga.ts +39 -5
- package/src/query/membership.test.ts +33 -0
- package/src/query/membership.ts +33 -0
- package/src/sync/live.saga.test.ts +51 -0
- package/src/sync/live.saga.ts +17 -8
- package/src/types.ts +29 -10
- package/src/utils/edges.ts +19 -1
package/src/types.ts
CHANGED
|
@@ -258,14 +258,20 @@ export interface Sp00kyConfig<S extends SchemaStructure> {
|
|
|
258
258
|
*/
|
|
259
259
|
streamDebounceTime?: number;
|
|
260
260
|
/**
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
* Lower = snappier propagation and more rows; higher = fewer rows and more
|
|
266
|
-
* lag for other collaborators. Defaults to 300ms.
|
|
261
|
+
* How long in milliseconds the first edit to a collaborative (CRDT) field
|
|
262
|
+
* after a quiet spell waits before it goes out, so a burst of keystrokes
|
|
263
|
+
* travels as one edit-log row. Local state is kept on every change (so
|
|
264
|
+
* reload and offline work). Defaults to 50ms.
|
|
267
265
|
*/
|
|
268
266
|
crdtDebounceMs?: number;
|
|
267
|
+
/**
|
|
268
|
+
* The shortest gap in milliseconds between two edit-log rows of one CRDT
|
|
269
|
+
* field: while someone keeps typing, their edits go out on this cadence
|
|
270
|
+
* instead of waiting for a pause. Lower = other collaborators see the text
|
|
271
|
+
* sooner and more rows are written; higher = fewer rows and more lag.
|
|
272
|
+
* Defaults to 200ms.
|
|
273
|
+
*/
|
|
274
|
+
crdtSendIntervalMs?: number;
|
|
269
275
|
/**
|
|
270
276
|
* Enable collaborative CRDT fields. When `true`, the `loro-crdt` engine is
|
|
271
277
|
* preloaded at client startup (fetched as a separate chunk on page load) so
|
|
@@ -299,15 +305,16 @@ export interface Sp00kyConfig<S extends SchemaStructure> {
|
|
|
299
305
|
* back for it.
|
|
300
306
|
*
|
|
301
307
|
* The client holds exactly one live subscription, on its `_00_list_ref`
|
|
302
|
-
* table
|
|
303
|
-
*
|
|
308
|
+
* table. Without this, the notification names the row and the client then
|
|
309
|
+
* pulls it with `SELECT * FROM $ids`. The edge
|
|
304
310
|
* is a real graph edge, so `FETCH out` makes SurrealDB resolve the row into
|
|
305
311
|
* the notification and the body fetch disappears.
|
|
306
312
|
*
|
|
307
313
|
* The push stays advisory: the body is landed exactly the way a fetched body
|
|
308
314
|
* is (store write, circuit ingest, version recorded), so a notification the
|
|
309
|
-
* server drops costs nothing but the old round trip.
|
|
310
|
-
*
|
|
315
|
+
* server drops costs nothing but the old round trip. A row joining a live
|
|
316
|
+
* view is committed to membership from the notification either way (no
|
|
317
|
+
* re-read); with this on, the body fetch is saved as well.
|
|
311
318
|
*
|
|
312
319
|
* Default `false`. The cost is wire bytes: one edit bumps the edge in every
|
|
313
320
|
* view holding the row, and under `FETCH` each of those notifications
|
|
@@ -542,6 +549,18 @@ export interface InlineRow {
|
|
|
542
549
|
record: Record<string, unknown>;
|
|
543
550
|
}
|
|
544
551
|
|
|
552
|
+
/**
|
|
553
|
+
* A row that joined a query's view, or moved to a newer version, as one LIVE
|
|
554
|
+
* notification on `_00_list_ref` reports it: the edge's `out` and `version`.
|
|
555
|
+
* The client commits it to the query's membership straight away instead of
|
|
556
|
+
* re-reading the edges (see `sync/live.saga.ts`).
|
|
557
|
+
*/
|
|
558
|
+
export interface EdgeDelta {
|
|
559
|
+
hash: QueryHash;
|
|
560
|
+
id: string;
|
|
561
|
+
version: number;
|
|
562
|
+
}
|
|
563
|
+
|
|
545
564
|
// Flat array format: [[record-id, version], [record-id, version], ...]
|
|
546
565
|
export type RecordVersionArray = Array<[string, number]>;
|
|
547
566
|
|
package/src/utils/edges.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RecordId } from 'surrealdb';
|
|
2
|
-
import type { InlineRow } from '../types';
|
|
2
|
+
import type { EdgeDelta, InlineRow } from '../types';
|
|
3
3
|
import { encodeRecordId } from './index';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -37,3 +37,21 @@ export function rowOfEdge(value: unknown): InlineRow | null {
|
|
|
37
37
|
if (version === null) return null;
|
|
38
38
|
return { id, version, record: out as Record<string, unknown> };
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The membership change a `CREATE` or `UPDATE` notification reports: the
|
|
43
|
+
* edge's target joined the view (or its version moved). Null for anything a
|
|
44
|
+
* re-read must settle instead: a `DELETE` (a row leaving, or every edge of a
|
|
45
|
+
* view being rewritten), a subquery child (`parent` set), or an edge missing
|
|
46
|
+
* its target or version. With `FETCH out` the target is the joined row, whose
|
|
47
|
+
* `id` is the record id.
|
|
48
|
+
*/
|
|
49
|
+
export function deltaOfEdge(action: string, value: unknown): EdgeDelta | null {
|
|
50
|
+
if (action !== 'CREATE' && action !== 'UPDATE') return null;
|
|
51
|
+
const hash = hashOfEdge(value);
|
|
52
|
+
const edge = value as { out?: unknown; version?: unknown; parent?: unknown };
|
|
53
|
+
if (!hash || edge.parent != null || typeof edge.version !== 'number') return null;
|
|
54
|
+
const out = edge.out instanceof RecordId ? edge.out : (edge.out as { id?: unknown } | null | undefined)?.id;
|
|
55
|
+
if (!(out instanceof RecordId)) return null;
|
|
56
|
+
return { hash, id: encodeRecordId(out), version: edge.version };
|
|
57
|
+
}
|