@spooky-sync/core 0.0.1-canary.69 → 0.0.1-canary.70

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
@@ -1,4 +1,4 @@
1
- import { C as UpdateOptions, E as EventSystem, S as StoreType, T as EventDefinition, _ as RecordVersionDiff, a as MutationCallback, b as Sp00kyQueryResult, c as PersistenceClient, d as QueryConfigRecord, f as QueryHash, g as RecordVersionArray, h as QueryUpdateCallback, i as MATERIALIZATION_SAMPLE_WINDOW, l as PinoTransmit, m as QueryTimeToLive, n as EventSubscriptionOptions, o as MutationEvent, p as QueryState, r as Level, s as MutationEventType, t as DebounceOptions, u as QueryConfig, v as RunOptions, w as Logger$1, x as Sp00kyQueryResultPromise, y as Sp00kyConfig } from "./types.js";
1
+ import { A as Logger$1, C as RunOptions, D as StoreType, E as Sp00kyQueryResultPromise, M as EventSystem, O as TimingPhase, S as RegistrationTimings, T as Sp00kyQueryResult, _ 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 EventDefinition, k as UpdateOptions, 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 } from "./types.js";
2
2
  import * as surrealdb0 from "surrealdb";
3
3
  import { Duration, RecordId, Surreal as Surreal$1, SurrealTransaction } from "surrealdb";
4
4
  import { AccessDefinition, BackendNames, BackendRoutes, BucketNames, ColumnSchema, GetTable, QueryBuilder, QueryOptions, RoutePayload, SchemaStructure, TableModel, TableNames, TypeNameToTypeMap } from "@spooky-sync/query-builder";
@@ -57,7 +57,23 @@ declare class LocalDatabaseService extends AbstractDatabaseService {
57
57
  constructor(config: Sp00kyConfig<any>['database'], logger: Logger$1);
58
58
  getConfig(): Sp00kyConfig<any>['database'];
59
59
  connect(): Promise<void>;
60
+ private unloadCloseRegistered;
61
+ /**
62
+ * Close the local DB on page unload so the SurrealDB-WASM worker releases its
63
+ * IndexedDB connection cleanly. Without this, the previous page's connection
64
+ * lingers; the next load's `client.connect` opens the store but the first
65
+ * write transaction in `client.use` hits an "IndexedDB error" — which then
66
+ * (mis)triggered the corrupt-store recovery and WIPED the cache on every
67
+ * reload, making warm loads as slow as cold ones. `pagehide` is the reliable
68
+ * unload signal (fires on bfcache + normal navigation); `close()` is async but
69
+ * the WASM worker initiates the IndexedDB connection teardown synchronously.
70
+ */
71
+ private registerUnloadClose;
72
+ private openStore;
60
73
  }
74
+ /** True for the SurrealDB-WASM error raised when its IndexedDB-backed key-value
75
+ * store can't be opened (corrupt / version-incompatible / blocked). Exported
76
+ * for unit testing the error-message match. */
61
77
  //#endregion
62
78
  //#region src/services/database/remote.d.ts
