@lunora/auth 1.0.0-alpha.120 → 1.0.0-alpha.122

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/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{l as r,a as o,b as a}from"./packem_shared/adapter-DA8DdALX.mjs";import{LunoraAuthAdminError as A,createAuthAdmin as u}from"./packem_shared/LunoraAuthAdminError-BIpCQR2m.mjs";import{AUTH_AUDIT_TABLE as s,appendAuthAuditEntry as m,createAuthAuditReader as l,ensureAuthAuditTable as p,readAuthAuditLog as d}from"./audit.mjs";import{authAuditHook as E,buildAuditEntry as T,eventForPath as f,withAuthAudit as S}from"./packem_shared/authAuditHook-Bmq06hsp.mjs";import{READ_AUDIT_PATH as x,INTERNAL_SECRET_HEADER as D,RESOLVE_SESSION_PATH as H,LunoraAuthDO as U}from"./packem_shared/AUTH_DO_AUDIT_PATH-CS1FCCoC.mjs";import{createAuth as I,resolveAuthOptions as R}from"./packem_shared/createAuth-BI6qU0c_.mjs";import{authDoColumnAdditions as L,authDoSchemaStatements as P}from"./packem_shared/authDoColumnAdditions-BCSs7qaN.mjs";import{createDoAuthWiring as O}from"./packem_shared/createDoAuthWiring-D0pT8Y9R.mjs";import{emailGateDatabaseHooks as N,withEmailGate as w}from"./packem_shared/emailGateDatabaseHooks-CAygR3iq.mjs";import{assertEmailAllowed as M,classifyEmail as k,emailGateMiddleware as q,loadEmailDomainLists as C}from"./email-guard.mjs";import{DEFAULT_AUTH_BASE_PATH as G,handleAuthRequest as B}from"./packem_shared/DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{createSignUpInvitation as W,listSignUpInvitations as Y,pruneSignUpInvitations as j,revokeSignUpInvitation as z}from"./packem_shared/createSignUpInvitation-CqqQc4S8.mjs";import{LunoraAuthHeadersError as K,withAuthPlugins as Q}from"./middleware.mjs";import{compileMigrationsSql as Z,ensureMigrated as $}from"./packem_shared/compileMigrationsSql-CCvK7g57.mjs";import{default as et}from"./schema.mjs";import{sessionPresets as ot,validateSessionPolicy as at}from"./packem_shared/sessionPresets-C867Mlo4.mjs";import{createSqlAuthStore as At,d1Executor as ut}from"./sql-store.mjs";import{createMemoryAuthStore as st,matchesWhere as mt}from"./store.mjs";import{TURNSTILE_VERIFY_ENDPOINT as pt,verifyTurnstile as dt}from"./turnstile.mjs";import{verifyTurnstileMiddleware as Et}from"./turnstile-middleware.mjs";export{s as AUTH_AUDIT_TABLE,x as AUTH_DO_AUDIT_PATH,D as AUTH_DO_SECRET_HEADER,H as AUTH_DO_SESSION_PATH,G as DEFAULT_AUTH_BASE_PATH,A as LunoraAuthAdminError,U as LunoraAuthDO,K as LunoraAuthHeadersError,pt as TURNSTILE_VERIFY_ENDPOINT,m as appendAuthAuditEntry,M as assertEmailAllowed,E as authAuditHook,L as authDoColumnAdditions,P as authDoSchemaStatements,et as authTables,T as buildAuditEntry,k as classifyEmail,Z as compileMigrationsSql,I as createAuth,u as createAuthAdmin,l as createAuthAuditReader,O as createDoAuthWiring,st as createMemoryAuthStore,W as createSignUpInvitation,At as createSqlAuthStore,ut as d1Executor,N as emailGateDatabaseHooks,q as emailGateMiddleware,p as ensureAuthAuditTable,$ as ensureMigrated,f as eventForPath,B as handleAuthRequest,Y as listSignUpInvitations,C as loadEmailDomainLists,r as lunoraAuthAdapter,o as lunoraD1Adapter,a as lunoraDoAdapter,mt as matchesWhere,j as pruneSignUpInvitations,d as readAuthAuditLog,R as resolveAuthOptions,z as revokeSignUpInvitation,ot as sessionPresets,at as validateSessionPolicy,dt as verifyTurnstile,Et as verifyTurnstileMiddleware,S as withAuthAudit,Q as withAuthPlugins,w as withEmailGate};
1
+ import{l as r,a as o,b as a}from"./packem_shared/adapter-DA8DdALX.mjs";import{LunoraAuthAdminError as A,createAuthAdmin as u}from"./packem_shared/LunoraAuthAdminError-BIpCQR2m.mjs";import{AUTH_AUDIT_TABLE as s,appendAuthAuditEntry as m,createAuthAuditReader as l,ensureAuthAuditTable as p,readAuthAuditLog as d}from"./audit.mjs";import{authAuditHook as E,buildAuditEntry as T,eventForPath as f,withAuthAudit as S}from"./packem_shared/authAuditHook-C9Gt2iYp.mjs";import{READ_AUDIT_PATH as x,INTERNAL_SECRET_HEADER as D,RESOLVE_SESSION_PATH as H,LunoraAuthDO as U}from"./packem_shared/AUTH_DO_AUDIT_PATH-BgmrlMWt.mjs";import{createAuth as I,resolveAuthOptions as R}from"./packem_shared/createAuth-8aUw8v8D.mjs";import{authDoColumnAdditions as L,authDoSchemaStatements as P}from"./packem_shared/authDoColumnAdditions-BCSs7qaN.mjs";import{createDoAuthWiring as O}from"./packem_shared/createDoAuthWiring-DlQ87uIT.mjs";import{emailGateDatabaseHooks as N,withEmailGate as w}from"./packem_shared/emailGateDatabaseHooks-CAygR3iq.mjs";import{assertEmailAllowed as M,classifyEmail as k,emailGateMiddleware as q,loadEmailDomainLists as C}from"./email-guard.mjs";import{DEFAULT_AUTH_BASE_PATH as G,handleAuthRequest as B}from"./packem_shared/DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{createSignUpInvitation as W,listSignUpInvitations as Y,pruneSignUpInvitations as j,revokeSignUpInvitation as z}from"./packem_shared/createSignUpInvitation-CqqQc4S8.mjs";import{LunoraAuthHeadersError as K,withAuthPlugins as Q}from"./middleware.mjs";import{compileMigrationsSql as Z,ensureMigrated as $}from"./packem_shared/compileMigrationsSql-DWKoCboe.mjs";import{default as et}from"./schema.mjs";import{sessionPresets as ot,validateSessionPolicy as at}from"./packem_shared/sessionPresets-C867Mlo4.mjs";import{createSqlAuthStore as At,d1Executor as ut}from"./sql-store.mjs";import{createMemoryAuthStore as st,matchesWhere as mt}from"./store.mjs";import{TURNSTILE_VERIFY_ENDPOINT as pt,verifyTurnstile as dt}from"./turnstile.mjs";import{verifyTurnstileMiddleware as Et}from"./turnstile-middleware.mjs";export{s as AUTH_AUDIT_TABLE,x as AUTH_DO_AUDIT_PATH,D as AUTH_DO_SECRET_HEADER,H as AUTH_DO_SESSION_PATH,G as DEFAULT_AUTH_BASE_PATH,A as LunoraAuthAdminError,U as LunoraAuthDO,K as LunoraAuthHeadersError,pt as TURNSTILE_VERIFY_ENDPOINT,m as appendAuthAuditEntry,M as assertEmailAllowed,E as authAuditHook,L as authDoColumnAdditions,P as authDoSchemaStatements,et as authTables,T as buildAuditEntry,k as classifyEmail,Z as compileMigrationsSql,I as createAuth,u as createAuthAdmin,l as createAuthAuditReader,O as createDoAuthWiring,st as createMemoryAuthStore,W as createSignUpInvitation,At as createSqlAuthStore,ut as d1Executor,N as emailGateDatabaseHooks,q as emailGateMiddleware,p as ensureAuthAuditTable,$ as ensureMigrated,f as eventForPath,B as handleAuthRequest,Y as listSignUpInvitations,C as loadEmailDomainLists,r as lunoraAuthAdapter,o as lunoraD1Adapter,a as lunoraDoAdapter,mt as matchesWhere,j as pruneSignUpInvitations,d as readAuthAuditLog,R as resolveAuthOptions,z as revokeSignUpInvitation,ot as sessionPresets,at as validateSessionPolicy,dt as verifyTurnstile,Et as verifyTurnstileMiddleware,S as withAuthAudit,Q as withAuthPlugins,w as withEmailGate};
@@ -1 +1 @@
1
- import{b as u,d as h}from"./adapter-DA8DdALX.mjs";import{ensureAuthAuditTable as c,createAuthAuditReader as l}from"../audit.mjs";import{resolveAuthOptions as m,createAuth as d}from"./createAuth-BI6qU0c_.mjs";import{authDoSchemaStatements as p,authDoColumnAdditions as f}from"./authDoColumnAdditions-BCSs7qaN.mjs";import{handleAuthRequest as A}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const g=(n,e)=>{const s=Math.max(n.length,e.length);let t=n.length^e.length;for(let r=0;r<s;r+=1){const i=r<n.length?n.charCodeAt(r):0,o=r<e.length?e.charCodeAt(r):0;t|=i^o}return t===0},R="/__lunora/auth/session",y="/__lunora/auth/audit",S="x-lunora-auth-do-secret",b=n=>{if(n===null||typeof n!="object"||Array.isArray(n))return{error:"body must be a JSON object"};const e={};for(const[s,t]of Object.entries(n))if(s==="limit"||s==="sinceSeq"){if(typeof t!="number"||!Number.isFinite(t))return{error:`"${s}" must be a finite number`};e[s]=t}else if(s==="actorId"||s==="event"){if(typeof t!="string")return{error:`"${s}" must be a string`};e[s]=t}else return{error:`unknown audit read option "${s}"`};return{options:e}};class v{#s;#r;#t;#e;#n=!1;constructor(e,s,t={}){this.#t=e.storage,this.#r=s,this.#s=t}#o(){if(this.#e!==void 0)return this.#e;const e=this.#r();if(!this.#n){const s=m(e);for(const t of p(s))[...this.#t.sql.exec(t)];for(const t of f(s,r=>this.#a(r)))[...this.#t.sql.exec(t)];this.#n=!0}return this.#e=d({...e,database:u(this.#t)}),this.#e}#a(e){return[...this.#t.sql.exec("SELECT name FROM pragma_table_info(?)",e)].map(t=>String(t.name))}async#u(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});let s;try{s=await e.json()}catch{return Response.json({error:"invalid body"},{status:400})}const t=b(s??{});if("error"in t)return Response.json({error:t.error},{status:400});const r=h(this.#t);await c(r);const i=await l(r).read(t.options);return Response.json({entries:i})}#i(e){const{internalSecret:s}=this.#s;if(s===void 0||s==="")return!1;const t=e.headers.get(S);return t!==null&&g(t,s)}async#h(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});const t=await this.#o().api.getSession({headers:e.headers}),r=t?.user.id;if(r===void 0)return Response.json({});const i=t?.session.expiresAt,o=t?.user,a=o?.role;return Response.json({...typeof o?.email=="string"&&o.email.length>0?{email:o.email}:{},...i instanceof Date?{expiresAtMs:i.getTime()}:{},...typeof o?.name=="string"&&o.name.length>0?{name:o.name}:{},...typeof a=="string"&&a.length>0?{role:a}:{},userId:r})}async fetch(e){const s=new URL(e.url);if(s.pathname===R)return this.#h(e);if(s.pathname===y)return this.#u(e);const t=this.#o();return await A(t,e,this.#s.basePath)??Response.json({error:"not an auth route"},{status:404})}}export{S as INTERNAL_SECRET_HEADER,v as LunoraAuthDO,y as READ_AUDIT_PATH,R as RESOLVE_SESSION_PATH};
1
+ import{b as u,d as h}from"./adapter-DA8DdALX.mjs";import{ensureAuthAuditTable as c,createAuthAuditReader as l}from"../audit.mjs";import{resolveAuthOptions as m,createAuth as d}from"./createAuth-8aUw8v8D.mjs";import{authDoSchemaStatements as p,authDoColumnAdditions as f}from"./authDoColumnAdditions-BCSs7qaN.mjs";import{handleAuthRequest as A}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const g=(n,e)=>{const s=Math.max(n.length,e.length);let t=n.length^e.length;for(let r=0;r<s;r+=1){const i=r<n.length?n.charCodeAt(r):0,o=r<e.length?e.charCodeAt(r):0;t|=i^o}return t===0},R="/__lunora/auth/session",y="/__lunora/auth/audit",S="x-lunora-auth-do-secret",b=n=>{if(n===null||typeof n!="object"||Array.isArray(n))return{error:"body must be a JSON object"};const e={};for(const[s,t]of Object.entries(n))if(s==="limit"||s==="sinceSeq"){if(typeof t!="number"||!Number.isFinite(t))return{error:`"${s}" must be a finite number`};e[s]=t}else if(s==="actorId"||s==="event"){if(typeof t!="string")return{error:`"${s}" must be a string`};e[s]=t}else return{error:`unknown audit read option "${s}"`};return{options:e}};class v{#s;#r;#t;#e;#n=!1;constructor(e,s,t={}){this.#t=e.storage,this.#r=s,this.#s=t}#o(){if(this.#e!==void 0)return this.#e;const e=this.#r();if(!this.#n){const s=m(e);for(const t of p(s))[...this.#t.sql.exec(t)];for(const t of f(s,r=>this.#a(r)))[...this.#t.sql.exec(t)];this.#n=!0}return this.#e=d({...e,database:u(this.#t)}),this.#e}#a(e){return[...this.#t.sql.exec("SELECT name FROM pragma_table_info(?)",e)].map(t=>String(t.name))}async#u(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});let s;try{s=await e.json()}catch{return Response.json({error:"invalid body"},{status:400})}const t=b(s??{});if("error"in t)return Response.json({error:t.error},{status:400});const r=h(this.#t);await c(r);const i=await l(r).read(t.options);return Response.json({entries:i})}#i(e){const{internalSecret:s}=this.#s;if(s===void 0||s==="")return!1;const t=e.headers.get(S);return t!==null&&g(t,s)}async#h(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});const t=await this.#o().api.getSession({headers:e.headers}),r=t?.user.id;if(r===void 0)return Response.json({});const i=t?.session.expiresAt,o=t?.user,a=o?.role;return Response.json({...typeof o?.email=="string"&&o.email.length>0?{email:o.email}:{},...i instanceof Date?{expiresAtMs:i.getTime()}:{},...typeof o?.name=="string"&&o.name.length>0?{name:o.name}:{},...typeof a=="string"&&a.length>0?{role:a}:{},userId:r})}async fetch(e){const s=new URL(e.url);if(s.pathname===R)return this.#h(e);if(s.pathname===y)return this.#u(e);const t=this.#o();return await A(t,e,this.#s.basePath)??Response.json({error:"not an auth route"},{status:404})}}export{S as INTERNAL_SECRET_HEADER,v as LunoraAuthDO,y as READ_AUDIT_PATH,R as RESOLVE_SESSION_PATH};
@@ -0,0 +1 @@
1
+ import{createAuthMiddleware as d}from"better-auth/api";import{o as c}from"./on-cloudflare-edge-Dx6gFjBO.mjs";import{appendAuthAuditEntry as l}from"../audit.mjs";const f=r=>{const t=r.toLowerCase(),e=i=>t===i||t.endsWith(i);if(e("/sign-up/email")||e("/sign-up"))return"sign-up";if(e("/sign-in/social")||e("/sign-in/magic-link"))return"sign-in-initiated";if(t.includes("/sign-in/")||t.includes("/callback/")||e("/magic-link/verify")||t.includes("/two-factor/verify-"))return"sign-in";if(e("/sign-out"))return"sign-out";if(e("/change-password")||e("/set-password"))return"password-change";if(e("/reset-password")||e("/request-password-reset")||e("/forget-password"))return"password-reset";if(e("/verify-email"))return"email-verification";if(t.includes("/two-factor/enable")||t.includes("/totp/enable"))return"mfa-enable";if(t.includes("/two-factor/disable")||t.includes("/totp/disable"))return"mfa-disable";if(e("/refresh-token")||e("/token"))return"token-refresh";if(e("/revoke-session")||e("/revoke-sessions")||e("/revoke-other-sessions"))return"session-revoke";if(e("/link-social"))return"account-link";if(e("/unlink-account"))return"account-unlink"},a=(r,t)=>r.headers?.get(t)??r.request?.headers.get(t)??void 0,v=(r,t)=>{if(c()){const e=a(r,"cf-connecting-ip");if(e!==void 0)return e}if(t===!0)return a(r,"x-forwarded-for")?.split(",")[0]?.trim()},p=r=>{const t=r.context?.newSession??r.context?.session,e=t?.user?.id??t?.session?.userId,i=t?.user?.email;return{...e===void 0?{}:{actorId:e},...i===void 0?{}:{actorEmail:i}}},h=r=>{const t=r.context?.returned;if(t instanceof Error)return"failure";if(typeof t=="object"&&t!==null&&"status"in t){const e=Number(t.status);if(Number.isFinite(e)&&e>=400)return"failure"}return"success"},u=320,g=(r,t)=>{if(t!=="sign-in"&&t!=="sign-in-initiated")return;const e=r.body?.email??r.body?.username;if(!(typeof e!="string"||e.length===0))return e.length>u?e.slice(0,u):e},m=(r,{now:t=Date.now(),trustProxyHeaders:e}={})=>{const i=r.path===void 0?void 0:f(r.path);if(i===void 0)return;const s=v(r,e),n=a(r,"user-agent"),o=g(r,i);return{...p(r),event:i,outcome:h(r),ts:t,...s===void 0?{}:{ip:s},...o===void 0?{}:{targetEmail:o},...n===void 0?{}:{userAgent:n},detail:{path:r.path}}},w=r=>d(async t=>{try{const e=m(t,{trustProxyHeaders:r.trustProxyHeaders});if(e!==void 0){const i=await l(r.executor,e,{redactDetail:r.redactDetail,retention:r.retention});r.onRecord!==void 0&&await r.onRecord(i)}}catch(e){console.error("@lunora/auth: audit hook failed to record event",e)}}),A=(r,t)=>{const e=w(t),i=r.hooks?.after,s=i?async n=>{const o=await i(n);return await e(n),o}:e;return{...r,hooks:{...r.hooks,after:s}}};export{w as authAuditHook,m as buildAuditEntry,f as eventForPath,A as withAuthAudit};
@@ -1 +1 @@
1
- import{LunoraError as g}from"@lunora/errors";import{getMigrations as u}from"better-auth/db/migration";import{resolveAuthOptions as w}from"./createAuth-BI6qU0c_.mjs";const I=/pragma_index_list\s*\(/iu,_=/sqlite_master/iu,y=/^["`[]|["\]`]$/gu,R=/\s+/u,x=/create\s+unique\s+index/iu,M=/\bwhere\b/iu,T=t=>I.test(t)&&_.test(t),D=t=>(t.trim().split(R)[0]??"").replaceAll(y,"").trim(),A=t=>{const n=t.indexOf("(");if(n===-1)return{columns:[],tail:""};let s=0;for(let e=n;e<t.length;e+=1){const o=t[e];if(o==="(")s+=1;else if(o===")"&&(s-=1,s===0))return{columns:t.slice(n+1,e).split(",").map(i=>D(i)).filter(i=>i!==""),tail:t.slice(e+1)}}return{columns:[],tail:""}},P=async t=>{const n=t.prepare("SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index'"),{results:s}=await n.all(),e=[];for(const o of s??[]){const i=o.name,a=o.tbl_name,r=o.sql;if(typeof i!="string"||typeof a!="string")continue;if(typeof r!="string"){e.push({columnPosition:0,indexName:i,isPartial:0,isUnique:1,tableName:a});continue}const m=x.test(r)?1:0,{columns:f,tail:d}=A(r),h=M.test(d)?1:0;for(const[E,b]of f.entries())e.push({columnName:b,columnPosition:E,indexName:i,isPartial:h,isUnique:m,tableName:a})}return e},N=t=>{const n=async()=>{const e=await P(t);return{meta:{},results:e,success:!0}},s={all:n,bind:()=>s,run:n};return s},U=t=>new Proxy(t,{get(n,s,e){if(s==="prepare")return i=>T(i)?N(n):n.prepare(i);const o=Reflect.get(n,s,e);return typeof o=="function"?o.bind(n):o}}),O=t=>typeof t=="object"&&t!==null&&typeof t.prepare=="function"&&typeof t.batch=="function",l=t=>{const{database:n}=t;if(!(n&&typeof n!="function"))throw new g("AUTH_MIGRATOR_UNSUPPORTED",n?"@lunora/auth: this auth instance's `database` is a custom adapter, which better-auth's migrator cannot drive.":"@lunora/auth: this auth instance has no `database`, so better-auth's migrator has nothing to introspect.")},p=t=>O(t.database)?{...t,database:U(t.database)}:t,c=new WeakMap,H=async t=>{const{options:n}=t;l(n);const s=c.get(n);if(s){await s;return}const e=(async()=>{const{runMigrations:o}=await u(p(n));await o()})();c.set(n,e);try{await e}catch(o){throw c.delete(n),o}},L=async t=>{const n=w(t);l(n);const{compileMigrations:s}=await u(p(n));return s()};export{L as compileMigrationsSql,H as ensureMigrated};
1
+ import{LunoraError as g}from"@lunora/errors";import{getMigrations as u}from"better-auth/db/migration";import{resolveAuthOptions as w}from"./createAuth-8aUw8v8D.mjs";const I=/pragma_index_list\s*\(/iu,_=/sqlite_master/iu,y=/^["`[]|["\]`]$/gu,R=/\s+/u,x=/create\s+unique\s+index/iu,M=/\bwhere\b/iu,T=t=>I.test(t)&&_.test(t),D=t=>(t.trim().split(R)[0]??"").replaceAll(y,"").trim(),A=t=>{const n=t.indexOf("(");if(n===-1)return{columns:[],tail:""};let s=0;for(let e=n;e<t.length;e+=1){const o=t[e];if(o==="(")s+=1;else if(o===")"&&(s-=1,s===0))return{columns:t.slice(n+1,e).split(",").map(i=>D(i)).filter(i=>i!==""),tail:t.slice(e+1)}}return{columns:[],tail:""}},P=async t=>{const n=t.prepare("SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index'"),{results:s}=await n.all(),e=[];for(const o of s??[]){const i=o.name,a=o.tbl_name,r=o.sql;if(typeof i!="string"||typeof a!="string")continue;if(typeof r!="string"){e.push({columnPosition:0,indexName:i,isPartial:0,isUnique:1,tableName:a});continue}const m=x.test(r)?1:0,{columns:f,tail:d}=A(r),h=M.test(d)?1:0;for(const[E,b]of f.entries())e.push({columnName:b,columnPosition:E,indexName:i,isPartial:h,isUnique:m,tableName:a})}return e},N=t=>{const n=async()=>{const e=await P(t);return{meta:{},results:e,success:!0}},s={all:n,bind:()=>s,run:n};return s},U=t=>new Proxy(t,{get(n,s,e){if(s==="prepare")return i=>T(i)?N(n):n.prepare(i);const o=Reflect.get(n,s,e);return typeof o=="function"?o.bind(n):o}}),O=t=>typeof t=="object"&&t!==null&&typeof t.prepare=="function"&&typeof t.batch=="function",l=t=>{const{database:n}=t;if(!(n&&typeof n!="function"))throw new g("AUTH_MIGRATOR_UNSUPPORTED",n?"@lunora/auth: this auth instance's `database` is a custom adapter, which better-auth's migrator cannot drive.":"@lunora/auth: this auth instance has no `database`, so better-auth's migrator has nothing to introspect.")},p=t=>O(t.database)?{...t,database:U(t.database)}:t,c=new WeakMap,H=async t=>{const{options:n}=t;l(n);const s=c.get(n);if(s){await s;return}const e=(async()=>{const{runMigrations:o}=await u(p(n));await o()})();c.set(n,e);try{await e}catch(o){throw c.delete(n),o}},L=async t=>{const n=w(t);l(n);const{compileMigrations:s}=await u(p(n));return s()};export{L as compileMigrationsSql,H as ensureMigrated};
@@ -0,0 +1 @@
1
+ import{LunoraError as c}from"@lunora/errors";import{betterAuth as l}from"better-auth";import{getIP as u}from"better-auth/api";import{o as f}from"./on-cloudflare-edge-Dx6gFjBO.mjs";import{validateSessionPolicy as h}from"./sessionPresets-C867Mlo4.mjs";const n="cf-connecting-ip",m=e=>{const t=e!==void 0&&e.length>0;return f()?t?[n,"x-forwarded-for"]:[n]:t?["x-forwarded-for"]:[]},a="/**",p=100,A=e=>{const t=e.rateLimit?.customRules??{};return t[a]!==void 0?t:{...t,[a]:(o,r)=>u(o,e)===null?{max:r.max*p,window:r.window}:r}},d=32,E=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<d},i=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,L=new Set(["127.0.0.1","localhost"]),g=e=>{if(typeof e!="string")return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"||!L.has(t.hostname)))return{allowedHosts:[t.hostname,`${t.hostname}:*`],fallback:e,protocol:"http"}},w=e=>{const t=g(e.baseURL)??e.baseURL;if(E(e.secret)){const s=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(d)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!i(t))throw new c("INTERNAL",s);console.warn(s)}const o=e.advanced??{},r=o.ipAddress??{};return{...e,advanced:{...o,defaultCookieAttributes:o.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},ipAddress:{...r,...r.ipAddressHeaders===void 0?{ipAddressHeaders:m(r.trustedProxies)}:{}},...o.useSecureCookies===void 0?{useSecureCookies:!i(t)}:{}},baseURL:t}},C=e=>{const t=w(e),o=t.rateLimit?.enabled===void 0,r=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1,s=t.rateLimit?.enabled===!1?{}:{rateLimit:{...t.rateLimit,...o?{enabled:!0}:{},...r?{storage:"database"}:{},customRules:A(t)}};return{...t,...s,...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},x=e=>{if(!e.secret||e.secret.trim()==="")throw new c("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&&h(e.session),l(C(e))};export{x as createAuth,C as resolveAuthOptions};
@@ -1 +1 @@
1
- import{INTERNAL_SECRET_HEADER as u,RESOLVE_SESSION_PATH as p,READ_AUDIT_PATH as f}from"./AUTH_DO_AUDIT_PATH-CS1FCCoC.mjs";import{DEFAULT_AUTH_BASE_PATH as h,isAuthRoutePath as A}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const g=l=>{const{basePath:c=h,internalSecret:a,namespace:o,objectName:d="auth"}=l,i=()=>{if(o)return o.get(o.idFromName(d))},m=async(t,n,r)=>{if(!a)return;const s=i();if(!s)return;const e=await s.fetch(new Request(new URL(t,n),{body:JSON.stringify(r),headers:{"content-type":"application/json",[u]:a},method:"POST"}));return e.ok?e:void 0};return{auditReader:{read:async t=>{const n=await m(f,"https://auth-do.invalid",t);return n?(await n.json())?.entries??[]:[]}},authHandler:async t=>{if(A(new URL(t.url).pathname,c))return i()?.fetch(t)},resolveIdentity:async t=>{if(!a)return null;const n=i();if(!n)return null;const r=new Headers(t.headers);r.set(u,a);const s=await n.fetch(new Request(new URL(p,t.url),{headers:r}));if(!s.ok)return null;const e=await s.json();return e?.userId?{...typeof e.email=="string"&&e.email.length>0?{email:e.email}:{},...typeof e.expiresAtMs=="number"&&Number.isFinite(e.expiresAtMs)?{expiresAtMs:e.expiresAtMs}:{},...typeof e.name=="string"&&e.name.length>0?{name:e.name}:{},...typeof e.role=="string"&&e.role.length>0?{role:e.role}:{},userId:e.userId}:null}}};export{g as createDoAuthWiring};
1
+ import{INTERNAL_SECRET_HEADER as u,RESOLVE_SESSION_PATH as p,READ_AUDIT_PATH as f}from"./AUTH_DO_AUDIT_PATH-BgmrlMWt.mjs";import{DEFAULT_AUTH_BASE_PATH as h,isAuthRoutePath as A}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const g=l=>{const{basePath:c=h,internalSecret:a,namespace:o,objectName:d="auth"}=l,i=()=>{if(o)return o.get(o.idFromName(d))},m=async(t,n,r)=>{if(!a)return;const s=i();if(!s)return;const e=await s.fetch(new Request(new URL(t,n),{body:JSON.stringify(r),headers:{"content-type":"application/json",[u]:a},method:"POST"}));return e.ok?e:void 0};return{auditReader:{read:async t=>{const n=await m(f,"https://auth-do.invalid",t);return n?(await n.json())?.entries??[]:[]}},authHandler:async t=>{if(A(new URL(t.url).pathname,c))return i()?.fetch(t)},resolveIdentity:async t=>{if(!a)return null;const n=i();if(!n)return null;const r=new Headers(t.headers);r.set(u,a);const s=await n.fetch(new Request(new URL(p,t.url),{headers:r}));if(!s.ok)return null;const e=await s.json();return e?.userId?{...typeof e.email=="string"&&e.email.length>0?{email:e.email}:{},...typeof e.expiresAtMs=="number"&&Number.isFinite(e.expiresAtMs)?{expiresAtMs:e.expiresAtMs}:{},...typeof e.name=="string"&&e.name.length>0?{name:e.name}:{},...typeof e.role=="string"&&e.role.length>0?{role:e.role}:{},userId:e.userId}:null}}};export{g as createDoAuthWiring};
@@ -0,0 +1 @@
1
+ const o=()=>globalThis.navigator?.userAgent==="Cloudflare-Workers";export{o};
@@ -1 +1 @@
1
- const o=n=>`"${n.replaceAll('"','""')}"`,O={params:[],sql:"0"},E=(n,r)=>r?{column:`LOWER(${n})`,placeholder:"LOWER(?)"}:{column:n,placeholder:"?"},I=(n,r,a,t)=>{if(typeof r!="string")return O;const s=E(n,t);return a==="contains"?{params:[r],sql:`instr(${s.column}, ${s.placeholder}) > 0`}:a==="starts_with"?{params:[r],sql:`instr(${s.column}, ${s.placeholder}) = 1`}:{params:[r,r],sql:`substr(${s.column}, -length(?)) = ${s.placeholder}`}},f=(n,r,a,t)=>{if(r===null)return{params:[],sql:`${n} IS ${t?"NOT ":""}NULL`};const s=E(n,a),e=t?"<>":"=";return{params:[r],sql:`${s.column} ${e} ${s.placeholder}`}},d=(n,r,a,t)=>{const s=Array.isArray(r)?r:[];if(s.length===0)return{params:[],sql:a?"1":"0"};const e=E(n,t),c=s.map(()=>e.placeholder).join(", ");return{params:[...s],sql:`${e.column} ${a?"NOT IN":"IN"} (${c})`}},R=n=>{const r=o(n.field),{mode:a,operator:t,value:s}=n,e=a==="insensitive";switch(t){case"contains":case"ends_with":case"starts_with":return I(r,s,t,e);case"gt":return{params:[s],sql:`${r} > ?`};case"gte":return{params:[s],sql:`${r} >= ?`};case"in":return d(r,s,!1,e);case"lt":return{params:[s],sql:`${r} < ?`};case"lte":return{params:[s],sql:`${r} <= ?`};case"ne":return f(r,s,e,!0);case"not_in":return d(r,s,!0,e);default:return f(r,s,e,!1)}},$=n=>{if(n.length===0)return{params:[],sql:""};let r=R(n[0]);for(const a of n.slice(1)){const t=R(a),s=a.connector==="OR"?"OR":"AND";r={params:[...r.params,...t.params],sql:`(${r.sql} ${s} ${t.sql})`}}return r},m=n=>n.sql?` WHERE ${n.sql}`:"",N=n=>{const r=(a,t)=>{const s=$(t);return n.all(`SELECT * FROM ${o(a)}${m(s)}`,s.params)};return{consumeOne:async(a,t)=>{const s=$(t),e=o(a),[c]=await n.all(`DELETE FROM ${e} WHERE rowid IN (SELECT rowid FROM ${e}${m(s)} LIMIT 1) RETURNING *`,s.params);return c},count:async(a,t)=>{const s=$(t),[e]=await n.all(`SELECT COUNT(*) AS __count FROM ${o(a)}${m(s)}`,s.params);return Number(e?.__count??0)},create:async(a,t)=>{const s=Object.keys(t),e=s.map(()=>"?").join(", "),c=`INSERT INTO ${o(a)} (${s.map(l=>o(l)).join(", ")}) VALUES (${e})`;return await n.run(c,s.map(l=>t[l])),{...t}},incrementOne:async(a,t,s,e)=>{const c=o(a),l=Object.keys(s),p=e?Object.keys(e):[],u=$(t);if(l.length===0&&p.length===0){const[i]=await n.all(`SELECT * FROM ${c}${m(u)} LIMIT 1`,u.params);return i}const T=[...l.map(i=>`${o(i)} = COALESCE(${o(i)}, 0) + ?`),...p.map(i=>`${o(i)} = ?`)].join(", "),[h]=await n.all(`UPDATE ${c} SET ${T} WHERE rowid IN (SELECT rowid FROM ${c}${m(u)} LIMIT 1) RETURNING *`,[...l.map(i=>s[i]),...p.map(i=>e[i]),...u.params]);return h},read:async(a,t)=>{const s=$(t.where),e=[...s.params];let c=`SELECT * FROM ${o(a)}${m(s)}`;return t.sortBy&&(c+=` ORDER BY ${o(t.sortBy.field)} ${t.sortBy.direction==="asc"?"ASC":"DESC"}`),t.limit!==void 0&&(c+=" LIMIT ?",e.push(Math.trunc(t.limit))),t.offset&&(c+=`${t.limit===void 0?" LIMIT -1":""} OFFSET ?`,e.push(Math.trunc(t.offset))),n.all(c,e)},remove:async(a,t)=>{const s=$(t);return(await n.all(`DELETE FROM ${o(a)}${m(s)} RETURNING *`,s.params)).length},update:async(a,t,s)=>{const e=Object.keys(s);if(e.length===0)return r(a,t);const c=e.map(p=>`${o(p)} = ?`).join(", "),l=$(t);return n.all(`UPDATE ${o(a)} SET ${c}${m(l)} RETURNING *`,[...e.map(p=>s[p]),...l.params])}}},g=n=>({all:async(r,a)=>(await n.prepare(r).bind(...a).all()).results??[],run:async(r,a)=>{await n.prepare(r).bind(...a).run()}});export{N as createSqlAuthStore,g as d1Executor};
1
+ const l=r=>`"${r.replaceAll('"','""')}"`,O={params:[],sql:"0"},E=(r,a)=>a?{column:`LOWER(${r})`,placeholder:"LOWER(?)"}:{column:r,placeholder:"?"},g=(r,a,s,n)=>{if(typeof a!="string")return O;const t=E(r,n);return s==="contains"?{params:[a],sql:`instr(${t.column}, ${t.placeholder}) > 0`}:s==="starts_with"?{params:[a],sql:`instr(${t.column}, ${t.placeholder}) = 1`}:{params:[a,a],sql:`substr(${t.column}, -length(?)) = ${t.placeholder}`}},f=(r,a,s,n)=>{if(a===null)return{params:[],sql:`${r} IS ${n?"NOT ":""}NULL`};const t=E(r,s),e=n?"<>":"=";return{params:[a],sql:`${t.column} ${e} ${t.placeholder}`}},d=(r,a,s,n)=>{const t=Array.isArray(a)?a:[];if(t.length===0)return{params:[],sql:s?"1":"0"};const e=E(r,n),o=t.map(()=>e.placeholder).join(", ");return{params:[...t],sql:`${e.column} ${s?"NOT IN":"IN"} (${o})`}},I=r=>{const a=l(r.field),{mode:s,operator:n,value:t}=r,e=s==="insensitive";switch(n){case"contains":case"ends_with":case"starts_with":return g(a,t,n,e);case"gt":return{params:[t],sql:`${a} > ?`};case"gte":return{params:[t],sql:`${a} >= ?`};case"in":return d(a,t,!1,e);case"lt":return{params:[t],sql:`${a} < ?`};case"lte":return{params:[t],sql:`${a} <= ?`};case"ne":return f(a,t,e,!0);case"not_in":return d(a,t,!0,e);default:return f(a,t,e,!1)}},R=(r,a)=>{if(r.length===0)return;const s=r.map(n=>I(n));return{params:s.flatMap(n=>n.params),sql:s.map(n=>`(${n.sql})`).join(` ${a} `)}},$=r=>{const a=[R(r.filter(s=>s.connector!=="OR"),"AND"),R(r.filter(s=>s.connector==="OR"),"OR")].filter(s=>s!==void 0);return a.length===0?{params:[],sql:""}:{params:a.flatMap(s=>s.params),sql:a.map(s=>`(${s.sql})`).join(" AND ")}},m=r=>r.sql?` WHERE ${r.sql}`:"",N=r=>{const a=(s,n)=>{const t=$(n);return r.all(`SELECT * FROM ${l(s)}${m(t)}`,t.params)};return{consumeOne:async(s,n)=>{const t=$(n),e=l(s),[o]=await r.all(`DELETE FROM ${e} WHERE rowid IN (SELECT rowid FROM ${e}${m(t)} LIMIT 1) RETURNING *`,t.params);return o},count:async(s,n)=>{const t=$(n),[e]=await r.all(`SELECT COUNT(*) AS __count FROM ${l(s)}${m(t)}`,t.params);return Number(e?.__count??0)},create:async(s,n)=>{const t=Object.keys(n),e=t.map(()=>"?").join(", "),o=`INSERT INTO ${l(s)} (${t.map(c=>l(c)).join(", ")}) VALUES (${e})`;return await r.run(o,t.map(c=>n[c])),{...n}},incrementOne:async(s,n,t,e)=>{const o=l(s),c=Object.keys(t),p=e?Object.keys(e):[],u=$(n);if(c.length===0&&p.length===0){const[i]=await r.all(`SELECT * FROM ${o}${m(u)} LIMIT 1`,u.params);return i}const h=[...c.map(i=>`${l(i)} = COALESCE(${l(i)}, 0) + ?`),...p.map(i=>`${l(i)} = ?`)].join(", "),[T]=await r.all(`UPDATE ${o} SET ${h} WHERE rowid IN (SELECT rowid FROM ${o}${m(u)} LIMIT 1) RETURNING *`,[...c.map(i=>t[i]),...p.map(i=>e[i]),...u.params]);return T},read:async(s,n)=>{const t=$(n.where),e=[...t.params];let o=`SELECT * FROM ${l(s)}${m(t)}`;return n.sortBy&&(o+=` ORDER BY ${l(n.sortBy.field)} ${n.sortBy.direction==="asc"?"ASC":"DESC"}`),n.limit!==void 0&&(o+=" LIMIT ?",e.push(Math.trunc(n.limit))),n.offset&&(o+=`${n.limit===void 0?" LIMIT -1":""} OFFSET ?`,e.push(Math.trunc(n.offset))),r.all(o,e)},remove:async(s,n)=>{const t=$(n);return(await r.all(`DELETE FROM ${l(s)}${m(t)} RETURNING *`,t.params)).length},update:async(s,n,t)=>{const e=Object.keys(t);if(e.length===0)return a(s,n);const o=e.map(p=>`${l(p)} = ?`).join(", "),c=$(n);return r.all(`UPDATE ${l(s)} SET ${o}${m(c)} RETURNING *`,[...e.map(p=>t[p]),...c.params])}}},w=r=>({all:async(a,s)=>(await r.prepare(a).bind(...s).all()).results??[],run:async(a,s)=>{await r.prepare(a).bind(...s).run()}});export{N as createSqlAuthStore,w as d1Executor};
package/dist/store.d.mts CHANGED
@@ -69,10 +69,28 @@ interface AuthStore {
69
69
  update: (model: string, where: ReadonlyArray<AuthWhereClause>, values: AuthRow) => Promise<AuthRow[]>;
70
70
  }
71
71
  /**
72
- * Evaluate a better-auth where clause list against a row. Clauses fold
73
- * left-to-right by their `connector` (`AND` by default, `OR` when set) — the
74
- * same precedence better-auth's own adapters use. An empty list matches every
75
- * row. Exported so any in-memory-style {@link AuthStore} can reuse it.
72
+ * Evaluate a better-auth where clause list against a row.
73
+ *
74
+ * better-auth hands an adapter a FLAT list in which each clause carries its own
75
+ * `connector` (`AND` by default). Every persistent adapter it ships resolves that
76
+ * by PARTITIONING: `AND(and-clauses) AND OR(or-clauses)`. The OR clauses are
77
+ * alternatives among themselves, never an escape hatch from the AND clauses —
78
+ * `@better-auth/kysely-adapter` pushes each group into its own `.where()` (two
79
+ * `.where()` calls are ANDed), `@better-auth/drizzle-adapter` ends in
80
+ * `and(andClause, orClause)`, and `@better-auth/prisma-adapter` emits
81
+ * `{ AND: […], OR: […] }`, which Prisma also ANDs.
82
+ *
83
+ * This used to fold the list left-associatively instead, so `[A, B(OR), C(OR)]`
84
+ * meant `A OR B OR C` — strictly BROADER than any of them. On a credential lookup
85
+ * that is an authentication bypass in shape: a row failing the primary condition
86
+ * is still returned because a secondary one matched. `@better-auth/memory-adapter`
87
+ * does fold left, but it is the only one and it re-evaluates `where[0]` inside the
88
+ * same loop; the persistent adapters are the contract worth mirroring, and they
89
+ * are also what a plugin author will have tested against.
90
+ *
91
+ * An empty list matches every row, and a list with no OR clause is unaffected —
92
+ * which is every clause list better-auth 1.7.1 itself builds. Exported so any
93
+ * in-memory-style {@link AuthStore} can reuse it.
76
94
  */
77
95
  declare const matchesWhere: (row: AuthRow, where: ReadonlyArray<AuthWhereClause>) => boolean;
78
96
  /**
package/dist/store.d.ts CHANGED
@@ -69,10 +69,28 @@ interface AuthStore {
69
69
  update: (model: string, where: ReadonlyArray<AuthWhereClause>, values: AuthRow) => Promise<AuthRow[]>;
70
70
  }
71
71
  /**
72
- * Evaluate a better-auth where clause list against a row. Clauses fold
73
- * left-to-right by their `connector` (`AND` by default, `OR` when set) — the
74
- * same precedence better-auth's own adapters use. An empty list matches every
75
- * row. Exported so any in-memory-style {@link AuthStore} can reuse it.
72
+ * Evaluate a better-auth where clause list against a row.
73
+ *
74
+ * better-auth hands an adapter a FLAT list in which each clause carries its own
75
+ * `connector` (`AND` by default). Every persistent adapter it ships resolves that
76
+ * by PARTITIONING: `AND(and-clauses) AND OR(or-clauses)`. The OR clauses are
77
+ * alternatives among themselves, never an escape hatch from the AND clauses —
78
+ * `@better-auth/kysely-adapter` pushes each group into its own `.where()` (two
79
+ * `.where()` calls are ANDed), `@better-auth/drizzle-adapter` ends in
80
+ * `and(andClause, orClause)`, and `@better-auth/prisma-adapter` emits
81
+ * `{ AND: […], OR: […] }`, which Prisma also ANDs.
82
+ *
83
+ * This used to fold the list left-associatively instead, so `[A, B(OR), C(OR)]`
84
+ * meant `A OR B OR C` — strictly BROADER than any of them. On a credential lookup
85
+ * that is an authentication bypass in shape: a row failing the primary condition
86
+ * is still returned because a secondary one matched. `@better-auth/memory-adapter`
87
+ * does fold left, but it is the only one and it re-evaluates `where[0]` inside the
88
+ * same loop; the persistent adapters are the contract worth mirroring, and they
89
+ * are also what a plugin author will have tested against.
90
+ *
91
+ * An empty list matches every row, and a list with no OR clause is unaffected —
92
+ * which is every clause list better-auth 1.7.1 itself builds. Exported so any
93
+ * in-memory-style {@link AuthStore} can reuse it.
76
94
  */
77
95
  declare const matchesWhere: (row: AuthRow, where: ReadonlyArray<AuthWhereClause>) => boolean;
78
96
  /**
package/dist/store.mjs CHANGED
@@ -1 +1 @@
1
- const d=i=>typeof i=="string"?i:void 0,a=i=>i==null,f=(i,o,r)=>{if(r){const s=d(i),t=d(o);if(s!==void 0&&t!==void 0)return s.toLowerCase()===t.toLowerCase()}return i===o},m=(i,o,r,s)=>{const t=d(i),e=d(o);if(t===void 0||e===void 0)return!1;const[n,c]=s?[t.toLowerCase(),e.toLowerCase()]:[t,e];return r==="contains"?n.includes(c):r==="starts_with"?n.startsWith(c):n.endsWith(c)},v=(i,o)=>{const{field:r,mode:s,operator:t,value:e}=o,n=i[r],c=s==="insensitive";switch(t){case"contains":case"ends_with":case"starts_with":return m(n,e,t,c);case"gt":return!a(e)&&n>e;case"gte":return!a(e)&&n>=e;case"in":return Array.isArray(e)&&e.some(u=>f(n,u,c));case"lt":return!a(e)&&n<e;case"lte":return!a(e)&&n<=e;case"ne":return!f(n,e,c);case"not_in":return Array.isArray(e)&&!e.some(u=>f(n,u,c));default:return a(e)?a(n):f(n,e,c)}},h=(i,o)=>{if(typeof i=="string"&&typeof o=="string")return i.localeCompare(o);const r=Number(i),s=Number(o);return r<s?-1:r>s?1:0},w=(i,o)=>{const r=o.direction==="asc"?1:-1;return i.toSorted((s,t)=>{const e=s[o.field],n=t[o.field];return a(e)&&a(n)?0:a(e)?-r:a(n)?r:h(e,n)*r})},l=(i,o)=>{let r=!0;for(const[s,t]of o.entries()){const e=v(i,t);s===0?r=e:t.connector==="OR"?r=r||e:r=r&&e}return r},p=()=>{const i=new Map,o=r=>{const s=i.get(r);if(s)return s;const t=[];return i.set(r,t),t};return{consumeOne:(r,s)=>{const t=o(r),e=t.findIndex(c=>l(c,s));if(e===-1)return Promise.resolve(void 0);const[n]=t.splice(e,1);return Promise.resolve(n?{...n}:void 0)},count:(r,s)=>Promise.resolve(o(r).filter(t=>l(t,s)).length),create:(r,s)=>{const t={...s};return o(r).push(t),Promise.resolve({...t})},incrementOne:(r,s,t,e)=>{const n=o(r).find(c=>l(c,s));if(!n)return Promise.resolve(void 0);for(const[c,u]of Object.entries(t))n[c]=(typeof n[c]=="number"?n[c]:0)+u;return e&&Object.assign(n,e),Promise.resolve({...n})},read:(r,s)=>{let t=o(r).filter(e=>l(e,s.where));return s.sortBy&&(t=w(t,s.sortBy)),s.offset&&(t=t.slice(s.offset)),s.limit!==void 0&&(t=t.slice(0,s.limit)),Promise.resolve(t.map(e=>({...e})))},remove:(r,s)=>{const t=o(r),e=t.filter(c=>!l(c,s)),n=t.length-e.length;return t.length=0,t.push(...e),Promise.resolve(n)},update:(r,s,t)=>{const e=o(r).filter(n=>l(n,s));for(const n of e)Object.assign(n,t);return Promise.resolve(e.map(n=>({...n})))}}};export{p as createMemoryAuthStore,l as matchesWhere};
1
+ const d=i=>typeof i=="string"?i:void 0,a=i=>i==null,f=(i,o,n)=>{if(n){const s=d(i),e=d(o);if(s!==void 0&&e!==void 0)return s.toLowerCase()===e.toLowerCase()}return i===o},v=(i,o,n,s)=>{const e=d(i),t=d(o);if(e===void 0||t===void 0)return!1;const[r,c]=s?[e.toLowerCase(),t.toLowerCase()]:[e,t];return n==="contains"?r.includes(c):n==="starts_with"?r.startsWith(c):r.endsWith(c)},m=(i,o)=>{const{field:n,mode:s,operator:e,value:t}=o,r=i[n],c=s==="insensitive";switch(e){case"contains":case"ends_with":case"starts_with":return v(r,t,e,c);case"gt":return!a(t)&&r>t;case"gte":return!a(t)&&r>=t;case"in":return Array.isArray(t)&&t.some(u=>f(r,u,c));case"lt":return!a(t)&&r<t;case"lte":return!a(t)&&r<=t;case"ne":return!f(r,t,c);case"not_in":return Array.isArray(t)&&!t.some(u=>f(r,u,c));default:return a(t)?a(r):f(r,t,c)}},h=(i,o)=>{if(typeof i=="string"&&typeof o=="string")return i.localeCompare(o);const n=Number(i),s=Number(o);return n<s?-1:n>s?1:0},g=(i,o)=>{const n=o.direction==="asc"?1:-1;return i.toSorted((s,e)=>{const t=s[o.field],r=e[o.field];return a(t)&&a(r)?0:a(t)?-n:a(r)?n:h(t,r)*n})},l=(i,o)=>{const n=o.filter(e=>e.connector==="OR");return o.filter(e=>e.connector!=="OR").every(e=>m(i,e))&&(n.length===0||n.some(e=>m(i,e)))},p=()=>{const i=new Map,o=n=>{const s=i.get(n);if(s)return s;const e=[];return i.set(n,e),e};return{consumeOne:(n,s)=>{const e=o(n),t=e.findIndex(c=>l(c,s));if(t===-1)return Promise.resolve(void 0);const[r]=e.splice(t,1);return Promise.resolve(r?{...r}:void 0)},count:(n,s)=>Promise.resolve(o(n).filter(e=>l(e,s)).length),create:(n,s)=>{const e={...s};return o(n).push(e),Promise.resolve({...e})},incrementOne:(n,s,e,t)=>{const r=o(n).find(c=>l(c,s));if(!r)return Promise.resolve(void 0);for(const[c,u]of Object.entries(e))r[c]=(typeof r[c]=="number"?r[c]:0)+u;return t&&Object.assign(r,t),Promise.resolve({...r})},read:(n,s)=>{let e=o(n).filter(t=>l(t,s.where));return s.sortBy&&(e=g(e,s.sortBy)),s.offset&&(e=e.slice(s.offset)),s.limit!==void 0&&(e=e.slice(0,s.limit)),Promise.resolve(e.map(t=>({...t})))},remove:(n,s)=>{const e=o(n),t=e.filter(c=>!l(c,s)),r=e.length-t.length;return e.length=0,e.push(...t),Promise.resolve(r)},update:(n,s,e)=>{const t=o(n).filter(r=>l(r,s));for(const r of t)Object.assign(r,e);return Promise.resolve(t.map(r=>({...r})))}}};export{p as createMemoryAuthStore,l as matchesWhere};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/auth",
3
- "version": "1.0.0-alpha.120",
3
+ "version": "1.0.0-alpha.122",
4
4
  "description": "Auth for Lunora — a thin better-auth wrapper: email/password, OAuth, plugins, D1-backed",
5
5
  "keywords": [
6
6
  "auth",
@@ -103,8 +103,8 @@
103
103
  "@better-auth/oauth-provider": "1.7.1",
104
104
  "@better-auth/passkey": "1.7.1",
105
105
  "@better-auth/scim": "1.7.1",
106
- "@lunora/errors": "1.0.0-alpha.33",
107
- "@lunora/values": "1.0.0-alpha.41",
106
+ "@lunora/errors": "1.0.0-alpha.34",
107
+ "@lunora/values": "1.0.0-alpha.42",
108
108
  "@visulima/disposable-email-domains": "1.1.0",
109
109
  "@visulima/email-verifier": "1.0.2",
110
110
  "@visulima/free-email-domains": "1.0.1",
@@ -1 +0,0 @@
1
- import{createAuthMiddleware as d}from"better-auth/api";import{appendAuthAuditEntry as c}from"../audit.mjs";import{onCloudflareEdge as l}from"./createAuth-BI6qU0c_.mjs";const f=r=>{const t=r.toLowerCase(),e=i=>t===i||t.endsWith(i);if(e("/sign-up/email")||e("/sign-up"))return"sign-up";if(e("/sign-in/social")||e("/sign-in/magic-link"))return"sign-in-initiated";if(t.includes("/sign-in/")||t.includes("/callback/")||e("/magic-link/verify")||t.includes("/two-factor/verify-"))return"sign-in";if(e("/sign-out"))return"sign-out";if(e("/change-password")||e("/set-password"))return"password-change";if(e("/reset-password")||e("/request-password-reset")||e("/forget-password"))return"password-reset";if(e("/verify-email"))return"email-verification";if(t.includes("/two-factor/enable")||t.includes("/totp/enable"))return"mfa-enable";if(t.includes("/two-factor/disable")||t.includes("/totp/disable"))return"mfa-disable";if(e("/refresh-token")||e("/token"))return"token-refresh";if(e("/revoke-session")||e("/revoke-sessions")||e("/revoke-other-sessions"))return"session-revoke";if(e("/link-social"))return"account-link";if(e("/unlink-account"))return"account-unlink"},a=(r,t)=>r.headers?.get(t)??r.request?.headers.get(t)??void 0,v=(r,t)=>{if(l()){const e=a(r,"cf-connecting-ip");if(e!==void 0)return e}if(t===!0)return a(r,"x-forwarded-for")?.split(",")[0]?.trim()},p=r=>{const t=r.context?.newSession??r.context?.session,e=t?.user?.id??t?.session?.userId,i=t?.user?.email;return{...e===void 0?{}:{actorId:e},...i===void 0?{}:{actorEmail:i}}},h=r=>{const t=r.context?.returned;if(t instanceof Error)return"failure";if(typeof t=="object"&&t!==null&&"status"in t){const e=Number(t.status);if(Number.isFinite(e)&&e>=400)return"failure"}return"success"},u=320,g=(r,t)=>{if(t!=="sign-in"&&t!=="sign-in-initiated")return;const e=r.body?.email??r.body?.username;if(!(typeof e!="string"||e.length===0))return e.length>u?e.slice(0,u):e},m=(r,{now:t=Date.now(),trustProxyHeaders:e}={})=>{const i=r.path===void 0?void 0:f(r.path);if(i===void 0)return;const s=v(r,e),n=a(r,"user-agent"),o=g(r,i);return{...p(r),event:i,outcome:h(r),ts:t,...s===void 0?{}:{ip:s},...o===void 0?{}:{targetEmail:o},...n===void 0?{}:{userAgent:n},detail:{path:r.path}}},w=r=>d(async t=>{try{const e=m(t,{trustProxyHeaders:r.trustProxyHeaders});if(e!==void 0){const i=await c(r.executor,e,{redactDetail:r.redactDetail,retention:r.retention});r.onRecord!==void 0&&await r.onRecord(i)}}catch(e){console.error("@lunora/auth: audit hook failed to record event",e)}}),A=(r,t)=>{const e=w(t),i=r.hooks?.after,s=i?async n=>{const o=await i(n);return await e(n),o}:e;return{...r,hooks:{...r.hooks,after:s}}};export{w as authAuditHook,m as buildAuditEntry,f as eventForPath,A as withAuthAudit};
@@ -1 +0,0 @@
1
- import{LunoraError as c}from"@lunora/errors";import{betterAuth as l}from"better-auth";import{getIP as u}from"better-auth/api";import{validateSessionPolicy as f}from"./sessionPresets-C867Mlo4.mjs";const n="cf-connecting-ip",h=()=>globalThis.navigator?.userAgent==="Cloudflare-Workers",m=e=>{const t=e!==void 0&&e.length>0;return h()?t?[n,"x-forwarded-for"]:[n]:t?["x-forwarded-for"]:[]},a="/**",p=100,A=e=>{const t=e.rateLimit?.customRules??{};return t[a]!==void 0?t:{...t,[a]:(o,r)=>u(o,e)===null?{max:r.max*p,window:r.window}:r}},d=32,E=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<d},i=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,g=new Set(["127.0.0.1","localhost"]),L=e=>{if(typeof e!="string")return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"||!g.has(t.hostname)))return{allowedHosts:[t.hostname,`${t.hostname}:*`],fallback:e,protocol:"http"}},C=e=>{const t=L(e.baseURL)??e.baseURL;if(E(e.secret)){const s=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(d)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!i(t))throw new c("INTERNAL",s);console.warn(s)}const o=e.advanced??{},r=o.ipAddress??{};return{...e,advanced:{...o,defaultCookieAttributes:o.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},ipAddress:{...r,...r.ipAddressHeaders===void 0?{ipAddressHeaders:m(r.trustedProxies)}:{}},...o.useSecureCookies===void 0?{useSecureCookies:!i(t)}:{}},baseURL:t}},v=e=>{const t=C(e),o=t.rateLimit?.enabled===void 0,r=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1,s=t.rateLimit?.enabled===!1?{}:{rateLimit:{...t.rateLimit,...o?{enabled:!0}:{},...r?{storage:"database"}:{},customRules:A(t)}};return{...t,...s,...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},H=e=>{if(!e.secret||e.secret.trim()==="")throw new c("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&&f(e.session),l(v(e))};export{H as createAuth,h as onCloudflareEdge,v as resolveAuthOptions};