<!-- SPDX-License-Identifier: MIT -->
<!-- SPDX-FileCopyrightText: 2025-2026 Marcus Quinn -->

# ES2025 Features

Set operations, iterator helpers, explicit resource management, Array.fromAsync, Error.isError, Promise.try, Float16, Intl.DurationFormat, import attributes, RegExp.escape, pattern modifiers, duplicate named capture groups.

## Set Methods

```javascript
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

setA.union(setB);                // Set {1, 2, 3, 4, 5, 6}
setA.intersection(setB);         // Set {3, 4}
setA.difference(setB);           // Set {1, 2}
setA.symmetricDifference(setB);  // Set {1, 2, 5, 6}

const setC = new Set([1, 2]);
setC.isSubsetOf(setA);           // true
setA.isSupersetOf(setC);         // true
setA.isDisjointFrom(new Set([7, 8])); // true

const missing = requiredPermissions.difference(userPermissions);
const hasAll  = requiredPermissions.isSubsetOf(userPermissions);
```

## Iterator Helpers

Process iterators lazily — no intermediate arrays.

```javascript
const nums = [1, 2, 3, 4, 5].values();

nums.map(n => n * 2)
nums.filter(n => n % 2 === 0)
nums.take(3)
nums.drop(2)
nums.flatMap(arr => arr)
nums.reduce((a, b) => a + b, 0) // 15
nums.toArray()
nums.some(n => n > 2)           // true
nums.every(n => n > 0)          // true
nums.find(n => n > 2)           // 3
nums.forEach(n => console.log(n))

// Iterator.from() — wrap any iterable
Iterator.from(new Set([1, 2, 3]))
  .filter(n => n > 1)
  .map(n => n * 2)
  .toArray(); // [4, 6]

// Lazy — only processes 10 of 1M items
generateLargeDataset()
  .filter(item => item.value > 0.9)
  .take(10)
  .toArray();
```

## RegExp.escape()

Replaces manual escaping (`userInput.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')`).

```javascript
const escaped = RegExp.escape('price: $100 (USD)');
// 'price: \\$100 \\(USD\\)'

function createSearchPattern(term) {
  return new RegExp(RegExp.escape(term), 'gi');
}
```

## RegExp Pattern Modifiers (Inline Flags)

Apply flags to part of a pattern only.

```javascript
/^(?i:hello) world$/.test('HELLO world');  // true — only hello is case-insensitive
/^(?i:hello) world$/.test('Hello World');  // false — World not modified

/(?i:abc)(?-i:DEF)/  // abc case-insensitive, DEF exact
/(?i:readme)\.md$/   // README.md ✓, readme.MD ✗
```

## Duplicate Named Capture Groups

Reuse group names across alternatives. Previously required unique names per alternative.

```javascript
const datePattern = /(?<year>\d{4})-(?<month>\d{2})|(?<month>\d{2})\/(?<year>\d{4})/;

'2024-03-15'.match(datePattern).groups; // { year: '2024', month: '03' }
'03/15/2024'.match(datePattern).groups; // { year: '2024', month: '03' }
```

## Explicit Resource Management (`using`)

Automatic cleanup via `Symbol.dispose` / `Symbol.asyncDispose`.

```javascript
{ using file = openFile('data.txt'); } // file[Symbol.dispose]() called on exit

{ await using db = await connectDatabase(); await db.query('...'); }

class FileHandle {
  #handle;
  constructor(path) { this.#handle = fs.openSync(path); }
  [Symbol.dispose]() { fs.closeSync(this.#handle); }
}

// Stack — multiple resources, disposed in reverse order
{ using stack = new DisposableStack();
  const f1 = stack.use(openFile('a.txt'));
  const f2 = stack.use(openFile('b.txt')); }

{ await using stack = new AsyncDisposableStack();
  const c1 = stack.use(await connect('db1'));
  const c2 = stack.use(await connect('db2')); }
```

**Key symbols:** `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`

## Array.fromAsync()

Async version of `Array.from`. Processes lazily (one at a time), unlike `Promise.all` (fetches all upfront).

```javascript
const pages = await Array.fromAsync(fetchPages());          // from async generator
const chunks = await Array.fromAsync(readableStream);       // from async iterable
const doubled = await Array.fromAsync(gen(), async x => x * 2); // with mapper
const results = await Array.fromAsync([Promise.resolve(1), Promise.resolve(2)]); // [1, 2]
```

## Error.isError()

Reliable cross-realm error check (`instanceof` fails across iframes/workers/vm).

```javascript
Error.isError(new TypeError('bad'));   // true
Error.isError({ message: 'fake' });    // false
Error.isError(null);                   // false
Error.isError(iframeError);            // true (instanceof would be false)
```

## Promise.try()

Wrap sync-or-async code in a Promise chain. Replaces `Promise.resolve().then(() => fn())`.

```javascript
const result = await Promise.try(() => riskyOperation());

Promise.try(() => syncOrAsync())
  .then(r => console.log(r))
  .catch(e => console.error(e));
```

## Import Attributes

```javascript
import config from './config.json' with { type: 'json' };
import styles from './styles.css'  with { type: 'css' };
const data = await import('./data.json', { with: { type: 'json' } });
```

## Float16 Support

```javascript
const f16 = new Float16Array([1.5, 2.5, 3.5]);
Math.f16round(1.337);   // rounds to float16 precision

const view = new DataView(new ArrayBuffer(2));
view.setFloat16(0, 1.5);
view.getFloat16(0);     // 1.5
```

## Intl.DurationFormat

```javascript
const dur = { hours: 1, minutes: 46, seconds: 40 };
new Intl.DurationFormat('en', { style: 'long'    }).format(dur); // "1 hour, 46 minutes, 40 seconds"
new Intl.DurationFormat('en', { style: 'short'   }).format(dur); // "1 hr, 46 min, 40 sec"
new Intl.DurationFormat('en', { style: 'narrow'  }).format(dur); // "1h 46m 40s"
new Intl.DurationFormat('en', { style: 'digital' }).format(dur); // "1:46:40"
new Intl.DurationFormat('fr', { style: 'long'    }).format(dur); // "1 heure, 46 minutes et 40 secondes"
```

## Browser/Node.js Support

| Feature | Chrome | Firefox | Safari | Node.js |
|---------|--------|---------|--------|---------|
| Set methods | 122+ | 127+ | 17+ | 22+ |
| Iterator helpers | 122+ | 131+ | 18+ | 22+ |
| Iterator.from | 122+ | 131+ | 18+ | 22+ |
| RegExp.escape | 136+ | - | - | 23+ |
| Explicit Resource Mgmt | 134+ | - | - | 22+ |
| Array.fromAsync | 121+ | 115+ | 16.4+ | 22+ |
| Error.isError | - | - | - | - |
| Promise.try | 128+ | 132+ | 18+ | 22+ |
| Float16 | 121+ | 129+ | - | 22+ |
| Intl.DurationFormat | 129+ | - | 16.4+ | - |

Check https://caniuse.com for current status.
