Skip to content

feat(first-party): exclude fingerprinting scripts, fix per-script opt-out#662

Open
harlan-zw wants to merge 5 commits intomainfrom
feat/first-party-fingerprint-optout
Open

feat(first-party): exclude fingerprinting scripts, fix per-script opt-out#662
harlan-zw wants to merge 5 commits intomainfrom
feat/first-party-fingerprint-optout

Conversation

@harlan-zw
Copy link
Collaborator

❓ Type of change

  • 📖 Documentation
  • 🐞 Bug fix
  • 👌 Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

📚 Description

Scripts requiring browser fingerprinting for fraud/bot detection (Stripe, PayPal, Google reCAPTCHA, Google Sign-In) were being included in first-party mode, which could strip fingerprinting data and break their core functionality. These now set proxy: false and scriptBundling: false to exclude them entirely.

Per-script firstParty: false opt-out in registry config was not respected by autoInject, causing Plausible (and other scripts with auto-inject) to still route through the proxy even when opted out. Both applyAutoInject and finalizeFirstParty now check for firstParty: false before injecting proxy endpoints or registering domains.

Changes

Fingerprinting script exclusion:

  • Stripe, PayPal, reCAPTCHA, Google Sign-In: proxy: false + scriptBundling: false in registry
  • Removed their proxy configs from proxy-configs.ts

Per-script firstParty opt-out fix:

  • applyAutoInject now checks firstParty: false in entry input and scriptOptions
  • finalizeFirstParty skips domain registration for opted-out entries
  • Users can opt out via plausibleAnalytics: { domain: 'mysite.com', firstParty: false }

Docs:

  • Updated FIRST_PARTY.md architecture reference
  • Updated docs/content/docs/1.guides/2.first-party.md user guide
  • Added "Scripts That Bypass First-Party Mode" section
  • Added per-script opt-out examples and troubleshooting

Tests:

  • Updated auto-inject, first-party, gravatar-proxy, proxy-configs tests to match domain-based format
  • Added firstParty opt-out test cases
  • Added fingerprinting exclusion assertions

Playground fixes:

  • Fixed $window reference in vercel-analytics page
  • Fixed undefined setupGoogleMaps handler in google-maps/center page

…-out

Scripts requiring browser fingerprinting for fraud/bot detection (Stripe,
PayPal, Google reCAPTCHA, Google Sign-In) now set proxy: false and
scriptBundling: false, excluding them from first-party mode entirely.

Per-script firstParty: false opt-out now works in registry config entries,
fixing the reported issue where Plausible autoInject ignored the opt-out.
Both applyAutoInject and finalizeFirstParty check the entry before
injecting proxy endpoints or registering domains.

Updated docs, architecture reference, and tests to match the current
domain-based proxy config format.
@vercel
Copy link
Contributor

vercel bot commented Mar 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
scripts-playground Ready Ready Preview, Comment Mar 20, 2026 4:40pm

@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

The diff replaces a pattern-based first-party proxy (InterceptRule/routes/rewrites) with a domain-driven model: proxy configs now declare domains and a single proxyPrefix. Build-time AST rewrites are derived from domains; runtime interceptors and Partytown resolve now rewrite non-same-origin URLs to origin + proxyPrefix + "/" + host + path. Exports and types for InterceptRule and route helpers were removed. Documentation (FIRST_PARTY.md) and many playground examples were updated to use proxy wrappers; auto-inject and per-script firstParty: false opt-outs were added. A new client error-overlay plugin and numerous test updates accompany the refactor.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: excluding fingerprinting scripts from first-party mode and fixing per-script opt-out functionality, which are the core objectives.
Description check ✅ Passed The description covers the problem statement, all major changes (fingerprinting exclusion, per-script opt-out fix), implementation details, and references doc/test updates, but the linked issue section is empty.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/first-party-fingerprint-optout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 20, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@nuxt/scripts@662

commit: 25a9eb7

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/first-party/proxy-configs.ts (1)

30-30: Unused parameter _proxyPrefix in buildProxyConfig.

