@spooky-sync/client-solid 0.0.1-canary.15 → 0.0.1-canary.151

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.js CHANGED
@@ -1,12 +1,13 @@
1
- import { SpookyClient, fileToUint8Array } from "@spooky-sync/core";
1
+ import { Sp00kyClient, fileToUint8Array, semverGt } from "@spooky-sync/core";
2
2
  import { RecordId, Uuid } from "surrealdb";
3
3
  import { createComponent, createContext, createEffect, createMemo, createSignal, mergeProps, onCleanup, onMount, useContext } from "solid-js";
4
+ import { createStore, reconcile } from "solid-js/store";
4
5
 
5
6
  //#region src/lib/context.ts
6
- const SpookyContext = createContext();
7
+ const Sp00kyContext = createContext();
7
8
  function useDb() {
8
- const db = useContext(SpookyContext);
9
- if (!db) throw new Error("useDb must be used within a <SpookyProvider>. Wrap your app in <SpookyProvider config={...}>.");
9
+ const db = useContext(Sp00kyContext);
10
+ if (!db) throw new Error("useDb must be used within a <Sp00kyProvider>. Wrap your app in <Sp00kyProvider config={...}>.");
10
11
  return db;
11
12
  }
12
13
 
@@ -21,30 +22,56 @@ function useQuery(dbOrQuery, queryOrOptions, maybeOptions) {
21
22
  finalQuery = queryOrOptions;
22
23
  options = maybeOptions;
23
24
  } else {
24
- const contextDb = useContext(SpookyContext);
25
- if (!contextDb) throw new Error("useQuery: No db argument provided and no SpookyContext found. Either pass a SyncedDb instance or wrap your app in <SpookyProvider>.");
25
+ const contextDb = useContext(Sp00kyContext);
26
+ if (!contextDb) throw new Error("useQuery: No db argument provided and no Sp00kyContext found. Either pass a SyncedDb instance or wrap your app in <Sp00kyProvider>.");
26
27
  db = contextDb;
27
28
  finalQuery = dbOrQuery;
28
29
  options = queryOrOptions;
29
30
  }
30
- const [data, setData] = createSignal(void 0);
31
31
  const [error, setError] = createSignal(void 0);
32
32
  const [isFetched, setIsFetched] = createSignal(false);
33
- const [unsubscribe, setUnsubscribe] = createSignal(void 0);
33
+ const [isFetching, setIsFetching] = createSignal(false);
34
+ const [state, setState] = createStore({ value: void 0 });
35
+ const [version, setVersion] = createSignal(0);
36
+ const data = () => {
37
+ version();
38
+ return state.value;
39
+ };
34
40
  let prevQueryString;
