Skip to content

Stop using Iterator helpers and other above-floor APIs so core-js can be removed - #2245

Open
mjuarros wants to merge 5 commits into
aws:mainfrom
mjuarros:issue-2180
Open

mjuarros wants to merge 5 commits into
aws:mainfrom
mjuarros:issue-2180

Conversation

@mjuarros

@mjuarros mjuarros commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Description

Removes the core-js dependency by replacing the runtime features it was polyfilling with native equivalents that fit the project's browser floor (baseline-widely-available).

  • Rewrites Iterator helper call sites (Map/Set.prototype.values().toArray(), .map(), .filter(), .reduce(), .forEach(), and Iterator.from(...)) to use Array.from and native array methods.
  • Replaces ES2025 Set methods (.difference, .union, .isSubsetOf) with small helpers in src/utils/setHelpers.ts.
  • Replaces String.prototype.isWellFormed() with a manual surrogate-pair check in src/utils/isWellFormedString.ts.
  • Narrows TypeScript lib to ES2023 in packages/graph-explorer/tsconfig.json and packages/shared/tsconfig.json so these APIs are caught at compile time.
  • Pins build.target to "baseline-widely-available" in vite.config.ts.

Why ES2023 instead of ES2024

Issue #2180 originally proposed narrowing lib to ES2024. After comparing both values against the current codebase, ES2023 was the better fit for the project's browser floor.

  • ES2024 still allows String.prototype.isWellFormed(), which is part of the ES2024 spec but is not supported by Firefox 114, the Firefox version in Vite's baseline-widely-available target. Narrowing to ES2023 surfaces those call sites at compile time and forces a native replacement.
  • ES2024 would also miss the ES2025 Set-method usages (Set.prototype.difference, .union, .isSubsetOf). Those are caught by the broader refactor and replaced with native helpers.
  • The only extra errors ES2023 produces over ES2024 are from isWellFormed(), which genuinely breaks the browser floor, so the stricter lib is the safer guard.

How to read

  1. https://github.com/aws/graph-explorer/pull/2245/files#diff-9a123d9c986e6d49b25941b73487100452414c4e1b2f4b80a8b7ef062f3c2f5c — narrow lib to ES2023
  2. https://github.com/aws/graph-explorer/pull/2245/files#diff-28fbbd10733902283f23978b34aacb8284bc7af50d729f6d0ea1526ba625d322 — narrow lib to ES2023
  3. https://github.com/aws/graph-explorer/pull/2245/files#diff-4a588c5a806e7338c171574737e2e2b9525c1321a470c82ced19bbb218d0a029 — pin the Vite build target
  4. https://github.com/aws/graph-explorer/pull/2245/files#diff-85ae7d1ae920636a625625b7bf63fe515947fb198af362302ee0644b68c7eb16 — remove the core-js import
  5. https://github.com/aws/graph-explorer/pull/2245/files#diff-d01b423527b0f3e6325bc62c4b5b434ef06f611a1568eec1fc10a1fea8eebd5a — native Set operations
  6. https://github.com/aws/graph-explorer/pull/2245/files#diff-1a911443adede3eb4f1befb176f8395717945020c0264741eebe9bcc418d110f — manual surrogate-pair check
  7. https://github.com/aws/graph-explorer/pull/2245/files#diff-9b35d78dbdcdd2187afcd580799799d3a3d37af3af11ea05f67e7c1d34fdeaf6 — adopt isWellFormedString
  8. https://github.com/aws/graph-explorer/pull/2245/files#diff-5a58b234d93d11c6d703b1dbfa281f9ae66e9acad3c4a0c432dc41fe202d4fec — replace Iterator.from
  9. https://github.com/aws/graph-explorer/pull/2245/files#diff-8a0f706cc93c2b7b17a17795a8a2dd21b9bb3fc4d7df5f07c97f0033014ef31f — replace Set method chains

