@better-auth/cli 1.4.6-beta.2 → 1.4.6

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.
Files changed (2) hide show
  1. package/dist/index.mjs +143 -77
  2. package/package.json +8 -7
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import fs, { existsSync, readFileSync } from "node:fs";
5
5
  import fs$1 from "node:fs/promises";
6
6
  import * as path$1 from "node:path";
7
7
  import path from "node:path";
8
- import { BetterAuthError, capitalizeFirstLetter, createTelemetry, getTelemetryAuthConfig, logger } from "better-auth";
8
+ import { createTelemetry, getTelemetryAuthConfig } from "@better-auth/telemetry";
9
9
  import { getAdapter, getAuthTables, getMigrations } from "better-auth/db";
10
10
  import chalk from "chalk";
11
11
  import prompts from "prompts";
@@ -13,9 +13,11 @@ import yoctoSpinner from "yocto-spinner";
13
13
  import * as z from "zod/v4";
14
14
  import { initGetFieldName, initGetModelName } from "better-auth/adapters";
15
15
  import prettier, { format } from "prettier";
16
+ import { capitalizeFirstLetter } from "@better-auth/core/utils";
16
17
  import { produceSchema } from "@mrleebo/prisma-ast";
17
18
  import babelPresetReact from "@babel/preset-react";
18
19
  import babelPresetTypeScript from "@babel/preset-typescript";
20
+ import { BetterAuthError } from "@better-auth/core/error";
19
21
  import { loadConfig } from "c12";
20
22
  import { exec, execSync } from "node:child_process";
21
23
  import * as os$1 from "node:os";
@@ -102,19 +104,19 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
102
104
  mysql: `timestamp('${name}', { fsp: 3 })`
103
105
  },
104
106
  "number[]": {
105
- sqlite: `integer('${name}').array()`,
107
+ sqlite: `text('${name}', { mode: "json" })`,
106
108
  pg: field.bigint ? `bigint('${name}', { mode: 'number' }).array()` : `integer('${name}').array()`,
107
- mysql: field.bigint ? `bigint('${name}', { mode: 'number' }).array()` : `int('${name}').array()`
109
+ mysql: `text('${name}', { mode: 'json' })`
108
110
  },
109
111
  "string[]": {
110
- sqlite: `text('${name}').array()`,
112
+ sqlite: `text('${name}', { mode: "json" })`,
111
113
  pg: `text('${name}').array()`,
112
- mysql: `text('${name}').array()`
114
+ mysql: `text('${name}', { mode: "json" })`
113
115
  },
114
116
  json: {
115
- sqlite: `text('${name}')`,
117
+ sqlite: `text('${name}', { mode: "json" })`,
116
118
  pg: `jsonb('${name}')`,
117
- mysql: `json('${name}')`
119
+ mysql: `json('${name}', { mode: "json" })`
118
120
  }
119
121
  }[type];
120
122
  if (!dbTypeMap) throw new Error(`Unsupported field type '${field.type}' for field '${name}'.`);
@@ -172,50 +174,104 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
172
174
  let relationsString = "";
