better-auth-instantdb 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/adapter/create-schema.d.ts +9 -0
  4. package/dist/adapter/create-schema.js +178 -0
  5. package/dist/adapter/instant-adapter.d.ts +25 -0
  6. package/dist/adapter/instant-adapter.js +273 -0
  7. package/dist/client-plugin.d.ts +2143 -0
  8. package/dist/client-plugin.js +21 -0
  9. package/dist/create-schema.d.ts +25 -0
  10. package/dist/create-schema.js +115 -0
  11. package/dist/create-schema.js.map +1 -0
  12. package/dist/index.d.mts +18 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +2 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/index.mjs +160 -0
  17. package/dist/instant-adapter.d.ts +26 -0
  18. package/dist/instant-adapter.js +214 -0
  19. package/dist/instant-adapter.js.map +1 -0
  20. package/dist/instant-auth.d.ts +3 -0
  21. package/dist/instant-auth.js +9 -0
  22. package/dist/lib/instant-auth.d.ts +3 -0
  23. package/dist/lib/instant-auth.js +9 -0
  24. package/dist/lib/utils.d.ts +12 -0
  25. package/dist/lib/utils.js +22 -0
  26. package/dist/metafile-cjs.json +1 -0
  27. package/dist/metafile-esm.json +1 -0
  28. package/dist/react/client-plugin.d.ts +2143 -0
  29. package/dist/react/client-plugin.js +21 -0
  30. package/dist/react/index.d.ts +4 -0
  31. package/dist/react/index.js +4 -0
  32. package/dist/react/instant-auth.d.ts +7 -0
  33. package/dist/react/instant-auth.js +5 -0
  34. package/dist/react/react.d.ts +2 -0
  35. package/dist/react/react.js +2 -0
  36. package/dist/react/types.d.ts +6 -0
  37. package/dist/react/types.js +1 -0
  38. package/dist/react/use-hydrated.d.ts +1 -0
  39. package/dist/react/use-hydrated.js +7 -0
  40. package/dist/react/use-instant-auth.d.ts +8 -0
  41. package/dist/react/use-instant-auth.js +13 -0
  42. package/dist/react/use-instant-session.d.ts +32 -0
  43. package/dist/react/use-instant-session.js +25 -0
  44. package/dist/react/use-persistent-session.d.ts +27 -0
  45. package/dist/react/use-persistent-session.js +49 -0
  46. package/dist/react/use-session.d.ts +0 -0
  47. package/dist/react/use-session.js +1 -0
  48. package/dist/react/with-instant.d.ts +3 -0
  49. package/dist/react/with-instant.js +47 -0
  50. package/dist/react.d.ts +2 -0
  51. package/dist/react.js +2 -0
  52. package/dist/shared/instant-auth.d.ts +4 -0
  53. package/dist/shared/instant-auth.js +9 -0
  54. package/dist/utils.d.ts +6 -0
  55. package/dist/utils.js +9 -0
  56. package/package.json +70 -0
  57. package/src/adapter/create-schema.ts +232 -0
  58. package/src/adapter/instant-adapter.ts +422 -0
  59. package/src/index.ts +2 -0
  60. package/src/lib/utils.ts +24 -0
  61. package/src/react/index.ts +4 -0
  62. package/src/react/instant-auth.tsx +17 -0
  63. package/src/react/types.ts +9 -0
  64. package/src/react/use-hydrated.ts +13 -0
  65. package/src/react/use-instant-auth.ts +28 -0
  66. package/src/react/use-instant-session.ts +46 -0
  67. package/src/react/use-persistent-session.ts +64 -0
  68. package/src/shared/instant-auth.ts +18 -0
