indexnowkit/history
A Laravel package for adding audit-style history to Eloquent models. Track creates, updates, and deletes, persist snapshots or changed fields, and review a clear timeline of record activity with simple, framework-friendly APIs. Ideal for lightweight auditing and accountability without a heavyweight...
Architecture fit. This release continues the family's core/adapters split cleanly: framework-agnostic console classes (Console\HistoryCommand, Console\StatusCommand) now live in this package over HistoryRunner/StatusRunner, with each adapter wiring the runner and supplying its own debounce-store lookup via HistoryServices::describeStore(). That is a good fit for Laravel, whose command layer is symfony/console-based and where a service provider can register #[AsCommand] classes directly. It removes three duplicated implementations (Laravel, Yii2, Yii3) of the store-description line, which lowers drift risk across adapters.
Integration feasibility. Moderate. The package is pre-1.0 and the family's versioning rule treats new public classes as minor bumps, so 0.4.0 is additive by intent, not a patch. Two hard requirements change the install surface: ext-mbstring is now declared (previously used undeclared) and indexnowkit/core ^0.13 is required. Commands additionally need indexnowkit/console ^0.5, declared as require-dev + suggest, so a Laravel app that wants indexnow:history/indexnow:status must add that dependency explicitly.
Technical risk.
HistoryServices::package() now delegates to Adapter\OptionalPackage::history(); same object and texts, but adapters are told to call the core class directly. Low functional risk, but it is an API-direction change that will surface as a soft deprecation in adapter code.describeStore(?string $store, string $default, Closure $lookup) changes how the status line is produced. Adapters that built the <id> (<ShortClass>) line themselves must now pass a lookup closure; a throwing or null-resolving lookup silently degrades to <id> (missing), which is user-visible output.Pdo\Schema::table() addition is a genuine hardening (Psalm taint-escape boundary between history.pdo.table and CREATE TABLE/INSERT), but it means table-name handling changed in the SQL path — worth a targeted regression check on non-default table names.HistoryCommand::DEFAULT_LIMIT (50) defines fallback behaviour for a non-numeric --limit; document it so support tickets don't treat it as a bug.indexnowkit/console, adapters register HistoryNotInstalledCommand/StatusNotInstalledCommand under the same command names — a deliberate, discoverable failure mode rather than a missing-command error.Key questions for the maintainer.
indexnowkit/core ^0.13 and this release, and is that pin released concurrently?HistoryServices::package() formally deprecated, and on what timeline?ext-mbstring enforced via composer.json platform config or only discovered at runtime?(missing) and NotInstalled outputs covered by contract tests we can rely on for support scripts?Stack fit. Laravel 9–11 on PHP 8.1+ with symfony/console already present. The new command classes slot into Laravel's command registration without adaptation; the runner pattern keeps the framework binding thin (provider builds historyRunner()/statusRunner(), the command stays portable). No new runtime services, queues, or storage backends are introduced.
Migration path.
composer require indexnowkit/core:^0.13 transitively; confirm the Laravel adapter release that allows it.ext-mbstring to the app's composer platform requirements and to CI images — this is the most likely silent breakage in slim containers.indexnowkit/console:^0.5 only if the app exposes indexnow:history / indexnow:status; otherwise the NotInstalled stubs remain and are harmless.HistoryServices::package() calls with OptionalPackage::history() in any app-side or custom-adapter code.describeStore() and pass the lookup closure.Compatibility. Additive at the class level; the only behaviour-visible changes are the status line's (missing) rendering, the --limit fallback, and the SQL identifier handling. Because the family uses minor versions for new public classes, treat 0.4.0 as "review the diff" rather than "bump and forget," even though no removals are listed.
Sequencing. Do the composer/platform work first (core pin, mbstring, optional console), then deploy, then verify command registration and status output in a staging environment before switching any dashboards or support runbooks over to the new describeStore format. Adapter-side package() cleanup can follow in a separate, low-risk change.
Maintenance. Net positive: three adapter-side duplicates collapse into one core implementation, and the SQL identifier check is centralized where taint analysis can see it. The cost is two more public classes and a *For() twin pair to keep stable across releases — small but permanent support surface in a pre-1.0 package.
Support. Expect two new ticket classes: "command says not installed" (missing indexnowkit/console) and "status shows (missing)" (lookup closure resolving nothing or throwing). Both need one-line explanations in the runbook. The --limit fallback to 50 should be documented alongside them.
Scaling. The commands are read-path only and bounded by DEFAULT_LIMIT; no scaling concerns. describeStore() is a pure formatting call with a closure lookup, so cost is whatever the adapter's lookup does — in Laravel that is a container/config resolve, negligible. The PDO store's CREATE TABLE/INSERT path is unchanged in shape.
Failure modes.
ext-mbstring → hard failure in RecordCodec when truncating an error text; likely to appear only on long error messages, i.e. in production, not in tests. Highest-priority pre-flight check.(missing) rather than an exception; failures become cosmetic and may be missed by monitoring. Consider asserting on the status output in a smoke test.^0.13 vs older) → class-not-found on OptionalPackage::history(); composer resolution should catch it, but pinned apps may not.history.pdo.table values → re-verify after the Schema::table() change.Ramp-up. Low for application teams: the commands behave as before, and the migration is a composer bump plus an optional dependency. The one concept to teach is the adapter/runner split — the adapter builds the runner and supplies the lookup, the package owns the command and the output format. Half a day for a Laravel developer, mostly spent confirming command registration and status output in staging.
How can I help you explore Laravel packages today?