clickclack/docs/development.md
2026-05-08 08:53:47 +01:00

4.4 KiB

read_when
setting up a fresh checkout
changing the gate (lint/typecheck/test/build/coverage)
adding a new package or tool to the monorepo

Development

The repo is a Go module plus a pnpm workspace. The Go binary embeds the built SPA, so a full local build runs both toolchains.

Prerequisites

  • Go (matching go.mod).
  • pnpm 11 (auto-managed via corepack).
  • TypeScript runs via tsgo from @typescript/native-preview — installed through pnpm.
  • Lint/format use oxlint and oxfmt — installed through pnpm.

First run

pnpm install
pnpm build                                          # builds SPA + SDK and copies dist into apps/api
go run ./apps/api/cmd/clickclack serve
open http://localhost:8080

The dev fallback creates Local Captain as the first user, a ClickClack workspace, and a general channel, so the SPA loads into a working state on first hit.

Two-process dev loop

# terminal 1
pnpm dev:api                                        # go run ./apps/api/cmd/clickclack serve

# terminal 2
pnpm dev:web                                        # vite dev server with API proxy

The Vite dev server proxies /api and /api/realtime/ws to localhost:8080.

Scripts

Command What it does
pnpm build Builds the Svelte app and the SDK, then copies apps/web/dist into apps/api/internal/webassets/dist.
pnpm check Full local gate: go test ./..., root/workspace tsgo, oxlint, and format checks.
pnpm coverage Go tests with coverage; fails under 90% line coverage.
pnpm dev:api go run ./apps/api/cmd/clickclack serve.
pnpm dev:web vite dev for the SPA.
pnpm fmt gofmt + oxfmt over Go and TS/Svelte.
pnpm fmt:check CI-compatible formatting check with gofmt -l and oxfmt --check.
pnpm lint oxlint over web, SDK, examples, and tests.
goreleaser release --snapshot --clean Local release smoke test for all configured OS/arch targets.
pnpm typecheck tsgo --noEmit -p tsconfig.json for root Playwright config/tests.
pnpm test go test ./... && pnpm build.
pnpm test:e2e Playwright suite in tests/e2e.

Layout

apps/
  api/                  # Go backend, single-binary entrypoint
    cmd/clickclack/     # CLI main
    internal/
      auth/             # placeholder
      config/           # flag/env/file resolution
      httpapi/          # chi router, handlers, auth resolution
      realtime/         # in-process pub/sub hub
      store/            # store interface + types
        sqlite/         # SQLite implementation, migrations, backup, export
      webassets/        # go:embed for the built SPA
  web/                  # Svelte 5 SPA
packages/
  protocol/             # OpenAPI spec, source of truth for the wire shape
  sdk-ts/               # TypeScript SDK (generated types + friendly wrapper)
examples/
  bot-ts/               # SDK usage example
infra/
  migrations/sqlite     # mirror of embedded migrations for tooling
  migrations/postgres   # placeholder for future Postgres support
tests/
  e2e/                  # Playwright tests
docs/                   # this directory

Adding a feature

  1. Update packages/protocol/openapi.yaml first when the wire shape changes. It is the contract.
  2. Add the store method on apps/api/internal/store/types.go and implement it in apps/api/internal/store/sqlite.
  3. Wire the handler in apps/api/internal/httpapi.
  4. Update the SDK in packages/sdk-ts/src/index.ts so TS clients have a typed surface.
  5. Update or add a docs/features/<thing>.md.
  6. Run pnpm check and pnpm coverage.

Testing

  • apps/api/internal/... is the bulk of the test suite. Coverage gate is 90%.
  • tests/e2e/chat.spec.ts exercises the SPA end-to-end via Playwright.
  • The SDK has no test target yet — the bot example is the smoke test.

Coding rules

  • IDs are sortable ULID-style with semantic prefixes (usr_, wsp_, chn_, msg_, evt_, upl_, idn_).
  • Keep transactions short. Outbox events are inserted in the same tx as the durable write that produced them.
  • Avoid Postgres-only SQL. Postgres support is planned to live behind the store interface, not by leaking dialect-specific SQL into handlers.
  • TypeScript: no Svelte imports in packages/sdk-ts. The SDK must stay framework-neutral.