@cmdoss/memwal-sdk 0.7.0 → 0.8.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 (192) hide show
  1. package/README.md +129 -0
  2. package/dist/client/ClientMemoryManager.js +2 -2
  3. package/dist/client/ClientMemoryManager.js.map +1 -1
  4. package/dist/client/PersonalDataWallet.d.ts.map +1 -1
  5. package/dist/client/SimplePDWClient.d.ts +28 -0
  6. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  7. package/dist/client/SimplePDWClient.js +29 -6
  8. package/dist/client/SimplePDWClient.js.map +1 -1
  9. package/dist/client/namespaces/MemoryNamespace.d.ts +4 -0
  10. package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
  11. package/dist/client/namespaces/MemoryNamespace.js +168 -39
  12. package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
  13. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts +12 -2
  14. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
  15. package/dist/client/namespaces/consolidated/BlockchainNamespace.js +40 -2
  16. package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
  17. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +67 -2
  18. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
  19. package/dist/client/namespaces/consolidated/StorageNamespace.js +549 -16
  20. package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
  21. package/dist/config/ConfigurationHelper.js +61 -61
  22. package/dist/config/defaults.js +2 -2
  23. package/dist/config/defaults.js.map +1 -1
  24. package/dist/graph/GraphService.js +20 -20
  25. package/dist/infrastructure/seal/EncryptionService.d.ts +9 -5
  26. package/dist/infrastructure/seal/EncryptionService.d.ts.map +1 -1
  27. package/dist/infrastructure/seal/EncryptionService.js +37 -15
  28. package/dist/infrastructure/seal/EncryptionService.js.map +1 -1
  29. package/dist/infrastructure/seal/SealService.d.ts +13 -5
  30. package/dist/infrastructure/seal/SealService.d.ts.map +1 -1
  31. package/dist/infrastructure/seal/SealService.js +36 -34
  32. package/dist/infrastructure/seal/SealService.js.map +1 -1
  33. package/dist/langchain/createPDWRAG.js +30 -30
  34. package/dist/retrieval/MemoryDecryptionPipeline.d.ts.map +1 -1
  35. package/dist/retrieval/MemoryDecryptionPipeline.js +2 -1
  36. package/dist/retrieval/MemoryDecryptionPipeline.js.map +1 -1
  37. package/dist/services/CapabilityService.d.ts.map +1 -1
  38. package/dist/services/CapabilityService.js +30 -14
  39. package/dist/services/CapabilityService.js.map +1 -1
  40. package/dist/services/CrossContextPermissionService.d.ts.map +1 -1
  41. package/dist/services/CrossContextPermissionService.js +9 -7
  42. package/dist/services/CrossContextPermissionService.js.map +1 -1
  43. package/dist/services/EncryptionService.d.ts.map +1 -1
  44. package/dist/services/EncryptionService.js +6 -5
  45. package/dist/services/EncryptionService.js.map +1 -1
  46. package/dist/services/GeminiAIService.js +309 -309
  47. package/dist/services/StorageService.d.ts +1 -0
  48. package/dist/services/StorageService.d.ts.map +1 -1
  49. package/dist/services/StorageService.js +60 -10
  50. package/dist/services/StorageService.js.map +1 -1
  51. package/dist/services/TransactionService.d.ts +20 -0
  52. package/dist/services/TransactionService.d.ts.map +1 -1
  53. package/dist/services/TransactionService.js +43 -0
  54. package/dist/services/TransactionService.js.map +1 -1
  55. package/dist/services/ViewService.js +2 -2
  56. package/dist/services/ViewService.js.map +1 -1
  57. package/package.json +1 -1
  58. package/src/access/PermissionService.ts +635 -635
  59. package/src/access/index.ts +8 -8
  60. package/src/aggregation/AggregationService.ts +389 -389
  61. package/src/aggregation/index.ts +8 -8
  62. package/src/ai-sdk/PDWVectorStore.ts +715 -715
  63. package/src/ai-sdk/index.ts +65 -65
  64. package/src/ai-sdk/tools.ts +460 -460
  65. package/src/ai-sdk/types.ts +404 -404
  66. package/src/batch/BatchManager.ts +597 -597
  67. package/src/batch/BatchingService.ts +429 -429
  68. package/src/batch/MemoryProcessingCache.ts +492 -492
  69. package/src/batch/index.ts +30 -30
  70. package/src/browser.ts +200 -200
  71. package/src/client/ClientMemoryManager.ts +987 -987
  72. package/src/client/PersonalDataWallet.ts +345 -345
  73. package/src/client/SimplePDWClient.ts +1289 -1237
  74. package/src/client/factory.ts +154 -154
  75. package/src/client/namespaces/AnalyticsNamespace.ts +377 -377
  76. package/src/client/namespaces/BatchNamespace.ts +356 -356
  77. package/src/client/namespaces/CacheNamespace.ts +123 -123
  78. package/src/client/namespaces/CapabilityNamespace.ts +217 -217
  79. package/src/client/namespaces/ClassifyNamespace.ts +169 -169
  80. package/src/client/namespaces/ContextNamespace.ts +297 -297
  81. package/src/client/namespaces/EmbeddingsNamespace.ts +99 -99
  82. package/src/client/namespaces/EncryptionNamespace.ts +221 -221
  83. package/src/client/namespaces/GraphNamespace.ts +468 -468
  84. package/src/client/namespaces/IndexNamespace.ts +361 -361
  85. package/src/client/namespaces/MemoryNamespace.ts +1422 -1272
  86. package/src/client/namespaces/PermissionsNamespace.ts +254 -254
  87. package/src/client/namespaces/PipelineNamespace.ts +220 -220
  88. package/src/client/namespaces/SearchNamespace.ts +1049 -1049
  89. package/src/client/namespaces/StorageNamespace.ts +458 -458
  90. package/src/client/namespaces/TxNamespace.ts +260 -260
  91. package/src/client/namespaces/WalletNamespace.ts +243 -243
  92. package/src/client/namespaces/consolidated/AINamespace.ts +449 -449
  93. package/src/client/namespaces/consolidated/BlockchainNamespace.ts +607 -564
  94. package/src/client/namespaces/consolidated/SecurityNamespace.ts +648 -648
  95. package/src/client/namespaces/consolidated/StorageNamespace.ts +1141 -497
  96. package/src/client/namespaces/consolidated/index.ts +39 -39
  97. package/src/client/signers/DappKitSigner.ts +207 -207
  98. package/src/client/signers/KeypairSigner.ts +108 -108
  99. package/src/client/signers/UnifiedSigner.ts +110 -110
  100. package/src/client/signers/WalletAdapterSigner.ts +159 -159
  101. package/src/client/signers/index.ts +26 -26
  102. package/src/config/ConfigurationHelper.ts +412 -412
  103. package/src/config/defaults.ts +51 -51
  104. package/src/config/index.ts +8 -8
  105. package/src/config/validation.ts +70 -70
  106. package/src/core/index.ts +14 -14
  107. package/src/core/interfaces/IService.ts +307 -307
  108. package/src/core/interfaces/index.ts +8 -8
  109. package/src/core/types/capability.ts +297 -297
  110. package/src/core/types/index.ts +870 -870
  111. package/src/core/types/wallet.ts +270 -270
  112. package/src/core/types.ts +9 -9
  113. package/src/core/wallet.ts +222 -222
  114. package/src/embedding/index.ts +19 -19
  115. package/src/embedding/types.ts +357 -357
  116. package/src/errors/index.ts +602 -602
  117. package/src/errors/recovery.ts +461 -461
  118. package/src/errors/validation.ts +567 -567
  119. package/src/generated/pdw/capability.ts +319 -319
  120. package/src/generated/pdw/deps/sui/object.ts +12 -12
  121. package/src/generated/pdw/deps/sui/vec_map.ts +32 -32
  122. package/src/generated/pdw/memory.ts +1087 -1087
  123. package/src/generated/pdw/wallet.ts +123 -123
  124. package/src/generated/utils/index.ts +159 -159
  125. package/src/graph/GraphService.ts +887 -887
  126. package/src/graph/KnowledgeGraphManager.ts +728 -728
  127. package/src/graph/index.ts +25 -25
  128. package/src/index.ts +498 -498
  129. package/src/infrastructure/index.ts +22 -22
  130. package/src/infrastructure/seal/EncryptionService.ts +628 -603
  131. package/src/infrastructure/seal/SealService.ts +613 -615
  132. package/src/infrastructure/seal/index.ts +9 -9
  133. package/src/infrastructure/sui/BlockchainManager.ts +627 -627
  134. package/src/infrastructure/sui/SuiService.ts +888 -888
  135. package/src/infrastructure/sui/index.ts +9 -9
  136. package/src/infrastructure/walrus/StorageManager.ts +604 -604
  137. package/src/infrastructure/walrus/WalrusStorageService.ts +612 -612
  138. package/src/infrastructure/walrus/index.ts +9 -9
  139. package/src/langchain/PDWEmbeddings.ts +145 -145
  140. package/src/langchain/PDWVectorStore.ts +456 -456
  141. package/src/langchain/createPDWRAG.ts +303 -303
  142. package/src/langchain/index.ts +47 -47
  143. package/src/permissions/ConsentRepository.browser.ts +249 -249
  144. package/src/permissions/ConsentRepository.ts +364 -364
  145. package/src/permissions/index.ts +9 -9
  146. package/src/pipeline/MemoryPipeline.ts +862 -862
  147. package/src/pipeline/PipelineManager.ts +683 -683
  148. package/src/pipeline/index.ts +26 -26
  149. package/src/retrieval/AdvancedSearchService.ts +629 -629
  150. package/src/retrieval/MemoryAnalyticsService.ts +711 -711
  151. package/src/retrieval/MemoryDecryptionPipeline.ts +825 -824
  152. package/src/retrieval/MemoryRetrievalService.ts +904 -904
  153. package/src/retrieval/index.ts +42 -42
  154. package/src/services/BatchService.ts +352 -352
  155. package/src/services/CapabilityService.ts +464 -448
  156. package/src/services/ClassifierService.ts +465 -465
  157. package/src/services/CrossContextPermissionService.ts +486 -484
  158. package/src/services/EmbeddingService.ts +771 -771
  159. package/src/services/EncryptionService.ts +712 -711
  160. package/src/services/GeminiAIService.ts +753 -753
  161. package/src/services/IndexManager.ts +977 -977
  162. package/src/services/MemoryIndexService.ts +1003 -1003
  163. package/src/services/MemoryService.ts +369 -369
  164. package/src/services/QueryService.ts +890 -890
  165. package/src/services/StorageService.ts +1182 -1126
  166. package/src/services/TransactionService.ts +838 -790
  167. package/src/services/VectorService.ts +462 -462
  168. package/src/services/ViewService.ts +484 -484
  169. package/src/services/index.ts +25 -25
  170. package/src/services/storage/BlobAttributesManager.ts +333 -333
  171. package/src/services/storage/KnowledgeGraphManager.ts +425 -425
  172. package/src/services/storage/MemorySearchManager.ts +387 -387
  173. package/src/services/storage/QuiltBatchManager.ts +1130 -1130
  174. package/src/services/storage/WalrusMetadataManager.ts +268 -268
  175. package/src/services/storage/WalrusStorageManager.ts +287 -287
  176. package/src/services/storage/index.ts +57 -57
  177. package/src/types/index.ts +13 -13
  178. package/src/utils/LRUCache.ts +378 -378
  179. package/src/utils/index.ts +76 -76
  180. package/src/utils/memoryIndexOnChain.ts +507 -507
  181. package/src/utils/rebuildIndex.ts +290 -290
  182. package/src/utils/rebuildIndexNode.ts +771 -771
  183. package/src/vector/BrowserHnswIndexService.ts +758 -758
  184. package/src/vector/HnswWasmService.ts +731 -731
  185. package/src/vector/IHnswService.ts +233 -233
  186. package/src/vector/NodeHnswService.ts +833 -833
  187. package/src/vector/VectorManager.ts +478 -478
  188. package/src/vector/createHnswService.ts +135 -135
  189. package/src/vector/index.ts +56 -56
  190. package/src/wallet/ContextWalletService.ts +656 -656
  191. package/src/wallet/MainWalletService.ts +317 -317
  192. package/src/wallet/index.ts +17 -17
