Repository navigation
[2.x] Adapt dynamic-codecs to Codama v2 - #1185
Conversation
|
trevor-cortex
left a comment
There was a problem hiding this comment.
Summary
Ports @codama/dynamic-codecs to the v2 node model. The visitor is restructured around three helpers (visitLinkedNode, getCollectionCodec, applyTransform), with the per-type-node transforms applied via an interceptVisitor layer that wraps every visit — so transforms compose innermost-first and link-node transforms naturally stack on top of the linked type's own. Integers now always decode to bigint (with a transformCodec shim for the sub-64-bit sizes) and honour endian; float/fixed-point/date-time/duration are added; enums move to { __kind, __discriminator, data } with raw identifiers; sentinelCountNode is wired through to kit's sentinel array size. On the visitors-core side, ProvidedScope gains resolveOrThrow (sharing a resolveOutcome core with resolve), and resolveWithin now tracks which injection dead-ended so the new CODAMA_ERROR__INJECTED_VALUE_NOT_PROVIDED names the innermost culprit rather than the enclosing struct.
The code is clean and the test coverage is solid — I checked the pre/post-offset hex expectations against kit's offsetCodec semantics (preOffset in the post-offset scope is the original offset, so ff000000aa00 for the preOffset strategy is right), and the injected-value tests cover the standalone-type, linked-from-instruction, and parent/child-instruction cases well.
Things I verified
interceptVisitorbindsthisto the outermost visitor, sovisit(node.type, this)insidebaseVisitorre-enters through the transform/scope/stack layers at every depth. Transforms on nested fields, enum variant data, count prefixes, etc. all get applied.getNodeValueCodecopens frames only for ancestor instructions and leaves the last path node torecordProvidedScopeVisitor, so there's no double-push when the path ends at an instruction.- Frames are ordered root→leaf and
ProvidedScope.findsearches innermost-first, so shadowing works as documented. resolve's docblock ("all-or-nothing …undefinedis returned") is still accurate after theOutcomerefactor.
Non-blocking notes
Changeset. I don't see a .changeset/*.md in the file list, and CONTRIBUTING.md asks for one per user-facing concern. This PR renames the public getNodeCodec/getNodeCodecVisitor API, adds ProvidedScope.resolveOrThrow to @codama/visitors-core, and adds an error code to @codama/errors. If the 2.x branch is covered by a seeded major changeset and per-PR entries aren't wanted during candidacy, ignore this — just flagging in case it was an oversight.
Enum links inside provided values resolve against the injection site. In values.ts, visitInjectedValue visits the resolved node with this, so any enumValueNode inside a provided value has its enum link resolved via the current stack.getPath(). If an instruction in program A provides an enumValueNode('foo', …) (relative link) and the injection lives inside a defined type in program B that A links to, the link would resolve in B, not A. Probably fine to leave — it's an edge case and the value author can use an explicit program on the link — but worth knowing for whoever wires up dynamic-parsers/dynamic-instructions on top of this.
Map keys. Maps encode via Object.entries, so keys always reach the key codec as strings. Kit's number encoders coerce, so an integer-keyed map should still round-trip as { "1": … }, but there's no test for non-string keys. Not a regression from v1 as far as I can tell.
For subsequent reviewers
- The enum value shape (
datanesting, raw identifiers instead ofpascalCase) is a wire-format change for anything consuming this package's output.dynamic-parsers,dynamic-instructionsanddynamic-clientonly get@solana/*bumps here, so presumably their own v2 adaptation follows in later PRs of the stack — worth confirming nothing in those packages currently depends on the old__kind: 'PascalCase'/ spread-fields shape. pnpm-lock.yamlchurn is the 8.3→8.4@solana/*bump; I didn't audit it line by line.
eec1aa4 to
ffe14c7
Compare
ffe14c7 to
b926e05
Compare
16b2a55 to
94fe34a
Compare
b926e05 to
8d5b927
Compare
aca2adc to
2bb3538
Compare
1416323 to
dfad4fa
Compare
2bb3538 to
0554451
Compare
dfad4fa to
d814828
Compare
df64b9f to
9c2e80a
Compare
ed7bb21 to
7cf264c
Compare
3dd3fd7 to
0d35b27
Compare
7cf264c to
ea8ce7d
Compare
497ad44 to
8955bbe
Compare
ea8ce7d to
22d0df8
Compare
8955bbe to
e9f57f4
Compare
22d0df8 to
26f1b77
Compare
26f1b77 to
027a158
Compare

This PR adapts
@codama/dynamic-codecsto Codama v2.getNodeCodecandgetNodeCodecVisitortogetNodeValueCodecandgetNodeValueCodecVisitor.bigintand respectsendian. AddsfloatTypeNode, fixed points, date-times and durations, which encode as their raw integer.{ __kind, __discriminator, data }using raw identifiers. Struct keys are raw identifiers too.sentinelCountNodesupport for arrays, sets and maps.data, and resolves injected values from theprovidesof enclosing instructions.ProvidedScope.resolveOrThrowto@codama/visitors-coreand theINJECTED_VALUE_NOT_PROVIDEDerror, thrown when a required injected value is not provided.@solana/*dependencies to 8.4.0.