Skip to main content

Overview

The packages/ directory contains shared libraries used across multiple applications. These packages are organized into TypeScript (frontend) and Rust (backend) libraries.

Frontend Packages (TypeScript/Vue)

@modrinth/ui

Path: packages/ui/
Description: Shared Vue component library for web and desktop app

Features

  • Vue 3 components (buttons, modals, cards, forms)
  • Cross-platform pages (used in both Nuxt and Tauri)
  • Composables and utilities
  • i18n translations (34 languages)
  • Dependency injection providers
  • Storybook component development

Structure

packages/ui/src/
├── components/       # Vue components by domain
│   ├── base/        # Base UI (Button, Input, Modal)
│   ├── project/     # Project-related components
│   ├── version/     # Version components
│   └── ...
├── pages/            # Cross-platform pages
├── composables/      # Vue composables
├── providers/        # DI context providers
├── utils/            # Utility functions
├── locales/          # Translation files (en-US.json, etc.)
├── styles/           # Tailwind utilities
└── stories/          # Storybook stories

Usage

import { Button, Modal, ProjectCard } from '@modrinth/ui'
import { injectModrinthClient, provideModrinthClient } from '@modrinth/ui'

Color System

Use semantic color variables for theme support:
  • Surfaces: bg-surface-1 through bg-surface-5
  • Text: text-contrast, text-primary, text-secondary
  • Brand: bg-brand, text-brand
  • Semantic: bg-red, bg-green, bg-orange, bg-blue
See the Frontend Web guide for complete color usage.

Storybook

Develop components in isolation:
pnpm storybook
Access at http://localhost:6006

@modrinth/api-client

Path: packages/api-client/
Description: Platform-agnostic API client for Modrinth services

Features

  • Multi-API support (Labrinth, Archon, Kyros, ISO3166)
  • Platform variants:
    • GenericModrinthClient - Node/browser (uses ofetch)
    • NuxtModrinthClient - Nuxt SSR-aware (uses $fetch)
    • TauriModrinthClient - Tauri (uses @tauri-apps/plugin-http)
  • Feature system (auth, retry, circuit breaker)
  • XHR upload with progress tracking
  • WebSocket support for real-time events
  • Type-safe API methods

Architecture

Request Flow:
  Module Method → client.request() → Feature Chain → Platform executeRequest()

Module Structure

client.labrinth.projects_v2
client.labrinth.projects_v3
client.labrinth.versions_v3
client.labrinth.users_v2
client.archon.servers_v0
client.archon.servers_v1
client.archon.backups_v1
client.kyros.files_v0
client.iso3166.data

Usage Example

import { NuxtModrinthClient, AuthFeature } from '@modrinth/api-client'

const client = new NuxtModrinthClient({
	userAgent: 'my-app/1.0.0',
	features: [
		new AuthFeature({
			token: async () => auth.value.token,
		}),
	],
})

// Fetch project
const project = await client.labrinth.projects_v3.get('sodium')

// Search with filters
const results = await client.labrinth.search_v3.search({
	query: 'optimization',
	facets: [['categories:optimization'], ['project_type:mod']],
	limit: 20,
})

Features (Middleware)

AuthFeature: Injects Bearer token
new AuthFeature({ token: 'mrp_...' })
new AuthFeature({ token: async () => getToken() })
RetryFeature: Exponential backoff retry
new RetryFeature({
	maxAttempts: 3,
	backoffStrategy: 'exponential',
	initialDelay: 1000,
})
CircuitBreakerFeature: Prevents cascade failures
new CircuitBreakerFeature({
	maxFailures: 3,
	resetTimeout: 30000,
})

File Uploads

const handle = client.kyros.files_v0.uploadFile(path, file, {
	onProgress: ({ progress }) => {
		uploadProgress.value = Math.round(progress * 100)
	},
})

// Cancel if needed
handle.cancel()

// Wait for completion
await handle.promise

@modrinth/assets

Path: packages/assets/
Description: Styling, design tokens, and auto-generated icons

Contents

  • Tailwind preset (tailwind-preset.ts)
  • CSS variables (styles/variables.scss) - theme tokens
  • SVG icons (auto-generated from icons/ directory)
  • Semantic color tokens

Adding Icons

# Add SVG file to packages/assets/icons/
pnpm icons:add my-icon.svg

