@lunora/auth 1.0.0-alpha.7 → 1.0.0-alpha.71

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 (69) hide show
  1. package/LICENSE.md +38 -0
  2. package/README.md +56 -1
  3. package/dist/adapter.d.mts +4 -43
  4. package/dist/adapter.d.ts +4 -43
  5. package/dist/adapter.mjs +1 -47
  6. package/dist/audit.d.mts +146 -0
  7. package/dist/audit.d.ts +146 -0
  8. package/dist/audit.mjs +12 -0
  9. package/dist/email-guard.d.mts +122 -0
  10. package/dist/email-guard.d.ts +122 -0
  11. package/dist/email-guard.mjs +1 -0
  12. package/dist/index.d.mts +698 -147
  13. package/dist/index.d.ts +698 -147
  14. package/dist/index.mjs +1 -12
  15. package/dist/middleware.d.mts +157 -156
  16. package/dist/middleware.d.ts +157 -156
  17. package/dist/middleware.mjs +1 -53
  18. package/dist/packem_shared/AUTH_DO_AUDIT_PATH-C4897amZ.mjs +1 -0
  19. package/dist/packem_shared/DEFAULT_AUTH_BASE_PATH-kIwlEt8i.mjs +1 -0
  20. package/dist/packem_shared/LunoraAuthAdminError-CiHsF1qZ.mjs +1 -0
  21. package/dist/packem_shared/adapter-RvDcm0Zy.mjs +1 -0
  22. package/dist/packem_shared/adapter.d-2a61HAY2.d.ts +76 -0
  23. package/dist/packem_shared/adapter.d-DrD3bb1u.d.mts +76 -0
  24. package/dist/packem_shared/authAuditHook-DG_ZNO53.mjs +1 -0
  25. package/dist/packem_shared/authDoColumnAdditions-B8BRbdzn.mjs +1 -0
  26. package/dist/packem_shared/compileMigrationsSql-BcvcHAqo.mjs +1 -0
  27. package/dist/packem_shared/create-auth.d-De6IOirt.d.mts +128 -0
  28. package/dist/packem_shared/create-auth.d-De6IOirt.d.ts +128 -0
  29. package/dist/packem_shared/createAuth-DRtd4q6u.mjs +1 -0
  30. package/dist/packem_shared/createDoAuthWiring-acnXUGZr.mjs +1 -0
  31. package/dist/packem_shared/createLunoraAuthClient-CedinxXU.mjs +1 -0
  32. package/dist/packem_shared/emailGateDatabaseHooks-DzBD1Qoq.mjs +1 -0
  33. package/dist/packem_shared/sessionPresets-DpEFjXKV.mjs +1 -0
  34. package/dist/packem_shared/uiConfig-BrYEFK3O.mjs +1 -0
  35. package/dist/plugins-client.d.mts +68 -1
  36. package/dist/plugins-client.d.ts +68 -1
  37. package/dist/plugins-client.mjs +1 -2
  38. package/dist/plugins-enterprise-client.d.mts +9 -0
  39. package/dist/plugins-enterprise-client.d.ts +9 -0
  40. package/dist/plugins-enterprise-client.mjs +1 -0
  41. package/dist/plugins-enterprise.d.mts +1576 -0
  42. package/dist/plugins-enterprise.d.ts +1576 -0
  43. package/dist/plugins-enterprise.mjs +1 -0
  44. package/dist/plugins.d.mts +83 -2
  45. package/dist/plugins.d.ts +83 -2
  46. package/dist/plugins.mjs +1 -22
  47. package/dist/schema.d.mts +39 -39
  48. package/dist/schema.d.ts +39 -39
  49. package/dist/schema.mjs +1 -62
  50. package/dist/sql-store.d.mts +28 -28
  51. package/dist/sql-store.d.ts +28 -28
  52. package/dist/sql-store.mjs +1 -162
  53. package/dist/store.d.mts +49 -31
  54. package/dist/store.d.ts +49 -31
  55. package/dist/store.mjs +1 -170
  56. package/dist/turnstile-middleware.d.mts +55 -55
  57. package/dist/turnstile-middleware.d.ts +55 -55
  58. package/dist/turnstile-middleware.mjs +1 -45
  59. package/dist/turnstile.d.mts +42 -59
  60. package/dist/turnstile.d.ts +42 -59
  61. package/dist/turnstile.mjs +1 -61
  62. package/package.json +38 -5
  63. package/dist/packem_shared/DEFAULT_AUTH_BASE_PATH-DjcUWEQl.mjs +0 -11
  64. package/dist/packem_shared/LunoraAuthAdminError-BxrfEeA_.mjs +0 -249
  65. package/dist/packem_shared/compileMigrationsSql-wZH3oXDu.mjs +0 -28
  66. package/dist/packem_shared/create-auth.d-M36jwG_Y.d.mts +0 -58
  67. package/dist/packem_shared/create-auth.d-M36jwG_Y.d.ts +0 -58
  68. package/dist/packem_shared/createAuth-B-tvsvQU.mjs +0 -56
  69. package/dist/packem_shared/sessionPresets-B95rXrd8.mjs +0 -35
