@miradorlabs/parallax-web 2.0.1 → 2.2.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.
package/example/README.md CHANGED
@@ -184,7 +184,7 @@ By default, the demo connects to:
184
184
  ```javascript
185
185
  const client = new ParallaxClient(
186
186
  'demo-api-key',
187
- 'https://parallax-gateway.dev.mirador.org:443'
187
+ 'https://parallax-gateway-dev.mirador.org:443'
188
188
  );
189
189
  ```
190
190
 
package/example/app.js CHANGED
@@ -3,11 +3,11 @@ import { ParallaxClient } from '../dist/index.esm.js';
3
3
 
4
4
  // Initialize the client
5
5
  // Use proxy to avoid CORS issues (default)
6
- // To use direct connection, change to: 'https://parallax-gateway.dev.mirador.org:443'
6
+ // To use direct connection, change to: 'https://parallax-gateway-dev.mirador.org:443'
7
7
  const USE_PROXY = true;
8
- const GATEWAY_URL = USE_PROXY
8
+ const GATEWAY_URL = USE_PROXY
9
9
  ? 'http://localhost:3001' // Proxy server (no CORS issues)
10
- : 'https://parallax-gateway.dev.mirador.org:443'; // Direct (may have CORS issues)
10
+ : 'https://parallax-gateway-dev.mirador.org:443'; // Direct (may have CORS issues)
11
11
 
12
12
  const client = new ParallaxClient('demo-api-key', GATEWAY_URL);
13
13
 
@@ -11,7 +11,7 @@ const app = express();
11
11
  const PORT = 3001;
12
12
 
13
13
  // Target Parallax Gateway
14
- const GATEWAY_URL = 'https://parallax-gateway.dev.mirador.org:443';
14
+ const GATEWAY_URL = 'https://parallax-gateway-dev.mirador.org:443';
15
15
 
16
16
  // Enable CORS for all origins (development only)
