Download the PHP package stubbedev/laravel-stoli without Composer
On this page you can find all versions of the php package stubbedev/laravel-stoli. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download stubbedev/laravel-stoli
More information about stubbedev/laravel-stoli
Files in stubbedev/laravel-stoli
Package laravel-stoli
Short Description An enhanced Laravel extension to export named routes to JavaScript, allowing you to use route names instead of URLs. Supports route grouping for modular applications. Provides type hinting for route params and responses.
License MIT
Informations about the package laravel-stoli
Laravel Stoli
stubbedev/laravel-stoli is a Laravel package that exports your application's named routes to TypeScript, enabling you to use route names instead of hardcoded URLs in your frontend code. It builds on top of spatie/laravel-data and spatie/laravel-typescript-transformer — both are required dependencies — and generates fully typed TypeScript definitions: route names, URI and domain parameters narrowed by their where constraints, and request and response types taken from the Data classes typescript:transform has already turned into TypeScript.
Requirements
- PHP 8.2+
- Laravel 12 or 13
spatie/laravel-data^3|^4spatie/laravel-typescript-transformer^3
Installation
Publish and configure spatie/laravel-typescript-transformer first — Stoli uses its output directory as the destination for all generated files:
Then publish the Stoli configuration:
Generate routes and types:
stoli:generate writes the following into the typescript-transformer output directory:
stoli.js— theRouteServiceruntimestoli.d.ts— TypeScript declarationsapi.ts(or one file per module) — typed route definitionsconstants.ts— the constants of the classes marked with#[TypeScriptConstants]
Usage
Basic
Pass rootUrl to point the URLs somewhere else at runtime, a dev proxy say. It replaces the
generated host of every route except those with a domain of their own:
Typed parameters
The generated route file exports ApiRouteParams and ApiRouteName. URI parameters ({id}, {slug?}) and route domain parameters ({account}.example.com) are always included. When a controller method accepts a Spatie\LaravelData\Data object, its generated type is intersected with them, so the request body is typed too.
Required parameters are required in the types as well: a route with any may not be called
without them, while {param?} parameters may be left out or passed as null.
URI constraint types
When routes declare ->where() constraints, the parameter type is narrowed accordingly:
Without a constraint the type is string | number. Optional parameters ({param?}) become param?: type.
Typed responses
Each generated module also exports ApiRouteResponse, a per-route map of response types. A route is included when its controller method returns a Spatie\LaravelData\Data subclass, or a collection of one, that php artisan typescript:transform has turned into TypeScript. Always run typescript:transform before stoli:generate:
| Controller return type | Response type |
|---|---|
UserData |
UserData |
ApiResponseData with @return ApiResponseData<UserData> |
ApiResponseData<UserData> |
DataCollection, Collection or array with @return DataCollection<int, UserData>, list<UserData> or UserData[] |
UserData[] |
PaginatedDataCollection with @return PaginatedDataCollection<int, UserData> |
Paginated<UserData> |
CursorPaginatedDataCollection with @return CursorPaginatedDataCollection<int, UserData> |
CursorPaginated<UserData> |
Paginated and CursorPaginated describe the data/links/meta envelope laravel-data sends and are exported from stoli.d.ts. Class names in @return tags are resolved the way PHP resolves them in the controller's file: through its use imports, grouped and aliased ones included, then relative to its namespace.
Types written by the transformer's GlobalNamespaceWriter are ambient globals and are referenced by their namespace path; types written as module exports are imported, with the path computed relative to the module's output directory:
Response types follow laravel-data's wrapping. A returned Data object is wrapped under its
defaultWrap() key, or otherwise the data.wrap key in config/data.php
({ data: UserData }); a DataCollection under the config key; and a paginated collection
has its data key renamed to it (Paginated<UserData, 'items'>). Plain arrays and Laravel
collections are sent by Laravel itself and never wrapped.
Routes without a resolvable response type are absent from the interface. The axios router (see below) falls back to Record<string, unknown> for those routes.
Typed constants
PHP classes marked with #[TypeScriptConstants] have their public constants exported to
constants.ts, discovered the same way the typescript-transformer discovers the classes and
enums it transforms — by scanning the configured directories for the attribute.
The PHP namespace is mirrored as nested objects, and each class also gets a union type of its values:
Only public constants declared on the class itself are exported — inherited and interface
constants belong to the class that declares them. Enums are skipped: they are already
transformed to TypeScript by typescript:transform.
#[TypeScriptConstants('Boundaries')] publishes the class under a different key. When two
classes share a basename, the second type alias takes in its enclosing namespace segment
(AdminStatus) so both remain exported.
Classes carrying spatie's #[TypeScript] attribute are picked up as well, so a class that is
already transformed for its types contributes its constants too. Narrow constants.attributes
in config/stoli.php to Stoli's own attribute to opt out of that.
Supported constant values
| PHP value | TypeScript |
|---|---|
string, int, float, bool, null |
the literal value |
| List array | array literal |
| Associative array | object literal |
| Backed enum case | its backing value |
| Pure enum case | its case name as a string |
JsonSerializable |
its serialized value |
Constants holding anything else — a plain object, a resource — are left out, and a class left
without a single exportable constant is dropped from the file. When no constants are left
at all, a constants.ts an earlier run generated is removed; a directory that cannot be
scanned is reported as an error rather than leaving the old file in place.
Axios router (optional)
Enable in config/stoli.php:
The generated router (router.ts, or <module>.router.ts for each module when there are several) wraps axios with full type inference for both params and responses:
File uploads
Pass files as normal params — a body containing a File/Blob anywhere, however deeply nested, is sent as multipart/form-data. Params stay fully typed, so file fields are checked against the Data type (map UploadedFile to File in your typescript-transformer config):
The body is flattened the way PHP reads it back (meta[tags][], rows[0][id]) with booleans sent as 1/0, so Laravel validates it as it would a JSON body. PHP only parses multipart bodies on POST, so a put or patch carrying files is sent as a POST with _method set, which Laravel routes back to the PUT or PATCH route.
post, put and patch also accept a raw FormData for hand-built forms.
Route parameters are read from the FormData and stripped from the body. No type checking on the fields in this form:
Requires axios: npm install axios
Route service methods
| Method | Description |
|---|---|
generateFullURL(name, params?) |
Full URL; leftover params are appended as query string. Leaves params untouched |
createURLWithoutQuery(name, params?) |
URL with only URI params substituted; no query string. Deletes the substituted keys from params so the rest can be used as a body — pass a copy if you still need it |
has(name) |
Returns true if the route exists |
Both throw on a missing required parameter rather than emitting a literal {id} in the URL.
Optional parameters ({page?}) may be omitted and take their path segment with them.
Query strings are written the way PHP's http_build_query writes them, so Laravel reads back
the same structure: arrays become tags[]=x&tags[]=y, nested objects filter[name]=x,
booleans 1/0 (what the boolean rule accepts), and dates ISO strings. The axios router
serializes its query parameters the same way. serializeQuery() is exported from stoli.js
for building a query string by hand.
Configuration
Publish and edit config/stoli.php to customise.
All generated files are written to the outputDirectory configured in config/typescript-transformer.php. Per-module path can override this for individual modules.
Module options
| Option | Default | Description |
|---|---|---|
match |
* |
URL prefix to filter routes. * matches all, /api/store matches api/store and the routes under it, but not api/storefront |
names |
null |
Route name pattern, or list of patterns, a route must also match (Str::is syntax, e.g. app.*). null keeps every name |
standalone |
false |
Keep the module in its own file even when split is false, and generate no axios router for it |
name |
— | Output filename (without extension) |
rootUrl |
APP_URL |
Base URL for absolute URLs. A route with its own domain (Route::domain()) keeps its domain, with the scheme taken from this URL |
absolute |
true |
Generate absolute (https://…) or relative (/…) URLs |
prefix |
null |
Prefix prepended to every generated URL |
path |
transformer output dir | Output directory for this module's .ts file |
stripPrefix |
null |
Route name prefix to strip (e.g. store. turns store.products.list into products.list) |
Multiple modules
Split routes into separate typed files per API consumer:
This generates store.ts and admin.ts, each with their own StoreRouteParams / AdminRouteParams / StoreRouteResponse / AdminRouteResponse interfaces.
Selecting routes by name
When the routes a consumer needs share a name prefix but not a URL prefix (the pages of a
single-page app, say), select them with names instead:
match and names combine: a route lands in the module only when both accept it.
standalone keeps page routes like these out of the merged single file and out of the
axios router, which only makes sense for API routes.
Development
tests/Integration/GeneratedTypeScriptTest.php publishes the files for a set of fixture
routes into tests/typescript/build and compiles them against tests/typescript/usage.ts,
which asserts the types every route should narrow to. Run it outside docker after
npm ci --prefix tests/typescript.
License
MIT. See LICENSE.
All versions of laravel-stoli with dependencies
laravel/framework Version ^12.0|^13.0
spatie/laravel-data Version ^4
spatie/laravel-typescript-transformer Version ^3
phpstan/phpdoc-parser Version ^2.0
spatie/php-structure-discoverer Version ^2.0