Monorepo Architecture
Soku is organized as a pnpm workspace monorepo with Turborepo for task orchestration. The repository contains four application packages and two shared library packages, all managed from a single root.
Directory Structure
sokuho/
├── apps/
│ ├── web/ # Next.js 16 web application (App Router)
│ ├── functions/ # Firebase Cloud Functions (Node.js 22)
│ ├── docs/ # Docusaurus 3.5 documentation site
│ └── cypress/ # Cypress 15 E2E test suite
├── packages/
│ ├── schema/ # @soku/schema — shared Zod schemas
│ └── shared/ # @repo/shared — shared utilities
├── scripts/
│ └── sync-schema.js # Copies built schema into functions
├── cors.json # Firebase Storage CORS configuration
├── cypress.config.js # Cypress configuration
├── firebase.json # Firebase project configuration
├── firestore.indexes.json # Firestore composite indexes
├── firestore.rules # Firestore security rules
├── package.json # Root workspace scripts and devDependencies
├── pnpm-lock.yaml # Lockfile
├── pnpm-workspace.yaml # Workspace package declarations
├── storage.rules # Firebase Storage security rules
├── tsconfig.base.json # Shared TypeScript configuration
└── turbo.json # Turborepo pipeline configuration
Workspace Packages
apps/web — Next.js Web Application
The primary user-facing application. Built with Next.js 16.0.8, React 19.2.1, and the App Router. Contains the full dashboard, post creation flow, integrations management, billing, templates, automations, and a public landing page. Published as a private package named web.
Key dependencies include Redux Toolkit 2.9.0 for client state, TanStack React Query v5 with localStorage persistence for server state, Tailwind CSS ~3.4 for styling, Storybook 10 for component development, and Stripe for billing. See Web App Architecture for full details.
apps/functions — Firebase Cloud Functions
The backend layer, published as @repo/functions. Runs on Node.js 22 with firebase-functions v6 and Express 5 for the public API. Handles OAuth flows for eight social platforms, content publishing and orchestration via Cloud Tasks, webhook processing (Stripe, Meta), media processing (Sharp, FFmpeg), AI features (OpenAI SDK 4.56.0), and scheduled maintenance jobs. See Functions Architecture for full details.
apps/docs — Docusaurus Documentation
Internal documentation site built with Docusaurus 3.5.2. Contains architecture docs, provider integration guides, setup instructions, and developer references. Runs on its own dev server at port 3100.
apps/cypress — E2E Test Suite
End-to-end tests using Cypress 15. Contains test specs in e2e/, reusable fixtures in fixtures/, and custom commands in support/. Cypress configuration lives at the repository root in cypress.config.js.
packages/schema — @soku/schema
Shared Zod schema definitions used by both the web app and functions. Published as @soku/schema (version 0.0.2). Contains 30+ schema modules organized into two categories:
- Domain schemas (
src/domain/): Core business types includingplatform,integration,posts,templates,subscription,credits,media,library,automations,jobs,queues,tiers,user,captions,youtube,dashboard,tracing,logs,config,api-keys,api-requests, andprimitives. - API schemas (
src/api/): Request/response shapes for the public API includingposts,media,api-keys,ai,templates,billing, andsharedutilities.
Built with tsup to produce both ESM and CJS output plus TypeScript declarations. The web app consumes it via workspace:*, while functions receive a file copy via the sync:schema script (since Firebase deploy bundles from the functions directory).
packages/shared — @repo/shared
Lightweight shared utilities package consumed by the web app via workspace:^. Currently minimal, serving as the extension point for any cross-app utility functions.
Build and Dependency Graph
Turborepo manages the task pipeline. The dependency graph flows upward from packages to apps:
@soku/schema ──────┬──> web
│
└──> @repo/functions (via file copy)
@repo/shared ──────────> web
Task Pipeline (turbo.json)
| Task | Dependencies | Outputs | Cache |
|---|---|---|---|
build | ^build (builds dependencies first) | .next/**, dist/** | Yes |
dev | None | None | No (persistent) |
lint | None | None | Yes |
typecheck | None | None | Yes |
test | ^build | coverage/** | Yes |
The ^build dependency ensures that @soku/schema is built before any app that depends on it. The dev task runs in parallel across workspaces with the --parallel flag.
Global Environment Variables
Turborepo hashes these environment variables for cache invalidation:
NEXT_PUBLIC_FIREBASE_API_KEYNEXT_PUBLIC_FIREBASE_AUTH_DOMAINNEXT_PUBLIC_FIREBASE_PROJECT_IDNEXT_PUBLIC_FIREBASE_APP_ID
Root Configuration Files
pnpm-workspace.yaml
Declares the two workspace globs:
packages:
- "apps/*"
- "packages/*"
package.json (Root)
Specifies pnpm@9.12.2 as the package manager and node: "22" as the engine. Contains workspace-level scripts and shared devDependencies:
| Script | Description |
|---|---|
dev | Runs all apps in parallel via Turborepo |
dev:web | Runs only the web app |
dev:functions | Runs only functions (Firebase emulators) |
build | Builds all packages and apps |
test | Runs tests across all packages (excludes docs) |
lint | Lints all packages |
typecheck | Type-checks all packages |
check | Runs lint + typecheck + test (excludes docs) |
ci | Full CI pipeline: lint, typecheck, test, build |
deploy:functions | Deploys functions to Firebase |
deploy-function | Deploys a single function by name |
sync:schema | Builds schema and copies to functions |
prepare | Sets up Husky git hooks |
Shared devDependencies at root: @types/node (22+), cypress (15), eslint (9), husky (9), prettier (2.8.8), tsup (8), and turbo (2.5).
tsconfig.base.json
Shared TypeScript configuration inherited by all packages:
- Target: ES2022
- Module: ESNext with Bundler resolution
- Strict mode enabled
- JSX: react-jsx
- Path alias:
@soku/schemamaps topackages/schema/src
firebase.json
Configures the Firebase project:
- Functions: Source at
apps/functions, runtimenodejs22, predeploy runssync:schema - Hosting: Two sites (
sokuho-vaiandsokuho-storage) plus their staging equivalents, routing/v1/**to theapifunction and/**to themediafunction - Firestore: Rules and indexes from root files
- Storage: Rules from
storage.rules - Emulators: Functions (5001), Firestore (8080), Storage (9199) with UI enabled
firestore.rules and storage.rules
Security rules for Firestore and Cloud Storage, defined at the repository root and deployed via firebase deploy.
Key Architectural Decisions
Why pnpm + Turborepo
pnpm provides strict dependency isolation (no phantom dependencies) and efficient disk usage via content-addressable storage. Turborepo adds incremental builds with remote caching support, parallel execution, and dependency-aware task ordering.
Why a Shared Schema Package
Zod schemas in @soku/schema provide a single source of truth for data shapes. Both the web app and functions validate against the same schemas, eliminating type drift between frontend and backend. The sync:schema script ensures functions always deploy with the latest schema build.
Why File Copy for Functions
Firebase Cloud Functions deploy from a self-contained directory. Unlike the web app, which can resolve workspace dependencies at build time, functions need all dependencies present in their directory at deploy time. The sync:schema script builds the schema package and copies the output into apps/functions/.schema/, which functions reference as "@soku/schema": "file:.schema" in their package.json.
Node.js 22 Everywhere
Both the root package.json and apps/functions/package.json specify Node.js 22. The Firebase runtime is configured as nodejs22 in firebase.json. This ensures consistent behavior between local development and production.
ESM Throughout
Both @soku/schema and @repo/functions use "type": "module" for native ESM. The web app uses ESM via Next.js bundling. This avoids mixed module system issues across the monorepo.
How to Add a New Package
- Create the package directory under
packages/(for shared libraries) orapps/(for applications):
mkdir -p packages/my-lib/src
- Add a
package.jsonwith a name matching the@soku/or@repo/convention:
{
"name": "@soku/my-lib",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --out-dir dist",
"dev": "tsup src/index.ts --format esm,cjs --out-dir dist --watch",
"lint": "echo 'lint placeholder'",
"typecheck": "tsc --noEmit"
}
}
- Add a
tsconfig.jsonthat extends the base config:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
- Install dependencies and reference from consumers:
pnpm install
# From an app that needs it:
pnpm --filter web add @soku/my-lib@workspace:*
-
If functions need the new package, add a
syncstep similar tosync-schema.jsand update thefirebase.jsonpredeploy command. -
Run
pnpm buildfrom the root to verify the dependency graph resolves correctly.