@spooky-sync/client-solid 0.0.1-canary.6 → 0.0.1-canary.61
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.cjs +87 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +25 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -55
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- 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 +60 -52
- 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 +54 -0
- package/src/lib/use-download-file.ts +2 -2
- package/src/lib/use-file-upload.ts +2 -1
- package/src/lib/use-query.ts +14 -14
- package/src/types/index.ts +3 -4
package/src/index.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { SyncedDbConfig } from './types';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type
|
|
7
|
-
UpdateOptions,
|
|
8
|
-
RunOptions,
|
|
3
|
+
Sp00kyClient,
|
|
4
|
+
type Sp00kyQueryResultPromise,
|
|
5
|
+
type AuthService,
|
|
6
|
+
type BucketHandle,
|
|
7
|
+
type UpdateOptions,
|
|
8
|
+
type RunOptions,
|
|
9
9
|
} from '@spooky-sync/core';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import type {
|
|
12
12
|
GetTable,
|
|
13
13
|
QueryBuilder,
|
|
14
14
|
SchemaStructure,
|
|
@@ -25,19 +25,27 @@ import {
|
|
|
25
25
|
RoutePayload,
|
|
26
26
|
BucketNames,
|
|
27
27
|
BucketDefinitionSchema,
|
|
28
|
+
QueryModifier,
|
|
29
|
+
QueryModifierBuilder,
|
|
30
|
+
QueryInfo,
|
|
31
|
+
RelationshipsMetadata,
|
|
32
|
+
RelationshipDefinition,
|
|
33
|
+
InferRelatedModelFromMetadata,
|
|
34
|
+
GetCardinality,
|
|
28
35
|
} from '@spooky-sync/query-builder';
|
|
29
36
|
|
|
30
|
-
import { RecordId, Uuid, Surreal } from 'surrealdb';
|
|
37
|
+
import { RecordId, Uuid, type Surreal } from 'surrealdb';
|
|
31
38
|
export { RecordId, Uuid };
|
|
32
39
|
export type { Model, GenericModel, GenericSchema, ModelPayload } from './lib/models';
|
|
33
40
|
export { useQuery } from './lib/use-query';
|
|
41
|
+
export { useCrdtField } from './lib/use-crdt-field';
|
|
34
42
|
export { useFileUpload, type FileUploadResult } from './lib/use-file-upload';
|
|
35
43
|
export { useDownloadFile, type UseDownloadFileOptions, type UseDownloadFileResult } from './lib/use-download-file';
|
|
36
|
-
export {
|
|
44
|
+
export { Sp00kyProvider, type Sp00kyProviderProps } from './lib/Sp00kyProvider';
|
|
37
45
|
export { useDb } from './lib/context';
|
|
38
46
|
|
|
39
47
|
// export { AuthEventTypes } from "@spooky-sync/core"; // TODO: Verify if AuthEventTypes exists in core
|
|
40
|
-
|
|
48
|
+
|
|
41
49
|
|
|
42
50
|
// Re-export query builder types for convenience
|
|
43
51
|
export type {
|
|
@@ -52,7 +60,7 @@ export type {
|
|
|
52
60
|
TableModel,
|
|
53
61
|
TableNames,
|
|
54
62
|
QueryResult,
|
|
55
|
-
}
|
|
63
|
+
};
|
|
56
64
|
|
|
57
65
|
export type RelationshipField<
|
|
58
66
|
Schema extends SchemaStructure,
|
|
@@ -94,30 +102,30 @@ export type WithRelatedMany<Field extends string, RelatedFields extends RelatedF
|
|
|
94
102
|
};
|
|
95
103
|
|
|
96
104
|
/**
|
|
97
|
-
* SyncedDb - A thin wrapper around
|
|
98
|
-
* Delegates all logic to the underlying
|
|
105
|
+
* SyncedDb - A thin wrapper around sp00ky-ts for Solid.js integration
|
|
106
|
+
* Delegates all logic to the underlying sp00ky-ts instance
|
|
99
107
|
*/
|
|
100
108
|
export class SyncedDb<S extends SchemaStructure> {
|
|
101
109
|
private config: SyncedDbConfig<S>;
|
|
102
|
-
private
|
|
110
|
+
private sp00ky: Sp00kyClient<S> | null = null;
|
|
103
111
|
private _initialized = false;
|
|
104
112
|
|
|
105
113
|
constructor(config: SyncedDbConfig<S>) {
|
|
106
114
|
this.config = config;
|
|
107
115
|
}
|
|
108
116
|
|
|
109
|
-
public
|
|
110
|
-
if (!this.
|
|
111
|
-
return this.
|
|
117
|
+
public getSp00ky(): Sp00kyClient<S> {
|
|
118
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
119
|
+
return this.sp00ky;
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
/**
|
|
115
|
-
* Initialize the
|
|
123
|
+
* Initialize the sp00ky-ts instance
|
|
116
124
|
*/
|
|
117
125
|
async init(): Promise<void> {
|
|
118
126
|
if (this._initialized) return;
|
|
119
|
-
this.
|
|
120
|
-
await this.
|
|
127
|
+
this.sp00ky = new Sp00kyClient<S>(this.config);
|
|
128
|
+
await this.sp00ky.init();
|
|
121
129
|
this._initialized = true;
|
|
122
130
|
}
|
|
123
131
|
|
|
@@ -125,8 +133,8 @@ export class SyncedDb<S extends SchemaStructure> {
|
|
|
125
133
|
* Create a new record in the database
|
|
126
134
|
*/
|
|
127
135
|
async create(id: string, payload: Record<string, unknown>): Promise<void> {
|
|
128
|
-
if (!this.
|
|
129
|
-
await this.
|
|
136
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
137
|
+
await this.sp00ky.create(id, payload as Record<string, unknown>);
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
/**
|
|
@@ -138,8 +146,8 @@ export class SyncedDb<S extends SchemaStructure> {
|
|
|
138
146
|
payload: Partial<TableModel<GetTable<S, TName>>>,
|
|
139
147
|
options?: UpdateOptions
|
|
140
148
|
): Promise<void> {
|
|
141
|
-
if (!this.
|
|
142
|
-
await this.
|
|
149
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
150
|
+
await this.sp00ky.update(
|
|
143
151
|
tableName as string,
|
|
144
152
|
recordId,
|
|
145
153
|
payload as Record<string, unknown>,
|
|
@@ -154,10 +162,10 @@ export class SyncedDb<S extends SchemaStructure> {
|
|
|
154
162
|
tableName: TName,
|
|
155
163
|
selector: string | InnerQuery<GetTable<S, TName>, boolean>
|
|
156
164
|
): Promise<void> {
|
|
157
|
-
if (!this.
|
|
165
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
158
166
|
if (typeof selector !== 'string')
|
|
159
167
|
throw new Error('Only string ID selectors are supported currently with core');
|
|
160
|
-
await this.
|
|
168
|
+
await this.sp00ky.delete(tableName as string, selector);
|
|
161
169
|
}
|
|
162
170
|
|
|
163
171
|
/**
|
|
@@ -165,9 +173,9 @@ export class SyncedDb<S extends SchemaStructure> {
|
|
|
165
173
|
*/
|
|
166
174
|
public query<TName extends TableNames<S>>(
|
|
167
175
|
table: TName
|
|
168
|
-
): QueryBuilder<S, TName,
|
|
169
|
-
if (!this.
|
|
170
|
-
return this.
|
|
176
|
+
): QueryBuilder<S, TName, Sp00kyQueryResultPromise, {}, false> {
|
|
177
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
178
|
+
return this.sp00ky.query(table, {});
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
/**
|
|
@@ -182,17 +190,17 @@ export class SyncedDb<S extends SchemaStructure> {
|
|
|
182
190
|
payload: RoutePayload<S, B, R>,
|
|
183
191
|
options?: RunOptions,
|
|
184
192
|
): Promise<void> {
|
|
185
|
-
if (!this.
|
|
186
|
-
await this.
|
|
193
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
194
|
+
await this.sp00ky.run(backend, path, payload, options);
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
/**
|
|
190
198
|
* Authenticate with the database
|
|
191
199
|
*/
|
|
192
200
|
public async authenticate(token: string): Promise<RecordId<string>> {
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
// Wait, checked
|
|
201
|
+
await this.sp00ky?.authenticate(token);
|
|
202
|
+
// Sp00kyClient.authenticate returns whatever remote.authenticate returns (boolean or token usually?)
|
|
203
|
+
// Wait, checked Sp00kyClient: return this.remote.getClient().authenticate(token);
|
|
196
204
|
// SurrealDB authenticate returns void? or token?
|
|
197
205
|
// Assuming void or token.
|
|
198
206
|
return new RecordId('user', 'me'); // Placeholder or actual?
|
|
@@ -210,54 +218,54 @@ export class SyncedDb<S extends SchemaStructure> {
|
|
|
210
218
|
* Sign out, clear session and local storage
|
|
211
219
|
*/
|
|
212
220
|
public async signOut(): Promise<void> {
|
|
213
|
-
if (!this.
|
|
214
|
-
await this.
|
|
221
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
222
|
+
await this.sp00ky.auth.signOut();
|
|
215
223
|
}
|
|
216
224
|
|
|
217
225
|
/**
|
|
218
226
|
* Execute a function with direct access to the remote database connection
|
|
219
227
|
*/
|
|
220
228
|
public async useRemote<T>(fn: (db: Surreal) => T | Promise<T>): Promise<T> {
|
|
221
|
-
if (!this.
|
|
222
|
-
return await this.
|
|
229
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
230
|
+
return await this.sp00ky.useRemote(fn);
|
|
223
231
|
}
|
|
224
232
|
/**
|
|
225
233
|
* Access the remote database service directly
|
|
226
234
|
*/
|
|
227
|
-
get remote():
|
|
228
|
-
if (!this.
|
|
229
|
-
return this.
|
|
235
|
+
get remote(): Sp00kyClient<S>['remoteClient'] {
|
|
236
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
237
|
+
return this.sp00ky.remoteClient;
|
|
230
238
|
}
|
|
231
239
|
|
|
232
240
|
/**
|
|
233
241
|
* Access the local database service directly
|
|
234
242
|
*/
|
|
235
|
-
get local():
|
|
236
|
-
if (!this.
|
|
237
|
-
return this.
|
|
243
|
+
get local(): Sp00kyClient<S>['localClient'] {
|
|
244
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
245
|
+
return this.sp00ky.localClient;
|
|
238
246
|
}
|
|
239
247
|
|
|
240
248
|
/**
|
|
241
249
|
* Access the auth service
|
|
242
250
|
*/
|
|
243
251
|
get auth(): AuthService<S> {
|
|
244
|
-
if (!this.
|
|
245
|
-
return this.
|
|
252
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
253
|
+
return this.sp00ky.auth;
|
|
246
254
|
}
|
|
247
255
|
|
|
248
256
|
get pendingMutationCount(): number {
|
|
249
|
-
if (!this.
|
|
250
|
-
return this.
|
|
257
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
258
|
+
return this.sp00ky.pendingMutationCount;
|
|
251
259
|
}
|
|
252
260
|
|
|
253
261
|
subscribeToPendingMutations(cb: (count: number) => void): () => void {
|
|
254
|
-
if (!this.
|
|
255
|
-
return this.
|
|
262
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
263
|
+
return this.sp00ky.subscribeToPendingMutations(cb);
|
|
256
264
|
}
|
|
257
265
|
|
|
258
266
|
bucket<B extends BucketNames<S>>(name: B): BucketHandle {
|
|
259
|
-
if (!this.
|
|
260
|
-
return this.
|
|
267
|
+
if (!this.sp00ky) throw new Error('SyncedDb not initialized');
|
|
268
|
+
return this.sp00ky.bucket(name);
|
|
261
269
|
}
|
|
262
270
|
|
|
263
271
|
getBucketConfig(name: string): BucketDefinitionSchema | undefined {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { JSX} from 'solid-js';
|
|
2
|
+
import { createSignal, onMount, createComponent, createMemo, mergeProps } from 'solid-js';
|
|
2
3
|
import type { SchemaStructure } from '@spooky/query-builder';
|
|
3
4
|
import type { SyncedDbConfig } from '../types';
|
|
4
5
|
import { SyncedDb } from '../index';
|
|
5
|
-
import {
|
|
6
|
+
import { Sp00kyContext } from './context';
|
|
6
7
|
|
|
7
|
-
export interface
|
|
8
|
+
export interface Sp00kyProviderProps<S extends SchemaStructure> {
|
|
8
9
|
config: SyncedDbConfig<S>;
|
|
9
10
|
fallback?: JSX.Element;
|
|
10
11
|
onError?: (error: Error) => void;
|
|
@@ -12,8 +13,8 @@ export interface SpookyProviderProps<S extends SchemaStructure> {
|
|
|
12
13
|
children: JSX.Element;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export function
|
|
16
|
-
props:
|
|
16
|
+
export function Sp00kyProvider<S extends SchemaStructure>(
|
|
17
|
+
props: Sp00kyProviderProps<S>
|
|
17
18
|
): JSX.Element {
|
|
18
19
|
const merged = mergeProps(
|
|
19
20
|
{
|
|
@@ -35,7 +36,8 @@ export function SpookyProvider<S extends SchemaStructure>(
|
|
|
35
36
|
if (merged.onError) {
|
|
36
37
|
merged.onError(error);
|
|
37
38
|
} else {
|
|
38
|
-
|
|
39
|
+
// oxlint-disable-next-line no-console
|
|
40
|
+
console.error('Sp00kyProvider: Failed to initialize database', error);
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
});
|
|
@@ -43,7 +45,7 @@ export function SpookyProvider<S extends SchemaStructure>(
|
|
|
43
45
|
const content = createMemo(() => {
|
|
44
46
|
const instance = db();
|
|
45
47
|
if (!instance) return merged.fallback;
|
|
46
|
-
return createComponent(
|
|
48
|
+
return createComponent(Sp00kyContext.Provider, {
|
|
47
49
|
value: instance,
|
|
48
50
|
get children() {
|
|
49
51
|
return merged.children;
|
package/src/lib/context.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { createContext, useContext } from 'solid-js';
|
|
|
2
2
|
import type { SchemaStructure } from '@spooky/query-builder';
|
|
3
3
|
import type { SyncedDb } from '../index';
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const Sp00kyContext = createContext<SyncedDb<any> | undefined>();
|
|
6
6
|
|
|
7
7
|
export function useDb<S extends SchemaStructure>(): SyncedDb<S> {
|
|
8
|
-
const db = useContext(
|
|
8
|
+
const db = useContext(Sp00kyContext);
|
|
9
9
|
if (!db) {
|
|
10
|
-
throw new Error('useDb must be used within a <
|
|
10
|
+
throw new Error('useDb must be used within a <Sp00kyProvider>. Wrap your app in <Sp00kyProvider config={...}>.');
|
|
11
11
|
}
|
|
12
12
|
return db as SyncedDb<S>;
|
|
13
13
|
}
|
package/src/lib/models.ts
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createEffect, createSignal, onCleanup, useContext, type Accessor } from 'solid-js';
|
|
2
|
+
import { Sp00kyContext } from './context';
|
|
3
|
+
import type { CrdtField } from '@spooky-sync/core';
|
|
4
|
+
|
|
5
|
+
export function useCrdtField(
|
|
6
|
+
table: string,
|
|
7
|
+
recordId: () => string | undefined,
|
|
8
|
+
field: string,
|
|
9
|
+
fallbackText?: () => string | undefined,
|
|
10
|
+
): Accessor<CrdtField | null> {
|
|
11
|
+
const db = useContext(Sp00kyContext);
|
|
12
|
+
if (!db) {
|
|
13
|
+
throw new Error('useCrdtField must be used within a <Sp00kyProvider>');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const [crdtField, setCrdtField] = createSignal<CrdtField | null>(null);
|
|
17
|
+
let currentId: string | undefined;
|
|
18
|
+
let initialized = false;
|
|
19
|
+
|
|
20
|
+
createEffect(() => {
|
|
21
|
+
const id = recordId();
|
|
22
|
+
|
|
23
|
+
// Skip if the ID hasn't changed (but allow the first non-undefined value through)
|
|
24
|
+
if (initialized && id === currentId) return;
|
|
25
|
+
|
|
26
|
+
// Close previous field
|
|
27
|
+
if (currentId && crdtField()) {
|
|
28
|
+
db.getSp00ky().closeCrdtField(table, currentId, field);
|
|
29
|
+
setCrdtField(null);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
currentId = id;
|
|
33
|
+
initialized = true;
|
|
34
|
+
|
|
35
|
+
if (!id) return;
|
|
36
|
+
|
|
37
|
+
const sp00ky = db.getSp00ky();
|
|
38
|
+
const text = fallbackText?.();
|
|
39
|
+
sp00ky.openCrdtField(table, id, field, text).then((cf) => {
|
|
40
|
+
if (currentId === id) {
|
|
41
|
+
setCrdtField(cf);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
onCleanup(() => {
|
|
47
|
+
if (currentId && crdtField()) {
|
|
48
|
+
db.getSp00ky().closeCrdtField(table, currentId, field);
|
|
49
|
+
setCrdtField(null);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return crdtField;
|
|
54
|
+
}
|
|
@@ -78,9 +78,8 @@ export function useDownloadFile<S extends SchemaStructure>(
|
|
|
78
78
|
|
|
79
79
|
let currentKey: string | null = null;
|
|
80
80
|
let privateUrl: string | null = null;
|
|
81
|
-
let refetchTrigger: () => void;
|
|
82
81
|
const [refetchSignal, setRefetchSignal] = createSignal(0);
|
|
83
|
-
refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
82
|
+
const refetchTrigger = () => setRefetchSignal((n) => n + 1);
|
|
84
83
|
|
|
85
84
|
async function doDownload(key: string, filePath: string): Promise<string | null> {
|
|
86
85
|
if (useCache) {
|
|
@@ -184,6 +183,7 @@ export function useDownloadFile<S extends SchemaStructure>(
|
|
|
184
183
|
setUrl(result);
|
|
185
184
|
setIsLoading(false);
|
|
186
185
|
}
|
|
186
|
+
return undefined;
|
|
187
187
|
},
|
|
188
188
|
(err) => {
|
|
189
189
|
if (!cancelled) {
|
|
@@ -33,6 +33,7 @@ export function useFileUpload<S extends SchemaStructure>(
|
|
|
33
33
|
bucketName = dbOrBucketName as BucketNames<S>;
|
|
34
34
|
} else {
|
|
35
35
|
db = dbOrBucketName as SyncedDb<S>;
|
|
36
|
+
// oxlint-disable-next-line no-non-null-assertion
|
|
36
37
|
bucketName = maybeBucketName!;
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -52,7 +53,7 @@ export function useFileUpload<S extends SchemaStructure>(
|
|
|
52
53
|
const config = db.getBucketConfig(bucketName as string);
|
|
53
54
|
if (!config) return;
|
|
54
55
|
|
|
55
|
-
if (config.maxSize
|
|
56
|
+
if (config.maxSize !== null && config.maxSize !== undefined && file.size > config.maxSize) {
|
|
56
57
|
const maxMB = (config.maxSize / (1024 * 1024)).toFixed(1);
|
|
57
58
|
throw new Error(`File exceeds maximum size of ${maxMB} MB.`);
|
|
58
59
|
}
|
package/src/lib/use-query.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
ColumnSchema,
|
|
3
3
|
FinalQuery,
|
|
4
4
|
SchemaStructure,
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
} from '@spooky-sync/query-builder';
|
|
8
8
|
import { createEffect, createSignal, onCleanup, useContext } from 'solid-js';
|
|
9
9
|
import { SyncedDb } from '..';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import type { Sp00kyQueryResultPromise } from '@spooky-sync/core';
|
|
11
|
+
import { Sp00kyContext } from './context';
|
|
12
12
|
|
|
13
13
|
type QueryArg<
|
|
14
14
|
S extends SchemaStructure,
|
|
@@ -17,9 +17,9 @@ type QueryArg<
|
|
|
17
17
|
RelatedFields extends Record<string, any>,
|
|
18
18
|
IsOne extends boolean,
|
|
19
19
|
> =
|
|
20
|
-
| FinalQuery<S, TableName, T, RelatedFields, IsOne,
|
|
20
|
+
| FinalQuery<S, TableName, T, RelatedFields, IsOne, Sp00kyQueryResultPromise>
|
|
21
21
|
| (() =>
|
|
22
|
-
| FinalQuery<S, TableName, T, RelatedFields, IsOne,
|
|
22
|
+
| FinalQuery<S, TableName, T, RelatedFields, IsOne, Sp00kyQueryResultPromise>
|
|
23
23
|
| null
|
|
24
24
|
| undefined);
|
|
25
25
|
|
|
@@ -82,11 +82,11 @@ export function useQuery<
|
|
|
82
82
|
options = maybeOptions;
|
|
83
83
|
} else {
|
|
84
84
|
// Context-based overload: useQuery(query, options?)
|
|
85
|
-
const contextDb = useContext(
|
|
85
|
+
const contextDb = useContext(Sp00kyContext);
|
|
86
86
|
if (!contextDb) {
|
|
87
87
|
throw new Error(
|
|
88
|
-
'useQuery: No db argument provided and no
|
|
89
|
-
'Either pass a SyncedDb instance or wrap your app in <
|
|
88
|
+
'useQuery: No db argument provided and no Sp00kyContext found. ' +
|
|
89
|
+
'Either pass a SyncedDb instance or wrap your app in <Sp00kyProvider>.'
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
92
|
db = contextDb as SyncedDb<S>;
|
|
@@ -100,23 +100,23 @@ export function useQuery<
|
|
|
100
100
|
const [unsubscribe, setUnsubscribe] = createSignal<(() => void) | undefined>(undefined);
|
|
101
101
|
let prevQueryString: string | undefined;
|
|
102
102
|
|
|
103
|
-
const
|
|
103
|
+
const sp00ky = db.getSp00ky();
|
|
104
104
|
|
|
105
105
|
const initQuery = async (
|
|
106
|
-
query: FinalQuery<S, TableName, T, RelatedFields, IsOne,
|
|
106
|
+
query: FinalQuery<S, TableName, T, RelatedFields, IsOne, Sp00kyQueryResultPromise>
|
|
107
107
|
) => {
|
|
108
108
|
const { hash } = await query.run();
|
|
109
109
|
setError(undefined);
|
|
110
110
|
|
|
111
111
|
let isFirstCall = true;
|
|
112
|
-
const unsub = await
|
|
112
|
+
const unsub = await sp00ky.subscribe(
|
|
113
113
|
hash,
|
|
114
114
|
(e) => {
|
|
115
|
-
const
|
|
116
|
-
setData(() =>
|
|
115
|
+
const queryData = (query.isOne ? e[0] : e) as TData;
|
|
116
|
+
setData(() => queryData);
|
|
117
117
|
// The first (immediate) callback with no data likely means the local DB
|
|
118
118
|
// hasn't synced yet — don't mark as fetched so UI shows loading state
|
|
119
|
-
const hasData = query.isOne ?
|
|
119
|
+
const hasData = query.isOne ? queryData !== null && queryData !== undefined : (e as any[]).length > 0;
|
|
120
120
|
if (!isFirstCall || hasData) {
|
|
121
121
|
setIsFetched(true);
|
|
122
122
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { Surreal } from 'surrealdb';
|
|
2
1
|
import type { SyncedDb } from '../index';
|
|
3
|
-
import { GenericSchema } from '../lib/models';
|
|
4
|
-
import type {
|
|
2
|
+
import type { GenericSchema } from '../lib/models';
|
|
3
|
+
import type { Sp00kyConfig } from '@spooky-sync/core';
|
|
5
4
|
import type { SchemaStructure, TableNames, GetTable, TableModel } from '@spooky-sync/query-builder';
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -44,7 +43,7 @@ export type InferRelationshipsFromConst<S extends SchemaStructure, Schema extend
|
|
|
44
43
|
// Prettify helper expands types for better intellisense
|
|
45
44
|
type Prettify<T> = { [K in keyof T]: T[K] } & {};
|
|
46
45
|
|
|
47
|
-
export type SyncedDbConfig<S extends SchemaStructure> = Prettify<
|
|
46
|
+
export type SyncedDbConfig<S extends SchemaStructure> = Prettify<Sp00kyConfig<S>>;
|
|
48
47
|
|
|
49
48
|
// export interface LocalDbConfig {
|
|
50
49
|
// name: string;
|