@mateosuarezdev/brpc 1.0.58 → 1.0.60
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
|
@@ -460,11 +460,13 @@ Import from `@mateosuarezdev/brpc/client`:
|
|
|
460
460
|
import { createBrpcClient } from "@mateosuarezdev/brpc/client";
|
|
461
461
|
import type { AppRoutes } from "./routes";
|
|
462
462
|
|
|
463
|
-
const client = createBrpcClient<AppRoutes>({
|
|
463
|
+
const client = createBrpcClient<AppRoutes>("https://api.example.com", {
|
|
464
464
|
headers: async () => ({
|
|
465
465
|
Authorization: `Bearer ${getToken()}`,
|
|
466
466
|
}),
|
|
467
467
|
prefix: "/api",
|
|
468
|
+
s3Endpoint: "https://cdn.example.com",
|
|
469
|
+
nodeEnv: "production",
|
|
468
470
|
debug: false,
|
|
469
471
|
});
|
|
470
472
|
|
|
@@ -500,16 +502,51 @@ unsubscribe();
|
|
|
500
502
|
| `WebSocket` | `typeof WebSocket` | Custom WebSocket implementation |
|
|
501
503
|
| `prefix` | `string` | API path prefix |
|
|
502
504
|
| `apiPrefix` | `string` | Additional API prefix |
|
|
505
|
+
| `s3Endpoint` | `string` | **Required.** S3/CDN endpoint for resolving storage object URLs |
|
|
506
|
+
| `nodeEnv` | `string` | **Required.** Current environment (`"development"` \| `"production"`) |
|
|
503
507
|
| `debug` | `boolean` | Enable client-side debug logging |
|
|
504
508
|
|
|
505
509
|
### Cache Keys
|
|
506
510
|
|
|
507
|
-
Every procedure exposes helpers for use with query libraries like TanStack Query
|
|
511
|
+
Every procedure exposes helpers for use with query libraries like TanStack Query or `@mateosuarezdev/query`:
|
|
508
512
|
|
|
509
513
|
```ts
|
|
510
|
-
|
|
511
|
-
client.routes.users.getById.
|
|
512
|
-
|
|
514
|
+
// String key
|
|
515
|
+
client.routes.users.getById.getStringKey({ id: "123" });
|
|
516
|
+
// → "users/getById?{"id":"123"}"
|
|
517
|
+
|
|
518
|
+
// Array key — input from backend schema
|
|
519
|
+
client.routes.users.getById.getArrayKey({ id: "123" });
|
|
520
|
+
// → ["users/getById", "id", "123"]
|
|
521
|
+
|
|
522
|
+
// Array key — no backend inputs
|
|
523
|
+
client.routes.users.list.getArrayKey();
|
|
524
|
+
// → ["users/list"]
|
|
525
|
+
|
|
526
|
+
// No-inputs shorthand (always ignores any input)
|
|
527
|
+
client.routes.users.list.getNoInputsArrayKey();
|
|
528
|
+
// → ["users/list"]
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### Client-side context keys
|
|
532
|
+
|
|
533
|
+
`getArrayKey` accepts an optional second `context` argument for keys that are meaningful on the client but not sent to the server — the most common case being scoping cached data by the current user so different users' data never overlaps in the same cache:
|
|
534
|
+
|
|
535
|
+
```ts
|
|
536
|
+
// Procedure has no backend input, but cache is scoped per user
|
|
537
|
+
client.routes.workout.getSessions.getArrayKey(undefined, { userId });
|
|
538
|
+
// → ["workout/getSessions", "userId", "abc123"]
|
|
539
|
+
|
|
540
|
+
// Procedure has backend input AND needs user scoping
|
|
541
|
+
client.routes.workout.getSession.getArrayKey({ sessionId }, { userId });
|
|
542
|
+
// → ["workout/getSession", "sessionId", "42", "userId", "abc123"]
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
`context` keys are sorted and merged with input keys deterministically, so the resulting array is always stable. Invalidation works the same way:
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
// Invalidate all session queries for a specific user
|
|
549
|
+
queryService.invalidateQueries(["workout/getSessions", "userId", userId]);
|
|
513
550
|
```
|
|
514
551
|
|
|
515
552
|
### Utilities
|
package/dist/client/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
function v(q,G={placeholder:void 0,throwOnInvalid:!0},L){if(!q)return G?.placeholder??"PLACEHOLDER";if(q.url)return q.url;if(q.key)if(q.isPrivate||q.provider==="local")return`/storage/${q.key}`;else return`${L}/${q.key}`;if(G.throwOnInvalid)throw Error("Invalid storage object");else return G?.placeholder??"PLACEHOLDER"}class w extends Error{status;code;clientCode;data;constructor(q,G,L,N,W){super(q);this.name="BrpcClientError",this.status=G,this.code=L,this.clientCode=N,this.data=W}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"}}function H(q){if(!q)return null;return q.replace(/\//g,"")}function b(q){let G=q&&q.length>0?q:typeof window<"u"?window.location.origin:"";return G=G.endsWith("/")?G.slice(0,-1):G,G}function y(q,G){let L=G?`/${G}/ws`:"/ws";return`${q.replace(/^http/,"ws")}${L}`}function k(q,G){if(q&&G)return`/${q}/${G}`;else if(q&&!G)return`/${q}`;else if(!q&&G)return`/${G}`;else return""}class U{baseUrl;ws=null;subscriptions=new Map;messageQueue=[];isConnected=!1;reconnectTimeout=null;WebSocketImpl;getHeaders;authToken=null;debug;pendingAuth=!1;authActions=[];constructor(q,G,L,N=!1,W){this.baseUrl=q;this.WebSocketImpl=G,this.debug=N&&W==="development",this.getHeaders=async()=>{if(typeof L==="function")return await L();return L??{}},this.connect()}async connect(){try{let q=await this.getHeaders();this.authToken=q.Authorization||null;let G=this.baseUrl;if(this.debug)console.log(`Connecting to WebSocket: ${G}`);this.ws=new this.WebSocketImpl(G),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");this.authActions.push(()=>{for(let L of this.subscriptions.keys())if(this.send({type:"subscribe",topic:L}),this.debug)console.log(`Resubscribed to topic: ${L}`);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((L)=>this.send(L)),this.messageQueue=[]})}else{for(let L of this.subscriptions.keys())if(this.send({type:"subscribe",topic:L}),this.debug)console.log(`Resubscribed to topic: ${L}`);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((L)=>this.send(L)),this.messageQueue=[]}},this.ws.onmessage=(L)=>{try{if(this.debug)console.log("WebSocket message received:",L.data);let N=JSON.parse(L.data);if(N.type==="auth_success"){let W=N.authenticated!==!1;if(this.debug)console.log(`WebSocket ${W?"authentication":"deauthentication"} successful`);if(this.pendingAuth=!1,W)this.authActions.forEach((D)=>D());this.authActions=[];return}if(N.type==="auth_error"){console.error("WebSocket authentication failed:",N.error),this.pendingAuth=!1,this.authActions=[];return}if(this.debug)console.log("Got new message",N);if(N.topic&&this.subscriptions.has(N.topic)){let W=this.subscriptions.get(N.topic);if(this.debug)console.log("Calling subscription callbacks");W?.forEach((D)=>D(N.data))}else if(N.error)console.error("WebSocket error:",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:",L.data)}},this.ws.onclose=(L)=>{if(this.isConnected=!1,this.pendingAuth=!1,this.debug)console.log(`WebSocket connection closed. Code: ${L.code}, Reason: ${L.reason}`);this.reconnectTimeout=setTimeout(()=>this.connect(),2000)},this.ws.onerror=(L)=>{if(this.debug)console.log("WebSocket error:",L)}}catch(q){if(this.debug)console.log("Failed to create WebSocket connection:",q);this.pendingAuth=!1,this.reconnectTimeout=setTimeout(()=>this.connect(),2000)}}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){if(this.isConnected&&this.ws?.readyState===this.WebSocketImpl.OPEN){if(this.pendingAuth&&q.type!=="authenticate"){if(this.messageQueue.push(q),this.debug)console.log("Message queued until authentication completes:",q);return}let G=JSON.stringify(q);if(this.ws.send(G),this.debug)console.log("WebSocket message sent:",G)}else if(this.messageQueue.push(q),this.debug)console.log("WebSocket message queued (not connected):",q)}subscribe(q,G){if(!this.subscriptions.has(q)){if(this.subscriptions.set(q,new Set),this.isConnected){if(this.pendingAuth)this.authActions.push(()=>{if(this.send({type:"subscribe",topic:q}),this.debug)console.log(`Subscribed to topic after auth: ${q}`)});else if(this.send({type:"subscribe",topic:q}),this.debug)console.log(`Subscribed to topic: ${q}`)}}return this.subscriptions.get(q).add(G),{unsubscribe:()=>this.unsubscribe(q,G),publish:(L)=>this.publish(q,L)}}unsubscribe(q,G){let L=this.subscriptions.get(q);if(L){if(L.delete(G),L.size===0){if(this.subscriptions.delete(q),this.send({type:"unsubscribe",topic:q}),this.debug)console.log(`Unsubscribed from topic: ${q}`)}}}publish(q,G){if(this.send({type:"publish",topic:q,data:G}),this.debug)console.log(`Published to topic: ${q}`,G)}async updateAuth(){let G=(await this.getHeaders()).Authorization||"",L=G.startsWith("Bearer ")?G.substring(7).trim():G.trim(),N=L.length>0,W=this.authToken!==null&&this.authToken.trim().length>0;if((N!==W||L!==this.authToken)&&this.isConnected){if(this.authToken=L,this.pendingAuth=N,this.send({type:"authenticate",token:G}),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,G={}){let L=G.fetch||fetch,N=G.WebSocket??(typeof WebSocket<"u"?WebSocket:null),W=G.prefix??"",D=G.apiPrefix??"",M=G.debug??!1,m=G.s3Endpoint,g=G.nodeEnv;if(!G.s3Endpoint)throw Error("BRPC Client: Pass s3Endpoint option to createBrpcClient");if(!G.nodeEnv)throw Error("BRPC Client: Pass nodeEnv option to createBrpcClient");if(!N)throw Error("WebSocket is not available in this environment");let V=G.headers??{},c=async()=>{if(typeof V==="function")return await V();return V},B=b(q),x=H(G.prefix),d=H(G.apiPrefix),E=k(x,d),P=y(B,x),S=new U(P,N,V,M,g),O=async(Q=!0)=>{let F={};if(Q)F["Content-Type"]="application/json";let J=await c();return{...F,...J}},u=async(Q)=>{if(!Q.ok){let J;try{J=await Q.json()}catch{try{J={error:await Q.text()}}catch{J={error:"Failed to parse error response"}}}if(J?.error&&typeof J.error==="object"){let A=J.error;throw new w(A.message||Q.statusText,Q.status,A.code,A.clientCode,A.data)}let z=J?.error||J?.message||Q.statusText;throw new w(z,Q.status,void 0,void 0,J)}let F=Q.headers.get("Content-Type")||"";try{if(F.includes("application/json"))return(await Q.json()).data;else if(F.includes("text/"))return Q.text();else return Q.blob()}catch(J){throw new w(`Failed to parse response: ${J instanceof Error?J.message:"Unknown error"}`,Q.status,"PARSE_ERROR")}},j=async(Q,F)=>{try{let J=await L(Q,F);return await u(J)}catch(J){if(J instanceof w)throw J;throw new w(J instanceof Error?J.message:"Network request failed",0,"NETWORK_ERROR")}},h=(Q=[])=>{return new Proxy({},{get(F,J){if(typeof J==="symbol")return;if(J==="query"||J==="mutation"||J==="formMutation"||J==="subscription"||J==="getStringKey"||J==="getArrayKey"||J==="getNoInputsArrayKey"){let z=Q.join("/"),A=`${B}${E}/${z}`;if(M)console.log(`BRPC ${J} procedure path: ${z}`),console.log(`BRPC ${J} full URL: ${A}`);if(J==="query")return async(X)=>{let _=await O(!0);if(M)console.log(`BRPC query request to ${z}`,{input:X,headers:_});let $=new URL(A);if(X&&typeof X==="object")Object.entries(X).forEach(([R,Y])=>{if(Y!==void 0)$.searchParams.append(R,String(Y))});let I=await j($.toString(),{method:"GET",headers:_,credentials:"include"});if(M)console.log(`BRPC query response from ${z}:`,I);return I};else if(J==="mutation")return async(X)=>{let _=await O(!0);if(M)console.log(`BRPC mutation request to ${z}`,{input:X,headers:_});let $=JSON.stringify(X),I=await j(A,{method:"POST",headers:_,body:$,credentials:"include"});if(M)console.log(`BRPC mutation response from ${z}:`,I);return I};else if(J==="formMutation")return async(X)=>{let _=await O(!1);if(M)console.log(`BRPC formMutation request to ${z}`,{input:X,headers:_});let $=new FormData,I=(Y,Z,f)=>{let C=f?`${f}[${Y}]`:Y;if(Z===null||Z===void 0)return;else if(Z instanceof File||Z instanceof Blob)$.append(C,Z);else if(Array.isArray(Z))Z.forEach((T,K)=>{if(T instanceof File||T instanceof Blob)$.append(C,T);else if(typeof T==="object"&&T!==null)Object.entries(T).forEach(([n,i])=>{I(n,i,`${C}[${K}]`)});else $.append(`${C}[${K}]`,String(T))});else if(typeof Z==="object"&&Z!==null)Object.entries(Z).forEach(([T,K])=>{I(T,K,C)});else $.append(C,String(Z))};Object.entries(X).forEach(([Y,Z])=>{I(Y,Z)});let R=await j(A,{method:"POST",headers:_,body:$,credentials:"include"});if(M)console.log(`BRPC formMutation response from ${z}:`,R);return R};else if(J==="subscription")return(X)=>{if(M)console.log(`BRPC subscription to ${z}`);let _=E?`${E.slice(1)}/${z}`:z;return S.updateAuth(),S.subscribe(_,X)};else if(J==="getStringKey")return(X)=>{let _=z;if(!X||Object.keys(X).length===0)return _;let $=Object.keys(X).sort().reduce((R,Y)=>{let Z=X[Y];if(Z!==void 0&&Z!==null)R[Y]=Z;return R},{});if(Object.keys($).length===0)return _;let I=JSON.stringify($);return`${_}?${I}`};else if(J==="getArrayKey")return(X)=>{let _=z;if(!X||Object.keys(X).length===0)return[_];let $=[];if(Object.keys(X).sort().forEach((R)=>{let Y=X[R];if(Y!==void 0&&Y!==null){let Z=Y;if(typeof Y==="boolean"||typeof Y==="number")Z=String(Y);else if(typeof Y==="object")Z=JSON.stringify(Y);$.push([R,Z])}}),$.length===0)return[_];let I=$.flatMap(([R,Y])=>[R,Y]);return[_,...I]};else if(J==="getNoInputsArrayKey")return()=>{return[z]}}return h([...Q,J])}})},l=h();if(M)console.log("BRPC client created",{baseUrl:B,prefix:E,wsUrl:P});return{routes:l,storage:{getObjectUrl:(Q,F)=>v(Q,F,m)},utils:{updateWsAuth:async()=>await S.updateAuth(),setHeader:async(Q,F)=>{if(typeof V==="function"){console.warn("Cannot use setHeader with function-based headers resolver");return}V={...V,[Q]:F},await S.updateAuth()},setHeaders:async(Q)=>{if(typeof V==="function"){console.warn("Cannot use setHeaders with function-based headers resolver");return}V={...V,...Q},await S.updateAuth()}}}}export{v as getObjectUrl,Gq as createBrpcClient,w as BrpcClientError};
|
|
2
|
+
function v(q,G={placeholder:void 0,throwOnInvalid:!0},L){if(!q)return G?.placeholder??"PLACEHOLDER";if(q.url)return q.url;if(q.key)if(q.isPrivate||q.provider==="local")return`/storage/${q.key}`;else return`${L}/${q.key}`;if(G.throwOnInvalid)throw Error("Invalid storage object");else return G?.placeholder??"PLACEHOLDER"}class S extends Error{status;code;clientCode;data;constructor(q,G,L,N,W){super(q);this.name="BrpcClientError",this.status=G,this.code=L,this.clientCode=N,this.data=W}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"}}function U(q){if(!q)return null;return q.replace(/\//g,"")}function b(q){let G=q&&q.length>0?q:typeof window<"u"?window.location.origin:"";return G=G.endsWith("/")?G.slice(0,-1):G,G}function y(q,G){let L=G?`/${G}/ws`:"/ws";return`${q.replace(/^http/,"ws")}${L}`}function k(q,G){if(q&&G)return`/${q}/${G}`;else if(q&&!G)return`/${q}`;else if(!q&&G)return`/${G}`;else return""}class x{baseUrl;ws=null;subscriptions=new Map;messageQueue=[];isConnected=!1;reconnectTimeout=null;WebSocketImpl;getHeaders;authToken=null;debug;pendingAuth=!1;authActions=[];constructor(q,G,L,N=!1,W){this.baseUrl=q;this.WebSocketImpl=G,this.debug=N&&W==="development",this.getHeaders=async()=>{if(typeof L==="function")return await L();return L??{}},this.connect()}async connect(){try{let q=await this.getHeaders();this.authToken=q.Authorization||null;let G=this.baseUrl;if(this.debug)console.log(`Connecting to WebSocket: ${G}`);this.ws=new this.WebSocketImpl(G),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");this.authActions.push(()=>{for(let L of this.subscriptions.keys())if(this.send({type:"subscribe",topic:L}),this.debug)console.log(`Resubscribed to topic: ${L}`);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((L)=>this.send(L)),this.messageQueue=[]})}else{for(let L of this.subscriptions.keys())if(this.send({type:"subscribe",topic:L}),this.debug)console.log(`Resubscribed to topic: ${L}`);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((L)=>this.send(L)),this.messageQueue=[]}},this.ws.onmessage=(L)=>{try{if(this.debug)console.log("WebSocket message received:",L.data);let N=JSON.parse(L.data);if(N.type==="auth_success"){let W=N.authenticated!==!1;if(this.debug)console.log(`WebSocket ${W?"authentication":"deauthentication"} successful`);if(this.pendingAuth=!1,W)this.authActions.forEach((E)=>E());this.authActions=[];return}if(N.type==="auth_error"){console.error("WebSocket authentication failed:",N.error),this.pendingAuth=!1,this.authActions=[];return}if(this.debug)console.log("Got new message",N);if(N.topic&&this.subscriptions.has(N.topic)){let W=this.subscriptions.get(N.topic);if(this.debug)console.log("Calling subscription callbacks");W?.forEach((E)=>E(N.data))}else if(N.error)console.error("WebSocket error:",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:",L.data)}},this.ws.onclose=(L)=>{if(this.isConnected=!1,this.pendingAuth=!1,this.debug)console.log(`WebSocket connection closed. Code: ${L.code}, Reason: ${L.reason}`);this.reconnectTimeout=setTimeout(()=>this.connect(),2000)},this.ws.onerror=(L)=>{if(this.debug)console.log("WebSocket error:",L)}}catch(q){if(this.debug)console.log("Failed to create WebSocket connection:",q);this.pendingAuth=!1,this.reconnectTimeout=setTimeout(()=>this.connect(),2000)}}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){if(this.isConnected&&this.ws?.readyState===this.WebSocketImpl.OPEN){if(this.pendingAuth&&q.type!=="authenticate"){if(this.messageQueue.push(q),this.debug)console.log("Message queued until authentication completes:",q);return}let G=JSON.stringify(q);if(this.ws.send(G),this.debug)console.log("WebSocket message sent:",G)}else if(this.messageQueue.push(q),this.debug)console.log("WebSocket message queued (not connected):",q)}subscribe(q,G){if(!this.subscriptions.has(q)){if(this.subscriptions.set(q,new Set),this.isConnected){if(this.pendingAuth)this.authActions.push(()=>{if(this.send({type:"subscribe",topic:q}),this.debug)console.log(`Subscribed to topic after auth: ${q}`)});else if(this.send({type:"subscribe",topic:q}),this.debug)console.log(`Subscribed to topic: ${q}`)}}return this.subscriptions.get(q).add(G),{unsubscribe:()=>this.unsubscribe(q,G),publish:(L)=>this.publish(q,L)}}unsubscribe(q,G){let L=this.subscriptions.get(q);if(L){if(L.delete(G),L.size===0){if(this.subscriptions.delete(q),this.send({type:"unsubscribe",topic:q}),this.debug)console.log(`Unsubscribed from topic: ${q}`)}}}publish(q,G){if(this.send({type:"publish",topic:q,data:G}),this.debug)console.log(`Published to topic: ${q}`,G)}async updateAuth(){let G=(await this.getHeaders()).Authorization||"",L=G.startsWith("Bearer ")?G.substring(7).trim():G.trim(),N=L.length>0,W=this.authToken!==null&&this.authToken.trim().length>0;if((N!==W||L!==this.authToken)&&this.isConnected){if(this.authToken=L,this.pendingAuth=N,this.send({type:"authenticate",token:G}),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,G={}){let L=G.fetch||fetch,N=G.WebSocket??(typeof WebSocket<"u"?WebSocket:null),W=G.prefix??"",E=G.apiPrefix??"",V=G.debug??!1,m=G.s3Endpoint,g=G.nodeEnv;if(!G.s3Endpoint)throw Error("BRPC Client: Pass s3Endpoint option to createBrpcClient");if(!G.nodeEnv)throw Error("BRPC Client: Pass nodeEnv option to createBrpcClient");if(!N)throw Error("WebSocket is not available in this environment");let A=G.headers??{},c=async()=>{if(typeof A==="function")return await A();return A},O=b(q),P=U(G.prefix),d=U(G.apiPrefix),K=k(P,d),h=y(O,P),D=new x(h,N,A,V,g),j=async(Q=!0)=>{let F={};if(Q)F["Content-Type"]="application/json";let J=await c();return{...F,...J}},u=async(Q)=>{if(!Q.ok){let J;try{J=await Q.json()}catch{try{J={error:await Q.text()}}catch{J={error:"Failed to parse error response"}}}if(J?.error&&typeof J.error==="object"){let w=J.error;throw new S(w.message||Q.statusText,Q.status,w.code,w.clientCode,w.data)}let _=J?.error||J?.message||Q.statusText;throw new S(_,Q.status,void 0,void 0,J)}let F=Q.headers.get("Content-Type")||"";try{if(F.includes("application/json"))return(await Q.json()).data;else if(F.includes("text/"))return Q.text();else return Q.blob()}catch(J){throw new S(`Failed to parse response: ${J instanceof Error?J.message:"Unknown error"}`,Q.status,"PARSE_ERROR")}},H=async(Q,F)=>{try{let J=await L(Q,F);return await u(J)}catch(J){if(J instanceof S)throw J;throw new S(J instanceof Error?J.message:"Network request failed",0,"NETWORK_ERROR")}},f=(Q=[])=>{return new Proxy({},{get(F,J){if(typeof J==="symbol")return;if(J==="query"||J==="mutation"||J==="formMutation"||J==="subscription"||J==="getStringKey"||J==="getArrayKey"||J==="getNoInputsArrayKey"){let _=Q.join("/"),w=`${O}${K}/${_}`;if(V)console.log(`BRPC ${J} procedure path: ${_}`),console.log(`BRPC ${J} full URL: ${w}`);if(J==="query")return async(Y)=>{let $=await j(!0);if(V)console.log(`BRPC query request to ${_}`,{input:Y,headers:$});let Z=new URL(w);if(Y&&typeof Y==="object")Object.entries(Y).forEach(([M,R])=>{if(R!==void 0)Z.searchParams.append(M,String(R))});let z=await H(Z.toString(),{method:"GET",headers:$,credentials:"include"});if(V)console.log(`BRPC query response from ${_}:`,z);return z};else if(J==="mutation")return async(Y)=>{let $=await j(!0);if(V)console.log(`BRPC mutation request to ${_}`,{input:Y,headers:$});let Z=JSON.stringify(Y),z=await H(w,{method:"POST",headers:$,body:Z,credentials:"include"});if(V)console.log(`BRPC mutation response from ${_}:`,z);return z};else if(J==="formMutation")return async(Y)=>{let $=await j(!1);if(V)console.log(`BRPC formMutation request to ${_}`,{input:Y,headers:$});let Z=new FormData,z=(R,X,I)=>{let T=I?`${I}[${R}]`:R;if(X===null||X===void 0)return;else if(X instanceof File||X instanceof Blob)Z.append(T,X);else if(Array.isArray(X))X.forEach((C,B)=>{if(C instanceof File||C instanceof Blob)Z.append(T,C);else if(typeof C==="object"&&C!==null)Object.entries(C).forEach(([n,i])=>{z(n,i,`${T}[${B}]`)});else Z.append(`${T}[${B}]`,String(C))});else if(typeof X==="object"&&X!==null)Object.entries(X).forEach(([C,B])=>{z(C,B,T)});else Z.append(T,String(X))};Object.entries(Y).forEach(([R,X])=>{z(R,X)});let M=await H(w,{method:"POST",headers:$,body:Z,credentials:"include"});if(V)console.log(`BRPC formMutation response from ${_}:`,M);return M};else if(J==="subscription")return(Y)=>{if(V)console.log(`BRPC subscription to ${_}`);let $=K?`${K.slice(1)}/${_}`:_;return D.updateAuth(),D.subscribe($,Y)};else if(J==="getStringKey")return(Y)=>{let $=_;if(!Y||Object.keys(Y).length===0)return $;let Z=Object.keys(Y).sort().reduce((M,R)=>{let X=Y[R];if(X!==void 0&&X!==null)M[R]=X;return M},{});if(Object.keys(Z).length===0)return $;let z=JSON.stringify(Z);return`${$}?${z}`};else if(J==="getArrayKey")return(Y,$)=>{let Z=_,z={...Y??{},...$??{}};if(Object.keys(z).length===0)return[Z];let M=[];if(Object.keys(z).sort().forEach((X)=>{let I=z[X];if(I!==void 0&&I!==null){let T=I;if(typeof I==="boolean"||typeof I==="number")T=String(I);else if(typeof I==="object")T=JSON.stringify(I);M.push([X,T])}}),M.length===0)return[Z];let R=M.flatMap(([X,I])=>[X,I]);return[Z,...R]};else if(J==="getNoInputsArrayKey")return()=>{return[_]}}return f([...Q,J])}})},l=f();if(V)console.log("BRPC client created",{baseUrl:O,prefix:K,wsUrl:h});return{routes:l,storage:{getObjectUrl:(Q,F)=>v(Q,F,m)},utils:{updateWsAuth:async()=>await D.updateAuth(),setHeader:async(Q,F)=>{if(typeof A==="function"){console.warn("Cannot use setHeader with function-based headers resolver");return}A={...A,[Q]:F},await D.updateAuth()},setHeaders:async(Q)=>{if(typeof A==="function"){console.warn("Cannot use setHeaders with function-based headers resolver");return}A={...A,...Q},await D.updateAuth()}}}}export{v as getObjectUrl,Gq as createBrpcClient,S as BrpcClientError};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=5DCF873B33A21E1164756E2164756E21
|
package/dist/client/index.js.map
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
"class BrpcClientError extends Error {\r\n readonly status: number;\r\n readonly code?: string;\r\n readonly clientCode?: string;\r\n readonly data?: any;\r\n\r\n constructor(\r\n message: string,\r\n status: number,\r\n code?: string,\r\n clientCode?: string,\r\n data?: any\r\n ) {\r\n super(message);\r\n this.name = \"BrpcClientError\";\r\n this.status = status;\r\n this.code = code;\r\n this.clientCode = clientCode;\r\n this.data = data;\r\n }\r\n\r\n // Helper method to check if this is a specific client error\r\n isClientError(clientCode: string): boolean {\r\n return this.clientCode === clientCode;\r\n }\r\n\r\n // Helper methods for common error types\r\n isUnauthorized(): boolean {\r\n return this.status === 401;\r\n }\r\n\r\n isForbidden(): boolean {\r\n return this.status === 403;\r\n }\r\n\r\n isNotFound(): boolean {\r\n return this.status === 404;\r\n }\r\n\r\n isValidationError(): boolean {\r\n return this.status === 400 || this.code === \"BAD_REQUEST\";\r\n }\r\n}\r\n\r\nexport { BrpcClientError };\r\n",
|
|
7
7
|
"/**\r\n * Removes any slashes (/) from prefixes\r\n */\r\nexport function parsePrefix(prefix?: string) {\r\n if (!prefix) return null;\r\n return prefix.replace(/\\//g, \"\");\r\n}\r\n\r\n/**\r\n * Returns base url without trailing slash\r\n */\r\nexport function getFormattedBaseUrl(baseUrl?: string) {\r\n let formattedBaseUrl =\r\n baseUrl && baseUrl.length > 0\r\n ? baseUrl\r\n : typeof window !== \"undefined\"\r\n ? window.location.origin\r\n : \"\";\r\n\r\n // Remove trailing slash if present\r\n formattedBaseUrl = formattedBaseUrl.endsWith(\"/\")\r\n ? formattedBaseUrl.slice(0, -1)\r\n : formattedBaseUrl;\r\n\r\n return formattedBaseUrl;\r\n}\r\n\r\nexport function getWsUrl(\r\n formattedBaseUrl: string,\r\n parsedPrefix: string | null\r\n) {\r\n const wsPath = parsedPrefix ? `/${parsedPrefix}/ws` : `/ws`;\r\n return `${formattedBaseUrl.replace(/^http/, \"ws\")}${wsPath}`;\r\n}\r\n\r\nexport function getProceduresPrefixPath(\r\n parsedPrefix: string | null,\r\n parsedApiPrefix: string | null\r\n) {\r\n if (parsedPrefix && parsedApiPrefix) {\r\n return `/${parsedPrefix}/${parsedApiPrefix}`;\r\n } else if (parsedPrefix && !parsedApiPrefix) {\r\n return `/${parsedPrefix}`;\r\n } else if (!parsedPrefix && parsedApiPrefix) {\r\n return `/${parsedApiPrefix}`;\r\n } else {\r\n return \"\";\r\n }\r\n}\r\n",
|
|
8
8
|
"import type { HeadersResolver } from \"./types\";\r\n\r\n// WebSocket Manager with improved authentication handling\r\nclass WebSocketManager {\r\n private ws: WebSocket | null = null;\r\n private subscriptions: Map<string, Set<(data: any) => void>> = new Map();\r\n private messageQueue: any[] = [];\r\n private isConnected: boolean = false;\r\n private reconnectTimeout: any = null;\r\n private WebSocketImpl: typeof WebSocket;\r\n private getHeaders: () => Promise<Record<string, string>>;\r\n private authToken: string | null = null;\r\n private debug: boolean;\r\n private pendingAuth: boolean = false; // Track if we're waiting for auth response\r\n private authActions: (() => void)[] = []; // Actions to perform after successful auth\r\n\r\n constructor(\r\n private baseUrl: string,\r\n WebSocketImpl: typeof WebSocket,\r\n headersResolver: HeadersResolver,\r\n debug: boolean = false,\r\n nodeEnv: string,\r\n ) {\r\n this.WebSocketImpl = WebSocketImpl;\r\n this.debug = debug && nodeEnv === \"development\";\r\n\r\n // Create headers resolver function\r\n this.getHeaders = async () => {\r\n if (typeof headersResolver === \"function\") {\r\n return await headersResolver();\r\n }\r\n return headersResolver ?? {};\r\n };\r\n\r\n this.connect();\r\n }\r\n\r\n private async connect() {\r\n try {\r\n // Get latest headers for authentication\r\n const headers = await this.getHeaders();\r\n this.authToken = headers[\"Authorization\"] || null;\r\n\r\n const wsUrl = this.baseUrl;\r\n\r\n if (this.debug) {\r\n console.log(`Connecting to WebSocket: ${wsUrl}`);\r\n }\r\n\r\n this.ws = new this.WebSocketImpl(wsUrl);\r\n\r\n this.ws.onopen = async () => {\r\n if (this.debug) {\r\n console.log(\"WebSocket connection established\");\r\n }\r\n\r\n this.isConnected = true;\r\n\r\n // Clear any pending auth actions from previous connection attempts\r\n this.authActions = [];\r\n\r\n // If we have auth token, send it first and mark auth as pending\r\n if (this.authToken) {\r\n this.pendingAuth = true;\r\n this.send({ type: \"authenticate\", token: this.authToken });\r\n if (this.debug) {\r\n console.log(\"Sent authentication token, waiting for response\");\r\n }\r\n\r\n // Queue resubscriptions to happen after authentication\r\n this.authActions.push(() => {\r\n // Resubscribe to all topics\r\n for (const topic of this.subscriptions.keys()) {\r\n this.send({ type: \"subscribe\", topic });\r\n if (this.debug) {\r\n console.log(`Resubscribed to topic: ${topic}`);\r\n }\r\n }\r\n\r\n // Send any queued messages\r\n if (this.messageQueue.length > 0 && this.debug) {\r\n console.log(\r\n `Sending ${this.messageQueue.length} queued messages`,\r\n );\r\n }\r\n\r\n this.messageQueue.forEach((msg) => this.send(msg));\r\n this.messageQueue = [];\r\n });\r\n } else {\r\n // No auth needed, proceed with subscriptions immediately\r\n for (const topic of this.subscriptions.keys()) {\r\n this.send({ type: \"subscribe\", topic });\r\n if (this.debug) {\r\n console.log(`Resubscribed to topic: ${topic}`);\r\n }\r\n }\r\n\r\n // Send any queued messages\r\n if (this.messageQueue.length > 0 && this.debug) {\r\n console.log(`Sending ${this.messageQueue.length} queued messages`);\r\n }\r\n\r\n this.messageQueue.forEach((msg) => this.send(msg));\r\n this.messageQueue = [];\r\n }\r\n };\r\n\r\n this.ws.onmessage = (event) => {\r\n try {\r\n if (this.debug) {\r\n // console.log(\"Active subscriptions\", this.subscriptions);\r\n console.log(\"WebSocket message received:\", event.data);\r\n }\r\n\r\n const message = JSON.parse(event.data);\r\n\r\n // Handle auth-specific messages\r\n if (message.type === \"auth_success\") {\r\n // Check if authentication was successful or if it was a logout confirmation\r\n const isAuthenticated = message.authenticated !== false;\r\n\r\n if (this.debug) {\r\n console.log(\r\n `WebSocket ${\r\n isAuthenticated ? \"authentication\" : \"deauthentication\"\r\n } successful`,\r\n );\r\n }\r\n\r\n // Clear pending auth state\r\n this.pendingAuth = false;\r\n\r\n // Only execute queued actions for successful authentication\r\n if (isAuthenticated) {\r\n // Execute all queued actions that were waiting for authentication\r\n this.authActions.forEach((action) => action());\r\n }\r\n\r\n // Clear action queue\r\n this.authActions = [];\r\n\r\n return;\r\n }\r\n\r\n if (message.type === \"auth_error\") {\r\n console.error(\"WebSocket authentication failed:\", message.error);\r\n\r\n // Clear pending auth state but don't process queued actions\r\n this.pendingAuth = false;\r\n this.authActions = [];\r\n\r\n return;\r\n }\r\n\r\n if (this.debug) {\r\n console.log(\"Got new message\", message);\r\n }\r\n\r\n if (message.topic && this.subscriptions.has(message.topic)) {\r\n const callbacks = this.subscriptions.get(message.topic);\r\n if (this.debug) {\r\n console.log(\"Calling subscription callbacks\");\r\n }\r\n callbacks?.forEach((callback) => callback(message.data));\r\n } else if (message.error) {\r\n console.error(\"WebSocket error:\", message.error);\r\n } else {\r\n // Log unknown message formats to help debug\r\n console.warn(\"Unhandled WebSocket message format:\", message);\r\n }\r\n } catch (error) {\r\n if (this.debug) {\r\n console.error(\"Error processing WebSocket message:\", error);\r\n console.error(\"Raw message:\", event.data);\r\n }\r\n }\r\n };\r\n\r\n this.ws.onclose = (event) => {\r\n this.isConnected = false;\r\n this.pendingAuth = false; // Reset auth state\r\n\r\n if (this.debug) {\r\n console.log(\r\n `WebSocket connection closed. Code: ${event.code}, Reason: ${event.reason}`,\r\n );\r\n }\r\n\r\n // Try to reconnect after a delay\r\n this.reconnectTimeout = setTimeout(() => this.connect(), 2000);\r\n };\r\n\r\n this.ws.onerror = (error) => {\r\n if (this.debug) {\r\n console.log(\"WebSocket error:\", error);\r\n }\r\n };\r\n } catch (error) {\r\n if (this.debug) {\r\n console.log(\"Failed to create WebSocket connection:\", error);\r\n }\r\n this.pendingAuth = false;\r\n // Try to reconnect after a delay\r\n this.reconnectTimeout = setTimeout(() => this.connect(), 2000);\r\n }\r\n }\r\n\r\n public disconnect() {\r\n if (this.reconnectTimeout) {\r\n clearTimeout(this.reconnectTimeout);\r\n this.reconnectTimeout = null;\r\n }\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n this.isConnected = false;\r\n this.pendingAuth = false;\r\n this.authActions = [];\r\n this.subscriptions.clear();\r\n }\r\n\r\n public send(message: any) {\r\n if (this.isConnected && this.ws?.readyState === this.WebSocketImpl.OPEN) {\r\n // If authentication is pending and this isn't an auth message, queue it\r\n if (this.pendingAuth && message.type !== \"authenticate\") {\r\n this.messageQueue.push(message);\r\n if (this.debug) {\r\n console.log(\r\n \"Message queued until authentication completes:\",\r\n message,\r\n );\r\n }\r\n return;\r\n }\r\n\r\n const messageStr = JSON.stringify(message);\r\n this.ws.send(messageStr);\r\n\r\n if (this.debug) {\r\n console.log(\"WebSocket message sent:\", messageStr);\r\n }\r\n } else {\r\n this.messageQueue.push(message);\r\n\r\n if (this.debug) {\r\n console.log(\"WebSocket message queued (not connected):\", message);\r\n }\r\n }\r\n }\r\n\r\n public subscribe(topic: string, callback: (data: any) => void) {\r\n if (!this.subscriptions.has(topic)) {\r\n this.subscriptions.set(topic, new Set());\r\n\r\n // If we're connected, send the subscription (with auth awareness)\r\n if (this.isConnected) {\r\n if (this.pendingAuth) {\r\n // Queue the subscription for after auth\r\n this.authActions.push(() => {\r\n this.send({ type: \"subscribe\", topic });\r\n if (this.debug) {\r\n console.log(`Subscribed to topic after auth: ${topic}`);\r\n }\r\n });\r\n } else {\r\n // Send immediately\r\n this.send({ type: \"subscribe\", topic });\r\n if (this.debug) {\r\n console.log(`Subscribed to topic: ${topic}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n this.subscriptions.get(topic)!.add(callback);\r\n\r\n return {\r\n unsubscribe: () => this.unsubscribe(topic, callback),\r\n publish: (data: any) => this.publish(topic, data),\r\n };\r\n }\r\n\r\n private unsubscribe(topic: string, callback: (data: any) => void) {\r\n const callbacks = this.subscriptions.get(topic);\r\n if (callbacks) {\r\n callbacks.delete(callback);\r\n if (callbacks.size === 0) {\r\n this.subscriptions.delete(topic);\r\n this.send({ type: \"unsubscribe\", topic });\r\n\r\n if (this.debug) {\r\n console.log(`Unsubscribed from topic: ${topic}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n public publish(topic: string, data: any) {\r\n this.send({ type: \"publish\", topic, data });\r\n\r\n if (this.debug) {\r\n console.log(`Published to topic: ${topic}`, data);\r\n }\r\n }\r\n\r\n // Method to update auth token when it changes\r\n public async updateAuth() {\r\n const headers = await this.getHeaders();\r\n const authHeader = headers[\"Authorization\"] || \"\";\r\n\r\n // Extract token from header - may be empty (\"Bearer \")\r\n const newToken = authHeader.startsWith(\"Bearer \")\r\n ? authHeader.substring(7).trim()\r\n : authHeader.trim();\r\n\r\n const hasValidToken = newToken.length > 0;\r\n const hadValidToken =\r\n this.authToken !== null && this.authToken.trim().length > 0;\r\n\r\n // If token status changed and we're connected, handle the change\r\n if (\r\n (hasValidToken !== hadValidToken || newToken !== this.authToken) &&\r\n this.isConnected\r\n ) {\r\n // Update stored token\r\n this.authToken = newToken;\r\n\r\n // Always send authenticate message, but with empty token when logging out\r\n this.pendingAuth = hasValidToken;\r\n this.send({ type: \"authenticate\", token: authHeader });\r\n\r\n if (this.debug) {\r\n if (hasValidToken) {\r\n console.log(\"Updated authentication token, waiting for confirmation\");\r\n } else {\r\n console.log(\"Sent empty token for deauthentication\");\r\n // Since we're not expecting auth confirmation for logout, clear pending state\r\n this.pendingAuth = false;\r\n this.authActions = [];\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport { WebSocketManager };\r\n",
|
|
9
|
-
"import { BrpcClientError } from \"./brpc-client-error\";\r\nimport { getObjectUrl } from \"./storage\";\r\nimport type {\r\n HeadersResolver,\r\n BrpcClient,\r\n InferClientRoutes,\r\n BrpcClientOptions,\r\n} from \"./types\";\r\nimport {\r\n getFormattedBaseUrl,\r\n getProceduresPrefixPath,\r\n getWsUrl,\r\n parsePrefix,\r\n} from \"./utils\";\r\nimport { WebSocketManager } from \"./ws-manager\";\r\n\r\n/**\r\n * Creates a type-safe client for the BRPC API\r\n * @param baseUrl The base URL of the BRPC server\r\n * @param options Client configuration options\r\n * @returns A client object that mirrors the server routes\r\n */\r\nexport function createBrpcClient<TRoutesStructure>(\r\n baseUrl: string,\r\n options: BrpcClientOptions = {},\r\n): BrpcClient<TRoutesStructure> {\r\n const fetchImpl = options.fetch || fetch;\r\n const WebSocketImpl =\r\n options.WebSocket ?? (typeof WebSocket !== \"undefined\" ? WebSocket : null);\r\n const prefix = options.prefix ?? \"\";\r\n const apiPrefix = options.apiPrefix ?? \"\";\r\n const debug = options.debug ?? false;\r\n const s3Endpoint = options.s3Endpoint;\r\n const nodeEnv = options.nodeEnv;\r\n\r\n if (!options.s3Endpoint) {\r\n throw new Error(\"BRPC Client: Pass s3Endpoint option to createBrpcClient\");\r\n }\r\n\r\n if (!options.nodeEnv) {\r\n throw new Error(\"BRPC Client: Pass nodeEnv option to createBrpcClient\");\r\n }\r\n\r\n if (!WebSocketImpl) {\r\n throw new Error(\"WebSocket is not available in this environment\");\r\n }\r\n\r\n let currentHeaders: HeadersResolver = options.headers ?? {};\r\n\r\n const headersResolver: HeadersResolver = async () => {\r\n if (typeof currentHeaders === \"function\") {\r\n return await currentHeaders();\r\n }\r\n return currentHeaders;\r\n };\r\n\r\n // Properly format the base URL\r\n const formattedBaseUrl = getFormattedBaseUrl(baseUrl);\r\n\r\n // Format prefixes (returns clean word, like api, without\r\n // initial and trailing slahes)\r\n const parsedPrefix = parsePrefix(options.prefix);\r\n const parsedApiPrefix = parsePrefix(options.apiPrefix);\r\n\r\n const proceduresPrefixedPath = getProceduresPrefixPath(\r\n parsedPrefix,\r\n parsedApiPrefix,\r\n );\r\n\r\n // Create WebSocket URL with proper path joining\r\n const wsUrl = getWsUrl(formattedBaseUrl, parsedPrefix);\r\n\r\n // Create WebSocket manager with headers resolver\r\n const wsManager = new WebSocketManager(\r\n wsUrl,\r\n WebSocketImpl,\r\n currentHeaders,\r\n debug,\r\n nodeEnv!,\r\n );\r\n\r\n // Helper to resolve headers before each request\r\n const resolveHeaders = async (\r\n defaultContentType = true,\r\n ): Promise<Record<string, string>> => {\r\n const baseHeaders: Record<string, string> = {};\r\n\r\n if (defaultContentType) {\r\n baseHeaders[\"Content-Type\"] = \"application/json\";\r\n }\r\n\r\n // Use the same headersResolver as WebSocket\r\n const dynamicHeaders = await headersResolver();\r\n return { ...baseHeaders, ...dynamicHeaders };\r\n };\r\n\r\n // Helper to handle HTTP responses\r\n const handleResponse = async (response: Response) => {\r\n if (!response.ok) {\r\n let errorData;\r\n try {\r\n errorData = await response.json();\r\n } catch {\r\n try {\r\n errorData = { error: await response.text() };\r\n } catch {\r\n errorData = { error: \"Failed to parse error response\" };\r\n }\r\n }\r\n\r\n // Handle the new BRPCError format from server\r\n if (errorData?.error && typeof errorData.error === \"object\") {\r\n const serverError = errorData.error;\r\n\r\n throw new BrpcClientError(\r\n serverError.message || response.statusText,\r\n response.status,\r\n serverError.code,\r\n serverError.clientCode,\r\n serverError.data,\r\n );\r\n }\r\n\r\n // Handle legacy error format or simple string errors\r\n const errorMessage =\r\n errorData?.error || errorData?.message || response.statusText;\r\n\r\n throw new BrpcClientError(\r\n errorMessage,\r\n response.status,\r\n undefined,\r\n undefined,\r\n errorData,\r\n );\r\n }\r\n\r\n // Check content type to determine how to handle the response\r\n const contentType = response.headers.get(\"Content-Type\") || \"\";\r\n\r\n try {\r\n if (contentType.includes(\"application/json\")) {\r\n const result = await response.json();\r\n return result.data;\r\n } else if (contentType.includes(\"text/\")) {\r\n return response.text();\r\n } else {\r\n return response.blob();\r\n }\r\n } catch (error) {\r\n // Wrap JSON parsing errors\r\n throw new BrpcClientError(\r\n `Failed to parse response: ${\r\n error instanceof Error ? error.message : \"Unknown error\"\r\n }`,\r\n response.status,\r\n \"PARSE_ERROR\",\r\n );\r\n }\r\n };\r\n\r\n // Wrap fetch AND response handling to always return BrpcClientError\r\n const safeFetch = async (url: string | URL, init?: RequestInit) => {\r\n try {\r\n const response = await fetchImpl(url, init);\r\n return await handleResponse(response); // This throws BrpcClientError if not ok\r\n } catch (error) {\r\n // If already a BrpcClientError (from handleResponse), re-throw as-is\r\n if (error instanceof BrpcClientError) {\r\n throw error;\r\n }\r\n\r\n // Network errors - wrap them\r\n throw new BrpcClientError(\r\n error instanceof Error ? error.message : \"Network request failed\",\r\n 0,\r\n \"NETWORK_ERROR\",\r\n );\r\n }\r\n };\r\n\r\n // Create a proxy for a specific path\r\n const createProxy = (pathSegments: string[] = []): any => {\r\n return new Proxy(\r\n {},\r\n {\r\n get(_, key: string | symbol) {\r\n if (typeof key === \"symbol\") return undefined;\r\n\r\n // Special methods for procedures\r\n if (\r\n key === \"query\" ||\r\n key === \"mutation\" ||\r\n key === \"formMutation\" ||\r\n key === \"subscription\" ||\r\n key === \"getStringKey\" ||\r\n key === \"getArrayKey\" ||\r\n key === \"getNoInputsArrayKey\"\r\n ) {\r\n // The path up to this point is the procedure path\r\n const procedurePath = pathSegments.join(\"/\");\r\n const fullUrl = `${formattedBaseUrl}${proceduresPrefixedPath}/${procedurePath}`;\r\n\r\n if (debug) {\r\n console.log(`BRPC ${key} procedure path: ${procedurePath}`);\r\n console.log(`BRPC ${key} full URL: ${fullUrl}`);\r\n }\r\n\r\n if (key === \"query\") {\r\n return async (input: any) => {\r\n // Get fresh headers for this request\r\n const headers = await resolveHeaders(true);\r\n\r\n if (debug) {\r\n console.log(`BRPC query request to ${procedurePath}`, {\r\n input,\r\n headers,\r\n });\r\n }\r\n\r\n const url = new URL(fullUrl);\r\n\r\n // Add input parameters to URL for GET request\r\n if (input && typeof input === \"object\") {\r\n Object.entries(input).forEach(([k, v]) => {\r\n if (v !== undefined) {\r\n url.searchParams.append(k, String(v));\r\n }\r\n });\r\n }\r\n\r\n const result = await safeFetch(url.toString(), {\r\n method: \"GET\",\r\n headers,\r\n credentials: \"include\", //always include credentials\r\n });\r\n\r\n if (debug) {\r\n console.log(\r\n `BRPC query response from ${procedurePath}:`,\r\n result,\r\n );\r\n }\r\n\r\n return result;\r\n };\r\n } else if (key === \"mutation\") {\r\n return async (input: any) => {\r\n // Get fresh headers for this request\r\n const headers = await resolveHeaders(true);\r\n\r\n if (debug) {\r\n console.log(`BRPC mutation request to ${procedurePath}`, {\r\n input,\r\n headers,\r\n });\r\n }\r\n\r\n const body = JSON.stringify(input);\r\n\r\n const result = await safeFetch(fullUrl, {\r\n method: \"POST\",\r\n headers,\r\n body,\r\n credentials: \"include\",\r\n });\r\n\r\n if (debug) {\r\n console.log(\r\n `BRPC mutation response from ${procedurePath}:`,\r\n result,\r\n );\r\n }\r\n\r\n return result;\r\n };\r\n } else if (key === \"formMutation\") {\r\n return async (input: Record<string, any>) => {\r\n // Get fresh headers for this request (no Content-Type for FormData)\r\n const headers = await resolveHeaders(false);\r\n\r\n if (debug) {\r\n console.log(`BRPC formMutation request to ${procedurePath}`, {\r\n input,\r\n headers,\r\n });\r\n }\r\n\r\n // Convert object input to FormData\r\n const formData = new FormData();\r\n\r\n // Helper function to append values to FormData\r\n const appendToFormData = (\r\n key: string,\r\n value: any,\r\n parentKey?: string,\r\n ) => {\r\n const fullKey = parentKey ? `${parentKey}[${key}]` : key;\r\n\r\n if (value === null || value === undefined) {\r\n // Skip null/undefined values\r\n return;\r\n } else if (value instanceof File || value instanceof Blob) {\r\n // Handle files directly\r\n formData.append(fullKey, value);\r\n } else if (Array.isArray(value)) {\r\n // Handle arrays\r\n value.forEach((item, index) => {\r\n if (item instanceof File || item instanceof Blob) {\r\n // ✅ Append files with the same key (not indexed)\r\n formData.append(fullKey, item);\r\n } else if (typeof item === \"object\" && item !== null) {\r\n Object.entries(item).forEach(([subKey, subValue]) => {\r\n appendToFormData(\r\n subKey,\r\n subValue,\r\n `${fullKey}[${index}]`,\r\n );\r\n });\r\n } else {\r\n formData.append(`${fullKey}[${index}]`, String(item));\r\n }\r\n });\r\n } else if (typeof value === \"object\" && value !== null) {\r\n // Handle nested objects\r\n Object.entries(value).forEach(([subKey, subValue]) => {\r\n appendToFormData(subKey, subValue, fullKey);\r\n });\r\n } else {\r\n // Handle primitive values\r\n formData.append(fullKey, String(value));\r\n }\r\n };\r\n\r\n // Convert input object to FormData\r\n Object.entries(input).forEach(([key, value]) => {\r\n appendToFormData(key, value);\r\n });\r\n\r\n const result = await safeFetch(fullUrl, {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n credentials: \"include\",\r\n });\r\n\r\n if (debug) {\r\n console.log(\r\n `BRPC formMutation response from ${procedurePath}:`,\r\n result,\r\n );\r\n }\r\n\r\n return result;\r\n };\r\n } else if (key === \"subscription\") {\r\n return (callback: (data: any) => void) => {\r\n if (debug) {\r\n console.log(`BRPC subscription to ${procedurePath}`);\r\n }\r\n\r\n // Use the same pattern as fullUrl for consistency\r\n const topicPath = proceduresPrefixedPath\r\n ? `${proceduresPrefixedPath.slice(1)}/${procedurePath}` // Remove leading /\r\n : procedurePath;\r\n\r\n // Update WebSocket auth if needed before subscribing\r\n wsManager.updateAuth();\r\n\r\n // Return an object with unsubscribe and publish methods\r\n return wsManager.subscribe(topicPath, callback);\r\n };\r\n } else if (key === \"getStringKey\") {\r\n return (input?: any) => {\r\n // Create a deterministic key based on procedure path and input\r\n const baseKey = procedurePath;\r\n\r\n if (!input || Object.keys(input).length === 0) {\r\n return baseKey;\r\n }\r\n\r\n // Sort input keys for deterministic key generation\r\n const sortedInput = Object.keys(input)\r\n .sort()\r\n .reduce(\r\n (result, key) => {\r\n const value = input[key];\r\n if (value !== undefined && value !== null) {\r\n result[key] = value;\r\n }\r\n return result;\r\n },\r\n {} as Record<string, any>,\r\n );\r\n\r\n // Only add input hash if there are actual values\r\n if (Object.keys(sortedInput).length === 0) {\r\n return baseKey;\r\n }\r\n\r\n // Create a simple hash from the sorted input\r\n const inputString = JSON.stringify(sortedInput);\r\n return `${baseKey}?${inputString}`;\r\n };\r\n } else if (key === \"getArrayKey\") {\r\n return (input?: any) => {\r\n // Use the full procedure path as the first segment for easier invalidation\r\n const basePath = procedurePath;\r\n\r\n if (!input || Object.keys(input).length === 0) {\r\n return [basePath];\r\n }\r\n\r\n // Normalize and sort input keys for deterministic key generation\r\n const normalizedPairs: Array<[string, any]> = [];\r\n\r\n Object.keys(input)\r\n .sort()\r\n .forEach((key) => {\r\n const value = input[key];\r\n if (value !== undefined && value !== null) {\r\n // Normalize values for consistent comparison\r\n let normalizedValue = value;\r\n\r\n // Convert primitives to strings for consistency\r\n if (\r\n typeof value === \"boolean\" ||\r\n typeof value === \"number\"\r\n ) {\r\n normalizedValue = String(value);\r\n } else if (typeof value === \"object\") {\r\n // For objects/arrays, use JSON representation\r\n normalizedValue = JSON.stringify(value);\r\n }\r\n\r\n normalizedPairs.push([key, normalizedValue]);\r\n }\r\n });\r\n\r\n // Only add input segments if there are actual values\r\n if (normalizedPairs.length === 0) {\r\n return [basePath];\r\n }\r\n\r\n // Create individual segments for each input pair\r\n // This allows for better pattern matching\r\n const inputSegments = normalizedPairs.flatMap(\r\n ([key, value]) => [key, value],\r\n );\r\n\r\n return [basePath, ...inputSegments];\r\n };\r\n } else if (key === \"getNoInputsArrayKey\") {\r\n return () => {\r\n // Always return just the procedure path, ignoring any inputs\r\n // Perfect for invalidating all variants of a procedure\r\n return [procedurePath];\r\n };\r\n }\r\n }\r\n\r\n // Continue traversing the path for nested routes\r\n return createProxy([...pathSegments, key]);\r\n },\r\n },\r\n );\r\n };\r\n\r\n // Create the base proxy\r\n const clientProxy = createProxy() as InferClientRoutes<TRoutesStructure>;\r\n\r\n if (debug) {\r\n console.log(\"BRPC client created\", {\r\n baseUrl: formattedBaseUrl,\r\n prefix: proceduresPrefixedPath,\r\n wsUrl: wsUrl,\r\n });\r\n }\r\n\r\n return {\r\n routes: clientProxy,\r\n\r\n storage: {\r\n getObjectUrl: (storageObject, opts) =>\r\n getObjectUrl(storageObject, opts, s3Endpoint!),\r\n },\r\n\r\n utils: {\r\n updateWsAuth: async () => await wsManager.updateAuth(),\r\n\r\n setHeader: async (key: string, value: string) => {\r\n if (typeof currentHeaders === \"function\") {\r\n console.warn(\r\n \"Cannot use setHeader with function-based headers resolver\",\r\n );\r\n return;\r\n }\r\n currentHeaders = { ...currentHeaders, [key]: value };\r\n await wsManager.updateAuth();\r\n },\r\n\r\n setHeaders: async (headers: Record<string, string>) => {\r\n if (typeof currentHeaders === \"function\") {\r\n console.warn(\r\n \"Cannot use setHeaders with function-based headers resolver\",\r\n );\r\n return;\r\n }\r\n currentHeaders = { ...currentHeaders, ...headers };\r\n await wsManager.updateAuth();\r\n },\r\n },\r\n };\r\n}\r\n"
|
|
9
|
+
"import { BrpcClientError } from \"./brpc-client-error\";\r\nimport { getObjectUrl } from \"./storage\";\r\nimport type {\r\n HeadersResolver,\r\n BrpcClient,\r\n InferClientRoutes,\r\n BrpcClientOptions,\r\n} from \"./types\";\r\nimport {\r\n getFormattedBaseUrl,\r\n getProceduresPrefixPath,\r\n getWsUrl,\r\n parsePrefix,\r\n} from \"./utils\";\r\nimport { WebSocketManager } from \"./ws-manager\";\r\n\r\n/**\r\n * Creates a type-safe client for the BRPC API\r\n * @param baseUrl The base URL of the BRPC server\r\n * @param options Client configuration options\r\n * @returns A client object that mirrors the server routes\r\n */\r\nexport function createBrpcClient<TRoutesStructure>(\r\n baseUrl: string,\r\n options: BrpcClientOptions = {},\r\n): BrpcClient<TRoutesStructure> {\r\n const fetchImpl = options.fetch || fetch;\r\n const WebSocketImpl =\r\n options.WebSocket ?? (typeof WebSocket !== \"undefined\" ? WebSocket : null);\r\n const prefix = options.prefix ?? \"\";\r\n const apiPrefix = options.apiPrefix ?? \"\";\r\n const debug = options.debug ?? false;\r\n const s3Endpoint = options.s3Endpoint;\r\n const nodeEnv = options.nodeEnv;\r\n\r\n if (!options.s3Endpoint) {\r\n throw new Error(\"BRPC Client: Pass s3Endpoint option to createBrpcClient\");\r\n }\r\n\r\n if (!options.nodeEnv) {\r\n throw new Error(\"BRPC Client: Pass nodeEnv option to createBrpcClient\");\r\n }\r\n\r\n if (!WebSocketImpl) {\r\n throw new Error(\"WebSocket is not available in this environment\");\r\n }\r\n\r\n let currentHeaders: HeadersResolver = options.headers ?? {};\r\n\r\n const headersResolver: HeadersResolver = async () => {\r\n if (typeof currentHeaders === \"function\") {\r\n return await currentHeaders();\r\n }\r\n return currentHeaders;\r\n };\r\n\r\n // Properly format the base URL\r\n const formattedBaseUrl = getFormattedBaseUrl(baseUrl);\r\n\r\n // Format prefixes (returns clean word, like api, without\r\n // initial and trailing slahes)\r\n const parsedPrefix = parsePrefix(options.prefix);\r\n const parsedApiPrefix = parsePrefix(options.apiPrefix);\r\n\r\n const proceduresPrefixedPath = getProceduresPrefixPath(\r\n parsedPrefix,\r\n parsedApiPrefix,\r\n );\r\n\r\n // Create WebSocket URL with proper path joining\r\n const wsUrl = getWsUrl(formattedBaseUrl, parsedPrefix);\r\n\r\n // Create WebSocket manager with headers resolver\r\n const wsManager = new WebSocketManager(\r\n wsUrl,\r\n WebSocketImpl,\r\n currentHeaders,\r\n debug,\r\n nodeEnv!,\r\n );\r\n\r\n // Helper to resolve headers before each request\r\n const resolveHeaders = async (\r\n defaultContentType = true,\r\n ): Promise<Record<string, string>> => {\r\n const baseHeaders: Record<string, string> = {};\r\n\r\n if (defaultContentType) {\r\n baseHeaders[\"Content-Type\"] = \"application/json\";\r\n }\r\n\r\n // Use the same headersResolver as WebSocket\r\n const dynamicHeaders = await headersResolver();\r\n return { ...baseHeaders, ...dynamicHeaders };\r\n };\r\n\r\n // Helper to handle HTTP responses\r\n const handleResponse = async (response: Response) => {\r\n if (!response.ok) {\r\n let errorData;\r\n try {\r\n errorData = await response.json();\r\n } catch {\r\n try {\r\n errorData = { error: await response.text() };\r\n } catch {\r\n errorData = { error: \"Failed to parse error response\" };\r\n }\r\n }\r\n\r\n // Handle the new BRPCError format from server\r\n if (errorData?.error && typeof errorData.error === \"object\") {\r\n const serverError = errorData.error;\r\n\r\n throw new BrpcClientError(\r\n serverError.message || response.statusText,\r\n response.status,\r\n serverError.code,\r\n serverError.clientCode,\r\n serverError.data,\r\n );\r\n }\r\n\r\n // Handle legacy error format or simple string errors\r\n const errorMessage =\r\n errorData?.error || errorData?.message || response.statusText;\r\n\r\n throw new BrpcClientError(\r\n errorMessage,\r\n response.status,\r\n undefined,\r\n undefined,\r\n errorData,\r\n );\r\n }\r\n\r\n // Check content type to determine how to handle the response\r\n const contentType = response.headers.get(\"Content-Type\") || \"\";\r\n\r\n try {\r\n if (contentType.includes(\"application/json\")) {\r\n const result = await response.json();\r\n return result.data;\r\n } else if (contentType.includes(\"text/\")) {\r\n return response.text();\r\n } else {\r\n return response.blob();\r\n }\r\n } catch (error) {\r\n // Wrap JSON parsing errors\r\n throw new BrpcClientError(\r\n `Failed to parse response: ${\r\n error instanceof Error ? error.message : \"Unknown error\"\r\n }`,\r\n response.status,\r\n \"PARSE_ERROR\",\r\n );\r\n }\r\n };\r\n\r\n // Wrap fetch AND response handling to always return BrpcClientError\r\n const safeFetch = async (url: string | URL, init?: RequestInit) => {\r\n try {\r\n const response = await fetchImpl(url, init);\r\n return await handleResponse(response); // This throws BrpcClientError if not ok\r\n } catch (error) {\r\n // If already a BrpcClientError (from handleResponse), re-throw as-is\r\n if (error instanceof BrpcClientError) {\r\n throw error;\r\n }\r\n\r\n // Network errors - wrap them\r\n throw new BrpcClientError(\r\n error instanceof Error ? error.message : \"Network request failed\",\r\n 0,\r\n \"NETWORK_ERROR\",\r\n );\r\n }\r\n };\r\n\r\n // Create a proxy for a specific path\r\n const createProxy = (pathSegments: string[] = []): any => {\r\n return new Proxy(\r\n {},\r\n {\r\n get(_, key: string | symbol) {\r\n if (typeof key === \"symbol\") return undefined;\r\n\r\n // Special methods for procedures\r\n if (\r\n key === \"query\" ||\r\n key === \"mutation\" ||\r\n key === \"formMutation\" ||\r\n key === \"subscription\" ||\r\n key === \"getStringKey\" ||\r\n key === \"getArrayKey\" ||\r\n key === \"getNoInputsArrayKey\"\r\n ) {\r\n // The path up to this point is the procedure path\r\n const procedurePath = pathSegments.join(\"/\");\r\n const fullUrl = `${formattedBaseUrl}${proceduresPrefixedPath}/${procedurePath}`;\r\n\r\n if (debug) {\r\n console.log(`BRPC ${key} procedure path: ${procedurePath}`);\r\n console.log(`BRPC ${key} full URL: ${fullUrl}`);\r\n }\r\n\r\n if (key === \"query\") {\r\n return async (input: any) => {\r\n // Get fresh headers for this request\r\n const headers = await resolveHeaders(true);\r\n\r\n if (debug) {\r\n console.log(`BRPC query request to ${procedurePath}`, {\r\n input,\r\n headers,\r\n });\r\n }\r\n\r\n const url = new URL(fullUrl);\r\n\r\n // Add input parameters to URL for GET request\r\n if (input && typeof input === \"object\") {\r\n Object.entries(input).forEach(([k, v]) => {\r\n if (v !== undefined) {\r\n url.searchParams.append(k, String(v));\r\n }\r\n });\r\n }\r\n\r\n const result = await safeFetch(url.toString(), {\r\n method: \"GET\",\r\n headers,\r\n credentials: \"include\", //always include credentials\r\n });\r\n\r\n if (debug) {\r\n console.log(\r\n `BRPC query response from ${procedurePath}:`,\r\n result,\r\n );\r\n }\r\n\r\n return result;\r\n };\r\n } else if (key === \"mutation\") {\r\n return async (input: any) => {\r\n // Get fresh headers for this request\r\n const headers = await resolveHeaders(true);\r\n\r\n if (debug) {\r\n console.log(`BRPC mutation request to ${procedurePath}`, {\r\n input,\r\n headers,\r\n });\r\n }\r\n\r\n const body = JSON.stringify(input);\r\n\r\n const result = await safeFetch(fullUrl, {\r\n method: \"POST\",\r\n headers,\r\n body,\r\n credentials: \"include\",\r\n });\r\n\r\n if (debug) {\r\n console.log(\r\n `BRPC mutation response from ${procedurePath}:`,\r\n result,\r\n );\r\n }\r\n\r\n return result;\r\n };\r\n } else if (key === \"formMutation\") {\r\n return async (input: Record<string, any>) => {\r\n // Get fresh headers for this request (no Content-Type for FormData)\r\n const headers = await resolveHeaders(false);\r\n\r\n if (debug) {\r\n console.log(`BRPC formMutation request to ${procedurePath}`, {\r\n input,\r\n headers,\r\n });\r\n }\r\n\r\n // Convert object input to FormData\r\n const formData = new FormData();\r\n\r\n // Helper function to append values to FormData\r\n const appendToFormData = (\r\n key: string,\r\n value: any,\r\n parentKey?: string,\r\n ) => {\r\n const fullKey = parentKey ? `${parentKey}[${key}]` : key;\r\n\r\n if (value === null || value === undefined) {\r\n // Skip null/undefined values\r\n return;\r\n } else if (value instanceof File || value instanceof Blob) {\r\n // Handle files directly\r\n formData.append(fullKey, value);\r\n } else if (Array.isArray(value)) {\r\n // Handle arrays\r\n value.forEach((item, index) => {\r\n if (item instanceof File || item instanceof Blob) {\r\n // ✅ Append files with the same key (not indexed)\r\n formData.append(fullKey, item);\r\n } else if (typeof item === \"object\" && item !== null) {\r\n Object.entries(item).forEach(([subKey, subValue]) => {\r\n appendToFormData(\r\n subKey,\r\n subValue,\r\n `${fullKey}[${index}]`,\r\n );\r\n });\r\n } else {\r\n formData.append(`${fullKey}[${index}]`, String(item));\r\n }\r\n });\r\n } else if (typeof value === \"object\" && value !== null) {\r\n // Handle nested objects\r\n Object.entries(value).forEach(([subKey, subValue]) => {\r\n appendToFormData(subKey, subValue, fullKey);\r\n });\r\n } else {\r\n // Handle primitive values\r\n formData.append(fullKey, String(value));\r\n }\r\n };\r\n\r\n // Convert input object to FormData\r\n Object.entries(input).forEach(([key, value]) => {\r\n appendToFormData(key, value);\r\n });\r\n\r\n const result = await safeFetch(fullUrl, {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n credentials: \"include\",\r\n });\r\n\r\n if (debug) {\r\n console.log(\r\n `BRPC formMutation response from ${procedurePath}:`,\r\n result,\r\n );\r\n }\r\n\r\n return result;\r\n };\r\n } else if (key === \"subscription\") {\r\n return (callback: (data: any) => void) => {\r\n if (debug) {\r\n console.log(`BRPC subscription to ${procedurePath}`);\r\n }\r\n\r\n // Use the same pattern as fullUrl for consistency\r\n const topicPath = proceduresPrefixedPath\r\n ? `${proceduresPrefixedPath.slice(1)}/${procedurePath}` // Remove leading /\r\n : procedurePath;\r\n\r\n // Update WebSocket auth if needed before subscribing\r\n wsManager.updateAuth();\r\n\r\n // Return an object with unsubscribe and publish methods\r\n return wsManager.subscribe(topicPath, callback);\r\n };\r\n } else if (key === \"getStringKey\") {\r\n return (input?: any) => {\r\n // Create a deterministic key based on procedure path and input\r\n const baseKey = procedurePath;\r\n\r\n if (!input || Object.keys(input).length === 0) {\r\n return baseKey;\r\n }\r\n\r\n // Sort input keys for deterministic key generation\r\n const sortedInput = Object.keys(input)\r\n .sort()\r\n .reduce(\r\n (result, key) => {\r\n const value = input[key];\r\n if (value !== undefined && value !== null) {\r\n result[key] = value;\r\n }\r\n return result;\r\n },\r\n {} as Record<string, any>,\r\n );\r\n\r\n // Only add input hash if there are actual values\r\n if (Object.keys(sortedInput).length === 0) {\r\n return baseKey;\r\n }\r\n\r\n // Create a simple hash from the sorted input\r\n const inputString = JSON.stringify(sortedInput);\r\n return `${baseKey}?${inputString}`;\r\n };\r\n } else if (key === \"getArrayKey\") {\r\n return (\r\n input?: any,\r\n context?: Record<string, string | number | boolean>,\r\n ) => {\r\n const basePath = procedurePath;\r\n const merged = { ...(input ?? {}), ...(context ?? {}) };\r\n\r\n if (Object.keys(merged).length === 0) {\r\n return [basePath];\r\n }\r\n\r\n // Normalize and sort keys for deterministic key generation\r\n const normalizedPairs: Array<[string, any]> = [];\r\n\r\n Object.keys(merged)\r\n .sort()\r\n .forEach((key) => {\r\n const value = merged[key];\r\n if (value !== undefined && value !== null) {\r\n let normalizedValue = value;\r\n\r\n if (\r\n typeof value === \"boolean\" ||\r\n typeof value === \"number\"\r\n ) {\r\n normalizedValue = String(value);\r\n } else if (typeof value === \"object\") {\r\n normalizedValue = JSON.stringify(value);\r\n }\r\n\r\n normalizedPairs.push([key, normalizedValue]);\r\n }\r\n });\r\n\r\n if (normalizedPairs.length === 0) {\r\n return [basePath];\r\n }\r\n\r\n const inputSegments = normalizedPairs.flatMap(\r\n ([key, value]) => [key, value],\r\n );\r\n\r\n return [basePath, ...inputSegments];\r\n };\r\n } else if (key === \"getNoInputsArrayKey\") {\r\n return () => {\r\n // Always return just the procedure path, ignoring any inputs\r\n // Perfect for invalidating all variants of a procedure\r\n return [procedurePath];\r\n };\r\n }\r\n }\r\n\r\n // Continue traversing the path for nested routes\r\n return createProxy([...pathSegments, key]);\r\n },\r\n },\r\n );\r\n };\r\n\r\n // Create the base proxy\r\n const clientProxy = createProxy() as InferClientRoutes<TRoutesStructure>;\r\n\r\n if (debug) {\r\n console.log(\"BRPC client created\", {\r\n baseUrl: formattedBaseUrl,\r\n prefix: proceduresPrefixedPath,\r\n wsUrl: wsUrl,\r\n });\r\n }\r\n\r\n return {\r\n routes: clientProxy,\r\n\r\n storage: {\r\n getObjectUrl: (storageObject, opts) =>\r\n getObjectUrl(storageObject, opts, s3Endpoint!),\r\n },\r\n\r\n utils: {\r\n updateWsAuth: async () => await wsManager.updateAuth(),\r\n\r\n setHeader: async (key: string, value: string) => {\r\n if (typeof currentHeaders === \"function\") {\r\n console.warn(\r\n \"Cannot use setHeader with function-based headers resolver\",\r\n );\r\n return;\r\n }\r\n currentHeaders = { ...currentHeaders, [key]: value };\r\n await wsManager.updateAuth();\r\n },\r\n\r\n setHeaders: async (headers: Record<string, string>) => {\r\n if (typeof currentHeaders === \"function\") {\r\n console.warn(\r\n \"Cannot use setHeaders with function-based headers resolver\",\r\n );\r\n return;\r\n }\r\n currentHeaders = { ...currentHeaders, ...headers };\r\n await wsManager.updateAuth();\r\n },\r\n },\r\n };\r\n}\r\n"
|
|
10
10
|
],
|
|
11
|
-
"mappings": "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",
|
|
12
|
-
"debugId": "
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "5DCF873B33A21E1164756E2164756E21",
|
|
13
13
|
"names": []
|
|
14
14
|
}
|
package/dist/client/types.d.ts
CHANGED
|
@@ -21,37 +21,37 @@ export type SubscriptionHandlers<InputType, _OutputType> = {
|
|
|
21
21
|
export type ProcedureMethods<T extends BaseProcedure> = T["_type"] extends "query" | "streamQuery" | "file" | "html" ? IsOptionalInput<T> extends true ? {
|
|
22
22
|
query: (input?: InferProcedureInput<T>) => Promise<InferProcedureOutput<T>>;
|
|
23
23
|
getStringKey: (input?: InferProcedureInput<T>) => string;
|
|
24
|
-
getArrayKey: (input?: InferProcedureInput<T>) => (string | number | boolean)[];
|
|
24
|
+
getArrayKey: (input?: InferProcedureInput<T>, context?: Record<string, string | number | boolean>) => (string | number | boolean)[];
|
|
25
25
|
getNoInputsArrayKey: () => (string | number | boolean)[];
|
|
26
26
|
} : {
|
|
27
27
|
query: (input: InferProcedureInput<T>) => Promise<InferProcedureOutput<T>>;
|
|
28
28
|
getStringKey: (input?: InferProcedureInput<T>) => string;
|
|
29
|
-
getArrayKey: (input?: InferProcedureInput<T>) => (string | number | boolean)[];
|
|
29
|
+
getArrayKey: (input?: InferProcedureInput<T>, context?: Record<string, string | number | boolean>) => (string | number | boolean)[];
|
|
30
30
|
getNoInputsArrayKey: () => (string | number | boolean)[];
|
|
31
31
|
} : T["_type"] extends "mutation" | "streamMutation" ? IsOptionalInput<T> extends true ? {
|
|
32
32
|
mutation: (input?: InferProcedureInput<T>) => Promise<InferProcedureOutput<T>>;
|
|
33
33
|
getStringKey: (input?: InferProcedureInput<T>) => string;
|
|
34
|
-
getArrayKey: (input?: InferProcedureInput<T>) => (string | number | boolean)[];
|
|
34
|
+
getArrayKey: (input?: InferProcedureInput<T>, context?: Record<string, string | number | boolean>) => (string | number | boolean)[];
|
|
35
35
|
getNoInputsArrayKey: () => (string | number | boolean)[];
|
|
36
36
|
} : {
|
|
37
37
|
mutation: (input: InferProcedureInput<T>) => Promise<InferProcedureOutput<T>>;
|
|
38
38
|
getStringKey: (input?: InferProcedureInput<T>) => string;
|
|
39
|
-
getArrayKey: (input?: InferProcedureInput<T>) => (string | number | boolean)[];
|
|
39
|
+
getArrayKey: (input?: InferProcedureInput<T>, context?: Record<string, string | number | boolean>) => (string | number | boolean)[];
|
|
40
40
|
getNoInputsArrayKey: () => (string | number | boolean)[];
|
|
41
41
|
} : T["_type"] extends "formMutation" ? IsOptionalInput<T> extends true ? {
|
|
42
42
|
formMutation: (input?: InferProcedureInput<T>) => Promise<InferProcedureOutput<T>>;
|
|
43
43
|
getStringKey: (input?: InferProcedureInput<T>) => string;
|
|
44
|
-
getArrayKey: (input?: InferProcedureInput<T>) => (string | number | boolean)[];
|
|
44
|
+
getArrayKey: (input?: InferProcedureInput<T>, context?: Record<string, string | number | boolean>) => (string | number | boolean)[];
|
|
45
45
|
getNoInputsArrayKey: () => (string | number | boolean)[];
|
|
46
46
|
} : {
|
|
47
47
|
formMutation: (input: InferProcedureInput<T>) => Promise<InferProcedureOutput<T>>;
|
|
48
48
|
getStringKey: (input?: InferProcedureInput<T>) => string;
|
|
49
|
-
getArrayKey: (input?: InferProcedureInput<T>) => (string | number | boolean)[];
|
|
49
|
+
getArrayKey: (input?: InferProcedureInput<T>, context?: Record<string, string | number | boolean>) => (string | number | boolean)[];
|
|
50
50
|
getNoInputsArrayKey: () => (string | number | boolean)[];
|
|
51
51
|
} : T["_type"] extends "subscription" ? {
|
|
52
52
|
subscription: (callback: (data: InferProcedureOutput<T>) => void) => SubscriptionHandlers<InferProcedureInput<T>, InferProcedureOutput<T>>;
|
|
53
53
|
getStringKey: (input?: InferProcedureInput<T>) => string;
|
|
54
|
-
getArrayKey: (input?: InferProcedureInput<T>) => string[];
|
|
54
|
+
getArrayKey: (input?: InferProcedureInput<T>, context?: Record<string, string | number | boolean>) => string[];
|
|
55
55
|
} : never;
|
|
56
56
|
export type InferClientRoutes<T> = {
|
|
57
57
|
[K in keyof T]: T[K] extends BaseProcedure ? ProcedureMethods<T[K]> : T[K] extends Record<string, any> ? InferClientRoutes<T[K]> : never;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { BunFile } from "bun";
|
|
2
2
|
import type { UploadableImageBuffer } from "./types";
|
|
3
3
|
import type { Acl } from "../types";
|
|
4
|
-
export declare function optimizeImage(file: File | BunFile | string, {
|
|
5
|
-
|
|
4
|
+
export declare function optimizeImage(file: File | BunFile | string, { size, quality, acl, }?: {
|
|
5
|
+
size?: number;
|
|
6
6
|
quality?: number;
|
|
7
7
|
acl?: Acl;
|
|
8
8
|
}): Promise<UploadableImageBuffer>;
|
|
9
|
-
export declare function optimizeImageBuffer(buffer: Buffer, { name,
|
|
9
|
+
export declare function optimizeImageBuffer(buffer: Buffer, { name, size, quality, acl, }?: {
|
|
10
10
|
name?: string;
|
|
11
|
-
|
|
11
|
+
size?: number;
|
|
12
12
|
quality?: number;
|
|
13
13
|
acl?: Acl;
|
|
14
14
|
}): Promise<UploadableImageBuffer>;
|
package/dist/storage/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var
|
|
2
|
+
var D=(M)=>M.startsWith("image/"),U=(M)=>M.startsWith("video/"),N=(M)=>M.startsWith("audio/"),x=(M)=>M.startsWith("text/")||M.includes("pdf")||M.includes("word")||M.includes("sheet")||M.includes("presentation")||M.includes("opendocument")||M==="application/rtf",S=(M)=>M.includes("zip")||M.includes("rar")||M.includes("tar")||M.includes("7z")||M.includes("gzip"),q=(M)=>M==="application/javascript"||M==="application/json"||M==="text/html"||M==="text/css"||M==="application/xml"||M.includes("javascript"),v=(M)=>M.startsWith("font/")||M.includes("font"),F=(M)=>{if(!M)return"other";let _=M.toLowerCase();if(D(_))return"image";if(U(_))return"video";if(N(_))return"audio";if(x(_))return"document";if(S(_))return"archive";if(q(_))return"code";if(v(_))return"font";return"other"},G={isImage:D,isVideo:U,isAudio:N,isDocument:x,isArchive:S,isCode:q,isFont:v};function n(){let _=Buffer.from("Hello, this is a test file for S3 upload!","utf-8");return new File([_],"test.txt",{type:"text/plain"})}function f(M,_){if(M.url)return M.url;if(M.key)if(M.isPrivate)return`/storage/${M.key}`;else return`https://${_?.bucket??process.env.AWS_BUCKET}.s3.amazonaws.com/${M.key}`;throw Error("Invalid storage object")}function V(M,_){if(!M||M<1||!_||_<1)return"1:1";let $=(L,H)=>H===0?L:$(H,L%H),W=$(M,_);return`${M/W}:${_/W}`}function s(M,_){if(!M||!_||M<1||_<1)return"1:1";let $=M/_,W=[{ratio:1,label:"1:1"},{ratio:0.8,label:"4:5"},{ratio:0.75,label:"3:4"},{ratio:0.6666666666666666,label:"2:3"},{ratio:0.5625,label:"9:16"},{ratio:1.5,label:"3:2"},{ratio:1.3333333333333333,label:"4:3"},{ratio:1.7777777777777777,label:"16:9"},{ratio:2.3333333333333335,label:"21:9"}],L=0.03;for(let{ratio:X,label:Y}of W)if(Math.abs($-X)/X<L)return Y;let H=(X,Y)=>Y===0?X:H(Y,X%Y),J=H(M,_);return`${M/J}:${_/J}`}class Q 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(M){super(M.message);if(this.name="BRPCError",this.code=M.code,this.clientCode=M.clientCode,this.httpStatus=Q.STATUS_MAP[M.code],this.data=M.data,this.cause=M.cause,Error.captureStackTrace)Error.captureStackTrace(this,Q)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest(M,_,$){return new Q({code:"BAD_REQUEST",message:M,clientCode:_,data:$})}static unauthorized(M="Unauthorized",_,$){return new Q({code:"UNAUTHORIZED",message:M,clientCode:_,data:$})}static forbidden(M="Forbidden",_,$){return new Q({code:"FORBIDDEN",message:M,clientCode:_,data:$})}static notFound(M="Not Found",_,$){return new Q({code:"NOT_FOUND",message:M,clientCode:_,data:$})}static preconditionFailed(M="Precondition failed",_,$){return new Q({code:"NOT_FOUND",message:M,clientCode:_,data:$})}static conflict(M,_,$){return new Q({code:"CONFLICT",message:M,clientCode:_,data:$})}static unprocessableContent(M,_,$){return new Q({code:"UNPROCESSABLE_CONTENT",message:M,clientCode:_,data:$})}static tooManyRequests(M="Too many requests",_,$){return new Q({code:"TOO_MANY_REQUESTS",message:M,clientCode:_,data:$})}static internalServerError(M="Internal Server Error",_,$){return new Q({code:"INTERNAL_SERVER_ERROR",message:M,clientCode:_,data:$})}static timeout(M="Request timeout",_,$){return new Q({code:"TIMEOUT",message:M,clientCode:_,data:$})}}import w from"sharp";async function j(M,_,$,W,L){try{let{data:H,info:J}=await w(M,{limitInputPixels:1e8}).rotate().resize({width:_,height:_,fit:"inside",withoutEnlargement:!0}).webp({quality:$}).toBuffer({resolveWithObject:!0});return{buffer:H,object:{name:W.replace(/\.[^/.]+$/,".webp"),resolvedType:"image",metadata:{type:"image/webp",size:H.length,extension:".webp",width:J.width,height:J.height,aspectRatio:J.width&&J.height?J.width/J.height:1,aspectRatioStr:J.width&&J.height?V(J.width,J.height):"1:1",acl:L}}}}catch(H){throw Q.conflict(`Failed to optimize image: ${H instanceof Error?H.message:"Unknown error"}`)}}async function e(M,{size:_,quality:$,acl:W}={}){let L=_??1200,H=$??80;if(!M)throw Q.conflict("A file must be provided");let J,X="unknown.webp";if(typeof M==="string"){if(!M.startsWith("data:"))throw Q.conflict("Invalid base64 format");let[,Y]=M.split(",");if(!Y)throw Q.conflict("Invalid base64 data");J=Buffer.from(Y,"base64")}else{if(!G.isImage(M.type))throw Q.conflict("File must be an image");let Y=await M.arrayBuffer();J=Buffer.from(Y),X=M.name??X}return j(J,L,H,X,W??"public-read")}async function M0(M,{name:_="unknown.webp",size:$,quality:W,acl:L}={}){if(!M?.length)throw Q.conflict("A buffer must be provided");return j(M,$??1200,W??80,_,L??"public-read")}import b from"sharp";import{randomUUID as k}from"crypto";import{extname as R}from"path";async function z(M,_,$){if(!_)return{data:null,error:Error("File not found")};let W=M.replace(/^\/+|\/+$/g,""),L=R(_.name),H=`${k()}${L}`,J=`${W}/${H}`,X=`./buckets/${W}/${H}`;try{let Y=await Bun.write(X,_);return{data:{key:J,file:_,bytesWritten:Y},error:null}}catch(Y){return{data:null,error:Error(`Failed to upload ${_.name} to local filesystem`)}}}async function E(M,_,$){if(!_||_.length===0)return[];let W=_.map((H)=>{let J=$?.(H)??H.name;return z(M,H,J)});return await Promise.all(W)}async function B(M){let _=Bun.file(`./buckets/${M}`);if(!await _.exists())return null;return _}async function P(M){let _=Bun.file(`./buckets/${M}`);if(!await _.exists())return!1;return await _.delete(),!0}var K={uploadOne:z,uploadMany:E,getOne:B,deleteOne:P};async function X0(M){let _=`./cache/images/${M.key}_${M.width}_${M.quality}.webp`,$=await K.getOne(_);if($){if(!G.isImage($.type))return null;return $}let W=M.key,L=await K.getOne(W);if(!L)return null;if(!G.isImage(L.type))return null;let H=await L.arrayBuffer(),J=await b(H).resize(M.width,null,{withoutEnlargement:!0}).webp({quality:M.quality??75}).toBuffer();await Bun.write(_,J);let X=Bun.file(_);if(!await X.exists())return null;return X}import g from"sharp";async function V0(M){if(!M?.length)return null;try{let _=await g(M).metadata(),$=_.width??0,W=_.height??0,L=_.height>0?$/W:1;return{type:`image/${_.format}`,size:_.size??0,extension:`.${_.format}`,width:$,height:W,aspectRatio:L,aspectRatioStr:V($,W)}}catch(_){return console.warn("Failed to read image metadata:",_),null}}var I=process.env.AWS_FOLDER,T=I?`${I}/`:"";var{s3:C}=globalThis.Bun;import{extname as m}from"path";async function u(M,_,$){let W="buffer"in _?_:{buffer:_,object:{name:_.name,resolvedType:F(_.type),metadata:{type:_.type,extension:m(_.name),size:_.size,acl:$?.acl??"public-read"}}};if("buffer"in _){if(!_.object?.name||!_.object?.metadata)throw Q.badRequest("Complete metadata required when uploading buffer")}let L=M.length>1?`${M.replace(/^\/+|\/+$/g,"")}/`:"",H=W.object.metadata.extension.startsWith(".")?W.object.metadata.extension:`.${W.object.metadata.extension}`,J=`${crypto.randomUUID()}${H}`,X=`${T}${L}${J}`,Y=C.file(X),Z=W?.object?.metadata?.acl??"public-read",A=Z!=="public-read"&&Z!=="public-read-write",O=await Y.write(W.buffer,{acl:Z});return{uploadedBy:$?.uploadedBy??null,key:X,url:null,name:W.object.name,thumbnail:null,resolvedType:W.object.resolvedType,provider:"s3",isPrivate:A,metadata:{...W.object.metadata,size:O,acl:Z},isActive:!0}}async function q0(M,_,$={throwIf:"any"}){let W=await Promise.allSettled(_.map((J)=>u(M,J,{acl:$?.acl,uploadedBy:$?.uploadedBy}))),L=[],H=[];if(W.forEach((J)=>{if(J.status==="fulfilled"){if(L.push(J.value),$?.externalKeys)$.externalKeys.push(J.value.key)}else H.push(J.reason)}),$?.throwIf==="any"&&H.length>0||$?.throwIf==="all"&&H.length===_.length)throw Q.internalServerError(`Upload failed: ${H.length} of ${_.length}`);return{data:L,errors:H.length?H:void 0}}var{s3:h}=globalThis.Bun;async function c(M,_){if(!M||typeof M!=="string")throw Q.badRequest("Key is required");let $=M.trim();if(!$||$.length===0)throw Q.badRequest("Key must be a non-empty string");try{return await h.delete(M),{key:M,deleted:!0,error:null}}catch(W){return{key:M,deleted:!1,error:W}}}async function z0(M,_){return(await Promise.allSettled(M.map((W)=>c(W,_)))).map((W,L)=>{if(W.status==="fulfilled")return W.value;return{key:M[L],deleted:!1,error:W.reason}})}var{s3:y}=globalThis.Bun;async function O0(M,_={debug:!0}){try{return{exists:await y.exists(M),error:null}}catch($){if(_?.debug)console.error("There was an error checking for file existance",$);return{exists:null,error:$}}}export{u as uploadOne,q0 as uploadMany,M0 as optimizeImageBuffer,e as optimizeImage,F as mimeTypeToResolvedType,X0 as getOptimizedImage,f as getObjectUrl,V0 as getImageMetadata,s as getAspectRatioStringRound,V as getAspectRatioString,n as generateTestFile,G as fileTypePredicates,c as deleteOne,z0 as deleteMany,O0 as checkFileExistance};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=995ECF19AFCDFAC964756E2164756E21
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"import type { ResolvedFileType, StorageObject } from \"../types\";\r\n\r\nconst isImage = (type: string): boolean => type.startsWith(\"image/\");\r\n\r\nconst isVideo = (type: string): boolean => type.startsWith(\"video/\");\r\n\r\nconst isAudio = (type: string): boolean => type.startsWith(\"audio/\");\r\n\r\nconst isDocument = (type: string): boolean =>\r\n type.startsWith(\"text/\") ||\r\n type.includes(\"pdf\") ||\r\n type.includes(\"word\") ||\r\n type.includes(\"sheet\") ||\r\n type.includes(\"presentation\") ||\r\n type.includes(\"opendocument\") ||\r\n type === \"application/rtf\";\r\n\r\nconst isArchive = (type: string): boolean =>\r\n type.includes(\"zip\") ||\r\n type.includes(\"rar\") ||\r\n type.includes(\"tar\") ||\r\n type.includes(\"7z\") ||\r\n type.includes(\"gzip\");\r\n\r\nconst isCode = (type: string): boolean =>\r\n type === \"application/javascript\" ||\r\n type === \"application/json\" ||\r\n type === \"text/html\" ||\r\n type === \"text/css\" ||\r\n type === \"application/xml\" ||\r\n type.includes(\"javascript\");\r\n\r\nconst isFont = (type: string): boolean =>\r\n type.startsWith(\"font/\") || type.includes(\"font\");\r\n\r\n/**\r\n * Maps MIME types to file categories\r\n * @param mimeType - The MIME type string\r\n * @returns FileType category\r\n */\r\nconst mimeTypeToResolvedType = (mimeType: string): ResolvedFileType => {\r\n if (!mimeType) return \"other\";\r\n\r\n const type = mimeType.toLowerCase();\r\n\r\n if (isImage(type)) return \"image\";\r\n if (isVideo(type)) return \"video\";\r\n if (isAudio(type)) return \"audio\";\r\n if (isDocument(type)) return \"document\";\r\n if (isArchive(type)) return \"archive\";\r\n if (isCode(type)) return \"code\";\r\n if (isFont(type)) return \"font\";\r\n\r\n return \"other\";\r\n};\r\n\r\n// Optional: Export the predicate functions if you need them elsewhere\r\nconst fileTypePredicates = {\r\n isImage,\r\n isVideo,\r\n isAudio,\r\n isDocument,\r\n isArchive,\r\n isCode,\r\n isFont,\r\n};\r\n\r\n/**\r\n * Generates a text.txt file so you can test file uploading is working\r\n */\r\nfunction generateTestFile() {\r\n // Create test content\r\n const textContent = \"Hello, this is a test file for S3 upload!\";\r\n const buffer = Buffer.from(textContent, \"utf-8\");\r\n\r\n // Create a File object (assuming you have File constructor available in Bun)\r\n const testFile = new File([buffer], \"test.txt\", { type: \"text/plain\" });\r\n\r\n return testFile;\r\n}\r\n\r\n/**\r\n * @returns url of the file\r\n */\r\nfunction getObjectUrl(\r\n storageObject: StorageObject,\r\n options?: { bucket?: string }\r\n): string {\r\n // External file\r\n if (storageObject.url) {\r\n return storageObject.url;\r\n }\r\n\r\n // Internal file\r\n if (storageObject.key) {\r\n if (storageObject.isPrivate) {\r\n return `/storage/${storageObject.key}`; // Proxy through server\r\n } else {\r\n return `https://${\r\n options?.bucket ?? process.env.AWS_BUCKET\r\n }.s3.amazonaws.com/${storageObject.key}`; // Direct S3\r\n }\r\n }\r\n\r\n throw new Error(\"Invalid storage object\");\r\n}\r\n\r\nexport {\r\n mimeTypeToResolvedType,\r\n fileTypePredicates,\r\n getObjectUrl,\r\n generateTestFile,\r\n};\r\n",
|
|
6
6
|
"import type { AspectRatioStr } from \"../types\";\r\n\r\n/**\r\n * @description\r\n * Returns standard 16:9, 1:1\r\n * from width and height\r\n */\r\nexport function getAspectRatioString(\r\n width: number,\r\n height: number,\r\n): AspectRatioStr {\r\n if (!width || width < 1 || !height || height < 1) return \"1:1\";\r\n const gcd = (a: number, b: number): number => (b === 0 ? a : gcd(b, a % b));\r\n const divisor = gcd(width, height);\r\n return `${width / divisor}:${height / divisor}`;\r\n}\r\n\r\n/**\r\n * @description\r\n * Returns standard 16:9, 1:1\r\n * from width and height but rounding to\r\n * the nearest common ratios with some tolerance\r\n */\r\nexport function getAspectRatioStringRound(\r\n width: number,\r\n height: number,\r\n): AspectRatioStr {\r\n if (!width || !height || width < 1 || height < 1) {\r\n return \"1:1\";\r\n }\r\n\r\n const ratio = width / height;\r\n\r\n // Common aspect ratios for AI image generation\r\n const commonRatios: { ratio: number; label: AspectRatioStr }[] = [\r\n { ratio: 1 / 1, label: \"1:1\" }, // 1.0 - Square\r\n { ratio: 4 / 5, label: \"4:5\" }, // 0.8 - Portrait\r\n { ratio: 3 / 4, label: \"3:4\" }, // 0.75 - Portrait\r\n { ratio: 2 / 3, label: \"2:3\" }, // 0.667 - Portrait\r\n { ratio: 9 / 16, label: \"9:16\" }, // 0.5625 - Vertical video\r\n { ratio: 3 / 2, label: \"3:2\" }, // 1.5 - Classic photo\r\n { ratio: 4 / 3, label: \"4:3\" }, // 1.333 - Standard\r\n { ratio: 16 / 9, label: \"16:9\" }, // 1.778 - Widescreen\r\n { ratio: 21 / 9, label: \"21:9\" }, // 2.333 - Ultrawide\r\n ];\r\n\r\n const TOLERANCE = 0.03; // 3% tolerance for AI variations\r\n\r\n // Find closest common ratio\r\n for (const { ratio: commonRatio, label } of commonRatios) {\r\n const difference = Math.abs(ratio - commonRatio);\r\n const percentDiff = difference / commonRatio;\r\n\r\n if (percentDiff < TOLERANCE) {\r\n return label;\r\n }\r\n }\r\n\r\n // Fallback to exact calculation for unusual ratios\r\n const gcd = (a: number, b: number): number => (b === 0 ? a : gcd(b, a % b));\r\n const divisor = gcd(width, height);\r\n return `${width / divisor}:${height / divisor}`;\r\n}\r\n",
|
|
7
7
|
"export type BRPCErrorCode =\r\n | \"BAD_REQUEST\"\r\n | \"UNAUTHORIZED\"\r\n | \"FORBIDDEN\"\r\n | \"NOT_FOUND\"\r\n | \"METHOD_NOT_SUPPORTED\"\r\n | \"TIMEOUT\"\r\n | \"CONFLICT\"\r\n | \"PRECONDITION_FAILED\"\r\n | \"PAYLOAD_TOO_LARGE\"\r\n | \"UNPROCESSABLE_CONTENT\"\r\n | \"TOO_MANY_REQUESTS\"\r\n | \"CLIENT_CLOSED_REQUEST\"\r\n | \"INTERNAL_SERVER_ERROR\"\r\n | \"NOT_IMPLEMENTED\"\r\n | \"BAD_GATEWAY\"\r\n | \"SERVICE_UNAVAILABLE\"\r\n | \"GATEWAY_TIMEOUT\";\r\n\r\nexport interface BRPCErrorOptions {\r\n code: BRPCErrorCode;\r\n message: string;\r\n clientCode?: string; // Custom code for client-side detection\r\n cause?: Error;\r\n data?: Record<string, any>;\r\n}\r\n\r\nexport class BRPCError extends Error {\r\n public readonly code: BRPCErrorCode;\r\n public readonly clientCode?: string;\r\n public readonly httpStatus: number;\r\n public readonly data?: Record<string, any>;\r\n public readonly cause?: Error;\r\n\r\n private static readonly STATUS_MAP: Record<BRPCErrorCode, number> = {\r\n BAD_REQUEST: 400,\r\n UNAUTHORIZED: 401,\r\n FORBIDDEN: 403,\r\n NOT_FOUND: 404,\r\n METHOD_NOT_SUPPORTED: 405,\r\n TIMEOUT: 408,\r\n CONFLICT: 409,\r\n PRECONDITION_FAILED: 412,\r\n PAYLOAD_TOO_LARGE: 413,\r\n UNPROCESSABLE_CONTENT: 422,\r\n TOO_MANY_REQUESTS: 429,\r\n CLIENT_CLOSED_REQUEST: 499,\r\n INTERNAL_SERVER_ERROR: 500,\r\n NOT_IMPLEMENTED: 501,\r\n BAD_GATEWAY: 502,\r\n SERVICE_UNAVAILABLE: 503,\r\n GATEWAY_TIMEOUT: 504,\r\n };\r\n\r\n constructor(options: BRPCErrorOptions) {\r\n super(options.message);\r\n\r\n this.name = \"BRPCError\";\r\n this.code = options.code;\r\n this.clientCode = options.clientCode;\r\n this.httpStatus = BRPCError.STATUS_MAP[options.code];\r\n this.data = options.data;\r\n this.cause = options.cause;\r\n\r\n // Maintains proper stack trace for where our error was thrown (only available on V8)\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, BRPCError);\r\n }\r\n }\r\n\r\n /**\r\n * Create a serializable object for sending to frontend\r\n */\r\n toJSON() {\r\n return {\r\n name: this.name,\r\n code: this.code,\r\n clientCode: this.clientCode,\r\n message: this.message,\r\n data: this.data,\r\n httpStatus: this.httpStatus,\r\n };\r\n }\r\n\r\n /**\r\n * Static factory methods for common errors\r\n */\r\n static badRequest(\r\n message: string,\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({ code: \"BAD_REQUEST\", message, clientCode, data });\r\n }\r\n\r\n static unauthorized(\r\n message: string = \"Unauthorized\",\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({ code: \"UNAUTHORIZED\", message, clientCode, data });\r\n }\r\n\r\n static forbidden(\r\n message: string = \"Forbidden\",\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({ code: \"FORBIDDEN\", message, clientCode, data });\r\n }\r\n\r\n static notFound(\r\n message: string = \"Not Found\",\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({ code: \"NOT_FOUND\", message, clientCode, data });\r\n }\r\n\r\n static preconditionFailed(\r\n message: string = \"Precondition failed\",\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({ code: \"NOT_FOUND\", message, clientCode, data });\r\n }\r\n\r\n static conflict(\r\n message: string,\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({ code: \"CONFLICT\", message, clientCode, data });\r\n }\r\n\r\n static unprocessableContent(\r\n message: string,\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({\r\n code: \"UNPROCESSABLE_CONTENT\",\r\n message,\r\n clientCode,\r\n data,\r\n });\r\n }\r\n\r\n static tooManyRequests(\r\n message: string = \"Too many requests\",\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({\r\n code: \"TOO_MANY_REQUESTS\",\r\n message,\r\n clientCode,\r\n data,\r\n });\r\n }\r\n\r\n static internalServerError(\r\n message: string = \"Internal Server Error\",\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({\r\n code: \"INTERNAL_SERVER_ERROR\",\r\n message,\r\n clientCode,\r\n data,\r\n });\r\n }\r\n\r\n static timeout(\r\n message: string = \"Request timeout\",\r\n clientCode?: string,\r\n data?: Record<string, any>\r\n ) {\r\n return new BRPCError({ code: \"TIMEOUT\", message, clientCode, data });\r\n }\r\n}\r\n",
|
|
8
|
-
"import type { BunFile } from \"bun\";\r\nimport type { UploadableImageBuffer } from \"./types\";\r\nimport { BRPCError } from \"../../errors\";\r\nimport { fileTypePredicates } from \"../utils\";\r\nimport sharp from \"sharp\";\r\nimport type { Acl } from \"../types\";\r\nimport { getAspectRatioString } from \"../utils/aspect-ratio\";\r\n\r\nasync function processSharpImage(\r\n buffer: Buffer,\r\n
|
|
8
|
+
"import type { BunFile } from \"bun\";\r\nimport type { UploadableImageBuffer } from \"./types\";\r\nimport { BRPCError } from \"../../errors\";\r\nimport { fileTypePredicates } from \"../utils\";\r\nimport sharp from \"sharp\";\r\nimport type { Acl } from \"../types\";\r\nimport { getAspectRatioString } from \"../utils/aspect-ratio\";\r\n\r\nasync function processSharpImage(\r\n buffer: Buffer,\r\n maxSize: number,\r\n quality: number,\r\n name: string,\r\n acl: Acl,\r\n): Promise<UploadableImageBuffer> {\r\n try {\r\n const { data: optimizedBuffer, info } = await sharp(buffer, {\r\n // Prevent decompression bombs / absurd images\r\n limitInputPixels: 10000 * 10000,\r\n })\r\n // Auto-apply EXIF orientation from phones/cameras\r\n .rotate()\r\n .resize({\r\n width: maxSize,\r\n height: maxSize,\r\n fit: \"inside\",\r\n withoutEnlargement: true,\r\n })\r\n .webp({\r\n quality,\r\n })\r\n .toBuffer({ resolveWithObject: true });\r\n\r\n return {\r\n buffer: optimizedBuffer,\r\n object: {\r\n name: name.replace(/\\.[^/.]+$/, \".webp\"),\r\n resolvedType: \"image\",\r\n metadata: {\r\n type: \"image/webp\",\r\n size: optimizedBuffer.length,\r\n extension: \".webp\",\r\n width: info.width,\r\n height: info.height,\r\n aspectRatio: info.width && info.height ? info.width / info.height : 1,\r\n aspectRatioStr:\r\n info.width && info.height\r\n ? getAspectRatioString(info.width, info.height)\r\n : \"1:1\",\r\n acl,\r\n },\r\n },\r\n };\r\n } catch (error) {\r\n throw BRPCError.conflict(\r\n `Failed to optimize image: ${\r\n error instanceof Error ? error.message : \"Unknown error\"\r\n }`,\r\n );\r\n }\r\n}\r\n\r\nexport async function optimizeImage(\r\n file: File | BunFile | string,\r\n {\r\n size,\r\n quality,\r\n acl,\r\n }: {\r\n size?: number;\r\n quality?: number;\r\n acl?: Acl;\r\n } = {},\r\n): Promise<UploadableImageBuffer> {\r\n const maxSize = size ?? 1200;\r\n const q = quality ?? 80;\r\n\r\n if (!file) {\r\n throw BRPCError.conflict(\"A file must be provided\");\r\n }\r\n\r\n let inputBuffer: Buffer;\r\n let originalName = \"unknown.webp\";\r\n\r\n if (typeof file === \"string\") {\r\n if (!file.startsWith(\"data:\")) {\r\n throw BRPCError.conflict(\"Invalid base64 format\");\r\n }\r\n\r\n const [, data] = file.split(\",\");\r\n\r\n if (!data) {\r\n throw BRPCError.conflict(\"Invalid base64 data\");\r\n }\r\n\r\n inputBuffer = Buffer.from(data, \"base64\");\r\n } else {\r\n if (!fileTypePredicates.isImage(file.type)) {\r\n throw BRPCError.conflict(\"File must be an image\");\r\n }\r\n\r\n const arrayBuffer = await file.arrayBuffer();\r\n\r\n inputBuffer = Buffer.from(arrayBuffer);\r\n originalName = file.name ?? originalName;\r\n }\r\n\r\n return processSharpImage(\r\n inputBuffer,\r\n maxSize,\r\n q,\r\n originalName,\r\n acl ?? \"public-read\",\r\n );\r\n}\r\n\r\nexport async function optimizeImageBuffer(\r\n buffer: Buffer,\r\n {\r\n name = \"unknown.webp\",\r\n size,\r\n quality,\r\n acl,\r\n }: {\r\n name?: string;\r\n size?: number;\r\n quality?: number;\r\n acl?: Acl;\r\n } = {},\r\n): Promise<UploadableImageBuffer> {\r\n if (!buffer?.length) {\r\n throw BRPCError.conflict(\"A buffer must be provided\");\r\n }\r\n\r\n return processSharpImage(\r\n buffer,\r\n size ?? 1200,\r\n quality ?? 80,\r\n name,\r\n acl ?? \"public-read\",\r\n );\r\n}\r\n\r\n// async function processSharpImage(\r\n// buffer: Buffer,\r\n// maxWidth: number,\r\n// quality: number,\r\n// name: string,\r\n// acl: Acl,\r\n// ): Promise<UploadableImageBuffer> {\r\n// try {\r\n// const { data: optimizedBuffer, info } = await sharp(buffer)\r\n// .resize(maxWidth, null, { withoutEnlargement: true })\r\n// .webp({ quality })\r\n// .toBuffer({ resolveWithObject: true });\r\n\r\n// return {\r\n// buffer: optimizedBuffer,\r\n// object: {\r\n// name: name.replace(/\\.[^/.]+$/, \".webp\"),\r\n// resolvedType: \"image\",\r\n// metadata: {\r\n// type: \"image/webp\",\r\n// size: optimizedBuffer.length,\r\n// extension: \".webp\",\r\n// width: info.width,\r\n// height: info.height,\r\n// aspectRatio: info.height > 0 ? info.width / info.height : 1,\r\n// aspectRatioStr: getAspectRatioString(info.width, info.height),\r\n// acl,\r\n// },\r\n// },\r\n// };\r\n// } catch (error) {\r\n// throw BRPCError.conflict(\r\n// `Failed to optimize image: ${error instanceof Error ? error.message : \"Unknown error\"}`,\r\n// );\r\n// }\r\n// }\r\n\r\n// export async function optimizeImage(\r\n// file: File | BunFile | string,\r\n// { width, quality, acl }: { width?: number; quality?: number; acl?: Acl } = {},\r\n// ): Promise<UploadableImageBuffer> {\r\n// const maxWidth = width ?? 1200;\r\n// const q = quality ?? 75;\r\n\r\n// if (!file) {\r\n// throw BRPCError.conflict(\"A file must be provided\");\r\n// }\r\n\r\n// let inputBuffer: Buffer;\r\n// let originalName = \"unknown.webp\";\r\n\r\n// if (typeof file === \"string\") {\r\n// if (!file.startsWith(\"data:\")) {\r\n// throw BRPCError.conflict(\"Invalid base64 format\");\r\n// }\r\n// const [, data] = file.split(\",\");\r\n// if (!data) {\r\n// throw BRPCError.conflict(\"Invalid base64 data\");\r\n// }\r\n// inputBuffer = Buffer.from(data, \"base64\");\r\n// } else {\r\n// if (!fileTypePredicates.isImage(file.type)) {\r\n// throw BRPCError.conflict(\"File must be an image\");\r\n// }\r\n// const arrayBuffer = await file.arrayBuffer();\r\n// inputBuffer = Buffer.from(arrayBuffer);\r\n// originalName = file.name ?? originalName;\r\n// }\r\n\r\n// return processSharpImage(\r\n// inputBuffer,\r\n// maxWidth,\r\n// q,\r\n// originalName,\r\n// acl ?? \"public-read\",\r\n// );\r\n// }\r\n\r\n// export async function optimizeImageBuffer(\r\n// buffer: Buffer,\r\n// {\r\n// name = \"unknown.webp\",\r\n// width,\r\n// quality,\r\n// acl,\r\n// }: { name?: string; width?: number; quality?: number; acl?: Acl } = {},\r\n// ): Promise<UploadableImageBuffer> {\r\n// if (!buffer?.length) {\r\n// throw BRPCError.conflict(\"A buffer must be provided\");\r\n// }\r\n\r\n// return processSharpImage(\r\n// buffer,\r\n// width ?? 1200,\r\n// quality ?? 75,\r\n// name,\r\n// acl ?? \"public-read\",\r\n// );\r\n// }\r\n",
|
|
9
9
|
"import sharp from \"sharp\";\r\nimport { local } from \"../local\";\r\nimport { fileTypePredicates } from \"../utils\";\r\nimport type { GetOptimizedImageOptions } from \"./types\";\r\n\r\n/**\r\n * Returns an existing optimized image from cache or\r\n * creates one on the fly\r\n * @example\r\n * ```ts\r\n * const optimizedImage = await getImage({\r\n * key: \"originalFileKey\",\r\n * width: 800\r\n * })\r\n * if (!optimizedImage) throw BRPCError.notFound()\r\n * return optimizedImage\r\n * ```\r\n */\r\nexport async function getOptimizedImage(options: GetOptimizedImageOptions) {\r\n // const url = new URL(req.url);\r\n // const imagePath = url.searchParams.get(\"src\");\r\n // const width = parseInt(url.searchParams.get(\"w\") || \"800\");\r\n // const quality = parseInt(url.searchParams.get(\"q\") || \"75\");\r\n\r\n const localCache = `./cache/images/${options.key}_${options.width}_${options.quality}.webp`;\r\n\r\n // 1. Check local cache\r\n const localFile = await local.getOne(localCache);\r\n\r\n if (localFile) {\r\n if (!fileTypePredicates.isImage(localFile.type)) {\r\n return null;\r\n }\r\n return localFile;\r\n }\r\n\r\n // 2. Check Spaces cache - using Bun's global s3 instance\r\n // const cachedFile = s3.file(`appS3Folder/cache/${cacheKey}`);\r\n // if (await cachedFile.exists()) {\r\n // const cached = await cachedFile.arrayBuffer();\r\n // await Bun.write(localCache, cached);\r\n // return new Response(cached, {\r\n // headers: { 'Content-Type': 'image/webp' }\r\n // });\r\n // }\r\n\r\n // 3. Fetch from Spaces, optimize, cache\r\n // const originalFile = s3.file(`originals/${imagePath}`);\r\n // const originalBuffer = await originalFile.arrayBuffer();\r\n\r\n // 3.2 Fetch from local\r\n // const originalPath = `./buckets/cache/images/originals/${options.path}`;\r\n const originalKey = options.key;\r\n const originalFile = await local.getOne(originalKey);\r\n\r\n if (!originalFile) return null;\r\n\r\n if (!fileTypePredicates.isImage(originalFile.type)) {\r\n return null;\r\n }\r\n\r\n const buffer = await originalFile.arrayBuffer();\r\n\r\n const optimizedBuffer = await sharp(buffer)\r\n .resize(options.width, null, { withoutEnlargement: true })\r\n .webp({ quality: options.quality ?? 75 })\r\n .toBuffer();\r\n\r\n // Cache everywhere\r\n // await Promise.all([\r\n // Bun.write(localCache, optimizedBuffer),\r\n // cachedFile.write(optimizedBuffer, { type: 'image/webp' })\r\n // ]);\r\n\r\n // Cache to local\r\n await Bun.write(localCache, optimizedBuffer);\r\n\r\n const cachedFile = Bun.file(localCache);\r\n\r\n const exists = await cachedFile.exists();\r\n\r\n if (!exists) return null;\r\n\r\n return cachedFile;\r\n}\r\n",
|
|
10
10
|
"import { randomUUID } from \"crypto\";\r\nimport { extname } from \"path\";\r\nimport type { SafeResult } from \"../../types\";\r\n\r\ntype UploadSuccess = {\r\n key: string;\r\n file: File;\r\n bytesWritten: number;\r\n};\r\n\r\n/**\r\n * Uploads a file to local filesystem\r\n * @param path - The folder path (e.g. \"public/images\")\r\n * @param file - The file to upload\r\n * @param filename - Optional custom filename to use instead of original file.name\r\n * @returns Promise<SafeResult<number>> - Success: {data: bytesWritten, error: null}\r\n * Failure: {data: null, error: Error}\r\n *\r\n * Example:\r\n * ```ts\r\n * const {data, error} = await uploadToLocal(\"public/avatars\", userPhoto);\r\n * if (error) {\r\n * console.error(\"Upload failed:\", error);\r\n * return;\r\n * }\r\n * console.log(`Uploaded ${data} bytes`);\r\n * ```\r\n */\r\nasync function uploadOne(\r\n path: string,\r\n file: File,\r\n _filename?: string\r\n): Promise<SafeResult<UploadSuccess>> {\r\n if (!file)\r\n return {\r\n data: null,\r\n error: new Error(`File not found`),\r\n };\r\n\r\n const cleanPath = path.replace(/^\\/+|\\/+$/g, \"\");\r\n const ext = extname(file.name);\r\n const uniqueName = `${randomUUID()}${ext}`;\r\n const filePath = `${cleanPath}/${uniqueName}`;\r\n const fullPath = `./buckets/${cleanPath}/${uniqueName}`;\r\n\r\n try {\r\n const writtenBytes = await Bun.write(fullPath, file);\r\n return {\r\n data: {\r\n key: filePath,\r\n file: file,\r\n bytesWritten: writtenBytes,\r\n },\r\n error: null,\r\n };\r\n } catch (err) {\r\n return {\r\n data: null,\r\n error: new Error(`Failed to upload ${file.name} to local filesystem`),\r\n };\r\n }\r\n}\r\n\r\nasync function uploadMany(\r\n path: string,\r\n files: File[],\r\n nameGetter?: (file: File) => string\r\n): Promise<SafeResult<UploadSuccess>[]> {\r\n if (!files || files.length === 0) {\r\n return [];\r\n }\r\n\r\n const uploadPromises = files.map((file) => {\r\n const filename = nameGetter?.(file) ?? file.name;\r\n return uploadOne(path, file, filename);\r\n });\r\n\r\n const results = await Promise.all(uploadPromises);\r\n\r\n return results;\r\n}\r\n\r\nasync function getOne(key: string) {\r\n const file = Bun.file(`./buckets/${key}`);\r\n const exists = await file.exists();\r\n\r\n if (!exists) return null;\r\n\r\n return file;\r\n}\r\n\r\nasync function deleteOne(key: string) {\r\n const file = Bun.file(`./buckets/${key}`);\r\n const exists = await file.exists();\r\n\r\n if (!exists) return false;\r\n\r\n await file.delete();\r\n\r\n return true;\r\n}\r\n\r\nexport const local = {\r\n uploadOne,\r\n uploadMany,\r\n getOne,\r\n deleteOne,\r\n};\r\n",
|
|
11
11
|
"import sharp from \"sharp\";\r\nimport type { StrictImageMetadata } from \"./types\";\r\nimport { getAspectRatioString } from \"../utils/aspect-ratio\";\r\n\r\nexport async function getImageMetadata(\r\n buffer: Buffer,\r\n): Promise<StrictImageMetadata | null> {\r\n if (!buffer?.length) return null;\r\n\r\n try {\r\n const meta = await sharp(buffer).metadata();\r\n\r\n const width = meta.width ?? 0;\r\n const height = meta.height ?? 0;\r\n const aspectRatio = meta.height > 0 ? width / height : 1;\r\n\r\n return {\r\n // general\r\n type: `image/${meta.format}`,\r\n size: meta.size ?? 0, // original file size in bytes\r\n extension: `.${meta.format}`,\r\n\r\n // image related\r\n width: width,\r\n height: height,\r\n aspectRatio: aspectRatio,\r\n aspectRatioStr: getAspectRatioString(width, height),\r\n };\r\n } catch (err) {\r\n console.warn(\"Failed to read image metadata:\", err);\r\n return null;\r\n }\r\n}\r\n",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"import { BRPCError } from \"../../errors\";\r\nimport { s3 as s3Bun } from \"bun\";\r\nimport type { DeleteOneResult } from \"./types\";\r\n\r\nexport async function deleteOne(\r\n key: string,\r\n opts?: { debug?: boolean }\r\n): Promise<DeleteOneResult> {\r\n if (!key || typeof key !== \"string\") {\r\n throw BRPCError.badRequest(\"Key is required\");\r\n }\r\n\r\n const sanitizedKey = key.trim();\r\n if (!sanitizedKey || sanitizedKey.length === 0) {\r\n throw BRPCError.badRequest(\"Key must be a non-empty string\");\r\n }\r\n\r\n try {\r\n await s3Bun.delete(key);\r\n return {\r\n key: key,\r\n deleted: true,\r\n error: null,\r\n };\r\n } catch (e) {\r\n return {\r\n key: key,\r\n deleted: false,\r\n error: e as Error,\r\n };\r\n // if (opts?.debug) {\r\n // console.error(`Error deleting file with key \"${key}\"`, e);\r\n // }\r\n //? Handle this at app level\r\n // throw BRPCError.conflict(\r\n // \"There was an error when trying to delete the file\",\r\n // \"FILE_NOT_DELETED\"\r\n // );\r\n }\r\n}\r\n\r\n/**\r\n * Deletes multiple S3 keys in parallel.\r\n * Returns per-key result so caller can handle failures/rollback.\r\n */\r\nexport async function deleteMany(\r\n keys: string[],\r\n opts?: { debug?: boolean }\r\n): Promise<DeleteOneResult[]> {\r\n const results = await Promise.allSettled(\r\n keys.map((key) => deleteOne(key, opts))\r\n );\r\n\r\n return results.map((res, i) => {\r\n if (res.status === \"fulfilled\") return res.value;\r\n return { key: keys[i], deleted: false, error: res.reason as Error };\r\n });\r\n}\r\n",
|
|
15
15
|
"import { s3 as s3Bun } from \"bun\";\r\n\r\n/**\r\n * Checks a file existance in S3\r\n */\r\nasync function checkFileExistance(\r\n key: string,\r\n opts: { debug?: boolean } = { debug: process.env.NODE_ENV !== \"production\" }\r\n): Promise<{ exists: boolean; error: null } | { exists: null; error: Error }> {\r\n try {\r\n const exists = await s3Bun.exists(key);\r\n return { exists, error: null };\r\n } catch (e) {\r\n if (opts?.debug) {\r\n console.error(\"There was an error checking for file existance\", e);\r\n }\r\n return { exists: null, error: e as Error };\r\n }\r\n}\r\n\r\nexport { checkFileExistance };\r\n"
|
|
16
16
|
],
|
|
17
|
-
"mappings": "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
|
|
18
|
-
"debugId": "
|
|
17
|
+
"mappings": ";AAEA,IAAM,EAAU,CAAC,IAA0B,EAAK,WAAW,QAAQ,EAE7D,EAAU,CAAC,IAA0B,EAAK,WAAW,QAAQ,EAE7D,EAAU,CAAC,IAA0B,EAAK,WAAW,QAAQ,EAE7D,EAAa,CAAC,IAClB,EAAK,WAAW,OAAO,GACvB,EAAK,SAAS,KAAK,GACnB,EAAK,SAAS,MAAM,GACpB,EAAK,SAAS,OAAO,GACrB,EAAK,SAAS,cAAc,GAC5B,EAAK,SAAS,cAAc,GAC5B,IAAS,kBAEL,EAAY,CAAC,IACjB,EAAK,SAAS,KAAK,GACnB,EAAK,SAAS,KAAK,GACnB,EAAK,SAAS,KAAK,GACnB,EAAK,SAAS,IAAI,GAClB,EAAK,SAAS,MAAM,EAEhB,EAAS,CAAC,IACd,IAAS,0BACT,IAAS,oBACT,IAAS,aACT,IAAS,YACT,IAAS,mBACT,EAAK,SAAS,YAAY,EAEtB,EAAS,CAAC,IACd,EAAK,WAAW,OAAO,GAAK,EAAK,SAAS,MAAM,EAO5C,EAAyB,CAAC,IAAuC,CACrE,GAAI,CAAC,EAAU,MAAO,QAEtB,IAAM,EAAO,EAAS,YAAY,EAElC,GAAI,EAAQ,CAAI,EAAG,MAAO,QAC1B,GAAI,EAAQ,CAAI,EAAG,MAAO,QAC1B,GAAI,EAAQ,CAAI,EAAG,MAAO,QAC1B,GAAI,EAAW,CAAI,EAAG,MAAO,WAC7B,GAAI,EAAU,CAAI,EAAG,MAAO,UAC5B,GAAI,EAAO,CAAI,EAAG,MAAO,OACzB,GAAI,EAAO,CAAI,EAAG,MAAO,OAEzB,MAAO,SAIH,EAAqB,CACzB,UACA,UACA,UACA,aACA,YACA,SACA,QACF,EAKA,SAAS,CAAgB,EAAG,CAG1B,IAAM,EAAS,OAAO,KADF,4CACoB,OAAO,EAK/C,OAFiB,IAAI,KAAK,CAAC,CAAM,EAAG,WAAY,CAAE,KAAM,YAAa,CAAC,EAQxE,SAAS,CAAY,CACnB,EACA,EACQ,CAER,GAAI,EAAc,IAChB,OAAO,EAAc,IAIvB,GAAI,EAAc,IAChB,GAAI,EAAc,UAChB,MAAO,YAAY,EAAc,MAEjC,WAAO,WACL,GAAS,QAAU,QAAQ,IAAI,+BACZ,EAAc,MAIvC,MAAU,MAAM,wBAAwB,ECjGnC,SAAS,CAAoB,CAClC,EACA,EACgB,CAChB,GAAI,CAAC,GAAS,EAAQ,GAAK,CAAC,GAAU,EAAS,EAAG,MAAO,MACzD,IAAM,EAAM,CAAC,EAAW,IAAuB,IAAM,EAAI,EAAI,EAAI,EAAG,EAAI,CAAC,EACnE,EAAU,EAAI,EAAO,CAAM,EACjC,MAAO,GAAG,EAAQ,KAAW,EAAS,IASjC,SAAS,CAAyB,CACvC,EACA,EACgB,CAChB,GAAI,CAAC,GAAS,CAAC,GAAU,EAAQ,GAAK,EAAS,EAC7C,MAAO,MAGT,IAAM,EAAQ,EAAQ,EAGhB,EAA2D,CAC/D,CAAE,MAAO,EAAO,MAAO,KAAM,EAC7B,CAAE,MAAO,IAAO,MAAO,KAAM,EAC7B,CAAE,MAAO,KAAO,MAAO,KAAM,EAC7B,CAAE,MAAO,mBAAO,MAAO,KAAM,EAC7B,CAAE,MAAO,OAAQ,MAAO,MAAO,EAC/B,CAAE,MAAO,IAAO,MAAO,KAAM,EAC7B,CAAE,MAAO,mBAAO,MAAO,KAAM,EAC7B,CAAE,MAAO,mBAAQ,MAAO,MAAO,EAC/B,CAAE,MAAO,mBAAQ,MAAO,MAAO,CACjC,EAEM,EAAY,KAGlB,QAAa,MAAO,EAAa,WAAW,EAI1C,GAHmB,KAAK,IAAI,EAAQ,CAAW,EACd,EAEf,EAChB,OAAO,EAKX,IAAM,EAAM,CAAC,EAAW,IAAuB,IAAM,EAAI,EAAI,EAAI,EAAG,EAAI,CAAC,EACnE,EAAU,EAAI,EAAO,CAAM,EACjC,MAAO,GAAG,EAAQ,KAAW,EAAS,IClCjC,MAAM,UAAkB,KAAM,CACnB,KACA,WACA,WACA,KACA,YAEQ,YAA4C,CAClE,YAAa,IACb,aAAc,IACd,UAAW,IACX,UAAW,IACX,qBAAsB,IACtB,QAAS,IACT,SAAU,IACV,oBAAqB,IACrB,kBAAmB,IACnB,sBAAuB,IACvB,kBAAmB,IACnB,sBAAuB,IACvB,sBAAuB,IACvB,gBAAiB,IACjB,YAAa,IACb,oBAAqB,IACrB,gBAAiB,GACnB,EAEA,WAAW,CAAC,EAA2B,CACrC,MAAM,EAAQ,OAAO,EAUrB,GARA,KAAK,KAAO,YACZ,KAAK,KAAO,EAAQ,KACpB,KAAK,WAAa,EAAQ,WAC1B,KAAK,WAAa,EAAU,WAAW,EAAQ,MAC/C,KAAK,KAAO,EAAQ,KACpB,KAAK,MAAQ,EAAQ,MAGjB,MAAM,kBACR,MAAM,kBAAkB,KAAM,CAAS,EAO3C,MAAM,EAAG,CACP,MAAO,CACL,KAAM,KAAK,KACX,KAAM,KAAK,KACX,WAAY,KAAK,WACjB,QAAS,KAAK,QACd,KAAM,KAAK,KACX,WAAY,KAAK,UACnB,QAMK,WAAU,CACf,EACA,EACA,EACA,CACA,OAAO,IAAI,EAAU,CAAE,KAAM,cAAe,UAAS,aAAY,MAAK,CAAC,QAGlE,aAAY,CACjB,EAAkB,eAClB,EACA,EACA,CACA,OAAO,IAAI,EAAU,CAAE,KAAM,eAAgB,UAAS,aAAY,MAAK,CAAC,QAGnE,UAAS,CACd,EAAkB,YAClB,EACA,EACA,CACA,OAAO,IAAI,EAAU,CAAE,KAAM,YAAa,UAAS,aAAY,MAAK,CAAC,QAGhE,SAAQ,CACb,EAAkB,YAClB,EACA,EACA,CACA,OAAO,IAAI,EAAU,CAAE,KAAM,YAAa,UAAS,aAAY,MAAK,CAAC,QAGhE,mBAAkB,CACvB,EAAkB,sBAClB,EACA,EACA,CACA,OAAO,IAAI,EAAU,CAAE,KAAM,YAAa,UAAS,aAAY,MAAK,CAAC,QAGhE,SAAQ,CACb,EACA,EACA,EACA,CACA,OAAO,IAAI,EAAU,CAAE,KAAM,WAAY,UAAS,aAAY,MAAK,CAAC,QAG/D,qBAAoB,CACzB,EACA,EACA,EACA,CACA,OAAO,IAAI,EAAU,CACnB,KAAM,wBACN,UACA,aACA,MACF,CAAC,QAGI,gBAAe,CACpB,EAAkB,oBAClB,EACA,EACA,CACA,OAAO,IAAI,EAAU,CACnB,KAAM,oBACN,UACA,aACA,MACF,CAAC,QAGI,oBAAmB,CACxB,EAAkB,wBAClB,EACA,EACA,CACA,OAAO,IAAI,EAAU,CACnB,KAAM,wBACN,UACA,aACA,MACF,CAAC,QAGI,QAAO,CACZ,EAAkB,kBAClB,EACA,EACA,CACA,OAAO,IAAI,EAAU,CAAE,KAAM,UAAW,UAAS,aAAY,MAAK,CAAC,EAEvE,CCjLA,qBAIA,eAAe,CAAiB,CAC9B,EACA,EACA,EACA,EACA,EACgC,CAChC,GAAI,CACF,IAAQ,KAAM,EAAiB,QAAS,MAAM,EAAM,EAAQ,CAE1D,iBAAkB,GACpB,CAAC,EAEE,OAAO,EACP,OAAO,CACN,MAAO,EACP,OAAQ,EACR,IAAK,SACL,mBAAoB,EACtB,CAAC,EACA,KAAK,CACJ,SACF,CAAC,EACA,SAAS,CAAE,kBAAmB,EAAK,CAAC,EAEvC,MAAO,CACL,OAAQ,EACR,OAAQ,CACN,KAAM,EAAK,QAAQ,YAAa,OAAO,EACvC,aAAc,QACd,SAAU,CACR,KAAM,aACN,KAAM,EAAgB,OACtB,UAAW,QACX,MAAO,EAAK,MACZ,OAAQ,EAAK,OACb,YAAa,EAAK,OAAS,EAAK,OAAS,EAAK,MAAQ,EAAK,OAAS,EACpE,eACE,EAAK,OAAS,EAAK,OACf,EAAqB,EAAK,MAAO,EAAK,MAAM,EAC5C,MACN,KACF,CACF,CACF,EACA,MAAO,EAAO,CACd,MAAM,EAAU,SACd,6BACE,aAAiB,MAAQ,EAAM,QAAU,iBAE7C,GAIJ,eAAsB,CAAa,CACjC,GAEE,OACA,UACA,OAKE,CAAC,EAC2B,CAChC,IAAM,EAAU,GAAQ,KAClB,EAAI,GAAW,GAErB,GAAI,CAAC,EACH,MAAM,EAAU,SAAS,yBAAyB,EAGpD,IAAI,EACA,EAAe,eAEnB,GAAI,OAAO,IAAS,SAAU,CAC5B,GAAI,CAAC,EAAK,WAAW,OAAO,EAC1B,MAAM,EAAU,SAAS,uBAAuB,EAGlD,KAAS,GAAQ,EAAK,MAAM,GAAG,EAE/B,GAAI,CAAC,EACH,MAAM,EAAU,SAAS,qBAAqB,EAGhD,EAAc,OAAO,KAAK,EAAM,QAAQ,EACnC,KACL,GAAI,CAAC,EAAmB,QAAQ,EAAK,IAAI,EACvC,MAAM,EAAU,SAAS,uBAAuB,EAGlD,IAAM,EAAc,MAAM,EAAK,YAAY,EAE3C,EAAc,OAAO,KAAK,CAAW,EACrC,EAAe,EAAK,MAAQ,EAG9B,OAAO,EACL,EACA,EACA,EACA,EACA,GAAO,aACT,EAGF,eAAsB,EAAmB,CACvC,GAEE,OAAO,eACP,OACA,UACA,OAME,CAAC,EAC2B,CAChC,GAAI,CAAC,GAAQ,OACX,MAAM,EAAU,SAAS,2BAA2B,EAGtD,OAAO,EACL,EACA,GAAQ,KACR,GAAW,GACX,EACA,GAAO,aACT,EC5IF,qBCAA,qBAAS,eACT,kBAAS,aA2BT,eAAe,CAAS,CACtB,EACA,EACA,EACoC,CACpC,GAAI,CAAC,EACH,MAAO,CACL,KAAM,KACN,MAAW,MAAM,gBAAgB,CACnC,EAEF,IAAM,EAAY,EAAK,QAAQ,aAAc,EAAE,EACzC,EAAM,EAAQ,EAAK,IAAI,EACvB,EAAa,GAAG,EAAW,IAAI,IAC/B,EAAW,GAAG,KAAa,IAC3B,EAAW,aAAa,KAAa,IAE3C,GAAI,CACF,IAAM,EAAe,MAAM,IAAI,MAAM,EAAU,CAAI,EACnD,MAAO,CACL,KAAM,CACJ,IAAK,EACL,KAAM,EACN,aAAc,CAChB,EACA,MAAO,IACT,EACA,MAAO,EAAK,CACZ,MAAO,CACL,KAAM,KACN,MAAW,MAAM,oBAAoB,EAAK,0BAA0B,CACtE,GAIJ,eAAe,CAAU,CACvB,EACA,EACA,EACsC,CACtC,GAAI,CAAC,GAAS,EAAM,SAAW,EAC7B,MAAO,CAAC,EAGV,IAAM,EAAiB,EAAM,IAAI,CAAC,IAAS,CACzC,IAAM,EAAW,IAAa,CAAI,GAAK,EAAK,KAC5C,OAAO,EAAU,EAAM,EAAM,CAAQ,EACtC,EAID,OAFgB,MAAM,QAAQ,IAAI,CAAc,EAKlD,eAAe,CAAM,CAAC,EAAa,CACjC,IAAM,EAAO,IAAI,KAAK,aAAa,GAAK,EAGxC,GAAI,CAFW,MAAM,EAAK,OAAO,EAEpB,OAAO,KAEpB,OAAO,EAGT,eAAe,CAAS,CAAC,EAAa,CACpC,IAAM,EAAO,IAAI,KAAK,aAAa,GAAK,EAGxC,GAAI,CAFW,MAAM,EAAK,OAAO,EAEpB,MAAO,GAIpB,OAFA,MAAM,EAAK,OAAO,EAEX,GAGF,IAAM,EAAQ,CACnB,YACA,aACA,SACA,WACF,EDzFA,eAAsB,EAAiB,CAAC,EAAmC,CAMzE,IAAM,EAAa,kBAAkB,EAAQ,OAAO,EAAQ,SAAS,EAAQ,eAGvE,EAAY,MAAM,EAAM,OAAO,CAAU,EAE/C,GAAI,EAAW,CACb,GAAI,CAAC,EAAmB,QAAQ,EAAU,IAAI,EAC5C,OAAO,KAET,OAAO,EAmBT,IAAM,EAAc,EAAQ,IACtB,EAAe,MAAM,EAAM,OAAO,CAAW,EAEnD,GAAI,CAAC,EAAc,OAAO,KAE1B,GAAI,CAAC,EAAmB,QAAQ,EAAa,IAAI,EAC/C,OAAO,KAGT,IAAM,EAAS,MAAM,EAAa,YAAY,EAExC,EAAkB,MAAM,EAAM,CAAM,EACvC,OAAO,EAAQ,MAAO,KAAM,CAAE,mBAAoB,EAAK,CAAC,EACxD,KAAK,CAAE,QAAS,EAAQ,SAAW,EAAG,CAAC,EACvC,SAAS,EASZ,MAAM,IAAI,MAAM,EAAY,CAAe,EAE3C,IAAM,EAAa,IAAI,KAAK,CAAU,EAItC,GAAI,CAFW,MAAM,EAAW,OAAO,EAE1B,OAAO,KAEpB,OAAO,EEnFT,qBAIA,eAAsB,EAAgB,CACpC,EACqC,CACrC,GAAI,CAAC,GAAQ,OAAQ,OAAO,KAE5B,GAAI,CACF,IAAM,EAAO,MAAM,EAAM,CAAM,EAAE,SAAS,EAEpC,EAAQ,EAAK,OAAS,EACtB,EAAS,EAAK,QAAU,EACxB,EAAc,EAAK,OAAS,EAAI,EAAQ,EAAS,EAEvD,MAAO,CAEL,KAAM,SAAS,EAAK,SACpB,KAAM,EAAK,MAAQ,EACnB,UAAW,IAAI,EAAK,SAGpB,MAAO,EACP,OAAQ,EACR,YAAa,EACb,eAAgB,EAAqB,EAAO,CAAM,CACpD,EACA,MAAO,EAAK,CAEZ,OADA,QAAQ,KAAK,iCAAkC,CAAG,EAC3C,MC9BX,IAAM,EAAa,QAAQ,IAAI,WACzB,EAAyB,EAAa,GAAG,KAAgB,GCE/D,yBACA,kBAAS,aAmCT,eAAe,CAAS,CACtB,EACA,EACA,EAC6B,CAE7B,IAAM,EACJ,WAAY,EACR,EACA,CACE,OAAQ,EACR,OAAQ,CACN,KAAM,EAAK,KACX,aAAc,EAAuB,EAAK,IAAI,EAC9C,SAAU,CACR,KAAM,EAAK,KACX,UAAW,EAAQ,EAAK,IAAI,EAC5B,KAAM,EAAK,KACX,IAAK,GAAM,KAAO,aACpB,CACF,CACF,EAGN,GAAI,WAAY,GACd,GAAI,CAAC,EAAK,QAAQ,MAAQ,CAAC,EAAK,QAAQ,SACtC,MAAM,EAAU,WACd,kDACF,EAKJ,IAAM,EAAY,EAAK,OAAS,EAAI,GAAG,EAAK,QAAQ,aAAc,EAAE,KAAO,GACrE,EAAY,EAAS,OAAO,SAAS,UAAU,WAAW,GAAG,EAC/D,EAAS,OAAO,SAAS,UACzB,IAAI,EAAS,OAAO,SAAS,YAC3B,EAAW,GAAG,OAAO,WAAW,IAAI,IACpC,EAAM,GAAG,IAAyB,IAAY,IAE9C,EAAS,EAAM,KAAK,CAAG,EAEvB,EAAM,GAAU,QAAQ,UAAU,KAAO,cAEzC,EAAY,IAAQ,eAAiB,IAAQ,oBAE7C,EAAe,MAAM,EAAO,MAAM,EAAS,OAAQ,CACvD,IAAK,CACP,CAAC,EAmBD,MAjB2C,CACzC,WAAY,GAAM,YAAc,KAChC,IAAK,EACL,IAAK,KACL,KAAM,EAAS,OAAO,KACtB,UAAW,KACX,aAAc,EAAS,OAAO,aAC9B,SAAU,KACV,UAAW,EACX,SAAU,IACL,EAAS,OAAO,SACnB,KAAM,EACN,IAAK,CACP,EACA,SAAU,EACZ,EAmBF,eAAsB,EAAU,CAC9B,EACA,EACA,EAA0B,CAAE,QAAS,KAAM,EAChB,CAC3B,IAAM,EAAU,MAAM,QAAQ,WAC5B,EAAM,IAAI,CAAC,IACT,EAAU,EAAM,EAAM,CACpB,IAAK,GAAM,IACX,WAAY,GAAM,UACpB,CAAC,CACH,CACF,EAEM,EAA6B,CAAC,EAC9B,EAAgB,CAAC,EAYvB,GAVA,EAAQ,QAAQ,CAAC,IAAQ,CACvB,GAAI,EAAI,SAAW,aAEjB,GADA,EAAK,KAAK,EAAI,KAAK,EACf,GAAM,aAAc,EAAK,aAAa,KAAK,EAAI,MAAM,GAAG,EAE5D,OAAO,KAAK,EAAI,MAAM,EAEzB,EAIE,GAAM,UAAY,OAAS,EAAO,OAAS,GAC3C,GAAM,UAAY,OAAS,EAAO,SAAW,EAAM,OAEpD,MAAM,EAAU,oBACd,kBAAkB,EAAO,aAAa,EAAM,QAC9C,EAGF,MAAO,CAAE,OAAM,OAAQ,EAAO,OAAS,EAAS,MAAU,EC9J5D,yBAGA,eAAsB,CAAS,CAC7B,EACA,EAC0B,CAC1B,GAAI,CAAC,GAAO,OAAO,IAAQ,SACzB,MAAM,EAAU,WAAW,iBAAiB,EAG9C,IAAM,EAAe,EAAI,KAAK,EAC9B,GAAI,CAAC,GAAgB,EAAa,SAAW,EAC3C,MAAM,EAAU,WAAW,gCAAgC,EAG7D,GAAI,CAEF,OADA,MAAM,EAAM,OAAO,CAAG,EACf,CACL,IAAK,EACL,QAAS,GACT,MAAO,IACT,EACA,MAAO,EAAG,CACV,MAAO,CACL,IAAK,EACL,QAAS,GACT,MAAO,CACT,GAgBJ,eAAsB,EAAU,CAC9B,EACA,EAC4B,CAK5B,OAJgB,MAAM,QAAQ,WAC5B,EAAK,IAAI,CAAC,IAAQ,EAAU,EAAK,CAAI,CAAC,CACxC,GAEe,IAAI,CAAC,EAAK,IAAM,CAC7B,GAAI,EAAI,SAAW,YAAa,OAAO,EAAI,MAC3C,MAAO,CAAE,IAAK,EAAK,GAAI,QAAS,GAAO,MAAO,EAAI,MAAgB,EACnE,ECxDH,yBAKA,eAAe,EAAkB,CAC/B,EACA,EAA4B,CAAE,MAAO,EAAsC,EACC,CAC5E,GAAI,CAEF,MAAO,CAAE,OADM,MAAM,EAAM,OAAO,CAAG,EACpB,MAAO,IAAK,EAC7B,MAAO,EAAG,CACV,GAAI,GAAM,MACR,QAAQ,MAAM,iDAAkD,CAAC,EAEnE,MAAO,CAAE,OAAQ,KAAM,MAAO,CAAW",
|
|
18
|
+
"debugId": "995ECF19AFCDFAC964756E2164756E21",
|
|
19
19
|
"names": []
|
|
20
20
|
}
|