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.
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.