Skip to content

MD-2768 Splice replaceRange in one pass - #38

Closed
benney wants to merge 1 commit into
mainfrom
feature/md-2768-mos-proxy-splice-replacerange-in-one-pass
Closed

benney wants to merge 1 commit into
mainfrom
feature/md-2768-mos-proxy-splice-replacerange-in-one-pass

Conversation

@benney

@benney benney commented Oct 15, 2026

Copy link
Copy Markdown
Member

buildReplacementHandlers cuts a range element by element after a marker pass over a clone: a JS callback per element in the container, twice. On a 200 KB article that is ~30 ms.

When every range's markers are direct children of one container, spliceRangeReplacements marks the cut with comments in one rewriter pass, slices the string, and leaves a placeholder component whose before renders replaceWith, so rendering still goes through ContentElementHandler. 200 KB range: 29.6 → 2.5 ms.

Any other shape (markers nested below a direct child, nested or overlapping containers, identical markers, an invalid selector) returns null and the existing per-element path, now transformComponents, runs on the whole decision. Only used when rewriter.capabilities.onEndTag.

  • Parity spec: 19 cases (the existing fixtures plus edge cases) through both paths, asserting identical output and which path ran
  • Known gap, same as today: another component targeting elements inside the removed range

Patch changeset.

buildReplacementHandlers cuts a range element by element after a marker pass over a clone,
a callback per element in the container, twice. When every range's markers are direct
children of one container, spliceRangeReplacements marks each cut with comments in one pass,
slices the string, and leaves a placeholder component so ContentElementHandler still renders
replaceWith. Any other shape returns null and the per-element path runs unchanged.

A parity test runs the range fixtures and edge cases through both paths and asserts identical
HTML. On a 200 KB article the range replacement drops from ~30 ms to ~3 ms.
Copilot AI balanced review requested due to automatic review settings October 15, 2026 14:00
@changeset-bot

changeset-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8b20ce7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@monetizationos/proxy Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved output-parity regressions affect range boundaries, component matching, rendering, and encoding.

6 open findings
What changed in this PR

Optimizes simple replaceRange operations in the HTML component pipeline using string splicing, while retaining the existing per-element fallback.

Changes:

  • Adds marker-based splicing with placeholder rendering.
  • Adds 19 parity cases covering supported shapes and fallbacks.
  • Documents the optimization in a patch changeset.
File Description
test/​stages/​spliceRangeReplacements.spec.ts Tests output parity and path selection.
src/​stages/​surfaceComponents.ts Routes eligible requests through splicing.
src/​stages/​spliceRangeReplacements.ts Implements eligibility checks, cuts, and placeholders.
.changeset/​range-splice.md Records the performance improvement.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

if (!eligible) {
return null
}
marked = await session.transform(new Response(await response.clone().text(), { headers: { 'content-type': 'text/html' } })).text()
Comment on lines +24 to +26
if (!ranges.length || !response.body) {
return null
}
let session = rewriter.create()
ranges.forEach(([, behavior], k) => {
const selector = behavior.metadata.cssSelector
const { fromMarker, toMarker } = behavior.content.replaceRange
Comment on lines +50 to +53
if (open || !element.onEndTag) {
eligible = false // nested containers
return
}
Comment on lines +147 to +151
let html = marked
for (const span of [...spans].reverse()) {
html = html.slice(0, span.from) + (span.hasReplacement ? placeholder(nonce, span.k) : '') + html.slice(span.to)
}
html = html.replaceAll(new RegExp(`<!--mos-range-${nonce}-[a-z]+-\\d+-\\d+-->`, 'g'), '')
}
const spliced = rewriter.capabilities.onEndTag ? await spliceRangeReplacements(response, surfaceDecisions, rewriter) : null
return spliced
? transformComponents(ctx, spliced.response, spliced.surfaceDecisions, rewriter)
@benney benney closed this Oct 15, 2026
@benney
benney deleted the feature/md-2768-mos-proxy-splice-replacerange-in-one-pass branch October 15, 2026 14:54
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