@wxt-dev/analytics 0.5.0 → 0.5.2

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/README.md CHANGED
@@ -10,15 +10,19 @@ Report analytics events from your web extension extension.
10
10
  ## Install With WXT
11
11
 
12
12
  1. Install the NPM package:
13
+
13
14
  ```bash
14
15
  pnpm i @wxt-dev/analytics
15
16
  ```
17
+
16
18
  2. In your `wxt.config.ts`, add the WXT module:
19
+
17
20
  ```ts
18
21
  export default defineConfig({
19
22
  modules: ['@wxt-dev/analytics/module'],
20
23
  });
21
24
  ```
25
+
22
26
  3. In your `<srcDir>/app.config.ts`, add a provider:
23
27
 
24
28
  ```ts
@@ -49,9 +53,11 @@ Report analytics events from your web extension extension.
49
53
  ## Install Without WXT
50
54
 
51
55
  1. Install the NPM package:
56
+
52
57
  ```bash
53
58
  pnpm i @wxt-dev/analytics
54
59
  ```
60
+
55
61
  2. Create an `analytics` instance:
56
62
 
57
63
  ```ts
@@ -66,10 +72,12 @@ Report analytics events from your web extension extension.
66
72
  ```
67
73
 
68
74
  3. Import your analytics module in the background to initialize the message listener:
75
+
69
76
  ```ts
70
77
  // background.ts
71
78
  import './utils/analytics';
72
79
  ```
80
+
73
81
  4. Then use your `analytics` instance to report events:
74
82
 
75
83
  ```ts
@@ -1,3 +1,6 @@
1
- declare const _default: () => void;
1
+ import "#analytics";
2
2
 
3
- export { _default as default };
3
+ //#region modules/analytics/background-plugin.d.ts
4
+ declare const _default: () => void;
5
+ //#endregion
6
+ export { _default as default };
@@ -1,6 +1,7 @@
1
- import '#analytics';
1
+ import "#analytics";
2
2
 
3
- const backgroundPlugin = () => {
4
- };
3
+ //#region modules/analytics/background-plugin.ts
4
+ var background_plugin_default = () => {};
5
5
 
6
- export { backgroundPlugin as default };
6
+ //#endregion
7
+ export { background_plugin_default as default };
package/dist/index.d.mts CHANGED
@@ -1,12 +1,13 @@
1
- import { AnalyticsConfig, Analytics, AnalyticsProvider } from './types.mjs';
1
+ import { Analytics, AnalyticsConfig, AnalyticsProvider } from "./types.mjs";
2
2
 
3
+ //#region modules/analytics/client.d.ts
3
4
  declare function createAnalytics(config?: AnalyticsConfig): Analytics;
4
- declare function defineAnalyticsProvider<T = never>(definition: (
5
- /** The analytics object. */
6
- analytics: Analytics,
7
- /** Config passed into the analytics module from `app.config.ts`. */
8
- config: AnalyticsConfig,
9
- /** Provider options */
10
- options: T) => ReturnType<AnalyticsProvider>): (options: T) => AnalyticsProvider;
5
+ declare function defineAnalyticsProvider<T = never>(definition: (/** The analytics object. */
6
+
7
+ analytics: Analytics, /** Config passed into the analytics module from `app.config.ts`. */
11
8
 
12
- export { createAnalytics, defineAnalyticsProvider };
9
+ config: AnalyticsConfig, /** Provider options */
10
+
11
+ options: T) => ReturnType<AnalyticsProvider>): (options: T) => AnalyticsProvider;
12
+ //#endregion
13
+ export { createAnalytics, defineAnalyticsProvider };
package/dist/index.mjs CHANGED
@@ -1,205 +1,177 @@
1
- import { UAParser } from 'ua-parser-js';
1
+ import { UAParser } from "ua-parser-js";
2
+ import { browser } from "@wxt-dev/browser";
2
3
 
4
+ //#region modules/analytics/client.ts
3
5
  const ANALYTICS_PORT = "@wxt-dev/analytics";
6
+ const INTERACTIVE_TAGS = new Set([
7
+ "A",
8
+ "BUTTON",
9
+ "INPUT",
10
+ "SELECT",
11
+ "TEXTAREA"
12
+ ]);
13
+ const INTERACTIVE_ROLES = new Set([
14
+ "button",
15
+ "link",
16
+ "checkbox",
17
+ "menuitem",
18
+ "tab",
19
+ "radio"
20
+ ]);
4
21
  function createAnalytics(config) {
5
- if (typeof chrome === "undefined" || !chrome?.runtime?.id)
6
- throw Error(
7
- "Cannot use WXT analytics in contexts without access to the browser.runtime APIs"
8
- );
9
- if (config == null) {
10
- console.warn(
11
- "[@wxt-dev/analytics] Config not provided to createAnalytics. If you're using WXT, add the 'analytics' property to '<srcDir>/app.config.ts'."
12
- );
13
- }
14
- if (location.pathname === "/background.js")
15
- return createBackgroundAnalytics(config);
16
- return createFrontendAnalytics();
22
+ if (!browser?.runtime?.id) throw Error("Cannot use WXT analytics in contexts without access to the browser.runtime APIs");
23
+ if (config == null) console.warn("[@wxt-dev/analytics] Config not provided to createAnalytics. If you're using WXT, add the 'analytics' property to '<srcDir>/app.config.ts'.");
24
+ if (location.pathname === "/background.js") return createBackgroundAnalytics(config);
25
+ return createFrontendAnalytics();
17
26
  }
