@nuxt/scripts 1.0.0-beta.24 → 1.0.0-beta.25

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.
Files changed (60) hide show
  1. package/dist/client/200.html +1 -1
  2. package/dist/client/404.html +1 -1
  3. package/dist/client/_nuxt/{Bh9fd9qr.js → C-7nRtzO.js} +1 -1
  4. package/dist/client/_nuxt/{UTi7FhVv.js → D5k4eN9O.js} +1 -1
  5. package/dist/client/_nuxt/DjhmCJlE.js +162 -0
  6. package/dist/client/_nuxt/{B7aPLMNo.js → TJ5JFHov.js} +1 -1
  7. package/dist/client/_nuxt/builds/latest.json +1 -1
  8. package/dist/client/_nuxt/builds/meta/2ec0342e-5e00-4781-82aa-c3c0f9154516.json +1 -0
  9. package/dist/client/_nuxt/entry.C5SUNdim.css +1 -0
  10. package/dist/client/_nuxt/error-404.C_3_IG5y.css +1 -0
  11. package/dist/client/_nuxt/error-500.DSv6YikH.css +1 -0
  12. package/dist/client/index.html +1 -1
  13. package/dist/module.d.mts +6 -20
  14. package/dist/module.d.ts +6 -20
  15. package/dist/module.json +1 -1
  16. package/dist/module.mjs +303 -277
  17. package/dist/registry.mjs +121 -71
  18. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue +5 -1
  19. package/dist/runtime/composables/useScriptTriggerConsent.d.ts +10 -0
  20. package/dist/runtime/composables/useScriptTriggerConsent.js +32 -19
  21. package/dist/runtime/registry/bing-uet.d.ts +20 -0
  22. package/dist/runtime/registry/bing-uet.js +29 -0
  23. package/dist/runtime/registry/bluesky-embed.d.ts +1 -1
  24. package/dist/runtime/registry/crisp.d.ts +1 -1
  25. package/dist/runtime/registry/fathom-analytics.d.ts +1 -1
  26. package/dist/runtime/registry/google-adsense.d.ts +1 -1
  27. package/dist/runtime/registry/hotjar.d.ts +1 -1
  28. package/dist/runtime/registry/instagram-embed.d.ts +1 -1
  29. package/dist/runtime/registry/intercom.d.ts +1 -1
  30. package/dist/runtime/registry/matomo-analytics.d.ts +1 -1
  31. package/dist/runtime/registry/meta-pixel.d.ts +1 -1
  32. package/dist/runtime/registry/mixpanel-analytics.d.ts +22 -0
  33. package/dist/runtime/registry/mixpanel-analytics.js +46 -0
  34. package/dist/runtime/registry/npm.d.ts +1 -1
  35. package/dist/runtime/registry/reddit-pixel.d.ts +1 -1
  36. package/dist/runtime/registry/schemas.d.ts +19 -0
  37. package/dist/runtime/registry/schemas.js +19 -0
  38. package/dist/runtime/registry/snapchat-pixel.d.ts +1 -1
  39. package/dist/runtime/registry/tiktok-pixel.d.ts +1 -1
  40. package/dist/runtime/registry/vercel-analytics.d.ts +1 -1
  41. package/dist/runtime/registry/x-embed.d.ts +1 -1
  42. package/dist/runtime/registry/x-pixel.d.ts +1 -1
  43. package/dist/runtime/server/proxy-handler.js +36 -23
  44. package/dist/runtime/server/utils/privacy.d.ts +1 -1
  45. package/dist/runtime/server/utils/privacy.js +3 -3
  46. package/dist/runtime/types.d.ts +30 -18
  47. package/dist/runtime/utils/pure.d.ts +1 -1
  48. package/dist/runtime/utils.js +1 -1
  49. package/dist/shared/scripts.ViOoYQXH.mjs +381 -0
  50. package/dist/stats.d.mts +78 -3
  51. package/dist/stats.d.ts +78 -3
  52. package/dist/stats.mjs +2255 -164
  53. package/dist/types-source.mjs +47 -0
  54. package/package.json +13 -13
  55. package/dist/client/_nuxt/BNNMZFwZ.js +0 -162
  56. package/dist/client/_nuxt/builds/meta/78647cef-f45a-4560-82b4-b9364815198a.json +0 -1
  57. package/dist/client/_nuxt/entry.CACgbLJl.css +0 -1
  58. package/dist/client/_nuxt/error-404.DMdWw4vT.css +0 -1
  59. package/dist/client/_nuxt/error-500.CROTF27X.css +0 -1
  60. package/dist/shared/scripts.Crpn87WB.mjs +0 -318
@@ -3,6 +3,7 @@ import type { Script } from '@unhead/vue/types';
3
3
  import type { Import } from 'unimport';
4
4
  import type { InferInput, ObjectSchema, UnionSchema, ValiError } from 'valibot';
