Maintain local ⭤ remote in sync with automatic AT Protocol parity for Laravel (alpha & unstable)

Update README with correct dependency versions

Changed files
+397 -28
+397 -28
README.md
··· 1 - # AtpReplicator 1 + [![Parity Header](./header.png)](https://github.com/socialdept/atp-parity) 2 + 3 + <h3 align="center"> 4 + Bidirectional mapping between AT Protocol records and Laravel Eloquent models. 5 + </h3> 6 + 7 + <p align="center"> 8 + <br> 9 + <a href="https://packagist.org/packages/socialdept/atp-parity" title="Latest Version on Packagist"><img src="https://img.shields.io/packagist/v/socialdept/atp-parity.svg?style=flat-square"></a> 10 + <a href="https://packagist.org/packages/socialdept/atp-parity" title="Total Downloads"><img src="https://img.shields.io/packagist/dt/socialdept/atp-parity.svg?style=flat-square"></a> 11 + <a href="https://github.com/socialdept/atp-parity/actions/workflows/tests.yml" title="GitHub Tests Action Status"><img src="https://img.shields.io/github/actions/workflow/status/socialdept/atp-parity/tests.yml?branch=main&label=tests&style=flat-square"></a> 12 + <a href="LICENSE" title="Software License"><img src="https://img.shields.io/github/license/socialdept/atp-parity?style=flat-square"></a> 13 + </p> 14 + 15 + --- 16 + 17 + ## What is Parity? 18 + 19 + **Parity** is a Laravel package that bridges your Eloquent models with AT Protocol records. It provides bidirectional mapping, automatic firehose synchronization, and type-safe transformations between your database and the decentralized social web. 20 + 21 + Think of it as Laravel's model casts, but for AT Protocol records. 22 + 23 + ## Why use Parity? 2 24 3 - [![Latest Version on Packagist][ico-version]][link-packagist] 4 - [![Total Downloads][ico-downloads]][link-downloads] 5 - [![Build Status][ico-travis]][link-travis] 6 - [![StyleCI][ico-styleci]][link-styleci] 25 + - **Laravel-style code** - Familiar patterns you already know 26 + - **Bidirectional mapping** - Transform records to models and back 27 + - **Firehose sync** - Automatically sync network events to your database 28 + - **Type-safe DTOs** - Full integration with atp-schema generated types 29 + - **Model traits** - Add AT Protocol awareness to any Eloquent model 30 + - **Flexible mappers** - Define custom transformations for your domain 31 + 32 + ## Quick Example 33 + 34 + ```php 35 + use SocialDept\AtpParity\RecordMapper; 36 + use SocialDept\AtpSchema\Data\Data; 37 + use Illuminate\Database\Eloquent\Model; 38 + 39 + class PostMapper extends RecordMapper 40 + { 41 + public function recordClass(): string 42 + { 43 + return \SocialDept\AtpSchema\Generated\App\Bsky\Feed\Post::class; 44 + } 45 + 46 + public function modelClass(): string 47 + { 48 + return \App\Models\Post::class; 49 + } 50 + 51 + protected function recordToAttributes(Data $record): array 52 + { 53 + return [ 54 + 'content' => $record->text, 55 + 'published_at' => $record->createdAt, 56 + ]; 57 + } 7 58 8 - This is where your description should go. Take a look at [contributing.md](contributing.md) to see a to do list. 59 + protected function modelToRecordData(Model $model): array 60 + { 61 + return [ 62 + 'text' => $model->content, 63 + 'createdAt' => $model->published_at->toIso8601String(), 64 + ]; 65 + } 66 + } 67 + ``` 9 68 10 69 ## Installation 11 70 12 - Via Composer 71 + ```bash 72 + composer require socialdept/atp-parity 73 + ``` 74 + 75 + Optionally publish the configuration: 76 + 77 + ```bash 78 + php artisan vendor:publish --tag=parity-config 79 + ``` 80 + 81 + ## Getting Started 82 + 83 + Once installed, you're three steps away from syncing AT Protocol records: 84 + 85 + ### 1. Create a Mapper 86 + 87 + Define how your record maps to your model: 88 + 89 + ```php 90 + class PostMapper extends RecordMapper 91 + { 92 + public function recordClass(): string 93 + { 94 + return Post::class; // Your atp-schema DTO or custom Record 95 + } 96 + 97 + public function modelClass(): string 98 + { 99 + return \App\Models\Post::class; 100 + } 101 + 102 + protected function recordToAttributes(Data $record): array 103 + { 104 + return ['content' => $record->text]; 105 + } 106 + 107 + protected function modelToRecordData(Model $model): array 108 + { 109 + return ['text' => $model->content]; 110 + } 111 + } 112 + ``` 113 + 114 + ### 2. Register Your Mapper 115 + 116 + ```php 117 + // config/parity.php 118 + return [ 119 + 'mappers' => [ 120 + App\AtpMappers\PostMapper::class, 121 + ], 122 + ]; 123 + ``` 124 + 125 + ### 3. Add the Trait to Your Model 126 + 127 + ```php 128 + use SocialDept\AtpParity\Concerns\HasAtpRecord; 129 + 130 + class Post extends Model 131 + { 132 + use HasAtpRecord; 133 + } 134 + ``` 135 + 136 + Your model can now convert to/from AT Protocol records and query by URI. 137 + 138 + ## What can you build? 139 + 140 + - **Data mirrors** - Keep local copies of AT Protocol data 141 + - **AppViews** - Build custom applications with synced data 142 + - **Analytics platforms** - Store and analyze network activity 143 + - **Content aggregators** - Collect and organize posts locally 144 + - **Moderation tools** - Track and manage content in your database 145 + - **Hybrid applications** - Combine local and federated data 146 + 147 + ## Ecosystem Integration 148 + 149 + Parity is designed to work seamlessly with the other atp-* packages: 150 + 151 + | Package | Integration | 152 + |---------|-------------| 153 + | **atp-schema** | Records extend `Data`, use generated DTOs directly | 154 + | **atp-client** | `RecordHelper` for fetching and hydrating records | 155 + | **atp-signals** | `ParitySignal` for automatic firehose sync | 156 + 157 + ### Using with atp-schema 158 + 159 + Use generated schema classes directly with `SchemaMapper`: 160 + 161 + ```php 162 + use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Post; 163 + use SocialDept\AtpParity\Support\SchemaMapper; 164 + 165 + $mapper = new SchemaMapper( 166 + schemaClass: Post::class, 167 + modelClass: \App\Models\Post::class, 168 + toAttributes: fn(Post $p) => [ 169 + 'content' => $p->text, 170 + 'published_at' => $p->createdAt, 171 + ], 172 + toRecordData: fn($m) => [ 173 + 'text' => $m->content, 174 + 'createdAt' => $m->published_at->toIso8601String(), 175 + ], 176 + ); 177 + 178 + $registry->register($mapper); 179 + ``` 180 + 181 + ### Using with atp-client 182 + 183 + Fetch records by URI and convert directly to models: 184 + 185 + ```php 186 + use SocialDept\AtpParity\Support\RecordHelper; 187 + 188 + $helper = app(RecordHelper::class); 189 + 190 + // Fetch as typed DTO 191 + $record = $helper->fetch('at://did:plc:xxx/app.bsky.feed.post/abc123'); 192 + 193 + // Fetch and convert to model (unsaved) 194 + $post = $helper->fetchAsModel('at://did:plc:xxx/app.bsky.feed.post/abc123'); 195 + 196 + // Fetch and sync to database (upsert) 197 + $post = $helper->sync('at://did:plc:xxx/app.bsky.feed.post/abc123'); 198 + ``` 199 + 200 + The helper automatically resolves the DID to find the correct PDS endpoint, so it works with any AT Protocol server - not just Bluesky. 201 + 202 + ### Using with atp-signals 203 + 204 + Enable automatic firehose synchronization by registering the `ParitySignal`: 205 + 206 + ```php 207 + // config/signal.php 208 + return [ 209 + 'signals' => [ 210 + \SocialDept\AtpParity\Signals\ParitySignal::class, 211 + ], 212 + ]; 213 + ``` 214 + 215 + Run `php artisan signal:consume` and your models will automatically sync with matching firehose events. 216 + 217 + ### Importing Historical Data 218 + 219 + For existing records created before you started consuming the firehose: 13 220 14 221 ```bash 15 - composer require socialdept/atp-parity 222 + # Import a user's records 223 + php artisan parity:import did:plc:z72i7hdynmk6r22z27h6tvur 224 + 225 + # Check import status 226 + php artisan parity:import-status 227 + ``` 228 + 229 + Or programmatically: 230 + 231 + ```php 232 + use SocialDept\AtpParity\Import\ImportService; 233 + 234 + $service = app(ImportService::class); 235 + $result = $service->importUser('did:plc:z72i7hdynmk6r22z27h6tvur'); 236 + 237 + echo "Synced {$result->recordsSynced} records"; 16 238 ``` 17 239 18 - ## Usage 240 + ## Documentation 241 + 242 + For detailed documentation on specific topics: 243 + 244 + - [Record Mappers](docs/mappers.md) - Creating and using mappers 245 + - [Model Traits](docs/traits.md) - HasAtpRecord and SyncsWithAtp 246 + - [atp-schema Integration](docs/atp-schema-integration.md) - Using generated DTOs 247 + - [atp-client Integration](docs/atp-client-integration.md) - RecordHelper and fetching 248 + - [atp-signals Integration](docs/atp-signals-integration.md) - ParitySignal and firehose sync 249 + - [Importing](docs/importing.md) - Syncing historical data 19 250 20 - ## Change log 251 + ## Model Traits 21 252 22 - Please see the [changelog](changelog.md) for more information on what has changed recently. 253 + ### HasAtpRecord 254 + 255 + Add AT Protocol awareness to your models: 256 + 257 + ```php 258 + use SocialDept\AtpParity\Concerns\HasAtpRecord; 259 + 260 + class Post extends Model 261 + { 262 + use HasAtpRecord; 263 + 264 + protected $fillable = ['content', 'atp_uri', 'atp_cid']; 265 + } 266 + ``` 267 + 268 + Available methods: 269 + 270 + ```php 271 + // Get AT Protocol metadata 272 + $post->getAtpUri(); // at://did:plc:xxx/app.bsky.feed.post/rkey 273 + $post->getAtpCid(); // bafyre... 274 + $post->getAtpDid(); // did:plc:xxx (extracted from URI) 275 + $post->getAtpCollection(); // app.bsky.feed.post (extracted from URI) 276 + $post->getAtpRkey(); // rkey (extracted from URI) 277 + 278 + // Check sync status 279 + $post->hasAtpRecord(); // true if synced 280 + 281 + // Convert to record DTO 282 + $record = $post->toAtpRecord(); 283 + 284 + // Query scopes 285 + Post::withAtpRecord()->get(); // Only synced posts 286 + Post::withoutAtpRecord()->get(); // Only unsynced posts 287 + Post::whereAtpUri($uri)->first(); // Find by URI 288 + ``` 289 + 290 + ### SyncsWithAtp 291 + 292 + Extended trait for bidirectional sync tracking: 293 + 294 + ```php 295 + use SocialDept\AtpParity\Concerns\SyncsWithAtp; 296 + 297 + class Post extends Model 298 + { 299 + use SyncsWithAtp; 300 + } 301 + ``` 302 + 303 + Additional methods: 304 + 305 + ```php 306 + // Track sync status 307 + $post->getAtpSyncedAt(); // Last sync timestamp 308 + $post->hasLocalChanges(); // True if updated since last sync 309 + 310 + // Mark as synced 311 + $post->markAsSynced($uri, $cid); 312 + 313 + // Update from remote 314 + $post->updateFromRecord($record, $uri, $cid); 315 + ``` 316 + 317 + ## Database Migration 318 + 319 + Add AT Protocol columns to your models: 320 + 321 + ```php 322 + Schema::table('posts', function (Blueprint $table) { 323 + $table->string('atp_uri')->nullable()->unique(); 324 + $table->string('atp_cid')->nullable(); 325 + $table->timestamp('atp_synced_at')->nullable(); // For SyncsWithAtp 326 + }); 327 + ``` 328 + 329 + ## Configuration 330 + 331 + ```php 332 + // config/parity.php 333 + return [ 334 + // Registered mappers 335 + 'mappers' => [ 336 + App\AtpMappers\PostMapper::class, 337 + App\AtpMappers\ProfileMapper::class, 338 + ], 339 + 340 + // Column names for AT Protocol metadata 341 + 'columns' => [ 342 + 'uri' => 'atp_uri', 343 + 'cid' => 'atp_cid', 344 + ], 345 + ]; 346 + ``` 347 + 348 + ## Creating Custom Records 349 + 350 + Extend the `Record` base class for custom AT Protocol records: 351 + 352 + ```php 353 + use SocialDept\AtpParity\Data\Record; 354 + use Carbon\Carbon; 355 + 356 + class PostRecord extends Record 357 + { 358 + public function __construct( 359 + public readonly string $text, 360 + public readonly Carbon $createdAt, 361 + public readonly ?array $facets = null, 362 + ) {} 363 + 364 + public static function getLexicon(): string 365 + { 366 + return 'app.bsky.feed.post'; 367 + } 368 + 369 + public static function fromArray(array $data): static 370 + { 371 + return new static( 372 + text: $data['text'], 373 + createdAt: Carbon::parse($data['createdAt']), 374 + facets: $data['facets'] ?? null, 375 + ); 376 + } 377 + } 378 + ``` 379 + 380 + The `Record` class extends `atp-schema`'s `Data` and implements `atp-client`'s `Recordable` interface, ensuring full compatibility with the ecosystem. 381 + 382 + ## Requirements 383 + 384 + - PHP 8.2+ 385 + - Laravel 10, 11, or 12 386 + - [socialdept/atp-schema](https://github.com/socialdept/atp-schema) ^0.3 387 + - [socialdept/atp-client](https://github.com/socialdept/atp-client) ^0.0 388 + - [socialdept/atp-resolver](https://github.com/socialdept/atp-resolver) ^1.1 389 + - [socialdept/atp-signals](https://github.com/socialdept/atp-signals) ^1.1 23 390 24 391 ## Testing 25 392 ··· 27 394 composer test 28 395 ``` 29 396 30 - ## Contributing 397 + ## Resources 398 + 399 + - [AT Protocol Documentation](https://atproto.com/) 400 + - [Bluesky API Docs](https://docs.bsky.app/) 401 + - [atp-schema](https://github.com/socialdept/atp-schema) - Generated AT Protocol DTOs 402 + - [atp-client](https://github.com/socialdept/atp-client) - AT Protocol HTTP client 403 + - [atp-signals](https://github.com/socialdept/atp-signals) - Firehose event consumer 404 + 405 + ## Support & Contributing 406 + 407 + Found a bug or have a feature request? [Open an issue](https://github.com/socialdept/atp-parity/issues). 31 408 32 - Please see [contributing.md](contributing.md) for details and a todolist. 409 + Want to contribute? Check out the [contribution guidelines](contributing.md). 33 410 34 - ## Security 411 + ## Changelog 35 412 36 - If you discover any security related issues, please email author@email.com instead of using the issue tracker. 413 + Please see [changelog](changelog.md) for recent changes. 37 414 38 415 ## Credits 39 416 40 - - [Author Name][link-author] 41 - - [All Contributors][link-contributors] 417 + - [Miguel Batres](https://batres.co) - founder & lead maintainer 418 + - [All contributors](https://github.com/socialdept/atp-parity/graphs/contributors) 42 419 43 420 ## License 44 421 45 - MIT. Please see the [license file](license.md) for more information. 422 + Parity is open-source software licensed under the [MIT license](license.md). 46 423 47 - [ico-version]: https://img.shields.io/packagist/v/socialdept/atp-parity.svg?style=flat-square 48 - [ico-downloads]: https://img.shields.io/packagist/dt/socialdept/atp-parity.svg?style=flat-square 49 - [ico-travis]: https://img.shields.io/travis/socialdept/atp-parity/master.svg?style=flat-square 50 - [ico-styleci]: https://styleci.io/repos/12345678/shield 424 + --- 51 425 52 - [link-packagist]: https://packagist.org/packages/socialdept/atp-parity 53 - [link-downloads]: https://packagist.org/packages/socialdept/atp-parity 54 - [link-travis]: https://travis-ci.org/socialdept/atp-parity 55 - [link-styleci]: https://styleci.io/repos/12345678 56 - [link-author]: https://github.com/social-dept 57 - [link-contributors]: ../../contributors 426 + **Built for the Federation** - By Social Dept.