Skip to content

[2.x] Adapt validators to Codama v2 - #1173

Merged
lorisleiva merged 1 commit into
mainfrom
09-23-adapt_validators_to_codama_v2
Sep 30, 2026
Merged

lorisleiva merged 1 commit into
mainfrom
09-23-adapt_validators_to_codama_v2

Conversation

@lorisleiva

@lorisleiva lorisleiva commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

This PR adapts @codama/validators to the v2 node model and adds the validation rules v2 introduces. The package is fully green: it type-checks, builds, tree-shakes, and 72/72 tests pass.

Adapted

  • Enum variants are validated through the single enumVariantTypeNode; the duplicated "variant has no name" check in visitEnumType is dropped.
  • The origin check is removed, as programNode.origin no longer exists.
  • Instructions run getResolvedInstructionInputsVisitor once, with the instruction's ancestry and enclosing providers. Its errors — cyclic dependencies, invalid dependencies, invalid provided values, optional PDA seed accounts — are reported as validation errors. Non-Codama errors are now rethrown instead of being swallowed.
  • The "bump of a potential signer" rule reads the resolved data-field defaults, so it also catches bumps supplied through an injection.
  • The "conflicting argument names" check is removed: instruction data is a struct, covered by the struct field uniqueness rule.
  • "has no name" messages now say "has no identifier".

Added

  • Identifier uniqueness per sibling set, following the casing-collision rule in Relax identifier uniqueness to casing collisions spec#175: identifiers must not have the same camelCase form, where words are split at underscores (dropping empty segments), at lowercase-or-digit-to-uppercase boundaries and before the last capital of an acronym run — so getURL/get_url and _foo/foo collide whilst foo_dart/food_art coexist. Exact duplicates and casing collisions are both errors. Covered scopes: root programs; a program's accounts, instructions, defined types, PDAs, events, errors and constants; an instruction's accounts, remaining accounts, provides keys and sub-instructions; struct fields; enum variants; variable PDA seeds.
  • An error for injected values within an instruction that are neither provided nor given a fallback.
  • An info item for textNodes without plugins, which the spec considers non-canonical.

Follow-ups

  • Injections within a defined type that an instruction links to are not checked, as they are visited under their program rather than the consuming instruction.
  • Identifiers may coincide across scopes (e.g. an account and a defined type, or instructions of two programs); renderers emitting them into a shared namespace must disambiguate them.

@changeset-bot

changeset-bot Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 2e57b9c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@lorisleiva lorisleiva changed the title Adapt validators to Codama v2 [2.x] Adapt validators to Codama v2 Sep 23, 2026
@lorisleiva

Copy link
Copy Markdown
Member Author

@trevor-cortex

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Adapts @codama/validators to the v2 node model: identifier everywhere, single enumVariantTypeNode, origin gone, and the instruction checks now go through getResolvedInstructionInputsVisitor with the real ancestry/ProvidedScope so cycles, bad dependencies, invalid provided values and injected bumps are all reported through one path. Adds sibling-set identifier uniqueness (exact + camelCase-fold) across programs/instructions/structs/enums/PDAs, an error for unresolvable injections consumed within an instruction, and an info for plugin-less textNodes.

