Skip to content

Standardize UMD global API strategy across rrweb packages #1786

@Juice10

Description

@Juice10

Problem

UMD usage is inconsistent across packages today. Some packages use a shared global (window.rrweb), while others use package-specific globals. This causes API confusion and load-order instability.

Why this matters

  • Script-tag consumers can get different behavior depending on bundle load order.
  • Docs and examples become hard to keep consistent.
  • It is unclear whether UMD should optimize for shared namespace ergonomics or package isolation.

Current behavior (verified, post-PR #1762)

  1. Core modular packages currently use the same UMD global name:
  • @rrweb/all -> rrweb
  1. Many other packages use package-specific globals:
  • rrweb-player -> rrwebPlayer
  • rrdom -> rrdom
  • rrdom-nodejs -> rrdomNodejs
  • rrweb-snapshot -> rrwebSnapshot
  • @rrweb/record -> rrwebRecord
  • @rrweb/replay -> rrwebReplay
  • @rrweb/types -> rrwebTypes
  • @rrweb/utils -> rrwebUtils
  • plugins -> rrwebPlugin...

Timeline relative to PR #1762

Reference: #1762

Before PR #1762:

  • UMD behavior was already mixed across packages.
  • Strategy 1 (see below) was effectively used for @rrweb/all, @rrweb/record, @rrweb/replay.
  • Strategy 2 (see below) was effectively used for most other packages.
  • Shared-global overwrite happens in practice:
    • Load @rrweb/record UMD first -> rrweb.record exists.
    • Load @rrweb/replay UMD second -> rrweb.Replayer exists, and rrweb.record is gone.
    • This is effectively “last one wins” for shared-global bundles.

After PR #1762 (current state):

  • Directionally, the project is moving toward package-specific globals for modular packages.
  • Mixed behavior/config still exists in practice.
  • See the Current behavior section above for the exact verified package matrix.

Target state:

  • One explicit, documented UMD policy across all packages.
  • Build config and runtime behavior match that policy consistently.
  • Docs/examples and tests enforce the selected behavior.

API matrix discussed

Package Strategy 1: Shared-global model Stategy 2: Package-specific model Strategy 3: Hybrid model
rrweb window.rrweb.record, window.rrweb.Replayer window.rrweb.record, window.rrweb.Replayer window.rrweb.record, window.rrweb.Replayer
@rrweb/all window.rrweb.record, window.rrweb.Replayer, window.rrweb.pack, window.rrweb.unpack window.rrwebAll.record, window.rrwebAll.Replayer, window.rrwebAll.pack, window.rrwebAll.unpack window.rrweb.record, window.rrweb.Replayer, window.rrweb.pack, window.rrweb.unpack
@rrweb/record window.rrweb.record, window.rrweb.record.addCustomEvent, window.rrweb.record.freezePage, window.rrweb.record.takeFullSnapshot window.rrwebRecord.record, window.rrwebRecord.addCustomEvent, window.rrwebRecord.freezePage, window.rrwebRecord.takeFullSnapshot window.rrwebRecord.record, window.rrwebRecord.addCustomEvent, window.rrwebRecord.freezePage, window.rrwebRecord.takeFullSnapshot
@rrweb/replay window.rrweb.Replayer window.rrwebReplay.Replayer window.rrwebReplay.Replayer
@rrweb/packer window.rrweb.pack, window.rrweb.unpack window.rrwebPacker.pack, window.rrwebPacker.unpack window.rrwebPacker.pack, window.rrwebPacker.unpack
rrweb-player window.rrweb.Player window.rrwebPlayer.Player window.rrwebPlayer.Player
rrweb-snapshot window.rrweb.snapshot, window.rrweb.rebuild window.rrwebSnapshot.snapshot, window.rrwebSnapshot.rebuild window.rrwebSnapshot.snapshot, window.rrwebSnapshot.rebuild
rrdom window.rrweb.RRDocument, window.rrweb.diff window.rrdom.RRDocument, window.rrdom.diff window.rrdom.RRDocument, window.rrdom.diff
rrdom-nodejs n/a (node-focused) window.rrdomNodejs.* (mostly not browser usage) n/a (node-focused)
rrvideo n/a (node-focused) n/a (node-focused) n/a (node-focused)
@rrweb/types window.rrweb.EventType, window.rrweb.IncrementalSource, etc. window.rrwebTypes.EventType, window.rrwebTypes.IncrementalSource, etc. window.rrwebTypes.EventType, window.rrwebTypes.IncrementalSource, etc.
@rrweb/utils window.rrweb.utils.patch window.rrwebUtils.patch window.rrwebUtils.patch
@rrweb/web-extension n/a (extension package) n/a (extension package) n/a (extension package)
@rrweb/rrweb-plugin-console-record window.rrweb.plugins.getRecordConsolePlugin window.rrwebPluginConsoleRecord.getRecordConsolePlugin window.rrwebPluginConsoleRecord.getRecordConsolePlugin
@rrweb/rrweb-plugin-console-replay window.rrweb.plugins.getReplayConsolePlugin window.rrwebPluginConsoleReplay.getReplayConsolePlugin window.rrwebPluginConsoleReplay.getReplayConsolePlugin
@rrweb/rrweb-plugin-sequential-id-record window.rrweb.plugins.getRecordSequentialIdPlugin window.rrwebPluginSequentialIdRecord.getRecordSequentialIdPlugin window.rrwebPluginSequentialIdRecord.getRecordSequentialIdPlugin
@rrweb/rrweb-plugin-sequential-id-replay window.rrweb.plugins.getReplaySequentialIdPlugin window.rrwebPluginSequentialIdReplay.getReplaySequentialIdPlugin window.rrwebPluginSequentialIdReplay.getReplaySequentialIdPlugin
@rrweb/rrweb-plugin-canvas-webrtc-record window.rrweb.plugins.RRWebPluginCanvasWebRTCRecord window.rrwebPluginCanvasWebRTCRecord.RRWebPluginCanvasWebRTCRecord window.rrwebPluginCanvasWebRTCRecord.RRWebPluginCanvasWebRTCRecord
@rrweb/rrweb-plugin-canvas-webrtc-replay window.rrweb.plugins.RRWebPluginCanvasWebRTCReplay window.rrwebPluginCanvasWebRTCReplay.RRWebPluginCanvasWebRTCReplay window.rrwebPluginCanvasWebRTCReplay.RRWebPluginCanvasWebRTCReplay

Strategy options

Strategy 1: Shared global (window.rrweb) for all packages.

  • Pros: concise UMD API, familiar namespace, old v1 docs mostly apply to new packages
  • Cons: collisions
    • Without build tool changes: last import overwrites the earlier imports
      • eg. @rrweb/record + @rrweb/replay: rrweb.record is overwritten by replay's window.rrweb = ...
    • With build tool changes: collisions still possible on method name when multiple packages are imported
      • eg. @rrweb/all + @rrweb/pack both define rrweb.pack
      • quite hard to detect
    • We've been using strategy 2 for most packages since (apart from 3) since the .alphas were introduced a while ago. We can still change it because it is marked as alpha but it will break some people's setups.

Stategy 2: Package-specific globals for modular packages.

  • Pros: package isolation, clearer ownership, no collision risk.
  • Cons: more verbose UMD API, documentation not 1:1 with rrweb v1 (eg. rrwebRecord.record instead of rrweb.record)

Strategy 3: Package-specific globals - except for @rrweb/all and rrweb

  • Keep rrweb global for monolithic bundle only (@rrweb/all and rrweb).
  • Use package-specific globals for all other packages.
  • Define compatibility behavior explicitly.

Decision requested

Pick one strategy and document it as the official UMD contract.

Acceptance criteria

  • One documented UMD/global policy for all packages.
  • Build config aligned with that policy.
  • Docs/examples aligned with that policy.
  • Load-order tests covering shared-global conflict cases.
  • Clear migration notes for any breaking UMD/global changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions