4.3 KiB
metadata/metadata
(no submodules)
Purpose
MetadataTransform is the high-level composing wrapper around
MimeTransform and ExifTransform. A single instance runs MIME detection
followed by EXIF extraction in sequence; the result is a fully enriched
payload carrying both mime and exif. Child events are forwarded
through the wrapper so a single on('mime:detected', fn) subscription on
the wrapper observes the child emission — no need to wire per-transform
listeners. If either child throws, the wrapper catches the exception,
emits metadata:error with the thrown value, and resolves with the
original input reference so the pipeline continues.
This is the entry point most callers should reach for; the individual transforms are exposed for manual composition or when only one stage is desired.
Features
- Composes
MimeTransformthenExifTransformwith shared mode flag. - Forwards
mime:detectedandexif:detectedevents to the wrapper's listeners (no child subscriptions required). - Emits
metadata:completeon success with the final enriched payload. - Emits
metadata:errorwith the thrown value on child failure and returns the original input reference so a downstream pipeline stage can continue. - Overrides
dispose()to release child transforms in addition to the wrapper's own listener / hook tables.
Usage: Basic Example
import { MetadataTransform } from "@suaveplan/streams-metadata";
const transform = new MetadataTransform();
const result = await transform.process({ bytes: jpegBytes, name: "photo.jpg" });
console.log(result.mime?.mimeType); // "image/jpeg"
console.log(result.exif?.format); // "jpeg"
Usage: Advanced Example
import {
MetadataTransform,
type EnrichedPayload,
} from "@suaveplan/streams-metadata";
const transform = new MetadataTransform({
mode: "enrich",
read: { skipXmp: true },
});
const events: string[] = [];
transform.on("mime:detected", (r) => events.push(`mime:${r.source}`));
transform.on("exif:detected", (m) => events.push(`exif:${m.format}`));
transform.on("metadata:complete", (p: EnrichedPayload) =>
events.push(`complete:${p.bytes.byteLength}`),
);
transform.on("metadata:error", (err) => {
console.error("metadata pipeline failed", err);
});
await transform.process({ bytes: jpegBytes, name: "photo.jpg" });
// events => ["mime:magic-bytes", "exif:jpeg", "complete:..."]
API Reference
class MetadataTransform extends BaseTransform<{}, StreamsMetadataEvents>:constructor(options?: MetadataTransformOptions)— accepts{ mode?, read? }.modeis shared across both children;readis forwarded to the innerExifTransform.async process<T = unknown>(input: T, options?: ProcessOptions): Promise<T>— runsmime.process(input) → exif.process(...). On success emitsmetadata:completeand returns the final enriched payload. On any child throw emitsmetadata:errorwith the thrown value and returns the originalinputreference unchanged.dispose(): void— releases both child transforms and the wrapper's own listener / hook tables. Idempotent.name: "metadata".
type MetadataTransformOptions = MimeTransformOptions & ExifTransformOptions— sharedmodeflag plus EXIF-specificreadoptions.
Implementation Notes
- Child events are wired in the constructor via
this.mime.on(...)andthis.exif.on(...); the lambdas callthis.emit(...)on the wrapper to re-emit. TheUnsubscribereturns are dropped because the child transforms' listener tables are released throughthis.mime.dispose()/this.exif.dispose()in the override. - Error containment: a child throw never propagates out of
process— observability viametadata:erroris the canonical failure channel. The originalinputreference is returned (not a clone) so referentially-stable downstream stages keep working. - Subclasses overriding
dispose()MUST callsuper.dispose()(per the base contract). This class follows that rule by chainingsuper.dispose()after disposing children. - The constructor honours
exactOptionalPropertyTypesby only forwardingmode/readwhen explicitly supplied, so the children use their own defaults when the wrapper's options bundle omits the field.