@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.
- package/LICENSE +1 -1
- package/README.md +1 -0
- package/dist/index.d.ts +60 -16
- package/dist/index.js +82 -11
- package/dist/index.js.map +1 -1
- package/package.json +26 -3
- package/src/auth/index.ts +3 -0
- package/src/cache/index.ts +14 -0
- package/src/core/http.ts +101 -6
- package/src/core/index.ts +10 -0
- package/src/core/interfaces/IAuthStrategy.ts +28 -0
- package/src/core/interfaces/IHttpTransport.ts +73 -0
- package/src/core/interfaces/IRetryPolicy.ts +20 -0
- package/src/core/interfaces/IWebSocketClient.ts +60 -0
- package/src/core/interfaces/index.ts +4 -0
- package/src/core/transport/AuthHeaderStrategy.ts +108 -0
- package/src/core/transport/RequestLogger.ts +116 -0
- package/src/core/transport/RequestRetryPolicy.ts +53 -0
- package/src/core/transport/TLSConfiguration.ts +53 -0
- package/src/core/transport/index.ts +4 -0
- package/src/core/ws.ts +39 -3
- package/src/db/index.ts +13 -0
- package/src/functions/index.ts +2 -0
- package/src/index.ts +10 -1
- package/src/network/index.ts +7 -0
- package/src/pubsub/index.ts +12 -0
- package/src/storage/index.ts +7 -0
- package/src/utils/codec.ts +68 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/platform.ts +44 -0
- package/src/utils/retry.ts +58 -0
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
|
-
|
|
61
|
-
|
|
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
|
});
|
package/src/db/index.ts
ADDED
|
@@ -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";
|
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,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,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
|
+
}
|