35
- const spooky = db.getSpooky();
36
- const initQuery = async (query) => {
41
+ let runId = 0;
42
+ let activeUnsub;
43
+ let activeHash;
44
+ const teardownActive = () => {
45
+ activeUnsub?.();
46
+ activeUnsub = void 0;
47
+ };
48
+ const sp00ky = db.getSp00ky();
49
+ const initQuery = async (query, myRun) => {
37
50
  const { hash } = await query.run();
51
+ if (myRun !== runId) return;
52
+ activeHash = hash;
38
53
  setError(void 0);
39
54
  let isFirstCall = true;
40
- const unsub = await spooky.subscribe(hash, (e) => {
41
- const data = query.isOne ? e[0] : e;
42
- setData(() => data);
43
- const hasData = query.isOne ? data != null : e.length > 0;
55
+ const unsub = await sp00ky.subscribe(hash, (e) => {
56
+ const queryData = query.isOne ? e[0] : e;
57
+ const reconcileStart = performance.now();
58
+ setState("value", reconcile(queryData, { key: "id" }));
59
+ setVersion((v) => v + 1);
60
+ sp00ky.reportFrontendTiming(hash, performance.now() - reconcileStart);
61
+ const hasData = query.isOne ? queryData !== null && queryData !== void 0 : e.length > 0;
44
62
  if (!isFirstCall || hasData) setIsFetched(true);
45
63
  isFirstCall = false;
46
64
  }, { immediate: true });
47
- setUnsubscribe(() => unsub);
65
+ const unsubStatus = sp00ky.subscribeQueryStatus(hash, (status) => setIsFetching(status === "fetching"), { immediate: true });
66
+ const teardown = () => {
67
+ unsub();
68
+ unsubStatus();
69
+ };
70
+ if (myRun !== runId) {
71
+ teardown();
72
+ return;
73
+ }
74
+ activeUnsub = teardown;
48
75
  };
49
76
  createEffect(() => {
50
77
  if (!(options?.enabled?.() ?? true)) {
@@ -53,22 +80,211 @@ function useQuery(dbOrQuery, queryOrOptions, maybeOptions) {
53
80
  }
54
81
  const query = typeof finalQuery === "function" ? finalQuery() : finalQuery;
55
82
  if (!query) return;
56
- const queryString = JSON.stringify(query);
83
+ const queryString = String(query.hash);
57
84
  if (queryString === prevQueryString) return;
58
85
  prevQueryString = queryString;
86
+ const myRun = ++runId;
87
+ teardownActive();
59
88
  setIsFetched(false);
60
- initQuery(query);
61
- onCleanup(() => {
62
- unsubscribe()?.();
63
- });
89
+ initQuery(query, myRun);
90
+ });
91
+ onCleanup(() => {
92
+ runId++;
93
+ teardownActive();
94
+ if (options?.deregisterOnCleanup && activeHash) sp00ky.deregisterQuery(activeHash);
64
95
  });
65
96
  const isLoading = () => {
66
97
  return !isFetched() && error() === void 0;
67
98
  };
99
+ const isSettled = () => isFetched() && !isFetching();
68
100
  return {
69
101
  data,
70
102
  error,
71
- isLoading
103
+ isLoading,
104
+ isFetching,
105
+ isSettled
106
+ };
107
+ }
108
+
109
+ //#endregion
110
+ //#region src/lib/create-preload.ts
111
+ /**
112
+ * Reactive, fire-and-forget prewarm. Resolves the query (calling it if it's a
113
+ * function so it tracks reactive deps), dedupes on the query's stable identity
114
+ * hash, and warms it into the local cache via `db.preload`. No subscription and
115
+ * no cleanup: preload registers nothing that needs tearing down.
116
+ *
117
+ * Typical use: inside a list row, preload the detail query the user is likely
118
+ * to open next, so navigation paints from cache instead of the network.
119
+ */
120
+ function createPreload(dbOrQuery, queryOrOptions, maybeOptions) {
121
+ let db;
122
+ let finalQuery;
123
+ let options;
124
+ if (dbOrQuery instanceof SyncedDb) {
125
+ db = dbOrQuery;
126
+ finalQuery = queryOrOptions;
127
+ options = maybeOptions;
128
+ } else {
129
+ const contextDb = useContext(Sp00kyContext);
130
+ if (!contextDb) throw new Error("createPreload: No db argument provided and no Sp00kyContext found. Either pass a SyncedDb instance or wrap your app in <Sp00kyProvider>.");
131
+ db = contextDb;
132
+ finalQuery = dbOrQuery;
133
+ options = queryOrOptions;
134
+ }
135
+ let prevHash;
136
+ createEffect(() => {
137
+ if (!(options?.enabled?.() ?? true)) return;
138
+ const query = typeof finalQuery === "function" ? finalQuery() : finalQuery;
139
+ if (!query) return;
140
+ if (query.hash === prevHash) return;
141
+ prevHash = query.hash;
142
+ db.getSp00ky().preload(query, {
143
+ refresh: options?.refresh,
144
+ staleTime: options?.staleTime
145
+ });
146
+ });
147
+ }
148
+
149
+ //#endregion
150
+ //#region src/lib/use-sync-status.ts
151
+ /**
152
+ * Observe sync health for a "can't reach the server" banner / indicator.
153
+ *
154
+ * Backed by `db.subscribeToSyncHealth`. Individual sync failures (a transient
155
+ * remote 500 on query registration, a dropped socket) are absorbed by the
156
+ * retry and never flip this; `isDegraded()` only goes true once failures
157
+ * persist for the configured number of consecutive rounds (sp00ky core config
158
+ * `syncHealth.degradeAfterConsecutiveFailures`, default 3), and flips back on
159
+ * the next successful round. Must be used within a `<Sp00kyProvider>`.
160
+ */
161
+ function useSyncStatus() {
162
+ const db = useDb();
163
+ const [health, setHealth] = createSignal(db.syncHealth);
164
+ onCleanup(db.subscribeToSyncHealth(setHealth));
165
+ return {
166
+ health,
167
+ status: () => health().status,
168
+ isHealthy: () => health().status === "healthy",
169
+ isDegraded: () => health().status === "degraded",
170
+ everConnected: () => health().everConnected,
171
+ isOffline: () => health().status === "degraded" && health().everConnected
172
+ };
173
+ }
174
+
175
+ //#endregion
176
+ //#region src/lib/use-crdt-field.ts
177
+ function useCrdtField(table, recordId, field, fallbackText) {
178
+ const db = useContext(Sp00kyContext);
179
+ if (!db) throw new Error("useCrdtField must be used within a <Sp00kyProvider>");
180
+ const [crdtField, setCrdtField] = createSignal(null);
181
+ let currentId;
182
+ let initialized = false;
183
+ createEffect(() => {
184
+ const id = recordId();
185
+ if (initialized && id === currentId) return;
186
+ if (currentId && crdtField()) {
187
+ db.getSp00ky().closeCrdtField(table, currentId, field);
188
+ setCrdtField(null);
189
+ }
190
+ currentId = id;
191
+ initialized = true;
192
+ if (!id) return;
193
+ const sp00ky = db.getSp00ky();
194
+ const text = fallbackText?.();
195
+ sp00ky.openCrdtField(table, id, field, text).then((cf) => {
196
+ if (currentId === id) setCrdtField(cf);
197
+ }).catch((err) => {
198
+ console.error(`[useCrdtField] Failed to open CRDT field ${table}.${field} on ${id}:`, err);
199
+ });
200
+ });
201
+ onCleanup(() => {
202
+ if (currentId && crdtField()) {
203
+ db.getSp00ky().closeCrdtField(table, currentId, field);
204
+ setCrdtField(null);
205
+ }
206
+ });
207
+ return crdtField;
208
+ }
209
+
210
+ //#endregion
211
+ //#region src/lib/use-feature-flag.ts
212
+ /**
213
+ * Subscribe to a feature flag for the currently authenticated user.
214
+ *
215
+ * Returns three Solid accessors that update reactively whenever the
216
+ * server-materialized assignment in `_00_user_feature` changes. Backed by
217
+ * the same SSP + sync pipeline that powers `useQuery`, so toggling a flag
218
+ * via `spky flag enable <key>` propagates to the UI without a refresh.
219
+ *
220
+ * `enabled()` is `true` when the resolved variant exists and is not 'off'.
221
+ * For multi-variant flags, prefer `variant()` directly.
222
+ */
223
+ function useFeatureFlag(key, options) {
224
+ const handle = useDb().getSp00ky().feature(key, options);
225
+ const [variant, setVariant] = createSignal(handle.variant());
226
+ const [payload, setPayload] = createSignal(handle.payload());
227
+ const unsub = handle.subscribe((s) => {
228
+ setVariant(s.variant ?? options?.fallback);
229
+ setPayload(s.payload);
230
+ });
231
+ onCleanup(() => {
232
+ unsub();
233
+ handle.close();
234
+ });
235
+ return {
236
+ variant,
237
+ payload,
238
+ enabled: () => {
239
+ const v = variant();
240
+ return v !== void 0 && v !== "off";
241
+ }
242
+ };
243
+ }
244
+
245
+ //#endregion
246
+ //#region src/lib/use-app-release.ts
247
+ async function reloadForSnapshot(snapshot) {
248
+ if (typeof window === "undefined") return;
249
+ if (snapshot.cacheBust) try {
250
+ if (window.caches) {
251
+ const keys = await window.caches.keys();
252
+ await Promise.all(keys.map((k) => window.caches.delete(k)));
253
+ }
254
+ if (navigator.serviceWorker) {
255
+ const regs = await navigator.serviceWorker.getRegistrations();
256
+ for (const r of regs) r.update().catch(() => {});
257
+ }
258
+ window.location.href = window.location.pathname + "?cb=" + Date.now();
259
+ return;
260
+ } catch {}
261
+ window.location.reload();
262
+ }
263
+ /**
264
+ * Observe the app's announced release (`_00_app_release:<app>`, written by
265
+ * `spky deploy` / `spky release`) and compare it against the running build.
266
+ *
267
+ * Typical use: mount a small "new version available — Reload" notification
268
+ * gated on `updateAvailable()`, auto-invoking `reload()` when `mandatory()`
269
+ * (guard the auto path against reload loops with a per-version marker, since
270
+ * a client can reload while the deploy is still rolling out and land on the
271
+ * old bundle again).
272
+ */
273
+ function useAppRelease(options) {
274
+ const handle = useDb().getSp00ky().appRelease(options.app, { ttl: options.ttl });
275
+ const [snapshot, setSnapshot] = createSignal(handle.snapshot());
276
+ const unsub = handle.subscribe(setSnapshot);
277
+ onCleanup(() => {
278
+ unsub();
279
+ handle.close();
280
+ });
281
+ const updateAvailable = () => semverGt(snapshot().version, options.currentVersion);
282
+ return {
283
+ latestVersion: () => snapshot().version,
284
+ updateAvailable,
285
+ mandatory: () => updateAvailable() && snapshot().mandatory,
286
+ cacheBust: () => snapshot().cacheBust,
287
+ reload: () => reloadForSnapshot(snapshot())
72
288
  };
73
289
  }
74
290
 
@@ -94,7 +310,7 @@ function useFileUpload(dbOrBucketName, maybeBucketName) {
94
310
  const validate = (file) => {
95
311
  const config = db.getBucketConfig(bucketName);
96
312
  if (!config) return;
97
- if (config.maxSize != null && file.size > config.maxSize) {
313
+ if (config.maxSize !== null && config.maxSize !== void 0 && file.size > config.maxSize) {
98
314
  const maxMB = (config.maxSize / (1024 * 1024)).toFixed(1);
99
315
  throw new Error(`File exceeds maximum size of ${maxMB} MB.`);
100
316
  }
@@ -203,9 +419,8 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
203
419
  const [error, setError] = createSignal(null);
204
420
  let currentKey = null;
205
421
  let privateUrl = null;
206
- let refetchTrigger;
207
422
  const [refetchSignal, setRefetchSignal] = createSignal(0);
208
- refetchTrigger = () => setRefetchSignal((n) => n + 1);
423
+ const refetchTrigger = () => setRefetchSignal((n) => n + 1);
209
424
  async function doDownload(key, filePath) {
210
425
  if (useCache) {
211
426
  const cached = downloadCache.get(key);
@@ -325,26 +540,31 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
325
540
  }
326
541
 
327
542
  //#endregion
328
- //#region src/lib/SpookyProvider.ts
329
- function SpookyProvider(props) {
543
+ //#region src/lib/Sp00kyProvider.ts
544
+ function Sp00kyProvider(props) {
330
545
  const merged = mergeProps({ fallback: void 0 }, props);
331
546
  const [db, setDb] = createSignal(void 0);
332
547
  onMount(async () => {
333
548
  try {
334
549
  const instance = new SyncedDb(merged.config);
335
550
  await instance.init();
551
+ if (merged.preload) try {
552
+ await merged.preload(instance);
553
+ } catch (e) {
554
+ console.error("Sp00kyProvider: preload failed; revealing UI anyway", e);
555
+ }
336
556
  setDb(() => instance);
337
557
  merged.onReady?.(instance);
338
558
  } catch (e) {
339
559
  const error = e instanceof Error ? e : new Error(String(e));
340
560
  if (merged.onError) merged.onError(error);
341
- else console.error("SpookyProvider: Failed to initialize database", error);
561
+ else console.error("Sp00kyProvider: Failed to initialize database", error);
342
562
  }
343
563
  });
344
564
  return createMemo(() => {
345
565
  const instance = db();
346
566
  if (!instance) return merged.fallback;
347
- return createComponent(SpookyContext.Provider, {
567
+ return createComponent(Sp00kyContext.Provider, {
348
568
  value: instance,
349
569
  get children() {
350
570
  return merged.children;
@@ -356,69 +576,82 @@ function SpookyProvider(props) {
356
576
  //#endregion
357
577
  //#region src/index.ts
358
578
  /**
359
- * SyncedDb - A thin wrapper around spooky-ts for Solid.js integration
360
- * Delegates all logic to the underlying spooky-ts instance
579
+ * SyncedDb - A thin wrapper around sp00ky-ts for Solid.js integration
580
+ * Delegates all logic to the underlying sp00ky-ts instance
361
581
  */
362
582
  var SyncedDb = class {
363
583
  constructor(config) {
364
- this.spooky = null;
584
+ this.sp00ky = null;
365
585
  this._initialized = false;
366
586
  this.config = config;
367
587
  }
368
- getSpooky() {
369
- if (!this.spooky) throw new Error("SyncedDb not initialized");
370
- return this.spooky;
588
+ getSp00ky() {
589
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
590
+ return this.sp00ky;
371
591
  }
372
592
  /**
373
- * Initialize the spooky-ts instance
593
+ * Initialize the sp00ky-ts instance
374
594
  */
375
595
  async init() {
376
596
  if (this._initialized) return;
377
- this.spooky = new SpookyClient(this.config);
378
- await this.spooky.init();
597
+ this.sp00ky = new Sp00kyClient(this.config);
598
+ await this.sp00ky.init();
379
599
  this._initialized = true;
380
600
  }
381
601
  /**
382
602
  * Create a new record in the database
383
603
  */
384
604
  async create(id, payload) {
385
- if (!this.spooky) throw new Error("SyncedDb not initialized");
386
- await this.spooky.create(id, payload);
605
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
606
+ await this.sp00ky.create(id, payload);
387
607
  }
388
608
  /**
389
609
  * Update an existing record in the database
390
610
  */
391
611
  async update(tableName, recordId, payload, options) {
392
- if (!this.spooky) throw new Error("SyncedDb not initialized");
393
- await this.spooky.update(tableName, recordId, payload, options);
612
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
613
+ await this.sp00ky.update(tableName, recordId, payload, options);
394
614
  }
395
615
  /**
396
616
  * Delete an existing record in the database
397
617
  */
398
618
  async delete(tableName, selector) {
399
- if (!this.spooky) throw new Error("SyncedDb not initialized");
400
- if (typeof selector !== "string") throw new Error("Only string ID selectors are supported currently with core");
401
- await this.spooky.delete(tableName, selector);
619
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
620
+ const isRecordId = selector instanceof RecordId || selector?.constructor?.name === "RecordId";
621
+ let id;
622
+ if (typeof selector === "string") id = selector;
623
+ else if (isRecordId) id = `${tableName}:${selector.id}`;
624
+ else throw new Error("Only string ID or RecordId selectors are supported currently with core");
625
+ await this.sp00ky.delete(tableName, id);
626
+ }
627
+ /**
628
+ * Preload/prewarm a built query into the local cache without registering a
629
+ * live view. Fetches once and stores the rows (+ embedded related children)
630
+ * locally so a later `useQuery` for the same data paints instantly. Best-effort.
631
+ */
632
+ async preload(finalQuery, options) {
633
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
634
+ await this.sp00ky.preload(finalQuery, options);
402
635
  }
403
636
  /**
404
637
  * Query data from the database
405
638
  */
406
639
  query(table) {
407
- if (!this.spooky) throw new Error("SyncedDb not initialized");
408
- return this.spooky.query(table, {});
640
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
641
+ return this.sp00ky.query(table, {});
409
642
  }
410
643
  /**
411
644
  * Run a backend operation
412
645
  */
413
646
  async run(backend, path, payload, options) {
414
- if (!this.spooky) throw new Error("SyncedDb not initialized");
415
- await this.spooky.run(backend, path, payload, options);
647
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
648
+ await this.sp00ky.run(backend, path, payload, options);
416
649
  }
417
650
  /**
418
651
  * Authenticate with the database
419
652
  */
420
653
  async authenticate(token) {
421
- await this.spooky?.authenticate(token);
654
+ await this.sp00ky?.authenticate(token);
422
655
  return new RecordId("user", "me");
423
656
  }
424
657
  /**
@@ -432,48 +665,67 @@ var SyncedDb = class {
432
665
  * Sign out, clear session and local storage
433
666
  */
434
667
  async signOut() {
435
- if (!this.spooky) throw new Error("SyncedDb not initialized");
436
- await this.spooky.auth.signOut();
668
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
669
+ await this.sp00ky.auth.signOut();
437
670
  }
438
671
  /**
439
672
  * Execute a function with direct access to the remote database connection
440
673
  */
441
674
  async useRemote(fn) {
442
- if (!this.spooky) throw new Error("SyncedDb not initialized");
443
- return await this.spooky.useRemote(fn);
675
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
676
+ return await this.sp00ky.useRemote(fn);
444
677
  }
445
678
  /**
446
679
  * Access the remote database service directly
447
680
  */
448
681
  get remote() {
449
- if (!this.spooky) throw new Error("SyncedDb not initialized");
450
- return this.spooky.remoteClient;
682
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
683
+ return this.sp00ky.remoteClient;
451
684
  }
452
685
  /**
453
686
  * Access the local database service directly
454
687
  */
455
688
  get local() {
456
- if (!this.spooky) throw new Error("SyncedDb not initialized");
457
- return this.spooky.localClient;
689
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
690
+ return this.sp00ky.localClient;
458
691
  }
459
692
  /**
460
693
  * Access the auth service
461
694
  */
462
695
  get auth() {
463
- if (!this.spooky) throw new Error("SyncedDb not initialized");
464
- return this.spooky.auth;
696
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
697
+ return this.sp00ky.auth;
465
698
  }
466
699
  get pendingMutationCount() {
467
- if (!this.spooky) throw new Error("SyncedDb not initialized");
468
- return this.spooky.pendingMutationCount;
700
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
701
+ return this.sp00ky.pendingMutationCount;
702
+ }
703
+ /** Diagnostic — see `Sp00kyClient.liveRetryCount`. */
704
+ get liveRetryCount() {
705
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
706
+ return this.sp00ky.liveRetryCount;
469
707
  }
470
708
  subscribeToPendingMutations(cb) {
471
- if (!this.spooky) throw new Error("SyncedDb not initialized");
472
- return this.spooky.subscribeToPendingMutations(cb);
709
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
710
+ return this.sp00ky.subscribeToPendingMutations(cb);
711
+ }
712
+ /** Current sync-health snapshot. See {@link useSyncStatus}. */
713
+ get syncHealth() {
714
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
715
+ return this.sp00ky.syncHealth;
716
+ }
717
+ /**
718
+ * Observe sync health. Fires immediately with the current status and again
719
+ * on every healthy↔degraded transition. Prefer the `useSyncStatus` hook in
720
+ * components; this is the imperative escape hatch.
721
+ */
722
+ subscribeToSyncHealth(cb) {
723
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
724
+ return this.sp00ky.subscribeToSyncHealth(cb);
473
725
  }
474
726
  bucket(name) {
475
- if (!this.spooky) throw new Error("SyncedDb not initialized");
476
- return this.spooky.bucket(name);
727
+ if (!this.sp00ky) throw new Error("SyncedDb not initialized");
728
+ return this.sp00ky.bucket(name);
477
729
  }
478
730
  getBucketConfig(name) {
479
731
  return this.config.schema.buckets?.find((b) => b.name === name);
@@ -481,5 +733,5 @@ var SyncedDb = class {
481
733
  };
482
734
 
483
735
  //#endregion
484
- export { RecordId, SpookyProvider, SyncedDb, Uuid, useDb, useDownloadFile, useFileUpload, useQuery };
736
+ export { RecordId, Sp00kyProvider, SyncedDb, Uuid, createPreload, useAppRelease, useCrdtField, useDb, useDownloadFile, useFeatureFlag, useFileUpload, useQuery, useSyncStatus };
485
737
  //# sourceMappingURL=index.js.map