@spooky-sync/client-solid 0.0.1-canary.8 → 0.0.1-canary.81
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 +177 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +57 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +176 -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 +84 -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 +130 -28
- package/src/types/index.ts +3 -4
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/lib/models.ts","../../../src/types/index.ts","../../../src/lib/use-query.ts","../../../src/lib/use-file-upload.ts","../../../src/lib/use-download-file.ts","../../../src/lib/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/lib/models.ts","../../../src/types/index.ts","../../../src/lib/use-query.ts","../../../src/lib/use-crdt-field.ts","../../../src/lib/use-feature-flag.ts","../../../src/lib/use-file-upload.ts","../../../src/lib/use-download-file.ts","../../../src/lib/Sp00kyProvider.ts","../../../src/lib/context.ts","../../../src/index.ts"],"sourcesContent":[],"mappings":"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"}
|
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,14 +80,18 @@ 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;
|
|
@@ -68,7 +99,78 @@ function useQuery(dbOrQuery, queryOrOptions, maybeOptions) {
|
|
|
68
99
|
return {
|
|
69
100
|
data,
|
|
70
101
|
error,
|
|
71
|
-
isLoading
|
|
102
|
+
isLoading,
|
|
103
|
+
isFetching
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/lib/use-crdt-field.ts
|
|
109
|
+
function useCrdtField(table, recordId, field, fallbackText) {
|
|
110
|
+
const db = useContext(Sp00kyContext);
|
|
111
|
+
if (!db) throw new Error("useCrdtField must be used within a <Sp00kyProvider>");
|
|
112
|
+
const [crdtField, setCrdtField] = createSignal(null);
|
|
113
|
+
let currentId;
|
|
114
|
+
let initialized = false;
|
|
115
|
+
createEffect(() => {
|
|
116
|
+
const id = recordId();
|
|
117
|
+
if (initialized && id === currentId) return;
|
|
118
|
+
if (currentId && crdtField()) {
|
|
119
|
+
db.getSp00ky().closeCrdtField(table, currentId, field);
|
|
120
|
+
setCrdtField(null);
|
|
121
|
+
}
|
|
122
|
+
currentId = id;
|
|
123
|
+
initialized = true;
|
|
124
|
+
if (!id) return;
|
|
125
|
+
const sp00ky = db.getSp00ky();
|
|
126
|
+
const text = fallbackText?.();
|
|
127
|
+
sp00ky.openCrdtField(table, id, field, text).then((cf) => {
|
|
128
|
+
if (currentId === id) setCrdtField(cf);
|
|
129
|
+
}).catch((err) => {
|
|
130
|
+
console.error(`[useCrdtField] Failed to open CRDT field ${table}.${field} on ${id}:`, err);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
onCleanup(() => {
|
|
134
|
+
if (currentId && crdtField()) {
|
|
135
|
+
db.getSp00ky().closeCrdtField(table, currentId, field);
|
|
136
|
+
setCrdtField(null);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return crdtField;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/lib/use-feature-flag.ts
|
|
144
|
+
/**
|
|
145
|
+
* Subscribe to a feature flag for the currently authenticated user.
|
|
146
|
+
*
|
|
147
|
+
* Returns three Solid accessors that update reactively whenever the
|
|
148
|
+
* server-materialized assignment in `_00_user_feature` changes. Backed by
|
|
149
|
+
* the same SSP + sync pipeline that powers `useQuery`, so toggling a flag
|
|
150
|
+
* via `spky flag enable <key>` propagates to the UI without a refresh.
|
|
151
|
+
*
|
|
152
|
+
* `enabled()` is `true` when the resolved variant exists and is not 'off'.
|
|
153
|
+
* For multi-variant flags, prefer `variant()` directly.
|
|
154
|
+
*/
|
|
155
|
+
function useFeatureFlag(key, options) {
|
|
156
|
+
const handle = useDb().getSp00ky().feature(key, options);
|
|
157
|
+
const [variant, setVariant] = createSignal(handle.variant());
|
|
158
|
+
const [payload, setPayload] = createSignal(handle.payload());
|
|
159
|
+
const unsub = handle.subscribe((s) => {
|
|
160
|
+
setVariant(s.variant ?? options?.fallback);
|
|
161
|
+
setPayload(s.payload);
|
|
162
|
+
});
|
|
163
|
+
onCleanup(() => {
|
|
164
|
+
unsub();
|
|
165
|
+
handle.close();
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
variant,
|
|
169
|
+
payload,
|
|
170
|
+
enabled: () => {
|
|
171
|
+
const v = variant();
|
|
172
|
+
return v !== void 0 && v !== "off";
|
|
173
|
+
}
|
|
72
174
|
};
|
|
73
175
|
}
|
|
74
176
|
|
|
@@ -94,7 +196,7 @@ function useFileUpload(dbOrBucketName, maybeBucketName) {
|
|
|
94
196
|
const validate = (file) => {
|
|
95
197
|
const config = db.getBucketConfig(bucketName);
|
|
96
198
|
if (!config) return;
|
|
97
|
-
if (config.maxSize
|
|
199
|
+
if (config.maxSize !== null && config.maxSize !== void 0 && file.size > config.maxSize) {
|
|
98
200
|
const maxMB = (config.maxSize / (1024 * 1024)).toFixed(1);
|
|
99
201
|
throw new Error(`File exceeds maximum size of ${maxMB} MB.`);
|
|
100
202
|
}
|
|
@@ -203,9 +305,8 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
|
|
|
203
305
|
const [error, setError] = createSignal(null);
|
|
204
306
|
let currentKey = null;
|
|
205
307
|
let privateUrl = null;
|
|
206
|
-
let refetchTrigger;
|
|
207
308
|
const [refetchSignal, setRefetchSignal] = createSignal(0);
|
|
208
|
-
refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
309
|
+
const refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
209
310
|
async function doDownload(key, filePath) {
|
|
210
311
|
if (useCache) {
|
|
211
312
|
const cached = downloadCache.get(key);
|
|
@@ -325,8 +426,8 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
|
|
|
325
426
|
}
|
|
326
427
|
|
|
327
428
|
//#endregion
|
|
328
|
-
//#region src/lib/
|
|
329
|
-
function
|
|
429
|
+
//#region src/lib/Sp00kyProvider.ts
|
|
430
|
+
function Sp00kyProvider(props) {
|
|
330
431
|
const merged = mergeProps({ fallback: void 0 }, props);
|
|
331
432
|
const [db, setDb] = createSignal(void 0);
|
|
332
433
|
onMount(async () => {
|
|
@@ -338,13 +439,13 @@ function SpookyProvider(props) {
|
|
|
338
439
|
} catch (e) {
|
|
339
440
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
340
441
|
if (merged.onError) merged.onError(error);
|
|
341
|
-
else console.error("
|
|
442
|
+
else console.error("Sp00kyProvider: Failed to initialize database", error);
|
|
342
443
|
}
|
|
343
444
|
});
|
|
344
445
|
return createMemo(() => {
|
|
345
446
|
const instance = db();
|
|
346
447
|
if (!instance) return merged.fallback;
|
|
347
|
-
return createComponent(
|
|
448
|
+
return createComponent(Sp00kyContext.Provider, {
|
|
348
449
|
value: instance,
|
|
349
450
|
get children() {
|
|
350
451
|
return merged.children;
|
|
@@ -356,69 +457,73 @@ function SpookyProvider(props) {
|
|
|
356
457
|
//#endregion
|
|
357
458
|
//#region src/index.ts
|
|
358
459
|
/**
|
|
359
|
-
* SyncedDb - A thin wrapper around
|
|
360
|
-
* Delegates all logic to the underlying
|
|
460
|
+
* SyncedDb - A thin wrapper around sp00ky-ts for Solid.js integration
|
|
461
|
+
* Delegates all logic to the underlying sp00ky-ts instance
|
|
361
462
|
*/
|
|
362
463
|
var SyncedDb = class {
|
|
363
464
|
constructor(config) {
|
|
364
|
-
this.
|
|
465
|
+
this.sp00ky = null;
|
|
365
466
|
this._initialized = false;
|
|
366
467
|
this.config = config;
|
|
367
468
|
}
|
|
368
|
-
|
|
369
|
-
if (!this.
|
|
370
|
-
return this.
|
|
469
|
+
getSp00ky() {
|
|
470
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
471
|
+
return this.sp00ky;
|
|
371
472
|
}
|
|
372
473
|
/**
|
|
373
|
-
* Initialize the
|
|
474
|
+
* Initialize the sp00ky-ts instance
|
|
374
475
|
*/
|
|
375
476
|
async init() {
|
|
376
477
|
if (this._initialized) return;
|
|
377
|
-
this.
|
|
378
|
-
await this.
|
|
478
|
+
this.sp00ky = new Sp00kyClient(this.config);
|
|
479
|
+
await this.sp00ky.init();
|
|
379
480
|
this._initialized = true;
|
|
380
481
|
}
|
|
381
482
|
/**
|
|
382
483
|
* Create a new record in the database
|
|
383
484
|
*/
|
|
384
485
|
async create(id, payload) {
|
|
385
|
-
if (!this.
|
|
386
|
-
await this.
|
|
486
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
487
|
+
await this.sp00ky.create(id, payload);
|
|
387
488
|
}
|
|
388
489
|
/**
|
|
389
490
|
* Update an existing record in the database
|
|
390
491
|
*/
|
|
391
492
|
async update(tableName, recordId, payload, options) {
|
|
392
|
-
if (!this.
|
|
393
|
-
await this.
|
|
493
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
494
|
+
await this.sp00ky.update(tableName, recordId, payload, options);
|
|
394
495
|
}
|
|
395
496
|
/**
|
|
396
497
|
* Delete an existing record in the database
|
|
397
498
|
*/
|
|
398
499
|
async delete(tableName, selector) {
|
|
399
|
-
if (!this.
|
|
400
|
-
|
|
401
|
-
|
|
500
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
501
|
+
const isRecordId = selector instanceof RecordId || selector?.constructor?.name === "RecordId";
|
|
502
|
+
let id;
|
|
503
|
+
if (typeof selector === "string") id = selector;
|
|
504
|
+
else if (isRecordId) id = `${tableName}:${selector.id}`;
|
|
505
|
+
else throw new Error("Only string ID or RecordId selectors are supported currently with core");
|
|
506
|
+
await this.sp00ky.delete(tableName, id);
|
|
402
507
|
}
|
|
403
508
|
/**
|
|
404
509
|
* Query data from the database
|
|
405
510
|
*/
|
|
406
511
|
query(table) {
|
|
407
|
-
if (!this.
|
|
408
|
-
return this.
|
|
512
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
513
|
+
return this.sp00ky.query(table, {});
|
|
409
514
|
}
|
|
410
515
|
/**
|
|
411
516
|
* Run a backend operation
|
|
412
517
|
*/
|
|
413
518
|
async run(backend, path, payload, options) {
|
|
414
|
-
if (!this.
|
|
415
|
-
await this.
|
|
519
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
520
|
+
await this.sp00ky.run(backend, path, payload, options);
|
|
416
521
|
}
|
|
417
522
|
/**
|
|
418
523
|
* Authenticate with the database
|
|
419
524
|
*/
|
|
420
525
|
async authenticate(token) {
|
|
421
|
-
await this.
|
|
526
|
+
await this.sp00ky?.authenticate(token);
|
|
422
527
|
return new RecordId("user", "me");
|
|
423
528
|
}
|
|
424
529
|
/**
|
|
@@ -432,48 +537,53 @@ var SyncedDb = class {
|
|
|
432
537
|
* Sign out, clear session and local storage
|
|
433
538
|
*/
|
|
434
539
|
async signOut() {
|
|
435
|
-
if (!this.
|
|
436
|
-
await this.
|
|
540
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
541
|
+
await this.sp00ky.auth.signOut();
|
|
437
542
|
}
|
|
438
543
|
/**
|
|
439
544
|
* Execute a function with direct access to the remote database connection
|
|
440
545
|
*/
|
|
441
546
|
async useRemote(fn) {
|
|
442
|
-
if (!this.
|
|
443
|
-
return await this.
|
|
547
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
548
|
+
return await this.sp00ky.useRemote(fn);
|
|
444
549
|
}
|
|
445
550
|
/**
|
|
446
551
|
* Access the remote database service directly
|
|
447
552
|
*/
|
|
448
553
|
get remote() {
|
|
449
|
-
if (!this.
|
|
450
|
-
return this.
|
|
554
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
555
|
+
return this.sp00ky.remoteClient;
|
|
451
556
|
}
|
|
452
557
|
/**
|
|
453
558
|
* Access the local database service directly
|
|
454
559
|
*/
|
|
455
560
|
get local() {
|
|
456
|
-
if (!this.
|
|
457
|
-
return this.
|
|
561
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
562
|
+
return this.sp00ky.localClient;
|
|
458
563
|
}
|
|
459
564
|
/**
|
|
460
565
|
* Access the auth service
|
|
461
566
|
*/
|
|
462
567
|
get auth() {
|
|
463
|
-
if (!this.
|
|
464
|
-
return this.
|
|
568
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
569
|
+
return this.sp00ky.auth;
|
|
465
570
|
}
|
|
466
571
|
get pendingMutationCount() {
|
|
467
|
-
if (!this.
|
|
468
|
-
return this.
|
|
572
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
573
|
+
return this.sp00ky.pendingMutationCount;
|
|
574
|
+
}
|
|
575
|
+
/** Diagnostic — see `Sp00kyClient.liveRetryCount`. */
|
|
576
|
+
get liveRetryCount() {
|
|
577
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
578
|
+
return this.sp00ky.liveRetryCount;
|
|
469
579
|
}
|
|
470
580
|
subscribeToPendingMutations(cb) {
|
|
471
|
-
if (!this.
|
|
472
|
-
return this.
|
|
581
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
582
|
+
return this.sp00ky.subscribeToPendingMutations(cb);
|
|
473
583
|
}
|
|
474
584
|
bucket(name) {
|
|
475
|
-
if (!this.
|
|
476
|
-
return this.
|
|
585
|
+
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
586
|
+
return this.sp00ky.bucket(name);
|
|
477
587
|
}
|
|
478
588
|
getBucketConfig(name) {
|
|
479
589
|
return this.config.schema.buckets?.find((b) => b.name === name);
|
|
@@ -481,5 +591,5 @@ var SyncedDb = class {
|
|
|
481
591
|
};
|
|
482
592
|
|
|
483
593
|
//#endregion
|
|
484
|
-
export { RecordId,
|
|
594
|
+
export { RecordId, Sp00kyProvider, SyncedDb, Uuid, useCrdtField, useDb, useDownloadFile, useFeatureFlag, useFileUpload, useQuery };
|
|
485
595
|
//# sourceMappingURL=index.js.map
|