From b6ed90933f695646f09ead04fc1bfdb3cf799f76 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sat, 22 Aug 2026 14:28:41 -0400 Subject: [PATCH 1/3] fix: align v3 auth dependency cohort --- README.md | 12 +-- docs/content/docs/cli.mdx | 55 ++++------- docs/content/docs/databases/adapters.mdx | 2 +- docs/content/docs/plugins/better-auth-ui.mdx | 82 +++++++++++----- packages/cli/scripts/test-init.sh | 25 ++--- .../src/templates/nextjs/pages-layout.tsx.hbs | 19 ++-- .../react-router/pages-layout.tsx.hbs | 17 +++- .../templates/tanstack/pages-layout.tsx.hbs | 17 +++- packages/cli/src/types.ts | 1 + .../utils/__tests__/package-installer.test.ts | 58 +++++++++++ .../src/utils/__tests__/passthrough.test.ts | 30 ++++++ .../src/utils/__tests__/scaffold-plan.test.ts | 98 ++++++++++++------- packages/cli/src/utils/constants.ts | 43 +++++++- packages/cli/src/utils/package-installer.ts | 31 ++++-- packages/cli/src/utils/passthrough.ts | 11 ++- packages/cli/src/utils/scaffold-plan.ts | 38 +++---- packages/stack/package.json | 4 +- .../src/__tests__/package-metadata.test.ts | 17 ++++ playground/src/app/actions.ts | 2 + .../src/components/playground-client.tsx | 2 + .../src/components/stackblitz-embed.tsx | 4 + playground/src/lib/stackblitz-template.ts | 43 +++++--- pnpm-lock.yaml | 35 ++++++- scripts/codegen/setup-nextjs.sh | 2 +- scripts/codegen/setup-react-router.sh | 2 +- scripts/codegen/setup-tanstack.sh | 2 +- 26 files changed, 464 insertions(+), 188 deletions(-) create mode 100644 packages/cli/src/utils/__tests__/package-installer.test.ts create mode 100644 packages/cli/src/utils/__tests__/passthrough.test.ts create mode 100644 packages/stack/src/__tests__/package-metadata.test.ts diff --git a/README.md b/README.md index 1cb0856f..d2c77335 100644 --- a/README.md +++ b/README.md @@ -140,16 +140,12 @@ for QueryClient wiring, database adapters, all three frameworks, and auth. ## Database schemas & migrations -Optional CLI to generate schemas and run migrations from enabled plugins: +Generate schemas and run migrations through the v3 codegen CLI. It runs the +aligned Better DB CLI in isolation, so its dependencies and `btst` binary do +not enter your application graph: ```bash -npm install -D @btst/cli -``` - -Generate drizzle schema: - -```bash -npx @btst/cli generate --orm drizzle --config lib/stack.ts --output db/schema.ts +npx @btst/codegen@next generate --orm drizzle --config lib/stack.ts --output db/schema.ts ``` Supports Prisma, Drizzle, MongoDB and Kysely SQL dialects. diff --git a/docs/content/docs/cli.mdx b/docs/content/docs/cli.mdx index f2c6f38c..2c918731 100644 --- a/docs/content/docs/cli.mdx +++ b/docs/content/docs/cli.mdx @@ -12,7 +12,9 @@ BTST has two CLI packages: - `@btst/codegen` owns `init` scaffolding (`npx @btst/codegen init`) - `@btst/cli` owns low-level DB schema generation and migrations -`@btst/codegen generate` and `@btst/codegen migrate` are passthrough entrypoints to the existing `@btst/cli` flow. +`@btst/codegen generate` and `@btst/codegen migrate` run the aligned +`@btst/cli@2.2.3` release in isolation. This avoids adding its dependency graph +or competing `btst` binary to the application. ## Init (Codegen) @@ -60,7 +62,8 @@ npx @btst/codegen generate --orm=prisma --config=lib/stack.ts --output=schema.pr npx @btst/codegen migrate --config=lib/stack.ts --database-url=postgres://... ``` -When a delegated command fails, fix the underlying issue and run the equivalent `npx @btst/cli ...` command directly. +When a delegated command fails, fix the underlying issue and run the equivalent +`npx @btst/cli@2.2.3 ...` command directly. ## About Better DB @@ -68,27 +71,11 @@ BTST uses [Better DB (`@btst/db`)](https://github.com/better-stack-ai/better-aut The CLI works with the `dbSchema` exported from your BTST configuration, which is built using Better DB's schema definition API. All plugin schemas are automatically merged into a unified schema that the CLI can process. -Install the CLI as a dev dependency: - - - - ```bash - npm install -D @btst/cli - ``` - - - - ```bash - pnpm add -D @btst/cli - ``` - - - - ```bash - yarn add -D @btst/cli - ``` - - +For v3 applications, prefer the codegen passthrough commands above. If a v2 +application lists `@btst/cli` in its dependencies, remove it during migration; +the pinned one-off CLI keeps Better DB dependencies from polluting the consumer +graph. You can still invoke the low-level CLI directly with +`npx @btst/cli@2.2.3`. ## Parameters @@ -106,13 +93,13 @@ Generate database schemas for your ORM from your BTST `dbSchema`: ```bash - npx @btst/cli generate --config=lib/stack.ts --orm=prisma --output=schema.prisma + npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=prisma --output=schema.prisma ``` ```bash - npx @btst/cli generate --config=lib/stack.ts --orm=drizzle --output=src/db/schema.ts + npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=drizzle --output=src/db/schema.ts ``` @@ -122,17 +109,17 @@ Generate database schemas for your ORM from your BTST `dbSchema`: **Using DATABASE_URL environment variable:** ```bash - DATABASE_URL=sqlite:./dev.db npx @btst/cli generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql + DATABASE_URL=sqlite:./dev.db npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql ``` **Or using --database-url flag:** ```bash - npx @btst/cli generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=sqlite:./dev.db + npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=sqlite:./dev.db ``` ```bash - npx @btst/cli generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=postgres://user:pass@localhost:5432/db + npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=postgres://user:pass@localhost:5432/db ``` @@ -145,17 +132,17 @@ Migrate your database schema directly (Kysely only). For Prisma and Drizzle, use **Using DATABASE_URL environment variable:** ```bash -DATABASE_URL=sqlite:./dev.db npx @btst/cli migrate --config=lib/stack.ts +DATABASE_URL=sqlite:./dev.db npx @btst/cli@2.2.3 migrate --config=lib/stack.ts ``` **Or using --database-url flag:** ```bash -npx @btst/cli migrate --config=lib/stack.ts --database-url=sqlite:./dev.db +npx @btst/cli@2.2.3 migrate --config=lib/stack.ts --database-url=sqlite:./dev.db ``` ```bash -npx @btst/cli migrate --config=lib/stack.ts --database-url=postgres://user:pass@localhost:5432/db +npx @btst/cli@2.2.3 migrate --config=lib/stack.ts --database-url=postgres://user:pass@localhost:5432/db ``` ### Generate SQL to File @@ -163,7 +150,7 @@ npx @btst/cli migrate --config=lib/stack.ts --database-url=postgres://user:pass@ Instead of running migrations directly, generate SQL to a file: ```bash -npx @btst/cli migrate --config=lib/stack.ts --output=migrations.sql --database-url=sqlite:./dev.db +npx @btst/cli@2.2.3 migrate --config=lib/stack.ts --output=migrations.sql --database-url=sqlite:./dev.db ``` ## Gotchas @@ -175,11 +162,11 @@ Because the CLI executes your config file to extract the `dbSchema`, there are a - **Environment variables**: If your config file or its imports have conditional checks for available environment variables (e.g., checking if `process.env.SOME_VAR` exists), you should also pass those environment variables when running CLI commands: ```bash -SOME_VAR=value npx @btst/cli generate --config=lib/stack.ts --orm=prisma --output=schema.prisma +SOME_VAR=value npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=prisma --output=schema.prisma ``` or using dotenv-cli: ```bash -npx dotenv-cli -e .env.local -- npx @btst/cli generate --orm drizzle --config lib/stack.ts --output db/btst-schema.ts +npx dotenv-cli -e .env.local -- npx @btst/cli@2.2.3 generate --orm drizzle --config lib/stack.ts --output db/btst-schema.ts ``` diff --git a/docs/content/docs/databases/adapters.mdx b/docs/content/docs/databases/adapters.mdx index 47615db8..b06a2305 100644 --- a/docs/content/docs/databases/adapters.mdx +++ b/docs/content/docs/databases/adapters.mdx @@ -14,7 +14,7 @@ BTST consists of separate npm packages under the `@btst` namespace: - **`@btst/stack`** - Core package (install this first) - **`@btst/adapter-*`** - Database adapters (install one based on your ORM) -- **`@btst/cli`** - CLI tools for schema generation (dev dependency) +- **`@btst/cli`** - schema tooling invoked in isolation through `@btst/codegen` - **`@btst/db`** - Internal database abstraction layer (installed as a dependency of other packages) See the [Installation guide](/installation) for setup instructions. diff --git a/docs/content/docs/plugins/better-auth-ui.mdx b/docs/content/docs/plugins/better-auth-ui.mdx index 7d4d3b27..8407d193 100644 --- a/docs/content/docs/plugins/better-auth-ui.mdx +++ b/docs/content/docs/plugins/better-auth-ui.mdx @@ -56,20 +56,64 @@ Before starting, ensure you have: - A database adapter (e.g., Drizzle with `@btst/adapter-drizzle`) -### 1. Install the Package +### 1. Install the v3 RC Dependency Cohort ```bash -pnpm add @btst/better-auth-ui +pnpm add --save-exact \ + @btst/stack@next \ + @btst/yar@1.3.0 \ + @btst/adapter-drizzle@2.2.3 \ + @btst/better-auth-ui@2.0.0-rc.1 \ + @tanstack/react-query@5.100.14 \ + better-auth@1.6.16 \ + @better-auth/core@1.6.16 \ + @better-auth/api-key@1.6.16 \ + @better-auth/drizzle-adapter@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 \ + drizzle-orm@0.45.2 ``` -Or with npm/yarn: +This exact cohort keeps BTST, Better Auth, and Drizzle on one copy of their +shared types. If you use another database, replace the two Drizzle packages +with the matching `@btst/adapter-*` `2.2.3` and `@better-auth/*-adapter` +`1.6.16` packages. The BTST initializer installs this aligned set when Better +Auth UI is selected. + +If you are upgrading from v2, remove a locally installed `@btst/cli`. The v3 +codegen commands run the aligned `@btst/cli@2.2.3` release in isolation, which +keeps its database tooling and `btst` binary out of the application dependency +graph: ```bash -npm install @btst/better-auth-ui -# or -yarn add @btst/better-auth-ui +pnpm remove @btst/cli ``` +With npm, use the same exact versions: + +```bash +npm install --save-exact \ + @btst/stack@next \ + @btst/yar@1.3.0 \ + @btst/adapter-drizzle@2.2.3 \ + @btst/better-auth-ui@2.0.0-rc.1 \ + @tanstack/react-query@5.100.14 \ + better-auth@1.6.16 \ + @better-auth/core@1.6.16 \ + @better-auth/api-key@1.6.16 \ + @better-auth/drizzle-adapter@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 \ + drizzle-orm@0.45.2 +``` + +For an npm-based v2 application, use `npm uninstall @btst/cli` during the +migration. + ### 2. Configure the Stack Client Import and register the auth plugins in your `stack-client.tsx` file: @@ -126,8 +170,9 @@ Configure the plugin overrides in your catch-all layout file. The `auth` overrid ```tsx title="app/p/layout.tsx" "use client" - import { StackProvider, type StackAuthProvider } from "@btst/stack/context" + import { StackProvider } from "@btst/stack/context" import { nextRouter } from "@btst/stack/next" + import { createBetterAuthProvider } from "@btst/better-auth-ui" import type { AuthPluginOverrides, AccountPluginOverrides, @@ -142,15 +187,9 @@ Configure the plugin overrides in your catch-all layout file. The `auth` overrid organization: OrganizationPluginOverrides } - const stackAuth = { - getIdentity: async () => { - const { data } = await authClient.getSession() - return data?.user ?? null - }, + const stackAuth = createBetterAuthProvider(authClient, { loginPath: "/p/auth/sign-in", - can: ({ resource, action, identity }) => - Boolean(identity && authorize(identity, resource, action)), - } satisfies StackAuthProvider + }) export default function PagesLayout({ children }: { children: ReactNode }) { // Better Auth UI-specific configuration shared by its three plugins @@ -277,10 +316,10 @@ The exact sub-paths come from the view paths constants in the library and match ### Auth Plugin (`AuthPluginOverrides`) -`Link`, `navigate`, and `replace` in this table are optional APIs of the -external `@btst/better-auth-ui` package. They are not the removed built-in BTST -override fields; the recommended setup above uses `StackProvider.router` and -the package defaults instead. +The standalone upstream UI provider still supports its own navigation +overrides. BTST plugin overrides intentionally omit those fields: the bridge +reads navigation, notifications, localization, and session refresh from the +top-level `StackProvider` instead. | Option | Type | Default | Description | |--------|------|---------|-------------| @@ -313,11 +352,6 @@ the package defaults instead. | `optimistic` | `boolean` | `false` | Optimistic user updates | | `hooks` | `Partial` | — | Custom data fetching hooks | | `mutators` | `Partial` | — | Custom mutation handlers | -| `Link` | `Link` | `` | Custom link component | -| `navigate` | `(href: string) => void` | `location.href` | Navigation function | -| `replace` | `(href: string) => void` | `navigate` | Replace navigation | -| `toast` | `RenderToast` | Sonner | Custom toast renderer | -| `onSessionChange` | `() => void` | — | Session change callback | | `onRouteError` | `(name, error, ctx) => void` | — | Route error callback | | `pageProps` | See [Per-Page Props](#per-page-props) | — | Per-page className/classNames/localization | diff --git a/packages/cli/scripts/test-init.sh b/packages/cli/scripts/test-init.sh index 59d27744..74f3b884 100644 --- a/packages/cli/scripts/test-init.sh +++ b/packages/cli/scripts/test-init.sh @@ -125,26 +125,21 @@ STACK_PEERS=$(node -e 'const fs=require("fs");const p=JSON.parse(fs.readFileSync # installInitDependencies() in package-installer.ts does at runtime. PLUGIN_EXTRA_PACKAGES=$(node -e ' const { PLUGINS } = require("./node_modules/@btst/codegen/dist/lib.cjs"); -const extras = PLUGINS.flatMap(p => p.extraPackages || []); +const extras = PLUGINS.flatMap(p => p.extraInstallSpecs || p.extraPackages || []); process.stdout.write([...new Set(extras)].join(" ")); ') # Install adapter, plugin extras (includes @btst/better-auth-ui, better-auth, and any # plugin-specific deps like @ai-sdk/openai + ai), and @btst/stack peers. # next-themes is generated by shadcn init (mode-toggle.tsx, sonner.tsx) but not auto-installed. -# @btst/better-auth-ui must be present before the next step for peer resolution. -npm install @btst/adapter-memory next-themes $PLUGIN_EXTRA_PACKAGES $STACK_PEERS --legacy-peer-deps -BETTER_AUTH_UI_PEERS=$(node -e ' -const fs=require("fs"); -const p=JSON.parse(fs.readFileSync("node_modules/@btst/better-auth-ui/package.json","utf8")); -const skip=new Set(["react","react-dom","tailwindcss","@btst/stack","@btst/yar","better-auth","@tanstack/react-query"]); -const optionalPrefixes=["@triplit","@instantdb","@daveyplate"]; -const keys=Object.keys(p.peerDependencies||{}).filter(d=>!skip.has(d)&&!optionalPrefixes.some(pre=>d.startsWith(pre))); -process.stdout.write(keys.join(" ")); -') -if [ -n "$BETTER_AUTH_UI_PEERS" ]; then - npm install $BETTER_AUTH_UI_PEERS --legacy-peer-deps -fi -success "Installed runtime deps (adapter + plugin extras + @btst/stack and @btst/better-auth-ui peers)" +# Re-enable strict peer resolution here so this fixture catches incompatible cohorts. +rm .npmrc +npm install --save-exact @btst/adapter-memory@2.2.3 next-themes $PLUGIN_EXTRA_PACKAGES $STACK_PEERS +success "Installed aligned runtime deps with strict peer resolution" + +BTST_CLI_VERSION=$(npx --yes @btst/cli@2.2.3 --version) +test "$BTST_CLI_VERSION" = "2.2.3" +test ! -e node_modules/@btst/cli +success "Ran @btst/cli@2.2.3 without adding it to the consumer graph" step "Asserting generated files and patches" test -f "lib/stack.ts" diff --git a/packages/cli/src/templates/nextjs/pages-layout.tsx.hbs b/packages/cli/src/templates/nextjs/pages-layout.tsx.hbs index 333c8992..367da54c 100644 --- a/packages/cli/src/templates/nextjs/pages-layout.tsx.hbs +++ b/packages/cli/src/templates/nextjs/pages-layout.tsx.hbs @@ -7,12 +7,11 @@ import { ChatLayout } from "@btst/stack/plugins/ai-chat/client" {{/if}} import { QueryClientProvider } from "@tanstack/react-query" {{#if hasBetterAuthUi}} -import { useRouter{{#if hasAiChat}}, usePathname{{/if}} } from "next/navigation" -{{else}} +import { createBetterAuthProvider } from "@btst/better-auth-ui" +{{/if}} {{#if hasAiChat}} import { usePathname } from "next/navigation" {{/if}} -{{/if}} import { getOrCreateQueryClient } from "{{alias}}lib/query-client" function getBaseURL() { @@ -27,14 +26,19 @@ function getBaseURL() { return "http://localhost:3000" } +{{#if hasBetterAuthUi}} +// TODO: replace this placeholder with your Better Auth client import. +const authClient = undefined as any +const stackAuth = createBetterAuthProvider(authClient, { + loginPath: "/pages/auth/sign-in", +}) + +{{/if}} export default function BtstPagesLayout({ children, }: { children: React.ReactNode }) { -{{#if hasBetterAuthUi}} - const router = useRouter() -{{/if}} const queryClient = getOrCreateQueryClient() {{#if hasAiChat}} const hasApiKey = typeof process !== "undefined" && !!process.env.NEXT_PUBLIC_HAS_OPENAI_KEY @@ -49,6 +53,9 @@ export default function BtstPagesLayout({ basePath="/pages" router={nextRouter()} api={{{providerApiLiteral}}} +{{#if hasBetterAuthUi}} + auth={stackAuth} +{{/if}} {{#if pagesLayoutOverrides}} overrides={ { diff --git a/packages/cli/src/templates/react-router/pages-layout.tsx.hbs b/packages/cli/src/templates/react-router/pages-layout.tsx.hbs index 8edf2902..27853abb 100644 --- a/packages/cli/src/templates/react-router/pages-layout.tsx.hbs +++ b/packages/cli/src/templates/react-router/pages-layout.tsx.hbs @@ -4,7 +4,10 @@ import { reactRouter } from "@btst/stack/react-router" import { ChatLayout } from "@btst/stack/plugins/ai-chat/client" {{/if}} import { QueryClientProvider } from "@tanstack/react-query" -import { Outlet{{#if hasBetterAuthUi}}, useNavigate{{/if}}{{#if hasAiChat}}, useLocation{{/if}} } from "react-router" +{{#if hasBetterAuthUi}} +import { createBetterAuthProvider } from "@btst/better-auth-ui" +{{/if}} +import { Outlet{{#if hasAiChat}}, useLocation{{/if}} } from "react-router" import { getOrCreateQueryClient } from "{{alias}}lib/query-client" function getBaseURL() { @@ -19,10 +22,15 @@ function getBaseURL() { return "http://localhost:5173" } -export default function BtstPagesLayout() { {{#if hasBetterAuthUi}} - const navigate = useNavigate() +// TODO: replace this placeholder with your Better Auth client import. +const authClient = undefined as any +const stackAuth = createBetterAuthProvider(authClient, { + loginPath: "/pages/auth/sign-in", +}) + {{/if}} +export default function BtstPagesLayout() { const queryClient = getOrCreateQueryClient() {{#if hasAiChat}} const hasApiKey = !!import.meta.env.VITE_HAS_OPENAI_KEY @@ -37,6 +45,9 @@ export default function BtstPagesLayout() { basePath="/pages" router={reactRouter()} api={{{providerApiLiteral}}} +{{#if hasBetterAuthUi}} + auth={stackAuth} +{{/if}} {{#if pagesLayoutOverrides}} overrides={ { diff --git a/packages/cli/src/templates/tanstack/pages-layout.tsx.hbs b/packages/cli/src/templates/tanstack/pages-layout.tsx.hbs index 5d2c3a71..53677f16 100644 --- a/packages/cli/src/templates/tanstack/pages-layout.tsx.hbs +++ b/packages/cli/src/templates/tanstack/pages-layout.tsx.hbs @@ -1,10 +1,13 @@ -import { createFileRoute, Outlet{{#if hasBetterAuthUi}}, useNavigate{{/if}}{{#if hasAiChat}}, useLocation{{/if}} } from "@tanstack/react-router" +import { createFileRoute, Outlet{{#if hasAiChat}}, useLocation{{/if}} } from "@tanstack/react-router" import { StackProvider } from "@btst/stack/context" import { tanstackRouter } from "@btst/stack/tanstack" {{#if hasAiChat}} import { ChatLayout } from "@btst/stack/plugins/ai-chat/client" {{/if}} import { QueryClientProvider } from "@tanstack/react-query" +{{#if hasBetterAuthUi}} +import { createBetterAuthProvider } from "@btst/better-auth-ui" +{{/if}} import { getOrCreateQueryClient } from "{{alias}}lib/query-client" export const Route = createFileRoute("/pages")({ @@ -23,10 +26,15 @@ function getBaseURL() { return "http://localhost:3000" } -function BtstPagesLayout() { {{#if hasBetterAuthUi}} - const navigate = useNavigate() +// TODO: replace this placeholder with your Better Auth client import. +const authClient = undefined as any +const stackAuth = createBetterAuthProvider(authClient, { + loginPath: "/pages/auth/sign-in", +}) + {{/if}} +function BtstPagesLayout() { const queryClient = getOrCreateQueryClient() {{#if hasAiChat}} const hasApiKey = !!import.meta.env.VITE_HAS_OPENAI_KEY @@ -41,6 +49,9 @@ function BtstPagesLayout() { basePath="/pages" router={tanstackRouter()} api={{{providerApiLiteral}}} +{{#if hasBetterAuthUi}} + auth={stackAuth} +{{/if}} {{#if pagesLayoutOverrides}} overrides={ { diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index 8c2c89a1..cf26e0b5 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -41,4 +41,5 @@ export interface ScaffoldPlan { pagesLayoutPath?: string; cssImports: string[]; extraPackages: string[]; + extraPackageVersions?: Record; } diff --git a/packages/cli/src/utils/__tests__/package-installer.test.ts b/packages/cli/src/utils/__tests__/package-installer.test.ts new file mode 100644 index 00000000..f3b5c7b1 --- /dev/null +++ b/packages/cli/src/utils/__tests__/package-installer.test.ts @@ -0,0 +1,58 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { execa } = vi.hoisted(() => ({ execa: vi.fn() })); + +vi.mock("execa", () => ({ execa })); + +import { installInitDependencies } from "../package-installer"; + +describe("installInitDependencies", () => { + beforeEach(() => { + execa.mockReset(); + execa.mockResolvedValue({}); + }); + + it("installs the coherent v3 auth and Drizzle release cohort", async () => { + await installInitDependencies({ + cwd: "/tmp/example", + packageManager: "pnpm", + adapter: "drizzle", + plugins: ["better-auth-ui"], + }); + + expect(execa).toHaveBeenCalledWith( + "pnpm", + [ + "add", + "@btst/stack@next", + "@btst/yar@1.3.0", + "@tanstack/react-query@5.100.14", + "@btst/adapter-drizzle@2.2.3", + "drizzle-orm@0.45.2", + "@btst/better-auth-ui@2.0.0-rc.1", + "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", + "@better-auth/drizzle-adapter@1.6.16", + ], + { cwd: "/tmp/example", stdio: "inherit" }, + ); + expect(execa).toHaveBeenCalledTimes(1); + }); + + it("saves npm runtime versions exactly", async () => { + await installInitDependencies({ + cwd: "/tmp/example", + packageManager: "npm", + adapter: "memory", + plugins: [], + }); + + expect(execa.mock.calls[0]?.[1]).toContain("--save-exact"); + expect(execa).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/cli/src/utils/__tests__/passthrough.test.ts b/packages/cli/src/utils/__tests__/passthrough.test.ts new file mode 100644 index 00000000..27b65999 --- /dev/null +++ b/packages/cli/src/utils/__tests__/passthrough.test.ts @@ -0,0 +1,30 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { execa } = vi.hoisted(() => ({ execa: vi.fn() })); + +vi.mock("execa", () => ({ execa })); + +import { runCliPassthrough } from "../passthrough"; + +describe("runCliPassthrough", () => { + beforeEach(() => { + execa.mockReset(); + execa.mockResolvedValue({}); + }); + + it("runs the aligned Better DB CLI outside the consumer dependency graph", async () => { + await expect( + runCliPassthrough({ + cwd: "/tmp/example", + command: "generate", + args: ["--orm=drizzle"], + }), + ).resolves.toBe(0); + + expect(execa).toHaveBeenCalledWith( + "npx", + ["--yes", "@btst/cli@2.2.3", "generate", "--orm=drizzle"], + { cwd: "/tmp/example", stdio: "inherit" }, + ); + }); +}); diff --git a/packages/cli/src/utils/__tests__/scaffold-plan.test.ts b/packages/cli/src/utils/__tests__/scaffold-plan.test.ts index cb3e078f..db2adaa9 100644 --- a/packages/cli/src/utils/__tests__/scaffold-plan.test.ts +++ b/packages/cli/src/utils/__tests__/scaffold-plan.test.ts @@ -392,17 +392,26 @@ describe("scaffold plan", () => { // No apiBaseURL/apiBasePath in better-auth-ui client entries expect(stackClientFile?.content).not.toContain('apiBasePath: "/api/data"'); - // Pages layout overrides — three blocks with authClient placeholder - expect(pagesLayoutFile?.content).toContain("authClient: undefined as any"); - expect(pagesLayoutFile?.content).toContain('basePath: "/pages/auth"'); - expect(pagesLayoutFile?.content).toContain('basePath: "/pages/account"'); - expect(pagesLayoutFile?.content).toContain('basePath: "/pages/org"'); + // The provider owns identity while the three plugin overrides share its client. + expect(pagesLayoutFile?.content).toContain( + 'import { createBetterAuthProvider } from "@btst/better-auth-ui"', + ); + expect(pagesLayoutFile?.content).toContain( + "const authClient = undefined as any", + ); expect(pagesLayoutFile?.content).toContain( - "replace: (path: string) => router.replace(path)", + "const stackAuth = createBetterAuthProvider(authClient, {", ); expect(pagesLayoutFile?.content).toContain( - "onSessionChange: () => router.refresh()", + 'loginPath: "/pages/auth/sign-in"', ); + expect(pagesLayoutFile?.content).toContain("auth={stackAuth}"); + expect(pagesLayoutFile?.content).toContain("authClient,"); + expect(pagesLayoutFile?.content).toContain('basePath: "/pages/auth"'); + expect(pagesLayoutFile?.content).toContain('basePath: "/pages/account"'); + expect(pagesLayoutFile?.content).toContain('basePath: "/pages/org"'); + expect(pagesLayoutFile?.content).not.toContain("replace: (path: string)"); + expect(pagesLayoutFile?.content).not.toContain("onSessionChange:"); }); it("does not include apiBaseURL/apiBasePath in better-auth-ui client entries when mixed with other plugins", async () => { @@ -547,37 +556,33 @@ describe("scaffold plan", () => { expect(layoutFile?.content).not.toContain("router.replace"); }); - it("uses window.location.reload for onSessionChange in react-router better-auth-ui", async () => { - const plan = await buildScaffoldPlan({ - framework: "react-router", - adapter: "memory", - plugins: ["better-auth-ui"], - alias: "~/", - cssFile: "app/app.css", - }); - - const layoutFile = plan.files.find((f) => f.path.endsWith("_layout.tsx")); - expect(layoutFile?.content).toContain("window.location.reload()"); - expect(layoutFile?.content).not.toContain("router.refresh()"); - expect(layoutFile?.content).toContain("navigate(path, { replace: true })"); - }); - - it("uses window.location.reload for onSessionChange in tanstack better-auth-ui", async () => { - const plan = await buildScaffoldPlan({ - framework: "tanstack", - adapter: "memory", - plugins: ["better-auth-ui"], - alias: "@/", - cssFile: "src/styles/globals.css", - }); + it.each(["nextjs", "react-router", "tanstack"] as const)( + "uses the top-level auth provider in %s better-auth-ui scaffolds", + async (framework) => { + const plan = await buildScaffoldPlan({ + framework, + adapter: "memory", + plugins: ["better-auth-ui"], + alias: framework === "react-router" ? "~/" : "@/", + cssFile: + framework === "nextjs" + ? "app/globals.css" + : framework === "react-router" + ? "app/app.css" + : "src/styles/globals.css", + }); - const layoutFile = plan.files.find((f) => f.path.endsWith("route.tsx")); - expect(layoutFile?.content).toContain("window.location.reload()"); - expect(layoutFile?.content).not.toContain("router.refresh()"); - expect(layoutFile?.content).toContain( - "navigate({ to: path, replace: true })", - ); - }); + const layoutFile = plan.files.find( + (file) => file.path === plan.pagesLayoutPath, + ); + expect(layoutFile?.content).toContain( + 'import { createBetterAuthProvider } from "@btst/better-auth-ui"', + ); + expect(layoutFile?.content).toContain("auth={stackAuth}"); + expect(layoutFile?.content).not.toContain("onSessionChange:"); + expect(layoutFile?.content).not.toContain("replace: (path: string)"); + }, + ); it("includes better-auth-ui in the PLUGINS registry", () => { const allKeys = PLUGINS.map((p) => p.key); @@ -1084,6 +1089,25 @@ describe("scaffold plan", () => { expect(plan.extraPackages.length).toBe(new Set(plan.extraPackages).size); }); + it("keeps Better Auth package names separate from their v3 install versions", async () => { + const plan = await buildScaffoldPlan({ + framework: "nextjs", + adapter: "memory", + plugins: ["better-auth-ui"], + alias: "@/", + cssFile: "app/globals.css", + }); + + expect(plan.extraPackages).toContain("@btst/better-auth-ui"); + expect(plan.extraPackages).not.toContain("@btst/better-auth-ui@2.0.0-rc.1"); + expect(plan.extraPackageVersions).toMatchObject({ + "@btst/better-auth-ui": "2.0.0-rc.1", + "better-auth": "1.6.16", + "@better-auth/core": "1.6.16", + "better-call": "1.3.6", + }); + }); + it("returns empty cssImports and extraPackages when no plugins selected", async () => { const plan = await buildScaffoldPlan({ framework: "nextjs", diff --git a/packages/cli/src/utils/constants.ts b/packages/cli/src/utils/constants.ts index dfcb9000..5404cfb4 100644 --- a/packages/cli/src/utils/constants.ts +++ b/packages/cli/src/utils/constants.ts @@ -4,9 +4,13 @@ export interface AdapterMeta { key: Adapter; label: string; packageName: string; + installSpec?: string; + betterAuthInstallSpec?: string; ormForGenerate?: "prisma" | "drizzle" | "kysely"; - /** Additional npm packages that must be installed when this adapter is selected. */ + /** Additional package names required when this adapter is selected. */ extraPackages?: string[]; + /** Version-qualified forms of extraPackages used by the installer. */ + extraInstallSpecs?: string[]; } export interface PluginMeta { @@ -18,8 +22,10 @@ export interface PluginMeta { clientImportPath?: string; clientSymbol?: string; configKey: string; - /** Additional npm packages that must be installed when this plugin is selected. */ + /** Additional package names required when this plugin is selected. */ extraPackages?: string[]; + /** Version-qualified forms of extraPackages used by the installer. */ + extraInstallSpecs?: string[]; /** Whether this plugin has sample seed data available for the playground. */ hasSeedData?: boolean; } @@ -29,11 +35,15 @@ export const ADAPTERS: readonly AdapterMeta[] = [ key: "memory", label: "Memory (local dev / testing)", packageName: "@btst/adapter-memory", + installSpec: "@btst/adapter-memory@2.2.3", + betterAuthInstallSpec: "@better-auth/memory-adapter@1.6.16", }, { key: "prisma", label: "Prisma", packageName: "@btst/adapter-prisma", + installSpec: "@btst/adapter-prisma@2.2.3", + betterAuthInstallSpec: "@better-auth/prisma-adapter@1.6.16", ormForGenerate: "prisma", extraPackages: ["@prisma/adapter-pg", "pg"], }, @@ -41,18 +51,26 @@ export const ADAPTERS: readonly AdapterMeta[] = [ key: "drizzle", label: "Drizzle", packageName: "@btst/adapter-drizzle", + installSpec: "@btst/adapter-drizzle@2.2.3", + betterAuthInstallSpec: "@better-auth/drizzle-adapter@1.6.16", ormForGenerate: "drizzle", + extraPackages: ["drizzle-orm"], + extraInstallSpecs: ["drizzle-orm@0.45.2"], }, { key: "kysely", label: "Kysely", packageName: "@btst/adapter-kysely", + installSpec: "@btst/adapter-kysely@2.2.3", + betterAuthInstallSpec: "@better-auth/kysely-adapter@1.6.16", ormForGenerate: "kysely", }, { key: "mongodb", label: "MongoDB", packageName: "@btst/adapter-mongodb", + installSpec: "@btst/adapter-mongodb@2.2.3", + betterAuthInstallSpec: "@better-auth/mongo-adapter@1.6.16", }, ]; @@ -153,7 +171,26 @@ export const PLUGINS: readonly PluginMeta[] = [ clientImportPath: "@btst/better-auth-ui/client", clientSymbol: "authClientPlugin", configKey: "auth", - extraPackages: ["@btst/better-auth-ui", "better-auth"], + extraPackages: [ + "@btst/better-auth-ui", + "better-auth", + "@better-auth/core", + "@better-auth/api-key", + "@better-auth/passkey", + "@better-auth/utils", + "@better-fetch/fetch", + "better-call", + ], + extraInstallSpecs: [ + "@btst/better-auth-ui@2.0.0-rc.1", + "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", + ], }, { key: "route-docs", diff --git a/packages/cli/src/utils/package-installer.ts b/packages/cli/src/utils/package-installer.ts index cf9b7b3d..01423a76 100644 --- a/packages/cli/src/utils/package-installer.ts +++ b/packages/cli/src/utils/package-installer.ts @@ -7,12 +7,21 @@ function getInstallCommand( packages: string[], ): { command: string; args: string[] } { if (packageManager === "pnpm") { - return { command: "pnpm", args: ["add", ...packages] }; + return { + command: "pnpm", + args: ["add", ...packages], + }; } if (packageManager === "yarn") { - return { command: "yarn", args: ["add", ...packages] }; + return { + command: "yarn", + args: ["add", ...packages], + }; } - return { command: "npm", args: ["install", ...packages] }; + return { + command: "npm", + args: ["install", "--save-exact", ...packages], + }; } export async function installInitDependencies(input: { @@ -31,16 +40,20 @@ export async function installInitDependencies(input: { const pluginExtraPackages = input.plugins.flatMap((key) => { const meta = PLUGINS.find((p) => p.key === key); - return meta?.extraPackages ?? []; + return meta?.extraInstallSpecs ?? meta?.extraPackages ?? []; }); const packages = [ - "@btst/stack", - "@btst/yar", - "@tanstack/react-query", - adapterMeta.packageName, - ...(adapterMeta.extraPackages ?? []), + "@btst/stack@next", + "@btst/yar@1.3.0", + "@tanstack/react-query@5.100.14", + adapterMeta.installSpec ?? adapterMeta.packageName, + ...(adapterMeta.extraInstallSpecs ?? adapterMeta.extraPackages ?? []), ...pluginExtraPackages, + ...(input.plugins.includes("better-auth-ui") && + adapterMeta.betterAuthInstallSpec + ? [adapterMeta.betterAuthInstallSpec] + : []), ]; const { command, args } = getInstallCommand(input.packageManager, packages); await execa(command, args, { cwd: input.cwd, stdio: "inherit" }); diff --git a/packages/cli/src/utils/passthrough.ts b/packages/cli/src/utils/passthrough.ts index 41fec26c..e3e4da83 100644 --- a/packages/cli/src/utils/passthrough.ts +++ b/packages/cli/src/utils/passthrough.ts @@ -2,6 +2,8 @@ import { execa } from "execa"; import { ADAPTERS } from "./constants"; import type { Adapter } from "../types"; +const BETTER_DB_CLI_SPEC = "@btst/cli@2.2.3"; + export function adapterNeedsGenerate(adapter: Adapter): boolean { if (adapter === "memory") return false; return Boolean(ADAPTERS.find((item) => item.key === adapter)?.ormForGenerate); @@ -34,7 +36,12 @@ export async function runCliPassthrough(input: { command: "generate" | "migrate"; args: string[]; }): Promise { - const effectiveCommand = ["@btst/cli", input.command, ...input.args]; + const effectiveCommand = [ + "--yes", + BETTER_DB_CLI_SPEC, + input.command, + ...input.args, + ]; console.log(`Delegating to: npx ${effectiveCommand.join(" ")}`); try { await execa("npx", effectiveCommand, { @@ -44,7 +51,7 @@ export async function runCliPassthrough(input: { return 0; } catch (error) { console.error( - `Delegated ${input.command} failed. Resolve the error, then run npx @btst/cli ${input.command} ... again.`, + `Delegated ${input.command} failed. Resolve the error, then run npx --yes ${BETTER_DB_CLI_SPEC} ${input.command} ... again.`, ); return 1; } diff --git a/packages/cli/src/utils/scaffold-plan.ts b/packages/cli/src/utils/scaffold-plan.ts index 85bfdb09..34a9f217 100644 --- a/packages/cli/src/utils/scaffold-plan.ts +++ b/packages/cli/src/utils/scaffold-plan.ts @@ -60,17 +60,6 @@ function getPublicSiteURLVar(framework: Framework) { return "VITE_PUBLIC_SITE_URL"; } -function getReplaceExpr(framework: Framework): string { - if (framework === "nextjs") return "router.replace(path)"; - if (framework === "react-router") return "navigate(path, { replace: true })"; - return "navigate({ to: path, replace: true })"; -} - -function getSessionChangeExpr(framework: Framework): string { - if (framework === "nextjs") return "router.refresh()"; - return "window.location.reload()"; -} - function getPagesLayoutFilePath(framework: Framework): string { if (framework === "nextjs") return "app/pages/layout.tsx"; if (framework === "react-router") return "app/routes/pages/_layout.tsx"; @@ -212,28 +201,20 @@ function buildPluginTemplateContext( if (m.key === "route-docs") { return ""; } - const rep = getReplaceExpr(framework); - const ses = getSessionChangeExpr(framework); const layoutFile = getPagesLayoutFilePath(framework); if (m.key === "better-auth-ui") { return `\t\t\t\t\tauth: { -\t\t\t\t\t\tauthClient: undefined as any, -\t\t\t\t\t\treplace: (path: string) => ${rep}, -\t\t\t\t\t\tonSessionChange: () => ${ses}, +\t\t\t\t\t\tauthClient, \t\t\t\t\t\tbasePath: "/pages/auth", \t\t\t\t\t\tredirectTo: "/pages/account/settings", \t\t\t\t\t}, \t\t\t\t\taccount: { -\t\t\t\t\t\tauthClient: undefined as any, -\t\t\t\t\t\treplace: (path: string) => ${rep}, -\t\t\t\t\t\tonSessionChange: () => ${ses}, +\t\t\t\t\t\tauthClient, \t\t\t\t\t\tbasePath: "/pages/account", \t\t\t\t\t\taccount: { fields: ["image", "name"] }, \t\t\t\t\t}, \t\t\t\t\torganization: { -\t\t\t\t\t\tauthClient: undefined as any, -\t\t\t\t\t\treplace: (path: string) => ${rep}, -\t\t\t\t\t\tonSessionChange: () => ${ses}, +\t\t\t\t\t\tauthClient, \t\t\t\t\t\tbasePath: "/pages/org", \t\t\t\t\t\torganization: { basePath: "/pages/org" }, \t\t\t\t\t},`; @@ -647,6 +628,18 @@ export async function buildScaffoldPlan( ), ), ); + const extraPackageVersions = Object.fromEntries( + PLUGINS.filter((plugin) => input.plugins.includes(plugin.key)).flatMap( + (plugin) => + (plugin.extraInstallSpecs ?? []).map((spec) => { + const versionSeparator = spec.lastIndexOf("@"); + return [ + spec.slice(0, versionSeparator), + spec.slice(versionSeparator + 1), + ]; + }), + ), + ); return { files, @@ -655,5 +648,6 @@ export async function buildScaffoldPlan( pagesLayoutPath: frameworkPaths.pagesLayoutPath, cssImports, extraPackages, + extraPackageVersions, }; } diff --git a/packages/stack/package.json b/packages/stack/package.json index a0fb7a92..3c48f035 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -833,7 +833,7 @@ } }, "dependencies": { - "@btst/db": "2.2.2", + "@btst/db": "2.2.3", "@milkdown/crepe": "^7.17.1", "@milkdown/kit": "^7.17.1", "remend": "^1.0.1", @@ -903,7 +903,7 @@ "@ai-sdk/react": "^2.0.94", "@aws-sdk/client-s3": "^3.1011.0", "@aws-sdk/s3-request-presigner": "^3.1011.0", - "@btst/adapter-memory": "2.2.2", + "@btst/adapter-memory": "2.2.3", "@btst/yar": "1.3.0", "@tanstack/react-router": "1.168.10", "@types/react": "^19.0.0", diff --git a/packages/stack/src/__tests__/package-metadata.test.ts b/packages/stack/src/__tests__/package-metadata.test.ts new file mode 100644 index 00000000..e3f4e31c --- /dev/null +++ b/packages/stack/src/__tests__/package-metadata.test.ts @@ -0,0 +1,17 @@ +import { readFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; + +describe("published dependency alignment", () => { + it("uses the Better DB release that shares the Better Auth 1.6.16 cohort", async () => { + const manifest = JSON.parse( + await readFile(resolve("package.json"), "utf8"), + ) as { + dependencies?: Record; + devDependencies?: Record; + }; + + expect(manifest.dependencies?.["@btst/db"]).toBe("2.2.3"); + expect(manifest.devDependencies?.["@btst/adapter-memory"]).toBe("2.2.3"); + }); +}); diff --git a/playground/src/app/actions.ts b/playground/src/app/actions.ts index f83ee24a..59846350 100644 --- a/playground/src/app/actions.ts +++ b/playground/src/app/actions.ts @@ -20,6 +20,7 @@ export interface GenerateResult { routes: string[]; cssImports: string[]; extraPackages: string[]; + extraPackageVersions: Record; hasAiChat: boolean; seedRouteFiles: SeedRouteFile[]; seedRunnerScript: string | null; @@ -178,6 +179,7 @@ export async function generateProject( routes, cssImports: plan.cssImports, extraPackages: plan.extraPackages, + extraPackageVersions: plan.extraPackageVersions ?? {}, hasAiChat: withRouteDocs.includes("ai-chat" as PluginKey), seedRouteFiles, seedRunnerScript, diff --git a/playground/src/components/playground-client.tsx b/playground/src/components/playground-client.tsx index 0deaa12a..c994f54b 100644 --- a/playground/src/components/playground-client.tsx +++ b/playground/src/components/playground-client.tsx @@ -42,6 +42,7 @@ interface GeneratedState { routes: string[]; cssImports: string[]; extraPackages: string[]; + extraPackageVersions: Record; hasAiChat: boolean; seedRouteFiles: SeedRouteFile[]; seedRunnerScript: string | null; @@ -379,6 +380,7 @@ export function PlaygroundClient({ generatedFiles={generated.files} cssImports={generated.cssImports} extraPackages={generated.extraPackages} + extraPackageVersions={generated.extraPackageVersions} hasAiChat={generated.hasAiChat} previewPath={activePreviewRoute} seedRouteFiles={generated.seedRouteFiles} diff --git a/playground/src/components/stackblitz-embed.tsx b/playground/src/components/stackblitz-embed.tsx index 859da994..cd271c62 100644 --- a/playground/src/components/stackblitz-embed.tsx +++ b/playground/src/components/stackblitz-embed.tsx @@ -18,6 +18,7 @@ interface StackBlitzEmbedProps { generatedFiles: FileWritePlanItem[]; cssImports: string[]; extraPackages: string[]; + extraPackageVersions: Record; hasAiChat?: boolean; previewPath?: string | null; extraButtons?: React.ReactNode; @@ -33,6 +34,7 @@ export function StackBlitzEmbed({ generatedFiles, cssImports, extraPackages, + extraPackageVersions, hasAiChat = false, previewPath, extraButtons, @@ -67,6 +69,7 @@ export function StackBlitzEmbed({ generatedFiles, cssImports, extraPackages, + extraPackageVersions, hasAiChat, seedRouteFiles, seedRunnerScript, @@ -218,6 +221,7 @@ export function StackBlitzEmbed({ generatedFiles, cssImports, extraPackages, + extraPackageVersions, hasAiChat, seedRouteFiles, seedRunnerScript, diff --git a/playground/src/lib/stackblitz-template.ts b/playground/src/lib/stackblitz-template.ts index a9dd284f..d3fd5763 100644 --- a/playground/src/lib/stackblitz-template.ts +++ b/playground/src/lib/stackblitz-template.ts @@ -18,15 +18,17 @@ function buildNextjsProjectFiles( generatedFiles: FileWritePlanItem[], cssImports: string[], extraPackages: string[] = [], + extraPackageVersions: Record = {}, hasAiChat = false, seedFiles: SeedRouteFile[] = [], seedRunnerScript: string | null = null, ): ProjectFiles { const cssImportLines = cssImports.map((c) => `@import "${c}";`).join("\n"); const baseDependencies: Record = { - "@btst/stack": "latest", - "@btst/adapter-memory": "latest", - "@tanstack/react-query": "^5.0.0", + "@btst/stack": "next", + "@btst/adapter-memory": "2.2.3", + "@btst/yar": "1.3.0", + "@tanstack/react-query": "5.100.14", next: "15.3.4", react: "19.2.4", "react-dom": "19.2.4", @@ -35,7 +37,10 @@ function buildNextjsProjectFiles( "lucide-react": "latest", }; const pluginDependencies = Object.fromEntries( - Array.from(new Set(extraPackages)).map((pkgName) => [pkgName, "latest"]), + Array.from(new Set(extraPackages)).map((pkgName) => [ + pkgName, + extraPackageVersions[pkgName] ?? "latest", + ]), ); const dependencies = Object.fromEntries( Object.entries({ @@ -435,6 +440,7 @@ function buildReactRouterProjectFiles( generatedFiles: FileWritePlanItem[], cssImports: string[], extraPackages: string[] = [], + extraPackageVersions: Record = {}, hasAiChat = false, seedFiles: SeedRouteFile[] = [], seedRunnerScript: string | null = null, @@ -442,14 +448,18 @@ function buildReactRouterProjectFiles( ): ProjectFiles { const cssImportLines = cssImports.map((c) => `@import "${c}";`).join("\n"); const pluginDependencies = Object.fromEntries( - Array.from(new Set(extraPackages)).map((pkgName) => [pkgName, "latest"]), + Array.from(new Set(extraPackages)).map((pkgName) => [ + pkgName, + extraPackageVersions[pkgName] ?? "latest", + ]), ); const baseDependencies: Record = { - "@btst/adapter-memory": "latest", - "@btst/stack": "latest", + "@btst/adapter-memory": "2.2.3", + "@btst/stack": "next", + "@btst/yar": "1.3.0", "@react-router/node": "^7.0.0", "@react-router/serve": "^7.0.0", - "@tanstack/react-query": "^5.0.0", + "@tanstack/react-query": "5.100.14", react: "^19.0.0", "react-dom": "^19.0.0", "react-router": "^7.0.0", @@ -783,6 +793,7 @@ function buildTanstackProjectFiles( generatedFiles: FileWritePlanItem[], cssImports: string[], extraPackages: string[] = [], + extraPackageVersions: Record = {}, hasAiChat = false, seedFiles: SeedRouteFile[] = [], seedRunnerScript: string | null = null, @@ -790,13 +801,17 @@ function buildTanstackProjectFiles( ): ProjectFiles { const cssImportLines = cssImports.map((c) => `@import "${c}";`).join("\n"); const pluginDependencies = Object.fromEntries( - Array.from(new Set(extraPackages)).map((pkgName) => [pkgName, "latest"]), + Array.from(new Set(extraPackages)).map((pkgName) => [ + pkgName, + extraPackageVersions[pkgName] ?? "latest", + ]), ); const baseDependencies: Record = { - "@btst/adapter-memory": "latest", - "@btst/stack": "latest", + "@btst/adapter-memory": "2.2.3", + "@btst/stack": "next", + "@btst/yar": "1.3.0", "@tailwindcss/postcss": "^4", - "@tanstack/react-query": "^5.0.0", + "@tanstack/react-query": "5.100.14", "@tanstack/react-router": "^1.0.0", "@tanstack/react-router-ssr-query": "^1.0.0", "@tanstack/react-start": "^1.0.0", @@ -1126,6 +1141,7 @@ export function buildProjectFiles( generatedFiles: FileWritePlanItem[], cssImports: string[], extraPackages: string[] = [], + extraPackageVersions: Record = {}, hasAiChat = false, seedFiles: SeedRouteFile[] = [], seedRunnerScript: string | null = null, @@ -1136,6 +1152,7 @@ export function buildProjectFiles( generatedFiles, cssImports, extraPackages, + extraPackageVersions, hasAiChat, seedFiles, seedRunnerScript, @@ -1147,6 +1164,7 @@ export function buildProjectFiles( generatedFiles, cssImports, extraPackages, + extraPackageVersions, hasAiChat, seedFiles, seedRunnerScript, @@ -1157,6 +1175,7 @@ export function buildProjectFiles( generatedFiles, cssImports, extraPackages, + extraPackageVersions, hasAiChat, seedFiles, seedRunnerScript, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b6275bb1..7105c8ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -339,8 +339,8 @@ importers: packages/stack: dependencies: '@btst/db': - specifier: 2.2.2 - version: 2.2.2(706a9ce1fcc616cc33147c0a84ffbdf8) + specifier: 2.2.3 + version: 2.2.3(11577220e6bea834a9815f29293913ef) '@hookform/resolvers': specifier: '>=5.0.0' version: 5.2.2(react-hook-form@7.66.1(react@19.2.7)) @@ -436,8 +436,8 @@ importers: specifier: ^3.1011.0 version: 3.1011.0 '@btst/adapter-memory': - specifier: 2.2.2 - version: 2.2.2(018b1bc68345b791db4bf45b13a5d907) + specifier: 2.2.3 + version: 2.2.3(11577220e6bea834a9815f29293913ef) '@btst/yar': specifier: 1.3.0 version: 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7) @@ -1454,9 +1454,23 @@ packages: '@better-auth/core': '>=1.6.0' better-auth: '>=1.6.0' + '@btst/adapter-memory@2.2.3': + resolution: {integrity: sha512-qBgmfN9Wx6mCG3NoNv+12b+3iUoXGQGWBbnT/nPd1Olk+tJo8GjiLXY824gqRYLWNjpk0LIUsmo+lDMQlNrbIA==} + peerDependencies: + '@better-auth/core': 1.6.16 + '@better-auth/utils': 0.4.1 + better-auth: 1.6.16 + '@btst/db@2.2.2': resolution: {integrity: sha512-NLT9FXK4c60wP1DQ3lTRPd9Hqa+54VoqtQTT0w9xilk3Vm6DfNnWE9npwf8Nc+5noUBVtvgESUYssQHcOjNSbA==} + '@btst/db@2.2.3': + resolution: {integrity: sha512-tbx0o9mJnv3y5WVole5TZQwMMVmboamVJxsSeyMZH3O4qF9CNdEg6NfiR9zm/b81eO7ie6CftTWT9R7pJ4msSA==} + peerDependencies: + '@better-auth/core': 1.6.16 + '@better-auth/utils': 0.4.1 + better-auth: 1.6.16 + '@btst/yar@1.3.0': resolution: {integrity: sha512-TD6/whPS6ES7uDFkL/1QIWSvrDyg7QDvtn9s7frAcGwbg4+0VNsC8DOhmJ7MlWb3qF5JuGQXM2hB62P4vSeWPQ==} peerDependencies: @@ -13234,6 +13248,13 @@ snapshots: - vitest - vue + '@btst/adapter-memory@2.2.3(11577220e6bea834a9815f29293913ef)': + dependencies: + '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) + '@better-auth/utils': 0.4.1 + '@btst/db': 2.2.3(11577220e6bea834a9815f29293913ef) + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + '@btst/db@2.2.2(706a9ce1fcc616cc33147c0a84ffbdf8)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) @@ -13300,6 +13321,12 @@ snapshots: - vitest - vue + '@btst/db@2.2.3(11577220e6bea834a9815f29293913ef)': + dependencies: + '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) + '@better-auth/utils': 0.4.1 + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + '@btst/yar@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7)': dependencies: '@types/react': 19.2.14 diff --git a/scripts/codegen/setup-nextjs.sh b/scripts/codegen/setup-nextjs.sh index c8c2272e..7be8a90c 100755 --- a/scripts/codegen/setup-nextjs.sh +++ b/scripts/codegen/setup-nextjs.sh @@ -124,7 +124,7 @@ pkg.scripts["start:e2e"] = "rm -rf .next && next build && NODE_ENV=test NODE_OPT // btst init --skip-install doesn't add packages to package.json, so add them manually. const btstDeps = { "@btst/stack": "workspace:*", - "@btst/adapter-memory": "^2.2.2", + "@btst/adapter-memory": "2.2.3", }; // Ensure required runtime deps diff --git a/scripts/codegen/setup-react-router.sh b/scripts/codegen/setup-react-router.sh index 4318944d..908966ff 100755 --- a/scripts/codegen/setup-react-router.sh +++ b/scripts/codegen/setup-react-router.sh @@ -124,7 +124,7 @@ pkg.scripts["start:e2e"] = "rm -rf build && rm -rf .react-router && react-router // btst init --skip-install doesn't add packages to package.json, so add them manually. const btstDeps = { "@btst/stack": "workspace:*", - "@btst/adapter-memory": "^2.2.2", + "@btst/adapter-memory": "2.2.3", }; // Ensure required runtime deps diff --git a/scripts/codegen/setup-tanstack.sh b/scripts/codegen/setup-tanstack.sh index 17448887..0c1c136b 100755 --- a/scripts/codegen/setup-tanstack.sh +++ b/scripts/codegen/setup-tanstack.sh @@ -134,7 +134,7 @@ pkg.scripts["start:e2e"] = "rm -rf .output && rm -rf .nitro && rm -rf .tanstack // btst init --skip-install doesn't add packages to package.json, so add them manually. const btstDeps = { "@btst/stack": "workspace:*", - "@btst/adapter-memory": "^2.2.2", + "@btst/adapter-memory": "2.2.3", }; // Ensure required runtime deps From 0b8b3d2a209bfcb77184ebd04ce972830c05a2e5 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sat, 22 Aug 2026 14:37:48 -0400 Subject: [PATCH 2/3] fix: pin stack better-call peer --- packages/stack/package.json | 2 +- packages/stack/scripts/test-registry.sh | 1 + packages/stack/src/__tests__/package-metadata.test.ts | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/stack/package.json b/packages/stack/package.json index 3c48f035..8ff1788a 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -854,7 +854,7 @@ "@tanstack/react-router": ">=1.0.0", "@vercel/blob": ">=0.14.0", "ai": ">=5.0.0", - "better-call": ">=1.3.5", + "better-call": "1.3.6", "class-variance-authority": ">=0.7.0", "clsx": ">=2.1.0", "cmdk": ">=1.1.0", diff --git a/packages/stack/scripts/test-registry.sh b/packages/stack/scripts/test-registry.sh index 2f356c5d..da423914 100755 --- a/packages/stack/scripts/test-registry.sh +++ b/packages/stack/scripts/test-registry.sh @@ -193,6 +193,7 @@ main() { react-hook-form \ @hookform/resolvers \ zod \ + better-call@1.3.6 \ lucide-react \ sonner \ clsx \ diff --git a/packages/stack/src/__tests__/package-metadata.test.ts b/packages/stack/src/__tests__/package-metadata.test.ts index e3f4e31c..48296e0f 100644 --- a/packages/stack/src/__tests__/package-metadata.test.ts +++ b/packages/stack/src/__tests__/package-metadata.test.ts @@ -9,9 +9,11 @@ describe("published dependency alignment", () => { ) as { dependencies?: Record; devDependencies?: Record; + peerDependencies?: Record; }; expect(manifest.dependencies?.["@btst/db"]).toBe("2.2.3"); expect(manifest.devDependencies?.["@btst/adapter-memory"]).toBe("2.2.3"); + expect(manifest.peerDependencies?.["better-call"]).toBe("1.3.6"); }); }); From 8cc569c3729f073dbab7ec6dda04b0f029a8e43c Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sat, 22 Aug 2026 14:49:30 -0400 Subject: [PATCH 3/3] fix: require React-compatible YAR release --- docs/content/docs/plugins/better-auth-ui.mdx | 4 ++-- .../utils/__tests__/package-installer.test.ts | 2 +- packages/cli/src/utils/package-installer.ts | 2 +- packages/stack/package.json | 4 ++-- .../stack/src/__tests__/package-metadata.test.ts | 2 ++ playground/src/lib/stackblitz-template.ts | 6 +++--- pnpm-lock.yaml | 16 ++++++++-------- 7 files changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/content/docs/plugins/better-auth-ui.mdx b/docs/content/docs/plugins/better-auth-ui.mdx index 8407d193..f87b6dd0 100644 --- a/docs/content/docs/plugins/better-auth-ui.mdx +++ b/docs/content/docs/plugins/better-auth-ui.mdx @@ -61,7 +61,7 @@ Before starting, ensure you have: ```bash pnpm add --save-exact \ @btst/stack@next \ - @btst/yar@1.3.0 \ + @btst/yar@1.3.2 \ @btst/adapter-drizzle@2.2.3 \ @btst/better-auth-ui@2.0.0-rc.1 \ @tanstack/react-query@5.100.14 \ @@ -96,7 +96,7 @@ With npm, use the same exact versions: ```bash npm install --save-exact \ @btst/stack@next \ - @btst/yar@1.3.0 \ + @btst/yar@1.3.2 \ @btst/adapter-drizzle@2.2.3 \ @btst/better-auth-ui@2.0.0-rc.1 \ @tanstack/react-query@5.100.14 \ diff --git a/packages/cli/src/utils/__tests__/package-installer.test.ts b/packages/cli/src/utils/__tests__/package-installer.test.ts index f3b5c7b1..77c17717 100644 --- a/packages/cli/src/utils/__tests__/package-installer.test.ts +++ b/packages/cli/src/utils/__tests__/package-installer.test.ts @@ -25,7 +25,7 @@ describe("installInitDependencies", () => { [ "add", "@btst/stack@next", - "@btst/yar@1.3.0", + "@btst/yar@1.3.2", "@tanstack/react-query@5.100.14", "@btst/adapter-drizzle@2.2.3", "drizzle-orm@0.45.2", diff --git a/packages/cli/src/utils/package-installer.ts b/packages/cli/src/utils/package-installer.ts index 01423a76..0783bcc5 100644 --- a/packages/cli/src/utils/package-installer.ts +++ b/packages/cli/src/utils/package-installer.ts @@ -45,7 +45,7 @@ export async function installInitDependencies(input: { const packages = [ "@btst/stack@next", - "@btst/yar@1.3.0", + "@btst/yar@1.3.2", "@tanstack/react-query@5.100.14", adapterMeta.installSpec ?? adapterMeta.packageName, ...(adapterMeta.extraInstallSpecs ?? adapterMeta.extraPackages ?? []), diff --git a/packages/stack/package.json b/packages/stack/package.json index 8ff1788a..106ca7cc 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -843,7 +843,7 @@ "@ai-sdk/react": ">=2.0.0", "@aws-sdk/client-s3": ">=3.0.0", "@aws-sdk/s3-request-presigner": ">=3.0.0", - "@btst/yar": ">=1.3.0", + "@btst/yar": ">=1.3.2", "@hookform/resolvers": ">=5.0.0", "@radix-ui/react-dialog": ">=1.1.0", "@radix-ui/react-label": ">=2.1.0", @@ -904,7 +904,7 @@ "@aws-sdk/client-s3": "^3.1011.0", "@aws-sdk/s3-request-presigner": "^3.1011.0", "@btst/adapter-memory": "2.2.3", - "@btst/yar": "1.3.0", + "@btst/yar": "1.3.2", "@tanstack/react-router": "1.168.10", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", diff --git a/packages/stack/src/__tests__/package-metadata.test.ts b/packages/stack/src/__tests__/package-metadata.test.ts index 48296e0f..b21d271d 100644 --- a/packages/stack/src/__tests__/package-metadata.test.ts +++ b/packages/stack/src/__tests__/package-metadata.test.ts @@ -14,6 +14,8 @@ describe("published dependency alignment", () => { expect(manifest.dependencies?.["@btst/db"]).toBe("2.2.3"); expect(manifest.devDependencies?.["@btst/adapter-memory"]).toBe("2.2.3"); + expect(manifest.devDependencies?.["@btst/yar"]).toBe("1.3.2"); + expect(manifest.peerDependencies?.["@btst/yar"]).toBe(">=1.3.2"); expect(manifest.peerDependencies?.["better-call"]).toBe("1.3.6"); }); }); diff --git a/playground/src/lib/stackblitz-template.ts b/playground/src/lib/stackblitz-template.ts index d3fd5763..88150020 100644 --- a/playground/src/lib/stackblitz-template.ts +++ b/playground/src/lib/stackblitz-template.ts @@ -27,7 +27,7 @@ function buildNextjsProjectFiles( const baseDependencies: Record = { "@btst/stack": "next", "@btst/adapter-memory": "2.2.3", - "@btst/yar": "1.3.0", + "@btst/yar": "1.3.2", "@tanstack/react-query": "5.100.14", next: "15.3.4", react: "19.2.4", @@ -456,7 +456,7 @@ function buildReactRouterProjectFiles( const baseDependencies: Record = { "@btst/adapter-memory": "2.2.3", "@btst/stack": "next", - "@btst/yar": "1.3.0", + "@btst/yar": "1.3.2", "@react-router/node": "^7.0.0", "@react-router/serve": "^7.0.0", "@tanstack/react-query": "5.100.14", @@ -809,7 +809,7 @@ function buildTanstackProjectFiles( const baseDependencies: Record = { "@btst/adapter-memory": "2.2.3", "@btst/stack": "next", - "@btst/yar": "1.3.0", + "@btst/yar": "1.3.2", "@tailwindcss/postcss": "^4", "@tanstack/react-query": "5.100.14", "@tanstack/react-router": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7105c8ed..0c2b7aaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -439,8 +439,8 @@ importers: specifier: 2.2.3 version: 2.2.3(11577220e6bea834a9815f29293913ef) '@btst/yar': - specifier: 1.3.0 - version: 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7) + specifier: 1.3.2 + version: 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7) '@tanstack/react-router': specifier: 1.168.10 version: 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -1471,12 +1471,12 @@ packages: '@better-auth/utils': 0.4.1 better-auth: 1.6.16 - '@btst/yar@1.3.0': - resolution: {integrity: sha512-TD6/whPS6ES7uDFkL/1QIWSvrDyg7QDvtn9s7frAcGwbg4+0VNsC8DOhmJ7MlWb3qF5JuGQXM2hB62P4vSeWPQ==} + '@btst/yar@1.3.2': + resolution: {integrity: sha512-2YqkTht2PdKizlkwF5EG/fmLPzK4SG7YCkOojwDVRrlj0s3YpszhSak0uongrqslLoexozxN0RixbOeEnz9Vew==} peerDependencies: - '@types/react': ^19.1.16 - '@types/react-dom': ^19.1.9 - react: 19.2.7 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 '@chevrotain/cst-dts-gen@10.5.0': resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} @@ -13327,7 +13327,7 @@ snapshots: '@better-auth/utils': 0.4.1 better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) - '@btst/yar@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7)': + '@btst/yar@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7)': dependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14)