Skip to content

fix(core): load Osano consent script async via client module#262

Open
marythought wants to merge 9 commits intomainfrom
fix/osano-async-loading
Open

fix(core): load Osano consent script async via client module#262
marythought wants to merge 9 commits intomainfrom
fix/osano-async-loading

Conversation

@marythought
Copy link
Copy Markdown
Contributor

@marythought marythought commented Mar 26, 2026

Summary

  • Move Osano CMP script from synchronous headTags to a Docusaurus client module (src/theme/OsanoLoader.js)
  • Only load Osano on production hosts (opentdf.io) via an allowlist — skipped on localhost, Surge previews, and all other non-production environments
  • Add querySelector guard against duplicate injection, script.onerror handler, and safe window.openOsanoPreferences() wrapper with pre-hydration guard
  • Add 200.html SPA fallback in Surge preview workflows

Root Cause

Docusaurus strips the async attribute from headTags script elements. The Osano CMP script blocks page loading when cmp.osano.com is slow or unreachable — preventing React hydration, chunk loading, and all client-side navigation. Even loading async via appendChild in a client module still blocks when the CDN is unreachable.

Confirmed by systematic bisect (2026-04-03):

Config Local nav works?
Pre-Osano config (commit 13d5235) Yes
+ consent defaults inline script Yes
+ GTM loader script Yes
+ Osano via client module (async, no host gate) No
+ Osano via client module (production allowlist) Yes

Changes

  • docusaurus.config.ts: Remove Osano from headTags, register OsanoLoader.js as client module, update footer to use window.openOsanoPreferences?.() (safe before hydration)
  • src/theme/OsanoLoader.js: New client module — injects Osano only on OSANO_HOSTS (opentdf.io, www.opentdf.io), exposes safe preferences wrapper everywhere
  • .github/workflows/surge-preview.yaml, surge-preview-deploy.yaml: Add 200.html SPA fallback for Surge

Test plan

  • Local dev navigation works (npm start)
  • Surge preview deploys and navigates correctly
  • npm run build succeeds

🤖 Generated with Claude Code

@marythought marythought requested review from a team as code owners March 26, 2026 18:45
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removed the inline Osano CMP script from Docusaurus config, added a client module that injects the Osano script and exposes window.openOsanoPreferences, and added post-build steps in two Surge workflows to copy build/index.htmlbuild/200.html after successful builds.

Changes

Cohort / File(s) Summary
Docusaurus config
docusaurus.config.ts
Removed Osano CMP <script> from headTags; added ./src/theme/OsanoLoader.js to clientModules; updated footer "Preferences" handler to call window.openOsanoPreferences() instead of Osano.cm.showDrawer(...).
Osano loader
src/theme/OsanoLoader.js
New client module that, when DOM is available, injects async Osano CMP <script> into document.head if absent and assigns window.openOsanoPreferences = () => window.Osano?.cm?.showDrawer?.('osano-cm-dom-info-dialog-open'); exports default {}.
CI / Surge preview workflows
.github/workflows/surge-preview.yaml, .github/workflows/surge-preview-deploy.yaml
Added conditional post-build steps to copy build/index.htmlbuild/200.html when the build step succeeds to ensure SPA fallback on Surge deployments.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser (Client)
    participant Loader as OsanoLoader (clientModule)
    participant Head as DocumentHead (DOM)
    participant Osano as OsanoCMP (External Script)

    rect rgba(200,230,255,0.5)
    Browser->>Loader: page loads (DOM available)
    Loader->>Head: check for existing Osano script
    alt script missing
        Loader->>Head: append async `<script src="https://cmp.osano.com/.../osano.js">`
    end
    end

    rect rgba(200,255,200,0.5)
    Note over Osano,Browser: External script downloads & initializes asynchronously
    Osano-->>Browser: exposes `window.Osano.cm.showDrawer`
    Loader->>Browser: set `window.openOsanoPreferences()` (calls Osano when available)
    end

    rect rgba(255,230,200,0.5)
    Browser->>Browser: user clicks "Preferences"
    Browser->>Loader: invokes `window.openOsanoPreferences()`
    Loader->>Osano: calls `window.Osano?.cm?.showDrawer?.('osano-cm-dom-info-dialog-open')`
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • CMP Update #258: Also modifies how the Osano CMP is included and how the footer "Preferences" action opens Osano (client-side loading / handler changes).

Poem

