Skip to content

[PANA-8578] Add support for session replay string namespaces - #428

Draft
sethfowler-datadog wants to merge 1 commit into
masterfrom
seth.fowler/PANA-8578-add-support-for-session-replay-string-namespaces
Draft

[PANA-8578] Add support for session replay string namespaces#428
sethfowler-datadog wants to merge 1 commit into
masterfrom
seth.fowler/PANA-8578-add-support-for-session-replay-string-namespaces

Conversation

@sethfowler-datadog

@sethfowler-datadog sethfowler-datadog commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

PANA-8578 Add support for session replay string namespaces

Summary

Adds string namespaces to the browser Session Replay "Change" data format. A string namespace is a separate underlying store of strings, distinct from the single flat string table the format uses today.

This PR lands the schema vocabulary and the two new change opcodes needed to create namespaces and add strings to them. It does not land the mapping mechanism that connects namespaces to the string table — that's deliberately deferred to a follow-up PR (see Scope).

Motivation

Splitting strings across multiple namespaces buys us two things:

  1. Recording size. Stylesheets contain very different strings than the DOM. Partitioning them keeps each namespace smaller, and — crucially — keeps the indices into each namespace smaller when serialized as JSON numbers. This reduces recording size both pre- and post-compression.

  2. Downstream transformations. Some transformations should apply to some strings but not others. For example, we may want to mask text that appeared on the page when a less-privileged user views the replay, while leaving structural text like tag names and attribute names unmasked. Partitioning those into separate namespaces makes this a namespace-level operation rather than a per-string classification problem.

Terminology

The format previously used "string table" for a single flat store. With multiple stores in play, this PR fixes the terminology and applies it consistently across every description in the schema:

Term Meaning
string table The single virtual table that string references index into.
string namespace An underlying, non-virtual store of strings, mapped into the string table.

Ultimately, string references remain indices into one virtual string table, working analogously to virtual memory: strings are added to whichever namespace they belong in, and regions of those namespaces are mapped into the shared string table at particular positions. This preserves the simplicity and compactness of "a string reference is just a number," while enabling later work such as rearranging recorded strings to shrink reference sizes, or dropping unmapped regions to reduce memory usage.

Changes

New change opcodes

Opcode Type Shape Purpose
11 AddStringNamespaceChange StringNamespaceName Creates a new string namespace, identified by name.
12 AddNamespacedStringChange [StringNamespaceOrStringNamespaceReference, ...StringLiteral[]] Adds a sequence of strings to a string namespace.

Namespace references are assigned by creation order: 0 is the default string namespace, and each AddStringNamespaceChange takes the next available reference. This matches how string indices already work for opcode 0.

// Creates namespaces "dom" (1), "css" (2), "user-text" (3)
[11, "dom", "css", "user-text"]

// Adds strings, addressing namespaces by name (explicit form)
[12, ["dom", "div", "span"], ["css", "div { color: blue }"]]

// The same content, addressing namespaces by reference (recording form)
[12, [1, "div", "span"], [2, "div { color: blue }"]]

New schema files

All under schemas/session-replay/browser/changes/:

  • add-string-namespace-change-schema.jsonAddStringNamespaceChange
  • add-namespaced-string-change-schema.jsonAddNamespacedStringChange
  • string-references/string-literal-schema.jsonStringLiteral
  • string-references/namespaced-string-literal-schema.jsonNamespacedStringLiteral
  • string-references/string-namespace-name-schema.jsonStringNamespaceName
  • string-references/string-namespace-reference-schema.jsonStringNamespaceReference
  • string-references/string-namespace-or-string-namespace-reference-schema.jsonStringNamespaceOrStringNamespaceReference

The naming now rhymes across both reference kinds:

Literal form Numeric form Union
String StringLiteral StringReference StringOrStringReference
Namespace StringNamespaceName StringNamespaceReference StringNamespaceOrStringNamespaceReference

Explicit vs. recording representations

Real recordings replace every string with a string reference, and will always address namespaces by numeric reference. The literal forms exist so the format can also express a more explicit representation — useful both as input to an encoder that builds the tables, and as output when presenting a recording in a form a human can read.

Accordingly, StringOrStringReference gains a third variant, NamespacedStringLiteral, which pairs a string with the namespace it belongs to:

{ "namespace": "user-text", "string": "Hello" }

Both properties are mandatory and no others are permitted. namespace accepts either form, so the same value can be written { "namespace": 3, "string": "Hello" } in compact form.

Library changes

  • ChangeType gains AddStringNamespace (11) and AddNamespacedString (12). These are guarded by the existing ChangeTypeId helper, so a constant that disagrees with the schema is a compile error.
  • Added a branded StringNamespaceId, alongside the existing branded NodeId / StringId / StyleSheetId, so namespace references can't be silently confused with the other numeric identifier spaces.
  • Regenerated lib/cjs and lib/esm.

Scope

The mapping mechanism is deliberately not included in this PR. Nothing here connects a namespace to a region of the string table, so namespaces are declared and populated but not yet addressable via StringReference. This is the subtle part of the design and gets its own PR.

Compatibility

Additive. Existing opcodes keep their discriminators — the two new ones are appended as 11 and 12 — so previously recorded data is unaffected and existing producers need no changes.

One consumer-visible note for TypeScript users: StringOrStringReference widens from string | StringReference to StringLiteral | NamespacedStringLiteral | StringReference. Code that exhaustively narrows this type will need a case for the object variant. In practice recordings will never contain one, but the type is now wider.

@sethfowler-datadog
sethfowler-datadog force-pushed the seth.fowler/PANA-8578-add-support-for-session-replay-string-namespaces branch from f072af4 to ad0c07a Compare August 13, 2026 09:09
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