@miradorlabs/parallax-web 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  export * from './src/parallax';
2
2
  export { GrpcWebRpc } from './src/grpc';
3
3
 
4
+ // Export ParallaxService
5
+ export { ParallaxService, parallaxService } from './src/parallax/ParallaxService';
6
+
4
7
  // Re-export proto types for convenience
5
8
  export {
6
9
  CreateTraceRequest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miradorlabs/parallax-web",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
4
4
  "description": "Parallax Web Client Side SDK ",
5
5
  "main": "dist/index.umd.js",
6
6
  "module": "dist/index.esm.js",
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "google-protobuf": "^3.21.4",
48
48
  "grpc-web": "^2.0.2",
49
- "mirador-gateway-parallax-web": "https://storage.googleapis.com/mirador-shd-packages/gateway/parallax/grpc-web/mirador-gateway-parallax-grpc-web-1.0.9.tgz",
49
+ "mirador-gateway-parallax-web": "https://storage.googleapis.com/mirador-shd-packages/gateway/parallax/grpc-web/mirador-gateway-parallax-grpc-web-1.0.10.tgz",
50
50
  "rxjs": "^7.8.2"
51
51
  },
52
52
  "author": "@mirador",
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Parallax Service - Transaction Tracing for Web Applications
3
+ * Creates individual traces for each transaction and tracks them through their lifecycle
4
+ *
5
+ * This service provides a simplified interface for tracking transactions with automatic
6
+ * client metadata collection and lifecycle management.
7
+ */
8
+
9
+ import { ParallaxClient } from './index';
10
+ import type {
11
+ CreateTraceResponse,
12
+ StartSpanResponse,
13
+ FinishSpanResponse,
14
+ AddSpanEventResponse,
15
+ AddSpanErrorResponse,
16
+ AddSpanHintResponse,
17
+ } from 'mirador-gateway-parallax-web/proto/gateway/parallax/v1/parallax_gateway_pb';
18
+
19
+ interface TransactionInfo {
20
+ traceId: string;
21
+ rootSpanId: string;
22
+ spanId: string | null;
23
+ timestamp: string;
24
+ txHash: string | null;
25
+ from?: string;
26
+ to?: string;
27
+ network?: string;
28
+ }
29
+
30
+ interface TransactionData {
31
+ from: string;
32
+ to: string;
33
+ value: string;
34
+ network?: string;
35
+ walletAddress?: string;
36
+ additionalData?: Record<string, any>;
37
+ }
38
+
39
+ interface FinishOptions {
40
+ success: boolean;
41
+ error?: string;
42
+ }
43
+
44
+ export class ParallaxService {
45
+ private client: ParallaxClient | null = null;
46
+ private activeTransactions: Map<string, TransactionInfo> = new Map();
47
+
48
+ constructor() {
49
+ // Client will be initialized lazily
50
+ }
51
+
52
+ /**
53
+ * Initialize the Parallax client (lazy initialization)
54
+ * @param apiKey - Optional API key for authentication
55
+ * @param gatewayUrl - Optional custom gateway URL
56
+ */
57
+ private _ensureClient(apiKey?: string, gatewayUrl?: string): void {
58
+ if (this.client) return;
59
+
60
+ // Determine gateway URL based on environment if not provided
61
+ let url = gatewayUrl;
62
+ if (!url && typeof window !== 'undefined') {
63
+ const isDevelopment =
64
+ window.location.hostname === 'localhost' ||
65
+ window.location.hostname === '127.0.0.1';
66
+
67
+ url = isDevelopment
68
+ ? `${window.location.protocol}//${window.location.host}/parallax-gateway`
69
+ : 'https://gateway-parallax-dev.mirador.org';
70
+ }
71
+
72
+ this.client = new ParallaxClient(apiKey || '', url);
73
+ console.log('[ParallaxService] Client initialized with URL:', url);
74
+ }
75
+
76
+ /**
77
+ * Start a new transaction trace
78
+ * Called when user initiates a transaction
79
+ *
80
+ * Note: Since createTrace now automatically creates a root span, we don't need
81
+ * to call startSpan separately. The rootSpanId from the response is the parent span.
82
+ *
83
+ * @param txData - Transaction data
84
+ * @param name - Name for the trace (e.g., 'SendingTrace', 'SwappingTrace')
85
+ * @param options - Optional configuration (apiKey, gatewayUrl, includeClientMeta)
86
+ * @returns Promise with traceId, rootSpanId, and txId
87
+ */
88
+ async startTransactionTrace(
89
+ txData: TransactionData,
90
+ name: string = 'WalletTransaction',
91
+ options?: { apiKey?: string; gatewayUrl?: string; includeClientMeta?: boolean }
92
+ ): Promise<{ traceId: string; rootSpanId: string; txId: string }> {
93
+ this._ensureClient(options?.apiKey, options?.gatewayUrl);
94
+
95
+ if (!this.client) {
96
+ throw new Error('Failed to initialize Parallax client');
97
+ }
98
+
99
+ try {
100
+ const txId = `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
101
+ const timestamp = new Date().toISOString();
102
+
103
+ // Create trace attributes
104
+ const traceAttributes: Record<string, string> = {
105
+ transactionId: txId,
106
+ walletAddress: txData.walletAddress || txData.from,
107
+ network: txData.network || 'Unknown',
108
+ transactionStart: timestamp,
109
+ from: txData.from,
110
+ to: txData.to,
111
+ value: txData.value,
112
+ };
113
+
114
+ // Add any additional transaction data
115
+ if (txData.additionalData) {
116
+ Object.entries(txData.additionalData).forEach(([key, value]) => {
117
+ traceAttributes[key] = typeof value === 'object' ? JSON.stringify(value) : String(value);
118
+ });
119
+ }
120
+
121
+ // Create a trace for this transaction
122
+ const createTraceReq = await this.client.createTraceRequest({
123
+ name: name,
124
+ attr: traceAttributes,
125
+ tags: ['transaction', 'wallet', txData.network || 'unknown'],
126
+ includeClientMeta: options?.includeClientMeta ?? true,
127
+ });
128
+
129
+ const traceResponse: CreateTraceResponse = await this.client.createTrace(createTraceReq);
130
+ const traceId = traceResponse.getTraceId();
131
+ const rootSpanId = traceResponse.getRootSpanId();
132
+
133
+ // Store transaction info
134
+ this.activeTransactions.set(txId, {
135
+ traceId,
136
+ rootSpanId,
137
+ spanId: rootSpanId, // Use rootSpanId as the main span for this transaction
138
+ timestamp,
139
+ txHash: null,
140
+ from: txData.from,
141
+ to: txData.to,
142
+ network: txData.network,
143
+ });
144
+
145
+ console.log('[ParallaxService] Transaction trace started:', {
146
+ txId,
147
+ traceId,
148
+ rootSpanId,
149
+ from: txData.from,
150
+ to: txData.to,
151
+ });
152
+
153
+ // Add initial event to the root span
154
+ await this.addTransactionEvent(txId, 'transaction_initiated', {
155
+ from: txData.from,
156
+ to: txData.to,
157
+ value: txData.value,
158
+ timestamp,
159
+ });
160
+
161
+ return { traceId, rootSpanId, txId };
162
+ } catch (error) {
163
+ console.error('[ParallaxService] Failed to start transaction trace:', error);
164
+ throw error;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Associate a transaction hash with an existing trace
170
+ * Called when the transaction hash is available after signing/sending
171
+ *
172
+ * @param txId - Transaction identifier returned from startTransactionTrace
173
+ * @param txHash - Blockchain transaction hash
174
+ * @param chainId - Chain ID
175
+ */
176
+ async associateTransactionHash(txId: string, txHash: string, chainId: number): Promise<void> {
177
+ const txInfo = this.activeTransactions.get(txId);
178
+ if (!txInfo) {
179
+ console.warn(`[ParallaxService] Transaction ${txId} not found in active transactions`);
180
+ return;
181
+ }
182
+
183
+ if (!this.client) {
184
+ console.warn('[ParallaxService] Client not initialized');
185
+ return;
186
+ }
187
+
188
+ try {
189
+ // Update stored tx info
190
+ txInfo.txHash = txHash;
191
+ this.activeTransactions.set(txId, txInfo);
192
+
193
+ // Add chain hint to correlate with blockchain events
194
+ const hintReq = this.client.createAddSpanHintRequest({
195
+ traceId: txInfo.traceId,
196
+ parentSpanId: txInfo.rootSpanId,
197
+ txHash: txHash,
198
+ chainId: chainId,
199
+ });
200
+
201
+ await this.client.addSpanHint(hintReq);
202
+
203
+ // Add event for transaction sent
204
+ await this.addTransactionEvent(txId, 'transaction_hash_available', {
205
+ txHash,
206
+ chainId: chainId.toString(),
207
+ timestamp: new Date().toISOString(),
208
+ });
209
+
210
+ console.log('[ParallaxService] Transaction hash associated with trace:', {
211
+ txId,
212
+ txHash,
213
+ traceId: txInfo.traceId,
214
+ });
215
+ } catch (error) {
216
+ console.error('[ParallaxService] Failed to associate transaction hash:', error);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Add an event to a transaction trace
222
+ *
223
+ * @param txId - Transaction identifier
224
+ * @param eventName - Event name
225
+ * @param attributes - Event attributes
226
+ */
227
+ async addTransactionEvent(
228
+ txId: string,
229
+ eventName: string,
230
+ attributes: Record<string, any> = {}
231
+ ): Promise<void> {
232
+ const txInfo = this.activeTransactions.get(txId);
233
+ if (!txInfo) {
234
+ console.warn(`[ParallaxService] Transaction ${txId} not found. Cannot add event '${eventName}'`);
235
+ return;
236
+ }
237
+
238
+ if (!this.client || !txInfo.spanId) {
239
+ console.warn('[ParallaxService] Client not initialized or span not available');
240
+ return;
241
+ }
242
+
243
+ try {
244
+ // Convert attributes to plain object with string values
245
+ const eventAttributes: Record<string, string> = {};
246
+ Object.entries(attributes).forEach(([key, value]) => {
247
+ eventAttributes[key] = typeof value === 'object' ? JSON.stringify(value) : String(value);
248
+ });
249
+
250
+ const request = this.client.createAddSpanEventRequest({
251
+ traceId: txInfo.traceId,
252
+ spanId: txInfo.spanId,
253
+ eventName: eventName,
254
+ attr: eventAttributes,
255
+ });
256
+
257
+ await this.client.addSpanEvent(request);
258
+ } catch (error) {
259
+ console.error(`[ParallaxService] Failed to add transaction event '${eventName}':`, error);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Add an error to a transaction trace
265
+ * Creates a proper span error event in Parallax
266
+ *
267
+ * @param txId - Transaction identifier
268
+ * @param error - Error object or message
269
+ * @param errorType - Type/category of error (e.g., 'TransactionError', 'NetworkError', 'UserRejection')
270
+ */
271
+ async addTransactionError(
272
+ txId: string,
273
+ error: Error | string,
274
+ errorType: string = 'TransactionError'
275
+ ): Promise<void> {
276
+ const txInfo = this.activeTransactions.get(txId);
277
+ if (!txInfo) {
278
+ console.warn(`[ParallaxService] Transaction ${txId} not found. Cannot add error.`);
279
+ return;
280
+ }
281
+
282
+ if (!this.client || !txInfo.spanId) {
283
+ console.warn('[ParallaxService] Client not initialized or span not available');
284
+ return;
285
+ }
286
+
287
+ try {
288
+ // Extract error message and stack trace
289
+ let errorMessage = '';
290
+ let stackTrace: string | undefined;
291
+
292
+ if (error instanceof Error) {
293
+ errorMessage = error.message;
294
+ stackTrace = error.stack;
295
+ } else {
296
+ errorMessage = String(error);
297
+ }
298
+
299
+ const request = this.client.createAddSpanErrorRequest({
300
+ traceId: txInfo.traceId,
301
+ spanId: txInfo.spanId,
302
+ errorMessage: errorMessage,
303
+ errorType: errorType,
304
+ stackTrace: stackTrace,
305
+ });
306
+
307
+ await this.client.addSpanError(request);
308
+
309
+ console.log('[ParallaxService] Transaction error added to trace:', {
310
+ txId,
311
+ errorType,
312
+ message: error instanceof Error ? error.message : String(error),
313
+ });
314
+ } catch (err) {
315
+ console.error('[ParallaxService] Failed to add transaction error:', err);
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Finish a transaction trace
321
+ * Called when transaction is confirmed or permanently failed
322
+ *
323
+ * @param txId - Transaction identifier
324
+ * @param options - Finish options (success, error message)
325
+ */
326
+ async finishTransactionTrace(txId: string, options: FinishOptions = { success: true }): Promise<void> {
327
+ const txInfo = this.activeTransactions.get(txId);
328
+ if (!txInfo) {
329
+ console.warn(`[ParallaxService] Transaction ${txId} not found. Cannot finish.`);
330
+ return;
331
+ }
332
+
333
+ if (!this.client || !txInfo.spanId) {
334
+ console.warn('[ParallaxService] Client not initialized or span not available');
335
+ return;
336
+ }
337
+
338
+ try {
339
+ // Add final event
340
+ await this.addTransactionEvent(
341
+ txId,
342
+ options.success ? 'transaction_completed' : 'transaction_failed',
343
+ {
344
+ success: options.success.toString(),
345
+ error: options.error || '',
346
+ duration: (Date.now() - new Date(txInfo.timestamp).getTime()).toString(),
347
+ timestamp: new Date().toISOString(),
348
+ }
349
+ );
350
+
351
+ // Finish the span
352
+ const request = this.client.createFinishSpanRequest({
353
+ traceId: txInfo.traceId,
354
+ spanId: txInfo.spanId,
355
+ status: {
356
+ success: options.success,
357
+ errorMessage: options.error || '',
358
+ },
359
+ });
360
+
361
+ await this.client.finishSpan(request);
362
+
363
+ console.log('[ParallaxService] Transaction trace finished:', {
364
+ txId,
365
+ traceId: txInfo.traceId,
366
+ success: options.success,
367
+ txHash: txInfo.txHash,
368
+ });
369
+
370
+ // Remove from active transactions
371
+ this.activeTransactions.delete(txId);
372
+ } catch (error) {
373
+ console.error('[ParallaxService] Failed to finish transaction trace:', error);
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Get info about an active transaction
379
+ *
380
+ * @param txId - Transaction identifier
381
+ * @returns Transaction info or null if not found
382
+ */
383
+ getTransactionInfo(txId: string): TransactionInfo | null {
384
+ return this.activeTransactions.get(txId) || null;
385
+ }
386
+
387
+ /**
388
+ * Get all active transactions
389
+ *
390
+ * @returns Array of active transaction info
391
+ */
392
+ getAllActiveTransactions(): Array<TransactionInfo & { txId: string }> {
393
+ return Array.from(this.activeTransactions.entries()).map(([txId, info]) => ({
394
+ txId,
395
+ ...info,
396
+ }));
397
+ }
398
+
399
+ /**
400
+ * Get the ParallaxClient instance for advanced usage
401
+ * @param apiKey - Optional API key
402
+ * @param gatewayUrl - Optional gateway URL
403
+ */
404
+ getClient(apiKey?: string, gatewayUrl?: string): ParallaxClient {
405
+ this._ensureClient(apiKey, gatewayUrl);
406
+ if (!this.client) {
407
+ throw new Error('Failed to initialize Parallax client');
408
+ }
409
+ return this.client;
410
+ }
411
+ }
412
+
413
+ // Export singleton instance
414
+ export const parallaxService = new ParallaxService();