Skip to content

chore: upgrade Fullstory to snippet 2.1 and skip it without an org ID - #7061

Merged
jennifer-shehane merged 3 commits into
mainfrom
fullstory/v2-snippet
Nov 6, 2026
Merged

jennifer-shehane merged 3 commits into
mainfrom
fullstory/v2-snippet

Conversation

@jennifer-shehane

@jennifer-shehane jennifer-shehane commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Summary

Replaces the Fullstory v1.3.0 snippet in plugins/fullstory.js with Fullstory's current 2.1.0 snippet, and stops adding the snippet to pages when FULLSTORY_ORG_ID is unset or empty.

Why

  • Outdated snippet. The site still shipped the legacy v1.3.0 snippet. Fullstory's Browser API v2 needs snippet 2.0 or later, and 2.1.0 is current.
  • Broken org ID without the variable. The org ID went into a hand-quoted string, so any production build without FULLSTORY_ORG_ID (including every CI build) shipped _fs_org = 'undefined' and loaded fs.js for an org that doesn't exist.

Production now builds on Cloudflare Workers Builds, and FULLSTORY_ORG_ID is set there as a build variable.

What changed

  • Snippet source. The snippet is SnippetSrc from Fullstory's @fullstory/snippet@2.1.0 npm package, copied verbatim. Fullstory's developer docs don't publish the snippet text; they point to the in-app Fullstory Setup page, and the snippet on that page matches the package's 2.1.0 code.
  • Arguments instead of globals. Snippet 2.1 takes the host, script, namespace, and org ID as arguments rather than window._fs_* globals. The values haven't changed: fullstory.com, edge.fullstory.com/s/fs.js, and FS.
  • Org ID handling. JSON.stringify fills all four values, so the org ID lands as a quoted, escaped string literal.
  • No snippet without an org ID. The plugin returns no head tags when FULLSTORY_ORG_ID is unset or empty, in addition to the existing non-production check.
  • Header comment. A short comment explains why the snippet is gated and how to upgrade it, following the code comment conventions in AGENTS.md.
  • No v1 calls to convert. I searched the repo (outside node_modules, dist, and .docusaurus) for v1-style calls (FS.identify, FS.event, FS.setUserVars, FS.consent, FS.shutdown, FS.restart, and the other helpers). None exist outside the old snippet.

How this was tested

  • Plugin called directly:
    • No tags in production with the org ID unset or empty.
    • No tags in a development build.
    • In production with the org ID set, one tag, byte-identical to the package's SnippetSrc with its placeholders filled.
  • npm run build with FULLSTORY_ORG_ID unset: 0 of 339 HTML files contain edge.fullstory.com.
  • FULLSTORY_ORG_ID=test-org npm run build:
    • dist/api/commands/visit/index.html has one Fullstory script in <head>, ending "FS","test-org"), with no undefined.
    • node --check on the extracted script passes.
  • Headless Chromium run of that script (with fs.js stubbed): it loads edge.fullstory.com/s/fs.js?org=test-org, sets data-fs-namespace="FS" and FS._v to 2.1.0, and queues init with the right org, host, and script.
  • Lint: npm run lint:fix and Prettier are clean.

Docusaurus minifies inline head scripts at build time, so the deployed page holds a minified version of the snippet. The source file keeps the verbatim copy, and the browser run above exercised the minified output.

Not tested: recording against the real Fullstory org, which needs production traffic.

Notes for reviewers

  • No Fullstory in PR previews. The cloudflare/preview check uploads the CI build, and CI doesn't set FULLSTORY_ORG_ID. That's expected.
  • Still to check: preview build variables. If Workers Builds preview builds are enabled, FULLSTORY_ORG_ID should be set only for production builds, so branch previews don't send sessions to the production org.
  • Still to check: Osano consent. The script address now ends in ?org=<id>. If Osano identifies Fullstory by its script address, confirm it still blocks Fullstory after a visitor declines. To check locally:
    1. Run FULLSTORY_ORG_ID=test-org npm run build, then npm run serve.
    2. Decline cookies in the Osano banner.
    3. Confirm fs.js doesn't load.
  • Out of scope: consent gating, page properties, and masking are handled in separate tasks.

Generated by Claude Code


Note

Medium Risk
Changes production-only third-party script injection and URL shape (?org=), which can affect session recording and consent/blocking behavior if those tools key off the old script URL.

Overview

Summary

Replaces the legacy Fullstory 1.3.0 inline bootstrapping code in plugins/fullstory.js with the 2.1.0 minified snippet (from @fullstory/snippet), passed host, script path, namespace, and org via JSON.stringify so values are safe string literals. Injection is skipped when the build is not production or when FULLSTORY_ORG_ID is unset/empty, so dev and CI builds no longer load edge.fullstory.com or send sessions for a non-existent org.