@@ -0,0 +1,128 @@
1
+ import { betterAuth, BetterAuthOptions } from 'better-auth';
2
+ /**
3
+ * Lunora's options pass straight through to better-auth — the only thing we add
4
+ * is requiring `secret` up front so a misconfigured deployment fails loudly
5
+ * instead of at the first sign-in.
6
+ *
7
+ * For `database`, prefer `lunoraD1Adapter` (`database: lunoraD1Adapter(env.DB)`)
8
+ * over passing the raw `env.DB`. better-auth *does* accept a D1Database directly,
9
+ * but it then resolves its Kysely adapter via a runtime `await import(...)` inside
10
+ * `auth.$context` — and that import never settles under `@cloudflare/vite-plugin`'s
11
+ * worker runner, hanging every auth request in `pnpm dev`. The explicit adapter
12
+ * skips it, so dev and prod behave the same. (Raw `env.DB` is still correct for
13
+ * the migration-only instance — see `lunoraD1Adapter`'s note.)
14
+ *
15
+ * Session rotation / richer session policies are configured via the `session`
16
+ * field (a `SessionPolicy`); Lunora validates it for obviously-broken
17
+ * durations and forwards it verbatim to better-auth. See `sessionPresets`
18
+ * for ready-made rotation/expiry trade-offs.
19
+ *
20
+ * ## Serverless background tasks (Cloudflare Workers)
21
+ *
22
+ * better-auth runs some work *after* sending the response — most importantly the
23
+ * password-reset email, whose background send is what keeps reset responses
24
+ * constant-time (a timing-attack defence: the response doesn't reveal whether
25
+ * the account exists). On Cloudflare Workers a promise that isn't handed to
26
+ * `ctx.waitUntil` can be cancelled the moment the response returns, dropping
27
+ * that send and weakening the guarantee. Wire your request's `ctx.waitUntil`
28
+ * into better-auth's background handler so the work survives:
29
+ *
30
+ * ```ts
31
+ * // in your worker fetch handler, where `ctx: ExecutionContext` is in scope
32
+ * const auth = createAuth({
33
+ * secret: env.AUTH_SECRET,
34
+ * database: lunoraD1Adapter(env.DB),
35
+ * advanced: {
36
+ * backgroundTasks: { handler: (promise) => ctx.waitUntil(promise) },
37
+ * },
38
+ * });
39
+ * ```
40
+ *
41
+ * (Lunora can't set this for you — `ctx.waitUntil` is per-request, but
42
+ * `createAuth` runs once at worker setup.)
43
+ */
44
+ type LunoraAuthOptions = BetterAuthOptions;
45
+ /**
46
+ * The full better-auth instance: `auth.handler` accepts a `Request` and
47
+ * returns a `Response` (used by `handleAuthRequest`); `auth.api`
48
+ * exposes the typed endpoint surface for server-side calls (e.g.
49
+ * `auth.api.getSession({ headers })` inside a query/mutation).
50
+ */
51
+ type LunoraAuth = ReturnType<typeof betterAuth>;
52
+ /**
53
+ * Resolve the caller's options into the exact shape `createAuth` hands to
54
+ * `betterAuth` — the hardened, default-filled options the running worker uses.
55
+ * Exported (and pure) so the migration path can compile the schema from the
56
+ * same resolved options: `compileMigrationsSql` routes through here, so the
57
+ * `rateLimit` table the worker's durable limiter writes to is included in the
58
+ * migration rather than silently omitted (it would be, if migrations saw the
59
+ * raw options while the worker ran the resolved ones).
60
+ *
61
+ * ## What it fills (each gated independently on caller silence)
62
+ *
63
+ * Secure-by-default cookies + secret-strength warning via {@link hardenAuthOptions},
64
+ * applied first so all hardening composes onto one options object.
65
+ *
66
+ * Rate limiting is ON by default for `/api/auth/*`.
67
+ *
68
+ * better-auth's own default is `rateLimit.enabled ?? isProduction`, and its
69
+ * `isProduction` is `"production" === "production"` resolved at
70
+ * module-load time. On Cloudflare Workers that check is unreliable: the
71
+ * runtime has no Node `process.env` (absent entirely without
72
+ * `nodejs_compat`, and even with it `NODE_ENV` is rarely `"production"` at
73
+ * request time). So better-auth would silently leave auth endpoints
74
+ * _unthrottled_ on a real deployment — the surprise we refuse to ship.
75
+ *
76
+ * We therefore default `enabled: true` whenever the caller hasn't made an
77
+ * explicit choice. We only fill the `enabled` flag and otherwise forward
78
+ * the caller's `rateLimit` verbatim, so better-auth's `window` (10s) / `max`
79
+ * (100) defaults and any custom rules still apply. Callers who genuinely
80
+ * want it off can pass `rateLimit: { enabled: false }` (e.g. when fronting
81
+ * auth with their own limiter), and any explicit `enabled` value wins.
82
+ *
83
+ * We also default `storage: "database"` — but only when rate limiting is not
84
+ * explicitly disabled (`enabled !== false`). Filling storage under a disabled
85
+ * limiter is harmless at runtime but makes `getAuthTables` emit an unused
86
+ * `rateLimit` table, so we skip it there.
87
+ *
88
+ * better-auth's own default is `storage: "memory"` — a per-isolate,
89
+ * non-durable counter. On Cloudflare Workers that means each isolate keeps
90
+ * its own tally, counters vanish on isolate recycle, and traffic spread
91
+ * across isolates never sums to the configured `max` — a limiter that
92
+ * reports "enabled" while never enforcing a global limit (the exact
93
+ * brute-force / credential-stuffing protection on `/sign-in`, OTP, and
94
+ * password-reset it is meant to buy). `storage: "database"` rides the counter
95
+ * through the configured `database` adapter — Lunora's store over the D1 auth
96
+ * tables — so the limit is durable *and* atomic (the store's native
97
+ * `incrementOne` gives a one-winner guarantee across isolates). Callers with
98
+ * their own durable store can pass an explicit `rateLimit: { storage: … }`
99
+ * (or `customStorage`), and any explicit value wins.
100
+ *
101
+ * Session cookie cache is ON by default too.
102
+ *
103
+ * Every authenticated call resolves identity through better-auth's
104
+ * `getSession`, which — without a cache — is a DB (D1) read on the hot path
105
+ * of every query/mutation/action that reads `ctx.auth` and of the WebSocket
106
+ * upgrade. better-auth's `session.cookieCache` carries the session payload in
107
+ * a short-lived signed cookie so `getSession` can answer without hitting the
108
+ * database until the cache window elapses. We default it on with a
109
+ * deliberately short 60s `maxAge` (better-auth's own default is 300s): long
110
+ * enough to erase the per-request read for a burst of calls, short enough
111
+ * that a revoked or role-changed session self-corrects within a minute.
112
+ * The one tradeoff — a revoked session stays valid until the cache expires —
113
+ * is bounded by that TTL; callers who need immediate revocation opt out with
114
+ * `session: { cookieCache: { enabled: false } }` (or the `strict` preset).
115
+ *
116
+ * Every explicit caller value is forwarded verbatim. The two `rateLimit` fills
117
+ * merge into a single `rateLimit` object so neither clobbers the other.
118
+ */
119
+ declare const resolveAuthOptions: (options: LunoraAuthOptions) => LunoraAuthOptions;
120
+ /**
121
+ * Create the auth instance. Thin wrapper around `betterAuth` that enforces
122
+ * the `secret` requirement at construction time so misconfigured deployments
123
+ * fail loudly at the first fetch rather than the first sign-in attempt, then
124
+ * hands {@link resolveAuthOptions}'s hardened, default-filled options to
125
+ * better-auth.
126
+ */
127
+ declare const createAuth: (options: LunoraAuthOptions) => LunoraAuth;
128
+ export { LunoraAuth as L, LunoraAuthOptions as a, createAuth as c, resolveAuthOptions as r };
@@ -0,0 +1,128 @@
1
+ import { betterAuth, BetterAuthOptions } from 'better-auth';
2
+ /**
3
+ * Lunora's options pass straight through to better-auth — the only thing we add
4
+ * is requiring `secret` up front so a misconfigured deployment fails loudly
5
+ * instead of at the first sign-in.
6
+ *
7
+ * For `database`, prefer `lunoraD1Adapter` (`database: lunoraD1Adapter(env.DB)`)
8
+ * over passing the raw `env.DB`. better-auth *does* accept a D1Database directly,
9
+ * but it then resolves its Kysely adapter via a runtime `await import(...)` inside
10
+ * `auth.$context` — and that import never settles under `@cloudflare/vite-plugin`'s
11
+ * worker runner, hanging every auth request in `pnpm dev`. The explicit adapter
12
+ * skips it, so dev and prod behave the same. (Raw `env.DB` is still correct for
13
+ * the migration-only instance — see `lunoraD1Adapter`'s note.)
14
+ *
15
+ * Session rotation / richer session policies are configured via the `session`
16
+ * field (a `SessionPolicy`); Lunora validates it for obviously-broken
17
+ * durations and forwards it verbatim to better-auth. See `sessionPresets`
18
+ * for ready-made rotation/expiry trade-offs.
19
+ *
20
+ * ## Serverless background tasks (Cloudflare Workers)
21
+ *
22
+ * better-auth runs some work *after* sending the response — most importantly the
23
+ * password-reset email, whose background send is what keeps reset responses
24
+ * constant-time (a timing-attack defence: the response doesn't reveal whether
25
+ * the account exists). On Cloudflare Workers a promise that isn't handed to
26
+ * `ctx.waitUntil` can be cancelled the moment the response returns, dropping
27
+ * that send and weakening the guarantee. Wire your request's `ctx.waitUntil`
28
+ * into better-auth's background handler so the work survives:
29
+ *
30
+ * ```ts
31
+ * // in your worker fetch handler, where `ctx: ExecutionContext` is in scope
32
+ * const auth = createAuth({
33
+ * secret: env.AUTH_SECRET,
34
+ * database: lunoraD1Adapter(env.DB),
35
+ * advanced: {
36
+ * backgroundTasks: { handler: (promise) => ctx.waitUntil(promise) },
37
+ * },
38
+ * });
39
+ * ```
40
+ *
41
+ * (Lunora can't set this for you — `ctx.waitUntil` is per-request, but
42
+ * `createAuth` runs once at worker setup.)
43
+ */
44
+ type LunoraAuthOptions = BetterAuthOptions;
45
+ /**
46
+ * The full better-auth instance: `auth.handler` accepts a `Request` and
47
+ * returns a `Response` (used by `handleAuthRequest`); `auth.api`
48
+ * exposes the typed endpoint surface for server-side calls (e.g.
49
+ * `auth.api.getSession({ headers })` inside a query/mutation).
50
+ */
51
+ type LunoraAuth = ReturnType<typeof betterAuth>;
52
+ /**
53
+ * Resolve the caller's options into the exact shape `createAuth` hands to
54
+ * `betterAuth` — the hardened, default-filled options the running worker uses.
55
+ * Exported (and pure) so the migration path can compile the schema from the
56
+ * same resolved options: `compileMigrationsSql` routes through here, so the
57
+ * `rateLimit` table the worker's durable limiter writes to is included in the
58
+ * migration rather than silently omitted (it would be, if migrations saw the
59
+ * raw options while the worker ran the resolved ones).
60
+ *
61
+ * ## What it fills (each gated independently on caller silence)
62
+ *
63
+ * Secure-by-default cookies + secret-strength warning via {@link hardenAuthOptions},
64
+ * applied first so all hardening composes onto one options object.
65
+ *
66
+ * Rate limiting is ON by default for `/api/auth/*`.
67
+ *
68
+ * better-auth's own default is `rateLimit.enabled ?? isProduction`, and its
69
+ * `isProduction` is `"production" === "production"` resolved at
70
+ * module-load time. On Cloudflare Workers that check is unreliable: the
71
+ * runtime has no Node `process.env` (absent entirely without
72
+ * `nodejs_compat`, and even with it `NODE_ENV` is rarely `"production"` at
73
+ * request time). So better-auth would silently leave auth endpoints
74
+ * _unthrottled_ on a real deployment — the surprise we refuse to ship.
75
+ *
76
+ * We therefore default `enabled: true` whenever the caller hasn't made an
77
+ * explicit choice. We only fill the `enabled` flag and otherwise forward
78
+ * the caller's `rateLimit` verbatim, so better-auth's `window` (10s) / `max`
79
+ * (100) defaults and any custom rules still apply. Callers who genuinely
80
+ * want it off can pass `rateLimit: { enabled: false }` (e.g. when fronting
81
+ * auth with their own limiter), and any explicit `enabled` value wins.
82
+ *
83
+ * We also default `storage: "database"` — but only when rate limiting is not
84
+ * explicitly disabled (`enabled !== false`). Filling storage under a disabled
85
+ * limiter is harmless at runtime but makes `getAuthTables` emit an unused
86
+ * `rateLimit` table, so we skip it there.
87
+ *
88
+ * better-auth's own default is `storage: "memory"` — a per-isolate,
89
+ * non-durable counter. On Cloudflare Workers that means each isolate keeps
90
+ * its own tally, counters vanish on isolate recycle, and traffic spread
91
+ * across isolates never sums to the configured `max` — a limiter that
92
+ * reports "enabled" while never enforcing a global limit (the exact
93
+ * brute-force / credential-stuffing protection on `/sign-in`, OTP, and
94
+ * password-reset it is meant to buy). `storage: "database"` rides the counter
95
+ * through the configured `database` adapter — Lunora's store over the D1 auth
96
+ * tables — so the limit is durable *and* atomic (the store's native
97
+ * `incrementOne` gives a one-winner guarantee across isolates). Callers with
98
+ * their own durable store can pass an explicit `rateLimit: { storage: … }`
99
+ * (or `customStorage`), and any explicit value wins.
100
+ *
101
+ * Session cookie cache is ON by default too.
102
+ *
103
+ * Every authenticated call resolves identity through better-auth's
104
+ * `getSession`, which — without a cache — is a DB (D1) read on the hot path
105
+ * of every query/mutation/action that reads `ctx.auth` and of the WebSocket
106
+ * upgrade. better-auth's `session.cookieCache` carries the session payload in
107
+ * a short-lived signed cookie so `getSession` can answer without hitting the
108
+ * database until the cache window elapses. We default it on with a
109
+ * deliberately short 60s `maxAge` (better-auth's own default is 300s): long
110
+ * enough to erase the per-request read for a burst of calls, short enough
111
+ * that a revoked or role-changed session self-corrects within a minute.
112
+ * The one tradeoff — a revoked session stays valid until the cache expires —
113
+ * is bounded by that TTL; callers who need immediate revocation opt out with
114
+ * `session: { cookieCache: { enabled: false } }` (or the `strict` preset).
115
+ *
116
+ * Every explicit caller value is forwarded verbatim. The two `rateLimit` fills
117
+ * merge into a single `rateLimit` object so neither clobbers the other.
118
+ */
119
+ declare const resolveAuthOptions: (options: LunoraAuthOptions) => LunoraAuthOptions;
120
+ /**
121
+ * Create the auth instance. Thin wrapper around `betterAuth` that enforces
122
+ * the `secret` requirement at construction time so misconfigured deployments
123
+ * fail loudly at the first fetch rather than the first sign-in attempt, then
124
+ * hands {@link resolveAuthOptions}'s hardened, default-filled options to
125
+ * better-auth.
126
+ */
127
+ declare const createAuth: (options: LunoraAuthOptions) => LunoraAuth;
128
+ export { LunoraAuth as L, LunoraAuthOptions as a, createAuth as c, resolveAuthOptions as r };
@@ -0,0 +1 @@
1
+ import{LunoraError as a}from"@lunora/errors";import{betterAuth as i}from"better-auth";import{validateSessionPolicy as l}from"./sessionPresets-DpEFjXKV.mjs";const n=32,c=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<n},s=e=>typeof e=="string"?e.toLowerCase().startsWith("http://"):e&&typeof e=="object"?e.protocol==="http"?!0:e.protocol==="https"?!1:typeof e.fallback=="string"&&e.fallback.toLowerCase().startsWith("http://"):!1,h=new Set(["127.0.0.1","localhost"]),u=e=>{if(typeof e!="string")return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"||!h.has(t.hostname)))return{allowedHosts:[t.hostname,`${t.hostname}:*`],fallback:e,protocol:"http"}},p=e=>{const t=u(e.baseURL)??e.baseURL;if(c(e.secret)){const o=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(n)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!s(t))throw new a("INTERNAL",o);console.warn(o)}const r=e.advanced??{};return{...e,advanced:{...r,defaultCookieAttributes:r.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},...r.useSecureCookies===void 0?{useSecureCookies:!s(t)}:{}},baseURL:t}},d=e=>{const t=p(e),r=t.rateLimit?.enabled===void 0,o=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1;return{...t,...r||o?{rateLimit:{...t.rateLimit,...r?{enabled:!0}:{},...o?{storage:"database"}:{}}}:{},...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},L=e=>{if(!e.secret||e.secret.trim()==="")throw new a("INTERNAL",'@lunora/auth: `secret` is required. Set AUTH_SECRET locally in .dev.vars (`lunora env set AUTH_SECRET "$(openssl rand -hex 32)"`), and in production with `wrangler secret put AUTH_SECRET`.');return e.session&&l(e.session),i(d(e))};export{L as createAuth,d as resolveAuthOptions};
@@ -0,0 +1 @@
1
+ import{INTERNAL_SECRET_HEADER as u,RESOLVE_SESSION_PATH as f,READ_AUDIT_PATH as w}from"./AUTH_DO_AUDIT_PATH-C4897amZ.mjs";import{DEFAULT_AUTH_BASE_PATH as A}from"./DEFAULT_AUTH_BASE_PATH-kIwlEt8i.mjs";const m=c=>{const{basePath:d=A,internalSecret:a,namespace:i,objectName:h="auth"}=c,o=()=>{if(i)return i.get(i.idFromName(h))},l=async(t,e,s)=>{if(!a)return;const n=o();if(!n)return;const r=await n.fetch(new Request(new URL(t,e),{body:JSON.stringify(s),headers:{"content-type":"application/json",[u]:a},method:"POST"}));return r.ok?r:void 0};return{auditReader:{read:async t=>{const e=await l(w,"https://auth-do.invalid",t);return e?(await e.json())?.entries??[]:[]}},authHandler:async t=>{if(new URL(t.url).pathname.startsWith(d))return o()?.fetch(t)},resolveIdentity:async t=>{if(!a)return null;const e=o();if(!e)return null;const s=new Headers(t.headers);s.set(u,a);const n=await e.fetch(new Request(new URL(f,t.url),{headers:s}));if(!n.ok)return null;const r=await n.json();return r?.userId?{userId:r.userId}:null}}};export{m as createDoAuthWiring};
@@ -0,0 +1 @@
1
+ import{oauthProviderClient as l}from"@better-auth/oauth-provider/client";import{passkeyClient as r}from"@better-auth/passkey/client";import{oneTapClient as p,organizationClient as h,twoFactorClient as m,magicLinkClient as C,emailOTPClient as c,adminClient as g,usernameClient as d,phoneNumberClient as L,multiSessionClient as b,anonymousClient as k,deviceAuthorizationClient as v,lastLoginMethodClient as y}from"better-auth/client/plugins";const P=(t={})=>{const n=[];return t.organization&&n.push(h()),t.twoFactor&&n.push(m()),t.passkey&&n.push(r()),t.magicLink&&n.push(C()),t.emailOtp&&n.push(c()),t.admin&&n.push(g()),t.username&&n.push(d()),t.phoneNumber&&n.push(L()),t.multiSession&&n.push(b()),t.anonymous&&n.push(k()),t.deviceAuthorization&&n.push(v()),t.lastLoginMethod&&n.push(y()),t.oauthProvider&&n.push(l()),n},z=()=>globalThis.location?.origin,w=(t,n={})=>{const{baseURL:o,extraPlugins:i=[],oneTapClientId:e,plugins:s,...a}=n,u=e===void 0||e===""?[]:[p({clientId:e})];return t({...a,baseURL:o??z(),plugins:[...P(s),...u,...i]})};export{w as createLunoraAuthClient,P as lunoraAuthPlugins};
@@ -0,0 +1 @@
1
+ import{LunoraError as i}from"@lunora/errors";import{APIError as u}from"better-auth/api";import{assertEmailAllowed as E}from"../email-guard.mjs";const m=a=>{switch(a){case 400:return"BAD_REQUEST";case 422:return"UNPROCESSABLE_ENTITY";case 429:return"TOO_MANY_REQUESTS";default:return"INTERNAL_SERVER_ERROR"}},n=a=>async(s,t)=>{const r=typeof s.email=="string"?s.email:void 0;if(r===void 0||r==="")return;let o;try{o=await E(r,a)}catch(e){throw e instanceof i?new u(m(e.status),{code:e.code,message:e.message}):e}a.onClassify?.(o,s,t)},b=(a={})=>({user:{create:{before:n(a)}}}),R=(a,s={})=>{const t=n(s),r=a.databaseHooks?.user?.create?.before,o=r?async(e,c)=>(await t(e,c),r(e,c)):t;return{...a,databaseHooks:{...a.databaseHooks,user:{...a.databaseHooks?.user,create:{...a.databaseHooks?.user?.create,before:o}}}}};export{b as emailGateDatabaseHooks,R as withEmailGate};
@@ -0,0 +1 @@
1
+ const r=s=>{const o=["expiresIn","updateAge","freshAge"];for(const n of o){const e=s[n];if(e!==void 0&&(typeof e!="number"||!Number.isFinite(e)||e<0))throw new TypeError(`@lunora/auth: \`session.${n}\` must be a non-negative, finite number of seconds`)}return s},i={longLived:{cookieCache:{enabled:!0,maxAge:60},expiresIn:2592e3,freshAge:86400,updateAge:86400},rolling:{cookieCache:{enabled:!0,maxAge:60},expiresIn:604800,freshAge:86400,updateAge:86400},strict:{cookieCache:{enabled:!1},expiresIn:3600,freshAge:300,updateAge:900}};export{i as sessionPresets,r as validateSessionPolicy};
@@ -0,0 +1 @@
1
+ import{createAuthEndpoint as p}from"better-auth/api";import{getAuthTables as g}from"better-auth/db";const l=i=>[...i].toSorted((o,e)=>o.localeCompare(e)),m=(i,o={})=>{const e=o.expose??{},t=new Set((i.plugins??[]).map(n=>n.id)),s=i.emailAndPassword?.enabled??!1,r=g(i),a=(i.plugins??[]).find(n=>n.id==="organization")?.options??{},d={allowUserToCreate:typeof a.allowUserToCreateOrganization=="function"?!0:a.allowUserToCreateOrganization!==!1,enabled:t.has("organization"),invitationLimit:a.invitationLimit,limit:a.organizationLimit,membershipLimit:a.membershipLimit,roles:!!r.organizationRole,teams:!!r.team};return{emailAndPassword:s,signUp:s&&i.emailAndPassword?.disableSignUp!==!0,...e.organization===!1?{}:{organization:d},...e.plugins===!1?{}:{plugins:l(t)},...e.socialProviders===!1?{}:{socialProviders:l(new Set([...Object.keys(i.socialProviders??{}),...o.extraProviders??[]]))}}},c=i=>p(i.path??"/ui-config",{metadata:{openapi:{description:"Public description of the enabled auth plugins and social providers.",responses:{200:{description:"The UI configuration."}}}},method:"GET"},o=>{const e=o;return Promise.resolve(e.json(m(e.context.options,i)))}),h=(i={})=>({endpoints:{getUiConfig:c(i)},id:"lunora-ui-config"});export{m as deriveUiConfig,h as uiConfig};
@@ -1,2 +1,69 @@
1
+ import { organizationClient } from 'better-auth/client/plugins';
2
+ export { adminClient, anonymousClient, customSessionClient, deviceAuthorizationClient, emailOTPClient, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oneTapClient, oneTimeTokenClient, organizationClient, phoneNumberClient, siweClient, twoFactorClient, usernameClient } from 'better-auth/client/plugins';
3
+ export { oauthProviderClient } from '@better-auth/oauth-provider/client';
1
4
  export { passkeyClient } from '@better-auth/passkey/client';
2
- export { adminClient, anonymousClient, customSessionClient, deviceAuthorizationClient, emailOTPClient, genericOAuthClient, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oidcClient, oneTimeTokenClient, organizationClient, phoneNumberClient, siweClient, twoFactorClient, usernameClient } from 'better-auth/client/plugins';
5
+ /**
6
+ * Which client plugins to include. Each defaults to `false`.
7
+ *
8
+ * These are the same names `@lunora/auth-ui` gates its cards on, so one object
9
+ * can drive both `createLunoraAuthClient` and `registerAuthClientPlugins` — a
10
+ * flow the UI can show but this cannot install would be a card that renders and
11
+ * then fails at call time.
12
+ *
13
+ * `oneTap` is absent on purpose: Google One Tap needs a client id, not a
14
+ * boolean, so it comes in through {@link CreateLunoraAuthClientOptions.oneTapClientId}.
15
+ */
16
+ interface LunoraAuthPluginToggles {
17
+ admin?: boolean;
18
+ anonymous?: boolean;
19
+ deviceAuthorization?: boolean;
20
+ emailOtp?: boolean;
21
+ lastLoginMethod?: boolean;
22
+ magicLink?: boolean;
23
+ multiSession?: boolean;
24
+ oauthProvider?: boolean;
25
+ organization?: boolean;
26
+ passkey?: boolean;
27
+ phoneNumber?: boolean;
28
+ twoFactor?: boolean;
29
+ username?: boolean;
30
+ }
31
+ type LunoraAuthClientPlugin = ReturnType<typeof organizationClient>;
32
+ declare const lunoraAuthPlugins: (toggles?: LunoraAuthPluginToggles) => LunoraAuthClientPlugin[];
33
+ /** Options for {@link createLunoraAuthClient}; anything else is forwarded to `createAuthClient`. */
34
+ interface CreateLunoraAuthClientOptions {
35
+ [option: string]: unknown;
36
+ /** Defaults to the current origin, which is right for a same-origin app. */
37
+ baseURL?: string;
38
+ /** Your own client plugins, appended after the standard set. */
39
+ extraPlugins?: LunoraAuthClientPlugin[];
40
+ /**
41
+ * Google OAuth client id. Setting it installs the One Tap client plugin —
42
+ * a boolean toggle can't, because the prompt is Google's and needs the id.
43
+ */
44
+ oneTapClientId?: string;
45
+ /** Which standard client plugins to include. */
46
+ plugins?: LunoraAuthPluginToggles;
47
+ }
48
+ /**
49
+ * Build a better-auth client with Lunora's standard plugin set from toggles.
50
+ *
51
+ * You pass your framework's `createAuthClient` in — `better-auth/react`,
52
+ * `/vue`, `/svelte`, or `/solid` — because the variant has to match the UI
53
+ * framework, and a helper that picked for you would either guess wrong or drag
54
+ * every variant into your bundle.
55
+ *
56
+ * ```ts
57
+ * import { createAuthClient } from "better-auth/react";
58
+ * import { createLunoraAuthClient } from "@lunora/auth/plugins/client";
59
+ *
60
+ * export const authClient = createLunoraAuthClient(createAuthClient, {
61
+ * plugins: { organization: true, passkey: true, twoFactor: true },
62
+ * });
63
+ * ```
64
+ *
65
+ * Reach for {@link lunoraAuthPlugins} instead when you want to own the
66
+ * `createAuthClient` call and only borrow the plugin array.
67
+ */
68
+ declare const createLunoraAuthClient: <TClient>(createAuthClient: (options: Record<string, unknown>) => TClient, options?: CreateLunoraAuthClientOptions) => TClient;
69
+ export { type CreateLunoraAuthClientOptions, type LunoraAuthClientPlugin, type LunoraAuthPluginToggles, createLunoraAuthClient, lunoraAuthPlugins };
@@ -1,2 +1,69 @@
1
+ import { organizationClient } from 'better-auth/client/plugins';
2
+ export { adminClient, anonymousClient, customSessionClient, deviceAuthorizationClient, emailOTPClient, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oneTapClient, oneTimeTokenClient, organizationClient, phoneNumberClient, siweClient, twoFactorClient, usernameClient } from 'better-auth/client/plugins';
3
+ export { oauthProviderClient } from '@better-auth/oauth-provider/client';
1
4
  export { passkeyClient } from '@better-auth/passkey/client';
2
- export { adminClient, anonymousClient, customSessionClient, deviceAuthorizationClient, emailOTPClient, genericOAuthClient, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oidcClient, oneTimeTokenClient, organizationClient, phoneNumberClient, siweClient, twoFactorClient, usernameClient } from 'better-auth/client/plugins';
5
+ /**
6
+ * Which client plugins to include. Each defaults to `false`.
7
+ *
8
+ * These are the same names `@lunora/auth-ui` gates its cards on, so one object
9
+ * can drive both `createLunoraAuthClient` and `registerAuthClientPlugins` — a
10
+ * flow the UI can show but this cannot install would be a card that renders and
11
+ * then fails at call time.
12
+ *
13
+ * `oneTap` is absent on purpose: Google One Tap needs a client id, not a
14
+ * boolean, so it comes in through {@link CreateLunoraAuthClientOptions.oneTapClientId}.
15
+ */
16
+ interface LunoraAuthPluginToggles {
17
+ admin?: boolean;
18
+ anonymous?: boolean;
19
+ deviceAuthorization?: boolean;
20
+ emailOtp?: boolean;
21
+ lastLoginMethod?: boolean;
22
+ magicLink?: boolean;
23
+ multiSession?: boolean;
24
+ oauthProvider?: boolean;
25
+ organization?: boolean;
26
+ passkey?: boolean;
27
+ phoneNumber?: boolean;
28
+ twoFactor?: boolean;
29
+ username?: boolean;
30
+ }
31
+ type LunoraAuthClientPlugin = ReturnType<typeof organizationClient>;
32
+ declare const lunoraAuthPlugins: (toggles?: LunoraAuthPluginToggles) => LunoraAuthClientPlugin[];
33
+ /** Options for {@link createLunoraAuthClient}; anything else is forwarded to `createAuthClient`. */
34
+ interface CreateLunoraAuthClientOptions {
35
+ [option: string]: unknown;
36
+ /** Defaults to the current origin, which is right for a same-origin app. */
37
+ baseURL?: string;
38
+ /** Your own client plugins, appended after the standard set. */
39
+ extraPlugins?: LunoraAuthClientPlugin[];
40
+ /**
41
+ * Google OAuth client id. Setting it installs the One Tap client plugin —
42
+ * a boolean toggle can't, because the prompt is Google's and needs the id.
43
+ */
44
+ oneTapClientId?: string;
45
+ /** Which standard client plugins to include. */
46
+ plugins?: LunoraAuthPluginToggles;
47
+ }
48
+ /**
49
+ * Build a better-auth client with Lunora's standard plugin set from toggles.
50
+ *
51
+ * You pass your framework's `createAuthClient` in — `better-auth/react`,
52
+ * `/vue`, `/svelte`, or `/solid` — because the variant has to match the UI
53
+ * framework, and a helper that picked for you would either guess wrong or drag
54
+ * every variant into your bundle.
55
+ *
56
+ * ```ts
57
+ * import { createAuthClient } from "better-auth/react";
58
+ * import { createLunoraAuthClient } from "@lunora/auth/plugins/client";
59
+ *
60
+ * export const authClient = createLunoraAuthClient(createAuthClient, {
61
+ * plugins: { organization: true, passkey: true, twoFactor: true },
62
+ * });
63
+ * ```
64
+ *
65
+ * Reach for {@link lunoraAuthPlugins} instead when you want to own the
66
+ * `createAuthClient` call and only borrow the plugin array.
67
+ */
68
+ declare const createLunoraAuthClient: <TClient>(createAuthClient: (options: Record<string, unknown>) => TClient, options?: CreateLunoraAuthClientOptions) => TClient;
69
+ export { type CreateLunoraAuthClientOptions, type LunoraAuthClientPlugin, type LunoraAuthPluginToggles, createLunoraAuthClient, lunoraAuthPlugins };
@@ -1,2 +1 @@
1
- export { passkeyClient } from '@better-auth/passkey/client';
2
- export { adminClient, anonymousClient, customSessionClient, deviceAuthorizationClient, emailOTPClient, genericOAuthClient, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oidcClient, oneTimeTokenClient, organizationClient, phoneNumberClient, siweClient, twoFactorClient, usernameClient } from 'better-auth/client/plugins';
1
+ import{createLunoraAuthClient as n,lunoraAuthPlugins as t}from"./packem_shared/createLunoraAuthClient-CedinxXU.mjs";import{oauthProviderClient as l}from"@better-auth/oauth-provider/client";import{passkeyClient as C}from"@better-auth/passkey/client";import{adminClient as s,anonymousClient as m,customSessionClient as u,deviceAuthorizationClient as d,emailOTPClient as p,inferAdditionalFields as f,inferOrgAdditionalFields as h,jwtClient as c,lastLoginMethodClient as g,magicLinkClient as A,multiSessionClient as x,oneTapClient as T,oneTimeTokenClient as k,organizationClient as w,phoneNumberClient as F,siweClient as L,twoFactorClient as P,usernameClient as v}from"better-auth/client/plugins";export{s as adminClient,m as anonymousClient,n as createLunoraAuthClient,u as customSessionClient,d as deviceAuthorizationClient,p as emailOTPClient,f as inferAdditionalFields,h as inferOrgAdditionalFields,c as jwtClient,g as lastLoginMethodClient,t as lunoraAuthPlugins,A as magicLinkClient,x as multiSessionClient,l as oauthProviderClient,T as oneTapClient,k as oneTimeTokenClient,w as organizationClient,C as passkeyClient,F as phoneNumberClient,L as siweClient,P as twoFactorClient,v as usernameClient};
@@ -0,0 +1,9 @@
1
+ import { ssoClient } from '@better-auth/sso/client';
2
+ export type { ssoClient } from '@better-auth/sso/client';
3
+ /**
4
+ * The plugin instance `ssoClient()` returns. Exported so an app can type the array it
5
+ * passes to `createAuthClient` when it assembles plugins conditionally.
6
+ * @experimental
7
+ */
8
+ type SSOClientPlugin = ReturnType<typeof ssoClient>;
9
+ export { SSOClientPlugin };
@@ -0,0 +1,9 @@
1
+ import { ssoClient } from '@better-auth/sso/client';
2
+ export type { ssoClient } from '@better-auth/sso/client';
3
+ /**
4
+ * The plugin instance `ssoClient()` returns. Exported so an app can type the array it
5
+ * passes to `createAuthClient` when it assembles plugins conditionally.
6
+ * @experimental
7
+ */
8
+ type SSOClientPlugin = ReturnType<typeof ssoClient>;
9
+ export { SSOClientPlugin };
@@ -0,0 +1 @@
1
+ import{ssoClient as r}from"@better-auth/sso/client";export{r as ssoClient};