Skip to content

feat(candid): add Zod-inspired Candid codec API (Slice 1)#184

Merged
b3hr4d merged 7 commits into
mainfrom
feat/candid-codec-slice-1
Jun 21, 2026
Merged

feat(candid): add Zod-inspired Candid codec API (Slice 1)#184
b3hr4d merged 7 commits into
mainfrom
feat/candid-codec-slice-1

Conversation

@b3hr4d

@b3hr4d b3hr4d commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add the foundational Candid codec layer to @ic-reactor/candid — Slice 1 of the Candid Codec plan.

This introduces a Zod-inspired c namespace for building typed, metadata-rich Candid contracts in TypeScript:

import { c } from "@ic-reactor/candid"

const Account = c.record({
  owner: c.principal(),
  subaccount: c.opt(c.blob()),
})
type Account = c.infer<typeof Account>

const Ledger = c.service({
  icrc1_balance_of: c.query([Account], c.nat()),
  icrc1_transfer: c.update([TransferArg], TransferResult),
})

const idlFactory = Ledger.idlFactory
type _SERVICE = c.ServiceOf<typeof Ledger>

What's Included

New: packages/candid/src/codec/

File Purpose
types.ts CandidMetadata, manifest types
codec.ts Abstract CandidCodec<T> base with immutable metadata chaining
primitives.ts 19 primitive factories (text, bool, nat, principal, etc.)
composites.ts opt, vec, record, variant, tuple with full type inference
service.ts query, update, oneway, service codecs with idlFactory & manifest()
index.ts The c namespace (const + namespace merge for c.infer<T>)

Modified

  • src/index.ts — added export * from "./codec"
  • tests/unit/index.test.ts — added new exports to the allowlist

Key Design Decisions

  • Immutable metadata: .describe(), .label(), .example(), .meta() return new instances
  • Lazy IDL: toIDL() called at consumption time (enables future c.recursive())
  • Namespace merge: c is both a const object and a TS namespace for c.infer<T> / c.ServiceOf<T>
  • Zero breaking changes: purely additive, no existing code modified

Tests

63 new tests covering:

  • Primitive IDL equivalence
  • Metadata immutability
  • Composite codec construction
  • Service idlFactory equivalence with hand-written factories
  • manifest() structured output
  • IDL encode/decode roundtrips
  • Compile-time type inference checks
  • Edge cases

Verification

  • vitest run tests/unit/codec.test.ts — 63/63 pass
  • vitest run tests/unit/index.test.ts — 11/11 pass
  • tsc --noEmit — clean type check

Next Steps

This is Slice 1 of 5 per the Candid Codec plan:

  1. Codec primitives & service layer ✅ (this PR)
  2. Codegen renderer
  3. Parser schema (parseDid())
  4. Comment metadata parsing
  5. Replace v4 generated output

Add the foundational codec layer to @ic-reactor/candid under src/codec/:

- Base CandidCodec<T> class with immutable metadata chaining
- 19 primitive codec factories (text, bool, nat, int, principal, etc.)
- 5 composite codecs (record, variant, opt, vec, tuple)
- Service/method codecs (query, update, oneway, service)
- idlFactory getter compatible with @icp-sdk/core actor creation
- manifest() for structured method/argument metadata
- c namespace with c.infer<T> and c.ServiceOf<T> type utilities
- CandidMetadata model for docs, labels, examples, form hints

63 new tests covering IDL equivalence, metadata immutability,
encode/decode roundtrips, type inference, and edge cases.
Comment thread packages/candid/tests/unit/codec.test.ts Fixed
Comment thread packages/cod/tests/codec.test.ts Fixed
Comment thread examples/candid-codegen-playground/src/main.ts Fixed
@b3hr4d b3hr4d merged commit 48954f2 into main Jun 21, 2026
10 checks passed
@b3hr4d b3hr4d deleted the feat/candid-codec-slice-1 branch June 21, 2026 07:37
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