@alpic-ai/insights 0.0.0-staging.g845fffa → 0.0.0-staging.g84dfc77
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 +4 -47
- package/dist/react/index.mjs +14 -130
- package/package.json +2 -2
package/dist/react/index.d.mts
CHANGED
|
@@ -1,67 +1,24 @@
|
|
|
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
|
|
13
2
|
//#region src/react/analytics-client.d.ts
|
|
14
3
|
interface CaptureOptions {
|
|
15
4
|
message?: string;
|
|
16
5
|
properties?: Record<string, unknown>;
|
|
17
|
-
/** Duration of the operation the event describes, in milliseconds. */
|
|
18
|
-
duration?: number;
|
|
19
|
-
isError?: boolean;
|
|
20
|
-
error?: string;
|
|
21
6
|
}
|
|
22
|
-
/** Inspect, transform, or drop (return `null`) each event before it is buffered for delivery. */
|
|
23
|
-
type BeforeSend = (event: AnalyticsEvent) => AnalyticsEvent | null;
|
|
24
7
|
//#endregion
|
|
25
8
|
//#region src/react/alpic-analytics.d.ts
|
|
26
9
|
interface Analytics {
|
|
27
10
|
/** Queue a custom widget event for delivery to Alpic Analytics. */
|
|
28
11
|
capture: (name: string, options?: CaptureOptions) => void;
|
|
29
12
|
}
|
|
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
|
-
}
|
|
42
13
|
/**
|
|
43
14
|
* Provides Alpic Analytics to descendant components and configures itself from the widget host.
|
|
44
15
|
* Wrap the widget once, then call `useAnalytics()` from components that capture events.
|
|
45
16
|
* Events captured before configuration is available are buffered automatically.
|
|
46
17
|
*/
|
|
47
|
-
declare function AlpicAnalytics({ children
|
|
18
|
+
declare function AlpicAnalytics({ children }: {
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
48
21
|
/** Returns the analytics client from the nearest `AlpicAnalytics` provider. */
|
|
49
22
|
declare function useAnalytics(): Analytics;
|
|
50
23
|
//#endregion
|
|
51
|
-
|
|
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 };
|
|
24
|
+
export { AlpicAnalytics, type Analytics, type CaptureOptions, useAnalytics };
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect,
|
|
1
|
+
import { createContext, useContext, useEffect, 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,13 +12,9 @@ function postAnalyticsBatch(ingestUrl, events) {
|
|
|
12
12
|
const FLUSH_DELAY_MS = 2e3;
|
|
13
13
|
var AnalyticsClient = class {
|
|
14
14
|
events = [];
|
|
15
|
-
beforeSend;
|
|
16
15
|
stamp = null;
|
|
17
16
|
flushTimer = null;
|
|
18
17
|
warnedBufferFull = false;
|
|
19
|
-
constructor(options) {
|
|
20
|
-
this.beforeSend = options?.beforeSend;
|
|
21
|
-
}
|
|
22
18
|
configure(stamp) {
|
|
23
19
|
if (this.stamp !== null && !isSameStamp(this.stamp, stamp)) this.flush();
|
|
24
20
|
this.stamp = stamp;
|
|
@@ -38,12 +34,7 @@ var AnalyticsClient = class {
|
|
|
38
34
|
};
|
|
39
35
|
if (options?.message !== void 0) event.message = options.message;
|
|
40
36
|
if (options?.properties !== void 0) event.properties = options.properties;
|
|
41
|
-
|
|
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);
|
|
37
|
+
this.events.push(event);
|
|
47
38
|
if (this.events.length >= 50) {
|
|
48
39
|
this.flush();
|
|
49
40
|
return;
|
|
@@ -71,88 +62,6 @@ function isSameStamp(left, right) {
|
|
|
71
62
|
return left.distinctId === right.distinctId && left.sessionId === right.sessionId && left.ingestUrl === right.ingestUrl;
|
|
72
63
|
}
|
|
73
64
|
//#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
|
|
156
65
|
//#region src/react/stamp.ts
|
|
157
66
|
const ANALYTICS_META_KEY = "alpic/analytics";
|
|
158
67
|
function parseAnalyticsStamp(metadata) {
|
|
@@ -222,49 +131,24 @@ const AnalyticsContext = createContext(null);
|
|
|
222
131
|
* Wrap the widget once, then call `useAnalytics()` from components that capture events.
|
|
223
132
|
* Events captured before configuration is available are buffered automatically.
|
|
224
133
|
*/
|
|
225
|
-
function AlpicAnalytics({ children
|
|
226
|
-
const
|
|
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;
|
|
134
|
+
function AlpicAnalytics({ children }) {
|
|
135
|
+
const [client] = useState(() => new AnalyticsClient());
|
|
235
136
|
useEffect(() => {
|
|
236
|
-
|
|
137
|
+
const unsubscribe = subscribeToAnalyticsStamp((stamp) => {
|
|
237
138
|
client.configure(stamp);
|
|
238
139
|
});
|
|
239
|
-
|
|
240
|
-
|
|
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();
|
|
140
|
+
const flushWhenHidden = () => {
|
|
141
|
+
if (document.visibilityState === "hidden") client.flush();
|
|
251
142
|
};
|
|
252
|
-
document.addEventListener("visibilitychange",
|
|
253
|
-
window.addEventListener("pagehide",
|
|
143
|
+
document.addEventListener("visibilitychange", flushWhenHidden);
|
|
144
|
+
window.addEventListener("pagehide", client.flush);
|
|
254
145
|
return () => {
|
|
255
|
-
|
|
256
|
-
|
|
146
|
+
unsubscribe();
|
|
147
|
+
document.removeEventListener("visibilitychange", flushWhenHidden);
|
|
148
|
+
window.removeEventListener("pagehide", client.flush);
|
|
257
149
|
client.flush();
|
|
258
150
|
};
|
|
259
|
-
}, [client
|
|
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]);
|
|
151
|
+
}, [client]);
|
|
268
152
|
return /* @__PURE__ */ jsx(AnalyticsContext.Provider, {
|
|
269
153
|
value: client,
|
|
270
154
|
children
|
|
@@ -277,4 +161,4 @@ function useAnalytics() {
|
|
|
277
161
|
return analytics;
|
|
278
162
|
}
|
|
279
163
|
//#endregion
|
|
280
|
-
export {
|
|
164
|
+
export { AlpicAnalytics, useAnalytics };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alpic-ai/insights",
|
|
3
|
-
"version": "0.0.0-staging.
|
|
3
|
+
"version": "0.0.0-staging.g84dfc77",
|
|
4
4
|
"description": "User insights middlewares for Alpic-hosted MCP servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"react-dom": "^19.2.7",
|
|
46
46
|
"shx": "^0.4.0",
|
|
47
47
|
"skybridge": "^1.2.7",
|
|
48
|
-
"tsdown": "^0.22.
|
|
48
|
+
"tsdown": "^0.22.5",
|
|
49
49
|
"typescript": "^6.0.3",
|
|
50
50
|
"vitest": "^4.1.10",
|
|
51
51
|
"zod": "^4.4.3"
|