@stacksee/analytics 0.10.0 → 0.11.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.
- package/dist/adapters/client/browser-analytics.d.ts +36 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +91 -42
- package/dist/core/events/types.d.ts +1 -0
- package/dist/index-CBs091W0.js +157 -0
- package/dist/providers/emitkit/server.d.ts +62 -0
- package/dist/providers/server.d.ts +2 -0
- package/dist/providers/server.js +305 -113
- package/package.json +3 -2
|
@@ -394,6 +394,42 @@ export declare class BrowserAnalytics<TEventMap extends DefaultEventMap = Defaul
|
|
|
394
394
|
* ```
|
|
395
395
|
*/
|
|
396
396
|
reset(): void;
|
|
397
|
+
/**
|
|
398
|
+
* Manually flush all queued events to providers.
|
|
399
|
+
*
|
|
400
|
+
* This method is useful when you need to ensure events are sent immediately,
|
|
401
|
+
* such as before navigation or critical user actions. Providers that support
|
|
402
|
+
* batching (like ProxyProvider) will flush their queues when this is called.
|
|
403
|
+
*
|
|
404
|
+
* If `useBeacon` is true, providers that support it will use the Beacon API
|
|
405
|
+
* for more reliable delivery during page unload.
|
|
406
|
+
*
|
|
407
|
+
* @param useBeacon Whether to use the Beacon API for flushing (default: false)
|
|
408
|
+
* @returns Promise that resolves when all providers have flushed
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```typescript
|
|
412
|
+
* // Flush before navigation
|
|
413
|
+
* analytics.track('button_clicked', { buttonId: 'checkout' });
|
|
414
|
+
* await analytics.flush();
|
|
415
|
+
* window.location.href = '/checkout';
|
|
416
|
+
* ```
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```typescript
|
|
420
|
+
* // Flush with beacon API before page unload
|
|
421
|
+
* await analytics.flush(true);
|
|
422
|
+
* ```
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```typescript
|
|
426
|
+
* // Ensure critical events are sent immediately
|
|
427
|
+
* await analytics.track('purchase_completed', { orderId: '123' });
|
|
428
|
+
* await analytics.flush(); // Wait for confirmation
|
|
429
|
+
* showThankYouPage();
|
|
430
|
+
* ```
|
|
431
|
+
*/
|
|
432
|
+
flush(useBeacon?: boolean): Promise<void>;
|
|
397
433
|
/**
|
|
398
434
|
* Updates the analytics context with new information.
|
|
399
435
|
*
|
package/dist/client.d.ts
CHANGED
|
@@ -60,6 +60,10 @@ export declare function pageLeave(properties?: Record<string, unknown>): void;
|
|
|
60
60
|
* Convenience function to reset user session
|
|
61
61
|
*/
|
|
62
62
|
export declare function reset(): void;
|
|
63
|
+
/**
|
|
64
|
+
* Convenience function to flush queued events
|
|
65
|
+
*/
|
|
66
|
+
export declare function flush(useBeacon?: boolean): Promise<void>;
|
|
63
67
|
/**
|
|
64
68
|
* Reset the analytics instance (for testing purposes)
|
|
65
69
|
* @internal
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var c = Object.defineProperty;
|
|
2
|
-
var u = (
|
|
3
|
-
var
|
|
2
|
+
var u = (r, e, i) => e in r ? c(r, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : r[e] = i;
|
|
3
|
+
var n = (r, e, i) => u(r, typeof e != "symbol" ? e + "" : e, i);
|
|
4
4
|
import { i as f } from "./client-DTHZYkxx.js";
|
|
5
5
|
import { P } from "./client-DTHZYkxx.js";
|
|
6
6
|
import { B as k } from "./base.provider-AfFL5W_P.js";
|
|
@@ -36,13 +36,13 @@ class p {
|
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
38
|
constructor(e) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
n(this, "providerConfigs", []);
|
|
40
|
+
n(this, "context", {});
|
|
41
|
+
n(this, "userId");
|
|
42
|
+
n(this, "sessionId");
|
|
43
|
+
n(this, "userTraits");
|
|
44
|
+
n(this, "initialized", !1);
|
|
45
|
+
n(this, "initializePromise");
|
|
46
46
|
this.providerConfigs = this.normalizeProviders(e.providers), e.defaultContext && (this.context = { ...e.defaultContext }), this.sessionId = this.generateSessionId();
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
@@ -57,26 +57,26 @@ class p {
|
|
|
57
57
|
"pageLeave",
|
|
58
58
|
"reset"
|
|
59
59
|
];
|
|
60
|
-
return e.map((
|
|
61
|
-
if ("initialize" in
|
|
60
|
+
return e.map((t) => {
|
|
61
|
+
if ("initialize" in t && "track" in t)
|
|
62
62
|
return {
|
|
63
|
-
provider:
|
|
63
|
+
provider: t,
|
|
64
64
|
enabledMethods: new Set(i)
|
|
65
65
|
};
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
`[Analytics] Provider ${
|
|
66
|
+
const s = t;
|
|
67
|
+
s.methods && s.exclude && console.warn(
|
|
68
|
+
`[Analytics] Provider ${s.provider.name} has both 'methods' and 'exclude' specified. Using 'methods' and ignoring 'exclude'.`
|
|
69
69
|
);
|
|
70
70
|
let o;
|
|
71
|
-
return
|
|
71
|
+
return s.methods ? o = new Set(s.methods) : s.exclude ? o = new Set(
|
|
72
72
|
i.filter(
|
|
73
73
|
(d) => {
|
|
74
74
|
var h;
|
|
75
|
-
return !((h =
|
|
75
|
+
return !((h = s.exclude) != null && h.includes(d));
|
|
76
76
|
}
|
|
77
77
|
)
|
|
78
78
|
) : o = new Set(i), {
|
|
79
|
-
provider:
|
|
79
|
+
provider: s.provider,
|
|
80
80
|
enabledMethods: o
|
|
81
81
|
};
|
|
82
82
|
});
|
|
@@ -209,11 +209,11 @@ class p {
|
|
|
209
209
|
* ```
|
|
210
210
|
*/
|
|
211
211
|
identify(e, i) {
|
|
212
|
-
this.userId = e, this.userTraits = i, this.ensureInitialized().catch((
|
|
213
|
-
console.error("[Analytics] Failed to initialize during identify:",
|
|
212
|
+
this.userId = e, this.userTraits = i, this.ensureInitialized().catch((t) => {
|
|
213
|
+
console.error("[Analytics] Failed to initialize during identify:", t);
|
|
214
214
|
});
|
|
215
|
-
for (const
|
|
216
|
-
this.shouldCallMethod(
|
|
215
|
+
for (const t of this.providerConfigs)
|
|
216
|
+
this.shouldCallMethod(t, "identify") && t.provider.identify(e, i);
|
|
217
217
|
}
|
|
218
218
|
/**
|
|
219
219
|
* Tracks a custom event with properties.
|
|
@@ -292,14 +292,14 @@ class p {
|
|
|
292
292
|
*/
|
|
293
293
|
async track(e, i) {
|
|
294
294
|
await this.ensureInitialized();
|
|
295
|
-
const
|
|
295
|
+
const t = {
|
|
296
296
|
action: e,
|
|
297
297
|
category: this.getCategoryFromEventName(e),
|
|
298
298
|
properties: i,
|
|
299
299
|
timestamp: Date.now(),
|
|
300
300
|
userId: this.userId,
|
|
301
301
|
sessionId: this.sessionId
|
|
302
|
-
},
|
|
302
|
+
}, s = {
|
|
303
303
|
...this.context,
|
|
304
304
|
user: this.userId || this.userTraits ? {
|
|
305
305
|
userId: this.userId,
|
|
@@ -308,7 +308,7 @@ class p {
|
|
|
308
308
|
} : void 0
|
|
309
309
|
}, o = this.providerConfigs.filter((d) => this.shouldCallMethod(d, "track")).map(async (d) => {
|
|
310
310
|
try {
|
|
311
|
-
await d.provider.track(
|
|
311
|
+
await d.provider.track(t, s);
|
|
312
312
|
} catch (h) {
|
|
313
313
|
console.error(
|
|
314
314
|
`[Analytics] Provider ${d.provider.name} failed to track event:`,
|
|
@@ -513,6 +513,55 @@ class p {
|
|
|
513
513
|
for (const e of this.providerConfigs)
|
|
514
514
|
this.shouldCallMethod(e, "reset") && e.provider.reset();
|
|
515
515
|
}
|
|
516
|
+
/**
|
|
517
|
+
* Manually flush all queued events to providers.
|
|
518
|
+
*
|
|
519
|
+
* This method is useful when you need to ensure events are sent immediately,
|
|
520
|
+
* such as before navigation or critical user actions. Providers that support
|
|
521
|
+
* batching (like ProxyProvider) will flush their queues when this is called.
|
|
522
|
+
*
|
|
523
|
+
* If `useBeacon` is true, providers that support it will use the Beacon API
|
|
524
|
+
* for more reliable delivery during page unload.
|
|
525
|
+
*
|
|
526
|
+
* @param useBeacon Whether to use the Beacon API for flushing (default: false)
|
|
527
|
+
* @returns Promise that resolves when all providers have flushed
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* // Flush before navigation
|
|
532
|
+
* analytics.track('button_clicked', { buttonId: 'checkout' });
|
|
533
|
+
* await analytics.flush();
|
|
534
|
+
* window.location.href = '/checkout';
|
|
535
|
+
* ```
|
|
536
|
+
*
|
|
537
|
+
* @example
|
|
538
|
+
* ```typescript
|
|
539
|
+
* // Flush with beacon API before page unload
|
|
540
|
+
* await analytics.flush(true);
|
|
541
|
+
* ```
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```typescript
|
|
545
|
+
* // Ensure critical events are sent immediately
|
|
546
|
+
* await analytics.track('purchase_completed', { orderId: '123' });
|
|
547
|
+
* await analytics.flush(); // Wait for confirmation
|
|
548
|
+
* showThankYouPage();
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
551
|
+
async flush(e = !1) {
|
|
552
|
+
const i = this.providerConfigs.map(async (t) => {
|
|
553
|
+
if (t.provider.flush && typeof t.provider.flush == "function")
|
|
554
|
+
try {
|
|
555
|
+
await t.provider.flush(e);
|
|
556
|
+
} catch (s) {
|
|
557
|
+
console.error(
|
|
558
|
+
`[Analytics] Provider ${t.provider.name} failed to flush:`,
|
|
559
|
+
s
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
await Promise.all(i);
|
|
564
|
+
}
|
|
516
565
|
/**
|
|
517
566
|
* Updates the analytics context with new information.
|
|
518
567
|
*
|
|
@@ -574,14 +623,14 @@ class p {
|
|
|
574
623
|
* ```
|
|
575
624
|
*/
|
|
576
625
|
updateContext(e) {
|
|
577
|
-
var i,
|
|
626
|
+
var i, t, s;
|
|
578
627
|
this.context = {
|
|
579
628
|
...this.context,
|
|
580
629
|
...e,
|
|
581
630
|
page: e.page ? {
|
|
582
631
|
path: e.page.path || ((i = this.context.page) == null ? void 0 : i.path) || window.location.pathname,
|
|
583
|
-
title: e.page.title || ((
|
|
584
|
-
referrer: e.page.referrer || ((
|
|
632
|
+
title: e.page.title || ((t = this.context.page) == null ? void 0 : t.title),
|
|
633
|
+
referrer: e.page.referrer || ((s = this.context.page) == null ? void 0 : s.referrer)
|
|
585
634
|
} : this.context.page,
|
|
586
635
|
device: {
|
|
587
636
|
...this.context.device,
|
|
@@ -616,13 +665,13 @@ class p {
|
|
|
616
665
|
}
|
|
617
666
|
}
|
|
618
667
|
let a = null;
|
|
619
|
-
function v(
|
|
668
|
+
function v(r) {
|
|
620
669
|
if (a)
|
|
621
670
|
return console.warn("[Analytics] Already initialized"), a;
|
|
622
671
|
const e = {
|
|
623
|
-
providers:
|
|
624
|
-
debug:
|
|
625
|
-
enabled:
|
|
672
|
+
providers: r.providers || [],
|
|
673
|
+
debug: r.debug,
|
|
674
|
+
enabled: r.enabled
|
|
626
675
|
};
|
|
627
676
|
return a = new p(e), a.initialize().catch((i) => {
|
|
628
677
|
console.error("[Analytics] Failed to initialize:", i);
|
|
@@ -635,17 +684,17 @@ function l() {
|
|
|
635
684
|
);
|
|
636
685
|
return a;
|
|
637
686
|
}
|
|
638
|
-
function
|
|
639
|
-
return l().track(
|
|
687
|
+
function y(r, e) {
|
|
688
|
+
return l().track(r, e);
|
|
640
689
|
}
|
|
641
|
-
function
|
|
642
|
-
l().identify(
|
|
690
|
+
function w(r, e) {
|
|
691
|
+
l().identify(r, e);
|
|
643
692
|
}
|
|
644
|
-
function x(
|
|
645
|
-
l().pageView(
|
|
693
|
+
function x(r) {
|
|
694
|
+
l().pageView(r);
|
|
646
695
|
}
|
|
647
|
-
function z(
|
|
648
|
-
l().pageLeave(
|
|
696
|
+
function z(r) {
|
|
697
|
+
l().pageLeave(r);
|
|
649
698
|
}
|
|
650
699
|
function C() {
|
|
651
700
|
l().reset();
|
|
@@ -657,9 +706,9 @@ export {
|
|
|
657
706
|
v as createAnalytics,
|
|
658
707
|
v as createClientAnalytics,
|
|
659
708
|
l as getAnalytics,
|
|
660
|
-
|
|
709
|
+
w as identify,
|
|
661
710
|
z as pageLeave,
|
|
662
711
|
x as pageView,
|
|
663
712
|
C as reset,
|
|
664
|
-
|
|
713
|
+
y as track
|
|
665
714
|
};
|
|
@@ -67,6 +67,7 @@ export interface AnalyticsProvider {
|
|
|
67
67
|
pageView(properties?: Record<string, unknown>, context?: EventContext): Promise<void> | void;
|
|
68
68
|
pageLeave?(properties?: Record<string, unknown>, context?: EventContext): Promise<void> | void;
|
|
69
69
|
reset(): Promise<void> | void;
|
|
70
|
+
flush?(useBeacon?: boolean): Promise<void> | void;
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
72
73
|
* Provider methods that can be selectively enabled/disabled through routing
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
var u = Object.defineProperty;
|
|
2
|
+
var l = (e, t, r) => t in e ? u(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
|
|
3
|
+
var c = (e, t, r) => l(e, typeof t != "symbol" ? t + "" : t, r);
|
|
4
|
+
var i = class extends Error {
|
|
5
|
+
constructor(e, t, r, a) {
|
|
6
|
+
super(e), this.statusCode = t, this.requestId = r, this.details = a, this.name = "EmitKitError";
|
|
7
|
+
}
|
|
8
|
+
}, y = class extends i {
|
|
9
|
+
constructor(e, t) {
|
|
10
|
+
super(
|
|
11
|
+
`Rate limit exceeded. Resets in ${Math.ceil(e.resetIn / 1e3)}s`,
|
|
12
|
+
429,
|
|
13
|
+
t
|
|
14
|
+
), this.rateLimit = e, this.name = "RateLimitError";
|
|
15
|
+
}
|
|
16
|
+
}, w = class extends i {
|
|
17
|
+
constructor(e, t, r) {
|
|
18
|
+
super(e, 400, r, t), this.validationErrors = t, this.name = "ValidationError";
|
|
19
|
+
}
|
|
20
|
+
}, I = class {
|
|
21
|
+
constructor(e, t) {
|
|
22
|
+
c(this, "config");
|
|
23
|
+
c(this, "lastRateLimit");
|
|
24
|
+
this.config = {
|
|
25
|
+
apiKey: e,
|
|
26
|
+
baseUrl: (t == null ? void 0 : t.baseUrl) || "https://api.emitkit.com",
|
|
27
|
+
timeout: (t == null ? void 0 : t.timeout) || 3e4,
|
|
28
|
+
fetch: (t == null ? void 0 : t.fetch) || globalThis.fetch
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the last known rate limit information
|
|
33
|
+
*/
|
|
34
|
+
get rateLimit() {
|
|
35
|
+
return this.lastRateLimit;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Events API
|
|
39
|
+
*/
|
|
40
|
+
get events() {
|
|
41
|
+
return {
|
|
42
|
+
/**
|
|
43
|
+
* Create a new event
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const result = await client.events.create({
|
|
48
|
+
* channelName: 'payments',
|
|
49
|
+
* title: 'Payment Received',
|
|
50
|
+
* metadata: { amount: 99.99 }
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
create: async (e, t) => this.request("/v1/events", {
|
|
55
|
+
method: "POST",
|
|
56
|
+
body: JSON.stringify(e),
|
|
57
|
+
headers: {
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
...(t == null ? void 0 : t.idempotencyKey) && {
|
|
60
|
+
"Idempotency-Key": t.idempotencyKey
|
|
61
|
+
},
|
|
62
|
+
...t == null ? void 0 : t.headers
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Identify a user with properties and aliases
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const result = await client.identify({
|
|
73
|
+
* user_id: 'user_123',
|
|
74
|
+
* properties: {
|
|
75
|
+
* email: 'user@example.com',
|
|
76
|
+
* name: 'John Doe',
|
|
77
|
+
* plan: 'pro'
|
|
78
|
+
* },
|
|
79
|
+
* aliases: ['user@example.com', 'johndoe']
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
async identify(e, t) {
|
|
84
|
+
return this.request("/v1/identify", {
|
|
85
|
+
method: "POST",
|
|
86
|
+
body: JSON.stringify(e),
|
|
87
|
+
headers: {
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
...t == null ? void 0 : t.headers
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Make a raw API request
|
|
95
|
+
*/
|
|
96
|
+
async request(e, t) {
|
|
97
|
+
const r = `${this.config.baseUrl}${e}`, a = new AbortController(), n = setTimeout(() => a.abort(), this.config.timeout);
|
|
98
|
+
try {
|
|
99
|
+
const s = await this.config.fetch(r, {
|
|
100
|
+
...t,
|
|
101
|
+
headers: {
|
|
102
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
103
|
+
...t.headers
|
|
104
|
+
},
|
|
105
|
+
signal: a.signal
|
|
106
|
+
});
|
|
107
|
+
clearTimeout(n);
|
|
108
|
+
const o = this.extractRateLimit(s);
|
|
109
|
+
o && (this.lastRateLimit = o);
|
|
110
|
+
const d = s.headers.get("X-Idempotent-Replay") === "true";
|
|
111
|
+
s.ok || await this.handleErrorResponse(s, o);
|
|
112
|
+
const h = await s.json(), m = h.requestId || s.headers.get("X-Request-ID");
|
|
113
|
+
return {
|
|
114
|
+
data: h.success ? h.data : h,
|
|
115
|
+
rateLimit: o,
|
|
116
|
+
requestId: m || "unknown",
|
|
117
|
+
wasReplayed: d
|
|
118
|
+
};
|
|
119
|
+
} catch (s) {
|
|
120
|
+
throw clearTimeout(n), s instanceof i ? s : s instanceof Error ? s.name === "AbortError" ? new i("Request timeout", 408) : new i(
|
|
121
|
+
`Network error: ${s.message}`,
|
|
122
|
+
0
|
|
123
|
+
) : new i("Unknown error occurred", 500);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
extractRateLimit(e) {
|
|
127
|
+
const t = e.headers.get("X-RateLimit-Limit"), r = e.headers.get("X-RateLimit-Remaining"), a = e.headers.get("X-RateLimit-Reset");
|
|
128
|
+
if (!t || !r || !a)
|
|
129
|
+
return null;
|
|
130
|
+
const n = parseInt(a, 10) * 1e3, s = Math.max(0, n - Date.now());
|
|
131
|
+
return {
|
|
132
|
+
limit: parseInt(t, 10),
|
|
133
|
+
remaining: parseInt(r, 10),
|
|
134
|
+
reset: parseInt(a, 10),
|
|
135
|
+
resetIn: s
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
async handleErrorResponse(e, t) {
|
|
139
|
+
const r = await e.json().catch(() => ({})), a = r.requestId || e.headers.get("X-Request-ID") || void 0;
|
|
140
|
+
throw e.status === 429 && t ? new y(t, a) : e.status === 400 && r.details ? new w(
|
|
141
|
+
r.error || "Validation error",
|
|
142
|
+
r.details,
|
|
143
|
+
a
|
|
144
|
+
) : new i(
|
|
145
|
+
r.error || r.message || `HTTP ${e.status}`,
|
|
146
|
+
e.status,
|
|
147
|
+
a || void 0,
|
|
148
|
+
r
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
export {
|
|
153
|
+
I as EmitKit,
|
|
154
|
+
i as EmitKitError,
|
|
155
|
+
y as RateLimitError,
|
|
156
|
+
w as ValidationError
|
|
157
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BaseEvent, EventContext } from '../../core/events/types.js';
|
|
2
|
+
import { BaseAnalyticsProvider } from '../base.provider.js';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for EmitKit server provider
|
|
5
|
+
*/
|
|
6
|
+
export interface EmitKitServerConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Your EmitKit API key (starts with emitkit_)
|
|
9
|
+
*/
|
|
10
|
+
apiKey: string;
|
|
11
|
+
/**
|
|
12
|
+
* Default channel name for events
|
|
13
|
+
* @default 'analytics'
|
|
14
|
+
*/
|
|
15
|
+
channelName?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Send notification for events
|
|
18
|
+
* @default true
|
|
19
|
+
*/
|
|
20
|
+
notify?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Display style for events
|
|
23
|
+
* @default 'notification'
|
|
24
|
+
*/
|
|
25
|
+
displayAs?: "message" | "notification";
|
|
26
|
+
/**
|
|
27
|
+
* Enable debug logging
|
|
28
|
+
*/
|
|
29
|
+
debug?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Enable/disable the provider
|
|
32
|
+
*/
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare class EmitKitServerProvider extends BaseAnalyticsProvider {
|
|
36
|
+
name: string;
|
|
37
|
+
private client?;
|
|
38
|
+
private initialized;
|
|
39
|
+
private config;
|
|
40
|
+
private currentUserId?;
|
|
41
|
+
private currentUserEmail?;
|
|
42
|
+
constructor(config: EmitKitServerConfig);
|
|
43
|
+
initialize(): Promise<void>;
|
|
44
|
+
identify(userId: string, traits?: Record<string, unknown>): void;
|
|
45
|
+
track(event: BaseEvent, context?: EventContext): Promise<void>;
|
|
46
|
+
pageView(properties?: Record<string, unknown>, context?: EventContext): void;
|
|
47
|
+
reset(): Promise<void>;
|
|
48
|
+
shutdown(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Format event action into a human-readable title
|
|
51
|
+
* Converts: "user_signed_up" -> "User Signed Up"
|
|
52
|
+
*/
|
|
53
|
+
private formatEventTitle;
|
|
54
|
+
/**
|
|
55
|
+
* Generate a description for the event
|
|
56
|
+
*/
|
|
57
|
+
private getEventDescription;
|
|
58
|
+
/**
|
|
59
|
+
* Get an appropriate icon for the event category
|
|
60
|
+
*/
|
|
61
|
+
private getEventIcon;
|
|
62
|
+
}
|
|
@@ -5,5 +5,7 @@ export { BentoServerProvider } from './bento/server.js';
|
|
|
5
5
|
export type { BentoServerConfig, BentoAnalyticsOptions, } from './bento/server.js';
|
|
6
6
|
export { PirschServerProvider } from './pirsch/server.js';
|
|
7
7
|
export type { PirschServerConfig } from './pirsch/server.js';
|
|
8
|
+
export { EmitKitServerProvider } from './emitkit/server.js';
|
|
9
|
+
export type { EmitKitServerConfig } from './emitkit/server.js';
|
|
8
10
|
export { ingestProxyEvents, createProxyHandler } from './proxy/server.js';
|
|
9
11
|
export type { IngestProxyEventsConfig } from './proxy/server.js';
|
package/dist/providers/server.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { B
|
|
5
|
-
import { P as
|
|
6
|
-
class
|
|
1
|
+
var O = Object.defineProperty;
|
|
2
|
+
var q = (g, t, i) => t in g ? O(g, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : g[t] = i;
|
|
3
|
+
var o = (g, t, i) => q(g, typeof t != "symbol" ? t + "" : t, i);
|
|
4
|
+
import { B } from "../base.provider-AfFL5W_P.js";
|
|
5
|
+
import { P as X } from "../server-DjEk1fUD.js";
|
|
6
|
+
class H extends B {
|
|
7
7
|
constructor(i) {
|
|
8
8
|
super({ debug: i.debug, enabled: i.enabled });
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
o(this, "name", "Bento-Server");
|
|
10
|
+
o(this, "client");
|
|
11
|
+
o(this, "initialized", !1);
|
|
12
|
+
o(this, "config");
|
|
13
|
+
o(this, "currentUserEmail");
|
|
14
14
|
this.config = i;
|
|
15
15
|
}
|
|
16
16
|
async initialize() {
|
|
@@ -23,7 +23,7 @@ class N extends V {
|
|
|
23
23
|
if (!((e = this.config.authentication) != null && e.secretKey) || typeof this.config.authentication.secretKey != "string")
|
|
24
24
|
throw new Error("Bento requires authentication.secretKey");
|
|
25
25
|
try {
|
|
26
|
-
const { Analytics: r } = await import("../bento-node-sdk.esm-CWEAoj97.js"), { debug:
|
|
26
|
+
const { Analytics: r } = await import("../bento-node-sdk.esm-CWEAoj97.js"), { debug: a, enabled: s, ...d } = this.config;
|
|
27
27
|
this.client = new r(d), this.initialized = !0, this.log("Initialized successfully", {
|
|
28
28
|
siteUuid: this.config.siteUuid
|
|
29
29
|
});
|
|
@@ -46,25 +46,25 @@ class N extends V {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
this.currentUserEmail = r;
|
|
49
|
-
const
|
|
50
|
-
|
|
49
|
+
const a = e ? { ...e } : {};
|
|
50
|
+
a.email = void 0, this.client.V1.addSubscriber({
|
|
51
51
|
email: r,
|
|
52
|
-
fields:
|
|
52
|
+
fields: a
|
|
53
53
|
}).catch((s) => {
|
|
54
54
|
console.error("[Bento-Server] Failed to identify user:", s);
|
|
55
55
|
}), this.log("Identified user", { userId: i, email: r, traits: e });
|
|
56
56
|
}
|
|
57
57
|
async track(i, e) {
|
|
58
|
-
var d,
|
|
58
|
+
var d, p, u, l;
|
|
59
59
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
60
|
-
const r = ((d = e == null ? void 0 : e.user) == null ? void 0 : d.email) || this.currentUserEmail || ((
|
|
60
|
+
const r = ((d = e == null ? void 0 : e.user) == null ? void 0 : d.email) || this.currentUserEmail || ((p = e == null ? void 0 : e.user) == null ? void 0 : p.userId) || i.userId;
|
|
61
61
|
if (!r || !r.includes("@")) {
|
|
62
62
|
console.warn(
|
|
63
63
|
"[Bento-Server] Skipping event - Bento requires an email address. Anonymous events are not currently supported by the Bento Node SDK. For now, use the Bento client provider for anonymous tracking. If you're using a proxy, use the hybrid pattern as described in the docs. For identified users, call identify() with a valid email before tracking events."
|
|
64
64
|
);
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
|
-
const
|
|
67
|
+
const a = {
|
|
68
68
|
...i.properties,
|
|
69
69
|
category: i.category,
|
|
70
70
|
timestamp: i.timestamp || Date.now(),
|
|
@@ -83,21 +83,21 @@ class N extends V {
|
|
|
83
83
|
...(e == null ? void 0 : e.device) && { device: e.device },
|
|
84
84
|
...(e == null ? void 0 : e.utm) && { utm: e.utm },
|
|
85
85
|
site: this.config.siteUuid,
|
|
86
|
-
...((
|
|
86
|
+
...((u = e == null ? void 0 : e.user) == null ? void 0 : u.userId) && { visitor: e.user.userId }
|
|
87
87
|
}, s = ((l = e == null ? void 0 : e.user) == null ? void 0 : l.traits) || {};
|
|
88
88
|
try {
|
|
89
89
|
await this.client.V1.track({
|
|
90
90
|
email: r,
|
|
91
91
|
type: `$${i.action}`,
|
|
92
|
-
details:
|
|
92
|
+
details: a,
|
|
93
93
|
fields: s
|
|
94
94
|
}), this.log("Tracked event", { event: i, context: e });
|
|
95
|
-
} catch (
|
|
96
|
-
console.error("[Bento-Server] Failed to track event:",
|
|
95
|
+
} catch (h) {
|
|
96
|
+
console.error("[Bento-Server] Failed to track event:", h);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
pageView(i, e) {
|
|
100
|
-
var d,
|
|
100
|
+
var d, p, u;
|
|
101
101
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
102
102
|
const r = ((d = e == null ? void 0 : e.user) == null ? void 0 : d.email) || this.currentUserEmail;
|
|
103
103
|
if (!r || !r.includes("@")) {
|
|
@@ -106,7 +106,7 @@ class N extends V {
|
|
|
106
106
|
);
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
-
const
|
|
109
|
+
const a = {
|
|
110
110
|
...i,
|
|
111
111
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
112
112
|
...(e == null ? void 0 : e.page) && {
|
|
@@ -121,12 +121,12 @@ class N extends V {
|
|
|
121
121
|
}
|
|
122
122
|
},
|
|
123
123
|
site: this.config.siteUuid,
|
|
124
|
-
...((
|
|
125
|
-
}, s = ((
|
|
124
|
+
...((p = e == null ? void 0 : e.user) == null ? void 0 : p.userId) && { visitor: e.user.userId }
|
|
125
|
+
}, s = ((u = e == null ? void 0 : e.user) == null ? void 0 : u.traits) || {};
|
|
126
126
|
this.client.V1.track({
|
|
127
127
|
email: r,
|
|
128
128
|
type: "$view",
|
|
129
|
-
details:
|
|
129
|
+
details: a,
|
|
130
130
|
fields: s
|
|
131
131
|
}).catch((l) => {
|
|
132
132
|
console.error("[Bento-Server] Failed to track page view:", l);
|
|
@@ -139,13 +139,13 @@ class N extends V {
|
|
|
139
139
|
this.client = void 0, this.initialized = !1, this.log("Shutdown complete");
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
-
class W extends
|
|
142
|
+
class W extends B {
|
|
143
143
|
constructor(i) {
|
|
144
144
|
super({ debug: i.debug, enabled: i.enabled });
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
o(this, "name", "Pirsch-Server");
|
|
146
|
+
o(this, "client");
|
|
147
|
+
o(this, "initialized", !1);
|
|
148
|
+
o(this, "config");
|
|
149
149
|
this.config = i;
|
|
150
150
|
}
|
|
151
151
|
async initialize() {
|
|
@@ -160,7 +160,7 @@ class W extends V {
|
|
|
160
160
|
"Pirsch requires a clientId when using OAuth authentication (clientSecret doesn't start with 'pa_'). Either provide a clientId or use an access key (starts with 'pa_') as clientSecret."
|
|
161
161
|
);
|
|
162
162
|
try {
|
|
163
|
-
const { Pirsch: e } = await import("../index-zS7gy63J.js").then((d) => d.i), { debug: r, enabled:
|
|
163
|
+
const { Pirsch: e } = await import("../index-zS7gy63J.js").then((d) => d.i), { debug: r, enabled: a, ...s } = this.config;
|
|
164
164
|
this.client = new e(s), this.initialized = !0, this.log("Initialized successfully", {
|
|
165
165
|
hostname: this.config.hostname,
|
|
166
166
|
authMode: i ? "access-key" : "oauth"
|
|
@@ -178,7 +178,7 @@ class W extends V {
|
|
|
178
178
|
url: "https://identify",
|
|
179
179
|
ip: "0.0.0.0",
|
|
180
180
|
user_agent: "analytics-library"
|
|
181
|
-
},
|
|
181
|
+
}, a = {
|
|
182
182
|
userId: i,
|
|
183
183
|
...e && Object.fromEntries(
|
|
184
184
|
Object.entries(e).filter(
|
|
@@ -186,113 +186,113 @@ class W extends V {
|
|
|
186
186
|
)
|
|
187
187
|
)
|
|
188
188
|
};
|
|
189
|
-
this.client.event("user_identified", r, 0,
|
|
189
|
+
this.client.event("user_identified", r, 0, a).catch((s) => {
|
|
190
190
|
console.error("[Pirsch-Server] Failed to track identify event:", s);
|
|
191
191
|
}), this.log("Identified user via event", { userId: i, traits: e });
|
|
192
192
|
}
|
|
193
193
|
async track(i, e) {
|
|
194
|
-
var
|
|
194
|
+
var h, n, c, v, y, m, w, b, E, I, k, S, _, z, P, U, K, A, f, $, D, j, V;
|
|
195
195
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
196
|
-
const r = e,
|
|
197
|
-
if (!
|
|
196
|
+
const r = e, a = ((h = r == null ? void 0 : r.device) == null ? void 0 : h.ip) || ((n = r == null ? void 0 : r.server) == null ? void 0 : n.ip), s = ((c = r == null ? void 0 : r.server) == null ? void 0 : c.userAgent) || ((v = r == null ? void 0 : r.device) == null ? void 0 : v.userAgent);
|
|
197
|
+
if (!a || !s) {
|
|
198
198
|
this.log(
|
|
199
199
|
"Skipping event - missing required IP or user-agent from context",
|
|
200
200
|
{
|
|
201
|
-
hasIp: !!
|
|
201
|
+
hasIp: !!a,
|
|
202
202
|
hasUserAgent: !!s,
|
|
203
203
|
event: i.action
|
|
204
204
|
}
|
|
205
205
|
);
|
|
206
206
|
return;
|
|
207
207
|
}
|
|
208
|
-
const
|
|
209
|
-
url: ((
|
|
210
|
-
ip:
|
|
208
|
+
const p = {
|
|
209
|
+
url: ((y = e == null ? void 0 : e.page) == null ? void 0 : y.url) || ((m = e == null ? void 0 : e.page) != null && m.protocol && ((w = e == null ? void 0 : e.page) != null && w.host) && ((b = e == null ? void 0 : e.page) != null && b.path) ? `${e.page.protocol}://${e.page.host}${e.page.path}` : (E = e == null ? void 0 : e.page) != null && E.path ? `https://${this.config.hostname}${e.page.path}` : "https://event"),
|
|
210
|
+
ip: a,
|
|
211
211
|
user_agent: s,
|
|
212
|
-
...((
|
|
213
|
-
...((
|
|
214
|
-
...((
|
|
212
|
+
...((I = e == null ? void 0 : e.page) == null ? void 0 : I.title) && { title: e.page.title },
|
|
213
|
+
...((k = e == null ? void 0 : e.page) == null ? void 0 : k.referrer) && { referrer: e.page.referrer },
|
|
214
|
+
...((_ = (S = e == null ? void 0 : e.device) == null ? void 0 : S.screen) == null ? void 0 : _.width) && {
|
|
215
215
|
screen_width: e.device.screen.width
|
|
216
216
|
},
|
|
217
|
-
...((
|
|
217
|
+
...((P = (z = e == null ? void 0 : e.device) == null ? void 0 : z.screen) == null ? void 0 : P.height) && {
|
|
218
218
|
screen_height: e.device.screen.height
|
|
219
219
|
},
|
|
220
|
-
...((
|
|
220
|
+
...((K = (U = e == null ? void 0 : e.device) == null ? void 0 : U.viewport) == null ? void 0 : K.width) && {
|
|
221
221
|
sec_ch_viewport_width: String(e.device.viewport.width)
|
|
222
222
|
},
|
|
223
|
-
...((
|
|
223
|
+
...((A = e == null ? void 0 : e.device) == null ? void 0 : A.language) && {
|
|
224
224
|
accept_language: e.device.language
|
|
225
225
|
},
|
|
226
|
-
...((
|
|
226
|
+
...((f = e == null ? void 0 : e.device) == null ? void 0 : f.type) && {
|
|
227
227
|
sec_ch_ua_mobile: e.device.type === "mobile" || e.device.type === "tablet" ? "?1" : "?0"
|
|
228
228
|
},
|
|
229
229
|
...(($ = e == null ? void 0 : e.device) == null ? void 0 : $.os) && { sec_ch_ua_platform: e.device.os }
|
|
230
230
|
}, l = {
|
|
231
231
|
...Object.fromEntries(
|
|
232
232
|
Object.entries(i.properties).filter(
|
|
233
|
-
([,
|
|
233
|
+
([, F]) => typeof F == "string" || typeof F == "number" || typeof F == "boolean"
|
|
234
234
|
)
|
|
235
235
|
),
|
|
236
236
|
category: i.category,
|
|
237
237
|
timestamp: String(i.timestamp || Date.now()),
|
|
238
238
|
...i.userId && { userId: i.userId },
|
|
239
239
|
...i.sessionId && { sessionId: i.sessionId },
|
|
240
|
-
...((
|
|
240
|
+
...((D = e == null ? void 0 : e.user) == null ? void 0 : D.email) && { user_email: e.user.email },
|
|
241
241
|
...((j = e == null ? void 0 : e.device) == null ? void 0 : j.timezone) && { timezone: e.device.timezone },
|
|
242
|
-
...((
|
|
242
|
+
...((V = e == null ? void 0 : e.device) == null ? void 0 : V.browser) && { browser: e.device.browser }
|
|
243
243
|
};
|
|
244
244
|
try {
|
|
245
|
-
await this.client.event(i.action,
|
|
246
|
-
} catch (
|
|
247
|
-
console.error("[Pirsch-Server] Failed to track event:",
|
|
245
|
+
await this.client.event(i.action, p, 0, l), this.log("Tracked event", { event: i, context: e });
|
|
246
|
+
} catch (F) {
|
|
247
|
+
console.error("[Pirsch-Server] Failed to track event:", F);
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
pageView(i, e) {
|
|
251
|
-
var
|
|
251
|
+
var u, l, h, n, c, v, y, m, w, b, E, I, k, S, _, z, P, U, K, A;
|
|
252
252
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
253
|
-
const r = e,
|
|
254
|
-
if (!
|
|
253
|
+
const r = e, a = ((u = r == null ? void 0 : r.device) == null ? void 0 : u.ip) || ((l = r == null ? void 0 : r.server) == null ? void 0 : l.ip), s = ((h = r == null ? void 0 : r.server) == null ? void 0 : h.userAgent) || ((n = r == null ? void 0 : r.device) == null ? void 0 : n.userAgent);
|
|
254
|
+
if (!a || !s) {
|
|
255
255
|
this.log(
|
|
256
256
|
"Skipping pageView - missing required IP or user-agent from context",
|
|
257
257
|
{
|
|
258
|
-
hasIp: !!
|
|
258
|
+
hasIp: !!a,
|
|
259
259
|
hasUserAgent: !!s
|
|
260
260
|
}
|
|
261
261
|
);
|
|
262
262
|
return;
|
|
263
263
|
}
|
|
264
|
-
const
|
|
265
|
-
url: ((
|
|
266
|
-
ip:
|
|
264
|
+
const p = {
|
|
265
|
+
url: ((c = e == null ? void 0 : e.page) == null ? void 0 : c.url) || ((v = e == null ? void 0 : e.page) != null && v.protocol && ((y = e == null ? void 0 : e.page) != null && y.host) && ((m = e == null ? void 0 : e.page) != null && m.path) ? `${e.page.protocol}://${e.page.host}${e.page.path}` : (w = e == null ? void 0 : e.page) != null && w.path ? `https://${this.config.hostname}${e.page.path}` : "https://pageview"),
|
|
266
|
+
ip: a,
|
|
267
267
|
user_agent: s,
|
|
268
268
|
...((b = e == null ? void 0 : e.page) == null ? void 0 : b.title) && { title: e.page.title },
|
|
269
|
-
...((
|
|
270
|
-
...((
|
|
269
|
+
...((E = e == null ? void 0 : e.page) == null ? void 0 : E.referrer) && { referrer: e.page.referrer },
|
|
270
|
+
...((k = (I = e == null ? void 0 : e.device) == null ? void 0 : I.screen) == null ? void 0 : k.width) && {
|
|
271
271
|
screen_width: e.device.screen.width
|
|
272
272
|
},
|
|
273
|
-
...((
|
|
273
|
+
...((_ = (S = e == null ? void 0 : e.device) == null ? void 0 : S.screen) == null ? void 0 : _.height) && {
|
|
274
274
|
screen_height: e.device.screen.height
|
|
275
275
|
},
|
|
276
|
-
...((
|
|
276
|
+
...((P = (z = e == null ? void 0 : e.device) == null ? void 0 : z.viewport) == null ? void 0 : P.width) && {
|
|
277
277
|
sec_ch_viewport_width: String(e.device.viewport.width)
|
|
278
278
|
},
|
|
279
|
-
...((
|
|
279
|
+
...((U = e == null ? void 0 : e.device) == null ? void 0 : U.language) && {
|
|
280
280
|
accept_language: e.device.language
|
|
281
281
|
},
|
|
282
|
-
...((
|
|
282
|
+
...((K = e == null ? void 0 : e.device) == null ? void 0 : K.type) && {
|
|
283
283
|
sec_ch_ua_mobile: e.device.type === "mobile" || e.device.type === "tablet" ? "?1" : "?0"
|
|
284
284
|
},
|
|
285
|
-
...((
|
|
285
|
+
...((A = e == null ? void 0 : e.device) == null ? void 0 : A.os) && { sec_ch_ua_platform: e.device.os },
|
|
286
286
|
...i && {
|
|
287
287
|
tags: Object.fromEntries(
|
|
288
288
|
Object.entries(i).filter(
|
|
289
|
-
([,
|
|
289
|
+
([, f]) => typeof f == "string" || typeof f == "number" || typeof f == "boolean"
|
|
290
290
|
)
|
|
291
291
|
)
|
|
292
292
|
}
|
|
293
293
|
};
|
|
294
|
-
this.client.hit(
|
|
295
|
-
console.error("[Pirsch-Server] Failed to track page view:",
|
|
294
|
+
this.client.hit(p).catch((f) => {
|
|
295
|
+
console.error("[Pirsch-Server] Failed to track page view:", f);
|
|
296
296
|
}), this.log("Tracked page view", { properties: i, context: e });
|
|
297
297
|
}
|
|
298
298
|
async reset() {
|
|
@@ -310,80 +310,271 @@ class W extends V {
|
|
|
310
310
|
this.client = void 0, this.initialized = !1, this.log("Shutdown complete");
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
|
-
|
|
314
|
-
|
|
313
|
+
class G extends B {
|
|
314
|
+
constructor(i) {
|
|
315
|
+
super({ debug: i.debug, enabled: i.enabled });
|
|
316
|
+
o(this, "name", "EmitKit-Server");
|
|
317
|
+
o(this, "client");
|
|
318
|
+
o(this, "initialized", !1);
|
|
319
|
+
o(this, "config");
|
|
320
|
+
o(this, "currentUserId");
|
|
321
|
+
o(this, "currentUserEmail");
|
|
322
|
+
this.config = i;
|
|
323
|
+
}
|
|
324
|
+
async initialize() {
|
|
325
|
+
if (this.isEnabled() && !this.initialized) {
|
|
326
|
+
if (!this.config.apiKey || typeof this.config.apiKey != "string")
|
|
327
|
+
throw new Error("EmitKit requires an apiKey");
|
|
328
|
+
this.config.apiKey.startsWith("emitkit_") || console.warn(
|
|
329
|
+
"[EmitKit-Server] API key should start with 'emitkit_'. Double check your configuration."
|
|
330
|
+
);
|
|
331
|
+
try {
|
|
332
|
+
const { EmitKit: i } = await import("../index-CBs091W0.js");
|
|
333
|
+
this.client = new i(this.config.apiKey), this.initialized = !0, this.log("Initialized successfully");
|
|
334
|
+
} catch (i) {
|
|
335
|
+
throw console.error(
|
|
336
|
+
"[EmitKit-Server] Failed to initialize. Make sure @emitkit/js is installed:",
|
|
337
|
+
i
|
|
338
|
+
), i;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
identify(i, e) {
|
|
343
|
+
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
344
|
+
this.currentUserId = i;
|
|
345
|
+
const r = (e == null ? void 0 : e.email) || i;
|
|
346
|
+
r && r.includes("@") && (this.currentUserEmail = r);
|
|
347
|
+
const a = [];
|
|
348
|
+
i && a.push(i), r && r !== i && a.push(r), e != null && e.username && typeof e.username == "string" && a.push(e.username), this.client.identify({
|
|
349
|
+
user_id: i,
|
|
350
|
+
properties: e || {},
|
|
351
|
+
aliases: a.length > 0 ? a : void 0
|
|
352
|
+
}).then((s) => {
|
|
353
|
+
var d, p, u, l, h;
|
|
354
|
+
this.log("Identified user", {
|
|
355
|
+
userId: i,
|
|
356
|
+
email: r,
|
|
357
|
+
identityId: s.data.id,
|
|
358
|
+
aliasesCreated: ((p = (d = s.data.aliases) == null ? void 0 : d.created) == null ? void 0 : p.length) || 0,
|
|
359
|
+
aliasesFailed: ((l = (u = s.data.aliases) == null ? void 0 : u.failed) == null ? void 0 : l.length) || 0
|
|
360
|
+
}), (h = s.data.aliases) != null && h.failed && s.data.aliases.failed.length > 0 && console.warn(
|
|
361
|
+
"[EmitKit-Server] Some aliases failed to create:",
|
|
362
|
+
s.data.aliases.failed
|
|
363
|
+
);
|
|
364
|
+
}).catch((s) => {
|
|
365
|
+
console.error("[EmitKit-Server] Failed to identify user:", s);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
async track(i, e) {
|
|
369
|
+
var u, l, h;
|
|
370
|
+
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
371
|
+
const r = ((u = e == null ? void 0 : e.user) == null ? void 0 : u.email) || ((l = e == null ? void 0 : e.user) == null ? void 0 : l.userId) || i.userId || this.currentUserEmail || this.currentUserId, a = this.formatEventTitle(i.action), s = {
|
|
372
|
+
...i.properties,
|
|
373
|
+
category: i.category,
|
|
374
|
+
timestamp: i.timestamp || Date.now(),
|
|
375
|
+
...i.sessionId && { sessionId: i.sessionId },
|
|
376
|
+
...(e == null ? void 0 : e.page) && {
|
|
377
|
+
page: {
|
|
378
|
+
url: e.page.url,
|
|
379
|
+
host: e.page.host,
|
|
380
|
+
path: e.page.path,
|
|
381
|
+
title: e.page.title,
|
|
382
|
+
protocol: e.page.protocol,
|
|
383
|
+
referrer: e.page.referrer,
|
|
384
|
+
...e.page.search && { search: e.page.search }
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
...(e == null ? void 0 : e.device) && { device: e.device },
|
|
388
|
+
...(e == null ? void 0 : e.utm) && { utm: e.utm },
|
|
389
|
+
...(e == null ? void 0 : e.server) && { server: e.server }
|
|
390
|
+
}, d = [];
|
|
391
|
+
i.category && d.push(i.category), (h = i.properties) != null && h.tags && Array.isArray(i.properties.tags) && i.properties.tags.every((n) => typeof n == "string") && d.push(...i.properties.tags);
|
|
392
|
+
const p = this.config.channelName || "analytics";
|
|
393
|
+
try {
|
|
394
|
+
const n = await this.client.events.create({
|
|
395
|
+
channelName: p,
|
|
396
|
+
title: a,
|
|
397
|
+
description: this.getEventDescription(i, e),
|
|
398
|
+
icon: this.getEventIcon(i.category),
|
|
399
|
+
tags: d.length > 0 ? d : void 0,
|
|
400
|
+
metadata: s,
|
|
401
|
+
userId: r || null,
|
|
402
|
+
notify: this.config.notify ?? !0,
|
|
403
|
+
displayAs: this.config.displayAs || "notification",
|
|
404
|
+
source: "stacksee-analytics"
|
|
405
|
+
});
|
|
406
|
+
this.log("Tracked event", {
|
|
407
|
+
eventId: n.data.id,
|
|
408
|
+
action: i.action,
|
|
409
|
+
userId: r,
|
|
410
|
+
channelName: p
|
|
411
|
+
});
|
|
412
|
+
} catch (n) {
|
|
413
|
+
throw console.error("[EmitKit-Server] Failed to track event:", n), n;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
pageView(i, e) {
|
|
417
|
+
var d, p, u;
|
|
418
|
+
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
419
|
+
const r = ((d = e == null ? void 0 : e.user) == null ? void 0 : d.email) || ((p = e == null ? void 0 : e.user) == null ? void 0 : p.userId) || this.currentUserEmail || this.currentUserId, a = {
|
|
420
|
+
...i,
|
|
421
|
+
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
422
|
+
...(e == null ? void 0 : e.page) && {
|
|
423
|
+
page: {
|
|
424
|
+
url: e.page.url,
|
|
425
|
+
host: e.page.host,
|
|
426
|
+
path: e.page.path,
|
|
427
|
+
title: e.page.title,
|
|
428
|
+
protocol: e.page.protocol,
|
|
429
|
+
referrer: e.page.referrer,
|
|
430
|
+
...e.page.search && { search: e.page.search }
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
...(e == null ? void 0 : e.device) && { device: e.device },
|
|
434
|
+
...(e == null ? void 0 : e.utm) && { utm: e.utm },
|
|
435
|
+
...(e == null ? void 0 : e.server) && { server: e.server }
|
|
436
|
+
}, s = this.config.channelName || "analytics";
|
|
437
|
+
this.client.events.create({
|
|
438
|
+
channelName: s,
|
|
439
|
+
title: "Page View",
|
|
440
|
+
description: ((u = e == null ? void 0 : e.page) == null ? void 0 : u.path) || "User viewed a page",
|
|
441
|
+
icon: "👁️",
|
|
442
|
+
tags: ["page_view", "navigation"],
|
|
443
|
+
metadata: a,
|
|
444
|
+
userId: r || null,
|
|
445
|
+
notify: !1,
|
|
446
|
+
// Don't notify for page views by default
|
|
447
|
+
displayAs: "message",
|
|
448
|
+
source: "stacksee-analytics"
|
|
449
|
+
}).then((l) => {
|
|
450
|
+
var h;
|
|
451
|
+
this.log("Tracked page view", {
|
|
452
|
+
eventId: l.data.id,
|
|
453
|
+
path: (h = e == null ? void 0 : e.page) == null ? void 0 : h.path,
|
|
454
|
+
userId: r
|
|
455
|
+
});
|
|
456
|
+
}).catch((l) => {
|
|
457
|
+
console.error("[EmitKit-Server] Failed to track page view:", l);
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
async reset() {
|
|
461
|
+
!this.isEnabled() || !this.initialized || !this.client || (this.currentUserId = void 0, this.currentUserEmail = void 0, this.log("Reset user session"));
|
|
462
|
+
}
|
|
463
|
+
async shutdown() {
|
|
464
|
+
this.client = void 0, this.initialized = !1, this.log("Shutdown complete");
|
|
465
|
+
}
|
|
466
|
+
// ============================================================================
|
|
467
|
+
// Helper Methods
|
|
468
|
+
// ============================================================================
|
|
469
|
+
/**
|
|
470
|
+
* Format event action into a human-readable title
|
|
471
|
+
* Converts: "user_signed_up" -> "User Signed Up"
|
|
472
|
+
*/
|
|
473
|
+
formatEventTitle(i) {
|
|
474
|
+
return i.split("_").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" ");
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Generate a description for the event
|
|
478
|
+
*/
|
|
479
|
+
getEventDescription(i, e) {
|
|
480
|
+
var a;
|
|
481
|
+
return (a = i.properties) != null && a.description && typeof i.properties.description == "string" ? i.properties.description : {
|
|
482
|
+
engagement: "User interaction event",
|
|
483
|
+
user: "User lifecycle event",
|
|
484
|
+
navigation: "Navigation event",
|
|
485
|
+
error: "Error or exception occurred",
|
|
486
|
+
performance: "Performance metric",
|
|
487
|
+
conversion: "Conversion event"
|
|
488
|
+
}[i.category] || void 0;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Get an appropriate icon for the event category
|
|
492
|
+
*/
|
|
493
|
+
getEventIcon(i) {
|
|
494
|
+
return {
|
|
495
|
+
engagement: "👆",
|
|
496
|
+
user: "👤",
|
|
497
|
+
navigation: "🧭",
|
|
498
|
+
error: "❌",
|
|
499
|
+
performance: "⚡",
|
|
500
|
+
conversion: "💰"
|
|
501
|
+
}[i];
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
async function T(g, t, i) {
|
|
505
|
+
var e, r, a, s;
|
|
315
506
|
try {
|
|
316
|
-
const d = await
|
|
507
|
+
const d = await g.json();
|
|
317
508
|
if (!d.events || !Array.isArray(d.events))
|
|
318
509
|
throw new Error("Invalid payload: missing events array");
|
|
319
|
-
const
|
|
320
|
-
for (const
|
|
510
|
+
const p = i != null && i.extractIp ? i.extractIp(g) : N(g), u = g.headers.get("user-agent"), l = i != null && i.enrichContext ? i.enrichContext(g) : {};
|
|
511
|
+
for (const h of d.events)
|
|
321
512
|
try {
|
|
322
|
-
switch (
|
|
513
|
+
switch (h.type) {
|
|
323
514
|
case "track": {
|
|
324
|
-
const
|
|
325
|
-
...
|
|
515
|
+
const n = {
|
|
516
|
+
...h.context,
|
|
326
517
|
...l,
|
|
327
518
|
server: {
|
|
328
|
-
...(e =
|
|
519
|
+
...(e = h.context) == null ? void 0 : e.server,
|
|
329
520
|
...typeof (l == null ? void 0 : l.server) == "object" && l.server !== null ? l.server : {},
|
|
330
|
-
...
|
|
521
|
+
...u ? { userAgent: u } : {}
|
|
331
522
|
},
|
|
332
523
|
device: {
|
|
333
|
-
...(r =
|
|
334
|
-
...
|
|
524
|
+
...(r = h.context) == null ? void 0 : r.device,
|
|
525
|
+
...p ? { ip: p } : {}
|
|
335
526
|
}
|
|
336
527
|
};
|
|
337
|
-
await
|
|
338
|
-
|
|
528
|
+
await t.track(
|
|
529
|
+
h.event.action,
|
|
339
530
|
// biome-ignore lint/suspicious/noExplicitAny: Properties from JSON cannot be type-checked against TEventMap at compile time
|
|
340
|
-
|
|
531
|
+
h.event.properties,
|
|
341
532
|
{
|
|
342
|
-
userId:
|
|
343
|
-
sessionId:
|
|
344
|
-
context:
|
|
533
|
+
userId: h.event.userId,
|
|
534
|
+
sessionId: h.event.sessionId,
|
|
535
|
+
context: n
|
|
345
536
|
}
|
|
346
537
|
);
|
|
347
538
|
break;
|
|
348
539
|
}
|
|
349
540
|
case "identify": {
|
|
350
|
-
|
|
541
|
+
t.identify(h.userId, h.traits);
|
|
351
542
|
break;
|
|
352
543
|
}
|
|
353
544
|
case "pageView": {
|
|
354
|
-
const
|
|
355
|
-
...
|
|
545
|
+
const n = {
|
|
546
|
+
...h.context,
|
|
356
547
|
...l,
|
|
357
548
|
server: {
|
|
358
|
-
...(
|
|
549
|
+
...(a = h.context) == null ? void 0 : a.server,
|
|
359
550
|
...typeof (l == null ? void 0 : l.server) == "object" && l.server !== null ? l.server : {},
|
|
360
|
-
...
|
|
551
|
+
...u ? { userAgent: u } : {}
|
|
361
552
|
},
|
|
362
553
|
device: {
|
|
363
|
-
...(s =
|
|
364
|
-
...
|
|
554
|
+
...(s = h.context) == null ? void 0 : s.device,
|
|
555
|
+
...p ? { ip: p } : {}
|
|
365
556
|
}
|
|
366
557
|
};
|
|
367
|
-
|
|
368
|
-
context:
|
|
558
|
+
t.pageView(h.properties, {
|
|
559
|
+
context: n
|
|
369
560
|
});
|
|
370
561
|
break;
|
|
371
562
|
}
|
|
372
563
|
case "reset":
|
|
373
564
|
break;
|
|
374
565
|
default:
|
|
375
|
-
console.warn("[Proxy] Unknown event type:",
|
|
566
|
+
console.warn("[Proxy] Unknown event type:", h);
|
|
376
567
|
}
|
|
377
|
-
} catch (
|
|
378
|
-
i != null && i.onError ? i.onError(
|
|
568
|
+
} catch (n) {
|
|
569
|
+
i != null && i.onError ? i.onError(n) : console.error("[Proxy] Failed to process event:", n);
|
|
379
570
|
}
|
|
380
571
|
} catch (d) {
|
|
381
572
|
throw i != null && i.onError ? i.onError(d) : console.error("[Proxy] Failed to ingest events:", d), d;
|
|
382
573
|
}
|
|
383
574
|
}
|
|
384
|
-
function
|
|
575
|
+
function N(g) {
|
|
385
576
|
var i;
|
|
386
|
-
const
|
|
577
|
+
const t = [
|
|
387
578
|
"x-forwarded-for",
|
|
388
579
|
"x-real-ip",
|
|
389
580
|
"cf-connecting-ip",
|
|
@@ -391,26 +582,27 @@ function T(p) {
|
|
|
391
582
|
"x-client-ip",
|
|
392
583
|
"x-cluster-client-ip"
|
|
393
584
|
];
|
|
394
|
-
for (const e of
|
|
395
|
-
const r =
|
|
585
|
+
for (const e of t) {
|
|
586
|
+
const r = g.headers.get(e);
|
|
396
587
|
if (r)
|
|
397
588
|
return (i = r.split(",")[0]) == null ? void 0 : i.trim();
|
|
398
589
|
}
|
|
399
590
|
}
|
|
400
|
-
function
|
|
591
|
+
function J(g, t) {
|
|
401
592
|
return async (i) => {
|
|
402
593
|
try {
|
|
403
|
-
return await
|
|
594
|
+
return await T(i, g, t), new Response("OK", { status: 200 });
|
|
404
595
|
} catch (e) {
|
|
405
596
|
return console.error("[Proxy] Handler error:", e), new Response("Internal Server Error", { status: 500 });
|
|
406
597
|
}
|
|
407
598
|
};
|
|
408
599
|
}
|
|
409
600
|
export {
|
|
410
|
-
|
|
411
|
-
|
|
601
|
+
B as BaseAnalyticsProvider,
|
|
602
|
+
H as BentoServerProvider,
|
|
603
|
+
G as EmitKitServerProvider,
|
|
412
604
|
W as PirschServerProvider,
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
605
|
+
X as PostHogServerProvider,
|
|
606
|
+
J as createProxyHandler,
|
|
607
|
+
T as ingestProxyEvents
|
|
416
608
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksee/analytics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "A highly typed, provider-agnostic analytics library for TypeScript applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@biomejs/biome": "1.9.4",
|
|
53
53
|
"@changesets/cli": "^2.29.4",
|
|
54
54
|
"@svitejs/changesets-changelog-github-compact": "^1.2.0",
|
|
55
|
-
"@types/node": "^20.
|
|
55
|
+
"@types/node": "^20.17.51",
|
|
56
56
|
"@vitest/coverage-v8": "3.1.4",
|
|
57
57
|
"globals": "^15.8.0",
|
|
58
58
|
"jsdom": "^26.1.0",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
66
|
"@bentonow/bento-node-sdk": "^0.2.1",
|
|
67
|
+
"@emitkit/js": "^2.1.0",
|
|
67
68
|
"pirsch-sdk": "^2.9.1",
|
|
68
69
|
"posthog-js": "^1.268.2",
|
|
69
70
|
"posthog-node": "^5.9.0"
|