@spooky-sync/core 0.0.1-canary.236 → 0.0.1-canary.238
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 +107 -5
- package/dist/index.js +437 -81
- package/dist/otel/index.d.ts +1 -1
- package/dist/types.d.ts +40 -27
- package/package.json +3 -3
- package/src/modules/data/data.authority.test.ts +122 -0
- package/src/modules/data/data.heartbeat-wake.test.ts +115 -0
- package/src/modules/data/data.membership.test.ts +168 -68
- package/src/modules/data/index.ts +201 -83
- package/src/modules/devtools/index.ts +3 -2
- package/src/modules/sync/engine.ts +12 -7
- package/src/modules/sync/sync.health.test.ts +13 -3
- package/src/modules/sync/sync.list-ref-meta.test.ts +193 -0
- package/src/modules/sync/sync.live-removal.test.ts +137 -23
- package/src/modules/sync/sync.ts +326 -60
- package/src/modules/sync/utils.test.ts +1 -1
- package/src/modules/sync/utils.ts +10 -2
- package/src/sp00ky.ts +49 -1
- package/src/types.ts +41 -26
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as ServerViewMeta, B as TimingPhase, C as QueryTimings, D as RecordVersionDiff, E as RecordVersionArray, F as StorageHealthStatus, G as DatabaseEventSystem, H as UpEvent, I as StoreType, J as SyncEventSystem, K as DatabaseEventTypes, L as SyncHealth, M as Sp00kyQueryResult, N as Sp00kyQueryResultPromise, O as RegistrationTimings, P as StorageHealth, R as SyncHealthConfig, S as QueryTimeToLive, T as ReconnectConfig, U as LocalStore, V as UpdateOptions, W as SealedQuery, X as EventSystem, Y as EventDefinition, _ as QueryConfigRecord, a as MATERIALIZATION_SAMPLE_WINDOW, b as QueryStatus, c as MutationEvent, d as PhaseStat, f as PinoTransmit, g as QueryConfig, h as QueryAuthorityCallback, i as Level, j as Sp00kyConfig, k as RunOptions, l as MutationEventType, m as PreloadRefresh, n as DebounceOptions, o as MembershipOutcome, p as PreloadOptions, q as Logger$1, r as EventSubscriptionOptions, s as MutationCallback, t as ConnectionState, u as PersistenceClient, v as QueryHash, w as QueryUpdateCallback, x as QueryStatusCallback, y as QueryState, z as SyncHealthStatus } from "./types.js";
|
|
2
2
|
import * as surrealdb0 from "surrealdb";
|
|
3
3
|
import { Duration, RecordId, Surreal as Surreal$1, SurrealEvents, SurrealTransaction } from "surrealdb";
|
|
4
4
|
import { AccessDefinition, BackendNames, BackendRoutes, BucketNames, ColumnSchema, FinalQuery, GetTable, QueryBuilder, QueryOptions, QueryPlan, RoutePayload, SchemaStructure, TableModel, TableNames, TypeNameToTypeMap } from "@spooky-sync/query-builder";
|
|
@@ -733,6 +733,8 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
733
733
|
private pendingQueries;
|
|
734
734
|
private subscriptions;
|
|
735
735
|
private statusSubscriptions;
|
|
736
|
+
/** Observers of a query's authority (`membershipKnown`) flips, keyed like `statusSubscriptions`. */
|
|
737
|
+
private authoritySubscriptions;
|
|
736
738
|
private mutationCallbacks;
|
|
737
739
|
private debounceTimers;
|
|
738
740
|
private pendingStreamUpdates;
|
|
@@ -754,6 +756,15 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
754
756
|
* DataModule is constructed — mirrors `onQueryStatusChange`.
|
|
755
757
|
*/
|
|
756
758
|
onHeartbeat?: (hash: QueryHash) => void;
|
|
759
|
+
/**
|
|
760
|
+
* Fired once per episode when the server's `_00_query` row for a query we
|
|
761
|
+
* hold membership for is found missing (see `updateQueryRemoteArray`). Wired
|
|
762
|
+
* by Sp00kyClient to enqueue a `register` down-event so the SSP rebuilds the
|
|
763
|
+
* view; the client keeps rendering what it has meanwhile.
|
|
764
|
+
*/
|
|
765
|
+
onViewLost?: (hash: QueryHash) => void;
|
|
766
|
+
/** DevTools observer for authority flips; mirrors `onQueryStatusChange`. */
|
|
767
|
+
onQueryAuthorityChange?: (hash: QueryHash, known: boolean) => void;
|
|
757
768
|
/**
|
|
758
769
|
* Optional hook fired by {@link deregisterQuery} when an opt-in query (e.g. a
|
|
759
770
|
* viewport-windowed list cancelling an off-screen window) loses its last
|
|
@@ -806,6 +817,22 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
806
817
|
subscribeStatus(queryHash: string, callback: QueryStatusCallback, options?: {
|
|
807
818
|
immediate?: boolean;
|
|
808
819
|
}): () => void;
|
|
820
|
+
/**
|
|
821
|
+
* Subscribe to a query's authority flips: whether server membership is known
|
|
822
|
+
* for it. With `{ immediate: true }` the callback fires synchronously with
|
|
823
|
+
* the current value (`false` for an unknown query).
|
|
824
|
+
*/
|
|
825
|
+
subscribeAuthority(queryHash: string, callback: QueryAuthorityCallback, options?: {
|
|
826
|
+
immediate?: boolean;
|
|
827
|
+
}): () => void;
|
|
828
|
+
/** Whether server membership is known for `queryHash` (see `QueryConfig.membershipKnown`). */
|
|
829
|
+
isAuthoritative(queryHash: string): boolean;
|
|
830
|
+
/**
|
|
831
|
+
* The single writer of `config.membershipKnown` for a live query. Fans out
|
|
832
|
+
* to `subscribeAuthority` listeners and DevTools on a change; no-op when
|
|
833
|
+
* unchanged or the query is unknown.
|
|
834
|
+
*/
|
|
835
|
+
private setMembershipKnown;
|
|
809
836
|
/**
|
|
810
837
|
* Set a query's fetch status and notify status observers (DevTools +
|
|
811
838
|
* `subscribeStatus` listeners). No-op when the status is unchanged or the
|
|
@@ -1117,9 +1144,15 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
1117
1144
|
getActiveQueryHashes(): QueryHash[];
|
|
1118
1145
|
updateQueryLocalArray(id: string, localArray: RecordVersionArray): Promise<void>;
|
|
1119
1146
|
updateQueryRemoteArray(hash: string, remoteArray: RecordVersionArray, opts?: {
|
|
1120
|
-
/** `_00_query
|
|
1121
|
-
|
|
1122
|
-
|
|
1147
|
+
/** The `_00_query` row as read in the same round trip as the edges. */
|
|
1148
|
+
meta?: ServerViewMeta;
|
|
1149
|
+
/**
|
|
1150
|
+
* The caller verified upstream that the ids missing from `remoteArray`
|
|
1151
|
+
* no longer exist (see `Sp00kySync` LIVE removal handling); an empty set
|
|
1152
|
+
* is then a real answer even without a readable row.
|
|
1153
|
+
*/
|
|
1154
|
+
verifiedRemoval?: boolean;
|
|
1155
|
+
}): Promise<MembershipOutcome>;
|
|
1123
1156
|
/**
|
|
1124
1157
|
* Cancel every armed timer ahead of a local-bucket switch: stream-update
|
|
1125
1158
|
* debounce timers (their pending updates carry the OLD bucket's id-sets) and
|
|
@@ -1205,6 +1238,26 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
1205
1238
|
private calculateMembershipKey;
|
|
1206
1239
|
private sha256;
|
|
1207
1240
|
private startTTLHeartbeat;
|
|
1241
|
+
/**
|
|
1242
|
+
* Heartbeat every watched query NOW and re-anchor its timer.
|
|
1243
|
+
*
|
|
1244
|
+
* The TTL timer is the only thing keeping a view alive on the server, and
|
|
1245
|
+
* browsers throttle timers in hidden tabs (Chrome aligns them to one-minute
|
|
1246
|
+
* ticks after five minutes hidden, and suspends them outright on some
|
|
1247
|
+
* mobile platforms). A tab that comes back after a long stretch may have
|
|
1248
|
+
* missed its beat by more than the server's slack, and the sweep will have
|
|
1249
|
+
* taken the row and its edges. The client does not blank on that any more
|
|
1250
|
+
* (`updateQueryRemoteArray` treats a missing row as "view lost"), but the
|
|
1251
|
+
* sooner it finds out, the sooner the view is rebuilt: `heartbeatQuery`
|
|
1252
|
+
* re-registers a query whose row is gone. Called on `visibilitychange` (to
|
|
1253
|
+
* visible) and `online`.
|
|
1254
|
+
*
|
|
1255
|
+
* Queries beaten within the last `minGapMs` are skipped, so a flurry of
|
|
1256
|
+
* wake events costs one beat per query. There is deliberately no local
|
|
1257
|
+
* expiry anywhere in this module: the TTL is the server's, and the client's
|
|
1258
|
+
* only duty is to keep asserting liveness while something is watching.
|
|
1259
|
+
*/
|
|
1260
|
+
heartbeatOnWake(minGapMs?: number): number;
|
|
1208
1261
|
private replaceRecordInQueries;
|
|
1209
1262
|
}
|
|
1210
1263
|
/**
|
|
@@ -1653,6 +1706,25 @@ declare class Sp00kySync<S extends SchemaStructure> {
|
|
|
1653
1706
|
* changed.
|
|
1654
1707
|
*/
|
|
1655
1708
|
private applyListRefSnapshot;
|
|
1709
|
+
/**
|
|
1710
|
+
* Re-read ONE query's server state now (edges + `_00_query` row) and land
|
|
1711
|
+
* it through {@link applyListRefSnapshot}. The targeted form of the poll,
|
|
1712
|
+
* for the moments that cannot wait for the next tick: a registration that
|
|
1713
|
+
* answered `materializing`, and a LIVE removal that has to be checked
|
|
1714
|
+
* against the row before it may empty a painted list.
|
|
1715
|
+
*/
|
|
1716
|
+
private refetchListRefForQuery;
|
|
1717
|
+
/** Per-query pending targeted re-reads: one timer and the ladder still to walk. */
|
|
1718
|
+
private listRefRefetches;
|
|
1719
|
+
/**
|
|
1720
|
+
* Schedule a targeted re-read of `queryHash` after `delays[0]` ms. If the
|
|
1721
|
+
* read still says the server is publishing (`materializing`), the next rung
|
|
1722
|
+
* of the ladder is armed; any other outcome ends it. Re-scheduling a hash
|
|
1723
|
+
* that already has a timer replaces it (latest wins), so bursts coalesce.
|
|
1724
|
+
*/
|
|
1725
|
+
private scheduleListRefRefetch;
|
|
1726
|
+
private clearListRefRefetch;
|
|
1727
|
+
private clearListRefRefetches;
|
|
1656
1728
|
/**
|
|
1657
1729
|
* Resolve the current `_00_list_ref` table name for the active auth
|
|
1658
1730
|
* context. Public so the `createRemoteQuery` initial-fetch path can
|
|
@@ -1708,6 +1780,26 @@ declare class Sp00kySync<S extends SchemaStructure> {
|
|
|
1708
1780
|
private refetchAfterReconnect;
|
|
1709
1781
|
private startRefLiveQueries;
|
|
1710
1782
|
private handleRemoteListRefChange;
|
|
1783
|
+
/** LIVE removals awaiting verification, per query, with their coalescing timer. */
|
|
1784
|
+
private pendingLiveRemovals;
|
|
1785
|
+
/**
|
|
1786
|
+
* Hold a LIVE `_00_list_ref` DELETE for {@link LIVE_REMOVAL_COALESCE_MS},
|
|
1787
|
+
* then verify the batch (see {@link flushLiveRemovals}). A sweep or a
|
|
1788
|
+
* republish delivers a view's whole set as one burst, so one verification
|
|
1789
|
+
* round covers it.
|
|
1790
|
+
*/
|
|
1791
|
+
private queueLiveRemoval;
|
|
1792
|
+
/**
|
|
1793
|
+
* Verify a coalesced batch of LIVE removals against the upstream records.
|
|
1794
|
+
* Ids that no longer exist upstream are dropped from membership (the body
|
|
1795
|
+
* delete already happened inside `syncRecords`) and the query re-renders.
|
|
1796
|
+
* Anything still present upstream - or a verification that could not run -
|
|
1797
|
+
* is settled by a targeted re-read of the server's set for this query,
|
|
1798
|
+
* where the `_00_query` row decides: gone = view lost (keep rows,
|
|
1799
|
+
* re-register), `materializing` = wait, `ready` = adopt the smaller set.
|
|
1800
|
+
*/
|
|
1801
|
+
private flushLiveRemovals;
|
|
1802
|
+
private clearLiveRemovals;
|
|
1711
1803
|
/**
|
|
1712
1804
|
* Handle a LIVE change to a SUBQUERY child edge (a `_00_list_ref` row with
|
|
1713
1805
|
* `parent` set) for a `.related()` query. Unlike primary rows, child rows
|
|
@@ -2821,6 +2913,16 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
2821
2913
|
subscribeQueryStatus(queryHash: string, callback: QueryStatusCallback, options?: {
|
|
2822
2914
|
immediate?: boolean;
|
|
2823
2915
|
}): () => void;
|
|
2916
|
+
/**
|
|
2917
|
+
* Subscribe to a query's authority flips: `true` once server membership is
|
|
2918
|
+
* known for it (a registration or poll landed, or a durable `_00_window`
|
|
2919
|
+
* seed was read on boot), `false` when it is reset (bucket switch). With
|
|
2920
|
+
* `{ immediate: true }` the callback fires synchronously with the current
|
|
2921
|
+
* value. Powers the bindings' `isAuthoritative()` / cold-only `isLoading()`.
|
|
2922
|
+
*/
|
|
2923
|
+
subscribeQueryAuthority(queryHash: string, callback: QueryAuthorityCallback, options?: {
|
|
2924
|
+
immediate?: boolean;
|
|
2925
|
+
}): () => void;
|
|
2824
2926
|
/**
|
|
2825
2927
|
* Report the frontend processing time (ms) a client framework spent applying
|
|
2826
2928
|
* an update for a query (e.g. `useQuery`'s `reconcile()`), so DevTools/MCP can
|
|
@@ -2936,4 +3038,4 @@ declare function textToHtml(text: string): string;
|
|
|
2936
3038
|
*/
|
|
2937
3039
|
|
|
2938
3040
|
//#endregion
|
|
2939
|
-
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 };
|
|
3041
|
+
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, MembershipOutcome, MutationCallback, MutationEvent, MutationEventType, PersistenceClient, PhaseStat, PinoTransmit, PreloadOptions, PreloadRefresh, QueryAuthorityCallback, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, ReconnectConfig, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, ServerViewMeta, 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 };
|