npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cantonjs

v0.4.0

Published

Application-side TypeScript SDK for Canton participant Ledger API V2

Downloads

584

Readme


cantonjs is the application-side TypeScript SDK for teams building directly against a Canton participant's Ledger API V2. It provides tree-shakeable Ledger, Admin, and Test clients; injected transports; real-time streaming; structured errors; and first-class testing support for participant-connected app code.

The repo is aimed first at backend or full-stack participant services, participant-private React apps, and integration or data teams with participant access. Public Scan consumers and advanced stable/public Splice integrators are secondary users. It starts after Daml lifecycle, full-stack onboarding, and wallet connection are already handled by the official upstream tools.

Start with docs/positioning.md, the target users guide, and the ecosystem-fit guide for the detailed scope and boundary story.

Existing users: the current positioning reset changes the repo mental model and package boundaries, and it removes the legacy wallet and validator-overlap surfaces from the current package set. See CHANGELOG.md and the migration notes.

Development policy: the included runtime surface is gated at 100% statements, branches, functions, and lines, and every coverage exclusion or inline v8 ignore must be justified in EXCLUSIONS.md.

Features

  • Participant-side clients — Ledger, Admin, and Test client factories for direct application work
  • Injected transports — JSON API V2, gRPC, and fallback transport layers without hidden I/O
  • Real-time streaming — AsyncIterator WebSocket streams with auto-reconnect
  • Structured errors and testing — CJ-coded errors, recovery hints, mock transports, and sandbox fixtures
  • Optional codegen — generate TypeScript from existing Daml DAR artifacts when app code needs it
  • Participant-private React hooks — TanStack Query-powered hooks via cantonjs-react
  • Focused add-ons — public Scan, validator, token-standard, and interface packages around the participant-runtime core

Install

npm install cantonjs

Package Map

The repo centers on an app-side Ledger API V2 core plus focused add-ons around that boundary.

| Tier | Package | Stability | Purpose | | ---- | ------- | --------- | ------- | | Core | cantonjs | GA | Application-side Ledger, Admin, and Test clients; transports; streaming; errors; chains; and runtime typing helpers | | Optional Convenience | cantonjs-react | GA | Participant-private React hooks for application code | | Optional Convenience | cantonjs-codegen | GA | Optional DAR-to-TypeScript convenience from existing Daml artifacts | | Add-On | cantonjs-splice-scan | GA | Public Scan reads for DSO metadata, update history, and public ANS lookups | | Add-On | cantonjs-splice-validator | GA | Selected stable external validator support: ANS and filtered Scan Proxy reads | | Add-On | cantonjs-splice-interfaces | GA | Stable published Splice interface descriptors and generated types | | Add-On | cantonjs-splice-token-standard | GA | Participant-first CIP-0056 helpers for new token transfer and allocation flows |

For the canonical scope note that drives this package map, see docs/positioning.md. For a tool-by-tool "when to use what" guide, see docs/guide/ecosystem-fit.md. For the persona and anti-pitch version of that same story, see docs/guide/target-users.md.

Stability Tiers

  • GA: Covered by the normal semver promise for the pinned release line.
  • Experimental: May break in minor releases while the upstream surface is still moving.

Current compatibility target:

  • Canton GA line: 3.4.x
  • Splice GA line: 0.5.x
  • Vendored Splice artifacts: 0.5.17
  • Last pre-prune legacy line: 0.3.1

See compatibility policy and migration notes.

Quick Start

import { createLedgerClient, jsonApi } from 'cantonjs'

// 1. Create a transport
const transport = jsonApi({
  url: 'http://localhost:7575',
  token: 'your-jwt-token',
})

// 2. Create a party-scoped client
const client = createLedgerClient({
  transport,
  actAs: 'Alice::1234',
})

// 3. Create a contract
const created = await client.createContract('#my-pkg:Main:Asset', {
  owner: 'Alice',
  value: '100',
})

// 4. Exercise a choice
const tx = await client.exerciseChoice('#my-pkg:Main:Asset', created.contractId, 'Transfer', {
  newOwner: 'Bob',
})

// 5. Query active contracts
const contracts = await client.queryContracts('#my-pkg:Main:Asset')

Static bearer tokens remain supported. For request-scoped auth, provide an async token or session provider:

import { createLedgerClient, jsonApi, type AuthProvider } from 'cantonjs'

