Skip to content

Add comprehensive documentation and improve cross-language parity#3

Merged
rjrodger merged 4 commits into
mainfrom
claude/compassionate-shannon-7SHgq
Jun 8, 2026
Merged

Add comprehensive documentation and improve cross-language parity#3
rjrodger merged 4 commits into
mainfrom
claude/compassionate-shannon-7SHgq

Conversation

@rjrodger

@rjrodger rjrodger commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds extensive user-facing documentation for @voxgiq/util and improves the TypeScript and Go implementations to ensure deterministic, cross-language-identical behaviour. The library now ships with four documentation sections covering getting started, how-to guides, API references for both languages, and implementation details.

Key Changes

Documentation (new)

  • Added docs/getting-started.md — hands-on walkthrough covering path helpers, tree traversal, collection ordering, and serialization
  • Added docs/how-to-guides.md — focused recipes for common tasks (flattening maps, reading nested values, sorting collections, etc.)
  • Added docs/api-typescript.md — complete TypeScript API reference with examples for all exports
  • Added docs/api-go.md — complete Go API reference with examples for all exports
  • Added docs/explanation.md — background on the canonical/parity model, deliberate divergences between languages, and design rationale
  • Added docs/README.md — documentation index
  • Added AGENTS.md — dense guidance for AI coding agents and developers working in the repo
  • Added CLAUDE.md — quick reference for Claude Code
  • Added ci/README.md — explanation of CI workflow source-of-truth pattern
  • Updated main README.md with install instructions, quick examples, and documentation links

TypeScript (ts/src/util.ts)

  • Modified dive() to iterate keys in sorted order for deterministic output matching Go
  • Added function overloads to dive() to support both array and mapper return forms with proper typing
  • Fixed prettyPino() formatting: removed incorrect .padStart() call that was breaking alignment
  • Improved diveInternal() to explicitly sort keys before iteration
  • Added optional chaining to entity() for safer property access

Go (go/util.go)

  • Modified Dive() to iterate keys in sorted order (previously randomized due to Go map iteration)
  • Added DiveMap() function — the mapper form of Dive() that transforms entries through a callback
  • Enhanced GetPath() to support array indexing via numeric path segments, matching TypeScript behaviour
  • Improved toString() to handle nil, int64, and float64 types correctly, matching JavaScript's Array.join() semantics
  • Added proper validation for array indices (rejects non-canonical forms like "01", "+1", "-1")
  • Fixed CamelifySlice() to avoid unnecessary slice copying

Tests

  • Updated ts/test/util.test.ts with new test cases for:
    • pinify() with odd-length paths (trailing : behaviour)
    • joins() with various types (floats, booleans, null)
    • dive() with mapper function
    • get() with array indexing
  • Updated go/util_test.go to verify sorted key order in Dive() output and added tests for DiveMap(), array indexing, and human-sort padding
  • Recompiled TypeScript test output (ts/dist-test/util.test.js)

Build & CI

  • Added ci/ci.yml — GitHub Actions workflow testing Node 24 and latest, plus Go 1.23
  • Updated go/go.mod to require Go 1.23
  • Updated TypeScript dependencies: @types/node 25.9.2, typescript 6.0.3
  • Added ts/LICENSE (MIT)
  • Reorganized .gitignore paths to reflect ts/ subdirectory structure

Notable Implementation Details

  • Deterministic output: Both dive() implementations now sort keys before iteration, ensuring identical output across TypeScript and Go regardless of insertion order or map randomization
  • Cross-language parity: GetPath() in Go now supports array indexing with the same validation rules as TypeScript (canonical non-negative integers only)
  • Type rendering: toString() in Go now matches JavaScript's Array.join() semantics for all primitive types, ensuring joins() output is identical
  • Mapper pattern: `D

https://claude.ai/code/session_01MWuia2drUKR7xEmhkpip3X

claude added 4 commits June 8, 2026 11:40
Document TypeScript (src/util.ts) as the canonical implementation in a new
CLAUDE.md and bring the Go port (go/util.go) into parity with it.

Dependencies:
- typescript ^5.9.3 -> ^6.0.3, @types/node 25.5.0 -> 25.9.2 (dist rebuilt)
- go directive 1.21 -> 1.23

Go parity fixes:
- toString: replace broken float formatting (it always produced "") and the
  hand-rolled intToString with strconv; render floats with FormatFloat 'f' to
  match JS String(); nil -> "" like Array.join
- Pinify: keep the trailing ':' after a final even index, matching TS
- GetPath: index []any via canonical non-negative integer segments only
  (rejects "01", "+1", ... like JS array indexing)
- Stringify: de-cycle via Decircular first, matching TS stringify
- human$ ordering: measure title padding in runes, not bytes
- add DiveMap (the mapper form of TS dive)

TypeScript:
- order: drop unknown sort keys instead of leaving null holes in the result

Tests added on both sides to lock the shared contract. Remaining intrinsic
divergences (map iteration order, malformed-input handling) are documented
in CLAUDE.md.

https://claude.ai/code/session_01MWuia2drUKR7xEmhkpip3X
Documentation (docs/), organised so each section answers one kind of question:
- getting-started.md : guided, hands-on tutorial
- how-to-guides.md   : task-focused recipes
- api-typescript.md  : full TypeScript API reference
- api-go.md          : full Go API reference
- explanation.md     : design, the canonical/parity model, and the divergences
- README.md          : documentation index

Agent files:
- AGENTS.md : repository conventions plus a scannable API quick-reference for AI
              coding agents (single source of truth)
- CLAUDE.md : now defers to AGENTS.md
- README.md : rewritten from a stub into a real entry point

CI:
- ci/ci.yml   : canonical workflow (Node 24+latest matrix and a Go build/vet/test job)
- ci/README.md: how to sync ci/ into .github/workflows/, working around the
                workflow-scope restriction on direct pushes

https://claude.ai/code/session_01MWuia2drUKR7xEmhkpip3X
The two implementations now sit side by side:
- ts/   TypeScript package (src, test, dist, dist-test, package.json, LICENSE)
- go/   Go module

Relative paths still resolve (tsconfig outDir ../dist, the test's '../' import),
verified by installing, building, and testing from ts/. A LICENSE copy is kept in
the package directory so it stays self-contained for npm publish.

Updated to match:
- ci/ci.yml      : the Node job now runs with working-directory: ts
- AGENTS.md, CLAUDE.md, README.md, docs/ : repository paths point at ts/...
- .gitignore     : anchored coverage paths moved under ts/

Note: the deployed .github/workflows/ci.yml still runs npm from the repo root and
must be synced from ci/ci.yml (now working-directory: ts) or CI on main will fail
after this move.

https://claude.ai/code/session_01MWuia2drUKR7xEmhkpip3X
Determinism (the priority):
- dive/Dive now iterate keys in sorted order in BOTH languages, so output is
  deterministic and byte-identical across the ports (Go map iteration was
  random). DiveMap and Entity inherit deterministic key-collision resolution,
  and Go's order sorts use SliceStable so ties break by (key-sorted) order.

Correctness:
- Go Entity: guard path length < 2 (was an index-out-of-range panic on a
  $-shaped ent); such entries are skipped, matching TS.
- TS getdlog().log(file): filter on the file field, not the timestamp field
  (the filtered form previously always returned []).
- TS dive: real overloads so the mapper form is typed Record<string,any>,
  not any[].

Robustness / no input mutation:
- TS order and entity copy items before adding key/title$ (caller input is no
  longer mutated); both treat a missing title as empty rather than throwing.
- TS decircular clones Errors (same prototype) instead of mutating them.
- Go Decircular skips empty containers, removing the pointer-0 aliasing trap.

Cleanup:
- Go orderSort: single key lookup instead of an O(n^2) title$ rescan; drop the
  now-unused itemMap param. CamelifySlice drops a redundant copy. Fix the
  misleading Order comment and collapse Decircular's contradictory doc comment.
- TS: remove the no-op padStart in prettyPino and the redundant seps guard.

Tests: dive determinism (exact sorted order, stable under -count/-shuffle),
getdlog filter, entity malformed-skip, order no-mutation, decircular empties.
Docs updated throughout to describe deterministic, sorted dive.

https://claude.ai/code/session_01MWuia2drUKR7xEmhkpip3X
@rjrodger rjrodger merged commit c163b5c into main Jun 8, 2026
0 of 2 checks passed
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