I verified the visitors-core plumbing this leans on and it lines up:

  • recordProvidedScopeVisitor only pushes a frame when provides is non-empty, so the outerScope.pop() guard is exactly right (though it's a coupling worth keeping in mind if that visitor ever changes).
  • The resolver's own recordNodeStackVisitor/recordProvidedScopeVisitor re-push the instruction, so passing slice(0, -1) for both is correct — no double push.
  • ProvidedScope.resolve with REGISTERED_NODE_KINDS can never hit the invalid-kind throw, so visitInjectedValue is exception-safe. The isNested exclusion for providedNode/injectedValueNode ancestors is sound: a nested injection is either unreachable (shadowed fallback) or surfaces as a dead-end when the consuming injection is checked.
  • instructionRemainingAccountsNode does carry an identifier in v2, so that collision scope is legitimate.

Things to watch out for

camelCase algorithm divergence (main point, inline). getCamelCaseForm disagrees with camelCase from @codama/nodes on SCREAMING_SNAKE_CASE: MAX_SIZE → maxSize in nodes vs mAXSIZE here. Since renderers emit via the nodes helper, MAX_SIZE + max_size constants would pass validation and then collide in generated code. Either the validator should reuse camelCase from nodes, or (if the spec rule deliberately defines the split-before-uppercase algorithm) the nodes helper is what needs aligning — but the two shouldn't disagree. A test with an all-caps identifier would pin the intended behaviour either way.

Changeset. CONTRIBUTING says user-facing changes need one and this changes messages and adds new error rules. I know the branch is in pre/rc mode with a seeded major changeset — if the convention on 2.x adaptation PRs is to skip per-package changesets, ignore this; otherwise npx changeset add --empty for @codama/validators.

Notes for other reviewers

  • The "has no identifier" checks for instruction accounts are still anchored on the instruction node rather than the account (pre-existing; inline nit).
  • The follow-ups in the description (injections inside linked defined types, cross-scope identifier coincidence) are real gaps but reasonable to defer — the resolver already tolerates missing providers inside linked data shapes by design.
  • Tests cover each new rule with a positive and a negative case; the it validates program nodes test now builds an invalid node via a cast, which is the honest way to do it now that constructors brand identifiers.

Comment thread packages/validators/src/identifierCollisions.ts Outdated
Comment thread packages/validators/src/getValidationItemsVisitor.ts

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review

Re-fetched the diff. The one substantive point from my first pass is resolved: getIdentifierWords now implements the spec#175 algorithm (underscore split, lower/digit→upper boundary, acronym-run boundary) rather than the ad-hoc split-before-uppercase, so MAX_SUPPLY/maxSupply and getURL/get_url collide as intended, and the parametrised tests pin both the colliding and coexisting pairs. I walked the regexes against every case in the two test.each tables plus a few extras (getURL2Go, ABCDef, _foo, foo__bar) and they behave as documented. Approving.

One follow-up (out of scope here)

The spec algorithm and camelCase in @codama/nodes now differ in the other direction: camelCase('getURL') yields getURL and camelCase('HTTPServer') yields hTTPServer in nodes, whereas the validator folds them to getUrl/httpServer. Consequences today are mild — the validator is stricter for acronym identifiers, and the only case where nodes collides but the validator doesn't is contrived (HTTPServer vs hTTPServer, which the coexist test explicitly permits). But since the spec now defines the canonical word-splitting, it'd be worth moving getIdentifierWords into @codama/nodes and building camelCase/pascalCase/snakeCase on top of it so renderers and the validator agree by construction. Not for this PR.

Not re-raising

Changeset question and the "instruction account has no identifier" anchoring nit from the previous review both stand as-is; neither blocks.

@lorisleiva
lorisleiva force-pushed the 09-23-adapt_visitors-core_to_codama_v2 branch 2 times, most recently from 039e0b0 to bf3f736 Compare September 30, 2026 14:07
@lorisleiva
lorisleiva force-pushed the 09-23-adapt_validators_to_codama_v2 branch from 8a167b4 to b91b06a Compare September 30, 2026 14:07
@lorisleiva
lorisleiva force-pushed the 09-23-adapt_visitors-core_to_codama_v2 branch from bf3f736 to 98f423f Compare September 30, 2026 14:08
@lorisleiva
lorisleiva force-pushed the 09-23-adapt_validators_to_codama_v2 branch from b91b06a to 2e1217c Compare September 30, 2026 14:08
@lorisleiva
lorisleiva force-pushed the 09-23-adapt_visitors-core_to_codama_v2 branch from 98f423f to 9c63d57 Compare September 30, 2026 14:09
@lorisleiva
lorisleiva force-pushed the 09-23-adapt_validators_to_codama_v2 branch from 2e1217c to 6a242eb Compare September 30, 2026 14:09
Base automatically changed from 09-23-adapt_visitors-core_to_codama_v2 to main September 30, 2026 14:10
@lorisleiva
lorisleiva force-pushed the 09-23-adapt_validators_to_codama_v2 branch from 6a242eb to 2e57b9c Compare September 30, 2026 14:11
@lorisleiva
lorisleiva marked this pull request as ready for review September 30, 2026 14:11
@lorisleiva
lorisleiva merged commit 514a143 into main Sep 30, 2026
2 of 4 checks passed
@lorisleiva
lorisleiva deleted the 09-23-adapt_validators_to_codama_v2 branch September 30, 2026 14:12
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