Skip to content

fix: require serializers for non-JSON cached values#50

Merged
lan17 merged 2 commits into
mainfrom
agent/honest-json-default-contract
Jul 17, 2026
Merged

fix: require serializers for non-JSON cached values#50
lan17 merged 2 commits into
mainfrom
agent/honest-json-default-contract

Conversation

@lan17

@lan17 lan17 commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

Reframe issue #16 as a compile-time guardrail around the existing JSON default.

Cached functions whose resolved return types are statically JSON-compatible continue to omit serializer. Known incompatible or unprovable return types now require a typed per-function serializer. Native JSON remains the default, and this adds no runtime validation pass.

Architecture and design

  • Introduce a bounded, structural conditional type that approximates whether decoded JSON remains assignable to the cached return type.
  • Keep the check declaration-only; it is erased from emitted JavaScript and adds no cache-hit, cache-write, or serialization CPU work.
  • Preserve native JSON.stringify / JSON.parse, the top-level undefined sentinel, Redis framing, adapters, and fail-open behavior.
  • Treat an explicitly supplied Serializer<T> as a trusted boundary. DialCache does not perform a semantic round-trip check.
  • Require the per-function serializer even for currently local-only declarations and when a global Redis serializer exists: layers are runtime-controlled, while the global serializer is not parameterized by each cached return type.

Public API impact

CachedOptions<Fn> changes from an interface with an always-optional serializer to a conditional type:

  • JSON-compatible return type: serializer?: Serializer<T> | null.
  • Statically incompatible or unprovable return type: serializer: Serializer<T>.

This is a source-level tightening: code that cached non-JSON values without a per-function serializer will stop compiling until it makes the codec choice explicit. It also changes CachedOptions from an interface to a type alias, so declaration merging is no longer supported.

The previous draft's public API change is reverted: JsonSerializer<T> remains generic, and explicitly using new JsonSerializer<T>() remains a trusted caller assertion.

Guard coverage and limits

The package declaration matrix accepts primitives, arrays, nested interface shapes, optional fields, empty objects, and top-level undefined/void. It rejects Date, Map, Set, bigint, functions, symbols, Buffers, typed arrays, method-bearing classes, required nested undefined, unknown, and any unless a matching serializer is present.

The guard is conservative, not a proof. TypeScript cannot identify non-finite numbers, runtime cycles/shared references, runtime getter or toJSON behavior, or data-only class instances that look structural. Opaque, generic, or deeply recursive types may need an explicit serializer.

V8 serialization is intentionally not added here; it remains a possible separate opt-in codec rather than a default change.

Validation

  • corepack pnpm check on Node 22.22.0
  • Unit and coverage suite: 132 tests passed
  • Focused serializer/request-local/Redis suite: 68 tests passed
  • Build and declaration emission
  • Packed ESM/CJS package-consumer type matrix on Node 22.22.0
  • Packed ESM/CJS package-consumer type matrix on Node 20.17.0
  • Redis and adapter integration suite: 46 tests passed
  • git diff --check
  • Exact-head GitHub CI run 29598775381 passed

Tracking

Closes #16

@lan17
lan17 force-pushed the agent/honest-json-default-contract branch from 5d702eb to 585e185 Compare July 17, 2026 07:25
@lan17 lan17 changed the title fix: make default JSON serializer contract explicit fix: require serializers for non-JSON cached values Jul 17, 2026
@lan17
lan17 marked this pull request as ready for review July 17, 2026 19:09
@lan17
lan17 merged commit 5b3fccb into main Jul 17, 2026
4 checks passed
@lan17
lan17 deleted the agent/honest-json-default-contract branch July 17, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Require explicit serializers for statically non-JSON cached values

1 participant