PHPackages                             splitstack/laravel-typewriter - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. splitstack/laravel-typewriter

ActiveLibrary

splitstack/laravel-typewriter
=============================

Unified TypeScript tooling for Laravel: DataPacket transport envelope, DTO/VO type generation, and Wayfinder route types in a single command.

v0.1.0(1mo ago)011↓75%MITPHPPHP ^8.4CI passing

Since Jul 15Pushed 1mo agoCompare

[ Source](https://github.com/EmilienKopp/laravel-typewriter)[ Packagist](https://packagist.org/packages/splitstack/laravel-typewriter)[ RSS](/packages/splitstack-laravel-typewriter/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (8)Versions (2)Used By (0)

laravel-typewriter
==================

[](#laravel-typewriter)

DDD-friendly unified TypeScript tooling for Laravel. One command generates:

- **DataPacket types** — a typed Back-to-Front transport envelope with defaults, metadata, and source/target routing
- **DTO / Value Object types** — from laravel-data `Data` classes, plain DTOs, and value objects
- **Route types** — via [laravel/wayfinder](https://github.com/laravel/wayfinder) (optional)

Powered by [spatie/laravel-typescript-transformer](https://github.com/spatie/laravel-typescript-transformer) under the hood.

---

Installation
------------

[](#installation)

```
composer require splitstack/laravel-typewriter
```

Publish the config:

```
php artisan vendor:publish --tag=typewriter-config
```

Publish the TypeScript `DataPacket` utility class:

```
php artisan vendor:publish --tag=typewriter-ts
# → resources/js/utils/DataPacket.ts
```

---

The DataPacket pattern
----------------------

[](#the-datapacket-pattern)

`DataPacket` is a typed envelope for passing data from PHP to the frontend. It gives you:

- **Typed payload** (`data`) — a laravel-data `Data` object, or an untyped array for convenience
- **Per-key defaults** (`defaults`) — validated against the keys in `data` so stale defaults can't sneak in
- **Metadata** (`meta`) — out-of-band context (pagination, permissions, flags)
- **Global fallback** (`default`) — a single value for when the whole packet has no specific match
- **Source / target routing** (`source`, `target`) — BackedEnum scalars that tell the frontend where data came from and where it's going, without magic strings on either side

### Convenience mode (untyped)

[](#convenience-mode-untyped)

Use `DataPacket` directly when you don't need end-to-end type safety:

```
use Splitstack\Typewriter\DataPacket;

return inertia('Orders/Index', [
    'orders' => DataPacket::from([
        'data'   => $orders->toArray(),
        'meta'   => ['total' => $orders->total()],
        'source' => 'orders',
    ]),
]);
```

TypeScript receives `DataPacket` where `data` is `Record`.

### Typed mode (end-to-end type safety)

[](#typed-mode-end-to-end-type-safety)

Scaffold a typed packet + its companion `Data` class:

```
php artisan make:packet OrderPacket --data=OrderData
```

This creates two files:

```
// app/Domain/Data/OrderData.php
#[TypeScript]
class OrderData extends Data
{
    public function __construct(
        public readonly int $id,
        public readonly string $status,
        public readonly float $total,
    ) {}
}

// app/Domain/Packets/OrderPacket.php
class OrderPacket extends DataPacket
{
    public function __construct(
        public readonly OrderData $data,
        array $meta = [],
        array $defaults = [],
        mixed $default = null,
        string|int|null $source = null,
        string|int|null $target = null,
    ) {
        parent::__construct($data->toArray(), $meta, $defaults, $default, $source, $target);
    }
}
```

Use it in your controller:

```
return inertia('Orders/Show', [
    'order' => OrderPacket::from([
        'data'    => OrderData::from($order),
        'defaults'=> ['status' => 'pending'],
        'source'  => OrderSource::Api->value,
        'target'  => OrderTarget::Checkout->value,
    ]),
]);
```

### TypeScript consumption

[](#typescript-consumption)

```
import type { OrderPacket } from '@/types/packets'
import { DataPacket } from '@/utils/DataPacket'

const packet = DataPacket.from(props.order)

packet.get('status')        // OrderData['status'] — falls back to defaults if null
packet.raw('status')        // raw value, no default resolution
packet.isFrom('api')        // source routing check
packet.isFor('checkout')    // target routing check
packet.getMeta('total')     // metadata access
packet.fallback()           // global default value
```

---

Type generation
---------------

[](#type-generation)

### Source groups

[](#source-groups)

Configure which directories to scan in `config/typewriter.php`:

```
'sources' => [
    'value_objects' => [
        'directories' => ['Domain/ValueObjects'],
    ],
    'dtos' => [
        'directories' => ['Domain/DTOs', 'Http/Resources'],
    ],
    'packets' => [
        'directories' => ['Domain/Packets', 'Http/Packets'],
        'enums'       => ['Domain/Enums'],  // BackedEnums used as source/target
        'enabled'     => true,
    ],
],
```

### Transformer chain

[](#transformer-chain)

For each source group, typewriter runs a single typescript-transformer pipeline:

PriorityTransformerHandles1`DataPacketTransformer`Typed `DataPacket` subclasses2`DataClassTransformer`laravel-data `Data` classes3`EnumTransformer`BackedEnums (source/target routing)4`PlainClassTransformer`Plain VOs, DTOs, any non-abstract classClasses that no transformer matches are silently skipped.

### Commands

[](#commands)

```
# Everything: wayfinder routes + all source groups
php artisan typewriter:generate

# Types only (skip wayfinder)
php artisan typewriter:generate --types-only

# Individual source groups
php artisan typewriter:typegen --value-objects-only
php artisan typewriter:typegen --dtos-only
php artisan typewriter:typegen --packets-only

# Skip barrel index.ts
php artisan typewriter:typegen --no-barrel
```

### Output

[](#output)

```
resources/js/types/
├── value-objects.ts
├── dtos.ts
├── packets.ts        ← DataPacket types + routing enums
└── index.ts          ← barrel re-export

```

---

Marking classes for export
--------------------------

[](#marking-classes-for-export)

**laravel-data `Data` classes** — add `#[TypeScript]`:

```
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
class OrderData extends Data { ... }
```

**Plain classes** — no attribute needed. Any public typed property or `@property` PHPDoc tag in a scanned directory is picked up automatically.

**DataPacket subclasses** — no attribute needed. The `DataPacketTransformer` detects them by class hierarchy.

**BackedEnums** — put them in the `enums` directories of the `packets` source group:

```
enum OrderSource: string
{
    case Api = 'api';
    case Admin = 'admin';
}
```

Generated output:

```
export type OrderSource = 'api' | 'admin';
```

---

Configuration reference
-----------------------

[](#configuration-reference)

```
// config/typewriter.php
return [
    'sources' => [
        'value_objects' => [
            'directories' => ['Domain/ValueObjects'],
            'include'     => ['*'],   // glob patterns
            'exclude'     => [],
        ],
        'dtos' => [
            'directories' => ['Domain/DTOs', 'Http/Resources'],
        ],
        'packets' => [
            'directories' => ['Domain/Packets', 'Http/Packets'],
            'enums'       => ['Domain/Enums'],
            'enabled'     => false,   // opt-in; enable once you have packets
        ],
    ],

    'output' => [
        'value_objects' => null,  // resource_path('js/types/value-objects.ts')
        'dtos'          => null,  // resource_path('js/types/dtos.ts')
        'packets'       => null,  // resource_path('js/types/packets.ts')
        'barrel'        => null,  // resource_path('js/types/index.ts')
    ],

    'ts_asset_path' => null, // resource_path('js/utils/DataPacket.ts')
];
```

---

Requirements
------------

[](#requirements)

- PHP 8.4+
- Laravel 11 / 12 / 13
- [spatie/laravel-data](https://github.com/spatie/laravel-data) ^4.0
- [spatie/laravel-typescript-transformer](https://github.com/spatie/laravel-typescript-transformer) ^3.0
- [laravel/wayfinder](https://github.com/laravel/wayfinder) *(optional)*

License
-------

[](#license)

MIT

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance91

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 75% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

47d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b9e77e55bb8341a1fa99f8348597070b4de230b5fc457e866e4f196dd56843f1?d=identicon)[EmilienKopp](/maintainers/EmilienKopp)

---

Top Contributors

[![EmilienKopp](https://avatars.githubusercontent.com/u/91975560?v=4)](https://github.com/EmilienKopp "EmilienKopp (3 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/splitstack-laravel-typewriter/health.svg)

```
[![Health](https://phpackages.com/badges/splitstack-laravel-typewriter/health.svg)](https://phpackages.com/packages/splitstack-laravel-typewriter)
```

###  Alternatives

[laravel/sail

Docker files for running a basic Laravel application.

1.9k220.0M1.5k](/packages/laravel-sail)[laravel/ai

The official AI SDK for Laravel.

1.1k6.4M360](/packages/laravel-ai)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M270](/packages/laravel-mcp)[laravel/surveyor

Static analysis tool for Laravel applications.

89228.4k17](/packages/laravel-surveyor)[illuminate/queue

The Illuminate Queue package.

20433.5M1.9k](/packages/illuminate-queue)[propaganistas/laravel-disposable-email

Disposable email validator

6093.4M9](/packages/propaganistas-laravel-disposable-email)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
