fix: cancel end of cycle additional add on

This commit is contained in:
John Yeo
2025-11-11 15:09:01 +00:00
parent bf3ee46b24
commit acae1179a4
18 changed files with 732 additions and 663 deletions

826
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,51 +1,61 @@
{
"name": "autumn",
"private": true,
"workspaces": [
"server",
"shared",
"vite",
"scripts"
],
"type": "module",
"scripts": {
"dev": "node scripts/start-dev.js",
"dev:simple": "concurrently \"cd shared && bun dev:watch\" \"cd server && bun dev\" \"cd server && bun workers:dev\" \"cd vite && bun dev\"",
"vite:build": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
"vite:start": "bun -F @autumn/vite start:bun",
"shared": "bun -F @autumn/shared build",
"server": "bun -F @autumn/shared build && bun -F @autumn/server start",
"workers": "bun -F @autumn/shared build && bun -F @autumn/server workers",
"cron": "bun -F @autumn/shared build && bun -F @autumn/server cron",
"check": "bun -F @autumn/shared build && bun -F @autumn/server check",
"server:cron": "pnpm -F server cron:start",
"server:check": "NODE_ENV=production pnpm -F server check",
"setup": "node scripts/setup.js",
"setup:test": "bun scripts/setup-test.ts",
"tests": "bun scripts/test.ts",
"setupci": "node scripts/setupci.js",
"replicate": "bun scripts/replicate.ts",
"db:push": " bun -F @autumn/shared db:push",
"db:generate": "bun -F @autumn/shared db:generate",
"db:migrate": " bun -F @autumn/shared db:migrate",
"docker:up": "docker compose -f docker-compose.dev.yml up --build",
"docker:up:unix": "docker compose -f docker-compose.unix.yml up --build",
"docker:up:ci": "docker compose -f docker-compose.ci.yml up --build",
"vite:build:bun": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
"vite:start:bun": "bun -F @autumn/shared build && bun -F @autumn/vite start:bun"
},
"dependencies": {
"@wooorm/starry-night": "^3.8.0",
"ag-charts-react": "^12.3.0",
"chalk": "^5.6.2",
"drizzle-kit": "^0.31.5",
"tailwind-scrollbar-hide": "^4.0.0"
},
"devDependencies": {
"@biomejs/biome": "^2.2.7",
"@types/node": "^24.9.1",
"concurrently": "^9.2.1",
"dotenv": "^16.6.1",
"inquirer": "^12.10.0"
}
"name": "autumn",
"private": true,
"workspaces": {
"packages": [
"server",
"shared",
"vite",
"scripts"
],
"catalog": {
"stripe": "18.4.0-beta.2"
}
},
"type": "module",
"scripts": {
"dev": "node scripts/start-dev.js",
"dev:simple": "concurrently \"cd shared && bun dev:watch\" \"cd server && bun dev\" \"cd server && bun workers:dev\" \"cd vite && bun dev\"",
"vite:build": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
"vite:start": "bun -F @autumn/vite start:bun",
"shared": "bun -F @autumn/shared build",
"server": "bun -F @autumn/shared build && bun -F @autumn/server start",
"workers": "bun -F @autumn/shared build && bun -F @autumn/server workers",
"cron": "bun -F @autumn/shared build && bun -F @autumn/server cron",
"check": "bun -F @autumn/shared build && bun -F @autumn/server check",
"server:cron": "pnpm -F server cron:start",
"server:check": "NODE_ENV=production pnpm -F server check",
"setup": "node scripts/setup.js",
"setup:test": "bun scripts/setup-test.ts",
"tests": "bun scripts/test.ts",
"setupci": "node scripts/setupci.js",
"replicate": "bun scripts/replicate.ts",
"db:push": " bun -F @autumn/shared db:push",
"db:generate": "bun -F @autumn/shared db:generate",
"db:migrate": " bun -F @autumn/shared db:migrate",
"docker:up": "docker compose -f docker-compose.dev.yml up --build",
"docker:up:unix": "docker compose -f docker-compose.unix.yml up --build",
"docker:up:ci": "docker compose -f docker-compose.ci.yml up --build",
"vite:build:bun": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
"vite:start:bun": "bun -F @autumn/shared build && bun -F @autumn/vite start:bun"
},
"dependencies": {
"@wooorm/starry-night": "^3.8.0",
"ag-charts-react": "^12.3.0",
"chalk": "^5.6.2",
"drizzle-kit": "^0.31.5",
"tailwind-scrollbar-hide": "^4.0.0"
},
"devDependencies": {
"@biomejs/biome": "^2.2.7",
"@types/node": "^24.9.1",
"concurrently": "^9.2.1",
"dotenv": "^16.6.1",
"inquirer": "^12.10.0",
"@types/bun": "latest"
},
"module": "index.ts",
"peerDependencies": {
"typescript": "^5"
}
}

