From fd7a949ce7293e24bd7ce9eebeab9a5f0e5dc54b Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:40:36 -0400 Subject: [PATCH] docs: add stable v3 migration playbook (#175) --- docs/content/docs/breaking-changes.mdx | 530 ++++++++++++++++++++++- docs/content/docs/databases/adapters.mdx | 61 ++- 2 files changed, 585 insertions(+), 6 deletions(-) diff --git a/docs/content/docs/breaking-changes.mdx b/docs/content/docs/breaking-changes.mdx index dff81388..558ce701 100644 --- a/docs/content/docs/breaking-changes.mdx +++ b/docs/content/docs/breaking-changes.mdx @@ -10,7 +10,533 @@ This page documents breaking changes between major versions and provides migrati --- -## v3 RC2 → RC3: Canonical stack and plugin DX +## v2 or release candidate → stable v3: production playbook + +Use this section as the canonical migration order for a production application. +The RC2→RC3 and v2 framework sections later on this page remain detailed +references for individual mechanical changes; they are not separate upgrade +paths. + + + Do not upgrade one package at a time in a deployed application. Pin the exact + verified cohort, migrate on a branch with a database backup, and keep the old + application artifact and lockfile available until production verification is + complete. + + +### 0. Pin the verified cohort and a rollback point + +The final stable package versions will be set in a separate release-preparation +commit before the release job validates and publishes them. This documentation +change does not invent those unpublished versions. Until the next core +candidate is published, use this exact public cohort: + +| Role | Exact version | +| --- | --- | +| Core | `@btst/stack@3.0.0-rc.3` | +| Scaffold and command wrapper | `@btst/codegen@0.2.0-rc.3` | +| Optional Better Auth UI companion | `@btst/better-auth-ui@2.0.0-rc.4` | +| Better DB and the selected BTST adapter | `2.2.3` | +| Database CLI for generation/migration | `@btst/cli@2.2.4` directly; public Codegen RC3 still delegates to `2.2.3` | +| Better Auth and Core | `better-auth@1.6.16`, `@better-auth/core@1.6.16` | +| Better Auth utilities and transport | `@better-auth/utils@0.4.1`, `@better-fetch/fetch@1.2.2`, `better-call@1.3.6` | +| API-key and passkey declarations, when the companion is installed | `@better-auth/api-key@1.6.16`, `@better-auth/passkey@1.6.16` | + +When stable v3 is published, replace only the three release-candidate entries +with their exact published stable versions. Retain the Better Auth `1.6.16` +and Better DB/adapter `2.2.3` cohorts unless a later migration guide explicitly +changes them. Do not move this release to Better Auth `1.7.x`, and do not use a +floating `latest`, `next`, caret, or workspace range in a production migration. + +For example, a Drizzle application without the optional auth companion can pin +the current candidate with: + +```bash +pnpm add --save-exact @btst/stack@3.0.0-rc.3 @btst/adapter-drizzle@2.2.3 +pnpm add --save-dev --save-exact @btst/codegen@0.2.0-rc.3 +pnpm install --frozen-lockfile +``` + +If the application selects Better Auth UI, add the complete aligned auth +cohort shown in [Better Auth UI companion](#8-migrate-the-better-auth-ui-companion) +rather than asking the package manager to repair peers opportunistically. + +Before changing code or data: + +- record the currently deployed commit, package-manager version, runtime + version, build command, start command, and environment names; +- commit the manifest and lockfile, export the generated schema, and take a + restorable database backup or provider snapshot; +- inventory every BTST plugin, embedded component, direct hook, provider root, + custom route, lifecycle hook, auth rule, and trusted/background call site; +- capture a production-like smoke baseline, including representative existing + records and anonymous, regular-user, and privileged-user behavior; and +- create a migration branch and keep the previous application artifact + deployable. Never treat a down migration as the only rollback for data that + the new application has already written. + +### 1. Apply the ownership changes in this order + +{/* canonical-dx-guard: migration:start reason="stable v3 before-and-after inventory" */} + +| Concern | Removed or intermediate shape | Stable-v3 shape | +| --- | --- | --- | +| Backend constructor | `stack(...)` | `createBackendStack(...)` from `@btst/stack/api` | +| Client constructor | `createStackClient(...)`, `stackClient(...)` | `createClientStack(...)` from `@btst/stack/client` | +| Client runtime | API, site, query client, and headers repeated in plugins/provider | one resolved `createClientStack({ api, site, queryClient, plugins })` | +| Plugin IDs | kebab-case programmatic keys such as `ai-chat` and `form-builder` | canonical camelCase keys such as `aiChat` and `formBuilder`; package paths and URL slugs stay kebab-case | +| Backend factories | positional arguments or top-level hook callbacks | zero or one options object with callbacks under `hooks` and required domain dependencies explicit | +| Lifecycle | mixed read/create/error spellings and boolean hook denials | `onBefore` / `onAfter` / `onError` and thrown domain failures | +| Provider | API/base-path fields and a manual override generic | browser-safe `stack`, framework `router`, optional `auth`, `initialIdentity`, and genuine application services | +| Overrides | empty blocks used to activate plugins or duplicated runtime/auth fields | optional inferred keys containing only plugin-specific browser or presentation customization | +| Request calls | ambiguous `api` namespace | `forRequest(request).operations` | +| Trusted calls | `internal` or a boolean bypass | `trusted`, which skips user authorization but retains validation, trusted facts, domain behavior, transactions, and lifecycle | +| Low-level calls | ordinary app code reaching exported getters/mutations | narrow `raw` prefetch escape hatches; standalone primitives remain caller-composed and are not the ordinary app API | + +{/* canonical-dx-guard: migration:end */} + +The canonical browser composition is: + +```tsx title="lib/stack-client.tsx" +const clientStack = createClientStack({ + api: { baseURL, basePath: "/api/data" }, + site: { baseURL, basePath: "/pages" }, + queryClient, + plugins: { + blog: blogClientPlugin(), + comments: commentsClientPlugin(), + }, +}) + + + {children} + +``` + +Omit `overrides` when there is no customization. An empty override does not +register or activate a plugin. The registered resolved definitions infer the +allowed keys and exact value types; do not restore a provider generic or a +manual application override map. + +Build one request-specific client stack for server loaders and metadata, with +filtered headers under `api.headers`, and a separate stable browser stack +without request headers. Only schema-validated identity and trusted API/site +origins may cross the server/client boundary. Never serialize a backend stack, +cookies, authorization headers, proxy headers, secrets, or a request-specific +client stack. + +Keep resolved client plugin definitions server-import-safe. Put React state, +browser auth clients, upload callbacks, navigation, and the provider itself in a +client-only module. A path-only per-plugin endpoint replacement inherits the +top-level origin and filtered request headers; a replacement origin is a new +transport boundary and must provide its own path and deliberately selected +credentials. Preserve that boundary instead of forwarding server credentials +to another origin. + +### 2. Register plugins, factories, and lifecycle hooks + +Register both halves of a full-stack plugin under the same canonical key. +OpenAPI is backend-only; Route Docs is client-only; UI Builder is client-only +and composes the registered CMS contract. Do not invent a matching half for a +one-sided plugin. + +Move each backend plugin to one options object and each callback to `hooks`: + +```ts +createBackendStack({ + basePath: "/api/data", + adapter, + auth: serverAuth, + plugins: { + blog: blogBackendPlugin({ hooks: { onAfterCreatePost } }), + comments: commentsBackendPlugin({ + allowEditing: false, + resolveUser, + hooks: { onBeforeCreateComment }, + }), + }, +}) +``` + +The stable lifecycle grammar is `onBefore`, +`onAfter`, and `onError`. The complete rename +inventory is in [Rename every backend lifecycle callback](#6-rename-every-backend-lifecycle-callback). +Update every used plugin, including callbacks referenced indirectly from shared +hook objects. A hook runs only after validation, authoritative fact derivation, +identity resolution, and authorization have succeeded; it is not a replacement +for permission enforcement. Hook denials throw—returning `false` is no longer a +denial. + +### 3. Configure atomic writes explicitly + +AI Chat, Form Builder, Kanban, and Media contain operations whose authorization +facts and writes must share one isolated transaction. Configure a supported +Prisma, Drizzle, or Kysely adapter with `transaction: true`; do not rely on the +sequential fallback. Form Builder does not support generated memory or MongoDB +configuration, and Media does not support generated MongoDB configuration, +because those combinations cannot provide the required isolation. + +See [Database adapters](/databases/adapters#isolated-transactions-for-atomic-plugin-writes) +for copyable adapter examples and the fail-closed behavior. + +### 4. Migrate authorization as one application-owned rule + +Plugins publish schema-backed descriptors and the minimum facts required for an +operation. The application owns its identity schema, local rules, and both +identity resolvers. Authentication discovers identity; authorization decides +whether that identity may perform a typed operation. + +Keep the rule module browser-safe: + +```ts title="lib/authorization.ts" +import { defineAuthorization } from "@btst/stack/authorization" +import { blogPermissions } from "@btst/stack/plugins/blog/permissions" +import { z } from "zod" + +export const authorization = defineAuthorization({ + identity: z.object({ id: z.string(), role: z.enum(["user", "admin"]) }), + permissions: [blogPermissions] as const, + rules: ({ blog }) => [ + blog.post.delete.when(({ identity, facts }) => + identity !== null && + (identity.role === "admin" || identity.id === facts.authorId), + ), + ], +}) +``` + +Bind it separately on each side. These modules are client-only and server-only, +respectively: + +```tsx title="lib/authorization.client.ts" +"use client" + +export const clientAuth = createClientAuth({ + authorization, + getIdentity: () => session?.user ?? null, + loginPath: "/sign-in", +}) + +const { CanAccess } = clientAuth +const control = ( + + + +) +``` + +```ts title="lib/authorization.server.ts" +import "server-only" + +export const serverAuth = createServerAuth({ + authorization, + getIdentityFromHeaders: async ({ headers }) => { + const session = await auth.api.getSession({ headers }) + return session?.user ?? null + }, +}) +``` + +The client check is a synchronous presentation decision. It makes no +permission request and creates no shared authorization-result cache. The +backend validates input, derives trusted facts from server data, resolves the +request identity, evaluates the descriptor, and only then enters domain and +lifecycle execution. A representative plugin operation binds that ordering +once: + +```ts title="plugins/posts/api.ts" +const deletePost = defineOperation({ + input: z.object({ id: z.string() }), + permission: blogPermissions.post.delete, + facts: async ({ input }) => { + const post = await adapter.findOne({ + model: "post", + where: [{ field: "id", value: input.id }], + }) + return { id: input.id, ...(post?.authorId ? { authorId: post.authorId } : {}) } + }, + execute: async ({ input }) => { + await adapter.delete({ + model: "post", + where: [{ field: "id", value: input.id }], + }) + return { success: true } as const + }, +}) +``` + +Do not accept `authorId`, role, tenant ownership, record visibility, or other +authoritative facts from the browser merely because the same shapes are used +for a local UI preview. Row and tenant query scoping is a separate server-only +data concern, not a boolean authorization check. + +Once server authorization is enabled, a missing rule denies. Ordinary anonymous +and authenticated denials become 401 and 403, respectively. Invalid identity, +schema, transport, fact derivation, and policy execution remain observable +errors; never convert them to `false`. Omitting server authorization preserves +the documented permissive compatibility behavior while the migration is staged, +but it should be an explicit temporary choice. + +Audit every call site against its trust contract: + +```ts +await backend.forRequest(request).operations.blog.deletePost({ id }) +await backend.trusted.blog.deletePost({ id }) +await backend.raw.blog.prefetchForRoute("post", queryClient, { slug }) +``` + +The first path is request-authorized. The second is for a trusted job or server +workflow and skips only user authorization. The third is a narrow composition +escape hatch, not an alternate business API. + +For a managed or separately deployed backend, publish only the rule-free, +versioned contract and descriptors: + +```ts title="packages/backend-contract/authorization.ts" +export const authorizationContract = defineAuthorizationContract({ + identity: z.object({ id: z.string(), role: z.enum(["user", "admin"]) }), + permissions: [blogPermissions] as const, +}) +``` + +The browser may bind that contract to `createRemoteAuthorizationEvaluator`, +but the remote service must parse the contract version and facts, resolve its +own identity, re-read authoritative records, and evaluate server-owned rules. +It never trusts browser identity or ownership facts. See +[Authorization](/auth#use-a-managed-or-separate-backend) for the transport +example. Core intentionally exports no provider-specific auth adapter and no +global open-string `useCan` or `CanAccess` API. + +### 5. Adopt framework entries and tri-state hydration + +Use the framework entry factories instead of copied route resolution, loader, +metadata, dehydration, or 404 logic: + +| Framework | API route | Page route | Provider router | Identity layout | +| --- | --- | --- | --- | --- | +| Next.js | `toNextRouteHandlers` | `createNextPage` | `nextRouter()` | `createNextLayout` from `@btst/stack/next/server` | +| React Router | `toReactRouterHandlers` | `createReactRouterPage` | `reactRouter()` | `createReactRouterLayout` on the parent route | +| TanStack Start | `toTanStackHandlers` | `createTanStackPageOptions` | `tanstackRouter()` | `createTanStackLayout` plus a server function | + +Hydrate identity at the layout or parent-route boundary that owns the complete +provider subtree: + +```tsx + + {children} + +``` + +`initialIdentity` has three deliberate states: + +| Value | Meaning | Initial client behavior | +| --- | --- | --- | +| `undefined` or omitted | no server snapshot | resolve identity in the browser | +| `null` | settled anonymous snapshot | do not duplicate the initial request | +| validated identity | settled authenticated snapshot | use it without a duplicate initial request | + +Next.js request-aware pages and layouts must construct their server client from +the current request and keep static/ISR routes in a separate header-free route +group. React Router resolves the snapshot in the parent layout loader so it +covers the full ``. TanStack Start resolves it in a server function +used by the parent route loader and later client navigations. The complete, +copyable implementations are in +[Authorization: hydrate identity at the layout boundary](/auth#hydrate-identity-at-the-layout-boundary). + +When an application-owned route renders one plugin page directly instead of +using the catch-all, keep a dedicated wrapper and pass the page component its +declarative `{ params }` route context. Supply synthetic params only when that +wrapper intentionally fixes a resource; do not call route internals or restore +the removed named-prop adapters. The exact parameter mappings are listed in +[Update parameterized page-component overrides](#5-update-parameterized-page-component-overrides). + +After login, logout, or account switching, refresh at the application/framework +seam: Next.js `router.refresh()`, React Router `revalidator.revalidate()`, +TanStack `router.invalidate()`, or `clientAuth.useIdentity().refetch()`. + +### 6. Migrate embedded surfaces and every provider root + +The catch-all pages layout is not automatically an ancestor of UI embedded in +the rest of the application. Inventory and migrate `CommentThread`, +`CommentCount`, `FormRenderer`, direct plugin hooks, and cards such as +`PostCard` or `TaskCard`. Each rendered surface must be below a `StackProvider` +whose resolved stack registers that plugin and below the same QueryClient +provider used to create the stack. + +If a modal, parallel route, portal host, microfrontend, or independently mounted +widget has a separate React root, give that root its own stable browser stack +and provider using the same trusted origin snapshot and auth contract. Context +does not cross sibling roots. Hydrate identity per root or intentionally leave +it `undefined`; never copy a server stack or request headers into the new root. + +Remove `apiBaseURL`, `apiBasePath`, `headers`, and current-user props from +embedded components. They read transport and identity from the nearest +provider. Keep `CommentThread.loginHref` only when the resource needs a +specific sign-in return URL; it overrides the provider's general `loginPath`. +Test embedded mutations and counts as well as their first render—a static card +that looks correct can still be bound to the wrong endpoint or identity cache. + +### 7. Regenerate or merge the framework scaffold + +`@btst/codegen` owns application scaffolding. The published +`@btst/codegen@0.2.0-rc.3` artifact still delegates `generate` and `migrate` to +`@btst/cli@2.2.3`; do not claim the immutable RC3 artifact has the post-RC3 +fix. For the current public candidate, run `@btst/cli@2.2.4` directly and in +isolation instead of installing it into the application graph. + +The forthcoming Codegen RC4 (versioned by the separate release-preparation +change) and stable release will delegate to `2.2.4` from the consumer project +directory. That behavior loads the application's +TypeScript/JavaScript aliases and standard Next.js environment files, resolves +its Prisma or Drizzle adapter and ORM peers, and ignores only a bare +`import "server-only"` marker while evaluating the server config. + +Use the scaffold as a reference diff for an existing application rather than +blindly overwriting owned files: + +```bash +npx @btst/codegen@0.2.0-rc.3 init --framework=nextjs --adapter=drizzle \ + --plugins=blog,comments --cwd=. --skip-install +npx --yes @btst/cli@2.2.4 generate \ + --orm=drizzle --config=lib/stack.ts --output=src/db/schema.ts +``` + +Select `react-router` or `tanstack` for those frameworks. Review every planned +write and TODO, preserve application-owned auth/domain dependencies, then run +the generated framework's typecheck and production build. See [CLI](/cli) for +the supported flags and direct failure fallback. + +### 8. Migrate the Better Auth UI companion + +Better Auth remains application-configured and is a prerequisite. The optional +companion reads its own Better Auth session and uses native Better Auth account, +organization, and permission APIs; it exports no Better Auth-to-BTST client or +server auth factory. Map the session into `createClientAuth` and +`createServerAuth` yourself when business plugins should authorize the same +person. Role and tenant fields remain application-owned. + +Auth plus account is the minimal runtime integration. Pin the candidate cohort +exactly when it is selected: + +```bash +pnpm add --save-exact @btst/better-auth-ui@2.0.0-rc.4 \ + better-auth@1.6.16 @better-auth/core@1.6.16 \ + @better-auth/api-key@1.6.16 @better-auth/passkey@1.6.16 \ + @better-auth/utils@0.4.1 @better-fetch/fetch@1.2.2 better-call@1.3.6 +``` + +Public `@btst/codegen@0.2.0-rc.3` predates the `better-auth-ui` scaffold +selection. Configure the companion manually for that immutable candidate; the +forthcoming RC4 and stable Codegen artifacts contain the generated +auth-and-account path described in the focused guide. + +API-key and passkey are required declaration peers in the RC4 package because +its complete `AuthClient` type exposes those surfaces. Installing them satisfies +the strict type/dependency graph; it does not enable either feature. Enable +organization, API-key, passkey, or multi-session only when the matching Better +Auth server and browser plugins are configured. Optional `tanstack`, +`instantdb`, and `triplit` companion subpaths have additional peers; the base +auth/account integration does not require those adapter peers. + +Configure `authClient` once under the `auth` override and avatar behavior only +under `account`. Account and organization overrides intentionally reject +auth-only fields. Companion route bases derive from the resolved +`createClientStack({ site })` runtime; do not repeat them in overrides. Use the +explicit framework session refresh described above. The focused setup and peer +table live in [Better Auth UI Companion](/plugins/better-auth-ui). + +### 9. Verify data, production behavior, and cleanup + +Generate the schema from the migrated stack, diff it against the recorded +baseline, and review the ORM migration before applying it. Test against a +restored production-like snapshot first. Prefer additive migrations during the +deployment window, deploy schema changes before code that needs them, and do +not remove old columns or compatibility reads until the rollback window closes. +Verify existing rows, relationships, cascades, tenant boundaries, and plugin +records—not only newly created fixtures. + +Run this checklist against the exact packed/published artifacts and the +optimized production server: + +- [ ] Clean install succeeds from the exact selected cohort with no + undocumented peer repair, duplicate Better Auth type universe, or application + dependency on `@btst/cli`. +- [ ] Typecheck, lint, unit/integration tests, schema generation, reviewed + migration, optimized build, and production start pass. +- [ ] Server and client bundles remain separated; no backend stack, request + headers, cookies, secrets, or server auth are serialized. +- [ ] Direct navigation, hard refresh, back/forward, 404 and error boundaries, + console/server errors, and hydration warnings are clean. +- [ ] SSR, authenticated SSR, SSG/ISR, metadata, sitemap, and browser refetch + use the same resolved endpoints. +- [ ] Anonymous, regular-user, and privileged-user controls match authoritative + backend results. +- [ ] An allowed operation succeeds; anonymous denial is 401; authenticated + denial is 403; a missing rule denies; identity/fact/policy failures remain + errors; spoofed browser facts do not grant access; trusted internal execution + retains validation, domain behavior, transactions, and lifecycle. +- [ ] Login, logout, account switching, explicit session refresh, and all three + `initialIdentity` states behave without duplicate initial requests. +- [ ] Embedded components outside the catch-all layout, every independent + provider root, resource-specific sign-in return URLs, counts/cards/direct + hooks, and representative plugin mutations work. +- [ ] Better Auth account, profile, avatar, and only the optional features the + application configured work on the retained `1.6.16` cohort. +- [ ] Existing database records remain readable and writable, failed atomic + operations roll back, and database plus remote test assets are removed. + +Release maintainers prove the clean-room and snippet contracts from the +repository root before publishing: + +```bash +pnpm test:packed-consumers +BTST_ARTIFACT_DIR="$(mktemp -d)" +npm pack @btst/better-auth-ui@2.0.0-rc.4 --pack-destination "$BTST_ARTIFACT_DIR" +BTST_AUTH_UI_TARBALL="$BTST_ARTIFACT_DIR/btst-better-auth-ui-2.0.0-rc.4.tgz" +pnpm smoke:packed-consumer -- --fixture core --package-manager npm +pnpm smoke:packed-consumer -- --fixture core --package-manager pnpm +pnpm smoke:packed-consumer -- --fixture auth --package-manager npm \ + --better-auth-ui "$BTST_AUTH_UI_TARBALL" +pnpm smoke:packed-consumer -- --fixture auth --package-manager pnpm \ + --better-auth-ui "$BTST_AUTH_UI_TARBALL" +pnpm --filter @btst/codegen test:better-auth-ui-fixtures +pnpm typecheck +``` + +The first command verifies the harness itself; the four smoke commands then +install only packed tarballs with npm and pnpm, under strict peers, and exercise +core plus the auth cohort. The Better Auth UI gate generates untouched Next.js, +React Router, and TanStack Start applications and builds and typechecks each +one. The root typecheck includes the constructor, authorization, +managed-contract, and hydration consumer fixtures used by this guide. A failure +in any gate blocks publication and must be corrected in the guide or +implementation rather than repaired by an undocumented fixture edit. + +Finally remove migration-only compatibility code: old constructors, positional +factory arguments, top-level or retired lifecycle names, kebab-case +programmatic IDs, duplicated API/site/query/header wiring, manual override maps, +empty activation blocks, render guards, open-string authorization calls, +provider-specific core auth bridges, ambiguous `api`/`internal` calls, and +temporary dual-read or dual-write paths after the rollback window. Commit the +final lockfile and deployment evidence with the migration. + +--- + +## Migration reference: v3 RC2 → RC3 canonical stack and plugin DX RC3 removes the remaining duplicate runtime configuration and historical naming seams. The migration is mechanical: rename the constructors, move shared client @@ -426,7 +952,7 @@ Better Auth UI companion for auth and account pages. --- -## v2 → v3: Framework entries and resolved client runtime +## Migration reference: v2 → v3 framework entries and resolved client runtime BTST v3 has one supported framework-wiring path: framework entry factories own the catch-all routes, `createClientStack()` owns shared API, site, and diff --git a/docs/content/docs/databases/adapters.mdx b/docs/content/docs/databases/adapters.mdx index a07e3e08..854433c6 100644 --- a/docs/content/docs/databases/adapters.mdx +++ b/docs/content/docs/databases/adapters.mdx @@ -43,6 +43,57 @@ npm install @btst/adapter-prisma See the [Installation guide](/installation#install-database-adapter) for detailed adapter setup instructions. +## Isolated transactions for atomic plugin writes + +Some plugin operations authorize against a database snapshot and then update +that same state. In production, the fact read, domain hooks, compare-and-set, +and write must commit as one isolated transaction. Configure +`transaction: true` when the installed plugin set includes **AI Chat, Form +Builder, Kanban, or Media**. + + + + ```ts + adapter: (db) => createPrismaAdapter(prisma, db, { + provider: "postgresql", + transaction: true, + })({}) + ``` + + + + ```ts + adapter: (db) => createDrizzleAdapter(drizzleDb, db, { + provider: "pg", + transaction: true, + })({}) + ``` + + + + ```ts + adapter: (db) => createKyselyAdapter(kyselyDb, db, { + transaction: true, + })({}) + ``` + + + +Choose the provider value that matches the real ORM connection. The flag opts +the adapter into its native transaction implementation; it is not a promise +that a sequential callback fallback is safe. Owner-sensitive and persistent +operations fail closed with `ATOMIC_TRANSACTION_REQUIRED` when isolation is +missing, before lifecycle hooks or writes run. + +The memory adapter remains useful for local, single-process development where +the plugin documents serialized access. It is not a production isolation +substitute. The generated scaffold rejects Form Builder with memory or MongoDB +and Media with MongoDB; use Prisma, Drizzle, or Kysely for those production +configurations. `@btst/codegen init` adds `transaction: true` automatically for +AI Chat, Form Builder, and Media in the public `0.2.0-rc.3` artifact. RC3 +Kanban applications must add the flag manually. The forthcoming RC4 and stable +Codegen releases generate it for all four atomic-write plugins. + ## Usage When you configure BTST, the `createBackendStack()` function collects all plugin database schemas and merges them into a unified schema. The adapter function receives this merged schema and returns an adapter that translates BTST's database operations to your ORM. @@ -62,9 +113,9 @@ When you configure BTST, the `createBackendStack()` function collects all plugin // Your plugins here }, // The adapter receives the merged db schema from all plugins - adapter: (db) => createPrismaAdapter(prisma, db, { + adapter: (db) => createPrismaAdapter(prisma, db, { provider: "postgresql" // or "mysql", "sqlite", "cockroachdb", "mongodb" - }) + })({}) }) export { handler, dbSchema } @@ -86,7 +137,9 @@ When you configure BTST, the `createBackendStack()` function collects all plugin plugins: { // Your plugins here }, - adapter: (db) => createDrizzleAdapter(drizzleDb, db, {}) + adapter: (db) => createDrizzleAdapter(drizzleDb, db, { + provider: "pg" // or "mysql", "sqlite" + })({}) }) export { handler, dbSchema } @@ -111,7 +164,7 @@ When you configure BTST, the `createBackendStack()` function collects all plugin plugins: { // Your plugins here }, - adapter: (db) => createKyselyAdapter(kyselyDb, db, {}) + adapter: (db) => createKyselyAdapter(kyselyDb, db, {})({}) }) export { handler, dbSchema }