@better-auth/cli 1.4.0-beta.19 → 1.4.0-beta.20

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 +110 -13
  2. package/package.json +4 -3
package/dist/index.mjs CHANGED
@@ -53,9 +53,12 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
53
53
  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`);
54
54
  name = convertToSnakeCase(name, adapter.options?.camelCase);
55
55
  if (field.references?.field === "id") {
56
- if (options.advanced?.database?.useNumberId) if (databaseType === "pg") return `integer('${name}')`;
56
+ const useNumberId$1 = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
57
+ const useUUIDs = options.advanced?.database?.generateId === "uuid";
58
+ if (useNumberId$1) if (databaseType === "pg") return `integer('${name}')`;
57
59
  else if (databaseType === "mysql") return `int('${name}')`;
58
60
  else return `integer('${name}')`;
61
+ if (useUUIDs && databaseType === "pg") return `uuid('${name}')`;
59
62
  if (field.references.field) {
60
63
  if (databaseType === "mysql") return `varchar('${name}', { length: 36 })`;
61
64
  }
@@ -72,7 +75,7 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
72
75
  string: {
73
76
  sqlite: `text('${name}')`,
74
77
  pg: `text('${name}')`,
75
- mysql: field.unique ? `varchar('${name}', { length: 255 })` : field.references ? `varchar('${name}', { length: 36 })` : `text('${name}')`
78
+ 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}')`
76
79
  },
77
80
  boolean: {
78
81
  sqlite: `integer('${name}', { mode: 'boolean' })`,
@@ -107,18 +110,38 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
107
110
  }[type][databaseType];
108
111
  }
109
112
  let id = "";
110
- if (options.advanced?.database?.useNumberId) if (databaseType === "pg") id = `serial("id").primaryKey()`;
113
+ const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
114
+ 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 = `serial("id").primaryKey()`;
111
116
  else if (databaseType === "sqlite") id = `integer("id", { mode: "number" }).primaryKey({ autoIncrement: true })`;
112
117
  else id = `int("id").autoincrement().primaryKey()`;
113
118
  else if (databaseType === "mysql") id = `varchar('id', { length: 36 }).primaryKey()`;
114
119
  else if (databaseType === "pg") id = `text('id').primaryKey()`;
115
120
  else id = `text('id').primaryKey()`;
121
+ let indexes = [];
122
+ const assignIndexes = (indexes$1) => {
123
+ if (!indexes$1.length) return "";
124
+ let code$1 = [`, (table) => [`];
125
+ for (const index of indexes$1) code$1.push(` ${index.type}("${index.name}").on(table.${index.on}),`);
126
+ code$1.push(`]`);
127
+ return code$1.join("\n");
128
+ };
116
129
  const schema = `export const ${modelName} = ${databaseType}Table("${convertToSnakeCase(modelName, adapter.options?.camelCase)}", {
117
130
  id: ${id},
118
131
  ${Object.keys(fields).map((field) => {
119
132
  const attr = fields[field];
120
133
  const fieldName = attr.fieldName || field;
121
134
  let type = getType(fieldName, attr);
135
+ if (attr.index && !attr.unique) indexes.push({
136
+ type: "index",
137
+ name: `${modelName}_${fieldName}_idx`,
138
+ on: fieldName
139
+ });
140
+ else if (attr.index && attr.unique) indexes.push({
141
+ type: "uniqueIndex",
142
+ name: `${modelName}_${fieldName}_uidx`,
143
+ on: fieldName
144
+ });
122
145
  if (attr.defaultValue !== null && typeof attr.defaultValue !== "undefined") if (typeof attr.defaultValue === "function") {
123
146
  if (attr.type === "date" && attr.defaultValue.toString().includes("new Date()")) if (databaseType === "sqlite") type += `.default(sql\`(cast(unixepoch('subsecond') * 1000 as integer))\`)`;
124
147
  else type += `.defaultNow()`;
@@ -129,7 +152,7 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
129
152
  }