Why

Fullstory’s current browser stack expects snippet 2.x; the old 1.3.0 path was outdated. The previous org ID was interpolated into JS without validation, so missing FULLSTORY_ORG_ID in prod-like builds produced 'undefined' and still requested fs.js. Production now has a real org ID in the deploy environment; gating on orgId matches that and avoids bad data.

Notes for reviewers

The file header documents copying verbatim SnippetSrc for future upgrades—prefer replacing the block over hand-editing. PR previews / CI stay without Fullstory if the org var is unset (expected). If Osano or other tooling matches Fullstory by script URL, the loader URL now includes ?org=…; worth a quick smoke check that blocking still works after cookie decline.

Closes #

Reviewed by Cursor Bugbot for commit 772fd3b. Bugbot is set up for automated code reviews on this repo. Configure here.

The docs site still shipped Fullstory's legacy v1.3.0 snippet. Fullstory's
Browser API v2 requires snippet 2.0 or later, and 2.1 is current, so we were
running on a snippet Fullstory no longer documents.

The org ID was also wrapped in hand-written quotes, so any production build
without FULLSTORY_ORG_ID (including every CI build) shipped
_fs_org = 'undefined' on every page and loaded fs.js for an org that doesn't
exist.

- Replace the snippet with SnippetSrc from @fullstory/snippet@2.1.0,
  copied verbatim. Its host, script, namespace, and org ID placeholders
  are filled with JSON.stringify, the same way the package's
  injectSnippetArgs() fills them. Snippet 2.1 takes these as arguments
  instead of window._fs_* globals; the values are unchanged
  (fullstory.com, edge.fullstory.com/s/fs.js, FS).
- Inject nothing when FULLSTORY_ORG_ID is unset or empty, in addition
  to the existing non-production check.
- Document at the top of the plugin when the snippet is injected and
  where it comes from.

No v1-style FS.identify/event/setUserVars/consent/shutdown/restart calls
exist elsewhere in the repo, so nothing else needed converting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r1UmXksNKFmsHaYoTF
Use active voice for how the placeholders get filled, and separate the
dev server case from builds that lack FULLSTORY_ORG_ID so each sentence
describes one behavior. No code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r1UmXksNKFmsHaYoTF
The new code comment rules in AGENTS.md ask for the why only. Drop the
parts that restated the production and org ID check, and give the reason
for JSON.stringify (a quoted, escaped literal) instead of pointing at the
package function it mirrors. No code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUv8r1UmXksNKFmsHaYoTF
@jennifer-shehane jennifer-shehane self-assigned this Nov 6, 2026
@jennifer-shehane
jennifer-shehane merged commit 16522ef into main Nov 6, 2026
21 checks passed
@jennifer-shehane
jennifer-shehane deleted the fullstory/v2-snippet branch November 6, 2026 20:00
@cypress

cypress Bot commented Nov 6, 2026

Copy link
Copy Markdown

cypress-documentation    Run #2795

Run Properties:  status check passed Passed #2795  •  git commit ca0b09b80a ℹ️: Merge 772fd3b821c736bf4f639e2485c2777697870af7 into fc274e3d59db489fef1d37cc8028...
Project cypress-documentation
Branch Review fullstory/v2-snippet
Run status status check passed Passed #2795
Run duration 02m 16s
Commit git commit ca0b09b80a ℹ️: Merge 772fd3b821c736bf4f639e2485c2777697870af7 into fc274e3d59db489fef1d37cc8028...
Committer Jennifer Shehane
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 427
View all changes introduced in this branch ↗︎
UI Coverage  32.91%
  Untested elements 738  
  Tested elements 26  
Accessibility  98.89%
  Failed rules  3 critical   5 serious   4 moderate   1 minor
  Failed elements 75  

@cypress

cypress Bot commented Nov 6, 2026

Copy link
Copy Markdown

cypress-documentation    Run #2796

Run Properties:  status check passed Passed #2796  •  git commit 16522ef1f7: chore: upgrade Fullstory to snippet 2.1 and skip it without an org ID (#7061)
Project cypress-documentation
Branch Review main
Run status status check passed Passed #2796
Run duration 02m 07s
Commit git commit 16522ef1f7: chore: upgrade Fullstory to snippet 2.1 and skip it without an org ID (#7061)
Committer Jennifer Shehane
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 427
View all changes introduced in this branch ↗︎
UI Coverage  32.91%
  Untested elements 738  
  Tested elements 26  
Accessibility  98.91%
  Failed rules  2 critical   5 serious   4 moderate   1 minor
  Failed elements 73  

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.

2 participants