The _proxyPrefix parameter is passed but never used within buildProxyConfig. The autoInject.computeValue functions receive proxyPrefix as an argument when they're called (at runtime), not from buildProxyConfig. Consider removing this parameter if it's truly unused, or document why it's retained.

🧹 Option A: Remove unused parameter
-function buildProxyConfig(_proxyPrefix: string) {
+function buildProxyConfig() {
   return {

And update the caller:

 export function getAllProxyConfigs(proxyPrefix: string): Partial<Record<RegistryScriptKey, ProxyConfig>> {
-  return buildProxyConfig(proxyPrefix)
+  return buildProxyConfig()
 }
🧹 Option B: Add comment explaining future use
-function buildProxyConfig(_proxyPrefix: string) {
+// proxyPrefix reserved for future static config generation
+function buildProxyConfig(_proxyPrefix: string) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/first-party/proxy-configs.ts` at line 30, The parameter `_proxyPrefix` on
function buildProxyConfig is unused — remove it from the buildProxyConfig
signature and update any callers to stop passing that argument, or if you
intentionally reserve it for future use, keep the parameter but add a one-line
comment above buildProxyConfig explaining it is retained intentionally for
future API compatibility (and that actual runtime values come from
autoInject.computeValue callbacks which receive proxyPrefix). Ensure references
to buildProxyConfig and the autoInject.computeValue usage remain consistent
after the change.
FIRST_PARTY.md (1)

77-84: Add language specifiers to fenced code blocks.

The code blocks are missing language specifiers, which aids syntax highlighting and satisfies markdownlint MD040.

📝 Proposed fix
-```
+```ts
 scripts.registry.plausibleAnalytics = { domain: 'mysite.com', firstParty: false }

Or in tuple form with scriptOptions:
- +ts
scripts.registry.plausibleAnalytics = [{ domain: 'mysite.com' }, { firstParty: false }]

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@FIRST_PARTY.md` around lines 77 - 84, The fenced code blocks showing examples
for scripts.registry.plausibleAnalytics lack language specifiers; update both
blocks to include a language (e.g., add "ts" after the opening ```), so the
examples "scripts.registry.plausibleAnalytics = { domain: 'mysite.com',
firstParty: false }" and "scripts.registry.plausibleAnalytics = [{ domain:
'mysite.com' }, { firstParty: false }]" are fenced as ```ts to satisfy
markdownlint MD040 and enable syntax highlighting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@playground/plugins/error-overlay.client.ts`:
- Around line 86-90: safeStringify currently JSON.stringify's Error objects into
{} because Error.message and Error.stack are non-enumerable; update
safeStringify to first check if the input is an instance of Error (or has
Error-like shape) and return a stringified representation that includes
error.message and error.stack (e.g., combine message and stack or build an
object with these properties) before falling back to JSON.stringify/String(a);
reference the safeStringify function to locate where to add the instanceof Error
extraction and ensure the final return remains a string.
- Around line 133-149: Update the MutationObserver usage (observer) to also
observe attribute changes by passing { childList: true, subtree: true,
attributes: true, attributeFilter: ['src','href'] } and extend the observer
callback to handle mutation.type === 'attributes' by calling checkElement on
mutation.target (or the element whose src/href changed) and/or reading the new
attribute value and passing it to reportIfThirdParty; keep existing childList
handling intact and use the existing checkElement and reportIfThirdParty helpers
to perform the same validation for attribute mutations.

In `@src/first-party/intercept-plugin.ts`:
- Around line 6-8: The server proxy currently allows fetching any host even when
privacy filtering is applied, creating an open relay; update the proxy handler
that implements __nuxtScripts.fetch to perform an explicit allowlist check
against domainPrivacy (or its helper) before proceeding to proxyPrefix logic and
return an HTTP 403/error for disallowed domains so requests are never forwarded
to hosts not listed in domainPrivacy.

In `@src/module.ts`:
- Around line 517-520: The current guard only checks config.partytown?.length
and misses registry-enabled Partytown flags; update the conditional around
hasNuxtModule('@nuxtjs/partytown') to also detect registry-level Partytown by
checking your registry entries for scriptOptions.partytown === true (or truthy)
before setting (nuxt.options as any).partytown.resolveUrl via
generatePartytownResolveUrl(proxyPrefix); specifically, replace the single
config.partytown?.length check with a predicate that is true if config.partytown
has entries OR any registry entry has scriptOptions.partytown truthy, then set
partytownConfig.resolveUrl as before.

In `@src/runtime/server/proxy-handler.ts`:
- Around line 87-101: The handler currently defaults to proxying even when a
domain has no entry in domainPrivacy (perScriptInput === undefined), enabling
SSRF; change the logic in proxy-handler.ts so that if perScriptInput is
undefined you reject the request immediately (e.g., return a 403/400 Response or
throw an error) rather than proceeding to build targetBase or proxying; update
the block that checks perScriptInput (and any later use of targetBase) to
enforce this allowlist check and log the rejection when debug is true.

---

Nitpick comments:
In `@FIRST_PARTY.md`:
- Around line 77-84: The fenced code blocks showing examples for
scripts.registry.plausibleAnalytics lack language specifiers; update both blocks
to include a language (e.g., add "ts" after the opening ```), so the examples
"scripts.registry.plausibleAnalytics = { domain: 'mysite.com', firstParty: false
}" and "scripts.registry.plausibleAnalytics = [{ domain: 'mysite.com' }, {
firstParty: false }]" are fenced as ```ts to satisfy markdownlint MD040 and
enable syntax highlighting.

In `@src/first-party/proxy-configs.ts`:
- Line 30: The parameter `_proxyPrefix` on function buildProxyConfig is unused —
remove it from the buildProxyConfig signature and update any callers to stop
passing that argument, or if you intentionally reserve it for future use, keep
the parameter but add a one-line comment above buildProxyConfig explaining it is
retained intentionally for future API compatibility (and that actual runtime
values come from autoInject.computeValue callbacks which receive proxyPrefix).
Ensure references to buildProxyConfig and the autoInject.computeValue usage
remain consistent after the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef75c7c0-42e1-4799-bb61-a3f586597abb

📥 Commits

Reviewing files that changed from the base of the PR and between 3f4d214 and c504112.

📒 Files selected for processing (31)
  • FIRST_PARTY.md
  • docs/content/docs/1.guides/2.first-party.md
  • playground/nuxt.config.ts
  • playground/pages/third-parties/clarity/nuxt-scripts.vue
  • playground/pages/third-parties/google-analytics/datalayers.vue
  • playground/pages/third-parties/google-analytics/nuxt-scripts.vue
  • playground/pages/third-parties/google-maps/center.vue
  • playground/pages/third-parties/hotjar.vue
  • playground/pages/third-parties/intercom/use-script.vue
  • playground/pages/third-parties/lemon-squeezy/script.vue
  • playground/pages/third-parties/meta-pixel.vue
  • playground/pages/third-parties/plausible-analytics.vue
  • playground/pages/third-parties/reddit-pixel/nuxt-scripts.vue
  • playground/pages/third-parties/snapchat/nuxt-scripts.vue
  • playground/pages/third-parties/vercel-analytics/nuxt-scripts.vue
  • playground/pages/third-parties/x-pixel/nuxt-scripts.vue
  • playground/plugins/error-overlay.client.ts
  • src/first-party/index.ts
  • src/first-party/intercept-plugin.ts
  • src/first-party/partytown-resolve.ts
  • src/first-party/proxy-configs.ts
  • src/first-party/setup.ts
  • src/first-party/types.ts
  • src/module.ts
  • src/plugins/transform.ts
  • src/registry.ts
  • src/runtime/server/proxy-handler.ts
  • test/unit/auto-inject.test.ts
  • test/unit/first-party.test.ts
  • test/unit/gravatar-proxy.test.ts
  • test/unit/proxy-configs.test.ts

Comment on lines +133 to +149
// observe DOM for script/img/link/iframe elements with third-party src
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType !== Node.ELEMENT_NODE) continue
const el = node as Element
checkElement(el)
for (const child of el.querySelectorAll('[src],[href]'))
checkElement(child)
}
}
})

