Skip to content

Releases: harryneufeld/webspine

webspine 0.1.27

Choose a tag to compare

@harryneufeld harryneufeld released this 15 Oct 21:03
038f06c

After-response lifecycle

Plugins can register short, best-effort work through $app->hooks->on('response.sent', ...). The public entry point now uses App::send() to dispatch the event once per managed emission, including HEAD, handled HTTP errors, and generic handler-failure responses. Resolves #69; implemented in #97.

  • Listeners receive a read-only ResponseSent context containing the original request, status, declared/default headers, HEAD flag, and FastCGI completion result. App::handle() alone, health rendering, and direct Response::send() do not fire the event.
  • Sessions close before hooks. The shared framework update lock remains held while PHP code executes, so activation cannot replace files in use.
  • FastCGI completion is opt-in through response.finish_fastcgi; unsupported runtimes use portable flushing. After successful FastCGI completion, the sender temporarily enables ignore_user_abort during hooks so flushing a finished connection cannot terminate dispatch, then restores the previous setting.
  • Hook output is discarded. Exceptions and reported warnings/notices receive private diagnostics without replacing the completed response or retrying work. Reentrant emission is rejected.
  • A configurable soft total dispatch budget defaults to 100 ms (response.hook_budget_ms, integer 1–1000). Late callbacks are logged and later listeners are skipped. This cannot interrupt blocking PHP or guarantee a wall-clock limit.

This hook does not guarantee browser receipt, background execution, or durable completion. Startup failures, early exits, and process termination can bypass it. Persist required business work before responding and use the existing durable queue/cron worker with idempotence and retries.

See core/docs/response-lifecycle.md for examples, context fields, server behavior, errors, session/lock timing, and execution limits.

Compatibility and installation

PHP 8.3+ and framework API 1 remain unchanged. No database migration is required. Custom entry points must use App::send($response, $request) to dispatch App lifecycle hooks; direct response sending remains available.

Existing websites should apply webspine-core-0.1.27.zip through the CLI updater after making a verified backup. Use webspine-0.1.27.zip to create a new website. Each ZIP is accompanied by a SHA-256 JSON sidecar; checksums verify integrity, not release authenticity. Signature verification is not implemented.

Validation

  • Framework/Starter, version, and real Apache/PHP-FPM/nginx/CloudPanel hosting CI passed on the PR, including update/rollback and the finished-connection flush regression.
  • Merged-commit hosting CI passed.
  • Release inventories, source bytes, vendor checksums, ZIP hashes, and all four publication assets verified against the clean merged checkout.
  • Fresh packaged installation/health, 23 packaged lifecycle checks, 15 packaged Starter checks, and updater staging verified using disposable local data.

webspine 0.1.26

Choose a tag to compare

@harryneufeld harryneufeld released this 15 Oct 20:28
96c7cd2

Component child content

Cards, modals, and other wrapper components can now receive caller-rendered child content through an optional children: callback on $ui->component() and $app->theme->component(). The wrapper renders the resulting $children string without passing HTML through an ordinary prop. Resolves #65; implemented in #96.

  • Callbacks run once before the wrapper. Buffered markup comes first, followed by an optional returned HTML string. Reusing $children reuses that string without rerunning the callback.
  • Omitted or empty children produce an empty string. Invalid return types and callback failures throw explicitly and restore renderer-owned output buffers. Nested slots use the existing 64-component nesting limit.
  • Components keep explicit props and isolated scopes. Props named children or ui remain accessible in $props; they cannot replace renderer locals. The presentation helper retains its three methods, and existing two-argument component calls remain compatible.
  • Slots compose trusted theme markup; they do not sanitize content. Escape plain-text content and URLs with e(). Named slots are not included.

See core/docs/site.md, under "Caller-supplied child content", for a card example and callback semantics.

Compatibility and installation

