@spooky-sync/core 0.0.1-canary.202 → 0.0.1-canary.204
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 +89 -6
- package/dist/index.js +353 -65
- package/dist/sqlite-open.js +28 -1
- package/dist/sqlite-worker.js +11 -8
- package/dist/types.d.ts +6 -0
- package/package.json +3 -3
- package/src/modules/auth/auth.local-first.test.ts +101 -0
- package/src/modules/auth/index.ts +78 -14
- package/src/modules/cache/index.ts +12 -11
- package/src/modules/data/data.pending-ids.test.ts +125 -0
- package/src/modules/data/index.ts +64 -0
- package/src/modules/sync/queue/queue-down.test.ts +73 -0
- package/src/modules/sync/queue/queue-down.ts +59 -21
- package/src/modules/sync/scheduler.retry.test.ts +82 -1
- package/src/modules/sync/scheduler.ts +59 -4
- package/src/modules/sync/sync.ts +36 -0
- package/src/services/database/sqlite-cache-engine.ts +13 -8
- package/src/services/database/sqlite-plan-sql.ts +32 -0
- package/src/services/database/sqlite-projection.test.ts +99 -0
- package/src/services/database/sqlite-select.ts +19 -9
- package/src/services/stream-processor/index.ts +66 -0
- package/src/services/stream-processor/stream-processor.batch.test.ts +60 -0
- package/src/services/stream-processor/wasm-types.ts +4 -0
- package/src/sp00ky.local-first.test.ts +60 -0
- package/src/sp00ky.ts +156 -50
- package/src/types.ts +6 -0
package/dist/index.d.ts
CHANGED
|
@@ -696,6 +696,13 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
696
696
|
private resolveMembership;
|
|
697
697
|
private readonly settledWrites;
|
|
698
698
|
private readonly settledDeletes;
|
|
699
|
+
private pendingIds;
|
|
700
|
+
private pendingIdsAt;
|
|
701
|
+
private pendingIdsInflight;
|
|
702
|
+
private static readonly PENDING_IDS_TTL_MS;
|
|
703
|
+
/** Drop the cached outbox ids. Cheap; call it on anything that could change
|
|
704
|
+
* `_00_pending_mutations`. */
|
|
705
|
+
private invalidatePendingIds;
|
|
699
706
|
/**
|
|
700
707
|
* Grace period for a settled write. Long enough to cover an SSP round trip
|
|
701
708
|
* that is running slowly (seconds, not milliseconds, when the edge path is
|
|
@@ -841,6 +848,9 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
841
848
|
writes: Set<string>;
|
|
842
849
|
deletes: Set<string>;
|
|
843
850
|
}>;
|
|
851
|
+
/** The uncached read. Also the reload path after an invalidation, so the ids
|
|
852
|
+
* still survive a reload exactly as before. */
|
|
853
|
+
private readPendingRecordIds;
|
|
844
854
|
/** True while ≥1 live subscriber is watching this query (refcount guard). */
|
|
845
855
|
hasSubscribers(hash: string): boolean;
|
|
846
856
|
/**
|
|
@@ -1079,6 +1089,15 @@ interface Sp00kySyncOptions {
|
|
|
1079
1089
|
* up-queue for the session. Defaults to 30000; `0` disables the timeout.
|
|
1080
1090
|
*/
|
|
1081
1091
|
pushTimeoutMs?: number;
|
|
1092
|
+
/**
|
|
1093
|
+
* Max time a single down event (`register`/`sync`/`cleanup`) may take before
|
|
1094
|
+
* it is treated as a network failure and retried. The mirror of
|
|
1095
|
+
* {@link pushTimeoutMs} for the read side, which had no such guard: a
|
|
1096
|
+
* `fn::query::register` that never settled held its slot in the down drain,
|
|
1097
|
+
* and every later registration behind it, for the rest of the session.
|
|
1098
|
+
* Defaults to 30000; `0` disables the timeout.
|
|
1099
|
+
*/
|
|
1100
|
+
downTimeoutMs?: number;
|
|
1082
1101
|
/**
|
|
1083
1102
|
* Transport supervisor. Sync reads its state to report `connection` in
|
|
1084
1103
|
* {@link SyncHealth} so a UI can show "reconnecting…" the instant the socket
|
|
@@ -1148,6 +1167,7 @@ declare class Sp00kySync<S extends SchemaStructure> {
|
|
|
1148
1167
|
private readonly degradeAfterFailures;
|
|
1149
1168
|
/** Per-push RPC deadline; see {@link withPushTimeout}. */
|
|
1150
1169
|
private readonly pushTimeoutMs;
|
|
1170
|
+
private readonly downTimeoutMs;
|
|
1151
1171
|
private consecutiveSyncFailures;
|
|
1152
1172
|
private syncHealthStatus;
|
|
1153
1173
|
private lastSyncErrorKind;
|
|
@@ -1378,6 +1398,8 @@ declare class Sp00kySync<S extends SchemaStructure> {
|
|
|
1378
1398
|
private handleMutationSettled;
|
|
1379
1399
|
private handleRollback;
|
|
1380
1400
|
private processDownEvent;
|
|
1401
|
+
private withDownTimeout;
|
|
1402
|
+
private runDownEvent;
|
|
1381
1403
|
/**
|
|
1382
1404
|
* Synchronizes a specific query by hash.
|
|
1383
1405
|
* Compares local and remote version arrays and fetches differences.
|
|
@@ -1471,6 +1493,24 @@ declare class AuthService<S extends SchemaStructure> {
|
|
|
1471
1493
|
*/
|
|
1472
1494
|
subscribe(cb: (userId: string | null) => void): () => void;
|
|
1473
1495
|
private notifyListeners;
|
|
1496
|
+
/**
|
|
1497
|
+
* Restore a session from the locally cached JWT, with NO network.
|
|
1498
|
+
*
|
|
1499
|
+
* This is what makes a warm boot paint instantly and what makes an offline
|
|
1500
|
+
* boot possible at all: the token is in local storage, and it already carries
|
|
1501
|
+
* both the access method and the `$auth.id` record id. Everything the client
|
|
1502
|
+
* needs to route queries (`setCurrentUserId`) and to satisfy `$auth`-gated
|
|
1503
|
+
* permission predicates in the in-browser SSP (`setSessionAuth`) is therefore
|
|
1504
|
+
* available before a socket exists.
|
|
1505
|
+
*
|
|
1506
|
+
* The session is OPTIMISTIC: the token is unverified here. `check()` runs
|
|
1507
|
+
* afterwards in the background and downgrades to a real sign-out if the
|
|
1508
|
+
* server rejects it. Nothing is trusted that the server has not also seen -
|
|
1509
|
+
* the local store only ever holds rows the server previously sent.
|
|
1510
|
+
*
|
|
1511
|
+
* Returns the restored user id, or null when there is no usable token.
|
|
1512
|
+
*/
|
|
1513
|
+
restoreSessionFromToken(): Promise<string | null>;
|
|
1474
1514
|
/**
|
|
1475
1515
|
* Check for existing session and validate
|
|
1476
1516
|
*/
|
|
@@ -2166,6 +2206,15 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
2166
2206
|
private sync;
|
|
2167
2207
|
private devTools;
|
|
2168
2208
|
private crdtManager;
|
|
2209
|
+
/**
|
|
2210
|
+
* True once the LOCAL half of boot is done and the client can serve reads
|
|
2211
|
+
* from the local store. Distinct from being connected: `syncHealth` covers
|
|
2212
|
+
* reaching the server and `storageHealth` covers whether the local store is
|
|
2213
|
+
* durable, but neither says "usable". Consumers gate their first paint on
|
|
2214
|
+
* this, which is what makes a warm boot instant and an offline boot possible.
|
|
2215
|
+
*/
|
|
2216
|
+
private localReady;
|
|
2217
|
+
private saltUserId;
|
|
2169
2218
|
private featureFlags;
|
|
2170
2219
|
private appReleases;
|
|
2171
2220
|
private preloadedHashes;
|
|
@@ -2211,6 +2260,16 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
2211
2260
|
*/
|
|
2212
2261
|
private setupCallbacks;
|
|
2213
2262
|
init(): Promise<void>;
|
|
2263
|
+
/**
|
|
2264
|
+
* The network half of boot: connect, verify the restored session, and let the
|
|
2265
|
+
* sync engine catch up. Runs in the background after `init()` has already
|
|
2266
|
+
* resolved, so nothing here is on the paint path.
|
|
2267
|
+
*
|
|
2268
|
+
* Every step is best-effort. A failure leaves the client in exactly the state
|
|
2269
|
+
* a warm offline boot is in - local reads working, writes queued in the
|
|
2270
|
+
* outbox - and the connection supervisor keeps retrying underneath.
|
|
2271
|
+
*/
|
|
2272
|
+
private initRemote;
|
|
2214
2273
|
private bucketSwitchChain;
|
|
2215
2274
|
private pendingBucketTarget;
|
|
2216
2275
|
/**
|
|
@@ -2363,14 +2422,38 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
2363
2422
|
[x: string]: /*elided*/any;
|
|
2364
2423
|
}>;
|
|
2365
2424
|
delete(table: string, id: string): Promise<void>;
|
|
2366
|
-
useRemote<T>(fn: (client: Surreal) => Promise<T> | T): Promise<T>;
|
|
2367
2425
|
/**
|
|
2368
|
-
*
|
|
2369
|
-
*
|
|
2370
|
-
* `_00_query` rows. Returns empty string if the query fails (we still
|
|
2371
|
-
* boot, just without session scoping for IDs).
|
|
2426
|
+
* Whether the local store is initialized and reads can be served. See the
|
|
2427
|
+
* `localReady` field: this is deliberately independent of connectivity.
|
|
2372
2428
|
*/
|
|
2373
|
-
|
|
2429
|
+
isLocalReady(): boolean;
|
|
2430
|
+
useRemote<T>(fn: (client: Surreal) => Promise<T> | T): Promise<T>;
|
|
2431
|
+
/**
|
|
2432
|
+
* Mint the salt used for query-id hashing, so two sessions registering the
|
|
2433
|
+
* same logical query get distinct `_00_query` rows.
|
|
2434
|
+
*
|
|
2435
|
+
* Generated LOCALLY, deliberately. This used to be `RETURN <string>session::id()`,
|
|
2436
|
+
* which cost a serial round trip on the critical boot path and resolved to
|
|
2437
|
+
* `''` offline. The value never needed to come from the server: the server
|
|
2438
|
+
* derives its own `clientId` inside `fn::query::register` and *ignores*
|
|
2439
|
+
* whatever the caller passed, and the permission rules that matter gate on
|
|
2440
|
+
* `auth_id = $auth.id` rather than the session (`_00_list_ref`). Session
|
|
2441
|
+
* scoping via `clientId = session::id()` was in fact removed upstream because
|
|
2442
|
+
* it broke a user with two tabs open. All this value has to be is unique per
|
|
2443
|
+
* browser session, which `randomUUID` gives us for free and offline.
|
|
2444
|
+
*/
|
|
2445
|
+
/**
|
|
2446
|
+
* The current principal as the `"table:id"` string the in-browser SSP wants
|
|
2447
|
+
* for `$auth.id`, or null when signed out.
|
|
2448
|
+
*
|
|
2449
|
+
* Tolerates BOTH shapes `currentUser.id` can take, which is the point:
|
|
2450
|
+
* a session restored from the cached token carries a plain string (the JWT's
|
|
2451
|
+
* `ID` claim), while one verified by the server carries a RecordId. Passing
|
|
2452
|
+
* the former to `encodeRecordId` reads `.table` off a string and throws
|
|
2453
|
+
* during boot.
|
|
2454
|
+
*/
|
|
2455
|
+
private sessionAuthId;
|
|
2456
|
+
private mintSessionSalt;
|
|
2374
2457
|
}
|
|
2375
2458
|
//#endregion
|
|
2376
2459
|
//#region src/utils/semver.d.ts
|