# This file documents the revision history for Perl extension PAGI

0.001019 - 2026-02-19
  [New Features]
  - Router: rewrite _compile_path() as tokenizer with proper regex escaping,
    so literal path segments containing regex metacharacters (e.g., /foo.bar)
    are matched literally instead of as patterns
  - Router: inline constraint syntax {name:pattern} for path parameters
    (e.g., /user/{id:\d+}) validated during dispatch
  - Router: chainable constraints() method for applying regex constraints
    to named path parameters after route definition
  - Router: any() multi-method matcher supporting wildcard ('*') and
    explicit method lists (e.g., any [qw(GET POST)] => '/path' => $handler)
  - Router: named routes and uri_for() now support {name:pattern} syntax
    and any() routes
  - Router: group() for organizing routes under a common prefix, with three
    forms: callback (sub), router-object, and string (auto-require). Supports
    middleware, nesting, named route namespacing via as(), and conflict detection
  - Router: mount() now accepts a string class name with auto-require and
    to_app dispatch
  - SSE over HTTP/2 with streaming DATA frames, keepalive comments, and
    disconnect handling
  - Add PAGI::Utils::Random module for cryptographically secure random bytes
  - Rate limiter: periodic cleanup and safety valve for expired buckets
  - CORS middleware: warn when configured with wildcard origins and credentials

  [Security Fixes]
  - Fix XSS in Debug middleware panel by escaping scope values in HTML output
  - Add max_chunk_size limit (default 1MB) to HTTP/1.1 chunked transfer
    parser to prevent denial-of-service via unbounded chunk sizes
  - Use cryptographically secure random bytes (via /dev/urandom with
    Crypt::URandom fallback) in RequestId, CSRF, and Session middleware
    instead of rand()
  - Default session cookie SameSite=Lax to prevent CSRF via cross-site requests
  - Fix double URL-decoding in Static middleware that could allow path traversal

  [Bug Fixes]
  - Fix HTTP/2 path decoding to match HTTP/1.1 decoding pipeline
  - Fix HTTP/2 WebSocket path decoding to match HTTP/1.1 pipeline
  - Require Net::HTTP2::nghttp2 0.007+ for HTTP/2 support (was unversioned)

  [Improvements]
  - Extract _format_sse_event() and _format_sse_comment() helpers and
    protocol-abstracted keepalive writer for consistent SSE formatting
    across HTTP/1.1 and HTTP/2

0.001018 - 2026-02-15
  [Bug Fixes]
  - Skip HTTP/2 subtests when Net::HTTP2::nghttp2 is not installed, fixing
    CPAN tester failures on systems without optional nghttp2 dependency

0.001017 - 2026-02-11
  [New Features]
  - HTTP/2 support (experimental). Requires Net::HTTP2::nghttp2. Enable with
    --http2 flag. Supports ALPN negotiation (h2/http1.1 over TLS), cleartext
    h2c via connection preface detection, WebSocket over HTTP/2 (RFC 8441),
    streaming responses with backpressure, and configurable protocol settings
    (h2_max_concurrent_streams, h2_initial_window_size, etc.)
  - Worker heartbeat monitoring for multi-worker mode. Parent process detects
    workers with blocked event loops via Unix pipe heartbeat and replaces them
    with SIGKILL + respawn. Default 50s timeout (heartbeat_timeout option,
    --heartbeat-timeout CLI flag). Only detects event loop starvation — async
    handlers using await are unaffected regardless of duration.
  - Custom access log format strings (--access-log-format). Supports atoms
    like %a (address), %s (status), %D (duration μs), %b (body size).
    Enables structured JSON logging via format string.
  - Track response body size in access log (%b atom)

  [Bug Fixes]
  - Fix TLS performance cliff at 8+ concurrent connections caused by
    per-connection SSL context creation parsing the entire CA bundle.
    Shared SSL context via SSL_reuse_ctx gives ~26x throughput improvement.
  - Fix TLS in multi-worker mode — workers now SSL-upgrade connections
    in on_stream rather than relying on listener-level TLS
  - Fix SSE wire format to handle CRLF, LF, and bare CR line endings
    per the SSE specification
  - Fix multi-worker shutdown escalation — SIGKILL timer now fires
    correctly when workers don't exit after SIGTERM
  - Fix multi-worker parameter pass-through for server options
  - Fix flaky multi-worker TLS test

  [Improvements]
  - Add on_ssl_error callback to TLS listener setup for cleaner error
    handling (suppresses "EV: error in callback" noise)
  - Cache Server header string to avoid per-response interpolation
  - Merge double header loop in serialize_response_start
  - Cache access log timestamp at per-second granularity
  - Cache client_host in access log to avoid per-request getpeername syscall
  - Skip HTTP/2 tests when Net::HTTP2::nghttp2 is not installed
  - Skip SSE tests when Future::IO is not installed
  - Remove unused Sys::Sendfile recommendation from cpanfile

0.001016 - 2026-01-31
  [New Features]
  - Add query parameter parsing to PAGI::SSE: query_params(), query_param(),
    raw_query_params(), raw_query_param() methods mirroring PAGI::Request
    and PAGI::WebSocket APIs
  - pagi-server now auto-configures Future::IO for IO::Async if installed,
    enabling seamless use of Future::IO-based libraries (Async::Redis, etc.)
    and PAGI::SSE->every(). Reports configuration in non-production mode.

  [Breaking Changes]
  - PAGI::Server (the module) no longer auto-configures Future::IO.
    pagi-server (the CLI) handles this automatically. Programmatic users
    of PAGI::Server must configure Future::IO themselves if using
    Future::IO-based libraries. See PAGI::Server "LOOP INTEROPERABILITY".

  [Improvements]
  - PAGI::SSE->every() now provides a clear error message if Future::IO
    backend is not configured, with example code showing how to fix it
  - New PAGI::Server documentation section "LOOP INTEROPERABILITY" explains
    Future::IO configuration for programmatic usage

0.001015 - 2026-01-18
  [New Features]
  - Add query parameter parsing to PAGI::WebSocket: query_params(), query_param(),
    raw_query_params(), raw_query_param() methods mirroring PAGI::Request API
  - Add form_param($name, %opts) and raw_form_param($name) singular accessors
    to PAGI::Request for fetching individual form values

  [Deprecations]
  - Rename PAGI::Request param methods for naming consistency (old names
    deprecated with warnings):
    - query() -> query_param()
    - raw_query() -> raw_query_param()
    - form() -> form_params()
    - raw_form() -> raw_form_params()

  [Improvements]
  - Improve PAGI::Test::Response json() error diagnostics: now shows HTTP
    status code, Content-Type header, and body preview when JSON parsing fails.
    Uses croak for better stack traces pointing to test file location.
  - Add $JSON_ERROR_BODY_LIMIT package variable to control body preview length
    in json() error messages (default 1500 bytes)


0.001014 - 2026-01-16
  [New Features]
  - Add Future::XS opt-in support for improved performance (~4% multi-worker)
    Use --future-xs flag or PAGI_FUTURE_XS=1 environment variable
  - path_param() now strict by default - dies with helpful message on missing
    keys, catching typos early. Use strict => 0 to opt out.

  [Bug Fixes]
  - Fix connection leak where connections weren't removed from tracking hash
    on EOF/disconnect, causing memory growth under sustained load
  - Fix POD reference to connection state specification (was pointing to
    non-existent file)

  [Improvements]
  - Simplify max_connections default to 1000 (removed unreliable auto-detection)
  - Lazy disconnect_future() allocation - Future only created when called,
    reducing per-request overhead for simple handlers
  - path_param() now delegates to path_params(), allowing subclasses to
    override path_params for custom parameter handling (e.g., lazy conversion
    from positional to named parameters)
  - Add _default_path_param_strict_opt() hook for subclasses to change default
  - Document query_params/query strict and raw options in POD

0.001013 - 2026-01-04
  [New Features]
  - Add PAGI::Utils::handle_lifespan utility for simplified lifespan handling
  - Add Response getters (status, headers, content_type, body) and try setters
  - Support static path rewrites in PAGI::App::File
  - Add full-demo example demonstrating lifespan, HTTP, WebSocket, and SSE

  [Bug Fixes]
  - Fix POD formatting of nested angle brackets
  - Make handle_lifespan croak on wrong scope type for clearer error messages

  [Improvements]
  - Match plackup FindBin behavior for app loading
  - Refactor PAGI::Response to use exists() for lazy defaults
  - Remove deprecated path_param/path_params from PAGI::Response
  - Document state method in PAGI::Request
  - Normalize spec docs to ASCII

  [Build/Distribution]
  - Add .editorconfig for consistent editor settings
  - Add Thunderhorse to implementations documentation

0.001012 - 2026-01-01
  [New Features]
  - PAGI 0.2 specification: Loop-agnostic apps, SSE POST support, keepalive events
  - Send-side backpressure to prevent unbounded memory growth under slow clients
  - Configurable write_high_watermark (64KB) and write_low_watermark (16KB)
    matching Python asyncio defaults for optimal memory/throughput balance
  - SSE disconnect reasons (client_closed, timeout, write_error, shutdown)

  [Bug Fixes]
  - Fix Lint middleware to preserve original app errors instead of masking them
  - Fix SSE keepalive to use chunked transfer encoding
  - Fix missing IO::Async::Timer::Periodic use statement
  - Fix missing 'method' field in SSE test scopes (per spec line 624)
  - Fix WebSocket scope incorrectly including 'method' field in test client
  - Fix middleware class resolution for nested namespaces (Auth::Basic, etc.)
    using ^ prefix for fully-qualified class names

  [Improvements]
  - Improved event loop portability (Future::IO now fully optional)
  - Improve max_connections auto-detection using getrlimit(RLIMIT_NOFILE)
    instead of sysconf(_SC_OPEN_MAX) for accuracy
  - Add fd_headroom config option (default 100) for max_connections calculation
  - Add TTY warning at startup when access_log goes to terminal
  - Security fixes from code review
  - PAGI spec compliance fixes for event validation

  [Build/Distribution]
  - Replace Markdown::Pod with App::sdview for spec POD generation
    - Fixes =item - to =item * (proper POD bullet points)
    - Converts module names to L<> links for metacpan

0.001011 - 2025-12-28
  - Switch license from Perl_5 to Artistic_2_0 (cleaner language, OSI recommended)
  - Fix Ctrl-C signal handling for multi-worker mode: lifespan.shutdown now runs
    for all workers ensuring proper cleanup
  - Add -e and -M flags to Runner for inline apps (like perl -e/-M)
  - Add .pl and .md to MIME types in PAGI::App::File
  - Add exception trapping to PAGI::Test::Client (default behavior)
  - Add SIGNAL HANDLING documentation to PAGI::Server POD
  - Add SECURITY.md and CONTRIBUTING.md to repository
  - Document async completion contract and detect missing responses
  - Cookbook improvements: JWT validation, Redis sessions, redirect behavior,
    WebSocket heartbeat example, IO::File::WithPath for filehandles

0.001010 - 2025-12-26
  - BREAKING: Removed sendfile() from PAGI::Server - use PAGI::Middleware::XSendfile
    with nginx/Apache X-Sendfile/X-Accel-Redirect instead for large file serving
  - Added handle_ranges option to PAGI::App::File for byte-range request support
  - Added FreeBSD CI testing
  - Documentation fixes for phantom module references

0.001009 - 2025-12-26
  - BREAKING: PAGI::Response constructor now takes ($scope, $send) instead of ($send, $scope)
    for consistency with Request, SSE, and WebSocket APIs
  - Added request_timeout for stalled requests, ws_idle_timeout and sse_idle_timeout
    for idle connection cleanup (all disabled by default for performance)
  - Test::Client: multi-value headers/query/form support, fixed arrayref headers with form/json
  - Scope-based caching for Request, Response, SSE, WebSocket objects
  - Replace retain() with adopt_future() for proper async error handling
  - Added on_error callback to heartbeat and rate limit middleware
  - Restructured Tutorial, added Cookbook for advanced patterns

0.001008 - 2025-12-25
  - Fixed 32-bit Perl compatibility in WebSocket test (pack 'Q' → 'NN')
  - Fixed sendfile on non-blocking sockets for FreeBSD/BSD systems
  - Added sendfile documentation and production recommendations
  - Improved test reliability: skip lsof/timing-dependent tests by default
  - Normalized test environment variables to use RELEASE_TESTING

0.001007 - 2025-12-24
  - More broken dist metadata fixes (part 2)

0.001006 - 2025-12-24
  - More broken dist metadata fixes

0.001005 - 2025-12-24
  - trying to get the indexer to ignore my test libs

0.001004 - 2025-12-24
  - More fixes to the code that generates the POD version of the specification
    in improve readability
  - exclude test and example libs from the CPAN indexer.
    
0.001003 - 2025-12-24
  - Fixes to the code that generates the POD version of the specification
    to try and fix UTF8 and other formatting issues.

0.001002 - 2025-12-24
  - Initial release. See README.md for a starting point

0.001001 - 2025-12-17
  - Placeholder release


