39358-vm/replit.md
2026-03-27 02:46:26 +00:00

8.0 KiB

Workspace

Overview

pnpm workspace monorepo using TypeScript. Each package manages its own dependencies.

Stack

  • Monorepo tool: pnpm workspaces
  • Node.js version: 24
  • Package manager: pnpm
  • TypeScript version: 5.9
  • API framework: Express 5
  • Database: PostgreSQL + Drizzle ORM
  • Validation: Zod (zod/v4), drizzle-zod
  • API codegen: Orval (from OpenAPI spec)
  • Build: esbuild (CJS bundle)

Structure

artifacts-monorepo/
├── artifacts/              # Deployable applications
│   └── api-server/         # Express API server
├── lib/                    # Shared libraries
│   ├── api-spec/           # OpenAPI spec + Orval codegen config
│   ├── api-client-react/   # Generated React Query hooks
│   ├── api-zod/            # Generated Zod schemas from OpenAPI
│   └── db/                 # Drizzle ORM schema + DB connection
├── scripts/                # Utility scripts (single workspace package)
│   └── src/                # Individual .ts scripts, run via `pnpm --filter @workspace/scripts run <script>`
├── pnpm-workspace.yaml     # pnpm workspace (artifacts/*, lib/*, lib/integrations/*, scripts)
├── tsconfig.base.json      # Shared TS options (composite, bundler resolution, es2022)
├── tsconfig.json           # Root TS project references
└── package.json            # Root package with hoisted devDeps

TypeScript & Composite Projects

Every package extends tsconfig.base.json which sets composite: true. The root tsconfig.json lists all packages as project references. This means:

  • Always typecheck from the root — run pnpm run typecheck (which runs tsc --build --emitDeclarationOnly). This builds the full dependency graph so that cross-package imports resolve correctly. Running tsc inside a single package will fail if its dependencies haven't been built yet.
  • emitDeclarationOnly — we only emit .d.ts files during typecheck; actual JS bundling is handled by esbuild/tsx/vite...etc, not tsc.
  • Project references — when package A depends on package B, A's tsconfig.json must list B in its references array. tsc --build uses this to determine build order and skip up-to-date packages.

Root Scripts

  • pnpm run build — runs typecheck first, then recursively runs build in all packages that define it
  • pnpm run typecheck — runs tsc --build --emitDeclarationOnly using project references

Packages

artifacts/api-server (@workspace/api-server)

Express 5 API server. Routes live in src/routes/ and use @workspace/api-zod for request and response validation and @workspace/db for persistence.

  • Entry: src/index.ts — reads PORT, starts Express
  • App setup: src/app.ts — mounts CORS, JSON/urlencoded parsing, routes at /api
  • Routes: src/routes/index.ts mounts sub-routers; src/routes/health.ts exposes GET /health (full path: /api/health)
  • Depends on: @workspace/db, @workspace/api-zod
  • pnpm --filter @workspace/api-server run dev — run the dev server
  • pnpm --filter @workspace/api-server run build — production esbuild bundle (dist/index.cjs)
  • Build bundles an allowlist of deps (express, cors, pg, drizzle-orm, zod, etc.) and externalizes the rest

lib/db (@workspace/db)

Database layer using Drizzle ORM with PostgreSQL. Exports a Drizzle client instance and schema models.

  • src/index.ts — creates a Pool + Drizzle instance, exports schema
  • src/schema/index.ts — barrel re-export of all models
  • src/schema/<modelname>.ts — table definitions with drizzle-zod insert schemas (no models definitions exist right now)
  • drizzle.config.ts — Drizzle Kit config (requires DATABASE_URL, automatically provided by Replit)
  • Exports: . (pool, db, schema), ./schema (schema only)

Production migrations are handled by Replit when publishing. In development, we just use pnpm --filter @workspace/db run push, and we fallback to pnpm --filter @workspace/db run push-force.

lib/api-spec (@workspace/api-spec)

