@vonall/auth 0.1.1 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @vonall/auth
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - The Product builds the better-auth instance and client itself, so their types are inferred where they are used: `betterAuth({ ...authOptions({…}), plugins: [twoFactor(), admin({ ac, roles }), …, sveltekitCookies(getRequestEvent)] })`, then `registerAuth(auth)` for the session handle and the API handler; `createAuth()`, `createAuthClient()` and the `./client` entry are gone.
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - The plugins passed to `createAuth()` and `createAuthClient()` are inferred as tuples, so `authClient.twoFactor`, `authClient.admin` and the MCP plugins' members type-check in a Product.
14
+
3
15
  ## 0.1.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -11,23 +11,30 @@ import { authSchema } from "@vonall/auth/schema";
11
11
  export const schema = authSchema({ admin: true, mcp: true });
12
12
  export const { user, session, account, verification, twoFactor, rateLimit, einladung } = schema;
13
13
 
14
- // src/lib/auth.ts: the wiring, once
15
- import { createAuth } from "@vonall/auth/server";
16
- export const { auth, handle: sessionHandle, apiHandler } = createAuth({
17
- db, schema, secret: BETTER_AUTH_SECRET, origin: ORIGIN, appName: APP_NAME, locale: LOCALE, signupMode: SIGNUP_MODE,
18
- mailer, admin: true, social: { github: { clientId, clientSecret } }, log: logger,
14
+ // src/lib/auth.ts: the wiring, once; the instance is built here so its types (every plugin's) are inferred here
15
+ import { ac, roles } from "@vonall/auth";
16
+ import { authOptions, registerAuth } from "@vonall/auth/server";
17
+ import { betterAuth } from "better-auth";
18
+ import { admin } from "better-auth/plugins";
19
+ import { twoFactor } from "better-auth/plugins/two-factor";
20
+ import { sveltekitCookies } from "better-auth/svelte-kit";
21
+ export const auth = betterAuth({
22
+ ...authOptions({ db, schema, secret: BETTER_AUTH_SECRET, origin: ORIGIN, appName: APP_NAME, locale: LOCALE, signupMode: SIGNUP_MODE, mailer, admin: true, social: { github: { clientId, clientSecret } }, log: logger }),
23
+ plugins: [twoFactor(), admin({ ac, roles }), /* the MCP endpoint's, from @vonall/mcp */ sveltekitCookies(getRequestEvent)],
19
24
  });
25
+ export const { handle: sessionHandle, apiHandler, providers } = registerAuth(auth);
20
26
 
27
+ // src/lib/auth-client.ts createAuthClient({ baseURL: PUBLIC_ORIGIN, plugins: [twoFactorClient(), adminClient({ ac, roles })] }) (from "better-auth/svelte")
21
28
  // src/hooks.server.ts sequence(securityHeaders, sessionHandle)
22
29
  // src/routes/api/auth/[...auth] export const GET = apiHandler, POST = apiHandler
23
30
  // src/routes/auth/login export { loginLoad as load } from "@vonall/auth/server"
24
- // src/app.d.ts user: AuthUser | null; session: AuthSession | null (from "@vonall/auth/server")
25
- // src/lib/auth-client.ts createAuthClient({ baseURL: PUBLIC_ORIGIN, admin: true }) (from "@vonall/auth/client")
31
+ // src/app.d.ts user: typeof auth.$Infer.Session.user | null; session: typeof auth.$Infer.Session.session | null
26
32
  ```
27
33
 
28
- Entry points: `@vonall/auth` (types, validation schemas, `isAdmin`, `mayInvite`, the fresh-session helpers, the
29
- messages), `@vonall/auth/schema`, `@vonall/auth/server`, `@vonall/auth/client`, `@vonall/auth/remote` (the remote
30
- functions a Product's components call), `@vonall/auth/testing` (`createUser`, `testAuth` for db tests).
34
+ Entry points: `@vonall/auth` (types, validation schemas, `ac`/`roles` for the admin plugins, `isAdmin`, `mayInvite`, the
35
+ fresh-session helpers, the messages), `@vonall/auth/schema`, `@vonall/auth/server`, `@vonall/auth/remote` (the remote
36
+ functions a Product's components call), `@vonall/auth/testing` (`createUser`, `testAuth` for db tests). `better-auth` is
37
+ this package's dependency; the Product imports its plugins from the same version.
31
38
 
32
39
  Strings come in English and German: `configureAuthMessages({ locale, overrides })`. A Product's own user-owned tables
33
40
  reference `user.id` with `onDelete: "cascade"`, so deleting a Konto takes everything with it. The MCP endpoint's
@@ -1,8 +1,8 @@
1
1
  export declare const ac: {
2
- newRole<const TRoleStatements extends import("better-auth/client").Statements>(statements: import("better-auth/client").RoleInput<{
2
+ newRole<const TRoleStatements extends import("better-auth/plugins").Statements>(statements: import("better-auth/plugins").RoleInput<{
3
3
  readonly user: readonly ["create", "list", "set-role", "ban", "impersonate", "impersonate-admins", "delete", "set-password", "set-email", "get", "update"];
4
4
  readonly session: readonly ["list", "revoke", "delete"];
5
- }, TRoleStatements>): import("better-auth/client").Role<import("better-auth/client").ExactRoleStatements<TRoleStatements>, {
5
+ }, TRoleStatements>): import("better-auth/plugins").Role<import("better-auth/plugins").ExactRoleStatements<TRoleStatements>, {
6
6
  readonly user: readonly ["create", "list", "set-role", "ban", "impersonate", "impersonate-admins", "delete", "set-password", "set-email", "get", "update"];
7
7
  readonly session: readonly ["list", "revoke", "delete"];
8
8
  }>;
@@ -15,14 +15,14 @@ export declare const ac: {
15
15
  * The app admin manages accounts but can't act as or take over a friend's account: no impersonation, no setting a
16
16
  * password or an email, no creating accounts (signup only through an Einladung).
17
17
  */
18
- export declare const adminRole: import("better-auth/client").Role<import("better-auth/client").ExactRoleStatements<{
18
+ export declare const adminRole: import("better-auth/plugins").Role<import("better-auth/plugins").ExactRoleStatements<{
19
19
  readonly user: ["list", "get", "set-role", "ban", "delete"];
20
20
  readonly session: ["list", "revoke"];
21
21
  }>, {
22
22
  readonly user: readonly ["create", "list", "set-role", "ban", "impersonate", "impersonate-admins", "delete", "set-password", "set-email", "get", "update"];
23
23
  readonly session: readonly ["list", "revoke", "delete"];
24
24
  }>;
25
- export declare const userRole: import("better-auth/client").Role<import("better-auth/client").ExactRoleStatements<{
25
+ export declare const userRole: import("better-auth/plugins").Role<import("better-auth/plugins").ExactRoleStatements<{
26
26
  readonly user: [];
27
27
  readonly session: [];
28
28
  }>, {
@@ -31,14 +31,14 @@ export declare const userRole: import("better-auth/client").Role<import("better-
31
31
  }>;
32
32
  /** The roles as better-auth's admin plugin and client take them. */
33
33
  export declare const roles: {
34
- admin: import("better-auth/client").Role<import("better-auth/client").ExactRoleStatements<{
34
+ admin: import("better-auth/plugins").Role<import("better-auth/plugins").ExactRoleStatements<{
35
35
  readonly user: ["list", "get", "set-role", "ban", "delete"];
36
36
  readonly session: ["list", "revoke"];
37
37
  }>, {
38
38
  readonly user: readonly ["create", "list", "set-role", "ban", "impersonate", "impersonate-admins", "delete", "set-password", "set-email", "get", "update"];
39
39
  readonly session: readonly ["list", "revoke", "delete"];
40
40
  }>;
41
- user: import("better-auth/client").Role<import("better-auth/client").ExactRoleStatements<{
41
+ user: import("better-auth/plugins").Role<import("better-auth/plugins").ExactRoleStatements<{
42
42
  readonly user: [];
43
43
  readonly session: [];
44
44
  }>, {
@@ -3,7 +3,7 @@ import { getRequestEvent } from "$app/server";
3
3
  import { z } from "zod";
4
4
  import { m } from "../messages.js";
5
5
  import { adminQuery, adminSchemaCommand, protectedCommand, protectedForm, protectedQuery, protectedSchemaCommand } from "../server/helpers.js";
6
- import { registry } from "../server/registry.js";
6
+ import { authInstance } from "../server/registry.js";
7
7
  import { UserRepo } from "../server/repositories/UserRepo.js";
8
8
  import { KontoService } from "../server/service/konto.service.js";
9
9
  import { emailSchema, nameSchema, passwordSchema } from "../types.js";
@@ -13,7 +13,7 @@ export const getAllUsers = adminQuery(async () => {
13
13
  return await UserRepo.getAllUsers();
14
14
  });
15
15
  export const updateUserProfile = protectedForm(z.object({ name: nameSchema, email: emailSchema }), async (data, _issue, user) => {
16
- const { auth } = registry();
16
+ const auth = authInstance();
17
17
  const event = getRequestEvent();
18
18
  if (data.email !== user.email) {
19
19
  await auth.api.changeEmail({ body: { newEmail: data.email }, headers: event.request.headers });
@@ -25,7 +25,7 @@ const passwordPair = { newPassword: passwordSchema, confirmPassword: z.string()
25
25
  const passwordsMatch = { error: () => m().passwordsMismatch, path: ["confirmPassword"] };
26
26
  export const changePassword = protectedForm(z.object({ currentPassword: passwordSchema, ...passwordPair }).refine((d) => d.newPassword === d.confirmPassword, passwordsMatch), async (data) => {
27
27
  const event = getRequestEvent();
28
- await registry().auth.api.changePassword({
28
+ await authInstance().api.changePassword({
29
29
  body: { currentPassword: data.currentPassword, newPassword: data.newPassword, revokeOtherSessions: true },
30
30
  headers: event.request.headers,
31
31
  });
@@ -33,13 +33,13 @@ export const changePassword = protectedForm(z.object({ currentPassword: password
33
33
  });
34
34
  export const setPassword = protectedForm(z.object(passwordPair).refine((d) => d.newPassword === d.confirmPassword, passwordsMatch), async (data) => {
35
35
  const event = getRequestEvent();
36
- await registry().auth.api.setPassword({ body: { newPassword: data.newPassword }, headers: event.request.headers });
36
+ await authInstance().api.setPassword({ body: { newPassword: data.newPassword }, headers: event.request.headers });
37
37
  return { success: true };
38
38
  });
39
39
  export const getSessions = protectedQuery(async () => {
40
40
  const event = getRequestEvent();
41
41
  try {
42
- return await registry().auth.api.listSessions({ headers: event.request.headers });
42
+ return await authInstance().api.listSessions({ headers: event.request.headers });
43
43
  }
44
44
  catch (e) {
45
45
  // Only a login from the last day may list the sessions; the sessions are in the settings
@@ -51,7 +51,7 @@ export const getSessions = protectedQuery(async () => {
51
51
  export const revokeSessionById = protectedSchemaCommand(z.object({ sessionId: z.string() }), async (data) => {
52
52
  const event = getRequestEvent();
53
53
  const currentSessionToken = event.locals.session?.token;
54
- await registry().auth.api.revokeSession({ body: { token: data.sessionId }, headers: event.request.headers });
54
+ await authInstance().api.revokeSession({ body: { token: data.sessionId }, headers: event.request.headers });
55
55
  const isCurrentSession = data.sessionId === currentSessionToken;
56
56
  if (!isCurrentSession)
57
57
  await getSessions().refresh();
@@ -59,7 +59,7 @@ export const revokeSessionById = protectedSchemaCommand(z.object({ sessionId: z.
59
59
  });
60
60
  export const revokeOthers = protectedCommand(async () => {
61
61
  const event = getRequestEvent();
62
- await registry().auth.api.revokeOtherSessions({ headers: event.request.headers });
62
+ await authInstance().api.revokeOtherSessions({ headers: event.request.headers });
63
63
  await getSessions().refresh();
64
64
  return { success: true };
65
65
  });
@@ -1,10 +1,11 @@
1
1
  /**
2
- * `createAuth()`: better-auth for a Product, from explicit options. The Product's `src/lib/auth.ts` calls it once with
3
- * its database, its tables (`authSchema`), its env and config; everything else in this package reads the result.
2
+ * better-auth for a Product, from explicit options. The Product's `src/lib/auth.ts` builds the instance itself, so its
3
+ * types are inferred in the Product (its plugins included): `betterAuth({ ...authOptions({…}), plugins: […] })`, then
4
+ * `registerAuth(auth)` for the session handle and the API handler; everything else in this package reads the result.
4
5
  */
5
6
  import type { RequestHandler } from "@sveltejs/kit";
6
7
  import type { Handle } from "@sveltejs/kit/hooks";
7
- import { betterAuth, type BetterAuthPlugin } from "better-auth";
8
+ import { betterAuth, type BetterAuthOptions, type BetterAuthPlugin } from "better-auth";
8
9
  import { admin as adminPlugin } from "better-auth/plugins";
9
10
  import { twoFactor } from "better-auth/plugins/two-factor";
10
11
  import { sveltekitCookies } from "better-auth/svelte-kit";
@@ -17,7 +18,7 @@ export type SocialProvider = {
17
18
  clientId: string;
18
19
  clientSecret: string;
19
20
  };
20
- export type CreateAuthOptions<P extends BetterAuthPlugin[] = []> = {
21
+ export type CreateAuthOptions = {
21
22
  /** The Product's Drizzle database. */
22
23
  db: Db;
23
24
  /** The Product's tables from `authSchema()`, the same objects its `schema/` re-exports. */
@@ -39,8 +40,6 @@ export type CreateAuthOptions<P extends BetterAuthPlugin[] = []> = {
39
40
  github?: SocialProvider | null;
40
41
  google?: SocialProvider | null;
41
42
  };
42
- /** More better-auth plugins, e.g. the MCP endpoint's (`@vonall/mcp`); they are part of the instance's type. */
43
- plugins?: P;
44
43
  /** More rate limit rules, by path (`@vonall/mcp` adds `/oauth2/register`). */
45
44
  rateLimitRules?: Record<string, {
46
45
  window: number;
@@ -56,10 +55,19 @@ export type CreateAuthOptions<P extends BetterAuthPlugin[] = []> = {
56
55
  type BasePlugins = [ReturnType<typeof twoFactor>, ReturnType<typeof adminPlugin<{
57
56
  ac: typeof ac;
58
57
  roles: typeof roles;
59
- }>>];
58
+ }>>, ReturnType<typeof sveltekitCookies>];
60
59
  export type Auth<P extends BetterAuthPlugin[] = []> = ReturnType<typeof betterAuth<{
61
- plugins: [...BasePlugins, ...P, ReturnType<typeof sveltekitCookies>];
60
+ plugins: [...BasePlugins, ...P];
62
61
  }>>;
62
+ /**
63
+ * The Product's instance as `registerAuth()` takes it: built on `authOptions()`, with any plugins (their types stay the
64
+ * Product's; this package uses the instance through its own `Auth` type).
65
+ */
66
+ export type AuthLike = {
67
+ handler: (request: Request) => Promise<Response>;
68
+ api: object;
69
+ $context: Promise<object>;
70
+ };
63
71
  /** The admin plugin's columns, named here because the plugin's type doesn't carry them into the session's user. */
64
72
  export type AdminFields = {
65
73
  role?: string | null;
@@ -70,9 +78,8 @@ export type AdminFields = {
70
78
  /** The user and the session as the hooks put them into `locals` (a Product's `app.d.ts` uses these). */
71
79
  export type AuthUser = Auth["$Infer"]["Session"]["user"] & AdminFields;
72
80
  export type AuthSession = Auth["$Infer"]["Session"]["session"];
73
- export type CreatedAuth<P extends BetterAuthPlugin[] = []> = {
74
- /** The better-auth instance: `auth.api`, `auth.handler`, `auth.$context`. */
75
- auth: Auth<P>;
81
+ export declare function authOptions(options: CreateAuthOptions): BetterAuthOptions;
82
+ export type RegisteredAuth = {
76
83
  /** The hooks handle: the session into `locals.user`/`locals.session`, and better-auth's own routes (/api/auth/*). */
77
84
  handle: Handle;
78
85
  /** For `src/routes/api/auth/[...auth]/+server.ts`: `export const GET = apiHandler, POST = apiHandler`. */
@@ -83,5 +90,6 @@ export type CreatedAuth<P extends BetterAuthPlugin[] = []> = {
83
90
  google: boolean;
84
91
  };
85
92
  };
86
- export declare function createAuth<P extends BetterAuthPlugin[] = []>(options: CreateAuthOptions<P>): CreatedAuth<P>;
93
+ /** Hands the Product's instance to this package (after `authOptions()`) and returns the handles. */
94
+ export declare function registerAuth(auth: AuthLike): RegisteredAuth;
87
95
  export {};
@@ -8,7 +8,7 @@ import { sveltekitCookies, svelteKitHandler } from "better-auth/svelte-kit";
8
8
  import { m } from "../messages.js";
9
9
  import { roles, ac } from "../permissions.js";
10
10
  import { hashPassword, verifyPassword } from "./crypto.js";
11
- import { consoleLog, setRegistry } from "./registry.js";
11
+ import { consoleLog, registry, setRegistry } from "./registry.js";
12
12
  import { UserRepo } from "./repositories/UserRepo.js";
13
13
  import { EINLADUNG_COOKIE, EinladungService } from "./service/einladung.service.js";
14
14
  import { sendPasswordResetEmail, sendVerifyEmail } from "./service/mail.js";
@@ -24,10 +24,23 @@ function einladungAusCookie() {
24
24
  }
25
25
  }
26
26
  const hasKeys = (p) => Boolean(p?.clientId && p.clientSecret && p.clientId !== "dummy");
27
- function build(options) {
28
- const { secret, origin, appName, signupMode, admin = false, social = {}, plugins, rateLimitRules = {}, log = consoleLog } = options;
27
+ export function authOptions(options) {
28
+ const { secret, origin, appName, signupMode, admin = false, social = {}, rateLimitRules = {}, log = consoleLog } = options;
29
+ setRegistry({
30
+ db: options.db,
31
+ schema: options.schema,
32
+ appName,
33
+ origin,
34
+ locale: options.locale,
35
+ signupMode,
36
+ mailer: options.mailer,
37
+ admin,
38
+ log,
39
+ auth: null,
40
+ providers: { github: hasKeys(social.github), google: hasKeys(social.google) },
41
+ });
29
42
  const baseURL = origin || "http://localhost";
30
- return betterAuth({
43
+ return {
31
44
  appName,
32
45
  baseURL,
33
46
  secret: secret || "dummy-secret-for-build-purposes-only",
@@ -120,40 +133,22 @@ function build(options) {
120
133
  // an admin) and a ban count at once instead of after up to 5 minutes; for a small app that one query is nothing
121
134
  cookieCache: { enabled: false },
122
135
  },
123
- plugins: [
124
- twoFactor(),
125
- ...(admin ? [adminPlugin({ ac, roles })] : []),
126
- ...(plugins ?? []),
127
- sveltekitCookies(getRequestEvent), // Ensure cookies are populated in SvelteKit
128
- ],
129
- });
136
+ };
130
137
  }
131
- export function createAuth(options) {
132
- // Inside, the instance is used with this package's plugins only; the Product gets it with its own plugins' types too
133
- const auth = build(options);
134
- const { social = {} } = options;
135
- setRegistry({
136
- db: options.db,
137
- schema: options.schema,
138
- appName: options.appName,
139
- origin: options.origin,
140
- locale: options.locale,
141
- signupMode: options.signupMode,
142
- mailer: options.mailer,
143
- admin: options.admin ?? false,
144
- log: options.log ?? consoleLog,
145
- auth,
146
- providers: { github: hasKeys(social.github), google: hasKeys(social.google) },
147
- });
138
+ /** Hands the Product's instance to this package (after `authOptions()`) and returns the handles. */
139
+ export function registerAuth(auth) {
140
+ const current = registry();
141
+ setRegistry({ ...current, auth: auth });
142
+ const instance = auth;
148
143
  const handle = async ({ event, resolve }) => {
149
- const current = await auth.api.getSession({ headers: event.request.headers });
150
- if (current) {
151
- event.locals.session = current.session;
152
- event.locals.user = current.user;
144
+ const session = await instance.api.getSession({ headers: event.request.headers });
145
+ if (session) {
146
+ event.locals.session = session.session;
147
+ event.locals.user = session.user;
153
148
  }
154
149
  const { building } = await import("$app/env");
155
- return svelteKitHandler({ event, resolve, auth, building });
150
+ return svelteKitHandler({ event, resolve, auth: instance, building });
156
151
  };
157
152
  const apiHandler = ({ request }) => auth.handler(request);
158
- return { auth: auth, handle, apiHandler, providers: { github: hasKeys(social.github), google: hasKeys(social.google) } };
153
+ return { handle, apiHandler, providers: current.providers };
159
154
  }
@@ -1,7 +1,7 @@
1
- export { createAuth, type AdminFields, type Auth, type AuthSession, type AuthUser, type CreateAuthOptions, type CreatedAuth, type SocialProvider } from "./create.js";
1
+ export { authOptions, registerAuth, type AdminFields, type Auth, type AuthLike, type AuthSession, type AuthUser, type CreateAuthOptions, type RegisteredAuth, type SocialProvider } from "./create.js";
2
2
  export { adminCommand, adminQuery, adminSchemaCommand, adminSchemaQuery, protectedCommand, protectedForm, protectedQuery, protectedSchemaCommand, protectedSchemaQuery, requireAdmin, requireUser, } from "./helpers.js";
3
3
  export { authLayoutLoad, einladungLoad, homeLayoutLoad, loginLoad, manageUserLoad, signupLoad } from "./loads.js";
4
- export { registry as authRegistry, type AuthLog, type Db } from "./registry.js";
4
+ export { authInstance, registry as authRegistry, type AuthLog, type Db } from "./registry.js";
5
5
  export { EinladungRepo } from "./repositories/EinladungRepo.js";
6
6
  export { UserRepo } from "./repositories/UserRepo.js";
7
7
  export { EINLADUNG_COOKIE, EINLADUNG_GUELTIG_TAGE, EinladungService, type EinladungError } from "./service/einladung.service.js";
@@ -1,7 +1,7 @@
1
- export { createAuth } from "./create.js";
1
+ export { authOptions, registerAuth } from "./create.js";
2
2
  export { adminCommand, adminQuery, adminSchemaCommand, adminSchemaQuery, protectedCommand, protectedForm, protectedQuery, protectedSchemaCommand, protectedSchemaQuery, requireAdmin, requireUser, } from "./helpers.js";
3
3
  export { authLayoutLoad, einladungLoad, homeLayoutLoad, loginLoad, manageUserLoad, signupLoad } from "./loads.js";
4
- export { registry as authRegistry } from "./registry.js";
4
+ export { authInstance, registry as authRegistry } from "./registry.js";
5
5
  export { EinladungRepo } from "./repositories/EinladungRepo.js";
6
6
  export { UserRepo } from "./repositories/UserRepo.js";
7
7
  export { EINLADUNG_COOKIE, EINLADUNG_GUELTIG_TAGE, EinladungService } from "./service/einladung.service.js";
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { error, redirect } from "@sveltejs/kit";
6
6
  import { m } from "../messages.js";
7
- import { registry } from "./registry.js";
7
+ import { authInstance, registry } from "./registry.js";
8
8
  import { EINLADUNG_COOKIE, EinladungService } from "./service/einladung.service.js";
9
9
  import { isStale, SESSION_NOT_FRESH } from "../util/fresh.js";
10
10
  import { sameSitePath } from "../util/sameSite.js";
@@ -16,7 +16,7 @@ export async function authLayoutLoad() {
16
16
  }
17
17
  /** `/auth/login`. */
18
18
  export async function loginLoad({ locals, url, request }) {
19
- const { auth } = registry();
19
+ const auth = authInstance();
20
20
  // An action refused the session as too old to be trusted (reauthPath): sign it out, so the form shows. Only a session
21
21
  // that really is that old, so a link can't log anyone out.
22
22
  if (locals.session && url.searchParams.get("error") === SESSION_NOT_FRESH.toLowerCase()) {
@@ -27,7 +27,8 @@ export type Registry = {
27
27
  mailer: Mailer;
28
28
  admin: boolean;
29
29
  log: AuthLog;
30
- auth: Auth;
30
+ /** The Product's instance, from `registerAuth()`; null between `authOptions()` and that call. */
31
+ auth: Auth | null;
31
32
  /** The social logins that have keys. */
32
33
  providers: {
33
34
  github: boolean;
@@ -36,4 +37,6 @@ export type Registry = {
36
37
  };
37
38
  export declare function setRegistry(registry: Registry): void;
38
39
  export declare function registry(): Registry;
40
+ /** The Product's better-auth instance. */
41
+ export declare function authInstance(): Auth;
39
42
  export declare const consoleLog: AuthLog;
@@ -4,9 +4,16 @@ export function setRegistry(registry) {
4
4
  }
5
5
  export function registry() {
6
6
  if (!current)
7
- throw new Error("@vonall/auth: createAuth() has not run; import the app's auth wiring (src/lib/auth.ts) first");
7
+ throw new Error("@vonall/auth: authOptions() has not run; import the app's auth wiring (src/lib/auth.ts) first");
8
8
  return current;
9
9
  }
10
+ /** The Product's better-auth instance. */
11
+ export function authInstance() {
12
+ const auth = registry().auth;
13
+ if (!auth)
14
+ throw new Error("@vonall/auth: registerAuth(auth) has not run; the app's auth wiring (src/lib/auth.ts) calls it after betterAuth()");
15
+ return auth;
16
+ }
10
17
  export const consoleLog = {
11
18
  debug: () => { },
12
19
  info: (message, context) => console.info(message, context ?? ""),