@better-auth/cli 1.5.0-beta.1 → 1.5.0-beta.3

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.
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { i as getPackageInfo, n as generateSchema } from "./generators-BJiZexpE.mjs";
2
3
  import { Command } from "commander";
3
4
  import * as fs$2 from "node:fs";
4
5
  import fs, { existsSync, readFileSync } from "node:fs";
@@ -6,15 +7,12 @@ import fs$1 from "node:fs/promises";
6
7
  import * as path$1 from "node:path";
7
8
  import path from "node:path";
8
9
  import { createTelemetry, getTelemetryAuthConfig } from "@better-auth/telemetry";
9
- import { getAdapter, getAuthTables, getMigrations } from "better-auth/db";
10
+ import { getAdapter, getMigrations } from "better-auth/db";
10
11
  import chalk from "chalk";
11
12
  import prompts from "prompts";
12
13
  import yoctoSpinner from "yocto-spinner";
13
14
  import * as z from "zod/v4";
14
- import { initGetFieldName, initGetModelName } from "better-auth/adapters";
15
- import prettier, { format } from "prettier";
16
- import { capitalizeFirstLetter } from "@better-auth/core/utils";
17
- import { produceSchema } from "@mrleebo/prisma-ast";
15
+ import { format } from "prettier";
18
16
  import babelPresetReact from "@babel/preset-react";
19
17
  import babelPresetTypeScript from "@babel/preset-typescript";
20
18
  import { BetterAuthError } from "@better-auth/core/error";
@@ -32,568 +30,6 @@ import open from "open";
32
30
  import { base64 } from "@better-auth/utils/base64";
33
31
  import "dotenv/config";
34
32
 
