@debros/network-ts-sdk 0.4.2 → 0.6.0

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,4 @@
1
+ export { PathBasedAuthStrategy } from "./AuthHeaderStrategy";
2
+ export { ExponentialBackoffRetryPolicy } from "./RequestRetryPolicy";
3
+ export { RequestLogger } from "./RequestLogger";
4
+ export { TLSConfiguration } from "./TLSConfiguration";
package/src/core/ws.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  import WebSocket from "isomorphic-ws";
2
2
  import { SDKError } from "../errors";
3
+ import { NetworkErrorCallback } from "./http";
3
4
 
4
5
  export interface WSClientConfig {
5
6
  wsURL: string;
6
7
  timeout?: number;
7
8
  authToken?: string;
8
9
  WebSocket?: typeof WebSocket;
10
+ /**
11
+ * Callback invoked on WebSocket errors.
12
+ * Use this to trigger gateway failover at the application layer.
13
+ */
14
+ onNetworkError?: NetworkErrorCallback;
9
15
  }
10
16
 
11
17
  export type WSMessageHandler = (data: string) => void;
@@ -23,6 +29,7 @@ export class WSClient {
23
29
  private timeout: number;
24
30
  private authToken?: string;
25
31
  private WebSocketClass: typeof WebSocket;
32
+ private onNetworkError?: NetworkErrorCallback;
26
33
 
27
34
  private ws?: WebSocket;
28
35
  private messageHandlers: Set<WSMessageHandler> = new Set();
@@ -36,6 +43,14 @@ export class WSClient {
36
43
  this.timeout = config.timeout ?? 30000;
37
44
  this.authToken = config.authToken;
38
45
  this.WebSocketClass = config.WebSocket ?? WebSocket;
46
+ this.onNetworkError = config.onNetworkError;
47
+ }
48
+
49
+ /**
50
+ * Set the network error callback
51
+ */
52
+ setOnNetworkError(callback: NetworkErrorCallback | undefined): void {
53
+ this.onNetworkError = callback;
39
54
  }
40
55
 
41
56
  /**
@@ -57,9 +72,19 @@ export class WSClient {
57
72
 
58
73
  const timeout = setTimeout(() => {
59
74
  this.ws?.close();
60
- reject(
61
- new SDKError("WebSocket connection timeout", 408, "WS_TIMEOUT")
62
- );
75
+ const error = new SDKError("WebSocket connection timeout", 408, "WS_TIMEOUT");
76
+
77
+ // Call the network error callback if configured
78
+ if (this.onNetworkError) {
79
+ this.onNetworkError(error, {
80
+ method: "WS",
81
+ path: this.wsURL,
82
+ isRetry: false,
83
+ attempt: 0,
84
+ });
85
+ }
86
+
87
+ reject(error);
63
88
  }, this.timeout);
64
89
 
65
90
  this.ws.addEventListener("open", () => {
@@ -78,6 +103,17 @@ export class WSClient {
78
103
  console.error("[WSClient] WebSocket error:", event);
79
104
  clearTimeout(timeout);
80
105
  const error = new SDKError("WebSocket error", 500, "WS_ERROR", event);
106
+
107
+ // Call the network error callback if configured
108
+ if (this.onNetworkError) {
109
+ this.onNetworkError(error, {
110
+ method: "WS",
111
+ path: this.wsURL,
112
+ isRetry: false,
113
+ attempt: 0,
114
+ });
115
+ }
116
+
81
117
  this.errorHandlers.forEach((handler) => handler(error));
82
118
  reject(error);
83
119
  });
@@ -0,0 +1,13 @@
1
+ export { DBClient } from "./client";
2
+ export { QueryBuilder } from "./qb";
3
+ export { Repository } from "./repository";
4
+ export type {
5
+ Entity,
6
+ QueryResponse,
7
+ TransactionOp,
8
+ TransactionRequest,
9
+ SelectOptions,
10
+ FindOptions,
11
+ ColumnDefinition,
12
+ } from "./types";
13
+ export { extractTableName, extractPrimaryKey } from "./types";
@@ -0,0 +1,2 @@
1
+ export { FunctionsClient, type FunctionsClientConfig } from "./client";
2
+ export type { FunctionResponse, SuccessResponse } from "./types";
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HttpClient, HttpClientConfig } from "./core/http";
1
+ import { HttpClient, HttpClientConfig, NetworkErrorCallback } from "./core/http";
2
2
  import { AuthClient } from "./auth/client";
3
3
  import { DBClient } from "./db/client";
4
4
  import { PubSubClient } from "./pubsub/client";
@@ -20,6 +20,11 @@ export interface ClientConfig extends Omit<HttpClientConfig, "fetch"> {
20
20
  wsConfig?: Partial<Omit<WSClientConfig, "wsURL">>;
21
21
  functionsConfig?: FunctionsClientConfig;
22
22
  fetch?: typeof fetch;
23
+ /**
24
+ * Callback invoked on network errors (HTTP and WebSocket).
25
+ * Use this to trigger gateway failover at the application layer.
26
+ */
27
+ onNetworkError?: NetworkErrorCallback;
23
28
  }
