Initial Commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user