UNPKG

@daveyplate/better-auth-instantdb

Version:

Better Auth InstantDB Adapter

308 lines (305 loc) 10.7 kB
import { id } from '@instantdb/react'; import { createAdapter } from 'better-auth/adapters'; import { useEffect } from 'react'; // src/instant-adapter.ts function parseWhere(where) { const whereQuery = {}; where == null ? void 0 : where.map((item) => { switch (item.operator) { case "eq": whereQuery[item.field] = item.value; break; case "in": whereQuery[item.field] = { $in: item.value }; break; case "contains": whereQuery[item.field] = { $like: `%${item.value}%` }; break; case "starts_with": whereQuery[item.field] = { $like: `${item.value}%` }; break; case "ends_with": whereQuery[item.field] = { $like: `%${item.value}` }; break; case "ne": whereQuery[item.field] = { $not: item.value }; break; case "gt": whereQuery[item.field] = { $gt: item.value }; break; case "gte": whereQuery[item.field] = { $gte: item.value }; break; case "lt": whereQuery[item.field] = { $lt: item.value }; break; case "lte": whereQuery[item.field] = { $lte: item.value }; break; } }); return whereQuery; } var instantDBAdapter = ({ usePlural = true, debugLogs = false, transactionHooks, db }) => createAdapter({ config: { adapterId: "instantdb-adapter", // A unique identifier for the adapter. adapterName: "InstantDB Adapter", // The name of the adapter. usePlural, // Whether the table names in the schema are plural. debugLogs, // Whether to enable debug logs. supportsJSON: false, // Whether the database supports JSON. (Default: true) supportsDates: false, // Whether the database supports dates. (Default: true) supportsBooleans: true, // Whether the database supports booleans. (Default: true) disableIdGeneration: true, // Whether to disable automatic ID generation. (Default: false) supportsNumericIds: false // Whether the database supports numeric IDs. (Default: true) }, adapter: ({ options, getFieldName, getDefaultModelName }) => { return { async create({ data, model }) { var _a, _b; data.id = ((_b = (_a = options.advanced) == null ? void 0 : _a.database) == null ? void 0 : _b.generateId) ? options.advanced.database.generateId({ model }) : id(); const transactions = []; if (getDefaultModelName(model) === "user") { transactions.push(db.tx.$users[data.id].update({ email: data.email })); } if (getDefaultModelName(model) === "session") { const queryData = await db.query({ $users: { $: { where: { id: data.userId } }, user: {} } }); const $users = queryData.$users; if ($users.length === 0) { throw new Error(`$users entity not found: ${data.userId}`); } const $user = $users[0]; const user = $user.user; if (!user) { throw new Error(`user link not found: ${data.userId}`); } if (debugLogs) { console.log("[InstantDB] Create token for:", $user.email); } const token = await db.auth.createToken($user.email); const tokenField = getFieldName({ model, field: "token" }); data[tokenField] = token; if (user.email !== $user.email) { transactions.push( db.tx.$users[data.userId].update({ email: user.email }) ); } } transactions.push(db.tx[model][data.id].update(data)); if (getDefaultModelName(model) === "user") { transactions.push(db.tx[model][data.id].link({ $user: data.id })); } try { const userIdField = getFieldName({ model, field: "userId" }); if (data[userIdField]) { transactions.push( db.tx[model][data.id].link({ user: data[userIdField] }) ); } } catch (error) { } if (transactionHooks == null ? void 0 : transactionHooks.create) { const hookTransactions = await transactionHooks.create({ data, model }); if (hookTransactions) transactions.push(...hookTransactions); } await db.transact(transactions); return data; }, async count({ model, where }) { const query = { [model]: { $: { where: parseWhere(where) } } }; if (debugLogs) { console.log("[InstantDB] Query:", JSON.stringify(query)); } const result = await db.query(query); if (debugLogs) { console.log("[InstantDB] Result:", JSON.stringify(result)); } const entities = result[model]; return entities.length; }, async delete({ model, where }) { const query = { [model]: { $: { where: parseWhere(where) } } }; if (debugLogs) { console.log("[InstantDB] Query:", JSON.stringify(query)); } const result = await db.query(query); if (debugLogs) { console.log("[InstantDB] Result:", JSON.stringify(result)); } const entities = result[model]; if (getDefaultModelName(model) === "session") { entities.map(async (entity) => { try { const tokenField = getFieldName({ model, field: "token" }); await db.auth.signOut({ refresh_token: entity[tokenField] }); } catch (error) { } }); } const transactions = entities.map((entity) => db.tx[model][entity.id].delete()); await db.transact(transactions); }, async deleteMany({ model, where }) { const query = { [model]: { $: { where: parseWhere(where) } } }; if (debugLogs) { console.log("[InstantDB] Query:", JSON.stringify(query)); } const result = await db.query(query); if (debugLogs) { console.log("[InstantDB] Result:", JSON.stringify(result)); } const entities = result[model]; if (getDefaultModelName(model) === "session") { entities.map(async (entity) => { try { const tokenField = getFieldName({ model, field: "token" }); await db.auth.signOut({ refresh_token: entity[tokenField] }); } catch (error) { } }); } const transactions = entities.map((entity) => db.tx[model][entity.id].delete()); await db.transact(transactions); return entities.length; }, async findMany({ model, where, limit, sortBy, offset }) { let order; if (sortBy) { order = { [sortBy.field]: sortBy.direction }; } const query = { [model]: { $: { where: parseWhere(where), limit, offset, order } } }; if (debugLogs) { console.log("[InstantDB] Query:", JSON.stringify(query)); } const result = await db.query(query); if (debugLogs) { console.log("[InstantDB] Result:", JSON.stringify(result)); } const entities = result[model]; return entities; }, async findOne({ model, where }) { const query = { [model]: { $: { where: parseWhere(where) } } }; if (debugLogs) { console.log("[InstantDB] Query:", JSON.stringify(query)); } const result = await db.query(query); if (debugLogs) { console.log("[InstantDB] Result:", JSON.stringify(result)); } const entities = result[model]; if (entities.length > 0) return entities[0]; return null; }, async update({ model, update, where }) { const query = { [model]: { $: { where: parseWhere(where) } } }; if (debugLogs) { console.log("[InstantDB] Query:", JSON.stringify(query)); } const result = await db.query(query); if (debugLogs) { console.log("[InstantDB] Result:", JSON.stringify(result)); } const entities = result[model]; const transactions = entities.map( (entity) => db.tx[model][entity.id].update(update) ); if (getDefaultModelName(model) === "user") { const emailField = getFieldName({ model, field: "email" }); const email = update[emailField]; if (email) { transactions.push( ...entities.map( (entity) => db.tx.$users[entity.id].update({ email }) ) ); } } if (transactionHooks == null ? void 0 : transactionHooks.update) { const hookTransactions = await transactionHooks.update({ update, model, where }); if (hookTransactions) transactions.push(...hookTransactions); } await db.transact(transactions); if (entities.length > 0) { return { ...entities[0], ...update }; } return null; }, async updateMany({ model, update, where }) { const query = { [model]: { $: { where: parseWhere(where) } } }; if (debugLogs) { console.log("[InstantDB] Query:", JSON.stringify(query)); } const result = await db.query(query); if (debugLogs) { console.log("[InstantDB] Result:", JSON.stringify(result)); } const entities = result[model]; const transactions = entities.map( (entity) => db.tx[model][entity.id].update(update) ); if (transactionHooks == null ? void 0 : transactionHooks.update) { const hookTransactions = await transactionHooks.update({ update, model, where }); if (hookTransactions) { transactions.push(...hookTransactions); } } await db.transact(transactions); return entities.length; }, options: { usePlural, debugLogs } }; } }); function useInstantAuth({ db, sessionData, isPending }) { const { user, isLoading } = db.useAuth(); useEffect(() => { if (isPending || isLoading) return; if (sessionData) { if (!user || user.id !== sessionData.user.id) { db.auth.signInWithToken(sessionData.session.token); } } else { db.auth.signOut({ invalidateToken: false }); } }, [db, isPending, isLoading, sessionData, user]); } export { instantDBAdapter, parseWhere, useInstantAuth };