# Changelog

All notable changes to the Nex framework will be documented in this file.

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

## [Nex 0.4.3] - 2026-06-06

### Added
- **NexBase full Supabase/postgrest-js API parity**: The `NexBase` query builder is now feature-complete with Supabase JavaScript SDK `postgrest-js`. Every public method from `PostgrestFilterBuilder`, `PostgrestTransformBuilder`, `PostgrestQueryBuilder`, and `PostgrestClient` has an Elixir equivalent, including identical semantics and chain behavior.
  - **Negated and extended filters**: `nlike/3` (`NOT LIKE`), `nilike/3` (`NOT ILIKE`), `not_in_list/3` (`NOT IN`), `is_null/2`, `is_not_null/2`, `like_all_of/3`, `like_any_of/3`, `ilike_all_of/3`, `ilike_any_of/3` — matching the `.likeAllOf`/`.likeAnyOf`/`.ilikeAllOf`/`.ilikeAnyOf` family.
  - **Fluent filters and builders**: `filter/4` (generic), `not_filter/4` (Supabase `.not`), `or_filter/3` (Supabase `.or`), `match/2`, `contains/3`, `contained_in/3` (`.containedBy`), `overlaps/3`, and PostgreSQL range operators `range_lt/3`, `range_gt/3`, `range_lte/3`, `range_gte/3`, `range_adjacent/3`.
  - **Full-text search with opts form**: `text_search/4` accepts `config:` and `type:` (`:plain`, `:phrase`, `:websearch`) options; `fts/4`, `plfts/4`, `phfts/4`, `wfts/4` accept either an options keyword list or a config string (backward-compatible). Uses PostgreSQL `to_tsvector`/`to_tsquery` with config support; falls back to `LIKE` on SQLite.
  - **Transform builder**: `select/2` accepts column-alias string syntax (`"display_name:name, profile(*)"`); chained `.select()` after a mutation enables SQL `RETURNING`. `select/3` accepts `head:` and `count:` options matching Supabase's two-argument form.
  - **Order / limit / range / or with `referencedTable`**: `order/4`, `limit/3`, `offset/3`, `range/4`, and `or_filter/3` all accept a `:referenced_table` (alias `:foreign_table`) option matching Supabase's `{ foreignTable, referencedTable }` parameter.
  - **single() / maybeSingle()**: `single/1` and `maybe_single/1` both set `LIMIT 1` and unwrap the result list at execution time — `.single()` raises/errors on 0 or >1 rows, `.maybeSingle()` returns `nil` for 0 rows. Behavior matches Supabase exactly.
  - **EXPLAIN, CSV, GeoJSON, rollback**: `explain/2` supports Supabase's full option set (`:analyze`, `:verbose`, `:settings`, `:buffers`, `:wal`, `format: :text | :json`). `csv/1` returns query results as a CSV string. `geojson/1` returns rows as a GeoJSON FeatureCollection. `rollback/1` wraps execution in a transaction that is rolled back (Supabase `tx=rollback`).
  - **Schema / maxAffected / throwOnError**: `schema/2` sets a query's schema or, when piped from a `Conn`, returns a schema-scoped builder function. `max_affected/2` caps rows affected by UPDATE/DELETE (PostgREST 13+). `throw_on_error/1` causes `run/1` to raise instead of returning `{:error, _}`.
  - **CRUD with full options**: `insert/3` and `upsert/4` support `:count`, `:default_to_null`; `upsert/4` additionally supports `:on_conflict` and `:ignore_duplicates`. `update/3` and `delete/2` support `:count`.
  - **RETURNING support for all mutations on all adapters**: Chaining `.select()` after any mutation enables `RETURNING` both on PostgreSQL (via Ecto) and on SQLite (via native raw-SQL `INSERT/UPDATE/DELETE ... RETURNING` with PRAGMA-based column introspection as a workaround for the exqlite driver).
- **NexBase count modifier**: Added `count/2` supporting `:exact`, `:planned`, `:estimated` modes; `run/1` returns `{:ok, data, count}` triple when count is set.
- **NexBase execution helpers**: Added `run!/1`, `one!/1`, `maybe_one/1`, `stream/2`, and `transaction/2` for the Supabase `.single()`, `.maybeSingle()`, and transaction equivalents.
- **`Nex.HTMX` helpers**: Added pipeline-friendly functions (`push_url/2`, `trigger/2`, `retarget/2`, etc.) to easily attach HTMX headers to HEEx templates and `%Nex.Response{}` structs. This allows seamless Out-of-Band updates and client-side event triggering.
- **Handler submodules**: Added internal `Nex.Handler.*` modules to isolate lifecycle, dispatch, page, API, streaming, WebSocket, and error responsibilities behind the existing `Nex.Handler.handle/1` entry point.
- **Datastar todo spike**: Extended the Datastar example with a store-backed todo flow that returns `patch_elements` and `patch_signals` events from a page action-style API endpoint.

### Changed
- **Removed standalone `nex_env` package**: `Nex.Env` was duplicated between `framework/lib/nex/env.ex` and the separate `nex_env/` Mix project. The duplicate package is deleted; all generated apps now get `Nex.Env` from `nex_core` (which they already depend on), and `{:nex_env, ...}` is no longer added to project deps.
- **Handler organization**: Refactored the request pipeline into smaller delegated modules without changing public routing or response behavior.
- **AI onboarding for generated apps**: `mix nex.new` scaffolds now ship with a bootstrap `AGENTS.md` plus a canonical project-local skill at `.agents/skills/nex-project/`.
- **Route matching precedence**: Optional catch-all routes (`[[...param]]`) now sort behind static, dynamic, and required catch-all routes so sibling dynamic routes keep winning for concrete paths.
- **Single-segment page actions**: Short action paths now resolve against the current page module when a referer page is available, instead of falling back to homepage actions with the same name.
- **Convention error pages**: `src/pages/404.ex` and `src/pages/500.ex` now render through the shared `_app.ex` / `_document.ex` shell when present.
- **Request contract docs**: Public docs, templates, and examples now consistently document page actions as receiving `Nex.Req`, with `req.query` / `req.body` access and `nil` bodies for requests without payloads.