PHP 8.3+ and framework API 1 remain unchanged. No database migration is required. Existing websites should apply webspine-core-0.1.26.zip through the CLI updater after making a verified backup; webspine-0.1.26.zip is the bootstrap for new websites. Each ZIP includes a SHA-256 JSON sidecar. Checksums verify integrity, not release authenticity; signature verification is not implemented.

Validation

  • 304 framework checks and 15 Starter checks passed for the PR; all PR CI checks passed.
  • Merged-commit hosting CI passed.
  • Release ZIPs, inventories, vendor checksums, source bytes, and both SHA-256 sidecars verified against the clean merged checkout.
  • Fresh packaged installation/health, child-content composition, and core updater staging verified with disposable local data.

webspine 0.1.25

Choose a tag to compare

@harryneufeld harryneufeld released this 10 Oct 22:13
90c8477

Makes Content Security Policy customization discoverable and reliable: explicit response headers now always override the defaults, regardless of header-name case.

  • Fixes default header merging in Response::send(). Defaults were merged case-sensitively, while PHP replaces headers case-insensitively, so a response setting content-security-policy in lowercase was silently replaced by the default policy. Defaults are now added only for header names the response has not set in any case.
  • Adds Response::DEFAULT_HEADERS and Response::emittedHeaders(), which returns the exact headers send() emits, for site and plugin tests.
  • Documents the default security headers and policy, and a narrowly scoped override for a single embed (frame-src for one origin on one route), with guidance against wildcards, 'unsafe-inline'/'unsafe-eval' or removing the header, and on the site's privacy and consent responsibilities.
  • Adds a hint to the site-owned site/tests/run.php: session-based checks such as the contact example belong in a subprocess or HTTP fixture, not after printed progress. The isolation, diagnostic and regression check were already provided by #75.

Responses that already set headers with the canonical names behave as before. Emergency 503 pages keep the default policy. Core updates preserve site/tests/run.php; adopt the new comment manually if wanted.

Requires PHP 8.3+; framework API remains 1.

Validation: 294 framework checks, 15 starter checks, version policy and CI passed. Verified release archives; a fresh packaged installation passed health, starter checks and HTTP rendering with default security headers; the documented embed override was exercised on a fresh site; a 0.1.24 site updated to 0.1.25 through the CLI updater and passed health and starter checks.

Includes #94; resolves #47 and #60.

For existing websites, use webspine-core-0.1.25.zip through the CLI updater. Use webspine-0.1.25.zip only for a new website. Both ZIPs have SHA-256 sidecars.

webspine 0.1.24

Choose a tag to compare

@harryneufeld harryneufeld released this 10 Oct 21:56
ea5036a

Makes getting started clearer: start with your AI or by hand, and set up PHP, including on Windows.

  • Splits the README's getting-started section into With your AI, with a copyable prompt, and By hand, with the release download, PHP requirements and install commands.
  • Clarifies that pdo_sqlite is required, while zip (core updates and release tooling) and openssl (SMTP) are optional.
  • Adds a PHP setup guide to core/docs/deployment.md: extension overview, php -v / php --ini / php -m diagnostics, what Loaded Configuration File: (none) means, development vs. production ini templates, extension_dir, and restarting the serving PHP process.
  • Documents the Windows winget package PHP.PHP.8.4 8.4.25, which ships without an active php.ini and therefore without pdo_sqlite, and how to enable the needed extensions.

Documentation only; no framework behavior changes. Requires PHP 8.3+; framework API remains 1. Core updates replace the README and framework docs; site files are preserved.

Validation: 292 framework checks, 15 starter checks, version policy and CI passed. Verified release archives and a fresh packaged installation passed health, starter checks and page rendering. Winget PHP instructions were checked against PHP 8.4.25 with and without php.ini.

Includes #93; resolves #63.

For existing websites, use webspine-core-0.1.24.zip through the CLI updater. Use webspine-0.1.24.zip only for a new website. Both ZIPs have SHA-256 sidecars.

webspine 0.1.23

Choose a tag to compare

@harryneufeld harryneufeld released this 10 Oct 20:17
a375310