function checkElement(el: Element) {
const src = el.getAttribute('src') || (el.tagName === 'LINK' ? el.getAttribute('href') : '')
if (src) reportIfThirdParty(src)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

With MutationObserver, does observing { childList: true, subtree: true } detect later src/href changes on existing elements, or do you need { attributes: true, attributeFilter: ["src","href"] } to catch them?

💡 Result:

No, observing { childList: true, subtree: true } does NOT detect src or href changes on existing elements. You need { attributes: true, attributeFilter: ["src", "href"] } (or { attributes: true } without filter) to catch attribute changes on existing nodes. childList only monitors addition/removal of child nodes, not attribute modifications on existing nodes. subtree extends childList monitoring to descendants but does not add attribute watching. Attribute changes produce mutation records of type "attributes", which require the attributes option. Examples from sources: - To detect img src change: observer.observe(img, {attributes: true}) [1st search result 1] - MDN: { childList: true } monitors "addition of new child nodes or removal of existing child nodes" [1st search result 6, 2nd search result 8] - MDN example checks if (mutation.type === "attributes") separately from "childList" [1st search result 7] - javascript.info: childList for "changes in the direct children", attributes for "attributes of node" [1st search result 9] - StackOverflow confirms attributes: true needed for attribute changes like src [2nd search result 16]

Citations:


🏁 Script executed:

cat -n playground/plugins/error-overlay.client.ts | sed -n '130,170p'

Repository: nuxt/scripts

Length of output: 1683


Observe src/href attribute mutations as well as inserted nodes.

The observer at line 164 subscribes only to childList, so elements appended first and assigned src/href afterward will bypass detection. This causes false negatives in third-party request checks.

Add attributes: true with attributeFilter: ['src', 'href'] to the observer options, and handle attribute mutations in the callback:

Suggested fix
  const observer = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
+     if (mutation.type === 'attributes') {
+       checkElement(mutation.target as Element)
+       continue
+     }
      for (const node of mutation.addedNodes) {
        if (node.nodeType !== Node.ELEMENT_NODE) continue
        const el = node as Element
        checkElement(el)
        for (const child of el.querySelectorAll('[src],[href]'))
          checkElement(child)
      }
    }
  })
