fix(better-auth): keep package subpaths in sync with build entries (#11822)

This commit is contained in:
Gustavo Valverde
2026-10-15 14:08:25 +01:00
committed by GitHub
parent 88c9a37856
commit 414e845f12
3 changed files with 85 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"better-auth": patch
---
You can now import the Captcha and OpenAPI plugins from `better-auth/plugins/captcha` and `better-auth/plugins/open-api`, as you can for other plugins. Previously these imports failed with a "not exported" error. Imports from `better-auth/plugins` are unchanged.
If your TypeScript config uses `"moduleResolution": "node"` (or `node10`), `better-auth/plugins/multi-session` and `better-auth/test` now resolve their types. Previously TypeScript reported that it could not find their declarations. Projects using `bundler`, `node16`, or `nodenext` resolution are unaffected.
+22
View File
@@ -231,6 +231,11 @@
"types": "./dist/plugins/bearer/index.d.mts",
"default": "./dist/plugins/bearer/index.mjs"
},
"./plugins/captcha": {
"dev-source": "./src/plugins/captcha/index.ts",
"types": "./dist/plugins/captcha/index.d.mts",
"default": "./dist/plugins/captcha/index.mjs"
},
"./plugins/custom-session": {
"dev-source": "./src/plugins/custom-session/index.ts",
"types": "./dist/plugins/custom-session/index.d.mts",
@@ -291,6 +296,11 @@
"types": "./dist/plugins/one-tap/index.d.mts",
"default": "./dist/plugins/one-tap/index.mjs"
},
"./plugins/open-api": {
"dev-source": "./src/plugins/open-api/index.ts",
"types": "./dist/plugins/open-api/index.d.mts",
"default": "./dist/plugins/open-api/index.mjs"
},
"./plugins/phone-number": {
"dev-source": "./src/plugins/phone-number/index.ts",
"types": "./dist/plugins/phone-number/index.d.mts",
@@ -370,6 +380,9 @@
"api": [
"./dist/api/index.d.mts"
],
"test": [
"./dist/test-utils/index.d.mts"
],
"db": [
"./dist/db/index.d.mts"
],
@@ -430,6 +443,9 @@
"plugins/bearer": [
"./dist/plugins/bearer/index.d.mts"
],
"plugins/captcha": [
"./dist/plugins/captcha/index.d.mts"
],
"plugins/custom-session": [
"./dist/plugins/custom-session/index.d.mts"
],
@@ -451,12 +467,18 @@
"plugins/one-tap": [
"./dist/plugins/one-tap/index.d.mts"
],
"plugins/open-api": [
"./dist/plugins/open-api/index.d.mts"
],
"plugins/jwt": [
"./dist/plugins/jwt/index.d.mts"
],
"plugins/magic-link": [
"./dist/plugins/magic-link/index.d.mts"
],
"plugins/multi-session": [
"./dist/plugins/multi-session/index.d.mts"
],
"plugins/organization": [
"./dist/plugins/organization/index.d.mts"
],
@@ -0,0 +1,56 @@
import { readFileSync } from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
const pkgDir = path.resolve(import.meta.dirname, "..");
const pkg: {
exports: Record<string, { "dev-source": string; types: string }>;
typesVersions: { "*": Record<string, string[]> };
} = JSON.parse(readFileSync(path.join(pkgDir, "package.json"), "utf8"));
const entries = [
...readFileSync(path.join(pkgDir, "tsdown.config.ts"), "utf8").matchAll(
/"(\.\/src\/[^"]+\.ts)"/g,
),
].map((match) => match[1]!);
/**
* Build entries that intentionally have no subpath export.
*/
const internalEntries = new Set([
// Lazy-loaded by the built-in Kysely adapter; users import `@better-auth/kysely-adapter`.
"./src/adapters/kysely-adapter/index.ts",
]);
const toTypes = (entry: string) =>
entry.replace("./src/", "./dist/").replace(/\.ts$/, ".d.mts");
/**
* @see https://github.com/better-auth/better-auth/issues/10172
*/
describe("package.json subpaths", () => {
it("reads the build entries from tsdown.config.ts", () => {
expect(entries).toContain("./src/index.ts");
});
it.each(entries.filter((entry) => !internalEntries.has(entry)))(
"exposes %s through exports and typesVersions",
(entry) => {
const subpath = Object.keys(pkg.exports).find(
(key) => pkg.exports[key]!["dev-source"] === entry,
);
expect(subpath, `no exports entry for ${entry}`).toBeDefined();
expect(pkg.exports[subpath!]!.types).toBe(toTypes(entry));
expect(
pkg.typesVersions["*"][subpath === "." ? "*" : subpath!.slice(2)],
).toEqual([toTypes(entry)]);
},
);
it("builds every exported subpath", () => {
for (const [subpath, target] of Object.entries(pkg.exports)) {
expect(entries, `${subpath} is not a build entry`).toContain(
target["dev-source"],
);
}
});
});