Sites can declare their page language and supply safe, localized visitor error copy, including when configuration, bootstrap, templates or database setup fail. Agents may now handle production deployment when the user authorizes it.

  • Adds a validated language in site/meta.php (BCP 47 subset, default en) and an escaped Starter <html lang> attribute.
  • Adds optional, bounded, data-only site/errors.json for localized framework HTTP errors (400–599) and emergency 503 pages. Status, Allow, cache and security headers, private diagnostics and empty HEAD responses are preserved; site 404 pages and health JSON are unchanged.
  • Updates AGENTS.md and the deployment guide: an agent may carry out authorized production deployment and framework updates, with local verification, verified backups, a rollback path and post-deployment checks.
  • Summarizes webspine's benefits in the README.

Language declares the page language only; it does not translate content, create alternate routes or emit hreflang links. Existing sites keep English defaults. Core updates preserve site themes, metadata, site/errors.json and the site's own AGENTS.md: adopt the layout lang change and the revised deployment instructions explicitly.

Requires PHP 8.3+; framework API remains 1. 0.1.22 was not published separately; its changes ship in this release.

Validation: 292 framework checks, 15 starter checks, version policy and CI passed. Verified release archives and a fresh packaged installation passed health, starter checks and page rendering. Hosting checks cover Apache, nginx/PHP-FPM and CloudPanel errors across activation and rollback.

Includes #91 and #92; resolves #56.

For existing websites, use webspine-core-0.1.23.zip through the CLI updater. Use webspine-0.1.23.zip only for a new website. Both ZIPs have SHA-256 sidecars.

webspine 0.1.21

Choose a tag to compare

@harryneufeld harryneufeld released this 08 Oct 20:56
7c117ac

Simplifies page route definitions and canonical redirects, and permits digits in safe template names.

  • Adds Response::redirect for validated fixed local or HTTP(S) destinations with 301, 302, 303, 307 and 308 statuses.
  • Adds optional path/slash page declarations with preserve, strip and append policies. GET/HEAD slash aliases use 308 and preserve the raw query suffix; root stays stable and POST is never silently rewritten into GET.
  • Detects conflicts between page paths, generated aliases, exact GET routes and matching GET patterns.
  • Accepts digit-containing template names such as section2 while preserving previously valid leading-hyphen identifiers and existing safety diagnostics.
  • Documents fixed WordPress query/permalink migration maps, redirect method semantics and adoption.

The new starter declares its unchanged /, /about and /contact URLs in pages.php. Customized site files are preserved by core updates. Existing sites without path declarations keep their manual routing. Declaring path enables automatic registration: remove equivalent manual GET mappings to avoid conflicts. Slash policies apply only to explicitly declared pages; unrelated custom/pattern routes and database pages remain explicit. This does not generate SEO canonical tags or multilingual URLs.

Requires PHP 8.3+; framework API remains 1. Starter tests now disable persistent asset caching to keep their data disposable.

Validation: 257 framework checks, 14 starter checks, PHP lint and CI passed. Verified release archives and a fresh packaged installation passed health, page-path rendering, query-preserving GET/HEAD redirects, method boundaries and digit templates. Hosting checks cover redirects/security headers through Apache, nginx/PHP-FPM and CloudPanel across activation and rollback.

Includes #90; resolves #55 and #61.

For existing websites, use webspine-core-0.1.21.zip through the CLI updater. Use webspine-0.1.21.zip only for a new website. Both ZIPs have SHA-256 sidecars.

webspine 0.1.20

Choose a tag to compare

@harryneufeld harryneufeld released this 08 Oct 20:25
f66aa52

Improves theme asset caching and adds optional diagnostics for stale manual rendering lifecycles.

  • Reuses asset URL hashes across requests in a bounded private cache (256 entries, ten-minute expiry), scoped to the site and framework version.
  • Adds SHA-256 ETags and If-None-Match support for GET/HEAD. Matching responses return 304 without body bytes, preserving the existing one-hour freshness policy and security headers.
  • Adds opt-in theme.debug checks for changed theme selection, manifests/dependencies and observed files at outer rendering boundaries, with an actionable refresh diagnostic.
  • Documents invalidation, read-only fallback, manual export workflows and performance measurements.