27
+ /**
28
+ * Creates an analytics client in the background responsible for uploading events to the server to avoid CORS errors.
29
+ */
18
30
  function createBackgroundAnalytics(config) {
19
- const userIdStorage = config?.userId ?? defineStorageItem("wxt-analytics:user-id");
20
- const userPropertiesStorage = config?.userProperties ?? defineStorageItem(
21
- "wxt-analytics:user-properties",
22
- {}
23
- );
24
- const enabled = config?.enabled ?? defineStorageItem("local:wxt-analytics:enabled", false);
25
- const platformInfo = chrome.runtime.getPlatformInfo();
26
- const userAgent = UAParser();
27
- let userId = Promise.resolve(userIdStorage.getValue()).then(
28
- (id) => id ?? globalThis.crypto.randomUUID()
29
- );
30
- let userProperties = userPropertiesStorage.getValue();
31
- const manifest = chrome.runtime.getManifest();
32
- const getBackgroundMeta = () => ({
33
- timestamp: Date.now(),
34
- // Don't track sessions for the background, it can be running
35
- // indefinitely, and will inflate session duration stats.
36
- sessionId: void 0,
37
- language: navigator.language,
38
- referrer: void 0,
39
- screen: void 0,
40
- url: location.href,
41
- title: void 0
42
- });
43
- const getBaseEvent = async (meta) => {
44
- const platform = await platformInfo;
45
- return {
46
- meta,
47
- user: {
48
- id: await userId,
49
- properties: {
50
- version: config?.version ?? manifest.version_name ?? manifest.version,
51
- wxtMode: import.meta.env.MODE,
52
- wxtBrowser: import.meta.env.BROWSER,
53
- arch: platform.arch,
54
- os: platform.os,
55
- browser: userAgent.browser.name,
56
- browserVersion: userAgent.browser.version,
57
- ...await userProperties
58
- }
59
- }
60
- };
61
- };
62
- const analytics = {
63
- identify: async (newUserId, newUserProperties = {}, meta = getBackgroundMeta()) => {
64
- userId = Promise.resolve(newUserId);
65
- userProperties = Promise.resolve(newUserProperties);
66
- await Promise.all([
67
- userIdStorage.setValue?.(newUserId),
68
- userPropertiesStorage.setValue?.(newUserProperties)
69
- ]);
70
- const event = await getBaseEvent(meta);
71
- if (config?.debug) console.debug("[@wxt-dev/analytics] identify", event);
72
- if (await enabled.getValue()) {
73
- await Promise.allSettled(
74
- providers.map((provider) => provider.identify(event))
75
- );
76
- } else if (config?.debug) {
77
- console.debug(
78
- "[@wxt-dev/analytics] Analytics disabled, identify() not uploaded"
79
- );
80
- }
81
- },
82
- page: async (location2, meta = getBackgroundMeta()) => {
83
- const baseEvent = await getBaseEvent(meta);
84
- const event = {
85
- ...baseEvent,
86
- page: {
87
- url: meta?.url ?? globalThis.location?.href,
88
- location: location2,
89
- title: meta?.title ?? globalThis.document?.title
90
- }
91
- };
92
- if (config?.debug) console.debug("[@wxt-dev/analytics] page", event);
93
- if (await enabled.getValue()) {
94
- await Promise.allSettled(
95
- providers.map((provider) => provider.page(event))
96
- );
97
- } else if (config?.debug) {
98
- console.debug(
99
- "[@wxt-dev/analytics] Analytics disabled, page() not uploaded"
100
- );
101
- }
102
- },
103
- track: async (eventName, eventProperties, meta = getBackgroundMeta()) => {
104
- const baseEvent = await getBaseEvent(meta);
105
- const event = {
106
- ...baseEvent,
107
- event: { name: eventName, properties: eventProperties }
108
- };
109
- if (config?.debug) console.debug("[@wxt-dev/analytics] track", event);
110
- if (await enabled.getValue()) {
111
- await Promise.allSettled(
112
- providers.map((provider) => provider.track(event))
113
- );
114
- } else if (config?.debug) {
115
- console.debug(
116
- "[@wxt-dev/analytics] Analytics disabled, track() not uploaded"
117
- );
118
- }
119
- },
120
- setEnabled: async (newEnabled) => {
121
- await enabled.setValue?.(newEnabled);
122
- },
123
- autoTrack: () => {
124
- return () => {
125
- };
126
- }
127
- };
128
- const providers = config?.providers?.map((provider) => provider(analytics, config)) ?? [];
129
- chrome.runtime.onConnect.addListener((port) => {
130
- if (port.name === ANALYTICS_PORT) {
131
- port.onMessage.addListener(({ fn, args }) => {
132
- void analytics[fn]?.(...args);
133
- });
134
- }
135
- });
136
- return analytics;
31
+ const userIdStorage = config?.userId ?? defineStorageItem("wxt-analytics:user-id");
32
+ const userPropertiesStorage = config?.userProperties ?? defineStorageItem("wxt-analytics:user-properties", {});
33
+ const enabled = config?.enabled ?? defineStorageItem("local:wxt-analytics:enabled", false);
34
+ const platformInfo = browser.runtime.getPlatformInfo();
35
+ const userAgent = UAParser();
36
+ let userId = Promise.resolve(userIdStorage.getValue()).then((id) => id ?? globalThis.crypto.randomUUID());
37
+ let userProperties = userPropertiesStorage.getValue();
38
+ const manifest = browser.runtime.getManifest();
39
+ const getBackgroundMeta = () => ({
40
+ timestamp: Date.now(),
41
+ sessionId: void 0,
42
+ language: navigator.language,
43
+ referrer: void 0,
44
+ screen: void 0,
45
+ url: location.href,
46
+ title: void 0
47
+ });
48
+ const getBaseEvent = async (meta) => {
49
+ const { arch, os } = await platformInfo;
50
+ return {
51
+ meta,
52
+ user: {
53
+ id: await userId,
54
+ properties: {
55
+ version: config?.version ?? manifest.version_name ?? manifest.version,
56
+ wxtMode: import.meta.env.MODE,
57
+ wxtBrowser: import.meta.env.BROWSER,
58
+ arch,
59
+ os,
60
+ browser: userAgent.browser.name,
61
+ browserVersion: userAgent.browser.version,
62
+ ...await userProperties
63
+ }
64
+ }
65
+ };
66
+ };
67
+ const analytics = {
68
+ identify: async (newUserId, newUserProperties = {}, meta = getBackgroundMeta()) => {
69
+ userId = Promise.resolve(newUserId);
70
+ userProperties = Promise.resolve(newUserProperties);
71
+ await Promise.all([userIdStorage.setValue?.(newUserId), userPropertiesStorage.setValue?.(newUserProperties)]);
72
+ const event = await getBaseEvent(meta);
73
+ if (config?.debug) console.debug("[@wxt-dev/analytics] identify", event);
74
+ if (await enabled.getValue()) await Promise.allSettled(providers.map((provider) => provider.identify(event)));
75
+ else if (config?.debug) console.debug("[@wxt-dev/analytics] Analytics disabled, identify() not uploaded");
76
+ },
77
+ page: async (location, meta = getBackgroundMeta()) => {
78
+ const event = {
79
+ ...await getBaseEvent(meta),
80
+ page: {
81
+ url: meta?.url ?? globalThis.location?.href,
82
+ location,
83
+ title: meta?.title ?? globalThis.document?.title
84
+ }
85
+ };
86
+ if (config?.debug) console.debug("[@wxt-dev/analytics] page", event);
87
+ if (await enabled.getValue()) await Promise.allSettled(providers.map((provider) => provider.page(event)));
88
+ else if (config?.debug) console.debug("[@wxt-dev/analytics] Analytics disabled, page() not uploaded");
89
+ },
90
+ track: async (eventName, eventProperties, meta = getBackgroundMeta()) => {
91
+ const event = {
92
+ ...await getBaseEvent(meta),
93
+ event: {
94
+ name: eventName,
95
+ properties: eventProperties
96
+ }
97
+ };
98
+ if (config?.debug) console.debug("[@wxt-dev/analytics] track", event);
99
+ if (await enabled.getValue()) await Promise.allSettled(providers.map((provider) => provider.track(event)));
100
+ else if (config?.debug) console.debug("[@wxt-dev/analytics] Analytics disabled, track() not uploaded");
101
+ },
102
+ setEnabled: async (newEnabled) => {
103
+ await enabled.setValue?.(newEnabled);
104
+ },
105
+ autoTrack: () => {
106
+ return () => {};
107
+ }
108
+ };
109
+ const providers = config?.providers?.map((provider) => provider(analytics, config)) ?? [];
110
+ browser.runtime.onConnect.addListener((port) => {
111
+ if (port.name === ANALYTICS_PORT) port.onMessage.addListener(({ fn, args }) => {
112
+ analytics[fn]?.(...args);
113
+ });
114
+ });
115
+ return analytics;
137
116
  }