### Fixed
- **NexBase `single()` / `maybeSingle()` for mutations**: `.insert() |> .select() |> .single()` (and similarly for update/delete/upsert) now correctly returns a single unwrapped row instead of the `%{count:, data:}` wrapper, matching Supabase behavior.
- **NexBase `.rollback()` now returns actual query data**: Previously the rolled-back transaction discarded the real result and always returned `{:ok, []}`. Results are now preserved and returned correctly.
- **NexBase `plfts` / `phfts` semantics swap**: The plainto_tsquery and phraseto_tsquery operators were reversed across the codebase (docstrings, `text_search` type mapping, SQL generation, and operator aliases). Now corrected: `plfts` = plainto_tsquery, `phfts` = phraseto_tsquery, matching Supabase and PostgREST exactly.
- **NexBase SQL injection — FTS config parameter**: Full-text search `config` values were interpolated as raw SQL string literals. Now parameterized via `$N::regconfig` placeholders.
- **NexBase SQL injection — planned count `table` literal**: `count(:planned)` built its pg_class query with the table name as an unescaped string literal. Now uses a parameterized placeholder.
- **NexBase SQL injection — unquoted identifiers**: Table names, schema names, and column names in the raw SELECT path, `WHERE` filters, and `ORDER BY` clauses were interpolated without quoting. All identifiers are now double-quoted with proper `"` escaping, matching the SQLite mutation path.
- **NexBase SQL injection — `rpc` function and argument names**: `rpc/3` built its function call with unquoted function and argument names. Both are now double-quoted.
- **NexBase `rpc` with `head: true` generated empty SQL**: The `head` / `get` path set `sql_str = ""` which produced a SQL syntax error; removed the dead GET-vs-head branch. Now the function always runs a real query and honors the head flag only at the result-processing layer.
- **NexBase `or_filters` / `not_filters` ignored on Postgres UPDATE/DELETE**: Previously only the `filters` list was applied via Ecto, silently allowing mass updates/deletes when only `or_filter/3` or `not_filter/4` were used. All three filter groups are now applied correctly.
- **NexBase unsupported filters SILENTLY PASSED on Ecto UPDATE/DELETE**: The `apply_filter/3` catch-all returned the query unchanged, meaning e.g. `from("t") |> overlaps(:c, v) |> delete()` would delete every row. Now raises with a clear error listing supported operators.
- **NexBase `upsert` default `on_conflict: :id`**: Previously always used `:id` as conflict target, breaking any table with a non-`id` primary key. Now only includes `conflict_target` in `insert_all` options when explicitly provided by the caller.
- **NexBase `max_affected(n)` on PostgreSQL UPDATE/DELETE**: Ecto's `update_all`/`delete_all` do not honor `LIMIT` for PostgreSQL. Now uses a `ctid IN (SELECT ctid FROM ... LIMIT n)` subquery for Postgres, matching the row-capping behavior already working for SQLite.
- **NexBase `order(:col, :Desc)` produced ASC**: Direction comparison was case-sensitive. Now normalizes via `String.downcase/1`, accepting `:desc`, `:Desc`, `:DESC`, `:descending`, `:dsc`, and `:asc` variants.
- **NexBase `run!` for SELECT bypassed `.rollback()` and `.throwOnError()`**: `run!` was calling `run_select` directly instead of going through the `run/1` pipeline. Now consistently routes all queries through `run/1`.
- **NexBase `one!` / `maybe_one` executed mutations**: When called on an insert/update/delete query, the mutation executed *before* being re-run as a SELECT. Now converts to `type: :select` upfront.
- **NexBase SQLite multi-row insert with `default_to_null: false`**: Used only the first row's column keys, dropping columns present only in later rows and inserting NULLs where DB defaults should have applied. Now uses the union of all column keys across all rows, matching Supabase.
- **NexBase `or_filter([])` generated invalid SQL**: Empty filter groups produced `AND ()` which is a syntax error. Empty groups are now ignored entirely.
- **NexBase `select(head: true, count: :exact)` discarded count**: The head branch short-circuited with `{:ok, []}` before the count was computed. Now attaches the count, matching Supabase's head+count contract.
- **NexBase `range(from, to)` with `from > to` produced negative `LIMIT`**: Now clamps both `from` and `limit` to non-negative values.
- **NexBase `order` with explicit `nulls_first: false`**: Did not emit `NULLS LAST`; now respects explicit `nulls_first: false` as well as `nulls_last: true`.
- **API handler FunctionClauseError heuristic was too broad**: A `FunctionClauseError` raised inside a handler (e.g., from a pattern-match failure in `def get(%Req{body: %{"required" => v}})` when the key is missing) was incorrectly surfaced as "API signature mismatch" with a misleading log. Now always re-raises the original error.
- **`Nex.Helpers.time_ago/1` silently returned `""` for `%Date{}`**: The `@spec` accepted `Date.t()` but no clause matched. Added a `%Date{}` clause that converts to UTC midnight before delegating to the DateTime path.
- **`Nex.Helpers.format_number/1` emitted scientific notation for large numbers**: `1_000_000_000` produced `"1.0e3M"`. Now uses `:io_lib.format("~.1f", ...)` for stable decimal rendering, and adds a `B` tier for billions.
- **Validator test name contradicted assertion**: `"passes when required field is empty string"` asserted an error. Renamed to `"fails when required field is empty string"` to match behavior.
- **API handler error response**: Fixed crash in `Nex.Handler.Api` when a handler returns a non-struct value, caused by unsafe `other.__struct__` access. Now safely uses `Map.get/3`.
- **Validator min/max dispatch**: Fixed `Nex.Validator` `:min`/`:max` rules which were using broken pattern matching order (`is_number` before `is_integer`), making string length validation completely non-functional. Now dispatches based on the value's type (number vs binary) instead of the parameter's type.
- **1-arity custom validators**: Fixed `Nex.Validator` custom validators with arity 1 being called with 2 arguments, which caused `FunctionClauseError`. Now properly checks function arity before calling.
- **`time_ago/1` pluralization**: Fixed grammatical error in `Nex.Helpers.time_ago/1` where singular units were incorrectly pluralized (e.g., "1 minutes ago" instead of "1 minute ago").
- **`class/2` space collapsing**: Fixed `Nex.Helpers.class/2` which had a useless `String.replace(" ", " ")` no-op. Now correctly collapses multiple spaces into a single space.
- **`attrs/1` XSS vulnerability**: Fixed `Nex.Helpers.attrs/1` which did not HTML-escape attribute values, allowing XSS attacks via malicious attribute values containing `"` or other special characters. Values are now properly escaped.
- **WebSocket initial state**: WebSocket handlers now receive merged query string parameters in the initial request context, aligning `initial_state/1` with Nex request semantics for query access.
- **Convention error page lookup**: Convention-based error pages now resolve correctly even when `:app_module` is configured as a module atom.
- **Handler HTTP method atom exhaustion DoS**: `conn.method` was converted via `String.to_atom/1`, allowing an attacker to send arbitrary HTTP methods and exhaust the Erlang atom table. Now uses `String.to_existing_atom/1` against a whitelist of known methods, falling back to `:get`.
- **`Nex.Store.get/2` returned expired data**: TTL was only enforced during periodic GenServer cleanup (every 5 min). Expired entries are now checked and pruned on every read.
- **`Nex.Store` crashed when GenServer not running**: All ETS operations now call `ensure_table()` first, and the GenServer's `init/1` is idempotent when the table already exists.
- **`Nex.Env.get_integer/2` crashed on malformed values**: `String.to_integer/1` raised for non-integer env values (e.g. `"abc"`, `"1.5"`). Now returns the configured default, consistent with `get_boolean/2`.
- **Rate limiter bypassable via forged `X-Forwarded-For`**: The header was trusted unconditionally. Now defaults to ignoring `X-Forwarded-For` and only honors it when `Application.put_env(:nex_core, :rate_limit, trust_x_forwarded_for: true)` is explicitly configured.
- **SSE stream conn not threaded through chunks**: Each `Plug.Conn.chunk/2` call reused the original conn from the closure, discarding adapter state updates. The conn is now threaded via the process dictionary so subsequent chunks see the updated struct.
- **Chunked page action response hardcoded HTTP 200**: `is_function(body, 1)` branch ignored `response.status`. Now uses the actual status from `%Nex.Response{}`.
- **NexBase FTS text_search matched config string instead of query text**: PostgreSQL full-text search params were `[config, config, query_text]` for a 2-placeholder query, so `$2` bound to the config name and `query_text` was never bound. Now `[config, query_text]` as intended.
- **NexBase unsupported OR filters silently passed on Ecto UPDATE/DELETE**: `filter_to_dynamic` catch-all returned `dynamic([t], true)`, disabling the filter entirely. Now raises an explicit error.
- **NexBase `count(:planned)` bound wrong params**: The `pg_class` query used `$1` for the table name but params were `filter_params ++ [table]`. Now uses just `[table]`.
- **NexBase SQLite mutation raw SQL used unquoted identifiers**: Table and column names in `sqlite_insert_with_returning`, `sqlite_update_with_returning`, `sqlite_delete_with_returning`, `sqlite_upsert_with_returning`, and `sqlite_table_columns` were interpolated as `"#{name}"` instead of using the safe `quote_ident/1` helper. Now consistently quoted.
- **NexBase Postgres `postgres_update_with_limit` set clause unquoted**: Column names in the `SET` clause used raw `~s("#{k}")`. Now uses `quote_ident/1`.
- **NexBase CSV output did not quote `\r`**: Only `,`, `"`, and `\n` triggered quoting. A carriage return in a cell value could break parsers by spanning rows. `\r` now also triggers quoting.
- **NexBase `limit/3` and `offset/3` accepted arbitrary values**: No type guard allowed SQL injection via string values (e.g. `"5; DROP TABLE..."`). Now guarded with `is_integer(count) and count >= 0`.
- **Installer silently ignored unknown CLI options**: `mix nex.new foo --statrer saas` quietly used the default starter. Now uses `strict:` mode and raises with a list of unknown options.
- **Installer crashed when a regular file existed at the target path**: `File.dir?/1` returned false for files, so `File.mkdir_p!` later raised an opaque error. Now uses `File.exists?/1` and raises a clear "Path already exists" message.
- **Installer `cd` instructions ignored `--path`**: Success and error messages always printed `cd #{name}` regardless of `--path /some/where`. Now resolves the correct relative or absolute path.

