pecunia-cli 0.0.0

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.
@@ -0,0 +1,748 @@
1
+ import fs, { existsSync } from "node:fs";
2
+ import fs$1 from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { getMigrations } from "pecunia";
5
+ import { getPaymentTables, initGetFieldName, initGetModelName } from "@pecunia/core";
6
+ import prettier from "prettier";
7
+ import { produceSchema } from "@mrleebo/prisma-ast";
8
+
9
+ //#region src/generators/drizzle.ts
10
+ function convertToSnakeCase(str, camelCase) {
11
+ if (camelCase) return str;
12
+ return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLowerCase();
13
+ }
14
+ const generateDrizzleSchema = async ({ options, file, adapter }) => {
15
+ const tables = getPaymentTables(options);
16
+ const filePath = file || "./auth-schema.ts";
17
+ const databaseType = adapter.options?.provider;
18
+ 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`);
19
+ const fileExist = existsSync(filePath);
20
+ let code = generateImport({
21
+ databaseType,
22
+ tables,
23
+ options
24
+ });
25
+ const getModelName = initGetModelName({
26
+ schema: tables,
27
+ usePlural: adapter.options?.adapterConfig?.usePlural
28
+ });
29
+ const getFieldName = initGetFieldName({
30
+ schema: tables,
31
+ usePlural: adapter.options?.adapterConfig?.usePlural
32
+ });
33
+ for (const tableKey in tables) {
34
+ const table = tables[tableKey];
35
+ const modelName = getModelName(tableKey);
36
+ const fields = table.fields;
37
+ function getType(name, field) {
38
+ 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`);
39
+ name = convertToSnakeCase(name, adapter.options?.camelCase);
40
+ if (field.references?.field === "id") {
41
+ if (field.references.field) {
42
+ if (databaseType === "mysql") return `varchar('${name}', { length: 36 })`;
43
+ }
44
+ return `text('${name}')`;
45
+ }
46
+ const type = field.type;
47
+ if (typeof type !== "string") if (Array.isArray(type) && type.every((x) => typeof x === "string")) return {
48
+ sqlite: `text({ enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
49
+ pg: `text('${name}', { enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
50
+ mysql: `mysqlEnum([${type.map((x) => `'${x}'`).join(", ")}])`
51
+ }[databaseType];
52
+ else throw new TypeError(`Invalid field type for field ${name} in model ${modelName}`);
53
+ const dbTypeMap = {
54
+ string: {
55
+ sqlite: `text('${name}')`,
56
+ pg: `text('${name}')`,
57
+ 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}')`
58
+ },
59
+ boolean: {
60
+ sqlite: `integer('${name}', { mode: 'boolean' })`,
61
+ pg: `boolean('${name}')`,
62
+ mysql: `boolean('${name}')`
63
+ },
64
+ number: {
65
+ sqlite: `integer('${name}')`,
66
+ pg: field.bigint ? `bigint('${name}', { mode: 'number' })` : `integer('${name}')`,
67
+ mysql: field.bigint ? `bigint('${name}', { mode: 'number' })` : `int('${name}')`
68
+ },
69
+ date: {
70
+ sqlite: `integer('${name}', { mode: 'timestamp_ms' })`,
71
+ pg: `timestamp('${name}')`,
72
+ mysql: `timestamp('${name}', { fsp: 3 })`
73
+ },
74
+ "number[]": {
75
+ sqlite: `text('${name}', { mode: "json" })`,
76
+ pg: field.bigint ? `bigint('${name}', { mode: 'number' }).array()` : `integer('${name}').array()`,
77
+ mysql: `text('${name}', { mode: 'json' })`
78
+ },
79
+ "string[]": {
80
+ sqlite: `text('${name}', { mode: "json" })`,
81
+ pg: `text('${name}').array()`,
82
+ mysql: `text('${name}', { mode: "json" })`
83
+ },
84
+ json: {
85
+ sqlite: `text('${name}', { mode: "json" })`,
86
+ pg: `jsonb('${name}')`,
87
+ mysql: `json('${name}', { mode: "json" })`
88
+ }
89
+ }[type];
90
+ if (!dbTypeMap) throw new Error(`Unsupported field type '${field.type}' for field '${name}'.`);
91
+ return dbTypeMap[databaseType];
92
+ }
93
+ let id = "";
94
+ if (databaseType === "mysql") id = `varchar('id', { length: 36 }).primaryKey()`;
95
+ else if (databaseType === "pg") id = `text('id').primaryKey()`;
96
+ else id = `text('id').primaryKey()`;
97
+ let indexes = [];
98
+ const assignIndexes = (indexes$1) => {
99
+ if (!indexes$1.length) return "";
100
+ let code$1 = [`, (table) => [`];
101
+ for (const index of indexes$1) code$1.push(` ${index.type}("${index.name}").on(table.${index.on}),`);
102
+ code$1.push(`]`);
103
+ return code$1.join("\n");
104
+ };
105
+ const schema = `export const ${modelName} = ${databaseType}Table("${convertToSnakeCase(modelName, adapter.options?.camelCase)}", {
106
+ id: ${id},
107
+ ${Object.keys(fields).map((field) => {
108
+ const attr = fields[field];
109
+ const fieldName = attr.fieldName || field;
110
+ let type = getType(fieldName, attr);
111
+ if (attr.index && !attr.unique) indexes.push({
112
+ type: "index",
113
+ name: `${modelName}_${fieldName}_idx`,
114
+ on: fieldName
115
+ });
116
+ else if (attr.index && attr.unique) indexes.push({
117
+ type: "uniqueIndex",
118
+ name: `${modelName}_${fieldName}_uidx`,
119
+ on: fieldName
120
+ });
121
+ if (attr.defaultValue !== null && typeof attr.defaultValue !== "undefined") if (typeof attr.defaultValue === "function") {
122
+ if (attr.type === "date" && attr.defaultValue.toString().includes("new Date()")) if (databaseType === "sqlite") type += `.default(sql\`(cast(unixepoch('subsecond') * 1000 as integer))\`)`;
123
+ else type += `.defaultNow()`;
124
+ } else if (typeof attr.defaultValue === "string") type += `.default("${attr.defaultValue}")`;
125
+ else type += `.default(${attr.defaultValue})`;
126
+ if (attr.onUpdate && attr.type === "date") {
127
+ if (typeof attr.onUpdate === "function") type += `.$onUpdate(${attr.onUpdate})`;
128
+ }
129
+ return `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${getModelName(attr.references.model)}.${getFieldName({
130
+ model: attr.references.model,
131
+ field: attr.references.field
132
+ })}, { onDelete: '${attr.references.onDelete || "cascade"}' })` : ""}`;
133
+ }).join(",\n ")}
134
+ }${assignIndexes(indexes)});`;
135
+ code += `\n${schema}\n`;
136
+ }
137
+ let relationsString = "";
138
+ for (const tableKey in tables) {
139
+ const table = tables[tableKey];
140
+ const modelName = getModelName(tableKey);
141
+ const oneRelations = [];
142
+ const manyRelations = [];
143
+ const manyRelationsSet = /* @__PURE__ */ new Set();
144
+ const foreignFields = Object.entries(table.fields).filter(([_, field]) => field.references);
145
+ for (const [fieldName, field] of foreignFields) {
146
+ const referencedModel = field.references.model;
147
+ const relationKey = getModelName(referencedModel);
148
+ const fieldRef = `${getModelName(tableKey)}.${getFieldName({
149
+ model: tableKey,
150
+ field: fieldName
151
+ })}`;
152
+ const referenceRef = `${getModelName(referencedModel)}.${getFieldName({
153
+ model: referencedModel,
154
+ field: field.references.field || "id"
155
+ })}`;
156
+ oneRelations.push({
157
+ key: relationKey,
158
+ model: getModelName(referencedModel),
159
+ type: "one",
160
+ reference: {
161
+ field: fieldRef,
162
+ references: referenceRef,
163
+ fieldName
164
+ }
165
+ });
166
+ }
167
+ const otherModels = Object.entries(tables).filter(([modelName$1]) => modelName$1 !== tableKey);
168
+ const modelRelationsMap = /* @__PURE__ */ new Map();
169
+ for (const [modelName$1, otherTable] of otherModels) {
170
+ const foreignKeysPointingHere = Object.entries(otherTable.fields).filter(([_, field]) => field.references?.model === tableKey || field.references?.model === getModelName(tableKey));
171
+ if (foreignKeysPointingHere.length === 0) continue;
172
+ const hasUnique = foreignKeysPointingHere.some(([_, field]) => !!field.unique);
173
+ const hasMany$1 = foreignKeysPointingHere.some(([_, field]) => !field.unique);
174
+ modelRelationsMap.set(modelName$1, {
175
+ modelName: modelName$1,
176
+ hasUnique,
177
+ hasMany: hasMany$1
178
+ });
179
+ }
180
+ for (const { modelName: modelName$1, hasMany: hasMany$1 } of modelRelationsMap.values()) {
181
+ const relationType = hasMany$1 ? "many" : "one";
182
+ let relationKey = getModelName(modelName$1);
183
+ if (!adapter.options?.adapterConfig?.usePlural && relationType === "many") relationKey = `${relationKey}s`;
184
+ if (!manyRelationsSet.has(relationKey)) {
185
+ manyRelationsSet.add(relationKey);
186
+ manyRelations.push({
187
+ key: relationKey,
188
+ model: getModelName(modelName$1),
189
+ type: relationType
190
+ });
191
+ }
192
+ }
193
+ const relationsByModel = /* @__PURE__ */ new Map();
194
+ for (const relation of oneRelations) if (relation.reference) {
195
+ const modelKey = relation.key;
196
+ if (!relationsByModel.has(modelKey)) relationsByModel.set(modelKey, []);
197
+ relationsByModel.get(modelKey).push(relation);
198
+ }
199
+ const duplicateRelations = [];
200
+ const singleRelations = [];
201
+ for (const [_modelKey, relations] of relationsByModel.entries()) if (relations.length > 1) duplicateRelations.push(...relations);
202
+ else singleRelations.push(relations[0]);
203
+ for (const relation of duplicateRelations) if (relation.reference) {
204
+ const fieldName = relation.reference.fieldName;
205
+ const tableRelation = `export const ${`${modelName}${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}Relations`} = relations(${getModelName(table.modelName)}, ({ one }) => ({
206
+ ${relation.key}: one(${relation.model}, {
207
+ fields: [${relation.reference.field}],
208
+ references: [${relation.reference.references}],
209
+ })
210
+ }))`;
211
+ relationsString += `\n${tableRelation}\n`;
212
+ }
213
+ const hasOne = singleRelations.length > 0;
214
+ const hasMany = manyRelations.length > 0;
215
+ if (hasOne && hasMany) {
216
+ const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ one, many }) => ({
217
+ ${singleRelations.map((relation) => relation.reference ? ` ${relation.key}: one(${relation.model}, {
218
+ fields: [${relation.reference.field}],
219
+ references: [${relation.reference.references}],
220
+ })` : "").filter((x) => x !== "").join(",\n ")}${singleRelations.length > 0 && manyRelations.length > 0 ? "," : ""}
221
+ ${manyRelations.map(({ key, model }) => ` ${key}: many(${model})`).join(",\n ")}
222
+ }))`;
223
+ relationsString += `\n${tableRelation}\n`;
224
+ } else if (hasOne) {
225
+ const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ one }) => ({
226
+ ${singleRelations.map((relation) => relation.reference ? ` ${relation.key}: one(${relation.model}, {
227
+ fields: [${relation.reference.field}],
228
+ references: [${relation.reference.references}],
229
+ })` : "").filter((x) => x !== "").join(",\n ")}
230
+ }))`;
231
+ relationsString += `\n${tableRelation}\n`;
232
+ } else if (hasMany) {
233
+ const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ many }) => ({
234
+ ${manyRelations.map(({ key, model }) => ` ${key}: many(${model})`).join(",\n ")}
235
+ }))`;
236
+ relationsString += `\n${tableRelation}\n`;
237
+ }
238
+ }
239
+ code += `\n${relationsString}`;
240
+ return {
241
+ code: await prettier.format(code, { parser: "typescript" }),
242
+ fileName: filePath,
243
+ overwrite: fileExist
244
+ };
245
+ };
246
+ function generateImport({ databaseType, tables, options }) {
247
+ const rootImports = ["relations"];
248
+ const coreImports = [];
249
+ let hasBigint = false;
250
+ let hasJson = false;
251
+ for (const table of Object.values(tables)) {
252
+ for (const field of Object.values(table.fields)) {
253
+ if (field.bigint) hasBigint = true;
254
+ if (field.type === "json") hasJson = true;
255
+ }
256
+ if (hasJson && hasBigint) break;
257
+ }
258
+ coreImports.push(`${databaseType}Table`);
259
+ coreImports.push(databaseType === "mysql" ? "varchar, text" : databaseType === "pg" ? "text" : "text");
260
+ coreImports.push(hasBigint ? databaseType !== "sqlite" ? "bigint" : "" : "");
261
+ coreImports.push(databaseType !== "sqlite" ? "timestamp, boolean" : "");
262
+ if (databaseType === "mysql") {
263
+ if (Object.values(tables).some((table) => Object.values(table.fields).some((field) => (field.type === "number" || field.type === "number[]") && !field.bigint))) coreImports.push("int");
264
+ 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");
265
+ } else if (databaseType === "pg") {
266
+ const hasNonBigintNumber = Object.values(tables).some((table) => Object.values(table.fields).some((field) => (field.type === "number" || field.type === "number[]") && !field.bigint));
267
+ Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.references?.field === "id"));
268
+ if (hasNonBigintNumber) coreImports.push("integer");
269
+ } else coreImports.push("integer");
270
+ if (hasJson) {
271
+ if (databaseType === "pg") coreImports.push("jsonb");
272
+ if (databaseType === "mysql") coreImports.push("json");
273
+ }
274
+ 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");
275
+ const hasIndexes = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.index && !field.unique));
276
+ const hasUniqueIndexes = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.unique && field.index));
277
+ if (hasIndexes) coreImports.push("index");
278
+ if (hasUniqueIndexes) coreImports.push("uniqueIndex");
279
+ 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`;
280
+ }
281
+
282
+ //#endregion
283
+ //#region src/generators/kysely.ts
284
+ const generateKyselySchema = async ({ options, file }) => {
285
+ const { compileMigrations } = await getMigrations(options);
286
+ const migrations = await compileMigrations();
287
+ return {
288
+ code: migrations.trim() === ";" ? "" : migrations,
289
+ fileName: file || `./better-auth_migrations/${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}.sql`
290
+ };
291
+ };
292
+
293
+ //#endregion
294
+ //#region ../core/dist/env-C7DHqPmD.mjs
295
+ const _envShim = Object.create(null);
296
+ const _getEnv = (useShim) => globalThis.process?.env || globalThis.Deno?.env.toObject() || globalThis.__env__ || (useShim ? _envShim : globalThis);
297
+ const env = new Proxy(_envShim, {
298
+ get(_, prop) {
299
+ return _getEnv()[prop] ?? _envShim[prop];
300
+ },
301
+ has(_, prop) {
302
+ return prop in _getEnv() || prop in _envShim;
303
+ },
304
+ set(_, prop, value) {
305
+ const env$1 = _getEnv(true);
306
+ env$1[prop] = value;
307
+ return true;
308
+ },
309
+ deleteProperty(_, prop) {
310
+ if (!prop) return false;
311
+ const env$1 = _getEnv(true);
312
+ delete env$1[prop];
313
+ return true;
314
+ },
315
+ ownKeys() {
316
+ const env$1 = _getEnv(true);
317
+ return Object.keys(env$1);
318
+ }
319
+ });
320
+ const nodeENV = typeof process !== "undefined" && process.env && process.env.NODE_ENV || "";
321
+ /**
322
+ * Get environment variable with fallback
323
+ */
324
+ function getEnvVar(key, fallback) {
325
+ if (typeof process !== "undefined" && process.env) return process.env[key] ?? fallback;
326
+ if (typeof Deno !== "undefined") return Deno.env.get(key) ?? fallback;
327
+ if (typeof Bun !== "undefined") return Bun.env[key] ?? fallback;
328
+ return fallback;
329
+ }
330
+ /**
331
+ * Common environment variables used in Pecunia
332
+ */
333
+ const ENV = Object.freeze({
334
+ get NODE_ENV() {
335
+ return getEnvVar("NODE_ENV", "development");
336
+ },
337
+ get PACKAGE_VERSION() {
338
+ return getEnvVar("PACKAGE_VERSION", "0.0.0");
339
+ }
340
+ });
341
+ const COLORS_2 = 1;
342
+ const COLORS_16 = 4;
343
+ const COLORS_256 = 8;
344
+ const COLORS_16m = 24;
345
+ const TERM_ENVS = {
346
+ eterm: COLORS_16,
347
+ cons25: COLORS_16,
348
+ console: COLORS_16,
349
+ cygwin: COLORS_16,
350
+ dtterm: COLORS_16,
351
+ gnome: COLORS_16,
352
+ hurd: COLORS_16,
353
+ jfbterm: COLORS_16,
354
+ konsole: COLORS_16,
355
+ kterm: COLORS_16,
356
+ mlterm: COLORS_16,
357
+ mosh: COLORS_16m,
358
+ putty: COLORS_16,
359
+ st: COLORS_16,
360
+ "rxvt-unicode-24bit": COLORS_16m,
361
+ terminator: COLORS_16m,
362
+ "xterm-kitty": COLORS_16m
363
+ };
364
+ const CI_ENVS_MAP = new Map(Object.entries({
365
+ APPVEYOR: COLORS_256,
366
+ BUILDKITE: COLORS_256,
367
+ CIRCLECI: COLORS_16m,
368
+ DRONE: COLORS_256,
369
+ GITEA_ACTIONS: COLORS_16m,
370
+ GITHUB_ACTIONS: COLORS_16m,
371
+ GITLAB_CI: COLORS_256,
372
+ TRAVIS: COLORS_256
373
+ }));
374
+ const TERM_ENVS_REG_EXP = [
375
+ /ansi/,
376
+ /color/,
377
+ /linux/,
378
+ /direct/,
379
+ /^con[0-9]*x[0-9]/,
380
+ /^rxvt/,
381
+ /^screen/,
382
+ /^xterm/,
383
+ /^vt100/,
384
+ /^vt220/
385
+ ];
386
+ function getColorDepth() {
387
+ if (getEnvVar("FORCE_COLOR") !== void 0) switch (getEnvVar("FORCE_COLOR")) {
388
+ case "":
389
+ case "1":
390
+ case "true": return COLORS_16;
391
+ case "2": return COLORS_256;
392
+ case "3": return COLORS_16m;
393
+ default: return COLORS_2;
394
+ }
395
+ if (getEnvVar("NODE_DISABLE_COLORS") !== void 0 && getEnvVar("NODE_DISABLE_COLORS") !== "" || getEnvVar("NO_COLOR") !== void 0 && getEnvVar("NO_COLOR") !== "" || getEnvVar("TERM") === "dumb") return COLORS_2;
396
+ if (getEnvVar("TMUX")) return COLORS_16m;
397
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) return COLORS_16;
398
+ if ("CI" in env) {
399
+ for (const { 0: envName, 1: colors } of CI_ENVS_MAP) if (envName in env) return colors;
400
+ if (getEnvVar("CI_NAME") === "codeship") return COLORS_256;
401
+ return COLORS_2;
402
+ }
403
+ if ("TEAMCITY_VERSION" in env) return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.exec(getEnvVar("TEAMCITY_VERSION")) !== null ? COLORS_16 : COLORS_2;
404
+ switch (getEnvVar("TERM_PROGRAM")) {
405
+ case "iTerm.app":
406
+ if (!getEnvVar("TERM_PROGRAM_VERSION") || /^[0-2]\./.exec(getEnvVar("TERM_PROGRAM_VERSION")) !== null) return COLORS_256;
407
+ return COLORS_16m;
408
+ case "HyperTerm":
409
+ case "MacTerm": return COLORS_16m;
410
+ case "Apple_Terminal": return COLORS_256;
411
+ }
412
+ if (getEnvVar("COLORTERM") === "truecolor" || getEnvVar("COLORTERM") === "24bit") return COLORS_16m;
413
+ if (getEnvVar("TERM")) {
414
+ if (/truecolor/.exec(getEnvVar("TERM")) !== null) return COLORS_16m;
415
+ if (/^xterm-256/.exec(getEnvVar("TERM")) !== null) return COLORS_256;
416
+ const termEnv = getEnvVar("TERM").toLowerCase();
417
+ if (TERM_ENVS[termEnv]) return TERM_ENVS[termEnv];
418
+ if (TERM_ENVS_REG_EXP.some((term) => term.exec(termEnv) !== null)) return COLORS_16;
419
+ }
420
+ if (getEnvVar("COLORTERM")) return COLORS_16;
421
+ return COLORS_2;
422
+ }
423
+ const TTY_COLORS = {
424
+ reset: "\x1B[0m",
425
+ bright: "\x1B[1m",
426
+ dim: "\x1B[2m",
427
+ undim: "\x1B[22m",
428
+ underscore: "\x1B[4m",
429
+ blink: "\x1B[5m",
430
+ reverse: "\x1B[7m",
431
+ hidden: "\x1B[8m",
432
+ fg: {
433
+ black: "\x1B[30m",
434
+ red: "\x1B[31m",
435
+ green: "\x1B[32m",
436
+ yellow: "\x1B[33m",
437
+ blue: "\x1B[34m",
438
+ magenta: "\x1B[35m",
439
+ cyan: "\x1B[36m",
440
+ white: "\x1B[37m"
441
+ },
442
+ bg: {
443
+ black: "\x1B[40m",
444
+ red: "\x1B[41m",
445
+ green: "\x1B[42m",
446
+ yellow: "\x1B[43m",
447
+ blue: "\x1B[44m",
448
+ magenta: "\x1B[45m",
449
+ cyan: "\x1B[46m",
450
+ white: "\x1B[47m"
451
+ }
452
+ };
453
+ const levels = [
454
+ "debug",
455
+ "info",
456
+ "success",
457
+ "warn",
458
+ "error"
459
+ ];
460
+ function shouldPublishLog(currentLogLevel, logLevel) {
461
+ return levels.indexOf(logLevel) >= levels.indexOf(currentLogLevel);
462
+ }
463
+ const levelColors = {
464
+ info: TTY_COLORS.fg.blue,
465
+ success: TTY_COLORS.fg.green,
466
+ warn: TTY_COLORS.fg.yellow,
467
+ error: TTY_COLORS.fg.red,
468
+ debug: TTY_COLORS.fg.magenta
469
+ };
470
+ const formatMessage = (level, message, colorsEnabled) => {
471
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
472
+ if (colorsEnabled) return `${TTY_COLORS.dim}${timestamp}${TTY_COLORS.reset} ${levelColors[level]}${level.toUpperCase()}${TTY_COLORS.reset} ${TTY_COLORS.bright}[Better Auth]:${TTY_COLORS.reset} ${message}`;
473
+ return `${timestamp} ${level.toUpperCase()} [Better Auth]: ${message}`;
474
+ };
475
+ const createLogger = (options) => {
476
+ const enabled = options?.disabled !== true;
477
+ const logLevel = options?.level ?? "error";
478
+ const colorsEnabled = options?.disableColors !== void 0 ? !options.disableColors : getColorDepth() !== 1;
479
+ const LogFunc = (level, message, args = []) => {
480
+ if (!enabled || !shouldPublishLog(logLevel, level)) return;
481
+ const formattedMessage = formatMessage(level, message, colorsEnabled);
482
+ if (!options || typeof options.log !== "function") {
483
+ if (level === "error") console.error(formattedMessage, ...args);
484
+ else if (level === "warn") console.warn(formattedMessage, ...args);
485
+ else console.log(formattedMessage, ...args);
486
+ return;
487
+ }
488
+ options.log(level === "success" ? "info" : level, message, ...args);
489
+ };
490
+ return {
491
+ ...Object.fromEntries(levels.map((level) => [level, (...[message, ...args]) => LogFunc(level, message, args)])),
492
+ get level() {
493
+ return logLevel;
494
+ }
495
+ };
496
+ };
497
+ const logger = createLogger();
498
+
499
+ //#endregion
500
+ //#region ../core/dist/utils-BNmlJWNJ.mjs
501
+ function capitalizeFirstLetter(val) {
502
+ return val.charAt(0).toUpperCase() + val.slice(1);
503
+ }
504
+
505
+ //#endregion
506
+ //#region src/utils/get-package-info.ts
507
+ function getPackageInfo(cwd) {
508
+ const packageJsonPath = cwd ? path.join(cwd, "package.json") : path.join("package.json");
509
+ return JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
510
+ }
511
+ function getPrismaVersion(cwd) {
512
+ try {
513
+ const packageInfo = getPackageInfo(cwd);
514
+ const prismaVersion = packageInfo.dependencies?.prisma || packageInfo.devDependencies?.prisma || packageInfo.dependencies?.["@prisma/client"] || packageInfo.devDependencies?.["@prisma/client"];
515
+ if (!prismaVersion) return null;
516
+ const match = prismaVersion.match(/(\d+)/);
517
+ return match ? parseInt(match[1], 10) : null;
518
+ } catch {
519
+ return null;
520
+ }
521
+ }
522
+
523
+ //#endregion
524
+ //#region src/generators/prisma.ts
525
+ const generatePrismaSchema = async ({ adapter, options, file }) => {
526
+ const provider = adapter.options?.provider || "postgresql";
527
+ const tables = getPaymentTables(options);
528
+ const filePath = file || "./prisma/schema.prisma";
529
+ const schemaPrismaExist = existsSync(path.join(process.cwd(), filePath));
530
+ const getModelName = initGetModelName({
531
+ schema: getPaymentTables(options),
532
+ usePlural: adapter.options?.adapterConfig?.usePlural
533
+ });
534
+ const getFieldName = initGetFieldName({
535
+ schema: getPaymentTables(options),
536
+ usePlural: false
537
+ });
538
+ let schemaPrisma = "";
539
+ if (schemaPrismaExist) schemaPrisma = await fs$1.readFile(path.join(process.cwd(), filePath), "utf-8");
540
+ else schemaPrisma = getNewPrisma(provider, process.cwd());
541
+ const prismaVersion = getPrismaVersion(process.cwd());
542
+ if (prismaVersion && prismaVersion >= 7 && schemaPrismaExist) schemaPrisma = produceSchema(schemaPrisma, (builder) => {
543
+ const generator = builder.findByType("generator", { name: "client" });
544
+ if (generator && generator.properties) {
545
+ const providerProp = generator.properties.find((prop) => prop.type === "assignment" && prop.key === "provider");
546
+ if (providerProp && providerProp.value === "\"prisma-client-js\"") providerProp.value = "\"prisma-client\"";
547
+ }
548
+ });
549
+ const manyToManyRelations = /* @__PURE__ */ new Map();
550
+ for (const table in tables) {
551
+ const fields = tables[table]?.fields;
552
+ for (const field in fields) {
553
+ const attr = fields[field];
554
+ if (attr.references) {
555
+ const referencedOriginalModel = attr.references.model;
556
+ const referencedModelNameCap = capitalizeFirstLetter(getModelName(tables[referencedOriginalModel]?.modelName || referencedOriginalModel));
557
+ if (!manyToManyRelations.has(referencedModelNameCap)) manyToManyRelations.set(referencedModelNameCap, /* @__PURE__ */ new Set());
558
+ const currentModelNameCap = capitalizeFirstLetter(getModelName(tables[table]?.modelName || table));
559
+ manyToManyRelations.get(referencedModelNameCap).add(currentModelNameCap);
560
+ }
561
+ }
562
+ }
563
+ const indexedFields = /* @__PURE__ */ new Map();
564
+ for (const table in tables) {
565
+ const fields = tables[table]?.fields;
566
+ const modelName = capitalizeFirstLetter(getModelName(tables[table]?.modelName || table));
567
+ indexedFields.set(modelName, []);
568
+ for (const field in fields) {
569
+ const attr = fields[field];
570
+ if (attr.index && !attr.unique) {
571
+ const fieldName = attr.fieldName || field;
572
+ indexedFields.get(modelName).push(fieldName);
573
+ }
574
+ }
575
+ }
576
+ const schema = produceSchema(schemaPrisma, (builder) => {
577
+ for (const table in tables) {
578
+ const originalTableName = table;
579
+ const customModelName = tables[table]?.modelName || table;
580
+ const modelName = capitalizeFirstLetter(getModelName(customModelName));
581
+ const fields = tables[table]?.fields;
582
+ function getType({ isBigint, isOptional, type }) {
583
+ if (type === "string") return isOptional ? "String?" : "String";
584
+ if (type === "number" && isBigint) return isOptional ? "BigInt?" : "BigInt";
585
+ if (type === "number") return isOptional ? "Int?" : "Int";
586
+ if (type === "boolean") return isOptional ? "Boolean?" : "Boolean";
587
+ if (type === "date") return isOptional ? "DateTime?" : "DateTime";
588
+ if (type === "json") {
589
+ if (provider === "sqlite" || provider === "mysql") return isOptional ? "String?" : "String";
590
+ return isOptional ? "Json?" : "Json";
591
+ }
592
+ if (type === "string[]") {
593
+ if (provider === "sqlite" || provider === "mysql") return isOptional ? "String?" : "String";
594
+ return "String[]";
595
+ }
596
+ if (type === "number[]") {
597
+ if (provider === "sqlite" || provider === "mysql") return "String";
598
+ return "Int[]";
599
+ }
600
+ }
601
+ const prismaModel = builder.findByType("model", { name: modelName });
602
+ if (!prismaModel) if (provider === "mongodb") builder.model(modelName).field("id", "String").attribute("id").attribute(`map("_id")`);
603
+ else builder.model(modelName).field("id", "String").attribute("id");
604
+ for (const field in fields) {
605
+ const attr = fields[field];
606
+ const fieldName = attr.fieldName || field;
607
+ if (prismaModel) {
608
+ if (builder.findByType("field", {
609
+ name: fieldName,
610
+ within: prismaModel.properties
611
+ })) continue;
612
+ }
613
+ const fieldBuilder = builder.model(modelName).field(fieldName, getType({
614
+ isBigint: attr?.bigint || false,
615
+ isOptional: !attr?.required,
616
+ type: "string"
617
+ }));
618
+ if (field === "id") {
619
+ fieldBuilder.attribute("id");
620
+ if (provider === "mongodb") fieldBuilder.attribute(`map("_id")`);
621
+ }
622
+ if (attr.unique) builder.model(modelName).blockAttribute(`unique([${fieldName}])`);
623
+ if (attr.defaultValue !== void 0) {
624
+ if (Array.isArray(attr.defaultValue)) {
625
+ if (attr.type === "json") {
626
+ if (Object.prototype.toString.call(attr.defaultValue[0]) === "[object Object]") {
627
+ fieldBuilder.attribute(`default("${JSON.stringify(attr.defaultValue).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`);
628
+ continue;
629
+ }
630
+ let jsonArray = [];
631
+ for (const value of attr.defaultValue) jsonArray.push(value);
632
+ fieldBuilder.attribute(`default("${JSON.stringify(jsonArray).replace(/"/g, "\\\"")}")`);
633
+ continue;
634
+ }
635
+ if (attr.defaultValue.length === 0) {
636
+ fieldBuilder.attribute(`default([])`);
637
+ continue;
638
+ } else if (typeof attr.defaultValue[0] === "string" && attr.type === "string[]") {
639
+ let valueArray = [];
640
+ for (const value of attr.defaultValue) valueArray.push(JSON.stringify(value));
641
+ fieldBuilder.attribute(`default([${valueArray}])`);
642
+ } else if (typeof attr.defaultValue[0] === "number") {
643
+ let valueArray = [];
644
+ for (const value of attr.defaultValue) valueArray.push(`${value}`);
645
+ fieldBuilder.attribute(`default([${valueArray}])`);
646
+ }
647
+ } else if (typeof attr.defaultValue === "object" && !Array.isArray(attr.defaultValue) && attr.defaultValue !== null) {
648
+ if (Object.entries(attr.defaultValue).length === 0) {
649
+ fieldBuilder.attribute(`default("{}")`);
650
+ continue;
651
+ }
652
+ fieldBuilder.attribute(`default("${JSON.stringify(attr.defaultValue).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`);
653
+ }
654
+ if (field === "createdAt") fieldBuilder.attribute("default(now())");
655
+ else if (typeof attr.defaultValue === "string" && provider !== "mysql") fieldBuilder.attribute(`default("${attr.defaultValue}")`);
656
+ else if (typeof attr.defaultValue === "boolean" || typeof attr.defaultValue === "number") fieldBuilder.attribute(`default(${attr.defaultValue})`);
657
+ else if (typeof attr.defaultValue === "function") {}
658
+ }
659
+ if (field === "updatedAt" && attr.onUpdate) fieldBuilder.attribute("updatedAt");
660
+ else if (attr.onUpdate) {}
661
+ if (attr.references) {
662
+ const referencedOriginalModelName = getModelName(attr.references.model);
663
+ const referencedCustomModelName = tables[referencedOriginalModelName]?.modelName || referencedOriginalModelName;
664
+ let action = "Cascade";
665
+ if (attr.references.onDelete === "no action") action = "NoAction";
666
+ else if (attr.references.onDelete === "set null") action = "SetNull";
667
+ else if (attr.references.onDelete === "set default") action = "SetDefault";
668
+ else if (attr.references.onDelete === "restrict") action = "Restrict";
669
+ const relationField = `relation(fields: [${getFieldName({
670
+ model: originalTableName,
671
+ field: fieldName
672
+ })}], references: [${getFieldName({
673
+ model: attr.references.model,
674
+ field: attr.references.field
675
+ })}], onDelete: ${action})`;
676
+ builder.model(modelName).field(referencedCustomModelName.toLowerCase(), `${capitalizeFirstLetter(referencedCustomModelName)}${!attr.required ? "?" : ""}`).attribute(relationField);
677
+ }
678
+ if (!attr.unique && !attr.references && provider === "mysql" && attr.type === "string") builder.model(modelName).field(fieldName).attribute("db.Text");
679
+ }
680
+ if (manyToManyRelations.has(modelName)) for (const relatedModel of manyToManyRelations.get(modelName)) {
681
+ const relatedTableName = Object.keys(tables).find((key) => capitalizeFirstLetter(tables[key]?.modelName || key) === relatedModel);
682
+ const relatedFields = relatedTableName ? tables[relatedTableName]?.fields : {};
683
+ const [_fieldKey, fkFieldAttr] = Object.entries(relatedFields || {}).find(([_fieldName, fieldAttr]) => fieldAttr.references && getModelName(fieldAttr.references.model) === getModelName(originalTableName)) || [];
684
+ const isUnique = fkFieldAttr?.unique === true;
685
+ const fieldName = isUnique || adapter.options?.usePlural === true ? `${relatedModel.toLowerCase()}` : `${relatedModel.toLowerCase()}s`;
686
+ if (!builder.findByType("field", {
687
+ name: fieldName,
688
+ within: prismaModel?.properties
689
+ })) builder.model(modelName).field(fieldName, `${relatedModel}${isUnique ? "?" : "[]"}`);
690
+ }
691
+ const indexedFieldsForModel = indexedFields.get(modelName);
692
+ if (indexedFieldsForModel && indexedFieldsForModel.length > 0) for (const fieldName of indexedFieldsForModel) {
693
+ if (prismaModel) {
694
+ if (prismaModel.properties.some((v) => v.type === "attribute" && v.name === "index" && JSON.stringify(v.args[0]?.value).includes(fieldName))) continue;
695
+ }
696
+ const field = Object.entries(fields).find(([key, attr]) => (attr.fieldName || key) === fieldName)?.[1];
697
+ let indexField = fieldName;
698
+ if (provider === "mysql" && field && field.type === "string") indexField = `${fieldName}(length: 191)`;
699
+ builder.model(modelName).blockAttribute(`index([${indexField}])`);
700
+ }
701
+ const hasAttribute = builder.findByType("attribute", {
702
+ name: "map",
703
+ within: prismaModel?.properties
704
+ });
705
+ const hasChanged = customModelName !== originalTableName;
706
+ if (!hasAttribute) builder.model(modelName).blockAttribute("map", `${getModelName(hasChanged ? customModelName : originalTableName)}`);
707
+ }
708
+ });
709
+ const schemaChanged = schema.trim() !== schemaPrisma.trim();
710
+ return {
711
+ code: schemaChanged ? schema : "",
712
+ fileName: filePath,
713
+ overwrite: schemaPrismaExist && schemaChanged
714
+ };
715
+ };
716
+ const getNewPrisma = (provider, cwd) => {
717
+ const prismaVersion = getPrismaVersion(cwd);
718
+ return `generator client {
719
+ provider = "${prismaVersion && prismaVersion >= 7 ? "prisma-client" : "prisma-client-js"}"
720
+ }
721
+
722
+ datasource db {
723
+ provider = "${provider}"
724
+ url = ${provider === "sqlite" ? `"file:./dev.db"` : `env("DATABASE_URL")`}
725
+ }`;
726
+ };
727
+
728
+ //#endregion
729
+ //#region src/generators/index.ts
730
+ const adapters = {
731
+ prisma: generatePrismaSchema,
732
+ drizzle: generateDrizzleSchema,
733
+ kysely: generateKyselySchema
734
+ };
735
+ const generateSchema = async (opts) => {
736
+ const adapter = opts.adapter;
737
+ const generator = adapter.id in adapters ? adapters[adapter.id] : null;
738
+ if (generator) return generator(opts);
739
+ if (adapter.createSchema) return adapter.createSchema(opts.options, opts.file).then(({ code, path: fileName, overwrite }) => ({
740
+ code,
741
+ fileName,
742
+ overwrite
743
+ }));
744
+ throw new Error(`${adapter.id} is not supported. If it is a custom adapter, please request the maintainer to implement createSchema`);
745
+ };
746
+
747
+ //#endregion
748
+ export { generateKyselySchema as a, getPackageInfo as i, generateSchema as n, generateDrizzleSchema as o, generatePrismaSchema as r, adapters as t };