Current section

Files

Jump to
quickbeam CHANGELOG.md
Raw

CHANGELOG.md

# Changelog
## 0.3.1
### Added
- **`eval/3` `:vars` option** — pass variables into JS code as temporary globals, cleaned up automatically via `try`/`finally` (even on errors or timeouts)
- **`set_global/3`** — set JS globals from Elixir using native BEAM→JS conversion, counterpart to `get_global/2`
## 0.3.0
### Changed
- **OXC upgraded to 0.5.0** — adds `jsx_factory`, `jsx_fragment`, and `import_source` options for JSX transform and bundle
- **Replaced examples** — removed `content_pipeline` and `plugin_sandbox`, added:
- `rule_engine` — user-defined business rules in sandboxed runtimes (`apis: false`, memory limits, timeouts, hot reload, handlers as controlled API)
- `live_dashboard` — Workers (BEAM processes) + BroadcastChannel (`:pg`) for parallel metrics computation with crash recovery
- `ssr` now uses JSX syntax via OXC classic mode
### Added
- **Node.js compatibility APIs**`process`, `path`, `fs`, `os` backed by OTP
- **`:apis` option** for `QuickBEAM.start/1` — controls which API surface to load:
- `:browser` — Web APIs (default, same as before)
- `:node` — Node.js compat
- `[:browser, :node]` — both
- `false` — bare QuickJS, no polyfills
- **Extended DOM API** backed by lexbor C library:
- `document.createDocumentFragment()`, `document.createComment()`
- `document.createElementNS()` for SVG/MathML/namespaced elements
- `document.getElementsByClassName()`, `document.getElementsByTagName()`
- `element.insertBefore()`, `element.replaceChild()`, `element.cloneNode()`
- `element.contains()`, `element.remove()`, `element.before()`, `element.after()`
- `element.prepend()`, `element.append()`, `element.replaceWith()`
- `element.matches()`, `element.closest()`
- `element.getElementsByClassName()`, `element.getElementsByTagName()`
- `element.lastChild`, `element.previousSibling`, `element.nodeType`, `element.nodeName`, `element.nodeValue`, `element.parentElement`
- `element.className` is now read-write
- `DocumentFragment` correctly moves children in `appendChild`/`insertBefore`
- **`element.classList`** — full `DOMTokenList` implementation (`add`, `remove`, `toggle`, `contains`, `replace`, `forEach`, `item`, `length`, `value`, `Symbol.iterator`)
- **`element.style`**`CSSStyleDeclaration` backed by lexbor CSS parser (`getPropertyValue`, `setProperty`, `removeProperty`, `getPropertyPriority`, `cssText`, `length`, `item`, camelCase property access via Proxy)
- **`getComputedStyle()`** — returns inline style declaration (no cascade/layout)
- **`addEventListener`/`removeEventListener`/`dispatchEvent`** on elements and `document`, with `once`, `stopImmediatePropagation`, `handleEvent` object listeners
- **`CustomEvent`** with `detail` property
### Improved
- Import extraction uses `OXC.imports/2` NIF instead of full AST parsing — faster bundling and script loading
### Fixed
- Locks tests failing due to redundant `LockManager` start (already started by application supervisor)
- CI cache keys now hash Zig sources, fixing stale native builds
- ASAN CI job correctly finds beam binary path
### Infrastructure
- `clang-tidy` static analysis for `lexbor_bridge.c` in CI (`clang-analyzer-*`, `bugprone-*`, `portability-*`)
## 0.2.0
### Breaking
- **Unified `Beam` namespace**`beam.call``Beam.call`, `beam.callSync``Beam.callSync`, `beam.send``Beam.send`, `beam.self``Beam.self`, `Process.onMessage``Beam.onMessage`, `Process.monitor``Beam.monitor`, `Process.demonitor``Beam.demonitor`
### Added
- Auto-bundle imports and TypeScript in `:script` option — `.ts`/`.tsx` files are transformed via OXC, files with `import` statements are bundled into a single IIFE with relative paths and bare specifiers resolved from `node_modules/`
- `QuickBEAM.JS.bundle_file/2` for programmatic bundling of entry files with all dependencies
- npm dependency management via `mix npm.install`
### Performance
- Atom cache for QuickJS↔BEAM boundary — pre-created JS strings for common atoms (`nil`, `true`, `false`, `ok`, `error`, etc.) avoid repeated allocations on every conversion
- Direct promise result inspection via `JS_PromiseState`/`JS_PromiseResult` — removes temporary globals, eval overhead, and a per-iteration string leak
- Lazy proxy objects for BEAM→JS map conversion — maps with >4 entries are wrapped in a `BeamMapProxy` backed by the original BEAM term, converting properties only on access
- Zero-copy string optimizations — map key conversion uses `JS_NewAtomLen`/`JS_AtomToCStringLen` directly, removing intermediate allocations and the 256-byte key length limit
- Worker `postMessage` uses direct `beam.send` instead of routing through `beam.call` handlers
- Non-blocking NIFs — `eval`/`call`/`compile` return immediately with a ref, results delivered asynchronously via `enif_send`. Dirty IO schedulers are no longer blocked.
### Fixed
- **Segfault on concurrent runtime init** — QuickJS class IDs for `BeamMapProxy` and DOM were allocated under separate mutexes, causing ID collisions when multiple runtimes initialized concurrently. All custom class IDs are now allocated under a single shared mutex.
- Deadlock on runtime shutdown — `beam.callSync` now uses a `shutting_down` flag with timed polling so the worker thread exits cleanly when `GenServer.stop` is called while a sync call is pending
- Locks and BroadcastChannel tests failing with `--no-start`
- Test suite hanging under parallel execution due to missing `:telemetry` dependency
## 0.1.0
Initial release.
### JavaScript Engine
- QuickJS-NG embedded via Zig NIFs — no system JS runtime needed
- Runtimes are GenServers with full OTP supervision support
- Persistent state across `eval/2` and `call/3` calls
- ES module loading with `load_module/3`
- Bytecode compilation and loading for fast cloning
- CPU timeout with `JS_SetInterruptHandler`
- Configurable memory limit and stack size
### BEAM Integration
- `Beam.call` / `Beam.callSync` — JS calls Elixir handler functions
- `Beam.send` / `Beam.self` — JS sends messages to BEAM processes
- `Beam.onMessage` / `Beam.monitor` / `Beam.demonitor`
- Direct BEAM term conversion (no JSON serialization)
- Runtime pools via NimblePool
### Web APIs
Standard browser APIs backed by BEAM/Zig primitives:
- `fetch`, `Request`, `Response`, `Headers``:httpc`
- `document`, `querySelector`, `createElement` — lexbor native DOM
- `URL`, `URLSearchParams``:uri_string`
- `crypto.subtle` (digest, sign, verify, encrypt, decrypt, generateKey, deriveBits) — `:crypto`
- `crypto.getRandomValues`, `randomUUID` — Zig `std.crypto.random`
- `TextEncoder`, `TextDecoder` — native Zig UTF-8
- `TextEncoderStream`, `TextDecoderStream`
- `ReadableStream`, `WritableStream`, `TransformStream` with `pipeThrough`/`pipeTo`
- `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval` — Zig timer heap
- `console.log/warn/error/debug/trace/assert/time/timeEnd/count/dir/group` — Erlang Logger
- `CompressionStream`, `DecompressionStream``:zlib`
- `Buffer` (encode, decode, byteLength) — `Base`, `:unicode`
- `EventTarget`, `Event`, `CustomEvent`, `MessageEvent`, `CloseEvent`, `ErrorEvent`
- `AbortController`, `AbortSignal`
- `Blob`, `File`
- `BroadcastChannel``:pg` (distributed across cluster)
- `WebSocket``:gun`
- `Worker` — BEAM process-backed JS workers with `postMessage`
- `navigator.locks` (Web Locks API) — GenServer with monitor-based cleanup
- `localStorage` — ETS (shared across runtimes)
- `EventSource` (Server-Sent Events) — `:httpc` streaming
- `DOMException`
- `atob`, `btoa` — Zig base64
- `structuredClone` — QuickJS serialization
- `queueMicrotask``JS_EnqueueJob`
- `performance.now` — nanosecond precision
### DOM
- Native DOM via lexbor C library
- Elixir-side DOM queries: `dom_find/2`, `dom_find_all/2`, `dom_text/2`, `dom_attr/3`, `dom_html/1`
- Returns Floki-compatible `{tag, attrs, children}` tuples
### TypeScript Toolchain
- `QuickBEAM.JS` — parse, transform, minify, bundle via OXC Rust NIFs
- `QuickBEAM.eval_ts/3` — evaluate TypeScript directly
- TypeScript sources compiled at build time via OXC (no Node.js/Bun required)