5
5
  import type { ComputedRef, Ref } from 'vue';
6
+ import type { BingUetInput } from './registry/bing-uet.js';
6
7
  import type { BlueskyEmbedInput } from './registry/bluesky-embed.js';
7
8
  import type { ClarityInput } from './registry/clarity.js';
8
9
  import type { CloudflareWebAnalyticsInput } from './registry/cloudflare-web-analytics.js';
@@ -17,10 +18,12 @@ import type { GoogleSignInInput } from './registry/google-sign-in.js';
17
18
  import type { GoogleTagManagerInput } from './registry/google-tag-manager.js';
18
19
  import type { GravatarInput } from './registry/gravatar.js';
19
20
  import type { HotjarInput } from './registry/hotjar.js';
21
+ import type { InstagramEmbedInput } from './registry/instagram-embed.js';
20
22
  import type { IntercomInput } from './registry/intercom.js';
21
23
  import type { LemonSqueezyInput } from './registry/lemon-squeezy.js';
22
24
  import type { MatomoAnalyticsInput } from './registry/matomo-analytics.js';
23
25
  import type { MetaPixelInput } from './registry/meta-pixel.js';
26
+ import type { MixpanelAnalyticsInput } from './registry/mixpanel-analytics.js';
24
27
  import type { NpmInput } from './registry/npm.js';
25
28
  import type { PayPalInput } from './registry/paypal.js';
26
29
  import type { PlausibleAnalyticsInput } from './registry/plausible-analytics.js';
@@ -34,6 +37,7 @@ import type { TikTokPixelInput } from './registry/tiktok-pixel.js';
34
37
  import type { UmamiAnalyticsInput } from './registry/umami-analytics.js';
35
38
  import type { VercelAnalyticsInput } from './registry/vercel-analytics.js';
36
39
  import type { VimeoPlayerInput } from './registry/vimeo-player.js';
40
+ import type { XEmbedInput } from './registry/x-embed.js';
37
41
  import type { XPixelInput } from './registry/x-pixel.js';
38
42
  import type { YouTubePlayerInput } from './registry/youtube-player.js';
39
43
  export type WarmupStrategy = false | 'preload' | 'preconnect' | 'dns-prefetch';
@@ -156,13 +160,16 @@ export interface NuxtDevToolsScriptInstance {
156
160
  }[];
157
161
  }
