Skip to content

fix: URLSearchParams construction and iteration spec compliance#388

Merged
NathanWalker merged 5 commits into
NativeScript:mainfrom
adrian-niculescu:fix/urlsearchparams-construction-live-iterators
Jun 12, 2026
Merged

fix: URLSearchParams construction and iteration spec compliance#388
NathanWalker merged 5 commits into
NativeScript:mainfrom
adrian-niculescu:fix/urlsearchparams-construction-live-iterators

Conversation

@adrian-niculescu

@adrian-niculescu adrian-niculescu commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

This is the iOS counterpart of NativeScript/android#1970. Same story: we noticed our app hitting a URL that looked like

https://api.example.com/items?[object+Object]=

because the binding only handles the string init form, so an innocent new URLSearchParams({ limit: "50", cursor: "abc123" }) got stringified to "[object Object]" and the whole query collapsed into one bogus key. The same code works in browsers and Node.

Same fix as on Android, adapted to this runtime's helpers:

  • The constructor follows the WebIDL init dispatch from the URL spec: record form for plain objects (own enumerable keys in order; a Symbol key throws), sequence form for iterables (Array, Map, Set, another URLSearchParams, generators, with IteratorClose on abrupt completion and a TypeError for a pair whose length isn't 2), USVString coercion for other primitives and null (a Symbol throws; null parses as the string "null", matching the whatwg-url reference implementation), and empty params for undefined (the IDL default).
  • entries()/keys()/values() return live ES iterators per spec instead of plain arrays, and @@iterator aliases entries, so for..of, spread, and the new URLSearchParams(other) copy form work. This also fixes a duplicate-key bug: the old get_keys()+get() walk returned the first value for every occurrence of a repeated key.
  • get() returns null for a missing name instead of undefined (one existing test asserted undefined and was updated to match the spec).
  • delete(name, value) and has(name, value) honor the optional value argument, which ada already exposes. An explicit undefined value is treated as omitted, matching WPT.

Tests are mirrored from the Android suite (49 specs in the URLSearchParams group; the pre-existing keys/values/entries tests now consume iterators via spread). Verified with the TestRunner scheme on an iPhone 17 simulator (iOS 26.5): TEST SUCCEEDED, 0 failures.

Update after review: every name and value argument across get/getAll/has/delete/append/set now goes through the same USVString coercion (a Symbol throws), entries()/keys()/values() and the iterator's next() brand-check their receivers, and GetPointer no longer reads an internal field from objects that have none (entries.call({}) used to be an out-of-bounds read). A second pass against the IDL and the whatwg-url reference implementation aligned three more corners: new URLSearchParams(null) parses as the string "null" instead of being treated as empty (the union conversion has no null special case; WPT does not cover it and Node is the outlier), an enumerable Symbol key in a record init throws, and the remaining members (get/getAll/has/append/set/delete/forEach/sort/toString/size) brand-check their receiver like the iterator methods.

Summary by CodeRabbit

  • New Features

    • Constructor accepts records, sequences, iterables, and primitives with spec-style coercion; iterators (entries/keys/values/@@iterator) are live, branded, and alias the default iterator.
  • Bug Fixes

    • Methods now throw on illegal (foreign) receivers.
    • delete(name[, value]) filters by value when provided; explicit undefined is treated as omitted.
    • get() returns null for missing names; Symbol coercion now throws.
    • Append/set/coercion behavior tightened to match spec.
  • Tests

    • Expanded tests for constructor variants, iterator behavior/branding/closure, coercions, and receiver safety.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR refactors URLSearchParams to implement WHATWG spec compliance: adds live iterators for entries/keys/values with Symbol.iterator aliasing, reworks the constructor to accept strings/records/sequences/primitives via USVString-like coercion, updates delete/has to support value-filtered operations, and changes get to return null.

Changes

URLSearchParams Spec Compliance

Layer / File(s) Summary
Live iterator infrastructure
NativeScript/runtime/URLSearchParamsImpl.cpp, TestRunner/app/tests/URLSearchParams.js
Implements spec-compliant live iterators for entries(), keys(), and values() with hidden iterator state, next() brand/receiver checks, and Symbol.iterator aliasing. Adds iterator open/step/close helpers and tests for liveness, receiver-brand errors, and iterator closing on abrupt completion.
Constructor input coercion and initialization
NativeScript/runtime/URLSearchParamsImpl.cpp, TestRunner/app/tests/URLSearchParams.js
Reworks URLSearchParamsImpl::Ctor to dispatch on input type: strings are URL-parsed; objects route to record or sequence initialization based on @@iterator presence and callability; primitives are coerced per USVString-like rules (Symbol throws); null/undefined produce empty params. Tests cover record inputs, sequence-of-pairs (Maps/Sets/generators), primitives, copy-construction, and error cases.
Method semantic updates and iterator returns
NativeScript/runtime/URLSearchParamsImpl.cpp, TestRunner/app/tests/URLSearchParams.js
entries()/keys()/values() now return live iterators; delete(name, value) supports optional value-filtered removal (explicit undefined treated as omitted); get returns null for missing names; getAll/has coerce name/value using shared conversion; core methods throw on illegal receivers. Tests validate coercion, error paths (e.g., Symbol), duplicate-key ordering, and receiver-brand behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A rabbit hops through iterator land,
Where live keys/values go hand-in-hand,
entries() hums as Symbol takes stage,
Constructor reads every input page—
Spec burrows lined, now snug and grand! 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main focus of the PR: fixing URLSearchParams to comply with spec requirements for construction and iteration behavior.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@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: 1

🤖 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 `@NativeScript/runtime/URLSearchParamsImpl.cpp`:
- Around line 568-584: The Delete and Has implementations still call
tns::ToString(args[0].As<v8::String>()) and must instead coerce the first
argument with ValueToString to follow USVString semantics; replace the existing
key acquisition in URLSearchParamsImpl::Delete and URLSearchParamsImpl::Has by
calling ValueToString(isolate->GetCurrentContext(), args[0], key) into a
std::string key, return immediately if coercion fails (preserving exceptions),
and then call ptr->GetURLSearchParams()->remove(key, ...) or remove(key.c_str())
/ ptr->GetURLSearchParams()->has(key) using that coerced key; remove the use of
args[0].As<v8::String>() and tns::ToString for the name.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b1464f66-8765-406b-bb66-26af6cb0d7fe

📥 Commits

Reviewing files that changed from the base of the PR and between cd28c41 and 2fa428f.

📒 Files selected for processing (2)
  • NativeScript/runtime/URLSearchParamsImpl.cpp
  • TestRunner/app/tests/URLSearchParams.js

Comment thread NativeScript/runtime/URLSearchParamsImpl.cpp Outdated

@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 (2)
NativeScript/runtime/URLSearchParamsImpl.cpp (2)

652-670: ⚠️ Potential issue | 🟠 Major

Guard URLSearchParams.forEach callback before args[0].As<v8::Function>().
v8::Local<T>::As<S>() is a cast that doesn’t do runtime type checking; without an args[0]->IsFunction() (and length) guard, non-function/undefined callbacks can reach the Call path instead of throwing the expected JS TypeError.

Proposed fix
 void URLSearchParamsImpl::ForEach(
     const v8::FunctionCallbackInfo<v8::Value>&amp; args) {
   URLSearchParamsImpl* ptr = GetPointer(args.This());
   auto isolate = args.GetIsolate();
   auto context = isolate-&gt;GetCurrentContext();
   if (ptr == nullptr) {
     ThrowTypeError(isolate, "Illegal invocation");
     return;
   }
+  if (args.Length() &lt; 1 || !args[0]-&gt;IsFunction()) {
+    ThrowTypeError(isolate, "URLSearchParams.forEach callback must be a function");
+    return;
+  }
   auto callback = args[0].As&lt;v8::Function&gt;();
   auto searchParams = args.This();
🤖 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 `@NativeScript/runtime/URLSearchParamsImpl.cpp` around lines 652 - 670, The
forEach implementation currently casts args[0] to v8::Function without runtime
checking; before calling args[0].As<v8::Function>() (and before using
callback->Call), add a guard that args.Length() > 0 and args[0]->IsFunction(),
and if not throw a JS TypeError (via v8::Exception::TypeError /
v8::TypeError::New and context->Throw). Move or defer creation of the
v8::Local<v8::Function> callback until after that check, keep thisArg and
entries logic the same, and ensure the code paths that call callback->Call only
run when the argument is a validated function (referencing the variables
callback, args, thisArg, and the existing Call invocation).

25-37: ⚠️ Potential issue | 🟠 Major

Strengthen receiver brand check before unwrapping slot 0 internal pointers

URLSearchParamsImpl::GetPointer only checks InternalFieldCount() >= 1, then casts slot 0 to URLSearchParamsImpl* (lines 25-37). Other wrapped natives also use internal field slot 0 (e.g. URLImpl / URLPatternImpl write slot 0 and their GetPointer also only casts), so calling URLSearchParams methods with a foreign receiver can bypass the “Illegal invocation” path and reinterpret a different native pointer as URLSearchParamsImpl*.

  • Add a real URLSearchParams brand/type gate in GetPointer (e.g., dedicated type tag in the internal field, or an instance-of/identity check) before performing the cast.
  • In URLSearchParamsImpl::ForEach, guard args[0] before auto callback = args[0].As<v8::Function>(); (missing/non-callable callback should throw TypeError instead of relying on an unsafe cast).
🤖 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 `@NativeScript/runtime/URLSearchParamsImpl.cpp` around lines 25 - 37, The
GetPointer implementation in URLSearchParamsImpl currently trusts slot 0 and can
miscast foreign receivers; modify URLSearchParamsImpl::GetPointer to perform a
real brand/type check before casting (e.g., store and verify a dedicated type
tag or magic value in another internal field or use an instance identity check)
instead of only checking InternalFieldCount and slot 0 with
GetAlignedPointerFromInternalField; additionally, harden
URLSearchParamsImpl::ForEach to validate args[0] is a callable before doing auto
callback = args[0].As<v8::Function>() and throw a TypeError when it is missing
or not a function. Ensure references to InternalFieldCount,
GetAlignedPointerFromInternalField, URLSearchParamsImpl::GetPointer and
URLSearchParamsImpl::ForEach are updated accordingly.
🤖 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 `@NativeScript/runtime/URLSearchParamsImpl.cpp`:
- Around line 652-670: The forEach implementation currently casts args[0] to
v8::Function without runtime checking; before calling args[0].As<v8::Function>()
(and before using callback->Call), add a guard that args.Length() > 0 and
args[0]->IsFunction(), and if not throw a JS TypeError (via
v8::Exception::TypeError / v8::TypeError::New and context->Throw). Move or defer
creation of the v8::Local<v8::Function> callback until after that check, keep
thisArg and entries logic the same, and ensure the code paths that call
callback->Call only run when the argument is a validated function (referencing
the variables callback, args, thisArg, and the existing Call invocation).
- Around line 25-37: The GetPointer implementation in URLSearchParamsImpl
currently trusts slot 0 and can miscast foreign receivers; modify
URLSearchParamsImpl::GetPointer to perform a real brand/type check before
casting (e.g., store and verify a dedicated type tag or magic value in another
internal field or use an instance identity check) instead of only checking
InternalFieldCount and slot 0 with GetAlignedPointerFromInternalField;
additionally, harden URLSearchParamsImpl::ForEach to validate args[0] is a
callable before doing auto callback = args[0].As<v8::Function>() and throw a
TypeError when it is missing or not a function. Ensure references to
InternalFieldCount, GetAlignedPointerFromInternalField,
URLSearchParamsImpl::GetPointer and URLSearchParamsImpl::ForEach are updated
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 09a44c34-d1e0-4493-a560-7db4692a020b

📥 Commits

Reviewing files that changed from the base of the PR and between 380e162 and c959e05.

📒 Files selected for processing (2)
  • NativeScript/runtime/URLSearchParamsImpl.cpp
  • TestRunner/app/tests/URLSearchParams.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • TestRunner/app/tests/URLSearchParams.js

The constructor previously handled only the string init form; any object
init was stringified to "[object Object]", collapsing every query
parameter into a single bogus key. It now follows the WHATWG/WebIDL init
dispatch (https://url.spec.whatwg.org/#interface-urlsearchparams):
record, sequence (any iterable of pairs: Array, Map, Set, another
URLSearchParams, generators - with IteratorClose on abrupt completion),
other primitives (USVString coercion; Symbol throws), and
null/undefined (empty).

entries()/keys()/values() returned plain v8::Arrays and the type exposed
no @@iterator, so for..of, spread, and the copy-constructor form did not
work; the array path also returned the first value for every occurrence
of a repeated key. They now return genuine live ES iterators (reflecting
mutations mid-iteration, per spec), and @@iterator aliases entries().

Also aligned to spec: get() returns null (not undefined) for a missing
name; delete(name, value) and has(name, value) honor the optional value
argument (coerced to USVString; an explicit undefined is treated as
omitted, matching WPT).
Review follow-up:

- get()/getAll()/has()/delete() now coerce the name argument through
the same USVString conversion as the optional value argument instead of
casting it to a string blindly, so a number/boolean/object name works
and a Symbol (or a throwing toString) raises a TypeError.

- entries()/keys()/values() and the live iterator's next() brand-check
their receivers and throw "Illegal invocation" for foreign objects,
per the WebIDL iterable<> semantics. GetPointer also refuses to read an
internal field from an object that has none (e.g. entries.call({})),
which was an out-of-bounds read.
Completes the review follow-up: append() and set() now run both
arguments through the same USVString coercion as the rest of the
binding, instead of casting them to strings blindly. A
number/boolean/object argument coerces via toString; a Symbol (or a
throwing toString) raises a TypeError before anything is appended or
replaced.
…bIDL

- new URLSearchParams(null) parses the string "null" (so "null=").
The IDL default "" applies only to undefined/missing; null goes
through the union conversion, which has no null special case (the
union is not nullable and a record is not a dictionary), so it lands
in the USVString branch. Matches the whatwg-url reference
implementation; Node treats null as empty and is the outlier, and WPT
does not cover the case.

- An own enumerable Symbol key in a record init throws a TypeError
instead of being silently skipped: WebIDL record conversion takes the
keys from [[OwnPropertyKeys]] filtered by enumerability only, and
converting a Symbol key to a USVString throws.

- The remaining members (get/getAll/has/append/set/delete/forEach/
sort/toString/size) brand-check their receiver and throw an
"Illegal invocation" TypeError for a foreign object, consistent with
entries()/keys()/values().
@adrian-niculescu adrian-niculescu force-pushed the fix/urlsearchparams-construction-live-iterators branch from c959e05 to c45030e Compare June 12, 2026 15:36

@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: 1

Caution

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

⚠️ Outside diff range comments (2)
NativeScript/runtime/URLSearchParamsImpl.cpp (2)

652-670: ⚠️ Potential issue | 🟠 Major

Add a type-check for the forEach callback before casting to v8::Function.

URLSearchParamsImpl::ForEach casts args[0] directly via args[0].As<v8::Function>() with no IsFunction() (or args-length) guard. Passing a non-callable (or missing) callback should throw a JS TypeError at the API boundary instead of relying on V8’s cast/call path.

Minimal fix
-  auto callback = args[0].As&lt;v8::Function&gt;();
+  if (args.Length() &lt; 1 || !args[0]-&gt;IsFunction()) {
+    ThrowTypeError(isolate, "The callback provided as parameter 1 is not a function");
+    return;
+  }
+  auto callback = args[0].As&lt;v8::Function&gt;();
🤖 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 `@NativeScript/runtime/URLSearchParamsImpl.cpp` around lines 652 - 670, In
URLSearchParamsImpl::ForEach, validate the first argument before casting: check
args.Length() > 0 and args[0]->IsFunction() (or context->Global()->Get(isolate,
...) style check) and if not, throw a JS TypeError; replace the direct cast
args[0].As<v8::Function>() with a guarded branch that creates a
v8::Local<v8::Function> only when IsFunction() is true and otherwise calls
isolate->ThrowException(v8::Exception::TypeError(...)) (referencing symbols:
URLSearchParamsImpl::ForEach, args, callback, thisArg, and the v8::Function
cast).

25-37: ⚠️ Potential issue | 🟠 Major

Fix receiver branding in URLSearchParamsImpl::GetPointer to prevent type confusion (UB)

The guard only checks InternalFieldCount() >= 1 and that internal field 0 is non-null, then unconditionally static_casts it to URLSearchParamsImpl*. Since other NativeScript bindings also use SetInternalFieldCount(1) and store their own native pointers in internal field 0 (e.g., NativeScript/runtime/URLImpl.cpp, NativeScript/runtime/URLPatternImpl.cpp), a foreign native wrapper can bypass the intended TypeError("Illegal invocation") path and produce undefined behavior from pointer re-interpretation.

🤖 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 `@NativeScript/runtime/URLSearchParamsImpl.cpp` around lines 25 - 37,
GetPointer currently trusts any object with InternalFieldCount() >= 1 and a
non-null internal field 0, which allows other native wrappers to be
misinterpreted; fix by adding a receiver-brand check before static_cast: store a
unique branding token (e.g., a static void* kURLSearchParamsBrand) in a second
internal field when creating URLSearchParams instances and in
URLSearchParamsImpl::GetPointer require InternalFieldCount() >= 2 and verify
object->GetAlignedPointerFromInternalField(1) equals that branding token (or
check the type of the internal field is the expected v8::External containing the
brand) before casting the pointer from field 0 to URLSearchParamsImpl*. Ensure
the code references URLSearchParamsImpl::GetPointer and the new
URLSearchParamsImpl::kURLSearchParamsBrand (or similar) to locate changes.
🤖 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 `@NativeScript/runtime/URLSearchParamsImpl.cpp`:
- Around line 394-395: StepIterator failures in MaterializeInnerSequence and
BuildFromSequence return false early and skip ES IteratorClose; update both
functions so that whenever StepIterator(context, iterator, next, element, done)
returns false you first call CloseIterator(context, iterator) (preserving any
original error/abrupt completion behavior) and only then return false,
propagating CloseIterator failures as appropriate; ensure you reference the same
iterator/next values used in the failing StepIterator call and follow the
existing error-return semantics used elsewhere in the file for IteratorClose
handling.

---

Outside diff comments:
In `@NativeScript/runtime/URLSearchParamsImpl.cpp`:
- Around line 652-670: In URLSearchParamsImpl::ForEach, validate the first
argument before casting: check args.Length() > 0 and args[0]->IsFunction() (or
context->Global()->Get(isolate, ...) style check) and if not, throw a JS
TypeError; replace the direct cast args[0].As<v8::Function>() with a guarded
branch that creates a v8::Local<v8::Function> only when IsFunction() is true and
otherwise calls isolate->ThrowException(v8::Exception::TypeError(...))
(referencing symbols: URLSearchParamsImpl::ForEach, args, callback, thisArg, and
the v8::Function cast).
- Around line 25-37: GetPointer currently trusts any object with
InternalFieldCount() >= 1 and a non-null internal field 0, which allows other
native wrappers to be misinterpreted; fix by adding a receiver-brand check
before static_cast: store a unique branding token (e.g., a static void*
kURLSearchParamsBrand) in a second internal field when creating URLSearchParams
instances and in URLSearchParamsImpl::GetPointer require InternalFieldCount() >=
2 and verify object->GetAlignedPointerFromInternalField(1) equals that branding
token (or check the type of the internal field is the expected v8::External
containing the brand) before casting the pointer from field 0 to
URLSearchParamsImpl*. Ensure the code references URLSearchParamsImpl::GetPointer
and the new URLSearchParamsImpl::kURLSearchParamsBrand (or similar) to locate
changes.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b96c87d0-944b-48d1-8c23-f6af1eccf024

📥 Commits

Reviewing files that changed from the base of the PR and between c959e05 and c45030e.

📒 Files selected for processing (2)
  • NativeScript/runtime/URLSearchParamsImpl.cpp
  • TestRunner/app/tests/URLSearchParams.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • TestRunner/app/tests/URLSearchParams.js

Comment thread NativeScript/runtime/URLSearchParamsImpl.cpp
Comment-only. The previous wording ("on either abrupt completion")
could be read as closing the iterator on any failure, including a
throw from next() itself. Spelled out the actual rule: conversion
failures and wrong-length pairs close the source iterator, while a
throw from the stepping (next(), or reading the result's done/value)
does not, because the ES iterator protocol marks the iterator done on
such a throw and IteratorClose is skipped for a done iterator.
@NathanWalker NathanWalker merged commit ad920c6 into NativeScript:main Jun 12, 2026
4 checks passed
@adrian-niculescu adrian-niculescu deleted the fix/urlsearchparams-construction-live-iterators branch June 12, 2026 17:20
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