Skip to content

Development

Contributor guide for working in the reprojs monorepo.

Prereqs

  • Bun 1.3+
  • Docker + Docker Compose (for dev Postgres)
  • Node is not required — bun is the runtime.

First-time setup

bash
git clone https://github.com/Ripwords/ReproJs.git
cd reprojs
bun install
cp .env.example .env
# Uncomment the LOCAL DEVELOPMENT line and point DATABASE_URL at the dev Postgres:
# DATABASE_URL=postgres://postgres:postgres@localhost:5436/repro

Fill in the four required secrets in .env (see Configuration). For local dev, BETTER_AUTH_URL=http://localhost:3000 and MAIL_PROVIDER=console are fine.

Starting the dev loop

bash
bun run dev:docker   # starts the dev Postgres on :5436
bun run db:push      # syncs the schema from TS (dev uses push, not migrate)
bun run dev          # starts the dashboard on :3000 with HMR

The dashboard runs on localhost:3000 with hot module reload. Magic-link URLs print to the terminal when MAIL_PROVIDER=console.

To stop Postgres without dropping data: bun run dev:stop. To reset the DB: docker compose -f apps/dashboard/docker/docker-compose.dev.yml down -v then bun run db:push.

SDK development

bash
bun run sdk:build    # builds packages/core — ESM + IIFE + .d.ts
bun run sdk:watch    # watches for SDK source changes
bun run demo         # runs the demo playground on :4000

The demo loads the freshly-built IIFE so you can exercise the widget against a real dashboard. Point it at your dev instance by editing packages/ui/demo/index.html's Repro.init({...}) call.

Tester extension development

bash
bun run ext:dev      # Vite dev mode — HMR for popup/options, rebuilds MV3 bundles on save
bun run ext:build    # one-off production build → apps/extension/dist/
bun run ext:test     # unit tests (bun test, src/)

ext:build calls scripts/sync-sdk.ts first, which copies the freshly-built @reprojs/core IIFE into the extension's public/ directory so the service worker ships the same SDK the embed uses. Run bun run sdk:build first (or bun run ext:build which chains it).

To load the extension locally: open chrome://extensions, enable Developer mode, click Load unpacked, and point it at apps/extension/dist/. For ext:dev's HMR bundle use apps/extension/dist/ the same way — CRXJS writes there continuously.

End-to-end tests run against a built extension via Playwright:

bash
bun run --filter @reprojs/extension test:e2e

That builds with vite.config.e2e.ts (extra test hooks) then runs the Playwright suite in apps/extension/tests/.

Useful scripts

ScriptWhat it does
bun run devDev server (Nuxt + HMR) on :3000
bun run buildProduction Nuxt build → apps/dashboard/.output/
bun run sdk:buildBuild the SDK bundles (ESM + IIFE + .d.ts)
bun run demoSDK demo playground on :4000
bun run ext:devVite dev mode for the tester Chrome extension
bun run ext:buildProduction build for the tester extension → apps/extension/dist/
bun run ext:testExtension unit tests
bun run checkoxfmt --check + oxlint — runs in CI
bun run lintoxlint only
bun run fmtoxfmt write
bun run fixoxfmt --write + oxlint --fix
bun run testFull test suite (SDK + dashboard — needs dev server up)
bun run test:sdkSDK tests only — no Postgres / dev server required
bun run db:genRegenerate Drizzle migrations from schema changes
bun run db:pushSync schema directly (dev only — prod uses migrate)
bun run docs:devVitePress docs dev server

Testing

  • SDK tests (packages/*/*.test.ts) — pure-JS, bun test packages/, no DB.
  • Dashboard tests (apps/dashboard/tests/) — integration tests that POST to a real dev server + real Postgres. Start bun run dev in another terminal first, then cd apps/dashboard && bun test tests/.
  • Extension tests — unit tests live next to source under apps/extension/src/ (bun run ext:test); Playwright E2E lives in apps/extension/tests/ and runs against a built extension via bun run --filter @reprojs/extension test:e2e.

The full bun run test runs both, so you need the dev server running.

TDD convention

Write the failing test first, then the implementation. The project uses bun:test everywhere — no jest, no vitest for unit tests.

Project conventions

  • TypeScript strict. No any to silence a type error. as unknown as X only when strictly necessary and justified inline.
  • Conventional Commits: feat:, fix:, chore:, refactor:, docs:, test:. One concern per commit.
  • Contract types single-sourced from @reprojs/shared — the SDK and dashboard UI both import from there rather than redeclaring.
  • No React/Vue/Svelte imports in SDK packages — the widget is framework-agnostic. Only Preact inside a Shadow DOM.
  • Privacy-first replay — masking defaults on; data-repro-mask / data-repro-block attributes on sensitive DOM nodes.

Monorepo layout

reprojs/
├── apps/
│   ├── dashboard/              # Nuxt 4 — admin UI + intake API
│   └── extension/              # Chrome MV3 tester extension (Preact + Vite + CRXJS)
├── packages/
│   ├── core/                   # @reprojs/core — the published SDK
│   ├── ui/                     # widget UI (Preact + Shadow DOM)
│   ├── recorder/               # 30s rolling DOM replay
│   ├── shared/                 # contract types + Zod schemas
│   └── integrations/
│       └── github/             # GitHub App adapter
├── docs/                       # this site (VitePress)
├── .github/workflows/          # CI + Docker image publishing + docs deploy
└── compose.yaml                # self-host entry point

@reprojs/core bundles every packages/* workspace package at build time. The others stay private and exist as build inputs only — they're never published standalone. apps/extension is also private: it ships as a zip on the GitHub releases page and (after review) the Chrome Web Store, not as an npm package.

Releases

Releases are driven by changelogen from Conventional Commits. Dashboard, SDK, and the tester extension release on independent tag prefixes so they can version independently.

bash
bun run release           # dashboard — patch bump
bun run release:minor     # dashboard — minor
bun run release:major     # dashboard — major
bun run release:sdk       # SDK packages (tag prefix: sdk-v)
bun run release:extension # tester extension (tag prefix: extension-v) — builds + zips artifacts
bun run release:all       # SDK + extension together (set BUMP=minor|major to override)
bun run postrelease       # push tags to GitHub

The prerelease hook runs lint, format:check, SDK build, and SDK tests. Pushing a v*.*.* tag triggers publish-docker which builds the multi-arch image and pushes to ripwords/reprojs-dashboard. Pushing an sdk-v*.*.* tag triggers publish-npm which publishes @reprojs/core with provenance. Pushing an extension-v*.*.* tag triggers publish-extension which uploads to the Chrome Web Store and creates a GitHub Release with the repro-tester.zip attached (what testers install unpacked while Web Store review is pending).

Where to file bugs / ideas

github.com/Ripwords/ReproJs/issues. PRs welcome.