@soulcraft/brainy 0.41.0 → 0.44.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.
Files changed (205) hide show
  1. package/README.md +605 -194
  2. package/dist/augmentationFactory.d.ts.map +1 -0
  3. package/dist/augmentationFactory.js +342 -0
  4. package/dist/augmentationFactory.js.map +1 -0
  5. package/dist/augmentationPipeline.d.ts.map +1 -0
  6. package/dist/augmentationPipeline.js +472 -0
  7. package/dist/augmentationPipeline.js.map +1 -0
  8. package/dist/augmentationRegistry.d.ts.map +1 -0
  9. package/dist/augmentationRegistry.js +105 -0
  10. package/dist/augmentationRegistry.js.map +1 -0
  11. package/dist/augmentationRegistryLoader.d.ts.map +1 -0
  12. package/dist/augmentationRegistryLoader.js +213 -0
  13. package/dist/augmentationRegistryLoader.js.map +1 -0
  14. package/dist/augmentations/conduitAugmentations.js +1158 -0
  15. package/dist/augmentations/conduitAugmentations.js.map +1 -0
  16. package/dist/augmentations/memoryAugmentations.d.ts +2 -0
  17. package/dist/augmentations/memoryAugmentations.d.ts.map +1 -1
  18. package/dist/augmentations/memoryAugmentations.js +270 -0
  19. package/dist/augmentations/memoryAugmentations.js.map +1 -0
  20. package/dist/augmentations/serverSearchAugmentations.js +531 -0
  21. package/dist/augmentations/serverSearchAugmentations.js.map +1 -0
  22. package/dist/brainyData.d.ts.map +1 -0
  23. package/dist/brainyData.js +3999 -0
  24. package/dist/brainyData.js.map +1 -0
  25. package/dist/browserFramework.d.ts +15 -0
  26. package/dist/browserFramework.d.ts.map +1 -0
  27. package/dist/browserFramework.js +31 -0
  28. package/dist/browserFramework.js.map +1 -0
  29. package/dist/coreTypes.d.ts.map +1 -0
  30. package/dist/coreTypes.js +5 -0
  31. package/dist/coreTypes.js.map +1 -0
  32. package/dist/demo.d.ts +106 -0
  33. package/dist/demo.d.ts.map +1 -0
  34. package/dist/demo.js +201 -0
  35. package/dist/demo.js.map +1 -0
  36. package/dist/distributed/configManager.d.ts.map +1 -0
  37. package/dist/distributed/configManager.js +322 -0
  38. package/dist/distributed/configManager.js.map +1 -0
  39. package/dist/distributed/domainDetector.d.ts.map +1 -0
  40. package/dist/distributed/domainDetector.js +307 -0
  41. package/dist/distributed/domainDetector.js.map +1 -0
  42. package/dist/distributed/hashPartitioner.d.ts.map +1 -0
  43. package/dist/distributed/hashPartitioner.js +146 -0
  44. package/dist/distributed/hashPartitioner.js.map +1 -0
  45. package/dist/distributed/healthMonitor.d.ts.map +1 -0
  46. package/dist/distributed/healthMonitor.js +244 -0
  47. package/dist/distributed/healthMonitor.js.map +1 -0
  48. package/dist/distributed/index.d.ts.map +1 -0
  49. package/dist/distributed/index.js +9 -0
  50. package/dist/distributed/index.js.map +1 -0
  51. package/dist/distributed/operationalModes.d.ts.map +1 -0
  52. package/dist/distributed/operationalModes.js +201 -0
  53. package/dist/distributed/operationalModes.js.map +1 -0
  54. package/dist/errors/brainyError.d.ts.map +1 -0
  55. package/dist/errors/brainyError.js +113 -0
  56. package/dist/errors/brainyError.js.map +1 -0
  57. package/dist/examples/basicUsage.js +118 -0
  58. package/dist/examples/basicUsage.js.map +1 -0
  59. package/dist/hnsw/distributedSearch.js +452 -0
  60. package/dist/hnsw/distributedSearch.js.map +1 -0
  61. package/dist/hnsw/hnswIndex.js +602 -0
  62. package/dist/hnsw/hnswIndex.js.map +1 -0
  63. package/dist/hnsw/hnswIndexOptimized.js +471 -0
  64. package/dist/hnsw/hnswIndexOptimized.js.map +1 -0
  65. package/dist/hnsw/optimizedHNSWIndex.js +313 -0
  66. package/dist/hnsw/optimizedHNSWIndex.js.map +1 -0
  67. package/dist/hnsw/partitionedHNSWIndex.js +304 -0
  68. package/dist/hnsw/partitionedHNSWIndex.js.map +1 -0
  69. package/dist/hnsw/scaledHNSWSystem.js +559 -0
  70. package/dist/hnsw/scaledHNSWSystem.js.map +1 -0
  71. package/dist/index.d.ts +3 -2
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +81 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/mcp/brainyMCPAdapter.js +142 -0
  76. package/dist/mcp/brainyMCPAdapter.js.map +1 -0
  77. package/dist/mcp/brainyMCPService.js +248 -0
  78. package/dist/mcp/brainyMCPService.js.map +1 -0
  79. package/dist/mcp/index.js +17 -0
  80. package/dist/mcp/index.js.map +1 -0
  81. package/dist/mcp/mcpAugmentationToolset.js +180 -0
  82. package/dist/mcp/mcpAugmentationToolset.js.map +1 -0
  83. package/dist/pipeline.d.ts.map +1 -0
  84. package/dist/pipeline.js +590 -0
  85. package/dist/pipeline.js.map +1 -0
  86. package/dist/sequentialPipeline.d.ts.map +1 -0
  87. package/dist/sequentialPipeline.js +417 -0
  88. package/dist/sequentialPipeline.js.map +1 -0
  89. package/dist/setup.d.ts.map +1 -0
  90. package/dist/setup.js +46 -0
  91. package/dist/setup.js.map +1 -0
  92. package/dist/storage/adapters/baseStorageAdapter.js +349 -0
  93. package/dist/storage/adapters/baseStorageAdapter.js.map +1 -0
  94. package/dist/storage/adapters/batchS3Operations.js +287 -0
  95. package/dist/storage/adapters/batchS3Operations.js.map +1 -0
  96. package/dist/storage/adapters/fileSystemStorage.js +846 -0
  97. package/dist/storage/adapters/fileSystemStorage.js.map +1 -0
  98. package/dist/storage/adapters/memoryStorage.js +532 -0
  99. package/dist/storage/adapters/memoryStorage.js.map +1 -0
  100. package/dist/storage/adapters/opfsStorage.d.ts.map +1 -1
  101. package/dist/storage/adapters/opfsStorage.js +1118 -0
  102. package/dist/storage/adapters/opfsStorage.js.map +1 -0
  103. package/dist/storage/adapters/optimizedS3Search.js +248 -0
  104. package/dist/storage/adapters/optimizedS3Search.js.map +1 -0
  105. package/dist/storage/adapters/s3CompatibleStorage.js +2026 -0
  106. package/dist/storage/adapters/s3CompatibleStorage.js.map +1 -0
  107. package/dist/storage/baseStorage.js +603 -0
  108. package/dist/storage/baseStorage.js.map +1 -0
  109. package/dist/storage/cacheManager.js +1306 -0
  110. package/dist/storage/cacheManager.js.map +1 -0
  111. package/dist/storage/enhancedCacheManager.js +520 -0
  112. package/dist/storage/enhancedCacheManager.js.map +1 -0
  113. package/dist/storage/readOnlyOptimizations.js +425 -0
  114. package/dist/storage/readOnlyOptimizations.js.map +1 -0
  115. package/dist/storage/storageFactory.d.ts +0 -1
  116. package/dist/storage/storageFactory.d.ts.map +1 -1
  117. package/dist/storage/storageFactory.js +227 -0
  118. package/dist/storage/storageFactory.js.map +1 -0
  119. package/dist/types/augmentations.js +16 -0
  120. package/dist/types/augmentations.js.map +1 -0
  121. package/dist/types/brainyDataInterface.js +8 -0
  122. package/dist/types/brainyDataInterface.js.map +1 -0
  123. package/dist/types/distributedTypes.js +6 -0
  124. package/dist/types/distributedTypes.js.map +1 -0
  125. package/dist/types/fileSystemTypes.js +8 -0
  126. package/dist/types/fileSystemTypes.js.map +1 -0
  127. package/dist/types/graphTypes.js +247 -0
  128. package/dist/types/graphTypes.js.map +1 -0
  129. package/dist/types/mcpTypes.js +22 -0
  130. package/dist/types/mcpTypes.js.map +1 -0
  131. package/dist/types/paginationTypes.js +5 -0
  132. package/dist/types/paginationTypes.js.map +1 -0
  133. package/dist/types/pipelineTypes.js +7 -0
  134. package/dist/types/pipelineTypes.js.map +1 -0
  135. package/dist/types/tensorflowTypes.js +6 -0
  136. package/dist/types/tensorflowTypes.js.map +1 -0
  137. package/dist/unified.d.ts.map +1 -0
  138. package/dist/unified.js +52 -128251
  139. package/dist/unified.js.map +1 -0
  140. package/dist/utils/autoConfiguration.js +341 -0
  141. package/dist/utils/autoConfiguration.js.map +1 -0
  142. package/dist/utils/cacheAutoConfig.js +261 -0
  143. package/dist/utils/cacheAutoConfig.js.map +1 -0
  144. package/dist/utils/crypto.js +45 -0
  145. package/dist/utils/crypto.js.map +1 -0
  146. package/dist/utils/distance.js +239 -0
  147. package/dist/utils/distance.js.map +1 -0
  148. package/dist/utils/embedding.d.ts.map +1 -1
  149. package/dist/utils/embedding.js +702 -0
  150. package/dist/utils/embedding.js.map +1 -0
  151. package/dist/utils/environment.js +75 -0
  152. package/dist/utils/environment.js.map +1 -0
  153. package/dist/utils/fieldNameTracking.js +90 -0
  154. package/dist/utils/fieldNameTracking.js.map +1 -0
  155. package/dist/utils/index.d.ts +1 -0
  156. package/dist/utils/index.d.ts.map +1 -1
  157. package/dist/utils/index.js +8 -0
  158. package/dist/utils/index.js.map +1 -0
  159. package/dist/utils/jsonProcessing.js +179 -0
  160. package/dist/utils/jsonProcessing.js.map +1 -0
  161. package/dist/utils/logger.js +129 -0
  162. package/dist/utils/logger.js.map +1 -0
  163. package/dist/utils/operationUtils.js +126 -0
  164. package/dist/utils/operationUtils.js.map +1 -0
  165. package/dist/utils/robustModelLoader.d.ts +14 -0
  166. package/dist/utils/robustModelLoader.d.ts.map +1 -1
  167. package/dist/utils/robustModelLoader.js +537 -0
  168. package/dist/utils/robustModelLoader.js.map +1 -0
  169. package/dist/utils/searchCache.js +248 -0
  170. package/dist/utils/searchCache.js.map +1 -0
  171. package/dist/utils/statistics.js +25 -0
  172. package/dist/utils/statistics.js.map +1 -0
  173. package/dist/utils/statisticsCollector.js +224 -0
  174. package/dist/utils/statisticsCollector.js.map +1 -0
  175. package/dist/utils/textEncoding.js +309 -0
  176. package/dist/utils/textEncoding.js.map +1 -0
  177. package/dist/utils/typeUtils.js +40 -0
  178. package/dist/utils/typeUtils.js.map +1 -0
  179. package/dist/utils/version.d.ts +15 -3
  180. package/dist/utils/version.d.ts.map +1 -1
  181. package/dist/utils/version.js +24 -0
  182. package/dist/utils/version.js.map +1 -0
  183. package/dist/utils/workerUtils.js +458 -0
  184. package/dist/utils/workerUtils.js.map +1 -0
  185. package/dist/worker.d.ts.map +1 -0
  186. package/dist/worker.js +54 -0
  187. package/dist/worker.js.map +1 -0
  188. package/package.json +30 -29
  189. package/dist/brainy.js +0 -90220
  190. package/dist/brainy.min.js +0 -12511
  191. package/dist/patched-platform-node.d.ts +0 -17
  192. package/dist/statistics/statisticsManager.d.ts +0 -121
  193. package/dist/storage/fileSystemStorage.d.ts +0 -73
  194. package/dist/storage/fileSystemStorage.d.ts.map +0 -1
  195. package/dist/storage/opfsStorage.d.ts +0 -236
  196. package/dist/storage/opfsStorage.d.ts.map +0 -1
  197. package/dist/storage/s3CompatibleStorage.d.ts +0 -157
  198. package/dist/storage/s3CompatibleStorage.d.ts.map +0 -1
  199. package/dist/testing/prettyReporter.d.ts +0 -23
  200. package/dist/testing/prettySummaryReporter.d.ts +0 -22
  201. package/dist/unified.min.js +0 -16153
  202. package/dist/utils/environmentDetection.d.ts +0 -47
  203. package/dist/utils/environmentDetection.d.ts.map +0 -1
  204. package/dist/utils/tensorflowUtils.d.ts +0 -17
  205. package/dist/utils/tensorflowUtils.d.ts.map +0 -1