# Icons are auto-generated and exported
import { MyIcon } from '@modrinth/assets'

@modrinth/utils

Path: packages/utils/
Description: Shared utility functions
import { formatNumber, debounce, formatDate } from '@modrinth/utils'

const downloads = formatNumber(1234567) // "1.2M"
const timestamp = formatDate(new Date()) // "2 hours ago"

@modrinth/blog

Path: packages/blog/
Description: Blog system and changelog data
  • Blog post rendering
  • Changelog parsing
  • Markdown processing

@modrinth/moderation

Path: packages/moderation/
Description: Moderation utilities and content filtering
  • Content censoring
  • Profanity detection
  • User report handling

@modrinth/tooling-config

Path: packages/tooling-config/
Description: Shared ESLint, Prettier, and TypeScript configurations
package.json
{
	"prettier": "@modrinth/tooling-config/prettier.config.cjs",
	"eslintConfig": {
		"extends": "@modrinth/tooling-config/eslint.config.js"
	}
}

Rust Packages

theseus (app-lib)

Path: packages/app-lib/
Crate: theseus
Description: Desktop app backend library

Features

  • Profile management: Create and manage Minecraft instances
  • Mod installation: Install from Modrinth or local files
  • Game launching: Launch Minecraft with mods
  • Metadata: Minecraft, Forge, Fabric, Quilt version data
  • Java detection: Auto-detect and download Java runtimes
  • Database: SQLite for local state
  • Analytics: Track usage (opt-in)

Core APIs

use theseus::profile::Profile;
use theseus::pack::install_mod;
use theseus::launcher::launch;

// Create profile
let profile = Profile::create("My Modpack", "1.20.1", "fabric").await?;

// Install mod
install_mod(&profile.id, "sodium", None).await?;

// Launch game
let process = launch(&profile.id, None, None).await?;
See Desktop App for complete documentation.

daedalus

Path: packages/daedalus/
Description: Minecraft and mod loader metadata library

Features

  • Minecraft version manifests
  • Forge version data
  • Fabric loader and mappings
  • Quilt loader versions
  • Modpack format parsing (mrpack, CurseForge)

Usage

use daedalus::minecraft::VersionManifest;
use daedalus::modded::LoaderVersion;

// Get Minecraft versions
let manifest = VersionManifest::fetch().await?;
let version_1_20_1 = manifest.get_version("1.20.1")?;

// Get Fabric versions
let fabric_versions = LoaderVersion::fetch_fabric("1.20.1").await?;

ariadne

Path: packages/ariadne/
Description: Analytics library for tracking usage and events

Features

  • Event tracking
  • ClickHouse integration
  • Privacy-preserving analytics
  • Opt-in/opt-out support
use ariadne::{Event, track};

track(Event::Download {
    version_id: "abc123",
    user_id: Some("user_456"),
}).await?;

modrinth-log

Path: packages/modrinth-log/
Description: Logging utilities with tracing integration
use modrinth_log::{info, error};

info!("Server started on port {}", 8000);
error!("Failed to connect to database: {}", err);

modrinth-util

Path: packages/modrinth-util/
Description: General Rust utilities
  • String utilities
  • Error handling helpers
  • Type conversions

modrinth-maxmind

Path: packages/modrinth-maxmind/
Description: MaxMind GeoIP integration
use modrinth_maxmind::GeoIp;

let geoip = GeoIp::new("/path/to/GeoLite2-City.mmdb")?;
let location = geoip.lookup("8.8.8.8")?;

muralpay

Path: packages/muralpay/
Description: Payment processing (Stripe integration)
use muralpay::{create_payment_intent, PaymentIntent};

let intent = create_payment_intent(
    1000, // $10.00
    "usd",
    "user_id",
).await?;

path-util

Path: packages/path-util/
Description: Cross-platform path utilities
use path_util::{normalize_path, get_app_data_dir};

let normalized = normalize_path("/foo/../bar"); // "/bar"
let app_dir = get_app_data_dir()?; // OS-specific app data directory

sqlx-tracing

Path: packages/sqlx-tracing/
Description: SQLx query tracing and logging
use sqlx_tracing::TracingExecutor;

let executor = TracingExecutor::new(pool);
// Queries are automatically logged with tracing

labrinth-derive

