better-auth-studio 1.0.59-beta.1 → 1.0.59-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAOA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAS9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8D9C,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAyLhG;AAwBD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAi8KR"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAeA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAS9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+D9C,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAqLhG;AAeD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA4mLR"}
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHmac, randomBytes } from 'node:crypto';
2
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync, writeFileSync, } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
5
  // @ts-expect-error
@@ -8,6 +8,7 @@ import { scryptAsync } from '@noble/hashes/scrypt.js';
8
8
  import { Router } from 'express';
9
9
  import { createJiti } from 'jiti';
10
10
  import { createMockAccount, createMockSession, createMockUser, createMockVerification, getAuthAdapter, } from './auth-adapter.js';
11
+ import { possiblePaths } from './config.js';
11
12
  import { getAuthData } from './data.js';
12
13
  import { initializeGeoService, resolveIPLocation, setGeoDbPath } from './geo-service.js';
13
14
  import { detectDatabaseWithDialect } from './utils/database-detection.js';
@@ -66,7 +67,6 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
66
67
  if (authConfigPath.endsWith('.ts')) {
67
68
  const aliases = {};
68
69
  const authConfigDir = dirname(authConfigPath);
69
- // Find project root by looking for tsconfig.json
70
70
  let projectDir = authConfigDir;
71
71
  let tsconfigPath = join(projectDir, 'tsconfig.json');
72
72
  while (!existsSync(tsconfigPath) && projectDir !== dirname(projectDir)) {
@@ -74,10 +74,8 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
74
74
  tsconfigPath = join(projectDir, 'tsconfig.json');
75
75
  }
76
76
  const content = readFileSync(authConfigPath, 'utf-8');
77
- // Get path aliases from tsconfig (including extends chain)
78
77
  const { getPathAliases } = await import('./config.js');
79
78
  const tsconfigAliases = getPathAliases(projectDir) || {};
80
- // Handle relative imports
81
79
  const relativeImportRegex = /import\s+.*?\s+from\s+['"](\.\/[^'"]+)['"]/g;
82
80
  const dynamicImportRegex = /import\s*\(\s*['"](\.\/[^'"]+)['"]\s*\)/g;
83
81
  const foundImports = new Set();
@@ -111,7 +109,6 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
111
109
  const pathAliasRegex = /import\s+.*?\s+from\s+['"](\$[^'"]+)['"]/g;
112
110
  while ((match = pathAliasRegex.exec(content)) !== null) {
113
111
  const aliasPath = match[1];
114
- // Check if we have a matching alias
115
112
  const aliasBase = aliasPath.split('/')[0];
116
113
  if (tsconfigAliases[aliasBase]) {
117
114
  const remainingPath = aliasPath.replace(aliasBase, '').replace(/^\//, '');
@@ -222,14 +219,6 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
222
219
  async function findAuthConfigPath() {
223
220
  const { join, dirname } = await import('node:path');
224
221
  const { existsSync } = await import('node:fs');
225
- const possiblePaths = [
226
- 'auth.js',
227
- 'auth.ts',
228
- 'src/auth.js',
229
- 'src/auth.ts',
230
- 'lib/auth.js',
231
- 'lib/auth.ts',
232
- ];
233
222
  for (const path of possiblePaths) {
234
223
  const fullPath = join(process.cwd(), path);
235
224
  if (existsSync(fullPath)) {
@@ -4354,15 +4343,44 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
4354
4343
  const fields = table.fields
4355
4344
  ?.filter((f) => f.name.trim())
4356
4345
  .map((field) => {
4357
- let attrStr = `type: "${field.type}"`;
4358
- if (field.required)
4359
- attrStr += ',\n required: true';
4360
- if (field.unique)
4361
- attrStr += ',\n unique: true';
4346
+ const attrs = [`type: "${field.type}"`];
4347
+ attrs.push(`required: ${field.required ? 'true' : 'false'}`);
4348
+ attrs.push(`unique: ${field.unique ? 'true' : 'false'}`);
4349
+ attrs.push('input: false');
4350
+ // Handle defaultValue
4351
+ if (field.defaultValue !== undefined &&
4352
+ field.defaultValue !== null &&
4353
+ field.defaultValue !== '') {
4354
+ if (field.type === 'string') {
4355
+ attrs.push(`defaultValue: "${field.defaultValue}"`);
4356
+ }
4357
+ else if (field.type === 'boolean') {
4358
+ attrs.push(`defaultValue: ${field.defaultValue === 'true' || field.defaultValue === true}`);
4359
+ }
4360
+ else if (field.type === 'number') {
4361
+ attrs.push(`defaultValue: ${field.defaultValue}`);
4362
+ }
4363
+ else if (field.type === 'date') {
4364
+ if (field.defaultValue === 'now()') {
4365
+ attrs.push('defaultValue: new Date()');
4366
+ }
4367
+ else {
4368
+ attrs.push(`defaultValue: new Date("${field.defaultValue}")`);
4369
+ }
4370
+ }
4371
+ }
4372
+ else if (field.type === 'boolean') {
4373
+ // Default to false for boolean if no defaultValue specified
4374
+ attrs.push('defaultValue: false');
4375
+ }
4376
+ const attrStr = attrs.join(',\n ');
4362
4377
  return ` ${field.name}: {\n ${attrStr}\n }`;
4363
4378
  })
4364
4379
  .join(',\n') || '';
4365
- return ` ${table.name}: {
4380
+ const tableName = table.isExtending && table.extendedTableName
4381
+ ? table.extendedTableName.trim()
4382
+ : table.name.trim();
4383
+ return ` ${tableName}: {
4366
4384
  fields: {
4367
4385
  ${fields}
4368
4386
  },
@@ -4370,7 +4388,29 @@ ${fields}
4370
4388
  })
4371
4389
  .join(',\n')
4372
4390
  : '';
4373
- // Generate hooks
4391
+ const preserveIndentation = (code, baseIndent) => {
4392
+ if (!code.trim())
4393
+ return '';
4394
+ const lines = code.split('\n');
4395
+ const nonEmptyLines = lines.filter((line) => line.trim());
4396
+ if (nonEmptyLines.length === 0)
4397
+ return '';
4398
+ const minIndent = Math.min(...nonEmptyLines.map((line) => {
4399
+ const match = line.match(/^(\s*)/);
4400
+ return match ? match[1].length : 0;
4401
+ }));
4402
+ return lines
4403
+ .map((line) => {
4404
+ if (!line.trim())
4405
+ return '';
4406
+ const currentIndent = line.match(/^(\s*)/)?.[1] || '';
4407
+ const relativeIndent = Math.max(0, currentIndent.length - minIndent);
4408
+ const content = line.trim();
4409
+ return baseIndent + ' '.repeat(relativeIndent) + content;
4410
+ })
4411
+ .filter(Boolean)
4412
+ .join('\n');
4413
+ };
4374
4414
  const beforeHooks = hooks
4375
4415
  .filter((h) => h.timing === 'before')
4376
4416
  .map((hook) => {
@@ -4390,11 +4430,11 @@ ${fields}
4390
4430
  else {
4391
4431
  matcher = `(ctx) => true`;
4392
4432
  }
4433
+ const formattedHookLogic = preserveIndentation(hook.hookLogic || '// Hook logic here', ' ');
4393
4434
  return ` {
4394
4435
  matcher: ${matcher},
4395
4436
  handler: createAuthMiddleware(async (ctx) => {
4396
- // ${hook.name || `${hook.timing} ${hook.action} hook`}
4397
- ${hook.hookLogic || '// Hook logic here'}
4437
+ ${formattedHookLogic}
4398
4438
  }),
4399
4439
  }`;
4400
4440
  });
@@ -4417,26 +4457,25 @@ ${fields}
4417
4457
  else {
4418
4458
  matcher = `(ctx) => true`;
4419
4459
  }
4460
+ const formattedHookLogic = preserveIndentation(hook.hookLogic || '// Hook logic here', ' ');
4420
4461
  return ` {
4421
4462
  matcher: ${matcher},
4422
4463
  handler: createAuthMiddleware(async (ctx) => {
4423
- // ${hook.name || `${hook.timing} ${hook.action} hook`}
4424
- ${hook.hookLogic || '// Hook logic here'}
4464
+ ${formattedHookLogic}
4425
4465
  }),
4426
4466
  }`;
4427
4467
  });
4428
4468
  const middlewareCode = middleware
4429
4469
  .map((mw) => {
4470
+ const formattedMiddlewareLogic = preserveIndentation(mw.middlewareLogic || '// Middleware logic here', ' ');
4430
4471
  return ` {
4431
4472
  path: "${mw.path}",
4432
4473
  middleware: createAuthMiddleware(async (ctx) => {
4433
- // ${mw.name || 'Middleware'}
4434
- ${mw.middlewareLogic || '// Middleware logic here'}
4474
+ ${formattedMiddlewareLogic}
4435
4475
  }),
4436
4476
  }`;
4437
4477
  })
4438
4478
  .join(',\n');
4439
- // Generate endpoints
4440
4479
  const endpointsCode = endpoints.length > 0
4441
4480
  ? endpoints
4442
4481
  .map((endpoint) => {
@@ -4444,27 +4483,14 @@ ${fields}
4444
4483
  const sanitizedName = endpointName.replace(/[^a-zA-Z0-9]/g, '');
4445
4484
  const endpointPath = endpoint.path?.trim() || `/${camelCaseName}/${sanitizedName}`;
4446
4485
  const handlerLogic = endpoint.handlerLogic ||
4447
- '// Endpoint handler logic here\n return ctx.json({ success: true });';
4448
- const formattedHandlerLogic = handlerLogic
4449
- .split('\n')
4450
- .map((line) => {
4451
- const trimmed = line.trim();
4452
- if (!trimmed)
4453
- return '';
4454
- if (!line.startsWith(' ')) {
4455
- return ' ' + trimmed;
4456
- }
4457
- return line;
4458
- })
4459
- .filter(Boolean)
4460
- .join('\n');
4486
+ '// Endpoint handler logic here\nreturn ctx.json({ success: true });';
4487
+ const formattedHandlerLogic = preserveIndentation(handlerLogic, ' ');
4461
4488
  return ` ${sanitizedName}: createAuthEndpoint(
4462
4489
  "${endpointPath}",
4463
4490
  {
4464
4491
  method: "${endpoint.method || 'POST'}",
4465
4492
  },
4466
4493
  async (ctx) => {
4467
- // ${endpoint.name || sanitizedName}
4468
4494
  ${formattedHandlerLogic}
4469
4495
  },
4470
4496
  ),`;
@@ -4530,7 +4556,6 @@ ${formattedHandlerLogic}
4530
4556
  if (rateLimitCode) {
4531
4557
  pluginParts.push(` rateLimit: {\n${rateLimitCode}\n }`);
4532
4558
  }
4533
- // Generate server plugin code
4534
4559
  const imports = ['import type { BetterAuthPlugin } from "@better-auth/core"'];
4535
4560
  if (hooks.length > 0 || middleware.length > 0 || endpoints.length > 0) {
4536
4561
  imports.push('import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api"');
@@ -4577,12 +4602,11 @@ import type { ${camelCaseName} } from "..";
4577
4602
 
4578
4603
  export const ${camelCaseName}Client = () => {
4579
4604
  return {
4580
- id: "${camelCaseName}" as const,
4605
+ id: "${camelCaseName}",
4581
4606
  $InferServerPlugin: {} as ReturnType<typeof ${camelCaseName}>,${pathMethods ? `\n pathMethods: {\n${pathMethods}\n },` : ''}${atomListenersCode}
4582
4607
  } satisfies BetterAuthClientPlugin;
4583
4608
  };
4584
4609
  `);
4585
- // Generate server setup code
4586
4610
  const serverSetupCode = cleanCode(`import { betterAuth } from "@better-auth/core";
4587
4611
  import { ${camelCaseName} } from "./plugin/${camelCaseName}";
4588
4612
 
@@ -4600,7 +4624,6 @@ export const auth = betterAuth({
4600
4624
  vue: 'better-auth/vue',
4601
4625
  };
4602
4626
  const frameworkImport = frameworkImportMap[clientFramework] || 'better-auth/react';
4603
- // Get baseURL based on framework
4604
4627
  const baseURLMap = {
4605
4628
  react: 'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"',
4606
4629
  svelte: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
@@ -4984,6 +5007,110 @@ export const authClient = createAuthClient({
4984
5007
  });
4985
5008
  }
4986
5009
  });
5010
+ router.post('/api/tools/check-env-secret', async (_req, res) => {
5011
+ try {
5012
+ const envPath = join(process.cwd(), '.env');
5013
+ const envLocalPath = join(process.cwd(), '.env.local');
5014
+ const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
5015
+ const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
5016
+ const envLines = envContent.split('\n');
5017
+ let existingSecret;
5018
+ envLines.forEach((line) => {
5019
+ const trimmed = line.trim();
5020
+ if (!trimmed || trimmed.startsWith('#'))
5021
+ return;
5022
+ const match = trimmed.match(/^BETTER_AUTH_SECRET\s*=\s*(.*)$/i);
5023
+ if (match) {
5024
+ let value = match[1].trim();
5025
+ if (value.length >= 2) {
5026
+ if ((value.startsWith('"') && value.endsWith('"')) ||
5027
+ (value.startsWith("'") && value.endsWith("'"))) {
5028
+ value = value.slice(1, -1).trim();
5029
+ }
5030
+ }
5031
+ if (value && value.trim() !== '') {
5032
+ existingSecret = value;
5033
+ }
5034
+ }
5035
+ });
5036
+ const hasExisting = !!existingSecret && existingSecret.trim() !== '';
5037
+ res.json({
5038
+ success: true,
5039
+ hasExisting,
5040
+ existingSecret: hasExisting ? existingSecret : undefined,
5041
+ path: targetPath,
5042
+ });
5043
+ }
5044
+ catch (error) {
5045
+ res.status(500).json({
5046
+ success: false,
5047
+ message: error instanceof Error ? error.message : 'Failed to check secret',
5048
+ });
5049
+ }
5050
+ });
5051
+ router.post('/api/tools/write-env-secret', async (req, res) => {
5052
+ try {
5053
+ const { secret, action = 'override' } = req.body || {};
5054
+ if (!secret) {
5055
+ return res.status(400).json({
5056
+ success: false,
5057
+ message: 'Secret is required',
5058
+ });
5059
+ }
5060
+ const envPath = join(process.cwd(), '.env');
5061
+ const envLocalPath = join(process.cwd(), '.env.local');
5062
+ const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
5063
+ const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
5064
+ const envLines = envContent.split('\n');
5065
+ const envMap = new Map();
5066
+ const newLines = [];
5067
+ envLines.forEach((line, index) => {
5068
+ const trimmed = line.trim();
5069
+ if (!trimmed || trimmed.startsWith('#')) {
5070
+ newLines.push(line);
5071
+ return;
5072
+ }
5073
+ const match = trimmed.match(/^([^=#]+)\s*=\s*(.*)$/);
5074
+ if (match) {
5075
+ const key = match[1].trim();
5076
+ if (key.toUpperCase() === 'BETTER_AUTH_SECRET') {
5077
+ envMap.set('BETTER_AUTH_SECRET', { line, index });
5078
+ if (action === 'override') {
5079
+ newLines.push(`BETTER_AUTH_SECRET=${secret}`);
5080
+ }
5081
+ else {
5082
+ newLines.push(line);
5083
+ }
5084
+ }
5085
+ else {
5086
+ newLines.push(line);
5087
+ }
5088
+ }
5089
+ else {
5090
+ newLines.push(line);
5091
+ }
5092
+ });
5093
+ if (!envMap.has('BETTER_AUTH_SECRET')) {
5094
+ if (newLines.length > 0 && newLines[newLines.length - 1].trim() !== '') {
5095
+ newLines.push('');
5096
+ }
5097
+ newLines.push(`BETTER_AUTH_SECRET=${secret}`);
5098
+ }
5099
+ const newContent = newLines.join('\n');
5100
+ writeFileSync(targetPath, newContent, 'utf-8');
5101
+ res.json({
5102
+ success: true,
5103
+ message: 'Secret written successfully',
5104
+ path: targetPath,
5105
+ });
5106
+ }
5107
+ catch (error) {
5108
+ res.status(500).json({
5109
+ success: false,
5110
+ message: error instanceof Error ? error.message : 'Failed to write secret to .env',
5111
+ });
5112
+ }
5113
+ });
4987
5114
  router.post('/api/tools/check-env-credentials', async (req, res) => {
4988
5115
  try {
4989
5116
  const { provider } = req.body || {};