...
-  observer.observe(document.documentElement, { childList: true, subtree: true })
+  observer.observe(document.documentElement, {
+    childList: true,
+    subtree: true,
+    attributes: true,
+    attributeFilter: ['src', 'href'],
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playground/plugins/error-overlay.client.ts` around lines 133 - 149, Update
the MutationObserver usage (observer) to also observe attribute changes by
passing { childList: true, subtree: true, attributes: true, attributeFilter:
['src','href'] } and extend the observer callback to handle mutation.type ===
'attributes' by calling checkElement on mutation.target (or the element whose
src/href changed) and/or reading the new attribute value and passing it to
reportIfThirdParty; keep existing childList handling intact and use the existing
checkElement and reportIfThirdParty helpers to perform the same validation for
attribute mutations.

Comment on lines +517 to +520
if (config.partytown?.length && hasNuxtModule('@nuxtjs/partytown')) {
const partytownConfig = (nuxt.options as any).partytown || {}
if (!partytownConfig.resolveUrl) {
partytownConfig.resolveUrl = generatePartytownResolveUrl(interceptRules)
partytownConfig.resolveUrl = generatePartytownResolveUrl(proxyPrefix)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Auto resolveUrl check misses registry-level Partytown scripts.

Line 517 only checks config.partytown?.length. If users enable Partytown per script via registry scriptOptions.partytown = true (without shorthand), Line 520 never runs and first-party rewriting in the worker is skipped.

🔧 Proposed fix
-        if (config.partytown?.length && hasNuxtModule('@nuxtjs/partytown')) {
+        const hasPartytownScripts = !!config.partytown?.length
+          || Object.values(config.registry || {}).some((entry: any) =>
+            Array.isArray(entry) && !!entry[1]?.partytown)
+        if (hasPartytownScripts && hasNuxtModule('@nuxtjs/partytown')) {
           const partytownConfig = (nuxt.options as any).partytown || {}
           if (!partytownConfig.resolveUrl) {
             partytownConfig.resolveUrl = generatePartytownResolveUrl(proxyPrefix)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/module.ts` around lines 517 - 520, The current guard only checks
config.partytown?.length and misses registry-enabled Partytown flags; update the
conditional around hasNuxtModule('@nuxtjs/partytown') to also detect
registry-level Partytown by checking your registry entries for
scriptOptions.partytown === true (or truthy) before setting (nuxt.options as
any).partytown.resolveUrl via generatePartytownResolveUrl(proxyPrefix);
specifically, replace the single config.partytown?.length check with a predicate
that is true if config.partytown has entries OR any registry entry has
scriptOptions.partytown truthy, then set partytownConfig.resolveUrl as before.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/stats.ts (1)

520-522: Deduplicate extracted domains before sorting.

Line [521] sorts but does not dedupe, so repeated domains in config can inflate proxyEndpoints and duplicate proxyDomains in stats.

♻️ Proposed change
 function extractDomains(proxyDomains: string[]): string[] {
-  return [...proxyDomains].sort()
+  return [...new Set(proxyDomains)].sort()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/stats.ts` around lines 520 - 522, The extractDomains function currently
returns a sorted copy of proxyDomains but doesn't remove duplicates; update
extractDomains to deduplicate entries before sorting (e.g., convert proxyDomains
to a Set or use Array.from(new Set(...)) then sort) so proxyEndpoints and
proxyDomains in the stats logic no longer contain repeated domains; ensure the
function name extractDomains is modified in-place and tested to produce a
unique, alphabetically-sorted array.
test/unit/rewrite-ast.test.ts (1)

6-8: Consider extracting the shared deriveRewrites helper to a test utility.

This helper is duplicated in both rewrite-ast.test.ts and third-party-proxy-replacements.test.ts. Extracting it to a shared test utility (e.g., test/utils/proxy-test-helpers.ts) would reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/rewrite-ast.test.ts` around lines 6 - 8, Extract the duplicated
deriveRewrites helper into a shared test utility by creating a new module (e.g.,
test/utils/proxy-test-helpers.ts) that exports the function
deriveRewrites(domains: string[], proxyPrefix: string): ProxyRewrite[] with the
same signature and return shape; remove the local deriveRewrites definitions
from rewrite-ast.test.ts and third-party-proxy-replacements.test.ts and replace
them with imports from the new utility, and ensure any required types
(ProxyRewrite) are imported or re-exported from the utility so both tests
compile without other changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/stats.ts`:
- Around line 520-522: The extractDomains function currently returns a sorted
copy of proxyDomains but doesn't remove duplicates; update extractDomains to
deduplicate entries before sorting (e.g., convert proxyDomains to a Set or use
Array.from(new Set(...)) then sort) so proxyEndpoints and proxyDomains in the
stats logic no longer contain repeated domains; ensure the function name
extractDomains is modified in-place and tested to produce a unique,
alphabetically-sorted array.

In `@test/unit/rewrite-ast.test.ts`:
- Around line 6-8: Extract the duplicated deriveRewrites helper into a shared
test utility by creating a new module (e.g., test/utils/proxy-test-helpers.ts)
that exports the function deriveRewrites(domains: string[], proxyPrefix:
string): ProxyRewrite[] with the same signature and return shape; remove the
local deriveRewrites definitions from rewrite-ast.test.ts and
third-party-proxy-replacements.test.ts and replace them with imports from the
new utility, and ensure any required types (ProxyRewrite) are imported or
re-exported from the utility so both tests compile without other changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c4d2d08c-d642-4ffc-9e14-7964373ab8b4

📥 Commits

Reviewing files that changed from the base of the PR and between c504112 and b039f43.

📒 Files selected for processing (3)
  • src/stats.ts
  • test/unit/rewrite-ast.test.ts
  • test/unit/third-party-proxy-replacements.test.ts

The proxy handler previously forwarded requests to any domain, even
those not in the domainPrivacy allowlist. Now returns 403 for domains
not registered via proxy configs, preventing use as an open relay.

Also fix Error serialization in playground error overlay.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
playground/plugins/error-overlay.client.ts (1)

135-145: ⚠️ Potential issue | 🟠 Major

Still missing attribute mutation tracking for src/href changes after insertion.

Line 165 only observes childList, so elements appended first and updated later (el.src = ...) are not detected. This is still the same unresolved issue previously reported.

Suggested fix
   const observer = new MutationObserver((mutations) => {
     for (const mutation of mutations) {
+      if (mutation.type === 'attributes') {
+        checkElement(mutation.target as Element)
+        continue
+      }
       for (const node of mutation.addedNodes) {
         if (node.nodeType !== Node.ELEMENT_NODE) continue
         const el = node as Element
         checkElement(el)
         for (const child of el.querySelectorAll('[src],[href]'))
           checkElement(child)
       }
     }
   })
@@
-  observer.observe(document.documentElement, { childList: true, subtree: true })
+  observer.observe(document.documentElement, {
+    childList: true,
+    subtree: true,
+    attributes: true,
+    attributeFilter: ['src', 'href'],
+  })
#!/bin/bash
# Verify whether attribute mutation handling exists for src/href.
rg -n "MutationObserver|observer\\.observe|mutation\\.type === 'attributes'|attributeFilter" playground/plugins/error-overlay.client.ts

Also applies to: 165-165

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playground/plugins/error-overlay.client.ts` around lines 135 - 145, The
MutationObserver callback and/or its observe call currently only handles
childList mutations so later attribute updates (el.src/href after insertion) are
missed; update the observer.observe options to include attributes: true and
attributeFilter: ['src','href'] (and keep childList/subtree:true), and extend
the MutationObserver callback (the function referenced as observer) to handle
mutation.type === 'attributes' by calling checkElement(mutation.target as
Element) for each attribute mutation; keep the existing addedNodes handling but
add this attributes branch so src/href changes after insertion are detected.
🧹 Nitpick comments (1)
src/runtime/server/proxy-handler.ts (1)

109-110: Unreachable fallback and misleading comment.

The ?? true fallback on line 110 is now unreachable since lines 96-103 throw a 403 error when perScriptInput === undefined. The comment on line 109 ("missing domain → full anonymization") is also inconsistent with the current control flow where missing domains are rejected, not anonymized.

🧹 Suggested cleanup
-  // Resolve effective privacy: per-script is the base, global user override on top
-  // Fail-closed: missing domain → full anonymization (most restrictive)
-  const perScriptResolved = resolvePrivacy(perScriptInput ?? true)
+  // Resolve effective privacy: per-script is the base, global user override on top
+  const perScriptResolved = resolvePrivacy(perScriptInput)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/server/proxy-handler.ts` around lines 109 - 110, The fallback "??
true" and the comment about "missing domain → full anonymization" are
inconsistent because perScriptInput is already rejected earlier; remove the
unreachable fallback and update the comment to reflect that missing domains are
rejected, or if you want anonymization instead, move the fallback logic before
the 403 throw and call resolvePrivacy(perScriptInput ?? true) there; locate the
perScriptInput check that throws 403 and the subsequent const perScriptResolved
= resolvePrivacy(perScriptInput ?? true) (symbols: perScriptInput,
resolvePrivacy, perScriptResolved) and either delete the "?? true" and change
the comment to note rejection, or shift the fallback so anonymization happens
before the authorization check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@playground/plugins/error-overlay.client.ts`:
- Around line 70-76: The tooltip content is only built when the overlay click
handler runs, so it becomes stale while visible; refactor the tooltip text
construction into a reusable helper (e.g., updateTooltipContent or
buildTooltipText) that computes lines from the errors and warnings arrays and
sets tooltip.textContent (and tooltip.style.display as needed), then call that
helper both from the overlay click listener (currently building the text) and
from render() (the code that updates counters at render) so the tooltip is
refreshed whenever new events arrive while it is visible.

---

Duplicate comments:
In `@playground/plugins/error-overlay.client.ts`:
- Around line 135-145: The MutationObserver callback and/or its observe call
currently only handles childList mutations so later attribute updates
(el.src/href after insertion) are missed; update the observer.observe options to
include attributes: true and attributeFilter: ['src','href'] (and keep
childList/subtree:true), and extend the MutationObserver callback (the function
referenced as observer) to handle mutation.type === 'attributes' by calling
checkElement(mutation.target as Element) for each attribute mutation; keep the
existing addedNodes handling but add this attributes branch so src/href changes
after insertion are detected.

---

Nitpick comments:
In `@src/runtime/server/proxy-handler.ts`:
- Around line 109-110: The fallback "?? true" and the comment about "missing
domain → full anonymization" are inconsistent because perScriptInput is already
rejected earlier; remove the unreachable fallback and update the comment to
reflect that missing domains are rejected, or if you want anonymization instead,
move the fallback logic before the 403 throw and call
resolvePrivacy(perScriptInput ?? true) there; locate the perScriptInput check
that throws 403 and the subsequent const perScriptResolved =
resolvePrivacy(perScriptInput ?? true) (symbols: perScriptInput, resolvePrivacy,
perScriptResolved) and either delete the "?? true" and change the comment to
note rejection, or shift the fallback so anonymization happens before the
authorization check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ccb77ef0-d4c1-45d6-87cd-f53ecf1e53ac

📥 Commits

Reviewing files that changed from the base of the PR and between b039f43 and 25a9eb7.

📒 Files selected for processing (2)
  • playground/plugins/error-overlay.client.ts
  • src/runtime/server/proxy-handler.ts

Comment on lines +70 to +76
overlay.addEventListener('click', () => {
if (tooltip.style.display === 'none') {
const lines: string[] = []
for (const msg of errors) lines.push(`[ERR] ${msg}`)
for (const msg of warnings) lines.push(`[WARN] ${msg}`)
tooltip.textContent = lines.length > 0 ? lines.join('\n') : 'No issues'
tooltip.style.display = 'block'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tooltip content becomes stale while it is open.

While render() updates counters on new events (Lines 93-113), tooltip text is only rebuilt when opening (Lines 70-76). If the tooltip is visible, fresh issues are not shown until it is closed/reopened.

Suggested fix
+  function renderTooltip() {
+    const lines: string[] = []
+    for (const msg of errors) lines.push(`[ERR] ${msg}`)
+    for (const msg of warnings) lines.push(`[WARN] ${msg}`)
+    tooltip.textContent = lines.length > 0 ? lines.join('\n') : 'No issues'
+  }
+
   function render() {
@@
     overlay.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)'
     overlay.textContent = parts.join(' | ')
+    if (tooltip.style.display !== 'none')
+      renderTooltip()
   }
@@
   overlay.addEventListener('click', () => {
     if (tooltip.style.display === 'none') {
-      const lines: string[] = []
-      for (const msg of errors) lines.push(`[ERR] ${msg}`)
-      for (const msg of warnings) lines.push(`[WARN] ${msg}`)
-      tooltip.textContent = lines.length > 0 ? lines.join('\n') : 'No issues'
+      renderTooltip()
       tooltip.style.display = 'block'
     }

Also applies to: 93-113

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playground/plugins/error-overlay.client.ts` around lines 70 - 76, The tooltip
content is only built when the overlay click handler runs, so it becomes stale
while visible; refactor the tooltip text construction into a reusable helper
(e.g., updateTooltipContent or buildTooltipText) that computes lines from the
errors and warnings arrays and sets tooltip.textContent (and
tooltip.style.display as needed), then call that helper both from the overlay
click listener (currently building the text) and from render() (the code that
updates counters at render) so the tooltip is refreshed whenever new events
arrive while it is visible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant