Skip to content

feat: add reviver option for decode#294

Open
mvanhorn wants to merge 1 commit into
toon-format:mainfrom
mvanhorn:feat/reviver-decode
Open

feat: add reviver option for decode#294
mvanhorn wants to merge 1 commit into
toon-format:mainfrom
mvanhorn:feat/reviver-decode

Conversation

@mvanhorn

Copy link
Copy Markdown

Summary

Adds a reviver option to DecodeOptions, mirroring the existing replacer for EncodeOptions. The reviver transforms values during decode, matching JSON.parse reviver semantics (bottom-up traversal: leaves first, then parents).

Why this matters

JSON.stringify has replacer, JSON.parse has reviver. TOON's encode already has replacer. The decode side had no equivalent, forcing users to walk the decoded tree manually for any post-decode transformation.

PR #161 (quoteStrings) was closed in favor of the general-purpose replacer, confirming the preference for general mechanisms over specific flags. reviver follows the same philosophy for the decode side.

Usage

import { decode, encode } from '@toon-format/toon'

const toon = encode({ name: 'Alice', password: 'secret', role: 'admin' })

const result = decode(toon, {
  reviver: (key, value) => {
    if (key === 'password') return undefined
    if (key === 'name' && typeof value === 'string')
      return value.toUpperCase()
    return value
  }
})
// { name: 'ALICE', role: 'admin' }

Changes

  • packages/toon/src/types.ts: added DecodeReviver type and reviver option to DecodeOptions
  • packages/toon/src/decode/reviver.ts: new module implementing bottom-up value traversal (mirrors encode/replacer.ts)
  • packages/toon/src/index.ts: wired reviver into decodeFromLines(), exported DecodeReviver type
  • packages/toon/test/reviver.test.ts: 22 tests covering filtering, transformation, root handling, bottom-up traversal, path tracking, edge cases, and JSON.parse parity

Demo

reviver demo

Testing

All 488 tests pass (22 new + 466 existing). Lint, build, and type checks clean.

This contribution was developed with AI assistance (Claude Code).

Mirrors the existing `replacer` option for encode. The reviver function
is called bottom-up (leaves first) during decode, matching JSON.parse
reviver semantics. Supports value transformation, property filtering
via `undefined` return, and path tracking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@johannschopplich

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

The bottom-up traversal matches JSON.parse's reviver semantics, and test/reviver.test.ts mirroring test/replacer.test.ts is the right shape.

Three things before merge:

  1. Dedupe the type guards. decode/reviver.ts defines local isJsonObject / isJsonArray. These already live in encode/normalize.ts, and replacer.ts imports them. Please import from there, or move them to a shared/ module if a shared home reads cleaner.

  2. Reuse normalizeValue. The new normalizeRevivedValue reimplements a subset of encode/normalize.ts:normalizeValue and has a buggy fallback. String(value) for unknown types means functions/symbols/undefined become strings, where they should be null. The existing normalizeValue already handles Date, BigInt, Map, Set, toJSON, NaN/Infinity, and -0.

  3. README section. Add a doc block under packages/toon/README.md mirroring the existing replacer entry – same shape, swap the verbs.

Once these land, this is ready to merge.

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.

2 participants