diff --git a/docs/releases/v1.0.0-rc.6.md b/docs/releases/v1.0.0-rc.6.md new file mode 100644 index 0000000..fb8f94f --- /dev/null +++ b/docs/releases/v1.0.0-rc.6.md @@ -0,0 +1,100 @@ +## WPToolkit 1.0.0-rc.6 — release candidate + +A rebuild of WPToolkit as a library that other plugins and themes can rely on in production: secure by default, translatable, and safe to bundle next to a plugin built on a different version of it. + +> **This is a release candidate.** It is feature-complete and CI-green, and is published so it can be tried on a real site before `1.0.0`. Expect fixes, not features, between now and the final release. Details: [what an RC is](#what-a-release-candidate-means). + +**Stored data is unchanged.** Meta keys, option keys and value formats are exactly 0.x's, pinned by tests against rows shaped like real 0.x plugins and themes wrote them. Upgrading needs **no data migration**, and rolling back to 0.x stays possible. + +### Changes since rc.5 + +- **New: the packager can show command output as it runs.** Commands run by a build (`npm run build`, Composer, scoping) were silent unless they failed, so a slow asset build looked hung. `Build::verbose()` streams every command's output line by line; `->runVerbose('npm run build')` does it for chosen commands only; `wptoolkit-build package --verbose` and `"verbose": true` in `wptoolkit.json` do the same. The default is unchanged. A command that fails after being streamed repeats only its last 20 lines in the error. + +### Changes in rc.5 (since rc.4) + +- **New: the guard can autoload your plugin's own classes** ([ADR-0025](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/adr/0025-the-guard-can-autoload-the-plugins-own-namespaces.md)). Without Composer, pass `'namespaces' => array('MyPlugin\\' => __DIR__ . '/src')` to the guard and your classes load by PSR-4, with no loader code of your own. This replaces 0.x's `Autoloader::init()`, which 1.0 had dropped without a replacement. A folder that does not exist pauses the plugin with a notice naming it, instead of a "class not found" later. With Composer nothing changes and you do not need it. + +### Changes in rc.4 (since rc.3) + +Two fixes to scoping, both reported by a plugin being migrated to 1.0 ([ADR-0024](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/adr/0024-the-packager-scopes-the-consumers-code-too.md)). **If you package a Composer-based plugin with `Build::scope()`, upgrade: rc.1–rc.3 built a zip that could not load.** + +- **Fixed: `Build::scope()` now scopes your plugin's code and Composer's autoloader, not only the library.** Before, a plugin written against `Codad5\WPToolkit` and packaged with `->composer()->scope()->zip()` kept the old namespace in its own code and in Composer's autoload map, so activating the packaged plugin was a fatal error. In the staged copy only, `scope()` now renames the library, your code's references to it, and the package prefix in Composer's metadata, then regenerates the autoloader. +- **New safety checks in the build.** After scoping a Composer install, the build loads the scoped `Application` in a separate process and fails if it does not load. Verification refuses any package whose bundled copy is scoped while its code still names the unscoped library. A class named in a string is reported (strings are not renamed; use `::class`). `composer()` after `scope()` is refused, because it would undo the scoping. +- **Fixed: `wp {your-slug} make:*` in a plugin with a scoped copy.** Generated files named the unscoped library and could not load. They now use the namespace of the copy that generated them. +- **Tests:** the end-to-end suite now packages the example plugin with `Build::scope()` and runs it on WordPress. + +Known limit: a dependency in `vendor/` that itself requires WPToolkit is not scoped by this step; use Strauss for that. See [scoping and coexistence](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/guides/scoping-and-coexistence.md). + +### Changes in rc.3 (since rc.2) + +- **New: `Settings` can keep an existing option prefix** ([ADR-0023](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/adr/0023-settings-may-keep-a-legacy-option-prefix.md)). 0.x `Settings` took an `option_prefix`; rc.1 and rc.2 always stored settings as `{slug}_{name}`, so a plugin whose saved options use another prefix could only upgrade by renaming its users' options. Pass `optionPrefix: 'acmeforms_'` and those options are read, written and registered where they already are. Without it, nothing changes. + +### Changes in rc.2 (since rc.1) + +- **New: shortcodes** (`Frontend\Shortcodes`, [ADR-0022](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/adr/0022-shortcodes-are-declared-validated-and-lazy.md)). A prefixed tag (or an explicit one a plugin already ships), attributes declared and validated like route args, a required access rule, handlers that return data for a view instead of echoing, and assets that load only on pages where the shortcode renders. A handler that throws is logged and prints nothing. `wp {your-slug} make:shortcode` scaffolds one. +- **New: on-demand assets.** `->onDemand()` on a script or style keeps it out of the normal enqueue hooks; `AssetManager::load()` enqueues it when needed. +- **Tests:** the example plugin gained a shortcode covered end to end as an anonymous visitor; end-to-end setup now retries the first login on a cold WordPress, which had failed CI intermittently. +- **Release process:** release notes are now written per tag (`docs/releases/.md`). + +Nothing in rc.1's API changed; both additions are additive. The option prefix restores something 0.x had, so it belongs before `1.0.0`. + +### Requirements + +PHP 8.1+, WordPress 6.4+. Composer is optional. + +### Install + +Composer (from this repository): + +```bash +composer config repositories.wptoolkit vcs https://github.com/codad5/wptoolkit +composer require codad5/wptoolkit:^1.0@RC +``` + +Without Composer: download `wptoolkit-1.0.0-rc.6.zip` below, verify it against the `.sha256` file, and scope the copy to your own namespace with `php bin/scope.php YourNamespace` — see [scoping and coexistence](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/guides/scoping-and-coexistence.md). + +### What's in it + +| Area | | +| --- | --- | +| **Safe boot** | Your main file goes through a guard that parses on PHP 5.6. On an old PHP or WordPress, or if your plugin throws while starting, the site keeps working and administrators see why. | +| **Coexistence** | Everything put into WordPress's global namespace is prefixed by your slug, and a copy scoped to your namespace can't collide with another plugin's. An incompatible copy refuses to activate and names the plugin that won. | +| **HTTP** | One deny-by-default pipeline serves REST, admin-ajax and public pages. Every route must say who may call it; input is validated before your code runs; rate limits and real HTTP status codes are built in; a JS client calls routes by name. | +| **Data** | Fields with pluggable types and meta boxes; entities stored as post types, custom tables or options behind one repository contract; a query builder with capped pages; search across title, meta and terms; versioned migrations (batched, locked, reversible); admin list columns with quick edit. | +| **Admin and front end** | Settings on the Settings API (sensitive values readable only by name, optionally encrypted), admin pages, per-user notices, shortcodes, PHP views with theme overrides and an escaper, assets enqueued at the right moment (RTL included), pretty front-end URLs. | +| **Tooling** | `wp {your-slug}` commands (`toolkit:info`, `routes:list`, `hooks:list`, `migrate`, `make:entity` …) and `wptoolkit-build` for reproducible, verified release zips. | +| **Dependencies** | None at runtime. PSR-3, PSR-11 and PSR-16 bridges are there if you want them. | + +### Security: findings from 0.x, closed by design + +| 0.x | 1.0 | +| --- | --- | +| REST routes were open by default | Every route needs an access rule; one without it is refused | +| Search was open to anonymous users, with client-chosen meta and no limit | Capability-based visibility, allow-listed fields, capped pages | +| A meta box exposed any post's meta to anonymous users through Ajax | No such endpoint | +| Exception messages were returned to clients | Generic error with a request id; details only in logs | +| `getAll()` on settings could return API keys | Sensitive settings are left out of bulk reads, refused for JavaScript and redacted in logs | +| Assets could be enqueued too early | Declared any time, enqueued only in the enqueue hooks | + +### Breaking changes + +The PHP API changed; the data did not. PHP 8.1 and WordPress 6.4 are required, every route needs an access rule, `Registry::get()` becomes constructor injection, `Model` becomes an entity plus a repository, and `Settings::getAll()` no longer returns secrets. **[Migrating from 0.x](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/guides/migrating-from-0x.md)** maps every 0.x class to its replacement with before/after code. + +Projects that require `dev-main` get 1.0 on their next `composer update`. The last 0.x is **`v0.2.0`** — pin `"codad5/wptoolkit": "0.2.*"` to stay on it. + +### Verified by + +CI on every change — unit tests on PHP 8.1–8.4, static analysis (PHPStan level 8), WordPress coding and security standards — plus integration tests on real WordPress (PHP 8.1 and 8.3 × WordPress 6.4 and latest) and end-to-end tests in a browser: safe boot, two versions of WPToolkit on one site, and a complete example plugin in English and Arabic (right-to-left), by keyboard, with zero notices. + +### Guides + +[Getting started](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/guides/getting-started.md) · [Routes and security](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/guides/routes-and-security.md) · [Data](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/guides/data.md) · [Admin and front end](https://github.com/codad5/wptoolkit/blob/v1.0.0-rc.6/docs/guides/admin-and-front-end.md) · [Todo example](https://github.com/codad5/wptoolkit/tree/v1.0.0-rc.6/examples/todo) + +### Known limits + +- Deferred to 1.1: a generated API reference, an agent-instructions template for new plugins, WordPress Abilities integration, Psalm taint analysis. +- Not ported from 0.x (nothing used them): appearance-menu groups and dashboard widgets. + +### What a release candidate means + +An RC is a version that is believed to be the final release, published so real use can find what tests can't. If it holds up on a real site with `WP_DEBUG_LOG` on, the same code is released as `1.0.0`; if something breaks, the fix ships as `rc.6`. Releases whose tag contains a hyphen are marked "pre-release" on GitHub, and Composer won't install them unless you ask (`@RC`). diff --git a/resources/js/client.js b/resources/js/client.js index 3d3bdba..221274a 100644 --- a/resources/js/client.js +++ b/resources/js/client.js @@ -15,7 +15,7 @@ (function (root) { 'use strict'; - var VERSION = '1.0.0-rc.5'; + var VERSION = '1.0.0-rc.6'; // The shared namespace is locked: no script can replace it; entries stay writable. if (!Object.prototype.hasOwnProperty.call(root, 'wptoolkit')) { diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index 5c52729..8f1b955 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -30,7 +30,7 @@ */ final class Application { - public const VERSION = '1.0.0-rc.5'; + public const VERSION = '1.0.0-rc.6'; private Container $container;