@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 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. Only the remote service sets this
16
- * (see `RemoteDatabaseService`) a local query can be legitimately slow and
17
- * has its own retry ladders, and there is no half-open-socket failure mode
18
- * for an in-process engine.
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 };