@mimdb/react 0.1.0 → 0.2.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/dist/index.cjs +112 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -10
- package/dist/index.d.ts +100 -10
- package/dist/index.js +105 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,12 +21,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
MimDBProvider: () => MimDBProvider,
|
|
24
|
+
createServerClient: () => import_client.createServerClient,
|
|
24
25
|
useAuth: () => useAuth,
|
|
25
26
|
useClient: () => useClient,
|
|
26
27
|
useDelete: () => useDelete,
|
|
27
28
|
useInsert: () => useInsert,
|
|
28
29
|
useQuery: () => useQuery,
|
|
29
30
|
useRealtime: () => useRealtime,
|
|
31
|
+
useSubscription: () => useSubscription,
|
|
30
32
|
useUpdate: () => useUpdate,
|
|
31
33
|
useUpload: () => useUpload
|
|
32
34
|
});
|
|
@@ -86,7 +88,7 @@ function useQuery(table, options) {
|
|
|
86
88
|
|
|
87
89
|
// src/use-mutation.ts
|
|
88
90
|
var import_react_query2 = require("@tanstack/react-query");
|
|
89
|
-
function useInsert(table) {
|
|
91
|
+
function useInsert(table, options) {
|
|
90
92
|
const client = useClient();
|
|
91
93
|
const queryClient = (0, import_react_query2.useQueryClient)();
|
|
92
94
|
return (0, import_react_query2.useMutation)({
|
|
@@ -95,12 +97,26 @@ function useInsert(table) {
|
|
|
95
97
|
if (error) throw error;
|
|
96
98
|
return result;
|
|
97
99
|
},
|
|
98
|
-
|
|
100
|
+
onMutate: options?.optimistic ? async (newData) => {
|
|
101
|
+
await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
|
|
102
|
+
const previous = queryClient.getQueryData(["mimdb", table]);
|
|
103
|
+
queryClient.setQueryData(["mimdb", table], (old) => [
|
|
104
|
+
...old ?? [],
|
|
105
|
+
newData
|
|
106
|
+
]);
|
|
107
|
+
return { previous };
|
|
108
|
+
} : void 0,
|
|
109
|
+
onError: options?.optimistic ? (_err, _data, context) => {
|
|
110
|
+
if (context?.previous !== void 0) {
|
|
111
|
+
queryClient.setQueryData(["mimdb", table], context.previous);
|
|
112
|
+
}
|
|
113
|
+
} : void 0,
|
|
114
|
+
onSettled: () => {
|
|
99
115
|
queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
|
|
100
116
|
}
|
|
101
117
|
});
|
|
102
118
|
}
|
|
103
|
-
function useUpdate(table) {
|
|
119
|
+
function useUpdate(table, options) {
|
|
104
120
|
const client = useClient();
|
|
105
121
|
const queryClient = (0, import_react_query2.useQueryClient)();
|
|
106
122
|
return (0, import_react_query2.useMutation)({
|
|
@@ -113,12 +129,32 @@ function useUpdate(table) {
|
|
|
113
129
|
if (error) throw error;
|
|
114
130
|
return result;
|
|
115
131
|
},
|
|
116
|
-
|
|
132
|
+
onMutate: options?.optimistic ? async ({ data, eq }) => {
|
|
133
|
+
await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
|
|
134
|
+
const previous = queryClient.getQueryData(["mimdb", table]);
|
|
135
|
+
queryClient.setQueryData(
|
|
136
|
+
["mimdb", table],
|
|
137
|
+
(old) => (old ?? []).map((row) => {
|
|
138
|
+
const record = row;
|
|
139
|
+
const matches = Object.entries(eq).every(
|
|
140
|
+
([col, val]) => String(record[col]) === val
|
|
141
|
+
);
|
|
142
|
+
return matches ? { ...row, ...data } : row;
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
return { previous };
|
|
146
|
+
} : void 0,
|
|
147
|
+
onError: options?.optimistic ? (_err, _data, context) => {
|
|
148
|
+
if (context?.previous !== void 0) {
|
|
149
|
+
queryClient.setQueryData(["mimdb", table], context.previous);
|
|
150
|
+
}
|
|
151
|
+
} : void 0,
|
|
152
|
+
onSettled: () => {
|
|
117
153
|
queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
|
|
118
154
|
}
|
|
119
155
|
});
|
|
120
156
|
}
|
|
121
|
-
function useDelete(table) {
|
|
157
|
+
function useDelete(table, options) {
|
|
122
158
|
const client = useClient();
|
|
123
159
|
const queryClient = (0, import_react_query2.useQueryClient)();
|
|
124
160
|
return (0, import_react_query2.useMutation)({
|
|
@@ -130,7 +166,25 @@ function useDelete(table) {
|
|
|
130
166
|
const { error } = await query;
|
|
131
167
|
if (error) throw error;
|
|
132
168
|
},
|
|
133
|
-
|
|
169
|
+
onMutate: options?.optimistic ? async (eq) => {
|
|
170
|
+
await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
|
|
171
|
+
const previous = queryClient.getQueryData(["mimdb", table]);
|
|
172
|
+
queryClient.setQueryData(
|
|
173
|
+
["mimdb", table],
|
|
174
|
+
(old) => (old ?? []).filter(
|
|
175
|
+
(row) => !Object.entries(eq).every(
|
|
176
|
+
([col, val]) => String(row[col]) === val
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
return { previous };
|
|
181
|
+
} : void 0,
|
|
182
|
+
onError: options?.optimistic ? (_err, _data, context) => {
|
|
183
|
+
if (context?.previous !== void 0) {
|
|
184
|
+
queryClient.setQueryData(["mimdb", table], context.previous);
|
|
185
|
+
}
|
|
186
|
+
} : void 0,
|
|
187
|
+
onSettled: () => {
|
|
134
188
|
queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
|
|
135
189
|
}
|
|
136
190
|
});
|
|
@@ -146,6 +200,7 @@ function useRealtime(table, options) {
|
|
|
146
200
|
const [status, setStatus] = (0, import_react2.useState)("pending");
|
|
147
201
|
const realtimeRef = (0, import_react2.useRef)(null);
|
|
148
202
|
(0, import_react2.useEffect)(() => {
|
|
203
|
+
if (typeof window === "undefined") return;
|
|
149
204
|
const config = client.getConfig();
|
|
150
205
|
if (!realtimeRef.current) {
|
|
151
206
|
realtimeRef.current = new import_realtime.MimDBRealtimeClient({
|
|
@@ -179,13 +234,43 @@ function useRealtime(table, options) {
|
|
|
179
234
|
return { status };
|
|
180
235
|
}
|
|
181
236
|
|
|
182
|
-
// src/use-
|
|
237
|
+
// src/use-subscription.ts
|
|
183
238
|
var import_react3 = require("react");
|
|
184
|
-
function
|
|
239
|
+
function useSubscription(table, options) {
|
|
185
240
|
const client = useClient();
|
|
186
|
-
const [
|
|
187
|
-
const [
|
|
241
|
+
const [lastEvent, setLastEvent] = (0, import_react3.useState)(null);
|
|
242
|
+
const [status, setStatus] = (0, import_react3.useState)("pending");
|
|
188
243
|
(0, import_react3.useEffect)(() => {
|
|
244
|
+
if (typeof window === "undefined") return;
|
|
245
|
+
const sub = client.realtime.subscribe(table, {
|
|
246
|
+
event: options?.event ?? "*",
|
|
247
|
+
filter: options?.filter,
|
|
248
|
+
onEvent(event) {
|
|
249
|
+
setLastEvent(event);
|
|
250
|
+
},
|
|
251
|
+
onSubscribed() {
|
|
252
|
+
setStatus("active");
|
|
253
|
+
},
|
|
254
|
+
onError() {
|
|
255
|
+
setStatus("error");
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
setStatus("pending");
|
|
259
|
+
return () => {
|
|
260
|
+
sub.unsubscribe();
|
|
261
|
+
setStatus("closed");
|
|
262
|
+
};
|
|
263
|
+
}, [table, options?.event, options?.filter]);
|
|
264
|
+
return { lastEvent, status };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/use-auth.ts
|
|
268
|
+
var import_react4 = require("react");
|
|
269
|
+
function useAuth() {
|
|
270
|
+
const client = useClient();
|
|
271
|
+
const [user, setUser] = (0, import_react4.useState)(null);
|
|
272
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
|
|
273
|
+
(0, import_react4.useEffect)(() => {
|
|
189
274
|
const session = client.auth.getSession();
|
|
190
275
|
if (session) {
|
|
191
276
|
client.auth.getUser().then(setUser).catch(() => setUser(null)).finally(() => setIsLoading(false));
|
|
@@ -195,31 +280,33 @@ function useAuth() {
|
|
|
195
280
|
const unsub = client.auth.onAuthStateChange((event, _session) => {
|
|
196
281
|
if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
|
|
197
282
|
client.auth.getUser().then(setUser).catch(() => setUser(null));
|
|
198
|
-
} else if (event === "SIGNED_OUT") {
|
|
283
|
+
} else if (event === "SIGNED_OUT" || event === "TOKEN_REFRESH_FAILED") {
|
|
199
284
|
setUser(null);
|
|
200
285
|
}
|
|
201
286
|
});
|
|
202
287
|
return unsub;
|
|
203
288
|
}, [client]);
|
|
204
|
-
const signIn = (0,
|
|
289
|
+
const signIn = (0, import_react4.useCallback)(
|
|
205
290
|
async (email, password) => {
|
|
206
291
|
await client.auth.signIn(email, password);
|
|
207
292
|
},
|
|
208
293
|
[client]
|
|
209
294
|
);
|
|
210
|
-
const signUp = (0,
|
|
295
|
+
const signUp = (0, import_react4.useCallback)(
|
|
211
296
|
async (email, password) => {
|
|
212
297
|
await client.auth.signUp(email, password);
|
|
213
298
|
},
|
|
214
299
|
[client]
|
|
215
300
|
);
|
|
216
|
-
const signOut = (0,
|
|
301
|
+
const signOut = (0, import_react4.useCallback)(async () => {
|
|
217
302
|
await client.auth.signOut();
|
|
218
303
|
}, [client]);
|
|
219
|
-
const signInWithOAuth = (0,
|
|
304
|
+
const signInWithOAuth = (0, import_react4.useCallback)(
|
|
220
305
|
(provider, opts) => {
|
|
221
306
|
const url = client.auth.signInWithOAuth(provider, opts);
|
|
222
|
-
window
|
|
307
|
+
if (typeof window !== "undefined") {
|
|
308
|
+
window.location.href = url;
|
|
309
|
+
}
|
|
223
310
|
},
|
|
224
311
|
[client]
|
|
225
312
|
);
|
|
@@ -227,12 +314,12 @@ function useAuth() {
|
|
|
227
314
|
}
|
|
228
315
|
|
|
229
316
|
// src/use-upload.ts
|
|
230
|
-
var
|
|
317
|
+
var import_react5 = require("react");
|
|
231
318
|
function useUpload(bucket) {
|
|
232
319
|
const client = useClient();
|
|
233
|
-
const [isUploading, setIsUploading] = (0,
|
|
234
|
-
const [error, setError] = (0,
|
|
235
|
-
const upload = (0,
|
|
320
|
+
const [isUploading, setIsUploading] = (0, import_react5.useState)(false);
|
|
321
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
322
|
+
const upload = (0, import_react5.useCallback)(
|
|
236
323
|
async (path, file, opts) => {
|
|
237
324
|
setIsUploading(true);
|
|
238
325
|
setError(null);
|
|
@@ -250,15 +337,20 @@ function useUpload(bucket) {
|
|
|
250
337
|
);
|
|
251
338
|
return { upload, isUploading, error };
|
|
252
339
|
}
|
|
340
|
+
|
|
341
|
+
// src/index.ts
|
|
342
|
+
var import_client = require("@mimdb/client");
|
|
253
343
|
// Annotate the CommonJS export names for ESM import in node:
|
|
254
344
|
0 && (module.exports = {
|
|
255
345
|
MimDBProvider,
|
|
346
|
+
createServerClient,
|
|
256
347
|
useAuth,
|
|
257
348
|
useClient,
|
|
258
349
|
useDelete,
|
|
259
350
|
useInsert,
|
|
260
351
|
useQuery,
|
|
261
352
|
useRealtime,
|
|
353
|
+
useSubscription,
|
|
262
354
|
useUpdate,
|
|
263
355
|
useUpload
|
|
264
356
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/provider.tsx","../src/use-query.ts","../src/use-mutation.ts","../src/use-realtime.ts","../src/use-auth.ts","../src/use-upload.ts"],"sourcesContent":["export { MimDBProvider, type MimDBProviderProps } from './provider'\nexport { useClient } from './context'\nexport { useQuery, type UseQueryOptions } from './use-query'\nexport { useInsert, useUpdate, useDelete, type UpdateInput } from './use-mutation'\nexport { useRealtime, type UseRealtimeOptions } from './use-realtime'\nexport { useAuth, type UseAuthResult } from './use-auth'\nexport { useUpload, type UseUploadResult } from './use-upload'\n","import { createContext, useContext } from 'react'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * React context that holds the MimDB client instance.\n *\n * Consumers should use the {@link useClient} hook rather than accessing\n * this context directly.\n *\n * @internal\n */\nconst MimDBContext = createContext<MimDBClient | null>(null)\n\n/**\n * Retrieve the MimDB client from the nearest `<MimDBProvider>`.\n *\n * @returns The `MimDBClient` instance provided by the enclosing provider.\n * @throws If called outside of a `<MimDBProvider>` tree.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useClient()\n * // Use client.from(), client.auth, etc.\n * }\n * ```\n */\nexport function useClient(): MimDBClient {\n const client = useContext(MimDBContext)\n if (!client) {\n throw new Error('useClient must be used within <MimDBProvider>')\n }\n return client\n}\n\nexport { MimDBContext }\n","import { type ReactNode } from 'react'\nimport { MimDBContext } from './context'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * Props for the {@link MimDBProvider} component.\n */\nexport interface MimDBProviderProps {\n /** A configured `MimDBClient` instance to make available to child components. */\n client: MimDBClient\n /** The React subtree that will have access to the MimDB client. */\n children: ReactNode\n}\n\n/**\n * Context provider that makes a `MimDBClient` available to all descendant\n * components via the {@link useClient} hook.\n *\n * Wrap your application (or a subtree) with this provider and pass a\n * pre-configured client instance.\n *\n * @param props - Provider props containing the client and children.\n *\n * @example\n * ```tsx\n * import { createClient } from '@mimdb/client'\n * import { MimDBProvider } from '@mimdb/react'\n *\n * const client = createClient('https://api.mimdb.dev', 'ref', 'key')\n *\n * function App() {\n * return (\n * <MimDBProvider client={client}>\n * <MyApp />\n * </MimDBProvider>\n * )\n * }\n * ```\n */\nexport function MimDBProvider({ client, children }: MimDBProviderProps) {\n return <MimDBContext.Provider value={client}>{children}</MimDBContext.Provider>\n}\n","import {\n useQuery as useTanstackQuery,\n type UseQueryResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useQuery} hook.\n */\nexport interface UseQueryOptions {\n /** Column selection string (PostgREST format). Defaults to `'*'`. */\n select?: string\n /** Equality filters applied as `query.eq(column, value)`. */\n eq?: Record<string, string>\n /** Not-equal filters applied as `query.neq(column, value)`. */\n neq?: Record<string, string>\n /** Column ordering configuration. */\n order?: { column: string; ascending?: boolean }\n /** Maximum number of rows to return. */\n limit?: number\n /** Number of rows to skip before returning results. */\n offset?: number\n /** Whether the query should execute. Maps to TanStack Query's `enabled`. */\n enabled?: boolean\n /** Duration in ms before cached data is considered stale. */\n staleTime?: number\n /** Polling interval in ms, or `false` to disable. */\n refetchInterval?: number | false\n}\n\n/**\n * Fetch rows from a MimDB table using the REST API, backed by TanStack Query\n * for caching, deduplication, and background refetching.\n *\n * Automatically derives a stable query key from the table name and options\n * so cache invalidation works out of the box with the mutation hooks.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table to query.\n * @param options - Query filters, modifiers, and TanStack Query settings.\n * @returns A TanStack `UseQueryResult` containing the row array and status flags.\n *\n * @example\n * ```tsx\n * const { data, isLoading } = useQuery<Todo>('todos', {\n * eq: { done: 'false' },\n * order: { column: 'created_at', ascending: false },\n * limit: 20,\n * })\n * ```\n */\nexport function useQuery<T = Record<string, unknown>>(\n table: string,\n options?: UseQueryOptions,\n): UseQueryResult<T[], Error> {\n const client = useClient()\n\n return useTanstackQuery({\n queryKey: ['mimdb', table, options],\n queryFn: async () => {\n let query = client.from<T>(table).select(options?.select ?? '*')\n\n if (options?.eq) {\n for (const [col, val] of Object.entries(options.eq)) {\n query = query.eq(col, val)\n }\n }\n if (options?.neq) {\n for (const [col, val] of Object.entries(options.neq)) {\n query = query.neq(col, val)\n }\n }\n if (options?.order) {\n query = query.order(options.order.column, {\n ascending: options.order.ascending,\n })\n }\n if (options?.limit) query = query.limit(options.limit)\n if (options?.offset) query = query.offset(options.offset)\n\n const { data, error } = await query\n if (error) throw error\n return (data ?? []) as T[]\n },\n enabled: options?.enabled,\n staleTime: options?.staleTime,\n refetchInterval: options?.refetchInterval,\n })\n}\n","import {\n useMutation as useTanstackMutation,\n useQueryClient,\n type UseMutationResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\nimport type { MimDBError } from '@mimdb/client'\n\n/**\n * React hook for inserting a row into a MimDB table.\n *\n * On success, all `useQuery` caches for the same table are automatically\n * invalidated so lists stay in sync.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @returns A TanStack `UseMutationResult` whose `mutate` / `mutateAsync`\n * accepts a partial row to insert.\n *\n * @example\n * ```tsx\n * const insert = useInsert<Todo>('todos')\n * insert.mutate({ task: 'Buy milk', done: false })\n * ```\n */\nexport function useInsert<T = Record<string, unknown>>(\n table: string,\n): UseMutationResult<T, MimDBError, Partial<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (data: Partial<T>) => {\n const { data: result, error } = await client\n .from<T>(table)\n .insert(data)\n .select()\n .single()\n if (error) throw error\n return result as T\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * Input shape for the {@link useUpdate} mutation.\n *\n * @typeParam T - Expected row type.\n */\nexport interface UpdateInput<T> {\n /** Fields to update. */\n data: Partial<T>\n /** Equality filters identifying which rows to update. */\n eq: Record<string, string>\n}\n\n/**\n * React hook for updating rows in a MimDB table.\n *\n * The mutation function accepts an object with `data` (fields to set) and\n * `eq` (equality filters to target specific rows). On success, all\n * `useQuery` caches for the same table are invalidated.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @returns A TanStack `UseMutationResult`.\n *\n * @example\n * ```tsx\n * const update = useUpdate<Todo>('todos')\n * update.mutate({ data: { done: true }, eq: { id: '42' } })\n * ```\n */\nexport function useUpdate<T = Record<string, unknown>>(\n table: string,\n): UseMutationResult<T, MimDBError, UpdateInput<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async ({ data, eq }: UpdateInput<T>) => {\n let query = client.from<T>(table).update(data)\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { data: result, error } = await query.select().single()\n if (error) throw error\n return result as T\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * React hook for deleting rows from a MimDB table.\n *\n * The mutation function accepts equality filters identifying which rows\n * to delete. On success, all `useQuery` caches for the same table are\n * invalidated.\n *\n * @param table - Name of the database table.\n * @returns A TanStack `UseMutationResult` whose `mutate` accepts equality filters.\n *\n * @example\n * ```tsx\n * const del = useDelete('todos')\n * del.mutate({ id: '42' })\n * ```\n */\nexport function useDelete(\n table: string,\n): UseMutationResult<void, MimDBError, Record<string, string>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (eq: Record<string, string>) => {\n let query = client.from(table).delete()\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { error } = await query\n if (error) throw error\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport {\n MimDBRealtimeClient,\n type RealtimeEvent,\n type SubscriptionStatus,\n} from '@mimdb/realtime'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useRealtime} hook.\n *\n * @typeParam T - Expected row type for realtime events.\n */\nexport interface UseRealtimeOptions<T = Record<string, unknown>> {\n /** Event type filter. Defaults to `'*'` (all events). */\n event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE'\n /** Row filter expression (e.g. `'user_id=eq.42'`). */\n filter?: string\n /** Called for each matching realtime event. */\n onEvent?: (event: RealtimeEvent<T>) => void\n /**\n * Whether to automatically invalidate the table's TanStack Query cache\n * when an event is received. Defaults to `true`.\n */\n invalidateQueries?: boolean\n}\n\n/**\n * Subscribe to realtime database changes for a table via WebSocket.\n *\n * Creates a `MimDBRealtimeClient` using the connection config from\n * `MimDBClient.getConfig()` and subscribes to the specified table.\n * On each event, the table's TanStack Query cache is invalidated\n * (unless opted out) so queries refetch automatically.\n *\n * The subscription is cleaned up when the component unmounts or when\n * the `table`, `event`, or `filter` options change.\n *\n * @typeParam T - Expected row type for realtime events.\n * @param table - Database table to subscribe to.\n * @param options - Event filters and callbacks.\n * @returns An object containing the current subscription status.\n *\n * @example\n * ```tsx\n * const { status } = useRealtime<Message>('messages', {\n * event: 'INSERT',\n * onEvent: (e) => console.log('New message:', e.new),\n * })\n * ```\n */\nexport function useRealtime<T = Record<string, unknown>>(\n table: string,\n options?: UseRealtimeOptions<T>,\n): { status: SubscriptionStatus } {\n const client = useClient()\n const queryClient = useQueryClient()\n const [status, setStatus] = useState<SubscriptionStatus>('pending')\n const realtimeRef = useRef<MimDBRealtimeClient | null>(null)\n\n useEffect(() => {\n const config = client.getConfig()\n\n if (!realtimeRef.current) {\n realtimeRef.current = new MimDBRealtimeClient({\n url: config.url,\n projectRef: config.ref,\n apiKey: config.apiKey,\n })\n }\n\n const sub = realtimeRef.current.subscribe<T>(table, {\n event: options?.event ?? '*',\n filter: options?.filter,\n onEvent(event) {\n options?.onEvent?.(event)\n if (options?.invalidateQueries !== false) {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n }\n },\n onSubscribed() {\n setStatus('active')\n },\n onError() {\n setStatus('error')\n },\n })\n\n setStatus('pending')\n\n return () => {\n sub.unsubscribe()\n setStatus('closed')\n }\n }, [table, options?.event, options?.filter]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return { status }\n}\n","import { useState, useEffect, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { User } from '@mimdb/client'\n\n/**\n * Return type of the {@link useAuth} hook.\n */\nexport interface UseAuthResult {\n /** The currently authenticated user, or null if signed out. */\n user: User | null\n /** True while the initial session check is in progress. */\n isLoading: boolean\n /** Sign in with email and password. */\n signIn: (email: string, password: string) => Promise<void>\n /** Create a new account with email and password. */\n signUp: (email: string, password: string) => Promise<void>\n /** Sign out the current user. */\n signOut: () => Promise<void>\n /** Redirect to an OAuth provider's authorization page. */\n signInWithOAuth: (provider: string, opts: { redirectTo: string }) => void\n}\n\n/**\n * React hook for authentication state management.\n *\n * On mount, checks for an existing session and fetches the current user.\n * Subscribes to auth state changes so the returned `user` stays in sync\n * with sign-in, sign-out, and token refresh events.\n *\n * @returns An object with the current user, loading state, and auth methods.\n *\n * @example\n * ```tsx\n * function LoginPage() {\n * const { user, isLoading, signIn, signOut } = useAuth()\n *\n * if (isLoading) return <p>Loading...</p>\n * if (user) return <button onClick={signOut}>Sign Out</button>\n *\n * return (\n * <button onClick={() => signIn('user@example.com', 'password')}>\n * Sign In\n * </button>\n * )\n * }\n * ```\n */\nexport function useAuth(): UseAuthResult {\n const client = useClient()\n const [user, setUser] = useState<User | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n useEffect(() => {\n const session = client.auth.getSession()\n if (session) {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n .finally(() => setIsLoading(false))\n } else {\n setIsLoading(false)\n }\n\n const unsub = client.auth.onAuthStateChange((event, _session) => {\n if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n } else if (event === 'SIGNED_OUT') {\n setUser(null)\n }\n })\n\n return unsub\n }, [client])\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n await client.auth.signIn(email, password)\n },\n [client],\n )\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n await client.auth.signUp(email, password)\n },\n [client],\n )\n\n const signOut = useCallback(async () => {\n await client.auth.signOut()\n }, [client])\n\n const signInWithOAuth = useCallback(\n (provider: string, opts: { redirectTo: string }) => {\n const url = client.auth.signInWithOAuth(provider, opts)\n window.location.href = url\n },\n [client],\n )\n\n return { user, isLoading, signIn, signUp, signOut, signInWithOAuth }\n}\n","import { useState, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { UploadOptions } from '@mimdb/client'\n\n/**\n * Return type of the {@link useUpload} hook.\n */\nexport interface UseUploadResult {\n /** Upload a file to the bucket. Re-throws on failure after setting `error`. */\n upload: (path: string, file: Blob | File, opts?: UploadOptions) => Promise<void>\n /** True while an upload is in progress. */\n isUploading: boolean\n /** The error from the most recent failed upload, or null. */\n error: Error | null\n}\n\n/**\n * React hook for uploading files to a MimDB storage bucket.\n *\n * Tracks the upload's loading and error state so components can show\n * progress indicators or error messages without manual state management.\n *\n * @param bucket - Name of the storage bucket to upload to.\n * @returns An object with the `upload` function and status flags.\n *\n * @example\n * ```tsx\n * function AvatarUpload() {\n * const { upload, isUploading, error } = useUpload('avatars')\n *\n * const handleFile = (file: File) => {\n * upload(`users/${userId}/avatar.png`, file, { contentType: 'image/png' })\n * }\n *\n * return (\n * <>\n * <input type=\"file\" onChange={(e) => handleFile(e.target.files![0])} />\n * {isUploading && <p>Uploading...</p>}\n * {error && <p>Error: {error.message}</p>}\n * </>\n * )\n * }\n * ```\n */\nexport function useUpload(bucket: string): UseUploadResult {\n const client = useClient()\n const [isUploading, setIsUploading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const upload = useCallback(\n async (path: string, file: Blob | File, opts?: UploadOptions) => {\n setIsUploading(true)\n setError(null)\n try {\n await client.storage.from(bucket).upload(path, file, opts)\n } catch (err) {\n const uploadError =\n err instanceof Error ? err : new Error(String(err))\n setError(uploadError)\n throw uploadError\n } finally {\n setIsUploading(false)\n }\n },\n [client, bucket],\n )\n\n return { upload, isUploading, error }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA0C;AAW1C,IAAM,mBAAe,4BAAkC,IAAI;AAgBpD,SAAS,YAAyB;AACvC,QAAM,aAAS,yBAAW,YAAY;AACtC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AACA,SAAO;AACT;;;ACOS;AADF,SAAS,cAAc,EAAE,QAAQ,SAAS,GAAuB;AACtE,SAAO,4CAAC,aAAa,UAAb,EAAsB,OAAO,QAAS,UAAS;AACzD;;;ACzCA,yBAGO;AAgDA,SAAS,SACd,OACA,SAC4B;AAC5B,QAAM,SAAS,UAAU;AAEzB,aAAO,mBAAAA,UAAiB;AAAA,IACtB,UAAU,CAAC,SAAS,OAAO,OAAO;AAAA,IAClC,SAAS,YAAY;AACnB,UAAI,QAAQ,OAAO,KAAQ,KAAK,EAAE,OAAO,SAAS,UAAU,GAAG;AAE/D,UAAI,SAAS,IAAI;AACf,mBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,QAAQ,EAAE,GAAG;AACnD,kBAAQ,MAAM,GAAG,KAAK,GAAG;AAAA,QAC3B;AAAA,MACF;AACA,UAAI,SAAS,KAAK;AAChB,mBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,QAAQ,GAAG,GAAG;AACpD,kBAAQ,MAAM,IAAI,KAAK,GAAG;AAAA,QAC5B;AAAA,MACF;AACA,UAAI,SAAS,OAAO;AAClB,gBAAQ,MAAM,MAAM,QAAQ,MAAM,QAAQ;AAAA,UACxC,WAAW,QAAQ,MAAM;AAAA,QAC3B,CAAC;AAAA,MACH;AACA,UAAI,SAAS,MAAO,SAAQ,MAAM,MAAM,QAAQ,KAAK;AACrD,UAAI,SAAS,OAAQ,SAAQ,MAAM,OAAO,QAAQ,MAAM;AAExD,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM;AAC9B,UAAI,MAAO,OAAM;AACjB,aAAQ,QAAQ,CAAC;AAAA,IACnB;AAAA,IACA,SAAS,SAAS;AAAA,IAClB,WAAW,SAAS;AAAA,IACpB,iBAAiB,SAAS;AAAA,EAC5B,CAAC;AACH;;;ACxFA,IAAAC,sBAIO;AAqBA,SAAS,UACd,OAC8C;AAC9C,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AAEnC,aAAO,oBAAAC,aAAoB;AAAA,IACzB,YAAY,OAAO,SAAqB;AACtC,YAAM,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,OACnC,KAAQ,KAAK,EACb,OAAO,IAAI,EACX,OAAO,EACP,OAAO;AACV,UAAI,MAAO,OAAM;AACjB,aAAO;AAAA,IACT;AAAA,IACA,WAAW,MAAM;AACf,kBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF,CAAC;AACH;AA+BO,SAAS,UACd,OACkD;AAClD,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AAEnC,aAAO,oBAAAA,aAAoB;AAAA,IACzB,YAAY,OAAO,EAAE,MAAM,GAAG,MAAsB;AAClD,UAAI,QAAQ,OAAO,KAAQ,KAAK,EAAE,OAAO,IAAI;AAC7C,iBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,EAAE,GAAG;AAC3C,gBAAQ,MAAM,GAAG,KAAK,GAAG;AAAA,MAC3B;AACA,YAAM,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,MAAM,OAAO,EAAE,OAAO;AAC5D,UAAI,MAAO,OAAM;AACjB,aAAO;AAAA,IACT;AAAA,IACA,WAAW,MAAM;AACf,kBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF,CAAC;AACH;AAkBO,SAAS,UACd,OAC6D;AAC7D,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AAEnC,aAAO,oBAAAA,aAAoB;AAAA,IACzB,YAAY,OAAO,OAA+B;AAChD,UAAI,QAAQ,OAAO,KAAK,KAAK,EAAE,OAAO;AACtC,iBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,EAAE,GAAG;AAC3C,gBAAQ,MAAM,GAAG,KAAK,GAAG;AAAA,MAC3B;AACA,YAAM,EAAE,MAAM,IAAI,MAAM;AACxB,UAAI,MAAO,OAAM;AAAA,IACnB;AAAA,IACA,WAAW,MAAM;AACf,kBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF,CAAC;AACH;;;ACrIA,IAAAC,gBAA4C;AAC5C,IAAAC,sBAA+B;AAC/B,sBAIO;AA8CA,SAAS,YACd,OACA,SACgC;AAChC,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AACnC,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAA6B,SAAS;AAClE,QAAM,kBAAc,sBAAmC,IAAI;AAE3D,+BAAU,MAAM;AACd,UAAM,SAAS,OAAO,UAAU;AAEhC,QAAI,CAAC,YAAY,SAAS;AACxB,kBAAY,UAAU,IAAI,oCAAoB;AAAA,QAC5C,KAAK,OAAO;AAAA,QACZ,YAAY,OAAO;AAAA,QACnB,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,UAAM,MAAM,YAAY,QAAQ,UAAa,OAAO;AAAA,MAClD,OAAO,SAAS,SAAS;AAAA,MACzB,QAAQ,SAAS;AAAA,MACjB,QAAQ,OAAO;AACb,iBAAS,UAAU,KAAK;AACxB,YAAI,SAAS,sBAAsB,OAAO;AACxC,sBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,QAC9D;AAAA,MACF;AAAA,MACA,eAAe;AACb,kBAAU,QAAQ;AAAA,MACpB;AAAA,MACA,UAAU;AACR,kBAAU,OAAO;AAAA,MACnB;AAAA,IACF,CAAC;AAED,cAAU,SAAS;AAEnB,WAAO,MAAM;AACX,UAAI,YAAY;AAChB,gBAAU,QAAQ;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,OAAO,SAAS,MAAM,CAAC;AAE3C,SAAO,EAAE,OAAO;AAClB;;;AClGA,IAAAC,gBAAiD;AA+C1C,SAAS,UAAyB;AACvC,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAsB,IAAI;AAClD,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,IAAI;AAE/C,+BAAU,MAAM;AACd,UAAM,UAAU,OAAO,KAAK,WAAW;AACvC,QAAI,SAAS;AACX,aAAO,KACJ,QAAQ,EACR,KAAK,OAAO,EACZ,MAAM,MAAM,QAAQ,IAAI,CAAC,EACzB,QAAQ,MAAM,aAAa,KAAK,CAAC;AAAA,IACtC,OAAO;AACL,mBAAa,KAAK;AAAA,IACpB;AAEA,UAAM,QAAQ,OAAO,KAAK,kBAAkB,CAAC,OAAO,aAAa;AAC/D,UAAI,UAAU,eAAe,UAAU,mBAAmB;AACxD,eAAO,KACJ,QAAQ,EACR,KAAK,OAAO,EACZ,MAAM,MAAM,QAAQ,IAAI,CAAC;AAAA,MAC9B,WAAW,UAAU,cAAc;AACjC,gBAAQ,IAAI;AAAA,MACd;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,aAAS;AAAA,IACb,OAAO,OAAe,aAAqB;AACzC,YAAM,OAAO,KAAK,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,aAAS;AAAA,IACb,OAAO,OAAe,aAAqB;AACzC,YAAM,OAAO,KAAK,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,cAAU,2BAAY,YAAY;AACtC,UAAM,OAAO,KAAK,QAAQ;AAAA,EAC5B,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,sBAAkB;AAAA,IACtB,CAAC,UAAkB,SAAiC;AAClD,YAAM,MAAM,OAAO,KAAK,gBAAgB,UAAU,IAAI;AACtD,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SAAO,EAAE,MAAM,WAAW,QAAQ,QAAQ,SAAS,gBAAgB;AACrE;;;ACzGA,IAAAC,gBAAsC;AA4C/B,SAAS,UAAU,QAAiC;AACzD,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAErD,QAAM,aAAS;AAAA,IACb,OAAO,MAAc,MAAmB,SAAyB;AAC/D,qBAAe,IAAI;AACnB,eAAS,IAAI;AACb,UAAI;AACF,cAAM,OAAO,QAAQ,KAAK,MAAM,EAAE,OAAO,MAAM,MAAM,IAAI;AAAA,MAC3D,SAAS,KAAK;AACZ,cAAM,cACJ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AACpD,iBAAS,WAAW;AACpB,cAAM;AAAA,MACR,UAAE;AACA,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,MAAM;AAAA,EACjB;AAEA,SAAO,EAAE,QAAQ,aAAa,MAAM;AACtC;","names":["useTanstackQuery","import_react_query","useTanstackMutation","import_react","import_react_query","import_react","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/provider.tsx","../src/use-query.ts","../src/use-mutation.ts","../src/use-realtime.ts","../src/use-subscription.ts","../src/use-auth.ts","../src/use-upload.ts"],"sourcesContent":["export { MimDBProvider, type MimDBProviderProps } from './provider'\nexport { useClient } from './context'\nexport { useQuery, type UseQueryOptions } from './use-query'\nexport {\n useInsert,\n useUpdate,\n useDelete,\n type UpdateInput,\n type UseInsertOptions,\n type UseUpdateOptions,\n type UseDeleteOptions,\n} from './use-mutation'\nexport { useRealtime, type UseRealtimeOptions } from './use-realtime'\nexport { useSubscription, type UseSubscriptionResult } from './use-subscription'\nexport { useAuth, type UseAuthResult } from './use-auth'\nexport { useUpload, type UseUploadResult } from './use-upload'\n\n// Re-export createServerClient for convenience\nexport { createServerClient } from '@mimdb/client'\n","import { createContext, useContext } from 'react'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * React context that holds the MimDB client instance.\n *\n * Consumers should use the {@link useClient} hook rather than accessing\n * this context directly.\n *\n * @internal\n */\nconst MimDBContext = createContext<MimDBClient | null>(null)\n\n/**\n * Retrieve the MimDB client from the nearest `<MimDBProvider>`.\n *\n * @returns The `MimDBClient` instance provided by the enclosing provider.\n * @throws If called outside of a `<MimDBProvider>` tree.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useClient()\n * // Use client.from(), client.auth, etc.\n * }\n * ```\n */\nexport function useClient(): MimDBClient {\n const client = useContext(MimDBContext)\n if (!client) {\n throw new Error('useClient must be used within <MimDBProvider>')\n }\n return client\n}\n\nexport { MimDBContext }\n","import { type ReactNode } from 'react'\nimport { MimDBContext } from './context'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * Props for the {@link MimDBProvider} component.\n */\nexport interface MimDBProviderProps {\n /** A configured `MimDBClient` instance to make available to child components. */\n client: MimDBClient\n /** The React subtree that will have access to the MimDB client. */\n children: ReactNode\n}\n\n/**\n * Context provider that makes a `MimDBClient` available to all descendant\n * components via the {@link useClient} hook.\n *\n * Wrap your application (or a subtree) with this provider and pass a\n * pre-configured client instance.\n *\n * @param props - Provider props containing the client and children.\n *\n * @example\n * ```tsx\n * import { createClient } from '@mimdb/client'\n * import { MimDBProvider } from '@mimdb/react'\n *\n * const client = createClient('https://api.mimdb.dev', 'ref', 'key')\n *\n * function App() {\n * return (\n * <MimDBProvider client={client}>\n * <MyApp />\n * </MimDBProvider>\n * )\n * }\n * ```\n */\nexport function MimDBProvider({ client, children }: MimDBProviderProps) {\n return <MimDBContext.Provider value={client}>{children}</MimDBContext.Provider>\n}\n","import {\n useQuery as useTanstackQuery,\n type UseQueryResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useQuery} hook.\n */\nexport interface UseQueryOptions {\n /** Column selection string (PostgREST format). Defaults to `'*'`. */\n select?: string\n /** Equality filters applied as `query.eq(column, value)`. */\n eq?: Record<string, string>\n /** Not-equal filters applied as `query.neq(column, value)`. */\n neq?: Record<string, string>\n /** Column ordering configuration. */\n order?: { column: string; ascending?: boolean }\n /** Maximum number of rows to return. */\n limit?: number\n /** Number of rows to skip before returning results. */\n offset?: number\n /** Whether the query should execute. Maps to TanStack Query's `enabled`. */\n enabled?: boolean\n /** Duration in ms before cached data is considered stale. */\n staleTime?: number\n /** Polling interval in ms, or `false` to disable. */\n refetchInterval?: number | false\n}\n\n/**\n * Fetch rows from a MimDB table using the REST API, backed by TanStack Query\n * for caching, deduplication, and background refetching.\n *\n * Automatically derives a stable query key from the table name and options\n * so cache invalidation works out of the box with the mutation hooks.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table to query.\n * @param options - Query filters, modifiers, and TanStack Query settings.\n * @returns A TanStack `UseQueryResult` containing the row array and status flags.\n *\n * @example\n * ```tsx\n * const { data, isLoading } = useQuery<Todo>('todos', {\n * eq: { done: 'false' },\n * order: { column: 'created_at', ascending: false },\n * limit: 20,\n * })\n * ```\n */\nexport function useQuery<T = Record<string, unknown>>(\n table: string,\n options?: UseQueryOptions,\n): UseQueryResult<T[], Error> {\n const client = useClient()\n\n return useTanstackQuery({\n queryKey: ['mimdb', table, options],\n queryFn: async () => {\n let query = client.from<T>(table).select(options?.select ?? '*')\n\n if (options?.eq) {\n for (const [col, val] of Object.entries(options.eq)) {\n query = query.eq(col, val)\n }\n }\n if (options?.neq) {\n for (const [col, val] of Object.entries(options.neq)) {\n query = query.neq(col, val)\n }\n }\n if (options?.order) {\n query = query.order(options.order.column, {\n ascending: options.order.ascending,\n })\n }\n if (options?.limit) query = query.limit(options.limit)\n if (options?.offset) query = query.offset(options.offset)\n\n const { data, error } = await query\n if (error) throw error\n return (data ?? []) as T[]\n },\n enabled: options?.enabled,\n staleTime: options?.staleTime,\n refetchInterval: options?.refetchInterval,\n })\n}\n","import {\n useMutation as useTanstackMutation,\n useQueryClient,\n type UseMutationResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\nimport type { MimDBError } from '@mimdb/client'\n\n/**\n * Options for the {@link useInsert} hook.\n */\nexport interface UseInsertOptions {\n /**\n * Enable optimistic updates. When true, the new row is appended to\n * the query cache immediately and rolled back on error.\n */\n optimistic?: boolean\n}\n\n/**\n * React hook for inserting a row into a MimDB table.\n *\n * On success, all `useQuery` caches for the same table are automatically\n * invalidated so lists stay in sync.\n *\n * When `optimistic` is enabled, the new row is appended to the cache\n * before the server responds. On error the cache is rolled back.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @param options - Insert hook options.\n * @returns A TanStack `UseMutationResult` whose `mutate` / `mutateAsync`\n * accepts a partial row to insert.\n *\n * @example\n * ```tsx\n * const insert = useInsert<Todo>('todos', { optimistic: true })\n * insert.mutate({ task: 'Buy milk', done: false })\n * ```\n */\nexport function useInsert<T = Record<string, unknown>>(\n table: string,\n options?: UseInsertOptions,\n): UseMutationResult<T, MimDBError, Partial<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (data: Partial<T>) => {\n const { data: result, error } = await client\n .from<T>(table)\n .insert(data)\n .select()\n .single()\n if (error) throw error\n return result as T\n },\n onMutate: options?.optimistic\n ? async (newData) => {\n await queryClient.cancelQueries({ queryKey: ['mimdb', table] })\n const previous = queryClient.getQueryData<T[]>(['mimdb', table])\n queryClient.setQueryData<T[]>(['mimdb', table], (old) => [\n ...(old ?? []),\n newData as T,\n ])\n return { previous }\n }\n : undefined,\n onError: options?.optimistic\n ? (_err, _data, context: { previous?: T[] } | undefined) => {\n if (context?.previous !== undefined) {\n queryClient.setQueryData(['mimdb', table], context.previous)\n }\n }\n : undefined,\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * Input shape for the {@link useUpdate} mutation.\n *\n * @typeParam T - Expected row type.\n */\nexport interface UpdateInput<T> {\n /** Fields to update. */\n data: Partial<T>\n /** Equality filters identifying which rows to update. */\n eq: Record<string, string>\n}\n\n/**\n * Options for the {@link useUpdate} hook.\n */\nexport interface UseUpdateOptions {\n /**\n * Enable optimistic updates. When true, matching rows in the cache\n * are updated immediately and rolled back on error.\n */\n optimistic?: boolean\n}\n\n/**\n * React hook for updating rows in a MimDB table.\n *\n * The mutation function accepts an object with `data` (fields to set) and\n * `eq` (equality filters to target specific rows). On success, all\n * `useQuery` caches for the same table are invalidated.\n *\n * When `optimistic` is enabled, matching rows in the cache are updated\n * in-place before the server responds and rolled back on error.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @param options - Update hook options.\n * @returns A TanStack `UseMutationResult`.\n *\n * @example\n * ```tsx\n * const update = useUpdate<Todo>('todos', { optimistic: true })\n * update.mutate({ data: { done: true }, eq: { id: '42' } })\n * ```\n */\nexport function useUpdate<T = Record<string, unknown>>(\n table: string,\n options?: UseUpdateOptions,\n): UseMutationResult<T, MimDBError, UpdateInput<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async ({ data, eq }: UpdateInput<T>) => {\n let query = client.from<T>(table).update(data)\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { data: result, error } = await query.select().single()\n if (error) throw error\n return result as T\n },\n onMutate: options?.optimistic\n ? async ({ data, eq }) => {\n await queryClient.cancelQueries({ queryKey: ['mimdb', table] })\n const previous = queryClient.getQueryData<T[]>(['mimdb', table])\n queryClient.setQueryData<T[]>(['mimdb', table], (old) =>\n (old ?? []).map((row) => {\n const record = row as Record<string, unknown>\n const matches = Object.entries(eq).every(\n ([col, val]) => String(record[col]) === val,\n )\n return matches ? { ...row, ...data } : row\n }),\n )\n return { previous }\n }\n : undefined,\n onError: options?.optimistic\n ? (_err, _data, context: { previous?: T[] } | undefined) => {\n if (context?.previous !== undefined) {\n queryClient.setQueryData(['mimdb', table], context.previous)\n }\n }\n : undefined,\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * Options for the {@link useDelete} hook.\n */\nexport interface UseDeleteOptions {\n /**\n * Enable optimistic updates. When true, matching rows are removed\n * from the cache immediately and restored on error.\n */\n optimistic?: boolean\n}\n\n/**\n * React hook for deleting rows from a MimDB table.\n *\n * The mutation function accepts equality filters identifying which rows\n * to delete. On success, all `useQuery` caches for the same table are\n * invalidated.\n *\n * When `optimistic` is enabled, matching rows are removed from the cache\n * before the server responds and restored on error.\n *\n * @param table - Name of the database table.\n * @param options - Delete hook options.\n * @returns A TanStack `UseMutationResult` whose `mutate` accepts equality filters.\n *\n * @example\n * ```tsx\n * const del = useDelete('todos', { optimistic: true })\n * del.mutate({ id: '42' })\n * ```\n */\nexport function useDelete(\n table: string,\n options?: UseDeleteOptions,\n): UseMutationResult<void, MimDBError, Record<string, string>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (eq: Record<string, string>) => {\n let query = client.from(table).delete()\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { error } = await query\n if (error) throw error\n },\n onMutate: options?.optimistic\n ? async (eq) => {\n await queryClient.cancelQueries({ queryKey: ['mimdb', table] })\n const previous = queryClient.getQueryData<Record<string, unknown>[]>(['mimdb', table])\n queryClient.setQueryData<Record<string, unknown>[]>(['mimdb', table], (old) =>\n (old ?? []).filter((row) =>\n !Object.entries(eq).every(\n ([col, val]) => String(row[col]) === val,\n ),\n ),\n )\n return { previous }\n }\n : undefined,\n onError: options?.optimistic\n ? (_err, _data, context: { previous?: Record<string, unknown>[] } | undefined) => {\n if (context?.previous !== undefined) {\n queryClient.setQueryData(['mimdb', table], context.previous)\n }\n }\n : undefined,\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport {\n MimDBRealtimeClient,\n type RealtimeEvent,\n type SubscriptionStatus,\n} from '@mimdb/realtime'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useRealtime} hook.\n *\n * @typeParam T - Expected row type for realtime events.\n */\nexport interface UseRealtimeOptions<T = Record<string, unknown>> {\n /** Event type filter. Defaults to `'*'` (all events). */\n event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE'\n /** Row filter expression (e.g. `'user_id=eq.42'`). */\n filter?: string\n /** Called for each matching realtime event. */\n onEvent?: (event: RealtimeEvent<T>) => void\n /**\n * Whether to automatically invalidate the table's TanStack Query cache\n * when an event is received. Defaults to `true`.\n */\n invalidateQueries?: boolean\n}\n\n/**\n * Subscribe to realtime database changes for a table via WebSocket.\n *\n * Creates a `MimDBRealtimeClient` using the connection config from\n * `MimDBClient.getConfig()` and subscribes to the specified table.\n * On each event, the table's TanStack Query cache is invalidated\n * (unless opted out) so queries refetch automatically.\n *\n * The subscription is cleaned up when the component unmounts or when\n * the `table`, `event`, or `filter` options change.\n *\n * @typeParam T - Expected row type for realtime events.\n * @param table - Database table to subscribe to.\n * @param options - Event filters and callbacks.\n * @returns An object containing the current subscription status.\n *\n * @example\n * ```tsx\n * const { status } = useRealtime<Message>('messages', {\n * event: 'INSERT',\n * onEvent: (e) => console.log('New message:', e.new),\n * })\n * ```\n */\nexport function useRealtime<T = Record<string, unknown>>(\n table: string,\n options?: UseRealtimeOptions<T>,\n): { status: SubscriptionStatus } {\n const client = useClient()\n const queryClient = useQueryClient()\n const [status, setStatus] = useState<SubscriptionStatus>('pending')\n const realtimeRef = useRef<MimDBRealtimeClient | null>(null)\n\n useEffect(() => {\n // Skip WebSocket connections during SSR\n if (typeof window === 'undefined') return\n\n const config = client.getConfig()\n\n if (!realtimeRef.current) {\n realtimeRef.current = new MimDBRealtimeClient({\n url: config.url,\n projectRef: config.ref,\n apiKey: config.apiKey,\n })\n }\n\n const sub = realtimeRef.current.subscribe<T>(table, {\n event: options?.event ?? '*',\n filter: options?.filter,\n onEvent(event) {\n options?.onEvent?.(event)\n if (options?.invalidateQueries !== false) {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n }\n },\n onSubscribed() {\n setStatus('active')\n },\n onError() {\n setStatus('error')\n },\n })\n\n setStatus('pending')\n\n return () => {\n sub.unsubscribe()\n setStatus('closed')\n }\n }, [table, options?.event, options?.filter]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return { status }\n}\n","import { useState, useEffect } from 'react'\nimport type { RealtimeEvent, SubscriptionStatus } from '@mimdb/realtime'\nimport { useClient } from './context'\n\n/**\n * Return type of the {@link useSubscription} hook.\n *\n * @typeParam T - Expected row type for realtime events.\n */\nexport interface UseSubscriptionResult<T> {\n /** The most recently received realtime event, or null if none yet. */\n lastEvent: RealtimeEvent<T> | null\n /** Current subscription lifecycle status. */\n status: SubscriptionStatus\n}\n\n/**\n * Subscribe to a table and maintain the latest event in React state.\n *\n * Unlike {@link useRealtime} (which fires callbacks), this hook returns\n * the most recent event directly as component state, making it ideal\n * for rendering the latest change inline.\n *\n * Uses `client.realtime` (the lazy realtime accessor on MimDBClient)\n * so no separate realtime client setup is needed.\n *\n * The subscription is cleaned up when the component unmounts or when\n * `table`, `event`, or `filter` change.\n *\n * @typeParam T - Expected row type for realtime events.\n * @param table - Database table to subscribe to.\n * @param options - Event type and filter configuration.\n * @returns The latest event and subscription status.\n *\n * @example\n * ```tsx\n * const { lastEvent, status } = useSubscription<Message>('messages', {\n * event: 'INSERT',\n * })\n *\n * if (lastEvent) {\n * console.log('Latest insert:', lastEvent.new)\n * }\n * ```\n */\nexport function useSubscription<T = Record<string, unknown>>(\n table: string,\n options?: {\n /** Event type filter. Defaults to `'*'` (all events). */\n event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE'\n /** Row filter expression (e.g. `'user_id=eq.42'`). */\n filter?: string\n },\n): UseSubscriptionResult<T> {\n const client = useClient()\n const [lastEvent, setLastEvent] = useState<RealtimeEvent<T> | null>(null)\n const [status, setStatus] = useState<SubscriptionStatus>('pending')\n\n useEffect(() => {\n // Skip WebSocket connections during SSR\n if (typeof window === 'undefined') return\n\n const sub = client.realtime.subscribe<T>(table, {\n event: options?.event ?? '*',\n filter: options?.filter,\n onEvent(event) {\n setLastEvent(event)\n },\n onSubscribed() {\n setStatus('active')\n },\n onError() {\n setStatus('error')\n },\n })\n\n setStatus('pending')\n\n return () => {\n sub.unsubscribe()\n setStatus('closed')\n }\n }, [table, options?.event, options?.filter]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return { lastEvent, status }\n}\n","import { useState, useEffect, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { User } from '@mimdb/client'\n\n/**\n * Return type of the {@link useAuth} hook.\n */\nexport interface UseAuthResult {\n /** The currently authenticated user, or null if signed out. */\n user: User | null\n /** True while the initial session check is in progress. */\n isLoading: boolean\n /** Sign in with email and password. */\n signIn: (email: string, password: string) => Promise<void>\n /** Create a new account with email and password. */\n signUp: (email: string, password: string) => Promise<void>\n /** Sign out the current user. */\n signOut: () => Promise<void>\n /** Redirect to an OAuth provider's authorization page. */\n signInWithOAuth: (provider: string, opts: { redirectTo: string }) => void\n}\n\n/**\n * React hook for authentication state management.\n *\n * On mount, checks for an existing session and fetches the current user.\n * Subscribes to auth state changes so the returned `user` stays in sync\n * with sign-in, sign-out, and token refresh events.\n *\n * @returns An object with the current user, loading state, and auth methods.\n *\n * @example\n * ```tsx\n * function LoginPage() {\n * const { user, isLoading, signIn, signOut } = useAuth()\n *\n * if (isLoading) return <p>Loading...</p>\n * if (user) return <button onClick={signOut}>Sign Out</button>\n *\n * return (\n * <button onClick={() => signIn('user@example.com', 'password')}>\n * Sign In\n * </button>\n * )\n * }\n * ```\n */\nexport function useAuth(): UseAuthResult {\n const client = useClient()\n const [user, setUser] = useState<User | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n useEffect(() => {\n const session = client.auth.getSession()\n if (session) {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n .finally(() => setIsLoading(false))\n } else {\n setIsLoading(false)\n }\n\n const unsub = client.auth.onAuthStateChange((event, _session) => {\n if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n } else if (event === 'SIGNED_OUT' || event === 'TOKEN_REFRESH_FAILED') {\n setUser(null)\n }\n })\n\n return unsub\n }, [client])\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n await client.auth.signIn(email, password)\n },\n [client],\n )\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n await client.auth.signUp(email, password)\n },\n [client],\n )\n\n const signOut = useCallback(async () => {\n await client.auth.signOut()\n }, [client])\n\n const signInWithOAuth = useCallback(\n (provider: string, opts: { redirectTo: string }) => {\n const url = client.auth.signInWithOAuth(provider, opts)\n if (typeof window !== 'undefined') {\n window.location.href = url\n }\n },\n [client],\n )\n\n return { user, isLoading, signIn, signUp, signOut, signInWithOAuth }\n}\n","import { useState, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { UploadOptions } from '@mimdb/client'\n\n/**\n * Return type of the {@link useUpload} hook.\n */\nexport interface UseUploadResult {\n /** Upload a file to the bucket. Re-throws on failure after setting `error`. */\n upload: (path: string, file: Blob | File, opts?: UploadOptions) => Promise<void>\n /** True while an upload is in progress. */\n isUploading: boolean\n /** The error from the most recent failed upload, or null. */\n error: Error | null\n}\n\n/**\n * React hook for uploading files to a MimDB storage bucket.\n *\n * Tracks the upload's loading and error state so components can show\n * progress indicators or error messages without manual state management.\n *\n * @param bucket - Name of the storage bucket to upload to.\n * @returns An object with the `upload` function and status flags.\n *\n * @example\n * ```tsx\n * function AvatarUpload() {\n * const { upload, isUploading, error } = useUpload('avatars')\n *\n * const handleFile = (file: File) => {\n * upload(`users/${userId}/avatar.png`, file, { contentType: 'image/png' })\n * }\n *\n * return (\n * <>\n * <input type=\"file\" onChange={(e) => handleFile(e.target.files![0])} />\n * {isUploading && <p>Uploading...</p>}\n * {error && <p>Error: {error.message}</p>}\n * </>\n * )\n * }\n * ```\n */\nexport function useUpload(bucket: string): UseUploadResult {\n const client = useClient()\n const [isUploading, setIsUploading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const upload = useCallback(\n async (path: string, file: Blob | File, opts?: UploadOptions) => {\n setIsUploading(true)\n setError(null)\n try {\n await client.storage.from(bucket).upload(path, file, opts)\n } catch (err) {\n const uploadError =\n err instanceof Error ? err : new Error(String(err))\n setError(uploadError)\n throw uploadError\n } finally {\n setIsUploading(false)\n }\n },\n [client, bucket],\n )\n\n return { upload, isUploading, error }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA0C;AAW1C,IAAM,mBAAe,4BAAkC,IAAI;AAgBpD,SAAS,YAAyB;AACvC,QAAM,aAAS,yBAAW,YAAY;AACtC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AACA,SAAO;AACT;;;ACOS;AADF,SAAS,cAAc,EAAE,QAAQ,SAAS,GAAuB;AACtE,SAAO,4CAAC,aAAa,UAAb,EAAsB,OAAO,QAAS,UAAS;AACzD;;;ACzCA,yBAGO;AAgDA,SAAS,SACd,OACA,SAC4B;AAC5B,QAAM,SAAS,UAAU;AAEzB,aAAO,mBAAAA,UAAiB;AAAA,IACtB,UAAU,CAAC,SAAS,OAAO,OAAO;AAAA,IAClC,SAAS,YAAY;AACnB,UAAI,QAAQ,OAAO,KAAQ,KAAK,EAAE,OAAO,SAAS,UAAU,GAAG;AAE/D,UAAI,SAAS,IAAI;AACf,mBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,QAAQ,EAAE,GAAG;AACnD,kBAAQ,MAAM,GAAG,KAAK,GAAG;AAAA,QAC3B;AAAA,MACF;AACA,UAAI,SAAS,KAAK;AAChB,mBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,QAAQ,GAAG,GAAG;AACpD,kBAAQ,MAAM,IAAI,KAAK,GAAG;AAAA,QAC5B;AAAA,MACF;AACA,UAAI,SAAS,OAAO;AAClB,gBAAQ,MAAM,MAAM,QAAQ,MAAM,QAAQ;AAAA,UACxC,WAAW,QAAQ,MAAM;AAAA,QAC3B,CAAC;AAAA,MACH;AACA,UAAI,SAAS,MAAO,SAAQ,MAAM,MAAM,QAAQ,KAAK;AACrD,UAAI,SAAS,OAAQ,SAAQ,MAAM,OAAO,QAAQ,MAAM;AAExD,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM;AAC9B,UAAI,MAAO,OAAM;AACjB,aAAQ,QAAQ,CAAC;AAAA,IACnB;AAAA,IACA,SAAS,SAAS;AAAA,IAClB,WAAW,SAAS;AAAA,IACpB,iBAAiB,SAAS;AAAA,EAC5B,CAAC;AACH;;;ACxFA,IAAAC,sBAIO;AAoCA,SAAS,UACd,OACA,SAC8C;AAC9C,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AAEnC,aAAO,oBAAAC,aAAoB;AAAA,IACzB,YAAY,OAAO,SAAqB;AACtC,YAAM,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,OACnC,KAAQ,KAAK,EACb,OAAO,IAAI,EACX,OAAO,EACP,OAAO;AACV,UAAI,MAAO,OAAM;AACjB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,SAAS,aACf,OAAO,YAAY;AACjB,YAAM,YAAY,cAAc,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAC9D,YAAM,WAAW,YAAY,aAAkB,CAAC,SAAS,KAAK,CAAC;AAC/D,kBAAY,aAAkB,CAAC,SAAS,KAAK,GAAG,CAAC,QAAQ;AAAA,QACvD,GAAI,OAAO,CAAC;AAAA,QACZ;AAAA,MACF,CAAC;AACD,aAAO,EAAE,SAAS;AAAA,IACpB,IACA;AAAA,IACJ,SAAS,SAAS,aACd,CAAC,MAAM,OAAO,YAA4C;AACxD,UAAI,SAAS,aAAa,QAAW;AACnC,oBAAY,aAAa,CAAC,SAAS,KAAK,GAAG,QAAQ,QAAQ;AAAA,MAC7D;AAAA,IACF,IACA;AAAA,IACJ,WAAW,MAAM;AACf,kBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF,CAAC;AACH;AA8CO,SAAS,UACd,OACA,SACkD;AAClD,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AAEnC,aAAO,oBAAAA,aAAoB;AAAA,IACzB,YAAY,OAAO,EAAE,MAAM,GAAG,MAAsB;AAClD,UAAI,QAAQ,OAAO,KAAQ,KAAK,EAAE,OAAO,IAAI;AAC7C,iBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,EAAE,GAAG;AAC3C,gBAAQ,MAAM,GAAG,KAAK,GAAG;AAAA,MAC3B;AACA,YAAM,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,MAAM,OAAO,EAAE,OAAO;AAC5D,UAAI,MAAO,OAAM;AACjB,aAAO;AAAA,IACT;AAAA,IACA,UAAU,SAAS,aACf,OAAO,EAAE,MAAM,GAAG,MAAM;AACtB,YAAM,YAAY,cAAc,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAC9D,YAAM,WAAW,YAAY,aAAkB,CAAC,SAAS,KAAK,CAAC;AAC/D,kBAAY;AAAA,QAAkB,CAAC,SAAS,KAAK;AAAA,QAAG,CAAC,SAC9C,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ;AACvB,gBAAM,SAAS;AACf,gBAAM,UAAU,OAAO,QAAQ,EAAE,EAAE;AAAA,YACjC,CAAC,CAAC,KAAK,GAAG,MAAM,OAAO,OAAO,GAAG,CAAC,MAAM;AAAA,UAC1C;AACA,iBAAO,UAAU,EAAE,GAAG,KAAK,GAAG,KAAK,IAAI;AAAA,QACzC,CAAC;AAAA,MACH;AACA,aAAO,EAAE,SAAS;AAAA,IACpB,IACA;AAAA,IACJ,SAAS,SAAS,aACd,CAAC,MAAM,OAAO,YAA4C;AACxD,UAAI,SAAS,aAAa,QAAW;AACnC,oBAAY,aAAa,CAAC,SAAS,KAAK,GAAG,QAAQ,QAAQ;AAAA,MAC7D;AAAA,IACF,IACA;AAAA,IACJ,WAAW,MAAM;AACf,kBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF,CAAC;AACH;AAiCO,SAAS,UACd,OACA,SAC6D;AAC7D,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AAEnC,aAAO,oBAAAA,aAAoB;AAAA,IACzB,YAAY,OAAO,OAA+B;AAChD,UAAI,QAAQ,OAAO,KAAK,KAAK,EAAE,OAAO;AACtC,iBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,EAAE,GAAG;AAC3C,gBAAQ,MAAM,GAAG,KAAK,GAAG;AAAA,MAC3B;AACA,YAAM,EAAE,MAAM,IAAI,MAAM;AACxB,UAAI,MAAO,OAAM;AAAA,IACnB;AAAA,IACA,UAAU,SAAS,aACf,OAAO,OAAO;AACZ,YAAM,YAAY,cAAc,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAC9D,YAAM,WAAW,YAAY,aAAwC,CAAC,SAAS,KAAK,CAAC;AACrF,kBAAY;AAAA,QAAwC,CAAC,SAAS,KAAK;AAAA,QAAG,CAAC,SACpE,OAAO,CAAC,GAAG;AAAA,UAAO,CAAC,QAClB,CAAC,OAAO,QAAQ,EAAE,EAAE;AAAA,YAClB,CAAC,CAAC,KAAK,GAAG,MAAM,OAAO,IAAI,GAAG,CAAC,MAAM;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AACA,aAAO,EAAE,SAAS;AAAA,IACpB,IACA;AAAA,IACJ,SAAS,SAAS,aACd,CAAC,MAAM,OAAO,YAAkE;AAC9E,UAAI,SAAS,aAAa,QAAW;AACnC,oBAAY,aAAa,CAAC,SAAS,KAAK,GAAG,QAAQ,QAAQ;AAAA,MAC7D;AAAA,IACF,IACA;AAAA,IACJ,WAAW,MAAM;AACf,kBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF,CAAC;AACH;;;ACnPA,IAAAC,gBAA4C;AAC5C,IAAAC,sBAA+B;AAC/B,sBAIO;AA8CA,SAAS,YACd,OACA,SACgC;AAChC,QAAM,SAAS,UAAU;AACzB,QAAM,kBAAc,oCAAe;AACnC,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAA6B,SAAS;AAClE,QAAM,kBAAc,sBAAmC,IAAI;AAE3D,+BAAU,MAAM;AAEd,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,SAAS,OAAO,UAAU;AAEhC,QAAI,CAAC,YAAY,SAAS;AACxB,kBAAY,UAAU,IAAI,oCAAoB;AAAA,QAC5C,KAAK,OAAO;AAAA,QACZ,YAAY,OAAO;AAAA,QACnB,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,UAAM,MAAM,YAAY,QAAQ,UAAa,OAAO;AAAA,MAClD,OAAO,SAAS,SAAS;AAAA,MACzB,QAAQ,SAAS;AAAA,MACjB,QAAQ,OAAO;AACb,iBAAS,UAAU,KAAK;AACxB,YAAI,SAAS,sBAAsB,OAAO;AACxC,sBAAY,kBAAkB,EAAE,UAAU,CAAC,SAAS,KAAK,EAAE,CAAC;AAAA,QAC9D;AAAA,MACF;AAAA,MACA,eAAe;AACb,kBAAU,QAAQ;AAAA,MACpB;AAAA,MACA,UAAU;AACR,kBAAU,OAAO;AAAA,MACnB;AAAA,IACF,CAAC;AAED,cAAU,SAAS;AAEnB,WAAO,MAAM;AACX,UAAI,YAAY;AAChB,gBAAU,QAAQ;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,OAAO,SAAS,MAAM,CAAC;AAE3C,SAAO,EAAE,OAAO;AAClB;;;ACrGA,IAAAC,gBAAoC;AA6C7B,SAAS,gBACd,OACA,SAM0B;AAC1B,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAkC,IAAI;AACxE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAA6B,SAAS;AAElE,+BAAU,MAAM;AAEd,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,MAAM,OAAO,SAAS,UAAa,OAAO;AAAA,MAC9C,OAAO,SAAS,SAAS;AAAA,MACzB,QAAQ,SAAS;AAAA,MACjB,QAAQ,OAAO;AACb,qBAAa,KAAK;AAAA,MACpB;AAAA,MACA,eAAe;AACb,kBAAU,QAAQ;AAAA,MACpB;AAAA,MACA,UAAU;AACR,kBAAU,OAAO;AAAA,MACnB;AAAA,IACF,CAAC;AAED,cAAU,SAAS;AAEnB,WAAO,MAAM;AACX,UAAI,YAAY;AAChB,gBAAU,QAAQ;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,OAAO,SAAS,MAAM,CAAC;AAE3C,SAAO,EAAE,WAAW,OAAO;AAC7B;;;ACrFA,IAAAC,gBAAiD;AA+C1C,SAAS,UAAyB;AACvC,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAsB,IAAI;AAClD,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,IAAI;AAE/C,+BAAU,MAAM;AACd,UAAM,UAAU,OAAO,KAAK,WAAW;AACvC,QAAI,SAAS;AACX,aAAO,KACJ,QAAQ,EACR,KAAK,OAAO,EACZ,MAAM,MAAM,QAAQ,IAAI,CAAC,EACzB,QAAQ,MAAM,aAAa,KAAK,CAAC;AAAA,IACtC,OAAO;AACL,mBAAa,KAAK;AAAA,IACpB;AAEA,UAAM,QAAQ,OAAO,KAAK,kBAAkB,CAAC,OAAO,aAAa;AAC/D,UAAI,UAAU,eAAe,UAAU,mBAAmB;AACxD,eAAO,KACJ,QAAQ,EACR,KAAK,OAAO,EACZ,MAAM,MAAM,QAAQ,IAAI,CAAC;AAAA,MAC9B,WAAW,UAAU,gBAAgB,UAAU,wBAAwB;AACrE,gBAAQ,IAAI;AAAA,MACd;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,aAAS;AAAA,IACb,OAAO,OAAe,aAAqB;AACzC,YAAM,OAAO,KAAK,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,aAAS;AAAA,IACb,OAAO,OAAe,aAAqB;AACzC,YAAM,OAAO,KAAK,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,cAAU,2BAAY,YAAY;AACtC,UAAM,OAAO,KAAK,QAAQ;AAAA,EAC5B,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,sBAAkB;AAAA,IACtB,CAAC,UAAkB,SAAiC;AAClD,YAAM,MAAM,OAAO,KAAK,gBAAgB,UAAU,IAAI;AACtD,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,SAAS,OAAO;AAAA,MACzB;AAAA,IACF;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SAAO,EAAE,MAAM,WAAW,QAAQ,QAAQ,SAAS,gBAAgB;AACrE;;;AC3GA,IAAAC,gBAAsC;AA4C/B,SAAS,UAAU,QAAiC;AACzD,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAErD,QAAM,aAAS;AAAA,IACb,OAAO,MAAc,MAAmB,SAAyB;AAC/D,qBAAe,IAAI;AACnB,eAAS,IAAI;AACb,UAAI;AACF,cAAM,OAAO,QAAQ,KAAK,MAAM,EAAE,OAAO,MAAM,MAAM,IAAI;AAAA,MAC3D,SAAS,KAAK;AACZ,cAAM,cACJ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AACpD,iBAAS,WAAW;AACpB,cAAM;AAAA,MACR,UAAE;AACA,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,MAAM;AAAA,EACjB;AAEA,SAAO,EAAE,QAAQ,aAAa,MAAM;AACtC;;;ARlDA,oBAAmC;","names":["useTanstackQuery","import_react_query","useTanstackMutation","import_react","import_react_query","import_react","import_react","import_react"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { MimDBClient, MimDBError, User, UploadOptions } from '@mimdb/client';
|
|
4
|
+
export { createServerClient } from '@mimdb/client';
|
|
4
5
|
import { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
|
|
5
6
|
import { RealtimeEvent, SubscriptionStatus } from '@mimdb/realtime';
|
|
6
7
|
|
|
@@ -105,24 +106,38 @@ interface UseQueryOptions {
|
|
|
105
106
|
*/
|
|
106
107
|
declare function useQuery<T = Record<string, unknown>>(table: string, options?: UseQueryOptions): UseQueryResult<T[], Error>;
|
|
107
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Options for the {@link useInsert} hook.
|
|
111
|
+
*/
|
|
112
|
+
interface UseInsertOptions {
|
|
113
|
+
/**
|
|
114
|
+
* Enable optimistic updates. When true, the new row is appended to
|
|
115
|
+
* the query cache immediately and rolled back on error.
|
|
116
|
+
*/
|
|
117
|
+
optimistic?: boolean;
|
|
118
|
+
}
|
|
108
119
|
/**
|
|
109
120
|
* React hook for inserting a row into a MimDB table.
|
|
110
121
|
*
|
|
111
122
|
* On success, all `useQuery` caches for the same table are automatically
|
|
112
123
|
* invalidated so lists stay in sync.
|
|
113
124
|
*
|
|
125
|
+
* When `optimistic` is enabled, the new row is appended to the cache
|
|
126
|
+
* before the server responds. On error the cache is rolled back.
|
|
127
|
+
*
|
|
114
128
|
* @typeParam T - Expected row type. Defaults to a generic record.
|
|
115
|
-
* @param table
|
|
129
|
+
* @param table - Name of the database table.
|
|
130
|
+
* @param options - Insert hook options.
|
|
116
131
|
* @returns A TanStack `UseMutationResult` whose `mutate` / `mutateAsync`
|
|
117
132
|
* accepts a partial row to insert.
|
|
118
133
|
*
|
|
119
134
|
* @example
|
|
120
135
|
* ```tsx
|
|
121
|
-
* const insert = useInsert<Todo>('todos')
|
|
136
|
+
* const insert = useInsert<Todo>('todos', { optimistic: true })
|
|
122
137
|
* insert.mutate({ task: 'Buy milk', done: false })
|
|
123
138
|
* ```
|
|
124
139
|
*/
|
|
125
|
-
declare function useInsert<T = Record<string, unknown>>(table: string): UseMutationResult<T, MimDBError, Partial<T>>;
|
|
140
|
+
declare function useInsert<T = Record<string, unknown>>(table: string, options?: UseInsertOptions): UseMutationResult<T, MimDBError, Partial<T>>;
|
|
126
141
|
/**
|
|
127
142
|
* Input shape for the {@link useUpdate} mutation.
|
|
128
143
|
*
|
|
@@ -134,6 +149,16 @@ interface UpdateInput<T> {
|
|
|
134
149
|
/** Equality filters identifying which rows to update. */
|
|
135
150
|
eq: Record<string, string>;
|
|
136
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Options for the {@link useUpdate} hook.
|
|
154
|
+
*/
|
|
155
|
+
interface UseUpdateOptions {
|
|
156
|
+
/**
|
|
157
|
+
* Enable optimistic updates. When true, matching rows in the cache
|
|
158
|
+
* are updated immediately and rolled back on error.
|
|
159
|
+
*/
|
|
160
|
+
optimistic?: boolean;
|
|
161
|
+
}
|
|
137
162
|
/**
|
|
138
163
|
* React hook for updating rows in a MimDB table.
|
|
139
164
|
*
|
|
@@ -141,17 +166,31 @@ interface UpdateInput<T> {
|
|
|
141
166
|
* `eq` (equality filters to target specific rows). On success, all
|
|
142
167
|
* `useQuery` caches for the same table are invalidated.
|
|
143
168
|
*
|
|
169
|
+
* When `optimistic` is enabled, matching rows in the cache are updated
|
|
170
|
+
* in-place before the server responds and rolled back on error.
|
|
171
|
+
*
|
|
144
172
|
* @typeParam T - Expected row type. Defaults to a generic record.
|
|
145
|
-
* @param table
|
|
173
|
+
* @param table - Name of the database table.
|
|
174
|
+
* @param options - Update hook options.
|
|
146
175
|
* @returns A TanStack `UseMutationResult`.
|
|
147
176
|
*
|
|
148
177
|
* @example
|
|
149
178
|
* ```tsx
|
|
150
|
-
* const update = useUpdate<Todo>('todos')
|
|
179
|
+
* const update = useUpdate<Todo>('todos', { optimistic: true })
|
|
151
180
|
* update.mutate({ data: { done: true }, eq: { id: '42' } })
|
|
152
181
|
* ```
|
|
153
182
|
*/
|
|
154
|
-
declare function useUpdate<T = Record<string, unknown>>(table: string): UseMutationResult<T, MimDBError, UpdateInput<T>>;
|
|
183
|
+
declare function useUpdate<T = Record<string, unknown>>(table: string, options?: UseUpdateOptions): UseMutationResult<T, MimDBError, UpdateInput<T>>;
|
|
184
|
+
/**
|
|
185
|
+
* Options for the {@link useDelete} hook.
|
|
186
|
+
*/
|
|
187
|
+
interface UseDeleteOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Enable optimistic updates. When true, matching rows are removed
|
|
190
|
+
* from the cache immediately and restored on error.
|
|
191
|
+
*/
|
|
192
|
+
optimistic?: boolean;
|
|
193
|
+
}
|
|
155
194
|
/**
|
|
156
195
|
* React hook for deleting rows from a MimDB table.
|
|
157
196
|
*
|
|
@@ -159,16 +198,20 @@ declare function useUpdate<T = Record<string, unknown>>(table: string): UseMutat
|
|
|
159
198
|
* to delete. On success, all `useQuery` caches for the same table are
|
|
160
199
|
* invalidated.
|
|
161
200
|
*
|
|
162
|
-
*
|
|
201
|
+
* When `optimistic` is enabled, matching rows are removed from the cache
|
|
202
|
+
* before the server responds and restored on error.
|
|
203
|
+
*
|
|
204
|
+
* @param table - Name of the database table.
|
|
205
|
+
* @param options - Delete hook options.
|
|
163
206
|
* @returns A TanStack `UseMutationResult` whose `mutate` accepts equality filters.
|
|
164
207
|
*
|
|
165
208
|
* @example
|
|
166
209
|
* ```tsx
|
|
167
|
-
* const del = useDelete('todos')
|
|
210
|
+
* const del = useDelete('todos', { optimistic: true })
|
|
168
211
|
* del.mutate({ id: '42' })
|
|
169
212
|
* ```
|
|
170
213
|
*/
|
|
171
|
-
declare function useDelete(table: string): UseMutationResult<void, MimDBError, Record<string, string>>;
|
|
214
|
+
declare function useDelete(table: string, options?: UseDeleteOptions): UseMutationResult<void, MimDBError, Record<string, string>>;
|
|
172
215
|
|
|
173
216
|
/**
|
|
174
217
|
* Options for the {@link useRealtime} hook.
|
|
@@ -216,6 +259,53 @@ declare function useRealtime<T = Record<string, unknown>>(table: string, options
|
|
|
216
259
|
status: SubscriptionStatus;
|
|
217
260
|
};
|
|
218
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Return type of the {@link useSubscription} hook.
|
|
264
|
+
*
|
|
265
|
+
* @typeParam T - Expected row type for realtime events.
|
|
266
|
+
*/
|
|
267
|
+
interface UseSubscriptionResult<T> {
|
|
268
|
+
/** The most recently received realtime event, or null if none yet. */
|
|
269
|
+
lastEvent: RealtimeEvent<T> | null;
|
|
270
|
+
/** Current subscription lifecycle status. */
|
|
271
|
+
status: SubscriptionStatus;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Subscribe to a table and maintain the latest event in React state.
|
|
275
|
+
*
|
|
276
|
+
* Unlike {@link useRealtime} (which fires callbacks), this hook returns
|
|
277
|
+
* the most recent event directly as component state, making it ideal
|
|
278
|
+
* for rendering the latest change inline.
|
|
279
|
+
*
|
|
280
|
+
* Uses `client.realtime` (the lazy realtime accessor on MimDBClient)
|
|
281
|
+
* so no separate realtime client setup is needed.
|
|
282
|
+
*
|
|
283
|
+
* The subscription is cleaned up when the component unmounts or when
|
|
284
|
+
* `table`, `event`, or `filter` change.
|
|
285
|
+
*
|
|
286
|
+
* @typeParam T - Expected row type for realtime events.
|
|
287
|
+
* @param table - Database table to subscribe to.
|
|
288
|
+
* @param options - Event type and filter configuration.
|
|
289
|
+
* @returns The latest event and subscription status.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```tsx
|
|
293
|
+
* const { lastEvent, status } = useSubscription<Message>('messages', {
|
|
294
|
+
* event: 'INSERT',
|
|
295
|
+
* })
|
|
296
|
+
*
|
|
297
|
+
* if (lastEvent) {
|
|
298
|
+
* console.log('Latest insert:', lastEvent.new)
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
declare function useSubscription<T = Record<string, unknown>>(table: string, options?: {
|
|
303
|
+
/** Event type filter. Defaults to `'*'` (all events). */
|
|
304
|
+
event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE';
|
|
305
|
+
/** Row filter expression (e.g. `'user_id=eq.42'`). */
|
|
306
|
+
filter?: string;
|
|
307
|
+
}): UseSubscriptionResult<T>;
|
|
308
|
+
|
|
219
309
|
/**
|
|
220
310
|
* Return type of the {@link useAuth} hook.
|
|
221
311
|
*/
|
|
@@ -303,4 +393,4 @@ interface UseUploadResult {
|
|
|
303
393
|
*/
|
|
304
394
|
declare function useUpload(bucket: string): UseUploadResult;
|
|
305
395
|
|
|
306
|
-
export { MimDBProvider, type MimDBProviderProps, type UpdateInput, type UseAuthResult, type UseQueryOptions, type UseRealtimeOptions, type UseUploadResult, useAuth, useClient, useDelete, useInsert, useQuery, useRealtime, useUpdate, useUpload };
|
|
396
|
+
export { MimDBProvider, type MimDBProviderProps, type UpdateInput, type UseAuthResult, type UseDeleteOptions, type UseInsertOptions, type UseQueryOptions, type UseRealtimeOptions, type UseSubscriptionResult, type UseUpdateOptions, type UseUploadResult, useAuth, useClient, useDelete, useInsert, useQuery, useRealtime, useSubscription, useUpdate, useUpload };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { MimDBClient, MimDBError, User, UploadOptions } from '@mimdb/client';
|
|
4
|
+
export { createServerClient } from '@mimdb/client';
|
|
4
5
|
import { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
|
|
5
6
|
import { RealtimeEvent, SubscriptionStatus } from '@mimdb/realtime';
|
|
6
7
|
|
|
@@ -105,24 +106,38 @@ interface UseQueryOptions {
|
|
|
105
106
|
*/
|
|
106
107
|
declare function useQuery<T = Record<string, unknown>>(table: string, options?: UseQueryOptions): UseQueryResult<T[], Error>;
|
|
107
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Options for the {@link useInsert} hook.
|
|
111
|
+
*/
|
|
112
|
+
interface UseInsertOptions {
|
|
113
|
+
/**
|
|
114
|
+
* Enable optimistic updates. When true, the new row is appended to
|
|
115
|
+
* the query cache immediately and rolled back on error.
|
|
116
|
+
*/
|
|
117
|
+
optimistic?: boolean;
|
|
118
|
+
}
|
|
108
119
|
/**
|
|
109
120
|
* React hook for inserting a row into a MimDB table.
|
|
110
121
|
*
|
|
111
122
|
* On success, all `useQuery` caches for the same table are automatically
|
|
112
123
|
* invalidated so lists stay in sync.
|
|
113
124
|
*
|
|
125
|
+
* When `optimistic` is enabled, the new row is appended to the cache
|
|
126
|
+
* before the server responds. On error the cache is rolled back.
|
|
127
|
+
*
|
|
114
128
|
* @typeParam T - Expected row type. Defaults to a generic record.
|
|
115
|
-
* @param table
|
|
129
|
+
* @param table - Name of the database table.
|
|
130
|
+
* @param options - Insert hook options.
|
|
116
131
|
* @returns A TanStack `UseMutationResult` whose `mutate` / `mutateAsync`
|
|
117
132
|
* accepts a partial row to insert.
|
|
118
133
|
*
|
|
119
134
|
* @example
|
|
120
135
|
* ```tsx
|
|
121
|
-
* const insert = useInsert<Todo>('todos')
|
|
136
|
+
* const insert = useInsert<Todo>('todos', { optimistic: true })
|
|
122
137
|
* insert.mutate({ task: 'Buy milk', done: false })
|
|
123
138
|
* ```
|
|
124
139
|
*/
|
|
125
|
-
declare function useInsert<T = Record<string, unknown>>(table: string): UseMutationResult<T, MimDBError, Partial<T>>;
|
|
140
|
+
declare function useInsert<T = Record<string, unknown>>(table: string, options?: UseInsertOptions): UseMutationResult<T, MimDBError, Partial<T>>;
|
|
126
141
|
/**
|
|
127
142
|
* Input shape for the {@link useUpdate} mutation.
|
|
128
143
|
*
|
|
@@ -134,6 +149,16 @@ interface UpdateInput<T> {
|
|
|
134
149
|
/** Equality filters identifying which rows to update. */
|
|
135
150
|
eq: Record<string, string>;
|
|
136
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Options for the {@link useUpdate} hook.
|
|
154
|
+
*/
|
|
155
|
+
interface UseUpdateOptions {
|
|
156
|
+
/**
|
|
157
|
+
* Enable optimistic updates. When true, matching rows in the cache
|
|
158
|
+
* are updated immediately and rolled back on error.
|
|
159
|
+
*/
|
|
160
|
+
optimistic?: boolean;
|
|
161
|
+
}
|
|
137
162
|
/**
|
|
138
163
|
* React hook for updating rows in a MimDB table.
|
|
139
164
|
*
|
|
@@ -141,17 +166,31 @@ interface UpdateInput<T> {
|
|
|
141
166
|
* `eq` (equality filters to target specific rows). On success, all
|
|
142
167
|
* `useQuery` caches for the same table are invalidated.
|
|
143
168
|
*
|
|
169
|
+
* When `optimistic` is enabled, matching rows in the cache are updated
|
|
170
|
+
* in-place before the server responds and rolled back on error.
|
|
171
|
+
*
|
|
144
172
|
* @typeParam T - Expected row type. Defaults to a generic record.
|
|
145
|
-
* @param table
|
|
173
|
+
* @param table - Name of the database table.
|
|
174
|
+
* @param options - Update hook options.
|
|
146
175
|
* @returns A TanStack `UseMutationResult`.
|
|
147
176
|
*
|
|
148
177
|
* @example
|
|
149
178
|
* ```tsx
|
|
150
|
-
* const update = useUpdate<Todo>('todos')
|
|
179
|
+
* const update = useUpdate<Todo>('todos', { optimistic: true })
|
|
151
180
|
* update.mutate({ data: { done: true }, eq: { id: '42' } })
|
|
152
181
|
* ```
|
|
153
182
|
*/
|
|
154
|
-
declare function useUpdate<T = Record<string, unknown>>(table: string): UseMutationResult<T, MimDBError, UpdateInput<T>>;
|
|
183
|
+
declare function useUpdate<T = Record<string, unknown>>(table: string, options?: UseUpdateOptions): UseMutationResult<T, MimDBError, UpdateInput<T>>;
|
|
184
|
+
/**
|
|
185
|
+
* Options for the {@link useDelete} hook.
|
|
186
|
+
*/
|
|
187
|
+
interface UseDeleteOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Enable optimistic updates. When true, matching rows are removed
|
|
190
|
+
* from the cache immediately and restored on error.
|
|
191
|
+
*/
|
|
192
|
+
optimistic?: boolean;
|
|
193
|
+
}
|
|
155
194
|
/**
|
|
156
195
|
* React hook for deleting rows from a MimDB table.
|
|
157
196
|
*
|
|
@@ -159,16 +198,20 @@ declare function useUpdate<T = Record<string, unknown>>(table: string): UseMutat
|
|
|
159
198
|
* to delete. On success, all `useQuery` caches for the same table are
|
|
160
199
|
* invalidated.
|
|
161
200
|
*
|
|
162
|
-
*
|
|
201
|
+
* When `optimistic` is enabled, matching rows are removed from the cache
|
|
202
|
+
* before the server responds and restored on error.
|
|
203
|
+
*
|
|
204
|
+
* @param table - Name of the database table.
|
|
205
|
+
* @param options - Delete hook options.
|
|
163
206
|
* @returns A TanStack `UseMutationResult` whose `mutate` accepts equality filters.
|
|
164
207
|
*
|
|
165
208
|
* @example
|
|
166
209
|
* ```tsx
|
|
167
|
-
* const del = useDelete('todos')
|
|
210
|
+
* const del = useDelete('todos', { optimistic: true })
|
|
168
211
|
* del.mutate({ id: '42' })
|
|
169
212
|
* ```
|
|
170
213
|
*/
|
|
171
|
-
declare function useDelete(table: string): UseMutationResult<void, MimDBError, Record<string, string>>;
|
|
214
|
+
declare function useDelete(table: string, options?: UseDeleteOptions): UseMutationResult<void, MimDBError, Record<string, string>>;
|
|
172
215
|
|
|
173
216
|
/**
|
|
174
217
|
* Options for the {@link useRealtime} hook.
|
|
@@ -216,6 +259,53 @@ declare function useRealtime<T = Record<string, unknown>>(table: string, options
|
|
|
216
259
|
status: SubscriptionStatus;
|
|
217
260
|
};
|
|
218
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Return type of the {@link useSubscription} hook.
|
|
264
|
+
*
|
|
265
|
+
* @typeParam T - Expected row type for realtime events.
|
|
266
|
+
*/
|
|
267
|
+
interface UseSubscriptionResult<T> {
|
|
268
|
+
/** The most recently received realtime event, or null if none yet. */
|
|
269
|
+
lastEvent: RealtimeEvent<T> | null;
|
|
270
|
+
/** Current subscription lifecycle status. */
|
|
271
|
+
status: SubscriptionStatus;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Subscribe to a table and maintain the latest event in React state.
|
|
275
|
+
*
|
|
276
|
+
* Unlike {@link useRealtime} (which fires callbacks), this hook returns
|
|
277
|
+
* the most recent event directly as component state, making it ideal
|
|
278
|
+
* for rendering the latest change inline.
|
|
279
|
+
*
|
|
280
|
+
* Uses `client.realtime` (the lazy realtime accessor on MimDBClient)
|
|
281
|
+
* so no separate realtime client setup is needed.
|
|
282
|
+
*
|
|
283
|
+
* The subscription is cleaned up when the component unmounts or when
|
|
284
|
+
* `table`, `event`, or `filter` change.
|
|
285
|
+
*
|
|
286
|
+
* @typeParam T - Expected row type for realtime events.
|
|
287
|
+
* @param table - Database table to subscribe to.
|
|
288
|
+
* @param options - Event type and filter configuration.
|
|
289
|
+
* @returns The latest event and subscription status.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```tsx
|
|
293
|
+
* const { lastEvent, status } = useSubscription<Message>('messages', {
|
|
294
|
+
* event: 'INSERT',
|
|
295
|
+
* })
|
|
296
|
+
*
|
|
297
|
+
* if (lastEvent) {
|
|
298
|
+
* console.log('Latest insert:', lastEvent.new)
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
declare function useSubscription<T = Record<string, unknown>>(table: string, options?: {
|
|
303
|
+
/** Event type filter. Defaults to `'*'` (all events). */
|
|
304
|
+
event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE';
|
|
305
|
+
/** Row filter expression (e.g. `'user_id=eq.42'`). */
|
|
306
|
+
filter?: string;
|
|
307
|
+
}): UseSubscriptionResult<T>;
|
|
308
|
+
|
|
219
309
|
/**
|
|
220
310
|
* Return type of the {@link useAuth} hook.
|
|
221
311
|
*/
|
|
@@ -303,4 +393,4 @@ interface UseUploadResult {
|
|
|
303
393
|
*/
|
|
304
394
|
declare function useUpload(bucket: string): UseUploadResult;
|
|
305
395
|
|
|
306
|
-
export { MimDBProvider, type MimDBProviderProps, type UpdateInput, type UseAuthResult, type UseQueryOptions, type UseRealtimeOptions, type UseUploadResult, useAuth, useClient, useDelete, useInsert, useQuery, useRealtime, useUpdate, useUpload };
|
|
396
|
+
export { MimDBProvider, type MimDBProviderProps, type UpdateInput, type UseAuthResult, type UseDeleteOptions, type UseInsertOptions, type UseQueryOptions, type UseRealtimeOptions, type UseSubscriptionResult, type UseUpdateOptions, type UseUploadResult, useAuth, useClient, useDelete, useInsert, useQuery, useRealtime, useSubscription, useUpdate, useUpload };
|
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
useMutation as useTanstackMutation,
|
|
58
58
|
useQueryClient
|
|
59
59
|
} from "@tanstack/react-query";
|
|
60
|
-
function useInsert(table) {
|
|
60
|
+
function useInsert(table, options) {
|
|
61
61
|
const client = useClient();
|
|
62
62
|
const queryClient = useQueryClient();
|
|
63
63
|
return useTanstackMutation({
|
|
@@ -66,12 +66,26 @@ function useInsert(table) {
|
|
|
66
66
|
if (error) throw error;
|
|
67
67
|
return result;
|
|
68
68
|
},
|
|
69
|
-
|
|
69
|
+
onMutate: options?.optimistic ? async (newData) => {
|
|
70
|
+
await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
|
|
71
|
+
const previous = queryClient.getQueryData(["mimdb", table]);
|
|
72
|
+
queryClient.setQueryData(["mimdb", table], (old) => [
|
|
73
|
+
...old ?? [],
|
|
74
|
+
newData
|
|
75
|
+
]);
|
|
76
|
+
return { previous };
|
|
77
|
+
} : void 0,
|
|
78
|
+
onError: options?.optimistic ? (_err, _data, context) => {
|
|
79
|
+
if (context?.previous !== void 0) {
|
|
80
|
+
queryClient.setQueryData(["mimdb", table], context.previous);
|
|
81
|
+
}
|
|
82
|
+
} : void 0,
|
|
83
|
+
onSettled: () => {
|
|
70
84
|
queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
|
|
71
85
|
}
|
|
72
86
|
});
|
|
73
87
|
}
|
|
74
|
-
function useUpdate(table) {
|
|
88
|
+
function useUpdate(table, options) {
|
|
75
89
|
const client = useClient();
|
|
76
90
|
const queryClient = useQueryClient();
|
|
77
91
|
return useTanstackMutation({
|
|
@@ -84,12 +98,32 @@ function useUpdate(table) {
|
|
|
84
98
|
if (error) throw error;
|
|
85
99
|
return result;
|
|
86
100
|
},
|
|
87
|
-
|
|
101
|
+
onMutate: options?.optimistic ? async ({ data, eq }) => {
|
|
102
|
+
await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
|
|
103
|
+
const previous = queryClient.getQueryData(["mimdb", table]);
|
|
104
|
+
queryClient.setQueryData(
|
|
105
|
+
["mimdb", table],
|
|
106
|
+
(old) => (old ?? []).map((row) => {
|
|
107
|
+
const record = row;
|
|
108
|
+
const matches = Object.entries(eq).every(
|
|
109
|
+
([col, val]) => String(record[col]) === val
|
|
110
|
+
);
|
|
111
|
+
return matches ? { ...row, ...data } : row;
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
return { previous };
|
|
115
|
+
} : void 0,
|
|
116
|
+
onError: options?.optimistic ? (_err, _data, context) => {
|
|
117
|
+
if (context?.previous !== void 0) {
|
|
118
|
+
queryClient.setQueryData(["mimdb", table], context.previous);
|
|
119
|
+
}
|
|
120
|
+
} : void 0,
|
|
121
|
+
onSettled: () => {
|
|
88
122
|
queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
|
|
89
123
|
}
|
|
90
124
|
});
|
|
91
125
|
}
|
|
92
|
-
function useDelete(table) {
|
|
126
|
+
function useDelete(table, options) {
|
|
93
127
|
const client = useClient();
|
|
94
128
|
const queryClient = useQueryClient();
|
|
95
129
|
return useTanstackMutation({
|
|
@@ -101,7 +135,25 @@ function useDelete(table) {
|
|
|
101
135
|
const { error } = await query;
|
|
102
136
|
if (error) throw error;
|
|
103
137
|
},
|
|
104
|
-
|
|
138
|
+
onMutate: options?.optimistic ? async (eq) => {
|
|
139
|
+
await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
|
|
140
|
+
const previous = queryClient.getQueryData(["mimdb", table]);
|
|
141
|
+
queryClient.setQueryData(
|
|
142
|
+
["mimdb", table],
|
|
143
|
+
(old) => (old ?? []).filter(
|
|
144
|
+
(row) => !Object.entries(eq).every(
|
|
145
|
+
([col, val]) => String(row[col]) === val
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
return { previous };
|
|
150
|
+
} : void 0,
|
|
151
|
+
onError: options?.optimistic ? (_err, _data, context) => {
|
|
152
|
+
if (context?.previous !== void 0) {
|
|
153
|
+
queryClient.setQueryData(["mimdb", table], context.previous);
|
|
154
|
+
}
|
|
155
|
+
} : void 0,
|
|
156
|
+
onSettled: () => {
|
|
105
157
|
queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
|
|
106
158
|
}
|
|
107
159
|
});
|
|
@@ -119,6 +171,7 @@ function useRealtime(table, options) {
|
|
|
119
171
|
const [status, setStatus] = useState("pending");
|
|
120
172
|
const realtimeRef = useRef(null);
|
|
121
173
|
useEffect(() => {
|
|
174
|
+
if (typeof window === "undefined") return;
|
|
122
175
|
const config = client.getConfig();
|
|
123
176
|
if (!realtimeRef.current) {
|
|
124
177
|
realtimeRef.current = new MimDBRealtimeClient({
|
|
@@ -152,13 +205,43 @@ function useRealtime(table, options) {
|
|
|
152
205
|
return { status };
|
|
153
206
|
}
|
|
154
207
|
|
|
208
|
+
// src/use-subscription.ts
|
|
209
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
210
|
+
function useSubscription(table, options) {
|
|
211
|
+
const client = useClient();
|
|
212
|
+
const [lastEvent, setLastEvent] = useState2(null);
|
|
213
|
+
const [status, setStatus] = useState2("pending");
|
|
214
|
+
useEffect2(() => {
|
|
215
|
+
if (typeof window === "undefined") return;
|
|
216
|
+
const sub = client.realtime.subscribe(table, {
|
|
217
|
+
event: options?.event ?? "*",
|
|
218
|
+
filter: options?.filter,
|
|
219
|
+
onEvent(event) {
|
|
220
|
+
setLastEvent(event);
|
|
221
|
+
},
|
|
222
|
+
onSubscribed() {
|
|
223
|
+
setStatus("active");
|
|
224
|
+
},
|
|
225
|
+
onError() {
|
|
226
|
+
setStatus("error");
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
setStatus("pending");
|
|
230
|
+
return () => {
|
|
231
|
+
sub.unsubscribe();
|
|
232
|
+
setStatus("closed");
|
|
233
|
+
};
|
|
234
|
+
}, [table, options?.event, options?.filter]);
|
|
235
|
+
return { lastEvent, status };
|
|
236
|
+
}
|
|
237
|
+
|
|
155
238
|
// src/use-auth.ts
|
|
156
|
-
import { useState as
|
|
239
|
+
import { useState as useState3, useEffect as useEffect3, useCallback } from "react";
|
|
157
240
|
function useAuth() {
|
|
158
241
|
const client = useClient();
|
|
159
|
-
const [user, setUser] =
|
|
160
|
-
const [isLoading, setIsLoading] =
|
|
161
|
-
|
|
242
|
+
const [user, setUser] = useState3(null);
|
|
243
|
+
const [isLoading, setIsLoading] = useState3(true);
|
|
244
|
+
useEffect3(() => {
|
|
162
245
|
const session = client.auth.getSession();
|
|
163
246
|
if (session) {
|
|
164
247
|
client.auth.getUser().then(setUser).catch(() => setUser(null)).finally(() => setIsLoading(false));
|
|
@@ -168,7 +251,7 @@ function useAuth() {
|
|
|
168
251
|
const unsub = client.auth.onAuthStateChange((event, _session) => {
|
|
169
252
|
if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
|
|
170
253
|
client.auth.getUser().then(setUser).catch(() => setUser(null));
|
|
171
|
-
} else if (event === "SIGNED_OUT") {
|
|
254
|
+
} else if (event === "SIGNED_OUT" || event === "TOKEN_REFRESH_FAILED") {
|
|
172
255
|
setUser(null);
|
|
173
256
|
}
|
|
174
257
|
});
|
|
@@ -192,7 +275,9 @@ function useAuth() {
|
|
|
192
275
|
const signInWithOAuth = useCallback(
|
|
193
276
|
(provider, opts) => {
|
|
194
277
|
const url = client.auth.signInWithOAuth(provider, opts);
|
|
195
|
-
window
|
|
278
|
+
if (typeof window !== "undefined") {
|
|
279
|
+
window.location.href = url;
|
|
280
|
+
}
|
|
196
281
|
},
|
|
197
282
|
[client]
|
|
198
283
|
);
|
|
@@ -200,11 +285,11 @@ function useAuth() {
|
|
|
200
285
|
}
|
|
201
286
|
|
|
202
287
|
// src/use-upload.ts
|
|
203
|
-
import { useState as
|
|
288
|
+
import { useState as useState4, useCallback as useCallback2 } from "react";
|
|
204
289
|
function useUpload(bucket) {
|
|
205
290
|
const client = useClient();
|
|
206
|
-
const [isUploading, setIsUploading] =
|
|
207
|
-
const [error, setError] =
|
|
291
|
+
const [isUploading, setIsUploading] = useState4(false);
|
|
292
|
+
const [error, setError] = useState4(null);
|
|
208
293
|
const upload = useCallback2(
|
|
209
294
|
async (path, file, opts) => {
|
|
210
295
|
setIsUploading(true);
|
|
@@ -223,14 +308,19 @@ function useUpload(bucket) {
|
|
|
223
308
|
);
|
|
224
309
|
return { upload, isUploading, error };
|
|
225
310
|
}
|
|
311
|
+
|
|
312
|
+
// src/index.ts
|
|
313
|
+
import { createServerClient } from "@mimdb/client";
|
|
226
314
|
export {
|
|
227
315
|
MimDBProvider,
|
|
316
|
+
createServerClient,
|
|
228
317
|
useAuth,
|
|
229
318
|
useClient,
|
|
230
319
|
useDelete,
|
|
231
320
|
useInsert,
|
|
232
321
|
useQuery,
|
|
233
322
|
useRealtime,
|
|
323
|
+
useSubscription,
|
|
234
324
|
useUpdate,
|
|
235
325
|
useUpload
|
|
236
326
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/use-query.ts","../src/use-mutation.ts","../src/use-realtime.ts","../src/use-auth.ts","../src/use-upload.ts"],"sourcesContent":["import { createContext, useContext } from 'react'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * React context that holds the MimDB client instance.\n *\n * Consumers should use the {@link useClient} hook rather than accessing\n * this context directly.\n *\n * @internal\n */\nconst MimDBContext = createContext<MimDBClient | null>(null)\n\n/**\n * Retrieve the MimDB client from the nearest `<MimDBProvider>`.\n *\n * @returns The `MimDBClient` instance provided by the enclosing provider.\n * @throws If called outside of a `<MimDBProvider>` tree.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useClient()\n * // Use client.from(), client.auth, etc.\n * }\n * ```\n */\nexport function useClient(): MimDBClient {\n const client = useContext(MimDBContext)\n if (!client) {\n throw new Error('useClient must be used within <MimDBProvider>')\n }\n return client\n}\n\nexport { MimDBContext }\n","import { type ReactNode } from 'react'\nimport { MimDBContext } from './context'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * Props for the {@link MimDBProvider} component.\n */\nexport interface MimDBProviderProps {\n /** A configured `MimDBClient` instance to make available to child components. */\n client: MimDBClient\n /** The React subtree that will have access to the MimDB client. */\n children: ReactNode\n}\n\n/**\n * Context provider that makes a `MimDBClient` available to all descendant\n * components via the {@link useClient} hook.\n *\n * Wrap your application (or a subtree) with this provider and pass a\n * pre-configured client instance.\n *\n * @param props - Provider props containing the client and children.\n *\n * @example\n * ```tsx\n * import { createClient } from '@mimdb/client'\n * import { MimDBProvider } from '@mimdb/react'\n *\n * const client = createClient('https://api.mimdb.dev', 'ref', 'key')\n *\n * function App() {\n * return (\n * <MimDBProvider client={client}>\n * <MyApp />\n * </MimDBProvider>\n * )\n * }\n * ```\n */\nexport function MimDBProvider({ client, children }: MimDBProviderProps) {\n return <MimDBContext.Provider value={client}>{children}</MimDBContext.Provider>\n}\n","import {\n useQuery as useTanstackQuery,\n type UseQueryResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useQuery} hook.\n */\nexport interface UseQueryOptions {\n /** Column selection string (PostgREST format). Defaults to `'*'`. */\n select?: string\n /** Equality filters applied as `query.eq(column, value)`. */\n eq?: Record<string, string>\n /** Not-equal filters applied as `query.neq(column, value)`. */\n neq?: Record<string, string>\n /** Column ordering configuration. */\n order?: { column: string; ascending?: boolean }\n /** Maximum number of rows to return. */\n limit?: number\n /** Number of rows to skip before returning results. */\n offset?: number\n /** Whether the query should execute. Maps to TanStack Query's `enabled`. */\n enabled?: boolean\n /** Duration in ms before cached data is considered stale. */\n staleTime?: number\n /** Polling interval in ms, or `false` to disable. */\n refetchInterval?: number | false\n}\n\n/**\n * Fetch rows from a MimDB table using the REST API, backed by TanStack Query\n * for caching, deduplication, and background refetching.\n *\n * Automatically derives a stable query key from the table name and options\n * so cache invalidation works out of the box with the mutation hooks.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table to query.\n * @param options - Query filters, modifiers, and TanStack Query settings.\n * @returns A TanStack `UseQueryResult` containing the row array and status flags.\n *\n * @example\n * ```tsx\n * const { data, isLoading } = useQuery<Todo>('todos', {\n * eq: { done: 'false' },\n * order: { column: 'created_at', ascending: false },\n * limit: 20,\n * })\n * ```\n */\nexport function useQuery<T = Record<string, unknown>>(\n table: string,\n options?: UseQueryOptions,\n): UseQueryResult<T[], Error> {\n const client = useClient()\n\n return useTanstackQuery({\n queryKey: ['mimdb', table, options],\n queryFn: async () => {\n let query = client.from<T>(table).select(options?.select ?? '*')\n\n if (options?.eq) {\n for (const [col, val] of Object.entries(options.eq)) {\n query = query.eq(col, val)\n }\n }\n if (options?.neq) {\n for (const [col, val] of Object.entries(options.neq)) {\n query = query.neq(col, val)\n }\n }\n if (options?.order) {\n query = query.order(options.order.column, {\n ascending: options.order.ascending,\n })\n }\n if (options?.limit) query = query.limit(options.limit)\n if (options?.offset) query = query.offset(options.offset)\n\n const { data, error } = await query\n if (error) throw error\n return (data ?? []) as T[]\n },\n enabled: options?.enabled,\n staleTime: options?.staleTime,\n refetchInterval: options?.refetchInterval,\n })\n}\n","import {\n useMutation as useTanstackMutation,\n useQueryClient,\n type UseMutationResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\nimport type { MimDBError } from '@mimdb/client'\n\n/**\n * React hook for inserting a row into a MimDB table.\n *\n * On success, all `useQuery` caches for the same table are automatically\n * invalidated so lists stay in sync.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @returns A TanStack `UseMutationResult` whose `mutate` / `mutateAsync`\n * accepts a partial row to insert.\n *\n * @example\n * ```tsx\n * const insert = useInsert<Todo>('todos')\n * insert.mutate({ task: 'Buy milk', done: false })\n * ```\n */\nexport function useInsert<T = Record<string, unknown>>(\n table: string,\n): UseMutationResult<T, MimDBError, Partial<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (data: Partial<T>) => {\n const { data: result, error } = await client\n .from<T>(table)\n .insert(data)\n .select()\n .single()\n if (error) throw error\n return result as T\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * Input shape for the {@link useUpdate} mutation.\n *\n * @typeParam T - Expected row type.\n */\nexport interface UpdateInput<T> {\n /** Fields to update. */\n data: Partial<T>\n /** Equality filters identifying which rows to update. */\n eq: Record<string, string>\n}\n\n/**\n * React hook for updating rows in a MimDB table.\n *\n * The mutation function accepts an object with `data` (fields to set) and\n * `eq` (equality filters to target specific rows). On success, all\n * `useQuery` caches for the same table are invalidated.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @returns A TanStack `UseMutationResult`.\n *\n * @example\n * ```tsx\n * const update = useUpdate<Todo>('todos')\n * update.mutate({ data: { done: true }, eq: { id: '42' } })\n * ```\n */\nexport function useUpdate<T = Record<string, unknown>>(\n table: string,\n): UseMutationResult<T, MimDBError, UpdateInput<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async ({ data, eq }: UpdateInput<T>) => {\n let query = client.from<T>(table).update(data)\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { data: result, error } = await query.select().single()\n if (error) throw error\n return result as T\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * React hook for deleting rows from a MimDB table.\n *\n * The mutation function accepts equality filters identifying which rows\n * to delete. On success, all `useQuery` caches for the same table are\n * invalidated.\n *\n * @param table - Name of the database table.\n * @returns A TanStack `UseMutationResult` whose `mutate` accepts equality filters.\n *\n * @example\n * ```tsx\n * const del = useDelete('todos')\n * del.mutate({ id: '42' })\n * ```\n */\nexport function useDelete(\n table: string,\n): UseMutationResult<void, MimDBError, Record<string, string>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (eq: Record<string, string>) => {\n let query = client.from(table).delete()\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { error } = await query\n if (error) throw error\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport {\n MimDBRealtimeClient,\n type RealtimeEvent,\n type SubscriptionStatus,\n} from '@mimdb/realtime'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useRealtime} hook.\n *\n * @typeParam T - Expected row type for realtime events.\n */\nexport interface UseRealtimeOptions<T = Record<string, unknown>> {\n /** Event type filter. Defaults to `'*'` (all events). */\n event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE'\n /** Row filter expression (e.g. `'user_id=eq.42'`). */\n filter?: string\n /** Called for each matching realtime event. */\n onEvent?: (event: RealtimeEvent<T>) => void\n /**\n * Whether to automatically invalidate the table's TanStack Query cache\n * when an event is received. Defaults to `true`.\n */\n invalidateQueries?: boolean\n}\n\n/**\n * Subscribe to realtime database changes for a table via WebSocket.\n *\n * Creates a `MimDBRealtimeClient` using the connection config from\n * `MimDBClient.getConfig()` and subscribes to the specified table.\n * On each event, the table's TanStack Query cache is invalidated\n * (unless opted out) so queries refetch automatically.\n *\n * The subscription is cleaned up when the component unmounts or when\n * the `table`, `event`, or `filter` options change.\n *\n * @typeParam T - Expected row type for realtime events.\n * @param table - Database table to subscribe to.\n * @param options - Event filters and callbacks.\n * @returns An object containing the current subscription status.\n *\n * @example\n * ```tsx\n * const { status } = useRealtime<Message>('messages', {\n * event: 'INSERT',\n * onEvent: (e) => console.log('New message:', e.new),\n * })\n * ```\n */\nexport function useRealtime<T = Record<string, unknown>>(\n table: string,\n options?: UseRealtimeOptions<T>,\n): { status: SubscriptionStatus } {\n const client = useClient()\n const queryClient = useQueryClient()\n const [status, setStatus] = useState<SubscriptionStatus>('pending')\n const realtimeRef = useRef<MimDBRealtimeClient | null>(null)\n\n useEffect(() => {\n const config = client.getConfig()\n\n if (!realtimeRef.current) {\n realtimeRef.current = new MimDBRealtimeClient({\n url: config.url,\n projectRef: config.ref,\n apiKey: config.apiKey,\n })\n }\n\n const sub = realtimeRef.current.subscribe<T>(table, {\n event: options?.event ?? '*',\n filter: options?.filter,\n onEvent(event) {\n options?.onEvent?.(event)\n if (options?.invalidateQueries !== false) {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n }\n },\n onSubscribed() {\n setStatus('active')\n },\n onError() {\n setStatus('error')\n },\n })\n\n setStatus('pending')\n\n return () => {\n sub.unsubscribe()\n setStatus('closed')\n }\n }, [table, options?.event, options?.filter]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return { status }\n}\n","import { useState, useEffect, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { User } from '@mimdb/client'\n\n/**\n * Return type of the {@link useAuth} hook.\n */\nexport interface UseAuthResult {\n /** The currently authenticated user, or null if signed out. */\n user: User | null\n /** True while the initial session check is in progress. */\n isLoading: boolean\n /** Sign in with email and password. */\n signIn: (email: string, password: string) => Promise<void>\n /** Create a new account with email and password. */\n signUp: (email: string, password: string) => Promise<void>\n /** Sign out the current user. */\n signOut: () => Promise<void>\n /** Redirect to an OAuth provider's authorization page. */\n signInWithOAuth: (provider: string, opts: { redirectTo: string }) => void\n}\n\n/**\n * React hook for authentication state management.\n *\n * On mount, checks for an existing session and fetches the current user.\n * Subscribes to auth state changes so the returned `user` stays in sync\n * with sign-in, sign-out, and token refresh events.\n *\n * @returns An object with the current user, loading state, and auth methods.\n *\n * @example\n * ```tsx\n * function LoginPage() {\n * const { user, isLoading, signIn, signOut } = useAuth()\n *\n * if (isLoading) return <p>Loading...</p>\n * if (user) return <button onClick={signOut}>Sign Out</button>\n *\n * return (\n * <button onClick={() => signIn('user@example.com', 'password')}>\n * Sign In\n * </button>\n * )\n * }\n * ```\n */\nexport function useAuth(): UseAuthResult {\n const client = useClient()\n const [user, setUser] = useState<User | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n useEffect(() => {\n const session = client.auth.getSession()\n if (session) {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n .finally(() => setIsLoading(false))\n } else {\n setIsLoading(false)\n }\n\n const unsub = client.auth.onAuthStateChange((event, _session) => {\n if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n } else if (event === 'SIGNED_OUT') {\n setUser(null)\n }\n })\n\n return unsub\n }, [client])\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n await client.auth.signIn(email, password)\n },\n [client],\n )\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n await client.auth.signUp(email, password)\n },\n [client],\n )\n\n const signOut = useCallback(async () => {\n await client.auth.signOut()\n }, [client])\n\n const signInWithOAuth = useCallback(\n (provider: string, opts: { redirectTo: string }) => {\n const url = client.auth.signInWithOAuth(provider, opts)\n window.location.href = url\n },\n [client],\n )\n\n return { user, isLoading, signIn, signUp, signOut, signInWithOAuth }\n}\n","import { useState, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { UploadOptions } from '@mimdb/client'\n\n/**\n * Return type of the {@link useUpload} hook.\n */\nexport interface UseUploadResult {\n /** Upload a file to the bucket. Re-throws on failure after setting `error`. */\n upload: (path: string, file: Blob | File, opts?: UploadOptions) => Promise<void>\n /** True while an upload is in progress. */\n isUploading: boolean\n /** The error from the most recent failed upload, or null. */\n error: Error | null\n}\n\n/**\n * React hook for uploading files to a MimDB storage bucket.\n *\n * Tracks the upload's loading and error state so components can show\n * progress indicators or error messages without manual state management.\n *\n * @param bucket - Name of the storage bucket to upload to.\n * @returns An object with the `upload` function and status flags.\n *\n * @example\n * ```tsx\n * function AvatarUpload() {\n * const { upload, isUploading, error } = useUpload('avatars')\n *\n * const handleFile = (file: File) => {\n * upload(`users/${userId}/avatar.png`, file, { contentType: 'image/png' })\n * }\n *\n * return (\n * <>\n * <input type=\"file\" onChange={(e) => handleFile(e.target.files![0])} />\n * {isUploading && <p>Uploading...</p>}\n * {error && <p>Error: {error.message}</p>}\n * </>\n * )\n * }\n * ```\n */\nexport function useUpload(bucket: string): UseUploadResult {\n const client = useClient()\n const [isUploading, setIsUploading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const upload = useCallback(\n async (path: string, file: Blob | File, opts?: UploadOptions) => {\n setIsUploading(true)\n setError(null)\n try {\n await client.storage.from(bucket).upload(path, file, opts)\n } catch (err) {\n const uploadError =\n err instanceof Error ? err : new Error(String(err))\n setError(uploadError)\n throw uploadError\n } finally {\n setIsUploading(false)\n }\n },\n [client, bucket],\n )\n\n return { upload, isUploading, error }\n}\n"],"mappings":"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","names":["useQueryClient","useQueryClient","useState","useEffect","useState","useEffect","useState","useCallback","useState","useCallback"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/use-query.ts","../src/use-mutation.ts","../src/use-realtime.ts","../src/use-subscription.ts","../src/use-auth.ts","../src/use-upload.ts","../src/index.ts"],"sourcesContent":["import { createContext, useContext } from 'react'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * React context that holds the MimDB client instance.\n *\n * Consumers should use the {@link useClient} hook rather than accessing\n * this context directly.\n *\n * @internal\n */\nconst MimDBContext = createContext<MimDBClient | null>(null)\n\n/**\n * Retrieve the MimDB client from the nearest `<MimDBProvider>`.\n *\n * @returns The `MimDBClient` instance provided by the enclosing provider.\n * @throws If called outside of a `<MimDBProvider>` tree.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useClient()\n * // Use client.from(), client.auth, etc.\n * }\n * ```\n */\nexport function useClient(): MimDBClient {\n const client = useContext(MimDBContext)\n if (!client) {\n throw new Error('useClient must be used within <MimDBProvider>')\n }\n return client\n}\n\nexport { MimDBContext }\n","import { type ReactNode } from 'react'\nimport { MimDBContext } from './context'\nimport type { MimDBClient } from '@mimdb/client'\n\n/**\n * Props for the {@link MimDBProvider} component.\n */\nexport interface MimDBProviderProps {\n /** A configured `MimDBClient` instance to make available to child components. */\n client: MimDBClient\n /** The React subtree that will have access to the MimDB client. */\n children: ReactNode\n}\n\n/**\n * Context provider that makes a `MimDBClient` available to all descendant\n * components via the {@link useClient} hook.\n *\n * Wrap your application (or a subtree) with this provider and pass a\n * pre-configured client instance.\n *\n * @param props - Provider props containing the client and children.\n *\n * @example\n * ```tsx\n * import { createClient } from '@mimdb/client'\n * import { MimDBProvider } from '@mimdb/react'\n *\n * const client = createClient('https://api.mimdb.dev', 'ref', 'key')\n *\n * function App() {\n * return (\n * <MimDBProvider client={client}>\n * <MyApp />\n * </MimDBProvider>\n * )\n * }\n * ```\n */\nexport function MimDBProvider({ client, children }: MimDBProviderProps) {\n return <MimDBContext.Provider value={client}>{children}</MimDBContext.Provider>\n}\n","import {\n useQuery as useTanstackQuery,\n type UseQueryResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useQuery} hook.\n */\nexport interface UseQueryOptions {\n /** Column selection string (PostgREST format). Defaults to `'*'`. */\n select?: string\n /** Equality filters applied as `query.eq(column, value)`. */\n eq?: Record<string, string>\n /** Not-equal filters applied as `query.neq(column, value)`. */\n neq?: Record<string, string>\n /** Column ordering configuration. */\n order?: { column: string; ascending?: boolean }\n /** Maximum number of rows to return. */\n limit?: number\n /** Number of rows to skip before returning results. */\n offset?: number\n /** Whether the query should execute. Maps to TanStack Query's `enabled`. */\n enabled?: boolean\n /** Duration in ms before cached data is considered stale. */\n staleTime?: number\n /** Polling interval in ms, or `false` to disable. */\n refetchInterval?: number | false\n}\n\n/**\n * Fetch rows from a MimDB table using the REST API, backed by TanStack Query\n * for caching, deduplication, and background refetching.\n *\n * Automatically derives a stable query key from the table name and options\n * so cache invalidation works out of the box with the mutation hooks.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table to query.\n * @param options - Query filters, modifiers, and TanStack Query settings.\n * @returns A TanStack `UseQueryResult` containing the row array and status flags.\n *\n * @example\n * ```tsx\n * const { data, isLoading } = useQuery<Todo>('todos', {\n * eq: { done: 'false' },\n * order: { column: 'created_at', ascending: false },\n * limit: 20,\n * })\n * ```\n */\nexport function useQuery<T = Record<string, unknown>>(\n table: string,\n options?: UseQueryOptions,\n): UseQueryResult<T[], Error> {\n const client = useClient()\n\n return useTanstackQuery({\n queryKey: ['mimdb', table, options],\n queryFn: async () => {\n let query = client.from<T>(table).select(options?.select ?? '*')\n\n if (options?.eq) {\n for (const [col, val] of Object.entries(options.eq)) {\n query = query.eq(col, val)\n }\n }\n if (options?.neq) {\n for (const [col, val] of Object.entries(options.neq)) {\n query = query.neq(col, val)\n }\n }\n if (options?.order) {\n query = query.order(options.order.column, {\n ascending: options.order.ascending,\n })\n }\n if (options?.limit) query = query.limit(options.limit)\n if (options?.offset) query = query.offset(options.offset)\n\n const { data, error } = await query\n if (error) throw error\n return (data ?? []) as T[]\n },\n enabled: options?.enabled,\n staleTime: options?.staleTime,\n refetchInterval: options?.refetchInterval,\n })\n}\n","import {\n useMutation as useTanstackMutation,\n useQueryClient,\n type UseMutationResult,\n} from '@tanstack/react-query'\nimport { useClient } from './context'\nimport type { MimDBError } from '@mimdb/client'\n\n/**\n * Options for the {@link useInsert} hook.\n */\nexport interface UseInsertOptions {\n /**\n * Enable optimistic updates. When true, the new row is appended to\n * the query cache immediately and rolled back on error.\n */\n optimistic?: boolean\n}\n\n/**\n * React hook for inserting a row into a MimDB table.\n *\n * On success, all `useQuery` caches for the same table are automatically\n * invalidated so lists stay in sync.\n *\n * When `optimistic` is enabled, the new row is appended to the cache\n * before the server responds. On error the cache is rolled back.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @param options - Insert hook options.\n * @returns A TanStack `UseMutationResult` whose `mutate` / `mutateAsync`\n * accepts a partial row to insert.\n *\n * @example\n * ```tsx\n * const insert = useInsert<Todo>('todos', { optimistic: true })\n * insert.mutate({ task: 'Buy milk', done: false })\n * ```\n */\nexport function useInsert<T = Record<string, unknown>>(\n table: string,\n options?: UseInsertOptions,\n): UseMutationResult<T, MimDBError, Partial<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (data: Partial<T>) => {\n const { data: result, error } = await client\n .from<T>(table)\n .insert(data)\n .select()\n .single()\n if (error) throw error\n return result as T\n },\n onMutate: options?.optimistic\n ? async (newData) => {\n await queryClient.cancelQueries({ queryKey: ['mimdb', table] })\n const previous = queryClient.getQueryData<T[]>(['mimdb', table])\n queryClient.setQueryData<T[]>(['mimdb', table], (old) => [\n ...(old ?? []),\n newData as T,\n ])\n return { previous }\n }\n : undefined,\n onError: options?.optimistic\n ? (_err, _data, context: { previous?: T[] } | undefined) => {\n if (context?.previous !== undefined) {\n queryClient.setQueryData(['mimdb', table], context.previous)\n }\n }\n : undefined,\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * Input shape for the {@link useUpdate} mutation.\n *\n * @typeParam T - Expected row type.\n */\nexport interface UpdateInput<T> {\n /** Fields to update. */\n data: Partial<T>\n /** Equality filters identifying which rows to update. */\n eq: Record<string, string>\n}\n\n/**\n * Options for the {@link useUpdate} hook.\n */\nexport interface UseUpdateOptions {\n /**\n * Enable optimistic updates. When true, matching rows in the cache\n * are updated immediately and rolled back on error.\n */\n optimistic?: boolean\n}\n\n/**\n * React hook for updating rows in a MimDB table.\n *\n * The mutation function accepts an object with `data` (fields to set) and\n * `eq` (equality filters to target specific rows). On success, all\n * `useQuery` caches for the same table are invalidated.\n *\n * When `optimistic` is enabled, matching rows in the cache are updated\n * in-place before the server responds and rolled back on error.\n *\n * @typeParam T - Expected row type. Defaults to a generic record.\n * @param table - Name of the database table.\n * @param options - Update hook options.\n * @returns A TanStack `UseMutationResult`.\n *\n * @example\n * ```tsx\n * const update = useUpdate<Todo>('todos', { optimistic: true })\n * update.mutate({ data: { done: true }, eq: { id: '42' } })\n * ```\n */\nexport function useUpdate<T = Record<string, unknown>>(\n table: string,\n options?: UseUpdateOptions,\n): UseMutationResult<T, MimDBError, UpdateInput<T>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async ({ data, eq }: UpdateInput<T>) => {\n let query = client.from<T>(table).update(data)\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { data: result, error } = await query.select().single()\n if (error) throw error\n return result as T\n },\n onMutate: options?.optimistic\n ? async ({ data, eq }) => {\n await queryClient.cancelQueries({ queryKey: ['mimdb', table] })\n const previous = queryClient.getQueryData<T[]>(['mimdb', table])\n queryClient.setQueryData<T[]>(['mimdb', table], (old) =>\n (old ?? []).map((row) => {\n const record = row as Record<string, unknown>\n const matches = Object.entries(eq).every(\n ([col, val]) => String(record[col]) === val,\n )\n return matches ? { ...row, ...data } : row\n }),\n )\n return { previous }\n }\n : undefined,\n onError: options?.optimistic\n ? (_err, _data, context: { previous?: T[] } | undefined) => {\n if (context?.previous !== undefined) {\n queryClient.setQueryData(['mimdb', table], context.previous)\n }\n }\n : undefined,\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n\n/**\n * Options for the {@link useDelete} hook.\n */\nexport interface UseDeleteOptions {\n /**\n * Enable optimistic updates. When true, matching rows are removed\n * from the cache immediately and restored on error.\n */\n optimistic?: boolean\n}\n\n/**\n * React hook for deleting rows from a MimDB table.\n *\n * The mutation function accepts equality filters identifying which rows\n * to delete. On success, all `useQuery` caches for the same table are\n * invalidated.\n *\n * When `optimistic` is enabled, matching rows are removed from the cache\n * before the server responds and restored on error.\n *\n * @param table - Name of the database table.\n * @param options - Delete hook options.\n * @returns A TanStack `UseMutationResult` whose `mutate` accepts equality filters.\n *\n * @example\n * ```tsx\n * const del = useDelete('todos', { optimistic: true })\n * del.mutate({ id: '42' })\n * ```\n */\nexport function useDelete(\n table: string,\n options?: UseDeleteOptions,\n): UseMutationResult<void, MimDBError, Record<string, string>> {\n const client = useClient()\n const queryClient = useQueryClient()\n\n return useTanstackMutation({\n mutationFn: async (eq: Record<string, string>) => {\n let query = client.from(table).delete()\n for (const [col, val] of Object.entries(eq)) {\n query = query.eq(col, val)\n }\n const { error } = await query\n if (error) throw error\n },\n onMutate: options?.optimistic\n ? async (eq) => {\n await queryClient.cancelQueries({ queryKey: ['mimdb', table] })\n const previous = queryClient.getQueryData<Record<string, unknown>[]>(['mimdb', table])\n queryClient.setQueryData<Record<string, unknown>[]>(['mimdb', table], (old) =>\n (old ?? []).filter((row) =>\n !Object.entries(eq).every(\n ([col, val]) => String(row[col]) === val,\n ),\n ),\n )\n return { previous }\n }\n : undefined,\n onError: options?.optimistic\n ? (_err, _data, context: { previous?: Record<string, unknown>[] } | undefined) => {\n if (context?.previous !== undefined) {\n queryClient.setQueryData(['mimdb', table], context.previous)\n }\n }\n : undefined,\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n },\n })\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport {\n MimDBRealtimeClient,\n type RealtimeEvent,\n type SubscriptionStatus,\n} from '@mimdb/realtime'\nimport { useClient } from './context'\n\n/**\n * Options for the {@link useRealtime} hook.\n *\n * @typeParam T - Expected row type for realtime events.\n */\nexport interface UseRealtimeOptions<T = Record<string, unknown>> {\n /** Event type filter. Defaults to `'*'` (all events). */\n event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE'\n /** Row filter expression (e.g. `'user_id=eq.42'`). */\n filter?: string\n /** Called for each matching realtime event. */\n onEvent?: (event: RealtimeEvent<T>) => void\n /**\n * Whether to automatically invalidate the table's TanStack Query cache\n * when an event is received. Defaults to `true`.\n */\n invalidateQueries?: boolean\n}\n\n/**\n * Subscribe to realtime database changes for a table via WebSocket.\n *\n * Creates a `MimDBRealtimeClient` using the connection config from\n * `MimDBClient.getConfig()` and subscribes to the specified table.\n * On each event, the table's TanStack Query cache is invalidated\n * (unless opted out) so queries refetch automatically.\n *\n * The subscription is cleaned up when the component unmounts or when\n * the `table`, `event`, or `filter` options change.\n *\n * @typeParam T - Expected row type for realtime events.\n * @param table - Database table to subscribe to.\n * @param options - Event filters and callbacks.\n * @returns An object containing the current subscription status.\n *\n * @example\n * ```tsx\n * const { status } = useRealtime<Message>('messages', {\n * event: 'INSERT',\n * onEvent: (e) => console.log('New message:', e.new),\n * })\n * ```\n */\nexport function useRealtime<T = Record<string, unknown>>(\n table: string,\n options?: UseRealtimeOptions<T>,\n): { status: SubscriptionStatus } {\n const client = useClient()\n const queryClient = useQueryClient()\n const [status, setStatus] = useState<SubscriptionStatus>('pending')\n const realtimeRef = useRef<MimDBRealtimeClient | null>(null)\n\n useEffect(() => {\n // Skip WebSocket connections during SSR\n if (typeof window === 'undefined') return\n\n const config = client.getConfig()\n\n if (!realtimeRef.current) {\n realtimeRef.current = new MimDBRealtimeClient({\n url: config.url,\n projectRef: config.ref,\n apiKey: config.apiKey,\n })\n }\n\n const sub = realtimeRef.current.subscribe<T>(table, {\n event: options?.event ?? '*',\n filter: options?.filter,\n onEvent(event) {\n options?.onEvent?.(event)\n if (options?.invalidateQueries !== false) {\n queryClient.invalidateQueries({ queryKey: ['mimdb', table] })\n }\n },\n onSubscribed() {\n setStatus('active')\n },\n onError() {\n setStatus('error')\n },\n })\n\n setStatus('pending')\n\n return () => {\n sub.unsubscribe()\n setStatus('closed')\n }\n }, [table, options?.event, options?.filter]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return { status }\n}\n","import { useState, useEffect } from 'react'\nimport type { RealtimeEvent, SubscriptionStatus } from '@mimdb/realtime'\nimport { useClient } from './context'\n\n/**\n * Return type of the {@link useSubscription} hook.\n *\n * @typeParam T - Expected row type for realtime events.\n */\nexport interface UseSubscriptionResult<T> {\n /** The most recently received realtime event, or null if none yet. */\n lastEvent: RealtimeEvent<T> | null\n /** Current subscription lifecycle status. */\n status: SubscriptionStatus\n}\n\n/**\n * Subscribe to a table and maintain the latest event in React state.\n *\n * Unlike {@link useRealtime} (which fires callbacks), this hook returns\n * the most recent event directly as component state, making it ideal\n * for rendering the latest change inline.\n *\n * Uses `client.realtime` (the lazy realtime accessor on MimDBClient)\n * so no separate realtime client setup is needed.\n *\n * The subscription is cleaned up when the component unmounts or when\n * `table`, `event`, or `filter` change.\n *\n * @typeParam T - Expected row type for realtime events.\n * @param table - Database table to subscribe to.\n * @param options - Event type and filter configuration.\n * @returns The latest event and subscription status.\n *\n * @example\n * ```tsx\n * const { lastEvent, status } = useSubscription<Message>('messages', {\n * event: 'INSERT',\n * })\n *\n * if (lastEvent) {\n * console.log('Latest insert:', lastEvent.new)\n * }\n * ```\n */\nexport function useSubscription<T = Record<string, unknown>>(\n table: string,\n options?: {\n /** Event type filter. Defaults to `'*'` (all events). */\n event?: '*' | 'INSERT' | 'UPDATE' | 'DELETE'\n /** Row filter expression (e.g. `'user_id=eq.42'`). */\n filter?: string\n },\n): UseSubscriptionResult<T> {\n const client = useClient()\n const [lastEvent, setLastEvent] = useState<RealtimeEvent<T> | null>(null)\n const [status, setStatus] = useState<SubscriptionStatus>('pending')\n\n useEffect(() => {\n // Skip WebSocket connections during SSR\n if (typeof window === 'undefined') return\n\n const sub = client.realtime.subscribe<T>(table, {\n event: options?.event ?? '*',\n filter: options?.filter,\n onEvent(event) {\n setLastEvent(event)\n },\n onSubscribed() {\n setStatus('active')\n },\n onError() {\n setStatus('error')\n },\n })\n\n setStatus('pending')\n\n return () => {\n sub.unsubscribe()\n setStatus('closed')\n }\n }, [table, options?.event, options?.filter]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return { lastEvent, status }\n}\n","import { useState, useEffect, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { User } from '@mimdb/client'\n\n/**\n * Return type of the {@link useAuth} hook.\n */\nexport interface UseAuthResult {\n /** The currently authenticated user, or null if signed out. */\n user: User | null\n /** True while the initial session check is in progress. */\n isLoading: boolean\n /** Sign in with email and password. */\n signIn: (email: string, password: string) => Promise<void>\n /** Create a new account with email and password. */\n signUp: (email: string, password: string) => Promise<void>\n /** Sign out the current user. */\n signOut: () => Promise<void>\n /** Redirect to an OAuth provider's authorization page. */\n signInWithOAuth: (provider: string, opts: { redirectTo: string }) => void\n}\n\n/**\n * React hook for authentication state management.\n *\n * On mount, checks for an existing session and fetches the current user.\n * Subscribes to auth state changes so the returned `user` stays in sync\n * with sign-in, sign-out, and token refresh events.\n *\n * @returns An object with the current user, loading state, and auth methods.\n *\n * @example\n * ```tsx\n * function LoginPage() {\n * const { user, isLoading, signIn, signOut } = useAuth()\n *\n * if (isLoading) return <p>Loading...</p>\n * if (user) return <button onClick={signOut}>Sign Out</button>\n *\n * return (\n * <button onClick={() => signIn('user@example.com', 'password')}>\n * Sign In\n * </button>\n * )\n * }\n * ```\n */\nexport function useAuth(): UseAuthResult {\n const client = useClient()\n const [user, setUser] = useState<User | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n useEffect(() => {\n const session = client.auth.getSession()\n if (session) {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n .finally(() => setIsLoading(false))\n } else {\n setIsLoading(false)\n }\n\n const unsub = client.auth.onAuthStateChange((event, _session) => {\n if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {\n client.auth\n .getUser()\n .then(setUser)\n .catch(() => setUser(null))\n } else if (event === 'SIGNED_OUT' || event === 'TOKEN_REFRESH_FAILED') {\n setUser(null)\n }\n })\n\n return unsub\n }, [client])\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n await client.auth.signIn(email, password)\n },\n [client],\n )\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n await client.auth.signUp(email, password)\n },\n [client],\n )\n\n const signOut = useCallback(async () => {\n await client.auth.signOut()\n }, [client])\n\n const signInWithOAuth = useCallback(\n (provider: string, opts: { redirectTo: string }) => {\n const url = client.auth.signInWithOAuth(provider, opts)\n if (typeof window !== 'undefined') {\n window.location.href = url\n }\n },\n [client],\n )\n\n return { user, isLoading, signIn, signUp, signOut, signInWithOAuth }\n}\n","import { useState, useCallback } from 'react'\nimport { useClient } from './context'\nimport type { UploadOptions } from '@mimdb/client'\n\n/**\n * Return type of the {@link useUpload} hook.\n */\nexport interface UseUploadResult {\n /** Upload a file to the bucket. Re-throws on failure after setting `error`. */\n upload: (path: string, file: Blob | File, opts?: UploadOptions) => Promise<void>\n /** True while an upload is in progress. */\n isUploading: boolean\n /** The error from the most recent failed upload, or null. */\n error: Error | null\n}\n\n/**\n * React hook for uploading files to a MimDB storage bucket.\n *\n * Tracks the upload's loading and error state so components can show\n * progress indicators or error messages without manual state management.\n *\n * @param bucket - Name of the storage bucket to upload to.\n * @returns An object with the `upload` function and status flags.\n *\n * @example\n * ```tsx\n * function AvatarUpload() {\n * const { upload, isUploading, error } = useUpload('avatars')\n *\n * const handleFile = (file: File) => {\n * upload(`users/${userId}/avatar.png`, file, { contentType: 'image/png' })\n * }\n *\n * return (\n * <>\n * <input type=\"file\" onChange={(e) => handleFile(e.target.files![0])} />\n * {isUploading && <p>Uploading...</p>}\n * {error && <p>Error: {error.message}</p>}\n * </>\n * )\n * }\n * ```\n */\nexport function useUpload(bucket: string): UseUploadResult {\n const client = useClient()\n const [isUploading, setIsUploading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const upload = useCallback(\n async (path: string, file: Blob | File, opts?: UploadOptions) => {\n setIsUploading(true)\n setError(null)\n try {\n await client.storage.from(bucket).upload(path, file, opts)\n } catch (err) {\n const uploadError =\n err instanceof Error ? err : new Error(String(err))\n setError(uploadError)\n throw uploadError\n } finally {\n setIsUploading(false)\n }\n },\n [client, bucket],\n )\n\n return { upload, isUploading, error }\n}\n","export { MimDBProvider, type MimDBProviderProps } from './provider'\nexport { useClient } from './context'\nexport { useQuery, type UseQueryOptions } from './use-query'\nexport {\n useInsert,\n useUpdate,\n useDelete,\n type UpdateInput,\n type UseInsertOptions,\n type UseUpdateOptions,\n type UseDeleteOptions,\n} from './use-mutation'\nexport { useRealtime, type UseRealtimeOptions } from './use-realtime'\nexport { useSubscription, type UseSubscriptionResult } from './use-subscription'\nexport { useAuth, type UseAuthResult } from './use-auth'\nexport { useUpload, type UseUploadResult } from './use-upload'\n\n// Re-export createServerClient for convenience\nexport { createServerClient } from '@mimdb/client'\n"],"mappings":"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","names":["useQueryClient","useQueryClient","useState","useEffect","useState","useEffect","useState","useEffect","useState","useEffect","useState","useCallback","useState","useCallback"]}
|