@mateosuarezdev/brpc 1.0.68 → 1.0.71
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/dist/auth/cookies.d.ts +12 -0
- package/dist/auth/index.d.ts +3 -1
- package/dist/auth/index.js +4 -0
- package/dist/auth/service.d.ts +89 -0
- package/dist/auth/types.d.ts +153 -1
- package/dist/client/helpers.d.ts +1 -1
- package/dist/client/index.js +2 -2
- package/package.json +29 -1
- package/dist/auth/utils.d.ts +0 -1
|
@@ -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>;
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -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,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>;
|
package/dist/auth/types.d.ts
CHANGED
|
@@ -1 +1,153 @@
|
|
|
1
|
-
|
|
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
|
+
};
|
package/dist/client/helpers.d.ts
CHANGED
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 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};
|
|
2
|
+
function b(q,G={placeholder:void 0,throwOnInvalid:!0},N){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`${N}/${q.key}`;if(G.throwOnInvalid)throw Error("Invalid storage object");else return G?.placeholder??"PLACEHOLDER"}class T extends Error{status;code;clientCode;data;constructor(q,G,N,Q,R){super(q);this.name="BrpcClientError",this.status=G,this.code=N,this.clientCode=Q,this.data=R}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"}}var U=(q)=>{return q instanceof File||q instanceof Blob||typeof q==="object"&&q!==null&&"uri"in q&&"name"in q&&"type"in q};function x(q){if(!q)return null;return q.replace(/\//g,"")}function v(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 m(q,G){let N=G?`/${G}/ws`:"/ws";return`${q.replace(/^http/,"ws")}${N}`}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 h{baseUrl;ws=null;subscriptions=new Map;messageQueue=[];isConnected=!1;reconnectTimeout=null;WebSocketImpl;getHeaders;authToken=null;debug;pendingAuth=!1;authActions=[];constructor(q,G,N,Q=!1,R){this.baseUrl=q;this.WebSocketImpl=G,this.debug=Q&&R==="development",this.getHeaders=async()=>{if(typeof N==="function")return await N();return N??{}},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 N of this.subscriptions.keys())if(this.send({type:"subscribe",topic:N}),this.debug)console.log(`Resubscribed to topic: ${N}`);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((N)=>this.send(N)),this.messageQueue=[]})}else{for(let N of this.subscriptions.keys())if(this.send({type:"subscribe",topic:N}),this.debug)console.log(`Resubscribed to topic: ${N}`);if(this.messageQueue.length>0&&this.debug)console.log(`Sending ${this.messageQueue.length} queued messages`);this.messageQueue.forEach((N)=>this.send(N)),this.messageQueue=[]}},this.ws.onmessage=(N)=>{try{if(this.debug)console.log("WebSocket message received:",N.data);let Q=JSON.parse(N.data);if(Q.type==="auth_success"){let R=Q.authenticated!==!1;if(this.debug)console.log(`WebSocket ${R?"authentication":"deauthentication"} successful`);if(this.pendingAuth=!1,R)this.authActions.forEach((S)=>S());this.authActions=[];return}if(Q.type==="auth_error"){console.error("WebSocket authentication failed:",Q.error),this.pendingAuth=!1,this.authActions=[];return}if(this.debug)console.log("Got new message",Q);if(Q.topic&&this.subscriptions.has(Q.topic)){let R=this.subscriptions.get(Q.topic);if(this.debug)console.log("Calling subscription callbacks");R?.forEach((S)=>S(Q.data))}else if(Q.error)console.error("WebSocket error:",Q.error);else console.warn("Unhandled WebSocket message format:",Q)}catch(Q){if(this.debug)console.error("Error processing WebSocket message:",Q),console.error("Raw message:",N.data)}},this.ws.onclose=(N)=>{if(this.isConnected=!1,this.pendingAuth=!1,this.debug)console.log(`WebSocket connection closed. Code: ${N.code}, Reason: ${N.reason}`);this.reconnectTimeout=setTimeout(()=>this.connect(),2000)},this.ws.onerror=(N)=>{if(this.debug)console.log("WebSocket error:",N)}}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:(N)=>this.publish(q,N)}}unsubscribe(q,G){let N=this.subscriptions.get(q);if(N){if(N.delete(G),N.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||"",N=G.startsWith("Bearer ")?G.substring(7).trim():G.trim(),Q=N.length>0,R=this.authToken!==null&&this.authToken.trim().length>0;if((Q!==R||N!==this.authToken)&&this.isConnected){if(this.authToken=N,this.pendingAuth=Q,this.send({type:"authenticate",token:G}),this.debug)if(Q)console.log("Updated authentication token, waiting for confirmation");else console.log("Sent empty token for deauthentication"),this.pendingAuth=!1,this.authActions=[]}}}function Qq(q,G={}){let N=G.fetch||fetch,Q=G.WebSocket??(typeof WebSocket<"u"?WebSocket:null),R=G.prefix??"",S=G.apiPrefix??"",M=G.debug??!1,g=G.s3Endpoint,c=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(!Q)throw Error("WebSocket is not available in this environment");let w=G.headers??{},d=async()=>{if(typeof w==="function")return await w();return w},j=v(q),f=x(G.prefix),u=x(G.apiPrefix),D=k(f,u),P=m(j,f),B=new h(P,Q,w,M,c),O=async(X=!0)=>{let I={};if(X)I["Content-Type"]="application/json";let J=await d();return{...I,...J}},n=async(X)=>{if(!X.ok){let J;try{J=await X.json()}catch{try{J={error:await X.text()}}catch{J={error:"Failed to parse error response"}}}if(J?.error&&typeof J.error==="object"){let A=J.error;throw new T(A.message||X.statusText,X.status,A.code,A.clientCode,A.data)}let $=J?.error||J?.message||X.statusText;throw new T($,X.status,void 0,void 0,J)}let I=X.headers.get("Content-Type")||"";try{if(I.includes("application/json"))return(await X.json()).data;else if(I.includes("text/"))return X.text();else return X.blob()}catch(J){throw new T(`Failed to parse response: ${J instanceof Error?J.message:"Unknown error"}`,X.status,"PARSE_ERROR")}},H=async(X,I)=>{try{let J=await N(X,I);return await n(J)}catch(J){if(J instanceof T)throw J;throw new T(J instanceof Error?J.message:"Network request failed",0,"NETWORK_ERROR")}},y=(X=[])=>{return new Proxy({},{get(I,J){if(typeof J==="symbol")return;if(J==="query"||J==="mutation"||J==="formMutation"||J==="subscription"||J==="getStringKey"||J==="getArrayKey"||J==="getNoInputsArrayKey"){let $=X.join("/"),A=`${j}${D}/${$}`;if(M)console.log(`BRPC ${J} procedure path: ${$}`),console.log(`BRPC ${J} full URL: ${A}`);if(J==="query")return async(Z)=>{let L=await O(!0);if(M)console.log(`BRPC query request to ${$}`,{input:Z,headers:L});let _=new URL(A);if(Z&&typeof Z==="object")Object.entries(Z).forEach(([W,F])=>{if(F!==void 0)_.searchParams.append(W,String(F))});let z=await H(_.toString(),{method:"GET",headers:L,credentials:"include"});if(M)console.log(`BRPC query response from ${$}:`,z);return z};else if(J==="mutation")return async(Z)=>{let L=await O(!0);if(M)console.log(`BRPC mutation request to ${$}`,{input:Z,headers:L});let _=JSON.stringify(Z),z=await H(A,{method:"POST",headers:L,body:_,credentials:"include"});if(M)console.log(`BRPC mutation response from ${$}:`,z);return z};else if(J==="formMutation")return async(Z)=>{let L=await O(!1);if(M)console.log(`BRPC formMutation request to ${$}`,{input:Z,headers:L});let _=new FormData,z=(F,Y,C)=>{let V=C?`${C}[${F}]`:F;if(Y===null||Y===void 0)return;else if(U(Y))_.append(V,Y);else if(Array.isArray(Y))Y.forEach((E,K)=>{if(U(E))_.append(V,E);else if(typeof E==="object"&&E!==null)Object.entries(E).forEach(([i,p])=>{z(i,p,`${V}[${K}]`)});else _.append(`${V}[${K}]`,String(E))});else if(typeof Y==="object"&&Y!==null)Object.entries(Y).forEach(([E,K])=>{z(E,K,V)});else _.append(V,String(Y))};Object.entries(Z).forEach(([F,Y])=>{z(F,Y)});let W=await H(A,{method:"POST",headers:L,body:_,credentials:"include"});if(M)console.log(`BRPC formMutation response from ${$}:`,W);return W};else if(J==="subscription")return(Z)=>{if(M)console.log(`BRPC subscription to ${$}`);let L=D?`${D.slice(1)}/${$}`:$;return B.updateAuth(),B.subscribe(L,Z)};else if(J==="getStringKey")return(Z)=>{let L=$;if(!Z||Object.keys(Z).length===0)return L;let _=Object.keys(Z).sort().reduce((W,F)=>{let Y=Z[F];if(Y!==void 0&&Y!==null)W[F]=Y;return W},{});if(Object.keys(_).length===0)return L;let z=JSON.stringify(_);return`${L}?${z}`};else if(J==="getArrayKey")return(Z,L)=>{let _=$,z={...Z??{},...L??{}};if(Object.keys(z).length===0)return[_];let W=[];if(Object.keys(z).sort().forEach((Y)=>{let C=z[Y];if(C!==void 0&&C!==null){let V=C;if(typeof C==="boolean"||typeof C==="number")V=String(C);else if(typeof C==="object")V=JSON.stringify(C);W.push([Y,V])}}),W.length===0)return[_];let F=W.flatMap(([Y,C])=>[Y,C]);return[_,...F]};else if(J==="getNoInputsArrayKey")return()=>{return[$]}}return y([...X,J])}})},l=y();if(M)console.log("BRPC client created",{baseUrl:j,prefix:D,wsUrl:P});return{routes:l,storage:{getObjectUrl:(X,I)=>b(X,I,g)},utils:{updateWsAuth:async()=>await B.updateAuth(),setHeader:async(X,I)=>{if(typeof w==="function"){console.warn("Cannot use setHeader with function-based headers resolver");return}w={...w,[X]:I},await B.updateAuth()},setHeaders:async(X)=>{if(typeof w==="function"){console.warn("Cannot use setHeaders with function-based headers resolver");return}w={...w,...X},await B.updateAuth()}}}}export{b as getObjectUrl,Qq as createBrpcClient,T as BrpcClientError};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=79958F420837D37364756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mateosuarezdev/brpc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.71",
|
|
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
|
},
|
package/dist/auth/utils.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|