Asset hash caching defaults on and can be disabled with theme.asset_hash_cache=false. Explicit $app->theme->refresh() clears cached hashes; normal requests and health retain them. Metadata-preserving edits require explicit invalidation and draining concurrent rendering workers. Debug defaults off, uses bounded checks, and cannot detect every metadata-preserving edit or reload executed PHP.

HTTP ETags always describe actual response bytes independently of the URL hash cache. Conditional delivery reduces transfer size but still reads/hashes full files; static publishing, streaming and server offloading remain separate work. Assets remain site-owned and core updates do not rewrite them.

Requires PHP 8.3+; framework API remains 1. Existing helper calls remain compatible.

Validation: 239 local framework checks, 14 starter checks, PHP lint and CI passed. Verified archives and a fresh packaged installation passed health, rendering, cross-request hash reuse, GET/HEAD 304 and debug diagnostics. The 260 KiB fixture hashes 260 KiB once across 50 lifecycles (147 hits), versus about 13 MiB previously; 50 conditional image responses reduce body bytes from 9,830,400 to zero. These are fixture measurements, not latency guarantees.

Includes #89; resolves #45 and #66.

For existing websites, use webspine-core-0.1.20.zip through the CLI updater. Use webspine-0.1.20.zip only for a new website. Both ZIPs have SHA-256 sidecars.

webspine 0.1.19

Choose a tag to compare

@harryneufeld harryneufeld released this 08 Oct 19:53
0cc8743

Adds provider-neutral entity queries for workflows such as listing open requests, without paging through all records in PHP.

  • Typed equality, comparisons, IN and null predicates, with bounded AND/OR groups.
  • Stable sorting, bounded pagination and filtered counts.
  • Optional SQLite index declarations, created only by the explicit entities:install command.
  • Strict validation, parameterized values and full-string matching across SQLite versions, including embedded NUL bytes.
  • Documentation and integration coverage for provider compatibility, indexing, larger datasets, and activation/rollback preservation.

The existing Entities CRUD contract is unchanged; callers check the optional EntityQueries and EntityIndexes capabilities. Existing databases and records are preserved. Core updates do not install indexes or migrate data: declare indexes in your plugin and run entities:install explicitly. Broad filters, unmatched sorts and large offsets can still require substantial database work.

Requires PHP 8.3+; framework API remains 1. Queries and indexes require SQLite JSON functions.

Validation: 208 local framework checks, 14 starter checks, PHP lint, and GitHub CI passed. Verified release archives and a fresh packaged installation passed health, rendering, index and query checks. In the 20,000-record status fixture, a matching index selects and verifies 20 candidates rather than scanning 20,000; this is a fixture measurement, not a timing guarantee.

Includes #88; resolves #67.

For existing websites, use webspine-core-0.1.19.zip through the CLI updater. Use webspine-0.1.19.zip only for a new website. Each ZIP includes its SHA-256 sidecar.

webspine 0.1.18

Choose a tag to compare

@harryneufeld harryneufeld released this 08 Oct 19:20
a9592d1

Release packaging now rejects CRLF and bare-CR project text and verifies the completed temporary archive before finalizing the ZIP. Source bytes remain unchanged and inventory hashes describe those exact bytes. Upstream vendor files and binary assets retain their original bytes/checksums; repository-owned core/vendor/dependencies.json now has a targeted LF rule.

New core/bin/release.php build/verify/verify-set tooling checks metadata, required files, exact portable inventories, links/duplicates/size limits, file hashes, project text endings, recorded vendor checksums and ZIP sidecars. Archive verification treats PHP as data and never executes it. Core/bootstrap pairs must agree on their managed framework files.

