Skip to main content
The repository is configured as an npm workspaces monorepo. The root package.json declares two workspace packages:
{
  "workspaces": ["astro-app", "studio"]
}
Running npm install at the repo root hoists shared dependencies and installs both packages in a single pass.

astro-app

The Astro frontend. Responsible for all public pages, the authenticated portal, block rendering, and Cloudflare edge integration.
AreaLocationDescription
Pagessrc/pages/SSG public routes and SSR portal routes
Block componentssrc/components/blocks/100+ fulldev/ui template variants
Custom blockssrc/components/blocks/custom/23 CMS-connected blocks with business logic
UI primitivessrc/components/ui/shadcn-style component copies (owned, not a dependency)
Portal componentssrc/components/portal/8 React-hydrated components for the sponsor portal
Block registrysrc/components/block-registry.tsAuto-discovers all block components via import.meta.glob()
Block renderersrc/components/BlockRenderer.astroSingle dispatch: block._type → spread props
GROQ queriessrc/lib/sanity.tsAll queries using defineQuery() + block resolver functions
Auth databasesrc/lib/drizzle-schema.tsDrizzle ORM schema for Cloudflare D1
Stylessrc/styles/global.css@import "tailwindcss" + CSS custom properties + theme tokens
Configastro.config.mjsBuild config, integrations, multi-site env vars

Key dependencies

PackageRole
astroCore framework (SSG + SSR)
@astrojs/cloudflareCloudflare Pages/Workers adapter
@sanity/astroSanity client integration + Visual Editing
@astrojs/reactReact island support for portal components
@tailwindcss/viteTailwind CSS v4 via Vite plugin
better-authAuthentication (OAuth + Magic Link)
drizzle-ormType-safe ORM for Cloudflare D1
nanostoresLightweight state store for React portal components
@iconify/utilsIcon resolution for Lucide and Simple Icons

studio

Sanity Studio v5. Responsible for content editing, schema definition, and Visual Editing presentation.
AreaLocationDescription
Document schemassrc/schemaTypes/documents/7 top-level document types
Block schemassrc/schemaTypes/blocks/25 page builder block object schemas
Object schemassrc/schemaTypes/objects/14 shared reusable field groups
Schema factorysrc/schemaTypes/helpers/defineBlock.tsMerges block-base into every block
Schema indexsrc/schemaTypes/index.tsExports all types; applies multi-workspace filtering

Key dependencies

PackageRole
sanityCore Sanity Studio SDK
@sanity/visionGROQ query tester in Studio

Root workspace

The root package.json is not a publishable package. It owns shared developer tooling and orchestrates both child packages.
AreaLocationDescription
E2E teststests/Playwright test suites
Playwright configplaywright.config.ts5 browser projects, axe-core, base URL
Release config.releaserc.jsonsemantic-release: version bump, changelog, GitHub Release, preview sync
ChangelogCHANGELOG.mdAuto-generated; do not edit manually
CI/CD.github/workflows/ci, release, sync-preview, deploy-storybook, enforce-preview-only
Dev docsdocs/Architecture and team guides

Root scripts

CommandWhat it does
npm run devStarts Astro dev server + Sanity Studio concurrently
npm run dev:storybookStarts Astro + Studio + Storybook (all three)
npm run storybookStarts Storybook alone on port 6006
npm run test:unitRuns Vitest unit tests
npm run test:unit:watchVitest in watch mode
npm run testBuilds then runs all Playwright E2E tests
npm run test:integrationPlaywright integration tests (no browser)
npm run test:uiPlaywright in interactive UI mode

How packages relate

  • studio defines the content schema. astro-app consumes it at build time via GROQ queries.
  • astro-app embeds stega metadata in rendered strings so Sanity Studio’s Presentation tool can map visible elements back to their schema fields.
  • The root workspace orchestrates both: npm run dev starts both dev servers via concurrently, and npm run test builds astro-app before running Playwright.

npm workspace command patterns

Run a script in a specific package:
# Build only the Astro app
npm run build --workspace=astro-app

# Deploy only Sanity Studio
npx sanity deploy --workspace=studio
Install a dependency into a specific package:
# Add a dependency to astro-app only
npm install some-package --workspace=astro-app

# Add a dev dependency to the root (shared tooling)
npm install -D some-tool
Never run npm install inside a child package directory. Always run it from the repo root so the workspace hoisting stays consistent and both lockfiles remain in sync.