@alpic-ai/insights 1.156.0 → 1.157.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/react/index.d.mts +47 -4
- package/dist/react/index.mjs +130 -14
- package/package.json +1 -1
package/dist/react/index.d.mts
CHANGED
|
@@ -1,24 +1,67 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
+
//#region src/react/analytics-transport.d.ts
|
|
3
|
+
interface AnalyticsEvent {
|
|
4
|
+
name: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
message?: string;
|
|
7
|
+
properties?: Record<string, unknown>;
|
|
8
|
+
duration?: number;
|
|
9
|
+
isError?: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
2
13
|
//#region src/react/analytics-client.d.ts
|
|
3
14
|
interface CaptureOptions {
|
|
4
15
|
message?: string;
|
|
5
16
|
properties?: Record<string, unknown>;
|
|
17
|
+
/** Duration of the operation the event describes, in milliseconds. */
|
|
18
|
+
duration?: number;
|
|
19
|
+
isError?: boolean;
|
|
20
|
+
error?: string;
|
|
6
21
|
}
|
|
22
|
+
/** Inspect, transform, or drop (return `null`) each event before it is buffered for delivery. */
|
|
23
|
+
type BeforeSend = (event: AnalyticsEvent) => AnalyticsEvent | null;
|
|
7
24
|
//#endregion
|
|
8
25
|
//#region src/react/alpic-analytics.d.ts
|
|
9
26
|
interface Analytics {
|
|
10
27
|
/** Queue a custom widget event for delivery to Alpic Analytics. */
|
|
11
28
|
capture: (name: string, options?: CaptureOptions) => void;
|
|
12
29
|
}
|
|
30
|
+
/** Toggles for the SDK's built-in event capture. Lifecycle and errors are on by default; interactions are opt-in. */
|
|
31
|
+
interface AutoCaptureOptions {
|
|
32
|
+
lifecycle?: boolean;
|
|
33
|
+
errors?: boolean;
|
|
34
|
+
interactions?: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface AlpicAnalyticsProps {
|
|
37
|
+
children?: ReactNode;
|
|
38
|
+
/** Inspect, transform, or drop each event before delivery. Runs synchronously in `capture`. */
|
|
39
|
+
beforeSend?: BeforeSend;
|
|
40
|
+
autoCapture?: AutoCaptureOptions;
|
|
41
|
+
}
|
|
13
42
|
/**
|
|
14
43
|
* Provides Alpic Analytics to descendant components and configures itself from the widget host.
|
|
15
44
|
* Wrap the widget once, then call `useAnalytics()` from components that capture events.
|
|
16
45
|
* Events captured before configuration is available are buffered automatically.
|
|
17
46
|
*/
|
|
18
|
-
declare function AlpicAnalytics({ children }:
|
|
19
|
-
children?: ReactNode;
|
|
20
|
-
}): import("react").JSX.Element;
|
|
47
|
+
declare function AlpicAnalytics({ children, beforeSend, autoCapture }: AlpicAnalyticsProps): import("react").JSX.Element;
|
|
21
48
|
/** Returns the analytics client from the nearest `AlpicAnalytics` provider. */
|
|
22
49
|
declare function useAnalytics(): Analytics;
|
|
23
50
|
//#endregion
|
|
24
|
-
|
|
51
|
+
//#region src/react/auto-capture.d.ts
|
|
52
|
+
/**
|
|
53
|
+
* Reserved auto-capture event names. The `$` prefix marks events emitted by the SDK itself
|
|
54
|
+
* (lifecycle, errors, interactions) so the analytics UI can distinguish them from custom
|
|
55
|
+
* `capture()` calls. Keep in sync with the server-side reserved namespace.
|
|
56
|
+
*/
|
|
57
|
+
declare const AUTO_EVENT: {
|
|
58
|
+
readonly loaded: "$loaded";
|
|
59
|
+
readonly visible: "$visible";
|
|
60
|
+
readonly hidden: "$hidden";
|
|
61
|
+
readonly closed: "$closed";
|
|
62
|
+
readonly error: "$error";
|
|
63
|
+
};
|
|
64
|
+
/** Public DOM contract for declarative interaction capture: the attribute value is the event name. */
|
|
65
|
+
declare const INTERACTION_ATTRIBUTE = "data-alpic-event";
|
|
66
|
+
//#endregion
|
|
67
|
+
export { AUTO_EVENT, AlpicAnalytics, type AlpicAnalyticsProps, type Analytics, type AutoCaptureOptions, type BeforeSend, type CaptureOptions, INTERACTION_ATTRIBUTE, useAnalytics };
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect, useState } from "react";
|
|
1
|
+
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
//#region src/react/analytics-transport.ts
|
|
4
4
|
function postAnalyticsBatch(ingestUrl, events) {
|
|
@@ -12,9 +12,13 @@ function postAnalyticsBatch(ingestUrl, events) {
|
|
|
12
12
|
const FLUSH_DELAY_MS = 2e3;
|
|
13
13
|
var AnalyticsClient = class {
|
|
14
14
|
events = [];
|
|
15
|
+
beforeSend;
|
|
15
16
|
stamp = null;
|
|
16
17
|
flushTimer = null;
|
|
17
18
|
warnedBufferFull = false;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.beforeSend = options?.beforeSend;
|
|
21
|
+
}
|
|
18
22
|
configure(stamp) {
|
|
19
23
|
if (this.stamp !== null && !isSameStamp(this.stamp, stamp)) this.flush();
|
|
20
24
|
this.stamp = stamp;
|
|
@@ -34,7 +38,12 @@ var AnalyticsClient = class {
|
|
|
34
38
|
};
|
|
35
39
|
if (options?.message !== void 0) event.message = options.message;
|
|
36
40
|
if (options?.properties !== void 0) event.properties = options.properties;
|
|
37
|
-
|
|
41
|
+
if (options?.duration !== void 0) event.duration = options.duration;
|
|
42
|
+
if (options?.isError !== void 0) event.isError = options.isError;
|
|
43
|
+
if (options?.error !== void 0) event.error = options.error;
|
|
44
|
+
const outgoing = this.beforeSend ? this.beforeSend(event) : event;
|
|
45
|
+
if (outgoing === null) return;
|
|
46
|
+
this.events.push(outgoing);
|
|
38
47
|
if (this.events.length >= 50) {
|
|
39
48
|
this.flush();
|
|
40
49
|
return;
|
|
@@ -62,6 +71,88 @@ function isSameStamp(left, right) {
|
|
|
62
71
|
return left.distinctId === right.distinctId && left.sessionId === right.sessionId && left.ingestUrl === right.ingestUrl;
|
|
63
72
|
}
|
|
64
73
|
//#endregion
|
|
74
|
+
//#region src/react/auto-capture.ts
|
|
75
|
+
/**
|
|
76
|
+
* Reserved auto-capture event names. The `$` prefix marks events emitted by the SDK itself
|
|
77
|
+
* (lifecycle, errors, interactions) so the analytics UI can distinguish them from custom
|
|
78
|
+
* `capture()` calls. Keep in sync with the server-side reserved namespace.
|
|
79
|
+
*/
|
|
80
|
+
const AUTO_EVENT = {
|
|
81
|
+
loaded: "$loaded",
|
|
82
|
+
visible: "$visible",
|
|
83
|
+
hidden: "$hidden",
|
|
84
|
+
closed: "$closed",
|
|
85
|
+
error: "$error"
|
|
86
|
+
};
|
|
87
|
+
/** Public DOM contract for declarative interaction capture: the attribute value is the event name. */
|
|
88
|
+
const INTERACTION_ATTRIBUTE = "data-alpic-event";
|
|
89
|
+
const INTERACTION_DATASET_KEY = "alpicEvent";
|
|
90
|
+
const INTERACTION_DATASET_PREFIX = "alpic";
|
|
91
|
+
/**
|
|
92
|
+
* Resolves the nearest `[data-alpic-event]` ancestor of a click target into an event: the
|
|
93
|
+
* attribute value is the name, and every other `data-alpic-*` attribute becomes a property.
|
|
94
|
+
*/
|
|
95
|
+
function resolveInteractionEvent(target) {
|
|
96
|
+
if (!(target instanceof Element)) return null;
|
|
97
|
+
const element = target.closest(`[${INTERACTION_ATTRIBUTE}]`);
|
|
98
|
+
if (element === null) return null;
|
|
99
|
+
const name = element.dataset[INTERACTION_DATASET_KEY];
|
|
100
|
+
if (name === void 0 || name === "") return null;
|
|
101
|
+
const properties = {};
|
|
102
|
+
for (const [key, value] of Object.entries(element.dataset)) {
|
|
103
|
+
if (key === INTERACTION_DATASET_KEY || !key.startsWith(INTERACTION_DATASET_PREFIX) || value === void 0) continue;
|
|
104
|
+
const unprefixed = key.slice(5);
|
|
105
|
+
const propertyKey = unprefixed.charAt(0).toLowerCase() + unprefixed.slice(1);
|
|
106
|
+
properties[propertyKey] = value;
|
|
107
|
+
}
|
|
108
|
+
return Object.keys(properties).length > 0 ? {
|
|
109
|
+
name,
|
|
110
|
+
properties
|
|
111
|
+
} : { name };
|
|
112
|
+
}
|
|
113
|
+
/** Captures uncaught errors and unhandled rejections as `$error`, mapped onto the ingest error columns. */
|
|
114
|
+
function installErrorCapture(client) {
|
|
115
|
+
const onError = (event) => {
|
|
116
|
+
client.capture(AUTO_EVENT.error, {
|
|
117
|
+
isError: true,
|
|
118
|
+
error: event.message,
|
|
119
|
+
properties: {
|
|
120
|
+
stack: event.error instanceof Error ? event.error.stack : void 0,
|
|
121
|
+
source: event.filename
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
const onRejection = (event) => {
|
|
126
|
+
const reason = event.reason;
|
|
127
|
+
client.capture(AUTO_EVENT.error, {
|
|
128
|
+
isError: true,
|
|
129
|
+
error: reason instanceof Error ? reason.message : String(reason),
|
|
130
|
+
properties: {
|
|
131
|
+
stack: reason instanceof Error ? reason.stack : void 0,
|
|
132
|
+
source: "unhandledrejection"
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
window.addEventListener("error", onError);
|
|
137
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
138
|
+
return () => {
|
|
139
|
+
window.removeEventListener("error", onError);
|
|
140
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/** Captures clicks on `[data-alpic-event]` elements via a single delegated `document` listener. */
|
|
144
|
+
function installInteractionCapture(client) {
|
|
145
|
+
const onClick = (event) => {
|
|
146
|
+
const resolved = resolveInteractionEvent(event.target);
|
|
147
|
+
if (resolved === null) return;
|
|
148
|
+
client.capture(resolved.name, resolved.properties ? { properties: resolved.properties } : void 0);
|
|
149
|
+
};
|
|
150
|
+
document.addEventListener("click", onClick);
|
|
151
|
+
return () => {
|
|
152
|
+
document.removeEventListener("click", onClick);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
65
156
|
//#region src/react/stamp.ts
|
|
66
157
|
const ANALYTICS_META_KEY = "alpic/analytics";
|
|
67
158
|
function parseAnalyticsStamp(metadata) {
|
|
@@ -131,24 +222,49 @@ const AnalyticsContext = createContext(null);
|
|
|
131
222
|
* Wrap the widget once, then call `useAnalytics()` from components that capture events.
|
|
132
223
|
* Events captured before configuration is available are buffered automatically.
|
|
133
224
|
*/
|
|
134
|
-
function AlpicAnalytics({ children }) {
|
|
135
|
-
const
|
|
225
|
+
function AlpicAnalytics({ children, beforeSend, autoCapture }) {
|
|
226
|
+
const beforeSendRef = useRef(beforeSend);
|
|
227
|
+
beforeSendRef.current = beforeSend;
|
|
228
|
+
const [client] = useState(() => new AnalyticsClient({ beforeSend: (event) => {
|
|
229
|
+
const transform = beforeSendRef.current;
|
|
230
|
+
return transform ? transform(event) : event;
|
|
231
|
+
} }));
|
|
232
|
+
const lifecycle = autoCapture?.lifecycle ?? true;
|
|
233
|
+
const errors = autoCapture?.errors ?? true;
|
|
234
|
+
const interactions = autoCapture?.interactions ?? false;
|
|
136
235
|
useEffect(() => {
|
|
137
|
-
|
|
236
|
+
return subscribeToAnalyticsStamp((stamp) => {
|
|
138
237
|
client.configure(stamp);
|
|
139
238
|
});
|
|
140
|
-
|
|
141
|
-
|
|
239
|
+
}, [client]);
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (lifecycle) client.capture(AUTO_EVENT.loaded);
|
|
242
|
+
const onVisibilityChange = () => {
|
|
243
|
+
if (document.visibilityState === "hidden") {
|
|
244
|
+
if (lifecycle) client.capture(AUTO_EVENT.hidden);
|
|
245
|
+
client.flush();
|
|
246
|
+
} else if (lifecycle) client.capture(AUTO_EVENT.visible);
|
|
247
|
+
};
|
|
248
|
+
const onPageHide = () => {
|
|
249
|
+
if (lifecycle) client.capture(AUTO_EVENT.closed);
|
|
250
|
+
client.flush();
|
|
142
251
|
};
|
|
143
|
-
document.addEventListener("visibilitychange",
|
|
144
|
-
window.addEventListener("pagehide",
|
|
252
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
253
|
+
window.addEventListener("pagehide", onPageHide);
|
|
145
254
|
return () => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
window.removeEventListener("pagehide", client.flush);
|
|
255
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
256
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
149
257
|
client.flush();
|
|
150
258
|
};
|
|
151
|
-
}, [client]);
|
|
259
|
+
}, [client, lifecycle]);
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
if (!errors) return;
|
|
262
|
+
return installErrorCapture(client);
|
|
263
|
+
}, [client, errors]);
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
if (!interactions) return;
|
|
266
|
+
return installInteractionCapture(client);
|
|
267
|
+
}, [client, interactions]);
|
|
152
268
|
return /* @__PURE__ */ jsx(AnalyticsContext.Provider, {
|
|
153
269
|
value: client,
|
|
154
270
|
children
|
|
@@ -161,4 +277,4 @@ function useAnalytics() {
|
|
|
161
277
|
return analytics;
|
|
162
278
|
}
|
|
163
279
|
//#endregion
|
|
164
|
-
export { AlpicAnalytics, useAnalytics };
|
|
280
|
+
export { AUTO_EVENT, AlpicAnalytics, INTERACTION_ATTRIBUTE, useAnalytics };
|