@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.
@@ -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, beforeSend, autoCapture }: AlpicAnalyticsProps): import("react").JSX.Element;
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
- //#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 };
24
+ export { AlpicAnalytics, type Analytics, type CaptureOptions, useAnalytics };
@@ -1,4 +1,4 @@
1
- import { createContext, useContext, useEffect, useRef, useState } from "react";
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
- 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);
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, 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;
134
+ function AlpicAnalytics({ children }) {
135
+ const [client] = useState(() => new AnalyticsClient());
235
136
  useEffect(() => {
236
- return subscribeToAnalyticsStamp((stamp) => {
137
+ const unsubscribe = subscribeToAnalyticsStamp((stamp) => {
237
138
  client.configure(stamp);
238
139
  });
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();
140
+ const flushWhenHidden = () => {
141
+ if (document.visibilityState === "hidden") client.flush();
251
142
  };
252
- document.addEventListener("visibilitychange", onVisibilityChange);
253
- window.addEventListener("pagehide", onPageHide);
143
+ document.addEventListener("visibilitychange", flushWhenHidden);
144
+ window.addEventListener("pagehide", client.flush);
254
145
  return () => {
255
- document.removeEventListener("visibilitychange", onVisibilityChange);
256
- window.removeEventListener("pagehide", onPageHide);
146
+ unsubscribe();
147
+ document.removeEventListener("visibilitychange", flushWhenHidden);
148
+ window.removeEventListener("pagehide", client.flush);
257
149
  client.flush();
258
150
  };
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]);
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 { AUTO_EVENT, AlpicAnalytics, INTERACTION_ATTRIBUTE, useAnalytics };
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.g845fffa",
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.9",
48
+ "tsdown": "^0.22.5",
49
49
  "typescript": "^6.0.3",
50
50
  "vitest": "^4.1.10",
51
51
  "zod": "^4.4.3"