5.0 KiB
Proposal: enterprise-streams-metadata
- Change ID: enterprise-streams-metadata
- Package:
@suaveplan/streams-metadata - Status: PROPOSED
- Target path:
packages/foundation/flow/streams-metadata - Spec delta: ./specs/streams-metadata/spec.md
- Upstream source:
/mnt/s/web/suaveplan/packages/universal/streams-metadata - Upstream enterprise audit:
/mnt/s/web/suaveplan/enterprise/streams-metadata.md
Why
@suaveplan/streams-metadata provides a composable, stream-oriented pipeline
for extracting MIME-type and EXIF/image metadata from Uint8Array payloads.
Genesis already ships the prerequisites (@suaveplan/streams,
@suaveplan/mime, @suaveplan/exif, @suaveplan/events) at 100/100/100/100
coverage — this change ports the metadata package on top of them at the same
quality bar.
This proposal supersedes an earlier draft in this folder that designed a
side-channel Promise<ExtractedMetadata> API and folded in audio metadata.
The upstream source (which shipped to consumers) implements three
BaseTransform subclasses with a typed event surface; migration parity
(openspec/AGENTS.md §0a) mandates shipping that contract, not a redesign.
What
A fresh universal-tier package providing:
MimeTransform— magic-bytes detection (priority) → extension fallback →application/octet-stream.ExifTransform—ImageMetadatavia@suaveplan/exif::readMetadata.MetadataTransform— composing wrapper running MIME then EXIF, forwarding child events, catching errors, emittingmetadata:complete/metadata:error.- Typed event surface via
BaseTransform.on/emit(genesis@suaveplan/eventsno longer exports anEventEmitterclass). - Typed
StreamsMetadataErrorwithdefineCodes("STREAMS_METADATA", …)registry. - Subpath exports per feature directory (CLAUDE.md §6).
Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
MimeTransform (enrich / passthrough) |
YES | YES | |
ExifTransform (enrich / passthrough, read pass-through) |
YES | YES | |
MetadataTransform wrapper |
YES | YES | |
Typed events (mime:detected, exif:detected, metadata:complete, metadata:error) |
YES | UP | Migrated from EventEmitter<Events> field to BaseTransform.on/emit. Genesis @suaveplan/events has no EventEmitter class. Observable surface unchanged. |
EnrichedPayload / MetadataPayload / MimeDetectionResult / MetadataMode |
YES | YES | |
| Detection precedence (magic → extension → fallback) | YES | YES | |
ExifTransform raise on unknown format |
YES | UP | Upstream threw plain Error. Migration lets @suaveplan/exif::readMetadata throw ExifError(UNSUPPORTED_FORMAT), re-wrapped as ExifFailedError(STREAMS_METADATA.EXIF_FAILED) with cause preserved. |
| Child-event forwarding through the wrapper | YES | YES | |
metadata:error + original-input return on failure |
YES | YES | |
Stable error codes (STREAMS_METADATA.*) |
NO | UP | Upstream had no registry. Migration adds STREAMS_METADATA_CODES via defineCodes. |
| Subpath exports per feature dir | NO | UP | Upstream exported only .. Migration adds per-feature subpaths for tree-shaking. |
@suaveplan/testing/runner test imports |
NO | UP | Upstream imported from vitest. Migration uses canonical wrapper per CLAUDE.md §11. |
Sibling .md per .ts with all six sections |
NO | UP | Upstream shipped partial docs. Migration ships full coverage. |
| 100/100/100/100 coverage gate | NO | UP | Upstream did not enforce 100%. Migration enforces it. |
Impact
Affected dependents
None inside genesis at change time — this package is a fresh universal-tier
leaf in this monorepo. Future consumers (@suaveplan/api-plugin-file-upload)
will consume after archive.
Risks
- Genesis
@suaveplan/eventsdoes not exportEventEmitter. The migration switches toBaseTransform's built-inon/emit, changing the public observation channel fromtransform.events.on(...)totransform.on(...). This isUP(upgraded) notNO(dropped) because the observable surface is unchanged. - The genesis
@suaveplan/mimedist tree-shakes its signature-registration side effect, so external consumers (including this package) get an empty registry at runtime. The migration installs explicit JPEG / PNG signatures viaregisterSignatureinsrc/mime/mime.ts's module load so detection works end-to-end without depending on an upstream fix. This is a temporary workaround — a future change will fix the mime dist build so the registration is universal.
Out of scope
- Streaming-mode (chunked) MIME / EXIF detection — both transforms operate
on a fully buffered
Uint8Arrayexactly as upstream did. - Audio metadata extraction — out of scope; reserved for a follow-up change.
- Side-channel metadata promises / 64 KiB byte caps — not part of the upstream contract.
- Adding new media format support beyond what
@suaveplan/mimeand@suaveplan/exifalready provide.