@introspection-sdk/introspection-browser 0.20.0 → 0.21.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/src/client.ts DELETED
@@ -1,796 +0,0 @@
1
- /**
2
- * Introspection Client for Browser
3
- * Provides an API for tracking events and feedback using OTLP Logs.
4
- *
5
- * Identity and gen_ai context can be managed via baggage (requires OpenTelemetry
6
- * browser SDK with Zone.js for automatic propagation) or via client instance.
7
- */
8
-
9
- import { SeverityNumber, type LogAttributes } from "@opentelemetry/api-logs";
10
- import {
11
- LoggerProvider,
12
- BatchLogRecordProcessor,
13
- } from "@opentelemetry/sdk-logs";
14
- import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
15
- import { propagation, context } from "@opentelemetry/api";
16
- import { VERSION } from "./version.js";
17
- import {
18
- generateEventId,
19
- type IntrospectionClientOptions,
20
- type FeedbackOptions,
21
- type UserTraits,
22
- type GenAiContext,
23
- type IdentityContext,
24
- } from "./types.js";
25
-
26
- const ANONYMOUS_ID_KEY = "introspection_anonymous_id";
27
- const USER_ID_KEY = "introspection_user_id";
28
- const TRAITS_KEY = "introspection_traits";
29
-
30
- /**
31
- * Generate a UUID-like anonymous ID
32
- */
33
- function generateAnonymousId(): string {
34
- const segments = [];
35
- for (let i = 0; i < 4; i++) {
36
- segments.push(Math.random().toString(16).substring(2, 6));
37
- }
38
- return segments.join("-");
39
- }
40
-
41
- /**
42
- * Simple logger that respects debug flag
43
- */
44
- class Logger {
45
- constructor(private debug: boolean) {}
46
-
47
- log(...args: unknown[]): void {
48
- if (this.debug) {
49
- console.log("[introspection-sdk]", ...args);
50
- }
51
- }
52
-
53
- warn(...args: unknown[]): void {
54
- console.warn("[introspection-sdk]", ...args);
55
- }
56
-
57
- error(...args: unknown[]): void {
58
- console.error("[introspection-sdk]", ...args);
59
- }
60
- }
61
-
62
- /**
63
- * Introspection client for tracking events and feedback in the browser using OTLP Logs.
64
- *
65
- * Identity (user_id, anonymous_id) and gen_ai context (conversation_id, previous_response_id, agent)
66
- * can be managed via client instance (persisted to localStorage) or via OpenTelemetry baggage
67
- * for scoped context.
68
- *
69
- * Note: Automatic baggage propagation in browsers requires Zone.js from @opentelemetry/context-zone.
70
- *
71
- * @example
72
- * ```typescript
73
- * const client = new IntrospectionClient({
74
- * token: "intro_xxx",
75
- * debug: true,
76
- * });
77
- *
78
- * // Set identity once (persisted to localStorage)
79
- * client.identify("user_123", { email: "user@example.com" });
80
- *
81
- * // Simple feedback
82
- * client.feedback("thumbs_up", { comments: "Great response!" });
83
- *
84
- * // With explicit gen_ai context
85
- * client.feedback("thumbs_down", {
86
- * conversationId: "conv_456",
87
- * previousResponseId: "msg_123",
88
- * comments: "Off topic",
89
- * });
90
- *
91
- * // With scoped baggage context
92
- * await client.withConversation("conv_456", undefined, async () => {
93
- * client.feedback("thumbs_up"); // automatically includes conversation_id
94
- * });
95
- *
96
- * // Track a custom event
97
- * client.track("Button Clicked", { buttonId: "submit" });
98
- * ```
99
- */
100
- export class IntrospectionClient {
101
- private loggerProvider: LoggerProvider;
102
- private otelLogger: ReturnType<LoggerProvider["getLogger"]>;
103
- private userId: string | undefined;
104
- private anonymousId: string;
105
- private traits: Record<string, unknown> = {};
106
- private logger: Logger;
107
-
108
- /**
109
- * @param options - Client configuration (token, service name, advanced settings).
110
- */
111
- constructor(options: IntrospectionClientOptions = {}) {
112
- const token = options.token || "";
113
- const advanced = options.advanced || {};
114
- const baseUrl =
115
- advanced.baseUrl ||
116
- (typeof process !== "undefined" &&
117
- process.env?.INTROSPECTION_BASE_OTEL_URL) ||
118
- "https://otel.introspection.dev";
119
- this.logger = new Logger(advanced.debug ?? false);
120
-
121
- // Load persisted state from localStorage
122
- this.anonymousId = this.loadAnonymousId();
123
- this.userId = this.loadUserId();
124
- this.traits = this.loadTraits();
125
-
126
- if (!token) {
127
- this.logger.warn("No token provided. Events will not be sent.");
128
- }
129
-
130
- // Construct endpoint URL for logs
131
- let endpoint: string;
132
- if (baseUrl.endsWith("/v1/logs")) {
133
- endpoint = baseUrl;
134
- } else {
135
- endpoint = `${baseUrl.replace(/\/$/, "")}/v1/logs`;
136
- }
137
-
138
- // Build headers
139
- const headers: Record<string, string> = {
140
- Authorization: `Bearer ${token}`,
141
- };
142
- if (advanced.additionalHeaders) {
143
- Object.assign(headers, advanced.additionalHeaders);
144
- }
145
-
146
- // Create OTLP log exporter with HTTP/JSON
147
- const exporter = new OTLPLogExporter({
148
- url: endpoint,
149
- headers,
150
- });
151
-
152
- // Create batch processor
153
- const processor = new BatchLogRecordProcessor({
154
- exporter,
155
- maxQueueSize: advanced.maxBatchSize ?? 100,
156
- scheduledDelayMillis: advanced.flushInterval ?? 5000,
157
- });
158
-
159
- // Create logger provider with processor
160
- this.loggerProvider = new LoggerProvider({
161
- processors: [processor],
162
- });
163
-
164
- // Get a logger instance
165
- this.otelLogger = this.loggerProvider.getLogger(
166
- "@introspection-sdk/introspection-browser",
167
- VERSION,
168
- );
169
-
170
- // Flush on page unload
171
- if (typeof window !== "undefined") {
172
- window.addEventListener("beforeunload", () => {
173
- this.loggerProvider.forceFlush();
174
- });
175
-
176
- document.addEventListener("visibilitychange", () => {
177
- if (document.visibilityState === "hidden") {
178
- this.loggerProvider.forceFlush();
179
- }
180
- });
181
- }
182
-
183
- this.logger.log(`Initialized with endpoint: ${endpoint}`);
184
- }
185
-
186
- /**
187
- * Load anonymous ID from localStorage, or generate a new one
188
- */
189
- private loadAnonymousId(): string {
190
- if (typeof localStorage !== "undefined") {
191
- const stored = localStorage.getItem(ANONYMOUS_ID_KEY);
192
- if (stored) {
193
- return stored;
194
- }
195
- }
196
- const newId = generateAnonymousId();
197
- this.persistAnonymousId(newId);
198
- return newId;
199
- }
200
-
201
- /**
202
- * Persist anonymous ID to localStorage
203
- */
204
- private persistAnonymousId(id: string): void {
205
- if (typeof localStorage !== "undefined") {
206
- localStorage.setItem(ANONYMOUS_ID_KEY, id);
207
- }
208
- }
209
-
210
- /**
211
- * Load user ID from localStorage
212
- */
213
- private loadUserId(): string | undefined {
214
- if (typeof localStorage !== "undefined") {
215
- return localStorage.getItem(USER_ID_KEY) || undefined;
216
- }
217
- return undefined;
218
- }
219
-
220
- /**
221
- * Persist user ID to localStorage
222
- */
223
- private persistUserId(id: string): void {
224
- if (typeof localStorage !== "undefined") {
225
- localStorage.setItem(USER_ID_KEY, id);
226
- }
227
- }
228
-
229
- /**
230
- * Load traits from localStorage
231
- */
232
- private loadTraits(): Record<string, unknown> {
233
- if (typeof localStorage !== "undefined") {
234
- const stored = localStorage.getItem(TRAITS_KEY);
235
- if (stored) {
236
- try {
237
- return JSON.parse(stored);
238
- } catch {
239
- return {};
240
- }
241
- }
242
- }
243
- return {};
244
- }
245
-
246
- /**
247
- * Persist traits to localStorage
248
- */
249
- private persistTraits(traits: Record<string, unknown>): void {
250
- if (typeof localStorage !== "undefined") {
251
- localStorage.setItem(TRAITS_KEY, JSON.stringify(traits));
252
- }
253
- }
254
-
255
- /** Get gen_ai context from baggage */
256
- private getGenAiFromContext(): GenAiContext {
257
- const baggage = propagation.getBaggage(context.active());
258
- return {
259
- conversationId: baggage?.getEntry("gen_ai.conversation.id")?.value,
260
- previousResponseId: baggage?.getEntry(
261
- "gen_ai.request.previous_response_id",
262
- )?.value,
263
- agentName: baggage?.getEntry("gen_ai.agent.name")?.value,
264
- agentId: baggage?.getEntry("gen_ai.agent.id")?.value,
265
- };
266
- }
267
-
268
- /** Get identity from baggage or client instance */
269
- private getIdentityFromContext(): IdentityContext {
270
- const baggage = propagation.getBaggage(context.active());
271
- return {
272
- userId: baggage?.getEntry("identity.user_id")?.value || this.userId,
273
- anonymousId:
274
- baggage?.getEntry("identity.anonymous_id")?.value || this.anonymousId,
275
- };
276
- }
277
-
278
- /**
279
- * Get current timestamp as OpenTelemetry HrTime.
280
- * Uses performance.now() for high-resolution timing in browsers.
281
- */
282
- private getTimestamp(): [number, number] {
283
- const timeOrigin = performance.timeOrigin;
284
- const now = performance.now();
285
- const epochMs = timeOrigin + now;
286
- const seconds = Math.floor(epochMs / 1000);
287
- const nanos = Math.floor((epochMs % 1000) * 1_000_000);
288
- return [seconds, nanos];
289
- }
290
-
291
- /**
292
- * Build log record attributes.
293
- */
294
- private buildAttributes(
295
- eventName: string,
296
- options: {
297
- properties?: Record<string, unknown>;
298
- traits?: Record<string, unknown>;
299
- conversationId?: string;
300
- previousResponseId?: string;
301
- eventId?: string;
302
- } = {},
303
- ): LogAttributes {
304
- const { properties, traits, conversationId, previousResponseId, eventId } =
305
- options;
306
-
307
- // Core fields
308
- const attributes: LogAttributes = {
309
- "event.name": eventName,
310
- "event.id": eventId || generateEventId(),
311
- };
312
-
313
- // Browser context
314
- if (typeof window !== "undefined") {
315
- attributes["context.page.path"] = window.location.pathname;
316
- attributes["context.page.url"] = window.location.href;
317
- attributes["context.page.title"] = document.title;
318
- attributes["context.page.referrer"] = document.referrer;
319
- attributes["context.page.search"] = window.location.search;
320
- attributes["context.userAgent"] = navigator.userAgent;
321
- attributes["context.locale"] = navigator.language;
322
- attributes["context.timezone"] =
323
- Intl.DateTimeFormat().resolvedOptions().timeZone;
324
- attributes["context.screen.width"] = window.screen.width;
325
- attributes["context.screen.height"] = window.screen.height;
326
- attributes["context.screen.density"] = window.devicePixelRatio;
327
- }
328
-
329
- // Identity
330
- const identity = this.getIdentityFromContext();
331
- if (identity.userId) {
332
- attributes["identity.user.id"] = identity.userId;
333
- }
334
- if (identity.anonymousId) {
335
- attributes["identity.anonymous.id"] = identity.anonymousId;
336
- }
337
-
338
- // Gen AI context (explicit params override baggage)
339
- const genAi = this.getGenAiFromContext();
340
- const finalConversationId = conversationId || genAi.conversationId;
341
- const finalPreviousResponseId =
342
- previousResponseId || genAi.previousResponseId;
343
-
344
- if (finalConversationId) {
345
- attributes["gen_ai.conversation.id"] = finalConversationId;
346
- }
347
- if (finalPreviousResponseId) {
348
- attributes["gen_ai.request.previous_response_id"] =
349
- finalPreviousResponseId;
350
- }
351
- if (genAi.agentName) {
352
- attributes["gen_ai.agent.name"] = genAi.agentName;
353
- }
354
- if (genAi.agentId) {
355
- attributes["gen_ai.agent.id"] = genAi.agentId;
356
- }
357
-
358
- // Flatten properties with "properties." prefix
359
- if (properties) {
360
- for (const [key, value] of Object.entries(properties)) {
361
- if (value != null) {
362
- const attrKey = `properties.${key}`;
363
- if (typeof value === "object") {
364
- attributes[attrKey] = JSON.stringify(value);
365
- } else if (
366
- typeof value === "string" ||
367
- typeof value === "number" ||
368
- typeof value === "boolean"
369
- ) {
370
- attributes[attrKey] = value;
371
- }
372
- }
373
- }
374
- }
375
-
376
- // Flatten traits with "context.traits." prefix
377
- if (traits) {
378
- for (const [key, value] of Object.entries(traits)) {
379
- if (value != null) {
380
- const attrKey = `context.traits.${key}`;
381
- if (typeof value === "object") {
382
- attributes[attrKey] = JSON.stringify(value);
383
- } else if (
384
- typeof value === "string" ||
385
- typeof value === "number" ||
386
- typeof value === "boolean"
387
- ) {
388
- attributes[attrKey] = value;
389
- }
390
- }
391
- }
392
- }
393
-
394
- return attributes;
395
- }
396
-
397
- /**
398
- * Track a custom event.
399
- *
400
- * @param eventName - The event name (e.g., `"Button Clicked"`).
401
- * @param properties - Optional properties to attach to the event.
402
- * @param options - Optional overrides (e.g., custom `eventId`).
403
- *
404
- * @example
405
- * ```ts
406
- * client.track("Button Clicked", { buttonId: "submit" });
407
- * ```
408
- */
409
- track(
410
- eventName: string,
411
- properties?: Record<string, unknown>,
412
- options?: { eventId?: string },
413
- ): void {
414
- const attributes = this.buildAttributes(eventName, {
415
- properties,
416
- eventId: options?.eventId,
417
- });
418
-
419
- this.otelLogger.emit({
420
- timestamp: this.getTimestamp(),
421
- context: context.active(),
422
- severityNumber: SeverityNumber.INFO,
423
- severityText: "INFO",
424
- attributes,
425
- });
426
-
427
- this.logger.log(`Tracked: ${eventName}`);
428
- }
429
-
430
- /**
431
- * Track feedback on a message or response.
432
- *
433
- * gen_ai.conversation.id, gen_ai.request.previous_response_id, and agent context
434
- * are automatically extracted from baggage if not provided explicitly.
435
- *
436
- * @param name - Feedback name/action (e.g., "thumbs_up", "thumbs_down")
437
- * @param options - Optional feedback options
438
- *
439
- * @example
440
- * ```typescript
441
- * // Simple feedback
442
- * client.feedback("thumbs_up");
443
- *
444
- * // With comments
445
- * client.feedback("thumbs_down", { comments: "Off topic" });
446
- *
447
- * // With explicit gen_ai context
448
- * client.feedback("thumbs_up", {
449
- * conversationId: "conv_456",
450
- * previousResponseId: "msg_123",
451
- * });
452
- *
453
- * // With baggage context
454
- * await client.withConversation("conv_456", undefined, async () => {
455
- * client.feedback("thumbs_up"); // auto-includes conversation_id
456
- * });
457
- * ```
458
- */
459
- feedback(name: string, options: FeedbackOptions = {}): void {
460
- const { comments, conversationId, previousResponseId, eventId, ...extra } =
461
- options;
462
-
463
- const properties: Record<string, unknown> = { name, ...extra };
464
- if (comments) {
465
- properties.comments = comments;
466
- }
467
-
468
- const attributes = this.buildAttributes("introspection.feedback", {
469
- properties,
470
- conversationId: conversationId as string | undefined,
471
- previousResponseId: previousResponseId as string | undefined,
472
- eventId: eventId as string | undefined,
473
- });
474
-
475
- this.otelLogger.emit({
476
- timestamp: this.getTimestamp(),
477
- context: context.active(),
478
- severityNumber: SeverityNumber.INFO,
479
- severityText: "INFO",
480
- attributes,
481
- });
482
-
483
- this.logger.log(`Feedback: ${name}`);
484
- }
485
-
486
- /**
487
- * Identify a user and persist their traits to `localStorage`.
488
- *
489
- * @param userId - The user's unique identifier.
490
- * @param traits - Optional user traits (email, name, plan, etc.).
491
- * @param anonymousId - Optional anonymous ID to associate.
492
- * @param eventId - Optional event ID (auto-generated if omitted).
493
- *
494
- * @example
495
- * ```ts
496
- * client.identify("user-123", { email: "user@example.com", plan: "pro" });
497
- * ```
498
- */
499
- identify(
500
- userId: string,
501
- traits?: UserTraits,
502
- anonymousId?: string,
503
- eventId?: string,
504
- ): void {
505
- // Store and persist
506
- this.userId = userId;
507
- this.persistUserId(userId);
508
- if (anonymousId) {
509
- this.anonymousId = anonymousId;
510
- this.persistAnonymousId(anonymousId);
511
- }
512
- if (traits) {
513
- this.traits = { ...this.traits, ...traits };
514
- this.persistTraits(this.traits);
515
- }
516
-
517
- const attributes = this.buildAttributes("identify", { traits, eventId });
518
-
519
- this.otelLogger.emit({
520
- timestamp: this.getTimestamp(),
521
- context: context.active(),
522
- severityNumber: SeverityNumber.INFO,
523
- severityText: "INFO",
524
- attributes,
525
- });
526
-
527
- this.logger.log(`Identified: ${userId}`);
528
- }
529
-
530
- /**
531
- * Set the authenticated user ID and persist it to `localStorage`.
532
- *
533
- * @param userId - The user's unique identifier.
534
- *
535
- * @example
536
- * ```ts
537
- * client.setUserId("user-123");
538
- * ```
539
- */
540
- setUserId(userId: string): void {
541
- this.userId = userId;
542
- this.persistUserId(userId);
543
- }
544
-
545
- /**
546
- * Override the anonymous ID and persist it to `localStorage`.
547
- *
548
- * @param anonymousId - The anonymous visitor identifier.
549
- *
550
- * @example
551
- * ```ts
552
- * client.setAnonymousId("anon-abc");
553
- * ```
554
- */
555
- setAnonymousId(anonymousId: string): void {
556
- this.anonymousId = anonymousId;
557
- this.persistAnonymousId(anonymousId);
558
- }
559
-
560
- /**
561
- * Get the current anonymous visitor identifier.
562
- *
563
- * @returns The anonymous ID (auto-generated on first access if not set).
564
- *
565
- * @example
566
- * ```ts
567
- * const anonId = client.getAnonymousId();
568
- * ```
569
- */
570
- getAnonymousId(): string {
571
- return this.anonymousId;
572
- }
573
-
574
- /**
575
- * Create a context with baggage values set.
576
- * Use with context.with() to run code in that context.
577
- *
578
- * Note: In browsers, automatic context propagation requires Zone.js.
579
- * Without it, you must manually pass context through async operations.
580
- *
581
- * @param values - Key/value pairs to set as baggage
582
- * @returns Context with baggage set
583
- *
584
- * @example
585
- * ```typescript
586
- * const ctx = client.createBaggageContext({
587
- * "gen_ai.agent.name": "support-bot",
588
- * "gen_ai.conversation.id": "conv_456"
589
- * });
590
- *
591
- * await context.with(ctx, async () => {
592
- * client.feedback("thumbs_up"); // picks up baggage
593
- * });
594
- * ```
595
- */
596
- createBaggageContext(values: Record<string, string>) {
597
- const ctx = context.active();
598
- let bag = propagation.getBaggage(ctx) || propagation.createBaggage();
599
-
600
- for (const [key, value] of Object.entries(values)) {
601
- bag = bag.setEntry(key, { value });
602
- }
603
-
604
- return propagation.setBaggage(ctx, bag);
605
- }
606
-
607
- /**
608
- * Helper to set multiple baggage values and run a callback.
609
- *
610
- * @param values - Key/value pairs to set as baggage.
611
- * @param callback - Function to run with baggage context.
612
- * @returns The return value of the callback.
613
- *
614
- * @example
615
- * ```typescript
616
- * await client.withBaggage({
617
- * "gen_ai.conversation.id": "conv_456",
618
- * "gen_ai.request.previous_response_id": "resp_123"
619
- * }, async () => {
620
- * client.feedback("thumbs_up");
621
- * });
622
- * ```
623
- */
624
- async withBaggage<T>(
625
- values: Record<string, string>,
626
- callback: () => T | Promise<T>,
627
- ): Promise<T> {
628
- const ctx = this.createBaggageContext(values);
629
- return await context.with(ctx, callback);
630
- }
631
-
632
- /**
633
- * Helper to set agent baggage and run a callback.
634
- *
635
- * @param agentName - Name of the agent.
636
- * @param agentId - Optional unique identifier for the agent.
637
- * @param callback - Function to run with agent context.
638
- * @returns The return value of the callback.
639
- *
640
- * @example
641
- * ```typescript
642
- * await client.withAgent("support-bot", "agent_123", async () => {
643
- * client.feedback("thumbs_up");
644
- * });
645
- * ```
646
- */
647
- async withAgent<T>(
648
- agentName: string,
649
- agentId: string | undefined,
650
- callback: () => T | Promise<T>,
651
- ): Promise<T> {
652
- const values: Record<string, string> = {
653
- "gen_ai.agent.name": agentName,
654
- };
655
- if (agentId) {
656
- values["gen_ai.agent.id"] = agentId;
657
- }
658
- const ctx = this.createBaggageContext(values);
659
- return await context.with(ctx, callback);
660
- }
661
-
662
- /**
663
- * Helper to set conversation baggage and run a callback.
664
- *
665
- * @param conversationId - Unique identifier for the conversation (optional).
666
- * @param previousResponseId - Previous response ID for conversation continuity (optional).
667
- * @param callback - Function to run with conversation context.
668
- * @returns The return value of the callback.
669
- *
670
- * @example
671
- * ```typescript
672
- * // Simple conversation context
673
- * await client.withConversation("conv_456", undefined, async () => {
674
- * client.feedback("thumbs_up");
675
- * });
676
- *
677
- * // With previous response for conversation continuity
678
- * await client.withConversation("conv_456", "resp_123", async () => {
679
- * client.feedback("thumbs_up");
680
- * });
681
- *
682
- * // Just previousResponseId when already in a conversation
683
- * await client.withConversation("conv_456", undefined, async () => {
684
- * await client.withConversation(undefined, "resp_1", async () => {
685
- * client.feedback("thumbs_up");
686
- * });
687
- * });
688
- * ```
689
- */
690
- async withConversation<T>(
691
- conversationId: string | undefined,
692
- previousResponseId: string | undefined,
693
- callback: () => T | Promise<T>,
694
- ): Promise<T> {
695
- const values: Record<string, string> = {};
696
- if (conversationId) {
697
- values["gen_ai.conversation.id"] = conversationId;
698
- }
699
- if (previousResponseId) {
700
- values["gen_ai.request.previous_response_id"] = previousResponseId;
701
- }
702
- const ctx = this.createBaggageContext(values);
703
- return await context.with(ctx, callback);
704
- }
705
-
706
- /**
707
- * Helper to set user ID baggage and run a callback.
708
- *
709
- * @param userId - User identifier.
710
- * @param callback - Function to run with user context.
711
- * @returns The return value of the callback.
712
- *
713
- * @example
714
- * ```typescript
715
- * await client.withUserId("user_123", async () => {
716
- * client.feedback("thumbs_up");
717
- * });
718
- * ```
719
- */
720
- async withUserId<T>(
721
- userId: string,
722
- callback: () => T | Promise<T>,
723
- ): Promise<T> {
724
- const ctx = this.createBaggageContext({
725
- "identity.user_id": userId,
726
- });
727
- return await context.with(ctx, callback);
728
- }
729
-
730
- /**
731
- * Helper to set anonymous ID baggage and run a callback.
732
- *
733
- * @param anonymousId - Anonymous visitor identifier.
734
- * @param callback - Function to run with anonymous context.
735
- * @returns The return value of the callback.
736
- *
737
- * @example
738
- * ```ts
739
- * await client.withAnonymousId("anon-abc", async () => {
740
- * client.feedback("thumbs_up");
741
- * });
742
- * ```
743
- */
744
- async withAnonymousId<T>(
745
- anonymousId: string,
746
- callback: () => T | Promise<T>,
747
- ): Promise<T> {
748
- const ctx = this.createBaggageContext({
749
- "identity.anonymous_id": anonymousId,
750
- });
751
- return await context.with(ctx, callback);
752
- }
753
-
754
- /**
755
- * Reset the client state, clearing user identity and generating a new
756
- * anonymous ID. Equivalent to a logout operation.
757
- *
758
- * @example
759
- * ```ts
760
- * client.reset();
761
- * ```
762
- */
763
- reset(): void {
764
- this.userId = undefined;
765
- this.anonymousId = generateAnonymousId();
766
- this.traits = {};
767
-
768
- if (typeof localStorage !== "undefined") {
769
- localStorage.removeItem(USER_ID_KEY);
770
- localStorage.removeItem(TRAITS_KEY);
771
- localStorage.setItem(ANONYMOUS_ID_KEY, this.anonymousId);
772
- }
773
-
774
- this.logger.log("Reset complete");
775
- }
776
-
777
- /**
778
- * Flush all queued log records to the Introspection backend.
779
- *
780
- * @returns A promise that resolves once the flush completes.
781
- */
782
- async flush(): Promise<void> {
783
- await this.loggerProvider.forceFlush();
784
- this.logger.log("Flushed pending log records");
785
- }
786
-
787
- /**
788
- * Shut down the client, flushing all pending log records.
789
- *
790
- * @returns A promise that resolves once shutdown is complete.
791
- */
792
- async shutdown(): Promise<void> {
793
- await this.loggerProvider.shutdown();
794
- this.logger.log("Shutdown complete");
795
- }
796
- }