Search by

omnibridge / laravel

GlitchArt

The Laravel host of omnisense/omnibase: its Foundation contracts (urls, translations, locales, access, parameters with the database settings laid over Laravel's config, clock, events...) answered by Laravel's services and seeded in Base\Contract\Runtime, the persistence through omnibridge/eloquent o

v1.0.0 2026-11-10 01:52 UTC

This package is auto-updated.

Last update: 2026-11-10 02:12:33 UTC


README

composer config allow-plugins.omnibridge/composer true
composer require omnisense/omnibase:4.x-dev omnibridge/laravel:1.x-dev
php artisan vendor:publish --tag=omnibridge-config

That hosts omnisense/omnibase in a Laravel application: the provider is discovered by Laravel, binds each contract of omnibase's Foundation to Laravel's services, and hands Laravel's container to the Foundation's registry, Base\Contract\Runtime (omnisense/omnibridge's Omnibridge\Runtime with omnibase's accessors). The host's contracts are omnisense/omnibridge's (Omnibridge\Contract\*), bound under their own names (their former names, Base\Contract\Urls... and omnibridge/twig's, were removed at omnibase 4's final pass: no former name is bound). omnibase's framework-free code - an entity's link, its noun, a translatable's language, a screen's builder - then runs under Laravel as it runs under Symfony:

use Base\Contract\Runtime;

Runtime::urls()->path('article', ['slug' => 'le-fuji']);   // Laravel's router
Runtime::translations()->trans('@messages.menu.home');      // omnibase's ICU catalogues
Runtime::parameters()->get('base.user.delay');              // Laravel's config, the database settings over it
app(Omnibridge\Contract\Urls::class) === Runtime::urls();   // one object

omnibase asks for PHP 8.4 and the extensions its own packages need (bcmath, intl, gd, exif, ftp, imagick, igbinary). omnibase stays on Doctrine for storage and Twig for its templates: this package opens Doctrine on one of Laravel's connections (omnibridge/doctrine) and Twig over the site's templates (omnibridge/twig).

What is bound

Contract Answered by Class
Omnibridge\Contract\Urls Laravel's router (route()), the build's asset manifest; a route Laravel does not have may be answered elsewhere RouterUrls
Omnibridge\Contract\Translations the ICU catalogues of the folders configured, read by symfony/translation, in Laravel's current locale; exists() CatalogueTranslations
Omnibridge\Contract\Locales Laravel's locale (fr) as the site's locale (fr-FR), the default first Contract\AppLocales
Base\Contract\Nouns omnibase's own rule (EntityNouns) on those catalogues -
Omnibridge\Contract\Access the gate (Gate::allows()), the default guard's user Contract\GateAccess
Omnibridge\Contract\Sessions the guard's logout() for the user signed in Contract\GuardSessions
Omnibridge\Contract\Tokens the session's CSRF token (one for every intent) Contract\SessionTokens
Omnibridge\Contract\Environment APP_ENV, app.debug, app.key Contract\AppEnvironment
Omnibridge\Contract\Routes a route's declared path, the route of an address; no icons Contract\RouterRoutes
Omnibridge\Contract\Files the disks (omnibase's storage names mapped to disks) Contract\DiskFiles
Base\Service\ParameterBagInterface Laravel's config, the settings linked to a parameter laid over it (omnibase's HotParameters) Parameters\ConfigParameters, Parameters\DatabaseSettings
Psr\Clock\ClockInterface Laravel's dates (Date::setTestNow(), travel() move it) Contract\DateClock
Psr\EventDispatcher\EventDispatcherInterface Laravel's dispatcher Contract\DispatcherEvents
Symfony\Component\Mailer\MailerInterface the default mailer's Symfony transport Contract\TransportMailer
Doctrine\DBAL\Connection, Doctrine\ORM\EntityManagerInterface the Laravel connection configured, omnibridge/doctrine's entity manager Doctrine\Connections
Twig\Environment the site's templates and namespaces, omnibridge/twig's contract (path(), url(), asset(), |trans), its AccessExtension (is_granted() on the gate, csrf_token() the session's token: the code a Symfony page asks too, omnibridge/symfony registering it there) and helpers View\Twig
Base\Contract\Pictures |picture's rule: a public file by its thumbnail at the size asked (MediaService's, omnibridge.media on) or its path, an address as it is, else the build's asset; a stored picture where the application serves it (stored()) Screen\AddressPictures
Base\Contract\Texts |wysiwyg and |wysiwyg_toc's passes (headings, links, every leaf a string, Editor.js's renderer or Quill's HTML) through omnibase's own views (@Base among the Twig paths, or on($twig)); a picture where the application serves it (images()) Screen\WysiwygTexts
Base\Screen\ContextProvider a screen's frame: the locale as a Symfony request carries it (fr_FR), the site's settings, home, login, register, logout and each language's address, the guard's user, the colour scheme of the visitor's mode (omnibridge.theme), the session's messages Screen\ScreenContext

With omnibridge.media on (see below): Base\Contract\Media (Media\ServiceMedia, MediaService's answers), Base\Service\MediaService, Base\Service\ImageRoute, and omnibase's image routes. Off (the default), none of them.

Screens. With those three, a screen of the core is built on Laravel as under Symfony, without code of the application's:

use Base\Contract\Runtime;
use Base\Screen\Builder\Stories;

$frame = app(Base\Screen\ContextProvider::class)->frame(['destination' => $destination]);
$text  = (new Stories(Runtime::texts(), Runtime::pictures()))->text($thread->getContent(), ['headings' => true]);

An application says where it serves its stored pictures, in a provider:

$this->app->extend(Base\Contract\Pictures::class, fn ($pictures) => $pictures->stored(fn (string $image) => /* its address, or null */));
$this->app->extend(Base\Contract\Texts::class, fn ($texts) => $texts->images(fn (string $source) => /* its address, or null */));

Actions. An action written once for every host (no Request, no Response: its inputs, and a result of omnibase's - Base\Screen\Template, Json, Shell, File, a View, or null for no such page) is a route here, answered by omnibase's Base\Screen\Negotiation (HTML, JSON when the template allows it and the client prefers it, omnibridge/react's protocol and its 409) through Screen\Responder, as Base\Bridge\Symfony\ScreenResponder answers it under Symfony:

use Omnibridge\Laravel\Screen\Responder;

Route::get('/{destination}/articles/{slug}', Responder::action(App\Action\Blog\Article::class))->name('app_article');

The action's parameters are the route's by name, $query the query string's values, $body what a POST sent (its JSON object, else its form fields), the rest the container's. An action that changes something checks its form's token itself, through the Tokens contract, as under Symfony: Laravel's CSRF middleware is left off the routes of the actions that are not GET. The templates are rendered by the container's Twig; an application whose pages carry more (a page context, what it adds once rendered) binds its own Base\Screen\Negotiation, rendering Negotiation::PAGE (a page), PART (a value of a JSON answer) and ALONE (a page whole, its own markup only) each its way.

Routes and bindings from the attributes. The routes an application's actions declare - symfony/routing's #[Route] on the action, the declaration Symfony's router reads on the same class - are registered here by Routing\ActionRoutes, by symfony/routing's attribute loader's rules (not the loader itself, which stands on symfony/config): the same names, localized paths (app_timeline.fr and its _locale, which Http\Localize takes for the page's language), requirements, optional parameters, priorities (registered in the order Symfony matches them) and environments (Laravel's local, testing, production are Symfony's dev, test, prod). A route naming no method answers GET and HEAD. RouterUrls::localized() reads a name as omnibase's router does (app_timeline is app_timeline.fr on a French page, a version without a destination name.default, a language the routes lack the fallback's). The services an application aliases with Symfony's #[AsAlias] are bound by Container\Aliases (read by the attribute's name: no symfony/dependency-injection needed), the core's repositories by their interfaces from omnibase's own list (Base\Repository\Aliases):

use Omnibridge\Laravel\Routing\ActionRoutes;

// routes/web.php: the application's actions and omnibase's.
ActionRoutes::register(app('router'), ActionRoutes::load([
    'App\\Action\\' => base_path('src/Action'),
    'Base\\Controller\\Action\\' => base_path('vendor/omnisense/omnibase/src/Controller/Action'),
], ActionRoutes::environment(app()->environment())));

// a service provider: Aliases::bind($this->app, Aliases::read(['App\\Repository\\' => base_path('src/Repository')]));

What omnibase's pages ask more. app.theme.layout(1) (omnibridge.theme.layouts: the folder of the site's layoutN.html.twig, as omnibase's Themizer finds them); app.request.attributes as Symfony's (the route's name and parameters) and its uri as Symfony writes it; a template's companions (page.css.twig, page.mobile.css.twig, page.js.twig: View\Companions, as omnibase's HtmlTagRenderer adds them); get_short_class() (omnibase's ClassTwigExtension), |trans_entity and |obfuscate (View\OmnibaseFilters); a key no catalogue holds translated as nothing, as omnibase's Translator answers it; an action's Base\Screen\Redirect.

Language. omnibase's language under Laravel, as its LocalizerSubscriber and LocalizerController give it under Symfony: Http\Localize (the web group's middleware: the LANG cookie, else the browser's language among omnibridge.locales.available, else omnibridge.locales.default; a route of one language is that language's) and Http\SwitchLocale (Route::get('/locale/{_locale?}', SwitchLocale::class)->name('_switch_locale'): the choice kept as Symfony writes it, en_GB, and back to the page it came from on the site). Leave LANG out of the encrypted cookies.

Parameters. Runtime::parameters() reads Laravel's config by the same dotted path (app.name; base.user.delay for a site with config/base.php), and, when omnibridge.parameters.hot is on (or the config's own base.parameter_bag.use_hot_bag), the settings the back office links to a parameter, read once in omnibase's tables (layoutSetting, layoutSettingIntl) on the first call: a setting stored as false wins over the config's true, as under Symfony. Laravel's config binding itself is not decorated: Laravel reads and caches its configuration before any provider, everywhere, as an array; settings read from a database have no place there.

Also here

The pieces an omnibase page asks of its framework, for an application that renders omnibase's templates with Laravel: View\Encore (encore_entry_link_tags() and encore_entry_script_tags() as omnibase prints them, with the breakpoints' stylesheets), View\Breakpoints (the style omnibase puts before the first stylesheet), Page\AppVariable and its parts (the app variable with Symfony's words: app.request.getLocale(), app.request.attributes.get('_route'), app.site, app.theme, app.session; Page\Theme is omnibase's ThemizerInterface too, which its theme menus read: Base\Twig\Extension\MenuTwigExtension), Form\FormView and Form\FormRenderer (a few simple forms in the markup of omnibase's form theme), and RouterUrlGenerator (Symfony's UrlGeneratorInterface over the Urls contract, for a library that asks for one).

omnibase's own Twig extensions in every page (View\OmnibaseExtensions, with omnisense/omnibase 4 from its framework-free extensions): the container's Twig\Environment gets the classes the core registers under Symfony - the menus, EnumTwigExtension (enum(), |colorify, |trans_enum), PictureTwigExtension (image(), |thumbnail, |soundify, |mimetype... with omnibridge.media on), LinkTwigExtension (|urlify), IconTwigExtension (|iconify, omnibase's adapters and the Routes contract's icons), TextsTwigExtension (|wysiwyg), NotificationTwigExtension (the bell; push on omnibridge.push), SecurityPolicyTwigExtension, DeployVersionExtension, DemoTwigExtension, and omnibase/consent's panel on omnibridge.consent.config - plus View\HostExtension, what the templates take from Symfony's bundles (Encore's tags on omnibridge.encore, stimulus_controller(), absolute_url(), |url). An application rendering a page in an environment of its own calls OmnibaseExtensions::extend($twig, $translations, $theme); admin() is app.admin (Base\Twig\Variable\AdminLinks); Form\FormExtension gives the form_*() functions over a page's FormViews. Contract\RouterRoutes::elsewhere() answers the routes Laravel does not have (their pattern, a route's icons) by another Routes, as RouterUrls::elsewhere() their addresses.

Signing in through omniauth

// config/omnibridge.php
'auth' => true,
// config/auth.php
'guards' => ['web' => ['driver' => 'omniauth', 'provider' => 'omnibase']],
'providers' => ['omnibase' => ['driver' => 'omniauth']],

With omnibridge.auth on and omnisense/omniauth installed (it is only suggested), Auth\AuthServiceProvider wires omniauth's sign-in under Laravel as omnibridge/symfony wires it under Symfony (plan "omniauth", ยง3.2, step AU8), from config/omniauth.php (php artisan vendor:publish --tag=omniauth-config) - the same keys as config/packages/omniauth.yaml there, so one site says the same thing under both hosts: each method's key turns it on (the password, the sign-in link, the e-mailed code, the backup codes, the trusted devices; passkeys, the authenticator application and the identity providers with their package, omniauth/webauthn, omniauth/otp and omniauth/google - the providers' mechanics are omniauth's own Omniauth\OAuth\), the stores ("omnibase": the core's Base\Auth\ on the entity manager - the user, userToken, userPasskey and userIdentity tables -, or the container's bindings by key), the throttle on a Laravel cache store, the messages. totp.secret_key (env('OMNIAUTH_TOTP_KEY'): base64 of 32 bytes, the Symfony site's very key, never in the repository) wraps the TotpSecrets store in omniauth/otp's SealedTotpSecrets over a SodiumSecretBox: the secrets sealed at rest, as omnibridge/symfony decorates its store.

  • Auth\OmniauthGuard (StatefulGuard, driver omniauth) keeps the account's handle in the session, reads the account back through omniauth's Accounts at each request, renews the session's id at a sign-in, sets the signed remember-me cookie (Auth\RememberMe: the handle, the expiry, the password hash and the address, as Security's signature_properties) and forgets it at a sign-out; attempt() asks omniauth the password. Auth\OmniauthUserProvider (driver omniauth) answers Laravel's UserProvider over the same stores, the passwords checked by symfony/password-hasher as under Symfony (>72-byte passwords included).
  • The account is Laravel's Authenticatable through Auth\AccountUser, a wrapper the contracts unwrap: Omnibridge\Contract\Access::getUser(), the screens' viewer and Twig's app.user give the application's own object, as under Symfony.
  • Auth\Controllers\SignIn is every way in: the password form (and its page, the site's security/login.html.twig with the form as omnibase's templates read it), the sign-in link, a passkey (web-auth's JSON), the second factor's code, an identity provider's return, and the sign-out (CSRF-checked as Security's logout with enable_csrf); Auth\Controllers\Ceremony the second factor's page (scheb/2fa's variables: the site's template renders unchanged), a passkey's options, the trip out to a provider. The forms' CSRF tokens are checked through the Tokens contract (Laravel's session token); Laravel's own CSRF middleware is left off these routes.
  • Auth\Routes declares them at the Symfony site's addresses and under its route names (security_login, 2fa_login, 2fa_login_check, oauth_google, oauth_google_check, security_logout, security_logoutRequest...), in the web group; omniauth.routes false to declare them yourself.
  • Auth\Middleware\SecondFactorPending (appended to the web group, before auth) keeps a visitor between two factors on the second factor's page; with omnibase's sign-in effects, Auth\Middleware\EnrolmentPrompt sends a signed-in account to the second factor's enrolment page as omnibase's SecurityEnrolmentSubscriber does under Symfony (its rule, Base\Auth\EnrolmentPrompt: the offer after a new device, a required second factor until "not now"), and the browser's PHPUSERID is written for the domain omnibase's router gives its cookies (Base\Routing\CookieDomain on base.router.use_fallbacks and host_fallbacks); Auth\Roles answers Security's attributes through Gate::before() - ROLE_* with omniauth.role_hierarchy, IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, IS_AUTHENTICATED_2FA_IN_PROGRESS, PUBLIC_ACCESS - so is_granted() and the Access contract answer as under Symfony.
  • The cookies the Symfony site shares (trusted_device in scheb/2fa's format, the remember-me) are left out of Laravel's cookie encryption; omniauth.secret is the Symfony site's kernel.secret, so the devices it trusted and the codes it sent are good here. An identity provider's return address is the path of its check route, completed with the host of each request (a long-running server keeps the providers).

With omnibase's stores, the core's Base\Auth\Listener\UpdateAccount brings the account up to date at its sign-in (a role dropped, its time zone and language kept, its kick lifted); Base\Auth\Policy decorates the second-factor rule where the application binds omnibase's SecurityPolicy; a sign-in link or an e-mailed code needs a Omniauth\Contract\Sender (omnibase's NotifierSender needs its Notifier).

Configuration

config/omnibridge.php: the site's locales, the catalogues' folders, the asset manifest, Twig's folders and namespaces, the Laravel connection Doctrine opens and the entities' folders, the parameters' overlay, the themes (the screens' colour scheme), the storages, the bundles' entity namespaces. A route Laravel does not serve can be answered elsewhere:

$this->app->extend(RouterUrls::class, fn (RouterUrls $urls) => $urls->elsewhere(
    fn (string $name, array $parameters, string $locale) => $otherSite->url($name, $parameters, $locale),
));

omnibase's Doctrine mapping

// config/omnibridge.php
'omnibase' => ['project_dir' => base_path()],   // or true; false (the default) leaves it off
'doctrine' => [
    'connection' => 'mysql',
    'entities' => [app_path('Entity')],
],

omnibridge.omnibase turns omnibase on whichever persistence is chosen (its former name omnibridge.doctrine.omnibase is still read where it is not given, deprecated, removed at 2.0). With it on and Doctrine chosen, the bound entity manager is omnibase's, as under Symfony: built by omnibridge/doctrine with Omnibridge\Doctrine\Omnibase\Mapping::configure() (formerly Base\Bridge\Doctrine\Mapping) (the repositories from omnibase's registry), over the core's entities beside the application's, then Mapping::register() - the class convention (App\Entity\X is Base\Entity\X where the application has none, an alias and not a subclass; an application's own class always wins), the column types and enumerations (the project's src/Enum), the naming strategy, the discriminator maps, #[Alias], the translations, the listeners and the trash and vault filters. Its options are Mapping::register()'s: project_dir (base_path()), environment (omnibase's name, the vault's: prod, dev, test for Laravel's production, local, testing), cache_dir, parameters (over Laravel's base config by full name - the entities read base.uploader.storage...), types, enumerations, attribute_paths, resolve_target_entities, entity_listeners. app(Mapping::class) gives what it registered. OmnibaseMappingTest checks the metadata against those omnisense/omnibase records from its Symfony harness, class by class.

omnibase's images

// config/omnibridge.php
'storages' => ['local.cache' => 'omnibase-images', 'local.uploads' => 'uploads'],   // Laravel's disks
'media' => true,   // or ['routes' => false, 'secret' => ...]; false (the default) leaves it off

With omnibridge.media on, omnibase's media rule runs under Laravel as under Symfony: MediaService (thumbnails, formats, the image route's obfuscated addresses) built on Laravel's router (Media\LaravelMediaRouter, the core's Base\Contract\MediaRouter), omnibase's Obfuscator and Flysystem (its storages are Laravel's disks, Media\DiskStorages: omnibridge.storages, else the disk of the storage's name; local.cache, where the thumbnails are kept, is required) and Imagine (Imagick, else GD). The image routes (/images/..., /contents/...) are declared under their Symfony names in Symfony's order (Base\Service\ImageRoute::ROUTES) and answered by the core's Base\Service\ImageRoute (Media\ImageController, a line per request): the same addresses, thumbnails, formats, redirections and placeholder. Its parameters are the config's base: base.images.* (max_quality, enable_webp, fallback, no_image...), base.obfuscator.* (uuid, compression... and cache_dir, the map of the identifiers: share the Symfony site's to read the addresses it wrote), base.uploader.storage, base.twig.editor.storage; omnibase's defaults where it says nothing. A file has the address Symfony gives it when it has the same path on both hosts (the identifiers are made of it: the same project folder, the uploads where the Symfony site's public/ links them).

With omnibase's mapping on as well, what a site's entities and repositories ask of their host is answered here too: the attribute reader reads the stored files through that Flysystem (#[Uploader]: an avatar, an article's audio, whose /contents/... addresses ImageRoute serves), BaseTrait::getMediaService() is the bound MediaService, the editor's pictures go through omnibase's MediaEnhancer (MediaEnhancerInterface, the image route's address of each picture of a text, as Symfony's |wysiwyg gives it), and a repository taking services beside the registry (a site's CommentRepository, its cache pool first) is given them from Laravel's container by type (Mapping::register()'s services). omnibase's cache pool (Psr\Cache\CacheItemPoolInterface, Symfony's CacheInterface) is Symfony's cache component in storage/framework/cache/omnibase-pool - not Laravel's store, which unserializes no object (cache.serializable_classes).

First used by lesringostars' Laravel twin, which renders the Symfony site's blog and articles from the same database and templates.

Maps through omnimap

Where omnisense/omnimap is installed (suggested), Map\MapServiceProvider reads config/omnimap.php - the keys of omnibridge/symfony's omnimap.yaml - and wires nothing unless it names a provider:

return [
    'provider' => 'openstreetmap',
    'secret' => env('MAP_SECRET'),
    'providers' => ['openstreetmap' => ['user_agent' => 'MySite/1.0 (+https://example.org)']],
    'cache' => ['storage' => storage_path('app/omnimap'), 'directory' => 'omnimap'],
    'routes' => ['tile' => ['path' => '/omnimap/{signature}/{id}.webp', 'name' => 'omnimap_tile']],
];

It binds Omnimap\Registry (omnimap/google, omnimap/openstreetmap and omnimap/maplibre when installed, and the application's factories tagged omnimap.provider_factory, over Symfony's HTTP client or the one bound as omnimap.http_client, the PSR-16 cache of cache.pool - a store of Laravel's cache - or one in storage/framework/cache/omnimap), Omnimap\GeocoderInterface (the provider geocoder names, the page's by default), Omnimap\Snapshot\Snapshots (the map cache on an omnidrive drive - cache.drive, of the bound Omnidrive\Drives - or over a directory or a bound Omnimap\Snapshot\Storage; cache.make false for a host reading another host's cache read only), Omnimap\MapPrinter and omnibridge/twig's MapExtension, which every page's Twig gets (omnimap(), omnimap_export(), omnimap_suppress()), and routes the tiles as omnibridge/symfony serves them (WebP at a .webp path fitted within cache.public_size, cache.max_age, written in cache.public_directory when set; routes.tile.register false when the web server serves them). Capturing and deleting a picture is omnibridge/symfony's (routes.export.name, routes.suppress.name name them for the buttons).

omnibase's console commands

With omnibase 5.0, OmnibridgeServiceProvider registers the core's console tasks (Base\Console\Task\*) as artisan commands of the same names as omnibridge/symfony's: translation:crud, translation:entities, translation:enums, translation:controllers, translation:settings, cache:clear:sessions, demo:reset, uploader:entities, uploader:images, uploader:images:crop, and omnibase:cache:clear (Laravel keeps its own cache:clear; omnibase's checks, lock and counter run around optimize:clear). Each prints the task's Base\Console\Report as Symfony's command prints it (Console\Reports). The application's App\ folder is omnibridge.console.source, else Composer's App\ folder that has entities, else app/.

And (slice 3b) base:mapping, icon:enums, icon:entities, autocomplete:entities, entity:discriminator, app:notifier, notifier:mail:test and user:create (omnibase's Notifier where omnibridge.notifier gives a channel policy; user:create --no-email without it), user:notifications, thread:publishable, trash:purge. Not here, on purpose: base:mailer:trap (the trap holds Symfony Mailer's messages; Laravel's mailer never consults it), timemachine:* (the TimeMachine is built on DoctrineBundle's registry, registered by Symfony's container only), doctrine:* (upgrades and imports of a database the Symfony site owns).

php artisan translation:entities --locale=fr

The schema created from the description: omnibridge:schema:create

With omnibase 5.0 and its mapping on (omnibridge.omnibase), OmnibridgeServiceProvider registers omnibridge:schema:create (Console\SchemaCreateCommand; omnibase's plan "omnibase agnostic", step F7a): the tables of the installed entities - omnibridge.doctrine.entities beside the core's, with omnibridge.omnibase's options (the project's folder and its enumerations, the targets) - created from their description (omnibase's Base\Database\Schema\EntitySchema) by omnisense/omnibridge's SchemaCreator, no ORM, in the empty database of the connection omnibridge.doctrine.connection names (Laravel's default otherwise), through Laravel's own PDO and with the connection's charset and collation. The same statements as omnibridge/symfony's command and the plain-PHP vendor/bin/omnibridge-schema-create. A database that already holds a table of the description is refused (status 1), nothing run; migrations evolve an existing database.

php artisan omnibridge:schema:create --dump-sql   # the statements, nothing run
php artisan omnibridge:schema:create              # "132 tables created (289 statements)." on lesringostars' entities

Static export (omnibridge/static)

Where omnibridge/static is installed, OmnibridgeServiceProvider registers the artisan command omnibridge:static:build: omnibridge/static's own command line (Console\Cli), handed a renderer through the application's HTTP kernel (each page a visitor's GET, at --render-origin, by default app.url), the container, public/ and, with --actions, the routes the actions of those folders declare (Routing\ActionRoutes).

php artisan omnibridge:static:build static.php --out=storage/static --actions='App\Action\=src/Action'

Run on lesringostars-laravel (2026-10-24, the working copy loaded by hand): the twin's home, intro, destinations, two articles and the travels pages rendered by Laravel and written, the routes read from lesringostars' actions.

Tests

docker compose run --rm test

PHPUnit on Orchestra Testbench (Laravel 13), PHP 8.4, omnisense/omnibase 4.x installed from Packagist: every contract reached through Runtime and the container as one object, each answer (routes, ICU messages and exists(), locales, the gate, sign-out, the CSRF token, the clock under travel(), events, disks, the parameters with a false setting over a true config), Doctrine on Laravel's SQLite database in UTC, omnibase's mapping (an App\ class over a Base\ one, another only by alias, the metadata of 94 classes equal to Symfony's), omnibase's images (a thumbnail's address made and read back, redirected to its extension, served at its size, in webp and without the local cache, a missing one a 404, Pictures' thumbnails; Imagick in the image), the settings of the site, Encore tags, the breakpoints, a form; a page's map through omnimap (MapTest: the picture made from OpenStreetMap's tiles answered in the test, its tiles served, the buttons for the gate's ROLE_ADMIN; GD in the image).

Licence

MIT.