@@ -1,616 +1,614 @@
1
- /**
2
- * SEAL Service Integration
3
- *
4
- * Production-ready SEAL service wrapper with comprehensive error handling,
5
- * session management, and performance analytics integration.
6
- */
7
-
8
- import { SuiClient } from '@mysten/sui/client';
9
- import { Transaction } from '@mysten/sui/transactions';
10
- import { fromHex, toHex, normalizeSuiAddress } from '@mysten/sui/utils';
11
- import { SealClient, SessionKey, EncryptedObject } from '@mysten/seal';
12
- import type {
13
- SealClientOptions,
14
- EncryptOptions,
15
- DecryptOptions,
16
- KeyServerConfig
17
- } from '@mysten/seal';
18
-
19
- // SEAL SDK types and interfaces
20
- interface SealConfig {
21
- suiClient: SuiClient;
22
- packageId: string;
23
- keyServerUrls: string[];
24
- keyServerObjectIds: string[];
25
- threshold: number;
26
- network: 'testnet' | 'mainnet';
27
- enableMetrics: boolean;
28
- retryAttempts: number;
29
- timeoutMs: number;
30
- }
31
-
32
- interface EncryptionOptions {
33
- data: Uint8Array;
34
- id: string;
35
- threshold?: number;
36
- }
37
-
38
- interface DecryptionOptions {
39
- encryptedObject: Uint8Array;
40
- sessionKey: any;
41
- txBytes: Uint8Array;
42
- }
43
-
44
- interface SessionConfig {
45
- address: string;
46
- packageId: string;
47
- ttlMin: number;
48
- }
49
-
50
- interface PerformanceMetric {
51
- operation: string;
52
- startTime: number;
53
- endTime: number;
54
- duration: number;
55
- success: boolean;
56
- errorType?: string;
57
- metadata?: any;
58
- }
59
-
60
- /**
61
- * SEAL Service - Production Implementation
62
- */
63
- export class SealService {
64
- private sealClient: SealClient | null = null;
65
- private sessionKey: SessionKey | null = null;
66
- private config: SealConfig;
67
- private performanceMetrics: PerformanceMetric[] = [];
68
- private activeSessions: Map<string, any> = new Map();
69
-
70
- constructor(config: SealConfig) {
71
- this.config = config;
72
- console.log('✅ SEAL SDK components loaded successfully');
73
- }
74
-
75
- /**
76
- * Initialize SEAL client with retry logic
77
- */
78
- async initializeClient(): Promise<void> {
79
- const metric = this.startMetric('seal_client_init');
80
-
81
- try {
82
- // Configure server configs from testnet servers
83
- const serverConfigs: KeyServerConfig[] = this.config.keyServerObjectIds.map((objectId, index) => ({
84
- objectId,
85
- weight: 1
86
- }));
87
-
88
- const sealClientOptions: SealClientOptions = {
89
- suiClient: this.config.suiClient,
90
- serverConfigs,
91
- verifyKeyServers: this.config.network === 'mainnet' // Only verify on mainnet
92
- };
93
-
94
- this.sealClient = new SealClient(sealClientOptions);
95
-
96
- this.completeMetric(metric, true, { serverCount: serverConfigs.length });
97
- console.log('✅ SEAL client initialized with', serverConfigs.length, 'servers');
98
-
99
- } catch (error) {
100
- this.completeMetric(metric, false, undefined, error);
101
- throw new Error(`Failed to initialize SEAL client: ${error}`);
102
- }
103
- }
104
-
105
- /**
106
- * Create and manage session key
107
- */
108
- async createSession(config: SessionConfig, signature?: Uint8Array | string): Promise<any> {
109
- const metric = this.startMetric('session_creation');
110
-
111
- try {
112
- const sessionKey = await SessionKey.create({
113
- address: config.address,
114
- packageId: config.packageId,
115
- ttlMin: config.ttlMin,
116
- suiClient: this.config.suiClient
117
- });
118
-
119
- const message = sessionKey.getPersonalMessage();
120
-
121
- // In production, signature would come from wallet
122
- if (signature) {
123
- // Convert Uint8Array to hex string if needed
124
- const signatureString = typeof signature === 'string' ? signature : toHex(signature);
125
- await sessionKey.setPersonalMessageSignature(signatureString);
126
- }
127
-
128
- // Store session for management
129
- this.activeSessions.set(config.address, {
130
- sessionKey,
131
- createdAt: Date.now(),
132
- ttl: config.ttlMin * 60 * 1000,
133
- address: config.address
134
- });
135
-
136
- this.completeMetric(metric, true, {
137
- address: config.address,
138
- ttlMin: config.ttlMin,
139
- messageLength: message.length
140
- });
141
-
142
- return { sessionKey, personalMessage: message };
143
-
144
- } catch (error) {
145
- this.completeMetric(metric, false, undefined, error);
146
- throw new Error(`Failed to create session: ${error}`);
147
- }
148
- }
149
-
150
- /**
151
- * Encrypt data with comprehensive error handling
152
- */
153
- async encryptData(options: EncryptionOptions): Promise<{ encryptedObject: Uint8Array; key: Uint8Array }> {
154
- const metric = this.startMetric('encryption');
155
-
156
- try {
157
- if (!this.sealClient) {
158
- await this.initializeClient();
159
- }
160
-
161
- if (!this.sealClient) {
162
- throw new Error('Failed to initialize SEAL client');
163
- }
164
-
165
- const threshold = options.threshold || this.config.threshold;
166
-
167
- // Validate threshold against available servers
168
- if (threshold > this.config.keyServerObjectIds.length) {
169
- throw new Error(`Threshold ${threshold} exceeds available servers ${this.config.keyServerObjectIds.length}`);
170
- }
171
-
172
- const result = await this.sealClient.encrypt({
173
- threshold,
174
- packageId: this.config.packageId,
175
- id: options.id,
176
- data: options.data
177
- });
178
-
179
- this.completeMetric(metric, true, {
180
- dataSize: options.data.length,
181
- threshold,
182
- encryptedSize: result.encryptedObject.length,
183
- keySize: result.key.length
184
- });
185
-
186
- return result;
187
-
188
- } catch (error) {
189
- this.completeMetric(metric, false, undefined, error);
190
-
191
- // Enhanced error handling
192
- const errorMessage = error instanceof Error ? error.message : String(error);
193
- if (errorMessage.includes('threshold')) {
194
- throw new Error(`Encryption threshold error: ${errorMessage}`);
195
- }
196
- if (errorMessage.includes('network')) {
197
- throw new Error(`Network error during encryption: ${errorMessage}`);
198
- }
199
-
200
- throw new Error(`Encryption failed: ${errorMessage}`);
201
- }
202
- }
203
-
204
- /**
205
- * Decrypt data with session management
206
- */
207
- async decryptData(options: DecryptionOptions): Promise<Uint8Array> {
208
- const metric = this.startMetric('decryption');
209
-
210
- try {
211
- if (!this.sealClient) {
212
- await this.initializeClient();
213
- }
214
-
215
- if (!this.sealClient) {
216
- throw new Error('Failed to initialize SEAL client');
217
- }
218
-
219
- // Add detailed debugging for SEAL decryption
220
- console.log('🔍 SEAL decrypt parameters:');
221
- console.log(` - Encrypted data type: ${typeof options.encryptedObject}`);
222
- console.log(` - Encrypted data length: ${options.encryptedObject.length}`);
223
- console.log(` - Session key type: ${typeof options.sessionKey}`);
224
- console.log(` - Session key constructor: ${options.sessionKey?.constructor?.name}`);
225
- console.log(` - Transaction bytes type: ${typeof options.txBytes}`);
226
- console.log(` - Transaction bytes length: ${options.txBytes.length}`);
227
-
228
- // Check if session key has expected methods
229
- if (options.sessionKey) {
230
- console.log(` - Session key methods: ${Object.getOwnPropertyNames(Object.getPrototypeOf(options.sessionKey)).join(', ')}`);
231
- }
232
-
233
- // Try SEAL decryption with error boundary
234
- let result;
235
- try {
236
- console.log('🔍 Attempting SEAL client decrypt...');
237
- result = await this.sealClient.decrypt({
238
- data: options.encryptedObject,
239
- sessionKey: options.sessionKey,
240
- txBytes: options.txBytes
241
- });
242
- console.log('🔍 SEAL decrypt completed without throwing');
243
- } catch (innerError) {
244
- console.log('🔍 SEAL decrypt threw an error:', innerError);
245
- throw innerError;
246
- }
247
-
248
- console.log(`🔍 SEAL decrypt result:`, {
249
- result,
250
- resultType: typeof result,
251
- resultLength: result ? result.length : 'undefined',
252
- hasResult: !!result,
253
- isUint8Array: result instanceof Uint8Array
254
- });
255
-
256
- if (!result) {
257
- throw new Error('SEAL decrypt returned undefined/null result');
258
- }
259
-
260
- this.completeMetric(metric, true, {
261
- encryptedSize: options.encryptedObject.length,
262
- decryptedSize: result ? result.length : 0
263
- });
264
-
265
- return result;
266
-
267
- } catch (error) {
268
- this.completeMetric(metric, false, undefined, error);
269
-
270
- // Enhanced error handling for decryption with detailed logging
271
- console.log('🚨 SEAL decrypt error details:', {
272
- error,
273
- errorType: typeof error,
274
- errorConstructor: error?.constructor?.name,
275
- errorMessage: error instanceof Error ? error.message : String(error),
276
- errorStack: error instanceof Error ? error.stack : 'No stack trace'
277
- });
278
-
279
- const errorMessage = error instanceof Error ? error.message : String(error);
280
- if (errorMessage.includes('access')) {
281
- throw new Error(`Access denied for decryption: ${errorMessage}`);
282
- }
283
- if (errorMessage.includes('session')) {
284
- throw new Error(`Session error during decryption: ${errorMessage}`);
285
- }
286
-
287
- throw new Error(`Decryption failed: ${errorMessage}`);
288
- }
289
- }
290
-
291
- /**
292
- * Create transaction for seal_approve using wallet-based allowlists
293
- * Matches Move signature: seal_approve(id: vector<u8>, requesting_wallet: address, ...)
294
- */
295
- async createSealApproveTransaction(
296
- id: string,
297
- userAddress: string,
298
- requestingWallet: string,
299
- accessRegistry?: string
300
- ): Promise<Uint8Array> {
301
- const metric = this.startMetric('transaction_creation');
302
-
303
- try {
304
- const tx = new Transaction();
305
-
306
- // Use the deployed AccessRegistry ID from environment or parameter
307
- const registryId = accessRegistry || process.env.ACCESS_REGISTRY_ID || "0xc2b8a9705516370e245f4d7ce58286ccbb56554edf31d1cc5a02155ac24d43c0";
308
- const normalizedWallet = normalizeSuiAddress(requestingWallet);
309
-
310
- // Wallet-based seal_approve call
311
- // entry fun seal_approve(id: vector<u8>, requesting_wallet: address, registry: &AccessRegistry, clock: &Clock, ctx: &TxContext)
312
- tx.moveCall({
313
- target: `${this.config.packageId}::seal_access_control::seal_approve`,
314
- arguments: [
315
- tx.pure.vector("u8", fromHex(id)), // Arg 1: Content ID (SEAL key ID)
316
- tx.pure.address(normalizedWallet), // Arg 2: Requesting wallet address
317
- tx.object(registryId), // Arg 3: AccessRegistry reference
318
- tx.object('0x6') // Arg 4: Clock object (system clock)
319
- ]
320
- });
321
-
322
- // Set the sender for the transaction
323
- tx.setSender(userAddress);
324
-
325
- const txBytes = await tx.build({
326
- client: this.config.suiClient,
327
- onlyTransactionKind: true
328
- });
329
-
330
- this.completeMetric(metric, true, {
331
- id,
332
- txSize: txBytes.length,
333
- registryId,
334
- clockId: '0x6',
335
- requestingWallet: normalizedWallet
336
- });
337
-
338
- return txBytes;
339
-
340
- } catch (error) {
341
- this.completeMetric(metric, false, undefined, error);
342
- const errorMessage = error instanceof Error ? error.message : String(error);
343
- throw new Error(`Failed to create transaction: ${errorMessage}`);
344
- }
345
- }
346
-
347
- /**
348
- * Build a seal_approve transaction for a specific requesting wallet
349
- */
350
- buildSealApproveTransaction(
351
- contentId: Uint8Array,
352
- requestingWallet: string,
353
- accessRegistry?: string
354
- ): Transaction {
355
- const metric = this.startMetric('transaction_creation_wallet');
356
-
357
- try {
358
- const tx = new Transaction();
359
-
360
- // Use the deployed AccessRegistry ID from environment or parameter
361
- const registryId = accessRegistry || process.env.ACCESS_REGISTRY_ID || "0x8088cc36468b53f210696f1c6b1a4de1b1666dd36a7c36f92c394ff1d342f6dd";
362
- const normalizedWallet = normalizeSuiAddress(requestingWallet);
363
-
364
- // Wallet-based seal_approve call
365
- tx.moveCall({
366
- target: `${this.config.packageId}::seal_access_control::seal_approve`,
367
- arguments: [
368
- tx.pure.vector('u8', Array.from(contentId)), // Content identifier
369
- tx.pure.address(normalizedWallet), // Requesting wallet address
370
- tx.object(registryId), // AccessRegistry reference
371
- tx.object('0x6') // Clock object (system clock)
372
- ]
373
- });
374
-
375
- this.completeMetric(metric, true, {
376
- requestingWallet: normalizedWallet,
377
- contentIdLength: contentId.length,
378
- registryId,
379
- clockId: '0x6'
380
- });
381
-
382
- return tx;
383
-
384
- } catch (error) {
385
- this.completeMetric(metric, false, undefined, error);
386
- const errorMessage = error instanceof Error ? error.message : String(error);
387
- throw new Error(`Failed to create transaction for wallet: ${errorMessage}`);
388
- }
389
- }
390
-
391
- /**
392
- * Parse encrypted object structure
393
- */
394
- parseEncryptedObject(encryptedBytes: Uint8Array): any {
395
- const metric = this.startMetric('object_parsing');
396
-
397
- try {
398
- const parsed = EncryptedObject.parse(encryptedBytes);
399
-
400
- this.completeMetric(metric, true, {
401
- version: parsed.version,
402
- packageId: parsed.packageId,
403
- threshold: parsed.threshold,
404
- dataSize: encryptedBytes.length
405
- });
406
-
407
- return parsed;
408
-
409
- } catch (error) {
410
- this.completeMetric(metric, false, undefined, error);
411
- throw new Error(`Failed to parse encrypted object: ${error}`);
412
- }
413
- }
414
-
415
- /**
416
- * Session management utilities
417
- */
418
- getActiveSession(address: string): any | null {
419
- const session = this.activeSessions.get(address);
420
- if (!session) return null;
421
-
422
- // Check if session is expired
423
- const now = Date.now();
424
- if (now > session.createdAt + session.ttl) {
425
- this.activeSessions.delete(address);
426
- return null;
427
- }
428
-
429
- return session.sessionKey;
430
- }
431
-
432
- cleanupExpiredSessions(): number {
433
- const now = Date.now();
434
- let cleanedCount = 0;
435
-
436
- for (const [address, session] of this.activeSessions.entries()) {
437
- if (now > session.createdAt + session.ttl) {
438
- this.activeSessions.delete(address);
439
- cleanedCount++;
440
- }
441
- }
442
-
443
- console.log(`🧹 Cleaned up ${cleanedCount} expired sessions`);
444
- return cleanedCount;
445
- }
446
-
447
- getSessionStats(): { total: number; expired: number; active: number } {
448
- const now = Date.now();
449
- let active = 0;
450
- let expired = 0;
451
-
452
- for (const session of this.activeSessions.values()) {
453
- if (now > session.createdAt + session.ttl) {
454
- expired++;
455
- } else {
456
- active++;
457
- }
458
- }
459
-
460
- return { total: this.activeSessions.size, active, expired };
461
- }
462
-
463
- /**
464
- * Performance metrics and analytics
465
- */
466
- private startMetric(operation: string): PerformanceMetric {
467
- const metric: PerformanceMetric = {
468
- operation,
469
- startTime: Date.now(),
470
- endTime: 0,
471
- duration: 0,
472
- success: false
473
- };
474
-
475
- return metric;
476
- }
477
-
478
- private completeMetric(metric: PerformanceMetric, success: boolean, metadata?: any, error?: any): void {
479
- metric.endTime = Date.now();
480
- metric.duration = metric.endTime - metric.startTime;
481
- metric.success = success;
482
- metric.metadata = metadata;
483
-
484
- if (error) {
485
- metric.errorType = error.constructor.name;
486
- }
487
-
488
- this.performanceMetrics.push(metric);
489
-
490
- // Limit metrics storage to prevent memory leaks
491
- if (this.performanceMetrics.length > 1000) {
492
- this.performanceMetrics.splice(0, 500); // Keep last 500 metrics
493
- }
494
- }
495
-
496
- getPerformanceMetrics(): PerformanceMetric[] {
497
- return [...this.performanceMetrics];
498
- }
499
-
500
- getPerformanceStats() {
501
- if (this.performanceMetrics.length === 0) {
502
- return { totalOperations: 0, averageTime: 0, successRate: 0 };
503
- }
504
-
505
- const successful = this.performanceMetrics.filter(m => m.success);
506
- const totalTime = this.performanceMetrics.reduce((sum, m) => sum + m.duration, 0);
507
-
508
- return {
509
- totalOperations: this.performanceMetrics.length,
510
- successfulOperations: successful.length,
511
- successRate: (successful.length / this.performanceMetrics.length) * 100,
512
- averageTime: totalTime / this.performanceMetrics.length,
513
- averageSuccessTime: successful.length > 0 ?
514
- successful.reduce((sum, m) => sum + m.duration, 0) / successful.length : 0,
515
- operationBreakdown: this.getOperationBreakdown()
516
- };
517
- }
518
-
519
- private getOperationBreakdown(): Record<string, any> {
520
- const breakdown: Record<string, any> = {};
521
-
522
- for (const metric of this.performanceMetrics) {
523
- if (!breakdown[metric.operation]) {
524
- breakdown[metric.operation] = {
525
- count: 0,
526
- totalTime: 0,
527
- successCount: 0,
528
- avgTime: 0,
529
- successRate: 0
530
- };
531
- }
532
-
533
- const op = breakdown[metric.operation];
534
- op.count++;
535
- op.totalTime += metric.duration;
536
- if (metric.success) op.successCount++;
537
- }
538
-
539
- // Calculate averages and rates
540
- for (const op of Object.values(breakdown) as any[]) {
541
- op.avgTime = op.totalTime / op.count;
542
- op.successRate = (op.successCount / op.count) * 100;
543
- }
544
-
545
- return breakdown;
546
- }
547
-
548
- /**
549
- * Health checks and diagnostics
550
- */
551
- async healthCheck(): Promise<{ status: 'healthy' | 'degraded' | 'unhealthy'; details: any }> {
552
- const checks = {
553
- sealClientInitialized: !!this.sealClient,
554
- activeSessions: this.activeSessions.size,
555
- performanceMetrics: this.performanceMetrics.length,
556
- keyServers: this.config.keyServerObjectIds.length
557
- };
558
-
559
- // Test key server connectivity
560
- const serverHealth = await Promise.allSettled(
561
- this.config.keyServerUrls.map(async (url) => {
562
- const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
563
- return { url, status: response.status, ok: response.ok };
564
- })
565
- );
566
-
567
- const healthyServers = serverHealth.filter(result =>
568
- result.status === 'fulfilled' && result.value.ok
569
- ).length;
570
-
571
- let status: 'healthy' | 'degraded' | 'unhealthy';
572
- if (healthyServers === this.config.keyServerUrls.length) {
573
- status = 'healthy';
574
- } else if (healthyServers >= this.config.threshold) {
575
- status = 'degraded';
576
- } else {
577
- status = 'unhealthy';
578
- }
579
-
580
- return {
581
- status,
582
- details: {
583
- ...checks,
584
- serverHealth: {
585
- total: this.config.keyServerUrls.length,
586
- healthy: healthyServers,
587
- threshold: this.config.threshold
588
- },
589
- performanceStats: this.getPerformanceStats()
590
- }
591
- };
592
- }
593
-
594
- /**
595
- * Configuration and utilities
596
- */
597
- getConfiguration(): Partial<SealConfig> {
598
- return {
599
- packageId: this.config.packageId,
600
- network: this.config.network,
601
- threshold: this.config.threshold,
602
- enableMetrics: this.config.enableMetrics,
603
- retryAttempts: this.config.retryAttempts,
604
- timeoutMs: this.config.timeoutMs
605
- };
606
- }
607
-
608
- updateConfiguration(updates: Partial<SealConfig>): void {
609
- this.config = { ...this.config, ...updates };
610
-
611
- // Reinitialize client if critical settings changed
612
- if (updates.keyServerObjectIds || updates.suiClient) {
613
- this.sealClient = null;
614
- }
615
- }
1
+ /**
2
+ * SEAL Service Integration
3
+ *
4
+ * Production-ready SEAL service wrapper with comprehensive error handling,
5
+ * session management, and performance analytics integration.
6
+ */
7
+
8
+ import { SuiClient } from '@mysten/sui/client';
9
+ import { Transaction } from '@mysten/sui/transactions';
10
+ import { fromHex, toHex, normalizeSuiAddress } from '@mysten/sui/utils';
11
+ import { SealClient, SessionKey, EncryptedObject } from '@mysten/seal';
12
+ import type {
13
+ SealClientOptions,
14
+ EncryptOptions,
15
+ DecryptOptions,
16
+ KeyServerConfig
17
+ } from '@mysten/seal';
18
+
19
+ // SEAL SDK types and interfaces
20
+ interface SealConfig {
21
+ suiClient: SuiClient;
22
+ packageId: string;
23
+ keyServerUrls: string[];
24
+ keyServerObjectIds: string[];
25
+ threshold: number;
26
+ network: 'testnet' | 'mainnet';
27
+ enableMetrics: boolean;
28
+ retryAttempts: number;
29
+ timeoutMs: number;
30
+ }
31
+
32
+ interface EncryptionOptions {
33
+ data: Uint8Array;
34
+ id: string;
35
+ threshold?: number;
36
+ }
37
+
38
+ interface DecryptionOptions {
39
+ encryptedObject: Uint8Array;
40
+ sessionKey: any;
41
+ txBytes: Uint8Array;
42
+ }
43
+
44
+ interface SessionConfig {
45
+ address: string;
46
+ packageId: string;
47
+ ttlMin: number;
48
+ }
49
+
50
+ interface PerformanceMetric {
51
+ operation: string;
52
+ startTime: number;
53
+ endTime: number;
54
+ duration: number;
55
+ success: boolean;
56
+ errorType?: string;
57
+ metadata?: any;
58
+ }
59
+
60
+ /**
61
+ * SEAL Service - Production Implementation
62
+ */
63
+ export class SealService {
64
+ private sealClient: SealClient | null = null;
65
+ private sessionKey: SessionKey | null = null;
66
+ private config: SealConfig;
67
+ private performanceMetrics: PerformanceMetric[] = [];
68
+ private activeSessions: Map<string, any> = new Map();
69
+
70
+ constructor(config: SealConfig) {
71
+ this.config = config;
72
+ console.log('✅ SEAL SDK components loaded successfully');
73
+ }
74
+
75
+ /**
76
+ * Initialize SEAL client with retry logic
77
+ */
78
+ async initializeClient(): Promise<void> {
79
+ const metric = this.startMetric('seal_client_init');
80
+
81
+ try {
82
+ // Configure server configs from testnet servers
83
+ const serverConfigs: KeyServerConfig[] = this.config.keyServerObjectIds.map((objectId, index) => ({
84
+ objectId,
85
+ weight: 1
86
+ }));
87
+
88
+ const sealClientOptions: SealClientOptions = {
89
+ suiClient: this.config.suiClient,
90
+ serverConfigs,
91
+ verifyKeyServers: this.config.network === 'mainnet' // Only verify on mainnet
92
+ };
93
+
94
+ this.sealClient = new SealClient(sealClientOptions);
95
+
96
+ this.completeMetric(metric, true, { serverCount: serverConfigs.length });
97
+ console.log('✅ SEAL client initialized with', serverConfigs.length, 'servers');
98
+
99
+ } catch (error) {
100
+ this.completeMetric(metric, false, undefined, error);
101
+ throw new Error(`Failed to initialize SEAL client: ${error}`);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Create and manage session key
107
+ */
108
+ async createSession(config: SessionConfig, signature?: Uint8Array | string): Promise<any> {
109
+ const metric = this.startMetric('session_creation');
110
+
111
+ try {
112
+ const sessionKey = await SessionKey.create({
113
+ address: config.address,
114
+ packageId: config.packageId,
115
+ ttlMin: config.ttlMin,
116
+ suiClient: this.config.suiClient
117
+ });
118
+
119
+ const message = sessionKey.getPersonalMessage();
120
+
121
+ // In production, signature would come from wallet
122
+ if (signature) {
123
+ // Convert Uint8Array to hex string if needed
124
+ const signatureString = typeof signature === 'string' ? signature : toHex(signature);
125
+ await sessionKey.setPersonalMessageSignature(signatureString);
126
+ }
127
+
128
+ // Store session for management
129
+ this.activeSessions.set(config.address, {
130
+ sessionKey,
131
+ createdAt: Date.now(),
132
+ ttl: config.ttlMin * 60 * 1000,
133
+ address: config.address
134
+ });
135
+
136
+ this.completeMetric(metric, true, {
137
+ address: config.address,
138
+ ttlMin: config.ttlMin,
139
+ messageLength: message.length
140
+ });
141
+
142
+ return { sessionKey, personalMessage: message };
143
+
144
+ } catch (error) {
145
+ this.completeMetric(metric, false, undefined, error);
146
+ throw new Error(`Failed to create session: ${error}`);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Encrypt data with comprehensive error handling
152
+ */
153
+ async encryptData(options: EncryptionOptions): Promise<{ encryptedObject: Uint8Array; key: Uint8Array }> {
154
+ const metric = this.startMetric('encryption');
155
+
156
+ try {
157
+ if (!this.sealClient) {
158
+ await this.initializeClient();
159
+ }
160
+
161
+ if (!this.sealClient) {
162
+ throw new Error('Failed to initialize SEAL client');
163
+ }
164
+
165
+ const threshold = options.threshold || this.config.threshold;
166
+
167
+ // Validate threshold against available servers
168
+ if (threshold > this.config.keyServerObjectIds.length) {
169
+ throw new Error(`Threshold ${threshold} exceeds available servers ${this.config.keyServerObjectIds.length}`);
170
+ }
171
+
172
+ const result = await this.sealClient.encrypt({
173
+ threshold,
174
+ packageId: this.config.packageId,
175
+ id: options.id,
176
+ data: options.data
177
+ });
178
+
179
+ this.completeMetric(metric, true, {
180
+ dataSize: options.data.length,
181
+ threshold,
182
+ encryptedSize: result.encryptedObject.length,
183
+ keySize: result.key.length
184
+ });
185
+
186
+ return result;
187
+
188
+ } catch (error) {
189
+ this.completeMetric(metric, false, undefined, error);
190
+
191
+ // Enhanced error handling
192
+ const errorMessage = error instanceof Error ? error.message : String(error);
193
+ if (errorMessage.includes('threshold')) {
194
+ throw new Error(`Encryption threshold error: ${errorMessage}`);
195
+ }
196
+ if (errorMessage.includes('network')) {
197
+ throw new Error(`Network error during encryption: ${errorMessage}`);
198
+ }
199
+
200
+ throw new Error(`Encryption failed: ${errorMessage}`);
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Decrypt data with session management
206
+ */
207
+ async decryptData(options: DecryptionOptions): Promise<Uint8Array> {
208
+ const metric = this.startMetric('decryption');
209
+
210
+ try {
211
+ if (!this.sealClient) {
212
+ await this.initializeClient();
213
+ }
214
+
215
+ if (!this.sealClient) {
216
+ throw new Error('Failed to initialize SEAL client');
217
+ }
218
+
219
+ // Add detailed debugging for SEAL decryption
220
+ console.log('🔍 SEAL decrypt parameters:');
221
+ console.log(` - Encrypted data type: ${typeof options.encryptedObject}`);
222
+ console.log(` - Encrypted data length: ${options.encryptedObject.length}`);
223
+ console.log(` - Session key type: ${typeof options.sessionKey}`);
224
+ console.log(` - Session key constructor: ${options.sessionKey?.constructor?.name}`);
225
+ console.log(` - Transaction bytes type: ${typeof options.txBytes}`);
226
+ console.log(` - Transaction bytes length: ${options.txBytes.length}`);
227
+
228
+ // Check if session key has expected methods
229
+ if (options.sessionKey) {
230
+ console.log(` - Session key methods: ${Object.getOwnPropertyNames(Object.getPrototypeOf(options.sessionKey)).join(', ')}`);
231
+ }
232
+
233
+ // Try SEAL decryption with error boundary
234
+ let result;
235
+ try {
236
+ console.log('🔍 Attempting SEAL client decrypt...');
237
+ result = await this.sealClient.decrypt({
238
+ data: options.encryptedObject,
239
+ sessionKey: options.sessionKey,
240
+ txBytes: options.txBytes
241
+ });
242
+ console.log('🔍 SEAL decrypt completed without throwing');
243
+ } catch (innerError) {
244
+ console.log('🔍 SEAL decrypt threw an error:', innerError);
245
+ throw innerError;
246
+ }
247
+
248
+ console.log(`🔍 SEAL decrypt result:`, {
249
+ result,
250
+ resultType: typeof result,
251
+ resultLength: result ? result.length : 'undefined',
252
+ hasResult: !!result,
253
+ isUint8Array: result instanceof Uint8Array
254
+ });
255
+
256
+ if (!result) {
257
+ throw new Error('SEAL decrypt returned undefined/null result');
258
+ }
259
+
260
+ this.completeMetric(metric, true, {
261
+ encryptedSize: options.encryptedObject.length,
262
+ decryptedSize: result ? result.length : 0
263
+ });
264
+
265
+ return result;
266
+
267
+ } catch (error) {
268
+ this.completeMetric(metric, false, undefined, error);
269
+
270
+ // Enhanced error handling for decryption with detailed logging
271
+ console.log('🚨 SEAL decrypt error details:', {
272
+ error,
273
+ errorType: typeof error,
274
+ errorConstructor: error?.constructor?.name,
275
+ errorMessage: error instanceof Error ? error.message : String(error),
276
+ errorStack: error instanceof Error ? error.stack : 'No stack trace'
277
+ });
278
+
279
+ const errorMessage = error instanceof Error ? error.message : String(error);
280
+ if (errorMessage.includes('access')) {
281
+ throw new Error(`Access denied for decryption: ${errorMessage}`);
282
+ }
283
+ if (errorMessage.includes('session')) {
284
+ throw new Error(`Session error during decryption: ${errorMessage}`);
285
+ }
286
+
287
+ throw new Error(`Decryption failed: ${errorMessage}`);
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Create transaction for seal_approve using capability pattern
293
+ * Matches Move signature: seal_approve(cap: &MemoryCap, key_id: vector<u8>, ctx: &TxContext)
294
+ *
295
+ * @param keyId - SEAL key ID (hex string or bytes)
296
+ * @param userAddress - User's wallet address (sender)
297
+ * @param memoryCapId - Object ID of the MemoryCap
298
+ */
299
+ async createSealApproveTransaction(
300
+ keyId: string,
301
+ userAddress: string,
302
+ memoryCapId: string,
303
+ _accessRegistry?: string // Deprecated, kept for backward compatibility
304
+ ): Promise<Uint8Array> {
305
+ const metric = this.startMetric('transaction_creation');
306
+
307
+ try {
308
+ const tx = new Transaction();
309
+
310
+ // Capability-based seal_approve call
311
+ // CRITICAL: key_id MUST be first argument for SEAL key server!
312
+ // entry fun seal_approve(key_id: vector<u8>, cap: &MemoryCap, ctx: &TxContext)
313
+ tx.moveCall({
314
+ target: `${this.config.packageId}::capability::seal_approve`,
315
+ arguments: [
316
+ tx.pure.vector("u8", fromHex(keyId)), // Arg 1: SEAL key ID (MUST BE FIRST!)
317
+ tx.object(memoryCapId), // Arg 2: MemoryCap object reference
318
+ // ctx: &TxContext is auto-provided by Sui
319
+ ]
320
+ });
321
+
322
+ // Set the sender for the transaction
323
+ tx.setSender(userAddress);
324
+
325
+ const txBytes = await tx.build({
326
+ client: this.config.suiClient,
327
+ onlyTransactionKind: true
328
+ });
329
+
330
+ this.completeMetric(metric, true, {
331
+ keyId,
332
+ memoryCapId,
333
+ txSize: txBytes.length,
334
+ userAddress
335
+ });
336
+
337
+ return txBytes;
338
+
339
+ } catch (error) {
340
+ this.completeMetric(metric, false, undefined, error);
341
+ const errorMessage = error instanceof Error ? error.message : String(error);
342
+ throw new Error(`Failed to create transaction: ${errorMessage}`);
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Build a seal_approve transaction using capability pattern
348
+ * Matches Move signature: seal_approve(cap: &MemoryCap, key_id: vector<u8>, ctx: &TxContext)
349
+ *
350
+ * @param keyId - SEAL key ID (bytes)
351
+ * @param memoryCapId - Object ID of the MemoryCap
352
+ */
353
+ buildSealApproveTransaction(
354
+ keyId: Uint8Array,
355
+ memoryCapId: string,
356
+ _accessRegistry?: string // Deprecated, kept for backward compatibility
357
+ ): Transaction {
358
+ const metric = this.startMetric('transaction_creation_wallet');
359
+
360
+ try {
361
+ const tx = new Transaction();
362
+
363
+ // Capability-based seal_approve call
364
+ // CRITICAL: key_id MUST be first argument for SEAL key server!
365
+ // entry fun seal_approve(key_id: vector<u8>, cap: &MemoryCap, ctx: &TxContext)
366
+ tx.moveCall({
367
+ target: `${this.config.packageId}::capability::seal_approve`,
368
+ arguments: [
369
+ tx.pure.vector('u8', Array.from(keyId)), // Arg 1: SEAL key ID (MUST BE FIRST!)
370
+ tx.object(memoryCapId), // Arg 2: MemoryCap object reference
371
+ // ctx: &TxContext is auto-provided by Sui
372
+ ]
373
+ });
374
+
375
+ this.completeMetric(metric, true, {
376
+ keyIdLength: keyId.length,
377
+ memoryCapId
378
+ });
379
+
380
+ return tx;
381
+
382
+ } catch (error) {
383
+ this.completeMetric(metric, false, undefined, error);
384
+ const errorMessage = error instanceof Error ? error.message : String(error);
385
+ throw new Error(`Failed to create transaction for capability: ${errorMessage}`);
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Parse encrypted object structure
391
+ */
392
+ parseEncryptedObject(encryptedBytes: Uint8Array): any {
393
+ const metric = this.startMetric('object_parsing');
394
+
395
+ try {
396
+ const parsed = EncryptedObject.parse(encryptedBytes);
397
+
398
+ this.completeMetric(metric, true, {
399
+ version: parsed.version,
400
+ packageId: parsed.packageId,
401
+ threshold: parsed.threshold,
402
+ dataSize: encryptedBytes.length
403
+ });
404
+
405
+ return parsed;
406
+
407
+ } catch (error) {
408
+ this.completeMetric(metric, false, undefined, error);
409
+ throw new Error(`Failed to parse encrypted object: ${error}`);
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Session management utilities
415
+ */
416
+ getActiveSession(address: string): any | null {
417
+ const session = this.activeSessions.get(address);
418
+ if (!session) return null;
419
+
420
+ // Check if session is expired
421
+ const now = Date.now();
422
+ if (now > session.createdAt + session.ttl) {
423
+ this.activeSessions.delete(address);
424
+ return null;
425
+ }
426
+
427
+ return session.sessionKey;
428
+ }
429
+
430
+ cleanupExpiredSessions(): number {
431
+ const now = Date.now();
432
+ let cleanedCount = 0;
433
+
434
+ for (const [address, session] of this.activeSessions.entries()) {
435
+ if (now > session.createdAt + session.ttl) {
436
+ this.activeSessions.delete(address);
437
+ cleanedCount++;
438
+ }
439
+ }
440
+
441
+ console.log(`🧹 Cleaned up ${cleanedCount} expired sessions`);
442
+ return cleanedCount;
443
+ }
444
+
445
+ getSessionStats(): { total: number; expired: number; active: number } {
446
+ const now = Date.now();
447
+ let active = 0;
448
+ let expired = 0;
449
+
450
+ for (const session of this.activeSessions.values()) {
451
+ if (now > session.createdAt + session.ttl) {
452
+ expired++;
453
+ } else {
454
+ active++;
455
+ }
456
+ }
457
+
458
+ return { total: this.activeSessions.size, active, expired };
459
+ }
460
+
461
+ /**
462
+ * Performance metrics and analytics
463
+ */
464
+ private startMetric(operation: string): PerformanceMetric {
465
+ const metric: PerformanceMetric = {
466
+ operation,
467
+ startTime: Date.now(),
468
+ endTime: 0,
469
+ duration: 0,
470
+ success: false
471
+ };
472
+
473
+ return metric;
474
+ }
475
+
476
+ private completeMetric(metric: PerformanceMetric, success: boolean, metadata?: any, error?: any): void {
477
+ metric.endTime = Date.now();
478
+ metric.duration = metric.endTime - metric.startTime;
479
+ metric.success = success;
480
+ metric.metadata = metadata;
481
+
482
+ if (error) {
483
+ metric.errorType = error.constructor.name;
484
+ }
485
+
486
+ this.performanceMetrics.push(metric);
487
+
488
+ // Limit metrics storage to prevent memory leaks
489
+ if (this.performanceMetrics.length > 1000) {
490
+ this.performanceMetrics.splice(0, 500); // Keep last 500 metrics
491
+ }
492
+ }
493
+
494
+ getPerformanceMetrics(): PerformanceMetric[] {
495
+ return [...this.performanceMetrics];
496
+ }
497
+
498
+ getPerformanceStats() {
499
+ if (this.performanceMetrics.length === 0) {
500
+ return { totalOperations: 0, averageTime: 0, successRate: 0 };
501
+ }
502
+
503
+ const successful = this.performanceMetrics.filter(m => m.success);
504
+ const totalTime = this.performanceMetrics.reduce((sum, m) => sum + m.duration, 0);
505
+
506
+ return {
507
+ totalOperations: this.performanceMetrics.length,
508
+ successfulOperations: successful.length,
509
+ successRate: (successful.length / this.performanceMetrics.length) * 100,
510
+ averageTime: totalTime / this.performanceMetrics.length,
511
+ averageSuccessTime: successful.length > 0 ?
512
+ successful.reduce((sum, m) => sum + m.duration, 0) / successful.length : 0,
513
+ operationBreakdown: this.getOperationBreakdown()
514
+ };
515
+ }
516
+
517
+ private getOperationBreakdown(): Record<string, any> {
518
+ const breakdown: Record<string, any> = {};
519
+
520
+ for (const metric of this.performanceMetrics) {
521
+ if (!breakdown[metric.operation]) {
522
+ breakdown[metric.operation] = {
523
+ count: 0,
524
+ totalTime: 0,
525
+ successCount: 0,
526
+ avgTime: 0,
527
+ successRate: 0
528
+ };
529
+ }
530
+
531
+ const op = breakdown[metric.operation];
532
+ op.count++;
533
+ op.totalTime += metric.duration;
534
+ if (metric.success) op.successCount++;
535
+ }
536
+
537
+ // Calculate averages and rates
538
+ for (const op of Object.values(breakdown) as any[]) {
539
+ op.avgTime = op.totalTime / op.count;
540
+ op.successRate = (op.successCount / op.count) * 100;
541
+ }
542
+
543
+ return breakdown;
544
+ }
545
+
546
+ /**
547
+ * Health checks and diagnostics
548
+ */
549
+ async healthCheck(): Promise<{ status: 'healthy' | 'degraded' | 'unhealthy'; details: any }> {
550
+ const checks = {
551
+ sealClientInitialized: !!this.sealClient,
552
+ activeSessions: this.activeSessions.size,
553
+ performanceMetrics: this.performanceMetrics.length,
554
+ keyServers: this.config.keyServerObjectIds.length
555
+ };
556
+
557
+ // Test key server connectivity
558
+ const serverHealth = await Promise.allSettled(
559
+ this.config.keyServerUrls.map(async (url) => {
560
+ const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
561
+ return { url, status: response.status, ok: response.ok };
562
+ })
563
+ );
564
+
565
+ const healthyServers = serverHealth.filter(result =>
566
+ result.status === 'fulfilled' && result.value.ok
567
+ ).length;
568
+
569
+ let status: 'healthy' | 'degraded' | 'unhealthy';
570
+ if (healthyServers === this.config.keyServerUrls.length) {
571
+ status = 'healthy';
572
+ } else if (healthyServers >= this.config.threshold) {
573
+ status = 'degraded';
574
+ } else {
575
+ status = 'unhealthy';
576
+ }
577
+
578
+ return {
579
+ status,
580
+ details: {
581
+ ...checks,
582
+ serverHealth: {
583
+ total: this.config.keyServerUrls.length,
584
+ healthy: healthyServers,
585
+ threshold: this.config.threshold
586
+ },
587
+ performanceStats: this.getPerformanceStats()
588
+ }
589
+ };
590
+ }
591
+
592
+ /**
593
+ * Configuration and utilities
594
+ */
595
+ getConfiguration(): Partial<SealConfig> {
596
+ return {
597
+ packageId: this.config.packageId,
598
+ network: this.config.network,
599
+ threshold: this.config.threshold,
600
+ enableMetrics: this.config.enableMetrics,
601
+ retryAttempts: this.config.retryAttempts,
602
+ timeoutMs: this.config.timeoutMs
603
+ };
604
+ }
605
+
606
+ updateConfiguration(updates: Partial<SealConfig>): void {
607
+ this.config = { ...this.config, ...updates };
608
+
609
+ // Reinitialize client if critical settings changed
610
+ if (updates.keyServerObjectIds || updates.suiClient) {
611
+ this.sealClient = null;
612
+ }
613
+ }
616
614
  }