const auth: AuthProvider = async ({ request }) => {
  if (request.path.startsWith('/v2/state/')) return undefined
  return await getFreshJwtForAudience('participant')
}

const transport = jsonApi({
  url: 'http://localhost:7575',
  auth,
})

const client = createLedgerClient({
  transport,
  actAs: 'Alice::1234',
})

Streaming

Subscribe to real-time updates with auto-reconnect and offset tracking:

import { streamUpdates } from 'cantonjs'

const controller = new AbortController()

for await (const update of streamUpdates(transport, {
  beginExclusive: '0',
  signal: controller.signal,
})) {
  console.log('Update:', update.updateId)
}

Subpath Imports

Import only what you need for smaller bundles:

import { createLedgerClient } from 'cantonjs/ledger'
import { createAdminClient } from 'cantonjs/admin'
import { createTestClient } from 'cantonjs/testing'
import { localNet, devNet, testNet, mainNet, withChainOverrides } from 'cantonjs/chains'
import type { TemplateDescriptor, InferPayload } from 'cantonjs/codegen'

Network Presets

Built-in public presets are discovery-first. They carry public Scan discovery metadata, auth audience hints, and the pinned Splice release line, but they do not commit operator-specific participant or validator URLs.

Layer concrete deployment settings on top:

import { devNet, withChainOverrides } from 'cantonjs/chains'

const chain = withChainOverrides(devNet, {
  participant: {
    jsonApiUrl: process.env.CANTON_JSON_API_URL,
  },
  scan: {
    url: process.env.SPLICE_SCAN_URL,
  },
  validator: {
    apiBaseUrl: process.env.SPLICE_VALIDATOR_URL,
  },
})

chain.scan.discoveryRoot remains available when you need to resolve a live public Scan deployment from operator documentation first.

Clients

| Client | Purpose | | ---------------------- | ------------------------------------------------------------------- | | createLedgerClient() | Party-scoped contract operations (create, exercise, query, stream) | | createAdminClient() | Node administration (parties, users, packages, IDP) | | createTestClient() | Sandbox testing (time control, party allocation, sandbox lifecycle) |

Transports

| Transport | Description | | ---------------------------------- | ------------------------------------------------------------------------------------- | | jsonApi({ url, token }) | HTTP transport for Canton JSON API V2 with static bearer auth | | jsonApi({ url, auth }) | HTTP transport with async per-request token lookup | | jsonApi({ url, session }) | HTTP transport with async per-request token and header injection | | grpc({ grpcTransport, token }) | gRPC via injected ConnectRPC client with static bearer auth | | grpc({ grpcTransport, auth }) | gRPC via injected ConnectRPC client with async per-request token lookup | | grpc({ grpcTransport, session }) | gRPC via injected ConnectRPC client with async per-request token and header injection | | fallback({ transports }) | Failover across multiple transports |

Error Handling

Every error is a CantonjsError with a machine-readable code, recovery hints, and a traversable cause chain:

import { CommandRejectedError, TokenExpiredError } from 'cantonjs'

try {
  await client.createContract(templateId, args)
} catch (error) {
  if (error instanceof TokenExpiredError) {
    // error.code === 'CJ2001'
    // error.metaMessages === ['Refresh your JWT token']
  }
}

| Range | Domain | | ------ | ------------------------------------------- | | CJ1xxx | Transport (connection, HTTP, gRPC, timeout) | | CJ2xxx | Authentication (JWT, token lifecycle) | | CJ3xxx | Ledger (command rejection, authorization) | | CJ4xxx | Admin (party, user, package management) | | CJ5xxx | Streaming (WebSocket, reconnection) | | CJ6xxx | Codegen (type mismatch, generation) |

Packages

cantonjs-codegen

cantonjs-codegen is an optional DAR-to-TypeScript convenience for application code built from existing Daml artifacts. DPM remains canonical for Daml build, test, and codegen workflows.

Generate TypeScript types from a DAR you already have:

npm install --save-dev cantonjs-codegen

npx cantonjs-codegen --dar ./model.dar --output ./src/generated

Records become type aliases, variants become discriminated unions, templates become companion const objects with templateId and choices. See packages/cantonjs-codegen.

cantonjs-react

React hooks for participant-private ledger application state, powered by TanStack Query:

