mirror of
https://github.com/DeBrosOfficial/network-ts-sdk.git
synced 2025-12-12 18:28:50 +00:00
Enhance WebSocket error handling and add base64 encoding/decoding utilities in PubSubClient; refactor message publishing logic to support base64 data
This commit is contained in:
parent
acf9540daa
commit
eab542952e
@ -56,7 +56,9 @@ export class WSClient {
|
|||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
this.ws?.close();
|
this.ws?.close();
|
||||||
reject(new SDKError("WebSocket connection timeout", 408, "WS_TIMEOUT"));
|
reject(
|
||||||
|
new SDKError("WebSocket connection timeout", 408, "WS_TIMEOUT")
|
||||||
|
);
|
||||||
}, this.timeout);
|
}, this.timeout);
|
||||||
|
|
||||||
this.ws.addEventListener("open", () => {
|
this.ws.addEventListener("open", () => {
|
||||||
@ -72,13 +74,9 @@ export class WSClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.ws.addEventListener("error", (event: Event) => {
|
this.ws.addEventListener("error", (event: Event) => {
|
||||||
|
console.error("[WSClient] WebSocket error:", event);
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
const error = new SDKError(
|
const error = new SDKError("WebSocket error", 500, "WS_ERROR", event);
|
||||||
"WebSocket error",
|
|
||||||
500,
|
|
||||||
"WS_ERROR",
|
|
||||||
event
|
|
||||||
);
|
|
||||||
this.errorHandlers.forEach((handler) => handler(error));
|
this.errorHandlers.forEach((handler) => handler(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -99,7 +97,7 @@ export class WSClient {
|
|||||||
|
|
||||||
private buildWSUrl(): string {
|
private buildWSUrl(): string {
|
||||||
let url = this.url;
|
let url = this.url;
|
||||||
|
|
||||||
// Always append auth token as query parameter for compatibility
|
// Always append auth token as query parameter for compatibility
|
||||||
// Works in both Node.js and browser environments
|
// Works in both Node.js and browser environments
|
||||||
if (this.authToken) {
|
if (this.authToken) {
|
||||||
@ -107,7 +105,7 @@ export class WSClient {
|
|||||||
const paramName = this.authToken.startsWith("ak_") ? "api_key" : "token";
|
const paramName = this.authToken.startsWith("ak_") ? "api_key" : "token";
|
||||||
url += `${separator}${paramName}=${encodeURIComponent(this.authToken)}`;
|
url += `${separator}${paramName}=${encodeURIComponent(this.authToken)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,11 +155,7 @@ export class WSClient {
|
|||||||
|
|
||||||
send(data: string) {
|
send(data: string) {
|
||||||
if (this.ws?.readyState !== WebSocket.OPEN) {
|
if (this.ws?.readyState !== WebSocket.OPEN) {
|
||||||
throw new SDKError(
|
throw new SDKError("WebSocket is not connected", 500, "WS_NOT_CONNECTED");
|
||||||
"WebSocket is not connected",
|
|
||||||
500,
|
|
||||||
"WS_NOT_CONNECTED"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.ws.send(data);
|
this.ws.send(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,38 @@ export interface Message {
|
|||||||
timestamp?: number;
|
timestamp?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cross-platform base64 encoding/decoding utilities
|
||||||
|
function base64Encode(str: string): string {
|
||||||
|
if (typeof Buffer !== "undefined") {
|
||||||
|
// Node.js environment
|
||||||
|
return Buffer.from(str).toString("base64");
|
||||||
|
} else if (typeof btoa !== "undefined") {
|
||||||
|
// Browser/React Native environment
|
||||||
|
return btoa(
|
||||||
|
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) =>
|
||||||
|
String.fromCharCode(parseInt(p1, 16))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw new Error("No base64 encoding method available");
|
||||||
|
}
|
||||||
|
|
||||||
|
function base64Decode(b64: string): string {
|
||||||
|
if (typeof Buffer !== "undefined") {
|
||||||
|
// Node.js environment
|
||||||
|
return Buffer.from(b64, "base64").toString("utf-8");
|
||||||
|
} else if (typeof atob !== "undefined") {
|
||||||
|
// Browser/React Native environment
|
||||||
|
const binary = atob(b64);
|
||||||
|
const bytes = new Uint8Array(binary.length);
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
bytes[i] = binary.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return new TextDecoder().decode(bytes);
|
||||||
|
}
|
||||||
|
throw new Error("No base64 decoding method available");
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageHandler = (message: Message) => void;
|
export type MessageHandler = (message: Message) => void;
|
||||||
export type ErrorHandler = (error: Error) => void;
|
export type ErrorHandler = (error: Error) => void;
|
||||||
export type CloseHandler = () => void;
|
export type CloseHandler = () => void;
|
||||||
@ -24,8 +56,15 @@ export class PubSubClient {
|
|||||||
* Publish a message to a topic.
|
* Publish a message to a topic.
|
||||||
*/
|
*/
|
||||||
async publish(topic: string, data: string | Uint8Array): Promise<void> {
|
async publish(topic: string, data: string | Uint8Array): Promise<void> {
|
||||||
const dataBase64 =
|
let dataBase64: string;
|
||||||
typeof data === "string" ? Buffer.from(data).toString("base64") : Buffer.from(data).toString("base64");
|
if (typeof data === "string") {
|
||||||
|
dataBase64 = base64Encode(data);
|
||||||
|
} else {
|
||||||
|
// Convert Uint8Array to string first
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const str = decoder.decode(data);
|
||||||
|
dataBase64 = base64Encode(str);
|
||||||
|
}
|
||||||
|
|
||||||
await this.httpClient.post("/v1/pubsub/publish", {
|
await this.httpClient.post("/v1/pubsub/publish", {
|
||||||
topic,
|
topic,
|
||||||
@ -95,13 +134,37 @@ export class Subscription {
|
|||||||
|
|
||||||
this.wsClient.onMessage((data) => {
|
this.wsClient.onMessage((data) => {
|
||||||
try {
|
try {
|
||||||
|
let messageData = data;
|
||||||
|
|
||||||
|
// Parse gateway JSON envelope: {data: base64String, timestamp, topic}
|
||||||
|
try {
|
||||||
|
const envelope = JSON.parse(data);
|
||||||
|
if (envelope.data && typeof envelope.data === "string") {
|
||||||
|
// The gateway sends base64-encoded data in the 'data' field
|
||||||
|
// Decode it back to the original string
|
||||||
|
try {
|
||||||
|
messageData = base64Decode(envelope.data);
|
||||||
|
} catch (decodeError) {
|
||||||
|
console.error(
|
||||||
|
"[Subscription] Base64 decode failed:",
|
||||||
|
decodeError
|
||||||
|
);
|
||||||
|
// If base64 decode fails, use the envelope data as-is
|
||||||
|
messageData = envelope.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
// If not JSON, use the data as-is
|
||||||
|
}
|
||||||
|
|
||||||
const message: Message = {
|
const message: Message = {
|
||||||
topic: this.topic,
|
topic: this.topic,
|
||||||
data: data,
|
data: messageData,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
};
|
};
|
||||||
this.messageHandlers.forEach((handler) => handler(message));
|
this.messageHandlers.forEach((handler) => handler(message));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("[Subscription] Error processing message:", error);
|
||||||
this.errorHandlers.forEach((handler) =>
|
this.errorHandlers.forEach((handler) =>
|
||||||
handler(error instanceof Error ? error : new Error(String(error)))
|
handler(error instanceof Error ? error : new Error(String(error)))
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user