Initial Commit
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# OpenSpec Change: enterprise-compress-image
|
||||
|
||||
- **Change ID**: enterprise-compress-image
|
||||
- **Package**: `@suaveplan/compress-image`
|
||||
- **Category**: universal
|
||||
- **Target path**: `universal/compression/compress-image`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-04-19
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/compress-image.md`
|
||||
|
||||
## Summary
|
||||
|
||||
High-level image compression façade. Orchestrates format-specific encoders (JPEG/MozJPEG, PNG/OxiPNG, WebP, AVIF) behind a uniform `ImageCompressor.compress(input, options)` API. Accepts quality presets, format override, max-dimension resize, and returns a `CompressionResult` with before/after stats.
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
Package does not exist yet — fresh build.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Workspace: `@suaveplan/error`, `@suaveplan/types`
|
||||
- External (via catalog): `zod`, `tslib`, `@opentelemetry/api`, `@jsquash/jpeg`, `@jsquash/png`, `@jsquash/webp`, `@jsquash/avif`, `@jsquash/resize`
|
||||
- Dev (via catalog): `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata`
|
||||
|
||||
## Preserved public surface
|
||||
|
||||
- `ImageCompressor` — `compress(input, options?): Promise<CompressionResult>`
|
||||
- Errors: `CompressionError`, `CompressionErrorCode`, `CompressionErrorContext`, `compressionFailed`, `dimensionTooLarge`, `invalidQuality`, `unsupportedFormat`, `CompressImageErrorCode`, `COMPRESS_IMAGE_ERROR_METADATA`, `registerCompressImageErrorCodes`
|
||||
- Types: `CompressionFormat`, `CompressionOptions`, `CompressionPlan`, `CompressionResult`, `CompressionStep`, `PresetConfig`, `QualityPreset`, `ResolvedCompressionOptions`
|
||||
- Constants: `FORMAT_QUALITY_RANGES`, `MAX_DIMENSION`, `QUALITY_PRESETS`, `VALID_FORMATS`, `VALID_PRESETS`
|
||||
|
||||
## New genesis additions
|
||||
|
||||
- Additional codes: `COMPRESS_IMAGE.ENCODER_UNAVAILABLE`, `COMPRESS_IMAGE.INPUT_TOO_LARGE`, `COMPRESS_IMAGE.OUTPUT_LARGER_THAN_INPUT`.
|
||||
- Zod schemas for every option / result type.
|
||||
- OTel histogram `compress_image.compress.duration_ms{format, preset}`; counter for outcome; gauge `compress_image.bytes.saved`.
|
||||
- Property tests: output byte length ≤ input byte length for any input under a fixed preset (or error thrown).
|
||||
|
||||
## Impact
|
||||
|
||||
- Adds one new package: `universal/compression/compress-image`
|
||||
- Adds one new spec capability: `compress-image`
|
||||
@@ -0,0 +1,37 @@
|
||||
# Capability Spec: compress-image
|
||||
|
||||
**Package**: `@suaveplan/compress-image`
|
||||
**Version**: 0.0.0 (genesis)
|
||||
**Status**: ADDED
|
||||
|
||||
---
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### CI-01 — Format coverage
|
||||
|
||||
`VALID_FORMATS` MUST include `'jpeg' | 'png' | 'webp' | 'avif'`. Each format MUST have a working adapter invoked by the dispatcher.
|
||||
|
||||
### CI-02 — Compression API
|
||||
|
||||
`ImageCompressor.compress(input, options?)` MUST accept `Uint8Array | ArrayBuffer | Blob`, resolve options to a `ResolvedCompressionOptions`, build a `CompressionPlan`, and execute it to produce `CompressionResult { format, bytes, width, height, durationMs, steps: CompressionStep[], inputBytes, outputBytes, savedBytes }`.
|
||||
|
||||
### CI-03 — Quality presets
|
||||
|
||||
`QUALITY_PRESETS` MUST define `'maximum' | 'high' | 'balanced' | 'fast' | 'aggressive'` with deterministic per-format quality values obeying `FORMAT_QUALITY_RANGES`. Custom `options.quality` numeric values outside the format range MUST throw `COMPRESS_IMAGE.INVALID_QUALITY`.
|
||||
|
||||
### CI-04 — Max dimension
|
||||
|
||||
`options.maxDimension` MUST resize the longest side to at most the given pixel count. A resize step MUST appear in `CompressionPlan.steps`. Inputs exceeding `MAX_DIMENSION` (default 20 000) MUST throw `COMPRESS_IMAGE.DIMENSION_TOO_LARGE`.
|
||||
|
||||
### CI-05 — Degenerate output
|
||||
|
||||
If `outputBytes > inputBytes` and `options.preserveSmaller` is `true` (default), the compressor MUST return the original input with a `CompressionStep { action: 'skip', reason: 'output-larger-than-input' }`. Otherwise the larger compressed output is returned.
|
||||
|
||||
### CI-06 — Format selection
|
||||
|
||||
When `options.format` is omitted, the compressor MUST keep the input format. `options.format: 'auto'` MUST choose based on input type (`jpeg` → `webp` at lower size if supported; `png` with alpha → `webp`/`avif`; etc.).
|
||||
|
||||
### CI-07 — Observability
|
||||
|
||||
Every compress call MUST record `compress_image.compress.duration_ms{format, preset}`, `compress_image.compress.total{format, outcome}`, and `compress_image.bytes.saved{format}` gauge.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Tasks: enterprise-compress-image
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [x] 1.1 `universal/compression/compress-image/`; 100/100/100/100; guard tests; CLAUDE/README/FOLDERS.
|
||||
|
||||
## 2. Errors + schemas
|
||||
|
||||
- [x] 2.1 `src/error-codes.ts`, `src/errors/compression-errors.ts`.
|
||||
- [x] 2.2 Zod schemas per type.
|
||||
|
||||
## 3. Format adapters
|
||||
|
||||
- [x] 3.1 `src/adapters/jpeg.ts` (jsquash/jpeg).
|
||||
- [x] 3.2 `src/adapters/png.ts` (jsquash/png).
|
||||
- [x] 3.3 `src/adapters/webp.ts` (jsquash/webp).
|
||||
- [x] 3.4 `src/adapters/avif.ts` (jsquash/avif).
|
||||
- [x] 3.5 Co-located `.md` + fixture tests.
|
||||
|
||||
## 4. Resize
|
||||
|
||||
- [x] 4.1 `src/resize/resize.ts` — max-dimension constraint via jsquash/resize.
|
||||
|
||||
## 5. Planner / compressor
|
||||
|
||||
- [x] 5.1 `src/planner/planner.ts` — build `CompressionPlan` from `ResolvedCompressionOptions`.
|
||||
- [x] 5.2 `src/compressor/image-compressor.ts` — `ImageCompressor`.
|
||||
- [x] 5.3 OTel histogram + counter + gauge.
|
||||
|
||||
## 6. Presets / constants
|
||||
|
||||
- [x] 6.1 `src/types/compression.types.ts` exports `QUALITY_PRESETS`, `VALID_FORMATS`, `VALID_PRESETS`, `FORMAT_QUALITY_RANGES`, `MAX_DIMENSION`.
|
||||
|
||||
## 7. Docs / barrel / subpath exports / README
|
||||
|
||||
## 8. Benchmarks
|
||||
|
||||
- [x] 8.1 Compress 1 MB / 10 MB test images per format.
|
||||
|
||||
## 9. Ship gate
|
||||
|
||||
- [x] 9.1 Verification sequence; fold + archive.
|
||||
Reference in New Issue
Block a user