# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/no-js-dev/nojs/compare/v1.23.0...HEAD)

## [1.23.0](https://github.com/no-js-dev/nojs/compare/v1.22.0...v1.23.0) — 2026-11-12

### Added

- Package `exports` map with a Node-safe ESM compiler entry, `@no-js-dev/nojs/compiler` (`dist/compiler/compiler.mjs`, built by `node build.js`): `parseExpression`, `parseStatements`, `segmentPipes` and `evaluate` without the framework runtime. Additive: in bundlers (`browser` condition) the package root still resolves to `dist/iife/no.js`, as the `browser` field did; Node `import` / `require` of the root now resolve to `src/index.js` (new `main` / `module` fields); `src/*`, `dist/*`, `types/*` and `package.json` have explicit entries and a final `"./*"` catch-all keeps every other existing path importable, so nothing previously reachable is blocked. Re-applies the exports half of [#348](https://github.com/no-js-dev/nojs/pull/348) ([#333](https://github.com/no-js-dev/nojs/issues/333))
- `strictUnmounted`: `on:unmounted` also fires when a directive removes the element (`if` / `else-if`, `each` / `foreach`, a route change), once and on the next microtask; a keyed `each` row that only moved no longer fires it. By default the hook only sees removals made by your own code, as in v1.21.1, because directives dispose the element (and the hook's observer) before removing it
- `router.strictOutlets`: only the default outlet looks routes up in the `router.templates` folder; a named outlet (`route-view="name"`) without its own `src` is filled only by `outlet="name"` routes, so on an explicitly matched route that does not fill it, it is cleared instead of requesting `pages/<route>.tpl` and rendering the `route="*"` wildcard or the built-in 404 (the v1.21.1 default, kept). Named outlets with `src` and the wildcard fallback on pages no explicit route matches are unchanged

### Fixed

- Watchers disposed while a reactive update is running no longer run in that update. A `switch` case being hidden used to re-evaluate its content once with the new value after getting `display: none`, so a `get` inside it requested the new URL (twice) and an `sse` reopened its connection for a case that was already hidden. Content that stays visible is unaffected. Cases hidden on the first render are still reactive until shown and hidden once; this is documented as a known limitation in [Conditionals](docs/md/conditionals.md#hidden-cases)
- Docs: the filters and i18n pages state that `number`, `currency`, `date` and `datetime` format with the browser's default locale, not `$i18n.locale`, and that `relative` / `fromNow` return English strings; the example outputs now match what core produces in an `en-US` browser (`R$1,234.56`, `2/25/26`, `2/25/2026, 3:45:00 PM`, `2h ago`, `3d ago`, `in 2h`). No code change

## [1.22.0](https://github.com/no-js-dev/nojs/compare/v1.21.1...v1.22.0) — 2026-11-10

Nine opt-in hardening flags, a v1.21.1 compatibility suite, and a batch of fixes. Every hardening flag defaults to `false`: without them, v1.21.1 behavior is unchanged.

### Added

- `scopeHeaders`: global `NoJS.config({ headers })` are sent only to the page origin, the `baseApiUrl` origin or the origin of the nearest `base=` ancestor; per-request `headers` still apply to any origin ([`3972a3c`](https://github.com/no-js-dev/nojs/commit/3972a3c), based on [#356](https://github.com/no-js-dev/nojs/pull/356) by @evertonfraga)
- `strictRedaction`: the configured `csrf.header` counts as a sensitive header, so untrusted interceptors can neither read nor override it; untrusted response interceptors no longer see sensitive response headers (`authorization`, `x-auth-*`, `x-api-*`, the CSRF header); URLs given to untrusted interceptors, to the `fetch:success` / `fetch:error` / `error` payloads and to failure warnings drop the `#fragment` and replace sensitive query values with `[REDACTED]` (names matched by word, so `author` or `sort_key` stay intact) ([`94ae9b1`](https://github.com/no-js-dev/nojs/commit/94ae9b1), based on [#366](https://github.com/no-js-dev/nojs/pull/366) by @evertonfraga)
- `strictDevtools`: `nojs:devtools` events and context ids require a local host; store and element snapshots, the globals getter, plugin options, `ctx:updated` values and config headers are redacted at every depth; fetch event URLs are redacted ([`8826d75`](https://github.com/no-js-dev/nojs/commit/8826d75), [`da04c54`](https://github.com/no-js-dev/nojs/commit/da04c54), based on [#370](https://github.com/no-js-dev/nojs/pull/370) by @evertonfraga)
- `strictJsonld`: `page-jsonld` values inside JSON strings are escaped as string content, so they cannot add properties and the output stays valid JSON; `<` is written as `<`. The default output is the v1.21.1 output byte-for-byte ([`0145189`](https://github.com/no-js-dev/nojs/commit/0145189), based on [#358](https://github.com/no-js-dev/nojs/pull/358) by @evertonfraga)
- `strictDirectives`: `NoJS.directive()` rejects names covered by a core wildcard (`bind-href`, `on:click`, `class-x`, `style-x`) with the same warning as a core name. The registry freezes at load, so set the flag before any `NoJS.directive()` / `NoJS.use()` call ([`9b4b7da`](https://github.com/no-js-dev/nojs/commit/9b4b7da), based on [#371](https://github.com/no-js-dev/nojs/pull/371) by @evertonfraga)
- `strictBindings`: `bind-on*` handler attributes (`on` + no hyphen, any case) are refused with a warning (`bind-onboarding-step` still binds); `bind-srcdoc` goes through the `bind-html` sanitizer; `bind-xlink:href` gets the same URL scheme check as `bind-href`. Read when each binding initializes; once on, it cannot be turned off after `init()` ([`c988ee2`](https://github.com/no-js-dev/nojs/commit/c988ee2) from [#353](https://github.com/no-js-dev/nojs/pull/353), [`3577819`](https://github.com/no-js-dev/nojs/commit/3577819) from [#355](https://github.com/no-js-dev/nojs/pull/355), [#417](https://github.com/no-js-dev/nojs/pull/417))
- `templates.pageRelative`: relative `<template src>` paths (includes, route templates, file-based routing) are fetched relative to the page instead of going through `baseApiUrl` / `base=`; `./x` inside a remote template still resolves against the parent template, and absolute URLs are unchanged ([`468730e`](https://github.com/no-js-dev/nojs/commit/468730e), based on [#362](https://github.com/no-js-dev/nojs/pull/362) by @evertonfraga)
- `i18n.escapeParams`: `t-html` param values are HTML-escaped before interpolation (the translation string stays markup); accepted by `NoJS.config({ i18n })` and `NoJS.i18n()`, and switching it after `init()` re-renders active `t` directives ([`94ed5f7`](https://github.com/no-js-dev/nojs/commit/94ed5f7), based on [#360](https://github.com/no-js-dev/nojs/pull/360) by @evertonfraga)
- `i18n.strictLocale`: a persisted `nojs-locale` outside a non-empty `supportedLocales` is ignored and removed from `localStorage`, and the locale falls back to browser detection / `defaultLocale` ([`f1f5263`](https://github.com/no-js-dev/nojs/commit/f1f5263), based on [#360](https://github.com/no-js-dev/nojs/pull/360) by @evertonfraga)
- The "Failed to load template" warning names the resolved URL and the `templates.pageRelative` opt-in when `base=` or `baseApiUrl` changed the template URL ([`b83267e`](https://github.com/no-js-dev/nojs/commit/b83267e))
- Warnings for silent failures (stored values and sent headers unchanged): `NoJS.global()` names the function paths an object value loses in its JSON copy; a sensitive header (`Authorization`, `X-CSRF-Token`) set by an untrusted request interceptor is still discarded, now with a warning once per interceptor and header (the value is never logged); `NoJS.use()` warns when the plugin object itself sets `trusted: true`, which is ignored ([#417](https://github.com/no-js-dev/nojs/pull/417))
- v1.21.1 compatibility suite (unit + Playwright on 3 engines) and `scripts/compat-check.sh`, which builds any git ref in a throwaway worktree and runs the suite against it; `test-server.js` serves another build at `/__local__/no.js` via `NOJS_BUILD_PATH` ([`ce842f6`](https://github.com/no-js-dev/nojs/commit/ce842f6), from [#388](https://github.com/no-js-dev/nojs/pull/388))

### Changed

- Router: a template the router resolves from its templates folder (file-based pages, layout probes, nested outlets, prefetch) whose response is a full HTML document (`<!doctype html>` / `<html>`) is treated as not found, with a warning. With an SPA fallback server, unmatched URLs now render `route="*"` / the built-in 404 instead of the app shell, and the fallback is never used as a layout. Explicit `<template src>` and `<template route src>` are unchanged ([#419](https://github.com/no-js-dev/nojs/pull/419))
- HTTP verb directives and `call`: a JSON `body` template such as `body='{"name": "{user.name}"}'` is sent as JSON (string values escaped, other values serialized) instead of a mangled URL-encoded text body; object-literal bodies (`body="{ id: item.id }"`, `body="{ email, password }"`) and a placeholder holding an object (`body="{payload}"`) are sent as JSON; `body="{ }"` sends `{}` instead of no body. Bodies that v1.21.1 sent as valid JSON, form-encoded or plain text keep their exact bytes and QUERY cache key ([#421](https://github.com/no-js-dev/nojs/pull/421))
- Expressions: code the parser used to drop silently now logs one `[No.JS] Expression error:` warning per expression at parse time, with unchanged results: the comma operator and other leftover tokens, assignments or `++`/`--` inside a ternary branch, an `&&`/`||` operand or a binding, and statement keywords (`if (…) { … }` now reports that statements are not supported instead of `if is not defined`) ([#420](https://github.com/no-js-dev/nojs/pull/420))
- Statement handlers: when a statement throws, in-place changes (`items.push(x)`) and `$store` writes made before the error now reach the DOM; `name = value` writes are still not applied ([#420](https://github.com/no-js-dev/nojs/pull/420))

### Fixed

- Test and docs dev servers: the static-file path guard no longer accepts sibling directories that share the root's name prefix, and `docs/dev-server.js` now applies the same guard; `test-server.js` accepts `TEST_SERVER_HOST` (e.g. `127.0.0.1`) to bind loopback only and logs the bound address ([#361](https://github.com/no-js-dev/nojs/pull/361), @evertonfraga)
- Router (history mode): the first page load and Back/Forward now keep `?query` and `#hash`, so `$route.query`/`$route.hash` are filled in and the URL is no longer rewritten without them; a query value that contains `?` (e.g. `?next=/a?b=1`) is no longer truncated ([#364](https://github.com/no-js-dev/nojs/pull/364), @evertonfraga)
- HTTP and SSE URL placeholders (`get`/`post`/`put`/`patch`/`delete`, `call`, `sse`): a placeholder in the path part whose value is exactly `.` or `..` is now escaped to `%252E` / `%252E%252E`, so it can no longer re-target the request to a parent path (e.g. `/api/users/{id}` with `id = ".."` used to request `/api/`). On by default, with no opt-out; a warning is logged once per request. Query-string and fragment placeholders and request bodies are unchanged ([#369](https://github.com/no-js-dev/nojs/pull/369), @evertonfraga)
- Directive errors during the DOM walk: a directive whose `init` throws no longer stops the rest of the page from rendering. The error is warned on its element and the first one is rethrown when the outermost walk finishes (inside a reactive batch update it is only warned). `NoJS.init()` still initializes the router and plugins and emits `plugins:ready`, then rejects with that error. A disposer that throws is warned and no longer stops the remaining disposers (based on [#375](https://github.com/no-js-dev/nojs/pull/375) by @evertonfraga)
- Router: when a newer navigation starts before the current one has rendered (e.g. a slow `lazy="ondemand"` or file-based template), the stale render is dropped instead of overwriting the newer page, `<head>` or route listeners. A navigation stopped by a guard or a hash-only change does not cancel a render in progress ([#380](https://github.com/no-js-dev/nojs/pull/380), @evertonfraga)
- Forms with a `loading` template send their fields again: they are read before the loading template replaces the form content (the body used to be `{}`) ([#363](https://github.com/no-js-dev/nojs/pull/363), @evertonfraga)
- Request interceptors run for every concurrent request: the re-entrancy guard is scoped to the synchronous interceptor call (`_guardReentry`), so sibling requests no longer skip the chain while a fetch issued from inside an interceptor still does ([#365](https://github.com/no-js-dev/nojs/pull/365), @evertonfraga)
- i18n flat `loadPath` mode (`/locales/{locale}.json`): switching locale fetches the new locale file before re-rendering ([#367](https://github.com/no-js-dev/nojs/pull/367), @evertonfraga)
- Reactivity: the `_collectKeys` cache no longer chains into itself, which kept every value ever assigned to state alive on long-lived pages ([#376](https://github.com/no-js-dev/nojs/pull/376), @evertonfraga)
- Loops: `each` re-renders after in-place array mutations (`push`, `splice`, index assignment, `reverse`, `sort`); `const` / `let` in `on:*` handlers create handler-local bindings that are never written to state, instead of being dropped silently ([`703bee2`](https://github.com/no-js-dev/nojs/commit/703bee2), from [#352](https://github.com/no-js-dev/nojs/pull/352))
- Filters: quoted arguments keep their whitespace (`join:', '` renders `a, b, c`), and empty or whitespace-only quoted arguments stay strings ([`c0e1a97`](https://github.com/no-js-dev/nojs/commit/c0e1a97), filter part of [#348](https://github.com/no-js-dev/nojs/pull/348))
- Validators: `NoJS.validator()` reserves only the five names Elements implements (`required`, `email`, `url`, `min`, `max`); `minlength`, `pattern`, `match` and the other names blocked since v1.15.0 can be registered again ([`478932a`](https://github.com/no-js-dev/nojs/commit/478932a), from [#396](https://github.com/no-js-dev/nojs/pull/396))
- Devtools: with `devtools: true` on a non-local host, contexts are no longer kept in an unreachable registry; context ids and `nojs:devtools` events are unchanged ([`bb8c10d`](https://github.com/no-js-dev/nojs/commit/bb8c10d))
- Stores: expressions that read or write more than one store update every store's bindings (`then="$store.auth.user = …; $store.ui.mode = …"`, `on:click` handlers, `bind="$store.auth.user + $store.ui.mode"`), and reactive `get` / `sse` URLs re-fetch when any named store changes; only the first store was notified since v1.13.0 ([#420](https://github.com/no-js-dev/nojs/pull/420))
- `parseStatements()` (tooling export) parses `const` / `let` as `VarDecl` nodes, like the runtime, instead of returning an identifier named `const` ([#420](https://github.com/no-js-dev/nojs/pull/420))
- Router prefetch no longer requests `pages/<pattern>.tpl` for inline `<template route>` definitions, `:param` / `*` patterns or paths that already have an explicit route for that outlet; `<a route>` links to file-based pages are prefetched as before ([#419](https://github.com/no-js-dev/nojs/pull/419))
- Browser globals: allow-listed `navigator` and `window` members now work on the real object instead of throwing `Illegal invocation` (or the Firefox/WebKit equivalent) and evaluating to `undefined`: `navigator.language`, `languages`, `onLine`, `userAgent`, `platform`, `cookieEnabled`, `hardwareConcurrency`, `maxTouchPoints`, `navigator.clipboard.writeText()`, and `window.innerWidth`, `innerHeight`, `scrollX`, `scrollY`, `pageXOffset`, `pageYOffset`, `devicePixelRatio`, `matchMedia()`, also through `document.defaultView` and `window.navigator`. These reads are not reactive. Every other member and the deny-lists behave as in v1.21.1 ([#425](https://github.com/no-js-dev/nojs/pull/425))

### Documentation

- Routing: recipe for removing one-time tokens (`?token=`, `?code=`) from the URL with `$router.replace()` now that history mode keeps the query ([#364](https://github.com/no-js-dev/nojs/pull/364))
- Routing: what happens to a navigation that a newer one supersedes ([#380](https://github.com/no-js-dev/nojs/pull/380))
- Configuration: "Opt-in hardening flags" table and per-topic notes for all nine flags (configuration, data fetching, plugins, head management, i18n, `llms*.txt`, docs site in 5 locales); the custom-directive warning now says the registry freezes at load ([`89a6027`](https://github.com/no-js-dev/nojs/commit/89a6027), [`9db464f`](https://github.com/no-js-dev/nojs/commit/9db464f), [`fb4eb8f`](https://github.com/no-js-dev/nojs/commit/fb4eb8f), [`18830ba`](https://github.com/no-js-dev/nojs/commit/18830ba), [`13fcd2e`](https://github.com/no-js-dev/nojs/commit/13fcd2e), [`c876909`](https://github.com/no-js-dev/nojs/commit/c876909))
- Corrected documented behavior: `if` keeps its host element; interceptor trust rules, with `Authorization` examples moved to a trusted plugin; `NoJS.global` drops functions inside object values; `into` writes to `$store.<into>.<as>`; `NoJS.notify()` after a store's `$set()` / `$notify()`; custom directive contract and `el.__disposers` cleanup; `router.current` / `router.on()` and `router.base`; absolute `templates` path for history-mode deep links; `fetch:end` fires only when pagination ends, plus the `error` bus event ([#418](https://github.com/no-js-dev/nojs/pull/418))
- Events and Routing pages (all 5 locales): unsupported statement forms and handler error semantics; prefetch rules and SPA fallback handling ([#419](https://github.com/no-js-dev/nojs/pull/419), [#420](https://github.com/no-js-dev/nojs/pull/420), [#422](https://github.com/no-js-dev/nojs/pull/422))
- Examples that failed at runtime: the error-handling retry button calls `$refs.users.refresh()`; the CDN snippet copy button uses a `$copy` helper registered with `NoJS.global`; form submissions are documented as a JSON object of the fields, without `<input type="file">` uploads ([#423](https://github.com/no-js-dev/nojs/pull/423))
- `llms.txt`: replace the stale v1.17.0 latest-release line ([`127a6d6`](https://github.com/no-js-dev/nojs/commit/127a6d6))

### Chore

- Version sync script (`scripts/version.mjs` sync / check), release hooks and `version:check` in CI ([`a1d7a00`](https://github.com/no-js-dev/nojs/commit/a1d7a00))
- CI runs on pull requests into release branches ([`4fcd400`](https://github.com/no-js-dev/nojs/commit/4fcd400))
- Dev-dependency bumps (jsdom 30, Playwright 1.62, esbuild 0.28.2 and others); `dist/` is byte-identical ([`81c3922`](https://github.com/no-js-dev/nojs/commit/81c3922), from [#377](https://github.com/no-js-dev/nojs/pull/377))

## [1.21.1](https://github.com/no-js-dev/nojs/compare/v1.21.0...v1.21.1) — 2026-08-14

### Fixed

- `each`/`filter` now re-evaluates when outer-scope dependencies change ([#335](https://github.com/no-js-dev/nojs/issues/335))

## [1.21.0](https://github.com/no-js-dev/nojs/compare/v1.20.2...v1.21.0) — 2026-08-12

### Added

- Compiler-support parser exports: `parseExpression`, `parseStatements`, and `segmentPipes` are now named exports from the module entry point (`src/index.js`), enabling offline tooling such as the `@no-js-dev/server` compiler to parse NoJS expressions without running the framework (ADR-024)
- Package entry-point reachability tests for the three parser exports

### Docs

- Added "Compiler Support — Parser Exports" section to `plugins.md`, `llms.txt`, and `llms-full.txt` documenting function signatures, the `segmentPipes`-before-`parseExpression` rule, and Forbidden node semantics

## [1.20.2](https://github.com/no-js-dev/nojs/compare/v1.20.1...v1.20.2) — 2026-08-05

Patch release — lockfile compatibility fix and version alignment with Elements v1.20.2 fixes.

### Chore

- chore(deps): regenerate `package-lock.json` with npm 10 so `npm ci` works on node 20/22 — fixes "Missing conventional-commits-parser" for npm 10 consumers ([`bb8e019`](https://github.com/no-js-dev/nojs/commit/bb8e019), PR [#330](https://github.com/no-js-dev/nojs/pull/330))

## [1.20.1](https://github.com/no-js-dev/nojs/compare/v1.20.0...v1.20.1) — 2026-08-04

Patch release — bug fixes, e2e suite hardening, docs improvements, and dependency bumps.

### Fixed

- fix(http): remove insert-mode duplicate rendering — context accumulation is now the single renderer ([`46b4d1f`](https://github.com/no-js-dev/nojs/commit/46b4d1f), [#275](https://github.com/no-js-dev/nojs/issues/275), PR [#308](https://github.com/no-js-dev/nojs/pull/308))
- fix(http): set IntersectionObserver root to nearest scrollable ancestor on all three pagination/visible observers ([`9764c49`](https://github.com/no-js-dev/nojs/commit/9764c49), [`94cf475`](https://github.com/no-js-dev/nojs/commit/94cf475), [#302](https://github.com/no-js-dev/nojs/issues/302), PR [#308](https://github.com/no-js-dev/nojs/pull/308))
- fix(i18n): load locale bundle when `NoJS.i18n({loadPath})` is called after auto-init ([`a7b2a5c`](https://github.com/no-js-dev/nojs/commit/a7b2a5c), [#213](https://github.com/no-js-dev/nojs/issues/213), PR [#304](https://github.com/no-js-dev/nojs/pull/304))
- fix(http): handle deferred scroll compensation for prepend with animate-leave via internal deferred-render slot signal ([`da7ab65`](https://github.com/no-js-dev/nojs/commit/da7ab65), [`241601a`](https://github.com/no-js-dev/nojs/commit/241601a), [`6c5fa75`](https://github.com/no-js-dev/nojs/commit/6c5fa75), [#316](https://github.com/no-js-dev/nojs/issues/316), PRs [#323](https://github.com/no-js-dev/nojs/pull/323)/[#327](https://github.com/no-js-dev/nojs/pull/327))
- fix(loops): strip all side-effect directives from loop clones ([`27c85a1`](https://github.com/no-js-dev/nojs/commit/27c85a1), PR [#293](https://github.com/no-js-dev/nojs/pull/293))
- fix(sse): add sse to `_LOOP_ATTRS` to prevent EventSource-per-clone in loops ([`98cd415`](https://github.com/no-js-dev/nojs/commit/98cd415), PR [#293](https://github.com/no-js-dev/nojs/pull/293))
- fix(sse): harden sse directive — loop guard, limit validation, terminal untrack, dead code removal ([`a31b58b`](https://github.com/no-js-dev/nojs/commit/a31b58b), PR [#293](https://github.com/no-js-dev/nojs/pull/293))
- fix(dom): block `noembed`, `noframes`, `plaintext` in sanitizer ([`75685b5`](https://github.com/no-js-dev/nojs/commit/75685b5), PR [#295](https://github.com/no-js-dev/nojs/pull/295))
- fix(router): reject protocol-relative URLs in `_isSafeRedirect` ([`992ffad`](https://github.com/no-js-dev/nojs/commit/992ffad), PR [#295](https://github.com/no-js-dev/nojs/pull/295))

### Changed

- refactor(http): remove dead `_isFirstFetch` assignment in accumulation branch ([`6c2e52d`](https://github.com/no-js-dev/nojs/commit/6c2e52d))
- test(e2e): harden flaky waits, eliminate `waitForTimeout`, add deterministic waits and exact-count assertions, Elements skip-guard ([`9ccc898`](https://github.com/no-js-dev/nojs/commit/9ccc898), [`50658de`](https://github.com/no-js-dev/nojs/commit/50658de), [#277](https://github.com/no-js-dev/nojs/issues/277) [#278](https://github.com/no-js-dev/nojs/issues/278) [#274](https://github.com/no-js-dev/nojs/issues/274) [#276](https://github.com/no-js-dev/nojs/issues/276), PRs [#303](https://github.com/no-js-dev/nojs/pull/303)/[#309](https://github.com/no-js-dev/nojs/pull/309))
- test(loops): cover template + animate-stagger path ([`7f24cc3`](https://github.com/no-js-dev/nojs/commit/7f24cc3), [#270](https://github.com/no-js-dev/nojs/issues/270), PR [#306](https://github.com/no-js-dev/nojs/pull/306))
- test(suite): overhaul unit test suite after full audit ([`719e5a4`](https://github.com/no-js-dev/nojs/commit/719e5a4), PR [#283](https://github.com/no-js-dev/nojs/pull/283))

### Docs

- docs(loops): add Common Mistakes callout for container-vs-item directive placement (md + llms + website tpl ×5 locales) ([`166d397`](https://github.com/no-js-dev/nojs/commit/166d397), [`9704e65`](https://github.com/no-js-dev/nojs/commit/9704e65), [#269](https://github.com/no-js-dev/nojs/issues/269) [#318](https://github.com/no-js-dev/nojs/issues/318), PRs [#305](https://github.com/no-js-dev/nojs/pull/305)/[#307](https://github.com/no-js-dev/nojs/pull/307))
- docs(site): deduplicate locale-list in docs shell ([`b2b6586`](https://github.com/no-js-dev/nojs/commit/b2b6586), [#214](https://github.com/no-js-dev/nojs/issues/214))
- docs(adr): commit ADR-002 as a repo file ([`6430c08`](https://github.com/no-js-dev/nojs/commit/6430c08), [#319](https://github.com/no-js-dev/nojs/issues/319))
- docs(sse): sweep SSE docs, fix callout typography, correct sse-limit contract ([`a79a082`](https://github.com/no-js-dev/nojs/commit/a79a082), PR [#296](https://github.com/no-js-dev/nojs/pull/296))
- docs(i18n): correct stale claims in home and FAQ locales ([`4f80de7`](https://github.com/no-js-dev/nojs/commit/4f80de7))
- docs(site): add Google Analytics gtag to docs entry pages ([`0abe55c`](https://github.com/no-js-dev/nojs/commit/0abe55c), PR [#315](https://github.com/no-js-dev/nojs/pull/315))
- chore(test-server): support `NOJS_ELEMENTS_PATH` env var override ([`11fcbc2`](https://github.com/no-js-dev/nojs/commit/11fcbc2), [#276](https://github.com/no-js-dev/nojs/issues/276) [#317](https://github.com/no-js-dev/nojs/issues/317))
- chore(dev-server): honor `NOJS_ELEMENTS_PATH` env override ([`b868729`](https://github.com/no-js-dev/nojs/commit/b868729))
- chore(security): add 2026-07 core security audit report ([`d47c237`](https://github.com/no-js-dev/nojs/commit/d47c237), PR [#294](https://github.com/no-js-dev/nojs/pull/294))

### Chore

- chore(deps-dev): bump `@testing-library/jest-dom` from 6.9.1 to 7.0.0 ([`9ae46f7`](https://github.com/no-js-dev/nojs/commit/9ae46f7))
- chore(deps-dev): bump `jsdom` from 28.1.0 to 30.0.0 ([`cbaf4ce`](https://github.com/no-js-dev/nojs/commit/cbaf4ce))
- chore(deps-dev): bump `eslint` from 10.6.0 to 10.8.0 ([`7f84aa6`](https://github.com/no-js-dev/nojs/commit/7f84aa6))
- chore(deps-dev): bump `@playwright/test` from 1.61.1 to 1.62.0 ([`e817f0a`](https://github.com/no-js-dev/nojs/commit/e817f0a))
- chore(deps): bump `actions/setup-node` from 6 to 7 ([`44219ad`](https://github.com/no-js-dev/nojs/commit/44219ad))
- chore(deps-dev): bump `@commitlint/cli` from 21.2.0 to 21.2.1 ([`0d83f82`](https://github.com/no-js-dev/nojs/commit/0d83f82))

## [1.20.0](https://github.com/no-js-dev/nojs/compare/v1.19.0...v1.20.0) — 2026-07-17

Declarative Server-Sent Events. The new `sse` directive opens a persistent EventSource connection and binds incoming messages to the reactive context — the streaming counterpart to `get`. Shares the `as`/`into`/`error`/`then` companion API with the HTTP directives and adds SSE-specific companions for named events, insert modes, array capping, and credentials.

### Added

- feat(sse): `sse` directive for declarative Server-Sent Events via the native `EventSource` API — opens a persistent connection to a server endpoint, parses incoming messages as JSON (raw-string fallback), and binds them to the element's reactive context
  - `sse="url"` with `{expr}` interpolation — URL changes close the old connection and open a new one reactively
  - `as` companion names the context variable (default `"data"`)
  - `sse-event` listens for a named SSE event instead of the default `message` event
  - `sse-insert="replace|append|prepend"` — `replace` (default) overwrites the variable, `append`/`prepend` accumulate in an array
  - `sse-limit` caps array length in append/prepend mode, preventing unbounded memory growth on long-lived streams
  - `sse-credentials` opens the EventSource with `withCredentials: true` for cross-origin cookie sending
  - `into` dual-writes event data to a global store
  - `error` renders a template on terminal connection close only (not during browser auto-reconnect)
  - `then` evaluates an expression per message (`$event` = parsed data)
  - `$sse` reactive status object — `connecting`, `open`, `error` booleans reflecting the EventSource lifecycle
  - Per-origin connection warning at the 6th concurrent EventSource to the same origin
- test(sse): unit test suite with MockEventSource covering all insert modes, named events, credentials, store dual-write, error templates, URL interpolation, and connection lifecycle
- test(e2e): Playwright specs for SSE directive — streaming endpoints, connection state, insert modes, limits, named events, credentials, and error handling
- docs(sse): markdown documentation, HTML docs page with cheatsheet, i18n locales (en/pt/es/fr/it), and LLM docs (llms.txt + llms-full.txt)

### Fixed

- fix(sse): register disposal once during init, add debug logging, validate `sse-limit` is a positive integer (0 and empty now warn as invalid)
- fix(e2e): rewrite insert-modes specs to JSON data-mode contract
- fix(e2e): deflake pagination spec 4 with request-settled sync
- fix(e2e): move loop+animation attrs from ul container to li element in stagger fixture

## [1.19.0](https://github.com/no-js-dev/nojs/compare/v1.18.0...v1.19.0) — 2026-07-09

Reactive-core performance overhaul. A backward-compatible pass across expression evaluation, list reconciliation, disposal, and memory — no API changes, all directives behave identically. Largest wins on list-heavy and interaction workloads.

### Performance

- perf(evaluate): compile expressions to reusable closure trees instead of re-parsing on every evaluation — removes per-tick parse/scan overhead in hot binding paths
- perf(loops): per-template process plans for loop clones — each `foreach`/`each` template is analyzed once and its clones follow a precomputed plan rather than re-resolving directives per item
- perf(directives): skip-unchanged reconciliation — reconciler diffs against previous values and skips DOM writes when a bound value is unchanged
- perf(reactive): keyed LIS (longest-increasing-subsequence) reordering — keyed lists move the minimum number of DOM nodes on reorder, with batched, key-scoped change notifications
- perf(disposal): subtree-discard fast path — skips element-only disposers and bulk-clears DOM on teardown of large subtrees
- perf(memory): shared Proxy handler and interned property keys reduce per-instance allocation and steady-state memory

Representative js-framework-benchmark deltas vs v1.18.0: swap-rows ~15.9× faster, select-row ~23.4× faster, create-10k ~1.9× faster, run-memory ~1.7× lower.

### Added

- test(bench): A/B measurement harness — benchmark runner plus fixed loop benchmarks to guard against performance regressions

### Fixed

- fix(router): handle View Transition `ready` rejection on skipped animations
- fix(events): revert document-level event delegation to per-element listeners — delegation inverted handler ordering, silently dropped handlers when any ancestor called `stopPropagation()`, and installed document-level listeners with no disposal
- fix(evaluate): statement write-back notifies aliased parent objects — mutating a loop item in place (`item.done = !item.done`) now wakes watchers keyed on the parent list
- fix(evaluate): statement root-key scanners consolidated into one memoized scan; `$event`/`$el`/`$error` are treated as per-invocation locals instead of forcing conservative re-notification
- fix(loops): process plans rebuild when a directive is registered after first render, so later clones match it
- fix(loops): external-template masters are refreshed once per update pass — in-place template content changes (remote template loads) now reach new clones
- fix(registry): plan replay snapshots planned nodes up front — an init that mutates the tree mid-run (e.g. `bind-html`) can no longer apply planned directives to freshly created nodes
- fix(registry): directive match cache evicts oldest-first at capacity instead of clearing wholesale
- fix(binding): `bind-*`, `class-*`, `style-*` value memos verify the live DOM before skipping — externally flipped state (user click on `bind-checked`, script-modified classes/styles/attributes) is resynced
- fix(context): `$notify(arg)` narrows to key-scoped listeners only for string arguments; non-string calls fire every listener

## [1.18.0](https://github.com/no-js-dev/nojs/compare/v1.17.0...v1.18.0) — 2026-07-08

Directive Incompatibility Remediation — resolves 18 compatibility findings discovered during the NOJS-244 audit, adds directive gating semantics, and documents all known directive interactions.

### Fixed

- fix(router): normalize trailing slashes — `/about/` and `/about` now resolve to the same route, preventing duplicate route matches and inconsistent URL state
- fix(directives): ancestor watcher race — watchers on ancestor elements now correctly observe state changes made by child directives during the same processing pass (finding 1)
- fix(directives): `bind`/`model`/`event` + `if` gate — `bind`, `model`, and `on:*` directives now skip initialization while the element's `if` condition is falsy, preventing flicker and stale bindings on gated elements (finding 2, 15)
- fix(directives): state clobber on `use` — `state` directive no longer overwrites context values that were already set by a `use` template stamp (finding 3)
- fix(directives): HTTP auto-fire on loop clones — `get`/`post`/`put`/`patch`/`delete`/`query` directives on loop elements now warn and are stripped from clones to prevent N duplicate requests (finding 4)
- fix(directives): `else-if` chain sniff — `else-if` no longer matches an `if` across a non-conditional sibling boundary (finding 6)
- fix(directives): `call` context restore — `call` directive restores the caller's context after invoking the target, preventing context leakage (finding 7)
- fix(directives): `else` ownership — `else` binds to its immediately preceding `if`/`else-if` sibling only; orphan `else` elements are now warned and ignored (finding 8)
- fix(directives): `t-*` parameter sniff — `t-name`/`t-count` no longer leak interpolation params across sibling `t` elements (finding 11)
- fix(directives): HTTP verb gates — HTTP verb directives on elements with a falsy `if` condition are not fired (finding 16)
- fix(directives): `computed`/`watch` template guards — `computed` and `watch` directives inside `<template>` elements are deferred until the template is instantiated (finding 17, 18)
- fix(directives): `i18n-ns` + `if` handoff — `i18n-ns` namespace loading completes before `if`-gated `t` elements attempt translation (finding 21)
- fix(directives): `use` priority — `use` directive priority changed from 10 to 9 so template stamping always completes before `if` snapshots the element (finding 22)
- fix(directives): `page-*` gates — `page-title`, `page-description`, `page-canonical`, `page-jsonld` skip evaluation while `if` is falsy (finding 23)
- fix(directives): `computed`/`watch` gates — `computed` and `watch` skip initialization while `if` is falsy (finding 24)
- fix(directives): `page-*` loop strip — `page-*` directives are stripped from loop clones to prevent duplicate head tags (finding 25)
- fix(directives): `else-if` loop guard — `else-if` on a loop element is warned and treated as a plain conditional, not a loop empty-state (finding 26)
- fix(http): JSON-LD regex — `page-jsonld` content extraction regex now correctly handles nested braces (finding B1)

### Changed

- `if` now gates the entire element — directives with `gated: true` skip initialization while the element's `if` condition is falsy, rather than only hiding children
- `use` directive priority changed from 10 to 9 — `use` always stamps template content before `if` captures the element snapshot
- Per-clone `computed`/`watch` is now the defined semantics — each loop clone gets its own reactive derivation rather than sharing one with the template
- `each`+`if`+`else` on the same element is now treated as a conditional else (not a loop empty-state)
- HTTP verbs on loop elements now warn and are stripped from clones

### Removed

- CJS and ESM build targets — NoJS is CDN-only (IIFE). `dist/cjs/` and `dist/esm/` directories, `main`/`module`/`exports` fields in `package.json` removed

### Added

- Directive compatibility documentation page (`docs/md/directive-compatibility.md`) with interaction matrix, 8 documented limitations with workarounds, and migration guide for v1.18.0 behavioral changes

### Migration

- **If-gate semantics**: elements with `if` + other directives now gate those directives. This is backward-compatible for well-formed usage but may affect edge cases where directives relied on initializing while `if` was falsy.
- **`use` priority 9**: `use` always stamps before `if` snapshots. Both changes are backward-compatible for well-formed usage but may affect edge cases relying on old ordering.

## [1.17.0](https://github.com/no-js-dev/nojs/compare/v1.16.1...v1.17.0) — 2026-07-06

### Added

- feat(http): `query` directive — issues an HTTP **QUERY** request ([RFC 10008](https://www.rfc-editor.org/rfc/rfc10008.html)), a safe, idempotent, cacheable read that carries a request body. Behaves like `get` for auto-firing, reactive re-fetch, rendering (`as`/`loading`/`error`/`empty`), and caching, but sends a body like `post` (via `body=` or, on a `<form>`, serialized fields).
- feat(http): body-aware caching for `query` — the cache key incorporates the request body, so two QUERY requests to the same URL with different bodies are cached separately while identical URL + body hit the cache. GET cache keys are unchanged.
- feat(http): `query-trigger`, `query-trigger-label`, and `query-threshold` companions — lazy-load triggers (`visible`/`scroll`/`hover`/`button`/`none`) for non-form `query` elements, mirroring the `get-trigger*` family.
- feat(http): QUERY is treated as a safe method — no CSRF token is injected on QUERY requests. Pagination (`get-cursor`/`get-page`/`get-insert`) remains GET-only.

## [1.16.1](https://github.com/no-js-dev/nojs/compare/v1.16.0...v1.16.1) — 2026-07-03

Security release resolving five findings from internal audit run-1 (no CVEs assigned).

### Security

- **[F1 · High]** Fixed a `javascript:`/`vbscript:` sanitizer bypass where interior ASCII control characters (tab/LF/CR) split the scheme token in `bind-html`/`t-html`/`bind-href`/`bind-src` and SVG data-URI paths.
- **[F3 · Medium]** Closed an evaluator sandbox escape that reached the real `Function` constructor via `Object` reflection methods.
- **[F4 · Medium]** Closed an evaluator escape to a cross-realm `Window` via an iframe's `$refs.…contentWindow`.
- **[F2 · Low]** Stopped the CSRF token from being attached to cross-origin requests expressed as `//`, `\\`, or uppercase-scheme URLs.
- **[F5 · Low]** Validated and encoded the i18n `locale` before building the `loadPath` fetch URL (path-traversal / translation-poisoning).

### Fixed

- fix(i18n): guard `supportedLocales` in `NoJS.i18n()` — a non-array value is now ignored instead of throwing during first-visit browser-language detection, and the detection use-site defends against a non-array config
- fix(i18n): browser-language detection no longer throws when `navigator` exists without a `language` property (`navigator.language` falsiness now falls back to `"en"`)

### Changed

- chore(docs): the docs language selector `aria-label` is now localized via `bind-aria-label="$i18n.t('shell.header.selectLanguage')"` on both the desktop toggle and the mobile pill row; added the `header.selectLanguage` key to all five shell locales (en/pt/es/fr/it)

### Tests

- test(i18n): added coverage for `supportedLocales` omitted (defaults to `[]`) and non-array `supportedLocales` under `detectBrowser`; fixed a jsdom `navigator.language` teardown leak between describes
- test(i18n): added an `@axe-core/playwright` accessibility check to the language-selector e2e spec and restructured the `lang-selector.html` fixture to wrap its scenario in a `<section>` (per the fixture rule)

## [1.16.0](https://github.com/no-js-dev/nojs/compare/v1.15.6...v1.16.0) — 2026-07-01

### Added

- feat(i18n): `supportedLocales` config option — enables first-visit browser-language detection when using `loadPath` (lazy locales) ([a070b41](https://github.com/no-js-dev/nojs/commit/a070b41))
- feat(docs): restored language selector on the docs site — NoJS-Elements dropdown with short locale codes, dark theme, and a mobile pill row; Tools/"More" menu converted to the Elements dropdown ([8d907d7](https://github.com/no-js-dev/nojs/commit/8d907d7), [f840562](https://github.com/no-js-dev/nojs/commit/f840562))
- test(i18n): e2e coverage for first-visit browser-language detection ([89a72b6](https://github.com/no-js-dev/nojs/commit/89a72b6))

### Fixed

- fix(i18n): `detectBrowser` was a no-op under `loadPath` — the browser language is now adopted on first visit when present in `supportedLocales`; priority order is unchanged (persisted `nojs-locale` > detection > `defaultLocale`) ([0cfe25e](https://github.com/no-js-dev/nojs/commit/0cfe25e))

### Changed

- docs(home): update hero stat copy to the directives count ([b840e95](https://github.com/no-js-dev/nojs/commit/b840e95))

## [1.15.6](https://github.com/no-js-dev/nojs/compare/v1.15.5...v1.15.6) — 2026-06-25

### Fixed

- fix(router): strip trailing slashes from route paths — prevents 404 on page refresh when servers append trailing slash
- fix(docs): improve mobile responsive layout — prevent content overflow, constrain code blocks and tables
- fix(docs): refactor mobile sidebar toggle into glass-style pill button
- fix(docs): add overscroll-behavior containment to mobile sidebar menu
- fix(docs): fix mobile sidebar menu not expanding (reactive context scoping)
- fix(docs): disable hash navigation in docs site

## [1.15.5](https://github.com/no-js-dev/nojs/compare/v1.15.4...v1.15.5) — 2026-06-24

### Added

- feat(docs): JS syntax highlight directive for code blocks on the docs site
- feat(docs): full docs site overhaul — new home page, asset cleanup, SPA architecture with route-view

### Fixed

- fix(http): add independent reactive params watcher so URL parameter changes trigger re-fetch
- fix(http): defer initial `doRequest` to microtask so computed values resolve before first fetch
- fix(http): add `$store`/`$route`/`$i18n` subscriptions to `get` directive URL watcher
- fix(router): split space-separated classes in `route-active` toggle
- fix(docs): prevent `_hl()` placeholder collision with number regex
- fix(docs): sync `404.html` with `index.html` highlight directive and hero guard
- fix(docs): re-create hero editor on same-route re-navigation
- fix(docs): fix `audit-i18n` crash, add `--error`/`--success` CSS tokens, rename landing to home

### Changed

- chore(docs): ecosystem sync — remove CLI and npm install references, CDN-only distribution
- chore(docs): update CLAUDE.md to reflect SPA architecture with route-view

## [1.15.4](https://github.com/no-js-dev/nojs/compare/v1.15.3...v1.15.4) — 2026-06-22

### Fixed

- fix(docs): remove npm install references from README and docs — NoJS Core is CDN-only
- fix(docs): remove old `@erickxavier/no-js` namespace from getting-started template
- fix(docs): add NoJS-CLI to ecosystem table, remove hardcoded v1.13.0
- fix(docs): remove surviving npmjs.com links from llms-full.txt
- fix(docs): update stale LSP counts in llms.txt (45+ directives, 41 snippets)

## [1.15.3](https://github.com/no-js-dev/nojs/compare/v1.15.2...v1.15.3) — 2026-06-20

### Fixed

- fix(test): make fromNow filter test timezone-safe — mock Date.now() to avoid UTC day boundary flakiness in CI

## [1.15.2](https://github.com/no-js-dev/nojs/compare/v1.15.1...v1.15.2) — 2026-06-20

### Fixed

- fix(build): ensure all dist files contain correct version on release

## [1.15.1](https://github.com/no-js-dev/nojs/compare/v1.15.0...v1.15.1) — 2026-06-20

### Fixed

- chore(docs): fix README.md badges and miscellaneous documentation files

## [1.15.0](https://github.com/no-js-dev/nojs/compare/v1.14.1...v1.15.0) — 2026-06-19

### Added

- `feat(i18n): $i18n.[path] reactive translation proxy — access translations as dot-notation properties in any expression context`
- Unit tests for the loop `else="templateId"` pattern, the conditionals guard, and null/undefined early-return behavior ([73b36b3](https://github.com/no-js-dev/nojs/commit/73b36b3))
- E2E tests for loop else patterns across `foreach`/`each`/`for` variants ([b841446](https://github.com/no-js-dev/nojs/commit/b841446))
- QA regression tests for the else-template hardening fixes ([f67ad0a](https://github.com/no-js-dev/nojs/commit/f67ad0a))

### Changed

- Docs: documented the inline `else="templateId"` pattern on loop elements across markdown docs and HTML templates with i18n keys for all 5 locales ([d25c1c8](https://github.com/no-js-dev/nojs/commit/d25c1c8), [d7d7334](https://github.com/no-js-dev/nojs/commit/d7d7334))
- Docs: purged all stale sibling-else teaching from markdown docs, `cheatsheet.tpl`, `llms-full.txt`, and all 5 locales; documented the new empty-or-null/undefined else-template semantics ([e8dd849](https://github.com/no-js-dev/nojs/commit/e8dd849))

### Fixed

- Conditionals: guard `else` directive from firing on loop elements that use `else="templateId"` for empty-state rendering — the conditional handler now skips elements with `foreach`, `each`, or `for` attributes ([b24b6e9](https://github.com/no-js-dev/nojs/commit/b24b6e9))
- Loops: same-ref fast path no longer swallows updates while the else template is showing ([f67ad0a](https://github.com/no-js-dev/nojs/commit/f67ad0a))
- Loops: a missing else-template ID now clears stale clones and warns once instead of failing silently ([f67ad0a](https://github.com/no-js-dev/nojs/commit/f67ad0a))
- Loops: the else template is no longer re-cloned on every update while the list stays empty — preserves input state inside the empty-state template ([f67ad0a](https://github.com/no-js-dev/nojs/commit/f67ad0a))
- Conditionals: `<label for="..." else>` is correctly treated as a conditional else, not a loop element — loop detection now uses the shared `_isLoopElement` predicate ([f67ad0a](https://github.com/no-js-dev/nojs/commit/f67ad0a))
- Conditionals: one-time warning for an orphan `else` with no preceding `if`/`else-if` ([f67ad0a](https://github.com/no-js-dev/nojs/commit/f67ad0a))

### Breaking Changes

- **Sibling else for loops removed**: the `<p else>No items</p>` pattern (placing an element with `else` after a loop element) is no longer supported. Use `else="templateId"` on the loop element itself to reference a `<template>` for the empty state ([4e20448](https://github.com/no-js-dev/nojs/commit/4e20448))
- **Loops: null/undefined/non-array lists now render the else template** — previously they rendered nothing; non-array values are now normalized to the empty path so the `else="templateId"` template shows ([f67ad0a](https://github.com/no-js-dev/nojs/commit/f67ad0a))

## [1.14.1](https://github.com/no-js-dev/nojs/compare/v1.14.0...v1.14.1) — 2026-06-11

### Fixed

- `style--*` CSS custom properties now use `setProperty()` instead of bracket assignment
- `style-map` handler now uses `setProperty()`/`removeProperty()` for CSS custom properties (same fix as `style--*` applied to the map code path)
- Replaced stale "v1.15" version references with "Unreleased" in conditionals, loops, and llms-full docs
- `.backspace` key modifier now correctly filters keydown events
- Fixed `on:keydown.ctrl.s` example → `on:keydown.ctrl.enter` (letter-key modifiers unsupported)
- Fixed `on:error` handler variable from `$event` to `$error`
- Added "(via NoJS-Elements)" qualifiers to validate/DnD features in README
- Added Elements CDN script to flagship form example
- Corrected error-boundary scope, head cleanup behavior, event bus wording
- Removed phantom `.html` extension fallback claims from router docs
- Added multi-select `model` caveat (not yet implemented)
- Documented pagination directives (`get-trigger`, `get-insert`, `get-page`, etc.)
- Documented `dangerouslyDisableSanitize`, `i18n.persist`, `maxEventListeners`, `appId` config options
- Regenerated `llms-full.txt` from current docs, fixed `llms.txt` signatures

## [1.14.0](https://github.com/no-js-dev/nojs/compare/v1.13.3...v1.14.0) — 2026-06-09

### Added

- **Pagination & Triggers** — extend the `get` directive with declarative pagination and fetch trigger control ([NOJS-98](https://github.com/no-js-dev/nojs/issues/98)):
  - `get-insert` attribute: `append` or `prepend` insertion modes with sentinel-based positioning ([83580f4](https://github.com/no-js-dev/nojs/commit/83580f4))
  - `get-trigger` attribute: `visible` (IntersectionObserver), `hover` (mouseenter), `none` (manual), `scroll` (infinite scroll), `button` (load more) ([a229263](https://github.com/no-js-dev/nojs/commit/a229263), [e44ee5c](https://github.com/no-js-dev/nojs/commit/e44ee5c))
  - `get-page` attribute: offset-based pagination with auto-increment ([e44ee5c](https://github.com/no-js-dev/nojs/commit/e44ee5c))
  - `get-cursor` and `get-cursor-field` attributes: cursor-based pagination with auto-detection from response headers or JSON body ([540e5af](https://github.com/no-js-dev/nojs/commit/540e5af))
  - `get-threshold` attribute: configurable IntersectionObserver rootMargin ([a229263](https://github.com/no-js-dev/nojs/commit/a229263))
  - `get-trigger-label` attribute: custom text for auto-generated load-more button ([e44ee5c](https://github.com/no-js-dev/nojs/commit/e44ee5c))
  - End-of-data detection via empty response, `X-NoJS-Last-Page` header, or null cursor
  - Scroll position preservation for prepend mode
  - Mutual exclusivity guards: cursor vs page, scroll/button vs refresh
- Pagination & Triggers documentation page with 7 live interactive demos
- i18n translations (PT, ES, FR, IT) for pagination documentation
- E2E tests: 17 Playwright tests covering pagination, triggers, and insert modes
- Unit tests: 60 tests for pagination directives covering all modes, security, and disposal

### Fixed

- `_resolveField()` security: added `_FORBIDDEN_PROPS` guard against prototype pollution ([b32fb35](https://github.com/no-js-dev/nojs/commit/b32fb35))
- `_resetPagination()` unconditionally recreates scroll observer — fixes stale observer after reset ([b32fb35](https://github.com/no-js-dev/nojs/commit/b32fb35))
- Sentinel generation counter (`_sentinelGen`) prevents disposer accumulation across resets ([b32fb35](https://github.com/no-js-dev/nojs/commit/b32fb35))
- First-fetch sentinel registers `_onDispose` — fixes leak on element removal before pagination ([b32fb35](https://github.com/no-js-dev/nojs/commit/b32fb35))
- `_removeInlineError()` helper added for proper error wrapper cleanup ([b32fb35](https://github.com/no-js-dev/nojs/commit/b32fb35))
- `fetch.js`: expose response headers via `meta` before `_REPLACE` early return ([b32fb35](https://github.com/no-js-dev/nojs/commit/b32fb35))

## [1.13.3](https://github.com/no-js-dev/nojs/compare/v1.13.1...v1.13.3) — 2026-06-05

### Fixed

- Router: notify context watchers on hash-only navigation.
- Sandbox: intercept `document.location` in `_safeDocument` proxy.
- Conditionals: count consecutive backslashes in `_splitTopLevel` escape detection.
- Error boundary: add re-entrancy guard to `showFallback` preventing infinite loops.
- E2E: reconcile tools-dropdown fixtures with current i18n and HTML output.
- Playground: load NoJS-Elements IIFE in preview iframe for drag-list support.

### Changed

- Reintegrated core hardening (engine, directives, subsystems) from security review onto current main.

## [1.13.2](https://github.com/no-js-dev/nojs/compare/v1.13.1...v1.13.2) — 2026-06-02

### Fixed

- Security and robustness hardening across the framework — 17 of 18 critical and high-severity findings from the 2026-05-29 deep code review remediated. Affected subsystems:
  - **Expression evaluator** (`evaluate.js`) — tightened identifier resolution and statement write-back against the allow-list contract.
  - **Fetch / HTTP** (`fetch.js`, `directives/http.js`) — hardened request/response handling, interceptor credential redaction, and caching.
  - **Context** (`context.js`) — reactive proxy and scope-chain edge cases.
  - **Router** (`router.js`) — navigation, nested-outlet, and view-transition guards.
  - **DOM and loops** (`dom.js`, `directives/loops.js`) — disposal-before-clear ordering and keyed-loop invariants.
  - **Conditionals** (`directives/conditionals.js`) — `switch` disposal guard and `else-if` deduplication.
  - **Index / public API** (`index.js`) — proxy-global sanitization, awaited `dispose()`, and config lock.
  - **Animations** (`animations.js`) — idempotent, cancellable timers.
  - Related modules: `globals.js`, `filters.js`, `i18n.js`, `devtools.js`, and the `binding`, `styling`, `state`, `events`, `refs`, and `head` directives.
- The prior DnD/validation extraction to `@no-js-dev/nojs-elements` remains intact — no behavior reverted.

## [1.13.1](https://github.com/no-js-dev/nojs/compare/v1.13.0...v1.13.1) — 2026-06-01

### Changed

- Ecosystem version-sync release — no functional changes to Core. Bumped to stay lockstep with the rest of the NoJS ecosystem, companion to the NoJS-LSP plugin-metadata polish.

## [1.13.0](https://github.com/no-js-dev/nojs/compare/v1.12.0...v1.13.0) — 2026-05-27

### Changed

- **BREAKING:** DnD directives (`drag`, `drop`, `drag-list`, `drag-multiple`) replaced with deprecation stubs — moved to `@no-js-dev/nojs-elements`
- **BREAKING:** `validate` directive replaced with deprecation stub — moved to `@no-js-dev/nojs-elements`
- Extracted `error-boundary` to standalone file `src/directives/error-boundary.js` (stays in core)

### Added

- `NoJS.internals` frozen getter exposing semi-private APIs for plugin access: `execStatement`, `cloneTemplate`, `disposeChildren`, `warn`, `validators`, `removeCoreDirective`, `onDispose`
- `_removeCoreDirective(name)` in registry — allows plugins to replace core stub directives
- Deprecation stubs emit clear migration warnings pointing to `@no-js-dev/nojs-elements`
- Stub and internals unit tests

### Removed

- `src/directives/dnd.js` (1,162 lines) — replaced by `dnd-stub.js`
- `src/directives/validation.js` (552 lines) — replaced by `validate-stub.js`
- DnD and validation unit tests (migrated to Elements)
- DnD and validation E2E tests (migrated to Elements)

## [1.12.0](https://github.com/no-js-dev/nojs/compare/v1.11.1...v1.12.0) — 2026-05-21

### Added

- Route-index auto-load for nested outlets — empty `[route-view][route-index]` outlets automatically load their default template after navigation ([`dea03c2`](https://github.com/no-js-dev/nojs/commit/dea03c2))
- View Transition API integration for smooth route transitions with presets (`slide`, `fade`, `scale`, `none`) ([`a4db4db`](https://github.com/no-js-dev/nojs/commit/a4db4db))
- Nested routing with hierarchical segment resolution and layout-aware prefetch ([`aae67d5`](https://github.com/no-js-dev/nojs/commit/aae67d5), [`4f9546c`](https://github.com/no-js-dev/nojs/commit/4f9546c))
- `skeleton` attribute on `get` directive for CLS prevention ([`9121824`](https://github.com/no-js-dev/nojs/commit/9121824))
- Route head attributes: `page-title`, `page-description`, `page-canonical`, `page-jsonld` on `<template route>` ([`9cd8605`](https://github.com/no-js-dev/nojs/commit/9cd8605))
- `suppressHashWarning` router option ([`46bb48b`](https://github.com/no-js-dev/nojs/commit/46bb48b))
- Right-side table of contents with scroll spy on doc pages ([`92544b9`](https://github.com/no-js-dev/nojs/commit/92544b9))

### Changed

- Docs layout migrated to nested routing architecture ([`e4b7a4a`](https://github.com/no-js-dev/nojs/commit/e4b7a4a))
- View transitions switched from slide to fade ([`ee25c80`](https://github.com/no-js-dev/nojs/commit/ee25c80))
- All doc templates synced with markdown sources — expanded sections for state management, data binding, events, error handling, filters, animations, and more ([`2fd1c89`](https://github.com/no-js-dev/nojs/commit/2fd1c89))

### Removed

- SSG documentation page and all related i18n keys — SSG was never a framework feature ([`2fd1c89`](https://github.com/no-js-dev/nojs/commit/2fd1c89))
- GitHub Discussions links (replaced with Discord) ([`2fd1c89`](https://github.com/no-js-dev/nojs/commit/2fd1c89))
- 25 dead i18n keys cleaned from all 5 locales ([`2fd1c89`](https://github.com/no-js-dev/nojs/commit/2fd1c89))

### Fixed

- 22 `t-html` attribute misuses across cheatsheet, drag-and-drop, events, i18n, and routing templates ([`2fd1c89`](https://github.com/no-js-dev/nojs/commit/2fd1c89))
- Hash-only navigation skip re-render and unique view-transition-names ([`19ef85d`](https://github.com/no-js-dev/nojs/commit/19ef85d))
- Nested outlets skip in prefetch to prevent 404s ([`3870937`](https://github.com/no-js-dev/nojs/commit/3870937))
- Relative `src` path resolution in nested outlets ([`45e9771`](https://github.com/no-js-dev/nojs/commit/45e9771))
- Horizontal scrollbar during view transitions ([`5751c55`](https://github.com/no-js-dev/nojs/commit/5751c55))
- Missing hero titles on 5 doc pages ([`1e34526`](https://github.com/no-js-dev/nojs/commit/1e34526))
- Styling page broken t-html and heading hierarchy ([`1675a18`](https://github.com/no-js-dev/nojs/commit/1675a18))
- Full docs accuracy audit: removed bundle size claims (dynamic value), fixed 14 incorrect animation names, replaced 7 phantom filters with real ones in reference table, corrected `first`/`last`/`count` filter descriptions, removed fabricated `trigger-bubbles`/`trigger-once` attributes, fixed `NoJS.on('error')` callback signature, replaced `el.__nojs_dispose` with `el.__disposers`, removed nonexistent `route:change` event and `RTL support` claims, updated README version badge and npm package name, removed 9 broken `ssg.md` links, updated test count from 800+ to 1,350+ and filter count from 30+ to 32 across all 5 locales

## [1.11.1](https://github.com/no-js-dev/nojs/compare/v1.11.0...v1.11.1) — 2026-05-20

### Changed

- Unified loop directives in docs — all code examples and live demos now use `foreach` as primary syntax
- Deprecated NoJS-MCP server — removed all references from docs, tests, and agent configs

### Fixed

- Rebuilt dist bundles with v1.11.1 version string

## [1.11.0](https://github.com/no-js-dev/nojs/compare/v1.10.1...v1.11.0) — 2026-03-26

### Added

- Plugin system with lifecycle hooks, interceptors, reactive globals, and security hardening ([`b0c46bc`](https://github.com/no-js-dev/nojs/commit/b0c46bc))
- Head management directives: `page-title`, `page-description`, `page-canonical`, `page-jsonld` ([`8cd58c2`](https://github.com/no-js-dev/nojs/commit/8cd58c2))
- `focusBehavior` option for accessible SPA navigation with configurable focus strategy ([`a940f18`](https://github.com/no-js-dev/nojs/commit/a940f18))
- SSG and pre-rendering guide ([`9fd7bf0`](https://github.com/no-js-dev/nojs/commit/9fd7bf0))
- `bind-html` now emits a `console.warn` in `debug` or `devtools` mode when given a non-literal (dynamic) expression ([`460b883`](https://github.com/no-js-dev/nojs/commit/460b883))
- GitHub Actions CI workflow and npm publishing automation ([`762c739`](https://github.com/no-js-dev/nojs/commit/762c739))

### Fixed

- Replace hardcoded `|| 2000` / `|| 1000` animation fallback timeouts with `|| 0` in `_animateOut`, `_animateIn`, and the `each` / `foreach` animate-leave branches ([`a77136c`](https://github.com/no-js-dev/nojs/commit/a77136c))
- Add missing fallback timeout to `_animateIn` animName branch ([`a420735`](https://github.com/no-js-dev/nojs/commit/a420735))
- Update default template extension to `.tpl` for route rendering ([`69d4fd6`](https://github.com/no-js-dev/nojs/commit/69d4fd6))
- Remove duplicate declarations in `index.js` from plugin system merge ([`2e4834b`](https://github.com/no-js-dev/nojs/commit/2e4834b))

## [1.10.1](https://github.com/no-js-dev/nojs/compare/v1.10.0...v1.10.1) — 2026-03-23

### Security

- Add `set` traps to `document` and `navigator` proxies preventing sandbox escape via property assignment ([`d763d2f`](https://github.com/no-js-dev/nojs/commit/d763d2f))
- Block `navigator.sendBeacon` and add targeted `window` set trap to prevent data exfiltration ([`0faf54a`](https://github.com/no-js-dev/nojs/commit/0faf54a))
- Sanitize flag enforcement, event bus limits, SVG DOMParser hardening, template integrity checks, persist schema validation ([`e0c72ec`](https://github.com/no-js-dev/nojs/commit/e0c72ec))
- Proxy sandbox hardening, expression cache LRU eviction, `nl2br` filter sanitization, devtools redaction ([`6c2d68a`](https://github.com/no-js-dev/nojs/commit/6c2d68a))

### Fixed

- Security hardening across expression evaluator, fetch proxy, DOM binding, and state persistence ([`1f44849`](https://github.com/no-js-dev/nojs/commit/1f44849))
- Documentation accuracy and playground bug fixes ([`1f44849`](https://github.com/no-js-dev/nojs/commit/1f44849))

## [1.10.0](https://github.com/no-js-dev/nojs/compare/v1.9.1...v1.10.0) — 2026-03-23

### Added

- Key-based reconciliation in `each` and `foreach` directives for efficient list diffing ([#19](https://github.com/no-js-dev/nojs/pull/19))
- `persist-fields` attribute to limit which state properties get persisted to storage ([#10](https://github.com/no-js-dev/nojs/pull/10))
- `llms.txt`, `llms-full.txt`, `sitemap.xml`, and inline LLM metadata for AI discoverability
- OG and Twitter Card metadata with thumbnail image

### Fixed

- Replace `globalThis` deny-list with explicit browser globals allow-list in expression evaluator ([#18](https://github.com/no-js-dev/nojs/pull/18))
- Replace regex HTML sanitizer with `DOMParser` structural sanitization in `bind-html` ([#17](https://github.com/no-js-dev/nojs/pull/17))
- Warn when sensitive headers (Authorization, Cookie) are set inline ([#16](https://github.com/no-js-dev/nojs/pull/16))
- Clear outlet and warn when route guard fails without a redirect ([#14](https://github.com/no-js-dev/nojs/pull/14))
- Warn loudly when `sanitize` is explicitly disabled on `bind-html` ([#15](https://github.com/no-js-dev/nojs/pull/15))
- Reduce MutationObserver cost with `subtree:false`; register ResizeObserver via `_onDispose` ([#12](https://github.com/no-js-dev/nojs/pull/12))
- Stop polling and observers when element disconnects from DOM ([#11](https://github.com/no-js-dev/nojs/pull/11))
- Restrict `window.__NOJS_DEVTOOLS__` to localhost only ([#9](https://github.com/no-js-dev/nojs/pull/9))
- Sanitize `javascript:` URLs and encode interpolated `href` values ([#8](https://github.com/no-js-dev/nojs/pull/8))

### Changed

- Cap expression caches and fix `_collectKeys` cache mutation leak ([#13](https://github.com/no-js-dev/nojs/pull/13))
- Fix documentation accuracy across `llms-full.txt` and `llms.txt`
- Add NoJS LSP link to site navigation

## [1.9.1](https://github.com/no-js-dev/nojs/compare/v1.9.0...v1.9.1) — 2026-03-18

### Fixed

- Fix `foreach` directive infinite recursion: strip 15 directive attributes from inline template clone before `processTree` re-entry ([#5](https://github.com/no-js-dev/nojs/issues/5))
- Add `_warn()` call in `evaluate()` catch block for visible error reporting ([#5](https://github.com/no-js-dev/nojs/issues/5))
- Make `_deepMerge`, `_i18nCache`, `_loadedNs`, `_loadLocale` module-private in i18n.js ([#5](https://github.com/no-js-dev/nojs/issues/5))
- Fix docs Example 1 (Login): replace verbose `success` template with `then` + `redirect`
- Fix docs Example 3 (Live Search): replace unsupported `function()` syntax with arrow function
- Fix docs Example 5 (Live Polling): correct `poll` → `refresh` attribute across template and 5 locales

### Added

- 11 new unit tests covering `foreach` inline-template and `evaluate` error-reporting fixes
- Add NoJS LSP link (`https://lsp.no-js.dev/`) to desktop header nav, mobile nav, and footer

## [1.9.0](https://github.com/no-js-dev/nojs/compare/v1.8.2...v1.9.0) — 2026-03-17

### Added

- Custom recursive-descent expression parser — replaces all `new Function()` calls for full CSP compliance
- Statement interpreter with assignment, compound operators (`+=`, `-=`, `*=`, `/=`, `%=`), prefix/postfix `++`/`--`
- Deny-list (`_DENY_GLOBALS`) blocks `eval`, `Function`, `process`, `require`, `importScripts` from globalThis fallback
- Forbidden property checks on `__proto__`, `constructor`, `prototype` in object expressions and member access
- Arrow function rest parameters support in expression evaluator
- 24 new unit tests for statement interpreter

### Changed

- Expression evaluation no longer uses `new Function()` — zero `unsafe-eval` CSP requirement
- `csp` config option deprecated (no longer needed — framework is CSP-compliant by default)
- Documentation updated across 5 locales (en, es, pt, fr, it) reflecting CSP-by-default
- Landing page hero fills viewport height; responsive fixes for 768px and new 480px breakpoint

### Removed

- `csp` option from `NoJS.config()` (shows deprecation warning if used)

## [1.8.2](https://github.com/no-js-dev/nojs/compare/v1.8.1...v1.8.2) — 2026-03-17

### Fixed

- Fix memory leaks across 10 directive files: dispose child contexts before `innerHTML = ""` ([#4](https://github.com/no-js-dev/nojs/issues/4))
- Fix `_watchExpr` in `globals.js`: capture `$watch` unsubscribe and register via `_onDispose` so ancestor-context watchers are cleaned on element disposal ([#4](https://github.com/no-js-dev/nojs/issues/4))
- Fix `on:*` and `trigger` event listeners leaking on re-render: register `removeEventListener` via `_onDispose` ([#4](https://github.com/no-js-dev/nojs/issues/4))
- Fix `bind-*`, `model`, `call`, `drag`, `drag-list` listener/watcher leaks via `_onDispose` cleanup ([#4](https://github.com/no-js-dev/nojs/issues/4))

### Added

- `_disposeChildren(parent)` utility in `registry.js` for safe child disposal before `innerHTML` clear
- 27 new unit tests for disposal behavior across `registry`, `core`, `directives-core`, and `directives-data`

### Changed

- Remove 86 disposable unit tests (duplicates, no-assertion, trivially obvious) identified by QA audit
- Remove landing page E2E tests (docs site tests moved out of scope)
- Remove NPM/ESM install references from documentation (CDN-only distribution)

## [1.8.1](https://github.com/no-js-dev/nojs/compare/v1.8.0...v1.8.1) — 2026-03-17

### Changed

- Redesign landing page v8 with new hero, feature grid, and community sections
- Update all 5 locales (en/es/pt/fr/it) with new landing page translation keys
- Change language switcher from `<a>` to `<button>` for accessibility
- Update README with npm install instructions, `notify()`, drag & drop, and wildcard routes
- Update agent definitions with codebase-first workflow step

### Added

- CONTRIBUTING.md with contribution guidelines
- GitHub issue template for bug reports (`bug_report.yml`)
- Firefox and WebKit browsers to Playwright E2E config
- Landing page E2E tests

## [1.8.0](https://github.com/no-js-dev/nojs/compare/v1.7.0...v1.8.0) — 2026-03-16

### Added

- `NoJS.notify()` public API to flush store watchers after external JS mutations to `NoJS.store` ([`5317c83`](https://github.com/no-js-dev/nojs/commit/5317c83))
- Documentation for `NoJS.notify()` with interceptor and cart examples ([`5317c83`](https://github.com/no-js-dev/nojs/commit/5317c83))
- `route="*"` wildcard catch-all route support with built-in 404 fallback ([`1fe73b0`](https://github.com/no-js-dev/nojs/commit/1fe73b0))
- `$route.matched` boolean for matched/unmatched route detection ([`1fe73b0`](https://github.com/no-js-dev/nojs/commit/1fe73b0))
- Guard and redirect attributes on wildcard route templates ([`1fe73b0`](https://github.com/no-js-dev/nojs/commit/1fe73b0))
- Graceful handling of failed template loads with `__loadFailed` flag ([`1fe73b0`](https://github.com/no-js-dev/nojs/commit/1fe73b0))
- Call directive: loading template support with element disable during request ([`1f5517e`](https://github.com/no-js-dev/nojs/commit/1f5517e))
- Call directive: AbortController switchMap — abort previous in-flight on re-click ([`1f5517e`](https://github.com/no-js-dev/nojs/commit/1f5517e))
- Call directive: custom headers attribute support ([`1f5517e`](https://github.com/no-js-dev/nojs/commit/1f5517e))
- Call directive: redirect attribute for SPA navigation on success ([`1f5517e`](https://github.com/no-js-dev/nojs/commit/1f5517e))
- Call directive: `fetch:success` / `fetch:error` events and devtools bridge integration ([`1f5517e`](https://github.com/no-js-dev/nojs/commit/1f5517e))
- Call directive: default `as` to `"data"` when not specified ([`1f5517e`](https://github.com/no-js-dev/nojs/commit/1f5517e))
- Call directive: error body included in error template context ([`1f5517e`](https://github.com/no-js-dev/nojs/commit/1f5517e))
- GitHub Copilot agent definitions and project instructions ([`37d5136`](https://github.com/no-js-dev/nojs/commit/37d5136))

### Changed

- Router: migrate from `mode:"history"/"hash"` to `useHash` boolean API (default `false`) ([`c102df3`](https://github.com/no-js-dev/nojs/commit/c102df3))
- Router: backward-compat shim for `mode:"hash"` → `useHash:true` with deprecation warning ([`c102df3`](https://github.com/no-js-dev/nojs/commit/c102df3))
- Router: fix base stripping with anchored regex via `_stripBase()` helper ([`c102df3`](https://github.com/no-js-dev/nojs/commit/c102df3))
- Router: fix anchor link scroll in history mode (now intercepts in both modes) ([`c102df3`](https://github.com/no-js-dev/nojs/commit/c102df3))
- Router: add popstate same-path guard to prevent re-render on hash-only changes ([`c102df3`](https://github.com/no-js-dev/nojs/commit/c102df3))
- Router: skip wildcard routes during prefetch ([`1fe73b0`](https://github.com/no-js-dev/nojs/commit/1fe73b0))

### Fixed

- E2E form validation tests (8, 13, 14, 16) now interact with fields before asserting errors, matching pristine-aware validation ([`5317c83`](https://github.com/no-js-dev/nojs/commit/5317c83))

## [1.7.0](https://github.com/no-js-dev/nojs/compare/v1.6.1...v1.7.0) — 2026-03-13

### Added

- Complete form validation revamp with declarative HTML-first API ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- Built-in validators: `required`, `email`, `url`, `min`, `max`, `custom` ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- Per-rule error messages via `error-{rule}` attributes (e.g. `error-required="Field is required"`) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `$form.errors` object with per-field error messages ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `$form.firstError` — first error message in DOM order ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `$form.errorCount` — count of invalid fields ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `$form.fields` — per-field state (`valid`, `dirty`, `touched`, `error`, `value`) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `$form.values` — reactive form values object ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `$form.pending` — async validator support ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `$form.reset()` — reset form state (dirty, touched, errors) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `error-class` attribute for automatic CSS class toggling on invalid fields ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `validate-on` attribute to control validation trigger (`input`, `blur`, `submit`) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `validate-if` attribute for conditional field validation ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- Error template references (`error-required="#tpl"`) for custom error rendering ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- Pristine-aware validation: errors only display for fields the user has interacted with ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- Submit automatically marks all fields as touched (revealing all errors) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- E2E test suite for form validation (Playwright) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- Updated docs with interactive Registration Form demo ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- Updated all locale files (en, es, fr, it, pt) with new form demo content ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))

### Removed

- `cpf`, `cnpj`, `phone`, `creditcard` validators (region-specific, use `custom` instead) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `between` validator (use `min` + `max` instead) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))
- `match` validator (use `custom` instead) ([`cad7be4`](https://github.com/no-js-dev/nojs/commit/cad7be4))

## [1.6.1](https://github.com/no-js-dev/nojs/compare/v1.6.0...v1.6.1) — 2026-03-13

### Changed

- Migrate CDN URL from `unpkg.com` to `cdn.no-js.dev` across all files
- Update README, docs site, playground engine, and getting-started templates
- Update FAQ answers across all locales (en, es, fr, it, pt)
- Update dev-server and test-server CDN rewrite patterns
- Update design file references

## [1.6.0](https://github.com/no-js-dev/nojs/compare/v1.5.2...v1.6.0) — 2026-03-12

### Added

- DevTools Protocol module (`src/devtools.js`) with zero-overhead event emission
- Context lifecycle tracking: `ctx:created`, `ctx:updated`, `ctx:disposed` events
- Element inspection API: `inspect(selector)`, `inspectTree(selector)`
- Store inspection: `inspectStore(name)`, `get:stats`, `get:routes` commands
- Runtime mutation: `mutate(id, key, value)`, `mutateStore(name, key, value)`
- Visual highlight overlay for element selection from DevTools
- Custom event bus: `nojs:devtools` (emit), `nojs:devtools:cmd` (commands)
- Batch lifecycle events: `batch:start`, `batch:end`
- Fetch lifecycle events: `fetch:success`, `fetch:error`
- Route navigation events: `route:navigate`
- Store creation events: `store:created`
- Directive init events: `directive:init`
- Context registry with automatic disposal cleanup
- `window.__NOJS_DEVTOOLS__` public API with inspect/mutate/highlight/on
- 428-line DevTools test suite (937 tests total across 14 suites)

## [1.5.2](https://github.com/no-js-dev/nojs/compare/v1.5.1...v1.5.2) — 2026-03-11

### Removed

- Vanilla JS FAQ close-animation handler — `<details>/<summary>` now handles open/close natively ([`6ef3251`](https://github.com/no-js-dev/nojs/commit/6ef3251))
- `.faq-closing` CSS and `faq-slide-out` keyframes (no longer needed) ([`6ef3251`](https://github.com/no-js-dev/nojs/commit/6ef3251))

## [1.5.1](https://github.com/no-js-dev/nojs/compare/v1.5.0...v1.5.1) — 2026-03-11

### Added

- Floating scroll-to-top button on mobile
- Doc tables horizontal scroll on mobile

### Changed

- Mobile nav popover alignment now uses header height CSS variable
- Language selector uses CSS grid 3-column layout
- GitHub/Discord links displayed side-by-side in mobile popover
- Code panel scrollability improvements (`overflow: auto`)
- CSS globalization: sidebar/doc-layout CSS moved from per-page files to global `style.css`
- CSS-to-template migration: moved per-page CSS from external files into template `<style>` blocks

### Removed

- "Get Started" CTA from mobile popover (low contrast issue)

### Fixed

- Mobile nav popover alignment

## [1.5.0](https://github.com/no-js-dev/nojs/compare/v1.4.3...v1.5.0) — 2026-03-11

### Added

- Discord community link in header nav icon, footer, FAQ sidebar, and FAQ CTA buttons
- `discord` translation key across all 5 locales (en, pt, es, fr, it) in `shell.json` and `faq.json`
- Community section in README with Discord and GitHub Discussions links
- Logo in README header with vertical alignment
- Changelog link in README Contributing section
- `CHANGELOG.md` with full version history (1.0.0–1.4.3)
- Dark logo assets: `logo-dark.svg`, `logo-dark.png`, `logo-dark-round.png`
- CHANGELOG update step in release agent workflow

### Changed

- Redesign footer as horizontal nav mirroring header menu (Features, Examples, FAQ, Playground, Docs, GitHub, Discord)
- Discord icon hover uses brand color (#5865F2)

## [1.4.3](https://github.com/no-js-dev/nojs/compare/v1.4.2...v1.4.3) — 2026-03-10

### Fixed

- Make `init()` idempotent — prevent double router creation when `cdn.js` auto-init and user scripts both call `NoJS.init()` ([`c71d290`](https://github.com/no-js-dev/nojs/commit/c71d290))

## [1.4.2](https://github.com/no-js-dev/nojs/compare/v1.4.0...v1.4.2) — 2026-03-10

### Changed

- Rewrite FAQ content across all 5 locales (en, pt, es, fr, it) with improved tone and accuracy ([`8b1c997`](https://github.com/no-js-dev/nojs/commit/8b1c997))

### Fixed

- Fix i18n locale persistence — saved locale from `localStorage` was being ignored on reload ([`8b1c997`](https://github.com/no-js-dev/nojs/commit/8b1c997))

## [1.4.0](https://github.com/no-js-dev/nojs/compare/v1.3.1...v1.4.0) — 2026-03-10

### Added

- Route prefetching and subtemplate cache warming ([`a71b63a`](https://github.com/no-js-dev/nojs/commit/a71b63a))
- FAQ page with full i18n support ([`a71b63a`](https://github.com/no-js-dev/nojs/commit/a71b63a))
- CSS split into modular files ([`a71b63a`](https://github.com/no-js-dev/nojs/commit/a71b63a))

### Changed

- Hide FAQ navigation links during initial setup ([`3cee1ee`](https://github.com/no-js-dev/nojs/commit/3cee1ee))

## [1.3.1](https://github.com/no-js-dev/nojs/compare/v1.3.0...v1.3.1) — 2026-03-09

### Added

- Locale keys for drag-and-drop docs, examples, and playground UI ([`5765bd8`](https://github.com/no-js-dev/nojs/commit/5765bd8))

## [1.3.0](https://github.com/no-js-dev/nojs/compare/v1.2.1...v1.3.0) — 2026-03-09

### Added

- Complete i18n coverage for all doc templates, playground demos, and locale JSON files ([`d762f00`](https://github.com/no-js-dev/nojs/commit/d762f00))
- `t=` directives on all doc templates with synced locale keys ([`49ce64c`](https://github.com/no-js-dev/nojs/commit/49ce64c))

## [1.2.1](https://github.com/no-js-dev/nojs/compare/v1.2.0...v1.2.1) — 2026-03-05

### Fixed

- GitHub link in `build.js` ([`62a1d0f`](https://github.com/no-js-dev/nojs/commit/62a1d0f))

## [1.2.0](https://github.com/no-js-dev/nojs/compare/v1.1.0...v1.2.0) — 2026-03-05

### Added

- Drag-and-drop system with `drag`, `drop`, `drag-disabled`, `drop-max` directives ([`0bc4490`](https://github.com/no-js-dev/nojs/commit/0bc4490))
- Interactive playground with live code editing ([`0bc4490`](https://github.com/no-js-dev/nojs/commit/0bc4490))
- Comprehensive codebase audit and documentation overhaul ([`0bc4490`](https://github.com/no-js-dev/nojs/commit/0bc4490))

## [1.1.0](https://github.com/no-js-dev/nojs/compare/v1.0.2...v1.1.0) — 2026-03-03

### Added

- File-based routing with `route-view` and `src` attributes ([`bb61fe7`](https://github.com/no-js-dev/nojs/commit/bb61fe7))
- External i18n locale loading from JSON files ([`bb61fe7`](https://github.com/no-js-dev/nojs/commit/bb61fe7))
- `t-html` directive for HTML content translation ([`bb61fe7`](https://github.com/no-js-dev/nojs/commit/bb61fe7))
- GitHub Actions workflow for automated npm publishing ([`270a4d0`](https://github.com/no-js-dev/nojs/commit/270a4d0))

### Fixed

- Memory leak cleanup in reactive watchers ([`4358b75`](https://github.com/no-js-dev/nojs/commit/4358b75))

## [1.0.2](https://github.com/no-js-dev/nojs/compare/v1.0.1...v1.0.2) — 2026-02-28

### Added

- End-to-end test suite with Playwright ([`12fec61`](https://github.com/no-js-dev/nojs/commit/12fec61))

### Fixed

- Reactive directive binding issues ([`12fec61`](https://github.com/no-js-dev/nojs/commit/12fec61))

## [1.0.1](https://github.com/no-js-dev/nojs/compare/v1.0.0...v1.0.1) — 2026-02-27

### Added

- Anchor links support in hash-mode documentation ([`db4b69d`](https://github.com/no-js-dev/nojs/commit/db4b69d))
- Custom domain (CNAME) configuration ([`a79c780`](https://github.com/no-js-dev/nojs/commit/a79c780))

## [1.0.0](https://github.com/no-js-dev/nojs/releases/tag/v1.0.0) — 2026-02-27

### Added

- Core reactive engine with `state`, `bind`, `show`, `hide`, `if`, `else`, `switch`, `case` directives
- Event handling with `on:` prefix
- `each` directive for list rendering
- `model` directive for two-way data binding
- CSS class bindings with `class-*` and `style-*` directives
- `fetch` directive for declarative data fetching
- Client-side router with hash and history modes
- i18n system with `t` directive
- Animation system with `animate` and `transition` directives
- Scoped context system with parent inheritance
- Filter system with `|` pipe syntax
- Zero dependencies
