Skip to content

Conversation

@sethfowler-datadog
Copy link
Contributor

@sethfowler-datadog sethfowler-datadog commented Dec 5, 2025

Motivation

The current DOM serialization algorithm serializes a node's children before assigning it an id. This results in a post-order assignment, like this:

#document  // id: 5
  <html>  // id: 4
    <body>  // id: 3
      <div></div>  // id:1
      <p></p>  // id: 2

This is a fine solution for the current data format; node ids are recorded explicitly, so we can assign them using whatever scheme we wish. However, the new format assigns node ids implicitly in the order that the nodes appear, which means that parent nodes will naturally be assigned a node id before their descendants. The new format uses a pre-order assignment, which looks like this:

#document  // id: 0
  <html>  // id: 1
    <body>  // id: 2
      <div></div>  // id:3
      <p></p>  // id: 4

This difference in node id numbering is the biggest source of complexity when comparing the output of the current DOM serialization algorithm with the output of the new one. It'd be ideal if both algorithms used the same ids; this would make translation between their representations straightforward.

The good news is that, because the current data format records node ids explicitly, there's no backwards compatibility issue with simply changing how node ids are assigned. So, we can switch the existing algorithm to use a pre-order assignment, and eliminate this impedance mismatch between the two algorithms.

Changes

This PR:

  1. Moves the code for assigning an id to a node from serializeNodeWithId() to SerializationTransaction#assignId().
  2. Updates each of the DOM node serialization functions to assign ids when each serialized node is constructed, using SerializationTransaction#assignId() to do the work.
  3. Removes serializeNodeWithId(), which now does nothing, and replaces calls to it with direct calls to serializeNode().
  4. Changes the first node id we'll assign from 1 to 0, for better alignment between the old and new algorithms.
  5. Updates the small number of tests that needed to be adjusted.

Test instructions

You can test the changes using the "Live Replay" tab of the browser SDK extension.

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.

@sethfowler-datadog sethfowler-datadog requested a review from a team as a code owner December 5, 2025 15:41
Comment on lines +476 to 478
const expectedHost = expectNewNode({ type: NodeType.Element, tagName: 'div' })
const shadowRootNode = expectNewNode({ type: NodeType.DocumentFragment, isShadowRoot: true })
const child = expectNewNode({ type: NodeType.Element, tagName: 'span' })
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The helper function expectNewNode() assigns ids to each node in order, so we have to reverse the order of these calls to get ids that match the new approach.


expect(serializeDocumentNode(document, NodePrivacyLevel.ALLOW, transaction)).toEqual({
type: NodeType.Document,
id: 0,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was necessary to add this because serializeDocumentNode() used to return a node with no id assigned, but now it returns a node with an id. Conveniently, the id of the document node is always zero with pre-order assignment!

export * from '../types'

export { serializeNodeWithId } from '../domain/record'
export { serializeNode, serializeNode as serializeNodeWithId } from '../domain/record'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are some known external callers of this function in Datadog-internal code. I've exported the function under both the old and new names for compatibility, but I plan to update those callers once this change ships, and then we can remove the old name.

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