35
- //#region src/generators/drizzle.ts
36
- function convertToSnakeCase(str, camelCase) {
37
- if (camelCase) return str;
38
- return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLowerCase();
39
- }
40
- const generateDrizzleSchema = async ({ options, file, adapter }) => {
41
- const tables = getAuthTables(options);
42
- const filePath = file || "./auth-schema.ts";
43
- const databaseType = adapter.options?.provider;
44
- if (!databaseType) throw new Error(`Database provider type is undefined during Drizzle schema generation. Please define a \`provider\` in the Drizzle adapter config. Read more at https://better-auth.com/docs/adapters/drizzle`);
45
- const fileExist = existsSync(filePath);
46
- let code = generateImport({
47
- databaseType,
48
- tables,
49
- options
50
- });
51
- const getModelName = initGetModelName({
52
- schema: tables,
53
- usePlural: adapter.options?.adapterConfig?.usePlural
54
- });
55
- const getFieldName = initGetFieldName({
56
- schema: tables,
57
- usePlural: adapter.options?.adapterConfig?.usePlural
58
- });
59
- for (const tableKey in tables) {
60
- const table = tables[tableKey];
61
- const modelName = getModelName(tableKey);
62
- const fields = table.fields;
63
- function getType(name, field) {
64
- if (!databaseType) throw new Error(`Database provider type is undefined during Drizzle schema generation. Please define a \`provider\` in the Drizzle adapter config. Read more at https://better-auth.com/docs/adapters/drizzle`);
65
- name = convertToSnakeCase(name, adapter.options?.camelCase);
66
- if (field.references?.field === "id") {
67
- const useNumberId$1 = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
68
- const useUUIDs = options.advanced?.database?.generateId === "uuid";
69
- if (useNumberId$1) if (databaseType === "pg") return `integer('${name}')`;
70
- else if (databaseType === "mysql") return `int('${name}')`;
71
- else return `integer('${name}')`;
72
- if (useUUIDs && databaseType === "pg") return `uuid('${name}')`;
73
- if (field.references.field) {
74
- if (databaseType === "mysql") return `varchar('${name}', { length: 36 })`;
75
- }
76
- return `text('${name}')`;
77
- }
78
- const type = field.type;
79
- if (typeof type !== "string") if (Array.isArray(type) && type.every((x) => typeof x === "string")) return {
80
- sqlite: `text({ enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
81
- pg: `text('${name}', { enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
82
- mysql: `mysqlEnum([${type.map((x) => `'${x}'`).join(", ")}])`
83
- }[databaseType];
84
- else throw new TypeError(`Invalid field type for field ${name} in model ${modelName}`);
85
- const dbTypeMap = {
86
- string: {
87
- sqlite: `text('${name}')`,
88
- pg: `text('${name}')`,
89
- mysql: field.unique ? `varchar('${name}', { length: 255 })` : field.references ? `varchar('${name}', { length: 36 })` : field.sortable ? `varchar('${name}', { length: 255 })` : field.index ? `varchar('${name}', { length: 255 })` : `text('${name}')`
90
- },
91
- boolean: {
92
- sqlite: `integer('${name}', { mode: 'boolean' })`,
93
- pg: `boolean('${name}')`,
94
- mysql: `boolean('${name}')`
95
- },
96
- number: {
97
- sqlite: `integer('${name}')`,
98
- pg: field.bigint ? `bigint('${name}', { mode: 'number' })` : `integer('${name}')`,
99
- mysql: field.bigint ? `bigint('${name}', { mode: 'number' })` : `int('${name}')`
100
- },
101
- date: {
102
- sqlite: `integer('${name}', { mode: 'timestamp_ms' })`,
103
- pg: `timestamp('${name}')`,
104
- mysql: `timestamp('${name}', { fsp: 3 })`
105
- },
106
- "number[]": {
107
- sqlite: `text('${name}', { mode: "json" })`,
108
- pg: field.bigint ? `bigint('${name}', { mode: 'number' }).array()` : `integer('${name}').array()`,
109
- mysql: `text('${name}', { mode: 'json' })`
110
- },
111
- "string[]": {
112
- sqlite: `text('${name}', { mode: "json" })`,
113
- pg: `text('${name}').array()`,
114
- mysql: `text('${name}', { mode: "json" })`
115
- },
116
- json: {
117
- sqlite: `text('${name}', { mode: "json" })`,
118
- pg: `jsonb('${name}')`,
119
- mysql: `json('${name}', { mode: "json" })`
120
- }
121
- }[type];
122
- if (!dbTypeMap) throw new Error(`Unsupported field type '${field.type}' for field '${name}'.`);
123
- return dbTypeMap[databaseType];
124
- }
125
- let id = "";
126
- const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
127
- if (options.advanced?.database?.generateId === "uuid" && databaseType === "pg") id = `uuid("id").default(sql\`pg_catalog.gen_random_uuid()\`).primaryKey()`;
128
- else if (useNumberId) if (databaseType === "pg") id = `integer("id").generatedByDefaultAsIdentity().primaryKey()`;
129
- else if (databaseType === "sqlite") id = `integer("id", { mode: "number" }).primaryKey({ autoIncrement: true })`;
130
- else id = `int("id").autoincrement().primaryKey()`;
131
- else if (databaseType === "mysql") id = `varchar('id', { length: 36 }).primaryKey()`;
132
- else if (databaseType === "pg") id = `text('id').primaryKey()`;
133
- else id = `text('id').primaryKey()`;
134
- let indexes = [];
135
- const assignIndexes = (indexes$1) => {
136
- if (!indexes$1.length) return "";
137
- let code$1 = [`, (table) => [`];
138
- for (const index of indexes$1) code$1.push(` ${index.type}("${index.name}").on(table.${index.on}),`);
139
- code$1.push(`]`);
140
- return code$1.join("\n");
141
- };
142
- const schema = `export const ${modelName} = ${databaseType}Table("${convertToSnakeCase(modelName, adapter.options?.camelCase)}", {
143
- id: ${id},
144
- ${Object.keys(fields).map((field) => {
145
- const attr = fields[field];
146
- const fieldName = attr.fieldName || field;
147
- let type = getType(fieldName, attr);
148
- if (attr.index && !attr.unique) indexes.push({
149
- type: "index",
150
- name: `${modelName}_${fieldName}_idx`,
151
- on: fieldName
152
- });
153
- else if (attr.index && attr.unique) indexes.push({
154
- type: "uniqueIndex",
155
- name: `${modelName}_${fieldName}_uidx`,
156
- on: fieldName
157
- });
158
- if (attr.defaultValue !== null && typeof attr.defaultValue !== "undefined") if (typeof attr.defaultValue === "function") {
159
- if (attr.type === "date" && attr.defaultValue.toString().includes("new Date()")) if (databaseType === "sqlite") type += `.default(sql\`(cast(unixepoch('subsecond') * 1000 as integer))\`)`;
160
- else type += `.defaultNow()`;
161
- } else if (typeof attr.defaultValue === "string") type += `.default("${attr.defaultValue}")`;
162
- else type += `.default(${attr.defaultValue})`;
163
- if (attr.onUpdate && attr.type === "date") {
164
- if (typeof attr.onUpdate === "function") type += `.$onUpdate(${attr.onUpdate})`;
165
- }
166
- return `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${getModelName(attr.references.model)}.${getFieldName({
167
- model: attr.references.model,
168
- field: attr.references.field
169
- })}, { onDelete: '${attr.references.onDelete || "cascade"}' })` : ""}`;
170
- }).join(",\n ")}
171
- }${assignIndexes(indexes)});`;
172
- code += `\n${schema}\n`;
173
- }
174
- let relationsString = "";
175
- for (const tableKey in tables) {
176
- const table = tables[tableKey];
177
- const modelName = getModelName(tableKey);
178
- const oneRelations = [];
179
- const manyRelations = [];
180
- const manyRelationsSet = /* @__PURE__ */ new Set();
181
- const foreignFields = Object.entries(table.fields).filter(([_, field]) => field.references);
182
- for (const [fieldName, field] of foreignFields) {
183
- const referencedModel = field.references.model;
184
- const relationKey = getModelName(referencedModel);
185
- const fieldRef = `${getModelName(tableKey)}.${getFieldName({
186
- model: tableKey,
187
- field: fieldName
188
- })}`;
189
- const referenceRef = `${getModelName(referencedModel)}.${getFieldName({
190
- model: referencedModel,
191
- field: field.references.field || "id"
192
- })}`;
193
- oneRelations.push({
194
- key: relationKey,
195
- model: getModelName(referencedModel),
196
- type: "one",
197
- reference: {
198
- field: fieldRef,
199
- references: referenceRef,
200
- fieldName
201
- }
202
- });
203
- }
204
- const otherModels = Object.entries(tables).filter(([modelName$1]) => modelName$1 !== tableKey);
205
- const modelRelationsMap = /* @__PURE__ */ new Map();
206
- for (const [modelName$1, otherTable] of otherModels) {
207
- const foreignKeysPointingHere = Object.entries(otherTable.fields).filter(([_, field]) => field.references?.model === tableKey || field.references?.model === getModelName(tableKey));
208
- if (foreignKeysPointingHere.length === 0) continue;
209
- const hasUnique = foreignKeysPointingHere.some(([_, field]) => !!field.unique);
210
- const hasMany$1 = foreignKeysPointingHere.some(([_, field]) => !field.unique);
211
- modelRelationsMap.set(modelName$1, {
212
- modelName: modelName$1,
213
- hasUnique,
214
- hasMany: hasMany$1
215
- });
216
- }
217
- for (const { modelName: modelName$1, hasMany: hasMany$1 } of modelRelationsMap.values()) {
218
- const relationType = hasMany$1 ? "many" : "one";
219
- let relationKey = getModelName(modelName$1);
220
- if (!adapter.options?.adapterConfig?.usePlural && relationType === "many") relationKey = `${relationKey}s`;
221
- if (!manyRelationsSet.has(relationKey)) {
222
- manyRelationsSet.add(relationKey);
223
- manyRelations.push({
224
- key: relationKey,
225
- model: getModelName(modelName$1),
226
- type: relationType
227
- });
228
- }
229
- }
230
- const relationsByModel = /* @__PURE__ */ new Map();
231
- for (const relation of oneRelations) if (relation.reference) {
232
- const modelKey = relation.key;
233
- if (!relationsByModel.has(modelKey)) relationsByModel.set(modelKey, []);
234
- relationsByModel.get(modelKey).push(relation);
235
- }
236
- const duplicateRelations = [];
237
- const singleRelations = [];
238
- for (const [_modelKey, relations] of relationsByModel.entries()) if (relations.length > 1) duplicateRelations.push(...relations);
239
- else singleRelations.push(relations[0]);
240
- for (const relation of duplicateRelations) if (relation.reference) {
241
- const fieldName = relation.reference.fieldName;
242
- const tableRelation = `export const ${`${modelName}${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}Relations`} = relations(${getModelName(table.modelName)}, ({ one }) => ({
243
- ${relation.key}: one(${relation.model}, {
244
- fields: [${relation.reference.field}],
245
- references: [${relation.reference.references}],
246
- })
247
- }))`;
248
- relationsString += `\n${tableRelation}\n`;
249
- }
250
- const hasOne = singleRelations.length > 0;
251
- const hasMany = manyRelations.length > 0;
252
- if (hasOne && hasMany) {
253
- const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ one, many }) => ({
254
- ${singleRelations.map((relation) => relation.reference ? ` ${relation.key}: one(${relation.model}, {
255
- fields: [${relation.reference.field}],
256
- references: [${relation.reference.references}],
257
- })` : "").filter((x) => x !== "").join(",\n ")}${singleRelations.length > 0 && manyRelations.length > 0 ? "," : ""}
258
- ${manyRelations.map(({ key, model }) => ` ${key}: many(${model})`).join(",\n ")}
259
- }))`;
260
- relationsString += `\n${tableRelation}\n`;
261
- } else if (hasOne) {
262
- const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ one }) => ({
263
- ${singleRelations.map((relation) => relation.reference ? ` ${relation.key}: one(${relation.model}, {
264
- fields: [${relation.reference.field}],
265
- references: [${relation.reference.references}],
266
- })` : "").filter((x) => x !== "").join(",\n ")}
267
- }))`;
268
- relationsString += `\n${tableRelation}\n`;
269
- } else if (hasMany) {
270
- const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ many }) => ({
271
- ${manyRelations.map(({ key, model }) => ` ${key}: many(${model})`).join(",\n ")}
272
- }))`;
273
- relationsString += `\n${tableRelation}\n`;
274
- }
275
- }
276
- code += `\n${relationsString}`;
277
- return {
278
- code: await prettier.format(code, { parser: "typescript" }),
279
- fileName: filePath,
280
- overwrite: fileExist
281
- };
282
- };
283
- function generateImport({ databaseType, tables, options }) {
284
- const rootImports = ["relations"];
285
- const coreImports = [];
286
- let hasBigint = false;
287
- let hasJson = false;
288
- for (const table of Object.values(tables)) {
289
- for (const field of Object.values(table.fields)) {
290
- if (field.bigint) hasBigint = true;
291
- if (field.type === "json") hasJson = true;
292
- }
293
- if (hasJson && hasBigint) break;
294
- }
295
- const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
296
- const useUUIDs = options.advanced?.database?.generateId === "uuid";
297
- coreImports.push(`${databaseType}Table`);
298
- coreImports.push(databaseType === "mysql" ? "varchar, text" : databaseType === "pg" ? "text" : "text");
299
- coreImports.push(hasBigint ? databaseType !== "sqlite" ? "bigint" : "" : "");
300
- coreImports.push(databaseType !== "sqlite" ? "timestamp, boolean" : "");
301
- if (databaseType === "mysql") {
302
- const hasNonBigintNumber = Object.values(tables).some((table) => Object.values(table.fields).some((field) => (field.type === "number" || field.type === "number[]") && !field.bigint));
303
- if (!!useNumberId || hasNonBigintNumber) coreImports.push("int");
304
- if (Object.values(tables).some((table) => Object.values(table.fields).some((field) => typeof field.type !== "string" && Array.isArray(field.type) && field.type.every((x) => typeof x === "string")))) coreImports.push("mysqlEnum");
305
- } else if (databaseType === "pg") {
306
- if (useUUIDs) rootImports.push("sql");
307
- const hasNonBigintNumber = Object.values(tables).some((table) => Object.values(table.fields).some((field) => (field.type === "number" || field.type === "number[]") && !field.bigint));
308
- const hasFkToId = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.references?.field === "id"));
309
- if (hasNonBigintNumber || (options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial") && hasFkToId) coreImports.push("integer");
310
- } else coreImports.push("integer");
311
- if (databaseType === "pg" && useUUIDs) coreImports.push("uuid");
312
- if (hasJson) {
313
- if (databaseType === "pg") coreImports.push("jsonb");
314
- if (databaseType === "mysql") coreImports.push("json");
315
- }
316
- if (databaseType === "sqlite" && Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.type === "date" && field.defaultValue && typeof field.defaultValue === "function" && field.defaultValue.toString().includes("new Date()")))) rootImports.push("sql");
317
- const hasIndexes = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.index && !field.unique));
318
- const hasUniqueIndexes = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.unique && field.index));
319
- if (hasIndexes) coreImports.push("index");
320
- if (hasUniqueIndexes) coreImports.push("uniqueIndex");
321
- return `${rootImports.length > 0 ? `import { ${rootImports.join(", ")} } from "drizzle-orm";\n` : ""}import { ${coreImports.map((x) => x.trim()).filter((x) => x !== "").join(", ")} } from "drizzle-orm/${databaseType}-core";\n`;
322
- }
323
-
324
- //#endregion
325
- //#region src/generators/kysely.ts
326
- const generateMigrations = async ({ options, file }) => {
327
- const { compileMigrations } = await getMigrations(options);
328
- const migrations = await compileMigrations();
329
- return {
330
- code: migrations.trim() === ";" ? "" : migrations,
331
- fileName: file || `./better-auth_migrations/${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}.sql`
332
- };
333
- };
334
-
335
- //#endregion
336
- //#region src/utils/get-package-info.ts
337
- function getPackageInfo(cwd) {
338
- const packageJsonPath = cwd ? path.join(cwd, "package.json") : path.join("package.json");
339
- return JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
340
- }
341
- function getPrismaVersion(cwd) {
342
- try {
343
- const packageInfo = getPackageInfo(cwd);
344
- const prismaVersion = packageInfo.dependencies?.prisma || packageInfo.devDependencies?.prisma || packageInfo.dependencies?.["@prisma/client"] || packageInfo.devDependencies?.["@prisma/client"];
345
- if (!prismaVersion) return null;
346
- const match = prismaVersion.match(/(\d+)/);
347
- return match ? parseInt(match[1], 10) : null;
348
- } catch {
349
- return null;
350
- }
351
- }
352
-
353
- //#endregion
354
- //#region src/generators/prisma.ts
355
- const generatePrismaSchema = async ({ adapter, options, file }) => {
356
- const provider = adapter.options?.provider || "postgresql";
357
- const tables = getAuthTables(options);
358
- const filePath = file || "./prisma/schema.prisma";
359
- const schemaPrismaExist = existsSync(path.join(process.cwd(), filePath));
360
- const getModelName = initGetModelName({
361
- schema: getAuthTables(options),
362
- usePlural: adapter.options?.adapterConfig?.usePlural
363
- });
364
- const getFieldName = initGetFieldName({
365
- schema: getAuthTables(options),
366
- usePlural: false
367
- });
368
- let schemaPrisma = "";
369
- if (schemaPrismaExist) schemaPrisma = await fs$1.readFile(path.join(process.cwd(), filePath), "utf-8");
370
- else schemaPrisma = getNewPrisma(provider, process.cwd());
371
- const prismaVersion = getPrismaVersion(process.cwd());
372
- if (prismaVersion && prismaVersion >= 7 && schemaPrismaExist) schemaPrisma = produceSchema(schemaPrisma, (builder) => {
373
- const generator = builder.findByType("generator", { name: "client" });
374
- if (generator && generator.properties) {
375
- const providerProp = generator.properties.find((prop) => prop.type === "assignment" && prop.key === "provider");
376
- if (providerProp && providerProp.value === "\"prisma-client-js\"") providerProp.value = "\"prisma-client\"";
377
- }
378
- });
379
- const manyToManyRelations = /* @__PURE__ */ new Map();
380
- for (const table in tables) {
381
- const fields = tables[table]?.fields;
382
- for (const field in fields) {
383
- const attr = fields[field];
384
- if (attr.references) {
385
- const referencedOriginalModel = attr.references.model;
386
- const referencedModelNameCap = capitalizeFirstLetter(getModelName(tables[referencedOriginalModel]?.modelName || referencedOriginalModel));
387
- if (!manyToManyRelations.has(referencedModelNameCap)) manyToManyRelations.set(referencedModelNameCap, /* @__PURE__ */ new Set());
388
- const currentModelNameCap = capitalizeFirstLetter(getModelName(tables[table]?.modelName || table));
389
- manyToManyRelations.get(referencedModelNameCap).add(currentModelNameCap);
390
- }
391
- }
392
- }
393
- const indexedFields = /* @__PURE__ */ new Map();
394
- for (const table in tables) {
395
- const fields = tables[table]?.fields;
396
- const modelName = capitalizeFirstLetter(getModelName(tables[table]?.modelName || table));
397
- indexedFields.set(modelName, []);
398
- for (const field in fields) {
399
- const attr = fields[field];
400
- if (attr.index && !attr.unique) {
401
- const fieldName = attr.fieldName || field;
402
- indexedFields.get(modelName).push(fieldName);
403
- }
404
- }
405
- }
406
- const schema = produceSchema(schemaPrisma, (builder) => {
407
- for (const table in tables) {
408
- const originalTableName = table;
409
- const customModelName = tables[table]?.modelName || table;
410
- const modelName = capitalizeFirstLetter(getModelName(customModelName));
411
- const fields = tables[table]?.fields;
412
- function getType({ isBigint, isOptional, type }) {
413
- if (type === "string") return isOptional ? "String?" : "String";
414
- if (type === "number" && isBigint) return isOptional ? "BigInt?" : "BigInt";
415
- if (type === "number") return isOptional ? "Int?" : "Int";
416
- if (type === "boolean") return isOptional ? "Boolean?" : "Boolean";
417
- if (type === "date") return isOptional ? "DateTime?" : "DateTime";
418
- if (type === "json") {
419
- if (provider === "sqlite" || provider === "mysql") return isOptional ? "String?" : "String";
420
- return isOptional ? "Json?" : "Json";
421
- }
422
- if (type === "string[]") {
423
- if (provider === "sqlite" || provider === "mysql") return isOptional ? "String?" : "String";
424
- return "String[]";
425
- }
426
- if (type === "number[]") {
427
- if (provider === "sqlite" || provider === "mysql") return "String";
428
- return "Int[]";
429
- }
430
- }
431
- const prismaModel = builder.findByType("model", { name: modelName });
432
- if (!prismaModel) if (provider === "mongodb") builder.model(modelName).field("id", "String").attribute("id").attribute(`map("_id")`);
433
- else {
434
- const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
435
- const useUUIDs = options.advanced?.database?.generateId === "uuid";
436
- if (useNumberId) builder.model(modelName).field("id", "Int").attribute("id").attribute("default(autoincrement())");
437
- else if (useUUIDs && provider === "postgresql") builder.model(modelName).field("id", "String").attribute("id").attribute("default(dbgenerated(\"pg_catalog.gen_random_uuid()\"))").attribute("db.Uuid");
438
- else builder.model(modelName).field("id", "String").attribute("id");
439
- }
440
- for (const field in fields) {
441
- const attr = fields[field];
442
- const fieldName = attr.fieldName || field;
443
- if (prismaModel) {
444
- if (builder.findByType("field", {
445
- name: fieldName,
446
- within: prismaModel.properties
447
- })) continue;
448
- }
449
- const useUUIDs = options.advanced?.database?.generateId === "uuid";
450
- const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
451
- const fieldBuilder = builder.model(modelName).field(fieldName, field === "id" && useNumberId ? getType({
452
- isBigint: false,
453
- isOptional: false,
454
- type: "number"
455
- }) : getType({
456
- isBigint: attr?.bigint || false,
457
- isOptional: !attr?.required,
458
- type: attr.references?.field === "id" ? useNumberId ? "number" : "string" : attr.type
459
- }));
460
- if (field === "id") {
461
- fieldBuilder.attribute("id");
462
- if (provider === "mongodb") fieldBuilder.attribute(`map("_id")`);
463
- }
464
- if (attr.unique) builder.model(modelName).blockAttribute(`unique([${fieldName}])`);
465
- if (attr.defaultValue !== void 0) {
466
- if (Array.isArray(attr.defaultValue)) {
467
- if (attr.type === "json") {
468
- if (Object.prototype.toString.call(attr.defaultValue[0]) === "[object Object]") {
469
- fieldBuilder.attribute(`default("${JSON.stringify(attr.defaultValue).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`);
470
- continue;
471
- }
472
- let jsonArray = [];
473
- for (const value of attr.defaultValue) jsonArray.push(value);
474
- fieldBuilder.attribute(`default("${JSON.stringify(jsonArray).replace(/"/g, "\\\"")}")`);
475
- continue;
476
- }
477
- if (attr.defaultValue.length === 0) {
478
- fieldBuilder.attribute(`default([])`);
479
- continue;
480
- } else if (typeof attr.defaultValue[0] === "string" && attr.type === "string[]") {
481
- let valueArray = [];
482
- for (const value of attr.defaultValue) valueArray.push(JSON.stringify(value));
483
- fieldBuilder.attribute(`default([${valueArray}])`);
484
- } else if (typeof attr.defaultValue[0] === "number") {
485
- let valueArray = [];
486
- for (const value of attr.defaultValue) valueArray.push(`${value}`);
487
- fieldBuilder.attribute(`default([${valueArray}])`);
488
- }
489
- } else if (typeof attr.defaultValue === "object" && !Array.isArray(attr.defaultValue) && attr.defaultValue !== null) {
490
- if (Object.entries(attr.defaultValue).length === 0) {
491
- fieldBuilder.attribute(`default("{}")`);
492
- continue;
493
- }
494
- fieldBuilder.attribute(`default("${JSON.stringify(attr.defaultValue).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`);
495
- }
496
- if (field === "createdAt") fieldBuilder.attribute("default(now())");
497
- else if (typeof attr.defaultValue === "string" && provider !== "mysql") fieldBuilder.attribute(`default("${attr.defaultValue}")`);
498
- else if (typeof attr.defaultValue === "boolean" || typeof attr.defaultValue === "number") fieldBuilder.attribute(`default(${attr.defaultValue})`);
499
- else if (typeof attr.defaultValue === "function") {}
500
- }
501
- if (field === "updatedAt" && attr.onUpdate) fieldBuilder.attribute("updatedAt");
502
- else if (attr.onUpdate) {}
503
- if (attr.references) {
504
- if (useUUIDs && provider === "postgresql" && attr.references?.field === "id") builder.model(modelName).field(fieldName).attribute(`db.Uuid`);
505
- const referencedOriginalModelName = getModelName(attr.references.model);
506
- const referencedCustomModelName = tables[referencedOriginalModelName]?.modelName || referencedOriginalModelName;
507
- let action = "Cascade";
508
- if (attr.references.onDelete === "no action") action = "NoAction";
509
- else if (attr.references.onDelete === "set null") action = "SetNull";
510
- else if (attr.references.onDelete === "set default") action = "SetDefault";
511
- else if (attr.references.onDelete === "restrict") action = "Restrict";
512
- const relationField = `relation(fields: [${getFieldName({
513
- model: originalTableName,
514
- field: fieldName
515
- })}], references: [${getFieldName({
516
- model: attr.references.model,
517
- field: attr.references.field
518
- })}], onDelete: ${action})`;
519
- builder.model(modelName).field(referencedCustomModelName.toLowerCase(), `${capitalizeFirstLetter(referencedCustomModelName)}${!attr.required ? "?" : ""}`).attribute(relationField);
520
- }
521
- if (!attr.unique && !attr.references && provider === "mysql" && attr.type === "string") builder.model(modelName).field(fieldName).attribute("db.Text");
522
- }
523
- if (manyToManyRelations.has(modelName)) for (const relatedModel of manyToManyRelations.get(modelName)) {
524
- const relatedTableName = Object.keys(tables).find((key) => capitalizeFirstLetter(tables[key]?.modelName || key) === relatedModel);
525
- const relatedFields = relatedTableName ? tables[relatedTableName]?.fields : {};
526
- const [_fieldKey, fkFieldAttr] = Object.entries(relatedFields || {}).find(([_fieldName, fieldAttr]) => fieldAttr.references && getModelName(fieldAttr.references.model) === getModelName(originalTableName)) || [];
527
- const isUnique = fkFieldAttr?.unique === true;
528
- const fieldName = isUnique || adapter.options?.usePlural === true ? `${relatedModel.toLowerCase()}` : `${relatedModel.toLowerCase()}s`;
529
- if (!builder.findByType("field", {
530
- name: fieldName,
531
- within: prismaModel?.properties
532
- })) builder.model(modelName).field(fieldName, `${relatedModel}${isUnique ? "?" : "[]"}`);
533
- }
534
- const indexedFieldsForModel = indexedFields.get(modelName);
535
- if (indexedFieldsForModel && indexedFieldsForModel.length > 0) for (const fieldName of indexedFieldsForModel) {
536
- if (prismaModel) {
537
- if (prismaModel.properties.some((v) => v.type === "attribute" && v.name === "index" && JSON.stringify(v.args[0]?.value).includes(fieldName))) continue;
538
- }
539
- const field = Object.entries(fields).find(([key, attr]) => (attr.fieldName || key) === fieldName)?.[1];
540
- let indexField = fieldName;
541
- if (provider === "mysql" && field && field.type === "string") {
542
- const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
543
- const useUUIDs = options.advanced?.database?.generateId === "uuid";
544
- if (field.references?.field === "id" && (useNumberId || useUUIDs)) indexField = `${fieldName}`;
545
- else indexField = `${fieldName}(length: 191)`;
546
- }
547
- builder.model(modelName).blockAttribute(`index([${indexField}])`);
548
- }
549
- const hasAttribute = builder.findByType("attribute", {
550
- name: "map",
551
- within: prismaModel?.properties
552
- });
553
- const hasChanged = customModelName !== originalTableName;
554
- if (!hasAttribute) builder.model(modelName).blockAttribute("map", `${getModelName(hasChanged ? customModelName : originalTableName)}`);
555
- }
556
- });
557
- const schemaChanged = schema.trim() !== schemaPrisma.trim();
558
- return {
559
- code: schemaChanged ? schema : "",
560
- fileName: filePath,
561
- overwrite: schemaPrismaExist && schemaChanged
562
- };
563
- };
564
- const getNewPrisma = (provider, cwd) => {
565
- const prismaVersion = getPrismaVersion(cwd);
566
- return `generator client {
567
- provider = "${prismaVersion && prismaVersion >= 7 ? "prisma-client" : "prisma-client-js"}"
568
- }
569
-
570
- datasource db {
571
- provider = "${provider}"
572
- url = ${provider === "sqlite" ? `"file:./dev.db"` : `env("DATABASE_URL")`}
573
- }`;
574
- };
575
-
576
- //#endregion
577
- //#region src/generators/index.ts
578
- const adapters = {
579
- prisma: generatePrismaSchema,
580
- drizzle: generateDrizzleSchema,
581
- kysely: generateMigrations
582
- };
583
- const generateSchema = (opts) => {
584
- const adapter = opts.adapter;
585
- const generator = adapter.id in adapters ? adapters[adapter.id] : null;
586
- if (generator) return generator(opts);
587
- if (adapter.createSchema) return adapter.createSchema(opts.options, opts.file).then(({ code, path: fileName, overwrite }) => ({
588
- code,
589
- fileName,
590
- overwrite
591
- }));
592
- console.error(`${adapter.id} is not supported. If it is a custom adapter, please request the maintainer to implement createSchema`);
593
- process.exit(1);
594
- };
595
-
596
- //#endregion
597
33
  //#region src/utils/add-cloudflare-modules.ts
