@quilibrium/quorum-shared 2.1.0-1

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 (51) hide show
  1. package/dist/index.d.mts +2414 -0
  2. package/dist/index.d.ts +2414 -0
  3. package/dist/index.js +2788 -0
  4. package/dist/index.mjs +2678 -0
  5. package/package.json +49 -0
  6. package/src/api/client.ts +86 -0
  7. package/src/api/endpoints.ts +87 -0
  8. package/src/api/errors.ts +179 -0
  9. package/src/api/index.ts +35 -0
  10. package/src/crypto/encryption-state.ts +249 -0
  11. package/src/crypto/index.ts +55 -0
  12. package/src/crypto/types.ts +307 -0
  13. package/src/crypto/wasm-provider.ts +298 -0
  14. package/src/hooks/index.ts +31 -0
  15. package/src/hooks/keys.ts +62 -0
  16. package/src/hooks/mutations/index.ts +15 -0
  17. package/src/hooks/mutations/useDeleteMessage.ts +67 -0
  18. package/src/hooks/mutations/useEditMessage.ts +87 -0
  19. package/src/hooks/mutations/useReaction.ts +163 -0
  20. package/src/hooks/mutations/useSendMessage.ts +131 -0
  21. package/src/hooks/useChannels.ts +49 -0
  22. package/src/hooks/useMessages.ts +77 -0
  23. package/src/hooks/useSpaces.ts +60 -0
  24. package/src/index.ts +32 -0
  25. package/src/signing/index.ts +10 -0
  26. package/src/signing/types.ts +83 -0
  27. package/src/signing/wasm-provider.ts +75 -0
  28. package/src/storage/adapter.ts +118 -0
  29. package/src/storage/index.ts +9 -0
  30. package/src/sync/index.ts +83 -0
  31. package/src/sync/service.test.ts +822 -0
  32. package/src/sync/service.ts +947 -0
  33. package/src/sync/types.ts +267 -0
  34. package/src/sync/utils.ts +588 -0
  35. package/src/transport/browser-websocket.ts +299 -0
  36. package/src/transport/index.ts +34 -0
  37. package/src/transport/rn-websocket.ts +321 -0
  38. package/src/transport/types.ts +56 -0
  39. package/src/transport/websocket.ts +212 -0
  40. package/src/types/bookmark.ts +29 -0
  41. package/src/types/conversation.ts +25 -0
  42. package/src/types/index.ts +57 -0
  43. package/src/types/message.ts +178 -0
  44. package/src/types/space.ts +75 -0
  45. package/src/types/user.ts +72 -0
  46. package/src/utils/encoding.ts +106 -0
  47. package/src/utils/formatting.ts +139 -0
  48. package/src/utils/index.ts +9 -0
  49. package/src/utils/logger.ts +141 -0
  50. package/src/utils/mentions.ts +135 -0
  51. package/src/utils/validation.ts +84 -0
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Browser/Electron WebSocket client implementation
3
+ *
4
+ * Uses the native WebSocket API available in browsers and Electron.
5
+ * Implements automatic reconnection, dual queue system, and subscription management.
6
+ */
7
+
8
+ import type {
9
+ WebSocketClient,
10
+ WebSocketClientOptions,
11
+ WebSocketConnectionState,
12
+ EncryptedWebSocketMessage,
13
+ MessageHandler,
14
+ StateChangeHandler,
15
+ ErrorHandler,
16
+ } from './websocket';
17
+
18
+ /**
19
+ * BrowserWebSocketClient - WebSocket implementation for browser/Electron
20
+ *
21
+ * Features:
22
+ * - Automatic reconnection with configurable interval
23
+ * - Dual queue system (inbound messages, outbound message generators)
24
+ * - Inbox address subscription management
25
+ * - Periodic queue processing
26
+ */
27
+ export class BrowserWebSocketClient implements WebSocketClient {
28
+ private url: string;
29
+ private reconnectInterval: number;
30
+ private maxReconnectAttempts: number;
31
+ private queueProcessInterval: number;
32
+
33
+ private ws: WebSocket | null = null;
34
+ private _state: WebSocketConnectionState = 'disconnected';
35
+ private reconnectAttempts = 0;
36
+ private shouldReconnect = true;
37
+
38
+ // Queue system
39
+ private inboundQueue: EncryptedWebSocketMessage[] = [];
40
+ private outboundQueue: Array<() => Promise<string[]>> = [];
41
+ private isProcessing = false;
42
+ private processIntervalId: ReturnType<typeof setInterval> | null = null;
43
+
44
+ // Handlers
45
+ private messageHandler: MessageHandler | null = null;
46
+ private resubscribeHandler: (() => Promise<void>) | null = null;
47
+ private stateChangeHandlers: Set<StateChangeHandler> = new Set();
48
+ private errorHandlers: Set<ErrorHandler> = new Set();
49
+
50
+ constructor(options: WebSocketClientOptions) {
51
+ this.url = options.url;
52
+ this.reconnectInterval = options.reconnectInterval ?? 1000;
53
+ this.maxReconnectAttempts = options.maxReconnectAttempts ?? Infinity;
54
+ this.queueProcessInterval = options.queueProcessInterval ?? 1000;
55
+ }
56
+
57
+ get state(): WebSocketConnectionState {
58
+ return this._state;
59
+ }
60
+
61
+ get isConnected(): boolean {
62
+ return this._state === 'connected';
63
+ }
64
+
65
+ private setState(newState: WebSocketConnectionState): void {
66
+ if (this._state !== newState) {
67
+ this._state = newState;
68
+ this.stateChangeHandlers.forEach((handler) => {
69
+ try {
70
+ handler(newState);
71
+ } catch (error) {
72
+ console.error('Error in state change handler:', error);
73
+ }
74
+ });
75
+ }
76
+ }
77
+
78
+ private emitError(error: Error): void {
79
+ this.errorHandlers.forEach((handler) => {
80
+ try {
81
+ handler(error);
82
+ } catch (e) {
83
+ console.error('Error in error handler:', e);
84
+ }
85
+ });
86
+ }
87
+
88
+ async connect(): Promise<void> {
89
+ if (this._state === 'connected' || this._state === 'connecting') {
90
+ return;
91
+ }
92
+
93
+ this.shouldReconnect = true;
94
+ return this.doConnect();
95
+ }
96
+
97
+ private doConnect(): Promise<void> {
98
+ return new Promise((resolve, reject) => {
99
+ this.setState('connecting');
100
+
101
+ try {
102
+ this.ws = new WebSocket(this.url);
103
+
104
+ this.ws.onopen = () => {
105
+ this.reconnectAttempts = 0;
106
+ this.setState('connected');
107
+ this.startQueueProcessing();
108
+
109
+ // Call resubscribe handler to restore subscriptions
110
+ if (this.resubscribeHandler) {
111
+ this.resubscribeHandler().catch((error) => {
112
+ console.error('Error in resubscribe handler:', error);
113
+ });
114
+ }
115
+
116
+ // Process any pending outbound messages
117
+ this.processQueues();
118
+ resolve();
119
+ };
120
+
121
+ this.ws.onclose = () => {
122
+ this.setState('disconnected');
123
+ this.stopQueueProcessing();
124
+
125
+ if (this.shouldReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
126
+ this.reconnectAttempts++;
127
+ this.setState('reconnecting');
128
+ setTimeout(() => this.doConnect(), this.reconnectInterval);
129
+ }
130
+ };
131
+
132
+ this.ws.onerror = (event) => {
133
+ const error = new Error('WebSocket error');
134
+ this.emitError(error);
135
+ reject(error);
136
+ };
137
+
138
+ this.ws.onmessage = (event) => {
139
+ try {
140
+ const message = JSON.parse(event.data) as EncryptedWebSocketMessage;
141
+ this.inboundQueue.push(message);
142
+ this.processQueues();
143
+ } catch (error) {
144
+ console.error('Failed to parse WebSocket message:', error);
145
+ }
146
+ };
147
+ } catch (error) {
148
+ this.setState('disconnected');
149
+ reject(error);
150
+ }
151
+ });
152
+ }
153
+
154
+ disconnect(): void {
155
+ this.shouldReconnect = false;
156
+ this.stopQueueProcessing();
157
+
158
+ if (this.ws) {
159
+ this.ws.close();
160
+ this.ws = null;
161
+ }
162
+
163
+ this.setState('disconnected');
164
+ }
165
+
166
+ async send(message: string): Promise<void> {
167
+ if (this.ws?.readyState === WebSocket.OPEN) {
168
+ this.ws.send(message);
169
+ } else {
170
+ // Queue the message for later
171
+ this.outboundQueue.push(async () => [message]);
172
+ }
173
+ }
174
+
175
+ enqueueOutbound(prepareMessage: () => Promise<string[]>): void {
176
+ this.outboundQueue.push(prepareMessage);
177
+ this.processQueues();
178
+ }
179
+
180
+ async subscribe(inboxAddresses: string[]): Promise<void> {
181
+ const message = JSON.stringify({
182
+ type: 'listen',
183
+ inbox_addresses: inboxAddresses,
184
+ });
185
+ await this.send(message);
186
+ }
187
+
188
+ async unsubscribe(inboxAddresses: string[]): Promise<void> {
189
+ const message = JSON.stringify({
190
+ type: 'unlisten',
191
+ inbox_addresses: inboxAddresses,
192
+ });
193
+ await this.send(message);
194
+ }
195
+
196
+ setMessageHandler(handler: MessageHandler): void {
197
+ this.messageHandler = handler;
198
+ this.processQueues();
199
+ }
200
+
201
+ setResubscribeHandler(handler: () => Promise<void>): void {
202
+ this.resubscribeHandler = handler;
203
+ }
204
+
205
+ onStateChange(handler: StateChangeHandler): () => void {
206
+ this.stateChangeHandlers.add(handler);
207
+ return () => {
208
+ this.stateChangeHandlers.delete(handler);
209
+ };
210
+ }
211
+
212
+ onError(handler: ErrorHandler): () => void {
213
+ this.errorHandlers.add(handler);
214
+ return () => {
215
+ this.errorHandlers.delete(handler);
216
+ };
217
+ }
218
+
219
+ private startQueueProcessing(): void {
220
+ if (this.processIntervalId === null) {
221
+ this.processIntervalId = setInterval(() => {
222
+ this.processQueues();
223
+ }, this.queueProcessInterval);
224
+ }
225
+ }
226
+
227
+ private stopQueueProcessing(): void {
228
+ if (this.processIntervalId !== null) {
229
+ clearInterval(this.processIntervalId);
230
+ this.processIntervalId = null;
231
+ }
232
+ }
233
+
234
+ private async processQueues(): Promise<void> {
235
+ if (this.isProcessing) {
236
+ return;
237
+ }
238
+
239
+ this.isProcessing = true;
240
+
241
+ try {
242
+ // Process inbound messages
243
+ if (this.messageHandler) {
244
+ // Group messages by inbox address for efficient processing
245
+ const inboxMap = new Map<string, EncryptedWebSocketMessage[]>();
246
+
247
+ while (this.inboundQueue.length > 0) {
248
+ const message = this.inboundQueue.shift()!;
249
+ const existing = inboxMap.get(message.inboxAddress) || [];
250
+ existing.push(message);
251
+ inboxMap.set(message.inboxAddress, existing);
252
+ }
253
+
254
+ // Process all inbox groups concurrently
255
+ const promises: Promise<void>[] = [];
256
+
257
+ for (const [_, messages] of inboxMap) {
258
+ promises.push(
259
+ (async () => {
260
+ for (const message of messages) {
261
+ try {
262
+ await this.messageHandler!(message);
263
+ } catch (error) {
264
+ console.error('Error processing inbound message:', error);
265
+ }
266
+ }
267
+ })()
268
+ );
269
+ }
270
+
271
+ await Promise.allSettled(promises);
272
+ }
273
+
274
+ // Process outbound messages only if connected
275
+ if (this.ws?.readyState === WebSocket.OPEN) {
276
+ while (this.outboundQueue.length > 0) {
277
+ const prepareMessage = this.outboundQueue.shift()!;
278
+ try {
279
+ const messages = await prepareMessage();
280
+ for (const m of messages) {
281
+ this.ws.send(m);
282
+ }
283
+ } catch (error) {
284
+ console.error('Error processing outbound message:', error);
285
+ }
286
+ }
287
+ }
288
+ } finally {
289
+ this.isProcessing = false;
290
+ }
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Factory function to create a browser WebSocket client
296
+ */
297
+ export function createBrowserWebSocketClient(options: WebSocketClientOptions): WebSocketClient {
298
+ return new BrowserWebSocketClient(options);
299
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Transport module exports
3
+ */
4
+
5
+ // HTTP transport types
6
+ export type {
7
+ TransportConfig,
8
+ TransportRequestOptions,
9
+ TransportResponse,
10
+ TransportClient,
11
+ } from './types';
12
+
13
+ // WebSocket types
14
+ export type {
15
+ WebSocketConnectionState,
16
+ EncryptedWebSocketMessage,
17
+ SealedMessage,
18
+ UnsealedEnvelope,
19
+ OutboundWebSocketMessage,
20
+ ListenMessage,
21
+ UnlistenMessage,
22
+ WebSocketClientOptions,
23
+ MessageHandler,
24
+ StateChangeHandler,
25
+ ErrorHandler,
26
+ WebSocketClient,
27
+ CreateWebSocketClient,
28
+ } from './websocket';
29
+
30
+ // Browser/Electron WebSocket implementation
31
+ export { BrowserWebSocketClient, createBrowserWebSocketClient } from './browser-websocket';
32
+
33
+ // React Native WebSocket implementation
34
+ export { RNWebSocketClient, createRNWebSocketClient } from './rn-websocket';
@@ -0,0 +1,321 @@
1
+ /**
2
+ * React Native WebSocket client implementation
3
+ *
4
+ * Uses the React Native WebSocket API which is similar to the browser's.
5
+ * Implements automatic reconnection, dual queue system, and subscription management.
6
+ *
7
+ * Note: This implementation is nearly identical to browser-websocket.ts
8
+ * but kept separate to allow for React Native-specific optimizations if needed.
9
+ */
10
+
11
+ import type {
12
+ WebSocketClient,
13
+ WebSocketClientOptions,
14
+ WebSocketConnectionState,
15
+ EncryptedWebSocketMessage,
16
+ MessageHandler,
17
+ StateChangeHandler,
18
+ ErrorHandler,
19
+ } from './websocket';
20
+ import { logger } from '../utils/logger';
21
+
22
+ /**
23
+ * RNWebSocketClient - WebSocket implementation for React Native
24
+ *
25
+ * Features:
26
+ * - Automatic reconnection with configurable interval
27
+ * - Dual queue system (inbound messages, outbound message generators)
28
+ * - Inbox address subscription management
29
+ * - Periodic queue processing
30
+ */
31
+ export class RNWebSocketClient implements WebSocketClient {
32
+ private url: string;
33
+ private reconnectInterval: number;
34
+ private maxReconnectAttempts: number;
35
+ private queueProcessInterval: number;
36
+
37
+ private ws: WebSocket | null = null;
38
+ private _state: WebSocketConnectionState = 'disconnected';
39
+ private reconnectAttempts = 0;
40
+ private shouldReconnect = true;
41
+
42
+ // Queue system
43
+ private inboundQueue: EncryptedWebSocketMessage[] = [];
44
+ private outboundQueue: Array<() => Promise<string[]>> = [];
45
+ private isProcessing = false;
46
+ private processIntervalId: ReturnType<typeof setInterval> | null = null;
47
+
48
+ // Handlers
49
+ private messageHandler: MessageHandler | null = null;
50
+ private resubscribeHandler: (() => Promise<void>) | null = null;
51
+ private stateChangeHandlers: Set<StateChangeHandler> = new Set();
52
+ private errorHandlers: Set<ErrorHandler> = new Set();
53
+
54
+ constructor(options: WebSocketClientOptions) {
55
+ this.url = options.url;
56
+ this.reconnectInterval = options.reconnectInterval ?? 1000;
57
+ this.maxReconnectAttempts = options.maxReconnectAttempts ?? Infinity;
58
+ this.queueProcessInterval = options.queueProcessInterval ?? 1000;
59
+ }
60
+
61
+ get state(): WebSocketConnectionState {
62
+ return this._state;
63
+ }
64
+
65
+ get isConnected(): boolean {
66
+ return this._state === 'connected';
67
+ }
68
+
69
+ private setState(newState: WebSocketConnectionState): void {
70
+ if (this._state !== newState) {
71
+ this._state = newState;
72
+ this.stateChangeHandlers.forEach((handler) => {
73
+ try {
74
+ handler(newState);
75
+ } catch (error) {
76
+ console.error('Error in state change handler:', error);
77
+ }
78
+ });
79
+ }
80
+ }
81
+
82
+ private emitError(error: Error): void {
83
+ this.errorHandlers.forEach((handler) => {
84
+ try {
85
+ handler(error);
86
+ } catch (e) {
87
+ console.error('Error in error handler:', e);
88
+ }
89
+ });
90
+ }
91
+
92
+ async connect(): Promise<void> {
93
+ if (this._state === 'connected' || this._state === 'connecting') {
94
+ return;
95
+ }
96
+
97
+ this.shouldReconnect = true;
98
+ return this.doConnect();
99
+ }
100
+
101
+ private doConnect(): Promise<void> {
102
+ return new Promise((resolve, reject) => {
103
+ this.setState('connecting');
104
+
105
+ try {
106
+ // React Native uses the global WebSocket class
107
+ this.ws = new WebSocket(this.url);
108
+
109
+ this.ws.onopen = () => {
110
+ this.reconnectAttempts = 0;
111
+ this.setState('connected');
112
+ this.startQueueProcessing();
113
+
114
+ // Call resubscribe handler to restore subscriptions
115
+ if (this.resubscribeHandler) {
116
+ this.resubscribeHandler().catch((error) => {
117
+ console.error('Error in resubscribe handler:', error);
118
+ });
119
+ }
120
+
121
+ // Process any pending outbound messages
122
+ this.processQueues();
123
+ resolve();
124
+ };
125
+
126
+ this.ws.onclose = () => {
127
+ this.setState('disconnected');
128
+ this.stopQueueProcessing();
129
+
130
+ if (this.shouldReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
131
+ this.reconnectAttempts++;
132
+ this.setState('reconnecting');
133
+ setTimeout(() => this.doConnect(), this.reconnectInterval);
134
+ }
135
+ };
136
+
137
+ this.ws.onerror = () => {
138
+ const error = new Error('WebSocket error');
139
+ this.emitError(error);
140
+ reject(error);
141
+ };
142
+
143
+ this.ws.onmessage = (event: { data: string }) => {
144
+ try {
145
+ // Log raw message for debugging
146
+ logger.debug('[WS-RN] Raw message received:', event.data?.substring(0, 200));
147
+
148
+ // Some server messages might not be encrypted content (e.g., acks, pings)
149
+ // Skip messages that don't look like JSON objects
150
+ if (!event.data || !event.data.startsWith('{')) {
151
+ logger.debug('[WS-RN] Ignoring non-JSON message');
152
+ return;
153
+ }
154
+
155
+ const message = JSON.parse(event.data) as EncryptedWebSocketMessage;
156
+
157
+ // Only process messages that have the expected structure
158
+ if (!message.inboxAddress && !message.encryptedContent) {
159
+ logger.debug('[WS-RN] Ignoring message without expected fields:', Object.keys(message));
160
+ return;
161
+ }
162
+
163
+ this.inboundQueue.push(message);
164
+ this.processQueues();
165
+ } catch (error) {
166
+ console.error('Failed to parse WebSocket message:', error, 'raw:', event.data?.substring(0, 100));
167
+ }
168
+ };
169
+ } catch (error) {
170
+ this.setState('disconnected');
171
+ reject(error);
172
+ }
173
+ });
174
+ }
175
+
176
+ disconnect(): void {
177
+ this.shouldReconnect = false;
178
+ this.stopQueueProcessing();
179
+
180
+ if (this.ws) {
181
+ this.ws.close();
182
+ this.ws = null;
183
+ }
184
+
185
+ this.setState('disconnected');
186
+ }
187
+
188
+ async send(message: string): Promise<void> {
189
+ if (this.ws?.readyState === WebSocket.OPEN) {
190
+ this.ws.send(message);
191
+ } else {
192
+ // Queue the message for later
193
+ this.outboundQueue.push(async () => [message]);
194
+ }
195
+ }
196
+
197
+ enqueueOutbound(prepareMessage: () => Promise<string[]>): void {
198
+ this.outboundQueue.push(prepareMessage);
199
+ this.processQueues();
200
+ }
201
+
202
+ async subscribe(inboxAddresses: string[]): Promise<void> {
203
+ const message = JSON.stringify({
204
+ type: 'listen',
205
+ inbox_addresses: inboxAddresses,
206
+ });
207
+ await this.send(message);
208
+ }
209
+
210
+ async unsubscribe(inboxAddresses: string[]): Promise<void> {
211
+ const message = JSON.stringify({
212
+ type: 'unlisten',
213
+ inbox_addresses: inboxAddresses,
214
+ });
215
+ await this.send(message);
216
+ }
217
+
218
+ setMessageHandler(handler: MessageHandler): void {
219
+ this.messageHandler = handler;
220
+ this.processQueues();
221
+ }
222
+
223
+ setResubscribeHandler(handler: () => Promise<void>): void {
224
+ this.resubscribeHandler = handler;
225
+ }
226
+
227
+ onStateChange(handler: StateChangeHandler): () => void {
228
+ this.stateChangeHandlers.add(handler);
229
+ return () => {
230
+ this.stateChangeHandlers.delete(handler);
231
+ };
232
+ }
233
+
234
+ onError(handler: ErrorHandler): () => void {
235
+ this.errorHandlers.add(handler);
236
+ return () => {
237
+ this.errorHandlers.delete(handler);
238
+ };
239
+ }
240
+
241
+ private startQueueProcessing(): void {
242
+ if (this.processIntervalId === null) {
243
+ this.processIntervalId = setInterval(() => {
244
+ this.processQueues();
245
+ }, this.queueProcessInterval);
246
+ }
247
+ }
248
+
249
+ private stopQueueProcessing(): void {
250
+ if (this.processIntervalId !== null) {
251
+ clearInterval(this.processIntervalId);
252
+ this.processIntervalId = null;
253
+ }
254
+ }
255
+
256
+ private async processQueues(): Promise<void> {
257
+ if (this.isProcessing) {
258
+ return;
259
+ }
260
+
261
+ this.isProcessing = true;
262
+
263
+ try {
264
+ // Process inbound messages
265
+ if (this.messageHandler) {
266
+ // Group messages by inbox address for efficient processing
267
+ const inboxMap = new Map<string, EncryptedWebSocketMessage[]>();
268
+
269
+ while (this.inboundQueue.length > 0) {
270
+ const message = this.inboundQueue.shift()!;
271
+ const existing = inboxMap.get(message.inboxAddress) || [];
272
+ existing.push(message);
273
+ inboxMap.set(message.inboxAddress, existing);
274
+ }
275
+
276
+ // Process all inbox groups concurrently
277
+ const promises: Promise<void>[] = [];
278
+
279
+ for (const [_, messages] of inboxMap) {
280
+ promises.push(
281
+ (async () => {
282
+ for (const message of messages) {
283
+ try {
284
+ await this.messageHandler!(message);
285
+ } catch (error) {
286
+ console.error('Error processing inbound message:', error);
287
+ }
288
+ }
289
+ })()
290
+ );
291
+ }
292
+
293
+ await Promise.allSettled(promises);
294
+ }
295
+
296
+ // Process outbound messages only if connected
297
+ if (this.ws?.readyState === WebSocket.OPEN) {
298
+ while (this.outboundQueue.length > 0) {
299
+ const prepareMessage = this.outboundQueue.shift()!;
300
+ try {
301
+ const messages = await prepareMessage();
302
+ for (const m of messages) {
303
+ this.ws.send(m);
304
+ }
305
+ } catch (error) {
306
+ console.error('Error processing outbound message:', error);
307
+ }
308
+ }
309
+ }
310
+ } finally {
311
+ this.isProcessing = false;
312
+ }
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Factory function to create a React Native WebSocket client
318
+ */
319
+ export function createRNWebSocketClient(options: WebSocketClientOptions): WebSocketClient {
320
+ return new RNWebSocketClient(options);
321
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Transport types and interfaces
3
+ *
4
+ * Platform-agnostic transport layer for HTTP and IPC communication.
5
+ */
6
+
7
+ // ============ HTTP Transport ============
8
+
9
+ export interface TransportConfig {
10
+ baseUrl: string;
11
+ timeout?: number;
12
+ defaultHeaders?: Record<string, string>;
13
+ }
14
+
15
+ export interface TransportRequestOptions {
16
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
17
+ headers?: Record<string, string>;
18
+ body?: unknown;
19
+ timeout?: number;
20
+ signal?: AbortSignal;
21
+ }
22
+
23
+ export interface TransportResponse<T> {
24
+ data: T;
25
+ status: number;
26
+ headers: Record<string, string>;
27
+ }
28
+
29
+ /**
30
+ * TransportClient - Platform-agnostic HTTP transport
31
+ *
32
+ * Implementations:
33
+ * - Fetch (mobile/web): Uses native fetch API
34
+ * - Electron IPC (desktop): Uses Electron IPC for main process requests
35
+ */
36
+ export interface TransportClient {
37
+ /**
38
+ * Make an HTTP request
39
+ */
40
+ request<T>(endpoint: string, options?: TransportRequestOptions): Promise<TransportResponse<T>>;
41
+
42
+ /**
43
+ * Configure the transport client
44
+ */
45
+ configure(config: TransportConfig): void;
46
+
47
+ /**
48
+ * Set authorization header for all requests
49
+ */
50
+ setAuthToken(token: string): void;
51
+
52
+ /**
53
+ * Clear authorization
54
+ */
55
+ clearAuth(): void;
56
+ }