@nemme/js-sdk 1.0.0 → 1.1.1

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +25 -3
  3. package/dist/chunks/{chunk-CZZ4p3Yf.js → chunk-DWNI8pZO.js} +1 -1
  4. package/dist/chunks/{client-D6k2IoC6.js → client-CKvtSbpa.js} +13 -7
  5. package/dist/chunks/{client-D6k2IoC6.js.map → client-CKvtSbpa.js.map} +1 -1
  6. package/dist/chunks/{client-BYPXJmEU.js → client-Db2ZE5y9.js} +2 -2
  7. package/dist/chunks/{client-BYPXJmEU.js.map → client-Db2ZE5y9.js.map} +1 -1
  8. package/dist/chunks/{config-C6VpkZGH.js → config-DCrsd6gZ.js} +1 -1
  9. package/dist/chunks/{config-C6VpkZGH.js.map → config-DCrsd6gZ.js.map} +1 -1
  10. package/dist/chunks/{config-BnABfG_n.js → config-hiNBGctX.js} +2 -2
  11. package/dist/chunks/{config-BnABfG_n.js.map → config-hiNBGctX.js.map} +1 -1
  12. package/dist/chunks/form-manager-BG1qr31N.js +12 -0
  13. package/dist/chunks/form-manager-BG1qr31N.js.map +1 -0
  14. package/dist/chunks/{form-manager-BqtPAs-A.js → form-manager-Dit5tdsl.js} +112 -104
  15. package/dist/chunks/form-manager-Dit5tdsl.js.map +1 -0
  16. package/dist/client.d.ts +6 -1
  17. package/dist/forms.cjs +1 -1
  18. package/dist/forms.esm.js +1 -1
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.esm.js +1 -1
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/js-sdk.css +2 -1
  24. package/dist/nemme-sdk.umd.js +7 -7
  25. package/dist/nemme-sdk.umd.js.map +1 -1
  26. package/dist/react.cjs +1 -1
  27. package/dist/react.cjs.map +1 -1
  28. package/dist/react.esm.js +1 -1
  29. package/dist/react.esm.js.map +1 -1
  30. package/dist/utils/safe-url.d.ts +1 -0
  31. package/package.json +16 -15
  32. package/dist/chunks/form-manager-3PwpV6gK.js +0 -12
  33. package/dist/chunks/form-manager-3PwpV6gK.js.map +0 -1
  34. package/dist/chunks/form-manager-BqtPAs-A.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@nemme/js-sdk` are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.1.1] - 2026-06-14
11
+
12
+ ### Security
13
+
14
+ - The form end-screen button now only opens `http(s)` links and adds
15
+ `noopener,noreferrer`, so a malicious `javascript:`/`data:` link configured on
16
+ a form can't execute in the embedding site's origin.
17
+
18
+ ## [1.1.0] - 2026-06-04
19
+
20
+ ### Added
21
+
22
+ - Optional `group` and `role` init options. When provided they are persisted on the
23
+ tracking Session so analytics can filter and aggregate by cohort. Changing either
24
+ value between `init()` calls starts a new Session. (#71)
25
+
26
+ ### Changed
27
+
28
+ - The SDK stylesheet now bundles self-hosted Inter and Fira Code fonts instead of
29
+ importing them from the Google Fonts CDN — no more external font request when
30
+ loading `style.css`. (#67)
31
+
32
+ ## [1.0.0] - 2026-05-27
33
+
34
+ First stable release.
package/README.md CHANGED
@@ -29,7 +29,18 @@ Add React/ReactDOM UMD, then the SDK and CSS, and initialize.
29
29
  <body>
30
30
  <script>
31
31
  NemmeSDK('YOUR_CLIENT_KEY')
32
- .init({ userIdentifier: 'user-123', debug: true })
32
+ .init({
33
+ userIdentifier: 'user-123',
34
+ // Optional cohort attributes - persisted on the Session so Intel
35
+ // can filter and aggregate by group/role. Changing either between
36
+ // init calls starts a new Session.
37
+ // To reduce the risk of re-identification, any group or role containing
38
+ // fewer than five members should be represented using a pseudonymous
39
+ // identifier rather than its actual name or identifier.
40
+ group: 'Company A',
41
+ role: 'Manager',
42
+ debug: true
43
+ })
33
44
  .then(client => {
34
45
  window.nemme = client;
35
46
  client.track({ eventKey: 'page_view' });
@@ -66,7 +77,12 @@ import NemmeSDK, { NemmeClient } from '@nemme/js-sdk';
66
77
 
67
78
  async function start() {
68
79
  const client = NemmeSDK('YOUR_CLIENT_KEY');
69
- await client.init({ userIdentifier: 'user-123', debug: true });
80
+ await client.init({
81
+ userIdentifier: 'user-123',
82
+ group: 'Company A', // optional, use pseudonym id if group makes user identifiable.
83
+ role: 'Manager', // optional, use pseudonym id if role makes user identifiable.
84
+ debug: true
85
+ });
70
86
  await client.track({ eventKey: 'button_clicked', data: { id: 'cta' } });
71
87
  }
72
88
 
@@ -103,7 +119,13 @@ function App() {
103
119
  async function bootstrap() {
104
120
  const root = ReactDOM.createRoot(document.getElementById('root')!);
105
121
  root.render(
106
- <NemmeProvider clientKey="YOUR_CLIENT_KEY" config={{ userIdentifier: 'user-123', debug: true }}>
122
+ <NemmeProvider
123
+ clientKey="YOUR_CLIENT_KEY"
124
+ // To reduce the risk of re-identification, any group or role containing
125
+ // fewer than five members should be represented using a pseudonymous
126
+ // identifier rather than its actual name or identifier.
127
+ config={{ userIdentifier: 'user-123', group: 'Company A', role: 'Manager', debug: true }}
128
+ >
107
129
  <App />
108
130
  </NemmeProvider>
109
131
  );
@@ -1 +1 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return o}});
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return o}});
@@ -1,4 +1,4 @@
1
- import { t as e } from "./config-C6VpkZGH.js";
1
+ import { t as e } from "./config-DCrsd6gZ.js";
2
2
  //#region src/deliveries/delivery-manager.ts
3
3
  var t = class {
4
4
  logger;
@@ -50,7 +50,7 @@ var t = class {
50
50
  try {
51
51
  let t = await this.createDeliveryResponse(e);
52
52
  if (!this.formManager) {
53
- let e = await import("./form-manager-BqtPAs-A.js").then((e) => e.n);
53
+ let e = await import("./form-manager-Dit5tdsl.js").then((e) => e.n);
54
54
  this.formManager = new e.FormManager(this.logger, this.headers, this.http, this.formConfig);
55
55
  }
56
56
  await this.formManager.fetchAndDisplayForm(e.productSlug, async () => {
@@ -426,6 +426,8 @@ var t = class {
426
426
  }, d = 3e3 * 1e3, f = class {
427
427
  clientKey;
428
428
  _userIdentifier;
429
+ _group;
430
+ _role;
429
431
  _initialized = !1;
430
432
  _lastInitError = null;
431
433
  clientLogger = r.child("client");
@@ -447,10 +449,10 @@ var t = class {
447
449
  tokenRefreshTimer;
448
450
  inflightRefresh;
449
451
  http = this.createRetryingHttpClient();
450
- async init({ userIdentifier: e, secretKey: t, debug: n = !1, batch: r, formConfig: i, deactivate: a = !1, trackUrlParamChanges: o, getPage: s }) {
452
+ async init({ userIdentifier: e, secretKey: t, debug: n = !1, batch: r, formConfig: i, deactivate: a = !1, trackUrlParamChanges: o, getPage: s, group: c, role: l }) {
451
453
  if (this._initialized = !1, this._lastInitError = null, this.trackingManager?.destroy(), this.trackingManager = void 0, a) return this.clientLogger.info("Nemme client deactivated and will stop initialization."), this._initialized = !0, this._lastInitError = null, this.clientLogger.configure({ enabled: !1 }), this;
452
454
  if (!e) throw Error("userIdentifier is required parameter");
453
- this._userIdentifier = e, this.clientLogger.configure({
455
+ this._userIdentifier = e, this._group = c, this._role = l, this.clientLogger.configure({
454
456
  enabled: n,
455
457
  level: n ? "debug" : "info"
456
458
  }), this.headers = {
@@ -482,9 +484,13 @@ var t = class {
482
484
  }
483
485
  return this.trackingManager.track(e);
484
486
  }
487
+ buildInitializeBody() {
488
+ let e = {};
489
+ return this._group !== void 0 && (e.group = this._group), this._role !== void 0 && (e.role = this._role), e;
490
+ }
485
491
  async initializeSession() {
486
492
  try {
487
- let e = { ...this.headers }, t = await s.post("/external/trackings/initialize", {}, { headers: e });
493
+ let e = { ...this.headers }, t = await s.post("/external/trackings/initialize", this.buildInitializeBody(), { headers: e });
488
494
  if (!t.ok) throw Error(t.error?.message || "Request for initialising session failed");
489
495
  return this.applySessionToken(t.data.sessionToken), this.scheduleTokenRefresh(), t.data;
490
496
  } catch (e) {
@@ -506,7 +512,7 @@ var t = class {
506
512
  let e = { ...this.headers };
507
513
  this._userIdentifier && (e["X-User-Id"] = this._userIdentifier, delete e.Authorization);
508
514
  try {
509
- let t = await s.post("/external/trackings/initialize", {}, { headers: e });
515
+ let t = await s.post("/external/trackings/initialize", this.buildInitializeBody(), { headers: e });
510
516
  t.ok ? this.applySessionToken(t.data.sessionToken) : this.clientLogger.warn("SDK token refresh failed; will retry", t.error);
511
517
  } catch (e) {
512
518
  this.clientLogger.warn("SDK token refresh threw", e);
@@ -554,4 +560,4 @@ var t = class {
554
560
  //#endregion
555
561
  export { p as n, f as t };
556
562
 
557
- //# sourceMappingURL=client-D6k2IoC6.js.map
563
+ //# sourceMappingURL=client-CKvtSbpa.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-D6k2IoC6.js","names":[],"sources":["../../src/deliveries/delivery-manager.ts","../../src/utils/logger.ts","../../src/networking/httpClient.ts","../../src/tracking/tracking-manager.ts","../../src/client.ts"],"sourcesContent":["import type { RetryingHttpClient } from '../client';\nimport type { FormConfig, ProductType } from '../types/forms';\nimport { Logger } from '../utils';\n\nexport type TriggerCondition = {\n id: number;\n conditionType: 'days_since';\n daysReference?: 'first_seen_at' | 'last_seen_at';\n minDays?: number;\n maxDays?: number;\n};\n\nexport type Trigger = {\n id: number;\n triggerType: 'page_url' | 'custom_event';\n urlPattern?: string;\n eventKey?: string;\n conditions?: TriggerCondition[];\n};\n\nexport type Channel = {\n channelType: string;\n channelStyle: string;\n};\n\nexport type Audience = {\n audienceType: string;\n};\n\nexport type Frequency = {\n frequencyType: string;\n};\n\nexport type Delivery = {\n id: number;\n productSlug: string;\n productType: ProductType;\n triggers: Trigger[];\n // channels: Channel[];\n // audiences: Audience[];\n // frequency: Frequency| null;\n};\n\nexport type DeliveryResponse = {\n id: number;\n shownAt: string;\n wasDismissed: boolean;\n wasCompleted: boolean;\n userId: string;\n respondedAt: string;\n};\n\nexport type TriggerContext = {\n url: string;\n eventKey?: string;\n eventData?: Record<string, unknown>;\n};\n\nexport class DeliveryManager {\n private logger: Logger;\n private headers: Record<string, string>;\n private http: RetryingHttpClient;\n private formConfig?: FormConfig;\n private formManager?: {\n fetchAndDisplayForm: (\n productSlug: string,\n onDismiss: () => Promise<void>,\n onComplete: (formState?: unknown) => Promise<void>\n ) => Promise<void>;\n };\n private deliveries: Delivery[] = [];\n\n constructor(logger: Logger, headers: Record<string, string>, http: RetryingHttpClient, formConfig?: FormConfig) {\n this.logger = logger;\n this.headers = headers;\n this.http = http;\n this.formConfig = formConfig;\n }\n\n public async loadDeliveries(): Promise<void> {\n this.logger.debug('Loading deliveries');\n const response = await this.http.get<Delivery[]>('/external/deliveries', { headers: this.headers });\n if (!response.ok) {\n this.logger.error('Failed to load deliveries', response.error);\n this.deliveries = [];\n return;\n }\n this.deliveries = response.data;\n this.logger.info(`Loaded ${this.deliveries.length} deliveries`);\n }\n\n public async evaluateDeliveryTriggers(context: TriggerContext): Promise<void> {\n for (const delivery of this.deliveries) {\n for (const trigger of delivery.triggers) {\n if (this.shouldTriggerDelivery(trigger, context)) {\n const deliveryValid = await this.isDeliveryValid(delivery, trigger.id);\n if (!deliveryValid) {\n this.logger.debug(`Delivery ${delivery.id} is not valid, skipping`);\n continue;\n }\n await this.executeDelivery(delivery, trigger);\n return; // Stop after first match\n }\n }\n }\n }\n\n private shouldTriggerDelivery(trigger: Trigger, context: TriggerContext): boolean {\n if (this.hasActiveDelivery()) {\n return false;\n }\n switch (trigger.triggerType) {\n case 'page_url':\n return trigger.urlPattern ? this.matchesUrlPattern(context.url, trigger.urlPattern) : true;\n case 'custom_event':\n return trigger.eventKey ? context.eventKey === trigger.eventKey : false;\n default:\n return false;\n }\n }\n\n private hasActiveDelivery(): boolean {\n return document.getElementById('nm') !== null;\n }\n\n private async executeDelivery(delivery: Delivery, trigger: Trigger): Promise<void> {\n const productType = delivery.productType;\n if (productType === 'FRM') {\n this.logger.debug(`Triggering form delivery: ${delivery.productSlug}`, {\n triggerType: trigger.triggerType,\n urlPattern: trigger.urlPattern,\n eventKey: trigger.eventKey\n });\n\n try {\n const deliveryResponse = await this.createDeliveryResponse(delivery);\n if (!this.formManager) {\n const mod = await import('../forms/form-manager');\n this.formManager = new mod.FormManager(this.logger, this.headers, this.http, this.formConfig);\n }\n await this.formManager.fetchAndDisplayForm(\n delivery.productSlug,\n async () => {\n this.logger.debug(`Form for delivery: ${delivery.productSlug} was cancelled`);\n if (deliveryResponse) await this.updateDeliveryResponse(delivery.id, deliveryResponse.id, true);\n },\n async () => {\n this.logger.debug(`Form for delivery: ${delivery.productSlug} was completed`);\n if (deliveryResponse) await this.updateDeliveryResponse(delivery.id, deliveryResponse.id, false, true);\n }\n );\n } catch (error) {\n this.logger.error(`Failed to load form for delivery: ${delivery.productSlug}`, error);\n }\n }\n }\n\n private matchesUrlPattern(url: string, pattern: string): boolean {\n // Extract pathname from URL (remove protocol, host, query params, etc.)\n let pathname: string;\n try {\n const urlObj = new URL(url);\n pathname = urlObj.pathname;\n } catch {\n // If URL parsing fails, assume it's already a pathname\n pathname = url.startsWith('/') ? url : `/${url}`;\n }\n\n // Normalize paths: remove trailing slash (except for root) and ensure leading slash\n const normalizePath = (p: string): string => {\n if (!p) return '/';\n let s = p.startsWith('/') ? p : `/${p}`;\n if (s.length > 1 && s.endsWith('/')) s = s.slice(0, -1);\n return s;\n };\n\n const normalizedPathname = normalizePath(pathname);\n const normalizedPattern = normalizePath(pattern);\n\n // Convert glob pattern to regex\n // First, replace glob tokens with placeholders, then escape regex special chars, then restore\n const regexPattern = normalizedPattern\n .replace(/\\*\\*/g, '§DOUBLE_STAR§')\n .replace(/\\*/g, '§SINGLE_STAR§')\n .replace(/\\?/g, '§QUESTION§')\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex special characters\n .replace(/§SINGLE_STAR§/g, '[^/]*') // * matches anything except /\n .replace(/§DOUBLE_STAR§/g, '.*') // ** matches anything including /\n .replace(/§QUESTION§/g, '.'); // ? matches single character\n\n const regex = new RegExp(`^${regexPattern}$`);\n const result = regex.test(normalizedPathname);\n\n this.logger.debug(`URL pattern matching`, {\n originalUrl: url,\n pathname,\n normalizedPathname,\n pattern,\n normalizedPattern,\n regexPattern,\n matches: result\n });\n\n return result;\n }\n\n private async createDeliveryResponse(delivery: Delivery): Promise<DeliveryResponse | undefined> {\n const response = await this.http.post<DeliveryResponse>(\n `/external/deliveries/${delivery.id}/responses`,\n {},\n { headers: this.headers }\n );\n if (!response.ok) {\n this.logger.error(`Failed to create delivery response for delivery: ${delivery.id}`, response.error);\n return undefined;\n }\n return response.data;\n }\n\n private async updateDeliveryResponse(\n deliveryId: number,\n deliveryResponseId: number,\n dismissed: boolean = false,\n completed: boolean = false\n ): Promise<void> {\n try {\n await this.http.patch(\n `/external/deliveries/${deliveryId}/responses/${deliveryResponseId}`,\n { wasDismissed: dismissed, wasCompleted: completed },\n { headers: this.headers }\n );\n } catch (error) {\n this.logger.error(`Failed to update delivery response: ${deliveryResponseId} for delivery: ${deliveryId}`, error);\n }\n }\n\n private async isDeliveryValid(delivery: Delivery, triggerId?: number): Promise<boolean> {\n const query = triggerId !== undefined ? `?triggerId=${triggerId}` : '';\n const response = await this.http.get<boolean>(`/external/deliveries/${delivery.id}/should-deliver${query}`, {\n headers: this.headers\n });\n if (!response.ok) {\n this.logger.error(`Failed to check delivery validity for delivery: ${delivery.id}`, response.error);\n return false;\n }\n return response.data;\n }\n}\n","/**\n * Logger utility for the SDK\n * Provides consistent logging across the SDK with different log levels\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\ninterface LoggerOptions {\n prefix?: string;\n enabled?: boolean;\n level?: LogLevel;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3\n};\n\nexport class Logger {\n private prefix: string;\n private enabled: boolean;\n private level: LogLevel;\n\n constructor(options: LoggerOptions = {}) {\n this.prefix = options.prefix || 'Nemme SDK';\n this.enabled = options.enabled !== undefined ? options.enabled : true;\n this.level = options.level || 'info';\n }\n\n private shouldLog(level: LogLevel): boolean {\n return this.enabled && LOG_LEVELS[level] >= LOG_LEVELS[this.level];\n }\n\n private formatMessage(message: string): string {\n return `[${this.prefix}] ${message}`;\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (this.shouldLog('debug')) {\n console.debug(this.formatMessage(message), ...args);\n }\n }\n\n info(message: string, ...args: unknown[]): void {\n if (this.shouldLog('info')) {\n console.info(this.formatMessage(message), ...args);\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (this.shouldLog('warn')) {\n console.warn(this.formatMessage(message), ...args);\n }\n }\n\n error(message: string, ...args: unknown[]): void {\n if (this.shouldLog('error')) {\n console.error(this.formatMessage(message), ...args);\n }\n }\n\n // Create a child logger with a new prefix\n child(prefix: string): Logger {\n return new Logger({\n prefix: `${this.prefix}:${prefix}`,\n enabled: this.enabled,\n level: this.level\n });\n }\n\n // Configure logger settings\n configure(options: LoggerOptions): void {\n if (options.prefix !== undefined) {\n this.prefix = options.prefix;\n }\n if (options.enabled !== undefined) {\n this.enabled = options.enabled;\n }\n if (options.level !== undefined) {\n this.level = options.level;\n }\n }\n}\n\n// Create and export a default logger instance\nexport const logger = new Logger();\n","import { config } from '../config';\nimport { logger } from '../utils';\n\n// Create a network-specific logger\nconst networkLogger = logger.child('network');\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\nexport interface RequestOptions {\n method?: HttpMethod;\n headers?: Record<string, string>;\n body?: unknown;\n params?: Record<string, string | number | boolean>;\n timeout?: number;\n}\n\nexport interface HttpResponse<T = unknown> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n ok: boolean;\n error?: {\n message: string;\n details?: unknown;\n };\n}\n\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Creates a URL with query parameters\n */\nconst createUrl = (endpoint: string, params?: Record<string, string | number | boolean>): string => {\n const url = new URL(endpoint, config.apiBaseUrl);\n\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n });\n }\n\n return url.toString();\n};\n\n/**\n * Global HTTP client for making API requests\n */\nexport const httpClient = {\n /**\n * Make an HTTP request\n */\n async request<T = unknown>(endpoint: string, options: RequestOptions = {}): Promise<HttpResponse<T>> {\n const { method = 'GET', headers = {}, body, params, timeout = DEFAULT_TIMEOUT } = options;\n\n const url = createUrl(endpoint, params);\n\n // Prepare headers with defaults\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n ...headers\n };\n\n // Prepare request options\n const requestOptions: RequestInit = {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined\n };\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n requestOptions.signal = controller.signal;\n\n try {\n const response = await fetch(url, requestOptions);\n\n // Parse response data\n let data: T;\n const contentType = response.headers.get('content-type');\n\n if (contentType && contentType.includes('application/json')) {\n data = await response.json();\n } else {\n const text = await response.text();\n try {\n data = JSON.parse(text) as T;\n } catch {\n data = text as unknown as T;\n }\n }\n\n const result: HttpResponse<T> = {\n data,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n ok: response.ok\n };\n\n // Handle error responses but don't throw\n if (!response.ok) {\n const errorMessage = `Request failed with status ${response.status}: ${response.statusText}`;\n networkLogger.error(errorMessage, {\n url,\n method,\n status: response.status,\n data: result.data\n });\n\n result.ok = false;\n result.error = {\n message: errorMessage,\n details: result.data\n };\n } else {\n result.ok = true;\n }\n\n return result;\n } catch (error) {\n // Log the error but don't throw\n let errorMessage = 'Network request failed';\n let errorDetails: Record<string, unknown>;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n errorMessage = `Request timeout after ${timeout}ms`;\n errorDetails = { timeout, url };\n } else {\n errorDetails = {\n message: error instanceof Error ? error.message : String(error),\n url,\n method\n };\n }\n\n networkLogger.error(errorMessage, errorDetails);\n\n // Return an error response instead of throwing\n const errorResponse: HttpResponse<T> = {\n data: {} as T,\n status: 0,\n statusText: errorMessage,\n headers: new Headers(),\n ok: false,\n error: {\n message: errorMessage,\n details: errorDetails\n }\n };\n\n return errorResponse;\n } finally {\n clearTimeout(timeoutId);\n }\n },\n\n /**\n * Convenience methods for common HTTP methods\n */\n async get<T = unknown>(endpoint: string, options: Omit<RequestOptions, 'method'> = {}): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'GET' });\n },\n\n async post<T = unknown>(\n endpoint: string,\n data?: unknown,\n options: Omit<RequestOptions, 'method' | 'body'> = {}\n ): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'POST', body: data });\n },\n\n async put<T = unknown>(\n endpoint: string,\n data?: unknown,\n options: Omit<RequestOptions, 'method' | 'body'> = {}\n ): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'PUT', body: data });\n },\n\n async patch<T = unknown>(\n endpoint: string,\n data?: unknown,\n options: Omit<RequestOptions, 'method' | 'body'> = {}\n ): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'PATCH', body: data });\n },\n\n async delete<T = unknown>(endpoint: string, options: Omit<RequestOptions, 'method'> = {}): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'DELETE' });\n }\n};\n","import type { RetryingHttpClient } from '../client';\nimport { Logger } from '../utils';\n\nexport type Config = {\n logger: Logger;\n headers: Record<string, string>;\n http: RetryingHttpClient;\n sessionId: number;\n eventDefinitions: EventDefinition[];\n batchConfig?: boolean | BatchConfig;\n onPageView?: (url: string) => Promise<void>;\n onEvent?: (event: TrackEventOptions, url: string) => Promise<void>;\n trackUrlParamChanges?: boolean;\n getPage?: () => string;\n};\n\nexport type BatchConfig = {\n enabled?: boolean;\n size?: number;\n delayMs?: number;\n sendOnUnload?: boolean;\n};\n\nexport type TrackEventOptions<T extends string = string> = {\n eventKey: T;\n data?: Record<string, unknown>;\n};\n\ntype HistoryParams = Parameters<typeof history.pushState>;\n\ntype EventOptions = TrackEventOptions & {\n sessionId: number;\n timestamp: string;\n page: string;\n};\n\ntype EventProperties = {\n name: string;\n type: string;\n};\n\nexport type EventDefinition = {\n properties: EventProperties[];\n eventKey: string;\n};\n\n// Backend event-definition types are stored as \"STR\" | \"NUM\" | \"BOOL\"; map them\n// to their JavaScript `typeof` equivalents for runtime validation. Lowercase JS\n// type names pass through unchanged so legacy / test payloads still work.\nconst BACKEND_TYPE_TO_JS: Record<string, string> = {\n STR: 'string',\n NUM: 'number',\n BOOL: 'boolean'\n};\n\nconst resolveExpectedJsType = (definedType: string): string => BACKEND_TYPE_TO_JS[definedType] ?? definedType;\n\n/**\n * Manages event tracking, batching, and page view tracking\n */\nexport class TrackingManager {\n private logger: Logger;\n private headers: Record<string, string>;\n private http: RetryingHttpClient;\n private sessionId?: number;\n private eventDefinitions: EventDefinition[] = [];\n private onPageView?: (url: string) => Promise<void>;\n private onEvent?: (event: TrackEventOptions, url: string) => Promise<void>;\n private trackUrlParamChanges = true;\n private getPage?: () => string;\n\n // Batching\n private backlog: Array<EventOptions> = [];\n private flushTimeout: number | null = null;\n private batchConfig: Required<BatchConfig> = {\n enabled: true,\n size: 10,\n delayMs: 10000, // 10 seconds\n sendOnUnload: true\n };\n\n // Page view tracking\n private originalHistoryMethods: {\n pushState: typeof history.pushState;\n replaceState: typeof history.replaceState;\n } | null = null;\n\n private readonly EVENT_PAGE_VIEW = 'page_view';\n private lastTrackedPageKey?: string;\n\n constructor({\n logger,\n headers,\n http,\n sessionId,\n eventDefinitions,\n batchConfig,\n onPageView,\n onEvent,\n trackUrlParamChanges,\n getPage\n }: Config) {\n this.logger = logger;\n this.headers = headers;\n this.http = http;\n this.sessionId = sessionId;\n this.eventDefinitions = eventDefinitions;\n this.onPageView = onPageView;\n this.onEvent = onEvent;\n this.getPage = getPage;\n if (typeof trackUrlParamChanges === 'boolean') {\n this.trackUrlParamChanges = trackUrlParamChanges;\n }\n\n // Bind methods before setting up event listeners\n this.flushEvents = this.flushEvents.bind(this);\n this.handlePopState = this.handlePopState.bind(this);\n this.handleVisibilityChange = this.handleVisibilityChange.bind(this);\n\n this.setupBatching(batchConfig);\n }\n\n private setupBatching(batchConfig?: boolean | BatchConfig): void {\n if (typeof batchConfig === 'boolean') {\n this.batchConfig.enabled = batchConfig;\n } else if (batchConfig) {\n this.batchConfig = {\n ...this.batchConfig,\n ...batchConfig\n };\n }\n\n // Setup unload handler if batching is enabled\n if (this.batchConfig.enabled && this.batchConfig.sendOnUnload && typeof window !== 'undefined') {\n window.addEventListener('beforeunload', this.flushEvents);\n window.addEventListener('pagehide', this.flushEvents);\n }\n }\n\n public async setupPageViewTracking(): Promise<void> {\n if (typeof window === 'undefined') return;\n\n this.originalHistoryMethods = {\n pushState: history.pushState,\n replaceState: history.replaceState\n };\n\n const originalPushState = history.pushState;\n history.pushState = async (...args: HistoryParams) => {\n originalPushState.apply(history, args);\n await this.trackPageView();\n };\n\n const originalReplaceState = history.replaceState;\n history.replaceState = async (...args: HistoryParams) => {\n originalReplaceState.apply(history, args);\n await this.trackPageView();\n };\n\n window.addEventListener('popstate', this.handlePopState);\n document.addEventListener('visibilitychange', this.handleVisibilityChange);\n }\n\n public async track<T extends TrackEventOptions>(options: T): Promise<void> {\n if (!this.sessionId) {\n this.logger.warn('Tracking manager not initialized, cannot track event');\n return;\n }\n\n const { eventKey, data = {} } = options;\n this.logger.debug('Tracking event', { eventKey, data });\n const url = this.getPageUrl();\n const event: EventOptions = {\n sessionId: this.sessionId,\n eventKey,\n data: data || {},\n timestamp: new Date().toISOString(),\n page: url\n };\n\n if (!this.sanitizeEventData(event)) {\n return;\n }\n\n this.validateEvent(event);\n\n if (this.batchConfig.enabled) {\n this.backlog.push(event);\n this.scheduleFlush();\n } else {\n await this.sendEvents([event]);\n }\n\n if (eventKey === this.EVENT_PAGE_VIEW) {\n this.onPageView?.(url);\n } else {\n this.onEvent?.(event, url);\n }\n }\n\n public async trackPageView(): Promise<void> {\n if (typeof window === 'undefined') return;\n\n const url = this.getPageUrl();\n\n if (!this.trackUrlParamChanges) {\n const currentPageKey = this.getPageKeyWithoutQuery(url);\n\n if (this.lastTrackedPageKey === currentPageKey) {\n return;\n }\n\n this.lastTrackedPageKey = currentPageKey;\n }\n\n await this.track({\n eventKey: this.EVENT_PAGE_VIEW,\n data: {\n title: document.title\n }\n });\n }\n\n private getPageUrl(): string {\n if (this.getPage) {\n return this.getPage();\n }\n\n const url = window.location.href;\n\n if (window.location.protocol === 'file:') {\n const filename = window.location.pathname.split('/').pop() || '';\n return `/${filename}${window.location.hash}`;\n }\n\n return url;\n }\n\n private getPageKeyWithoutQuery(url: string): string {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}${parsed.hash}`;\n } catch {\n const [base] = url.split('?');\n return base;\n }\n }\n\n /**\n * Manually flush any pending events\n */\n public async flush(): Promise<void> {\n return this.flushEvents();\n }\n\n /**\n * Clean up event listeners and restore original history methods\n */\n public destroy(): void {\n if (this.originalHistoryMethods) {\n history.pushState = this.originalHistoryMethods.pushState;\n history.replaceState = this.originalHistoryMethods.replaceState;\n this.originalHistoryMethods = null;\n }\n\n if (typeof window !== 'undefined') {\n window.removeEventListener('popstate', this.handlePopState);\n document.removeEventListener('visibilitychange', this.handleVisibilityChange);\n window.removeEventListener('beforeunload', this.flushEvents);\n window.removeEventListener('pagehide', this.flushEvents);\n }\n\n // Clear any pending flush timeout\n if (this.flushTimeout) {\n clearTimeout(this.flushTimeout);\n this.flushTimeout = null;\n }\n }\n\n private sanitizeEventData(event: EventOptions): boolean {\n if (event.eventKey === this.EVENT_PAGE_VIEW) return true;\n if (!event.data) return true;\n\n const data = event.data;\n\n for (const key of Object.keys(data)) {\n const value = data[key];\n\n if (typeof value === 'string' && value === '[object Object]') {\n this.logger.error(`Property ${key} contains [object Object], dropping event ${event.eventKey}`);\n return false;\n }\n\n const valueType = typeof value;\n\n if (valueType !== 'boolean' && valueType !== 'number' && valueType !== 'string') {\n this.logger.warn(`Property ${key} has unsupported type ${valueType}, removing from event ${event.eventKey}`);\n delete data[key];\n continue;\n }\n\n if (typeof value === 'string') {\n data[key] = value.toLowerCase();\n }\n }\n\n return true;\n }\n\n private validateEvent(event: EventOptions): void {\n if (event.eventKey === this.EVENT_PAGE_VIEW) return;\n\n const eventDefinition = this.eventDefinitions.find(def => def.eventKey === event.eventKey);\n if (!eventDefinition) {\n this.logger.warn(`Event ${event.eventKey} is not registered`);\n return;\n }\n\n const definedProperties = eventDefinition.properties;\n const eventProperties = event.data;\n if (!eventProperties) return;\n\n for (const eventPropertyKey of Object.keys(eventProperties)) {\n const eventProperty = eventProperties[eventPropertyKey];\n const definedProperty = definedProperties.find(prop => prop.name === eventPropertyKey);\n\n if (!definedProperty) {\n this.logger.warn(`Property ${eventPropertyKey} is not registered for event ${event.eventKey}`);\n continue;\n }\n\n const expectedJsType = resolveExpectedJsType(definedProperty.type);\n if (typeof eventProperty !== expectedJsType) {\n this.logger.warn(\n `Property ${definedProperty.name} has type ${expectedJsType} but value is of type ${typeof eventProperty}`\n );\n }\n }\n }\n\n private async handlePopState(): Promise<void> {\n await this.trackPageView();\n }\n\n private async handleVisibilityChange(): Promise<void> {\n if (document.visibilityState === 'visible') {\n await this.trackPageView();\n }\n }\n\n private scheduleFlush(): void {\n if (this.backlog.length >= this.batchConfig.size) {\n this.flushEvents();\n return;\n }\n\n if (!this.flushTimeout) {\n this.flushTimeout = window.setTimeout(() => {\n this.flushEvents();\n }, this.batchConfig.delayMs);\n }\n }\n\n private async flushEvents(): Promise<void> {\n if (this.flushTimeout) {\n clearTimeout(this.flushTimeout);\n this.flushTimeout = null;\n }\n\n if (this.backlog.length === 0) return;\n\n const eventsToSend = [...this.backlog];\n this.backlog = [];\n\n try {\n await this.sendEvents(eventsToSend);\n } catch (error) {\n this.backlog.unshift(...eventsToSend);\n this.logger.error('Failed to send batched events', error);\n }\n }\n\n private async sendEvents(events: Array<EventOptions>): Promise<void> {\n if (events.length === 0) return;\n\n // Captured as late as possible — paired with the server's `received_at`,\n // the delta `sentAt - received_at` is the client clock skew the server\n // subtracts from each event's `timestamp` to realign to its own clock.\n const sentAt = new Date().toISOString();\n\n if (events.length === 1) {\n await this.http.post('/external/trackings/track', { ...events[0], sentAt }, { headers: this.headers });\n } else {\n await this.http.post('/external/trackings/batch', { sentAt, events }, { headers: this.headers });\n }\n }\n}\n","import { DeliveryManager } from './deliveries/delivery-manager';\nimport { httpClient, HttpResponse } from './networking';\nimport { BatchConfig, EventDefinition, TrackEventOptions, TrackingManager } from './tracking/tracking-manager';\nimport { FormConfig } from './types/forms';\nimport { Logger, logger } from './utils';\n\n// Subset of the httpClient surface that the managers use, plus the same\n// shapes — so the retry wrapper is a drop-in replacement.\nexport type RetryingHttpClient = Pick<typeof httpClient, 'get' | 'post' | 'patch'>;\n\nexport type ClientConfigType = {\n userIdentifier: string;\n secretKey?: string;\n debug?: boolean;\n batch?: boolean | BatchConfig;\n formConfig?: FormConfig;\n deactivate?: boolean;\n trackUrlParamChanges?: boolean;\n getPage?: () => string;\n};\n\n// Re-export types for backward compatibility\nexport type { BatchConfig, TrackEventOptions } from './tracking/tracking-manager';\n\ntype InitializeResponse = {\n sessionId: number;\n sessionToken: string;\n eventDefinitions: EventDefinition[];\n};\n\n// Server token TTL is 3600s; refresh well before that so the SDK never sends\n// an expired bearer in normal usage. Refresh is best-effort and silent on\n// failure — the next refresh attempt will pick up where this one left off.\nconst TOKEN_REFRESH_INTERVAL_MS = 50 * 60 * 1000;\n\n/**\n * Main client class for the Nemme SDK\n * Coordinates initialization, tracking, and form management\n */\nexport class NemmeClient {\n readonly clientKey: string;\n private _userIdentifier?: string;\n private _initialized = false;\n private _lastInitError: Error | null = null;\n private clientLogger: Logger = logger.child('client');\n private headers: Record<string, string> = {};\n private formConfig?: FormConfig;\n\n get userIdentifier(): string | undefined {\n return this._userIdentifier;\n }\n\n get initialized(): boolean {\n return this._initialized;\n }\n\n get lastInitError(): Error | null {\n return this._lastInitError;\n }\n\n constructor(clientKey: string) {\n this.clientKey = clientKey;\n }\n\n // Module managers\n private trackingManager?: TrackingManager;\n private tokenRefreshTimer?: ReturnType<typeof setTimeout>;\n // De-duplicates concurrent refreshes: many in-flight calls all getting 401\n // at roughly the same time must only fire one /initialize round-trip.\n private inflightRefresh?: Promise<void>;\n private http: RetryingHttpClient = this.createRetryingHttpClient();\n\n /**\n * Create a new Nemme SDK client instance\n */\n public async init({\n userIdentifier,\n secretKey,\n debug = false,\n batch,\n formConfig,\n deactivate = false,\n trackUrlParamChanges,\n getPage\n }: ClientConfigType): Promise<NemmeClient> {\n this._initialized = false;\n this._lastInitError = null;\n this.trackingManager?.destroy();\n this.trackingManager = undefined;\n\n if (deactivate) {\n this.clientLogger.info('Nemme client deactivated and will stop initialization.');\n this._initialized = true;\n this._lastInitError = null;\n this.clientLogger.configure({ enabled: false });\n return this;\n }\n if (!userIdentifier) throw new Error('userIdentifier is required parameter');\n this._userIdentifier = userIdentifier;\n\n // Configure logger based on debug setting\n this.clientLogger.configure({\n enabled: debug,\n level: debug ? 'debug' : 'info'\n });\n\n this.headers = {\n 'X-Client-Key': this.clientKey,\n 'X-User-Id': this._userIdentifier,\n ...(secretKey && { 'X-Client-Secret': secretKey })\n };\n\n this.formConfig = formConfig;\n\n try {\n const session = await this.initializeSession();\n await this.initializeManagers(session, batch, trackUrlParamChanges, getPage);\n this._initialized = true;\n this._lastInitError = null;\n this.clientLogger.info('Nemme client initialized', {\n clientKey: this.clientKey,\n userIdentifier: this._userIdentifier\n });\n } catch (error) {\n this.clientLogger.error('Error during initialization:', error);\n this._initialized = false;\n this._lastInitError = error instanceof Error ? error : new Error('Error during initialization');\n }\n\n return this;\n }\n\n public async flush(): Promise<void> {\n return this.trackingManager?.flush();\n }\n\n public destroy(): void {\n this.trackingManager?.destroy();\n this.clearTokenRefresh();\n }\n\n public async track<T extends TrackEventOptions>(options: T): Promise<void> {\n if (!this._initialized || !this.trackingManager) {\n this.clientLogger.warn('Nemme client not initialized, some operations may fail');\n return;\n }\n\n return this.trackingManager.track(options);\n }\n\n private async initializeSession(): Promise<InitializeResponse> {\n try {\n // Snapshot the current headers so the captured call reflects the\n // pre-mutation state (X-User-Id present, no Authorization yet).\n const initHeaders = { ...this.headers };\n const response: HttpResponse<InitializeResponse> = await httpClient.post(\n '/external/trackings/initialize',\n {},\n { headers: initHeaders }\n );\n if (!response.ok) {\n throw new Error(response.error?.message || 'Request for initialising session failed');\n }\n // R17: switch to bearer-token auth for all subsequent calls. Mutating\n // this.headers in place propagates to managers that captured the ref.\n this.applySessionToken(response.data.sessionToken);\n this.scheduleTokenRefresh();\n return response.data;\n } catch (error) {\n this.clientLogger.error('Error during initialization', error);\n throw error;\n }\n }\n\n private applySessionToken(token: string): void {\n this.headers['Authorization'] = `Bearer ${token}`;\n delete this.headers['X-User-Id'];\n }\n\n private scheduleTokenRefresh(): void {\n if (typeof window === 'undefined') return;\n this.clearTokenRefresh();\n this.tokenRefreshTimer = setTimeout(() => {\n this.refreshToken();\n }, TOKEN_REFRESH_INTERVAL_MS);\n }\n\n private clearTokenRefresh(): void {\n if (this.tokenRefreshTimer) {\n clearTimeout(this.tokenRefreshTimer);\n this.tokenRefreshTimer = undefined;\n }\n }\n\n private async refreshToken(): Promise<void> {\n // Initialize still needs X-User-Id (server mints the token from it). Build\n // a one-shot header snapshot so this.headers stays bearer-only for\n // concurrent in-flight calls from the managers.\n const refreshHeaders = { ...this.headers };\n if (this._userIdentifier) {\n refreshHeaders['X-User-Id'] = this._userIdentifier;\n delete refreshHeaders['Authorization'];\n }\n try {\n const response: HttpResponse<InitializeResponse> = await httpClient.post(\n '/external/trackings/initialize',\n {},\n { headers: refreshHeaders }\n );\n if (response.ok) {\n this.applySessionToken(response.data.sessionToken);\n } else {\n this.clientLogger.warn('SDK token refresh failed; will retry', response.error);\n }\n } catch (error) {\n this.clientLogger.warn('SDK token refresh threw', error);\n } finally {\n this.scheduleTokenRefresh();\n }\n }\n\n /**\n * Wraps httpClient so that any call returning 401 triggers a single\n * refresh+retry. Concurrent 401s coalesce into one refresh round-trip.\n *\n * Refresh mutates `this.headers` in place; managers hold the same reference\n * so the retried call automatically picks up the fresh bearer.\n */\n private createRetryingHttpClient(): RetryingHttpClient {\n const withRetry = async <T>(call: () => Promise<HttpResponse<T>>): Promise<HttpResponse<T>> => {\n const first = await call();\n if (first.status !== 401) return first;\n this.clientLogger.debug('SDK got 401; refreshing token and retrying once');\n if (!this.inflightRefresh) {\n this.inflightRefresh = this.refreshToken().finally(() => {\n this.inflightRefresh = undefined;\n });\n }\n await this.inflightRefresh;\n return call();\n };\n return {\n get: <T>(endpoint: string, options: Parameters<typeof httpClient.get>[1] = {}) =>\n withRetry<T>(() => httpClient.get<T>(endpoint, options)),\n post: <T>(endpoint: string, data?: unknown, options: Parameters<typeof httpClient.post>[2] = {}) =>\n withRetry<T>(() => httpClient.post<T>(endpoint, data, options)),\n patch: <T>(endpoint: string, data?: unknown, options: Parameters<typeof httpClient.patch>[2] = {}) =>\n withRetry<T>(() => httpClient.patch<T>(endpoint, data, options))\n };\n }\n\n private async initializeManagers(\n session: InitializeResponse,\n batchConfig?: boolean | BatchConfig,\n trackUrlParamChanges?: boolean,\n getPage?: () => string\n ): Promise<void> {\n const deliveryManager = new DeliveryManager(this.clientLogger, this.headers, this.http, this.formConfig);\n const trackingManager = new TrackingManager({\n logger: this.clientLogger,\n headers: this.headers,\n http: this.http,\n sessionId: session.sessionId,\n eventDefinitions: session.eventDefinitions,\n batchConfig,\n trackUrlParamChanges,\n getPage,\n onPageView: async (url: string) => {\n await deliveryManager.evaluateDeliveryTriggers({ url });\n },\n onEvent: async (event: TrackEventOptions, url: string) => {\n await deliveryManager.evaluateDeliveryTriggers({ eventKey: event.eventKey, eventData: event.data, url });\n }\n });\n\n await deliveryManager.loadDeliveries();\n\n if (typeof window !== 'undefined') {\n await trackingManager.setupPageViewTracking();\n await trackingManager.trackPageView();\n await trackingManager.flush();\n }\n\n this.trackingManager = trackingManager;\n }\n}\n\nexport const initAsModule = (clientKey: string) => new NemmeClient(clientKey);\n"],"mappings":"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"}
1
+ {"version":3,"file":"client-CKvtSbpa.js","names":[],"sources":["../../src/deliveries/delivery-manager.ts","../../src/utils/logger.ts","../../src/networking/httpClient.ts","../../src/tracking/tracking-manager.ts","../../src/client.ts"],"sourcesContent":["import type { RetryingHttpClient } from '../client';\nimport type { FormConfig, ProductType } from '../types/forms';\nimport { Logger } from '../utils';\n\nexport type TriggerCondition = {\n id: number;\n conditionType: 'days_since';\n daysReference?: 'first_seen_at' | 'last_seen_at';\n minDays?: number;\n maxDays?: number;\n};\n\nexport type Trigger = {\n id: number;\n triggerType: 'page_url' | 'custom_event';\n urlPattern?: string;\n eventKey?: string;\n conditions?: TriggerCondition[];\n};\n\nexport type Channel = {\n channelType: string;\n channelStyle: string;\n};\n\nexport type Audience = {\n audienceType: string;\n};\n\nexport type Frequency = {\n frequencyType: string;\n};\n\nexport type Delivery = {\n id: number;\n productSlug: string;\n productType: ProductType;\n triggers: Trigger[];\n // channels: Channel[];\n // audiences: Audience[];\n // frequency: Frequency| null;\n};\n\nexport type DeliveryResponse = {\n id: number;\n shownAt: string;\n wasDismissed: boolean;\n wasCompleted: boolean;\n userId: string;\n respondedAt: string;\n};\n\nexport type TriggerContext = {\n url: string;\n eventKey?: string;\n eventData?: Record<string, unknown>;\n};\n\nexport class DeliveryManager {\n private logger: Logger;\n private headers: Record<string, string>;\n private http: RetryingHttpClient;\n private formConfig?: FormConfig;\n private formManager?: {\n fetchAndDisplayForm: (\n productSlug: string,\n onDismiss: () => Promise<void>,\n onComplete: (formState?: unknown) => Promise<void>\n ) => Promise<void>;\n };\n private deliveries: Delivery[] = [];\n\n constructor(logger: Logger, headers: Record<string, string>, http: RetryingHttpClient, formConfig?: FormConfig) {\n this.logger = logger;\n this.headers = headers;\n this.http = http;\n this.formConfig = formConfig;\n }\n\n public async loadDeliveries(): Promise<void> {\n this.logger.debug('Loading deliveries');\n const response = await this.http.get<Delivery[]>('/external/deliveries', { headers: this.headers });\n if (!response.ok) {\n this.logger.error('Failed to load deliveries', response.error);\n this.deliveries = [];\n return;\n }\n this.deliveries = response.data;\n this.logger.info(`Loaded ${this.deliveries.length} deliveries`);\n }\n\n public async evaluateDeliveryTriggers(context: TriggerContext): Promise<void> {\n for (const delivery of this.deliveries) {\n for (const trigger of delivery.triggers) {\n if (this.shouldTriggerDelivery(trigger, context)) {\n const deliveryValid = await this.isDeliveryValid(delivery, trigger.id);\n if (!deliveryValid) {\n this.logger.debug(`Delivery ${delivery.id} is not valid, skipping`);\n continue;\n }\n await this.executeDelivery(delivery, trigger);\n return; // Stop after first match\n }\n }\n }\n }\n\n private shouldTriggerDelivery(trigger: Trigger, context: TriggerContext): boolean {\n if (this.hasActiveDelivery()) {\n return false;\n }\n switch (trigger.triggerType) {\n case 'page_url':\n return trigger.urlPattern ? this.matchesUrlPattern(context.url, trigger.urlPattern) : true;\n case 'custom_event':\n return trigger.eventKey ? context.eventKey === trigger.eventKey : false;\n default:\n return false;\n }\n }\n\n private hasActiveDelivery(): boolean {\n return document.getElementById('nm') !== null;\n }\n\n private async executeDelivery(delivery: Delivery, trigger: Trigger): Promise<void> {\n const productType = delivery.productType;\n if (productType === 'FRM') {\n this.logger.debug(`Triggering form delivery: ${delivery.productSlug}`, {\n triggerType: trigger.triggerType,\n urlPattern: trigger.urlPattern,\n eventKey: trigger.eventKey\n });\n\n try {\n const deliveryResponse = await this.createDeliveryResponse(delivery);\n if (!this.formManager) {\n const mod = await import('../forms/form-manager');\n this.formManager = new mod.FormManager(this.logger, this.headers, this.http, this.formConfig);\n }\n await this.formManager.fetchAndDisplayForm(\n delivery.productSlug,\n async () => {\n this.logger.debug(`Form for delivery: ${delivery.productSlug} was cancelled`);\n if (deliveryResponse) await this.updateDeliveryResponse(delivery.id, deliveryResponse.id, true);\n },\n async () => {\n this.logger.debug(`Form for delivery: ${delivery.productSlug} was completed`);\n if (deliveryResponse) await this.updateDeliveryResponse(delivery.id, deliveryResponse.id, false, true);\n }\n );\n } catch (error) {\n this.logger.error(`Failed to load form for delivery: ${delivery.productSlug}`, error);\n }\n }\n }\n\n private matchesUrlPattern(url: string, pattern: string): boolean {\n // Extract pathname from URL (remove protocol, host, query params, etc.)\n let pathname: string;\n try {\n const urlObj = new URL(url);\n pathname = urlObj.pathname;\n } catch {\n // If URL parsing fails, assume it's already a pathname\n pathname = url.startsWith('/') ? url : `/${url}`;\n }\n\n // Normalize paths: remove trailing slash (except for root) and ensure leading slash\n const normalizePath = (p: string): string => {\n if (!p) return '/';\n let s = p.startsWith('/') ? p : `/${p}`;\n if (s.length > 1 && s.endsWith('/')) s = s.slice(0, -1);\n return s;\n };\n\n const normalizedPathname = normalizePath(pathname);\n const normalizedPattern = normalizePath(pattern);\n\n // Convert glob pattern to regex\n // First, replace glob tokens with placeholders, then escape regex special chars, then restore\n const regexPattern = normalizedPattern\n .replace(/\\*\\*/g, '§DOUBLE_STAR§')\n .replace(/\\*/g, '§SINGLE_STAR§')\n .replace(/\\?/g, '§QUESTION§')\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex special characters\n .replace(/§SINGLE_STAR§/g, '[^/]*') // * matches anything except /\n .replace(/§DOUBLE_STAR§/g, '.*') // ** matches anything including /\n .replace(/§QUESTION§/g, '.'); // ? matches single character\n\n const regex = new RegExp(`^${regexPattern}$`);\n const result = regex.test(normalizedPathname);\n\n this.logger.debug(`URL pattern matching`, {\n originalUrl: url,\n pathname,\n normalizedPathname,\n pattern,\n normalizedPattern,\n regexPattern,\n matches: result\n });\n\n return result;\n }\n\n private async createDeliveryResponse(delivery: Delivery): Promise<DeliveryResponse | undefined> {\n const response = await this.http.post<DeliveryResponse>(\n `/external/deliveries/${delivery.id}/responses`,\n {},\n { headers: this.headers }\n );\n if (!response.ok) {\n this.logger.error(`Failed to create delivery response for delivery: ${delivery.id}`, response.error);\n return undefined;\n }\n return response.data;\n }\n\n private async updateDeliveryResponse(\n deliveryId: number,\n deliveryResponseId: number,\n dismissed: boolean = false,\n completed: boolean = false\n ): Promise<void> {\n try {\n await this.http.patch(\n `/external/deliveries/${deliveryId}/responses/${deliveryResponseId}`,\n { wasDismissed: dismissed, wasCompleted: completed },\n { headers: this.headers }\n );\n } catch (error) {\n this.logger.error(`Failed to update delivery response: ${deliveryResponseId} for delivery: ${deliveryId}`, error);\n }\n }\n\n private async isDeliveryValid(delivery: Delivery, triggerId?: number): Promise<boolean> {\n const query = triggerId !== undefined ? `?triggerId=${triggerId}` : '';\n const response = await this.http.get<boolean>(`/external/deliveries/${delivery.id}/should-deliver${query}`, {\n headers: this.headers\n });\n if (!response.ok) {\n this.logger.error(`Failed to check delivery validity for delivery: ${delivery.id}`, response.error);\n return false;\n }\n return response.data;\n }\n}\n","/**\n * Logger utility for the SDK\n * Provides consistent logging across the SDK with different log levels\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\ninterface LoggerOptions {\n prefix?: string;\n enabled?: boolean;\n level?: LogLevel;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3\n};\n\nexport class Logger {\n private prefix: string;\n private enabled: boolean;\n private level: LogLevel;\n\n constructor(options: LoggerOptions = {}) {\n this.prefix = options.prefix || 'Nemme SDK';\n this.enabled = options.enabled !== undefined ? options.enabled : true;\n this.level = options.level || 'info';\n }\n\n private shouldLog(level: LogLevel): boolean {\n return this.enabled && LOG_LEVELS[level] >= LOG_LEVELS[this.level];\n }\n\n private formatMessage(message: string): string {\n return `[${this.prefix}] ${message}`;\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (this.shouldLog('debug')) {\n console.debug(this.formatMessage(message), ...args);\n }\n }\n\n info(message: string, ...args: unknown[]): void {\n if (this.shouldLog('info')) {\n console.info(this.formatMessage(message), ...args);\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (this.shouldLog('warn')) {\n console.warn(this.formatMessage(message), ...args);\n }\n }\n\n error(message: string, ...args: unknown[]): void {\n if (this.shouldLog('error')) {\n console.error(this.formatMessage(message), ...args);\n }\n }\n\n // Create a child logger with a new prefix\n child(prefix: string): Logger {\n return new Logger({\n prefix: `${this.prefix}:${prefix}`,\n enabled: this.enabled,\n level: this.level\n });\n }\n\n // Configure logger settings\n configure(options: LoggerOptions): void {\n if (options.prefix !== undefined) {\n this.prefix = options.prefix;\n }\n if (options.enabled !== undefined) {\n this.enabled = options.enabled;\n }\n if (options.level !== undefined) {\n this.level = options.level;\n }\n }\n}\n\n// Create and export a default logger instance\nexport const logger = new Logger();\n","import { config } from '../config';\nimport { logger } from '../utils';\n\n// Create a network-specific logger\nconst networkLogger = logger.child('network');\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\nexport interface RequestOptions {\n method?: HttpMethod;\n headers?: Record<string, string>;\n body?: unknown;\n params?: Record<string, string | number | boolean>;\n timeout?: number;\n}\n\nexport interface HttpResponse<T = unknown> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n ok: boolean;\n error?: {\n message: string;\n details?: unknown;\n };\n}\n\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Creates a URL with query parameters\n */\nconst createUrl = (endpoint: string, params?: Record<string, string | number | boolean>): string => {\n const url = new URL(endpoint, config.apiBaseUrl);\n\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n });\n }\n\n return url.toString();\n};\n\n/**\n * Global HTTP client for making API requests\n */\nexport const httpClient = {\n /**\n * Make an HTTP request\n */\n async request<T = unknown>(endpoint: string, options: RequestOptions = {}): Promise<HttpResponse<T>> {\n const { method = 'GET', headers = {}, body, params, timeout = DEFAULT_TIMEOUT } = options;\n\n const url = createUrl(endpoint, params);\n\n // Prepare headers with defaults\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n ...headers\n };\n\n // Prepare request options\n const requestOptions: RequestInit = {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined\n };\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n requestOptions.signal = controller.signal;\n\n try {\n const response = await fetch(url, requestOptions);\n\n // Parse response data\n let data: T;\n const contentType = response.headers.get('content-type');\n\n if (contentType && contentType.includes('application/json')) {\n data = await response.json();\n } else {\n const text = await response.text();\n try {\n data = JSON.parse(text) as T;\n } catch {\n data = text as unknown as T;\n }\n }\n\n const result: HttpResponse<T> = {\n data,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n ok: response.ok\n };\n\n // Handle error responses but don't throw\n if (!response.ok) {\n const errorMessage = `Request failed with status ${response.status}: ${response.statusText}`;\n networkLogger.error(errorMessage, {\n url,\n method,\n status: response.status,\n data: result.data\n });\n\n result.ok = false;\n result.error = {\n message: errorMessage,\n details: result.data\n };\n } else {\n result.ok = true;\n }\n\n return result;\n } catch (error) {\n // Log the error but don't throw\n let errorMessage = 'Network request failed';\n let errorDetails: Record<string, unknown>;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n errorMessage = `Request timeout after ${timeout}ms`;\n errorDetails = { timeout, url };\n } else {\n errorDetails = {\n message: error instanceof Error ? error.message : String(error),\n url,\n method\n };\n }\n\n networkLogger.error(errorMessage, errorDetails);\n\n // Return an error response instead of throwing\n const errorResponse: HttpResponse<T> = {\n data: {} as T,\n status: 0,\n statusText: errorMessage,\n headers: new Headers(),\n ok: false,\n error: {\n message: errorMessage,\n details: errorDetails\n }\n };\n\n return errorResponse;\n } finally {\n clearTimeout(timeoutId);\n }\n },\n\n /**\n * Convenience methods for common HTTP methods\n */\n async get<T = unknown>(endpoint: string, options: Omit<RequestOptions, 'method'> = {}): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'GET' });\n },\n\n async post<T = unknown>(\n endpoint: string,\n data?: unknown,\n options: Omit<RequestOptions, 'method' | 'body'> = {}\n ): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'POST', body: data });\n },\n\n async put<T = unknown>(\n endpoint: string,\n data?: unknown,\n options: Omit<RequestOptions, 'method' | 'body'> = {}\n ): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'PUT', body: data });\n },\n\n async patch<T = unknown>(\n endpoint: string,\n data?: unknown,\n options: Omit<RequestOptions, 'method' | 'body'> = {}\n ): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'PATCH', body: data });\n },\n\n async delete<T = unknown>(endpoint: string, options: Omit<RequestOptions, 'method'> = {}): Promise<HttpResponse<T>> {\n return this.request<T>(endpoint, { ...options, method: 'DELETE' });\n }\n};\n","import type { RetryingHttpClient } from '../client';\nimport { Logger } from '../utils';\n\nexport type Config = {\n logger: Logger;\n headers: Record<string, string>;\n http: RetryingHttpClient;\n sessionId: number;\n eventDefinitions: EventDefinition[];\n batchConfig?: boolean | BatchConfig;\n onPageView?: (url: string) => Promise<void>;\n onEvent?: (event: TrackEventOptions, url: string) => Promise<void>;\n trackUrlParamChanges?: boolean;\n getPage?: () => string;\n};\n\nexport type BatchConfig = {\n enabled?: boolean;\n size?: number;\n delayMs?: number;\n sendOnUnload?: boolean;\n};\n\nexport type TrackEventOptions<T extends string = string> = {\n eventKey: T;\n data?: Record<string, unknown>;\n};\n\ntype HistoryParams = Parameters<typeof history.pushState>;\n\ntype EventOptions = TrackEventOptions & {\n sessionId: number;\n timestamp: string;\n page: string;\n};\n\ntype EventProperties = {\n name: string;\n type: string;\n};\n\nexport type EventDefinition = {\n properties: EventProperties[];\n eventKey: string;\n};\n\n// Backend event-definition types are stored as \"STR\" | \"NUM\" | \"BOOL\"; map them\n// to their JavaScript `typeof` equivalents for runtime validation. Lowercase JS\n// type names pass through unchanged so legacy / test payloads still work.\nconst BACKEND_TYPE_TO_JS: Record<string, string> = {\n STR: 'string',\n NUM: 'number',\n BOOL: 'boolean'\n};\n\nconst resolveExpectedJsType = (definedType: string): string => BACKEND_TYPE_TO_JS[definedType] ?? definedType;\n\n/**\n * Manages event tracking, batching, and page view tracking\n */\nexport class TrackingManager {\n private logger: Logger;\n private headers: Record<string, string>;\n private http: RetryingHttpClient;\n private sessionId?: number;\n private eventDefinitions: EventDefinition[] = [];\n private onPageView?: (url: string) => Promise<void>;\n private onEvent?: (event: TrackEventOptions, url: string) => Promise<void>;\n private trackUrlParamChanges = true;\n private getPage?: () => string;\n\n // Batching\n private backlog: Array<EventOptions> = [];\n private flushTimeout: number | null = null;\n private batchConfig: Required<BatchConfig> = {\n enabled: true,\n size: 10,\n delayMs: 10000, // 10 seconds\n sendOnUnload: true\n };\n\n // Page view tracking\n private originalHistoryMethods: {\n pushState: typeof history.pushState;\n replaceState: typeof history.replaceState;\n } | null = null;\n\n private readonly EVENT_PAGE_VIEW = 'page_view';\n private lastTrackedPageKey?: string;\n\n constructor({\n logger,\n headers,\n http,\n sessionId,\n eventDefinitions,\n batchConfig,\n onPageView,\n onEvent,\n trackUrlParamChanges,\n getPage\n }: Config) {\n this.logger = logger;\n this.headers = headers;\n this.http = http;\n this.sessionId = sessionId;\n this.eventDefinitions = eventDefinitions;\n this.onPageView = onPageView;\n this.onEvent = onEvent;\n this.getPage = getPage;\n if (typeof trackUrlParamChanges === 'boolean') {\n this.trackUrlParamChanges = trackUrlParamChanges;\n }\n\n // Bind methods before setting up event listeners\n this.flushEvents = this.flushEvents.bind(this);\n this.handlePopState = this.handlePopState.bind(this);\n this.handleVisibilityChange = this.handleVisibilityChange.bind(this);\n\n this.setupBatching(batchConfig);\n }\n\n private setupBatching(batchConfig?: boolean | BatchConfig): void {\n if (typeof batchConfig === 'boolean') {\n this.batchConfig.enabled = batchConfig;\n } else if (batchConfig) {\n this.batchConfig = {\n ...this.batchConfig,\n ...batchConfig\n };\n }\n\n // Setup unload handler if batching is enabled\n if (this.batchConfig.enabled && this.batchConfig.sendOnUnload && typeof window !== 'undefined') {\n window.addEventListener('beforeunload', this.flushEvents);\n window.addEventListener('pagehide', this.flushEvents);\n }\n }\n\n public async setupPageViewTracking(): Promise<void> {\n if (typeof window === 'undefined') return;\n\n this.originalHistoryMethods = {\n pushState: history.pushState,\n replaceState: history.replaceState\n };\n\n const originalPushState = history.pushState;\n history.pushState = async (...args: HistoryParams) => {\n originalPushState.apply(history, args);\n await this.trackPageView();\n };\n\n const originalReplaceState = history.replaceState;\n history.replaceState = async (...args: HistoryParams) => {\n originalReplaceState.apply(history, args);\n await this.trackPageView();\n };\n\n window.addEventListener('popstate', this.handlePopState);\n document.addEventListener('visibilitychange', this.handleVisibilityChange);\n }\n\n public async track<T extends TrackEventOptions>(options: T): Promise<void> {\n if (!this.sessionId) {\n this.logger.warn('Tracking manager not initialized, cannot track event');\n return;\n }\n\n const { eventKey, data = {} } = options;\n this.logger.debug('Tracking event', { eventKey, data });\n const url = this.getPageUrl();\n const event: EventOptions = {\n sessionId: this.sessionId,\n eventKey,\n data: data || {},\n timestamp: new Date().toISOString(),\n page: url\n };\n\n if (!this.sanitizeEventData(event)) {\n return;\n }\n\n this.validateEvent(event);\n\n if (this.batchConfig.enabled) {\n this.backlog.push(event);\n this.scheduleFlush();\n } else {\n await this.sendEvents([event]);\n }\n\n if (eventKey === this.EVENT_PAGE_VIEW) {\n this.onPageView?.(url);\n } else {\n this.onEvent?.(event, url);\n }\n }\n\n public async trackPageView(): Promise<void> {\n if (typeof window === 'undefined') return;\n\n const url = this.getPageUrl();\n\n if (!this.trackUrlParamChanges) {\n const currentPageKey = this.getPageKeyWithoutQuery(url);\n\n if (this.lastTrackedPageKey === currentPageKey) {\n return;\n }\n\n this.lastTrackedPageKey = currentPageKey;\n }\n\n await this.track({\n eventKey: this.EVENT_PAGE_VIEW,\n data: {\n title: document.title\n }\n });\n }\n\n private getPageUrl(): string {\n if (this.getPage) {\n return this.getPage();\n }\n\n const url = window.location.href;\n\n if (window.location.protocol === 'file:') {\n const filename = window.location.pathname.split('/').pop() || '';\n return `/${filename}${window.location.hash}`;\n }\n\n return url;\n }\n\n private getPageKeyWithoutQuery(url: string): string {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}${parsed.hash}`;\n } catch {\n const [base] = url.split('?');\n return base;\n }\n }\n\n /**\n * Manually flush any pending events\n */\n public async flush(): Promise<void> {\n return this.flushEvents();\n }\n\n /**\n * Clean up event listeners and restore original history methods\n */\n public destroy(): void {\n if (this.originalHistoryMethods) {\n history.pushState = this.originalHistoryMethods.pushState;\n history.replaceState = this.originalHistoryMethods.replaceState;\n this.originalHistoryMethods = null;\n }\n\n if (typeof window !== 'undefined') {\n window.removeEventListener('popstate', this.handlePopState);\n document.removeEventListener('visibilitychange', this.handleVisibilityChange);\n window.removeEventListener('beforeunload', this.flushEvents);\n window.removeEventListener('pagehide', this.flushEvents);\n }\n\n // Clear any pending flush timeout\n if (this.flushTimeout) {\n clearTimeout(this.flushTimeout);\n this.flushTimeout = null;\n }\n }\n\n private sanitizeEventData(event: EventOptions): boolean {\n if (event.eventKey === this.EVENT_PAGE_VIEW) return true;\n if (!event.data) return true;\n\n const data = event.data;\n\n for (const key of Object.keys(data)) {\n const value = data[key];\n\n if (typeof value === 'string' && value === '[object Object]') {\n this.logger.error(`Property ${key} contains [object Object], dropping event ${event.eventKey}`);\n return false;\n }\n\n const valueType = typeof value;\n\n if (valueType !== 'boolean' && valueType !== 'number' && valueType !== 'string') {\n this.logger.warn(`Property ${key} has unsupported type ${valueType}, removing from event ${event.eventKey}`);\n delete data[key];\n continue;\n }\n\n if (typeof value === 'string') {\n data[key] = value.toLowerCase();\n }\n }\n\n return true;\n }\n\n private validateEvent(event: EventOptions): void {\n if (event.eventKey === this.EVENT_PAGE_VIEW) return;\n\n const eventDefinition = this.eventDefinitions.find(def => def.eventKey === event.eventKey);\n if (!eventDefinition) {\n this.logger.warn(`Event ${event.eventKey} is not registered`);\n return;\n }\n\n const definedProperties = eventDefinition.properties;\n const eventProperties = event.data;\n if (!eventProperties) return;\n\n for (const eventPropertyKey of Object.keys(eventProperties)) {\n const eventProperty = eventProperties[eventPropertyKey];\n const definedProperty = definedProperties.find(prop => prop.name === eventPropertyKey);\n\n if (!definedProperty) {\n this.logger.warn(`Property ${eventPropertyKey} is not registered for event ${event.eventKey}`);\n continue;\n }\n\n const expectedJsType = resolveExpectedJsType(definedProperty.type);\n if (typeof eventProperty !== expectedJsType) {\n this.logger.warn(\n `Property ${definedProperty.name} has type ${expectedJsType} but value is of type ${typeof eventProperty}`\n );\n }\n }\n }\n\n private async handlePopState(): Promise<void> {\n await this.trackPageView();\n }\n\n private async handleVisibilityChange(): Promise<void> {\n if (document.visibilityState === 'visible') {\n await this.trackPageView();\n }\n }\n\n private scheduleFlush(): void {\n if (this.backlog.length >= this.batchConfig.size) {\n this.flushEvents();\n return;\n }\n\n if (!this.flushTimeout) {\n this.flushTimeout = window.setTimeout(() => {\n this.flushEvents();\n }, this.batchConfig.delayMs);\n }\n }\n\n private async flushEvents(): Promise<void> {\n if (this.flushTimeout) {\n clearTimeout(this.flushTimeout);\n this.flushTimeout = null;\n }\n\n if (this.backlog.length === 0) return;\n\n const eventsToSend = [...this.backlog];\n this.backlog = [];\n\n try {\n await this.sendEvents(eventsToSend);\n } catch (error) {\n this.backlog.unshift(...eventsToSend);\n this.logger.error('Failed to send batched events', error);\n }\n }\n\n private async sendEvents(events: Array<EventOptions>): Promise<void> {\n if (events.length === 0) return;\n\n // Captured as late as possible — paired with the server's `received_at`,\n // the delta `sentAt - received_at` is the client clock skew the server\n // subtracts from each event's `timestamp` to realign to its own clock.\n const sentAt = new Date().toISOString();\n\n if (events.length === 1) {\n await this.http.post('/external/trackings/track', { ...events[0], sentAt }, { headers: this.headers });\n } else {\n await this.http.post('/external/trackings/batch', { sentAt, events }, { headers: this.headers });\n }\n }\n}\n","import { DeliveryManager } from './deliveries/delivery-manager';\nimport { httpClient, HttpResponse } from './networking';\nimport { BatchConfig, EventDefinition, TrackEventOptions, TrackingManager } from './tracking/tracking-manager';\nimport { FormConfig } from './types/forms';\nimport { Logger, logger } from './utils';\n\n// Subset of the httpClient surface that the managers use, plus the same\n// shapes — so the retry wrapper is a drop-in replacement.\nexport type RetryingHttpClient = Pick<typeof httpClient, 'get' | 'post' | 'patch'>;\n\nexport type ClientConfigType = {\n userIdentifier: string;\n secretKey?: string;\n debug?: boolean;\n batch?: boolean | BatchConfig;\n formConfig?: FormConfig;\n deactivate?: boolean;\n trackUrlParamChanges?: boolean;\n getPage?: () => string;\n // Optional cohort attributes. When provided, persisted on the Session row\n // so analytics can filter/aggregate by group and role. Changing either\n // value between init calls starts a new Session.\n group?: string;\n role?: string;\n};\n\n// Re-export types for backward compatibility\nexport type { BatchConfig, TrackEventOptions } from './tracking/tracking-manager';\n\ntype InitializeResponse = {\n sessionId: number;\n sessionToken: string;\n eventDefinitions: EventDefinition[];\n};\n\n// Server token TTL is 3600s; refresh well before that so the SDK never sends\n// an expired bearer in normal usage. Refresh is best-effort and silent on\n// failure — the next refresh attempt will pick up where this one left off.\nconst TOKEN_REFRESH_INTERVAL_MS = 50 * 60 * 1000;\n\n/**\n * Main client class for the Nemme SDK\n * Coordinates initialization, tracking, and form management\n */\nexport class NemmeClient {\n readonly clientKey: string;\n private _userIdentifier?: string;\n private _group?: string;\n private _role?: string;\n private _initialized = false;\n private _lastInitError: Error | null = null;\n private clientLogger: Logger = logger.child('client');\n private headers: Record<string, string> = {};\n private formConfig?: FormConfig;\n\n get userIdentifier(): string | undefined {\n return this._userIdentifier;\n }\n\n get initialized(): boolean {\n return this._initialized;\n }\n\n get lastInitError(): Error | null {\n return this._lastInitError;\n }\n\n constructor(clientKey: string) {\n this.clientKey = clientKey;\n }\n\n // Module managers\n private trackingManager?: TrackingManager;\n private tokenRefreshTimer?: ReturnType<typeof setTimeout>;\n // De-duplicates concurrent refreshes: many in-flight calls all getting 401\n // at roughly the same time must only fire one /initialize round-trip.\n private inflightRefresh?: Promise<void>;\n private http: RetryingHttpClient = this.createRetryingHttpClient();\n\n /**\n * Create a new Nemme SDK client instance\n */\n public async init({\n userIdentifier,\n secretKey,\n debug = false,\n batch,\n formConfig,\n deactivate = false,\n trackUrlParamChanges,\n getPage,\n group,\n role\n }: ClientConfigType): Promise<NemmeClient> {\n this._initialized = false;\n this._lastInitError = null;\n this.trackingManager?.destroy();\n this.trackingManager = undefined;\n\n if (deactivate) {\n this.clientLogger.info('Nemme client deactivated and will stop initialization.');\n this._initialized = true;\n this._lastInitError = null;\n this.clientLogger.configure({ enabled: false });\n return this;\n }\n if (!userIdentifier) throw new Error('userIdentifier is required parameter');\n this._userIdentifier = userIdentifier;\n this._group = group;\n this._role = role;\n\n // Configure logger based on debug setting\n this.clientLogger.configure({\n enabled: debug,\n level: debug ? 'debug' : 'info'\n });\n\n this.headers = {\n 'X-Client-Key': this.clientKey,\n 'X-User-Id': this._userIdentifier,\n ...(secretKey && { 'X-Client-Secret': secretKey })\n };\n\n this.formConfig = formConfig;\n\n try {\n const session = await this.initializeSession();\n await this.initializeManagers(session, batch, trackUrlParamChanges, getPage);\n this._initialized = true;\n this._lastInitError = null;\n this.clientLogger.info('Nemme client initialized', {\n clientKey: this.clientKey,\n userIdentifier: this._userIdentifier\n });\n } catch (error) {\n this.clientLogger.error('Error during initialization:', error);\n this._initialized = false;\n this._lastInitError = error instanceof Error ? error : new Error('Error during initialization');\n }\n\n return this;\n }\n\n public async flush(): Promise<void> {\n return this.trackingManager?.flush();\n }\n\n public destroy(): void {\n this.trackingManager?.destroy();\n this.clearTokenRefresh();\n }\n\n public async track<T extends TrackEventOptions>(options: T): Promise<void> {\n if (!this._initialized || !this.trackingManager) {\n this.clientLogger.warn('Nemme client not initialized, some operations may fail');\n return;\n }\n\n return this.trackingManager.track(options);\n }\n\n private buildInitializeBody(): Record<string, string> {\n const body: Record<string, string> = {};\n if (this._group !== undefined) body.group = this._group;\n if (this._role !== undefined) body.role = this._role;\n return body;\n }\n\n private async initializeSession(): Promise<InitializeResponse> {\n try {\n // Snapshot the current headers so the captured call reflects the\n // pre-mutation state (X-User-Id present, no Authorization yet).\n const initHeaders = { ...this.headers };\n const response: HttpResponse<InitializeResponse> = await httpClient.post(\n '/external/trackings/initialize',\n this.buildInitializeBody(),\n { headers: initHeaders }\n );\n if (!response.ok) {\n throw new Error(response.error?.message || 'Request for initialising session failed');\n }\n // R17: switch to bearer-token auth for all subsequent calls. Mutating\n // this.headers in place propagates to managers that captured the ref.\n this.applySessionToken(response.data.sessionToken);\n this.scheduleTokenRefresh();\n return response.data;\n } catch (error) {\n this.clientLogger.error('Error during initialization', error);\n throw error;\n }\n }\n\n private applySessionToken(token: string): void {\n this.headers['Authorization'] = `Bearer ${token}`;\n delete this.headers['X-User-Id'];\n }\n\n private scheduleTokenRefresh(): void {\n if (typeof window === 'undefined') return;\n this.clearTokenRefresh();\n this.tokenRefreshTimer = setTimeout(() => {\n this.refreshToken();\n }, TOKEN_REFRESH_INTERVAL_MS);\n }\n\n private clearTokenRefresh(): void {\n if (this.tokenRefreshTimer) {\n clearTimeout(this.tokenRefreshTimer);\n this.tokenRefreshTimer = undefined;\n }\n }\n\n private async refreshToken(): Promise<void> {\n // Initialize still needs X-User-Id (server mints the token from it). Build\n // a one-shot header snapshot so this.headers stays bearer-only for\n // concurrent in-flight calls from the managers.\n const refreshHeaders = { ...this.headers };\n if (this._userIdentifier) {\n refreshHeaders['X-User-Id'] = this._userIdentifier;\n delete refreshHeaders['Authorization'];\n }\n // Refresh re-sends group/role (not {}). If the original Session is still\n // within the 30-min dedup window the server reuses it; if it aged out,\n // the server creates a new Session row carrying the same cohort\n // attributes, which is the correct behavior — not a bug.\n try {\n const response: HttpResponse<InitializeResponse> = await httpClient.post(\n '/external/trackings/initialize',\n this.buildInitializeBody(),\n { headers: refreshHeaders }\n );\n if (response.ok) {\n this.applySessionToken(response.data.sessionToken);\n } else {\n this.clientLogger.warn('SDK token refresh failed; will retry', response.error);\n }\n } catch (error) {\n this.clientLogger.warn('SDK token refresh threw', error);\n } finally {\n this.scheduleTokenRefresh();\n }\n }\n\n /**\n * Wraps httpClient so that any call returning 401 triggers a single\n * refresh+retry. Concurrent 401s coalesce into one refresh round-trip.\n *\n * Refresh mutates `this.headers` in place; managers hold the same reference\n * so the retried call automatically picks up the fresh bearer.\n */\n private createRetryingHttpClient(): RetryingHttpClient {\n const withRetry = async <T>(call: () => Promise<HttpResponse<T>>): Promise<HttpResponse<T>> => {\n const first = await call();\n if (first.status !== 401) return first;\n this.clientLogger.debug('SDK got 401; refreshing token and retrying once');\n if (!this.inflightRefresh) {\n this.inflightRefresh = this.refreshToken().finally(() => {\n this.inflightRefresh = undefined;\n });\n }\n await this.inflightRefresh;\n return call();\n };\n return {\n get: <T>(endpoint: string, options: Parameters<typeof httpClient.get>[1] = {}) =>\n withRetry<T>(() => httpClient.get<T>(endpoint, options)),\n post: <T>(endpoint: string, data?: unknown, options: Parameters<typeof httpClient.post>[2] = {}) =>\n withRetry<T>(() => httpClient.post<T>(endpoint, data, options)),\n patch: <T>(endpoint: string, data?: unknown, options: Parameters<typeof httpClient.patch>[2] = {}) =>\n withRetry<T>(() => httpClient.patch<T>(endpoint, data, options))\n };\n }\n\n private async initializeManagers(\n session: InitializeResponse,\n batchConfig?: boolean | BatchConfig,\n trackUrlParamChanges?: boolean,\n getPage?: () => string\n ): Promise<void> {\n const deliveryManager = new DeliveryManager(this.clientLogger, this.headers, this.http, this.formConfig);\n const trackingManager = new TrackingManager({\n logger: this.clientLogger,\n headers: this.headers,\n http: this.http,\n sessionId: session.sessionId,\n eventDefinitions: session.eventDefinitions,\n batchConfig,\n trackUrlParamChanges,\n getPage,\n onPageView: async (url: string) => {\n await deliveryManager.evaluateDeliveryTriggers({ url });\n },\n onEvent: async (event: TrackEventOptions, url: string) => {\n await deliveryManager.evaluateDeliveryTriggers({ eventKey: event.eventKey, eventData: event.data, url });\n }\n });\n\n await deliveryManager.loadDeliveries();\n\n if (typeof window !== 'undefined') {\n await trackingManager.setupPageViewTracking();\n await trackingManager.trackPageView();\n await trackingManager.flush();\n }\n\n this.trackingManager = trackingManager;\n }\n}\n\nexport const initAsModule = (clientKey: string) => new NemmeClient(clientKey);\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`./config-BnABfG_n.js`);var t=class{logger;headers;http;formConfig;formManager;deliveries=[];constructor(e,t,n,r){this.logger=e,this.headers=t,this.http=n,this.formConfig=r}async loadDeliveries(){this.logger.debug(`Loading deliveries`);let e=await this.http.get(`/external/deliveries`,{headers:this.headers});if(!e.ok){this.logger.error(`Failed to load deliveries`,e.error),this.deliveries=[];return}this.deliveries=e.data,this.logger.info(`Loaded ${this.deliveries.length} deliveries`)}async evaluateDeliveryTriggers(e){for(let t of this.deliveries)for(let n of t.triggers)if(this.shouldTriggerDelivery(n,e)){if(!await this.isDeliveryValid(t,n.id)){this.logger.debug(`Delivery ${t.id} is not valid, skipping`);continue}await this.executeDelivery(t,n);return}}shouldTriggerDelivery(e,t){if(this.hasActiveDelivery())return!1;switch(e.triggerType){case`page_url`:return e.urlPattern?this.matchesUrlPattern(t.url,e.urlPattern):!0;case`custom_event`:return e.eventKey?t.eventKey===e.eventKey:!1;default:return!1}}hasActiveDelivery(){return document.getElementById(`nm`)!==null}async executeDelivery(e,t){if(e.productType===`FRM`){this.logger.debug(`Triggering form delivery: ${e.productSlug}`,{triggerType:t.triggerType,urlPattern:t.urlPattern,eventKey:t.eventKey});try{let t=await this.createDeliveryResponse(e);if(!this.formManager){let e=await Promise.resolve().then(()=>require(`./form-manager-3PwpV6gK.js`)).then(e=>e.n);this.formManager=new e.FormManager(this.logger,this.headers,this.http,this.formConfig)}await this.formManager.fetchAndDisplayForm(e.productSlug,async()=>{this.logger.debug(`Form for delivery: ${e.productSlug} was cancelled`),t&&await this.updateDeliveryResponse(e.id,t.id,!0)},async()=>{this.logger.debug(`Form for delivery: ${e.productSlug} was completed`),t&&await this.updateDeliveryResponse(e.id,t.id,!1,!0)})}catch(t){this.logger.error(`Failed to load form for delivery: ${e.productSlug}`,t)}}}matchesUrlPattern(e,t){let n;try{n=new URL(e).pathname}catch{n=e.startsWith(`/`)?e:`/${e}`}let r=e=>{if(!e)return`/`;let t=e.startsWith(`/`)?e:`/${e}`;return t.length>1&&t.endsWith(`/`)&&(t=t.slice(0,-1)),t},i=r(n),a=r(t),o=a.replace(/\*\*/g,`§DOUBLE_STAR§`).replace(/\*/g,`§SINGLE_STAR§`).replace(/\?/g,`§QUESTION§`).replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/§SINGLE_STAR§/g,`[^/]*`).replace(/§DOUBLE_STAR§/g,`.*`).replace(/§QUESTION§/g,`.`),s=RegExp(`^${o}$`).test(i);return this.logger.debug(`URL pattern matching`,{originalUrl:e,pathname:n,normalizedPathname:i,pattern:t,normalizedPattern:a,regexPattern:o,matches:s}),s}async createDeliveryResponse(e){let t=await this.http.post(`/external/deliveries/${e.id}/responses`,{},{headers:this.headers});if(!t.ok){this.logger.error(`Failed to create delivery response for delivery: ${e.id}`,t.error);return}return t.data}async updateDeliveryResponse(e,t,n=!1,r=!1){try{await this.http.patch(`/external/deliveries/${e}/responses/${t}`,{wasDismissed:n,wasCompleted:r},{headers:this.headers})}catch(n){this.logger.error(`Failed to update delivery response: ${t} for delivery: ${e}`,n)}}async isDeliveryValid(e,t){let n=t===void 0?``:`?triggerId=${t}`,r=await this.http.get(`/external/deliveries/${e.id}/should-deliver${n}`,{headers:this.headers});return r.ok?r.data:(this.logger.error(`Failed to check delivery validity for delivery: ${e.id}`,r.error),!1)}},n={debug:0,info:1,warn:2,error:3},r=new class e{prefix;enabled;level;constructor(e={}){this.prefix=e.prefix||`Nemme SDK`,this.enabled=e.enabled===void 0?!0:e.enabled,this.level=e.level||`info`}shouldLog(e){return this.enabled&&n[e]>=n[this.level]}formatMessage(e){return`[${this.prefix}] ${e}`}debug(e,...t){this.shouldLog(`debug`)&&console.debug(this.formatMessage(e),...t)}info(e,...t){this.shouldLog(`info`)&&console.info(this.formatMessage(e),...t)}warn(e,...t){this.shouldLog(`warn`)&&console.warn(this.formatMessage(e),...t)}error(e,...t){this.shouldLog(`error`)&&console.error(this.formatMessage(e),...t)}child(t){return new e({prefix:`${this.prefix}:${t}`,enabled:this.enabled,level:this.level})}configure(e){e.prefix!==void 0&&(this.prefix=e.prefix),e.enabled!==void 0&&(this.enabled=e.enabled),e.level!==void 0&&(this.level=e.level)}},i=r.child(`network`),a=3e4,o=(t,n)=>{let r=new URL(t,e.t.apiBaseUrl);return n&&Object.entries(n).forEach(([e,t])=>{t!=null&&r.searchParams.append(e,String(t))}),r.toString()},s={async request(e,t={}){let{method:n=`GET`,headers:r={},body:s,params:c,timeout:l=a}=t,u=o(e,c),d={method:n,headers:{"Content-Type":`application/json`,Accept:`application/json`,...r},body:s?JSON.stringify(s):void 0},f=new AbortController,p=setTimeout(()=>f.abort(),l);d.signal=f.signal;try{let e=await fetch(u,d),t,r=e.headers.get(`content-type`);if(r&&r.includes(`application/json`))t=await e.json();else{let n=await e.text();try{t=JSON.parse(n)}catch{t=n}}let a={data:t,status:e.status,statusText:e.statusText,headers:e.headers,ok:e.ok};if(e.ok)a.ok=!0;else{let t=`Request failed with status ${e.status}: ${e.statusText}`;i.error(t,{url:u,method:n,status:e.status,data:a.data}),a.ok=!1,a.error={message:t,details:a.data}}return a}catch(e){let t=`Network request failed`,r;return e instanceof DOMException&&e.name===`AbortError`?(t=`Request timeout after ${l}ms`,r={timeout:l,url:u}):r={message:e instanceof Error?e.message:String(e),url:u,method:n},i.error(t,r),{data:{},status:0,statusText:t,headers:new Headers,ok:!1,error:{message:t,details:r}}}finally{clearTimeout(p)}},async get(e,t={}){return this.request(e,{...t,method:`GET`})},async post(e,t,n={}){return this.request(e,{...n,method:`POST`,body:t})},async put(e,t,n={}){return this.request(e,{...n,method:`PUT`,body:t})},async patch(e,t,n={}){return this.request(e,{...n,method:`PATCH`,body:t})},async delete(e,t={}){return this.request(e,{...t,method:`DELETE`})}},c={STR:`string`,NUM:`number`,BOOL:`boolean`},l=e=>c[e]??e,u=class{logger;headers;http;sessionId;eventDefinitions=[];onPageView;onEvent;trackUrlParamChanges=!0;getPage;backlog=[];flushTimeout=null;batchConfig={enabled:!0,size:10,delayMs:1e4,sendOnUnload:!0};originalHistoryMethods=null;EVENT_PAGE_VIEW=`page_view`;lastTrackedPageKey;constructor({logger:e,headers:t,http:n,sessionId:r,eventDefinitions:i,batchConfig:a,onPageView:o,onEvent:s,trackUrlParamChanges:c,getPage:l}){this.logger=e,this.headers=t,this.http=n,this.sessionId=r,this.eventDefinitions=i,this.onPageView=o,this.onEvent=s,this.getPage=l,typeof c==`boolean`&&(this.trackUrlParamChanges=c),this.flushEvents=this.flushEvents.bind(this),this.handlePopState=this.handlePopState.bind(this),this.handleVisibilityChange=this.handleVisibilityChange.bind(this),this.setupBatching(a)}setupBatching(e){typeof e==`boolean`?this.batchConfig.enabled=e:e&&(this.batchConfig={...this.batchConfig,...e}),this.batchConfig.enabled&&this.batchConfig.sendOnUnload&&typeof window<`u`&&(window.addEventListener(`beforeunload`,this.flushEvents),window.addEventListener(`pagehide`,this.flushEvents))}async setupPageViewTracking(){if(typeof window>`u`)return;this.originalHistoryMethods={pushState:history.pushState,replaceState:history.replaceState};let e=history.pushState;history.pushState=async(...t)=>{e.apply(history,t),await this.trackPageView()};let t=history.replaceState;history.replaceState=async(...e)=>{t.apply(history,e),await this.trackPageView()},window.addEventListener(`popstate`,this.handlePopState),document.addEventListener(`visibilitychange`,this.handleVisibilityChange)}async track(e){if(!this.sessionId){this.logger.warn(`Tracking manager not initialized, cannot track event`);return}let{eventKey:t,data:n={}}=e;this.logger.debug(`Tracking event`,{eventKey:t,data:n});let r=this.getPageUrl(),i={sessionId:this.sessionId,eventKey:t,data:n||{},timestamp:new Date().toISOString(),page:r};this.sanitizeEventData(i)&&(this.validateEvent(i),this.batchConfig.enabled?(this.backlog.push(i),this.scheduleFlush()):await this.sendEvents([i]),t===this.EVENT_PAGE_VIEW?this.onPageView?.(r):this.onEvent?.(i,r))}async trackPageView(){if(typeof window>`u`)return;let e=this.getPageUrl();if(!this.trackUrlParamChanges){let t=this.getPageKeyWithoutQuery(e);if(this.lastTrackedPageKey===t)return;this.lastTrackedPageKey=t}await this.track({eventKey:this.EVENT_PAGE_VIEW,data:{title:document.title}})}getPageUrl(){if(this.getPage)return this.getPage();let e=window.location.href;return window.location.protocol===`file:`?`/${window.location.pathname.split(`/`).pop()||``}${window.location.hash}`:e}getPageKeyWithoutQuery(e){try{let t=new URL(e);return`${t.origin}${t.pathname}${t.hash}`}catch{let[t]=e.split(`?`);return t}}async flush(){return this.flushEvents()}destroy(){this.originalHistoryMethods&&=(history.pushState=this.originalHistoryMethods.pushState,history.replaceState=this.originalHistoryMethods.replaceState,null),typeof window<`u`&&(window.removeEventListener(`popstate`,this.handlePopState),document.removeEventListener(`visibilitychange`,this.handleVisibilityChange),window.removeEventListener(`beforeunload`,this.flushEvents),window.removeEventListener(`pagehide`,this.flushEvents)),this.flushTimeout&&=(clearTimeout(this.flushTimeout),null)}sanitizeEventData(e){if(e.eventKey===this.EVENT_PAGE_VIEW||!e.data)return!0;let t=e.data;for(let n of Object.keys(t)){let r=t[n];if(typeof r==`string`&&r===`[object Object]`)return this.logger.error(`Property ${n} contains [object Object], dropping event ${e.eventKey}`),!1;let i=typeof r;if(i!==`boolean`&&i!==`number`&&i!==`string`){this.logger.warn(`Property ${n} has unsupported type ${i}, removing from event ${e.eventKey}`),delete t[n];continue}typeof r==`string`&&(t[n]=r.toLowerCase())}return!0}validateEvent(e){if(e.eventKey===this.EVENT_PAGE_VIEW)return;let t=this.eventDefinitions.find(t=>t.eventKey===e.eventKey);if(!t){this.logger.warn(`Event ${e.eventKey} is not registered`);return}let n=t.properties,r=e.data;if(r)for(let t of Object.keys(r)){let i=r[t],a=n.find(e=>e.name===t);if(!a){this.logger.warn(`Property ${t} is not registered for event ${e.eventKey}`);continue}let o=l(a.type);typeof i!==o&&this.logger.warn(`Property ${a.name} has type ${o} but value is of type ${typeof i}`)}}async handlePopState(){await this.trackPageView()}async handleVisibilityChange(){document.visibilityState===`visible`&&await this.trackPageView()}scheduleFlush(){if(this.backlog.length>=this.batchConfig.size){this.flushEvents();return}this.flushTimeout||=window.setTimeout(()=>{this.flushEvents()},this.batchConfig.delayMs)}async flushEvents(){if(this.flushTimeout&&=(clearTimeout(this.flushTimeout),null),this.backlog.length===0)return;let e=[...this.backlog];this.backlog=[];try{await this.sendEvents(e)}catch(t){this.backlog.unshift(...e),this.logger.error(`Failed to send batched events`,t)}}async sendEvents(e){if(e.length===0)return;let t=new Date().toISOString();e.length===1?await this.http.post(`/external/trackings/track`,{...e[0],sentAt:t},{headers:this.headers}):await this.http.post(`/external/trackings/batch`,{sentAt:t,events:e},{headers:this.headers})}},d=3e3*1e3,f=class{clientKey;_userIdentifier;_initialized=!1;_lastInitError=null;clientLogger=r.child(`client`);headers={};formConfig;get userIdentifier(){return this._userIdentifier}get initialized(){return this._initialized}get lastInitError(){return this._lastInitError}constructor(e){this.clientKey=e}trackingManager;tokenRefreshTimer;inflightRefresh;http=this.createRetryingHttpClient();async init({userIdentifier:e,secretKey:t,debug:n=!1,batch:r,formConfig:i,deactivate:a=!1,trackUrlParamChanges:o,getPage:s}){if(this._initialized=!1,this._lastInitError=null,this.trackingManager?.destroy(),this.trackingManager=void 0,a)return this.clientLogger.info(`Nemme client deactivated and will stop initialization.`),this._initialized=!0,this._lastInitError=null,this.clientLogger.configure({enabled:!1}),this;if(!e)throw Error(`userIdentifier is required parameter`);this._userIdentifier=e,this.clientLogger.configure({enabled:n,level:n?`debug`:`info`}),this.headers={"X-Client-Key":this.clientKey,"X-User-Id":this._userIdentifier,...t&&{"X-Client-Secret":t}},this.formConfig=i;try{let e=await this.initializeSession();await this.initializeManagers(e,r,o,s),this._initialized=!0,this._lastInitError=null,this.clientLogger.info(`Nemme client initialized`,{clientKey:this.clientKey,userIdentifier:this._userIdentifier})}catch(e){this.clientLogger.error(`Error during initialization:`,e),this._initialized=!1,this._lastInitError=e instanceof Error?e:Error(`Error during initialization`)}return this}async flush(){return this.trackingManager?.flush()}destroy(){this.trackingManager?.destroy(),this.clearTokenRefresh()}async track(e){if(!this._initialized||!this.trackingManager){this.clientLogger.warn(`Nemme client not initialized, some operations may fail`);return}return this.trackingManager.track(e)}async initializeSession(){try{let e={...this.headers},t=await s.post(`/external/trackings/initialize`,{},{headers:e});if(!t.ok)throw Error(t.error?.message||`Request for initialising session failed`);return this.applySessionToken(t.data.sessionToken),this.scheduleTokenRefresh(),t.data}catch(e){throw this.clientLogger.error(`Error during initialization`,e),e}}applySessionToken(e){this.headers.Authorization=`Bearer ${e}`,delete this.headers[`X-User-Id`]}scheduleTokenRefresh(){typeof window>`u`||(this.clearTokenRefresh(),this.tokenRefreshTimer=setTimeout(()=>{this.refreshToken()},d))}clearTokenRefresh(){this.tokenRefreshTimer&&=(clearTimeout(this.tokenRefreshTimer),void 0)}async refreshToken(){let e={...this.headers};this._userIdentifier&&(e[`X-User-Id`]=this._userIdentifier,delete e.Authorization);try{let t=await s.post(`/external/trackings/initialize`,{},{headers:e});t.ok?this.applySessionToken(t.data.sessionToken):this.clientLogger.warn(`SDK token refresh failed; will retry`,t.error)}catch(e){this.clientLogger.warn(`SDK token refresh threw`,e)}finally{this.scheduleTokenRefresh()}}createRetryingHttpClient(){let e=async e=>{let t=await e();return t.status===401?(this.clientLogger.debug(`SDK got 401; refreshing token and retrying once`),this.inflightRefresh||=this.refreshToken().finally(()=>{this.inflightRefresh=void 0}),await this.inflightRefresh,e()):t};return{get:(t,n={})=>e(()=>s.get(t,n)),post:(t,n,r={})=>e(()=>s.post(t,n,r)),patch:(t,n,r={})=>e(()=>s.patch(t,n,r))}}async initializeManagers(e,n,r,i){let a=new t(this.clientLogger,this.headers,this.http,this.formConfig),o=new u({logger:this.clientLogger,headers:this.headers,http:this.http,sessionId:e.sessionId,eventDefinitions:e.eventDefinitions,batchConfig:n,trackUrlParamChanges:r,getPage:i,onPageView:async e=>{await a.evaluateDeliveryTriggers({url:e})},onEvent:async(e,t)=>{await a.evaluateDeliveryTriggers({eventKey:e.eventKey,eventData:e.data,url:t})}});await a.loadDeliveries(),typeof window<`u`&&(await o.setupPageViewTracking(),await o.trackPageView(),await o.flush()),this.trackingManager=o}},p=e=>new f(e);Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return f}});
2
- //# sourceMappingURL=client-BYPXJmEU.js.map
1
+ const e=require("./config-hiNBGctX.js");var t=class{logger;headers;http;formConfig;formManager;deliveries=[];constructor(e,t,n,r){this.logger=e,this.headers=t,this.http=n,this.formConfig=r}async loadDeliveries(){this.logger.debug(`Loading deliveries`);let e=await this.http.get(`/external/deliveries`,{headers:this.headers});if(!e.ok){this.logger.error(`Failed to load deliveries`,e.error),this.deliveries=[];return}this.deliveries=e.data,this.logger.info(`Loaded ${this.deliveries.length} deliveries`)}async evaluateDeliveryTriggers(e){for(let t of this.deliveries)for(let n of t.triggers)if(this.shouldTriggerDelivery(n,e)){if(!await this.isDeliveryValid(t,n.id)){this.logger.debug(`Delivery ${t.id} is not valid, skipping`);continue}await this.executeDelivery(t,n);return}}shouldTriggerDelivery(e,t){if(this.hasActiveDelivery())return!1;switch(e.triggerType){case`page_url`:return e.urlPattern?this.matchesUrlPattern(t.url,e.urlPattern):!0;case`custom_event`:return e.eventKey?t.eventKey===e.eventKey:!1;default:return!1}}hasActiveDelivery(){return document.getElementById(`nm`)!==null}async executeDelivery(e,t){if(e.productType===`FRM`){this.logger.debug(`Triggering form delivery: ${e.productSlug}`,{triggerType:t.triggerType,urlPattern:t.urlPattern,eventKey:t.eventKey});try{let t=await this.createDeliveryResponse(e);if(!this.formManager){let e=await Promise.resolve().then(()=>require("./form-manager-BG1qr31N.js")).then(e=>e.n);this.formManager=new e.FormManager(this.logger,this.headers,this.http,this.formConfig)}await this.formManager.fetchAndDisplayForm(e.productSlug,async()=>{this.logger.debug(`Form for delivery: ${e.productSlug} was cancelled`),t&&await this.updateDeliveryResponse(e.id,t.id,!0)},async()=>{this.logger.debug(`Form for delivery: ${e.productSlug} was completed`),t&&await this.updateDeliveryResponse(e.id,t.id,!1,!0)})}catch(t){this.logger.error(`Failed to load form for delivery: ${e.productSlug}`,t)}}}matchesUrlPattern(e,t){let n;try{n=new URL(e).pathname}catch{n=e.startsWith(`/`)?e:`/${e}`}let r=e=>{if(!e)return`/`;let t=e.startsWith(`/`)?e:`/${e}`;return t.length>1&&t.endsWith(`/`)&&(t=t.slice(0,-1)),t},i=r(n),a=r(t),o=a.replace(/\*\*/g,`§DOUBLE_STAR§`).replace(/\*/g,`§SINGLE_STAR§`).replace(/\?/g,`§QUESTION§`).replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/§SINGLE_STAR§/g,`[^/]*`).replace(/§DOUBLE_STAR§/g,`.*`).replace(/§QUESTION§/g,`.`),s=RegExp(`^${o}$`).test(i);return this.logger.debug(`URL pattern matching`,{originalUrl:e,pathname:n,normalizedPathname:i,pattern:t,normalizedPattern:a,regexPattern:o,matches:s}),s}async createDeliveryResponse(e){let t=await this.http.post(`/external/deliveries/${e.id}/responses`,{},{headers:this.headers});if(!t.ok){this.logger.error(`Failed to create delivery response for delivery: ${e.id}`,t.error);return}return t.data}async updateDeliveryResponse(e,t,n=!1,r=!1){try{await this.http.patch(`/external/deliveries/${e}/responses/${t}`,{wasDismissed:n,wasCompleted:r},{headers:this.headers})}catch(n){this.logger.error(`Failed to update delivery response: ${t} for delivery: ${e}`,n)}}async isDeliveryValid(e,t){let n=t===void 0?``:`?triggerId=${t}`,r=await this.http.get(`/external/deliveries/${e.id}/should-deliver${n}`,{headers:this.headers});return r.ok?r.data:(this.logger.error(`Failed to check delivery validity for delivery: ${e.id}`,r.error),!1)}},n={debug:0,info:1,warn:2,error:3},r=new class e{prefix;enabled;level;constructor(e={}){this.prefix=e.prefix||`Nemme SDK`,this.enabled=e.enabled===void 0?!0:e.enabled,this.level=e.level||`info`}shouldLog(e){return this.enabled&&n[e]>=n[this.level]}formatMessage(e){return`[${this.prefix}] ${e}`}debug(e,...t){this.shouldLog(`debug`)&&console.debug(this.formatMessage(e),...t)}info(e,...t){this.shouldLog(`info`)&&console.info(this.formatMessage(e),...t)}warn(e,...t){this.shouldLog(`warn`)&&console.warn(this.formatMessage(e),...t)}error(e,...t){this.shouldLog(`error`)&&console.error(this.formatMessage(e),...t)}child(t){return new e({prefix:`${this.prefix}:${t}`,enabled:this.enabled,level:this.level})}configure(e){e.prefix!==void 0&&(this.prefix=e.prefix),e.enabled!==void 0&&(this.enabled=e.enabled),e.level!==void 0&&(this.level=e.level)}},i=r.child(`network`),a=3e4,o=(t,n)=>{let r=new URL(t,e.t.apiBaseUrl);return n&&Object.entries(n).forEach(([e,t])=>{t!=null&&r.searchParams.append(e,String(t))}),r.toString()},s={async request(e,t={}){let{method:n=`GET`,headers:r={},body:s,params:c,timeout:l=a}=t,u=o(e,c),d={method:n,headers:{"Content-Type":`application/json`,Accept:`application/json`,...r},body:s?JSON.stringify(s):void 0},f=new AbortController,p=setTimeout(()=>f.abort(),l);d.signal=f.signal;try{let e=await fetch(u,d),t,r=e.headers.get(`content-type`);if(r&&r.includes(`application/json`))t=await e.json();else{let n=await e.text();try{t=JSON.parse(n)}catch{t=n}}let a={data:t,status:e.status,statusText:e.statusText,headers:e.headers,ok:e.ok};if(e.ok)a.ok=!0;else{let t=`Request failed with status ${e.status}: ${e.statusText}`;i.error(t,{url:u,method:n,status:e.status,data:a.data}),a.ok=!1,a.error={message:t,details:a.data}}return a}catch(e){let t=`Network request failed`,r;return e instanceof DOMException&&e.name===`AbortError`?(t=`Request timeout after ${l}ms`,r={timeout:l,url:u}):r={message:e instanceof Error?e.message:String(e),url:u,method:n},i.error(t,r),{data:{},status:0,statusText:t,headers:new Headers,ok:!1,error:{message:t,details:r}}}finally{clearTimeout(p)}},async get(e,t={}){return this.request(e,{...t,method:`GET`})},async post(e,t,n={}){return this.request(e,{...n,method:`POST`,body:t})},async put(e,t,n={}){return this.request(e,{...n,method:`PUT`,body:t})},async patch(e,t,n={}){return this.request(e,{...n,method:`PATCH`,body:t})},async delete(e,t={}){return this.request(e,{...t,method:`DELETE`})}},c={STR:`string`,NUM:`number`,BOOL:`boolean`},l=e=>c[e]??e,u=class{logger;headers;http;sessionId;eventDefinitions=[];onPageView;onEvent;trackUrlParamChanges=!0;getPage;backlog=[];flushTimeout=null;batchConfig={enabled:!0,size:10,delayMs:1e4,sendOnUnload:!0};originalHistoryMethods=null;EVENT_PAGE_VIEW=`page_view`;lastTrackedPageKey;constructor({logger:e,headers:t,http:n,sessionId:r,eventDefinitions:i,batchConfig:a,onPageView:o,onEvent:s,trackUrlParamChanges:c,getPage:l}){this.logger=e,this.headers=t,this.http=n,this.sessionId=r,this.eventDefinitions=i,this.onPageView=o,this.onEvent=s,this.getPage=l,typeof c==`boolean`&&(this.trackUrlParamChanges=c),this.flushEvents=this.flushEvents.bind(this),this.handlePopState=this.handlePopState.bind(this),this.handleVisibilityChange=this.handleVisibilityChange.bind(this),this.setupBatching(a)}setupBatching(e){typeof e==`boolean`?this.batchConfig.enabled=e:e&&(this.batchConfig={...this.batchConfig,...e}),this.batchConfig.enabled&&this.batchConfig.sendOnUnload&&typeof window<`u`&&(window.addEventListener(`beforeunload`,this.flushEvents),window.addEventListener(`pagehide`,this.flushEvents))}async setupPageViewTracking(){if(typeof window>`u`)return;this.originalHistoryMethods={pushState:history.pushState,replaceState:history.replaceState};let e=history.pushState;history.pushState=async(...t)=>{e.apply(history,t),await this.trackPageView()};let t=history.replaceState;history.replaceState=async(...e)=>{t.apply(history,e),await this.trackPageView()},window.addEventListener(`popstate`,this.handlePopState),document.addEventListener(`visibilitychange`,this.handleVisibilityChange)}async track(e){if(!this.sessionId){this.logger.warn(`Tracking manager not initialized, cannot track event`);return}let{eventKey:t,data:n={}}=e;this.logger.debug(`Tracking event`,{eventKey:t,data:n});let r=this.getPageUrl(),i={sessionId:this.sessionId,eventKey:t,data:n||{},timestamp:new Date().toISOString(),page:r};this.sanitizeEventData(i)&&(this.validateEvent(i),this.batchConfig.enabled?(this.backlog.push(i),this.scheduleFlush()):await this.sendEvents([i]),t===this.EVENT_PAGE_VIEW?this.onPageView?.(r):this.onEvent?.(i,r))}async trackPageView(){if(typeof window>`u`)return;let e=this.getPageUrl();if(!this.trackUrlParamChanges){let t=this.getPageKeyWithoutQuery(e);if(this.lastTrackedPageKey===t)return;this.lastTrackedPageKey=t}await this.track({eventKey:this.EVENT_PAGE_VIEW,data:{title:document.title}})}getPageUrl(){if(this.getPage)return this.getPage();let e=window.location.href;return window.location.protocol===`file:`?`/${window.location.pathname.split(`/`).pop()||``}${window.location.hash}`:e}getPageKeyWithoutQuery(e){try{let t=new URL(e);return`${t.origin}${t.pathname}${t.hash}`}catch{let[t]=e.split(`?`);return t}}async flush(){return this.flushEvents()}destroy(){this.originalHistoryMethods&&=(history.pushState=this.originalHistoryMethods.pushState,history.replaceState=this.originalHistoryMethods.replaceState,null),typeof window<`u`&&(window.removeEventListener(`popstate`,this.handlePopState),document.removeEventListener(`visibilitychange`,this.handleVisibilityChange),window.removeEventListener(`beforeunload`,this.flushEvents),window.removeEventListener(`pagehide`,this.flushEvents)),this.flushTimeout&&=(clearTimeout(this.flushTimeout),null)}sanitizeEventData(e){if(e.eventKey===this.EVENT_PAGE_VIEW||!e.data)return!0;let t=e.data;for(let n of Object.keys(t)){let r=t[n];if(typeof r==`string`&&r===`[object Object]`)return this.logger.error(`Property ${n} contains [object Object], dropping event ${e.eventKey}`),!1;let i=typeof r;if(i!==`boolean`&&i!==`number`&&i!==`string`){this.logger.warn(`Property ${n} has unsupported type ${i}, removing from event ${e.eventKey}`),delete t[n];continue}typeof r==`string`&&(t[n]=r.toLowerCase())}return!0}validateEvent(e){if(e.eventKey===this.EVENT_PAGE_VIEW)return;let t=this.eventDefinitions.find(t=>t.eventKey===e.eventKey);if(!t){this.logger.warn(`Event ${e.eventKey} is not registered`);return}let n=t.properties,r=e.data;if(r)for(let t of Object.keys(r)){let i=r[t],a=n.find(e=>e.name===t);if(!a){this.logger.warn(`Property ${t} is not registered for event ${e.eventKey}`);continue}let o=l(a.type);typeof i!==o&&this.logger.warn(`Property ${a.name} has type ${o} but value is of type ${typeof i}`)}}async handlePopState(){await this.trackPageView()}async handleVisibilityChange(){document.visibilityState===`visible`&&await this.trackPageView()}scheduleFlush(){if(this.backlog.length>=this.batchConfig.size){this.flushEvents();return}this.flushTimeout||=window.setTimeout(()=>{this.flushEvents()},this.batchConfig.delayMs)}async flushEvents(){if(this.flushTimeout&&=(clearTimeout(this.flushTimeout),null),this.backlog.length===0)return;let e=[...this.backlog];this.backlog=[];try{await this.sendEvents(e)}catch(t){this.backlog.unshift(...e),this.logger.error(`Failed to send batched events`,t)}}async sendEvents(e){if(e.length===0)return;let t=new Date().toISOString();e.length===1?await this.http.post(`/external/trackings/track`,{...e[0],sentAt:t},{headers:this.headers}):await this.http.post(`/external/trackings/batch`,{sentAt:t,events:e},{headers:this.headers})}},d=3e3*1e3,f=class{clientKey;_userIdentifier;_group;_role;_initialized=!1;_lastInitError=null;clientLogger=r.child(`client`);headers={};formConfig;get userIdentifier(){return this._userIdentifier}get initialized(){return this._initialized}get lastInitError(){return this._lastInitError}constructor(e){this.clientKey=e}trackingManager;tokenRefreshTimer;inflightRefresh;http=this.createRetryingHttpClient();async init({userIdentifier:e,secretKey:t,debug:n=!1,batch:r,formConfig:i,deactivate:a=!1,trackUrlParamChanges:o,getPage:s,group:c,role:l}){if(this._initialized=!1,this._lastInitError=null,this.trackingManager?.destroy(),this.trackingManager=void 0,a)return this.clientLogger.info(`Nemme client deactivated and will stop initialization.`),this._initialized=!0,this._lastInitError=null,this.clientLogger.configure({enabled:!1}),this;if(!e)throw Error(`userIdentifier is required parameter`);this._userIdentifier=e,this._group=c,this._role=l,this.clientLogger.configure({enabled:n,level:n?`debug`:`info`}),this.headers={"X-Client-Key":this.clientKey,"X-User-Id":this._userIdentifier,...t&&{"X-Client-Secret":t}},this.formConfig=i;try{let e=await this.initializeSession();await this.initializeManagers(e,r,o,s),this._initialized=!0,this._lastInitError=null,this.clientLogger.info(`Nemme client initialized`,{clientKey:this.clientKey,userIdentifier:this._userIdentifier})}catch(e){this.clientLogger.error(`Error during initialization:`,e),this._initialized=!1,this._lastInitError=e instanceof Error?e:Error(`Error during initialization`)}return this}async flush(){return this.trackingManager?.flush()}destroy(){this.trackingManager?.destroy(),this.clearTokenRefresh()}async track(e){if(!this._initialized||!this.trackingManager){this.clientLogger.warn(`Nemme client not initialized, some operations may fail`);return}return this.trackingManager.track(e)}buildInitializeBody(){let e={};return this._group!==void 0&&(e.group=this._group),this._role!==void 0&&(e.role=this._role),e}async initializeSession(){try{let e={...this.headers},t=await s.post(`/external/trackings/initialize`,this.buildInitializeBody(),{headers:e});if(!t.ok)throw Error(t.error?.message||`Request for initialising session failed`);return this.applySessionToken(t.data.sessionToken),this.scheduleTokenRefresh(),t.data}catch(e){throw this.clientLogger.error(`Error during initialization`,e),e}}applySessionToken(e){this.headers.Authorization=`Bearer ${e}`,delete this.headers[`X-User-Id`]}scheduleTokenRefresh(){typeof window>`u`||(this.clearTokenRefresh(),this.tokenRefreshTimer=setTimeout(()=>{this.refreshToken()},d))}clearTokenRefresh(){this.tokenRefreshTimer&&=(clearTimeout(this.tokenRefreshTimer),void 0)}async refreshToken(){let e={...this.headers};this._userIdentifier&&(e[`X-User-Id`]=this._userIdentifier,delete e.Authorization);try{let t=await s.post(`/external/trackings/initialize`,this.buildInitializeBody(),{headers:e});t.ok?this.applySessionToken(t.data.sessionToken):this.clientLogger.warn(`SDK token refresh failed; will retry`,t.error)}catch(e){this.clientLogger.warn(`SDK token refresh threw`,e)}finally{this.scheduleTokenRefresh()}}createRetryingHttpClient(){let e=async e=>{let t=await e();return t.status===401?(this.clientLogger.debug(`SDK got 401; refreshing token and retrying once`),this.inflightRefresh||=this.refreshToken().finally(()=>{this.inflightRefresh=void 0}),await this.inflightRefresh,e()):t};return{get:(t,n={})=>e(()=>s.get(t,n)),post:(t,n,r={})=>e(()=>s.post(t,n,r)),patch:(t,n,r={})=>e(()=>s.patch(t,n,r))}}async initializeManagers(e,n,r,i){let a=new t(this.clientLogger,this.headers,this.http,this.formConfig),o=new u({logger:this.clientLogger,headers:this.headers,http:this.http,sessionId:e.sessionId,eventDefinitions:e.eventDefinitions,batchConfig:n,trackUrlParamChanges:r,getPage:i,onPageView:async e=>{await a.evaluateDeliveryTriggers({url:e})},onEvent:async(e,t)=>{await a.evaluateDeliveryTriggers({eventKey:e.eventKey,eventData:e.data,url:t})}});await a.loadDeliveries(),typeof window<`u`&&(await o.setupPageViewTracking(),await o.trackPageView(),await o.flush()),this.trackingManager=o}},p=e=>new f(e);Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return f}});
2
+ //# sourceMappingURL=client-Db2ZE5y9.js.map