598
34
  const createModule = () => {
599
35
  return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
@@ -2915,7 +2351,7 @@ const login = new Command("login").description("Demo: Test device authorization
2915
2351
  //#region src/commands/mcp.ts
2916
2352
  async function mcpAction(options) {
2917
2353
  const mcpUrl = "https://mcp.chonkie.ai/better-auth/better-auth-builder/mcp";
2918
- const mcpName = "Better Auth";
2354
+ const mcpName = "better-auth";
2919
2355
  if (options.cursor) await handleCursorAction(mcpUrl, mcpName);
2920
2356
  else if (options.claudeCode) handleClaudeCodeAction(mcpUrl);
2921
2357
  else if (options.openCode) handleOpenCodeAction(mcpUrl);
@@ -2971,7 +2407,7 @@ function handleOpenCodeAction(mcpUrl) {
2971
2407
  console.log(chalk.bold.blue("🔧 Adding Better Auth MCP to Open Code..."));
2972
2408
  const openCodeConfig = {
2973
2409
  $schema: "https://opencode.ai/config.json",
2974
- mcp: { "Better Auth": {
2410
+ mcp: { "better-auth": {
2975
2411
  type: "remote",
2976
2412
  url: mcpUrl,
2977
2413
  enabled: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/cli",
3
- "version": "1.5.0-beta.1",
3
+ "version": "1.5.0-beta.3",
4
4
  "type": "module",
5
5
  "description": "The CLI for Better Auth",
6
6
  "module": "dist/index.mjs",
@@ -24,7 +24,20 @@
24
24
  "typescript",
25
25
  "better-auth"
26
26
  ],
27
- "exports": "./dist/index.mjs",
27
+ "exports": {
28
+ ".": "./dist/index.mjs",
29
+ "./api": {
30
+ "types": "./dist/api.d.mts",
31
+ "default": "./dist/api.mjs"
32
+ }
33
+ },
34
+ "typesVersions": {
35
+ "*": {
36
+ "api": [
37
+ "./dist/api.d.mts"
38
+ ]
39
+ }
40
+ },
28
41
  "bin": {
29
42
  "better-auth": "./dist/index.mjs"
30
43
  },
@@ -37,7 +50,7 @@
37
50
  "tsx": "^4.20.6",
38
51
  "type-fest": "^5.2.0",
39
52
  "typescript": "^5.9.3",
40
- "@better-auth/passkey": "1.5.0-beta.1"
53
+ "@better-auth/passkey": "1.5.0-beta.3"
41
54
  },
42
55
  "dependencies": {
43
56
  "@babel/core": "^7.28.4",
@@ -53,7 +66,7 @@
53
66
  "chalk": "^5.6.2",
54
67
  "commander": "^12.1.0",
55
68
  "dotenv": "^17.2.2",
56
- "drizzle-orm": "^0.33.0",
69
+ "drizzle-orm": "^0.41.0",
57
70
  "open": "^10.2.0",
58
71
  "pg": "^8.16.3",
59
72
  "prettier": "^3.6.2",
@@ -61,9 +74,9 @@
61
74
  "semver": "^7.7.2",
62
75
  "yocto-spinner": "^0.2.3",
63
76
  "zod": "^4.1.12",
64
- "@better-auth/core": "1.5.0-beta.1",
65
- "better-auth": "^1.5.0-beta.1",
66
- "@better-auth/telemetry": "1.5.0-beta.1"
77
+ "@better-auth/core": "1.5.0-beta.3",
78
+ "@better-auth/telemetry": "1.5.0-beta.3",
79
+ "better-auth": "^1.5.0-beta.3"
67
80
  },
68
81
  "files": [
69
82
  "dist"