better-auth-studio 1.0.59-beta.3 → 1.0.59-beta.5

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,CAo9KR"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAOA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAa9D,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,CAilLR"}
package/dist/routes.js CHANGED
@@ -7,6 +7,10 @@ import { hex } from '@better-auth/utils/hex';
7
7
  import { scryptAsync } from '@noble/hashes/scrypt.js';
8
8
  import { Router } from 'express';
9
9
  import { createJiti } from 'jiti';
10
+ import { execSync } from 'node:child_process';
11
+ import { writeFileSync as writeFile, unlinkSync, readFileSync as readFile } from 'node:fs';
12
+ import { tmpdir } from 'node:os';
13
+ import { join as pathJoin } from 'node:path';
10
14
  import { createMockAccount, createMockSession, createMockUser, createMockVerification, getAuthAdapter, } from './auth-adapter.js';
11
15
  import { getAuthData } from './data.js';
12
16
  import { initializeGeoService, resolveIPLocation, setGeoDbPath } from './geo-service.js';
@@ -4355,13 +4359,9 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
4355
4359
  ?.filter((f) => f.name.trim())
4356
4360
  .map((field) => {
4357
4361
  const attrs = [`type: "${field.type}"`];
4358
- // Always include required (default to false)
4359
4362
  attrs.push(`required: ${field.required ? 'true' : 'false'}`);
4360
- // Always include unique (default to false)
4361
4363
  attrs.push(`unique: ${field.unique ? 'true' : 'false'}`);
4362
- // Always include input (default to false)
4363
4364
  attrs.push('input: false');
4364
- // Add defaultValue for boolean fields
4365
4365
  if (field.type === 'boolean') {
4366
4366
  attrs.push('defaultValue: false');
4367
4367
  }
@@ -4369,7 +4369,6 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
4369
4369
  return ` ${field.name}: {\n ${attrStr}\n }`;
4370
4370
  })
4371
4371
  .join(',\n') || '';
4372
- // If extending an existing table, use the extended table name
4373
4372
  const tableName = table.isExtending && table.extendedTableName
4374
4373
  ? table.extendedTableName.trim()
4375
4374
  : table.name.trim();
@@ -4381,7 +4380,6 @@ ${fields}
4381
4380
  })
4382
4381
  .join(',\n')
4383
4382
  : '';
4384
- // Generate hooks
4385
4383
  const beforeHooks = hooks
4386
4384
  .filter((h) => h.timing === 'before')
4387
4385
  .map((hook) => {
@@ -4404,7 +4402,6 @@ ${fields}
4404
4402
  return ` {
4405
4403
  matcher: ${matcher},
4406
4404
  handler: createAuthMiddleware(async (ctx) => {
4407
- // ${hook.name || `${hook.timing} ${hook.action} hook`}
4408
4405
  ${hook.hookLogic || '// Hook logic here'}
4409
4406
  }),
4410
4407
  }`;
@@ -4431,7 +4428,6 @@ ${fields}
4431
4428
  return ` {
4432
4429
  matcher: ${matcher},
4433
4430
  handler: createAuthMiddleware(async (ctx) => {
4434
- // ${hook.name || `${hook.timing} ${hook.action} hook`}
4435
4431
  ${hook.hookLogic || '// Hook logic here'}
4436
4432
  }),
4437
4433
  }`;
@@ -4441,13 +4437,11 @@ ${fields}
4441
4437
  return ` {
4442
4438
  path: "${mw.path}",
4443
4439
  middleware: createAuthMiddleware(async (ctx) => {
4444
- // ${mw.name || 'Middleware'}
4445
4440
  ${mw.middlewareLogic || '// Middleware logic here'}
4446
4441
  }),
4447
4442
  }`;
4448
4443
  })
4449
4444
  .join(',\n');
4450
- // Generate endpoints
4451
4445
  const endpointsCode = endpoints.length > 0
4452
4446
  ? endpoints