### Removed
- **God-module pressure**: Removed the single-file concentration of handler responsibilities by moving private implementation details out of `Nex.Handler`.

## [Nex 0.4.2] - 2026-03-27

### Added
- **Installer (`mix nex.new`)**: Added a `--starter saas` mode that generates a database-backed SaaS starter with NexBase, Nex.Env, session auth, protected dashboard routes, and starter project CRUD.

### Fixed
- **Generated SaaS starter**: Added the SQLite adapter dependency required for the default `DATABASE_URL`, aligned generated page actions with the `%Nex.Req{}` contract, and removed generated HEEx/template issues that caused warnings or runtime startup failures.

### Changed
- **Starter guidance**: Clarified across the CLI, README, installer docs, and website docs that `basic` remains the default starter and `saas` is the explicit product-oriented path.

## [Nex 0.4.1] - 2026-03-14

### Fixed
- **Installer (`mix nex.new`)**: Fixed archive compilation in Hex install paths by removing compile-time filesystem dependency on `VERSION` and resolving generator dependency version from application metadata.

### Changed
- **Release synchronization**: Updated all published Nex packages (`nex_core`, `nex_new`, `nex_env`, `nex_base`) to version `0.4.1` to keep monorepo package versions aligned.

## [Nex 0.4.0] - 2026-03-11

### Added
- **`Nex.Validator`**: Params validation for Nex applications. Supports common validators: `:required`, `:string`, `:number`, `:boolean`, `:email`, `:url`, `:min`, `:max`, `:format`, `:in`. Also supports custom validators as functions. Returns `{:ok, params}` or `{:error, errors}`.
- **`Nex.Upload`**: File upload handling. Files from multipart forms are available in `req.body` as `%Plug.Upload{}` structs. Provides `save/2,3` for saving to disk and `validate/2` for file validation (size, type, extension).
- **Custom error pages**: Configure custom error page module via `Application.put_env(:nex_core, :error_page_module, MyApp.ErrorPages)`. Custom module must implement `render_error/4` function.

### Fixed
- **`Nex.CSRF`**: Fixed syntax error where Logger.warning was missing around the warning string.
- **`Nex.Session`**: Fixed compilation error - `dev_env?/0` function was defined outside the module.
- **`Nex.Handler`**: Fixed compilation error - removed duplicate `dev_env?/0` function definition.
- **`Nex.Upload`**: Fixed path traversal vulnerability - filename is now sanitized using `Path.basename/1` before saving.
- **`Nex.RateLimit`**: Fixed memory leak - added periodic cleanup of expired ETS entries to prevent unbounded growth.
- **Installer (`mix nex.new`)**: 
  - Fixed command injection vulnerability by using `System.cmd/3` with proper arguments instead of shell interpolation
  - Fixed argument parsing - now properly handles missing project name after options like `--path`
  - Fixed version hardcoding - now reads version dynamically from VERSION file

### Changed
- **`Nex.Handler`**: Now checks for custom error page module before falling back to default error page.
## [Nex 0.3.9] - 2026-02-21

### Fixed
- **`Nex.Handler` — `send_action_response/2`**: Page actions returning `%Nex.Response{}` structs (via `Nex.redirect/1`, `Nex.json/2`, `Nex.html/2`, `Nex.text/2`, `Nex.status/2`) now work correctly. Previously crashed with `Protocol.UndefinedError` for `Phoenix.HTML.Safe`. HTMX redirects use `HX-Redirect` header; non-HTMX redirects use standard HTTP 302.

