@spooky-sync/core 0.0.1-canary.207 → 0.0.1-canary.209
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 +69 -6
- package/dist/index.js +234 -61
- package/dist/sqlite-worker.js +22 -7
- package/dist/types.d.ts +10 -0
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/modules/data/data.pending-ids.test.ts +74 -0
- package/src/modules/data/data.rematerialize.test.ts +114 -0
- package/src/modules/data/index.ts +161 -62
- package/src/modules/devtools/index.ts +65 -18
- package/src/modules/devtools/notify-throttle.test.ts +6 -1
- package/src/modules/devtools/state-shape.test.ts +146 -0
- package/src/modules/sync/sync.live-removal.test.ts +1 -0
- package/src/modules/sync/sync.ts +10 -0
- package/src/services/database/database.ts +14 -5
- package/src/services/database/errors.ts +34 -0
- package/src/services/database/local.ts +6 -0
- package/src/services/database/sqlite-cache-engine.timeout.test.ts +61 -0
- package/src/services/database/sqlite-cache-engine.ts +27 -1
- package/src/services/database/sqlite-lock-verify.test.ts +33 -0
- package/src/services/database/sqlite-lock-verify.ts +45 -0
- package/src/services/database/sqlite-transport.ts +5 -2
- package/src/services/database/sqlite-worker.ts +11 -11
- package/src/services/stream-processor/index.ts +8 -0
- package/src/sp00ky.ts +3 -3
- package/src/types.ts +10 -0
- package/src/utils/index.ts +8 -2
- package/src/utils/parser.test.ts +49 -120
- package/src/utils/parser.ts +30 -1
package/dist/index.d.ts
CHANGED
|
@@ -12,12 +12,18 @@ declare abstract class AbstractDatabaseService {
|
|
|
12
12
|
protected logger: Logger$1;
|
|
13
13
|
protected events: DatabaseEventSystem;
|
|
14
14
|
/**
|
|
15
|
-
* Per-query deadline in ms; `0` disables.
|
|
16
|
-
* (see `RemoteDatabaseService`)
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* Per-query deadline in ms; `0` disables. The remote service sets it from
|
|
16
|
+
* `queryTimeoutMs` (see `RemoteDatabaseService`), the local one from
|
|
17
|
+
* `localOpTimeoutMs` (see `LocalDatabaseService`): a local query can be
|
|
18
|
+
* legitimately slow, but it must never be endless - every query waits on the
|
|
19
|
+
* previous link of {@link query}'s chain, and one that never settled wedged
|
|
20
|
+
* every later local op behind it.
|
|
19
21
|
*/
|
|
20
22
|
protected queryTimeoutMs: number;
|
|
23
|
+
/** The error a deadline expiry rejects with; the local service substitutes
|
|
24
|
+
* its typed `LocalOpTimeoutError`. "timed out" in the message is
|
|
25
|
+
* load-bearing either way: `classifySyncError` keys off it. */
|
|
26
|
+
protected timeoutError(_query: string): Error;
|
|
21
27
|
protected abstract eventType: typeof DatabaseEventTypes.LocalQuery | typeof DatabaseEventTypes.RemoteQuery;
|
|
22
28
|
constructor(client: Surreal$1, logger: Logger$1, events: DatabaseEventSystem);
|
|
23
29
|
abstract connect(): Promise<void>;
|
|
@@ -256,6 +262,14 @@ interface StreamUpdate {
|
|
|
256
262
|
queryHash: string;
|
|
257
263
|
localArray: RecordVersionArray;
|
|
258
264
|
op?: 'CREATE' | 'UPDATE' | 'DELETE';
|
|
265
|
+
/**
|
|
266
|
+
* Client-internal: not from the circuit. A membership-only change that
|
|
267
|
+
* needed no fetch is re-materialized through this same path so it cannot
|
|
268
|
+
* race a real update (DataModule.scheduleRematerialize). Carries the last
|
|
269
|
+
* known `localArray`; consumers that describe an INGEST (persist, metrics,
|
|
270
|
+
* devtools events) skip it.
|
|
271
|
+
*/
|
|
272
|
+
synthetic?: boolean;
|
|
259
273
|
/**
|
|
260
274
|
* End-to-end ingest latency for the WASM call that produced this update,
|
|
261
275
|
* in milliseconds. Populated by StreamProcessorService.ingest. Undefined
|
|
@@ -734,6 +748,22 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
734
748
|
* Handle stream updates from DBSP (via CacheModule)
|
|
735
749
|
*/
|
|
736
750
|
onStreamUpdate(update: StreamUpdate): Promise<void>;
|
|
751
|
+
/** Coalesce `update` onto the query's trailing timer (see onStreamUpdate). */
|
|
752
|
+
private queueStreamUpdate;
|
|
753
|
+
/**
|
|
754
|
+
* Re-materialize + notify a query whose MEMBERSHIP changed without any row
|
|
755
|
+
* needing to be fetched, i.e. without the SSP stream update that normally
|
|
756
|
+
* carries the notify. That is every row this client wrote itself: the local
|
|
757
|
+
* CREATE memoized it at `_00_rv = 1`, the server publishes it at 1, so the
|
|
758
|
+
* sync engine rightly fetches nothing - and then nobody told the subscribers
|
|
759
|
+
* that `remoteArray` now holds the id. The row appeared on reload only.
|
|
760
|
+
*
|
|
761
|
+
* Routed through the same per-query debounce as a real stream update, so it
|
|
762
|
+
* cannot race one: a pending real update already materializes against the
|
|
763
|
+
* current `remoteArray` and wins. The synthetic update re-uses the circuit's
|
|
764
|
+
* last `localArray` and skips the persist/metrics that describe an ingest.
|
|
765
|
+
*/
|
|
766
|
+
scheduleRematerialize(queryHash: string): void;
|
|
737
767
|
/**
|
|
738
768
|
* Process a query's pending (debounced) stream update NOW instead of on the
|
|
739
769
|
* trailing edge. Called by the sync engine before it flips a query back to
|
|
@@ -794,6 +824,8 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
794
824
|
private pendingIdsAt;
|
|
795
825
|
private pendingIdsInflight;
|
|
796
826
|
private static readonly PENDING_IDS_TTL_MS;
|
|
827
|
+
private pendingIdsGen;
|
|
828
|
+
private static readonly PENDING_IDS_MAX_REREADS;
|
|
797
829
|
/** Drop the cached outbox ids. Cheap; call it on anything that could change
|
|
798
830
|
* `_00_pending_mutations`. */
|
|
799
831
|
private invalidatePendingIds;
|
|
@@ -957,7 +989,8 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
957
989
|
deletes: Set<string>;
|
|
958
990
|
}>;
|
|
959
991
|
/** The uncached read. Also the reload path after an invalidation, so the ids
|
|
960
|
-
* still survive a reload exactly as before.
|
|
992
|
+
* still survive a reload exactly as before. `gen` is the generation the read
|
|
993
|
+
* was issued under; the result is cached only if it is still current. */
|
|
961
994
|
private readPendingRecordIds;
|
|
962
995
|
/** True while ≥1 live subscriber is watching this query (refcount guard). */
|
|
963
996
|
hasSubscribers(hash: string): boolean;
|
|
@@ -2640,6 +2673,36 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
2640
2673
|
/** True when `a` is a valid version strictly greater than valid version `b`. */
|
|
2641
2674
|
declare function semverGt(a: unknown, b: unknown): boolean;
|
|
2642
2675
|
//#endregion
|
|
2676
|
+
//#region src/services/database/errors.d.ts
|
|
2677
|
+
/**
|
|
2678
|
+
* A local-store operation that did not answer within its deadline.
|
|
2679
|
+
*
|
|
2680
|
+
* The local write path (`db.create` / `db.update` / `db.delete`, every local
|
|
2681
|
+
* query behind them) used to have no deadline anywhere: the SQLite worker
|
|
2682
|
+
* transport parks a call until the worker replies, the surrealdb engine's
|
|
2683
|
+
* query chain waits on the previous link, and `withRetry` retries without a
|
|
2684
|
+
* clock. One op that never settled (a worker starved behind a long select, a
|
|
2685
|
+
* lock verification awaiting `navigator.locks.query()` forever) left the
|
|
2686
|
+
* caller's promise pending for the tab's lifetime - a chat composer that never
|
|
2687
|
+
* re-enabled, a call that never got past "Connecting".
|
|
2688
|
+
*
|
|
2689
|
+
* The message says "timed out" on purpose: `classifySyncError` keys off it and
|
|
2690
|
+
* treats the failure as transient (re-queue), never as an application error
|
|
2691
|
+
* that rolls the mutation back. `retryable: false` keeps `withRetry` from
|
|
2692
|
+
* spinning on it: the op is still running in the engine, retrying queues a
|
|
2693
|
+
* second copy behind it.
|
|
2694
|
+
*/
|
|
2695
|
+
declare class LocalOpTimeoutError extends Error {
|
|
2696
|
+
readonly name = "LocalOpTimeoutError";
|
|
2697
|
+
readonly retryable = false;
|
|
2698
|
+
readonly op: string;
|
|
2699
|
+
readonly timeoutMs: number;
|
|
2700
|
+
constructor(op: string, timeoutMs: number);
|
|
2701
|
+
}
|
|
2702
|
+
/** Default deadline for one local-store operation. Generous: a cold 4k-row
|
|
2703
|
+
* select on a throttled tab is seconds, not tens of seconds. */
|
|
2704
|
+
declare const DEFAULT_LOCAL_OP_TIMEOUT_MS = 30000;
|
|
2705
|
+
//#endregion
|
|
2643
2706
|
//#region src/utils/index.d.ts
|
|
2644
2707
|
declare function fileToUint8Array(file: File | Blob): Promise<Uint8Array>;
|
|
2645
2708
|
/**
|
|
@@ -2652,4 +2715,4 @@ declare function textToHtml(text: string): string;
|
|
|
2652
2715
|
*/
|
|
2653
2716
|
|
|
2654
2717
|
//#endregion
|
|
2655
|
-
export { AppReleaseHandle, AppReleaseModule, type AppReleaseOptions, type AppReleaseSnapshot, AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, BLURHASH_IMAGE_EXTENSIONS, type BlobCacheStats, type BlobEntry, type BlobKey, type BlobReadOptions, type BlobUrlLease, type BlurhashEncodeOptions, type BlurhashSetting, BucketHandle, BucketPutOptions, BucketPutResult, CURSOR_COLORS, ConnectionState, CrdtField, CrdtManager, DebounceOptions, EventSubscriptionOptions, FeatureFlagHandle, FeatureFlagModule, type FeatureFlagOptions, type FeatureFlagOverride, type FeatureFlagSnapshot, Level, MATERIALIZATION_SAMPLE_WINDOW, MutationCallback, MutationEvent, MutationEventType, PersistenceClient, PhaseStat, PinoTransmit, PreloadOptions, PreloadRefresh, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, ReconnectConfig, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, Sp00kyClient, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StorageHealth, StorageHealthStatus, StoreType, SyncHealth, SyncHealthConfig, SyncHealthStatus, TimingPhase, UpdateOptions, blurhashSidecarPath, bucketContentToBlob, createAuthEventSystem, cursorColorFromName, decode as decodeBlurhash, encode as encodeBlurhash, encodeImageToBlurhash, fileToUint8Array, isBlurhashValid, isImagePath, semverGt, textToHtml };
|
|
2718
|
+
export { AppReleaseHandle, AppReleaseModule, type AppReleaseOptions, type AppReleaseSnapshot, AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, BLURHASH_IMAGE_EXTENSIONS, type BlobCacheStats, type BlobEntry, type BlobKey, type BlobReadOptions, type BlobUrlLease, type BlurhashEncodeOptions, type BlurhashSetting, BucketHandle, BucketPutOptions, BucketPutResult, CURSOR_COLORS, ConnectionState, CrdtField, CrdtManager, DEFAULT_LOCAL_OP_TIMEOUT_MS, DebounceOptions, EventSubscriptionOptions, FeatureFlagHandle, FeatureFlagModule, type FeatureFlagOptions, type FeatureFlagOverride, type FeatureFlagSnapshot, Level, LocalOpTimeoutError, MATERIALIZATION_SAMPLE_WINDOW, MutationCallback, MutationEvent, MutationEventType, PersistenceClient, PhaseStat, PinoTransmit, PreloadOptions, PreloadRefresh, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, ReconnectConfig, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, Sp00kyClient, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StorageHealth, StorageHealthStatus, StoreType, SyncHealth, SyncHealthConfig, SyncHealthStatus, TimingPhase, UpdateOptions, blurhashSidecarPath, bucketContentToBlob, createAuthEventSystem, cursorColorFromName, decode as decodeBlurhash, encode as encodeBlurhash, encodeImageToBlurhash, fileToUint8Array, isBlurhashValid, isImagePath, semverGt, textToHtml };
|