4453
4447
  .map((endpoint) => {
@@ -4475,7 +4469,6 @@ ${fields}
4475
4469
  method: "${endpoint.method || 'POST'}",
4476
4470
  },
4477
4471
  async (ctx) => {
4478
- // ${endpoint.name || sanitizedName}
4479
4472
  ${formattedHandlerLogic}
4480
4473
  },
4481
4474
  ),`;
@@ -4518,6 +4511,33 @@ ${formattedHandlerLogic}
4518
4511
  .replace(/\n{3,}/g, '\n\n')
4519
4512
  .trim();
4520
4513
  };
4514
+ const formatCode = (code) => {
4515
+ try {
4516
+ const tempFile = pathJoin(tmpdir(), `biome-format-${Date.now()}-${Math.random().toString(36).substring(7)}.ts`);
4517
+ writeFile(tempFile, code, 'utf-8');
4518
+ try {
4519
+ execSync(`npx @biomejs/biome format --write ${tempFile}`, {
4520
+ stdio: 'pipe',
4521
+ cwd: process.cwd(),
4522
+ });
4523
+ const formatted = readFile(tempFile, 'utf-8');
4524
+ unlinkSync(tempFile);
4525
+ return formatted;
4526
+ }
4527
+ catch (formatError) {
4528
+ try {
4529
+ unlinkSync(tempFile);
4530
+ }
4531
+ catch {
4532
+ // Ignore cleanup errors
4533
+ }
4534
+ return code;
4535
+ }
4536
+ }
4537
+ catch (error) {
4538
+ return code;
4539
+ }
4540
+ };
4521
4541
  const pluginParts = [];
4522
4542
  if (schemaCode) {
4523
4543
  pluginParts.push(` schema: {\n${schemaCode}\n }`);
@@ -4541,7 +4561,6 @@ ${formattedHandlerLogic}
4541
4561
  if (rateLimitCode) {
4542
4562
  pluginParts.push(` rateLimit: {\n${rateLimitCode}\n }`);
4543
4563
  }
4544
- // Generate server plugin code
4545
4564
  const imports = ['import type { BetterAuthPlugin } from "@better-auth/core"'];
4546
4565
  if (hooks.length > 0 || middleware.length > 0 || endpoints.length > 0) {
4547
4566
  imports.push('import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api"');
@@ -4549,7 +4568,7 @@ ${formattedHandlerLogic}
4549
4568
  const serverPluginBody = pluginParts.length > 0
4550
4569
  ? ` id: "${camelCaseName}",\n${pluginParts.join(',\n')}`
4551
4570
  : ` id: "${camelCaseName}"`;
4552
- const serverPluginCode = cleanCode(`import type { BetterAuthPlugin } from "@better-auth/core";
4571
+ const serverPluginCode = formatCode(cleanCode(`import type { BetterAuthPlugin } from "@better-auth/core";
4553
4572
  ${imports.join('\n')}
4554
4573
 
4555
4574
  ${description ? `/**\n * ${description.replace(/\n/g, '\n * ')}\n */` : ''}
@@ -4558,7 +4577,7 @@ export const ${camelCaseName} = (options?: Record<string, any>) => {
4558
4577
  ${serverPluginBody}
4559
4578
  } satisfies BetterAuthPlugin;
4560
4579
  };
4561
- `);
4580
+ `));
4562
4581
  const pathMethods = endpoints.length > 0
4563
4582
  ? endpoints
4564
4583
  .map((endpoint) => {
@@ -4583,7 +4602,7 @@ ${serverPluginBody}
4583
4602
  const atomListenersCode = sessionAffectingPaths.length > 0
4584
4603
  ? `\n atomListeners: [\n${sessionAffectingPaths.join(',\n')}\n ],`
4585
4604
  : '';
4586
- const clientPluginCode = cleanCode(`import type { BetterAuthClientPlugin } from "@better-auth/core";
4605
+ const clientPluginCode = formatCode(cleanCode(`import type { BetterAuthClientPlugin } from "@better-auth/core";
4587
4606
  import type { ${camelCaseName} } from "..";
4588
4607
 
4589
4608
  export const ${camelCaseName}Client = () => {
@@ -4592,9 +4611,8 @@ export const ${camelCaseName}Client = () => {
4592
4611
  $InferServerPlugin: {} as ReturnType<typeof ${camelCaseName}>,${pathMethods ? `\n pathMethods: {\n${pathMethods}\n },` : ''}${atomListenersCode}
4593
4612
  } satisfies BetterAuthClientPlugin;
4594
4613
  };
4595
- `);
4596
- // Generate server setup code
4597
- const serverSetupCode = cleanCode(`import { betterAuth } from "@better-auth/core";
4614
+ `));
4615
+ const serverSetupCode = formatCode(cleanCode(`import { betterAuth } from "@better-auth/core";
4598
4616
  import { ${camelCaseName} } from "./plugin/${camelCaseName}";
4599
4617
 
4600
4618
  export const auth = betterAuth({
@@ -4603,7 +4621,7 @@ export const auth = betterAuth({
4603
4621
  ${camelCaseName}(),
4604
4622
  ],
4605
4623
  });
4606
- `);
4624
+ `));
4607
4625
  const frameworkImportMap = {
4608
4626
  react: 'better-auth/react',
4609
4627
  svelte: 'better-auth/svelte',
@@ -4611,7 +4629,6 @@ export const auth = betterAuth({
4611
4629
  vue: 'better-auth/vue',
4612
4630
  };
4613
4631
  const frameworkImport = frameworkImportMap[clientFramework] || 'better-auth/react';
4614
- // Get baseURL based on framework
4615
4632
  const baseURLMap = {
4616
4633
  react: 'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"',
4617
4634
  svelte: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
@@ -4620,7 +4637,7 @@ export const auth = betterAuth({
4620
4637
  };
4621
4638
  const baseURL = baseURLMap[clientFramework] ||
4622
4639
  'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"';
4623
- const clientSetupCode = cleanCode(`import { createAuthClient } from "${frameworkImport}";
4640
+ const clientSetupCode = formatCode(cleanCode(`import { createAuthClient } from "${frameworkImport}";
4624
4641
  import { ${camelCaseName}Client } from "./plugin/${camelCaseName}/client";
4625
4642
 
4626
4643
  export const authClient = createAuthClient({
@@ -4629,7 +4646,7 @@ export const authClient = createAuthClient({
4629
4646
  ${camelCaseName}Client(),
4630
4647
  ],
4631
4648
  });
4632
- `);
4649
+ `));
4633
4650
  return res.json({
4634
4651
  success: true,
4635
4652
  plugin: {
@@ -4995,6 +5012,110 @@ export const authClient = createAuthClient({
4995
5012
  });
4996
5013
  }
4997
5014
  });
5015
+ router.post('/api/tools/check-env-secret', async (_req, res) => {
5016
+ try {
5017
+ const envPath = join(process.cwd(), '.env');
5018
+ const envLocalPath = join(process.cwd(), '.env.local');
5019
+ const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
5020
+ const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
5021
+ const envLines = envContent.split('\n');
5022
+ let existingSecret;
5023
+ envLines.forEach((line) => {
5024
+ const trimmed = line.trim();
5025
+ if (!trimmed || trimmed.startsWith('#'))
5026
+ return;
5027
+ const match = trimmed.match(/^BETTER_AUTH_SECRET\s*=\s*(.*)$/i);
5028
+ if (match) {
5029
+ let value = match[1].trim();
5030
+ if (value.length >= 2) {
5031
+ if ((value.startsWith('"') && value.endsWith('"')) ||
5032
+ (value.startsWith("'") && value.endsWith("'"))) {
5033
+ value = value.slice(1, -1).trim();
5034
+ }
5035
+ }
5036
+ if (value && value.trim() !== '') {
5037
+ existingSecret = value;
5038
+ }
5039
+ }
5040
+ });
5041
+ const hasExisting = !!existingSecret && existingSecret.trim() !== '';
5042
+ res.json({
5043
+ success: true,
5044
+ hasExisting,
5045
+ existingSecret: hasExisting ? existingSecret : undefined,
5046
+ path: targetPath,
5047
+ });
5048
+ }
5049
+ catch (error) {
5050
+ res.status(500).json({
5051
+ success: false,
5052
+ message: error instanceof Error ? error.message : 'Failed to check secret',
5053
+ });
5054
+ }
5055
+ });
5056
+ router.post('/api/tools/write-env-secret', async (req, res) => {
5057
+ try {
5058
+ const { secret, action = 'override' } = req.body || {};
5059
+ if (!secret) {
5060
+ return res.status(400).json({
5061
+ success: false,
5062
+ message: 'Secret is required',
5063
+ });
5064
+ }
5065
+ const envPath = join(process.cwd(), '.env');
5066
+ const envLocalPath = join(process.cwd(), '.env.local');
5067
+ const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
5068
+ const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
5069
+ const envLines = envContent.split('\n');
5070
+ const envMap = new Map();
5071
+ const newLines = [];
5072
+ envLines.forEach((line, index) => {
5073
+ const trimmed = line.trim();
5074
+ if (!trimmed || trimmed.startsWith('#')) {
5075
+ newLines.push(line);
5076
+ return;
5077
+ }
5078
+ const match = trimmed.match(/^([^=#]+)\s*=\s*(.*)$/);
5079
+ if (match) {
5080
+ const key = match[1].trim();
5081
+ if (key.toUpperCase() === 'BETTER_AUTH_SECRET') {
5082
+ envMap.set('BETTER_AUTH_SECRET', { line, index });
5083
+ if (action === 'override') {
5084
+ newLines.push(`BETTER_AUTH_SECRET=${secret}`);
5085
+ }
5086
+ else {
5087
+ newLines.push(line);
5088
+ }
5089
+ }
5090
+ else {
5091
+ newLines.push(line);
5092
+ }
5093
+ }
5094
+ else {
5095
+ newLines.push(line);
5096
+ }
5097
+ });
5098
+ if (!envMap.has('BETTER_AUTH_SECRET')) {
5099
+ if (newLines.length > 0 && newLines[newLines.length - 1].trim() !== '') {
5100
+ newLines.push('');
5101
+ }
5102
+ newLines.push(`BETTER_AUTH_SECRET=${secret}`);
5103
+ }
5104
+ const newContent = newLines.join('\n');
5105
+ writeFileSync(targetPath, newContent, 'utf-8');
5106
+ res.json({
5107
+ success: true,
5108
+ message: 'Secret written successfully',
5109
+ path: targetPath,
5110
+ });
5111
+ }
5112
+ catch (error) {
5113
+ res.status(500).json({
5114
+ success: false,
5115
+ message: error instanceof Error ? error.message : 'Failed to write secret to .env',
5116
+ });
5117
+ }
5118
+ });
4998
5119
  router.post('/api/tools/check-env-credentials', async (req, res) => {
4999
5120
  try {
5000
5121
  const { provider } = req.body || {};