Validation

  • pnpm checks passes.
  • pnpm test passes (2,721 tests).
  • pnpm build passes.
  • Added unit tests for setHelpers and isWellFormedString.

Related Issues

Check List

  • I confirm that my contribution is made under the terms of the Apache 2.0 license.
  • I have verified pnpm checks passes with no errors.
  • I have verified pnpm test passes with no failures.
  • I have covered new added functionality with unit tests if necessary.
  • I have updated documentation if necessary.

@mjuarros
mjuarros marked this pull request as ready for review September 23, 2026 19:08
@mjuarros
mjuarros force-pushed the issue-2180 branch 2 times, most recently from 70fa996 to 141fc3a Compare September 29, 2026 18:55

@kmcginnes kmcginnes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This does everything issue #2180 asks for, including the ES2023 amendment. pnpm check:types and pnpm build pass, dist has no Iterator polyfill, core-js is gone from the package, lockfile, workspace, and index.tsx, and no Iterator helpers, ES2025 Set methods, or .isWellFormed() calls remain. The set helpers match native semantics and isWellFormedString handles a trailing lone high surrogate correctly.

Needs fixing

  • connector/utils/warnMissingIds.test.ts:3-8: vi.mock("@/utils") stubs an internal module, which docs/agents/testing.md rules out. Use vi.spyOn(logger, "warn") instead.
  • utils/setHelpers.test.ts: use toStrictEqual instead of toEqual, per testing.md.
  • connector/utils/warnMissingIds.ts:10,16: context: Record<string, unknown> spread into the log payload goes against the AGENTS.md rules on named domain types and spreads. The generic T also accepts raw strings where it should be VertexId | EdgeId.
  • utils/setHelpers.ts:9,13,20: all three helpers spread the set into an array. These run on graph-sized sets in useRemoveFromGraph, so plain for…of loops would avoid the copies.

Worth considering

  • Some rewrites copy more than they need to, where the issue's Array.from(iter, fn) form or a loop would do:
    • neighbors.ts ~111 does Array.from().filter().map(), making three arrays.
    • neighbors.ts ~146 copies only to reduce.
    • sparql/fetchNeighbors/index.ts ~52 and FakeExplorer.ts ~139 copy then filter.
  • sparql/fetchSchema/index.ts ~138,144: Array.from(candidates, map).find() maps every candidate instead of stopping at the first match. The result is the same.
  • displayEdge.ts ~50 and displayVertex.ts ~60: the e is Edge / n is Vertex predicates aren't needed, since TS 5.5+ infers them.
  • The warnMissingIds extraction goes beyond what the issue asked for. It's fine to keep, but it's the source of two of the fixes above.

@kmcginnes

Copy link
Copy Markdown
Collaborator

I benchmarked the old and new code at 10 to 100k items. The runs were in Node 26, which uses V8, so the results should match Chrome. The "before" measurements used both the native iterator helpers and the core-js polyfill that floor browsers were actually running.

One change to make: Array.from(iter, f) is slow in V8. Array.from(iter).map(f) is about twice as fast, and a for…of loop with push is about four times as fast. At 100k items:

  • Array.from(iter, f): 2.2 ms
  • Array.from(iter).map(f): 1.0 ms
  • for…of with push: 0.5 ms
  • The old .map().toArray(): 3.0 ms

With the mapping function, the new code is only as fast as the old native helpers. I'd switch those call sites to Array.from(iter).map(f), or use a loop on hot paths.

The rest

  • Iterator rewrites (toArray, filter, filter then map, reduce): 5–12× faster than native iterator helpers, and 10–40× faster than the core-js polyfill. The polyfill is the fair comparison for Safari 16.4–18.3 and Firefox 114–130.
  • Set helpers: 1.4–3× slower than the native Set methods. Union is the worst case, at 6.7 ms against 2.8 ms native for 100k items. Loops would bring union back to native speed and help difference a little, which is another reason for the loop suggestion in my review. Floor browsers have no native Set methods, so the old code there threw instead of running slowly.
  • isWellFormedString: about 20× slower than native, but that's 0.2 ms for a 100k-character string. It only runs on query values, so it doesn't matter.

Every variant returned the same result as the new code at every size.

mjuarros and others added 5 commits October 2, 2026 11:55
…can be removed

Replace Iterator helper chains (Map/Set .values().toArray(), .map(), .filter(),
.reduce(), .forEach()) with Array.from and native array methods, which are
supported in our browser floor.

Replace ES2025 Set methods (.difference, .union, .isSubsetOf) with small
helpers in src/utils/setHelpers.ts.

Replace String.prototype.isWellFormed() with a manual surrogate check in
src/utils/isWellFormedString.ts.

Narrow TypeScript lib to ES2023 in graph-explorer and shared to prevent the
above-floor APIs from being reintroduced.

Pin the Vite build target to baseline-widely-available and remove the
core-js dependency and import.

Keep pnpm-workspace.yaml allowBuilds: core-js: false as a guard against
accidental reintroduction.

Issue: aws#2180
Extract warnMissingIds to replace the duplicated missing-ID warning
blocks across the connector detail fetchers, rewrite the session and
graph-update ID collection as single-pass loops, import setHelpers
through the utils barrel, drop the manual useMemo in DataExplorer in
favour of the compiler-memoized hoisted form, and remove the dangling
core-js allowBuilds entry.
- Constrain warnMissingIds to VertexId/EdgeId and a named context type
- Replace the internal-module mock with a spy on logger.warn
- Implement setHelpers with for...of loops instead of array copies
- Use toStrictEqual in setHelpers tests
- Remove redundant type predicates in displayEdge/displayVertex selectors
- Rewrite neighbors.ts ID collection as single-pass loops

Generated with [Devin](https://devin.ai)
- Check well-formedness with a Unicode property regex and test it against encodeURIComponent
- Map after Array.from rather than through its callback, which V8 runs slower
- Restore the early exit when picking SPARQL display attributes
- Replace the iterator chain main added in connectionLink
- Import Vitest globals in the new tests

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

Copy link
Copy Markdown
Collaborator

@mjuarros I rebased this onto latest main and pushed a few changes on top of your review fixes, so pull before you push again. I kept your version everywhere our changes overlapped, including warnMissingIds.

Rebase fallout

  • main dropped Vitest globals, so the new test files now import them.
  • main added a new .values().filter().toArray() in connectionLink, which I rewrote.

isWellFormedString

  • It now uses /\p{Cs}/u. With the u flag a paired surrogate matches as one code point, so only unpaired surrogates have the Cs category. That makes the regex exactly equivalent to native isWellFormed(), and it's supported at the floor.
  • It guards assertRepresentable, so I checked it hard. Both your loop and the regex matched native isWellFormed() on every string of up to 5 code units built from surrogate boundary values, plus over 2 million random surrogate-heavy strings. A separate security review traced every caller and found the check still runs on the raw value before escaping.
  • I added a fast-check property test against encodeURIComponent, which throws on exactly the same inputs, plus edge cases for a trailing high surrogate, a reversed pair, and two adjacent high surrogates.

Array.from(iter).map(f) instead of Array.from(iter, f)

  • V8 runs Array.from's mapping callback about 2× slower than copying first and then mapping, even though the second builds an extra array (1.0 ms vs 2.2 ms for 100k items). So I changed it everywhere, which is why it doesn't match the form the issue suggested.

Smaller

  • I put back the early exit when picking SPARQL display attributes (firstAttributeName). The Array.from(...).find() version mapped every candidate before finding one.
  • I updated REVIEW.md, which still said tsc uses lib: ESNext.
  • I removed the unused = {} default from warnMissingIds.

pnpm checks, pnpm test and pnpm build all pass, and dist has no Iterator polyfill. As before, toSorted (Firefox 115) is still out of scope and tracked in #2237.

This branch has not been deployed

No deployments
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.

Stop using Iterator helpers so core-js can be removed

2 participants