This page documents the inter-package dependency graph for the did-btcr2-js monorepo. Understanding it is essential when:
dependencies AND add a TypeScript project reference to its tsconfig.json)common
├── keypair
│ ├── cryptosuite ──────────┐
│ ├── bitcoin ──────────────┤
│ └── kms ──────────────────┤
├── smt ──────────────────────┤
└── method <─────common,keypair,cryptosuite,bitcoin
└── api <─────common,keypair,cryptosuite,bitcoin,kms,method,smt
└── cli <─common,api,cryptosuite,method
The <─ arrows show “depends on”: method depends on common, keypair, cryptosuite, and bitcoin. The leftmost packages have no upstream workspace dependencies; the rightmost (cli) has the most.
| Package | Workspace dependencies | Direction |
|---|---|---|
common |
(none) | Foundation: depends on nothing |
keypair |
common |
Used by all crypto-touching packages |
cryptosuite |
common, keypair |
Data Integrity proofs |
bitcoin |
common, keypair |
Bitcoin RPC/REST clients |
kms |
common, keypair |
Key management |
smt |
(none: no workspace deps) | Self-contained; uses @noble/* directly |
method |
common, keypair, cryptosuite, bitcoin |
Core implementation |
api |
common, keypair, cryptosuite, bitcoin, kms, method, smt |
High-level SDK facade |
cli |
common, cryptosuite, method, api |
Binary entry point |
All workspace dependencies use the workspace:^ protocol in package.json. At publish time, pnpm rewrites these to concrete semver ranges in the published tarballs.
The build proceeds in topological order. Three “waves” can run in parallel within each:
Wave 1: common
Wave 2: keypair, smt (parallel: both only depend on common or nothing)
Wave 3: cryptosuite, bitcoin, kms (parallel: all depend on common + keypair)
Wave 4: method (depends on cryptosuite + bitcoin from wave 3)
Wave 5: api (depends on method + kms + smt)
Wave 6: cli (depends on api)
When you run pnpm build from the repo root, pnpm walks this graph automatically. When you run pnpm build:ts, TypeScript’s project references mechanism walks the same graph (declared in the references field of each tsconfig.json) and uses incremental caches via dist/.tsbuildinfo to skip unchanged packages.
Same as build order, but with the additional constraint that you cannot publish a package whose updated dependency is not yet on npm. The recommended manual sequence:
common to keypair to smt to cryptosuite to bitcoin to kms to method to api to cli
pnpm -r publish does this automatically: workspace deps are resolved in topological order.
When you add a new import from package A to package B (e.g., import { Foo } from '@did-btcr2/bar' in packages/foo/src/index.ts), you must:
package.json dependencies with the workspace:^ protocol:
"@did-btcr2/bar": "workspace:^"
tsconfig.json:
"references": [
{ "path": "../bar" }
]
pnpm install so pnpm refreshes the symlink in packages/foo/node_modules/@did-btcr2/bar.pnpm build:ts to verify the project reference resolves and B is built before A.If you skip step 2, the build will fail at runtime (because B’s dist/ doesn’t exist when A tries to consume it). If you skip step 1, pnpm won’t create the symlink and the import will fail to resolve.
The dependency graph is intentionally acyclic. Don’t introduce cycles. If you find yourself wanting to import from a downstream package back into an upstream one, the right answer is almost always:
common)TypeScript project references would catch a cycle attempt at build time, but it’s better to think about the design before the type checker has to enforce it.