🐰 I hopped through code and left a trail,
I moved the script to load in browsers' gale,
I plant a key on window's sill,
Tap "Preferences" — Osano answers still. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately describes the main change: moving the Osano CMP script loading from synchronous headTags to an async client module, which directly addresses the core issue of blocking hydration.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/osano-async-loading

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.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the Osano script loading by moving it from a static script tag in docusaurus.config.ts to a dynamic loader in a new client module, src/theme/OsanoLoader.js. A review comment suggests extracting the hardcoded script URL into a constant to improve maintainability.

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/theme/OsanoLoader.js`:
- Around line 3-9: The Osano script loader in OsanoLoader.js appends the script
unconditionally causing duplicate injection and race errors when footer onclick
calls Osano.cm.showDrawer; update the loader to first check
document.querySelector for the Osano script src or an id before
creating/appending the script, attach script.onerror to log/handle load
failures, and expose a safe wrapper function window.openOsanoPreferences() that
calls window.Osano?.cm?.showDrawer(...) with optional chaining to avoid
exceptions if the script hasn’t loaded yet; finally change footer invocation to
call window.openOsanoPreferences() instead of Osano.cm.showDrawer().
🪄 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: 16f21428-cd35-42e3-8f8b-d855cf7dda91

📥 Commits

Reviewing files that changed from the base of the PR and between c7a2653 and 8eaabb6.

📒 Files selected for processing (2)
  • docusaurus.config.ts
  • src/theme/OsanoLoader.js

@github-actions
Copy link
Copy Markdown
Contributor

📄 Preview deployed to https://opentdf-docs-pr-262.surge.sh

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/surge-preview.yaml:
- Around line 43-45: Add the same SPA fallback step used in the surge-preview
workflow to the manual deployment workflow by inserting a step that runs the
command `cp build/index.html build/200.html` after the build step in
surge-preview-deploy.yaml (mirror the behavior of the existing "Add SPA fallback
for Surge" step) so manual deployments also serve 200.html for client-side
routes.
🪄 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: 19cccb46-d322-4f8e-9c78-97321a15a324

📥 Commits

Reviewing files that changed from the base of the PR and between 8eaabb6 and 68150d0.

📒 Files selected for processing (1)
  • .github/workflows/surge-preview.yaml

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docusaurus.config.ts`:
- Line 236: The footer button currently calls window.openOsanoPreferences
directly causing a ReferenceError during SSR or before client hydration; change
the onclick to safely guard the call (e.g., check typeof
window.openOsanoPreferences === 'function' before invoking) or remove the inline
onclick and wire the click handler from the client-side OsanoLoader
initialization so clicks before hydration no-op; update the element that renders
the button to use the guarded call (reference: window.openOsanoPreferences and
the client module OsanoLoader.js) so the handler degrades gracefully when the
client module hasn't loaded yet.
🪄 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: dd8b6dcf-8324-4559-9463-587e95711f89

📥 Commits

Reviewing files that changed from the base of the PR and between 887c8a4 and c3b7449.

📒 Files selected for processing (1)
  • docusaurus.config.ts

@marythought marythought changed the title fix(core): load Osano consent script async via client module chore(core): load Osano consent script via client module Apr 3, 2026
@marythought marythought changed the title chore(core): load Osano consent script via client module fix(core): load Osano consent script async via client module Apr 3, 2026
marythought and others added 7 commits April 3, 2026 10:50
The synchronous Osano CMP script in headTags blocks Docusaurus
client-side hydration when the consent service is unreachable (local
dev, surge previews), breaking interactive components like tabs.

Docusaurus strips the async attribute from headTags script elements,
so we load Osano via a client module instead, which runs after
hydration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Surge requires a 200.html file to handle client-side routing fallback.
Without it, Docusaurus SPA navigation breaks when navigating between
doc sections (e.g., Components → SDKs) because Surge returns a 404
for routes not matching a static file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches the same 200.html fallback added to the auto-deploy workflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… errors

- Extract URL into constant and check for existing script before appending
- Add safe wrapper window.openOsanoPreferences() with optional chaining
- Update footer onclick to use wrapper instead of calling Osano.cm directly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds script.onerror handler to log a warning if the Osano CMP script
fails to load, addressing CodeRabbit review feedback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Osano CMP script blocks page loading when cmp.osano.com is
unreachable or slow (localhost, some preview environments), breaking
React hydration and client-side navigation. Gate the script injection
on hostname so it only loads in production and deployed previews.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace localhost exclusion with a production hostname allowlist
(OSANO_HOSTS). Osano only loads on opentdf.io — skipped on localhost,
Surge previews, and any other non-production environment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@marythought marythought force-pushed the fix/osano-async-loading branch from ca6d298 to beebbb2 Compare April 3, 2026 17:51
marythought and others added 2 commits April 3, 2026 10:53
Use optional chaining on the onclick handler so pre-hydration clicks
degrade silently instead of throwing a ReferenceError.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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