@mateosuarezdev/brpc 1.0.89 → 1.0.92

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
@@ -276,7 +276,7 @@ router.publish(
276
276
  );
277
277
  ```
278
278
 
279
- Parameters in the route path (e.g. `":roomId"`) scope the subscription topic — subscribers only receive messages matching their params.
279
+ Parameters in the route path (e.g. `":roomId"`) scope the subscription topic — subscribers only receive messages matching their params. Pass raw parameter values to `router.publish()` and `ctx.publishToProcedure()`; BRPC URI-encodes only those dynamic topic segments, while subscription handlers receive decoded `ctx.params`.
280
280
 
281
281
  Use `.onSubscribe()` for admission checks. It runs after dynamic topic params are resolved and before the socket joins the topic. Normal `.use()` middleware runs only when a client publishes to a subscription; server-originated `router.publish()` calls remain trusted.
282
282
 
@@ -776,23 +776,26 @@ try {
776
776
 
777
777
  ### React
778
778
 
779
- Import from `@mateosuarezdev/brpc/client/react`:
779
+ Create one subscription service, then bind the React hook once:
780
780
 
781
781
  ```ts
782
- import { useSubscription } from "@mateosuarezdev/brpc/client/react";
782
+ import { SubscriptionService } from "@mateosuarezdev/brpc/client";
783
+ import { createUseSubscription } from "@mateosuarezdev/brpc/client/react";
784
+
785
+ const subscriptionService = new SubscriptionService();
786
+ const useSubscription = createUseSubscription(subscriptionService);
783
787
 
784
788
  function ChatRoom({ roomId }: { roomId: string }) {
785
789
  const [messages, setMessages] = useState<Message[]>([]);
786
790
 
787
- const { publish } = useSubscription(
788
- (callback, options) =>
789
- client.routes.messages[":roomId"].subscription(callback, options),
790
- (error, message) => {
791
- if (error || !message) return;
791
+ const { publish } = useSubscription({
792
+ procedure: client.routes.messages[":roomId"],
793
+ options: { params: { roomId } },
794
+ callback: (error, message) => {
795
+ if (error) return;
792
796
  setMessages((prev) => [...prev, message]);
793
797
  },
794
- { params: { roomId } },
795
- );
798
+ });
796
799
 
797
800
  return (
798
801
  <button onClick={() => publish({ roomId, text: "hello" })}>
@@ -1,4 +1,5 @@
1
1
  export * from "./storage";
2
2
  export * from "./service";
3
3
  export * from "./brpc-client-error";
4
+ export * from "./subscription-service";
4
5
  export type { Headers, InferRouterOutput } from "./types";
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- function d(q){return(J)=>{if(!J)return null;return`${q}/${J}`}}class M extends Error{status;code;clientCode;data;constructor(q,J,Q,N,z){super(q);this.name="BrpcClientError",this.status=J,this.code=Q,this.clientCode=N,this.data=z}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 f extends M{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 M&&J.clientCode===Q;return J instanceof M}}var h=(q)=>{return q instanceof File||q instanceof Blob||typeof q==="object"&&q!==null&&"uri"in q&&"name"in q&&"type"in q};function y(q){if(!q)return null;return q.replace(/\//g,"")}function c(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 u(q,J){let Q=J?`/${J}/ws`:"/ws";return`${q.replace(/^http/,"ws")}${Q}`}function n(q,J){if(q&&J)return`/${q}/${J}`;else if(q&&!J)return`/${q}`;else if(!q&&J)return`/${J}`;else return""}class b{baseUrl;ws=null;subscriptions=new Map;messageQueue=[];isConnected=!1;reconnectTimeout=null;WebSocketImpl;getHeaders;authToken=null;debug;pendingAuth=!1;authActions=[];constructor(q,J,Q,N=!1,z){this.baseUrl=q;this.WebSocketImpl=J,this.debug=N&&z==="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");if(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 Q of this.subscriptions.keys())this.startTopicSubscription(Q);this.authActions.push(()=>{if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((Q)=>this.send(Q)),this.messageQueue=[]})}else{for(let Q of this.subscriptions.keys())this.startTopicSubscription(Q);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((Q)=>this.send(Q)),this.messageQueue=[]}},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 z=N.authenticated!==!1;if(this.debug)console.log(`WebSocket ${z?"authentication":"deauthentication"} successful`);if(this.pendingAuth=!1,z)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:z}=this.subscriptions.get(N.topic);if(this.debug)console.log("Calling subscription callbacks");z?.forEach((D)=>D(void 0,N.data))}else if(["subscribe_error","publish_error"].includes(N.type)&&N.topic&&this.subscriptions.has(N.topic)){let z=this.createSubscriptionError(N.type,N.error),D=this.subscriptions.get(N.topic);D.status="failed",D.callbacks.forEach((F)=>F(z,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(),2000)},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(),2000)}}createClientError(q){return new M(q?.message??"WebSocket request failed",q?.httpStatus??0,q?.code,q?.clientCode,q?.data)}createSubscriptionError(q,J){return new f(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()}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:(z)=>this.publish(q,z,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,z=this.authToken!==null&&this.authToken.trim().length>0;if((N!==z||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 Gq(q,J={}){let Q=J.fetch||fetch,N=J.WebSocket??(typeof WebSocket<"u"?WebSocket:null),z=J.prefix??"",D=J.apiPrefix??"",F=J.debug??!1,l=J.s3Endpoint,p=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 A=J.headers??{},k=async()=>{if(typeof A==="function")return await A();return A},E=c(q),g=y(J.prefix),i=y(J.apiPrefix),H=n(g,i),m=u(E,g),C=new b(m,N,k,F,p),U=async(Y=!0)=>{let R={};if(Y)R["Content-Type"]="application/json";let X=await k();return{...R,...X}},o=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 V=X.error;throw new M(V.message||Y.statusText,Y.status,V.code,V.clientCode,V.data)}let I=X?.error||X?.message||Y.statusText;throw new M(I,Y.status,void 0,void 0,X)}let R=Y.headers.get("Content-Type")||"";try{if(R.includes("application/json"))return(await Y.json()).data;else if(R.includes("text/"))return Y.text();else return Y.blob()}catch(X){throw new M(`Failed to parse response: ${X instanceof Error?X.message:"Unknown error"}`,Y.status,"PARSE_ERROR")}},x=async(Y,R)=>{try{let X=await Q(Y,R);return await o(X)}catch(X){if(X instanceof M)throw X;throw new M(X instanceof Error?X.message:"Network request failed",0,"NETWORK_ERROR")}},K=(Y,R)=>{return Y.split("/").map((X)=>{if(!X.startsWith(":"))return X;let I=X.slice(1),V=R?.[I];if(V===void 0||V===null)throw new M(`Missing route parameter: ${I}`,400,"MISSING_ROUTE_PARAM","MISSING_ROUTE_PARAM",{param:I});return encodeURIComponent(String(V))}).join("/")},v=(Y=[])=>{return new Proxy({},{get(R,X){if(typeof X==="symbol")return;if(X==="query"||X==="mutation"||X==="formMutation"||X==="subscription"||X==="getStringKey"||X==="getArrayKey"||X==="getNoInputsArrayKey"){let I=Y.join("/"),V=(Z)=>`${E}${H}/${Z}`;if(F)console.log(`BRPC ${X} procedure path: ${I}`);if(X==="query")return async(Z,w={})=>{let _=K(I,w.params),j=await U(!0);if(F)console.log(`BRPC query request to ${_}`,{input:Z,headers:j});let G=new URL(V(_));if(Z&&typeof Z==="object")Object.entries(Z).forEach(([O,$])=>{if($!==void 0)G.searchParams.append(O,String($))});let W=await x(G.toString(),{method:"GET",headers:j,credentials:"include"});if(F)console.log(`BRPC query response from ${_}:`,W);return W};else if(X==="mutation")return async(Z,w={})=>{let _=K(I,w.params),j=await U(!0);if(F)console.log(`BRPC mutation request to ${_}`,{input:Z,headers:j});let G=JSON.stringify(Z),W=await x(V(_),{method:"POST",headers:j,body:G,credentials:"include"});if(F)console.log(`BRPC mutation response from ${_}:`,W);return W};else if(X==="formMutation")return async(Z,w={})=>{let _=K(I,w.params),j=await U(!1);if(F)console.log(`BRPC formMutation request to ${_}`,{input:Z,headers:j});let G=new FormData,W=($,L,P)=>{let S=P?`${P}[${$}]`:$;if(L===null||L===void 0)return;else if(h(L))G.append(S,L);else if(Array.isArray(L))L.forEach((T,B)=>{if(h(T))G.append(S,T);else if(typeof T==="object"&&T!==null)Object.entries(T).forEach(([a,s])=>{W(a,s,`${S}[${B}]`)});else G.append(`${S}[${B}]`,String(T))});else if(typeof L==="object"&&L!==null)Object.entries(L).forEach(([T,B])=>{W(T,B,S)});else G.append(S,String(L))};Object.entries(Z).forEach(([$,L])=>{W($,L)});let O=await x(V(_),{method:"POST",headers:j,body:G,credentials:"include"});if(F)console.log(`BRPC formMutation response from ${_}:`,O);return O};else if(X==="subscription")return(Z,w={})=>{let _=K(I,w.params);if(F)console.log(`BRPC subscription to ${_}`);let j=H?`${H.slice(1)}/${_}`:_;return C.updateAuth(),C.subscribe(j,Z,w)};else if(X==="getStringKey")return(Z,w={})=>{let _=K(I,w.params);if(!Z||Object.keys(Z).length===0)return _;let j=Object.keys(Z).sort().reduce((W,O)=>{let $=Z[O];if($!==void 0&&$!==null)W[O]=$;return W},{});if(Object.keys(j).length===0)return _;let G=JSON.stringify(j);return`${_}?${G}`};else if(X==="getArrayKey")return(Z,w={})=>{let _=K(I,w.params),j=Z??{};if(Object.keys(j).length===0)return[_];let G=[];if(Object.keys(j).sort().forEach((O)=>{let $=j[O];if($!==void 0&&$!==null){let L=$;if(typeof $==="boolean"||typeof $==="number")L=String($);else if(typeof $==="object")L=JSON.stringify($);G.push([O,L])}}),G.length===0)return[_];let W=G.flatMap(([O,$])=>[O,$]);return[_,...W]};else if(X==="getNoInputsArrayKey")return(Z={})=>{return[K(I,Z.params)]}}return v([...Y,X])}})},t=v();if(F)console.log("BRPC client created",{baseUrl:E,prefix:H,wsUrl:m});return{routes:t,storage:{getObjectUrl:d(l)},utils:{updateWsAuth:async()=>await C.updateAuth(),setHeader:async(Y,R)=>{if(typeof A==="function"){console.warn("Cannot use setHeader with function-based headers resolver");return}A={...A,[Y]:R},await C.updateAuth()},setHeaders:async(Y)=>{if(typeof A==="function"){console.warn("Cannot use setHeaders with function-based headers resolver");return}A={...A,...Y},await C.updateAuth()}}}}export{e as isBrpcClientErrorUtility,d as createGetObjectUrl,Gq as createBrpcClient,f as BrpcClientSubscriptionError,M as BrpcClientError};
2
+ function u(q){return(J)=>{if(!J)return null;return`${q}/${J}`}}class F extends Error{status;code;clientCode;data;constructor(q,J,Q,N,G){super(q);this.name="BrpcClientError",this.status=J,this.code=Q,this.clientCode=N,this.data=G}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 b extends F{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 qq(){return function(J,Q){if(Q)return J instanceof F&&J.clientCode===Q;return J instanceof F}}var v=(q)=>{return q instanceof File||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 l(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 p(q,J){let Q=J?`/${J}/ws`:"/ws";return`${q.replace(/^http/,"ws")}${Q}`}function n(q,J){if(q&&J)return`/${q}/${J}`;else if(q&&!J)return`/${q}`;else if(!q&&J)return`/${J}`;else return""}class P{baseUrl;ws=null;subscriptions=new Map;messageQueue=[];isConnected=!1;reconnectTimeout=null;WebSocketImpl;getHeaders;authToken=null;debug;pendingAuth=!1;authActions=[];constructor(q,J,Q,N=!1,G){this.baseUrl=q;this.WebSocketImpl=J,this.debug=N&&G==="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");if(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 Q of this.subscriptions.keys())this.startTopicSubscription(Q);this.authActions.push(()=>{if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((Q)=>this.send(Q)),this.messageQueue=[]})}else{for(let Q of this.subscriptions.keys())this.startTopicSubscription(Q);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((Q)=>this.send(Q)),this.messageQueue=[]}},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 G=N.authenticated!==!1;if(this.debug)console.log(`WebSocket ${G?"authentication":"deauthentication"} successful`);if(this.pendingAuth=!1,G)this.authActions.forEach((w)=>w());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:G}=this.subscriptions.get(N.topic);if(this.debug)console.log("Calling subscription callbacks");G?.forEach((w)=>w(void 0,N.data))}else if(["subscribe_error","publish_error"].includes(N.type)&&N.topic&&this.subscriptions.has(N.topic)){let G=this.createSubscriptionError(N.type,N.error),w=this.subscriptions.get(N.topic);w.status="failed",w.callbacks.forEach((Z)=>Z(G,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(),2000)},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(),2000)}}createClientError(q){return new F(q?.message??"WebSocket request failed",q?.httpStatus??0,q?.code,q?.clientCode,q?.data)}createSubscriptionError(q,J){return new b(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()}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:(G)=>this.publish(q,G,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,G=this.authToken!==null&&this.authToken.trim().length>0;if((N!==G||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 Lq(q,J={}){let Q=J.fetch||fetch,N=J.WebSocket??(typeof WebSocket<"u"?WebSocket:null),G=J.prefix??"",w=J.apiPrefix??"",Z=J.debug??!1,D=J.s3Endpoint,U=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},x=l(q),g=m(J.prefix),i=m(J.apiPrefix),B=n(g,i),y=p(x,g),E=new P(y,N,k,Z,U),f=async(Y=!0)=>{let R={};if(Y)R["Content-Type"]="application/json";let X=await k();return{...R,...X}},o=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 V=X.error;throw new F(V.message||Y.statusText,Y.status,V.code,V.clientCode,V.data)}let W=X?.error||X?.message||Y.statusText;throw new F(W,Y.status,void 0,void 0,X)}let R=Y.headers.get("Content-Type")||"";try{if(R.includes("application/json"))return(await Y.json()).data;else if(R.includes("text/"))return Y.text();else return Y.blob()}catch(X){throw new F(`Failed to parse response: ${X instanceof Error?X.message:"Unknown error"}`,Y.status,"PARSE_ERROR")}},h=async(Y,R)=>{try{let X=await Q(Y,R);return await o(X)}catch(X){if(X instanceof F)throw X;throw new F(X instanceof Error?X.message:"Network request failed",0,"NETWORK_ERROR")}},H=(Y,R)=>{return Y.split("/").map((X)=>{if(!X.startsWith(":"))return X;let W=X.slice(1),V=R?.[W];if(V===void 0||V===null)throw new F(`Missing route parameter: ${W}`,400,"MISSING_ROUTE_PARAM","MISSING_ROUTE_PARAM",{param:W});return encodeURIComponent(String(V))}).join("/")},d=(Y=[])=>{return new Proxy({},{get(R,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("/"),V=(_)=>`${x}${B}/${_}`;if(Z)console.log(`BRPC ${X} procedure path: ${W}`);if(X==="query")return async(_,O={})=>{let $=H(W,O.params),L=await f(!0);if(Z)console.log(`BRPC query request to ${$}`,{input:_,headers:L});let z=new URL(V($));if(_&&typeof _==="object")Object.entries(_).forEach(([A,j])=>{if(j!==void 0)z.searchParams.append(A,String(j))});let M=await h(z.toString(),{method:"GET",headers:L,credentials:"include"});if(Z)console.log(`BRPC query response from ${$}:`,M);return M};else if(X==="mutation")return async(_,O={})=>{let $=H(W,O.params),L=await f(!0);if(Z)console.log(`BRPC mutation request to ${$}`,{input:_,headers:L});let z=JSON.stringify(_),M=await h(V($),{method:"POST",headers:L,body:z,credentials:"include"});if(Z)console.log(`BRPC mutation response from ${$}:`,M);return M};else if(X==="formMutation")return async(_,O={})=>{let $=H(W,O.params),L=await f(!1);if(Z)console.log(`BRPC formMutation request to ${$}`,{input:_,headers:L});let z=new FormData,M=(j,I,c)=>{let C=c?`${c}[${j}]`:j;if(I===null||I===void 0)return;else if(v(I))z.append(C,I);else if(Array.isArray(I))I.forEach((K,S)=>{if(v(K))z.append(C,K);else if(typeof K==="object"&&K!==null)Object.entries(K).forEach(([a,s])=>{M(a,s,`${C}[${S}]`)});else z.append(`${C}[${S}]`,String(K))});else if(typeof I==="object"&&I!==null)Object.entries(I).forEach(([K,S])=>{M(K,S,C)});else z.append(C,String(I))};Object.entries(_).forEach(([j,I])=>{M(j,I)});let A=await h(V($),{method:"POST",headers:L,body:z,credentials:"include"});if(Z)console.log(`BRPC formMutation response from ${$}:`,A);return A};else if(X==="subscription")return(_,O={})=>{let $=H(W,O.params);if(Z)console.log(`BRPC subscription to ${$}`);let L=B?`${B.slice(1)}/${$}`:$;return E.updateAuth(),E.subscribe(L,_,O)};else if(X==="getStringKey")return(_,O={})=>{let $=H(W,O.params);if(!_||Object.keys(_).length===0)return $;let L=Object.keys(_).sort().reduce((M,A)=>{let j=_[A];if(j!==void 0&&j!==null)M[A]=j;return M},{});if(Object.keys(L).length===0)return $;let z=JSON.stringify(L);return`${$}?${z}`};else if(X==="getArrayKey")return(_,O={})=>{let $=H(W,O.params),L=_??{};if(Object.keys(L).length===0)return[$];let z=[];if(Object.keys(L).sort().forEach((A)=>{let j=L[A];if(j!==void 0&&j!==null){let I=j;if(typeof j==="boolean"||typeof j==="number")I=String(j);else if(typeof j==="object")I=JSON.stringify(j);z.push([A,I])}}),z.length===0)return[$];let M=z.flatMap(([A,j])=>[A,j]);return[$,...M]};else if(X==="getNoInputsArrayKey")return(_={})=>{return[H(W,_.params)]}}return d([...Y,X])}})},t=d();if(Z)console.log("BRPC client created",{baseUrl:x,prefix:B,wsUrl:y});return{routes:t,storage:{getObjectUrl:u(D)},utils:{updateWsAuth:async()=>await E.updateAuth(),setHeader:async(Y,R)=>{if(typeof T==="function"){console.warn("Cannot use setHeader with function-based headers resolver");return}T={...T,[Y]:R},await E.updateAuth()},setHeaders:async(Y)=>{if(typeof T==="function"){console.warn("Cannot use setHeaders with function-based headers resolver");return}T={...T,...Y},await E.updateAuth()}}}}class r{entries=new Map;subscribe({key:q,subscription:J,options:Q,listener:N}){let G=JSON.stringify(q),w=(...D)=>N(...D),Z=this.entries.get(G);if(!Z){Z={listeners:new Set,publish:()=>{},unsubscribe:()=>{}},this.entries.set(G,Z);try{let D=J((...U)=>{Z?.listeners.forEach((T)=>T(...U))},Q);Z.publish=D.publish,Z.unsubscribe=D.unsubscribe}catch(D){throw this.entries.delete(G),D}}return Z.listeners.add(w),{publish:(D)=>{if(this.entries.get(G)===Z)Z.publish(D)},unsubscribe:()=>this.unsubscribe(G,Z,w)}}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{qq as isBrpcClientErrorUtility,u as createGetObjectUrl,Lq as createBrpcClient,r as SubscriptionService,b as BrpcClientSubscriptionError,F as BrpcClientError};
3
3
 
4
- //# debugId=6A46A4B49FFA34B564756E2164756E21
4
+ //# debugId=9779B2DB508C399A64756E2164756E21
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{useCallback as F,useEffect as G,useRef as H}from"react";function K(j,q,...y){let x=y[0],m=H(null);return G(()=>{if(x?.enabled===!1){m.current=null;return}let{enabled:w,...z}=x??{},{publish:B,unsubscribe:D}=j(q,z);return m.current=B,()=>{m.current=null,D()}},[j,q,x]),{publish:F((w)=>{m.current?.(w)},[])}}export{K as useSubscription};
2
+ import{useCallback as D,useEffect as K,useRef as F}from"react";function O(G){return function({procedure:q,options:j,callback:w,enabled:z=!0}){let A=q.getNoInputsArrayKey(j),H=JSON.stringify(A),B=F(w),x=F(null);B.current=w;let I=D((...m)=>B.current(...m),[]);return K(()=>{if(!z){x.current=null;return}let{publish:m,unsubscribe:J}=G.subscribe({key:A,subscription:q.subscription,options:j,listener:I});return x.current=m,()=>{x.current=null,J()}},[z,H,j.auth]),{publish:D((m)=>{x.current?.(m)},[])}}}export{O as createUseSubscription};
3
3
 
4
- //# debugId=6D0EB72372875B3364756E2164756E21
4
+ //# debugId=1F36C573C33D7C1C64756E2164756E21
@@ -0,0 +1,14 @@
1
+ import type { RuntimeSubscriptionOptions, SubscriptionCallback, SubscriptionHandlers } from "../types";
2
+ export type UseSubscriptionDeprecatedOptions<TOptions extends RuntimeSubscriptionOptions = RuntimeSubscriptionOptions> = TOptions & {
3
+ enabled?: boolean;
4
+ };
5
+ type UseSubscriptionDeprecatedOptionsArgs<TOptions extends RuntimeSubscriptionOptions> = TOptions extends {
6
+ params: infer _Params;
7
+ } ? [options: UseSubscriptionDeprecatedOptions<TOptions>] : [options?: UseSubscriptionDeprecatedOptions<TOptions>];
8
+ /**
9
+ * @deprecated Use `createUseSubscription` with `SubscriptionService`.
10
+ */
11
+ export declare function useSubscriptionDeprecated<TInput, TOutput, TOptions extends RuntimeSubscriptionOptions>(subscription: (callback: SubscriptionCallback<TOutput>, options: TOptions) => SubscriptionHandlers<TInput, TOutput>, callback: SubscriptionCallback<TOutput>, ...optionsArgs: UseSubscriptionDeprecatedOptionsArgs<TOptions>): {
12
+ publish: (data: TInput) => void;
13
+ };
14
+ export {};
@@ -1,14 +1,18 @@
1
- import type { RuntimeSubscriptionOptions, SubscriptionCallback, SubscriptionHandlers } from "../types";
2
- export type UseSubscriptionOptions<TOptions extends RuntimeSubscriptionOptions = RuntimeSubscriptionOptions> = TOptions & {
1
+ import type { SubscriptionKey, SubscriptionService, SubscriptionSource } from "../subscription-service";
2
+ import type { RuntimeSubscriptionOptions, SubscriptionCallback } from "../types";
3
+ export type UseSubscriptionOptions<TInput, TOutput, TOptions extends RuntimeSubscriptionOptions> = {
4
+ procedure: SubscriptionProcedure<TInput, TOutput, TOptions>;
5
+ options: TOptions;
6
+ callback: SubscriptionCallback<TOutput>;
3
7
  enabled?: boolean;
4
8
  };
5
- type UseSubscriptionOptionsArgs<TOptions extends RuntimeSubscriptionOptions> = TOptions extends {
6
- params: infer _Params;
7
- } ? [options: UseSubscriptionOptions<TOptions>] : [options?: UseSubscriptionOptions<TOptions>];
9
+ export type SubscriptionProcedure<TInput, TOutput, TOptions extends RuntimeSubscriptionOptions> = {
10
+ subscription: SubscriptionSource<TInput, TOutput, TOptions>;
11
+ getNoInputsArrayKey: (options: TOptions) => SubscriptionKey;
12
+ };
8
13
  /**
9
- * Connects React state to a BRPC subscription.
14
+ * Creates a React hook backed by one keyed subscription registry.
10
15
  */
11
- export declare function useSubscription<TInput, TOutput, TOptions extends RuntimeSubscriptionOptions>(subscription: (callback: SubscriptionCallback<TOutput>, options: TOptions) => SubscriptionHandlers<TInput, TOutput>, callback: SubscriptionCallback<TOutput>, ...optionsArgs: UseSubscriptionOptionsArgs<TOptions>): {
12
- publish: (data: TInput) => void;
16
+ export declare function createUseSubscription(subscriptionService: SubscriptionService): <TInput, TOutput, TOptions extends RuntimeSubscriptionOptions>({ procedure, options, callback, enabled, }: UseSubscriptionOptions<TInput, TOutput, TOptions>) => {
17
+ publish: (input: TInput) => void;
13
18
  };
14
- export {};
@@ -0,0 +1,20 @@
1
+ import type { RuntimeSubscriptionOptions, SubscriptionCallback, SubscriptionHandlers } from "./types";
2
+ export type SubscriptionKey = readonly (string | number | boolean)[];
3
+ export type SubscriptionSource<TInput, TOutput, TOptions extends RuntimeSubscriptionOptions> = (callback: SubscriptionCallback<TOutput>, options: TOptions) => SubscriptionHandlers<TInput, TOutput>;
4
+ /**
5
+ * Shares one underlying BRPC topic subscription between local listeners with
6
+ * the same deterministic key.
7
+ */
8
+ export declare class SubscriptionService {
9
+ private entries;
10
+ subscribe<TInput, TOutput, TOptions extends RuntimeSubscriptionOptions>({ key, subscription, options, listener, }: {
11
+ key: SubscriptionKey;
12
+ subscription: SubscriptionSource<TInput, TOutput, TOptions>;
13
+ options: TOptions;
14
+ listener: SubscriptionCallback<TOutput>;
15
+ }): {
16
+ publish: (input: TInput) => void;
17
+ unsubscribe: () => void;
18
+ };
19
+ private unsubscribe;
20
+ }
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // @bun
2
- import{z as I}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 N(this.middlewares,$,void 0,this.subscribeMiddlewares)}query($){return{_input:I.object({}),_output:null,_ctx:null,_type:"query",handler:$,middlewares:this.middlewares}}mutation($){return{_input:I.object({}),_output:null,_ctx:null,_type:"mutation",handler:$,middlewares:this.middlewares}}formMutation($){return{_input:I.object({}),_output:null,_ctx:null,_type:"formMutation",handler:$,middlewares:this.middlewares}}subscription($){return{_input:I.object({}),_output:null,_ctx:null,_type:"subscription",handler:$,middlewares:this.middlewares,subscribeMiddlewares:this.subscribeMiddlewares}}file($){return{_input:I.object({}),_output:null,_ctx:null,_type:"file",handler:$,middlewares:this.middlewares}}fileStream($){return{_input:I.object({}),_output:null,_ctx:null,_type:"fileStream",handler:$,middlewares:this.middlewares}}html($){return{_input:I.object({}),_output:null,_ctx:null,_type:"html",handler:$,middlewares:this.middlewares}}}class N{middlewares;inputSchema;timeoutMs;subscribeMiddlewares;constructor($,F,J,_=[]){this.middlewares=$;this.inputSchema=F;this.timeoutMs=J;this.subscribeMiddlewares=_}timeout($){return new N(this.middlewares,this.inputSchema,$,this.subscribeMiddlewares)}onSubscribe($){return new N(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:I.object({}),_output:null,_ctx:null,_type:"html",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}}function k($){return new Q}class D 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=D.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,D)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,F,J){return new D({code:"BAD_REQUEST",message:$,clientCode:F,data:J})}static unauthorized($="Unauthorized",F,J){return new D({code:"UNAUTHORIZED",message:$,clientCode:F,data:J})}static forbidden($="Forbidden",F,J){return new D({code:"FORBIDDEN",message:$,clientCode:F,data:J})}static notFound($="Not Found",F,J){return new D({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static preconditionFailed($="Precondition failed",F,J){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
2
+ import{z as I}from"zod";class N{middlewares=[];subscribeMiddlewares=[];constructor($=[],F=[]){this.middlewares=$,this.subscribeMiddlewares=F}use($){return new N([...this.middlewares,$],this.subscribeMiddlewares)}onSubscribe($){return new N(this.middlewares,[...this.subscribeMiddlewares,$])}input($){return new Q(this.middlewares,$,void 0,this.subscribeMiddlewares)}query($){return{_input:I.object({}),_output:null,_ctx:null,_type:"query",handler:$,middlewares:this.middlewares}}mutation($){return{_input:I.object({}),_output:null,_ctx:null,_type:"mutation",handler:$,middlewares:this.middlewares}}formMutation($){return{_input:I.object({}),_output:null,_ctx:null,_type:"formMutation",handler:$,middlewares:this.middlewares}}subscription($){return{_input:I.object({}),_output:null,_ctx:null,_type:"subscription",handler:$,middlewares:this.middlewares,subscribeMiddlewares:this.subscribeMiddlewares}}file($){return{_input:I.object({}),_output:null,_ctx:null,_type:"file",handler:$,middlewares:this.middlewares}}fileStream($){return{_input:I.object({}),_output:null,_ctx:null,_type:"fileStream",handler:$,middlewares:this.middlewares}}html($){return{_input:I.object({}),_output:null,_ctx:null,_type:"html",handler:$,middlewares:this.middlewares}}}class Q{middlewares;inputSchema;timeoutMs;subscribeMiddlewares;constructor($,F,J,_=[]){this.middlewares=$;this.inputSchema=F;this.timeoutMs=J;this.subscribeMiddlewares=_}timeout($){return new Q(this.middlewares,this.inputSchema,$,this.subscribeMiddlewares)}onSubscribe($){return new Q(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:I.object({}),_output:null,_ctx:null,_type:"html",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}}function k($){return new N}class L 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=L.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,L)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,F,J){return new L({code:"BAD_REQUEST",message:$,clientCode:F,data:J})}static unauthorized($="Unauthorized",F,J){return new L({code:"UNAUTHORIZED",message:$,clientCode:F,data:J})}static forbidden($="Forbidden",F,J){return new L({code:"FORBIDDEN",message:$,clientCode:F,data:J})}static notFound($="Not Found",F,J){return new L({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static preconditionFailed($="Precondition failed",F,J){//! 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 D({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static conflict($,F,J){return new D({code:"CONFLICT",message:$,clientCode:F,data:J})}static unprocessableContent($,F,J){return new D({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:F,data:J})}static tooManyRequests($="Too many requests",F,J){return new D({code:"TOO_MANY_REQUESTS",message:$,clientCode:F,data:J})}static internalServerError($="Internal Server Error",F,J){return new D({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:F,data:J})}static timeout($="Request timeout",F,J){return new D({code:"TIMEOUT",message:$,clientCode:F,data:J})}}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 _=this.routeCache.get(F);if(this.options.debug&&_)console.log("[ROUTEMATCHER]",`\uD83C\uDFAF Cache hit for route: /${F}`);return _}let J=this.matchDirect($);if(this.cacheResult(F,J),this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDCBE Cached route: /${F} -> ${J?"found":"not found"}`);return J}matchDirect($){let F=this.options.debug?performance.now():0,J=this.traverseTree(this.routeTree,$,0,{});if(this.options.debug){let _=performance.now()-F,W=J?.route._type||"none";console.log("[ROUTEMATCHER]",`\u26A1 Route match: /${$.join("/")} (${W}) took ${_.toFixed(3)}ms`)}return J}traverseTree($,F,J,_){if(J>=F.length){if($.handler)return{params:_,route:$.handler.handler};if($.index)return{params:_,route:$.index.handler};//! This was the last change I made trying to fix
5
+ return new L({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static conflict($,F,J){return new L({code:"CONFLICT",message:$,clientCode:F,data:J})}static unprocessableContent($,F,J){return new L({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:F,data:J})}static tooManyRequests($="Too many requests",F,J){return new L({code:"TOO_MANY_REQUESTS",message:$,clientCode:F,data:J})}static internalServerError($="Internal Server Error",F,J){return new L({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:F,data:J})}static timeout($="Request timeout",F,J){return new L({code:"TIMEOUT",message:$,clientCode:F,data:J})}}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 _=this.routeCache.get(F);if(this.options.debug&&_)console.log("[ROUTEMATCHER]",`\uD83C\uDFAF Cache hit for route: /${F}`);return _}let J=this.matchDirect($);if(this.cacheResult(F,J),this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDCBE Cached route: /${F} -> ${J?"found":"not found"}`);return J}matchDirect($){let F=this.options.debug?performance.now():0,J=this.traverseTree(this.routeTree,$,0,{});if(this.options.debug){let _=performance.now()-F,W=J?.route._type||"none";console.log("[ROUTEMATCHER]",`\u26A1 Route match: /${$.join("/")} (${W}) took ${_.toFixed(3)}ms`)}return J}traverseTree($,F,J,_){if(J>=F.length){if($.handler)return{params:_,route:$.handler.handler};if($.index)return{params:_,route:$.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:{..._,wildcardPath:""},route:$.wildcard.handler};return null}let W=F[J],X=decodeURIComponent(W),Y=$.static.get(X);if(Y){let Z=this.traverseTree(Y,F,J+1,_);if(Z)return Z}if($.param){let Z={..._,[$.param.name]:X},j=this.traverseTree($.param.node,F,J+1,Z);if(j)return j}if($.wildcard){let Z=F.slice(J).join("/");return{params:{..._,wildcardPath:Z},route:$.wildcard.handler}}return null}compileRoutes($,F=[]){let J={static:new Map};for(let[_,W]of Object.entries($)){if(_==="index"&&this.isProcedure(W)){J.index={handler:W,paramNames:[...F],hasWildcard:!1};continue}if(_==="*"){if(this.isProcedure(W))J.wildcard={handler:W,paramNames:[...F,"wildcardPath"],hasWildcard:!0,wildcardIndex:F.length};continue}if(this.isProcedure(W)){let X={handler:W,paramNames:[...F],hasWildcard:!1};if(_.startsWith(":")){let Y=_.slice(1),Z={static:new Map,handler:X};J.param={name:Y,node:Z}}else{let Y={static:new Map,handler:X};J.static.set(_,Y)}}else if(typeof W==="object"&&W!==null)if(_.startsWith(":")){let X=_.slice(1),Y=this.compileRoutes(W,[...F,X]);J.param={name:X,node:Y}}else{let X=this.compileRoutes(W,F);J.static.set(_,X)}}return J}isProcedure($){return $&&typeof $==="object"&&"_type"in $&&"_input"in $}cacheResult($,F){if(this.routeCache.size>=this.options.maxCacheSize){let J=this.routeCache.keys().next().value;if(J!==void 0){if(this.routeCache.delete(J),this.options.debug)console.log(`\uD83D\uDDD1\uFE0F Evicted oldest cache entry: ${J}`)}}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}},J=(_,W=0)=>{F.maxDepth=Math.max(F.maxDepth,W);for(let[X,Y]of Object.entries(_))if(this.isProcedure(Y)){if(F.totalRoutes++,Y._type in F.procedureTypes)F.procedureTypes[Y._type]++;if(X==="index")F.indexRoutes++;else if(X.startsWith(":"))F.paramRoutes++;else if(X==="*")F.wildcardRoutes++;else F.staticRoutes++}else if(typeof Y==="object"&&Y!==null)J(Y,W+1)};return J($),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),J=F.join("/"),_=this.routeCache.has(J),W=performance.now(),X=this.matchDirect(F),Y=performance.now()-W;return{found:!!X,route:X?.route,params:X?.params,cached:_,matchTime:Y}}benchmark($,F=1000){let J=$.map((G)=>G.split("/").filter(Boolean)),_=new Map(this.routeCache);this.routeCache.clear();let W=performance.now();for(let G=0;G<F;G++)for(let U of J)this.matchDirect(U);let Y=performance.now()-W,Z=F*$.length,j=Y/Z,L=Z/Y*1000;return this.routeCache=_,{averageTime:j,totalTime:Y,routesPerSecond:L}}warmupCache($){if(!this.options.enableCaching)return;let F=performance.now(),J=0;for(let W of $){let X=W.split("/").filter(Boolean),Y=this.matchDirect(X);if(Y)this.cacheResult(X.join("/"),Y),J++}let _=performance.now();if(this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDD25 Cache warmed up: ${J} routes in ${(_-F).toFixed(2)}ms`)}}var C=import.meta.require,q={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 v($){return q[$]}var x={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 x[$]||"application/octet-stream"}function f($){if(typeof Bun<"u"&&Bun.hash)return Bun.hash($).toString(16);try{let F=C("crypto"),J;if(typeof $==="string")J=Buffer.from($);else if($ instanceof ArrayBuffer)J=Buffer.from(new Uint8Array($));else J=Buffer.from($);return F.createHash("md5").update(J).digest("hex")}catch{let F=typeof $==="string"?new TextEncoder().encode($):$ instanceof ArrayBuffer?new Uint8Array($):$,J=0;for(let _=0;_<F.length;_++)J=(J<<5)-J+F[_],J=J&J;return Math.abs(J).toString(16).padStart(16,"0")}}function V($,F,J){let _=$.headers.get("Origin"),W=!0;if((J?.allowAll??!0)&&_)return _;if(F.length===0)return _||"*";if(!_)return F[0];if(F.includes(_))return _;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:J}=v($);if(this.headers["Cache-Control"]=F,J)this.headers.Expires=J;return this}eTag($){if(!$)return this;let F;if(typeof $==="string"&&$.startsWith('"'))F=$;else F=`"${f($)}"`;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:J=["Content-Type","Authorization"],maxAge:_=86400,credentials:W}={}){let X=W??$!=="*";if(X&&$==="*")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"]=J.join(", "),X)this.headers["Access-Control-Allow-Credentials"]="true";return this.headers["Access-Control-Max-Age"]=_.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 J=typeof F.hsts==="number"?F.hsts:31536000;this.headers["Strict-Transport-Security"]=`max-age=${J}; 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,J=F?`${F}, ${$.join(", ")}`:$.join(", ");return this.headers.Vary=J,this}compress($){if($)this.headers["Content-Encoding"]=$;return this}build($){let F={...this.headers};if($&&!F["Content-Length"]){let J=typeof $==="string"?new TextEncoder().encode($).length:$.byteLength;F["Content-Length"]=J.toString()}return F}buildHeaders($){return new Headers(this.build($))}}function A(){return new S}function p($,F){let J=A().contentType($);if(F)J.cache(F);return J.build()}var l={css:($)=>A().contentType("css").cache("STYLESHEET").eTag($),javascript:($)=>A().contentType("javascript").cache("JAVASCRIPT").eTag($),hashedAsset:($,F)=>A().filePath($).cache("HASHED_ASSET").eTag(F),font:($)=>A().filePath($).cache("FONT").cors({origin:"*"}),image:($)=>A().filePath($).cache("IMAGE"),favicon:()=>A().contentType("icon").cache("FAVICON"),api:()=>A().contentType("json").cache("API_RESPONSE").cors(),expensiveApi:($)=>A().contentType("json").cache("API_RESPONSE").cors().eTag($),realtime:()=>A().contentType("json").cache("NO_CACHE").cors(),html:()=>A().contentType("html").cache("HTML_PAGE").security(),expensiveHtml:($)=>A().contentType("html").cache("HTML_PAGE").security().eTag($),manifest:($)=>A().contentType("json").cache("MANIFEST").eTag($),serviceWorker:()=>A().contentType("javascript").cache("SERVICE_WORKER"),sitemap:($)=>A().contentType("xml").cache("SITEMAP").eTag($),feed:($,F="rss")=>A().contentType("xml").cache("FEED").eTag($).mimeType(F==="rss"?"application/rss+xml":"application/atom+xml"),video:($)=>A().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),audio:($)=>A().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),pdf:($)=>A().contentType("pdf").cache("DOCUMENT").custom("Content-Disposition","inline").eTag($),download:($,F,J)=>A().filePath(F).cache("ARCHIVE").custom("Content-Disposition",`attachment; filename="${$}"`).eTag(J),secure:()=>A().security({csp:"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",hsts:31536000}).custom("X-Powered-By","Bun"),permanentRedirect:($)=>A().redirect($,!0),temporaryRedirect:($)=>A().redirect($,!1)};async function T($,F,J,_={}){let{maxChunkSize:W=2097152,cacheMaxAge:X=3600,acceptedExtensions:Y}=_;if(!await $.exists())return new Response("Media file not found",{status:404,headers:A().contentType("text").cors({origin:V(F,J)}).build()});if(Y&&Y.length>0){let b=$.name?.toLowerCase().split(".").pop();if(!b||!Y.includes(b))return new Response("File type not supported",{status:415,headers:A().contentType("text").cors({origin:V(F,J)}).build()})}let j=$.size,L=F.headers.get("Range");if(!L)return new Response($,{headers:A().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${X}`).cors({origin:V(F,J)}).build()});let G=L.match(/bytes=(\d*)-(\d*)/);if(!G)return new Response("Invalid Range header",{status:416,headers:A().contentType("text").cors({origin:V(F,J)}).custom("Content-Range",`bytes */${j}`).build()});let U=G[1]?parseInt(G[1]):0,R=G[2]?parseInt(G[2]):j-1,O=Math.min(R,U+W-1,j-1);if(U>=j||O>=j||U>O)return new Response("Range not satisfiable",{status:416,headers:A().contentType("text").cors({origin:V(F,J)}).custom("Content-Range",`bytes */${j}`).build()});return new Response($.slice(U,O+1),{status:206,headers:A().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${X}`).cors({origin:V(F,J)}).build()})}function y($,F,J){return{type:"stream",file:$,request:F,options:J}}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 J of $){let _=await J(F);if(_&&typeof _==="object")Object.assign(F,_)}}async parseInput($,F,J){let _={},W=new URL($.url),X=Object.fromEntries(W.searchParams),Y=$.method;if(Y==="POST"){if(parseInt($.headers.get("content-length")||"0")>this.maxRequestSize)throw Error("Request payload too large")}if(Y==="GET"&&["query","streamQuery","file","fileStream","html"].includes(J))_=X;else if(Y==="POST"&&["mutation","formMutation","streamMutation"].includes(J)){let Z=$.headers.get("content-type");if(Z?.includes("application/json"))_=await $.json();else if(Z?.includes("multipart/form-data")){let j=await $.formData();_=await this.parseFormData(j)}else throw new D({code:"BAD_REQUEST",message:"Unsupported content type"})}else throw new D({code:"METHOD_NOT_SUPPORTED",message:"Unsupported request method"});return F.parse(_)}async parseFormData($){let F={},J={};$.forEach(async(_,W)=>{if(_ instanceof File){if(!J[W])J[W]=[];J[W].push(_)}else if(typeof _==="string")try{F[W]=JSON.parse(_)}catch{F[W]=_}});for(let[_,W]of Object.entries(J))F[_]=W;return F}handleError($,F){if(this.onError)try{this.onError($,{req:F,route:F.url})}catch(_){console.error("Error logger failed:",_)}let J=this.serializeError($);return new Response(JSON.stringify({error:J.error}),{status:J.status,headers:A().contentType("json").cors({origin:V(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:A().cors({origin:V($,this.allowedOrigins)}).build()});let _=new URL($.url).pathname;if(_=_.startsWith(this.prefix)?_.slice(this.prefix.length):_,_=_.startsWith("/")?_:"/"+_,_==="/ws"&&F){if(this.debug)console.log("\uD83D\uDD0C WebSocket upgrade requested");return this.handleWebSocketUpgrade($,F)}let W=_==="/"?[]:_.split("/").filter(Boolean),X=this.findRoute(W);if(!X)return new Response("Not Found",{status:404,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let{route:Y}=X,Z=Y._timeout??this.requestTimeout,j=new Promise((L,G)=>{setTimeout(()=>G(Error("Request timeout")),Z)});return await Promise.race([this._handleRequest($,F,X),j])}catch(J){return this.handleError(J,$)}}async _handleRequest($,F,J){try{if(!J)return new Response("Not Found",{status:404,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let{params:_,route:W}=J;if(!W.handler)return new Response("Handler not implemented",{status:501,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let X=await this.contextCreator($);Object.assign(X,{req:$,params:_,publishToProcedure:async(L,G,U)=>{await this.publishToProcedure(L,G,X,U)}});for(let L of this.globalMiddlewares){let G=await L(X);if(G!==null&&G!==void 0&&typeof G==="object")Object.assign(X,G)}for(let L of W.middlewares){let G=await L(X);if(G!==null&&G!==void 0&&typeof G==="object")Object.assign(X,G)}let Y=await this.parseInput($,W._input,W._type),Z=await W.handler({ctx:X,input:Y}),j=A().cors({origin:V($,X.allowedOrigins??this.allowedOrigins)}).build();return Object.keys(j).forEach((L)=>{X.headers.set(L,j[L])}),await this.handleProcedureResponse(W._type,Z,X.headers)}catch(_){return this.handleError(_,$)}}async testRequest($){return this.handleRequest($)}async handleProcedureResponse($,F,J){switch($){case"file":if(F instanceof Response)return F;let _=F.type;return J.append("Content-Type",_),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(_)),new Response(F,{headers:J});case"fileStream":if(typeof F==="object"&&F!==null&&"type"in F&&F.type==="stream"){let X=F;return await T(F.file,F.request,this.allowedOrigins,X.options)}let W=F.type;return J.append("Content-Type",W),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(W)),new Response(F,{headers:J});case"html":if(typeof F==="string")return J.append("Content-Type","text/html"),new Response(F,{headers:J});break;case"query":case"mutation":case"formMutation":case"streamQuery":case"streamMutation":case"subscription":default:return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}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 J=await this.createWebSocketContext($);if(F.upgrade($,{data:{ctx:J,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=$,J=F?.issues??F?.errors;if(Array.isArray(J)&&F.name==="ZodError")return{status:400,error:{name:"ValidationError",code:"BAD_REQUEST",clientCode:"VALIDATION_ERROR",message:"Input validation failed",data:{validationErrors:J.map((_)=>({path:_.path.join("."),message:_.message,code:_.code,received:_.received}))},httpStatus:400}};if($ instanceof D)return{status:$.httpStatus,error:$.toJSON()};if($ instanceof Error){let _=$.message==="Not Found"?404:$.message==="Unauthorized"?401:$.message==="Forbidden"?403:$.message==="Request payload too large"?413:$.message==="Request timeout"?504:500;return{status:_,error:{code:_===404?"NOT_FOUND":_===401?"UNAUTHORIZED":_===403?"FORBIDDEN":_===413?"PAYLOAD_TOO_LARGE":_===504?"GATEWAY_TIMEOUT":"INTERNAL_SERVER_ERROR",message:_<500?$.message:"Internal Server Error"}}}return{status:500,error:{code:"INTERNAL_SERVER_ERROR",message:"Internal Server Error"}}}sendWebSocketError($,F,J,_){let W=F==="authenticate"?"auth_error":F==="subscribe"?"subscribe_error":F==="unsubscribe"?"unsubscribe_error":F==="publish"?"publish_error":"error",X=this.serializeError(_);$.send(JSON.stringify({type:W,...J?{topic:J}:{},error:{...X.error,httpStatus:X.error.httpStatus??X.status}}))}async handleAuthenticate($,F,J){try{let _=F.startsWith("Bearer ")?F.substring(7):F,W=new Headers(J.req.headers);if(_)W.set("authorization",`Bearer ${_}`);else W.delete("authorization");let X=new Request(J.req,{headers:W});if($.data.ctx=await this.createWebSocketContext(X),$.send(JSON.stringify({type:"auth_success",message:"Authentication successful"})),this.debug)console.log("[BRPC]","WebSocket authenticated successfully")}catch(_){if(this.sendWebSocketError($,"authenticate",void 0,_),this.debug)console.error("WebSocket authentication error:",_)}}async handleSubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");let W=this.createWebSocketOperationContext(J,_.params);await this.runMiddlewares(_.route.subscribeMiddlewares??[],W),$.subscribe(F),$.data.topics.add(F),$.send(JSON.stringify({type:"subscribe_success",topic:F}))}async handleUnsubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");$.unsubscribe(F),$.data.topics.delete(F)}async handlePublish($,F,J,_){let W=this.findRoute(F.split("/").filter(Boolean));if(!W)throw Error("Subscription not found");let{params:X,route:Y}=W;if(Y._type!=="subscription"||!Y.handler)throw Error("Invalid subscription route");let Z=this.createWebSocketOperationContext(_,X);await this.runMiddlewares(Y.middlewares,Z);let j=Y._input.fields?Y._input.parse(J):J,L=await Y.handler({ctx:Z,input:j}),G={topic:F,data:L,type:"publish"};if(this.debug)console.log(`Server will publish to ${F}`,G);this.server.publish(F,JSON.stringify(G))}async publishToProcedure($,F,J,_){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let W=this.findRoutePath(this.routes,$);if(!W)throw Error("Subscription route not found");let X=W.split("/").map((j)=>{if(j.startsWith(":")){let L=j.slice(1),G=_[L];if(!G)throw Error(`Missing parameter: ${L}`);return G}return j}).join("/"),Y=await $.handler({ctx:{...J,params:_},input:F}),Z={topic:X,data:Y,type:"publish"};if(this.debug)console.log(`Server will publish to ${X}`,Z);return this.server?.publish(X,JSON.stringify(Z)),Y}async publish($,F,J){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let _=this.findRoutePath(this.routes,$);if(!_)throw Error("Subscription route not found");let W=_.split("/").map((Z)=>{if(Z.startsWith(":")){let j=Z.slice(1),L=J?J[j]:null;if(!L)throw Error(`Missing parameter: ${j}`);return L}return Z}).join("/"),X=await $.handler({ctx:{params:J},input:F}),Y={topic:W,data:X,type:"publish"};if(this.debug)console.log(`Server will publish to ${W}`,Y);return this.server?.publish(W,JSON.stringify(Y)),X}freePublish($,F){let J={topic:$,data:F,type:"publish"};this.server?.publish($,JSON.stringify(J))}findRoutePath($,F,J=""){for(let[_,W]of Object.entries($)){let X=J?`${J}/${_}`:_;if(W===F)return X;if(W&&typeof W==="object"&&!("_type"in W)){let Y=this.findRoutePath(W,F,X);if(Y)return Y}}return null}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(J)=>{return await F(J)}}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 J=this.routeMatcher.benchmark($,F);return console.log("[ROUTEMATCHER]","Route matching benchmark:",J),J}}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:(J,_)=>this.handleRequest(J,_),websocket:{open:(J)=>{if(this.activeConnections.add(J),this.wsConfig?.onOpen)this.wsConfig.onOpen(J,J.data.ctx);if(this.debug)console.log("[BRPC]","WebSocket connection opened",{connections:this.activeConnections.size})},message:async(J,_)=>{let W,X;try{J.data.lastActivity=Date.now();let Y=JSON.parse(_),{token:Z,data:j}=Y;W=Y.type,X=Y.topic;let L=J.data.ctx;if(this.debug)console.log("[BRPC]","WebSocket message received:",{type:W,topic:X});switch(W){case"authenticate":await this.handleAuthenticate(J,Z,L);break;case"subscribe":if(!X)throw D.badRequest("Subscription topic is required");await this.handleSubscribe(J,X,L);break;case"unsubscribe":if(!X)throw D.badRequest("Subscription topic is required");await this.handleUnsubscribe(J,X,L);break;case"publish":if(!X)throw D.badRequest("Subscription topic is required");await this.handlePublish(J,X,j,L);break;default:if(this.sendWebSocketError(J,W,X,D.badRequest("Unknown message type")),this.debug)console.error("Unknown WebSocket message type:",W)}}catch(Y){if(this.debug)console.error("[BRPC]","WebSocket message error:",Y);this.sendWebSocketError(J,W,X,Y)}},close:(J,_,W)=>{if(this.activeConnections.delete(J),J.data.topics.forEach((X)=>{J.unsubscribe(X)}),this.debug)console.log("[BRPC]","WebSocket connection closed",{code:_,reason:W,remainingConnections:this.activeConnections.size});if(this.wsConfig?.onClose)this.wsConfig.onClose(J,_,W,J.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}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:J,messages:_={}}={})=>{let W=F?B(F):1/0,X=J?B(J):0,Y=$?Object.entries($).flatMap(([j,L])=>{let G=j;if(L==="*")return P[G];if(Array.isArray(L))return L;return[]}):void 0,Z=z.instanceof(File,{message:_.required??"File is required"});if(Z=Z.refine((j)=>j.size<=W,_.maxSize??`File size must be less than ${F}MB`).refine((j)=>j.size>=X,_.minSize??`File size must be at least ${J}MB`),Y?.length)Z=Z.refine((j)=>Y.includes(j.type),_.type??`File must be of type: ${Y.join(", ")}`);return Z};var _$=($)=>{let F=$.paths.map((_)=>new RegExp(_)),J="Not Found";return async(_)=>{let X=new URL(_.req.url).pathname;if(F.some((Y)=>Y.test(X)))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[J,_]of this.requests.entries()){let W=$-_.startTime>this.config.windowMs,X=$-_.lastAccessed>this.config.windowMs*2;if(W||X)this.requests.delete(J),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(),J=this.requests.get($);if(!J||F-J.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};J.lastAccessed=F;let _=J.count>=this.config.maxRequests;if(!_)J.count++;return{isLimited:_,remaining:Math.max(0,this.config.maxRequests-J.count),resetTime:J.startTime+this.config.windowMs}}getConfig(){return this.config}}var X$=($)=>{let F=new E($);return async(J)=>{if(J.req.headers.get("range"))return;let _=J.req.headers.get("x-forwarded-for")?.split(",")[0]||J.req.headers.get("x-real-ip")||"unknown",{isLimited:W,remaining:X,resetTime:Y}=F.check(_),{headerPrefix:Z}=F.getConfig();if(J.headers.set(`${Z}-Remaining`,X.toString()),J.headers.set(`${Z}-Reset`,Y.toString()),W)throw J.headers.set(`${Z}-Exceeded`,"true"),Error(F.getConfig().message)}};function Z$($,F){return F??$}async function L$($,F){let J=performance.now();try{return await F()}catch(_){let W=performance.now();throw console.log(`[PERF] ${$} failed after ${(W-J).toFixed(4)} ms`),_}finally{let _=performance.now();console.log(`[PERF] ${$}: ${(_-J).toFixed(4)} ms`)}}export{y as streamMedia,p as quickHeaders,L$ as measure,f as hashContent,K as getMimeType,V as getCorsOrigin,a as createRouter,X$ as createRateLimiter,k as createProcedure,_$ as createPathBlocker,Z$ as createMiddleware,F$ as createFileSchema,r as createContext,l as commonHeaders,A as buildHeaders,H as Router,D as BRPCError};
7
+ if($.wildcard&&F.length===0)return{params:{..._,wildcardPath:""},route:$.wildcard.handler};return null}let W=F[J],X=decodeURIComponent(W),Y=$.static.get(X);if(Y){let j=this.traverseTree(Y,F,J+1,_);if(j)return j}if($.param){let j={..._,[$.param.name]:X},A=this.traverseTree($.param.node,F,J+1,j);if(A)return A}if($.wildcard){let j=F.slice(J).join("/");return{params:{..._,wildcardPath:j},route:$.wildcard.handler}}return null}compileRoutes($,F=[]){let J={static:new Map};for(let[_,W]of Object.entries($)){if(_==="index"&&this.isProcedure(W)){J.index={handler:W,paramNames:[...F],hasWildcard:!1};continue}if(_==="*"){if(this.isProcedure(W))J.wildcard={handler:W,paramNames:[...F,"wildcardPath"],hasWildcard:!0,wildcardIndex:F.length};continue}if(this.isProcedure(W)){let X={handler:W,paramNames:[...F],hasWildcard:!1};if(_.startsWith(":")){let Y=_.slice(1),j={static:new Map,handler:X};J.param={name:Y,node:j}}else{let Y={static:new Map,handler:X};J.static.set(_,Y)}}else if(typeof W==="object"&&W!==null)if(_.startsWith(":")){let X=_.slice(1),Y=this.compileRoutes(W,[...F,X]);J.param={name:X,node:Y}}else{let X=this.compileRoutes(W,F);J.static.set(_,X)}}return J}isProcedure($){return $&&typeof $==="object"&&"_type"in $&&"_input"in $}cacheResult($,F){if(this.routeCache.size>=this.options.maxCacheSize){let J=this.routeCache.keys().next().value;if(J!==void 0){if(this.routeCache.delete(J),this.options.debug)console.log(`\uD83D\uDDD1\uFE0F Evicted oldest cache entry: ${J}`)}}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}},J=(_,W=0)=>{F.maxDepth=Math.max(F.maxDepth,W);for(let[X,Y]of Object.entries(_))if(this.isProcedure(Y)){if(F.totalRoutes++,Y._type in F.procedureTypes)F.procedureTypes[Y._type]++;if(X==="index")F.indexRoutes++;else if(X.startsWith(":"))F.paramRoutes++;else if(X==="*")F.wildcardRoutes++;else F.staticRoutes++}else if(typeof Y==="object"&&Y!==null)J(Y,W+1)};return J($),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),J=F.join("/"),_=this.routeCache.has(J),W=performance.now(),X=this.matchDirect(F),Y=performance.now()-W;return{found:!!X,route:X?.route,params:X?.params,cached:_,matchTime:Y}}benchmark($,F=1000){let J=$.map((G)=>G.split("/").filter(Boolean)),_=new Map(this.routeCache);this.routeCache.clear();let W=performance.now();for(let G=0;G<F;G++)for(let U of J)this.matchDirect(U);let Y=performance.now()-W,j=F*$.length,A=Y/j,V=j/Y*1000;return this.routeCache=_,{averageTime:A,totalTime:Y,routesPerSecond:V}}warmupCache($){if(!this.options.enableCaching)return;let F=performance.now(),J=0;for(let W of $){let X=W.split("/").filter(Boolean),Y=this.matchDirect(X);if(Y)this.cacheResult(X.join("/"),Y),J++}let _=performance.now();if(this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDD25 Cache warmed up: ${J} routes in ${(_-F).toFixed(2)}ms`)}}var C=import.meta.require,q={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 v($){return q[$]}var x={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 x[$]||"application/octet-stream"}function f($){if(typeof Bun<"u"&&Bun.hash)return Bun.hash($).toString(16);try{let F=C("crypto"),J;if(typeof $==="string")J=Buffer.from($);else if($ instanceof ArrayBuffer)J=Buffer.from(new Uint8Array($));else J=Buffer.from($);return F.createHash("md5").update(J).digest("hex")}catch{let F=typeof $==="string"?new TextEncoder().encode($):$ instanceof ArrayBuffer?new Uint8Array($):$,J=0;for(let _=0;_<F.length;_++)J=(J<<5)-J+F[_],J=J&J;return Math.abs(J).toString(16).padStart(16,"0")}}function D($,F,J){let _=$.headers.get("Origin"),W=!0;if((J?.allowAll??!0)&&_)return _;if(F.length===0)return _||"*";if(!_)return F[0];if(F.includes(_))return _;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:J}=v($);if(this.headers["Cache-Control"]=F,J)this.headers.Expires=J;return this}eTag($){if(!$)return this;let F;if(typeof $==="string"&&$.startsWith('"'))F=$;else F=`"${f($)}"`;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:J=["Content-Type","Authorization"],maxAge:_=86400,credentials:W}={}){let X=W??$!=="*";if(X&&$==="*")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"]=J.join(", "),X)this.headers["Access-Control-Allow-Credentials"]="true";return this.headers["Access-Control-Max-Age"]=_.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 J=typeof F.hsts==="number"?F.hsts:31536000;this.headers["Strict-Transport-Security"]=`max-age=${J}; 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,J=F?`${F}, ${$.join(", ")}`:$.join(", ");return this.headers.Vary=J,this}compress($){if($)this.headers["Content-Encoding"]=$;return this}build($){let F={...this.headers};if($&&!F["Content-Length"]){let J=typeof $==="string"?new TextEncoder().encode($).length:$.byteLength;F["Content-Length"]=J.toString()}return F}buildHeaders($){return new Headers(this.build($))}}function Z(){return new S}function p($,F){let J=Z().contentType($);if(F)J.cache(F);return J.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,J)=>Z().filePath(F).cache("ARCHIVE").custom("Content-Disposition",`attachment; filename="${$}"`).eTag(J),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,J,_={}){let{maxChunkSize:W=2097152,cacheMaxAge:X=3600,acceptedExtensions:Y}=_;if(!await $.exists())return new Response("Media file not found",{status:404,headers:Z().contentType("text").cors({origin:D(F,J)}).build()});if(Y&&Y.length>0){let b=$.name?.toLowerCase().split(".").pop();if(!b||!Y.includes(b))return new Response("File type not supported",{status:415,headers:Z().contentType("text").cors({origin:D(F,J)}).build()})}let A=$.size,V=F.headers.get("Range");if(!V)return new Response($,{headers:Z().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${X}`).cors({origin:D(F,J)}).build()});let G=V.match(/bytes=(\d*)-(\d*)/);if(!G)return new Response("Invalid Range header",{status:416,headers:Z().contentType("text").cors({origin:D(F,J)}).custom("Content-Range",`bytes */${A}`).build()});let U=G[1]?parseInt(G[1]):0,R=G[2]?parseInt(G[2]):A-1,O=Math.min(R,U+W-1,A-1);if(U>=A||O>=A||U>O)return new Response("Range not satisfiable",{status:416,headers:Z().contentType("text").cors({origin:D(F,J)}).custom("Content-Range",`bytes */${A}`).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=${X}`).cors({origin:D(F,J)}).build()})}function y($,F,J){return{type:"stream",file:$,request:F,options:J}}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 J of $){let _=await J(F);if(_&&typeof _==="object")Object.assign(F,_)}}async parseInput($,F,J){let _={},W=new URL($.url),X=Object.fromEntries(W.searchParams),Y=$.method;if(Y==="POST"){if(parseInt($.headers.get("content-length")||"0")>this.maxRequestSize)throw Error("Request payload too large")}if(Y==="GET"&&["query","streamQuery","file","fileStream","html"].includes(J))_=X;else if(Y==="POST"&&["mutation","formMutation","streamMutation"].includes(J)){let j=$.headers.get("content-type");if(j?.includes("application/json"))_=await $.json();else if(j?.includes("multipart/form-data")){let A=await $.formData();_=await this.parseFormData(A)}else throw new L({code:"BAD_REQUEST",message:"Unsupported content type"})}else throw new L({code:"METHOD_NOT_SUPPORTED",message:"Unsupported request method"});return F.parse(_)}async parseFormData($){let F={},J={};$.forEach(async(_,W)=>{if(_ instanceof File){if(!J[W])J[W]=[];J[W].push(_)}else if(typeof _==="string")try{F[W]=JSON.parse(_)}catch{F[W]=_}});for(let[_,W]of Object.entries(J))F[_]=W;return F}handleError($,F){if(this.onError)try{this.onError($,{req:F,route:F.url})}catch(_){console.error("Error logger failed:",_)}let J=this.serializeError($);return new Response(JSON.stringify({error:J.error}),{status:J.status,headers:Z().contentType("json").cors({origin:D(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:D($,this.allowedOrigins)}).build()});let _=new URL($.url).pathname;if(_=_.startsWith(this.prefix)?_.slice(this.prefix.length):_,_=_.startsWith("/")?_:"/"+_,_==="/ws"&&F){if(this.debug)console.log("\uD83D\uDD0C WebSocket upgrade requested");return this.handleWebSocketUpgrade($,F)}let W=_==="/"?[]:_.split("/").filter(Boolean),X=this.findRoute(W);if(!X)return new Response("Not Found",{status:404,headers:Z().contentType("text").cors({origin:D($,this.allowedOrigins)}).build()});let{route:Y}=X,j=Y._timeout??this.requestTimeout,A=new Promise((V,G)=>{setTimeout(()=>G(Error("Request timeout")),j)});return await Promise.race([this._handleRequest($,F,X),A])}catch(J){return this.handleError(J,$)}}async _handleRequest($,F,J){try{if(!J)return new Response("Not Found",{status:404,headers:Z().contentType("text").cors({origin:D($,this.allowedOrigins)}).build()});let{params:_,route:W}=J;if(!W.handler)return new Response("Handler not implemented",{status:501,headers:Z().contentType("text").cors({origin:D($,this.allowedOrigins)}).build()});let X=await this.contextCreator($);Object.assign(X,{req:$,params:_,publishToProcedure:async(V,G,U)=>{await this.publishToProcedure(V,G,X,U)}});for(let V of this.globalMiddlewares){let G=await V(X);if(G!==null&&G!==void 0&&typeof G==="object")Object.assign(X,G)}for(let V of W.middlewares){let G=await V(X);if(G!==null&&G!==void 0&&typeof G==="object")Object.assign(X,G)}let Y=await this.parseInput($,W._input,W._type),j=await W.handler({ctx:X,input:Y}),A=Z().cors({origin:D($,X.allowedOrigins??this.allowedOrigins)}).build();return Object.keys(A).forEach((V)=>{X.headers.set(V,A[V])}),await this.handleProcedureResponse(W._type,j,X.headers)}catch(_){return this.handleError(_,$)}}async testRequest($){return this.handleRequest($)}async handleProcedureResponse($,F,J){switch($){case"file":if(F instanceof Response)return F;let _=F.type;return J.append("Content-Type",_),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(_)),new Response(F,{headers:J});case"fileStream":if(typeof F==="object"&&F!==null&&"type"in F&&F.type==="stream"){let X=F;return await T(F.file,F.request,this.allowedOrigins,X.options)}let W=F.type;return J.append("Content-Type",W),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(W)),new Response(F,{headers:J});case"html":if(typeof F==="string")return J.append("Content-Type","text/html"),new Response(F,{headers:J});break;case"query":case"mutation":case"formMutation":case"streamQuery":case"streamMutation":case"subscription":default:return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}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 J=await this.createWebSocketContext($);if(F.upgrade($,{data:{ctx:J,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=$,J=F?.issues??F?.errors;if(Array.isArray(J)&&F.name==="ZodError")return{status:400,error:{name:"ValidationError",code:"BAD_REQUEST",clientCode:"VALIDATION_ERROR",message:"Input validation failed",data:{validationErrors:J.map((_)=>({path:_.path.join("."),message:_.message,code:_.code,received:_.received}))},httpStatus:400}};if($ instanceof L)return{status:$.httpStatus,error:$.toJSON()};if($ instanceof Error){let _=$.message==="Not Found"?404:$.message==="Unauthorized"?401:$.message==="Forbidden"?403:$.message==="Request payload too large"?413:$.message==="Request timeout"?504:500;return{status:_,error:{code:_===404?"NOT_FOUND":_===401?"UNAUTHORIZED":_===403?"FORBIDDEN":_===413?"PAYLOAD_TOO_LARGE":_===504?"GATEWAY_TIMEOUT":"INTERNAL_SERVER_ERROR",message:_<500?$.message:"Internal Server Error"}}}return{status:500,error:{code:"INTERNAL_SERVER_ERROR",message:"Internal Server Error"}}}sendWebSocketError($,F,J,_){let W=F==="authenticate"?"auth_error":F==="subscribe"?"subscribe_error":F==="unsubscribe"?"unsubscribe_error":F==="publish"?"publish_error":"error",X=this.serializeError(_);$.send(JSON.stringify({type:W,...J?{topic:J}:{},error:{...X.error,httpStatus:X.error.httpStatus??X.status}}))}async handleAuthenticate($,F,J){try{let _=F.startsWith("Bearer ")?F.substring(7):F,W=new Headers(J.req.headers);if(_)W.set("authorization",`Bearer ${_}`);else W.delete("authorization");let X=new Request(J.req,{headers:W});if($.data.ctx=await this.createWebSocketContext(X),$.send(JSON.stringify({type:"auth_success",message:"Authentication successful"})),this.debug)console.log("[BRPC]","WebSocket authenticated successfully")}catch(_){if(this.sendWebSocketError($,"authenticate",void 0,_),this.debug)console.error("WebSocket authentication error:",_)}}async handleSubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");let W=this.createWebSocketOperationContext(J,_.params);await this.runMiddlewares(_.route.subscribeMiddlewares??[],W),$.subscribe(F),$.data.topics.add(F),$.send(JSON.stringify({type:"subscribe_success",topic:F}))}async handleUnsubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");$.unsubscribe(F),$.data.topics.delete(F)}async handlePublish($,F,J,_){let W=this.findRoute(F.split("/").filter(Boolean));if(!W)throw Error("Subscription not found");let{params:X,route:Y}=W;if(Y._type!=="subscription"||!Y.handler)throw Error("Invalid subscription route");let j=this.createWebSocketOperationContext(_,X);await this.runMiddlewares(Y.middlewares,j);let A=Y._input.fields?Y._input.parse(J):J,V=await Y.handler({ctx:j,input:A}),G={topic:F,data:V,type:"publish"};if(this.debug)console.log(`Server will publish to ${F}`,G);this.server.publish(F,JSON.stringify(G))}async publishToProcedure($,F,J,_){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let W=this.resolveSubscriptionTopic($,_),X=await $.handler({ctx:{...J,params:_},input:F}),Y={topic:W,data:X,type:"publish"};if(this.debug)console.log(`Server will publish to ${W}`,Y);return this.server?.publish(W,JSON.stringify(Y)),X}async publish($,F,J){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let _=this.resolveSubscriptionTopic($,J),W=await $.handler({ctx:{params:J},input:F}),X={topic:_,data:W,type:"publish"};if(this.debug)console.log(`Server will publish to ${_}`,X);return this.server?.publish(_,JSON.stringify(X)),W}freePublish($,F){let J={topic:$,data:F,type:"publish"};this.server?.publish($,JSON.stringify(J))}findRoutePath($,F,J=""){for(let[_,W]of Object.entries($)){let X=J?`${J}/${_}`:_;if(W===F)return X;if(W&&typeof W==="object"&&!("_type"in W)){let Y=this.findRoutePath(W,F,X);if(Y)return Y}}return null}resolveSubscriptionTopic($,F){let J=this.findRoutePath(this.routes,$);if(!J)throw Error("Subscription route not found");return J.split("/").map((_)=>{if(!_.startsWith(":"))return _;let W=_.slice(1),X=F?.[W];if(!X)throw Error(`Missing parameter: ${W}`);return encodeURIComponent(X)}).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(J)=>{return await F(J)}}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 J=this.routeMatcher.benchmark($,F);return console.log("[ROUTEMATCHER]","Route matching benchmark:",J),J}}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:(J,_)=>this.handleRequest(J,_),websocket:{open:(J)=>{if(this.activeConnections.add(J),this.wsConfig?.onOpen)this.wsConfig.onOpen(J,J.data.ctx);if(this.debug)console.log("[BRPC]","WebSocket connection opened",{connections:this.activeConnections.size})},message:async(J,_)=>{let W,X;try{J.data.lastActivity=Date.now();let Y=JSON.parse(_),{token:j,data:A}=Y;W=Y.type,X=Y.topic;let V=J.data.ctx;if(this.debug)console.log("[BRPC]","WebSocket message received:",{type:W,topic:X});switch(W){case"authenticate":await this.handleAuthenticate(J,j,V);break;case"subscribe":if(!X)throw L.badRequest("Subscription topic is required");await this.handleSubscribe(J,X,V);break;case"unsubscribe":if(!X)throw L.badRequest("Subscription topic is required");await this.handleUnsubscribe(J,X,V);break;case"publish":if(!X)throw L.badRequest("Subscription topic is required");await this.handlePublish(J,X,A,V);break;default:if(this.sendWebSocketError(J,W,X,L.badRequest("Unknown message type")),this.debug)console.error("Unknown WebSocket message type:",W)}}catch(Y){if(this.debug)console.error("[BRPC]","WebSocket message error:",Y);this.sendWebSocketError(J,W,X,Y)}},close:(J,_,W)=>{if(this.activeConnections.delete(J),J.data.topics.forEach((X)=>{J.unsubscribe(X)}),this.debug)console.log("[BRPC]","WebSocket connection closed",{code:_,reason:W,remainingConnections:this.activeConnections.size});if(this.wsConfig?.onClose)this.wsConfig.onClose(J,_,W,J.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}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:J,messages:_={}}={})=>{let W=F?B(F):1/0,X=J?B(J):0,Y=$?Object.entries($).flatMap(([A,V])=>{let G=A;if(V==="*")return P[G];if(Array.isArray(V))return V;return[]}):void 0,j=z.instanceof(File,{message:_.required??"File is required"});if(j=j.refine((A)=>A.size<=W,_.maxSize??`File size must be less than ${F}MB`).refine((A)=>A.size>=X,_.minSize??`File size must be at least ${J}MB`),Y?.length)j=j.refine((A)=>Y.includes(A.type),_.type??`File must be of type: ${Y.join(", ")}`);return j};var _$=($)=>{let F=$.paths.map((_)=>new RegExp(_)),J="Not Found";return async(_)=>{let X=new URL(_.req.url).pathname;if(F.some((Y)=>Y.test(X)))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[J,_]of this.requests.entries()){let W=$-_.startTime>this.config.windowMs,X=$-_.lastAccessed>this.config.windowMs*2;if(W||X)this.requests.delete(J),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(),J=this.requests.get($);if(!J||F-J.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};J.lastAccessed=F;let _=J.count>=this.config.maxRequests;if(!_)J.count++;return{isLimited:_,remaining:Math.max(0,this.config.maxRequests-J.count),resetTime:J.startTime+this.config.windowMs}}getConfig(){return this.config}}var X$=($)=>{let F=new E($);return async(J)=>{if(J.req.headers.get("range"))return;let _=J.req.headers.get("x-forwarded-for")?.split(",")[0]||J.req.headers.get("x-real-ip")||"unknown",{isLimited:W,remaining:X,resetTime:Y}=F.check(_),{headerPrefix:j}=F.getConfig();if(J.headers.set(`${j}-Remaining`,X.toString()),J.headers.set(`${j}-Reset`,Y.toString()),W)throw J.headers.set(`${j}-Exceeded`,"true"),Error(F.getConfig().message)}};function Z$($,F){return F??$}async function V$($,F){let J=performance.now();try{return await F()}catch(_){let W=performance.now();throw console.log(`[PERF] ${$} failed after ${(W-J).toFixed(4)} ms`),_}finally{let _=performance.now();console.log(`[PERF] ${$}: ${(_-J).toFixed(4)} ms`)}}export{y as streamMedia,p as quickHeaders,V$ as measure,f as hashContent,K as getMimeType,D as getCorsOrigin,a as createRouter,X$ as createRateLimiter,k as createProcedure,_$ as createPathBlocker,Z$ as createMiddleware,F$ as createFileSchema,r as createContext,l as commonHeaders,Z as buildHeaders,H as Router,L as BRPCError};
8
8
 
9
- //# debugId=821D3C5A1AE5704064756E2164756E21
9
+ //# debugId=0BB226F48EDCE9A664756E2164756E21
@@ -115,6 +115,7 @@ export declare class Router<C extends BaseContext> {
115
115
  * @description finds the full path to a procedure in the routes tree
116
116
  */
117
117
  private findRoutePath;
118
+ private resolveSubscriptionTopic;
118
119
  private getIntegrationRoutes;
119
120
  /**
120
121
  * New utility methods for route management
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mateosuarezdev/brpc",
3
- "version": "1.0.89",
3
+ "version": "1.0.92",
4
4
  "description": "A Type-Safe, Flexible Web application framework for Bun",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",