# GeoLibre npm Workspaces Monorepo Structure: Apps, Packages, and Workers Explained

> Understand the GeoLibre npm workspaces monorepo structure. Learn how apps packages and workers organize your React desktop app UI libraries and Cloudflare Workers efficiently.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: internals
- Published: 2026-08-01

---

**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`](https://github.com/opengeos/GeoLibre/blob/main/package.json) declares three workspace patterns that partition the codebase by responsibility:

```json
{
  "workspaces": [
    "apps/*",
    "packages/*",
    "workers/*"
  ]
}

```

*Source:* [[`package.json`](https://github.com/opengeos/GeoLibre/blob/main/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)](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`](https://github.com/opengeos/GeoLibre/blob/main/packages/ui/src/components/button.tsx) |
| **`@geolibre/plugins`** | Extensible plugin system for map layers | [`packages/plugins/src/plugins/maplibre-vector.ts`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) schema | — |

Because these are workspace packages, the app imports them directly:

```tsx
import { Button } from '@geolibre/ui';

export function MyToolbar() {
  return <Button onClick={() => console.log('clicked')}>Launch</Button>;
}

```

Plugin registration follows the same pattern:

```ts
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)](https://github.com/opengeos/GeoLibre/blob/main/workers/viewer/tsconfig.json) — TypeScript configuration for the viewer worker
- [`workers/ai-proxy/wrangler.jsonc`](https://github.com/opengeos/GeoLibre/blob/main/workers/ai-proxy/wrangler.jsonc) — Wrangler deployment configuration

## How the npm Workspaces Monorepo Fits Together

1. **Single dependency installation** — `npm install` at the repository root resolves and links all workspace dependencies.
2. **Cross-package imports** — Workspace packages reference each other by name (`@geolibre/ui`, `@geolibre/plugins`) without npm publishing.
3. **Unified command interface** — Top-level scripts delegate to workspace-specific scripts using `npm run <script> -w <workspace>`.

## Common Development Workflows

Install everything once:

```bash
npm install

```

Start the web dev server (runs `geolibre-desktop` in browser mode):

```bash
npm run dev

# Equivalent to: npm run dev -w geolibre-desktop

```

Build the desktop installer:

```bash
npm run tauri:build

```

Run a worker locally:

```bash
cd workers/viewer
npm run dev    # Uses wrangler dev under the hood

```

Run the full CI pipeline:

```bash
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/*`, and `workers/*`
- **`apps/geolibre-desktop`** is the sole UI application, building to web, desktop (Tauri), and Jupyter embed targets
- **`packages/*`** contains reusable libraries (`@geolibre/ui`, `@geolibre/plugins`, `@geolibre/processing`) imported via workspace resolution
- **`workers/*`** houses Cloudflare Workers (`viewer`, `ai-proxy`) with independent deployment configs
- Cross-package imports use scoped package names without npm publishing
- A single `npm install` and 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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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.