# Developer reference

This section is for people changing this repository — not just calling the npm client. If you’re integrating a product app, you can stop at Troubleshooting.


# Standing up an environment from scratch

Need a new staging/production stack (database included)? Follow the guided walkthrough:

Do not invent a parallel schema: apply supabase/migrations/0001_notifications.sql only. Keep RLS deny-all; the Worker service role is the sole DB client.

# Integrating from another app (agent context)

If you are wiring a product app (not this repo), give your coding agent the full contents of docs/AGENT.md — it is the self-contained integration brief (auth, client APIs, dedupeKey, do/don’t).


# Repo map

org.inovus.notifications/
├── packages/
│   ├── core/       # Zod schemas + pure TS types (wire contract)
│   ├── api/        # Cloudflare Worker (Hono + OpenAPI + stores + V1 compat)
│   └── client/     # @inovus-medical/notifications (published)
├── supabase/migrations/
├── docs/           # ops + design companions
│   └── userguide/  # this guide
├── DESIGN.md       # architecture decisions
└── CLAUDE.md       # agent/dev invariants and commands
flowchart TB
  Core["packages/core schemas"] --> API["packages/api validation + OpenAPI"]
  Core --> Client["packages/client types inlined at build"]
  API --> Memory["store/memory"]
  API --> Supa["store/supabase"]
  Supa --> PG[(Postgres)]

# Commands you’ll use constantly

pnpm install
pnpm test                 # all packages
pnpm test:coverage        # ≥90% lines/branches — CI fails below
pnpm lint                 # biome
pnpm typecheck
pnpm openapi:generate     # refresh packages/api/openapi.json (committed)
pnpm --filter @inovus-medical/notifications-api dev
pnpm --filter @inovus-medical/notifications build

Single file:

pnpm --filter @inovus-medical/notifications-api exec vitest run test/routes.test.ts

# Invariants (do not break)

These are load-bearing. Full list in CLAUDE.md:

  1. Every NotificationStore method is user-scoped — handlers don’t re-check ownership ad hoc
  2. source is token-derived — never client-supplied on V2
  3. Dedupe is (user_guid, source, dedupe_key) and race-safe at the DB
  4. Wire = camelCase; DB = snake_case — mapping only in the Supabase adapter
  5. Errors are RFC 9457 problem+json on V2 (V1 envelope is the exception)
  6. openapi.json must match code — regenerate after route/schema changes
  7. No Supabase Auth / RLS policies for authz — Worker is the trusted path
  8. Client retries publishes because dedupeKey makes them idempotent — keep both sides of that contract (no key → no publish retry)

# Adding an API route

  1. Extend Zod schemas / types in packages/core
  2. Register an OpenAPI route in packages/api/src/app.ts
  3. Add route tests in packages/api/test/routes.test.ts
  4. Run pnpm openapi:generate and commit the snapshot
  5. Expose a client method if apps need it; add a changeset under .changeset/

# Storage adapters

Implement NotificationStore (packages/api/src/store/types.ts). See adapters.md.

Today:

  • memory — tests + local
  • supabase — PostgREST over fetch (Workers-native)

Selected by DB_DRIVER.


# Auth internals

  • jose verifies JWTs against JWKS (cached in-isolate + Cache API)
  • Multi-issuer via comma-separated AUTH_ISSUER
  • Publisher = groups ∩ PUBLISHER_GROUPS or scopes ∩ PUBLISHER_SCOPES
  • Cognito M2M tokens carry scopes, not groups — that’s why scopes exist

# V1 compat code

Lives in packages/api/src/v1compat.ts. Contract: v1-compatibility.md.

Don’t change V1 behaviour casually — existing callers depend on the envelope. Prefer adding V2 features on /v1/*.


# Client packaging notes

  • Zero runtime deps; React optional peer for /react
  • Core types imported by relative path so tsup inlines them
  • Convenience façade: createNotificationService in packages/client/src/notification-service.ts
  • Publish retries gated on dedupeKey in NotificationsClient.publish / publishSelf

User-visible client changes need a changeset (pnpm changeset).


# Release pipeline

High level (see .github/workflows/release.yml):

  1. PR includes a changeset
  2. Merge to default branch → Version Packages PR
  3. Merge that → npm publish (provenance) → staging deploy + smoke → production deploy + smoke

Also watch for branch-name mismatch (main vs master) if CI never fires.


# Design & deeper reading

Doc Contents
DESIGN.md Requirements, data model, trade-offs, milestones
why-v2.md Exhaustive V1 gap mapping
setup.md Full ops walkthrough
cognito-setup.md Resource server / M2M
adapters.md New store backends
Live /docs Generated OpenAPI UI

# Contributing mindset

  • Prefer small, reviewable PRs
  • Keep OpenAPI + tests in the same change as behaviour
  • Don’t add toast/bell UI to this repo — headless by design
  • When in doubt, re-read the invariants above

Back to the User Guide home.