Medienbibliothek: signierte Direkt-Uploads, echte Typprüfung, EXIF/GPS entfernt, Alt-Text, Fokuspunkt, srcset
Installieren
genpm add @core/mediaWas du bekommst
- Quellcode in src/lib/media/, 13 Dateien. (32,5 kB)
- KI-Regeln in src/lib/media/AGENTS.md, dazu Regeldateien für die IDE.
- Umgebungsvariablen in .env.example ergänzt: MEDIA_TRANSFORM.
- Löst @core/contracts, @core/db, @core/storage für dich auf.
README
Dieses Paket hat keine README.
Genau das liest deine KI, wenn sie in src/lib/media arbeitet. Sonst wird ihrem Kontext nichts hinzugefügt.
@core/media — rules for AI agents
Purpose
Media library on top of @core/storage: the browser uploads directly with a signed URL to a private staging key
(uploads/pending/<id>.<ext>), then confirmUpload checks the real file type by its bytes (declared type must match),
removes EXIF/GPS/XMP from JPEG, PNG and WebP without re-encoding, writes the validated bytes to the final public/
key, deletes the staging copy and stores dimensions. Malformed images (chunk lengths past the end) are rejected. Alt text and focal point per file, responsive URLs (Cloudflare Images or Next.js
image optimizer) and ready <img> attributes. SVG and HTML are never accepted. Table: media.
Map
index.ts— public API:createUpload,confirmUpload,getMedia,updateMedia,deleteMedia,imageUrl,imgAttrs,mediaAdminResource.remote.ts—importRemoteImage(url, { allowedHosts })for server-side imports (https, host allowlist, size limit).library.ts— upload flow, allowed types andstagingKey(media).sniff.ts,strip.ts— byte checks.urls.ts— URLs and<img>attributes.adapters/hono.ts—mediaRoutes({ authorize }).adapters/next.ts—createUploadRoute,confirmUploadRoute.
Integration
- Configure @core/storage first (bucket CORS must allow
PUTfrom the site). Recommended: a bucket lifecycle rule that deletesuploads/pending/objects after 1 day (abandoned or replayed uploads). OptionalMEDIA_TRANSFORM:cloudflare(Images transformations enabled on the zone),vercel(Next.js image optimizer; add the storage host toimages.remotePatterns) ornone. - Migrations as in
src/lib/db/AGENTS.md. - Mount the upload routes with an
authorize(req)that returns{ userId }only for users withmedia:create(@core/rbac). - Upload from the browser:
POST /api/media/uploadswith{ filename, mime, size }→PUTthe file toupload.urlwithupload.headers→POST /api/media/uploads/<id>/confirm. Store the mediaidin your content. - Render:
const m = await getMedia(id); <img {...imgAttrs(m, { sizes: '(min-width: 768px) 50vw, 100vw' })} />. - Add
mediaAdminResourcetosrc/genpm/admin.ts. - Verify: upload a photo with GPS data; after confirm, the stored file has no EXIF and the row has width/height.
Conventions
- Reference media by
idin content; resolve URLs at render time (driver or CDN can change). - Every non-decorative image needs
alt; the admin can filtermissingAlt=true. - Hero/LCP images:
imgAttrs(m, { priority: true }); everything else stays lazy.
Don't
- Don't accept files without
confirmUpload; pending or rejected media must never be shown. - Don't presign uploads to the final
public/key: a signed PUT can be replayed until it expires and would swap a validated file for an unchecked one (servedimmutable). Upload tostagingKey(); onlyconfirmUploadwritespublic/. - Don't allow SVG uploads or serve user files from the site's own origin without the storage route's sandbox headers.
- Don't hotlink third-party images; copy them into storage first.
# @core/media — rules for AI agents
## Purpose
Media library on top of @core/storage: the browser uploads directly with a signed URL to a private staging key
(`uploads/pending/<id>.<ext>`), then `confirmUpload` checks the real file type by its bytes (declared type must match),
removes EXIF/GPS/XMP from JPEG, PNG and WebP without re-encoding, writes the validated bytes to the final `public/`
key, deletes the staging copy and stores dimensions. Malformed images (chunk lengths past the end) are rejected. Alt text and focal point per file, responsive URLs (Cloudflare Images or Next.js
image optimizer) and ready `<img>` attributes. SVG and HTML are never accepted. Table: `media`.
## Map
- `index.ts` — public API: `createUpload`, `confirmUpload`, `getMedia`, `updateMedia`, `deleteMedia`, `imageUrl`, `imgAttrs`, `mediaAdminResource`.
- `remote.ts` — `importRemoteImage(url, { allowedHosts })` for server-side imports (https, host allowlist, size limit).
- `library.ts` — upload flow, allowed types and `stagingKey(media)`. `sniff.ts`, `strip.ts` — byte checks. `urls.ts` — URLs and `<img>` attributes.
- `adapters/hono.ts` — `mediaRoutes({ authorize })`. `adapters/next.ts` — `createUploadRoute`, `confirmUploadRoute`.
## Integration
1. Configure @core/storage first (bucket CORS must allow `PUT` from the site). Recommended: a bucket lifecycle rule
that deletes `uploads/pending/` objects after 1 day (abandoned or replayed uploads). Optional `MEDIA_TRANSFORM`:
`cloudflare` (Images transformations enabled on the zone), `vercel` (Next.js image optimizer; add the storage host to `images.remotePatterns`) or `none`.
2. Migrations as in `src/lib/db/AGENTS.md`.
3. Mount the upload routes with an `authorize(req)` that returns `{ userId }` only for users with `media:create` (@core/rbac).
4. Upload from the browser: `POST /api/media/uploads` with `{ filename, mime, size }` → `PUT` the file to `upload.url`
with `upload.headers` → `POST /api/media/uploads/<id>/confirm`. Store the media `id` in your content.
5. Render: `const m = await getMedia(id); <img {...imgAttrs(m, { sizes: '(min-width: 768px) 50vw, 100vw' })} />`.
6. Add `mediaAdminResource` to `src/genpm/admin.ts`.
7. Verify: upload a photo with GPS data; after confirm, the stored file has no EXIF and the row has width/height.
## Conventions
- Reference media by `id` in content; resolve URLs at render time (driver or CDN can change).
- Every non-decorative image needs `alt`; the admin can filter `missingAlt=true`.
- Hero/LCP images: `imgAttrs(m, { priority: true })`; everything else stays lazy.
## Don't
- Don't accept files without `confirmUpload`; pending or rejected media must never be shown.
- Don't presign uploads to the final `public/` key: a signed PUT can be replayed until it expires and would swap a
validated file for an unchecked one (served `immutable`). Upload to `stagingKey()`; only `confirmUpload` writes `public/`.
- Don't allow SVG uploads or serve user files from the site's own origin without the storage route's sandbox headers.
- Don't hotlink third-party images; copy them into storage first.
Der genaue Baum, der nach .genpmignore eingebunden wird. Gepinnt an
MIT License
Copyright (c) 2026 GenPM
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Dieses Paket deklariert keine MCP-Server.
| Version | Commit | Veröffentlicht | Prüfung |
|---|---|---|---|
| 1.1.0 | 868490c | vor 4 Stunden | Prüfung bestanden |
- npm
- zod ^4.0.0
- vorgeschlagen
- GenPM schlägt den npm-Befehl vor und führt ihn nur aus, wenn du zustimmst.
- Verwendet von (3)
- @core/blocks ^1.0.0@core/kit-cms ^1.0.0@core/suppliers ^1.0.0
- Prüfung
- Prüfung bestanden · 0 Befunde
- Commit
- v1.1.0 → 868490cea569361668f1903822e20c1becc618d4 · nach dem Abruf verifiziert
- Skripte
- Keine. GenPM führt niemals Paketcode aus.
- Lizenz
- MIT
- Qualität
- 100/100
- Anerkannte Lizenzerfüllt
- AGENTS.md erklärt den Zweckerfüllt
- AGENTS.md enthält Integrationsschritteerfüllt
- AGENTS.md nennt Konventionen oder Verboteerfüllt
- Enthält Testserfüllt
- Sicherheitsscan bestandenerfüllt
- In den letzten 6 Monaten veröffentlichterfüllt
- Verifizierter Herausgebererfüllt
- Zusammenfassung und Schlagwörtererfüllt
- Meldung
- Stimmt etwas nicht?