117
+ /**
118
+ * Creates an analytics client for non-background contexts.
119
+ */
138
120
  function createFrontendAnalytics() {
139
- const port = chrome.runtime.connect({ name: ANALYTICS_PORT });
140
- const sessionId = Date.now();
141
- const getFrontendMetadata = () => ({
142
- sessionId,
143
- timestamp: Date.now(),
144
- language: navigator.language,
145
- referrer: globalThis.document?.referrer || void 0,
146
- screen: globalThis.window ? `${globalThis.window.screen.width}x${globalThis.window.screen.height}` : void 0,
147
- url: location.href,
148
- title: document.title || void 0
149
- });
150
- const methodForwarder = (fn) => (...args) => {
151
- port.postMessage({ fn, args: [...args, getFrontendMetadata()] });
152
- };
153
- const analytics = {
154
- identify: methodForwarder("identify"),
155
- page: methodForwarder("page"),
156
- track: methodForwarder("track"),
157
- setEnabled: methodForwarder("setEnabled"),
158
- autoTrack: (root) => {
159
- const onClick = (event) => {
160
- const element = event.target;
161
- if (!element || !INTERACTIVE_TAGS.has(element.tagName) && !INTERACTIVE_ROLES.has(element.getAttribute("role")))
162
- return;
163
- void analytics.track("click", {
164
- tagName: element.tagName?.toLowerCase(),
165
- id: element.id || void 0,
166
- className: element.className || void 0,
167
- textContent: element.textContent?.substring(0, 50) || void 0,
168
- // Limit text content length
169
- href: element.href
170
- });
171
- };
172
- root.addEventListener("click", onClick, { capture: true, passive: true });
173
- return () => {
174
- root.removeEventListener("click", onClick);
175
- };
176
- }
177
- };
178
- return analytics;
121
+ const port = browser.runtime.connect({ name: ANALYTICS_PORT });
122
+ const sessionId = Date.now();
123
+ const getFrontendMetadata = () => ({
124
+ sessionId,
125
+ timestamp: Date.now(),
126
+ language: navigator.language,
127
+ referrer: globalThis.document?.referrer || void 0,
128
+ screen: globalThis.window ? `${globalThis.window.screen.width}x${globalThis.window.screen.height}` : void 0,
129
+ url: location.href,
130
+ title: document.title || void 0
131
+ });
132
+ const methodForwarder = (fn) => (...args) => {
133
+ port.postMessage({
134
+ fn,
135
+ args: [...args, getFrontendMetadata()]
136
+ });
137
+ };
138
+ const analytics = {
139
+ identify: methodForwarder("identify"),
140
+ page: methodForwarder("page"),
141
+ track: methodForwarder("track"),
142
+ setEnabled: methodForwarder("setEnabled"),
143
+ autoTrack: (root) => {
144
+ const onClick = (event) => {
145
+ const element = event.target;
146
+ if (!element || !INTERACTIVE_TAGS.has(element.tagName) && !INTERACTIVE_ROLES.has(element.getAttribute("role"))) return;
147
+ analytics.track("click", {
148
+ tagName: element.tagName?.toLowerCase(),
149
+ id: element.id || void 0,
150
+ className: element.className || void 0,
151
+ textContent: element.textContent?.substring(0, 50) || void 0,
152
+ href: element.href
153
+ });
154
+ };
155
+ root.addEventListener("click", onClick, {
156
+ capture: true,
157
+ passive: true
158
+ });
159
+ return () => {
160
+ root.removeEventListener("click", onClick);
161
+ };
162
+ }
163
+ };
164
+ return analytics;
179
165
  }
180
166
  function defineStorageItem(key, defaultValue) {
181
- return {
182
- getValue: async () => (await chrome.storage.local.get(key))[key] ?? defaultValue,
183
- setValue: (newValue) => chrome.storage.local.set({ [key]: newValue })
184
- };
167
+ return {
168
+ getValue: async () => (await browser.storage.local.get(key))[key] ?? defaultValue,
169
+ setValue: (newValue) => browser.storage.local.set({ [key]: newValue })
170
+ };
185
171
  }
