@better-auth/cli 1.5.0-beta.1 → 1.5.0-beta.11
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/LICENSE.md +15 -12
- package/README.md +13 -6
- package/dist/api.d.mts +45 -0
- package/dist/api.mjs +3 -0
- package/dist/generators-BA4_Oq0Q.mjs +573 -0
- package/dist/generators-BA4_Oq0Q.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +125 -685
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -16
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-BA4_Oq0Q.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,13 @@ 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
|
|
10
|
+
import { getAdapter } from "better-auth/db/adapter";
|
|
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 {
|
|
15
|
-
import
|
|
16
|
-
import { capitalizeFirstLetter } from "@better-auth/core/utils";
|
|
17
|
-
import { produceSchema } from "@mrleebo/prisma-ast";
|
|
15
|
+
import { format } from "prettier";
|
|
16
|
+
import { getMigrations } from "better-auth/db/migration";
|
|
18
17
|
import babelPresetReact from "@babel/preset-react";
|
|
19
18
|
import babelPresetTypeScript from "@babel/preset-typescript";
|
|
20
19
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
@@ -32,568 +31,6 @@ import open from "open";
|
|
|
32
31
|
import { base64 } from "@better-auth/utils/base64";
|
|
33
32
|
import "dotenv/config";
|
|
34
33
|
|
|
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
34
|
//#region src/utils/add-cloudflare-modules.ts
|
|
598
35
|
const createModule = () => {
|
|
599
36
|
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
|
|
@@ -829,8 +266,8 @@ function getTsconfigInfo(cwd, flatPath) {
|
|
|
829
266
|
if (flatPath) tsConfigPath = flatPath;
|
|
830
267
|
else tsConfigPath = cwd ? path.join(cwd, "tsconfig.json") : path.join("tsconfig.json");
|
|
831
268
|
try {
|
|
832
|
-
const text
|
|
833
|
-
return JSON.parse(stripJsonComments(text
|
|
269
|
+
const text = fs.readFileSync(tsConfigPath, "utf-8");
|
|
270
|
+
return JSON.parse(stripJsonComments(text));
|
|
834
271
|
} catch (error) {
|
|
835
272
|
throw error;
|
|
836
273
|
}
|
|
@@ -904,7 +341,8 @@ function getPathAliasesRecursive(tsconfigPath, visited = /* @__PURE__ */ new Set
|
|
|
904
341
|
}
|
|
905
342
|
}
|
|
906
343
|
function getPathAliases(cwd) {
|
|
907
|
-
|
|
344
|
+
let tsConfigPath = path.join(cwd, "tsconfig.json");
|
|
345
|
+
if (!fs.existsSync(tsConfigPath)) tsConfigPath = path.join(cwd, "jsconfig.json");
|
|
908
346
|
if (!fs.existsSync(tsConfigPath)) return null;
|
|
909
347
|
try {
|
|
910
348
|
const result = getPathAliasesRecursive(tsConfigPath);
|
|
@@ -1024,13 +462,13 @@ async function generateAction(opts) {
|
|
|
1024
462
|
console.error(e.message);
|
|
1025
463
|
process.exit(1);
|
|
1026
464
|
});
|
|
1027
|
-
const spinner
|
|
465
|
+
const spinner = yoctoSpinner({ text: "preparing schema..." }).start();
|
|
1028
466
|
const schema = await generateSchema({
|
|
1029
467
|
adapter,
|
|
1030
468
|
file: options.output,
|
|
1031
469
|
options: config
|
|
1032
470
|
});
|
|
1033
|
-
spinner
|
|
471
|
+
spinner.stop();
|
|
1034
472
|
if (!schema.code) {
|
|
1035
473
|
console.log("Your schema is already up to date.");
|
|
1036
474
|
try {
|
|
@@ -1048,13 +486,13 @@ async function generateAction(opts) {
|
|
|
1048
486
|
process.exit(0);
|
|
1049
487
|
}
|
|
1050
488
|
if (schema.overwrite) {
|
|
1051
|
-
let confirm
|
|
1052
|
-
if (!confirm
|
|
489
|
+
let confirm = options.y || options.yes;
|
|
490
|
+
if (!confirm) confirm = (await prompts({
|
|
1053
491
|
type: "confirm",
|
|
1054
492
|
name: "confirm",
|
|
1055
493
|
message: `The file ${schema.fileName} already exists. Do you want to ${chalk.yellow(`${schema.overwrite ? "overwrite" : "append"}`)} the schema to the file?`
|
|
1056
494
|
})).confirm;
|
|
1057
|
-
if (confirm
|
|
495
|
+
if (confirm) {
|
|
1058
496
|
if (!existsSync(path.join(cwd, schema.fileName))) await fs$1.mkdir(path.dirname(path.join(cwd, schema.fileName)), { recursive: true });
|
|
1059
497
|
if (schema.overwrite) await fs$1.writeFile(path.join(cwd, schema.fileName), schema.code);
|
|
1060
498
|
else await fs$1.appendFile(path.join(cwd, schema.fileName), schema.code);
|
|
@@ -1087,13 +525,13 @@ async function generateAction(opts) {
|
|
|
1087
525
|
console.warn("WARNING: --y is deprecated. Consider -y or --yes");
|
|
1088
526
|
options.yes = true;
|
|
1089
527
|
}
|
|
1090
|
-
let confirm
|
|
1091
|
-
if (!confirm
|
|
528
|
+
let confirm = options.yes;
|
|
529
|
+
if (!confirm) confirm = (await prompts({
|
|
1092
530
|
type: "confirm",
|
|
1093
531
|
name: "confirm",
|
|
1094
532
|
message: `Do you want to generate the schema to ${chalk.yellow(schema.fileName)}?`
|
|
1095
533
|
})).confirm;
|
|
1096
|
-
if (!confirm
|
|
534
|
+
if (!confirm) {
|
|
1097
535
|
console.error("Schema generation aborted.");
|
|
1098
536
|
try {
|
|
1099
537
|
await (await createTelemetry(config)).publish({
|
|
@@ -1489,7 +927,7 @@ ${JSON.stringify(betterAuthInfo, null, 2)}
|
|
|
1489
927
|
|
|
1490
928
|
//#endregion
|
|
1491
929
|
//#region src/generators/auth-config.ts
|
|
1492
|
-
async function generateAuthConfig({ format
|
|
930
|
+
async function generateAuthConfig({ format, current_user_config, spinner, plugins, database }) {
|
|
1493
931
|
const common_indexes = {
|
|
1494
932
|
START_OF_PLUGINS: { START_OF_PLUGINS: {
|
|
1495
933
|
type: "regex",
|
|
@@ -1514,9 +952,9 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1514
952
|
};
|
|
1515
953
|
const config_generation = {
|
|
1516
954
|
add_plugin: async (opts) => {
|
|
1517
|
-
|
|
955
|
+
const start_of_plugins = getGroupInfo(opts.config, common_indexes.START_OF_PLUGINS, {});
|
|
1518
956
|
if (!start_of_plugins) throw new Error("Couldn't find start of your plugins array in your auth config file.");
|
|
1519
|
-
|
|
957
|
+
const end_of_plugins = getGroupInfo(opts.config, common_indexes.END_OF_PLUGINS, { start_of_plugins: start_of_plugins.index });
|
|
1520
958
|
if (!end_of_plugins) throw new Error("Couldn't find end of your plugins array in your auth config file.");
|
|
1521
959
|
let new_content;
|
|
1522
960
|
if (opts.direction_in_plugins_array === "prepend") new_content = insertContent({
|
|
@@ -1538,7 +976,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1538
976
|
});
|
|
1539
977
|
}
|
|
1540
978
|
try {
|
|
1541
|
-
new_content = await format
|
|
979
|
+
new_content = await format(new_content);
|
|
1542
980
|
} catch (error) {
|
|
1543
981
|
console.error(error);
|
|
1544
982
|
throw new Error(`Failed to generate new auth config during plugin addition phase.`);
|
|
@@ -1555,7 +993,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1555
993
|
else importString += `import ${import_.variables.asType ? "type " : ""}${import_.variables.name}${import_.variables.as ? ` as ${import_.variables.as}` : ""} from "${import_.path}";\n`;
|
|
1556
994
|
try {
|
|
1557
995
|
return {
|
|
1558
|
-
code: await format
|
|
996
|
+
code: await format(importString + opts.config),
|
|
1559
997
|
dependencies: [],
|
|
1560
998
|
envs: []
|
|
1561
999
|
};
|
|
@@ -1570,10 +1008,10 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1570
1008
|
let database_code_str = "";
|
|
1571
1009
|
async function add_db({ db_code, dependencies, envs, imports, code_before_betterAuth }) {
|
|
1572
1010
|
if (code_before_betterAuth) {
|
|
1573
|
-
|
|
1574
|
-
if (!start_of_betterauth
|
|
1011
|
+
const start_of_betterauth = getGroupInfo(opts.config, common_indexes.START_OF_BETTERAUTH, {});
|
|
1012
|
+
if (!start_of_betterauth) throw new Error("Couldn't find start of betterAuth() function.");
|
|
1575
1013
|
opts.config = insertContent({
|
|
1576
|
-
line: start_of_betterauth
|
|
1014
|
+
line: start_of_betterauth.line - 1,
|
|
1577
1015
|
character: 0,
|
|
1578
1016
|
content: opts.config,
|
|
1579
1017
|
insert_content: `\n${code_before_betterAuth}\n`
|
|
@@ -1718,7 +1156,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1718
1156
|
variables: [{ name: "MongoClient" }]
|
|
1719
1157
|
}]
|
|
1720
1158
|
});
|
|
1721
|
-
|
|
1159
|
+
const start_of_betterauth = getGroupInfo(opts.config, common_indexes.START_OF_BETTERAUTH, {});
|
|
1722
1160
|
if (!start_of_betterauth) throw new Error("Couldn't find start of betterAuth() function.");
|
|
1723
1161
|
let new_content;
|
|
1724
1162
|
new_content = insertContent({
|
|
@@ -1728,7 +1166,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1728
1166
|
insert_content: `database: ${database_code_str},`
|
|
1729
1167
|
});
|
|
1730
1168
|
try {
|
|
1731
|
-
new_content = await format
|
|
1169
|
+
new_content = await format(new_content);
|
|
1732
1170
|
return {
|
|
1733
1171
|
code: new_content,
|
|
1734
1172
|
dependencies: required_deps,
|
|
@@ -1740,9 +1178,9 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1740
1178
|
}
|
|
1741
1179
|
}
|
|
1742
1180
|
};
|
|
1743
|
-
let new_user_config = await format
|
|
1744
|
-
|
|
1745
|
-
|
|
1181
|
+
let new_user_config = await format(current_user_config);
|
|
1182
|
+
const total_dependencies = [];
|
|
1183
|
+
const total_envs = [];
|
|
1746
1184
|
if (plugins.length !== 0) {
|
|
1747
1185
|
const imports = [];
|
|
1748
1186
|
for await (const plugin of plugins) {
|
|
@@ -1785,7 +1223,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1785
1223
|
total_envs.push(...envs);
|
|
1786
1224
|
total_dependencies.push(...dependencies);
|
|
1787
1225
|
} catch (error) {
|
|
1788
|
-
spinner
|
|
1226
|
+
spinner.stop(`Something went wrong while generating/updating your new auth config file.`, 1);
|
|
1789
1227
|
console.error(error.message);
|
|
1790
1228
|
process.exit(1);
|
|
1791
1229
|
}
|
|
@@ -1798,7 +1236,7 @@ async function generateAuthConfig({ format: format$1, current_user_config, spinn
|
|
|
1798
1236
|
total_dependencies.push(...dependencies);
|
|
1799
1237
|
total_envs.push(...envs);
|
|
1800
1238
|
} catch (error) {
|
|
1801
|
-
spinner
|
|
1239
|
+
spinner.stop(`Something went wrong while generating/updating your new auth config file.`, 1);
|
|
1802
1240
|
console.error(error.message);
|
|
1803
1241
|
process.exit(1);
|
|
1804
1242
|
}
|
|
@@ -2186,7 +1624,7 @@ const getDefaultAuthClientConfig = async ({ auth_config_path, framework, clientP
|
|
|
2186
1624
|
}
|
|
2187
1625
|
return result;
|
|
2188
1626
|
}
|
|
2189
|
-
|
|
1627
|
+
const imports = groupImportVariables();
|
|
2190
1628
|
let importString = "";
|
|
2191
1629
|
for (const import_ of imports) if (Array.isArray(import_.variables)) importString += `import { ${import_.variables.map((x) => `${x.asType ? "type " : ""}${x.name}${x.as ? ` as ${x.as}` : ""}`).join(", ")} } from "${import_.path}";\n`;
|
|
2192
1630
|
else importString += `import ${import_.variables.asType ? "type " : ""}${import_.variables.name}${import_.variables.as ? ` as ${import_.variables.as}` : ""} from "${import_.path}";\n`;
|
|
@@ -2316,8 +1754,8 @@ async function initAction(opts) {
|
|
|
2316
1754
|
}
|
|
2317
1755
|
if (shouldInstallBetterAuthDep) {
|
|
2318
1756
|
if (packageManagerPreference === void 0) packageManagerPreference = await getPackageManager();
|
|
2319
|
-
const s
|
|
2320
|
-
s
|
|
1757
|
+
const s = spinner({ indicator: "dots" });
|
|
1758
|
+
s.start(`Updating Better Auth using ${chalk.bold(packageManagerPreference)}`);
|
|
2321
1759
|
try {
|
|
2322
1760
|
const start = Date.now();
|
|
2323
1761
|
await installDependencies({
|
|
@@ -2325,9 +1763,9 @@ async function initAction(opts) {
|
|
|
2325
1763
|
packageManager: packageManagerPreference,
|
|
2326
1764
|
cwd
|
|
2327
1765
|
});
|
|
2328
|
-
s
|
|
1766
|
+
s.stop(`Better Auth updated ${chalk.greenBright(`successfully`)}! ${chalk.gray(`(${formatMilliseconds(Date.now() - start)})`)}`);
|
|
2329
1767
|
} catch (error) {
|
|
2330
|
-
s
|
|
1768
|
+
s.stop(`Failed to update Better Auth:`);
|
|
2331
1769
|
log.error(error.message);
|
|
2332
1770
|
process.exit(1);
|
|
2333
1771
|
}
|
|
@@ -2343,26 +1781,26 @@ async function initAction(opts) {
|
|
|
2343
1781
|
}
|
|
2344
1782
|
appName = newAppName;
|
|
2345
1783
|
} else appName = packageJson.name;
|
|
2346
|
-
let possiblePaths
|
|
1784
|
+
let possiblePaths = [
|
|
2347
1785
|
"auth.ts",
|
|
2348
1786
|
"auth.tsx",
|
|
2349
1787
|
"auth.js",
|
|
2350
1788
|
"auth.jsx"
|
|
2351
1789
|
];
|
|
2352
|
-
possiblePaths
|
|
2353
|
-
...possiblePaths
|
|
2354
|
-
...possiblePaths
|
|
2355
|
-
...possiblePaths
|
|
2356
|
-
...possiblePaths
|
|
2357
|
-
...possiblePaths
|
|
1790
|
+
possiblePaths = [
|
|
1791
|
+
...possiblePaths,
|
|
1792
|
+
...possiblePaths.map((it) => `lib/server/${it}`),
|
|
1793
|
+
...possiblePaths.map((it) => `server/${it}`),
|
|
1794
|
+
...possiblePaths.map((it) => `lib/${it}`),
|
|
1795
|
+
...possiblePaths.map((it) => `utils/${it}`)
|
|
2358
1796
|
];
|
|
2359
|
-
possiblePaths
|
|
2360
|
-
...possiblePaths
|
|
2361
|
-
...possiblePaths
|
|
2362
|
-
...possiblePaths
|
|
1797
|
+
possiblePaths = [
|
|
1798
|
+
...possiblePaths,
|
|
1799
|
+
...possiblePaths.map((it) => `src/${it}`),
|
|
1800
|
+
...possiblePaths.map((it) => `app/${it}`)
|
|
2363
1801
|
];
|
|
2364
1802
|
if (options.config) config_path = path.join(cwd, options.config);
|
|
2365
|
-
else for (const possiblePath of possiblePaths
|
|
1803
|
+
else for (const possiblePath of possiblePaths) if (existsSync(path.join(cwd, possiblePath))) {
|
|
2366
1804
|
config_path = path.join(cwd, possiblePath);
|
|
2367
1805
|
break;
|
|
2368
1806
|
}
|
|
@@ -2509,9 +1947,9 @@ async function initAction(opts) {
|
|
|
2509
1947
|
process.exit(0);
|
|
2510
1948
|
}
|
|
2511
1949
|
if (shouldInstallDeps) {
|
|
2512
|
-
const s
|
|
1950
|
+
const s = spinner({ indicator: "dots" });
|
|
2513
1951
|
if (packageManagerPreference === void 0) packageManagerPreference = await getPackageManager();
|
|
2514
|
-
s
|
|
1952
|
+
s.start(`Installing dependencies using ${chalk.bold(packageManagerPreference)}...`);
|
|
2515
1953
|
try {
|
|
2516
1954
|
const start = Date.now();
|
|
2517
1955
|
await installDependencies({
|
|
@@ -2519,9 +1957,9 @@ async function initAction(opts) {
|
|
|
2519
1957
|
packageManager: packageManagerPreference,
|
|
2520
1958
|
cwd
|
|
2521
1959
|
});
|
|
2522
|
-
s
|
|
1960
|
+
s.stop(`Dependencies installed ${chalk.greenBright(`successfully`)} ${chalk.gray(`(${formatMilliseconds(Date.now() - start)})`)}`);
|
|
2523
1961
|
} catch (error) {
|
|
2524
|
-
s
|
|
1962
|
+
s.stop(`Failed to install dependencies using ${packageManagerPreference}:`);
|
|
2525
1963
|
log.error(error.message);
|
|
2526
1964
|
process.exit(1);
|
|
2527
1965
|
}
|
|
@@ -2584,13 +2022,13 @@ async function initAction(opts) {
|
|
|
2584
2022
|
authClientConfigPath = path.join(cwd, "auth-client.ts");
|
|
2585
2023
|
log.info(`Creating auth client config file: ${authClientConfigPath}`);
|
|
2586
2024
|
try {
|
|
2587
|
-
|
|
2025
|
+
const contents = await getDefaultAuthClientConfig({
|
|
2588
2026
|
auth_config_path: ("./" + path.join(config_path.replace(cwd, ""))).replace(".//", "./"),
|
|
2589
2027
|
clientPlugins: add_plugins.filter((x) => x.clientName).map((plugin) => {
|
|
2590
|
-
let contents
|
|
2591
|
-
if (plugin.id === "one-tap") contents
|
|
2028
|
+
let contents = "";
|
|
2029
|
+
if (plugin.id === "one-tap") contents = `{ clientId: "MY_CLIENT_ID" }`;
|
|
2592
2030
|
return {
|
|
2593
|
-
contents
|
|
2031
|
+
contents,
|
|
2594
2032
|
id: plugin.id,
|
|
2595
2033
|
name: plugin.clientName,
|
|
2596
2034
|
imports: [{
|
|
@@ -2643,7 +2081,7 @@ async function initAction(opts) {
|
|
|
2643
2081
|
cancel(`✋ Operation cancelled.`);
|
|
2644
2082
|
process.exit(0);
|
|
2645
2083
|
}
|
|
2646
|
-
|
|
2084
|
+
const envs = [];
|
|
2647
2085
|
if (isMissingSecret) envs.push("BETTER_AUTH_SECRET");
|
|
2648
2086
|
if (isMissingUrl) envs.push("BETTER_AUTH_URL");
|
|
2649
2087
|
if (shouldAdd === "yes") {
|
|
@@ -2727,7 +2165,7 @@ async function getPackageManager() {
|
|
|
2727
2165
|
value: "npm",
|
|
2728
2166
|
hint: "not recommended"
|
|
2729
2167
|
});
|
|
2730
|
-
|
|
2168
|
+
const packageManager = await select({
|
|
2731
2169
|
message: "Choose a package manager",
|
|
2732
2170
|
options: packageManagerOptions
|
|
2733
2171
|
});
|
|
@@ -2794,14 +2232,14 @@ async function loginAction(opts) {
|
|
|
2794
2232
|
baseURL: serverUrl,
|
|
2795
2233
|
plugins: [deviceAuthorizationClient()]
|
|
2796
2234
|
});
|
|
2797
|
-
const spinner
|
|
2798
|
-
spinner
|
|
2235
|
+
const spinner = yoctoSpinner({ text: "Requesting device authorization..." });
|
|
2236
|
+
spinner.start();
|
|
2799
2237
|
try {
|
|
2800
2238
|
const { data, error } = await authClient.device.code({
|
|
2801
2239
|
client_id: clientId,
|
|
2802
2240
|
scope: "openid profile email"
|
|
2803
2241
|
});
|
|
2804
|
-
spinner
|
|
2242
|
+
spinner.stop();
|
|
2805
2243
|
if (error || !data) {
|
|
2806
2244
|
console.error(`Failed to request device authorization: ${error?.error_description || "Unknown error"}`);
|
|
2807
2245
|
process.exit(1);
|
|
@@ -2828,14 +2266,14 @@ async function loginAction(opts) {
|
|
|
2828
2266
|
console.log(chalk.blue("\nFor more information, visit: https://better-auth.com/docs/plugins/device-authorization"));
|
|
2829
2267
|
}
|
|
2830
2268
|
} catch (err) {
|
|
2831
|
-
spinner
|
|
2269
|
+
spinner.stop();
|
|
2832
2270
|
console.error(`Login failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
2833
2271
|
process.exit(1);
|
|
2834
2272
|
}
|
|
2835
2273
|
}
|
|
2836
2274
|
async function pollForToken(authClient, deviceCode, clientId, initialInterval) {
|
|
2837
2275
|
let pollingInterval = initialInterval;
|
|
2838
|
-
const spinner
|
|
2276
|
+
const spinner = yoctoSpinner({
|
|
2839
2277
|
text: "",
|
|
2840
2278
|
color: "cyan"
|
|
2841
2279
|
});
|
|
@@ -2843,8 +2281,8 @@ async function pollForToken(authClient, deviceCode, clientId, initialInterval) {
|
|
|
2843
2281
|
return new Promise((resolve, reject) => {
|
|
2844
2282
|
const poll = async () => {
|
|
2845
2283
|
dots = (dots + 1) % 4;
|
|
2846
|
-
spinner
|
|
2847
|
-
if (!spinner
|
|
2284
|
+
spinner.text = chalk.gray(`Polling for authorization${".".repeat(dots)}${" ".repeat(3 - dots)}`);
|
|
2285
|
+
if (!spinner.isSpinning) spinner.start();
|
|
2848
2286
|
try {
|
|
2849
2287
|
const { data, error } = await authClient.device.token({
|
|
2850
2288
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
@@ -2853,32 +2291,32 @@ async function pollForToken(authClient, deviceCode, clientId, initialInterval) {
|
|
|
2853
2291
|
fetchOptions: { headers: { "user-agent": `Better Auth CLI` } }
|
|
2854
2292
|
});
|
|
2855
2293
|
if (data?.access_token) {
|
|
2856
|
-
spinner
|
|
2294
|
+
spinner.stop();
|
|
2857
2295
|
resolve(data);
|
|
2858
2296
|
return;
|
|
2859
2297
|
} else if (error) switch (error.error) {
|
|
2860
2298
|
case "authorization_pending": break;
|
|
2861
2299
|
case "slow_down":
|
|
2862
2300
|
pollingInterval += 5;
|
|
2863
|
-
spinner
|
|
2301
|
+
spinner.text = chalk.yellow(`Slowing down polling to ${pollingInterval}s`);
|
|
2864
2302
|
break;
|
|
2865
2303
|
case "access_denied":
|
|
2866
|
-
spinner
|
|
2304
|
+
spinner.stop();
|
|
2867
2305
|
console.error("Access was denied by the user");
|
|
2868
2306
|
process.exit(1);
|
|
2869
2307
|
break;
|
|
2870
2308
|
case "expired_token":
|
|
2871
|
-
spinner
|
|
2309
|
+
spinner.stop();
|
|
2872
2310
|
console.error("The device code has expired. Please try again.");
|
|
2873
2311
|
process.exit(1);
|
|
2874
2312
|
break;
|
|
2875
2313
|
default:
|
|
2876
|
-
spinner
|
|
2314
|
+
spinner.stop();
|
|
2877
2315
|
console.error(`Error: ${error.error_description}`);
|
|
2878
2316
|
process.exit(1);
|
|
2879
2317
|
}
|
|
2880
2318
|
} catch (err) {
|
|
2881
|
-
spinner
|
|
2319
|
+
spinner.stop();
|
|
2882
2320
|
console.error(`Network error: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
2883
2321
|
process.exit(1);
|
|
2884
2322
|
}
|
|
@@ -2913,52 +2351,50 @@ const login = new Command("login").description("Demo: Test device authorization
|
|
|
2913
2351
|
|
|
2914
2352
|
//#endregion
|
|
2915
2353
|
//#region src/commands/mcp.ts
|
|
2354
|
+
const REMOTE_MCP_URL = "https://mcp.inkeep.com/better-auth/mcp";
|
|
2916
2355
|
async function mcpAction(options) {
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
if (options.
|
|
2920
|
-
else if (options.
|
|
2921
|
-
else
|
|
2922
|
-
else if (options.manual) handleManualAction(mcpUrl, mcpName);
|
|
2923
|
-
else showAllOptions(mcpUrl, mcpName);
|
|
2356
|
+
if (options.cursor) await handleCursorAction();
|
|
2357
|
+
else if (options.claudeCode) handleClaudeCodeAction();
|
|
2358
|
+
else if (options.openCode) handleOpenCodeAction();
|
|
2359
|
+
else if (options.manual) handleManualAction();
|
|
2360
|
+
else showAllOptions();
|
|
2924
2361
|
}
|
|
2925
|
-
async function handleCursorAction(
|
|
2926
|
-
const mcpConfig = { url: mcpUrl };
|
|
2927
|
-
const encodedConfig = base64.encode(new TextEncoder().encode(JSON.stringify(mcpConfig)));
|
|
2928
|
-
const deeplinkUrl = `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(mcpName)}&config=${encodedConfig}`;
|
|
2362
|
+
async function handleCursorAction() {
|
|
2929
2363
|
console.log(chalk.bold.blue("🚀 Adding Better Auth MCP to Cursor..."));
|
|
2364
|
+
const platform = os$1.platform();
|
|
2365
|
+
let openCommand;
|
|
2366
|
+
switch (platform) {
|
|
2367
|
+
case "darwin":
|
|
2368
|
+
openCommand = "open";
|
|
2369
|
+
break;
|
|
2370
|
+
case "win32":
|
|
2371
|
+
openCommand = "start";
|
|
2372
|
+
break;
|
|
2373
|
+
case "linux":
|
|
2374
|
+
openCommand = "xdg-open";
|
|
2375
|
+
break;
|
|
2376
|
+
default: throw new Error(`Unsupported platform: ${platform}`);
|
|
2377
|
+
}
|
|
2378
|
+
const remoteConfig = { url: REMOTE_MCP_URL };
|
|
2379
|
+
const encodedRemote = base64.encode(new TextEncoder().encode(JSON.stringify(remoteConfig)));
|
|
2380
|
+
const remoteDeeplink = `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent("better-auth")}&config=${encodedRemote}`;
|
|
2930
2381
|
try {
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
switch (platform) {
|
|
2934
|
-
case "darwin":
|
|
2935
|
-
command = `open "${deeplinkUrl}"`;
|
|
2936
|
-
break;
|
|
2937
|
-
case "win32":
|
|
2938
|
-
command = `start "" "${deeplinkUrl}"`;
|
|
2939
|
-
break;
|
|
2940
|
-
case "linux":
|
|
2941
|
-
command = `xdg-open "${deeplinkUrl}"`;
|
|
2942
|
-
break;
|
|
2943
|
-
default: throw new Error(`Unsupported platform: ${platform}`);
|
|
2944
|
-
}
|
|
2945
|
-
execSync(command, { stdio: "inherit" });
|
|
2946
|
-
console.log(chalk.green("\n✓ Cursor MCP installed successfully!"));
|
|
2382
|
+
execSync(platform === "win32" ? `start "" "${remoteDeeplink}"` : `${openCommand} "${remoteDeeplink}"`, { stdio: "inherit" });
|
|
2383
|
+
console.log(chalk.green("\n✓ Better Auth MCP server installed!"));
|
|
2947
2384
|
} catch {
|
|
2948
|
-
console.log(chalk.yellow("\n⚠ Could not automatically open Cursor
|
|
2949
|
-
console.log(chalk.gray("\nYou can also manually add this configuration to your Cursor MCP settings:"));
|
|
2950
|
-
console.log(chalk.gray(JSON.stringify(mcpConfig, null, 2)));
|
|
2385
|
+
console.log(chalk.yellow("\n⚠ Could not automatically open Cursor for MCP installation."));
|
|
2951
2386
|
}
|
|
2952
2387
|
console.log(chalk.bold.white("\n✨ Next Steps:"));
|
|
2953
2388
|
console.log(chalk.gray("• The MCP server will be added to your Cursor configuration"));
|
|
2954
2389
|
console.log(chalk.gray("• You can now use Better Auth features directly in Cursor"));
|
|
2390
|
+
console.log(chalk.gray("• Try: \"Set up Better Auth with Google login\" or \"Help me debug my auth\""));
|
|
2955
2391
|
}
|
|
2956
|
-
function handleClaudeCodeAction(
|
|
2392
|
+
function handleClaudeCodeAction() {
|
|
2957
2393
|
console.log(chalk.bold.blue("🤖 Adding Better Auth MCP to Claude Code..."));
|
|
2958
|
-
const command = `claude mcp add --transport http better-auth ${
|
|
2394
|
+
const command = `claude mcp add --transport http better-auth ${REMOTE_MCP_URL}`;
|
|
2959
2395
|
try {
|
|
2960
2396
|
execSync(command, { stdio: "inherit" });
|
|
2961
|
-
console.log(chalk.green("\n✓ Claude Code MCP
|
|
2397
|
+
console.log(chalk.green("\n✓ Claude Code MCP configured!"));
|
|
2962
2398
|
} catch {
|
|
2963
2399
|
console.log(chalk.yellow("\n⚠ Could not automatically add to Claude Code. Please run this command manually:"));
|
|
2964
2400
|
console.log(chalk.cyan(command));
|
|
@@ -2967,13 +2403,13 @@ function handleClaudeCodeAction(mcpUrl) {
|
|
|
2967
2403
|
console.log(chalk.gray("• The MCP server will be added to your Claude Code configuration"));
|
|
2968
2404
|
console.log(chalk.gray("• You can now use Better Auth features directly in Claude Code"));
|
|
2969
2405
|
}
|
|
2970
|
-
function handleOpenCodeAction(
|
|
2406
|
+
function handleOpenCodeAction() {
|
|
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: { "
|
|
2410
|
+
mcp: { "better-auth": {
|
|
2975
2411
|
type: "remote",
|
|
2976
|
-
url:
|
|
2412
|
+
url: REMOTE_MCP_URL,
|
|
2977
2413
|
enabled: true
|
|
2978
2414
|
} }
|
|
2979
2415
|
};
|
|
@@ -2994,7 +2430,7 @@ function handleOpenCodeAction(mcpUrl) {
|
|
|
2994
2430
|
};
|
|
2995
2431
|
fs$2.writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2));
|
|
2996
2432
|
console.log(chalk.green(`\n✓ Open Code configuration written to ${configPath}`));
|
|
2997
|
-
console.log(chalk.green("✓ Better Auth MCP added successfully!"));
|
|
2433
|
+
console.log(chalk.green("✓ Better Auth MCP server added successfully!"));
|
|
2998
2434
|
} catch {
|
|
2999
2435
|
console.log(chalk.yellow("\n⚠ Could not automatically write opencode.json. Please add this configuration manually:"));
|
|
3000
2436
|
console.log(chalk.cyan(JSON.stringify(openCodeConfig, null, 2)));
|
|
@@ -3003,9 +2439,9 @@ function handleOpenCodeAction(mcpUrl) {
|
|
|
3003
2439
|
console.log(chalk.gray("• Restart Open Code to load the new MCP server"));
|
|
3004
2440
|
console.log(chalk.gray("• You can now use Better Auth features directly in Open Code"));
|
|
3005
2441
|
}
|
|
3006
|
-
function handleManualAction(
|
|
3007
|
-
console.log(chalk.bold.blue("📝
|
|
3008
|
-
const manualConfig = {
|
|
2442
|
+
function handleManualAction() {
|
|
2443
|
+
console.log(chalk.bold.blue("📝 Better Auth MCP Configuration..."));
|
|
2444
|
+
const manualConfig = { "better-auth": { url: REMOTE_MCP_URL } };
|
|
3009
2445
|
const configPath = path$1.join(process.cwd(), "mcp.json");
|
|
3010
2446
|
try {
|
|
3011
2447
|
let existingConfig = {};
|
|
@@ -3019,7 +2455,7 @@ function handleManualAction(mcpUrl, mcpName) {
|
|
|
3019
2455
|
};
|
|
3020
2456
|
fs$2.writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2));
|
|
3021
2457
|
console.log(chalk.green(`\n✓ MCP configuration written to ${configPath}`));
|
|
3022
|
-
console.log(chalk.green("✓ Better Auth MCP added successfully!"));
|
|
2458
|
+
console.log(chalk.green("✓ Better Auth MCP server added successfully!"));
|
|
3023
2459
|
} catch {
|
|
3024
2460
|
console.log(chalk.yellow("\n⚠ Could not automatically write mcp.json. Please add this configuration manually:"));
|
|
3025
2461
|
console.log(chalk.cyan(JSON.stringify(manualConfig, null, 2)));
|
|
@@ -3028,16 +2464,19 @@ function handleManualAction(mcpUrl, mcpName) {
|
|
|
3028
2464
|
console.log(chalk.gray("• Restart your MCP client to load the new server"));
|
|
3029
2465
|
console.log(chalk.gray("• You can now use Better Auth features directly in your MCP client"));
|
|
3030
2466
|
}
|
|
3031
|
-
function showAllOptions(
|
|
2467
|
+
function showAllOptions() {
|
|
3032
2468
|
console.log(chalk.bold.blue("🔌 Better Auth MCP Server"));
|
|
3033
2469
|
console.log(chalk.gray("Choose your MCP client to get started:"));
|
|
3034
2470
|
console.log();
|
|
3035
|
-
console.log(chalk.bold.white("
|
|
2471
|
+
console.log(chalk.bold.white("MCP Clients:"));
|
|
3036
2472
|
console.log(chalk.cyan(" --cursor ") + chalk.gray("Add to Cursor"));
|
|
3037
2473
|
console.log(chalk.cyan(" --claude-code ") + chalk.gray("Add to Claude Code"));
|
|
3038
2474
|
console.log(chalk.cyan(" --open-code ") + chalk.gray("Add to Open Code"));
|
|
3039
2475
|
console.log(chalk.cyan(" --manual ") + chalk.gray("Manual configuration"));
|
|
3040
2476
|
console.log();
|
|
2477
|
+
console.log(chalk.bold.white("Server:"));
|
|
2478
|
+
console.log(chalk.gray(" • ") + chalk.white("better-auth") + chalk.gray(" - Search documentation, code examples, setup assistance"));
|
|
2479
|
+
console.log();
|
|
3041
2480
|
}
|
|
3042
2481
|
const mcp = new Command("mcp").description("Add Better Auth MCP server to MCP Clients").option("--cursor", "Automatically open Cursor with the MCP configuration").option("--claude-code", "Show Claude Code MCP configuration command").option("--open-code", "Show Open Code MCP configuration").option("--manual", "Show manual MCP configuration for mcp.json").action(mcpAction);
|
|
3043
2482
|
|
|
@@ -3111,10 +2550,10 @@ async function migrateAction(opts) {
|
|
|
3111
2550
|
} catch {}
|
|
3112
2551
|
process.exit(1);
|
|
3113
2552
|
}
|
|
3114
|
-
const spinner
|
|
2553
|
+
const spinner = yoctoSpinner({ text: "preparing migration..." }).start();
|
|
3115
2554
|
const { toBeAdded, toBeCreated, runMigrations } = await getMigrations(config);
|
|
3116
2555
|
if (!toBeAdded.length && !toBeCreated.length) {
|
|
3117
|
-
spinner
|
|
2556
|
+
spinner.stop();
|
|
3118
2557
|
console.log("🚀 No migrations needed.");
|
|
3119
2558
|
try {
|
|
3120
2559
|
await (await createTelemetry(config)).publish({
|
|
@@ -3127,21 +2566,21 @@ async function migrateAction(opts) {
|
|
|
3127
2566
|
} catch {}
|
|
3128
2567
|
process.exit(0);
|
|
3129
2568
|
}
|
|
3130
|
-
spinner
|
|
2569
|
+
spinner.stop();
|
|
3131
2570
|
console.log(`🔑 The migration will affect the following:`);
|
|
3132
2571
|
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."));
|
|
3133
2572
|
if (options.y) {
|
|
3134
2573
|
console.warn("WARNING: --y is deprecated. Consider -y or --yes");
|
|
3135
2574
|
options.yes = true;
|
|
3136
2575
|
}
|
|
3137
|
-
let migrate
|
|
3138
|
-
if (!migrate
|
|
2576
|
+
let migrate = options.yes;
|
|
2577
|
+
if (!migrate) migrate = (await prompts({
|
|
3139
2578
|
type: "confirm",
|
|
3140
2579
|
name: "migrate",
|
|
3141
2580
|
message: "Are you sure you want to run these migrations?",
|
|
3142
2581
|
initial: false
|
|
3143
2582
|
})).migrate;
|
|
3144
|
-
if (!migrate
|
|
2583
|
+
if (!migrate) {
|
|
3145
2584
|
console.log("Migration cancelled.");
|
|
3146
2585
|
try {
|
|
3147
2586
|
await (await createTelemetry(config)).publish({
|
|
@@ -3154,9 +2593,9 @@ async function migrateAction(opts) {
|
|
|
3154
2593
|
} catch {}
|
|
3155
2594
|
process.exit(0);
|
|
3156
2595
|
}
|
|
3157
|
-
spinner
|
|
2596
|
+
spinner?.start("migrating...");
|
|
3158
2597
|
await runMigrations();
|
|
3159
|
-
spinner
|
|
2598
|
+
spinner.stop();
|
|
3160
2599
|
console.log("🚀 migration was completed successfully!");
|
|
3161
2600
|
try {
|
|
3162
2601
|
await (await createTelemetry(config)).publish({
|
|
@@ -3190,4 +2629,5 @@ main().catch((error) => {
|
|
|
3190
2629
|
});
|
|
3191
2630
|
|
|
3192
2631
|
//#endregion
|
|
3193
|
-
export { };
|
|
2632
|
+
export { };
|
|
2633
|
+
//# sourceMappingURL=index.mjs.map
|