@spooky-sync/client-solid 0.0.1-canary.11 → 0.0.1-canary.111
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/AGENTS.md +66 -0
- package/dist/index.cjs +220 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +99 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +218 -66
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/skills/sp00ky-solid/SKILL.md +264 -0
- package/skills/sp00ky-solid/references/file-hooks.md +112 -0
- package/src/cache/index.ts +1 -1
- package/src/cache/surrealdb-wasm-factory.ts +4 -1
- package/src/index.ts +103 -55
- package/src/lib/{SpookyProvider.ts → Sp00kyProvider.ts} +9 -7
- package/src/lib/context.ts +3 -3
- package/src/lib/models.ts +1 -1
- package/src/lib/use-crdt-field.ts +68 -0
- package/src/lib/use-download-file.ts +2 -2
- package/src/lib/use-feature-flag.ts +50 -0
- package/src/lib/use-file-upload.ts +2 -1
- package/src/lib/use-query.ts +143 -28
- package/src/lib/use-sync-status.ts +50 -0
- package/src/types/index.ts +3 -4
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Sp00kyClient, fileToUint8Array } 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
|
|
7
|
+
const Sp00kyContext = createContext();
|
|
7
8
|
function useDb() {
|
|
8
|
-
const db = useContext(
|
|
9
|
-
if (!db) throw new Error("useDb must be used within a <
|
|
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(
|
|
25
|
-
if (!contextDb) throw new Error("useQuery: No db argument provided and no
|
|
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 [
|
|
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
|
-
|
|
36
|
-
|
|
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
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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,125 @@ function useQuery(dbOrQuery, queryOrOptions, maybeOptions) {
|
|
|
53
80
|
}
|
|
54
81
|
const query = typeof finalQuery === "function" ? finalQuery() : finalQuery;
|
|
55
82
|
if (!query) return;
|
|
56
|
-
const queryString =
|
|
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
|
-
|
|
62
|
-
|
|
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/use-sync-status.ts
|
|
111
|
+
/**
|
|
112
|
+
* Observe sync health for a "can't reach the server" banner / indicator.
|
|
113
|
+
*
|
|
114
|
+
* Backed by `db.subscribeToSyncHealth`. Individual sync failures (a transient
|
|
115
|
+
* remote 500 on query registration, a dropped socket) are absorbed by the
|
|
116
|
+
* retry and never flip this; `isDegraded()` only goes true once failures
|
|
117
|
+
* persist for the configured number of consecutive rounds (sp00ky core config
|
|
118
|
+
* `syncHealth.degradeAfterConsecutiveFailures`, default 3), and flips back on
|
|
119
|
+
* the next successful round. Must be used within a `<Sp00kyProvider>`.
|
|
120
|
+
*/
|
|
121
|
+
function useSyncStatus() {
|
|
122
|
+
const db = useDb();
|
|
123
|
+
const [health, setHealth] = createSignal(db.syncHealth);
|
|
124
|
+
onCleanup(db.subscribeToSyncHealth(setHealth));
|
|
125
|
+
return {
|
|
126
|
+
health,
|
|
127
|
+
status: () => health().status,
|
|
128
|
+
isHealthy: () => health().status === "healthy",
|
|
129
|
+
isDegraded: () => health().status === "degraded",
|
|
130
|
+
everConnected: () => health().everConnected,
|
|
131
|
+
isOffline: () => health().status === "degraded" && health().everConnected
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/lib/use-crdt-field.ts
|
|
137
|
+
function useCrdtField(table, recordId, field, fallbackText) {
|
|
138
|
+
const db = useContext(Sp00kyContext);
|
|
139
|
+
if (!db) throw new Error("useCrdtField must be used within a <Sp00kyProvider>");
|
|
140
|
+
const [crdtField, setCrdtField] = createSignal(null);
|
|
141
|
+
let currentId;
|
|
142
|
+
let initialized = false;
|
|
143
|
+
createEffect(() => {
|
|
144
|
+
const id = recordId();
|
|
145
|
+
if (initialized && id === currentId) return;
|
|
146
|
+
if (currentId && crdtField()) {
|
|
147
|
+
db.getSp00ky().closeCrdtField(table, currentId, field);
|
|
148
|
+
setCrdtField(null);
|
|
149
|
+
}
|
|
150
|
+
currentId = id;
|
|
151
|
+
initialized = true;
|
|
152
|
+
if (!id) return;
|
|
153
|
+
const sp00ky = db.getSp00ky();
|
|
154
|
+
const text = fallbackText?.();
|
|
155
|
+
sp00ky.openCrdtField(table, id, field, text).then((cf) => {
|
|
156
|
+
if (currentId === id) setCrdtField(cf);
|
|
157
|
+
}).catch((err) => {
|
|
158
|
+
console.error(`[useCrdtField] Failed to open CRDT field ${table}.${field} on ${id}:`, err);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
onCleanup(() => {
|
|
162
|
+
if (currentId && crdtField()) {
|
|
163
|
+
db.getSp00ky().closeCrdtField(table, currentId, field);
|
|
164
|
+
setCrdtField(null);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
return crdtField;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/lib/use-feature-flag.ts
|
|
172
|
+
/**
|
|
173
|
+
* Subscribe to a feature flag for the currently authenticated user.
|
|
174
|
+
*
|
|
175
|
+
* Returns three Solid accessors that update reactively whenever the
|
|
176
|
+
* server-materialized assignment in `_00_user_feature` changes. Backed by
|
|
177
|
+
* the same SSP + sync pipeline that powers `useQuery`, so toggling a flag
|
|
178
|
+
* via `spky flag enable <key>` propagates to the UI without a refresh.
|
|
179
|
+
*
|
|
180
|
+
* `enabled()` is `true` when the resolved variant exists and is not 'off'.
|
|
181
|
+
* For multi-variant flags, prefer `variant()` directly.
|
|
182
|
+
*/
|
|
183
|
+
function useFeatureFlag(key, options) {
|
|
184
|
+
const handle = useDb().getSp00ky().feature(key, options);
|
|
185
|
+
const [variant, setVariant] = createSignal(handle.variant());
|
|
186
|
+
const [payload, setPayload] = createSignal(handle.payload());
|
|
187
|
+
const unsub = handle.subscribe((s) => {
|
|
188
|
+
setVariant(s.variant ?? options?.fallback);
|
|
189
|
+
setPayload(s.payload);
|
|
190
|
+
});
|
|
191
|
+
onCleanup(() => {
|
|
192
|
+
unsub();
|
|
193
|
+
handle.close();
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
variant,
|
|
197
|
+
payload,
|
|
198
|
+
enabled: () => {
|
|
199
|
+
const v = variant();
|
|
200
|
+
return v !== void 0 && v !== "off";
|
|
201
|
+
}
|
|
72
202
|
};
|
|
73
203
|
}
|
|
74
204
|
|
|
@@ -94,7 +224,7 @@ function useFileUpload(dbOrBucketName, maybeBucketName) {
|
|
|
94
224
|
const validate = (file) => {
|
|
95
225
|
const config = db.getBucketConfig(bucketName);
|
|
96
226
|
if (!config) return;
|
|
97
|
-
if (config.maxSize
|
|
227
|
+
if (config.maxSize !== null && config.maxSize !== void 0 && file.size > config.maxSize) {
|
|
98
228
|
const maxMB = (config.maxSize / (1024 * 1024)).toFixed(1);
|
|
99
229
|
throw new Error(`File exceeds maximum size of ${maxMB} MB.`);
|
|
100
230
|
}
|
|
@@ -203,9 +333,8 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
|
|
|
203
333
|
const [error, setError] = createSignal(null);
|
|
204
334
|
let currentKey = null;
|
|
205
335
|
let privateUrl = null;
|
|
206
|
-
let refetchTrigger;
|
|
207
336
|
const [refetchSignal, setRefetchSignal] = createSignal(0);
|
|
208
|
-
refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
337
|
+
const refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
209
338
|
async function doDownload(key, filePath) {
|
|
210
339
|
if (useCache) {
|
|
211
340
|
const cached = downloadCache.get(key);
|
|
@@ -325,8 +454,8 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
|
|
|
325
454
|
}
|
|
326
455
|
|
|
327
456
|
//#endregion
|
|
328
|
-
//#region src/lib/
|
|
329
|
-
function
|
|
457
|
+
//#region src/lib/Sp00kyProvider.ts
|
|
458
|
+
function Sp00kyProvider(props) {
|
|
330
459
|
const merged = mergeProps({ fallback: void 0 }, props);
|
|
331
460
|
const [db, setDb] = createSignal(void 0);
|
|
332
461
|
onMount(async () => {
|
|
@@ -338,13 +467,13 @@ function SpookyProvider(props) {
|
|
|
338
467
|
} catch (e) {
|
|
339
468
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
340
469
|
if (merged.onError) merged.onError(error);
|
|
341
|
-
else console.error("
|
|
470
|
+
else console.error("Sp00kyProvider: Failed to initialize database", error);
|
|
342
471
|
}
|
|
343
472
|
});
|
|
344
473
|
return createMemo(() => {
|
|
345
474
|
const instance = db();
|
|
346
475
|
if (!instance) return merged.fallback;
|
|
347
|
-
return createComponent(
|
|
476
|
+
return createComponent(Sp00kyContext.Provider, {
|
|
348
477
|
value: instance,
|
|
349
478
|
get children() {
|
|
350
479
|
return merged.children;
|
|
@@ -356,69 +485,73 @@ function SpookyProvider(props) {
|
|
|
356
485
|
//#endregion
|
|
357
486
|
//#region src/index.ts
|
|
358
487
|
/**
|
|
359
|
-
* SyncedDb - A thin wrapper around
|
|
360
|
-
* Delegates all logic to the underlying
|
|
488
|
+
* SyncedDb - A thin wrapper around sp00ky-ts for Solid.js integration
|
|
489
|
+
* Delegates all logic to the underlying sp00ky-ts instance
|
|
361
490
|
*/
|
|
362
491
|
var SyncedDb = class {
|
|
363
492
|
constructor(config) {
|
|
364
|
-
this.
|
|
493
|
+
this.sp00ky = null;
|
|
365
494
|
this._initialized = false;
|
|
366
495
|
this.config = config;
|
|
367
496
|
}
|
|
368
|
-
|
|
369
|
-
if (!this.
|
|
370
|
-
return this.
|
|
497
|
+
getSp00ky() {
|
|
498
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
499
|
+
return this.sp00ky;
|
|
371
500
|
}
|
|
372
501
|
/**
|
|
373
|
-
* Initialize the
|
|
502
|
+
* Initialize the sp00ky-ts instance
|
|
374
503
|
*/
|
|
375
504
|
async init() {
|
|
376
505
|
if (this._initialized) return;
|
|
377
|
-
this.
|
|
378
|
-
await this.
|
|
506
|
+
this.sp00ky = new Sp00kyClient(this.config);
|
|
507
|
+
await this.sp00ky.init();
|
|
379
508
|
this._initialized = true;
|
|
380
509
|
}
|
|
381
510
|
/**
|
|
382
511
|
* Create a new record in the database
|
|
383
512
|
*/
|
|
384
513
|
async create(id, payload) {
|
|
385
|
-
if (!this.
|
|
386
|
-
await this.
|
|
514
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
515
|
+
await this.sp00ky.create(id, payload);
|
|
387
516
|
}
|
|
388
517
|
/**
|
|
389
518
|
* Update an existing record in the database
|
|
390
519
|
*/
|
|
391
520
|
async update(tableName, recordId, payload, options) {
|
|
392
|
-
if (!this.
|
|
393
|
-
await this.
|
|
521
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
522
|
+
await this.sp00ky.update(tableName, recordId, payload, options);
|
|
394
523
|
}
|
|
395
524
|
/**
|
|
396
525
|
* Delete an existing record in the database
|
|
397
526
|
*/
|
|
398
527
|
async delete(tableName, selector) {
|
|
399
|
-
if (!this.
|
|
400
|
-
|
|
401
|
-
|
|
528
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
529
|
+
const isRecordId = selector instanceof RecordId || selector?.constructor?.name === "RecordId";
|
|
530
|
+
let id;
|
|
531
|
+
if (typeof selector === "string") id = selector;
|
|
532
|
+
else if (isRecordId) id = `${tableName}:${selector.id}`;
|
|
533
|
+
else throw new Error("Only string ID or RecordId selectors are supported currently with core");
|
|
534
|
+
await this.sp00ky.delete(tableName, id);
|
|
402
535
|
}
|
|
403
536
|
/**
|
|
404
537
|
* Query data from the database
|
|
405
538
|
*/
|
|
406
539
|
query(table) {
|
|
407
|
-
if (!this.
|
|
408
|
-
return this.
|
|
540
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
541
|
+
return this.sp00ky.query(table, {});
|
|
409
542
|
}
|
|
410
543
|
/**
|
|
411
544
|
* Run a backend operation
|
|
412
545
|
*/
|
|
413
546
|
async run(backend, path, payload, options) {
|
|
414
|
-
if (!this.
|
|
415
|
-
await this.
|
|
547
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
548
|
+
await this.sp00ky.run(backend, path, payload, options);
|
|
416
549
|
}
|
|
417
550
|
/**
|
|
418
551
|
* Authenticate with the database
|
|
419
552
|
*/
|
|
420
553
|
async authenticate(token) {
|
|
421
|
-
await this.
|
|
554
|
+
await this.sp00ky?.authenticate(token);
|
|
422
555
|
return new RecordId("user", "me");
|
|
423
556
|
}
|
|
424
557
|
/**
|
|
@@ -432,48 +565,67 @@ var SyncedDb = class {
|
|
|
432
565
|
* Sign out, clear session and local storage
|
|
433
566
|
*/
|
|
434
567
|
async signOut() {
|
|
435
|
-
if (!this.
|
|
436
|
-
await this.
|
|
568
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
569
|
+
await this.sp00ky.auth.signOut();
|
|
437
570
|
}
|
|
438
571
|
/**
|
|
439
572
|
* Execute a function with direct access to the remote database connection
|
|
440
573
|
*/
|
|
441
574
|
async useRemote(fn) {
|
|
442
|
-
if (!this.
|
|
443
|
-
return await this.
|
|
575
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
576
|
+
return await this.sp00ky.useRemote(fn);
|
|
444
577
|
}
|
|
445
578
|
/**
|
|
446
579
|
* Access the remote database service directly
|
|
447
580
|
*/
|
|
448
581
|
get remote() {
|
|
449
|
-
if (!this.
|
|
450
|
-
return this.
|
|
582
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
583
|
+
return this.sp00ky.remoteClient;
|
|
451
584
|
}
|
|
452
585
|
/**
|
|
453
586
|
* Access the local database service directly
|
|
454
587
|
*/
|
|
455
588
|
get local() {
|
|
456
|
-
if (!this.
|
|
457
|
-
return this.
|
|
589
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
590
|
+
return this.sp00ky.localClient;
|
|
458
591
|
}
|
|
459
592
|
/**
|
|
460
593
|
* Access the auth service
|
|
461
594
|
*/
|
|
462
595
|
get auth() {
|
|
463
|
-
if (!this.
|
|
464
|
-
return this.
|
|
596
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
597
|
+
return this.sp00ky.auth;
|
|
465
598
|
}
|
|
466
599
|
get pendingMutationCount() {
|
|
467
|
-
if (!this.
|
|
468
|
-
return this.
|
|
600
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
601
|
+
return this.sp00ky.pendingMutationCount;
|
|
602
|
+
}
|
|
603
|
+
/** Diagnostic — see `Sp00kyClient.liveRetryCount`. */
|
|
604
|
+
get liveRetryCount() {
|
|
605
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
606
|
+
return this.sp00ky.liveRetryCount;
|
|
469
607
|
}
|
|
470
608
|
subscribeToPendingMutations(cb) {
|
|
471
|
-
if (!this.
|
|
472
|
-
return this.
|
|
609
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
610
|
+
return this.sp00ky.subscribeToPendingMutations(cb);
|
|
611
|
+
}
|
|
612
|
+
/** Current sync-health snapshot. See {@link useSyncStatus}. */
|
|
613
|
+
get syncHealth() {
|
|
614
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
615
|
+
return this.sp00ky.syncHealth;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Observe sync health. Fires immediately with the current status and again
|
|
619
|
+
* on every healthy↔degraded transition. Prefer the `useSyncStatus` hook in
|
|
620
|
+
* components; this is the imperative escape hatch.
|
|
621
|
+
*/
|
|
622
|
+
subscribeToSyncHealth(cb) {
|
|
623
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
624
|
+
return this.sp00ky.subscribeToSyncHealth(cb);
|
|
473
625
|
}
|
|
474
626
|
bucket(name) {
|
|
475
|
-
if (!this.
|
|
476
|
-
return this.
|
|
627
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
628
|
+
return this.sp00ky.bucket(name);
|
|
477
629
|
}
|
|
478
630
|
getBucketConfig(name) {
|
|
479
631
|
return this.config.schema.buckets?.find((b) => b.name === name);
|
|
@@ -481,5 +633,5 @@ var SyncedDb = class {
|
|
|
481
633
|
};
|
|
482
634
|
|
|
483
635
|
//#endregion
|
|
484
|
-
export { RecordId,
|
|
636
|
+
export { RecordId, Sp00kyProvider, SyncedDb, Uuid, useCrdtField, useDb, useDownloadFile, useFeatureFlag, useFileUpload, useQuery, useSyncStatus };
|
|
485
637
|
//# sourceMappingURL=index.js.map
|