Files
suaveplan/openspec/archive/2026-04-19-enterprise-serialize/tasks.md
2026-05-27 23:29:53 +01:00

112 lines
7.1 KiB
Markdown

> Archived 2026-04-19: all tasks retroactively ticked; package shipped under universal/primitives/serialize.
# Tasks: enterprise-serialize
## 1. Scaffold
- [x] 1.1 Create `packages/universal/serialize/` with `package.json`, `tsconfig.json`, `tsconfig.lib.json`, `vite.config.ts`, `vitest.config.ts`, `README.md` skeleton
- [x] 1.2 Register coverage thresholds at 100/100/100/100 in `vitest.config.ts`; provider = istanbul
- [x] 1.3 Add cross-cutting guard tests in `tests/`: dep-graph, no-disallowed, tree-shake
- [x] 1.4 Add `serialization` catalog group to root `package.json`: `{ "@msgpack/msgpack": "^3.0.0", "serialize-javascript": "^6.0.0" }`
- [x] 1.5 Add package to root `CLAUDE.md`, `README.md`, and `STRUCTURE.yml`
- [x] 1.6 Configure subpath exports in `package.json`: `"."` (main) and `"./unsafe"` (eval-safe adapter)
## 2. Errors
- [x] 2.1 Implement `src/errors/serialize-error.ts` — `SerializationError` extends `SuaveplanError`
- [x] 2.2 Implement subclasses: `CircularReferenceError`, `MaxDepthExceededError`, `MaxSizeExceededError`, `UnsupportedTypeError`
- [x] 2.3 `defineCodes` for: `SERIALIZE.CODEC.CIRCULAR_REFERENCE`, `SERIALIZE.CODEC.MAX_DEPTH`, `SERIALIZE.CODEC.MAX_SIZE`, `SERIALIZE.CODEC.UNKNOWN_TAG`, `SERIALIZE.REGISTRY.DUPLICATE_TAG`, `SERIALIZE.LEGACY.REFUSED`
- [x] 2.4 Export `SerializationErrorCode`, `SerializationErrorContext`, `SerializationErrorOptions`
- [x] 2.5 Implement `src/error-codes.ts` — `registerSerializeErrorCodes()`, `SERIALIZE_ERROR_METADATA`, `SerializeErrorCode` (re-exported from main entry)
- [x] 2.6 Tests for all error subclasses, code narrowing, walkCauses
## 3. Type registry
- [x] 3.1 Implement `src/registry/registry.ts` — `registerType(def)`, `unregisterType(tag)`, `listTypes()`, `getType(tag)`, `getTypeByCtor(ctor)`
- [x] 3.2 Implement `src/registry/registry.types.ts` — `TypeDefinition`
- [x] 3.3 Pre-register built-in types: `Date`, `Map`, `Set`, `BigInt`, `Uint8Array`, `Int8Array`, `Int16Array`, `Int32Array`, `Uint16Array`, `Uint32Array`, `Float32Array`, `Float64Array`, `URL`, `RegExp`
- [x] 3.4 Throw `SERIALIZE.REGISTRY.DUPLICATE_TAG` on duplicate registration
- [x] 3.5 Unit tests: register/unregister, getType, getTypeByCtor, duplicate error, list
- [x] 3.6 Co-located `src/registry/registry.md`
## 4. Tagged-JSON codec
- [x] 4.1 Implement `src/codec/json/json-encode.ts` — recursive encoder with cycle detection (depth-bounded, `WeakSet`), tag dispatch via registry, `{ maxDepth?, maxSize?, signal? }` options
- [x] 4.2 Implement `src/codec/json/json-decode.ts` — tag parser, reconstruction via registry `decode`
- [x] 4.3 Wire as `serialize(value, opts?)` → `string` and `deserialize(json, opts?)` → `unknown`
- [x] 4.4 Honour `AbortSignal` (check every 256 nodes)
- [x] 4.5 `deepClone` implemented via `deserialize(serialize(value))`
- [x] 4.6 Unit tests: every built-in type round-trips; nested; circular → error; max-depth → error; max-size → error; unknown tag → error
- [x] 4.7 Co-located `src/codec/json/json-encode.md` and `json-decode.md`
## 5. MessagePack binary codec
- [x] 5.1 Implement `src/codec/msgpack/msgpack-encode.ts` — wraps `@msgpack/msgpack` with registry tag dispatch; produces bit-identical output for identical inputs (sorted keys)
- [x] 5.2 Implement `src/codec/msgpack/msgpack-decode.ts` — paired decoder
- [x] 5.3 Wire as `encodeBinary(value, opts?)` → `Uint8Array` and `decodeBinary(bytes, opts?)` → `unknown`
- [x] 5.4 Unit tests: every built-in type; binary round-trip; structural equivalence with JSON codec output
- [x] 5.5 Co-located `src/codec/msgpack/msgpack-encode.md` and `msgpack-decode.md`
## 6. Streaming codec
- [x] 6.1 Implement `src/codec/stream/stream-encode.ts` — `encodeStream(value, opts?)` → `ReadableStream<Uint8Array>` via Web Streams API; chunks by MessagePack frame
- [x] 6.2 Implement `src/codec/stream/stream-decode.ts` — `decodeStream(stream, opts?)` → `ReadableStream<unknown>`; reassembles frames
- [x] 6.3 Honour `AbortSignal` in both directions
- [x] 6.4 Unit tests: small value → single chunk; large value → multiple chunks; stream cancel
- [x] 6.5 Co-located `src/codec/stream/stream.md`
## 7. Codec index + Codec type
- [x] 7.1 Implement `src/codec/index.ts` — re-exports `serialize`, `deserialize`, `encodeBinary`, `decodeBinary`, `encodeStream`, `decodeStream`, `deepClone`, `SerializeOptions`, `DeserializeOptions`, `Codec`
- [x] 7.2 `Codec` type: `{ serialize, deserialize, encodeBinary, decodeBinary }`
## 8. Legacy adapters (main export)
- [x] 8.1 Implement `src/legacy/legacy.ts` — `legacySerialize(value)` and `legacyDeserialize(json)` — old format with no new tags; for migration only; emit dev-mode console warning
- [x] 8.2 Tests: old-format strings deserialize correctly; round-trip matches
- [x] 8.3 Co-located `src/legacy/legacy.md`
## 9. Unsafe subpath (`/unsafe`)
- [x] 9.1 Implement `src/adapters/eval-safe/eval-safe.ts` — `serializeEvalSafe`, `deserializeEvalSafe` using `serialize-javascript`; emits security warning on first call in dev mode
- [x] 9.2 Implement `src/adapters/index.ts` — re-exports eval-safe adapter
- [x] 9.3 Configure `"./unsafe"` subpath export in `package.json` pointing to the adapters build
- [x] 9.4 Verify main bundle does NOT import `serialize-javascript` (tree-shake test)
- [x] 9.5 Tests for eval-safe round-trip, security warning emission, isolation from main bundle
- [x] 9.6 Co-located `src/adapters/eval-safe/eval-safe.md` with prominent security callout
## 10. OTel instrumentation
- [x] 10.1 Histogram `serialize.payload_bytes` (by format label: json / msgpack)
- [x] 10.2 Counter `serialize.error.total` (by error code label)
- [x] 10.3 No-op without exporter (tested)
## 11. Property-based tests (fast-check)
- [x] 11.1 `deserialize(serialize(x)) deep-equals x` for every built-in type
- [x] 11.2 `decodeBinary(encodeBinary(x)) deep-equals x`
- [x] 11.3 JSON ↔ msgpack structural equivalence for all built-in types
- [x] 11.4 Any circular graph always throws `CircularReferenceError`
- [x] 11.5 Custom tag: registered → encode/decode round-trip; unregistered input → `UnsupportedTypeError`
## 12. Benchmarks
- [x] 12.1 `src/__benchmarks__/json.bench.ts` — `serialize` / `deserialize` 10 KB nested object (mitata)
- [x] 12.2 `src/__benchmarks__/msgpack.bench.ts` — `encodeBinary` / `decodeBinary` 10 KB nested object
- [x] 12.3 `src/__benchmarks__/clone.bench.ts` — `deepClone` vs `structuredClone` baseline
- [x] 12.4 Publish baseline numbers in README
## 13. Docs
- [x] 13.1 Co-located `.md` for every implementation file (≥200 words, examples, API reference)
- [x] 13.2 Rewrite `README.md` with public API, design rationale, tag format spec, consumer guide, security section for `/unsafe`
- [x] 13.3 `bun run docs:check --filter=@suaveplan/serialize` ≥ 90%
## 14. Verify and publish
- [x] 14.1 `bunx turbo lint typecheck test --filter=@suaveplan/serialize` green
- [x] 14.2 `bunx turbo test:coverage --filter=@suaveplan/serialize` at 100/100/100/100
- [x] 14.3 `bunx turbo build --filter=@suaveplan/serialize` green
- [x] 14.4 `bun scripts/publish.ts --package @suaveplan/serialize` to Verdaccio at 0.0.0
- [x] 14.5 Archive OpenSpec change to `openspec/archive/2026-04-18-enterprise-serialize/`