@mateosuarezdev/brpc 1.0.67 → 1.0.70

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.
@@ -0,0 +1,12 @@
1
+ export declare const COOKIE_NAMES: {
2
+ readonly ACCESS_TOKEN: "access_token";
3
+ readonly REFRESH_TOKEN: "refresh_token";
4
+ readonly SESSION_ID: "session_id";
5
+ };
6
+ export declare function setAuthCookies(headers: Headers, tokens: {
7
+ accessToken: string;
8
+ refreshToken: string;
9
+ sessionId: string;
10
+ }, isProd: boolean): void;
11
+ export declare function clearAuthCookies(headers: Headers, isProd: boolean): void;
12
+ export declare function parseCookies(cookieHeader: string | null): Record<string, string>;
@@ -1 +1,3 @@
1
- export {};
1
+ export { AuthService, createAuth } from "./service";
2
+ export { COOKIE_NAMES, setAuthCookies, clearAuthCookies, parseCookies } from "./cookies";
3
+ export type { AppMetadata, AuthConfig, AuthResponse, ContactMethod, DecodedJWT, EmailSignIn, EmailSignUp, IdentityData, IdentityRecord, OTPProvider, OTPRecord, OTPResponse, PhoneSignIn, PhoneSignUp, Session, SessionRecord, SignInMethod, SignUpMethod, SignUpOptions, UserRecord, UserResponse, } from "./types";
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ export{r as setAuthCookies,d as parseCookies,n as createAuth,p as clearAuthCookies,s as COOKIE_NAMES,t as AuthService};
3
+
4
+ //# debugId=6CBC2D2D58A69A8964756E2164756E21
@@ -0,0 +1,89 @@
1
+ import type { AppMetadata, AuthConfig, AuthResponse, ContactMethod, DecodedJWT, IdentityRecord, OTPResponse, Session, SignInMethod, SignUpMethod, SignUpOptions, UserMetadata, UserRecord, UserResponse } from "./types";
2
+ export declare class AuthService<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> {
3
+ private userManager;
4
+ private identityManager;
5
+ private sessionManager;
6
+ private otpManager;
7
+ private tokenManager;
8
+ private cache;
9
+ private config;
10
+ constructor(config: AuthConfig<TAppMetadata>);
11
+ initialize(): Promise<void>;
12
+ signUp(method: SignUpMethod<TAppMetadata, TUserMetadata>, req: Request): Promise<AuthResponse<TAppMetadata, TUserMetadata> | OTPResponse>;
13
+ signIn(method: SignInMethod, req: Request): Promise<AuthResponse<TAppMetadata, TUserMetadata> | OTPResponse>;
14
+ verifyOTP(otpId: string, code: string, req: Request): Promise<AuthResponse<TAppMetadata, TUserMetadata>>;
15
+ signOut(sessionId: string): Promise<void>;
16
+ signOutAll(userId: string): Promise<void>;
17
+ signOutAllExcept(userId: string, currentSessionId: string): Promise<void>;
18
+ getUserSessions(userId: string): Promise<Omit<Session, "userId">[]>;
19
+ refreshTokens(refreshToken: string): Promise<{
20
+ accessToken: string;
21
+ refreshToken: string;
22
+ sessionId: string;
23
+ user: UserResponse<TAppMetadata, TUserMetadata>;
24
+ }>;
25
+ verifyToken(authorizationHeader?: string | null): Promise<DecodedJWT<TAppMetadata, TUserMetadata> | null>;
26
+ fastVerifyToken(authorizationHeader?: string | null): Promise<DecodedJWT<TAppMetadata, TUserMetadata> | null>;
27
+ verifyTokenFromCookie(cookieHeader: string | null): Promise<DecodedJWT<TAppMetadata, TUserMetadata> | null>;
28
+ refreshTokenFromCookie(cookieHeader: string | null): Promise<{
29
+ accessToken: string;
30
+ refreshToken: string;
31
+ user: UserResponse<TAppMetadata, TUserMetadata>;
32
+ } | null>;
33
+ getUserById(userId: string): Promise<UserRecord<TAppMetadata, TUserMetadata> | undefined>;
34
+ getUserByContact(contact: ContactMethod): Promise<UserRecord<TAppMetadata, TUserMetadata> | undefined>;
35
+ getUserIdentities(userId: string): Promise<IdentityRecord[]>;
36
+ updateUserPassword(userId: string, password: string): Promise<void>;
37
+ updateAppMetadata(userId: string, appMetadata?: Partial<TAppMetadata>): Promise<void>;
38
+ updateUserMetadata(userId: string, userMetadata?: Partial<TUserMetadata>): Promise<void>;
39
+ changePassword(userId: string, currentPassword: string, newPassword: string): Promise<void>;
40
+ requestPasswordReset(contact: ContactMethod): Promise<OTPResponse>;
41
+ resetPassword(otpId: string, code: string, newPassword: string): Promise<{
42
+ success: true;
43
+ }>;
44
+ adminCreateUser({ email, phone, password, options, }: {
45
+ email?: string;
46
+ phone?: string;
47
+ password?: string;
48
+ options?: SignUpOptions<TAppMetadata, TUserMetadata>;
49
+ }): Promise<UserResponse<TAppMetadata, TUserMetadata>>;
50
+ cacheSession(token: string, session: DecodedJWT<TAppMetadata, TUserMetadata>): void;
51
+ getCachedSession(token: string): DecodedJWT<TAppMetadata, TUserMetadata> | null;
52
+ invalidateCachedSession(token: string): boolean;
53
+ getCacheStats(): {
54
+ size: number;
55
+ valid: number;
56
+ expired: number;
57
+ maxSize: number;
58
+ defaultTTL: number | null;
59
+ entries: {
60
+ key: string;
61
+ expiresIn: number | null;
62
+ age: number;
63
+ }[];
64
+ };
65
+ clearCache(): void;
66
+ isTokenNearExpiry(exp: number): boolean;
67
+ testCreateUserWithSession({ email, phone, password, options, req, }: {
68
+ email?: string;
69
+ phone?: string;
70
+ password?: string;
71
+ options?: SignUpOptions<TAppMetadata, TUserMetadata>;
72
+ req: Request;
73
+ }): Promise<AuthResponse<TAppMetadata, TUserMetadata>>;
74
+ private startPasswordlessSignUp;
75
+ private startPasswordlessSignIn;
76
+ private completePasswordSignUp;
77
+ private completePasswordSignIn;
78
+ private completePasswordlessSignUp;
79
+ private completePasswordlessSignIn;
80
+ private createAuthSession;
81
+ private validateSignUpMethod;
82
+ private validateSignInMethod;
83
+ private validateEmail;
84
+ private normalizePhoneNumber;
85
+ private extractContact;
86
+ private extractIdentityKey;
87
+ private initializeCleanup;
88
+ }
89
+ export declare function createAuth<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata>(config: AuthConfig<TAppMetadata>): AuthService<TAppMetadata, TUserMetadata>;
@@ -1 +1,153 @@
1
- export {};
1
+ import type { RecordId, Surreal } from "surrealdb";
2
+ export type AppMetadata = {
3
+ roles: string[];
4
+ [key: string]: unknown;
5
+ };
6
+ export type UserMetadata = {
7
+ name: string | null;
8
+ app_theme: string | null;
9
+ preferred_language: string | null;
10
+ };
11
+ export type UserRecord<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = {
12
+ id: RecordId;
13
+ created_at: string;
14
+ updated_at: string;
15
+ email: string | null;
16
+ email_verified: boolean;
17
+ phone_verified: boolean;
18
+ phone: string | null;
19
+ app_metadata: TAppMetadata;
20
+ user_metadata: TUserMetadata;
21
+ last_sign_in_at: string | null;
22
+ };
23
+ export type IdentityData = {
24
+ password_hash?: string;
25
+ avatar_url?: string;
26
+ [key: string]: unknown;
27
+ };
28
+ export type IdentityRecord = {
29
+ id: RecordId;
30
+ user_id: RecordId;
31
+ provider: string;
32
+ provider_id: string;
33
+ identity_data: IdentityData;
34
+ created_at: string;
35
+ updated_at: string;
36
+ };
37
+ export type SessionRecord = {
38
+ id: RecordId;
39
+ user_id: RecordId;
40
+ ip_address: string | null;
41
+ user_agent: string | null;
42
+ invalidated_at: string | null;
43
+ created_at: string;
44
+ };
45
+ export type OTPRecord = {
46
+ id: RecordId;
47
+ user_id: RecordId | null;
48
+ email: string | null;
49
+ phone: string | null;
50
+ code: string;
51
+ hashed_code: string;
52
+ type: "signup" | "signin" | "password_reset";
53
+ signup_options: SignUpOptions | null;
54
+ expires_at: string;
55
+ verified_at: string | null;
56
+ attempts: number;
57
+ created_at: string;
58
+ };
59
+ export type Session = {
60
+ id: string;
61
+ userId: string;
62
+ ipAddress: string | null;
63
+ userAgent: string | null;
64
+ invalidatedAt?: string | null;
65
+ };
66
+ export type UserResponse<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = {
67
+ id: string;
68
+ email: string | null;
69
+ phone: string | null;
70
+ appMetadata: TAppMetadata;
71
+ userMetadata: TUserMetadata;
72
+ };
73
+ export type AuthResponse<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = {
74
+ user: UserResponse<TAppMetadata, TUserMetadata>;
75
+ session: Session;
76
+ accessToken: string;
77
+ refreshToken: string;
78
+ };
79
+ export type OTPResponse = {
80
+ otpId: string;
81
+ expiresAt: string;
82
+ message: string;
83
+ };
84
+ export type DecodedJWT<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = {
85
+ sub: string;
86
+ sessionId: string;
87
+ appMetadata: TAppMetadata;
88
+ userMetadata: TUserMetadata;
89
+ exp: number;
90
+ };
91
+ export type ContactMethod = {
92
+ email?: string;
93
+ phone?: string;
94
+ };
95
+ export type SignUpOptions<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = {
96
+ appMetadata?: Partial<TAppMetadata>;
97
+ userMetadata?: Partial<TUserMetadata>;
98
+ };
99
+ export type EmailSignUp<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = {
100
+ type: "email";
101
+ email: string;
102
+ password?: string;
103
+ passwordless?: boolean;
104
+ options?: SignUpOptions<TAppMetadata, TUserMetadata>;
105
+ };
106
+ export type PhoneSignUp<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = {
107
+ type: "phone";
108
+ phone: string;
109
+ password?: string;
110
+ passwordless?: boolean;
111
+ options?: SignUpOptions<TAppMetadata, TUserMetadata>;
112
+ };
113
+ export type EmailSignIn = {
114
+ type: "email";
115
+ email: string;
116
+ password?: string;
117
+ passwordless?: boolean;
118
+ };
119
+ export type PhoneSignIn = {
120
+ type: "phone";
121
+ phone: string;
122
+ password?: string;
123
+ passwordless?: boolean;
124
+ };
125
+ export type SignUpMethod<TAppMetadata extends AppMetadata = AppMetadata, TUserMetadata extends UserMetadata = UserMetadata> = EmailSignUp<TAppMetadata, TUserMetadata> | PhoneSignUp<TAppMetadata, TUserMetadata>;
126
+ export type SignInMethod = EmailSignIn | PhoneSignIn;
127
+ export interface OTPProvider {
128
+ sendEmailOTP(params: {
129
+ email: string;
130
+ code: string;
131
+ }): Promise<any>;
132
+ sendSMSOTP(params: {
133
+ phone: string;
134
+ code: string;
135
+ }): Promise<any>;
136
+ sendWhatsAppOTP(params: {
137
+ phone: string;
138
+ code: string;
139
+ }): Promise<any>;
140
+ }
141
+ export type AuthConfig<TAppMetadata extends AppMetadata = AppMetadata> = {
142
+ db: Surreal;
143
+ secrets: {
144
+ jwt: string;
145
+ jwtRefresh: string;
146
+ };
147
+ defaultSignupRoles: TAppMetadata["roles"];
148
+ otpProvider?: OTPProvider;
149
+ accessTokenExpiry?: string;
150
+ isProd?: boolean;
151
+ debug?: boolean;
152
+ disableCleanupCron?: boolean;
153
+ };
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- var S=($)=>$.startsWith("image/"),j=($)=>$.startsWith("video/"),q=($)=>$.startsWith("audio/"),F=($)=>$.startsWith("text/")||$.includes("pdf")||$.includes("word")||$.includes("sheet")||$.includes("presentation")||$.includes("opendocument")||$==="application/rtf",N=($)=>$.includes("zip")||$.includes("rar")||$.includes("tar")||$.includes("7z")||$.includes("gzip"),T=($)=>$==="application/javascript"||$==="application/json"||$==="text/html"||$==="text/css"||$==="application/xml"||$.includes("javascript"),z=($)=>$.startsWith("font/")||$.includes("font"),O=($)=>{if(!$)return"other";let W=$.toLowerCase();if(S(W))return"image";if(j(W))return"video";if(q(W))return"audio";if(F(W))return"document";if(N(W))return"archive";if(T(W))return"code";if(z(W))return"font";return"other"},_={isImage:S,isVideo:j,isAudio:q,isDocument:F,isArchive:N,isCode:T,isFont:z};function o(){let W=Buffer.from("Hello, this is a test file for S3 upload!","utf-8");return new File([W],"test.txt",{type:"text/plain"})}function r($,W){if($.url)return $.url;if($.key)if($.isPrivate)return`/storage/${$.key}`;else return`https://${W?.bucket??process.env.AWS_BUCKET}.s3.amazonaws.com/${$.key}`;throw Error("Invalid storage object")}function G($,W){if(!$||$<1||!W||W<1)return"1:1";let H=(L,Q)=>Q===0?L:H(Q,L%Q),J=H($,W);return`${$/J}:${W/J}`}function a($,W){if(!$||!W||$<1||W<1)return"1:1";let H=$/W,J=[{ratio:1,label:"1:1"},{ratio:0.8,label:"4:5"},{ratio:0.75,label:"3:4"},{ratio:0.6666666666666666,label:"2:3"},{ratio:0.5625,label:"9:16"},{ratio:1.5,label:"3:2"},{ratio:1.3333333333333333,label:"4:3"},{ratio:1.7777777777777777,label:"16:9"},{ratio:2.3333333333333335,label:"21:9"}],L=0.03;for(let{ratio:X,label:Z}of J)if(Math.abs(H-X)/X<L)return Z;let Q=(X,Z)=>Z===0?X:Q(Z,X%Z),Y=Q($,W);return`${$/Y}:${W/Y}`}class M extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=M.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,M)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,W,H){return new M({code:"BAD_REQUEST",message:$,clientCode:W,data:H})}static unauthorized($="Unauthorized",W,H){return new M({code:"UNAUTHORIZED",message:$,clientCode:W,data:H})}static forbidden($="Forbidden",W,H){return new M({code:"FORBIDDEN",message:$,clientCode:W,data:H})}static notFound($="Not Found",W,H){return new M({code:"NOT_FOUND",message:$,clientCode:W,data:H})}static preconditionFailed($="Precondition failed",W,H){return new M({code:"NOT_FOUND",message:$,clientCode:W,data:H})}static conflict($,W,H){return new M({code:"CONFLICT",message:$,clientCode:W,data:H})}static unprocessableContent($,W,H){return new M({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:W,data:H})}static tooManyRequests($="Too many requests",W,H){return new M({code:"TOO_MANY_REQUESTS",message:$,clientCode:W,data:H})}static internalServerError($="Internal Server Error",W,H){return new M({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:W,data:H})}static timeout($="Request timeout",W,H){return new M({code:"TIMEOUT",message:$,clientCode:W,data:H})}}import P from"sharp";var w={xs:200,sm:400,md:800,lg:1200},V=1920;async function D($,W,H,J,L){try{let{data:Q,info:Y}=await P($,{limitInputPixels:1e8}).rotate().resize({width:W,height:W,fit:"inside",withoutEnlargement:!0}).webp({quality:H}).toBuffer({resolveWithObject:!0});return{buffer:Q,object:{name:J.replace(/\.[^/.]+$/,".webp"),resolvedType:"image",metadata:{type:"image/webp",size:Q.length,extension:".webp",width:Y.width,height:Y.height,aspectRatio:Y.width&&Y.height?Y.width/Y.height:1,aspectRatioStr:Y.width&&Y.height?G(Y.width,Y.height):"1:1",acl:L}}}}catch(Q){throw M.conflict(`Failed to optimize image: ${Q instanceof Error?Q.message:"Unknown error"}`)}}async function I($){if(!$)throw M.conflict("A file must be provided");let W,H="unknown.webp";if(typeof $==="string"){if(console.log("I'm going to fail here for file",$),!$.startsWith("data:"))throw console.log("I never got here"),M.conflict("Invalid base64 format");let[,J]=$.split(",");if(!J)throw M.conflict("Invalid base64 data");W=Buffer.from(J,"base64")}else{if(!_.isImage($.type))throw M.conflict("File must be an image");let J=await $.arrayBuffer();W=Buffer.from(J),H=$.name??H}return{buffer:W,name:H}}async function L0($,{size:W,quality:H,acl:J}={}){let L=await I($);return D(L.buffer,W??V,H??80,L.name,J??"public-read")}async function M0($,{name:W="unknown.webp",size:H,quality:J,acl:L}={}){if(!$?.length)throw M.conflict("A buffer must be provided");return D($,H??V,J??80,W,L??"public-read")}async function k($,{quality:W=80,acl:H="public-read"}={}){console.log("Going to normalizeImageInput");let J=await I($);console.log("Going to processSharpImage for original");let L=await D(J.buffer,V,W,J.name,H);console.log("Going to processSharpImage for variantsEntries");let Q=await Promise.all(Object.entries(w).map(async([Y,X])=>{let Z=await D(J.buffer,X,W,J.name,H);return[Y,Z]}));return{original:L,variants:Object.fromEntries(Q)}}import h from"sharp";import{randomUUID as b}from"crypto";import{extname as C}from"path";async function v($,W,H){if(!W)return{data:null,error:Error("File not found")};let J=$.replace(/^\/+|\/+$/g,""),L=C(W.name),Q=`${b()}${L}`,Y=`${J}/${Q}`,X=`./buckets/${J}/${Q}`;try{let Z=await Bun.write(X,W);return{data:{key:Y,file:W,bytesWritten:Z},error:null}}catch(Z){return{data:null,error:Error(`Failed to upload ${W.name} to local filesystem`)}}}async function g($,W,H){if(!W||W.length===0)return[];let J=W.map((Q)=>{let Y=H?.(Q)??Q.name;return v($,Q,Y)});return await Promise.all(J)}async function m($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return null;return W}async function u($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return!1;return await W.delete(),!0}var U={uploadOne:v,uploadMany:g,getOne:m,deleteOne:u};async function D0($){let W=`./cache/images/${$.key}_${$.width}_${$.quality}.webp`,H=await U.getOne(W);if(H){if(!_.isImage(H.type))return null;return H}let J=$.key,L=await U.getOne(J);if(!L)return null;if(!_.isImage(L.type))return null;let Q=await L.arrayBuffer(),Y=await h(Q).resize($.width,null,{withoutEnlargement:!0}).webp({quality:$.quality??75}).toBuffer();await Bun.write(W,Y);let X=Bun.file(W);if(!await X.exists())return null;return X}import c from"sharp";async function j0($){if(!$?.length)return null;try{let W=await c($).metadata(),H=W.width??0,J=W.height??0,L=W.height>0?H/J:1;return{type:`image/${W.format}`,size:W.size??0,extension:`.${W.format}`,width:H,height:J,aspectRatio:L,aspectRatioStr:G(H,J)}}catch(W){return console.warn("Failed to read image metadata:",W),null}}var R=process.env.AWS_FOLDER,B=R?`${R}/`:"";var{s3:y}=globalThis.Bun;import{extname as d}from"path";async function A($,W,H){let J="buffer"in W?W:{buffer:W,object:{name:W.name,resolvedType:O(W.type),metadata:{type:W.type,extension:d(W.name),size:W.size,acl:H?.acl??"public-read"}}};if("buffer"in W){if(!W.object?.name||!W.object?.metadata)throw M.badRequest("Complete metadata required when uploading buffer")}let L=$.length>1?`${$.replace(/^\/+|\/+$/g,"")}/`:"",Q=J.object.metadata.extension.startsWith(".")?J.object.metadata.extension:`.${J.object.metadata.extension}`,Y=J.object.name?J.object.name:`${crypto.randomUUID()}${Q}`,X=`${B}${L}${Y}`,Z=y.file(X),K=J?.object?.metadata?.acl??"public-read",x=K!=="public-read"&&K!=="public-read-write",E=await Z.write(J.buffer,{acl:K,type:J.object.metadata.type});return{uploadedBy:H?.uploadedBy??null,key:X,url:null,name:J.object.name,thumbnail:null,resolvedType:J.object.resolvedType,provider:"s3",isPrivate:x,metadata:{...J.object.metadata,size:E,acl:K},isActive:!0}}async function k0($,W,H={throwIf:"any"}){let J=await Promise.allSettled(W.map((Y)=>A($,Y,{acl:H?.acl,uploadedBy:H?.uploadedBy}))),L=[],Q=[];if(J.forEach((Y)=>{if(Y.status==="fulfilled"){if(L.push(Y.value),H?.externalKeys)H.externalKeys.push(Y.value.key)}else Q.push(Y.reason)}),H?.throwIf==="any"&&Q.length>0||H?.throwIf==="all"&&Q.length===W.length)throw M.internalServerError(`Upload failed: ${Q.length} of ${W.length}`);return{data:L,errors:Q.length?Q:void 0}}async function f($,W,H){let J=H?.baseId??crypto.randomUUID(),L=W.original.object.metadata.extension,Q=await A($,{...W.original,object:{...W.original.object,name:`${J}${L}`}},H),Y=await Promise.all(Object.entries(W.variants).map(async([X,Z])=>{if(!Z)return[X,null];let K=Z.object.metadata.extension,x=await A($,{...Z,object:{...Z.object,name:`${J}_${X}${K}`}},H);return[X,x]}));return{original:Q,variants:Object.fromEntries(Y.filter(([,X])=>X!==null))}}async function v0($,W,H){if(Array.isArray(W))throw Error("File can't be an array, pass a single file");let J=await k(W,{quality:H?.quality,acl:H?.acl});return f($,J,{acl:H?.acl,uploadedBy:H?.uploadedBy})}var{s3:p}=globalThis.Bun;async function n($,W){if(!$||typeof $!=="string")throw M.badRequest("Key is required");let H=$.trim();if(!H||H.length===0)throw M.badRequest("Key must be a non-empty string");try{return await p.delete($),{key:$,deleted:!0,error:null}}catch(J){return{key:$,deleted:!1,error:J}}}async function P0($,W){return(await Promise.allSettled($.map((J)=>n(J,W)))).map((J,L)=>{if(J.status==="fulfilled")return J.value;return{key:$[L],deleted:!1,error:J.reason}})}var{s3:s}=globalThis.Bun;async function g0($,W={debug:!0}){try{return{exists:await s.exists($),error:null}}catch(H){if(W?.debug)console.error("There was an error checking for file existance",H);return{exists:null,error:H}}}export{A as uploadOne,k0 as uploadMany,f as uploadGeneratedVariants,M0 as optimizeImageBuffer,L0 as optimizeImage,O as mimeTypeToResolvedType,D0 as getOptimizedImage,r as getObjectUrl,j0 as getImageMetadata,a as getAspectRatioStringRound,G as getAspectRatioString,k as generateVariants,o as generateTestFile,v0 as generateAndUploadImage,_ as fileTypePredicates,n as deleteOne,P0 as deleteMany,g0 as checkFileExistance};
2
+ var S=($)=>$.startsWith("image/"),j=($)=>$.startsWith("video/"),q=($)=>$.startsWith("audio/"),F=($)=>$.startsWith("text/")||$.includes("pdf")||$.includes("word")||$.includes("sheet")||$.includes("presentation")||$.includes("opendocument")||$==="application/rtf",N=($)=>$.includes("zip")||$.includes("rar")||$.includes("tar")||$.includes("7z")||$.includes("gzip"),T=($)=>$==="application/javascript"||$==="application/json"||$==="text/html"||$==="text/css"||$==="application/xml"||$.includes("javascript"),z=($)=>$.startsWith("font/")||$.includes("font"),O=($)=>{if(!$)return"other";let W=$.toLowerCase();if(S(W))return"image";if(j(W))return"video";if(q(W))return"audio";if(F(W))return"document";if(N(W))return"archive";if(T(W))return"code";if(z(W))return"font";return"other"},x={isImage:S,isVideo:j,isAudio:q,isDocument:F,isArchive:N,isCode:T,isFont:z};function o(){let W=Buffer.from("Hello, this is a test file for S3 upload!","utf-8");return new File([W],"test.txt",{type:"text/plain"})}function r($,W){if($.url)return $.url;if($.key)if($.isPrivate)return`/storage/${$.key}`;else return`https://${W?.bucket??process.env.AWS_BUCKET}.s3.amazonaws.com/${$.key}`;throw Error("Invalid storage object")}function G($,W){if(!$||$<1||!W||W<1)return"1:1";let H=(L,Q)=>Q===0?L:H(Q,L%Q),J=H($,W);return`${$/J}:${W/J}`}function a($,W){if(!$||!W||$<1||W<1)return"1:1";let H=$/W,J=[{ratio:1,label:"1:1"},{ratio:0.8,label:"4:5"},{ratio:0.75,label:"3:4"},{ratio:0.6666666666666666,label:"2:3"},{ratio:0.5625,label:"9:16"},{ratio:1.5,label:"3:2"},{ratio:1.3333333333333333,label:"4:3"},{ratio:1.7777777777777777,label:"16:9"},{ratio:2.3333333333333335,label:"21:9"}],L=0.03;for(let{ratio:X,label:Z}of J)if(Math.abs(H-X)/X<L)return Z;let Q=(X,Z)=>Z===0?X:Q(Z,X%Z),Y=Q($,W);return`${$/Y}:${W/Y}`}class M extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=M.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,M)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,W,H){return new M({code:"BAD_REQUEST",message:$,clientCode:W,data:H})}static unauthorized($="Unauthorized",W,H){return new M({code:"UNAUTHORIZED",message:$,clientCode:W,data:H})}static forbidden($="Forbidden",W,H){return new M({code:"FORBIDDEN",message:$,clientCode:W,data:H})}static notFound($="Not Found",W,H){return new M({code:"NOT_FOUND",message:$,clientCode:W,data:H})}static preconditionFailed($="Precondition failed",W,H){return new M({code:"NOT_FOUND",message:$,clientCode:W,data:H})}static conflict($,W,H){return new M({code:"CONFLICT",message:$,clientCode:W,data:H})}static unprocessableContent($,W,H){return new M({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:W,data:H})}static tooManyRequests($="Too many requests",W,H){return new M({code:"TOO_MANY_REQUESTS",message:$,clientCode:W,data:H})}static internalServerError($="Internal Server Error",W,H){return new M({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:W,data:H})}static timeout($="Request timeout",W,H){return new M({code:"TIMEOUT",message:$,clientCode:W,data:H})}}import P from"sharp";var w={xs:200,sm:400,md:800,lg:1200},V=1920;async function D($,W,H,J,L){try{let{data:Q,info:Y}=await P($,{limitInputPixels:1e8}).rotate().resize({width:W,height:W,fit:"inside",withoutEnlargement:!0}).webp({quality:H}).toBuffer({resolveWithObject:!0});return{buffer:Q,object:{name:J.replace(/\.[^/.]+$/,".webp"),resolvedType:"image",metadata:{type:"image/webp",size:Q.length,extension:".webp",width:Y.width,height:Y.height,aspectRatio:Y.width&&Y.height?Y.width/Y.height:1,aspectRatioStr:Y.width&&Y.height?G(Y.width,Y.height):"1:1",acl:L}}}}catch(Q){throw M.conflict(`Failed to optimize image: ${Q instanceof Error?Q.message:"Unknown error"}`)}}async function I($){if(!$)throw M.conflict("A file must be provided");let W,H="unknown.webp";if(typeof $==="string"){if(console.log("I'm going to fail here for file",$),!$.startsWith("data:"))throw console.log("I never got here"),M.conflict("Invalid base64 format");let[,J]=$.split(",");if(!J)throw M.conflict("Invalid base64 data");W=Buffer.from(J,"base64")}else{if(!x.isImage($.type))throw M.conflict("File must be an image");let J=await $.arrayBuffer();W=Buffer.from(J),H=$.name??H}return{buffer:W,name:H}}async function L0($,{size:W,quality:H,acl:J}={}){let L=await I($);return D(L.buffer,W??V,H??80,L.name,J??"public-read")}async function M0($,{name:W="unknown.webp",size:H,quality:J,acl:L}={}){if(!$?.length)throw M.conflict("A buffer must be provided");return D($,H??V,J??80,W,L??"public-read")}async function k($,{quality:W=80,acl:H="public-read"}={}){console.log("Going to normalizeImageInput");let J=await I($);console.log("Going to processSharpImage for original");let L=await D(J.buffer,V,W,J.name,H);console.log("Going to processSharpImage for variantsEntries");let Q=await Promise.all(Object.entries(w).map(async([Y,X])=>{let Z=await D(J.buffer,X,W,J.name,H);return[Y,Z]}));return{original:L,variants:Object.fromEntries(Q)}}import c from"sharp";import{randomUUID as b}from"crypto";import{extname as C}from"path";async function v($,W,H){if(!W)return{data:null,error:Error("File not found")};let J=$.replace(/^\/+|\/+$/g,""),L=C(W.name),Q=`${b()}${L}`,Y=`${J}/${Q}`,X=`./buckets/${J}/${Q}`;try{let Z=await Bun.write(X,W);return{data:{key:Y,file:W,bytesWritten:Z},error:null}}catch(Z){return{data:null,error:Error(`Failed to upload ${W.name} to local filesystem`)}}}async function g($,W,H){if(!W||W.length===0)return[];let J=W.map((Q)=>{let Y=H?.(Q)??Q.name;return v($,Q,Y)});return await Promise.all(J)}async function m($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return null;return W}async function h($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return!1;return await W.delete(),!0}var U={uploadOne:v,uploadMany:g,getOne:m,deleteOne:h};async function D0($){let W=`./cache/images/${$.key}_${$.width}_${$.quality}.webp`,H=await U.getOne(W);if(H){if(!x.isImage(H.type))return null;return H}let J=$.key,L=await U.getOne(J);if(!L)return null;if(!x.isImage(L.type))return null;let Q=await L.arrayBuffer(),Y=await c(Q).resize($.width,null,{withoutEnlargement:!0}).webp({quality:$.quality??75}).toBuffer();await Bun.write(W,Y);let X=Bun.file(W);if(!await X.exists())return null;return X}import u from"sharp";async function j0($){if(!$?.length)return null;try{let W=await u($).metadata(),H=W.width??0,J=W.height??0,L=W.height>0?H/J:1;return{type:`image/${W.format}`,size:W.size??0,extension:`.${W.format}`,width:H,height:J,aspectRatio:L,aspectRatioStr:G(H,J)}}catch(W){return console.warn("Failed to read image metadata:",W),null}}var R=process.env.AWS_FOLDER,B=R?`${R}/`:"";var{s3:y}=globalThis.Bun;import{extname as d}from"path";async function A($,W,H){let J="buffer"in W?W:{buffer:W,object:{name:W.name,resolvedType:O(W.type),metadata:{type:W.type,extension:d(W.name),size:W.size,acl:H?.acl??"public-read"}}};if("buffer"in W){if(!W.object?.name||!W.object?.metadata)throw M.badRequest("Complete metadata required when uploading buffer")}let L=$.length>1?`${$.replace(/^\/+|\/+$/g,"")}/`:"",Q=J.object.metadata.extension.startsWith(".")?J.object.metadata.extension:`.${J.object.metadata.extension}`,Y=J.object.name?J.object.name:`${crypto.randomUUID()}${Q}`,X=`${B}${L}${Y}`,Z=y.file(X),_=J?.object?.metadata?.acl??"public-read",K=_!=="public-read"&&_!=="public-read-write",E=await Z.write(J.buffer,{acl:_,type:J.object.metadata.type});return{uploadedBy:H?.uploadedBy??null,key:X,url:null,name:J.object.name,thumbnail:null,resolvedType:J.object.resolvedType,provider:"s3",isPrivate:K,metadata:{...J.object.metadata,size:E,acl:_},isActive:!0}}async function k0($,W,H={throwIf:"any"}){let J=await Promise.allSettled(W.map((Y)=>A($,Y,{acl:H?.acl,uploadedBy:H?.uploadedBy}))),L=[],Q=[];if(J.forEach((Y)=>{if(Y.status==="fulfilled"){if(L.push(Y.value),H?.externalKeys)H.externalKeys.push(Y.value.key)}else Q.push(Y.reason)}),H?.throwIf==="any"&&Q.length>0||H?.throwIf==="all"&&Q.length===W.length)throw M.internalServerError(`Upload failed: ${Q.length} of ${W.length}`);return{data:L,errors:Q.length?Q:void 0}}async function f($,W,H){let J=H?.baseId??crypto.randomUUID(),L=W.original.object.metadata.extension,Q=await A($,{...W.original,object:{...W.original.object,name:`${J}${L}`}},H),Y=await Promise.all(Object.entries(W.variants).map(async([X,Z])=>{if(!Z)return[X,null];let _=Z.object.metadata.extension,K=await A($,{...Z,object:{...Z.object,name:`${J}_${X}${_}`}},H);return[X,{key:K.key,url:K.url,name:K.name,metadata:{type:K.metadata?.type,size:K.metadata?.size,extension:K.metadata?.extension,width:K.metadata?.width,height:K.metadata?.height,aspectRatio:K.metadata?.aspectRatio,aspectRatioStr:K.metadata?.aspectRatioStr,resolution:K.metadata?.resolution,bitrate:K.metadata?.bitrate,codec:K.metadata?.codec,fps:K.metadata?.fps}}]}));return{original:Q,variants:Object.fromEntries(Y.filter(([,X])=>X!==null))}}async function v0($,W,H){if(Array.isArray(W))throw Error("File can't be an array, pass a single file");let J=await k(W,{quality:H?.quality,acl:H?.acl});return f($,J,{acl:H?.acl,uploadedBy:H?.uploadedBy})}var{s3:n}=globalThis.Bun;async function p($,W){if(!$||typeof $!=="string")throw M.badRequest("Key is required");let H=$.trim();if(!H||H.length===0)throw M.badRequest("Key must be a non-empty string");try{return await n.delete($),{key:$,deleted:!0,error:null}}catch(J){return{key:$,deleted:!1,error:J}}}async function P0($,W){return(await Promise.allSettled($.map((J)=>p(J,W)))).map((J,L)=>{if(J.status==="fulfilled")return J.value;return{key:$[L],deleted:!1,error:J.reason}})}var{s3:s}=globalThis.Bun;async function g0($,W={debug:!0}){try{return{exists:await s.exists($),error:null}}catch(H){if(W?.debug)console.error("There was an error checking for file existance",H);return{exists:null,error:H}}}export{A as uploadOne,k0 as uploadMany,f as uploadGeneratedVariants,M0 as optimizeImageBuffer,L0 as optimizeImage,O as mimeTypeToResolvedType,D0 as getOptimizedImage,r as getObjectUrl,j0 as getImageMetadata,a as getAspectRatioStringRound,G as getAspectRatioString,k as generateVariants,o as generateTestFile,v0 as generateAndUploadImage,x as fileTypePredicates,p as deleteOne,P0 as deleteMany,g0 as checkFileExistance};
3
3
 