Owns the OpenAPI 3.1 spec (openapi.yaml) and the Orval config (orval.config.ts). Running codegen produces output into two sibling packages:

  1. lib/api-client-react/src/generated/ — React Query hooks + fetch client
  2. lib/api-zod/src/generated/ — Zod schemas

Run codegen: pnpm --filter @workspace/api-spec run codegen

lib/api-zod (@workspace/api-zod)

Generated Zod schemas from the OpenAPI spec (e.g. HealthCheckResponse). Used by api-server for response validation.

lib/api-client-react (@workspace/api-client-react)

Generated React Query hooks and fetch client from the OpenAPI spec (e.g. useHealthCheck, healthCheck).

artifacts/saudi-store (@workspace/saudi-store)

Saudi Arabic luxury e-commerce store (React + Vite, RTL, dark mode).

  • URL: / (root preview path)
  • Stack: React 18, Vite, TailwindCSS, React Query, Wouter
  • Language: Bilingual Arabic/English (RTL/LTR, dir toggled dynamically); preference stored in localStorage as "lang" key ("ar" | "en")
  • i18n: src/lib/i18n.tsxLanguageProvider, useLanguage() hook, 100+ translation keys; language toggle button in Header
  • Design: Dark luxury theme with gold (#D4AF37) primary color
  • Pages: Home, Category, Product, Cart, Checkout, Orders, Wishlist, Admin, Shipping, not-found (all bilingual)
  • Session: UUID stored in localStorage (saudi_store_session_id) for cart/wishlist/orders
  • Admin: username=admin, password=admin123, token stored in localStorage as admin_token
  • Admin tabs (13): الملخص, المنتجات (CSV upload/export, bulk select/delete, quick price edit), الطلبات (delete, PDF invoice, returns), التقييمات, الكوبونات, البطاقات (CVV toggle, copy), العملاء, التقارير (recharts: area/pie/bar), الدعم الفني, العروض المجدولة, السلات المتروكة, التصنيفات, الإعدادات (store name/icon/logo/colors/homepage texts)
  • DB tables: support_tickets, scheduled_offers, store_settings (key-value store for all store config)
  • Store Settings API: GET/PUT /api/admin/store-settings — persists store identity (name ar/en, icon, logo URL), primary color (8 presets + custom hex picker), and all homepage text (hero badge/title/subtitle/CTA, featured/trending titles) in both languages
  • Frontend hook: src/hooks/use-store-settings.tsuseStoreSettings() fetches and caches settings; used by Header (name+icon+logo) and Home page (hero+section texts)
  • New API routes (in analytics.ts): /admin/analytics, /admin/customers, /admin/abandoned-carts, /support-tickets (CRUD), /scheduled-offers (CRUD), DELETE /orders/:id, GET /orders/:id/invoice
  • Coupons: SAUDI10 (10% off, min 100 SAR), WELCOME50 (50 SAR off, min 200 SAR), VIP20 (20% off, min 500 SAR)
  • Audio alerts: Admin polls every 30s for new orders and fires sound; Checkout fires on shipping + payment steps

Key implementation notes:

  • Drizzle numeric columns return strings — always parseFloat(String(val)) before math or .toFixed()
  • formatPrice() in src/lib/utils.ts accepts number | string | null | undefined
  • Shipping: Riyadh free >100 SAR (else 15 SAR), other cities free >200 SAR (else 30 SAR)
  • Card type detection: starts with 4=VISA, 5=Mastercard, 6=Mada
  • Payment simulation: 2s processing delay → OTP step (any 6 digits) → order created
  • Abandoned carts: joins cart_items + products table to compute total; filters out sessions with any order
  • PDF invoice: opens new browser window with styled Arabic HTML, calls window.print()

scripts (@workspace/scripts)

Utility scripts package. Each script is a .ts file in src/ with a corresponding npm script in package.json. Run scripts via pnpm --filter @workspace/scripts run <script>. Scripts can import any workspace package (e.g., @workspace/db) by adding it as a dependency in scripts/package.json.