CONTRIBUTING.md
Rama main · réplica de lectura del repositorio original.
# Contributing to OpenVPM
Thank you for your interest in contributing to OpenVPM!
## Development Setup
1. **Prerequisites**: Node.js 24.22.1 or a newer Node.js 24 patch, pnpm 9+, Docker
2. **Clone and install**:
```bash
git clone https://github.com/evangauer/openvpm.git
cd openvpm
nvm use # when using nvm; see .nvmrc
cp .env.example .env
# set NEXTAUTH_SECRET in .env to the output of: openssl rand -base64 32
pnpm install --frozen-lockfile
```
3. **Start services**:
```bash
docker compose --env-file .env -f docker/docker-compose.yml up -d postgres minio minio-bootstrap
pnpm db:migrate
pnpm db:seed
pnpm dev
```
Keep `.env` at the repository root beside `.env.example`. `pnpm dev` loads it
for the web server in `apps/web`; you do not need a second env file there.
Existing shell exports and app env values take precedence. If `DATABASE_URL`
is missing, the dev server stops with setup instructions. Restart `pnpm dev`
after changing the root env file. Production continues to use its deployment
environment.
## Project Structure
- `apps/web/` — Next.js product + dashboard and REST/tRPC APIs
- `apps/docs/` — Searchable Next.js staff-guide site
- `packages/db/` — Drizzle ORM schema and migrations
- `packages/api/` — Shared Zod validators and types
- `packages/config/` — Shared TypeScript and Tailwind config
- `packages/docs-content/` — Shared guide content and metadata
## Development Workflow
1. Create a feature branch from `main`
2. Make your changes
3. Run `pnpm test` (unit) and `pnpm build` (type-check + lint) to verify
4. Submit a pull request
## Testing
- **Unit tests** (Vitest): `pnpm test`. Co-locate as `*.test.ts` next to the code
(e.g. `lib/compat/openvpm/__tests__/`). Pure logic — mappers, helpers — should
be unit-tested without a database.
- **E2E tests** (Playwright): `pnpm test:e2e`.
## Code Style
- TypeScript strict mode
- Tailwind CSS for styling (follow existing design tokens)
- tRPC for all internal API endpoints
- Drizzle ORM for database queries
## Adding a compatibility endpoint or target
The public REST API ([docs/api](docs/api/README.md)) is OpenVPM's integration
moat: integrators (and AI agents) plug into a frozen, vendor-shaped contract.
The layout makes adding endpoints — and entire vendor-compatible "targets" —
repeatable:
- **Route handlers**: `apps/web/app/api/v1/<resource>/route.ts`. Keep them
**thin** — authenticate → validate → tenant-scoped Drizzle query → map →
respond. No business logic or shape knowledge in the handler.
- **Auth**: call `authenticateApiKey(req, "<scope>")` from `lib/api-auth.ts`.
**Every** query MUST be scoped by `ctx.practiceId` and filter
`isNull(table.deletedAt)` — a cross-tenant read is a security bug.
- **Mappers**: `lib/compat/<target>/mappers.ts` holds **pure** functions that
translate internal rows ↔ the target's shapes (enum crosswalks, date formats).
The target's request/response shapes live in `lib/compat/<target>/schema.ts`.
- **A new target** (e.g. an existing PIMS's public API) is a new
`lib/compat/<vendor>/` module plus an `app/api/compat/<vendor>/v1/` namespace —
the auth, pagination, and error helpers are shared.
Every endpoint ships with mapper unit tests **and** a contract test
(`schema.parse(toApiX(row))`) so internal changes can't silently break the
public contract.
## Good first issues
New here? These are scoped, self-contained, and well-tested — great first PRs.
Comment on the matching [issue](https://github.com/evangauer/openvpm/issues) (or
open one) to claim it.
- **Expand the drug dosing formulary** — add drugs with species-specific reference
ranges to `apps/web/lib/dosing/formulary.ts`. Data-only, fully unit-tested
(`apps/web/lib/dosing/__tests__/`). Keep ranges conservative and cite a source.
- **Drag-to-reschedule on the calendar** — the `appointments.reschedule` API
already exists with conflict checking; wire it into the schedule UI.
- **Appointment waitlist UI** — build a staff-facing list on the existing
tenant-scoped schema and tRPC router.
- **CSV import review UX** — deepen the existing Settings → Data flow with
clearer row-level validation and issue resolution for supported import types.
- **Embeddable booking widget** — an iframe/script version of the client-portal
booking flow (`apps/web/app/portal/[token]/book`).
Want to help but unsure where to start? Open a
[Discussion](https://github.com/evangauer/openvpm/discussions) and say hello.
## License
By contributing, you agree that your contributions will be licensed under the GNU AGPLv3.