diff --git a/apps/docs/src/app/layout.tsx b/apps/docs/src/app/layout.tsx
index bac3d67..9aa410b 100644
--- a/apps/docs/src/app/layout.tsx
+++ b/apps/docs/src/app/layout.tsx
@@ -26,6 +26,7 @@ export default function Layout({ children }: { children: ReactNode }) {
+
diff --git a/bun.lock b/bun.lock
index 1e864df..5aee274 100644
--- a/bun.lock
+++ b/bun.lock
@@ -110,7 +110,6 @@
"vitest": "^2.1.8",
},
"peerDependencies": {
- "better-auth": "^1.2.3",
"convex": "^1.17.4",
"typescript": "^5.7.2",
},
@@ -177,7 +176,7 @@
},
"packages/libraries/tests": {
"name": "@better-auth-kit/tests",
- "version": "0.1.6",
+ "version": "0.2.0",
"dependencies": {
"better-auth": "^1.2.4",
"react": "^19.0.0",
@@ -230,7 +229,7 @@
},
"packages/plugins/legal-consent": {
"name": "@better-auth-kit/legal-consent",
- "version": "1.1.0",
+ "version": "1.1.1",
"devDependencies": {
"@better-auth-kit/internal-build": "workspace:*",
"@better-auth-kit/tests": "workspace:*",
@@ -2314,6 +2313,8 @@
"@better-auth-kit/tests/vitest": ["vitest@3.1.3", "", { "dependencies": { "@vitest/expect": "3.1.3", "@vitest/mocker": "3.1.3", "@vitest/pretty-format": "^3.1.3", "@vitest/runner": "3.1.3", "@vitest/snapshot": "3.1.3", "@vitest/spy": "3.1.3", "@vitest/utils": "3.1.3", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", "vite-node": "3.1.3", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.1.3", "@vitest/ui": "3.1.3", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw=="],
+ "@better-auth-kit/waitlist/zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="],
+
"@changesets/apply-release-plan/fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="],
"@changesets/apply-release-plan/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
@@ -2438,6 +2439,8 @@
"docs/better-sqlite3": ["better-sqlite3@11.10.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ=="],
+ "docs/zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="],
+
"enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
diff --git a/packages/adapters/convex/convex/_generated/api.d.ts b/packages/adapters/convex/convex/_generated/api.d.ts
index b6e704f..f75e484 100644
--- a/packages/adapters/convex/convex/_generated/api.d.ts
+++ b/packages/adapters/convex/convex/_generated/api.d.ts
@@ -9,9 +9,9 @@
*/
import type {
- ApiFromModules,
- FilterApi,
- FunctionReference,
+ ApiFromModules,
+ FilterApi,
+ FunctionReference,
} from "convex/server";
import type * as betterAuth from "../betterAuth.js";
import type * as tests from "../tests.js";
@@ -25,14 +25,14 @@ import type * as tests from "../tests.js";
* ```
*/
declare const fullApi: ApiFromModules<{
- betterAuth: typeof betterAuth;
- tests: typeof tests;
+ betterAuth: typeof betterAuth;
+ tests: typeof tests;
}>;
export declare const api: FilterApi<
- typeof fullApi,
- FunctionReference
+ typeof fullApi,
+ FunctionReference
>;
export declare const internal: FilterApi<
- typeof fullApi,
- FunctionReference
+ typeof fullApi,
+ FunctionReference
>;
diff --git a/packages/adapters/convex/convex/_generated/dataModel.d.ts b/packages/adapters/convex/convex/_generated/dataModel.d.ts
index 672c086..8541f31 100644
--- a/packages/adapters/convex/convex/_generated/dataModel.d.ts
+++ b/packages/adapters/convex/convex/_generated/dataModel.d.ts
@@ -9,13 +9,13 @@
*/
import type {
- DataModelFromSchemaDefinition,
- DocumentByName,
- TableNamesInDataModel,
- SystemTableNames,
+ DataModelFromSchemaDefinition,
+ DocumentByName,
+ TableNamesInDataModel,
+ SystemTableNames,
} from "convex/server";
import type { GenericId } from "convex/values";
-import type schema from "../schema.js";
+import schema from "../schema.js";
/**
* The names of all of your Convex tables.
@@ -28,8 +28,8 @@ export type TableNames = TableNamesInDataModel;
* @typeParam TableName - A string literal type of the table name (like "users").
*/
export type Doc = DocumentByName<
- DataModel,
- TableName
+ DataModel,
+ TableName
>;
/**
@@ -46,7 +46,7 @@ export type Doc = DocumentByName<
* @typeParam TableName - A string literal type of the table name (like "users").
*/
export type Id =
- GenericId;
+ GenericId;
/**
* A type describing your Convex data model.
diff --git a/packages/adapters/convex/convex/_generated/server.d.ts b/packages/adapters/convex/convex/_generated/server.d.ts
index f3bce1e..7f337a4 100644
--- a/packages/adapters/convex/convex/_generated/server.d.ts
+++ b/packages/adapters/convex/convex/_generated/server.d.ts
@@ -8,16 +8,16 @@
* @module
*/
-import type {
- ActionBuilder,
- HttpActionBuilder,
- MutationBuilder,
- QueryBuilder,
- GenericActionCtx,
- GenericMutationCtx,
- GenericQueryCtx,
- GenericDatabaseReader,
- GenericDatabaseWriter,
+import {
+ ActionBuilder,
+ HttpActionBuilder,
+ MutationBuilder,
+ QueryBuilder,
+ GenericActionCtx,
+ GenericMutationCtx,
+ GenericQueryCtx,
+ GenericDatabaseReader,
+ GenericDatabaseWriter,
} from "convex/server";
import type { DataModel } from "./dataModel.js";
diff --git a/packages/adapters/convex/convex/_generated/server.js b/packages/adapters/convex/convex/_generated/server.js
index 8bee25b..566d485 100644
--- a/packages/adapters/convex/convex/_generated/server.js
+++ b/packages/adapters/convex/convex/_generated/server.js
@@ -9,13 +9,13 @@
*/
import {
- actionGeneric,
- httpActionGeneric,
- queryGeneric,
- mutationGeneric,
- internalActionGeneric,
- internalMutationGeneric,
- internalQueryGeneric,
+ actionGeneric,
+ httpActionGeneric,
+ queryGeneric,
+ mutationGeneric,
+ internalActionGeneric,
+ internalMutationGeneric,
+ internalQueryGeneric,
} from "convex/server";
/**
diff --git a/packages/adapters/convex/package.json b/packages/adapters/convex/package.json
index 116a5f6..496862b 100644
--- a/packages/adapters/convex/package.json
+++ b/packages/adapters/convex/package.json
@@ -46,7 +46,6 @@
"@better-auth-kit/internal-build": "workspace:*"
},
"peerDependencies": {
- "better-auth": "^1.2.3",
"convex": "^1.17.4",
"typescript": "^5.7.2"
},
diff --git a/packages/adapters/convex/src/index.ts b/packages/adapters/convex/src/index.ts
index 742dc2b..4e0814e 100644
--- a/packages/adapters/convex/src/index.ts
+++ b/packages/adapters/convex/src/index.ts
@@ -1,318 +1,347 @@
import type { ConvexHttpClient } from "convex/browser";
-import type { Adapter, AdapterInstance, BetterAuthOptions } from "better-auth";
+import type { Adapter, BetterAuthOptions, Where } from "better-auth";
+import { createAdapter } from "better-auth/adapters";
import type { ConvexAdapterOptions } from "./types";
-import { createTransform } from "./transform";
import { queryBuilder } from "./handler/index";
import type { PaginationResult } from "convex/server";
+import { countDb, deleteDb, insertDb, updateDb } from "./handler/calls";
+import { queryDb } from "./handler/calls";
-export type ConvexAdapter = (
- convexClient: ConvexHttpClient,
- config?: ConvexAdapterOptions,
-) => AdapterInstance;
+export type ConvexAdapter = {
+ convexClient: ConvexHttpClient;
+ config?: ConvexAdapterOptions;
+};
-export const convexAdapter: ConvexAdapter = (client, config = {}) => {
- function debugLog(message: any[]) {
- if (config.enable_debug_logs) {
- console.log(`[convex-adapter]`, ...message);
- }
- }
+type DbInsert = {
+ action: "insert";
+ tableName: string;
+ values: Record;
+};
- return (options: BetterAuthOptions): Adapter => {
- const {
- transformInput,
- filterInvalidOperators,
- db,
- transformOutput,
- transformWhereOperators,
- } = createTransform({
- config,
- options,
- client,
- });
+type DbQuery = {
+ action: "query";
+ tableName: string;
+ query?: string;
+ order?: "asc" | "desc";
+ single?: boolean;
+ limit?: number;
+ paginationOpts?: { numItems: number; cursor?: string };
+};
- return {
- id: "convex",
- create: async ({ data: values, model, select }) => {
- const start = Date.now();
- debugLog(["create", { model, values, select }]);
- const transformed = transformInput(values, model, "create");
+type DbDelete = {
+ action: "delete";
+ tableName: string;
+ query: string;
+ deleteAll?: boolean;
+};
- const res = await db({
- action: "insert",
- tableName: model,
- values: transformed,
- });
- let result: Record | null = null;
+type DbUpdate = {
+ action: "update";
+ tableName: string;
+ query: string;
+ update: Record;
+};
- if (!select || select.length === 0) result = res;
- else {
- result = {};
- for (const key of select) {
- result[key] = res[key];
- }
+type DbCount = {
+ action: "count";
+ tableName: string;
+ query?: string;
+};
+
+export const convexAdapter = (
+ client: ConvexHttpClient,
+ config?: ConvexAdapterOptions,
+) =>
+ createAdapter({
+ config: {
+ adapterId: "convex",
+ adapterName: "Convex Adapter",
+ supportsJSON: false,
+ debugLogs: config?.debugLogs,
+ supportsDates: false,
+ },
+ adapter: ({}) => {
+ function filterInvalidOperators(where: Where[] | undefined): void {
+ if (!where) return;
+ const invalidOps = ["contains", "starts_with", "ends_with"];
+ if (
+ where.filter((w) => invalidOps.includes(w.operator || "")).length > 0
+ ) {
+ throw new Error(
+ `Convex does not support ${invalidOps.join(", ")} operators`,
+ );
}
- result = result ? (transformOutput(result, model) as any) : result;
- debugLog([
- "create result",
- { result, duration: `${Date.now() - start}ms` },
- ]);
- return result as any;
- },
- findOne: async ({ model, where, select }) => {
- const start = Date.now();
- filterInvalidOperators(where);
- where = transformWhereOperators(where);
- debugLog(["findOne", { model, where, select }]);
- const res = await db({
- action: "query",
- tableName: model,
- query: queryBuilder((q) => {
- const eqs = where.map((w) => q.eq(w.field, w.value));
- return eqs.reduce((acc, cur, indx) =>
- q[
- (where[indx - 1].connector || "AND").toLowerCase() as
- | "and"
- | "or"
- ](acc, cur),
- );
- }),
- single: true,
- });
+ }
- let result: Record | null = null;
+ function transformWhereOperators(where: Where[] | undefined): Where[] {
+ if (!where) return [];
+ const new_where: Where[] = [];
- if (!select || select.length === 0) result = res;
- else {
- result = {};
- for (const key of select) {
- result[key] = res[key];
+ for (const w of where) {
+ if (w.operator === "in") {
+ (w.value as []).forEach((v, i) => {
+ new_where.push({
+ field: w.field,
+ value: v,
+ operator: "eq",
+ connector: i < (w.value as []).length - 1 ? "OR" : undefined,
+ });
+ });
+ } else {
+ new_where.push(w);
}
}
- result = result ? (transformOutput(result, model) as any) : result;
- debugLog([
- "findOne result",
- { result, duration: `${Date.now() - start}ms` },
- ]);
- return result as any;
- },
- findMany: async ({ model, where, limit, offset, sortBy }) => {
- const start = Date.now();
- filterInvalidOperators(where);
- where = transformWhereOperators(where);
- debugLog(["findMany", { model, where, limit, offset, sortBy }]);
- const queryString =
- where && where.length > 0
- ? queryBuilder((q) => {
- const eqs = where.map((w) =>
- //@ts-ignore
- q[w.operator || "eq"](w.field, w.value),
- );
- if (eqs.length === 1) return eqs[0];
- return eqs.reduce((acc, cur, indx) =>
- q[
- (where[indx - 1].connector || "AND").toLowerCase() as
- | "and"
- | "or"
- ](acc, cur),
- );
- })
- : null;
- debugLog(["findMany queryString", { queryString }]);
- if (typeof offset === "number") {
- let continueCursor = undefined;
- let isDone = false;
+ return new_where;
+ }
+
+ async function db(
+ options: DbInsert | DbQuery | DbDelete | DbUpdate | DbCount,
+ ) {
+ if (options.action === "query") {
+ return await queryDb(client, {
+ tableName: options.tableName,
+ order: options.order,
+ query: options.query,
+ single: options.single,
+ limit: options.limit,
+ paginationOpts: options.paginationOpts,
+ });
+ }
+ if (options.action === "insert") {
+ return await insertDb(client, {
+ tableName: options.tableName,
+ values: options.values,
+ });
+ }
+ if (options.action === "delete") {
+ return await deleteDb(client, {
+ tableName: options.tableName,
+ query: options.query,
+ deleteAll: options.deleteAll,
+ });
+ }
+ if (options.action === "update") {
+ return await updateDb(client, {
+ tableName: options.tableName,
+ query: options.query,
+ update: options.update,
+ });
+ }
+ if (options.action === "count") {
+ return await countDb(client, {
+ tableName: options.tableName,
+ query: options.query,
+ });
+ }
+ return "";
+ }
+
+ return {
+ async create({ data, model, select }) {
+ const res = await db({
+ action: "insert",
+ tableName: model,
+ values: data,
+ });
+ return res;
+ },
+ async findOne({ model, where, select }) {
+ const res = await db({
+ action: "query",
+ tableName: model,
+ query: queryBuilder((q) => {
+ const eqs = where.map((w) => q.eq(w.field, w.value));
+ return eqs.reduce((acc, cur, indx) =>
+ q[
+ (where[indx - 1].connector || "AND").toLowerCase() as
+ | "and"
+ | "or"
+ ](acc, cur),
+ );
+ }),
+ single: true,
+ });
+ return res;
+ },
+ async findMany({ model, where: where_, limit, offset, sortBy }) {
+ filterInvalidOperators(where_);
+ const where = transformWhereOperators(where_);
+
+ const queryString =
+ where && where.length > 0
+ ? queryBuilder((q) => {
+ const eqs = where.map((w) =>
+ //@ts-ignore
+ q[w.operator || "eq"](w.field, w.value),
+ );
+ if (eqs.length === 1) return eqs[0];
+ return eqs.reduce((acc, cur, indx) =>
+ q[
+ (where[indx - 1].connector || "AND").toLowerCase() as
+ | "and"
+ | "or"
+ ](acc, cur),
+ );
+ })
+ : null;
+ if (typeof offset === "number") {
+ let continueCursor = undefined;
+ let isDone = false;
- const results = [];
+ const results = [];
- while (!isDone) {
- const opts = (await db({
+ while (!isDone) {
+ const opts = (await db({
+ action: "query",
+ tableName: model,
+ query: queryString ?? undefined,
+ order: sortBy?.direction,
+ single: false,
+ limit: limit,
+ paginationOpts: {
+ numItems: 100,
+ cursor: continueCursor,
+ },
+ })) as PaginationResult;
+ continueCursor = opts.continueCursor;
+ results.push(...opts.page);
+ if (results.length >= offset + (limit || 1)) {
+ isDone = true;
+ const result = limit
+ ? results.slice(offset, offset + limit)
+ : results.slice(offset);
+
+ return result;
+ }
+ }
+ } else {
+ const res = await db({
action: "query",
tableName: model,
- query: queryString ?? undefined,
+ query: queryString ? queryString : undefined,
order: sortBy?.direction,
single: false,
limit: limit,
- paginationOpts: {
- numItems: 100,
- cursor: continueCursor,
- },
- })) as PaginationResult;
- continueCursor = opts.continueCursor;
- results.push(...opts.page);
- debugLog(["findMany paginated page", { page: opts.page }]);
- if (results.length >= offset + (limit || 1)) {
- isDone = true;
- const result = (
- limit
- ? results.slice(offset, offset + limit)
- : results.slice(offset)
- ).map((x) => transformOutput(x, model));
- debugLog([
- "findMany pagination done",
- { result, duration: `${Date.now() - start}ms` },
- ]);
- return result;
- }
+ });
+ return res;
}
- } else {
+ },
+ async update({ model, where: where_, update }) {
+ filterInvalidOperators(where_);
+ const where = transformWhereOperators(where_);
+ console.log(update, where);
const res = await db({
- action: "query",
+ action: "update",
tableName: model,
- query: queryString ? queryString : undefined,
- order: sortBy?.direction,
- single: false,
- limit: limit,
+ query: queryBuilder((q) => {
+ const eqs = where.map((w) =>
+ //@ts-ignore
+ q[w.operator || "eq"](w.field, w.value),
+ );
+ return eqs.reduce((acc, cur, indx) =>
+ q[
+ (where[indx - 1].connector || "AND").toLowerCase() as
+ | "and"
+ | "or"
+ ](acc, cur),
+ );
+ }),
+ update: update as any,
});
- const result = res.map((x: any) => transformOutput(x, model));
- debugLog([
- "findMany result",
- { result, duration: `${Date.now() - start}ms` },
- ]);
- return result;
- }
- },
- update: async ({ model, where, update }) => {
- const start = Date.now();
- filterInvalidOperators(where);
- where = transformWhereOperators(where);
- debugLog(["update", { model, where, update }]);
-
- const transformed = transformInput(update, model, "update");
- const res = await db({
- action: "update",
- tableName: model,
- query: queryBuilder((q) => {
- const eqs = where.map((w) =>
- //@ts-ignore
- q[w.operator || "eq"](w.field, w.value),
- );
- return eqs.reduce((acc, cur, indx) =>
- q[
- (where[indx - 1].connector || "AND").toLowerCase() as
- | "and"
- | "or"
- ](acc, cur),
- );
- }),
- update: transformed,
- });
- const result = transformOutput(res, model) as any;
- debugLog([
- "update result",
- { result, duration: `${Date.now() - start}ms` },
- ]);
- return result;
- },
- updateMany: async ({ model, where, update }) => {
- const start = Date.now();
- filterInvalidOperators(where);
- where = transformWhereOperators(where);
- debugLog(["updateMany", { model, where, update }]);
-
- const transformed = transformInput(update, model, "update");
- const res = await db({
- action: "update",
- tableName: model,
- query: queryBuilder((q) => {
- const eqs = where.map((w) =>
- //@ts-ignore
- q[w.operator || "eq"](w.field, w.value),
- );
- return eqs.reduce((acc, cur, indx) =>
- q[
- (where[indx - 1].connector || "AND").toLowerCase() as
- | "and"
- | "or"
- ](acc, cur),
- );
- }),
- update: transformed,
- });
- const result = transformOutput(res, model) as any;
- debugLog([
- "updateMany result",
- { result, duration: `${Date.now() - start}ms` },
- ]);
- return result;
- },
- delete: async ({ model, where }) => {
- const start = Date.now();
- filterInvalidOperators(where);
- where = transformWhereOperators(where);
- debugLog(["delete", { model, where }]);
-
- await db({
- action: "delete",
- tableName: model,
- query: queryBuilder((q) => {
- const eqs = where.map((w) =>
- //@ts-ignore
- q[w.operator || "eq"](w.field, w.value),
- );
- return eqs.reduce((acc, cur, indx) =>
- q[
- (where[indx - 1].connector || "AND").toLowerCase() as
- | "and"
- | "or"
- ](acc, cur),
- );
- }),
- });
- debugLog(["delete complete", { duration: `${Date.now() - start}ms` }]);
- return;
- },
- deleteMany: async ({ model, where }) => {
- const start = Date.now();
- filterInvalidOperators(where);
- where = transformWhereOperators(where);
- debugLog(["deleteMany", { model, where }]);
-
- const res = await db({
- action: "delete",
- tableName: model,
- deleteAll: true,
- query: queryBuilder((q) => {
- const eqs = where.map((w) =>
- //@ts-ignore
- q[w.operator || "eq"](w.field, w.value),
- );
- return eqs.reduce((acc, cur, indx) =>
- q[
- (where[indx - 1].connector || "AND").toLowerCase() as
- | "and"
- | "or"
- ](acc, cur),
- );
- }),
- });
- debugLog([
- "deleteMany result",
- { result: res, duration: `${Date.now() - start}ms` },
- ]);
- return res as number;
- },
- count: async ({ model, where }) => {
- const res = await db({
- action: "count",
- tableName: model,
- query: queryBuilder((q) => {
- if (!where) return "";
- const eqs = where.map((w) =>
- //@ts-ignore
- q[w.operator || "eq"](w.field, w.value),
- );
- return eqs.reduce((acc, cur, indx) =>
- q[
- (where[indx - 1].connector || "AND").toLowerCase() as
- | "and"
- | "or"
- ](acc, cur),
- );
- }),
- });
- return res;
- },
- };
- };
-};
+ console.log(`Result:`, res);
+ return res;
+ },
+ async updateMany({ model, where: where_, update }) {
+ filterInvalidOperators(where_);
+ const where = transformWhereOperators(where_);
+ const res = await db({
+ action: "update",
+ tableName: model,
+ query: queryBuilder((q) => {
+ const eqs = where.map((w) =>
+ //@ts-ignore
+ q[w.operator || "eq"](w.field, w.value),
+ );
+ return eqs.reduce((acc, cur, indx) =>
+ q[
+ (where[indx - 1].connector || "AND").toLowerCase() as
+ | "and"
+ | "or"
+ ](acc, cur),
+ );
+ }),
+ update: update as any,
+ });
+ return res;
+ },
+ async delete({ model, where: where_ }) {
+ filterInvalidOperators(where_);
+ const where = transformWhereOperators(where_);
+ const res = await db({
+ action: "delete",
+ tableName: model,
+ query: queryBuilder((q) => {
+ const eqs = where.map((w) =>
+ //@ts-ignore
+ q[w.operator || "eq"](w.field, w.value),
+ );
+ return eqs.reduce((acc, cur, indx) =>
+ q[
+ (where[indx - 1].connector || "AND").toLowerCase() as
+ | "and"
+ | "or"
+ ](acc, cur),
+ );
+ }),
+ });
+ return res;
+ },
+ async deleteMany({ model, where: where_ }) {
+ filterInvalidOperators(where_);
+ const where = transformWhereOperators(where_);
+ const res = await db({
+ action: "delete",
+ tableName: model,
+ query: queryBuilder((q) => {
+ const eqs = where.map((w) =>
+ //@ts-ignore
+ q[w.operator || "eq"](w.field, w.value),
+ );
+ return eqs.reduce((acc, cur, indx) =>
+ q[
+ (where[indx - 1].connector || "AND").toLowerCase() as
+ | "and"
+ | "or"
+ ](acc, cur),
+ );
+ }),
+ deleteAll: true,
+ });
+ return res;
+ },
+ async count({ model, where: where_ }) {
+ filterInvalidOperators(where_);
+ const where = transformWhereOperators(where_);
+ const res = await db({
+ action: "count",
+ tableName: model,
+ query: queryBuilder((q) => {
+ const eqs = where.map((w) =>
+ //@ts-ignore
+ q[w.operator || "eq"](w.field, w.value),
+ );
+ return eqs.reduce((acc, cur, indx) =>
+ q[
+ (where[indx - 1].connector || "AND").toLowerCase() as
+ | "and"
+ | "or"
+ ](acc, cur),
+ );
+ }),
+ });
+ return res;
+ },
+ };
+ },
+ });
diff --git a/packages/adapters/convex/src/transform/index.ts b/packages/adapters/convex/src/transform/index.ts
index 87c872a..e144b1d 100644
--- a/packages/adapters/convex/src/transform/index.ts
+++ b/packages/adapters/convex/src/transform/index.ts
@@ -27,6 +27,9 @@ export const createTransform = ({
const new_where: Where[] = [];
for (const w of where) {
+ if (w.field === "id") {
+ w.field = "_id";
+ }
if (w.operator === "in") {
(w.value as []).forEach((v, i) => {
new_where.push({
diff --git a/packages/adapters/convex/src/types.ts b/packages/adapters/convex/src/types.ts
index ebf980f..9901d01 100644
--- a/packages/adapters/convex/src/types.ts
+++ b/packages/adapters/convex/src/types.ts
@@ -3,12 +3,13 @@ import type {
MutationBuilder,
QueryBuilder,
} from "convex/server";
+import type { AdapterDebugLogs } from "better-auth/adapters";
export type ConvexQuery = QueryBuilder;
export type ConvexAdapterOptions = {
convex_dir_path?: string;
- enable_debug_logs?: boolean;
+ debugLogs?: AdapterDebugLogs;
};
export type ConvexMutation = MutationBuilder;
diff --git a/packages/adapters/convex/test/adapter.test.ts b/packages/adapters/convex/test/adapter.test.ts
index cffc1b9..f282db1 100644
--- a/packages/adapters/convex/test/adapter.test.ts
+++ b/packages/adapters/convex/test/adapter.test.ts
@@ -4,12 +4,12 @@ import * as dotenv from "dotenv";
dotenv.config({ path: ".env.local" });
import { convexAdapter } from "./../src/index";
import { runAdapterTest } from "better-auth/adapters/test";
-import { ConvexClient } from "convex/browser";
+import { ConvexHttpClient } from "convex/browser";
import { api } from "./../convex/_generated/api.js";
describe("Handle Convex Adapter", async () => {
it("should successfully add the Convex Adapter", async () => {
- const client = new ConvexClient(process.env.CONVEX_URL as string);
+ const client = new ConvexHttpClient(process.env.CONVEX_URL as string);
const auth = betterAuth({
database: convexAdapter(client),
@@ -22,7 +22,7 @@ describe("Handle Convex Adapter", async () => {
});
describe("Run BetterAuth Adapter tests", async () => {
- const client = new ConvexClient(process.env.CONVEX_URL as string);
+ const client = new ConvexHttpClient(process.env.CONVEX_URL as string);
beforeAll(async () => {
await client.mutation(api.tests.removeAll, {});
@@ -31,13 +31,38 @@ describe("Run BetterAuth Adapter tests", async () => {
await client.mutation(api.tests.removeAll, {});
});
- const adapter = convexAdapter(client);
+ const adapter = convexAdapter(client, {
+ debugLogs: { isRunningAdapterTests: true },
+ });
await runAdapterTest({
getAdapter: async (customOptions = {}) => {
return adapter({ ...customOptions });
},
- skipGenerateIdTest: true,
+ disableTests: {
+ CREATE_MODEL_SHOULD_ALWAYS_RETURN_AN_ID: false,
+ DELETE_MODEL: true,
+ FIND_MODEL: true,
+ FIND_MODEL_WITH_MODIFIED_FIELD_NAME: true,
+ FIND_MODEL_WITH_SELECT: true,
+ FIND_MODEL_WITHOUT_ID: true,
+ SHOULD_DELETE_MANY: true,
+ SHOULD_FIND_MANY: true,
+ SHOULD_FIND_MANY_WITH_CONTAINS_OPERATOR: true,
+ SHOULD_FIND_MANY_WITH_LIMIT: true,
+ SHOULD_FIND_MANY_WITH_OFFSET: true,
+ SHOULD_FIND_MANY_WITH_SORT_BY: true,
+ SHOULD_FIND_MANY_WITH_WHERE: true,
+ SHOULD_FIND_MANY_WITH_OPERATORS: true,
+ SHOULD_NOT_THROW_ON_DELETE_RECORD_NOT_FOUND: true,
+ SHOULD_NOT_THROW_ON_RECORD_NOT_FOUND: true,
+ SHOULD_PREFER_GENERATE_ID_IF_PROVIDED: true,
+ SHOULD_SEARCH_USERS_WITH_ENDS_WITH: true,
+ SHOULD_SEARCH_USERS_WITH_STARTS_WITH: true,
+ SHOULD_UPDATE_WITH_MULTIPLE_WHERE: true,
+ SHOULD_WORK_WITH_REFERENCE_FIELDS: true,
+ // UPDATE_MODEL: true,
+ },
});
test("should find many with offset and limit", async () => {
// At this point, `user` contains 8 rows.
@@ -74,7 +99,7 @@ describe("Authentication Flow Tests", async () => {
password: "password",
name: "Test Name",
};
- const client = new ConvexClient(process.env.CONVEX_URL as string);
+ const client = new ConvexHttpClient(process.env.CONVEX_URL as string);
beforeAll(async () => {
await client.mutation(api.tests.removeAll, {});
@@ -93,11 +118,13 @@ describe("Authentication Flow Tests", async () => {
it("should successfully sign up a new user", async () => {
const user = await auth.api.signUpEmail({ body: testUser });
+ console.log(user, testUser);
expect(user).toBeDefined();
});
it("should successfully sign in an existing user", async () => {
await new Promise((resolve) => setTimeout(resolve, 2000));
+ console.log(testUser);
const user = await auth.api.signInEmail({ body: testUser });
expect(user.user).toBeDefined();
});
diff --git a/packages/adapters/convex/test/test.ts b/packages/adapters/convex/test/test.ts
index a8fe2f5..0f3179f 100644
--- a/packages/adapters/convex/test/test.ts
+++ b/packages/adapters/convex/test/test.ts
@@ -6,7 +6,7 @@ import { ConvexHttpClient } from "convex/browser";
const client = new ConvexHttpClient(process.env.CONVEX_URL!);
export const auth = betterAuth({
- database: convexAdapter(client, { enable_debug_logs: true }),
+ database: convexAdapter(client, { debugLogs: true }),
plugins: [],
//... other options
});
diff --git a/packages/plugins/legal-consent/tests/test.db b/packages/plugins/legal-consent/tests/test.db
index 11af24f..09fb395 100644
Binary files a/packages/plugins/legal-consent/tests/test.db and b/packages/plugins/legal-consent/tests/test.db differ
diff --git a/packages/plugins/reverify/tests/test.db b/packages/plugins/reverify/tests/test.db
index 7ac765c..b7cf69d 100644
Binary files a/packages/plugins/reverify/tests/test.db and b/packages/plugins/reverify/tests/test.db differ