fp/jsformvalidator-bundle
Symfony bundle that adds client-side JavaScript validation to forms. It generates rules from Symfony form constraints, supports AJAX checks, and helps keep front-end and back-end validation consistent while reducing unnecessary server requests.
Architecture fit — this is still not a Laravel package, and 1.8.0 makes that more definitive, not less. fp/jsformvalidator-bundle remains a Symfony Bundle: symfony/framework-bundle is required, it registers a Bundle subclass with a DI extension/compiler pass, extends symfony/form types via FormTypeExtension, and ships a Twig template plus the companion JS runtime (jsformvalidator) that consumes a serialized constraint graph. Laravel has no Form component, no bundle/compiler-pass mechanism, no Twig default, and no FormEvents lifecycle. The 1.8.0 changelog contains nothing that opens a framework-agnostic path — every substantive item (Symfony 8 baseline, PHPUnit 13, Luhn mirroring Symfony's LuhnValidator) deepens the Symfony coupling. The reusable asset is unchanged: the client-side runtime plus the constraint-to-rule JSON contract it consumes.
Integration feasibility. Any Laravel use remains a build, not an install. Path: (1) reverse-engineer the payload schema from the JS runtime and its fixture output; (2) write a Laravel-side serializer mapping FormRequest::rules() / Rule objects / string rules into that schema; (3) wire a Blade/Vite bootstrap. Estimate holds at 3–6 engineer-weeks for a credible MVP (scalars, required, min/max, email, regex, same/confirmed, in) and 2–3 months to production-grade parity. Structural gaps that cannot close client-side remain: closure rules, custom Rule objects, after/prepareForValidation, unique/exists, and sometimes/exclude/bail semantics — those degrade to remote validation or stay server-only, which is where drift risk lives.
Critically, the PHP 8.4+ / Symfony 8 floor in 1.8.0 does not block the donor-codebase plan, because that plan installs only the JS runtime and deletes the PHP. It only blocks the Symfony-estate fallback (below) for orgs not yet on Symfony 8.
Technical risk.
| Risk | Severity | Change since 1.7.0-beta1 |
|---|---|---|
| Beta-quality dependency | Medium ↓ | 1.8.0 is the first stable release; semver line now exists. But zero 1.8.x patch history yet |
| Dormancy recurrence | High — unchanged | One stable release after a revival PR burst (#173–#178); still single-org concentration, no funded-maintainer signal |
| PHP 8.4+ / Symfony 8 floor | High — new | 1.8.0 requires PHP 8.4+, symfony/* ^8.0, Twig 3. Laravel 11/12 run on PHP 8.2+; most estates are on 8.2/8.3 |
| Dual-line maintenance divergence | Medium — new | 1.7 is the line for Symfony 5.4/6.4/7.x; 1.8 for Symfony 8. Two JS runtimes to track — bug fixes may land on one only |
| Thin verification | Medium ↓ | PHPStan clean baseline in CI (#181), enforced PHP + JS line-coverage gates with Cobertura (#182–#184), Cypress browser tests, Dependabot (#185). Still a small absolute suite against a wide rule surface |
| Rule-surface asymmetry | Medium — new | New Luhn constraint (#212, #213) has no first-party Laravel counterpart — inverse drift: client rejects, server accepts |
| Version skew | Medium | CI now validates PHP 8.4/8.5/8.6 only, widening the gap from a PHP 8.2/8.3 target |
| Adoption signal | Medium — unchanged | 125 stars, 0 dependents; still no evidence of production use anywhere |
| Metadata anomaly | Low ↓ | A concrete 1.8.0 artifact now exists against the previously future-dated 1.7 date; cadence still unproven but the anomaly is less material |
Key questions.
Luhn ship a documented payload shape, or only a PHP-side serializer emitting it?)Luhn change the payload schema in a backward-incompatible way?Stack fit — still a conditional no, with a narrower fallback. If the target is genuinely Laravel, evaluate this as a donor codebase, not a dependency. Name the alternatives up front so build-vs-adopt is honest: proengsoft/laravel-jsvalidation (direct analog, largely unmaintained, PHP 8 friction — likely why this scored as an "opportunity"), Laravel Precognition for Inertia/Vue, and Livewire/Blade component validation for reactive cases.
The Symfony-in-estate escape hatch is now narrower: adopting 1.8.0 requires Symfony 8 and PHP 8.4+. An org on Symfony 6.4/7.x must pin the 1.7 line — which is explicitly the documented path — and that line is now legacy, so the low-effort-install calculus only holds for estates already mid-migration to Symfony 8.
Migration path — gated spike, version-pinned. Run a 5-day, timeboxed spike with an explicit kill criterion: stand up the 1.8.0 JS runtime alone, feed it a hand-authored JSON payload, and confirm it renders rules and validates against a plain HTML form with no PHP backend present. If it fails, stop — the package is not separable. If it succeeds, the deliverable is a documented schema contract plus a one-page rule-mapping table, which becomes the spec for the Laravel serializer. Pin the runtime version explicitly and record it — 1.8.0 adds Luhn, so 1.7 and 1.8 payloads are not interchangeable. Vendor the JS runtime and write a fresh PHP serializer; forking the bundle remains the wrong bet, since its PHP is the part you delete and the part now carrying the Symfony 8 / PHP 8.4 constraint.
Compatibility. Four seams, one of them now harder:
Sequencing.
Luhn payload shape confirmed.Luhn flagged as client-only (no Laravel counterpart → either add a server rule or accept one-way validation). Exit: golden-file tests comparing generated payloads to expected output.unique/exists, harden CSP/XSS.Maintenance — you still own it, but upstream is now a better upstream. The 1.8.0 quality investment is real and should be credited: PHPStan in CI with a clean baseline, enforced PHP and JS coverage gates with Cobertura uploads, Cypress browser tests, Dependabot across Composer/npm/Actions, and a Nix dev environment. That materially reduces the odds of silent regression in the JS runtime — the part we actually vendor. It does not reduce our work: the serializer, the parity harness, and the Blade/Vite bootstrap are ours regardless. Revised budget: ~15% of one engineer standing (down from ~20%), plus a spike whenever Laravel adds or changes a validation rule. Pin the exact 1.8.x version; semver discipline is now plausible but unproven at patch level.
Support. Still no SLA, no vendor, and no evident security-disclosure process — a stable version string does not create an escalation path. The 1.7 branch remains the only option for Symfony 6.4/7.x users, so support questions will be split across two lines with unclear backport policy. Community-only through a repo whose history includes an abandonment cycle; acceptable for a browser-side runtime, not acceptable for anything on the server-side authority path.
Scaling. Server-side cost stays near zero — validation runs in the browser, which is the real value proposition. The exception is remote validation for rules with no client equivalent (unique, exists, closures, and now any org-specific checksum logic if Luhn is not adopted). At scale that becomes per-keystroke request fanout: it needs debouncing, AbortController cancellation, caching of repeated values, and per-endpoint rate limiting, or it shows up as a p95 regression on the exact endpoints already doing DB writes.
Failure modes.
| Failure | Impact | Mitigation |
|---|---|---|
| Rule drift (client passes, server rejects) | Erodes trust in inline validation; support tickets | Parity harness in CI; server is sole authority |
Inverse drift (Luhn, client-only rules) |
Client rejects valid input; user cannot submit; no server error to explain it | Explicit "client-only rule" register; either add a server-side rule or a documented exception |
| Unknown rule serializes to nothing | Fails open — user believes field is validated | Explicit whitelist + noisy CI failure on unmapped rules |
| Payload parse error | Silent no-op; entire form unvalidated client-side | Fail loudly in console + telemetry counter |
| Remote validation storm | p95 latency, DB load | Debounce, cancel, cache |
| Unescaped message interpolation | XSS | Escape at injection; audit before pilot |
| Asset version skew (serializer vs JS runtime) | Stale or wrong rules in prod | Version field in payload; pin 1.8.x; deploy atomically via Vite |
| 1.7/1.8 line divergence | A runtime bug fixed on the line you don't use | Pin the line in Phase 0; track upstream branches explicitly |
Ramp-up. Two to four weeks for a senior full-stack engineer to net-productivity, and the profile remains narrow: Symfony Form internals literacy plus JS runtime internals plus Laravel validation internals — a bus factor of one in most teams. The 1.8.0 work does not change this; if anything, the two-line split adds a small amount of upstream-tracking overhead. Document the schema contract in-repo in week one, pair a second engineer onto Phase 2, and if no one on staff has Symfony Form experience, add two weeks and treat it as a hiring/contracting decision rather than a schedule slip.
How can I help you explore Laravel packages today?