@klime/browser 1.2.1 → 1.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/README.md +9 -0
- package/dist/index.cjs +18 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.global.js +471 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,15 @@ client.group(
|
|
|
44
44
|
client.group("org_456", null, { userId: "user_123" });
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
## Good to know
|
|
48
|
+
|
|
49
|
+
- **No anonymous tracking.** Every `track()` call needs a `userId` or `groupId`. Wait until the user is identified before sending events.
|
|
50
|
+
- **Name your events in past tense** with an Object + Action pattern: `"Report Generated"`, `"User Invited"`, `"Export Completed"`.
|
|
51
|
+
- **Always set `email` and `name` traits** in `identify()` and `group()`. These are used for display and search in the dashboard.
|
|
52
|
+
- **Traits can be strings, numbers, booleans, or ISO 8601 date strings.** Use camelCase for trait keys.
|
|
53
|
+
- **In Group mode, your dashboard stays empty until you call `group()`.** Events from `identify()` and `track()` are recorded, but customers won't appear until users are linked to groups.
|
|
54
|
+
- **Order doesn't matter.** Events sent before `identify()` or `group()` are retroactively attributed once the relationships are established.
|
|
55
|
+
|
|
47
56
|
## Installation Prompt
|
|
48
57
|
|
|
49
58
|
Copy and paste this prompt into Cursor, Copilot, or your favorite AI editor to integrate Klime:
|
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
KlimeClient: () => KlimeClient,
|
|
24
|
-
SendError: () => SendError
|
|
24
|
+
SendError: () => SendError,
|
|
25
|
+
init: () => init
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
27
28
|
|
|
@@ -52,6 +53,19 @@ var createDefaultLogger = () => ({
|
|
|
52
53
|
warn: (message, ...args) => console.warn(`[Klime] ${message}`, ...args),
|
|
53
54
|
error: (message, ...args) => console.error(`[Klime] ${message}`, ...args)
|
|
54
55
|
});
|
|
56
|
+
function init(config) {
|
|
57
|
+
if (typeof window !== "undefined" && window.__klime && window.__klime._writeKey === config.writeKey) {
|
|
58
|
+
return window.__klime;
|
|
59
|
+
}
|
|
60
|
+
const client = new KlimeClient(config);
|
|
61
|
+
if (typeof window !== "undefined") {
|
|
62
|
+
window.__klime = client;
|
|
63
|
+
window.__klime_track = client.track.bind(client);
|
|
64
|
+
window.__klime_identify = client.identify.bind(client);
|
|
65
|
+
window.__klime_group = client.group.bind(client);
|
|
66
|
+
}
|
|
67
|
+
return client;
|
|
68
|
+
}
|
|
55
69
|
var KlimeClient = class {
|
|
56
70
|
constructor(config) {
|
|
57
71
|
this.queue = [];
|
|
@@ -63,6 +77,7 @@ var KlimeClient = class {
|
|
|
63
77
|
if (!config.writeKey) {
|
|
64
78
|
throw new Error("writeKey is required");
|
|
65
79
|
}
|
|
80
|
+
this._writeKey = config.writeKey;
|
|
66
81
|
this.config = {
|
|
67
82
|
writeKey: config.writeKey,
|
|
68
83
|
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
@@ -454,6 +469,7 @@ var KlimeClient = class {
|
|
|
454
469
|
// Annotate the CommonJS export names for ESM import in node:
|
|
455
470
|
0 && (module.exports = {
|
|
456
471
|
KlimeClient,
|
|
457
|
-
SendError
|
|
472
|
+
SendError,
|
|
473
|
+
init
|
|
458
474
|
});
|
|
459
475
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types.ts"],"sourcesContent":["import {\n KlimeConfig,\n TrackOptions,\n Event,\n BatchRequest,\n BatchResponse,\n EventContext,\n Logger,\n SendError,\n} from \"./types\";\n\n// Re-export types for users\nexport { SendError, BatchResponse, Logger } from \"./types\";\nexport type { KlimeConfig, TrackOptions, Event } from \"./types\";\n\nconst DEFAULT_ENDPOINT = \"https://i.klime.com\";\nconst DEFAULT_FLUSH_INTERVAL = 2000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_QUEUE_SIZE = 1000;\nconst DEFAULT_RETRY_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_INITIAL_DELAY = 1000;\nconst MAX_BATCH_SIZE = 100;\nconst MAX_EVENT_SIZE_BYTES = 200 * 1024; // 200KB\nconst MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024; // 10MB\n/** Max body size for fetch keepalive (spec limit). Requests larger than this omit keepalive. */\nconst KEEPALIVE_MAX_BODY_BYTES = 64 * 1024; // 64KB\nconst SDK_VERSION = \"1.1.0\";\n\n// Default logger that wraps console with [Klime] prefix\nconst createDefaultLogger = (): Logger => ({\n debug: (message: string, ...args: any[]) =>\n console.debug(`[Klime] ${message}`, ...args),\n info: (message: string, ...args: any[]) =>\n console.info(`[Klime] ${message}`, ...args),\n warn: (message: string, ...args: any[]) =>\n console.warn(`[Klime] ${message}`, ...args),\n error: (message: string, ...args: any[]) =>\n console.error(`[Klime] ${message}`, ...args),\n});\n\n// Internal config type with required fields and optional callbacks/logger\ninterface InternalConfig {\n writeKey: string;\n endpoint: string;\n flushInterval: number;\n maxBatchSize: number;\n maxQueueSize: number;\n retryMaxAttempts: number;\n retryInitialDelay: number;\n autoFlushOnUnload: boolean;\n logger: Logger;\n onError?: (error: Error, events: Event[]) => void;\n onSuccess?: (response: BatchResponse) => void;\n}\n\nexport class KlimeClient {\n private config: InternalConfig;\n private queue: Event[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private isShutdown = false;\n private flushPromise: Promise<void> | null = null;\n private unloadHandler: (() => void) | null = null;\n private visibilityChangeHandler: (() => void) | null = null;\n\n constructor(config: KlimeConfig) {\n if (!config.writeKey) {\n throw new Error(\"writeKey is required\");\n }\n\n this.config = {\n writeKey: config.writeKey,\n endpoint: config.endpoint || DEFAULT_ENDPOINT,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: Math.min(\n config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n MAX_BATCH_SIZE\n ),\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay:\n config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,\n autoFlushOnUnload: config.autoFlushOnUnload ?? true,\n logger: config.logger ?? createDefaultLogger(),\n onError: config.onError,\n onSuccess: config.onSuccess,\n };\n\n if (this.config.autoFlushOnUnload && typeof window !== \"undefined\") {\n this.unloadHandler = () => {\n this.flush();\n };\n window.addEventListener(\"beforeunload\", this.unloadHandler);\n window.addEventListener(\"pagehide\", this.unloadHandler);\n this.visibilityChangeHandler = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n this.flush();\n }\n };\n document.addEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n this.scheduleFlush();\n }\n\n track(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n this.enqueue(eventObj);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n this.enqueue(eventObj);\n }\n\n group(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n this.enqueue(eventObj);\n }\n\n /**\n * Track an event synchronously. Returns BatchResponse or throws SendError.\n */\n async trackSync(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Identify a user synchronously. Returns BatchResponse or throws SendError.\n */\n async identifySync(\n userId: string,\n traits?: Record<string, any>\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Associate a user with a group synchronously. Returns BatchResponse or throws SendError.\n */\n async groupSync(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Return the number of events currently in the queue.\n */\n getQueueSize(): number {\n return this.queue.length;\n }\n\n async flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n\n this.flushPromise = this.doFlush();\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n this.isShutdown = true;\n\n if (this.unloadHandler && typeof window !== \"undefined\") {\n window.removeEventListener(\"beforeunload\", this.unloadHandler);\n window.removeEventListener(\"pagehide\", this.unloadHandler);\n }\n if (this.visibilityChangeHandler && typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n await this.flush();\n }\n\n private enqueue(event: Event): void {\n // Check event size\n const eventSize = this.estimateEventSize(event);\n if (eventSize > MAX_EVENT_SIZE_BYTES) {\n this.config.logger.warn(\n `Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`\n );\n return;\n }\n\n // Drop oldest if queue is full\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.logger.warn(`Queue full, dropping oldest event: ${dropped?.type}`);\n }\n\n this.queue.push(event);\n this.config.logger.debug(`Enqueued ${event.type} event, queue size: ${this.queue.length}`);\n\n // Check if we should flush immediately\n if (this.queue.length >= this.config.maxBatchSize) {\n this.flush();\n }\n }\n\n private async doFlush(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n // Clear the flush timer\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Process batches\n while (this.queue.length > 0) {\n const batch = this.extractBatch();\n if (batch.length === 0) {\n break;\n }\n\n await this.sendBatch(batch);\n }\n\n // Schedule next flush\n this.scheduleFlush();\n }\n\n private extractBatch(): Event[] {\n const batch: Event[] = [];\n let batchSize = 0;\n\n while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {\n const event = this.queue[0];\n const eventSize = this.estimateEventSize(event);\n\n // Check if adding this event would exceed batch size limit\n if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {\n break;\n }\n\n batch.push(this.queue.shift()!);\n batchSize += eventSize;\n }\n\n return batch;\n }\n\n private async sendBatch(batch: Event[]): Promise<void> {\n if (batch.length === 0) {\n return;\n }\n\n this.config.logger.debug(`Sending batch of ${batch.length} events`);\n const result = await this.doSend(batch);\n\n if (result === null) {\n // Send failed after all retries\n const error = new Error(`Failed to send batch of ${batch.length} events after retries`);\n this.invokeOnError(error, batch);\n } else if (result.failed > 0 && result.errors) {\n this.config.logger.warn(\n `Batch partially failed. Accepted: ${result.accepted}, Failed: ${result.failed}`,\n result.errors\n );\n // Still invoke success callback since some events were accepted\n this.invokeOnSuccess(result);\n } else {\n this.config.logger.debug(`Batch sent successfully. Accepted: ${result.accepted}`);\n this.invokeOnSuccess(result);\n }\n }\n\n private async sendSync(events: Event[]): Promise<BatchResponse> {\n this.config.logger.debug(`Sending ${events.length} events synchronously`);\n const result = await this.doSend(events);\n\n if (result === null) {\n throw new SendError(`Failed to send ${events.length} events after retries`, events);\n }\n\n return result;\n }\n\n private async doSend(batch: Event[]): Promise<BatchResponse | null> {\n const request: BatchRequest = { batch };\n const requestUrl = `${this.config.endpoint}/v1/batch`;\n const body = JSON.stringify(request);\n const useKeepalive = body.length <= KEEPALIVE_MAX_BODY_BYTES;\n\n let attempt = 0;\n let delay = this.config.retryInitialDelay;\n\n while (attempt < this.config.retryMaxAttempts) {\n try {\n const response = await fetch(requestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body,\n keepalive: useKeepalive,\n });\n\n const data: BatchResponse = await response.json();\n\n if (response.ok) {\n return data;\n }\n\n // Handle error responses\n if (response.status === 400 || response.status === 401) {\n // Permanent errors - don't retry\n this.config.logger.error(`Permanent error (${response.status}):`, data);\n return null;\n }\n\n // Transient errors - retry with backoff\n if (response.status === 429 || response.status === 503) {\n const retryAfter = response.headers.get(\"Retry-After\");\n if (retryAfter) {\n delay = parseInt(retryAfter, 10) * 1000;\n }\n\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000); // Cap at 16s\n continue;\n }\n }\n\n // Other errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n }\n } catch (error) {\n // Network errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n } else {\n this.config.logger.error(\"Failed to send batch after retries:\", error);\n }\n }\n }\n\n return null;\n }\n\n private invokeOnError(error: Error, batch: Event[]): void {\n if (this.config.onError) {\n try {\n this.config.onError(error, batch);\n } catch (e) {\n this.config.logger.error(\"Error in onError callback:\", e);\n }\n }\n }\n\n private invokeOnSuccess(response: BatchResponse): void {\n if (this.config.onSuccess) {\n try {\n this.config.onSuccess(response);\n } catch (e) {\n this.config.logger.error(\"Error in onSuccess callback:\", e);\n }\n }\n }\n\n private scheduleFlush(): void {\n if (this.isShutdown || this.flushTimer) {\n return;\n }\n\n this.flushTimer = setTimeout(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private generateUUID(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\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\n private generateTimestamp(): string {\n return new Date().toISOString();\n }\n\n private getContext(): EventContext {\n const context: EventContext = {\n library: {\n name: \"js-sdk\",\n version: SDK_VERSION,\n },\n };\n\n if (typeof navigator !== \"undefined\") {\n if (navigator.userAgent) {\n context.userAgent = navigator.userAgent;\n }\n if (navigator.language) {\n context.locale = navigator.language;\n }\n }\n\n if (typeof Intl !== \"undefined\" && Intl.DateTimeFormat) {\n try {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n if (timezone) {\n context.timezone = timezone;\n }\n } catch (e) {\n // Ignore timezone errors\n }\n }\n\n return context;\n }\n\n private estimateEventSize(event: Event): number {\n // Rough estimate: JSON stringified size\n try {\n return JSON.stringify(event).length;\n } catch {\n // Fallback: rough estimate based on structure\n return 500; // Conservative estimate\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export interface Logger {\n debug(message: string, ...args: any[]): void;\n info(message: string, ...args: any[]): void;\n warn(message: string, ...args: any[]): void;\n error(message: string, ...args: any[]): void;\n}\n\nexport interface KlimeConfig {\n writeKey: string;\n endpoint?: string;\n flushInterval?: number; // milliseconds, default 2000\n maxBatchSize?: number; // default 20, max 100\n maxQueueSize?: number; // default 1000\n retryMaxAttempts?: number; // default 5\n retryInitialDelay?: number; // milliseconds, default 1000\n autoFlushOnUnload?: boolean; // default true\n logger?: Logger; // optional custom logger\n onError?: (error: Error, events: Event[]) => void; // callback for batch failures\n onSuccess?: (response: BatchResponse) => void; // callback for successful sends\n}\n\nexport interface TrackOptions {\n userId?: string;\n groupId?: string;\n}\n\nexport interface Event {\n type: 'track' | 'identify' | 'group';\n messageId: string;\n event?: string; // required for track\n userId?: string; // required for identify\n groupId?: string; // required for group\n timestamp: string; // ISO 8601\n properties?: Record<string, any>; // for track\n traits?: Record<string, any>; // for identify/group\n context?: EventContext;\n}\n\nexport interface EventContext {\n library?: {\n name: string;\n version: string;\n };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n}\n\nexport interface BatchRequest {\n batch: Event[];\n}\n\nexport interface BatchResponse {\n status: string;\n accepted: number;\n failed: number;\n errors?: ValidationError[];\n}\n\nexport interface ValidationError {\n index: number;\n message: string;\n code: string;\n}\n\nexport class SendError extends Error {\n events: Event[];\n\n constructor(message: string, events: Event[]) {\n super(message);\n this.name = 'SendError';\n this.events = events;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts"],"sourcesContent":["import {\n KlimeConfig,\n TrackOptions,\n Event,\n BatchRequest,\n BatchResponse,\n EventContext,\n Logger,\n SendError,\n} from \"./types\";\n\n// Re-export types for users\nexport { SendError, BatchResponse, Logger } from \"./types\";\nexport type { KlimeConfig, TrackOptions, Event } from \"./types\";\n\nconst DEFAULT_ENDPOINT = \"https://i.klime.com\";\nconst DEFAULT_FLUSH_INTERVAL = 2000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_QUEUE_SIZE = 1000;\nconst DEFAULT_RETRY_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_INITIAL_DELAY = 1000;\nconst MAX_BATCH_SIZE = 100;\nconst MAX_EVENT_SIZE_BYTES = 200 * 1024; // 200KB\nconst MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024; // 10MB\n/** Max body size for fetch keepalive (spec limit). Requests larger than this omit keepalive. */\nconst KEEPALIVE_MAX_BODY_BYTES = 64 * 1024; // 64KB\nconst SDK_VERSION = \"1.1.0\";\n\n// Default logger that wraps console with [Klime] prefix\nconst createDefaultLogger = (): Logger => ({\n debug: (message: string, ...args: any[]) =>\n console.debug(`[Klime] ${message}`, ...args),\n info: (message: string, ...args: any[]) =>\n console.info(`[Klime] ${message}`, ...args),\n warn: (message: string, ...args: any[]) =>\n console.warn(`[Klime] ${message}`, ...args),\n error: (message: string, ...args: any[]) =>\n console.error(`[Klime] ${message}`, ...args),\n});\n\n// Internal config type with required fields and optional callbacks/logger\ninterface InternalConfig {\n writeKey: string;\n endpoint: string;\n flushInterval: number;\n maxBatchSize: number;\n maxQueueSize: number;\n retryMaxAttempts: number;\n retryInitialDelay: number;\n autoFlushOnUnload: boolean;\n logger: Logger;\n onError?: (error: Error, events: Event[]) => void;\n onSuccess?: (response: BatchResponse) => void;\n}\n\n/**\n * Factory function for environments where `new` is unavailable (e.g. GTM sandboxed JS).\n * Creates a KlimeClient and registers bound methods as standalone globals so\n * GTM's `callInWindow` can invoke them without `this` context issues.\n */\nexport function init(config: KlimeConfig): KlimeClient {\n if (\n typeof window !== \"undefined\" &&\n (window as any).__klime &&\n (window as any).__klime._writeKey === config.writeKey\n ) {\n return (window as any).__klime;\n }\n\n const client = new KlimeClient(config);\n if (typeof window !== \"undefined\") {\n (window as any).__klime = client;\n (window as any).__klime_track = client.track.bind(client);\n (window as any).__klime_identify = client.identify.bind(client);\n (window as any).__klime_group = client.group.bind(client);\n }\n return client;\n}\n\nexport class KlimeClient {\n /** Exposed for idempotent re-initialization (used by GTM `init()`). */\n readonly _writeKey: string;\n private config: InternalConfig;\n private queue: Event[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private isShutdown = false;\n private flushPromise: Promise<void> | null = null;\n private unloadHandler: (() => void) | null = null;\n private visibilityChangeHandler: (() => void) | null = null;\n\n constructor(config: KlimeConfig) {\n if (!config.writeKey) {\n throw new Error(\"writeKey is required\");\n }\n\n this._writeKey = config.writeKey;\n this.config = {\n writeKey: config.writeKey,\n endpoint: config.endpoint || DEFAULT_ENDPOINT,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: Math.min(\n config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n MAX_BATCH_SIZE\n ),\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay:\n config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,\n autoFlushOnUnload: config.autoFlushOnUnload ?? true,\n logger: config.logger ?? createDefaultLogger(),\n onError: config.onError,\n onSuccess: config.onSuccess,\n };\n\n if (this.config.autoFlushOnUnload && typeof window !== \"undefined\") {\n this.unloadHandler = () => {\n this.flush();\n };\n window.addEventListener(\"beforeunload\", this.unloadHandler);\n window.addEventListener(\"pagehide\", this.unloadHandler);\n this.visibilityChangeHandler = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n this.flush();\n }\n };\n document.addEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n this.scheduleFlush();\n }\n\n track(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n this.enqueue(eventObj);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n this.enqueue(eventObj);\n }\n\n group(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n this.enqueue(eventObj);\n }\n\n /**\n * Track an event synchronously. Returns BatchResponse or throws SendError.\n */\n async trackSync(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Identify a user synchronously. Returns BatchResponse or throws SendError.\n */\n async identifySync(\n userId: string,\n traits?: Record<string, any>\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Associate a user with a group synchronously. Returns BatchResponse or throws SendError.\n */\n async groupSync(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Return the number of events currently in the queue.\n */\n getQueueSize(): number {\n return this.queue.length;\n }\n\n async flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n\n this.flushPromise = this.doFlush();\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n this.isShutdown = true;\n\n if (this.unloadHandler && typeof window !== \"undefined\") {\n window.removeEventListener(\"beforeunload\", this.unloadHandler);\n window.removeEventListener(\"pagehide\", this.unloadHandler);\n }\n if (this.visibilityChangeHandler && typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n await this.flush();\n }\n\n private enqueue(event: Event): void {\n // Check event size\n const eventSize = this.estimateEventSize(event);\n if (eventSize > MAX_EVENT_SIZE_BYTES) {\n this.config.logger.warn(\n `Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`\n );\n return;\n }\n\n // Drop oldest if queue is full\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.logger.warn(`Queue full, dropping oldest event: ${dropped?.type}`);\n }\n\n this.queue.push(event);\n this.config.logger.debug(`Enqueued ${event.type} event, queue size: ${this.queue.length}`);\n\n // Check if we should flush immediately\n if (this.queue.length >= this.config.maxBatchSize) {\n this.flush();\n }\n }\n\n private async doFlush(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n // Clear the flush timer\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Process batches\n while (this.queue.length > 0) {\n const batch = this.extractBatch();\n if (batch.length === 0) {\n break;\n }\n\n await this.sendBatch(batch);\n }\n\n // Schedule next flush\n this.scheduleFlush();\n }\n\n private extractBatch(): Event[] {\n const batch: Event[] = [];\n let batchSize = 0;\n\n while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {\n const event = this.queue[0];\n const eventSize = this.estimateEventSize(event);\n\n // Check if adding this event would exceed batch size limit\n if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {\n break;\n }\n\n batch.push(this.queue.shift()!);\n batchSize += eventSize;\n }\n\n return batch;\n }\n\n private async sendBatch(batch: Event[]): Promise<void> {\n if (batch.length === 0) {\n return;\n }\n\n this.config.logger.debug(`Sending batch of ${batch.length} events`);\n const result = await this.doSend(batch);\n\n if (result === null) {\n // Send failed after all retries\n const error = new Error(`Failed to send batch of ${batch.length} events after retries`);\n this.invokeOnError(error, batch);\n } else if (result.failed > 0 && result.errors) {\n this.config.logger.warn(\n `Batch partially failed. Accepted: ${result.accepted}, Failed: ${result.failed}`,\n result.errors\n );\n // Still invoke success callback since some events were accepted\n this.invokeOnSuccess(result);\n } else {\n this.config.logger.debug(`Batch sent successfully. Accepted: ${result.accepted}`);\n this.invokeOnSuccess(result);\n }\n }\n\n private async sendSync(events: Event[]): Promise<BatchResponse> {\n this.config.logger.debug(`Sending ${events.length} events synchronously`);\n const result = await this.doSend(events);\n\n if (result === null) {\n throw new SendError(`Failed to send ${events.length} events after retries`, events);\n }\n\n return result;\n }\n\n private async doSend(batch: Event[]): Promise<BatchResponse | null> {\n const request: BatchRequest = { batch };\n const requestUrl = `${this.config.endpoint}/v1/batch`;\n const body = JSON.stringify(request);\n const useKeepalive = body.length <= KEEPALIVE_MAX_BODY_BYTES;\n\n let attempt = 0;\n let delay = this.config.retryInitialDelay;\n\n while (attempt < this.config.retryMaxAttempts) {\n try {\n const response = await fetch(requestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body,\n keepalive: useKeepalive,\n });\n\n const data: BatchResponse = await response.json();\n\n if (response.ok) {\n return data;\n }\n\n // Handle error responses\n if (response.status === 400 || response.status === 401) {\n // Permanent errors - don't retry\n this.config.logger.error(`Permanent error (${response.status}):`, data);\n return null;\n }\n\n // Transient errors - retry with backoff\n if (response.status === 429 || response.status === 503) {\n const retryAfter = response.headers.get(\"Retry-After\");\n if (retryAfter) {\n delay = parseInt(retryAfter, 10) * 1000;\n }\n\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000); // Cap at 16s\n continue;\n }\n }\n\n // Other errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n }\n } catch (error) {\n // Network errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n } else {\n this.config.logger.error(\"Failed to send batch after retries:\", error);\n }\n }\n }\n\n return null;\n }\n\n private invokeOnError(error: Error, batch: Event[]): void {\n if (this.config.onError) {\n try {\n this.config.onError(error, batch);\n } catch (e) {\n this.config.logger.error(\"Error in onError callback:\", e);\n }\n }\n }\n\n private invokeOnSuccess(response: BatchResponse): void {\n if (this.config.onSuccess) {\n try {\n this.config.onSuccess(response);\n } catch (e) {\n this.config.logger.error(\"Error in onSuccess callback:\", e);\n }\n }\n }\n\n private scheduleFlush(): void {\n if (this.isShutdown || this.flushTimer) {\n return;\n }\n\n this.flushTimer = setTimeout(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private generateUUID(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\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\n private generateTimestamp(): string {\n return new Date().toISOString();\n }\n\n private getContext(): EventContext {\n const context: EventContext = {\n library: {\n name: \"js-sdk\",\n version: SDK_VERSION,\n },\n };\n\n if (typeof navigator !== \"undefined\") {\n if (navigator.userAgent) {\n context.userAgent = navigator.userAgent;\n }\n if (navigator.language) {\n context.locale = navigator.language;\n }\n }\n\n if (typeof Intl !== \"undefined\" && Intl.DateTimeFormat) {\n try {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n if (timezone) {\n context.timezone = timezone;\n }\n } catch (e) {\n // Ignore timezone errors\n }\n }\n\n return context;\n }\n\n private estimateEventSize(event: Event): number {\n // Rough estimate: JSON stringified size\n try {\n return JSON.stringify(event).length;\n } catch {\n // Fallback: rough estimate based on structure\n return 500; // Conservative estimate\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export interface Logger {\n debug(message: string, ...args: any[]): void;\n info(message: string, ...args: any[]): void;\n warn(message: string, ...args: any[]): void;\n error(message: string, ...args: any[]): void;\n}\n\nexport interface KlimeConfig {\n writeKey: string;\n endpoint?: string;\n flushInterval?: number; // milliseconds, default 2000\n maxBatchSize?: number; // default 20, max 100\n maxQueueSize?: number; // default 1000\n retryMaxAttempts?: number; // default 5\n retryInitialDelay?: number; // milliseconds, default 1000\n autoFlushOnUnload?: boolean; // default true\n logger?: Logger; // optional custom logger\n onError?: (error: Error, events: Event[]) => void; // callback for batch failures\n onSuccess?: (response: BatchResponse) => void; // callback for successful sends\n}\n\nexport interface TrackOptions {\n userId?: string;\n groupId?: string;\n}\n\nexport interface Event {\n type: 'track' | 'identify' | 'group';\n messageId: string;\n event?: string; // required for track\n userId?: string; // required for identify\n groupId?: string; // required for group\n timestamp: string; // ISO 8601\n properties?: Record<string, any>; // for track\n traits?: Record<string, any>; // for identify/group\n context?: EventContext;\n}\n\nexport interface EventContext {\n library?: {\n name: string;\n version: string;\n };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n}\n\nexport interface BatchRequest {\n batch: Event[];\n}\n\nexport interface BatchResponse {\n status: string;\n accepted: number;\n failed: number;\n errors?: ValidationError[];\n}\n\nexport interface ValidationError {\n index: number;\n message: string;\n code: string;\n}\n\nexport class SendError extends Error {\n events: Event[];\n\n constructor(message: string, events: Event[]) {\n super(message);\n this.name = 'SendError';\n this.events = events;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -57,7 +57,15 @@ declare class SendError extends Error {
|
|
|
57
57
|
constructor(message: string, events: Event[]);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Factory function for environments where `new` is unavailable (e.g. GTM sandboxed JS).
|
|
62
|
+
* Creates a KlimeClient and registers bound methods as standalone globals so
|
|
63
|
+
* GTM's `callInWindow` can invoke them without `this` context issues.
|
|
64
|
+
*/
|
|
65
|
+
declare function init(config: KlimeConfig): KlimeClient;
|
|
60
66
|
declare class KlimeClient {
|
|
67
|
+
/** Exposed for idempotent re-initialization (used by GTM `init()`). */
|
|
68
|
+
readonly _writeKey: string;
|
|
61
69
|
private config;
|
|
62
70
|
private queue;
|
|
63
71
|
private flushTimer;
|
|
@@ -103,4 +111,4 @@ declare class KlimeClient {
|
|
|
103
111
|
private sleep;
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
export { type BatchResponse, type Event, KlimeClient, type KlimeConfig, type Logger, SendError, type TrackOptions };
|
|
114
|
+
export { type BatchResponse, type Event, KlimeClient, type KlimeConfig, type Logger, SendError, type TrackOptions, init };
|
package/dist/index.d.ts
CHANGED
|
@@ -57,7 +57,15 @@ declare class SendError extends Error {
|
|
|
57
57
|
constructor(message: string, events: Event[]);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Factory function for environments where `new` is unavailable (e.g. GTM sandboxed JS).
|
|
62
|
+
* Creates a KlimeClient and registers bound methods as standalone globals so
|
|
63
|
+
* GTM's `callInWindow` can invoke them without `this` context issues.
|
|
64
|
+
*/
|
|
65
|
+
declare function init(config: KlimeConfig): KlimeClient;
|
|
60
66
|
declare class KlimeClient {
|
|
67
|
+
/** Exposed for idempotent re-initialization (used by GTM `init()`). */
|
|
68
|
+
readonly _writeKey: string;
|
|
61
69
|
private config;
|
|
62
70
|
private queue;
|
|
63
71
|
private flushTimer;
|
|
@@ -103,4 +111,4 @@ declare class KlimeClient {
|
|
|
103
111
|
private sleep;
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
export { type BatchResponse, type Event, KlimeClient, type KlimeConfig, type Logger, SendError, type TrackOptions };
|
|
114
|
+
export { type BatchResponse, type Event, KlimeClient, type KlimeConfig, type Logger, SendError, type TrackOptions, init };
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var Klime = (() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
KlimeClient: () => KlimeClient,
|
|
25
|
+
SendError: () => SendError,
|
|
26
|
+
init: () => init
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// src/types.ts
|
|
30
|
+
var SendError = class extends Error {
|
|
31
|
+
constructor(message, events) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "SendError";
|
|
34
|
+
this.events = events;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/index.ts
|
|
39
|
+
var DEFAULT_ENDPOINT = "https://i.klime.com";
|
|
40
|
+
var DEFAULT_FLUSH_INTERVAL = 2e3;
|
|
41
|
+
var DEFAULT_MAX_BATCH_SIZE = 20;
|
|
42
|
+
var DEFAULT_MAX_QUEUE_SIZE = 1e3;
|
|
43
|
+
var DEFAULT_RETRY_MAX_ATTEMPTS = 5;
|
|
44
|
+
var DEFAULT_RETRY_INITIAL_DELAY = 1e3;
|
|
45
|
+
var MAX_BATCH_SIZE = 100;
|
|
46
|
+
var MAX_EVENT_SIZE_BYTES = 200 * 1024;
|
|
47
|
+
var MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024;
|
|
48
|
+
var KEEPALIVE_MAX_BODY_BYTES = 64 * 1024;
|
|
49
|
+
var SDK_VERSION = "1.1.0";
|
|
50
|
+
var createDefaultLogger = () => ({
|
|
51
|
+
debug: (message, ...args) => console.debug(`[Klime] ${message}`, ...args),
|
|
52
|
+
info: (message, ...args) => console.info(`[Klime] ${message}`, ...args),
|
|
53
|
+
warn: (message, ...args) => console.warn(`[Klime] ${message}`, ...args),
|
|
54
|
+
error: (message, ...args) => console.error(`[Klime] ${message}`, ...args)
|
|
55
|
+
});
|
|
56
|
+
function init(config) {
|
|
57
|
+
if (typeof window !== "undefined" && window.__klime && window.__klime._writeKey === config.writeKey) {
|
|
58
|
+
return window.__klime;
|
|
59
|
+
}
|
|
60
|
+
const client = new KlimeClient(config);
|
|
61
|
+
if (typeof window !== "undefined") {
|
|
62
|
+
window.__klime = client;
|
|
63
|
+
window.__klime_track = client.track.bind(client);
|
|
64
|
+
window.__klime_identify = client.identify.bind(client);
|
|
65
|
+
window.__klime_group = client.group.bind(client);
|
|
66
|
+
}
|
|
67
|
+
return client;
|
|
68
|
+
}
|
|
69
|
+
var KlimeClient = class {
|
|
70
|
+
constructor(config) {
|
|
71
|
+
this.queue = [];
|
|
72
|
+
this.flushTimer = null;
|
|
73
|
+
this.isShutdown = false;
|
|
74
|
+
this.flushPromise = null;
|
|
75
|
+
this.unloadHandler = null;
|
|
76
|
+
this.visibilityChangeHandler = null;
|
|
77
|
+
if (!config.writeKey) {
|
|
78
|
+
throw new Error("writeKey is required");
|
|
79
|
+
}
|
|
80
|
+
this._writeKey = config.writeKey;
|
|
81
|
+
this.config = {
|
|
82
|
+
writeKey: config.writeKey,
|
|
83
|
+
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
84
|
+
flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
|
|
85
|
+
maxBatchSize: Math.min(
|
|
86
|
+
config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,
|
|
87
|
+
MAX_BATCH_SIZE
|
|
88
|
+
),
|
|
89
|
+
maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,
|
|
90
|
+
retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,
|
|
91
|
+
retryInitialDelay: config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,
|
|
92
|
+
autoFlushOnUnload: config.autoFlushOnUnload ?? true,
|
|
93
|
+
logger: config.logger ?? createDefaultLogger(),
|
|
94
|
+
onError: config.onError,
|
|
95
|
+
onSuccess: config.onSuccess
|
|
96
|
+
};
|
|
97
|
+
if (this.config.autoFlushOnUnload && typeof window !== "undefined") {
|
|
98
|
+
this.unloadHandler = () => {
|
|
99
|
+
this.flush();
|
|
100
|
+
};
|
|
101
|
+
window.addEventListener("beforeunload", this.unloadHandler);
|
|
102
|
+
window.addEventListener("pagehide", this.unloadHandler);
|
|
103
|
+
this.visibilityChangeHandler = () => {
|
|
104
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
105
|
+
this.flush();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
document.addEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
109
|
+
}
|
|
110
|
+
this.scheduleFlush();
|
|
111
|
+
}
|
|
112
|
+
track(event, properties, options) {
|
|
113
|
+
if (this.isShutdown) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const eventObj = {
|
|
117
|
+
type: "track",
|
|
118
|
+
messageId: this.generateUUID(),
|
|
119
|
+
event,
|
|
120
|
+
timestamp: this.generateTimestamp(),
|
|
121
|
+
properties: properties || {},
|
|
122
|
+
context: this.getContext()
|
|
123
|
+
};
|
|
124
|
+
if (options?.userId) {
|
|
125
|
+
eventObj.userId = options.userId;
|
|
126
|
+
}
|
|
127
|
+
if (options?.groupId) {
|
|
128
|
+
eventObj.groupId = options.groupId;
|
|
129
|
+
}
|
|
130
|
+
this.enqueue(eventObj);
|
|
131
|
+
}
|
|
132
|
+
identify(userId, traits) {
|
|
133
|
+
if (this.isShutdown) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const eventObj = {
|
|
137
|
+
type: "identify",
|
|
138
|
+
messageId: this.generateUUID(),
|
|
139
|
+
userId,
|
|
140
|
+
timestamp: this.generateTimestamp(),
|
|
141
|
+
traits: traits || {},
|
|
142
|
+
context: this.getContext()
|
|
143
|
+
};
|
|
144
|
+
this.enqueue(eventObj);
|
|
145
|
+
}
|
|
146
|
+
group(groupId, traits, options) {
|
|
147
|
+
if (this.isShutdown) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const eventObj = {
|
|
151
|
+
type: "group",
|
|
152
|
+
messageId: this.generateUUID(),
|
|
153
|
+
groupId,
|
|
154
|
+
timestamp: this.generateTimestamp(),
|
|
155
|
+
traits: traits || {},
|
|
156
|
+
context: this.getContext()
|
|
157
|
+
};
|
|
158
|
+
if (options?.userId) {
|
|
159
|
+
eventObj.userId = options.userId;
|
|
160
|
+
}
|
|
161
|
+
this.enqueue(eventObj);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Track an event synchronously. Returns BatchResponse or throws SendError.
|
|
165
|
+
*/
|
|
166
|
+
async trackSync(event, properties, options) {
|
|
167
|
+
if (this.isShutdown) {
|
|
168
|
+
throw new SendError("Client is shutdown", []);
|
|
169
|
+
}
|
|
170
|
+
const eventObj = {
|
|
171
|
+
type: "track",
|
|
172
|
+
messageId: this.generateUUID(),
|
|
173
|
+
event,
|
|
174
|
+
timestamp: this.generateTimestamp(),
|
|
175
|
+
properties: properties || {},
|
|
176
|
+
context: this.getContext()
|
|
177
|
+
};
|
|
178
|
+
if (options?.userId) {
|
|
179
|
+
eventObj.userId = options.userId;
|
|
180
|
+
}
|
|
181
|
+
if (options?.groupId) {
|
|
182
|
+
eventObj.groupId = options.groupId;
|
|
183
|
+
}
|
|
184
|
+
return this.sendSync([eventObj]);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Identify a user synchronously. Returns BatchResponse or throws SendError.
|
|
188
|
+
*/
|
|
189
|
+
async identifySync(userId, traits) {
|
|
190
|
+
if (this.isShutdown) {
|
|
191
|
+
throw new SendError("Client is shutdown", []);
|
|
192
|
+
}
|
|
193
|
+
const eventObj = {
|
|
194
|
+
type: "identify",
|
|
195
|
+
messageId: this.generateUUID(),
|
|
196
|
+
userId,
|
|
197
|
+
timestamp: this.generateTimestamp(),
|
|
198
|
+
traits: traits || {},
|
|
199
|
+
context: this.getContext()
|
|
200
|
+
};
|
|
201
|
+
return this.sendSync([eventObj]);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Associate a user with a group synchronously. Returns BatchResponse or throws SendError.
|
|
205
|
+
*/
|
|
206
|
+
async groupSync(groupId, traits, options) {
|
|
207
|
+
if (this.isShutdown) {
|
|
208
|
+
throw new SendError("Client is shutdown", []);
|
|
209
|
+
}
|
|
210
|
+
const eventObj = {
|
|
211
|
+
type: "group",
|
|
212
|
+
messageId: this.generateUUID(),
|
|
213
|
+
groupId,
|
|
214
|
+
timestamp: this.generateTimestamp(),
|
|
215
|
+
traits: traits || {},
|
|
216
|
+
context: this.getContext()
|
|
217
|
+
};
|
|
218
|
+
if (options?.userId) {
|
|
219
|
+
eventObj.userId = options.userId;
|
|
220
|
+
}
|
|
221
|
+
return this.sendSync([eventObj]);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Return the number of events currently in the queue.
|
|
225
|
+
*/
|
|
226
|
+
getQueueSize() {
|
|
227
|
+
return this.queue.length;
|
|
228
|
+
}
|
|
229
|
+
async flush() {
|
|
230
|
+
if (this.flushPromise) {
|
|
231
|
+
return this.flushPromise;
|
|
232
|
+
}
|
|
233
|
+
this.flushPromise = this.doFlush();
|
|
234
|
+
try {
|
|
235
|
+
await this.flushPromise;
|
|
236
|
+
} finally {
|
|
237
|
+
this.flushPromise = null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async shutdown() {
|
|
241
|
+
if (this.isShutdown) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
this.isShutdown = true;
|
|
245
|
+
if (this.unloadHandler && typeof window !== "undefined") {
|
|
246
|
+
window.removeEventListener("beforeunload", this.unloadHandler);
|
|
247
|
+
window.removeEventListener("pagehide", this.unloadHandler);
|
|
248
|
+
}
|
|
249
|
+
if (this.visibilityChangeHandler && typeof document !== "undefined") {
|
|
250
|
+
document.removeEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
251
|
+
}
|
|
252
|
+
if (this.flushTimer) {
|
|
253
|
+
clearTimeout(this.flushTimer);
|
|
254
|
+
this.flushTimer = null;
|
|
255
|
+
}
|
|
256
|
+
await this.flush();
|
|
257
|
+
}
|
|
258
|
+
enqueue(event) {
|
|
259
|
+
const eventSize = this.estimateEventSize(event);
|
|
260
|
+
if (eventSize > MAX_EVENT_SIZE_BYTES) {
|
|
261
|
+
this.config.logger.warn(
|
|
262
|
+
`Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`
|
|
263
|
+
);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (this.queue.length >= this.config.maxQueueSize) {
|
|
267
|
+
const dropped = this.queue.shift();
|
|
268
|
+
this.config.logger.warn(`Queue full, dropping oldest event: ${dropped?.type}`);
|
|
269
|
+
}
|
|
270
|
+
this.queue.push(event);
|
|
271
|
+
this.config.logger.debug(`Enqueued ${event.type} event, queue size: ${this.queue.length}`);
|
|
272
|
+
if (this.queue.length >= this.config.maxBatchSize) {
|
|
273
|
+
this.flush();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async doFlush() {
|
|
277
|
+
if (this.queue.length === 0) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (this.flushTimer) {
|
|
281
|
+
clearTimeout(this.flushTimer);
|
|
282
|
+
this.flushTimer = null;
|
|
283
|
+
}
|
|
284
|
+
while (this.queue.length > 0) {
|
|
285
|
+
const batch = this.extractBatch();
|
|
286
|
+
if (batch.length === 0) {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
await this.sendBatch(batch);
|
|
290
|
+
}
|
|
291
|
+
this.scheduleFlush();
|
|
292
|
+
}
|
|
293
|
+
extractBatch() {
|
|
294
|
+
const batch = [];
|
|
295
|
+
let batchSize = 0;
|
|
296
|
+
while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {
|
|
297
|
+
const event = this.queue[0];
|
|
298
|
+
const eventSize = this.estimateEventSize(event);
|
|
299
|
+
if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
batch.push(this.queue.shift());
|
|
303
|
+
batchSize += eventSize;
|
|
304
|
+
}
|
|
305
|
+
return batch;
|
|
306
|
+
}
|
|
307
|
+
async sendBatch(batch) {
|
|
308
|
+
if (batch.length === 0) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
this.config.logger.debug(`Sending batch of ${batch.length} events`);
|
|
312
|
+
const result = await this.doSend(batch);
|
|
313
|
+
if (result === null) {
|
|
314
|
+
const error = new Error(`Failed to send batch of ${batch.length} events after retries`);
|
|
315
|
+
this.invokeOnError(error, batch);
|
|
316
|
+
} else if (result.failed > 0 && result.errors) {
|
|
317
|
+
this.config.logger.warn(
|
|
318
|
+
`Batch partially failed. Accepted: ${result.accepted}, Failed: ${result.failed}`,
|
|
319
|
+
result.errors
|
|
320
|
+
);
|
|
321
|
+
this.invokeOnSuccess(result);
|
|
322
|
+
} else {
|
|
323
|
+
this.config.logger.debug(`Batch sent successfully. Accepted: ${result.accepted}`);
|
|
324
|
+
this.invokeOnSuccess(result);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async sendSync(events) {
|
|
328
|
+
this.config.logger.debug(`Sending ${events.length} events synchronously`);
|
|
329
|
+
const result = await this.doSend(events);
|
|
330
|
+
if (result === null) {
|
|
331
|
+
throw new SendError(`Failed to send ${events.length} events after retries`, events);
|
|
332
|
+
}
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
335
|
+
async doSend(batch) {
|
|
336
|
+
const request = { batch };
|
|
337
|
+
const requestUrl = `${this.config.endpoint}/v1/batch`;
|
|
338
|
+
const body = JSON.stringify(request);
|
|
339
|
+
const useKeepalive = body.length <= KEEPALIVE_MAX_BODY_BYTES;
|
|
340
|
+
let attempt = 0;
|
|
341
|
+
let delay = this.config.retryInitialDelay;
|
|
342
|
+
while (attempt < this.config.retryMaxAttempts) {
|
|
343
|
+
try {
|
|
344
|
+
const response = await fetch(requestUrl, {
|
|
345
|
+
method: "POST",
|
|
346
|
+
headers: {
|
|
347
|
+
"Content-Type": "application/json",
|
|
348
|
+
Authorization: `Bearer ${this.config.writeKey}`
|
|
349
|
+
},
|
|
350
|
+
body,
|
|
351
|
+
keepalive: useKeepalive
|
|
352
|
+
});
|
|
353
|
+
const data = await response.json();
|
|
354
|
+
if (response.ok) {
|
|
355
|
+
return data;
|
|
356
|
+
}
|
|
357
|
+
if (response.status === 400 || response.status === 401) {
|
|
358
|
+
this.config.logger.error(`Permanent error (${response.status}):`, data);
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
if (response.status === 429 || response.status === 503) {
|
|
362
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
363
|
+
if (retryAfter) {
|
|
364
|
+
delay = parseInt(retryAfter, 10) * 1e3;
|
|
365
|
+
}
|
|
366
|
+
attempt++;
|
|
367
|
+
if (attempt < this.config.retryMaxAttempts) {
|
|
368
|
+
this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);
|
|
369
|
+
await this.sleep(delay);
|
|
370
|
+
delay = Math.min(delay * 2, 16e3);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
attempt++;
|
|
375
|
+
if (attempt < this.config.retryMaxAttempts) {
|
|
376
|
+
this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);
|
|
377
|
+
await this.sleep(delay);
|
|
378
|
+
delay = Math.min(delay * 2, 16e3);
|
|
379
|
+
}
|
|
380
|
+
} catch (error) {
|
|
381
|
+
attempt++;
|
|
382
|
+
if (attempt < this.config.retryMaxAttempts) {
|
|
383
|
+
this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);
|
|
384
|
+
await this.sleep(delay);
|
|
385
|
+
delay = Math.min(delay * 2, 16e3);
|
|
386
|
+
} else {
|
|
387
|
+
this.config.logger.error("Failed to send batch after retries:", error);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
invokeOnError(error, batch) {
|
|
394
|
+
if (this.config.onError) {
|
|
395
|
+
try {
|
|
396
|
+
this.config.onError(error, batch);
|
|
397
|
+
} catch (e) {
|
|
398
|
+
this.config.logger.error("Error in onError callback:", e);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
invokeOnSuccess(response) {
|
|
403
|
+
if (this.config.onSuccess) {
|
|
404
|
+
try {
|
|
405
|
+
this.config.onSuccess(response);
|
|
406
|
+
} catch (e) {
|
|
407
|
+
this.config.logger.error("Error in onSuccess callback:", e);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
scheduleFlush() {
|
|
412
|
+
if (this.isShutdown || this.flushTimer) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
this.flushTimer = setTimeout(() => {
|
|
416
|
+
this.flush();
|
|
417
|
+
}, this.config.flushInterval);
|
|
418
|
+
}
|
|
419
|
+
generateUUID() {
|
|
420
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
421
|
+
return crypto.randomUUID();
|
|
422
|
+
}
|
|
423
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
424
|
+
const r = Math.random() * 16 | 0;
|
|
425
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
426
|
+
return v.toString(16);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
generateTimestamp() {
|
|
430
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
431
|
+
}
|
|
432
|
+
getContext() {
|
|
433
|
+
const context = {
|
|
434
|
+
library: {
|
|
435
|
+
name: "js-sdk",
|
|
436
|
+
version: SDK_VERSION
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
if (typeof navigator !== "undefined") {
|
|
440
|
+
if (navigator.userAgent) {
|
|
441
|
+
context.userAgent = navigator.userAgent;
|
|
442
|
+
}
|
|
443
|
+
if (navigator.language) {
|
|
444
|
+
context.locale = navigator.language;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (typeof Intl !== "undefined" && Intl.DateTimeFormat) {
|
|
448
|
+
try {
|
|
449
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
450
|
+
if (timezone) {
|
|
451
|
+
context.timezone = timezone;
|
|
452
|
+
}
|
|
453
|
+
} catch (e) {
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return context;
|
|
457
|
+
}
|
|
458
|
+
estimateEventSize(event) {
|
|
459
|
+
try {
|
|
460
|
+
return JSON.stringify(event).length;
|
|
461
|
+
} catch {
|
|
462
|
+
return 500;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
sleep(ms) {
|
|
466
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
return __toCommonJS(index_exports);
|
|
470
|
+
})();
|
|
471
|
+
//# sourceMappingURL=index.global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts"],"sourcesContent":["import {\n KlimeConfig,\n TrackOptions,\n Event,\n BatchRequest,\n BatchResponse,\n EventContext,\n Logger,\n SendError,\n} from \"./types\";\n\n// Re-export types for users\nexport { SendError, BatchResponse, Logger } from \"./types\";\nexport type { KlimeConfig, TrackOptions, Event } from \"./types\";\n\nconst DEFAULT_ENDPOINT = \"https://i.klime.com\";\nconst DEFAULT_FLUSH_INTERVAL = 2000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_QUEUE_SIZE = 1000;\nconst DEFAULT_RETRY_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_INITIAL_DELAY = 1000;\nconst MAX_BATCH_SIZE = 100;\nconst MAX_EVENT_SIZE_BYTES = 200 * 1024; // 200KB\nconst MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024; // 10MB\n/** Max body size for fetch keepalive (spec limit). Requests larger than this omit keepalive. */\nconst KEEPALIVE_MAX_BODY_BYTES = 64 * 1024; // 64KB\nconst SDK_VERSION = \"1.1.0\";\n\n// Default logger that wraps console with [Klime] prefix\nconst createDefaultLogger = (): Logger => ({\n debug: (message: string, ...args: any[]) =>\n console.debug(`[Klime] ${message}`, ...args),\n info: (message: string, ...args: any[]) =>\n console.info(`[Klime] ${message}`, ...args),\n warn: (message: string, ...args: any[]) =>\n console.warn(`[Klime] ${message}`, ...args),\n error: (message: string, ...args: any[]) =>\n console.error(`[Klime] ${message}`, ...args),\n});\n\n// Internal config type with required fields and optional callbacks/logger\ninterface InternalConfig {\n writeKey: string;\n endpoint: string;\n flushInterval: number;\n maxBatchSize: number;\n maxQueueSize: number;\n retryMaxAttempts: number;\n retryInitialDelay: number;\n autoFlushOnUnload: boolean;\n logger: Logger;\n onError?: (error: Error, events: Event[]) => void;\n onSuccess?: (response: BatchResponse) => void;\n}\n\n/**\n * Factory function for environments where `new` is unavailable (e.g. GTM sandboxed JS).\n * Creates a KlimeClient and registers bound methods as standalone globals so\n * GTM's `callInWindow` can invoke them without `this` context issues.\n */\nexport function init(config: KlimeConfig): KlimeClient {\n if (\n typeof window !== \"undefined\" &&\n (window as any).__klime &&\n (window as any).__klime._writeKey === config.writeKey\n ) {\n return (window as any).__klime;\n }\n\n const client = new KlimeClient(config);\n if (typeof window !== \"undefined\") {\n (window as any).__klime = client;\n (window as any).__klime_track = client.track.bind(client);\n (window as any).__klime_identify = client.identify.bind(client);\n (window as any).__klime_group = client.group.bind(client);\n }\n return client;\n}\n\nexport class KlimeClient {\n /** Exposed for idempotent re-initialization (used by GTM `init()`). */\n readonly _writeKey: string;\n private config: InternalConfig;\n private queue: Event[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private isShutdown = false;\n private flushPromise: Promise<void> | null = null;\n private unloadHandler: (() => void) | null = null;\n private visibilityChangeHandler: (() => void) | null = null;\n\n constructor(config: KlimeConfig) {\n if (!config.writeKey) {\n throw new Error(\"writeKey is required\");\n }\n\n this._writeKey = config.writeKey;\n this.config = {\n writeKey: config.writeKey,\n endpoint: config.endpoint || DEFAULT_ENDPOINT,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: Math.min(\n config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n MAX_BATCH_SIZE\n ),\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay:\n config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,\n autoFlushOnUnload: config.autoFlushOnUnload ?? true,\n logger: config.logger ?? createDefaultLogger(),\n onError: config.onError,\n onSuccess: config.onSuccess,\n };\n\n if (this.config.autoFlushOnUnload && typeof window !== \"undefined\") {\n this.unloadHandler = () => {\n this.flush();\n };\n window.addEventListener(\"beforeunload\", this.unloadHandler);\n window.addEventListener(\"pagehide\", this.unloadHandler);\n this.visibilityChangeHandler = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n this.flush();\n }\n };\n document.addEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n this.scheduleFlush();\n }\n\n track(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n this.enqueue(eventObj);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n this.enqueue(eventObj);\n }\n\n group(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n this.enqueue(eventObj);\n }\n\n /**\n * Track an event synchronously. Returns BatchResponse or throws SendError.\n */\n async trackSync(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Identify a user synchronously. Returns BatchResponse or throws SendError.\n */\n async identifySync(\n userId: string,\n traits?: Record<string, any>\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Associate a user with a group synchronously. Returns BatchResponse or throws SendError.\n */\n async groupSync(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Return the number of events currently in the queue.\n */\n getQueueSize(): number {\n return this.queue.length;\n }\n\n async flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n\n this.flushPromise = this.doFlush();\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n this.isShutdown = true;\n\n if (this.unloadHandler && typeof window !== \"undefined\") {\n window.removeEventListener(\"beforeunload\", this.unloadHandler);\n window.removeEventListener(\"pagehide\", this.unloadHandler);\n }\n if (this.visibilityChangeHandler && typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n await this.flush();\n }\n\n private enqueue(event: Event): void {\n // Check event size\n const eventSize = this.estimateEventSize(event);\n if (eventSize > MAX_EVENT_SIZE_BYTES) {\n this.config.logger.warn(\n `Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`\n );\n return;\n }\n\n // Drop oldest if queue is full\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.logger.warn(`Queue full, dropping oldest event: ${dropped?.type}`);\n }\n\n this.queue.push(event);\n this.config.logger.debug(`Enqueued ${event.type} event, queue size: ${this.queue.length}`);\n\n // Check if we should flush immediately\n if (this.queue.length >= this.config.maxBatchSize) {\n this.flush();\n }\n }\n\n private async doFlush(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n // Clear the flush timer\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Process batches\n while (this.queue.length > 0) {\n const batch = this.extractBatch();\n if (batch.length === 0) {\n break;\n }\n\n await this.sendBatch(batch);\n }\n\n // Schedule next flush\n this.scheduleFlush();\n }\n\n private extractBatch(): Event[] {\n const batch: Event[] = [];\n let batchSize = 0;\n\n while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {\n const event = this.queue[0];\n const eventSize = this.estimateEventSize(event);\n\n // Check if adding this event would exceed batch size limit\n if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {\n break;\n }\n\n batch.push(this.queue.shift()!);\n batchSize += eventSize;\n }\n\n return batch;\n }\n\n private async sendBatch(batch: Event[]): Promise<void> {\n if (batch.length === 0) {\n return;\n }\n\n this.config.logger.debug(`Sending batch of ${batch.length} events`);\n const result = await this.doSend(batch);\n\n if (result === null) {\n // Send failed after all retries\n const error = new Error(`Failed to send batch of ${batch.length} events after retries`);\n this.invokeOnError(error, batch);\n } else if (result.failed > 0 && result.errors) {\n this.config.logger.warn(\n `Batch partially failed. Accepted: ${result.accepted}, Failed: ${result.failed}`,\n result.errors\n );\n // Still invoke success callback since some events were accepted\n this.invokeOnSuccess(result);\n } else {\n this.config.logger.debug(`Batch sent successfully. Accepted: ${result.accepted}`);\n this.invokeOnSuccess(result);\n }\n }\n\n private async sendSync(events: Event[]): Promise<BatchResponse> {\n this.config.logger.debug(`Sending ${events.length} events synchronously`);\n const result = await this.doSend(events);\n\n if (result === null) {\n throw new SendError(`Failed to send ${events.length} events after retries`, events);\n }\n\n return result;\n }\n\n private async doSend(batch: Event[]): Promise<BatchResponse | null> {\n const request: BatchRequest = { batch };\n const requestUrl = `${this.config.endpoint}/v1/batch`;\n const body = JSON.stringify(request);\n const useKeepalive = body.length <= KEEPALIVE_MAX_BODY_BYTES;\n\n let attempt = 0;\n let delay = this.config.retryInitialDelay;\n\n while (attempt < this.config.retryMaxAttempts) {\n try {\n const response = await fetch(requestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body,\n keepalive: useKeepalive,\n });\n\n const data: BatchResponse = await response.json();\n\n if (response.ok) {\n return data;\n }\n\n // Handle error responses\n if (response.status === 400 || response.status === 401) {\n // Permanent errors - don't retry\n this.config.logger.error(`Permanent error (${response.status}):`, data);\n return null;\n }\n\n // Transient errors - retry with backoff\n if (response.status === 429 || response.status === 503) {\n const retryAfter = response.headers.get(\"Retry-After\");\n if (retryAfter) {\n delay = parseInt(retryAfter, 10) * 1000;\n }\n\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000); // Cap at 16s\n continue;\n }\n }\n\n // Other errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n }\n } catch (error) {\n // Network errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n } else {\n this.config.logger.error(\"Failed to send batch after retries:\", error);\n }\n }\n }\n\n return null;\n }\n\n private invokeOnError(error: Error, batch: Event[]): void {\n if (this.config.onError) {\n try {\n this.config.onError(error, batch);\n } catch (e) {\n this.config.logger.error(\"Error in onError callback:\", e);\n }\n }\n }\n\n private invokeOnSuccess(response: BatchResponse): void {\n if (this.config.onSuccess) {\n try {\n this.config.onSuccess(response);\n } catch (e) {\n this.config.logger.error(\"Error in onSuccess callback:\", e);\n }\n }\n }\n\n private scheduleFlush(): void {\n if (this.isShutdown || this.flushTimer) {\n return;\n }\n\n this.flushTimer = setTimeout(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private generateUUID(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\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\n private generateTimestamp(): string {\n return new Date().toISOString();\n }\n\n private getContext(): EventContext {\n const context: EventContext = {\n library: {\n name: \"js-sdk\",\n version: SDK_VERSION,\n },\n };\n\n if (typeof navigator !== \"undefined\") {\n if (navigator.userAgent) {\n context.userAgent = navigator.userAgent;\n }\n if (navigator.language) {\n context.locale = navigator.language;\n }\n }\n\n if (typeof Intl !== \"undefined\" && Intl.DateTimeFormat) {\n try {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n if (timezone) {\n context.timezone = timezone;\n }\n } catch (e) {\n // Ignore timezone errors\n }\n }\n\n return context;\n }\n\n private estimateEventSize(event: Event): number {\n // Rough estimate: JSON stringified size\n try {\n return JSON.stringify(event).length;\n } catch {\n // Fallback: rough estimate based on structure\n return 500; // Conservative estimate\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","export interface Logger {\n debug(message: string, ...args: any[]): void;\n info(message: string, ...args: any[]): void;\n warn(message: string, ...args: any[]): void;\n error(message: string, ...args: any[]): void;\n}\n\nexport interface KlimeConfig {\n writeKey: string;\n endpoint?: string;\n flushInterval?: number; // milliseconds, default 2000\n maxBatchSize?: number; // default 20, max 100\n maxQueueSize?: number; // default 1000\n retryMaxAttempts?: number; // default 5\n retryInitialDelay?: number; // milliseconds, default 1000\n autoFlushOnUnload?: boolean; // default true\n logger?: Logger; // optional custom logger\n onError?: (error: Error, events: Event[]) => void; // callback for batch failures\n onSuccess?: (response: BatchResponse) => void; // callback for successful sends\n}\n\nexport interface TrackOptions {\n userId?: string;\n groupId?: string;\n}\n\nexport interface Event {\n type: 'track' | 'identify' | 'group';\n messageId: string;\n event?: string; // required for track\n userId?: string; // required for identify\n groupId?: string; // required for group\n timestamp: string; // ISO 8601\n properties?: Record<string, any>; // for track\n traits?: Record<string, any>; // for identify/group\n context?: EventContext;\n}\n\nexport interface EventContext {\n library?: {\n name: string;\n version: string;\n };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n}\n\nexport interface BatchRequest {\n batch: Event[];\n}\n\nexport interface BatchResponse {\n status: string;\n accepted: number;\n failed: number;\n errors?: ValidationError[];\n}\n\nexport interface ValidationError {\n index: number;\n message: string;\n code: string;\n}\n\nexport class SendError extends Error {\n events: Event[];\n\n constructor(message: string, events: Event[]) {\n super(message);\n this.name = 'SendError';\n this.events = events;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,19 @@ var createDefaultLogger = () => ({
|
|
|
25
25
|
warn: (message, ...args) => console.warn(`[Klime] ${message}`, ...args),
|
|
26
26
|
error: (message, ...args) => console.error(`[Klime] ${message}`, ...args)
|
|
27
27
|
});
|
|
28
|
+
function init(config) {
|
|
29
|
+
if (typeof window !== "undefined" && window.__klime && window.__klime._writeKey === config.writeKey) {
|
|
30
|
+
return window.__klime;
|
|
31
|
+
}
|
|
32
|
+
const client = new KlimeClient(config);
|
|
33
|
+
if (typeof window !== "undefined") {
|
|
34
|
+
window.__klime = client;
|
|
35
|
+
window.__klime_track = client.track.bind(client);
|
|
36
|
+
window.__klime_identify = client.identify.bind(client);
|
|
37
|
+
window.__klime_group = client.group.bind(client);
|
|
38
|
+
}
|
|
39
|
+
return client;
|
|
40
|
+
}
|
|
28
41
|
var KlimeClient = class {
|
|
29
42
|
constructor(config) {
|
|
30
43
|
this.queue = [];
|
|
@@ -36,6 +49,7 @@ var KlimeClient = class {
|
|
|
36
49
|
if (!config.writeKey) {
|
|
37
50
|
throw new Error("writeKey is required");
|
|
38
51
|
}
|
|
52
|
+
this._writeKey = config.writeKey;
|
|
39
53
|
this.config = {
|
|
40
54
|
writeKey: config.writeKey,
|
|
41
55
|
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
@@ -426,6 +440,7 @@ var KlimeClient = class {
|
|
|
426
440
|
};
|
|
427
441
|
export {
|
|
428
442
|
KlimeClient,
|
|
429
|
-
SendError
|
|
443
|
+
SendError,
|
|
444
|
+
init
|
|
430
445
|
};
|
|
431
446
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":["export interface Logger {\n debug(message: string, ...args: any[]): void;\n info(message: string, ...args: any[]): void;\n warn(message: string, ...args: any[]): void;\n error(message: string, ...args: any[]): void;\n}\n\nexport interface KlimeConfig {\n writeKey: string;\n endpoint?: string;\n flushInterval?: number; // milliseconds, default 2000\n maxBatchSize?: number; // default 20, max 100\n maxQueueSize?: number; // default 1000\n retryMaxAttempts?: number; // default 5\n retryInitialDelay?: number; // milliseconds, default 1000\n autoFlushOnUnload?: boolean; // default true\n logger?: Logger; // optional custom logger\n onError?: (error: Error, events: Event[]) => void; // callback for batch failures\n onSuccess?: (response: BatchResponse) => void; // callback for successful sends\n}\n\nexport interface TrackOptions {\n userId?: string;\n groupId?: string;\n}\n\nexport interface Event {\n type: 'track' | 'identify' | 'group';\n messageId: string;\n event?: string; // required for track\n userId?: string; // required for identify\n groupId?: string; // required for group\n timestamp: string; // ISO 8601\n properties?: Record<string, any>; // for track\n traits?: Record<string, any>; // for identify/group\n context?: EventContext;\n}\n\nexport interface EventContext {\n library?: {\n name: string;\n version: string;\n };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n}\n\nexport interface BatchRequest {\n batch: Event[];\n}\n\nexport interface BatchResponse {\n status: string;\n accepted: number;\n failed: number;\n errors?: ValidationError[];\n}\n\nexport interface ValidationError {\n index: number;\n message: string;\n code: string;\n}\n\nexport class SendError extends Error {\n events: Event[];\n\n constructor(message: string, events: Event[]) {\n super(message);\n this.name = 'SendError';\n this.events = events;\n }\n}\n","import {\n KlimeConfig,\n TrackOptions,\n Event,\n BatchRequest,\n BatchResponse,\n EventContext,\n Logger,\n SendError,\n} from \"./types\";\n\n// Re-export types for users\nexport { SendError, BatchResponse, Logger } from \"./types\";\nexport type { KlimeConfig, TrackOptions, Event } from \"./types\";\n\nconst DEFAULT_ENDPOINT = \"https://i.klime.com\";\nconst DEFAULT_FLUSH_INTERVAL = 2000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_QUEUE_SIZE = 1000;\nconst DEFAULT_RETRY_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_INITIAL_DELAY = 1000;\nconst MAX_BATCH_SIZE = 100;\nconst MAX_EVENT_SIZE_BYTES = 200 * 1024; // 200KB\nconst MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024; // 10MB\n/** Max body size for fetch keepalive (spec limit). Requests larger than this omit keepalive. */\nconst KEEPALIVE_MAX_BODY_BYTES = 64 * 1024; // 64KB\nconst SDK_VERSION = \"1.1.0\";\n\n// Default logger that wraps console with [Klime] prefix\nconst createDefaultLogger = (): Logger => ({\n debug: (message: string, ...args: any[]) =>\n console.debug(`[Klime] ${message}`, ...args),\n info: (message: string, ...args: any[]) =>\n console.info(`[Klime] ${message}`, ...args),\n warn: (message: string, ...args: any[]) =>\n console.warn(`[Klime] ${message}`, ...args),\n error: (message: string, ...args: any[]) =>\n console.error(`[Klime] ${message}`, ...args),\n});\n\n// Internal config type with required fields and optional callbacks/logger\ninterface InternalConfig {\n writeKey: string;\n endpoint: string;\n flushInterval: number;\n maxBatchSize: number;\n maxQueueSize: number;\n retryMaxAttempts: number;\n retryInitialDelay: number;\n autoFlushOnUnload: boolean;\n logger: Logger;\n onError?: (error: Error, events: Event[]) => void;\n onSuccess?: (response: BatchResponse) => void;\n}\n\nexport class KlimeClient {\n private config: InternalConfig;\n private queue: Event[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private isShutdown = false;\n private flushPromise: Promise<void> | null = null;\n private unloadHandler: (() => void) | null = null;\n private visibilityChangeHandler: (() => void) | null = null;\n\n constructor(config: KlimeConfig) {\n if (!config.writeKey) {\n throw new Error(\"writeKey is required\");\n }\n\n this.config = {\n writeKey: config.writeKey,\n endpoint: config.endpoint || DEFAULT_ENDPOINT,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: Math.min(\n config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n MAX_BATCH_SIZE\n ),\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay:\n config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,\n autoFlushOnUnload: config.autoFlushOnUnload ?? true,\n logger: config.logger ?? createDefaultLogger(),\n onError: config.onError,\n onSuccess: config.onSuccess,\n };\n\n if (this.config.autoFlushOnUnload && typeof window !== \"undefined\") {\n this.unloadHandler = () => {\n this.flush();\n };\n window.addEventListener(\"beforeunload\", this.unloadHandler);\n window.addEventListener(\"pagehide\", this.unloadHandler);\n this.visibilityChangeHandler = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n this.flush();\n }\n };\n document.addEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n this.scheduleFlush();\n }\n\n track(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n this.enqueue(eventObj);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n this.enqueue(eventObj);\n }\n\n group(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n this.enqueue(eventObj);\n }\n\n /**\n * Track an event synchronously. Returns BatchResponse or throws SendError.\n */\n async trackSync(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Identify a user synchronously. Returns BatchResponse or throws SendError.\n */\n async identifySync(\n userId: string,\n traits?: Record<string, any>\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Associate a user with a group synchronously. Returns BatchResponse or throws SendError.\n */\n async groupSync(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Return the number of events currently in the queue.\n */\n getQueueSize(): number {\n return this.queue.length;\n }\n\n async flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n\n this.flushPromise = this.doFlush();\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n this.isShutdown = true;\n\n if (this.unloadHandler && typeof window !== \"undefined\") {\n window.removeEventListener(\"beforeunload\", this.unloadHandler);\n window.removeEventListener(\"pagehide\", this.unloadHandler);\n }\n if (this.visibilityChangeHandler && typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n await this.flush();\n }\n\n private enqueue(event: Event): void {\n // Check event size\n const eventSize = this.estimateEventSize(event);\n if (eventSize > MAX_EVENT_SIZE_BYTES) {\n this.config.logger.warn(\n `Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`\n );\n return;\n }\n\n // Drop oldest if queue is full\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.logger.warn(`Queue full, dropping oldest event: ${dropped?.type}`);\n }\n\n this.queue.push(event);\n this.config.logger.debug(`Enqueued ${event.type} event, queue size: ${this.queue.length}`);\n\n // Check if we should flush immediately\n if (this.queue.length >= this.config.maxBatchSize) {\n this.flush();\n }\n }\n\n private async doFlush(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n // Clear the flush timer\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Process batches\n while (this.queue.length > 0) {\n const batch = this.extractBatch();\n if (batch.length === 0) {\n break;\n }\n\n await this.sendBatch(batch);\n }\n\n // Schedule next flush\n this.scheduleFlush();\n }\n\n private extractBatch(): Event[] {\n const batch: Event[] = [];\n let batchSize = 0;\n\n while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {\n const event = this.queue[0];\n const eventSize = this.estimateEventSize(event);\n\n // Check if adding this event would exceed batch size limit\n if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {\n break;\n }\n\n batch.push(this.queue.shift()!);\n batchSize += eventSize;\n }\n\n return batch;\n }\n\n private async sendBatch(batch: Event[]): Promise<void> {\n if (batch.length === 0) {\n return;\n }\n\n this.config.logger.debug(`Sending batch of ${batch.length} events`);\n const result = await this.doSend(batch);\n\n if (result === null) {\n // Send failed after all retries\n const error = new Error(`Failed to send batch of ${batch.length} events after retries`);\n this.invokeOnError(error, batch);\n } else if (result.failed > 0 && result.errors) {\n this.config.logger.warn(\n `Batch partially failed. Accepted: ${result.accepted}, Failed: ${result.failed}`,\n result.errors\n );\n // Still invoke success callback since some events were accepted\n this.invokeOnSuccess(result);\n } else {\n this.config.logger.debug(`Batch sent successfully. Accepted: ${result.accepted}`);\n this.invokeOnSuccess(result);\n }\n }\n\n private async sendSync(events: Event[]): Promise<BatchResponse> {\n this.config.logger.debug(`Sending ${events.length} events synchronously`);\n const result = await this.doSend(events);\n\n if (result === null) {\n throw new SendError(`Failed to send ${events.length} events after retries`, events);\n }\n\n return result;\n }\n\n private async doSend(batch: Event[]): Promise<BatchResponse | null> {\n const request: BatchRequest = { batch };\n const requestUrl = `${this.config.endpoint}/v1/batch`;\n const body = JSON.stringify(request);\n const useKeepalive = body.length <= KEEPALIVE_MAX_BODY_BYTES;\n\n let attempt = 0;\n let delay = this.config.retryInitialDelay;\n\n while (attempt < this.config.retryMaxAttempts) {\n try {\n const response = await fetch(requestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body,\n keepalive: useKeepalive,\n });\n\n const data: BatchResponse = await response.json();\n\n if (response.ok) {\n return data;\n }\n\n // Handle error responses\n if (response.status === 400 || response.status === 401) {\n // Permanent errors - don't retry\n this.config.logger.error(`Permanent error (${response.status}):`, data);\n return null;\n }\n\n // Transient errors - retry with backoff\n if (response.status === 429 || response.status === 503) {\n const retryAfter = response.headers.get(\"Retry-After\");\n if (retryAfter) {\n delay = parseInt(retryAfter, 10) * 1000;\n }\n\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000); // Cap at 16s\n continue;\n }\n }\n\n // Other errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n }\n } catch (error) {\n // Network errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n } else {\n this.config.logger.error(\"Failed to send batch after retries:\", error);\n }\n }\n }\n\n return null;\n }\n\n private invokeOnError(error: Error, batch: Event[]): void {\n if (this.config.onError) {\n try {\n this.config.onError(error, batch);\n } catch (e) {\n this.config.logger.error(\"Error in onError callback:\", e);\n }\n }\n }\n\n private invokeOnSuccess(response: BatchResponse): void {\n if (this.config.onSuccess) {\n try {\n this.config.onSuccess(response);\n } catch (e) {\n this.config.logger.error(\"Error in onSuccess callback:\", e);\n }\n }\n }\n\n private scheduleFlush(): void {\n if (this.isShutdown || this.flushTimer) {\n return;\n }\n\n this.flushTimer = setTimeout(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private generateUUID(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\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\n private generateTimestamp(): string {\n return new Date().toISOString();\n }\n\n private getContext(): EventContext {\n const context: EventContext = {\n library: {\n name: \"js-sdk\",\n version: SDK_VERSION,\n },\n };\n\n if (typeof navigator !== \"undefined\") {\n if (navigator.userAgent) {\n context.userAgent = navigator.userAgent;\n }\n if (navigator.language) {\n context.locale = navigator.language;\n }\n }\n\n if (typeof Intl !== \"undefined\" && Intl.DateTimeFormat) {\n try {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n if (timezone) {\n context.timezone = timezone;\n }\n } catch (e) {\n // Ignore timezone errors\n }\n }\n\n return context;\n }\n\n private estimateEventSize(event: Event): number {\n // Rough estimate: JSON stringified size\n try {\n return JSON.stringify(event).length;\n } catch {\n // Fallback: rough estimate based on structure\n return 500; // Conservative estimate\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":";AAiEO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAGnC,YAAY,SAAiB,QAAiB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;;;AC1DA,IAAM,mBAAmB;AACzB,IAAM,yBAAyB;AAC/B,IAAM,yBAAyB;AAC/B,IAAM,yBAAyB;AAC/B,IAAM,6BAA6B;AACnC,IAAM,8BAA8B;AACpC,IAAM,iBAAiB;AACvB,IAAM,uBAAuB,MAAM;AACnC,IAAM,uBAAuB,KAAK,OAAO;AAEzC,IAAM,2BAA2B,KAAK;AACtC,IAAM,cAAc;AAGpB,IAAM,sBAAsB,OAAe;AAAA,EACzC,OAAO,CAAC,YAAoB,SAC1B,QAAQ,MAAM,WAAW,OAAO,IAAI,GAAG,IAAI;AAAA,EAC7C,MAAM,CAAC,YAAoB,SACzB,QAAQ,KAAK,WAAW,OAAO,IAAI,GAAG,IAAI;AAAA,EAC5C,MAAM,CAAC,YAAoB,SACzB,QAAQ,KAAK,WAAW,OAAO,IAAI,GAAG,IAAI;AAAA,EAC5C,OAAO,CAAC,YAAoB,SAC1B,QAAQ,MAAM,WAAW,OAAO,IAAI,GAAG,IAAI;AAC/C;AAiBO,IAAM,cAAN,MAAkB;AAAA,EASvB,YAAY,QAAqB;AAPjC,SAAQ,QAAiB,CAAC;AAC1B,SAAQ,aAAmD;AAC3D,SAAQ,aAAa;AACrB,SAAQ,eAAqC;AAC7C,SAAQ,gBAAqC;AAC7C,SAAQ,0BAA+C;AAGrD,QAAI,CAAC,OAAO,UAAU;AACpB,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,SAAK,SAAS;AAAA,MACZ,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO,YAAY;AAAA,MAC7B,eAAe,OAAO,iBAAiB;AAAA,MACvC,cAAc,KAAK;AAAA,QACjB,OAAO,gBAAgB;AAAA,QACvB;AAAA,MACF;AAAA,MACA,cAAc,OAAO,gBAAgB;AAAA,MACrC,kBAAkB,OAAO,oBAAoB;AAAA,MAC7C,mBACE,OAAO,qBAAqB;AAAA,MAC9B,mBAAmB,OAAO,qBAAqB;AAAA,MAC/C,QAAQ,OAAO,UAAU,oBAAoB;AAAA,MAC7C,SAAS,OAAO;AAAA,MAChB,WAAW,OAAO;AAAA,IACpB;AAEA,QAAI,KAAK,OAAO,qBAAqB,OAAO,WAAW,aAAa;AAClE,WAAK,gBAAgB,MAAM;AACzB,aAAK,MAAM;AAAA,MACb;AACA,aAAO,iBAAiB,gBAAgB,KAAK,aAAa;AAC1D,aAAO,iBAAiB,YAAY,KAAK,aAAa;AACtD,WAAK,0BAA0B,MAAM;AACnC,YAAI,OAAO,aAAa,eAAe,SAAS,oBAAoB,UAAU;AAC5E,eAAK,MAAM;AAAA,QACb;AAAA,MACF;AACA,eAAS,iBAAiB,oBAAoB,KAAK,uBAAuB;AAAA,IAC5E;AAEA,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MACE,OACA,YACA,SACM;AACN,QAAI,KAAK,YAAY;AACnB;AAAA,IACF;AAEA,UAAM,WAAkB;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,aAAa;AAAA,MAC7B;AAAA,MACA,WAAW,KAAK,kBAAkB;AAAA,MAClC,YAAY,cAAc,CAAC;AAAA,MAC3B,SAAS,KAAK,WAAW;AAAA,IAC3B;AAEA,QAAI,SAAS,QAAQ;AACnB,eAAS,SAAS,QAAQ;AAAA,IAC5B;AACA,QAAI,SAAS,SAAS;AACpB,eAAS,UAAU,QAAQ;AAAA,IAC7B;AAEA,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,SAAS,QAAgB,QAAoC;AAC3D,QAAI,KAAK,YAAY;AACnB;AAAA,IACF;AAEA,UAAM,WAAkB;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,aAAa;AAAA,MAC7B;AAAA,MACA,WAAW,KAAK,kBAAkB;AAAA,MAClC,QAAQ,UAAU,CAAC;AAAA,MACnB,SAAS,KAAK,WAAW;AAAA,IAC3B;AAEA,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,MACE,SACA,QACA,SACM;AACN,QAAI,KAAK,YAAY;AACnB;AAAA,IACF;AAEA,UAAM,WAAkB;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,aAAa;AAAA,MAC7B;AAAA,MACA,WAAW,KAAK,kBAAkB;AAAA,MAClC,QAAQ,UAAU,CAAC;AAAA,MACnB,SAAS,KAAK,WAAW;AAAA,IAC3B;AAEA,QAAI,SAAS,QAAQ;AACnB,eAAS,SAAS,QAAQ;AAAA,IAC5B;AAEA,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UACJ,OACA,YACA,SACwB;AACxB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI,UAAU,sBAAsB,CAAC,CAAC;AAAA,IAC9C;AAEA,UAAM,WAAkB;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,aAAa;AAAA,MAC7B;AAAA,MACA,WAAW,KAAK,kBAAkB;AAAA,MAClC,YAAY,cAAc,CAAC;AAAA,MAC3B,SAAS,KAAK,WAAW;AAAA,IAC3B;AAEA,QAAI,SAAS,QAAQ;AACnB,eAAS,SAAS,QAAQ;AAAA,IAC5B;AACA,QAAI,SAAS,SAAS;AACpB,eAAS,UAAU,QAAQ;AAAA,IAC7B;AAEA,WAAO,KAAK,SAAS,CAAC,QAAQ,CAAC;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,QACA,QACwB;AACxB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI,UAAU,sBAAsB,CAAC,CAAC;AAAA,IAC9C;AAEA,UAAM,WAAkB;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,aAAa;AAAA,MAC7B;AAAA,MACA,WAAW,KAAK,kBAAkB;AAAA,MAClC,QAAQ,UAAU,CAAC;AAAA,MACnB,SAAS,KAAK,WAAW;AAAA,IAC3B;AAEA,WAAO,KAAK,SAAS,CAAC,QAAQ,CAAC;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UACJ,SACA,QACA,SACwB;AACxB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI,UAAU,sBAAsB,CAAC,CAAC;AAAA,IAC9C;AAEA,UAAM,WAAkB;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,aAAa;AAAA,MAC7B;AAAA,MACA,WAAW,KAAK,kBAAkB;AAAA,MAClC,QAAQ,UAAU,CAAC;AAAA,MACnB,SAAS,KAAK,WAAW;AAAA,IAC3B;AAEA,QAAI,SAAS,QAAQ;AACnB,eAAS,SAAS,QAAQ;AAAA,IAC5B;AAEA,WAAO,KAAK,SAAS,CAAC,QAAQ,CAAC;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,eAAuB;AACrB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,cAAc;AACrB,aAAO,KAAK;AAAA,IACd;AAEA,SAAK,eAAe,KAAK,QAAQ;AACjC,QAAI;AACF,YAAM,KAAK;AAAA,IACb,UAAE;AACA,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,KAAK,YAAY;AACnB;AAAA,IACF;AAEA,SAAK,aAAa;AAElB,QAAI,KAAK,iBAAiB,OAAO,WAAW,aAAa;AACvD,aAAO,oBAAoB,gBAAgB,KAAK,aAAa;AAC7D,aAAO,oBAAoB,YAAY,KAAK,aAAa;AAAA,IAC3D;AACA,QAAI,KAAK,2BAA2B,OAAO,aAAa,aAAa;AACnE,eAAS,oBAAoB,oBAAoB,KAAK,uBAAuB;AAAA,IAC/E;AAEA,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAAA,IACpB;AAEA,UAAM,KAAK,MAAM;AAAA,EACnB;AAAA,EAEQ,QAAQ,OAAoB;AAElC,UAAM,YAAY,KAAK,kBAAkB,KAAK;AAC9C,QAAI,YAAY,sBAAsB;AACpC,WAAK,OAAO,OAAO;AAAA,QACjB,eAAe,SAAS,mBAAmB,oBAAoB;AAAA,MACjE;AACA;AAAA,IACF;AAGA,QAAI,KAAK,MAAM,UAAU,KAAK,OAAO,cAAc;AACjD,YAAM,UAAU,KAAK,MAAM,MAAM;AACjC,WAAK,OAAO,OAAO,KAAK,sCAAsC,SAAS,IAAI,EAAE;AAAA,IAC/E;AAEA,SAAK,MAAM,KAAK,KAAK;AACrB,SAAK,OAAO,OAAO,MAAM,YAAY,MAAM,IAAI,uBAAuB,KAAK,MAAM,MAAM,EAAE;AAGzF,QAAI,KAAK,MAAM,UAAU,KAAK,OAAO,cAAc;AACjD,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA,EAEA,MAAc,UAAyB;AACrC,QAAI,KAAK,MAAM,WAAW,GAAG;AAC3B;AAAA,IACF;AAGA,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAAA,IACpB;AAGA,WAAO,KAAK,MAAM,SAAS,GAAG;AAC5B,YAAM,QAAQ,KAAK,aAAa;AAChC,UAAI,MAAM,WAAW,GAAG;AACtB;AAAA,MACF;AAEA,YAAM,KAAK,UAAU,KAAK;AAAA,IAC5B;AAGA,SAAK,cAAc;AAAA,EACrB;AAAA,EAEQ,eAAwB;AAC9B,UAAM,QAAiB,CAAC;AACxB,QAAI,YAAY;AAEhB,WAAO,KAAK,MAAM,SAAS,KAAK,MAAM,SAAS,gBAAgB;AAC7D,YAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,YAAM,YAAY,KAAK,kBAAkB,KAAK;AAG9C,UAAI,YAAY,YAAY,sBAAsB;AAChD;AAAA,MACF;AAEA,YAAM,KAAK,KAAK,MAAM,MAAM,CAAE;AAC9B,mBAAa;AAAA,IACf;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,UAAU,OAA+B;AACrD,QAAI,MAAM,WAAW,GAAG;AACtB;AAAA,IACF;AAEA,SAAK,OAAO,OAAO,MAAM,oBAAoB,MAAM,MAAM,SAAS;AAClE,UAAM,SAAS,MAAM,KAAK,OAAO,KAAK;AAEtC,QAAI,WAAW,MAAM;AAEnB,YAAM,QAAQ,IAAI,MAAM,2BAA2B,MAAM,MAAM,uBAAuB;AACtF,WAAK,cAAc,OAAO,KAAK;AAAA,IACjC,WAAW,OAAO,SAAS,KAAK,OAAO,QAAQ;AAC7C,WAAK,OAAO,OAAO;AAAA,QACjB,qCAAqC,OAAO,QAAQ,aAAa,OAAO,MAAM;AAAA,QAC9E,OAAO;AAAA,MACT;AAEA,WAAK,gBAAgB,MAAM;AAAA,IAC7B,OAAO;AACL,WAAK,OAAO,OAAO,MAAM,sCAAsC,OAAO,QAAQ,EAAE;AAChF,WAAK,gBAAgB,MAAM;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAc,SAAS,QAAyC;AAC9D,SAAK,OAAO,OAAO,MAAM,WAAW,OAAO,MAAM,uBAAuB;AACxE,UAAM,SAAS,MAAM,KAAK,OAAO,MAAM;AAEvC,QAAI,WAAW,MAAM;AACnB,YAAM,IAAI,UAAU,kBAAkB,OAAO,MAAM,yBAAyB,MAAM;AAAA,IACpF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,OAAO,OAA+C;AAClE,UAAM,UAAwB,EAAE,MAAM;AACtC,UAAM,aAAa,GAAG,KAAK,OAAO,QAAQ;AAC1C,UAAM,OAAO,KAAK,UAAU,OAAO;AACnC,UAAM,eAAe,KAAK,UAAU;AAEpC,QAAI,UAAU;AACd,QAAI,QAAQ,KAAK,OAAO;AAExB,WAAO,UAAU,KAAK,OAAO,kBAAkB;AAC7C,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,YAAY;AAAA,UACvC,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,eAAe,UAAU,KAAK,OAAO,QAAQ;AAAA,UAC/C;AAAA,UACA;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAED,cAAM,OAAsB,MAAM,SAAS,KAAK;AAEhD,YAAI,SAAS,IAAI;AACf,iBAAO;AAAA,QACT;AAGA,YAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AAEtD,eAAK,OAAO,OAAO,MAAM,oBAAoB,SAAS,MAAM,MAAM,IAAI;AACtE,iBAAO;AAAA,QACT;AAGA,YAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,gBAAM,aAAa,SAAS,QAAQ,IAAI,aAAa;AACrD,cAAI,YAAY;AACd,oBAAQ,SAAS,YAAY,EAAE,IAAI;AAAA,UACrC;AAEA;AACA,cAAI,UAAU,KAAK,OAAO,kBAAkB;AAC1C,iBAAK,OAAO,OAAO,MAAM,kBAAkB,KAAK,eAAe,OAAO,GAAG;AACzE,kBAAM,KAAK,MAAM,KAAK;AACtB,oBAAQ,KAAK,IAAI,QAAQ,GAAG,IAAK;AACjC;AAAA,UACF;AAAA,QACF;AAGA;AACA,YAAI,UAAU,KAAK,OAAO,kBAAkB;AAC1C,eAAK,OAAO,OAAO,MAAM,kBAAkB,KAAK,eAAe,OAAO,GAAG;AACzE,gBAAM,KAAK,MAAM,KAAK;AACtB,kBAAQ,KAAK,IAAI,QAAQ,GAAG,IAAK;AAAA,QACnC;AAAA,MACF,SAAS,OAAO;AAEd;AACA,YAAI,UAAU,KAAK,OAAO,kBAAkB;AAC1C,eAAK,OAAO,OAAO,MAAM,kBAAkB,KAAK,eAAe,OAAO,GAAG;AACzE,gBAAM,KAAK,MAAM,KAAK;AACtB,kBAAQ,KAAK,IAAI,QAAQ,GAAG,IAAK;AAAA,QACnC,OAAO;AACL,eAAK,OAAO,OAAO,MAAM,uCAAuC,KAAK;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,OAAc,OAAsB;AACxD,QAAI,KAAK,OAAO,SAAS;AACvB,UAAI;AACF,aAAK,OAAO,QAAQ,OAAO,KAAK;AAAA,MAClC,SAAS,GAAG;AACV,aAAK,OAAO,OAAO,MAAM,8BAA8B,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB,UAA+B;AACrD,QAAI,KAAK,OAAO,WAAW;AACzB,UAAI;AACF,aAAK,OAAO,UAAU,QAAQ;AAAA,MAChC,SAAS,GAAG;AACV,aAAK,OAAO,OAAO,MAAM,gCAAgC,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,cAAc,KAAK,YAAY;AACtC;AAAA,IACF;AAEA,SAAK,aAAa,WAAW,MAAM;AACjC,WAAK,MAAM;AAAA,IACb,GAAG,KAAK,OAAO,aAAa;AAAA,EAC9B;AAAA,EAEQ,eAAuB;AAC7B,QAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,aAAO,OAAO,WAAW;AAAA,IAC3B;AAEA,WAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACpE,YAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,YAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,aAAO,EAAE,SAAS,EAAE;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEQ,oBAA4B;AAClC,YAAO,oBAAI,KAAK,GAAE,YAAY;AAAA,EAChC;AAAA,EAEQ,aAA2B;AACjC,UAAM,UAAwB;AAAA,MAC5B,SAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAEA,QAAI,OAAO,cAAc,aAAa;AACpC,UAAI,UAAU,WAAW;AACvB,gBAAQ,YAAY,UAAU;AAAA,MAChC;AACA,UAAI,UAAU,UAAU;AACtB,gBAAQ,SAAS,UAAU;AAAA,MAC7B;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,eAAe,KAAK,gBAAgB;AACtD,UAAI;AACF,cAAM,WAAW,KAAK,eAAe,EAAE,gBAAgB,EAAE;AACzD,YAAI,UAAU;AACZ,kBAAQ,WAAW;AAAA,QACrB;AAAA,MACF,SAAS,GAAG;AAAA,MAEZ;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,OAAsB;AAE9C,QAAI;AACF,aAAO,KAAK,UAAU,KAAK,EAAE;AAAA,IAC/B,QAAQ;AAEN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,MAAM,IAA2B;AACvC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":["export interface Logger {\n debug(message: string, ...args: any[]): void;\n info(message: string, ...args: any[]): void;\n warn(message: string, ...args: any[]): void;\n error(message: string, ...args: any[]): void;\n}\n\nexport interface KlimeConfig {\n writeKey: string;\n endpoint?: string;\n flushInterval?: number; // milliseconds, default 2000\n maxBatchSize?: number; // default 20, max 100\n maxQueueSize?: number; // default 1000\n retryMaxAttempts?: number; // default 5\n retryInitialDelay?: number; // milliseconds, default 1000\n autoFlushOnUnload?: boolean; // default true\n logger?: Logger; // optional custom logger\n onError?: (error: Error, events: Event[]) => void; // callback for batch failures\n onSuccess?: (response: BatchResponse) => void; // callback for successful sends\n}\n\nexport interface TrackOptions {\n userId?: string;\n groupId?: string;\n}\n\nexport interface Event {\n type: 'track' | 'identify' | 'group';\n messageId: string;\n event?: string; // required for track\n userId?: string; // required for identify\n groupId?: string; // required for group\n timestamp: string; // ISO 8601\n properties?: Record<string, any>; // for track\n traits?: Record<string, any>; // for identify/group\n context?: EventContext;\n}\n\nexport interface EventContext {\n library?: {\n name: string;\n version: string;\n };\n userAgent?: string;\n locale?: string;\n timezone?: string;\n}\n\nexport interface BatchRequest {\n batch: Event[];\n}\n\nexport interface BatchResponse {\n status: string;\n accepted: number;\n failed: number;\n errors?: ValidationError[];\n}\n\nexport interface ValidationError {\n index: number;\n message: string;\n code: string;\n}\n\nexport class SendError extends Error {\n events: Event[];\n\n constructor(message: string, events: Event[]) {\n super(message);\n this.name = 'SendError';\n this.events = events;\n }\n}\n","import {\n KlimeConfig,\n TrackOptions,\n Event,\n BatchRequest,\n BatchResponse,\n EventContext,\n Logger,\n SendError,\n} from \"./types\";\n\n// Re-export types for users\nexport { SendError, BatchResponse, Logger } from \"./types\";\nexport type { KlimeConfig, TrackOptions, Event } from \"./types\";\n\nconst DEFAULT_ENDPOINT = \"https://i.klime.com\";\nconst DEFAULT_FLUSH_INTERVAL = 2000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_QUEUE_SIZE = 1000;\nconst DEFAULT_RETRY_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_INITIAL_DELAY = 1000;\nconst MAX_BATCH_SIZE = 100;\nconst MAX_EVENT_SIZE_BYTES = 200 * 1024; // 200KB\nconst MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024; // 10MB\n/** Max body size for fetch keepalive (spec limit). Requests larger than this omit keepalive. */\nconst KEEPALIVE_MAX_BODY_BYTES = 64 * 1024; // 64KB\nconst SDK_VERSION = \"1.1.0\";\n\n// Default logger that wraps console with [Klime] prefix\nconst createDefaultLogger = (): Logger => ({\n debug: (message: string, ...args: any[]) =>\n console.debug(`[Klime] ${message}`, ...args),\n info: (message: string, ...args: any[]) =>\n console.info(`[Klime] ${message}`, ...args),\n warn: (message: string, ...args: any[]) =>\n console.warn(`[Klime] ${message}`, ...args),\n error: (message: string, ...args: any[]) =>\n console.error(`[Klime] ${message}`, ...args),\n});\n\n// Internal config type with required fields and optional callbacks/logger\ninterface InternalConfig {\n writeKey: string;\n endpoint: string;\n flushInterval: number;\n maxBatchSize: number;\n maxQueueSize: number;\n retryMaxAttempts: number;\n retryInitialDelay: number;\n autoFlushOnUnload: boolean;\n logger: Logger;\n onError?: (error: Error, events: Event[]) => void;\n onSuccess?: (response: BatchResponse) => void;\n}\n\n/**\n * Factory function for environments where `new` is unavailable (e.g. GTM sandboxed JS).\n * Creates a KlimeClient and registers bound methods as standalone globals so\n * GTM's `callInWindow` can invoke them without `this` context issues.\n */\nexport function init(config: KlimeConfig): KlimeClient {\n if (\n typeof window !== \"undefined\" &&\n (window as any).__klime &&\n (window as any).__klime._writeKey === config.writeKey\n ) {\n return (window as any).__klime;\n }\n\n const client = new KlimeClient(config);\n if (typeof window !== \"undefined\") {\n (window as any).__klime = client;\n (window as any).__klime_track = client.track.bind(client);\n (window as any).__klime_identify = client.identify.bind(client);\n (window as any).__klime_group = client.group.bind(client);\n }\n return client;\n}\n\nexport class KlimeClient {\n /** Exposed for idempotent re-initialization (used by GTM `init()`). */\n readonly _writeKey: string;\n private config: InternalConfig;\n private queue: Event[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private isShutdown = false;\n private flushPromise: Promise<void> | null = null;\n private unloadHandler: (() => void) | null = null;\n private visibilityChangeHandler: (() => void) | null = null;\n\n constructor(config: KlimeConfig) {\n if (!config.writeKey) {\n throw new Error(\"writeKey is required\");\n }\n\n this._writeKey = config.writeKey;\n this.config = {\n writeKey: config.writeKey,\n endpoint: config.endpoint || DEFAULT_ENDPOINT,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: Math.min(\n config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n MAX_BATCH_SIZE\n ),\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay:\n config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,\n autoFlushOnUnload: config.autoFlushOnUnload ?? true,\n logger: config.logger ?? createDefaultLogger(),\n onError: config.onError,\n onSuccess: config.onSuccess,\n };\n\n if (this.config.autoFlushOnUnload && typeof window !== \"undefined\") {\n this.unloadHandler = () => {\n this.flush();\n };\n window.addEventListener(\"beforeunload\", this.unloadHandler);\n window.addEventListener(\"pagehide\", this.unloadHandler);\n this.visibilityChangeHandler = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n this.flush();\n }\n };\n document.addEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n this.scheduleFlush();\n }\n\n track(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n this.enqueue(eventObj);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n this.enqueue(eventObj);\n }\n\n group(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n this.enqueue(eventObj);\n }\n\n /**\n * Track an event synchronously. Returns BatchResponse or throws SendError.\n */\n async trackSync(\n event: string,\n properties?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"track\",\n messageId: this.generateUUID(),\n event,\n timestamp: this.generateTimestamp(),\n properties: properties || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n if (options?.groupId) {\n eventObj.groupId = options.groupId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Identify a user synchronously. Returns BatchResponse or throws SendError.\n */\n async identifySync(\n userId: string,\n traits?: Record<string, any>\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"identify\",\n messageId: this.generateUUID(),\n userId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Associate a user with a group synchronously. Returns BatchResponse or throws SendError.\n */\n async groupSync(\n groupId: string,\n traits?: Record<string, any>,\n options?: TrackOptions\n ): Promise<BatchResponse> {\n if (this.isShutdown) {\n throw new SendError(\"Client is shutdown\", []);\n }\n\n const eventObj: Event = {\n type: \"group\",\n messageId: this.generateUUID(),\n groupId,\n timestamp: this.generateTimestamp(),\n traits: traits || {},\n context: this.getContext(),\n };\n\n if (options?.userId) {\n eventObj.userId = options.userId;\n }\n\n return this.sendSync([eventObj]);\n }\n\n /**\n * Return the number of events currently in the queue.\n */\n getQueueSize(): number {\n return this.queue.length;\n }\n\n async flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n\n this.flushPromise = this.doFlush();\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n this.isShutdown = true;\n\n if (this.unloadHandler && typeof window !== \"undefined\") {\n window.removeEventListener(\"beforeunload\", this.unloadHandler);\n window.removeEventListener(\"pagehide\", this.unloadHandler);\n }\n if (this.visibilityChangeHandler && typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", this.visibilityChangeHandler);\n }\n\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n await this.flush();\n }\n\n private enqueue(event: Event): void {\n // Check event size\n const eventSize = this.estimateEventSize(event);\n if (eventSize > MAX_EVENT_SIZE_BYTES) {\n this.config.logger.warn(\n `Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`\n );\n return;\n }\n\n // Drop oldest if queue is full\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.logger.warn(`Queue full, dropping oldest event: ${dropped?.type}`);\n }\n\n this.queue.push(event);\n this.config.logger.debug(`Enqueued ${event.type} event, queue size: ${this.queue.length}`);\n\n // Check if we should flush immediately\n if (this.queue.length >= this.config.maxBatchSize) {\n this.flush();\n }\n }\n\n private async doFlush(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n // Clear the flush timer\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Process batches\n while (this.queue.length > 0) {\n const batch = this.extractBatch();\n if (batch.length === 0) {\n break;\n }\n\n await this.sendBatch(batch);\n }\n\n // Schedule next flush\n this.scheduleFlush();\n }\n\n private extractBatch(): Event[] {\n const batch: Event[] = [];\n let batchSize = 0;\n\n while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {\n const event = this.queue[0];\n const eventSize = this.estimateEventSize(event);\n\n // Check if adding this event would exceed batch size limit\n if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {\n break;\n }\n\n batch.push(this.queue.shift()!);\n batchSize += eventSize;\n }\n\n return batch;\n }\n\n private async sendBatch(batch: Event[]): Promise<void> {\n if (batch.length === 0) {\n return;\n }\n\n this.config.logger.debug(`Sending batch of ${batch.length} events`);\n const result = await this.doSend(batch);\n\n if (result === null) {\n // Send failed after all retries\n const error = new Error(`Failed to send batch of ${batch.length} events after retries`);\n this.invokeOnError(error, batch);\n } else if (result.failed > 0 && result.errors) {\n this.config.logger.warn(\n `Batch partially failed. Accepted: ${result.accepted}, Failed: ${result.failed}`,\n result.errors\n );\n // Still invoke success callback since some events were accepted\n this.invokeOnSuccess(result);\n } else {\n this.config.logger.debug(`Batch sent successfully. Accepted: ${result.accepted}`);\n this.invokeOnSuccess(result);\n }\n }\n\n private async sendSync(events: Event[]): Promise<BatchResponse> {\n this.config.logger.debug(`Sending ${events.length} events synchronously`);\n const result = await this.doSend(events);\n\n if (result === null) {\n throw new SendError(`Failed to send ${events.length} events after retries`, events);\n }\n\n return result;\n }\n\n private async doSend(batch: Event[]): Promise<BatchResponse | null> {\n const request: BatchRequest = { batch };\n const requestUrl = `${this.config.endpoint}/v1/batch`;\n const body = JSON.stringify(request);\n const useKeepalive = body.length <= KEEPALIVE_MAX_BODY_BYTES;\n\n let attempt = 0;\n let delay = this.config.retryInitialDelay;\n\n while (attempt < this.config.retryMaxAttempts) {\n try {\n const response = await fetch(requestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body,\n keepalive: useKeepalive,\n });\n\n const data: BatchResponse = await response.json();\n\n if (response.ok) {\n return data;\n }\n\n // Handle error responses\n if (response.status === 400 || response.status === 401) {\n // Permanent errors - don't retry\n this.config.logger.error(`Permanent error (${response.status}):`, data);\n return null;\n }\n\n // Transient errors - retry with backoff\n if (response.status === 429 || response.status === 503) {\n const retryAfter = response.headers.get(\"Retry-After\");\n if (retryAfter) {\n delay = parseInt(retryAfter, 10) * 1000;\n }\n\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000); // Cap at 16s\n continue;\n }\n }\n\n // Other errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n }\n } catch (error) {\n // Network errors - retry\n attempt++;\n if (attempt < this.config.retryMaxAttempts) {\n this.config.logger.debug(`Retrying after ${delay}ms (attempt ${attempt})`);\n await this.sleep(delay);\n delay = Math.min(delay * 2, 16000);\n } else {\n this.config.logger.error(\"Failed to send batch after retries:\", error);\n }\n }\n }\n\n return null;\n }\n\n private invokeOnError(error: Error, batch: Event[]): void {\n if (this.config.onError) {\n try {\n this.config.onError(error, batch);\n } catch (e) {\n this.config.logger.error(\"Error in onError callback:\", e);\n }\n }\n }\n\n private invokeOnSuccess(response: BatchResponse): void {\n if (this.config.onSuccess) {\n try {\n this.config.onSuccess(response);\n } catch (e) {\n this.config.logger.error(\"Error in onSuccess callback:\", e);\n }\n }\n }\n\n private scheduleFlush(): void {\n if (this.isShutdown || this.flushTimer) {\n return;\n }\n\n this.flushTimer = setTimeout(() => {\n this.flush();\n }, this.config.flushInterval);\n }\n\n private generateUUID(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\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\n private generateTimestamp(): string {\n return new Date().toISOString();\n }\n\n private getContext(): EventContext {\n const context: EventContext = {\n library: {\n name: \"js-sdk\",\n version: SDK_VERSION,\n },\n };\n\n if (typeof navigator !== \"undefined\") {\n if (navigator.userAgent) {\n context.userAgent = navigator.userAgent;\n }\n if (navigator.language) {\n context.locale = navigator.language;\n }\n }\n\n if (typeof Intl !== \"undefined\" && Intl.DateTimeFormat) {\n try {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n if (timezone) {\n context.timezone = timezone;\n }\n } catch (e) {\n // Ignore timezone errors\n }\n }\n\n return context;\n }\n\n private estimateEventSize(event: Event): number {\n // Rough estimate: JSON stringified size\n try {\n return JSON.stringify(event).length;\n } catch {\n // Fallback: rough estimate based on structure\n return 500; // Conservative estimate\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":"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","names":[]}
|