@weirdscience/based-client 0.4.0 → 0.6.0
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/README.md +267 -49
- package/dist/client.d.ts.map +1 -1
- package/dist/core.cjs +376 -0
- package/dist/core.d.ts +4 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +334 -0
- package/dist/hooks/use-mutation.d.ts +1 -3
- package/dist/hooks/use-mutation.d.ts.map +1 -1
- package/dist/hooks/use-query.d.ts.map +1 -1
- package/dist/hooks/use-record.d.ts.map +1 -1
- package/dist/hooks/use-user.d.ts.map +1 -1
- package/dist/index.cjs +593 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +142 -42
- package/dist/provider.d.ts +4 -4
- package/dist/provider.d.ts.map +1 -1
- package/dist/types.d.ts +44 -13
- package/dist/types.d.ts.map +1 -1
- package/package.json +25 -6
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
// src/types.ts
|
|
2
4
|
class BasedError extends Error {
|
|
3
5
|
code;
|
|
@@ -11,6 +13,20 @@ class BasedError extends Error {
|
|
|
11
13
|
this.details = details;
|
|
12
14
|
}
|
|
13
15
|
}
|
|
16
|
+
function toHookError(err) {
|
|
17
|
+
if (err instanceof BasedError) {
|
|
18
|
+
return {
|
|
19
|
+
code: err.code,
|
|
20
|
+
message: err.message,
|
|
21
|
+
status: err.status,
|
|
22
|
+
details: err.details
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
code: "NETWORK_ERROR",
|
|
27
|
+
message: err instanceof Error ? err.message : String(err)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
14
30
|
|
|
15
31
|
// src/client.ts
|
|
16
32
|
var DEFAULT_STORAGE_KEY = "based.session";
|
|
@@ -49,6 +65,9 @@ function createClient(options) {
|
|
|
49
65
|
function getState() {
|
|
50
66
|
return state;
|
|
51
67
|
}
|
|
68
|
+
function onAuthStateChange(callback) {
|
|
69
|
+
return subscribe(() => callback(state));
|
|
70
|
+
}
|
|
52
71
|
async function persist() {
|
|
53
72
|
if (!storage)
|
|
54
73
|
return;
|
|
@@ -119,16 +138,22 @@ function createClient(options) {
|
|
|
119
138
|
if (!headers.has("Content-Type") && init.body) {
|
|
120
139
|
headers.set("Content-Type", "application/json");
|
|
121
140
|
}
|
|
141
|
+
const tokenAtRequest = state.accessToken;
|
|
122
142
|
let res = await fetch(`${baseUrl}${path}`, { ...init, headers });
|
|
123
143
|
if (res.status === 401 && state.refreshToken) {
|
|
124
|
-
const
|
|
125
|
-
|
|
144
|
+
const alreadyRefreshed = state.accessToken !== tokenAtRequest;
|
|
145
|
+
const refreshed = alreadyRefreshed ? true : await refreshSession();
|
|
146
|
+
if (refreshed && state.accessToken) {
|
|
126
147
|
headers.set("Authorization", `Bearer ${state.accessToken}`);
|
|
127
148
|
res = await fetch(`${baseUrl}${path}`, { ...init, headers });
|
|
128
149
|
}
|
|
129
150
|
}
|
|
130
151
|
return res;
|
|
131
152
|
}
|
|
153
|
+
async function authError(res, fallback) {
|
|
154
|
+
const json = await res.json().catch(() => null);
|
|
155
|
+
return new BasedError(json?.error?.code ?? "AUTH_ERROR", json?.error?.message || fallback, res.status, json?.error?.details);
|
|
156
|
+
}
|
|
132
157
|
async function signUp(email, password) {
|
|
133
158
|
const res = await fetch(`${baseUrl}/auth/signup`, {
|
|
134
159
|
method: "POST",
|
|
@@ -136,8 +161,7 @@ function createClient(options) {
|
|
|
136
161
|
body: JSON.stringify({ email, password })
|
|
137
162
|
});
|
|
138
163
|
if (!res.ok) {
|
|
139
|
-
|
|
140
|
-
throw new Error(json?.error?.message || "Sign up failed");
|
|
164
|
+
throw await authError(res, "Sign up failed");
|
|
141
165
|
}
|
|
142
166
|
const { data } = await res.json();
|
|
143
167
|
setState({
|
|
@@ -155,8 +179,7 @@ function createClient(options) {
|
|
|
155
179
|
body: JSON.stringify({ email, password })
|
|
156
180
|
});
|
|
157
181
|
if (!res.ok) {
|
|
158
|
-
|
|
159
|
-
throw new Error(json?.error?.message || "Sign in failed");
|
|
182
|
+
throw await authError(res, "Sign in failed");
|
|
160
183
|
}
|
|
161
184
|
const { data } = await res.json();
|
|
162
185
|
setState({
|
|
@@ -178,7 +201,16 @@ function createClient(options) {
|
|
|
178
201
|
}
|
|
179
202
|
setState({ user: null, accessToken: null, refreshToken: null, isLoading: false });
|
|
180
203
|
}
|
|
181
|
-
|
|
204
|
+
let refreshInFlight = null;
|
|
205
|
+
function refreshSession() {
|
|
206
|
+
if (!refreshInFlight) {
|
|
207
|
+
refreshInFlight = doRefresh().finally(() => {
|
|
208
|
+
refreshInFlight = null;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return refreshInFlight;
|
|
212
|
+
}
|
|
213
|
+
async function doRefresh() {
|
|
182
214
|
if (!state.refreshToken)
|
|
183
215
|
return false;
|
|
184
216
|
try {
|
|
@@ -215,7 +247,7 @@ function createClient(options) {
|
|
|
215
247
|
return null;
|
|
216
248
|
}
|
|
217
249
|
const { data } = await res.json();
|
|
218
|
-
const user = { id: data.id, email: data.email };
|
|
250
|
+
const user = { id: data.id, email: data.email, role: data.role };
|
|
219
251
|
setState({ user });
|
|
220
252
|
return user;
|
|
221
253
|
} catch {
|
|
@@ -223,12 +255,12 @@ function createClient(options) {
|
|
|
223
255
|
}
|
|
224
256
|
}
|
|
225
257
|
async function parseOrThrow(res, fallbackCode) {
|
|
226
|
-
let json =
|
|
258
|
+
let json = {};
|
|
227
259
|
try {
|
|
228
260
|
json = await res.json();
|
|
229
261
|
} catch {}
|
|
230
262
|
if (!res.ok) {
|
|
231
|
-
const err = json
|
|
263
|
+
const err = json.error;
|
|
232
264
|
throw new BasedError(err?.code || fallbackCode, err?.message || `Request failed with status ${res.status}`, res.status, err?.details);
|
|
233
265
|
}
|
|
234
266
|
return json;
|
|
@@ -242,6 +274,8 @@ function createClient(options) {
|
|
|
242
274
|
params.set("limit", String(opts.limit));
|
|
243
275
|
if (opts?.offset !== undefined)
|
|
244
276
|
params.set("offset", String(opts.offset));
|
|
277
|
+
if (opts?.order !== undefined)
|
|
278
|
+
params.set("order", opts.order);
|
|
245
279
|
if (opts?.filter) {
|
|
246
280
|
for (const [key, value] of Object.entries(opts.filter)) {
|
|
247
281
|
if (value !== undefined)
|
|
@@ -269,22 +303,25 @@ function createClient(options) {
|
|
|
269
303
|
return json.data;
|
|
270
304
|
},
|
|
271
305
|
async update(id, data) {
|
|
272
|
-
const
|
|
306
|
+
const body = { ...data };
|
|
307
|
+
delete body.id;
|
|
273
308
|
const res = await fetchWithAuth(`${basePath}/${encodeURIComponent(id)}`, {
|
|
274
309
|
method: "PUT",
|
|
275
|
-
body: JSON.stringify(
|
|
310
|
+
body: JSON.stringify(body)
|
|
276
311
|
});
|
|
277
312
|
const json = await parseOrThrow(res, "UPDATE_FAILED");
|
|
278
313
|
return json.data;
|
|
279
314
|
},
|
|
280
315
|
async upsert(data) {
|
|
281
|
-
const {
|
|
316
|
+
const body = { ...data };
|
|
317
|
+
const id = body.id;
|
|
282
318
|
if (!id) {
|
|
283
319
|
throw new BasedError("MISSING_ID", "upsert() requires `data.id`. Use insert() to let the server generate one.", 400);
|
|
284
320
|
}
|
|
321
|
+
delete body.id;
|
|
285
322
|
const res = await fetchWithAuth(`${basePath}/${encodeURIComponent(id)}`, {
|
|
286
323
|
method: "PUT",
|
|
287
|
-
body: JSON.stringify(
|
|
324
|
+
body: JSON.stringify(body)
|
|
288
325
|
});
|
|
289
326
|
const json = await parseOrThrow(res, "UPSERT_FAILED");
|
|
290
327
|
return json.data;
|
|
@@ -299,7 +336,7 @@ function createClient(options) {
|
|
|
299
336
|
};
|
|
300
337
|
}
|
|
301
338
|
return {
|
|
302
|
-
auth: { signUp, signIn, signOut, refreshSession, getUser },
|
|
339
|
+
auth: { signUp, signIn, signOut, refreshSession, getUser, onAuthStateChange },
|
|
303
340
|
fetch: fetchWithAuth,
|
|
304
341
|
from,
|
|
305
342
|
subscribe,
|
|
@@ -310,6 +347,7 @@ function createClient(options) {
|
|
|
310
347
|
// src/provider.tsx
|
|
311
348
|
import { createContext, useContext } from "react";
|
|
312
349
|
import { jsx } from "react/jsx-runtime";
|
|
350
|
+
"use client";
|
|
313
351
|
var BasedContext = createContext(null);
|
|
314
352
|
function BasedProvider({
|
|
315
353
|
client,
|
|
@@ -329,9 +367,16 @@ function useBasedClient() {
|
|
|
329
367
|
}
|
|
330
368
|
// src/hooks/use-user.ts
|
|
331
369
|
import { useSyncExternalStore } from "react";
|
|
370
|
+
"use client";
|
|
371
|
+
var SERVER_SNAPSHOT = Object.freeze({
|
|
372
|
+
user: null,
|
|
373
|
+
accessToken: null,
|
|
374
|
+
refreshToken: null,
|
|
375
|
+
isLoading: true
|
|
376
|
+
});
|
|
332
377
|
function useUser() {
|
|
333
378
|
const client = useBasedClient();
|
|
334
|
-
const state = useSyncExternalStore(client.subscribe, () => client.getState(), () =>
|
|
379
|
+
const state = useSyncExternalStore(client.subscribe, () => client.getState(), () => SERVER_SNAPSHOT);
|
|
335
380
|
return {
|
|
336
381
|
user: state.user,
|
|
337
382
|
isLoading: state.isLoading,
|
|
@@ -339,79 +384,134 @@ function useUser() {
|
|
|
339
384
|
};
|
|
340
385
|
}
|
|
341
386
|
// src/hooks/use-query.ts
|
|
342
|
-
import {
|
|
387
|
+
import {
|
|
388
|
+
useState,
|
|
389
|
+
useEffect,
|
|
390
|
+
useCallback,
|
|
391
|
+
useMemo,
|
|
392
|
+
useRef,
|
|
393
|
+
useSyncExternalStore as useSyncExternalStore2
|
|
394
|
+
} from "react";
|
|
395
|
+
"use client";
|
|
396
|
+
function stableKey(value) {
|
|
397
|
+
if (value === undefined)
|
|
398
|
+
return "undefined";
|
|
399
|
+
if (value === null || typeof value !== "object")
|
|
400
|
+
return JSON.stringify(value);
|
|
401
|
+
if (Array.isArray(value))
|
|
402
|
+
return `[${value.map(stableKey).join(",")}]`;
|
|
403
|
+
const entries = Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
404
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableKey(v)}`).join(",")}}`;
|
|
405
|
+
}
|
|
343
406
|
function useQuery(table, options) {
|
|
344
407
|
const client = useBasedClient();
|
|
345
408
|
const enabled = options?.enabled !== false;
|
|
409
|
+
const authKey = useSyncExternalStore2(client.subscribe, () => client.getState().user?.id ?? null, () => null);
|
|
346
410
|
const [data, setData] = useState(null);
|
|
347
411
|
const [total, setTotal] = useState(0);
|
|
348
412
|
const [isLoading, setIsLoading] = useState(enabled);
|
|
349
413
|
const [error, setError] = useState(null);
|
|
350
|
-
const
|
|
414
|
+
const optionsKey = stableKey(options);
|
|
415
|
+
const selectOptions = useMemo(() => ({
|
|
416
|
+
filter: options?.filter,
|
|
417
|
+
limit: options?.limit,
|
|
418
|
+
offset: options?.offset,
|
|
419
|
+
order: options?.order
|
|
420
|
+
}), [optionsKey]);
|
|
421
|
+
const latestRequest = useRef(0);
|
|
422
|
+
const runQuery = useCallback(async () => {
|
|
423
|
+
const requestId = ++latestRequest.current;
|
|
351
424
|
setIsLoading(true);
|
|
352
425
|
setError(null);
|
|
353
426
|
try {
|
|
354
|
-
const result = await client.from(table).select(
|
|
427
|
+
const result = await client.from(table).select(selectOptions);
|
|
428
|
+
if (latestRequest.current !== requestId)
|
|
429
|
+
return;
|
|
355
430
|
setData(result.data);
|
|
356
431
|
setTotal(result.total);
|
|
357
432
|
} catch (err) {
|
|
358
|
-
|
|
359
|
-
|
|
433
|
+
if (latestRequest.current !== requestId)
|
|
434
|
+
return;
|
|
435
|
+
setError(toHookError(err));
|
|
360
436
|
setData(null);
|
|
361
437
|
setTotal(0);
|
|
362
438
|
} finally {
|
|
363
|
-
|
|
439
|
+
if (latestRequest.current === requestId)
|
|
440
|
+
setIsLoading(false);
|
|
364
441
|
}
|
|
365
|
-
}, [client, table,
|
|
442
|
+
}, [client, table, selectOptions]);
|
|
366
443
|
useEffect(() => {
|
|
367
444
|
if (!enabled) {
|
|
368
|
-
|
|
445
|
+
latestRequest.current += 1;
|
|
369
446
|
setData(null);
|
|
370
447
|
setTotal(0);
|
|
371
448
|
setError(null);
|
|
449
|
+
setIsLoading(false);
|
|
372
450
|
return;
|
|
373
451
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
452
|
+
runQuery();
|
|
453
|
+
return () => {
|
|
454
|
+
latestRequest.current += 1;
|
|
455
|
+
};
|
|
456
|
+
}, [enabled, authKey, runQuery]);
|
|
457
|
+
return { data, total, isLoading, error, refetch: runQuery };
|
|
377
458
|
}
|
|
378
459
|
// src/hooks/use-record.ts
|
|
379
|
-
import {
|
|
460
|
+
import {
|
|
461
|
+
useState as useState2,
|
|
462
|
+
useEffect as useEffect2,
|
|
463
|
+
useCallback as useCallback2,
|
|
464
|
+
useRef as useRef2,
|
|
465
|
+
useSyncExternalStore as useSyncExternalStore3
|
|
466
|
+
} from "react";
|
|
467
|
+
"use client";
|
|
380
468
|
function useRecord(table, id, options) {
|
|
381
469
|
const client = useBasedClient();
|
|
382
470
|
const enabled = options?.enabled !== false && !!id;
|
|
471
|
+
const authKey = useSyncExternalStore3(client.subscribe, () => client.getState().user?.id ?? null, () => null);
|
|
383
472
|
const [data, setData] = useState2(null);
|
|
384
473
|
const [isLoading, setIsLoading] = useState2(enabled);
|
|
385
474
|
const [error, setError] = useState2(null);
|
|
386
|
-
const
|
|
475
|
+
const latestRequest = useRef2(0);
|
|
476
|
+
const runQuery = useCallback2(async () => {
|
|
387
477
|
if (!id)
|
|
388
478
|
return;
|
|
479
|
+
const requestId = ++latestRequest.current;
|
|
389
480
|
setIsLoading(true);
|
|
390
481
|
setError(null);
|
|
391
482
|
try {
|
|
392
483
|
const row = await client.from(table).get(id);
|
|
484
|
+
if (latestRequest.current !== requestId)
|
|
485
|
+
return;
|
|
393
486
|
setData(row);
|
|
394
487
|
} catch (err) {
|
|
395
|
-
|
|
396
|
-
|
|
488
|
+
if (latestRequest.current !== requestId)
|
|
489
|
+
return;
|
|
490
|
+
setError(toHookError(err));
|
|
397
491
|
setData(null);
|
|
398
492
|
} finally {
|
|
399
|
-
|
|
493
|
+
if (latestRequest.current === requestId)
|
|
494
|
+
setIsLoading(false);
|
|
400
495
|
}
|
|
401
496
|
}, [client, table, id]);
|
|
402
497
|
useEffect2(() => {
|
|
403
498
|
if (!enabled) {
|
|
404
|
-
|
|
499
|
+
latestRequest.current += 1;
|
|
405
500
|
setData(null);
|
|
406
501
|
setError(null);
|
|
502
|
+
setIsLoading(false);
|
|
407
503
|
return;
|
|
408
504
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
505
|
+
runQuery();
|
|
506
|
+
return () => {
|
|
507
|
+
latestRequest.current += 1;
|
|
508
|
+
};
|
|
509
|
+
}, [enabled, authKey, runQuery]);
|
|
510
|
+
return { data, isLoading, error, refetch: runQuery };
|
|
412
511
|
}
|
|
413
512
|
// src/hooks/use-mutation.ts
|
|
414
513
|
import { useState as useState3, useCallback as useCallback3 } from "react";
|
|
514
|
+
"use client";
|
|
415
515
|
function useMutation(table, operation) {
|
|
416
516
|
const client = useBasedClient();
|
|
417
517
|
const [isLoading, setIsLoading] = useState3(false);
|
|
@@ -427,8 +527,7 @@ function useMutation(table, operation) {
|
|
|
427
527
|
case "update": {
|
|
428
528
|
if (!data.id)
|
|
429
529
|
throw new BasedError("MISSING_ID", "Update requires an 'id' field", 400);
|
|
430
|
-
|
|
431
|
-
return await builder.update(String(id), rest);
|
|
530
|
+
return await builder.update(String(data.id), data);
|
|
432
531
|
}
|
|
433
532
|
case "upsert": {
|
|
434
533
|
if (!data.id)
|
|
@@ -443,16 +542,17 @@ function useMutation(table, operation) {
|
|
|
443
542
|
}
|
|
444
543
|
}
|
|
445
544
|
} catch (err) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
setError({ code, message });
|
|
449
|
-
throw err;
|
|
545
|
+
setError(toHookError(err));
|
|
546
|
+
return null;
|
|
450
547
|
} finally {
|
|
451
548
|
setIsLoading(false);
|
|
452
549
|
}
|
|
453
550
|
}, [client, table, operation]);
|
|
454
551
|
return { mutate, isLoading, error };
|
|
455
552
|
}
|
|
553
|
+
|
|
554
|
+
// src/index.ts
|
|
555
|
+
"use client";
|
|
456
556
|
export {
|
|
457
557
|
useUser,
|
|
458
558
|
useRecord,
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { BasedClient } from "./types";
|
|
3
|
-
export declare function BasedProvider({ client, children, }: {
|
|
4
|
-
client: BasedClient
|
|
2
|
+
import type { BasedClient, DefaultTables } from "./types";
|
|
3
|
+
export declare function BasedProvider<T extends Record<string, Record<string, unknown>> = DefaultTables>({ client, children, }: {
|
|
4
|
+
client: BasedClient<T>;
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
-
export declare function useBasedClient(): BasedClient
|
|
7
|
+
export declare function useBasedClient<T extends Record<string, Record<string, unknown>> = DefaultTables>(): BasedClient<T>;
|
|
8
8
|
//# sourceMappingURL=provider.d.ts.map
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAI1D,wBAAgB,aAAa,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,EACjE,EACA,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACvB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAMA;AAED,wBAAgB,cAAc,CAC5B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,KAC9D,WAAW,CAAC,CAAC,CAAC,CAMlB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export interface BasedClientOptions {
|
|
|
19
19
|
export interface AuthUser {
|
|
20
20
|
id: string;
|
|
21
21
|
email: string;
|
|
22
|
+
/**
|
|
23
|
+
* Workspace role as reported by the server. The first user to sign up is the
|
|
24
|
+
* `owner`; everyone after is a `member`.
|
|
25
|
+
*/
|
|
26
|
+
role: "owner" | "member";
|
|
22
27
|
}
|
|
23
28
|
export interface AuthState {
|
|
24
29
|
user: AuthUser | null;
|
|
@@ -40,6 +45,12 @@ export interface BasedClient<T extends Record<string, Record<string, unknown>> =
|
|
|
40
45
|
* Returns the current authenticated user by calling `/auth/me`.
|
|
41
46
|
*/
|
|
42
47
|
getUser(): Promise<AuthUser | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Subscribe to auth state changes. The callback receives the new state on
|
|
50
|
+
* every change (sign in, sign out, refresh, rehydration). Returns an
|
|
51
|
+
* unsubscribe function. Sugar over `client.subscribe` + `client.getState`.
|
|
52
|
+
*/
|
|
53
|
+
onAuthStateChange(callback: (state: AuthState) => void): () => void;
|
|
43
54
|
};
|
|
44
55
|
fetch(path: string, init?: RequestInit): Promise<Response>;
|
|
45
56
|
/**
|
|
@@ -97,24 +108,41 @@ export declare class BasedError extends Error {
|
|
|
97
108
|
details?: unknown;
|
|
98
109
|
constructor(code: string, message: string, status: number, details?: unknown);
|
|
99
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Narrow anything thrown by the data builder into the hooks' error shape.
|
|
113
|
+
* Internal — not part of the public surface.
|
|
114
|
+
*/
|
|
115
|
+
export declare function toHookError(err: unknown): HookError;
|
|
100
116
|
export interface QueryOptions<T = Record<string, unknown>> {
|
|
101
117
|
filter?: Partial<Record<keyof T, string | number | boolean>>;
|
|
102
118
|
limit?: number;
|
|
103
119
|
offset?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Sort order, as `<column>.asc` or `<column>.desc`. Defaults to `id.asc`
|
|
122
|
+
* server-side so pagination is stable.
|
|
123
|
+
*/
|
|
124
|
+
order?: `${string & keyof T}.asc` | `${string & keyof T}.desc`;
|
|
104
125
|
/**
|
|
105
126
|
* When false, the query won't fire. Useful for gating queries behind auth:
|
|
106
127
|
* `useQuery("notes", { enabled: !!user })`. Defaults to true.
|
|
107
128
|
*/
|
|
108
129
|
enabled?: boolean;
|
|
109
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* The error shape the hooks expose. Mirrors `BasedError` without being an
|
|
133
|
+
* `Error` instance, so it survives React state and serialization.
|
|
134
|
+
*/
|
|
135
|
+
export interface HookError {
|
|
136
|
+
code: string;
|
|
137
|
+
message: string;
|
|
138
|
+
status?: number;
|
|
139
|
+
details?: unknown;
|
|
140
|
+
}
|
|
110
141
|
export interface QueryResult<T = unknown> {
|
|
111
142
|
data: T[] | null;
|
|
112
143
|
total: number;
|
|
113
144
|
isLoading: boolean;
|
|
114
|
-
error:
|
|
115
|
-
code: string;
|
|
116
|
-
message: string;
|
|
117
|
-
} | null;
|
|
145
|
+
error: HookError | null;
|
|
118
146
|
refetch: () => void;
|
|
119
147
|
}
|
|
120
148
|
export interface RecordOptions {
|
|
@@ -126,19 +154,22 @@ export interface RecordOptions {
|
|
|
126
154
|
export interface RecordResult<T = unknown> {
|
|
127
155
|
data: T | null;
|
|
128
156
|
isLoading: boolean;
|
|
129
|
-
error:
|
|
130
|
-
code: string;
|
|
131
|
-
message: string;
|
|
132
|
-
} | null;
|
|
157
|
+
error: HookError | null;
|
|
133
158
|
refetch: () => void;
|
|
134
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Which write the `useMutation` hook performs.
|
|
162
|
+
*/
|
|
163
|
+
export type MutationOperation = "create" | "update" | "upsert" | "delete";
|
|
135
164
|
export interface MutationResult<T = unknown> {
|
|
136
|
-
|
|
165
|
+
/**
|
|
166
|
+
* Performs the write. Never throws — on failure it resolves to `null` and
|
|
167
|
+
* populates `error`, so `onClick={() => mutate(...)}` can't produce an
|
|
168
|
+
* unhandled rejection.
|
|
169
|
+
*/
|
|
170
|
+
mutate: (data: Partial<T> & Record<string, unknown>) => Promise<T | null>;
|
|
137
171
|
isLoading: boolean;
|
|
138
|
-
error:
|
|
139
|
-
code: string;
|
|
140
|
-
message: string;
|
|
141
|
-
} | null;
|
|
172
|
+
error: HookError | null;
|
|
142
173
|
}
|
|
143
174
|
export interface DefaultTables {
|
|
144
175
|
[table: string]: Record<string, unknown>;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC;IACjC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weirdscience/based-client",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "SDK for Based — a minimal self-hosted BaaS. Typed client for any JavaScript app, plus React hooks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -10,13 +10,20 @@
|
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://based.weirdscience.dev",
|
|
12
12
|
"type": "module",
|
|
13
|
-
"main": "./dist/index.
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
14
|
"module": "./dist/index.js",
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
|
+
"sideEffects": false,
|
|
16
17
|
"exports": {
|
|
17
18
|
".": {
|
|
18
19
|
"types": "./dist/index.d.ts",
|
|
19
|
-
"import": "./dist/index.js"
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"require": "./dist/index.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./core": {
|
|
24
|
+
"types": "./dist/core.d.ts",
|
|
25
|
+
"import": "./dist/core.js",
|
|
26
|
+
"require": "./dist/core.cjs"
|
|
20
27
|
}
|
|
21
28
|
},
|
|
22
29
|
"files": [
|
|
@@ -24,15 +31,27 @@
|
|
|
24
31
|
"README.md"
|
|
25
32
|
],
|
|
26
33
|
"scripts": {
|
|
27
|
-
"build": "rm -rf dist && NODE_ENV=production bun build src/index.ts --outdir dist --target browser --format esm --external react --external react/jsx-runtime && bun
|
|
34
|
+
"build": "rm -rf dist && NODE_ENV=production bun build src/index.ts --outdir dist --target browser --format esm --external react --external react/jsx-runtime --banner '\"use client\";' && NODE_ENV=production bun build src/index.ts --outfile dist/index.cjs --target node --format cjs --external react --external react/jsx-runtime --banner '\"use client\";' && NODE_ENV=production bun build src/core.ts --outfile dist/core.js --target browser --format esm && NODE_ENV=production bun build src/core.ts --outfile dist/core.cjs --target node --format cjs && bun x tsc -p tsconfig.json",
|
|
28
35
|
"prepublishOnly": "bun run build",
|
|
29
|
-
"test": "bun test"
|
|
36
|
+
"test": "bun test",
|
|
37
|
+
"typecheck": "tsc --noEmit -p tsconfig.test.json"
|
|
30
38
|
},
|
|
31
39
|
"peerDependencies": {
|
|
32
40
|
"react": "^18.0.0 || ^19.0.0"
|
|
33
41
|
},
|
|
34
42
|
"devDependencies": {
|
|
43
|
+
"@happy-dom/global-registrator": "^20.0.0",
|
|
44
|
+
"@testing-library/dom": "^10.4.1",
|
|
45
|
+
"@testing-library/react": "^16.0.0",
|
|
35
46
|
"@types/react": "^19.0.0",
|
|
47
|
+
"@types/react-dom": "^19.0.0",
|
|
48
|
+
"react": "^19.0.0",
|
|
49
|
+
"react-dom": "^19.0.0",
|
|
36
50
|
"typescript": "^5.0.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"react": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
37
56
|
}
|
|
38
57
|
}
|