@lunora/auth 0.0.0 → 1.0.0-alpha.1

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.
Files changed (41) hide show
  1. package/LICENSE.md +105 -0
  2. package/README.md +125 -9
  3. package/__assets__/package-og.svg +14 -0
  4. package/dist/adapter.d.mts +43 -0
  5. package/dist/adapter.d.ts +43 -0
  6. package/dist/adapter.mjs +47 -0
  7. package/dist/index.d.mts +386 -0
  8. package/dist/index.d.ts +386 -0
  9. package/dist/index.mjs +12 -0
  10. package/dist/middleware.d.mts +189 -0
  11. package/dist/middleware.d.ts +189 -0
  12. package/dist/middleware.mjs +53 -0
  13. package/dist/packem_shared/DEFAULT_AUTH_BASE_PATH-DjcUWEQl.mjs +11 -0
  14. package/dist/packem_shared/create-auth.d-M36jwG_Y.d.mts +58 -0
  15. package/dist/packem_shared/create-auth.d-M36jwG_Y.d.ts +58 -0
  16. package/dist/packem_shared/createAuth-B-tvsvQU.mjs +56 -0
  17. package/dist/packem_shared/createAuthAdmin-BxrfEeA_.mjs +249 -0
  18. package/dist/packem_shared/ensureMigrated-wZH3oXDu.mjs +28 -0
  19. package/dist/packem_shared/sessionPresets-B95rXrd8.mjs +35 -0
  20. package/dist/plugins-client.d.mts +2 -0
  21. package/dist/plugins-client.d.ts +2 -0
  22. package/dist/plugins-client.mjs +2 -0
  23. package/dist/plugins.d.mts +22 -0
  24. package/dist/plugins.d.ts +22 -0
  25. package/dist/plugins.mjs +22 -0
  26. package/dist/schema.d.mts +44 -0
  27. package/dist/schema.d.ts +44 -0
  28. package/dist/schema.mjs +62 -0
  29. package/dist/sql-store.d.mts +51 -0
  30. package/dist/sql-store.d.ts +51 -0
  31. package/dist/sql-store.mjs +162 -0
  32. package/dist/store.d.mts +66 -0
  33. package/dist/store.d.ts +66 -0
  34. package/dist/store.mjs +170 -0
  35. package/dist/turnstile-middleware.d.mts +80 -0
  36. package/dist/turnstile-middleware.d.ts +80 -0
  37. package/dist/turnstile-middleware.mjs +45 -0
  38. package/dist/turnstile.d.mts +98 -0
  39. package/dist/turnstile.d.ts +98 -0
  40. package/dist/turnstile.mjs +61 -0
  41. package/package.json +80 -18