63
79
  declare class RemoteDatabaseService extends AbstractDatabaseService {
@@ -77,6 +93,12 @@ interface WasmStreamUpdate {
77
93
  query_id: string;
78
94
  result_hash: string;
79
95
  result_data: RecordVersionArray;
96
+ timing_store_apply_ms?: number;
97
+ timing_circuit_step_ms?: number;
98
+ timing_transform_ms?: number;
99
+ timing_parse_ms?: number;
100
+ timing_plan_ms?: number;
101
+ timing_snapshot_ms?: number;
80
102
  }
81
103
  //#endregion
82
104
  //#region src/services/stream-processor/index.d.ts
@@ -103,6 +125,19 @@ interface StreamUpdate {
103
125
  * for the initial register_view snapshot.
104
126
  */
105
127
  materializationTimeMs?: number;
128
+ /** SSP internal sub-phase timings (ms) for this ingest, from the WASM binding. */
129
+ storeApplyMs?: number;
130
+ circuitStepMs?: number;
131
+ transformMs?: number;
132
+ /**
133
+ * One-shot registration timings (ms). Only set on the StreamUpdate returned
134
+ * by `registerQueryPlan` (the register_view snapshot), not on ingest updates.
135
+ */
136
+ registration?: {
137
+ parseMs: number;
138
+ planMs: number;
139
+ snapshotMs: number;
140
+ };
106
141
  }
107
142
  type StreamProcessorEvents = {
108
143
  stream_update: EventDefinition<'stream_update', StreamUpdate[]>;
@@ -122,6 +157,8 @@ declare class StreamProcessorService {
122
157
  private processor;
123
158
  private isInitialized;
124
159
  private receivers;
160
+ private batching;
161
+ private batchBuffer;
125
162
  constructor(events: EventSystem<StreamProcessorEvents>, db: LocalDatabaseService, persistenceClient: PersistenceClient, logger: Logger);
126
163
  /**
127
164
  * Add a receiver for stream updates.
@@ -129,12 +166,53 @@ declare class StreamProcessorService {
129
166
  */
130
167
  addReceiver(receiver: StreamUpdateReceiver): void;
131
168
  private notifyUpdates;
169
+ private dispatchUpdates;
170
+ /**
171
+ * Ingest a batch of record changes as a single bulk operation, firing only
172
+ * one coalesced `StreamUpdate` per affected query once every record has been
173
+ * ingested (instead of one update per record). Use this whenever multiple
174
+ * records land at once — e.g. sync fetching N missing rows — so a list query
175
+ * re-runs and the UI re-renders once for the whole batch rather than
176
+ * row-by-row.
177
+ *
178
+ * Internally opens a coalescing window, ingests each record, then flushes;
179
+ * processor state is persisted once for the whole batch. No-op for an empty
180
+ * batch.
181
+ */
182
+ ingestMany(records: Array<{
183
+ table: string;
184
+ op: 'CREATE' | 'UPDATE' | 'DELETE';
185
+ id: string;
186
+ record: any;
187
+ }>): void;
188
+ /**
189
+ * Open a coalescing window. While open, the per-record stream updates
190
+ * emitted by `ingest` are buffered (one entry per queryHash) instead of
191
+ * dispatched. Always paired with `flushCoalescing()` in a try/finally by
192
+ * `ingestMany` so the window always closes — otherwise the processor stays
193
+ * stuck buffering forever.
194
+ *
195
+ * No-op if a window is already open (nested batches aren't expected here).
196
+ */
197
+ private beginCoalescing;
198
+ /**
199
+ * Close the coalescing window and flush: dispatch one coalesced
200
+ * `StreamUpdate` per buffered queryHash, then persist processor state once
201
+ * for the whole batch (instead of once per ingest).
202
+ */
203
+ private flushCoalescing;
132
204
  /**
133
205
  * Initialize the WASM module and processor.
134
206
  * This must be called before using other methods.
135
207
  */
136
208
  init(): Promise<void>;
137
209
  loadState(): Promise<void>;
210
+ /**
211
+ * Seed per-table `select` permission predicates ({ [table]: whereText }).
212
+ * Must run after the processor exists and before any `register_view`, else
213
+ * non-`_00_` tables are default-denied and registration fails.
214
+ */
215
+ setPermissions(permissions: Record<string, string>): void;
138
216
  saveState(): Promise<void>;
139
217
  /**
140
218
  * Ingest a record change into the processor.
@@ -409,6 +487,28 @@ declare class Sp00kyClient<S extends SchemaStructure> {
409
487
  subscribe(queryHash: string, callback: (records: Record<string, any>[]) => void, options?: {
410
488
  immediate?: boolean;
411
489
  }): Promise<() => void>;
490
+ /**
491
+ * Opt-in eager teardown for a query whose last subscriber has gone away
492
+ * (e.g. a viewport-windowed list cancelling an off-screen window). No-op
493
+ * while any subscriber remains. Tears down the remote `_00_query` view +
494
+ * local WASM view instead of waiting for the TTL sweep. Default behavior
495
+ * (no call here) keeps the view resident for cheap re-subscription.
496
+ */
497
+ deregisterQuery(queryHash: string): void;
498
+ /**
499
+ * Subscribe to a query's fetch-status changes (idle/fetching). With
500
+ * `{ immediate: true }` the callback fires synchronously with the current
501
+ * status. Powers the `useQuery` hook's `isFetching()` accessor.
502
+ */
503
+ subscribeQueryStatus(queryHash: string, callback: QueryStatusCallback, options?: {
504
+ immediate?: boolean;
505
+ }): () => void;
506
+ /**
507
+ * Report the frontend processing time (ms) a client framework spent applying
508
+ * an update for a query (e.g. `useQuery`'s `reconcile()`), so DevTools/MCP can
509
+ * surface the "frontend" phase of the per-query timing breakdown.
510
+ */
511
+ reportFrontendTiming(queryHash: string, ms: number): void;
412
512
  run<B extends BackendNames<S>, R extends BackendRoutes<S, B>>(backend: B, path: R, payload: RoutePayload<S, B, R>, options?: RunOptions): Promise<void>;
413
513
  bucket<B extends BucketNames<S>>(name: B): BucketHandle;
414
514
  create(id: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
@@ -438,4 +538,4 @@ declare function textToHtml(text: string): string;
438
538
  */
439
539
 
440
540
  //#endregion
441
- export { AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, BucketHandle, CURSOR_COLORS, CrdtField, CrdtManager, DebounceOptions, EventSubscriptionOptions, Level, MATERIALIZATION_SAMPLE_WINDOW, MutationCallback, MutationEvent, MutationEventType, PersistenceClient, PinoTransmit, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryTimeToLive, QueryUpdateCallback, RecordVersionArray, RecordVersionDiff, RunOptions, Sp00kyClient, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StoreType, UpdateOptions, createAuthEventSystem, cursorColorFromName, fileToUint8Array, textToHtml };
541
+ export { AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, BucketHandle, CURSOR_COLORS, CrdtField, CrdtManager, DebounceOptions, EventSubscriptionOptions, Level, MATERIALIZATION_SAMPLE_WINDOW, MutationCallback, MutationEvent, MutationEventType, PersistenceClient, PhaseStat, PinoTransmit, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, Sp00kyClient, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StoreType, TimingPhase, UpdateOptions, createAuthEventSystem, cursorColorFromName, fileToUint8Array, textToHtml };