All skills
ccheney avatar

/modern-javascript

@00695ba

Implement or debug JavaScript language behavior, including promise completion, async iteration, mutation, and runtime compatibility. Use for ECMAScript semantics or polyfill/transform choices; not unrelated framework or TypeScript type design.

Use this Skill: https://skilld.dev/gh/ccheney/robust-skills/modern-javascript

This session only. Nothing lands on disk.

referencesES2026.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

ES2026 Features

ECMAScript 2026 is the finalized ECMA-262 17th edition, approved on 2026-06-30. Its seven feature proposals are Array.fromAsync, Error.isError, Math.sumPrecise, Uint8Array base64/hex conversion, Iterator.concat, Map/WeakMap upsert, and JSON source text access.

Runtime ship dates are independent of edition membership. Several ES2026 APIs were available years before publication, while others still require the newest engines or a polyfill.

Contents

Array.fromAsync

Create an array from an async iterable, a sync iterable, or an array-like value. The optional map function may also be async.

async function* fetchPages() {
  yield await fetch('/page/1').then(response => response.json());
  yield await fetch('/page/2').then(response => response.json());
}

const pages = await Array.fromAsync(fetchPages());

const normalized = await Array.fromAsync(
  asyncSource,
  async value => normalize(value),
);

The source is pulled in order and each value is awaited before the next is requested. This differs from Promise.all, whose input work is normally created up front and can proceed concurrently.

// Concurrent after the map callbacks create all fetch promises.
const responses = await Promise.all(urls.map(url => fetch(url)));

// Sequentially pulls and awaits values yielded by this generator.
const responsesInOrder = await Array.fromAsync(fetchSequentially(urls));

Use Array.fromAsync to drain an async source. Do not replace Promise.all with it when concurrency is intentional.

Error.isError

Check the Error internal brand rather than the current realm's prototype chain.

const otherRealmError = iframe.contentWindow.eval('new Error("oops")');

otherRealmError instanceof Error; // false
Error.isError(otherRealmError);    // true

Error.isError(new TypeError('bad')); // true
Error.isError({ message: 'fake' });  // false
Error.isError(Object.create(Error.prototype)); // false

The ECMA-262 operation checks the [[ErrorData]] internal slot, so it is cross-realm-safe for ECMAScript Error instances. Host error objects are a separate integration question: current Safari has partial support because DOMException is not recognized there. Feature or behavior-test that case if the application depends on it.

Math.sumPrecise

Sum an iterable of Number values as an exact mathematical sum of the binary Number inputs, then round once to the nearest representable Number.

const values = [1e20, 0.1, -1e20];

values.reduce((sum, value) => sum + value, 0); // 0
Math.sumPrecise(values);                       // 0.1

Object.is(Math.sumPrecise([]), -0);            // true

It rejects non-Number values (including BigInt), preserves the specified NaN/infinity behavior, and returns -0 for an empty iterable.

Math.sumPrecise removes intermediate floating-point rounding loss. It does not turn decimal fractions into exact decimal values, so it is not a substitute for integer minor units or a decimal arithmetic design in financial code.

Uint8Array base64 and hex

Convert byte arrays without Latin-1-only atob/btoa loops or Node-specific Buffer code.

const bytes = Uint8Array.fromBase64('SGVsbG8gV29ybGQ=');
const fromHex = Uint8Array.fromHex('48656c6c6f');

bytes.toBase64();
bytes.toBase64({ alphabet: 'base64url', omitPadding: true });
bytes.toHex();

const target = new Uint8Array(1024);
const { read, written } = target.setFromBase64('SGVsbG8=');

The complete group is:

  • Uint8Array.fromBase64 / fromHex
  • Uint8Array.prototype.toBase64 / toHex
  • Uint8Array.prototype.setFromBase64 / setFromHex

Base64 options control the alphabet and final-chunk handling. Choose strict decoding when accepting security-sensitive or canonical input rather than relying on permissive defaults.

Iterator.concat

Lazily sequence any number of iterables.

const combined = Iterator.concat(
  [1, 2],
  new Set([3, 4]),
  [5, 6],
);

combined.toArray(); // [1, 2, 3, 4, 5, 6]

It composes with the ES2025 sync iterator helpers and does not build an intermediate collection:

const firstThreeEven = Iterator.concat(streamA(), streamB())
  .filter(value => value % 2 === 0)
  .take(3)
  .toArray();

The result is still single-pass. Do not reuse it as though it were an array.

Map and WeakMap upsert

Read an existing value or insert a default. The same two methods exist on Map.prototype and WeakMap.prototype:

  • getOrInsert(key, defaultValue)
  • getOrInsertComputed(key, callback)
const byCategory = new Map();

for (const item of items) {
  byCategory.getOrInsert(item.category, []).push(item);
}

const cache = new WeakMap();
const metadata = cache.getOrInsertComputed(node, key => inspect(key));

Use the computed form when creating the default is expensive. Function arguments are evaluated before the call, so getOrInsert(key, expensive()) performs the work even when the key is already present.

These methods insert only when the key is absent. An existing value of undefined is still an existing value and is returned without replacement.

JSON source text access

The reviver's third context argument exposes the exact source text for primitive values. JSON.rawJSON lets a replacer or value preserve already-validated primitive JSON text during serialization.

const data = JSON.parse(
  '{"id": 12345678901234567890}',
  (key, value, context) => key === 'id'
    ? BigInt(context.source)
    : value,
);

data.id; // 12345678901234567890n

JSON.stringify({ id: JSON.rawJSON(data.id.toString()) });
// '{"id":12345678901234567890}'

JSON.isRawJSON(JSON.rawJSON('123')); // true

The context object with source is supplied when reviving primitive values, not objects or arrays. Guard accesses when a reviver handles both:

const parsed = JSON.parse(text, (key, value, context) => {
  if (key === 'amount' && context?.source !== undefined) {
    return parseExactAmount(context.source);
  }
  return value;
});

JSON.rawJSON validates that its input is suitable primitive JSON text. It is not a way to splice arbitrary object syntax into output.

Compatibility

First unflagged versions from MDN Browser Compatibility Data, checked 2026-08-13:

Feature Chrome Firefox Safari Node.js
Array.fromAsync 121 115 16.4 22.0.0
Error.isError 134 138 18.4 partial 24.3.0
Math.sumPrecise 147 137 26.2 No current support recorded
Uint8Array base64/hex 140 133 18.2 25.0.0
Iterator.concat 146 147 26.4 26.0.0
Map/WeakMap upsert 145 144 26.2 26.0.0
JSON source access/raw JSON 114 135 18.4 21.0.0

Safari 18.4's Error.isError does not recognize DOMException; Node is complete from 24.3. Current core-js has modules for all seven feature groups. Read COMPATIBILITY.md before selecting a polyfill strategy.

Sources

Source: SKILL.md on GitHub

No alerts14d4 checks · Risk SAFE
  • Gen Agent Trust Hub14d

    The skill provides a technical reference for modern JavaScript features and toolchains. It includes an indirect prompt injection surface by directing the agent to read and analyze project files, such as lockfiles and build configurations, which may contain untrusted data that could influence the agent's code generation logic.

  • Socket14d

    No alerts

  • Snyk14d

    Risk: LOW · No issues

  • Runlayer7mo

    12 files scanned · No issues

Signed by skilld at 00695ba. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 weeks ago.

Activeupdated 3 weeks ago

README badge

README badge for ccheney/robust-skills/modern-javascript