@@ -0,0 +1,43 @@
1
+ import { createAdapterFactory } from 'better-auth/adapters';
2
+ import { d1Executor } from "./sql-store.mjs";
3
+ import { AuthStore } from "./store.mjs";
4
+ /**
5
+ * A better-auth database adapter backed by an {@link AuthStore} — the bridge
6
+ * that routes better-auth's reads and writes through Lunora's data layer
7
+ * instead of better-auth's built-in D1/Kysely adapter. Pass the result as
8
+ * `createAuth({ database: lunoraAuthAdapter(store) })`; better-auth's
9
+ * `createAdapterFactory` handles id generation, default values, field-name
10
+ * mapping and output shaping, so this only translates the cleaned CRUD calls
11
+ * onto the store.
12
+ *
13
+ * ```ts
14
+ * const auth = createAuth({
15
+ * secret: env.AUTH_SECRET,
16
+ * emailAndPassword: { enabled: true },
17
+ * database: lunoraAuthAdapter(lunoraStore), // lunoraStore writes via ctx.db
18
+ * });
19
+ * ```
20
+ *
21
+ * Scope: the {@link AuthStore} interface is single-table CRUD. better-auth's
22
+ * relational `join` reads (an advanced opt-in) are not handled — pair the
23
+ * adapter with `disableJoins` or let better-auth fall back to per-table reads.
24
+ */
25
+ declare const lunoraAuthAdapter: (store: AuthStore) => ReturnType<typeof createAdapterFactory>;
26
+ /**
27
+ * One-liner for the common case: a better-auth `database` backed by a Cloudflare
28
+ * D1 binding, via Lunora's SQL store — equivalent to
29
+ * `lunoraAuthAdapter(createSqlAuthStore(d1Executor(d1)))`.
30
+ *
31
+ * Prefer this over passing the raw `env.DB` as `database`. With raw D1,
32
+ * better-auth resolves its Kysely adapter through a runtime `await import(...)`
33
+ * inside `auth.$context`, and that dynamic import never settles under
34
+ * `@cloudflare/vite-plugin`'s worker runner — so it hangs *every* auth request
35
+ * in `pnpm dev` (a standalone `wrangler dev` or a deployed worker bundle it
36
+ * up-front, so they're unaffected — which makes the hang baffling to debug).
37
+ * This explicit adapter skips that import entirely, so dev and prod behave the
38
+ * same. The migration instance is the one exception — it wants raw `env.DB` so
39
+ * `ensureMigrated`'s Kysely migrator can create the tables (its `$context` is
40
+ * never resolved, so the hang doesn't apply there).
41
+ */
42
+ declare const lunoraD1Adapter: (d1: Parameters<typeof d1Executor>[0]) => ReturnType<typeof lunoraAuthAdapter>;
43
+ export { lunoraAuthAdapter, lunoraD1Adapter };
@@ -0,0 +1,43 @@
1
+ import { createAdapterFactory } from 'better-auth/adapters';
2
+ import { d1Executor } from "./sql-store.js";
3
+ import { AuthStore } from "./store.js";
4
+ /**
5
+ * A better-auth database adapter backed by an {@link AuthStore} — the bridge
6
+ * that routes better-auth's reads and writes through Lunora's data layer
7
+ * instead of better-auth's built-in D1/Kysely adapter. Pass the result as
8
+ * `createAuth({ database: lunoraAuthAdapter(store) })`; better-auth's
9
+ * `createAdapterFactory` handles id generation, default values, field-name
10
+ * mapping and output shaping, so this only translates the cleaned CRUD calls
11
+ * onto the store.
12
+ *
13
+ * ```ts
14
+ * const auth = createAuth({
15
+ * secret: env.AUTH_SECRET,
16
+ * emailAndPassword: { enabled: true },
17
+ * database: lunoraAuthAdapter(lunoraStore), // lunoraStore writes via ctx.db
18
+ * });
19
+ * ```
20
+ *
21
+ * Scope: the {@link AuthStore} interface is single-table CRUD. better-auth's
22
+ * relational `join` reads (an advanced opt-in) are not handled — pair the
23
+ * adapter with `disableJoins` or let better-auth fall back to per-table reads.
24
+ */
25
+ declare const lunoraAuthAdapter: (store: AuthStore) => ReturnType<typeof createAdapterFactory>;
26
+ /**
27
+ * One-liner for the common case: a better-auth `database` backed by a Cloudflare
28
+ * D1 binding, via Lunora's SQL store — equivalent to
29
+ * `lunoraAuthAdapter(createSqlAuthStore(d1Executor(d1)))`.
30
+ *
31
+ * Prefer this over passing the raw `env.DB` as `database`. With raw D1,
32
+ * better-auth resolves its Kysely adapter through a runtime `await import(...)`
33
+ * inside `auth.$context`, and that dynamic import never settles under
34
+ * `@cloudflare/vite-plugin`'s worker runner — so it hangs *every* auth request
35
+ * in `pnpm dev` (a standalone `wrangler dev` or a deployed worker bundle it
36
+ * up-front, so they're unaffected — which makes the hang baffling to debug).
37
+ * This explicit adapter skips that import entirely, so dev and prod behave the
38
+ * same. The migration instance is the one exception — it wants raw `env.DB` so
39
+ * `ensureMigrated`'s Kysely migrator can create the tables (its `$context` is
40
+ * never resolved, so the hang doesn't apply there).
41
+ */
42
+ declare const lunoraD1Adapter: (d1: Parameters<typeof d1Executor>[0]) => ReturnType<typeof lunoraAuthAdapter>;
43
+ export { lunoraAuthAdapter, lunoraD1Adapter };
@@ -0,0 +1,47 @@
1
+ import { createAdapterFactory } from 'better-auth/adapters';
2
+ import { createSqlAuthStore, d1Executor } from './sql-store.mjs';
3
+
4
+ const asRow = (row) => row;
5
+ const asRowOrNull = (row) => row ?? null;
6
+ const asRows = (rows) => rows;
7
+ const lunoraAuthAdapter = (store) => createAdapterFactory({
8
+ adapter: () => {
9
+ return {
10
+ consumeOne: async ({ model, where }) => asRowOrNull(await store.consumeOne(model, where)),
11
+ count: async ({ model, where }) => store.count(model, where ?? []),
12
+ create: async ({ data, model }) => asRow(await store.create(model, data)),
13
+ delete: async ({ model, where }) => {
14
+ await store.remove(model, where);
15
+ },
16
+ deleteMany: async ({ model, where }) => store.remove(model, where),
17
+ findMany: async ({ limit, model, offset, sortBy, where }) => asRows(await store.read(model, { limit, offset, sortBy, where: where ?? [] })),
18
+ findOne: async ({ model, where }) => {
19
+ const [row] = await store.read(model, { limit: 1, where });
20
+ return asRowOrNull(row);
21
+ },
22
+ update: async ({ model, update, where }) => {
23
+ const [row] = await store.update(model, where, update);
24
+ return asRowOrNull(row);
25
+ },
26
+ updateMany: async ({ model, update, where }) => {
27
+ const updated = await store.update(model, where, update);
28
+ return updated.length;
29
+ }
30
+ };
31
+ },
32
+ config: {
33
+ adapterId: "lunora",
34
+ adapterName: "Lunora Adapter",
35
+ // Conservative flags so the adapter is store-agnostic: better-auth
36
+ // serializes dates/booleans/json to primitives (string/number) before a
37
+ // write and parses them back after a read, so a store — in-memory or
38
+ // SQL — only ever handles primitives, never schema-aware codecs.
39
+ supportsBooleans: false,
40
+ supportsDates: false,
41
+ supportsJSON: false,
42
+ supportsNumericIds: false
43
+ }
44
+ });
45
+ const lunoraD1Adapter = (d1) => lunoraAuthAdapter(createSqlAuthStore(d1Executor(d1)));
46
+
47
+ export { lunoraAuthAdapter, lunoraD1Adapter };
@@ -0,0 +1,386 @@
1
+ export { lunoraAuthAdapter, lunoraD1Adapter } from "./adapter.mjs";
2
+ import { L as LunoraAuth, a as LunoraAuthOptions } from "./packem_shared/create-auth.d-M36jwG_Y.mjs";
3
+ export { c as createAuth } from "./packem_shared/create-auth.d-M36jwG_Y.mjs";
4
+ export { type LunoraAuthApiContext, LunoraAuthHeadersError, type WithAuthPluginsMiddleware, type WithAuthPluginsOptions, withAuthPlugins } from "./middleware.mjs";
5
+ export { default as authTables } from "./schema.mjs";
6
+ import { BetterAuthOptions } from 'better-auth';
7
+ export { type SqlExecutor, createSqlAuthStore, d1Executor } from "./sql-store.mjs";
8
+ export { type AuthQuery, type AuthRow, type AuthStore, type AuthWhereClause, createMemoryAuthStore, matchesWhere } from "./store.mjs";
9
+ export { type FetchLike, TURNSTILE_VERIFY_ENDPOINT, type TurnstileVerifyResult, type VerifyTurnstileOptions, verifyTurnstile } from "./turnstile.mjs";
10
+ export { type VerifyTurnstileMiddlewareOptions, verifyTurnstileMiddleware } from "./turnstile-middleware.mjs";
11
+ import 'better-auth/adapters';
12
+ import '@lunora/server';
13
+ /**
14
+ * A timestamp as it leaves the admin API: epoch-ms (better-auth stores `Date`s,
15
+ * which we normalize on output) or `null` when the column is unset.
16
+ */
17
+ type AuthTimestamp = null | number;
18
+ /**
19
+ * One user row as the admin API surfaces it. The fixed keys mirror better-auth's
20
+ * core `user` table plus the `admin()` plugin columns (`role`/`banned`/…); the
21
+ * index signature carries any app-defined `user.additionalFields` so callers
22
+ * (the studio) can render them generically. Password material never lives on
23
+ * this row (it's in the `account` table) and is never returned.
24
+ */
25
+ interface AuthAdminUser {
26
+ [key: string]: unknown;
27
+ banExpires?: AuthTimestamp;
28
+ banned?: boolean | null;
29
+ banReason?: null | string;
30
+ createdAt?: AuthTimestamp;
31
+ email?: null | string;
32
+ emailVerified?: boolean | null;
33
+ id: string;
34
+ image?: null | string;
35
+ name?: null | string;
36
+ role?: null | string;
37
+ updatedAt?: AuthTimestamp;
38
+ }
39
+ /**
40
+ * One session row as the admin API surfaces it. Mirrors better-auth's `session`
41
+ * table; `impersonatedBy` is set when the session was minted by
42
+ * {@link AuthAdmin.impersonateUser}. The signing `token` is stripped — it's a
43
+ * bearer credential, and the only place we hand one back is the explicit
44
+ * impersonation flow.
45
+ */
46
+ interface AuthAdminSession {
47
+ [key: string]: unknown;
48
+ createdAt?: AuthTimestamp;
49
+ expiresAt?: AuthTimestamp;
50
+ id: string;
51
+ impersonatedBy?: null | string;
52
+ ipAddress?: null | string;
53
+ userAgent?: null | string;
54
+ userId: string;
55
+ }
56
+ /** One linked account (a credential or OAuth provider). All token material is stripped. */
57
+ interface AuthAccount {
58
+ [key: string]: unknown;
59
+ accountId?: null | string;
60
+ createdAt?: AuthTimestamp;
61
+ id: string;
62
+ providerId?: null | string;
63
+ scope?: null | string;
64
+ userId: string;
65
+ }
66
+ /** One organization row (from the `organization` plugin). */
67
+ interface AuthOrganization {
68
+ [key: string]: unknown;
69
+ createdAt?: AuthTimestamp;
70
+ id: string;
71
+ name?: null | string;
72
+ slug?: null | string;
73
+ }
74
+ /** One organization membership row. */
75
+ interface AuthMember {
76
+ [key: string]: unknown;
77
+ createdAt?: AuthTimestamp;
78
+ id: string;
79
+ organizationId: string;
80
+ role?: null | string;
81
+ userId: string;
82
+ }
83
+ /** One pending organization invitation. */
84
+ interface AuthInvitation {
85
+ [key: string]: unknown;
86
+ email?: null | string;
87
+ expiresAt?: AuthTimestamp;
88
+ id: string;
89
+ organizationId: string;
90
+ role?: null | string;
91
+ status?: null | string;
92
+ }
93
+ /** One registered passkey. Credential secrets (`publicKey`) are stripped. */
94
+ interface AuthPasskey {
95
+ [key: string]: unknown;
96
+ createdAt?: AuthTimestamp;
97
+ deviceType?: null | string;
98
+ id: string;
99
+ name?: null | string;
100
+ userId: string;
101
+ }
102
+ /** A page of rows plus the unpaginated total. */
103
+ interface AuthPage<T> {
104
+ rows: T[];
105
+ total: number;
106
+ }
107
+ /**
108
+ * Which admin surfaces a given auth instance supports, derived from the enabled
109
+ * better-auth plugins (and any {@link CreateAuthAdminOptions.features} overrides).
110
+ * The studio calls {@link AuthAdmin.capabilities} once and renders only the
111
+ * panels whose capability is `true` — so a deployment that doesn't enable, say,
112
+ * the `organization` plugin never shows an Organizations section.
113
+ */
114
+ interface AuthCapabilities {
115
+ /** Linked-account browsing/unlinking — core (the `account` table always exists). */
116
+ accounts: boolean;
117
+ /** The `admin()` plugin: ban/role/impersonate/create/delete/set-password. */
118
+ admin: boolean;
119
+ /** The `organization` plugin: orgs, members, invitations. */
120
+ organization: boolean;
121
+ /** The `@better-auth/passkey` plugin: per-user passkeys. */
122
+ passkey: boolean;
123
+ /** The `two-factor` plugin: per-user 2FA status / disable. */
124
+ twoFactor: boolean;
125
+ }
126
+ /** A scalar value usable in an adapter `where` clause / filter. */
127
+ type WhereValue = boolean | number | string;
128
+ /** Filtering / paging options for {@link AuthAdmin.listUsers}. */
129
+ interface ListUsersOptions {
130
+ /** Column to filter on with `=` (defaults to `email` when `filterValue` is set). */
131
+ filterField?: string;
132
+ /** Exact-match filter value. */
133
+ filterValue?: WhereValue;
134
+ limit?: number;
135
+ offset?: number;
136
+ /** Substring search value (matched with `contains`). */
137
+ search?: string;
138
+ /** Column the `search` substring matches against (default `email`). */
139
+ searchField?: string;
140
+ /** Column to order by (default `createdAt`). */
141
+ sortBy?: string;
142
+ sortDirection?: "asc" | "desc";
143
+ }
144
+ /** The result of {@link AuthAdmin.impersonateUser}: a fresh session token to act as the target user. */
145
+ interface ImpersonationResult {
146
+ expiresAt: AuthTimestamp;
147
+ /** Bearer session token for the impersonated user. The caller is responsible for using it (e.g. setting the cookie). */
148
+ token: string;
149
+ user: AuthAdminUser;
150
+ }
151
+ /**
152
+ * The full read + write surface the studio's auth dashboard drives, backed by
153
+ * better-auth's tables. Returned by {@link createAuthAdmin}. The runtime accepts
154
+ * a structurally-compatible object as its `authAdmin` option and exposes each
155
+ * method behind an admin-token-gated endpoint. Methods whose backing plugin is
156
+ * absent still exist (they're not conditionally omitted) but the studio gates
157
+ * them on {@link AuthAdmin.capabilities}; calling one for an unconfigured plugin
158
+ * surfaces the underlying adapter error.
159
+ */
160
+ interface AuthAdmin {
161
+ banUser: (input: {
162
+ expiresInSeconds?: number;
163
+ reason?: string;
164
+ userId: string;
165
+ }) => Promise<AuthAdminUser>;
166
+ cancelInvitation: (input: {
167
+ invitationId: string;
168
+ }) => Promise<void>;
169
+ capabilities: () => Promise<AuthCapabilities>;
170
+ createUser: (input: {
171
+ data?: Record<string, unknown>;
172
+ email: string;
173
+ name: string;
174
+ password?: string;
175
+ role?: string | string[];
176
+ }) => Promise<AuthAdminUser>;
177
+ deletePasskey: (input: {
178
+ passkeyId: string;
179
+ }) => Promise<void>;
180
+ disableTwoFactor: (input: {
181
+ userId: string;
182
+ }) => Promise<void>;
183
+ impersonateUser: (input: {
184
+ userId: string;
185
+ }) => Promise<ImpersonationResult>;
186
+ listAccounts: (input: {
187
+ userId: string;
188
+ }) => Promise<AuthAccount[]>;
189
+ listInvitations: (options: {
190
+ limit?: number;
191
+ offset?: number;
192
+ organizationId: string;
193
+ }) => Promise<AuthPage<AuthInvitation>>;
194
+ listMembers: (options: {
195
+ limit?: number;
196
+ offset?: number;
197
+ organizationId: string;
198
+ }) => Promise<AuthPage<AuthMember>>;
199
+ listOrganizations: (options: {
200
+ limit?: number;
201
+ offset?: number;
202
+ }) => Promise<AuthPage<AuthOrganization>>;
203
+ listPasskeys: (input: {
204
+ userId: string;
205
+ }) => Promise<AuthPasskey[]>;
206
+ listSessions: (options: {
207
+ limit?: number;
208
+ offset?: number;
209
+ userId?: string;
210
+ }) => Promise<AuthPage<AuthAdminSession>>;
211
+ listUsers: (options: ListUsersOptions) => Promise<AuthPage<AuthAdminUser>>;
212
+ removeMember: (input: {
213
+ memberId: string;
214
+ }) => Promise<void>;
215
+ removeUser: (input: {
216
+ userId: string;
217
+ }) => Promise<void>;
218
+ revokeUserSession: (input: {
219
+ sessionId: string;
220
+ }) => Promise<void>;
221
+ revokeUserSessions: (input: {
222
+ userId: string;
223
+ }) => Promise<void>;
224
+ setRole: (input: {
225
+ role: string | string[];
226
+ userId: string;
227
+ }) => Promise<AuthAdminUser>;
228
+ setUserPassword: (input: {
229
+ newPassword: string;
230
+ userId: string;
231
+ }) => Promise<void>;
232
+ unbanUser: (input: {
233
+ userId: string;
234
+ }) => Promise<AuthAdminUser>;
235
+ unlinkAccount: (input: {
236
+ accountId: string;
237
+ userId: string;
238
+ }) => Promise<void>;
239
+ updateUser: (input: {
240
+ data: Record<string, unknown>;
241
+ userId: string;
242
+ }) => Promise<AuthAdminUser>;
243
+ }
244
+ /** Options for {@link createAuthAdmin}. */
245
+ interface CreateAuthAdminOptions {
246
+ /**
247
+ * Force individual {@link AuthCapabilities} on or off regardless of which
248
+ * plugins are detected — e.g. `{ impersonate: false }`-style opt-outs by
249
+ * setting `admin: false`, or hiding linked accounts with `accounts: false`.
250
+ * A capability is reported only when both its plugin is enabled *and* its
251
+ * override isn't `false`.
252
+ */
253
+ features?: Partial<AuthCapabilities>;
254
+ /**
255
+ * User id recorded as the impersonator on sessions minted by
256
+ * {@link AuthAdmin.impersonateUser}. Defaults to the impersonated user's own
257
+ * id (a self-reference) since the trusted admin plane has no acting user.
258
+ */
259
+ impersonatedBy?: string;
260
+ /**
261
+ * How long (in seconds) an impersonation session lives. Must be a positive
262
+ * finite integer. Capped at 24 × {@link DEFAULT_IMPERSONATION_SECONDS}
263
+ * (86 400 s / 24 h). Defaults to {@link DEFAULT_IMPERSONATION_SECONDS}
264
+ * (3 600 s / 1 h).
265
+ */
266
+ impersonationSeconds?: number;
267
+ }
268
+ /**
269
+ * A normalized failure from an admin operation. better-auth throws `APIError`s
270
+ * carrying a `body.code` (e.g. `USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL`); we
271
+ * surface that `code` so the runtime can map it onto an HTTP status and the
272
+ * studio can show a meaningful message instead of a generic 500.
273
+ */
274
+ declare class LunoraAuthAdminError extends Error {
275
+ readonly code: string;
276
+ constructor(message: string, code: string);
277
+ }
278
+ /**
279
+ * Build the studio's auth user-management plane on top of better-auth.
280
+ *
281
+ * Pass the result as the runtime's `authAdmin` option; the runtime exposes each
282
+ * method behind an admin-token-gated `/_lunora/admin/auth/*` endpoint. The set
283
+ * of usable surfaces is reported by {@link AuthAdmin.capabilities} — derived
284
+ * from the enabled better-auth plugins, so enabling `admin()`, `organization()`,
285
+ * `twoFactor()`, or the passkey plugin in the auth config is what lights up the
286
+ * matching dashboard panels.
287
+ *
288
+ * **Trust model — important.** These operations talk to better-auth's
289
+ * `internalAdapter` (and `adapter`/password hasher) **directly**, deliberately
290
+ * bypassing the plugins' own endpoints, which require the caller to hold an
291
+ * admin-role session. That session check is the wrong gate here: the runtime
292
+ * already authorizes every call with `LUNORA_ADMIN_TOKEN`, so this helper acts
293
+ * as a trusted server-side operator. It is therefore not an end-user-callable
294
+ * API — never expose it on a path that isn't admin-token gated.
295
+ *
296
+ * `auth.$context` is a promise (better-auth resolves the adapter, password
297
+ * config, etc. lazily); we memoize it so the first call pays the cost once.
298
+ */
299
+ declare const createAuthAdmin: (auth: LunoraAuth, options?: CreateAuthAdminOptions) => AuthAdmin;
300
+ /**
301
+ * Default basePath used by better-auth's client + handler. Override via the
302
+ * second argument if you mount the auth routes somewhere else.
303
+ */
304
+ declare const DEFAULT_AUTH_BASE_PATH: string;
305
+ /**
306
+ * Route an inbound `Request` to better-auth if the path falls under
307
+ * `basePath`; otherwise return `null` so the caller can continue dispatching.
308
+ *
309
+ * Better-auth handles arbitrarily nested paths (`/api/auth/sign-in/email`,
310
+ * `/api/auth/callback/github`, …), so we use prefix matching instead of the
311
+ * exact-path map `createWorker` consumes for top-level routes.
312
+ */
313
+ declare const handleAuthRequest: (auth: LunoraAuth, request: Request, basePath?: string) => Promise<Response | undefined>;
314
+ /**
315
+ * Apply better-auth's required schema (`user`, `session`, `account`,
316
+ * `verification`) to the configured database. Idempotent — better-auth
317
+ * diffs the existing schema and only runs the missing DDL.
318
+ *
319
+ * Cached per `options` reference so the diff cost (one PRAGMA-style sweep
320
+ * per table) doesn't fire on every request. In Cloudflare Workers the same
321
+ * `env.DB` binding is reused across invocations within a single isolate, so
322
+ * caching against the options object — which captures the binding — is
323
+ * sufficient.
324
+ *
325
+ * For production you should prefer pre-applying the schema at deploy time
326
+ * via `compileMigrationsSql` + `wrangler d1 execute`; this helper exists for
327
+ * dev/playground and small deployments.
328
+ */
329
+ declare const ensureMigrated: (auth: LunoraAuth | {
330
+ options: LunoraAuthOptions;
331
+ }) => Promise<void>;
332
+ /**
333
+ * Compile better-auth's migrations to a single SQL string. Useful for
334
+ * `wrangler d1 execute --file -` in CI so the deploy step applies the schema
335
+ * before the first user request.
336
+ */
337
+ declare const compileMigrationsSql: (options: LunoraAuthOptions) => Promise<string>;
338
+ /**
339
+ * Lunora-friendly view over better-auth's `session` option.
340
+ *
341
+ * This is a typed alias for better-auth's own `session` shape — Lunora stays a
342
+ * thin wrapper, so we don't reimplement the fields, we just give them a named,
343
+ * documented home so callers get autocomplete without reaching into
344
+ * `BetterAuthOptions`. The most relevant fields for session rotation / richer
345
+ * policies are `expiresIn` (absolute session lifetime, in seconds),
346
+ * `updateAge` (rolling-rotation interval, in seconds — how often an active
347
+ * session's expiry is pushed forward; `0` rotates on every use),
348
+ * `disableSessionRefresh` (turn rolling rotation off entirely so sessions
349
+ * expire at the absolute `expiresIn` regardless of activity), `freshAge`
350
+ * (freshness window, in seconds, for sensitive operations like account
351
+ * deletion; `0` treats every session as fresh — not recommended), and
352
+ * `cookieCache` (opt-in signed-cookie session cache to skip DB reads).
353
+ *
354
+ * See better-auth's `session` option for the full field list.
355
+ */
356
+ type SessionPolicy = NonNullable<BetterAuthOptions["session"]>;
357
+ /**
358
+ * Validate a {@link SessionPolicy} and return it unchanged (pass-through).
359
+ *
360
+ * better-auth happily accepts the `session` block verbatim, so the only job
361
+ * here is to catch obviously-broken numeric inputs at construction time —
362
+ * negative or non-finite durations — rather than letting them produce
363
+ * surprising cookie expiries at runtime. Field names mirror better-auth's
364
+ * `session` option exactly so the validated object forwards 1:1.
365
+ */
366
+ declare const validateSessionPolicy: (policy: SessionPolicy) => SessionPolicy;
367
+ /**
368
+ * Ready-made {@link SessionPolicy} presets covering the common rotation /
369
+ * expiry trade-offs. Spread or override fields as needed:
370
+ *
371
+ * ```ts
372
+ * import { createAuth, sessionPresets } from "@lunora/auth";
373
+ *
374
+ * export const auth = createAuth({
375
+ * secret: env.AUTH_SECRET,
376
+ * database: env.DB,
377
+ * session: { ...sessionPresets.rolling, freshAge: 60 * 5 },
378
+ * });
379
+ * ```
380
+ *
381
+ * - `rolling` — balanced default: 7-day absolute expiry, rotated once per day.
382
+ * - `strict` — short, security-sensitive: 1-hour expiry, 15-minute rotation.
383
+ * - `longLived` — low-friction consumer apps: 30-day expiry, daily rotation.
384
+ */
385
+ declare const sessionPresets: Record<"longLived" | "rolling" | "strict", SessionPolicy>;
386
+ export { type AuthAccount, type AuthAdmin, type AuthAdminSession, type AuthAdminUser, type AuthCapabilities, type AuthInvitation, type AuthMember, type AuthOrganization, type AuthPage, type AuthPasskey, type AuthTimestamp, type CreateAuthAdminOptions, DEFAULT_AUTH_BASE_PATH, type ImpersonationResult, type ListUsersOptions, type LunoraAuth, LunoraAuthAdminError, type LunoraAuthOptions, type SessionPolicy, compileMigrationsSql, createAuthAdmin, ensureMigrated, handleAuthRequest, sessionPresets, validateSessionPolicy };