@beeblock/svelar 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli/Cli.d.ts +1 -0
- package/dist/cli/bin.js +1714 -949
- package/dist/cli/commands/MakeEventCommand.d.ts +6 -2
- package/dist/cli/commands/MakeListenerCommand.d.ts +7 -2
- package/dist/cli/commands/NewCommand.d.ts +9 -2
- package/dist/cli/commands/NewCommandTemplates.d.ts +16 -0
- package/dist/cli/index.js +73 -70
- package/dist/hooks/index.js +2 -2
- package/dist/index.js +2 -2
- package/dist/search/index.d.ts +112 -0
- package/dist/search/index.js +1 -0
- package/dist/session/index.js +1 -1
- package/package.json +10 -2
package/dist/hooks/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var se=(a,e)=>()=>(a&&(e=a(a=0)),e);function X(a,e){let t=Symbol.for(a),n=globalThis;return n[t]||(n[t]=e()),n[t]}var B=se(()=>{"use strict"});var l=class{},m=class{middleware=[];namedMiddleware=new Map;use(e){return typeof e=="function"&&"prototype"in e&&typeof e.prototype?.handle=="function"?this.middleware.push(new e):this.middleware.push(e),this}register(e,t){return typeof t=="function"&&"prototype"in t&&typeof t.prototype?.handle=="function"?this.namedMiddleware.set(e,new t):this.namedMiddleware.set(e,t),this}get(e){return this.namedMiddleware.get(e)}async execute(e,t,n){let s=[...this.middleware];if(n)for(let i of n){let o=this.namedMiddleware.get(i);o&&s.push(o)}let r=t;for(let i=s.length-1;i>=0;i--){let o=s[i],c=r;typeof o.handle=="function"?r=()=>o.handle(e,c):r=()=>o(e,c)}return r()}count(){return this.middleware.length}};var w=class extends l{requests=new Map;maxRequests;windowMs;constructor(e={}){super(),this.maxRequests=e.maxRequests??60,this.windowMs=e.windowMs??6e4}async handle(e,t){let n=e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown",s=Date.now(),r=this.requests.get(n);if(r&&s<r.resetAt){if(r.count>=this.maxRequests)return new Response(JSON.stringify({error:"Too many requests"}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(Math.ceil((r.resetAt-s)/1e3))}});r.count++}else this.requests.set(n,{count:1,resetAt:s+this.windowMs});return t()}};var y=class extends l{cookieName;headerName;fieldName;excludePaths;onlyPaths;constructor(e={}){super(),this.cookieName=e.cookieName??"XSRF-TOKEN",this.headerName=e.headerName??"X-CSRF-Token",this.fieldName=e.fieldName??"_csrf",this.excludePaths=e.excludePaths??[],this.onlyPaths=e.onlyPaths??null}async handle(e,t){let{event:n}=e,s=n.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(s))return this.setTokenAndContinue(e,t);if((n.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let i=n.url.pathname;if(this.onlyPaths&&!this.onlyPaths.some(u=>i.startsWith(u)))return this.setTokenAndContinue(e,t);if(this.excludePaths.some(u=>i.startsWith(u)))return t();let o=this.getCookieToken(n),c=n.request.headers.get(this.headerName)??await this.getBodyToken(n);return!o||!c||!this.timingSafeEqual(o,c)?new Response(JSON.stringify({message:"CSRF token mismatch"}),{status:419,headers:{"Content-Type":"application/json"}}):t()}async setTokenAndContinue(e,t){let n=await t();if(!(n instanceof Response))return n;let r=this.getCookieToken(e.event)||this.generateToken();return n.headers.append("Set-Cookie",`${this.cookieName}=${r}; Path=/; SameSite=Lax`),n}getCookieToken(e){let n=(e.request.headers.get("cookie")??"").match(new RegExp(`${this.cookieName}=([^;]+)`));return n?n[1]:null}async getBodyToken(e){try{let t=e.request.headers.get("content-type")??"";if(t.includes("application/x-www-form-urlencoded")||t.includes("multipart/form-data"))return(await e.request.clone().formData()).get(this.fieldName);if(t.includes("application/json"))return(await e.request.clone().json())[this.fieldName]??null}catch{}return null}generateToken(){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}timingSafeEqual(e,t){if(e.length!==t.length)return!1;let n=new TextEncoder,s=n.encode(e),r=n.encode(t),i=0;for(let o=0;o<s.length;o++)i|=s[o]^r[o];return i===0}},v=class extends l{allowedOrigins;constructor(e={}){super(),this.allowedOrigins=new Set(e.allowedOrigins??[])}async handle(e,t){let{event:n}=e,s=n.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(s)||(n.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let i=n.request.headers.get("origin");if(!i)return t();let o=n.url.origin;return i===o||this.allowedOrigins.has(i)?t():new Response(JSON.stringify({message:"Cross-origin request blocked"}),{status:403,headers:{"Content-Type":"application/json"}})}};var E=class extends l{attempts=new Map;maxAttempts;decayMinutes;constructor(e={}){super(),this.maxAttempts=e.maxAttempts??5,this.decayMinutes=e.decayMinutes??1}async handle(e,t){let s=`${e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown"}:${e.event.url.pathname}`,r=Date.now(),i=this.attempts.get(s);if(i){if(r<i.blockedUntil){let c=Math.ceil((i.blockedUntil-r)/1e3);return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:c}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(c)}})}if(i.count>=this.maxAttempts){i.blockedUntil=r+this.decayMinutes*6e4,i.count=0;let c=this.decayMinutes*60;return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:c}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(c)}})}}let o=await t();if(o instanceof Response&&o.status>=400&&o.status<500){let c=this.attempts.get(s)??{count:0,blockedUntil:0};if(c.count++,this.attempts.set(s,c),this.attempts.size>1e4)for(let[u,N]of this.attempts)r>N.blockedUntil+this.decayMinutes*6e4*2&&this.attempts.delete(u)}return o}};import{randomBytes as re,createHmac as V,timingSafeEqual as ie}from"crypto";import{promises as me}from"fs";import{join as ye}from"path";var x=class a{constructor(e,t){this.id=e;t&&(this.data={...t},this.data._flash&&(this.previousFlashData=this.data._flash,delete this.data._flash))}data={};dirty=!1;flashData={};previousFlashData={};get(e,t){return e in this.flashData?this.flashData[e]:e in this.previousFlashData?this.previousFlashData[e]:e in this.data?this.data[e]:t}set(e,t){this.data[e]=t,this.dirty=!0}has(e){return e in this.data||e in this.flashData||e in this.previousFlashData}forget(e){delete this.data[e],this.dirty=!0}flush(){this.data={},this.dirty=!0}flash(e,t){this.flashData[e]=t,this.dirty=!0}all(){return{...this.data,...this.previousFlashData,...this.flashData}}isDirty(){return this.dirty||Object.keys(this.flashData).length>0}toPersist(){let e={...this.data};return Object.keys(this.flashData).length>0&&(e._flash=this.flashData),e}regenerateId(){let e=this.id,t=a.generateId();this.id=t,this.dirty=!0;let n=p.get(e);return n instanceof g&&n.markOldSessionId(e),p.delete(e),t}static generateId(){return re(32).toString("hex")}},p=new Map,g=class{sessions=new Map;oldSessionIds=new Set;async read(e){if(this.oldSessionIds.has(e))return null;let t=this.sessions.get(e);return t?Date.now()>t.expiresAt?(this.sessions.delete(e),null):t.data:null}async write(e,t,n){this.sessions.set(e,{data:t,expiresAt:Date.now()+n*1e3}),p.set(e,this)}async destroy(e){this.sessions.delete(e),p.delete(e)}async gc(e){let t=Date.now();for(let[n,s]of this.sessions)t>s.expiresAt&&(this.sessions.delete(n),p.delete(n))}markOldSessionId(e){this.oldSessionIds.add(e),this.sessions.delete(e)}};var T=class extends l{config;constructor(e){super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:process.env.APP_KEY??(()=>{throw new Error("APP_KEY is not set. Set it in your .env file before using sessions.")})(),path:"/",domain:"",secure:process.env.NODE_ENV==="production",httpOnly:!0,sameSite:"lax",...e}}async handle(e,t){let n=e.event.request.headers.get("cookie")??"",s=this.getSessionIdFromCookie(n),r=null;if(s){let c=this.verifySignedId(s);c?(r=await this.config.store.read(c),s=c):s=null}s||(s=x.generateId());let i=new x(s,r??{});e.event.locals.session=i,e.locals.session=i;let o=await t();if(i.isDirty()&&await this.config.store.write(i.id,i.toPersist(),this.config.lifetime),o instanceof Response){let c=this.signId(i.id),u=this.buildCookieString(c);o.headers.append("Set-Cookie",u)}return o}getSessionIdFromCookie(e){let t=e.split(";").map(n=>n.trim());for(let n of t){let[s,...r]=n.split("=");if(s===this.config.cookieName)return decodeURIComponent(r.join("="))}return null}signId(e){let t=V("sha256",this.config.secret).update(e).digest("base64url");return`${e}.${t}`}verifySignedId(e){let t=e.lastIndexOf(".");if(t===-1)return null;let n=e.slice(0,t),s=e.slice(t+1),r=V("sha256",this.config.secret).update(n).digest("base64url");if(s.length!==r.length)return null;let i=Buffer.from(s),o=Buffer.from(r);if(i.length!==o.length)return null;try{if(ie(i,o))return n}catch{}return null}buildCookieString(e){let t=[`${this.config.cookieName}=${encodeURIComponent(e)}`];return t.push(`Path=${this.config.path}`),t.push(`Max-Age=${this.config.lifetime}`),this.config.domain&&t.push(`Domain=${this.config.domain}`),this.config.secure&&t.push("Secure"),this.config.httpOnly&&t.push("HttpOnly"),t.push(`SameSite=${this.config.sameSite}`),t.join("; ")}};import{createHmac as Te,randomBytes as be}from"crypto";var b=class extends l{constructor(t){super();this.authManager=t}async handle(t,n){let s=null;if(t.event.locals.session&&(s=await this.authManager.resolveFromSession(t.event.locals.session)),!s){let r=t.event.request.headers.get("authorization");if(r?.startsWith("Bearer ")){let i=r.slice(7);try{s=await this.authManager.resolveFromToken(i)}catch{s=await this.authManager.resolveFromApiToken(i)}}}return t.event.locals.user=s,t.event.locals.auth=this.authManager,n()}};B();import{appendFile as ae,mkdir as oe}from"fs/promises";import{dirname as ce}from"path";var S={debug:0,info:1,warn:2,error:3,fatal:4},f=class{minLevel;format;constructor(e){this.minLevel=e.level??"debug",this.format=e.format??"text"}write(e){if(S[e.level]<S[this.minLevel])return;if(this.format==="json"){console.log(JSON.stringify(e));return}let t={debug:"\x1B[90m",info:"\x1B[34m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},n="\x1B[0m",s=t[e.level]??"",r=e.level.toUpperCase().padEnd(5),i=Object.keys(e.context).length>0?` ${JSON.stringify(e.context)}`:"",o=e.level==="error"||e.level==="fatal"?"error":"log";console[o](`${s}[${e.timestamp}] ${r}${n} ${e.message}${i}`)}},D=class{minLevel;path;format;initialized=!1;constructor(e){this.minLevel=e.level??"info",this.path=e.path??"storage/logs/app.log",this.format=e.format??"text"}async write(e){if(S[e.level]<S[this.minLevel])return;this.initialized||(await oe(ce(this.path),{recursive:!0}),this.initialized=!0);let t;if(this.format==="json")t=JSON.stringify(e)+`
|
|
1
|
+
var se=(a,e)=>()=>(a&&(e=a(a=0)),e);function X(a,e){let t=Symbol.for(a),n=globalThis;return n[t]||(n[t]=e()),n[t]}var B=se(()=>{"use strict"});var l=class{},m=class{middleware=[];namedMiddleware=new Map;use(e){return typeof e=="function"&&"prototype"in e&&typeof e.prototype?.handle=="function"?this.middleware.push(new e):this.middleware.push(e),this}register(e,t){return typeof t=="function"&&"prototype"in t&&typeof t.prototype?.handle=="function"?this.namedMiddleware.set(e,new t):this.namedMiddleware.set(e,t),this}get(e){return this.namedMiddleware.get(e)}async execute(e,t,n){let s=[...this.middleware];if(n)for(let i of n){let o=this.namedMiddleware.get(i);o&&s.push(o)}let r=t;for(let i=s.length-1;i>=0;i--){let o=s[i],c=r;typeof o.handle=="function"?r=()=>o.handle(e,c):r=()=>o(e,c)}return r()}count(){return this.middleware.length}};var w=class extends l{requests=new Map;maxRequests;windowMs;constructor(e={}){super(),this.maxRequests=e.maxRequests??60,this.windowMs=e.windowMs??6e4}async handle(e,t){let n=e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown",s=Date.now(),r=this.requests.get(n);if(r&&s<r.resetAt){if(r.count>=this.maxRequests)return new Response(JSON.stringify({error:"Too many requests"}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(Math.ceil((r.resetAt-s)/1e3))}});r.count++}else this.requests.set(n,{count:1,resetAt:s+this.windowMs});return t()}};var y=class extends l{cookieName;headerName;fieldName;excludePaths;onlyPaths;constructor(e={}){super(),this.cookieName=e.cookieName??"XSRF-TOKEN",this.headerName=e.headerName??"X-CSRF-Token",this.fieldName=e.fieldName??"_csrf",this.excludePaths=e.excludePaths??[],this.onlyPaths=e.onlyPaths??null}async handle(e,t){let{event:n}=e,s=n.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(s))return this.setTokenAndContinue(e,t);if((n.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let i=n.url.pathname;if(this.onlyPaths&&!this.onlyPaths.some(u=>i.startsWith(u)))return this.setTokenAndContinue(e,t);if(this.excludePaths.some(u=>i.startsWith(u)))return t();let o=this.getCookieToken(n),c=n.request.headers.get(this.headerName)??await this.getBodyToken(n);return!o||!c||!this.timingSafeEqual(o,c)?new Response(JSON.stringify({message:"CSRF token mismatch"}),{status:419,headers:{"Content-Type":"application/json"}}):t()}async setTokenAndContinue(e,t){let n=await t();if(!(n instanceof Response))return n;let r=this.getCookieToken(e.event)||this.generateToken();return n.headers.append("Set-Cookie",`${this.cookieName}=${r}; Path=/; SameSite=Lax`),n}getCookieToken(e){let n=(e.request.headers.get("cookie")??"").match(new RegExp(`${this.cookieName}=([^;]+)`));return n?n[1]:null}async getBodyToken(e){try{let t=e.request.headers.get("content-type")??"";if(t.includes("application/x-www-form-urlencoded")||t.includes("multipart/form-data"))return(await e.request.clone().formData()).get(this.fieldName);if(t.includes("application/json"))return(await e.request.clone().json())[this.fieldName]??null}catch{}return null}generateToken(){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}timingSafeEqual(e,t){if(e.length!==t.length)return!1;let n=new TextEncoder,s=n.encode(e),r=n.encode(t),i=0;for(let o=0;o<s.length;o++)i|=s[o]^r[o];return i===0}},v=class extends l{allowedOrigins;constructor(e={}){super(),this.allowedOrigins=new Set(e.allowedOrigins??[])}async handle(e,t){let{event:n}=e,s=n.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(s)||(n.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let i=n.request.headers.get("origin");if(!i)return t();let o=n.url.origin;return i===o||this.allowedOrigins.has(i)?t():new Response(JSON.stringify({message:"Cross-origin request blocked"}),{status:403,headers:{"Content-Type":"application/json"}})}};var E=class extends l{attempts=new Map;maxAttempts;decayMinutes;constructor(e={}){super(),this.maxAttempts=e.maxAttempts??5,this.decayMinutes=e.decayMinutes??1}async handle(e,t){let s=`${e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown"}:${e.event.url.pathname}`,r=Date.now(),i=this.attempts.get(s);if(i){if(r<i.blockedUntil){let c=Math.ceil((i.blockedUntil-r)/1e3);return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:c}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(c)}})}if(i.count>=this.maxAttempts){i.blockedUntil=r+this.decayMinutes*6e4,i.count=0;let c=this.decayMinutes*60;return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:c}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(c)}})}}let o=await t();if(o instanceof Response&&o.status>=400&&o.status<500){let c=this.attempts.get(s)??{count:0,blockedUntil:0};if(c.count++,this.attempts.set(s,c),this.attempts.size>1e4)for(let[u,A]of this.attempts)r>A.blockedUntil+this.decayMinutes*6e4*2&&this.attempts.delete(u)}return o}};import{randomBytes as re,createHmac as V,timingSafeEqual as ie}from"crypto";import{promises as me}from"fs";import{join as ye}from"path";var x=class a{constructor(e,t){this.id=e;t&&(this.data={...t},this.data._flash&&(this.previousFlashData=this.data._flash,delete this.data._flash))}data={};dirty=!1;flashData={};previousFlashData={};get(e,t){return e in this.flashData?this.flashData[e]:e in this.previousFlashData?this.previousFlashData[e]:e in this.data?this.data[e]:t}set(e,t){this.data[e]=t,this.dirty=!0}has(e){return e in this.data||e in this.flashData||e in this.previousFlashData}forget(e){delete this.data[e],this.dirty=!0}flush(){this.data={},this.dirty=!0}flash(e,t){this.flashData[e]=t,this.dirty=!0}all(){return{...this.data,...this.previousFlashData,...this.flashData}}isDirty(){return this.dirty||Object.keys(this.flashData).length>0}toPersist(){let e={...this.data};return Object.keys(this.flashData).length>0&&(e._flash=this.flashData),e}regenerateId(){let e=this.id,t=a.generateId();this.id=t,this.dirty=!0;let n=p.get(e);return n instanceof g&&n.markOldSessionId(e),p.delete(e),t}static generateId(){return re(32).toString("hex")}},p=new Map,g=class{sessions=new Map;oldSessionIds=new Set;async read(e){if(this.oldSessionIds.has(e))return null;let t=this.sessions.get(e);return t?Date.now()>t.expiresAt?(this.sessions.delete(e),null):t.data:null}async write(e,t,n){this.sessions.set(e,{data:t,expiresAt:Date.now()+n*1e3}),p.set(e,this)}async destroy(e){this.sessions.delete(e),p.delete(e)}async gc(e){let t=Date.now();for(let[n,s]of this.sessions)t>s.expiresAt&&(this.sessions.delete(n),p.delete(n))}markOldSessionId(e){this.oldSessionIds.add(e),this.sessions.delete(e)}};var T=class extends l{config;constructor(e){if(super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:"",path:"/",domain:"",secure:process.env.NODE_ENV==="production",httpOnly:!0,sameSite:"lax",...e},!this.config.secret)throw new Error("APP_KEY is not set. Pass `secret` to createSvelarApp() \u2014 e.g. secret: env.APP_KEY (from $env/dynamic/private).")}async handle(e,t){let n=e.event.request.headers.get("cookie")??"",s=this.getSessionIdFromCookie(n),r=null;if(s){let c=this.verifySignedId(s);c?(r=await this.config.store.read(c),s=c):s=null}s||(s=x.generateId());let i=new x(s,r??{});e.event.locals.session=i,e.locals.session=i;let o=await t();if(i.isDirty()&&await this.config.store.write(i.id,i.toPersist(),this.config.lifetime),o instanceof Response){let c=this.signId(i.id),u=this.buildCookieString(c);o.headers.append("Set-Cookie",u)}return o}getSessionIdFromCookie(e){let t=e.split(";").map(n=>n.trim());for(let n of t){let[s,...r]=n.split("=");if(s===this.config.cookieName)return decodeURIComponent(r.join("="))}return null}signId(e){let t=V("sha256",this.config.secret).update(e).digest("base64url");return`${e}.${t}`}verifySignedId(e){let t=e.lastIndexOf(".");if(t===-1)return null;let n=e.slice(0,t),s=e.slice(t+1),r=V("sha256",this.config.secret).update(n).digest("base64url");if(s.length!==r.length)return null;let i=Buffer.from(s),o=Buffer.from(r);if(i.length!==o.length)return null;try{if(ie(i,o))return n}catch{}return null}buildCookieString(e){let t=[`${this.config.cookieName}=${encodeURIComponent(e)}`];return t.push(`Path=${this.config.path}`),t.push(`Max-Age=${this.config.lifetime}`),this.config.domain&&t.push(`Domain=${this.config.domain}`),this.config.secure&&t.push("Secure"),this.config.httpOnly&&t.push("HttpOnly"),t.push(`SameSite=${this.config.sameSite}`),t.join("; ")}};import{createHmac as Te,randomBytes as be}from"crypto";var b=class extends l{constructor(t){super();this.authManager=t}async handle(t,n){let s=null;if(t.event.locals.session&&(s=await this.authManager.resolveFromSession(t.event.locals.session)),!s){let r=t.event.request.headers.get("authorization");if(r?.startsWith("Bearer ")){let i=r.slice(7);try{s=await this.authManager.resolveFromToken(i)}catch{s=await this.authManager.resolveFromApiToken(i)}}}return t.event.locals.user=s,t.event.locals.auth=this.authManager,n()}};B();import{appendFile as ae,mkdir as oe}from"fs/promises";import{dirname as ce}from"path";var S={debug:0,info:1,warn:2,error:3,fatal:4},f=class{minLevel;format;constructor(e){this.minLevel=e.level??"debug",this.format=e.format??"text"}write(e){if(S[e.level]<S[this.minLevel])return;if(this.format==="json"){console.log(JSON.stringify(e));return}let t={debug:"\x1B[90m",info:"\x1B[34m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},n="\x1B[0m",s=t[e.level]??"",r=e.level.toUpperCase().padEnd(5),i=Object.keys(e.context).length>0?` ${JSON.stringify(e.context)}`:"",o=e.level==="error"||e.level==="fatal"?"error":"log";console[o](`${s}[${e.timestamp}] ${r}${n} ${e.message}${i}`)}},D=class{minLevel;path;format;initialized=!1;constructor(e){this.minLevel=e.level??"info",this.path=e.path??"storage/logs/app.log",this.format=e.format??"text"}async write(e){if(S[e.level]<S[this.minLevel])return;this.initialized||(await oe(ce(this.path),{recursive:!0}),this.initialized=!0);let t;if(this.format==="json")t=JSON.stringify(e)+`
|
|
2
2
|
`;else{let n=e.level.toUpperCase().padEnd(5),s=Object.keys(e.context).length>0?` ${JSON.stringify(e.context)}`:"";t=`[${e.timestamp}] ${n} ${e.message}${s}
|
|
3
3
|
`}await ae(this.path,t)}},M=class{minLevel="debug";channelNames;resolver;constructor(e,t){this.channelNames=e.channels??[],this.resolver=t,this.minLevel=e.level??"debug"}async write(e){for(let t of this.channelNames){let n=this.resolver(t);n&&await n.write(e)}}},_=class{minLevel="debug";write(){}},I=class{config={default:"console",channels:{console:{driver:"console",level:"debug"}}};channels=new Map;configure(e){this.config=e,this.channels.clear()}channel(e){return new U(this.resolveChannel(e))}debug(e,t={}){this.writeToDefault({level:"debug",message:e,context:t,timestamp:d()})}info(e,t={}){this.writeToDefault({level:"info",message:e,context:t,timestamp:d()})}warn(e,t={}){this.writeToDefault({level:"warn",message:e,context:t,timestamp:d()})}error(e,t={}){this.writeToDefault({level:"error",message:e,context:t,timestamp:d()})}fatal(e,t={}){this.writeToDefault({level:"fatal",message:e,context:t,timestamp:d()})}writeToDefault(e){this.resolveChannel(this.config.default).write(e)}resolveChannel(e){if(this.channels.has(e))return this.channels.get(e);let t=this.config.channels[e];if(!t){let s=new f({driver:"console"});return this.channels.set(e,s),s}let n=this.createChannel(t);return this.channels.set(e,n),n}createChannel(e){switch(e.driver){case"console":return new f(e);case"file":return new D(e);case"stack":return new M(e,t=>this.resolveChannel(t));case"null":return new _;default:return new f(e)}}},U=class{constructor(e){this.channel=e}debug(e,t={}){this.channel.write({level:"debug",message:e,context:t,timestamp:d()})}info(e,t={}){this.channel.write({level:"info",message:e,context:t,timestamp:d()})}warn(e,t={}){this.channel.write({level:"warn",message:e,context:t,timestamp:d()})}error(e,t={}){this.channel.write({level:"error",message:e,context:t,timestamp:d()})}fatal(e,t={}){this.channel.write({level:"fatal",message:e,context:t,timestamp:d()})}};function d(){return new Date().toISOString()}var Y=X("svelar.log",()=>new I);var h=class extends Error{constructor(t,n,s){super(n);this.statusCode=t;this.details=s;this.name="HttpError"}},$=class extends h{constructor(e="The requested resource was not found"){super(404,e),this.name="NotFoundError"}},H=class extends h{constructor(e="Unauthenticated"){super(401,e),this.name="UnauthorizedError"}},F=class extends h{constructor(e="You do not have permission to perform this action"){super(403,e),this.name="ForbiddenError"}},R=class extends h{constructor(t,n="The given data was invalid"){super(422,n,{errors:t});this.errors=t;this.name="ValidationError"}};var C=class{config;constructor(e={}){this.config={debug:process.env.NODE_ENV!=="production",dontReport:[R,$,H,F],...e}}async handle(e,t){let n=e instanceof Error?e:new Error(String(e));return await this.reportError(n,t),this.config.render?this.config.render(n,t):this.renderError(n)}handleSvelteKitError(){return({error:e,event:t,status:n,message:s})=>{let r=e instanceof Error?e:new Error(String(e));return this.reportError(r,t),r instanceof h?{message:r.message,status:r.statusCode,...r.details??{},...this.config.debug?{stack:r.stack}:{}}:{message:this.config.debug?r.message:"An unexpected error occurred",status:n,...this.config.debug?{stack:r.stack}:{}}}}middleware(){let e=this;return async(t,n)=>{try{return await n()}catch(s){return e.handle(s,t.event)}}}async reportError(e,t){if(this.config.dontReport){for(let s of this.config.dontReport)if(e instanceof s)return}let n={error:e.name,...t?.url?{url:t.url.toString()}:{},...e.stack?{stack:e.stack}:{}};if(Y.error(e.message,n),this.config.report)try{await this.config.report(e,t?{url:t.url?.toString()}:void 0)}catch{}}renderError(e){if(e instanceof h){let n={message:e.message};return e instanceof R&&(n.errors=e.errors),e.details&&Object.assign(n,e.details),this.config.debug&&(n.exception=e.name,n.stack=e.stack?.split(`
|
|
4
4
|
`).map(s=>s.trim())),new Response(JSON.stringify(n),{status:e.statusCode,headers:{"Content-Type":"application/json"}})}let t={message:this.config.debug?e.message:"Internal server error"};return this.config.debug&&(t.exception=e.name,t.stack=e.stack?.split(`
|
|
5
|
-
`).map(n=>n.trim())),new Response(JSON.stringify(t),{status:500,headers:{"Content-Type":"application/json"}})}};function $e(a={}){let{auth:e,secret:t=
|
|
5
|
+
`).map(n=>n.trim())),new Response(JSON.stringify(t),{status:500,headers:{"Content-Type":"application/json"}})}};function $e(a={}){let{auth:e,secret:t=(()=>{throw new Error("APP_KEY is not set. Pass `secret` to createSvelarApp() \u2014 e.g. secret: env.APP_KEY (from $env/dynamic/private).")})(),sessionStore:n,sessionLifetime:s=86400,rateLimit:r=100,rateLimitWindow:i=6e4,csrfPaths:o=["/api/"],csrfExcludePaths:c=["/api/webhooks"],authThrottleAttempts:u=5,authThrottleDecay:A=1,debug:K=process.env.NODE_ENV!=="production",middleware:z=[],namedMiddleware:G={},i18n:q,errorConfig:Z={}}=a,N=[new v,new w({maxRequests:r,windowMs:i}),new y({onlyPaths:o,excludePaths:c}),new T({store:n??new g,secret:t,lifetime:s})];e&&N.push(new b(e)),N.push(...z);let Q={"auth-throttle":new E({maxAttempts:u,decayMinutes:A}),...G},j=new C({debug:K,...Z}),J=le({middleware:N,namedMiddleware:Q,onError:(k,O)=>j.handle(k,O)}),P;if(q){let{paraglideMiddleware:k,getTextDirection:O=()=>"ltr"}=q;P=de(async({event:L,resolve:ee})=>k(L.request,({request:te,locale:W})=>(L.request=te,ee(L,{transformPageChunk:({html:ne})=>ne.replace("%lang%",W).replace("%dir%",O(W))}))),J)}else P=J;return{handle:P,handleError:j.handleSvelteKitError()}}function le(a={}){let e=new m;if(a.middleware)for(let t of a.middleware)e.use(t);if(a.namedMiddleware)for(let[t,n]of Object.entries(a.namedMiddleware))e.register(t,n);return async function({event:n,resolve:s}){let r={event:n,params:n.params??{},locals:n.locals??{}};try{a.app&&!a.app.isBooted()&&await a.app.bootstrap();let i=await e.execute(r,async()=>s(n));return i instanceof Response?i:s(n)}catch(i){if(a.onError){let o=await a.onError(i,n);if(o instanceof Response)return o}return console.error("[Svelar] Unhandled error in hooks:",i),new Response(JSON.stringify({message:process.env.NODE_ENV==="production"?"Internal server error":i.message}),{status:500,headers:{"Content-Type":"application/json"}})}}}function de(...a){return async function({event:t,resolve:n}){let s=n;for(let r=a.length-1;r>=0;r--){let i=a[r],o=s;s=c=>i({event:c,resolve:o})}return s(t)}}export{$e as createSvelarApp,le as createSvelarHooks,de as sequence};
|
package/dist/index.js
CHANGED
|
@@ -72,7 +72,7 @@ Access until: {{accessUntilDate}}`,variables:["user.name","plan.name","accessUnt
|
|
|
72
72
|
) ENGINE=InnoDB`,[],this.connectionName);break}this.tableEnsured=!0}catch{}}async read(e){await this.ensureTable();let{Connection:t}=await Promise.resolve().then(()=>(m(),y)),s=await t.raw(`SELECT payload, expires_at FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName);if(s.length===0)return null;let r=s[0];if(new Date(r.expires_at)<new Date)return await this.destroy(e),null;try{return JSON.parse(r.payload)}catch{return null}}async write(e,t,s){await this.ensureTable();let{Connection:r}=await Promise.resolve().then(()=>(m(),y)),n=JSON.stringify(t),i=new Date(Date.now()+s*1e3).toISOString(),a=r.getDriver(this.connectionName);a==="sqlite"?await r.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
73
73
|
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, expires_at = excluded.expires_at`,[e,n,i],this.connectionName):a==="postgres"?await r.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES ($1, $2, $3)
|
|
74
74
|
ON CONFLICT(id) DO UPDATE SET payload = $2, expires_at = $3`,[e,n,i],this.connectionName):await r.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
75
|
-
ON DUPLICATE KEY UPDATE payload = VALUES(payload), expires_at = VALUES(expires_at)`,[e,n,i],this.connectionName)}async destroy(e){let{Connection:t}=await Promise.resolve().then(()=>(m(),y));await t.raw(`DELETE FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName)}async gc(e){let{Connection:t}=await Promise.resolve().then(()=>(m(),y));await t.raw(`DELETE FROM ${this.tableName} WHERE expires_at < ?`,[new Date().toISOString()],this.connectionName)}},We=class{dir;constructor(e){this.dir=e??ee(process.cwd(),"storage","sessions")}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return ee(this.dir,`${t}.json`)}async ensureDir(){await N.mkdir(this.dir,{recursive:!0})}async read(e){try{let t=await N.readFile(this.filePath(e),"utf-8"),s=JSON.parse(t);return new Date(s.expiresAt)<new Date?(await this.destroy(e),null):s.data}catch{return null}}async write(e,t,s){await this.ensureDir();let r={data:t,expiresAt:new Date(Date.now()+s*1e3).toISOString()};await N.writeFile(this.filePath(e),JSON.stringify(r),"utf-8")}async destroy(e){try{await N.unlink(this.filePath(e))}catch{}}async gc(e){try{let t=await N.readdir(this.dir),s=new Date;for(let r of t)if(r.endsWith(".json"))try{let n=await N.readFile(ee(this.dir,r),"utf-8"),i=JSON.parse(n);new Date(i.expiresAt)<s&&await N.unlink(ee(this.dir,r))}catch{await N.unlink(ee(this.dir,r)).catch(()=>{})}}catch{}}},Ve=class{redis;prefix;constructor(e){this.prefix=e?.prefix??"svelar_session:",e?.client?this.redis=e.client:this._url=e?.url}_url;_connecting;async getClient(){return this.redis?this.redis:(this._connecting||(this._connecting=(async()=>{try{let{default:e}=await import("ioredis");return this.redis=this._url?new e(this._url):new e,this.redis}catch{throw new Error('RedisSessionStore requires "ioredis" package. Install it: npm install ioredis')}})()),this._connecting)}async read(e){let s=await(await this.getClient()).get(this.prefix+e);if(!s)return null;try{return JSON.parse(s)}catch{return null}}async write(e,t,s){await(await this.getClient()).set(this.prefix+e,JSON.stringify(t),"EX",s)}async destroy(e){await(await this.getClient()).del(this.prefix+e)}async gc(e){}},re=class extends v{config;constructor(e){super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:
|
|
75
|
+
ON DUPLICATE KEY UPDATE payload = VALUES(payload), expires_at = VALUES(expires_at)`,[e,n,i],this.connectionName)}async destroy(e){let{Connection:t}=await Promise.resolve().then(()=>(m(),y));await t.raw(`DELETE FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName)}async gc(e){let{Connection:t}=await Promise.resolve().then(()=>(m(),y));await t.raw(`DELETE FROM ${this.tableName} WHERE expires_at < ?`,[new Date().toISOString()],this.connectionName)}},We=class{dir;constructor(e){this.dir=e??ee(process.cwd(),"storage","sessions")}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return ee(this.dir,`${t}.json`)}async ensureDir(){await N.mkdir(this.dir,{recursive:!0})}async read(e){try{let t=await N.readFile(this.filePath(e),"utf-8"),s=JSON.parse(t);return new Date(s.expiresAt)<new Date?(await this.destroy(e),null):s.data}catch{return null}}async write(e,t,s){await this.ensureDir();let r={data:t,expiresAt:new Date(Date.now()+s*1e3).toISOString()};await N.writeFile(this.filePath(e),JSON.stringify(r),"utf-8")}async destroy(e){try{await N.unlink(this.filePath(e))}catch{}}async gc(e){try{let t=await N.readdir(this.dir),s=new Date;for(let r of t)if(r.endsWith(".json"))try{let n=await N.readFile(ee(this.dir,r),"utf-8"),i=JSON.parse(n);new Date(i.expiresAt)<s&&await N.unlink(ee(this.dir,r))}catch{await N.unlink(ee(this.dir,r)).catch(()=>{})}}catch{}}},Ve=class{redis;prefix;constructor(e){this.prefix=e?.prefix??"svelar_session:",e?.client?this.redis=e.client:this._url=e?.url}_url;_connecting;async getClient(){return this.redis?this.redis:(this._connecting||(this._connecting=(async()=>{try{let{default:e}=await import("ioredis");return this.redis=this._url?new e(this._url):new e,this.redis}catch{throw new Error('RedisSessionStore requires "ioredis" package. Install it: npm install ioredis')}})()),this._connecting)}async read(e){let s=await(await this.getClient()).get(this.prefix+e);if(!s)return null;try{return JSON.parse(s)}catch{return null}}async write(e,t,s){await(await this.getClient()).set(this.prefix+e,JSON.stringify(t),"EX",s)}async destroy(e){await(await this.getClient()).del(this.prefix+e)}async gc(e){}},re=class extends v{config;constructor(e){if(super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:"",path:"/",domain:"",secure:process.env.NODE_ENV==="production",httpOnly:!0,sameSite:"lax",...e},!this.config.secret)throw new Error("APP_KEY is not set. Pass `secret` to createSvelarApp() \u2014 e.g. secret: env.APP_KEY (from $env/dynamic/private).")}async handle(e,t){let s=e.event.request.headers.get("cookie")??"",r=this.getSessionIdFromCookie(s),n=null;if(r){let c=this.verifySignedId(r);c?(n=await this.config.store.read(c),r=c):r=null}r||(r=se.generateId());let i=new se(r,n??{});e.event.locals.session=i,e.locals.session=i;let a=await t();if(i.isDirty()&&await this.config.store.write(i.id,i.toPersist(),this.config.lifetime),a instanceof Response){let c=this.signId(i.id),l=this.buildCookieString(c);a.headers.append("Set-Cookie",l)}return a}getSessionIdFromCookie(e){let t=e.split(";").map(s=>s.trim());for(let s of t){let[r,...n]=s.split("=");if(r===this.config.cookieName)return decodeURIComponent(n.join("="))}return null}signId(e){let t=Ft("sha256",this.config.secret).update(e).digest("base64url");return`${e}.${t}`}verifySignedId(e){let t=e.lastIndexOf(".");if(t===-1)return null;let s=e.slice(0,t),r=e.slice(t+1),n=Ft("sha256",this.config.secret).update(s).digest("base64url");if(r.length!==n.length)return null;let i=Buffer.from(r),a=Buffer.from(n);if(i.length!==a.length)return null;try{if(vs(i,a))return s}catch{}return null}buildCookieString(e){let t=[`${this.config.cookieName}=${encodeURIComponent(e)}`];return t.push(`Path=${this.config.path}`),t.push(`Max-Age=${this.config.lifetime}`),this.config.domain&&t.push(`Domain=${this.config.domain}`),this.config.secure&&t.push("Secure"),this.config.httpOnly&&t.push("HttpOnly"),t.push(`SameSite=${this.config.sameSite}`),t.join("; ")}};import{createHmac as U,randomBytes as ie}from"crypto";function Xt(o){return(typeof o=="string"?Buffer.from(o):o).toString("base64url")}function Gt(o){return Buffer.from(o,"base64url").toString("utf-8")}function Yt(o,e,t,s){return U(s==="HS384"?"sha384":s==="HS512"?"sha512":"sha256",t).update(`${o}.${e}`).digest("base64url")}function Zt(o,e,t="HS256"){let s=Xt(JSON.stringify({alg:t,typ:"JWT"})),r=Xt(JSON.stringify(o)),n=Yt(s,r,e,t);return`${s}.${r}.${n}`}function es(o,e){let t=o.split(".");if(t.length!==3)return null;let[s,r,n]=t,i;try{i=JSON.parse(Gt(s))}catch{return null}let a=Yt(s,r,e,i.alg);if(n!==a)return null;try{let c=JSON.parse(Gt(r));return c.exp&&Date.now()/1e3>c.exp?null:c}catch{return null}}var it=class{config;currentUser=null;constructor(e){this.config={identifierColumn:"email",passwordColumn:"password",...e}}async attempt(e,t){let{Hash:s}=await Promise.resolve().then(()=>(_(),ne)),r=e[this.config.identifierColumn],n=e[this.config.passwordColumn];if(!r||!n)return null;let i=await this.config.model.where(this.config.identifierColumn,r).first();if(!i)return null;let a=i.getAttribute(this.config.passwordColumn);return await s.verify(n,a)?(this.currentUser=i,t&&(t.set("auth_user_id",i.getAttribute("id")),t.regenerateId()),i):null}async attemptJwt(e){let{Hash:t}=await Promise.resolve().then(()=>(_(),ne));if(!this.config.jwt)throw new Error("JWT configuration required for JWT guard.");let s=e[this.config.identifierColumn],r=e[this.config.passwordColumn];if(!s||!r)return null;let n=await this.config.model.where(this.config.identifierColumn,s).first();if(!n)return null;let i=n.getAttribute(this.config.passwordColumn);return await t.verify(r,i)?(this.currentUser=n,this.issueTokenPair(n)):null}async issueTokenPair(e){let t=this.config.jwt,s=t.expiresIn??3600,r=Math.floor(Date.now()/1e3),n={sub:e.getAttribute("id"),iat:r,exp:r+s,...t.issuer?{iss:t.issuer}:{}},i=Zt(n,t.secret,t.algorithm),a=new Date((r+s)*1e3),c={user:e,token:i,expiresAt:a};if(t.refreshTokens){let l=t.refreshExpiresIn??604800,u=ie(32).toString("base64url"),h=U("sha256",t.secret).update(u).digest("hex"),g=new Date((r+l)*1e3),{Connection:b}=await Promise.resolve().then(()=>(m(),y)),f=t.refreshTable??"refresh_tokens";await b.raw(`INSERT INTO ${f} (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),h,g.toISOString(),new Date().toISOString()]),c.refreshToken=u,c.refreshExpiresAt=g}return c}async refreshJwt(e){if(!this.config.jwt)throw new Error("JWT configuration required.");if(!this.config.jwt.refreshTokens)throw new Error("Refresh tokens are not enabled. Set jwt.refreshTokens = true.");let t=this.config.jwt,s=U("sha256",t.secret).update(e).digest("hex"),{Connection:r}=await Promise.resolve().then(()=>(m(),y)),n=t.refreshTable??"refresh_tokens",i=await r.raw(`SELECT user_id, expires_at, revoked_at FROM ${n} WHERE token = ?`,[s]);if(i.length===0)return null;let a=i[0];if(a.revoked_at||new Date(a.expires_at)<new Date)return null;await r.raw(`UPDATE ${n} SET revoked_at = ? WHERE token = ?`,[new Date().toISOString(),s]);let c=await this.config.model.find(a.user_id);return c?(this.currentUser=c,this.issueTokenPair(c)):null}async revokeRefreshTokens(e){if(!this.config.jwt?.refreshTokens)return;let{Connection:t}=await Promise.resolve().then(()=>(m(),y)),s=this.config.jwt.refreshTable??"refresh_tokens";await t.raw(`UPDATE ${s} SET revoked_at = ? WHERE user_id = ? AND revoked_at IS NULL`,[new Date().toISOString(),e])}async resolveFromToken(e){if(!this.config.jwt)throw new Error("JWT configuration required.");let t=es(e,this.config.jwt.secret);if(!t)return null;let s=await this.config.model.find(t.sub);return s&&(this.currentUser=s),s}async resolveFromSession(e){let t=e.get("auth_user_id");if(!t)return null;let s=await this.config.model.find(t);return s&&(this.currentUser=s),s}async register(e){let{Hash:t}=await Promise.resolve().then(()=>(_(),ne));e[this.config.passwordColumn]&&(e[this.config.passwordColumn]=await t.make(e[this.config.passwordColumn]));let s=await this.config.model.create(e);return this.currentUser=s,s}async logout(e){this.currentUser=null,e&&(e.forget("auth_user_id"),e.regenerateId())}user(){return this.currentUser}check(){return this.currentUser!==null}id(){return this.currentUser?.getAttribute("id")??null}async generateApiToken(e,t="default"){let s=ie(32).toString("hex"),r=this.config.jwt?.secret??process.env.APP_KEY;if(!r)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let n=U("sha256",r).update(s).digest("hex"),{Connection:i}=await Promise.resolve().then(()=>(m(),y)),a=this.config.token?.table??"personal_access_tokens";return await i.raw(`INSERT INTO ${a} (user_id, name, token, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),t,n,new Date().toISOString()]),s}async resolveFromApiToken(e){let{Connection:t}=await Promise.resolve().then(()=>(m(),y)),s=this.config.token?.table??"personal_access_tokens",r=this.config.jwt?.secret??process.env.APP_KEY;if(!r)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let n=U("sha256",r).update(e).digest("hex"),i=await t.raw(`SELECT user_id FROM ${s} WHERE token = ?`,[n]);if(i.length===0)return null;let a=await this.config.model.find(i[0].user_id);return a&&(this.currentUser=a),a}async sendPasswordReset(e){let t=await this.config.model.where(this.config.identifierColumn,e).first();if(!t)return!1;let{Connection:s}=await Promise.resolve().then(()=>(m(),y)),r=this.config.passwordResets?.table??"password_resets",n=this.config.passwordResets?.expiresIn??3600;await this.ensureTable(r,`
|
|
76
76
|
CREATE TABLE IF NOT EXISTS ${r} (
|
|
77
77
|
email TEXT NOT NULL,
|
|
78
78
|
token TEXT NOT NULL,
|
|
@@ -99,7 +99,7 @@ Access until: {{accessUntilDate}}`,variables:["user.name","plan.name","accessUnt
|
|
|
99
99
|
`;else{let s=e.level.toUpperCase().padEnd(5),r=Object.keys(e.context).length>0?` ${JSON.stringify(e.context)}`:"";t=`[${e.timestamp}] ${s} ${e.message}${r}
|
|
100
100
|
`}await Ps(this.path,t)}},ct=class{minLevel="debug";channelNames;resolver;constructor(e,t){this.channelNames=e.channels??[],this.resolver=t,this.minLevel=e.level??"debug"}async write(e){for(let t of this.channelNames){let s=this.resolver(t);s&&await s.write(e)}}},lt=class{minLevel="debug";write(){}},ut=class{config={default:"console",channels:{console:{driver:"console",level:"debug"}}};channels=new Map;configure(e){this.config=e,this.channels.clear()}channel(e){return new dt(this.resolveChannel(e))}debug(e,t={}){this.writeToDefault({level:"debug",message:e,context:t,timestamp:P()})}info(e,t={}){this.writeToDefault({level:"info",message:e,context:t,timestamp:P()})}warn(e,t={}){this.writeToDefault({level:"warn",message:e,context:t,timestamp:P()})}error(e,t={}){this.writeToDefault({level:"error",message:e,context:t,timestamp:P()})}fatal(e,t={}){this.writeToDefault({level:"fatal",message:e,context:t,timestamp:P()})}writeToDefault(e){this.resolveChannel(this.config.default).write(e)}resolveChannel(e){if(this.channels.has(e))return this.channels.get(e);let t=this.config.channels[e];if(!t){let r=new oe({driver:"console"});return this.channels.set(e,r),r}let s=this.createChannel(t);return this.channels.set(e,s),s}createChannel(e){switch(e.driver){case"console":return new oe(e);case"file":return new ot(e);case"stack":return new ct(e,t=>this.resolveChannel(t));case"null":return new lt;default:return new oe(e)}}},dt=class{constructor(e){this.channel=e}debug(e,t={}){this.channel.write({level:"debug",message:e,context:t,timestamp:P()})}info(e,t={}){this.channel.write({level:"info",message:e,context:t,timestamp:P()})}warn(e,t={}){this.channel.write({level:"warn",message:e,context:t,timestamp:P()})}error(e,t={}){this.channel.write({level:"error",message:e,context:t,timestamp:P()})}fatal(e,t={}){this.channel.write({level:"fatal",message:e,context:t,timestamp:P()})}};function P(){return new Date().toISOString()}var ht=p("svelar.log",()=>new ut);var S=class extends Error{constructor(t,s,r){super(s);this.statusCode=t;this.details=r;this.name="HttpError"}},Te=class extends S{constructor(e="The requested resource was not found"){super(404,e),this.name="NotFoundError"}},gt=class extends S{constructor(e="Unauthenticated"){super(401,e),this.name="UnauthorizedError"}},pt=class extends S{constructor(e="You do not have permission to perform this action"){super(403,e),this.name="ForbiddenError"}},Ce=class extends S{constructor(t,s="The given data was invalid"){super(422,s,{errors:t});this.errors=t;this.name="ValidationError"}};var mt=class extends Te{constructor(e,t){super(t?`${e} with ID ${t} not found`:`${e} not found`),this.name="ModelNotFoundError"}};function ft(o,e){throw new S(o,e??Ms(o))}function Rs(o,e,t){o&&ft(e,t)}function Ns(o,e,t){o||ft(e,t)}var ce=class{config;constructor(e={}){this.config={debug:process.env.NODE_ENV!=="production",dontReport:[Ce,Te,gt,pt],...e}}async handle(e,t){let s=e instanceof Error?e:new Error(String(e));return await this.reportError(s,t),this.config.render?this.config.render(s,t):this.renderError(s)}handleSvelteKitError(){return({error:e,event:t,status:s,message:r})=>{let n=e instanceof Error?e:new Error(String(e));return this.reportError(n,t),n instanceof S?{message:n.message,status:n.statusCode,...n.details??{},...this.config.debug?{stack:n.stack}:{}}:{message:this.config.debug?n.message:"An unexpected error occurred",status:s,...this.config.debug?{stack:n.stack}:{}}}}middleware(){let e=this;return async(t,s)=>{try{return await s()}catch(r){return e.handle(r,t.event)}}}async reportError(e,t){if(this.config.dontReport){for(let r of this.config.dontReport)if(e instanceof r)return}let s={error:e.name,...t?.url?{url:t.url.toString()}:{},...e.stack?{stack:e.stack}:{}};if(ht.error(e.message,s),this.config.report)try{await this.config.report(e,t?{url:t.url?.toString()}:void 0)}catch{}}renderError(e){if(e instanceof S){let s={message:e.message};return e instanceof Ce&&(s.errors=e.errors),e.details&&Object.assign(s,e.details),this.config.debug&&(s.exception=e.name,s.stack=e.stack?.split(`
|
|
101
101
|
`).map(r=>r.trim())),new Response(JSON.stringify(s),{status:e.statusCode,headers:{"Content-Type":"application/json"}})}let t={message:this.config.debug?e.message:"Internal server error"};return this.config.debug&&(t.exception=e.name,t.stack=e.stack?.split(`
|
|
102
|
-
`).map(s=>s.trim())),new Response(JSON.stringify(t),{status:500,headers:{"Content-Type":"application/json"}})}};function Ms(o){return{400:"Bad request",401:"Unauthenticated",403:"Forbidden",404:"Not found",405:"Method not allowed",409:"Conflict",419:"Page expired",422:"Unprocessable entity",429:"Too many requests",500:"Internal server error",502:"Bad gateway",503:"Service unavailable",504:"Gateway timeout"}[o]??"An error occurred"}function Ds(o={}){let{auth:e,secret:t=process.env.APP_KEY||(()=>{throw new Error("APP_KEY is not set. Set it in your .env file.")})(),sessionStore:s,sessionLifetime:r=86400,rateLimit:n=100,rateLimitWindow:i=6e4,csrfPaths:a=["/api/"],csrfExcludePaths:c=["/api/webhooks"],authThrottleAttempts:l=5,authThrottleDecay:u=1,debug:h=process.env.NODE_ENV!=="production",middleware:g=[],namedMiddleware:b={},i18n:f,errorConfig:C={}}=o,x=[new Y,new X({maxRequests:n,windowMs:i}),new G({onlyPaths:a,excludePaths:c}),new re({store:s??new q,secret:t,lifetime:r})];e&&x.push(new ae(e)),x.push(...g);let us={"auth-throttle":new Z({maxAttempts:l,decayMinutes:u}),...b},Ut=new ce({debug:h,...C}),Bt=ts({middleware:x,namedMiddleware:us,onError:(ke,Oe)=>Ut.handle(ke,Oe)}),De;if(f){let{paraglideMiddleware:ke,getTextDirection:Oe=()=>"ltr"}=f;De=ss(async({event:$e,resolve:ds})=>ke($e.request,({request:hs,locale:Ht})=>($e.request=hs,ds($e,{transformPageChunk:({html:gs})=>gs.replace("%lang%",Ht).replace("%dir%",Oe(Ht))}))),Bt)}else De=Bt;return{handle:De,handleError:Ut.handleSvelteKitError()}}function ts(o={}){let e=new k;if(o.middleware)for(let t of o.middleware)e.use(t);if(o.namedMiddleware)for(let[t,s]of Object.entries(o.namedMiddleware))e.register(t,s);return async function({event:s,resolve:r}){let n={event:s,params:s.params??{},locals:s.locals??{}};try{o.app&&!o.app.isBooted()&&await o.app.bootstrap();let i=await e.execute(n,async()=>r(s));return i instanceof Response?i:r(s)}catch(i){if(o.onError){let a=await o.onError(i,s);if(a instanceof Response)return a}return console.error("[Svelar] Unhandled error in hooks:",i),new Response(JSON.stringify({message:process.env.NODE_ENV==="production"?"Internal server error":i.message}),{status:500,headers:{"Content-Type":"application/json"}})}}}function ss(...o){return async function({event:t,resolve:s}){let r=s;for(let n=o.length-1;n>=0;n--){let i=o[n],a=r;r=c=>i({event:c,resolve:a})}return r(t)}}T();function ks(o,e){let t=process.env[o];return t===void 0?e!==void 0?e:"":t==="true"?!0:t==="false"?!1:t==="null"?null:/^\d+$/.test(t)?Number(t):t}var yt=class{items=new Map;clear(){this.items.clear()}load(e){for(let[t,s]of Object.entries(e))this.set(t,s)}async loadFromDirectory(e){let{resolve:t,basename:s,extname:r}=await import("path"),{existsSync:n,readdirSync:i}=await import("fs"),{pathToFileURL:a}=await import("url"),c=t(e);if(!n(c))return[];let l=i(c).filter(h=>(h.endsWith(".ts")||h.endsWith(".js"))&&!h.startsWith(".")),u=[];for(let h of l){let g=s(h,r(h)),b=t(c,h);try{let C=await import(a(b).href),x=C.default??C.config??C;x&&typeof x=="object"&&!Array.isArray(x)&&(this.set(g,x),u.push(g))}catch{}}return u}get(e,t){let s=e.split("."),r=this.items.get(s[0]);for(let n=1;n<s.length;n++){if(r==null)return t;r=r[s[n]]}return r??t}set(e,t){let s=e.split(".");if(s.length===1){this.items.set(e,t);return}let r=this.items.get(s[0]);(r===void 0||typeof r!="object")&&(r={},this.items.set(s[0],r));let n=r;for(let i=1;i<s.length-1;i++)(n[s[i]]===void 0||typeof n[s[i]]!="object")&&(n[s[i]]={}),n=n[s[i]];n[s[s.length-1]]=t}has(e){return this.get(e)!==void 0}all(){let e={};for(let[t,s]of this.items)e[t]=s;return e}},Os=p("svelar.config",()=>new yt);import{z as w}from"zod";var $s={required:()=>w.string().min(1,"This field is required"),email:()=>w.string().email("Must be a valid email address"),string:(o,e)=>{let t=w.string();return o!==void 0&&(t=t.min(o)),e!==void 0&&(t=t.max(e)),t},number:(o,e)=>{let t=w.number();return o!==void 0&&(t=t.min(o)),e!==void 0&&(t=t.max(e)),t},integer:()=>w.number().int(),boolean:()=>w.boolean(),date:()=>w.coerce.date(),url:()=>w.string().url(),uuid:()=>w.string().uuid(),enum:o=>w.enum(o),array:o=>w.array(o),nullable:o=>o.nullable(),optional:o=>o.optional(),confirmed:(o="password")=>w.object({[o]:w.string(),[`${o}_confirmation`]:w.string()}).refine(e=>e[o]===e[`${o}_confirmation`],{message:"Confirmation does not match",path:[`${o}_confirmation`]}),min:o=>w.number().min(o),max:o=>w.number().max(o),between:(o,e)=>w.number().min(o).max(e),regex:(o,e)=>w.string().regex(o,e),ip:()=>w.string().refine(o=>{let e=o.split(".");return e.length!==4?!1:e.every(t=>{let s=Number(t);return Number.isInteger(s)&&s>=0&&s<=255})},{message:"Must be a valid IP address"}),json:()=>w.string().refine(o=>{try{return JSON.parse(o),!0}catch{return!1}},{message:"Must be valid JSON"})};function Ls(o,e){let t=o.safeParse(e);if(t.success)return{success:!0,data:t.data};let s={};for(let r of t.error.issues){let n=r.path.length>0?r.path:["_root"],i=s;for(let c=0;c<n.length-1;c++){let l=n[c];l in i||(i[l]={}),i=i[l]}let a=n[n.length-1];i[a]||(i[a]=[]),i[a].push(r.message)}return{success:!1,errors:s}}_();wt();T();import{readFile as ns,writeFile as Is,unlink as js,mkdir as de,readdir as bt,stat as is,copyFile as qs,rename as _s}from"fs/promises";import{existsSync as Us}from"fs";import{join as Bs,dirname as xe}from"path";var vt=class{constructor(e){this.config=e;if(!e.root)throw new Error('Local disk requires a "root" path.')}resolve(e){return Bs(this.config.root,e)}async get(e){return ns(this.resolve(e))}async getText(e){return ns(this.resolve(e),"utf-8")}async put(e,t){let s=this.resolve(e);await de(xe(s),{recursive:!0}),await Is(s,t)}async append(e,t){let{appendFile:s}=await import("fs/promises"),r=this.resolve(e);await de(xe(r),{recursive:!0}),await s(r,t)}async exists(e){return Us(this.resolve(e))}async delete(e){try{return await js(this.resolve(e)),!0}catch{return!1}}async copy(e,t){let s=this.resolve(t);await de(xe(s),{recursive:!0}),await qs(this.resolve(e),s)}async move(e,t){let s=this.resolve(t);await de(xe(s),{recursive:!0}),await _s(this.resolve(e),s)}async files(e=""){let t=this.resolve(e);try{return(await bt(t,{withFileTypes:!0})).filter(r=>r.isFile()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async allFiles(e=""){let t=[],s=this.resolve(e);try{let r=await bt(s,{withFileTypes:!0});for(let n of r){let i=e?`${e}/${n.name}`:n.name;n.isFile()?t.push(i):n.isDirectory()&&t.push(...await this.allFiles(i))}}catch{}return t}async directories(e=""){let t=this.resolve(e);try{return(await bt(t,{withFileTypes:!0})).filter(r=>r.isDirectory()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async makeDirectory(e){await de(this.resolve(e),{recursive:!0})}async deleteDirectory(e){let{rm:t}=await import("fs/promises");await t(this.resolve(e),{recursive:!0,force:!0})}async size(e){return(await is(this.resolve(e))).size}async lastModified(e){return(await is(this.resolve(e))).mtime}url(e){return`${this.config.urlPrefix??""}/${e}`}},Pe=class{config;_client=null;_s3Module=null;constructor(e){if(!e.bucket)throw new Error('S3 disk requires a "bucket" name.');this.config=e}async getS3(){if(this._s3Module)return this._s3Module;try{return this._s3Module=await Function('return import("@aws-sdk/client-s3")')(),this._s3Module}catch{throw new Error("S3 storage driver requires @aws-sdk/client-s3. Install it with: npm install @aws-sdk/client-s3")}}async getClient(){if(this._client)return this._client;let e=await this.getS3();return this._client=new e.S3Client({region:this.config.region??"us-east-1",endpoint:this.config.endpoint,forcePathStyle:this.config.forcePathStyle??!0,credentials:{accessKeyId:this.config.accessKeyId??"",secretAccessKey:this.config.secretAccessKey??""}}),this._client}key(e){let t=this.config.prefix;return t?`${t}/${e}`:e}async get(e){let t=await this.getS3(),n=await(await(await this.getClient()).send(new t.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).Body.transformToByteArray();return Buffer.from(n)}async getText(e){return(await this.get(e)).toString("utf-8")}async put(e,t){let s=await this.getS3(),r=await this.getClient(),n=typeof t=="string"?Buffer.from(t,"utf-8"):t;await r.send(new s.PutObjectCommand({Bucket:this.config.bucket,Key:this.key(e),Body:n}))}async append(e,t){let s=null;try{s=await this.get(e)}catch{}let r=typeof t=="string"?Buffer.from(t,"utf-8"):t,n=s?Buffer.concat([s,r]):r;await this.put(e,n)}async exists(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async delete(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.DeleteObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async copy(e,t){let s=await this.getS3();await(await this.getClient()).send(new s.CopyObjectCommand({Bucket:this.config.bucket,CopySource:`${this.config.bucket}/${this.key(e)}`,Key:this.key(t)}))}async move(e,t){await this.copy(e,t),await this.delete(e)}async files(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).Contents??[]).map(i=>i.Key).filter(i=>i!==r).map(i=>{let a=this.config.prefix;return a?i.slice(a.length+1):i})}catch{return[]}}async allFiles(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:""),n=[],i;do{let a=await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,ContinuationToken:i}));for(let c of a.Contents??[]){let l=this.config.prefix,u=l?c.Key.slice(l.length+1):c.Key;u&&n.push(u)}i=a.IsTruncated?a.NextContinuationToken:void 0}while(i);return n}async directories(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).CommonPrefixes??[]).map(i=>{let a=this.config.prefix;return(a?i.Prefix.slice(a.length+1):i.Prefix).replace(/\/$/,"")}).filter(i=>i.length>0)}catch{return[]}}async makeDirectory(e){}async deleteDirectory(e){let t=await this.allFiles(e);for(let s of t)await this.delete(s)}async size(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).ContentLength??0}async lastModified(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).LastModified??new Date}url(e){let t=this.config.urlPrefix;if(t)return`${t}/${e}`;let s=this.config.endpoint??`https://s3.${this.config.region??"us-east-1"}.amazonaws.com`;return this.config.forcePathStyle!==!1?`${s}/${this.config.bucket}/${this.key(e)}`:`${s.replace("://",`://${this.config.bucket}.`)}/${this.key(e)}`}async temporaryUrl(e,t=3600){try{let s=await Function('return import("@aws-sdk/s3-request-presigner")')(),r=await this.getS3(),n=await this.getClient(),i=new r.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)});return await s.getSignedUrl(n,i,{expiresIn:t})}catch{throw new Error("Pre-signed URLs require @aws-sdk/s3-request-presigner. Install it with: npm install @aws-sdk/s3-request-presigner")}}async ensureBucket(){let e=await this.getS3(),t=await this.getClient();try{await t.send(new e.HeadBucketCommand({Bucket:this.config.bucket}))}catch{await t.send(new e.CreateBucketCommand({Bucket:this.config.bucket}))}}},Tt=class{config=null;disks=new Map;configure(e){this.config=e}disk(e){let t=e??this.config?.default??"local";if(this.disks.has(t))return this.disks.get(t);if(!this.config)throw new Error("Storage not configured. Call Storage.configure() first.");let s=this.config.disks[t];if(!s)throw new Error(`Storage disk "${t}" is not defined.`);let r=this.createDisk(s);return this.disks.set(t,r),r}async get(e){return this.disk().get(e)}async getText(e){return this.disk().getText(e)}async put(e,t){return this.disk().put(e,t)}async append(e,t){return this.disk().append(e,t)}async exists(e){return this.disk().exists(e)}async delete(e){return this.disk().delete(e)}async copy(e,t){return this.disk().copy(e,t)}async move(e,t){return this.disk().move(e,t)}async files(e){return this.disk().files(e)}async allFiles(e){return this.disk().allFiles(e)}async directories(e){return this.disk().directories(e)}async makeDirectory(e){return this.disk().makeDirectory(e)}async deleteDirectory(e){return this.disk().deleteDirectory(e)}async size(e){return this.disk().size(e)}async lastModified(e){return this.disk().lastModified(e)}url(e){return this.disk().url(e)}createDisk(e){switch(e.driver){case"local":return new vt(e);case"s3":return new Pe(e);default:throw new Error(`Unknown storage driver: ${e.driver}`)}}s3Disk(e){let t=this.disk(e);if(!(t instanceof Pe))throw new Error(`Disk "${e??this.config?.default}" is not an S3 disk.`);return t}},Hs=p("svelar.storage",()=>new Tt);T();import{readFile as Fs,writeFile as Ks,unlink as as,mkdir as os}from"fs/promises";import{join as Js,dirname as Qs}from"path";import{createHash as Ws}from"crypto";var Ct=class{store=new Map;async get(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),null):t.value:null}async put(e,t,s){this.store.set(e,{value:t,expiresAt:s?Date.now()+s*1e3:null})}async forget(e){return this.store.delete(e)}async flush(){this.store.clear()}async has(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),!1):!0:!1}async increment(e,t=1){let r=(await this.get(e)??0)+t,n=this.store.get(e);return await this.put(e,r,n?.expiresAt?Math.ceil((n.expiresAt-Date.now())/1e3):void 0),r}async decrement(e,t=1){return this.increment(e,-t)}},Et=class{basePath;constructor(e){this.basePath=e.path??"storage/cache"}filePath(e){let t=Ws("md5").update(e).digest("hex");return Js(this.basePath,t.slice(0,2),t)}async get(e){let t=this.filePath(e);try{let s=await Fs(t,"utf-8"),r=JSON.parse(s);return r.expiresAt&&Date.now()>r.expiresAt?(await as(t).catch(()=>{}),null):r.value}catch{return null}}async put(e,t,s){let r=this.filePath(e),n={value:t,expiresAt:s?Date.now()+s*1e3:null};await os(Qs(r),{recursive:!0}),await Ks(r,JSON.stringify(n))}async forget(e){try{return await as(this.filePath(e)),!0}catch{return!1}}async flush(){let{rm:e}=await import("fs/promises");await e(this.basePath,{recursive:!0,force:!0}),await os(this.basePath,{recursive:!0})}async has(e){return await this.get(e)!==null}async increment(e,t=1){let r=(await this.get(e)??0)+t;return await this.put(e,r),r}async decrement(e,t=1){return this.increment(e,-t)}},xt=class{async get(){return null}async put(){}async forget(){return!0}async flush(){}async has(){return!1}async increment(){return 0}async decrement(){return 0}},Pt=class{config={default:"memory",stores:{memory:{driver:"memory"}}};stores=new Map;configure(e){this.config=e,this.stores.clear()}store(e){let t=e??this.config.default;if(this.stores.has(t))return this.stores.get(t);let s=this.config.stores[t];if(!s)throw new Error(`Cache store "${t}" is not defined.`);let r=this.createStore(s);return this.stores.set(t,r),r}async get(e,t){let s=this.store();return await s.has(e)?s.get(e):t??null}async put(e,t,s){return this.store().put(e,t,s??this.config.stores[this.config.default]?.ttl)}async forget(e){return this.store().forget(e)}async flush(){return this.store().flush()}async has(e){return this.store().has(e)}async increment(e,t){return this.store().increment(e,t)}async decrement(e,t){return this.store().decrement(e,t)}async remember(e,t,s){let r=await this.store().get(e);if(r!==null)return r;let n=await s();return await this.store().put(e,n,t),n}async rememberForever(e,t){let s=await this.store().get(e);if(s!==null)return s;let r=await t();return await this.store().put(e,r),r}async pull(e,t){let s=await this.get(e,t);return await this.forget(e),s}createStore(e){switch(e.driver){case"memory":return new Ct;case"file":return new Et(e);case"null":return new xt;case"redis":throw new Error("Redis cache requires ioredis. Install: npm install ioredis");default:throw new Error(`Unknown cache driver: ${e.driver}`)}}},Vs=p("svelar.cache",()=>new Pt);T();var Se=class{attempts=0;maxAttempts=3;retryDelay=60;queue="default";failed(e){console.error(`[Queue] Job ${this.constructor.name} permanently failed:`,e.message)}retrying(e){}serialize(){let e={};for(let[t,s]of Object.entries(this))typeof s!="function"&&(e[t]=s);return JSON.stringify(e)}restore(e){for(let[t,s]of Object.entries(e))t!=="attempts"&&t!=="maxAttempts"&&t!=="retryDelay"&&t!=="queue"&&(this[t]=s)}},Ae=class{async push(e){try{e.job.attempts=1,await e.job.handle()}catch(t){if(e.attempts+1<e.maxAttempts)return e.attempts++,e.job.attempts=e.attempts+1,e.job.retrying(e.job.attempts),this.push(e);e.job.failed(t)}}async pop(){return null}async size(){return 0}async clear(){}},St=class{queues=new Map;async push(e){let t=e.queue;this.queues.has(t)||this.queues.set(t,[]),this.queues.get(t).push(e)}async pop(e="default"){let t=this.queues.get(e)??[],s=Date.now(),r=t.findIndex(n=>n.availableAt<=s);return r===-1?null:t.splice(r,1)[0]}async size(e="default"){return this.queues.get(e)?.length??0}async clear(e){e?this.queues.delete(e):this.queues.clear()}},B=class{constructor(e,t){this.table=e;this.registry=t}async getConnection(){let{Connection:e}=await Promise.resolve().then(()=>(m(),y));return e}async push(e){await(await this.getConnection()).raw(`INSERT INTO ${this.table} (id, queue, payload, attempts, max_attempts, available_at, created_at)
|
|
102
|
+
`).map(s=>s.trim())),new Response(JSON.stringify(t),{status:500,headers:{"Content-Type":"application/json"}})}};function Ms(o){return{400:"Bad request",401:"Unauthenticated",403:"Forbidden",404:"Not found",405:"Method not allowed",409:"Conflict",419:"Page expired",422:"Unprocessable entity",429:"Too many requests",500:"Internal server error",502:"Bad gateway",503:"Service unavailable",504:"Gateway timeout"}[o]??"An error occurred"}function Ds(o={}){let{auth:e,secret:t=(()=>{throw new Error("APP_KEY is not set. Pass `secret` to createSvelarApp() \u2014 e.g. secret: env.APP_KEY (from $env/dynamic/private).")})(),sessionStore:s,sessionLifetime:r=86400,rateLimit:n=100,rateLimitWindow:i=6e4,csrfPaths:a=["/api/"],csrfExcludePaths:c=["/api/webhooks"],authThrottleAttempts:l=5,authThrottleDecay:u=1,debug:h=process.env.NODE_ENV!=="production",middleware:g=[],namedMiddleware:b={},i18n:f,errorConfig:C={}}=o,x=[new Y,new X({maxRequests:n,windowMs:i}),new G({onlyPaths:a,excludePaths:c}),new re({store:s??new q,secret:t,lifetime:r})];e&&x.push(new ae(e)),x.push(...g);let us={"auth-throttle":new Z({maxAttempts:l,decayMinutes:u}),...b},Ut=new ce({debug:h,...C}),Bt=ts({middleware:x,namedMiddleware:us,onError:(ke,Oe)=>Ut.handle(ke,Oe)}),De;if(f){let{paraglideMiddleware:ke,getTextDirection:Oe=()=>"ltr"}=f;De=ss(async({event:$e,resolve:ds})=>ke($e.request,({request:hs,locale:Ht})=>($e.request=hs,ds($e,{transformPageChunk:({html:gs})=>gs.replace("%lang%",Ht).replace("%dir%",Oe(Ht))}))),Bt)}else De=Bt;return{handle:De,handleError:Ut.handleSvelteKitError()}}function ts(o={}){let e=new k;if(o.middleware)for(let t of o.middleware)e.use(t);if(o.namedMiddleware)for(let[t,s]of Object.entries(o.namedMiddleware))e.register(t,s);return async function({event:s,resolve:r}){let n={event:s,params:s.params??{},locals:s.locals??{}};try{o.app&&!o.app.isBooted()&&await o.app.bootstrap();let i=await e.execute(n,async()=>r(s));return i instanceof Response?i:r(s)}catch(i){if(o.onError){let a=await o.onError(i,s);if(a instanceof Response)return a}return console.error("[Svelar] Unhandled error in hooks:",i),new Response(JSON.stringify({message:process.env.NODE_ENV==="production"?"Internal server error":i.message}),{status:500,headers:{"Content-Type":"application/json"}})}}}function ss(...o){return async function({event:t,resolve:s}){let r=s;for(let n=o.length-1;n>=0;n--){let i=o[n],a=r;r=c=>i({event:c,resolve:a})}return r(t)}}T();function ks(o,e){let t=process.env[o];return t===void 0?e!==void 0?e:"":t==="true"?!0:t==="false"?!1:t==="null"?null:/^\d+$/.test(t)?Number(t):t}var yt=class{items=new Map;clear(){this.items.clear()}load(e){for(let[t,s]of Object.entries(e))this.set(t,s)}async loadFromDirectory(e){let{resolve:t,basename:s,extname:r}=await import("path"),{existsSync:n,readdirSync:i}=await import("fs"),{pathToFileURL:a}=await import("url"),c=t(e);if(!n(c))return[];let l=i(c).filter(h=>(h.endsWith(".ts")||h.endsWith(".js"))&&!h.startsWith(".")),u=[];for(let h of l){let g=s(h,r(h)),b=t(c,h);try{let C=await import(a(b).href),x=C.default??C.config??C;x&&typeof x=="object"&&!Array.isArray(x)&&(this.set(g,x),u.push(g))}catch{}}return u}get(e,t){let s=e.split("."),r=this.items.get(s[0]);for(let n=1;n<s.length;n++){if(r==null)return t;r=r[s[n]]}return r??t}set(e,t){let s=e.split(".");if(s.length===1){this.items.set(e,t);return}let r=this.items.get(s[0]);(r===void 0||typeof r!="object")&&(r={},this.items.set(s[0],r));let n=r;for(let i=1;i<s.length-1;i++)(n[s[i]]===void 0||typeof n[s[i]]!="object")&&(n[s[i]]={}),n=n[s[i]];n[s[s.length-1]]=t}has(e){return this.get(e)!==void 0}all(){let e={};for(let[t,s]of this.items)e[t]=s;return e}},Os=p("svelar.config",()=>new yt);import{z as w}from"zod";var $s={required:()=>w.string().min(1,"This field is required"),email:()=>w.string().email("Must be a valid email address"),string:(o,e)=>{let t=w.string();return o!==void 0&&(t=t.min(o)),e!==void 0&&(t=t.max(e)),t},number:(o,e)=>{let t=w.number();return o!==void 0&&(t=t.min(o)),e!==void 0&&(t=t.max(e)),t},integer:()=>w.number().int(),boolean:()=>w.boolean(),date:()=>w.coerce.date(),url:()=>w.string().url(),uuid:()=>w.string().uuid(),enum:o=>w.enum(o),array:o=>w.array(o),nullable:o=>o.nullable(),optional:o=>o.optional(),confirmed:(o="password")=>w.object({[o]:w.string(),[`${o}_confirmation`]:w.string()}).refine(e=>e[o]===e[`${o}_confirmation`],{message:"Confirmation does not match",path:[`${o}_confirmation`]}),min:o=>w.number().min(o),max:o=>w.number().max(o),between:(o,e)=>w.number().min(o).max(e),regex:(o,e)=>w.string().regex(o,e),ip:()=>w.string().refine(o=>{let e=o.split(".");return e.length!==4?!1:e.every(t=>{let s=Number(t);return Number.isInteger(s)&&s>=0&&s<=255})},{message:"Must be a valid IP address"}),json:()=>w.string().refine(o=>{try{return JSON.parse(o),!0}catch{return!1}},{message:"Must be valid JSON"})};function Ls(o,e){let t=o.safeParse(e);if(t.success)return{success:!0,data:t.data};let s={};for(let r of t.error.issues){let n=r.path.length>0?r.path:["_root"],i=s;for(let c=0;c<n.length-1;c++){let l=n[c];l in i||(i[l]={}),i=i[l]}let a=n[n.length-1];i[a]||(i[a]=[]),i[a].push(r.message)}return{success:!1,errors:s}}_();wt();T();import{readFile as ns,writeFile as Is,unlink as js,mkdir as de,readdir as bt,stat as is,copyFile as qs,rename as _s}from"fs/promises";import{existsSync as Us}from"fs";import{join as Bs,dirname as xe}from"path";var vt=class{constructor(e){this.config=e;if(!e.root)throw new Error('Local disk requires a "root" path.')}resolve(e){return Bs(this.config.root,e)}async get(e){return ns(this.resolve(e))}async getText(e){return ns(this.resolve(e),"utf-8")}async put(e,t){let s=this.resolve(e);await de(xe(s),{recursive:!0}),await Is(s,t)}async append(e,t){let{appendFile:s}=await import("fs/promises"),r=this.resolve(e);await de(xe(r),{recursive:!0}),await s(r,t)}async exists(e){return Us(this.resolve(e))}async delete(e){try{return await js(this.resolve(e)),!0}catch{return!1}}async copy(e,t){let s=this.resolve(t);await de(xe(s),{recursive:!0}),await qs(this.resolve(e),s)}async move(e,t){let s=this.resolve(t);await de(xe(s),{recursive:!0}),await _s(this.resolve(e),s)}async files(e=""){let t=this.resolve(e);try{return(await bt(t,{withFileTypes:!0})).filter(r=>r.isFile()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async allFiles(e=""){let t=[],s=this.resolve(e);try{let r=await bt(s,{withFileTypes:!0});for(let n of r){let i=e?`${e}/${n.name}`:n.name;n.isFile()?t.push(i):n.isDirectory()&&t.push(...await this.allFiles(i))}}catch{}return t}async directories(e=""){let t=this.resolve(e);try{return(await bt(t,{withFileTypes:!0})).filter(r=>r.isDirectory()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async makeDirectory(e){await de(this.resolve(e),{recursive:!0})}async deleteDirectory(e){let{rm:t}=await import("fs/promises");await t(this.resolve(e),{recursive:!0,force:!0})}async size(e){return(await is(this.resolve(e))).size}async lastModified(e){return(await is(this.resolve(e))).mtime}url(e){return`${this.config.urlPrefix??""}/${e}`}},Pe=class{config;_client=null;_s3Module=null;constructor(e){if(!e.bucket)throw new Error('S3 disk requires a "bucket" name.');this.config=e}async getS3(){if(this._s3Module)return this._s3Module;try{return this._s3Module=await Function('return import("@aws-sdk/client-s3")')(),this._s3Module}catch{throw new Error("S3 storage driver requires @aws-sdk/client-s3. Install it with: npm install @aws-sdk/client-s3")}}async getClient(){if(this._client)return this._client;let e=await this.getS3();return this._client=new e.S3Client({region:this.config.region??"us-east-1",endpoint:this.config.endpoint,forcePathStyle:this.config.forcePathStyle??!0,credentials:{accessKeyId:this.config.accessKeyId??"",secretAccessKey:this.config.secretAccessKey??""}}),this._client}key(e){let t=this.config.prefix;return t?`${t}/${e}`:e}async get(e){let t=await this.getS3(),n=await(await(await this.getClient()).send(new t.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).Body.transformToByteArray();return Buffer.from(n)}async getText(e){return(await this.get(e)).toString("utf-8")}async put(e,t){let s=await this.getS3(),r=await this.getClient(),n=typeof t=="string"?Buffer.from(t,"utf-8"):t;await r.send(new s.PutObjectCommand({Bucket:this.config.bucket,Key:this.key(e),Body:n}))}async append(e,t){let s=null;try{s=await this.get(e)}catch{}let r=typeof t=="string"?Buffer.from(t,"utf-8"):t,n=s?Buffer.concat([s,r]):r;await this.put(e,n)}async exists(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async delete(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.DeleteObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async copy(e,t){let s=await this.getS3();await(await this.getClient()).send(new s.CopyObjectCommand({Bucket:this.config.bucket,CopySource:`${this.config.bucket}/${this.key(e)}`,Key:this.key(t)}))}async move(e,t){await this.copy(e,t),await this.delete(e)}async files(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).Contents??[]).map(i=>i.Key).filter(i=>i!==r).map(i=>{let a=this.config.prefix;return a?i.slice(a.length+1):i})}catch{return[]}}async allFiles(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:""),n=[],i;do{let a=await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,ContinuationToken:i}));for(let c of a.Contents??[]){let l=this.config.prefix,u=l?c.Key.slice(l.length+1):c.Key;u&&n.push(u)}i=a.IsTruncated?a.NextContinuationToken:void 0}while(i);return n}async directories(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).CommonPrefixes??[]).map(i=>{let a=this.config.prefix;return(a?i.Prefix.slice(a.length+1):i.Prefix).replace(/\/$/,"")}).filter(i=>i.length>0)}catch{return[]}}async makeDirectory(e){}async deleteDirectory(e){let t=await this.allFiles(e);for(let s of t)await this.delete(s)}async size(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).ContentLength??0}async lastModified(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).LastModified??new Date}url(e){let t=this.config.urlPrefix;if(t)return`${t}/${e}`;let s=this.config.endpoint??`https://s3.${this.config.region??"us-east-1"}.amazonaws.com`;return this.config.forcePathStyle!==!1?`${s}/${this.config.bucket}/${this.key(e)}`:`${s.replace("://",`://${this.config.bucket}.`)}/${this.key(e)}`}async temporaryUrl(e,t=3600){try{let s=await Function('return import("@aws-sdk/s3-request-presigner")')(),r=await this.getS3(),n=await this.getClient(),i=new r.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)});return await s.getSignedUrl(n,i,{expiresIn:t})}catch{throw new Error("Pre-signed URLs require @aws-sdk/s3-request-presigner. Install it with: npm install @aws-sdk/s3-request-presigner")}}async ensureBucket(){let e=await this.getS3(),t=await this.getClient();try{await t.send(new e.HeadBucketCommand({Bucket:this.config.bucket}))}catch{await t.send(new e.CreateBucketCommand({Bucket:this.config.bucket}))}}},Tt=class{config=null;disks=new Map;configure(e){this.config=e}disk(e){let t=e??this.config?.default??"local";if(this.disks.has(t))return this.disks.get(t);if(!this.config)throw new Error("Storage not configured. Call Storage.configure() first.");let s=this.config.disks[t];if(!s)throw new Error(`Storage disk "${t}" is not defined.`);let r=this.createDisk(s);return this.disks.set(t,r),r}async get(e){return this.disk().get(e)}async getText(e){return this.disk().getText(e)}async put(e,t){return this.disk().put(e,t)}async append(e,t){return this.disk().append(e,t)}async exists(e){return this.disk().exists(e)}async delete(e){return this.disk().delete(e)}async copy(e,t){return this.disk().copy(e,t)}async move(e,t){return this.disk().move(e,t)}async files(e){return this.disk().files(e)}async allFiles(e){return this.disk().allFiles(e)}async directories(e){return this.disk().directories(e)}async makeDirectory(e){return this.disk().makeDirectory(e)}async deleteDirectory(e){return this.disk().deleteDirectory(e)}async size(e){return this.disk().size(e)}async lastModified(e){return this.disk().lastModified(e)}url(e){return this.disk().url(e)}createDisk(e){switch(e.driver){case"local":return new vt(e);case"s3":return new Pe(e);default:throw new Error(`Unknown storage driver: ${e.driver}`)}}s3Disk(e){let t=this.disk(e);if(!(t instanceof Pe))throw new Error(`Disk "${e??this.config?.default}" is not an S3 disk.`);return t}},Hs=p("svelar.storage",()=>new Tt);T();import{readFile as Fs,writeFile as Ks,unlink as as,mkdir as os}from"fs/promises";import{join as Js,dirname as Qs}from"path";import{createHash as Ws}from"crypto";var Ct=class{store=new Map;async get(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),null):t.value:null}async put(e,t,s){this.store.set(e,{value:t,expiresAt:s?Date.now()+s*1e3:null})}async forget(e){return this.store.delete(e)}async flush(){this.store.clear()}async has(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),!1):!0:!1}async increment(e,t=1){let r=(await this.get(e)??0)+t,n=this.store.get(e);return await this.put(e,r,n?.expiresAt?Math.ceil((n.expiresAt-Date.now())/1e3):void 0),r}async decrement(e,t=1){return this.increment(e,-t)}},Et=class{basePath;constructor(e){this.basePath=e.path??"storage/cache"}filePath(e){let t=Ws("md5").update(e).digest("hex");return Js(this.basePath,t.slice(0,2),t)}async get(e){let t=this.filePath(e);try{let s=await Fs(t,"utf-8"),r=JSON.parse(s);return r.expiresAt&&Date.now()>r.expiresAt?(await as(t).catch(()=>{}),null):r.value}catch{return null}}async put(e,t,s){let r=this.filePath(e),n={value:t,expiresAt:s?Date.now()+s*1e3:null};await os(Qs(r),{recursive:!0}),await Ks(r,JSON.stringify(n))}async forget(e){try{return await as(this.filePath(e)),!0}catch{return!1}}async flush(){let{rm:e}=await import("fs/promises");await e(this.basePath,{recursive:!0,force:!0}),await os(this.basePath,{recursive:!0})}async has(e){return await this.get(e)!==null}async increment(e,t=1){let r=(await this.get(e)??0)+t;return await this.put(e,r),r}async decrement(e,t=1){return this.increment(e,-t)}},xt=class{async get(){return null}async put(){}async forget(){return!0}async flush(){}async has(){return!1}async increment(){return 0}async decrement(){return 0}},Pt=class{config={default:"memory",stores:{memory:{driver:"memory"}}};stores=new Map;configure(e){this.config=e,this.stores.clear()}store(e){let t=e??this.config.default;if(this.stores.has(t))return this.stores.get(t);let s=this.config.stores[t];if(!s)throw new Error(`Cache store "${t}" is not defined.`);let r=this.createStore(s);return this.stores.set(t,r),r}async get(e,t){let s=this.store();return await s.has(e)?s.get(e):t??null}async put(e,t,s){return this.store().put(e,t,s??this.config.stores[this.config.default]?.ttl)}async forget(e){return this.store().forget(e)}async flush(){return this.store().flush()}async has(e){return this.store().has(e)}async increment(e,t){return this.store().increment(e,t)}async decrement(e,t){return this.store().decrement(e,t)}async remember(e,t,s){let r=await this.store().get(e);if(r!==null)return r;let n=await s();return await this.store().put(e,n,t),n}async rememberForever(e,t){let s=await this.store().get(e);if(s!==null)return s;let r=await t();return await this.store().put(e,r),r}async pull(e,t){let s=await this.get(e,t);return await this.forget(e),s}createStore(e){switch(e.driver){case"memory":return new Ct;case"file":return new Et(e);case"null":return new xt;case"redis":throw new Error("Redis cache requires ioredis. Install: npm install ioredis");default:throw new Error(`Unknown cache driver: ${e.driver}`)}}},Vs=p("svelar.cache",()=>new Pt);T();var Se=class{attempts=0;maxAttempts=3;retryDelay=60;queue="default";failed(e){console.error(`[Queue] Job ${this.constructor.name} permanently failed:`,e.message)}retrying(e){}serialize(){let e={};for(let[t,s]of Object.entries(this))typeof s!="function"&&(e[t]=s);return JSON.stringify(e)}restore(e){for(let[t,s]of Object.entries(e))t!=="attempts"&&t!=="maxAttempts"&&t!=="retryDelay"&&t!=="queue"&&(this[t]=s)}},Ae=class{async push(e){try{e.job.attempts=1,await e.job.handle()}catch(t){if(e.attempts+1<e.maxAttempts)return e.attempts++,e.job.attempts=e.attempts+1,e.job.retrying(e.job.attempts),this.push(e);e.job.failed(t)}}async pop(){return null}async size(){return 0}async clear(){}},St=class{queues=new Map;async push(e){let t=e.queue;this.queues.has(t)||this.queues.set(t,[]),this.queues.get(t).push(e)}async pop(e="default"){let t=this.queues.get(e)??[],s=Date.now(),r=t.findIndex(n=>n.availableAt<=s);return r===-1?null:t.splice(r,1)[0]}async size(e="default"){return this.queues.get(e)?.length??0}async clear(e){e?this.queues.delete(e):this.queues.clear()}},B=class{constructor(e,t){this.table=e;this.registry=t}async getConnection(){let{Connection:e}=await Promise.resolve().then(()=>(m(),y));return e}async push(e){await(await this.getConnection()).raw(`INSERT INTO ${this.table} (id, queue, payload, attempts, max_attempts, available_at, created_at)
|
|
103
103
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,[e.id,e.queue,JSON.stringify({jobClass:e.jobClass,payload:e.payload}),e.attempts,e.maxAttempts,Math.floor(e.availableAt/1e3),Math.floor(e.createdAt/1e3)])}async pop(e="default"){let t=await this.getConnection(),s=Math.floor(Date.now()/1e3),r=await t.raw(`SELECT * FROM ${this.table}
|
|
104
104
|
WHERE queue = ? AND available_at <= ? AND reserved_at IS NULL
|
|
105
105
|
ORDER BY created_at ASC LIMIT 1`,[e,s]);if(!r||r.length===0)return null;let n=r[0];await t.raw(`UPDATE ${this.table} SET reserved_at = ?, attempts = attempts + 1 WHERE id = ?`,[s,n.id]);let i=JSON.parse(n.payload),a=this.registry.resolve(i.jobClass,i.payload);return{id:n.id,jobClass:i.jobClass,payload:i.payload,queue:n.queue,attempts:n.attempts+1,maxAttempts:n.max_attempts,availableAt:n.available_at*1e3,createdAt:n.created_at*1e3,job:a}}async size(e="default"){return(await(await this.getConnection()).raw(`SELECT COUNT(*) as count FROM ${this.table} WHERE queue = ? AND reserved_at IS NULL`,[e]))?.[0]?.count??0}async clear(e){let t=await this.getConnection();e?await t.raw(`DELETE FROM ${this.table} WHERE queue = ?`,[e]):await t.raw(`DELETE FROM ${this.table}`,[])}async delete(e){await(await this.getConnection()).raw(`DELETE FROM ${this.table} WHERE id = ?`,[e])}async release(e,t=0){let s=await this.getConnection(),r=Math.floor(Date.now()/1e3)+t;await s.raw(`UPDATE ${this.table} SET reserved_at = NULL, available_at = ? WHERE id = ?`,[r,e])}},Re=class{queues=new Map;config;registry;_bullmq=null;constructor(e,t){this.config=e,this.registry=t}async getBullMQ(){if(this._bullmq)return this._bullmq;try{return this._bullmq=await Function('return import("bullmq")')(),this._bullmq}catch{throw new Error("bullmq is required for the Redis queue driver. Install it with: npm install bullmq")}}getRedisConnection(){if(this.config.url){let e=new URL(this.config.url);return{host:e.hostname||"localhost",port:parseInt(e.port)||6379,password:e.password||this.config.password||void 0,db:parseInt(e.pathname?.slice(1)||"0")||this.config.db||0}}return{host:this.config.host??"localhost",port:this.config.port??6379,password:this.config.password,db:this.config.db??0}}async getQueue(e){if(this.queues.has(e))return this.queues.get(e);let t=await this.getBullMQ(),s=this.getRedisConnection(),r=this.config.prefix??"svelar",n=new t.Queue(e,{connection:s,prefix:r,defaultJobOptions:{removeOnComplete:this.config.defaultJobOptions?.removeOnComplete??100,removeOnFail:this.config.defaultJobOptions?.removeOnFail??500}});return this.queues.set(e,n),n}async push(e){let t=await this.getQueue(e.queue),s=Math.max(0,e.availableAt-Date.now());await t.add(e.jobClass,{jobClass:e.jobClass,payload:e.payload},{jobId:e.id,delay:s>0?s:void 0,attempts:e.maxAttempts,backoff:{type:"fixed",delay:(e.job.retryDelay??60)*1e3}})}async pop(e){return null}async size(e="default"){let s=await(await this.getQueue(e)).getJobCounts("waiting","delayed","active");return s.waiting+s.delayed+s.active}async clear(e){if(e)await(await this.getQueue(e)).obliterate({force:!0});else for(let t of this.queues.values())await t.obliterate({force:!0})}async createWorker(e,t,s,r){let n=await this.getBullMQ(),i=this.getRedisConnection(),a=this.config.prefix??"svelar",c=new n.Worker(e,async l=>{let u=l.data,h=t.resolve(u.jobClass,u.payload);h.attempts=l.attemptsMade+1,await h.handle()},{connection:i,prefix:a,concurrency:r?.concurrency??1});return c.on("failed",async(l,u)=>{let h=l?.data;if(h)try{let g=t.resolve(h.jobClass,h.payload);l.attemptsMade>=(l.opts?.attempts??3)&&(g.failed(u),await s.store({id:l.id,jobClass:h.jobClass,payload:h.payload,queue:e,attempts:l.attemptsMade,maxAttempts:l.opts?.attempts??3,availableAt:Date.now(),createdAt:l.timestamp??Date.now(),job:g},u))}catch{console.error("[Queue] Failed to resolve job for failure handler:",u.message)}}),c}},At=class{table="svelar_failed_jobs";async getConnection(){let{Connection:e}=await Promise.resolve().then(()=>(m(),y));return e}async store(e,t){try{await(await this.getConnection()).raw(`INSERT INTO ${this.table} (id, queue, job_class, payload, exception, failed_at)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Search — Meilisearch integration for models
|
|
3
|
+
*
|
|
4
|
+
* Provides a Searchable mixin for automatic index syncing,
|
|
5
|
+
* a Search singleton for configuration, and a SearchObserver
|
|
6
|
+
* for hooking into model lifecycle events.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { Search, Searchable } from '@beeblock/svelar/search';
|
|
11
|
+
* import { Model } from '@beeblock/svelar/orm';
|
|
12
|
+
*
|
|
13
|
+
* Search.configure({
|
|
14
|
+
* host: env.MEILISEARCH_HOST ?? 'http://localhost:7700',
|
|
15
|
+
* apiKey: env.MEILISEARCH_KEY,
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* class Post extends Searchable(Model) {
|
|
19
|
+
* static table = 'posts';
|
|
20
|
+
*
|
|
21
|
+
* // Customize what gets indexed (optional)
|
|
22
|
+
* toSearchableObject() {
|
|
23
|
+
* return {
|
|
24
|
+
* id: this.getAttribute('id'),
|
|
25
|
+
* title: this.getAttribute('title'),
|
|
26
|
+
* content: this.getAttribute('content'),
|
|
27
|
+
* author: this.getAttribute('author_name'),
|
|
28
|
+
* };
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* // Search
|
|
33
|
+
* const results = await Post.search('hello world');
|
|
34
|
+
*
|
|
35
|
+
* // Bulk operations without syncing
|
|
36
|
+
* await Search.withoutSyncing(async () => {
|
|
37
|
+
* await Post.query().where('status', 'draft').update({ status: 'archived' });
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* // Manually re-index all records
|
|
41
|
+
* await Post.makeAllSearchable();
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export interface SearchConfig {
|
|
45
|
+
host: string;
|
|
46
|
+
apiKey?: string;
|
|
47
|
+
indexPrefix?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface SearchHit {
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
}
|
|
52
|
+
export interface SearchResults {
|
|
53
|
+
hits: SearchHit[];
|
|
54
|
+
query: string;
|
|
55
|
+
processingTimeMs: number;
|
|
56
|
+
estimatedTotalHits?: number;
|
|
57
|
+
limit: number;
|
|
58
|
+
offset: number;
|
|
59
|
+
}
|
|
60
|
+
export interface SearchOptions {
|
|
61
|
+
limit?: number;
|
|
62
|
+
offset?: number;
|
|
63
|
+
filter?: string | string[];
|
|
64
|
+
sort?: string[];
|
|
65
|
+
attributesToRetrieve?: string[];
|
|
66
|
+
attributesToHighlight?: string[];
|
|
67
|
+
facets?: string[];
|
|
68
|
+
}
|
|
69
|
+
export interface SearchableInstance {
|
|
70
|
+
getSearchableIndex(): string;
|
|
71
|
+
getSearchableId(): string | number;
|
|
72
|
+
toSearchableObject(): Record<string, any>;
|
|
73
|
+
searchableAs(): string;
|
|
74
|
+
shouldBeSearchable(): boolean;
|
|
75
|
+
}
|
|
76
|
+
type Constructor = new (...args: any[]) => any;
|
|
77
|
+
declare class SearchManager {
|
|
78
|
+
private config;
|
|
79
|
+
private client;
|
|
80
|
+
private syncingDisabled;
|
|
81
|
+
configure(config: SearchConfig): void;
|
|
82
|
+
getClient(): any;
|
|
83
|
+
getIndexName(baseName: string): string;
|
|
84
|
+
isSyncingDisabled(): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Execute a callback with search index syncing disabled.
|
|
87
|
+
* Useful for bulk operations, seeding, or migrations where
|
|
88
|
+
* you don't want every model save to trigger an index update.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* await Search.withoutSyncing(async () => {
|
|
93
|
+
* for (const row of bulkData) {
|
|
94
|
+
* await Post.create(row);
|
|
95
|
+
* }
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* // Re-index everything after bulk insert
|
|
99
|
+
* await Post.makeAllSearchable();
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
withoutSyncing<T>(callback: () => T | Promise<T>): Promise<T>;
|
|
103
|
+
/**
|
|
104
|
+
* Check if the Meilisearch instance is reachable.
|
|
105
|
+
*/
|
|
106
|
+
health(): Promise<{
|
|
107
|
+
status: string;
|
|
108
|
+
}>;
|
|
109
|
+
}
|
|
110
|
+
export declare const Search: SearchManager;
|
|
111
|
+
export declare function Searchable<TBase extends Constructor>(Base: TBase): TBase & (new (...args: any[]) => SearchableInstance);
|
|
112
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var x=(s=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(s,{get:(n,c)=>(typeof require<"u"?require:n)[c]}):s)(function(s){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+s+'" is not supported')});function S(s,n){let c=Symbol.for(s),o=globalThis;return o[c]||(o[c]=n()),o[c]}var u=class{config=null;client=null;syncingDisabled=!1;configure(n){this.config=n,this.client=null}getClient(){if(!this.config)throw new Error("Search not configured. Call Search.configure({ host, apiKey }) first.");if(!this.client)try{let{MeiliSearch:n}=x("meilisearch");this.client=new n({host:this.config.host,apiKey:this.config.apiKey})}catch{throw new Error("meilisearch package not installed. Run: npm install meilisearch")}return this.client}getIndexName(n){let c=this.config?.indexPrefix??"";return c?`${c}_${n}`:n}isSyncingDisabled(){return this.syncingDisabled}async withoutSyncing(n){this.syncingDisabled=!0;try{return await n()}finally{this.syncingDisabled=!1}}async health(){return this.getClient().health()}},a=S("svelar.search",()=>new u);function I(s){class n extends s{getSearchableIndex(){let t=this.constructor.table??this.constructor.name.toLowerCase()+"s";return a.getIndexName(t)}getSearchableId(){return this.id??this.getAttribute?.("id")}searchableAs(){return"id"}shouldBeSearchable(){return!0}toSearchableObject(){return{...this.attributes??this.toJSON?.()??{}}}async searchable(){if(!this.shouldBeSearchable()){await this.unsearchable();return}let e=a.getClient().index(this.getSearchableIndex()),r=this.toSearchableObject();r[this.searchableAs()]=this.getSearchableId(),await e.addDocuments([r],{primaryKey:this.searchableAs()})}async unsearchable(){let e=a.getClient().index(this.getSearchableIndex());try{await e.deleteDocument(this.getSearchableId())}catch{}}static async search(t,e={}){let r=new this,d=a.getClient().index(r.getSearchableIndex()),i={};return e.limit!==void 0&&(i.limit=e.limit),e.offset!==void 0&&(i.offset=e.offset),e.filter&&(i.filter=e.filter),e.sort&&(i.sort=e.sort),e.attributesToRetrieve&&(i.attributesToRetrieve=e.attributesToRetrieve),e.attributesToHighlight&&(i.attributesToHighlight=e.attributesToHighlight),e.facets&&(i.facets=e.facets),d.search(t,i)}static async makeAllSearchable(t=500){let e=new this,g=a.getClient().index(e.getSearchableIndex()),d=await this.query().get(),i=[];for(let l of d)if(l.shouldBeSearchable()){let b=l.toSearchableObject();b[l.searchableAs()]=l.getSearchableId(),i.push(b)}for(let l=0;l<i.length;l+=t){let b=i.slice(l,l+t);await g.addDocuments(b,{primaryKey:e.searchableAs()})}return{indexed:i.length}}static async removeAllFromSearch(){let t=new this;await a.getClient().index(t.getSearchableIndex()).deleteAllDocuments()}static async configureSearchIndex(t){let e=new this;await a.getClient().index(e.getSearchableIndex()).updateSettings(t)}static async searchIndexStats(){let t=new this;return a.getClient().index(t.getSearchableIndex()).getStats()}}let c=s.boot;n._searchableBooted=!1;let o=n.prototype.save;o&&(n.prototype.save=async function(...h){let t=await o.apply(this,h);if(!a.isSyncingDisabled())try{await this.searchable()}catch{}return t});let y=n.create;y&&(n.create=async function(h){let t=await y.call(this,h);if(!a.isSyncingDisabled()&&t)try{await t.searchable()}catch{}return t});let f=n.prototype.delete;return f&&(n.prototype.delete=async function(...h){let t=this.getSearchableId(),e=this.getSearchableIndex(),r=await f.apply(this,h);if(!a.isSyncingDisabled())try{await a.getClient().index(e).deleteDocument(t)}catch{}return r}),n}export{a as Search,I as Searchable};
|
package/dist/session/index.js
CHANGED
|
@@ -13,4 +13,4 @@ var A=Object.defineProperty;var N=(c,e)=>()=>(c&&(e=c(c=0)),e);var E=(c,e)=>{for
|
|
|
13
13
|
) ENGINE=InnoDB`,[],this.connectionName);break}this.tableEnsured=!0}catch{}}async read(e){await this.ensureTable();let{Connection:t}=await Promise.resolve().then(()=>(p(),h)),s=await t.raw(`SELECT payload, expires_at FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName);if(s.length===0)return null;let n=s[0];if(new Date(n.expires_at)<new Date)return await this.destroy(e),null;try{return JSON.parse(n.payload)}catch{return null}}async write(e,t,s){await this.ensureTable();let{Connection:n}=await Promise.resolve().then(()=>(p(),h)),i=JSON.stringify(t),r=new Date(Date.now()+s*1e3).toISOString(),a=n.getDriver(this.connectionName);a==="sqlite"?await n.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
14
14
|
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, expires_at = excluded.expires_at`,[e,i,r],this.connectionName):a==="postgres"?await n.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES ($1, $2, $3)
|
|
15
15
|
ON CONFLICT(id) DO UPDATE SET payload = $2, expires_at = $3`,[e,i,r],this.connectionName):await n.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
16
|
-
ON DUPLICATE KEY UPDATE payload = VALUES(payload), expires_at = VALUES(expires_at)`,[e,i,r],this.connectionName)}async destroy(e){let{Connection:t}=await Promise.resolve().then(()=>(p(),h));await t.raw(`DELETE FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName)}async gc(e){let{Connection:t}=await Promise.resolve().then(()=>(p(),h));await t.raw(`DELETE FROM ${this.tableName} WHERE expires_at < ?`,[new Date().toISOString()],this.connectionName)}},C=class{dir;constructor(e){this.dir=e??g(process.cwd(),"storage","sessions")}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return g(this.dir,`${t}.json`)}async ensureDir(){await u.mkdir(this.dir,{recursive:!0})}async read(e){try{let t=await u.readFile(this.filePath(e),"utf-8"),s=JSON.parse(t);return new Date(s.expiresAt)<new Date?(await this.destroy(e),null):s.data}catch{return null}}async write(e,t,s){await this.ensureDir();let n={data:t,expiresAt:new Date(Date.now()+s*1e3).toISOString()};await u.writeFile(this.filePath(e),JSON.stringify(n),"utf-8")}async destroy(e){try{await u.unlink(this.filePath(e))}catch{}}async gc(e){try{let t=await u.readdir(this.dir),s=new Date;for(let n of t)if(n.endsWith(".json"))try{let i=await u.readFile(g(this.dir,n),"utf-8"),r=JSON.parse(i);new Date(r.expiresAt)<s&&await u.unlink(g(this.dir,n))}catch{await u.unlink(g(this.dir,n)).catch(()=>{})}}catch{}}},x=class{redis;prefix;constructor(e){this.prefix=e?.prefix??"svelar_session:",e?.client?this.redis=e.client:this._url=e?.url}_url;_connecting;async getClient(){return this.redis?this.redis:(this._connecting||(this._connecting=(async()=>{try{let{default:e}=await import("ioredis");return this.redis=this._url?new e(this._url):new e,this.redis}catch{throw new Error('RedisSessionStore requires "ioredis" package. Install it: npm install ioredis')}})()),this._connecting)}async read(e){let s=await(await this.getClient()).get(this.prefix+e);if(!s)return null;try{return JSON.parse(s)}catch{return null}}async write(e,t,s){await(await this.getClient()).set(this.prefix+e,JSON.stringify(t),"EX",s)}async destroy(e){await(await this.getClient()).del(this.prefix+e)}async gc(e){}},b=class extends w{config;constructor(e){super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:
|
|
16
|
+
ON DUPLICATE KEY UPDATE payload = VALUES(payload), expires_at = VALUES(expires_at)`,[e,i,r],this.connectionName)}async destroy(e){let{Connection:t}=await Promise.resolve().then(()=>(p(),h));await t.raw(`DELETE FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName)}async gc(e){let{Connection:t}=await Promise.resolve().then(()=>(p(),h));await t.raw(`DELETE FROM ${this.tableName} WHERE expires_at < ?`,[new Date().toISOString()],this.connectionName)}},C=class{dir;constructor(e){this.dir=e??g(process.cwd(),"storage","sessions")}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return g(this.dir,`${t}.json`)}async ensureDir(){await u.mkdir(this.dir,{recursive:!0})}async read(e){try{let t=await u.readFile(this.filePath(e),"utf-8"),s=JSON.parse(t);return new Date(s.expiresAt)<new Date?(await this.destroy(e),null):s.data}catch{return null}}async write(e,t,s){await this.ensureDir();let n={data:t,expiresAt:new Date(Date.now()+s*1e3).toISOString()};await u.writeFile(this.filePath(e),JSON.stringify(n),"utf-8")}async destroy(e){try{await u.unlink(this.filePath(e))}catch{}}async gc(e){try{let t=await u.readdir(this.dir),s=new Date;for(let n of t)if(n.endsWith(".json"))try{let i=await u.readFile(g(this.dir,n),"utf-8"),r=JSON.parse(i);new Date(r.expiresAt)<s&&await u.unlink(g(this.dir,n))}catch{await u.unlink(g(this.dir,n)).catch(()=>{})}}catch{}}},x=class{redis;prefix;constructor(e){this.prefix=e?.prefix??"svelar_session:",e?.client?this.redis=e.client:this._url=e?.url}_url;_connecting;async getClient(){return this.redis?this.redis:(this._connecting||(this._connecting=(async()=>{try{let{default:e}=await import("ioredis");return this.redis=this._url?new e(this._url):new e,this.redis}catch{throw new Error('RedisSessionStore requires "ioredis" package. Install it: npm install ioredis')}})()),this._connecting)}async read(e){let s=await(await this.getClient()).get(this.prefix+e);if(!s)return null;try{return JSON.parse(s)}catch{return null}}async write(e,t,s){await(await this.getClient()).set(this.prefix+e,JSON.stringify(t),"EX",s)}async destroy(e){await(await this.getClient()).del(this.prefix+e)}async gc(e){}},b=class extends w{config;constructor(e){if(super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:"",path:"/",domain:"",secure:process.env.NODE_ENV==="production",httpOnly:!0,sameSite:"lax",...e},!this.config.secret)throw new Error("APP_KEY is not set. Pass `secret` to createSvelarApp() \u2014 e.g. secret: env.APP_KEY (from $env/dynamic/private).")}async handle(e,t){let s=e.event.request.headers.get("cookie")??"",n=this.getSessionIdFromCookie(s),i=null;if(n){let o=this.verifySignedId(n);o?(i=await this.config.store.read(o),n=o):n=null}n||(n=f.generateId());let r=new f(n,i??{});e.event.locals.session=r,e.locals.session=r;let a=await t();if(r.isDirty()&&await this.config.store.write(r.id,r.toPersist(),this.config.lifetime),a instanceof Response){let o=this.signId(r.id),d=this.buildCookieString(o);a.headers.append("Set-Cookie",d)}return a}getSessionIdFromCookie(e){let t=e.split(";").map(s=>s.trim());for(let s of t){let[n,...i]=s.split("=");if(n===this.config.cookieName)return decodeURIComponent(i.join("="))}return null}signId(e){let t=P("sha256",this.config.secret).update(e).digest("base64url");return`${e}.${t}`}verifySignedId(e){let t=e.lastIndexOf(".");if(t===-1)return null;let s=e.slice(0,t),n=e.slice(t+1),i=P("sha256",this.config.secret).update(s).digest("base64url");if(n.length!==i.length)return null;let r=Buffer.from(n),a=Buffer.from(i);if(r.length!==a.length)return null;try{if(O(r,a))return s}catch{}return null}buildCookieString(e){let t=[`${this.config.cookieName}=${encodeURIComponent(e)}`];return t.push(`Path=${this.config.path}`),t.push(`Max-Age=${this.config.lifetime}`),this.config.domain&&t.push(`Domain=${this.config.domain}`),this.config.secure&&t.push("Secure"),this.config.httpOnly&&t.push("HttpOnly"),t.push(`SameSite=${this.config.sameSite}`),t.join("; ")}};export{S as DatabaseSessionStore,C as FileSessionStore,y as MemorySessionStore,x as RedisSessionStore,f as Session,b as SessionMiddleware};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beeblock/svelar",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Laravel-inspired framework on top of SvelteKit 2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -150,6 +150,10 @@
|
|
|
150
150
|
"types": "./dist/permissions/index.d.ts",
|
|
151
151
|
"import": "./dist/permissions/index.js"
|
|
152
152
|
},
|
|
153
|
+
"./search": {
|
|
154
|
+
"types": "./dist/search/index.d.ts",
|
|
155
|
+
"import": "./dist/search/index.js"
|
|
156
|
+
},
|
|
153
157
|
"./support": {
|
|
154
158
|
"types": "./dist/support/index.d.ts",
|
|
155
159
|
"import": "./dist/support/index.js"
|
|
@@ -277,7 +281,8 @@
|
|
|
277
281
|
"pdfkit": ">=0.18.0",
|
|
278
282
|
"postgres": "*",
|
|
279
283
|
"pusher-js": ">=8.0.0",
|
|
280
|
-
"sveltekit-superforms": ">=2.0.0"
|
|
284
|
+
"sveltekit-superforms": ">=2.0.0",
|
|
285
|
+
"meilisearch": ">=0.44.0"
|
|
281
286
|
},
|
|
282
287
|
"peerDependenciesMeta": {
|
|
283
288
|
"@aws-sdk/client-s3": {
|
|
@@ -312,6 +317,9 @@
|
|
|
312
317
|
},
|
|
313
318
|
"sveltekit-superforms": {
|
|
314
319
|
"optional": true
|
|
320
|
+
},
|
|
321
|
+
"meilisearch": {
|
|
322
|
+
"optional": true
|
|
315
323
|
}
|
|
316
324
|
},
|
|
317
325
|
"keywords": [
|