## [Nex 0.3.8] - 2026-02-20

### Added
- **`Nex.WebSocket`**: User-level WebSocket support. Define a handler module with `use Nex.WebSocket` in `src/api/`, implement `handle_connect/1`, `handle_message/2`, `handle_disconnect/1`, and optionally `initial_state/1`. Automatically routed at `/ws/*` (e.g. `src/api/chat.ex` → `ws://host/ws/chat`). Supports broadcasting via `Nex.WebSocket.broadcast/2` and topic subscriptions via `Nex.WebSocket.subscribe/1`.
- **`Nex.RateLimit`**: ETS-based sliding window rate limiter. `Nex.RateLimit.check/2` returns `:ok` or `{:error, :rate_limited}`. Configurable via `Application.put_env(:nex_core, :rate_limit, max: 100, window: 60)`.
- **`Nex.RateLimit.Plug`**: Drop-in Plug middleware for per-IP rate limiting. Adds `X-RateLimit-Limit` and `X-RateLimit-Remaining` response headers. Returns HTTP 429 with JSON body when exceeded.

### Changed
- **`Nex.Handler`**: Added `/ws/*` routing — WebSocket upgrade requests are dispatched to `src/api/` modules that implement `handle_message/2`.

## [Nex 0.3.7] - 2026-02-20

### Added
- **`Nex.Cookie`**: Cookie read/write API. `Nex.Cookie.put/3`, `Nex.Cookie.delete/2`, `Nex.Cookie.get/2`, `Nex.Cookie.all/0`. Cookies are loaded at request start and applied to the response automatically. Available as `Cookie` alias in all page/component modules via `use Nex`.
- **`Nex.Session`**: Server-side session storage backed by ETS. Persists across page navigations via a signed `_nex_session` cookie. `Nex.Session.get/2`, `Nex.Session.put/2`, `Nex.Session.update/3`, `Nex.Session.delete/1`, `Nex.Session.clear/0`. Requires `SECRET_KEY_BASE` in production. Available as `Session` alias via `use Nex`.
- **`Nex.Flash`**: One-time flash messages stored in session. `Nex.Flash.put/2`, `Nex.Flash.pop_all/0`, `Nex.Flash.get/1`, `Nex.Flash.clear/0`. Messages survive one redirect and are cleared after being read. Available as `Flash` alias via `use Nex`.
- **`Nex.Middleware`**: Plug pipeline support. Configure plugs via `Application.put_env(:nex_core, :plugs, [MyApp.Plugs.Auth])`. Plugs run on every request before routing; halting a plug skips routing entirely.
- **`Nex.SessionCleaner`**: Background GenServer that cleans up expired session ETS entries every 10 minutes.

### Changed
- **`use Nex`**: Now automatically aliases `Nex.Cookie`, `Nex.Session`, and `Nex.Flash` in all page/component/layout modules — no manual import needed.
- **`Nex.Supervisor`**: Added `Nex.SessionCleaner` to the supervision tree. Session ETS table is initialized at supervisor start.
- **`Nex.Handler`**: Loads cookies and session at request start; persists session cookie and applies pending cookie writes before sending response; runs middleware pipeline before routing.

## [Nex 0.3.6] - 2026-02-20

### Added
- **Static file serving**: Framework now automatically serves files from `priv/static/` at `/static/*` URLs. Drop a file in `priv/static/` and it is immediately accessible — no `Plug.Static` configuration needed.
- **`Nex.Helpers` — `truncate/2,3`**: Truncates a string to a given length with configurable omission string (default `"..."`). Available in all page/component modules via `use Nex`.
- **`Nex.Helpers` — `pluralize/3`**: Returns `"N word"` or `"N words"` based on count. Available in all page/component modules.
- **`Nex.Helpers` — `clsx/1`**: Builds a CSS class string from a list, filtering out `nil`/`false` and supporting `{class, condition}` tuples. Available in all page/component modules.

### Changed
- **`Nex.Env` production-safe**: `Nex.Env.init/0` no longer calls `Mix.env()` (unavailable in compiled releases). Now uses `function_exported?(Mix, :env, 0)` guard with `System.get_env("MIX_ENV", "prod")` fallback. All `IO.puts` replaced with `Logger` calls.
- **CSRF — signed tokens**: `Nex.CSRF` now uses `Phoenix.Token.sign/verify` for cryptographically signed, stateless CSRF tokens. Tokens are verified across request cycles without server-side session storage. Requires `SECRET_KEY_BASE` env var in production (logs a warning if missing, uses a dev-only default).
- **Dev error page**: Unhandled exceptions in development now render a styled dark-theme error page showing exception type, message, formatted stacktrace (app frames highlighted), and request details (method, path, params, host). Production shows a clean minimal error page unchanged.

## [Nex 0.3.5] - 2026-02-19

### Changed
- **Zero-boilerplate layouts**: Layouts no longer require `{meta_tag()}` or `hx-headers={hx_headers()}`. The framework now handles both automatically:
  - `<meta name="csrf-token">` is auto-injected into `</head>` on every page render.
  - HTMX CSRF headers are already handled by the framework's injected JS (`htmx:configRequest` listener). `hx-headers` on `<body>` was always redundant and is no longer needed.
  - A minimal layout now only needs `hx-boost="true"` on `<body>` (optional, for SPA navigation).

## [Nex 0.3.4] - 2026-02-19

### Added
- **`Nex.Helpers` module**: Common formatting helpers (`format_number/1`, `format_date/1`, `time_ago/1`) automatically imported into all page, component, and layout modules via `use Nex`. Eliminates repeated boilerplate across projects.

### Fixed
- **CSRF auto-injection now covers HTMX forms**: The automatic CSRF token injection in `Nex.Handler` previously only matched `<form method="post">`. It now also matches `<form hx-post="...">`, `<form hx-put="...">`, `<form hx-patch="...">`, and `<form hx-delete="...">` — the common HTMX pattern that omits the `method` attribute.
- **`Nex.Store` cleanup logging**: Replaced `IO.puts` with `Logger.debug` so store cleanup messages are suppressed in production by default.

## [NexBase 0.3.4] - 2026-02-18

### Fixed
- **`NexBase.sql/3` nil rows crash**: INSERT/UPDATE/DELETE statements return `nil` rows from Postgrex. Now correctly returns `{:ok, []}` instead of crashing with `Enumerable not implemented for nil`.

## [NexBase 0.3.3] - 2026-02-18