130
153
  return `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${getModelName(tables[attr.references.model]?.modelName || attr.references.model, adapter.options)}.${fields[attr.references.field]?.fieldName || attr.references.field}, { onDelete: '${attr.references.onDelete || "cascade"}' })` : ""}`;
131
154
  }).join(",\n ")}
132
- });`;
155
+ }${assignIndexes(indexes)});`;
133
156
  code += `\n${schema}\n`;
134
157
  }
135
158
  return {
@@ -150,7 +173,8 @@ function generateImport({ databaseType, tables, options }) {
150
173
  }
151
174
  if (hasJson && hasBigint) break;
152
175
  }
153
- const useNumberId = options.advanced?.database?.useNumberId;
176
+ const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
177
+ const useUUIDs = options.advanced?.database?.generateId === "uuid";
154
178
  coreImports.push(`${databaseType}Table`);
155
179
  coreImports.push(databaseType === "mysql" ? "varchar, text" : databaseType === "pg" ? "text" : "text");
156
180
  coreImports.push(hasBigint ? databaseType !== "sqlite" ? "bigint" : "" : "");
@@ -160,16 +184,24 @@ function generateImport({ databaseType, tables, options }) {
160
184
  if (!!useNumberId || hasNonBigintNumber) coreImports.push("int");
161
185
  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");
162
186
  } else if (databaseType === "pg") {
187
+ if (useUUIDs) rootImports.push("sql");
163
188
  const hasNonBigintNumber = Object.values(tables).some((table) => Object.values(table.fields).some((field) => (field.type === "number" || field.type === "number[]") && !field.bigint));
164
189
  const hasFkToId = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.references?.field === "id"));
165
- if (hasNonBigintNumber || options.advanced?.database?.useNumberId && hasFkToId) coreImports.push("integer");
190
+ if (hasNonBigintNumber || (options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial") && hasFkToId) coreImports.push("integer");
166
191
  } else coreImports.push("integer");
167
- coreImports.push(useNumberId ? databaseType === "pg" ? "serial" : "" : "");
192
+ if (databaseType === "pg") {
193
+ if (useNumberId) coreImports.push("serial");
194
+ else if (useUUIDs) coreImports.push("uuid");
195
+ }
168
196
  if (hasJson) {
169
197
  if (databaseType === "pg") coreImports.push("jsonb");
170
198
  if (databaseType === "mysql") coreImports.push("json");
171
199
  }
172
200
  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");
201
+ const hasIndexes = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.index && !field.unique));
202
+ const hasUniqueIndexes = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.unique && field.index));
203
+ if (hasIndexes) coreImports.push("index");
204
+ if (hasUniqueIndexes) coreImports.push("uniqueIndex");
173
205
  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`;
174
206
  }
175
207
  function getModelName(modelName, options) {
@@ -211,6 +243,19 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
211
243
  }
212
244
  }
213
245
  }
246
+ const indexedFields = /* @__PURE__ */ new Map();
247
+ for (const table in tables) {
248
+ const fields = tables[table]?.fields;
249
+ const modelName = capitalizeFirstLetter(tables[table]?.modelName || table);
250
+ indexedFields.set(modelName, []);
251
+ for (const field in fields) {
252
+ const attr = fields[field];
253
+ if (attr.index && !attr.unique) {
254
+ const fieldName = attr.fieldName || field;
255
+ indexedFields.get(modelName).push(fieldName);
256
+ }
257
+ }
258
+ }
214
259
  const schema = produceSchema(schemaPrisma, (builder) => {
215
260
  for (const table in tables) {
216
261
  const originalTableName = table;
@@ -229,8 +274,13 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
229
274
  }
230
275
  const prismaModel = builder.findByType("model", { name: modelName });
231
276
  if (!prismaModel) if (provider === "mongodb") builder.model(modelName).field("id", "String").attribute("id").attribute(`map("_id")`);
232
- else if (options.advanced?.database?.useNumberId) builder.model(modelName).field("id", "Int").attribute("id").attribute("default(autoincrement())");
233
- else builder.model(modelName).field("id", "String").attribute("id");
277
+ else {
278
+ const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
279
+ const useUUIDs = options.advanced?.database?.generateId === "uuid";
280
+ 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("db.Uuid").attribute("default(dbgenerated(\"pg_catalog.gen_random_uuid()\"))");
282
+ else builder.model(modelName).field("id", "String").attribute("id");
283
+ }
234
284
  for (const field in fields) {
235
285
  const attr = fields[field];
236
286
  const fieldName = attr.fieldName || field;
@@ -240,14 +290,16 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
240
290
  within: prismaModel.properties
241
291
  })) continue;
242
292
  }
