onerway-analytics 1.3.0 → 1.5.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.
@@ -102,6 +102,10 @@ export interface AnalyticsConfig {
102
102
  gtm?: GTMConfig;
103
103
  disableServer?: boolean;
104
104
  sampleRate?: number;
105
+ /** Persist key events until HTTP acknowledgement (24h / 500-event bound).
106
+ * Enable only for applications whose event properties are safe to store locally.
107
+ */
108
+ persistKeyEvents?: boolean;
105
109
  /** @deprecated use appKey instead */
106
110
  appId?: string;
107
111
  }
@@ -135,7 +139,7 @@ export interface RetryEntry {
135
139
  timer: ReturnType<typeof setTimeout> | null;
136
140
  }
137
141
  export declare const API_KEY_MAP: Record<string, string>;
138
- export declare const SDK_VERSION = "1.0.0";
142
+ export declare const SDK_VERSION = "1.3.2";
139
143
  export declare const DEFAULT_CONFIG: {
140
144
  readonly debug: false;
141
145
  readonly autoTrack: true;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ export const API_KEY_MAP = {
8
8
  prod: 'HUk5Kogyuv9zH5qDA2qSCjVEnhanSkf8',
9
9
  production: 'HUk5Kogyuv9zH5qDA2qSCjVEnhanSkf8',
10
10
  };
11
- export const SDK_VERSION = '1.0.0';
11
+ export const SDK_VERSION = '1.3.2';
12
12
  export const DEFAULT_CONFIG = {
13
13
  debug: false,
14
14
  autoTrack: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onerway-analytics",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Onrway Analytics SDK",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -17,12 +17,15 @@
17
17
  "dist"
18
18
  ],
19
19
  "scripts": {
20
- "build": "tsc",
20
+ "build": "node scripts/build.cjs",
21
21
  "build:browser": "node build-browser.js",
22
22
  "build:watch": "tsc --watch",
23
23
  "lint": "eslint src --ext .ts",
24
24
  "test": "jest",
25
- "typecheck": "tsc --noEmit"
25
+ "typecheck": "tsc --noEmit",
26
+ "test:browser": "npm run build:browser && node scripts/verify-delivery.cjs",
27
+ "prepack": "npm run build && npm run build:browser",
28
+ "test:browser:comparison": "npm run build:browser && node scripts/verify-checkout-comparison.cjs"
26
29
  },
27
30
  "keywords": [
28
31
  "analytics",
@@ -38,6 +41,7 @@
38
41
  "eslint": "^8.57.0",
39
42
  "jest": "^29.7.0",
40
43
  "jest-environment-jsdom": "^30.4.1",
44
+ "playwright": "^1.63.0",
41
45
  "ts-jest": "^29.1.0",
42
46
  "typescript": "^5.3.0"
43
47
  },
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=analytics.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"analytics.test.d.ts","sourceRoot":"","sources":["../../src/core/analytics.test.ts"],"names":[],"mappings":""}
@@ -1,169 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { createAnalytics } from '../core/analytics';
3
- import { StorageQueue } from '../core/storage';
4
- function createMockXHR() {
5
- const calls = [];
6
- globalThis.XMLHttpRequest = class {
7
- constructor() {
8
- this._url = '';
9
- this._body = '';
10
- this._headers = {};
11
- this.status = 0;
12
- this.statusText = '';
13
- this.readyState = 0;
14
- this.onload = null;
15
- this.onerror = null;
16
- this.ontimeout = null;
17
- this.timeout = 0;
18
- }
19
- open(_method, url) {
20
- this._url = url;
21
- }
22
- setRequestHeader(key, value) {
23
- this._headers[key] = value;
24
- }
25
- send(body) {
26
- this._body = body || '';
27
- calls.push({ url: this._url, body: this._body, headers: { ...this._headers } });
28
- setTimeout(() => {
29
- this.status = 200;
30
- this.statusText = 'OK';
31
- this.readyState = 4;
32
- if (this.onload)
33
- this.onload({});
34
- }, 0);
35
- }
36
- };
37
- return calls;
38
- }
39
- describe('Analytics SDK', () => {
40
- let analytics;
41
- let xhrCalls;
42
- beforeEach(() => {
43
- localStorage.clear();
44
- sessionStorage.clear();
45
- xhrCalls = createMockXHR();
46
- analytics = createAnalytics({
47
- appKey: 'test-app-id',
48
- serverUrl: 'https://example.com/track',
49
- apiKey: 'test-api-key',
50
- debug: false,
51
- });
52
- });
53
- afterEach(() => {
54
- analytics.reset();
55
- jest.restoreAllMocks();
56
- });
57
- describe('track', () => {
58
- it('should send events on flush', async () => {
59
- analytics.track('test_event', { custom_prop: 'value' });
60
- analytics.flush();
61
- await new Promise((r) => setTimeout(r, 50));
62
- expect(xhrCalls.length).toBeGreaterThanOrEqual(1);
63
- const body = JSON.parse(xhrCalls[0].body);
64
- const pageViewEvent = body.events.find((e) => e.event_name === 'page_view');
65
- const testEvent = body.events.find((e) => e.event_name === 'test_event');
66
- expect(pageViewEvent).toBeDefined();
67
- expect(testEvent).toBeDefined();
68
- expect(testEvent.event_properties.custom_prop).toBe('value');
69
- });
70
- it('should ignore invalid event name', async () => {
71
- analytics.track('');
72
- analytics.track(123);
73
- analytics.flush();
74
- await new Promise((r) => setTimeout(r, 50));
75
- if (xhrCalls.length > 0) {
76
- const body = JSON.parse(xhrCalls[0].body);
77
- const names = body.events.map((e) => e.event_name);
78
- expect(names).not.toContain('');
79
- expect(names).toContain('page_view');
80
- }
81
- });
82
- });
83
- describe('identify', () => {
84
- it('should set user profile', () => {
85
- analytics.identify('user-123', { name: 'Test User' });
86
- const profile = analytics.getUserProfile();
87
- expect(profile.userId).toBe('user-123');
88
- expect(profile.attributes).toEqual({ name: 'Test User' });
89
- });
90
- });
91
- describe('reset', () => {
92
- it('should clear user profile and queue', () => {
93
- analytics.identify('user-123', { name: 'Test User' });
94
- analytics.track('some_event');
95
- analytics.reset();
96
- const profile = analytics.getUserProfile();
97
- expect(profile.userId).toBeUndefined();
98
- });
99
- });
100
- describe('configuration', () => {
101
- it('should support appId backward compatibility', () => {
102
- const analytics2 = createAnalytics({
103
- appId: 'backward-compat-app',
104
- serverUrl: 'https://example.com/track',
105
- apiKey: 'test-api-key',
106
- debug: false,
107
- });
108
- expect(analytics2.getClientId()).toBeDefined();
109
- analytics2.reset();
110
- });
111
- it('should generate client_id on creation', () => {
112
- const cid = analytics.getClientId();
113
- expect(cid).toBeDefined();
114
- expect(typeof cid).toBe('string');
115
- expect(cid.length).toBeGreaterThan(0);
116
- });
117
- it('should generate session_id on creation', () => {
118
- const sid = analytics.getSessionId();
119
- expect(sid).toBeDefined();
120
- expect(typeof sid).toBe('string');
121
- expect(sid.length).toBeGreaterThan(0);
122
- });
123
- });
124
- describe('payload structure', () => {
125
- it('should assemble correct request payload', async () => {
126
- analytics.track('test_event', { scene: 'checkout.payment' });
127
- analytics.flush();
128
- await new Promise((r) => setTimeout(r, 50));
129
- expect(xhrCalls.length).toBeGreaterThanOrEqual(1);
130
- const body = JSON.parse(xhrCalls[0].body);
131
- expect(body.client_id).toBeDefined();
132
- expect(body.session_id).toBeDefined();
133
- expect(body.user_id).toBe('');
134
- expect(body.user_properties).toBeUndefined();
135
- expect(body.context).toBeDefined();
136
- expect(body.context.app_key).toBe('test-app-id');
137
- expect(body.context.sdk_version).toBeDefined();
138
- expect(body.marketing).toBeDefined();
139
- expect(body.events).toBeInstanceOf(Array);
140
- const testEvent = body.events.find((e) => e.event_name === 'test_event');
141
- expect(testEvent).toBeDefined();
142
- expect(testEvent.event_id).toBeDefined();
143
- expect(testEvent.timestamp).toBeGreaterThan(0);
144
- expect(testEvent.key_event).toBe(false);
145
- expect(testEvent.event_properties.scene).toBe('checkout.payment');
146
- expect(testEvent.batch_ordering_id).toBeDefined();
147
- expect(testEvent.batch_page_id).toBeDefined();
148
- });
149
- });
150
- describe('StorageQueue', () => {
151
- it('should persist and recover failed events', () => {
152
- StorageQueue.clear();
153
- StorageQueue.enqueue({
154
- event_id: 'evt-1',
155
- event_name: 'failed_event',
156
- batch_event_index: 0,
157
- batch_ordering_id: 'ord-1',
158
- batch_page_id: 'page-1',
159
- timestamp: Date.now(),
160
- key_event: false,
161
- event_properties: {},
162
- });
163
- const recovered = StorageQueue.recover();
164
- expect(recovered.length).toBe(1);
165
- expect(recovered[0].event_name).toBe('failed_event');
166
- expect(StorageQueue.getQueueSize()).toBe(0);
167
- });
168
- });
169
- });
@@ -1,11 +0,0 @@
1
- import { MarketingPayload } from '../types';
2
- export interface GAEventOptions {
3
- sessionId: string;
4
- pageUrl: string;
5
- referrer: string;
6
- }
7
- export declare function trackToGA(eventName: string, properties: Record<string, unknown>, options: GAEventOptions): void;
8
- export declare function setGAUserId(userId: string | null): void;
9
- export declare function setGAUserProperties(properties: Record<string, unknown>): void;
10
- export declare function setGAMarketing(marketing: MarketingPayload): void;
11
- //# sourceMappingURL=ga-adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ga-adapter.d.ts","sourceRoot":"","sources":["../../src/core/ga-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqBD,wBAAgB,SAAS,CACvB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,OAAO,EAAE,cAAc,GACtB,IAAI,CAqBN;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGvD;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAG7E;AAID,wBAAgB,cAAc,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAWhE"}
@@ -1,66 +0,0 @@
1
- // GA4 reserved event names — sending these as custom events causes silent drops
2
- const GA4_RESERVED_NAMES = new Set([
3
- 'ad_activeview', 'ad_click', 'ad_exposure', 'ad_impression', 'ad_query',
4
- 'adunit_exposure', 'app_clear_data', 'app_exception', 'app_install',
5
- 'app_remove', 'app_update', 'app_upgrade', 'error', 'firebase_campaign',
6
- 'first_open', 'first_visit', 'in_app_purchase', 'notification_dismiss',
7
- 'notification_foreground', 'notification_open', 'notification_receive',
8
- 'os_update', 'session_start', 'user_engagement',
9
- ]);
10
- // System/automated events — don't contribute to engagement_time_msec
11
- const SYSTEM_EVENTS = new Set(['web_vital', 'error_event']);
12
- function sanitizeEventName(name) {
13
- let result = name.replace(/[^a-zA-Z0-9_]/g, '_');
14
- if (!/^[a-zA-Z]/.test(result))
15
- result = 'e_' + result;
16
- return result.slice(0, 40);
17
- }
18
- export function trackToGA(eventName, properties, options) {
19
- if (typeof window === 'undefined' || !window.gtag)
20
- return;
21
- // Sanitize event name for GA4 compliance
22
- const safeName = GA4_RESERVED_NAMES.has(eventName)
23
- ? `custom_${eventName}`.slice(0, 40)
24
- : sanitizeEventName(eventName);
25
- // Business properties first, then GA reserved fields override — prevents
26
- // caller properties from corrupting session_id / page_location etc.
27
- const params = { ...properties };
28
- params.session_id = options.sessionId;
29
- params.page_location = options.pageUrl;
30
- params.page_referrer = options.referrer;
31
- // engagement_time_msec only for user-facing events, not automated system events
32
- if (!SYSTEM_EVENTS.has(eventName)) {
33
- params.engagement_time_msec = 100;
34
- }
35
- window.gtag('event', safeName, params);
36
- }
37
- export function setGAUserId(userId) {
38
- if (typeof window === 'undefined' || !window.gtag)
39
- return;
40
- window.gtag('set', { user_id: userId ?? undefined });
41
- }
42
- export function setGAUserProperties(properties) {
43
- if (typeof window === 'undefined' || !window.gtag)
44
- return;
45
- window.gtag('set', 'user_properties', properties);
46
- }
47
- // Set UTM attribution once at session level — GA4 auto-associates with all
48
- // subsequent events, no need to repeat on every event payload
49
- export function setGAMarketing(marketing) {
50
- if (typeof window === 'undefined' || !window.gtag)
51
- return;
52
- const params = {};
53
- if (marketing.utm_source)
54
- params.utm_source = marketing.utm_source;
55
- if (marketing.utm_medium)
56
- params.utm_medium = marketing.utm_medium;
57
- if (marketing.utm_campaign)
58
- params.utm_campaign = marketing.utm_campaign;
59
- if (marketing.utm_term)
60
- params.utm_term = marketing.utm_term;
61
- if (marketing.utm_content)
62
- params.utm_content = marketing.utm_content;
63
- if (Object.keys(params).length > 0) {
64
- window.gtag('set', params);
65
- }
66
- }
@@ -1,10 +0,0 @@
1
- import { GAConfig } from '../types';
2
- declare global {
3
- interface Window {
4
- gtag: (...args: unknown[]) => void;
5
- dataLayer: unknown[];
6
- }
7
- }
8
- export declare function loadGtagScript(measurementId: string): Promise<void>;
9
- export declare function initGA(config: GAConfig): Promise<void>;
10
- //# sourceMappingURL=ga-loader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ga-loader.d.ts","sourceRoot":"","sources":["../../src/core/ga-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QACnC,SAAS,EAAE,OAAO,EAAE,CAAC;KACtB;CACF;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAenE;AAED,wBAAsB,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5D"}
@@ -1,34 +0,0 @@
1
- export function loadGtagScript(measurementId) {
2
- return new Promise((resolve, reject) => {
3
- // Check for existing script tag, not window.gtag — we define gtag before loading
4
- if (document.querySelector(`script[src*="googletagmanager.com/gtag/js"]`)) {
5
- resolve();
6
- return;
7
- }
8
- const script = document.createElement('script');
9
- script.async = true;
10
- script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`;
11
- script.onload = () => resolve();
12
- script.onerror = () => reject(new Error('Failed to load gtag.js'));
13
- document.head.appendChild(script);
14
- });
15
- }
16
- export async function initGA(config) {
17
- if (!config.enabled || !config.measurementId)
18
- return;
19
- if (typeof window === 'undefined')
20
- return;
21
- // Must be defined BEFORE the script loads so gtag.js finds them on execution
22
- window.dataLayer = window.dataLayer || [];
23
- // gtag.js checks e.constructor == Arguments, so must push `arguments` not a rest-param array
24
- window.gtag = function gtag() {
25
- // eslint-disable-next-line prefer-rest-params
26
- window.dataLayer.push(arguments);
27
- };
28
- window.gtag('js', new Date());
29
- window.gtag('config', config.measurementId, {
30
- send_page_view: config.sendPageView ?? false,
31
- debug_mode: config.debugMode ?? false,
32
- });
33
- await loadGtagScript(config.measurementId);
34
- }
@@ -1,24 +0,0 @@
1
- import type { GAConsentMode, GAConsentParams } from '../types';
2
- declare global {
3
- interface Window {
4
- gtag: (...args: unknown[]) => void;
5
- dataLayer: unknown[];
6
- }
7
- }
8
- export declare class GAManager {
9
- private _initialized;
10
- private _measurementId;
11
- get ready(): boolean;
12
- get measurementId(): string | undefined;
13
- track(eventName: string, properties?: Record<string, unknown>): void;
14
- setUserId(userId: string | null): void;
15
- setUserProperties(properties: Record<string, unknown>): void;
16
- set(keyOrParams: string | Record<string, unknown>, value?: unknown): void;
17
- config(params?: Record<string, unknown>): void;
18
- consent(mode: GAConsentMode, params: GAConsentParams): void;
19
- get(key: string, callback: (value: unknown) => void): void;
20
- gtag(...args: unknown[]): void;
21
- _init(measurementId: string): void;
22
- _destroy(): void;
23
- }
24
- //# sourceMappingURL=ga-manager.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ga-manager.d.ts","sourceRoot":"","sources":["../../src/core/ga-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAChB,MAAM,UAAU,CAAC;AAElB,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QACnC,SAAS,EAAE,OAAO,EAAE,CAAC;KACtB;CACF;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAqB;IAE3C,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAEtC;IAED,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAKpE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKtC,iBAAiB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5D,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI;IASzE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK9C,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAK3D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAK1D,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAK9B,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;IAKlC,QAAQ,IAAI,IAAI;CAIjB"}
@@ -1,64 +0,0 @@
1
- export class GAManager {
2
- constructor() {
3
- this._initialized = false;
4
- }
5
- get ready() {
6
- return this._initialized && typeof window !== 'undefined' && typeof window.gtag === 'function';
7
- }
8
- get measurementId() {
9
- return this._measurementId;
10
- }
11
- track(eventName, properties) {
12
- if (!this.ready)
13
- return;
14
- window.gtag('event', eventName, properties ?? {});
15
- }
16
- setUserId(userId) {
17
- if (!this.ready)
18
- return;
19
- window.gtag('set', { user_id: userId ?? undefined });
20
- }
21
- setUserProperties(properties) {
22
- if (!this.ready)
23
- return;
24
- window.gtag('set', 'user_properties', properties);
25
- }
26
- set(keyOrParams, value) {
27
- if (!this.ready)
28
- return;
29
- if (typeof keyOrParams === 'string') {
30
- window.gtag('set', keyOrParams, value);
31
- }
32
- else {
33
- window.gtag('set', keyOrParams);
34
- }
35
- }
36
- config(params) {
37
- if (!this.ready || !this._measurementId)
38
- return;
39
- window.gtag('config', this._measurementId, params ?? {});
40
- }
41
- consent(mode, params) {
42
- if (!this.ready)
43
- return;
44
- window.gtag('consent', mode, params);
45
- }
46
- get(key, callback) {
47
- if (!this.ready || !this._measurementId)
48
- return;
49
- window.gtag('get', this._measurementId, key, callback);
50
- }
51
- gtag(...args) {
52
- if (typeof window === 'undefined' || !window.gtag)
53
- return;
54
- window.gtag(...args);
55
- }
56
- _init(measurementId) {
57
- this._measurementId = measurementId;
58
- this._initialized = true;
59
- }
60
- _destroy() {
61
- this._initialized = false;
62
- this._measurementId = undefined;
63
- }
64
- }