### Fixed
- **Extra Postgrex options passthrough**: `NexBase.init/1` now forwards extra options (`prepare`, `queue_target`, `queue_interval`, `timeout`, `connect_timeout`) to the Postgrex config. This enables `prepare: :unnamed` for pgBouncer/Supabase Transaction Pooler compatibility.

## [NexBase 0.3.2] - 2026-02-10

### Fixed
- **Single-connection name bug**: First `NexBase.init/1` call now assigns the Ecto module name (e.g. `NexBase.Repo.SQLite`) instead of a unique atom (`:nex_base_N`). This fixes "could not lookup Ecto repo because it was not started" errors when using the default single-connection mode.
- **First-init-wins semantics**: `default_conn` is now only set on the first `init/1` call, preventing subsequent calls from overwriting the default connection with a multi-db unique name.

## [NexBase 0.3.1] - Unreleased

### Fixed
- **`postgrex` is now a regular (non-optional) dependency** — PostgreSQL is the default adapter and should work out of the box without users manually adding `{:postgrex, "~> 0.19"}` to their deps

## [NexBase 0.3.0] - Unreleased

### Added
- **Multi-connection support**: Connect to multiple databases simultaneously. `NexBase.init/1` returns a `%NexBase.Conn{}` struct that flows through the query chain via pipe: `conn |> NexBase.from("users") |> NexBase.run()`
- **`NexBase.Conn` struct**: Encapsulates connection identity (adapter, repo module, process name)
- **`NexBase.from/2`**: Accepts a conn as first argument for pipe-friendly multi-db queries
- **`NexBase.sql/3`, `query/3`, `query!/3`**: Accept conn as first argument for raw SQL on specific connections
- **Dynamic Repo routing**: Uses Ecto's `put_dynamic_repo` to route queries to the correct database process

### Changed
- **`NexBase.init/1` returns `%NexBase.Conn{}`** instead of `:ok` (backward compatible — can be ignored for single-connection usage)
- **`NexBase.Repo.child_spec/1`** accepts `%NexBase.Conn{}` for supervision tree registration
- **Internal architecture**: All query execution now resolves the target Repo from the conn struct

### Migration from 0.2.x
No API changes required for single-connection usage. Multi-connection is opt-in.

## [NexBase 0.2.0] - Unreleased

### Added
- **SQLite support**: Auto-detected from URL scheme (`sqlite:///path/to/db` or `sqlite::memory:`)
- **Multi-database architecture**: Two internal Repo modules (`NexBase.Repo.Postgres`, `NexBase.Repo.SQLite`) behind a unified `NexBase.Repo` facade
- **Portable SELECT**: Replaced PostgreSQL-specific `row_to_json` with raw SQL for cross-database compatibility
- **`NexBase.adapter/0`**: Returns the currently active adapter (`:postgres` or `:sqlite`)

### Changed
- **Dependencies**: `postgrex` and `ecto_sqlite3` are now optional — users add the driver they need
- **`ilike` on SQLite**: Silently degrades to `LIKE` (SQLite LIKE is case-insensitive for ASCII by default)
- **`rpc/2` on SQLite**: Raises a clear error (stored procedures are PostgreSQL-only)
- **SQL placeholders**: Automatically converts `$1, $2` to `?` for SQLite

### Migration from 0.1.x
No API changes required. Existing PostgreSQL users just need to add `{:postgrex, "~> 0.19"}` to their deps (it was previously a transitive dependency).

## [0.3.3] - 2026-02-07

### Added
- **Automated CSRF Protection**: Framework now automatically injects hidden CSRF tokens into all state-changing `<form>` tags (POST, PUT, PATCH, DELETE) and handles HTMX headers. Manual `{csrf_input_tag()}` and `hx-headers` are no longer required.
- **Refined Project Installer**: Enhanced `mix nex.new` with reserved name validation, automatic git initialization, and `.formatter.exs` generation.
- **Enhanced AI Agent Instructions**: Deeply optimized `AGENTS.md` with anti-hallucination guidelines, startup command instructions, and surgical UX patterns.
- **NexBase `sql/2` helper**: `NexBase.sql("SELECT ...", [params])` returns `{:ok, [%{col => val}]}` directly, hiding raw Postgrex result parsing.

### Changed
- **NexBase Supabase-style API**: Redesigned to `NexBase.init(url: ..., ssl: true)` + `NexBase.from("table")` — no client, no Repo knowledge needed.
- **NexBase decoupled from nex_core**: NexBase is now a standalone library with only `ecto_sql`, `postgrex`, and `jason`. Config passed via `NexBase.init/1`.
- **Removed `client/0`, `client/1`, `connect/1`**: Replaced by `NexBase.init/1` (for apps) and `NexBase.init(start: true)` (for scripts).

### NexBase 0.1.0 — Hex Publish Prep
- **Fixed**: `elixir: "~> 1.19"` → `"~> 1.17"` (1.19 does not exist)
- **Fixed**: `postgrex` version constraint `">= 0.0.0"` → `"~> 0.19"`
- **Added**: `source_url`, `homepage_url` to mix.exs package metadata
- **Added**: Type specs (`@type t`) to `NexBase.Query`
- **Removed**: Dead `NexBase.Client` module (replaced by direct `NexBase.from/1` API)
- **Removed**: Unused `opts` parameter from `NexBase.run/1`
- **Fixed**: `rpc/3` doc referencing non-existent `:repo` option
- **Rewritten**: README.md — English, matches actual API (no more `@client` pattern), documents `init/1`, `sql/2`, script usage

## [0.3.2] - 2026-01-04

### Added
- **AI Agent Handbook (AGENTS.md)**: Automatically generated for new projects to guide AI assistants (Cursor, Claude, etc.) in writing idiomatic Nex code.
- **Improved Vibe Coding Support**: Optimized framework architecture and documentation for intent-driven development.

### Changed
- **Renamed `Nex.CSRF.input_tag/0` to `Nex.CSRF.csrf_input_tag/0`**: Better clarity and consistency with AI guidance.
  - **Migration**: Replace `{csrf_input_tag()}` with `{csrf_input_tag()}` (wait, actually just update the name in your templates).
- **Refined API Terminology**: Removed misleading "API 2.0" references in favor of "JSON API Standards".

### Improved
- **REST API Guidance**: Clearer mapping between file-system routes and HTTP method handlers.
- **SSE Streaming UX**: Standardized placeholder rendering and chunk encoding for AI responses.

### Added
- **Unified `use Nex` Interface**: One statement for all module types
  - All modules (Pages, APIs, Components, Layouts) now use the same `use Nex` statement
  - Framework automatically detects module type based on path (`.Api.`, `.Pages.`, `.Components.`, `.Layouts`)
  - API modules: no imports (pure functions)
  - Page/Partial/Layout modules: automatic HEEx + CSRF imports
  - Provides the simplest possible developer experience
  - Fully aligned with Next.js convention-over-configuration philosophy

