feat: introduce ArgCodec system with symbol tags for built-in codecs#6
Merged
feat: introduce ArgCodec system with symbol tags for built-in codecs#6
Conversation
- Add ArgCodec<T> type with is/encode/decode/writeBack/reconcile hooks
- Add SharedResourcesConfig type with optional codecs array
- Add configureCodecs() export and resource.configure({ codecs }) API
- Extract all built-in type handling into composable codec files:
src/codecs/undefined.ts, bigint.ts, date.ts, map.ts, set.ts, array.ts, object.ts
- Use Symbol.for() tags for all built-in codecs to prevent shadowing
- Simplify shared-resources.ts: encodeArg, decodeArg, tryReconcileInPlace
now fully delegate to the codec registry
- Support class instances without a codec by encoding own enumerable
data properties and reconciling back onto the original instance
- Preserve function-valued properties during encoding and writeBack
- Remove collision-escape (esc) mechanism — outer sentinel wrap makes it unnecessary
- Add tagToWire() helper for symbol-to-string wire conversion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the ad-hoc type-switch logic in shared-resources.ts with a composable codec registry, and exposes a public API for users to register custom serialization strategies for class instances and other non-JSON-representable values.
Motivation
The previous implementation handled
Date,Map,Set,undefined,BigInt, arrays, and plain objects through a series ofif/elsebranches hardcoded inencodeArg,decodeArg, andwriteBackObject/writeBackArray. This made it impossible for users to teach the system about their own types (e.g. class instances), and any new built-in type would require modifying the core file directly.Three concrete bugs also existed:
JSON.stringifyas a plain object, decoded as a plain object — no reconstruction).writeBackbecause the old code iterated all keys without distinguishing functions.TypeErrorinstead of degrading gracefully.Changes
New public API
ArgCodec<T>type — definestag,is,encode,decode, and an optionalwriteBackhook with areconcilecallback for nested in-place updates.SharedResourcesConfigtype —{ codecs?: ArgCodec<any>[] }passed tosharedResources().configureCodecs(codecs)— registers codecs globally; new codecs are prepended (checked first) and merge by tag, so a later registration with the same tag replaces the earlier one.resource.configure({ codecs })— per-resource codec registration that runs in both parent and child via module evaluation.codecs — seven new files
Each built-in type is now a self-contained codec module. Tags use
Symbol.for('sr:*')so they can never be accidentally overwritten by a user codec that picks the same string tag.undefinedSymbol.for('sr:u')bigintSymbol.for('sr:bi')DateSymbol.for('sr:d')MapSymbol.for('sr:m')SetSymbol.for('sr:s')ArraySymbol.for('sr:a')isPlainObjectSymbol.for('sr:obj')shared-resources.ts
encodeArg,decodeArg,decodeEncoded,tryReconcileInPlaceall simplified to codec-registry loops — no more hardcoded type checks.tagToWire(tag)helper converts symbol tags to theirSymbol.keyFor()string for the wiretfield.decodeEncodedresolves symbol-tagged codecs by matchingSymbol.keyFor(c.tag) === enc.t.objectCodec(own enumerable data properties, functions skipped) and reconciled back onto the original instance on the caller side — prototype is preserved, data is updated.encodeObjectValues,encodeObject,decodeObjectValues,writeBackArray,writeBackObject,writeBackDate,writeBackMap,writeBackSet, and the collision-escape (esc) mechanism (no longer needed since the outer{ __sr_enc, t, v }sentinel wraps encoded data).Tests
pointCodec) and function-property preservation.writeBackhook invocation,reconcilecallback for nested values.Wire format compatibility
The wire format for arrays and plain objects has changed:
[...](raw){ "__sr_enc": "c", "t": "sr:a", "v": [...] }{...}(raw){ "__sr_enc": "c", "t": "sr:obj", "v": {...} }