View File

@@ -0,0 +1 @@
../../CLAUDE.md

111
server/CLAUDE.md Normal file
View File

@@ -0,0 +1,111 @@
---
description: Use Bun instead of Node.js, npm, pnpm, or vite.
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
alwaysApply: false
---
Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Bun automatically loads .env, so don't use dotenv.
## APIs
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.
## Testing
Use `bun test` to run tests.
```ts#index.test.ts
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
```
## Frontend
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
Server:
```ts#index.ts
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```
With the following `frontend.tsx`:
```tsx#frontend.tsx
import React from "react";
// import .css files directly and it works
import './index.css';
import { createRoot } from "react-dom/client";
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
```
Then, run index.ts
```sh
bun --hot ./index.ts
```
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.

15
server/README.md Normal file
View File

@@ -0,0 +1,15 @@
# @autumn/server
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.js
```
This project was created using `bun init` in bun v1.3.2. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.

View File

@@ -1,127 +1,130 @@
{
"name": "@autumn/server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"email": "email dev -p 3001",
"start": "bun src/index.ts",
"dev": "cross-env NODE_ENV=development bunx nodemon -w ../shared/dist -w src --ext js,ts --ignore '../shared/dist/**/*.d.ts' --ignore scripts --ignore tests --exec bun src/index.ts",
"workers:dev": "cross-env NODE_ENV=development bunx nodemon -w ../shared/dist -w src/workers.ts -w src/queue --ext js,ts --ignore '../shared/dist/**/*.d.ts' --ignore scripts --ignore tests --exec bun src/workers.ts",
"workers": "bun src/workers.ts",
"cron": "bun src/cron.ts",
"check": "bun src/check.ts",
"build": "bun build ./src/index.ts ./src/workers.ts ./src/cron.ts --outdir dist --target bun",
"build:check": "tsc -b tsconfig.build.json"
},
"mocha": {
"node-option": [
"import=tsx"
]
},
"author": "Recase Inc.",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/anthropic": "^1.2.10",
"@amplitude/analytics-node": "^1.5.18",
"@anthropic-ai/sdk": "^0.32.1",
"@autumn/shared": "workspace:*",
"@axiomhq/pino": "^1.3.1",
"@browserbasehq/sdk": "^2.6.0",
"@clerk/express": "^1.3.22",
"@clickhouse/client": "^1.11.2",
"@date-fns/tz": "^1.2.0",
"@date-fns/utc": "^2.1.0",
"@hono/node-server": "^1.19.5",
"@hono/zod-validator": "^0.7.3",
"@hyperbrowser/sdk": "^0.54.0",
"@hyperdx/node-opentelemetry": "^0.8.2",
"@logtail/node": "^0.5.2",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/auto-instrumentations-node": "^0.60.1",
"@opentelemetry/exporter-trace-otlp-proto": "^0.202.0",
"@opentelemetry/resources": "^2.0.1",
"@opentelemetry/sdk-metrics": "^2.0.1",
"@opentelemetry/sdk-node": "^0.202.0",
"@opentelemetry/sdk-trace-base": "^2.0.1",
"@opentelemetry/sdk-trace-node": "^2.0.1",
"@opentelemetry/semantic-conventions": "^1.34.0",
"@react-email/components": "^0.0.42",
"@sentry/node": "^9.30.0",
"@supabase/supabase-js": "^2.46.2",
"@types/qs": "^6.14.0",
"@types/semver": "^7.7.1",
"@upstash/redis": "^1.35.1",
"ai": "^4.3.10",
"autumn-js": "^0.1.8",
"axios": "^1.8.3",
"better-auth": "^1.2.9",
"body-parser": "^1.20.3",
"bullmq": "^5.56.2",
"chai": "^5.1.2",
"chai-http": "^5.1.1",
"chalk": "^5.3.0",
"cloudflare": "^4.4.1",
"cors": "^2.8.5",
"cron": "^3.5.0",
"csv-parse": "^5.6.0",
"csv-parser": "^3.2.0",
"currency-symbol-map": "^5.1.0",
"date-fns": "^4.1.0",
"decimal.js": "^10.5.0",
"detect-content-type": "^1.2.0",
"dotenv": "^16.5.0",
"drizzle-orm": "^0.43.1",
"express": "^4.21.1",
"express-rate-limit": "^7.5.1",
"fetch-retry": "^6.0.0",
"hono": "^4.9.9",
"http-status-codes": "^2.3.0",
"ioredis": "^5.5.0",
"ksuid": "^3.0.0",
"lodash-es": "^4.17.21",
"loops": "^5.0.1",
"mime-detect": "^1.3.0",
"nanoid": "^5.1.6",
"openai": "^4.85.2",
"pg": "^8.13.1",
"pino": "^9.6.0",
"pino-pretty": "^13.0.0",
"postgres": "^3.4.7",
"posthog-node": "^4.17.2",
"puppeteer-core": "^24.14.0",
"qs": "^6.14.0",
"react": "^18.2.0",
"recaseai": "^0.0.37",
"resend": "^4.1.1",
"semver": "^7.7.2",
"stripe": "18.4.0-beta.2",
"svix": "^1.45.1",
"tsc-alias": "^1.8.16",
"ws": "^8.18.0",
"zod": "^3.25.23"
},
"devDependencies": {
"@types/chai": "^5.0.1",
"@types/chai-http": "^3.0.5",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/lodash-es": "^4.17.12",
"@types/mocha": "^10.0.10",
"@types/node": "^24.9.1",
"@types/pg": "^8.11.10",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/ws": "^8.18.1",
"cross-env": "^7.0.3",
"drizzle-kit": "^0.31.1",
"mocha": "^11.1.0",
"nodemon": "^3.1.10",
"react-email": "4.0.16",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.19.4",
"typescript": "^5.7.3"
}
"name": "@autumn/server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"email": "email dev -p 3001",
"start": "bun src/index.ts",
"dev": "cross-env NODE_ENV=development bunx nodemon -w ../shared/dist -w src --ext js,ts --ignore '../shared/dist/**/*.d.ts' --ignore scripts --ignore tests --exec bun src/index.ts",
"workers:dev": "cross-env NODE_ENV=development bunx nodemon -w ../shared/dist -w src/workers.ts -w src/queue --ext js,ts --ignore '../shared/dist/**/*.d.ts' --ignore scripts --ignore tests --exec bun src/workers.ts",
"workers": "bun src/workers.ts",
"cron": "bun src/cron.ts",
"check": "bun src/check.ts",
"build": "bun build ./src/index.ts ./src/workers.ts ./src/cron.ts --outdir dist --target bun",
"build:check": "tsc -b tsconfig.build.json"
},
"mocha": {
"node-option": [
"import=tsx"
]
},
"author": "Recase Inc.",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/anthropic": "^1.2.10",
"@amplitude/analytics-node": "^1.5.18",
"@anthropic-ai/sdk": "^0.32.1",
"@autumn/shared": "workspace:*",
"@axiomhq/pino": "^1.3.1",
"@browserbasehq/sdk": "^2.6.0",
"@clerk/express": "^1.3.22",
"@clickhouse/client": "^1.11.2",
"@date-fns/tz": "^1.2.0",
"@date-fns/utc": "^2.1.0",
"@hono/node-server": "^1.19.5",
"@hono/zod-validator": "^0.7.3",
"@hyperbrowser/sdk": "^0.54.0",
"@hyperdx/node-opentelemetry": "^0.8.2",
"@logtail/node": "^0.5.2",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/auto-instrumentations-node": "^0.60.1",
"@opentelemetry/exporter-trace-otlp-proto": "^0.202.0",
"@opentelemetry/resources": "^2.0.1",
"@opentelemetry/sdk-metrics": "^2.0.1",
"@opentelemetry/sdk-node": "^0.202.0",
"@opentelemetry/sdk-trace-base": "^2.0.1",
"@opentelemetry/sdk-trace-node": "^2.0.1",
"@opentelemetry/semantic-conventions": "^1.34.0",
"@react-email/components": "^0.0.42",
"@sentry/node": "^9.30.0",
"@supabase/supabase-js": "^2.46.2",
"@types/bun": "^1.3.2",
"@types/qs": "^6.14.0",
"@types/semver": "^7.7.1",
"@upstash/redis": "^1.35.1",
"ai": "^4.3.10",
"autumn-js": "^0.1.8",
"axios": "^1.8.3",
"better-auth": "^1.2.9",
"body-parser": "^1.20.3",
"bullmq": "^5.56.2",
"chai": "^5.1.2",
"chai-http": "^5.1.1",
"chalk": "^5.3.0",
"cloudflare": "^4.4.1",
"cors": "^2.8.5",
"cron": "^3.5.0",
"csv-parse": "^5.6.0",
"csv-parser": "^3.2.0",
"currency-symbol-map": "^5.1.0",
"date-fns": "^4.1.0",
"decimal.js": "^10.5.0",
"detect-content-type": "^1.2.0",
"dotenv": "^16.5.0",
"drizzle-orm": "^0.43.1",
"express": "^4.21.1",
"express-rate-limit": "^7.5.1",
"fetch-retry": "^6.0.0",
"hono": "^4.9.9",
"http-status-codes": "^2.3.0",
"ioredis": "^5.5.0",
"ksuid": "^3.0.0",
"lodash-es": "^4.17.21",
"loops": "^5.0.1",
"mime-detect": "^1.3.0",
"nanoid": "^5.1.6",
"openai": "^4.85.2",
"pg": "^8.13.1",
"pino": "^9.6.0",
"pino-pretty": "^13.0.0",
"postgres": "^3.4.7",
"posthog-node": "^4.17.2",
"puppeteer-core": "^24.14.0",
"qs": "^6.14.0",
"react": "^18.2.0",
"recaseai": "^0.0.37",
"resend": "^4.1.1",
"semver": "^7.7.2",
"stripe": "catalog:",
"svix": "^1.45.1",
"tsc-alias": "^1.8.16",
"ws": "^8.18.0",
"zod": "^3.25.23"
},
"devDependencies": {
"@types/chai": "^5.0.1",
"@types/chai-http": "^3.0.5",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/lodash-es": "^4.17.12",
"@types/mocha": "^10.0.10",
"@types/node": "^24.9.1",
"@types/pg": "^8.11.10",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/ws": "^8.18.1",
"cross-env": "^7.0.3",
"drizzle-kit": "^0.31.1",
"mocha": "^11.1.0",
"nodemon": "^3.1.10",
"react-email": "4.0.16",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.19.4",
"typescript": "^5.7.3",
"@types/bun": "latest"
},
"private": true
}

View File

@@ -102,7 +102,6 @@ export const handleScheduleFunction2 = async ({
});
const currentPhaseIndex = getCurrentPhaseIndex({
// biome-ignore lint/suspicious/noExplicitAny: ok
schedule: { phases: newItems.phases } as any,
now: attachParams.now,
});

View File

@@ -33,9 +33,10 @@ export const subToNewSchedule = async ({
attachParams,
includeCanceled: true,
});
console.log(
`REMOVING CUS PRODUCTS: ${cusProductsToRemove.map((cp) => `${cp.product.id} (E: ${cp.entity_id})`).join(", ")}`,
); // See mergedDowngrade5.test.ts -- if there's no schedule and creating one, might need to include canceled cus products
);
const res = await paramsToScheduleItems({
req,

View File

@@ -158,6 +158,7 @@ export const handleCancelProduct = async ({
replaceables: [],
entities: fullCus.entities,
features: req.features,
fromCancel: true,
},
config: getDefaultAttachConfig(),
skipInsertCusProduct,

View File

@@ -1,9 +1,10 @@
import { beforeAll, describe, test } from "bun:test";
import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared";
import { TestFeature } from "@tests/setup/v2Features.js";
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
import chalk from "chalk";
import type { Stripe } from "stripe";
import { TestFeature } from "tests/setup/v2Features.js";
import ctx from "tests/utils/testInitUtils/createTestContext.js";
import type { DrizzleCli } from "@/db/initDrizzle.js";
import { AutumnInt } from "@/external/autumn/autumnCli.js";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
@@ -64,4 +65,18 @@ describe(`${chalk.yellowBright("temp1: Testing add ons")}`, () => {
product_id: addOn.id,
});
});
// test("should cancel one add on", async () => {
// await autumn.cancel({
// customer_id: customerId,
// product_id: addOn.id,
// });
// await expectSubToBeCorrect({
// customerId,
// db,
// org,
// env,
// });
// });
});

View File

@@ -1,9 +1,9 @@
import { beforeAll, describe, test } from "bun:test";
import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared";
import { TestFeature } from "@tests/setup/v2Features.js";
import chalk from "chalk";
import { addWeeks } from "date-fns";
import type { Stripe } from "stripe";
import { TestFeature } from "tests/setup/v2Features.js";
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
import { advanceTestClock } from "tests/utils/stripeUtils.js";
import ctx from "tests/utils/testInitUtils/createTestContext.js";

View File

@@ -22,7 +22,8 @@
"@/*": ["src/*"],
"@shared/*": ["../shared/*"],
"@scripts/*": ["scripts/*"],
"@emails/*": ["emails/*"]
"@emails/*": ["emails/*"],
"@tests/*": ["tests/*"]
}
},
"include": ["src", "tests", "scripts", "emails"],

View File

@@ -1,3 +1,4 @@
import type { ZodOpenApiPathsObject } from "zod-openapi";
import {
CreateReferralCodeResponseSchema,
RedeemReferralCodeResponseSchema,
@@ -7,7 +8,7 @@ import {
RedeemReferralCodeParamsSchema,
} from "./referralOpModels.js";
export const referralOps = {
export const referralOps: ZodOpenApiPathsObject = {
"/referrals/code": {
post: {
summary: "Create a referral code",

View File

@@ -11,7 +11,19 @@ import type {
const changes = new Map<ApiVersion, VersionChangeConstructor[]>();
const instances = new Map<string, VersionChange>();
export const VersionChangeRegistryClass = {
export const VersionChangeRegistryClass: {
register(args: {
version: ApiVersion;
changes: VersionChangeConstructor[];
}): void;
getChangesForVersion(args: { version: ApiVersion }): VersionChange[];
getRegisteredVersions(): ApiVersion[];
isChangeActive(args: {
targetVersion: ApiVersion;
changeClass: VersionChangeConstructor;
}): boolean;
clear(): void;
} = {
register({
version,
changes: versionChanges,

View File

@@ -1,7 +1,7 @@
import { type ApiVersion, LATEST_VERSION } from "../ApiVersion.js";
import { ApiVersionClass } from "../ApiVersionClass.js";
import { getVersionsBetween } from "../versionRegistryUtils.js";
import type { AffectedResource } from "./VersionChange.js";
import type { AffectedResource, VersionChange } from "./VersionChange.js";
import { VersionChangeRegistryClass } from "./VersionChangeRegistryClass.js";
/**
@@ -290,7 +290,7 @@ export function getChangesForResource({
currentVersion: ApiVersion;
targetVersion: ApiVersion;
resource: AffectedResource;
}) {
}): VersionChange[] {
const versionsToApply = getVersionsBetween({
from: targetVersion,
to: currentVersion,

View File

@@ -24,6 +24,7 @@
"db:studio": "cross-env NODE_OPTIONS=\"--import tsx\" bunx drizzle-kit studio --config drizzle.config.ts"
},
"dependencies": {
"stripe": "catalog:",
"date-fns": "^4.1.0",
"decimal.js": "^10.5.0",
"dotenv": "^16.5.0",

29
tsconfig.json Normal file
View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}

View File

@@ -35,6 +35,7 @@ export default defineConfig({
"better-auth",
"better-auth/react",
"@better-auth/stripe",
"zod/v4",
],
},
// Clear cache on config change