- **`Nex.stream/1` - Server-Sent Events (SSE) Support**: Native streaming response for AI applications
  - Simple callback function API: `Nex.stream(fn send -> send.("message") end)`
  - Similar to Python's `StreamingResponse` with generators and Next.js's `ReadableStream`
  - Real-time streaming using `Finch.stream` - data is sent as it arrives (true typewriter effect)
  - Automatic SSE formatting and header management
  - Zero boilerplate - simpler than Python FastAPI and Next.js (5 lines vs 7 lines vs 15 lines)
  - Perfect for AI streaming responses (OpenAI, Anthropic, etc.)
  - Automatic connection close detection and error handling
  - Smart history management using `Nex.Store` to avoid URL length limits
  - Supports custom events: `send.(%{event: "message", data: "content"})`
  - Example: Complete AI chatbot with history in under 50 lines of code

### Changed
- **BREAKING: Removed `Nex.Page`, `Nex.Api`, `Nex.Partial`, and `Nex.SSE` modules**: Use `use Nex` instead
  - All `use Nex.*` modules have been completely removed
  - Using `use Nex.Page`, `use Nex.Api`, `use Nex.Partial`, or `use Nex.SSE` will now cause compilation errors
  - **Migration required**: Replace all with `use Nex`
  - Framework automatically detects module type based on path and imports appropriate functions
  - This is a hard breaking change - old code will not compile until updated

- **BREAKING: Renamed `partials/` to `components/`**: Better alignment with modern frameworks
  - Directory: `src/partials/` → `src/components/`
  - Module namespace: `MyApp.Partials.*` → `MyApp.Components.*`
  - Path detection: `.Partials.` → `.Components.`
  - **Reason**: Aligns with React, Vue, Svelte, and Phoenix 1.7+ conventions
  - **Migration required**: Rename directories and update module names
  - Improves DX for developers familiar with modern frontend frameworks

### Migration Guide

**Before:**
```elixir
defmodule MyApp.Pages.Index do
  use Nex.Page  # ❌ Will cause compilation error
end

defmodule MyApp.Api.Users do
  use Nex.Api  # ❌ Will cause compilation error
end

defmodule MyApp.Partials.Header do
  use Nex.Partial  # ❌ Will cause compilation error
end

# Old directory structure
src/partials/  # ❌ Old naming
```

**After:**
```elixir
defmodule MyApp.Pages.Index do
  use Nex  # ✅ Unified interface
end

defmodule MyApp.Api.Users do
  use Nex  # ✅ Unified interface
end

defmodule MyApp.Components.Header do
  use Nex  # ✅ Unified interface + new naming
end

# New directory structure
src/components/  # ✅ Modern naming (aligned with React/Vue/Phoenix)

# SSE endpoints use unified interface with Nex.stream/1
defmodule MyApp.Api.Chat.Stream do
  use Nex  # ✅ Unified interface

  def get(req) do
    message = req.query["message"]
    
    Nex.stream(fn send ->
      send.("Thinking...")
      send.("Processing...")
      send.("Done!")
    end)
  end
end
```

### Improved
- **Simplified Module API**: Reduced cognitive load for developers
  - No need to remember different `use` statements for different module types
  - One unified interface: `use Nex`
  - Better alignment with Next.js simplicity
  - Automatic type detection based on module path

### Removed
- **Legacy SSE Code Cleanup**: Removed ~160 lines of deprecated SSE handling code
  - Deleted old `handle_sse/3`, `handle_sse_endpoint/3`, `try_sse_index_module/2`, `send_sse_stream/3`
  - Deleted old `format_sse_event/1` (4 overloads) and `format_sse_data/1` (2 overloads)
  - Removed `/sse/*` route (use `/api/*` with `Nex.stream/1` instead)
  - Removed `__sse_endpoint__` marker check
  - All SSE functionality now uses the unified `Nex.stream/1` API

### Refactored
- **Unified Route Resolution**: Consolidated all route resolution logic into `RouteDiscovery`
  - Moved `resolve_action`, `resolve_page_module`, `resolve_api_module` from Handler to RouteDiscovery
  - **Removed all legacy fallback code** - now only uses file-system based routing
  - Deleted `resolve_legacy`, `resolve_action_legacy`, `path_to_module_parts`, `is_dynamic_segment?`
  - New unified API: `RouteDiscovery.resolve(:pages | :api | :action, path)`
  - Handler now only orchestrates request handling, RouteDiscovery handles all routing
  - Handler reduced from 714 lines to 537 lines (-25%)
  - RouteDiscovery reduced from 394 lines to 342 lines (-13%)
  - Clearer separation of concerns: Handler = request processing, RouteDiscovery = routing
  - **Breaking**: Dynamic routes now require explicit file-system routing (e.g., `[id].ex`)
  - Fixed: `index.ex` files now correctly match their parent path (e.g., `pages/index.ex` → `/`, `users/index.ex` → `/users`)

## [0.3.0] - 2025-12-31

### Added
- **`Nex.html/2` Response Helper**: New helper function for returning HTML responses
  - Designed for HTMX scenarios where API endpoints return HTML fragments
  - Consistent API with other response helpers (`json`, `text`, `redirect`, `status`)
  - Example: `Nex.html("<div>User Profile</div>", status: 200)`

### Changed
- **BREAKING: API Request Object Redesign**: Fully aligned with Next.js API Routes
  - Removed `req.params` field (not in Next.js)
  - Removed `req.path_params`, `req.query_params`, `req.body_params` (not in Next.js)
  - Kept only Next.js standard fields: `req.query`, `req.body`, `req.method`, `req.headers`, `req.cookies`
  - `req.query` now contains path params + query string (path params take precedence)
  - `req.body` is completely independent and never merged with `req.query`
  - Migration: Replace `req.params["id"]` with `req.query["id"]` or `req.body["name"]`

### Fixed
- **API Request Body Handling**: Fixed crash when request has no body or invalid Content-Type
  - `req.body` is now guaranteed to be a Map (never `%Plug.Conn.Unfetched{}`)
  - Prevents `FunctionClauseError` when using `Map.has_key?/2` on `req.body`
  - GET requests and requests without body now have `req.body == %{}`

### Improved
- **Developer Experience**: Enhanced error messages for API handlers
  - Development environment now returns detailed error information in JSON response
  - Production environment returns generic error message for security
  - Error messages now include `Nex.html/2` in the list of available helpers
  - Added comprehensive documentation to `Nex.Req` module with Next.js comparison

## [0.2.4] - 2025-12-30

