Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
Media
pushword/search
cursor
symfony
search
cms
sqlite
full-text-search
loupe
pushword

(MediaExtension::transformStringToMedia()) calls setAlt($caption, true) whenever content containing caption renders, mutating alt in memory only — the DB still holds the previous value, and the render runs before AfterEntityUpdatedEvent. Any rename or diff heuristic must read the persisted alt...

Install
php artisan boost:add-skill pushword/search

Save this content to: .cursor/rules/media.md

# Media and image cache

- **`Media.alt` has two values during a request.** The `media_from_string()` Twig function
  (`MediaExtension::transformStringToMedia()`) calls `setAlt($caption, true)` whenever
  content containing `![caption](src)` renders, mutating alt **in memory only** — the DB
  still holds the previous value, and the render runs before `AfterEntityUpdatedEvent`.
  Any rename or diff heuristic must read the *persisted* alt via
  `getUnitOfWork()->getOriginalEntityData($media)['alt']`, not the live one.
  `Media::isFileNameAutoGenerated(?string $referenceAlt)` takes the reference alt for
  exactly this reason.
- **A 0-byte derivative means an in-place optimizer truncated it, not an encode failure.**
  `ImageEncoder` (vips) is atomic and rejects empty payloads — a real encode failure
  throws and leaves the file *missing*. A genuinely 0-byte file is the fingerprint of an
  external binary writing its own target (`cwebp … 'x.webp' -o 'x.webp'`) and being killed
  mid-write. Optimize into a temp copy, validate (non-empty + `getimagesize`), then
  atomically rename. Treat 0-byte cache files as missing everywhere they are read.
- Generalize that shape: any "truncated cache file, non-deterministic, size-correlated,
  silent RC=0" symptom points at an external binary writing in place, not at the primary
  encoder.
- **A gd decode counts against `memory_limit`, and overrunning it is a fatal no `catch`
  reaches.** `ImageReader::read()` asks for width × height × 4 bytes in one allocation —
  192 MB for a 48 Mpx master — so any decode on a request-side path has to be declined
  first (`ImageCacheGenerator::decodeFitsIn()`), never wrapped in a `try`. imagick and vips
  allocate outside the limit, which is why the check is gd-only. Beware when reproducing:
  whether gd allocations are counted depends on the PHP build (they were under FrankenPHP's
  8.4, not under the local CLI 8.5), so a local `memory_get_peak_usage()` of 17 MB proves
  nothing about production — reproduce through the real SAPI.
- **Filesystem work owed to a media write belongs after the commit, never in `preUpdate`.**
  `MediaStorageListener` decides a rename in `preUpdate` (so an impossible one still rolls
  the row back) and moves the file in `postFlush`. A move inside the transaction is
  destructive by construction: anything throwing later in the same flush rolls back the row
  and leaves the DB naming a file that was renamed away — the whole site 404s on it. The
  `preRemove` file delete still has that shape.
- **Derivatives are written to `pw.media_cache_dir`, never to `public_dir`/`public_media_dir`.**
  The default is `public/{public_media_dir}/`, so nothing moves for a site; `public_media_dir`
  alone stays what the *browser* path is built from. Compute a derivative path from anything
  else and you reintroduce the leak that made a `pw:static` build ship 17k OG images of pages
  that only ever existed inside a test.

Media and image cache

  • Media.alt has two values during a request. The media_from_string() Twig function (MediaExtension::transformStringToMedia()) calls setAlt($caption, true) whenever content containing ![caption](src) renders, mutating alt in memory only — the DB still holds the previous value, and the render runs before AfterEntityUpdatedEvent. Any rename or diff heuristic must read the persisted alt via getUnitOfWork()->getOriginalEntityData($media)['alt'], not the live one. Media::isFileNameAutoGenerated(?string $referenceAlt) takes the reference alt for exactly this reason.
  • A 0-byte derivative means an in-place optimizer truncated it, not an encode failure. ImageEncoder (vips) is atomic and rejects empty payloads — a real encode failure throws and leaves the file missing. A genuinely 0-byte file is the fingerprint of an external binary writing its own target (cwebp … 'x.webp' -o 'x.webp') and being killed mid-write. Optimize into a temp copy, validate (non-empty + getimagesize), then atomically rename. Treat 0-byte cache files as missing everywhere they are read.
  • Generalize that shape: any "truncated cache file, non-deterministic, size-correlated, silent RC=0" symptom points at an external binary writing in place, not at the primary encoder.
  • A gd decode counts against memory_limit, and overrunning it is a fatal no catch reaches. ImageReader::read() asks for width × height × 4 bytes in one allocation — 192 MB for a 48 Mpx master — so any decode on a request-side path has to be declined first (ImageCacheGenerator::decodeFitsIn()), never wrapped in a try. imagick and vips allocate outside the limit, which is why the check is gd-only. Beware when reproducing: whether gd allocations are counted depends on the PHP build (they were under FrankenPHP's 8.4, not under the local CLI 8.5), so a local memory_get_peak_usage() of 17 MB proves nothing about production — reproduce through the real SAPI.
  • Filesystem work owed to a media write belongs after the commit, never in preUpdate. MediaStorageListener decides a rename in preUpdate (so an impossible one still rolls the row back) and moves the file in postFlush. A move inside the transaction is destructive by construction: anything throwing later in the same flush rolls back the row and leaves the DB naming a file that was renamed away — the whole site 404s on it. The preRemove file delete still has that shape.
  • Derivatives are written to pw.media_cache_dir, never to public_dir/public_media_dir. The default is public/{public_media_dir}/, so nothing moves for a site; public_media_dir alone stays what the browser path is built from. Compute a derivative path from anything else and you reintroduce the leak that made a pw:static build ship 17k OG images of pages that only ever existed inside a test.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource