@mimdb/react 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,28 @@ function useInsert(table) {
95
97
  if (error) throw error;
96
98
  return result;
97
99
  },
98
- onSuccess: () => {
100
+ onMutate: options?.optimistic ? async (newData) => {
101
+ await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
102
+ const previous = queryClient.getQueriesData({ queryKey: ["mimdb", table] });
103
+ queryClient.setQueriesData({ queryKey: ["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) {
111
+ for (const [key, data] of context.previous) {
112
+ queryClient.setQueryData(key, data);
113
+ }
114
+ }
115
+ } : void 0,
116
+ onSettled: () => {
99
117
  queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
100
118
  }
101
119
  });
102
120
  }
103
- function useUpdate(table) {
121
+ function useUpdate(table, options) {
104
122
  const client = useClient();
105
123
  const queryClient = (0, import_react_query2.useQueryClient)();
106
124
  return (0, import_react_query2.useMutation)({
@@ -113,12 +131,34 @@ function useUpdate(table) {
113
131
  if (error) throw error;
114
132
  return result;
115
133
  },
116
- onSuccess: () => {
134
+ onMutate: options?.optimistic ? async ({ data, eq }) => {
135
+ await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
136
+ const previous = queryClient.getQueriesData({ queryKey: ["mimdb", table] });
137
+ queryClient.setQueriesData(
138
+ { queryKey: ["mimdb", table] },
139
+ (old) => (old ?? []).map((row) => {
140
+ const record = row;
141
+ const matches = Object.entries(eq).every(
142
+ ([col, val]) => String(record[col]) === val
143
+ );
144
+ return matches ? { ...row, ...data } : row;
145
+ })
146
+ );
147
+ return { previous };
148
+ } : void 0,
149
+ onError: options?.optimistic ? (_err, _data, context) => {
150
+ if (context?.previous) {
151
+ for (const [key, data] of context.previous) {
152
+ queryClient.setQueryData(key, data);
153
+ }
154
+ }
155
+ } : void 0,
156
+ onSettled: () => {
117
157
  queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
118
158
  }
119
159
  });
120
160
  }
121
- function useDelete(table) {
161
+ function useDelete(table, options) {
122
162
  const client = useClient();
123
163
  const queryClient = (0, import_react_query2.useQueryClient)();
124
164
  return (0, import_react_query2.useMutation)({
@@ -130,7 +170,27 @@ function useDelete(table) {
130
170
  const { error } = await query;
131
171
  if (error) throw error;
132
172
  },
133
- onSuccess: () => {
173
+ onMutate: options?.optimistic ? async (eq) => {
174
+ await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
175
+ const previous = queryClient.getQueriesData({ queryKey: ["mimdb", table] });
176
+ queryClient.setQueriesData(
177
+ { queryKey: ["mimdb", table] },
178
+ (old) => (old ?? []).filter(
179
+ (row) => !Object.entries(eq).every(
180
+ ([col, val]) => String(row[col]) === val
181
+ )
182
+ )
183
+ );
184
+ return { previous };
185
+ } : void 0,
186
+ onError: options?.optimistic ? (_err, _data, context) => {
187
+ if (context?.previous) {
188
+ for (const [key, data] of context.previous) {
189
+ queryClient.setQueryData(key, data);
190
+ }
191
+ }
192
+ } : void 0,
193
+ onSettled: () => {
134
194
  queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
135
195
  }
136
196
  });
@@ -146,6 +206,7 @@ function useRealtime(table, options) {
146
206
  const [status, setStatus] = (0, import_react2.useState)("pending");
147
207
  const realtimeRef = (0, import_react2.useRef)(null);
148
208
  (0, import_react2.useEffect)(() => {
209
+ if (typeof window === "undefined") return;
149
210
  const config = client.getConfig();
150
211
  if (!realtimeRef.current) {
151
212
  realtimeRef.current = new import_realtime.MimDBRealtimeClient({
@@ -179,13 +240,43 @@ function useRealtime(table, options) {
179
240
  return { status };
180
241
  }
181
242
 
182
- // src/use-auth.ts
243
+ // src/use-subscription.ts
183
244
  var import_react3 = require("react");
184
- function useAuth() {
245
+ function useSubscription(table, options) {
185
246
  const client = useClient();
186
- const [user, setUser] = (0, import_react3.useState)(null);
187
- const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
247
+ const [lastEvent, setLastEvent] = (0, import_react3.useState)(null);
248
+ const [status, setStatus] = (0, import_react3.useState)("pending");
188
249
  (0, import_react3.useEffect)(() => {
250
+ if (typeof window === "undefined") return;
251
+ const sub = client.realtime.subscribe(table, {
252
+ event: options?.event ?? "*",
253
+ filter: options?.filter,
254
+ onEvent(event) {
255
+ setLastEvent(event);
256
+ },
257
+ onSubscribed() {
258
+ setStatus("active");
259
+ },
260
+ onError() {
261
+ setStatus("error");
262
+ }
263
+ });
264
+ setStatus("pending");
265
+ return () => {
266
+ sub.unsubscribe();
267
+ setStatus("closed");
268
+ };
269
+ }, [table, options?.event, options?.filter]);
270
+ return { lastEvent, status };
271
+ }
272
+
273
+ // src/use-auth.ts
274
+ var import_react4 = require("react");
275
+ function useAuth() {
276
+ const client = useClient();
277
+ const [user, setUser] = (0, import_react4.useState)(null);
278
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
279
+ (0, import_react4.useEffect)(() => {
189
280
  const session = client.auth.getSession();
190
281
  if (session) {
191
282
  client.auth.getUser().then(setUser).catch(() => setUser(null)).finally(() => setIsLoading(false));
@@ -195,31 +286,33 @@ function useAuth() {
195
286
  const unsub = client.auth.onAuthStateChange((event, _session) => {
196
287
  if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
197
288
  client.auth.getUser().then(setUser).catch(() => setUser(null));
198
- } else if (event === "SIGNED_OUT") {
289
+ } else if (event === "SIGNED_OUT" || event === "TOKEN_REFRESH_FAILED") {
199
290
  setUser(null);
200
291
  }
201
292
  });
202
293
  return unsub;
203
294
  }, [client]);
204
- const signIn = (0, import_react3.useCallback)(
295
+ const signIn = (0, import_react4.useCallback)(
205
296
  async (email, password) => {
206
297
  await client.auth.signIn(email, password);
207
298
  },
208
299
  [client]
209
300
  );
210
- const signUp = (0, import_react3.useCallback)(
301
+ const signUp = (0, import_react4.useCallback)(
211
302
  async (email, password) => {
212
303
  await client.auth.signUp(email, password);
213
304
  },
214
305
  [client]
215
306
  );
216
- const signOut = (0, import_react3.useCallback)(async () => {
307
+ const signOut = (0, import_react4.useCallback)(async () => {
217
308
  await client.auth.signOut();
218
309
  }, [client]);
219
- const signInWithOAuth = (0, import_react3.useCallback)(
310
+ const signInWithOAuth = (0, import_react4.useCallback)(
220
311
  (provider, opts) => {
221
312
  const url = client.auth.signInWithOAuth(provider, opts);
222
- window.location.href = url;
313
+ if (typeof window !== "undefined") {
314
+ window.location.href = url;
315
+ }
223
316
  },
224
317
  [client]
225
318
  );
@@ -227,12 +320,12 @@ function useAuth() {
227
320
  }
228
321
 
229
322
  // src/use-upload.ts
230
- var import_react4 = require("react");
323
+ var import_react5 = require("react");
231
324
  function useUpload(bucket) {
232
325
  const client = useClient();
233
- const [isUploading, setIsUploading] = (0, import_react4.useState)(false);
234
- const [error, setError] = (0, import_react4.useState)(null);
235
- const upload = (0, import_react4.useCallback)(
326
+ const [isUploading, setIsUploading] = (0, import_react5.useState)(false);
327
+ const [error, setError] = (0, import_react5.useState)(null);
328
+ const upload = (0, import_react5.useCallback)(
236
329
  async (path, file, opts) => {
237
330
  setIsUploading(true);
238
331
  setError(null);
@@ -250,15 +343,20 @@ function useUpload(bucket) {
250
343
  );
251
344
  return { upload, isUploading, error };
252
345
  }
346
+
347
+ // src/index.ts
348
+ var import_client = require("@mimdb/client");
253
349
  // Annotate the CommonJS export names for ESM import in node:
254
350
  0 && (module.exports = {
255
351
  MimDBProvider,
352
+ createServerClient,
256
353
  useAuth,
257
354
  useClient,
258
355
  useDelete,
259
356
  useInsert,
260
357
  useQuery,
261
358
  useRealtime,
359
+ useSubscription,
262
360
  useUpdate,
263
361
  useUpload
264
362
  });
@@ -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":"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","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.getQueriesData<T[]>({ queryKey: ['mimdb', table] })\n queryClient.setQueriesData<T[]>({ queryKey: ['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?: [readonly unknown[], T[] | undefined][] } | undefined) => {\n if (context?.previous) {\n for (const [key, data] of context.previous) {\n queryClient.setQueryData(key as unknown[], data)\n }\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.getQueriesData<T[]>({ queryKey: ['mimdb', table] })\n queryClient.setQueriesData<T[]>({ queryKey: ['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?: [readonly unknown[], T[] | undefined][] } | undefined) => {\n if (context?.previous) {\n for (const [key, data] of context.previous) {\n queryClient.setQueryData(key as unknown[], data)\n }\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.getQueriesData<Record<string, unknown>[]>({ queryKey: ['mimdb', table] })\n queryClient.setQueriesData<Record<string, unknown>[]>({ queryKey: ['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?: [readonly unknown[], Record<string, unknown>[] | undefined][] } | undefined) => {\n if (context?.previous) {\n for (const [key, data] of context.previous) {\n queryClient.setQueryData(key as unknown[], data)\n }\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":"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","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 - Name of the database 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 - Name of the database 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
- * @param table - Name of the database table.
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 - Name of the database 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 - Name of the database 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
- * @param table - Name of the database table.
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,28 @@ function useInsert(table) {
66
66
  if (error) throw error;
67
67
  return result;
68
68
  },
69
- onSuccess: () => {
69
+ onMutate: options?.optimistic ? async (newData) => {
70
+ await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
71
+ const previous = queryClient.getQueriesData({ queryKey: ["mimdb", table] });
72
+ queryClient.setQueriesData({ queryKey: ["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) {
80
+ for (const [key, data] of context.previous) {
81
+ queryClient.setQueryData(key, data);
82
+ }
83
+ }
84
+ } : void 0,
85
+ onSettled: () => {
70
86
  queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
71
87
  }
72
88
  });
73
89
  }
74
- function useUpdate(table) {
90
+ function useUpdate(table, options) {
75
91
  const client = useClient();
76
92
  const queryClient = useQueryClient();
77
93
  return useTanstackMutation({
@@ -84,12 +100,34 @@ function useUpdate(table) {
84
100
  if (error) throw error;
85
101
  return result;
86
102
  },
87
- onSuccess: () => {
103
+ onMutate: options?.optimistic ? async ({ data, eq }) => {
104
+ await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
105
+ const previous = queryClient.getQueriesData({ queryKey: ["mimdb", table] });
106
+ queryClient.setQueriesData(
107
+ { queryKey: ["mimdb", table] },
108
+ (old) => (old ?? []).map((row) => {
109
+ const record = row;
110
+ const matches = Object.entries(eq).every(
111
+ ([col, val]) => String(record[col]) === val
112
+ );
113
+ return matches ? { ...row, ...data } : row;
114
+ })
115
+ );
116
+ return { previous };
117
+ } : void 0,
118
+ onError: options?.optimistic ? (_err, _data, context) => {
119
+ if (context?.previous) {
120
+ for (const [key, data] of context.previous) {
121
+ queryClient.setQueryData(key, data);
122
+ }
123
+ }
124
+ } : void 0,
125
+ onSettled: () => {
88
126
  queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
89
127
  }
90
128
  });
91
129
  }
92
- function useDelete(table) {
130
+ function useDelete(table, options) {
93
131
  const client = useClient();
94
132
  const queryClient = useQueryClient();
95
133
  return useTanstackMutation({
@@ -101,7 +139,27 @@ function useDelete(table) {
101
139
  const { error } = await query;
102
140
  if (error) throw error;
103
141
  },
104
- onSuccess: () => {
142
+ onMutate: options?.optimistic ? async (eq) => {
143
+ await queryClient.cancelQueries({ queryKey: ["mimdb", table] });
144
+ const previous = queryClient.getQueriesData({ queryKey: ["mimdb", table] });
145
+ queryClient.setQueriesData(
146
+ { queryKey: ["mimdb", table] },
147
+ (old) => (old ?? []).filter(
148
+ (row) => !Object.entries(eq).every(
149
+ ([col, val]) => String(row[col]) === val
150
+ )
151
+ )
152
+ );
153
+ return { previous };
154
+ } : void 0,
155
+ onError: options?.optimistic ? (_err, _data, context) => {
156
+ if (context?.previous) {
157
+ for (const [key, data] of context.previous) {
158
+ queryClient.setQueryData(key, data);
159
+ }
160
+ }
161
+ } : void 0,
162
+ onSettled: () => {
105
163
  queryClient.invalidateQueries({ queryKey: ["mimdb", table] });
106
164
  }
107
165
  });
@@ -119,6 +177,7 @@ function useRealtime(table, options) {
119
177
  const [status, setStatus] = useState("pending");
120
178
  const realtimeRef = useRef(null);
121
179
  useEffect(() => {
180
+ if (typeof window === "undefined") return;
122
181
  const config = client.getConfig();
123
182
  if (!realtimeRef.current) {
124
183
  realtimeRef.current = new MimDBRealtimeClient({
@@ -152,13 +211,43 @@ function useRealtime(table, options) {
152
211
  return { status };
153
212
  }
154
213
 
214
+ // src/use-subscription.ts
215
+ import { useState as useState2, useEffect as useEffect2 } from "react";
216
+ function useSubscription(table, options) {
217
+ const client = useClient();
218
+ const [lastEvent, setLastEvent] = useState2(null);
219
+ const [status, setStatus] = useState2("pending");
220
+ useEffect2(() => {
221
+ if (typeof window === "undefined") return;
222
+ const sub = client.realtime.subscribe(table, {
223
+ event: options?.event ?? "*",
224
+ filter: options?.filter,
225
+ onEvent(event) {
226
+ setLastEvent(event);
227
+ },
228
+ onSubscribed() {
229
+ setStatus("active");
230
+ },
231
+ onError() {
232
+ setStatus("error");
233
+ }
234
+ });
235
+ setStatus("pending");
236
+ return () => {
237
+ sub.unsubscribe();
238
+ setStatus("closed");
239
+ };
240
+ }, [table, options?.event, options?.filter]);
241
+ return { lastEvent, status };
242
+ }
243
+
155
244
  // src/use-auth.ts
156
- import { useState as useState2, useEffect as useEffect2, useCallback } from "react";
245
+ import { useState as useState3, useEffect as useEffect3, useCallback } from "react";
157
246
  function useAuth() {
158
247
  const client = useClient();
159
- const [user, setUser] = useState2(null);
160
- const [isLoading, setIsLoading] = useState2(true);
161
- useEffect2(() => {
248
+ const [user, setUser] = useState3(null);
249
+ const [isLoading, setIsLoading] = useState3(true);
250
+ useEffect3(() => {
162
251
  const session = client.auth.getSession();
163
252
  if (session) {
164
253
  client.auth.getUser().then(setUser).catch(() => setUser(null)).finally(() => setIsLoading(false));
@@ -168,7 +257,7 @@ function useAuth() {
168
257
  const unsub = client.auth.onAuthStateChange((event, _session) => {
169
258
  if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
170
259
  client.auth.getUser().then(setUser).catch(() => setUser(null));
171
- } else if (event === "SIGNED_OUT") {
260
+ } else if (event === "SIGNED_OUT" || event === "TOKEN_REFRESH_FAILED") {
172
261
  setUser(null);
173
262
  }
174
263
  });
@@ -192,7 +281,9 @@ function useAuth() {
192
281
  const signInWithOAuth = useCallback(
193
282
  (provider, opts) => {
194
283
  const url = client.auth.signInWithOAuth(provider, opts);
195
- window.location.href = url;
284
+ if (typeof window !== "undefined") {
285
+ window.location.href = url;
286
+ }
196
287
  },
197
288
  [client]
198
289
  );
@@ -200,11 +291,11 @@ function useAuth() {
200
291
  }
201
292
 
202
293
  // src/use-upload.ts
203
- import { useState as useState3, useCallback as useCallback2 } from "react";
294
+ import { useState as useState4, useCallback as useCallback2 } from "react";
204
295
  function useUpload(bucket) {
205
296
  const client = useClient();
206
- const [isUploading, setIsUploading] = useState3(false);
207
- const [error, setError] = useState3(null);
297
+ const [isUploading, setIsUploading] = useState4(false);
298
+ const [error, setError] = useState4(null);
208
299
  const upload = useCallback2(
209
300
  async (path, file, opts) => {
210
301
  setIsUploading(true);
@@ -223,14 +314,19 @@ function useUpload(bucket) {
223
314
  );
224
315
  return { upload, isUploading, error };
225
316
  }
317
+
318
+ // src/index.ts
319
+ import { createServerClient } from "@mimdb/client";
226
320
  export {
227
321
  MimDBProvider,
322
+ createServerClient,
228
323
  useAuth,
229
324
  useClient,
230
325
  useDelete,
231
326
  useInsert,
232
327
  useQuery,
233
328
  useRealtime,
329
+ useSubscription,
234
330
  useUpdate,
235
331
  useUpload
236
332
  };
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":";AAAA,SAAS,eAAe,kBAAkB;AAW1C,IAAM,eAAe,cAAkC,IAAI;AAgBpD,SAAS,YAAyB;AACvC,QAAM,SAAS,WAAW,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,oBAAC,aAAa,UAAb,EAAsB,OAAO,QAAS,UAAS;AACzD;;;ACzCA;AAAA,EACE,YAAY;AAAA,OAEP;AAgDA,SAAS,SACd,OACA,SAC4B;AAC5B,QAAM,SAAS,UAAU;AAEzB,SAAO,iBAAiB;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;AAAA,EACE,eAAe;AAAA,EACf;AAAA,OAEK;AAqBA,SAAS,UACd,OAC8C;AAC9C,QAAM,SAAS,UAAU;AACzB,QAAM,cAAc,eAAe;AAEnC,SAAO,oBAAoB;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,cAAc,eAAe;AAEnC,SAAO,oBAAoB;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,cAAc,eAAe;AAEnC,SAAO,oBAAoB;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,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,kBAAAA,uBAAsB;AAC/B;AAAA,EACE;AAAA,OAGK;AA8CA,SAAS,YACd,OACA,SACgC;AAChC,QAAM,SAAS,UAAU;AACzB,QAAM,cAAcC,gBAAe;AACnC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA6B,SAAS;AAClE,QAAM,cAAc,OAAmC,IAAI;AAE3D,YAAU,MAAM;AACd,UAAM,SAAS,OAAO,UAAU;AAEhC,QAAI,CAAC,YAAY,SAAS;AACxB,kBAAY,UAAU,IAAI,oBAAoB;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,SAAS,YAAAC,WAAU,aAAAC,YAAW,mBAAmB;AA+C1C,SAAS,UAAyB;AACvC,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAsB,IAAI;AAClD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,IAAI;AAE/C,EAAAC,WAAU,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,SAAS;AAAA,IACb,OAAO,OAAe,aAAqB;AACzC,YAAM,OAAO,KAAK,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,SAAS;AAAA,IACb,OAAO,OAAe,aAAqB;AACzC,YAAM,OAAO,KAAK,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,UAAU,YAAY,YAAY;AACtC,UAAM,OAAO,KAAK,QAAQ;AAAA,EAC5B,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,kBAAkB;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,SAAS,YAAAC,WAAU,eAAAC,oBAAmB;AA4C/B,SAAS,UAAU,QAAiC;AACzD,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAErD,QAAM,SAASC;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":["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.getQueriesData<T[]>({ queryKey: ['mimdb', table] })\n queryClient.setQueriesData<T[]>({ queryKey: ['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?: [readonly unknown[], T[] | undefined][] } | undefined) => {\n if (context?.previous) {\n for (const [key, data] of context.previous) {\n queryClient.setQueryData(key as unknown[], data)\n }\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.getQueriesData<T[]>({ queryKey: ['mimdb', table] })\n queryClient.setQueriesData<T[]>({ queryKey: ['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?: [readonly unknown[], T[] | undefined][] } | undefined) => {\n if (context?.previous) {\n for (const [key, data] of context.previous) {\n queryClient.setQueryData(key as unknown[], data)\n }\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.getQueriesData<Record<string, unknown>[]>({ queryKey: ['mimdb', table] })\n queryClient.setQueriesData<Record<string, unknown>[]>({ queryKey: ['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?: [readonly unknown[], Record<string, unknown>[] | undefined][] } | undefined) => {\n if (context?.previous) {\n for (const [key, data] of context.previous) {\n queryClient.setQueryData(key as unknown[], data)\n }\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mimdb/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "React hooks for MimDB",
5
5
  "type": "module",
6
6
  "exports": {
@@ -23,7 +23,8 @@
23
23
  "peerDependencies": {
24
24
  "react": ">=18.0.0",
25
25
  "@tanstack/react-query": ">=5.0.0",
26
- "@mimdb/client": ">=0.1.0"
26
+ "@mimdb/client": ">=0.2.0",
27
+ "@mimdb/realtime": ">=0.1.1"
27
28
  },
28
29
  "devDependencies": {
29
30
  "tsup": "^8.5.1",