### Added
- **RESTful API Support**: Extended HTTP method support for DELETE, PUT, and PATCH requests
  - CSRF validation now applies to all modifying methods (POST, DELETE, PUT, PATCH)
  - Action handlers can now respond to DELETE/PUT/PATCH requests
  - Enables full RESTful API implementations
- **SSE Performance Documentation**: Added comprehensive guide for Server-Sent Events
  - Performance characteristics and concurrency limits
  - System tuning recommendations for high-concurrency deployments
  - Time synchronization patterns for real-time applications
  - Production-ready SSE endpoint examples
- **LLM Usage Guide**: Created `.cursorrules` file for AI-assisted development
  - Complete framework architecture overview
  - Module usage patterns and conventions
  - Common patterns and best practices
  - Security and performance guidelines
- **New Example Projects**:
  - `todos_api`: RESTful API example with HTMX integration demonstrating DELETE/PUT operations
  - `energy_dashboard`: Time-synchronized SSE dashboard showing real-time energy prices
- **Architecture Documentation**: Added comprehensive framework architecture guide (`arch.md`)

### Fixed
- **SSE Handler**: Fixed connection closure to return `Plug.Conn` instead of `:ok`
  - Prevents "expected dispatch/2 to return a Plug.Conn" error
  - Properly handles client disconnections
- **SSE Event Format**: Corrected event format for HTMX SSE extension compatibility
  - Changed from JSON-wrapped format to plain text format
  - Events now properly trigger `sse-swap` updates in HTMX

### Changed
- Removed unused `test_app` and old `todos` examples for clarity

## [0.2.3] - 2025-12-30

### Changed
- **HTMX Boost**: Added `hx-boost="true"` to body tag in all layouts (installer template and examples)
  - Automatically converts regular links to AJAX requests for smoother page transitions
  - Maintains browser history and back/forward functionality
  - Progressive enhancement: works without JavaScript
  - Users can selectively disable with `hx-boost="false"` on specific elements

## [0.2.2] - 2025-12-28

### Changed
- **Internationalization**: Translated all project documentation and code comments to English
  - Core documentation: AGENTS.md, CLAUDE.md, VERSIONING.md, CHANGELOG.md
  - Framework code: Module documentation and inline comments
  - Example projects: All README files and UI text
  - Installer documentation: CHANGELOG.md entries
  - Specifications: website.md and related docs
  - Result: Project is now fully English-ready for open source release

## [0.2.1] - 2025-12-28

### Changed
- **Version Sync**: Synchronized version number to 0.2.1, consistent with nex_new

## [0.2.0] - 2025-12-28

### Removed
- **Mix.Tasks.Nex.Release**: Removed `mix nex.release` command, switched to Docker as standard deployment method
  - Projects automatically generate Dockerfile and .dockerignore on creation
  - Simplified deployment process, unified containerization approach

### Changed
- **Documentation**: Rewrote README to clarify framework positioning and philosophy
  - Emphasized Nex is suitable for rapid prototyping, indie hackers, learning HTMX, internal tools
  - Clarified Nex is not a Phoenix competitor, not an enterprise framework
  - Highlighted CDN-first, Docker-ready design philosophy

## [0.1.6] - 2025-12-28

### Fixed
- **Framework & Installer**: Fixed version number management approach, now hardcoding version in mix.exs to completely resolve VERSION file reading issues when installed as Hex dependency

## [0.1.5] - 2025-12-28

### Fixed
- **Framework**: Fixed VERSION file reading path issue, using `__DIR__` to ensure correct version number reading when installed as dependency
- **Mix.Tasks.Nex.Start**: Added compilation step to fix 404 errors in production mode caused by unloaded modules
- **Mix.Tasks.Nex.Dev**: Optimized dependency checking logic to avoid triggering Hex compatibility issues when using path dependencies

### Changed
- **Package Metadata**: Updated GitHub repository link to `gofenix/nex`
- **Installer**: Added README.md file, improved package documentation

## [0.1.4] - 2025-12-28

### Changed
- **Installer Package**: Updated default project template to improve out-of-the-box experience
  - `layouts.ex`: Added DaisyUI CDN for semantic components (card, btn, navbar, etc.)
  - `index.ex`: Rewrote example page using DaisyUI components
  - Used semantic colors like `bg-base-100`, `bg-base-300` instead of hardcoded colors

## [0.1.3] - 2025-12-28

### Added
- **Mix.Tasks.Nex.Start**: Added production environment start command `mix nex.start`
  - Supports deployment to platforms like Railway, Fly.io, Docker
  - Reads `PORT` and `HOST` from environment variables
  - Automatically disables hot reload in production
  - Listens on `0.0.0.0` by default for container environments
- **Website**: Added `railway.json` configuration file to simplify Railway deployment
- **Website**: Restructured project, added `src/support/` directory for auxiliary modules

## [0.1.2] - 2025-12-28

### Changed
- **Mix.Tasks.Nex.Dev**: Automatically checks and installs missing dependencies to prevent startup failures due to not running `mix deps.get`
- **Mix.Tasks.Nex.New**: Automatically runs `mix deps.get` after project creation, users can directly run `mix nex.dev` to start the project

### Fixed
- **Mix.Tasks.Nex.Dev**: Fixed `app_name` type error to ensure `Application.ensure_all_started/2` receives atom type parameter

## [0.1.1] - 2025-12-28

### Fixed
- **Installer Package**: Fixed VERSION file path issue that prevented `mix archive.install hex nex_new` from working
- **Package Structure**: Both `nex_core` and `nex_new` now include their own VERSION, README.md, and CHANGELOG.md files instead of referencing parent directory

## [0.1.0] - 2025-12-28

### Published
- **Hex.pm Release**: Published `nex_core` v0.1.0 to Hex.pm at https://hex.pm/packages/nex_core/0.1.0
- **Hex.pm Release**: Published `nex_new` v0.1.0 to Hex.pm at https://hex.pm/packages/nex_new/0.1.0
- **Documentation**: Published documentation to HexDocs at https://hexdocs.pm/nex_core/0.1.0 and https://hexdocs.pm/nex_new/0.1.0

### Changed
- **Naming**: Renamed framework core package to `nex_core`, renamed project generator to `nex_new` to resolve Hex.pm naming conflicts
- **Version Management**: Installer and framework now share a single VERSION file for synchronized releases
- **Dependencies**: Added `ex_doc` as dev dependency for documentation generation

## [0.1.0] - 2025-12-28