@@ -0,0 +1,1158 @@
1
+ import { AugmentationType } from '../types/augmentations.js';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ /**
4
+ * Base class for conduit augmentations that provide data synchronization between Brainy instances
5
+ */
6
+ class BaseConduitAugmentation {
7
+ constructor(name) {
8
+ this.description = 'Base conduit augmentation';
9
+ this.enabled = true;
10
+ this.isInitialized = false;
11
+ this.connections = new Map();
12
+ this.name = name;
13
+ }
14
+ async initialize() {
15
+ if (this.isInitialized) {
16
+ return;
17
+ }
18
+ try {
19
+ this.isInitialized = true;
20
+ }
21
+ catch (error) {
22
+ console.error(`Failed to initialize ${this.name}:`, error);
23
+ throw new Error(`Failed to initialize ${this.name}: ${error}`);
24
+ }
25
+ }
26
+ async shutDown() {
27
+ // Close all connections
28
+ for (const [connectionId, connection] of this.connections.entries()) {
29
+ try {
30
+ if (connection.close) {
31
+ await connection.close();
32
+ }
33
+ }
34
+ catch (error) {
35
+ console.error(`Failed to close connection ${connectionId}:`, error);
36
+ }
37
+ }
38
+ this.connections.clear();
39
+ this.isInitialized = false;
40
+ }
41
+ async getStatus() {
42
+ return this.isInitialized ? 'active' : 'inactive';
43
+ }
44
+ async ensureInitialized() {
45
+ if (!this.isInitialized) {
46
+ await this.initialize();
47
+ }
48
+ }
49
+ }
50
+ /**
51
+ * WebSocket conduit augmentation for syncing Brainy instances using WebSockets
52
+ *
53
+ * This conduit is for syncing between browsers and servers, or between servers.
54
+ * WebSockets cannot be used for direct browser-to-browser communication without a server in the middle.
55
+ */
56
+ export class WebSocketConduitAugmentation extends BaseConduitAugmentation {
57
+ constructor(name = 'websocket-conduit') {
58
+ super(name);
59
+ this.description = 'Conduit augmentation that syncs Brainy instances using WebSockets';
60
+ this.webSocketConnections = new Map();
61
+ this.messageCallbacks = new Map();
62
+ }
63
+ getType() {
64
+ return AugmentationType.CONDUIT;
65
+ }
66
+ /**
67
+ * Establishes a connection to another Brainy instance
68
+ * @param targetSystemId The URL or identifier of the target system
69
+ * @param config Configuration options for the connection
70
+ */
71
+ async establishConnection(targetSystemId, config) {
72
+ await this.ensureInitialized();
73
+ try {
74
+ // For WebSocket connections, targetSystemId should be a WebSocket URL
75
+ const url = targetSystemId;
76
+ const protocols = config.protocols;
77
+ // Create a WebSocket connection
78
+ const connection = await this.connectWebSocket(url, protocols);
79
+ // Store the connection
80
+ this.connections.set(connection.connectionId, connection);
81
+ return {
82
+ success: true,
83
+ data: connection
84
+ };
85
+ }
86
+ catch (error) {
87
+ console.error(`Failed to establish connection to ${targetSystemId}:`, error);
88
+ return {
89
+ success: false,
90
+ data: null,
91
+ error: `Failed to establish connection: ${error}`
92
+ };
93
+ }
94
+ }
95
+ /**
96
+ * Reads data from a connected Brainy instance
97
+ * @param query Query parameters for reading data
98
+ * @param options Additional options
99
+ */
100
+ async readData(query, options) {
101
+ await this.ensureInitialized();
102
+ try {
103
+ const connectionId = query.connectionId;
104
+ if (!connectionId) {
105
+ throw new Error('connectionId is required for reading data');
106
+ }
107
+ const connection = this.webSocketConnections.get(connectionId);
108
+ if (!connection) {
109
+ throw new Error(`Connection ${connectionId} not found`);
110
+ }
111
+ // Create a request message
112
+ const requestMessage = {
113
+ type: 'read',
114
+ query: query.query || {},
115
+ requestId: uuidv4(),
116
+ options
117
+ };
118
+ // Send the request
119
+ await this.sendWebSocketMessage(connectionId, requestMessage);
120
+ // Return a promise that will be resolved when the response is received
121
+ return new Promise((resolve) => {
122
+ const responseHandler = (data) => {
123
+ // Check if this is the response to our request
124
+ const response = data;
125
+ if (response && response.type === 'readResponse' && response.requestId === requestMessage.requestId) {
126
+ // Remove the handler
127
+ this.offWebSocketMessage(connectionId, responseHandler);
128
+ // Resolve with the response data
129
+ resolve({
130
+ success: response.success,
131
+ data: response.data,
132
+ error: response.error
133
+ });
134
+ }
135
+ };
136
+ // Register the response handler
137
+ this.onWebSocketMessage(connectionId, responseHandler);
138
+ // Set a timeout to prevent hanging
139
+ setTimeout(() => {
140
+ this.offWebSocketMessage(connectionId, responseHandler);
141
+ resolve({
142
+ success: false,
143
+ data: null,
144
+ error: 'Timeout waiting for read response'
145
+ });
146
+ }, 30000); // 30 second timeout
147
+ });
148
+ }
149
+ catch (error) {
150
+ console.error(`Failed to read data:`, error);
151
+ return {
152
+ success: false,
153
+ data: null,
154
+ error: `Failed to read data: ${error}`
155
+ };
156
+ }
157
+ }
158
+ /**
159
+ * Writes data to a connected Brainy instance
160
+ * @param data The data to write
161
+ * @param options Additional options
162
+ */
163
+ async writeData(data, options) {
164
+ await this.ensureInitialized();
165
+ try {
166
+ const connectionId = data.connectionId;
167
+ if (!connectionId) {
168
+ throw new Error('connectionId is required for writing data');
169
+ }
170
+ const connection = this.webSocketConnections.get(connectionId);
171
+ if (!connection) {
172
+ throw new Error(`Connection ${connectionId} not found`);
173
+ }
174
+ // Create a write message
175
+ const writeMessage = {
176
+ type: 'write',
177
+ data: data.data || {},
178
+ requestId: uuidv4(),
179
+ options
180
+ };
181
+ // Send the write message
182
+ await this.sendWebSocketMessage(connectionId, writeMessage);
183
+ // Return a promise that will be resolved when the response is received
184
+ return new Promise((resolve) => {
185
+ const responseHandler = (data) => {
186
+ // Check if this is the response to our request
187
+ const response = data;
188
+ if (response && response.type === 'writeResponse' && response.requestId === writeMessage.requestId) {
189
+ // Remove the handler
190
+ this.offWebSocketMessage(connectionId, responseHandler);
191
+ // Resolve with the response data
192
+ resolve({
193
+ success: response.success,
194
+ data: response.data,
195
+ error: response.error
196
+ });
197
+ }
198
+ };
199
+ // Register the response handler
200
+ this.onWebSocketMessage(connectionId, responseHandler);
201
+ // Set a timeout to prevent hanging
202
+ setTimeout(() => {
203
+ this.offWebSocketMessage(connectionId, responseHandler);
204
+ resolve({
205
+ success: false,
206
+ data: null,
207
+ error: 'Timeout waiting for write response'
208
+ });
209
+ }, 30000); // 30 second timeout
210
+ });
211
+ }
212
+ catch (error) {
213
+ console.error(`Failed to write data:`, error);
214
+ return {
215
+ success: false,
216
+ data: null,
217
+ error: `Failed to write data: ${error}`
218
+ };
219
+ }
220
+ }
221
+ /**
222
+ * Monitors a data stream from a connected Brainy instance
223
+ * @param streamId The ID of the stream to monitor (usually a connection ID)
224
+ * @param callback Function to call when new data is received
225
+ */
226
+ async monitorStream(streamId, callback) {
227
+ await this.ensureInitialized();
228
+ try {
229
+ const connection = this.webSocketConnections.get(streamId);
230
+ if (!connection) {
231
+ throw new Error(`Connection ${streamId} not found`);
232
+ }
233
+ // Register the callback for all messages on this connection
234
+ await this.onWebSocketMessage(streamId, callback);
235
+ }
236
+ catch (error) {
237
+ console.error(`Failed to monitor stream ${streamId}:`, error);
238
+ throw new Error(`Failed to monitor stream: ${error}`);
239
+ }
240
+ }
241
+ /**
242
+ * Establishes a WebSocket connection
243
+ * @param url The WebSocket server URL to connect to
244
+ * @param protocols Optional subprotocols
245
+ */
246
+ async connectWebSocket(url, protocols) {
247
+ await this.ensureInitialized();
248
+ return new Promise((resolve, reject) => {
249
+ try {
250
+ // Check if WebSocket is available
251
+ if (typeof WebSocket === 'undefined') {
252
+ throw new Error('WebSocket is not available in this environment');
253
+ }
254
+ // Create a new WebSocket connection
255
+ const ws = new WebSocket(url, protocols);
256
+ const connectionId = uuidv4();
257
+ // Create a connection object
258
+ const connection = {
259
+ connectionId,
260
+ url,
261
+ status: 'disconnected',
262
+ send: async (data) => {
263
+ if (ws.readyState !== WebSocket.OPEN) {
264
+ throw new Error('WebSocket is not open');
265
+ }
266
+ ws.send(data);
267
+ },
268
+ close: async () => {
269
+ ws.close();
270
+ }
271
+ };
272
+ // Set up event handlers
273
+ ws.onopen = () => {
274
+ connection.status = 'connected';
275
+ resolve(connection);
276
+ };
277
+ ws.onerror = (error) => {
278
+ connection.status = 'error';
279
+ console.error(`WebSocket error for ${url}:`, error);
280
+ if (ws.readyState !== WebSocket.OPEN) {
281
+ reject(new Error(`WebSocket connection failed: ${error}`));
282
+ }
283
+ };
284
+ ws.onclose = () => {
285
+ connection.status = 'disconnected';
286
+ // Remove from connections map
287
+ this.webSocketConnections.delete(connectionId);
288
+ // Remove all callbacks
289
+ this.messageCallbacks.delete(connectionId);
290
+ };
291
+ // Create a message handler wrapper that will call all registered callbacks
292
+ const messageHandlerWrapper = (data) => {
293
+ const callbacks = this.messageCallbacks.get(connectionId);
294
+ if (callbacks) {
295
+ for (const callback of callbacks) {
296
+ try {
297
+ callback(data);
298
+ }
299
+ catch (error) {
300
+ console.error(`Error in WebSocket message callback:`, error);
301
+ }
302
+ }
303
+ }
304
+ };
305
+ // Store the message handler wrapper
306
+ connection._messageHandlerWrapper = messageHandlerWrapper;
307
+ // Set up the message handler
308
+ ws.onmessage = (event) => {
309
+ try {
310
+ // Parse the message if it's a string
311
+ let data = event.data;
312
+ if (typeof data === 'string') {
313
+ try {
314
+ data = JSON.parse(data);
315
+ }
316
+ catch {
317
+ // If parsing fails, use the raw string
318
+ }
319
+ }
320
+ // Call the message handler wrapper
321
+ messageHandlerWrapper(data);
322
+ }
323
+ catch (error) {
324
+ console.error(`Error handling WebSocket message:`, error);
325
+ }
326
+ };
327
+ // Store the stream message handler
328
+ connection._streamMessageHandler = (event) => ws.onmessage && ws.onmessage(event);
329
+ // Store the connection
330
+ this.webSocketConnections.set(connectionId, connection);
331
+ // Initialize the callbacks set
332
+ this.messageCallbacks.set(connectionId, new Set());
333
+ }
334
+ catch (error) {
335
+ reject(error);
336
+ }
337
+ });
338
+ }
339
+ /**
340
+ * Sends data through an established WebSocket connection
341
+ * @param connectionId The identifier of the established connection
342
+ * @param data The data to send (will be serialized if not a string)
343
+ */
344
+ async sendWebSocketMessage(connectionId, data) {
345
+ await this.ensureInitialized();
346
+ const connection = this.webSocketConnections.get(connectionId);
347
+ if (!connection) {
348
+ throw new Error(`WebSocket connection ${connectionId} not found`);
349
+ }
350
+ if (!connection.send) {
351
+ throw new Error(`WebSocket connection ${connectionId} does not support sending messages`);
352
+ }
353
+ // Serialize the data if it's not already a string or binary
354
+ let serializedData;
355
+ if (typeof data === 'string' ||
356
+ data instanceof ArrayBuffer ||
357
+ data instanceof Blob ||
358
+ ArrayBuffer.isView(data)) {
359
+ serializedData = data;
360
+ }
361
+ else {
362
+ // Convert to JSON string
363
+ serializedData = JSON.stringify(data);
364
+ }
365
+ // Send the data
366
+ await connection.send(serializedData);
367
+ }
368
+ /**
369
+ * Registers a callback for incoming WebSocket messages
370
+ * @param connectionId The identifier of the established connection
371
+ * @param callback The function to call when a message is received
372
+ */
373
+ async onWebSocketMessage(connectionId, callback) {
374
+ await this.ensureInitialized();
375
+ const connection = this.webSocketConnections.get(connectionId);
376
+ if (!connection) {
377
+ throw new Error(`WebSocket connection ${connectionId} not found`);
378
+ }
379
+ // Get or create the callbacks set for this connection
380
+ let callbacks = this.messageCallbacks.get(connectionId);
381
+ if (!callbacks) {
382
+ callbacks = new Set();
383
+ this.messageCallbacks.set(connectionId, callbacks);
384
+ }
385
+ // Add the callback
386
+ callbacks.add(callback);
387
+ }
388
+ /**
389
+ * Removes a callback for incoming WebSocket messages
390
+ * @param connectionId The identifier of the established connection
391
+ * @param callback The function to remove from the callbacks
392
+ */
393
+ async offWebSocketMessage(connectionId, callback) {
394
+ await this.ensureInitialized();
395
+ const callbacks = this.messageCallbacks.get(connectionId);
396
+ if (callbacks) {
397
+ callbacks.delete(callback);
398
+ }
399
+ }
400
+ /**
401
+ * Closes an established WebSocket connection
402
+ * @param connectionId The identifier of the established connection
403
+ * @param code Optional close code
404
+ * @param reason Optional close reason
405
+ */
406
+ async closeWebSocket(connectionId, code, reason) {
407
+ await this.ensureInitialized();
408
+ const connection = this.webSocketConnections.get(connectionId);
409
+ if (!connection) {
410
+ throw new Error(`WebSocket connection ${connectionId} not found`);
411
+ }
412
+ if (!connection.close) {
413
+ throw new Error(`WebSocket connection ${connectionId} does not support closing`);
414
+ }
415
+ // Close the connection
416
+ await connection.close();
417
+ // Remove from connections map
418
+ this.webSocketConnections.delete(connectionId);
419
+ // Remove all callbacks
420
+ this.messageCallbacks.delete(connectionId);
421
+ }
422
+ }
423
+ /**
424
+ * WebRTC conduit augmentation for syncing Brainy instances using WebRTC
425
+ *
426
+ * This conduit is for direct peer-to-peer syncing between browsers.
427
+ * It is the recommended approach for browser-to-browser communication.
428
+ */
429
+ export class WebRTCConduitAugmentation extends BaseConduitAugmentation {
430
+ constructor(name = 'webrtc-conduit') {
431
+ super(name);
432
+ this.description = 'Conduit augmentation that syncs Brainy instances using WebRTC';
433
+ this.peerConnections = new Map();
434
+ this.dataChannels = new Map();
435
+ this.webSocketConnections = new Map();
436
+ this.messageCallbacks = new Map();
437
+ this.signalServer = null;
438
+ }
439
+ getType() {
440
+ return AugmentationType.CONDUIT;
441
+ }
442
+ async initialize() {
443
+ if (this.isInitialized) {
444
+ return;
445
+ }
446
+ try {
447
+ // Check if WebRTC is available
448
+ if (typeof RTCPeerConnection === 'undefined') {
449
+ throw new Error('WebRTC is not available in this environment');
450
+ }
451
+ this.isInitialized = true;
452
+ }
453
+ catch (error) {
454
+ console.error(`Failed to initialize ${this.name}:`, error);
455
+ throw new Error(`Failed to initialize ${this.name}: ${error}`);
456
+ }
457
+ }
458
+ /**
459
+ * Establishes a connection to another Brainy instance using WebRTC
460
+ * @param targetSystemId The peer ID or signal server URL
461
+ * @param config Configuration options for the connection
462
+ */
463
+ async establishConnection(targetSystemId, config) {
464
+ await this.ensureInitialized();
465
+ try {
466
+ // For WebRTC, we need to:
467
+ // 1. Connect to a signaling server (if not already connected)
468
+ // 2. Create a peer connection
469
+ // 3. Create a data channel
470
+ // 4. Exchange ICE candidates and SDP offers/answers
471
+ // Check if we need to connect to a signaling server
472
+ if (!this.signalServer && config.signalServerUrl) {
473
+ // Connect to the signaling server
474
+ this.signalServer = await this.connectWebSocket(config.signalServerUrl);
475
+ // Set up message handling for the signaling server
476
+ await this.onWebSocketMessage(this.signalServer.connectionId, async (data) => {
477
+ // Handle signaling messages
478
+ const message = data;
479
+ if (message.type === 'ice-candidate' && message.targetPeerId === config.localPeerId) {
480
+ // Add ICE candidate to the appropriate peer connection
481
+ const peerConnection = this.peerConnections.get(message.sourcePeerId);
482
+ if (peerConnection) {
483
+ try {
484
+ await peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
485
+ }
486
+ catch (error) {
487
+ console.error(`Failed to add ICE candidate:`, error);
488
+ }
489
+ }
490
+ }
491
+ else if (message.type === 'offer' && message.targetPeerId === config.localPeerId) {
492
+ // Handle incoming offer
493
+ await this.handleOffer(message.sourcePeerId, message.offer, config);
494
+ }
495
+ else if (message.type === 'answer' && message.targetPeerId === config.localPeerId) {
496
+ // Handle incoming answer
497
+ const peerConnection = this.peerConnections.get(message.sourcePeerId);
498
+ if (peerConnection) {
499
+ try {
500
+ await peerConnection.setRemoteDescription(new RTCSessionDescription(message.answer));
501
+ }
502
+ catch (error) {
503
+ console.error(`Failed to set remote description:`, error);
504
+ }
505
+ }
506
+ }
507
+ });
508
+ }
509
+ // Create a peer connection
510
+ const peerConnection = new RTCPeerConnection({
511
+ iceServers: config.iceServers || [
512
+ { urls: 'stun:stun.l.google.com:19302' }
513
+ ]
514
+ });
515
+ // Generate a connection ID
516
+ const connectionId = uuidv4();
517
+ // Store the peer connection
518
+ this.peerConnections.set(targetSystemId, peerConnection);
519
+ // Create a data channel
520
+ const dataChannel = peerConnection.createDataChannel('brainy-sync', {
521
+ ordered: true
522
+ });
523
+ // Set up data channel event handlers
524
+ dataChannel.onopen = () => {
525
+ console.log(`Data channel to ${targetSystemId} opened`);
526
+ };
527
+ dataChannel.onclose = () => {
528
+ console.log(`Data channel to ${targetSystemId} closed`);
529
+ // Clean up
530
+ this.dataChannels.delete(targetSystemId);
531
+ this.peerConnections.delete(targetSystemId);
532
+ this.webSocketConnections.delete(connectionId);
533
+ this.messageCallbacks.delete(connectionId);
534
+ };
535
+ dataChannel.onerror = (error) => {
536
+ console.error(`Data channel error:`, error);
537
+ };
538
+ // Create a message handler wrapper that will call all registered callbacks
539
+ const messageHandlerWrapper = (data) => {
540
+ const callbacks = this.messageCallbacks.get(connectionId);
541
+ if (callbacks) {
542
+ for (const callback of callbacks) {
543
+ try {
544
+ callback(data);
545
+ }
546
+ catch (error) {
547
+ console.error(`Error in WebRTC message callback:`, error);
548
+ }
549
+ }
550
+ }
551
+ };
552
+ dataChannel.onmessage = (event) => {
553
+ try {
554
+ // Parse the message if it's a string
555
+ let data = event.data;
556
+ if (typeof data === 'string') {
557
+ try {
558
+ data = JSON.parse(data);
559
+ }
560
+ catch {
561
+ // If parsing fails, use the raw string
562
+ }
563
+ }
564
+ // Call the message handler wrapper
565
+ messageHandlerWrapper(data);
566
+ }
567
+ catch (error) {
568
+ console.error(`Error handling WebRTC message:`, error);
569
+ }
570
+ };
571
+ // Store the data channel
572
+ this.dataChannels.set(targetSystemId, dataChannel);
573
+ // Set up ICE candidate handling
574
+ peerConnection.onicecandidate = (event) => {
575
+ if (event.candidate && this.signalServer) {
576
+ // Send the ICE candidate to the peer via the signaling server
577
+ this.sendWebSocketMessage(this.signalServer.connectionId, {
578
+ type: 'ice-candidate',
579
+ sourcePeerId: config.localPeerId,
580
+ targetPeerId: targetSystemId,
581
+ candidate: event.candidate
582
+ });
583
+ }
584
+ };
585
+ // Create a WebSocket-like connection object for the WebRTC connection
586
+ const connection = {
587
+ connectionId,
588
+ url: `webrtc://${targetSystemId}`,
589
+ status: 'disconnected',
590
+ send: async (data) => {
591
+ const dc = this.dataChannels.get(targetSystemId);
592
+ if (!dc || dc.readyState !== 'open') {
593
+ throw new Error('WebRTC data channel is not open');
594
+ }
595
+ // Send the data
596
+ if (typeof data === 'string') {
597
+ dc.send(data);
598
+ }
599
+ else if (data instanceof Blob) {
600
+ dc.send(data);
601
+ }
602
+ else if (data instanceof ArrayBuffer) {
603
+ dc.send(new Uint8Array(data));
604
+ }
605
+ else if (ArrayBuffer.isView(data)) {
606
+ dc.send(data);
607
+ }
608
+ else {
609
+ // Convert to JSON string
610
+ dc.send(JSON.stringify(data));
611
+ }
612
+ },
613
+ close: async () => {
614
+ const dc = this.dataChannels.get(targetSystemId);
615
+ if (dc) {
616
+ dc.close();
617
+ }
618
+ const pc = this.peerConnections.get(targetSystemId);
619
+ if (pc) {
620
+ pc.close();
621
+ }
622
+ // Clean up
623
+ this.dataChannels.delete(targetSystemId);
624
+ this.peerConnections.delete(targetSystemId);
625
+ this.webSocketConnections.delete(connectionId);
626
+ this.messageCallbacks.delete(connectionId);
627
+ },
628
+ _messageHandlerWrapper: messageHandlerWrapper
629
+ };
630
+ // Store the connection
631
+ this.webSocketConnections.set(connectionId, connection);
632
+ // Initialize the callbacks set
633
+ this.messageCallbacks.set(connectionId, new Set());
634
+ // Create and send an offer
635
+ const offer = await peerConnection.createOffer();
636
+ await peerConnection.setLocalDescription(offer);
637
+ // Send the offer to the peer via the signaling server
638
+ if (this.signalServer) {
639
+ await this.sendWebSocketMessage(this.signalServer.connectionId, {
640
+ type: 'offer',
641
+ sourcePeerId: config.localPeerId,
642
+ targetPeerId: targetSystemId,
643
+ offer
644
+ });
645
+ }
646
+ // Return the connection
647
+ return {
648
+ success: true,
649
+ data: connection
650
+ };
651
+ }
652
+ catch (error) {
653
+ console.error(`Failed to establish WebRTC connection to ${targetSystemId}:`, error);
654
+ return {
655
+ success: false,
656
+ data: null,
657
+ error: `Failed to establish WebRTC connection: ${error}`
658
+ };
659
+ }
660
+ }
661
+ /**
662
+ * Handles an incoming WebRTC offer
663
+ * @param peerId The ID of the peer sending the offer
664
+ * @param offer The SDP offer
665
+ * @param config Configuration options
666
+ */
667
+ async handleOffer(peerId, offer, config) {
668
+ try {
669
+ // Create a peer connection if it doesn't exist
670
+ let peerConnection = this.peerConnections.get(peerId);
671
+ if (!peerConnection) {
672
+ peerConnection = new RTCPeerConnection({
673
+ iceServers: config.iceServers || [
674
+ { urls: 'stun:stun.l.google.com:19302' }
675
+ ]
676
+ });
677
+ // Store the peer connection
678
+ this.peerConnections.set(peerId, peerConnection);
679
+ // Set up ICE candidate handling
680
+ peerConnection.onicecandidate = (event) => {
681
+ if (event.candidate && this.signalServer) {
682
+ // Send the ICE candidate to the peer via the signaling server
683
+ this.sendWebSocketMessage(this.signalServer.connectionId, {
684
+ type: 'ice-candidate',
685
+ sourcePeerId: config.localPeerId,
686
+ targetPeerId: peerId,
687
+ candidate: event.candidate
688
+ });
689
+ }
690
+ };
691
+ // Handle data channel creation by the remote peer
692
+ peerConnection.ondatachannel = (event) => {
693
+ const dataChannel = event.channel;
694
+ // Generate a connection ID
695
+ const connectionId = uuidv4();
696
+ // Store the data channel
697
+ this.dataChannels.set(peerId, dataChannel);
698
+ // Set up data channel event handlers
699
+ dataChannel.onopen = () => {
700
+ console.log(`Data channel from ${peerId} opened`);
701
+ };
702
+ dataChannel.onclose = () => {
703
+ console.log(`Data channel from ${peerId} closed`);
704
+ // Clean up
705
+ this.dataChannels.delete(peerId);
706
+ this.peerConnections.delete(peerId);
707
+ this.webSocketConnections.delete(connectionId);
708
+ this.messageCallbacks.delete(connectionId);
709
+ };
710
+ dataChannel.onerror = (error) => {
711
+ console.error(`Data channel error:`, error);
712
+ };
713
+ // Create a message handler wrapper that will call all registered callbacks
714
+ const messageHandlerWrapper = (data) => {
715
+ const callbacks = this.messageCallbacks.get(connectionId);
716
+ if (callbacks) {
717
+ for (const callback of callbacks) {
718
+ try {
719
+ callback(data);
720
+ }
721
+ catch (error) {
722
+ console.error(`Error in WebRTC message callback:`, error);
723
+ }
724
+ }
725
+ }
726
+ };
727
+ dataChannel.onmessage = (event) => {
728
+ try {
729
+ // Parse the message if it's a string
730
+ let data = event.data;
731
+ if (typeof data === 'string') {
732
+ try {
733
+ data = JSON.parse(data);
734
+ }
735
+ catch {
736
+ // If parsing fails, use the raw string
737
+ }
738
+ }
739
+ // Call the message handler wrapper
740
+ messageHandlerWrapper(data);
741
+ }
742
+ catch (error) {
743
+ console.error(`Error handling WebRTC message:`, error);
744
+ }
745
+ };
746
+ // Create a WebSocket-like connection object for the WebRTC connection
747
+ const connection = {
748
+ connectionId,
749
+ url: `webrtc://${peerId}`,
750
+ status: 'disconnected',
751
+ send: async (data) => {
752
+ if (dataChannel.readyState !== 'open') {
753
+ throw new Error('WebRTC data channel is not open');
754
+ }
755
+ // Send the data
756
+ if (typeof data === 'string') {
757
+ dataChannel.send(data);
758
+ }
759
+ else if (data instanceof Blob) {
760
+ dataChannel.send(data);
761
+ }
762
+ else if (data instanceof ArrayBuffer) {
763
+ dataChannel.send(new Uint8Array(data));
764
+ }
765
+ else if (ArrayBuffer.isView(data)) {
766
+ dataChannel.send(data);
767
+ }
768
+ else {
769
+ // Convert to JSON string
770
+ dataChannel.send(JSON.stringify(data));
771
+ }
772
+ },
773
+ close: async () => {
774
+ dataChannel.close();
775
+ const pc = this.peerConnections.get(peerId);
776
+ if (pc) {
777
+ pc.close();
778
+ }
779
+ // Clean up
780
+ this.dataChannels.delete(peerId);
781
+ this.peerConnections.delete(peerId);
782
+ this.webSocketConnections.delete(connectionId);
783
+ this.messageCallbacks.delete(connectionId);
784
+ },
785
+ _messageHandlerWrapper: messageHandlerWrapper
786
+ };
787
+ // Store the connection
788
+ this.webSocketConnections.set(connectionId, connection);
789
+ // Initialize the callbacks set
790
+ this.messageCallbacks.set(connectionId, new Set());
791
+ };
792
+ }
793
+ // Set the remote description (the offer)
794
+ await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
795
+ // Create an answer
796
+ const answer = await peerConnection.createAnswer();
797
+ await peerConnection.setLocalDescription(answer);
798
+ // Send the answer to the peer via the signaling server
799
+ if (this.signalServer) {
800
+ await this.sendWebSocketMessage(this.signalServer.connectionId, {
801
+ type: 'answer',
802
+ sourcePeerId: config.localPeerId,
803
+ targetPeerId: peerId,
804
+ answer
805
+ });
806
+ }
807
+ }
808
+ catch (error) {
809
+ console.error(`Failed to handle WebRTC offer:`, error);
810
+ throw new Error(`Failed to handle WebRTC offer: ${error}`);
811
+ }
812
+ }
813
+ /**
814
+ * Reads data from a connected Brainy instance
815
+ * @param query Query parameters for reading data
816
+ * @param options Additional options
817
+ */
818
+ async readData(query, options) {
819
+ await this.ensureInitialized();
820
+ try {
821
+ const connectionId = query.connectionId;
822
+ if (!connectionId) {
823
+ throw new Error('connectionId is required for reading data');
824
+ }
825
+ const connection = this.webSocketConnections.get(connectionId);
826
+ if (!connection) {
827
+ throw new Error(`Connection ${connectionId} not found`);
828
+ }
829
+ // Create a request message
830
+ const requestMessage = {
831
+ type: 'read',
832
+ query: query.query || {},
833
+ requestId: uuidv4(),
834
+ options
835
+ };
836
+ // Send the request
837
+ await this.sendWebSocketMessage(connectionId, requestMessage);
838
+ // Return a promise that will be resolved when the response is received
839
+ return new Promise((resolve) => {
840
+ const responseHandler = (data) => {
841
+ // Check if this is the response to our request
842
+ const response = data;
843
+ if (response && response.type === 'readResponse' && response.requestId === requestMessage.requestId) {
844
+ // Remove the handler
845
+ this.offWebSocketMessage(connectionId, responseHandler);
846
+ // Resolve with the response data
847
+ resolve({
848
+ success: response.success,
849
+ data: response.data,
850
+ error: response.error
851
+ });
852
+ }
853
+ };
854
+ // Register the response handler
855
+ this.onWebSocketMessage(connectionId, responseHandler);
856
+ // Set a timeout to prevent hanging
857
+ setTimeout(() => {
858
+ this.offWebSocketMessage(connectionId, responseHandler);
859
+ resolve({
860
+ success: false,
861
+ data: null,
862
+ error: 'Timeout waiting for read response'
863
+ });
864
+ }, 30000); // 30 second timeout
865
+ });
866
+ }
867
+ catch (error) {
868
+ console.error(`Failed to read data:`, error);
869
+ return {
870
+ success: false,
871
+ data: null,
872
+ error: `Failed to read data: ${error}`
873
+ };
874
+ }
875
+ }
876
+ /**
877
+ * Writes data to a connected Brainy instance
878
+ * @param data The data to write
879
+ * @param options Additional options
880
+ */
881
+ async writeData(data, options) {
882
+ await this.ensureInitialized();
883
+ try {
884
+ const connectionId = data.connectionId;
885
+ if (!connectionId) {
886
+ throw new Error('connectionId is required for writing data');
887
+ }
888
+ const connection = this.webSocketConnections.get(connectionId);
889
+ if (!connection) {
890
+ throw new Error(`Connection ${connectionId} not found`);
891
+ }
892
+ // Create a write message
893
+ const writeMessage = {
894
+ type: 'write',
895
+ data: data.data || {},
896
+ requestId: uuidv4(),
897
+ options
898
+ };
899
+ // Send the write message
900
+ await this.sendWebSocketMessage(connectionId, writeMessage);
901
+ // Return a promise that will be resolved when the response is received
902
+ return new Promise((resolve) => {
903
+ const responseHandler = (data) => {
904
+ // Check if this is the response to our request
905
+ const response = data;
906
+ if (response && response.type === 'writeResponse' && response.requestId === writeMessage.requestId) {
907
+ // Remove the handler
908
+ this.offWebSocketMessage(connectionId, responseHandler);
909
+ // Resolve with the response data
910
+ resolve({
911
+ success: response.success,
912
+ data: response.data,
913
+ error: response.error
914
+ });
915
+ }
916
+ };
917
+ // Register the response handler
918
+ this.onWebSocketMessage(connectionId, responseHandler);
919
+ // Set a timeout to prevent hanging
920
+ setTimeout(() => {
921
+ this.offWebSocketMessage(connectionId, responseHandler);
922
+ resolve({
923
+ success: false,
924
+ data: null,
925
+ error: 'Timeout waiting for write response'
926
+ });
927
+ }, 30000); // 30 second timeout
928
+ });
929
+ }
930
+ catch (error) {
931
+ console.error(`Failed to write data:`, error);
932
+ return {
933
+ success: false,
934
+ data: null,
935
+ error: `Failed to write data: ${error}`
936
+ };
937
+ }
938
+ }
939
+ /**
940
+ * Monitors a data stream from a connected Brainy instance
941
+ * @param streamId The ID of the stream to monitor (usually a connection ID)
942
+ * @param callback Function to call when new data is received
943
+ */
944
+ async monitorStream(streamId, callback) {
945
+ await this.ensureInitialized();
946
+ try {
947
+ const connection = this.webSocketConnections.get(streamId);
948
+ if (!connection) {
949
+ throw new Error(`Connection ${streamId} not found`);
950
+ }
951
+ // Register the callback for all messages on this connection
952
+ await this.onWebSocketMessage(streamId, callback);
953
+ }
954
+ catch (error) {
955
+ console.error(`Failed to monitor stream ${streamId}:`, error);
956
+ throw new Error(`Failed to monitor stream: ${error}`);
957
+ }
958
+ }
959
+ /**
960
+ * Establishes a WebSocket connection (used for signaling in WebRTC)
961
+ * @param url The WebSocket server URL to connect to
962
+ * @param protocols Optional subprotocols
963
+ */
964
+ async connectWebSocket(url, protocols) {
965
+ await this.ensureInitialized();
966
+ return new Promise((resolve, reject) => {
967
+ try {
968
+ // Check if WebSocket is available
969
+ if (typeof WebSocket === 'undefined') {
970
+ throw new Error('WebSocket is not available in this environment');
971
+ }
972
+ // Create a new WebSocket connection
973
+ const ws = new WebSocket(url, protocols);
974
+ const connectionId = uuidv4();
975
+ // Create a connection object
976
+ const connection = {
977
+ connectionId,
978
+ url,
979
+ status: 'disconnected',
980
+ send: async (data) => {
981
+ if (ws.readyState !== WebSocket.OPEN) {
982
+ throw new Error('WebSocket is not open');
983
+ }
984
+ ws.send(data);
985
+ },
986
+ close: async () => {
987
+ ws.close();
988
+ }
989
+ };
990
+ // Set up event handlers
991
+ ws.onopen = () => {
992
+ connection.status = 'connected';
993
+ resolve(connection);
994
+ };
995
+ ws.onerror = (error) => {
996
+ connection.status = 'error';
997
+ console.error(`WebSocket error for ${url}:`, error);
998
+ if (ws.readyState !== WebSocket.OPEN) {
999
+ reject(new Error(`WebSocket connection failed: ${error}`));
1000
+ }
1001
+ };
1002
+ ws.onclose = () => {
1003
+ connection.status = 'disconnected';
1004
+ // Remove from connections map
1005
+ this.webSocketConnections.delete(connectionId);
1006
+ // Remove all callbacks
1007
+ this.messageCallbacks.delete(connectionId);
1008
+ };
1009
+ // Create a message handler wrapper that will call all registered callbacks
1010
+ const messageHandlerWrapper = (data) => {
1011
+ const callbacks = this.messageCallbacks.get(connectionId);
1012
+ if (callbacks) {
1013
+ for (const callback of callbacks) {
1014
+ try {
1015
+ callback(data);
1016
+ }
1017
+ catch (error) {
1018
+ console.error(`Error in WebSocket message callback:`, error);
1019
+ }
1020
+ }
1021
+ }
1022
+ };
1023
+ // Store the message handler wrapper
1024
+ connection._messageHandlerWrapper = messageHandlerWrapper;
1025
+ // Set up the message handler
1026
+ ws.onmessage = (event) => {
1027
+ try {
1028
+ // Parse the message if it's a string
1029
+ let data = event.data;
1030
+ if (typeof data === 'string') {
1031
+ try {
1032
+ data = JSON.parse(data);
1033
+ }
1034
+ catch {
1035
+ // If parsing fails, use the raw string
1036
+ }
1037
+ }
1038
+ // Call the message handler wrapper
1039
+ messageHandlerWrapper(data);
1040
+ }
1041
+ catch (error) {
1042
+ console.error(`Error handling WebSocket message:`, error);
1043
+ }
1044
+ };
1045
+ // Store the stream message handler
1046
+ connection._streamMessageHandler = (event) => ws.onmessage && ws.onmessage(event);
1047
+ // Store the connection
1048
+ this.webSocketConnections.set(connectionId, connection);
1049
+ // Initialize the callbacks set
1050
+ this.messageCallbacks.set(connectionId, new Set());
1051
+ }
1052
+ catch (error) {
1053
+ reject(error);
1054
+ }
1055
+ });
1056
+ }
1057
+ /**
1058
+ * Sends data through an established WebSocket or WebRTC connection
1059
+ * @param connectionId The identifier of the established connection
1060
+ * @param data The data to send (will be serialized if not a string)
1061
+ */
1062
+ async sendWebSocketMessage(connectionId, data) {
1063
+ await this.ensureInitialized();
1064
+ const connection = this.webSocketConnections.get(connectionId);
1065
+ if (!connection) {
1066
+ throw new Error(`Connection ${connectionId} not found`);
1067
+ }
1068
+ if (!connection.send) {
1069
+ throw new Error(`Connection ${connectionId} does not support sending messages`);
1070
+ }
1071
+ // Serialize the data if it's not already a string or binary
1072
+ let serializedData;
1073
+ if (typeof data === 'string' ||
1074
+ data instanceof ArrayBuffer ||
1075
+ data instanceof Blob ||
1076
+ ArrayBuffer.isView(data)) {
1077
+ serializedData = data;
1078
+ }
1079
+ else {
1080
+ // Convert to JSON string
1081
+ serializedData = JSON.stringify(data);
1082
+ }
1083
+ // Send the data
1084
+ await connection.send(serializedData);
1085
+ }
1086
+ /**
1087
+ * Registers a callback for incoming WebSocket or WebRTC messages
1088
+ * @param connectionId The identifier of the established connection
1089
+ * @param callback The function to call when a message is received
1090
+ */
1091
+ async onWebSocketMessage(connectionId, callback) {
1092
+ await this.ensureInitialized();
1093
+ const connection = this.webSocketConnections.get(connectionId);
1094
+ if (!connection) {
1095
+ throw new Error(`Connection ${connectionId} not found`);
1096
+ }
1097
+ // Get or create the callbacks set for this connection
1098
+ let callbacks = this.messageCallbacks.get(connectionId);
1099
+ if (!callbacks) {
1100
+ callbacks = new Set();
1101
+ this.messageCallbacks.set(connectionId, callbacks);
1102
+ }
1103
+ // Add the callback
1104
+ callbacks.add(callback);
1105
+ }
1106
+ /**
1107
+ * Removes a callback for incoming WebSocket or WebRTC messages
1108
+ * @param connectionId The identifier of the established connection
1109
+ * @param callback The function to remove from the callbacks
1110
+ */
1111
+ async offWebSocketMessage(connectionId, callback) {
1112
+ await this.ensureInitialized();
1113
+ const callbacks = this.messageCallbacks.get(connectionId);
1114
+ if (callbacks) {
1115
+ callbacks.delete(callback);
1116
+ }
1117
+ }
1118
+ /**
1119
+ * Closes an established WebSocket or WebRTC connection
1120
+ * @param connectionId The identifier of the established connection
1121
+ * @param code Optional close code
1122
+ * @param reason Optional close reason
1123
+ */
1124
+ async closeWebSocket(connectionId, code, reason) {
1125
+ await this.ensureInitialized();
1126
+ const connection = this.webSocketConnections.get(connectionId);
1127
+ if (!connection) {
1128
+ throw new Error(`Connection ${connectionId} not found`);
1129
+ }
1130
+ if (!connection.close) {
1131
+ throw new Error(`Connection ${connectionId} does not support closing`);
1132
+ }
1133
+ // Close the connection
1134
+ await connection.close();
1135
+ // Remove from connections map
1136
+ this.webSocketConnections.delete(connectionId);
1137
+ // Remove all callbacks
1138
+ this.messageCallbacks.delete(connectionId);
1139
+ }
1140
+ }
1141
+ /**
1142
+ * Factory function to create the appropriate conduit augmentation based on the type
1143
+ */
1144
+ export async function createConduitAugmentation(type, name, options = {}) {
1145
+ switch (type) {
1146
+ case 'websocket':
1147
+ const wsAugmentation = new WebSocketConduitAugmentation(name || 'websocket-conduit');
1148
+ await wsAugmentation.initialize();
1149
+ return wsAugmentation;
1150
+ case 'webrtc':
1151
+ const webrtcAugmentation = new WebRTCConduitAugmentation(name || 'webrtc-conduit');
1152
+ await webrtcAugmentation.initialize();
1153
+ return webrtcAugmentation;
1154
+ default:
1155
+ throw new Error(`Unknown conduit augmentation type: ${type}`);
1156
+ }
1157
+ }
1158
+ //# sourceMappingURL=conduitAugmentations.js.map