Architecture
One npm package, not a monorepo. examples/* are pnpm workspace members that consume the package; website is this documentation site (Astro with Starlight), also a workspace member.
Folders are the public imports
Section titled “Folders are the public imports”| Folder | Entry | Runs in | May import |
|---|---|---|---|
src/schema |
protobase/schema |
anywhere | nothing from other src folders |
src/query |
protobase/query |
server | schema |
src/ui |
protobase/ui |
browser | schema |
src/server |
protobase/server |
server | schema, query |
src/cli |
bin/protobase.mjs |
Node; src/cli/serve also Bun |
schema, query, server |
src/fields/<type>/ |
none | schema.ts anywhere, sql.ts server, ui.tsx browser |
schema, per the rules below |
examples/* |
none | any | protobase/* entry points only |
Rules, enforced by pnpm check:boundaries (.dependency-cruiser.cjs):
src/ui/**andfields/*/ui.tsxnever importquery,server,cliorfields/*/sql.ts.src/schema/**andfields/*/schema.tsnever importquery,server,uiorcli.src/query/**never importsui,serverorcli.- Folders import each other only through
index.ts, never deep paths (exceptsrc/fields/*). - No circular dependencies.
Entry points export source directly; the package has no build step. The builds are for deployment: the serve runtime, src/cli/serve/main.ts bundled with its dependencies into dist/protobase-serve.js by protobase build-serve, and a project’s bundle, its config module plus a production build of src/ui/app, by protobase build (see CLI).
Tests and stories
Section titled “Tests and stories”- Unit tests:
*.test.tsnext to the code, run by Vitest in a node environment. - Stories:
*.stories.tsxnext to the component insrc/ui; Storybook config lives in.storybook/.
Contract
Section titled “Contract”src/schema/model.ts is the contract between folders. Change its types deliberately; every folder builds against them.
Filters
Section titled “Filters”Filters are Google AIP-160 text. Parsing, printing, order_by and the generic checker come from the standalone aip-parsers library (published on npm, entry points aip-parsers/filter and aip-parsers/order-by). src/schema/filter/ is the Protobase profile on top of it:
profile.tsregisters Protobase’s functions (in,search,similar,regex,isNull,now) and turns aResourceModelinto the library’s schema.parse.ts/lower.tsturn the generic tree intoFilterExpr;lift.ts/print.tsgo back.check.tsruns the library checker, thencheck-model.tsfor value formats (decimal, bigint, uuid, date, …), traversal and search fields.search(...)and bare words need.search((r) => [...])on the resource.where.tsbuilds typed filters in code.
ResourceModel.search is authoritative for server-side search; ViewModel.list.search only seeds the UI’s search box.
Documentation
Section titled “Documentation”website/ is this site. pnpm docs:dev serves it on port 4321; pnpm docs:build writes website/dist/ and fails on a broken internal link or anchor, so CI runs it on every pull request, followed by pnpm docs:check, which opens every page in Chromium and fails on a console error or a failed request. Pages are Markdown or MDX in website/src/content/docs/, and the sidebar is listed in website/astro.config.mjs.
Every push to the production branch builds the site and deploys it, and so does running the workflow by hand (gh workflow run docs.yml); either way it deploys to the Cloudflare Pages project protobase-docs, served at docs.protobase.net (.github/workflows/docs.yml). The workflow reads the Cloudflare account id from the repository variable CLOUDFLARE_ACCOUNT_ID and a token with Cloudflare Pages edit permission from the repository secret CLOUDFLARE_API_TOKEN.