4
- //# debugId=C1DE6ED0C292ED5764756E2164756E21
4
+ //# debugId=5E1C8B325A3FF69364756E2164756E21
@@ -35,6 +35,28 @@ type StorageObjectMetadata = {
35
35
  alt?: string;
36
36
  colorPalette?: string[];
37
37
  };
38
+ type StorageObjectVariantMetadata = {
39
+ /**
40
+ * Mime type
41
+ */
42
+ type: string;
43
+ /**
44
+ * File size in bytes
45
+ */
46
+ size: number;
47
+ /**
48
+ * File extension
49
+ */
50
+ extension: string;
51
+ width?: number;
52
+ height?: number;
53
+ aspectRatio?: number;
54
+ aspectRatioStr?: AspectRatioStr;
55
+ resolution?: string;
56
+ bitrate?: number;
57
+ codec?: string;
58
+ fps?: number;
59
+ };
38
60
  /**
39
61
  * s3 for s3 compatible provider
40
62
  * local for local filesystem
@@ -64,6 +86,12 @@ type StorageObject = {
64
86
  isActive: boolean;
65
87
  referenceCount: number;
66
88
  };
89
+ type StorageObjectVariant = {
90
+ key: string | null;
91
+ url: string | null;
92
+ name: string | null;
93
+ metadata: StorageObjectVariantMetadata;
94
+ };
67
95
  type InsertStorageObjet = Omit<StorageObject, "id" | "createdAt" | "updatedAt" | "referenceCount"> & {
68
96
  key: string;
69
97
  };
@@ -75,4 +103,4 @@ export type UploadableObjectBuffer = {
75
103
  metadata: StorageObjectMetadata;
76
104
  };
77
105
  };
78
- export type { ResolvedFileType, StorageObject, StorageObjectMetadata, Acl, StorageProvider, InsertStorageObjet, };
106
+ export type { ResolvedFileType, StorageObject, StorageObjectMetadata, StorageObjectVariant, StorageObjectVariantMetadata, Acl, StorageProvider, InsertStorageObjet, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mateosuarezdev/brpc",
3
- "version": "1.0.67",
3
+ "version": "1.0.70",
4
4
  "description": "A Type-Safe, Flexible Web application framework for Bun",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -31,6 +31,11 @@
31
31
  "types": "./dist/cache/index.d.ts",
32
32
  "import": "./dist/cache/index.js",
33
33
  "require": "./dist/cache/index.cjs"
34
+ },
35
+ "./auth": {
36
+ "types": "./dist/auth/index.d.ts",
37
+ "import": "./dist/auth/index.js",
38
+ "require": "./dist/auth/index.cjs"
34
39
  }
35
40
  },
36
41
  "files": [
@@ -73,12 +78,25 @@
73
78
  "bun": ">=1.2.0"
74
79
  },
75
80
  "peerDependencies": {
81
+ "bcryptjs": ">=2.4.0",
82
+ "jsonwebtoken": ">=9.0.0",
83
+ "node-cron": ">=3.0.0",
76
84
  "react": "^19.0.0",
77
85
  "react-dom": "^19.0.0",
78
86
  "sharp": "^0.34.4",
87
+ "surrealdb": ">=2.0.0",
79
88
  "zod": "^3.23.8"
80
89
  },
81
90
  "peerDependenciesMeta": {
91
+ "bcryptjs": {
92
+ "optional": true
93
+ },
94
+ "jsonwebtoken": {
95
+ "optional": true
96
+ },
97
+ "node-cron": {
98
+ "optional": true
99
+ },
82
100
  "react": {
83
101
  "optional": true
84
102
  },
@@ -88,16 +106,26 @@
88
106
  "sharp": {
89
107
  "optional": true
90
108
  },
109
+ "surrealdb": {
110
+ "optional": true
111
+ },
91
112
  "zod": {
92
113
  "optional": false
93
114
  }
94
115
  },
95
116
  "devDependencies": {
117
+ "@types/bcryptjs": "^2.4.6",
96
118
  "@types/bun": "^1.2.23",
119
+ "@types/jsonwebtoken": "^9.0.0",
120
+ "@types/node-cron": "^3.0.0",
97
121
  "@types/react": "^19.2.2",
98
122
  "@types/react-dom": "^19.2.2",
123
+ "bcryptjs": "^2.4.2",
124
+ "jsonwebtoken": "^9.0.0",
125
+ "node-cron": "^3.0.0",
99
126
  "rimraf": "^6.0.1",
100
127
  "sharp": "^0.34.4",
128
+ "surrealdb": "^2.0.0",
101
129
  "typescript": "^5.9.3",
102
130
  "zod": "^3.23.8"
103
131
  },
@@ -1 +0,0 @@
1
- export {};