@spooky-sync/core 0.0.1-canary.108 → 0.0.1-canary.109
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/dist/index.d.ts +10 -114
- package/dist/index.js +1070 -46
- package/dist/sqlite-worker.d.ts +1 -0
- package/dist/sqlite-worker.js +107 -0
- package/dist/types.d.ts +157 -2
- package/package.json +4 -3
- package/src/modules/cache/index.ts +2 -2
- package/src/modules/crdt/crdt-field.ts +3 -3
- package/src/modules/crdt/crdt-hydration.test.ts +5 -5
- package/src/modules/crdt/index.ts +2 -2
- package/src/modules/data/index.ts +45 -17
- package/src/modules/data/window-query.ts +24 -0
- package/src/modules/devtools/index.ts +2 -2
- package/src/modules/sync/queue/queue-down.ts +2 -2
- package/src/modules/sync/queue/queue-up.ts +2 -2
- package/src/modules/sync/sync.ts +2 -2
- package/src/services/database/cache-engine.ts +143 -0
- package/src/services/database/engine-factory.ts +32 -0
- package/src/services/database/index.ts +6 -0
- package/src/services/database/local-migrator.ts +2 -2
- package/src/services/database/plan-render.test.ts +85 -0
- package/src/services/database/plan-render.ts +86 -0
- package/src/services/database/relation-resolver.test.ts +413 -0
- package/src/services/database/relation-resolver.ts +0 -0
- package/src/services/database/sqlite-cache-engine.ts +631 -0
- package/src/services/database/sqlite-worker.ts +116 -0
- package/src/services/database/surql-translate.ts +291 -0
- package/src/services/database/surreal-cache-engine.ts +122 -0
- package/src/services/persistence/surrealdb.ts +2 -2
- package/src/services/stream-processor/index.ts +2 -2
- package/src/sp00ky.ts +24 -7
- package/src/types.ts +17 -1
- package/tsdown.config.ts +10 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,35 +1,10 @@
|
|
|
1
|
-
import { A as SyncHealthStatus, C as RunOptions, D as StoreType, E as Sp00kyQueryResultPromise, F as
|
|
1
|
+
import { A as SyncHealthStatus, B as EventDefinition, C as RunOptions, D as StoreType, E as Sp00kyQueryResultPromise, F as SealedQuery, I as DatabaseEventSystem, L as DatabaseEventTypes, M as UpdateOptions, N as UpEvent, O as SyncHealth, P as LocalStore, R as Logger$1, S as RegistrationTimings, T as Sp00kyQueryResult, V as EventSystem, _ as QueryTimeToLive, a as MutationCallback, b as RecordVersionArray, c as PersistenceClient, d as QueryConfig, f as QueryConfigRecord, g as QueryStatusCallback, h as QueryStatus, i as MATERIALIZATION_SAMPLE_WINDOW, j as TimingPhase, k as SyncHealthConfig, l as PhaseStat, m as QueryState, n as EventSubscriptionOptions, o as MutationEvent, p as QueryHash, r as Level, s as MutationEventType, t as DebounceOptions, u as PinoTransmit, v as QueryTimings, w as Sp00kyConfig, x as RecordVersionDiff, y as QueryUpdateCallback, z as SyncEventSystem } from "./types.js";
|
|
2
2
|
import * as surrealdb0 from "surrealdb";
|
|
3
3
|
import { Duration, RecordId, Surreal as Surreal$1, SurrealTransaction } from "surrealdb";
|
|
4
|
-
import { AccessDefinition, BackendNames, BackendRoutes, BucketNames, ColumnSchema, GetTable, QueryBuilder, QueryOptions, RoutePayload, SchemaStructure, TableModel, TableNames, TypeNameToTypeMap } from "@spooky-sync/query-builder";
|
|
4
|
+
import { AccessDefinition, BackendNames, BackendRoutes, BucketNames, ColumnSchema, GetTable, QueryBuilder, QueryOptions, QueryPlan, RoutePayload, SchemaStructure, TableModel, TableNames, TypeNameToTypeMap } from "@spooky-sync/query-builder";
|
|
5
5
|
import { Logger } from "pino";
|
|
6
6
|
import { LoroDoc } from "loro-crdt";
|
|
7
7
|
|
|
8
|
-
//#region src/services/database/events/index.d.ts
|
|
9
|
-
declare const DatabaseEventTypes: {
|
|
10
|
-
readonly LocalQuery: "DATABASE_LOCAL_QUERY";
|
|
11
|
-
readonly RemoteQuery: "DATABASE_REMOTE_QUERY";
|
|
12
|
-
};
|
|
13
|
-
interface DatabaseQueryEventPayload {
|
|
14
|
-
query: string;
|
|
15
|
-
vars?: Record<string, unknown>;
|
|
16
|
-
duration: number;
|
|
17
|
-
success: boolean;
|
|
18
|
-
error?: string;
|
|
19
|
-
timestamp: number;
|
|
20
|
-
}
|
|
21
|
-
type DatabaseEventTypeMap = {
|
|
22
|
-
[DatabaseEventTypes.LocalQuery]: EventDefinition<typeof DatabaseEventTypes.LocalQuery, DatabaseQueryEventPayload>;
|
|
23
|
-
[DatabaseEventTypes.RemoteQuery]: EventDefinition<typeof DatabaseEventTypes.RemoteQuery, DatabaseQueryEventPayload>;
|
|
24
|
-
};
|
|
25
|
-
type DatabaseEventSystem = EventSystem<DatabaseEventTypeMap>;
|
|
26
|
-
//#endregion
|
|
27
|
-
//#region src/utils/surql.d.ts
|
|
28
|
-
interface SealedQuery<T = void> {
|
|
29
|
-
readonly sql: string;
|
|
30
|
-
readonly extract: (results: unknown[]) => T;
|
|
31
|
-
}
|
|
32
|
-
//#endregion
|
|
33
8
|
//#region src/services/database/database.d.ts
|
|
34
9
|
declare abstract class AbstractDatabaseService {
|
|
35
10
|
protected client: Surreal$1;
|
|
@@ -50,85 +25,6 @@ declare abstract class AbstractDatabaseService {
|
|
|
50
25
|
close(): Promise<void>;
|
|
51
26
|
}
|
|
52
27
|
//#endregion
|
|
53
|
-
//#region src/services/database/local.d.ts
|
|
54
|
-
declare class LocalDatabaseService extends AbstractDatabaseService {
|
|
55
|
-
private config;
|
|
56
|
-
protected eventType: "DATABASE_LOCAL_QUERY";
|
|
57
|
-
/** Bucket currently open. Set by `connect`/`switchStore`. */
|
|
58
|
-
private bucketId;
|
|
59
|
-
/**
|
|
60
|
-
* Monotonic store generation. Bumped on every `switchStore`. Async chains
|
|
61
|
-
* that read from the store, await something remote, and then write back
|
|
62
|
-
* (sync poll, SSP stream updates) capture this at chain start and drop
|
|
63
|
-
* their write when it no longer matches — a stale-epoch write would land
|
|
64
|
-
* another user's data in the new bucket.
|
|
65
|
-
*/
|
|
66
|
-
private storeEpoch;
|
|
67
|
-
/** Gate that `query()`/`execute()` await; closed for the switch window. */
|
|
68
|
-
private gate;
|
|
69
|
-
/** The incoming client while a switch is in flight (for unload cleanup). */
|
|
70
|
-
private pendingSwitchClient;
|
|
71
|
-
constructor(config: Sp00kyConfig<any>['database'], logger: Logger$1);
|
|
72
|
-
getConfig(): Sp00kyConfig<any>['database'];
|
|
73
|
-
get currentBucketId(): string;
|
|
74
|
-
get epoch(): number;
|
|
75
|
-
/**
|
|
76
|
-
* Close the query gate for a bucket switch. Every `query()`/`execute()`
|
|
77
|
-
* issued after this waits until the returned release fn runs — so work
|
|
78
|
-
* triggered mid-switch (sibling auth subscribers registering queries)
|
|
79
|
-
* lands on the NEW bucket instead of racing the swap. The migrator uses
|
|
80
|
-
* `queryUngated()` to provision the new bucket while the gate is closed.
|
|
81
|
-
*/
|
|
82
|
-
beginSwitch(): () => void;
|
|
83
|
-
query<T extends unknown[]>(query: string, vars?: Record<string, unknown>, opts?: {
|
|
84
|
-
epoch?: number;
|
|
85
|
-
}): Promise<T>;
|
|
86
|
-
execute<T>(query: SealedQuery<T>, vars?: Record<string, unknown>, opts?: {
|
|
87
|
-
epoch?: number;
|
|
88
|
-
}): Promise<T>;
|
|
89
|
-
/** Gate-bypassing query — ONLY for the switch path itself (schema
|
|
90
|
-
* provisioning must run while the gate is closed, or it deadlocks). */
|
|
91
|
-
queryUngated<T extends unknown[]>(query: string, vars?: Record<string, unknown>): Promise<T>;
|
|
92
|
-
connect(bucketId?: string): Promise<void>;
|
|
93
|
-
/**
|
|
94
|
-
* Switch the local store to another user's bucket. Opens the NEW bucket on a
|
|
95
|
-
* second client first (with the same 3-tier recovery), then atomically swaps
|
|
96
|
-
* `this.client` and closes the old one — a failed open never leaves the
|
|
97
|
-
* service on a dead client. Bumps the store epoch so in-flight old-bucket
|
|
98
|
-
* async chains can detect they're stale.
|
|
99
|
-
*
|
|
100
|
-
* Callers own the drain/rebind choreography (close the gate, quiesce sync +
|
|
101
|
-
* timers BEFORE calling this; re-provision + rebind AFTER).
|
|
102
|
-
*/
|
|
103
|
-
switchStore(bucketId: string): Promise<void>;
|
|
104
|
-
/**
|
|
105
|
-
* Open `storeUrl` on `client` with tiered recovery:
|
|
106
|
-
* tier 1 retries the same store (transient idb-handle races — preserves the
|
|
107
|
-
* cache), tier 2 drops THIS bucket's IndexedDB store and reconnects fresh,
|
|
108
|
-
* tier 3 falls back to `mem://` for the session. Only ever drops the bucket
|
|
109
|
-
* being opened — other users' buckets hold their own caches AND un-pushed
|
|
110
|
-
* mutation outboxes, which must survive another bucket's corruption.
|
|
111
|
-
*/
|
|
112
|
-
private openWithRecovery;
|
|
113
|
-
private unloadCloseRegistered;
|
|
114
|
-
/**
|
|
115
|
-
* Close the local DB on page unload so the SurrealDB-WASM worker releases its
|
|
116
|
-
* IndexedDB connection cleanly. Without this, the previous page's connection
|
|
117
|
-
* lingers; the next load's `client.connect` opens the store but the first
|
|
118
|
-
* write transaction in `client.use` hits an "IndexedDB error" — which then
|
|
119
|
-
* (mis)triggered the corrupt-store recovery and WIPED the cache on every
|
|
120
|
-
* reload, making warm loads as slow as cold ones. `pagehide` is the reliable
|
|
121
|
-
* unload signal (fires on bfcache + normal navigation); `close()` is async but
|
|
122
|
-
* the WASM worker initiates the IndexedDB connection teardown synchronously.
|
|
123
|
-
* Also closes a mid-switch incoming client so its fresh handle doesn't linger.
|
|
124
|
-
*/
|
|
125
|
-
private registerUnloadClose;
|
|
126
|
-
private openStore;
|
|
127
|
-
}
|
|
128
|
-
/** True for the SurrealDB-WASM error raised when its IndexedDB-backed key-value
|
|
129
|
-
* store can't be opened (corrupt / version-incompatible / blocked). Exported
|
|
130
|
-
* for unit testing the error-message match. */
|
|
131
|
-
//#endregion
|
|
132
28
|
//#region src/services/database/remote.d.ts
|
|
133
29
|
declare class RemoteDatabaseService extends AbstractDatabaseService {
|
|
134
30
|
private config;
|
|
@@ -243,7 +139,7 @@ declare class StreamProcessorService {
|
|
|
243
139
|
private sessionAuth;
|
|
244
140
|
private stateKeySuffix;
|
|
245
141
|
private stateGeneration;
|
|
246
|
-
constructor(events: EventSystem<StreamProcessorEvents>, db:
|
|
142
|
+
constructor(events: EventSystem<StreamProcessorEvents>, db: LocalStore, persistenceClient: PersistenceClient, logger: Logger);
|
|
247
143
|
/**
|
|
248
144
|
* Add a receiver for stream updates.
|
|
249
145
|
* Multiple receivers can be registered (DataManager, DevTools, etc.)
|
|
@@ -371,7 +267,7 @@ declare class CacheModule implements StreamUpdateReceiver {
|
|
|
371
267
|
private logger;
|
|
372
268
|
private streamUpdateCallback;
|
|
373
269
|
private versionLookups;
|
|
374
|
-
constructor(local:
|
|
270
|
+
constructor(local: LocalStore, streamProcessor: StreamProcessorService, streamUpdateCallback: (update: StreamUpdate) => void, logger: Logger$1);
|
|
375
271
|
/**
|
|
376
272
|
* Implements StreamUpdateReceiver interface
|
|
377
273
|
* Called directly by StreamProcessor when views change
|
|
@@ -463,7 +359,7 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
463
359
|
onDeregister?: (hash: QueryHash) => void;
|
|
464
360
|
private sessionId;
|
|
465
361
|
private currentUserId;
|
|
466
|
-
constructor(cache: CacheModule, local:
|
|
362
|
+
constructor(cache: CacheModule, local: LocalStore, schema: S, logger: Logger$1, streamDebounceTime?: number);
|
|
467
363
|
init(sessionId: string): Promise<void>;
|
|
468
364
|
/**
|
|
469
365
|
* Update the session salt used in query-id hashing. Call this when the
|
|
@@ -485,7 +381,7 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
485
381
|
/**
|
|
486
382
|
* Register a query and return its hash for subscriptions
|
|
487
383
|
*/
|
|
488
|
-
query<T extends TableNames<S>>(tableName: T, surqlString: string, params: Record<string, any>, ttl: QueryTimeToLive): Promise<QueryHash>;
|
|
384
|
+
query<T extends TableNames<S>>(tableName: T, surqlString: string, params: Record<string, any>, ttl: QueryTimeToLive, plan?: QueryPlan): Promise<QueryHash>;
|
|
489
385
|
/**
|
|
490
386
|
* Subscribe to query updates
|
|
491
387
|
*/
|
|
@@ -781,7 +677,7 @@ declare class Sp00kySync<S extends SchemaStructure> {
|
|
|
781
677
|
private startSelfHeal;
|
|
782
678
|
private scheduleSelfHeal;
|
|
783
679
|
private stopSelfHeal;
|
|
784
|
-
constructor(local:
|
|
680
|
+
constructor(local: LocalStore, remote: RemoteDatabaseService, cache: CacheModule, dataModule: DataModule<S>, schema: S, logger: Logger$1, options?: Sp00kySyncOptions);
|
|
785
681
|
/**
|
|
786
682
|
* Initializes the synchronization system.
|
|
787
683
|
* Starts the scheduler and initiates the initial sync cycles.
|
|
@@ -1026,7 +922,7 @@ declare class CrdtField {
|
|
|
1026
922
|
getDoc(): LoroDoc;
|
|
1027
923
|
/** Whether the LoroDoc was loaded from saved CRDT state */
|
|
1028
924
|
hasContent(): boolean;
|
|
1029
|
-
startSync(local:
|
|
925
|
+
startSync(local: LocalStore, remote: RemoteDatabaseService, recordId: string, sessionId: string, debounceMs: number): void;
|
|
1030
926
|
/**
|
|
1031
927
|
* Stop syncing this field. Flushes one final remote push by default so the
|
|
1032
928
|
* last keystrokes aren't lost. Pass `{ flush: false }` on a bucket switch —
|
|
@@ -1093,7 +989,7 @@ declare class CrdtManager {
|
|
|
1093
989
|
private pendingLive;
|
|
1094
990
|
private logger;
|
|
1095
991
|
private sessionId;
|
|
1096
|
-
constructor(schema: SchemaStructure, local:
|
|
992
|
+
constructor(schema: SchemaStructure, local: LocalStore, remote: RemoteDatabaseService, logger: Logger$1, debounceMs?: number);
|
|
1097
993
|
/** Set the session id that scopes this client's cursor entries. Must be
|
|
1098
994
|
* called before `open()` for cursors to be pushed under a stable key.
|
|
1099
995
|
* Passed in from `sp00ky.ts` at boot (it already fetches `session::id()`
|
|
@@ -1241,7 +1137,7 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
1241
1137
|
auth: AuthService<S>;
|
|
1242
1138
|
streamProcessor: StreamProcessorService;
|
|
1243
1139
|
get remoteClient(): surrealdb0.Surreal;
|
|
1244
|
-
get localClient():
|
|
1140
|
+
get localClient(): unknown;
|
|
1245
1141
|
get pendingMutationCount(): number;
|
|
1246
1142
|
/** Number of times the initial list_ref LIVE subscription retried on
|
|
1247
1143
|
* the most recent `setCurrentUserId` call. 0 when the SSP's
|