@scoperat/tracker 0.2.0 → 0.3.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/dist/index.d.ts +39 -1
- package/dist/index.js +160 -13
- package/dist/index.js.map +1 -1
- package/dist/widget/index.js +160 -13
- package/dist/widget/index.js.map +1 -1
- package/dist/widget.global.js +48 -48
- package/dist/widget.global.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -93,6 +93,8 @@ declare class FlagClient {
|
|
|
93
93
|
private fetch;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/** Why a session ended / began, attached as the event's `reason` property. */
|
|
97
|
+
type SessionReason = "initial" | "login" | "logout" | "timeout";
|
|
96
98
|
interface StoredTicketSession {
|
|
97
99
|
anonymousId: string;
|
|
98
100
|
token: string;
|
|
@@ -113,6 +115,7 @@ declare class TrackerImpl {
|
|
|
113
115
|
private traits;
|
|
114
116
|
private userHash;
|
|
115
117
|
private anonymousId;
|
|
118
|
+
private sessionId;
|
|
116
119
|
private sessionTimeout;
|
|
117
120
|
private ticketSessionFetch;
|
|
118
121
|
private _flagClient;
|
|
@@ -121,6 +124,26 @@ declare class TrackerImpl {
|
|
|
121
124
|
get flags(): FlagClient;
|
|
122
125
|
identify(userId: string, traits?: IdentifyTraits, options?: IdentifyOptions): void;
|
|
123
126
|
reset(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Begin a new analytics session, ending the current one first if it
|
|
129
|
+
* exists. Emits `$session_end` (for the old session) and
|
|
130
|
+
* `$session_start` (for the new). Called automatically on login via
|
|
131
|
+
* {@link identify}; also exposed for explicit control.
|
|
132
|
+
*/
|
|
133
|
+
startSession(reason?: SessionReason): string | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* End the current analytics session (if any), emitting `$session_end`,
|
|
136
|
+
* and clear the stored session. The next tracked event will lazily
|
|
137
|
+
* start a fresh session. Called automatically on logout via
|
|
138
|
+
* {@link reset}; also exposed for explicit control.
|
|
139
|
+
*
|
|
140
|
+
* @param immediate - flush buffered events synchronously (via
|
|
141
|
+
* `sendBeacon`) so the end event isn't lost if the page unloads.
|
|
142
|
+
*/
|
|
143
|
+
endSession(reason?: SessionReason, immediate?: boolean): void;
|
|
144
|
+
private readStoredUserId;
|
|
145
|
+
private writeStoredUserId;
|
|
146
|
+
private clearStoredUserId;
|
|
124
147
|
private clearTicketSession;
|
|
125
148
|
private readStoredTicketSession;
|
|
126
149
|
/**
|
|
@@ -131,7 +154,22 @@ declare class TrackerImpl {
|
|
|
131
154
|
* API calls don't each trigger a `POST /ticket/session`.
|
|
132
155
|
*/
|
|
133
156
|
_ensureTicketSession(): Promise<StoredTicketSession | null>;
|
|
134
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Return the id of the current session, rotating it when the previous
|
|
159
|
+
* session has expired through inactivity. On rotation it emits a
|
|
160
|
+
* `$session_end` for the timed-out session (backdated to the last
|
|
161
|
+
* recorded activity) and a `$session_start` for the new one. Called on
|
|
162
|
+
* every tracked event so sessions advance with real user activity.
|
|
163
|
+
*/
|
|
164
|
+
private ensureSession;
|
|
165
|
+
private readStoredSession;
|
|
166
|
+
private readLastActivity;
|
|
167
|
+
private persistSession;
|
|
168
|
+
/**
|
|
169
|
+
* Enqueue a session lifecycle event already stamped with its session id
|
|
170
|
+
* (so it never re-enters {@link ensureSession} and risks recursion).
|
|
171
|
+
*/
|
|
172
|
+
private emitSessionEvent;
|
|
135
173
|
private clearSession;
|
|
136
174
|
getSessionId(): string | undefined;
|
|
137
175
|
/** @internal Used by the widget to read tracker config. */
|
package/dist/index.js
CHANGED
|
@@ -266,6 +266,9 @@ var MAX_BATCH_SIZE = 100;
|
|
|
266
266
|
var ANON_ID_KEY = "__scoperat_anon_id";
|
|
267
267
|
var SESSION_ID_KEY = "__scoperat_session_id";
|
|
268
268
|
var LAST_ACTIVITY_KEY = "__scoperat_last_activity";
|
|
269
|
+
var USER_ID_KEY = "__scoperat_user_id";
|
|
270
|
+
var SESSION_START_EVENT = "$session_start";
|
|
271
|
+
var SESSION_END_EVENT = "$session_end";
|
|
269
272
|
var TICKET_SESSION_KEY = "__scoperat_ticket_session";
|
|
270
273
|
function generateAnonId() {
|
|
271
274
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
@@ -307,6 +310,7 @@ var TrackerImpl = class {
|
|
|
307
310
|
traits = {};
|
|
308
311
|
userHash;
|
|
309
312
|
anonymousId;
|
|
313
|
+
sessionId;
|
|
310
314
|
sessionTimeout = DEFAULT_SESSION_TIMEOUT;
|
|
311
315
|
ticketSessionFetch = null;
|
|
312
316
|
_flagClient = null;
|
|
@@ -369,6 +373,7 @@ var TrackerImpl = class {
|
|
|
369
373
|
return this._flagClient;
|
|
370
374
|
}
|
|
371
375
|
identify(userId, traits, options) {
|
|
376
|
+
const previousUserId = this.userId ?? this.readStoredUserId();
|
|
372
377
|
this.userId = userId;
|
|
373
378
|
if (traits) {
|
|
374
379
|
this.traits = { ...traits };
|
|
@@ -379,17 +384,88 @@ var TrackerImpl = class {
|
|
|
379
384
|
if (!evt.user_id) evt.user_id = userId;
|
|
380
385
|
if (!evt.org_id && this.orgId) evt.org_id = this.orgId;
|
|
381
386
|
}
|
|
387
|
+
if (userId !== previousUserId) {
|
|
388
|
+
this.writeStoredUserId(userId);
|
|
389
|
+
this.startSession("login");
|
|
390
|
+
}
|
|
382
391
|
this._flagClient?.refresh();
|
|
383
392
|
}
|
|
384
393
|
reset() {
|
|
394
|
+
this.endSession("logout", true);
|
|
385
395
|
this.userId = void 0;
|
|
386
396
|
this.orgId = void 0;
|
|
387
397
|
this.traits = {};
|
|
388
398
|
this.userHash = void 0;
|
|
389
399
|
this.anonymousId = getOrCreateAnonId();
|
|
390
|
-
this.
|
|
400
|
+
this.clearStoredUserId();
|
|
391
401
|
this.clearTicketSession();
|
|
392
402
|
}
|
|
403
|
+
/**
|
|
404
|
+
* Begin a new analytics session, ending the current one first if it
|
|
405
|
+
* exists. Emits `$session_end` (for the old session) and
|
|
406
|
+
* `$session_start` (for the new). Called automatically on login via
|
|
407
|
+
* {@link identify}; also exposed for explicit control.
|
|
408
|
+
*/
|
|
409
|
+
startSession(reason = "login") {
|
|
410
|
+
if (typeof window === "undefined") return this.sessionId;
|
|
411
|
+
const now = Date.now();
|
|
412
|
+
const existingId = this.readStoredSession();
|
|
413
|
+
if (existingId) {
|
|
414
|
+
this.emitSessionEvent(SESSION_END_EVENT, existingId, reason);
|
|
415
|
+
}
|
|
416
|
+
const newId = generateAnonId();
|
|
417
|
+
this.persistSession(newId, now);
|
|
418
|
+
this.emitSessionEvent(SESSION_START_EVENT, newId, reason);
|
|
419
|
+
return newId;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* End the current analytics session (if any), emitting `$session_end`,
|
|
423
|
+
* and clear the stored session. The next tracked event will lazily
|
|
424
|
+
* start a fresh session. Called automatically on logout via
|
|
425
|
+
* {@link reset}; also exposed for explicit control.
|
|
426
|
+
*
|
|
427
|
+
* @param immediate - flush buffered events synchronously (via
|
|
428
|
+
* `sendBeacon`) so the end event isn't lost if the page unloads.
|
|
429
|
+
*/
|
|
430
|
+
endSession(reason = "logout", immediate = false) {
|
|
431
|
+
if (typeof window === "undefined") return;
|
|
432
|
+
const existingId = this.readStoredSession();
|
|
433
|
+
if (existingId) {
|
|
434
|
+
this.emitSessionEvent(SESSION_END_EVENT, existingId, reason);
|
|
435
|
+
}
|
|
436
|
+
this.sessionId = void 0;
|
|
437
|
+
this.clearSession();
|
|
438
|
+
if (immediate) {
|
|
439
|
+
if (typeof navigator === "undefined" || !navigator.sendBeacon) {
|
|
440
|
+
this.flush().catch(() => {
|
|
441
|
+
});
|
|
442
|
+
} else {
|
|
443
|
+
this.flushSync();
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
readStoredUserId() {
|
|
448
|
+
if (typeof localStorage === "undefined") return void 0;
|
|
449
|
+
try {
|
|
450
|
+
return localStorage.getItem(USER_ID_KEY) ?? void 0;
|
|
451
|
+
} catch {
|
|
452
|
+
return void 0;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
writeStoredUserId(userId) {
|
|
456
|
+
if (typeof localStorage === "undefined") return;
|
|
457
|
+
try {
|
|
458
|
+
localStorage.setItem(USER_ID_KEY, userId);
|
|
459
|
+
} catch {
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
clearStoredUserId() {
|
|
463
|
+
if (typeof localStorage === "undefined") return;
|
|
464
|
+
try {
|
|
465
|
+
localStorage.removeItem(USER_ID_KEY);
|
|
466
|
+
} catch {
|
|
467
|
+
}
|
|
468
|
+
}
|
|
393
469
|
clearTicketSession() {
|
|
394
470
|
this.ticketSessionFetch = null;
|
|
395
471
|
if (typeof localStorage === "undefined") return;
|
|
@@ -454,25 +530,96 @@ var TrackerImpl = class {
|
|
|
454
530
|
})();
|
|
455
531
|
return this.ticketSessionFetch;
|
|
456
532
|
}
|
|
457
|
-
|
|
458
|
-
|
|
533
|
+
/**
|
|
534
|
+
* Return the id of the current session, rotating it when the previous
|
|
535
|
+
* session has expired through inactivity. On rotation it emits a
|
|
536
|
+
* `$session_end` for the timed-out session (backdated to the last
|
|
537
|
+
* recorded activity) and a `$session_start` for the new one. Called on
|
|
538
|
+
* every tracked event so sessions advance with real user activity.
|
|
539
|
+
*/
|
|
540
|
+
ensureSession(now) {
|
|
541
|
+
if (typeof window === "undefined") return this.sessionId;
|
|
459
542
|
try {
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
const
|
|
463
|
-
const expired = !lastActivity || now - Number(lastActivity) > this.sessionTimeout;
|
|
543
|
+
const lastActivity = this.readLastActivity();
|
|
544
|
+
const existingId = this.readStoredSession();
|
|
545
|
+
const expired = lastActivity === void 0 || now - lastActivity > this.sessionTimeout;
|
|
464
546
|
if (existingId && !expired) {
|
|
465
|
-
|
|
547
|
+
this.sessionId = existingId;
|
|
548
|
+
try {
|
|
549
|
+
localStorage.setItem(LAST_ACTIVITY_KEY, String(now));
|
|
550
|
+
} catch {
|
|
551
|
+
}
|
|
466
552
|
return existingId;
|
|
467
553
|
}
|
|
554
|
+
if (existingId) {
|
|
555
|
+
this.emitSessionEvent(
|
|
556
|
+
SESSION_END_EVENT,
|
|
557
|
+
existingId,
|
|
558
|
+
"timeout",
|
|
559
|
+
lastActivity !== void 0 ? new Date(lastActivity).toISOString() : void 0
|
|
560
|
+
);
|
|
561
|
+
}
|
|
468
562
|
const newId = generateAnonId();
|
|
469
|
-
|
|
470
|
-
|
|
563
|
+
this.persistSession(newId, now);
|
|
564
|
+
this.emitSessionEvent(
|
|
565
|
+
SESSION_START_EVENT,
|
|
566
|
+
newId,
|
|
567
|
+
existingId ? "timeout" : "initial"
|
|
568
|
+
);
|
|
471
569
|
return newId;
|
|
472
570
|
} catch {
|
|
473
|
-
return generateAnonId();
|
|
571
|
+
return this.sessionId ?? generateAnonId();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
readStoredSession() {
|
|
575
|
+
if (this.sessionId) return this.sessionId;
|
|
576
|
+
if (typeof sessionStorage === "undefined") return void 0;
|
|
577
|
+
try {
|
|
578
|
+
return sessionStorage.getItem(SESSION_ID_KEY) ?? void 0;
|
|
579
|
+
} catch {
|
|
580
|
+
return void 0;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
readLastActivity() {
|
|
584
|
+
if (typeof localStorage === "undefined") return void 0;
|
|
585
|
+
try {
|
|
586
|
+
const raw = localStorage.getItem(LAST_ACTIVITY_KEY);
|
|
587
|
+
return raw ? Number(raw) : void 0;
|
|
588
|
+
} catch {
|
|
589
|
+
return void 0;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
persistSession(id, now) {
|
|
593
|
+
this.sessionId = id;
|
|
594
|
+
if (typeof window === "undefined") return;
|
|
595
|
+
try {
|
|
596
|
+
sessionStorage.setItem(SESSION_ID_KEY, id);
|
|
597
|
+
localStorage.setItem(LAST_ACTIVITY_KEY, String(now));
|
|
598
|
+
} catch {
|
|
474
599
|
}
|
|
475
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* Enqueue a session lifecycle event already stamped with its session id
|
|
603
|
+
* (so it never re-enters {@link ensureSession} and risks recursion).
|
|
604
|
+
*/
|
|
605
|
+
emitSessionEvent(event, sessionId, reason, occurredAt) {
|
|
606
|
+
if (!this.writeKey) return;
|
|
607
|
+
const traitsContext = Object.keys(this.traits).length > 0 ? { traits: this.traits } : {};
|
|
608
|
+
this.buffer.push({
|
|
609
|
+
event,
|
|
610
|
+
properties: { reason },
|
|
611
|
+
occurred_at: occurredAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
612
|
+
source: "browser",
|
|
613
|
+
org_id: this.orgId,
|
|
614
|
+
user_id: this.userId,
|
|
615
|
+
anonymous_id: this.anonymousId,
|
|
616
|
+
session_id: sessionId,
|
|
617
|
+
context: {
|
|
618
|
+
...getBrowserContext(),
|
|
619
|
+
...traitsContext
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
476
623
|
clearSession() {
|
|
477
624
|
if (typeof window === "undefined") return;
|
|
478
625
|
try {
|
|
@@ -482,7 +629,7 @@ var TrackerImpl = class {
|
|
|
482
629
|
}
|
|
483
630
|
}
|
|
484
631
|
getSessionId() {
|
|
485
|
-
return this.
|
|
632
|
+
return this.ensureSession(Date.now());
|
|
486
633
|
}
|
|
487
634
|
/** @internal Used by the widget to read tracker config. */
|
|
488
635
|
_getEndpoint() {
|
|
@@ -536,7 +683,7 @@ var TrackerImpl = class {
|
|
|
536
683
|
org_id: evt.org_id ?? this.orgId,
|
|
537
684
|
user_id: evt.user_id ?? this.userId,
|
|
538
685
|
anonymous_id: evt.anonymous_id ?? this.anonymousId,
|
|
539
|
-
session_id: evt.session_id ?? this.
|
|
686
|
+
session_id: evt.session_id ?? this.ensureSession(Date.now()),
|
|
540
687
|
context: {
|
|
541
688
|
...getBrowserContext(),
|
|
542
689
|
...traitsContext,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/breadcrumbs.ts","../src/context.ts","../src/errors.ts","../src/flag-client.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["export interface Breadcrumb {\n type: string;\n category: string;\n message: string;\n timestamp: number;\n data?: Record<string, unknown>;\n}\n\nconst MAX_BREADCRUMBS = 20;\nconst MAX_TEXT_LENGTH = 120;\n\nlet buffer: Breadcrumb[] = [];\nlet installed = false;\n\nfunction push(crumb: Breadcrumb): void {\n buffer.push(crumb);\n if (buffer.length > MAX_BREADCRUMBS) {\n buffer = buffer.slice(-MAX_BREADCRUMBS);\n }\n}\n\nfunction truncate(str: string, max = MAX_TEXT_LENGTH): string {\n return str.length > max ? str.slice(0, max) + \"…\" : str;\n}\n\nexport function getBreadcrumbs(): Breadcrumb[] {\n return [...buffer];\n}\n\nexport function addBreadcrumb(crumb: Breadcrumb): void {\n push(crumb);\n}\n\nexport function startBreadcrumbCollection(): void {\n if (installed || typeof window === \"undefined\") return;\n installed = true;\n\n document.addEventListener(\n \"click\",\n (e) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n const text =\n target.textContent?.trim() || target.getAttribute(\"aria-label\") || \"\";\n push({\n type: \"user\",\n category: \"click\",\n message: truncate(\n `${target.tagName.toLowerCase()}${text ? ` \"${text}\"` : \"\"}`,\n ),\n timestamp: Date.now(),\n data: {\n tagName: target.tagName.toLowerCase(),\n className: target.className\n ? truncate(target.className, 80)\n : undefined,\n },\n });\n },\n { capture: true },\n );\n\n const origPushState = history.pushState;\n history.pushState = function (...args) {\n const from = location.pathname;\n origPushState.apply(this, args);\n push({\n type: \"navigation\",\n category: \"navigation\",\n message: `${from} → ${location.pathname}`,\n timestamp: Date.now(),\n });\n };\n\n window.addEventListener(\"popstate\", () => {\n push({\n type: \"navigation\",\n category: \"navigation\",\n message: `popstate → ${location.pathname}`,\n timestamp: Date.now(),\n });\n });\n\n const wrapConsole = (level: \"error\" | \"warn\") => {\n const orig = console[level];\n console[level] = (...args: unknown[]) => {\n push({\n type: \"console\",\n category: `console.${level}`,\n message: truncate(\n args.map((a) => (typeof a === \"string\" ? a : String(a))).join(\" \"),\n ),\n timestamp: Date.now(),\n });\n orig.apply(console, args);\n };\n };\n\n wrapConsole(\"error\");\n wrapConsole(\"warn\");\n}\n","/**\n * Auto-capture browser context: page, referrer, screen, locale, user agent.\n */\nexport function getBrowserContext(): Record<string, unknown> {\n if (typeof window === \"undefined\") {\n return {};\n }\n\n return {\n page: {\n url: window.location.href,\n path: window.location.pathname,\n title: document.title,\n referrer: document.referrer || undefined,\n },\n screen: {\n width: window.screen.width,\n height: window.screen.height,\n },\n locale: navigator.language,\n userAgent: navigator.userAgent,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n };\n}\n","interface TrackerLike {\n track(event: string, properties?: Record<string, unknown>): void;\n}\n\ninterface ErrorProperties {\n type: string;\n message: string;\n stack: string | undefined;\n handled: boolean;\n mechanism: string;\n source: \"browser\";\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\ntype BreadcrumbGetter = () => Array<{\n type: string;\n category: string;\n message: string;\n timestamp: number;\n data?: Record<string, unknown>;\n}>;\n\nconst DEDUP_COOLDOWN_MS = 60_000;\nconst dedupMap = new Map<string, number>();\n\nfunction simpleFingerprint(type: string, stack?: string): string {\n const firstFrame = stack?.split(\"\\n\")[1]?.trim() ?? \"\";\n return `${type}:${firstFrame}`;\n}\n\nfunction isDuplicate(fingerprint: string): boolean {\n const lastSent = dedupMap.get(fingerprint);\n if (lastSent && Date.now() - lastSent < DEDUP_COOLDOWN_MS) return true;\n dedupMap.set(fingerprint, Date.now());\n return false;\n}\n\nfunction sendException(\n tracker: TrackerLike,\n props: ErrorProperties,\n getBreadcrumbs: BreadcrumbGetter,\n): void {\n const fp = simpleFingerprint(props.type, props.stack);\n if (isDuplicate(fp)) return;\n\n tracker.track(\"$exception\", {\n ...props,\n context: { breadcrumbs: getBreadcrumbs() as unknown[] },\n });\n}\n\nexport function installGlobalHandlers(\n tracker: TrackerLike,\n getBreadcrumbs: BreadcrumbGetter,\n): void {\n if (typeof window === \"undefined\") return;\n\n window.onerror = (\n _message: string | Event,\n filename?: string,\n lineno?: number,\n colno?: number,\n error?: Error,\n ) => {\n const err = error ?? new Error(String(_message));\n sendException(\n tracker,\n {\n type: err.name || \"Error\",\n message: err.message || String(_message),\n stack: err.stack,\n handled: false,\n mechanism: \"onerror\",\n source: \"browser\",\n filename,\n lineno,\n colno,\n },\n getBreadcrumbs,\n );\n };\n\n window.onunhandledrejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n const err = reason instanceof Error ? reason : new Error(String(reason));\n sendException(\n tracker,\n {\n type: err.name || \"UnhandledRejection\",\n message: err.message,\n stack: err.stack,\n handled: false,\n mechanism: \"onunhandledrejection\",\n source: \"browser\",\n },\n getBreadcrumbs,\n );\n };\n}\n","import type { EvaluatedFlag } from \"./types.js\";\n\nconst DEFAULT_FLAG_POLL_INTERVAL = 60_000;\n\nexport interface FlagClientConfig {\n endpoint: string;\n writeKey: string;\n timeout?: number;\n environment?: string;\n pollInterval?: number;\n getIdentity: () => {\n userId?: string;\n anonymousId?: string;\n orgId?: string;\n };\n /** Called when a flag is first evaluated (for impression tracking). */\n onEvaluation?: (key: string, flag: EvaluatedFlag) => void;\n}\n\nexport class FlagClient {\n private endpoint: string;\n private writeKey: string;\n private timeout: number;\n private environment: string | undefined;\n private getIdentity: FlagClientConfig[\"getIdentity\"];\n private onEvaluation: FlagClientConfig[\"onEvaluation\"];\n\n private cache = new Map<string, EvaluatedFlag>();\n private ready = false;\n private readyCallbacks: Array<() => void> = [];\n private changeListeners = new Set<() => void>();\n private evalTracked = new Set<string>();\n private timer: ReturnType<typeof setInterval> | null = null;\n\n constructor(config: FlagClientConfig) {\n this.endpoint = config.endpoint;\n this.writeKey = config.writeKey;\n this.timeout = config.timeout ?? 10_000;\n this.environment = config.environment;\n this.getIdentity = config.getIdentity;\n this.onEvaluation = config.onEvaluation;\n\n const interval = config.pollInterval ?? DEFAULT_FLAG_POLL_INTERVAL;\n this.fetch();\n this.timer = setInterval(() => this.fetch(), interval);\n }\n\n getFlag<T = unknown>(key: string, defaultValue: T): T {\n const flag = this.cache.get(key);\n if (!flag) return defaultValue;\n\n if (!this.evalTracked.has(key)) {\n this.evalTracked.add(key);\n this.onEvaluation?.(key, flag);\n }\n\n return flag.value as T;\n }\n\n onFlagsReady(callback: () => void): void {\n if (this.ready) {\n callback();\n } else {\n this.readyCallbacks.push(callback);\n }\n }\n\n onFlagChange(listener: () => void): () => void {\n this.changeListeners.add(listener);\n return () => this.changeListeners.delete(listener);\n }\n\n async refresh(): Promise<void> {\n await this.fetch();\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private async fetch(): Promise<void> {\n if (!this.writeKey || !this.endpoint) return;\n\n const params = new URLSearchParams();\n if (this.environment) params.set(\"environment\", this.environment);\n const identity = this.getIdentity();\n const identifier = identity.userId ?? identity.anonymousId;\n if (identifier) params.set(\"identifier\", identifier);\n if (identity.orgId) params.set(\"orgId\", identity.orgId);\n\n const qs = params.toString();\n const url = `${this.endpoint}/api/flags${qs ? `?${qs}` : \"\"}`;\n\n try {\n const res = await globalThis.fetch(url, {\n headers: { Authorization: `Bearer ${this.writeKey}` },\n signal: AbortSignal.timeout(this.timeout),\n });\n if (!res.ok) return;\n\n const json = (await res.json()) as {\n flags: Record<string, EvaluatedFlag>;\n };\n\n this.cache.clear();\n for (const [key, flag] of Object.entries(json.flags)) {\n this.cache.set(key, flag);\n }\n\n if (!this.ready) {\n this.ready = true;\n for (const cb of this.readyCallbacks) {\n try {\n cb();\n } catch {\n // ignore callback errors\n }\n }\n this.readyCallbacks = [];\n }\n\n for (const listener of this.changeListeners) {\n try {\n listener();\n } catch {\n // ignore listener errors\n }\n }\n } catch {\n // Silently fail — flags use cached values or defaults\n }\n }\n}\n","import {\n addBreadcrumb,\n getBreadcrumbs,\n startBreadcrumbCollection,\n} from \"./breadcrumbs.js\";\nimport { getBrowserContext } from \"./context.js\";\nimport { installGlobalHandlers } from \"./errors.js\";\nimport { FlagClient } from \"./flag-client.js\";\nimport type {\n FlushResult,\n IdentifyOptions,\n IdentifyTraits,\n InitOptions,\n TrackEvent,\n} from \"./types.js\";\n\nconst DEFAULT_FLUSH_AT = 10;\nconst DEFAULT_FLUSH_INTERVAL = 3_000;\nconst DEFAULT_TIMEOUT = 10_000;\nconst DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1_000; // 30 minutes\nconst MAX_BATCH_SIZE = 100;\nconst ANON_ID_KEY = \"__scoperat_anon_id\";\nconst SESSION_ID_KEY = \"__scoperat_session_id\";\nconst LAST_ACTIVITY_KEY = \"__scoperat_last_activity\";\n/**\n * Storage key for the anonymous support-ticket session token minted\n * by the server via `POST /ticket/session`. Stored as\n * `{ anonymousId, token, expiresAt }` JSON. The widget replays this\n * token on every ticket call so the server never has to trust a\n * client-supplied anonymous id (see `lib/ticket-session.ts` in the\n * API).\n */\nconst TICKET_SESSION_KEY = \"__scoperat_ticket_session\";\n\ninterface StoredTicketSession {\n anonymousId: string;\n token: string;\n expiresAt: number;\n}\n\nfunction generateAnonId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getOrCreateAnonId(): string | undefined {\n if (typeof window === \"undefined\" || typeof localStorage === \"undefined\") {\n return undefined;\n }\n\n try {\n let id = localStorage.getItem(ANON_ID_KEY);\n if (!id) {\n id = generateAnonId();\n localStorage.setItem(ANON_ID_KEY, id);\n }\n return id;\n } catch {\n return generateAnonId();\n }\n}\n\nclass TrackerImpl {\n private writeKey: string | null = null;\n private endpoint: string = \"\";\n private flushAt: number = DEFAULT_FLUSH_AT;\n private flushInterval: number = DEFAULT_FLUSH_INTERVAL;\n private timeout: number = DEFAULT_TIMEOUT;\n private onError?: (error: Error, events: TrackEvent[]) => void;\n\n private buffer: TrackEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private visibilityHandler: (() => void) | null = null;\n private userId: string | undefined;\n private orgId: string | undefined;\n private traits: IdentifyTraits = {};\n private userHash: string | undefined;\n private anonymousId: string | undefined;\n private sessionTimeout: number = DEFAULT_SESSION_TIMEOUT;\n private ticketSessionFetch: Promise<StoredTicketSession | null> | null = null;\n\n private _flagClient: FlagClient | null = null;\n\n init(writeKey: string, options: InitOptions = {}): void {\n this.writeKey = writeKey;\n this.endpoint = (\n options.endpoint ??\n globalThis.location?.origin ??\n \"\"\n ).replace(/\\/+$/, \"\");\n this.flushAt = options.flushAt ?? DEFAULT_FLUSH_AT;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.onError = options.onError;\n this.sessionTimeout = options.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n\n this.anonymousId = getOrCreateAnonId();\n\n if (this.timer) clearInterval(this.timer);\n this.timer = setInterval(() => {\n if (this.buffer.length > 0) {\n this.flush().catch(() => {});\n }\n }, this.flushInterval);\n\n if (typeof window !== \"undefined\") {\n if (this.visibilityHandler) {\n window.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n this.visibilityHandler = () => {\n if (document.visibilityState === \"hidden\" && this.buffer.length > 0) {\n this.flushSync();\n }\n };\n window.addEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n\n if (options.captureErrors) {\n installGlobalHandlers(this, getBreadcrumbs);\n startBreadcrumbCollection();\n }\n\n this._flagClient?.destroy();\n this._flagClient = new FlagClient({\n endpoint: this.endpoint,\n writeKey,\n timeout: this.timeout,\n environment: options.environment,\n pollInterval: options.flagPollInterval,\n getIdentity: () => ({\n userId: this.userId,\n anonymousId: this.anonymousId,\n orgId: this.orgId,\n }),\n onEvaluation: (key, flag) =>\n this.track(\"$flag_evaluation\", {\n flag_key: key,\n flag_value: flag.value,\n flag_type: flag.type,\n flag_source: flag.source,\n }),\n });\n }\n\n /** The underlying flag evaluation client. Use for direct SDK integration. */\n get flags(): FlagClient {\n if (!this._flagClient) {\n throw new Error(\"Tracker not initialized. Call init() first.\");\n }\n return this._flagClient;\n }\n\n identify(\n userId: string,\n traits?: IdentifyTraits,\n options?: IdentifyOptions,\n ): void {\n this.userId = userId;\n if (traits) {\n this.traits = { ...traits };\n if (traits.orgId) this.orgId = traits.orgId;\n }\n // Switching from one identified user to another invalidates the\n // prior userHash — callers must either pass a new one or accept\n // that ticket calls degrade to the anonymous session channel.\n this.userHash = options?.userHash;\n\n for (const evt of this.buffer) {\n if (!evt.user_id) evt.user_id = userId;\n if (!evt.org_id && this.orgId) evt.org_id = this.orgId;\n }\n\n this._flagClient?.refresh();\n }\n\n reset(): void {\n this.userId = undefined;\n this.orgId = undefined;\n this.traits = {};\n this.userHash = undefined;\n this.anonymousId = getOrCreateAnonId();\n this.clearSession();\n this.clearTicketSession();\n }\n\n private clearTicketSession(): void {\n this.ticketSessionFetch = null;\n if (typeof localStorage === \"undefined\") return;\n try {\n localStorage.removeItem(TICKET_SESSION_KEY);\n } catch {\n // ignore storage errors\n }\n }\n\n private readStoredTicketSession(): StoredTicketSession | null {\n if (typeof localStorage === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(TICKET_SESSION_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Partial<StoredTicketSession>;\n if (\n typeof parsed.anonymousId === \"string\" &&\n typeof parsed.token === \"string\" &&\n typeof parsed.expiresAt === \"number\" &&\n parsed.expiresAt > Date.now() + 60_000\n ) {\n return parsed as StoredTicketSession;\n }\n localStorage.removeItem(TICKET_SESSION_KEY);\n } catch {\n // fall through to network fetch\n }\n return null;\n }\n\n /**\n * @internal Used by the widget API helpers. Returns a cached or\n * newly-minted anonymous session token, or `null` if the server\n * has anonymous sessions disabled. Coalesces concurrent callers\n * onto a single in-flight network request so mount-time parallel\n * API calls don't each trigger a `POST /ticket/session`.\n */\n _ensureTicketSession(): Promise<StoredTicketSession | null> {\n const cached = this.readStoredTicketSession();\n if (cached) return Promise.resolve(cached);\n\n if (this.ticketSessionFetch) return this.ticketSessionFetch;\n if (!this.writeKey) return Promise.resolve(null);\n\n this.ticketSessionFetch = (async () => {\n try {\n const res = await fetch(`${this.endpoint}/api/ingest/ticket/session`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.writeKey}`,\n },\n body: \"{}\",\n signal: AbortSignal.timeout(this.timeout),\n });\n if (!res.ok) return null;\n const json = (await res.json()) as StoredTicketSession;\n if (\n typeof json?.anonymousId === \"string\" &&\n typeof json?.token === \"string\" &&\n typeof json?.expiresAt === \"number\"\n ) {\n try {\n localStorage?.setItem(TICKET_SESSION_KEY, JSON.stringify(json));\n } catch {\n // ignore storage errors\n }\n // Keep our in-memory anonymousId in sync with the\n // server-issued one so any code that reads `_getIdentity`\n // outside the ticket flow (e.g. analytics) sees a\n // consistent value.\n this.anonymousId = json.anonymousId;\n return json;\n }\n return null;\n } catch {\n return null;\n } finally {\n this.ticketSessionFetch = null;\n }\n })();\n\n return this.ticketSessionFetch;\n }\n\n private getOrCreateSessionId(): string | undefined {\n if (typeof window === \"undefined\") return undefined;\n\n try {\n const now = Date.now();\n const lastActivity = localStorage.getItem(LAST_ACTIVITY_KEY);\n const existingId = sessionStorage.getItem(SESSION_ID_KEY);\n\n const expired =\n !lastActivity || now - Number(lastActivity) > this.sessionTimeout;\n\n if (existingId && !expired) {\n localStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n return existingId;\n }\n\n const newId = generateAnonId();\n sessionStorage.setItem(SESSION_ID_KEY, newId);\n localStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n return newId;\n } catch {\n return generateAnonId();\n }\n }\n\n private clearSession(): void {\n if (typeof window === \"undefined\") return;\n try {\n sessionStorage.removeItem(SESSION_ID_KEY);\n localStorage.removeItem(LAST_ACTIVITY_KEY);\n } catch {\n // Ignore storage errors\n }\n }\n\n getSessionId(): string | undefined {\n return this.getOrCreateSessionId();\n }\n\n /** @internal Used by the widget to read tracker config. */\n _getEndpoint(): string {\n return this.endpoint;\n }\n\n /** @internal Used by the widget to read the write key. */\n _getWriteKey(): string | null {\n return this.writeKey;\n }\n\n /** @internal Used by the widget to read the current user identity. */\n _getIdentity(): {\n userId?: string;\n anonymousId?: string;\n email?: string;\n name?: string;\n userHash?: string;\n } {\n return {\n userId: this.userId,\n anonymousId: this.anonymousId,\n email: this.traits.email as string | undefined,\n name: this.traits.name as string | undefined,\n userHash: this.userHash,\n };\n }\n\n captureException(error: Error, context?: Record<string, unknown>): void {\n if (!this.writeKey) return;\n\n const breadcrumbs = getBreadcrumbs();\n this.track(\"$exception\", {\n type: error.name || \"Error\",\n message: error.message,\n stack: error.stack,\n handled: true,\n mechanism: (context?.mechanism as string) ?? \"manual\",\n source: \"browser\",\n ...context,\n context: { breadcrumbs: breadcrumbs as unknown[] },\n });\n }\n\n addBreadcrumb(\n category: string,\n message: string,\n data?: Record<string, unknown>,\n ): void {\n addBreadcrumb({\n type: \"custom\",\n category,\n message,\n timestamp: Date.now(),\n data,\n });\n }\n\n track(event: string, properties?: Record<string, unknown>): void;\n track(event: TrackEvent): void;\n track(\n eventOrName: string | TrackEvent,\n properties?: Record<string, unknown>,\n ): void {\n if (!this.writeKey) return;\n\n const evt: TrackEvent =\n typeof eventOrName === \"string\"\n ? { event: eventOrName, properties }\n : eventOrName;\n\n const traitsContext =\n Object.keys(this.traits).length > 0 ? { traits: this.traits } : {};\n\n const enriched: TrackEvent = {\n ...evt,\n occurred_at: evt.occurred_at ?? new Date().toISOString(),\n source: evt.source ?? \"browser\",\n org_id: evt.org_id ?? this.orgId,\n user_id: evt.user_id ?? this.userId,\n anonymous_id: evt.anonymous_id ?? this.anonymousId,\n session_id: evt.session_id ?? this.getOrCreateSessionId(),\n context: {\n ...getBrowserContext(),\n ...traitsContext,\n ...evt.context,\n },\n };\n\n this.buffer.push(enriched);\n\n if (this.buffer.length >= this.flushAt) {\n this.flush().catch(() => {});\n }\n }\n\n async flush(): Promise<FlushResult> {\n if (!this.writeKey || this.buffer.length === 0) {\n return { success: true, ingested: 0 };\n }\n\n const batch = this.buffer.splice(0, MAX_BATCH_SIZE);\n\n try {\n const res = await fetch(`${this.endpoint}/api/ingest/track`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.writeKey}`,\n },\n body: JSON.stringify({ events: batch }),\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`API returned ${res.status}: ${body}`);\n }\n\n const json = (await res.json()) as { ingested: number };\n return { success: true, ingested: json.ingested };\n } catch (error) {\n this.buffer.unshift(...batch);\n const err = error instanceof Error ? error : new Error(String(error));\n this.onError?.(err, batch);\n return { success: false, ingested: 0 };\n }\n }\n\n private flushSync(): void {\n if (!this.writeKey) return;\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) return;\n if (this.buffer.length === 0) return;\n\n const batch = this.buffer.splice(0, MAX_BATCH_SIZE);\n const payload = JSON.stringify({ events: batch });\n const url = `${this.endpoint}/api/ingest/track?key=${encodeURIComponent(this.writeKey)}`;\n\n try {\n const blob = new Blob([payload], { type: \"application/json\" });\n navigator.sendBeacon(url, blob);\n } catch {\n // Best effort on unload\n }\n }\n\n getFlag<T = unknown>(key: string, defaultValue: T): T {\n return this._flagClient?.getFlag(key, defaultValue) ?? defaultValue;\n }\n\n onFlagsReady(callback: () => void): void {\n this._flagClient?.onFlagsReady(callback);\n }\n\n onFlagChange(listener: () => void): () => void {\n return this._flagClient?.onFlagChange(listener) ?? (() => {});\n }\n\n async refreshFlags(): Promise<void> {\n await this._flagClient?.refresh();\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n this._flagClient?.destroy();\n this._flagClient = null;\n\n if (typeof window !== \"undefined\" && this.visibilityHandler) {\n window.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n this.visibilityHandler = null;\n }\n\n while (this.buffer.length > 0) {\n await this.flush();\n }\n }\n}\n\nconst scoperat = new TrackerImpl();\nexport default scoperat;\n","import scoperat from \"./client.js\";\n\nexport default scoperat;\nexport { FlagClient } from \"./flag-client.js\";\nexport type {\n EvaluatedFlag,\n EventSource,\n FlushResult,\n IdentifyOptions,\n IdentifyTraits,\n InitOptions,\n TrackEvent,\n} from \"./types.js\";\n"],"mappings":"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","names":["getBreadcrumbs"]}
|
|
1
|
+
{"version":3,"sources":["../src/breadcrumbs.ts","../src/context.ts","../src/errors.ts","../src/flag-client.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["export interface Breadcrumb {\n type: string;\n category: string;\n message: string;\n timestamp: number;\n data?: Record<string, unknown>;\n}\n\nconst MAX_BREADCRUMBS = 20;\nconst MAX_TEXT_LENGTH = 120;\n\nlet buffer: Breadcrumb[] = [];\nlet installed = false;\n\nfunction push(crumb: Breadcrumb): void {\n buffer.push(crumb);\n if (buffer.length > MAX_BREADCRUMBS) {\n buffer = buffer.slice(-MAX_BREADCRUMBS);\n }\n}\n\nfunction truncate(str: string, max = MAX_TEXT_LENGTH): string {\n return str.length > max ? str.slice(0, max) + \"…\" : str;\n}\n\nexport function getBreadcrumbs(): Breadcrumb[] {\n return [...buffer];\n}\n\nexport function addBreadcrumb(crumb: Breadcrumb): void {\n push(crumb);\n}\n\nexport function startBreadcrumbCollection(): void {\n if (installed || typeof window === \"undefined\") return;\n installed = true;\n\n document.addEventListener(\n \"click\",\n (e) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n const text =\n target.textContent?.trim() || target.getAttribute(\"aria-label\") || \"\";\n push({\n type: \"user\",\n category: \"click\",\n message: truncate(\n `${target.tagName.toLowerCase()}${text ? ` \"${text}\"` : \"\"}`,\n ),\n timestamp: Date.now(),\n data: {\n tagName: target.tagName.toLowerCase(),\n className: target.className\n ? truncate(target.className, 80)\n : undefined,\n },\n });\n },\n { capture: true },\n );\n\n const origPushState = history.pushState;\n history.pushState = function (...args) {\n const from = location.pathname;\n origPushState.apply(this, args);\n push({\n type: \"navigation\",\n category: \"navigation\",\n message: `${from} → ${location.pathname}`,\n timestamp: Date.now(),\n });\n };\n\n window.addEventListener(\"popstate\", () => {\n push({\n type: \"navigation\",\n category: \"navigation\",\n message: `popstate → ${location.pathname}`,\n timestamp: Date.now(),\n });\n });\n\n const wrapConsole = (level: \"error\" | \"warn\") => {\n const orig = console[level];\n console[level] = (...args: unknown[]) => {\n push({\n type: \"console\",\n category: `console.${level}`,\n message: truncate(\n args.map((a) => (typeof a === \"string\" ? a : String(a))).join(\" \"),\n ),\n timestamp: Date.now(),\n });\n orig.apply(console, args);\n };\n };\n\n wrapConsole(\"error\");\n wrapConsole(\"warn\");\n}\n","/**\n * Auto-capture browser context: page, referrer, screen, locale, user agent.\n */\nexport function getBrowserContext(): Record<string, unknown> {\n if (typeof window === \"undefined\") {\n return {};\n }\n\n return {\n page: {\n url: window.location.href,\n path: window.location.pathname,\n title: document.title,\n referrer: document.referrer || undefined,\n },\n screen: {\n width: window.screen.width,\n height: window.screen.height,\n },\n locale: navigator.language,\n userAgent: navigator.userAgent,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n };\n}\n","interface TrackerLike {\n track(event: string, properties?: Record<string, unknown>): void;\n}\n\ninterface ErrorProperties {\n type: string;\n message: string;\n stack: string | undefined;\n handled: boolean;\n mechanism: string;\n source: \"browser\";\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\ntype BreadcrumbGetter = () => Array<{\n type: string;\n category: string;\n message: string;\n timestamp: number;\n data?: Record<string, unknown>;\n}>;\n\nconst DEDUP_COOLDOWN_MS = 60_000;\nconst dedupMap = new Map<string, number>();\n\nfunction simpleFingerprint(type: string, stack?: string): string {\n const firstFrame = stack?.split(\"\\n\")[1]?.trim() ?? \"\";\n return `${type}:${firstFrame}`;\n}\n\nfunction isDuplicate(fingerprint: string): boolean {\n const lastSent = dedupMap.get(fingerprint);\n if (lastSent && Date.now() - lastSent < DEDUP_COOLDOWN_MS) return true;\n dedupMap.set(fingerprint, Date.now());\n return false;\n}\n\nfunction sendException(\n tracker: TrackerLike,\n props: ErrorProperties,\n getBreadcrumbs: BreadcrumbGetter,\n): void {\n const fp = simpleFingerprint(props.type, props.stack);\n if (isDuplicate(fp)) return;\n\n tracker.track(\"$exception\", {\n ...props,\n context: { breadcrumbs: getBreadcrumbs() as unknown[] },\n });\n}\n\nexport function installGlobalHandlers(\n tracker: TrackerLike,\n getBreadcrumbs: BreadcrumbGetter,\n): void {\n if (typeof window === \"undefined\") return;\n\n window.onerror = (\n _message: string | Event,\n filename?: string,\n lineno?: number,\n colno?: number,\n error?: Error,\n ) => {\n const err = error ?? new Error(String(_message));\n sendException(\n tracker,\n {\n type: err.name || \"Error\",\n message: err.message || String(_message),\n stack: err.stack,\n handled: false,\n mechanism: \"onerror\",\n source: \"browser\",\n filename,\n lineno,\n colno,\n },\n getBreadcrumbs,\n );\n };\n\n window.onunhandledrejection = (event: PromiseRejectionEvent) => {\n const reason = event.reason;\n const err = reason instanceof Error ? reason : new Error(String(reason));\n sendException(\n tracker,\n {\n type: err.name || \"UnhandledRejection\",\n message: err.message,\n stack: err.stack,\n handled: false,\n mechanism: \"onunhandledrejection\",\n source: \"browser\",\n },\n getBreadcrumbs,\n );\n };\n}\n","import type { EvaluatedFlag } from \"./types.js\";\n\nconst DEFAULT_FLAG_POLL_INTERVAL = 60_000;\n\nexport interface FlagClientConfig {\n endpoint: string;\n writeKey: string;\n timeout?: number;\n environment?: string;\n pollInterval?: number;\n getIdentity: () => {\n userId?: string;\n anonymousId?: string;\n orgId?: string;\n };\n /** Called when a flag is first evaluated (for impression tracking). */\n onEvaluation?: (key: string, flag: EvaluatedFlag) => void;\n}\n\nexport class FlagClient {\n private endpoint: string;\n private writeKey: string;\n private timeout: number;\n private environment: string | undefined;\n private getIdentity: FlagClientConfig[\"getIdentity\"];\n private onEvaluation: FlagClientConfig[\"onEvaluation\"];\n\n private cache = new Map<string, EvaluatedFlag>();\n private ready = false;\n private readyCallbacks: Array<() => void> = [];\n private changeListeners = new Set<() => void>();\n private evalTracked = new Set<string>();\n private timer: ReturnType<typeof setInterval> | null = null;\n\n constructor(config: FlagClientConfig) {\n this.endpoint = config.endpoint;\n this.writeKey = config.writeKey;\n this.timeout = config.timeout ?? 10_000;\n this.environment = config.environment;\n this.getIdentity = config.getIdentity;\n this.onEvaluation = config.onEvaluation;\n\n const interval = config.pollInterval ?? DEFAULT_FLAG_POLL_INTERVAL;\n this.fetch();\n this.timer = setInterval(() => this.fetch(), interval);\n }\n\n getFlag<T = unknown>(key: string, defaultValue: T): T {\n const flag = this.cache.get(key);\n if (!flag) return defaultValue;\n\n if (!this.evalTracked.has(key)) {\n this.evalTracked.add(key);\n this.onEvaluation?.(key, flag);\n }\n\n return flag.value as T;\n }\n\n onFlagsReady(callback: () => void): void {\n if (this.ready) {\n callback();\n } else {\n this.readyCallbacks.push(callback);\n }\n }\n\n onFlagChange(listener: () => void): () => void {\n this.changeListeners.add(listener);\n return () => this.changeListeners.delete(listener);\n }\n\n async refresh(): Promise<void> {\n await this.fetch();\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private async fetch(): Promise<void> {\n if (!this.writeKey || !this.endpoint) return;\n\n const params = new URLSearchParams();\n if (this.environment) params.set(\"environment\", this.environment);\n const identity = this.getIdentity();\n const identifier = identity.userId ?? identity.anonymousId;\n if (identifier) params.set(\"identifier\", identifier);\n if (identity.orgId) params.set(\"orgId\", identity.orgId);\n\n const qs = params.toString();\n const url = `${this.endpoint}/api/flags${qs ? `?${qs}` : \"\"}`;\n\n try {\n const res = await globalThis.fetch(url, {\n headers: { Authorization: `Bearer ${this.writeKey}` },\n signal: AbortSignal.timeout(this.timeout),\n });\n if (!res.ok) return;\n\n const json = (await res.json()) as {\n flags: Record<string, EvaluatedFlag>;\n };\n\n this.cache.clear();\n for (const [key, flag] of Object.entries(json.flags)) {\n this.cache.set(key, flag);\n }\n\n if (!this.ready) {\n this.ready = true;\n for (const cb of this.readyCallbacks) {\n try {\n cb();\n } catch {\n // ignore callback errors\n }\n }\n this.readyCallbacks = [];\n }\n\n for (const listener of this.changeListeners) {\n try {\n listener();\n } catch {\n // ignore listener errors\n }\n }\n } catch {\n // Silently fail — flags use cached values or defaults\n }\n }\n}\n","import {\n addBreadcrumb,\n getBreadcrumbs,\n startBreadcrumbCollection,\n} from \"./breadcrumbs.js\";\nimport { getBrowserContext } from \"./context.js\";\nimport { installGlobalHandlers } from \"./errors.js\";\nimport { FlagClient } from \"./flag-client.js\";\nimport type {\n FlushResult,\n IdentifyOptions,\n IdentifyTraits,\n InitOptions,\n TrackEvent,\n} from \"./types.js\";\n\nconst DEFAULT_FLUSH_AT = 10;\nconst DEFAULT_FLUSH_INTERVAL = 3_000;\nconst DEFAULT_TIMEOUT = 10_000;\nconst DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1_000; // 30 minutes\nconst MAX_BATCH_SIZE = 100;\nconst ANON_ID_KEY = \"__scoperat_anon_id\";\nconst SESSION_ID_KEY = \"__scoperat_session_id\";\nconst LAST_ACTIVITY_KEY = \"__scoperat_last_activity\";\n/**\n * Last identified user id, persisted so we can tell a genuine login /\n * account switch (→ start a fresh session) apart from a re-identify of\n * the same user across a page reload (→ keep the current session).\n */\nconst USER_ID_KEY = \"__scoperat_user_id\";\n\n/**\n * Lifecycle events emitted as a session begins and ends, mirroring the\n * `session_start` / `session_end` convention used by analytics platforms\n * like GA4 and Amplitude. A session ends on logout, on an explicit\n * `startSession`, or after `sessionTimeout` of inactivity (detected lazily\n * on the next activity, backdated to the last recorded activity).\n */\nconst SESSION_START_EVENT = \"$session_start\";\nconst SESSION_END_EVENT = \"$session_end\";\n\n/** Why a session ended / began, attached as the event's `reason` property. */\nexport type SessionReason = \"initial\" | \"login\" | \"logout\" | \"timeout\";\n/**\n * Storage key for the anonymous support-ticket session token minted\n * by the server via `POST /ticket/session`. Stored as\n * `{ anonymousId, token, expiresAt }` JSON. The widget replays this\n * token on every ticket call so the server never has to trust a\n * client-supplied anonymous id (see `lib/ticket-session.ts` in the\n * API).\n */\nconst TICKET_SESSION_KEY = \"__scoperat_ticket_session\";\n\ninterface StoredTicketSession {\n anonymousId: string;\n token: string;\n expiresAt: number;\n}\n\nfunction generateAnonId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getOrCreateAnonId(): string | undefined {\n if (typeof window === \"undefined\" || typeof localStorage === \"undefined\") {\n return undefined;\n }\n\n try {\n let id = localStorage.getItem(ANON_ID_KEY);\n if (!id) {\n id = generateAnonId();\n localStorage.setItem(ANON_ID_KEY, id);\n }\n return id;\n } catch {\n return generateAnonId();\n }\n}\n\nclass TrackerImpl {\n private writeKey: string | null = null;\n private endpoint: string = \"\";\n private flushAt: number = DEFAULT_FLUSH_AT;\n private flushInterval: number = DEFAULT_FLUSH_INTERVAL;\n private timeout: number = DEFAULT_TIMEOUT;\n private onError?: (error: Error, events: TrackEvent[]) => void;\n\n private buffer: TrackEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private visibilityHandler: (() => void) | null = null;\n private userId: string | undefined;\n private orgId: string | undefined;\n private traits: IdentifyTraits = {};\n private userHash: string | undefined;\n private anonymousId: string | undefined;\n private sessionId: string | undefined;\n private sessionTimeout: number = DEFAULT_SESSION_TIMEOUT;\n private ticketSessionFetch: Promise<StoredTicketSession | null> | null = null;\n\n private _flagClient: FlagClient | null = null;\n\n init(writeKey: string, options: InitOptions = {}): void {\n this.writeKey = writeKey;\n this.endpoint = (\n options.endpoint ??\n globalThis.location?.origin ??\n \"\"\n ).replace(/\\/+$/, \"\");\n this.flushAt = options.flushAt ?? DEFAULT_FLUSH_AT;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.onError = options.onError;\n this.sessionTimeout = options.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n\n this.anonymousId = getOrCreateAnonId();\n\n if (this.timer) clearInterval(this.timer);\n this.timer = setInterval(() => {\n if (this.buffer.length > 0) {\n this.flush().catch(() => {});\n }\n }, this.flushInterval);\n\n if (typeof window !== \"undefined\") {\n if (this.visibilityHandler) {\n window.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n this.visibilityHandler = () => {\n if (document.visibilityState === \"hidden\" && this.buffer.length > 0) {\n this.flushSync();\n }\n };\n window.addEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n\n if (options.captureErrors) {\n installGlobalHandlers(this, getBreadcrumbs);\n startBreadcrumbCollection();\n }\n\n this._flagClient?.destroy();\n this._flagClient = new FlagClient({\n endpoint: this.endpoint,\n writeKey,\n timeout: this.timeout,\n environment: options.environment,\n pollInterval: options.flagPollInterval,\n getIdentity: () => ({\n userId: this.userId,\n anonymousId: this.anonymousId,\n orgId: this.orgId,\n }),\n onEvaluation: (key, flag) =>\n this.track(\"$flag_evaluation\", {\n flag_key: key,\n flag_value: flag.value,\n flag_type: flag.type,\n flag_source: flag.source,\n }),\n });\n }\n\n /** The underlying flag evaluation client. Use for direct SDK integration. */\n get flags(): FlagClient {\n if (!this._flagClient) {\n throw new Error(\"Tracker not initialized. Call init() first.\");\n }\n return this._flagClient;\n }\n\n identify(\n userId: string,\n traits?: IdentifyTraits,\n options?: IdentifyOptions,\n ): void {\n // Compare against the last identified user (in-memory, falling back to\n // the persisted id so a fresh page load doesn't look like a new login).\n const previousUserId = this.userId ?? this.readStoredUserId();\n\n this.userId = userId;\n if (traits) {\n this.traits = { ...traits };\n if (traits.orgId) this.orgId = traits.orgId;\n }\n // Switching from one identified user to another invalidates the\n // prior userHash — callers must either pass a new one or accept\n // that ticket calls degrade to the anonymous session channel.\n this.userHash = options?.userHash;\n\n for (const evt of this.buffer) {\n if (!evt.user_id) evt.user_id = userId;\n if (!evt.org_id && this.orgId) evt.org_id = this.orgId;\n }\n\n // A genuine login or account switch starts a fresh analytics session.\n // Re-identifying the same user (e.g. after a reload) keeps the\n // existing session so reloads don't fragment a single visit.\n if (userId !== previousUserId) {\n this.writeStoredUserId(userId);\n this.startSession(\"login\");\n }\n\n this._flagClient?.refresh();\n }\n\n reset(): void {\n // End the active session (logout) and best-effort deliver it before\n // the caller tears down / navigates away.\n this.endSession(\"logout\", true);\n this.userId = undefined;\n this.orgId = undefined;\n this.traits = {};\n this.userHash = undefined;\n this.anonymousId = getOrCreateAnonId();\n this.clearStoredUserId();\n this.clearTicketSession();\n }\n\n /**\n * Begin a new analytics session, ending the current one first if it\n * exists. Emits `$session_end` (for the old session) and\n * `$session_start` (for the new). Called automatically on login via\n * {@link identify}; also exposed for explicit control.\n */\n startSession(reason: SessionReason = \"login\"): string | undefined {\n if (typeof window === \"undefined\") return this.sessionId;\n\n const now = Date.now();\n const existingId = this.readStoredSession();\n if (existingId) {\n this.emitSessionEvent(SESSION_END_EVENT, existingId, reason);\n }\n\n const newId = generateAnonId();\n this.persistSession(newId, now);\n this.emitSessionEvent(SESSION_START_EVENT, newId, reason);\n return newId;\n }\n\n /**\n * End the current analytics session (if any), emitting `$session_end`,\n * and clear the stored session. The next tracked event will lazily\n * start a fresh session. Called automatically on logout via\n * {@link reset}; also exposed for explicit control.\n *\n * @param immediate - flush buffered events synchronously (via\n * `sendBeacon`) so the end event isn't lost if the page unloads.\n */\n endSession(reason: SessionReason = \"logout\", immediate = false): void {\n if (typeof window === \"undefined\") return;\n\n const existingId = this.readStoredSession();\n if (existingId) {\n this.emitSessionEvent(SESSION_END_EVENT, existingId, reason);\n }\n\n this.sessionId = undefined;\n this.clearSession();\n\n if (immediate) {\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) {\n this.flush().catch(() => {});\n } else {\n this.flushSync();\n }\n }\n }\n\n private readStoredUserId(): string | undefined {\n if (typeof localStorage === \"undefined\") return undefined;\n try {\n return localStorage.getItem(USER_ID_KEY) ?? undefined;\n } catch {\n return undefined;\n }\n }\n\n private writeStoredUserId(userId: string): void {\n if (typeof localStorage === \"undefined\") return;\n try {\n localStorage.setItem(USER_ID_KEY, userId);\n } catch {\n // ignore storage errors\n }\n }\n\n private clearStoredUserId(): void {\n if (typeof localStorage === \"undefined\") return;\n try {\n localStorage.removeItem(USER_ID_KEY);\n } catch {\n // ignore storage errors\n }\n }\n\n private clearTicketSession(): void {\n this.ticketSessionFetch = null;\n if (typeof localStorage === \"undefined\") return;\n try {\n localStorage.removeItem(TICKET_SESSION_KEY);\n } catch {\n // ignore storage errors\n }\n }\n\n private readStoredTicketSession(): StoredTicketSession | null {\n if (typeof localStorage === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(TICKET_SESSION_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Partial<StoredTicketSession>;\n if (\n typeof parsed.anonymousId === \"string\" &&\n typeof parsed.token === \"string\" &&\n typeof parsed.expiresAt === \"number\" &&\n parsed.expiresAt > Date.now() + 60_000\n ) {\n return parsed as StoredTicketSession;\n }\n localStorage.removeItem(TICKET_SESSION_KEY);\n } catch {\n // fall through to network fetch\n }\n return null;\n }\n\n /**\n * @internal Used by the widget API helpers. Returns a cached or\n * newly-minted anonymous session token, or `null` if the server\n * has anonymous sessions disabled. Coalesces concurrent callers\n * onto a single in-flight network request so mount-time parallel\n * API calls don't each trigger a `POST /ticket/session`.\n */\n _ensureTicketSession(): Promise<StoredTicketSession | null> {\n const cached = this.readStoredTicketSession();\n if (cached) return Promise.resolve(cached);\n\n if (this.ticketSessionFetch) return this.ticketSessionFetch;\n if (!this.writeKey) return Promise.resolve(null);\n\n this.ticketSessionFetch = (async () => {\n try {\n const res = await fetch(`${this.endpoint}/api/ingest/ticket/session`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.writeKey}`,\n },\n body: \"{}\",\n signal: AbortSignal.timeout(this.timeout),\n });\n if (!res.ok) return null;\n const json = (await res.json()) as StoredTicketSession;\n if (\n typeof json?.anonymousId === \"string\" &&\n typeof json?.token === \"string\" &&\n typeof json?.expiresAt === \"number\"\n ) {\n try {\n localStorage?.setItem(TICKET_SESSION_KEY, JSON.stringify(json));\n } catch {\n // ignore storage errors\n }\n // Keep our in-memory anonymousId in sync with the\n // server-issued one so any code that reads `_getIdentity`\n // outside the ticket flow (e.g. analytics) sees a\n // consistent value.\n this.anonymousId = json.anonymousId;\n return json;\n }\n return null;\n } catch {\n return null;\n } finally {\n this.ticketSessionFetch = null;\n }\n })();\n\n return this.ticketSessionFetch;\n }\n\n /**\n * Return the id of the current session, rotating it when the previous\n * session has expired through inactivity. On rotation it emits a\n * `$session_end` for the timed-out session (backdated to the last\n * recorded activity) and a `$session_start` for the new one. Called on\n * every tracked event so sessions advance with real user activity.\n */\n private ensureSession(now: number): string | undefined {\n if (typeof window === \"undefined\") return this.sessionId;\n\n try {\n const lastActivity = this.readLastActivity();\n const existingId = this.readStoredSession();\n const expired =\n lastActivity === undefined || now - lastActivity > this.sessionTimeout;\n\n if (existingId && !expired) {\n this.sessionId = existingId;\n try {\n localStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n } catch {\n // ignore storage errors\n }\n return existingId;\n }\n\n if (existingId) {\n // Previous session lapsed — close it, backdated to the moment of\n // last activity so the session duration reflects reality rather\n // than the gap until the user returned.\n this.emitSessionEvent(\n SESSION_END_EVENT,\n existingId,\n \"timeout\",\n lastActivity !== undefined\n ? new Date(lastActivity).toISOString()\n : undefined,\n );\n }\n\n const newId = generateAnonId();\n this.persistSession(newId, now);\n this.emitSessionEvent(\n SESSION_START_EVENT,\n newId,\n existingId ? \"timeout\" : \"initial\",\n );\n return newId;\n } catch {\n return this.sessionId ?? generateAnonId();\n }\n }\n\n private readStoredSession(): string | undefined {\n if (this.sessionId) return this.sessionId;\n if (typeof sessionStorage === \"undefined\") return undefined;\n try {\n return sessionStorage.getItem(SESSION_ID_KEY) ?? undefined;\n } catch {\n return undefined;\n }\n }\n\n private readLastActivity(): number | undefined {\n if (typeof localStorage === \"undefined\") return undefined;\n try {\n const raw = localStorage.getItem(LAST_ACTIVITY_KEY);\n return raw ? Number(raw) : undefined;\n } catch {\n return undefined;\n }\n }\n\n private persistSession(id: string, now: number): void {\n this.sessionId = id;\n if (typeof window === \"undefined\") return;\n try {\n sessionStorage.setItem(SESSION_ID_KEY, id);\n localStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n } catch {\n // ignore storage errors\n }\n }\n\n /**\n * Enqueue a session lifecycle event already stamped with its session id\n * (so it never re-enters {@link ensureSession} and risks recursion).\n */\n private emitSessionEvent(\n event: string,\n sessionId: string,\n reason: SessionReason,\n occurredAt?: string,\n ): void {\n if (!this.writeKey) return;\n\n const traitsContext =\n Object.keys(this.traits).length > 0 ? { traits: this.traits } : {};\n\n this.buffer.push({\n event,\n properties: { reason },\n occurred_at: occurredAt ?? new Date().toISOString(),\n source: \"browser\",\n org_id: this.orgId,\n user_id: this.userId,\n anonymous_id: this.anonymousId,\n session_id: sessionId,\n context: {\n ...getBrowserContext(),\n ...traitsContext,\n },\n });\n }\n\n private clearSession(): void {\n if (typeof window === \"undefined\") return;\n try {\n sessionStorage.removeItem(SESSION_ID_KEY);\n localStorage.removeItem(LAST_ACTIVITY_KEY);\n } catch {\n // Ignore storage errors\n }\n }\n\n getSessionId(): string | undefined {\n return this.ensureSession(Date.now());\n }\n\n /** @internal Used by the widget to read tracker config. */\n _getEndpoint(): string {\n return this.endpoint;\n }\n\n /** @internal Used by the widget to read the write key. */\n _getWriteKey(): string | null {\n return this.writeKey;\n }\n\n /** @internal Used by the widget to read the current user identity. */\n _getIdentity(): {\n userId?: string;\n anonymousId?: string;\n email?: string;\n name?: string;\n userHash?: string;\n } {\n return {\n userId: this.userId,\n anonymousId: this.anonymousId,\n email: this.traits.email as string | undefined,\n name: this.traits.name as string | undefined,\n userHash: this.userHash,\n };\n }\n\n captureException(error: Error, context?: Record<string, unknown>): void {\n if (!this.writeKey) return;\n\n const breadcrumbs = getBreadcrumbs();\n this.track(\"$exception\", {\n type: error.name || \"Error\",\n message: error.message,\n stack: error.stack,\n handled: true,\n mechanism: (context?.mechanism as string) ?? \"manual\",\n source: \"browser\",\n ...context,\n context: { breadcrumbs: breadcrumbs as unknown[] },\n });\n }\n\n addBreadcrumb(\n category: string,\n message: string,\n data?: Record<string, unknown>,\n ): void {\n addBreadcrumb({\n type: \"custom\",\n category,\n message,\n timestamp: Date.now(),\n data,\n });\n }\n\n track(event: string, properties?: Record<string, unknown>): void;\n track(event: TrackEvent): void;\n track(\n eventOrName: string | TrackEvent,\n properties?: Record<string, unknown>,\n ): void {\n if (!this.writeKey) return;\n\n const evt: TrackEvent =\n typeof eventOrName === \"string\"\n ? { event: eventOrName, properties }\n : eventOrName;\n\n const traitsContext =\n Object.keys(this.traits).length > 0 ? { traits: this.traits } : {};\n\n const enriched: TrackEvent = {\n ...evt,\n occurred_at: evt.occurred_at ?? new Date().toISOString(),\n source: evt.source ?? \"browser\",\n org_id: evt.org_id ?? this.orgId,\n user_id: evt.user_id ?? this.userId,\n anonymous_id: evt.anonymous_id ?? this.anonymousId,\n session_id: evt.session_id ?? this.ensureSession(Date.now()),\n context: {\n ...getBrowserContext(),\n ...traitsContext,\n ...evt.context,\n },\n };\n\n this.buffer.push(enriched);\n\n if (this.buffer.length >= this.flushAt) {\n this.flush().catch(() => {});\n }\n }\n\n async flush(): Promise<FlushResult> {\n if (!this.writeKey || this.buffer.length === 0) {\n return { success: true, ingested: 0 };\n }\n\n const batch = this.buffer.splice(0, MAX_BATCH_SIZE);\n\n try {\n const res = await fetch(`${this.endpoint}/api/ingest/track`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.writeKey}`,\n },\n body: JSON.stringify({ events: batch }),\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`API returned ${res.status}: ${body}`);\n }\n\n const json = (await res.json()) as { ingested: number };\n return { success: true, ingested: json.ingested };\n } catch (error) {\n this.buffer.unshift(...batch);\n const err = error instanceof Error ? error : new Error(String(error));\n this.onError?.(err, batch);\n return { success: false, ingested: 0 };\n }\n }\n\n private flushSync(): void {\n if (!this.writeKey) return;\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) return;\n if (this.buffer.length === 0) return;\n\n const batch = this.buffer.splice(0, MAX_BATCH_SIZE);\n const payload = JSON.stringify({ events: batch });\n const url = `${this.endpoint}/api/ingest/track?key=${encodeURIComponent(this.writeKey)}`;\n\n try {\n const blob = new Blob([payload], { type: \"application/json\" });\n navigator.sendBeacon(url, blob);\n } catch {\n // Best effort on unload\n }\n }\n\n getFlag<T = unknown>(key: string, defaultValue: T): T {\n return this._flagClient?.getFlag(key, defaultValue) ?? defaultValue;\n }\n\n onFlagsReady(callback: () => void): void {\n this._flagClient?.onFlagsReady(callback);\n }\n\n onFlagChange(listener: () => void): () => void {\n return this._flagClient?.onFlagChange(listener) ?? (() => {});\n }\n\n async refreshFlags(): Promise<void> {\n await this._flagClient?.refresh();\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n this._flagClient?.destroy();\n this._flagClient = null;\n\n if (typeof window !== \"undefined\" && this.visibilityHandler) {\n window.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n this.visibilityHandler = null;\n }\n\n while (this.buffer.length > 0) {\n await this.flush();\n }\n }\n}\n\nconst scoperat = new TrackerImpl();\nexport default scoperat;\n","import scoperat from \"./client.js\";\n\nexport default scoperat;\nexport { FlagClient } from \"./flag-client.js\";\nexport type {\n EvaluatedFlag,\n EventSource,\n FlushResult,\n IdentifyOptions,\n IdentifyTraits,\n InitOptions,\n TrackEvent,\n} from \"./types.js\";\n"],"mappings":"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","names":["getBreadcrumbs"]}
|