158
162
  export interface ScriptRegistry {
163
+ bingUet?: BingUetInput;
159
164
  blueskyEmbed?: BlueskyEmbedInput;
165
+ carbonAds?: true;
160
166
  crisp?: CrispInput;
161
167
  clarity?: ClarityInput;
162
168
  cloudflareWebAnalytics?: CloudflareWebAnalyticsInput;
163
169
  databuddyAnalytics?: DatabuddyAnalyticsInput;
164
170
  metaPixel?: MetaPixelInput;
165
171
  fathomAnalytics?: FathomAnalyticsInput;
172
+ instagramEmbed?: InstagramEmbedInput;
166
173
  plausibleAnalytics?: PlausibleAnalyticsInput;
167
174
  googleAdsense?: GoogleAdsenseInput;
168
175
  googleAnalytics?: GoogleAnalyticsInput;
@@ -176,11 +183,13 @@ export interface ScriptRegistry {
176
183
  paypal?: PayPalInput;
177
184
  posthog?: PostHogInput;
178
185
  matomoAnalytics?: MatomoAnalyticsInput;
186
+ mixpanelAnalytics?: MixpanelAnalyticsInput;
179
187
  rybbitAnalytics?: RybbitAnalyticsInput;
180
188
  redditPixel?: RedditPixelInput;
181
189
  segment?: SegmentInput;
182
190
  stripe?: StripeInput;
183
191
  tiktokPixel?: TikTokPixelInput;
192
+ xEmbed?: XEmbedInput;
184
193
  xPixel?: XPixelInput;
185
194
  snapchatPixel?: SnapTrPixelInput;
186
195
  youtubePlayer?: YouTubePlayerInput;
@@ -188,9 +197,20 @@ export interface ScriptRegistry {
188
197
  vimeoPlayer?: VimeoPlayerInput;
189
198
  umamiAnalytics?: UmamiAnalyticsInput;
190
199
  gravatar?: GravatarInput;
200
+ npm?: NpmInput;
191
201
  [key: `${string}-npm`]: NpmInput;
192
202
  }
193
- export type NuxtConfigScriptRegistryEntry<T> = true | 'mock' | T | [T, NuxtUseScriptOptionsSerializable];
203
+ /**
204
+ * Built-in registry script keys — not affected by module augmentation.
205
+ * Use this to type-check records that must enumerate all built-in scripts (logos, meta, etc.).
206
+ */
207
+ export type BuiltInRegistryScriptKey = 'bingUet' | 'blueskyEmbed' | 'carbonAds' | 'crisp' | 'clarity' | 'cloudflareWebAnalytics' | 'databuddyAnalytics' | 'metaPixel' | 'fathomAnalytics' | 'instagramEmbed' | 'plausibleAnalytics' | 'googleAdsense' | 'googleAnalytics' | 'googleMaps' | 'googleRecaptcha' | 'googleSignIn' | 'lemonSqueezy' | 'googleTagManager' | 'hotjar' | 'intercom' | 'paypal' | 'posthog' | 'matomoAnalytics' | 'mixpanelAnalytics' | 'rybbitAnalytics' | 'redditPixel' | 'segment' | 'stripe' | 'tiktokPixel' | 'xEmbed' | 'xPixel' | 'snapchatPixel' | 'youtubePlayer' | 'vercelAnalytics' | 'vimeoPlayer' | 'umamiAnalytics' | 'gravatar' | 'npm';
208
+ /**
209
+ * Union of all explicit registry script keys (excludes npm pattern).
210
+ * Includes both built-in and augmented keys.
211
+ */
212
+ export type RegistryScriptKey = Exclude<keyof ScriptRegistry, `${string}-npm`>;
213
+ export type NuxtConfigScriptRegistryEntry<T> = true | false | 'mock' | T | [T, NuxtUseScriptOptionsSerializable];
194
214
  export type NuxtConfigScriptRegistry<T extends keyof ScriptRegistry = keyof ScriptRegistry> = Partial<{
195
215
  [key in T]: NuxtConfigScriptRegistryEntry<ScriptRegistry[key]>;
196
216
  }> & Record<string & {}, NuxtConfigScriptRegistryEntry<any>>;
@@ -201,21 +221,15 @@ declare const _emptyOptions: ObjectSchema<{}, undefined>;
201
221
  export type EmptyOptionsSchema = typeof _emptyOptions;
202
222
  type ScriptInput = Script;
203
223
  export type InferIfSchema<T> = T extends ObjectSchema<any, any> | UnionSchema<any, any> ? InferInput<T> : T;
204
- export type RegistryScriptInput<T = EmptyOptionsSchema, Bundelable extends boolean = true, Usable extends boolean = false, CanBypassOptions extends boolean = true> = (InferIfSchema<T> & {
224
+ export interface RegistryScriptInputExtras<Bundelable extends boolean = true, Usable extends boolean = false> {
205
225
  /**
206
226
  * A unique key to use for the script, this can be used to load multiple of the same script with different options.
207
227
  */
208
228
  key?: string;
209
229
  scriptInput?: ScriptInput;
210
230
  scriptOptions?: Omit<NuxtUseScriptOptions, Bundelable extends true ? '' : 'bundle' | Usable extends true ? '' : 'use'>;
211
- }) | Partial<InferIfSchema<T>> & (CanBypassOptions extends true ? {
212
- /**
213
- * A unique key to use for the script, this can be used to load multiple of the same script with different options.
214
- */
215
- key?: string;
216
- scriptInput: Required<Pick<ScriptInput, 'src'>> & ScriptInput;
217
- scriptOptions?: Omit<NuxtUseScriptOptions, Bundelable extends true ? '' : 'bundle' | Usable extends true ? '' : 'use'>;
218
- } : never);
231
+ }
232
+ export type RegistryScriptInput<T = EmptyOptionsSchema, Bundelable extends boolean = true, Usable extends boolean = false> = Partial<InferIfSchema<T>> & RegistryScriptInputExtras<Bundelable, Usable>;
219
233
  export interface RegistryScriptServerHandler {
220
234
  route: string;
221
235
  handler: string;
@@ -226,20 +240,18 @@ export interface RegistryScript {
226
240
  * The config key used in `scripts.registry` in nuxt.config (e.g., 'googleAnalytics', 'plausibleAnalytics').
227
241
  * Used for direct lookup from config to script — avoids fragile import name convention matching.
228
242
  */
229
- registryKey?: string;
243
+ registryKey?: RegistryScriptKey;
230
244
  import?: Import;
231
245
  scriptBundling?: false | ((options?: any) => string | false);
232
246
  /**
233
- * First-party routing configuration for this script.
234
- * - `string` - The proxy config key to use (e.g., 'googleAnalytics', 'metaPixel')
235
- * - `false` - Explicitly disable first-party routing for this script
236
- * - `undefined` - Use the default key derived from the function name
247
+ * First-party proxy config alias. Only needed when a script shares another script's
248
+ * proxy config (e.g., googleAdsense uses `proxy: 'googleAnalytics'`).
237
249
  *
238
- * When set to a string, the script's URLs will be rewritten and collection
239
- * endpoints will be routed through your server when `scripts.firstParty` is enabled.
250
+ * By default, the proxy config is looked up by `registryKey`. Set to `false` to
251
+ * explicitly disable first-party routing for this script.
240
252
  * @internal
241
253
  */
242
- proxy?: string | false;
254
+ proxy?: RegistryScriptKey | false;
243
255
  label?: string;
244
256
  src?: string | false;
245
257
  category?: string;
@@ -4,6 +4,6 @@
4
4
  export interface ProxyRewrite {
5
5
  /** Domain and path to match (e.g., 'www.google-analytics.com/g/collect') */
6
6
  from: string;
7
- /** Local path to rewrite to (e.g., '/_scripts/c/ga/g/collect') */
7
+ /** Local path to rewrite to (e.g., '/_scripts/p/ga/g/collect') */
8
8
  to: string;
9
9
  }
@@ -32,7 +32,7 @@ export function requireRegistryEndpoint(componentName, registryKey) {
32
32
  }
33
33
  export function useRegistryScript(registryKey, optionsFn, _userOptions) {
34
34
  const scriptConfig = scriptRuntimeConfig(registryKey);
35
- const userOptions = Object.assign(_userOptions || {}, typeof scriptConfig === "object" ? scriptConfig : {});
35
+ const userOptions = defu(_userOptions || {}, typeof scriptConfig === "object" ? scriptConfig : {});
36
36
  const options = optionsFn(userOptions, { scriptInput: userOptions.scriptInput });
37
37
  if (options.scriptMode === "npm") {
38
38
  return createNpmScriptStub({
@@ -0,0 +1,381 @@
1
+ const GA_COLLECT_RE = /([\w$])?"https:\/\/"\+\(.*?\)\+"\.google-analytics\.com\/g\/collect"/g;
2
+ const GA_ANALYTICS_COLLECT_RE = /([\w$])?"https:\/\/"\+\(.*?\)\+"\.analytics\.google\.com\/g\/collect"/g;
3
+ const FATHOM_SELF_HOSTED_RE = /\.src\.indexOf\("cdn\.usefathom\.com"\)\s*<\s*0/;
4
+ const RYBBIT_HOST_SPLIT_RE = /\w+\.split\(["']\/script\.js["']\)\[0\]/g;
5
+ const PRIVACY_NONE = { ip: false, userAgent: false, language: false, screen: false, timezone: false, hardware: false };
6
+ const PRIVACY_FULL = { ip: true, userAgent: true, language: true, screen: true, timezone: true, hardware: true };
7
+ const PRIVACY_HEATMAP = { ip: true, userAgent: false, language: true, screen: false, timezone: false, hardware: true };
8
+ const PRIVACY_IP_ONLY = { ip: true, userAgent: false, language: false, screen: false, timezone: false, hardware: false };
9
+ function deriveRewrites(domains, proxyPrefix) {
10
+ return domains.map(([domain, alias]) => ({
11
+ from: domain,
12
+ to: `${proxyPrefix}/${alias}`
13
+ }));
14
+ }
15
+ function deriveRoutes(domains, proxyPrefix) {
16
+ const routes = {};
17
+ const seen = /* @__PURE__ */ new Set();
18
+ for (const [domain, alias] of domains) {
19
+ if (seen.has(alias))
20
+ continue;
21
+ seen.add(alias);
22
+ routes[`${proxyPrefix}/${alias}/**`] = { proxy: `https://${domain}/**` };
23
+ }
24
+ return routes;
25
+ }
26
+ function fromDomains(domains, proxyPrefix, opts) {
27
+ const { extraRewrites, ...rest } = opts;
28
+ const rewrites = deriveRewrites(domains, proxyPrefix);
29
+ if (extraRewrites) {
30
+ for (const r of extraRewrites)
31
+ rewrites.push({ from: r.from, to: `${proxyPrefix}/${r.to}` });
32
+ }
33
+ return { rewrite: rewrites, routes: deriveRoutes(domains, proxyPrefix), ...rest };
34
+ }
35
+ function buildProxyConfig(proxyPrefix) {
36
+ return {
37
+ googleAnalytics: fromDomains(
38
+ [
39
+ ["www.google-analytics.com", "ga"],
40
+ ["analytics.google.com", "ga"],
41
+ ["stats.g.doubleclick.net", "ga-dc"],
42
+ ["pagead2.googlesyndication.com", "ga-syn"],
43
+ ["www.googleadservices.com", "ga-ads"],
44
+ ["googleads.g.doubleclick.net", "ga-gads"]
45
+ ],
46
+ proxyPrefix,
47
+ {
48
+ // GA4: screen/timezone/UA needed for device, time, and OS reports; rest anonymized safely
49
+ privacy: PRIVACY_HEATMAP,
50
+ extraRewrites: [
51
+ // Modern gtag.js uses www.google.com/g/collect
52
+ { from: "www.google.com/g/collect", to: "ga/g/collect" },
53
+ // Suffix patterns for dynamically constructed URLs
54
+ { from: ".google-analytics.com/g/collect", to: "ga/g/collect" },
55
+ { from: ".analytics.google.com/g/collect", to: "ga/g/collect" },
56
+ // Full domain + path patterns
57
+ { from: "www.google-analytics.com/g/collect", to: "ga/g/collect" },
58
+ { from: "analytics.google.com/g/collect", to: "ga/g/collect" },
59
+ // DoubleClick collect endpoint
60
+ { from: "stats.g.doubleclick.net/g/collect", to: "ga/g/collect" }
61
+ ],
62
+ // GA4 dynamically constructs collect URLs via string concatenation that can't be
63
+ // caught by AST rewriting. These regex patches handle the remaining patterns.
64
+ postProcess(output, rewrites) {
65
+ const gaRewrite = rewrites.find((r) => r.from.includes("google-analytics.com/g/collect"));
66
+ if (gaRewrite) {
67
+ output = output.replace(
68
+ GA_COLLECT_RE,
69
+ (_, prevChar) => `${prevChar ? `${prevChar} ` : ""}self.location.origin+"${gaRewrite.to}"`
70
+ );
71
+ output = output.replace(
72
+ GA_ANALYTICS_COLLECT_RE,
73
+ (_, prevChar) => `${prevChar ? `${prevChar} ` : ""}self.location.origin+"${gaRewrite.to}"`
74
+ );
75
+ }
76
+ return output;
77
+ }
78
+ }
79
+ ),
80
+ googleTagManager: fromDomains(
81
+ [["www.googletagmanager.com", "gtm"]],
82
+ proxyPrefix,
83
+ { privacy: PRIVACY_NONE }
84
+ ),
85
+ metaPixel: fromDomains(
86
+ [
87
+ ["connect.facebook.net", "meta"],
88
+ ["www.facebook.com/tr", "meta-tr"],
89
+ ["facebook.com/tr", "meta-tr"],
90
+ ["pixel.facebook.com", "meta-px"],
91
+ ["www.facebook.com/plugins", "meta-plugins"]
92
+ ],
93
+ proxyPrefix,
94
+ { privacy: PRIVACY_FULL }
95
+ ),
96
+ tiktokPixel: fromDomains(
97
+ [["analytics.tiktok.com", "tiktok"]],
98
+ proxyPrefix,
99
+ { privacy: PRIVACY_FULL }
100
+ ),
101
+ segment: fromDomains(
102
+ [
103
+ ["api.segment.io", "segment"],
104
+ ["cdn.segment.com", "segment-cdn"]
105
+ ],
106
+ proxyPrefix,
107
+ { privacy: PRIVACY_NONE }
108
+ ),
109
+ xPixel: fromDomains(
110
+ [
111
+ ["analytics.twitter.com", "x"],
112
+ ["static.ads-twitter.com", "x-ads"],
113
+ ["t.co", "x-t"]
114
+ ],
115
+ proxyPrefix,
116
+ { privacy: PRIVACY_FULL }
117
+ ),
118
+ snapchatPixel: fromDomains(
119
+ [
120
+ ["sc-static.net", "snap-cdn"],
121
+ ["tr.snapchat.com", "snap"],
122
+ ["pixel.tapad.com", "snap-tapad"]
123
+ ],
124
+ proxyPrefix,
125
+ { privacy: PRIVACY_FULL }
126
+ ),
127
+ redditPixel: fromDomains(
128
+ [
129
+ ["www.redditstatic.com", "reddit-cdn"],
130
+ ["alb.reddit.com", "reddit"],
131
+ ["pixel-config.reddit.com", "reddit-cfg"]
132
+ ],
133
+ proxyPrefix,
134
+ { privacy: PRIVACY_FULL }
135
+ ),
136
+ clarity: fromDomains(
137
+ [
138
+ ["www.clarity.ms", "clarity"],
139
+ ["scripts.clarity.ms", "clarity-scripts"],
140
+ ["d.clarity.ms", "clarity-data"],
141
+ ["e.clarity.ms", "clarity-events"],
142
+ ["k.clarity.ms", "clarity-collect"]
143
+ ],
144
+ proxyPrefix,
145
+ { privacy: PRIVACY_HEATMAP }
146
+ ),
147
+ posthog: {
148
+ // No rewrites needed - PostHog uses NPM mode, SDK URLs are set via api_host config
149
+ privacy: PRIVACY_NONE,
150
+ routes: {
151
+ // US region
152
+ [`${proxyPrefix}/ph/static/**`]: { proxy: "https://us-assets.i.posthog.com/static/**" },
153
+ [`${proxyPrefix}/ph/**`]: { proxy: "https://us.i.posthog.com/**" },
154
+ // EU region
155
+ [`${proxyPrefix}/ph-eu/static/**`]: { proxy: "https://eu-assets.i.posthog.com/static/**" },
156
+ [`${proxyPrefix}/ph-eu/**`]: { proxy: "https://eu.i.posthog.com/**" }
157
+ },
158
+ autoInject: {
159
+ configField: "apiHost",
160
+ computeValue: (proxyPrefix2, config) => {
161
+ const region = config.region || "us";
162
+ return region === "eu" ? `${proxyPrefix2}/ph-eu` : `${proxyPrefix2}/ph`;
163
+ }
164
+ }
165
+ },
166
+ hotjar: fromDomains(
167
+ [
168
+ ["static.hotjar.com", "hotjar"],
169
+ ["script.hotjar.com", "hotjar-script"],
170
+ ["vars.hotjar.com", "hotjar-vars"],
171
+ ["in.hotjar.com", "hotjar-in"],
172
+ ["vc.hotjar.com", "hotjar-vc"],
173
+ ["vc.hotjar.io", "hotjar-vc"],
174
+ ["metrics.hotjar.io", "hotjar-metrics"],
175
+ ["insights.hotjar.com", "hotjar-insights"],
176
+ ["ask.hotjar.io", "hotjar-ask"],
177
+ ["events.hotjar.io", "hotjar-events"],
178
+ ["identify.hotjar.com", "hotjar-identify"],
179
+ ["surveystats.hotjar.io", "hotjar-surveys"]
180
+ ],
181
+ proxyPrefix,
182
+ { privacy: PRIVACY_HEATMAP }
183
+ ),
184
+ plausibleAnalytics: fromDomains(
185
+ [["plausible.io", "plausible"]],
186
+ proxyPrefix,
187
+ {
188
+ privacy: PRIVACY_NONE,
189
+ autoInject: {
190
+ configField: "endpoint",
191
+ computeValue: (proxyPrefix2) => `${proxyPrefix2}/plausible/api/event`
192
+ }
193
+ }
194
+ ),
195
+ cloudflareWebAnalytics: fromDomains(
196
+ [
197
+ ["static.cloudflareinsights.com", "cfwa"],
198
+ ["cloudflareinsights.com", "cfwa-beacon"]
199
+ ],
200
+ proxyPrefix,
201
+ { privacy: PRIVACY_NONE }
202
+ ),
203
+ rybbitAnalytics: fromDomains(
204
+ [["app.rybbit.io", "rybbit"]],
205
+ proxyPrefix,
206
+ {
207
+ privacy: PRIVACY_NONE,
208
+ autoInject: {
209
+ configField: "analyticsHost",
210
+ computeValue: (proxyPrefix2) => `${proxyPrefix2}/rybbit/api`
211
+ },
212
+ // Rybbit SDK derives API host via `e.split("/script.js")[0]` from the script tag's
213
+ // src attribute. When bundled, src becomes /_scripts/assets/<hash>.js so the split fails.
214
+ postProcess(output, rewrites) {
215
+ const rybbitRewrite = rewrites.find((r) => r.from === "app.rybbit.io");
216
+ if (rybbitRewrite) {
217
+ output = output.replace(
218
+ RYBBIT_HOST_SPLIT_RE,
219
+ `self.location.origin+"${rybbitRewrite.to}/api"`
220
+ );
221
+ }
222
+ return output;
223
+ }
224
+ }
225
+ ),
226
+ umamiAnalytics: fromDomains(
227
+ [["cloud.umami.is", "umami"]],
228
+ proxyPrefix,
229
+ {
230
+ privacy: PRIVACY_NONE,
231
+ extraRewrites: [
232
+ { from: "api-gateway.umami.dev", to: "umami" }
233
+ ],
234
+ autoInject: {
235
+ configField: "hostUrl",
236
+ computeValue: (proxyPrefix2) => `${proxyPrefix2}/umami`
237
+ }
238
+ }
239
+ ),
240
+ databuddyAnalytics: fromDomains(
241
+ [
242
+ ["cdn.databuddy.cc", "databuddy"],
243
+ ["basket.databuddy.cc", "databuddy-api"]
244
+ ],
245
+ proxyPrefix,
246
+ {
247
+ privacy: PRIVACY_NONE,
248
+ autoInject: {
249
+ configField: "apiUrl",
250
+ computeValue: (proxyPrefix2) => `${proxyPrefix2}/databuddy-api`
251
+ }
252
+ }
253
+ ),
254
+ fathomAnalytics: fromDomains(
255
+ [["cdn.usefathom.com", "fathom"]],
256
+ proxyPrefix,
257
+ {
258
+ privacy: PRIVACY_NONE,
259
+ // Fathom SDK checks if script src contains "cdn.usefathom.com" to detect self-hosted
260
+ // mode, then overrides trackerUrl with the script host's root. After AST rewrite already
261
+ // set trackerUrl to the proxy URL, neutralize this check so it doesn't override it.
262
+ postProcess(output) {
263
+ return output.replace(
264
+ FATHOM_SELF_HOSTED_RE,
265
+ '.src.indexOf("cdn.usefathom.com")<-1'
266
+ );
267
+ }
268
+ }
269
+ ),
270
+ intercom: fromDomains(
271
+ [
272
+ ["widget.intercom.io", "intercom"],
273
+ ["api-iam.intercom.io", "intercom-api"],
274
+ ["api-iam.eu.intercom.io", "intercom-api-eu"],
275
+ ["api-iam.au.intercom.io", "intercom-api-au"],
276
+ ["js.intercomcdn.com", "intercom-cdn"],
277
+ ["downloads.intercomcdn.com", "intercom-downloads"],
278
+ ["video-messages.intercomcdn.com", "intercom-video"]
279
+ ],
280
+ proxyPrefix,
281
+ { privacy: PRIVACY_IP_ONLY }
282
+ ),
283
+ crisp: fromDomains(
284
+ [
285
+ ["client.crisp.chat", "crisp"],
286
+ ["client.relay.crisp.chat", "crisp-relay"],
287
+ ["assets.crisp.chat", "crisp-assets"],
288
+ ["go.crisp.chat", "crisp-go"],
289
+ ["image.crisp.chat", "crisp-image"]
290
+ ],
291
+ proxyPrefix,
292
+ { privacy: PRIVACY_IP_ONLY }
293
+ ),
294
+ vercelAnalytics: fromDomains(
295
+ [["va.vercel-scripts.com", "vercel"]],
296
+ proxyPrefix,
297
+ { privacy: PRIVACY_NONE }
298
+ ),
299
+ gravatar: fromDomains(
300
+ [
301
+ ["secure.gravatar.com", "gravatar"],
302
+ ["gravatar.com/avatar", "gravatar-avatar"]
303
+ ],
304
+ proxyPrefix,
305
+ { privacy: PRIVACY_IP_ONLY }
306
+ ),
307
+ carbonAds: fromDomains(
308
+ [["cdn.carbonads.com", "carbon"]],
309
+ proxyPrefix,
310
+ { privacy: PRIVACY_NONE }
311
+ ),
312
+ lemonSqueezy: fromDomains(
313
+ [["assets.lemonsqueezy.com", "lemonsqueezy"]],
314
+ proxyPrefix,
315
+ { privacy: PRIVACY_NONE }
316
+ ),
317
+ matomoAnalytics: fromDomains(
318
+ [["cdn.matomo.cloud", "matomo"]],
319
+ proxyPrefix,
320
+ { privacy: PRIVACY_NONE }
321
+ ),
322
+ stripe: fromDomains(
323
+ [["js.stripe.com", "stripe"]],
324
+ proxyPrefix,
325
+ { privacy: PRIVACY_IP_ONLY }
326
+ ),
327
+ paypal: fromDomains(
328
+ [["www.paypal.com", "paypal"]],
329
+ proxyPrefix,
330
+ { privacy: PRIVACY_IP_ONLY }
331
+ ),
332
+ youtubePlayer: fromDomains(
333
+ [["www.youtube.com", "youtube"]],
334
+ proxyPrefix,
335
+ { privacy: PRIVACY_IP_ONLY }
336
+ ),
337
+ vimeoPlayer: fromDomains(
338
+ [["player.vimeo.com", "vimeo"]],
339
+ proxyPrefix,
340
+ { privacy: PRIVACY_IP_ONLY }
341
+ ),
342
+ googleRecaptcha: {
343
+ privacy: PRIVACY_IP_ONLY,
344
+ rewrite: [
345
+ { from: "www.gstatic.com", to: `${proxyPrefix}/gstatic` },
346
+ // www.google.com is shared with GA — only rewrite /recaptcha paths
347
+ { from: "www.google.com/recaptcha", to: `${proxyPrefix}/grecaptcha` },
348
+ { from: "www.recaptcha.net/recaptcha", to: `${proxyPrefix}/grecaptcha` }
349
+ ],
350
+ routes: {
351
+ [`${proxyPrefix}/gstatic/**`]: { proxy: "https://www.gstatic.com/**" },
352
+ [`${proxyPrefix}/grecaptcha/**`]: { proxy: "https://www.google.com/recaptcha/**" }
353
+ }
354
+ },
355
+ googleSignIn: fromDomains(
356
+ [["accounts.google.com", "gsignin"]],
357
+ proxyPrefix,
358
+ { privacy: PRIVACY_IP_ONLY }
359
+ )
360
+ };
361
+ }
362
+ function getAllProxyConfigs(proxyPrefix) {
363
+ return buildProxyConfig(proxyPrefix);
364
+ }
365
+ const PROXY_URL_RE = /^https?:\/\/([^/]+)(\/.*)?\/\*\*$/;
366
+ const ROUTE_WILDCARD_RE = /\/\*\*$/;
367
+ function routesToInterceptRules(routes) {
368
+ const rules = [];
369
+ for (const [localPath, { proxy }] of Object.entries(routes)) {
370
+ const match = proxy.match(PROXY_URL_RE);
371
+ if (match?.[1]) {
372
+ const domain = match[1];
373
+ const pathPrefix = match[2] || "";
374
+ const target = localPath.replace(ROUTE_WILDCARD_RE, "");
375
+ rules.push({ pattern: domain, pathPrefix, target });
376
+ }
377
+ }
378
+ return rules;
379
+ }
380
+
381
+ export { getAllProxyConfigs as g, routesToInterceptRules as r };
package/dist/stats.d.mts CHANGED
@@ -17,6 +17,74 @@ interface ScriptSizeDetail {
17
17
  initiatorType: string;
18
18
  protocol: string;
19
19
  }
20
+ interface ScriptApis {
21
+ cookies: boolean;
22
+ localStorage: boolean;
23
+ sessionStorage: boolean;
24
+ indexedDB: boolean;
25
+ canvas: boolean;
26
+ webgl: boolean;
27
+ audioContext: boolean;
28
+ userAgent: boolean;
29
+ hardwareConcurrency: boolean;
30
+ deviceMemory: boolean;
31
+ plugins: boolean;
32
+ languages: boolean;
33
+ screen: boolean;
34
+ sendBeacon: boolean;
35
+ fetch: boolean;
36
+ xhr: boolean;
37
+ websocket: boolean;
38
+ mutationObserver: boolean;
39
+ performanceObserver: boolean;
40
+ intersectionObserver: boolean;
41
+ }
42
+ interface ApiPrivacyScore {
43
+ /** 0–100, higher = more invasive */
44
+ score: number;
45
+ /** Persistence APIs used (cookies, localStorage, sessionStorage, indexedDB) */
46
+ persistence: number;
47
+ /** Fingerprinting APIs used (canvas, webgl, audioContext, deviceMemory, hardwareConcurrency, plugins, screen, userAgent, languages) */
48
+ fingerprinting: number;
49
+ /** Behavioral monitoring APIs used (mutationObserver, intersectionObserver) */
50
+ monitoring: number;
51
+ }
52
+ interface ScriptCookie {
53
+ name: string;
54
+ domain: string;
55
+ path: string;
56
+ httpOnly: boolean;
57
+ secure: boolean;
58
+ sameSite: string;
59
+ session: boolean;
60
+ lifetimeDays: number;
61
+ firstParty: boolean;
62
+ }
63
+ interface NetworkSummary {
64
+ requestCount: number;
65
+ domains: string[];
66
+ outboundBytes: number;
67
+ inboundBytes: number;
68
+ injectedElements: {
69
+ tag: string;
70
+ src: string;
71
+ }[];
72
+ }
73
+ interface CwvEstimate {
74
+ /** Estimated LCP delay: scriptDurationMs blocks rendering during page load */
75
+ lcpImpactMs: number;
76
+ /** CLS risk: true if script injects visible DOM elements (iframes, images) without reserved space */
77
+ clsRisk: boolean;
78
+ /** Number of visible elements injected that could cause layout shifts */
79
+ clsElements: number;
80
+ /** INP risk level based on script weight + DOM observation */
81
+ inpRiskLevel: 'low' | 'medium' | 'high';
82
+ }
83
+ interface PerformanceSummary {
84
+ taskDurationMs: number;
85
+ scriptDurationMs: number;
86
+ heapDeltaKb: number;
87
+ }
20
88
  interface ScriptStats {
21
89
  id: string;
22
90
  label: string;
@@ -25,10 +93,17 @@ interface ScriptStats {
25
93
  totalTransferKb: number;
26
94
  totalDecodedKb: number;
27
95
  trackedData: TrackedDataType[];
96
+ collectsWebVitals: boolean;
97
+ apis: ScriptApis;
98
+ apiPrivacyScore: ApiPrivacyScore;
99
+ cookies: ScriptCookie[];
100
+ network: NetworkSummary;
101
+ performance: PerformanceSummary;
102
+ cwvEstimate: CwvEstimate;
28
103
  hasBundling: boolean;
29
104
  hasProxy: boolean;
30
- domains: string[];
31
- endpoints: number;
105
+ proxyDomains: string[];
106
+ proxyEndpoints: number;
32
107
  privacy: ScriptPrivacy | null;
33
108
  privacyLevel: 'full' | 'partial' | 'none' | 'unknown';
34
109
  loadingMethod: 'cdn' | 'npm' | 'dynamic';
@@ -36,4 +111,4 @@ interface ScriptStats {
36
111
  declare function getScriptStats(): Promise<ScriptStats[]>;
37
112
 
38
113
  export { getScriptStats };
39
- export type { ScriptPrivacy, ScriptSizeDetail, ScriptStats, TrackedDataType };
114
+ export type { ApiPrivacyScore, CwvEstimate, NetworkSummary, PerformanceSummary, ScriptApis, ScriptCookie, ScriptPrivacy, ScriptSizeDetail, ScriptStats, TrackedDataType };