The checked-in stable publisher selects exactly four versioned assets, compares archives against a clean merged checkout, verifies committed version/current official main/CI, and rejects existing tags/releases. It creates a draft, verifies upload SHA-256 digests and exact asset names, then publishes and verifies the latest release/tag target. Failed checks or uploads leave publication stopped. Offline publisher tests exercise exact asset selection and failed digest/CI/tag gates in PR CI without network calls. See the release workflow.

The updater's existing compatibility/staging policy and API 1 remain unchanged; no database migration is required. ZIP timestamps/compression can vary across builds, so byte-identical archives are not guaranteed. Git/PowerShell are maintainer-tool dependencies, not website runtime requirements. Hashes verify integrity, not authenticity; signatures are not implemented.

Validation: the merged tree matches the tested PR source, where 177 framework checks and 14 Starter checks passed. Linux framework/Starter/offline-publisher CI and PR/post-merge hosting CI passed. Both final ZIPs and sidecars passed source/pair/hash/text/vendor verification; fresh packaged installation, health, 14 Starter checks and updater staging from 0.1.17 passed. Fixtures cover mixed endings, vendor/binary preservation, tampering, unrelated old archives, inconsistent pairs, archived PHP non-execution and publisher failure gates. The console packaging fixture now includes public/, and independent queue test clocks begin after enqueue to avoid a second-boundary race. No production services or data were used.

New website: webspine-0.1.18.zip.

Existing website: webspine-core-0.1.18.zip, through the CLI updater on a local copy after a verified backup. Never overlay the bootstrap onto a customized site.

PHP 8.3+; API 1. Changes: #87, closes #62. Released from merged commit a9592d19247e7baa0f9c53a6ff44f0cf3463530c.

webspine 0.1.17

Choose a tag to compare

@harryneufeld harryneufeld released this 08 Oct 16:05
a227213

Enabled plugins can now register namespaced commands through the main CLI without editing core dispatch for each feature. The CLI-only cli.register hook receives a per-run ConsoleCommands registry with sorted discovery, per-command help, bounded positional arguments, and integer exit status propagation. Duplicate names, core collisions, invalid definitions and invalid return statuses fail clearly.

The bundled queue now offers job-queue:install, job-queue:status, job-queue:health, job-queue:work, job-queue:retry <job-id> and job-queue:prune [days] through core/bin/console.php. Its existing CLI script remains supported and uses the same handlers. Queue JSON, health warning status, explicit installation, ten-job worker bounds and completed-job retention remain unchanged. Surplus queue arguments now fail instead of being silently ignored.

Use php core/bin/console.php help to discover enabled commands, or help <command> for usage. help --core provides built-in help without loading site configuration/plugins. Plugin discovery and execution hold the framework's shared update lock and refuse interrupted activation before booting the App. Core maintenance commands retain their existing bootstrap; plugins cannot replace update or recovery commands. Commands needing no installed storage can run before installation if normal plugin registration can boot without it. Discovery does not install or migrate storage. Documentation includes plugin examples and absolute-path cron invocation.

This is a trusted-plugin extension API, not a scheduler or sandbox. Handlers own business rules, value validation, finite work bounds and any additional locks; concurrent plugin commands can still run together. Existing site-owned scripts remain valid. No database migration is required.

Validation: the merged tree matches the tested PR source, where 176 framework checks and 14 Starter checks passed. PR CI and post-merge hosting CI passed. Disposable CLI fixtures cover discovery, disabled plugins, arguments, exit codes, exceptions, collisions, locking, maintenance isolation, interrupted updates and unified/legacy queue compatibility. Both ZIP inventories, file hashes and SHA-256 sidecars were verified; fresh packaged installation, health, CLI help, 14 Starter checks and updater staging from 0.1.16 passed. No production services or data were used.

New website: webspine-0.1.17.zip.

Existing website: webspine-core-0.1.17.zip, through the CLI updater on a local copy after a verified backup. Never overlay the bootstrap onto a customized site. SHA-256 checks integrity, not authenticity.

PHP 8.3+; API 1. Changes: #86, closes #68. Released from merged commit a2272139e79462331c0b9b5c3006defab985ea5d.