@nuxt/scripts 1.0.0-beta.3 → 1.0.0-beta.30

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 (217) hide show
  1. package/README.md +3 -3
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/{Ds2G8aQM.js → AwAKM0sG.js} +1 -1
  5. package/dist/client/_nuxt/Bl23o3st.js +162 -0
  6. package/dist/client/_nuxt/{DdVDSbUA.js → CYlYSSNW.js} +1 -1
  7. package/dist/client/_nuxt/{CD5B-xvT.js → D5FIkDae.js} +1 -1
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/f0b4dd20-8496-4003-b7a3-05cbae515923.json +1 -0
  10. package/dist/client/_nuxt/entry.C5SUNdim.css +1 -0
  11. package/dist/client/_nuxt/error-404.1K8v8Su2.css +1 -0
  12. package/dist/client/_nuxt/error-500.B9qvKpQm.css +1 -0
  13. package/dist/client/index.html +1 -1
  14. package/dist/module.d.mts +7 -19
  15. package/dist/module.d.ts +164 -0
  16. package/dist/module.json +1 -1
  17. package/dist/module.mjs +935 -645
  18. package/dist/registry.d.ts +6 -0
  19. package/dist/registry.mjs +235 -69
  20. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +16 -9
  21. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +57 -30
  22. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +16 -9
  23. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.d.vue.ts +22 -39
  24. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue +69 -72
  25. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue.d.ts +22 -39
  26. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.d.vue.ts +5 -1
  27. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.vue +25 -38
  28. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.vue.d.ts +5 -1
  29. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsGeoJson.d.vue.ts +43 -0
  30. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsGeoJson.vue +61 -0
  31. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsGeoJson.vue.d.ts +43 -0
  32. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.d.vue.ts +4 -0
  33. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue +22 -26
  34. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue.d.ts +4 -0
  35. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsInfoWindow.d.vue.ts +9 -5
  36. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsInfoWindow.vue +62 -53
  37. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsInfoWindow.vue.d.ts +9 -5
  38. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.d.vue.ts +26 -11
  39. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.vue +48 -45
  40. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.vue.d.ts +26 -11
  41. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.d.vue.ts +10 -2
  42. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue +38 -37
  43. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue.d.ts +10 -2
  44. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.d.vue.ts +63 -0
  45. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue +160 -0
  46. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue.d.ts +63 -0
  47. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.d.vue.ts +4 -0
  48. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.vue +23 -32
  49. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.vue.d.ts +4 -0
  50. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolygon.d.vue.ts +7 -3
  51. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolygon.vue +24 -38
  52. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolygon.vue.d.ts +7 -3
  53. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolyline.d.vue.ts +7 -3
  54. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolyline.vue +24 -38
  55. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolyline.vue.d.ts +7 -3
  56. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsRectangle.d.vue.ts +7 -3
  57. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsRectangle.vue +25 -38
  58. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsRectangle.vue.d.ts +7 -3
  59. package/dist/runtime/components/GoogleMaps/bindGoogleMapsEvents.d.ts +13 -0
  60. package/dist/runtime/components/GoogleMaps/bindGoogleMapsEvents.js +8 -0
  61. package/dist/runtime/components/GoogleMaps/injectionKeys.d.ts +13 -0
  62. package/dist/runtime/components/GoogleMaps/injectionKeys.js +3 -0
  63. package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.d.ts +26 -0
  64. package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.js +42 -0
  65. package/dist/runtime/components/ScriptBlueskyEmbed.d.vue.ts +87 -0
  66. package/dist/runtime/components/ScriptBlueskyEmbed.vue +85 -0
  67. package/dist/runtime/components/ScriptBlueskyEmbed.vue.d.ts +87 -0
  68. package/dist/runtime/components/ScriptCrisp.vue +1 -1
  69. package/dist/runtime/components/ScriptGoogleAdsense.vue +1 -1
  70. package/dist/runtime/components/ScriptGravatar.d.vue.ts +22 -0
  71. package/dist/runtime/components/ScriptGravatar.vue +46 -0
  72. package/dist/runtime/components/ScriptGravatar.vue.d.ts +22 -0
  73. package/dist/runtime/components/ScriptInstagramEmbed.d.vue.ts +2 -2
  74. package/dist/runtime/components/ScriptInstagramEmbed.vue +5 -2
  75. package/dist/runtime/components/ScriptInstagramEmbed.vue.d.ts +2 -2
  76. package/dist/runtime/components/ScriptIntercom.vue +4 -3
  77. package/dist/runtime/components/ScriptPayPalButtons.d.vue.ts +43 -32
  78. package/dist/runtime/components/ScriptPayPalButtons.vue +48 -79
  79. package/dist/runtime/components/ScriptPayPalButtons.vue.d.ts +43 -32
  80. package/dist/runtime/components/ScriptPayPalMessages.d.vue.ts +37 -23
  81. package/dist/runtime/components/ScriptPayPalMessages.vue +46 -50
  82. package/dist/runtime/components/ScriptPayPalMessages.vue.d.ts +37 -23
  83. package/dist/runtime/components/ScriptStripePricingTable.vue +2 -2
  84. package/dist/runtime/components/ScriptVimeoPlayer.d.vue.ts +9 -0
  85. package/dist/runtime/components/ScriptVimeoPlayer.vue +13 -10
  86. package/dist/runtime/components/ScriptVimeoPlayer.vue.d.ts +9 -0
  87. package/dist/runtime/components/ScriptXEmbed.d.vue.ts +2 -2
  88. package/dist/runtime/components/ScriptXEmbed.vue +6 -3
  89. package/dist/runtime/components/ScriptXEmbed.vue.d.ts +2 -2
  90. package/dist/runtime/components/ScriptYouTubePlayer.d.vue.ts +2 -2
  91. package/dist/runtime/components/ScriptYouTubePlayer.vue +11 -5
  92. package/dist/runtime/components/ScriptYouTubePlayer.vue.d.ts +2 -2
  93. package/dist/runtime/composables/useScript.js +13 -6
  94. package/dist/runtime/composables/useScriptEventPage.js +2 -2
  95. package/dist/runtime/composables/useScriptTriggerConsent.d.ts +10 -0
  96. package/dist/runtime/composables/useScriptTriggerConsent.js +33 -20
  97. package/dist/runtime/composables/useScriptTriggerElement.js +1 -1
  98. package/dist/runtime/composables/useScriptTriggerIdleTimeout.js +1 -1
  99. package/dist/runtime/registry/bing-uet.d.ts +20 -0
  100. package/dist/runtime/registry/bing-uet.js +29 -0
  101. package/dist/runtime/registry/bluesky-embed.d.ts +116 -0
  102. package/dist/runtime/registry/bluesky-embed.js +72 -0
  103. package/dist/runtime/registry/clarity.d.ts +10 -15
  104. package/dist/runtime/registry/clarity.js +22 -31
  105. package/dist/runtime/registry/cloudflare-web-analytics.d.ts +2 -13
  106. package/dist/runtime/registry/cloudflare-web-analytics.js +2 -14
  107. package/dist/runtime/registry/crisp.d.ts +10 -40
  108. package/dist/runtime/registry/crisp.js +2 -33
  109. package/dist/runtime/registry/databuddy-analytics.d.ts +2 -35
  110. package/dist/runtime/registry/databuddy-analytics.js +20 -45
  111. package/dist/runtime/registry/fathom-analytics.d.ts +7 -26
  112. package/dist/runtime/registry/fathom-analytics.js +2 -24
  113. package/dist/runtime/registry/google-adsense.d.ts +3 -11
  114. package/dist/runtime/registry/google-adsense.js +2 -11
  115. package/dist/runtime/registry/google-analytics.d.ts +3 -5
  116. package/dist/runtime/registry/google-analytics.js +3 -8
  117. package/dist/runtime/registry/google-maps.d.ts +3 -9
  118. package/dist/runtime/registry/google-maps.js +2 -8
  119. package/dist/runtime/registry/google-recaptcha.d.ts +2 -6
  120. package/dist/runtime/registry/google-recaptcha.js +4 -12
  121. package/dist/runtime/registry/google-sign-in.d.ts +2 -13
  122. package/dist/runtime/registry/google-sign-in.js +2 -22
  123. package/dist/runtime/registry/google-tag-manager.d.ts +3 -28
  124. package/dist/runtime/registry/google-tag-manager.js +4 -27
  125. package/dist/runtime/registry/gravatar.d.ts +26 -0
  126. package/dist/runtime/registry/gravatar.js +33 -0
  127. package/dist/runtime/registry/hotjar.d.ts +4 -6
  128. package/dist/runtime/registry/hotjar.js +2 -5
  129. package/dist/runtime/registry/instagram-embed.d.ts +3 -18
  130. package/dist/runtime/registry/instagram-embed.js +4 -19
  131. package/dist/runtime/registry/intercom.d.ts +4 -12
  132. package/dist/runtime/registry/intercom.js +2 -12
  133. package/dist/runtime/registry/matomo-analytics.d.ts +3 -12
  134. package/dist/runtime/registry/matomo-analytics.js +3 -12
  135. package/dist/runtime/registry/meta-pixel.d.ts +4 -6
  136. package/dist/runtime/registry/meta-pixel.js +2 -4
  137. package/dist/runtime/registry/mixpanel-analytics.d.ts +22 -0
  138. package/dist/runtime/registry/mixpanel-analytics.js +46 -0
  139. package/dist/runtime/registry/npm.d.ts +3 -7
  140. package/dist/runtime/registry/npm.js +2 -9
  141. package/dist/runtime/registry/paypal.d.ts +4 -25
  142. package/dist/runtime/registry/paypal.js +3 -66
  143. package/dist/runtime/registry/plausible-analytics.js +18 -13
  144. package/dist/runtime/registry/posthog.d.ts +10 -12
  145. package/dist/runtime/registry/posthog.js +7 -14
  146. package/dist/runtime/registry/reddit-pixel.d.ts +5 -6
  147. package/dist/runtime/registry/reddit-pixel.js +2 -4
  148. package/dist/runtime/registry/rybbit-analytics.d.ts +2 -14
  149. package/dist/runtime/registry/rybbit-analytics.js +10 -20
  150. package/dist/runtime/registry/schemas.d.ts +982 -0
  151. package/dist/runtime/registry/schemas.js +937 -0
  152. package/dist/runtime/registry/segment.d.ts +2 -5
  153. package/dist/runtime/registry/segment.js +2 -5
  154. package/dist/runtime/registry/snapchat-pixel.d.ts +4 -33
  155. package/dist/runtime/registry/snapchat-pixel.js +2 -20
  156. package/dist/runtime/registry/stripe.d.ts +3 -4
  157. package/dist/runtime/registry/stripe.js +2 -4
  158. package/dist/runtime/registry/tiktok-pixel.d.ts +4 -7
  159. package/dist/runtime/registry/tiktok-pixel.js +2 -6
  160. package/dist/runtime/registry/umami-analytics.d.ts +2 -31
  161. package/dist/runtime/registry/umami-analytics.js +2 -36
  162. package/dist/runtime/registry/vercel-analytics.d.ts +29 -0
  163. package/dist/runtime/registry/vercel-analytics.js +84 -0
  164. package/dist/runtime/registry/vimeo-player.d.ts +2 -2
  165. package/dist/runtime/registry/vimeo-player.js +1 -1
  166. package/dist/runtime/registry/x-embed.d.ts +3 -17
  167. package/dist/runtime/registry/x-embed.js +3 -18
  168. package/dist/runtime/registry/x-pixel.d.ts +4 -7
  169. package/dist/runtime/registry/x-pixel.js +2 -5
  170. package/dist/runtime/registry/youtube-player.d.ts +7 -7
  171. package/dist/runtime/registry/youtube-player.js +1 -1
  172. package/dist/runtime/server/{sw-handler.d.ts → bluesky-embed-image.d.ts} +1 -1
  173. package/dist/runtime/server/bluesky-embed-image.js +7 -0
  174. package/dist/runtime/server/bluesky-embed.d.ts +16 -0
  175. package/dist/runtime/server/bluesky-embed.js +59 -0
  176. package/dist/runtime/server/google-maps-geocode-proxy.d.ts +2 -0
  177. package/dist/runtime/server/google-maps-geocode-proxy.js +34 -0
  178. package/dist/runtime/server/google-static-maps-proxy.js +2 -13
  179. package/dist/runtime/server/gravatar-proxy.d.ts +2 -0
  180. package/dist/runtime/server/gravatar-proxy.js +46 -0
  181. package/dist/runtime/server/instagram-embed-asset.js +8 -41
  182. package/dist/runtime/server/instagram-embed-image.js +6 -53
  183. package/dist/runtime/server/instagram-embed.d.ts +16 -0
  184. package/dist/runtime/server/instagram-embed.js +173 -35
  185. package/dist/runtime/server/proxy-handler.js +134 -93
  186. package/dist/runtime/server/utils/image-proxy.d.ts +12 -0
  187. package/dist/runtime/server/utils/image-proxy.js +70 -0
  188. package/dist/runtime/server/utils/privacy.d.ts +1 -2
  189. package/dist/runtime/server/utils/privacy.js +54 -34
  190. package/dist/runtime/server/x-embed-image.js +5 -49
  191. package/dist/runtime/server/x-embed.js +3 -2
  192. package/dist/runtime/types.d.ts +74 -40
  193. package/dist/runtime/utils/pure.d.ts +1 -5
  194. package/dist/runtime/utils/pure.js +0 -67
  195. package/dist/runtime/utils.d.ts +4 -3
  196. package/dist/runtime/utils.js +24 -10
  197. package/dist/shared/scripts.ViOoYQXH.mjs +381 -0
  198. package/dist/stats.d.mts +202 -0
  199. package/dist/stats.d.ts +202 -0
  200. package/dist/stats.mjs +3868 -0
  201. package/dist/types-source.d.mts +17 -0
  202. package/dist/types-source.d.ts +17 -0
  203. package/dist/types-source.mjs +3600 -0
  204. package/package.json +52 -38
  205. package/dist/client/_nuxt/D-kOnTuH.js +0 -162
  206. package/dist/client/_nuxt/builds/meta/f1474569-6922-450d-bc3f-4fd5f3e1391a.json +0 -1
  207. package/dist/client/_nuxt/entry.D45OuV0w.css +0 -1
  208. package/dist/client/_nuxt/error-404.B57D-jUQ.css +0 -1
  209. package/dist/client/_nuxt/error-500.DTHUW7BI.css +0 -1
  210. package/dist/runtime/components/ScriptPayPalMarks.d.vue.ts +0 -52
  211. package/dist/runtime/components/ScriptPayPalMarks.vue +0 -69
  212. package/dist/runtime/components/ScriptPayPalMarks.vue.d.ts +0 -52
  213. package/dist/runtime/plugins/sw-register.client.d.ts +0 -2
  214. package/dist/runtime/plugins/sw-register.client.js +0 -12
  215. package/dist/runtime/server/sw-handler.js +0 -25
  216. package/dist/runtime/sw/proxy-sw.template.d.ts +0 -1
  217. package/dist/runtime/sw/proxy-sw.template.js +0 -54