186
- const INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
187
- "A",
188
- "BUTTON",
189
- "INPUT",
190
- "SELECT",
191
- "TEXTAREA"
192
- ]);
193
- const INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
194
- "button",
195
- "link",
196
- "checkbox",
197
- "menuitem",
198
- "tab",
199
- "radio"
200
- ]);
201
172
  function defineAnalyticsProvider(definition) {
202
- return (options) => (analytics, config) => definition(analytics, config, options);
173
+ return (options) => (analytics, config) => definition(analytics, config, options);
203
174
  }
204
175
 
205
- export { createAnalytics, defineAnalyticsProvider };
176
+ //#endregion
177
+ export { createAnalytics, defineAnalyticsProvider };
package/dist/module.d.mts CHANGED
@@ -1,11 +1,13 @@
1
- import * as wxt from 'wxt';
2
- import { AnalyticsConfig } from './types.mjs';
1
+ import { AnalyticsConfig } from "./types.mjs";
2
+ import * as wxt from "wxt";
3
+ import "wxt/utils/define-app-config";
3
4
 
5
+ //#region modules/analytics/index.d.ts
4
6
  declare module 'wxt/utils/define-app-config' {
5
- interface WxtAppConfig {
6
- analytics: AnalyticsConfig;
7
- }
7
+ interface WxtAppConfig {
8
+ analytics: AnalyticsConfig;
9
+ }
8
10
  }
9
11
  declare const _default: wxt.WxtModule<wxt.WxtModuleOptions>;
10
-
11
- export { _default as default };
12
+ //#endregion
13
+ export { _default as default };
package/dist/module.mjs CHANGED
@@ -1,61 +1,52 @@
1
- import 'wxt';
2
- import 'wxt/utils/define-app-config';
3
- import { defineWxtModule, addAlias, addWxtPlugin, addViteConfig } from 'wxt/modules';
4
- import { resolve } from 'node:path';
1
+ import "wxt";
2
+ import "wxt/utils/define-app-config";
3
+ import { addAlias, addViteConfig, addWxtPlugin, defineWxtModule } from "wxt/modules";
4
+ import { resolve } from "node:path";
5
5
 
