@journium/js 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,10 +18,9 @@ Learn how to use Journium to power your application:
18
18
 
19
19
  ### Prerequisites
20
20
  - Modern browser (ES2017+)
21
- - Node.js `>=18.17.0` or later
22
21
  - An existing Journium application. [Create your account for free](https://dashboard.journium.app/sign-up?utm_source=github&utm_medium=journium_js).
23
22
 
24
- ## Installation
23
+ ## Installation Option 1: Via package manager
25
24
 
26
25
  ```bash
27
26
  npm install @journium/js
@@ -35,13 +34,58 @@ import { init } from '@journium/js';
35
34
  const journium = init({
36
35
  publishableKey: "your-publishable-key"
37
36
  });
37
+ ```
38
+
39
+ For more detailed examples and configuration options, visit the [Journium documentation](https://journium.app/docs/js/).
38
40
 
39
- journium.track('button_clicked', {
40
- button_name: 'signup'
41
+ ## Installation Option 2: Adding the JavaScript snippet to your HTML
42
+
43
+ For websites without module bundlers (Shopify, WordPress, static HTML), use the CDN version with a single global `journium` object.
44
+
45
+ ### Script Tag
46
+
47
+ ```html
48
+ <script>
49
+ !function(j,o,u,r,n,i,u,m){
50
+ if(!o.__JV){
51
+ window.journium=o;
52
+ o._q=[];
53
+ o._i=null;
54
+ o.init=function(c,n){o._i=[c,n]};
55
+ var methods="track identify reset capturePageview startAutocapture stopAutocapture flush getEffectiveOptions onOptionsChange destroy".split(" ");
56
+ for(var k=0;k<methods.length;k++){
57
+ !function(method){
58
+ o[method]=function(){o._q.push([method].concat(Array.prototype.slice.call(arguments)));return o}
59
+ }(methods[k])
60
+ }
61
+ o.__JV=1;
62
+ (m=j.createElement("script")).type="text/javascript";
63
+ m.async=!0;
64
+ m.src="https://cdn.jsdelivr.net/npm/@journium/js@1/dist/journium.min.js";
65
+ m.onerror=function(){console.warn("Journium: Failed to load SDK from CDN")};
66
+ (i=j.getElementsByTagName("script")[0]).parentNode.insertBefore(m,i);
67
+ }
68
+ }(document,window.journium||[]);
69
+
70
+ journium.init({
71
+ publishableKey: 'YOUR_PUBLISHABLE_KEY'
41
72
  });
73
+ </script>
42
74
  ```
43
75
 
44
- For more detailed examples and configuration options, visit the [Journium documentation](https://journium.app/docs/js/).
76
+ ### Usage Examples
77
+
78
+ ```javascript
79
+ // Track events
80
+ journium.track('page_view', { page: 'home' });
81
+ journium.track('button_click', { button: 'signup' });
82
+
83
+ // Identify users
84
+ journium.identify('user123', {
85
+ email: 'user@example.com',
86
+ plan: 'pro'
87
+ });
88
+ ```
45
89
 
46
90
  ## Other SDKs
47
91
 
@@ -34,7 +34,6 @@ export declare class JourniumAnalytics {
34
34
  export declare const init: (config: JourniumConfig) => JourniumAnalytics;
35
35
  declare const _default: {
36
36
  init: (config: JourniumConfig) => JourniumAnalytics;
37
- JourniumAnalytics: typeof JourniumAnalytics;
38
37
  };
39
38
  export default _default;
40
39
  //# sourceMappingURL=JourniumAnalytics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"JourniumAnalytics.d.ts","sourceRoot":"","sources":["../src/JourniumAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAsB,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAK1F,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,wBAAwB,CAAC,CAAa;gBAElC,MAAM,EAAE,cAAc;IAqBlC,OAAO,CAAC,yBAAyB;IAiBjC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIhE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIxE,KAAK,IAAI,IAAI;IAIb,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,gBAAgB,IAAI,IAAI;IA4BxB,eAAe,IAAI,IAAI;IAMvB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAkCjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA6BrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,mBAAmB;IAInB;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI;IAI9E,OAAO,IAAI,IAAI;CAQhB;AAED,eAAO,MAAM,IAAI,GAAI,QAAQ,cAAc,KAAG,iBAE7C,CAAC;;mBAF2B,cAAc,KAAG,iBAAiB;;;AAI/D,wBAA2C"}
1
+ {"version":3,"file":"JourniumAnalytics.d.ts","sourceRoot":"","sources":["../src/JourniumAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAsB,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAK1F,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,wBAAwB,CAAC,CAAa;gBAElC,MAAM,EAAE,cAAc;IAqBlC,OAAO,CAAC,yBAAyB;IAiBjC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIhE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIxE,KAAK,IAAI,IAAI;IAIb,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,gBAAgB,IAAI,IAAI;IA4BxB,eAAe,IAAI,IAAI;IAMvB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAkCjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA+BrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,mBAAmB;IAInB;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI;IAI9E,OAAO,IAAI,IAAI;CAQhB;AAED,eAAO,MAAM,IAAI,GAAI,QAAQ,cAAc,KAAG,iBAE7C,CAAC;;mBAF2B,cAAc,KAAG,iBAAiB;;AAI/D,wBAAwB"}
@@ -1,15 +1,18 @@
1
1
  import { JourniumConfig, JourniumLocalOptions } from '@journium/core';
2
2
  export declare class JourniumClient {
3
+ private static readonly REMOTE_OPTIONS_REFRESH_INTERVAL;
3
4
  private config;
4
5
  private effectiveOptions;
5
6
  private queue;
6
7
  private stagedEvents;
7
8
  private flushTimer;
9
+ private remoteOptionsRefreshTimer;
10
+ private isRefreshing;
11
+ private lastRemoteOptions;
8
12
  private initializationComplete;
9
13
  private initializationFailed;
10
14
  private identityManager;
11
15
  private optionsStorageKey;
12
- private disabled;
13
16
  private optionsChangeCallbacks;
14
17
  constructor(config: JourniumConfig);
15
18
  private loadCachedOptions;
@@ -23,6 +26,10 @@ export declare class JourniumClient {
23
26
  private notifyOptionsChange;
24
27
  private processStagedEvents;
25
28
  private startFlushTimer;
29
+ private startRemoteOptionsRefreshTimer;
30
+ private refreshRemoteOptions;
31
+ private applyRemoteOptions;
32
+ private buildIdentityProperties;
26
33
  private sendEvents;
27
34
  identify(distinctId: string, attributes?: Record<string, unknown>): void;
28
35
  reset(): void;
@@ -30,5 +37,6 @@ export declare class JourniumClient {
30
37
  flush(): Promise<void>;
31
38
  destroy(): void;
32
39
  getEffectiveOptions(): JourniumLocalOptions;
40
+ private get ingestionPaused();
33
41
  }
34
42
  //# sourceMappingURL=JourniumClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"JourniumClient.d.ts","sourceRoot":"","sources":["../src/JourniumClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,cAAc,EAAE,oBAAoB,EAAyG,MAAM,gBAAgB,CAAC;AAE5L,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,sBAAsB,CAAkB;IAChD,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,sBAAsB,CAA2D;gBAE7E,MAAM,EAAE,cAAc;IAoClC,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,iBAAiB;YAYX,eAAe;YAwEf,2BAA2B;IA8CzC;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI;IAQ9E,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,mBAAmB;IA0C3B,OAAO,CAAC,eAAe;YAYT,UAAU;IA0BxB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IA2B5E,KAAK,IAAI,IAAI;IAmBb,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAiE9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,OAAO,IAAI,IAAI;IAQf,mBAAmB,IAAI,oBAAoB;CAG5C"}
1
+ {"version":3,"file":"JourniumClient.d.ts","sourceRoot":"","sources":["../src/JourniumClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,cAAc,EAAyB,oBAAoB,EAAyG,MAAM,gBAAgB,CAAC;AAEnN,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,+BAA+B,CAAkB;IAEzE,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,yBAAyB,CAA+C;IAChF,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,iBAAiB,CAAsC;IAC/D,OAAO,CAAC,sBAAsB,CAAkB;IAChD,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,sBAAsB,CAA2D;gBAE7E,MAAM,EAAE,cAAc;IAiClC,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,iBAAiB;YAYX,eAAe;YAoCf,2BAA2B;IAiDzC;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI;IAQ9E,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,8BAA8B;YAcxB,oBAAoB;IA6ClC,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,uBAAuB;YAiBjB,UAAU;IA0BxB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAqB5E,KAAK,IAAI,IAAI;IAab,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAsC9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB5B,OAAO,IAAI,IAAI;IAYf,mBAAmB,IAAI,oBAAoB;IAI3C,OAAO,KAAK,eAAe,GAE1B;CACF"}
@@ -9,9 +9,10 @@ export declare class PageviewTracker {
9
9
  capturePageview(customProperties?: Record<string, unknown>): void;
10
10
  /**
11
11
  * Start automatic autocapture for pageviews
12
- * @returns void
12
+ * @param captureInitialPageview - whether to fire a $pageview immediately on start (default: true).
13
+ * Pass false when restarting after a remote options update to avoid a spurious pageview.
13
14
  */
14
- startAutoPageviewTracking(): void;
15
+ startAutoPageviewTracking(captureInitialPageview?: boolean): void;
15
16
  /**
16
17
  * Stop automatic autocapture for pageviews
17
18
  * @returns void
@@ -1 +1 @@
1
- {"version":3,"file":"PageviewTracker.d.ts","sourceRoot":"","sources":["../src/PageviewTracker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,iBAAiB,CAAgD;IACzE,OAAO,CAAC,oBAAoB,CAAmD;IAC/E,OAAO,CAAC,eAAe,CAA6B;gBAExC,MAAM,EAAE,cAAc;IAIlC,eAAe,CAAC,gBAAgB,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAkBrE;;;OAGG;IACH,yBAAyB,IAAI,IAAI;IA0BjC;;;OAGG;IACH,eAAe,IAAI,IAAI;CAmBxB"}
1
+ {"version":3,"file":"PageviewTracker.d.ts","sourceRoot":"","sources":["../src/PageviewTracker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,iBAAiB,CAAgD;IACzE,OAAO,CAAC,oBAAoB,CAAmD;IAC/E,OAAO,CAAC,eAAe,CAA6B;gBAExC,MAAM,EAAE,cAAc;IAIlC,eAAe,CAAC,gBAAgB,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAkBrE;;;;OAIG;IACH,yBAAyB,CAAC,sBAAsB,GAAE,OAAc,GAAG,IAAI;IA4BvE;;;OAGG;IACH,eAAe,IAAI,IAAI;CAmBxB"}
package/dist/cdn.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * CDN Entry Point for Journium Analytics
3
+ * This file provides global browser integration for script snippet usage
4
+ * Features: auto method stubbing, error handling, improved queue processing
5
+ */
6
+ import { init } from './index';
7
+ type JourniumAnalyticsInstance = ReturnType<typeof init>;
8
+ interface JourniumSnippetQueue {
9
+ _q?: any[][];
10
+ _i?: [any, string?];
11
+ __JV?: boolean;
12
+ _error?: Error | null;
13
+ _retry?: boolean;
14
+ [key: string]: any;
15
+ }
16
+ interface GlobalJournium {
17
+ init: (config: any, instanceName?: string) => JourniumAnalyticsInstance;
18
+ track?: (event: string, properties?: any) => void;
19
+ identify?: (distinctId: string, attributes?: any) => void;
20
+ reset?: () => void;
21
+ capturePageview?: (properties?: any) => void;
22
+ startAutocapture?: () => void;
23
+ stopAutocapture?: () => void;
24
+ flush?: () => Promise<void>;
25
+ getEffectiveOptions?: () => any;
26
+ onOptionsChange?: (callback: (options: any) => void) => () => void;
27
+ destroy?: () => void;
28
+ [key: string]: any;
29
+ }
30
+ declare global {
31
+ interface Window {
32
+ journium?: JourniumSnippetQueue | GlobalJournium;
33
+ }
34
+ }
35
+ export { init };
36
+ declare const _default: {
37
+ init: (config: import("@journium/core").JourniumConfig) => import("./JourniumAnalytics").JourniumAnalytics;
38
+ };
39
+ export default _default;
40
+ //# sourceMappingURL=cdn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG/B,KAAK,yBAAyB,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AASzD,UAAU,oBAAoB;IAC5B,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;IACb,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAGD,UAAU,cAAc;IACtB,IAAI,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,yBAAyB,CAAC;IACxE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAC1D,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,eAAe,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,GAAG,CAAC;IAChC,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,QAAQ,CAAC,EAAE,oBAAoB,GAAG,cAAc,CAAC;KAClD;CACF;AA6OD,OAAO,EAAE,IAAI,EAAE,CAAC;;;;AAChB,wBAAwB"}
package/dist/index.cjs CHANGED
@@ -448,9 +448,9 @@ const fetchRemoteOptions = async (apiHost, publishableKey, fetchFn) => {
448
448
  'Content-Type': 'application/json',
449
449
  },
450
450
  });
451
- if (!response.ok) {
452
- throw new Error(`Options fetch failed: ${response.status} ${response.statusText}`);
453
- }
451
+ // if (!response.ok) {
452
+ // throw new Error(`Options fetch failed: ${response.status} ${response.statusText}`);
453
+ // }
454
454
  const data = await response.json();
455
455
  return data;
456
456
  }
@@ -703,20 +703,19 @@ class JourniumClient {
703
703
  this.queue = [];
704
704
  this.stagedEvents = [];
705
705
  this.flushTimer = null;
706
+ this.remoteOptionsRefreshTimer = null;
707
+ this.isRefreshing = false;
708
+ this.lastRemoteOptions = null;
706
709
  this.initializationComplete = false;
707
710
  this.initializationFailed = false;
708
- this.disabled = false;
709
711
  this.optionsChangeCallbacks = new Set();
710
- // Validate required configuration - put in disabled state if invalid
712
+ // Validate required configuration
711
713
  if (!config.publishableKey || config.publishableKey.trim() === '') {
712
- this.disabled = true;
714
+ // Reject initialization with clear error
715
+ const errorMsg = 'Journium: publishableKey is required but not provided or is empty. SDK cannot be initialized.';
713
716
  Logger.setDebug(true);
714
- Logger.error('Journium: publishableKey is required but not provided or is empty. SDK will not function.');
715
- // Create minimal config to prevent crashes
716
- this.config = { publishableKey: '', apiHost: 'https://events.journium.app' };
717
- this.effectiveOptions = { debug: true };
718
- this.optionsStorageKey = 'jrnm_invalid_options';
719
- return;
717
+ Logger.error(errorMsg);
718
+ throw new Error(errorMsg);
720
719
  }
721
720
  // Set default apiHost if not provided
722
721
  this.config = {
@@ -760,62 +759,24 @@ class JourniumClient {
760
759
  }
761
760
  }
762
761
  async initializeAsync() {
763
- var _a;
764
762
  try {
765
763
  Logger.log('Journium: Starting initialization - fetching fresh remote config...');
766
- // Step 1: Try to fetch fresh remote config with timeout and retry
767
764
  const remoteOptions = await this.fetchRemoteOptionsWithRetry();
768
- if (remoteOptions) {
769
- // Step 2: Cache the fresh remote config
770
- this.saveCachedOptions(remoteOptions);
771
- // Step 3: Merge local options over remote config (local overrides remote)
772
- if (this.config.options) {
773
- this.effectiveOptions = mergeOptions(this.config.options, remoteOptions);
774
- Logger.log('Journium: Using fresh remote config merged with local options:', this.effectiveOptions);
775
- }
776
- else {
777
- this.effectiveOptions = remoteOptions;
778
- Logger.log('Journium: Using fresh remote config:', this.effectiveOptions);
779
- }
780
- }
781
- else {
782
- // Step 4: Fallback to cached config if fresh fetch failed
783
- const cachedRemoteOptions = this.loadCachedOptions();
784
- if (cachedRemoteOptions) {
785
- if (this.config.options) {
786
- this.effectiveOptions = mergeOptions(this.config.options, cachedRemoteOptions);
787
- Logger.log('Journium: Fresh config failed, using cached remote config merged with local options:', this.effectiveOptions);
788
- }
789
- else {
790
- this.effectiveOptions = cachedRemoteOptions;
791
- Logger.log('Journium: Fresh config failed, using cached remote config:', this.effectiveOptions);
792
- }
793
- }
794
- else {
795
- // Step 5: No remote config and no cached config - initialization fails
796
- Logger.error('Journium: Initialization failed - no remote config available and no cached config found');
797
- this.initializationFailed = true;
798
- this.initializationComplete = false;
799
- return;
800
- }
801
- }
802
- // Step 6: Update identity manager session timeout if provided
803
- if (this.effectiveOptions.sessionTimeout) {
804
- this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
765
+ if (!remoteOptions) {
766
+ Logger.error('Journium: Initialization failed - no remote config available');
767
+ this.initializationFailed = true;
768
+ return;
805
769
  }
806
- // Step 7: Update Logger debug setting
807
- Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
808
- // Step 8: Mark initialization as complete
770
+ this.applyRemoteOptions(remoteOptions);
771
+ Logger.log('Journium: Effective options after init:', this.effectiveOptions);
809
772
  this.initializationComplete = true;
810
773
  this.initializationFailed = false;
811
- // Step 9: Process any staged events
812
774
  this.processStagedEvents();
813
- // Step 10: Start flush timer
814
775
  if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
815
776
  this.startFlushTimer();
816
777
  }
817
- Logger.log('Journium: Initialization complete with options:', this.effectiveOptions);
818
- // Step 11: Notify callbacks about options
778
+ this.startRemoteOptionsRefreshTimer();
779
+ Logger.log('Journium: Initialization complete');
819
780
  this.notifyOptionsChange();
820
781
  }
821
782
  catch (error) {
@@ -837,11 +798,15 @@ class JourniumClient {
837
798
  // Race fetch against timeout
838
799
  const fetchPromise = fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
839
800
  const remoteOptionsResponse = await Promise.race([fetchPromise, timeoutPromise]);
840
- if (remoteOptionsResponse && remoteOptionsResponse.success) {
801
+ if (remoteOptionsResponse && remoteOptionsResponse.status === 'success') {
841
802
  Logger.log('Journium: Successfully fetched fresh remote config:', remoteOptionsResponse.config);
842
- return remoteOptionsResponse.config;
803
+ return remoteOptionsResponse.config || null;
843
804
  }
844
- else {
805
+ else if (remoteOptionsResponse && remoteOptionsResponse.status === 'error' && remoteOptionsResponse.errorCode === 'J_ERR_TENANT_NOT_FOUND') {
806
+ Logger.error('Journium: Invalid publishableKey is being used.');
807
+ return null;
808
+ }
809
+ {
845
810
  throw new Error('Remote config fetch unsuccessful');
846
811
  }
847
812
  }
@@ -882,35 +847,21 @@ class JourniumClient {
882
847
  processStagedEvents() {
883
848
  if (this.stagedEvents.length === 0)
884
849
  return;
850
+ if (this.ingestionPaused) {
851
+ Logger.warn(`Journium: Ingestion is paused — discarding ${this.stagedEvents.length} staged events`);
852
+ this.stagedEvents = [];
853
+ return;
854
+ }
885
855
  Logger.log(`Journium: Processing ${this.stagedEvents.length} staged events`);
886
- // Move staged events to main queue, adding identity properties now
887
- const identity = this.identityManager.getIdentity();
888
- const userAgentInfo = this.identityManager.getUserAgentInfo();
889
856
  for (const stagedEvent of this.stagedEvents) {
890
- // Add identity properties that weren't available during staging
891
- const eventWithIdentity = {
857
+ this.queue.push({
892
858
  ...stagedEvent,
893
- properties: {
894
- $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
895
- distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
896
- $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
897
- $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
898
- $current_url: typeof window !== 'undefined' ? window.location.href : '',
899
- $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
900
- ...userAgentInfo,
901
- $lib_version: '0.1.0', // TODO: Get from package.json
902
- $platform: 'web',
903
- ...stagedEvent.properties, // Original properties override system properties
904
- },
905
- };
906
- this.queue.push(eventWithIdentity);
859
+ properties: this.buildIdentityProperties(stagedEvent.properties),
860
+ });
907
861
  }
908
- // Clear staged events
909
862
  this.stagedEvents = [];
910
863
  Logger.log('Journium: Staged events processed and moved to main queue');
911
- // Check if we should flush immediately
912
864
  if (this.queue.length >= this.effectiveOptions.flushAt) {
913
- // console.log('1 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
914
865
  this.flush();
915
866
  }
916
867
  }
@@ -918,12 +869,88 @@ class JourniumClient {
918
869
  if (this.flushTimer) {
919
870
  clearInterval(this.flushTimer);
920
871
  }
921
- // Use universal setInterval (works in both browser and Node.js)
922
872
  this.flushTimer = setInterval(() => {
923
- // console.log('2 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
924
873
  this.flush();
925
874
  }, this.effectiveOptions.flushInterval);
926
875
  }
876
+ startRemoteOptionsRefreshTimer() {
877
+ // Clear any existing timer to prevent duplicate intervals
878
+ if (this.remoteOptionsRefreshTimer) {
879
+ clearInterval(this.remoteOptionsRefreshTimer);
880
+ this.remoteOptionsRefreshTimer = null;
881
+ }
882
+ this.remoteOptionsRefreshTimer = setInterval(() => {
883
+ this.refreshRemoteOptions();
884
+ }, JourniumClient.REMOTE_OPTIONS_REFRESH_INTERVAL);
885
+ Logger.log(`Journium: Scheduling remote options refresh every ${JourniumClient.REMOTE_OPTIONS_REFRESH_INTERVAL}ms`);
886
+ }
887
+ async refreshRemoteOptions() {
888
+ if (this.isRefreshing) {
889
+ Logger.log('Journium: Remote options refresh already in progress, skipping');
890
+ return;
891
+ }
892
+ this.isRefreshing = true;
893
+ Logger.log('Journium: Periodic remote options refresh triggered');
894
+ try {
895
+ const remoteOptions = await this.fetchRemoteOptionsWithRetry();
896
+ if (!remoteOptions) {
897
+ Logger.warn('Journium: Periodic remote options refresh failed, keeping current options');
898
+ return;
899
+ }
900
+ const prevRemoteSnapshot = JSON.stringify(this.lastRemoteOptions);
901
+ const prevFlushInterval = this.effectiveOptions.flushInterval;
902
+ this.applyRemoteOptions(remoteOptions);
903
+ if (prevRemoteSnapshot === JSON.stringify(this.lastRemoteOptions)) {
904
+ Logger.log('Journium: Remote options unchanged after refresh, no update needed');
905
+ return;
906
+ }
907
+ Logger.log('Journium: Remote options updated after refresh:', this.effectiveOptions);
908
+ if (this.effectiveOptions.flushInterval !== prevFlushInterval) {
909
+ if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
910
+ this.startFlushTimer();
911
+ }
912
+ else if (this.flushTimer) {
913
+ clearInterval(this.flushTimer);
914
+ this.flushTimer = null;
915
+ }
916
+ }
917
+ this.notifyOptionsChange();
918
+ }
919
+ catch (error) {
920
+ Logger.error('Journium: Periodic remote options refresh encountered an error:', error);
921
+ }
922
+ finally {
923
+ this.isRefreshing = false;
924
+ }
925
+ }
926
+ applyRemoteOptions(remoteOptions) {
927
+ var _a;
928
+ this.lastRemoteOptions = remoteOptions;
929
+ this.effectiveOptions = this.config.options
930
+ ? mergeOptions(this.config.options, remoteOptions)
931
+ : remoteOptions;
932
+ this.saveCachedOptions(remoteOptions);
933
+ if (this.effectiveOptions.sessionTimeout) {
934
+ this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
935
+ }
936
+ Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
937
+ }
938
+ buildIdentityProperties(userProperties = {}) {
939
+ const identity = this.identityManager.getIdentity();
940
+ const userAgentInfo = this.identityManager.getUserAgentInfo();
941
+ return {
942
+ $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
943
+ distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
944
+ $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
945
+ $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
946
+ $current_url: typeof window !== 'undefined' ? window.location.href : '',
947
+ $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
948
+ ...userAgentInfo,
949
+ $lib_version: '0.1.0', // TODO: Get from package.json
950
+ $platform: 'web',
951
+ ...userProperties,
952
+ };
953
+ }
927
954
  async sendEvents(events) {
928
955
  if (!events.length)
929
956
  return;
@@ -949,11 +976,6 @@ class JourniumClient {
949
976
  }
950
977
  }
951
978
  identify(distinctId, attributes = {}) {
952
- // Don't identify if SDK is not properly configured or disabled
953
- if (this.disabled || !this.config || !this.config.publishableKey) {
954
- Logger.warn('Journium: identify() call rejected - SDK not ready or disabled');
955
- return;
956
- }
957
979
  // Don't identify if initialization failed
958
980
  if (this.initializationFailed) {
959
981
  Logger.warn('Journium: identify() call rejected - initialization failed');
@@ -970,11 +992,6 @@ class JourniumClient {
970
992
  Logger.log('Journium: User identified', { distinctId, attributes, previousDistinctId });
971
993
  }
972
994
  reset() {
973
- // Don't reset if SDK is not properly configured or disabled
974
- if (this.disabled || !this.config || !this.config.publishableKey) {
975
- Logger.warn('Journium: reset() call rejected - SDK not ready or disabled');
976
- return;
977
- }
978
995
  // Don't reset if initialization failed
979
996
  if (this.initializationFailed) {
980
997
  Logger.warn('Journium: reset() call rejected - initialization failed');
@@ -985,11 +1002,6 @@ class JourniumClient {
985
1002
  Logger.log('Journium: User identity reset');
986
1003
  }
987
1004
  track(event, properties = {}) {
988
- // Don't track if SDK is not properly configured or disabled
989
- if (this.disabled || !this.config || !this.config.publishableKey) {
990
- Logger.warn('Journium: track() call rejected - SDK not ready or disabled');
991
- return;
992
- }
993
1005
  // Create minimal event without identity properties (will be added later if staging)
994
1006
  const journiumEvent = {
995
1007
  uuid: generateUuidv7(),
@@ -998,9 +1010,7 @@ class JourniumClient {
998
1010
  event,
999
1011
  properties: { ...properties }, // Only user properties for now
1000
1012
  };
1001
- // Stage events during initialization, add to queue after initialization
1002
1013
  if (!this.initializationComplete) {
1003
- // If initialization failed, reject events
1004
1014
  if (this.initializationFailed) {
1005
1015
  Logger.warn('Journium: track() call rejected - initialization failed');
1006
1016
  return;
@@ -1009,42 +1019,21 @@ class JourniumClient {
1009
1019
  Logger.log('Journium: Event staged during initialization', journiumEvent);
1010
1020
  return;
1011
1021
  }
1012
- // If initialization failed, reject events
1013
- if (this.initializationFailed) {
1014
- Logger.warn('Journium: track() call rejected - initialization failed');
1022
+ if (this.ingestionPaused) {
1023
+ Logger.warn('Journium: Ingestion is paused — event dropped:', journiumEvent.event);
1015
1024
  return;
1016
1025
  }
1017
- // Add identity properties for immediate events (after initialization)
1018
- const identity = this.identityManager.getIdentity();
1019
- const userAgentInfo = this.identityManager.getUserAgentInfo();
1020
1026
  const eventWithIdentity = {
1021
1027
  ...journiumEvent,
1022
- properties: {
1023
- $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
1024
- distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
1025
- $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
1026
- $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
1027
- $current_url: typeof window !== 'undefined' ? window.location.href : '',
1028
- $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
1029
- ...userAgentInfo,
1030
- $lib_version: '0.1.0', // TODO: Get from package.json
1031
- $platform: 'web',
1032
- ...properties, // User-provided properties override system properties
1033
- },
1028
+ properties: this.buildIdentityProperties(properties),
1034
1029
  };
1035
1030
  this.queue.push(eventWithIdentity);
1036
1031
  Logger.log('Journium: Event tracked', eventWithIdentity);
1037
- // Only flush if we have effective options (after initialization)
1038
1032
  if (this.effectiveOptions.flushAt && this.queue.length >= this.effectiveOptions.flushAt) {
1039
- // console.log('3 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
1040
1033
  this.flush();
1041
1034
  }
1042
1035
  }
1043
1036
  async flush() {
1044
- // Don't flush if SDK is not properly configured
1045
- if (!this.config || !this.config.publishableKey) {
1046
- return;
1047
- }
1048
1037
  // Don't flush if initialization failed
1049
1038
  if (this.initializationFailed) {
1050
1039
  Logger.warn('Journium: flush() call rejected - initialization failed');
@@ -1067,12 +1056,20 @@ class JourniumClient {
1067
1056
  clearInterval(this.flushTimer);
1068
1057
  this.flushTimer = null;
1069
1058
  }
1059
+ if (this.remoteOptionsRefreshTimer) {
1060
+ clearInterval(this.remoteOptionsRefreshTimer);
1061
+ this.remoteOptionsRefreshTimer = null;
1062
+ }
1070
1063
  this.flush();
1071
1064
  }
1072
1065
  getEffectiveOptions() {
1073
1066
  return this.effectiveOptions;
1074
1067
  }
1068
+ get ingestionPaused() {
1069
+ return this.effectiveOptions['ingestionPaused'] === true;
1070
+ }
1075
1071
  }
1072
+ JourniumClient.REMOTE_OPTIONS_REFRESH_INTERVAL = 15 * 60 * 1000; // 15 minutes
1076
1073
 
1077
1074
  class PageviewTracker {
1078
1075
  constructor(client) {
@@ -1099,10 +1096,13 @@ class PageviewTracker {
1099
1096
  }
1100
1097
  /**
1101
1098
  * Start automatic autocapture for pageviews
1102
- * @returns void
1099
+ * @param captureInitialPageview - whether to fire a $pageview immediately on start (default: true).
1100
+ * Pass false when restarting after a remote options update to avoid a spurious pageview.
1103
1101
  */
1104
- startAutoPageviewTracking() {
1105
- this.capturePageview();
1102
+ startAutoPageviewTracking(captureInitialPageview = true) {
1103
+ if (captureInitialPageview) {
1104
+ this.capturePageview();
1105
+ }
1106
1106
  if (typeof window !== 'undefined') {
1107
1107
  // Store original methods for cleanup
1108
1108
  this.originalPushState = window.history.pushState;
@@ -1621,21 +1621,22 @@ class JourniumAnalytics {
1621
1621
  * Handle effective options change (e.g., when remote options are fetched)
1622
1622
  */
1623
1623
  handleOptionsChange(effectiveOptions) {
1624
- // Stop current autocapture if it was already started
1624
+ // If autocapture was never started before, this is the initial options application
1625
+ // (async init completed) — treat it like a page load and capture a pageview.
1626
+ // If it was already started, this is a periodic remote options update — only
1627
+ // re-register listeners without emitting a spurious pageview.
1628
+ const isFirstStart = !this.autocaptureStarted;
1625
1629
  if (this.autocaptureStarted) {
1626
1630
  this.pageviewTracker.stopAutocapture();
1627
1631
  this.autocaptureTracker.stop();
1628
1632
  this.autocaptureStarted = false;
1629
1633
  }
1630
- // Evaluate if autocapture should be enabled with new options
1631
1634
  const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1632
1635
  const autocaptureEnabled = effectiveOptions.autocapture !== false;
1633
- // Update autocapture tracker options
1634
1636
  const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1635
1637
  this.autocaptureTracker.updateOptions(autocaptureOptions);
1636
- // Start autocapture based on new options (even if it wasn't started before)
1637
1638
  if (autoTrackPageviews) {
1638
- this.pageviewTracker.startAutoPageviewTracking();
1639
+ this.pageviewTracker.startAutoPageviewTracking(isFirstStart);
1639
1640
  }
1640
1641
  if (autocaptureEnabled) {
1641
1642
  this.autocaptureTracker.start();
@@ -1669,7 +1670,6 @@ const init = (config) => {
1669
1670
 
1670
1671
  exports.AutocaptureTracker = AutocaptureTracker;
1671
1672
  exports.BrowserIdentityManager = BrowserIdentityManager;
1672
- exports.JourniumAnalytics = JourniumAnalytics;
1673
1673
  exports.JourniumClient = JourniumClient;
1674
1674
  exports.Logger = Logger;
1675
1675
  exports.PageviewTracker = PageviewTracker;