### Added
- **Nex.CSRF**: Added CSRF protection module to prevent cross-site request forgery attacks
  - `Nex.CSRF.generate_token/0` - Generate CSRF token
  - `Nex.CSRF.input_tag/0` - Generate form hidden field
  - `Nex.CSRF.hx_headers/0` - Generate HTMX hx-headers attribute
  - `Nex.CSRF.validate/1` - Validate CSRF token in requests
  - Auto-injected into pages, HTMX requests automatically carry `X-CSRF-Token` header
  - POST requests automatically validate CSRF token
- **Nex.RouteDiscovery**: Added dynamic route discovery module supporting file-system-based automatic route discovery
  - Supports single-parameter dynamic routes `[param]` (e.g., `users/[id].ex` matches `/users/123`)
  - Supports named parameter routes `[slug]` (e.g., `posts/[slug].ex` matches `/posts/hello-world`)
  - Supports multi-parameter dynamic routes (e.g., `posts/[year]/[month].ex` matches `/posts/2024/12`)
  - Supports wildcard routes `[...path]` (e.g., `docs/[...path].ex` matches any level of path)
  - Supports mixed routes (e.g., `files/[category]/[...path].ex`)
  - Route caching mechanism (ETS), auto-refresh on file changes in development
  - Route priority sorting: static routes > dynamic routes > wildcard routes
- **Examples**: Added `dynamic_routes` example project showcasing all dynamic route types
- **Mix.Tasks.Nex.New**: Global project generator to create complete Nex projects from scratch
  - `mix nex.new my_app` - Create complete project structure (no need to run mix new first)
  - Supports specifying path `mix nex.new my_app --path ~/projects`
  - Automatically generates clean mix.exs, no need to clean up lib/ and test/
  - Packaged as Mix archive via `installer/` directory
- **Mix.Tasks.Nex.Release**: Added production build task
  - `mix nex.release` - Compile and package for deployment
  - Automatically generates optimized Dockerfile for containerized deployment

### Changed
- **Nex.Handler**: Refactored route parsing logic to prioritize `Nex.RouteDiscovery` for dynamic route matching
  - `resolve_page_module/1` now supports filename-based dynamic parameter extraction
  - `resolve_api_module/1` also supports dynamic routes
  - `resolve_action/1` supports POST actions under dynamic routes
  - Retained legacy route logic for backward compatibility
- **Nex.Reloader**: Automatically clears route cache on file changes to ensure new routes take effect immediately
- **Nex.Reloader**: Automatically disables hot reload in production
  - `Nex.Reloader.enabled?/0` checks if enabled (only in `:dev` environment)
  - Production environment does not inject live reload WebSocket script
  - Environment configured via `Application.get_env(:nex, :env)`
- **Mix.Tasks.Nex.Dev**: Refactored process startup logic, using `Nex.Supervisor` instead of manually starting individual processes
  - Before: Manually starting Store, PubSub, Reloader (no supervision)
  - Now: Unified management through `Nex.Supervisor.start_link()` (with supervision)

### Added (Earlier)
- **Nex.Supervisor**: Added framework-level supervision tree module to uniformly manage Nex core processes (Store, PubSub, Reloader)
  - Any process crash automatically restarts, improving framework reliability
  - Completely transparent to users, no additional configuration required
  - Follows OTP best practices

### Security
- **Nex.Handler**: Fixed atom exhaustion vulnerability (CVE-level security issue) by replacing `String.to_atom/1` with `String.to_existing_atom/1` for user-supplied input. This prevents attackers from crashing the server by requesting random paths like `/api/random_1`, `/api/random_2`, etc.
- **Nex.Handler**: Improved privacy by moving `page_id` from request payload to HTTP header (`X-Nex-Page-Id`), preventing exposure in browser dev tools and network logs
- **Nex.Store**: Added automatic process dictionary cleanup after each request using `Plug.Conn.register_before_send/2`, preventing potential session leakage when HTTP server processes are reused

### Performance
- **Nex.Store**: Optimized `touch_page/1` to use `:ets.match/2` instead of `:ets.foldl/3`, reducing complexity from O(n) to O(m) where m is the number of keys for a specific page. This significantly improves performance when the ETS table contains many pages.

### Removed
- **Mix.Tasks.Nex.Init**: Removed `mix nex.init` task. Now recommend using `mix nex.new` from `installer` to create projects, to maintain framework simplicity and single responsibility principle.

### Fixed
- **Nex.Env**: Fixed `.env` file loading to correctly resolve project root directory using `Mix.Project.app_path()` instead of relying on current working directory
- **Nex.Env**: Fixed environment variables not being accessible via `System.get_env/1` by explicitly setting loaded variables to system environment after Dotenvy parsing
- **Nex.Env**: Added debug logging to show which `.env` files are being loaded, their paths, and how many variables were loaded
- **Mix.Tasks.Nex.Dev**: Fixed user application not being started, which prevented application supervision trees (like Finch for HTTP clients) from running

### Added
- **Nex.SSE**: Added new `Nex.SSE` behaviour module for defining Server-Sent Events (SSE) endpoints
- **SSE Routing**: SSE endpoints can now be placed anywhere in the application structure (e.g., `src/api/chat/stream.ex`) and are identified by `use Nex.SSE` instead of requiring a specific `/sse/` path
- **SSE Handler**: Added `handle_sse_endpoint/3` function to automatically detect and handle modules that use `Nex.SSE` behaviour
- **Documentation**: Added comprehensive `application.md` guide explaining Application modules and supervision trees
- **Examples**: All example projects now include a default `Application` module template with documentation
- **Examples**: Added `chatbot_sse` example demonstrating SSE streaming with HTMX SSE extension for zero-JavaScript real-time streaming
- **Getting Started**: Updated quick start guide to include Application module setup

### Changed
- **SSE Routing**: SSE endpoints are now identified by `use Nex.SSE` behaviour instead of path-based detection, allowing SSE endpoints to coexist with JSON API endpoints at the same path structure
- **Handler**: Refactored API routing to check for SSE endpoints (via `__sse_endpoint__/0` function) before treating as regular API endpoints
- **Handler**: SSE format now supports plain text for `message` event type (for HTMX SSE extension compatibility) while maintaining JSON format for other event types
- **Nex.Env**: Improved `.env` file path resolution to work correctly regardless of where `mix nex.dev` is executed from
- **Nex.Env**: Enhanced error handling to show specific error messages when `.env` file loading fails
- **Examples**: All example projects (`chatbot`, `guestbook`, `todos`) now have `Application` modules configured in `mix.exs`
- **Live Reload**: Migrated from HTTP polling to WebSocket push for instant file change notifications without network request spam

### Deprecated
- **SSE Path Convention**: The `/sse/*` path convention is still supported for backward compatibility but is now considered legacy. New SSE endpoints should use `use Nex.SSE` and can be placed under `/api/*` or any other path structure