Path: packages/labrinth-derive/
Description: Derive macros for Labrinth models
use labrinth_derive::Queryable;

#[derive(Queryable)]
struct Project {
    id: String,
    slug: String,
    title: String,
}

Package Dependencies

Workspace Dependencies

Packages reference each other using workspace protocol: package.json:
{
	"dependencies": {
		"@modrinth/ui": "workspace:*",
		"@modrinth/api-client": "workspace:*",
		"@modrinth/assets": "workspace:*"
	}
}
Cargo.toml:
[dependencies]
theseus = { path = "../packages/app-lib" }
daedalus = { path = "../packages/daedalus" }
ariadne = { path = "../packages/ariadne" }

Dependency Graph

apps/frontend/
  └── @modrinth/ui
      ├── @modrinth/api-client
      ├── @modrinth/assets
      └── @modrinth/utils

apps/app-frontend/
  └── @modrinth/ui
      ├── @modrinth/api-client
      └── @modrinth/assets

apps/app/
  └── theseus (app-lib)
      ├── daedalus
      ├── ariadne
      └── modrinth-util

apps/labrinth/
  ├── ariadne
  ├── daedalus
  ├── modrinth-log
  ├── modrinth-util
  ├── modrinth-maxmind
  └── muralpay

Creating a New Package

TypeScript Package

1

Create Directory

mkdir packages/my-package
cd packages/my-package
2

Initialize package.json

package.json
{
  "name": "@modrinth/my-package",
  "version": "0.0.0",
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  },
  "scripts": {
    "build": "tsup src/index.ts --format esm --dts",
    "lint": "eslint .",
    "fix": "eslint . --fix"
  },
  "devDependencies": {
    "@modrinth/tooling-config": "workspace:*",
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  }
}
3

Create Source Files

src/index.ts
export function myUtility() {
  return 'Hello from my package'
}
4

Add TypeScript Config

tsconfig.json
{
  "extends": "@modrinth/tooling-config/tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist"
  },
  "include": ["src"]
}
5

Update pnpm-workspace.yaml

Already includes packages/*, so no change needed.
6

Build and Test

pnpm build

# Use in another package
pnpm add @modrinth/my-package --filter @modrinth/frontend

Rust Package

1

Create Crate

cargo new --lib packages/my-crate
2

Update Cargo.toml

packages/my-crate/Cargo.toml
[package]
name = "my-crate"
version = "0.1.0"
edition.workspace = true

[dependencies]
# Add dependencies

[lints]
workspace = true
3

Add to Workspace

Edit root Cargo.toml:
[workspace]
members = [
  # ...
  "packages/my-crate",
]
4

Implement Library

packages/my-crate/src/lib.rs
pub fn my_function() -> String {
    "Hello from my crate".to_string()
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_my_function() {
        assert_eq!(my_function(), "Hello from my crate");
    }
}
5

Use in Other Crates

[dependencies]
my-crate = { path = "../../packages/my-crate" }

Publishing

Internal Packages

Most packages are internal and not published to npm/crates.io. They’re only used within the monorepo.

Public Packages

Some packages may be published:
  • @modrinth/api-client - Public npm package
  • daedalus - May be published to crates.io
Publishing to npm:
cd packages/api-client
pnpm build
npm publish --access public
Publishing to crates.io:
cd packages/daedalus
cargo publish

Best Practices

Package Design

  1. Single Responsibility: Each package should have a clear, focused purpose
  2. Minimal Dependencies: Avoid heavy dependencies in shared packages
  3. Platform Agnostic: Shared code should work across web and app (when possible)
  4. Type Safety: Export TypeScript types for all public APIs
  5. Documentation: Include README with usage examples

Versioning

Internal packages use workspace:* to always use the local version. Version numbers are primarily for tracking, not for dependency resolution.

Breaking Changes

When making breaking changes to a shared package:
  1. Update all consumers in the same PR
  2. Run full test suite: pnpm test
  3. Ensure all apps build: pnpm build

Testing

Test packages in isolation:
# TypeScript package
pnpm --filter @modrinth/my-package test

# Rust crate
cargo test -p my-crate

Next Steps

Frontend (Web)

See how packages are used in the web app

Desktop App

Learn about Theseus and app integration

Backend (Labrinth)

Explore Rust packages used in the backend

Code Style

Follow coding standards for packages