Skip to content

feat: add Intl.RelativeTimeFormat/PluralRules and named window access - #50

Merged
eumaninho54 merged 7 commits into
mainfrom
feat/intl-relative-time-plural-rules
Jul 25, 2026
Merged

feat: add Intl.RelativeTimeFormat/PluralRules and named window access#50
eumaninho54 merged 7 commits into
mainfrom
feat/intl-relative-time-plural-rules

Conversation

@eumaninho54

@eumaninho54 eumaninho54 commented Jul 25, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds Intl.RelativeTimeFormat and Intl.PluralRules (pure-JS, en/pt locale data), closing the two Intl gaps flagged as unmodeled in v0.18 — directly relevant to the project's countdown-timer and pluralized-greeting examples.
  • Adds "named access on the Window object" per the HTML spec: elements with an id (or a name on embed/form/img/object/iframe/frame) become reachable as bare globals while connected to the document — a common pattern in real-world CMS-embedded widget scripts.
  • Minor unrelated cleanup: removes an erroneous breaking-change note from the CHANGELOG's v2.0.0 entry, and bumps the local CocoaPods lockfile version.

Test plan

  • npx tsc --noEmit in example/ passes
  • yarn test:harness:ios passes (361 tests, including new JSDOM.intl.harness.ts and JSDOM.namedaccess.harness.ts cases)
  • yarn test:harness:android

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Intl.PluralRules support, including locale-aware plural-category selection and resolvedOptions().
    • Added Intl.RelativeTimeFormat support with localized unit names and numeric modes (including auto behavior).
    • Implemented named global/window access for connected document elements (with live updates on common DOM mutations and duplicate-id support).
  • Documentation

    • Updated the roadmap to describe the new Intl APIs and named window access behavior/limitations.
  • Tests

    • Expanded Intl and named-access harness test suites to validate formatting, options, and dynamic DOM behavior.

eumaninho54 and others added 4 commits July 25, 2026 16:34
Closes the two Intl gaps left unmodeled in v0.18 (RelativeTimeFormat,
PluralRules) — directly relevant to the project's own countdown-timer
and pluralized-greeting examples.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Elements with an id, or a name attribute on embed/form/img/object/
iframe/frame, become reachable as bare globals while connected to the
primary document — a common pattern in real-world CMS-embedded widget
scripts that predate/skip getElementById/querySelector.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Intl.PluralRules and Intl.RelativeTimeFormat, introduces live named access for connected DOM elements on globalThis, adds native wiring and integration tests, documents both capabilities, and removes the v2.0.0 breaking-changes changelog section.

Changes

Intl formatting APIs

Layer / File(s) Summary
Intl polyfill implementation
cpp/quickjs/bindings/IntlBindings.*
Adds PluralRules and RelativeTimeFormat behavior and documents supported limitations.
Intl integration coverage
example/src/__harness__/JSDOM.intl.harness.ts, docs/overview.md
Tests locale selection, formatting modes, resolved options, invalid units, and roadmap details.

Window named access

Layer / File(s) Summary
Named access binding contract
cpp/quickjs/bindings/WindowNamedPropertiesBindings.hpp
Declares the binding installer and its installation-order requirements.
Named access registry and installation
cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp
Tracks eligible connected elements and synchronizes named globals across supported DOM mutations and attribute changes.
Binding build and initialization
android/CMakeLists.txt, cpp/quickjs/DOMBindings.cpp
Compiles the new binding and installs it during DOM initialization.
Named access integration coverage
example/src/__harness__/JSDOM.namedaccess.harness.ts
Covers exposure, updates, removals, duplicates, protected globals, Shadow DOM, and the insertBefore() limitation.
Named access roadmap
docs/overview.md
Documents named global behavior and supported restrictions.

Release metadata

