Files
suaveplan/openspec/archive/2026-05-23-enterprise-streams-metadata/proposal.md
2026-05-27 23:29:53 +01:00

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:

  1. MimeTransform — magic-bytes detection (priority) → extension fallback → application/octet-stream.
  2. ExifTransform — ImageMetadata via @suaveplan/exif::readMetadata.
  3. MetadataTransform — composing wrapper running MIME then EXIF, forwarding child events, catching errors, emitting metadata:complete / metadata:error.
  4. Typed event surface via BaseTransform.on/emit (genesis @suaveplan/events no longer exports an EventEmitter class).
  5. Typed StreamsMetadataError with defineCodes("STREAMS_METADATA", …) registry.
  6. 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/events does not export EventEmitter. The migration switches to BaseTransform's built-in on/emit, changing the public observation channel from transform.events.on(...) to transform.on(...). This is UP (upgraded) not NO (dropped) because the observable surface is unchanged.
  • The genesis @suaveplan/mime dist 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 via registerSignature in src/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 Uint8Array exactly 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/mime and @suaveplan/exif already provide.