@nifrajs/better-auth 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ Mount [better-auth](https://better-auth.com) into a [nifra](https://github.com/n
5
5
  session, 2FA, magic links, …) under `/api/auth/*`, plus typed `getSession` / `requireSession` guards
6
6
  for reading and protecting routes.
7
7
 
8
- This package has **no runtime dependency on better-auth** — it consumes your `auth` object
8
+ This package has **no runtime dependency on better-auth** - it consumes your `auth` object
9
9
  structurally, so your better-auth types flow through by inference and your tests need no DB.
10
10
 
11
11
  ## Install
@@ -51,14 +51,14 @@ works in both core handlers (`c.req`) and `@nifrajs/web` loaders/actions (`reque
51
51
  import { getSession } from "@nifrajs/better-auth"
52
52
 
53
53
  app.get("/me", async (c) => {
54
- const session = await getSession(auth, c.req) // { user, session } | null — fully typed
54
+ const session = await getSession(auth, c.req) // { user, session } | null - fully typed
55
55
  return session ? { email: session.user.email } : { email: null }
56
56
  })
57
57
  ```
58
58
 
59
59
  ## Protect a route
60
60
 
61
- `requireSession(auth, request, options?)` returns the non-null session or **throws a `Response`** —
61
+ `requireSession(auth, request, options?)` returns the non-null session or **throws a `Response`** -
62
62
  nifra returns a thrown `Response` as-is, short-circuiting the handler:
63
63
 
64
64
  ```ts
@@ -82,7 +82,7 @@ export const loader = async ({ request }) => {
82
82
  | Export | Description |
83
83
  | --- | --- |
84
84
  | `betterAuth(auth, options?)` | Plugin that mounts better-auth's handler at `${basePath}/*` for GET + POST. |
85
- | `getSession(auth, request)` | `Promise<SessionOf<A> \| null>` — typed wrapper over `auth.api.getSession`. |
85
+ | `getSession(auth, request)` | `Promise<SessionOf<A> \| null>` - typed wrapper over `auth.api.getSession`. |
86
86
  | `requireSession(auth, request, options?)` | Returns the session or throws a `Response` (302 `redirectTo` / 401). |
87
87
  | `SessionOf<A>` | The non-null session payload type inferred from your `auth`. |
88
88
  | `BetterAuthLike` | The structural contract a better-auth instance satisfies. |
@@ -93,7 +93,7 @@ MIT
93
93
 
94
94
  ## For AI agents
95
95
 
96
- Start with [`LLM.md`](./LLM.md) — this package's contract card (the exports you call + its footguns),
96
+ Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
97
97
  one cheap read instead of the whole corpus. For the wider framework: the repo's
98
98
  [`AGENTS.md`](../../AGENTS.md) is the copy-paste quick reference, and
99
99
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ import { type AnyServer, type Server } from "@nifrajs/core/server";
6
6
  * {@link getSession} / {@link requireSession} via inference.
7
7
  */
8
8
  export interface BetterAuthLike {
9
- /** better-auth's catch-all handler — serves every request under `basePath`. */
9
+ /** better-auth's catch-all handler - serves every request under `basePath`. */
10
10
  readonly handler: (request: Request) => Response | Promise<Response>;
11
11
  readonly api: {
12
12
  /** Resolve the session from request headers (cookie or bearer). Returns a nullable payload. */
@@ -18,7 +18,7 @@ export interface BetterAuthLike {
18
18
  * better-auth's resolved options; `basePath` (when set) defaults the mount path. Intersected with
19
19
  * `Record<string, unknown>` so a real better-auth instance (whose `options` is a large concrete object
20
20
  * with no structural overlap to a bare `{ basePath? }`) stays assignable to `BetterAuthLike` WITHOUT a
21
- * cast — keeping `A` concrete so `SessionOf<A>` recovers the real session type instead of collapsing
21
+ * cast - keeping `A` concrete so `SessionOf<A>` recovers the real session type instead of collapsing
22
22
  * to `{}` and forcing per-call session casts downstream.
23
23
  */
24
24
  readonly options?: {
@@ -36,8 +36,8 @@ export interface BetterAuthOptions {
36
36
  }
37
37
  /**
38
38
  * Mount a better-auth instance into a nifra app: registers its handler at `${basePath}/*`
39
- * (default `/api/auth/*`) for `GET` + `POST`, so every better-auth endpoint — sign-in/up/out, OAuth
40
- * callbacks, session, 2FA, magic links, … — is served by your nifra server.
39
+ * (default `/api/auth/*`) for `GET` + `POST`, so every better-auth endpoint - sign-in/up/out, OAuth
40
+ * callbacks, session, 2FA, magic links, … - is served by your nifra server.
41
41
  *
42
42
  * ```ts
43
43
  * import { betterAuth, requireSession } from "@nifrajs/better-auth"
@@ -47,12 +47,12 @@ export interface BetterAuthOptions {
47
47
  * .get("/me", async (c) => (await requireSession(auth, c.req)).user)
48
48
  * ```
49
49
  *
50
- * Idempotent (named `"better-auth"` — applying twice mounts once). Read the session with
50
+ * Idempotent (named `"better-auth"` - applying twice mounts once). Read the session with
51
51
  * {@link getSession} / {@link requireSession}, which infer your better-auth types.
52
52
  */
53
53
  export declare function betterAuth(auth: BetterAuthLike, options?: BetterAuthOptions): import("@nifrajs/core/server").IdentityPlugin;
54
54
  /**
55
- * Resolve the better-auth session for a request — a thin, typed wrapper over `auth.api.getSession`.
55
+ * Resolve the better-auth session for a request - a thin, typed wrapper over `auth.api.getSession`.
56
56
  * Returns `null` when unauthenticated. Takes the raw `Request` so it works in both server handlers
57
57
  * (`c.req`) and web loaders/actions (`request`).
58
58
  *
@@ -64,14 +64,14 @@ export declare function betterAuth(auth: BetterAuthLike, options?: BetterAuthOpt
64
64
  export declare function getSession<A extends BetterAuthLike>(auth: A, request: Request): Promise<SessionOf<A> | null>;
65
65
  /**
66
66
  * What {@link requireSession} does on a missing session: `302` to `redirectTo` (a same-origin path),
67
- * or — when omitted — a `401` JSON (`{ ok: false, error: "unauthorized" }`). Mirrors `@nifrajs/auth` guards.
67
+ * or - when omitted - a `401` JSON (`{ ok: false, error: "unauthorized" }`). Mirrors `@nifrajs/auth` guards.
68
68
  */
69
69
  export interface RequireSessionOptions {
70
70
  readonly redirectTo?: string;
71
71
  }
72
72
  /**
73
73
  * Require an authenticated better-auth session at the top of a protected handler/loader/action.
74
- * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) — nifra
74
+ * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) - nifra
75
75
  * returns a thrown `Response` as-is, short-circuiting the rest of the handler.
76
76
  *
77
77
  * ```ts
package/dist/index.js CHANGED
@@ -3,8 +3,8 @@ import { defineIdentityPlugin } from "@nifrajs/core/server";
3
3
  const HANDLED_METHODS = ["GET", "POST"];
4
4
  /**
5
5
  * Mount a better-auth instance into a nifra app: registers its handler at `${basePath}/*`
6
- * (default `/api/auth/*`) for `GET` + `POST`, so every better-auth endpoint — sign-in/up/out, OAuth
7
- * callbacks, session, 2FA, magic links, … — is served by your nifra server.
6
+ * (default `/api/auth/*`) for `GET` + `POST`, so every better-auth endpoint - sign-in/up/out, OAuth
7
+ * callbacks, session, 2FA, magic links, … - is served by your nifra server.
8
8
  *
9
9
  * ```ts
10
10
  * import { betterAuth, requireSession } from "@nifrajs/better-auth"
@@ -14,7 +14,7 @@ const HANDLED_METHODS = ["GET", "POST"];
14
14
  * .get("/me", async (c) => (await requireSession(auth, c.req)).user)
15
15
  * ```
16
16
  *
17
- * Idempotent (named `"better-auth"` — applying twice mounts once). Read the session with
17
+ * Idempotent (named `"better-auth"` - applying twice mounts once). Read the session with
18
18
  * {@link getSession} / {@link requireSession}, which infer your better-auth types.
19
19
  */
20
20
  export function betterAuth(auth, options = {}) {
@@ -22,7 +22,7 @@ export function betterAuth(auth, options = {}) {
22
22
  const pattern = `${base.replace(/\/+$/, "")}/*`; // strip trailing slash(es), then wildcard the subtree
23
23
  // A type-IDENTITY plugin (see defineIdentityPlugin): mounting the auth handler must NOT change the
24
24
  // route registry's type. A plain `definePlugin((app) => app)` would infer `app: Server<any, any>`, so
25
- // `use`'s result — and the entire typed client derived from it — collapsed to `any`. This was the #1
25
+ // `use`'s result - and the entire typed client derived from it - collapsed to `any`. This was the #1
26
26
  // reported anti-drift bug: routes declared after `.use(betterAuth(...))` silently lost their types.
27
27
  return defineIdentityPlugin("better-auth", (app) => {
28
28
  for (const method of HANDLED_METHODS) {
@@ -34,7 +34,7 @@ export function betterAuth(auth, options = {}) {
34
34
  });
35
35
  }
36
36
  /**
37
- * Resolve the better-auth session for a request — a thin, typed wrapper over `auth.api.getSession`.
37
+ * Resolve the better-auth session for a request - a thin, typed wrapper over `auth.api.getSession`.
38
38
  * Returns `null` when unauthenticated. Takes the raw `Request` so it works in both server handlers
39
39
  * (`c.req`) and web loaders/actions (`request`).
40
40
  *
@@ -53,7 +53,7 @@ const rejection = (options) => {
53
53
  if (to === undefined)
54
54
  return Response.json({ ok: false, error: "unauthorized" }, { status: 401 });
55
55
  // Same-origin guard (mirrors @nifrajs/web `redirect` + @nifrajs/auth guards): a single leading "/", never
56
- // "//host" or an absolute URL. `redirectTo` is dev-authored, so a bad value is a config bug — fail loud.
56
+ // "//host" or an absolute URL. `redirectTo` is dev-authored, so a bad value is a config bug - fail loud.
57
57
  if (!to.startsWith("/") || to.startsWith("//")) {
58
58
  throw new Error(`[nifra/better-auth] requireSession redirectTo must be a same-origin path beginning with "/" (got ${JSON.stringify(to)})`);
59
59
  }
@@ -61,7 +61,7 @@ const rejection = (options) => {
61
61
  };
62
62
  /**
63
63
  * Require an authenticated better-auth session at the top of a protected handler/loader/action.
64
- * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) — nifra
64
+ * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) - nifra
65
65
  * returns a thrown `Response` as-is, short-circuiting the rest of the handler.
66
66
  *
67
67
  * ```ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nifrajs/better-auth",
3
- "version": "2.1.0",
4
- "description": "Mount better-auth into a nifra app — one app.use() wires /api/auth/*, plus typed getSession/requireSession guards. Structural typing: no hard dependency on better-auth.",
3
+ "version": "2.3.0",
4
+ "description": "Mount better-auth into a nifra app - one app.use() wires /api/auth/*, plus typed getSession/requireSession guards. Structural typing: no hard dependency on better-auth.",
5
5
  "keywords": [
6
6
  "bun",
7
7
  "typescript",
@@ -40,7 +40,7 @@
40
40
  "access": "public"
41
41
  },
42
42
  "peerDependencies": {
43
- "@nifrajs/core": "^2.1.0",
43
+ "@nifrajs/core": "^2.3.0",
44
44
  "better-auth": ">=1.0.0"
45
45
  },
46
46
  "peerDependenciesMeta": {
package/src/index.ts CHANGED
@@ -7,7 +7,7 @@ import { type AnyServer, defineIdentityPlugin, type Server } from "@nifrajs/core
7
7
  * {@link getSession} / {@link requireSession} via inference.
8
8
  */
9
9
  export interface BetterAuthLike {
10
- /** better-auth's catch-all handler — serves every request under `basePath`. */
10
+ /** better-auth's catch-all handler - serves every request under `basePath`. */
11
11
  readonly handler: (request: Request) => Response | Promise<Response>
12
12
  readonly api: {
13
13
  /** Resolve the session from request headers (cookie or bearer). Returns a nullable payload. */
@@ -17,7 +17,7 @@ export interface BetterAuthLike {
17
17
  * better-auth's resolved options; `basePath` (when set) defaults the mount path. Intersected with
18
18
  * `Record<string, unknown>` so a real better-auth instance (whose `options` is a large concrete object
19
19
  * with no structural overlap to a bare `{ basePath? }`) stays assignable to `BetterAuthLike` WITHOUT a
20
- * cast — keeping `A` concrete so `SessionOf<A>` recovers the real session type instead of collapsing
20
+ * cast - keeping `A` concrete so `SessionOf<A>` recovers the real session type instead of collapsing
21
21
  * to `{}` and forcing per-call session casts downstream.
22
22
  */
23
23
  readonly options?: { readonly basePath?: string } & Record<string, unknown>
@@ -41,8 +41,8 @@ const HANDLED_METHODS = ["GET", "POST"] as const
41
41
 
42
42
  /**
43
43
  * Mount a better-auth instance into a nifra app: registers its handler at `${basePath}/*`
44
- * (default `/api/auth/*`) for `GET` + `POST`, so every better-auth endpoint — sign-in/up/out, OAuth
45
- * callbacks, session, 2FA, magic links, … — is served by your nifra server.
44
+ * (default `/api/auth/*`) for `GET` + `POST`, so every better-auth endpoint - sign-in/up/out, OAuth
45
+ * callbacks, session, 2FA, magic links, … - is served by your nifra server.
46
46
  *
47
47
  * ```ts
48
48
  * import { betterAuth, requireSession } from "@nifrajs/better-auth"
@@ -52,7 +52,7 @@ const HANDLED_METHODS = ["GET", "POST"] as const
52
52
  * .get("/me", async (c) => (await requireSession(auth, c.req)).user)
53
53
  * ```
54
54
  *
55
- * Idempotent (named `"better-auth"` — applying twice mounts once). Read the session with
55
+ * Idempotent (named `"better-auth"` - applying twice mounts once). Read the session with
56
56
  * {@link getSession} / {@link requireSession}, which infer your better-auth types.
57
57
  */
58
58
  export function betterAuth(auth: BetterAuthLike, options: BetterAuthOptions = {}) {
@@ -60,7 +60,7 @@ export function betterAuth(auth: BetterAuthLike, options: BetterAuthOptions = {}
60
60
  const pattern = `${base.replace(/\/+$/, "")}/*` // strip trailing slash(es), then wildcard the subtree
61
61
  // A type-IDENTITY plugin (see defineIdentityPlugin): mounting the auth handler must NOT change the
62
62
  // route registry's type. A plain `definePlugin((app) => app)` would infer `app: Server<any, any>`, so
63
- // `use`'s result — and the entire typed client derived from it — collapsed to `any`. This was the #1
63
+ // `use`'s result - and the entire typed client derived from it - collapsed to `any`. This was the #1
64
64
  // reported anti-drift bug: routes declared after `.use(betterAuth(...))` silently lost their types.
65
65
  return defineIdentityPlugin("better-auth", <S extends AnyServer>(app: S): S => {
66
66
  for (const method of HANDLED_METHODS) {
@@ -75,7 +75,7 @@ export function betterAuth(auth: BetterAuthLike, options: BetterAuthOptions = {}
75
75
  }
76
76
 
77
77
  /**
78
- * Resolve the better-auth session for a request — a thin, typed wrapper over `auth.api.getSession`.
78
+ * Resolve the better-auth session for a request - a thin, typed wrapper over `auth.api.getSession`.
79
79
  * Returns `null` when unauthenticated. Takes the raw `Request` so it works in both server handlers
80
80
  * (`c.req`) and web loaders/actions (`request`).
81
81
  *
@@ -95,7 +95,7 @@ export function getSession<A extends BetterAuthLike>(
95
95
 
96
96
  /**
97
97
  * What {@link requireSession} does on a missing session: `302` to `redirectTo` (a same-origin path),
98
- * or — when omitted — a `401` JSON (`{ ok: false, error: "unauthorized" }`). Mirrors `@nifrajs/auth` guards.
98
+ * or - when omitted - a `401` JSON (`{ ok: false, error: "unauthorized" }`). Mirrors `@nifrajs/auth` guards.
99
99
  */
100
100
  export interface RequireSessionOptions {
101
101
  readonly redirectTo?: string
@@ -105,7 +105,7 @@ const rejection = (options: RequireSessionOptions): Response => {
105
105
  const to = options.redirectTo
106
106
  if (to === undefined) return Response.json({ ok: false, error: "unauthorized" }, { status: 401 })
107
107
  // Same-origin guard (mirrors @nifrajs/web `redirect` + @nifrajs/auth guards): a single leading "/", never
108
- // "//host" or an absolute URL. `redirectTo` is dev-authored, so a bad value is a config bug — fail loud.
108
+ // "//host" or an absolute URL. `redirectTo` is dev-authored, so a bad value is a config bug - fail loud.
109
109
  if (!to.startsWith("/") || to.startsWith("//")) {
110
110
  throw new Error(
111
111
  `[nifra/better-auth] requireSession redirectTo must be a same-origin path beginning with "/" (got ${JSON.stringify(to)})`,
@@ -116,7 +116,7 @@ const rejection = (options: RequireSessionOptions): Response => {
116
116
 
117
117
  /**
118
118
  * Require an authenticated better-auth session at the top of a protected handler/loader/action.
119
- * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) — nifra
119
+ * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) - nifra
120
120
  * returns a thrown `Response` as-is, short-circuiting the rest of the handler.
121
121
  *
122
122
  * ```ts