6
- const index = defineWxtModule({
7
- name: "analytics",
8
- imports: [{ name: "analytics", from: "#analytics" }],
9
- setup(wxt) {
10
- const wxtAnalyticsFolder = resolve(wxt.config.wxtDir, "analytics");
11
- const wxtAnalyticsIndex = resolve(wxtAnalyticsFolder, "index.ts");
12
- const clientModuleId = "@wxt-dev/analytics" ;
13
- const pluginModuleId = "@wxt-dev/analytics/background-plugin" ;
14
- wxt.hook("build:manifestGenerated", (_, manifest) => {
15
- manifest.permissions ??= [];
16
- if (!manifest.permissions.includes("storage")) {
17
- manifest.permissions.push("storage");
18
- }
19
- });
20
- const wxtAnalyticsCode = [
21
- `import { createAnalytics } from '${clientModuleId }';`,
22
- `import { useAppConfig } from '#imports';`,
23
- ``,
24
- `export const analytics = createAnalytics(useAppConfig().analytics);`,
25
- ``
26
- ].join("\n");
27
- addAlias(wxt, "#analytics", wxtAnalyticsIndex);
28
- wxt.hook("prepare:types", async (_, entries) => {
29
- entries.push({
30
- path: wxtAnalyticsIndex,
31
- text: wxtAnalyticsCode
32
- });
33
- });
34
- wxt.hook("entrypoints:resolved", (_, entrypoints) => {
35
- const hasBackground = entrypoints.find(
36
- (entry) => entry.type === "background"
37
- );
38
- if (!hasBackground) {
39
- entrypoints.push({
40
- type: "background",
41
- inputPath: "virtual:user-background",
42
- name: "background",
43
- options: {},
44
- outputDir: wxt.config.outDir,
45
- skipped: false
46
- });
47
- }
48
- });
49
- addWxtPlugin(wxt, pluginModuleId);
50
- addViteConfig(wxt, () => ({
51
- optimizeDeps: {
52
- // Ensure the "#analytics" import is processed by vite in the background plugin
53
- exclude: ["@wxt-dev/analytics"],
54
- // Ensure the CJS subdependency is preprocessed into ESM
55
- include: ["@wxt-dev/analytics > ua-parser-js"]
56
- }
57
- }));
58
- }
6
+ //#region modules/analytics/index.ts
7
+ var analytics_default = defineWxtModule({
8
+ name: "analytics",
9
+ imports: [{
10
+ name: "analytics",
11
+ from: "#analytics"
12
+ }],
13
+ setup(wxt) {
14
+ const wxtAnalyticsIndex = resolve(resolve(wxt.config.wxtDir, "analytics"), "index.ts");
15
+ const clientModuleId = "@wxt-dev/analytics";
16
+ const pluginModuleId = "@wxt-dev/analytics/background-plugin";
17
+ wxt.hook("build:manifestGenerated", (_, manifest) => {
18
+ manifest.permissions ??= [];
19
+ if (!manifest.permissions.includes("storage")) manifest.permissions.push("storage");
20
+ });
21
+ const wxtAnalyticsCode = `import { createAnalytics } from '${clientModuleId}';
22
+ import { useAppConfig } from '#imports';
23
+
24
+ export const analytics = createAnalytics(useAppConfig().analytics);
25
+ `;
26
+ addAlias(wxt, "#analytics", wxtAnalyticsIndex);
27
+ wxt.hook("prepare:types", async (_, entries) => {
28
+ entries.push({
29
+ path: wxtAnalyticsIndex,
30
+ text: wxtAnalyticsCode
31
+ });
32
+ });
33
+ wxt.hook("entrypoints:resolved", (_, entrypoints) => {
34
+ if (!entrypoints.find((entry) => entry.type === "background")) entrypoints.push({
35
+ type: "background",
36
+ inputPath: "virtual:user-background",
37
+ name: "background",
38
+ options: {},
39
+ outputDir: wxt.config.outDir,
40
+ skipped: false
41
+ });
42
+ });
43
+ addWxtPlugin(wxt, pluginModuleId);
44
+ addViteConfig(wxt, () => ({ optimizeDeps: {
45
+ exclude: ["@wxt-dev/analytics"],
46
+ include: ["@wxt-dev/analytics > ua-parser-js"]
47
+ } }));
48
+ }
59
49
  });
60
50
 
61
- export { index as default };
51
+ //#endregion
52
+ export { analytics_default as default };
@@ -1,9 +1,11 @@
1
- import { AnalyticsProvider } from '../types.mjs';
1
+ import { AnalyticsProvider } from "../types.mjs";
2
2
 
3
+ //#region modules/analytics/providers/google-analytics-4.d.ts
3
4
  interface GoogleAnalytics4ProviderOptions {
4
- apiSecret: string;
5
- measurementId: string;
5
+ apiUrl?: string;
6
+ apiSecret: string;
7
+ measurementId: string;
6
8
  }
7
9
  declare const googleAnalytics4: (options: GoogleAnalytics4ProviderOptions) => AnalyticsProvider;
8
-
9
- export { type GoogleAnalytics4ProviderOptions, googleAnalytics4 };
10
+ //#endregion
11
+ export { GoogleAnalytics4ProviderOptions, googleAnalytics4 };
@@ -1,61 +1,48 @@
1
- import { defineAnalyticsProvider } from '../index.mjs';
2
- import 'ua-parser-js';
1
+ import { defineAnalyticsProvider } from "../index.mjs";
3
2
 
3
+ //#region modules/analytics/providers/google-analytics-4.ts
4
4
  const DEFAULT_ENGAGEMENT_TIME_IN_MSEC = 100;
5
- const googleAnalytics4 = defineAnalyticsProvider(
6
- (_, config, options) => {
7
- const send = async (data, eventName, eventProperties) => {
8
- const url = new URL(
9
- config?.debug ? "/debug/mp/collect" : "/mp/collect",
10
- "https://www.google-analytics.com"
11
- );
12
- if (options.apiSecret)
13
- url.searchParams.set("api_secret", options.apiSecret);
14
- if (options.measurementId)
15
- url.searchParams.set("measurement_id", options.measurementId);
16
- const userProperties = {
17
- language: data.meta.language,
18
- screen: data.meta.screen,
19
- ...data.user.properties
20
- };
21
- const mappedUserProperties = Object.fromEntries(
22
- Object.entries(userProperties).map(([name, value]) => [
23
- name,
24
- value == null ? void 0 : { value }
25
- ])
26
- );
27
- await fetch(url.href, {
28
- method: "POST",
29
- body: JSON.stringify({
30
- client_id: data.user.id,
31
- consent: {
32
- ad_user_data: "DENIED",
33
- ad_personalization: "DENIED"
34
- },
35
- user_properties: mappedUserProperties,
36
- events: [
37
- {
38
- name: eventName,
39
- params: {
40
- session_id: data.meta.sessionId,
41
- engagement_time_msec: DEFAULT_ENGAGEMENT_TIME_IN_MSEC,
42
- ...eventProperties
43
- }
44
- }
45
- ]
46
- })
47
- });
48
- };
49
- return {
50
- identify: () => Promise.resolve(),
51
- // No-op, user data uploaded in page/track
52
- page: (event) => send(event, "page_view", {
53
- page_title: event.page.title,
54
- page_location: event.page.location
55
- }),
56
- track: (event) => send(event, event.event.name, event.event.properties)
57
- };
58
- }
59
- );
5
+ const googleAnalytics4 = defineAnalyticsProvider((_, config, options) => {
6
+ const send = async (data, eventName, eventProperties) => {
7
+ const url = new URL(config?.debug ? "/debug/mp/collect" : "/mp/collect", options.apiUrl ?? "https://www.google-analytics.com");
8
+ if (options.apiSecret) url.searchParams.set("api_secret", options.apiSecret);
9
+ if (options.measurementId) url.searchParams.set("measurement_id", options.measurementId);
10
+ const userProperties = {
11
+ language: data.meta.language,
12
+ screen: data.meta.screen,
13
+ ...data.user.properties
14
+ };
15
+ const mappedUserProperties = Object.fromEntries(Object.entries(userProperties).map(([name, value]) => [name, value == null ? void 0 : { value }]));
16
+ await fetch(url.href, {
17
+ method: "POST",
18
+ body: JSON.stringify({
19
+ client_id: data.user.id,
20
+ consent: {
21
+ ad_user_data: "DENIED",
22
+ ad_personalization: "DENIED"
23
+ },
24
+ user_properties: mappedUserProperties,
25
+ user_agent: navigator.userAgent,
26
+ events: [{
27
+ name: eventName,
28
+ params: {
29
+ session_id: data.meta.sessionId,
30
+ engagement_time_msec: DEFAULT_ENGAGEMENT_TIME_IN_MSEC,
31
+ ...eventProperties
32
+ }
33
+ }]
34
+ })
35
+ });
36
+ };
37
+ return {
38
+ identify: () => Promise.resolve(),
39
+ page: (event) => send(event, "page_view", {
40
+ page_title: event.page.title,
41
+ page_location: event.page.location
42
+ }),
43
+ track: (event) => send(event, event.event.name, event.event.properties)
44
+ };
45
+ });
60
46
 
61
- export { googleAnalytics4 };
47
+ //#endregion
48
+ export { googleAnalytics4 };
@@ -1,10 +1,11 @@
1
- import { AnalyticsProvider } from '../types.mjs';
1
+ import { AnalyticsProvider } from "../types.mjs";
2
2
 
3
+ //#region modules/analytics/providers/umami.d.ts
3
4
  interface UmamiProviderOptions {
4
- apiUrl: string;
5
- websiteId: string;
6
- domain: string;
5
+ apiUrl: string;
6
+ websiteId: string;
7
+ domain: string;
7
8
  }
8
9
  declare const umami: (options: UmamiProviderOptions) => AnalyticsProvider;
9
-
10
- export { type UmamiProviderOptions, umami };
10
+ //#endregion
11
+ export { UmamiProviderOptions, umami };
@@ -1,54 +1,51 @@
1
- import { defineAnalyticsProvider } from '../index.mjs';
2
- import 'ua-parser-js';
1
+ import { defineAnalyticsProvider } from "../index.mjs";
3
2
 
4
- const umami = defineAnalyticsProvider(
5
- (_, config, options) => {
6
- const send = (payload) => {
7
- if (config.debug) {
8
- console.debug("[@wxt-dev/analytics] Sending event to Umami:", payload);
9
- }
10
- return fetch(`${options.apiUrl}/send`, {
11
- method: "POST",
12
- headers: {
13
- "Content-Type": "application/json"
14
- },
15
- body: JSON.stringify({ type: "event", payload })
16
- });
17
- };
18
- return {
19
- identify: () => Promise.resolve(),
20
- // No-op, user data uploaded in page/track
21
- page: async (event) => {
22
- await send({
23
- name: "page_view",
24
- website: options.websiteId,
25
- url: event.page.url,
26
- hostname: options.domain,
27
- language: event.meta.language ?? "",
28
- referrer: event.meta.referrer ?? "",
29
- screen: event.meta.screen ?? "",
30
- title: event.page.title ?? "<blank>",
31
- data: event.user.properties
32
- });
33
- },
34
- track: async (event) => {
35
- await send({
36
- name: event.event.name,
37
- website: options.websiteId,
38
- url: event.meta.url ?? "/",
39
- title: "<blank>",
40
- hostname: options.domain,
41
- language: event.meta.language ?? "",
42
- referrer: event.meta.referrer ?? "",
43
- screen: event.meta.screen ?? "",
44
- data: {
45
- ...event.event.properties,
46
- ...event.user.properties
47
- }
48
- });
49
- }
50
- };
51
- }
52
- );
3
+ //#region modules/analytics/providers/umami.ts
4
+ const umami = defineAnalyticsProvider((_, config, options) => {
5
+ const send = (payload) => {
6
+ if (config.debug) console.debug("[@wxt-dev/analytics] Sending event to Umami:", payload);
7
+ return fetch(`${options.apiUrl}/send`, {
8
+ method: "POST",
9
+ headers: { "Content-Type": "application/json" },
10
+ body: JSON.stringify({
11
+ type: "event",
12
+ payload
13
+ })
14
+ });
15
+ };
16
+ return {
17
+ identify: () => Promise.resolve(),
18
+ page: async (event) => {
19
+ await send({
20
+ name: "page_view",
21
+ website: options.websiteId,
22
+ url: event.page.url,
23
+ hostname: options.domain,
24
+ language: event.meta.language ?? "",
25
+ referrer: event.meta.referrer ?? "",
26
+ screen: event.meta.screen ?? "",
27
+ title: event.page.title ?? "<blank>",
28
+ data: event.user.properties
29
+ });
30
+ },
31
+ track: async (event) => {
32
+ await send({
33
+ name: event.event.name,
34
+ website: options.websiteId,
35
+ url: event.meta.url ?? "/",
36
+ title: "<blank>",
37
+ hostname: options.domain,
38
+ language: event.meta.language ?? "",
39
+ referrer: event.meta.referrer ?? "",
40
+ screen: event.meta.screen ?? "",
41
+ data: {
42
+ ...event.event.properties,
43
+ ...event.user.properties
44
+ }
45
+ });
46
+ }
47
+ };
48
+ });
53
49
 
54
- export { umami };
50
+ //#endregion
51
+ export { umami };
package/dist/types.d.mts CHANGED
@@ -1,90 +1,88 @@
1
+ //#region modules/analytics/types.d.ts
1
2
  interface Analytics {
2
- /** Report a page change. */
3
- page: (url: string) => void;
4
- /** Report a custom event. */
5
- track: (eventName: string, eventProperties?: Record<string, string>) => void;
6
- /** Save information about the user. */
7
- identify: (userId: string, userProperties?: Record<string, string>) => void;
8
- /** Automatically setup and track user interactions, returning a function to remove any listeners that were setup. */
9
- autoTrack: (root: Document | ShadowRoot | Element) => () => void;
10
- /** Calls `config.enabled.setValue`. */
11
- setEnabled: (enabled: boolean) => void;
3
+ /** Report a page change. */
4
+ page: (url: string) => void;
5
+ /** Report a custom event. */
6
+ track: (eventName: string, eventProperties?: Record<string, string>) => void;
7
+ /** Save information about the user. */
8
+ identify: (userId: string, userProperties?: Record<string, string>) => void;
9
+ /** Automatically setup and track user interactions, returning a function to remove any listeners that were setup. */
10
+ autoTrack: (root: Document | ShadowRoot | Element) => () => void;
11
+ /** Calls `config.enabled.setValue`. */
12
+ setEnabled: (enabled: boolean) => void;
12
13
  }
13
14
  interface AnalyticsConfig {
14
- /**
15
- * Array of providers to send analytics to.
16
- */
17
- providers: AnalyticsProvider[];
18
- /**
19
- * Enable debug logs and other provider-specific debugging features.
20
- */
21
- debug?: boolean;
22
- /**
23
- * Your extension's version, reported alongside events.
24
- * @default browser.runtime.getManifest().version`.
25
- */
26
- version?: string;
27
- /**
28
- * Configure how the enabled flag is persisted. Defaults to using `browser.storage.local`.
29
- */
30
- enabled?: AnalyticsStorageItem<boolean>;
31
- /**
32
- * Configure how the user Id is persisted. Defaults to using `browser.storage.local`.
33
- */
34
- userId?: AnalyticsStorageItem<string>;
35
- /**
36
- * Configure how user properties are persisted. Defaults to using `browser.storage.local`.
37
- */
38
- userProperties?: AnalyticsStorageItem<Record<string, string>>;
15
+ /**
16
+ * Array of providers to send analytics to.
17
+ */
18
+ providers: AnalyticsProvider[];
19
+ /**
20
+ * Enable debug logs and other provider-specific debugging features.
21
+ */
22
+ debug?: boolean;
23
+ /**
24
+ * Your extension's version, reported alongside events.
25
+ * @default browser.runtime.getManifest().version`.
26
+ */
27
+ version?: string;
28
+ /**
29
+ * Configure how the enabled flag is persisted. Defaults to using `browser.storage.local`.
30
+ */
31
+ enabled?: AnalyticsStorageItem<boolean>;
32
+ /**
33
+ * Configure how the user Id is persisted. Defaults to using `browser.storage.local`.
34
+ */
35
+ userId?: AnalyticsStorageItem<string>;
36
+ /**
37
+ * Configure how user properties are persisted. Defaults to using `browser.storage.local`.
38
+ */
39
+ userProperties?: AnalyticsStorageItem<Record<string, string>>;
39
40
  }
40
41
  interface AnalyticsStorageItem<T> {
41
- getValue: () => T | Promise<T>;
42
- setValue?: (newValue: T) => void | Promise<void>;
42
+ getValue: () => T | Promise<T>;
43
+ setValue?: (newValue: T) => void | Promise<void>;
43
44
  }
44
45
  type AnalyticsProvider = (analytics: Analytics, config: AnalyticsConfig) => {
45
- /** Upload a page view event. */
46
- page: (event: AnalyticsPageViewEvent) => Promise<void>;
47
- /** Upload a custom event. */
48
- track: (event: AnalyticsTrackEvent) => Promise<void>;
49
- /** Upload information about the user. */
50
- identify: (event: BaseAnalyticsEvent) => Promise<void>;
46
+ /** Upload a page view event. */page: (event: AnalyticsPageViewEvent) => Promise<void>; /** Upload a custom event. */
47
+ track: (event: AnalyticsTrackEvent) => Promise<void>; /** Upload information about the user. */
48
+ identify: (event: BaseAnalyticsEvent) => Promise<void>;
51
49
  };
52
50
  interface BaseAnalyticsEvent {
53
- meta: AnalyticsEventMetadata;
54
- user: {
55
- id: string;
56
- properties: Record<string, string | undefined>;
57
- };
51
+ meta: AnalyticsEventMetadata;
52
+ user: {
53
+ id: string;
54
+ properties: Record<string, string | undefined>;
55
+ };
58
56
  }
59
57
  interface AnalyticsEventMetadata {
60
- /** Identifier of the session the event was fired from. */
61
- sessionId: number | undefined;
62
- /** `Date.now()` of when the event was reported. */
63
- timestamp: number;
64
- /** Ex: `"1920x1080"`. */
65
- screen: string | undefined;
66
- /** `document.referrer` */
67
- referrer: string | undefined;
68
- /** `navigator.language` */
69
- language: string | undefined;
70
- /** `location.href` */
71
- url: string | undefined;
72
- /** `document.title` */
73
- title: string | undefined;
58
+ /** Identifier of the session the event was fired from. */
59
+ sessionId: number | undefined;
60
+ /** `Date.now()` of when the event was reported. */
61
+ timestamp: number;
62
+ /** Ex: `"1920x1080"`. */
63
+ screen: string | undefined;
64
+ /** `document.referrer` */
65
+ referrer: string | undefined;
66
+ /** `navigator.language` */
67
+ language: string | undefined;
68
+ /** `location.href` */
69
+ url: string | undefined;
70
+ /** `document.title` */
71
+ title: string | undefined;
74
72
  }
75
73
  interface AnalyticsPageInfo {
76
- url: string;
77
- title: string | undefined;
78
- location: string | undefined;
74
+ url: string;
75
+ title: string | undefined;
76
+ location: string | undefined;
79
77
  }
80
78
  interface AnalyticsPageViewEvent extends BaseAnalyticsEvent {
81
- page: AnalyticsPageInfo;
79
+ page: AnalyticsPageInfo;
82
80
  }
83
81
  interface AnalyticsTrackEvent extends BaseAnalyticsEvent {
84
- event: {
85
- name: string;
86
- properties?: Record<string, string>;
87
- };
82
+ event: {
83
+ name: string;
84
+ properties?: Record<string, string>;
85
+ };
88
86
  }
89
-
90
- export type { Analytics, AnalyticsConfig, AnalyticsEventMetadata, AnalyticsPageInfo, AnalyticsPageViewEvent, AnalyticsProvider, AnalyticsStorageItem, AnalyticsTrackEvent, BaseAnalyticsEvent };
87
+ //#endregion
88
+ export { Analytics, AnalyticsConfig, AnalyticsEventMetadata, AnalyticsPageInfo, AnalyticsPageViewEvent, AnalyticsProvider, AnalyticsStorageItem, AnalyticsTrackEvent, BaseAnalyticsEvent };
package/dist/types.mjs CHANGED
@@ -1 +1 @@
1
-
1
+ export { };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxt-dev/analytics",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Add analytics to your web extension",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,7 +35,7 @@
35
35
  }
36
36
  },
