Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/better-auth-type-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@proofkit/better-auth": patch
---

Fix TypeScript build errors by making adapter/migration types resilient to upstream Better Auth changes.

4 changes: 2 additions & 2 deletions packages/better-auth/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** biome-ignore-all lint/suspicious/noExplicitAny: library code */
import { logger } from "better-auth";
import { type AdapterDebugLogs, type CleanedWhere, createAdapter } from "better-auth/adapters";
import { type CleanedWhere, createAdapter } from "better-auth/adapters";
import buildQuery from "odata-query";
import { prettifyError, z } from "zod/v4";
import { createRawFetch, type FmOdataConfig } from "./odata";
Expand All @@ -19,7 +19,7 @@ interface FileMakerAdapterConfig {
/**
* Helps you debug issues with the adapter.
*/
debugLogs?: AdapterDebugLogs;
debugLogs?: boolean | { isRunningAdapterTests?: boolean };
/**
* If the table names in the schema are plural.
*/
Expand Down
31 changes: 30 additions & 1 deletion packages/better-auth/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,33 @@ import { executeMigration, planMigration, prettyPrintMigrationPlan } from "../mi
import { createRawFetch } from "../odata";
import "dotenv/config";

function getFileMakerAdapterConfigFromAdapterOptions(
options: unknown,
): AdapterOptions["config"] {
if (!options || typeof options !== "object") {
throw new Error("FileMaker adapter options were missing.");
}

const opts = options as Record<string, unknown>;

// Our adapter returns: { options: { config: FileMakerAdapterConfig } }
if (opts.config && typeof opts.config === "object") {
return opts.config as AdapterOptions["config"];
}

// Some Better Auth versions wrap adapter options under `adapterConfig`.
if (opts.adapterConfig && typeof opts.adapterConfig === "object") {
const adapterConfig = opts.adapterConfig as Record<string, unknown>;
if (adapterConfig.config && typeof adapterConfig.config === "object") {
return adapterConfig.config as AdapterOptions["config"];
}
}

throw new Error(
"Could not locate FileMaker adapter configuration from Better Auth adapter options.",
);
}

async function main() {
const program = new Command();

Expand Down Expand Up @@ -52,7 +79,9 @@ async function main() {

const betterAuthSchema = getAuthTables(config);

const adapterConfig = (adapter.options as AdapterOptions).config;
const adapterConfig = getFileMakerAdapterConfigFromAdapterOptions(
adapter.options,
);
const { fetch } = createRawFetch({
...adapterConfig.odata,
auth:
Expand Down
33 changes: 25 additions & 8 deletions packages/better-auth/src/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import type { BetterAuthDbSchema } from "better-auth/db";
import chalk from "chalk";
import type { Metadata } from "fm-odata-client";
import z from "zod/v4";
import type { createRawFetch } from "./odata";

type BetterAuthTableField = {
fieldName?: string;
type: unknown;
};

type BetterAuthTableDef = {
modelName: string;
fields: Record<string, BetterAuthTableField>;
order?: number;
};

export type BetterAuthAuthTables = Record<string, BetterAuthTableDef>;

function normalizeBetterAuthFieldType(fieldType: unknown): string {
if (typeof fieldType === "string") return fieldType;
if (Array.isArray(fieldType)) return fieldType.map(String).join("|");
return String(fieldType);
}

export async function getMetadata(fetch: ReturnType<typeof createRawFetch>["fetch"], databaseName: string) {
console.log("getting metadata...");
const result = await fetch("/$metadata", {
Expand All @@ -28,7 +46,7 @@ export async function getMetadata(fetch: ReturnType<typeof createRawFetch>["fetc

export async function planMigration(
fetch: ReturnType<typeof createRawFetch>["fetch"],
betterAuthSchema: BetterAuthDbSchema,
betterAuthSchema: BetterAuthAuthTables,
databaseName: string,
): Promise<MigrationPlan> {
const metadata = await getMetadata(fetch, databaseName);
Expand Down Expand Up @@ -93,12 +111,11 @@ export async function planMigration(

for (const baTable of baTables) {
const fields: FmField[] = Object.entries(baTable.fields).map(([key, field]) => {
let type: "varchar" | "numeric" | "timestamp" = "varchar";
if (field.type === "boolean" || field.type.includes("number")) {
type = "numeric";
} else if (field.type === "date") {
type = "timestamp";
}
// Better Auth's FieldType can be a string literal union or arrays.
// Normalize it to a string so our FM mapping logic remains stable.
const t = normalizeBetterAuthFieldType(field.type);
const type: "varchar" | "numeric" | "timestamp" =
t === "boolean" || t.includes("number") ? "numeric" : t === "date" ? "timestamp" : "varchar";
return {
name: field.fieldName ?? key,
type,
Expand Down
Loading