Skip to main content

Build Variants

World Monitor supports 4 build variants from a single codebase:
VariantEnvironment VariableProduction URLFocus
FullVITE_VARIANT=fullworldmonitor.appGeopolitics, military, conflicts
TechVITE_VARIANT=techtech.worldmonitor.appAI/ML, startups, cloud
FinanceVITE_VARIANT=financefinance.worldmonitor.appMarkets, trading, central banks
HappyVITE_VARIANT=happyhappy.worldmonitor.appGood news, positive trends
Each variant:
  • Loads curated data layers and RSS feeds
  • Uses variant-specific branding and metadata
  • Shares the same codebase and build pipeline

Web Builds (Production)

Build All Variants

npm run build
Each build command:
  1. Runs TypeScript type checking (tsc)
  2. Bundles assets with Vite
  3. Generates Brotli-compressed files (.br)
  4. Creates a service worker for PWA features
  5. Outputs to dist/

Build Output

dist/
├── index.html                    # Main entry point
├── settings.html                 # Settings window (desktop)
├── live-channels.html            # Live video grid
├── assets/
│   ├── index-[hash].js          # Main bundle
│   ├── index-[hash].css         # Styles
│   ├── deck-stack-[hash].js     # deck.gl + luma.gl
│   ├── maplibre-[hash].js       # MapLibre GL JS
│   ├── transformers-[hash].js   # Transformers.js ML
│   ├── onnxruntime-[hash].js    # ONNX Runtime Web
│   ├── locale-fr-[hash].js      # French translations
│   └── ...                      # Other chunks
├── favico/                       # Favicon variants
├── sw.js                         # Service worker
└── manifest.webmanifest          # PWA manifest

Preview Production Build

npm run preview
Starts a local server at http://localhost:4173 serving the production build.

Build Configuration

Vite configuration (vite.config.ts):
  • Chunk splitting — Large dependencies are split into separate chunks:
    • deck-stack — deck.gl, luma.gl, loaders.gl, math.gl, h3-js
    • maplibre — MapLibre GL JS
    • transformers — Transformers.js
    • onnxruntime — ONNX Runtime Web
    • d3 — D3.js
    • i18n — i18next
    • sentry — Error tracking
    • locale-{lang} — Lazy-loaded translations
  • Brotli pre-compression — All .js, .css, .html, .svg, .json, .wasm files > 1KB are pre-compressed with Brotli (.br extension)
  • Service worker — PWA features with offline map tile caching

Desktop Builds (Tauri)

Prerequisites

  1. Install Rust toolchain:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  1. Install platform-specific dependencies:
xcode-select --install

Build Desktop App

npm run desktop:build:full
Each command:
  1. Syncs version from package.json to Tauri config
  2. Sets VITE_VARIANT and VITE_DESKTOP_RUNTIME=1
  3. Builds the sidecar sebuf gateway
  4. Builds the Tauri app with Rust

Build Output

src-tauri/target/release/bundle/
├── dmg/
│   └── World Monitor_2.5.21_aarch64.dmg    # Apple Silicon
│   └── World Monitor_2.5.21_x64.dmg        # Intel
└── macos/
    └── World Monitor.app                    # Unsigned app bundle

Package and Sign (Release)

For distributable releases with code signing:
npm run desktop:package:macos:full:sign -- \
  --cert "Developer ID Application: Your Name" \
  --team-id TEAMID123
The packaging script (scripts/desktop-package.mjs):
  1. Builds the Tauri app
  2. Signs the app bundle/installer
  3. Notarizes (macOS only)
  4. Creates a distributable package

Type Checking

Check Frontend

npm run typecheck
Runs tsc --noEmit on the main TypeScript configuration.

Check API Handlers

npm run typecheck:api
Runs tsc --noEmit -p tsconfig.api.json on server-side code.

Check Everything

npm run typecheck:all
Runs both frontend and API type checks.

Build Optimizations

Chunk Size Limits

The build is configured to warn for chunks larger than 1200 KB:
// vite.config.ts
build: {
  chunkSizeWarningLimit: 1200,
}
This is intentionally higher than Vite’s default (500 KB) because geospatial libraries (MapLibre, deck.gl) produce large bundles even when split.

Manual Chunk Splitting

// vite.config.ts
output: {
  manualChunks(id) {
    if (id.includes('/@deck.gl/')) return 'deck-stack';
    if (id.includes('/maplibre-gl/')) return 'maplibre';
    // ...
  },
}
This ensures:
  • Heavy dependencies are loaded on-demand
  • Browser can cache stable libraries separately
  • Initial page load is faster

Tree Shaking

Unused exports are automatically removed during build:
// vite.config.ts
build: {
  rollupOptions: {
    output: {
      manualChunks: /* ... */,
    },
  },
}

Brotli Compression

The brotliPrecompressPlugin generates .br files for all static assets:
assets/index-abc123.js       → 450 KB
assets/index-abc123.js.br    → 120 KB (73% smaller)
Vercel/Cloudflare automatically serve .br files when the client sends Accept-Encoding: br.

Environment Variables

Build-Time Variables

These are embedded into the JavaScript bundle at build time:
VariablePurposeExample
VITE_VARIANTBuild variantfull, tech, finance, happy
VITE_DESKTOP_RUNTIMEEnable desktop features1
VITE_SENTRY_DSNError trackinghttps://...
VITE_POSTHOG_KEYProduct analyticsphc_...
VITE_MAP_INTERACTION_MODEMap controls3d or flat

Runtime Variables (Server-Only)

These are available only in Vercel/serverless functions:
VariablePurpose
GROQ_API_KEYAI summarization (Groq)
OPENROUTER_API_KEYAI summarization (OpenRouter)
UPSTASH_REDIS_REST_URLCache backend
UPSTASH_REDIS_REST_TOKENCache authentication
FINNHUB_API_KEYStock quotes
EIA_API_KEYEnergy data
ACLED_ACCESS_TOKENConflict data
WINGBITS_API_KEYAircraft enrichment
See .env.example for the complete list.

CI/CD Integration

Vercel Deployment

Production builds are deployed automatically on push to main:
# vercel.json
{
  "buildCommand": "npm run build:full",
  "outputDirectory": "dist",
  "framework": "vite"
}
Variants are deployed to separate domains:
  • worldmonitor.app — Full variant
  • tech.worldmonitor.app — Tech variant
  • finance.worldmonitor.app — Finance variant
  • happy.worldmonitor.app — Happy variant

GitHub Actions

Desktop builds are automated via GitHub Actions:
# .github/workflows/release.yml
name: Release Desktop Builds
on:
  push:
    tags:
      - 'v*'

jobs:
  build-macos:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run desktop:build:full
      - run: npm run desktop:package:macos:full:sign

Troubleshooting

Install buf CLI:
make install-buf
Regenerate proto code:
make clean
make generate
Update Rust toolchain:
rustup update stable
Check which dependencies are bloating the bundle:
npx vite-bundle-visualizer
Clear service worker cache:
# In DevTools Console:
navigator.serviceWorker.getRegistrations().then(registrations => {
  registrations.forEach(r => r.unregister());
});

Next Steps

Testing

Run E2E, API, and regression tests

Deployment

Deploy to production

Build docs developers (and LLMs) love