37
37
  "module": "./dist/index.mjs",
38
- "types": "./dist/index.d.ts",
38
+ "types": "./dist/index.d.mts",
39
39
  "files": [
40
40
  "dist"
41
41
  ],
@@ -43,21 +43,19 @@
43
43
  "wxt": ">=0.20.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@aklinker1/check": "^1.4.5",
47
- "@types/chrome": "^0.0.280",
48
46
  "@types/ua-parser-js": "^0.7.39",
49
- "publint": "^0.2.12",
50
- "typescript": "^5.6.3",
51
- "unbuild": "^3.5.0",
52
- "wxt": "0.20.0"
47
+ "publint": "^0.3.17",
48
+ "typescript": "^5.9.3",
49
+ "wxt": "0.20.16"
53
50
  },
54
51
  "dependencies": {
55
- "ua-parser-js": "^1.0.40"
52
+ "ua-parser-js": "^1.0.40",
53
+ "@wxt-dev/browser": "^0.1.36"
56
54
  },
57
55
  "scripts": {
58
56
  "dev": "buildc --deps-only -- wxt",
59
57
  "dev:build": "buildc --deps-only -- wxt build",
60
58
  "check": "pnpm build && check",
61
- "build": "buildc -- unbuild"
59
+ "build": "buildc -- tsdown"
62
60
  }
