We want to make it easy to contribute to coco.
The kinds of changes that are usually a good fit:
- Bug fixes
- Adapter improvements
- Better tests and reproducible fixtures
- Type safety or API ergonomics improvements
- Documentation updates and examples
- Performance or reliability fixes
If you want to add a new public API, change protocol behavior, or introduce a new package, please start with an issue or design discussion first. Small fixes can go straight to a pull request.
If you are unsure whether a change is in scope, open an issue with the problem you want to solve and the approach you have in mind.
-
Requirements: Bun (CI currently runs Bun 1.2.18)
-
Install dependencies from the repo root:
bun install
-
If you plan to run IndexedDB browser tests locally, install Playwright browsers:
bunx playwright install
packages/core- storage-agnostic core library, services, operations, models, repositories, and testspackages/react- React hooks and providers for the core packagepackages/adapter-tests- shared contract test helpers for storage adapterspackages/indexeddb- IndexedDB adapter for web environmentspackages/expo-sqlite- Expo SQLite adapter for React Native and Expo appspackages/sqlite3-better-sqlite3adapter for Node.jspackages/sqlite-bun- Bun SQLite adapterpackages/docs- VitePress documentation site
From the repository root:
bun install
bun run build
bun run typecheck
bun run docs:dev
bun run docs:buildUseful package-level commands:
bun run --filter='@cashu/coco-core' test
bun run --filter='@cashu/coco-core' test:unit
bun run --filter='@cashu/coco-core' test:integration
bun run --filter='@cashu/coco-react' lint
bun run --filter='@cashu/coco-indexeddb' test
bun run --filter='@cashu/coco-indexeddb' test:browser
bun run --filter='@cashu/coco-sqlite' test
bun run --filter='@cashu/coco-sqlite-bun' test
bun --cwd packages/expo-sqlite testRun the smallest relevant test set for your change. If you touch shared logic,
running bun run build, bun run typecheck, and the affected package tests is a
good default.
bun run --filter='@cashu/coco-core' test -- test/unit/Manager.test.ts
bun run --filter='@cashu/coco-core' test -- -t "initializeCoco" test/unit/Manager.test.ts
bun run --filter='@cashu/coco-sqlite' test -- src/test/integration.test.ts
bun run --filter='@cashu/coco-indexeddb' test -- src/test/integration.test.ts
bun run --filter='@cashu/coco-indexeddb' test:browser -- src/test/integration.test.ts- Keep pull requests narrow and easy to review
- Prefer one logical change per PR
- Update docs when public behavior changes
- Do not edit generated
dist/output
Please open an issue before spending time on:
- new packages or adapters
- significant public API changes
- protocol behavior changes
- large refactors
This helps us agree on direction before implementation.
We often use a git worktree per feature. If you are working from a feature
worktree and there is a FEATURE_TODO.md file at the root, keep it updated as you
work.
- Explain the problem and why your change is the right fix
- Include the verification steps you ran
- Keep descriptions short and concrete
- Add screenshots when a PR changes UI or docs visuals
- Mention follow-up work instead of bundling unrelated fixes into the same PR
If your change affects a published package, add a changeset:
bunx changesetUse concise, conventional commit-style titles, and prefer adding a scope when the affected package or area is clear:
feat:new functionalityfix:bug fixesdocs:documentation changesrefactor:code cleanup without behavior changestest:test changeschore:maintenance work
We commonly use scoped messages such as:
fix(core): prevent duplicate quote syncfeat(react): add wallet provider reset hookdocs(docs): clarify adapter setup
If a change spans the whole repository rather than one package, an unscoped title
like chore: update release workflow is fine.
Please follow the project guidance in AGENTS.md. The short version:
- Use TypeScript with ESM
importandexport - Prefer
import typefor type-only imports - Keep formatting aligned with
.prettierrc: 2 spaces, semicolons, 100-column lines - Order imports as external, then internal or alias, then relative
- Use
PascalCasefor classes and types,camelCasefor values and functions - Avoid
anyunless it is tightly scoped and justified - Add JSDoc for public APIs and non-obvious flows
- Validate inputs early and return empty arrays for no-op cases when appropriate
- Prefer domain errors from
packages/core/models/Error.ts - Preserve error causes when wrapping failures
- Use structured logging with context
- Keep repository operations atomic and check invariants before mutating state
- Normalize mint URLs with
normalizeMintUrl()before persistence - Export public package APIs through each package
index.ts
- Keep hook dependency arrays correct
- Use
useCallbackoruseMemowhen a value participates in dependencies - Normalize unknown caught errors with
e instanceof Error ? e : new Error(String(e))
We use bun:test across most packages, plus Vitest for some adapter coverage.
- Put tests under
test/unitortest/integration - Name test files
*.test.ts - Prefer Bun
mock()for spies and doubles - Keep tests deterministic and await async work explicitly
- Add or update tests with behavior changes whenever practical
For browser coverage in packages/indexeddb, run:
CI=1 bun run --filter='@cashu/coco-indexeddb' test:browserPublished packages are versioned with Changesets. If your PR changes runtime behavior, public types, package exports, or documentation for a published package, you should usually include a changeset unless a maintainer tells you otherwise.
Stable and prerelease npm publishes validate and publish the tagged commit.
Package versions and changelogs must be committed before the GitHub Release is
published. See RELEASING.md for the maintainer release checklist.
The fastest way to get a PR reviewed is to keep it easy to understand:
- describe the user-visible problem
- keep the implementation straightforward
- show how you verified the change
- avoid unrelated cleanup in the same PR
Thanks for contributing.