17
17
  app.use(cors({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miradorlabs/parallax-web",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "description": "Parallax Web Client Side SDK ",
6
6
  "main": "dist/index.umd.js",
@@ -50,7 +50,7 @@
50
50
  "dependencies": {
51
51
  "google-protobuf": "^3.21.4",
52
52
  "grpc-web": "^2.0.2",
53
- "mirador-gateway-parallax-web": "https://storage.googleapis.com/mirador-shd-packages/gateway/parallax/grpc-web/mirador-gateway-parallax-grpc-web-1.0.13.tgz",
53
+ "mirador-gateway-parallax-web": "https://storage.googleapis.com/mirador-shd-packages/gateway/parallax/grpc-web/mirador-gateway-parallax-grpc-web-1.0.14.tgz",
54
54
  "rxjs": "^7.8.2"
55
55
  },
56
56
  "author": "@mirador",
@@ -1,11 +1,21 @@
1
1
  /**
2
2
  * ParallaxClient - Main client for interacting with the Parallax Gateway
3
3
  */
4
- import { CreateTraceRequest } from 'mirador-gateway-parallax-web/proto/gateway/parallax/v1/parallax_gateway_pb';
4
+ import {
5
+ CreateTraceRequest,
6
+ UpdateTraceRequest,
7
+ } from 'mirador-gateway-parallax-web/proto/gateway/parallax/v1/parallax_gateway_pb';
5
8
  import { ParallaxGatewayServiceClient } from 'mirador-gateway-parallax-web/proto/gateway/parallax/v1/Parallax_gatewayServiceClientPb';
6
9
  import { ParallaxTrace } from './trace';
10
+ import type { ParallaxClientOptions, TraceOptions } from './types';
7
11
 
8
- const DEFAULT_API_URL = 'https://parallax-gateway.dev.mirador.org:443';
12
+ // Default configuration values
13
+ const DEFAULT_API_URL = 'https://parallax-gateway-dev.mirador.org:443';
14
+ const DEFAULT_AUTO_FLUSH = true;
15
+ const DEFAULT_FLUSH_PERIOD_MS = 50;
16
+ const DEFAULT_INCLUDE_CLIENT_META = true;
17
+ const DEFAULT_MAX_RETRIES = 3;
18
+ const DEFAULT_RETRY_BACKOFF = 1000;
9
19
 
10
20
  /**
11
21
  * Main client for interacting with the Parallax Gateway API
@@ -18,49 +28,42 @@ export class ParallaxClient {
18
28
  /**
19
29
  * Create a new ParallaxClient instance
20
30
  * @param apiKey Required API key for authentication (sent as x-parallax-api-key header)
21
- * @param apiUrl Optional gateway URL (defaults to parallax-gateway.dev.mirador.org:443)
31
+ * @param options Optional configuration options
22
32
  */
23
- constructor(apiKey: string, apiUrl?: string) {
33
+ constructor(apiKey: string, options?: ParallaxClientOptions) {
24
34
  this.apiKey = apiKey;
25
- this.apiUrl = apiUrl || DEFAULT_API_URL;
35
+ this.apiUrl = options?.apiUrl || DEFAULT_API_URL;
26
36
 
27
- // API key is required - always include in credentials
28
37
  const credentials = { 'x-parallax-api-key': apiKey };
29
-
30
- // Initialize the gRPC-Web client
31
38
  this.client = new ParallaxGatewayServiceClient(this.apiUrl, credentials);
32
39
  }
33
40
 
34
- /**
35
- * Internal method to send trace to gateway
36
- * @internal
37
- */
41
+ /** @internal */
38
42
  async _sendTrace(request: CreateTraceRequest) {
39
43
  const metadata = { 'x-parallax-api-key': this.apiKey };
40
44
  return await this.client.createTrace(request, metadata);
41
45
  }
42
46
 
47
+ /** @internal */
48
+ async _updateTrace(request: UpdateTraceRequest) {
49
+ const metadata = { 'x-parallax-api-key': this.apiKey };
50
+ return await this.client.updateTrace(request, metadata);
51
+ }
52
+
43
53
  /**
44
54
  * Create a new trace builder
45
55
  *
46
- * Example usage:
47
- * ```typescript
48
- * const response = await client.trace("swap_execution")
49
- * .addAttribute("user", "0xabc...")
50
- * .addAttribute("slippage_bps", 25)
51
- * .addTag("dex")
52
- * .addTag("swap")
53
- * .addEvent("wallet_connected", { wallet: "MetaMask" })
54
- * .addEvent("quote_received")
55
- * .setTxHint("0x123...", "ethereum")
56
- * .create();
57
- * ```
58
- *
59
- * @param name Optional name of the trace (defaults to empty string)
60
- * @param includeClientMeta Optional flag to automatically include client metadata
56
+ * @param options Trace configuration options
61
57
  * @returns A ParallaxTrace builder instance
62
58
  */
63
- trace(name: string = '', includeClientMeta: boolean = true): ParallaxTrace {
64
- return new ParallaxTrace(this, name, includeClientMeta);
59
+ trace(options?: TraceOptions): ParallaxTrace {
60
+ return new ParallaxTrace(this, {
61
+ name: options?.name,
62
+ autoFlush: options?.autoFlush ?? DEFAULT_AUTO_FLUSH,
63
+ flushPeriodMs: options?.flushPeriodMs ?? DEFAULT_FLUSH_PERIOD_MS,
64
+ includeClientMeta: options?.includeClientMeta ?? DEFAULT_INCLUDE_CLIENT_META,
65
+ maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
66
+ retryBackoff: options?.retryBackoff ?? DEFAULT_RETRY_BACKOFF,
67
+ });
65
68
  }
66
69
  }
@@ -9,7 +9,8 @@ export { ParallaxTrace } from './trace';
9
9
 
10
10
  // Types
11
11
  export type {
12
- ParallaxClientConfig,
12
+ ParallaxClientOptions,
13
+ TraceOptions,
13
14
  ClientMetadata,
14
15
  TraceEvent,
15
16
  TxHashHint,
@@ -4,6 +4,11 @@
4
4
  import {
5
5
  CreateTraceRequest,
6
6
  CreateTraceResponse,
7
+ UpdateTraceRequest,
8
+ UpdateTraceResponse,
9
+ TraceData,
10
+ Attributes,
11
+ Tags,
7
12
  Event,
8
13
  TxHashHint as TxHashHintProto,
9
14
  Chain,
@@ -31,25 +36,78 @@ const CHAIN_MAP: Record<ChainName, Chain> = {
31
36
  */
32
37
  export interface TraceSubmitter {
33
38
  _sendTrace(request: CreateTraceRequest): Promise<CreateTraceResponse>;
39
+ _updateTrace(request: UpdateTraceRequest): Promise<UpdateTraceResponse>;
40
+ }
41
+
42
+ /** Options passed to ParallaxTrace constructor (with defaults applied) */
43
+ interface ResolvedTraceOptions {
44
+ name?: string;
45
+ autoFlush: boolean;
46
+ flushPeriodMs: number;
47
+ includeClientMeta: boolean;
48
+ maxRetries: number;
49
+ retryBackoff: number;
34
50
  }
35
51
 
36
52
  /**
37
- * Builder class for constructing traces with method chaining
38
- * Automatically handles web-specific features like client metadata
53
+ * Builder class for constructing traces with method chaining.
54
+ * Supports auto-flush mode (default) where data is automatically sent after a period of inactivity,
55
+ * or manual flush mode where you explicitly call flush().
39
56
  */
40
57
  export class ParallaxTrace {
41
- private name: string;
42
- private attributes: { [key: string]: string } = {};
43
- private tags: string[] = [];
44
- private events: TraceEvent[] = [];
45
- private txHashHint?: TxHashHint;
58
+ private name?: string;
46
59
  private client: TraceSubmitter;
47
60
  private includeClientMeta: boolean;
48
61
 
49
- constructor(client: TraceSubmitter, name: string = '', includeClientMeta: boolean = true) {
62
+ // Flush configuration
63
+ private autoFlush: boolean;
64
+ private flushPeriodMs: number;
65
+ private flushTimer: ReturnType<typeof setTimeout> | null = null;
66
+
67
+ // Retry configuration
68
+ private maxRetries: number;
69
+ private retryBackoff: number;
70
+
71
+ // State tracking
72
+ private traceId: string | null = null;
73
+ private pendingAttributes: { [key: string]: string } = {};
74
+ private pendingTags: string[] = [];
75
+ private pendingEvents: TraceEvent[] = [];
76
+ private pendingTxHashHints: TxHashHint[] = [];
77
+
78
+ // Queue for maintaining strict ordering of flushes
79
+ private flushQueue: Promise<void> = Promise.resolve();
80
+
81
+ constructor(client: TraceSubmitter, options: ResolvedTraceOptions) {
50
82
  this.client = client;
51
- this.name = name;
52
- this.includeClientMeta = includeClientMeta;
83
+ this.name = options.name;
84
+ this.autoFlush = options.autoFlush;
85
+ this.flushPeriodMs = options.flushPeriodMs;
86
+ this.includeClientMeta = options.includeClientMeta;
87
+ this.maxRetries = options.maxRetries;
88
+ this.retryBackoff = options.retryBackoff;
89
+ }
90
+
91
+ /**
92
+ * Schedule an auto-flush after the configured period.
93
+ * Resets the timer on each call.
94
+ * If flushPeriodMs is 0, flushes immediately on every call.
95
+ */
96
+ private scheduleFlush(): void {
97
+ if (!this.autoFlush) return;
98
+
99
+ // flushPeriodMs === 0 means flush immediately on every call
100
+ if (this.flushPeriodMs === 0) {
101
+ this.flush();
102
+ return;
103
+ }
104
+
105
+ if (this.flushTimer) {
106
+ clearTimeout(this.flushTimer);
107
+ }
108
+ this.flushTimer = setTimeout(() => {
109
+ this.flush();
110
+ }, this.flushPeriodMs);
53
111
  }
54
112
 
55
113
  /**
@@ -59,10 +117,11 @@ export class ParallaxTrace {
59
117
  * @returns This trace builder for chaining
60
118
  */
61
119
  addAttribute(key: string, value: string | number | boolean | object): this {
62
- this.attributes[key] =
120
+ this.pendingAttributes[key] =
63
121
  typeof value === 'object' && value !== null
64
122
  ? JSON.stringify(value)
65
123
  : String(value);
124
+ this.scheduleFlush();
66
125
  return this;
67
126
  }
68
127
 
@@ -73,11 +132,12 @@ export class ParallaxTrace {
73
132
  */
74
133
  addAttributes(attributes: { [key: string]: string | number | boolean | object }): this {
75
134
  for (const [key, value] of Object.entries(attributes)) {
76
- this.attributes[key] =
135
+ this.pendingAttributes[key] =
77
136
  typeof value === 'object' && value !== null
78
137
  ? JSON.stringify(value)
79
138
  : String(value);
80
139
  }
140
+ this.scheduleFlush();
81
141
  return this;
82
142
  }
83
143
 
@@ -87,7 +147,8 @@ export class ParallaxTrace {
87
147
  * @returns This trace builder for chaining
88
148
  */
89
149
  addTag(tag: string): this {
90
- this.tags.push(tag);
150
+ this.pendingTags.push(tag);
151
+ this.scheduleFlush();
91
152
  return this;
92
153
  }
93
154
 
@@ -97,7 +158,8 @@ export class ParallaxTrace {
97
158
  * @returns This trace builder for chaining
98
159
  */
99
160
  addTags(tags: string[]): this {
100
- this.tags.push(...tags);
161
+ this.pendingTags.push(...tags);
162
+ this.scheduleFlush();
101
163
  return this;
102
164
  }
103
165
 
@@ -113,97 +175,238 @@ export class ParallaxTrace {
113
175
  ? JSON.stringify(details)
114
176
  : details;
115
177
 
116
- this.events.push({
178
+ this.pendingEvents.push({
117
179
  eventName,
118
180
  details: detailsString,
119
181
  timestamp: timestamp || new Date(),
120
182
  });
183
+ this.scheduleFlush();
121
184
  return this;
122
185
  }
123
186
 
124
187
  /**
125
- * Set the transaction hash hint for blockchain correlation
188
+ * Add a transaction hash hint for blockchain correlation.
189
+ * Multiple hints can be added to the same trace.
126
190
  * @param txHash Transaction hash
127
191
  * @param chain Chain name (e.g., "ethereum", "polygon", "base")
128
192
  * @param details Optional details about the transaction
129
193
  * @returns This trace builder for chaining
130
194
  */
131
- setTxHint(txHash: string, chain: ChainName, details?: string): this {
132
- this.txHashHint = {
195
+ addTxHint(txHash: string, chain: ChainName, details?: string): this {
196
+ this.pendingTxHashHints.push({
133
197
  txHash,
134
198
  chain,
135
199
  details,
136
200
  timestamp: new Date(),
137
- };
201
+ });
202
+ this.scheduleFlush();
138
203
  return this;
139
204
  }
140
205
 
141
206
  /**
142
- * Create and submit the trace to the gateway
143
- * @returns The trace ID if successful, undefined if failed
207
+ * Flush pending data to the gateway.
208
+ * Fire-and-forget - returns immediately but maintains strict ordering of requests.
209
+ * First flush calls CreateTrace, subsequent flushes call UpdateTrace.
144
210
  */
145
- async create(): Promise<string | undefined> {
146
- // Build the CreateTraceRequest
147
- const request = new CreateTraceRequest();
148
- request.setName(this.name);
149
- request.setTagsList(this.tags);
211
+ flush(): void {
212
+ // Cancel any pending auto-flush timer
213
+ if (this.flushTimer) {
214
+ clearTimeout(this.flushTimer);
215
+ this.flushTimer = null;
216
+ }
217
+
218
+ // Check if there's anything to flush
219
+ const hasPendingData =
220
+ Object.keys(this.pendingAttributes).length > 0 ||
221
+ this.pendingTags.length > 0 ||
222
+ this.pendingEvents.length > 0 ||
223
+ this.pendingTxHashHints.length > 0;
150
224
 
151
- // Add attributes
152
- const attrsMap = request.getAttributesMap();
153
- for (const [key, value] of Object.entries(this.attributes)) {
154
- attrsMap.set(key, value);
225
+ if (!hasPendingData && this.traceId !== null) {
226
+ return; // Nothing to flush
155
227
  }
156
228
 
157
- // Add client metadata if requested
158
- if (this.includeClientMeta) {
159
- const clientMetadata = getClientMetadata();
160
- for (const [key, value] of Object.entries(clientMetadata)) {
161
- attrsMap.set(`client.${key}`, value);
229
+ // Capture pending data NOW (before it changes)
230
+ const traceData = this.buildTraceData();
231
+
232
+ // Capture context for error reporting
233
+ const isCreateOp = this.traceId === null;
234
+ const traceName = this.name;
235
+
236
+ // Clear pending immediately so next flush doesn't re-send
237
+ this.clearPending();
238
+
239
+ // Chain onto the queue for strict ordering
240
+ // Check traceId inside the queue to ensure proper ordering
241
+ this.flushQueue = this.flushQueue.then(async () => {
242
+ if (this.traceId === null) {
243
+ await this.createTrace(traceData);
244
+ } else {
245
+ await this.updateTrace(traceData);
162
246
  }
247
+ }).catch(err => {
248
+ const operation = isCreateOp ? 'CreateTrace' : 'UpdateTrace';
249
+ const context = traceName ? ` (trace: ${traceName})` : '';
250
+ console.error(`[ParallaxTrace] Flush error during ${operation}${context}:`, err);
251
+ });
252
+ }
253
+
254
+ /**
255
+ * Build TraceData from pending state
256
+ */
257
+ private buildTraceData(): TraceData {
258
+ const traceData = new TraceData();
259
+
260
+ // Add pending attributes (+ client metadata on first flush)
261
+ const allAttrs = { ...this.pendingAttributes };
262
+ if (this.traceId === null && this.includeClientMeta) {
263
+ const clientMeta = getClientMetadata();
264
+ for (const [key, value] of Object.entries(clientMeta)) {
265
+ allAttrs[`client.${key}`] = value;
266
+ }
267
+ }
268
+ if (Object.keys(allAttrs).length > 0) {
269
+ const attrsMsg = new Attributes();
270
+ const attrsMap = attrsMsg.getAttributesMap();
271
+ for (const [key, value] of Object.entries(allAttrs)) {
272
+ attrsMap.set(key, value);
273
+ }
274
+ traceData.addAttributes(attrsMsg);
275
+ }
276
+
277
+ // Add pending tags
278
+ if (this.pendingTags.length > 0) {
279
+ const tagsMsg = new Tags();
280
+ tagsMsg.setTagsList(this.pendingTags);
281
+ traceData.addTags(tagsMsg);
163
282
  }
164
283
 
165
- // Add events
166
- const eventsList: Event[] = [];
167
- for (const event of this.events) {
284
+ // Add pending events
285
+ for (const event of this.pendingEvents) {
168
286
  const eventMsg = new Event();
169
287
  eventMsg.setName(event.eventName);
170
288
  if (event.details) {
171
289
  eventMsg.setDetails(event.details);
172
290
  }
173
- const timestamp = new Timestamp();
174
- timestamp.fromDate(event.timestamp);
175
- eventMsg.setTimestamp(timestamp);
176
- eventsList.push(eventMsg);
291
+ const ts = new Timestamp();
292
+ ts.fromDate(event.timestamp);
293
+ eventMsg.setTimestamp(ts);
294
+ traceData.addEvents(eventMsg);
177
295
  }
178
- request.setEventsList(eventsList);
179
-
180
- // Add transaction hash hint if present
181
- if (this.txHashHint) {
182
- const txHint = new TxHashHintProto();
183
- txHint.setTxHash(this.txHashHint.txHash);
184
- txHint.setChain(CHAIN_MAP[this.txHashHint.chain]);
185
- if (this.txHashHint.details) {
186
- txHint.setDetails(this.txHashHint.details);
296
+
297
+ // Add pending tx hints
298
+ for (const hint of this.pendingTxHashHints) {
299
+ const hintMsg = new TxHashHintProto();
300
+ hintMsg.setTxHash(hint.txHash);
301
+ hintMsg.setChain(CHAIN_MAP[hint.chain]);
302
+ if (hint.details) {
303
+ hintMsg.setDetails(hint.details);
187
304
  }
188
- const timestamp = new Timestamp();
189
- timestamp.fromDate(this.txHashHint.timestamp);
190
- txHint.setTimestamp(timestamp);
191
- request.setTxHashHint(txHint);
305
+ const ts = new Timestamp();
306
+ ts.fromDate(hint.timestamp);
307
+ hintMsg.setTimestamp(ts);
308
+ traceData.addTxHashHints(hintMsg);
192
309
  }
193
310
 
194
- try {
195
- const response = await this.client._sendTrace(request);
196
- const responseStatus = response.getStatus();
311
+ return traceData;
312
+ }
197
313
 
198
- if (responseStatus?.getCode() !== ResponseStatus.StatusCode.STATUS_CODE_SUCCESS) {
199
- console.log('[ParallaxTrace] Error:', responseStatus?.getErrorMessage() || 'Unknown error');
200
- return undefined;
314
+ /**
315
+ * Sleep for the specified duration
316
+ */
317
+ private sleep(ms: number): Promise<void> {
318
+ return new Promise(resolve => setTimeout(resolve, ms));
319
+ }
320
+
321
+ /**
322
+ * Execute an operation with exponential backoff retry
323
+ */
324
+ private async retryWithBackoff<T>(
325
+ operation: () => Promise<T>,
326
+ operationName: string
327
+ ): Promise<T> {
328
+ let lastError: Error | undefined;
329
+
330
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
331
+ try {
332
+ return await operation();
333
+ } catch (err) {
334
+ lastError = err as Error;
335
+
336
+ if (attempt < this.maxRetries) {
337
+ const delay = this.retryBackoff * Math.pow(2, attempt);
338
+ console.warn(
339
+ `[ParallaxTrace] ${operationName} failed, retrying in ${delay}ms (attempt ${attempt + 1}/${this.maxRetries})`
340
+ );
341
+ await this.sleep(delay);
342
+ }
343
+ }
344
+ }
345
+
346
+ throw lastError;
347
+ }
348
+
349
+ /**
350
+ * Send CreateTrace request
351
+ */
352
+ private async createTrace(traceData: TraceData): Promise<void> {
353
+ const request = new CreateTraceRequest();
354
+ if (this.name) {
355
+ request.setName(this.name);
356
+ }
357
+ request.setData(traceData);
358
+
359
+ try {
360
+ const response = await this.retryWithBackoff(
361
+ () => this.client._sendTrace(request),
362
+ 'CreateTrace'
363
+ );
364
+ if (response.getStatus()?.getCode() === ResponseStatus.StatusCode.STATUS_CODE_SUCCESS) {
365
+ this.traceId = response.getTraceId();
366
+ } else {
367
+ console.error('[ParallaxTrace] CreateTrace failed:', response.getStatus()?.getErrorMessage());
201
368
  }
369
+ } catch (err) {
370
+ console.error('[ParallaxTrace] CreateTrace error after retries:', err);
371
+ }
372
+ }
202
373
 
203
- return response.getTraceId();
204
- } catch (error) {
205
- console.log('[ParallaxTrace] Error creating trace:', error);
206
- return undefined;
374
+ /**
375
+ * Send UpdateTrace request
376
+ */
377
+ private async updateTrace(traceData: TraceData): Promise<void> {
378
+ const request = new UpdateTraceRequest();
379
+ request.setTraceId(this.traceId!);
380
+ request.setData(traceData);
381
+
382
+ try {
383
+ const response = await this.retryWithBackoff(
384
+ () => this.client._updateTrace(request),
385
+ 'UpdateTrace'
386
+ );
387
+ if (response.getStatus()?.getCode() !== ResponseStatus.StatusCode.STATUS_CODE_SUCCESS) {
388
+ console.error('[ParallaxTrace] UpdateTrace failed:', response.getStatus()?.getErrorMessage());
389
+ }
390
+ } catch (err) {
391
+ console.error('[ParallaxTrace] UpdateTrace error after retries:', err);
207
392
  }
208
393
  }
394
+
395
+ /**
396
+ * Clear all pending data
397
+ */
398
+ private clearPending(): void {
399
+ this.pendingAttributes = {};
400
+ this.pendingTags = [];
401
+ this.pendingEvents = [];
402
+ this.pendingTxHashHints = [];
403
+ }
404
+
405
+ /**
406
+ * Get the trace ID (available after first flush completes)
407
+ * @returns The trace ID or null if not yet created
408
+ */
409
+ getTraceId(): string | null {
410
+ return this.traceId;
411
+ }
209
412
  }
@@ -3,13 +3,31 @@
3
3
  */
4
4
 
5
5
  /**
6
- * Configuration options for ParallaxClient
6
+ * Options for ParallaxClient constructor
7
7
  */
8
- export interface ParallaxClientConfig {
9
- apiKey: string;
8
+ export interface ParallaxClientOptions {
9
+ /** Gateway URL (defaults to parallax-gateway-dev.mirador.org:443) */
10
10
  apiUrl?: string;
11
11
  }
12
12
 
13
+ /**
14
+ * Options for creating a trace
15
+ */
16
+ export interface TraceOptions {
17
+ /** Trace name */
18
+ name?: string;
19
+ /** Enable auto-flush mode (default: true) */
20
+ autoFlush?: boolean;
21
+ /** Debounce period in ms before auto-flush triggers (default: 50) */
22
+ flushPeriodMs?: number;
23
+ /** Include browser/OS metadata in first flush (default: true) */
24
+ includeClientMeta?: boolean;
25
+ /** Maximum number of retry attempts on failure (default: 3) */
26
+ maxRetries?: number;
27
+ /** Base delay in ms for exponential backoff between retries (default: 1000) */
28
+ retryBackoff?: number;
29
+ }
30
+
13
31
  /**
14
32
  * Client metadata collected from the browser environment
15
33
  */