63
61
  }
@@ -1,3 +0,0 @@
1
- declare const _default: () => void;
2
-
3
- export { _default as default };
package/dist/index.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import { AnalyticsConfig, Analytics, AnalyticsProvider } from './types.js';
2
-
3
- declare function createAnalytics(config?: AnalyticsConfig): Analytics;
4
- declare function defineAnalyticsProvider<T = never>(definition: (
5
- /** The analytics object. */
6
- analytics: Analytics,
7
- /** Config passed into the analytics module from `app.config.ts`. */
8
- config: AnalyticsConfig,
9
- /** Provider options */
10
- options: T) => ReturnType<AnalyticsProvider>): (options: T) => AnalyticsProvider;
11
-
12
- export { createAnalytics, defineAnalyticsProvider };
package/dist/module.d.ts DELETED
@@ -1,11 +0,0 @@
1
- import * as wxt from 'wxt';
2
- import { AnalyticsConfig } from './types.js';
3
-
4
- declare module 'wxt/utils/define-app-config' {
5
- interface WxtAppConfig {
6
- analytics: AnalyticsConfig;
7
- }
8
- }
9
- declare const _default: wxt.WxtModule<wxt.WxtModuleOptions>;
10
-
11
- export { _default as default };
@@ -1,9 +0,0 @@
1
- import { AnalyticsProvider } from '../types.js';
2
-
3
- interface GoogleAnalytics4ProviderOptions {
4
- apiSecret: string;
5
- measurementId: string;
6
- }
7
- declare const googleAnalytics4: (options: GoogleAnalytics4ProviderOptions) => AnalyticsProvider;
8
-
9
- export { type GoogleAnalytics4ProviderOptions, googleAnalytics4 };
@@ -1,10 +0,0 @@
1
- import { AnalyticsProvider } from '../types.js';
2
-
3
- interface UmamiProviderOptions {
4
- apiUrl: string;
5
- websiteId: string;
6
- domain: string;
7
- }
8
- declare const umami: (options: UmamiProviderOptions) => AnalyticsProvider;
9
-
10
- export { type UmamiProviderOptions, umami };
package/dist/types.d.ts DELETED
@@ -1,90 +0,0 @@
1
- interface Analytics {
2
- /** Report a page change. */
3
- page: (url: string) => void;
4
- /** Report a custom event. */
5
- track: (eventName: string, eventProperties?: Record<string, string>) => void;
6
- /** Save information about the user. */
7
- identify: (userId: string, userProperties?: Record<string, string>) => void;
8
- /** Automatically setup and track user interactions, returning a function to remove any listeners that were setup. */
9
- autoTrack: (root: Document | ShadowRoot | Element) => () => void;
10
- /** Calls `config.enabled.setValue`. */
11
- setEnabled: (enabled: boolean) => void;
12
- }
13
- interface AnalyticsConfig {
14
- /**
15
- * Array of providers to send analytics to.
16
- */
17
- providers: AnalyticsProvider[];
18
- /**
19
- * Enable debug logs and other provider-specific debugging features.
20
- */
21
- debug?: boolean;
22
- /**
23
- * Your extension's version, reported alongside events.
24
- * @default browser.runtime.getManifest().version`.
25
- */
26
- version?: string;
27
- /**
28
- * Configure how the enabled flag is persisted. Defaults to using `browser.storage.local`.
29
- */
30
- enabled?: AnalyticsStorageItem<boolean>;
31
- /**
32
- * Configure how the user Id is persisted. Defaults to using `browser.storage.local`.
33
- */
34
- userId?: AnalyticsStorageItem<string>;
35
- /**
36
- * Configure how user properties are persisted. Defaults to using `browser.storage.local`.
37
- */
38
- userProperties?: AnalyticsStorageItem<Record<string, string>>;
39
- }
40
- interface AnalyticsStorageItem<T> {
41
- getValue: () => T | Promise<T>;
42
- setValue?: (newValue: T) => void | Promise<void>;
43
- }
44
- type AnalyticsProvider = (analytics: Analytics, config: AnalyticsConfig) => {
45
- /** Upload a page view event. */
46
- page: (event: AnalyticsPageViewEvent) => Promise<void>;
47
- /** Upload a custom event. */
48
- track: (event: AnalyticsTrackEvent) => Promise<void>;
49
- /** Upload information about the user. */
50
- identify: (event: BaseAnalyticsEvent) => Promise<void>;
51
- };
52
- interface BaseAnalyticsEvent {
53
- meta: AnalyticsEventMetadata;
54
- user: {
55
- id: string;
56
- properties: Record<string, string | undefined>;
57
- };
58
- }
59
- interface AnalyticsEventMetadata {
60
- /** Identifier of the session the event was fired from. */
61
- sessionId: number | undefined;
62
- /** `Date.now()` of when the event was reported. */
63
- timestamp: number;
64
- /** Ex: `"1920x1080"`. */
65
- screen: string | undefined;
66
- /** `document.referrer` */
67
- referrer: string | undefined;
68
- /** `navigator.language` */
69
- language: string | undefined;
70
- /** `location.href` */
71
- url: string | undefined;
72
- /** `document.title` */
73
- title: string | undefined;
74
- }
75
- interface AnalyticsPageInfo {
76
- url: string;
77
- title: string | undefined;
78
- location: string | undefined;
79
- }
80
- interface AnalyticsPageViewEvent extends BaseAnalyticsEvent {
81
- page: AnalyticsPageInfo;
82
- }
83
- interface AnalyticsTrackEvent extends BaseAnalyticsEvent {
84
- event: {
85
- name: string;
86
- properties?: Record<string, string>;
87
- };
88
- }
89
-
90
- export type { Analytics, AnalyticsConfig, AnalyticsEventMetadata, AnalyticsPageInfo, AnalyticsPageViewEvent, AnalyticsProvider, AnalyticsStorageItem, AnalyticsTrackEvent, BaseAnalyticsEvent };