@spooky-sync/client-solid 0.0.1-canary.13 → 0.0.1-canary.130
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 +68 -0
- package/README.md +20 -0
- package/dist/index.cjs +275 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +129 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +129 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +272 -66
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/skills/sp00ky-solid/SKILL.md +335 -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 +121 -55
- package/src/lib/Sp00kyProvider.ts +76 -0
- package/src/lib/context.ts +3 -3
- package/src/lib/create-preload.ts +111 -0
- 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/src/lib/SpookyProvider.ts +0 -55
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,165 @@ 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/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
|
+
}
|
|
72
242
|
};
|
|
73
243
|
}
|
|
74
244
|
|
|
@@ -94,7 +264,7 @@ function useFileUpload(dbOrBucketName, maybeBucketName) {
|
|
|
94
264
|
const validate = (file) => {
|
|
95
265
|
const config = db.getBucketConfig(bucketName);
|
|
96
266
|
if (!config) return;
|
|
97
|
-
if (config.maxSize
|
|
267
|
+
if (config.maxSize !== null && config.maxSize !== void 0 && file.size > config.maxSize) {
|
|
98
268
|
const maxMB = (config.maxSize / (1024 * 1024)).toFixed(1);
|
|
99
269
|
throw new Error(`File exceeds maximum size of ${maxMB} MB.`);
|
|
100
270
|
}
|
|
@@ -203,9 +373,8 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
|
|
|
203
373
|
const [error, setError] = createSignal(null);
|
|
204
374
|
let currentKey = null;
|
|
205
375
|
let privateUrl = null;
|
|
206
|
-
let refetchTrigger;
|
|
207
376
|
const [refetchSignal, setRefetchSignal] = createSignal(0);
|
|
208
|
-
refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
377
|
+
const refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
209
378
|
async function doDownload(key, filePath) {
|
|
210
379
|
if (useCache) {
|
|
211
380
|
const cached = downloadCache.get(key);
|
|
@@ -325,26 +494,31 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
|
|
|
325
494
|
}
|
|
326
495
|
|
|
327
496
|
//#endregion
|
|
328
|
-
//#region src/lib/
|
|
329
|
-
function
|
|
497
|
+
//#region src/lib/Sp00kyProvider.ts
|
|
498
|
+
function Sp00kyProvider(props) {
|
|
330
499
|
const merged = mergeProps({ fallback: void 0 }, props);
|
|
331
500
|
const [db, setDb] = createSignal(void 0);
|
|
332
501
|
onMount(async () => {
|
|
333
502
|
try {
|
|
334
503
|
const instance = new SyncedDb(merged.config);
|
|
335
504
|
await instance.init();
|
|
505
|
+
if (merged.preload) try {
|
|
506
|
+
await merged.preload(instance);
|
|
507
|
+
} catch (e) {
|
|
508
|
+
console.error("Sp00kyProvider: preload failed; revealing UI anyway", e);
|
|
509
|
+
}
|
|
336
510
|
setDb(() => instance);
|
|
337
511
|
merged.onReady?.(instance);
|
|
338
512
|
} catch (e) {
|
|
339
513
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
340
514
|
if (merged.onError) merged.onError(error);
|
|
341
|
-
else console.error("
|
|
515
|
+
else console.error("Sp00kyProvider: Failed to initialize database", error);
|
|
342
516
|
}
|
|
343
517
|
});
|
|
344
518
|
return createMemo(() => {
|
|
345
519
|
const instance = db();
|
|
346
520
|
if (!instance) return merged.fallback;
|
|
347
|
-
return createComponent(
|
|
521
|
+
return createComponent(Sp00kyContext.Provider, {
|
|
348
522
|
value: instance,
|
|
349
523
|
get children() {
|
|
350
524
|
return merged.children;
|
|
@@ -356,69 +530,82 @@ function SpookyProvider(props) {
|
|
|
356
530
|
//#endregion
|
|
357
531
|
//#region src/index.ts
|
|
358
532
|
/**
|
|
359
|
-
* SyncedDb - A thin wrapper around
|
|
360
|
-
* Delegates all logic to the underlying
|
|
533
|
+
* SyncedDb - A thin wrapper around sp00ky-ts for Solid.js integration
|
|
534
|
+
* Delegates all logic to the underlying sp00ky-ts instance
|
|
361
535
|
*/
|
|
362
536
|
var SyncedDb = class {
|
|
363
537
|
constructor(config) {
|
|
364
|
-
this.
|
|
538
|
+
this.sp00ky = null;
|
|
365
539
|
this._initialized = false;
|
|
366
540
|
this.config = config;
|
|
367
541
|
}
|
|
368
|
-
|
|
369
|
-
if (!this.
|
|
370
|
-
return this.
|
|
542
|
+
getSp00ky() {
|
|
543
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
544
|
+
return this.sp00ky;
|
|
371
545
|
}
|
|
372
546
|
/**
|
|
373
|
-
* Initialize the
|
|
547
|
+
* Initialize the sp00ky-ts instance
|
|
374
548
|
*/
|
|
375
549
|
async init() {
|
|
376
550
|
if (this._initialized) return;
|
|
377
|
-
this.
|
|
378
|
-
await this.
|
|
551
|
+
this.sp00ky = new Sp00kyClient(this.config);
|
|
552
|
+
await this.sp00ky.init();
|
|
379
553
|
this._initialized = true;
|
|
380
554
|
}
|
|
381
555
|
/**
|
|
382
556
|
* Create a new record in the database
|
|
383
557
|
*/
|
|
384
558
|
async create(id, payload) {
|
|
385
|
-
if (!this.
|
|
386
|
-
await this.
|
|
559
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
560
|
+
await this.sp00ky.create(id, payload);
|
|
387
561
|
}
|
|
388
562
|
/**
|
|
389
563
|
* Update an existing record in the database
|
|
390
564
|
*/
|
|
391
565
|
async update(tableName, recordId, payload, options) {
|
|
392
|
-
if (!this.
|
|
393
|
-
await this.
|
|
566
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
567
|
+
await this.sp00ky.update(tableName, recordId, payload, options);
|
|
394
568
|
}
|
|
395
569
|
/**
|
|
396
570
|
* Delete an existing record in the database
|
|
397
571
|
*/
|
|
398
572
|
async delete(tableName, selector) {
|
|
399
|
-
if (!this.
|
|
400
|
-
|
|
401
|
-
|
|
573
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
574
|
+
const isRecordId = selector instanceof RecordId || selector?.constructor?.name === "RecordId";
|
|
575
|
+
let id;
|
|
576
|
+
if (typeof selector === "string") id = selector;
|
|
577
|
+
else if (isRecordId) id = `${tableName}:${selector.id}`;
|
|
578
|
+
else throw new Error("Only string ID or RecordId selectors are supported currently with core");
|
|
579
|
+
await this.sp00ky.delete(tableName, id);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Preload/prewarm a built query into the local cache without registering a
|
|
583
|
+
* live view. Fetches once and stores the rows (+ embedded related children)
|
|
584
|
+
* locally so a later `useQuery` for the same data paints instantly. Best-effort.
|
|
585
|
+
*/
|
|
586
|
+
async preload(finalQuery, options) {
|
|
587
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
588
|
+
await this.sp00ky.preload(finalQuery, options);
|
|
402
589
|
}
|
|
403
590
|
/**
|
|
404
591
|
* Query data from the database
|
|
405
592
|
*/
|
|
406
593
|
query(table) {
|
|
407
|
-
if (!this.
|
|
408
|
-
return this.
|
|
594
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
595
|
+
return this.sp00ky.query(table, {});
|
|
409
596
|
}
|
|
410
597
|
/**
|
|
411
598
|
* Run a backend operation
|
|
412
599
|
*/
|
|
413
600
|
async run(backend, path, payload, options) {
|
|
414
|
-
if (!this.
|
|
415
|
-
await this.
|
|
601
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
602
|
+
await this.sp00ky.run(backend, path, payload, options);
|
|
416
603
|
}
|
|
417
604
|
/**
|
|
418
605
|
* Authenticate with the database
|
|
419
606
|
*/
|
|
420
607
|
async authenticate(token) {
|
|
421
|
-
await this.
|
|
608
|
+
await this.sp00ky?.authenticate(token);
|
|
422
609
|
return new RecordId("user", "me");
|
|
423
610
|
}
|
|
424
611
|
/**
|
|
@@ -432,48 +619,67 @@ var SyncedDb = class {
|
|
|
432
619
|
* Sign out, clear session and local storage
|
|
433
620
|
*/
|
|
434
621
|
async signOut() {
|
|
435
|
-
if (!this.
|
|
436
|
-
await this.
|
|
622
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
623
|
+
await this.sp00ky.auth.signOut();
|
|
437
624
|
}
|
|
438
625
|
/**
|
|
439
626
|
* Execute a function with direct access to the remote database connection
|
|
440
627
|
*/
|
|
441
628
|
async useRemote(fn) {
|
|
442
|
-
if (!this.
|
|
443
|
-
return await this.
|
|
629
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
630
|
+
return await this.sp00ky.useRemote(fn);
|
|
444
631
|
}
|
|
445
632
|
/**
|
|
446
633
|
* Access the remote database service directly
|
|
447
634
|
*/
|
|
448
635
|
get remote() {
|
|
449
|
-
if (!this.
|
|
450
|
-
return this.
|
|
636
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
637
|
+
return this.sp00ky.remoteClient;
|
|
451
638
|
}
|
|
452
639
|
/**
|
|
453
640
|
* Access the local database service directly
|
|
454
641
|
*/
|
|
455
642
|
get local() {
|
|
456
|
-
if (!this.
|
|
457
|
-
return this.
|
|
643
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
644
|
+
return this.sp00ky.localClient;
|
|
458
645
|
}
|
|
459
646
|
/**
|
|
460
647
|
* Access the auth service
|
|
461
648
|
*/
|
|
462
649
|
get auth() {
|
|
463
|
-
if (!this.
|
|
464
|
-
return this.
|
|
650
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
651
|
+
return this.sp00ky.auth;
|
|
465
652
|
}
|
|
466
653
|
get pendingMutationCount() {
|
|
467
|
-
if (!this.
|
|
468
|
-
return this.
|
|
654
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
655
|
+
return this.sp00ky.pendingMutationCount;
|
|
656
|
+
}
|
|
657
|
+
/** Diagnostic — see `Sp00kyClient.liveRetryCount`. */
|
|
658
|
+
get liveRetryCount() {
|
|
659
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
660
|
+
return this.sp00ky.liveRetryCount;
|
|
469
661
|
}
|
|
470
662
|
subscribeToPendingMutations(cb) {
|
|
471
|
-
if (!this.
|
|
472
|
-
return this.
|
|
663
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
664
|
+
return this.sp00ky.subscribeToPendingMutations(cb);
|
|
665
|
+
}
|
|
666
|
+
/** Current sync-health snapshot. See {@link useSyncStatus}. */
|
|
667
|
+
get syncHealth() {
|
|
668
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
669
|
+
return this.sp00ky.syncHealth;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Observe sync health. Fires immediately with the current status and again
|
|
673
|
+
* on every healthy↔degraded transition. Prefer the `useSyncStatus` hook in
|
|
674
|
+
* components; this is the imperative escape hatch.
|
|
675
|
+
*/
|
|
676
|
+
subscribeToSyncHealth(cb) {
|
|
677
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
678
|
+
return this.sp00ky.subscribeToSyncHealth(cb);
|
|
473
679
|
}
|
|
474
680
|
bucket(name) {
|
|
475
|
-
if (!this.
|
|
476
|
-
return this.
|
|
681
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
682
|
+
return this.sp00ky.bucket(name);
|
|
477
683
|
}
|
|
478
684
|
getBucketConfig(name) {
|
|
479
685
|
return this.config.schema.buckets?.find((b) => b.name === name);
|
|
@@ -481,5 +687,5 @@ var SyncedDb = class {
|
|
|
481
687
|
};
|
|
482
688
|
|
|
483
689
|
//#endregion
|
|
484
|
-
export { RecordId,
|
|
690
|
+
export { RecordId, Sp00kyProvider, SyncedDb, Uuid, createPreload, useCrdtField, useDb, useDownloadFile, useFeatureFlag, useFileUpload, useQuery, useSyncStatus };
|
|
485
691
|
//# sourceMappingURL=index.js.map
|