GeoLibre npm Workspaces Monorepo Structure: Apps, Packages, and Workers Explained
The GeoLibre repository uses an npm workspaces monorepo with three workspace globs—apps/*, packages/*, and workers/*—to organize a React desktop app, reusable UI libraries, and Cloudflare Workers under a single dependency tree.
The opengeos/GeoLibre project is organized as a single npm-workspaces monorepo, enabling code sharing between a React-based desktop application, shared component libraries, and serverless edge workers. This structure eliminates version mismatches and allows cross-package imports without publishing to npm.
How the Workspace Globs Are Defined
The top-level package.json declares three workspace patterns that partition the codebase by responsibility:
{
"workspaces": [
"apps/*",
"packages/*",
"workers/*"
]
}
Source: [package.json](https://github.com/opengeos/GeoLibre/blob/main/package.json)
Running npm install from the repository root installs dependencies for all workspaces simultaneously, creating symlinks that let each workspace import from others as if they were published packages.
The apps/* Workspace: End-User Applications
The apps/* directory contains the primary user-facing application. Currently, this houses a single multi-target React app.
apps/geolibre-desktop
apps/geolibre-desktop is a React + Vite project compiling to three distinct targets:
| Target | Build Command | Output Location |
|---|---|---|
| Web (browser) | npm run build |
apps/geolibre-desktop/dist/ |
| Desktop (Tauri) | npm run tauri:build |
apps/geolibre-desktop/src-tauri/ |
| Jupyter (embed) | npm run build:embed |
Bundled into Python package |
The main entry point is [apps/geolibre-desktop/src/main.tsx](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/main.tsx).
The packages/* Workspace: Shared Libraries
Reusable code lives under packages/* as versioned workspace packages. These are imported using scoped names like @geolibre/ui.
| Package | Purpose | Key File |
|---|---|---|
@geolibre/ui |
UI primitives (buttons, dialogs, tables) | packages/ui/src/components/button.tsx |
@geolibre/plugins |
Extensible plugin system for map layers | packages/plugins/src/plugins/maplibre-vector.ts |
@geolibre/processing |
Client-side algorithms & Whitebox tool catalog | packages/processing/src/... |
@geolibre/core (planned) |
Zustand store, types, .geolibre.json schema |
— |
Because these are workspace packages, the app imports them directly:
import { Button } from '@geolibre/ui';
export function MyToolbar() {
return <Button onClick={() => console.log('clicked')}>Launch</Button>;
}
Plugin registration follows the same pattern:
import { registerPlugin } from '@geolibre/plugins';
registerPlugin('maplibre-vector', {
source: () => import('@geolibre/plugins/src/plugins/maplibre-vector')
});
The workers/* Workspace: Cloudflare Edge Services
The workers/* directory houses Cloudflare Workers that run background services at the edge. Each worker is its own workspace with independent TypeScript and Wrangler configurations.
| Worker | Role |
|---|---|
viewer |
Serves map-viewer assets and proxies vector data requests |
ai-proxy |
Lightweight AI endpoint for chat-like assistance |
Each worker maintains its own toolchain:
- [
workers/viewer/tsconfig.json](https://github.com/opengeos/GeoLibre/blob/main/workers/viewer/tsconfig.json) — TypeScript configuration for the viewer worker workers/ai-proxy/wrangler.jsonc— Wrangler deployment configuration
How the npm Workspaces Monorepo Fits Together
- Single dependency installation —
npm installat the repository root resolves and links all workspace dependencies. - Cross-package imports — Workspace packages reference each other by name (
@geolibre/ui,@geolibre/plugins) without npm publishing. - Unified command interface — Top-level scripts delegate to workspace-specific scripts using
npm run <script> -w <workspace>.
Common Development Workflows
Install everything once:
npm install
Start the web dev server (runs geolibre-desktop in browser mode):
npm run dev
# Equivalent to: npm run dev -w geolibre-desktop
Build the desktop installer:
npm run tauri:build
Run a worker locally:
cd workers/viewer
npm run dev # Uses wrangler dev under the hood
Run the full CI pipeline:
npm run ci # Builds app, runs tests, type-checks workers, checks Rust crate
Key Benefits of This Structure
- Code reuse — UI components, plugins, and processing tools are shared across web, desktop, and worker contexts
- Consistent type safety — All workspaces inherit from the same TypeScript configuration hierarchy
- Simplified CI/CD — Single pipeline builds, tests, and validates the entire stack including the Rust Tauri backend
Summary
- GeoLibre uses npm workspaces with three globs:
apps/*,packages/*, andworkers/* apps/geolibre-desktopis the sole UI application, building to web, desktop (Tauri), and Jupyter embed targetspackages/*contains reusable libraries (@geolibre/ui,@geolibre/plugins,@geolibre/processing) imported via workspace resolutionworkers/*houses Cloudflare Workers (viewer,ai-proxy) with independent deployment configs- Cross-package imports use scoped package names without npm publishing
- A single
npm installand unified scripts keep the developer experience simple
Frequently Asked Questions
What is an npm workspaces monorepo?
An npm workspaces monorepo is a single repository containing multiple Node.js packages that share a common node_modules directory and dependency tree. The root package.json declares workspace globs, and npm automatically creates symlinks between workspaces so they can import each other by package name. GeoLibre uses this to coordinate its app, libraries, and workers under one dependency management system.
How do packages in packages/* get imported by the app?
Packages in packages/* are declared as workspaces with their own package.json files and scoped names like @geolibre/ui. When npm install runs, npm creates symlinks in the root node_modules pointing to each workspace. The app then imports them using standard ES module syntax: import { Button } from '@geolibre/ui'. No npm publishing is required for internal workspace dependencies.
Why separate workers into their own workspace?
Cloudflare Workers require distinct Wrangler configurations, TypeScript compiler settings, and deployment targets compared to browser or Node.js code. Isolating them in workers/* lets each worker define its own wrangler.jsonc, tsconfig.json, and build pipeline while still sharing utilities from packages/* via workspace imports. This separation prevents worker-specific tooling from leaking into the main application build.
Can I add new packages or workers to the monorepo?
Yes. Create a new directory under apps/, packages/, or workers/ with a valid package.json including a name field. The package automatically becomes available as a workspace. For packages intended for cross-workspace import, use the @geolibre/ scope for consistency. Run npm install after adding any workspace to regenerate symlinks.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →