@mateosuarezdev/brpc 1.0.113 → 1.0.115

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 CHANGED
@@ -181,6 +181,37 @@ Repeated unindexed names and append-style `files[]` are intentionally not
181
181
  accepted. They cannot distinguish a one-item array from a single file; direct
182
182
  callers use indexed keys just like the BRPC client.
183
183
 
184
+ #### Typed context in a form mutation
185
+
186
+ `formMutation` is also useful when a file upload carries current UI context.
187
+ The router rebuilds its nested shape, but every non-file leaf is still text.
188
+ Describe that context at the procedure input boundary and coerce only the
189
+ fields that need their native type before passing it to application code or an
190
+ LLM prompt:
191
+
192
+ ```ts
193
+ const dayContextSchema = z.object({
194
+ targets: z.object({
195
+ calories: z.coerce.number(),
196
+ protein: z.coerce.number(),
197
+ }),
198
+ proteinProtection: z.stringbool(), // "true" / "false"
199
+ meals: z.array(z.object({ calories: z.coerce.number() })).default([]),
200
+ });
201
+
202
+ procedure.input(z.object({
203
+ image: createFileSchema({ acceptedTypes: { image: "*" }, maxSize: 15 }),
204
+ day: dayContextSchema.optional(),
205
+ }));
206
+ ```
207
+
208
+ Do not use `z.coerce.boolean()` for multipart text: JavaScript treats the
209
+ non-empty string `"false"` as truthy. Use `z.stringbool()` instead. Empty
210
+ arrays emit no multipart fields, so use `.default([])` when an absent array
211
+ means an empty one. Send a nested value as one JSON string only when it must
212
+ retain every native primitive before its route-specific parsing; otherwise the
213
+ typed Zod input is the normal path.
214
+
184
215
  ```ts
185
216
  import { createFileSchema } from "@mateosuarezdev/brpc";
186
217
 
@@ -330,6 +361,16 @@ const page = procedure.html(async ({ ctx }) => {
330
361
 
331
362
  WebSocket-based pub/sub. Clients subscribe to a topic and receive messages when your server publishes.
332
363
 
364
+ Quiet subscriptions stay connected without application-level heartbeat messages.
365
+ BRPC relies on Bun's native WebSocket ping/pong and transport timeout handling;
366
+ it does not close sockets merely because no application messages arrive.
367
+ The former `WS_TIMEOUT` environment variable is no longer used. Normal socket
368
+ closure still removes the connection and its topic subscriptions.
369
+
370
+ Run `yarn workspace @mateosuarezdev/brpc test tests/subscriptions.test.ts`
371
+ from the monorepo root for real-socket lifecycle coverage, including a 65-second
372
+ quiet production subscription followed by publish delivery and normal closure.
373
+
333
374
  ```ts
334
375
  const messages = procedure
335
376
  .input(z.object({ roomId: z.string(), text: z.string() }))
@@ -1526,7 +1567,6 @@ type UserOutput = AppOutput["users"]["getById"];
1526
1567
  | Variable | Default | Description |
1527
1568
  | -------------------- | ---------- | -------------------------------------- |
1528
1569
  | `MAX_WS_CONNECTIONS` | `1000` | Max simultaneous WebSocket connections |
1529
- | `WS_TIMEOUT` | `30000` | WebSocket idle timeout in ms |
1530
1570
  | `MAX_REQUEST_SIZE` | `10485760` | Max request body size in bytes (10MB) |
1531
1571
  | `REQUEST_TIMEOUT` | `30000` | Default procedure timeout in ms |
1532
1572
  | `ROUTE_CACHE_SIZE` | `1000` | Route matcher cache size |
@@ -1,7 +1,7 @@
1
1
  // @bun
2
- import*as w from"jsonwebtoken";import{RecordId as W,DateTime as a,Table as T,eq as B,or as c,surql as _}from"surrealdb";import*as A from"bcryptjs";import n from"node-cron";import i from"crypto";class J extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=J.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,J)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,K,j){return new J({code:"BAD_REQUEST",message:$,clientCode:K,data:j})}static unauthorized($="Unauthorized",K,j){return new J({code:"UNAUTHORIZED",message:$,clientCode:K,data:j})}static forbidden($="Forbidden",K,j){return new J({code:"FORBIDDEN",message:$,clientCode:K,data:j})}static notFound($="Not Found",K,j){return new J({code:"NOT_FOUND",message:$,clientCode:K,data:j})}static preconditionFailed($="Precondition failed",K,j){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
2
+ import*as E from"jsonwebtoken";import{RecordId as l,DateTime as V,Table as b,eq as v,or as Y,surql as h}from"surrealdb";import*as g from"bcryptjs";import K from"node-cron";import J from"crypto";class n extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor(e){super(e.message);if(this.name="BRPCError",this.code=e.code,this.clientCode=e.clientCode,this.httpStatus=n.STATUS_MAP[e.code],this.data=e.data,this.cause=e.cause,Error.captureStackTrace)Error.captureStackTrace(this,n)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest(e,t,a){return new n({code:"BAD_REQUEST",message:e,clientCode:t,data:a})}static unauthorized(e="Unauthorized",t,a){return new n({code:"UNAUTHORIZED",message:e,clientCode:t,data:a})}static forbidden(e="Forbidden",t,a){return new n({code:"FORBIDDEN",message:e,clientCode:t,data:a})}static notFound(e="Not Found",t,a){return new n({code:"NOT_FOUND",message:e,clientCode:t,data:a})}static preconditionFailed(e="Precondition failed",t,a){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
3
3
  //! but the exact cause is unknown. Suspected: some client/proxy/middleware misbehaved on 412
4
4
  //! (not a browser auto-retry issue — 412 doesn't trigger those). Investigate before changing back.
5
- return new J({code:"NOT_FOUND",message:$,clientCode:K,data:j})}static conflict($,K,j){return new J({code:"CONFLICT",message:$,clientCode:K,data:j})}static unprocessableContent($,K,j){return new J({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:K,data:j})}static tooManyRequests($="Too many requests",K,j){return new J({code:"TOO_MANY_REQUESTS",message:$,clientCode:K,data:j})}static internalServerError($="Internal Server Error",K,j){return new J({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:K,data:j})}static timeout($="Request timeout",K,j){return new J({code:"TIMEOUT",message:$,clientCode:K,data:j})}}var O={ACCESS_TOKEN:"access_token",REFRESH_TOKEN:"refresh_token",SESSION_ID:"session_id"},g={httpOnly:!0,sameSite:"Lax",path:"/",maxAge:1209600};function q($){return Object.entries($).map(([K,j])=>{if(j===void 0||j===null)return"";switch(K){case"maxAge":return`Max-Age=${j}`;case"httpOnly":return j?"HttpOnly":"";case"secure":return j?"Secure":"";case"sameSite":return`SameSite=${j}`;case"domain":return`Domain=${j}`;case"path":return`Path=${j}`;default:return`${K}=${j}`}}).filter(Boolean).join("; ")}function e($,K,j){let F={...g,secure:j};[`${O.ACCESS_TOKEN}=${K.accessToken}; ${q(F)}`,`${O.REFRESH_TOKEN}=${K.refreshToken}; ${q(F)}`,`${O.SESSION_ID}=${K.sessionId}; ${q({...F,maxAge:86400})}`].forEach((G)=>$.append("Set-Cookie",G))}function $$($,K){let j={...g,secure:K,maxAge:0};[O.ACCESS_TOKEN,O.REFRESH_TOKEN,O.SESSION_ID].forEach((F)=>$.append("Set-Cookie",`${F}=; ${q(j)}`))}function P($){if(!$)return{};return $.split(";").reduce((K,j)=>{let F=j.trim().split("="),G=F[0],Q=F.slice(1).join("=");if(G&&Q)K[G]=Q;return K},{})}var t=10,d=3,S=15,s=3,o=["cf-connecting-ip","x-real-ip","x-client-ip","x-forwarded","forwarded-for","x-forwarded-host"],H=new T("users"),U=new T("user_identities"),b=new T("sessions"),m=new T("otp_codes");function L($){return`${$.table}:${$.id}`}function N($=new Date){return new a($)}function y($){return $?.deleted_at!==void 0}function v($){let K=$?.headers.get("user-agent")?.toLowerCase();if(!K)return"desktop";if(K.includes("android"))return"android";if(/iphone|ipad|ipod/.test(K))return"apple";if(/windows|macintosh|linux|cros/.test(K))return"desktop";return"other"}class E{db;defaultSignupRoles;constructor($,K){this.db=$;this.defaultSignupRoles=K}async findByEmail($){let[K]=await this.db.select(H).where(B("email",$)).limit(1);return y(K)?void 0:K}async findByPhone($){let[K]=await this.db.select(H).where(B("phone",$)).limit(1);return y(K)?void 0:K}async findByContact($){if(!$.email&&!$.phone)return;let K=[];if($.email)K.push(B("email",$.email));if($.phone)K.push(B("phone",$.phone));let[j]=await this.db.select(H).where(c(...K)).limit(1);return y(j)?void 0:j}async findById($){let[K,j]=$.split(":"),F=await this.db.select(new W(K,j));return y(F)?void 0:F??void 0}async create({email:$,phone:K,options:j,ctx:F=this.db}){let[G]=await F.create(H).content({email:$??null,phone:K??null,app_metadata:{...j?.appMetadata,roles:this.defaultSignupRoles,signup_device:v()},user_metadata:j?.userMetadata,last_sign_in_at:null});if(!G)throw J.internalServerError("Failed to create user","USER_CREATE_FAILED");return G}async updateAppMetadata($,K){let j=await this.findById($);if(!j)throw J.notFound("User not found","USER_NOT_FOUND");await this.db.update(j.id).merge({app_metadata:K})}async updateUserMetadata($,K){let j=await this.findById($);if(!j)throw J.notFound("User not found","USER_NOT_FOUND");await this.db.update(j.id).merge({user_metadata:K})}async updateLastSignIn($,K=this.db){let[j,F]=$.split(":");await K.update(new W(j,F)).merge({last_sign_in_at:N()})}}class h{db;constructor($){this.db=$}async findByProvider($,K){let[j]=await this.db.query(_`SELECT * FROM type::table(${U}) WHERE provider = ${$} AND provider_id = ${K} LIMIT 1`);return j?.[0]}async findByUserId($){let[K,j]=$.split(":"),F=new W(K,j),[G]=await this.db.query(_`SELECT * FROM type::table(${U}) WHERE user_id = ${F}`);return G??[]}async findPasswordIdentity($){let[K,j]=$.split(":"),F=new W(K,j),[G]=await this.db.query(_`SELECT * FROM type::table(${U}) WHERE user_id = ${F} AND identity_data.password_hash != NONE LIMIT 1`);return G?.[0]}async create({userId:$,provider:K,providerId:j,identityData:F={},ctx:G=this.db}){let[Q]=await G.create(U).content({user_id:$,provider:K,provider_id:j,identity_data:F});if(!Q)throw J.internalServerError("Failed to create identity","IDENTITY_CREATE_FAILED");return Q}async updateData($,K){await this.db.update($).merge({identity_data:K})}}class R{db;debug;constructor($,K){this.db=$;this.debug=K}async create({userId:$,req:K,ctx:j=this.db}){let F=this.getClientIP(K),G=K.headers.get("user-agent"),[Q,V]=$.split(":"),Y=new W(Q,V),[Z]=await j.create(b).content({user_id:Y,ip_address:F,user_agent:G,invalidated_at:null});if(!Z)throw J.internalServerError("Failed to create session","SESSION_CREATE_FAILED");return k(Z)}async get($){let[K,j]=$.split(":"),F=await this.db.select(new W(K,j));if(!F||F.invalidated_at)return;return k(F)}async getUserSessions($){let[K,j]=$.split(":"),F=new W(K,j),[G]=await this.db.query(_`SELECT * FROM type::table(${b}) WHERE user_id = ${F} AND invalidated_at = null`);return(G??[]).map((Q)=>({id:L(Q.id),ipAddress:Q.ip_address,userAgent:Q.user_agent,invalidatedAt:Q.invalidated_at}))}async invalidate($){let[K,j]=$.split(":");await this.db.update(new W(K,j)).merge({invalidated_at:N()})}async invalidateAllForUser($,K=this.db){let[j,F]=$.split(":"),G=new W(j,F);await K.query(_`UPDATE type::table(${b}) SET invalidated_at = time::now() WHERE user_id = ${G} AND invalidated_at = null`)}async invalidateAllExceptCurrent($,K){let[j,F]=$.split(":"),[G,Q]=K.split(":"),V=new W(j,F),Y=new W(G,Q);await this.db.query(_`UPDATE type::table(${b}) SET invalidated_at = time::now() WHERE user_id = ${V} AND id != ${Y} AND invalidated_at = null`)}async cleanup(){let[$]=await this.db.query(_`DELETE type::table(${b}) WHERE invalidated_at != NONE AND invalidated_at != null RETURN BEFORE`);return $?.length??0}getClientIP($){let K=$.headers.get("x-forwarded-for");if(K)return K.split(",")[0].trim();for(let j of o){let F=$.headers.get(j);if(F)return F.trim()}return this.debug?"127.0.0.1":null}}class p{db;otpProvider;constructor($,K){this.db=$;this.otpProvider=K}async checkRateLimit($){let K=new Date;K.setMinutes(K.getMinutes()-S);let j=[];if($.email)j.push(_`email = ${$.email}`);if($.phone)j.push(_`phone = ${$.phone}`);let F=j.length===1?j[0]:_`(${j[0]} OR ${j[1]})`,[G]=await this.db.query(_`SELECT * FROM type::table(${m}) WHERE (${F}) AND created_at >= ${N(K)}`);if((G?.length??0)>=s)throw J.tooManyRequests(`Too many OTP requests. Please wait ${S} minutes.`,"OTP_RATE_LIMIT")}async generate({user:$,type:K,signupOptions:j}){await this.checkRateLimit({email:$.email,phone:$.phone});let F=i.randomInt(1e5,999999).toString(),G=await A.hash(F,10),Q=new Date;Q.setMinutes(Q.getMinutes()+t);let V=$.id?(()=>{let[Z,X]=$.id.split(":");return new W(Z,X)})():null,[Y]=await this.db.create(m).content({user_id:V,email:$.email??null,phone:$.phone??null,code:F,hashed_code:G,type:K,signup_options:j??null,expires_at:N(Q),attempts:0,verified_at:null});if(!Y)throw J.internalServerError("Failed to create OTP record","OTP_CREATE_FAILED");try{await this.sendOTP({email:$.email,phone:$.phone,code:F})}catch{throw await this.db.delete(Y.id),J.internalServerError("Failed to send OTP","OTP_SEND_FAILED")}return{otpId:L(Y.id),expiresAt:Q.toISOString(),message:`OTP sent to ${$.email?"email":"phone"}`}}async verify($,K){let[j,F]=$.split(":"),G=await this.db.select(new W(j,F));if(!G)throw J.notFound("Invalid OTP","INVALID_OTP");if(new Date>new Date(G.expires_at))throw J.badRequest("OTP has expired","OTP_EXPIRED");if(G.verified_at)throw J.badRequest("OTP already used","OTP_ALREADY_USED");if(G.attempts>=d)throw J.badRequest("Too many failed attempts","OTP_MAX_ATTEMPTS");if(!await A.compare(K,G.hashed_code))throw await this.db.update(G.id).merge({attempts:G.attempts+1}),J.badRequest("Invalid OTP code","INVALID_OTP_CODE");return await this.db.update(G.id).merge({verified_at:N()}),G}async cleanup(){let $=N(),[K]=await this.db.query(_`DELETE type::table(${m}) WHERE expires_at < ${$} OR verified_at != NONE RETURN BEFORE`);return K?.length??0}async sendOTP({email:$,phone:K,code:j}){if($&&this.otpProvider?.sendEmailOTP)await this.otpProvider.sendEmailOTP({email:$,code:j});if(K&&this.otpProvider?.sendSMSOTP)await this.otpProvider.sendSMSOTP({phone:K,code:j})}}class I{jwtSecret;jwtRefreshSecret;accessTokenExpiry;constructor($,K,j){this.jwtSecret=$;this.jwtRefreshSecret=K;this.accessTokenExpiry=j}generateAccessToken($){return w.sign($,this.jwtSecret,{expiresIn:this.accessTokenExpiry})}generateRefreshToken($,K){return w.sign({sub:$,sessionId:K},this.jwtRefreshSecret)}verifyAccessToken($){return w.verify($,this.jwtSecret)}verifyRefreshToken($){return w.verify($,this.jwtRefreshSecret)}}function x($){return{id:L($.id),email:$.email,phone:$.phone,appMetadata:$.app_metadata,userMetadata:$.user_metadata}}function k($){return{id:L($.id),userId:L($.user_id),ipAddress:$.ip_address,userAgent:$.user_agent,invalidatedAt:$.invalidated_at}}class u{userManager;identityManager;sessionManager;otpManager;tokenManager;cache;config;constructor($){this.config=$,this.userManager=new E($.db,$.defaultSignupRoles),this.identityManager=new h($.db),this.sessionManager=new R($.db,$.debug??!1),this.otpManager=new p($.db,$.otpProvider),this.tokenManager=new I($.secrets.jwt,$.secrets.jwtRefresh,$.accessTokenExpiry??"24h"),this.cache=$.cache?new $.cache({ttl:900000,maxSize:500,cleanupInterval:300000}):null}async initialize(){if(!this.config.disableCleanupCron)this.initializeCleanup()}async signUp($,K){this.validateSignUpMethod($);let{provider:j,providerId:F}=this.extractIdentityKey($);if(await this.identityManager.findByProvider(j,F))throw J.conflict("User already exists","USER_ALREADY_EXISTS");let Q=this.extractContact($);if($.passwordless||!$.password)return this.startPasswordlessSignUp(Q,$.options);return this.completePasswordSignUp(j,F,Q,$.password,K,$.options)}async signIn($,K){this.validateSignInMethod($);let{provider:j,providerId:F}=this.extractIdentityKey($),G=await this.identityManager.findByProvider(j,F);if(!G)throw J.notFound(`No account found with this ${$.type}`,"USER_NOT_FOUND");if($.passwordless||!$.password)return this.startPasswordlessSignIn(G);return this.completePasswordSignIn(G,$.password,K)}async verifyOTP($,K,j){let F=await this.otpManager.verify($,K);if(F.type==="signup")return this.completePasswordlessSignUp(F,j);if(F.type==="signin")return this.completePasswordlessSignIn(F,j);throw J.badRequest("Invalid OTP type","INVALID_OTP_TYPE")}async signOut($){await this.sessionManager.invalidate($)}async signOutAll($){await this.sessionManager.invalidateAllForUser($)}async signOutAllExcept($,K){await this.sessionManager.invalidateAllExceptCurrent($,K)}async getUserSessions($){if(!await this.userManager.findById($))throw J.notFound("User not found","USER_NOT_FOUND");return this.sessionManager.getUserSessions($)}async refreshTokens($){let K=this.tokenManager.verifyRefreshToken($),j=await this.sessionManager.get(K.sessionId);if(!j)throw J.notFound("Session not found","SESSION_NOT_FOUND");if(j.userId!==K.sub)throw J.conflict("Session mismatch","SESSION_USER_MISMATCH");let F=await this.userManager.findById(K.sub);if(!F)throw J.notFound("User not found","USER_NOT_FOUND");let G=this.tokenManager.generateAccessToken({sub:L(F.id),sessionId:j.id,appMetadata:F.app_metadata,userMetadata:F.user_metadata}),Q=this.tokenManager.generateRefreshToken(L(F.id),j.id);return{accessToken:G,refreshToken:Q,sessionId:j.id,user:x(F)}}async verifyToken($){try{if(!$||!$.startsWith("Bearer "))return null;let K=$.split(" ")[1],j=this.tokenManager.verifyAccessToken(K),F=await this.sessionManager.get(j.sessionId);if(!F||F.userId!==j.sub)return null;let G=await this.userManager.findById(j.sub);if(!G)return null;return{sub:j.sub,sessionId:j.sessionId,appMetadata:G.app_metadata,userMetadata:G.user_metadata,exp:j.exp}}catch{return null}}async fastVerifyToken($){try{if(!$||!$.startsWith("Bearer "))return null;let K=$.split(" ")[1];return this.tokenManager.verifyAccessToken(K)}catch{return null}}async verifyTokenFromCookie($){if(!$)return null;let j=P($)[O.ACCESS_TOKEN];if(!j)return null;return this.verifyToken(`Bearer ${j}`)}async refreshTokenFromCookie($){if(!$)return null;let j=P($)[O.REFRESH_TOKEN];if(!j)return null;try{return await this.refreshTokens(j)}catch{return null}}async getUserById($){return this.userManager.findById($)}async getUserByContact($){return this.userManager.findByContact($)}async getUserIdentities($){if(!await this.userManager.findById($))throw J.notFound("User not found","USER_NOT_FOUND");return this.identityManager.findByUserId($)}async markUserDeleted($){let K=await this.userManager.findById($);if(!K)throw J.notFound("User not found","USER_NOT_FOUND");let j=await this.config.db.beginTransaction();try{await j.update(K.id).merge({deleted_at:N()}),await this.sessionManager.invalidateAllForUser($,j),await j.commit(),this.clearCache()}catch(F){throw await j.cancel(),F}}async updateUserPassword($,K){if(!await this.userManager.findById($))throw J.notFound("User not found","USER_NOT_FOUND");let F=await this.identityManager.findPasswordIdentity($);if(!F)throw J.notFound("No password identity found","NO_PASSWORD_SET");let G=await A.hash(K,10);await this.identityManager.updateData(F.id,{password_hash:G})}async updateAppMetadata($,K){return this.userManager.updateAppMetadata($,K)}async updateUserMetadata($,K){return this.userManager.updateUserMetadata($,K)}async changePassword($,K,j){if(!await this.userManager.findById($))throw J.notFound("User not found","USER_NOT_FOUND");let G=await this.identityManager.findPasswordIdentity($);if(!G)throw J.badRequest("User has no password set","NO_PASSWORD_SET");if(!await A.compare(K,G.identity_data.password_hash))throw J.badRequest("Current password is incorrect","INVALID_PASSWORD");let V=await A.hash(j,10);await this.identityManager.updateData(G.id,{password_hash:V})}async requestPasswordReset($){let K=$.email?"email":"phone",j=$.email??$.phone,F=await this.identityManager.findByProvider(K,j);if(!F)throw J.notFound("User not found","USER_NOT_FOUND");let G=L(F.user_id);if(!await this.userManager.findById(G))throw J.notFound("User not found","USER_NOT_FOUND");return this.otpManager.generate({user:{id:G,email:$.email,phone:$.phone},type:"password_reset"})}async resetPassword($,K,j){let F=await this.otpManager.verify($,K);if(F.type!=="password_reset")throw J.badRequest("Invalid OTP type for password reset","INVALID_OTP_TYPE");if(!F.user_id)throw J.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let G=F.email?"email":"phone",Q=F.email??F.phone,V=await this.identityManager.findByProvider(G,Q);if(!V)throw J.internalServerError("Identity not found","IDENTITY_NOT_FOUND");let Y=L(F.user_id);if(!await this.userManager.findById(Y))throw J.notFound("User not found","USER_NOT_FOUND");let X=await A.hash(j,10);return await this.identityManager.updateData(V.id,{password_hash:X}),await this.sessionManager.invalidateAllForUser(Y),{success:!0}}async adminCreateUser({email:$,phone:K,password:j,options:F}){let G=j?await A.hash(j,10):null,Q=await this.config.db.beginTransaction();try{let[V]=await Q.create(H).content({email:$??null,phone:K??null,app_metadata:{...F?.appMetadata,roles:F?.appMetadata?.roles??this.config.defaultSignupRoles,signup_device:v()},user_metadata:F?.userMetadata,last_sign_in_at:null});if(!V)throw Error("Failed to create user");if($||K){let Y=$?"email":"phone",Z=$??K;await Q.create(U).content({user_id:V.id,provider:Y,provider_id:Z,identity_data:G?{password_hash:G}:{}})}return await Q.commit(),x(V)}catch(V){throw await Q.cancel(),V}}cacheSession($,K){this.cache?.set($,K)}getCachedSession($){return this.cache?.get($)??null}invalidateCachedSession($){return this.cache?.delete($)??!1}getCacheStats(){return this.cache?.stats()??null}clearCache(){this.cache?.clear()}isTokenNearExpiry($){return $-Math.floor(Date.now()/1000)<300}async testCreateUserWithSession({email:$,phone:K,password:j,options:F,req:G}){let Q=j?await A.hash(j,10):null,V=this.sessionManager.getClientIP(G),Y=await this.config.db.beginTransaction();try{let[Z]=await Y.create(H).content({email:$??null,phone:K??null,app_metadata:{...F?.appMetadata,roles:this.config.defaultSignupRoles,signup_device:v(G)},user_metadata:F?.userMetadata,last_sign_in_at:N()});if(!Z)throw Error("Failed to create user");if($||K){let l=$?"email":"phone",C=$??K;await Y.create(U).content({user_id:Z.id,provider:l,provider_id:C,identity_data:Q?{password_hash:Q}:{}})}let[X]=await Y.create(b).content({user_id:Z.id,ip_address:V,user_agent:G.headers.get("user-agent"),invalidated_at:null});if(!X)throw Error("Failed to create session");let D=L(Z.id),z=L(X.id),M=this.tokenManager.generateAccessToken({sub:D,sessionId:z,appMetadata:Z.app_metadata,userMetadata:Z.user_metadata}),f=this.tokenManager.generateRefreshToken(D,z);return await Y.commit(),{user:x(Z),session:k(X),accessToken:M,refreshToken:f}}catch(Z){throw await Y.cancel(),Z}}async startPasswordlessSignUp($,K){return this.otpManager.generate({user:{id:void 0,email:$.email,phone:$.phone},type:"signup",signupOptions:K})}async startPasswordlessSignIn($){let K=L($.user_id);if(!await this.userManager.findById(K))throw J.notFound("User not found","USER_NOT_FOUND");let F=$.provider==="email"?$.provider_id:void 0,G=$.provider==="phone"?$.provider_id:void 0;return this.otpManager.generate({user:{id:K,email:F,phone:G},type:"signin"})}async completePasswordSignUp($,K,j,F,G,Q){let V=await A.hash(F,10),Y=await this.config.db.beginTransaction();try{let[Z]=await Y.create(H).content({email:j.email??null,phone:j.phone??null,app_metadata:{...Q?.appMetadata,roles:this.config.defaultSignupRoles,signup_device:v(G)},user_metadata:Q?.userMetadata,last_sign_in_at:N()});if(!Z)throw Error("Failed to create user");let[X]=await Y.create(U).content({user_id:Z.id,provider:$,provider_id:K,identity_data:{password_hash:V}});if(!X)throw Error("Failed to create identity");let D=this.sessionManager.getClientIP(G),[z]=await Y.create(b).content({user_id:Z.id,ip_address:D,user_agent:G.headers.get("user-agent"),invalidated_at:null});if(!z)throw Error("Failed to create session");let M=L(Z.id),f=L(z.id),l=this.tokenManager.generateAccessToken({sub:M,sessionId:f,appMetadata:Z.app_metadata,userMetadata:Z.user_metadata}),C=this.tokenManager.generateRefreshToken(M,f);return await Y.commit(),{user:x(Z),session:k(z),accessToken:l,refreshToken:C}}catch(Z){throw await Y.cancel(),Z}}async completePasswordSignIn($,K,j){let{password_hash:F}=$.identity_data;if(!F)throw J.conflict("This account has no password. Use OTP to sign in.","NO_PASSWORD_SET");if(!await A.compare(K,F))throw J.conflict("Invalid credentials","INVALID_CREDENTIALS");let Q=L($.user_id),V=await this.userManager.findById(Q);if(!V)throw J.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(V,j)}async completePasswordlessSignUp($,K){let j=$.email?"email":"phone",F=$.email??$.phone,G=$.signup_options,Q=await this.config.db.beginTransaction();try{let[V]=await Q.create(H).content({email:$.email??null,phone:$.phone??null,app_metadata:{...G?.appMetadata,roles:this.config.defaultSignupRoles,signup_device:v(K)},user_metadata:G?.userMetadata,last_sign_in_at:N()});if(!V)throw Error("Failed to create user");let[Y]=await Q.create(U).content({user_id:V.id,provider:j,provider_id:F,identity_data:{}});if(!Y)throw Error("Failed to create identity");let Z=this.sessionManager.getClientIP(K),[X]=await Q.create(b).content({user_id:V.id,ip_address:Z,user_agent:K.headers.get("user-agent"),invalidated_at:null});if(!X)throw Error("Failed to create session");let D=L(V.id),z=L(X.id),M=this.tokenManager.generateAccessToken({sub:D,sessionId:z,appMetadata:V.app_metadata,userMetadata:V.user_metadata}),f=this.tokenManager.generateRefreshToken(D,z);return await Q.commit(),{user:x(V),session:k(X),accessToken:M,refreshToken:f}}catch(V){throw await Q.cancel(),V}}async completePasswordlessSignIn($,K){if(!$.user_id)throw J.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let j=L($.user_id),F=await this.userManager.findById(j);if(!F)throw J.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(F,K)}async createAuthSession($,K){if(y($))throw J.notFound("User not found","USER_NOT_FOUND");let j=await this.config.db.beginTransaction();try{let F=this.sessionManager.getClientIP(K),[G]=await j.create(b).content({user_id:$.id,ip_address:F,user_agent:K.headers.get("user-agent"),invalidated_at:null});if(!G)throw Error("Failed to create session");await j.update($.id).merge({last_sign_in_at:N()});let Q=L($.id),V=L(G.id),Y=this.tokenManager.generateAccessToken({sub:Q,sessionId:V,appMetadata:$.app_metadata,userMetadata:$.user_metadata}),Z=this.tokenManager.generateRefreshToken(Q,V);return await j.commit(),{user:x($),session:k(G),accessToken:Y,refreshToken:Z}}catch(F){throw await j.cancel(),F}}validateSignUpMethod($){if($.type==="email")this.validateEmail($.email);else $.phone=this.normalizePhoneNumber($.phone)}validateSignInMethod($){if($.type==="email")this.validateEmail($.email);else $.phone=this.normalizePhoneNumber($.phone)}validateEmail($){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test($))throw J.badRequest("Invalid email format","INVALID_EMAIL")}normalizePhoneNumber($){let K=$.replace(/\D/g,"");if(K.length===10)return`+1${K}`;if(K.length===11&&K.startsWith("1"))return`+${K}`;if(!K.startsWith("+"))return`+${K}`;return K}extractContact($){return $.type==="email"?{email:$.email}:{phone:$.phone}}extractIdentityKey($){return $.type==="email"?{provider:"email",providerId:$.email}:{provider:"phone",providerId:$.phone}}initializeCleanup(){Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{}),n.schedule("0 2 * * *",async()=>{await Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{})})}}function Z$($){return new u($)}export{e as setAuthCookies,P as parseCookies,Z$ as createAuth,$$ as clearAuthCookies,O as COOKIE_NAMES,u as AuthService};
5
+ return new n({code:"NOT_FOUND",message:e,clientCode:t,data:a})}static conflict(e,t,a){return new n({code:"CONFLICT",message:e,clientCode:t,data:a})}static unprocessableContent(e,t,a){return new n({code:"UNPROCESSABLE_CONTENT",message:e,clientCode:t,data:a})}static tooManyRequests(e="Too many requests",t,a){return new n({code:"TOO_MANY_REQUESTS",message:e,clientCode:t,data:a})}static internalServerError(e="Internal Server Error",t,a){return new n({code:"INTERNAL_SERVER_ERROR",message:e,clientCode:t,data:a})}static timeout(e="Request timeout",t,a){return new n({code:"TIMEOUT",message:e,clientCode:t,data:a})}}var _={ACCESS_TOKEN:"access_token",REFRESH_TOKEN:"refresh_token",SESSION_ID:"session_id"},k={httpOnly:!0,sameSite:"Lax",path:"/",maxAge:1209600};function P(e){return Object.entries(e).map(([t,a])=>{if(a===void 0||a===null)return"";switch(t){case"maxAge":return`Max-Age=${a}`;case"httpOnly":return a?"HttpOnly":"";case"secure":return a?"Secure":"";case"sameSite":return`SameSite=${a}`;case"domain":return`Domain=${a}`;case"path":return`Path=${a}`;default:return`${t}=${a}`}}).filter(Boolean).join("; ")}function ae(e,t,a){let s={...k,secure:a};[`${_.ACCESS_TOKEN}=${t.accessToken}; ${P(s)}`,`${_.REFRESH_TOKEN}=${t.refreshToken}; ${P(s)}`,`${_.SESSION_ID}=${t.sessionId}; ${P({...s,maxAge:86400})}`].forEach((r)=>e.append("Set-Cookie",r))}function se(e,t){let a={...k,secure:t,maxAge:0};[_.ACCESS_TOKEN,_.REFRESH_TOKEN,_.SESSION_ID].forEach((s)=>e.append("Set-Cookie",`${s}=; ${P(a)}`))}function x(e){if(!e)return{};return e.split(";").reduce((t,a)=>{let s=a.trim().split("="),r=s[0],i=s.slice(1).join("=");if(r&&i)t[r]=i;return t},{})}var Q=10,z=3,F=15,G=3,X=["cf-connecting-ip","x-real-ip","x-client-ip","x-forwarded","forwarded-for","x-forwarded-host"],y=new b("users"),U=new b("user_identities"),w=new b("sessions"),C=new b("otp_codes");function c(e){return`${e.table}:${e.id}`}function T(e=new Date){return new V(e)}function I(e){return e?.deleted_at!==void 0}function O(e){let t=e?.headers.get("user-agent")?.toLowerCase();if(!t)return"desktop";if(t.includes("android"))return"android";if(/iphone|ipad|ipod/.test(t))return"apple";if(/windows|macintosh|linux|cros/.test(t))return"desktop";return"other"}class L{db;defaultSignupRoles;constructor(e,t){this.db=e;this.defaultSignupRoles=t}async findByEmail(e){let[t]=await this.db.select(y).where(v("email",e)).limit(1);return I(t)?void 0:t}async findByPhone(e){let[t]=await this.db.select(y).where(v("phone",e)).limit(1);return I(t)?void 0:t}async findByContact(e){if(!e.email&&!e.phone)return;let t=[];if(e.email)t.push(v("email",e.email));if(e.phone)t.push(v("phone",e.phone));let[a]=await this.db.select(y).where(Y(...t)).limit(1);return I(a)?void 0:a}async findById(e){let[t,a]=e.split(":"),s=await this.db.select(new l(t,a));return I(s)?void 0:s??void 0}async create({email:e,phone:t,options:a,ctx:s=this.db}){let[r]=await s.create(y).content({email:e??null,phone:t??null,app_metadata:{...a?.appMetadata,roles:this.defaultSignupRoles,signup_device:O()},user_metadata:a?.userMetadata,last_sign_in_at:null});if(!r)throw n.internalServerError("Failed to create user","USER_CREATE_FAILED");return r}async updateAppMetadata(e,t){let a=await this.findById(e);if(!a)throw n.notFound("User not found","USER_NOT_FOUND");await this.db.update(a.id).merge({app_metadata:t})}async updateUserMetadata(e,t){let a=await this.findById(e);if(!a)throw n.notFound("User not found","USER_NOT_FOUND");await this.db.update(a.id).merge({user_metadata:t})}async updateLastSignIn(e,t=this.db){let[a,s]=e.split(":");await t.update(new l(a,s)).merge({last_sign_in_at:T()})}}class B{db;constructor(e){this.db=e}async findByProvider(e,t){let[a]=await this.db.query(h`SELECT * FROM type::table(${U}) WHERE provider = ${e} AND provider_id = ${t} LIMIT 1`);return a?.[0]}async findByUserId(e){let[t,a]=e.split(":"),s=new l(t,a),[r]=await this.db.query(h`SELECT * FROM type::table(${U}) WHERE user_id = ${s}`);return r??[]}async findPasswordIdentity(e){let[t,a]=e.split(":"),s=new l(t,a),[r]=await this.db.query(h`SELECT * FROM type::table(${U}) WHERE user_id = ${s} AND identity_data.password_hash != NONE LIMIT 1`);return r?.[0]}async create({userId:e,provider:t,providerId:a,identityData:s={},ctx:r=this.db}){let[i]=await r.create(U).content({user_id:e,provider:t,provider_id:a,identity_data:s});if(!i)throw n.internalServerError("Failed to create identity","IDENTITY_CREATE_FAILED");return i}async updateData(e,t){await this.db.update(e).merge({identity_data:t})}}class W{db;debug;constructor(e,t){this.db=e;this.debug=t}async create({userId:e,req:t,ctx:a=this.db}){let s=this.getClientIP(t),r=t.headers.get("user-agent"),[i,o]=e.split(":"),p=new l(i,o),[d]=await a.create(w).content({user_id:p,ip_address:s,user_agent:r,invalidated_at:null});if(!d)throw n.internalServerError("Failed to create session","SESSION_CREATE_FAILED");return m(d)}async get(e){let[t,a]=e.split(":"),s=await this.db.select(new l(t,a));if(!s||s.invalidated_at)return;return m(s)}async getUserSessions(e){let[t,a]=e.split(":"),s=new l(t,a),[r]=await this.db.query(h`SELECT * FROM type::table(${w}) WHERE user_id = ${s} AND invalidated_at = null`);return(r??[]).map((i)=>({id:c(i.id),ipAddress:i.ip_address,userAgent:i.user_agent,invalidatedAt:i.invalidated_at}))}async invalidate(e){let[t,a]=e.split(":");await this.db.update(new l(t,a)).merge({invalidated_at:T()})}async invalidateAllForUser(e,t=this.db){let[a,s]=e.split(":"),r=new l(a,s);await t.query(h`UPDATE type::table(${w}) SET invalidated_at = time::now() WHERE user_id = ${r} AND invalidated_at = null`)}async invalidateAllExceptCurrent(e,t){let[a,s]=e.split(":"),[r,i]=t.split(":"),o=new l(a,s),p=new l(r,i);await this.db.query(h`UPDATE type::table(${w}) SET invalidated_at = time::now() WHERE user_id = ${o} AND id != ${p} AND invalidated_at = null`)}async cleanup(){let[e]=await this.db.query(h`DELETE type::table(${w}) WHERE invalidated_at != NONE AND invalidated_at != null RETURN BEFORE`);return e?.length??0}getClientIP(e){let t=e.headers.get("x-forwarded-for");if(t)return t.split(",")[0].trim();for(let a of X){let s=e.headers.get(a);if(s)return s.trim()}return this.debug?"127.0.0.1":null}}class q{db;otpProvider;constructor(e,t){this.db=e;this.otpProvider=t}async checkRateLimit(e){let t=new Date;t.setMinutes(t.getMinutes()-F);let a=[];if(e.email)a.push(h`email = ${e.email}`);if(e.phone)a.push(h`phone = ${e.phone}`);let s=a.length===1?a[0]:h`(${a[0]} OR ${a[1]})`,[r]=await this.db.query(h`SELECT * FROM type::table(${C}) WHERE (${s}) AND created_at >= ${T(t)}`);if((r?.length??0)>=G)throw n.tooManyRequests(`Too many OTP requests. Please wait ${F} minutes.`,"OTP_RATE_LIMIT")}async generate({user:e,type:t,signupOptions:a}){await this.checkRateLimit({email:e.email,phone:e.phone});let s=J.randomInt(1e5,999999).toString(),r=await g.hash(s,10),i=new Date;i.setMinutes(i.getMinutes()+Q);let o=e.id?(()=>{let[d,u]=e.id.split(":");return new l(d,u)})():null,[p]=await this.db.create(C).content({user_id:o,email:e.email??null,phone:e.phone??null,code:s,hashed_code:r,type:t,signup_options:a??null,expires_at:T(i),attempts:0,verified_at:null});if(!p)throw n.internalServerError("Failed to create OTP record","OTP_CREATE_FAILED");try{await this.sendOTP({email:e.email,phone:e.phone,code:s})}catch{throw await this.db.delete(p.id),n.internalServerError("Failed to send OTP","OTP_SEND_FAILED")}return{otpId:c(p.id),expiresAt:i.toISOString(),message:`OTP sent to ${e.email?"email":"phone"}`}}async verify(e,t){let[a,s]=e.split(":"),r=await this.db.select(new l(a,s));if(!r)throw n.notFound("Invalid OTP","INVALID_OTP");if(new Date>new Date(r.expires_at))throw n.badRequest("OTP has expired","OTP_EXPIRED");if(r.verified_at)throw n.badRequest("OTP already used","OTP_ALREADY_USED");if(r.attempts>=z)throw n.badRequest("Too many failed attempts","OTP_MAX_ATTEMPTS");if(!await g.compare(t,r.hashed_code))throw await this.db.update(r.id).merge({attempts:r.attempts+1}),n.badRequest("Invalid OTP code","INVALID_OTP_CODE");return await this.db.update(r.id).merge({verified_at:T()}),r}async cleanup(){let e=T(),[t]=await this.db.query(h`DELETE type::table(${C}) WHERE expires_at < ${e} OR verified_at != NONE RETURN BEFORE`);return t?.length??0}async sendOTP({email:e,phone:t,code:a}){if(e&&this.otpProvider?.sendEmailOTP)await this.otpProvider.sendEmailOTP({email:e,code:a});if(t&&this.otpProvider?.sendSMSOTP)await this.otpProvider.sendSMSOTP({phone:t,code:a})}}class H{jwtSecret;jwtRefreshSecret;accessTokenExpiry;constructor(e,t,a){this.jwtSecret=e;this.jwtRefreshSecret=t;this.accessTokenExpiry=a}generateAccessToken(e){return E.sign(e,this.jwtSecret,{expiresIn:this.accessTokenExpiry})}generateRefreshToken(e,t){return E.sign({sub:e,sessionId:t},this.jwtRefreshSecret)}verifyAccessToken(e){return E.verify(e,this.jwtSecret)}verifyRefreshToken(e){return E.verify(e,this.jwtRefreshSecret)}}function A(e){return{id:c(e.id),email:e.email,phone:e.phone,appMetadata:e.app_metadata,userMetadata:e.user_metadata}}function m(e){return{id:c(e.id),userId:c(e.user_id),ipAddress:e.ip_address,userAgent:e.user_agent,invalidatedAt:e.invalidated_at}}class j{userManager;identityManager;sessionManager;otpManager;tokenManager;cache;config;constructor(e){this.config=e,this.userManager=new L(e.db,e.defaultSignupRoles),this.identityManager=new B(e.db),this.sessionManager=new W(e.db,e.debug??!1),this.otpManager=new q(e.db,e.otpProvider),this.tokenManager=new H(e.secrets.jwt,e.secrets.jwtRefresh,e.accessTokenExpiry??"24h"),this.cache=e.cache?new e.cache({ttl:900000,maxSize:500,cleanupInterval:300000}):null}async initialize(){if(!this.config.disableCleanupCron)this.initializeCleanup()}async signUp(e,t){this.validateSignUpMethod(e);let{provider:a,providerId:s}=this.extractIdentityKey(e);if(await this.identityManager.findByProvider(a,s))throw n.conflict("User already exists","USER_ALREADY_EXISTS");let i=this.extractContact(e);if(e.passwordless||!e.password)return this.startPasswordlessSignUp(i,e.options);return this.completePasswordSignUp(a,s,i,e.password,t,e.options)}async signIn(e,t){this.validateSignInMethod(e);let{provider:a,providerId:s}=this.extractIdentityKey(e),r=await this.identityManager.findByProvider(a,s);if(!r)throw n.notFound(`No account found with this ${e.type}`,"USER_NOT_FOUND");if(e.passwordless||!e.password)return this.startPasswordlessSignIn(r);return this.completePasswordSignIn(r,e.password,t)}async verifyOTP(e,t,a){let s=await this.otpManager.verify(e,t);if(s.type==="signup")return this.completePasswordlessSignUp(s,a);if(s.type==="signin")return this.completePasswordlessSignIn(s,a);throw n.badRequest("Invalid OTP type","INVALID_OTP_TYPE")}async signOut(e){await this.sessionManager.invalidate(e)}async signOutAll(e){await this.sessionManager.invalidateAllForUser(e)}async signOutAllExcept(e,t){await this.sessionManager.invalidateAllExceptCurrent(e,t)}async getUserSessions(e){if(!await this.userManager.findById(e))throw n.notFound("User not found","USER_NOT_FOUND");return this.sessionManager.getUserSessions(e)}async refreshTokens(e){let t=this.tokenManager.verifyRefreshToken(e),a=await this.sessionManager.get(t.sessionId);if(!a)throw n.notFound("Session not found","SESSION_NOT_FOUND");if(a.userId!==t.sub)throw n.conflict("Session mismatch","SESSION_USER_MISMATCH");let s=await this.userManager.findById(t.sub);if(!s)throw n.notFound("User not found","USER_NOT_FOUND");let r=this.tokenManager.generateAccessToken({sub:c(s.id),sessionId:a.id,appMetadata:s.app_metadata,userMetadata:s.user_metadata}),i=this.tokenManager.generateRefreshToken(c(s.id),a.id);return{accessToken:r,refreshToken:i,sessionId:a.id,user:A(s)}}async verifyToken(e){try{if(!e||!e.startsWith("Bearer "))return null;let t=e.split(" ")[1],a=this.tokenManager.verifyAccessToken(t),s=await this.sessionManager.get(a.sessionId);if(!s||s.userId!==a.sub)return null;let r=await this.userManager.findById(a.sub);if(!r)return null;return{sub:a.sub,sessionId:a.sessionId,appMetadata:r.app_metadata,userMetadata:r.user_metadata,exp:a.exp}}catch{return null}}async fastVerifyToken(e){try{if(!e||!e.startsWith("Bearer "))return null;let t=e.split(" ")[1];return this.tokenManager.verifyAccessToken(t)}catch{return null}}async verifyTokenFromCookie(e){if(!e)return null;let a=x(e)[_.ACCESS_TOKEN];if(!a)return null;return this.verifyToken(`Bearer ${a}`)}async refreshTokenFromCookie(e){if(!e)return null;let a=x(e)[_.REFRESH_TOKEN];if(!a)return null;try{return await this.refreshTokens(a)}catch{return null}}async getUserById(e){return this.userManager.findById(e)}async getUserByContact(e){return this.userManager.findByContact(e)}async getUserIdentities(e){if(!await this.userManager.findById(e))throw n.notFound("User not found","USER_NOT_FOUND");return this.identityManager.findByUserId(e)}async markUserDeleted(e){let t=await this.userManager.findById(e);if(!t)throw n.notFound("User not found","USER_NOT_FOUND");let a=await this.config.db.beginTransaction();try{await a.update(t.id).merge({deleted_at:T()}),await this.sessionManager.invalidateAllForUser(e,a),await a.commit(),this.clearCache()}catch(s){throw await a.cancel(),s}}async updateUserPassword(e,t){if(!await this.userManager.findById(e))throw n.notFound("User not found","USER_NOT_FOUND");let s=await this.identityManager.findPasswordIdentity(e);if(!s)throw n.notFound("No password identity found","NO_PASSWORD_SET");let r=await g.hash(t,10);await this.identityManager.updateData(s.id,{password_hash:r})}async updateAppMetadata(e,t){return this.userManager.updateAppMetadata(e,t)}async updateUserMetadata(e,t){return this.userManager.updateUserMetadata(e,t)}async changePassword(e,t,a){if(!await this.userManager.findById(e))throw n.notFound("User not found","USER_NOT_FOUND");let r=await this.identityManager.findPasswordIdentity(e);if(!r)throw n.badRequest("User has no password set","NO_PASSWORD_SET");if(!await g.compare(t,r.identity_data.password_hash))throw n.badRequest("Current password is incorrect","INVALID_PASSWORD");let o=await g.hash(a,10);await this.identityManager.updateData(r.id,{password_hash:o})}async requestPasswordReset(e){let t=e.email?"email":"phone",a=e.email??e.phone,s=await this.identityManager.findByProvider(t,a);if(!s)throw n.notFound("User not found","USER_NOT_FOUND");let r=c(s.user_id);if(!await this.userManager.findById(r))throw n.notFound("User not found","USER_NOT_FOUND");return this.otpManager.generate({user:{id:r,email:e.email,phone:e.phone},type:"password_reset"})}async resetPassword(e,t,a){let s=await this.otpManager.verify(e,t);if(s.type!=="password_reset")throw n.badRequest("Invalid OTP type for password reset","INVALID_OTP_TYPE");if(!s.user_id)throw n.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let r=s.email?"email":"phone",i=s.email??s.phone,o=await this.identityManager.findByProvider(r,i);if(!o)throw n.internalServerError("Identity not found","IDENTITY_NOT_FOUND");let p=c(s.user_id);if(!await this.userManager.findById(p))throw n.notFound("User not found","USER_NOT_FOUND");let u=await g.hash(a,10);return await this.identityManager.updateData(o.id,{password_hash:u}),await this.sessionManager.invalidateAllForUser(p),{success:!0}}async adminCreateUser({email:e,phone:t,password:a,options:s}){let r=a?await g.hash(a,10):null,i=await this.config.db.beginTransaction();try{let[o]=await i.create(y).content({email:e??null,phone:t??null,app_metadata:{...s?.appMetadata,roles:s?.appMetadata?.roles??this.config.defaultSignupRoles,signup_device:O()},user_metadata:s?.userMetadata,last_sign_in_at:null});if(!o)throw Error("Failed to create user");if(e||t){let p=e?"email":"phone",d=e??t;await i.create(U).content({user_id:o.id,provider:p,provider_id:d,identity_data:r?{password_hash:r}:{}})}return await i.commit(),A(o)}catch(o){throw await i.cancel(),o}}cacheSession(e,t){this.cache?.set(e,t)}getCachedSession(e){return this.cache?.get(e)??null}invalidateCachedSession(e){return this.cache?.delete(e)??!1}getCacheStats(){return this.cache?.stats()??null}clearCache(){this.cache?.clear()}isTokenNearExpiry(e){return e-Math.floor(Date.now()/1000)<300}async testCreateUserWithSession({email:e,phone:t,password:a,options:s,req:r}){let i=a?await g.hash(a,10):null,o=this.sessionManager.getClientIP(r),p=await this.config.db.beginTransaction();try{let[d]=await p.create(y).content({email:e??null,phone:t??null,app_metadata:{...s?.appMetadata,roles:this.config.defaultSignupRoles,signup_device:O(r)},user_metadata:s?.userMetadata,last_sign_in_at:T()});if(!d)throw Error("Failed to create user");if(e||t){let N=e?"email":"phone",D=e??t;await p.create(U).content({user_id:d.id,provider:N,provider_id:D,identity_data:i?{password_hash:i}:{}})}let[u]=await p.create(w).content({user_id:d.id,ip_address:o,user_agent:r.headers.get("user-agent"),invalidated_at:null});if(!u)throw Error("Failed to create session");let R=c(d.id),M=c(u.id),S=this.tokenManager.generateAccessToken({sub:R,sessionId:M,appMetadata:d.app_metadata,userMetadata:d.user_metadata}),f=this.tokenManager.generateRefreshToken(R,M);return await p.commit(),{user:A(d),session:m(u),accessToken:S,refreshToken:f}}catch(d){throw await p.cancel(),d}}async startPasswordlessSignUp(e,t){return this.otpManager.generate({user:{id:void 0,email:e.email,phone:e.phone},type:"signup",signupOptions:t})}async startPasswordlessSignIn(e){let t=c(e.user_id);if(!await this.userManager.findById(t))throw n.notFound("User not found","USER_NOT_FOUND");let s=e.provider==="email"?e.provider_id:void 0,r=e.provider==="phone"?e.provider_id:void 0;return this.otpManager.generate({user:{id:t,email:s,phone:r},type:"signin"})}async completePasswordSignUp(e,t,a,s,r,i){let o=await g.hash(s,10),p=await this.config.db.beginTransaction();try{let[d]=await p.create(y).content({email:a.email??null,phone:a.phone??null,app_metadata:{...i?.appMetadata,roles:this.config.defaultSignupRoles,signup_device:O(r)},user_metadata:i?.userMetadata,last_sign_in_at:T()});if(!d)throw Error("Failed to create user");let[u]=await p.create(U).content({user_id:d.id,provider:e,provider_id:t,identity_data:{password_hash:o}});if(!u)throw Error("Failed to create identity");let R=this.sessionManager.getClientIP(r),[M]=await p.create(w).content({user_id:d.id,ip_address:R,user_agent:r.headers.get("user-agent"),invalidated_at:null});if(!M)throw Error("Failed to create session");let S=c(d.id),f=c(M.id),N=this.tokenManager.generateAccessToken({sub:S,sessionId:f,appMetadata:d.app_metadata,userMetadata:d.user_metadata}),D=this.tokenManager.generateRefreshToken(S,f);return await p.commit(),{user:A(d),session:m(M),accessToken:N,refreshToken:D}}catch(d){throw await p.cancel(),d}}async completePasswordSignIn(e,t,a){let{password_hash:s}=e.identity_data;if(!s)throw n.conflict("This account has no password. Use OTP to sign in.","NO_PASSWORD_SET");if(!await g.compare(t,s))throw n.conflict("Invalid credentials","INVALID_CREDENTIALS");let i=c(e.user_id),o=await this.userManager.findById(i);if(!o)throw n.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(o,a)}async completePasswordlessSignUp(e,t){let a=e.email?"email":"phone",s=e.email??e.phone,r=e.signup_options,i=await this.config.db.beginTransaction();try{let[o]=await i.create(y).content({email:e.email??null,phone:e.phone??null,app_metadata:{...r?.appMetadata,roles:this.config.defaultSignupRoles,signup_device:O(t)},user_metadata:r?.userMetadata,last_sign_in_at:T()});if(!o)throw Error("Failed to create user");let[p]=await i.create(U).content({user_id:o.id,provider:a,provider_id:s,identity_data:{}});if(!p)throw Error("Failed to create identity");let d=this.sessionManager.getClientIP(t),[u]=await i.create(w).content({user_id:o.id,ip_address:d,user_agent:t.headers.get("user-agent"),invalidated_at:null});if(!u)throw Error("Failed to create session");let R=c(o.id),M=c(u.id),S=this.tokenManager.generateAccessToken({sub:R,sessionId:M,appMetadata:o.app_metadata,userMetadata:o.user_metadata}),f=this.tokenManager.generateRefreshToken(R,M);return await i.commit(),{user:A(o),session:m(u),accessToken:S,refreshToken:f}}catch(o){throw await i.cancel(),o}}async completePasswordlessSignIn(e,t){if(!e.user_id)throw n.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let a=c(e.user_id),s=await this.userManager.findById(a);if(!s)throw n.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(s,t)}async createAuthSession(e,t){if(I(e))throw n.notFound("User not found","USER_NOT_FOUND");let a=await this.config.db.beginTransaction();try{let s=this.sessionManager.getClientIP(t),[r]=await a.create(w).content({user_id:e.id,ip_address:s,user_agent:t.headers.get("user-agent"),invalidated_at:null});if(!r)throw Error("Failed to create session");await a.update(e.id).merge({last_sign_in_at:T()});let i=c(e.id),o=c(r.id),p=this.tokenManager.generateAccessToken({sub:i,sessionId:o,appMetadata:e.app_metadata,userMetadata:e.user_metadata}),d=this.tokenManager.generateRefreshToken(i,o);return await a.commit(),{user:A(e),session:m(r),accessToken:p,refreshToken:d}}catch(s){throw await a.cancel(),s}}validateSignUpMethod(e){if(e.type==="email")this.validateEmail(e.email);else e.phone=this.normalizePhoneNumber(e.phone)}validateSignInMethod(e){if(e.type==="email")this.validateEmail(e.email);else e.phone=this.normalizePhoneNumber(e.phone)}validateEmail(e){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e))throw n.badRequest("Invalid email format","INVALID_EMAIL")}normalizePhoneNumber(e){let t=e.replace(/\D/g,"");if(t.length===10)return`+1${t}`;if(t.length===11&&t.startsWith("1"))return`+${t}`;if(!t.startsWith("+"))return`+${t}`;return t}extractContact(e){return e.type==="email"?{email:e.email}:{phone:e.phone}}extractIdentityKey(e){return e.type==="email"?{provider:"email",providerId:e.email}:{provider:"phone",providerId:e.phone}}initializeCleanup(){Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{}),K.schedule("0 2 * * *",async()=>{await Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{})})}}function ue(e){return new j(e)}export{j as AuthService,_ as COOKIE_NAMES,se as clearAuthCookies,ue as createAuth,x as parseCookies,ae as setAuthCookies};
6
6
 
7
- //# debugId=C556FDA52DC0269064756E2164756E21
7
+ //# debugId=0ECD3CA29F1AE43564756E2164756E21
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- class E{entries;accessOrder;maxSize;defaultTTL;cleanupTimer=null;accessCounter=0;constructor(b={}){if(this.entries=new Map,this.accessOrder=new Map,this.maxSize=b.maxSize??1/0,this.defaultTTL=b.ttl??null,b.cleanupInterval)this.cleanupTimer=setInterval(()=>this.cleanup(),b.cleanupInterval)}set(b,j,q){let z=q??this.defaultTTL,B=z?Date.now()+z:null;if(this.entries.size>=this.maxSize&&!this.entries.has(b))this.evictLRU();this.entries.set(b,{value:j,expiresAt:B,createdAt:Date.now()}),this.trackAccess(b)}get(b){let j=this.entries.get(b);if(!j)return null;if(j.expiresAt&&Date.now()>j.expiresAt)return this.entries.delete(b),this.accessOrder.delete(b),null;return this.trackAccess(b),j.value}has(b){return this.get(b)!==null}delete(b){return this.accessOrder.delete(b),this.entries.delete(b)}clear(){this.entries.clear(),this.accessOrder.clear(),this.accessCounter=0}keys(){return Array.from(this.entries.keys())}validKeys(){let b=Date.now();return Array.from(this.entries.entries()).filter(([j,q])=>!q.expiresAt||q.expiresAt>b).map(([j])=>j)}size(){return this.entries.size}async getOrSet(b,j,q){let z=this.get(b);if(z!==null)return z;let B=await j();if(B!==null&&B!==void 0)this.set(b,B,q);return B}setWithExpiry(b,j,q){let z=Math.max(0,q-Date.now());this.set(b,j,z)}ttl(b){let j=this.entries.get(b);if(!j)return null;if(!j.expiresAt)return-1;let q=j.expiresAt-Date.now();return q>0?q:0}expire(b,j){let q=this.entries.get(b);if(!q)return!1;return q.expiresAt=Date.now()+j,!0}persist(b){let j=this.entries.get(b);if(!j)return!1;return j.expiresAt=null,!0}deletePattern(b){let j=new RegExp("^"+b.replace(/\*/g,".*").replace(/\?/g,".")+"$"),q=0;for(let z of this.entries.keys())if(j.test(z))this.delete(z),q++;return q}mget(b){return b.map((j)=>this.get(j))}mset(b,j){for(let[q,z]of Object.entries(b))this.set(q,z,j)}incr(b,j=1){let q=this.get(b),z=(typeof q==="number"?q:0)+j;return this.set(b,z),z}decr(b,j=1){return this.incr(b,-j)}stats(){let b=Date.now(),j=Array.from(this.entries.entries()),q=j.filter(([B,D])=>D.expiresAt&&D.expiresAt<=b).length,z=j.length-q;return{size:this.entries.size,valid:z,expired:q,maxSize:this.maxSize,defaultTTL:this.defaultTTL,entries:j.map(([B,D])=>({key:B,expiresIn:D.expiresAt?Math.max(0,D.expiresAt-b):null,age:b-D.createdAt}))}}cleanup(){let b=Date.now(),j=0;for(let[q,z]of this.entries.entries())if(z.expiresAt&&z.expiresAt<=b)this.entries.delete(q),this.accessOrder.delete(q),j++;return j}trackAccess(b){this.accessOrder.set(b,++this.accessCounter)}evictLRU(){let b=null,j=1/0;for(let[q,z]of this.accessOrder.entries())if(z<j)j=z,b=q;if(b)this.entries.delete(b),this.accessOrder.delete(b)}destroy(){if(this.cleanupTimer)clearInterval(this.cleanupTimer),this.cleanupTimer=null;this.clear()}}export{E as CacheService};
2
+ class a{entries;accessOrder;maxSize;defaultTTL;cleanupTimer=null;accessCounter=0;constructor(e={}){if(this.entries=new Map,this.accessOrder=new Map,this.maxSize=e.maxSize??1/0,this.defaultTTL=e.ttl??null,e.cleanupInterval)this.cleanupTimer=setInterval(()=>this.cleanup(),e.cleanupInterval)}set(e,t,r){let s=r??this.defaultTTL,n=s?Date.now()+s:null;if(this.entries.size>=this.maxSize&&!this.entries.has(e))this.evictLRU();this.entries.set(e,{value:t,expiresAt:n,createdAt:Date.now()}),this.trackAccess(e)}get(e){let t=this.entries.get(e);if(!t)return null;if(t.expiresAt&&Date.now()>t.expiresAt)return this.entries.delete(e),this.accessOrder.delete(e),null;return this.trackAccess(e),t.value}has(e){return this.get(e)!==null}delete(e){return this.accessOrder.delete(e),this.entries.delete(e)}clear(){this.entries.clear(),this.accessOrder.clear(),this.accessCounter=0}keys(){return Array.from(this.entries.keys())}validKeys(){let e=Date.now();return Array.from(this.entries.entries()).filter(([t,r])=>!r.expiresAt||r.expiresAt>e).map(([t])=>t)}size(){return this.entries.size}async getOrSet(e,t,r){let s=this.get(e);if(s!==null)return s;let n=await t();if(n!==null&&n!==void 0)this.set(e,n,r);return n}setWithExpiry(e,t,r){let s=Math.max(0,r-Date.now());this.set(e,t,s)}ttl(e){let t=this.entries.get(e);if(!t)return null;if(!t.expiresAt)return-1;let r=t.expiresAt-Date.now();return r>0?r:0}expire(e,t){let r=this.entries.get(e);if(!r)return!1;return r.expiresAt=Date.now()+t,!0}persist(e){let t=this.entries.get(e);if(!t)return!1;return t.expiresAt=null,!0}deletePattern(e){let t=new RegExp("^"+e.replace(/\*/g,".*").replace(/\?/g,".")+"$"),r=0;for(let s of this.entries.keys())if(t.test(s))this.delete(s),r++;return r}mget(e){return e.map((t)=>this.get(t))}mset(e,t){for(let[r,s]of Object.entries(e))this.set(r,s,t)}incr(e,t=1){let r=this.get(e),s=(typeof r==="number"?r:0)+t;return this.set(e,s),s}decr(e,t=1){return this.incr(e,-t)}stats(){let e=Date.now(),t=Array.from(this.entries.entries()),r=t.filter(([n,i])=>i.expiresAt&&i.expiresAt<=e).length,s=t.length-r;return{size:this.entries.size,valid:s,expired:r,maxSize:this.maxSize,defaultTTL:this.defaultTTL,entries:t.map(([n,i])=>({key:n,expiresIn:i.expiresAt?Math.max(0,i.expiresAt-e):null,age:e-i.createdAt}))}}cleanup(){let e=Date.now(),t=0;for(let[r,s]of this.entries.entries())if(s.expiresAt&&s.expiresAt<=e)this.entries.delete(r),this.accessOrder.delete(r),t++;return t}trackAccess(e){this.accessOrder.set(e,++this.accessCounter)}evictLRU(){let e=null,t=1/0;for(let[r,s]of this.accessOrder.entries())if(s<t)t=s,e=r;if(e)this.entries.delete(e),this.accessOrder.delete(e)}destroy(){if(this.cleanupTimer)clearInterval(this.cleanupTimer),this.cleanupTimer=null;this.clear()}}export{a as CacheService};
3
3
 
4
- //# debugId=1C20A6C795E38F5964756E2164756E21
4
+ //# debugId=22CCA7998B768ABA64756E2164756E21
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- function u(q){return(J)=>{if(!J)return null;return`${q}/${J}`}}class V extends Error{status;code;clientCode;data;constructor(q,J,Q,N,_){super(q);this.name="BrpcClientError",this.status=J,this.code=Q,this.clientCode=N,this.data=_}isClientError(q){return this.clientCode===q}isUnauthorized(){return this.status===401}isForbidden(){return this.status===403}isNotFound(){return this.status===404}isValidationError(){return this.status===400||this.code==="BAD_REQUEST"}}class P extends V{type;constructor(q,J){super(J.message??"WebSocket request failed",J.httpStatus??0,J.code,J.clientCode,J.data);this.name="BrpcClientSubscriptionError",this.type=q}}function e(){return function(J,Q){if(Q)return J instanceof V&&J.clientCode===Q;return J instanceof V}}function c(q){return typeof File<"u"&&q instanceof File||typeof Blob<"u"&&q instanceof Blob||typeof q==="object"&&q!==null&&"uri"in q&&"name"in q&&"type"in q}function m(q){if(!q)return null;return q.replace(/\//g,"")}function p(q){let J=q&&q.length>0?q:typeof window<"u"?window.location.origin:"";return J=J.endsWith("/")?J.slice(0,-1):J,J}function l(q,J){let Q=J?`/${J}/ws`:"/ws";return`${q.replace(/^http/,"ws")}${Q}`}function i(q,J){if(q&&J)return`/${q}/${J}`;else if(q&&!J)return`/${q}`;else if(!q&&J)return`/${J}`;else return""}class v{baseUrl;reconnectDelayMs;ws=null;subscriptions=new Map;messageQueue=[];isConnected=!1;reconnectTimeout=null;hasConnected=!1;reconnectListeners=new Set;WebSocketImpl;getHeaders;authToken=null;debug;pendingAuth=!1;authActions=[];constructor(q,J,Q,N=!1,_,D=2000){this.baseUrl=q;this.reconnectDelayMs=D;this.WebSocketImpl=J,this.debug=N&&_==="development",this.getHeaders=async()=>{if(typeof Q==="function")return await Q();return Q??{}},this.connect()}startTopicSubscription(q){let J=this.subscriptions.get(q);if(!J)return;if(J.status="pending",J.auth==="required"&&this.pendingAuth){this.authActions.push(()=>this.startTopicSubscription(q));return}this.send({type:"subscribe",topic:q},{allowDuringPendingAuth:J.auth==="none"})}async connect(){try{let q=await this.getHeaders();this.authToken=q.Authorization||null;let J=this.baseUrl;if(this.debug)console.log(`Connecting to WebSocket: ${J}`);this.ws=new this.WebSocketImpl(J),this.ws.onopen=async()=>{if(this.debug)console.log("WebSocket connection established");let Q=this.hasConnected;if(this.hasConnected=!0,this.isConnected=!0,this.authActions=[],this.authToken){if(this.pendingAuth=!0,this.send({type:"authenticate",token:this.authToken}),this.debug)console.log("Sent authentication token, waiting for response");for(let N of this.subscriptions.keys())this.startTopicSubscription(N);this.authActions.push(()=>{if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((N)=>this.send(N)),this.messageQueue=[]})}else{for(let N of this.subscriptions.keys())this.startTopicSubscription(N);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((N)=>this.send(N)),this.messageQueue=[]}if(Q)queueMicrotask(()=>{this.reconnectListeners.forEach((N)=>{try{N()}catch(_){console.error("WebSocket reconnect listener failed:",_)}})})},this.ws.onmessage=(Q)=>{try{if(this.debug)console.log("WebSocket message received:",Q.data);let N=JSON.parse(Q.data);if(N.type==="auth_success"){let _=N.authenticated!==!1;if(this.debug)console.log(`WebSocket ${_?"authentication":"deauthentication"} successful`);if(this.pendingAuth=!1,_)this.authActions.forEach((D)=>D());this.authActions=[];return}if(N.type==="auth_error"){console.error("WebSocket authentication failed:",this.createClientError(N.error)),this.pendingAuth=!1,this.authActions=[];return}if(this.debug)console.log("Got new message",N);if(N.type==="subscribe_success"&&N.topic&&this.subscriptions.has(N.topic))this.subscriptions.get(N.topic).status="subscribed";else if(N.type==="publish"&&N.topic&&this.subscriptions.has(N.topic)){let{callbacks:_}=this.subscriptions.get(N.topic);if(this.debug)console.log("Calling subscription callbacks");_?.forEach((D)=>D(void 0,N.data))}else if(["subscribe_error","publish_error"].includes(N.type)&&N.topic&&this.subscriptions.has(N.topic)){let _=this.createSubscriptionError(N.type,N.error),D=this.subscriptions.get(N.topic);D.status="failed",D.callbacks.forEach(($)=>$(_,void 0))}else if(N.type==="unsubscribe_error")console.error(`WebSocket unsubscribe failed for topic ${N.topic??"unknown"}:`,this.createClientError(N.error));else if(N.error)console.error("WebSocket error:",this.createClientError(N.error));else console.warn("Unhandled WebSocket message format:",N)}catch(N){if(this.debug)console.error("Error processing WebSocket message:",N),console.error("Raw message:",Q.data)}},this.ws.onclose=(Q)=>{if(this.isConnected=!1,this.pendingAuth=!1,this.subscriptions.forEach((N)=>{N.status="stale"}),this.debug)console.log(`WebSocket connection closed. Code: ${Q.code}, Reason: ${Q.reason}`);this.reconnectTimeout=setTimeout(()=>this.connect(),this.reconnectDelayMs)},this.ws.onerror=(Q)=>{if(this.debug)console.log("WebSocket error:",Q)}}catch(q){if(this.debug)console.log("Failed to create WebSocket connection:",q);this.pendingAuth=!1,this.reconnectTimeout=setTimeout(()=>this.connect(),this.reconnectDelayMs)}}createClientError(q){return new V(q?.message??"WebSocket request failed",q?.httpStatus??0,q?.code,q?.clientCode,q?.data)}createSubscriptionError(q,J){return new P(q,J??{})}disconnect(){if(this.reconnectTimeout)clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null;if(this.ws)this.ws.close(),this.ws=null;this.isConnected=!1,this.pendingAuth=!1,this.authActions=[],this.subscriptions.clear(),this.reconnectListeners.clear()}onReconnect(q){return this.reconnectListeners.add(q),()=>this.reconnectListeners.delete(q)}send(q,J={}){if(this.isConnected&&this.ws?.readyState===this.WebSocketImpl.OPEN){if(this.pendingAuth&&q.type!=="authenticate"&&!J.allowDuringPendingAuth){if(this.messageQueue.push(q),this.debug)console.log("Message queued until authentication completes:",q);return}let Q=JSON.stringify(q);if(this.ws.send(Q),this.debug)console.log("WebSocket message sent:",Q)}else if(this.messageQueue.push(q),this.debug)console.log("WebSocket message queued (not connected):",q)}subscribe(q,J,Q={}){let N=this.subscriptions.get(q);if(!N){if(N={auth:Q.auth??"required",callbacks:new Set,status:"pending"},this.subscriptions.set(q,N),N.callbacks.add(J),this.isConnected)this.startTopicSubscription(q)}else N.callbacks.add(J);return{unsubscribe:()=>this.unsubscribe(q,J),publish:(_)=>this.publish(q,_,J)}}unsubscribe(q,J){let Q=this.subscriptions.get(q);if(Q){if(Q.callbacks.delete(J),Q.callbacks.size===0){if(this.subscriptions.delete(q),this.send({type:"unsubscribe",topic:q}),this.debug)console.log(`Unsubscribed from topic: ${q}`)}}}publish(q,J,Q){let N=this.subscriptions.get(q);if(N?.status!=="subscribed"){Q(this.createSubscriptionError("publish_error",{message:"Cannot publish to a topic that is not subscribed",code:"SUBSCRIPTION_NOT_SUBSCRIBED",clientCode:"SUBSCRIPTION_NOT_SUBSCRIBED",data:{status:N?.status??"unsubscribed"}}),void 0);return}if(this.send({type:"publish",topic:q,data:J}),this.debug)console.log(`Published to topic: ${q}`,J)}async updateAuth(){let J=(await this.getHeaders()).Authorization||"",Q=J.startsWith("Bearer ")?J.substring(7).trim():J.trim(),N=Q.length>0,_=this.authToken!==null&&this.authToken.trim().length>0;if((N!==_||Q!==this.authToken)&&this.isConnected){if(this.authToken=Q,this.pendingAuth=N,this.send({type:"authenticate",token:J}),this.debug)if(N)console.log("Updated authentication token, waiting for confirmation");else console.log("Sent empty token for deauthentication"),this.pendingAuth=!1,this.authActions=[]}}}function n(q){if(!q)return[];let J=[];return Object.keys(q).sort().forEach((Q)=>{let N=q[Q];if(N===void 0||N===null||N==="")return;J.push(Q,String(N))}),J}function jq(q,J={}){let Q=J.fetch||fetch,N=J.WebSocket??(typeof WebSocket<"u"?WebSocket:null),_=J.prefix??"",D=J.apiPrefix??"",$=J.debug??!1,K=J.s3Endpoint,C=J.nodeEnv;if(!J.s3Endpoint)throw Error("BRPC Client: Pass s3Endpoint option to createBrpcClient");if(!J.nodeEnv)throw Error("BRPC Client: Pass nodeEnv option to createBrpcClient");if(!N)throw Error("WebSocket is not available in this environment");let T=J.headers??{},k=async()=>{if(typeof T==="function")return await T();return T},S=p(q),g=m(J.prefix),o=m(J.apiPrefix),B=i(g,o),y=l(S,g),w=new v(y,N,k,$,C),x=async(Y=!0)=>{let A={};if(Y)A["Content-Type"]="application/json";let X=await k();return{...A,...X}},t=async(Y)=>{if(!Y.ok){let X;try{X=await Y.json()}catch{try{X={error:await Y.text()}}catch{X={error:"Failed to parse error response"}}}if(X?.error&&typeof X.error==="object"){let R=X.error;throw new V(R.message||Y.statusText,Y.status,R.code,R.clientCode,R.data)}let W=X?.error||X?.message||Y.statusText;throw new V(W,Y.status,void 0,void 0,X)}let A=Y.headers.get("Content-Type")||"";try{if(A.includes("application/json"))return(await Y.json()).data;else if(A.includes("text/"))return Y.text();else return Y.blob()}catch(X){throw new V(`Failed to parse response: ${X instanceof Error?X.message:"Unknown error"}`,Y.status,"PARSE_ERROR")}},f=async(Y,A)=>{try{let X=await Q(Y,A);return await t(X)}catch(X){if(X instanceof V)throw X;throw new V(X instanceof Error?X.message:"Network request failed",0,"NETWORK_ERROR")}},H=(Y,A)=>{return Y.split("/").map((X)=>{if(!X.startsWith(":"))return X;let W=X.slice(1),R=A?.[W];if(R===void 0||R===null)throw new V(`Missing route parameter: ${W}`,400,"MISSING_ROUTE_PARAM","MISSING_ROUTE_PARAM",{param:W});return encodeURIComponent(String(R))}).join("/")},d=(Y=[])=>{return new Proxy({},{get(A,X){if(typeof X==="symbol")return;if(X==="query"||X==="mutation"||X==="formMutation"||X==="subscription"||X==="getStringKey"||X==="getArrayKey"||X==="getNoInputsArrayKey"){let W=Y.join("/"),R=(Z)=>`${S}${B}/${Z}`;if($)console.log(`BRPC ${X} procedure path: ${W}`);if(X==="query")return async(Z,M={})=>{let G=H(W,M.params),I=await x(!0);if($)console.log(`BRPC query request to ${G}`,{input:Z,headers:I});let F=new URL(R(G));if(Z&&typeof Z==="object")Object.entries(Z).forEach(([O,z])=>{if(z!==void 0)F.searchParams.append(O,String(z))});let L=await f(F.toString(),{method:"GET",headers:I,credentials:"include"});if($)console.log(`BRPC query response from ${G}:`,L);return L};else if(X==="mutation")return async(Z,M={})=>{let G=H(W,M.params),I=await x(!0);if($)console.log(`BRPC mutation request to ${G}`,{input:Z,headers:I});let F=JSON.stringify(Z??{}),L=await f(R(G),{method:"POST",headers:I,body:F,credentials:"include"});if($)console.log(`BRPC mutation response from ${G}:`,L);return L};else if(X==="formMutation")return async(Z,M={})=>{let G=H(W,M.params),I=await x(!1);if($)console.log(`BRPC formMutation request to ${G}`,{input:Z,headers:I});let F=new FormData,L=(z,j,E)=>{let U=E?`${E}[${z}]`:z;if(j===null||j===void 0)return;else if(c(j))F.append(U,j);else if(Array.isArray(j))j.forEach((b,h)=>{L(String(h),b,U)});else if(typeof j==="object"&&j!==null)Object.entries(j).forEach(([b,h])=>{L(b,h,U)});else F.append(U,String(j))};Object.entries(Z).forEach(([z,j])=>{L(z,j)});let O=await f(R(G),{method:"POST",headers:I,body:F,credentials:"include"});if($)console.log(`BRPC formMutation response from ${G}:`,O);return O};else if(X==="subscription")return(Z,M={})=>{let G=H(W,M.params);if($)console.log(`BRPC subscription to ${G}`);let I=B?`${B.slice(1)}/${G}`:G;return w.updateAuth(),w.subscribe(I,Z,M)};else if(X==="getStringKey")return(Z,M={})=>{let G=H(W,M.params);if(!Z||Object.keys(Z).length===0)return G;let I=Object.keys(Z).sort().reduce((L,O)=>{let z=Z[O];if(z!==void 0&&z!==null)L[O]=z;return L},{});if(Object.keys(I).length===0)return G;let F=JSON.stringify(I);return`${G}?${F}`};else if(X==="getArrayKey")return(Z,M={})=>{let G=H(W,M.params),I=n(M.keys),F=Z??{};if(Object.keys(F).length===0)return[G,...I];let L=[];if(Object.keys(F).sort().forEach((z)=>{let j=F[z];if(j!==void 0&&j!==null){let E=j;if(typeof j==="boolean"||typeof j==="number")E=String(j);else if(typeof j==="object")E=JSON.stringify(j);L.push([z,E])}}),L.length===0)return[G,...I];let O=L.flatMap(([z,j])=>[z,j]);return[G,...O,...I]};else if(X==="getNoInputsArrayKey")return(Z={})=>{return[H(W,Z.params),...n(Z.keys)]}}return d([...Y,X])}})},a=d();if($)console.log("BRPC client created",{baseUrl:S,prefix:B,wsUrl:y});return{routes:a,storage:{getObjectUrl:u(K)},utils:{updateWsAuth:async()=>await w.updateAuth(),onWsReconnect:(Y)=>w.onReconnect(Y),setHeader:async(Y,A)=>{if(typeof T==="function"){console.warn("Cannot use setHeader with function-based headers resolver");return}T={...T,[Y]:A},await w.updateAuth()},setHeaders:async(Y)=>{if(typeof T==="function"){console.warn("Cannot use setHeaders with function-based headers resolver");return}T={...T,...Y},await w.updateAuth()}}}}class s{entries=new Map;subscribe({key:q,subscription:J,options:Q,listener:N}){let _=JSON.stringify(q),D=(...K)=>N(...K),$=this.entries.get(_);if(!$){$={listeners:new Set,publish:()=>{},unsubscribe:()=>{}},this.entries.set(_,$);try{let K=J((...C)=>{$?.listeners.forEach((T)=>T(...C))},Q);$.publish=K.publish,$.unsubscribe=K.unsubscribe}catch(K){throw this.entries.delete(_),K}}return $.listeners.add(D),{publish:(K)=>{if(this.entries.get(_)===$)$.publish(K)},unsubscribe:()=>this.unsubscribe(_,$,D)}}unsubscribe(q,J,Q){if(J.listeners.delete(Q),J.listeners.size>0||this.entries.get(q)!==J)return;this.entries.delete(q),J.unsubscribe()}}export{e as isBrpcClientErrorUtility,u as createGetObjectUrl,jq as createBrpcClient,s as SubscriptionService,P as BrpcClientSubscriptionError,V as BrpcClientError};
2
+ function D(e){return(t)=>{if(!t)return null;return`${e}/${t}`}}class y extends Error{status;code;clientCode;data;constructor(e,t,n,s,c){super(e);this.name="BrpcClientError",this.status=t,this.code=n,this.clientCode=s,this.data=c}isClientError(e){return this.clientCode===e}isUnauthorized(){return this.status===401}isForbidden(){return this.status===403}isNotFound(){return this.status===404}isValidationError(){return this.status===400||this.code==="BAD_REQUEST"}}class j extends y{type;constructor(e,t){super(t.message??"WebSocket request failed",t.httpStatus??0,t.code,t.clientCode,t.data);this.name="BrpcClientSubscriptionError",this.type=e}}function ee(){return function(t,n){if(n)return t instanceof y&&t.clientCode===n;return t instanceof y}}function K(e){return typeof File<"u"&&e instanceof File||typeof Blob<"u"&&e instanceof Blob||typeof e==="object"&&e!==null&&"uri"in e&&"name"in e&&"type"in e}function _(e){if(!e)return null;return e.replace(/\//g,"")}function L(e){let t=e&&e.length>0?e:typeof window<"u"?window.location.origin:"";return t=t.endsWith("/")?t.slice(0,-1):t,t}function Q(e,t){let n=t?`/${t}/ws`:"/ws";return`${e.replace(/^http/,"ws")}${n}`}function G(e,t){if(e&&t)return`/${e}/${t}`;else if(e&&!t)return`/${e}`;else if(!e&&t)return`/${t}`;else return""}class H{baseUrl;reconnectDelayMs;ws=null;subscriptions=new Map;messageQueue=[];isConnected=!1;reconnectTimeout=null;hasConnected=!1;reconnectListeners=new Set;WebSocketImpl;getHeaders;authToken=null;debug;pendingAuth=!1;authActions=[];constructor(e,t,n,s=!1,c,w=2000){this.baseUrl=e;this.reconnectDelayMs=w;this.WebSocketImpl=t,this.debug=s&&c==="development",this.getHeaders=async()=>{if(typeof n==="function")return await n();return n??{}},this.connect()}startTopicSubscription(e){let t=this.subscriptions.get(e);if(!t)return;if(t.status="pending",t.auth==="required"&&this.pendingAuth){this.authActions.push(()=>this.startTopicSubscription(e));return}this.send({type:"subscribe",topic:e},{allowDuringPendingAuth:t.auth==="none"})}async connect(){try{let e=await this.getHeaders();this.authToken=e.Authorization||null;let t=this.baseUrl;if(this.debug)console.log(`Connecting to WebSocket: ${t}`);this.ws=new this.WebSocketImpl(t),this.ws.onopen=async()=>{if(this.debug)console.log("WebSocket connection established");let n=this.hasConnected;if(this.hasConnected=!0,this.isConnected=!0,this.authActions=[],this.authToken){if(this.pendingAuth=!0,this.send({type:"authenticate",token:this.authToken}),this.debug)console.log("Sent authentication token, waiting for response");for(let s of this.subscriptions.keys())this.startTopicSubscription(s);this.authActions.push(()=>{if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((s)=>this.send(s)),this.messageQueue=[]})}else{for(let s of this.subscriptions.keys())this.startTopicSubscription(s);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((s)=>this.send(s)),this.messageQueue=[]}if(n)queueMicrotask(()=>{this.reconnectListeners.forEach((s)=>{try{s()}catch(c){console.error("WebSocket reconnect listener failed:",c)}})})},this.ws.onmessage=(n)=>{try{if(this.debug)console.log("WebSocket message received:",n.data);let s=JSON.parse(n.data);if(s.type==="auth_success"){let c=s.authenticated!==!1;if(this.debug)console.log(`WebSocket ${c?"authentication":"deauthentication"} successful`);if(this.pendingAuth=!1,c)this.authActions.forEach((w)=>w());this.authActions=[];return}if(s.type==="auth_error"){console.error("WebSocket authentication failed:",this.createClientError(s.error)),this.pendingAuth=!1,this.authActions=[];return}if(this.debug)console.log("Got new message",s);if(s.type==="subscribe_success"&&s.topic&&this.subscriptions.has(s.topic))this.subscriptions.get(s.topic).status="subscribed";else if(s.type==="publish"&&s.topic&&this.subscriptions.has(s.topic)){let{callbacks:c}=this.subscriptions.get(s.topic);if(this.debug)console.log("Calling subscription callbacks");c?.forEach((w)=>w(void 0,s.data))}else if(["subscribe_error","publish_error"].includes(s.type)&&s.topic&&this.subscriptions.has(s.topic)){let c=this.createSubscriptionError(s.type,s.error),w=this.subscriptions.get(s.topic);w.status="failed",w.callbacks.forEach((a)=>a(c,void 0))}else if(s.type==="unsubscribe_error")console.error(`WebSocket unsubscribe failed for topic ${s.topic??"unknown"}:`,this.createClientError(s.error));else if(s.error)console.error("WebSocket error:",this.createClientError(s.error));else console.warn("Unhandled WebSocket message format:",s)}catch(s){if(this.debug)console.error("Error processing WebSocket message:",s),console.error("Raw message:",n.data)}},this.ws.onclose=(n)=>{if(this.isConnected=!1,this.pendingAuth=!1,this.subscriptions.forEach((s)=>{s.status="stale"}),this.debug)console.log(`WebSocket connection closed. Code: ${n.code}, Reason: ${n.reason}`);this.reconnectTimeout=setTimeout(()=>this.connect(),this.reconnectDelayMs)},this.ws.onerror=(n)=>{if(this.debug)console.log("WebSocket error:",n)}}catch(e){if(this.debug)console.log("Failed to create WebSocket connection:",e);this.pendingAuth=!1,this.reconnectTimeout=setTimeout(()=>this.connect(),this.reconnectDelayMs)}}createClientError(e){return new y(e?.message??"WebSocket request failed",e?.httpStatus??0,e?.code,e?.clientCode,e?.data)}createSubscriptionError(e,t){return new j(e,t??{})}disconnect(){if(this.reconnectTimeout)clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null;if(this.ws)this.ws.close(),this.ws=null;this.isConnected=!1,this.pendingAuth=!1,this.authActions=[],this.subscriptions.clear(),this.reconnectListeners.clear()}onReconnect(e){return this.reconnectListeners.add(e),()=>this.reconnectListeners.delete(e)}send(e,t={}){if(this.isConnected&&this.ws?.readyState===this.WebSocketImpl.OPEN){if(this.pendingAuth&&e.type!=="authenticate"&&!t.allowDuringPendingAuth){if(this.messageQueue.push(e),this.debug)console.log("Message queued until authentication completes:",e);return}let n=JSON.stringify(e);if(this.ws.send(n),this.debug)console.log("WebSocket message sent:",n)}else if(this.messageQueue.push(e),this.debug)console.log("WebSocket message queued (not connected):",e)}subscribe(e,t,n={}){let s=this.subscriptions.get(e);if(!s){if(s={auth:n.auth??"required",callbacks:new Set,status:"pending"},this.subscriptions.set(e,s),s.callbacks.add(t),this.isConnected)this.startTopicSubscription(e)}else s.callbacks.add(t);return{unsubscribe:()=>this.unsubscribe(e,t),publish:(c)=>this.publish(e,c,t)}}unsubscribe(e,t){let n=this.subscriptions.get(e);if(n){if(n.callbacks.delete(t),n.callbacks.size===0){if(this.subscriptions.delete(e),this.send({type:"unsubscribe",topic:e}),this.debug)console.log(`Unsubscribed from topic: ${e}`)}}}publish(e,t,n){let s=this.subscriptions.get(e);if(s?.status!=="subscribed"){n(this.createSubscriptionError("publish_error",{message:"Cannot publish to a topic that is not subscribed",code:"SUBSCRIPTION_NOT_SUBSCRIBED",clientCode:"SUBSCRIPTION_NOT_SUBSCRIBED",data:{status:s?.status??"unsubscribed"}}),void 0);return}if(this.send({type:"publish",topic:e,data:t}),this.debug)console.log(`Published to topic: ${e}`,t)}async updateAuth(){let t=(await this.getHeaders()).Authorization||"",n=t.startsWith("Bearer ")?t.substring(7).trim():t.trim(),s=n.length>0,c=this.authToken!==null&&this.authToken.trim().length>0;if((s!==c||n!==this.authToken)&&this.isConnected){if(this.authToken=n,this.pendingAuth=s,this.send({type:"authenticate",token:t}),this.debug)if(s)console.log("Updated authentication token, waiting for confirmation");else console.log("Sent empty token for deauthentication"),this.pendingAuth=!1,this.authActions=[]}}}function z(e){if(!e)return[];let t=[];return Object.keys(e).sort().forEach((n)=>{let s=e[n];if(s===void 0||s===null||s==="")return;t.push(n,String(s))}),t}function pe(e,t={}){let n=t.fetch||fetch,s=t.WebSocket??(typeof WebSocket<"u"?WebSocket:null),c=t.prefix??"",w=t.apiPrefix??"",a=t.debug??!1,{s3Endpoint:R,nodeEnv:A}=t;if(!t.s3Endpoint)throw Error("BRPC Client: Pass s3Endpoint option to createBrpcClient");if(!t.nodeEnv)throw Error("BRPC Client: Pass nodeEnv option to createBrpcClient");if(!s)throw Error("WebSocket is not available in this environment");let C=t.headers??{},M=async()=>{if(typeof C==="function")return await C();return C},W=L(e),N=_(t.prefix),J=_(t.apiPrefix),O=G(N,J),F=Q(W,N),E=new H(F,s,M,a,A),x=async(i=!0)=>{let m={};if(i)m["Content-Type"]="application/json";let r=await M();return{...m,...r}},V=async(i)=>{if(!i.ok){let r;try{r=await i.json()}catch{try{r={error:await i.text()}}catch{r={error:"Failed to parse error response"}}}if(r?.error&&typeof r.error==="object"){let S=r.error;throw new y(S.message||i.statusText,i.status,S.code,S.clientCode,S.data)}let b=r?.error||r?.message||i.statusText;throw new y(b,i.status,void 0,void 0,r)}let m=i.headers.get("Content-Type")||"";try{if(m.includes("application/json"))return(await i.json()).data;else if(m.includes("text/"))return i.text();else return i.blob()}catch(r){throw new y(`Failed to parse response: ${r instanceof Error?r.message:"Unknown error"}`,i.status,"PARSE_ERROR")}},v=async(i,m)=>{try{let r=await n(i,m);return await V(r)}catch(r){if(r instanceof y)throw r;throw new y(r instanceof Error?r.message:"Network request failed",0,"NETWORK_ERROR")}},T=(i,m)=>i.split("/").map((r)=>{if(!r.startsWith(":"))return r;let b=r.slice(1),S=m?.[b];if(S===void 0||S===null)throw new y(`Missing route parameter: ${b}`,400,"MISSING_ROUTE_PARAM","MISSING_ROUTE_PARAM",{param:b});return encodeURIComponent(String(S))}).join("/"),q=(i=[])=>new Proxy({},{get(m,r){if(typeof r==="symbol")return;if(r==="query"||r==="mutation"||r==="formMutation"||r==="subscription"||r==="getStringKey"||r==="getArrayKey"||r==="getNoInputsArrayKey"){let b=i.join("/"),S=(o)=>`${W}${O}/${o}`;if(a)console.log(`BRPC ${r} procedure path: ${b}`);if(r==="query")return async(o,g={})=>{let u=T(b,g.params),p=await x(!0);if(a)console.log(`BRPC query request to ${u}`,{input:o,headers:p});let f=new URL(S(u));if(o&&typeof o==="object")Object.entries(o).forEach(([k,d])=>{if(d!==void 0)f.searchParams.append(k,String(d))});let h=await v(f.toString(),{method:"GET",headers:p,credentials:"include"});if(a)console.log(`BRPC query response from ${u}:`,h);return h};else if(r==="mutation")return async(o,g={})=>{let u=T(b,g.params),p=await x(!0);if(a)console.log(`BRPC mutation request to ${u}`,{input:o,headers:p});let f=JSON.stringify(o??{}),h=await v(S(u),{method:"POST",headers:p,body:f,credentials:"include"});if(a)console.log(`BRPC mutation response from ${u}:`,h);return h};else if(r==="formMutation")return async(o,g={})=>{let u=T(b,g.params),p=await x(!1);if(a)console.log(`BRPC formMutation request to ${u}`,{input:o,headers:p});let f=new FormData,h=(d,l,P)=>{let B=P?`${P}[${d}]`:d;if(l===null||l===void 0)return;else if(K(l))f.append(B,l);else if(Array.isArray(l))l.forEach((U,I)=>{h(String(I),U,B)});else if(typeof l==="object"&&l!==null)Object.entries(l).forEach(([U,I])=>{h(U,I,B)});else f.append(B,String(l))};Object.entries(o).forEach(([d,l])=>{h(d,l)});let k=await v(S(u),{method:"POST",headers:p,body:f,credentials:"include"});if(a)console.log(`BRPC formMutation response from ${u}:`,k);return k};else if(r==="subscription")return(o,g={})=>{let u=T(b,g.params);if(a)console.log(`BRPC subscription to ${u}`);let p=O?`${O.slice(1)}/${u}`:u;return E.updateAuth(),E.subscribe(p,o,g)};else if(r==="getStringKey")return(o,g={})=>{let u=T(b,g.params);if(!o||Object.keys(o).length===0)return u;let p=Object.keys(o).sort().reduce((h,k)=>{let d=o[k];if(d!==void 0&&d!==null)h[k]=d;return h},{});if(Object.keys(p).length===0)return u;let f=JSON.stringify(p);return`${u}?${f}`};else if(r==="getArrayKey")return(o,g={})=>{let u=T(b,g.params),p=z(g.keys),f=o??{};if(Object.keys(f).length===0)return[u,...p];let h=[];if(Object.keys(f).sort().forEach((d)=>{let l=f[d];if(l!==void 0&&l!==null){let P=l;if(typeof l==="boolean"||typeof l==="number")P=String(l);else if(typeof l==="object")P=JSON.stringify(l);h.push([d,P])}}),h.length===0)return[u,...p];let k=h.flatMap(([d,l])=>[d,l]);return[u,...k,...p]};else if(r==="getNoInputsArrayKey")return(o={})=>[T(b,o.params),...z(o.keys)]}return q([...i,r])}}),X=q();if(a)console.log("BRPC client created",{baseUrl:W,prefix:O,wsUrl:F});return{routes:X,storage:{getObjectUrl:D(R)},utils:{updateWsAuth:async()=>await E.updateAuth(),onWsReconnect:(i)=>E.onReconnect(i),setHeader:async(i,m)=>{if(typeof C==="function"){console.warn("Cannot use setHeader with function-based headers resolver");return}C={...C,[i]:m},await E.updateAuth()},setHeaders:async(i)=>{if(typeof C==="function"){console.warn("Cannot use setHeaders with function-based headers resolver");return}C={...C,...i},await E.updateAuth()}}}}class Y{entries=new Map;subscribe({key:e,subscription:t,options:n,listener:s}){let c=JSON.stringify(e),w=(...R)=>s(...R),a=this.entries.get(c);if(!a){a={listeners:new Set,publish:()=>{},unsubscribe:()=>{}},this.entries.set(c,a);try{let R=t((...A)=>{a?.listeners.forEach((C)=>C(...A))},n);a.publish=R.publish,a.unsubscribe=R.unsubscribe}catch(R){throw this.entries.delete(c),R}}return a.listeners.add(w),{publish:(R)=>{if(this.entries.get(c)===a)a.publish(R)},unsubscribe:()=>this.unsubscribe(c,a,w)}}unsubscribe(e,t,n){if(t.listeners.delete(n),t.listeners.size>0||this.entries.get(e)!==t)return;this.entries.delete(e),t.unsubscribe()}}export{y as BrpcClientError,j as BrpcClientSubscriptionError,Y as SubscriptionService,pe as createBrpcClient,D as createGetObjectUrl,ee as isBrpcClientErrorUtility};
3
3
 
4
- //# debugId=0D60FB067272A2FE64756E2164756E21
4
+ //# debugId=938DE8668B06676364756E2164756E21
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{useCallback as G,useEffect as M,useRef as H}from"react";function T(w){return function({procedure:j,options:x,callback:z,enabled:A=!0}){let D=j.getNoInputsArrayKey(x),J=JSON.stringify(D),F=H(z),q=H(null);F.current=z;let K=G((...m)=>F.current(...m),[]);return M(()=>{if(!A){q.current=null;return}let{publish:m,unsubscribe:L}=w.subscribe({key:D,subscription:j.subscription,options:x,listener:K});return q.current=m,()=>{q.current=null,L()}},[A,J,x.auth]),{publish:G((m)=>{q.current?.(m)},[])}}}import{useEffect as N,useRef as O}from"react";function X(w){return function(j){let x=O(j);x.current=j,N(()=>{return w.utils.onWsReconnect(()=>x.current())},[])}}export{T as createUseSubscription,X as createUseOnWsReconnect};
2
+ import{useCallback as p,useEffect as f,useRef as b}from"react";function I(i){return function({procedure:n,options:t,callback:r,enabled:s=!0}){let o=n.getNoInputsArrayKey(t),S=JSON.stringify(o),c=b(r),u=b(null);c.current=r;let O=p((...e)=>c.current(...e),[]);return f(()=>{if(!s){u.current=null;return}let{publish:e,unsubscribe:T}=i.subscribe({key:o,subscription:n.subscription,options:t,listener:O});return u.current=e,()=>{u.current=null,T()}},[s,S,t.auth]),{publish:p((e)=>{u.current?.(e)},[])}}}import{useEffect as a,useRef as y}from"react";function k(i){return function(n){let t=y(n);t.current=n,a(()=>i.utils.onWsReconnect(()=>t.current()),[])}}export{k as createUseOnWsReconnect,I as createUseSubscription};
3
3
 
4
- //# debugId=6911078E96D70E4164756E2164756E21
4
+ //# debugId=598BB147440ABA6264756E2164756E21
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // @bun
2
- import{z as N}from"zod";class Q{middlewares=[];subscribeMiddlewares=[];constructor($=[],F=[]){this.middlewares=$,this.subscribeMiddlewares=F}use($){return new Q([...this.middlewares,$],this.subscribeMiddlewares)}onSubscribe($){return new Q(this.middlewares,[...this.subscribeMiddlewares,$])}input($){return new I(this.middlewares,$,void 0,this.subscribeMiddlewares)}query($){return{_input:N.object({}),_output:null,_ctx:null,_type:"query",handler:$,middlewares:this.middlewares}}mutation($){return{_input:N.object({}),_output:null,_ctx:null,_type:"mutation",handler:$,middlewares:this.middlewares}}formMutation($){return{_input:N.object({}),_output:null,_ctx:null,_type:"formMutation",handler:$,middlewares:this.middlewares}}subscription($){return{_input:N.object({}),_output:null,_ctx:null,_type:"subscription",handler:$,middlewares:this.middlewares,subscribeMiddlewares:this.subscribeMiddlewares}}file($){return{_input:N.object({}),_output:null,_ctx:null,_type:"file",handler:$,middlewares:this.middlewares}}fileStream($){return{_input:N.object({}),_output:null,_ctx:null,_type:"fileStream",handler:$,middlewares:this.middlewares}}html($){return{_input:N.object({}),_output:null,_ctx:null,_type:"html",handler:$,middlewares:this.middlewares}}}class I{middlewares;inputSchema;timeoutMs;subscribeMiddlewares;constructor($,F,G,J=[]){this.middlewares=$;this.inputSchema=F;this.timeoutMs=G;this.subscribeMiddlewares=J}timeout($){return new I(this.middlewares,this.inputSchema,$,this.subscribeMiddlewares)}onSubscribe($){return new I(this.middlewares,this.inputSchema,this.timeoutMs,[...this.subscribeMiddlewares,$])}query($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"query",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}mutation($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"mutation",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}formMutation($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"formMutation",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}subscription($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"subscription",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares,subscribeMiddlewares:this.subscribeMiddlewares}}file($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"file",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}fileStream($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"fileStream",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}html($){return{_input:N.object({}),_output:null,_ctx:null,_type:"html",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}}function k($){return new Q}class A extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=A.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,A)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,F,G){return new A({code:"BAD_REQUEST",message:$,clientCode:F,data:G})}static unauthorized($="Unauthorized",F,G){return new A({code:"UNAUTHORIZED",message:$,clientCode:F,data:G})}static forbidden($="Forbidden",F,G){return new A({code:"FORBIDDEN",message:$,clientCode:F,data:G})}static notFound($="Not Found",F,G){return new A({code:"NOT_FOUND",message:$,clientCode:F,data:G})}static preconditionFailed($="Precondition failed",F,G){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
2
+ import{z as f}from"zod";class g{middlewares=[];subscribeMiddlewares=[];constructor(e=[],t=[]){this.middlewares=e,this.subscribeMiddlewares=t}use(e){return new g([...this.middlewares,e],this.subscribeMiddlewares)}onSubscribe(e){return new g(this.middlewares,[...this.subscribeMiddlewares,e])}input(e){return new b(this.middlewares,e,void 0,this.subscribeMiddlewares)}query(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"query",handler:e,middlewares:this.middlewares}}mutation(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"mutation",handler:e,middlewares:this.middlewares}}formMutation(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"formMutation",handler:e,middlewares:this.middlewares}}subscription(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"subscription",handler:e,middlewares:this.middlewares,subscribeMiddlewares:this.subscribeMiddlewares}}file(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"file",handler:e,middlewares:this.middlewares}}fileStream(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"fileStream",handler:e,middlewares:this.middlewares}}html(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"html",handler:e,middlewares:this.middlewares}}}class b{middlewares;inputSchema;timeoutMs;subscribeMiddlewares;constructor(e,t,r,n=[]){this.middlewares=e;this.inputSchema=t;this.timeoutMs=r;this.subscribeMiddlewares=n}timeout(e){return new b(this.middlewares,this.inputSchema,e,this.subscribeMiddlewares)}onSubscribe(e){return new b(this.middlewares,this.inputSchema,this.timeoutMs,[...this.subscribeMiddlewares,e])}query(e){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"query",_timeout:this.timeoutMs,handler:e,middlewares:this.middlewares}}mutation(e){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"mutation",_timeout:this.timeoutMs,handler:e,middlewares:this.middlewares}}formMutation(e){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"formMutation",_timeout:this.timeoutMs,handler:e,middlewares:this.middlewares}}subscription(e){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"subscription",_timeout:this.timeoutMs,handler:e,middlewares:this.middlewares,subscribeMiddlewares:this.subscribeMiddlewares}}file(e){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"file",_timeout:this.timeoutMs,handler:e,middlewares:this.middlewares}}fileStream(e){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"fileStream",_timeout:this.timeoutMs,handler:e,middlewares:this.middlewares}}html(e){return{_input:f.object({}),_output:null,_ctx:null,_type:"html",_timeout:this.timeoutMs,handler:e,middlewares:this.middlewares}}}function k(e){return new g}class p extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor(e){super(e.message);if(this.name="BRPCError",this.code=e.code,this.clientCode=e.clientCode,this.httpStatus=p.STATUS_MAP[e.code],this.data=e.data,this.cause=e.cause,Error.captureStackTrace)Error.captureStackTrace(this,p)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest(e,t,r){return new p({code:"BAD_REQUEST",message:e,clientCode:t,data:r})}static unauthorized(e="Unauthorized",t,r){return new p({code:"UNAUTHORIZED",message:e,clientCode:t,data:r})}static forbidden(e="Forbidden",t,r){return new p({code:"FORBIDDEN",message:e,clientCode:t,data:r})}static notFound(e="Not Found",t,r){return new p({code:"NOT_FOUND",message:e,clientCode:t,data:r})}static preconditionFailed(e="Precondition failed",t,r){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
3
3
  //! but the exact cause is unknown. Suspected: some client/proxy/middleware misbehaved on 412
4
4
  //! (not a browser auto-retry issue — 412 doesn't trigger those). Investigate before changing back.
5
- return new A({code:"NOT_FOUND",message:$,clientCode:F,data:G})}static conflict($,F,G){return new A({code:"CONFLICT",message:$,clientCode:F,data:G})}static unprocessableContent($,F,G){return new A({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:F,data:G})}static tooManyRequests($="Too many requests",F,G){return new A({code:"TOO_MANY_REQUESTS",message:$,clientCode:F,data:G})}static internalServerError($="Internal Server Error",F,G){return new A({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:F,data:G})}static timeout($="Request timeout",F,G){return new A({code:"TIMEOUT",message:$,clientCode:F,data:G})}}class M{routeTree;routeCache;options;routeStats;constructor($,F={}){if(this.options={enableCaching:!0,maxCacheSize:1000,debug:!1,...F},this.routeCache=new Map,this.routeStats=this.analyzeRoutes($),this.routeTree=this.compileRoutes($),this.options.debug)console.log("[ROUTEMATCHER]","initialized with stats:",this.routeStats)}match($){if(this.options.enableCaching)return this.matchWithCache($);return this.matchDirect($)}matchWithCache($){let F=$.join("/");if(this.routeCache.has(F)){let J=this.routeCache.get(F);if(this.options.debug&&J)console.log("[ROUTEMATCHER]",`\uD83C\uDFAF Cache hit for route: /${F}`);return J}let G=this.matchDirect($);if(this.cacheResult(F,G),this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDCBE Cached route: /${F} -> ${G?"found":"not found"}`);return G}matchDirect($){let F=this.options.debug?performance.now():0,G=this.traverseTree(this.routeTree,$,0,{});if(this.options.debug){let J=performance.now()-F,W=G?.route._type||"none";console.log("[ROUTEMATCHER]",`\u26A1 Route match: /${$.join("/")} (${W}) took ${J.toFixed(3)}ms`)}return G}traverseTree($,F,G,J){if(G>=F.length){if($.handler)return{params:J,route:$.handler.handler};if($.index)return{params:J,route:$.index.handler};//! This was the last change I made trying to fix
5
+ return new p({code:"NOT_FOUND",message:e,clientCode:t,data:r})}static conflict(e,t,r){return new p({code:"CONFLICT",message:e,clientCode:t,data:r})}static unprocessableContent(e,t,r){return new p({code:"UNPROCESSABLE_CONTENT",message:e,clientCode:t,data:r})}static tooManyRequests(e="Too many requests",t,r){return new p({code:"TOO_MANY_REQUESTS",message:e,clientCode:t,data:r})}static internalServerError(e="Internal Server Error",t,r){return new p({code:"INTERNAL_SERVER_ERROR",message:e,clientCode:t,data:r})}static timeout(e="Request timeout",t,r){return new p({code:"TIMEOUT",message:e,clientCode:t,data:r})}}class R{routeTree;routeCache;options;routeStats;constructor(e,t={}){if(this.options={enableCaching:!0,maxCacheSize:1000,debug:!1,...t},this.routeCache=new Map,this.routeStats=this.analyzeRoutes(e),this.routeTree=this.compileRoutes(e),this.options.debug)console.log("[ROUTEMATCHER]","initialized with stats:",this.routeStats)}match(e){if(this.options.enableCaching)return this.matchWithCache(e);return this.matchDirect(e)}matchWithCache(e){let t=e.join("/");if(this.routeCache.has(t)){let n=this.routeCache.get(t);if(this.options.debug&&n)console.log("[ROUTEMATCHER]",`\uD83C\uDFAF Cache hit for route: /${t}`);return n}let r=this.matchDirect(e);if(this.cacheResult(t,r),this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDCBE Cached route: /${t} -> ${r?"found":"not found"}`);return r}matchDirect(e){let t=this.options.debug?performance.now():0,r=this.traverseTree(this.routeTree,e,0,{});if(this.options.debug){let n=performance.now()-t,i=r?.route._type||"none";console.log("[ROUTEMATCHER]",`\u26A1 Route match: /${e.join("/")} (${i}) took ${n.toFixed(3)}ms`)}return r}traverseTree(e,t,r,n){if(r>=t.length){if(e.handler)return{params:n,route:e.handler.handler};if(e.index)return{params:n,route:e.index.handler};//! This was the last change I made trying to fix
6
6
  //! Wildacrd not matching root (/) when index wasn't defined
7
- if($.wildcard&&F.length===0)return{params:{...J,wildcardPath:""},route:$.wildcard.handler};return null}let W=F[G],_=decodeURIComponent(W),X=$.static.get(_);if(X){let Y=this.traverseTree(X,F,G+1,J);if(Y)return Y}if($.param){let Y={...J,[$.param.name]:_},D=this.traverseTree($.param.node,F,G+1,Y);if(D)return D}if($.wildcard){let Y=F.slice(G).join("/");return{params:{...J,wildcardPath:Y},route:$.wildcard.handler}}return null}compileRoutes($,F=[]){let G={static:new Map};for(let[J,W]of Object.entries($)){if(J==="index"&&this.isProcedure(W)){G.index={handler:W,paramNames:[...F],hasWildcard:!1};continue}if(J==="*"){if(this.isProcedure(W))G.wildcard={handler:W,paramNames:[...F,"wildcardPath"],hasWildcard:!0,wildcardIndex:F.length};continue}if(this.isProcedure(W)){let _={handler:W,paramNames:[...F],hasWildcard:!1};if(J.startsWith(":")){let X=J.slice(1),Y={static:new Map,handler:_};G.param={name:X,node:Y}}else{let X={static:new Map,handler:_};G.static.set(J,X)}}else if(typeof W==="object"&&W!==null)if(J.startsWith(":")){let _=J.slice(1),X=this.compileRoutes(W,[...F,_]);G.param={name:_,node:X}}else{let _=this.compileRoutes(W,F);G.static.set(J,_)}}return G}isProcedure($){return $&&typeof $==="object"&&"_type"in $&&"_input"in $}cacheResult($,F){if(this.routeCache.size>=this.options.maxCacheSize){let G=this.routeCache.keys().next().value;if(G!==void 0){if(this.routeCache.delete(G),this.options.debug)console.log(`\uD83D\uDDD1\uFE0F Evicted oldest cache entry: ${G}`)}}this.routeCache.set($,F)}analyzeRoutes($){let F={totalRoutes:0,staticRoutes:0,paramRoutes:0,wildcardRoutes:0,indexRoutes:0,maxDepth:0,procedureTypes:{query:0,mutation:0,subscription:0,file:0,fileStream:0,streamQuery:0,streamMutation:0,formMutation:0,html:0}},G=(J,W=0)=>{F.maxDepth=Math.max(F.maxDepth,W);for(let[_,X]of Object.entries(J))if(this.isProcedure(X)){if(F.totalRoutes++,X._type in F.procedureTypes)F.procedureTypes[X._type]++;if(_==="index")F.indexRoutes++;else if(_.startsWith(":"))F.paramRoutes++;else if(_==="*")F.wildcardRoutes++;else F.staticRoutes++}else if(typeof X==="object"&&X!==null)G(X,W+1)};return G($),F}clearCache(){if(this.routeCache.clear(),this.options.debug)console.log("[ROUTEMATCHER]","\uD83E\uDDF9 Route cache cleared")}getCacheStats(){return{size:this.routeCache.size,maxSize:this.options.maxCacheSize,routeStats:this.routeStats}}getOptimizationSuggestions(){let $=[],{routeStats:F}=this;if(F.paramRoutes>F.staticRoutes*2)$.push("\uD83D\uDE80 Consider grouping routes to reduce parameter route overhead");if(F.maxDepth>6)$.push("\uD83D\uDCCA Deep route nesting detected - consider flattening some routes");if(F.totalRoutes>200)$.push("\u26A1 Large route table - consider route splitting or lazy loading");if(!this.options.enableCaching&&F.totalRoutes>50)$.push("\uD83D\uDCBE Enable caching for better performance with many routes");if(F.procedureTypes.file>20)$.push("\uD83D\uDCC1 Many file routes detected - consider static file serving");if(F.procedureTypes.subscription>10)$.push("\uD83D\uDD04 Many subscription routes - ensure WebSocket optimization");return $}findRoutesByPattern($){return[]}getRouteInfo($){let F=$.split("/").filter(Boolean),G=F.join("/"),J=this.routeCache.has(G),W=performance.now(),_=this.matchDirect(F),X=performance.now()-W;return{found:!!_,route:_?.route,params:_?.params,cached:J,matchTime:X}}benchmark($,F=1000){let G=$.map((V)=>V.split("/").filter(Boolean)),J=new Map(this.routeCache);this.routeCache.clear();let W=performance.now();for(let V=0;V<F;V++)for(let U of G)this.matchDirect(U);let X=performance.now()-W,Y=F*$.length,D=X/Y,j=Y/X*1000;return this.routeCache=J,{averageTime:D,totalTime:X,routesPerSecond:j}}warmupCache($){if(!this.options.enableCaching)return;let F=performance.now(),G=0;for(let W of $){let _=W.split("/").filter(Boolean),X=this.matchDirect(_);if(X)this.cacheResult(_.join("/"),X),G++}let J=performance.now();if(this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDD25 Cache warmed up: ${G} routes in ${(J-F).toFixed(2)}ms`)}}var q=import.meta.require,v={NO_CACHE:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},ONE_YEAR:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMMUTABLE:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},ONE_MONTH:{cacheControl:"public, max-age=2592000"},ONE_WEEK:{cacheControl:"public, max-age=604800"},ONE_DAY:{cacheControl:"public, max-age=86400, must-revalidate"},ONE_HOUR:{cacheControl:"public, max-age=3600, must-revalidate"},FIVE_MINUTES:{cacheControl:"public, max-age=300"},API:{cacheControl:"public, max-age=300, s-maxage=3600"},STYLESHEET:{cacheControl:"public, max-age=2592000, must-revalidate"},JAVASCRIPT:{cacheControl:"public, max-age=2592000, must-revalidate"},HASHED_ASSET:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},FONT:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMAGE:{cacheControl:"public, max-age=2592000"},FAVICON:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},HTML_PAGE:{cacheControl:"public, max-age=3600, must-revalidate"},API_RESPONSE:{cacheControl:"public, max-age=300, s-maxage=900"},FEED:{cacheControl:"public, max-age=3600"},SITEMAP:{cacheControl:"public, max-age=86400"},MANIFEST:{cacheControl:"public, max-age=86400, must-revalidate"},SERVICE_WORKER:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},MEDIA_STREAM:{cacheControl:"no-cache, no-store"},DOCUMENT:{cacheControl:"public, max-age=86400"},ARCHIVE:{cacheControl:"public, max-age=604800"}};function C($){return v[$]}var f={html:"text/html; charset=utf-8",css:"text/css",javascript:"application/javascript",json:"application/json",xml:"application/xml",text:"text/plain; charset=utf-8",csv:"text/csv",markdown:"text/markdown; charset=utf-8",png:"image/png",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",webp:"image/webp",icon:"image/x-icon",bmp:"image/bmp",woff:"font/woff",woff2:"font/woff2",truetype:"font/ttf","embedded-opentype":"application/vnd.ms-fontobject",opentype:"font/otf",mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",mpeg:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",zip:"application/zip",tar:"application/x-tar",gzip:"application/gzip","7z":"application/x-7z-compressed",js:"application/javascript",txt:"text/plain; charset=utf-8",jpg:"image/jpeg",ico:"image/x-icon",ttf:"font/ttf",eot:"application/vnd.ms-fontobject",otf:"font/otf",mp3:"audio/mpeg",gz:"application/gzip"};function K($){return f[$]||"application/octet-stream"}function z($){if(typeof Bun<"u"&&Bun.hash)return Bun.hash($).toString(16);try{let F=q("crypto"),G;if(typeof $==="string")G=Buffer.from($);else if($ instanceof ArrayBuffer)G=Buffer.from(new Uint8Array($));else G=Buffer.from($);return F.createHash("md5").update(G).digest("hex")}catch{let F=typeof $==="string"?new TextEncoder().encode($):$ instanceof ArrayBuffer?new Uint8Array($):$,G=0;for(let J=0;J<F.length;J++)G=(G<<5)-G+F[J],G=G&G;return Math.abs(G).toString(16).padStart(16,"0")}}function L($,F,G){let J=$.headers.get("Origin"),W=!0;if((G?.allowAll??!0)&&J)return J;if(F.length===0)return J||"*";if(!J)return F[0];if(F.includes(J))return J;return F[0]}class S{headers={};contentType($){return this.headers["Content-Type"]=K($),this}filePath($){let F=$.split(".").pop()?.toLowerCase()||"";return this.headers["Content-Type"]=K(F),this}mimeType($){return this.headers["Content-Type"]=$,this}cache($){let{cacheControl:F,expires:G}=C($);if(this.headers["Cache-Control"]=F,G)this.headers.Expires=G;return this}eTag($){if(!$)return this;let F;if(typeof $==="string"&&$.startsWith('"'))F=$;else F=`"${z($)}"`;return this.headers.ETag=F,this}lastModified($){return this.headers["Last-Modified"]=$.toUTCString(),this}contentLength($){return this.headers["Content-Length"]=$.toString(),this}redirect($,F=!1){return this.headers.Location=$,this.headers["X-Redirect-Type"]=F?"permanent":"temporary",this}cors({origin:$="*",methods:F=["GET","POST","PUT","DELETE","OPTIONS"],headers:G=["Content-Type","Authorization"],maxAge:J=86400,credentials:W}={}){let _=W??$!=="*";if(_&&$==="*")throw Error('CORS: Cannot use credentials with wildcard origin "*". Specify an exact origin or set credentials to false.');if(this.headers["Access-Control-Allow-Origin"]=$,this.headers["Access-Control-Allow-Methods"]=F.join(", "),this.headers["Access-Control-Allow-Headers"]=G.join(", "),_)this.headers["Access-Control-Allow-Credentials"]="true";return this.headers["Access-Control-Max-Age"]=J.toString(),this}security($){let F={hsts:!0,noSniff:!0,frameOptions:"SAMEORIGIN",xssProtection:!0,...$};if(F.csp)this.headers["Content-Security-Policy"]=F.csp;if(F.hsts){let G=typeof F.hsts==="number"?F.hsts:31536000;this.headers["Strict-Transport-Security"]=`max-age=${G}; includeSubDomains`}if(F.noSniff)this.headers["X-Content-Type-Options"]="nosniff";if(F.frameOptions)this.headers["X-Frame-Options"]=F.frameOptions;if(F.xssProtection)this.headers["X-XSS-Protection"]="1; mode=block";return this}custom($,F){return this.headers[$]=F,this}customHeaders($){return Object.assign(this.headers,$),this}vary(...$){let F=this.headers.Vary,G=F?`${F}, ${$.join(", ")}`:$.join(", ");return this.headers.Vary=G,this}compress($){if($)this.headers["Content-Encoding"]=$;return this}build($){let F={...this.headers};if($&&!F["Content-Length"]){let G=typeof $==="string"?new TextEncoder().encode($).length:$.byteLength;F["Content-Length"]=G.toString()}return F}buildHeaders($){return new Headers(this.build($))}}function Z(){return new S}function p($,F){let G=Z().contentType($);if(F)G.cache(F);return G.build()}var l={css:($)=>Z().contentType("css").cache("STYLESHEET").eTag($),javascript:($)=>Z().contentType("javascript").cache("JAVASCRIPT").eTag($),hashedAsset:($,F)=>Z().filePath($).cache("HASHED_ASSET").eTag(F),font:($)=>Z().filePath($).cache("FONT").cors({origin:"*"}),image:($)=>Z().filePath($).cache("IMAGE"),favicon:()=>Z().contentType("icon").cache("FAVICON"),api:()=>Z().contentType("json").cache("API_RESPONSE").cors(),expensiveApi:($)=>Z().contentType("json").cache("API_RESPONSE").cors().eTag($),realtime:()=>Z().contentType("json").cache("NO_CACHE").cors(),html:()=>Z().contentType("html").cache("HTML_PAGE").security(),expensiveHtml:($)=>Z().contentType("html").cache("HTML_PAGE").security().eTag($),manifest:($)=>Z().contentType("json").cache("MANIFEST").eTag($),serviceWorker:()=>Z().contentType("javascript").cache("SERVICE_WORKER"),sitemap:($)=>Z().contentType("xml").cache("SITEMAP").eTag($),feed:($,F="rss")=>Z().contentType("xml").cache("FEED").eTag($).mimeType(F==="rss"?"application/rss+xml":"application/atom+xml"),video:($)=>Z().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),audio:($)=>Z().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),pdf:($)=>Z().contentType("pdf").cache("DOCUMENT").custom("Content-Disposition","inline").eTag($),download:($,F,G)=>Z().filePath(F).cache("ARCHIVE").custom("Content-Disposition",`attachment; filename="${$}"`).eTag(G),secure:()=>Z().security({csp:"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",hsts:31536000}).custom("X-Powered-By","Bun"),permanentRedirect:($)=>Z().redirect($,!0),temporaryRedirect:($)=>Z().redirect($,!1)};async function T($,F,G,J={}){let{maxChunkSize:W=2097152,cacheMaxAge:_=3600,acceptedExtensions:X}=J;if(!await $.exists())return new Response("Media file not found",{status:404,headers:Z().contentType("text").cors({origin:L(F,G)}).build()});if(X&&X.length>0){let b=$.name?.toLowerCase().split(".").pop();if(!b||!X.includes(b))return new Response("File type not supported",{status:415,headers:Z().contentType("text").cors({origin:L(F,G)}).build()})}let D=$.size,j=F.headers.get("Range");if(!j)return new Response($,{headers:Z().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${_}`).cors({origin:L(F,G)}).build()});let V=j.match(/bytes=(\d*)-(\d*)/);if(!V)return new Response("Invalid Range header",{status:416,headers:Z().contentType("text").cors({origin:L(F,G)}).custom("Content-Range",`bytes */${D}`).build()});let U=V[1]?parseInt(V[1]):0,R=V[2]?parseInt(V[2]):D-1,O=Math.min(R,U+W-1,D-1);if(U>=D||O>=D||U>O)return new Response("Range not satisfiable",{status:416,headers:Z().contentType("text").cors({origin:L(F,G)}).custom("Content-Range",`bytes */${D}`).build()});return new Response($.slice(U,O+1),{status:206,headers:Z().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${_}`).cors({origin:L(F,G)}).build()})}function y($,F,G){return{type:"stream",file:$,request:F,options:G}}class H{routes;routeMatcher;contextCreator;prefix;globalMiddlewares;server=null;wsConfig;integrations;development=!0;debug=!1;allowedOrigins=[];onError;activeConnections=new Set;maxConnections=parseInt(process.env.MAX_WS_CONNECTIONS??"1000");connectionTimeout=parseInt(process.env.WS_TIMEOUT??"30000");maxRequestSize=parseInt(process.env.MAX_REQUEST_SIZE??"10485760");requestTimeout=parseInt(process.env.REQUEST_TIMEOUT??"30000");constructor($){if(this.contextCreator=$.context,this.prefix=$.prefix??"",this.routes=$.routes,this.globalMiddlewares=$.globalMiddlewares??[],this.wsConfig=$.websocket??{},this.integrations=$.integrations,this.debug=!!$.debug&&!0,this.allowedOrigins=$.allowedOrigins??[],this.onError=$.onError,this.routeMatcher=new M($.routes,{enableCaching:!this.development,maxCacheSize:parseInt(process.env.ROUTE_CACHE_SIZE??"1000"),debug:this.debug}),!this.development)setInterval(()=>this.cleanupStaleConnections(),60000);if(this.debug)console.log("Router initialized with route matcher"),console.log("Route cache stats:",this.routeMatcher.getCacheStats())}findRoute($){return this.routeMatcher.match($)}async runMiddlewares($,F){for(let G of $){let J=await G(F);if(J&&typeof J==="object")Object.assign(F,J)}}async parseInput($,F,G){let J={},W=new URL($.url),_=Object.fromEntries(W.searchParams),X=$.method;if(X==="POST"){if(parseInt($.headers.get("content-length")||"0")>this.maxRequestSize)throw Error("Request payload too large")}if(X==="GET"&&["query","streamQuery","file","fileStream","html"].includes(G))J=_;else if(X==="POST"&&["mutation","streamMutation"].includes(G))if($.headers.get("content-type")?.includes("application/json"))J=await $.json();else throw new A({code:"BAD_REQUEST",message:"Unsupported content type"});else if(X==="POST"&&G==="formMutation"){if(!$.headers.get("content-type")?.includes("multipart/form-data"))throw new A({code:"BAD_REQUEST",message:"Unsupported content type"});J=await this.parseFormData(await $.formData())}else throw new A({code:"METHOD_NOT_SUPPORTED",message:"Unsupported request method"});return F.parse(J)}async parseFormData($){let F=Object.create(null);return $.forEach((G,J)=>{this.setFormDataValue(F,this.parseFormDataPath(J),G)}),F}setFormDataValue($,F,G){let J=$;for(let W=0;W<F.length;W++){let _=F[W],X=W===F.length-1,Y=this.isFormDataArrayIndex(F[W+1]);if(Array.isArray(J)){if(!this.isFormDataArrayIndex(_))throw A.badRequest("Invalid multipart array path");let j=Number(_);if(j>J.length)throw A.badRequest("Multipart array indexes must be contiguous");if(X){if(j===J.length)J.push(G);else if(J[j]===void 0)J[j]=G;else throw A.badRequest("Duplicate multipart field path");return}let V=J[j];if(V===void 0)J[j]=Y?[]:Object.create(null);else if(Array.isArray(V)!==Y)throw A.badRequest("Conflicting multipart field path");J=J[j];continue}if(X){if(Object.hasOwn(J,_))throw A.badRequest("Duplicate multipart field path");J[_]=G;return}let D=J[_];if(D===void 0)J[_]=Y?[]:Object.create(null);else if(Array.isArray(D)!==Y)throw A.badRequest("Conflicting multipart field path");J=J[_]}}parseFormDataPath($){let F=/^([^\[\]]+)((?:\[[^\[\]]+\])*)$/.exec($);if(!F)throw A.badRequest("Invalid multipart field path");let G=[F[1]];for(let J of F[2].matchAll(/\[([^\[\]]+)\]/g))G.push(J[1]);if(G.some((J)=>["__proto__","constructor","prototype"].includes(J)))throw A.badRequest("Invalid multipart field path");return G}isFormDataArrayIndex($){return $!==void 0&&/^(0|[1-9]\d*)$/.test($)}handleError($,F){if(this.onError)try{this.onError($,{req:F,route:F.url})}catch(J){console.error("Error logger failed:",J)}let G=this.serializeError($);return new Response(JSON.stringify({error:G.error}),{status:G.status,headers:Z().contentType("json").cors({origin:L(F,this.allowedOrigins)}).build()})}handleFileCacheControl($){let F;if($?.includes("text/html"))F="public, max-age=0, must-revalidate";else if($?.includes("text/css")||$?.includes("text/javascript")||$?.includes("application/javascript"))F="public, max-age=31536000, immutable";else F="public, max-age=86400, stale-while-revalidate=604800";return F}async handleRequest($,F){try{if($.method==="OPTIONS")return new Response(null,{status:204,headers:Z().cors({origin:L($,this.allowedOrigins)}).build()});let J=new URL($.url).pathname;if(J=J.startsWith(this.prefix)?J.slice(this.prefix.length):J,J=J.startsWith("/")?J:"/"+J,J==="/ws"&&F){if(this.debug)console.log("\uD83D\uDD0C WebSocket upgrade requested");return this.handleWebSocketUpgrade($,F)}let W=J==="/"?[]:J.split("/").filter(Boolean),_=this.findRoute(W);if(!_)return new Response("Not Found",{status:404,headers:Z().contentType("text").cors({origin:L($,this.allowedOrigins)}).build()});let{route:X}=_,Y=X._timeout??this.requestTimeout,D=new Promise((j,V)=>{setTimeout(()=>V(Error("Request timeout")),Y)});return await Promise.race([this._handleRequest($,F,_),D])}catch(G){return this.handleError(G,$)}}async _handleRequest($,F,G){try{if(!G)return new Response("Not Found",{status:404,headers:Z().contentType("text").cors({origin:L($,this.allowedOrigins)}).build()});let{params:J,route:W}=G;if(!W.handler)return new Response("Handler not implemented",{status:501,headers:Z().contentType("text").cors({origin:L($,this.allowedOrigins)}).build()});let _=await this.contextCreator($);Object.assign(_,{req:$,params:J,publishToProcedure:async(j,V,U)=>{await this.publishToProcedure(j,V,_,U)}});for(let j of this.globalMiddlewares){let V=await j(_);if(V!==null&&V!==void 0&&typeof V==="object")Object.assign(_,V)}for(let j of W.middlewares){let V=await j(_);if(V!==null&&V!==void 0&&typeof V==="object")Object.assign(_,V)}let X=await this.parseInput($,W._input,W._type),Y=await W.handler({ctx:_,input:X}),D=Z().cors({origin:L($,_.allowedOrigins??this.allowedOrigins)}).build();return Object.keys(D).forEach((j)=>{_.headers.set(j,D[j])}),await this.handleProcedureResponse(W._type,Y,_.headers)}catch(J){return this.handleError(J,$)}}async testRequest($){return this.handleRequest($)}async handleProcedureResponse($,F,G){switch($){case"file":if(F instanceof Response)return F;let J=F.type;return G.append("Content-Type",J),G.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(J)),new Response(F,{headers:G});case"fileStream":if(typeof F==="object"&&F!==null&&"type"in F&&F.type==="stream"){let _=F;return await T(F.file,F.request,this.allowedOrigins,_.options)}let W=F.type;return G.append("Content-Type",W),G.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(W)),new Response(F,{headers:G});case"html":if(typeof F==="string")return G.append("Content-Type","text/html"),new Response(F,{headers:G});break;case"query":case"mutation":case"formMutation":case"streamQuery":case"streamMutation":case"subscription":default:return G.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:G})}return G.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:G})}cleanupStaleConnections(){let $=Date.now();for(let F of this.activeConnections)if($-F.data.lastActivity>this.connectionTimeout)F.close(),this.activeConnections.delete(F)}async handleWebSocketUpgrade($,F){if(this.activeConnections.size>=this.maxConnections)return new Response("Too many connections",{status:503});let G=await this.createWebSocketContext($);if(F.upgrade($,{data:{ctx:G,topics:new Set,lastActivity:Date.now()}}))return new Response(null,{status:101});return new Response("WebSocket upgrade failed",{status:500})}async createWebSocketContext($){let F=await this.contextCreator($);return Object.assign(F,{req:$}),await this.runMiddlewares(this.globalMiddlewares,F),F}createWebSocketOperationContext($,F){return{...$,params:F}}serializeError($){let F=$,G=F?.issues??F?.errors;if(Array.isArray(G)&&F.name==="ZodError")return{status:400,error:{name:"ValidationError",code:"BAD_REQUEST",clientCode:"VALIDATION_ERROR",message:"Input validation failed",data:{validationErrors:G.map((J)=>({path:J.path.join("."),message:J.message,code:J.code,received:J.received}))},httpStatus:400}};if($ instanceof A)return{status:$.httpStatus,error:$.toJSON()};if($ instanceof Error){let J=$.message==="Not Found"?404:$.message==="Unauthorized"?401:$.message==="Forbidden"?403:$.message==="Request payload too large"?413:$.message==="Request timeout"?504:500;return{status:J,error:{code:J===404?"NOT_FOUND":J===401?"UNAUTHORIZED":J===403?"FORBIDDEN":J===413?"PAYLOAD_TOO_LARGE":J===504?"GATEWAY_TIMEOUT":"INTERNAL_SERVER_ERROR",message:J<500?$.message:"Internal Server Error"}}}return{status:500,error:{code:"INTERNAL_SERVER_ERROR",message:"Internal Server Error"}}}sendWebSocketError($,F,G,J){let W=F==="authenticate"?"auth_error":F==="subscribe"?"subscribe_error":F==="unsubscribe"?"unsubscribe_error":F==="publish"?"publish_error":"error",_=this.serializeError(J);$.send(JSON.stringify({type:W,...G?{topic:G}:{},error:{..._.error,httpStatus:_.error.httpStatus??_.status}}))}async handleAuthenticate($,F,G){try{let J=F.startsWith("Bearer ")?F.substring(7):F,W=new Headers(G.req.headers);if(J)W.set("authorization",`Bearer ${J}`);else W.delete("authorization");let _=new Request(G.req,{headers:W});if($.data.ctx=await this.createWebSocketContext(_),$.send(JSON.stringify({type:"auth_success",message:"Authentication successful"})),this.debug)console.log("[BRPC]","WebSocket authenticated successfully")}catch(J){if(this.sendWebSocketError($,"authenticate",void 0,J),this.debug)console.error("WebSocket authentication error:",J)}}async handleSubscribe($,F,G){let J=this.findRoute(F.split("/").filter(Boolean));if(!J||J.route._type!=="subscription")throw Error("Invalid subscription route");let W=this.createWebSocketOperationContext(G,J.params);await this.runMiddlewares(J.route.subscribeMiddlewares??[],W),$.subscribe(F),$.data.topics.add(F),$.send(JSON.stringify({type:"subscribe_success",topic:F}))}async handleUnsubscribe($,F,G){let J=this.findRoute(F.split("/").filter(Boolean));if(!J||J.route._type!=="subscription")throw Error("Invalid subscription route");$.unsubscribe(F),$.data.topics.delete(F)}async handlePublish($,F,G,J){let W=this.findRoute(F.split("/").filter(Boolean));if(!W)throw Error("Subscription not found");let{params:_,route:X}=W;if(X._type!=="subscription"||!X.handler)throw Error("Invalid subscription route");let Y=this.createWebSocketOperationContext(J,_);await this.runMiddlewares(X.middlewares,Y);let D=X._input.fields?X._input.parse(G):G,j=await X.handler({ctx:Y,input:D}),V={topic:F,data:j,type:"publish"};if(this.debug)console.log(`Server will publish to ${F}`,V);this.server.publish(F,JSON.stringify(V))}async publishToProcedure($,F,G,J){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let W=this.resolveSubscriptionTopic($,J),_=await $.handler({ctx:{...G,params:J},input:F}),X={topic:W,data:_,type:"publish"};if(this.debug)console.log(`Server will publish to ${W}`,X);return this.server?.publish(W,JSON.stringify(X)),_}async publish($,F,G){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let J=this.resolveSubscriptionTopic($,G),W=await $.handler({ctx:{params:G},input:F}),_={topic:J,data:W,type:"publish"};if(this.debug)console.log(`Server will publish to ${J}`,_);return this.server?.publish(J,JSON.stringify(_)),W}freePublish($,F){let G={topic:$,data:F,type:"publish"};this.server?.publish($,JSON.stringify(G))}findRoutePath($,F,G=""){for(let[J,W]of Object.entries($)){let _=G?`${G}/${J}`:J;if(W===F)return _;if(W&&typeof W==="object"&&!("_type"in W)){let X=this.findRoutePath(W,F,_);if(X)return X}}return null}resolveSubscriptionTopic($,F){let G=this.findRoutePath(this.routes,$);if(!G)throw Error("Subscription route not found");return G.split("/").map((J)=>{if(!J.startsWith(":"))return J;let W=J.slice(1),_=F?.[W];if(!_)throw Error(`Missing parameter: ${W}`);return encodeURIComponent(_)}).join("/")}getIntegrationRoutes(){if(!this.integrations)return;let $={};if(this.integrations.betterAuth){let F=this.integrations.betterAuth.handler;if(!F)throw Error("Please provide a handler for betterAuth integration");$["/api/auth/*"]=async(G)=>{return await F(G)}}if(this.integrations.rawRoutes)Object.keys(this.integrations.rawRoutes).forEach((F)=>{$[F]=this.integrations.rawRoutes[F]});return Object.keys($).length>0?$:void 0}getRouteStats(){return{cache:this.routeMatcher.getCacheStats(),suggestions:this.routeMatcher.getOptimizationSuggestions()}}clearRouteCache(){if(this.routeMatcher.clearCache(),this.debug)console.log("[ROUTEMATCHER] Route cache cleared")}benchmarkRoutes($=["/users/123","/posts/456/comments"],F=1000){if(this.debug){let G=this.routeMatcher.benchmark($,F);return console.log("[ROUTEMATCHER]","Route matching benchmark:",G),G}}updateRoutes($){if(this.development)this.routes=$,this.routeMatcher=new M($,{enableCaching:!1,debug:this.debug}),console.log("[HMR]","Server Routes updated");else console.warn("Route updates are only allowed in development mode")}get url(){return this.server?.url.toString()??null}listen($,F){if(this.server=Bun.serve({port:$,routes:this.getIntegrationRoutes(),fetch:(G,J)=>this.handleRequest(G,J),websocket:{open:(G)=>{if(this.activeConnections.add(G),this.wsConfig?.onOpen)this.wsConfig.onOpen(G,G.data.ctx);if(this.debug)console.log("[BRPC]","WebSocket connection opened",{connections:this.activeConnections.size})},message:async(G,J)=>{let W,_;try{G.data.lastActivity=Date.now();let X=JSON.parse(J),{token:Y,data:D}=X;W=X.type,_=X.topic;let j=G.data.ctx;if(this.debug)console.log("[BRPC]","WebSocket message received:",{type:W,topic:_});switch(W){case"authenticate":await this.handleAuthenticate(G,Y,j);break;case"subscribe":if(!_)throw A.badRequest("Subscription topic is required");await this.handleSubscribe(G,_,j);break;case"unsubscribe":if(!_)throw A.badRequest("Subscription topic is required");await this.handleUnsubscribe(G,_,j);break;case"publish":if(!_)throw A.badRequest("Subscription topic is required");await this.handlePublish(G,_,D,j);break;default:if(this.sendWebSocketError(G,W,_,A.badRequest("Unknown message type")),this.debug)console.error("Unknown WebSocket message type:",W)}}catch(X){if(this.debug)console.error("[BRPC]","WebSocket message error:",X);this.sendWebSocketError(G,W,_,X)}},close:(G,J,W)=>{if(this.activeConnections.delete(G),G.data.topics.forEach((_)=>{G.unsubscribe(_)}),this.debug)console.log("[BRPC]","WebSocket connection closed",{code:J,reason:W,remainingConnections:this.activeConnections.size});if(this.wsConfig?.onClose)this.wsConfig.onClose(G,J,W,G.data.ctx)}}}),F)F()}stop(){this.server?.stop(!0),this.server=null,this.activeConnections.clear()}}function a($){return new H($)}function r($){return $}import{z as x}from"zod";var P={image:["image/jpeg","image/png","image/gif","image/webp","image/svg+xml"],video:["video/mp4","video/webm","video/ogg"],audio:["audio/mpeg","audio/ogg","audio/wav"],document:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]},B=($,F="MB")=>{switch(F){case"MB":return $*1024*1024;case"KB":return $*1024;case"B":return $}},F$=({acceptedTypes:$,maxSize:F=1/0,minSize:G,messages:J={}}={})=>{let W=F?B(F):1/0,_=G?B(G):0,X=$?Object.entries($).flatMap(([D,j])=>{let V=D;if(j==="*")return P[V];if(Array.isArray(j))return j;return[]}):void 0,Y=x.instanceof(File,{message:J.required??"File is required"});if(Y=Y.refine((D)=>D.size<=W,J.maxSize??`File size must be less than ${F}MB`).refine((D)=>D.size>=_,J.minSize??`File size must be at least ${G}MB`),X?.length)Y=Y.refine((D)=>X.includes(D.type),J.type??`File must be of type: ${X.join(", ")}`);return Y};var J$=($)=>{let F=$.paths.map((J)=>new RegExp(J)),G="Not Found";return async(J)=>{let _=new URL(J.req.url).pathname;if(F.some((X)=>X.test(_)))throw Error("Not Found")}};class E{requests=new Map;lastCleanupTime=Date.now();alertThreshold;config;constructor($){this.config={windowMs:$.windowMs,maxRequests:$.maxRequests,maxEntries:$.maxEntries??1e4,cleanupIntervalMs:$.cleanupIntervalMs??60000,message:$.message??"Too Many Requests",statusCode:$.statusCode??429,headerPrefix:$.headerPrefix??"X-RateLimit"},this.alertThreshold=this.config.maxEntries*0.8}cleanup(){let $=Date.now(),F=0;for(let[G,J]of this.requests.entries()){let W=$-J.startTime>this.config.windowMs,_=$-J.lastAccessed>this.config.windowMs*2;if(W||_)this.requests.delete(G),F++}return this.lastCleanupTime=$,F}handleMaxEntries($){let F=this.requests.size;if(Date.now()-this.lastCleanupTime>this.config.cleanupIntervalMs)this.cleanup();if(F>=this.alertThreshold)console.warn(`Rate limiter at ${Math.round(F/this.config.maxEntries*100)}% capacity`);if(this.requests.has($))return!0;return F<this.config.maxEntries}check($){if(!this.handleMaxEntries($))return{isLimited:!0,remaining:0,resetTime:Date.now()+this.config.windowMs};let F=Date.now(),G=this.requests.get($);if(!G||F-G.startTime>=this.config.windowMs)return this.requests.set($,{count:1,startTime:F,lastAccessed:F}),{isLimited:!1,remaining:this.config.maxRequests-1,resetTime:F+this.config.windowMs};G.lastAccessed=F;let J=G.count>=this.config.maxRequests;if(!J)G.count++;return{isLimited:J,remaining:Math.max(0,this.config.maxRequests-G.count),resetTime:G.startTime+this.config.windowMs}}getConfig(){return this.config}}var W$=($)=>{let F=new E($);return async(G)=>{if(G.req.headers.get("range"))return;let J=G.req.headers.get("x-forwarded-for")?.split(",")[0]||G.req.headers.get("x-real-ip")||"unknown",{isLimited:W,remaining:_,resetTime:X}=F.check(J),{headerPrefix:Y}=F.getConfig();if(G.headers.set(`${Y}-Remaining`,_.toString()),G.headers.set(`${Y}-Reset`,X.toString()),W)throw G.headers.set(`${Y}-Exceeded`,"true"),Error(F.getConfig().message)}};function Y$($,F){return F??$}async function D$($,F){let G=performance.now();try{return await F()}catch(J){let W=performance.now();throw console.log(`[PERF] ${$} failed after ${(W-G).toFixed(4)} ms`),J}finally{let J=performance.now();console.log(`[PERF] ${$}: ${(J-G).toFixed(4)} ms`)}}export{y as streamMedia,p as quickHeaders,D$ as measure,z as hashContent,K as getMimeType,L as getCorsOrigin,a as createRouter,W$ as createRateLimiter,k as createProcedure,J$ as createPathBlocker,Y$ as createMiddleware,F$ as createFileSchema,r as createContext,l as commonHeaders,Z as buildHeaders,H as Router,A as BRPCError};
7
+ if(e.wildcard&&t.length===0)return{params:{...n,wildcardPath:""},route:e.wildcard.handler};return null}let i=t[r],s=decodeURIComponent(i),o=e.static.get(s);if(o){let a=this.traverseTree(o,t,r+1,n);if(a)return a}if(e.param){let a={...n,[e.param.name]:s},l=this.traverseTree(e.param.node,t,r+1,a);if(l)return l}if(e.wildcard){let a=t.slice(r).join("/");return{params:{...n,wildcardPath:a},route:e.wildcard.handler}}return null}compileRoutes(e,t=[]){let r={static:new Map};for(let[n,i]of Object.entries(e)){if(n==="index"&&this.isProcedure(i)){r.index={handler:i,paramNames:[...t],hasWildcard:!1};continue}if(n==="*"){if(this.isProcedure(i))r.wildcard={handler:i,paramNames:[...t,"wildcardPath"],hasWildcard:!0,wildcardIndex:t.length};continue}if(this.isProcedure(i)){let s={handler:i,paramNames:[...t],hasWildcard:!1};if(n.startsWith(":")){let o=n.slice(1),a={static:new Map,handler:s};r.param={name:o,node:a}}else{let o={static:new Map,handler:s};r.static.set(n,o)}}else if(typeof i==="object"&&i!==null)if(n.startsWith(":")){let s=n.slice(1),o=this.compileRoutes(i,[...t,s]);r.param={name:s,node:o}}else{let s=this.compileRoutes(i,t);r.static.set(n,s)}}return r}isProcedure(e){return e&&typeof e==="object"&&"_type"in e&&"_input"in e}cacheResult(e,t){if(this.routeCache.size>=this.options.maxCacheSize){let r=this.routeCache.keys().next().value;if(r!==void 0){if(this.routeCache.delete(r),this.options.debug)console.log(`\uD83D\uDDD1\uFE0F Evicted oldest cache entry: ${r}`)}}this.routeCache.set(e,t)}analyzeRoutes(e){let t={totalRoutes:0,staticRoutes:0,paramRoutes:0,wildcardRoutes:0,indexRoutes:0,maxDepth:0,procedureTypes:{query:0,mutation:0,subscription:0,file:0,fileStream:0,streamQuery:0,streamMutation:0,formMutation:0,html:0}},r=(n,i=0)=>{t.maxDepth=Math.max(t.maxDepth,i);for(let[s,o]of Object.entries(n))if(this.isProcedure(o)){if(t.totalRoutes++,o._type in t.procedureTypes)t.procedureTypes[o._type]++;if(s==="index")t.indexRoutes++;else if(s.startsWith(":"))t.paramRoutes++;else if(s==="*")t.wildcardRoutes++;else t.staticRoutes++}else if(typeof o==="object"&&o!==null)r(o,i+1)};return r(e),t}clearCache(){if(this.routeCache.clear(),this.options.debug)console.log("[ROUTEMATCHER]","\uD83E\uDDF9 Route cache cleared")}getCacheStats(){return{size:this.routeCache.size,maxSize:this.options.maxCacheSize,routeStats:this.routeStats}}getOptimizationSuggestions(){let e=[],{routeStats:t}=this;if(t.paramRoutes>t.staticRoutes*2)e.push("\uD83D\uDE80 Consider grouping routes to reduce parameter route overhead");if(t.maxDepth>6)e.push("\uD83D\uDCCA Deep route nesting detected - consider flattening some routes");if(t.totalRoutes>200)e.push("\u26A1 Large route table - consider route splitting or lazy loading");if(!this.options.enableCaching&&t.totalRoutes>50)e.push("\uD83D\uDCBE Enable caching for better performance with many routes");if(t.procedureTypes.file>20)e.push("\uD83D\uDCC1 Many file routes detected - consider static file serving");if(t.procedureTypes.subscription>10)e.push("\uD83D\uDD04 Many subscription routes - ensure WebSocket optimization");return e}findRoutesByPattern(e){return[]}getRouteInfo(e){let t=e.split("/").filter(Boolean),r=t.join("/"),n=this.routeCache.has(r),i=performance.now(),s=this.matchDirect(t),o=performance.now()-i;return{found:!!s,route:s?.route,params:s?.params,cached:n,matchTime:o}}benchmark(e,t=1000){let r=e.map((d)=>d.split("/").filter(Boolean)),n=new Map(this.routeCache);this.routeCache.clear();let i=performance.now();for(let d=0;d<t;d++)for(let m of r)this.matchDirect(m);let o=performance.now()-i,a=t*e.length,l=o/a,u=a/o*1000;return this.routeCache=n,{averageTime:l,totalTime:o,routesPerSecond:u}}warmupCache(e){if(!this.options.enableCaching)return;let t=performance.now(),r=0;for(let i of e){let s=i.split("/").filter(Boolean),o=this.matchDirect(s);if(o)this.cacheResult(s.join("/"),o),r++}let n=performance.now();if(this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDD25 Cache warmed up: ${r} routes in ${(n-t).toFixed(2)}ms`)}}var M=import.meta.require,P={NO_CACHE:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},ONE_YEAR:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMMUTABLE:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},ONE_MONTH:{cacheControl:"public, max-age=2592000"},ONE_WEEK:{cacheControl:"public, max-age=604800"},ONE_DAY:{cacheControl:"public, max-age=86400, must-revalidate"},ONE_HOUR:{cacheControl:"public, max-age=3600, must-revalidate"},FIVE_MINUTES:{cacheControl:"public, max-age=300"},API:{cacheControl:"public, max-age=300, s-maxage=3600"},STYLESHEET:{cacheControl:"public, max-age=2592000, must-revalidate"},JAVASCRIPT:{cacheControl:"public, max-age=2592000, must-revalidate"},HASHED_ASSET:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},FONT:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMAGE:{cacheControl:"public, max-age=2592000"},FAVICON:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},HTML_PAGE:{cacheControl:"public, max-age=3600, must-revalidate"},API_RESPONSE:{cacheControl:"public, max-age=300, s-maxage=900"},FEED:{cacheControl:"public, max-age=3600"},SITEMAP:{cacheControl:"public, max-age=86400"},MANIFEST:{cacheControl:"public, max-age=86400, must-revalidate"},SERVICE_WORKER:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},MEDIA_STREAM:{cacheControl:"no-cache, no-store"},DOCUMENT:{cacheControl:"public, max-age=86400"},ARCHIVE:{cacheControl:"public, max-age=604800"}};function _(e){return P[e]}var I={html:"text/html; charset=utf-8",css:"text/css",javascript:"application/javascript",json:"application/json",xml:"application/xml",text:"text/plain; charset=utf-8",csv:"text/csv",markdown:"text/markdown; charset=utf-8",png:"image/png",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",webp:"image/webp",icon:"image/x-icon",bmp:"image/bmp",woff:"font/woff",woff2:"font/woff2",truetype:"font/ttf","embedded-opentype":"application/vnd.ms-fontobject",opentype:"font/otf",mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",mpeg:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",zip:"application/zip",tar:"application/x-tar",gzip:"application/gzip","7z":"application/x-7z-compressed",js:"application/javascript",txt:"text/plain; charset=utf-8",jpg:"image/jpeg",ico:"image/x-icon",ttf:"font/ttf",eot:"application/vnd.ms-fontobject",otf:"font/otf",mp3:"audio/mpeg",gz:"application/gzip"};function w(e){return I[e]||"application/octet-stream"}function A(e){if(typeof Bun<"u"&&Bun.hash)return Bun.hash(e).toString(16);try{let t=M("crypto"),r;if(typeof e==="string")r=Buffer.from(e);else if(e instanceof ArrayBuffer)r=Buffer.from(new Uint8Array(e));else r=Buffer.from(e);return t.createHash("md5").update(r).digest("hex")}catch{let t=typeof e==="string"?new TextEncoder().encode(e):e instanceof ArrayBuffer?new Uint8Array(e):e,r=0;for(let n=0;n<t.length;n++)r=(r<<5)-r+t[n],r=r&r;return Math.abs(r).toString(16).padStart(16,"0")}}function h(e,t,r){let n=e.headers.get("Origin"),i=Reflect.get(process.env,"NODE_ENV")!=="production";if((r?.allowAll??i)&&n)return n;if(t.length===0)return n||"*";if(!n)return t[0];if(t.includes(n))return n;return t[0]}class x{headers={};contentType(e){return this.headers["Content-Type"]=w(e),this}filePath(e){let t=e.split(".").pop()?.toLowerCase()||"";return this.headers["Content-Type"]=w(t),this}mimeType(e){return this.headers["Content-Type"]=e,this}cache(e){let{cacheControl:t,expires:r}=_(e);if(this.headers["Cache-Control"]=t,r)this.headers.Expires=r;return this}eTag(e){if(!e)return this;let t;if(typeof e==="string"&&e.startsWith('"'))t=e;else t=`"${A(e)}"`;return this.headers.ETag=t,this}lastModified(e){return this.headers["Last-Modified"]=e.toUTCString(),this}contentLength(e){return this.headers["Content-Length"]=e.toString(),this}redirect(e,t=!1){return this.headers.Location=e,this.headers["X-Redirect-Type"]=t?"permanent":"temporary",this}cors({origin:e="*",methods:t=["GET","POST","PUT","DELETE","OPTIONS"],headers:r=["Content-Type","Authorization"],maxAge:n=86400,credentials:i}={}){let s=i??e!=="*";if(s&&e==="*")throw Error('CORS: Cannot use credentials with wildcard origin "*". Specify an exact origin or set credentials to false.');if(this.headers["Access-Control-Allow-Origin"]=e,this.headers["Access-Control-Allow-Methods"]=t.join(", "),this.headers["Access-Control-Allow-Headers"]=r.join(", "),s)this.headers["Access-Control-Allow-Credentials"]="true";return this.headers["Access-Control-Max-Age"]=n.toString(),this}security(e){let t={hsts:!0,noSniff:!0,frameOptions:"SAMEORIGIN",xssProtection:!0,...e};if(t.csp)this.headers["Content-Security-Policy"]=t.csp;if(t.hsts){let r=typeof t.hsts==="number"?t.hsts:31536000;this.headers["Strict-Transport-Security"]=`max-age=${r}; includeSubDomains`}if(t.noSniff)this.headers["X-Content-Type-Options"]="nosniff";if(t.frameOptions)this.headers["X-Frame-Options"]=t.frameOptions;if(t.xssProtection)this.headers["X-XSS-Protection"]="1; mode=block";return this}custom(e,t){return this.headers[e]=t,this}customHeaders(e){return Object.assign(this.headers,e),this}vary(...e){let t=this.headers.Vary,r=t?`${t}, ${e.join(", ")}`:e.join(", ");return this.headers.Vary=r,this}compress(e){if(e)this.headers["Content-Encoding"]=e;return this}build(e){let t={...this.headers};if(e&&!t["Content-Length"]){let r=typeof e==="string"?new TextEncoder().encode(e).length:e.byteLength;t["Content-Length"]=r.toString()}return t}buildHeaders(e){return new Headers(this.build(e))}}function c(){return new x}function F(e,t){let r=c().contentType(e);if(t)r.cache(t);return r.build()}var W={css:(e)=>c().contentType("css").cache("STYLESHEET").eTag(e),javascript:(e)=>c().contentType("javascript").cache("JAVASCRIPT").eTag(e),hashedAsset:(e,t)=>c().filePath(e).cache("HASHED_ASSET").eTag(t),font:(e)=>c().filePath(e).cache("FONT").cors({origin:"*"}),image:(e)=>c().filePath(e).cache("IMAGE"),favicon:()=>c().contentType("icon").cache("FAVICON"),api:()=>c().contentType("json").cache("API_RESPONSE").cors(),expensiveApi:(e)=>c().contentType("json").cache("API_RESPONSE").cors().eTag(e),realtime:()=>c().contentType("json").cache("NO_CACHE").cors(),html:()=>c().contentType("html").cache("HTML_PAGE").security(),expensiveHtml:(e)=>c().contentType("html").cache("HTML_PAGE").security().eTag(e),manifest:(e)=>c().contentType("json").cache("MANIFEST").eTag(e),serviceWorker:()=>c().contentType("javascript").cache("SERVICE_WORKER"),sitemap:(e)=>c().contentType("xml").cache("SITEMAP").eTag(e),feed:(e,t="rss")=>c().contentType("xml").cache("FEED").eTag(e).mimeType(t==="rss"?"application/rss+xml":"application/atom+xml"),video:(e)=>c().filePath(e).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),audio:(e)=>c().filePath(e).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),pdf:(e)=>c().contentType("pdf").cache("DOCUMENT").custom("Content-Disposition","inline").eTag(e),download:(e,t,r)=>c().filePath(t).cache("ARCHIVE").custom("Content-Disposition",`attachment; filename="${e}"`).eTag(r),secure:()=>c().security({csp:"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",hsts:31536000}).custom("X-Powered-By","Bun"),permanentRedirect:(e)=>c().redirect(e,!0),temporaryRedirect:(e)=>c().redirect(e,!1)};async function T(e,t,r,n={}){let{maxChunkSize:i=2097152,cacheMaxAge:s=3600,acceptedExtensions:o}=n;if(!await e.exists())return new Response("Media file not found",{status:404,headers:c().contentType("text").cors({origin:h(t,r)}).build()});if(o&&o.length>0){let C=e.name?.toLowerCase().split(".").pop();if(!C||!o.includes(C))return new Response("File type not supported",{status:415,headers:c().contentType("text").cors({origin:h(t,r)}).build()})}let l=e.size,u=t.headers.get("Range");if(!u)return new Response(e,{headers:c().filePath(e.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${s}`).cors({origin:h(t,r)}).build()});let d=u.match(/bytes=(\d*)-(\d*)/);if(!d)return new Response("Invalid Range header",{status:416,headers:c().contentType("text").cors({origin:h(t,r)}).custom("Content-Range",`bytes */${l}`).build()});let m=d[1]?parseInt(d[1]):0,O=d[2]?parseInt(d[2]):l-1,y=Math.min(O,m+i-1,l-1);if(m>=l||y>=l||m>y)return new Response("Range not satisfiable",{status:416,headers:c().contentType("text").cors({origin:h(t,r)}).custom("Content-Range",`bytes */${l}`).build()});return new Response(e.slice(m,y+1),{status:206,headers:c().filePath(e.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${s}`).cors({origin:h(t,r)}).build()})}function N(e,t,r){return{type:"stream",file:e,request:t,options:r}}class S{routes;routeMatcher;contextCreator;prefix;globalMiddlewares;server=null;wsConfig;integrations;development=Reflect.get(process.env,"NODE_ENV")!=="production";debug=!1;allowedOrigins=[];onError;activeConnections=new Set;maxConnections=parseInt(process.env.MAX_WS_CONNECTIONS??"1000");maxRequestSize=parseInt(process.env.MAX_REQUEST_SIZE??"10485760");requestTimeout=parseInt(process.env.REQUEST_TIMEOUT??"30000");constructor(e){if(this.contextCreator=e.context,this.prefix=e.prefix??"",this.routes=e.routes,this.globalMiddlewares=e.globalMiddlewares??[],this.wsConfig=e.websocket??{},this.integrations=e.integrations,this.debug=!!e.debug&&Reflect.get(process.env,"NODE_ENV")==="development",this.allowedOrigins=e.allowedOrigins??[],this.onError=e.onError,this.routeMatcher=new R(e.routes,{enableCaching:!this.development,maxCacheSize:parseInt(process.env.ROUTE_CACHE_SIZE??"1000"),debug:this.debug}),this.debug)console.log("Router initialized with route matcher"),console.log("Route cache stats:",this.routeMatcher.getCacheStats())}findRoute(e){return this.routeMatcher.match(e)}async runMiddlewares(e,t){for(let r of e){let n=await r(t);if(n&&typeof n==="object")Object.assign(t,n)}}async parseInput(e,t,r){let n={},i=new URL(e.url),s=Object.fromEntries(i.searchParams),o=e.method;if(o==="POST"){if(parseInt(e.headers.get("content-length")||"0")>this.maxRequestSize)throw Error("Request payload too large")}if(o==="GET"&&["query","streamQuery","file","fileStream","html"].includes(r))n=s;else if(o==="POST"&&["mutation","streamMutation"].includes(r))if(e.headers.get("content-type")?.includes("application/json"))n=await e.json();else throw new p({code:"BAD_REQUEST",message:"Unsupported content type"});else if(o==="POST"&&r==="formMutation"){if(!e.headers.get("content-type")?.includes("multipart/form-data"))throw new p({code:"BAD_REQUEST",message:"Unsupported content type"});n=await this.parseFormData(await e.formData())}else throw new p({code:"METHOD_NOT_SUPPORTED",message:"Unsupported request method"});return t.parse(n)}async parseFormData(e){let t=Object.create(null);return e.forEach((r,n)=>{this.setFormDataValue(t,this.parseFormDataPath(n),r)}),t}setFormDataValue(e,t,r){let n=e;for(let i=0;i<t.length;i++){let s=t[i],o=i===t.length-1,a=this.isFormDataArrayIndex(t[i+1]);if(Array.isArray(n)){if(!this.isFormDataArrayIndex(s))throw p.badRequest("Invalid multipart array path");let u=Number(s);if(u>n.length)throw p.badRequest("Multipart array indexes must be contiguous");if(o){if(u===n.length)n.push(r);else if(n[u]===void 0)n[u]=r;else throw p.badRequest("Duplicate multipart field path");return}let d=n[u];if(d===void 0)n[u]=a?[]:Object.create(null);else if(Array.isArray(d)!==a)throw p.badRequest("Conflicting multipart field path");n=n[u];continue}if(o){if(Object.hasOwn(n,s))throw p.badRequest("Duplicate multipart field path");n[s]=r;return}let l=n[s];if(l===void 0)n[s]=a?[]:Object.create(null);else if(Array.isArray(l)!==a)throw p.badRequest("Conflicting multipart field path");n=n[s]}}parseFormDataPath(e){let t=/^([^\[\]]+)((?:\[[^\[\]]+\])*)$/.exec(e);if(!t)throw p.badRequest("Invalid multipart field path");let r=[t[1]];for(let n of t[2].matchAll(/\[([^\[\]]+)\]/g))r.push(n[1]);if(r.some((n)=>["__proto__","constructor","prototype"].includes(n)))throw p.badRequest("Invalid multipart field path");return r}isFormDataArrayIndex(e){return e!==void 0&&/^(0|[1-9]\d*)$/.test(e)}handleError(e,t){if(this.onError)try{this.onError(e,{req:t,route:t.url})}catch(n){console.error("Error logger failed:",n)}let r=this.serializeError(e);return new Response(JSON.stringify({error:r.error}),{status:r.status,headers:c().contentType("json").cors({origin:h(t,this.allowedOrigins)}).build()})}handleFileCacheControl(e){let t;if(e?.includes("text/html"))t="public, max-age=0, must-revalidate";else if(e?.includes("text/css")||e?.includes("text/javascript")||e?.includes("application/javascript"))t="public, max-age=31536000, immutable";else t="public, max-age=86400, stale-while-revalidate=604800";return t}async handleRequest(e,t){try{if(e.method==="OPTIONS")return new Response(null,{status:204,headers:c().cors({origin:h(e,this.allowedOrigins)}).build()});let n=new URL(e.url).pathname;if(n=n.startsWith(this.prefix)?n.slice(this.prefix.length):n,n=n.startsWith("/")?n:"/"+n,n==="/ws"&&t){if(this.debug)console.log("\uD83D\uDD0C WebSocket upgrade requested");return this.handleWebSocketUpgrade(e,t)}let i=n==="/"?[]:n.split("/").filter(Boolean),s=this.findRoute(i);if(!s)return new Response("Not Found",{status:404,headers:c().contentType("text").cors({origin:h(e,this.allowedOrigins)}).build()});let{route:o}=s,a=o._timeout??this.requestTimeout,l=new Promise((u,d)=>{setTimeout(()=>d(Error("Request timeout")),a)});return await Promise.race([this._handleRequest(e,t,s),l])}catch(r){return this.handleError(r,e)}}async _handleRequest(e,t,r){try{if(!r)return new Response("Not Found",{status:404,headers:c().contentType("text").cors({origin:h(e,this.allowedOrigins)}).build()});let{params:n,route:i}=r;if(!i.handler)return new Response("Handler not implemented",{status:501,headers:c().contentType("text").cors({origin:h(e,this.allowedOrigins)}).build()});let s=await this.contextCreator(e);Object.assign(s,{req:e,params:n,publishToProcedure:async(u,d,m)=>{await this.publishToProcedure(u,d,s,m)}});for(let u of this.globalMiddlewares){let d=await u(s);if(d!==null&&d!==void 0&&typeof d==="object")Object.assign(s,d)}for(let u of i.middlewares){let d=await u(s);if(d!==null&&d!==void 0&&typeof d==="object")Object.assign(s,d)}let o=await this.parseInput(e,i._input,i._type),a=await i.handler({ctx:s,input:o}),l=c().cors({origin:h(e,s.allowedOrigins??this.allowedOrigins)}).build();return Object.keys(l).forEach((u)=>{s.headers.set(u,l[u])}),await this.handleProcedureResponse(i._type,a,s.headers)}catch(n){return this.handleError(n,e)}}async testRequest(e){return this.handleRequest(e)}async handleProcedureResponse(e,t,r){switch(e){case"file":if(t instanceof Response)return t;let n=t.type;return r.append("Content-Type",n),r.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(n)),new Response(t,{headers:r});case"fileStream":if(typeof t==="object"&&t!==null&&"type"in t&&t.type==="stream"){let s=t;return await T(t.file,t.request,this.allowedOrigins,s.options)}let i=t.type;return r.append("Content-Type",i),r.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(i)),new Response(t,{headers:r});case"html":if(typeof t==="string")return r.append("Content-Type","text/html"),new Response(t,{headers:r});break;case"query":case"mutation":case"formMutation":case"streamQuery":case"streamMutation":case"subscription":default:return r.append("Content-Type","application/json"),new Response(JSON.stringify({data:t}),{headers:r})}return r.append("Content-Type","application/json"),new Response(JSON.stringify({data:t}),{headers:r})}async handleWebSocketUpgrade(e,t){if(this.activeConnections.size>=this.maxConnections)return new Response("Too many connections",{status:503});let r=await this.createWebSocketContext(e);if(t.upgrade(e,{data:{ctx:r,topics:new Set}}))return new Response(null,{status:101});return new Response("WebSocket upgrade failed",{status:500})}async createWebSocketContext(e){let t=await this.contextCreator(e);return Object.assign(t,{req:e}),await this.runMiddlewares(this.globalMiddlewares,t),t}createWebSocketOperationContext(e,t){return{...e,params:t}}serializeError(e){let t=e,r=t?.issues??t?.errors;if(Array.isArray(r)&&t.name==="ZodError")return{status:400,error:{name:"ValidationError",code:"BAD_REQUEST",clientCode:"VALIDATION_ERROR",message:"Input validation failed",data:{validationErrors:r.map((n)=>({path:n.path.join("."),message:n.message,code:n.code,received:n.received}))},httpStatus:400}};if(e instanceof p)return{status:e.httpStatus,error:e.toJSON()};if(e instanceof Error){let n=e.message==="Not Found"?404:e.message==="Unauthorized"?401:e.message==="Forbidden"?403:e.message==="Request payload too large"?413:e.message==="Request timeout"?504:500;return{status:n,error:{code:n===404?"NOT_FOUND":n===401?"UNAUTHORIZED":n===403?"FORBIDDEN":n===413?"PAYLOAD_TOO_LARGE":n===504?"GATEWAY_TIMEOUT":"INTERNAL_SERVER_ERROR",message:n<500?e.message:"Internal Server Error"}}}return{status:500,error:{code:"INTERNAL_SERVER_ERROR",message:"Internal Server Error"}}}sendWebSocketError(e,t,r,n){let i=t==="authenticate"?"auth_error":t==="subscribe"?"subscribe_error":t==="unsubscribe"?"unsubscribe_error":t==="publish"?"publish_error":"error",s=this.serializeError(n);e.send(JSON.stringify({type:i,...r?{topic:r}:{},error:{...s.error,httpStatus:s.error.httpStatus??s.status}}))}async handleAuthenticate(e,t,r){try{let n=t.startsWith("Bearer ")?t.substring(7):t,i=new Headers(r.req.headers);if(n)i.set("authorization",`Bearer ${n}`);else i.delete("authorization");let s=new Request(r.req,{headers:i});if(e.data.ctx=await this.createWebSocketContext(s),e.send(JSON.stringify({type:"auth_success",message:"Authentication successful"})),this.debug)console.log("[BRPC]","WebSocket authenticated successfully")}catch(n){if(this.sendWebSocketError(e,"authenticate",void 0,n),this.debug)console.error("WebSocket authentication error:",n)}}async handleSubscribe(e,t,r){let n=this.findRoute(t.split("/").filter(Boolean));if(!n||n.route._type!=="subscription")throw Error("Invalid subscription route");let i=this.createWebSocketOperationContext(r,n.params);await this.runMiddlewares(n.route.subscribeMiddlewares??[],i),e.subscribe(t),e.data.topics.add(t),e.send(JSON.stringify({type:"subscribe_success",topic:t}))}async handleUnsubscribe(e,t,r){let n=this.findRoute(t.split("/").filter(Boolean));if(!n||n.route._type!=="subscription")throw Error("Invalid subscription route");e.unsubscribe(t),e.data.topics.delete(t)}async handlePublish(e,t,r,n){let i=this.findRoute(t.split("/").filter(Boolean));if(!i)throw Error("Subscription not found");let{params:s,route:o}=i;if(o._type!=="subscription"||!o.handler)throw Error("Invalid subscription route");let a=this.createWebSocketOperationContext(n,s);await this.runMiddlewares(o.middlewares,a);let l=o._input.fields?o._input.parse(r):r,u=await o.handler({ctx:a,input:l}),d={topic:t,data:u,type:"publish"};if(this.debug)console.log(`Server will publish to ${t}`,d);this.server.publish(t,JSON.stringify(d))}async publishToProcedure(e,t,r,n){if(e._type!=="subscription"||!e.handler)throw Error("Can only publish to subscription procedures");let i=this.resolveSubscriptionTopic(e,n),s=await e.handler({ctx:{...r,params:n},input:t}),o={topic:i,data:s,type:"publish"};if(this.debug)console.log(`Server will publish to ${i}`,o);return this.server?.publish(i,JSON.stringify(o)),s}async publish(e,t,r){if(e._type!=="subscription"||!e.handler)throw Error("Can only publish to subscription procedures");let n=this.resolveSubscriptionTopic(e,r),i=await e.handler({ctx:{params:r},input:t}),s={topic:n,data:i,type:"publish"};if(this.debug)console.log(`Server will publish to ${n}`,s);return this.server?.publish(n,JSON.stringify(s)),i}freePublish(e,t){let r={topic:e,data:t,type:"publish"};this.server?.publish(e,JSON.stringify(r))}findRoutePath(e,t,r=""){for(let[n,i]of Object.entries(e)){let s=r?`${r}/${n}`:n;if(i===t)return s;if(i&&typeof i==="object"&&!("_type"in i)){let o=this.findRoutePath(i,t,s);if(o)return o}}return null}resolveSubscriptionTopic(e,t){let r=this.findRoutePath(this.routes,e);if(!r)throw Error("Subscription route not found");return r.split("/").map((n)=>{if(!n.startsWith(":"))return n;let i=n.slice(1),s=t?.[i];if(!s)throw Error(`Missing parameter: ${i}`);return encodeURIComponent(s)}).join("/")}getIntegrationRoutes(){if(!this.integrations)return;let e={};if(this.integrations.betterAuth){let t=this.integrations.betterAuth.handler;if(!t)throw Error("Please provide a handler for betterAuth integration");e["/api/auth/*"]=async(r)=>await t(r)}if(this.integrations.rawRoutes)Object.keys(this.integrations.rawRoutes).forEach((t)=>{e[t]=this.integrations.rawRoutes[t]});return Object.keys(e).length>0?e:void 0}getRouteStats(){return{cache:this.routeMatcher.getCacheStats(),suggestions:this.routeMatcher.getOptimizationSuggestions()}}clearRouteCache(){if(this.routeMatcher.clearCache(),this.debug)console.log("[ROUTEMATCHER] Route cache cleared")}benchmarkRoutes(e=["/users/123","/posts/456/comments"],t=1000){if(this.debug){let r=this.routeMatcher.benchmark(e,t);return console.log("[ROUTEMATCHER]","Route matching benchmark:",r),r}}updateRoutes(e){if(this.development)this.routes=e,this.routeMatcher=new R(e,{enableCaching:!1,debug:this.debug}),console.log("[HMR]","Server Routes updated");else console.warn("Route updates are only allowed in development mode")}get url(){return this.server?.url.toString()??null}listen(e,t){if(this.server=Bun.serve({port:e,routes:this.getIntegrationRoutes(),fetch:(r,n)=>this.handleRequest(r,n),websocket:{open:(r)=>{if(this.activeConnections.add(r),this.wsConfig?.onOpen)this.wsConfig.onOpen(r,r.data.ctx);if(this.debug)console.log("[BRPC]","WebSocket connection opened",{connections:this.activeConnections.size})},message:async(r,n)=>{let i,s;try{let o=JSON.parse(n),{token:a,data:l}=o;i=o.type,s=o.topic;let u=r.data.ctx;if(this.debug)console.log("[BRPC]","WebSocket message received:",{type:i,topic:s});switch(i){case"authenticate":await this.handleAuthenticate(r,a,u);break;case"subscribe":if(!s)throw p.badRequest("Subscription topic is required");await this.handleSubscribe(r,s,u);break;case"unsubscribe":if(!s)throw p.badRequest("Subscription topic is required");await this.handleUnsubscribe(r,s,u);break;case"publish":if(!s)throw p.badRequest("Subscription topic is required");await this.handlePublish(r,s,l,u);break;default:if(this.sendWebSocketError(r,i,s,p.badRequest("Unknown message type")),this.debug)console.error("Unknown WebSocket message type:",i)}}catch(o){if(this.debug)console.error("[BRPC]","WebSocket message error:",o);this.sendWebSocketError(r,i,s,o)}},close:(r,n,i)=>{if(this.activeConnections.delete(r),r.data.topics.forEach((s)=>{r.unsubscribe(s)}),this.debug)console.log("[BRPC]","WebSocket connection closed",{code:n,reason:i,remainingConnections:this.activeConnections.size});if(this.wsConfig?.onClose)this.wsConfig.onClose(r,n,i,r.data.ctx)}}}),t)t()}stop(){this.server?.stop(!0),this.server=null,this.activeConnections.clear()}}function Q(e){return new S(e)}function te(e){return e}import{z as D}from"zod";var B={image:["image/jpeg","image/png","image/gif","image/webp","image/svg+xml"],video:["video/mp4","video/webm","video/ogg"],audio:["audio/mpeg","audio/ogg","audio/wav"],document:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]},E=(e,t="MB")=>{switch(t){case"MB":return e*1024*1024;case"KB":return e*1024;case"B":return e}},se=({acceptedTypes:e,maxSize:t=1/0,minSize:r,messages:n={}}={})=>{let i=t?E(t):1/0,s=r?E(r):0,o=e?Object.entries(e).flatMap(([l,u])=>{let d=l;if(u==="*")return B[d];if(Array.isArray(u))return u;return[]}):void 0,a=D.instanceof(File,{message:n.required??"File is required"});if(a=a.refine((l)=>l.size<=i,n.maxSize??`File size must be less than ${t}MB`).refine((l)=>l.size>=s,n.minSize??`File size must be at least ${r}MB`),o?.length)a=a.refine((l)=>o.includes(l.type),n.type??`File must be of type: ${o.join(", ")}`);return a};var oe=(e)=>{let t=e.paths.map((n)=>new RegExp(n)),r="Not Found";return async(n)=>{let s=new URL(n.req.url).pathname;if(t.some((o)=>o.test(s)))throw Error("Not Found")}};class v{requests=new Map;lastCleanupTime=Date.now();alertThreshold;config;constructor(e){if(this.config={windowMs:e.windowMs,maxRequests:e.maxRequests,maxEntries:e.maxEntries??1e4,cleanupIntervalMs:e.cleanupIntervalMs??60000,message:e.message??"Too Many Requests",statusCode:e.statusCode??429,headerPrefix:e.headerPrefix??"X-RateLimit"},this.alertThreshold=this.config.maxEntries*0.8,Reflect.get(process.env,"NODE_ENV")!=="development")setInterval(()=>this.cleanup(),this.config.cleanupIntervalMs)}cleanup(){let e=Date.now(),t=0;for(let[r,n]of this.requests.entries()){let i=e-n.startTime>this.config.windowMs,s=e-n.lastAccessed>this.config.windowMs*2;if(i||s)this.requests.delete(r),t++}return this.lastCleanupTime=e,t}handleMaxEntries(e){let t=this.requests.size;if(Date.now()-this.lastCleanupTime>this.config.cleanupIntervalMs)this.cleanup();if(t>=this.alertThreshold)console.warn(`Rate limiter at ${Math.round(t/this.config.maxEntries*100)}% capacity`);if(this.requests.has(e))return!0;return t<this.config.maxEntries}check(e){if(!this.handleMaxEntries(e))return{isLimited:!0,remaining:0,resetTime:Date.now()+this.config.windowMs};let t=Date.now(),r=this.requests.get(e);if(!r||t-r.startTime>=this.config.windowMs)return this.requests.set(e,{count:1,startTime:t,lastAccessed:t}),{isLimited:!1,remaining:this.config.maxRequests-1,resetTime:t+this.config.windowMs};r.lastAccessed=t;let n=r.count>=this.config.maxRequests;if(!n)r.count++;return{isLimited:n,remaining:Math.max(0,this.config.maxRequests-r.count),resetTime:r.startTime+this.config.windowMs}}getConfig(){return this.config}}var ce=(e)=>{let t=new v(e);return async(r)=>{if(r.req.headers.get("range"))return;let n=r.req.headers.get("x-forwarded-for")?.split(",")[0]||r.req.headers.get("x-real-ip")||"unknown",{isLimited:i,remaining:s,resetTime:o}=t.check(n),{headerPrefix:a}=t.getConfig();if(r.headers.set(`${a}-Remaining`,s.toString()),r.headers.set(`${a}-Reset`,o.toString()),i)throw r.headers.set(`${a}-Exceeded`,"true"),Error(t.getConfig().message)}};function pe(e,t){return t??e}async function Te(e,t){let r=performance.now();try{return await t()}catch(n){let i=performance.now();throw console.log(`[PERF] ${e} failed after ${(i-r).toFixed(4)} ms`),n}finally{let n=performance.now();console.log(`[PERF] ${e}: ${(n-r).toFixed(4)} ms`)}}export{p as BRPCError,S as Router,c as buildHeaders,W as commonHeaders,te as createContext,se as createFileSchema,pe as createMiddleware,oe as createPathBlocker,k as createProcedure,ce as createRateLimiter,Q as createRouter,h as getCorsOrigin,w as getMimeType,A as hashContent,Te as measure,F as quickHeaders,N as streamMedia};
8
8
 
9
- //# debugId=5C2A64B8EF31A30E64756E2164756E21
9
+ //# debugId=1772125FF0C8DF0464756E2164756E21
@@ -14,7 +14,6 @@ export declare class Router<C extends BaseContext> {
14
14
  private onError?;
15
15
  private activeConnections;
16
16
  private readonly maxConnections;
17
- private readonly connectionTimeout;
18
17
  private readonly maxRequestSize;
19
18
  private readonly requestTimeout;
20
19
  constructor(config: RouterConfig<C>);
@@ -74,7 +73,6 @@ export declare class Router<C extends BaseContext> {
74
73
  * Handle responses based on procedure type
75
74
  */
76
75
  private handleProcedureResponse;
77
- private cleanupStaleConnections;
78
76
  /**
79
77
  * handleWebSocketUpgrade
80
78
  * @description upgrades the incoming request to
package/dist/schemas.d.ts CHANGED
@@ -39,6 +39,10 @@ type FileSchemaOptions = {
39
39
  };
40
40
  };
41
41
  /**
42
+ * @deprecated Only while brpc upgrades to Zod 4: this is built on Zod 3, so
43
+ * apps on Zod 4 get an `unknown` output type. Use the app's own
44
+ * `createFileSchema` (template: `apps/brpc/zod/files.ts`) until then.
45
+ *
42
46
  * Creates a Zod schema for file validation with support for MIME types and size constraints
43
47
  * you can extend it as any other zod schema after calling it
44
48
  * @param {Object} options - The configuration options for the file schema
@@ -1,7 +1,7 @@
1
1
  // @bun
2
- var T=($)=>$.startsWith("image/"),w=($)=>$.startsWith("video/"),z=($)=>$.startsWith("audio/"),k=($)=>$.startsWith("text/")||$.includes("pdf")||$.includes("word")||$.includes("sheet")||$.includes("presentation")||$.includes("opendocument")||$==="application/rtf",I=($)=>$.includes("zip")||$.includes("rar")||$.includes("tar")||$.includes("7z")||$.includes("gzip"),R=($)=>$==="application/javascript"||$==="application/json"||$==="text/html"||$==="text/css"||$==="application/xml"||$.includes("javascript"),b=($)=>$.startsWith("font/")||$.includes("font"),P=($)=>{if(!$)return"other";let W=$.toLowerCase();if(T(W))return"image";if(w(W))return"video";if(z(W))return"audio";if(k(W))return"document";if(I(W))return"archive";if(R(W))return"code";if(b(W))return"font";return"other"},U={isImage:T,isVideo:w,isAudio:z,isDocument:k,isArchive:I,isCode:R,isFont:b};function $0(){let W=Buffer.from("Hello, this is a test file for S3 upload!","utf-8");return new File([W],"test.txt",{type:"text/plain"})}function q($,W){if(!$||$<1||!W||W<1)return"1:1";let H=(Q,Y)=>Y===0?Q:H(Y,Q%Y),J=H($,W);return`${$/J}:${W/J}`}function H0($,W){if(!$||!W||$<1||W<1)return"1:1";let H=$/W,J=[{ratio:1,label:"1:1"},{ratio:0.8,label:"4:5"},{ratio:0.75,label:"3:4"},{ratio:0.6666666666666666,label:"2:3"},{ratio:0.5625,label:"9:16"},{ratio:1.5,label:"3:2"},{ratio:1.3333333333333333,label:"4:3"},{ratio:1.7777777777777777,label:"16:9"},{ratio:2.3333333333333335,label:"21:9"}],Q=0.03;for(let{ratio:Z,label:_}of J)if(Math.abs(H-Z)/Z<Q)return _;let Y=(Z,_)=>_===0?Z:Y(_,Z%_),L=Y($,W);return`${$/L}:${W/L}`}class X extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=X.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,X)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,W,H){return new X({code:"BAD_REQUEST",message:$,clientCode:W,data:H})}static unauthorized($="Unauthorized",W,H){return new X({code:"UNAUTHORIZED",message:$,clientCode:W,data:H})}static forbidden($="Forbidden",W,H){return new X({code:"FORBIDDEN",message:$,clientCode:W,data:H})}static notFound($="Not Found",W,H){return new X({code:"NOT_FOUND",message:$,clientCode:W,data:H})}static preconditionFailed($="Precondition failed",W,H){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
2
+ var A=(e)=>e.startsWith("image/"),w=(e)=>e.startsWith("video/"),x=(e)=>e.startsWith("audio/"),I=(e)=>e.startsWith("text/")||e.includes("pdf")||e.includes("word")||e.includes("sheet")||e.includes("presentation")||e.includes("opendocument")||e==="application/rtf",v=(e)=>e.includes("zip")||e.includes("rar")||e.includes("tar")||e.includes("7z")||e.includes("gzip"),U=(e)=>e==="application/javascript"||e==="application/json"||e==="text/html"||e==="text/css"||e==="application/xml"||e.includes("javascript"),B=(e)=>e.startsWith("font/")||e.includes("font"),N=(e)=>{if(!e)return"other";let t=e.toLowerCase();if(A(t))return"image";if(w(t))return"video";if(x(t))return"audio";if(I(t))return"document";if(v(t))return"archive";if(U(t))return"code";if(B(t))return"font";return"other"},y={isImage:A,isVideo:w,isAudio:x,isDocument:I,isArchive:v,isCode:U,isFont:B};function te(){let t=Buffer.from("Hello, this is a test file for S3 upload!","utf-8");return new File([t],"test.txt",{type:"text/plain"})}function b(e,t){if(!e||e<1||!t||t<1)return"1:1";let r=(n,o)=>o===0?n:r(o,n%o),a=r(e,t);return`${e/a}:${t/a}`}function ae(e,t){if(!e||!t||e<1||t<1)return"1:1";let r=e/t,a=[{ratio:1,label:"1:1"},{ratio:0.8,label:"4:5"},{ratio:0.75,label:"3:4"},{ratio:0.6666666666666666,label:"2:3"},{ratio:0.5625,label:"9:16"},{ratio:1.5,label:"3:2"},{ratio:1.3333333333333333,label:"4:3"},{ratio:1.7777777777777777,label:"16:9"},{ratio:2.3333333333333335,label:"21:9"}],n=0.03;for(let{ratio:l,label:d}of a)if(Math.abs(r-l)/l<n)return d;let o=(l,d)=>d===0?l:o(d,l%d),i=o(e,t);return`${e/i}:${t/i}`}class s extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor(e){super(e.message);if(this.name="BRPCError",this.code=e.code,this.clientCode=e.clientCode,this.httpStatus=s.STATUS_MAP[e.code],this.data=e.data,this.cause=e.cause,Error.captureStackTrace)Error.captureStackTrace(this,s)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest(e,t,r){return new s({code:"BAD_REQUEST",message:e,clientCode:t,data:r})}static unauthorized(e="Unauthorized",t,r){return new s({code:"UNAUTHORIZED",message:e,clientCode:t,data:r})}static forbidden(e="Forbidden",t,r){return new s({code:"FORBIDDEN",message:e,clientCode:t,data:r})}static notFound(e="Not Found",t,r){return new s({code:"NOT_FOUND",message:e,clientCode:t,data:r})}static preconditionFailed(e="Precondition failed",t,r){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
3
3
  //! but the exact cause is unknown. Suspected: some client/proxy/middleware misbehaved on 412
4
4
  //! (not a browser auto-retry issue — 412 doesn't trigger those). Investigate before changing back.
5
- return new X({code:"NOT_FOUND",message:$,clientCode:W,data:H})}static conflict($,W,H){return new X({code:"CONFLICT",message:$,clientCode:W,data:H})}static unprocessableContent($,W,H){return new X({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:W,data:H})}static tooManyRequests($="Too many requests",W,H){return new X({code:"TOO_MANY_REQUESTS",message:$,clientCode:W,data:H})}static internalServerError($="Internal Server Error",W,H){return new X({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:W,data:H})}static timeout($="Request timeout",W,H){return new X({code:"TIMEOUT",message:$,clientCode:W,data:H})}}import m from"sharp";var E={xs:200,sm:400,md:800,lg:1200,xl:1920},A=1920;async function S($,W,H,J,Q){try{let{data:Y,info:L}=await m($,{limitInputPixels:1e8}).rotate().resize({width:W,height:W,fit:"inside",withoutEnlargement:!0}).webp({quality:H}).toBuffer({resolveWithObject:!0});return{buffer:Y,object:{name:J.replace(/\.[^/.]+$/,".webp"),resolvedType:"image",metadata:{type:"image/webp",size:Y.length,extension:".webp",width:L.width,height:L.height,aspectRatio:L.width&&L.height?L.width/L.height:1,aspectRatioStr:L.width&&L.height?q(L.width,L.height):"1:1",acl:Q}}}}catch(Y){throw X.conflict(`Failed to optimize image: ${Y instanceof Error?Y.message:"Unknown error"}`)}}async function B($){if(!$)throw X.conflict("A file must be provided");let W,H="unknown.webp";if(typeof $==="string"){if(console.log("I'm going to fail here for file",$),!$.startsWith("data:"))throw console.log("I never got here"),X.conflict("Invalid base64 format");let[,J]=$.split(",");if(!J)throw X.conflict("Invalid base64 data");W=Buffer.from(J,"base64")}else{if(!U.isImage($.type))throw X.conflict("File must be an image");let J=await $.arrayBuffer();W=Buffer.from(J),H=$.name??H}return{buffer:W,name:H}}async function x0($,{size:W,quality:H,acl:J}={}){let Q=await B($);return S(Q.buffer,W??A,H??80,Q.name,J??"public-read")}async function D0($,{name:W="unknown.webp",size:H,quality:J,acl:Q}={}){if(!$?.length)throw X.conflict("A buffer must be provided");return S($,H??A,J??80,W,Q??"public-read")}async function v($,{quality:W=80,acl:H="public-read"}={}){let J=await B($),Q=await Promise.all(Object.entries(E).map(async([Y,L])=>{let Z=await S(J.buffer,L,W,J.name,H);return[Y,Z]}));return{variants:Object.fromEntries(Q)}}import n from"sharp";import{randomUUID as y}from"crypto";import{extname as u}from"path";async function C($,W,H){if(!W)return{data:null,error:Error("File not found")};let J=$.replace(/^\/+|\/+$/g,""),Q=u(W.name),Y=`${y()}${Q}`,L=`${J}/${Y}`,Z=`./buckets/${J}/${Y}`;try{let _=await Bun.write(Z,W);return{data:{key:L,file:W,bytesWritten:_},error:null}}catch(_){return{data:null,error:Error(`Failed to upload ${W.name} to local filesystem`)}}}async function f($,W,H){if(!W||W.length===0)return[];let J=W.map((Y)=>{let L=H?.(Y)??Y.name;return C($,Y,L)});return await Promise.all(J)}async function d($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return null;return W}async function p($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return!1;return await W.delete(),!0}var V={uploadOne:C,uploadMany:f,getOne:d,deleteOne:p};async function S0($){let W=`./cache/images/${$.key}_${$.width}_${$.quality}.webp`,H=await V.getOne(W);if(H){if(!U.isImage(H.type))return null;return H}let J=$.key,Q=await V.getOne(J);if(!Q)return null;if(!U.isImage(Q.type))return null;let Y=await Q.arrayBuffer(),L=await n(Y).resize($.width,null,{withoutEnlargement:!0}).webp({quality:$.quality??75}).toBuffer();await Bun.write(W,L);let Z=Bun.file(W);if(!await Z.exists())return null;return Z}import s from"sharp";async function T0($){if(!$?.length)return null;try{let W=await s($).metadata(),H=W.width??0,J=W.height??0,Q=W.height>0?H/J:1;return{type:`image/${W.format}`,size:W.size??0,extension:`.${W.format}`,width:H,height:J,aspectRatio:Q,aspectRatioStr:q(H,J)}}catch(W){return console.warn("Failed to read image metadata:",W),null}}var g=process.env.AWS_FOLDER,h=g?`${g}/`:"";var{s3:i}=globalThis.Bun;import{extname as t}from"path";function c($={}){return $}var{s3:l}=globalThis.Bun;function o($){return[...new Set($.flatMap(({variants:W})=>Object.values(W).flatMap((H)=>H&&typeof H==="object"&&("key"in H)&&typeof H.key==="string"&&H.key.length>0?[H.key]:[])))]}async function r($,W){if(!$||typeof $!=="string")throw X.badRequest("Key is required");let H=$.trim();if(!H||H.length===0)throw X.badRequest("Key must be a non-empty string");try{return await l.delete(H),{key:H,deleted:!0,error:null}}catch(J){return{key:$,deleted:!1,error:J}}}async function F($,W){return(await Promise.allSettled($.map((J)=>r(J,W)))).map((J,Q)=>{if(J.status==="fulfilled")return J.value;return{key:$[Q],deleted:!1,error:J.reason}})}async function b0($,W){return F(o($),W)}async function N($,W,H){let J="buffer"in W?W:{buffer:W,object:{name:W.name,resolvedType:P(W.type),metadata:{type:W.type,extension:t(W.name),size:W.size,acl:H?.acl??"public-read"}}};if("buffer"in W){if(!W.object?.name||!W.object?.metadata)throw X.badRequest("Complete metadata required when uploading buffer")}let Q=$.length>1?`${$.replace(/^\/+|\/+$/g,"")}/`:"",Y=J.object.metadata.extension.startsWith(".")?J.object.metadata.extension:`.${J.object.metadata.extension}`,L=J.object.name?J.object.name:`${crypto.randomUUID()}${Y}`,Z=`${h}${Q}${L}`,_=i.file(Z),G=J?.object?.metadata?.acl??"public-read",M=G!=="public-read"&&G!=="public-read-write",D=await _.write(J.buffer,{acl:G,type:J.object.metadata.type});return{uploadedBy:H?.uploadedBy,key:Z,name:J.object.name,resolvedType:J.object.resolvedType,provider:"s3",isPrivate:M,metadata:{...J.object.metadata,size:D,acl:G},isActive:!0}}async function y0($,W,H={throwIf:"any"}){let J=await Promise.allSettled(W.map((L)=>N($,L,{acl:H?.acl,uploadedBy:H?.uploadedBy}))),Q=[],Y=[];if(J.forEach((L)=>{if(L.status==="fulfilled"){if(Q.push(L.value),H?.externalKeys)H.externalKeys.push(L.value.key)}else Y.push(L.reason)}),H?.throwIf==="any"&&Y.length>0||H?.throwIf==="all"&&Y.length===W.length)throw X.internalServerError(`Upload failed: ${Y.length} of ${W.length}`);return{data:Q,errors:Y.length?Y:void 0}}async function a($,W,H){let J=H?.baseId??crypto.randomUUID(),Q=Object.values(W.variants).find(Boolean),Y=await Promise.allSettled(Object.entries(W.variants).map(async([M,D])=>{if(!D)return[M,null];let K=D.object.metadata.extension,j=`${J}_${M}${K}`,O=await N($,{...D,object:{...D.object,name:j}},H);if(!O.metadata)throw X.internalServerError("Upload returned no metadata, cannot build a storage variant");let x=O.metadata;return[M,{key:O.key,name:j,metadata:{type:x.type,extension:x.extension,size:x.size,hash:x.hash,width:x.width,height:x.height,aspectRatio:x.aspectRatio,aspectRatioStr:x.aspectRatioStr,resolution:x.resolution,bitrate:x.bitrate,codec:x.codec,fps:x.fps}}]})),L=Y.flatMap((M)=>M.status==="fulfilled"?[M.value]:[]),Z=Y.flatMap((M)=>M.status==="rejected"?[M.reason]:[]);if(Z.length>0){let M=L.flatMap(([,j])=>j?.key?[j.key]:[]),K=(await F(M)).filter(({deleted:j})=>!j);if(K.length>0)console.error("Failed to clean up partially uploaded image variants",{keys:M,failures:K});throw Z[0]}let _=(H?.acl??"public-read")!=="public-read"&&(H?.acl??"public-read")!=="public-read-write",G={};for(let[M,D]of L)if(D)G[M]=D;return{shared:{name:Q.object.name,resolvedType:Q.object.resolvedType,provider:"s3",acl:H?.acl??"public-read",isPrivate:_,uploadedBy:H?.uploadedBy},variants:G}}async function u0($,W,H){let J=await N($,W,H);if(!J.metadata)throw X.internalServerError("Upload returned no metadata, cannot build a storage object");let Q=J.name??J.key.split("/").pop()??J.key;return{shared:{name:Q,resolvedType:J.resolvedType,provider:J.provider,acl:J.metadata.acl,isPrivate:J.isPrivate,uploadedBy:H?.uploadedBy},variants:c({default:{key:J.key,name:Q,metadata:{type:J.metadata.type,extension:J.metadata.extension,size:J.metadata.size,hash:J.metadata.hash,width:J.metadata.width,height:J.metadata.height,aspectRatio:J.metadata.aspectRatio,aspectRatioStr:J.metadata.aspectRatioStr,resolution:J.metadata.resolution,bitrate:J.metadata.bitrate,codec:J.metadata.codec,fps:J.metadata.fps}}})}}async function f0($,W,H){if(Array.isArray(W))throw Error("File can't be an array, pass a single file");let J=await v(W,{quality:H?.quality,acl:H?.acl});return a($,J,{acl:H?.acl,uploadedBy:H?.uploadedBy})}var{s3:e}=globalThis.Bun;async function n0($,W={debug:!0}){try{return{exists:await e.exists($),error:null}}catch(H){if(W?.debug)console.error("There was an error checking for file existance",H);return{exists:null,error:H}}}export{u0 as uploadSingleObject,N as uploadOne,y0 as uploadMany,a as uploadGeneratedVariants,D0 as optimizeImageBuffer,x0 as optimizeImage,P as mimeTypeToResolvedType,S0 as getOptimizedImage,T0 as getImageMetadata,H0 as getAspectRatioStringRound,q as getAspectRatioString,v as generateVariants,$0 as generateTestFile,f0 as generateAndUploadImage,U as fileTypePredicates,o as extractObjectVariantKeys,r as deleteOne,b0 as deleteManyObjects,F as deleteManyKeys,c as createUploadVariants,n0 as checkFileExistance};
5
+ return new s({code:"NOT_FOUND",message:e,clientCode:t,data:r})}static conflict(e,t,r){return new s({code:"CONFLICT",message:e,clientCode:t,data:r})}static unprocessableContent(e,t,r){return new s({code:"UNPROCESSABLE_CONTENT",message:e,clientCode:t,data:r})}static tooManyRequests(e="Too many requests",t,r){return new s({code:"TOO_MANY_REQUESTS",message:e,clientCode:t,data:r})}static internalServerError(e="Internal Server Error",t,r){return new s({code:"INTERNAL_SERVER_ERROR",message:e,clientCode:t,data:r})}static timeout(e="Request timeout",t,r){return new s({code:"TIMEOUT",message:e,clientCode:t,data:r})}}import C from"sharp";var F={xs:200,sm:400,md:800,lg:1200,xl:1920},h=1920;async function O(e,t,r,a,n){try{let{data:o,info:i}=await C(e,{limitInputPixels:1e8}).rotate().resize({width:t,height:t,fit:"inside",withoutEnlargement:!0}).webp({quality:r}).toBuffer({resolveWithObject:!0});return{buffer:o,object:{name:a.replace(/\.[^/.]+$/,".webp"),resolvedType:"image",metadata:{type:"image/webp",size:o.length,extension:".webp",width:i.width,height:i.height,aspectRatio:i.width&&i.height?i.width/i.height:1,aspectRatioStr:i.width&&i.height?b(i.width,i.height):"1:1",acl:n}}}}catch(o){throw s.conflict(`Failed to optimize image: ${o instanceof Error?o.message:"Unknown error"}`)}}async function _(e){if(!e)throw s.conflict("A file must be provided");let t,r="unknown.webp";if(typeof e==="string"){if(console.log("I'm going to fail here for file",e),!e.startsWith("data:"))throw console.log("I never got here"),s.conflict("Invalid base64 format");let[,a]=e.split(",");if(!a)throw s.conflict("Invalid base64 data");t=Buffer.from(a,"base64")}else{if(!y.isImage(e.type))throw s.conflict("File must be an image");let a=await e.arrayBuffer();t=Buffer.from(a),r=e.name??r}return{buffer:t,name:r}}async function be(e,{size:t,quality:r,acl:a}={}){let n=await _(e);return O(n.buffer,t??h,r??80,n.name,a??"public-read")}async function Re(e,{name:t="unknown.webp",size:r,quality:a,acl:n}={}){if(!e?.length)throw s.conflict("A buffer must be provided");return O(e,r??h,a??80,t,n??"public-read")}async function j(e,{quality:t=80,acl:r="public-read"}={}){let a=await _(e),n=await Promise.all(Object.entries(F).map(async([o,i])=>{let l=await O(a.buffer,i,t,a.name,r);return[o,l]}));return{variants:Object.fromEntries(n)}}import q from"sharp";import{randomUUID as k}from"crypto";import{extname as z}from"path";async function P(e,t,r){if(!t)return{data:null,error:Error("File not found")};let a=e.replace(/^\/+|\/+$/g,""),n=z(t.name),o=`${k()}${n}`,i=`${a}/${o}`,l=`./buckets/${a}/${o}`;try{let d=await Bun.write(l,t);return{data:{key:i,file:t,bytesWritten:d},error:null}}catch(d){return{data:null,error:Error(`Failed to upload ${t.name} to local filesystem`)}}}async function L(e,t,r){if(!t||t.length===0)return[];let a=t.map((o)=>{let i=r?.(o)??o.name;return P(e,o,i)});return await Promise.all(a)}async function G(e){let t=Bun.file(`./buckets/${e}`);if(!await t.exists())return null;return t}async function W(e){let t=Bun.file(`./buckets/${e}`);if(!await t.exists())return!1;return await t.delete(),!0}var E={uploadOne:P,uploadMany:L,getOne:G,deleteOne:W};async function xe(e){let t=`./cache/images/${e.key}_${e.width}_${e.quality}.webp`,r=await E.getOne(t);if(r){if(!y.isImage(r.type))return null;return r}let a=e.key,n=await E.getOne(a);if(!n)return null;if(!y.isImage(n.type))return null;let o=await n.arrayBuffer(),i=await q(o).resize(e.width,null,{withoutEnlargement:!0}).webp({quality:e.quality??75}).toBuffer();await Bun.write(t,i);let l=Bun.file(t);if(!await l.exists())return null;return l}import K from"sharp";async function Be(e){if(!e?.length)return null;try{let t=await K(e).metadata(),r=t.width??0,a=t.height??0,n=t.height>0?r/a:1;return{type:`image/${t.format}`,size:t.size??0,extension:`.${t.format}`,width:r,height:a,aspectRatio:n,aspectRatioStr:b(r,a)}}catch(t){return console.warn("Failed to read image metadata:",t),null}}var D=process.env.AWS_FOLDER,V=D?`${D}/`:"";var{s3:Z}=globalThis.Bun;import{extname as X}from"path";function M(e={}){return e}var{s3:Y}=globalThis.Bun;function Q(e){return[...new Set(e.flatMap(({variants:t})=>Object.values(t).flatMap((r)=>r&&typeof r==="object"&&("key"in r)&&typeof r.key==="string"&&r.key.length>0?[r.key]:[])))]}async function H(e,t){if(!e||typeof e!=="string")throw s.badRequest("Key is required");let r=e.trim();if(!r||r.length===0)throw s.badRequest("Key must be a non-empty string");try{return await Y.delete(r),{key:r,deleted:!0,error:null}}catch(a){return{key:e,deleted:!1,error:a}}}async function S(e,t){return(await Promise.allSettled(e.map((a)=>H(a,t)))).map((a,n)=>{if(a.status==="fulfilled")return a.value;return{key:e[n],deleted:!1,error:a.reason}})}async function $e(e,t){return S(Q(e),t)}async function T(e,t,r){let a="buffer"in t?t:{buffer:t,object:{name:t.name,resolvedType:N(t.type),metadata:{type:t.type,extension:X(t.name),size:t.size,acl:r?.acl??"public-read"}}};if("buffer"in t){if(!t.object?.name||!t.object?.metadata)throw s.badRequest("Complete metadata required when uploading buffer")}let n=e.length>1?`${e.replace(/^\/+|\/+$/g,"")}/`:"",o=a.object.metadata.extension.startsWith(".")?a.object.metadata.extension:`.${a.object.metadata.extension}`,i=a.object.name?a.object.name:`${crypto.randomUUID()}${o}`,l=`${V}${n}${i}`,d=Z.file(l),m=a?.object?.metadata?.acl??"public-read",c=m!=="public-read"&&m!=="public-read-write",p=await d.write(a.buffer,{acl:m,type:a.object.metadata.type});return{uploadedBy:r?.uploadedBy,key:l,name:a.object.name,resolvedType:a.object.resolvedType,provider:"s3",isPrivate:c,metadata:{...a.object.metadata,size:p,acl:m},isActive:!0}}async function Ze(e,t,r={throwIf:"any"}){let a=await Promise.allSettled(t.map((i)=>T(e,i,{acl:r?.acl,uploadedBy:r?.uploadedBy}))),n=[],o=[];if(a.forEach((i)=>{if(i.status==="fulfilled"){if(n.push(i.value),r?.externalKeys)r.externalKeys.push(i.value.key)}else o.push(i.reason)}),r?.throwIf==="any"&&o.length>0||r?.throwIf==="all"&&o.length===t.length)throw s.internalServerError(`Upload failed: ${o.length} of ${t.length}`);return{data:n,errors:o.length?o:void 0}}async function J(e,t,r){let a=r?.baseId??crypto.randomUUID(),n=Object.values(t.variants).find(Boolean),o=await Promise.allSettled(Object.entries(t.variants).map(async([c,p])=>{if(!p)return[c,null];let g=p.object.metadata.extension,f=`${a}_${c}${g}`,R=await T(e,{...p,object:{...p.object,name:f}},r);if(!R.metadata)throw s.internalServerError("Upload returned no metadata, cannot build a storage variant");let u=R.metadata;return[c,{key:R.key,name:f,metadata:{type:u.type,extension:u.extension,size:u.size,hash:u.hash,width:u.width,height:u.height,aspectRatio:u.aspectRatio,aspectRatioStr:u.aspectRatioStr,resolution:u.resolution,bitrate:u.bitrate,codec:u.codec,fps:u.fps}}]})),i=o.flatMap((c)=>c.status==="fulfilled"?[c.value]:[]),l=o.flatMap((c)=>c.status==="rejected"?[c.reason]:[]);if(l.length>0){let c=i.flatMap(([,f])=>f?.key?[f.key]:[]),g=(await S(c)).filter(({deleted:f})=>!f);if(g.length>0)console.error("Failed to clean up partially uploaded image variants",{keys:c,failures:g});throw l[0]}let d=(r?.acl??"public-read")!=="public-read"&&(r?.acl??"public-read")!=="public-read-write",m={};for(let[c,p]of i)if(p)m[c]=p;return{shared:{name:n.object.name,resolvedType:n.object.resolvedType,provider:"s3",acl:r?.acl??"public-read",isPrivate:d,uploadedBy:r?.uploadedBy},variants:m}}async function Xe(e,t,r){let a=await T(e,t,r);if(!a.metadata)throw s.internalServerError("Upload returned no metadata, cannot build a storage object");let n=a.name??a.key.split("/").pop()??a.key;return{shared:{name:n,resolvedType:a.resolvedType,provider:a.provider,acl:a.metadata.acl,isPrivate:a.isPrivate,uploadedBy:r?.uploadedBy},variants:M({default:{key:a.key,name:n,metadata:{type:a.metadata.type,extension:a.metadata.extension,size:a.metadata.size,hash:a.metadata.hash,width:a.metadata.width,height:a.metadata.height,aspectRatio:a.metadata.aspectRatio,aspectRatioStr:a.metadata.aspectRatioStr,resolution:a.metadata.resolution,bitrate:a.metadata.bitrate,codec:a.metadata.codec,fps:a.metadata.fps}}})}}async function Je(e,t,r){if(Array.isArray(t))throw Error("File can't be an array, pass a single file");let a=await j(t,{quality:r?.quality,acl:r?.acl});return J(e,a,{acl:r?.acl,uploadedBy:r?.uploadedBy})}var{s3:ee}=globalThis.Bun;async function rt(e,t={debug:Reflect.get(process.env,"NODE_ENV")!=="production"}){try{return{exists:await ee.exists(e),error:null}}catch(r){if(t?.debug)console.error("There was an error checking for file existance",r);return{exists:null,error:r}}}export{rt as checkFileExistance,M as createUploadVariants,S as deleteManyKeys,$e as deleteManyObjects,H as deleteOne,Q as extractObjectVariantKeys,y as fileTypePredicates,Je as generateAndUploadImage,te as generateTestFile,j as generateVariants,b as getAspectRatioString,ae as getAspectRatioStringRound,Be as getImageMetadata,xe as getOptimizedImage,N as mimeTypeToResolvedType,be as optimizeImage,Re as optimizeImageBuffer,J as uploadGeneratedVariants,Ze as uploadMany,T as uploadOne,Xe as uploadSingleObject};
6
6
 
7
- //# debugId=BF9212D9C6E0325364756E2164756E21
7
+ //# debugId=E8F83337A5ED8C6964756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mateosuarezdev/brpc",
3
- "version": "1.0.113",
3
+ "version": "1.0.115",
4
4
  "description": "A Type-Safe, Flexible Web application framework for Bun",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -43,7 +43,6 @@
43
43
  "dist/**/*.cjs",
44
44
  "dist/**/*.d.ts"
45
45
  ],
46
- "sideEffects": false,
47
46
  "scripts": {
48
47
  "typecheck": "tsc --noEmit",
49
48
  "test": "bun test",
@@ -131,6 +130,6 @@
131
130
  "zod": "^3.23.8"
132
131
  },
133
132
  "dependencies": {
134
- "@mateosuarezdev/headers-builder": "^1.0.5"
133
+ "@mateosuarezdev/headers-builder": "^1.0.6"
135
134
  }
136
135
  }