173
175
  for (const tableKey in tables) {
174
176
  const table = tables[tableKey];
175
- const relations = [];
177
+ const modelName = getModelName(tableKey);
178
+ const oneRelations = [];
179
+ const manyRelations = [];
180
+ const manyRelationsSet = /* @__PURE__ */ new Set();
176
181
  const foreignFields = Object.entries(table.fields).filter(([_, field]) => field.references);
177
182
  for (const [fieldName, field] of foreignFields) {
178
183
  const referencedModel = field.references.model;
179
- relations.push({
180
- key: getModelName(referencedModel),
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,
181
195
  model: getModelName(referencedModel),
182
196
  type: "one",
183
197
  reference: {
184
- field: `${getModelName(tableKey)}.${getFieldName({
185
- model: tableKey,
186
- field: fieldName
187
- })}`,
188
- references: `${getModelName(referencedModel)}.${getFieldName({
189
- model: referencedModel,
190
- field: field.references.field || "id"
191
- })}`
198
+ field: fieldRef,
199
+ references: referenceRef,
200
+ fieldName
192
201
  }
193
202
  });
194
203
  }
195
- const otherModels = Object.entries(tables).filter(([modelName]) => modelName !== tableKey);
196
- for (const [modelName, otherTable] of otherModels) {
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) {
197
207
  const foreignKeysPointingHere = Object.entries(otherTable.fields).filter(([_, field]) => field.references?.model === tableKey || field.references?.model === getModelName(tableKey));
198
- for (const [fieldName, field] of foreignKeysPointingHere) {
199
- const isUnique = !!field.unique;
200
- let relationKey = getModelName(modelName);
201
- if (!adapter.options?.adapterConfig?.usePlural && !isUnique) relationKey = `${relationKey}s`;
202
- const model = getModelName(modelName);
203
- relations.push({
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, hasUnique, 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({
204
224
  key: relationKey,
205
- model,
206
- type: isUnique ? "one" : "many"
225
+ model: getModelName(modelName$1),
226
+ type: relationType
207
227
  });
208
228
  }
209
229
  }
210
- const hasOne = relations.some((relation) => relation.type === "one");
211
- const hasMany = relations.some((relation) => relation.type === "many");
212
- let tableRelation = `export const ${table.modelName}Relations = relations(${getModelName(table.modelName)}, ({ ${hasOne ? "one" : ""}${hasMany ? `${hasOne ? ", " : ""}many` : ""} }) => ({
213
- ${relations.map(({ key, type, model, reference }) => ` ${key}: ${type}(${model}${!reference ? "" : `, {
214
- fields: [${reference.field}],
215
- references: [${reference.references}],
216
- }`})`).join(",\n ")}
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 ")}
217
267
  }))`;
218
- if (relations.length > 0) relationsString += `\n${tableRelation}\n`;
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
+ }
219
275
  }
220
276
  code += `\n${relationsString}`;
221
277
  return {
@@ -359,9 +415,18 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
359
415
  if (type === "number") return isOptional ? "Int?" : "Int";
360
416
  if (type === "boolean") return isOptional ? "Boolean?" : "Boolean";
361
417
  if (type === "date") return isOptional ? "DateTime?" : "DateTime";
362
- if (type === "json") return isOptional ? "Json?" : "Json";
363
- if (type === "string[]") return isOptional ? "String[]" : "String[]";
364
- if (type === "number[]") return isOptional ? "Int[]" : "Int[]";
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
+ }
365
430
  }
366
431
  const prismaModel = builder.findByType("model", { name: modelName });
367
432
  if (!prismaModel) if (provider === "mongodb") builder.model(modelName).field("id", "String").attribute("id").attribute(`map("_id")`);
@@ -524,7 +589,7 @@ const generateSchema = (opts) => {
524
589
  fileName,
525
590
  overwrite
526
591
  }));
527
- logger.error(`${adapter.id} is not supported. If it is a custom adapter, please request the maintainer to implement createSchema`);
592
+ console.error(`${adapter.id} is not supported. If it is a custom adapter, please request the maintainer to implement createSchema`);
528
593
  process.exit(1);
529
594
  };
530
595
 
@@ -813,7 +878,7 @@ function getPathAliasesRecursive(tsconfigPath, visited = /* @__PURE__ */ new Set
813
878
  if (visited.has(tsconfigPath)) return {};
814
879
  visited.add(tsconfigPath);
815
880
  if (!fs.existsSync(tsconfigPath)) {
816
- logger.warn(`Referenced tsconfig not found: ${tsconfigPath}`);
881
+ console.warn(`Referenced tsconfig not found: ${tsconfigPath}`);
817
882
  return {};
818
883
  }
819
884
  try {
@@ -834,7 +899,7 @@ function getPathAliasesRecursive(tsconfigPath, visited = /* @__PURE__ */ new Set
834
899
  }
835
900
  return result;
836
901
  } catch (error) {
837
- logger.warn(`Error parsing tsconfig at ${tsconfigPath}: ${error}`);
902
+ console.warn(`Error parsing tsconfig at ${tsconfigPath}: ${error}`);
838
903
  return {};
839
904
  }
840
905
  }
@@ -886,7 +951,7 @@ async function getConfig({ cwd, configPath, shouldThrowOnError = false }) {
886
951
  });
887
952
  if (!("auth" in config) && !isDefaultExport(config)) {
888
953
  if (shouldThrowOnError) throw new Error(`Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`);
889
- logger.error(`[#better-auth]: Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`);
954
+ console.error(`[#better-auth]: Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`);
890
955
  process.exit(1);
891
956
  }
892
957
  configFile = "auth" in config ? config.auth?.options : config.options;
