wehaa/custom-links
wehaa/custom-links is a Laravel package for managing customizable links in your application, with configurable labels, URLs, and ordering to simplify navigation and resource link setup.
Architecture fit. The package occupies a deliberately narrow slot: it extends the Laravel Nova sidebar with operator-defined links and, as of the only feature release (v0.1.1, September 2019), per-link authorization. It is not infrastructure — it is an admin-panel presentation layer. That makes architectural risk low in blast radius and equally low in strategic value. Two things must be reconciled before any decision: the marketplace description markets this as a model-slug / URL-resolution package ("attach custom slugs to any model, resolve incoming links to their target models"), while the hands-on evaluation describes a thin Nova sidebar component with a config-driven link registry and a compiled Vue asset. Those are different products with different risk profiles. A TPM should treat the description as unreliable until the source under src/ and the published config confirm which one is actually shipped. If it is genuinely a slug/resolver package, the integration surface expands to routing, middleware, and 301/404 semantics — a materially larger commitment.
Integration feasibility. Feasible only under a tight set of conditions, all verifiable in an afternoon: Composer constraints resolve against your PHP and laravel/nova versions; the compiled asset mounts on your Nova major; the authorization check runs server-side and fails closed. The realistic fit is Nova 1.x/2.x era. Nova 4/5 uses Vite and Vue 3 while this asset is a 2019-era Webpack/Vue 2 build, so the expected outcome on a modern Nova install is a non-mounting component or a silent no-op. Installation is a composer require plus a provider publish — under an hour if constraints cooperate, and a hard stop if they do not.
Technical risk. Ranked by likelihood × impact:
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Client-side-only authorization; URL present in Nova bootstrap JSON | Medium | High (security theater in an admin panel) | Read compiled Vue + grep page payload before sign-off |
Fail-open default for links with no can callback |
Medium | High | Explicit test with low-privilege user; document default in config |
| Nova 4/5 asset incompatibility | High | Medium (feature simply absent) | Verify on a staging Nova instance first |
Closures in config break config:cache |
High if attempted | Medium (deploy failure) | Invokable class strings or register at boot |
| Dormant upstream, 0 dependents, pre-1.0 semver, no visible CI | Certain | Medium | Pin 0.1.1, commit lock file, budget for fork |
| Description/implementation mismatch | Medium | Medium (scope surprise) | Source review as a gate, not a formality |
The governing risk is not technical failure — it is false confidence. A package that never checked permissions was obviously unsafe; one that checks them cosmetically is unsafe in a way that reads as safe in a review. That asymmetry is what raises the bar rather than lowering it.
Key questions.
can callback: visible or hidden?php, illuminate/support, and laravel/nova constraints, and which Nova major are we on?NovaServiceProvider::mainMenu() with MenuItem::make(...)->canSee(...)?That last question is the decision. The native path is typically under an hour and carries zero dependency. If native covers the requirement — and for a handful of static or semi-static links it does — the package has to justify itself on something other than effort.
Stack fit. Laravel + Nova, admin surface only, authenticated internal users. This is a convenience dependency, not a platform one. It should never appear in a customer-facing path, a billing decision, or anything where a wrong visibility call has financial or compliance consequence. If the roadmap includes consolidating internal systems behind one admin entry point, per-link visibility becomes a prerequisite and this becomes roadmap-relevant; if it does not, this is cosmetic tooling and should be governed as such.
Migration path. Greenfield or additive only. There is no meaningful migration from an existing custom sidebar — the code volume is small enough that a fork is cheaper than an adapter. Recommended shape: run the package concurrently with NovaServiceProvider::mainMenu() during a short trial window, keep genuinely static links in the package, and route anything needing conditional logic, counters, submenus, or localization to native MenuSection/MenuItem. That contains exposure to the simplest case and makes removal trivial.
Compatibility. Treat Nova major as the hard gate and gate it first. Confirm against composer.json before reading config docs. Pin exactly — "wehaa/custom-links": "0.1.1", not ^0.1 — because pre-1.0 semver means a patch release can change the authorization API without notice and the release history suggests the config contract is not treated as stable. Commit the lock file. If the constraint set forces a Composer resolution hack on Nova 4/5, stop; the correct answer is native menu, not a constraint override.
Sequencing.
composer.json, src/, the published config, the compiled JS, and git diff v0.1.0 v0.1.1. Answer the six key questions. Kill the initiative here if authorization is client-side.canSee. This is the control group; it establishes the real cost of not adopting.Ctrl+F the URL. Not display:none — absent.config:cache in a production-like environment with the real config file. This is where closure-in-config failures surface.Budget: roughly 1.5–2 engineer-days to a go/no-go, plus a two-week observation window. Nothing about this warrants a longer runway.
Maintenance. Effectively unmaintained upstream: last release 2019, twelve stars, zero dependents, no visible CI or test suite. Assume you own the code from day one regardless of what the license says. The MIT license and small surface make a fork cheap — copy into packages/ as a path repository and treat it as first-party. Adopt-with-fork is the honest framing; adopt-as-dependency is a fiction. Budget a nominal half-day per Nova upgrade cycle for compatibility triage, and expect at least one forced fork at the next Nova major.
Support. No upstream support channel worth relying on. Internal ownership means internal documentation: which links exist, who owns each, what gate governs visibility, and what the fail-open/fail-closed default is. Document the default in a comment in the config file itself — that is the artifact a future engineer will actually read. Build the regression net now: one feature test per authorization branch asserting both the label and the target URL are absent for unauthorized users. That test suite is the support contract.
Scaling. Scales fine along the axis it was designed for — a handful of static or semi-static links. Degrades sharply outside it: dynamic link generation, per-tenant link sets, submenus, localization, or click analytics are all out of scope and turn this into a fork-and-own project. If the requirement grows along any of those axes, the correct move is to migrate to native MenuSection/MenuItem rather than extend the package. Treat "we need a loop or a DB query to build the link list" as the explicit trigger to re-evaluate.
Failure modes. Ranked by how they present:
config:clear → composer show → browser console → inspect page payload → dump() in the callback → provider boot().config:cache. Loud, therefore manageable — but it fails at deploy, not in review.order values sort inconsistently between PHP and JS. Cosmetic, but use unique integers with gaps of ten and it never happens.config:clear step one of every debugging checklist.Ramp-up. Low for a Laravel developer: config file plus one trait or callback convention, readable in an afternoon. The ramp-up cost is not the package — it is the institutional knowledge that this is a dormant dependency with a cosmetic-security failure mode. Onboarding should include the two-link smoke test as a standing verification, not a one-time setup step, because the regression is silent. For the PM, the ramp-up is governance: this is the test case that sets precedent for how the team treats low-adoption, dormant dependencies with security-adjacent behavior. Decide it deliberately, write the decision down, and apply the same standard next time — that precedent is worth more than the sidebar links.
How can I help you explore Laravel packages today?