@@ -1,17 +1,28 @@
1
- import { defineEventHandler, getHeaders, getRequestIP, readBody, getQuery, setResponseHeader, createError } from "h3";
2
1
  import { useRuntimeConfig } from "#imports";
3
- import { useStorage, useNitroApp } from "nitropack/runtime";
4
- import { hash } from "ohash";
5
- import { rewriteScriptUrls } from "../utils/pure.js";
2
+ import { createError, defineEventHandler, getHeaders, getQuery, getRequestIP, getRequestWebStream, readBody, setResponseHeader } from "h3";
3
+ import { useNitroApp } from "nitropack/runtime";
6
4
  import {
7
- SENSITIVE_HEADERS,
8
5
  anonymizeIP,
6
+ mergePrivacy,
9
7
  normalizeLanguage,
10
8
  normalizeUserAgent,
11
- stripPayloadFingerprinting,
12
9
  resolvePrivacy,
13
- mergePrivacy
10
+ SENSITIVE_HEADERS,
11
+ stripPayloadFingerprinting
14
12
  } from "./utils/privacy.js";
13
+ const COMPRESSION_RE = /gzip|deflate|br|compress|base64/i;
14
+ const ROUTE_WILDCARD_RE = /\/\*\*$/;
15
+ const CLIENT_HINT_VERSION_RE = /;v="(\d+)\.[^"]*"/g;
16
+ const SKIP_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["set-cookie", "transfer-encoding", "content-encoding", "content-length"]);
17
+ let sortedRoutesCache;
18
+ function getSortedRoutes(routes) {
19
+ const key = JSON.stringify(routes);
20
+ if (sortedRoutesCache?.key === key)
21
+ return sortedRoutesCache.sorted;
22
+ const sorted = Object.entries(routes).sort((a, b) => b[0].length - a[0].length);
23
+ sortedRoutesCache = { key, sorted };
24
+ return sorted;
25
+ }
15
26
  function stripQueryFingerprinting(query, privacy) {
16
27
  const stripped = stripPayloadFingerprinting(query, privacy);
17
28
  const params = new URLSearchParams();
@@ -20,11 +31,10 @@ function stripQueryFingerprinting(query, privacy) {
20
31
  params.set(key, typeof value === "object" ? JSON.stringify(value) : String(value));
21
32
  }
22
33
  }
