Stop using Iterator helpers so core-js can be removed

Open
#2,180 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
15/100
Issue type
Refactor
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript, vite

Research direction

Start with prerequisite issue #2181, then inspect packages/graph-explorer/tsconfig.json, the listed TypeScript call sites, src/utils/createErrorDetails.ts, index.tsx, package.json, pnpm-lock.yaml, and vite.config.ts. Run pnpm check:types while narrowing the lib setting, then verify pnpm checks, pnpm test, and pnpm build. Done means no iterator helpers or core-js remain, the browser target is explicit, and the build contains no Iterator polyfill.

Written by the indexing model from the issue text.

Description

dependencies internal ready-for-agent tech debt

Iterator helpers (Iterator.prototype.map/filter/toArray, Iterator.from) are Baseline newly available as of March 2025, so they will not reach Baseline Widely Available until roughly September 2027. Vite's default build.target is baseline-widely-available, which in Vite 8 resolves to ['chrome111', 'edge111', 'firefox114', 'safari16.4', 'ios16.4'], and Vite only handles syntax transforms and does not cover polyfills. That is why packages/graph-explorer/src/index.tsx imports a core-js polyfill, and why core-js is a production dependency (#2130, #2167).

The usage does not earn that cost. Across 30 files (28 source, 2 test) only four helpers appear, and .toArray() accounts for the large majority. Every call site is drained eagerly, so nothing relies on laziness.

Work

Rewrite the call sites to natively supported equivalents:

  • m.values().toArray() becomes Array.from(m.values())
  • m.values().map(f).toArray() becomes Array.from(m.values(), f), with no intermediate array
  • m.values().filter(p) becomes [...m.values()].filter(p)

The 7 sites that do not end in .toArray() are each immediately consumed by new Map(...), Promise.all(...), or a similar iterable consumer, so they rewrite the same way: connector/sparql/fetchNeighbors/index.ts:39 and :53, connector/sparql/neighborCounts.ts:268, core/StateProvider/neighbors.ts:157, modules/GraphViewer/GraphViewer.tsx:95 and :98.

Then remove the polyfill import from index.tsx and remove core-js from packages/graph-explorer/package.json and pnpm-lock.yaml.

Approach

Narrowing the TypeScript lib is the instrument. packages/graph-explorer/tsconfig.json currently sets lib: ["ESNext", "DOM", "DOM.Iterable"], which is the same class of mistake as the devDependencies entry was: it claims a runtime we do not ship to. Under ESNext a .take() call typechecks clean, so nothing catches a new helper today.

Measured by editing the lib value and running pnpm check:types:

lib Errors Catches
ESNext (today) 0 nothing
ES2025 12 Error.isError only; iterator helpers still allowed
ES2024 185 iterator helpers and Error.isError
ES2023 187 2 more, from features we do not need to give up

So the value is lib: ["ES2024", "DOM", "DOM.Iterable"]. It reports every call site by file and line from one command, in-editor, with no test run: 75 x TS2339 (Property 'toArray' does not exist on type 'MapIterator<Vertex>'), plus 45 x TS7006 and 21 x TS7031 where callbacks lose their inferred parameter types downstream.

  1. Land #2181 first. It is a separate bug and a hard prerequisite, not optional sequencing.
  2. Narrow lib to ES2024. Also decide on packages/shared, which sets no lib, inherits target: "esnext" from tsconfig.base.json, and runs in the browser through the @shared/* path mapping. graph-explorer-proxy-server is Node-only and can keep ESNext.
  3. Run pnpm check:types and rewrite until green.
  4. Remove the polyfill import from index.tsx and remove core-js from packages/graph-explorer/package.json and pnpm-lock.yaml.
  5. Pin the build target in packages/graph-explorer/vite.config.ts: build: { target: "baseline-widely-available" }. This matches Vite 8's current default, so nothing changes behaviourally. It belongs in this issue because import "core-js/full/iterator" is what currently documents, implicitly, that we support browsers without native iterator helpers. Once that import is gone, build.target is the only written record of the browser floor, and Vite fixes its Baseline snapshot per major release, so a Vite 9 upgrade would otherwise raise the floor silently.

Keep the narrowed lib afterwards. It is what stops a helper from being reintroduced, and it is the only tool that can: oxlint has no no-restricted-syntax, and no-restricted-properties only matches a static object name, so it would catch Iterator.from but not m.values().take(3). One caveat, no lib value means "Baseline Widely Available", so ES2024 closes the iterator-helper hole without making the config a precise statement of the browser floor.

Why #2181 blocks this

Narrowing lib turns Error.isError into a compile error. 15 of the 185 errors are that one call at src/utils/createErrorDetails.ts:52 and :66: 2 x TS2550 for the method plus 13 x TS18046 where error and cause stay unknown once the type guard stops narrowing. pnpm check:types cannot pass until #2181 lands, so start there.

Performance

The rewrite is faster, not slower. Benchmarked on Node 26, whole-loop times, lower is better:

n [...m.values()].filter() Array.from(...).filter() native iterator .filter().toArray() core-js .filter().toArray()
10 3.0 ms 3.6 ms 3.1x slower 30.5x slower
100 4.1 ms 1.6 ms 1.7x 17.0x
1,000 3.5 ms 1.6 ms 1.8x 19.9x
20,000 3.8 ms 2.1 ms 1.8x 17.4x

Iterator helpers are slower than array methods even natively, because the iterator protocol costs a next() call and an object allocation per element while V8 heavily optimizes array methods. The comparison that matters is against the polyfill we actually ship, which is the 17-30x column.

Array.from beat spread at every size above 10, so prefer it over spread in the rewrites. That makes the one .filter() site at connector/sparql/fetchNeighbors/index.ts:53 a paren move:

// from
Array.from(verticesMap.values().filter(v => v.id !== req.resourceURI))
// to
Array.from(verticesMap.values()).filter(v => v.id !== req.resourceURI)

Acceptance

  • No Iterator.prototype helper or Iterator.from call remains in packages/
  • core-js appears nowhere in packages/graph-explorer/package.json or the lockfile importer
  • pnpm checks and pnpm test pass
  • pnpm build succeeds and dist no longer contains the Iterator polyfill (about 22 KB gzipped today, 13 KB after the narrowing work)
  • build.target is set explicitly in packages/graph-explorer/vite.config.ts
  • core-js is absent from the built image's node_modules, removing the 1.3 MB / 3,728 files the --prod install currently keeps

Related Issues

  • Blocked by #2181
  • Related to #2130, #2157, #2167

[!IMPORTANT]
Internal only — this issue is maintained by the core team and is not accepting external contributions.

Dominant language
TypeScript
Stars
481
Forks
110
Avg merge
2d 14h
Merged PRs (30d)
9

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from aws/graph-explorer

All issues in aws/graph-explorer

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.