diff --git a/packages/better-auth/src/adapters/adapter-factory/index.ts b/packages/better-auth/src/adapters/adapter-factory/index.ts index 94a875f0..050b6831 100644 --- a/packages/better-auth/src/adapters/adapter-factory/index.ts +++ b/packages/better-auth/src/adapters/adapter-factory/index.ts @@ -14,7 +14,7 @@ import type { AdapterTestDebugLogs, CleanedWhere, } from "./types"; -import type { FieldAttribute } from "../../db"; +import type { DBFieldAttribute } from "@better-auth/core/db"; export * from "./types"; let debugLogs: any[] = []; @@ -303,7 +303,7 @@ export const createAdapterFactory = }, } : {}), - } satisfies FieldAttribute; + } satisfies DBFieldAttribute; }; const getFieldAttributes = ({ diff --git a/packages/better-auth/src/adapters/adapter-factory/types.ts b/packages/better-auth/src/adapters/adapter-factory/types.ts index a5ce9832..48249fe4 100644 --- a/packages/better-auth/src/adapters/adapter-factory/types.ts +++ b/packages/better-auth/src/adapters/adapter-factory/types.ts @@ -1,5 +1,5 @@ -import type { FieldAttribute } from "../../db"; -import type { BetterAuthDbSchema } from "../../db/get-tables"; +import type { DBFieldAttribute } from "@better-auth/core/db"; +import type { BetterAuthDBSchema } from "@better-auth/core/db"; import type { AdapterSchemaCreation, BetterAuthOptions, @@ -164,7 +164,7 @@ export interface AdapterFactoryConfig { /** * The fields of the model. */ - fieldAttributes: FieldAttribute; + fieldAttributes: DBFieldAttribute; /** * The field to transform. */ @@ -180,7 +180,7 @@ export interface AdapterFactoryConfig { /** * The schema of the user's Better-Auth instance. */ - schema: BetterAuthDbSchema; + schema: BetterAuthDBSchema; /** * The options of the user's Better-Auth instance. */ @@ -196,7 +196,7 @@ export interface AdapterFactoryConfig { /** * The fields of the model. */ - fieldAttributes: FieldAttribute; + fieldAttributes: DBFieldAttribute; /** * The field to transform. */ @@ -212,7 +212,7 @@ export interface AdapterFactoryConfig { /** * The schema of the user's Better-Auth instance. */ - schema: BetterAuthDbSchema; + schema: BetterAuthDBSchema; /** * The options of the user's Better-Auth instance. */ @@ -246,7 +246,7 @@ export type AdapterFactoryCustomizeAdapterCreator = (config: { /** * The schema of the user's Better-Auth instance. */ - schema: BetterAuthDbSchema; + schema: BetterAuthDBSchema; /** * The debug log function. * @@ -303,7 +303,7 @@ export type AdapterFactoryCustomizeAdapterCreator = (config: { }: { model: string; field: string; - }) => FieldAttribute; + }) => DBFieldAttribute; }) => CustomAdapter; export interface CustomAdapter { @@ -377,7 +377,7 @@ export interface CustomAdapter { /** * The tables from the user's Better-Auth instance schema. */ - tables: BetterAuthDbSchema; + tables: BetterAuthDBSchema; }) => Promise; /** * Your adapter's options. diff --git a/packages/better-auth/src/adapters/utils.ts b/packages/better-auth/src/adapters/utils.ts index 2526795a..8be8cc7d 100644 --- a/packages/better-auth/src/adapters/utils.ts +++ b/packages/better-auth/src/adapters/utils.ts @@ -1,8 +1,8 @@ -import type { FieldAttribute } from "../db"; +import type { DBFieldAttribute } from "@better-auth/core/db"; export function withApplyDefault( value: any, - field: FieldAttribute, + field: DBFieldAttribute, action: "create" | "update", ) { if (action === "update") { diff --git a/packages/better-auth/src/db/field.ts b/packages/better-auth/src/db/field.ts index b66cef58..b9b58fa1 100644 --- a/packages/better-auth/src/db/field.ts +++ b/packages/better-auth/src/db/field.ts @@ -1,118 +1,13 @@ -import type { ZodType } from "zod"; +import type { + DBFieldAttribute, + DBFieldAttributeConfig, + DBFieldType, +} from "@better-auth/core/db"; import type { BetterAuthOptions } from "../types"; -import type { LiteralString } from "../types/helper"; - -export type FieldType = - | "string" - | "number" - | "boolean" - | "date" - | "json" - | `${"string" | "number"}[]` - | Array; - -type Primitive = - | string - | number - | boolean - | Date - | null - | undefined - | string[] - | number[]; - -export type FieldAttributeConfig = { - /** - * If the field should be required on a new record. - * @default true - */ - required?: boolean; - /** - * If the value should be returned on a response body. - * @default true - */ - returned?: boolean; - /** - * If a value should be provided when creating a new record. - * @default true - */ - input?: boolean; - /** - * Default value for the field - * - * Note: This will not create a default value on the database level. It will only - * be used when creating a new record. - */ - defaultValue?: Primitive | (() => Primitive); - /** - * Update value for the field - * - * Note: This will create an onUpdate trigger on the database level for supported adapters. - * It will be called when updating a record. - */ - onUpdate?: () => Primitive; - /** - * transform the value before storing it. - */ - transform?: { - input?: (value: Primitive) => Primitive | Promise; - output?: (value: Primitive) => Primitive | Promise; - }; - /** - * Reference to another model. - */ - references?: { - /** - * The model to reference. - */ - model: string; - /** - * The field on the referenced model. - */ - field: string; - /** - * The action to perform when the reference is deleted. - * @default "cascade" - */ - onDelete?: - | "no action" - | "restrict" - | "cascade" - | "set null" - | "set default"; - }; - unique?: boolean; - /** - * If the field should be a bigint on the database instead of integer. - */ - bigint?: boolean; - /** - * A zod schema to validate the value. - */ - validator?: { - input?: ZodType; - output?: ZodType; - }; - /** - * The name of the field on the database. - */ - fieldName?: string; - /** - * If the field should be sortable. - * - * applicable only for `text` type. - * It's useful to mark fields varchar instead of text. - */ - sortable?: boolean; -}; - -export type FieldAttribute = { - type: T; -} & FieldAttributeConfig; export const createFieldAttribute = < - T extends FieldType, - C extends Omit, "type">, + T extends DBFieldType, + C extends DBFieldAttributeConfig, >( type: T, config?: C, @@ -120,10 +15,10 @@ export const createFieldAttribute = < return { type, ...config, - } satisfies FieldAttribute; + } satisfies DBFieldAttribute; }; -export type InferValueType = T extends "string" +export type InferValueType = T extends "string" ? string : T extends "number" ? number @@ -141,7 +36,7 @@ export type InferValueType = T extends "string" export type InferFieldsOutput = Field extends Record< infer Key, - FieldAttribute + DBFieldAttribute > ? { [key in Key as Field[key]["required"] extends false @@ -158,7 +53,7 @@ export type InferFieldsOutput = Field extends Record< export type InferFieldsInput = Field extends Record< infer Key, - FieldAttribute + DBFieldAttribute > ? { [key in Key as Field[key]["required"] extends false @@ -181,7 +76,7 @@ export type InferFieldsInput = Field extends Record< */ export type InferFieldsInputClient = Field extends Record< infer Key, - FieldAttribute + DBFieldAttribute > ? { [key in Key as Field[key]["required"] extends false @@ -202,18 +97,18 @@ export type InferFieldsInputClient = Field extends Record< } : {}; -type InferFieldOutput = T["returned"] extends false +type InferFieldOutput = T["returned"] extends false ? never : T["required"] extends false ? InferValueType | undefined | null : InferValueType; /** - * Converts a Record to an object type - * with keys and value types inferred from FieldAttribute["type"]. + * Converts a Record to an object type + * with keys and value types inferred from DBFieldAttribute["type"]. */ export type FieldAttributeToObject< - Fields extends Record, + Fields extends Record, > = AddOptionalFields< { [K in keyof Fields]: InferValueType; @@ -223,7 +118,7 @@ export type FieldAttributeToObject< type AddOptionalFields< T extends Record, - Fields extends Record, + Fields extends Record, > = { // Required fields: required === true [K in keyof T as Fields[K] extends { required: true } ? K : never]: T[K]; @@ -244,14 +139,14 @@ export type InferAdditionalFieldsFromPluginOptions< Options extends { schema?: { [key in SchemaName]?: { - additionalFields?: Record; + additionalFields?: Record; }; }; }, isClientSide extends boolean = true, > = Options["schema"] extends { [key in SchemaName]?: { - additionalFields: infer Field extends Record; + additionalFields: infer Field extends Record; }; } ? isClientSide extends true @@ -259,14 +154,14 @@ export type InferAdditionalFieldsFromPluginOptions< : FieldAttributeToObject : {}; -type RemoveFieldsWithInputFalse> = { +type RemoveFieldsWithInputFalse> = { [K in keyof T as T[K]["input"] extends false ? never : K]: T[K]; }; -type InferFieldInput = InferValueType; +type InferFieldInput = InferValueType; export type PluginFieldAttribute = Omit< - FieldAttribute, + DBFieldAttribute, "transform" | "defaultValue" | "hashValue" >; diff --git a/packages/better-auth/src/db/get-migration.ts b/packages/better-auth/src/db/get-migration.ts index 99742f9b..d7f68c9c 100644 --- a/packages/better-auth/src/db/get-migration.ts +++ b/packages/better-auth/src/db/get-migration.ts @@ -2,7 +2,7 @@ import type { AlterTableColumnAlteringBuilder, CreateTableBuilder, } from "kysely"; -import type { FieldAttribute, FieldType } from "."; +import type { DBFieldAttribute, DBFieldType } from "@better-auth/core/db"; import { sql } from "kysely"; import { createLogger } from "../utils/logger"; import type { BetterAuthOptions } from "../types"; @@ -66,7 +66,7 @@ const map = { export function matchType( columnDataType: string, - fieldType: FieldType, + fieldType: DBFieldType, dbType: KyselyDatabaseType, ) { function normalize(type: string) { @@ -104,12 +104,12 @@ export async function getMigrations(config: BetterAuthOptions) { const tableMetadata = await db.introspection.getTables(); const toBeCreated: { table: string; - fields: Record; + fields: Record; order: number; }[] = []; const toBeAdded: { table: string; - fields: Record; + fields: Record; order: number; }[] = []; @@ -141,7 +141,7 @@ export async function getMigrations(config: BetterAuthOptions) { } continue; } - let toBeAddedFields: Record = {}; + let toBeAddedFields: Record = {}; for (const [fieldName, field] of Object.entries(value.fields)) { const column = table.columns.find((c) => c.name === fieldName); if (!column) { @@ -171,7 +171,7 @@ export async function getMigrations(config: BetterAuthOptions) { | CreateTableBuilder )[] = []; - function getType(field: FieldAttribute, fieldName: string) { + function getType(field: DBFieldAttribute, fieldName: string) { const type = field.type; const typeMap = { string: { diff --git a/packages/better-auth/src/db/get-schema.ts b/packages/better-auth/src/db/get-schema.ts index 5bee962f..48772e21 100644 --- a/packages/better-auth/src/db/get-schema.ts +++ b/packages/better-auth/src/db/get-schema.ts @@ -1,19 +1,20 @@ -import { getAuthTables, type FieldAttribute } from "."; +import { getAuthTables } from "."; import type { BetterAuthOptions } from "../types"; +import type { DBFieldAttribute } from "@better-auth/core/db"; export function getSchema(config: BetterAuthOptions) { const tables = getAuthTables(config); let schema: Record< string, { - fields: Record; + fields: Record; order: number; } > = {}; for (const key in tables) { const table = tables[key]!; const fields = table.fields; - let actualFields: Record = {}; + let actualFields: Record = {}; Object.entries(fields).forEach(([key, field]) => { actualFields[field.fieldName || key] = field; if (field.references) { diff --git a/packages/better-auth/src/db/get-tables.ts b/packages/better-auth/src/db/get-tables.ts index 4a416c94..438734f8 100644 --- a/packages/better-auth/src/db/get-tables.ts +++ b/packages/better-auth/src/db/get-tables.ts @@ -1,32 +1,12 @@ -import type { FieldAttribute } from "."; import type { BetterAuthOptions } from "../types"; - -export type BetterAuthDbSchema = Record< - string, - { - /** - * The name of the table in the database - */ - modelName: string; - /** - * The fields of the table - */ - fields: Record; - /** - * Whether to disable migrations for this table - * @default false - */ - disableMigrations?: boolean; - /** - * The order of the table - */ - order?: number; - } ->; +import type { + BetterAuthDBSchema, + DBFieldAttribute, +} from "@better-auth/core/db"; export const getAuthTables = ( options: BetterAuthOptions, -): BetterAuthDbSchema => { +): BetterAuthDBSchema => { const pluginSchema = (options.plugins ?? []).reduce( (acc, plugin) => { const schema = plugin.schema; @@ -44,7 +24,7 @@ export const getAuthTables = ( }, {} as Record< string, - { fields: Record; modelName: string } + { fields: Record; modelName: string } >, ); @@ -68,7 +48,7 @@ export const getAuthTables = ( }, }, }, - } satisfies BetterAuthDbSchema; + } satisfies BetterAuthDBSchema; const { user, session, account, ...pluginTables } = pluginSchema; @@ -124,7 +104,7 @@ export const getAuthTables = ( }, order: 2, }, - } satisfies BetterAuthDbSchema; + } satisfies BetterAuthDBSchema; return { user: { @@ -290,5 +270,5 @@ export const getAuthTables = ( }, ...pluginTables, ...(shouldAddRateLimitTable ? rateLimitTable : {}), - } satisfies BetterAuthDbSchema; + } satisfies BetterAuthDBSchema; }; diff --git a/packages/better-auth/src/db/index.ts b/packages/better-auth/src/db/index.ts index 427d8b80..34f60aa2 100644 --- a/packages/better-auth/src/db/index.ts +++ b/packages/better-auth/src/db/index.ts @@ -1,3 +1,4 @@ +export * from "@better-auth/core/db"; export * from "./internal-adapter"; export * from "./field"; export * from "./get-tables"; diff --git a/packages/better-auth/src/db/schema.ts b/packages/better-auth/src/db/schema.ts index 37e7c8d7..5271c2f9 100644 --- a/packages/better-auth/src/db/schema.ts +++ b/packages/better-auth/src/db/schema.ts @@ -1,9 +1,9 @@ import * as z from "zod"; -import type { FieldAttribute } from "."; import type { AuthPluginSchema } from "../types/plugins"; import type { BetterAuthOptions } from "../types/options"; import { APIError } from "better-call"; import type { Account, Session, User } from "../types"; +import type { DBFieldAttribute } from "@better-auth/core/db"; export const coreSchema = z.object({ id: z.string(), @@ -60,7 +60,7 @@ export const verificationSchema = coreSchema.extend({ export function parseOutputData>( data: T, schema: { - fields: Record; + fields: Record; }, ) { const fields = schema.fields; @@ -80,7 +80,7 @@ export function parseOutputData>( } export function getAllFields(options: BetterAuthOptions, table: string) { - let schema: Record = { + let schema: Record = { ...(table === "user" ? options.user?.additionalFields : {}), ...(table === "session" ? options.session?.additionalFields : {}), }; @@ -119,7 +119,7 @@ export function parseSessionOutput( export function parseInputData>( data: T, schema: { - fields: Record; + fields: Record; action?: "create" | "update"; }, ) { diff --git a/packages/better-auth/src/db/to-zod.ts b/packages/better-auth/src/db/to-zod.ts index 6ba187d5..9accf397 100644 --- a/packages/better-auth/src/db/to-zod.ts +++ b/packages/better-auth/src/db/to-zod.ts @@ -1,9 +1,9 @@ import * as z from "zod"; import type { ZodType } from "zod"; -import type { FieldAttribute } from "."; +import type { DBFieldAttribute } from "@better-auth/core/db"; export function toZodSchema< - Fields extends Record, + Fields extends Record, IsClientSide extends boolean, >({ fields, @@ -56,14 +56,14 @@ export function toZodSchema< } export type FieldAttributeToSchema< - Field extends FieldAttribute | Record, + Field extends DBFieldAttribute | Record, // if it's client side, then field attributes of `input` that are false should be removed isClientSide extends boolean = false, > = Field extends { type: any } ? GetInput>> : Record; -type GetType = F extends { +type GetType = F extends { type: "string"; } ? z.ZodString @@ -76,7 +76,7 @@ type GetType = F extends { : z.ZodAny; type GetRequired< - F extends FieldAttribute, + F extends DBFieldAttribute, Schema extends z.core.SomeType, > = F extends { required: true; @@ -86,7 +86,7 @@ type GetRequired< type GetInput< isClientSide extends boolean, - Field extends FieldAttribute, + Field extends DBFieldAttribute, Schema extends z.core.SomeType, > = Field extends { input: false; diff --git a/packages/better-auth/src/db/utils.ts b/packages/better-auth/src/db/utils.ts index 2d0d11ba..065c3e9d 100644 --- a/packages/better-auth/src/db/utils.ts +++ b/packages/better-auth/src/db/utils.ts @@ -1,10 +1,11 @@ -import { getAuthTables, type FieldAttribute } from "."; +import { getAuthTables } from "."; import { BetterAuthError } from "../error"; import type { Adapter, BetterAuthOptions } from "../types"; import { createKyselyAdapter } from "../adapters/kysely-adapter/dialect"; import { kyselyAdapter } from "../adapters/kysely-adapter"; import { memoryAdapter, type MemoryDB } from "../adapters/memory-adapter"; import { logger } from "../utils"; +import type { DBFieldAttribute } from "@better-auth/core/db"; export async function getAdapter(options: BetterAuthOptions): Promise { if (!options.database) { @@ -37,7 +38,7 @@ export async function getAdapter(options: BetterAuthOptions): Promise { } export function convertToDB>( - fields: Record, + fields: Record, values: T, ) { let result: Record = values.id @@ -57,7 +58,7 @@ export function convertToDB>( } export function convertFromDB>( - fields: Record, + fields: Record, values: T | null, ) { if (!values) { diff --git a/packages/better-auth/src/init.ts b/packages/better-auth/src/init.ts index 3e524784..f45f052a 100644 --- a/packages/better-auth/src/init.ts +++ b/packages/better-auth/src/init.ts @@ -1,11 +1,6 @@ import { defu } from "defu"; import { hashPassword, verifyPassword } from "./crypto/password"; -import { - type BetterAuthDbSchema, - createInternalAdapter, - getAuthTables, - getMigrations, -} from "./db"; +import { createInternalAdapter, getAuthTables, getMigrations } from "./db"; import type { Entries } from "type-fest"; import { getAdapter } from "./db/utils"; import type { @@ -37,6 +32,7 @@ import type { TelemetryEvent } from "./telemetry/types"; import { getKyselyDatabaseType } from "./adapters/kysely-adapter"; import { checkEndpointConflicts } from "./api"; import { isPromise } from "./utils/is-promise"; +import type { BetterAuthDBSchema } from "@better-auth/core/db"; export const init = async (options: BetterAuthOptions) => { const adapter = await getAdapter(options); @@ -245,7 +241,7 @@ export type AuthContext = { }; checkPassword: typeof checkPassword; }; - tables: BetterAuthDbSchema; + tables: BetterAuthDBSchema; runMigrations: () => Promise; publishTelemetry: (event: TelemetryEvent) => Promise; }; diff --git a/packages/better-auth/src/plugins/additional-fields/client.ts b/packages/better-auth/src/plugins/additional-fields/client.ts index f3ac889a..faeb4851 100644 --- a/packages/better-auth/src/plugins/additional-fields/client.ts +++ b/packages/better-auth/src/plugins/additional-fields/client.ts @@ -1,4 +1,4 @@ -import type { FieldAttribute } from "../../db"; +import type { DBFieldAttribute } from "@better-auth/core/db"; import type { BetterAuthClientPlugin, BetterAuthOptions } from "../../types"; import type { BetterAuthPlugin } from "../../types"; @@ -6,10 +6,10 @@ export const inferAdditionalFields = < T, S extends { user?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; session?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; } = {}, >( @@ -26,10 +26,10 @@ export const inferAdditionalFields = < type Plugin = Opts extends never ? S extends { user?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; session?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; } ? { diff --git a/packages/better-auth/src/plugins/open-api/generator.ts b/packages/better-auth/src/plugins/open-api/generator.ts index 7c2f56f4..f887c1a9 100644 --- a/packages/better-auth/src/plugins/open-api/generator.ts +++ b/packages/better-auth/src/plugins/open-api/generator.ts @@ -6,13 +6,13 @@ import type { } from "better-call"; import * as z from "zod"; import { getEndpoints } from "../../api"; -import { - type FieldAttributeConfig, - type FieldType, - getAuthTables, -} from "../../db"; +import { getAuthTables } from "../../db"; import type { AuthContext, BetterAuthOptions } from "../../types"; -import type { FieldAttribute } from "../../db"; +import type { + DBFieldAttribute, + DBFieldAttributeConfig, + DBFieldType, +} from "@better-auth/core/db"; export interface Path { get?: { @@ -81,8 +81,8 @@ function getTypeFromZodType(zodType: z.ZodType) { } type FieldSchema = { - type: FieldType; - default?: FieldAttributeConfig["defaultValue"] | "Generated at runtime"; + type: DBFieldType; + default?: DBFieldAttributeConfig["defaultValue"] | "Generated at runtime"; readOnly?: boolean; }; @@ -92,7 +92,7 @@ type OpenAPIModelSchema = { required?: string[]; }; -function getFieldSchema(field: FieldAttribute) { +function getFieldSchema(field: DBFieldAttribute) { const schema: FieldSchema = { type: field.type === "date" ? "string" : field.type, }; diff --git a/packages/better-auth/src/plugins/organization/client.ts b/packages/better-auth/src/plugins/organization/client.ts index eaece585..ed10aefe 100644 --- a/packages/better-auth/src/plugins/organization/client.ts +++ b/packages/better-auth/src/plugins/organization/client.ts @@ -19,7 +19,7 @@ import { ownerAc, defaultRoles, } from "./access"; -import type { FieldAttribute } from "../../db"; +import type { DBFieldAttribute } from "@better-auth/core/db"; import type { BetterAuthOptions, BetterAuthPlugin } from "../../types"; import type { OrganizationOptions } from "./types"; import type { HasPermissionBaseInput } from "./permission"; @@ -47,27 +47,27 @@ interface OrganizationClientOptions { schema?: { organization?: { additionalFields?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; }; member?: { additionalFields?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; }; invitation?: { additionalFields?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; }; team?: { additionalFields?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; }; organizationRole?: { additionalFields?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; }; }; diff --git a/packages/better-auth/src/plugins/organization/routes/crud-access-control.test.ts b/packages/better-auth/src/plugins/organization/routes/crud-access-control.test.ts index 13e3e6f8..05691d6c 100644 --- a/packages/better-auth/src/plugins/organization/routes/crud-access-control.test.ts +++ b/packages/better-auth/src/plugins/organization/routes/crud-access-control.test.ts @@ -6,7 +6,7 @@ import { inferOrgAdditionalFields, organizationClient } from "../client"; import { createAccessControl } from "../../access"; import { adminAc, defaultStatements, memberAc, ownerAc } from "../access"; import { parseSetCookieHeader } from "../../../cookies"; -import type { FieldAttribute } from "../../../db"; +import type { DBFieldAttribute } from "@better-auth/core/db"; import { ORGANIZATION_ERROR_CODES } from "../error-codes"; describe("dynamic access control", async (it) => { @@ -43,7 +43,7 @@ describe("dynamic access control", async (it) => { input: false, required: true, }, - } satisfies Record; + } satisfies Record; const { auth, customFetchImpl, sessionSetter, signInWithTestUser } = await getTestInstance({ diff --git a/packages/better-auth/src/plugins/organization/types.ts b/packages/better-auth/src/plugins/organization/types.ts index f053f96a..70f423dd 100644 --- a/packages/better-auth/src/plugins/organization/types.ts +++ b/packages/better-auth/src/plugins/organization/types.ts @@ -1,4 +1,4 @@ -import type { FieldAttribute } from "../../db"; +import type { DBFieldAttribute } from "@better-auth/core/db"; import type { User, Session, AuthContext } from "../../types"; import type { AccessControl, Role } from "../access"; import type { @@ -257,7 +257,7 @@ export interface OrganizationOptions { [key in keyof Omit]?: string; }; additionalFields?: { - [key in string]: FieldAttribute; + [key in string]: DBFieldAttribute; }; }; member?: { @@ -266,7 +266,7 @@ export interface OrganizationOptions { [key in keyof Omit]?: string; }; additionalFields?: { - [key in string]: FieldAttribute; + [key in string]: DBFieldAttribute; }; }; invitation?: { @@ -275,7 +275,7 @@ export interface OrganizationOptions { [key in keyof Omit]?: string; }; additionalFields?: { - [key in string]: FieldAttribute; + [key in string]: DBFieldAttribute; }; }; team?: { @@ -284,7 +284,7 @@ export interface OrganizationOptions { [key in keyof Omit]?: string; }; additionalFields?: { - [key in string]: FieldAttribute; + [key in string]: DBFieldAttribute; }; }; teamMember?: { @@ -299,7 +299,7 @@ export interface OrganizationOptions { [key in keyof Omit]?: string; }; additionalFields?: { - [key in string]: FieldAttribute; + [key in string]: DBFieldAttribute; }; }; }; diff --git a/packages/better-auth/src/types/options.ts b/packages/better-auth/src/types/options.ts index 83db00a5..df79ab06 100644 --- a/packages/better-auth/src/types/options.ts +++ b/packages/better-auth/src/types/options.ts @@ -10,7 +10,7 @@ import type { BetterAuthPlugin } from "./plugins"; import type { SocialProviderList, SocialProviders } from "../social-providers"; import type { AdapterInstance, SecondaryStorage } from "./adapter"; import type { KyselyDatabaseType } from "../adapters/kysely-adapter/types"; -import type { FieldAttribute } from "../db"; +import type { DBFieldAttribute } from "@better-auth/core/db"; import type { Models, RateLimit } from "./models"; import type { AuthContext } from "."; import type { CookieOptions } from "better-call"; @@ -457,7 +457,7 @@ export type BetterAuthOptions = { * Additional fields for the session */ additionalFields?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; /** * Changing email configuration @@ -567,7 +567,7 @@ export type BetterAuthOptions = { * Additional fields for the session */ additionalFields?: { - [key: string]: FieldAttribute; + [key: string]: DBFieldAttribute; }; /** * By default if secondary storage is provided diff --git a/packages/better-auth/src/types/plugins.ts b/packages/better-auth/src/types/plugins.ts index 54eab952..64b59ebd 100644 --- a/packages/better-auth/src/types/plugins.ts +++ b/packages/better-auth/src/types/plugins.ts @@ -1,6 +1,5 @@ import type { Migration } from "kysely"; import { type AuthMiddleware } from "../api/call"; -import type { FieldAttribute } from "../db/field"; import type { HookEndpointContext } from "."; import type { Awaitable, @@ -11,11 +10,12 @@ import type { import type { AuthContext, BetterAuthOptions } from "."; import type { Endpoint, Middleware } from "better-call"; +import type { DBFieldAttribute } from "@better-auth/core/db"; export type AuthPluginSchema = { [table in string]: { fields: { - [field in string]: FieldAttribute; + [field in string]: DBFieldAttribute; }; disableMigration?: boolean; modelName?: string; diff --git a/packages/better-auth/tsconfig.json b/packages/better-auth/tsconfig.json index a033d8b7..91133754 100644 --- a/packages/better-auth/tsconfig.json +++ b/packages/better-auth/tsconfig.json @@ -6,5 +6,6 @@ "lib": ["esnext", "dom", "dom.iterable"], "types": ["node", "bun"] }, - "include": ["src"] + "include": ["src"], + "references": [{ "path": "../core" }] } diff --git a/packages/cli/src/generators/drizzle.ts b/packages/cli/src/generators/drizzle.ts index 9d0c4f3c..428a1028 100644 --- a/packages/cli/src/generators/drizzle.ts +++ b/packages/cli/src/generators/drizzle.ts @@ -1,7 +1,7 @@ import { getAuthTables, - type BetterAuthDbSchema, - type FieldAttribute, + type BetterAuthDBSchema, + type DBFieldAttribute, } from "better-auth/db"; import type { BetterAuthOptions } from "better-auth/types"; import { existsSync } from "fs"; @@ -43,7 +43,7 @@ export const generateDrizzleSchema: SchemaGenerator = async ({ const modelName = getModelName(table.modelName, adapter.options); const fields = table.fields; - function getType(name: string, field: FieldAttribute) { + function getType(name: string, field: DBFieldAttribute) { // Not possible to reach, it's here to make typescript happy if (!databaseType) { throw new Error( @@ -224,7 +224,7 @@ function generateImport({ options, }: { databaseType: "sqlite" | "mysql" | "pg"; - tables: BetterAuthDbSchema; + tables: BetterAuthDBSchema; options: BetterAuthOptions; }) { const rootImports: string[] = []; diff --git a/packages/core/build.config.ts b/packages/core/build.config.ts index e08f4fda..8856678f 100644 --- a/packages/core/build.config.ts +++ b/packages/core/build.config.ts @@ -8,5 +8,5 @@ export default defineBuildConfig({ outDir: "dist", clean: true, failOnWarn: false, - entries: ["./src/index.ts"], + entries: ["./src/index.ts", "./src/db/index.ts"], }); diff --git a/packages/core/package.json b/packages/core/package.json index 103b1b32..131734d6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -12,15 +12,28 @@ "default": "./dist/index.mjs" }, "require": { - "types": "./dist/index.d.cjs", + "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } + }, + "./db": { + "import": { + "types": "./dist/db/index.d.ts", + "default": "./dist/db/index.mjs" + }, + "require": { + "types": "./dist/db/index.d.cts", + "default": "./dist/db/index.cjs" + } } }, "typesVersions": { "*": { "index": [ "dist/index.d.ts" + ], + "db": [ + "dist/db.d.ts" ] } }, @@ -31,5 +44,9 @@ }, "devDependencies": { "unbuild": "catalog:" + }, + "dependencies": { + "better-call": "catalog:", + "zod": "^4.1.5" } } diff --git a/packages/core/src/db/index.ts b/packages/core/src/db/index.ts new file mode 100644 index 00000000..a6b5dcec --- /dev/null +++ b/packages/core/src/db/index.ts @@ -0,0 +1,36 @@ +import type { + DBFieldAttribute, + DBFieldAttributeConfig, + DBFieldType, + DBPrimitive, + BetterAuthDBSchema, +} from "./type"; + +export type { + DBFieldAttribute, + DBFieldAttributeConfig, + DBFieldType, + DBPrimitive, + BetterAuthDBSchema, +}; + +/** + * @deprecated Backport for 1.3.x, we will remove this in 1.4.x + */ +export type FieldAttribute = DBFieldAttribute; +/** + * @deprecated Backport for 1.3.x, we will remove this in 1.4.x + */ +export type FieldAttributeConfig = DBFieldAttributeConfig; +/** + * @deprecated Backport for 1.3.x, we will remove this in 1.4.x + */ +export type FieldType = DBFieldType; +/** + * @deprecated Backport for 1.3.x, we will remove this in 1.4.x + */ +export type Primitive = DBPrimitive; +/** + * @deprecated Backport for 1.3.x, we will remove this in 1.4.x + */ +export type BetterAuthDbSchema = BetterAuthDBSchema; diff --git a/packages/core/src/db/type.ts b/packages/core/src/db/type.ts new file mode 100644 index 00000000..a216fa4e --- /dev/null +++ b/packages/core/src/db/type.ts @@ -0,0 +1,133 @@ +import type { ZodType } from "zod"; +import type { LiteralString } from "../types"; + +export type DBFieldType = + | "string" + | "number" + | "boolean" + | "date" + | "json" + | `${"string" | "number"}[]` + | Array; + +export type DBPrimitive = + | string + | number + | boolean + | Date + | null + | undefined + | string[] + | number[]; + +export type DBFieldAttributeConfig = { + /** + * If the field should be required on a new record. + * @default true + */ + required?: boolean; + /** + * If the value should be returned on a response body. + * @default true + */ + returned?: boolean; + /** + * If a value should be provided when creating a new record. + * @default true + */ + input?: boolean; + /** + * Default value for the field + * + * Note: This will not create a default value on the database level. It will only + * be used when creating a new record. + */ + defaultValue?: DBPrimitive | (() => DBPrimitive); + /** + * Update value for the field + * + * Note: This will create an onUpdate trigger on the database level for supported adapters. + * It will be called when updating a record. + */ + onUpdate?: () => DBPrimitive; + /** + * transform the value before storing it. + */ + transform?: { + input?: (value: DBPrimitive) => DBPrimitive | Promise; + output?: (value: DBPrimitive) => DBPrimitive | Promise; + }; + /** + * Reference to another model. + */ + references?: { + /** + * The model to reference. + */ + model: string; + /** + * The field on the referenced model. + */ + field: string; + /** + * The action to perform when the reference is deleted. + * @default "cascade" + */ + onDelete?: + | "no action" + | "restrict" + | "cascade" + | "set null" + | "set default"; + }; + unique?: boolean; + /** + * If the field should be a bigint on the database instead of integer. + */ + bigint?: boolean; + /** + * A zod schema to validate the value. + */ + validator?: { + input?: ZodType; + output?: ZodType; + }; + /** + * The name of the field on the database. + */ + fieldName?: string; + /** + * If the field should be sortable. + * + * applicable only for `text` type. + * It's useful to mark fields varchar instead of text. + */ + sortable?: boolean; +}; + +export type DBFieldAttribute = { + type: T; +} & DBFieldAttributeConfig; + +export type BetterAuthDBSchema = Record< + string, + { + /** + * The name of the table in the database + */ + modelName: string; + /** + * The fields of the table + */ + fields: Record; + /** + * Whether to disable migrations for this table + * @default false + */ + disableMigrations?: boolean; + /** + * The order of the table + */ + order?: number; + } +>; diff --git a/packages/core/src/types/helper.ts b/packages/core/src/types/helper.ts new file mode 100644 index 00000000..106af4a2 --- /dev/null +++ b/packages/core/src/types/helper.ts @@ -0,0 +1 @@ +export type LiteralString = "" | (string & Record); diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts new file mode 100644 index 00000000..b9755e78 --- /dev/null +++ b/packages/core/src/types/index.ts @@ -0,0 +1 @@ +export * from "./helper"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7fd615b..975732e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -906,6 +906,13 @@ importers: version: 3.6.1(sass@1.90.0)(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2)) packages/core: + dependencies: + better-call: + specifier: 'catalog:' + version: 1.0.19 + zod: + specifier: ^4.1.5 + version: 4.1.5 devDependencies: unbuild: specifier: 'catalog:' @@ -16013,7 +16020,9 @@ snapshots: metro-runtime: 0.83.1 transitivePeerDependencies: - '@babel/core' + - bufferutil - supports-color + - utf-8-validate optional: true '@react-native/normalize-colors@0.79.6': {}