Revision history for PAGI-Nano

0.001000  2026-07-15
        - Initial release.
        - app { } block-scoped collector returning an assembled PAGI app value
          (no global state), built on PAGI::App::Router, PAGI::Middleware,
          PAGI::Lifespan, and PAGI::App::File.
        - Routing verbs get/post/put/patch/del/any; group (prefix + shared
          middleware) and mount (nest any PAGI app); path placeholders
          (:name, {name}, {name:regex}, *splat) map to the handler signature.
        - Return-value coercion: PAGI::Response as-is; hashref/arrayref to JSON
          (convert_blessed, so nested TO_JSON objects serialize); scalar to
          text/plain; undef/return; a loud 500. Thrown respond-ables are sent.
        - enable (app-wide) and [\@middleware] (route/group) event-layer
          middleware; named middleware resolved like PAGI::Middleware::Builder.
        - startup/shutdown lifecycle with app-lifetime shared state ($c->state).
        - static file serving, custom not_found, websocket and sse (imperative),
          and streaming request/response bodies.
        - $c->params strong parameters via PAGI::StructuredParameters
          (PAGI::Nano::Context::HTTP).
        - Two run shapes: single-file app.pl and a lib/MyApp.pm modulino.
        - Requires PAGI::Tools 0.002002 (extension-aware default WebSocket
          declines; stricter Test::Client lifespan-startup) and
          PAGI::StructuredParameters.
        - Per-request service makers run at most once per service name per
          request even when they return undef; memoization is based on cache-key
          existence rather than value definedness.
        - ServiceRegistry: a service builder that returns a Future croaks
          loudly at lifespan startup, naming the service, rather than silently
          storing the unresolved Future as the "service" (which fails
          cryptically later at $c->service). Service builders are synchronous
          by design; use a per-request maker (a plain coderef) or factory(...)
          for deferred or per-call construction.
        - PAGI::Nano::resolve_service($app, $name): a test-facing seam to
          resolve an app-scoped service value after lifespan startup without
          issuing a request, via a private probe token on the assembled app
          coderef. Returns the raw app-scoped value (a per-request maker or
          factory marker comes back as-is, not per-request-resolved).
        - Custom not_found responses emit protocol-valid decline events:
          SSE uses sse.http.response.*, and WebSocket uses
          websocket.http.response.* when the server advertises that extension.
          WebSockets without it bypass the handler and close for a portable
          body-less 403. Translated byte bodies may be buffered or streamed;
          SSE and WebSocket file/fh bodies fail loudly.
        - Context uri_for percent-encodes decoded strings from UTF-8 bytes.
          Ordinary placeholders encode one segment and reject slash-containing
          values in favor of *splat routes; splats preserve separators while
          encoding each segment. Query keys and values remain sorted and use
          %20 for spaces.