24
29
 
25
30
  export interface Client {
@@ -38,7 +43,9 @@ export function createClient(config: ClientConfig): Client {
38
43
  timeout: config.timeout,
39
44
  maxRetries: config.maxRetries,
40
45
  retryDelayMs: config.retryDelayMs,
46
+ debug: config.debug,
41
47
  fetch: config.fetch,
48
+ onNetworkError: config.onNetworkError,
42
49
  });
43
50
 
44
51
  const auth = new AuthClient({
@@ -55,6 +62,7 @@ export function createClient(config: ClientConfig): Client {
55
62
  const pubsub = new PubSubClient(httpClient, {
56
63
  ...config.wsConfig,
57
64
  wsURL,
65
+ onNetworkError: config.onNetworkError,
58
66
  });
59
67
  const network = new NetworkClient(httpClient);
60
68
  const cache = new CacheClient(httpClient);
@@ -73,6 +81,7 @@ export function createClient(config: ClientConfig): Client {
73
81
  }
74
82
 
75
83
  export { HttpClient } from "./core/http";
84
+ export type { NetworkErrorCallback, NetworkErrorContext } from "./core/http";
76
85
  export { WSClient } from "./core/ws";
77
86
  export { AuthClient } from "./auth/client";
78
87
  export { DBClient } from "./db/client";
@@ -0,0 +1,7 @@
1
+ export { NetworkClient } from "./client";
2
+ export type {
3
+ PeerInfo,
4
+ NetworkStatus,
5
+ ProxyRequest,
6
+ ProxyResponse,
7
+ } from "./client";
@@ -0,0 +1,12 @@
1
+ export { PubSubClient, Subscription } from "./client";
2
+ export type {
3
+ PubSubMessage,
4
+ RawEnvelope,
5
+ MessageHandler,
6
+ ErrorHandler,
7
+ CloseHandler,
8
+ PresenceMember,
9
+ PresenceResponse,
10
+ PresenceOptions,
11
+ SubscribeOptions,
12
+ } from "./types";
@@ -0,0 +1,7 @@
1
+ export { StorageClient } from "./client";
2
+ export type {
3
+ StorageUploadResponse,
4
+ StoragePinRequest,
5
+ StoragePinResponse,
6
+ StorageStatus,
7
+ } from "./client";
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Base64 Codec for cross-platform encoding/decoding
3
+ * Works in both Node.js and browser environments
4
+ */
5
+ export class Base64Codec {
6
+ /**
7
+ * Encode string or Uint8Array to base64
8
+ */
9
+ static encode(input: string | Uint8Array): string {
10
+ if (typeof input === "string") {
11
+ return this.encodeString(input);
12
+ }
13
+ return this.encodeBytes(input);
14
+ }
15
+
16
+ /**
17
+ * Encode string to base64
18
+ */
19
+ static encodeString(str: string): string {
20
+ if (this.isNode()) {
21
+ return Buffer.from(str).toString("base64");
22
+ }
23
+ // Browser
24
+ return btoa(
25
+ encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) =>
26
+ String.fromCharCode(parseInt(p1, 16))
27
+ )
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Encode Uint8Array to base64
33
+ */
34
+ static encodeBytes(bytes: Uint8Array): string {
35
+ if (this.isNode()) {
36
+ return Buffer.from(bytes).toString("base64");
37
+ }
38
+ // Browser
39
+ let binary = "";
40
+ for (let i = 0; i < bytes.length; i++) {
41
+ binary += String.fromCharCode(bytes[i]);
42
+ }
43
+ return btoa(binary);
44
+ }
45
+
46
+ /**
47
+ * Decode base64 to string
48
+ */
49
+ static decode(b64: string): string {
50
+ if (this.isNode()) {
51
+ return Buffer.from(b64, "base64").toString("utf-8");
52
+ }
53
+ // Browser
54
+ const binary = atob(b64);
55
+ const bytes = new Uint8Array(binary.length);
56
+ for (let i = 0; i < binary.length; i++) {
57
+ bytes[i] = binary.charCodeAt(i);
58
+ }
59
+ return new TextDecoder().decode(bytes);
60
+ }
61
+
62
+ /**
63
+ * Check if running in Node.js environment
64
+ */
65
+ private static isNode(): boolean {
66
+ return typeof Buffer !== "undefined";
67
+ }
68
+ }
@@ -0,0 +1,3 @@
1
+ export { Base64Codec } from "./codec";
2
+ export { retryWithBackoff, type RetryConfig } from "./retry";
3
+ export { Platform } from "./platform";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Platform detection utilities
3
+ * Helps determine runtime environment (Node.js vs Browser)
4
+ */
5
+ export const Platform = {
6
+ /**
7
+ * Check if running in Node.js
8
+ */
9
+ isNode: (): boolean => {
10
+ return typeof process !== "undefined" && !!process.versions?.node;
11
+ },
12
+
13
+ /**
14
+ * Check if running in browser
15
+ */
16
+ isBrowser: (): boolean => {
17
+ return typeof window !== "undefined";
18
+ },
19
+
20
+ /**
21
+ * Check if localStorage is available
22
+ */
23
+ hasLocalStorage: (): boolean => {
24
+ try {
25
+ return typeof localStorage !== "undefined" && localStorage !== null;
26
+ } catch {
27
+ return false;
28
+ }
29
+ },
30
+
31
+ /**
32
+ * Check if Buffer is available (Node.js)
33
+ */
34
+ hasBuffer: (): boolean => {
35
+ return typeof Buffer !== "undefined";
36
+ },
37
+
38
+ /**
39
+ * Check if btoa/atob are available (Browser)
40
+ */
41
+ hasBase64: (): boolean => {
42
+ return typeof btoa !== "undefined" && typeof atob !== "undefined";
43
+ },
44
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Retry configuration
3
+ */
4
+ export interface RetryConfig {
5
+ /**
6
+ * Maximum number of retry attempts
7
+ */
8
+ maxAttempts: number;
9
+
10
+ /**
11
+ * Function to calculate backoff delay in milliseconds
12
+ */
13
+ backoffMs: (attempt: number) => number;
14
+
15
+ /**
16
+ * Function to determine if error should trigger retry
17
+ */
18
+ shouldRetry: (error: any) => boolean;
19
+ }
20
+
21
+ /**
22
+ * Retry an operation with exponential backoff
23
+ * @param operation - The async operation to retry
24
+ * @param config - Retry configuration
25
+ * @returns Promise resolving to operation result
26
+ * @throws Last error if all retries exhausted
27
+ */
28
+ export async function retryWithBackoff<T>(
29
+ operation: () => Promise<T>,
30
+ config: RetryConfig
31
+ ): Promise<T> {
32
+ let lastError: Error | null = null;
33
+
34
+ for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
35
+ try {
36
+ return await operation();
37
+ } catch (error: any) {
38
+ lastError = error instanceof Error ? error : new Error(String(error));
39
+
40
+ // Check if we should retry this error
41
+ if (!config.shouldRetry(error)) {
42
+ throw error;
43
+ }
44
+
45
+ // If this was the last attempt, throw
46
+ if (attempt === config.maxAttempts) {
47
+ throw error;
48
+ }
49
+
50
+ // Wait before next attempt
51
+ const delay = config.backoffMs(attempt);
52
+ await new Promise((resolve) => setTimeout(resolve, delay));
53
+ }
54
+ }
55
+
56
+ // Fallback (should never reach here)
57
+ throw lastError || new Error("Retry failed");
58
+ }