🌿 Collaborative wiki on ATProto forked from juprodh.me/lichen.wiki
lichen.wiki CONTRIBUTING.md
3.3 kB · 74 lines
Markdown
at main

Getting started #

Prerequisites: Bun. Everything runs under Bun — no Node, no native addons.

bun install
bun run dev            # appview on :3000 (watch mode)
bun test               # full test suite
bun run lint           # check with Biome
bun run typecheck      # tsgo --noEmit

The database auto-seeds with sample data on first run. To reset, delete lichen.db and restart.

All commands #

bun run dev              # appview server (watch mode)
bun run dev:firehose     # firehose subscriber only
bun run dev:viz          # watch-mode viz bundle
bun run dev:editor       # watch-mode editor bundle
bun test                 # full test suite
bun run lint:fix         # auto-fix lint/format
bun run typecheck        # type check
bun run build:css        # build Tailwind CSS
bun run build:viz        # bundle viz renderers
bun run build:editor     # bundle editor

Project layout #

src/
  server/          # Elysia routes, database (schema, queries/, seed)
  atproto/         # OAuth, PDS write functions
  firehose/        # standalone firehose subscriber
  lib/             # shared logic (diff, markdown, slugs, viz, image, blob, i18n, urls...)
  shared/          # shared type definitions
  views/           # HTML templates
lexicons/          # ATProto lexicon schemas
public/            # static assets (editor, viz, CSS)
tests/             # mirrors src/ structure

Code style #

  • TypeScript, strict mode
  • Biome for formatting (tabs, double quotes), run bun run lint:fix before committing
  • Named exports, no default exports
  • Functions over classes unless state is needed
  • Throw typed error classes (WikiNotFoundError, AccessDeniedError, PdsWriteError), caught by a single onError hook. Route handlers never catch errors themselves.
  • Route handlers stay thin: orchestrators in src/lib/orchestrators/ own the PDS-then-DB lifecycle. Routes parse input, call the orchestrator, redirect.
  • PDS write must succeed before DB write. If PDS fails, the request fails.
  • No ORMs, raw SQL with prepared statements
  • UI strings go through src/lib/i18n/, no hardcoded English in views
  • Tailwind classes come from src/views/theme.ts (THEME.*), no hardcoded class strings in views

Database #

SQLite via bun:sqlite. Schema in src/server/db/schema.ts, queries in src/server/db/queries/. All queries use prepared statements, writes are wrapped in transactions.

ATProto #

Lexicon namespace: wiki.lichen (domain: lichen.wiki). Schemas in lexicons/.

Record types: wiki.lichen.wiki, wiki.lichen.note, wiki.lichen.noteRevision, wiki.lichen.memberRequest, wiki.lichen.membership, wiki.lichen.bookmark

In dev mode (no OAuth configured), PDS writes are skipped and a mock DID is used.

Tests #

Tests mirror src/ under tests/. All test files share one in-memory DB singleton (tests/preload.ts sets DB_PATH=:memory:), so tests that create data must clean up in afterAll.

Tests in tests/integration/ are scenario tests that synthesize firehose commit events and invoke handleCommitEvent directly — no real PDS, no Node subprocess. They cover multi-step flows (revision chaining, multi-user edits, membership lifecycle) on top of the per-event coverage in tests/firehose/handlers.test.ts.