@better-auth/cli 1.4.0-beta.20 → 1.4.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +88 -17
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import path from "path";
|
|
|
11
11
|
import prompts from "prompts";
|
|
12
12
|
import yoctoSpinner from "yocto-spinner";
|
|
13
13
|
import * as z from "zod/v4";
|
|
14
|
+
import { initGetFieldName, initGetModelName } from "better-auth/adapters";
|
|
14
15
|
import prettier, { format } from "prettier";
|
|
15
16
|
import { produceSchema } from "@mrleebo/prisma-ast";
|
|
16
17
|
import babelPresetReact from "@babel/preset-react";
|
|
@@ -45,9 +46,17 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
45
46
|
tables,
|
|
46
47
|
options
|
|
47
48
|
});
|
|
49
|
+
const getModelName = initGetModelName({
|
|
50
|
+
schema: tables,
|
|
51
|
+
usePlural: adapter.options?.adapterConfig?.usePlural
|
|
52
|
+
});
|
|
53
|
+
const getFieldName = initGetFieldName({
|
|
54
|
+
schema: tables,
|
|
55
|
+
usePlural: adapter.options?.adapterConfig?.usePlural
|
|
56
|
+
});
|
|
48
57
|
for (const tableKey in tables) {
|
|
49
58
|
const table = tables[tableKey];
|
|
50
|
-
const modelName = getModelName(
|
|
59
|
+
const modelName = getModelName(tableKey);
|
|
51
60
|
const fields = table.fields;
|
|
52
61
|
function getType(name, field) {
|
|
53
62
|
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`);
|
|
@@ -112,7 +121,7 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
112
121
|
let id = "";
|
|
113
122
|
const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
|
|
114
123
|
if (options.advanced?.database?.generateId === "uuid" && databaseType === "pg") id = `uuid("id").default(sql\`pg_catalog.gen_random_uuid()\`).primaryKey()`;
|
|
115
|
-
else if (useNumberId) if (databaseType === "pg") id = `
|
|
124
|
+
else if (useNumberId) if (databaseType === "pg") id = `integer("id").generatedByDefaultAsIdentity().primaryKey()`;
|
|
116
125
|
else if (databaseType === "sqlite") id = `integer("id", { mode: "number" }).primaryKey({ autoIncrement: true })`;
|
|
117
126
|
else id = `int("id").autoincrement().primaryKey()`;
|
|
118
127
|
else if (databaseType === "mysql") id = `varchar('id', { length: 36 }).primaryKey()`;
|
|
@@ -150,11 +159,61 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
150
159
|
if (attr.onUpdate && attr.type === "date") {
|
|
151
160
|
if (typeof attr.onUpdate === "function") type += `.$onUpdate(${attr.onUpdate})`;
|
|
152
161
|
}
|
|
153
|
-
return `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${getModelName(
|
|
162
|
+
return `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${getModelName(attr.references.model)}.${getFieldName({
|
|
163
|
+
model: attr.references.model,
|
|
164
|
+
field: attr.references.field
|
|
165
|
+
})}, { onDelete: '${attr.references.onDelete || "cascade"}' })` : ""}`;
|
|
154
166
|
}).join(",\n ")}
|
|
155
167
|
}${assignIndexes(indexes)});`;
|
|
156
168
|
code += `\n${schema}\n`;
|
|
157
169
|
}
|
|
170
|
+
let relationsString = "";
|
|
171
|
+
for (const tableKey in tables) {
|
|
172
|
+
const table = tables[tableKey];
|
|
173
|
+
const relations = [];
|
|
174
|
+
const foreignFields = Object.entries(table.fields).filter(([_, field]) => field.references);
|
|
175
|
+
for (const [fieldName, field] of foreignFields) {
|
|
176
|
+
const referencedModel = field.references.model;
|
|
177
|
+
relations.push({
|
|
178
|
+
key: getModelName(referencedModel),
|
|
179
|
+
model: getModelName(referencedModel),
|
|
180
|
+
type: "one",
|
|
181
|
+
reference: {
|
|
182
|
+
field: `${getModelName(tableKey)}.${getFieldName({
|
|
183
|
+
model: tableKey,
|
|
184
|
+
field: fieldName
|
|
185
|
+
})}`,
|
|
186
|
+
references: `${getModelName(referencedModel)}.${getFieldName({
|
|
187
|
+
model: referencedModel,
|
|
188
|
+
field: field.references.field || "id"
|
|
189
|
+
})}`
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const otherModels = Object.entries(tables).filter(([modelName]) => modelName !== tableKey);
|
|
194
|
+
for (const [modelName, otherTable] of otherModels) {
|
|
195
|
+
const foreignKeysPointingHere = Object.entries(otherTable.fields).filter(([_, field]) => field.references?.model === tableKey || field.references?.model === getModelName(tableKey));
|
|
196
|
+
for (const [fieldName, field] of foreignKeysPointingHere) {
|
|
197
|
+
const isUnique = !!field.unique;
|
|
198
|
+
const relationKey = isUnique ? getModelName(modelName) : `${getModelName(modelName)}s`;
|
|
199
|
+
relations.push({
|
|
200
|
+
key: relationKey,
|
|
201
|
+
model: getModelName(modelName),
|
|
202
|
+
type: isUnique ? "one" : "many"
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const hasOne = relations.some((relation) => relation.type === "one");
|
|
207
|
+
const hasMany = relations.some((relation) => relation.type === "many");
|
|
208
|
+
let tableRelation = `export const ${table.modelName}Relations = relations(${getModelName(table.modelName)}, ({ ${hasOne ? "one" : ""}${hasMany ? `${hasOne ? ", " : ""}many` : ""} }) => ({
|
|
209
|
+
${relations.map(({ key, type, model, reference }) => ` ${key}: ${type}(${model}${!reference ? "" : `, {
|
|
210
|
+
fields: [${reference.field}],
|
|
211
|
+
references: [${reference.references}],
|
|
212
|
+
}`})`).join(",\n ")}
|
|
213
|
+
}))`;
|
|
214
|
+
if (relations.length > 0) relationsString += `\n${tableRelation}\n`;
|
|
215
|
+
}
|
|
216
|
+
code += `\n${relationsString}`;
|
|
158
217
|
return {
|
|
159
218
|
code: await prettier.format(code, { parser: "typescript" }),
|
|
160
219
|
fileName: filePath,
|
|
@@ -162,7 +221,7 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
162
221
|
};
|
|
163
222
|
};
|
|
164
223
|
function generateImport({ databaseType, tables, options }) {
|
|
165
|
-
const rootImports = [];
|
|
224
|
+
const rootImports = ["relations"];
|
|
166
225
|
const coreImports = [];
|
|
167
226
|
let hasBigint = false;
|
|
168
227
|
let hasJson = false;
|
|
@@ -189,10 +248,7 @@ function generateImport({ databaseType, tables, options }) {
|
|
|
189
248
|
const hasFkToId = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.references?.field === "id"));
|
|
190
249
|
if (hasNonBigintNumber || (options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial") && hasFkToId) coreImports.push("integer");
|
|
191
250
|
} else coreImports.push("integer");
|
|
192
|
-
if (databaseType === "pg")
|
|
193
|
-
if (useNumberId) coreImports.push("serial");
|
|
194
|
-
else if (useUUIDs) coreImports.push("uuid");
|
|
195
|
-
}
|
|
251
|
+
if (databaseType === "pg" && useUUIDs) coreImports.push("uuid");
|
|
196
252
|
if (hasJson) {
|
|
197
253
|
if (databaseType === "pg") coreImports.push("jsonb");
|
|
198
254
|
if (databaseType === "mysql") coreImports.push("json");
|
|
@@ -204,9 +260,6 @@ function generateImport({ databaseType, tables, options }) {
|
|
|
204
260
|
if (hasUniqueIndexes) coreImports.push("uniqueIndex");
|
|
205
261
|
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`;
|
|
206
262
|
}
|
|
207
|
-
function getModelName(modelName, options) {
|
|
208
|
-
return options?.usePlural ? `${modelName}s` : modelName;
|
|
209
|
-
}
|
|
210
263
|
|
|
211
264
|
//#endregion
|
|
212
265
|
//#region src/generators/kysely.ts
|
|
@@ -278,9 +331,17 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
278
331
|
const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
|
|
279
332
|
const useUUIDs = options.advanced?.database?.generateId === "uuid";
|
|
280
333
|
if (useNumberId) builder.model(modelName).field("id", "Int").attribute("id").attribute("default(autoincrement())");
|
|
281
|
-
else if (useUUIDs && provider === "postgresql") builder.model(modelName).field("id", "String").attribute("id").attribute("
|
|
334
|
+
else if (useUUIDs && provider === "postgresql") builder.model(modelName).field("id", "String").attribute("id").attribute("default(dbgenerated(\"pg_catalog.gen_random_uuid()\"))").attribute("db.Uuid");
|
|
282
335
|
else builder.model(modelName).field("id", "String").attribute("id");
|
|
283
336
|
}
|
|
337
|
+
const getModelName = initGetModelName({
|
|
338
|
+
schema: getAuthTables(options),
|
|
339
|
+
usePlural: adapter.options?.adapterConfig?.usePlural
|
|
340
|
+
});
|
|
341
|
+
const getFieldName = initGetFieldName({
|
|
342
|
+
schema: getAuthTables(options),
|
|
343
|
+
usePlural: false
|
|
344
|
+
});
|
|
284
345
|
for (const field in fields) {
|
|
285
346
|
const attr = fields[field];
|
|
286
347
|
const fieldName = attr.fieldName || field;
|
|
@@ -345,25 +406,35 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
345
406
|
if (field === "updatedAt" && attr.onUpdate) fieldBuilder.attribute("updatedAt");
|
|
346
407
|
else if (attr.onUpdate) {}
|
|
347
408
|
if (attr.references) {
|
|
348
|
-
if (useUUIDs && provider === "postgresql")
|
|
349
|
-
const referencedOriginalModelName = attr.references.model;
|
|
409
|
+
if (useUUIDs && provider === "postgresql" && attr.references?.field === "id") builder.model(modelName).field(fieldName).attribute(`db.Uuid`);
|
|
410
|
+
const referencedOriginalModelName = getModelName(attr.references.model);
|
|
350
411
|
const referencedCustomModelName = tables[referencedOriginalModelName]?.modelName || referencedOriginalModelName;
|
|
351
412
|
let action = "Cascade";
|
|
352
413
|
if (attr.references.onDelete === "no action") action = "NoAction";
|
|
353
414
|
else if (attr.references.onDelete === "set null") action = "SetNull";
|
|
354
415
|
else if (attr.references.onDelete === "set default") action = "SetDefault";
|
|
355
416
|
else if (attr.references.onDelete === "restrict") action = "Restrict";
|
|
356
|
-
const relationField = `relation(fields: [${
|
|
417
|
+
const relationField = `relation(fields: [${getFieldName({
|
|
418
|
+
model: originalTableName,
|
|
419
|
+
field: fieldName
|
|
420
|
+
})}], references: [${getFieldName({
|
|
421
|
+
model: attr.references.model,
|
|
422
|
+
field: attr.references.field
|
|
423
|
+
})}], onDelete: ${action})`;
|
|
357
424
|
builder.model(modelName).field(referencedCustomModelName.toLowerCase(), `${capitalizeFirstLetter(referencedCustomModelName)}${!attr.required ? "?" : ""}`).attribute(relationField);
|
|
358
425
|
}
|
|
359
426
|
if (!attr.unique && !attr.references && provider === "mysql" && attr.type === "string") builder.model(modelName).field(fieldName).attribute("db.Text");
|
|
360
427
|
}
|
|
361
428
|
if (manyToManyRelations.has(modelName)) for (const relatedModel of manyToManyRelations.get(modelName)) {
|
|
362
|
-
const
|
|
429
|
+
const relatedTableName = Object.keys(tables).find((key) => capitalizeFirstLetter(tables[key]?.modelName || key) === relatedModel);
|
|
430
|
+
const relatedFields = relatedTableName ? tables[relatedTableName]?.fields : {};
|
|
431
|
+
const [_fieldKey, fkFieldAttr] = Object.entries(relatedFields || {}).find(([_fieldName, fieldAttr]) => fieldAttr.references && getModelName(fieldAttr.references.model) === getModelName(originalTableName)) || [];
|
|
432
|
+
const isUnique = fkFieldAttr?.unique === true;
|
|
433
|
+
const fieldName = isUnique ? `${relatedModel.toLowerCase()}` : `${relatedModel.toLowerCase()}s`;
|
|
363
434
|
if (!builder.findByType("field", {
|
|
364
435
|
name: fieldName,
|
|
365
436
|
within: prismaModel?.properties
|
|
366
|
-
})) builder.model(modelName).field(fieldName, `${relatedModel}[]`);
|
|
437
|
+
})) builder.model(modelName).field(fieldName, `${relatedModel}${isUnique ? "?" : "[]"}`);
|
|
367
438
|
}
|
|
368
439
|
const indexedFieldsForModel = indexedFields.get(modelName);
|
|
369
440
|
if (indexedFieldsForModel && indexedFieldsForModel.length > 0) for (const fieldName of indexedFieldsForModel) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/cli",
|
|
3
|
-
"version": "1.4.0-beta.
|
|
3
|
+
"version": "1.4.0-beta.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The CLI for Better Auth",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"tsx": "^4.20.6",
|
|
35
35
|
"type-fest": "^5.2.0",
|
|
36
36
|
"typescript": "^5.9.3",
|
|
37
|
-
"@better-auth/passkey": "1.4.0-beta.
|
|
37
|
+
"@better-auth/passkey": "1.4.0-beta.22"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@babel/core": "^7.28.4",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"tinyexec": "^0.3.2",
|
|
63
63
|
"yocto-spinner": "^0.2.3",
|
|
64
64
|
"zod": "^4.1.12",
|
|
65
|
-
"better-auth": "1.4.0-beta.
|
|
66
|
-
"@better-auth/core": "1.4.0-beta.
|
|
65
|
+
"better-auth": "1.4.0-beta.22",
|
|
66
|
+
"@better-auth/core": "1.4.0-beta.22"
|
|
67
67
|
},
|
|
68
68
|
"files": [
|
|
69
69
|
"dist"
|