23
- return params.toString();
34
+ return { queryString: params.toString(), stripped };
24
35
  }
25
36
  export default defineEventHandler(async (event) => {
26
37
  const config = useRuntimeConfig();
27
- const nitro = useNitroApp();
28
38
  const proxyConfig = config["nuxt-scripts-proxy"];
29
39
  if (!proxyConfig) {
30
40
  throw createError({
@@ -32,7 +42,7 @@ export default defineEventHandler(async (event) => {
32
42
  statusMessage: "First-party proxy not configured"
33
43
  });
34
44
  }
35
- const { routes, privacy: globalPrivacy, routePrivacy, cacheTtl = 3600, debug = import.meta.dev } = proxyConfig;
45
+ const { routes, privacy: globalPrivacy, routePrivacy, debug = import.meta.dev } = proxyConfig;
36
46
  const path = event.path;
37
47
  const log = debug ? (message, ...args) => {
38
48
  console.debug(message, ...args);
@@ -41,11 +51,10 @@ export default defineEventHandler(async (event) => {
41
51
  let targetBase;
42
52
  let matchedPrefix;
43
53
  let matchedRoutePattern;
44
- const sortedRoutes = Object.entries(routes).sort((a, b) => b[0].length - a[0].length);
45
- for (const [routePattern, target] of sortedRoutes) {
46
- const prefix = routePattern.replace(/\/\*\*$/, "");
54
+ for (const [routePattern, target] of getSortedRoutes(routes)) {
55
+ const prefix = routePattern.replace(ROUTE_WILDCARD_RE, "");
47
56
  if (path.startsWith(prefix)) {
48
- targetBase = target.replace(/\/\*\*$/, "");
57
+ targetBase = target.replace(ROUTE_WILDCARD_RE, "");
49
58
  matchedPrefix = prefix;
50
59
  matchedRoutePattern = routePattern;
51
60
  log("[proxy] Matched:", prefix, "->", targetBase);
@@ -67,28 +76,45 @@ export default defineEventHandler(async (event) => {
67
76
  const perScriptResolved = resolvePrivacy(perScriptInput ?? true);
68
77
  const privacy = globalPrivacy !== void 0 ? mergePrivacy(perScriptResolved, globalPrivacy) : perScriptResolved;
69
78
  const anyPrivacy = privacy.ip || privacy.userAgent || privacy.language || privacy.screen || privacy.timezone || privacy.hardware;
79
+ const originalHeaders = getHeaders(event);
80
+ const originalQuery = getQuery(event);
81
+ const contentType = originalHeaders["content-type"] || "";
82
+ const compressionParam = originalQuery.compression || "";
83
+ const isBinaryBody = Boolean(
84
+ originalHeaders["content-encoding"] || contentType.includes("octet-stream") || compressionParam && COMPRESSION_RE.test(compressionParam)
85
+ );
70
86
  let targetPath = path.slice(matchedPrefix.length);
71
87
  if (targetPath && !targetPath.startsWith("/")) {
72
- targetPath = "/" + targetPath;
88
+ targetPath = `/${targetPath}`;
73
89
  }
74
90
  let targetUrl = targetBase + targetPath;
91
+ let strippedQueryRecord;
75
92
  if (anyPrivacy) {
76
- const query = getQuery(event);
77
- if (Object.keys(query).length > 0) {
78
- const strippedQuery = stripQueryFingerprinting(query, privacy);
93
+ if (Object.keys(originalQuery).length > 0) {
94
+ const { queryString, stripped } = stripQueryFingerprinting(originalQuery, privacy);
95
+ strippedQueryRecord = stripped;
79
96
  const basePath = targetUrl.split("?")[0] || targetUrl;
80
- targetUrl = strippedQuery ? `${basePath}?${strippedQuery}` : basePath;
97
+ targetUrl = queryString ? `${basePath}?${queryString}` : basePath;
81
98
  }
82
99
  }
83
- const originalHeaders = getHeaders(event);
84
100
  const headers = {};
85
101
  for (const [key, value] of Object.entries(originalHeaders)) {
86
- if (!value) continue;
102
+ if (!value)
103
+ continue;
87
104
  const lowerKey = key.toLowerCase();
88
- if (SENSITIVE_HEADERS.includes(lowerKey)) continue;
89
- if (anyPrivacy && lowerKey === "content-length") continue;
105
+ if (lowerKey === "host")
106
+ continue;
107
+ if (SENSITIVE_HEADERS.includes(lowerKey))
108
+ continue;
109
+ if (lowerKey === "content-length") {
110
+ if (anyPrivacy && !isBinaryBody)
111
+ continue;
112
+ headers[lowerKey] = value;
113
+ continue;
114
+ }
90
115
  if (lowerKey === "x-forwarded-for" || lowerKey === "x-real-ip" || lowerKey === "forwarded" || lowerKey === "cf-connecting-ip" || lowerKey === "true-client-ip" || lowerKey === "x-client-ip" || lowerKey === "x-cluster-client-ip") {
91
- if (privacy.ip) continue;
116
+ if (privacy.ip)
117
+ continue;
92
118
  headers[lowerKey] = value;
93
119
  continue;
94
120
  }
@@ -101,11 +127,12 @@ export default defineEventHandler(async (event) => {
101
127
  continue;
102
128
  }
103
129
  if (lowerKey === "sec-ch-ua" || lowerKey === "sec-ch-ua-full-version-list") {
104
- headers[lowerKey] = privacy.hardware ? value.replace(/;v="(\d+)\.[^"]*"/g, ';v="$1"') : value;
130
+ headers[lowerKey] = privacy.hardware ? value.replace(CLIENT_HINT_VERSION_RE, ';v="$1"') : value;
105
131
  continue;
106
132
  }
107
133
  if (lowerKey === "sec-ch-ua-platform-version" || lowerKey === "sec-ch-ua-arch" || lowerKey === "sec-ch-ua-model" || lowerKey === "sec-ch-ua-bitness") {
108
- if (privacy.hardware) continue;
134
+ if (privacy.hardware)
135
+ continue;
109
136
  headers[lowerKey] = value;
110
137
  continue;
111
138
  }
@@ -125,98 +152,127 @@ export default defineEventHandler(async (event) => {
125
152
  }
126
153
  let body;
127
154
  let rawBody;
128
- const contentType = originalHeaders["content-type"] || "";
155
+ let passthroughBody = false;
129
156
  const method = event.method?.toUpperCase();
130
- const originalQuery = getQuery(event);
131
- if (method === "POST" || method === "PUT" || method === "PATCH") {
132
- rawBody = await readBody(event);
133
- if (anyPrivacy && rawBody) {
134
- if (typeof rawBody === "object") {
135
- body = stripPayloadFingerprinting(rawBody, privacy);
136
- } else if (typeof rawBody === "string") {
137
- if (rawBody.startsWith("{") || rawBody.startsWith("[")) {
138
- let parsed = null;
139
- try {
140
- parsed = JSON.parse(rawBody);
141
- } catch {
142
- }
143
- if (parsed && typeof parsed === "object") {
144
- body = stripPayloadFingerprinting(parsed, privacy);
157
+ const isWriteMethod = method === "POST" || method === "PUT" || method === "PATCH";
158
+ if (isWriteMethod) {
159
+ if (isBinaryBody || !anyPrivacy) {
160
+ passthroughBody = true;
161
+ } else {
162
+ rawBody = await readBody(event);
163
+ if (rawBody != null) {
164
+ if (Array.isArray(rawBody)) {
165
+ body = rawBody.map(
166
+ (item) => item && typeof item === "object" && !Array.isArray(item) ? stripPayloadFingerprinting(item, privacy) : item
167
+ );
168
+ } else if (typeof rawBody === "object") {
169
+ body = stripPayloadFingerprinting(rawBody, privacy);
170
+ } else if (typeof rawBody === "string") {
171
+ if (contentType.includes("application/x-www-form-urlencoded")) {
172
+ const params = new URLSearchParams(rawBody);
173
+ const obj = {};
174
+ for (const [key, value] of params.entries()) {
175
+ if (key in obj) {
176
+ const existing = obj[key];
177
+ obj[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
178
+ } else {
179
+ obj[key] = value;
180
+ }
181
+ }
182
+ const stripped = stripPayloadFingerprinting(obj, privacy);
183
+ const out = new URLSearchParams();
184
+ for (const [k, v] of Object.entries(stripped)) {
185
+ if (v === void 0 || v === null)
186
+ continue;
187
+ if (Array.isArray(v)) {
188
+ for (const item of v)
189
+ out.append(k, typeof item === "string" ? item : JSON.stringify(item));
190
+ } else {
191
+ out.append(k, typeof v === "string" ? v : JSON.stringify(v));
192
+ }
193
+ }
194
+ body = out.toString();
145
195
  } else {
146
- body = rawBody;
196
+ const maybeJson = contentType.includes("json") || (rawBody.startsWith("{") || rawBody.startsWith("["));
197
+ if (maybeJson) {
198
+ let parsed = null;
199
+ try {
200
+ parsed = JSON.parse(rawBody);
201
+ } catch {
202
+ }
203
+ if (Array.isArray(parsed)) {
204
+ body = parsed.map(
205
+ (item) => item && typeof item === "object" && !Array.isArray(item) ? stripPayloadFingerprinting(item, privacy) : item
206
+ );
207
+ } else if (parsed && typeof parsed === "object") {
208
+ body = stripPayloadFingerprinting(parsed, privacy);
209
+ } else {
210
+ body = rawBody;
211
+ }
212
+ } else {
213
+ body = rawBody;
214
+ }
147
215
  }
148
- } else if (contentType.includes("application/x-www-form-urlencoded")) {
149
- const params = new URLSearchParams(rawBody);
150
- const obj = {};
151
- params.forEach((value, key) => {
152
- obj[key] = value;
153
- });
154
- const stripped = stripPayloadFingerprinting(obj, privacy);
155
- const stringified = {};
156
- for (const [k, v] of Object.entries(stripped)) {
157
- if (v === void 0 || v === null) continue;
158
- stringified[k] = typeof v === "string" ? v : JSON.stringify(v);
159
- }
160
- body = new URLSearchParams(stringified).toString();
161
216
  } else {
162
217
  body = rawBody;
163
218
  }
164
- } else {
165
- body = rawBody;
166
219
  }
167
- } else {
168
- body = rawBody;
169
220
  }
170
221
  }
222
+ const nitro = useNitroApp();
171
223
  await nitro.hooks.callHook("nuxt-scripts:proxy", {
172
224
  timestamp: Date.now(),
173
225
  path: event.path,
174
226
  targetUrl,
175
227
  method: method || "GET",
176
228
  privacy,
229
+ passthroughBody,
177
230
  original: {
178
231
  headers: { ...originalHeaders },
179
232
  query: originalQuery,
180
- body: rawBody ?? null
233
+ body: passthroughBody ? "<passthrough>" : rawBody ?? null
181
234
  },
182
235
  stripped: {
183
236
  headers,
184
- query: anyPrivacy ? stripPayloadFingerprinting(originalQuery, privacy) : originalQuery,
185
- body: body ?? null
237
+ query: strippedQueryRecord ?? originalQuery,
238
+ body: passthroughBody ? "<passthrough>" : body ?? null
186
239
  }
187
240
  });
188
241
  log("[proxy] Fetching:", targetUrl);
189
242
  const controller = new AbortController();
190
243
  const timeoutId = setTimeout(() => controller.abort(), 15e3);
244
+ let fetchBody;
245
+ if (passthroughBody) {
246
+ fetchBody = getRequestWebStream(event);
247
+ } else if (body !== void 0) {
248
+ fetchBody = typeof body === "string" ? body : JSON.stringify(body);
249
+ }
191
250
  let response;
192
251
  try {
193
252
  response = await fetch(targetUrl, {
194
253
  method: method || "GET",
195
254
  headers,
196
- body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
255
+ body: fetchBody,
197
256
  credentials: "omit",
198
257
  // Don't send cookies to third parties
199
- signal: controller.signal
258
+ signal: controller.signal,
259
+ // @ts-expect-error Node fetch supports duplex for streaming request bodies
260
+ duplex: passthroughBody ? "half" : void 0
200
261
  });
201
262
  } catch (err) {
202
263
  clearTimeout(timeoutId);
203
- log("[proxy] Fetch error:", err instanceof Error ? err.message : err);
204
- if (path.includes("/collect") || path.includes("/tr") || path.includes("/events")) {
205
- event.node.res.statusCode = 204;
206
- return "";
207
- }
208
- const isTimeout = err instanceof Error && (err.message.includes("aborted") || err.message.includes("timeout"));
264
+ log("[proxy] Upstream error:", err);
209
265
  throw createError({
210
- statusCode: isTimeout ? 504 : 502,
211
- statusMessage: isTimeout ? "Upstream timeout" : "Bad Gateway",
212
- message: "Failed to reach upstream"
266
+ statusCode: 502,
267
+ statusMessage: "Bad Gateway",
268
+ message: `Proxy upstream request failed: ${targetUrl}`
213
269
  });
270
+ } finally {
271
+ clearTimeout(timeoutId);
214
272
  }
215
- clearTimeout(timeoutId);
216
273
  log("[proxy] Response:", response.status, response.statusText);
217
- const skipHeaders = ["set-cookie", "transfer-encoding", "content-encoding", "content-length"];
218
274
  response.headers.forEach((value, key) => {
219
- if (!skipHeaders.includes(key.toLowerCase())) {
275
+ if (!SKIP_RESPONSE_HEADERS.has(key.toLowerCase())) {
220
276
  setResponseHeader(event, key, value);
221
277
  }
222
278
  });
@@ -225,22 +281,7 @@ export default defineEventHandler(async (event) => {
225
281
  const responseContentType = response.headers.get("content-type") || "";
226
282
  const isTextContent = responseContentType.includes("text") || responseContentType.includes("javascript") || responseContentType.includes("json");
227
283
  if (isTextContent) {
228
- let content = await response.text();
229
- if (responseContentType.includes("javascript") && proxyConfig?.rewrites?.length) {
230
- const cacheKey = `nuxt-scripts:proxy:${hash(targetUrl + JSON.stringify(proxyConfig.rewrites))}`;
231
- const storage = useStorage("cache");
232
- const cached = await storage.getItem(cacheKey);
233
- if (cached && typeof cached === "string") {
234
- log("[proxy] Serving rewritten script from cache");
235
- content = cached;
236
- } else {
237
- content = rewriteScriptUrls(content, proxyConfig.rewrites);
238
- await storage.setItem(cacheKey, content, { ttl: cacheTtl });
239
- log("[proxy] Rewrote URLs in JavaScript response and cached");
240
- }
241
- setResponseHeader(event, "cache-control", `public, max-age=${cacheTtl}, stale-while-revalidate=${cacheTtl * 2}`);
242
- }
243
- return content;
284
+ return await response.text();
244
285
  }
245
286
  return Buffer.from(await response.arrayBuffer());
246
287
  });
@@ -0,0 +1,12 @@
1
+ export interface ImageProxyConfig {
2
+ allowedDomains: string[] | ((hostname: string) => boolean);
3
+ accept?: string;
4
+ userAgent?: string;
5
+ cacheMaxAge?: number;
6
+ contentType?: string;
7
+ /** Follow redirects (default: true). Set to false to reject redirects (SSRF protection). */
8
+ followRedirects?: boolean;
9
+ /** Decode &amp; in URL query parameter */
10
+ decodeAmpersands?: boolean;
11
+ }
12
+ export declare function createImageProxyHandler(config: ImageProxyConfig): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
@@ -0,0 +1,70 @@
1
+ import { createError, defineEventHandler, getQuery, setHeader } from "h3";
2
+ import { $fetch } from "ofetch";
3
+ const AMP_RE = /&amp;/g;
4
+ export function createImageProxyHandler(config) {
5
+ const {
6
+ accept = "image/webp,image/jpeg,image/png,image/*,*/*;q=0.8",
7
+ userAgent,
8
+ cacheMaxAge = 3600,
9
+ contentType = "image/jpeg",
10
+ followRedirects = true,
11
+ decodeAmpersands = false
12
+ } = config;
13
+ return defineEventHandler(async (event) => {
14
+ const query = getQuery(event);
15
+ let url = query.url;
16
+ if (decodeAmpersands && url)
17
+ url = url.replace(AMP_RE, "&");
18
+ if (!url) {
19
+ throw createError({
20
+ statusCode: 400,
21
+ statusMessage: "Image URL is required"
22
+ });
23
+ }
24
+ let parsedUrl;
25
+ try {
26
+ parsedUrl = new URL(url);
27
+ } catch {
28
+ throw createError({
29
+ statusCode: 400,
30
+ statusMessage: "Invalid image URL"
31
+ });
32
+ }
33
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
34
+ throw createError({
35
+ statusCode: 400,
36
+ statusMessage: "Invalid URL scheme"
37
+ });
38
+ }
39
+ const domainAllowed = typeof config.allowedDomains === "function" ? config.allowedDomains(parsedUrl.hostname) : config.allowedDomains.includes(parsedUrl.hostname);
40
+ if (!domainAllowed) {
41
+ throw createError({
42
+ statusCode: 403,
43
+ statusMessage: "Domain not allowed"
44
+ });
45
+ }
46
+ const headers = { Accept: accept };
47
+ if (userAgent)
48
+ headers["User-Agent"] = userAgent;
49
+ const response = await $fetch.raw(url, {
50
+ timeout: 5e3,
51
+ redirect: followRedirects ? "follow" : "manual",
52
+ ignoreResponseError: !followRedirects,
53
+ headers
54
+ }).catch((error) => {
55
+ throw createError({
56
+ statusCode: error.statusCode || 500,
57
+ statusMessage: error.statusMessage || "Failed to fetch image"
58
+ });
59
+ });
60
+ if (!followRedirects && response.status >= 300 && response.status < 400) {
61
+ throw createError({
62
+ statusCode: 403,
63
+ statusMessage: "Redirects not allowed"
64
+ });
65
+ }
66
+ setHeader(event, "Content-Type", response.headers.get("content-type") || contentType);
67
+ setHeader(event, "Cache-Control", `public, max-age=${cacheMaxAge}, s-maxage=${cacheMaxAge}`);
68
+ return response._data;
69
+ });
70
+ }
@@ -20,7 +20,7 @@ export interface ProxyPrivacy {
20
20
  * Privacy input: `true` = full anonymize, `false` = passthrough (still strips sensitive headers),
21
21
  * or a `ProxyPrivacy` object for granular control (unset flags default to `false` — opt-in).
22
22
  */
23
- export type ProxyPrivacyInput = boolean | ProxyPrivacy | null;
23
+ export type ProxyPrivacyInput = boolean | ProxyPrivacy;
24
24
  /** Resolved privacy with all flags explicitly set. */
25
25
  export type ResolvedProxyPrivacy = Required<ProxyPrivacy>;
26
26
  /**
@@ -71,7 +71,6 @@ export declare const STRIP_PARAMS: {
71
71
  browserVersion: string[];
72
72
  browserData: string[];
73
73
  location: string[];
74
- canvas: string[];
75
74
  deviceInfo: string[];
76
75
  };
77
76
  /**
@@ -1,8 +1,17 @@
1
1
  const FULL_PRIVACY = { ip: true, userAgent: true, language: true, screen: true, timezone: true, hardware: true };
2
2
  const NO_PRIVACY = { ip: false, userAgent: false, language: false, screen: false, timezone: false, hardware: false };
3
+ const MAJOR_VERSION_RE = /^(\d+)/;
4
+ const VERSION_RE = /^(\d+)(([.\-_])\d+)*/;
5
+ const VERSION_SPLIT_RE = /[.\-_]/;
6
+ const SNAPCHAT_VERSION_RE = /("version"\s*:\s*")(\d+(?:\.\d+)*)/g;
7
+ const GA_VERSION_RE = /;(\d+(?:\.\d+)*)/g;
8
+ const UPPERCASE_RE = /^[A-Z]/;
9
+ const LANG_CODE_RE = /^[a-z]{2}(?:-[a-z]{2,})?$/i;
3
10
  export function resolvePrivacy(input) {
4
- if (input === true) return { ...FULL_PRIVACY };
5
- if (input === false || input === void 0 || input === null) return { ...NO_PRIVACY };
11
+ if (input === true)
12
+ return { ...FULL_PRIVACY };
13
+ if (input === false || input === void 0)
14
+ return { ...NO_PRIVACY };
6
15
  return {
7
16
  ip: input.ip ?? false,
8
17
  userAgent: input.userAgent ?? false,
@@ -13,8 +22,10 @@ export function resolvePrivacy(input) {
13
22
  };
14
23
  }
15
24
  export function mergePrivacy(base, override) {
16
- if (override === void 0 || override === null) return base;
17
- if (typeof override === "boolean") return resolvePrivacy(override);
25
+ if (override === void 0)
26
+ return base;
27
+ if (typeof override === "boolean")
28
+ return resolvePrivacy(override);
18
29
  return {
19
30
  ip: override.ip !== void 0 ? override.ip : base.ip,
20
31
  userAgent: override.userAgent !== void 0 ? override.userAgent : base.userAgent,
@@ -67,11 +78,13 @@ export const STRIP_PARAMS = {
67
78
  // Browser version lists — generalized to major versions (d_bvs = Snapchat, uafvl = GA Client Hints)
68
79
  browserVersion: ["d_bvs", "uafvl"],
69
80
  // Browser data lists — replaced with empty value
70
- browserData: ["plugins", "fonts"],
81
+ browserData: ["plugins", "fonts", "audiofingerprint"],
71
82
  // Location/Timezone — generalized
72
83
  location: ["tz", "timezone", "timezoneoffset"],
73
- // Canvas/WebGL/Audio fingerprints — replaced with empty value (pure fingerprints, no analytics value)
74
- canvas: ["canvas", "webgl", "audiofingerprint"],
84
+ // Canvas/WebGL fingerprints — neutralized at build time via AST rewriting (rewrite-ast.ts).
85
+ // These params are no longer stripped at runtime; the source APIs (toDataURL, WEBGL_debug_renderer_info)
86
+ // are neutralized before the script ever runs.
87
+ // canvas: ['canvas', 'webgl'],
75
88
  // Combined device fingerprinting (X/Twitter dv param contains: timezone, locale, vendor, platform, screen, etc.)
76
89
  deviceInfo: ["dv", "device_info", "deviceinfo"]
77
90
  };
@@ -81,7 +94,7 @@ export const NORMALIZE_PARAMS = {
81
94
  };
82
95
  export function anonymizeIP(ip) {
83
96
  if (ip.includes(":")) {
84
- return ip.split(":").slice(0, 3).join(":") + "::";
97
+ return `${ip.split(":").slice(0, 3).join(":")}::`;
85
98
  }
86
99
  const parts = ip.split(".");
87
100
  if (parts.length === 4) {
@@ -103,7 +116,7 @@ export function normalizeUserAgent(ua) {
103
116
  const idx = ua.indexOf(pattern);
104
117
  if (idx !== -1) {
105
118
  const versionStart = idx + pattern.length;
106
- const majorVersion = ua.slice(versionStart).match(/^(\d+)/)?.[1];
119
+ const majorVersion = ua.slice(versionStart).match(MAJOR_VERSION_RE)?.[1];
107
120
  if (majorVersion)
108
121
  return `Mozilla/5.0 (compatible; ${family}/${majorVersion}.0)`;
109
122
  }
@@ -119,8 +132,10 @@ const SCREEN_BUCKETS = {
119
132
  mobile: { w: 360, h: 640 }
120
133
  };
121
134
  function getDeviceClass(width) {
122
- if (width >= 1200) return "desktop";
123
- if (width >= 700) return "tablet";
135
+ if (width >= 1200)
136
+ return "desktop";
137
+ if (width >= 700)
138
+ return "tablet";
124
139
  return "mobile";
125
140
  }
126
141
  export function generalizeScreen(value, dimension) {
@@ -139,31 +154,38 @@ export function generalizeScreen(value, dimension) {
139
154
  }
140
155
  export function generalizeHardware(value) {
141
156
  const num = typeof value === "number" ? value : Number(value);
142
- if (Number.isNaN(num)) return 4;
143
- if (num >= 16) return 16;
144
- if (num >= 8) return 8;
145
- if (num >= 4) return 4;
157
+ if (Number.isNaN(num))
158
+ return 4;
159
+ if (num >= 16)
160
+ return 16;
161
+ if (num >= 8)
162
+ return 8;
163
+ if (num >= 4)
164
+ return 4;
146
165
  return 2;
147
166
  }
148
167
  export function generalizeVersion(value) {
149
- if (typeof value !== "string") return String(value);
150
- const match = value.match(/^(\d+)(([.\-_])\d+)*/);
151
- if (!match) return String(value);
168
+ if (typeof value !== "string")
169
+ return String(value);
170
+ const match = value.match(VERSION_RE);
171
+ if (!match)
172
+ return String(value);
152
173
  const major = match[1];
153
174
  const sep = match[3] || ".";
154
- const segmentCount = value.split(/[.\-_]/).length;
155
- return major + (sep + "0").repeat(segmentCount - 1);
175
+ const segmentCount = value.split(VERSION_SPLIT_RE).length;
176
+ return major + `${sep}0`.repeat(segmentCount - 1);
156
177
  }
157
178
  export function generalizeBrowserVersions(value) {
158
- if (typeof value !== "string") return String(value);
179
+ if (typeof value !== "string")
180
+ return String(value);
159
181
  const zeroSegments = (ver) => {
160
182
  const parts = ver.split(".");
161
183
  return parts[0] + parts.slice(1).map(() => ".0").join("");
162
184
  };
163
185
  if (value.includes('"version"'))
164
- return value.replace(/("version"\s*:\s*")(\d+(?:\.\d+)*)/g, (_, prefix, ver) => prefix + zeroSegments(ver));
186
+ return value.replace(SNAPCHAT_VERSION_RE, (_, prefix, ver) => prefix + zeroSegments(ver));
165
187
  if (value.includes(";"))
166
- return value.replace(/;(\d+(?:\.\d+)*)/g, (_, ver) => ";" + zeroSegments(ver));
188
+ return value.replace(GA_VERSION_RE, (_, ver) => `;${zeroSegments(ver)}`);
167
189
  return value;
168
190
  }
169
191
  export function generalizeTimezone(value) {
@@ -178,15 +200,16 @@ export function generalizeTimezone(value) {
178
200
  export function anonymizeDeviceInfo(value) {
179
201
  const sep = value.includes("|") ? "|" : "&";
180
202
  const parts = value.split(sep);
181
- if (parts.length < 4) return value;
203
+ if (parts.length < 4)
204
+ return value;
182
205
  const result = [...parts];
183
206
  for (let i = 0; i < parts.length; i++) {
184
207
  const part = parts[i];
185
- if (part.includes("/") && /^[A-Z]/.test(part)) {
208
+ if (part.includes("/") && UPPERCASE_RE.test(part)) {
186
209
  result[i] = String(generalizeTimezone(part));
187
210
  continue;
188
211
  }
189
- if (/^[a-z]{2}(?:-[a-z]{2,})?$/i.test(part)) {
212
+ if (LANG_CODE_RE.test(part)) {
190
213
  result[i] = normalizeLanguage(part);
191
214
  continue;
192
215
  }
@@ -216,7 +239,8 @@ export function stripPayloadFingerprinting(payload, privacy) {
216
239
  for (const [key, value] of Object.entries(payload)) {
217
240
  if (key.toLowerCase() === "sw") {
218
241
  const num = typeof value === "number" ? value : Number(value);
219
- if (!Number.isNaN(num)) deviceClass = getDeviceClass(num);
242
+ if (!Number.isNaN(num))
243
+ deviceClass = getDeviceClass(num);
220
244
  }
221
245
  }
222
246
  for (const [key, value] of Object.entries(payload)) {
@@ -225,7 +249,7 @@ export function stripPayloadFingerprinting(payload, privacy) {
225
249
  const lk = key2.toLowerCase();
226
250
  return params.some((pm) => {
227
251
  const lp = pm.toLowerCase();
228
- return lk === lp || lk.startsWith(lp + "[");
252
+ return lk === lp || lk.startsWith(`${lp}[`);
229
253
  });
230
254
  };
231
255
  const isLanguageParam = NORMALIZE_PARAMS.language.some((pm) => lowerKey === pm.toLowerCase());
@@ -264,7 +288,7 @@ export function stripPayloadFingerprinting(payload, privacy) {
264
288
  continue;
265
289
  }
266
290
  if (matchesParam(key, STRIP_PARAMS.hardware)) {
267
- result[key] = p.screen ? generalizeHardware(value) : value;
291
+ result[key] = p.hardware ? generalizeHardware(value) : value;
268
292
  continue;
269
293
  }
270
294
  if (matchesParam(key, STRIP_PARAMS.version)) {
@@ -280,11 +304,7 @@ export function stripPayloadFingerprinting(payload, privacy) {
280
304
  continue;
281
305
  }
282
306
  if (matchesParam(key, STRIP_PARAMS.browserData)) {
283
- result[key] = p.hardware ? Array.isArray(value) ? [] : "" : value;
284
- continue;
285
- }
286
- if (matchesParam(key, STRIP_PARAMS.canvas)) {
287
- result[key] = p.hardware ? typeof value === "number" ? 0 : typeof value === "object" ? {} : "" : value;
307
+ result[key] = p.hardware ? Array.isArray(value) ? [] : typeof value === "number" ? 0 : "" : value;
288
308
  continue;
289
309
  }
290
310
  if (matchesParam(key, STRIP_PARAMS.deviceInfo)) {