orchestra/asset
Asset management and pipeline component for Laravel: define named containers, register scripts and stylesheets with dependencies, then render combined, versioned output—keeping templates clean while cache busting and asset URLs stay automatic. Part of the Orchestra Platform ecosystem.
Architecture fit. Orchestra\Asset is a thin orchestration layer, not a build tool. It gives the app named asset containers, dependency-aware ordering, versioned URLs, and Blade render helpers — nothing else. That is a good fit when the product's real problem is "many contributors need to inject CSS/JS into a host layout we don't want them editing": modules, plugins, tenant themes, white-label skins, legacy pages that predate the Vite pipeline. It is a poor fit if the requirement behind the ask is "ship less JavaScript" or "compile SCSS" — those are Vite's job, and adopting this package for them makes the build story worse. The healthy framing for the PRD is a split: Vite owns compiled, hashed app bundles; Orchestra\Asset owns the runtime tail (CDN vendor scripts with hard ordering constraints, per-tenant theme bundles, marketing tags, not-yet-migrated pages).
Integration feasibility. The install is genuinely cheap: composer require, service-provider auto-discovery, one config publish, and two Blade calls (styles() in <head>, scripts() before </body>). A working two-container spike is a 1–2 dev-day task. The cost is not integration; it is governance and discipline — every registration lives in a service provider, controller, or view composer, never in Blade; every asset name is namespaced (billing.app, not app); every dependency declaration stays coarse enough to reason about on a napkin. Those are policy decisions a TPM should write into the rollout plan rather than discover in code review.
Technical risk.
assertSeeInOrder tests on every ordering constraint you declare.integrity= on third-party scripts, budget for post-processing the rendered output or extending the render path — and validate in staging with production headers enforced.composer.json edit. Decide this before rollout, not after.v6.x ↔ a Laravel major). Framework upgrades become coordinated package upgrades and must live on the dependency-upgrade runbook.Key questions to settle before approving.
Stack fit. Framework-native, Laravel-only, no infrastructure footprint. It slots in beside Vite without conflict because it never touches compilation. It should be positioned in the architecture as a presentation-layer registry, not as part of the asset build.
Migration path. Incremental and reversible, page by page:
<script>/<link> tags into containers with no behavioral change. This proves the pipes and gives a clean diff to review.head, body-end); one container (or contribution set) per module, registered from that module's boot().boot() if Octane is in play.Compatibility. Confirm the installed version's extra.laravel.providers/aliases block in vendor/orchestra/asset/composer.json — the docs are a changelog line, the manifest is the source of truth. Pin the constraint explicitly in composer.json. If the app registers assets from config values, test with php artisan config:cache active, because cached config freezes those values in production while local will happily mutate them.
Sequencing for the delivery plan.
| Phase | Deliverable | Effort (indicative) |
|---|---|---|
| 0 | Spike: install, two-container layout, verify head/body split | 1–2 dev-days |
| 1 | Shell container in the base layout; existing tags lifted | 2–3 dev-days |
| 2 | Module registration pattern + naming convention documented | 1–2 dev-days |
| 3 | Octane soak test (if applicable) — gate | 1–2 dev-days |
| 4 | Observability listener, ordering assertions in tests | 2–3 dev-days |
| 5 | Deploy script: vendor:publish --tag=asset --force --no-interaction after composer install; version key = git SHA/release tag |
<1 dev-day |
| 6 | Theme/tenant registration via middleware | 2–4 dev-days |
Total: roughly two to three weeks of one engineer's time, with Phase 3 as a stop/go gate.
Acceptance criteria worth writing into the PRD. Two-container render verified; ordering proven by assertSeeInOrder; container contents logged in non-production; asset naming convention merged as a documented rule; deploy publishes and stamps a deploy-correlated version; CSP validated in staging if applicable; vendor-or-fork contingency recorded in the risk register.
Maintenance. Small, readable surface (~a few hundred lines) with one upstream maintainer and no dependent ecosystem. Your team is effectively the support organization. The mitigation is asymmetry: keep your footprint small (macros and Blade directives rather than forks), and keep a vendored copy ready so upstream abandonment is a composer.json edit rather than a rewrite. Budget a small recurring line item for "check upstream compatibility before each Laravel bump" — it belongs on the upgrade runbook, not in someone's head.
Support. No commercial support tier. Anything that breaks is yours. Treat the "fails open silently" property as an operational requirement rather than a nice-to-have: without log-side visibility into resolved container contents, mean time to diagnose a missing-stylesheet incident is measured in afternoons.
Scaling. Runtime cost is negligible — string assembly on the order of microseconds. The scaling concerns are elsewhere:
max_requests so workers recycle during the test, hit the same worker twice with different tenant context, and diff the rendered <head>.Failure modes to enumerate in the runbook.
| Failure | Symptom | Detection / prevention |
|---|---|---|
| Misspelled asset or container | One page missing CSS; looks like cache | Debug dump of ->all(); non-prod logging; assertSee in tests |
| Asset name collision across modules | Silent overwrite or duplicate tag | Namespaced names enforced in review |
| Octane state bleed | Stale theme or cross-tenant CSS | Soak test; per-request flush documented |
| CSP violation in prod | Broken page only under enforced headers | Staging test with production CSP, not Report-Only |
| Vendor publish forgotten on deploy | New vendor CSS 404s | Publish step in release script, --force --no-interaction |
config:cache freezes dynamic registration |
Works locally, inert in prod | Test with cached config in staging |
| Laravel major bump breaks constraint | Install failure mid-upgrade | Explicit pin; entry on upgrade runbook |
Ramp-up. Learning curve is under a day for a mid-level Laravel developer — the API is container(), add(), style(), script(), styles(), scripts(), all(). The organizational ramp-up is the actual cost: getting every module team to register assets in providers rather than editing layouts, to namespace asset names, and to keep dependency declarations coarse. That is a written convention plus one review checklist item, and it is worth doing in Phase 2 rather than correcting later — because the package's silent failure mode means the first violation surfaces as a production page missing styling, not as a failing build.
How can I help you explore Laravel packages today?