7.4 KiB
Change: enterprise-video
- Package:
@suaveplan/video - Status: PROPOSED
- Design: ./design.md
- Spec delta: ./specs/video/spec.md
- Layout:
packages/media/video/video/ - Tier: universal (no Node-only / browser-only APIs in
src/) - Genesis deps:
@suaveplan/errorshipped,@suaveplan/eventsshipped
Why
@suaveplan/video is the canonical universal-tier video-metadata and
transform-planning capability on the suaveplan platform. It underpins the
pending @suaveplan/video-thumbnails (ffmpeg-backed thumbnail renderer) and
@suaveplan/react-video (browser playback) siblings, both of which read
VideoMetadata/VideoTransformPipeline/VideoTransformResult from this
package. Upstream ships 1517 LOC covering magic-byte format detection,
metadata extraction with optional caller-supplied probe data, resolution
presets, an evented VideoProcessor + fluent VideoPipelineBuilder, and a
15-factory typed error registry with HTTP/severity/retry metadata.
Genesis must own this capability before the siblings can land. The legacy
package uses the deprecated BaseError / ErrorCodes.registerMany registry
and the Node-style EventEmitter<[T]> array-tuple shape — genesis migrates
these to SuaveplanError + defineCodes("VIDEO", …) + EventBus<TMap> while
preserving the full public surface and adding the enterprise quality bar the
audit calls for: Zod boundary validation, OTel counters + histograms + spans,
AbortSignal propagation, schema-validated config, deterministic clocks,
100/100/100/100 coverage, and tier-pure imports.
What changes
- New package
packages/media/video/video/at@suaveplan/video@0.1.0. assignment.yml__categories__:row updated:medianow includes thevideoplugin folder (alongsideaudio/image/fonts/etc.).- Full upstream public surface preserved — every type, every constant, every
error factory, every method on
VideoProcessor/VideoPipelineBuilder. VideoErrormigrates fromBaseError<Code, Context>to aSuaveplanError<VideoCode>subclass that retains a typedcontextfield; codes move fromErrorCodes.registerManytodefineCodes("VIDEO", […]).VideoProcessormigrates from Node-styleEventEmitter<VideoEventMap>(array-tuple payloads) to composingEventBus<VideoEventMap>(single payload per topic, matching@suaveplan/image's convention) and exposeson/once/off.- Enterprise enhancements: Zod-validated config + pipeline boundaries, OTel
meter
@suaveplan/videoexposing 2 counters + 2 histograms + named spans,AbortSignalon every public entrypoint that can do work, injectableClock. - Co-located
.mddocs per implementation file (≥200 words, 6 required sections), package-rootREADME.mdwith a Modules index. - Subpath exports per feature module so consumers can tree-shake.
Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
VideoFormat / VideoCodec / AudioCodec / ResolutionPreset enums |
x | x | Re-expressed as Zod enums + inferred types. |
VideoDimensions / VideoMetadata / TrimRange / VideoResizeOptions |
x | x | Mirrored with Zod schemas (VideoMetadataSchema etc.). |
VideoTransformOp discriminated union (9 variants) |
x | x | Migrated to z.discriminatedUnion for boundary validation. |
VideoTransformPipeline / VideoTransformResult |
x | x | Schemas + inferred types. |
VideoEventMap |
x | up | Re-shaped from [T] tuples to single-payload T to match EventBus<TMap>. Semantic metadata.extracted / transform.planned / pipeline.built keys preserved. |
detectVideoFormat (MP4/MOV/MKV/AVI/FLV/OGG magic bytes) |
x | x | Byte-for-byte identical sniffer. |
calculateVideoResize (preset + maintainAspectRatio + even-dimensions) |
x | x | Identical algorithm; Zod-validated input. |
validateTrimRange |
x | x | Identical. |
formatDuration / parseDuration round-trip |
x | x | Identical. |
videoFormatToExtension |
x | x | Identical. |
RESOLUTION_PRESETS (6 presets) |
x | x | Identical. |
VIDEO_MIME_TYPES (8 formats) |
x | x | Identical. |
VideoError typed error |
x | up | Migrated to SuaveplanError<VideoCode>; codes namespaced via defineCodes("VIDEO", …). |
| 15 error-factory functions | x | x | All preserved: unknownVideoFormat, invalidVideoDimensions, invalidTrimRange, videoMetadataParseFailed, invalidVideoTransform, durationExceeded, unsupportedCodec, videoNotFound, videoDecodeFailed, videoEncodeFailed, videoFormatUnsupported, videoTooLarge, videoProcessingFailed, videoCodecUnsupported, videoTimeout. |
VIDEO_ERROR_METADATA HTTP/severity/retry table |
x | x | Preserved as a typed const for problem+json / retry consumers. |
registerVideoErrorCodes callable |
x | up | Kept as a callable; genesis defineCodes is module-local so the function is a no-op alias returning the metadata table — same name, same shape, callers keep compiling. |
VideoProcessor.extractMetadata |
x | up | Adds AbortSignal, OTel counter + histogram + span, Zod-validated config, deterministic clock. |
VideoProcessor.planTransform (9 op types) |
x | up | Adds AbortSignal, Zod-validated pipeline, OTel counter + histogram + span. |
VideoProcessor.createPipeline() |
x | x | Identical. |
VideoPipelineBuilder (resize/trim/rotate/flip/speed/mute/toFormat/codec/bitrate/build/apply) |
x | x | Identical fluent surface. |
VideoProcessorConfig (maxDuration, defaultFormat) |
x | up | Zod-validated; configs that fail validation throw VideoError(VIDEO.CONFIG_INVALID). |
pipeline.built event |
x | up | Now actually emitted from VideoPipelineBuilder.build() (upstream declared but never fired). |
Side-effect import "./error-codes.js" registration |
x | down | Genesis defineCodes is module-local and side-effect-free; equivalent metadata is exposed via VIDEO_ERROR_METADATA re-export and registerVideoErrorCodes() is retained as a no-op alias. |
The single down row (the side-effect import) is structurally replaced by
the defineCodes model — same callers, same outcome, no functional drop.
Impact
Affected dependents
bunx turbo ls --filter=...@suaveplan/video — package is brand-new on genesis,
so no genesis consumers exist yet. The future @suaveplan/video-thumbnails
(ffmpeg renderer) and @suaveplan/react-video (browser playback) packages
will consume this; both are still - (pending) in STATUS.md and will be
migrated in follow-up changes against this stable interface.
No dependents (verified by bunx turbo ls --filter=...@suaveplan/video).
Migration
For consumers porting from the upstream @suaveplan/video shape:
- VideoError: same import path, same
err.codestring values, sameerr.message,err.cause,err.context. Base class moved fromBaseErrortoSuaveplanError;instanceof VideoErrorkeeps working. - Event listeners: upstream listeners on Node
EventEmitterreceived the payload as the first element of a tuple ((meta) => …sinceargs[0]destructures the tuple); underEventBusthe listener receives the payload directly. Idiomatic call sites are unchanged. registerVideoErrorCodes(): still callable, still side-effect-safe; registration is now driven bydefineCodesat module-load time.- Search command for upstream consumers (none on genesis today):
grep -rln "@suaveplan/video" packages/ | grep -v "/dist/\|/coverage/".