Initial Commit
This commit is contained in:
55
openspec/archive/2026-04-19-enterprise-exif/proposal.md
Normal file
55
openspec/archive/2026-04-19-enterprise-exif/proposal.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# OpenSpec Change: enterprise-exif
|
||||
|
||||
- **Change ID**: enterprise-exif
|
||||
- **Package**: `@suaveplan/exif`
|
||||
- **Category**: universal
|
||||
- **Target path**: `universal/media/exif`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-04-19
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/exif.md`
|
||||
|
||||
## Summary
|
||||
Greenfield rebuild of `@suaveplan/exif` — a comprehensive, dependency-free, runtime-agnostic metadata reader/writer/stripper for JPEG, PNG, WebP, TIFF, HEIC/ISOBMFF, and RAW images. Supports EXIF, XMP, IPTC, ICC, GPS, and MakerNotes (Apple, Canon, Nikon, Sony, incl. ciphered MakerNote decoders). The package's main-branch change `openspec/changes/exif-migration` is the most recent authoritative delta and is folded in here.
|
||||
|
||||
## Current-state audit (genesis)
|
||||
Package does not exist yet — fresh build. Main branch has `universal/exif` with 40+ exported primitives.
|
||||
|
||||
## Dependencies
|
||||
- Workspace: none (leaf)
|
||||
- External (via catalog): `zod`, `tslib`, `@opentelemetry/api`
|
||||
- Dev (via catalog): `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata`
|
||||
|
||||
## Preserved public surface (grouped)
|
||||
|
||||
### Binary primitives
|
||||
`ByteReader`, `ByteWriter`, `ByteOrder`, `crc32`, `crc32Multi`, `md5`, `md5Hex`, `numberToRational`, `rationalToNumber`, `Rational`.
|
||||
|
||||
### Core dispatch
|
||||
`detectImageFormat`, `readMetadata`, `writeMetadata`, `stripMetadata`, `ReadOptions`, `StripOptions`.
|
||||
|
||||
### Format-specific
|
||||
JPEG: `readJpegSegments`, `writeJpegSegments`. PNG: `readPng`, `writePng`. TIFF: `readTiffMetadata`, `writeTiffMetadata`. WebP: `readWebp`, `writeWebp`. ISOBMFF/HEIC: `findAllBoxes`, `findBox`, `readBoxes`, `readFtypBrands`, `readIsobmffMetadata`, `writeIsobmffMetadata`. RAW: `detectRaw`.
|
||||
|
||||
### IFD engine
|
||||
`parseTiff`, `ParsedTiff`, `ParseOptions`, `buildTiff`, `BuildInput`, `IfdEntry`, `TiffType`, `decodeEntryValue`, `encodeEntryValue`, `entrySize`, `TIFF_TYPE`, `TIFF_TYPE_SIZE`.
|
||||
|
||||
### GPS, IPTC, XMP, ICC
|
||||
GPS: `decimalToDms`, `dmsToDecimal`, `formatDms`, `gpsToIsoTimestamp`, `isoToGpsFields`, `LatRef`, `LonRef`. IPTC: `readIptc`, `writeIptc`. XMP: `readXmp`, `writeXmp`, `XMP_NAMESPACES`, `namespaceFor`. ICC: `readIccProfile`, `writeIccProfile`, `validateIccProfile`, `classifyProfile`, `isSRGB`, `isAdobeRGB`, `isDisplayP3`, `profileDescription`.
|
||||
|
||||
### MakerNotes
|
||||
`decodeMakerNote`, `decodeAppleMakerNote`, `decodeBplist`, `decodeCanonMakerNote`, `decodeNikonMakerNote`, `nikonDecrypt`, `nikonEncrypt`, `sonyDecrypt`, `sonyEncrypt`.
|
||||
|
||||
### Types
|
||||
`ExifData`, `ExifIfd`, `ExifValue`, `IccCurve`, `IccDateTime`, `IccHeader`, `IccProfile`, `IccTagValue`, `IccText`, `IccXyz`, `ImageFormat`, `ISOBMFF_FAMILY`, `TIFF_FAMILY`, `ImageMetadata`, `CameraInfo`, `ExposureInfo`, `GpsCoordinates`, `LensInfo`, `IptcData`, `IptcValue`, `MakerNote`, `MakerNoteVendor`, `GpsPatch`, `MakerNotePatch`, `MetadataPatch`, `XmpData`, `XmpScalar`, `XmpStruct`, `XmpValue`.
|
||||
|
||||
## New genesis additions
|
||||
- Codes: `EXIF.UNSUPPORTED_FORMAT`, `EXIF.PARSE_ERROR`, `EXIF.WRITE_FAILED`, `EXIF.INVALID_IFD`, `EXIF.BUFFER_OVERRUN`.
|
||||
- Zod schemas for every wire-facing type: `ExifDataSchema`, `ImageMetadataSchema`, `MetadataPatchSchema`.
|
||||
- OTel histograms per format: `exif.read.duration_ms{format}`, `exif.write.duration_ms{format}`, `exif.strip.duration_ms{format}`.
|
||||
- Property tests: strip is idempotent; read→write→read yields equal metadata; GPS DMS↔decimal round-trip; `numberToRational`→`rationalToNumber` within tolerance.
|
||||
- Differential fuzzing harness against `exifr` for read parity on corpus of 1 000 real images.
|
||||
|
||||
## Impact
|
||||
- Adds one new package: `universal/media/exif`
|
||||
- Adds one new spec capability: `exif`
|
||||
- Unblocks `metadata`, `image`, `react-image` (downstream waves).
|
||||
@@ -0,0 +1,36 @@
|
||||
# Capability Spec: exif
|
||||
|
||||
**Package**: `@suaveplan/exif`
|
||||
**Version**: 0.0.0 (genesis)
|
||||
**Status**: ADDED
|
||||
|
||||
---
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### EXIF-01 — Format detection
|
||||
`detectImageFormat(bytes)` MUST identify JPEG, PNG, WebP, TIFF, HEIC (ISOBMFF), and the RAW families (CR2, CR3, NEF, ARW, RAF, ORF, RW2, DNG) by magic-byte sniffing. Unknown inputs MUST return `'unknown'`, not throw.
|
||||
|
||||
### EXIF-02 — Read / write / strip dispatch
|
||||
`readMetadata(bytes, options?)` MUST dispatch to the format-specific reader and return a unified `ImageMetadata`. `writeMetadata(bytes, patch)` MUST apply a `MetadataPatch` and return a new buffer. `stripMetadata(bytes, options?)` MUST produce a buffer containing image data only; running it twice MUST be idempotent.
|
||||
|
||||
### EXIF-03 — IFD codec
|
||||
`parseTiff(bytes, options?)` MUST return `ParsedTiff` with every IFD, entry, and sub-IFD. `buildTiff(input)` MUST produce a byte buffer that round-trips through `parseTiff` to an equal `ParsedTiff`. Entry type widths MUST follow `TIFF_TYPE_SIZE`.
|
||||
|
||||
### EXIF-04 — GPS
|
||||
`decimalToDms` and `dmsToDecimal` MUST round-trip within 1e-7 degrees. `gpsToIsoTimestamp` and `isoToGpsFields` MUST round-trip to second precision.
|
||||
|
||||
### EXIF-05 — ICC
|
||||
`readIccProfile` MUST return `IccProfile` with header, tag table, and parsed tag values. `validateIccProfile` MUST reject profiles with invalid size, version, or checksum. `classifyProfile` MUST detect `sRGB`, `AdobeRGB`, `DisplayP3` reliably for canonical profiles.
|
||||
|
||||
### EXIF-06 — IPTC / XMP
|
||||
`readIptc` / `writeIptc` MUST support IIM records 1, 2 with UTF-8 and ISO-8859-1 charsets. `readXmp` MUST return an `XmpData` tree (scalars, structs, language-alternatives). `XMP_NAMESPACES` MUST expose the canonical URI per prefix.
|
||||
|
||||
### EXIF-07 — MakerNotes
|
||||
`decodeMakerNote` MUST dispatch to the correct vendor decoder. Nikon and Sony encrypted MakerNote tags MUST be decoded via `nikonDecrypt` / `sonyDecrypt` using the serial+shutter-derived keys. `decodeAppleMakerNote` MUST decode embedded bplist via `decodeBplist`.
|
||||
|
||||
### EXIF-08 — Safety
|
||||
All readers MUST operate on bounded `ByteReader` views and MUST throw `EXIF.BUFFER_OVERRUN` rather than reading past the buffer end. No reader MUST allocate buffers larger than the input.
|
||||
|
||||
### EXIF-09 — Observability
|
||||
Every read/write/strip MUST record the corresponding OTel histogram with label `format`.
|
||||
51
openspec/archive/2026-04-19-enterprise-exif/tasks.md
Normal file
51
openspec/archive/2026-04-19-enterprise-exif/tasks.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Tasks: enterprise-exif
|
||||
|
||||
## 1. Scaffold
|
||||
- [x] 1.1 `universal/media/exif/`; 100/100/100/100; guard tests; CLAUDE/README/FOLDERS.
|
||||
|
||||
## 2. Errors + schemas
|
||||
- [x] 2.1 `src/errors/exif-error.ts` — codes.
|
||||
- [x] 2.2 Zod schemas for ExifData, ImageMetadata, MetadataPatch, IccProfile.
|
||||
|
||||
## 3. Binary primitives
|
||||
- [x] 3.1 `src/binary/reader/*` — `ByteReader`, `ByteOrder`.
|
||||
- [x] 3.2 `src/binary/writer/*` — `ByteWriter`.
|
||||
- [x] 3.3 `src/binary/rational/*` — rational arithmetic + property tests.
|
||||
- [x] 3.4 `src/binary/crc32/*`, `src/binary/md5/*`.
|
||||
|
||||
## 4. IFD engine
|
||||
- [x] 4.1 `src/ifd/entry/*` — entry codec + `TIFF_TYPE`.
|
||||
- [x] 4.2 `src/ifd/parse/*` — `parseTiff`.
|
||||
- [x] 4.3 `src/ifd/build/*` — `buildTiff`.
|
||||
- [x] 4.4 Round-trip property test on random IFD trees.
|
||||
|
||||
## 5. Format readers/writers
|
||||
- [x] 5.1 JPEG segments `src/formats/jpeg/*`.
|
||||
- [x] 5.2 PNG chunks `src/formats/png/*`.
|
||||
- [x] 5.3 TIFF `src/formats/tiff/*`.
|
||||
- [x] 5.4 WebP chunks `src/formats/webp/*`.
|
||||
- [x] 5.5 ISOBMFF/HEIC box reader + read/write `src/formats/isobmff/*`.
|
||||
- [x] 5.6 RAW dispatch `src/formats/raw/dispatch/*`.
|
||||
- [x] 5.7 Real-image fixture tests per format.
|
||||
|
||||
## 6. Core dispatch
|
||||
- [x] 6.1 `src/core/detect/*` — `detectImageFormat`.
|
||||
- [x] 6.2 `src/core/read/*`, `src/core/write/*`, `src/core/strip/*`.
|
||||
- [x] 6.3 OTel histograms per format.
|
||||
|
||||
## 7. Metadata sub-systems
|
||||
- [x] 7.1 GPS: convert + timestamp.
|
||||
- [x] 7.2 ICC: read / write / validate / convert (classifyProfile, isSRGB, isAdobeRGB, isDisplayP3, profileDescription).
|
||||
- [x] 7.3 IPTC: read / write.
|
||||
- [x] 7.4 XMP: read / write + namespaces.
|
||||
|
||||
## 8. MakerNotes
|
||||
- [x] 8.1 Dispatch + Apple bplist + Apple + Canon + Nikon (+ cipher) + Sony (+ cipher).
|
||||
|
||||
## 9. Differential fuzz
|
||||
- [x] 9.1 Differential harness vs `exifr` on 1 000-image corpus; record discrepancies.
|
||||
|
||||
## 10. Docs / barrel / subpath exports / README / benchmarks
|
||||
|
||||
## 11. Ship gate
|
||||
- [x] 11.1 Verification sequence; fold + archive (merge with main-branch `exif-migration` delta).
|
||||
Reference in New Issue
Block a user