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

# ES2024 Features

Object.groupBy(), Map.groupBy(), Promise.withResolvers(), String.isWellFormed(), String.toWellFormed(), RegExp v flag (Unicode sets), resizable ArrayBuffers, Atomics.waitAsync().

## Object.groupBy() and Map.groupBy()

Group array elements by a key. Use `Map.groupBy` when keys are non-strings or insertion order matters.

```javascript
const inventory = [
  { name: 'asparagus', type: 'vegetable', quantity: 5 },
  { name: 'banana',    type: 'fruit',     quantity: 0 },
  { name: 'cherry',    type: 'fruit',     quantity: 5 },
  { name: 'fish',      type: 'meat',      quantity: 22 }
];

const byType = Object.groupBy(inventory, item => item.type);
// { vegetable: [...], fruit: [...], meat: [...] }

const byAvailability = Object.groupBy(inventory, item =>
  item.quantity > 0 ? 'inStock' : 'outOfStock'
);

// Map.groupBy — keys are Date objects (non-string)
const byDate = Map.groupBy(events, event => event.date);
```

Replaces `items.reduce((acc, item) => { ... }, {})` grouping patterns.

## Promise.withResolvers()

Expose resolve/reject outside the Promise constructor. Replaces the awkward `let resolve, reject; new Promise((res, rej) => { resolve = res; reject = rej; })` pattern.

```javascript
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('done'), 1000);
await promise; // 'done'

// Event-to-Promise
function waitForEvent(element, eventName) {
  const { promise, resolve } = Promise.withResolvers();
  element.addEventListener(eventName, resolve, { once: true });
  return promise;
}
await waitForEvent(document.querySelector('button'), 'click');
```

## Well-Formed Unicode Strings

Check and fix malformed UTF-16 (lone surrogates).

```javascript
'Hello 👋'.isWellFormed();  // true
'Hi \uD800'.isWellFormed(); // false

'Hi \uD800'.toWellFormed(); // 'Hi \uFFFD' (replacement character)

// Safe URL encoding
const safeEncode = str => encodeURIComponent(str.toWellFormed());
```

## RegExp v Flag (Unicode Sets)

Set operations in character classes.

```javascript
const emoji       = /[\p{Emoji}--\p{ASCII}]/v;  // Emoji minus ASCII
const greekLetters = /\p{Script=Greek}/v;
const consonants  = /[[a-z]&&[^aeiou]]/v;        // intersection
const fiveToNine  = /[[0-9]--[0-4]]/v;           // difference
```

## Resizable ArrayBuffers

```javascript
const buffer = new ArrayBuffer(1024, { maxByteLength: 4096 });
buffer.resizable;    // true
buffer.byteLength;   // 1024
buffer.resize(2048);
buffer.byteLength;   // 2048

const newBuffer = buffer.transfer(512);
buffer.byteLength;    // 0 (detached)
newBuffer.byteLength; // 512
```

## Atomics.waitAsync()

Non-blocking shared memory wait (usable on main thread).

```javascript
const sharedArray = new Int32Array(new SharedArrayBuffer(4));

const result = Atomics.waitAsync(sharedArray, 0, 0);
if (result.async) {
  result.value.then(status => console.log('Woke up:', status)); // 'ok' | 'timed-out'
}

// In another context/worker
Atomics.notify(sharedArray, 0);
```

## Browser/Node.js Support

| Feature | Chrome | Firefox | Safari | Node.js |
|---------|--------|---------|--------|---------|
| Object.groupBy | 117+ | 119+ | 17.4+ | 21+ |
| Promise.withResolvers | 119+ | 121+ | 17.4+ | 22+ |
| String.isWellFormed | 111+ | 119+ | 16.4+ | 20+ |
| RegExp v flag | 112+ | 116+ | 17+ | 20+ |
| Resizable ArrayBuffer | 111+ | 111+ | 16.4+ | 20+ |

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