@@ -0,0 +1,422 @@
1
+ import type { BetterAuthDBSchema } from "@better-auth/core/db"
2
+ import {
3
+ type InstantAdminDatabase,
4
+ type InstaQLParams,
5
+ id
6
+ } from "@instantdb/admin"
7
+ import {
8
+ createAdapterFactory,
9
+ type DBAdapterDebugLogOption,
10
+ type Where
11
+ } from "better-auth/adapters"
12
+
13
+ import { fieldNameToLabel, prettyObject } from "../lib/utils"
14
+ import { createSchema } from "./create-schema"
15
+
16
+ type Direction = "asc" | "desc"
17
+ type Order = { [key: string]: Direction }
18
+
19
+ /**
20
+ * Gets the InstantDB entity name for a given model name
21
+ */
22
+ function getEntityName(
23
+ modelName: string,
24
+ tableKey: string,
25
+ usePlural: boolean
26
+ ): string {
27
+ if (modelName === "user") {
28
+ return "$users"
29
+ }
30
+ return usePlural ? `${tableKey}s` : tableKey
31
+ }
32
+
33
+ /**
34
+ * Builds entity name mapping from schema
35
+ */
36
+ function buildEntityNameMap(
37
+ schema: BetterAuthDBSchema,
38
+ usePlural: boolean
39
+ ): Record<string, string> {
40
+ const entityNameMap: Record<string, string> = {}
41
+ for (const [key, table] of Object.entries(schema)) {
42
+ const { modelName } = table
43
+ entityNameMap[modelName] = getEntityName(modelName, key, usePlural)
44
+ }
45
+ return entityNameMap
46
+ }
47
+
48
+ /**
49
+ * Creates link transactions for fields with references
50
+ */
51
+ function createLinkTransactions({
52
+ db,
53
+ model,
54
+ modelSchema,
55
+ data,
56
+ entityNameMap
57
+ }: {
58
+ db: InstantAdminDatabase<any, any>
59
+ model: string
60
+ modelSchema: BetterAuthDBSchema[string]
61
+ data: Record<string, unknown>
62
+ entityNameMap: Record<string, string>
63
+ }): any[] {
64
+ const linkTransactions: any[] = []
65
+ const { fields, modelName } = modelSchema
66
+
67
+ for (const [fieldKey, field] of Object.entries(fields)) {
68
+ const { references } = field
69
+
70
+ if (references) {
71
+ const { model: targetModel } = references
72
+ const targetEntityName = entityNameMap[targetModel]
73
+
74
+ if (!targetEntityName) {
75
+ console.warn(
76
+ `Warning: Could not find entity name for model "${targetModel}" referenced by ${modelName}.${fieldKey}`
77
+ )
78
+ continue
79
+ }
80
+
81
+ // Check if data has a value for this reference field
82
+ const fieldValue = data[fieldKey]
83
+ if (fieldValue != null) {
84
+ // Generate forward label from field name, using target model if field doesn't end with "id"
85
+ const forwardLabel = fieldNameToLabel(fieldKey, targetModel)
86
+
87
+ // Create link transaction
88
+ const linkParams: Record<string, string | string[]> = {
89
+ [forwardLabel]: fieldValue as string | string[]
90
+ }
91
+ const linkTransaction = db.tx[model][data.id as string].link(linkParams)
92
+
93
+ linkTransactions.push(linkTransaction)
94
+ }
95
+ }
96
+ }
97
+
98
+ return linkTransactions
99
+ }
100
+
101
+ /**
102
+ * The InstantDB adapter config options.
103
+ */
104
+ interface InstantAdapterConfig {
105
+ /**
106
+ * The InstantDB admin database instance.
107
+ */
108
+ db: InstantAdminDatabase<any, any>
109
+ /**
110
+ * If the table names in the schema are plural.
111
+ */
112
+ usePlural?: boolean
113
+ /**
114
+ * Helps you debug issues with the adapter.
115
+ */
116
+ debugLogs?: DBAdapterDebugLogOption
117
+ }
118
+
119
+ /**
120
+ * The InstantDB adapter.
121
+ */
122
+ export const instantAdapter = ({
123
+ db,
124
+ usePlural = true,
125
+ debugLogs = false
126
+ }: InstantAdapterConfig) => {
127
+ return createAdapterFactory({
128
+ config: {
129
+ customIdGenerator: id,
130
+ adapterId: "instantdb-adapter", // A unique identifier for the adapter.
131
+ adapterName: "InstantDB Adapter", // The name of the adapter.
132
+ usePlural, // Whether the table names in the schema are plural.
133
+ debugLogs, // Whether to enable debug logs.
134
+ supportsJSON: true, // Whether the database supports JSON. (Default: false)
135
+ supportsDates: false, // Whether the database supports dates. (Default: true)
136
+ supportsBooleans: true, // Whether the database supports booleans. (Default: true)
137
+ supportsNumericIds: false // Whether the database supports auto-incrementing numeric IDs. (Default: true)
138
+ },
139
+ adapter: ({ debugLog, getDefaultModelName, getFieldName, schema }) => {
140
+ return {
141
+ create: async ({ data, model }) => {
142
+ const defaultModelName = getDefaultModelName(model)
143
+ const modelSchema = schema[defaultModelName]
144
+
145
+ // Create the InstantDB token and override session.token
146
+ if (defaultModelName === "session") {
147
+ // Get the $users entity for this session's userId
148
+ const result = await db.query({
149
+ $users: { $: { where: { id: data.userId } } }
150
+ })
151
+
152
+ const $users = result.$users
153
+
154
+ if (!$users.length) {
155
+ throw new Error(`$users entity not found: ${data.userId}`)
156
+ }
157
+
158
+ const $user = $users[0]
159
+
160
+ // Create the InstantDB token and override session.token
161
+
162
+ debugLog("Create Token", $user.email)
163
+
164
+ const token = await db.auth.createToken($user.email as string)
165
+ const tokenField = getFieldName({ model, field: "token" })
166
+
167
+ Object.assign(data, { [tokenField]: token })
168
+ }
169
+
170
+ if (defaultModelName === "user") {
171
+ model = "$users"
172
+ }
173
+
174
+ debugLog("Create", model, prettyObject(data))
175
+
176
+ // Build entity name map for link resolution
177
+ const entityNameMap = buildEntityNameMap(schema, usePlural)
178
+
179
+ // Create the main entity transaction
180
+ const createTransaction = db.tx[model][data.id].create(data)
181
+
182
+ // Create link transactions for fields with references
183
+ const linkTransactions = createLinkTransactions({
184
+ db,
185
+ model,
186
+ modelSchema,
187
+ data,
188
+ entityNameMap
189
+ })
190
+
191
+ // Combine all transactions and execute in a single transaction
192
+ const allTransactions = [createTransaction, ...linkTransactions]
193
+ await db.transact(allTransactions)
194
+
195
+ return data
196
+ },
197
+ update: async ({ update, model, where }) => {
198
+ if (getDefaultModelName(model) === "user") {
199
+ model = "$users"
200
+ }
201
+
202
+ const entities = await fetchEntities({ db, model, where, debugLog })
203
+
204
+ if (!entities.length) return null
205
+
206
+ debugLog(
207
+ "Update:",
208
+ entities.map((entity) => entity.id),
209
+ prettyObject(update)
210
+ )
211
+
212
+ const transactions = entities.map((entity) =>
213
+ db.tx[model][entity.id].update(update as Record<string, unknown>)
214
+ )
215
+
216
+ await db.transact(transactions)
217
+
218
+ return { ...entities[0], ...update }
219
+ },
220
+ updateMany: async ({ update, model, where }) => {
221
+ if (getDefaultModelName(model) === "user") {
222
+ model = "$users"
223
+ }
224
+
225
+ const entities = await fetchEntities({ db, model, where, debugLog })
226
+
227
+ if (!entities.length) return 0
228
+
229
+ debugLog(
230
+ "Update:",
231
+ entities.map((entity) => entity.id),
232
+ prettyObject(update)
233
+ )
234
+
235
+ const transactions = entities.map((entity) =>
236
+ db.tx[model][entity.id].update(update)
237
+ )
238
+
239
+ await db.transact(transactions)
240
+
241
+ return entities.length
242
+ },
243
+ delete: async ({ model, where }) => {
244
+ if (getDefaultModelName(model) === "user") {
245
+ model = "$users"
246
+ }
247
+
248
+ const entities = await fetchEntities({ db, model, where, debugLog })
249
+
250
+ if (!entities.length) return
251
+
252
+ const transactions = entities.map((entity) =>
253
+ db.tx[model][entity.id].delete()
254
+ )
255
+
256
+ await db.transact(transactions)
257
+
258
+ if (getDefaultModelName(model) === "session") {
259
+ Promise.all(
260
+ entities.map(async (entity) => {
261
+ try {
262
+ const tokenField = getFieldName({ model, field: "token" })
263
+ await db.auth.signOut({
264
+ refresh_token: entity[tokenField]
265
+ })
266
+ } catch {}
267
+ })
268
+ )
269
+ }
270
+ },
271
+ deleteMany: async ({ model, where }) => {
272
+ if (getDefaultModelName(model) === "user") {
273
+ model = "$users"
274
+ }
275
+
276
+ const entities = await fetchEntities({ db, model, where, debugLog })
277
+
278
+ if (!entities.length) return 0
279
+
280
+ const transactions = entities.map((entity) =>
281
+ db.tx[model][entity.id].delete()
282
+ )
283
+
284
+ await db.transact(transactions)
285
+
286
+ if (getDefaultModelName(model) === "session") {
287
+ Promise.all(
288
+ entities.map(async (entity) => {
289
+ try {
290
+ const tokenField = getFieldName({ model, field: "token" })
291
+ await db.auth.signOut({
292
+ refresh_token: entity[tokenField]
293
+ })
294
+ } catch {}
295
+ })
296
+ )
297
+ }
298
+
299
+ return entities.length
300
+ },
301
+ findOne: async ({ model, where }) => {
302
+ if (getDefaultModelName(model) === "user") {
303
+ model = "$users"
304
+ }
305
+
306
+ const entities = await fetchEntities({ db, model, where, debugLog })
307
+
308
+ if (entities.length) return entities[0]
309
+
310
+ return null
311
+ },
312
+ findMany: async ({ model, where, limit, sortBy, offset }) => {
313
+ if (getDefaultModelName(model) === "user") {
314
+ model = "$users"
315
+ }
316
+
317
+ const entities = await fetchEntities({
318
+ db,
319
+ model,
320
+ where,
321
+ limit,
322
+ sortBy,
323
+ offset,
324
+ debugLog
325
+ })
326
+
327
+ return entities
328
+ },
329
+ count: async ({ model, where }) => {
330
+ if (getDefaultModelName(model) === "user") {
331
+ model = "$users"
332
+ }
333
+
334
+ const entities = await fetchEntities({ db, model, where, debugLog })
335
+
336
+ return entities.length
337
+ },
338
+ createSchema: async ({ file = "./auth.schema.ts", tables }) => {
339
+ const code = createSchema(tables, usePlural)
340
+ return { code, path: file }
341
+ }
342
+ }
343
+ }
344
+ })
345
+ }
346
+
347
+ async function fetchEntities({
348
+ db,
349
+ debugLog,
350
+ model,
351
+ where,
352
+ limit,
353
+ offset,
354
+ sortBy
355
+ }: {
356
+ db: InstantAdminDatabase<any, any>
357
+ debugLog: (...args: any[]) => void
358
+ model: string
359
+ where?: Where[]
360
+ limit?: number
361
+ offset?: number
362
+ sortBy?: { field: string; direction: "asc" | "desc" }
363
+ }) {
364
+ let order: Order | undefined
365
+ if (sortBy) {
366
+ order = {
367
+ [sortBy.field]: sortBy.direction
368
+ }
369
+ }
370
+
371
+ const query = {
372
+ [model]: { $: { where: parseWhere(where), limit, offset, order } }
373
+ } as InstaQLParams<any>
374
+
375
+ debugLog("Query", prettyObject(query))
376
+
377
+ const result = await db.query(query)
378
+
379
+ debugLog("Result", prettyObject(result))
380
+
381
+ return result[model] as any[]
382
+ }
383
+
384
+ export function parseWhere(where?: Where[]) {
385
+ const whereQuery = {} as Record<string, unknown>
386
+ where?.forEach((item) => {
387
+ switch (item.operator) {
388
+ case "eq":
389
+ whereQuery[item.field] = item.value
390
+ break
391
+ case "in":
392
+ whereQuery[item.field] = { $in: item.value }
393
+ break
394
+ case "contains":
395
+ whereQuery[item.field] = { $like: `%${item.value}%` }
396
+ break
397
+ case "starts_with":
398
+ whereQuery[item.field] = { $like: `${item.value}%` }
399
+ break
400
+ case "ends_with":
401
+ whereQuery[item.field] = { $like: `%${item.value}` }
402
+ break
403
+ case "ne":
404
+ whereQuery[item.field] = { $not: item.value }
405
+ break
406
+ case "gt":
407
+ whereQuery[item.field] = { $gt: item.value }
408
+ break
409
+ case "gte":
410
+ whereQuery[item.field] = { $gte: item.value }
411
+ break
412
+ case "lt":
413
+ whereQuery[item.field] = { $lt: item.value }
414
+ break
415
+ case "lte":
416
+ whereQuery[item.field] = { $lte: item.value }
417
+ break
418
+ }
419
+ })
420
+
421
+ return whereQuery
422
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./adapter/instant-adapter"
2
+ export * from "./shared/instant-auth"
@@ -0,0 +1,24 @@
1
+ import util from "node:util"
2
+
3
+ /**
4
+ * Pretty an object.
5
+ * @param object - The object to pretty.
6
+ * @returns The pretty object.
7
+ */
8
+ export function prettyObject(object: unknown) {
9
+ return util.inspect(object, { colors: true, depth: null })
10
+ }
11
+
12
+ /**
13
+ * Converts a field name to a relationship label
14
+ * e.g., "userId" -> "user", "organizationId" -> "organization"
15
+ * If field doesn't end with "id", uses the target model name
16
+ */
17
+ export function fieldNameToLabel(fieldName: string, targetModel: string): string {
18
+ // Remove "Id" suffix if present
19
+ if (fieldName.toLowerCase().endsWith("id")) {
20
+ return fieldName.slice(0, -2)
21
+ }
22
+ // If it doesn't end with "id", use the target model name
23
+ return targetModel
24
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./instant-auth"
2
+ export * from "./use-instant-auth"
3
+ export * from "./use-instant-session"
4
+ export * from "./use-persistent-session"
@@ -0,0 +1,17 @@
1
+ import type { InstantReactWebDatabase } from "@instantdb/react"
2
+ import type { MinimalAuthClient, SessionResult } from "./types"
3
+ import { useInstantAuth } from "./use-instant-auth"
4
+
5
+ export function InstantAuth<TSessionResult extends SessionResult>({
6
+ db,
7
+ authClient,
8
+ persistent
9
+ }: {
10
+ db: InstantReactWebDatabase<any, any>
11
+ authClient: MinimalAuthClient<TSessionResult>
12
+ persistent?: boolean
13
+ }) {
14
+ useInstantAuth({ db, authClient, persistent })
15
+
16
+ return null
17
+ }
@@ -0,0 +1,9 @@
1
+ import type { createAuthClient } from "better-auth/react"
2
+
3
+ export type SessionResult = ReturnType<AuthClient["useSession"]>
4
+
5
+ export type MinimalAuthClient<TSessionResult extends SessionResult> = {
6
+ useSession: () => TSessionResult
7
+ }
8
+
9
+ export type AuthClient = ReturnType<typeof createAuthClient>
@@ -0,0 +1,13 @@
1
+ import { useSyncExternalStore } from "react"
2
+
3
+ function subscribe() {
4
+ return () => {}
5
+ }
6
+
7
+ export function useHydrated() {
8
+ return useSyncExternalStore(
9
+ subscribe,
10
+ () => true,
11
+ () => false
12
+ )
13
+ }
@@ -0,0 +1,28 @@
1
+ import type { InstantReactWebDatabase } from "@instantdb/react"
2
+ import { useEffect } from "react"
3
+
4
+ import { instantAuth } from "../shared/instant-auth"
5
+ import type { MinimalAuthClient, SessionResult } from "./types"
6
+ import { usePersistentSession } from "./use-persistent-session"
7
+
8
+ export interface InstantAuthProps<TSessionResult extends SessionResult> {
9
+ db: InstantReactWebDatabase<any, any>
10
+ authClient: MinimalAuthClient<TSessionResult>
11
+ persistent?: boolean
12
+ }
13
+
14
+ export function useInstantAuth<TSessionResult extends SessionResult>({
15
+ db,
16
+ authClient,
17
+ persistent
18
+ }: InstantAuthProps<TSessionResult>) {
19
+ const { isPending, data } = persistent
20
+ ? usePersistentSession(authClient)
21
+ : authClient.useSession()
22
+
23
+ useEffect(() => {
24
+ if (isPending) return
25
+
26
+ instantAuth(db, data?.session)
27
+ }, [db, isPending, data?.session])
28
+ }
@@ -0,0 +1,46 @@
1
+ import type { BetterFetchError } from "better-auth/react"
2
+
3
+ import type { SessionResult } from "./types"
4
+ import type { InstantAuthProps } from "./use-instant-auth"
5
+ import { usePersistentSession } from "./use-persistent-session"
6
+
7
+ export function useInstantSession<TSessionResult extends SessionResult>({
8
+ db,
9
+ authClient,
10
+ persistent
11
+ }: InstantAuthProps<TSessionResult>) {
12
+ const {
13
+ data: sessionData,
14
+ isPending,
15
+ error,
16
+ isRefetching,
17
+ ...rest
18
+ } = persistent ? usePersistentSession(authClient) : authClient.useSession()
19
+
20
+ const { user: authUser, error: authError } = db.useAuth()
21
+ const authPending = sessionData && !authUser && !authError
22
+
23
+ const { data } = db.useQuery(
24
+ authUser
25
+ ? {
26
+ $users: { $: { where: { id: authUser.id } } }
27
+ }
28
+ : null
29
+ )
30
+
31
+ if (data?.$users?.length) {
32
+ const user = data.$users[0]
33
+
34
+ if (sessionData?.user?.id === user.id) {
35
+ sessionData.user = user as typeof sessionData.user
36
+ }
37
+ }
38
+
39
+ return {
40
+ data: !authError && !authPending ? sessionData : null,
41
+ isPending: authPending || isPending,
42
+ isRefetching: authPending || isRefetching,
43
+ error: (authError as BetterFetchError) || error,
44
+ ...rest
45
+ }
46
+ }
@@ -0,0 +1,64 @@
1
+ import { useEffect } from "react"
2
+
3
+ import type { MinimalAuthClient, SessionResult } from "./types"
4
+ import { useHydrated } from "./use-hydrated"
5
+
6
+ let lastPersisted: any | undefined | null
7
+ let restoredData: any | undefined | null
8
+
9
+ export function usePersistentSession<
10
+ TSessionResult extends SessionResult,
11
+ TAuthClient extends MinimalAuthClient<TSessionResult>
12
+ >(authClient: TAuthClient) {
13
+ const { data, isPending, isRefetching, error, ...rest } =
14
+ authClient.useSession()
15
+ const hydrated = useHydrated()
16
+
17
+ useEffect(() => {
18
+ if (isPending) return
19
+
20
+ const persistSession = () => {
21
+ if (!data || lastPersisted?.session.id === data?.session.id) return
22
+
23
+ lastPersisted = data
24
+ localStorage.setItem("ba-instant-session", JSON.stringify(data))
25
+ }
26
+
27
+ const unpersistSession = () => {
28
+ if (data || error || (lastPersisted === null && restoredData === null))
29
+ return
30
+
31
+ localStorage.removeItem("ba-instant-session")
32
+ lastPersisted = null
33
+ restoredData = null
34
+ }
35
+
36
+ persistSession()
37
+ unpersistSession()
38
+ }, [data, isPending, error])
39
+
40
+ if (hydrated && !data) {
41
+ if (restoredData === undefined) {
42
+ const persisted = localStorage.getItem("ba-instant-session")
43
+
44
+ if (persisted) {
45
+ const data = JSON.parse(persisted) as TSessionResult
46
+ restoredData = data
47
+ } else {
48
+ restoredData = null
49
+ }
50
+ }
51
+
52
+ if (restoredData) {
53
+ return {
54
+ data: restoredData,
55
+ isPending: false,
56
+ isRefetching: false,
57
+ error: null,
58
+ ...rest
59
+ } as TSessionResult
60
+ }
61
+ }
62
+
63
+ return { data, isPending, isRefetching, error, ...rest }
64
+ }
@@ -0,0 +1,18 @@
1
+ import type { InstantCoreDatabase } from "@instantdb/core"
2
+ import type { InstantReactWebDatabase } from "@instantdb/react"
3
+ import type { Session } from "better-auth"
4
+
5
+ export async function instantAuth(
6
+ db: InstantCoreDatabase<any, any> | InstantReactWebDatabase<any, any>,
7
+ session?: Session
8
+ ) {
9
+ const user = await db.getAuth()
10
+
11
+ if (session && user?.id !== session?.userId) {
12
+ db.auth.signInWithToken(session.token)
13
+ }
14
+
15
+ if (!session && user) {
16
+ db.auth.signOut()
17
+ }
18
+ }