@@ -900,9 +965,9 @@ async function getConfig({ cwd, configPath, shouldThrowOnError = false }) {
900
965
  configFile = config.auth?.options || config.default?.options || null;
901
966
  if (!configFile) {
902
967
  if (shouldThrowOnError) throw new Error("Couldn't read your auth config. Make sure to default export your auth instance or to export as a variable named auth.");
903
- logger.error("[#better-auth]: Couldn't read your auth config.");
968
+ console.error("[#better-auth]: Couldn't read your auth config.");
904
969
  console.log("");
905
- logger.info("[#better-auth]: Make sure to default export your auth instance or to export as a variable named auth.");
970
+ console.log("[#better-auth]: Make sure to default export your auth instance or to export as a variable named auth.");
906
971
  process.exit(1);
907
972
  }
908
973
  break;
@@ -910,22 +975,22 @@ async function getConfig({ cwd, configPath, shouldThrowOnError = false }) {
910
975
  } catch (e) {
911
976
  if (typeof e === "object" && e && "message" in e && typeof e.message === "string" && e.message.includes("This module cannot be imported from a Client Component module")) {
912
977
  if (shouldThrowOnError) throw new Error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
913
- logger.error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
978
+ console.error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
914
979
  process.exit(1);
915
980
  }
916
981
  if (shouldThrowOnError) throw e;
917
- logger.error("[#better-auth]: Couldn't read your auth config.", e);
982
+ console.error("[#better-auth]: Couldn't read your auth config.", e);
918
983
  process.exit(1);
919
984
  }
920
985
  return configFile;
921
986
  } catch (e) {
922
987
  if (typeof e === "object" && e && "message" in e && typeof e.message === "string" && e.message.includes("This module cannot be imported from a Client Component module")) {
923
988
  if (shouldThrowOnError) throw new Error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
924
- logger.error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
989
+ console.error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
925
990
  process.exit(1);
926
991
  }
927
992
  if (shouldThrowOnError) throw e;
928
- logger.error("Couldn't read your auth config.", e);
993
+ console.error("Couldn't read your auth config.", e);
929
994
  process.exit(1);
930
995
  }
931
996
  }
@@ -942,7 +1007,7 @@ async function generateAction(opts) {
942
1007
  }).parse(opts);
943
1008
  const cwd = path.resolve(options.cwd);
944
1009
  if (!existsSync(cwd)) {
945
- logger.error(`The directory "${cwd}" does not exist.`);
1010
+ console.error(`The directory "${cwd}" does not exist.`);
946
1011
  process.exit(1);
947
1012
  }
948
1013
  const config = await getConfig({
@@ -950,11 +1015,11 @@ async function generateAction(opts) {
950
1015
  configPath: options.config
951
1016
  });
952
1017
  if (!config) {
953
- logger.error("No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag.");
1018
+ console.error("No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag.");
954
1019
  return;
955
1020
  }
956
1021
  const adapter = await getAdapter(config).catch((e) => {
957
- logger.error(e.message);
1022
+ console.error(e.message);
958
1023
  process.exit(1);
959
1024
  });
960
1025
  const spinner$1 = yoctoSpinner({ text: "preparing schema..." }).start();
@@ -965,7 +1030,7 @@ async function generateAction(opts) {
965
1030
  });
966
1031
  spinner$1.stop();
