10 KiB
10 KiB
Tasks: enterprise-mime
1. Scaffold
- 1.1 Create
packages/universal/mime/withpackage.json,tsconfig.json,tsconfig.lib.json,vite.config.ts,vitest.config.ts,README.mdskeleton - 1.2 Register coverage thresholds at 100/100/100/100 in
vitest.config.ts; provider = istanbul - 1.3 Add cross-cutting guard tests in
tests/: dep-graph, no-disallowed, tree-shake - 1.4 Add package to root
CLAUDE.md,README.md, andSTRUCTURE.yml
2. Error types
- 2.1 Implement
src/errors/mime-error.ts—MimeErrorbase extendsSuaveplanError - 2.2 Implement error code constants in
src/error-codes.ts—MIME_ERROR_METADATA,MimeErrorCode,registerMimeErrorCodes()usingdefineCodesforMIME.PARSE_ERROR,MIME.INSUFFICIENT_BYTES,MIME.UNKNOWN_SIGNATURE,MIME.UNKNOWN_TYPE,MIME.INVALID_CONTENT_TYPE - 2.3 Implement
src/errors/insufficient-bytes-error.ts—InsufficientBytesErrorextendsMimeError;InsufficientBytesErrorCode,InsufficientBytesErrorContext({ needed: number; got: number }),InsufficientBytesErrorOptions - 2.4 Implement
src/errors/invalid-content-type-error.ts—InvalidContentTypeErrorextendsMimeError; context{ raw: string } - 2.5 Implement
src/errors/unknown-mime-type-error.ts—UnknownMimeTypeErrorextendsMimeError; context{ mimeType: string } - 2.6 Implement
src/errors/parse-error.ts—ParseErrorextendsMimeError(codeMIME.PARSE_ERROR) - 2.7 Implement
src/errors/unknown-signature-error.ts—UnknownSignatureErrorextendsMimeError(codeMIME.UNKNOWN_SIGNATURE) - 2.8 Tests: construction, code narrowing, context field shapes,
instanceof MimeError - 2.9 Co-located
.mdfor each error file
3. Zod schemas
- 3.1 Implement
src/schemas/content-type.schemas.ts—ParsedContentTypeSchema,MediaTypeSchema - 3.2 Implement
src/schemas/db.schemas.ts—MimeCategorySchema,MimeEntrySchema - 3.3 Implement
src/schemas/detection.schemas.ts—SignatureSchema,DetectInputSchema,MagicBytesResultSchema,ExtensionDetectionResultSchema,ContentTypeDetectionResultSchema,StreamDetectionResultSchema,DetectResultSchema(discriminated union) - 3.4 Tests: valid and invalid inputs for every schema; discriminated union branches
- 3.5 Co-located
.mdfor each schemas file
4. Content-type parser
- 4.1 Implement
src/content-type/parse-content-type.ts—parseContentType(value)→ParsedContentType; RFC 7231 compliant; handles quoted-string parameters, case-insensitive type/subtype, OWS trimming; throwsInvalidContentTypeErroron parse failure - 4.2 Implement
src/content-type/format-content-type.ts—formatContentType(parsed)→string; canonical round-trip serialisation; quotes parameter values that require it - 4.3 Implement
src/content-type/content-type-equals.ts—contentTypeEquals(a, b)→boolean; structural equality ignoring parameter order and case - 4.4 Implement
src/content-type/content-type-matches.ts—contentTypeMatches(actual, pattern)→boolean; wildcard*subtype matching - 4.5 Define
src/content-type/content-type.types.ts—MediaType,ParsedContentType - 4.6 Unit tests: RFC 7231 edge cases (quoted strings, OWS, empty params, case), equality, matching, round-trip
- 4.7 Property-based tests:
formatContentType(parseContentType(s))round-trip invariant - 4.8 Co-located
.mdfor each content-type file
5. MIME database
- 5.1 Implement
src/db/mime-entry.types.ts—MimeCategory,MimeEntry - 5.2 Implement
src/db/builtin-db.ts—BUILTIN_DB: curated, IANA-sourced, comprehensive set ofMimeEntryrecords covering all major categories - 5.3 Implement
src/db/registry.ts— mutable runtime registry layered overBUILTIN_DB;getMimeEntry(mimeType),getEntryByExtension(ext),listMimeEntries(),registerMimeEntry(entry)— throwsUnknownMimeTypeErroron missing lookup when strict mode requested - 5.4 Implement
src/reverse/reverse.ts—toAllExtensions(mimeType),toCategory(mimeType),toDefaultExtension(mimeType) - 5.5 Implement
src/legacy/get-mime-type.ts—getMimeType(ext)legacy shim delegating togetEntryByExtension - 5.6 Unit tests: lookups hit BUILTIN_DB;
registerMimeEntryoverrides;listMimeEntriesreturns sorted copy; reverse lookups; legacy shim - 5.7 Property-based tests:
getMimeEntry(registerMimeEntry(e).mimeType)round-trip - 5.8 Co-located
.mdfor each db file
6. Magic-byte detection
- 6.1 Define
src/detection/signature.types.ts—Signature({ mimeType: string; bytes: Uint8Array; offset?: number; mask?: Uint8Array });MagicBytesResult - 6.2 Implement
src/detection/trie.ts— compiled byte trie built from the signature table; supports offset-aware matching and bitmask application; exported asbuildTrie,matchTrie - 6.3 Implement
src/detection/builtin-signatures.ts— built-in signature table covering common formats: PNG, JPEG, GIF, WebP, AVIF, PDF, ZIP, GZIP, BZIP2, XZ, WASM, ELF, MP4/ISO-BMFF, OGG, FLAC, MP3, WAV, DOCX/XLSX/PPTX (ZIP-based), 7z, RAR, TAR, SQLite, ICO, BMP, TIFF, PSD, SWF, RTF, XML, HTML, and others - 6.4 Implement
src/detection/signature-registry.ts—registerSignature(sig),getSignatures(),resetSignatures()(test-only); validates viaSignatureSchemabefore insertion; throwsUnknownSignatureErroron duplicate with conflict - 6.5 Implement
src/detection/detect-from-magic-bytes.ts—detectFromMagicBytes(bytes)→MagicBytesResult; uses compiled trie; throwsInsufficientBytesErrorif buffer is shorter than the shortest registered signature - 6.6 Implement
src/detection/detect-all-from-magic-bytes.ts—detectAllFromMagicBytes(bytes)→MagicBytesResult[]; returns all matching signatures, sorted by specificity (longest match first) - 6.7 Unit tests: known-format buffers match expected MIME types; insufficient buffer throws
InsufficientBytesError; masked signatures; offset signatures - 6.8 Property-based tests:
detectFromMagicBytes(bytes)is always a result orInsufficientBytesError— never another error class - 6.9 Co-located
.mdfor each detection file in this step
7. Charset detection
- 7.1 Implement
src/charset/sniff-bom.ts—sniffBom(bytes)→BomDetection | null; recognises UTF-8 BOM (EF BB BF), UTF-16 BE (FE FF), UTF-16 LE (FF FE), UTF-32 BE/LE (00 00 FE FF / FF FE 00 00) - 7.2 Implement
src/charset/sniff-html-meta.ts—sniffHtmlMeta(bytes)→string | null; scans up to 1 024 bytes for<meta charset="…">and<meta http-equiv="Content-Type" content="…; charset=…">patterns (case-insensitive) - 7.3 Implement
src/charset/sniff-xml-decl.ts—sniffXmlDecl(bytes)→string | null; extractsencoding="…"from the XML declaration if present within the first 128 bytes - 7.4 Define
src/charset/charset.types.ts—BomDetection({ charset: string; bomLength: number }) - 7.5 Unit tests: known BOM sequences; HTML meta with various attribute orderings; XML declaration with and without encoding; non-matching inputs return null
- 7.6 Co-located
.mdfor each charset file
8. Extension + content-type detection
- 8.1 Implement
src/detection/detect-from-extension.ts—detectFromExtension(ext)→ExtensionDetectionResult; delegates togetEntryByExtension; normalises leading dot; throwsUnknownMimeTypeErroron unknown extension - 8.2 Implement
src/detection/detect-from-content-type.ts—detectFromContentType(contentType)→ContentTypeDetectionResult; callsparseContentTypethengetMimeEntry; throwsInvalidContentTypeErrororUnknownMimeTypeError - 8.3 Define
src/detection/detection.types.ts—DetectSource,DetectInput,DetectResult,ExtensionDetectionResult,ContentTypeDetectionResult,StreamDetectionResult - 8.4 Unit tests: known extensions map to correct MIME types; unknown extension throws; invalid Content-Type throws; round-trip through
formatContentType - 8.5 Co-located
.mdfor each file
9. Unified detect() and detectFromStream()
- 9.1 Implement
src/detection/detect.ts—detect(input)→Promise<DetectResult>; priority order: magic bytes → extension → content-type header; respectsinput.signal; validates input viaDetectInputSchema - 9.2 Implement
src/detection/detect-from-stream.ts—detectFromStream(stream, opts?)→Promise<StreamDetectionResult>; reads the first chunk from an async iterable orReadableStream, then delegates todetectFromMagicBytes; honoursAbortSignal; documents deferred-streaming intent - 9.3 Unit tests: all three detection paths exercised; signal cancellation; empty input handled gracefully
- 9.4 Co-located
.mdfor each file
10. OTel instrumentation
- 10.1 Implement
src/telemetry/meters.ts— create meter@suaveplan/mime; define countermime.detect.total(attribute:source), histogrammime.detect.duration_ms, countermime.parse.total(attribute:outcome) - 10.2 Wire instrumentation into
detect,detectFromMagicBytes,detectFromExtension,detectFromContentType,detectFromStream,parseContentType - 10.3 Tests: confirm no-op behaviour when no OTel exporter is registered (no throw, no side-effects visible)
- 10.4 Co-located
src/telemetry/meters.md
11. Property tests + benchmarks
- 11.1
src/__benchmarks__/parse-content-type.bench.ts—parseContentType10 000 iterations (mitata) - 11.2
src/__benchmarks__/detect.bench.ts—detectfrom 4 KBUint8Array10 000 iterations - 11.3
src/__benchmarks__/get-mime-entry.bench.ts—getMimeEntrylookup 100 000 iterations - 11.4 Publish baseline numbers in
README.md
12. Docs
- 12.1 Co-located
.mdfor every implementation file (≥200 words, simple + advanced examples, API reference, implementation notes) - 12.2 Rewrite
README.mdwith full public API table, detection priority diagram, database category table, charset detection guide, consumer quick-start, and benchmark results - 12.3
bun run docs:check --filter=@suaveplan/mime≥ 90%
13. Verify and publish
- 13.1
bunx turbo lint typecheck test --filter=@suaveplan/mimegreen - 13.2
bunx turbo test:coverage --filter=@suaveplan/mimeat 100/100/100/100 - 13.3
bunx turbo build --filter=@suaveplan/mimegreen - 13.4
bun scripts/publish.ts --package @suaveplan/mimeto Verdaccio at 0.0.0 - 13.5 Archive OpenSpec change to
openspec/archive/2026-04-19-enterprise-mime/