mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-28 14:55:45 +00:00
5.6 KiB
5.6 KiB
LifeForge – AI contributor guide
This monorepo uses Bun workspaces with a type-safe API pattern shared between server and client. Follow these rules to be productive and avoid breaking conventions.
Big picture
- Monorepo packages:
server/(Express API),client/(React + Vite),shared/(typed API client + hooks/providers),packages/lifeforge-ui/(component library), plusapps/*(standalone playgrounds/tools). - Data layer: PocketBase (see
database/pb_migrations/and runtime DB indatabase/pocketbase/pb_data/). Server accesses PB through a typedPBServicewrapper and enforces auth in middleware. - API architecture: server routes are declared via a fluent, type-safe DSL built on Zod and Express:
forgebuilds endpoints with.input({ body?, query? }),.existenceCheck(),.media()(for uploads),.isDownloadable(),.callback(...).forgeRouter({...})composes nested routers. The main router inserver/src/core/routes/routes.tslazy-imports “apps” and mounts them.- Client imports the server’s route tree type (
AppRoutes) to generate a fully typed client (shared→createForgeProxy). This ensures client endpoints mirror server definitions.
- Realtime: Socket.IO is initialized in
server/src/index.ts. The task pool middleware (taskPoolMiddleware.ts) and helpers (addToTaskPool,updateTaskInPool) emittaskPoolUpdateevents; the client subscribes viaSocketProviderand authenticates with the PocketBase session cookie.
Dev and build workflows (Bun)
- Install once at repo root:
bun install. - Orchestrated scripts (see
scripts/forge.ts):- Run dev for multiple projects concurrently, e.g.:
bun run forge dev client server shared ui - Typecheck:
bun run forge types server client shared ui - Lint/format where available:
bun run forge lint server
- Run dev for multiple projects concurrently, e.g.:
- Individual packages:
- Server:
bun run dev(env fromserver/env/.env.local),bun run build,bun run start - Client:
bun run dev,bun run build(buildssharedandlifeforge-uifirst),bun run preview - Shared:
bun run build - UI:
bun run dev(tsup watch) orbun run build
- Server:
- Tests: server uses Jest (run
bun run testinserver/).
Server conventions (Express + PocketBase)
- Define endpoints with
forgein app routers. Always specify Zod schemas via.input({ body, query })—handlers receive fully typedbody,query,pb,io, andmedia(if used). - Use
.existenceCheck('body'|'query', { field: 'collection' | '[collection]' })to assert PocketBase IDs exist before running logic. Brackets mark optional fields; arrays are supported. - For uploads, declare
.media({ field: { optional?: boolean } }). Upload middlewares are auto-attached;.callbackreceivesmediawith files. - Set
.isDownloadable()for file responses (adds headers) and.noDefaultResponse()when manually writing responses; otherwise responses are wrapped bysuccessWithBaseResponsewithstatusCode(). - Central router:
server/src/core/routes/routes.tsexposes common utilities:mediaproxies PocketBase file URLs;corsAnywherefetches external URLs to bypass CORS;statusand_listRoutesaid health/discovery.
- PocketBase access/auth:
pocketbaseMiddleware.tsenforces auth on non-whitelisted routes and injectsreq.pb = new PBService(...).- Socket.IO handshake validates
auth.tokenagainst PB (PB_HOST).
- Keep environment in
server/env/.env.local(required:PORT,PB_HOST,PB_EMAIL,PB_PASSWORD).
Client conventions (React + Vite + TanStack Query)
- API client:
client/src/core/utils/forgeAPI.tsxcreatesforgeAPIwithcreateForgeProxy<typeof routes>(import.meta.env.VITE_API_HOST). Use it for all XHR:- GET:
forgeAPI.module.endpoint.input({ ... }).query()or.queryOptions()for React Query. - POST:
forgeAPI.module.endpoint.mutate(data)or.mutationOptions().
- GET:
- Routing and modules:
core/routes/routes.jsondefines category → module entries.Routes.tsxresolves each item’sconfig.tsxviaimport.meta.glob(Vite) and renders inMainRoutesRenderer.- Module visibility is user-driven:
userData.enabledModules(kebab-cased names) controls which routes render.
- Realtime: wrap UI in
SocketProvider; the server emitstaskPoolUpdateevents for long-running tasks.
Shared package patterns
shared/src/api/core/forgeAPIClient.tsis the single source for creating typed clients from the server’s route type. Don’t hand-roll fetchers.- Providers and hooks (e.g., API endpoint/online status, personalization, sidebar, toast) live in
shared/src/providers/*—reuse them inclientandlifeforge-ui.
Adding features — concrete examples
- New server endpoint: create under
server/src/apps/<feature>/routes/*.tsusingforge. Register it in that feature’sindex.tsviaforgeRouter({ ... }); it will be mounted automatically in the mainroutes.ts. - New client call: use
forgeAPI.<feature>.<endpoint>...with.input()and.query()/.mutate(); don’t hardcode URLs. - New client module/page: add
config.tsxunderclient/src/apps/<Module>/orclient/src/core/pages/<Page>/and reference it incore/routes/routes.json.
External dependencies and integration points
- PocketBase (primary DB/storage), Socket.IO (server push), Zod (validation), React Query (data fetching), Tailwind v4, Day.js, lodash. The server also includes media tooling (Sharp/FFmpeg/PDF utilities) and various 3rd-party integrations—follow existing patterns in
server/src/core/lib/*.
Questions or gaps? If an endpoint or config is unclear, point to the exact file and propose the smallest diff aligned with the patterns above, and ask for environment values when required.