npm install cantonjs-react @tanstack/react-query
import { createLedgerClient, jsonApi } from 'cantonjs'
import { CantonProvider, useContracts, useCreateContract } from 'cantonjs-react'

const client = createLedgerClient({
  transport: jsonApi({ url: 'http://localhost:7575', token: 'your-jwt-token' }),
  actAs: 'Alice::1234',
})

function App() {
  return (
    <CantonProvider client={client}>
      <AssetList />
    </CantonProvider>
  )
}

function AssetList() {
  const { data: assets, isLoading } = useContracts({
    templateId: '#my-pkg:Main:Asset',
  })

  const { mutate: create } = useCreateContract({
    templateId: '#my-pkg:Main:Asset',
  })

  if (isLoading) return <div>Loading...</div>

  return (
    <div>
      <button onClick={() => create({ createArguments: { owner: 'Alice', value: '100' } })}>
        Create
      </button>
      <ul>
        {assets?.map((c) => (
          <li key={c.createdEvent.contractId}>{JSON.stringify(c.createdEvent.createArgument)}</li>
        ))}
      </ul>
    </div>
  )
}

See packages/cantonjs-react.

cantonjs-react stays focused on participant-private ledger state. For public Splice data, use TanStack Query directly with cantonjs-splice-scan; see docs/examples/react.md and docs/examples/public-scan-dashboard.md.

Splice Packages

  • cantonjs-splice-scan — GA public Scan reads for DSO metadata, updates, and public ANS lookups. Experimental Scan routes stay behind cantonjs-splice-scan/experimental. See docs/guide/scan.md.
  • cantonjs-splice-validator — selected stable external validator support for ANS and the filtered GA Scan Proxy subset. See docs/guide/validator-ans.md.
  • cantonjs-splice-token-standard and cantonjs-splice-interfaces — GA stable CIP-0056 descriptors and ledger-centric helpers for new transfer and allocation flows. See docs/guide/token-standard.md.

Testing

cantonjs provides first-class testing utilities. No vi.mock() needed — all dependencies are injected:

import { createLedgerClient } from 'cantonjs'
import { createMockTransport } from 'cantonjs/testing'

const transport = createMockTransport({
  responses: [{ contractId: 'contract-1', templateId: '#pkg:Mod:T' }],
})

const client = createLedgerClient({ transport, actAs: 'Alice::1234' })
const created = await client.createContract('#pkg:Mod:T', { owner: 'Alice' })

Integration testing with a real Canton sandbox:

import { setupCantonSandbox } from 'cantonjs/testing'

const sandbox = await setupCantonSandbox()
// sandbox.client is a fully configured TestClient

Canton Concepts

| Concept | Description | | ---------------- | ------------------------------------------------------------ | | Party | Identity unit (not an address), permissions via JWT | | Template | Daml contract definition (packageId:moduleName:entityName) | | Choice | Operation on a contract (like a function call) | | ContractId | Unique UTXO-style identifier for a contract instance | | DAR | Daml Archive — the deployment artifact | | Synchronizer | Consensus domain for transaction ordering |

Bundle Size

cantonjs is designed for minimal footprint:

| Entry Point | Size (minified + brotli) | | ----------------- | ------------------------ | | cantonjs | 5.78 kB | | cantonjs/ledger | 1.1 KB |

Requirements

  • Node.js >= 18
  • TypeScript >= 5.0.4 (optional peer dependency)

Development

git clone https://github.com/merged-one/cantonjs.git
cd cantonjs
npm install

npm test              # Run root tests
npm run test:coverage # Enforced root coverage gate
npm run test:coverage:all # Root + package coverage gates
npm run verify:ci:pr  # Full PR validation suite
npm run typecheck     # Type-check
npm run lint          # Lint
npm run build         # Build ESM + CJS + types
npm run size          # Bundle size audit
npm run docs:dev      # Start docs dev server

Contributing

See CONTRIBUTING.md for development setup, coding conventions, and pull request guidelines.

Architecture Decisions

Design decisions are documented as Architecture Decision Records (ADRs):

| ADR | Topic | | ------------------------------------------------------ | -------------------------------- | | 0001 | TypeScript with function exports | | 0002 | Transport abstraction | | 0003 | Party-scoped client architecture | | 0004 | Structured error model | | 0005 | Streaming architecture | | 0006 | Testing strategy | | 0007 | Codegen architecture | | 0008 | React integration |

Related

License

Apache-2.0