first commit
This commit is contained in:
107
docs/foundation/flow/streams-metadata/src/metadata/metadata.md
Normal file
107
docs/foundation/flow/streams-metadata/src/metadata/metadata.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# `metadata/metadata`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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 `MimeTransform` then `ExifTransform` with shared mode flag.
|
||||
- Forwards `mime:detected` and `exif:detected` events to the wrapper's
|
||||
listeners (no child subscriptions required).
|
||||
- Emits `metadata:complete` on success with the final enriched payload.
|
||||
- Emits `metadata:error` with 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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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? }`. `mode` is shared across both children; `read` is
|
||||
forwarded to the inner `ExifTransform`.
|
||||
- `async process<T = unknown>(input: T, options?: ProcessOptions): Promise<T>`
|
||||
— runs `mime.process(input) → exif.process(...)`. On success emits
|
||||
`metadata:complete` and returns the final enriched payload. On any
|
||||
child throw emits `metadata:error` with the thrown value and returns
|
||||
the original `input` reference unchanged.
|
||||
- `dispose(): void` — releases both child transforms and the wrapper's
|
||||
own listener / hook tables. Idempotent.
|
||||
- `name: "metadata"`.
|
||||
- `type MetadataTransformOptions = MimeTransformOptions & ExifTransformOptions`
|
||||
— shared `mode` flag plus EXIF-specific `read` options.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Child events are wired in the constructor via `this.mime.on(...)` and
|
||||
`this.exif.on(...)`; the lambdas call `this.emit(...)` on the wrapper to
|
||||
re-emit. The `Unsubscribe` returns are dropped because the child
|
||||
transforms' listener tables are released through `this.mime.dispose()` /
|
||||
`this.exif.dispose()` in the override.
|
||||
- Error containment: a child throw never propagates out of `process` —
|
||||
observability via `metadata:error` is the canonical failure channel.
|
||||
The original `input` reference is returned (not a clone) so
|
||||
referentially-stable downstream stages keep working.
|
||||
- Subclasses overriding `dispose()` MUST call `super.dispose()` (per the
|
||||
base contract). This class follows that rule by chaining
|
||||
`super.dispose()` after disposing children.
|
||||
- The constructor honours `exactOptionalPropertyTypes` by only forwarding
|
||||
`mode` / `read` when explicitly supplied, so the children use their own
|
||||
defaults when the wrapper's options bundle omits the field.
|
||||
Reference in New Issue
Block a user