The toolkit follows Semantic Versioning, and the promise worth holding it to
is the one below the top number: nothing but a major release breaks working code. Minor releases add
functionality. A patch release occasionally does too, where the addition closes a gap rather than
opening a new surface — 2.4.1's asset discovery gave hasAssets() the file discovery every other
hasX() already had, and a package that named its entries never notices. Major releases may break.
| Constraint | Gets you |
|---|---|
^2.4 |
the current line, including seeders, factories, stubs and broadcast channels |
^2.0 |
Laravel 12/13, PHP 8.2+ |
~2.0.0 |
the last line supporting Laravel 10 and 11 |
^1.0 |
Laravel 9 |
2.3.0 has been withdrawn
It is no longer available to install, and ^2.3 now resolves to 2.4 or later. Everything 2.3.0
introduced — the asset mirror and the laravel-assets publish tag — is in 2.4 unchanged, so
the minimum supported version is ^2.4. If a lock file still pins 2.3.0,
composer update nyoncode/laravel-package-toolkit is the whole migration.
To 2.4.2
Nothing to do. Two additions and one correction, none of which changes what a package already renders.
The three tag directives take no package name.
@packageAssets, @packageStyles and @packageScripts with no argument render every package that
declared entries. @packageAssets('blog') renders byte for byte what it did, so this is only worth
adopting where the layout is the application's own — that is the line that otherwise has to be
edited every time a package is installed or removed. @packageAssetUrl still takes both arguments.
hasAssetFallback() says where to serve a shipped
file from when nothing is published. Worth declaring if your package already serves its assets from
a route of its own and you support deployments where public/ cannot be written — a read-only
container, Vapor, shared hosting. There, an entry used to render no tag at all, and a page lost its
stylesheet or its behaviour with nothing to say why. Every other package is unaffected: the
resolver is reached only after both the mirror and public/vendor/{short-name} came back empty.
resolution() stops reporting shipped for a copy that can never be written. A mirrored
package — the default — claimed shipped for every entry outright, so an unwritable public/
looked healthy in php artisan about while the page rendered nothing. It now says fallback, or
not published where there is no fallback either. Nothing else moves: an entry the lazy mirror
simply has not reached yet, which is every entry on a fresh install, still reports shipped.
To 2.4.1
Nothing to do. One thing starts working that previously did nothing:
hasAssets() with no entries named now discovers them,
instead of leaving @packageAssets with nothing to render.
A package that already names its entries is untouched — naming any entry replaces discovery
outright. A package that called hasAssets() bare gets the stylesheets and scripts from the asset
directory's root and its css/ and js/ subdirectories, which is what a template asking for
@packageAssets wanted in the first place. The two cases discovery cannot read off a directory
listing still need naming: a code-split build, whose chunk directory is deliberately skipped,
and an IIFE or UMD bundle, since a discovered script is emitted as a module.
To 2.4
Purely additive. Update the constraint and run composer update:
{ "require": { "nyoncode/laravel-package-toolkit": "^2.4" }}
What is new
hasBroadcastChannels() registers channel authorization files with the
broadcaster. If you were passing a channel file to hasRoutes() — the only option before — move it:
$packager ->hasRoutes() ->hasRoutes(['web.php', 'api.php']) ->hasBroadcastChannels(['channels.php']);
The old arrangement kept working, but it loaded the authorization callbacks inside a route group and
put your channel names into the router. Both defaults point at ../routes, so name the files
explicitly or move channels to their own directory.
hasSeeders(), hasFactories() and hasStubs()
add three publish-only resources, with the tags blog::seeders, blog::factories and
blog::stubs.
The install command gained publishSeeders(), publishFactories() and publishStubs(), all
three included in publishEverything().
PublishedAssets::flush() clears the resolved URLs and
per-request sync marks, for applications running the toolkit under a long-lived worker.
To 2.3
Withdrawn
2.3.0 is no longer available to install. This section is kept because both features below shipped
unchanged in 2.4 — read it as "what 2.4 brought along", and target ^2.4.
Additive, with one behaviour change worth knowing about.
Assets now also publish under laravel-assets
In addition to {short-name}::assets. That tag is what the Laravel application skeleton runs from
Composer's post-update-cmd, so your assets are republished after every composer update in an
application that keeps the default script — the same mechanism Horizon and Telescope rely on.
Laravel accumulates publish groups per path, so both tags publish the same files and an untagged
vendor:publish is unaffected. No action needed, but be aware that a consumer's
composer update may now overwrite hand-edited files under public/vendor/{short-name} — that hook
runs with --force.
The asset mirror
Support\PublishedAssets keeps public/vendor/{short-name} in step with
your dist/ directory without anyone running a command. It is registered automatically by
hasAssets(); opt out with:
$packager->hasAssets(mirror: false);
To 2.2
Additive: events, optimize commands and the configurable publish tag separator.
To 2.1.1
A bug-fix release, but several of the fixes changed behaviour that was previously broken — so something in your package may start working, and a workaround you wrote may become redundant.
| Was broken | Now |
|---|---|
hasAssets() stored a path without ../, so the assets tag published nothing |
Publishes correctly. Remove any manual publishes() workaround. |
hasTranslations() rejected the first supported language, and all region locales |
pt_BR, en-US and the rest are accepted |
hasViews() with a custom path failed either relative or absolute |
Both resolve |
routes, view-components, view-component-namespaces tags did nothing |
All three publish. Routes go to routes/vendor/{short-name}/ |
publishMigrations() ignored an explicit file list |
Publishes exactly the files you named |
List-style component arrays got numeric aliases (1, 2) |
Only string keys become aliases |
packageCommands() was never called |
The override is honoured |
The install command called exit(0) on a declined production prompt |
Returns an exit code |
getVersion() threw for a package with no composer name |
Returns null |
Renamed
bootVewComposers() → bootViewComposers(). The misspelling is kept as a deprecated alias; if you
override it, move to the correct spelling.
Deprecated
Contracts\HasAbout will be removed in 3.0. Packable already declares aboutData(), and
ProvidesPackageServices extends Packable, so PackageServiceProvider covers it:
class BlogServiceProvider extends PackageServiceProvider implements HasAbout class BlogServiceProvider extends PackageServiceProvider implements Packable
To 2.1
Laravel 10 and 11 support was removed. The requirement is now Laravel 12 (>= 12.61.1) or 13 (>= 13.12.0).
Both branches had passed security-support end of life, and the June 2026 advisories — including the High-severity CRLF injection CVE-2026-48019 — were patched only in Laravel 12.60+ and 13.9+, never backported. There is no secure release on those branches, so the minimums are pinned to the first patched versions.
{ "require": { "nyoncode/laravel-package-toolkit": "^2.1" }}
No code changes are required. If your package must still support Laravel 10 or 11, stay on
~2.0.0 — and consider what that implies for the applications installing it.
To 2.0 from 1.x
| Change | Action |
|---|---|
| Laravel 9 support dropped | Stay on ^1.0 if you need it |
| Minimum PHP raised to 8.2 | Update your require.php constraint |
| Laravel 13 support added | — |
| Timeless migrations added | Non-breaking; timestamped migrations are unaffected |
{ "require": { "php": "^8.2", "nyoncode/laravel-package-toolkit": "^2.0" }}
Then composer update. No code changes are required unless your package explicitly depends on
Laravel 9 or PHP 8.1.
Timeless migrations, in detail
The addition is non-breaking, but it changes what happens when a migration file has no date prefix. Before 2.0 such a file was published verbatim, which produced a migration Laravel could not order. From 2.0, the toolkit detects the missing prefix and generates one at publish time.
If your package ships migrations with a date prefix, nothing changes. If it shipped prefix-less files and you worked around the ordering by hand, you can drop the workaround.
Compatibility promise
- Major versions may contain breaking changes.
- Minor versions stay backward compatible within a major.
- Patch versions contain bug fixes and security updates only.
Recommended constraint:
{ "require": { "nyoncode/laravel-package-toolkit": "^2.4" }}
Looking ahead to 3.0
Known removals:
Contracts\HasAbout— usePackable.bootVewComposers()— usebootViewComposers().
One rule holds the whole plan for 3.0 together: nothing gets taken away without a replacement that is already in place.