#
tokens: 34242/50000 1/1099 files (page 61/69)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 61 of 69. Use http://codebase.md/better-auth/better-auth?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .github
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   ├── renovate.json5
│   └── workflows
│       ├── ci.yml
│       ├── e2e.yml
│       ├── preview.yml
│       └── release.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode
│   └── settings.json
├── banner-dark.png
├── banner.png
├── biome.json
├── bump.config.ts
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── demo
│   ├── expo-example
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── app.config.ts
│   │   ├── assets
│   │   │   ├── bg-image.jpeg
│   │   │   ├── fonts
│   │   │   │   └── SpaceMono-Regular.ttf
│   │   │   ├── icon.png
│   │   │   └── images
│   │   │       ├── adaptive-icon.png
│   │   │       ├── favicon.png
│   │   │       ├── logo.png
│   │   │       ├── partial-react-logo.png
│   │   │       ├── react-logo.png
│   │   │       ├── [email protected]
│   │   │       ├── [email protected]
│   │   │       └── splash.png
│   │   ├── babel.config.js
│   │   ├── components.json
│   │   ├── expo-env.d.ts
│   │   ├── index.ts
│   │   ├── metro.config.js
│   │   ├── nativewind-env.d.ts
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── app
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── api
│   │   │   │   │   └── auth
│   │   │   │   │       └── [...route]+api.ts
│   │   │   │   ├── dashboard.tsx
│   │   │   │   ├── forget-password.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── sign-up.tsx
│   │   │   ├── components
│   │   │   │   ├── icons
│   │   │   │   │   └── google.tsx
│   │   │   │   └── ui
│   │   │   │       ├── avatar.tsx
│   │   │   │       ├── button.tsx
│   │   │   │       ├── card.tsx
│   │   │   │       ├── dialog.tsx
│   │   │   │       ├── input.tsx
│   │   │   │       ├── separator.tsx
│   │   │   │       └── text.tsx
│   │   │   ├── global.css
│   │   │   └── lib
│   │   │       ├── auth-client.ts
│   │   │       ├── auth.ts
│   │   │       ├── icons
│   │   │       │   ├── iconWithClassName.ts
│   │   │       │   └── X.tsx
│   │   │       └── utils.ts
│   │   ├── tailwind.config.js
│   │   └── tsconfig.json
│   └── nextjs
│       ├── .env.example
│       ├── .gitignore
│       ├── app
│       │   ├── (auth)
│       │   │   ├── forget-password
│       │   │   │   └── page.tsx
│       │   │   ├── reset-password
│       │   │   │   └── page.tsx
│       │   │   ├── sign-in
│       │   │   │   ├── loading.tsx
│       │   │   │   └── page.tsx
│       │   │   └── two-factor
│       │   │       ├── otp
│       │   │       │   └── page.tsx
│       │   │       └── page.tsx
│       │   ├── accept-invitation
│       │   │   └── [id]
│       │   │       ├── invitation-error.tsx
│       │   │       └── page.tsx
│       │   ├── admin
│       │   │   └── page.tsx
│       │   ├── api
│       │   │   └── auth
│       │   │       └── [...all]
│       │   │           └── route.ts
│       │   ├── apps
│       │   │   └── register
│       │   │       └── page.tsx
│       │   ├── client-test
│       │   │   └── page.tsx
│       │   ├── dashboard
│       │   │   ├── change-plan.tsx
│       │   │   ├── client.tsx
│       │   │   ├── organization-card.tsx
│       │   │   ├── page.tsx
│       │   │   ├── upgrade-button.tsx
│       │   │   └── user-card.tsx
│       │   ├── device
│       │   │   ├── approve
│       │   │   │   └── page.tsx
│       │   │   ├── denied
│       │   │   │   └── page.tsx
│       │   │   ├── layout.tsx
│       │   │   ├── page.tsx
│       │   │   └── success
│       │   │       └── page.tsx
│       │   ├── favicon.ico
│       │   ├── features.tsx
│       │   ├── fonts
│       │   │   ├── GeistMonoVF.woff
│       │   │   └── GeistVF.woff
│       │   ├── globals.css
│       │   ├── layout.tsx
│       │   ├── oauth
│       │   │   └── authorize
│       │   │       ├── concet-buttons.tsx
│       │   │       └── page.tsx
│       │   ├── page.tsx
│       │   └── pricing
│       │       └── page.tsx
│       ├── components
│       │   ├── account-switch.tsx
│       │   ├── blocks
│       │   │   └── pricing.tsx
│       │   ├── logo.tsx
│       │   ├── one-tap.tsx
│       │   ├── sign-in-btn.tsx
│       │   ├── sign-in.tsx
│       │   ├── sign-up.tsx
│       │   ├── theme-provider.tsx
│       │   ├── theme-toggle.tsx
│       │   ├── tier-labels.tsx
│       │   ├── ui
│       │   │   ├── accordion.tsx
│       │   │   ├── alert-dialog.tsx
│       │   │   ├── alert.tsx
│       │   │   ├── aspect-ratio.tsx
│       │   │   ├── avatar.tsx
│       │   │   ├── badge.tsx
│       │   │   ├── breadcrumb.tsx
│       │   │   ├── button.tsx
│       │   │   ├── calendar.tsx
│       │   │   ├── card.tsx
│       │   │   ├── carousel.tsx
│       │   │   ├── chart.tsx
│       │   │   ├── checkbox.tsx
│       │   │   ├── collapsible.tsx
│       │   │   ├── command.tsx
│       │   │   ├── context-menu.tsx
│       │   │   ├── copy-button.tsx
│       │   │   ├── dialog.tsx
│       │   │   ├── drawer.tsx
│       │   │   ├── dropdown-menu.tsx
│       │   │   ├── form.tsx
│       │   │   ├── hover-card.tsx
│       │   │   ├── input-otp.tsx
│       │   │   ├── input.tsx
│       │   │   ├── label.tsx
│       │   │   ├── menubar.tsx
│       │   │   ├── navigation-menu.tsx
│       │   │   ├── pagination.tsx
│       │   │   ├── password-input.tsx
│       │   │   ├── popover.tsx
│       │   │   ├── progress.tsx
│       │   │   ├── radio-group.tsx
│       │   │   ├── resizable.tsx
│       │   │   ├── scroll-area.tsx
│       │   │   ├── select.tsx
│       │   │   ├── separator.tsx
│       │   │   ├── sheet.tsx
│       │   │   ├── skeleton.tsx
│       │   │   ├── slider.tsx
│       │   │   ├── sonner.tsx
│       │   │   ├── switch.tsx
│       │   │   ├── table.tsx
│       │   │   ├── tabs.tsx
│       │   │   ├── tabs2.tsx
│       │   │   ├── textarea.tsx
│       │   │   ├── toast.tsx
│       │   │   ├── toaster.tsx
│       │   │   ├── toggle-group.tsx
│       │   │   ├── toggle.tsx
│       │   │   └── tooltip.tsx
│       │   └── wrapper.tsx
│       ├── components.json
│       ├── hooks
│       │   └── use-toast.ts
│       ├── lib
│       │   ├── auth-client.ts
│       │   ├── auth-types.ts
│       │   ├── auth.ts
│       │   ├── email
│       │   │   ├── invitation.tsx
│       │   │   ├── resend.ts
│       │   │   └── reset-password.tsx
│       │   ├── metadata.ts
│       │   ├── shared.ts
│       │   └── utils.ts
│       ├── next.config.ts
│       ├── package.json
│       ├── postcss.config.mjs
│       ├── proxy.ts
│       ├── public
│       │   ├── __og.png
│       │   ├── _og.png
│       │   ├── favicon
│       │   │   ├── android-chrome-192x192.png
│       │   │   ├── android-chrome-512x512.png
│       │   │   ├── apple-touch-icon.png
│       │   │   ├── favicon-16x16.png
│       │   │   ├── favicon-32x32.png
│       │   │   ├── favicon.ico
│       │   │   ├── light
│       │   │   │   ├── android-chrome-192x192.png
│       │   │   │   ├── android-chrome-512x512.png
│       │   │   │   ├── apple-touch-icon.png
│       │   │   │   ├── favicon-16x16.png
│       │   │   │   ├── favicon-32x32.png
│       │   │   │   ├── favicon.ico
│       │   │   │   └── site.webmanifest
│       │   │   └── site.webmanifest
│       │   ├── logo.svg
│       │   └── og.png
│       ├── README.md
│       ├── tailwind.config.ts
│       ├── tsconfig.json
│       └── turbo.json
├── docker-compose.yml
├── docs
│   ├── .env.example
│   ├── .gitignore
│   ├── app
│   │   ├── api
│   │   │   ├── ai-chat
│   │   │   │   └── route.ts
│   │   │   ├── analytics
│   │   │   │   ├── conversation
│   │   │   │   │   └── route.ts
│   │   │   │   ├── event
│   │   │   │   │   └── route.ts
│   │   │   │   └── feedback
│   │   │   │       └── route.ts
│   │   │   ├── chat
│   │   │   │   └── route.ts
│   │   │   ├── og
│   │   │   │   └── route.tsx
│   │   │   ├── og-release
│   │   │   │   └── route.tsx
│   │   │   ├── search
│   │   │   │   └── route.ts
│   │   │   └── support
│   │   │       └── route.ts
│   │   ├── blog
│   │   │   ├── _components
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── blog-list.tsx
│   │   │   │   ├── changelog-layout.tsx
│   │   │   │   ├── default-changelog.tsx
│   │   │   │   ├── fmt-dates.tsx
│   │   │   │   ├── icons.tsx
│   │   │   │   ├── stat-field.tsx
│   │   │   │   └── support.tsx
│   │   │   ├── [[...slug]]
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── changelogs
│   │   │   ├── _components
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── changelog-layout.tsx
│   │   │   │   ├── default-changelog.tsx
│   │   │   │   ├── fmt-dates.tsx
│   │   │   │   ├── grid-pattern.tsx
│   │   │   │   ├── icons.tsx
│   │   │   │   └── stat-field.tsx
│   │   │   ├── [[...slug]]
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── community
│   │   │   ├── _components
│   │   │   │   ├── header.tsx
│   │   │   │   └── stats.tsx
│   │   │   └── page.tsx
│   │   ├── docs
│   │   │   ├── [[...slug]]
│   │   │   │   ├── page.client.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   └── lib
│   │   │       └── get-llm-text.ts
│   │   ├── global.css
│   │   ├── layout.config.tsx
│   │   ├── layout.tsx
│   │   ├── llms.txt
│   │   │   ├── [...slug]
│   │   │   │   └── route.ts
│   │   │   └── route.ts
│   │   ├── not-found.tsx
│   │   ├── page.tsx
│   │   ├── reference
│   │   │   └── route.ts
│   │   ├── sitemap.xml
│   │   ├── static.json
│   │   │   └── route.ts
│   │   └── v1
│   │       ├── _components
│   │       │   └── v1-text.tsx
│   │       ├── bg-line.tsx
│   │       └── page.tsx
│   ├── assets
│   │   ├── Geist.ttf
│   │   └── GeistMono.ttf
│   ├── components
│   │   ├── ai-chat-modal.tsx
│   │   ├── anchor-scroll-fix.tsx
│   │   ├── api-method-tabs.tsx
│   │   ├── api-method.tsx
│   │   ├── banner.tsx
│   │   ├── blocks
│   │   │   └── features.tsx
│   │   ├── builder
│   │   │   ├── beam.tsx
│   │   │   ├── code-tabs
│   │   │   │   ├── code-editor.tsx
│   │   │   │   ├── code-tabs.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── tab-bar.tsx
│   │   │   │   └── theme.ts
│   │   │   ├── index.tsx
│   │   │   ├── sign-in.tsx
│   │   │   ├── sign-up.tsx
│   │   │   ├── social-provider.tsx
│   │   │   ├── store.ts
│   │   │   └── tabs.tsx
│   │   ├── display-techstack.tsx
│   │   ├── divider-text.tsx
│   │   ├── docs
│   │   │   ├── docs.client.tsx
│   │   │   ├── docs.tsx
│   │   │   ├── layout
│   │   │   │   ├── nav.tsx
│   │   │   │   ├── theme-toggle.tsx
│   │   │   │   ├── toc-thumb.tsx
│   │   │   │   └── toc.tsx
│   │   │   ├── page.client.tsx
│   │   │   ├── page.tsx
│   │   │   ├── shared.tsx
│   │   │   └── ui
│   │   │       ├── button.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── popover.tsx
│   │   │       └── scroll-area.tsx
│   │   ├── endpoint.tsx
│   │   ├── features.tsx
│   │   ├── floating-ai-search.tsx
│   │   ├── fork-button.tsx
│   │   ├── generate-apple-jwt.tsx
│   │   ├── generate-secret.tsx
│   │   ├── github-stat.tsx
│   │   ├── icons.tsx
│   │   ├── landing
│   │   │   ├── gradient-bg.tsx
│   │   │   ├── grid-pattern.tsx
│   │   │   ├── hero.tsx
│   │   │   ├── section-svg.tsx
│   │   │   ├── section.tsx
│   │   │   ├── spotlight.tsx
│   │   │   └── testimonials.tsx
│   │   ├── logo-context-menu.tsx
│   │   ├── logo.tsx
│   │   ├── markdown-renderer.tsx
│   │   ├── markdown.tsx
│   │   ├── mdx
│   │   │   ├── add-to-cursor.tsx
│   │   │   └── database-tables.tsx
│   │   ├── message-feedback.tsx
│   │   ├── mobile-search-icon.tsx
│   │   ├── nav-bar.tsx
│   │   ├── nav-link.tsx
│   │   ├── nav-mobile.tsx
│   │   ├── promo-card.tsx
│   │   ├── resource-card.tsx
│   │   ├── resource-grid.tsx
│   │   ├── resource-section.tsx
│   │   ├── ripple.tsx
│   │   ├── search-dialog.tsx
│   │   ├── side-bar.tsx
│   │   ├── sidebar-content.tsx
│   │   ├── techstack-icons.tsx
│   │   ├── theme-provider.tsx
│   │   ├── theme-toggler.tsx
│   │   └── ui
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── aside-link.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── background-beams.tsx
│   │       ├── background-boxes.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── callout.tsx
│   │       ├── card.tsx
│   │       ├── carousel.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── code-block.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── context-menu.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── dynamic-code-block.tsx
│   │       ├── fade-in.tsx
│   │       ├── form.tsx
│   │       ├── hover-card.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── menubar.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── pagination.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── resizable.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── slider.tsx
│   │       ├── sonner.tsx
│   │       ├── sparkles.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toggle-group.tsx
│   │       ├── toggle.tsx
│   │       ├── tooltip-docs.tsx
│   │       ├── tooltip.tsx
│   │       └── use-copy-button.tsx
│   ├── components.json
│   ├── content
│   │   ├── blogs
│   │   │   ├── 0-supabase-auth-to-planetscale-migration.mdx
│   │   │   ├── 1-3.mdx
│   │   │   ├── authjs-joins-better-auth.mdx
│   │   │   └── seed-round.mdx
│   │   ├── changelogs
│   │   │   ├── 1-2.mdx
│   │   │   └── 1.0.mdx
│   │   └── docs
│   │       ├── adapters
│   │       │   ├── community-adapters.mdx
│   │       │   ├── drizzle.mdx
│   │       │   ├── mongo.mdx
│   │       │   ├── mssql.mdx
│   │       │   ├── mysql.mdx
│   │       │   ├── other-relational-databases.mdx
│   │       │   ├── postgresql.mdx
│   │       │   ├── prisma.mdx
│   │       │   └── sqlite.mdx
│   │       ├── authentication
│   │       │   ├── apple.mdx
│   │       │   ├── atlassian.mdx
│   │       │   ├── cognito.mdx
│   │       │   ├── discord.mdx
│   │       │   ├── dropbox.mdx
│   │       │   ├── email-password.mdx
│   │       │   ├── facebook.mdx
│   │       │   ├── figma.mdx
│   │       │   ├── github.mdx
│   │       │   ├── gitlab.mdx
│   │       │   ├── google.mdx
│   │       │   ├── huggingface.mdx
│   │       │   ├── kakao.mdx
│   │       │   ├── kick.mdx
│   │       │   ├── line.mdx
│   │       │   ├── linear.mdx
│   │       │   ├── linkedin.mdx
│   │       │   ├── microsoft.mdx
│   │       │   ├── naver.mdx
│   │       │   ├── notion.mdx
│   │       │   ├── other-social-providers.mdx
│   │       │   ├── paypal.mdx
│   │       │   ├── reddit.mdx
│   │       │   ├── roblox.mdx
│   │       │   ├── salesforce.mdx
│   │       │   ├── slack.mdx
│   │       │   ├── spotify.mdx
│   │       │   ├── tiktok.mdx
│   │       │   ├── twitch.mdx
│   │       │   ├── twitter.mdx
│   │       │   ├── vk.mdx
│   │       │   └── zoom.mdx
│   │       ├── basic-usage.mdx
│   │       ├── comparison.mdx
│   │       ├── concepts
│   │       │   ├── api.mdx
│   │       │   ├── cli.mdx
│   │       │   ├── client.mdx
│   │       │   ├── cookies.mdx
│   │       │   ├── database.mdx
│   │       │   ├── email.mdx
│   │       │   ├── hooks.mdx
│   │       │   ├── oauth.mdx
│   │       │   ├── plugins.mdx
│   │       │   ├── rate-limit.mdx
│   │       │   ├── session-management.mdx
│   │       │   ├── typescript.mdx
│   │       │   └── users-accounts.mdx
│   │       ├── examples
│   │       │   ├── astro.mdx
│   │       │   ├── next-js.mdx
│   │       │   ├── nuxt.mdx
│   │       │   ├── remix.mdx
│   │       │   └── svelte-kit.mdx
│   │       ├── guides
│   │       │   ├── auth0-migration-guide.mdx
│   │       │   ├── browser-extension-guide.mdx
│   │       │   ├── clerk-migration-guide.mdx
│   │       │   ├── create-a-db-adapter.mdx
│   │       │   ├── next-auth-migration-guide.mdx
│   │       │   ├── optimizing-for-performance.mdx
│   │       │   ├── saml-sso-with-okta.mdx
│   │       │   ├── supabase-migration-guide.mdx
│   │       │   └── your-first-plugin.mdx
│   │       ├── installation.mdx
│   │       ├── integrations
│   │       │   ├── astro.mdx
│   │       │   ├── convex.mdx
│   │       │   ├── elysia.mdx
│   │       │   ├── expo.mdx
│   │       │   ├── express.mdx
│   │       │   ├── fastify.mdx
│   │       │   ├── hono.mdx
│   │       │   ├── lynx.mdx
│   │       │   ├── nestjs.mdx
│   │       │   ├── next.mdx
│   │       │   ├── nitro.mdx
│   │       │   ├── nuxt.mdx
│   │       │   ├── remix.mdx
│   │       │   ├── solid-start.mdx
│   │       │   ├── svelte-kit.mdx
│   │       │   ├── tanstack.mdx
│   │       │   └── waku.mdx
│   │       ├── introduction.mdx
│   │       ├── meta.json
│   │       ├── plugins
│   │       │   ├── 2fa.mdx
│   │       │   ├── admin.mdx
│   │       │   ├── anonymous.mdx
│   │       │   ├── api-key.mdx
│   │       │   ├── autumn.mdx
│   │       │   ├── bearer.mdx
│   │       │   ├── captcha.mdx
│   │       │   ├── community-plugins.mdx
│   │       │   ├── device-authorization.mdx
│   │       │   ├── dodopayments.mdx
│   │       │   ├── dub.mdx
│   │       │   ├── email-otp.mdx
│   │       │   ├── generic-oauth.mdx
│   │       │   ├── have-i-been-pwned.mdx
│   │       │   ├── jwt.mdx
│   │       │   ├── last-login-method.mdx
│   │       │   ├── magic-link.mdx
│   │       │   ├── mcp.mdx
│   │       │   ├── multi-session.mdx
│   │       │   ├── oauth-proxy.mdx
│   │       │   ├── oidc-provider.mdx
│   │       │   ├── one-tap.mdx
│   │       │   ├── one-time-token.mdx
│   │       │   ├── open-api.mdx
│   │       │   ├── organization.mdx
│   │       │   ├── passkey.mdx
│   │       │   ├── phone-number.mdx
│   │       │   ├── polar.mdx
│   │       │   ├── siwe.mdx
│   │       │   ├── sso.mdx
│   │       │   ├── stripe.mdx
│   │       │   └── username.mdx
│   │       └── reference
│   │           ├── contributing.mdx
│   │           ├── faq.mdx
│   │           ├── options.mdx
│   │           ├── resources.mdx
│   │           ├── security.mdx
│   │           └── telemetry.mdx
│   ├── hooks
│   │   └── use-mobile.ts
│   ├── ignore-build.sh
│   ├── lib
│   │   ├── blog.ts
│   │   ├── chat
│   │   │   └── inkeep-qa-schema.ts
│   │   ├── constants.ts
│   │   ├── export-search-indexes.ts
│   │   ├── inkeep-analytics.ts
│   │   ├── is-active.ts
│   │   ├── metadata.ts
│   │   ├── source.ts
│   │   └── utils.ts
│   ├── next.config.js
│   ├── package.json
│   ├── postcss.config.js
│   ├── proxy.ts
│   ├── public
│   │   ├── avatars
│   │   │   └── beka.jpg
│   │   ├── blogs
│   │   │   ├── authjs-joins.png
│   │   │   ├── seed-round.png
│   │   │   └── supabase-ps.png
│   │   ├── branding
│   │   │   ├── better-auth-brand-assets.zip
│   │   │   ├── better-auth-logo-dark.png
│   │   │   ├── better-auth-logo-dark.svg
│   │   │   ├── better-auth-logo-light.png
│   │   │   ├── better-auth-logo-light.svg
│   │   │   ├── better-auth-logo-wordmark-dark.png
│   │   │   ├── better-auth-logo-wordmark-dark.svg
│   │   │   ├── better-auth-logo-wordmark-light.png
│   │   │   └── better-auth-logo-wordmark-light.svg
│   │   ├── extension-id.png
│   │   ├── favicon
│   │   │   ├── android-chrome-192x192.png
│   │   │   ├── android-chrome-512x512.png
│   │   │   ├── apple-touch-icon.png
│   │   │   ├── favicon-16x16.png
│   │   │   ├── favicon-32x32.png
│   │   │   ├── favicon.ico
│   │   │   ├── light
│   │   │   │   ├── android-chrome-192x192.png
│   │   │   │   ├── android-chrome-512x512.png
│   │   │   │   ├── apple-touch-icon.png
│   │   │   │   ├── favicon-16x16.png
│   │   │   │   ├── favicon-32x32.png
│   │   │   │   ├── favicon.ico
│   │   │   │   └── site.webmanifest
│   │   │   └── site.webmanifest
│   │   ├── images
│   │   │   └── blogs
│   │   │       └── better auth (1).png
│   │   ├── logo.png
│   │   ├── logo.svg
│   │   ├── LogoDark.webp
│   │   ├── LogoLight.webp
│   │   ├── og.png
│   │   ├── open-api-reference.png
│   │   ├── people-say
│   │   │   ├── code-with-antonio.jpg
│   │   │   ├── dagmawi-babi.png
│   │   │   ├── dax.png
│   │   │   ├── dev-ed.png
│   │   │   ├── egoist.png
│   │   │   ├── guillermo-rauch.png
│   │   │   ├── jonathan-wilke.png
│   │   │   ├── josh-tried-coding.jpg
│   │   │   ├── kitze.jpg
│   │   │   ├── lazar-nikolov.png
│   │   │   ├── nizzy.png
│   │   │   ├── omar-mcadam.png
│   │   │   ├── ryan-vogel.jpg
│   │   │   ├── saltyatom.jpg
│   │   │   ├── sebastien-chopin.png
│   │   │   ├── shreyas-mididoddi.png
│   │   │   ├── tech-nerd.png
│   │   │   ├── theo.png
│   │   │   ├── vybhav-bhargav.png
│   │   │   └── xavier-pladevall.jpg
│   │   ├── plus.svg
│   │   ├── release-og
│   │   │   ├── 1-2.png
│   │   │   ├── 1-3.png
│   │   │   └── changelog-og.png
│   │   └── v1-og.png
│   ├── README.md
│   ├── scripts
│   │   ├── endpoint-to-doc
│   │   │   ├── index.ts
│   │   │   ├── input.ts
│   │   │   ├── output.mdx
│   │   │   └── readme.md
│   │   └── sync-orama.ts
│   ├── source.config.ts
│   ├── tailwind.config.js
│   ├── tsconfig.json
│   └── turbo.json
├── e2e
│   ├── integration
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   ├── solid-vinxi
│   │   │   ├── .gitignore
│   │   │   ├── app.config.ts
│   │   │   ├── e2e
│   │   │   │   ├── test.spec.ts
│   │   │   │   └── utils.ts
│   │   │   ├── package.json
│   │   │   ├── public
│   │   │   │   └── favicon.ico
│   │   │   ├── src
│   │   │   │   ├── app.tsx
│   │   │   │   ├── entry-client.tsx
│   │   │   │   ├── entry-server.tsx
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── lib
│   │   │   │   │   ├── auth-client.ts
│   │   │   │   │   └── auth.ts
│   │   │   │   └── routes
│   │   │   │       ├── [...404].tsx
│   │   │   │       ├── api
│   │   │   │       │   └── auth
│   │   │   │       │       └── [...all].ts
│   │   │   │       └── index.tsx
│   │   │   └── tsconfig.json
│   │   ├── test-utils
│   │   │   ├── package.json
│   │   │   └── src
│   │   │       └── playwright.ts
│   │   └── vanilla-node
│   │       ├── e2e
│   │       │   ├── app.ts
│   │       │   ├── domain.spec.ts
│   │       │   ├── postgres-js.spec.ts
│   │       │   ├── test.spec.ts
│   │       │   └── utils.ts
│   │       ├── index.html
│   │       ├── package.json
│   │       ├── src
│   │       │   ├── main.ts
│   │       │   └── vite-env.d.ts
│   │       ├── tsconfig.json
│   │       └── vite.config.ts
│   └── smoke
│       ├── package.json
│       ├── test
│       │   ├── bun.spec.ts
│       │   ├── cloudflare.spec.ts
│       │   ├── deno.spec.ts
│       │   ├── fixtures
│       │   │   ├── bun-simple.ts
│       │   │   ├── cloudflare
│       │   │   │   ├── .gitignore
│       │   │   │   ├── drizzle
│       │   │   │   │   ├── 0000_clean_vector.sql
│       │   │   │   │   └── meta
│       │   │   │   │       ├── _journal.json
│       │   │   │   │       └── 0000_snapshot.json
│       │   │   │   ├── drizzle.config.ts
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── auth-schema.ts
│       │   │   │   │   ├── db.ts
│       │   │   │   │   └── index.ts
│       │   │   │   ├── test
│       │   │   │   │   ├── apply-migrations.ts
│       │   │   │   │   ├── env.d.ts
│       │   │   │   │   └── index.test.ts
│       │   │   │   ├── tsconfig.json
│       │   │   │   ├── vitest.config.ts
│       │   │   │   ├── worker-configuration.d.ts
│       │   │   │   └── wrangler.json
│       │   │   ├── deno-simple.ts
│       │   │   ├── tsconfig-decelration
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── demo.ts
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-exact-optional-property-types
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── organization.ts
│       │   │   │   │   ├── user-additional-fields.ts
│       │   │   │   │   └── username.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-isolated-module-bundler
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-verbatim-module-syntax-node10
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   └── vite
│       │   │       ├── package.json
│       │   │       ├── src
│       │   │       │   ├── client.ts
│       │   │       │   └── server.ts
│       │   │       ├── tsconfig.json
│       │   │       └── vite.config.ts
│       │   ├── ssr.ts
│       │   ├── typecheck.spec.ts
│       │   └── vite.spec.ts
│       └── tsconfig.json
├── LICENSE.md
├── package.json
├── packages
│   ├── better-auth
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── __snapshots__
│   │   │   │   └── init.test.ts.snap
│   │   │   ├── adapters
│   │   │   │   ├── adapter-factory
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── __snapshots__
│   │   │   │   │   │   │   └── adapter-factory.test.ts.snap
│   │   │   │   │   │   └── adapter-factory.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── create-test-suite.ts
│   │   │   │   ├── drizzle-adapter
│   │   │   │   │   ├── drizzle-adapter.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── adapter.drizzle.mysql.test.ts
│   │   │   │   │       ├── adapter.drizzle.pg.test.ts
│   │   │   │   │       ├── adapter.drizzle.sqlite.test.ts
│   │   │   │   │       └── generate-schema.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kysely-adapter
│   │   │   │   │   ├── bun-sqlite-dialect.ts
│   │   │   │   │   ├── dialect.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── kysely-adapter.ts
│   │   │   │   │   ├── node-sqlite-dialect.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── adapter.kysely.mssql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.mysql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.pg.test.ts
│   │   │   │   │   │   ├── adapter.kysely.sqlite.test.ts
│   │   │   │   │   │   └── node-sqlite-dialect.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── memory-adapter
│   │   │   │   │   ├── adapter.memory.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── memory-adapter.ts
│   │   │   │   ├── mongodb-adapter
│   │   │   │   │   ├── adapter.mongo-db.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── mongodb-adapter.ts
│   │   │   │   ├── prisma-adapter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── prisma-adapter.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── base.prisma
│   │   │   │   │       ├── generate-auth-config.ts
│   │   │   │   │       ├── generate-prisma-schema.ts
│   │   │   │   │       ├── get-prisma-client.ts
│   │   │   │   │       ├── prisma.mysql.test.ts
│   │   │   │   │       ├── prisma.pg.test.ts
│   │   │   │   │       ├── prisma.sqlite.test.ts
│   │   │   │   │       └── push-prisma-schema.ts
│   │   │   │   ├── test-adapter.ts
│   │   │   │   ├── test.ts
│   │   │   │   ├── tests
│   │   │   │   │   ├── auth-flow.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── normal.ts
│   │   │   │   │   ├── number-id.ts
│   │   │   │   │   ├── performance.ts
│   │   │   │   │   └── transactions.ts
│   │   │   │   └── utils.ts
│   │   │   ├── api
│   │   │   │   ├── check-endpoint-conflicts.test.ts
│   │   │   │   ├── index.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── middlewares
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── origin-check.test.ts
│   │   │   │   │   └── origin-check.ts
│   │   │   │   ├── rate-limiter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── rate-limiter.test.ts
│   │   │   │   ├── routes
│   │   │   │   │   ├── account.test.ts
│   │   │   │   │   ├── account.ts
│   │   │   │   │   ├── callback.ts
│   │   │   │   │   ├── email-verification.test.ts
│   │   │   │   │   ├── email-verification.ts
│   │   │   │   │   ├── error.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── ok.ts
│   │   │   │   │   ├── reset-password.test.ts
│   │   │   │   │   ├── reset-password.ts
│   │   │   │   │   ├── session-api.test.ts
│   │   │   │   │   ├── session.ts
│   │   │   │   │   ├── sign-in.test.ts
│   │   │   │   │   ├── sign-in.ts
│   │   │   │   │   ├── sign-out.test.ts
│   │   │   │   │   ├── sign-out.ts
│   │   │   │   │   ├── sign-up.test.ts
│   │   │   │   │   ├── sign-up.ts
│   │   │   │   │   ├── update-user.test.ts
│   │   │   │   │   └── update-user.ts
│   │   │   │   ├── to-auth-endpoints.test.ts
│   │   │   │   └── to-auth-endpoints.ts
│   │   │   ├── auth.test.ts
│   │   │   ├── auth.ts
│   │   │   ├── call.test.ts
│   │   │   ├── client
│   │   │   │   ├── client-ssr.test.ts
│   │   │   │   ├── client.test.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── fetch-plugins.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── lynx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── lynx-store.ts
│   │   │   │   ├── parser.ts
│   │   │   │   ├── path-to-object.ts
│   │   │   │   ├── plugins
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── infer-plugin.ts
│   │   │   │   ├── proxy.ts
│   │   │   │   ├── query.ts
│   │   │   │   ├── react
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── react-store.ts
│   │   │   │   ├── session-atom.ts
│   │   │   │   ├── solid
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── solid-store.ts
│   │   │   │   ├── svelte
│   │   │   │   │   └── index.ts
│   │   │   │   ├── test-plugin.ts
│   │   │   │   ├── types.ts
│   │   │   │   ├── url.test.ts
│   │   │   │   ├── vanilla.ts
│   │   │   │   └── vue
│   │   │   │       ├── index.ts
│   │   │   │       └── vue-store.ts
│   │   │   ├── cookies
│   │   │   │   ├── check-cookies.ts
│   │   │   │   ├── cookie-utils.ts
│   │   │   │   ├── cookies.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── crypto
│   │   │   │   ├── buffer.ts
│   │   │   │   ├── hash.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jwt.ts
│   │   │   │   ├── password.test.ts
│   │   │   │   ├── password.ts
│   │   │   │   └── random.ts
│   │   │   ├── db
│   │   │   │   ├── db.test.ts
│   │   │   │   ├── field.ts
│   │   │   │   ├── get-migration.ts
│   │   │   │   ├── get-schema.ts
│   │   │   │   ├── get-tables.test.ts
│   │   │   │   ├── get-tables.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── internal-adapter.test.ts
│   │   │   │   ├── internal-adapter.ts
│   │   │   │   ├── schema.ts
│   │   │   │   ├── secondary-storage.test.ts
│   │   │   │   ├── to-zod.ts
│   │   │   │   ├── utils.ts
│   │   │   │   └── with-hooks.ts
│   │   │   ├── index.ts
│   │   │   ├── init.test.ts
│   │   │   ├── init.ts
│   │   │   ├── integrations
│   │   │   │   ├── next-js.ts
│   │   │   │   ├── node.ts
│   │   │   │   ├── react-start.ts
│   │   │   │   ├── solid-start.ts
│   │   │   │   └── svelte-kit.ts
│   │   │   ├── oauth2
│   │   │   │   ├── index.ts
│   │   │   │   ├── link-account.test.ts
│   │   │   │   ├── link-account.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── utils.ts
│   │   │   ├── plugins
│   │   │   │   ├── access
│   │   │   │   │   ├── access.test.ts
│   │   │   │   │   ├── access.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── additional-fields
│   │   │   │   │   ├── additional-fields.test.ts
│   │   │   │   │   └── client.ts
│   │   │   │   ├── admin
│   │   │   │   │   ├── access
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── statement.ts
│   │   │   │   │   ├── admin.test.ts
│   │   │   │   │   ├── admin.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── has-permission.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── anonymous
│   │   │   │   │   ├── anon.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── api-key
│   │   │   │   │   ├── api-key.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── rate-limit.ts
│   │   │   │   │   ├── routes
│   │   │   │   │   │   ├── create-api-key.ts
│   │   │   │   │   │   ├── delete-all-expired-api-keys.ts
│   │   │   │   │   │   ├── delete-api-key.ts
│   │   │   │   │   │   ├── get-api-key.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── list-api-keys.ts
│   │   │   │   │   │   ├── update-api-key.ts
│   │   │   │   │   │   └── verify-api-key.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── bearer
│   │   │   │   │   ├── bearer.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── captcha
│   │   │   │   │   ├── captcha.test.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   └── verify-handlers
│   │   │   │   │       ├── captchafox.ts
│   │   │   │   │       ├── cloudflare-turnstile.ts
│   │   │   │   │       ├── google-recaptcha.ts
│   │   │   │   │       ├── h-captcha.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── custom-session
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── custom-session.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── device-authorization
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── device-authorization.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── schema.ts
│   │   │   │   ├── email-otp
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── email-otp.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── generic-oauth
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── generic-oauth.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── haveibeenpwned
│   │   │   │   │   ├── haveibeenpwned.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jwt
│   │   │   │   │   ├── adapter.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── jwt.test.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── sign.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── last-login-method
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── custom-prefix.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── last-login-method.test.ts
│   │   │   │   ├── magic-link
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── magic-link.test.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── mcp
│   │   │   │   │   ├── authorize.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── mcp.test.ts
│   │   │   │   ├── multi-session
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── multi-session.test.ts
│   │   │   │   ├── oauth-proxy
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── oauth-proxy.test.ts
│   │   │   │   ├── oidc-provider
│   │   │   │   │   ├── authorize.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── oidc.test.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── ui.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── one-tap
│   │   │   │   │   ├── client.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── one-time-token
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── one-time-token.test.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── open-api
│   │   │   │   │   ├── generator.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── logo.ts
│   │   │   │   │   └── open-api.test.ts
│   │   │   │   ├── organization
│   │   │   │   │   ├── access
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── statement.ts
│   │   │   │   │   ├── adapter.ts
│   │   │   │   │   ├── call.ts
│   │   │   │   │   ├── client.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── has-permission.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── organization-hook.test.ts
│   │   │   │   │   ├── organization.test.ts
│   │   │   │   │   ├── organization.ts
│   │   │   │   │   ├── permission.ts
│   │   │   │   │   ├── routes
│   │   │   │   │   │   ├── crud-access-control.test.ts
│   │   │   │   │   │   ├── crud-access-control.ts
│   │   │   │   │   │   ├── crud-invites.ts
│   │   │   │   │   │   ├── crud-members.test.ts
│   │   │   │   │   │   ├── crud-members.ts
│   │   │   │   │   │   ├── crud-org.test.ts
│   │   │   │   │   │   ├── crud-org.ts
│   │   │   │   │   │   └── crud-team.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── team.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── passkey
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── passkey.test.ts
│   │   │   │   ├── phone-number
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── phone-number-error.ts
│   │   │   │   │   └── phone-number.test.ts
│   │   │   │   ├── siwe
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── siwe.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── two-factor
│   │   │   │   │   ├── backup-codes
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── constant.ts
│   │   │   │   │   ├── error-code.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── otp
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── totp
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── two-factor.test.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   └── verify-two-factor.ts
│   │   │   │   └── username
│   │   │   │       ├── client.ts
│   │   │   │       ├── error-codes.ts
│   │   │   │       ├── index.ts
│   │   │   │       ├── schema.ts
│   │   │   │       └── username.test.ts
│   │   │   ├── social-providers
│   │   │   │   └── index.ts
│   │   │   ├── social.test.ts
│   │   │   ├── test-utils
│   │   │   │   ├── headers.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── test-instance.ts
│   │   │   ├── types
│   │   │   │   ├── adapter.ts
│   │   │   │   ├── api.ts
│   │   │   │   ├── helper.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── models.ts
│   │   │   │   ├── plugins.ts
│   │   │   │   └── types.test.ts
│   │   │   └── utils
│   │   │       ├── await-object.ts
│   │   │       ├── boolean.ts
│   │   │       ├── clone.ts
│   │   │       ├── constants.ts
│   │   │       ├── date.ts
│   │   │       ├── ensure-utc.ts
│   │   │       ├── get-request-ip.ts
│   │   │       ├── hashing.ts
│   │   │       ├── hide-metadata.ts
│   │   │       ├── id.ts
│   │   │       ├── import-util.ts
│   │   │       ├── index.ts
│   │   │       ├── is-atom.ts
│   │   │       ├── is-promise.ts
│   │   │       ├── json.ts
│   │   │       ├── merger.ts
│   │   │       ├── middleware-response.ts
│   │   │       ├── misc.ts
│   │   │       ├── password.ts
│   │   │       ├── plugin-helper.ts
│   │   │       ├── shim.ts
│   │   │       ├── time.ts
│   │   │       ├── url.ts
│   │   │       └── wildcard.ts
│   │   ├── tsconfig.json
│   │   ├── tsdown.config.ts
│   │   ├── vitest.config.ts
│   │   └── vitest.setup.ts
│   ├── cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── commands
│   │   │   │   ├── generate.ts
│   │   │   │   ├── info.ts
│   │   │   │   ├── init.ts
│   │   │   │   ├── login.ts
│   │   │   │   ├── mcp.ts
│   │   │   │   ├── migrate.ts
│   │   │   │   └── secret.ts
│   │   │   ├── generators
│   │   │   │   ├── auth-config.ts
│   │   │   │   ├── drizzle.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kysely.ts
│   │   │   │   ├── prisma.ts
│   │   │   │   └── types.ts
│   │   │   ├── index.ts
│   │   │   └── utils
│   │   │       ├── add-svelte-kit-env-modules.ts
│   │   │       ├── check-package-managers.ts
│   │   │       ├── format-ms.ts
│   │   │       ├── get-config.ts
│   │   │       ├── get-package-info.ts
│   │   │       ├── get-tsconfig-info.ts
│   │   │       └── install-dependencies.ts
│   │   ├── test
│   │   │   ├── __snapshots__
│   │   │   │   ├── auth-schema-mysql-enum.txt
│   │   │   │   ├── auth-schema-mysql-number-id.txt
│   │   │   │   ├── auth-schema-mysql-passkey-number-id.txt
│   │   │   │   ├── auth-schema-mysql-passkey.txt
│   │   │   │   ├── auth-schema-mysql.txt
│   │   │   │   ├── auth-schema-number-id.txt
│   │   │   │   ├── auth-schema-pg-enum.txt
│   │   │   │   ├── auth-schema-pg-passkey.txt
│   │   │   │   ├── auth-schema-sqlite-enum.txt
│   │   │   │   ├── auth-schema-sqlite-number-id.txt
│   │   │   │   ├── auth-schema-sqlite-passkey-number-id.txt
│   │   │   │   ├── auth-schema-sqlite-passkey.txt
│   │   │   │   ├── auth-schema-sqlite.txt
│   │   │   │   ├── auth-schema.txt
│   │   │   │   ├── migrations.sql
│   │   │   │   ├── schema-mongodb.prisma
│   │   │   │   ├── schema-mysql-custom.prisma
│   │   │   │   ├── schema-mysql.prisma
│   │   │   │   ├── schema-numberid.prisma
│   │   │   │   └── schema.prisma
│   │   │   ├── generate-all-db.test.ts
│   │   │   ├── generate.test.ts
│   │   │   ├── get-config.test.ts
│   │   │   ├── info.test.ts
│   │   │   └── migrate.test.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.test.json
│   │   └── tsdown.config.ts
│   ├── core
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── api
│   │   │   │   └── index.ts
│   │   │   ├── async_hooks
│   │   │   │   └── index.ts
│   │   │   ├── context
│   │   │   │   ├── endpoint-context.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── transaction.ts
│   │   │   ├── db
│   │   │   │   ├── adapter
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── plugin.ts
│   │   │   │   ├── schema
│   │   │   │   │   ├── account.ts
│   │   │   │   │   ├── rate-limit.ts
│   │   │   │   │   ├── session.ts
│   │   │   │   │   ├── shared.ts
│   │   │   │   │   ├── user.ts
│   │   │   │   │   └── verification.ts
│   │   │   │   └── type.ts
│   │   │   ├── env
│   │   │   │   ├── color-depth.ts
│   │   │   │   ├── env-impl.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── logger.test.ts
│   │   │   │   └── logger.ts
│   │   │   ├── error
│   │   │   │   ├── codes.ts
│   │   │   │   └── index.ts
│   │   │   ├── index.ts
│   │   │   ├── oauth2
│   │   │   │   ├── client-credentials-token.ts
│   │   │   │   ├── create-authorization-url.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── oauth-provider.ts
│   │   │   │   ├── refresh-access-token.ts
│   │   │   │   ├── utils.ts
│   │   │   │   └── validate-authorization-code.ts
│   │   │   ├── social-providers
│   │   │   │   ├── apple.ts
│   │   │   │   ├── atlassian.ts
│   │   │   │   ├── cognito.ts
│   │   │   │   ├── discord.ts
│   │   │   │   ├── dropbox.ts
│   │   │   │   ├── facebook.ts
│   │   │   │   ├── figma.ts
│   │   │   │   ├── github.ts
│   │   │   │   ├── gitlab.ts
│   │   │   │   ├── google.ts
│   │   │   │   ├── huggingface.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kakao.ts
│   │   │   │   ├── kick.ts
│   │   │   │   ├── line.ts
│   │   │   │   ├── linear.ts
│   │   │   │   ├── linkedin.ts
│   │   │   │   ├── microsoft-entra-id.ts
│   │   │   │   ├── naver.ts
│   │   │   │   ├── notion.ts
│   │   │   │   ├── paypal.ts
│   │   │   │   ├── reddit.ts
│   │   │   │   ├── roblox.ts
│   │   │   │   ├── salesforce.ts
│   │   │   │   ├── slack.ts
│   │   │   │   ├── spotify.ts
│   │   │   │   ├── tiktok.ts
│   │   │   │   ├── twitch.ts
│   │   │   │   ├── twitter.ts
│   │   │   │   ├── vk.ts
│   │   │   │   └── zoom.ts
│   │   │   ├── types
│   │   │   │   ├── context.ts
│   │   │   │   ├── cookie.ts
│   │   │   │   ├── helper.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── init-options.ts
│   │   │   │   ├── plugin-client.ts
│   │   │   │   └── plugin.ts
│   │   │   └── utils
│   │   │       ├── error-codes.ts
│   │   │       └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── expo
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── client.ts
│   │   │   ├── expo.test.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── sso
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── client.ts
│   │   │   ├── index.ts
│   │   │   ├── oidc.test.ts
│   │   │   └── saml.test.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── stripe
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── client.ts
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   ├── schema.ts
│   │   │   ├── stripe.test.ts
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   ├── tsconfig.json
│   │   ├── tsdown.config.ts
│   │   └── vitest.config.ts
│   └── telemetry
│       ├── package.json
│       ├── src
│       │   ├── detectors
│       │   │   ├── detect-auth-config.ts
│       │   │   ├── detect-database.ts
│       │   │   ├── detect-framework.ts
│       │   │   ├── detect-project-info.ts
│       │   │   ├── detect-runtime.ts
│       │   │   └── detect-system-info.ts
│       │   ├── index.ts
│       │   ├── project-id.ts
│       │   ├── telemetry.test.ts
│       │   ├── types.ts
│       │   └── utils
│       │       ├── hash.ts
│       │       ├── id.ts
│       │       ├── import-util.ts
│       │       └── package-json.ts
│       ├── tsconfig.json
│       └── tsdown.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md
├── SECURITY.md
├── tsconfig.json
└── turbo.json
```

# Files

--------------------------------------------------------------------------------
/packages/better-auth/src/adapters/adapter-factory/test/adapter-factory.test.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { describe, test, expect } from "vitest";
   2 | import { createAdapterFactory } from "..";
   3 | import type {
   4 | 	AdapterFactoryConfig,
   5 | 	AdapterFactoryCustomizeAdapterCreator,
   6 | } from "../types";
   7 | import type { CleanedWhere, Where } from "@better-auth/core/db/adapter";
   8 | import type { User } from "../../../types";
   9 | import type { BetterAuthOptions } from "@better-auth/core";
  10 | import { betterAuth } from "../../../auth";
  11 | 
  12 | /*
  13 | 
  14 | Note that there are basically 2 types of tests here:
  15 | 
  16 | 1. Making sure that the data within each adapter call is correct. (Transformed to suit the DB, accurate according to the schema, etc.)
  17 | 2. Making sure the output of each adapter call is correct. (The data is transformed back to the correct format, etc.)
  18 | 
  19 | The rest are just edge cases.
  20 | 
  21 | */
  22 | 
  23 | async function createTestAdapter(
  24 | 	props: {
  25 | 		config?: Partial<AdapterFactoryConfig>;
  26 | 		options?: BetterAuthOptions;
  27 | 		adapter?: (
  28 | 			...args: Parameters<AdapterFactoryCustomizeAdapterCreator>
  29 | 		) => Partial<ReturnType<AdapterFactoryCustomizeAdapterCreator>>;
  30 | 	} = {
  31 | 		config: {
  32 | 			adapterId: "test-id",
  33 | 			adapterName: "Test Adapter",
  34 | 			usePlural: false,
  35 | 			debugLogs: false,
  36 | 			supportsJSON: true,
  37 | 			supportsDates: true,
  38 | 			supportsBooleans: true,
  39 | 		},
  40 | 		options: {},
  41 | 		adapter: () => ({}),
  42 | 	},
  43 | ) {
  44 | 	const {
  45 | 		config = {
  46 | 			adapterId: "test-id",
  47 | 			adapterName: "Test Adapter",
  48 | 			usePlural: false,
  49 | 			debugLogs: false,
  50 | 			supportsJSON: true,
  51 | 			supportsDates: true,
  52 | 			supportsBooleans: true,
  53 | 		},
  54 | 		options = {},
  55 | 		adapter = () => ({}),
  56 | 	} = props;
  57 | 	const testAdapter = createAdapterFactory({
  58 | 		config: Object.assign(
  59 | 			{
  60 | 				adapterId: "test-id",
  61 | 				adapterName: "Test Adapter",
  62 | 				usePlural: false,
  63 | 				debugLogs: false,
  64 | 				supportsJSON: true,
  65 | 				supportsDates: true,
  66 | 				supportsBooleans: true,
  67 | 			},
  68 | 			config,
  69 | 		),
  70 | 		adapter: (...args) => {
  71 | 			const x = adapter(...args) as Partial<
  72 | 				ReturnType<AdapterFactoryCustomizeAdapterCreator>
  73 | 			>;
  74 | 			return {
  75 | 				async create(data) {
  76 | 					if (x.create) {
  77 | 						return await x.create(data);
  78 | 					}
  79 | 					return data.data;
  80 | 				},
  81 | 				async update(data) {
  82 | 					if (x.update) {
  83 | 						return await x.update(data);
  84 | 					}
  85 | 					return data.update;
  86 | 				},
  87 | 				async updateMany(data) {
  88 | 					if (x.updateMany) {
  89 | 						return await x.updateMany(data);
  90 | 					}
  91 | 					return 0;
  92 | 				},
  93 | 				async count(data) {
  94 | 					if (x.count) {
  95 | 						return await x.count(data);
  96 | 					}
  97 | 					return 0;
  98 | 				},
  99 | 				async delete(data) {
 100 | 					if (x.delete) {
 101 | 						return await x.delete(data);
 102 | 					}
 103 | 					return;
 104 | 				},
 105 | 				async deleteMany(data) {
 106 | 					if (x.deleteMany) {
 107 | 						return await x.deleteMany(data);
 108 | 					}
 109 | 					return 0;
 110 | 				},
 111 | 				async findMany(data) {
 112 | 					if (x.findMany) {
 113 | 						return await x.findMany(data);
 114 | 					}
 115 | 					return [];
 116 | 				},
 117 | 				async findOne(data) {
 118 | 					if (x.findOne) {
 119 | 						return await x.findOne(data);
 120 | 					}
 121 | 					return null;
 122 | 				},
 123 | 				options: x.options ?? {},
 124 | 			};
 125 | 		},
 126 | 	});
 127 | 	const auth = betterAuth({
 128 | 		...options,
 129 | 		database: testAdapter,
 130 | 	});
 131 | 
 132 | 	return (await auth.$context).adapter;
 133 | }
 134 | 
 135 | describe("Create Adapter Helper", async () => {
 136 | 	const adapterId = "test-adapter-id";
 137 | 	const adapter = await createTestAdapter({
 138 | 		config: {
 139 | 			adapterId,
 140 | 		},
 141 | 	});
 142 | 
 143 | 	test("Should have the correct adapter id", () => {
 144 | 		expect(adapter.id).toBe(adapterId);
 145 | 	});
 146 | 
 147 | 	test("Should use the id generator if passed into the betterAuth config", async () => {
 148 | 		const adapter = await createTestAdapter({
 149 | 			config: {
 150 | 				debugLogs: {},
 151 | 			},
 152 | 			options: {
 153 | 				advanced: {
 154 | 					database: {
 155 | 						generateId(options) {
 156 | 							return "HARD-CODED-ID";
 157 | 						},
 158 | 					},
 159 | 				},
 160 | 			},
 161 | 		});
 162 | 		const res = await adapter.create({
 163 | 			model: "user",
 164 | 			data: { name: "test-name" },
 165 | 		});
 166 | 		expect(res).toHaveProperty("id");
 167 | 		expect(res.id).toBe("HARD-CODED-ID");
 168 | 	});
 169 | 
 170 | 	test("Should not generate an id if `advanced.database.generateId` is not defined or false", async () => {
 171 | 		const adapter = await createTestAdapter({
 172 | 			config: {},
 173 | 			options: {
 174 | 				advanced: {
 175 | 					database: { generateId: false, useNumberId: false },
 176 | 				},
 177 | 			},
 178 | 			adapter(args_0) {
 179 | 				return {
 180 | 					async create(data) {
 181 | 						expect(data.data.id).not.toBeDefined();
 182 | 						return data.data;
 183 | 					},
 184 | 				};
 185 | 			},
 186 | 		});
 187 | 
 188 | 		const testResult = await adapter.create({
 189 | 			model: "user",
 190 | 			data: { name: "test-name" },
 191 | 		});
 192 | 		expect(testResult.id).not.toBeDefined();
 193 | 	});
 194 | 
 195 | 	test("Should throw an error if the database doesn't support numeric ids and the user has enabled `useNumberId`", async () => {
 196 | 		let error: any | null = null;
 197 | 		try {
 198 | 			await createTestAdapter({
 199 | 				config: {
 200 | 					supportsNumericIds: false,
 201 | 				},
 202 | 				options: {
 203 | 					advanced: {
 204 | 						database: {
 205 | 							useNumberId: true,
 206 | 						},
 207 | 					},
 208 | 				},
 209 | 			});
 210 | 		} catch (err) {
 211 | 			error = err;
 212 | 		}
 213 | 		expect(error).not.toBeNull();
 214 | 	});
 215 | 
 216 | 	describe("Checking for the results of an adapter call, as well as the parameters passed into the adapter call", () => {
 217 | 		describe("create", () => {
 218 | 			test("Should fill in the missing fields in the result", async () => {
 219 | 				const res = await adapter.create({
 220 | 					model: "user",
 221 | 					data: { name: "test-name" },
 222 | 				});
 223 | 				expect(res).toHaveProperty("id");
 224 | 				expect(res).toHaveProperty("name");
 225 | 				expect(res).toHaveProperty("email");
 226 | 				expect(res).toHaveProperty("emailVerified");
 227 | 				expect(res).toHaveProperty("image");
 228 | 				expect(res).toHaveProperty("createdAt");
 229 | 				expect(res).toHaveProperty("updatedAt");
 230 | 				expect(res?.emailVerified).toEqual(false);
 231 | 				expect(res?.name).toEqual("test-name");
 232 | 				expect(res?.email).toEqual(undefined);
 233 | 				expect(res?.image).toEqual(undefined);
 234 | 				expect(res?.createdAt).toBeInstanceOf(Date);
 235 | 				expect(res?.updatedAt).toBeInstanceOf(Date);
 236 | 			});
 237 | 
 238 | 			test("should not return string for nullable foreign keys", async () => {
 239 | 				const adapter = await createTestAdapter({
 240 | 					config: {
 241 | 						debugLogs: {},
 242 | 					},
 243 | 					options: {
 244 | 						plugins: [
 245 | 							{
 246 | 								id: "test",
 247 | 								schema: {
 248 | 									testModel: {
 249 | 										fields: {
 250 | 											nullableReference: {
 251 | 												type: "string",
 252 | 												references: { field: "id", model: "user" },
 253 | 												required: false,
 254 | 											},
 255 | 										},
 256 | 									},
 257 | 								},
 258 | 							},
 259 | 						],
 260 | 					},
 261 | 				});
 262 | 				const res = await adapter.create({
 263 | 					model: "testModel",
 264 | 					data: { nullableReference: null },
 265 | 				});
 266 | 				expect(res).toHaveProperty("nullableReference");
 267 | 				expect(res.nullableReference).toBeNull();
 268 | 
 269 | 				const adapter2 = await createTestAdapter({
 270 | 					config: {
 271 | 						debugLogs: {},
 272 | 					},
 273 | 					options: {
 274 | 						plugins: [
 275 | 							{
 276 | 								id: "test",
 277 | 								schema: {
 278 | 									testModel: {
 279 | 										fields: {
 280 | 											nullableReference: {
 281 | 												type: "string",
 282 | 												references: { field: "id", model: "user" },
 283 | 												required: false,
 284 | 											},
 285 | 										},
 286 | 									},
 287 | 								},
 288 | 							},
 289 | 						],
 290 | 						advanced: {
 291 | 							database: {
 292 | 								useNumberId: true,
 293 | 							},
 294 | 						},
 295 | 					},
 296 | 				});
 297 | 				const res2 = await adapter2.create({
 298 | 					model: "testModel",
 299 | 					data: { nullableReference: null },
 300 | 				});
 301 | 				expect(res2).toHaveProperty("nullableReference");
 302 | 				expect(res2.nullableReference).toBeNull();
 303 | 			});
 304 | 
 305 | 			test('Should include an "id" in the result in all cases, unless "select" is used to exclude it', async () => {
 306 | 				const res = await adapter.create({
 307 | 					model: "user",
 308 | 					data: { name: "test-name" },
 309 | 				});
 310 | 				expect(res).toHaveProperty("id");
 311 | 				expect(typeof res?.id).toEqual("string");
 312 | 
 313 | 				const adapterWithoutIdGeneration = await createTestAdapter({
 314 | 					config: {
 315 | 						disableIdGeneration: true,
 316 | 						debugLogs: {},
 317 | 					},
 318 | 				});
 319 | 				const res2 = await adapterWithoutIdGeneration.create({
 320 | 					model: "user",
 321 | 					data: { name: "test-name" },
 322 | 				});
 323 | 				// Id will still be present, due to the transformOutput function. However it will be undefined, vvvvv
 324 | 				expect(res2).toHaveProperty("id");
 325 | 				expect(typeof res2?.id).toEqual("undefined");
 326 | 				// In a real case, the `id` should always be present
 327 | 
 328 | 				const res3 = await adapter.create({
 329 | 					model: "user",
 330 | 					data: { name: "test-name" },
 331 | 					select: ["name"],
 332 | 				});
 333 | 				expect(res3).toHaveProperty("name");
 334 | 				expect(res3).not.toHaveProperty("id");
 335 | 			});
 336 | 
 337 | 			test('Should receive a generated id during the call, unless "disableIdGeneration" is set to true', async () => {
 338 | 				const createWithId: { id: unknown } = await new Promise(async (r) => {
 339 | 					const adapter = await createTestAdapter({
 340 | 						adapter(args_0) {
 341 | 							return {
 342 | 								async create({ data, model, select }) {
 343 | 									r(data as any);
 344 | 									return data;
 345 | 								},
 346 | 							};
 347 | 						},
 348 | 					});
 349 | 					adapter.create({
 350 | 						model: "user",
 351 | 						data: { name: "test-name" },
 352 | 					});
 353 | 				});
 354 | 
 355 | 				expect(createWithId).toBeDefined();
 356 | 				expect(createWithId.id).toBeDefined();
 357 | 				expect(typeof createWithId.id).toBe("string");
 358 | 
 359 | 				const createWithoutId: { id: unknown } = await new Promise(
 360 | 					async (r) => {
 361 | 						const adapter = await createTestAdapter({
 362 | 							config: {
 363 | 								disableIdGeneration: true,
 364 | 								debugLogs: {},
 365 | 							},
 366 | 							adapter(args_0) {
 367 | 								return {
 368 | 									async create({ data, model, select }) {
 369 | 										r(data as any);
 370 | 										return data;
 371 | 									},
 372 | 								};
 373 | 							},
 374 | 						});
 375 | 						adapter.create({
 376 | 							model: "user",
 377 | 							data: { name: "test-name" },
 378 | 						});
 379 | 					},
 380 | 				);
 381 | 
 382 | 				expect(createWithoutId).toBeDefined();
 383 | 				expect(createWithoutId.id).toBeUndefined();
 384 | 			});
 385 | 
 386 | 			test("Should not modify result null to string for id or fields referencing id", async () => {
 387 | 				const result: { id: string; testPluginField: string | null } =
 388 | 					await new Promise(async (r) => {
 389 | 						const adapter = await createTestAdapter({
 390 | 							adapter(args_0) {
 391 | 								return {
 392 | 									async create({ data, model, select }) {
 393 | 										return data;
 394 | 									},
 395 | 								};
 396 | 							},
 397 | 							options: {
 398 | 								plugins: [
 399 | 									{
 400 | 										id: "test-plugin-id",
 401 | 										schema: {
 402 | 											testPluginTable: {
 403 | 												fields: {
 404 | 													testPluginField: {
 405 | 														type: "string",
 406 | 														required: false,
 407 | 														references: {
 408 | 															model: "user",
 409 | 															field: "id",
 410 | 														},
 411 | 													},
 412 | 												},
 413 | 											},
 414 | 										},
 415 | 									},
 416 | 								],
 417 | 							},
 418 | 						});
 419 | 						r(
 420 | 							await adapter.create({
 421 | 								model: "testPluginTable",
 422 | 								data: {
 423 | 									testPluginField: null,
 424 | 								},
 425 | 							}),
 426 | 						);
 427 | 					});
 428 | 
 429 | 				expect(result.id).toBeTypeOf("string");
 430 | 				expect(result.testPluginField).toBeNull();
 431 | 			});
 432 | 
 433 | 			test("Should modify boolean type to 1 or 0 if the DB doesn't support it. And expect the result to be transformed back to boolean", async () => {
 434 | 				// Testing true
 435 | 				const createTRUEParameters: { data: { emailVerified: number } } =
 436 | 					await new Promise(async (r) => {
 437 | 						const adapter = await createTestAdapter({
 438 | 							config: {
 439 | 								supportsBooleans: false,
 440 | 							},
 441 | 							adapter(args_0) {
 442 | 								return {
 443 | 									async create(data) {
 444 | 										r(data as any);
 445 | 										return data.data;
 446 | 									},
 447 | 								};
 448 | 							},
 449 | 						});
 450 | 						const res = await adapter.create({
 451 | 							model: "user",
 452 | 							data: { emailVerified: true },
 453 | 						});
 454 | 						expect(res).toHaveProperty("emailVerified");
 455 | 						expect(res.emailVerified).toBe(true);
 456 | 					});
 457 | 				expect(createTRUEParameters.data).toHaveProperty("emailVerified");
 458 | 				expect(createTRUEParameters.data.emailVerified).toBe(1);
 459 | 
 460 | 				// Testing false
 461 | 				const createFALSEParameters: { data: { emailVerified: number } } =
 462 | 					await new Promise(async (r) => {
 463 | 						const adapter = await createTestAdapter({
 464 | 							config: {
 465 | 								supportsBooleans: false,
 466 | 							},
 467 | 							adapter(args_0) {
 468 | 								return {
 469 | 									async create(data) {
 470 | 										r(data as any);
 471 | 										return data.data;
 472 | 									},
 473 | 								};
 474 | 							},
 475 | 						});
 476 | 						const res = await adapter.create({
 477 | 							model: "user",
 478 | 							data: { emailVerified: false },
 479 | 						});
 480 | 						expect(res).toHaveProperty("emailVerified");
 481 | 						expect(res.emailVerified).toBe(false);
 482 | 					});
 483 | 				expect(createFALSEParameters.data).toHaveProperty("emailVerified");
 484 | 				expect(createFALSEParameters.data.emailVerified).toBe(0);
 485 | 			});
 486 | 
 487 | 			test("Should modify JSON type to TEXT if the DB doesn't support it. And expect the result to be transformed back to JSON", async () => {
 488 | 				const createJSONParameters: { data: { preferences: string } } =
 489 | 					await new Promise(async (r) => {
 490 | 						const adapter = await createTestAdapter({
 491 | 							config: {
 492 | 								supportsJSON: false,
 493 | 							},
 494 | 							options: {
 495 | 								user: {
 496 | 									additionalFields: {
 497 | 										preferences: {
 498 | 											type: "json",
 499 | 										},
 500 | 									},
 501 | 								},
 502 | 							},
 503 | 							adapter(args_0) {
 504 | 								return {
 505 | 									async create(data) {
 506 | 										r(data as any);
 507 | 										return data.data;
 508 | 									},
 509 | 								};
 510 | 							},
 511 | 						});
 512 | 						const obj = { preferences: { color: "blue", size: "large" } };
 513 | 						const res = await adapter.create({
 514 | 							model: "user",
 515 | 							data: obj,
 516 | 						});
 517 | 						expect(res).toHaveProperty("preferences");
 518 | 						expect(res.preferences).toEqual(obj.preferences);
 519 | 					});
 520 | 				expect(createJSONParameters.data).toHaveProperty("preferences");
 521 | 				expect(createJSONParameters.data.preferences).toEqual(
 522 | 					'{"color":"blue","size":"large"}',
 523 | 				);
 524 | 			});
 525 | 
 526 | 			test("Should modify date type to TEXT if the DB doesn't support it. And expect the result to be transformed back to date", async () => {
 527 | 				const testDate = new Date();
 528 | 				const createDateParameters: { data: { createdAt: string } } =
 529 | 					await new Promise(async (r) => {
 530 | 						const adapter = await createTestAdapter({
 531 | 							config: {
 532 | 								supportsDates: false,
 533 | 							},
 534 | 							adapter(args_0) {
 535 | 								return {
 536 | 									async create(data) {
 537 | 										r(data as any);
 538 | 										return data.data;
 539 | 									},
 540 | 								};
 541 | 							},
 542 | 						});
 543 | 						const res = await adapter.create({
 544 | 							model: "user",
 545 | 							data: { createdAt: testDate },
 546 | 						});
 547 | 						expect(res).toHaveProperty("createdAt");
 548 | 						expect(res.createdAt).toBeInstanceOf(Date);
 549 | 					});
 550 | 				expect(createDateParameters.data).toHaveProperty("createdAt");
 551 | 				expect(createDateParameters.data.createdAt).toEqual(
 552 | 					testDate.toISOString(),
 553 | 				);
 554 | 			});
 555 | 
 556 | 			test("Should allow custom transform input", async () => {
 557 | 				const createCustomTransformInputParameters: { data: { name: string } } =
 558 | 					await new Promise(async (r) => {
 559 | 						const adapter = await createTestAdapter({
 560 | 							config: {
 561 | 								debugLogs: {},
 562 | 								customTransformInput({ field, data }) {
 563 | 									if (field === "name") {
 564 | 										return data.toUpperCase();
 565 | 									}
 566 | 									return data;
 567 | 								},
 568 | 							},
 569 | 							adapter(args_0) {
 570 | 								return {
 571 | 									async create(data) {
 572 | 										r(data as any);
 573 | 										return data.data;
 574 | 									},
 575 | 								};
 576 | 							},
 577 | 						});
 578 | 						const res = await adapter.create({
 579 | 							model: "user",
 580 | 							data: { name: "test-name" },
 581 | 						});
 582 | 						expect(res).toHaveProperty("name");
 583 | 						expect(res.name).toEqual("TEST-NAME");
 584 | 					});
 585 | 				expect(createCustomTransformInputParameters.data).toHaveProperty(
 586 | 					"name",
 587 | 				);
 588 | 				expect(createCustomTransformInputParameters.data.name).toEqual(
 589 | 					"TEST-NAME",
 590 | 				);
 591 | 			});
 592 | 
 593 | 			test("Should allow custom transform output", async () => {
 594 | 				const createCustomTransformOutputParameters: {
 595 | 					data: { name: string };
 596 | 				} = await new Promise(async (r) => {
 597 | 					const adapter = await createTestAdapter({
 598 | 						config: {
 599 | 							debugLogs: {},
 600 | 							customTransformOutput({ field, data }) {
 601 | 								if (field === "name") {
 602 | 									return data.toLowerCase();
 603 | 								}
 604 | 								return data;
 605 | 							},
 606 | 						},
 607 | 						adapter(args_0) {
 608 | 							return {
 609 | 								async create(data) {
 610 | 									r(data as any);
 611 | 									return data.data;
 612 | 								},
 613 | 							};
 614 | 						},
 615 | 					});
 616 | 					const res = await adapter.create({
 617 | 						model: "user",
 618 | 						data: { name: "TEST-NAME" },
 619 | 					});
 620 | 					expect(res).toHaveProperty("name");
 621 | 					expect(res.name).toEqual("test-name");
 622 | 				});
 623 | 				expect(createCustomTransformOutputParameters.data).toHaveProperty(
 624 | 					"name",
 625 | 				);
 626 | 				expect(createCustomTransformOutputParameters.data.name).toEqual(
 627 | 					"TEST-NAME", // Remains the same as the input because we're only transforming the output
 628 | 				);
 629 | 			});
 630 | 
 631 | 			test("Should allow custom transform input and output", async () => {
 632 | 				const createCustomTransformInputAndOutputParameters: {
 633 | 					data: { name: string };
 634 | 				} = await new Promise(async (r) => {
 635 | 					const adapter = await createTestAdapter({
 636 | 						config: {
 637 | 							debugLogs: {},
 638 | 							customTransformInput({ field, data }) {
 639 | 								if (field === "name") {
 640 | 									return data.toUpperCase();
 641 | 								}
 642 | 								return data;
 643 | 							},
 644 | 							customTransformOutput({ field, data }) {
 645 | 								if (field === "name") {
 646 | 									return data.toLowerCase();
 647 | 								}
 648 | 								return data;
 649 | 							},
 650 | 						},
 651 | 						adapter(args_0) {
 652 | 							return {
 653 | 								async create(data) {
 654 | 									r(data as any);
 655 | 									return data.data;
 656 | 								},
 657 | 							};
 658 | 						},
 659 | 					});
 660 | 					const res = await adapter.create({
 661 | 						model: "user",
 662 | 						data: { name: "TEST-NAME" },
 663 | 					});
 664 | 					expect(res).toHaveProperty("name");
 665 | 					expect(res.name).toEqual("test-name");
 666 | 				});
 667 | 				expect(
 668 | 					createCustomTransformInputAndOutputParameters.data,
 669 | 				).toHaveProperty("name");
 670 | 				expect(createCustomTransformInputAndOutputParameters.data.name).toEqual(
 671 | 					"TEST-NAME",
 672 | 				);
 673 | 			});
 674 | 
 675 | 			test("Should allow custom map input key transformation", async () => {
 676 | 				const parameters: {
 677 | 					data: { email_address: string };
 678 | 				} = await new Promise(async (r) => {
 679 | 					const adapter = await createTestAdapter({
 680 | 						config: {
 681 | 							debugLogs: {},
 682 | 							mapKeysTransformInput: {
 683 | 								email: "email_address",
 684 | 							},
 685 | 						},
 686 | 						adapter(args_0) {
 687 | 							return {
 688 | 								async create(data) {
 689 | 									r(data as any);
 690 | 									return data.data;
 691 | 								},
 692 | 							};
 693 | 						},
 694 | 					});
 695 | 
 696 | 					const res = (await adapter.create({
 697 | 						model: "user",
 698 | 						data: { email: "[email protected]" },
 699 | 					})) as { email: string };
 700 | 
 701 | 					expect(res).toHaveProperty("email");
 702 | 					expect(res).not.toHaveProperty("email_address");
 703 | 					expect(res.email).toEqual(undefined); // The reason it's undefined is because we did transform `email` to `email_address`, however we never transformed `email_address` back to `email`.
 704 | 				});
 705 | 				expect(parameters.data).toHaveProperty("email_address");
 706 | 				expect(parameters.data.email_address).toEqual("[email protected]");
 707 | 			});
 708 | 
 709 | 			test("Should allow custom transform input to transform the where clause", async () => {
 710 | 				const parameters: CleanedWhere[] = await new Promise(async (r) => {
 711 | 					const adapter = await createTestAdapter({
 712 | 						config: {
 713 | 							debugLogs: {},
 714 | 							mapKeysTransformInput: {
 715 | 								id: "_id",
 716 | 							},
 717 | 						},
 718 | 						adapter(args_0) {
 719 | 							return {
 720 | 								async findOne({ model, where, select }) {
 721 | 									r(where);
 722 | 									return {} as any;
 723 | 								},
 724 | 							};
 725 | 						},
 726 | 					});
 727 | 					adapter.findOne({
 728 | 						model: "user",
 729 | 						where: [{ field: "id", value: "123" }],
 730 | 					});
 731 | 				});
 732 | 
 733 | 				expect(parameters[0]!.field).toEqual("_id");
 734 | 			});
 735 | 
 736 | 			test("Should allow custom map output key transformation", async () => {
 737 | 				const parameters: {
 738 | 					data: { email: string };
 739 | 				} = await new Promise(async (r) => {
 740 | 					const adapter = await createTestAdapter({
 741 | 						config: {
 742 | 							debugLogs: {},
 743 | 							mapKeysTransformOutput: {
 744 | 								email: "wrong_email_key",
 745 | 							},
 746 | 						},
 747 | 
 748 | 						adapter(args_0) {
 749 | 							return {
 750 | 								async create(data) {
 751 | 									r(data as any);
 752 | 									return data.data;
 753 | 								},
 754 | 							};
 755 | 						},
 756 | 					});
 757 | 					const res = (await adapter.create({
 758 | 						model: "user",
 759 | 						data: { email: "[email protected]" },
 760 | 					})) as { wrong_email_key: string };
 761 | 					// Even though we're using the output key transformation, we still don't actually get the key transformation we want.
 762 | 					// This is because the output is also parsed against the schema, and the `wrong_email_key` key is not in the schema.
 763 | 					expect(res).toHaveProperty("wrong_email_key");
 764 | 					expect(res).not.toHaveProperty("email");
 765 | 					expect(res.wrong_email_key).toEqual("[email protected]");
 766 | 				});
 767 | 
 768 | 				expect(parameters.data).toHaveProperty("email");
 769 | 				expect(parameters.data.email).toEqual("[email protected]");
 770 | 			});
 771 | 
 772 | 			test("Should allow custom map input and output key transformation", async () => {
 773 | 				const parameters: {
 774 | 					data: { email_address: string };
 775 | 				} = await new Promise(async (r) => {
 776 | 					const adapter = await createTestAdapter({
 777 | 						config: {
 778 | 							debugLogs: {},
 779 | 							mapKeysTransformInput: {
 780 | 								email: "email_address",
 781 | 							},
 782 | 							mapKeysTransformOutput: {
 783 | 								email_address: "email",
 784 | 							},
 785 | 						},
 786 | 						adapter(args_0) {
 787 | 							return {
 788 | 								async create(data) {
 789 | 									r(data as any);
 790 | 									return data.data;
 791 | 								},
 792 | 							};
 793 | 						},
 794 | 					});
 795 | 					const res = await adapter.create({
 796 | 						model: "user",
 797 | 						data: { email: "[email protected]" },
 798 | 					});
 799 | 					expect(res).toHaveProperty("email");
 800 | 					expect(res).not.toHaveProperty("email_address");
 801 | 					expect(res.email).toEqual("[email protected]");
 802 | 				});
 803 | 				expect(parameters.data).toHaveProperty("email_address");
 804 | 				expect(parameters.data).not.toHaveProperty("email");
 805 | 				expect(parameters.data.email_address).toEqual("[email protected]");
 806 | 			});
 807 | 
 808 | 			test("Should expect the fields to be transformed into the correct field names if customized", async () => {
 809 | 				const parameters: { data: any; select?: string[]; model: string } =
 810 | 					await new Promise(async (r) => {
 811 | 						const adapter = await createTestAdapter({
 812 | 							config: {
 813 | 								debugLogs: {},
 814 | 							},
 815 | 							options: {
 816 | 								user: {
 817 | 									fields: {
 818 | 										email: "email_address",
 819 | 									},
 820 | 								},
 821 | 							},
 822 | 							adapter(args_0) {
 823 | 								return {
 824 | 									async create(data) {
 825 | 										r(data as any);
 826 | 										return data.data;
 827 | 									},
 828 | 								};
 829 | 							},
 830 | 						});
 831 | 						const res = await adapter.create({
 832 | 							model: "user",
 833 | 							data: { email: "[email protected]" },
 834 | 						});
 835 | 						expect(res).toHaveProperty("email");
 836 | 						expect(res).not.toHaveProperty("email_address");
 837 | 						expect(res.email).toEqual("[email protected]");
 838 | 					});
 839 | 				expect(parameters).toHaveProperty("data");
 840 | 				expect(parameters.data).toHaveProperty("email_address");
 841 | 				expect(parameters.data).not.toHaveProperty("email");
 842 | 				expect(parameters.data.email_address).toEqual("[email protected]");
 843 | 			});
 844 | 
 845 | 			test("Should expect the model to be transformed into the correct model name if customized", async () => {
 846 | 				const parameters: { data: any; select?: string[]; model: string } =
 847 | 					await new Promise(async (r) => {
 848 | 						const adapter = await createTestAdapter({
 849 | 							config: {
 850 | 								debugLogs: {},
 851 | 							},
 852 | 							options: {
 853 | 								user: {
 854 | 									modelName: "user_table",
 855 | 								},
 856 | 							},
 857 | 							adapter(args_0) {
 858 | 								return {
 859 | 									async create(data) {
 860 | 										r(data as any);
 861 | 										return data.data;
 862 | 									},
 863 | 								};
 864 | 							},
 865 | 						});
 866 | 						const res = await adapter.create({
 867 | 							model: "user",
 868 | 							data: { email: "[email protected]" },
 869 | 						});
 870 | 						expect(res).toHaveProperty("id");
 871 | 						expect(res).toHaveProperty("email");
 872 | 					});
 873 | 				expect(parameters).toHaveProperty("model");
 874 | 				expect(parameters.model).toEqual("user_table");
 875 | 			});
 876 | 
 877 | 			test("Should expect the result to follow the schema", async () => {
 878 | 				const parameters: { data: any; select?: string[]; model: string } =
 879 | 					await new Promise(async (r) => {
 880 | 						const adapter = await createTestAdapter({
 881 | 							config: {
 882 | 								debugLogs: {},
 883 | 							},
 884 | 							options: {
 885 | 								user: {
 886 | 									fields: {
 887 | 										email: "email_address",
 888 | 									},
 889 | 								},
 890 | 							},
 891 | 							adapter(args_0) {
 892 | 								return {
 893 | 									async create(data) {
 894 | 										r(data as any);
 895 | 										return data.data;
 896 | 									},
 897 | 								};
 898 | 							},
 899 | 						});
 900 | 						const res = await adapter.create({
 901 | 							model: "user",
 902 | 							data: { email: "[email protected]" },
 903 | 						});
 904 | 						expect(res).toHaveProperty("email");
 905 | 						expect(res).toHaveProperty("id");
 906 | 						expect(res).toHaveProperty("createdAt");
 907 | 						expect(res).toHaveProperty("updatedAt");
 908 | 						expect(res).toHaveProperty("name");
 909 | 						expect(res).toHaveProperty("emailVerified");
 910 | 						expect(res).toHaveProperty("image");
 911 | 						expect(res).not.toHaveProperty("email_address");
 912 | 					});
 913 | 				expect(parameters).toHaveProperty("data");
 914 | 				expect(parameters.data).toHaveProperty("email_address");
 915 | 				expect(parameters.data).not.toHaveProperty("email");
 916 | 				expect(parameters.data.email_address).toEqual("[email protected]");
 917 | 			});
 918 | 
 919 | 			test("Should expect the result to respect the select fields", async () => {
 920 | 				const adapter = await createTestAdapter({
 921 | 					config: {
 922 | 						debugLogs: {},
 923 | 					},
 924 | 					options: {
 925 | 						user: {
 926 | 							fields: {
 927 | 								email: "email_address",
 928 | 							},
 929 | 						},
 930 | 					},
 931 | 				});
 932 | 				const res = await adapter.create({
 933 | 					model: "user",
 934 | 					data: {
 935 | 						email: "[email protected]",
 936 | 						name: "test-name",
 937 | 						emailVerified: false,
 938 | 						image: "test-image",
 939 | 					},
 940 | 					select: ["email"],
 941 | 				});
 942 | 				expect(res).toHaveProperty("email");
 943 | 				expect(res).not.toHaveProperty("name");
 944 | 				expect(res).not.toHaveProperty("emailVerified");
 945 | 				expect(res).not.toHaveProperty("image");
 946 | 				expect(res).toMatchSnapshot();
 947 | 			});
 948 | 		});
 949 | 
 950 | 		describe("update", () => {
 951 | 			test("Should fill in the missing fields in the result", async () => {
 952 | 				const user: { id: string; name: string } = await adapter.create({
 953 | 					model: "user",
 954 | 					data: { name: "test-name" },
 955 | 				});
 956 | 				const res = await adapter.update({
 957 | 					model: "user",
 958 | 					where: [{ field: "id", value: user.id }],
 959 | 					update: { name: "test-name-2" },
 960 | 				});
 961 | 				expect(res).toHaveProperty("id");
 962 | 				expect(res).toHaveProperty("name");
 963 | 				expect(res).toHaveProperty("email");
 964 | 				expect(res).toHaveProperty("emailVerified");
 965 | 				expect(res).toHaveProperty("image");
 966 | 				expect(res).toHaveProperty("createdAt");
 967 | 				expect(res).toHaveProperty("updatedAt");
 968 | 			});
 969 | 
 970 | 			test(`Should include an "id" in the result in all cases`, async () => {
 971 | 				const user: { id: string; name: string } = await adapter.create({
 972 | 					model: "user",
 973 | 					data: { name: "test-name" },
 974 | 				});
 975 | 				const res: { id: string } | null = await adapter.update({
 976 | 					model: "user",
 977 | 					where: [{ field: "id", value: user.id }],
 978 | 					update: { name: "test-name-2" },
 979 | 				});
 980 | 				expect(res).toHaveProperty("id");
 981 | 			});
 982 | 
 983 | 			test("Should modify boolean type to 1 or 0 if the DB doesn't support it. And expect the result to be transformed back to boolean", async () => {
 984 | 				// Testing true
 985 | 				const updateTRUEParameters: { update: { emailVerified: number } } =
 986 | 					await new Promise(async (r) => {
 987 | 						const adapter = await createTestAdapter({
 988 | 							config: {
 989 | 								supportsBooleans: false,
 990 | 							},
 991 | 							adapter(args_0) {
 992 | 								return {
 993 | 									async update(data) {
 994 | 										r(data as any);
 995 | 										return data.update;
 996 | 									},
 997 | 								};
 998 | 							},
 999 | 						});
1000 | 						const user: { emailVerified: boolean; id: string } =
1001 | 							await adapter.create({
1002 | 								model: "user",
1003 | 								data: { emailVerified: false },
1004 | 							});
1005 | 						const res = await adapter.update({
1006 | 							model: "user",
1007 | 							where: [{ field: "id", value: user.id }],
1008 | 							update: { emailVerified: true },
1009 | 						});
1010 | 						expect(res).toHaveProperty("emailVerified");
1011 | 						//@ts-expect-error
1012 | 						expect(res.emailVerified).toBe(true);
1013 | 					});
1014 | 				expect(updateTRUEParameters.update).toHaveProperty("emailVerified");
1015 | 				expect(updateTRUEParameters.update.emailVerified).toBe(1);
1016 | 
1017 | 				// Testing false
1018 | 				const createFALSEParameters: { update: { emailVerified: number } } =
1019 | 					await new Promise(async (r) => {
1020 | 						const adapter = await createTestAdapter({
1021 | 							config: {
1022 | 								supportsBooleans: false,
1023 | 							},
1024 | 							adapter(args_0) {
1025 | 								return {
1026 | 									async update(data) {
1027 | 										r(data as any);
1028 | 										return data.update;
1029 | 									},
1030 | 								};
1031 | 							},
1032 | 						});
1033 | 						const user: { emailVerified: boolean; id: string } =
1034 | 							await adapter.create({
1035 | 								model: "user",
1036 | 								data: { emailVerified: true },
1037 | 							});
1038 | 						const res = await adapter.update({
1039 | 							model: "user",
1040 | 							where: [{ field: "id", value: user.id }],
1041 | 							update: { emailVerified: false },
1042 | 						});
1043 | 						expect(res).toHaveProperty("emailVerified");
1044 | 						//@ts-expect-error
1045 | 						expect(res.emailVerified).toBe(false);
1046 | 					});
1047 | 				expect(createFALSEParameters.update).toHaveProperty("emailVerified");
1048 | 				expect(createFALSEParameters.update.emailVerified).toBe(0);
1049 | 			});
1050 | 
1051 | 			test("Should modify JSON type to TEXT if the DB doesn't support it. And expect the result to be transformed back to JSON", async () => {
1052 | 				const createJSONParameters: { update: { preferences: string } } =
1053 | 					await new Promise(async (r) => {
1054 | 						const adapter = await createTestAdapter({
1055 | 							config: {
1056 | 								supportsJSON: false,
1057 | 							},
1058 | 							options: {
1059 | 								user: {
1060 | 									additionalFields: {
1061 | 										preferences: {
1062 | 											type: "json",
1063 | 										},
1064 | 									},
1065 | 								},
1066 | 							},
1067 | 							adapter(args_0) {
1068 | 								return {
1069 | 									async update(data) {
1070 | 										r(data as any);
1071 | 										return data.update;
1072 | 									},
1073 | 								};
1074 | 							},
1075 | 						});
1076 | 						const obj = { preferences: { color: "blue", size: "large" } };
1077 | 						const user: { email: string; id: string } = await adapter.create({
1078 | 							model: "user",
1079 | 							data: { email: "[email protected]" },
1080 | 						});
1081 | 						const res: typeof obj | null = await adapter.update({
1082 | 							model: "user",
1083 | 							where: [{ field: "id", value: user.id }],
1084 | 							update: { preferences: obj.preferences },
1085 | 						});
1086 | 						expect(res).toHaveProperty("preferences");
1087 | 						expect(res?.preferences).toEqual(obj.preferences);
1088 | 					});
1089 | 				expect(createJSONParameters.update).toHaveProperty("preferences");
1090 | 				expect(createJSONParameters.update.preferences).toEqual(
1091 | 					'{"color":"blue","size":"large"}',
1092 | 				);
1093 | 			});
1094 | 
1095 | 			test("Should modify date type to TEXT if the DB doesn't support it. And expect the result to be transformed back to date", async () => {
1096 | 				const testDate = new Date();
1097 | 				const createDateParameters: { update: { createdAt: string } } =
1098 | 					await new Promise(async (r) => {
1099 | 						const adapter = await createTestAdapter({
1100 | 							config: {
1101 | 								supportsDates: false,
1102 | 							},
1103 | 							adapter(args_0) {
1104 | 								return {
1105 | 									async update(data) {
1106 | 										r(data as any);
1107 | 										return data.update;
1108 | 									},
1109 | 								};
1110 | 							},
1111 | 						});
1112 | 						const user: { email: string; id: string } = await adapter.create({
1113 | 							model: "user",
1114 | 							data: { email: "[email protected]" },
1115 | 						});
1116 | 						const res: { createdAt: Date } | null = await adapter.update({
1117 | 							model: "user",
1118 | 							where: [{ field: "id", value: user.id }],
1119 | 							update: { createdAt: testDate },
1120 | 						});
1121 | 						expect(res).toHaveProperty("createdAt");
1122 | 						expect(res?.createdAt).toBeInstanceOf(Date);
1123 | 					});
1124 | 				expect(createDateParameters.update).toHaveProperty("createdAt");
1125 | 				expect(createDateParameters.update.createdAt).toEqual(
1126 | 					testDate.toISOString(),
1127 | 				);
1128 | 			});
1129 | 
1130 | 			test("Should allow custom transform input", async () => {
1131 | 				const createCustomTransformInputParameters: {
1132 | 					update: { name: string };
1133 | 				} = await new Promise(async (r) => {
1134 | 					const adapter = await createTestAdapter({
1135 | 						config: {
1136 | 							customTransformInput({ field, data }) {
1137 | 								if (field === "name") {
1138 | 									return data.toUpperCase();
1139 | 								}
1140 | 								return data;
1141 | 							},
1142 | 						},
1143 | 						adapter(args_0) {
1144 | 							return {
1145 | 								async update(data) {
1146 | 									r(data as any);
1147 | 									return data.update;
1148 | 								},
1149 | 							};
1150 | 						},
1151 | 					});
1152 | 					const user: { id: string; name: string } = await adapter.create({
1153 | 						model: "user",
1154 | 						data: { name: "test-name" },
1155 | 					});
1156 | 					const res: { name: string } | null = await adapter.update({
1157 | 						model: "user",
1158 | 						where: [{ field: "id", value: user.id }],
1159 | 						update: { name: "test-name-2" },
1160 | 					});
1161 | 					expect(res).toHaveProperty("name");
1162 | 					expect(res?.name).toEqual("TEST-NAME-2");
1163 | 				});
1164 | 				expect(createCustomTransformInputParameters.update).toHaveProperty(
1165 | 					"name",
1166 | 				);
1167 | 				expect(createCustomTransformInputParameters.update.name).toEqual(
1168 | 					"TEST-NAME-2",
1169 | 				);
1170 | 			});
1171 | 
1172 | 			test("Should allow custom transform output", async () => {
1173 | 				const createCustomTransformOutputParameters: {
1174 | 					update: { name: string };
1175 | 				} = await new Promise(async (r) => {
1176 | 					const adapter = await createTestAdapter({
1177 | 						config: {
1178 | 							customTransformOutput({ field, data }) {
1179 | 								if (field === "name") {
1180 | 									return data.toLowerCase();
1181 | 								}
1182 | 								return data;
1183 | 							},
1184 | 						},
1185 | 						adapter(args_0) {
1186 | 							return {
1187 | 								async update(data) {
1188 | 									r(data as any);
1189 | 									return data.update;
1190 | 								},
1191 | 							};
1192 | 						},
1193 | 					});
1194 | 					const user: { id: string; name: string } = await adapter.create({
1195 | 						model: "user",
1196 | 						data: { name: "TEST-NAME" },
1197 | 					});
1198 | 					const res: { name: string } | null = await adapter.update({
1199 | 						model: "user",
1200 | 						where: [{ field: "id", value: user.id }],
1201 | 						update: { name: "test-name-2" },
1202 | 					});
1203 | 					expect(res).toHaveProperty("name");
1204 | 					expect(res?.name).toEqual("test-name-2");
1205 | 				});
1206 | 				expect(createCustomTransformOutputParameters.update).toHaveProperty(
1207 | 					"name",
1208 | 				);
1209 | 				expect(createCustomTransformOutputParameters.update.name).toEqual(
1210 | 					"test-name-2",
1211 | 				);
1212 | 			});
1213 | 
1214 | 			test("Should allow custom transform input and output", async () => {
1215 | 				const createCustomTransformInputAndOutputParameters: {
1216 | 					update: { name: string };
1217 | 				} = await new Promise(async (r) => {
1218 | 					const adapter = await createTestAdapter({
1219 | 						config: {
1220 | 							customTransformInput({ field, data }) {
1221 | 								if (field === "name") {
1222 | 									return data.toUpperCase();
1223 | 								}
1224 | 								return data;
1225 | 							},
1226 | 							customTransformOutput({ field, data }) {
1227 | 								if (field === "name") {
1228 | 									return data.toLowerCase();
1229 | 								}
1230 | 								return data;
1231 | 							},
1232 | 						},
1233 | 						adapter(args_0) {
1234 | 							return {
1235 | 								async update(data) {
1236 | 									r(data as any);
1237 | 									return data.update;
1238 | 								},
1239 | 							};
1240 | 						},
1241 | 					});
1242 | 					const user: { id: string; name: string } = await adapter.create({
1243 | 						model: "user",
1244 | 						data: { name: "test-name" },
1245 | 					});
1246 | 					const res: { name: string } | null = await adapter.update({
1247 | 						model: "user",
1248 | 						where: [{ field: "id", value: user.id }],
1249 | 						update: { name: "test-name-2" },
1250 | 					});
1251 | 					expect(res).toHaveProperty("name");
1252 | 					expect(res?.name).toEqual("test-name-2");
1253 | 				});
1254 | 				expect(
1255 | 					createCustomTransformInputAndOutputParameters.update,
1256 | 				).toHaveProperty("name");
1257 | 				expect(
1258 | 					createCustomTransformInputAndOutputParameters.update.name,
1259 | 				).toEqual("test-name-2".toUpperCase());
1260 | 			});
1261 | 
1262 | 			test("Should allow custom map input key transformation", async () => {
1263 | 				const parameters: {
1264 | 					update: { email_address: string };
1265 | 				} = await new Promise(async (r) => {
1266 | 					const adapter = await createTestAdapter({
1267 | 						config: {
1268 | 							debugLogs: {},
1269 | 							mapKeysTransformInput: {
1270 | 								email: "email_address",
1271 | 							},
1272 | 						},
1273 | 						adapter(args_0) {
1274 | 							return {
1275 | 								async update(data) {
1276 | 									r(data as any);
1277 | 									return data.update;
1278 | 								},
1279 | 							};
1280 | 						},
1281 | 					});
1282 | 					const user = (await adapter.create({
1283 | 						model: "user",
1284 | 						data: { email: "[email protected]" },
1285 | 					})) as { email: string; id: string };
1286 | 
1287 | 					const res: { email: string } | null = await adapter.update({
1288 | 						model: "user",
1289 | 						update: { email: "[email protected]" },
1290 | 						where: [{ field: "id", value: user.id }],
1291 | 					});
1292 | 
1293 | 					expect(res).toHaveProperty("email");
1294 | 					expect(res).not.toHaveProperty("email_address");
1295 | 					expect(res?.email).toEqual(undefined); // The reason it's undefined is because we did transform `email` to `email_address`, however we never transformed `email_address` back to `email`.
1296 | 				});
1297 | 				expect(parameters.update).toHaveProperty("email_address");
1298 | 				expect(parameters.update.email_address).toEqual("[email protected]");
1299 | 			});
1300 | 
1301 | 			test("Should allow custom map output key transformation", async () => {
1302 | 				const parameters: {
1303 | 					update: { email: string };
1304 | 				} = await new Promise(async (r) => {
1305 | 					const adapter = await createTestAdapter({
1306 | 						config: {
1307 | 							debugLogs: {},
1308 | 							mapKeysTransformOutput: {
1309 | 								email: "email_address",
1310 | 							},
1311 | 						},
1312 | 						adapter(args_0) {
1313 | 							return {
1314 | 								async update(data) {
1315 | 									r(data as any);
1316 | 									return data.update;
1317 | 								},
1318 | 							};
1319 | 						},
1320 | 					});
1321 | 					const user = (await adapter.create({
1322 | 						model: "user",
1323 | 						data: { email: "[email protected]" },
1324 | 					})) as { email: string; id: string };
1325 | 
1326 | 					const res: { email_address: string } | null = await adapter.update({
1327 | 						model: "user",
1328 | 						update: { email: "[email protected]" },
1329 | 						where: [{ field: "id", value: user.id }],
1330 | 					});
1331 | 
1332 | 					expect(res).toHaveProperty("email_address");
1333 | 					expect(res).not.toHaveProperty("email");
1334 | 					expect(res?.email_address).toEqual("[email protected]");
1335 | 				});
1336 | 				expect(parameters.update).toHaveProperty("email");
1337 | 				expect(parameters.update).not.toHaveProperty("email_address");
1338 | 				expect(parameters.update.email).toEqual("[email protected]");
1339 | 			});
1340 | 
1341 | 			test("Should allow custom map input and output key transformation", async () => {
1342 | 				const parameters: {
1343 | 					update: { email_address: string };
1344 | 				} = await new Promise(async (r) => {
1345 | 					const adapter = await createTestAdapter({
1346 | 						config: {
1347 | 							debugLogs: {},
1348 | 							mapKeysTransformInput: {
1349 | 								email: "email_address",
1350 | 							},
1351 | 							mapKeysTransformOutput: {
1352 | 								email_address: "email",
1353 | 							},
1354 | 						},
1355 | 						adapter(args_0) {
1356 | 							return {
1357 | 								async update(data) {
1358 | 									r(data as any);
1359 | 									return data.update;
1360 | 								},
1361 | 							};
1362 | 						},
1363 | 					});
1364 | 					const user = (await adapter.create({
1365 | 						model: "user",
1366 | 						data: { email: "[email protected]" },
1367 | 					})) as { email: string; id: string };
1368 | 
1369 | 					const res: { email: string } | null = await adapter.update({
1370 | 						model: "user",
1371 | 						update: { email: "[email protected]" },
1372 | 						where: [{ field: "id", value: user.id }],
1373 | 					});
1374 | 
1375 | 					expect(res).toHaveProperty("email");
1376 | 					expect(res).not.toHaveProperty("email_address");
1377 | 					expect(res?.email).toEqual("[email protected]");
1378 | 				});
1379 | 				expect(parameters.update).toHaveProperty("email_address");
1380 | 				expect(parameters.update).not.toHaveProperty("email");
1381 | 				expect(parameters.update.email_address).toEqual("[email protected]");
1382 | 			});
1383 | 
1384 | 			test("Should expect the fields to be transformed into the correct field names if customized", async () => {
1385 | 				const parameters: {
1386 | 					update: { email_address: string };
1387 | 				} = await new Promise(async (r) => {
1388 | 					const adapter = await createTestAdapter({
1389 | 						config: {
1390 | 							debugLogs: {},
1391 | 						},
1392 | 						options: {
1393 | 							user: {
1394 | 								fields: {
1395 | 									email: "email_address",
1396 | 								},
1397 | 							},
1398 | 						},
1399 | 						adapter(args_0) {
1400 | 							return {
1401 | 								async update(data) {
1402 | 									r(data as any);
1403 | 									return data.update;
1404 | 								},
1405 | 							};
1406 | 						},
1407 | 					});
1408 | 					const user: { id: string; email: string } = await adapter.create({
1409 | 						model: "user",
1410 | 						data: { email: "[email protected]" },
1411 | 					});
1412 | 					const res: { email: string } | null = await adapter.update({
1413 | 						model: "user",
1414 | 						update: { email: "[email protected]" },
1415 | 						where: [{ field: "id", value: user.id }],
1416 | 					});
1417 | 					expect(res).toHaveProperty("email");
1418 | 					expect(res).not.toHaveProperty("email_address");
1419 | 					expect(res?.email).toEqual("[email protected]");
1420 | 				});
1421 | 				expect(parameters.update).toHaveProperty("email_address");
1422 | 				expect(parameters.update).not.toHaveProperty("email");
1423 | 				expect(parameters.update.email_address).toEqual("[email protected]");
1424 | 			});
1425 | 
1426 | 			test("Should expect not to receive an id even if disableIdGeneration is false in an update call", async () => {
1427 | 				const parameters: {
1428 | 					update: { id: string };
1429 | 				} = await new Promise(async (r) => {
1430 | 					const adapter = await createTestAdapter({
1431 | 						config: {
1432 | 							disableIdGeneration: true,
1433 | 						},
1434 | 						adapter(args_0) {
1435 | 							return {
1436 | 								async update(data) {
1437 | 									r(data as any);
1438 | 									return data.update;
1439 | 								},
1440 | 							};
1441 | 						},
1442 | 					});
1443 | 					const user: { email: string; id: string } = await adapter.create({
1444 | 						model: "user",
1445 | 						data: { email: "[email protected]" },
1446 | 					});
1447 | 					await adapter.update({
1448 | 						model: "user",
1449 | 						update: { email: "[email protected]" },
1450 | 						where: [{ field: "id", value: user.id }],
1451 | 					});
1452 | 				});
1453 | 				expect(parameters.update).not.toHaveProperty("id");
1454 | 			});
1455 | 		});
1456 | 
1457 | 		describe("find", () => {
1458 | 			test("findOne: Should transform the where clause according to the schema", async () => {
1459 | 				const parameters: { where: Where[]; model: string; select?: string[] } =
1460 | 					await new Promise(async (r) => {
1461 | 						const adapter = await createTestAdapter({
1462 | 							options: {
1463 | 								user: {
1464 | 									fields: {
1465 | 										email: "email_address",
1466 | 									},
1467 | 								},
1468 | 							},
1469 | 							adapter(args_0) {
1470 | 								return {
1471 | 									async findOne({ model, where, select }) {
1472 | 										const fakeResult: Omit<User, "email"> & {
1473 | 											email_address: string;
1474 | 										} = {
1475 | 											id: "random-id-oudwduwbdouwbdu123b",
1476 | 											email_address: "[email protected]",
1477 | 											emailVerified: false,
1478 | 											createdAt: new Date(),
1479 | 											updatedAt: new Date(),
1480 | 											name: "test-name",
1481 | 										};
1482 | 										r({ model, where, select });
1483 | 										return fakeResult as any;
1484 | 									},
1485 | 								};
1486 | 							},
1487 | 						});
1488 | 						const res = await adapter.findOne<User>({
1489 | 							model: "user",
1490 | 							where: [{ field: "email", value: "[email protected]" }],
1491 | 						});
1492 | 						expect(res).not.toHaveProperty("email_address");
1493 | 						expect(res).toHaveProperty("email");
1494 | 						expect(res?.email).toEqual("[email protected]");
1495 | 					});
1496 | 				expect(parameters.where[0]!.field).toEqual("email_address");
1497 | 			});
1498 | 			test("findMany: Should transform the where clause according to the schema", async () => {
1499 | 				const parameters: { where: Where[] | undefined; model: string } =
1500 | 					await new Promise(async (r) => {
1501 | 						const adapter = await createTestAdapter({
1502 | 							options: {
1503 | 								user: {
1504 | 									fields: {
1505 | 										email: "email_address",
1506 | 									},
1507 | 								},
1508 | 							},
1509 | 							adapter(args_0) {
1510 | 								return {
1511 | 									async findMany({ model, where }) {
1512 | 										const fakeResult: (Omit<User, "email"> & {
1513 | 											email_address: string;
1514 | 										})[] = [
1515 | 											{
1516 | 												id: "random-id-eio1d1u12h33123ed",
1517 | 												email_address: "[email protected]",
1518 | 												emailVerified: false,
1519 | 												createdAt: new Date(),
1520 | 												updatedAt: new Date(),
1521 | 												name: "test-name",
1522 | 											},
1523 | 										];
1524 | 										r({ model, where });
1525 | 										return fakeResult as any;
1526 | 									},
1527 | 								};
1528 | 							},
1529 | 						});
1530 | 						const res = await adapter.findMany<User>({
1531 | 							model: "user",
1532 | 							where: [{ field: "email", value: "[email protected]" }],
1533 | 						});
1534 | 						expect(res[0]).not.toHaveProperty("email_address");
1535 | 						expect(res[0]).toHaveProperty("email");
1536 | 						expect(res[0]?.email).toEqual("[email protected]");
1537 | 					});
1538 | 				expect(parameters.where?.[0]!.field).toEqual("email_address");
1539 | 			});
1540 | 
1541 | 			test("findOne: Should receive an integer id in where clause if the user has enabled `useNumberId`", async () => {
1542 | 				const parameters: { where: Where[]; model: string; select?: string[] } =
1543 | 					await new Promise(async (r) => {
1544 | 						const adapter = await createTestAdapter({
1545 | 							options: {
1546 | 								advanced: {
1547 | 									database: {
1548 | 										useNumberId: true,
1549 | 									},
1550 | 								},
1551 | 							},
1552 | 							adapter(args_0) {
1553 | 								return {
1554 | 									async findOne({ model, where, select }) {
1555 | 										const fakeResult: Omit<User, "id"> & { id: number } = {
1556 | 											id: 1,
1557 | 											email: "[email protected]",
1558 | 											emailVerified: false,
1559 | 											createdAt: new Date(),
1560 | 											updatedAt: new Date(),
1561 | 											name: "test-name",
1562 | 										};
1563 | 										r({ model, where, select });
1564 | 										return fakeResult as any;
1565 | 									},
1566 | 								};
1567 | 							},
1568 | 						});
1569 | 						const res = await adapter.findOne<User>({
1570 | 							model: "user",
1571 | 							where: [{ field: "id", value: "1" }],
1572 | 						});
1573 | 
1574 | 						expect(res).toHaveProperty("id");
1575 | 						expect(res?.id).toEqual("1");
1576 | 					});
1577 | 				// The where clause should convert the string id value of `"1"` to an int since `useNumberId` is true
1578 | 				expect(parameters.where[0]!.value).toEqual(1);
1579 | 			});
1580 | 			test("findMany: Should receive an integer id in where clause if the user has enabled `useNumberId`", async () => {
1581 | 				const parameters: { where: Where[] | undefined; model: string } =
1582 | 					await new Promise(async (r) => {
1583 | 						const adapter = await createTestAdapter({
1584 | 							options: {
1585 | 								advanced: {
1586 | 									database: {
1587 | 										useNumberId: true,
1588 | 									},
1589 | 								},
1590 | 							},
1591 | 							adapter(args_0) {
1592 | 								return {
1593 | 									async findMany({ model, where }) {
1594 | 										const fakeResult: (Omit<User, "id"> & { id: number })[] = [
1595 | 											{
1596 | 												id: 1,
1597 | 												email: "[email protected]",
1598 | 												emailVerified: false,
1599 | 												createdAt: new Date(),
1600 | 												updatedAt: new Date(),
1601 | 												name: "test-name",
1602 | 											},
1603 | 										];
1604 | 										r({ model, where });
1605 | 										return fakeResult as any;
1606 | 									},
1607 | 								};
1608 | 							},
1609 | 						});
1610 | 						const res = await adapter.findMany<User>({
1611 | 							model: "user",
1612 | 							where: [{ field: "id", value: "1" }],
1613 | 						});
1614 | 
1615 | 						expect(res[0]).toHaveProperty("id");
1616 | 						expect(res[0]!.id).toEqual("1");
1617 | 					});
1618 | 				// The where clause should convert the string id value of `"1"` to an int since `useNumberId` is true
1619 | 				expect(parameters.where?.[0]!.value).toEqual(1);
1620 | 			});
1621 | 		});
1622 | 	});
1623 | });
1624 | 
```
Page 61/69FirstPrevNextLast