Layer / File(s) Summary
Changelog update
CHANGELOG.md
Removes the v2.0.0 breaking-changes section.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DOMMutationAPI
  participant NamedPropertiesRegistry
  participant globalThis
  DOMMutationAPI->>NamedPropertiesRegistry: connect, remove, or rename element
  NamedPropertiesRegistry->>NamedPropertiesRegistry: update element ownership
  NamedPropertiesRegistry->>globalThis: assign element, array, or undefined
Loading

Possibly related PRs

Suggested labels: released

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main additions: Intl.RelativeTimeFormat/PluralRules and named window access.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/intl-relative-time-plural-rules

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 6

🧹 Nitpick comments (2)
example/src/__harness__/JSDOM.namedaccess.harness.ts (1)

31-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover every advertised name-eligible element.

This test title claims embed, object, iframe, and frame support, but only asserts form and img. Add fixtures and assertions for the remaining four types so a partial registry implementation cannot pass the harness.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example/src/__harness__/JSDOM.namedaccess.harness.ts` around lines 31 - 50,
Extend the named-access harness test around the existing form/img fixtures to
include embed, object, iframe, and frame elements with distinct name attributes.
Add corresponding evaluate-result fields and expected tag-name assertions for
each, while retaining the span assertion to verify arbitrary elements remain
excluded.
cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp (1)

138-143: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

insertAdjacentHTML re-walks the whole subtree on every call.

For beforebegin/afterend this calls registerSubtree(this.parentNode), which runs querySelectorAll('*') over the entire parent subtree (not just the newly-inserted nodes) on every invocation; afterbegin/beforeend similarly re-walks all of this. The indexOf guard in addToRegistry keeps this idempotent, but it's O(subtree size) per call rather than O(inserted nodes), which can matter on large trees or hot insertion loops.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp` around lines 138 -
143, Update the insertAdjacentHTML override to register only the nodes newly
inserted by the operation, rather than re-walking this or its parent subtree via
registerSubtree. Capture the relevant insertion boundary before and after
calling origInsertAdjacentHTML, then register the inserted element nodes while
preserving correct handling for beforebegin, afterend, afterbegin, and beforeend
positions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/quickjs/bindings/IntlBindings.cpp`:
- Around line 300-305: In the numeric:auto lookup within IntlBindings.cpp,
remove rounding and use String(value) so idiomatic labels apply only to exact
supported values; otherwise preserve numeric formatting. Add a fractional-value
assertion in example/src/__harness__/JSDOM.intl.harness.ts (lines 224-251), such
as format(1.2, 'day'), verifying it falls back to numeric output.

In `@cpp/quickjs/bindings/IntlBindings.hpp`:
- Around line 18-20: Update the comment near the Intl relative-time formatting
behavior to document that RTF_AUTO also maps second: 0 to the localized
“now”/“agora” phrase, alongside the existing day/week/month/year auto-phrasing
description.

In `@cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp`:
- Around line 109-119: Update the removeChild and remove wrappers to capture the
subtree’s unregister keys before invoking the native operation, but defer
registry/globalThis mutation until that operation completes successfully. Ensure
removeChild leaves the registry unchanged when origRemoveChild.call throws,
while Element.prototype.remove preserves its no-op behavior for unparented
elements and commits unregistration only after successful removal.
- Around line 39-91: Update the WindowNamedPropertiesBindings bootstrap to
intercept the element textContent assignment path and unregister the element’s
existing descendant subtree before delegating to the native setter, then allow
normal registration tracking afterward. Reuse unregisterSubtree and the existing
inner* setter monkey-patch pattern; do not rely on native invalidation to remove
stale registry entries.

In `@cpp/quickjs/DOMBindings.cpp`:
- Line 72: Update the binding installation order around
WindowNamedPropertiesBindings::install so named-property handling cannot claim
or delete the window’s localStorage or sessionStorage properties. Preserve the
required ordering relative to ElementBindings and CustomElementsBindings while
ensuring storage bindings are installed or protected before named-access
processing.

In `@example/src/__harness__/JSDOM.namedaccess.harness.ts`:
- Around line 132-146: Update duplicate named-access handling in the relevant
binding so multiple matches return a live HTMLCollection rather than a plain
Array, preserving item(), namedItem(), liveness, and HTMLCollection type
behavior. Revise the test case “duplicate ids resolve to a plain array of the
matching elements” to assert HTMLCollection semantics and retain verification of
both matched elements.

---

Nitpick comments:
In `@cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp`:
- Around line 138-143: Update the insertAdjacentHTML override to register only
the nodes newly inserted by the operation, rather than re-walking this or its
parent subtree via registerSubtree. Capture the relevant insertion boundary
before and after calling origInsertAdjacentHTML, then register the inserted
element nodes while preserving correct handling for beforebegin, afterend,
afterbegin, and beforeend positions.

In `@example/src/__harness__/JSDOM.namedaccess.harness.ts`:
- Around line 31-50: Extend the named-access harness test around the existing
form/img fixtures to include embed, object, iframe, and frame elements with
distinct name attributes. Add corresponding evaluate-result fields and expected
tag-name assertions for each, while retaining the span assertion to verify
arbitrary elements remain excluded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b40da912-4d4d-4635-8fc7-1730de912601

📥 Commits

Reviewing files that changed from the base of the PR and between 2f7e957 and b1ed1fa.

⛔ Files ignored due to path filters (1)
  • example/ios/Podfile.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • CHANGELOG.md
  • android/CMakeLists.txt
  • cpp/quickjs/DOMBindings.cpp
  • cpp/quickjs/bindings/IntlBindings.cpp
  • cpp/quickjs/bindings/IntlBindings.hpp
  • cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp
  • cpp/quickjs/bindings/WindowNamedPropertiesBindings.hpp
  • docs/overview.md
  • example/src/__harness__/JSDOM.intl.harness.ts
  • example/src/__harness__/JSDOM.namedaccess.harness.ts
💤 Files with no reviewable changes (1)
  • CHANGELOG.md

Comment thread cpp/quickjs/bindings/IntlBindings.cpp
Comment thread cpp/quickjs/bindings/IntlBindings.hpp Outdated
Comment thread cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp
Comment thread cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp
Comment thread cpp/quickjs/DOMBindings.cpp Outdated
Comment thread example/src/__harness__/JSDOM.namedaccess.harness.ts
eumaninho54 and others added 3 commits July 25, 2026 19:20
format(1.2, 'day') was rounding to 1 before checking the idiomatic-
phrase map, incorrectly returning "tomorrow" instead of falling
through to numeric output. Address CodeRabbit review on PR #50.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Hook element.textContent: the native setter destroys old children
  without going through removeChild, so ids/names on those children
  were left stale in the registry.
- removeChild()/remove() now check parentNode before committing the
  unregistration, instead of unregistering unconditionally: both
  silently no-op on the wrong parent/no parent, so a still-connected
  element no longer disappears from named access.
- Install WindowNamedPropertiesBindings after localStorage/
  sessionStorage are set up, so it can never claim (and later delete)
  those globals.

Address CodeRabbit review on PR #50.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
<frame> inside <body> is dropped by HTML parsing (only valid inside
<frameset>), so the parsed-HTML fixture never actually created the
element. Test it via createElement + appendChild instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
example/src/__harness__/JSDOM.namedaccess.harness.ts (1)

209-217: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not codify insertBefore() as an accepted limitation.

insertBefore() connects the element to the document, so this test makes notHooked unreachable in the harness while contradicting the named-access contract established by appendChild(). Hook insertBefore() in the named-properties bootstrap and assert that notHooked === el, or remove the behavior from the named-access contract entirely.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example/src/__harness__/JSDOM.namedaccess.harness.ts` around lines 209 - 217,
Update the named-properties bootstrap and the related harness test so
insertBefore() registers inserted elements consistently with appendChild();
change the test to retain the inserted element and assert that notHooked
resolves to that element, rather than documenting it as unsupported.
♻️ Duplicate comments (1)
example/src/__harness__/JSDOM.namedaccess.harness.ts (1)

