Date: February 18, 2026
Auditor: Automated code audit
Codebase snapshot: main branch at time of audit
Total source files: 33 TypeScript/TSX files across 3 packages
Total lines of code: ~2,354 (excluding config, tests, and generated files)
CollabBoard is a real-time collaborative whiteboard built with React/Fabric.js on the frontend and Hocuspocus/Yjs on the backend. The codebase is well-structured as a pnpm monorepo with shared TypeScript types, strict linting, and proper separation of concerns between hooks and components.
MVP Verdict: CONDITIONAL PASS
Seven of the nine MVP requirements are cleanly met. Two items have issues that need attention before the MVP can be considered fully passing:
- Multiplayer cursors have a coordinate-space bug that causes remote cursors to display at incorrect positions after any pan or zoom operation.
- Object editing is incomplete because individual object deletion is not exposed in the UI despite the underlying function existing in the
useBoardhook. - Deployment status cannot be verified from the repository alone.
The architecture is sound and follows the Pre-Search document closely. The Yjs CRDT sync layer, Firebase authentication, and Fabric.js canvas rendering are all wired together correctly. The primary risks are in edge-case bugs (cursor coordinates, sync loop timing), missing hardening (no error boundaries, no structured logging, no graceful shutdown), and a documentation mismatch between the planned Fly.io deployment and the actual Render.com configuration.
Each requirement is assessed against the CLAUDE.md MVP gate criteria.
Implementation: packages/client/src/components/Board/Canvas.tsx lines 150-198
- Pan via Alt+drag or middle mouse button using
viewportTransformmatrix manipulation - Zoom via scroll wheel with
zoomToPoint(), clamped to 0.1x-5x range - Window resize handler updates canvas dimensions
- Scene-center calculation accounts for viewport transform and zoom level
Assessment: Fully functional. Zoom centers on cursor position correctly. Pan and zoom compose well together.
Implementation:
- Creation:
packages/client/src/hooks/useBoard.tscreateStickyNote()(lines 33-55) - Rendering:
packages/client/src/components/Board/Canvas.tsxcreateStickyGroup()(lines 56-100) - Editing:
Canvas.tsxdouble-click handler (lines 218-252) + textarea overlay (lines 452-482)
Assessment: Sticky notes are created as Fabric.js Groups (Rect background + Textbox). Double-click opens an HTML textarea overlay positioned at screen coordinates. Text saves on blur or Escape. Color is configurable from the toolbar palette. Fixed size (200x200), no rotation/resize on stickies.
Implementation:
- Creation:
packages/client/src/hooks/useBoard.tscreateRectangle()(lines 57-79) - Rendering:
Canvas.tsxrectangle sync (lines 336-378)
Assessment: Rectangles support creation, movement, resizing (with scale normalization), custom fill/stroke colors. Rotation is disabled. Meets the minimum requirement.
Implementation:
- Create: Toolbar buttons create objects at scene center
- Move: Fabric.js built-in drag via
object:modifiedevent synced to Yjs (lines 255-291) - Edit: Text editing via double-click overlay; color changes via toolbar palette
- Delete:
useBoard.deleteObject()exists (lines 96-102) but is never wired to any UI element
Gap: There is no way for a user to delete an individual object. No Delete/Backspace key handler, no right-click context menu, no delete button in the toolbar. The only destructive action available is "Clear All." The deleteObject function works correctly at the data layer but is inaccessible to users.
Recommendation: Wire Delete/Backspace key to deleteObject() for the currently selected Fabric object. This is a small change (~15 lines) that closes the MVP gap.
Implementation:
- Yjs provider:
packages/client/src/lib/yjs.tscreatesHocuspocusProviderwith Firebase token auth - Board state:
Y.Map('objects')keyed by UUID, observed inCanvas.tsx(lines 308-419) - Server:
packages/server/src/index.tsconfigures Hocuspocus with SQLite persistence via@hocuspocus/extension-database - Loop prevention:
isRemoteUpdateRefandisLocalUpdateRefflags prevent Yjs observer / Fabric event infinite loops
Assessment: The sync architecture is correct. Objects created, moved, or edited by one user propagate to all connected clients via Yjs CRDT updates. The object:modified handler updates Yjs on local changes; the objectsMap.observe() handler updates Fabric on remote changes. Tested with real Y.Doc instances in useBoard.test.ts.
Implementation:
- Broadcasting:
packages/client/src/hooks/useCursors.tsuses Yjs awareness protocol with 30ms throttle - Rendering:
packages/client/src/components/Cursors/CursorOverlay.tsx
Bug: CursorOverlay positions remote cursors using raw scene coordinates as CSS left/top pixel values. However, cursor positions are broadcast in scene-space (via canvas.getScenePoint() in Canvas.tsx line 167). When any user pans or zooms, the overlay does not apply the local viewport transform to convert scene coordinates to screen coordinates.
Impact: After any pan or zoom operation, all remote cursors appear at incorrect screen positions. The further the local user pans/zooms from the origin, the more severe the offset. At 2x zoom, cursors are displaced by double their actual offset.
Fix: The CursorOverlay component (or a wrapper) needs access to the current viewportTransform matrix and zoom level. Each remote cursor's scene coordinates must be transformed: screenX = sceneX * zoom + vpt[4], screenY = sceneY * zoom + vpt[5].
Implementation:
- Hook:
packages/client/src/hooks/usePresence.tsreads all Yjs awareness states - UI:
packages/client/src/components/Presence/PresencePanel.tsxrenders avatar + name list
Assessment: Shows online user count, color-coded avatars (photo or initial), and display names. Scrollable list supports multiple users. Presence state includes userId, displayName, photoURL, and color (deterministic hash of userId).
Implementation:
- Client:
packages/client/src/lib/firebase.ts(Google OAuth + email/password via Firebase Auth) - Auth state:
packages/client/src/hooks/useAuth.tswith React Context - Route guard:
packages/client/src/components/Auth/AuthGuard.tsxredirects to/login - Server WebSocket:
packages/server/src/hocuspocus/onAuthenticate.tsverifies Firebase JWT via Admin SDK - Server HTTP:
packages/server/src/middleware/auth.tsverifies Bearer token for API routes
Assessment: Authentication is implemented end-to-end. Both WebSocket (Hocuspocus) and HTTP (Express) connections require valid Firebase JWTs. Token refresh is handled by getIdToken() which waits for auth state on page refresh.
Implementation:
- Frontend:
packages/client/vercel.jsonconfigures Vercel deployment with SPA rewrites - Backend:
render.yamlconfigures Render.com Docker deployment with persistent disk and health check - Docker:
packages/server/Dockerfilemulti-stage build targeting Node 20
Assessment: Deployment configuration exists for both frontend (Vercel) and backend (Render.com), but there is no evidence in the repository (deployment URLs, CI logs, environment configs) that the application has been successfully deployed and tested with two browser windows on a public URL. This must be verified externally.
| ID | Bug | Location | Impact |
|---|---|---|---|
| BUG-001 | Remote cursors ignore viewport transform (pan/zoom) | CursorOverlay.tsx |
Remote cursor positions are wrong after any pan/zoom; core multiplayer feature broken |
| BUG-002 | No individual object deletion in UI | Toolbar.tsx, Canvas.tsx |
Users cannot delete specific objects; only "Clear All" available; MVP gap |
| ID | Bug | Location | Impact |
|---|---|---|---|
| BUG-003 | LoginPage calls navigate() during render |
LoginPage.tsx:14-16 |
Violates React rules; may cause "Cannot update during render" warning; could cause redirect loops |
| BUG-004 | isLocalUpdateRef race condition |
Canvas.tsx:264-289 |
If Yjs observer fires after flag is cleared (e.g., batched transactions), a feedback loop between Fabric and Yjs could occur |
| BUG-005 | MAX_OBJECTS_PER_BOARD (500) not enforced |
useBoard.ts, constants.ts |
Unlimited objects can be created, potentially degrading performance below 60fps target |
| ID | Bug | Location | Impact |
|---|---|---|---|
| BUG-006 | Keyboard shortcuts advertised but not implemented | Toolbar.tsx tooltips "(V)", "(S)", "(R)" |
Misleading UX; tooltips promise functionality that does not exist |
| BUG-007 | useRequireAuth is a no-op wrapper |
useAuth.ts:26-29 |
Dead code; returns identical result to useAuthState(); unused in the codebase |
| BUG-008 | lib/fabric.ts referenced in CLAUDE.md but does not exist |
packages/client/src/lib/ |
Documentation/plan divergence; no functional impact since Canvas.tsx handles setup directly |
The implementation closely follows the Pre-Search document and CLAUDE.md with a few notable deviations:
| Planned | Actual | Assessment |
|---|---|---|
| Fly.io backend hosting | Render.com (render.yaml) |
Significant deviation; Render starter plan has 15-min cold starts |
lib/fabric.ts helper module |
Canvas setup inline in Canvas.tsx |
Minor; all logic works but is less modular |
router.tsx separate file |
Routes inline in App.tsx |
Acceptable simplification |
onChange Hocuspocus hook |
Not implemented | No server-side validation of object changes |
| CORS middleware module | Inline in index.ts |
Acceptable for 2-endpoint surface |
Monorepo structure: Clean three-package layout (client, server, shared) with pnpm workspaces. The @collabboard/shared package provides a single source of truth for board object types and constants.
Type system: Discriminated union BoardObject type with exhaustive type guards (isStickyNote, isRectangleShape, etc.). BaseBoardObject interface enforces consistent metadata fields. TypeScript strict mode with noUncheckedIndexedAccess catches undefined access on Yjs map reads.
Sync architecture: The Yjs/Fabric binding follows the correct pattern: local Fabric events update Yjs, Yjs observers update Fabric, and boolean ref flags prevent infinite loops. The object:modified handler normalizes Fabric scale back to 1 after saving actual dimensions, which prevents compounding scale drift.
Separation of concerns: Hooks (useYjs, useBoard, useCursors, usePresence) encapsulate all data logic. Components are purely presentational. BoardPage composes hooks and passes callbacks between components.
Sticky note recreation: Every remote update to a sticky note destroys the existing Fabric Group and creates a new one (Canvas.tsx:324-335). With many stickies and frequent updates (e.g., rapid text editing by a remote user), this triggers full Group layout passes repeatedly. An in-place update strategy (updating sub-object properties) would be more performant but requires careful handling of Fabric.js Group layout internals.
Monolithic Canvas component: Canvas.tsx at 485 lines handles initialization, pan/zoom, object creation, object sync, text editing overlay, and selection tracking all in one component. This makes it the hardest file to maintain and test.
No error boundaries: A runtime error in any component (Canvas, Toolbar, CursorOverlay) will crash the entire React tree with a white screen. No ErrorBoundary wrapper exists.
Inline styles everywhere: All styling is done via React.CSSProperties objects. No CSS modules, Tailwind, or styled-components. This works but makes responsive design, hover states, and theme consistency harder to maintain.
strict: truewithnoUncheckedIndexedAccess: true-- excellent- ESLint
@typescript-eslint/strict-type-checked-- highest strictness level @typescript-eslint/no-explicit-any: "error"-- enforced@typescript-eslint/consistent-type-imports: "error"-- enforced- Prettier configured with single quotes, trailing commas, 100 char width
ESLint suppressions: Two eslint-disable-next-line react-hooks/exhaustive-deps in Canvas.tsx (lines 304 and 418). Both are for effects that intentionally run only on mount or when objectsMap changes. These are acceptable patterns for imperative Fabric.js integration but should carry documentation explaining the rationale.
Test files found:
packages/client/src/hooks/useBoard.test.ts(289 lines, 14 test cases)packages/server/src/hocuspocus/database.test.ts(94 lines, 6 test cases)
Coverage assessment:
- Board CRUD operations: well tested with real Y.Doc instances
- Yjs sync between two docs: tested (create, update, delete sync)
- SQLite persistence: tested (load, store, overwrite, empty, large data)
- Authentication hooks/middleware: not tested
- Canvas rendering/sync: not tested (difficult without DOM/canvas mocking)
- Cursor/presence hooks: not tested
- AI handler: not tested (stub implementation)
Testing approach: Follows the CLAUDE.md guidance of "never mock Yjs" correctly. Both test files use real in-memory instances (Y.Doc, BetterSqlite3 :memory:). Test framework is Vitest.
Gap: The Pre-Search document targets "95%+ on the critical path" but actual coverage is limited to two files. The Yjs-to-Fabric binding functions in Canvas.tsx (identified as "most complex, most bug-prone") have zero test coverage.
No JSDoc, Docstring, or structured documentation exists on any function, hook, component, or module. Interface types are self-documenting to a degree, but complex functions like syncObjectToCanvas, createStickyGroup, and the Canvas initialization effect have no documentation explaining their behavior, parameters, or side effects.
- Naming conventions followed: PascalCase components/types, camelCase hooks/utilities
UPPER_SNAKE_CASEfor constants inshared/constants.ts- Event handlers use
handleprefix internally - File naming: PascalCase for components, camelCase for hooks/lib (consistent with CLAUDE.md)
- No
anytypes found in the codebase
- WebSocket: Hocuspocus
onAuthenticateverifies Firebase JWT viafirebase-adminSDK. Connections without valid tokens are rejected. User metadata (uid, displayName, photoURL, email) attached to connection context. - HTTP: Express
authMiddlewareextracts Bearer token and verifies via Firebase Admin SDK. AttachesuserIdanddisplayNameto request. - Client: Firebase Auth SDK handles token lifecycle, refresh, and persistence.
- No board-level authorization: Any authenticated user can access any board by navigating to
/board/<boardId>. Board IDs are URL path segments (the default is literally"default"). While the Pre-Search document describes this as intentional ("anyone with the link can edit"), there is no mechanism to prevent enumeration of board IDs. - No Hocuspocus
onConnectoronChangevalidation: The server does not validate that a user should have access to a specific board document. All authenticated users have full read/write access to all boards.
- AI endpoint: Checks for
commandandboardIdexistence only. No type validation, length limits, or sanitization. - Board objects: Data written to
Y.Map('objects')is not validated against theBoardObjectschema. A malicious client could inject arbitrary JSON into the map, potentially crashing other clients' canvas rendering. - Express body size:
express.json()is called without alimitoption. Default is 100KB which is reasonable, but should be explicitly set.
.env.exampledocuments required variables without values.gitignoreexcludes.envfilesrender.yamlmarks secrets withsync: false- Firebase service account loaded from environment variable (JSON string or file path)
ANTHROPIC_API_KEYis server-side only
No security headers middleware (Helmet or equivalent). Missing headers:
Strict-Transport-Security(HSTS)Content-Security-Policy(CSP)X-Content-Type-OptionsX-Frame-OptionsX-XSS-Protection(legacy but harmless)
Canvas rendering is inherently XSS-safe (draws pixels, not DOM), but the login page and any future HTML-rendered user content would benefit from CSP.
- AI endpoint: 10 requests/minute/IP via
express-rate-limit-- good - WebSocket connections: no rate limiting -- a single IP could open unlimited connections
- Board object creation: no rate limiting -- could flood the Yjs document
| Area | Status |
|---|---|
| Firebase auth errors | Caught and displayed to user in LoginPage |
| WebSocket disconnect | Connection banner shown; Hocuspocus auto-reconnects |
| Yjs sync errors | No explicit handling; relies on Hocuspocus provider defaults |
| Canvas rendering errors | No error boundary; crash = white screen |
| Server startup errors | Will crash process; no retry logic |
| SQLite errors | No try/catch around DB operations in database.ts |
| AI endpoint errors | Stub returns placeholder; no real error handling yet |
All logging uses console.log with tag prefixes:
[SERVER]inindex.ts[DB]indatabase.ts[AUTH]infirebaseAdmin.ts[YJS]in clientyjs.tsanduseYjs.ts
No structured logging library, no log levels, no request correlation IDs, no production log aggregation. The Pre-Search document recommends structured tagged logging with [HOCUS] and [AI] prefixes, which is partially followed.
/api/health returns { status: 'ok' } unconditionally. It does not verify:
- SQLite database connectivity
- Hocuspocus server state
- Memory/disk usage
For Render.com health checks, this means the service will appear healthy even if the database is corrupt or Hocuspocus has crashed internally.
No SIGTERM or SIGINT handler exists in index.ts. When Render.com sends SIGTERM during deploys or scaling:
- In-flight WebSocket connections are terminated abruptly
- SQLite writes may be interrupted (WAL mode provides some protection)
- Yjs documents in memory are lost without a final persistence flush
- Hocuspocus provider handles WebSocket reconnection automatically
- UI shows "Reconnecting..." banner when disconnected
- Yjs buffers local changes during disconnect and syncs on reconnect
- No exponential backoff or user-facing retry button
The CLAUDE.md file and Pre-Search document consistently reference Fly.io as the backend hosting platform. The actual deployment configuration uses Render.com (render.yaml). No fly.toml exists in the repository.
This is a significant documentation debt. Any developer (or evaluator) following CLAUDE.md instructions for deployment will encounter mismatched guidance.
Dockerfile (packages/server/Dockerfile):
- Multi-stage build: builder stage compiles TypeScript, production stage copies built artifacts
- Base image:
node:20-slim - pnpm installed globally in both stages
/datadirectory created for SQLite persistent volume
Issues:
- Runs as root (no
USER nodedirective). Running containers as root is a security risk. - No
HEALTHCHECKinstruction. Container orchestrators cannot distinguish a healthy container from an unhealthy one without this. pnpm install --frozen-lockfile --prodin production stage installs all workspace prod deps, including client deps that are not needed on the server.
render.yaml services:
collabboard-server: Docker web service with/api/healthhealth check, 1GB persistent disk at/datacollabboard-client: Static site built with pnpm, published frompackages/client/dist
Issues:
starterplan spins down after 15 minutes of inactivity. WebSocket connections require an always-on server. Users connecting to a cold-started instance will experience a 30-60 second delay before the WebSocket server is ready.ANTHROPIC_API_KEYis not listed inrender.yamlenvironment variables (needed when AI handler is implemented).- No
PORTenvironment variable listed (Render injects it automatically for Docker services, but explicit documentation would help).
vercel.json (packages/client/vercel.json):
- Build command chains: install -> build shared -> build client
- SPA rewrite rule (
/*->/index.html) - Framework: Vite
Assessment: Standard Vercel SPA deployment. Should work correctly.
No CI/CD pipeline exists. No GitHub Actions, no Render.com auto-deploy hooks visible in the repo. The Pre-Search document notes "manual deploy" for the backend and "auto-deploy on push" for Vercel, which is likely configured outside the repository.
Prioritized by impact and effort. Items marked (MVP-blocking) should be done before declaring MVP complete.
| Item | Effort | Description |
|---|---|---|
| Fix cursor coordinate transform | ~1 hour | Pass viewportTransform and zoom from Canvas to CursorOverlay; transform scene coords to screen coords: screenX = sceneX * zoom + vpt[4]. (MVP-blocking, BUG-001) |
| Add individual object deletion | ~30 min | Add Delete/Backspace keydown listener on Canvas; call board.deleteObject(id) for the selected object. (MVP-blocking, BUG-002) |
| Fix LoginPage render-time navigation | ~10 min | Replace the if (user) { navigate(...) } block with a useEffect or <Navigate> component. (BUG-003) |
| Item | Effort | Description |
|---|---|---|
| Add React error boundary | ~30 min | Wrap BoardPage in an ErrorBoundary component that catches canvas/rendering errors and shows a recovery UI instead of a white screen. |
| Add graceful shutdown handler | ~30 min | Listen for SIGTERM/SIGINT in index.ts; flush Hocuspocus documents to SQLite; close HTTP server; close DB connection. |
Enforce MAX_OBJECTS_PER_BOARD |
~20 min | Check objectsMap.size before creating new objects in useBoard; show user feedback when limit reached. |
| Add Express body size limit | ~5 min | Pass { limit: '100kb' } to express.json(). |
| Deep health check | ~30 min | Make /api/health verify SQLite read and Hocuspocus document count. Return 503 if unhealthy. |
| Item | Effort | Description |
|---|---|---|
| Add security headers | ~15 min | Install helmet and add as Express middleware. Covers HSTS, CSP, X-Content-Type-Options, etc. |
| Add Yjs data validation | ~1 hour | Validate board objects against the BoardObject schema when received from Yjs observers. Reject/ignore malformed data. |
| Add non-root Docker user | ~10 min | Add RUN addgroup --system app && adduser --system --ingroup app app and USER app to Dockerfile. |
| Board-level access control | ~2 hours | Optional: add board creator tracking and invite-based access. Low priority for demo/evaluation use case. |
| Item | Effort | Description |
|---|---|---|
| Add JSDoc to all hooks and key functions | ~2 hours | Document parameters, return values, side effects, and usage for useBoard, useYjs, useCursors, syncObjectToCanvas, createStickyGroup, etc. |
| Implement keyboard shortcuts | ~1 hour | Add keydown listener for V (select), S (sticky), R (rectangle), Delete/Backspace (delete selected). Remove misleading tooltips or make them functional. |
| Extract Canvas sub-modules | ~2 hours | Split Canvas.tsx (485 lines) into: pan/zoom handler, object sync observer, text editing overlay, and selection manager. |
| Add error boundary tests | ~1 hour | Test that rendering errors in child components are caught and displayed. |
| Remove dead code | ~10 min | Remove useRequireAuth (unused, no-op wrapper). |
| Item | Effort | Description |
|---|---|---|
| Update CLAUDE.md for Render.com | ~30 min | Replace all Fly.io references with Render.com; document actual deployment commands and configuration. |
| Add Docker HEALTHCHECK | ~5 min | Add `HEALTHCHECK CMD wget -q --spider http://localhost:$PORT/api/health |
| Upgrade Render plan for always-on | ~5 min | Switch from starter to a paid plan that does not spin down, or implement a keep-alive ping. Cold starts break WebSocket-dependent applications. |
Add ANTHROPIC_API_KEY to render.yaml |
~5 min | Add the env var entry (with sync: false) for when AI handler is implemented. |
| Add CI pipeline | ~2 hours | GitHub Actions workflow: install -> typecheck -> lint -> test -> build. Block merges on failure. |
| Item | Effort | Description |
|---|---|---|
| In-place sticky note updates | ~2 hours | Instead of destroying/recreating the Fabric Group on every remote update, update sub-object properties in place. Requires careful handling of Fabric.js Group layout. |
| Add structured logging | ~1 hour | Replace console.log with a lightweight logger (e.g., pino) with log levels, timestamps, and structured JSON output. |
| Add WebSocket connection limits | ~30 min | Limit concurrent connections per IP or per user to prevent resource exhaustion. |
| File | Lines | Purpose |
|---|---|---|
main.tsx |
12 | React entry point, mounts App to DOM |
App.tsx |
26 | React Router setup with auth-guarded routes |
vite-env.d.ts |
1 | Vite type declarations |
components/Auth/AuthProvider.tsx |
12 | React Context provider for auth state |
components/Auth/AuthGuard.tsx |
25 | Route guard; redirects unauthenticated users to /login |
components/Auth/LoginPage.tsx |
147 | Login UI with Google OAuth and email/password forms |
components/Board/BoardPage.tsx |
83 | Main board container; composes all hooks and components |
components/Board/Canvas.tsx |
485 | Fabric.js canvas: init, pan/zoom, object sync, text editing |
components/Cursors/CursorOverlay.tsx |
68 | SVG cursor rendering overlay for remote users |
components/Presence/PresencePanel.tsx |
95 | Online users panel with avatars and names |
components/Toolbar/Toolbar.tsx |
170 | Tool selection, color palette, clear board button |
hooks/useAuth.ts |
29 | Firebase auth state hook and AuthContext |
hooks/useYjs.ts |
43 | Yjs document and Hocuspocus provider lifecycle |
hooks/useBoard.ts |
132 | Board CRUD operations against Yjs Y.Map |
hooks/useCursors.ts |
87 | Cursor position broadcasting/receiving via awareness |
hooks/usePresence.ts |
35 | Online users list from awareness state |
hooks/useBoard.test.ts |
289 | Unit tests for board CRUD and Yjs sync |
lib/firebase.ts |
61 | Firebase app/auth initialization and auth methods |
lib/yjs.ts |
41 | Yjs provider factory with token auth |
| File | Lines | Purpose |
|---|---|---|
index.ts |
65 | Entry point: HTTP server + WebSocket upgrade + Hocuspocus |
hocuspocus/onAuthenticate.ts |
26 | Firebase JWT verification for WebSocket connections |
hocuspocus/firebaseAdmin.ts |
32 | Firebase Admin SDK singleton initialization |
hocuspocus/database.ts |
39 | SQLite document persistence (load/store) |
hocuspocus/database.test.ts |
94 | SQLite persistence unit tests |
ai/handler.ts |
26 | AI command handler (stub, returns placeholder) |
middleware/auth.ts |
30 | Express Bearer token verification middleware |
middleware/rateLimit.ts |
12 | Express rate limiter factory (10 req/min/IP) |
| File | Lines | Purpose |
|---|---|---|
index.ts |
2 | Re-exports types and constants |
types.ts |
115 | BoardObject discriminated union, type guards, cursor/presence types |
constants.ts |
48 | Colors, dimensions, limits, rate-limit config |
| File | Purpose |
|---|---|
package.json (root) |
Workspace scripts, shared devDependencies |
pnpm-workspace.yaml |
Workspace package paths |
tsconfig.base.json |
Base TypeScript config (strict, ES2022) |
.eslintrc.json |
ESLint strict-type-checked + Prettier |
.prettierrc |
Code formatting rules |
render.yaml |
Render.com deployment (server + client) |
packages/client/vercel.json |
Vercel frontend deployment |
packages/server/Dockerfile |
Multi-stage Docker build |
.env.example |
Environment variable documentation |
End of audit report.