243
- const fieldBuilder = builder.model(modelName).field(fieldName, field === "id" && options.advanced?.database?.useNumberId ? getType({
293
+ const useUUIDs = options.advanced?.database?.generateId === "uuid";
294
+ const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
295
+ const fieldBuilder = builder.model(modelName).field(fieldName, field === "id" && useNumberId ? getType({
244
296
  isBigint: false,
245
297
  isOptional: false,
246
298
  type: "number"
247
299
  }) : getType({
248
300
  isBigint: attr?.bigint || false,
249
301
  isOptional: !attr?.required,
250
- type: attr.references?.field === "id" ? options.advanced?.database?.useNumberId ? "number" : "string" : attr.type
302
+ type: attr.references?.field === "id" ? useNumberId ? "number" : "string" : attr.type
251
303
  }));
252
304
  if (field === "id") {
253
305
  fieldBuilder.attribute("id");
@@ -255,13 +307,45 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
255
307
  }
256
308
  if (attr.unique) builder.model(modelName).blockAttribute(`unique([${fieldName}])`);
257
309
  if (attr.defaultValue !== void 0) {
310
+ if (Array.isArray(attr.defaultValue)) {
311
+ if (attr.type === "json") {
312
+ if (Object.prototype.toString.call(attr.defaultValue[0]) === "[object Object]") {
313
+ fieldBuilder.attribute(`default("${JSON.stringify(attr.defaultValue).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`);
314
+ continue;
315
+ }
316
+ let jsonArray = [];
317
+ for (const value of attr.defaultValue) jsonArray.push(value);
318
+ fieldBuilder.attribute(`default("${JSON.stringify(jsonArray).replace(/"/g, "\\\"")}")`);
319
+ continue;
320
+ }
321
+ if (attr.defaultValue.length === 0) {
322
+ fieldBuilder.attribute(`default([])`);
323
+ continue;
324
+ } else if (typeof attr.defaultValue[0] === "string" && attr.type === "string[]") {
325
+ let valueArray = [];
326
+ for (const value of attr.defaultValue) valueArray.push(JSON.stringify(value));
327
+ fieldBuilder.attribute(`default([${valueArray}])`);
328
+ } else if (typeof attr.defaultValue[0] === "number") {
329
+ let valueArray = [];
330
+ for (const value of attr.defaultValue) valueArray.push(`${value}`);
331
+ fieldBuilder.attribute(`default([${valueArray}])`);
332
+ }
333
+ } else if (typeof attr.defaultValue === "object" && !Array.isArray(attr.defaultValue) && attr.defaultValue !== null) {
334
+ if (Object.entries(attr.defaultValue).length === 0) {
335
+ fieldBuilder.attribute(`default("{}")`);
336
+ continue;
337
+ }
338
+ fieldBuilder.attribute(`default("${JSON.stringify(attr.defaultValue).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`);
339
+ }
258
340
  if (field === "createdAt") fieldBuilder.attribute("default(now())");
259
- else if (typeof attr.defaultValue === "boolean") fieldBuilder.attribute(`default(${attr.defaultValue})`);
341
+ else if (typeof attr.defaultValue === "string" && provider !== "mysql") fieldBuilder.attribute(`default("${attr.defaultValue}")`);
342
+ else if (typeof attr.defaultValue === "boolean" || typeof attr.defaultValue === "number") fieldBuilder.attribute(`default(${attr.defaultValue})`);
260
343
  else if (typeof attr.defaultValue === "function") {}
261
344
  }
262
345
  if (field === "updatedAt" && attr.onUpdate) fieldBuilder.attribute("updatedAt");
263
346
  else if (attr.onUpdate) {}
264
347
  if (attr.references) {
348
+ if (useUUIDs && provider === "postgresql") fieldBuilder.attribute(`db.Uuid`);
265
349
  const referencedOriginalModelName = attr.references.model;
266
350
  const referencedCustomModelName = tables[referencedOriginalModelName]?.modelName || referencedOriginalModelName;
267
351
  let action = "Cascade";
@@ -269,7 +353,8 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
269
353
  else if (attr.references.onDelete === "set null") action = "SetNull";
270
354
  else if (attr.references.onDelete === "set default") action = "SetDefault";
271
355
  else if (attr.references.onDelete === "restrict") action = "Restrict";
272
- builder.model(modelName).field(`${referencedCustomModelName.toLowerCase()}`, `${capitalizeFirstLetter(referencedCustomModelName)}${!attr.required ? "?" : ""}`).attribute(`relation(fields: [${fieldName}], references: [${attr.references.field}], onDelete: ${action})`);
356
+ const relationField = `relation(fields: [${fieldName}], references: [${attr.references.field}], onDelete: ${action})`;
357
+ builder.model(modelName).field(referencedCustomModelName.toLowerCase(), `${capitalizeFirstLetter(referencedCustomModelName)}${!attr.required ? "?" : ""}`).attribute(relationField);
273
358
  }
274
359
  if (!attr.unique && !attr.references && provider === "mysql" && attr.type === "string") builder.model(modelName).field(fieldName).attribute("db.Text");
275
360
  }
@@ -280,6 +365,18 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
280
365
  within: prismaModel?.properties
281
366
  })) builder.model(modelName).field(fieldName, `${relatedModel}[]`);
282
367
  }
368
+ const indexedFieldsForModel = indexedFields.get(modelName);
369
+ if (indexedFieldsForModel && indexedFieldsForModel.length > 0) for (const fieldName of indexedFieldsForModel) {
370
+ const field = Object.entries(fields).find(([key, attr]) => (attr.fieldName || key) === fieldName)?.[1];
371
+ let indexField = fieldName;
372
+ if (provider === "mysql" && field && field.type === "string") {
373
+ const useNumberId = options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial";
374
+ const useUUIDs = options.advanced?.database?.generateId === "uuid";
375
+ if (field.references?.field === "id" && (useNumberId || useUUIDs)) indexField = `${fieldName}`;
376
+ else indexField = `${fieldName}(length: 191)`;
377
+ }
378
+ builder.model(modelName).blockAttribute(`index([${indexField}])`);
379
+ }
283
380
  const hasAttribute = builder.findByType("attribute", {
284
381
  name: "map",
285
382
  within: prismaModel?.properties
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/cli",
3
- "version": "1.4.0-beta.19",
3
+ "version": "1.4.0-beta.20",
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.19"
37
+ "@better-auth/passkey": "1.4.0-beta.20"
38
38
  },
39
39
  "dependencies": {
40
40
  "@babel/core": "^7.28.4",
@@ -62,7 +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.19"
65
+ "better-auth": "1.4.0-beta.20",
66
+ "@better-auth/core": "1.4.0-beta.20"
66
67
  },
67
68
  "files": [
68
69
  "dist"