967
1032
  if (!schema.code) {
968
- logger.info("Your schema is already up to date.");
1033
+ console.log("Your schema is already up to date.");
969
1034
  try {
970
1035
  await (await createTelemetry(config)).publish({
971
1036
  type: "cli_generate",
@@ -991,7 +1056,7 @@ async function generateAction(opts) {
991
1056
  if (!existsSync(path.join(cwd, schema.fileName))) await fs$1.mkdir(path.dirname(path.join(cwd, schema.fileName)), { recursive: true });
992
1057
  if (schema.overwrite) await fs$1.writeFile(path.join(cwd, schema.fileName), schema.code);
993
1058
  else await fs$1.appendFile(path.join(cwd, schema.fileName), schema.code);
994
- logger.success(`🚀 Schema was ${schema.overwrite ? "overwritten" : "appended"} successfully!`);
1059
+ console.log(`🚀 Schema was ${schema.overwrite ? "overwritten" : "appended"} successfully!`);
995
1060
  try {
996
1061
  await (await createTelemetry(config)).publish({
997
1062
  type: "cli_generate",
@@ -1003,7 +1068,7 @@ async function generateAction(opts) {
1003
1068
  } catch {}
1004
1069
  process.exit(0);
1005
1070
  } else {
1006
- logger.error("Schema generation aborted.");
1071
+ console.error("Schema generation aborted.");
1007
1072
  try {
1008
1073
  await (await createTelemetry(config)).publish({
1009
1074
  type: "cli_generate",
@@ -1027,7 +1092,7 @@ async function generateAction(opts) {
1027
1092
  message: `Do you want to generate the schema to ${chalk.yellow(schema.fileName)}?`
1028
1093
  })).confirm;
1029
1094
  if (!confirm$1) {
1030
- logger.error("Schema generation aborted.");
1095
+ console.error("Schema generation aborted.");
1031
1096
  try {
1032
1097
  await (await createTelemetry(config)).publish({
1033
1098
  type: "cli_generate",
@@ -1043,7 +1108,7 @@ async function generateAction(opts) {
1043
1108
  if (!existsSync(path.dirname(path.join(cwd, schema.fileName)))) await fs$1.mkdir(path.dirname(path.join(cwd, schema.fileName)), { recursive: true });
1044
1109
  }
1045
1110
  await fs$1.writeFile(options.output || path.join(cwd, schema.fileName), schema.code);
1046
- logger.success(`🚀 Schema was generated successfully!`);
1111
+ console.log(`🚀 Schema was generated successfully!`);
1047
1112
  try {
1048
1113
  await (await createTelemetry(config)).publish({
1049
1114
  type: "cli_generate",
@@ -1719,7 +1784,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
1719
1784
  total_dependencies.push(...dependencies);
1720
1785
  } catch (error) {
1721
1786
  spinner$1.stop(`Something went wrong while generating/updating your new auth config file.`, 1);
1722
- logger.error(error.message);
1787
+ console.error(error.message);
1723
1788
  process.exit(1);
1724
1789
  }
1725
1790
  if (database) try {
@@ -1732,7 +1797,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
1732
1797
  total_envs.push(...envs);
1733
1798
  } catch (error) {
1734
1799
  spinner$1.stop(`Something went wrong while generating/updating your new auth config file.`, 1);
1735
- logger.error(error.message);
1800
+ console.error(error.message);
1736
1801
  process.exit(1);
1737
1802
  }
1738
1803
  return {
@@ -1892,7 +1957,7 @@ function installDependencies({ dependencies, packageManager, cwd }) {
1892
1957
  //#region src/commands/secret.ts
1893
1958
  const generateSecret = new Command("secret").action(() => {
1894
1959
  const secret = generateSecretHash();
1895
- logger.info(`\nAdd the following to your .env file:
1960
+ console.log(`\nAdd the following to your .env file:
1896
1961
  ${chalk.gray("# Auth Secret") + chalk.green(`\nBETTER_AUTH_SECRET=${secret}`)}`);
1897
1962
  });
1898
1963
  const generateSecretHash = () => {
@@ -2722,7 +2787,7 @@ async function loginAction(opts) {
2722
2787
  });
2723
2788
  spinner$1.stop();
2724
2789
  if (error || !data) {
2725
- logger.error(`Failed to request device authorization: ${error?.error_description || "Unknown error"}`);
2790
+ console.error(`Failed to request device authorization: ${error?.error_description || "Unknown error"}`);
2726
2791
  process.exit(1);
2727
2792
  }
2728
2793
  const { device_code, user_code, verification_uri, verification_uri_complete, interval = 5, expires_in } = data;
@@ -2748,7 +2813,7 @@ async function loginAction(opts) {
2748
2813
  }
2749
2814
  } catch (err) {
2750
2815
  spinner$1.stop();
2751
- logger.error(`Login failed: ${err instanceof Error ? err.message : "Unknown error"}`);
2816
+ console.error(`Login failed: ${err instanceof Error ? err.message : "Unknown error"}`);
2752
2817
  process.exit(1);
2753
2818
  }
2754
2819
  }
@@ -2783,22 +2848,22 @@ async function pollForToken(authClient, deviceCode, clientId, initialInterval) {
2783
2848
  break;
2784
2849
  case "access_denied":
2785
2850
  spinner$1.stop();
2786
- logger.error("Access was denied by the user");
2851
+ console.error("Access was denied by the user");
2787
2852
  process.exit(1);
2788
2853
  break;
2789
2854
  case "expired_token":
2790
2855
  spinner$1.stop();
2791
- logger.error("The device code has expired. Please try again.");
2856
+ console.error("The device code has expired. Please try again.");
2792
2857
  process.exit(1);
2793
2858
  break;
2794
2859
  default:
2795
2860
  spinner$1.stop();
2796
- logger.error(`Error: ${error.error_description}`);
2861
+ console.error(`Error: ${error.error_description}`);
2797
2862
  process.exit(1);
2798
2863
  }
2799
2864
  } catch (err) {
2800
2865
  spinner$1.stop();
2801
- logger.error(`Network error: ${err instanceof Error ? err.message : "Unknown error"}`);
2866
+ console.error(`Network error: ${err instanceof Error ? err.message : "Unknown error"}`);
2802
2867
  process.exit(1);
2803
2868
  }
2804
2869
  setTimeout(poll, pollingInterval * 1e3);
@@ -2817,7 +2882,7 @@ async function storeToken(token) {
2817
2882
  };
2818
2883
  await fs$1.writeFile(TOKEN_FILE, JSON.stringify(tokenData, null, 2), "utf-8");
2819
2884
  } catch (error) {
2820
- logger.warn("Failed to store authentication token locally");
2885
+ console.warn("Failed to store authentication token locally");
2821
2886
  }
2822
2887
  }
2823
2888
  async function getStoredToken() {
@@ -2962,6 +3027,7 @@ const mcp = new Command("mcp").description("Add Better Auth MCP server to MCP Cl
2962
3027
 
2963
3028
  //#endregion
2964
3029
  //#region src/commands/migrate.ts
3030
+ /** @internal */
2965
3031
  async function migrateAction(opts) {
2966
3032
  const options = z.object({
2967
3033
  cwd: z.string(),
@@ -2971,7 +3037,7 @@ async function migrateAction(opts) {
2971
3037
  }).parse(opts);
2972
3038
  const cwd = path.resolve(options.cwd);
2973
3039
  if (!existsSync(cwd)) {
2974
- logger.error(`The directory "${cwd}" does not exist.`);
3040
+ console.error(`The directory "${cwd}" does not exist.`);
2975
3041
  process.exit(1);
2976
3042
  }
2977
3043
  const config = await getConfig({
@@ -2979,17 +3045,17 @@ async function migrateAction(opts) {
2979
3045
  configPath: options.config
2980
3046
  });
2981
3047
  if (!config) {
2982
- logger.error("No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag.");
3048
+ console.error("No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag.");
2983
3049
  return;
2984
3050
  }
2985
3051
  const db = await getAdapter(config);
2986
3052
  if (!db) {
2987
- logger.error("Invalid database configuration. Make sure you're not using adapters. Migrate command only works with built-in Kysely adapter.");
3053
+ console.error("Invalid database configuration. Make sure you're not using adapters. Migrate command only works with built-in Kysely adapter.");
2988
3054
  process.exit(1);
2989
3055
  }
2990
3056
  if (db.id !== "kysely") {
2991
3057
  if (db.id === "prisma") {
2992
- logger.error("The migrate command only works with the built-in Kysely adapter. For Prisma, run `npx @better-auth/cli generate` to create the schema, then use Prismas migrate or push to apply it.");
3058
+ console.error("The migrate command only works with the built-in Kysely adapter. For Prisma, run `npx @better-auth/cli generate` to create the schema, then use Prisma's migrate or push to apply it.");
2993
3059
  try {
2994
3060
  await (await createTelemetry(config)).publish({
2995
3061
  type: "cli_migrate",
@@ -3003,7 +3069,7 @@ async function migrateAction(opts) {
3003
3069
  process.exit(0);
3004
3070
  }
3005
3071
  if (db.id === "drizzle") {
3006
- logger.error("The migrate command only works with the built-in Kysely adapter. For Drizzle, run `npx @better-auth/cli generate` to create the schema, then use Drizzles migrate or push to apply it.");
3072
+ console.error("The migrate command only works with the built-in Kysely adapter. For Drizzle, run `npx @better-auth/cli generate` to create the schema, then use Drizzle's migrate or push to apply it.");
3007
3073
  try {
3008
3074
  await (await createTelemetry(config)).publish({
3009
3075
  type: "cli_migrate",
@@ -3016,7 +3082,7 @@ async function migrateAction(opts) {
3016
3082
  } catch {}
3017
3083
  process.exit(0);
3018
3084
  }
3019
- logger.error("Migrate command isn't supported for this adapter.");
3085
+ console.error("Migrate command isn't supported for this adapter.");
3020
3086
  try {
3021
3087
  await (await createTelemetry(config)).publish({
3022
3088
  type: "cli_migrate",
@@ -3033,7 +3099,7 @@ async function migrateAction(opts) {
3033
3099
  const { toBeAdded, toBeCreated, runMigrations } = await getMigrations(config);
3034
3100
  if (!toBeAdded.length && !toBeCreated.length) {
3035
3101
  spinner$1.stop();
3036
- logger.info("🚀 No migrations needed.");
3102
+ console.log("🚀 No migrations needed.");
3037
3103
  try {
3038
3104
  await (await createTelemetry(config)).publish({
3039
3105
  type: "cli_migrate",
@@ -3046,7 +3112,7 @@ async function migrateAction(opts) {
3046
3112
  process.exit(0);
3047
3113
  }
3048
3114
  spinner$1.stop();
3049
- logger.info(`🔑 The migration will affect the following:`);
3115
+ console.log(`🔑 The migration will affect the following:`);
3050
3116
  for (const table of [...toBeCreated, ...toBeAdded]) console.log("->", chalk.magenta(Object.keys(table.fields).join(", ")), chalk.white("fields on"), chalk.yellow(`${table.table}`), chalk.white("table."));
3051
3117
  if (options.y) {
3052
3118
  console.warn("WARNING: --y is deprecated. Consider -y or --yes");
@@ -3060,7 +3126,7 @@ async function migrateAction(opts) {
3060
3126
  initial: false
3061
3127
  })).migrate;
3062
3128
  if (!migrate$1) {
3063
- logger.info("Migration cancelled.");
3129
+ console.log("Migration cancelled.");
3064
3130
  try {
3065
3131
  await (await createTelemetry(config)).publish({
3066
3132
  type: "cli_migrate",
@@ -3075,7 +3141,7 @@ async function migrateAction(opts) {
3075
3141
  spinner$1?.start("migrating...");
3076
3142
  await runMigrations();
3077
3143
  spinner$1.stop();
3078
- logger.info("🚀 migration was completed successfully!");
3144
+ console.log("🚀 migration was completed successfully!");
3079
3145
  try {
3080
3146
  await (await createTelemetry(config)).publish({
3081
3147
  type: "cli_migrate",
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@better-auth/cli",
3
- "version": "1.4.6-beta.2",
3
+ "version": "1.4.6",
4
4
  "type": "module",
5
5
  "description": "The CLI for Better Auth",
6
6
  "module": "dist/index.mjs",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/better-auth/better-auth",
9
+ "url": "git+https://github.com/better-auth/better-auth.git",
10
10
  "directory": "packages/cli"
11
11
  },
12
12
  "homepage": "https://www.better-auth.com/docs/concepts/cli",
@@ -33,11 +33,11 @@
33
33
  "@types/prompts": "^2.4.9",
34
34
  "@types/semver": "^7.7.1",
35
35
  "jiti": "^2.6.0",
36
- "tsdown": "^0.16.0",
36
+ "tsdown": "^0.17.0",
37
37
  "tsx": "^4.20.6",
38
38
  "type-fest": "^5.2.0",
39
39
  "typescript": "^5.9.3",
40
- "@better-auth/passkey": "1.4.6-beta.2"
40
+ "@better-auth/passkey": "1.4.6"
41
41
  },
42
42
  "dependencies": {
43
43
  "@babel/core": "^7.28.4",
@@ -57,13 +57,13 @@
57
57
  "open": "^10.2.0",
58
58
  "pg": "^8.16.3",
59
59
  "prettier": "^3.6.2",
60
- "prisma": "^5.22.0",
61
60
  "prompts": "^2.4.2",
62
61
  "semver": "^7.7.2",
63
62
  "yocto-spinner": "^0.2.3",
64
63
  "zod": "^4.1.12",
65
- "@better-auth/core": "1.4.6-beta.2",
66
- "better-auth": "1.4.6-beta.2"
64
+ "@better-auth/core": "1.4.6",
65
+ "@better-auth/telemetry": "1.4.6",
66
+ "better-auth": "^1.4.6"
67
67
  },
68
68
  "files": [
69
69
  "dist"
@@ -74,6 +74,7 @@
74
74
  "lint:package": "publint run --strict",
75
75
  "dev": "tsx ./src/index.ts",
76
76
  "test": "vitest",
77
+ "coverage": "vitest run --coverage",
77
78
  "typecheck": "tsc --project tsconfig.json"
78
79
  }
79
80
  }