From 9205704862827ab0b2eaebf9ed9d97256a88f75c Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 10 Mar 2026 08:59:35 +0800 Subject: [PATCH 01/17] . --- packages/sv/src/addons/drizzle.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index 18bb98d43..6beaae854 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -6,11 +6,12 @@ import { defineAddon, defineAddonOptions } from '../core/config.ts'; import type { OptionValues } from '../core/options.ts'; import { getNodeTypesVersion } from './common.ts'; -type Database = 'mysql' | 'postgresql' | 'sqlite'; +type Database = 'mysql' | 'postgresql' | 'sqlite' | 'd1'; const PORTS: Record = { mysql: '3306', postgresql: '5432', - sqlite: '' + sqlite: '', + d1: '' }; const options = defineAddonOptions() @@ -21,7 +22,8 @@ const options = defineAddonOptions() options: [ { value: 'postgresql', label: 'PostgreSQL' }, { value: 'mysql', label: 'MySQL' }, - { value: 'sqlite', label: 'SQLite' } + { value: 'sqlite', label: 'SQLite' }, + { value: 'd1', label: 'Cloudflare D1' } ] }) .add('postgresql', { From 03669db4464f5cfd5edcaacfc0ddcb0c0b1ff9ee Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 10 Mar 2026 09:19:23 +0800 Subject: [PATCH 02/17] . --- packages/sv/src/addons/drizzle.ts | 72 ++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index 6beaae854..85a5bac25 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -211,24 +211,44 @@ export default defineAddon({ js.imports.addNamed(ast, { from: 'drizzle-kit', imports: { defineConfig: 'defineConfig' } }); - ast.body.push( - js.common.parseStatement( - "if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');" - ) - ); + if (options.database !== 'd1') { + ast.body.push( + js.common.parseStatement( + "if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');" + ) + ); + } + + const d1 = options.database === 'd1'; + const turso = options.sqlite === 'turso'; + const getDialect = (): string => { + if (d1) return 'sqlite'; + if (turso) return 'turso'; + return options.database; + }; js.exports.createDefault(ast, { fallback: js.common.parseExpression(` defineConfig({ schema: "./src/lib/server/db/schema.${typescript ? 'ts' : 'js'}", - dialect: "${options.sqlite === 'turso' ? 'turso' : options.database}", + dialect: "${getDialect()}", + ${d1 ? "driver: 'd1-http'," : ''} dbCredentials: { - ${options.sqlite === 'turso' ? 'authToken: process.env.DATABASE_AUTH_TOKEN,' : ''} - url: process.env.DATABASE_URL + ${ + d1 + ? ` + accountId: process.env.CLOUDFLARE_ACCOUNT_ID, + databaseId: process.env.CLOUDFLARE_DATABASE_ID, + token: process.env.CLOUDFLARE_D1_TOKEN,` + : '' + } + ${turso ? 'authToken: process.env.DATABASE_AUTH_TOKEN,' : ''} + ${!d1 ? 'url: process.env.DATABASE_URL' : ''}, }, verbose: true, strict: true - })`) + }) + `) }); return generateCode(); @@ -292,11 +312,27 @@ export default defineAddon({ sv.file(paths['database'], (content) => { const { ast, generateCode } = parse.script(content); + js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); + + if (options.database === 'd1') { + js.imports.addNamed(ast, { + from: 'drizzle-orm/d1', + imports: ['drizzle', 'type DrizzleD1Database'] + }); + + const getDbFn = js.common.parseStatement( + 'export const getDb = (db: DrizzleD1Database) => drizzle(db, { schema });' + ); + + ast.body.push(getDbFn); + + return generateCode(); + } + js.imports.addNamed(ast, { from: '$env/dynamic/private', imports: ['env'] }); - js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); // env var checks const dbURLCheck = js.common.parseStatement( @@ -416,6 +452,14 @@ export default defineAddon({ }, nextSteps: ({ options, packageManager }) => { const steps: string[] = []; + if (options.database === 'd1') { + steps.push( + `Add your ${color.env('CLOUDFLARE_ACCOUNT_ID')}, ${color.env('CLOUDFLARE_DATABASE_ID')}, and ${color.env('CLOUDFLARE_D1_TOKEN')} to ${color.path('.env')}` + ); + steps.push( + `Add a D1 database binding to your ${color.path('wrangler.jsonc')} (e.g., { binding: "DB", database_name: "my-db", database_id: "..." })` + ); + } if (options.docker) { const { command, args } = resolveCommand(packageManager, 'run', ['db:start'])!; steps.push( @@ -442,6 +486,14 @@ function generateEnvFileContent( ) { const DB_URL_KEY = 'DATABASE_URL'; + if (opts.database === 'd1') { + content = text.upsert(content, '', { value: '', comment: ['Cloudflare D1'], separator: true }); + content = text.upsert(content, 'CLOUDFLARE_ACCOUNT_ID', { value: '""' }); + content = text.upsert(content, 'CLOUDFLARE_DATABASE_ID', { value: '""' }); + content = text.upsert(content, 'CLOUDFLARE_D1_TOKEN', { value: '""' }); + return content; + } + // Calculate value and comment based on database options let value: string; const comment: NonNullable[2]>['comment'] = ['Drizzle']; From 91082731e730bc8a8fc005e414b5810794e88cb0 Mon Sep 17 00:00:00 2001 From: jycouet Date: Wed, 11 Mar 2026 21:40:22 +0100 Subject: [PATCH 03/17] feat(drizzle): add cloudflare `D1` database --- .changeset/cloudflare-d1-drizzle.md | 5 ++ packages/sv/src/addons/better-auth.ts | 108 ++++++++++++++++++++------ packages/sv/src/addons/drizzle.ts | 15 ++-- 3 files changed, 99 insertions(+), 29 deletions(-) create mode 100644 .changeset/cloudflare-d1-drizzle.md diff --git a/.changeset/cloudflare-d1-drizzle.md b/.changeset/cloudflare-d1-drizzle.md new file mode 100644 index 000000000..e694d8ae0 --- /dev/null +++ b/.changeset/cloudflare-d1-drizzle.md @@ -0,0 +1,5 @@ +--- +'sv': minor +--- + +feat(drizzle): add cloudflare `D1` database diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index 1bc641059..2107635d0 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -49,6 +49,7 @@ export default defineAddon({ const hasDemo = demoPassword || demoGithub; let drizzleDialect: Dialect; + let isD1 = false; sv.devDependency('better-auth', '~1.4.21'); sv.devDependency('@better-auth/cli', '~1.4.21'); @@ -67,6 +68,13 @@ export default defineAddon({ ) { drizzleDialect = node.value.value as Dialect; } + if ( + isProp('driver', node) && + node.value.type === 'Literal' && + node.value.value === 'd1-http' + ) { + isD1 = true; + } } }); @@ -82,7 +90,11 @@ export default defineAddon({ sv.file(`${kit?.libDirectory}/server/auth.${language}`, (content) => { const { ast, generateCode, comments } = parse.script(content); - js.imports.addNamed(ast, { from: '$lib/server/db', imports: ['db'] }); + if (isD1) { + js.imports.addNamed(ast, { from: '$lib/server/db', imports: ['getDb'] }); + } else { + js.imports.addNamed(ast, { from: '$lib/server/db', imports: ['db'] }); + } js.imports.addNamed(ast, { from: '$app/server', imports: ['getRequestEvent'] }); js.imports.addNamed(ast, { from: '$env/dynamic/private', imports: ['env'] }); js.imports.addNamed(ast, { from: 'better-auth/svelte-kit', imports: ['sveltekitCookies'] }); @@ -110,19 +122,35 @@ export default defineAddon({ },` : ''; - const authConfig = dedent` - export const auth = betterAuth({ - baseURL: env.ORIGIN, - secret: env.BETTER_AUTH_SECRET, - database: drizzleAdapter(db, { - provider: '${provider}' - }), - emailAndPassword: { - enabled: true - },${githubProvider} - plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array - });`; - js.common.appendFromString(ast, { code: authConfig, comments }); + if (isD1) { + const authConfig = dedent` + export const createAuth = (d1${language === 'ts' ? ': D1Database' : ''}) => betterAuth({ + baseURL: env.ORIGIN, + secret: env.BETTER_AUTH_SECRET, + database: drizzleAdapter(getDb(d1), { + provider: '${provider}' + }), + emailAndPassword: { + enabled: true + },${githubProvider} + plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array + });`; + js.common.appendFromString(ast, { code: authConfig, comments }); + } else { + const authConfig = dedent` + export const auth = betterAuth({ + baseURL: env.ORIGIN, + secret: env.BETTER_AUTH_SECRET, + database: drizzleAdapter(db, { + provider: '${provider}' + }), + emailAndPassword: { + enabled: true + },${githubProvider} + plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array + });`; + js.common.appendFromString(ast, { code: authConfig, comments }); + } return generateCode(); }); @@ -192,10 +220,32 @@ export default defineAddon({ const { ast, generateCode, comments } = parse.script(content); js.imports.addNamed(ast, { imports: ['svelteKitHandler'], from: 'better-auth/svelte-kit' }); - js.imports.addNamed(ast, { imports: ['auth'], from: '$lib/server/auth' }); + if (isD1) { + js.imports.addNamed(ast, { imports: ['createAuth'], from: '$lib/server/auth' }); + } else { + js.imports.addNamed(ast, { imports: ['auth'], from: '$lib/server/auth' }); + } js.imports.addNamed(ast, { imports: ['building'], from: '$app/environment' }); - const handleContent = dedent` + const handleContent = isD1 + ? dedent` + async ({ event, resolve }) => { + const auth = createAuth(event.platform.env.DB); + // Fetch current session from Better Auth + const session = await auth.api.getSession({ + headers: event.request.headers + }); + // Make session and user available on server + if (session) { + event.locals.session = session.session; + event.locals.user = session.user; + } + return svelteKitHandler({ event, resolve, auth, building }); + }; + + export const handle = sequence(handleBetterAuth, handleSession); + ` + : dedent` async ({ event, resolve }) => { // Fetch current session from Better Auth const session = await auth.api.getSession({ @@ -237,9 +287,13 @@ export default defineAddon({ const [ts] = createPrinter(language === 'ts'); + const d1AuthLine = isD1 + ? '\n\t\t\t\t\t\t\tconst auth = createAuth(event.platform.env.DB);' + : ''; + const signInEmailAction = demoPassword ? ` - signInEmail: async (event) => { + signInEmail: async (event) => {${d1AuthLine} const formData = await event.request.formData(); const email = formData.get('email')?.toString() ?? ''; const password = formData.get('password')?.toString() ?? ''; @@ -261,7 +315,7 @@ export default defineAddon({ return redirect(302, '/demo/better-auth'); }, - signUpEmail: async (event) => { + signUpEmail: async (event) => {${d1AuthLine} const formData = await event.request.formData(); const email = formData.get('email')?.toString() ?? ''; const password = formData.get('password')?.toString() ?? ''; @@ -289,7 +343,7 @@ export default defineAddon({ const signInSocialAction = demoGithub ? ` - signInSocial: async (event) => { + signInSocial: async (event) => {${d1AuthLine} const formData = await event.request.formData(); const provider = formData.get('provider')?.toString() ?? 'github'; const callbackURL = formData.get('callbackURL')?.toString() ?? '/demo/better-auth'; @@ -310,11 +364,15 @@ export default defineAddon({ const needsAPIError = demoPassword; + const authImport = isD1 + ? "import { createAuth } from '$lib/server/auth';" + : "import { auth } from '$lib/server/auth';"; + return dedent` import { fail, redirect } from '@sveltejs/kit'; ${ts("import type { Actions } from './$types';")} ${ts("import type { PageServerLoad } from './$types';")} - import { auth } from '$lib/server/auth'; + ${authImport} ${needsAPIError ? "import { APIError } from 'better-auth/api';" : ''} export const load${ts(': PageServerLoad')} = async (event) => { @@ -400,11 +458,17 @@ export default defineAddon({ } const [ts] = createPrinter(language === 'ts'); + const authImport = isD1 + ? "import { createAuth } from '$lib/server/auth';" + : "import { auth } from '$lib/server/auth';"; + const d1AuthLine = isD1 + ? '\n\t\t\t\t\t\t\tconst auth = createAuth(event.platform.env.DB);' + : ''; return dedent` import { redirect } from '@sveltejs/kit'; ${ts("import type { Actions } from './$types';")} ${ts("import type { PageServerLoad } from './$types';")} - import { auth } from '$lib/server/auth'; + ${authImport} export const load${ts(': PageServerLoad')} = async (event) => { if (!event.locals.user) { @@ -414,7 +478,7 @@ export default defineAddon({ }; export const actions${ts(': Actions')} = { - signOut: async (event) => { + signOut: async (event) => {${d1AuthLine} await auth.api.signOut({ headers: event.request.headers }); diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index 85a5bac25..248040348 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -243,7 +243,7 @@ export default defineAddon({ : '' } ${turso ? 'authToken: process.env.DATABASE_AUTH_TOKEN,' : ''} - ${!d1 ? 'url: process.env.DATABASE_URL' : ''}, + ${!d1 ? 'url: process.env.DATABASE_URL,' : ''} }, verbose: true, strict: true @@ -258,7 +258,7 @@ export default defineAddon({ const { ast, generateCode } = parse.script(content); let taskSchemaExpression; - if (options.database === 'sqlite') { + if (options.database === 'sqlite' || options.database === 'd1') { js.imports.addNamed(ast, { from: 'drizzle-orm/sqlite-core', imports: ['integer', 'sqliteTable', 'text'] @@ -312,16 +312,16 @@ export default defineAddon({ sv.file(paths['database'], (content) => { const { ast, generateCode } = parse.script(content); - js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); - if (options.database === 'd1') { js.imports.addNamed(ast, { from: 'drizzle-orm/d1', - imports: ['drizzle', 'type DrizzleD1Database'] + imports: ['drizzle'] }); const getDbFn = js.common.parseStatement( - 'export const getDb = (db: DrizzleD1Database) => drizzle(db, { schema });' + typescript + ? 'export const getDb = (d1: D1Database) => drizzle(d1, { schema });' + : 'export const getDb = (d1) => drizzle(d1, { schema });' ); ast.body.push(getDbFn); @@ -333,6 +333,7 @@ export default defineAddon({ from: '$env/dynamic/private', imports: ['env'] }); + js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); // env var checks const dbURLCheck = js.common.parseStatement( @@ -465,7 +466,7 @@ export default defineAddon({ steps.push( `Run ${color.command(`${command} ${args.join(' ')}`)} to start the docker container` ); - } else { + } else if (options.database !== 'd1') { steps.push( `Check ${color.env('DATABASE_URL')} in ${color.path('.env')} and adjust it to your needs` ); From febade0397ab9f20ce6b20887d0a00cdd2a9ba66 Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Thu, 12 Mar 2026 09:35:39 +0800 Subject: [PATCH 04/17] . --- packages/sv/src/addons/better-auth.ts | 119 ++++++++++++++------------ packages/sv/src/addons/drizzle.ts | 21 ++--- packages/sv/src/core/common.ts | 15 ++-- 3 files changed, 83 insertions(+), 72 deletions(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index 2107635d0..4604da83c 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -9,10 +9,14 @@ import { json, parse, resolveCommand, - createPrinter + createPrinter, + sanitizeName } from '@sveltejs/sv-utils'; import crypto from 'node:crypto'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { defineAddon, defineAddonOptions } from '../core/config.ts'; +import { fileExists } from '../core/files.ts'; import { addToDemoPage } from './common.ts'; type Dialect = 'mysql' | 'postgresql' | 'sqlite' | 'turso'; @@ -39,9 +43,11 @@ export default defineAddon({ if (!kit) unsupported('Requires SvelteKit'); if (!dependencyVersion('drizzle-orm')) dependsOn('drizzle'); + // some sort of wrangler / d1 / sveltekit adapter check + runsAfter('sveltekitAdapter'); runsAfter('tailwindcss'); }, - run: ({ sv, language, options, kit, dependencyVersion, files }) => { + run: ({ sv, language, options, kit, dependencyVersion, files, cwd }) => { if (!kit) throw new Error('SvelteKit is required'); const demoPassword = options.demo.includes('password'); @@ -49,7 +55,7 @@ export default defineAddon({ const hasDemo = demoPassword || demoGithub; let drizzleDialect: Dialect; - let isD1 = false; + let d1 = false; sv.devDependency('better-auth', '~1.4.21'); sv.devDependency('@better-auth/cli', '~1.4.21'); @@ -73,7 +79,7 @@ export default defineAddon({ node.value.type === 'Literal' && node.value.value === 'd1-http' ) { - isD1 = true; + d1 = true; } } }); @@ -90,11 +96,13 @@ export default defineAddon({ sv.file(`${kit?.libDirectory}/server/auth.${language}`, (content) => { const { ast, generateCode, comments } = parse.script(content); - if (isD1) { - js.imports.addNamed(ast, { from: '$lib/server/db', imports: ['getDb'] }); - } else { - js.imports.addNamed(ast, { from: '$lib/server/db', imports: ['db'] }); - } + if (d1) + js.imports.addNamed(ast, { + from: 'drizzle-orm/d1', + imports: ['DrizzleD1Database'], + isType: true + }); + js.imports.addNamed(ast, { from: '$lib/server/db', imports: [d1 ? 'getDb' : 'db'] }); js.imports.addNamed(ast, { from: '$app/server', imports: ['getRequestEvent'] }); js.imports.addNamed(ast, { from: '$env/dynamic/private', imports: ['env'] }); js.imports.addNamed(ast, { from: 'better-auth/svelte-kit', imports: ['sveltekitCookies'] }); @@ -122,9 +130,10 @@ export default defineAddon({ },` : ''; - if (isD1) { - const authConfig = dedent` - export const createAuth = (d1${language === 'ts' ? ': D1Database' : ''}) => betterAuth({ + let authConfig = ''; + if (d1) { + authConfig = dedent` + export const createAuth = (d1${language === 'ts' ? ': DrizzleD1Database' : ''}) => betterAuth({ baseURL: env.ORIGIN, secret: env.BETTER_AUTH_SECRET, database: drizzleAdapter(getDb(d1), { @@ -135,9 +144,8 @@ export default defineAddon({ },${githubProvider} plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array });`; - js.common.appendFromString(ast, { code: authConfig, comments }); } else { - const authConfig = dedent` + authConfig = dedent` export const auth = betterAuth({ baseURL: env.ORIGIN, secret: env.BETTER_AUTH_SECRET, @@ -149,8 +157,8 @@ export default defineAddon({ },${githubProvider} plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array });`; - js.common.appendFromString(ast, { code: authConfig, comments }); } + js.common.appendFromString(ast, { code: authConfig, comments }); return generateCode(); }); @@ -186,6 +194,7 @@ export default defineAddon({ sv.file('src/app.d.ts', (content) => { const { ast, comments, generateCode } = parse.script(content); + if (d1) js.imports.addNamed(ast, { imports: ['getAuth'], from: '$lib/server/auth' }); js.imports.addNamed(ast, { imports: ['User', 'Session'], from: 'better-auth/minimal', @@ -206,6 +215,9 @@ export default defineAddon({ const session = locals.body.body.find((prop) => js.common.hasTypeProperty(prop, { name: 'session' }) ); + const auth = locals.body.body.find((prop) => + js.common.hasTypeProperty(prop, { name: 'auth' }) + ); if (!user) { locals.body.body.push(js.common.createTypeProperty('user', 'User', true)); @@ -213,6 +225,11 @@ export default defineAddon({ if (!session) { locals.body.body.push(js.common.createTypeProperty('session', 'Session', true)); } + if (!auth) { + locals.body.body.push( + js.common.createTypeProperty('auth', 'ReturnType', false) + ); + } return generateCode(); }); @@ -220,33 +237,17 @@ export default defineAddon({ const { ast, generateCode, comments } = parse.script(content); js.imports.addNamed(ast, { imports: ['svelteKitHandler'], from: 'better-auth/svelte-kit' }); - if (isD1) { - js.imports.addNamed(ast, { imports: ['createAuth'], from: '$lib/server/auth' }); - } else { - js.imports.addNamed(ast, { imports: ['auth'], from: '$lib/server/auth' }); - } + js.imports.addNamed(ast, { imports: [d1 ? 'createAuth' : 'auth'], from: '$lib/server/auth' }); js.imports.addNamed(ast, { imports: ['building'], from: '$app/environment' }); - const handleContent = isD1 - ? dedent` + const handleContent = dedent` async ({ event, resolve }) => { - const auth = createAuth(event.platform.env.DB); - // Fetch current session from Better Auth - const session = await auth.api.getSession({ - headers: event.request.headers - }); - // Make session and user available on server - if (session) { - event.locals.session = session.session; - event.locals.user = session.user; + ${ + d1 + ? `event.locals.auth = createAuth(event.platform.env.DB); + const { auth } = event.locals` + : '' /* same thing, creates new line */ } - return svelteKitHandler({ event, resolve, auth, building }); - }; - - export const handle = sequence(handleBetterAuth, handleSession); - ` - : dedent` - async ({ event, resolve }) => { // Fetch current session from Better Auth const session = await auth.api.getSession({ headers: event.request.headers @@ -271,6 +272,29 @@ export default defineAddon({ return generateCode(); }); + if (d1) { + const ext = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; + const pkg = parse.json(readFileSync(join(cwd, 'package.json'), 'utf-8')); + const dbName = sanitizeName(pkg.data.name, 'package') + '-db'; + + sv.file(`wrangler.${ext}`, (content) => { + const { data, generateCode } = ext === 'jsonc' ? parse.json(content) : parse.toml(content); + + data.d1_databases ??= [ + { + binding: 'DB', + database_name: dbName, + // is optional with auto-provisoning + database_id: '', + // i cannot find a reference to remote + remote: true + } + ]; + + return generateCode(); + }); + } + if (hasDemo) { sv.file(`${kit?.routesDirectory}/demo/+page.svelte`, (content) => { return addToDemoPage(content, 'better-auth', language); @@ -287,9 +311,7 @@ export default defineAddon({ const [ts] = createPrinter(language === 'ts'); - const d1AuthLine = isD1 - ? '\n\t\t\t\t\t\t\tconst auth = createAuth(event.platform.env.DB);' - : ''; + const d1AuthLine = d1 ? '\n\t\t\t\t\t\t\tconst { auth } = event.locals;\n' : ''; const signInEmailAction = demoPassword ? ` @@ -364,15 +386,11 @@ export default defineAddon({ const needsAPIError = demoPassword; - const authImport = isD1 - ? "import { createAuth } from '$lib/server/auth';" - : "import { auth } from '$lib/server/auth';"; - return dedent` import { fail, redirect } from '@sveltejs/kit'; ${ts("import type { Actions } from './$types';")} ${ts("import type { PageServerLoad } from './$types';")} - ${authImport} + ${!d1 ? "import { auth } from '$lib/server/auth';" : '' /* this creates a new line */} ${needsAPIError ? "import { APIError } from 'better-auth/api';" : ''} export const load${ts(': PageServerLoad')} = async (event) => { @@ -458,17 +476,12 @@ export default defineAddon({ } const [ts] = createPrinter(language === 'ts'); - const authImport = isD1 - ? "import { createAuth } from '$lib/server/auth';" - : "import { auth } from '$lib/server/auth';"; - const d1AuthLine = isD1 - ? '\n\t\t\t\t\t\t\tconst auth = createAuth(event.platform.env.DB);' - : ''; + const d1AuthLine = d1 ? '\n\t\t\t\t\t\t\tconst { auth } = event.locals;\n' : ''; return dedent` import { redirect } from '@sveltejs/kit'; ${ts("import type { Actions } from './$types';")} ${ts("import type { PageServerLoad } from './$types';")} - ${authImport} + ${!d1 ? "import { auth } from '$lib/server/auth';" : ''} export const load${ts(': PageServerLoad')} = async (event) => { if (!event.locals.user) { diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index 248040348..bb0dcc603 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -207,11 +207,14 @@ export default defineAddon({ } sv.file(paths['drizzle config'], (content) => { + const d1 = options.database === 'd1'; + const turso = options.sqlite === 'turso'; + const { ast, generateCode } = parse.script(content); js.imports.addNamed(ast, { from: 'drizzle-kit', imports: { defineConfig: 'defineConfig' } }); - if (options.database !== 'd1') { + if (!d1) { ast.body.push( js.common.parseStatement( "if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');" @@ -219,8 +222,6 @@ export default defineAddon({ ); } - const d1 = options.database === 'd1'; - const turso = options.sqlite === 'turso'; const getDialect = (): string => { if (d1) return 'sqlite'; if (turso) return 'turso'; @@ -309,19 +310,18 @@ export default defineAddon({ return generateCode(); }); - sv.file(paths['database'], (content) => { + sv.file(paths.database, (content) => { const { ast, generateCode } = parse.script(content); if (options.database === 'd1') { + js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); js.imports.addNamed(ast, { from: 'drizzle-orm/d1', - imports: ['drizzle'] + imports: ['drizzle', 'type DrizzleD1Database'] }); const getDbFn = js.common.parseStatement( - typescript - ? 'export const getDb = (d1: D1Database) => drizzle(d1, { schema });' - : 'export const getDb = (d1) => drizzle(d1, { schema });' + `export const getDb = (d1${typescript ? ': DrizzleD1Database' : ''}) => drizzle(d1, { schema });` ); ast.body.push(getDbFn); @@ -329,10 +329,7 @@ export default defineAddon({ return generateCode(); } - js.imports.addNamed(ast, { - from: '$env/dynamic/private', - imports: ['env'] - }); + js.imports.addNamed(ast, { from: '$env/dynamic/private', imports: ['env'] }); js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); // env var checks diff --git a/packages/sv/src/core/common.ts b/packages/sv/src/core/common.ts index 13378e6c7..6923ce958 100644 --- a/packages/sv/src/core/common.ts +++ b/packages/sv/src/core/common.ts @@ -173,13 +173,14 @@ export function updateReadme(projectPath: string, command: string) { // Append to the existing Creating a project section const existingSection = creatingSectionMatch[0]; - const updatedSection = - `${existingSection.trim()}\n\n` + - 'To recreate this project with the same configuration:\n\n' + - '```sh\n' + - '# recreate this project\n' + - `${command}\n` + - '```\n\n'; + const updatedSection = [ + `${existingSection.trim()}\n`, + 'To recreate this project with the same configuration:\n', + '```sh', + '# recreate this project', + command, + '```\n' + ].join('\n'); content = content.replace(creatingSectionPattern, updatedSection); fs.writeFileSync(readmePath, content); From 4899894468504744a14bcad40b4343c5ec042984 Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Thu, 12 Mar 2026 13:47:30 +0800 Subject: [PATCH 05/17] . --- packages/sv/src/addons/drizzle.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index bb0dcc603..de28d9c5c 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -214,7 +214,19 @@ export default defineAddon({ js.imports.addNamed(ast, { from: 'drizzle-kit', imports: { defineConfig: 'defineConfig' } }); - if (!d1) { + if (d1) { + ast.body.push( + js.common.parseStatement( + "if (!process.env.CLOUDFLARE_ACCOUNT_ID) throw new Error('CLOUDFLARE_ACCOUNT_ID is not set');" + ), + js.common.parseStatement( + "if (!process.env.CLOUDFLARE_DATABASE_ID) throw new Error('CLOUDFLARE_DATABASE_ID is not set');" + ), + js.common.parseStatement( + "if (!process.env.CLOUDFLARE_D1_TOKEN) throw new Error('CLOUDFLARE_D1_TOKEN is not set');" + ) + ); + } else { ast.body.push( js.common.parseStatement( "if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');" From 1bc16164bb9b532496b27843e386541b6921106d Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Thu, 12 Mar 2026 14:33:09 +0800 Subject: [PATCH 06/17] . --- packages/sv/src/addons/better-auth.ts | 2 +- packages/sv/src/core/common.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index 4604da83c..13d47dbf3 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -225,7 +225,7 @@ export default defineAddon({ if (!session) { locals.body.body.push(js.common.createTypeProperty('session', 'Session', true)); } - if (!auth) { + if (d1 && !auth) { locals.body.body.push( js.common.createTypeProperty('auth', 'ReturnType', false) ); diff --git a/packages/sv/src/core/common.ts b/packages/sv/src/core/common.ts index 6923ce958..0aba300f0 100644 --- a/packages/sv/src/core/common.ts +++ b/packages/sv/src/core/common.ts @@ -179,7 +179,7 @@ export function updateReadme(projectPath: string, command: string) { '```sh', '# recreate this project', command, - '```\n' + '```\n\n' ].join('\n'); content = content.replace(creatingSectionPattern, updatedSection); From 5c4e7c1348145a68ec40d36bfcb9ea98a2931ade Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 14 Mar 2026 21:51:40 +0100 Subject: [PATCH 07/17] refactor(auth): restructure createAuth and auth configuration for better clarity and usage --- packages/sv/src/addons/better-auth.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index 13d47dbf3..7c15f3c97 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -133,17 +133,23 @@ export default defineAddon({ let authConfig = ''; if (d1) { authConfig = dedent` - export const createAuth = (d1${language === 'ts' ? ': DrizzleD1Database' : ''}) => betterAuth({ + const authConfig = { baseURL: env.ORIGIN, secret: env.BETTER_AUTH_SECRET, - database: drizzleAdapter(getDb(d1), { - provider: '${provider}' - }), emailAndPassword: { enabled: true },${githubProvider} plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array - });`; + }${language === 'ts' ? ' satisfies Omit[0], "database">' : ''}; + + // Used at runtime with D1 binding + export const createAuth = (d1${language === 'ts' ? ': DrizzleD1Database' : ''}) => betterAuth({ + ...authConfig, + database: drizzleAdapter(getDb(d1), { provider: '${provider}' }), + }); + + // Used by Better Auth CLI for schema generation (do not use at runtime) + export const auth = betterAuth(authConfig);`; } else { authConfig = dedent` export const auth = betterAuth({ @@ -194,7 +200,7 @@ export default defineAddon({ sv.file('src/app.d.ts', (content) => { const { ast, comments, generateCode } = parse.script(content); - if (d1) js.imports.addNamed(ast, { imports: ['getAuth'], from: '$lib/server/auth' }); + if (d1) js.imports.addNamed(ast, { imports: ['createAuth'], from: '$lib/server/auth' }); js.imports.addNamed(ast, { imports: ['User', 'Session'], from: 'better-auth/minimal', @@ -227,7 +233,7 @@ export default defineAddon({ } if (d1 && !auth) { locals.body.body.push( - js.common.createTypeProperty('auth', 'ReturnType', false) + js.common.createTypeProperty('auth', 'ReturnType', false) ); } return generateCode(); From 28e967adc81181280768c789019f64ba8a04e356 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 14 Mar 2026 22:10:38 +0100 Subject: [PATCH 08/17] what about this?! --- packages/sv/src/addons/better-auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index 7c15f3c97..f7c2ff8c6 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -149,7 +149,7 @@ export default defineAddon({ }); // Used by Better Auth CLI for schema generation (do not use at runtime) - export const auth = betterAuth(authConfig);`; + export const auth = createAuth(${language === 'ts' ? 'null!' : 'null'});`; } else { authConfig = dedent` export const auth = betterAuth({ From 08ef4db947819d00a9d2b22b8a22b312d3a094be Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 14 Mar 2026 23:01:32 +0100 Subject: [PATCH 09/17] update --- packages/sv/src/addons/better-auth.ts | 39 +++------------------------ packages/sv/src/addons/drizzle.ts | 38 +++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index f7c2ff8c6..873e8edc9 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -9,14 +9,10 @@ import { json, parse, resolveCommand, - createPrinter, - sanitizeName + createPrinter } from '@sveltejs/sv-utils'; import crypto from 'node:crypto'; -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; import { defineAddon, defineAddonOptions } from '../core/config.ts'; -import { fileExists } from '../core/files.ts'; import { addToDemoPage } from './common.ts'; type Dialect = 'mysql' | 'postgresql' | 'sqlite' | 'turso'; @@ -96,12 +92,6 @@ export default defineAddon({ sv.file(`${kit?.libDirectory}/server/auth.${language}`, (content) => { const { ast, generateCode, comments } = parse.script(content); - if (d1) - js.imports.addNamed(ast, { - from: 'drizzle-orm/d1', - imports: ['DrizzleD1Database'], - isType: true - }); js.imports.addNamed(ast, { from: '$lib/server/db', imports: [d1 ? 'getDb' : 'db'] }); js.imports.addNamed(ast, { from: '$app/server', imports: ['getRequestEvent'] }); js.imports.addNamed(ast, { from: '$env/dynamic/private', imports: ['env'] }); @@ -143,7 +133,7 @@ export default defineAddon({ }${language === 'ts' ? ' satisfies Omit[0], "database">' : ''}; // Used at runtime with D1 binding - export const createAuth = (d1${language === 'ts' ? ': DrizzleD1Database' : ''}) => betterAuth({ + export const createAuth = (d1${language === 'ts' ? ': D1Database' : ''}) => betterAuth({ ...authConfig, database: drizzleAdapter(getDb(d1), { provider: '${provider}' }), }); @@ -250,7 +240,7 @@ export default defineAddon({ async ({ event, resolve }) => { ${ d1 - ? `event.locals.auth = createAuth(event.platform.env.DB); + ? `event.locals.auth = createAuth(event.platform!.env.DB); const { auth } = event.locals` : '' /* same thing, creates new line */ } @@ -278,29 +268,6 @@ export default defineAddon({ return generateCode(); }); - if (d1) { - const ext = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; - const pkg = parse.json(readFileSync(join(cwd, 'package.json'), 'utf-8')); - const dbName = sanitizeName(pkg.data.name, 'package') + '-db'; - - sv.file(`wrangler.${ext}`, (content) => { - const { data, generateCode } = ext === 'jsonc' ? parse.json(content) : parse.toml(content); - - data.d1_databases ??= [ - { - binding: 'DB', - database_name: dbName, - // is optional with auto-provisoning - database_id: '', - // i cannot find a reference to remote - remote: true - } - ]; - - return generateCode(); - }); - } - if (hasDemo) { sv.file(`${kit?.routesDirectory}/demo/+page.svelte`, (content) => { return addToDemoPage(content, 'better-auth', language); diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index de28d9c5c..38ea73c1a 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -1,8 +1,18 @@ -import { color, dedent, text, js, parse, resolveCommand, json } from '@sveltejs/sv-utils'; +import { + color, + dedent, + text, + js, + parse, + resolveCommand, + json, + sanitizeName +} from '@sveltejs/sv-utils'; import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; import { defineAddon, defineAddonOptions } from '../core/config.ts'; +import { fileExists } from '../core/files.ts'; import type { OptionValues } from '../core/options.ts'; import { getNodeTypesVersion } from './common.ts'; @@ -329,11 +339,11 @@ export default defineAddon({ js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); js.imports.addNamed(ast, { from: 'drizzle-orm/d1', - imports: ['drizzle', 'type DrizzleD1Database'] + imports: ['drizzle'] }); const getDbFn = js.common.parseStatement( - `export const getDb = (d1${typescript ? ': DrizzleD1Database' : ''}) => drizzle(d1, { schema });` + `export const getDb = (d1${typescript ? ': D1Database' : ''}) => drizzle(d1, { schema });` ); ast.body.push(getDbFn); @@ -459,6 +469,26 @@ export default defineAddon({ return generateCode(); }); + + if (options.database === 'd1') { + const ext = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; + const pkg = parse.json(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')); + const dbName = sanitizeName(pkg.data.name, 'package') + '-db'; + + sv.file(`wrangler.${ext}`, (content) => { + const { data, generateCode } = ext === 'jsonc' ? parse.json(content) : parse.toml(content); + + data.d1_databases ??= [ + { + binding: 'DB', + database_name: dbName, + database_id: '' + } + ]; + + return generateCode(); + }); + } }, nextSteps: ({ options, packageManager }) => { const steps: string[] = []; @@ -467,7 +497,7 @@ export default defineAddon({ `Add your ${color.env('CLOUDFLARE_ACCOUNT_ID')}, ${color.env('CLOUDFLARE_DATABASE_ID')}, and ${color.env('CLOUDFLARE_D1_TOKEN')} to ${color.path('.env')}` ); steps.push( - `Add a D1 database binding to your ${color.path('wrangler.jsonc')} (e.g., { binding: "DB", database_name: "my-db", database_id: "..." })` + `Update ${color.env('database_id')} in ${color.path('wrangler.jsonc')} with your D1 database ID` ); } if (options.docker) { From 6cf8709b18407666531511627915b9a3da6b5483 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 14 Mar 2026 23:17:15 +0100 Subject: [PATCH 10/17] refactor(auth, drizzle): improve D1 handling and update next steps for Cloudflare integration --- packages/sv/src/addons/better-auth.ts | 15 ++++++++------- packages/sv/src/addons/drizzle.ts | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index 873e8edc9..e3e9d7412 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -39,7 +39,6 @@ export default defineAddon({ if (!kit) unsupported('Requires SvelteKit'); if (!dependencyVersion('drizzle-orm')) dependsOn('drizzle'); - // some sort of wrangler / d1 / sveltekit adapter check runsAfter('sveltekitAdapter'); runsAfter('tailwindcss'); }, @@ -236,14 +235,16 @@ export default defineAddon({ js.imports.addNamed(ast, { imports: [d1 ? 'createAuth' : 'auth'], from: '$lib/server/auth' }); js.imports.addNamed(ast, { imports: ['building'], from: '$app/environment' }); + const d1HandleSetup = d1 + ? dedent` + if (!event.platform?.env?.DB) throw new Error('D1 binding "DB" not found — are you running with wrangler?'); + event.locals.auth = createAuth(event.platform.env.DB); + const { auth } = event.locals;` + : ''; + const handleContent = dedent` async ({ event, resolve }) => { - ${ - d1 - ? `event.locals.auth = createAuth(event.platform!.env.DB); - const { auth } = event.locals` - : '' /* same thing, creates new line */ - } + ${d1HandleSetup} // Fetch current session from Better Auth const session = await auth.api.getSession({ headers: event.request.headers diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index 38ea73c1a..fc841eef5 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -87,12 +87,17 @@ export default defineAddon({ options, setup: ({ kit, unsupported, runsAfter }) => { runsAfter('prettier'); + runsAfter('sveltekitAdapter'); if (!kit) return unsupported('Requires SvelteKit'); }, run: ({ sv, language, options, kit, dependencyVersion, cwd, cancel, files }) => { if (!kit) throw new Error('SvelteKit is required'); + if (options.database === 'd1' && !dependencyVersion('@sveltejs/adapter-cloudflare')) { + return cancel('Cloudflare D1 requires @sveltejs/adapter-cloudflare — add the adapter first'); + } + const typescript = language === 'ts'; const baseDBPath = path.resolve(cwd, kit.libDirectory, 'server', 'db'); const paths = { @@ -490,14 +495,15 @@ export default defineAddon({ }); } }, - nextSteps: ({ options, packageManager }) => { + nextSteps: ({ options, packageManager, cwd }) => { const steps: string[] = []; if (options.database === 'd1') { + const wranglerFile = `wrangler.${fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'}`; steps.push( `Add your ${color.env('CLOUDFLARE_ACCOUNT_ID')}, ${color.env('CLOUDFLARE_DATABASE_ID')}, and ${color.env('CLOUDFLARE_D1_TOKEN')} to ${color.path('.env')}` ); steps.push( - `Update ${color.env('database_id')} in ${color.path('wrangler.jsonc')} with your D1 database ID` + `Update ${color.env('database_id')} in ${color.path(wranglerFile)} with your D1 database ID` ); } if (options.docker) { @@ -527,8 +533,11 @@ function generateEnvFileContent( const DB_URL_KEY = 'DATABASE_URL'; if (opts.database === 'd1') { - content = text.upsert(content, '', { value: '', comment: ['Cloudflare D1'], separator: true }); - content = text.upsert(content, 'CLOUDFLARE_ACCOUNT_ID', { value: '""' }); + content = text.upsert(content, 'CLOUDFLARE_ACCOUNT_ID', { + value: '""', + comment: ['Cloudflare D1'], + separator: true + }); content = text.upsert(content, 'CLOUDFLARE_DATABASE_ID', { value: '""' }); content = text.upsert(content, 'CLOUDFLARE_D1_TOKEN', { value: '""' }); return content; From b47f692b1bb01922d7b828917b841dfb54769982 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 14 Mar 2026 23:20:15 +0100 Subject: [PATCH 11/17] clean --- packages/sv/src/addons/better-auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index e3e9d7412..dddf75620 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -42,7 +42,7 @@ export default defineAddon({ runsAfter('sveltekitAdapter'); runsAfter('tailwindcss'); }, - run: ({ sv, language, options, kit, dependencyVersion, files, cwd }) => { + run: ({ sv, language, options, kit, dependencyVersion, files }) => { if (!kit) throw new Error('SvelteKit is required'); const demoPassword = options.demo.includes('password'); From e433adf667be9be9eec337ea426a1f95f937dc7b Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 17 Mar 2026 14:32:21 +0800 Subject: [PATCH 12/17] update comments --- packages/sv/src/addons/better-auth.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index dddf75620..8931c0bad 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -131,13 +131,17 @@ export default defineAddon({ plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array }${language === 'ts' ? ' satisfies Omit[0], "database">' : ''}; - // Used at runtime with D1 binding export const createAuth = (d1${language === 'ts' ? ': D1Database' : ''}) => betterAuth({ ...authConfig, database: drizzleAdapter(getDb(d1), { provider: '${provider}' }), }); - // Used by Better Auth CLI for schema generation (do not use at runtime) + /** + * DO NOT USE! + * + * This instance is used by the \`better-auth\` CLI for schema generation ONLY. + * To access \`auth\` at runtime, use \`event.locals.auth\`. + */ export const auth = createAuth(${language === 'ts' ? 'null!' : 'null'});`; } else { authConfig = dedent` From 265a91d87da7362e70357229765a70bf0593bd12 Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 17 Mar 2026 14:52:14 +0800 Subject: [PATCH 13/17] update instructions --- packages/sv/src/addons/drizzle.ts | 15 +++++++++++++-- packages/sv/src/addons/sveltekit-adapter.ts | 9 ++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index fc841eef5..c482b393c 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -498,14 +498,24 @@ export default defineAddon({ nextSteps: ({ options, packageManager, cwd }) => { const steps: string[] = []; if (options.database === 'd1') { - const wranglerFile = `wrangler.${fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'}`; + const ext = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; + const pkg = parse.json(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')); + const dbName = sanitizeName(pkg.data.name, 'package') + '-db'; + const { command, args } = resolveCommand(packageManager, 'run', [ + 'wrangler', + 'd1', + 'create', + dbName + ])!; + steps.push( `Add your ${color.env('CLOUDFLARE_ACCOUNT_ID')}, ${color.env('CLOUDFLARE_DATABASE_ID')}, and ${color.env('CLOUDFLARE_D1_TOKEN')} to ${color.path('.env')}` ); steps.push( - `Update ${color.env('database_id')} in ${color.path(wranglerFile)} with your D1 database ID` + `Run ${color.command(`${command} ${args.join(' ')}`)} to generate a D1 database ID for your ${color.path(`wrangler.${ext}`)}` ); } + if (options.docker) { const { command, args } = resolveCommand(packageManager, 'run', ['db:start'])!; steps.push( @@ -516,6 +526,7 @@ export default defineAddon({ `Check ${color.env('DATABASE_URL')} in ${color.path('.env')} and adjust it to your needs` ); } + const { command, args } = resolveCommand(packageManager, 'run', ['db:push'])!; steps.push( `Run ${color.command(`${command} ${args.join(' ')}`)} to update your database schema` diff --git a/packages/sv/src/addons/sveltekit-adapter.ts b/packages/sv/src/addons/sveltekit-adapter.ts index 8752cb712..00a4d1c0d 100644 --- a/packages/sv/src/addons/sveltekit-adapter.ts +++ b/packages/sv/src/addons/sveltekit-adapter.ts @@ -125,14 +125,13 @@ export default defineAddon({ sv.devDependency('wrangler', '^4.63.0'); // default to jsonc - const configFormat = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; + const ext = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; // Setup Cloudlfare workers/pages config - sv.file(`wrangler.${configFormat}`, (content) => { - const { data, generateCode } = - configFormat === 'jsonc' ? parse.json(content) : parse.toml(content); + sv.file(`wrangler.${ext}`, (content) => { + const { data, generateCode } = ext === 'jsonc' ? parse.json(content) : parse.toml(content); - if (configFormat === 'jsonc') { + if (ext === 'jsonc') { data.$schema ??= './node_modules/wrangler/config-schema.json'; } From baed038d277655633661025690e247ab9c0fbc47 Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 17 Mar 2026 15:07:12 +0800 Subject: [PATCH 14/17] . --- packages/sv/src/addons/drizzle.ts | 4 +--- packages/sv/src/addons/sveltekit-adapter.ts | 11 ++++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index c482b393c..4b1c67146 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -499,13 +499,11 @@ export default defineAddon({ const steps: string[] = []; if (options.database === 'd1') { const ext = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; - const pkg = parse.json(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')); - const dbName = sanitizeName(pkg.data.name, 'package') + '-db'; const { command, args } = resolveCommand(packageManager, 'run', [ 'wrangler', 'd1', 'create', - dbName + `` ])!; steps.push( diff --git a/packages/sv/src/addons/sveltekit-adapter.ts b/packages/sv/src/addons/sveltekit-adapter.ts index 00a4d1c0d..a05057391 100644 --- a/packages/sv/src/addons/sveltekit-adapter.ts +++ b/packages/sv/src/addons/sveltekit-adapter.ts @@ -223,16 +223,13 @@ export default defineAddon({ } }, nextSteps({ options, packageManager }) { - const toReturn: string[] = []; + const steps: string[] = []; if (options.adapter === 'cloudflare') { const { command, args } = resolveCommand(packageManager, 'run', ['gen'])!; - toReturn.push( - `${color.command(`${command} ${args.join(' ')}`)} ` + - `${color.optional(`# updates `)}` + - `${color.route(`cloudflare`)}` + - `${color.optional(` types`)}` + steps.push( + `Run ${color.command(`${command} ${args.join(' ')}`)} to update ${color.addon('cloudflare')} types` ); } - return toReturn; + return steps; } }); From 580da11b26e1824e416a50cf0d1cff74e0b2e4ca Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 17 Mar 2026 15:20:21 +0800 Subject: [PATCH 15/17] dont add to the wrangler file --- packages/sv/src/addons/drizzle.ts | 34 +++---------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index 4b1c67146..e78c7bff1 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -1,13 +1,4 @@ -import { - color, - dedent, - text, - js, - parse, - resolveCommand, - json, - sanitizeName -} from '@sveltejs/sv-utils'; +import { color, dedent, text, js, parse, resolveCommand, json } from '@sveltejs/sv-utils'; import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; @@ -91,6 +82,7 @@ export default defineAddon({ if (!kit) return unsupported('Requires SvelteKit'); }, + run: ({ sv, language, options, kit, dependencyVersion, cwd, cancel, files }) => { if (!kit) throw new Error('SvelteKit is required'); @@ -474,27 +466,8 @@ export default defineAddon({ return generateCode(); }); - - if (options.database === 'd1') { - const ext = fileExists(cwd, 'wrangler.toml') ? 'toml' : 'jsonc'; - const pkg = parse.json(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')); - const dbName = sanitizeName(pkg.data.name, 'package') + '-db'; - - sv.file(`wrangler.${ext}`, (content) => { - const { data, generateCode } = ext === 'jsonc' ? parse.json(content) : parse.toml(content); - - data.d1_databases ??= [ - { - binding: 'DB', - database_name: dbName, - database_id: '' - } - ]; - - return generateCode(); - }); - } }, + nextSteps: ({ options, packageManager, cwd }) => { const steps: string[] = []; if (options.database === 'd1') { @@ -582,7 +555,6 @@ function generateEnvFileContent( value = '"mysql://user:password@host:port/db-name"'; comment.push('Replace with your DB credentials!'); } else if (opts.database === 'postgresql') { - // postgresql value = '"postgres://user:password@host:port/db-name"'; comment.push('Replace with your DB credentials!'); } else { From c00542a6c802eae93c6958d3176055797d1c71c7 Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 17 Mar 2026 17:20:54 +0800 Subject: [PATCH 16/17] clean up --- packages/sv/src/addons/better-auth.ts | 23 ++++++++++--------- packages/sv/src/addons/drizzle.ts | 32 +++++++++++++-------------- packages/sv/src/core/common.ts | 15 ++++++------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/packages/sv/src/addons/better-auth.ts b/packages/sv/src/addons/better-auth.ts index 8931c0bad..0531e2df9 100644 --- a/packages/sv/src/addons/better-auth.ts +++ b/packages/sv/src/addons/better-auth.ts @@ -128,7 +128,9 @@ export default defineAddon({ emailAndPassword: { enabled: true },${githubProvider} - plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array + plugins: [ + sveltekitCookies(getRequestEvent) // make sure this is the last plugin in the array + ], }${language === 'ts' ? ' satisfies Omit[0], "database">' : ''}; export const createAuth = (d1${language === 'ts' ? ': D1Database' : ''}) => betterAuth({ @@ -148,13 +150,13 @@ export default defineAddon({ export const auth = betterAuth({ baseURL: env.ORIGIN, secret: env.BETTER_AUTH_SECRET, - database: drizzleAdapter(db, { - provider: '${provider}' - }), + database: drizzleAdapter(db, { provider: '${provider}' }), emailAndPassword: { enabled: true },${githubProvider} - plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array + plugins: [ + sveltekitCookies(getRequestEvent) // make sure this is the last plugin in the array + ], });`; } js.common.appendFromString(ast, { code: authConfig, comments }); @@ -243,12 +245,11 @@ export default defineAddon({ ? dedent` if (!event.platform?.env?.DB) throw new Error('D1 binding "DB" not found — are you running with wrangler?'); event.locals.auth = createAuth(event.platform.env.DB); - const { auth } = event.locals;` + const { auth } = event.locals;\n` : ''; const handleContent = dedent` - async ({ event, resolve }) => { - ${d1HandleSetup} + async ({ event, resolve }) => {${d1HandleSetup} // Fetch current session from Better Auth const session = await auth.api.getSession({ headers: event.request.headers @@ -368,7 +369,7 @@ export default defineAddon({ import { fail, redirect } from '@sveltejs/kit'; ${ts("import type { Actions } from './$types';")} ${ts("import type { PageServerLoad } from './$types';")} - ${!d1 ? "import { auth } from '$lib/server/auth';" : '' /* this creates a new line */} + ${!d1 ? "import { auth } from '$lib/server/auth';" : ''} ${needsAPIError ? "import { APIError } from 'better-auth/api';" : ''} export const load${ts(': PageServerLoad')} = async (event) => { @@ -424,7 +425,9 @@ export default defineAddon({ : ''; const separator = - demoPassword && demoGithub ? `\n\n
` : ''; + demoPassword && demoGithub + ? `\n\n\t\t\t\t\t
\n` + : ''; const githubForm = demoGithub ? ` diff --git a/packages/sv/src/addons/drizzle.ts b/packages/sv/src/addons/drizzle.ts index e78c7bff1..9a242e1be 100644 --- a/packages/sv/src/addons/drizzle.ts +++ b/packages/sv/src/addons/drizzle.ts @@ -82,7 +82,6 @@ export default defineAddon({ if (!kit) return unsupported('Requires SvelteKit'); }, - run: ({ sv, language, options, kit, dependencyVersion, cwd, cancel, files }) => { if (!kit) throw new Error('SvelteKit is required'); @@ -247,23 +246,27 @@ export default defineAddon({ return options.database; }; + const getCredentials = (): string => { + const creds: string[] = []; + if (d1) { + creds.push('accountId: process.env.CLOUDFLARE_ACCOUNT_ID,'); + creds.push('databaseId: process.env.CLOUDFLARE_DATABASE_ID,'); + creds.push('token: process.env.CLOUDFLARE_D1_TOKEN,'); + } + if (turso) creds.push('authToken: process.env.DATABASE_AUTH_TOKEN,'); + if (!d1) creds.push('url: process.env.DATABASE_URL,'); + + return creds.join('\n'); + }; + js.exports.createDefault(ast, { fallback: js.common.parseExpression(` defineConfig({ - schema: "./src/lib/server/db/schema.${typescript ? 'ts' : 'js'}", + schema: "./src/lib/server/db/schema.${language}", dialect: "${getDialect()}", ${d1 ? "driver: 'd1-http'," : ''} dbCredentials: { - ${ - d1 - ? ` - accountId: process.env.CLOUDFLARE_ACCOUNT_ID, - databaseId: process.env.CLOUDFLARE_DATABASE_ID, - token: process.env.CLOUDFLARE_D1_TOKEN,` - : '' - } - ${turso ? 'authToken: process.env.DATABASE_AUTH_TOKEN,' : ''} - ${!d1 ? 'url: process.env.DATABASE_URL,' : ''} + ${getCredentials()} }, verbose: true, strict: true @@ -334,10 +337,7 @@ export default defineAddon({ if (options.database === 'd1') { js.imports.addNamespace(ast, { from: './schema', as: 'schema' }); - js.imports.addNamed(ast, { - from: 'drizzle-orm/d1', - imports: ['drizzle'] - }); + js.imports.addNamed(ast, { from: 'drizzle-orm/d1', imports: ['drizzle'] }); const getDbFn = js.common.parseStatement( `export const getDb = (d1${typescript ? ': D1Database' : ''}) => drizzle(d1, { schema });` diff --git a/packages/sv/src/core/common.ts b/packages/sv/src/core/common.ts index 0aba300f0..13378e6c7 100644 --- a/packages/sv/src/core/common.ts +++ b/packages/sv/src/core/common.ts @@ -173,14 +173,13 @@ export function updateReadme(projectPath: string, command: string) { // Append to the existing Creating a project section const existingSection = creatingSectionMatch[0]; - const updatedSection = [ - `${existingSection.trim()}\n`, - 'To recreate this project with the same configuration:\n', - '```sh', - '# recreate this project', - command, - '```\n\n' - ].join('\n'); + const updatedSection = + `${existingSection.trim()}\n\n` + + 'To recreate this project with the same configuration:\n\n' + + '```sh\n' + + '# recreate this project\n' + + `${command}\n` + + '```\n\n'; content = content.replace(creatingSectionPattern, updatedSection); fs.writeFileSync(readmePath, content); From 26eb9c4a54274e3da93b3cfec192a9e38700969c Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Tue, 17 Mar 2026 17:29:40 +0800 Subject: [PATCH 17/17] update snapshot --- .../snapshots/create-with-all-addons/src/lib/server/auth.ts | 4 +++- .../src/routes/demo/better-auth/login/+page.svelte | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/lib/server/auth.ts b/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/lib/server/auth.ts index 6dfcb75b0..b05416873 100644 --- a/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/lib/server/auth.ts +++ b/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/lib/server/auth.ts @@ -16,5 +16,7 @@ export const auth = betterAuth({ clientSecret: env.GITHUB_CLIENT_SECRET } }, - plugins: [sveltekitCookies(getRequestEvent)] // make sure this is the last plugin in the array + plugins: [ + sveltekitCookies(getRequestEvent) // make sure this is the last plugin in the array + ] }); diff --git a/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/routes/demo/better-auth/login/+page.svelte b/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/routes/demo/better-auth/login/+page.svelte index d57780435..fdeb891ba 100644 --- a/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/routes/demo/better-auth/login/+page.svelte +++ b/packages/sv/src/cli/tests/snapshots/create-with-all-addons/src/routes/demo/better-auth/login/+page.svelte @@ -25,6 +25,7 @@

{form?.message ?? ''}


+