# Contributing to OGraph ## Project Structure ``` packages/ engine/ # CF Worker — API + Engine logic + UI src/ index.ts # Hono routes + middleware engine.ts # Core engine functions (CRUD, projections, reactions) types.ts # TypeScript interfaces ui.html # Built UI (committed, auto-generated) ui/ # React SPA (Vite + Tailwind + headless-ui) src/ App.tsx api.ts components/ wrangler.toml cli/ # CLI client (@uncaged/ograph-cli) src/ client.ts # HTTP client for OGraph API commands/ # CLI commands (events, projections, reactions, etc.) dispatcher/ # Local daemon — polls projections, notifies Agent board/ # (experimental) Kanban board UI ``` ## Prerequisites - Node.js 22+ - npm (workspaces) - Cloudflare account (for deployment) - `wrangler` CLI (installed via npm) ## Setup ```bash git clone https://github.com/oc-xiaoju/ograph.git cd ograph npm install ``` UI has its own dependencies: ```bash cd packages/engine/ui npm install ``` ## Development Workflow ### Engine (API + Core Logic) ```bash cd packages/engine npm run dev # Local dev server (wrangler dev) npm test # Run tests (vitest, 105+ tests) ``` ### UI (React SPA) ```bash cd packages/engine/ui npm run dev # Vite dev server (hot reload) npm run build # Build → dist/index.html (single-file) ``` After building UI, copy to engine: ```bash cp packages/engine/ui/dist/index.html packages/engine/src/ui.html ``` ### CLI ```bash cd packages/cli npm run build # TypeScript compile npm test # Run tests (vitest, 31+ tests) npm run dev # Watch mode ``` ### Run All Tests ```bash npm test # Runs tests across all workspaces ``` ## CI/CD **Pipeline:** `.github/workflows/ci.yml` On every push to `main`: 1. **Test** — `npm install` → `npm test` (all workspaces) 2. **Deploy** — Build UI → copy `ui.html` → `wrangler deploy` to Cloudflare Workers On pull requests: 1. **Test** only (no deploy) ### Secrets (GitHub Actions) | Secret | Description | |--------|-------------| | `CLOUDFLARE_API_TOKEN` | Wrangler deploy auth | | `CLOUDFLARE_ACCOUNT_ID` | CF account | ### Production URL - API: `https://ograph.shazhou.workers.dev` - UI: `https://ograph.shazhou.workers.dev/ui` ## Commit Conventions Format: `type(scope): description` Types: `feat`, `fix`, `refactor`, `ci`, `docs`, `test`, `chore` Scopes: `engine`, `cli`, `ui`, `dispatcher`, `board` Examples: ``` feat(engine): add incremental event query with ?after=N fix(ui): Projection ref params now filter objects by matching type ci: add CD — auto deploy to Cloudflare Workers on push to main ``` Sign commits: ``` 小橘 🍊(NEKO Team) 小墨 🖊️(KUMA Team) ``` Git config: ```bash git config user.name "小橘" git config user.email "xiaoju@shazhou.work" ``` ## Architecture Notes ### Engine (Cloudflare Worker) - **Runtime:** Cloudflare Workers (D1 database, Hono framework) - **Auth:** API Key in `Authorization: Bearer ogk_xxx` header - **UI:** Single HTML file served at `/ui`, built with Vite singlefile plugin - **Logs:** Request logs auto-cleaned (7-day retention via `waitUntil`) ### Key Design Decisions - **Snake_case everywhere** — API, DB, event names (`task_created`, not `taskCreated`) - **Event names:** `{object}_{past_participle}` (e.g. `task_created`, `comment_added`) - **Params use `_id` suffix** — `task_id`, `agent_id` - **Content-addressed hashing** — defs are immutable versions identified by hash - **Projection health:** expression errors → `errored` status, returns stale value, no reaction triggered - **Lazy computation:** projections compute on query, not on event emit ### UI Routing Hash-based routing (`#/events`, `#/reaction-logs`). No react-router dependency — hand-rolled. Pages: | Route | Page | |-------|------| | `#/health` | Health dashboard | | `#/object-defs` | Object type definitions | | `#/objects` | Object instances | | `#/event-defs` | Event type definitions | | `#/events` | Event log | | `#/projection-defs` | Projection definitions | | `#/projections` | Query projections | | `#/reactions` | Reaction definitions | | `#/reaction-logs` | Reaction execution logs | | `#/request-logs` | API request logs | | `#/api-keys` | API key management | ### D1 Storage - Free tier: 500 MB storage, 5M reads/writes per month - TEXT fields: no length limit (SQLite underneath, ~1 MB per row) - Logs: auto-pruned after 7 days - Events: append-only, never deleted ### Testing Engine tests use miniflare (local D1). Tests cover: - CRUD for all entity types (object defs, event defs, projection defs, etc.) - Projection computation + incremental reduce - Reaction execution + handler sandboxing - API key auth + permissions - Error handling + edge cases To add a test, edit `packages/engine/src/index.test.ts`. ## Manual Deploy (if needed) ```bash cd packages/engine # Build UI first cd ui && npm run build && cp dist/index.html ../src/ui.html && cd .. # Deploy npx wrangler deploy ``` Requires `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` env vars. ## D1 Database Access ```bash # Query production D1 npx wrangler d1 execute ograph --remote --command "SELECT COUNT(*) FROM events" # Time Travel (point-in-time recovery) npx wrangler d1 time-travel ograph --before ``` --- *Built by 小橘 🍊 & 小墨 🖊️ — NEKO + KUMA Teams*