@stacksee/analytics 0.7.0 → 0.9.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/dist/providers/bento/client.d.ts +101 -0
- package/dist/providers/client.d.ts +3 -0
- package/dist/providers/client.js +415 -141
- package/dist/providers/proxy/client.d.ts +56 -0
- package/dist/providers/proxy/server.d.ts +55 -0
- package/dist/providers/proxy/types.d.ts +64 -0
- package/dist/providers/server.d.ts +2 -0
- package/dist/providers/server.js +209 -123
- package/package.json +5 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { BaseEvent, EventContext } from '../../core/events/types.js';
|
|
2
|
+
import { BaseAnalyticsProvider } from '../base.provider.js';
|
|
3
|
+
import { ProxyBatchConfig, ProxyRetryConfig } from './types.js';
|
|
4
|
+
export interface ProxyProviderConfig {
|
|
5
|
+
/**
|
|
6
|
+
* The endpoint to send events to (e.g., '/api/events')
|
|
7
|
+
*/
|
|
8
|
+
endpoint: string;
|
|
9
|
+
/**
|
|
10
|
+
* Batching configuration
|
|
11
|
+
*/
|
|
12
|
+
batch?: ProxyBatchConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Retry configuration
|
|
15
|
+
*/
|
|
16
|
+
retry?: ProxyRetryConfig;
|
|
17
|
+
/**
|
|
18
|
+
* Custom headers to include in requests
|
|
19
|
+
*/
|
|
20
|
+
headers?: Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Enable debug logging
|
|
23
|
+
*/
|
|
24
|
+
debug?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Enable/disable the provider
|
|
27
|
+
*/
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare class ProxyProvider extends BaseAnalyticsProvider {
|
|
31
|
+
name: string;
|
|
32
|
+
private config;
|
|
33
|
+
private queue;
|
|
34
|
+
private flushTimer?;
|
|
35
|
+
private readonly batchSize;
|
|
36
|
+
private readonly batchInterval;
|
|
37
|
+
private readonly retryAttempts;
|
|
38
|
+
private readonly retryBackoff;
|
|
39
|
+
private readonly retryInitialDelay;
|
|
40
|
+
constructor(config: ProxyProviderConfig);
|
|
41
|
+
initialize(): Promise<void>;
|
|
42
|
+
identify(userId: string, traits?: Record<string, unknown>): void;
|
|
43
|
+
track(event: BaseEvent, context?: EventContext): Promise<void>;
|
|
44
|
+
pageView(properties?: Record<string, unknown>, context?: EventContext): void;
|
|
45
|
+
reset(): Promise<void>;
|
|
46
|
+
shutdown(): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Manually flush all queued events
|
|
49
|
+
*/
|
|
50
|
+
flush(useBeacon?: boolean): Promise<void>;
|
|
51
|
+
private queueEvent;
|
|
52
|
+
private sendEvents;
|
|
53
|
+
private sendWithRetry;
|
|
54
|
+
private calculateRetryDelay;
|
|
55
|
+
private enrichContext;
|
|
56
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ServerAnalytics } from '../../server.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for ingesting proxy events
|
|
4
|
+
*/
|
|
5
|
+
export interface IngestProxyEventsConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Enrich context with server-side data
|
|
8
|
+
*/
|
|
9
|
+
enrichContext?: (request: Request) => Record<string, unknown>;
|
|
10
|
+
/**
|
|
11
|
+
* Extract IP address from request
|
|
12
|
+
* Default: Uses standard headers (X-Forwarded-For, X-Real-IP)
|
|
13
|
+
*/
|
|
14
|
+
extractIp?: (request: Request) => string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Error handler
|
|
17
|
+
*/
|
|
18
|
+
onError?: (error: unknown) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Ingests events from ProxyProvider and replays them through server analytics
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Next.js App Router
|
|
26
|
+
* export async function POST(req: Request) {
|
|
27
|
+
* await ingestProxyEvents(req, serverAnalytics);
|
|
28
|
+
* return new Response('OK');
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* // With custom IP extraction
|
|
32
|
+
* export async function POST(req: Request) {
|
|
33
|
+
* await ingestProxyEvents(req, serverAnalytics, {
|
|
34
|
+
* extractIp: (req) => req.headers.get('cf-connecting-ip') // Cloudflare
|
|
35
|
+
* });
|
|
36
|
+
* return new Response('OK');
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function ingestProxyEvents<TEventMap extends Record<string, Record<string, unknown>> = Record<string, Record<string, unknown>>, TUserTraits extends Record<string, unknown> = Record<string, unknown>>(request: Request, analytics: ServerAnalytics<TEventMap, TUserTraits>, config?: IngestProxyEventsConfig): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a Request handler for common frameworks
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Next.js App Router
|
|
47
|
+
* export const POST = createProxyHandler(serverAnalytics);
|
|
48
|
+
*
|
|
49
|
+
* // With custom config
|
|
50
|
+
* export const POST = createProxyHandler(serverAnalytics, {
|
|
51
|
+
* extractIp: (req) => req.headers.get('cf-connecting-ip')
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function createProxyHandler<TEventMap extends Record<string, Record<string, unknown>> = Record<string, Record<string, unknown>>, TUserTraits extends Record<string, unknown> = Record<string, unknown>>(analytics: ServerAnalytics<TEventMap, TUserTraits>, config?: IngestProxyEventsConfig): (request: Request) => Promise<Response>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { BaseEvent, EventContext } from '../../core/events/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Proxy event types for batching and sending to server
|
|
4
|
+
*/
|
|
5
|
+
export type ProxyTrackEvent = {
|
|
6
|
+
type: "track";
|
|
7
|
+
event: BaseEvent;
|
|
8
|
+
context?: EventContext;
|
|
9
|
+
};
|
|
10
|
+
export type ProxyIdentifyEvent = {
|
|
11
|
+
type: "identify";
|
|
12
|
+
userId: string;
|
|
13
|
+
traits?: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
export type ProxyPageViewEvent = {
|
|
16
|
+
type: "pageView";
|
|
17
|
+
properties?: Record<string, unknown>;
|
|
18
|
+
context?: EventContext;
|
|
19
|
+
};
|
|
20
|
+
export type ProxyResetEvent = {
|
|
21
|
+
type: "reset";
|
|
22
|
+
};
|
|
23
|
+
export type ProxyEvent = ProxyTrackEvent | ProxyIdentifyEvent | ProxyPageViewEvent | ProxyResetEvent;
|
|
24
|
+
/**
|
|
25
|
+
* Payload sent to the proxy endpoint
|
|
26
|
+
*/
|
|
27
|
+
export interface ProxyPayload {
|
|
28
|
+
events: ProxyEvent[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Configuration for batching behavior
|
|
32
|
+
*/
|
|
33
|
+
export interface ProxyBatchConfig {
|
|
34
|
+
/**
|
|
35
|
+
* Maximum number of events before auto-flush
|
|
36
|
+
* @default 10
|
|
37
|
+
*/
|
|
38
|
+
size?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Maximum time in ms before auto-flush
|
|
41
|
+
* @default 5000
|
|
42
|
+
*/
|
|
43
|
+
interval?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Configuration for retry behavior
|
|
47
|
+
*/
|
|
48
|
+
export interface ProxyRetryConfig {
|
|
49
|
+
/**
|
|
50
|
+
* Maximum retry attempts
|
|
51
|
+
* @default 3
|
|
52
|
+
*/
|
|
53
|
+
attempts?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Backoff strategy
|
|
56
|
+
* @default 'exponential'
|
|
57
|
+
*/
|
|
58
|
+
backoff?: "exponential" | "linear";
|
|
59
|
+
/**
|
|
60
|
+
* Initial delay in ms
|
|
61
|
+
* @default 1000
|
|
62
|
+
*/
|
|
63
|
+
initialDelay?: number;
|
|
64
|
+
}
|
|
@@ -5,3 +5,5 @@ 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 { ingestProxyEvents, createProxyHandler } from './proxy/server.js';
|
|
9
|
+
export type { IngestProxyEventsConfig } from './proxy/server.js';
|
package/dist/providers/server.js
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { B as
|
|
5
|
-
import { P as
|
|
6
|
-
class
|
|
7
|
-
constructor(
|
|
8
|
-
super({ debug:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
this.config =
|
|
1
|
+
var f = Object.defineProperty;
|
|
2
|
+
var y = (l, n, i) => n in l ? f(l, n, { enumerable: !0, configurable: !0, writable: !0, value: i }) : l[n] = i;
|
|
3
|
+
var d = (l, n, i) => y(l, typeof n != "symbol" ? n + "" : n, i);
|
|
4
|
+
import { B as p } from "../base.provider-AfFL5W_P.js";
|
|
5
|
+
import { P as z } from "../server-DjEk1fUD.js";
|
|
6
|
+
class v extends p {
|
|
7
|
+
constructor(i) {
|
|
8
|
+
super({ debug: i.debug, enabled: i.enabled });
|
|
9
|
+
d(this, "name", "Bento-Server");
|
|
10
|
+
d(this, "client");
|
|
11
|
+
d(this, "initialized", !1);
|
|
12
|
+
d(this, "config");
|
|
13
|
+
d(this, "currentUserEmail");
|
|
14
|
+
this.config = i;
|
|
15
15
|
}
|
|
16
16
|
async initialize() {
|
|
17
|
-
var
|
|
17
|
+
var i, e;
|
|
18
18
|
if (this.isEnabled() && !this.initialized) {
|
|
19
19
|
if (!this.config.siteUuid || typeof this.config.siteUuid != "string")
|
|
20
20
|
throw new Error("Bento requires a siteUuid");
|
|
21
|
-
if (!((
|
|
21
|
+
if (!((i = this.config.authentication) != null && i.publishableKey) || typeof this.config.authentication.publishableKey != "string")
|
|
22
22
|
throw new Error("Bento requires authentication.publishableKey");
|
|
23
|
-
if (!((
|
|
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:
|
|
27
|
-
this.client = new
|
|
26
|
+
const { Analytics: s } = await import("../bento-node-sdk.esm-C4HG7SVz.js"), { debug: a, enabled: t, ...o } = this.config;
|
|
27
|
+
this.client = new s(o), this.initialized = !0, this.log("Initialized successfully", {
|
|
28
28
|
siteUuid: this.config.siteUuid
|
|
29
29
|
});
|
|
30
|
-
} catch (
|
|
30
|
+
} catch (s) {
|
|
31
31
|
throw console.error(
|
|
32
32
|
"[Bento-Server] Failed to initialize. Make sure @bentonow/bento-node-sdk is installed:",
|
|
33
|
-
|
|
34
|
-
),
|
|
33
|
+
s
|
|
34
|
+
), s;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
identify(
|
|
38
|
+
identify(i, e) {
|
|
39
39
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
40
|
-
const
|
|
41
|
-
this.currentUserEmail =
|
|
42
|
-
const a =
|
|
40
|
+
const s = (e == null ? void 0 : e.email) || i;
|
|
41
|
+
this.currentUserEmail = s;
|
|
42
|
+
const a = e ? { ...e } : {};
|
|
43
43
|
delete a.email, this.client.V1.addSubscriber({
|
|
44
|
-
email:
|
|
44
|
+
email: s,
|
|
45
45
|
fields: a
|
|
46
|
-
}).catch((
|
|
47
|
-
console.error("[Bento-Server] Failed to identify user:",
|
|
48
|
-
}), this.log("Identified user", { userId:
|
|
46
|
+
}).catch((t) => {
|
|
47
|
+
console.error("[Bento-Server] Failed to identify user:", t);
|
|
48
|
+
}), this.log("Identified user", { userId: i, email: s, traits: e });
|
|
49
49
|
}
|
|
50
|
-
async track(
|
|
51
|
-
var
|
|
50
|
+
async track(i, e) {
|
|
51
|
+
var o, r, h;
|
|
52
52
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
53
|
-
const
|
|
54
|
-
...
|
|
55
|
-
category:
|
|
56
|
-
timestamp:
|
|
57
|
-
...
|
|
58
|
-
...(
|
|
53
|
+
const s = ((o = e == null ? void 0 : e.user) == null ? void 0 : o.email) || this.currentUserEmail || ((r = e == null ? void 0 : e.user) == null ? void 0 : r.userId) || i.userId || "anonymous@unknown.com", a = {
|
|
54
|
+
...i.properties,
|
|
55
|
+
category: i.category,
|
|
56
|
+
timestamp: i.timestamp || Date.now(),
|
|
57
|
+
...i.sessionId && { sessionId: i.sessionId },
|
|
58
|
+
...(e == null ? void 0 : e.page) && {
|
|
59
59
|
page: {
|
|
60
|
-
path:
|
|
61
|
-
title:
|
|
62
|
-
referrer:
|
|
60
|
+
path: e.page.path,
|
|
61
|
+
title: e.page.title,
|
|
62
|
+
referrer: e.page.referrer
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
-
...(
|
|
66
|
-
...(
|
|
67
|
-
},
|
|
65
|
+
...(e == null ? void 0 : e.device) && { device: e.device },
|
|
66
|
+
...(e == null ? void 0 : e.utm) && { utm: e.utm }
|
|
67
|
+
}, t = ((h = e == null ? void 0 : e.user) == null ? void 0 : h.traits) || {};
|
|
68
68
|
try {
|
|
69
69
|
await this.client.V1.track({
|
|
70
|
-
email:
|
|
71
|
-
type: `$${
|
|
70
|
+
email: s,
|
|
71
|
+
type: `$${i.action}`,
|
|
72
72
|
details: a,
|
|
73
|
-
fields:
|
|
74
|
-
}), this.log("Tracked event", { event:
|
|
75
|
-
} catch (
|
|
76
|
-
console.error("[Bento-Server] Failed to track event:",
|
|
73
|
+
fields: t
|
|
74
|
+
}), this.log("Tracked event", { event: i, context: e });
|
|
75
|
+
} catch (u) {
|
|
76
|
+
console.error("[Bento-Server] Failed to track event:", u);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
pageView(
|
|
80
|
-
var
|
|
79
|
+
pageView(i, e) {
|
|
80
|
+
var o, r;
|
|
81
81
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
82
|
-
const
|
|
83
|
-
...
|
|
84
|
-
...(
|
|
85
|
-
path:
|
|
86
|
-
title:
|
|
87
|
-
referrer:
|
|
82
|
+
const s = ((o = e == null ? void 0 : e.user) == null ? void 0 : o.email) || this.currentUserEmail || "anonymous@unknown.com", a = {
|
|
83
|
+
...i,
|
|
84
|
+
...(e == null ? void 0 : e.page) && {
|
|
85
|
+
path: e.page.path,
|
|
86
|
+
title: e.page.title,
|
|
87
|
+
referrer: e.page.referrer
|
|
88
88
|
}
|
|
89
|
-
},
|
|
89
|
+
}, t = ((r = e == null ? void 0 : e.user) == null ? void 0 : r.traits) || {};
|
|
90
90
|
this.client.V1.track({
|
|
91
|
-
email:
|
|
92
|
-
type: "$
|
|
91
|
+
email: s,
|
|
92
|
+
type: "$view",
|
|
93
93
|
details: a,
|
|
94
|
-
fields:
|
|
95
|
-
}).catch((
|
|
96
|
-
console.error("[Bento-Server] Failed to track page view:",
|
|
97
|
-
}), this.log("Tracked page view", { properties:
|
|
94
|
+
fields: t
|
|
95
|
+
}).catch((h) => {
|
|
96
|
+
console.error("[Bento-Server] Failed to track page view:", h);
|
|
97
|
+
}), this.log("Tracked page view", { properties: i, context: e });
|
|
98
98
|
}
|
|
99
99
|
async reset() {
|
|
100
100
|
!this.isEnabled() || !this.initialized || !this.client || (this.currentUserEmail = void 0, this.log("Reset user session"));
|
|
@@ -103,14 +103,14 @@ class b extends f {
|
|
|
103
103
|
this.client = void 0, this.initialized = !1, this.log("Shutdown complete");
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
class
|
|
107
|
-
constructor(
|
|
108
|
-
super({ debug:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
this.config =
|
|
106
|
+
class E extends p {
|
|
107
|
+
constructor(i) {
|
|
108
|
+
super({ debug: i.debug, enabled: i.enabled });
|
|
109
|
+
d(this, "name", "Pirsch-Server");
|
|
110
|
+
d(this, "client");
|
|
111
|
+
d(this, "initialized", !1);
|
|
112
|
+
d(this, "config");
|
|
113
|
+
this.config = i;
|
|
114
114
|
}
|
|
115
115
|
async initialize() {
|
|
116
116
|
if (this.isEnabled() && !this.initialized) {
|
|
@@ -119,106 +119,192 @@ class w extends f {
|
|
|
119
119
|
if (!this.config.clientSecret || typeof this.config.clientSecret != "string")
|
|
120
120
|
throw new Error("Pirsch requires a clientSecret (or access key)");
|
|
121
121
|
try {
|
|
122
|
-
const { Pirsch:
|
|
123
|
-
this.client = new
|
|
122
|
+
const { Pirsch: i } = await import("../index-bgxxv-IJ.js").then((t) => t.i), { debug: e, enabled: s, ...a } = this.config;
|
|
123
|
+
this.client = new i(a), this.initialized = !0, this.log("Initialized successfully", {
|
|
124
124
|
hostname: this.config.hostname
|
|
125
125
|
});
|
|
126
|
-
} catch (
|
|
126
|
+
} catch (i) {
|
|
127
127
|
throw console.error(
|
|
128
128
|
"[Pirsch-Server] Failed to initialize. Make sure pirsch-sdk is installed:",
|
|
129
|
-
|
|
130
|
-
),
|
|
129
|
+
i
|
|
130
|
+
), i;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
-
identify(
|
|
134
|
+
identify(i, e) {
|
|
135
135
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
136
|
-
const
|
|
136
|
+
const s = {
|
|
137
137
|
url: "https://identify",
|
|
138
138
|
ip: "0.0.0.0",
|
|
139
139
|
user_agent: "analytics-library"
|
|
140
140
|
}, a = {
|
|
141
|
-
userId:
|
|
142
|
-
...
|
|
143
|
-
Object.entries(
|
|
144
|
-
([,
|
|
141
|
+
userId: i,
|
|
142
|
+
...e && Object.fromEntries(
|
|
143
|
+
Object.entries(e).filter(
|
|
144
|
+
([, t]) => typeof t == "string" || typeof t == "number" || typeof t == "boolean"
|
|
145
145
|
)
|
|
146
146
|
)
|
|
147
147
|
};
|
|
148
|
-
this.client.event("user_identified",
|
|
149
|
-
console.error("[Pirsch-Server] Failed to track identify event:",
|
|
150
|
-
}), this.log("Identified user via event", { userId:
|
|
148
|
+
this.client.event("user_identified", s, 0, a).catch((t) => {
|
|
149
|
+
console.error("[Pirsch-Server] Failed to track identify event:", t);
|
|
150
|
+
}), this.log("Identified user via event", { userId: i, traits: e });
|
|
151
151
|
}
|
|
152
|
-
async track(
|
|
153
|
-
var
|
|
152
|
+
async track(i, e) {
|
|
153
|
+
var o, r, h, u;
|
|
154
154
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
155
|
-
const
|
|
156
|
-
url: ((
|
|
155
|
+
const s = {
|
|
156
|
+
url: ((o = e == null ? void 0 : e.page) == null ? void 0 : o.path) || "https://event",
|
|
157
157
|
ip: "0.0.0.0",
|
|
158
158
|
// Server-side should provide real IP if available
|
|
159
159
|
user_agent: "analytics-library",
|
|
160
160
|
// Server-side should provide real UA if available
|
|
161
|
-
...((
|
|
162
|
-
...((
|
|
163
|
-
},
|
|
161
|
+
...((r = e == null ? void 0 : e.page) == null ? void 0 : r.title) && { title: e.page.title },
|
|
162
|
+
...((h = e == null ? void 0 : e.page) == null ? void 0 : h.referrer) && { referrer: e.page.referrer }
|
|
163
|
+
}, t = {
|
|
164
164
|
...Object.fromEntries(
|
|
165
|
-
Object.entries(
|
|
166
|
-
([,
|
|
165
|
+
Object.entries(i.properties).filter(
|
|
166
|
+
([, c]) => typeof c == "string" || typeof c == "number" || typeof c == "boolean"
|
|
167
167
|
)
|
|
168
168
|
),
|
|
169
|
-
category:
|
|
170
|
-
timestamp: String(
|
|
171
|
-
...
|
|
172
|
-
...
|
|
173
|
-
...((
|
|
169
|
+
category: i.category,
|
|
170
|
+
timestamp: String(i.timestamp || Date.now()),
|
|
171
|
+
...i.userId && { userId: i.userId },
|
|
172
|
+
...i.sessionId && { sessionId: i.sessionId },
|
|
173
|
+
...((u = e == null ? void 0 : e.user) == null ? void 0 : u.email) && { user_email: e.user.email }
|
|
174
174
|
};
|
|
175
175
|
try {
|
|
176
|
-
await this.client.event(
|
|
177
|
-
} catch (
|
|
178
|
-
console.error("[Pirsch-Server] Failed to track event:",
|
|
176
|
+
await this.client.event(i.action, s, 0, t), this.log("Tracked event", { event: i, context: e });
|
|
177
|
+
} catch (c) {
|
|
178
|
+
console.error("[Pirsch-Server] Failed to track event:", c);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
pageView(
|
|
182
|
-
var a,
|
|
181
|
+
pageView(i, e) {
|
|
182
|
+
var a, t, o;
|
|
183
183
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
184
|
-
const
|
|
185
|
-
url: ((a =
|
|
184
|
+
const s = {
|
|
185
|
+
url: ((a = e == null ? void 0 : e.page) == null ? void 0 : a.path) || "https://pageview",
|
|
186
186
|
ip: "0.0.0.0",
|
|
187
187
|
// Server-side should provide real IP if available
|
|
188
188
|
user_agent: "analytics-library",
|
|
189
189
|
// Server-side should provide real UA if available
|
|
190
|
-
...((
|
|
191
|
-
...((
|
|
192
|
-
...
|
|
190
|
+
...((t = e == null ? void 0 : e.page) == null ? void 0 : t.title) && { title: e.page.title },
|
|
191
|
+
...((o = e == null ? void 0 : e.page) == null ? void 0 : o.referrer) && { referrer: e.page.referrer },
|
|
192
|
+
...i && {
|
|
193
193
|
tags: Object.fromEntries(
|
|
194
|
-
Object.entries(
|
|
195
|
-
([,
|
|
194
|
+
Object.entries(i).filter(
|
|
195
|
+
([, r]) => typeof r == "string" || typeof r == "number" || typeof r == "boolean"
|
|
196
196
|
)
|
|
197
197
|
)
|
|
198
198
|
}
|
|
199
199
|
};
|
|
200
|
-
this.client.hit(
|
|
201
|
-
console.error("[Pirsch-Server] Failed to track page view:",
|
|
202
|
-
}), this.log("Tracked page view", { properties:
|
|
200
|
+
this.client.hit(s).catch((r) => {
|
|
201
|
+
console.error("[Pirsch-Server] Failed to track page view:", r);
|
|
202
|
+
}), this.log("Tracked page view", { properties: i, context: e });
|
|
203
203
|
}
|
|
204
204
|
async reset() {
|
|
205
205
|
if (!this.isEnabled() || !this.initialized || !this.client) return;
|
|
206
|
-
const
|
|
206
|
+
const i = {
|
|
207
207
|
url: "https://session-reset",
|
|
208
208
|
ip: "0.0.0.0",
|
|
209
209
|
user_agent: "analytics-library"
|
|
210
210
|
};
|
|
211
|
-
await this.client.event("session_reset",
|
|
212
|
-
console.error("[Pirsch-Server] Failed to track session reset:",
|
|
211
|
+
await this.client.event("session_reset", i, 0, {}).catch((e) => {
|
|
212
|
+
console.error("[Pirsch-Server] Failed to track session reset:", e);
|
|
213
213
|
}), this.log("Reset user session");
|
|
214
214
|
}
|
|
215
215
|
async shutdown() {
|
|
216
216
|
this.client = void 0, this.initialized = !1, this.log("Shutdown complete");
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
+
async function g(l, n, i) {
|
|
220
|
+
var e, s;
|
|
221
|
+
try {
|
|
222
|
+
const a = await l.json();
|
|
223
|
+
if (!a.events || !Array.isArray(a.events))
|
|
224
|
+
throw new Error("Invalid payload: missing events array");
|
|
225
|
+
const t = i != null && i.extractIp ? i.extractIp(l) : m(l), o = i != null && i.enrichContext ? i.enrichContext(l) : {};
|
|
226
|
+
for (const r of a.events)
|
|
227
|
+
try {
|
|
228
|
+
switch (r.type) {
|
|
229
|
+
case "track": {
|
|
230
|
+
const h = {
|
|
231
|
+
...r.context,
|
|
232
|
+
...o,
|
|
233
|
+
device: {
|
|
234
|
+
...(e = r.context) == null ? void 0 : e.device,
|
|
235
|
+
// Add IP (using type assertion for extended fields)
|
|
236
|
+
// biome-ignore lint/suspicious/noExplicitAny: IP field not in base device type
|
|
237
|
+
...t ? { ip: t } : {}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
await n.track(r.event.action, r.event.properties, {
|
|
241
|
+
userId: r.event.userId,
|
|
242
|
+
sessionId: r.event.sessionId,
|
|
243
|
+
// biome-ignore lint/suspicious/noExplicitAny: Generic context forwarding requires type assertion
|
|
244
|
+
context: h
|
|
245
|
+
});
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
case "identify": {
|
|
249
|
+
n.identify(r.userId, r.traits);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case "pageView": {
|
|
253
|
+
const h = {
|
|
254
|
+
...r.context,
|
|
255
|
+
...o,
|
|
256
|
+
device: {
|
|
257
|
+
...(s = r.context) == null ? void 0 : s.device,
|
|
258
|
+
// biome-ignore lint/suspicious/noExplicitAny: IP field not in base device type
|
|
259
|
+
// Add IP (using type assertion for extended fields)
|
|
260
|
+
...t ? { ip: t } : {}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
n.pageView(r.properties, h);
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
case "reset":
|
|
267
|
+
break;
|
|
268
|
+
default:
|
|
269
|
+
console.warn("[Proxy] Unknown event type:", r);
|
|
270
|
+
}
|
|
271
|
+
} catch (h) {
|
|
272
|
+
i != null && i.onError ? i.onError(h) : console.error("[Proxy] Failed to process event:", h);
|
|
273
|
+
}
|
|
274
|
+
} catch (a) {
|
|
275
|
+
throw i != null && i.onError ? i.onError(a) : console.error("[Proxy] Failed to ingest events:", a), a;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function m(l) {
|
|
279
|
+
var i;
|
|
280
|
+
const n = [
|
|
281
|
+
"x-forwarded-for",
|
|
282
|
+
"x-real-ip",
|
|
283
|
+
"cf-connecting-ip",
|
|
284
|
+
// Cloudflare
|
|
285
|
+
"x-client-ip",
|
|
286
|
+
"x-cluster-client-ip"
|
|
287
|
+
];
|
|
288
|
+
for (const e of n) {
|
|
289
|
+
const s = l.headers.get(e);
|
|
290
|
+
if (s)
|
|
291
|
+
return (i = s.split(",")[0]) == null ? void 0 : i.trim();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function k(l, n) {
|
|
295
|
+
return async (i) => {
|
|
296
|
+
try {
|
|
297
|
+
return await g(i, l, n), new Response("OK", { status: 200 });
|
|
298
|
+
} catch (e) {
|
|
299
|
+
return console.error("[Proxy] Handler error:", e), new Response("Internal Server Error", { status: 500 });
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
219
303
|
export {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
304
|
+
p as BaseAnalyticsProvider,
|
|
305
|
+
v as BentoServerProvider,
|
|
306
|
+
E as PirschServerProvider,
|
|
307
|
+
z as PostHogServerProvider,
|
|
308
|
+
k as createProxyHandler,
|
|
309
|
+
g as ingestProxyEvents
|
|
224
310
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksee/analytics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A highly typed, provider-agnostic analytics library for TypeScript applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"pnpm": ">=9.0.0",
|
|
71
71
|
"node": ">=20"
|
|
72
72
|
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@tailwindcss/vite": "^4.1.16",
|
|
75
|
+
"tailwindcss": "^4.1.16"
|
|
76
|
+
},
|
|
73
77
|
"scripts": {
|
|
74
78
|
"test": "vitest run",
|
|
75
79
|
"test:watch": "vitest",
|