171-185: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not lock duplicate named access to a plain Array.

This repeats the previously reported contract issue: HTML-spec named access should expose a live HTMLCollection for multiple matches, while the current test requires Array.isArray(dup). Update the binding and assertions to cover HTMLCollection behavior (item(), namedItem(), and liveness).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example/src/__harness__/JSDOM.namedaccess.harness.ts` around lines 171 - 185,
Update the duplicate named-access implementation and the test around the
duplicate `dup` fixture so multiple matches return a live `HTMLCollection`, not
a plain array. Replace the `Array.isArray` expectation with assertions for
`item()` and `namedItem()`, and verify the collection reflects subsequent DOM
changes while preserving element order and length updates.
🧹 Nitpick comments (1)
example/src/__harness__/JSDOM.intl.harness.ts (1)

224-240: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the month and year auto phrases promised by this test.

The test title claims day/week/month/year coverage, but it only exercises day and week. Add at least one month and one year assertion so regressions in the corresponding RTF_AUTO entries are detected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example/src/__harness__/JSDOM.intl.harness.ts` around lines 224 - 240, Extend
the Intl.RelativeTimeFormat test in the evaluate payload to assert at least one
numeric:"auto" month phrase and one year phrase, alongside the existing day/week
assertions. Add corresponding result fields with expected localized outputs so
the RTF_AUTO month and year entries are covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@example/src/__harness__/JSDOM.namedaccess.harness.ts`:
- Around line 209-217: Update the named-properties bootstrap and the related
harness test so insertBefore() registers inserted elements consistently with
appendChild(); change the test to retain the inserted element and assert that
notHooked resolves to that element, rather than documenting it as unsupported.

---

Duplicate comments:
In `@example/src/__harness__/JSDOM.namedaccess.harness.ts`:
- Around line 171-185: Update the duplicate named-access implementation and the
test around the duplicate `dup` fixture so multiple matches return a live
`HTMLCollection`, not a plain array. Replace the `Array.isArray` expectation
with assertions for `item()` and `namedItem()`, and verify the collection
reflects subsequent DOM changes while preserving element order and length
updates.

---

Nitpick comments:
In `@example/src/__harness__/JSDOM.intl.harness.ts`:
- Around line 224-240: Extend the Intl.RelativeTimeFormat test in the evaluate
payload to assert at least one numeric:"auto" month phrase and one year phrase,
alongside the existing day/week assertions. Add corresponding result fields with
expected localized outputs so the RTF_AUTO month and year entries are covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bb695d48-a9af-4cef-83fe-8f01878ae2df

📥 Commits

Reviewing files that changed from the base of the PR and between b1ed1fa and bbb8423.

📒 Files selected for processing (7)
  • cpp/quickjs/DOMBindings.cpp
  • cpp/quickjs/bindings/IntlBindings.cpp
  • cpp/quickjs/bindings/IntlBindings.hpp
  • cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp
  • cpp/quickjs/bindings/WindowNamedPropertiesBindings.hpp
  • example/src/__harness__/JSDOM.intl.harness.ts
  • example/src/__harness__/JSDOM.namedaccess.harness.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • cpp/quickjs/DOMBindings.cpp
  • cpp/quickjs/bindings/IntlBindings.hpp
  • cpp/quickjs/bindings/WindowNamedPropertiesBindings.hpp
  • cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp
  • cpp/quickjs/bindings/IntlBindings.cpp

@eumaninho54
eumaninho54 merged commit a7ae41e into main Jul 25, 2026
5 checks passed
@eumaninho54
eumaninho54 deleted the feat/intl-relative-time-plural-rules branch July 25, 2026 22:42
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant