chatly-sdk 0.0.5 → 0.0.6

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.
Files changed (56) hide show
  1. package/CONTRIBUTING.md +658 -0
  2. package/IMPROVEMENTS.md +402 -0
  3. package/README.md +1538 -164
  4. package/dist/index.d.ts +430 -9
  5. package/dist/index.js +1420 -63
  6. package/examples/01-basic-chat/README.md +61 -0
  7. package/examples/01-basic-chat/index.js +58 -0
  8. package/examples/01-basic-chat/package.json +13 -0
  9. package/examples/02-group-chat/README.md +78 -0
  10. package/examples/02-group-chat/index.js +76 -0
  11. package/examples/02-group-chat/package.json +13 -0
  12. package/examples/03-offline-messaging/README.md +73 -0
  13. package/examples/03-offline-messaging/index.js +80 -0
  14. package/examples/03-offline-messaging/package.json +13 -0
  15. package/examples/04-live-chat/README.md +80 -0
  16. package/examples/04-live-chat/index.js +114 -0
  17. package/examples/04-live-chat/package.json +13 -0
  18. package/examples/05-hybrid-messaging/README.md +71 -0
  19. package/examples/05-hybrid-messaging/index.js +106 -0
  20. package/examples/05-hybrid-messaging/package.json +13 -0
  21. package/examples/06-postgresql-integration/README.md +101 -0
  22. package/examples/06-postgresql-integration/adapters/groupStore.js +73 -0
  23. package/examples/06-postgresql-integration/adapters/messageStore.js +47 -0
  24. package/examples/06-postgresql-integration/adapters/userStore.js +40 -0
  25. package/examples/06-postgresql-integration/index.js +92 -0
  26. package/examples/06-postgresql-integration/package.json +14 -0
  27. package/examples/06-postgresql-integration/schema.sql +58 -0
  28. package/examples/08-customer-support/README.md +70 -0
  29. package/examples/08-customer-support/index.js +104 -0
  30. package/examples/08-customer-support/package.json +13 -0
  31. package/examples/README.md +105 -0
  32. package/jest.config.cjs +28 -0
  33. package/package.json +12 -8
  34. package/src/chat/ChatSession.ts +81 -0
  35. package/src/chat/GroupSession.ts +79 -0
  36. package/src/constants.ts +61 -0
  37. package/src/crypto/e2e.ts +0 -20
  38. package/src/index.ts +525 -63
  39. package/src/models/mediaTypes.ts +58 -0
  40. package/src/models/message.ts +4 -1
  41. package/src/transport/adapters.ts +51 -1
  42. package/src/transport/memoryTransport.ts +75 -13
  43. package/src/transport/websocketClient.ts +269 -21
  44. package/src/transport/websocketServer.ts +26 -26
  45. package/src/utils/errors.ts +97 -0
  46. package/src/utils/logger.ts +96 -0
  47. package/src/utils/mediaUtils.ts +235 -0
  48. package/src/utils/messageQueue.ts +162 -0
  49. package/src/utils/validation.ts +99 -0
  50. package/test/crypto.test.ts +122 -35
  51. package/test/sdk.test.ts +276 -0
  52. package/test/validation.test.ts +64 -0
  53. package/tsconfig.json +11 -10
  54. package/tsconfig.test.json +11 -0
  55. package/src/ChatManager.ts +0 -103
  56. package/src/crypto/keyManager.ts +0 -28
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Media types supported by the SDK
3
+ */
4
+ export enum MediaType {
5
+ IMAGE = 'image',
6
+ AUDIO = 'audio',
7
+ VIDEO = 'video',
8
+ DOCUMENT = 'document',
9
+ }
10
+
11
+ /**
12
+ * Media metadata
13
+ */
14
+ export interface MediaMetadata {
15
+ filename: string;
16
+ mimeType: string;
17
+ size: number;
18
+ width?: number; // For images/videos
19
+ height?: number; // For images/videos
20
+ duration?: number; // For audio/video (in seconds)
21
+ thumbnail?: string; // Base64 thumbnail for images/videos
22
+ }
23
+
24
+ /**
25
+ * Media attachment
26
+ */
27
+ export interface MediaAttachment {
28
+ type: MediaType;
29
+ data: string; // Base64 encoded file data
30
+ metadata: MediaMetadata;
31
+ }
32
+
33
+ /**
34
+ * Supported MIME types
35
+ */
36
+ export const SUPPORTED_MIME_TYPES = {
37
+ image: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
38
+ audio: ['audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/wav', 'audio/webm'],
39
+ video: ['video/mp4', 'video/webm', 'video/ogg'],
40
+ document: [
41
+ 'application/pdf',
42
+ 'application/msword',
43
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
44
+ 'application/vnd.ms-excel',
45
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
46
+ 'text/plain',
47
+ ],
48
+ };
49
+
50
+ /**
51
+ * File size limits (in bytes)
52
+ */
53
+ export const FILE_SIZE_LIMITS = {
54
+ image: 10 * 1024 * 1024, // 10 MB
55
+ audio: 16 * 1024 * 1024, // 16 MB
56
+ video: 100 * 1024 * 1024, // 100 MB
57
+ document: 100 * 1024 * 1024, // 100 MB
58
+ };
@@ -1,4 +1,6 @@
1
- export type MessageType = "text" | "system";
1
+ import type { MediaAttachment } from './mediaTypes.js';
2
+
3
+ export type MessageType = "text" | "media" | "system";
2
4
 
3
5
  export interface Message {
4
6
  id: string;
@@ -9,5 +11,6 @@ export interface Message {
9
11
  iv: string;
10
12
  timestamp: number;
11
13
  type: MessageType;
14
+ media?: MediaAttachment; // Optional media attachment
12
15
  }
13
16
 
@@ -1,7 +1,57 @@
1
- import type { Message } from "../models/message.js";
1
+ import { Message } from "../models/message.js";
2
+ import { ConnectionState } from "../constants.js";
2
3
 
4
+ /**
5
+ * Transport adapter interface for network communication
6
+ */
3
7
  export interface TransportAdapter {
8
+ /**
9
+ * Connect to the transport
10
+ * @param userId - User ID to connect as
11
+ */
4
12
  connect(userId: string): Promise<void>;
13
+
14
+ /**
15
+ * Disconnect from the transport
16
+ */
17
+ disconnect(): Promise<void>;
18
+
19
+ /**
20
+ * Reconnect to the transport
21
+ */
22
+ reconnect(): Promise<void>;
23
+
24
+ /**
25
+ * Send a message
26
+ * @param message - Message to send
27
+ */
5
28
  send(message: Message): Promise<void>;
29
+
30
+ /**
31
+ * Register a message handler
32
+ * @param handler - Function to call when a message is received
33
+ */
6
34
  onMessage(handler: (message: Message) => void): void;
35
+
36
+ /**
37
+ * Register a connection state change handler
38
+ * @param handler - Function to call when connection state changes
39
+ */
40
+ onConnectionStateChange?(handler: (state: ConnectionState) => void): void;
41
+
42
+ /**
43
+ * Register an error handler
44
+ * @param handler - Function to call when an error occurs
45
+ */
46
+ onError?(handler: (error: Error) => void): void;
47
+
48
+ /**
49
+ * Get the current connection state
50
+ */
51
+ getConnectionState(): ConnectionState;
52
+
53
+ /**
54
+ * Check if transport is connected
55
+ */
56
+ isConnected(): boolean;
7
57
  }
@@ -1,24 +1,86 @@
1
- import type { Message } from "../models/message.js";
2
- import type { TransportAdapter } from "./adapters.js";
3
-
4
- type MessageHandler = (message: Message) => void;
1
+ import { Message } from "../models/message.js";
2
+ import { TransportAdapter } from "./adapters.js";
3
+ import { ConnectionState } from "../constants.js";
5
4
 
5
+ /**
6
+ * In-memory transport for testing (no actual network communication)
7
+ */
6
8
  export class InMemoryTransport implements TransportAdapter {
7
- private handler?: MessageHandler;
8
- private connected = false;
9
+ private messageHandler: ((message: Message) => void) | null = null;
10
+ private connectionState: ConnectionState = ConnectionState.DISCONNECTED;
11
+ private stateHandler: ((state: ConnectionState) => void) | null = null;
12
+ private errorHandler: ((error: Error) => void) | null = null;
13
+
14
+ async connect(userId: string): Promise<void> {
15
+ this.connectionState = ConnectionState.CONNECTED;
16
+ if (this.stateHandler) {
17
+ this.stateHandler(this.connectionState);
18
+ }
19
+ }
20
+
21
+ async disconnect(): Promise<void> {
22
+ this.connectionState = ConnectionState.DISCONNECTED;
23
+ if (this.stateHandler) {
24
+ this.stateHandler(this.connectionState);
25
+ }
26
+ }
9
27
 
10
- async connect(_userId: string): Promise<void> {
11
- this.connected = true;
28
+ async reconnect(): Promise<void> {
29
+ this.connectionState = ConnectionState.CONNECTING;
30
+ if (this.stateHandler) {
31
+ this.stateHandler(this.connectionState);
32
+ }
33
+
34
+ // Simulate reconnection delay
35
+ await new Promise(resolve => setTimeout(resolve, 100));
36
+
37
+ this.connectionState = ConnectionState.CONNECTED;
38
+ if (this.stateHandler) {
39
+ this.stateHandler(this.connectionState);
40
+ }
12
41
  }
13
42
 
14
43
  async send(message: Message): Promise<void> {
15
- if (!this.connected) {
16
- throw new Error("Transport not connected");
44
+ // In-memory transport just echoes back the message
45
+ if (this.messageHandler) {
46
+ // Simulate async delivery
47
+ setTimeout(() => {
48
+ this.messageHandler!(message);
49
+ }, 10);
17
50
  }
18
- this.handler?.(message);
19
51
  }
20
52
 
21
- onMessage(handler: MessageHandler): void {
22
- this.handler = handler;
53
+ onMessage(handler: (message: Message) => void): void {
54
+ this.messageHandler = handler;
55
+ }
56
+
57
+ onConnectionStateChange(handler: (state: ConnectionState) => void): void {
58
+ this.stateHandler = handler;
59
+ }
60
+
61
+ onError(handler: (error: Error) => void): void {
62
+ this.errorHandler = handler;
63
+ }
64
+
65
+ getConnectionState(): ConnectionState {
66
+ return this.connectionState;
67
+ }
68
+
69
+ isConnected(): boolean {
70
+ return this.connectionState === ConnectionState.CONNECTED;
71
+ }
72
+
73
+ // Test helper to simulate receiving a message
74
+ simulateReceive(message: Message): void {
75
+ if (this.messageHandler) {
76
+ this.messageHandler(message);
77
+ }
78
+ }
79
+
80
+ // Test helper to simulate an error
81
+ simulateError(error: Error): void {
82
+ if (this.errorHandler) {
83
+ this.errorHandler(error);
84
+ }
23
85
  }
24
86
  }
@@ -1,37 +1,285 @@
1
+ import { Message } from "../models/message.js";
2
+ import { TransportAdapter } from "./adapters.js";
3
+ import {
4
+ ConnectionState,
5
+ RECONNECT_MAX_ATTEMPTS,
6
+ RECONNECT_BASE_DELAY,
7
+ RECONNECT_MAX_DELAY,
8
+ HEARTBEAT_INTERVAL,
9
+ CONNECTION_TIMEOUT,
10
+ } from "../constants.js";
11
+ import { NetworkError, TransportError } from "../utils/errors.js";
12
+ import { logger } from "../utils/logger.js";
1
13
 
2
- import WebSocket from 'ws';
3
-
4
- export class ChatClient {
14
+ export class WebSocketClient implements TransportAdapter {
5
15
  private ws: WebSocket | null = null;
6
- private messageHandlers: ((message: Buffer) => void)[] = [];
16
+ private url: string;
17
+ private messageHandler: ((message: Message) => void) | null = null;
18
+ private stateHandler: ((state: ConnectionState) => void) | null = null;
19
+ private errorHandler: ((error: Error) => void) | null = null;
20
+ private connectionState: ConnectionState = ConnectionState.DISCONNECTED;
21
+ private reconnectAttempts: number = 0;
22
+ private reconnectTimer: NodeJS.Timeout | null = null;
23
+ private heartbeatTimer: NodeJS.Timeout | null = null;
24
+ private currentUserId: string | null = null;
25
+ private shouldReconnect: boolean = true;
7
26
 
8
- constructor(private url: string) {}
27
+ constructor(url: string) {
28
+ this.url = url;
29
+ }
9
30
 
10
- connect(): void {
11
- this.ws = new WebSocket(this.url);
31
+ async connect(userId: string): Promise<void> {
32
+ this.currentUserId = userId;
33
+ this.shouldReconnect = true;
34
+ return this.doConnect();
35
+ }
12
36
 
13
- this.ws.on('open', () => {
14
- console.log('Connected to WebSocket server');
15
- });
37
+ private async doConnect(): Promise<void> {
38
+ if (this.connectionState === ConnectionState.CONNECTING) {
39
+ logger.warn('Already connecting, skipping duplicate connect attempt');
40
+ return;
41
+ }
16
42
 
17
- this.ws.on('message', (message: Buffer) => {
18
- this.messageHandlers.forEach(handler => handler(message));
19
- });
43
+ this.updateState(ConnectionState.CONNECTING);
44
+ logger.info('Connecting to WebSocket', { url: this.url, userId: this.currentUserId });
45
+
46
+ return new Promise((resolve, reject) => {
47
+ try {
48
+ const wsUrl = this.currentUserId
49
+ ? `${this.url}?userId=${this.currentUserId}`
50
+ : this.url;
51
+
52
+ this.ws = new WebSocket(wsUrl);
53
+
54
+ const connectionTimeout = setTimeout(() => {
55
+ if (this.connectionState === ConnectionState.CONNECTING) {
56
+ this.ws?.close();
57
+ const error = new NetworkError('Connection timeout');
58
+ this.handleError(error);
59
+ reject(error);
60
+ }
61
+ }, CONNECTION_TIMEOUT);
62
+
63
+ this.ws.onopen = () => {
64
+ clearTimeout(connectionTimeout);
65
+ this.reconnectAttempts = 0;
66
+ this.updateState(ConnectionState.CONNECTED);
67
+ logger.info('WebSocket connected');
68
+ this.startHeartbeat();
69
+ resolve();
70
+ };
71
+
72
+ this.ws.onmessage = (event) => {
73
+ try {
74
+ const message = JSON.parse(event.data);
75
+
76
+ // Handle pong response
77
+ if (message.type === 'pong') {
78
+ logger.debug('Received pong');
79
+ return;
80
+ }
81
+
82
+ if (this.messageHandler) {
83
+ this.messageHandler(message);
84
+ }
85
+ } catch (error) {
86
+ const parseError = new TransportError(
87
+ 'Failed to parse message',
88
+ false,
89
+ { error: error instanceof Error ? error.message : String(error) }
90
+ );
91
+ logger.error('Message parse error', parseError);
92
+ this.handleError(parseError);
93
+ }
94
+ };
95
+
96
+ this.ws.onerror = (event) => {
97
+ clearTimeout(connectionTimeout);
98
+ const error = new NetworkError('WebSocket error', {
99
+ event: event.type,
100
+ });
101
+ logger.error('WebSocket error', error);
102
+ this.handleError(error);
103
+ reject(error);
104
+ };
105
+
106
+ this.ws.onclose = (event) => {
107
+ clearTimeout(connectionTimeout);
108
+ this.stopHeartbeat();
109
+ logger.info('WebSocket closed', {
110
+ code: event.code,
111
+ reason: event.reason,
112
+ wasClean: event.wasClean,
113
+ });
20
114
 
21
- this.ws.on('close', () => {
22
- console.log('Disconnected from WebSocket server');
115
+ if (this.connectionState !== ConnectionState.DISCONNECTED) {
116
+ this.updateState(ConnectionState.DISCONNECTED);
117
+
118
+ // Attempt reconnection if not manually disconnected
119
+ if (this.shouldReconnect && this.reconnectAttempts < RECONNECT_MAX_ATTEMPTS) {
120
+ this.scheduleReconnect();
121
+ } else if (this.reconnectAttempts >= RECONNECT_MAX_ATTEMPTS) {
122
+ this.updateState(ConnectionState.FAILED);
123
+ const error = new NetworkError('Max reconnection attempts exceeded');
124
+ this.handleError(error);
125
+ }
126
+ }
127
+ };
128
+ } catch (error) {
129
+ const connectError = new NetworkError(
130
+ 'Failed to create WebSocket connection',
131
+ { error: error instanceof Error ? error.message : String(error) }
132
+ );
133
+ logger.error('Connection error', connectError);
134
+ this.handleError(connectError);
135
+ reject(connectError);
136
+ }
23
137
  });
24
138
  }
25
139
 
26
- sendMessage(message: string | Buffer): void {
27
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
28
- this.ws.send(message);
140
+ async disconnect(): Promise<void> {
141
+ logger.info('Disconnecting WebSocket');
142
+ this.shouldReconnect = false;
143
+ this.clearReconnectTimer();
144
+ this.stopHeartbeat();
145
+
146
+ if (this.ws) {
147
+ this.ws.close(1000, 'Client disconnect');
148
+ this.ws = null;
149
+ }
150
+
151
+ this.updateState(ConnectionState.DISCONNECTED);
152
+ }
153
+
154
+ async reconnect(): Promise<void> {
155
+ logger.info('Manual reconnect requested');
156
+ this.reconnectAttempts = 0;
157
+ this.shouldReconnect = true;
158
+ await this.disconnect();
159
+
160
+ if (this.currentUserId) {
161
+ await this.doConnect();
29
162
  } else {
30
- console.error('WebSocket is not connected.');
163
+ throw new TransportError('Cannot reconnect: no user ID set');
31
164
  }
32
165
  }
33
166
 
34
- onMessage(handler: (message: Buffer) => void): void {
35
- this.messageHandlers.push(handler);
167
+ private scheduleReconnect(): void {
168
+ this.clearReconnectTimer();
169
+ this.reconnectAttempts++;
170
+
171
+ // Exponential backoff with jitter
172
+ const delay = Math.min(
173
+ RECONNECT_BASE_DELAY * Math.pow(2, this.reconnectAttempts - 1),
174
+ RECONNECT_MAX_DELAY
175
+ );
176
+ const jitter = Math.random() * 1000;
177
+ const totalDelay = delay + jitter;
178
+
179
+ logger.info('Scheduling reconnect', {
180
+ attempt: this.reconnectAttempts,
181
+ delay: totalDelay,
182
+ });
183
+
184
+ this.updateState(ConnectionState.RECONNECTING);
185
+
186
+ this.reconnectTimer = setTimeout(async () => {
187
+ try {
188
+ await this.doConnect();
189
+ } catch (error) {
190
+ logger.error('Reconnect failed', error);
191
+ }
192
+ }, totalDelay);
193
+ }
194
+
195
+ private clearReconnectTimer(): void {
196
+ if (this.reconnectTimer) {
197
+ clearTimeout(this.reconnectTimer);
198
+ this.reconnectTimer = null;
199
+ }
200
+ }
201
+
202
+ private startHeartbeat(): void {
203
+ this.stopHeartbeat();
204
+
205
+ this.heartbeatTimer = setInterval(() => {
206
+ if (this.isConnected()) {
207
+ try {
208
+ this.ws?.send(JSON.stringify({ type: 'ping' }));
209
+ logger.debug('Sent ping');
210
+ } catch (error) {
211
+ logger.error('Failed to send heartbeat', error);
212
+ }
213
+ }
214
+ }, HEARTBEAT_INTERVAL);
215
+ }
216
+
217
+ private stopHeartbeat(): void {
218
+ if (this.heartbeatTimer) {
219
+ clearInterval(this.heartbeatTimer);
220
+ this.heartbeatTimer = null;
221
+ }
222
+ }
223
+
224
+ async send(message: Message): Promise<void> {
225
+ if (!this.isConnected() || !this.ws) {
226
+ throw new NetworkError('WebSocket not connected');
227
+ }
228
+
229
+ try {
230
+ this.ws.send(JSON.stringify(message));
231
+ logger.debug('Message sent', { messageId: message.id });
232
+ } catch (error) {
233
+ const sendError = new NetworkError(
234
+ 'Failed to send message',
235
+ { error: error instanceof Error ? error.message : String(error) }
236
+ );
237
+ logger.error('Send error', sendError);
238
+ throw sendError;
239
+ }
240
+ }
241
+
242
+ onMessage(handler: (message: Message) => void): void {
243
+ this.messageHandler = handler;
244
+ }
245
+
246
+ onConnectionStateChange(handler: (state: ConnectionState) => void): void {
247
+ this.stateHandler = handler;
248
+ }
249
+
250
+ onError(handler: (error: Error) => void): void {
251
+ this.errorHandler = handler;
252
+ }
253
+
254
+ getConnectionState(): ConnectionState {
255
+ return this.connectionState;
256
+ }
257
+
258
+ isConnected(): boolean {
259
+ return (
260
+ this.connectionState === ConnectionState.CONNECTED &&
261
+ this.ws?.readyState === WebSocket.OPEN
262
+ );
263
+ }
264
+
265
+ private updateState(newState: ConnectionState): void {
266
+ if (this.connectionState !== newState) {
267
+ const oldState = this.connectionState;
268
+ this.connectionState = newState;
269
+ logger.info('Connection state changed', {
270
+ from: oldState,
271
+ to: newState,
272
+ });
273
+
274
+ if (this.stateHandler) {
275
+ this.stateHandler(newState);
276
+ }
277
+ }
278
+ }
279
+
280
+ private handleError(error: Error): void {
281
+ if (this.errorHandler) {
282
+ this.errorHandler(error);
283
+ }
36
284
  }
37
285
  }
@@ -1,33 +1,33 @@
1
1
 
2
- import { WebSocketServer, WebSocket } from 'ws';
2
+ // import { WebSocketServer, WebSocket } from 'ws';
3
3
 
4
- export class ChatServer {
5
- private wss: WebSocketServer;
4
+ // export class ChatServer {
5
+ // private wss: WebSocketServer;
6
6
 
7
- constructor(port: number) {
8
- this.wss = new WebSocketServer({ port });
9
- this.initialize();
10
- }
7
+ // constructor(port: number) {
8
+ // this.wss = new WebSocketServer({ port });
9
+ // this.initialize();
10
+ // }
11
11
 
12
- private initialize(): void {
13
- this.wss.on('connection', (ws: WebSocket) => {
14
- console.log('Client connected');
12
+ // private initialize(): void {
13
+ // this.wss.on('connection', (ws: WebSocket) => {
14
+ // console.log('Client connected');
15
15
 
16
- ws.on('message', (message: Buffer) => {
17
- console.log('Received message:', message.toString());
18
- // Broadcast the message to all other clients
19
- this.wss.clients.forEach((client) => {
20
- if (client !== ws && client.readyState === WebSocket.OPEN) {
21
- client.send(message);
22
- }
23
- });
24
- });
16
+ // ws.on('message', (message: Buffer) => {
17
+ // console.log('Received message:', message.toString());
18
+ // // Broadcast the message to all other clients
19
+ // this.wss.clients.forEach((client) => {
20
+ // if (client !== ws && client.readyState === WebSocket.OPEN) {
21
+ // client.send(message);
22
+ // }
23
+ // });
24
+ // });
25
25
 
26
- ws.on('close', () => {
27
- console.log('Client disconnected');
28
- });
29
- });
26
+ // ws.on('close', () => {
27
+ // console.log('Client disconnected');
28
+ // });
29
+ // });
30
30
 
31
- console.log(`WebSocket server started on port ${this.wss.options.port}`);
32
- }
33
- }
31
+ // console.log(`WebSocket server started on port ${this.wss.options.port}`);
32
+ // }
33
+ // }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Base SDK Error class
3
+ */
4
+ export class SDKError extends Error {
5
+ constructor(
6
+ message: string,
7
+ public readonly code: string,
8
+ public readonly retryable: boolean = false,
9
+ public readonly details?: Record<string, unknown>
10
+ ) {
11
+ super(message);
12
+ this.name = this.constructor.name;
13
+ Error.captureStackTrace(this, this.constructor);
14
+ }
15
+
16
+ toJSON() {
17
+ return {
18
+ name: this.name,
19
+ message: this.message,
20
+ code: this.code,
21
+ retryable: this.retryable,
22
+ details: this.details,
23
+ };
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Network-related errors (connection, timeout, etc.)
29
+ */
30
+ export class NetworkError extends SDKError {
31
+ constructor(message: string, details?: Record<string, unknown>) {
32
+ super(message, 'NETWORK_ERROR', true, details);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Encryption/Decryption errors
38
+ */
39
+ export class EncryptionError extends SDKError {
40
+ constructor(message: string, details?: Record<string, unknown>) {
41
+ super(message, 'ENCRYPTION_ERROR', false, details);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Authentication/Authorization errors
47
+ */
48
+ export class AuthError extends SDKError {
49
+ constructor(message: string, details?: Record<string, unknown>) {
50
+ super(message, 'AUTH_ERROR', false, details);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Validation errors (invalid input)
56
+ */
57
+ export class ValidationError extends SDKError {
58
+ constructor(message: string, details?: Record<string, unknown>) {
59
+ super(message, 'VALIDATION_ERROR', false, details);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Storage-related errors
65
+ */
66
+ export class StorageError extends SDKError {
67
+ constructor(message: string, retryable: boolean = true, details?: Record<string, unknown>) {
68
+ super(message, 'STORAGE_ERROR', retryable, details);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Session-related errors
74
+ */
75
+ export class SessionError extends SDKError {
76
+ constructor(message: string, details?: Record<string, unknown>) {
77
+ super(message, 'SESSION_ERROR', false, details);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Transport-related errors
83
+ */
84
+ export class TransportError extends SDKError {
85
+ constructor(message: string, retryable: boolean = true, details?: Record<string, unknown>) {
86
+ super(message, 'TRANSPORT_ERROR', retryable, details);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Configuration errors
92
+ */
93
+ export class ConfigError extends SDKError {
94
+ constructor(message: string, details?: Record<string, unknown>) {
95
+ super(message, 'CONFIG_ERROR', false, details);
96
+ }
97
+ }