better-auth-studio 1.0.20-beta.9 → 1.0.21

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/config.js CHANGED
@@ -1,666 +1,276 @@
1
- import { existsSync, readFileSync } from 'fs';
2
- import { createJiti } from 'jiti';
3
- import { dirname, join } from 'path';
4
- import { pathToFileURL } from 'url';
5
- function resolveModuleWithExtensions(id, parent) {
6
- if (!id.startsWith('./') && !id.startsWith('../')) {
7
- return id;
1
+ // @ts-expect-error
2
+ import babelPresetReact from '@babel/preset-react';
3
+ // @ts-expect-error
4
+ import babelPresetTypeScript from '@babel/preset-typescript';
5
+ import { BetterAuthError, logger } from 'better-auth';
6
+ import { loadConfig } from 'c12';
7
+ import fs, { existsSync } from 'fs';
8
+ import path from 'path';
9
+ import { addSvelteKitEnvModules } from './add-svelte-kit-env-modules.js';
10
+ import { getTsconfigInfo } from './get-tsconfig-info.js';
11
+ let possiblePaths = [
12
+ 'auth.ts',
13
+ 'auth.tsx',
14
+ 'auth.js',
15
+ 'auth.jsx',
16
+ 'auth.server.js',
17
+ 'auth.server.ts',
18
+ ];
19
+ possiblePaths = [
20
+ ...possiblePaths,
21
+ ...possiblePaths.map((it) => `lib/server/${it}`),
22
+ ...possiblePaths.map((it) => `server/${it}`),
23
+ ...possiblePaths.map((it) => `lib/${it}`),
24
+ ...possiblePaths.map((it) => `utils/${it}`),
25
+ ];
26
+ possiblePaths = [
27
+ ...possiblePaths,
28
+ ...possiblePaths.map((it) => `src/${it}`),
29
+ ...possiblePaths.map((it) => `app/${it}`),
30
+ ];
31
+ function resolveReferencePath(configDir, refPath) {
32
+ const resolvedPath = path.resolve(configDir, refPath);
33
+ // If it ends with .json, treat as direct file reference
34
+ if (refPath.endsWith('.json')) {
35
+ return resolvedPath;
8
36
  }
9
- const parentDir = dirname(parent);
10
- const basePath = join(parentDir, id);
11
- const extensions = ['.ts', '.js', '.mjs', '.cjs'];
12
- for (const ext of extensions) {
13
- const fullPath = basePath + ext;
14
- if (existsSync(fullPath)) {
15
- return pathToFileURL(fullPath).href;
37
+ // If the exact path exists and is a file, use it
38
+ if (fs.existsSync(resolvedPath)) {
39
+ try {
40
+ const stats = fs.statSync(resolvedPath);
41
+ if (stats.isFile()) {
42
+ return resolvedPath;
43
+ }
44
+ }
45
+ catch {
46
+ // Fall through to directory handling
16
47
  }
17
48
  }
18
- return id;
49
+ // Otherwise, assume directory reference
50
+ return path.resolve(configDir, refPath, 'tsconfig.json');
19
51
  }
20
- export async function findAuthConfig(configPath) {
21
- if (configPath) {
22
- // Handle both relative and absolute paths
23
- let resolvedPath;
24
- if (configPath.startsWith('/')) {
25
- // Absolute path
26
- resolvedPath = configPath;
27
- }
28
- else {
29
- // Relative path - try multiple resolution strategies
30
- const cwd = process.cwd();
31
- const possiblePaths = [
32
- join(cwd, configPath), // Direct relative to cwd
33
- join(cwd, '..', configPath), // One level up
34
- join(cwd, '../..', configPath), // Two levels up
35
- configPath, // Try as-is (in case it's already resolved)
36
- ];
37
- resolvedPath = possiblePaths.find((path) => existsSync(path)) || join(cwd, configPath);
38
- }
39
- if (existsSync(resolvedPath)) {
40
- try {
41
- const config = await loadConfig(resolvedPath);
42
- if (config) {
43
- return config;
44
- }
45
- }
46
- catch (error) {
47
- console.warn(`Failed to load config from ${resolvedPath}:`, error);
52
+ function getPathAliasesRecursive(tsconfigPath, visited = new Set()) {
53
+ if (visited.has(tsconfigPath)) {
54
+ return {};
55
+ }
56
+ visited.add(tsconfigPath);
57
+ if (!fs.existsSync(tsconfigPath)) {
58
+ logger.warn(`Referenced tsconfig not found: ${tsconfigPath}`);
59
+ return {};
60
+ }
61
+ try {
62
+ const tsConfig = getTsconfigInfo(undefined, tsconfigPath);
63
+ const { paths = {}, baseUrl = '.' } = tsConfig.compilerOptions || {};
64
+ const result = {};
65
+ const configDir = path.dirname(tsconfigPath);
66
+ const obj = Object.entries(paths);
67
+ for (const [alias, aliasPaths] of obj) {
68
+ for (const aliasedPath of aliasPaths) {
69
+ const resolvedBaseUrl = path.resolve(configDir, baseUrl);
70
+ const finalAlias = alias.slice(-1) === '*' ? alias.slice(0, -1) : alias;
71
+ const finalAliasedPath = aliasedPath.slice(-1) === '*' ? aliasedPath.slice(0, -1) : aliasedPath;
72
+ result[finalAlias || ''] = path.join(resolvedBaseUrl, finalAliasedPath);
48
73
  }
49
74
  }
50
- else {
51
- console.warn(`Config file not found: ${resolvedPath}`);
52
- // Try to find the file in common monorepo locations
53
- const cwd = process.cwd();
54
- const commonPaths = [
55
- join(cwd, 'apps', 'backend', 'src', 'auth.ts'),
56
- join(cwd, 'apps', 'backend', 'auth.ts'),
57
- join(cwd, 'packages', 'backend', 'src', 'auth.ts'),
58
- join(cwd, 'packages', 'backend', 'auth.ts'),
59
- join(cwd, 'src', 'auth.ts'),
60
- join(cwd, 'auth.ts'),
61
- ];
62
- for (const path of commonPaths) {
63
- if (existsSync(path)) {
64
- console.log(`Found config file at: ${path}`);
65
- try {
66
- const config = await loadConfig(path);
67
- if (config) {
68
- return config;
69
- }
70
- }
71
- catch (error) {
72
- console.warn(`Failed to load config from ${path}:`, error);
75
+ if (tsConfig.references) {
76
+ for (const ref of tsConfig.references) {
77
+ const refPath = resolveReferencePath(configDir, ref.path);
78
+ const refAliases = getPathAliasesRecursive(refPath, visited);
79
+ for (const [alias, aliasPath] of Object.entries(refAliases)) {
80
+ if (!(alias in result)) {
81
+ result[alias] = aliasPath;
73
82
  }
74
83
  }
75
84
  }
76
85
  }
77
- return null;
86
+ return result;
78
87
  }
79
- const possibleConfigFiles = [
80
- 'studio-config.json',
81
- 'auth.ts',
82
- 'auth.js',
83
- 'src/auth.ts',
84
- 'src/auth.js',
85
- 'lib/auth.ts',
86
- 'lib/auth.js',
87
- 'better-auth.config.ts',
88
- 'better-auth.config.js',
89
- 'better-auth.config.json',
90
- 'auth.config.ts',
91
- 'auth.config.js',
92
- 'auth.config.json',
93
- ];
94
- let currentDir = process.cwd();
95
- const maxDepth = 10;
96
- let depth = 0;
97
- while (currentDir && depth < maxDepth) {
98
- for (const configFile of possibleConfigFiles) {
99
- const configPath = join(currentDir, configFile);
100
- if (existsSync(configPath)) {
101
- try {
102
- const config = await loadConfig(configPath);
103
- if (config) {
104
- return config;
105
- }
106
- }
107
- catch (error) {
108
- console.warn(`Failed to load config from ${configPath}:`, error);
109
- }
110
- }
111
- }
112
- const parentDir = dirname(currentDir);
113
- if (parentDir === currentDir) {
114
- break;
115
- }
116
- currentDir = parentDir;
117
- depth++;
88
+ catch (error) {
89
+ logger.warn(`Error parsing tsconfig at ${tsconfigPath}: ${error}`);
90
+ return {};
118
91
  }
119
- return null;
120
92
  }
121
- async function loadConfig(configPath) {
122
- const ext = configPath.split('.').pop();
93
+ function getPathAliases(cwd) {
94
+ const tsConfigPath = path.join(cwd, 'tsconfig.json');
95
+ if (!fs.existsSync(tsConfigPath)) {
96
+ return null;
97
+ }
123
98
  try {
124
- if (ext === 'json') {
125
- const content = readFileSync(configPath, 'utf-8');
126
- return JSON.parse(content);
127
- }
128
- else if (ext === 'js' || ext === 'ts') {
129
- return await loadTypeScriptConfig(configPath);
130
- }
99
+ const result = getPathAliasesRecursive(tsConfigPath);
100
+ addSvelteKitEnvModules(result);
101
+ return result;
131
102
  }
132
103
  catch (error) {
133
- console.warn(`Error loading config from ${configPath}:`, error);
104
+ console.error(error);
105
+ throw new BetterAuthError('Error parsing tsconfig.json');
134
106
  }
135
- return null;
136
107
  }
137
- async function loadTypeScriptConfig(configPath) {
108
+ /**
109
+ * .tsx files are not supported by Jiti.
110
+ */
111
+ const jitiOptions = (cwd) => {
112
+ const alias = getPathAliases(cwd) || {};
113
+ return {
114
+ transformOptions: {
115
+ babel: {
116
+ presets: [
117
+ [
118
+ babelPresetTypeScript,
119
+ {
120
+ isTSX: true,
121
+ allExtensions: true,
122
+ },
123
+ ],
124
+ [babelPresetReact, { runtime: 'automatic' }],
125
+ ],
126
+ },
127
+ },
128
+ extensions: ['.ts', '.tsx', '.js', '.jsx'],
129
+ alias,
130
+ };
131
+ };
132
+ const isDefaultExport = (object) => {
133
+ return (typeof object === 'object' &&
134
+ object !== null &&
135
+ !Array.isArray(object) &&
136
+ Object.keys(object).length > 0 &&
137
+ 'options' in object);
138
+ };
139
+ export async function getConfig({ cwd, configPath, shouldThrowOnError = false, }) {
138
140
  try {
139
- if (configPath.endsWith('.ts')) {
140
- try {
141
- const aliases = {};
142
- const configDir = dirname(configPath);
143
- const content = readFileSync(configPath, 'utf-8');
144
- const relativeImportRegex = /import\s+.*?\s+from\s+['"](\.\/[^'"]+)['"]/g;
145
- const dynamicImportRegex = /import\s*\(\s*['"](\.\/[^'"]+)['"]\s*\)/g;
146
- const foundImports = new Set();
147
- let match;
148
- while ((match = relativeImportRegex.exec(content)) !== null) {
149
- foundImports.add(match[1]);
150
- }
151
- while ((match = dynamicImportRegex.exec(content)) !== null) {
152
- foundImports.add(match[1]);
153
- }
154
- for (const importPath of foundImports) {
155
- const importName = importPath.replace('./', '');
156
- const possiblePaths = [
157
- join(configDir, importName + '.ts'),
158
- join(configDir, importName + '.js'),
159
- join(configDir, importName + '.mjs'),
160
- join(configDir, importName + '.cjs'),
161
- join(configDir, importName, 'index.ts'),
162
- join(configDir, importName, 'index.js'),
163
- join(configDir, importName, 'index.mjs'),
164
- join(configDir, importName, 'index.cjs'),
165
- ];
166
- for (const path of possiblePaths) {
167
- if (existsSync(path)) {
168
- aliases[importPath] = pathToFileURL(path).href;
169
- break;
170
- }
171
- }
141
+ let configFile = null;
142
+ if (configPath) {
143
+ let resolvedPath = path.join(cwd, configPath);
144
+ if (existsSync(configPath))
145
+ resolvedPath = configPath; // If the configPath is a file, use it as is, as it means the path wasn't relative.
146
+ const { config } = await loadConfig({
147
+ configFile: resolvedPath,
148
+ dotenv: true,
149
+ jitiOptions: jitiOptions(cwd),
150
+ });
151
+ if (!('auth' in config) && !isDefaultExport(config)) {
152
+ if (shouldThrowOnError) {
153
+ throw new Error(`Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`);
172
154
  }
155
+ logger.error(`[#better-auth]: Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`);
156
+ process.exit(1);
157
+ }
158
+ configFile = 'auth' in config ? config.auth?.options : config.options;
159
+ }
160
+ if (!configFile) {
161
+ for (const possiblePath of possiblePaths) {
173
162
  try {
174
- let importPath = configPath;
175
- if (!configPath.startsWith('/')) {
176
- importPath = join(process.cwd(), configPath);
177
- }
178
- console.log({ importPath });
179
- const jitiInstance = createJiti(importPath, {
180
- debug: true,
181
- fsCache: true,
182
- moduleCache: true,
183
- interopDefault: true,
163
+ const { config } = await loadConfig({
164
+ configFile: possiblePath,
165
+ jitiOptions: jitiOptions(cwd),
184
166
  });
185
- console.log({ jitiInstance });
186
- const authModule = await jitiInstance.import(importPath);
187
- console.log({ authModule });
188
- const auth = authModule.auth || authModule.default || authModule;
189
- if (auth && typeof auth === 'object') {
190
- try {
191
- if (auth.$context) {
192
- console.log('Found auth.$context, attempting to await...');
193
- const context = await auth.$context;
194
- const options = context.options;
195
- console.log({ context });
196
- if (!options) {
197
- console.warn('No options found in auth context');
198
- return null;
199
- }
200
- const config = {
201
- database: {
202
- type: options.database ? 'drizzle' : 'unknown',
203
- adapter: 'drizzle-adapter',
204
- ...options.database,
205
- },
206
- emailAndPassword: {
207
- enabled: options.emailAndPassword?.enabled || false,
208
- ...options.emailAndPassword,
209
- },
210
- socialProviders: options.socialProviders
211
- ? Object.keys(options.socialProviders).map((provider) => ({
212
- id: provider,
213
- name: provider,
214
- enabled: true,
215
- }))
216
- : [],
217
- trustedOrigins: options.trustedOrigins || ['http://localhost:3000'],
218
- advanced: {
219
- defaultCookieAttributes: options.advanced?.defaultCookieAttributes || {
220
- sameSite: 'none',
221
- secure: true,
222
- httpOnly: true,
223
- },
224
- ...options.advanced,
225
- },
226
- };
227
- console.log('Returning config from auth.$context:', config);
228
- return config;
167
+ const hasConfig = Object.keys(config).length > 0;
168
+ if (hasConfig) {
169
+ configFile = config.auth?.options || config.default?.options || null;
170
+ if (!configFile) {
171
+ if (shouldThrowOnError) {
172
+ throw new Error("Couldn't read your auth config. Make sure to default export your auth instance or to export as a variable named auth.");
229
173
  }
174
+ logger.error("[#better-auth]: Couldn't read your auth config.");
175
+ console.log('');
176
+ logger.info('[#better-auth]: Make sure to default export your auth instance or to export as a variable named auth.');
177
+ process.exit(1);
230
178
  }
231
- catch (contextError) {
232
- console.warn('Failed to await auth.$context:', contextError.message);
233
- }
179
+ break;
234
180
  }
235
181
  }
236
- catch (importError) {
237
- console.warn(`Failed to import auth config from ${configPath}:`, importError.message);
238
- }
239
- const config = {
240
- database: {
241
- type: 'drizzle',
242
- adapter: 'drizzle-adapter',
243
- },
244
- emailAndPassword: {
245
- enabled: true,
246
- },
247
- trustedOrigins: ['http://localhost:3000'],
248
- advanced: {
249
- defaultCookieAttributes: {
250
- sameSite: 'none',
251
- secure: true,
252
- httpOnly: true,
253
- },
254
- },
255
- };
256
- return config;
257
- }
258
- catch (importError) {
259
- console.warn(`Failed to import auth config from ${configPath}:`, importError.message);
260
- }
261
- }
262
- const content = readFileSync(configPath, 'utf-8');
263
- const authConfig = extractBetterAuthConfig(content);
264
- if (authConfig) {
265
- return authConfig;
266
- }
267
- if (configPath.endsWith('.js')) {
268
- return await evaluateJSConfig(configPath);
269
- }
270
- return null;
271
- }
272
- catch (error) {
273
- console.warn(`Error loading TypeScript config from ${configPath}:`, error);
274
- return null;
275
- }
276
- }
277
- function detectDatabaseAdapter(content) {
278
- const database = {};
279
- if (content.includes('drizzleAdapter')) {
280
- database.adapter = 'drizzle';
281
- const providerMatch = content.match(/drizzleAdapter\s*\(\s*\w+\s*,\s*\{[^}]*provider\s*:\s*["']([^"']+)["'][^}]*\}/);
282
- if (providerMatch) {
283
- const provider = providerMatch[1];
284
- database.provider = provider;
285
- database.type = provider === 'pg' ? 'postgresql' : provider;
286
- }
287
- else {
288
- database.provider = 'postgresql';
289
- database.type = 'postgresql';
290
- }
291
- }
292
- else if (content.includes('prismaAdapter')) {
293
- database.adapter = 'prisma';
294
- const providerMatch = content.match(/prismaAdapter\s*\(\s*\w+\s*,\s*\{[^}]*provider\s*:\s*["']([^"']+)["'][^}]*\}/);
295
- if (providerMatch) {
296
- database.provider = providerMatch[1];
297
- database.type = providerMatch[1];
298
- }
299
- else {
300
- database.provider = 'postgresql';
301
- database.type = 'postgresql';
302
- }
303
- }
304
- else if (content.includes('better-sqlite3') || content.includes('new Database(')) {
305
- database.adapter = 'sqlite';
306
- database.type = 'sqlite';
307
- database.provider = 'sqlite';
308
- const dbPathMatch = content.match(/new\s+Database\s*\(\s*["']([^"']+)["']\s*\)/);
309
- if (dbPathMatch) {
310
- database.name = dbPathMatch[1];
311
- }
312
- }
313
- const urlMatch = content.match(/DATABASE_URL|DB_URL|DB_CONNECTION_STRING/);
314
- if (urlMatch) {
315
- database.url = `process.env.${urlMatch[0]}`;
316
- }
317
- return database;
318
- }
319
- function cleanConfigString(configStr) {
320
- let cleaned = configStr;
321
- cleaned = cleaned.replace(/:\s*prismaAdapter\s*\(\s*\w+\s*,\s*\{[^}]*\}\s*\)/g, ':"prisma-adapter"');
322
- cleaned = cleaned.replace(/:\s*prismaAdapter\s*\(\s*\w+\s*\)/g, ':"prisma-adapter"');
323
- cleaned = cleaned.replace(/:\s*drizzleAdapter\s*\(\s*\w+\s*,\s*\{[^}]*\}\s*\)/g, ':"drizzle-adapter"');
324
- cleaned = cleaned.replace(/:\s*drizzleAdapter\s*\(\s*\w+\s*\)/g, ':"drizzle-adapter"');
325
- cleaned = cleaned.replace(/:\s*betterSqlite3\s*\(\s*[^)]*\)/g, ':"better-sqlite3"');
326
- cleaned = cleaned.replace(/:\s*postgres\s*\(\s*[^)]*\)/g, ':"postgres"');
327
- cleaned = cleaned.replace(/:\s*mysql2\s*\(\s*[^)]*\)/g, ':"mysql2"');
328
- cleaned = cleaned.replace(/:\s*bun:sqlite\s*\(\s*[^)]*\)/g, ':"bun-sqlite"');
329
- cleaned = cleaned.replace(/:\s*new\s+Database\s*\(\s*[^)]*\)/g, ':"sqlite-database"');
330
- cleaned = cleaned.replace(/new\s+Database\s*\(\s*[^)]*\)/g, '"sqlite-database"');
331
- cleaned = cleaned.replace(/:\s*(\w+)\s*\(\s*[^)]*\)/g, ':"$1-function"');
332
- cleaned = cleaned.replace(/:\s*\[[^\]]*\]/g, ':"array"');
333
- cleaned = cleaned.replace(/:\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g, ':"object"');
334
- cleaned = cleaned.replace(/:\s*process\.env\.(\w+)(\s*\|\|\s*"[^"]*")?/g, ':"$1"');
335
- cleaned = cleaned.replace(/:\s*`([^`]*)`/g, ':"$1"');
336
- cleaned = cleaned.replace(/:\s*"([^"]*)"/g, ':"$1"');
337
- cleaned = cleaned.replace(/:\s*'([^']*)'/g, ':"$1"');
338
- cleaned = cleaned.replace(/:\s*([a-zA-Z_][a-zA-Z0-9_]*)(?=\s*[,}])/g, ':"$1"');
339
- cleaned = cleaned.replace(/([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '"$1":');
340
- cleaned = cleaned.replace(/,\s*}/g, '}');
341
- cleaned = cleaned.replace(/,\s*]/g, ']');
342
- cleaned = cleaned.replace(/\/\/.*$/gm, '');
343
- cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, '');
344
- cleaned = cleaned.replace(/\s+/g, ' ');
345
- cleaned = cleaned.replace(/:\s*(\d+\.?\d*)/g, ':$1');
346
- cleaned = cleaned.replace(/:\s*(true|false)/g, ':$1');
347
- cleaned = cleaned.replace(/:\s*null/g, ':null');
348
- return cleaned.trim();
349
- }
350
- export function extractBetterAuthConfig(content) {
351
- const pluginsMatch = content.match(/plugins\s*:\s*(?:\[)?(\w+)(?:\])?/);
352
- if (pluginsMatch) {
353
- const pluginsVar = pluginsMatch[1];
354
- const varMatch = content.match(new RegExp(`const\\s+${pluginsVar}\\s*=\\s*[^\\[]*\\[([^\\]]*)\\]`));
355
- if (varMatch) {
356
- const pluginsContent = varMatch[1];
357
- const plugins = [];
358
- const pluginMatches = pluginsContent.match(/(\w+)\(\)/g);
359
- if (pluginMatches) {
360
- for (const pluginMatch of pluginMatches) {
361
- const pluginName = pluginMatch.replace(/\(\)/, '');
362
- const plugin = {
363
- id: pluginName,
364
- name: pluginName,
365
- version: 'unknown',
366
- description: `${pluginName} plugin for Better Auth`,
367
- enabled: true,
368
- };
369
- if (pluginName === 'organization') {
370
- const orgConfigMatch = content.match(/organization\s*\(\s*\{[^}]*teams[^}]*enabled[^}]*\}/);
371
- if (orgConfigMatch) {
372
- plugin.teams = { enabled: true };
182
+ catch (e) {
183
+ if (typeof e === 'object' &&
184
+ e &&
185
+ 'message' in e &&
186
+ typeof e.message === 'string' &&
187
+ e.message.includes('This module cannot be imported from a Client Component module')) {
188
+ if (shouldThrowOnError) {
189
+ throw new Error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
373
190
  }
191
+ logger.error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
192
+ process.exit(1);
374
193
  }
375
- plugins.push(plugin);
376
- }
377
- }
378
- if (plugins.length > 0) {
379
- const database = detectDatabaseAdapter(content);
380
- return {
381
- plugins: plugins,
382
- baseURL: 'http://localhost:3000',
383
- database: database,
384
- };
385
- }
386
- }
387
- }
388
- const directPluginsMatch = content.match(/plugins\s*:\s*\[([^\]]*)\]/);
389
- if (directPluginsMatch) {
390
- const pluginsContent = directPluginsMatch[1];
391
- const plugins = [];
392
- const pluginMatches = pluginsContent.match(/(\w+)\(\)/g);
393
- if (pluginMatches) {
394
- for (const pluginMatch of pluginMatches) {
395
- const pluginName = pluginMatch.replace(/\(\)/, '');
396
- const plugin = {
397
- id: pluginName,
398
- name: pluginName,
399
- version: 'unknown',
400
- description: `${pluginName} plugin for Better Auth`,
401
- enabled: true,
402
- };
403
- if (pluginName === 'organization') {
404
- const orgConfigMatch = content.match(/organization\s*\(\s*\{[^}]*teams[^}]*enabled[^}]*\}/);
405
- if (orgConfigMatch) {
406
- plugin.teams = { enabled: true };
194
+ if (shouldThrowOnError) {
195
+ throw e;
407
196
  }
197
+ logger.error("[#better-auth]: Couldn't read your auth config.", e);
198
+ process.exit(1);
408
199
  }
409
- plugins.push(plugin);
410
200
  }
411
201
  }
412
- if (plugins.length > 0) {
413
- const database = detectDatabaseAdapter(content);
414
- return {
415
- plugins: plugins,
416
- baseURL: 'http://localhost:3000',
417
- database: database,
418
- };
419
- }
202
+ return configFile;
420
203
  }
421
- const patterns = [
422
- /export\s+const\s+\w+\s*=\s*betterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
423
- /export\s+const\s+\w+\s*=\s*BetterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
424
- /const\s+\w+\s*=\s*betterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
425
- /const\s+\w+\s*=\s*BetterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
426
- /export\s+default\s+betterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
427
- /export\s+default\s+BetterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
428
- /module\.exports\s*=\s*betterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
429
- /module\.exports\s*=\s*BetterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
430
- /betterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
431
- /BetterAuth\s*\(\s*({[^{}]*(?:{[^{}]*}[^{}]*)*})\s*\)/,
432
- /betterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
433
- /BetterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
434
- /betterAuth\s*\(\s*({[^{}]*baseURL[^{}]*database[^{}]*})\s*\)/,
435
- /BetterAuth\s*\(\s*({[^{}]*baseURL[^{}]*database[^{}]*})\s*\)/,
436
- ];
437
- for (let i = 0; i < patterns.length; i++) {
438
- const pattern = patterns[i];
439
- const match = content.match(pattern);
440
- if (match) {
441
- try {
442
- let configStr = match[1];
443
- configStr = configStr
444
- .replace(/(\d+\s*\*\s*\d+\s*\*\s*\d+\s*\*\s*\d+)/g, (match) => {
445
- try {
446
- return eval(match).toString();
447
- }
448
- catch {
449
- return match;
450
- }
451
- })
452
- .replace(/(\d+\s*\*\s*\d+\s*\*\s*\d+)/g, (match) => {
453
- try {
454
- return eval(match).toString();
455
- }
456
- catch {
457
- return match;
458
- }
459
- })
460
- .replace(/(\d+\s*\*\s*\d+)/g, (match) => {
461
- try {
462
- return eval(match).toString();
463
- }
464
- catch {
465
- return match;
466
- }
467
- });
468
- configStr = cleanConfigString(configStr);
469
- let config;
470
- try {
471
- config = JSON.parse(configStr);
472
- }
473
- catch (error) {
474
- console.warn(`Failed to parse config: ${error instanceof Error ? error.message : 'Unknown error'}`);
475
- console.warn('Config string that failed:', configStr.substring(0, 200) + '...');
476
- return null;
477
- }
478
- const authConfig = extractBetterAuthFields(config);
479
- if (authConfig) {
480
- const detectedDatabase = detectDatabaseAdapter(content);
481
- if (detectedDatabase.adapter) {
482
- authConfig.database = { ...authConfig.database, ...detectedDatabase };
483
- }
484
- }
485
- return authConfig;
486
- }
487
- catch (error) {
488
- console.warn(`Failed to parse config pattern: ${error instanceof Error ? error.message : 'Unknown error'}`);
204
+ catch (e) {
205
+ if (typeof e === 'object' &&
206
+ e &&
207
+ 'message' in e &&
208
+ typeof e.message === 'string' &&
209
+ e.message.includes('This module cannot be imported from a Client Component module')) {
210
+ if (shouldThrowOnError) {
211
+ throw new Error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
489
212
  }
213
+ logger.error(`Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`);
214
+ process.exit(1);
490
215
  }
491
- }
492
- return null;
493
- }
494
- function extractBetterAuthFields(config) {
495
- const authConfig = {};
496
- if (config.database) {
497
- let dbType = 'postgresql';
498
- let dbName = config.database.name;
499
- let adapter = 'unknown';
500
- if (typeof config.database === 'function') {
501
- if (config.database.options?.adapterId) {
502
- adapter = config.database.options.adapterId;
503
- if (config.database.options.provider) {
504
- const provider = config.database.options.provider;
505
- dbType = provider === 'pg' ? 'postgresql' : provider;
506
- }
507
- }
508
- else if (config.database.options?.provider) {
509
- const provider = config.database.options.provider;
510
- if (provider === 'pg' || provider === 'mysql' || provider === 'sqlite') {
511
- adapter = 'drizzle';
512
- dbType = provider === 'pg' ? 'postgresql' : provider;
513
- }
514
- else {
515
- adapter = 'prisma';
516
- dbType = 'postgresql';
517
- }
518
- }
519
- else if (config.database.provider) {
520
- const provider = config.database.provider;
521
- if (provider === 'pg' || provider === 'mysql' || provider === 'sqlite') {
522
- adapter = 'drizzle';
523
- dbType = provider === 'pg' ? 'postgresql' : provider;
524
- }
525
- else {
526
- adapter = 'prisma';
527
- dbType = 'postgresql';
528
- }
529
- }
530
- else {
531
- adapter = 'prisma';
532
- dbType = 'postgresql';
533
- }
216
+ if (shouldThrowOnError) {
217
+ throw e;
534
218
  }
535
- else if (config.database.constructor?.name === 'Database' ||
536
- (typeof config.database === 'object' &&
537
- config.database.constructor &&
538
- config.database.constructor.name === 'Database')) {
539
- dbType = 'sqlite';
540
- dbName = config.database.name || './better-auth.db';
541
- adapter = 'sqlite';
542
- }
543
- else if (config.database.name?.endsWith('.db') ||
544
- (typeof config.database === 'string' && config.database.endsWith('.db'))) {
545
- dbType = 'sqlite';
546
- adapter = 'sqlite';
547
- }
548
- else if (config.database.provider) {
549
- const provider = config.database.provider;
550
- if (provider === 'pg' ||
551
- provider === 'postgresql' ||
552
- provider === 'mysql' ||
553
- provider === 'sqlite') {
554
- adapter = 'drizzle';
555
- dbType = provider === 'pg' ? 'postgresql' : provider;
556
- }
557
- else {
558
- adapter = 'prisma';
559
- dbType = provider;
560
- }
561
- }
562
- else if (config.database.adapter) {
563
- adapter = config.database.adapter;
564
- if (config.database.provider) {
565
- const provider = config.database.provider;
566
- if (provider === 'pg' ||
567
- provider === 'postgresql' ||
568
- provider === 'mysql' ||
569
- provider === 'sqlite') {
570
- adapter = 'drizzle';
571
- dbType = provider === 'pg' ? 'postgresql' : provider;
572
- }
573
- else {
574
- dbType = provider;
575
- }
576
- }
577
- else {
578
- dbType = adapter;
579
- }
580
- }
581
- else if (config.database.type) {
582
- dbType = config.database.type;
583
- adapter = config.database.type;
584
- }
585
- if (config.database.provider &&
586
- (config.database.provider === 'postgresql' ||
587
- config.database.provider === 'pg' ||
588
- config.database.provider === 'mysql' ||
589
- config.database.provider === 'sqlite')) {
590
- adapter = 'drizzle';
591
- dbType = config.database.provider === 'pg' ? 'postgresql' : config.database.provider;
592
- }
593
- authConfig.database = {
594
- url: config.database.url || config.database.connectionString,
595
- name: dbName,
596
- type: adapter === 'drizzle' ? `${dbType} (${adapter})` : dbType,
597
- adapter: adapter,
598
- dialect: config.database.dialect,
599
- provider: config.database.provider,
600
- casing: config.database.casing,
601
- };
602
- }
603
- if (config.socialProviders) {
604
- if (typeof config.socialProviders === 'object' && !Array.isArray(config.socialProviders)) {
605
- authConfig.socialProviders = config.socialProviders;
606
- authConfig.providers = Object.entries(config.socialProviders).map(([provider, config]) => ({
607
- type: provider,
608
- clientId: config.clientId,
609
- clientSecret: config.clientSecret,
610
- redirectUri: config.redirectUri,
611
- ...config,
612
- }));
613
- }
614
- else if (Array.isArray(config.socialProviders)) {
615
- authConfig.socialProviders = config.socialProviders;
616
- authConfig.providers = config.socialProviders;
617
- }
618
- }
619
- if (config.providers && Array.isArray(config.providers)) {
620
- authConfig.providers = config.providers.map((provider) => ({
621
- type: provider.type || provider.id,
622
- clientId: provider.clientId || provider.client_id,
623
- clientSecret: provider.clientSecret || provider.client_secret,
624
- ...provider,
625
- }));
219
+ logger.error("Couldn't read your auth config.", e);
220
+ process.exit(1);
626
221
  }
627
- if (config.emailAndPassword) {
628
- authConfig.emailAndPassword = config.emailAndPassword;
629
- }
630
- if (config.session) {
631
- authConfig.session = config.session;
632
- }
633
- if (config.secret) {
634
- authConfig.secret = config.secret;
635
- }
636
- if (config.rateLimit) {
637
- authConfig.rateLimit = config.rateLimit;
638
- }
639
- if (config.telemetry) {
640
- authConfig.telemetry = config.telemetry;
641
- }
642
- if (config.plugins) {
643
- authConfig.plugins = config.plugins.map((plugin) => plugin.id);
644
- }
645
- return authConfig;
646
222
  }
647
- async function evaluateJSConfig(configPath) {
223
+ export { possiblePaths };
224
+ // Legacy function for backward compatibility - kept for routes.ts
225
+ export function extractBetterAuthConfig(content) {
226
+ // This is a simplified version that returns null
227
+ // The actual config loading is now handled by the better-auth getConfig function
228
+ return null;
229
+ }
230
+ export async function findAuthConfig(configPath) {
648
231
  try {
649
- const config = require(configPath);
650
- if (config.auth) {
651
- const authConfig = config.auth.options || config.auth;
652
- return extractBetterAuthFields(authConfig);
653
- }
654
- if (config.default) {
655
- return extractBetterAuthFields(config.default);
656
- }
657
- else if (typeof config === 'object') {
658
- return extractBetterAuthFields(config);
232
+ const betterAuthConfig = await getConfig({
233
+ cwd: process.cwd(),
234
+ configPath,
235
+ shouldThrowOnError: false,
236
+ });
237
+ if (betterAuthConfig) {
238
+ // Convert BetterAuthOptions to AuthConfig format
239
+ const authConfig = {
240
+ database: {
241
+ type: betterAuthConfig.database ? 'drizzle' : 'unknown',
242
+ adapter: 'drizzle-adapter',
243
+ ...betterAuthConfig.database,
244
+ },
245
+ emailAndPassword: {
246
+ enabled: betterAuthConfig.emailAndPassword?.enabled || false,
247
+ ...betterAuthConfig.emailAndPassword,
248
+ },
249
+ socialProviders: betterAuthConfig.socialProviders
250
+ ? Object.keys(betterAuthConfig.socialProviders).map((provider) => ({
251
+ id: provider,
252
+ name: provider,
253
+ enabled: true,
254
+ }))
255
+ : [],
256
+ trustedOrigins: Array.isArray(betterAuthConfig.trustedOrigins)
257
+ ? betterAuthConfig.trustedOrigins
258
+ : ['http://localhost:3000'],
259
+ advanced: {
260
+ defaultCookieAttributes: betterAuthConfig.advanced?.defaultCookieAttributes || {
261
+ sameSite: 'none',
262
+ secure: true,
263
+ httpOnly: true,
264
+ },
265
+ ...betterAuthConfig.advanced,
266
+ },
267
+ };
268
+ return authConfig;
659
269
  }
660
270
  return null;
661
271
  }
662
272
  catch (error) {
663
- console.warn(`Error evaluating JS config from ${configPath}:`, error);
273
+ console.warn(`Failed to load config:`, error);
664
274
  return null;
665
275
  }
666
276
  }