Symbol.iterator polyfill in ecmascript-runtime-client breaks native iterator protocol on the client

Symbol.iterator polyfill in ecmascript-runtime-client breaks native iterator protocol on the client

Versions

  • Meteor: METEOR@3.4
  • ecmascript-runtime-client: 0.12.3
  • core-js (transitive, bundled): 3.47.0
  • @meteorjs/rspack: 1.0.2 (modern web targets only)

Symptom

On the client, any code that uses the native iterator protocol on a built-in iterator — for (const x of arr.entries()), for (const [k, v] of map.entries()), for (const v of map.values()), etc. — can throw at runtime:

TypeError: b.entries is not a function or its return value is not iterable
    at … in our app bundle

The first part of the message is misleading; Array.prototype.entries does exist. What actually fails is the [Symbol.iterator] lookup on the iterator returned by .entries() — the polyfilled Symbol.iterator is a different symbol than the one the engine’s built-in iterators are keyed under, so for..of declines to iterate.

What I found tracing this

Meteor’s ecmascript-runtime-client package pulls in core-js and installs a Symbol.iterator shim with a UID-suffixed symbol (Symbol(Symbol.iterator)_a.<uid> or similar — readable as String(Symbol.iterator) on the client). Code that calls a built-in iterator method then for..of’s the result hits the mismatch: the iterator’s internal @@iterator is the engine’s symbol, the for..of is looking up the polyfilled one.

This already bit us once with mime v4, which uses for..of map.values() internally. Downgrading to mime v3 (indexed loops) made the client boot again. The same pattern then bit us again in our own app code at:

for (const [priority, placement] of placements.entries()) { … }

…where placements was a plain Array. Same root cause.

Workaround

In client code, avoid native-iterator-protocol calls and use indexed iteration or .forEach() instead:

// Bad — relies on Symbol.iterator
for (const [priority, placement] of placements.entries()) { … }

// Fine — never touches the iterator protocol
for (let priority = 0; priority < placements.length; priority++) {
  const placement = placements[priority]
  …
}

For Map/Set, Array.from(map.values()).forEach(…) works too — Array.from does the iterator dance up front, then you’re back on a regular Array.

Question

Is this a known issue with ecmascript-runtime-client@0.12.3 + bundled core-js@3.47.0? Has anyone else hit this in production? Is there a way to opt out of the Symbol.iterator polyfill for modern browser targets (where the engine’s native iterator protocol is already correct)? Our @meteorjs/rspack@1.0.2 build targets modern browsers only, so the polyfill is pure liability for us — it’s only causing breakage, not enabling anything.

Happy to share concrete repros if useful. The two we hit:

  1. mime@4.x on the client.
  2. Our own app code: a for..of arr.entries() loop in a tooltip placement helper.

Both fix the same way: stop using the iterator protocol.

Thank you!