@weirdscience/based-client 0.4.1 → 0.6.1

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.
@@ -1,5 +1,3 @@
1
- import type { MutationResult, DefaultTables } from "../types";
2
- type MutationOperation = "create" | "update" | "upsert" | "delete";
1
+ import type { MutationResult, MutationOperation, DefaultTables } from "../types";
3
2
  export declare function useMutation<T extends Record<string, Record<string, unknown>> = DefaultTables, K extends string & keyof T = string & keyof T>(table: K, operation: MutationOperation): MutationResult<T[K]>;
4
- export {};
5
3
  //# sourceMappingURL=use-mutation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-mutation.d.ts","sourceRoot":"","sources":["../../src/hooks/use-mutation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9D,KAAK,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEnE,wBAAgB,WAAW,CACzB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,EACjE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,EAE7C,KAAK,EAAE,CAAC,EACR,SAAS,EAAE,iBAAiB,GAC3B,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CA0CtB"}
1
+ {"version":3,"file":"use-mutation.d.ts","sourceRoot":"","sources":["../../src/hooks/use-mutation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,cAAc,EACd,iBAAiB,EACjB,aAAa,EACd,MAAM,UAAU,CAAC;AAElB,wBAAgB,WAAW,CACzB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,EACjE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,EAC7C,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CA6C9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-query.d.ts","sourceRoot":"","sources":["../../src/hooks/use-query.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzE,wBAAgB,QAAQ,CACtB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,EACjE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,EAE7C,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAC3B,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CA0CnB"}
1
+ {"version":3,"file":"use-query.d.ts","sourceRoot":"","sources":["../../src/hooks/use-query.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAEV,YAAY,EACZ,WAAW,EACX,aAAa,EACd,MAAM,UAAU,CAAC;AAmBlB,wBAAgB,QAAQ,CACtB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,EACjE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,EAC7C,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CA6E3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-record.d.ts","sourceRoot":"","sources":["../../src/hooks/use-record.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE3E,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,EACjE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,EAE7C,KAAK,EAAE,CAAC,EACR,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC7B,OAAO,CAAC,EAAE,aAAa,GACtB,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAmCpB"}
1
+ {"version":3,"file":"use-record.d.ts","sourceRoot":"","sources":["../../src/hooks/use-record.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAEV,aAAa,EACb,YAAY,EACZ,aAAa,EACd,MAAM,UAAU,CAAC;AAElB,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,EACjE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,EAE7C,KAAK,EAAE,CAAC,EACR,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC7B,OAAO,CAAC,EAAE,aAAa,GACtB,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAwDpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-user.d.ts","sourceRoot":"","sources":["../../src/hooks/use-user.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,wBAAgB,OAAO,IAAI;IACzB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB,CAcA"}
1
+ {"version":3,"file":"use-user.d.ts","sourceRoot":"","sources":["../../src/hooks/use-user.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,UAAU,CAAC;AAepD,wBAAgB,OAAO,IAAI;IACzB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB,CAcA"}
package/dist/index.cjs ADDED
@@ -0,0 +1,593 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ function __accessProp(key) {
7
+ return this[key];
8
+ }
9
+ var __toCommonJS = (from) => {
10
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
11
+ if (entry)
12
+ return entry;
13
+ entry = __defProp({}, "__esModule", { value: true });
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (var key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(entry, key))
17
+ __defProp(entry, key, {
18
+ get: __accessProp.bind(from, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ __moduleCache.set(from, entry);
23
+ return entry;
24
+ };
25
+ var __moduleCache;
26
+ var __returnValue = (v) => v;
27
+ function __exportSetter(name, newValue) {
28
+ this[name] = __returnValue.bind(null, newValue);
29
+ }
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, {
33
+ get: all[name],
34
+ enumerable: true,
35
+ configurable: true,
36
+ set: __exportSetter.bind(all, name)
37
+ });
38
+ };
39
+
40
+ // src/index.ts
41
+ var exports_src = {};
42
+ __export(exports_src, {
43
+ useUser: () => useUser,
44
+ useRecord: () => useRecord,
45
+ useQuery: () => useQuery,
46
+ useMutation: () => useMutation,
47
+ useBasedClient: () => useBasedClient,
48
+ createClient: () => createClient,
49
+ BasedProvider: () => BasedProvider,
50
+ BasedError: () => BasedError
51
+ });
52
+ module.exports = __toCommonJS(exports_src);
53
+
54
+ // src/types.ts
55
+ class BasedError extends Error {
56
+ code;
57
+ status;
58
+ details;
59
+ constructor(code, message, status, details) {
60
+ super(message);
61
+ this.name = "BasedError";
62
+ this.code = code;
63
+ this.status = status;
64
+ this.details = details;
65
+ }
66
+ }
67
+ function toHookError(err) {
68
+ if (err instanceof BasedError) {
69
+ return {
70
+ code: err.code,
71
+ message: err.message,
72
+ status: err.status,
73
+ details: err.details
74
+ };
75
+ }
76
+ return {
77
+ code: "NETWORK_ERROR",
78
+ message: err instanceof Error ? err.message : String(err)
79
+ };
80
+ }
81
+
82
+ // src/client.ts
83
+ var DEFAULT_STORAGE_KEY = "based.session";
84
+ function defaultStorage() {
85
+ if (typeof globalThis === "undefined")
86
+ return null;
87
+ const ls = globalThis.localStorage;
88
+ if (!ls || typeof ls.getItem !== "function")
89
+ return null;
90
+ return {
91
+ getItem: (k) => ls.getItem(k),
92
+ setItem: (k, v) => ls.setItem(k, v),
93
+ removeItem: (k) => ls.removeItem(k)
94
+ };
95
+ }
96
+ function createClient(options) {
97
+ const { url, anonKey } = options;
98
+ const baseUrl = url.replace(/\/$/, "");
99
+ const storage = options.storage === false ? null : options.storage ?? defaultStorage();
100
+ const storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;
101
+ let state = {
102
+ user: null,
103
+ accessToken: null,
104
+ refreshToken: null,
105
+ isLoading: !!storage
106
+ };
107
+ const listeners = new Set;
108
+ function notify() {
109
+ for (const listener of listeners)
110
+ listener();
111
+ }
112
+ function subscribe(listener) {
113
+ listeners.add(listener);
114
+ return () => listeners.delete(listener);
115
+ }
116
+ function getState() {
117
+ return state;
118
+ }
119
+ function onAuthStateChange(callback) {
120
+ return subscribe(() => callback(state));
121
+ }
122
+ async function persist() {
123
+ if (!storage)
124
+ return;
125
+ const snapshot = {
126
+ user: state.user,
127
+ accessToken: state.accessToken,
128
+ refreshToken: state.refreshToken
129
+ };
130
+ if (!snapshot.refreshToken) {
131
+ await storage.removeItem(storageKey);
132
+ return;
133
+ }
134
+ await storage.setItem(storageKey, JSON.stringify(snapshot));
135
+ }
136
+ function setState(next) {
137
+ state = { ...state, ...next };
138
+ notify();
139
+ persist().catch(() => {});
140
+ }
141
+ const readyPromise = (async () => {
142
+ if (!storage) {
143
+ setState({ isLoading: false });
144
+ return;
145
+ }
146
+ try {
147
+ const raw = await storage.getItem(storageKey);
148
+ if (!raw) {
149
+ setState({ isLoading: false });
150
+ return;
151
+ }
152
+ const stored = JSON.parse(raw);
153
+ if (!stored.refreshToken) {
154
+ setState({ isLoading: false });
155
+ return;
156
+ }
157
+ state = {
158
+ user: stored.user,
159
+ accessToken: stored.accessToken,
160
+ refreshToken: stored.refreshToken,
161
+ isLoading: true
162
+ };
163
+ notify();
164
+ const user = await getUser();
165
+ if (!user) {
166
+ const refreshed = await refreshSession();
167
+ if (refreshed) {
168
+ await getUser();
169
+ }
170
+ }
171
+ } catch {
172
+ try {
173
+ await storage.removeItem(storageKey);
174
+ } catch {}
175
+ } finally {
176
+ setState({ isLoading: false });
177
+ }
178
+ })();
179
+ function ready() {
180
+ return readyPromise;
181
+ }
182
+ async function fetchWithAuth(path, init = {}) {
183
+ const headers = new Headers(init.headers);
184
+ if (state.accessToken) {
185
+ headers.set("Authorization", `Bearer ${state.accessToken}`);
186
+ } else {
187
+ headers.set("apikey", anonKey);
188
+ }
189
+ if (!headers.has("Content-Type") && init.body) {
190
+ headers.set("Content-Type", "application/json");
191
+ }
192
+ const tokenAtRequest = state.accessToken;
193
+ let res = await fetch(`${baseUrl}${path}`, { ...init, headers });
194
+ if (res.status === 401 && state.refreshToken) {
195
+ const alreadyRefreshed = state.accessToken !== tokenAtRequest;
196
+ const refreshed = alreadyRefreshed ? true : await refreshSession();
197
+ if (refreshed && state.accessToken) {
198
+ headers.set("Authorization", `Bearer ${state.accessToken}`);
199
+ res = await fetch(`${baseUrl}${path}`, { ...init, headers });
200
+ }
201
+ }
202
+ return res;
203
+ }
204
+ async function authError(res, fallback) {
205
+ const json = await res.json().catch(() => null);
206
+ return new BasedError(json?.error?.code ?? "AUTH_ERROR", json?.error?.message || fallback, res.status, json?.error?.details);
207
+ }
208
+ async function signUp(email, password) {
209
+ const res = await fetch(`${baseUrl}/auth/signup`, {
210
+ method: "POST",
211
+ headers: { "Content-Type": "application/json" },
212
+ body: JSON.stringify({ email, password })
213
+ });
214
+ if (!res.ok) {
215
+ throw await authError(res, "Sign up failed");
216
+ }
217
+ const { data } = await res.json();
218
+ setState({
219
+ user: data.user,
220
+ accessToken: data.accessToken,
221
+ refreshToken: data.refreshToken,
222
+ isLoading: false
223
+ });
224
+ return data.user;
225
+ }
226
+ async function signIn(email, password) {
227
+ const res = await fetch(`${baseUrl}/auth/signin`, {
228
+ method: "POST",
229
+ headers: { "Content-Type": "application/json" },
230
+ body: JSON.stringify({ email, password })
231
+ });
232
+ if (!res.ok) {
233
+ throw await authError(res, "Sign in failed");
234
+ }
235
+ const { data } = await res.json();
236
+ setState({
237
+ user: data.user,
238
+ accessToken: data.accessToken,
239
+ refreshToken: data.refreshToken,
240
+ isLoading: false
241
+ });
242
+ return data.user;
243
+ }
244
+ async function signOut() {
245
+ if (state.accessToken) {
246
+ try {
247
+ await fetch(`${baseUrl}/auth/signout`, {
248
+ method: "POST",
249
+ headers: { Authorization: `Bearer ${state.accessToken}` }
250
+ });
251
+ } catch {}
252
+ }
253
+ setState({ user: null, accessToken: null, refreshToken: null, isLoading: false });
254
+ }
255
+ let refreshInFlight = null;
256
+ function refreshSession() {
257
+ if (!refreshInFlight) {
258
+ refreshInFlight = doRefresh().finally(() => {
259
+ refreshInFlight = null;
260
+ });
261
+ }
262
+ return refreshInFlight;
263
+ }
264
+ async function doRefresh() {
265
+ if (!state.refreshToken)
266
+ return false;
267
+ try {
268
+ const res = await fetch(`${baseUrl}/auth/refresh`, {
269
+ method: "POST",
270
+ headers: { "Content-Type": "application/json" },
271
+ body: JSON.stringify({ refreshToken: state.refreshToken })
272
+ });
273
+ if (!res.ok) {
274
+ setState({ user: null, accessToken: null, refreshToken: null, isLoading: false });
275
+ return false;
276
+ }
277
+ const { data } = await res.json();
278
+ setState({
279
+ accessToken: data.accessToken,
280
+ refreshToken: data.refreshToken
281
+ });
282
+ return true;
283
+ } catch {
284
+ return false;
285
+ }
286
+ }
287
+ async function getUser() {
288
+ if (!state.accessToken)
289
+ return null;
290
+ try {
291
+ const res = await fetch(`${baseUrl}/auth/me`, {
292
+ headers: { Authorization: `Bearer ${state.accessToken}` }
293
+ });
294
+ if (!res.ok) {
295
+ if (res.status === 401) {
296
+ setState({ user: null });
297
+ }
298
+ return null;
299
+ }
300
+ const { data } = await res.json();
301
+ const user = { id: data.id, email: data.email, role: data.role };
302
+ setState({ user });
303
+ return user;
304
+ } catch {
305
+ return null;
306
+ }
307
+ }
308
+ async function parseOrThrow(res, fallbackCode) {
309
+ let json = {};
310
+ try {
311
+ json = await res.json();
312
+ } catch {}
313
+ if (!res.ok) {
314
+ const err = json.error;
315
+ throw new BasedError(err?.code || fallbackCode, err?.message || `Request failed with status ${res.status}`, res.status, err?.details);
316
+ }
317
+ return json;
318
+ }
319
+ function from(table) {
320
+ const basePath = `/api/${table}`;
321
+ return {
322
+ async select(opts) {
323
+ const params = new URLSearchParams;
324
+ if (opts?.limit !== undefined)
325
+ params.set("limit", String(opts.limit));
326
+ if (opts?.offset !== undefined)
327
+ params.set("offset", String(opts.offset));
328
+ if (opts?.order !== undefined)
329
+ params.set("order", opts.order);
330
+ if (opts?.filter) {
331
+ for (const [key, value] of Object.entries(opts.filter)) {
332
+ if (value !== undefined)
333
+ params.set(key, String(value));
334
+ }
335
+ }
336
+ const query = params.toString();
337
+ const res = await fetchWithAuth(`${basePath}${query ? `?${query}` : ""}`);
338
+ const json = await parseOrThrow(res, "SELECT_FAILED");
339
+ return { data: json.data, total: json.total ?? json.data.length };
340
+ },
341
+ async get(id) {
342
+ const res = await fetchWithAuth(`${basePath}/${encodeURIComponent(id)}`);
343
+ if (res.status === 404)
344
+ return null;
345
+ const json = await parseOrThrow(res, "GET_FAILED");
346
+ return json.data;
347
+ },
348
+ async insert(data) {
349
+ const res = await fetchWithAuth(basePath, {
350
+ method: "POST",
351
+ body: JSON.stringify(data)
352
+ });
353
+ const json = await parseOrThrow(res, "INSERT_FAILED");
354
+ return json.data;
355
+ },
356
+ async update(id, data) {
357
+ const body = { ...data };
358
+ delete body.id;
359
+ const res = await fetchWithAuth(`${basePath}/${encodeURIComponent(id)}`, {
360
+ method: "PUT",
361
+ body: JSON.stringify(body)
362
+ });
363
+ const json = await parseOrThrow(res, "UPDATE_FAILED");
364
+ return json.data;
365
+ },
366
+ async upsert(data) {
367
+ const body = { ...data };
368
+ const id = body.id;
369
+ if (!id) {
370
+ throw new BasedError("MISSING_ID", "upsert() requires `data.id`. Use insert() to let the server generate one.", 400);
371
+ }
372
+ delete body.id;
373
+ const res = await fetchWithAuth(`${basePath}/${encodeURIComponent(id)}`, {
374
+ method: "PUT",
375
+ body: JSON.stringify(body)
376
+ });
377
+ const json = await parseOrThrow(res, "UPSERT_FAILED");
378
+ return json.data;
379
+ },
380
+ async delete(id) {
381
+ const res = await fetchWithAuth(`${basePath}/${encodeURIComponent(id)}`, {
382
+ method: "DELETE"
383
+ });
384
+ await parseOrThrow(res, "DELETE_FAILED");
385
+ return { deleted: true };
386
+ }
387
+ };
388
+ }
389
+ return {
390
+ auth: { signUp, signIn, signOut, refreshSession, getUser, onAuthStateChange },
391
+ fetch: fetchWithAuth,
392
+ from,
393
+ subscribe,
394
+ getState,
395
+ ready
396
+ };
397
+ }
398
+ // src/provider.tsx
399
+ var import_react = require("react");
400
+ var jsx_runtime = require("react/jsx-runtime");
401
+ "use client";
402
+ var BasedContext = import_react.createContext(null);
403
+ function BasedProvider({
404
+ client,
405
+ children
406
+ }) {
407
+ return /* @__PURE__ */ jsx_runtime.jsx(BasedContext.Provider, {
408
+ value: client,
409
+ children
410
+ });
411
+ }
412
+ function useBasedClient() {
413
+ const client = import_react.useContext(BasedContext);
414
+ if (!client) {
415
+ throw new Error("useBasedClient must be used within a BasedProvider");
416
+ }
417
+ return client;
418
+ }
419
+ // src/hooks/use-user.ts
420
+ var import_react2 = require("react");
421
+ "use client";
422
+ var SERVER_SNAPSHOT = Object.freeze({
423
+ user: null,
424
+ accessToken: null,
425
+ refreshToken: null,
426
+ isLoading: true
427
+ });
428
+ function useUser() {
429
+ const client = useBasedClient();
430
+ const state = import_react2.useSyncExternalStore(client.subscribe, () => client.getState(), () => SERVER_SNAPSHOT);
431
+ return {
432
+ user: state.user,
433
+ isLoading: state.isLoading,
434
+ error: null
435
+ };
436
+ }
437
+ // src/hooks/use-query.ts
438
+ var import_react3 = require("react");
439
+ "use client";
440
+ function stableKey(value) {
441
+ if (value === undefined)
442
+ return "undefined";
443
+ if (value === null || typeof value !== "object")
444
+ return JSON.stringify(value);
445
+ if (Array.isArray(value))
446
+ return `[${value.map(stableKey).join(",")}]`;
447
+ const entries = Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
448
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableKey(v)}`).join(",")}}`;
449
+ }
450
+ function useQuery(table, options) {
451
+ const client = useBasedClient();
452
+ const enabled = options?.enabled !== false;
453
+ const authKey = import_react3.useSyncExternalStore(client.subscribe, () => client.getState().user?.id ?? null, () => null);
454
+ const [data, setData] = import_react3.useState(null);
455
+ const [total, setTotal] = import_react3.useState(0);
456
+ const [isLoading, setIsLoading] = import_react3.useState(enabled);
457
+ const [error, setError] = import_react3.useState(null);
458
+ const optionsKey = stableKey(options);
459
+ const selectOptions = import_react3.useMemo(() => ({
460
+ filter: options?.filter,
461
+ limit: options?.limit,
462
+ offset: options?.offset,
463
+ order: options?.order
464
+ }), [optionsKey]);
465
+ const latestRequest = import_react3.useRef(0);
466
+ const runQuery = import_react3.useCallback(async () => {
467
+ const requestId = ++latestRequest.current;
468
+ setIsLoading(true);
469
+ setError(null);
470
+ try {
471
+ const result = await client.from(table).select(selectOptions);
472
+ if (latestRequest.current !== requestId)
473
+ return;
474
+ setData(result.data);
475
+ setTotal(result.total);
476
+ } catch (err) {
477
+ if (latestRequest.current !== requestId)
478
+ return;
479
+ setError(toHookError(err));
480
+ setData(null);
481
+ setTotal(0);
482
+ } finally {
483
+ if (latestRequest.current === requestId)
484
+ setIsLoading(false);
485
+ }
486
+ }, [client, table, selectOptions]);
487
+ import_react3.useEffect(() => {
488
+ if (!enabled) {
489
+ latestRequest.current += 1;
490
+ setData(null);
491
+ setTotal(0);
492
+ setError(null);
493
+ setIsLoading(false);
494
+ return;
495
+ }
496
+ runQuery();
497
+ return () => {
498
+ latestRequest.current += 1;
499
+ };
500
+ }, [enabled, authKey, runQuery]);
501
+ return { data, total, isLoading, error, refetch: runQuery };
502
+ }
503
+ // src/hooks/use-record.ts
504
+ var import_react4 = require("react");
505
+ "use client";
506
+ function useRecord(table, id, options) {
507
+ const client = useBasedClient();
508
+ const enabled = options?.enabled !== false && !!id;
509
+ const authKey = import_react4.useSyncExternalStore(client.subscribe, () => client.getState().user?.id ?? null, () => null);
510
+ const [data, setData] = import_react4.useState(null);
511
+ const [isLoading, setIsLoading] = import_react4.useState(enabled);
512
+ const [error, setError] = import_react4.useState(null);
513
+ const latestRequest = import_react4.useRef(0);
514
+ const runQuery = import_react4.useCallback(async () => {
515
+ if (!id)
516
+ return;
517
+ const requestId = ++latestRequest.current;
518
+ setIsLoading(true);
519
+ setError(null);
520
+ try {
521
+ const row = await client.from(table).get(id);
522
+ if (latestRequest.current !== requestId)
523
+ return;
524
+ setData(row);
525
+ } catch (err) {
526
+ if (latestRequest.current !== requestId)
527
+ return;
528
+ setError(toHookError(err));
529
+ setData(null);
530
+ } finally {
531
+ if (latestRequest.current === requestId)
532
+ setIsLoading(false);
533
+ }
534
+ }, [client, table, id]);
535
+ import_react4.useEffect(() => {
536
+ if (!enabled) {
537
+ latestRequest.current += 1;
538
+ setData(null);
539
+ setError(null);
540
+ setIsLoading(false);
541
+ return;
542
+ }
543
+ runQuery();
544
+ return () => {
545
+ latestRequest.current += 1;
546
+ };
547
+ }, [enabled, authKey, runQuery]);
548
+ return { data, isLoading, error, refetch: runQuery };
549
+ }
550
+ // src/hooks/use-mutation.ts
551
+ var import_react5 = require("react");
552
+ "use client";
553
+ function useMutation(table, operation) {
554
+ const client = useBasedClient();
555
+ const [isLoading, setIsLoading] = import_react5.useState(false);
556
+ const [error, setError] = import_react5.useState(null);
557
+ const mutate = import_react5.useCallback(async (data) => {
558
+ setIsLoading(true);
559
+ setError(null);
560
+ try {
561
+ const builder = client.from(table);
562
+ switch (operation) {
563
+ case "create":
564
+ return await builder.insert(data);
565
+ case "update": {
566
+ if (!data.id)
567
+ throw new BasedError("MISSING_ID", "Update requires an 'id' field", 400);
568
+ return await builder.update(String(data.id), data);
569
+ }
570
+ case "upsert": {
571
+ if (!data.id)
572
+ throw new BasedError("MISSING_ID", "Upsert requires an 'id' field", 400);
573
+ return await builder.upsert(data);
574
+ }
575
+ case "delete": {
576
+ if (!data.id)
577
+ throw new BasedError("MISSING_ID", "Delete requires an 'id' field", 400);
578
+ await builder.delete(String(data.id));
579
+ return data;
580
+ }
581
+ }
582
+ } catch (err) {
583
+ setError(toHookError(err));
584
+ return null;
585
+ } finally {
586
+ setIsLoading(false);
587
+ }
588
+ }, [client, table, operation]);
589
+ return { mutate, isLoading, error };
590
+ }
591
+
592
+ // src/index.ts
593
+ "use client";
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- export { createClient } from "./client";
1
+ export { createClient, BasedError, type BasedClient, type BasedClientOptions, type BasedTable, type ListResult, type StorageAdapter, type AuthUser, type AuthState, type QueryOptions, type DefaultTables, } from "./core";
2
2
  export { BasedProvider, useBasedClient } from "./provider";
3
3
  export { useUser } from "./hooks/use-user";
4
4
  export { useQuery } from "./hooks/use-query";
5
5
  export { useRecord } from "./hooks/use-record";
6
6
  export { useMutation } from "./hooks/use-mutation";
7
- export { BasedError } from "./types";
8
- export type { BasedClient, BasedClientOptions, BasedTable, ListResult, StorageAdapter, AuthUser, AuthState, QueryOptions, QueryResult, RecordOptions, RecordResult, MutationResult, DefaultTables, } from "./types";
7
+ export type { QueryResult, RecordOptions, RecordResult, HookError, MutationResult, MutationOperation, } from "./types";
9
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,GACd,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,SAAS,EACT,cAAc,EACd,iBAAiB,GAClB,MAAM,SAAS,CAAC"}