-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Software Design Specification (SDS)
Self-Hosted Monorepo Package Manager Dashboard
Document Version: 1.0
Date: October 08, 2025
Prepared By: Development Team
Approved By: Project Stakeholders
1. Introduction
1.1 Purpose
This Software Design Specification (SDS) describes the complete technical architecture, implementation details, and development setup for a self-hosted monorepo package manager dashboard. The system will be distributed as an npm package that automatically generates a web UI for package oversight within any monorepo.
1.2 Scope
The dashboard provides visual management and monitoring for packages in monorepos using pnpm, Turborepo, and Nx, with automated CI/CD integration, semantic versioning, and development workflow optimization.
1.3 Intended Audience
- Software Architects
- Frontend/Backend Developers
- DevOps Engineers
- Quality Assurance Engineers
- Technical Project Managers
1.4 Reference Documents
- Functional Requirements Specification (FRS) v1.0
- Turborepo Documentation[124][125][127]
- PNPM Workspace Configuration[132][141][144]
- Semantic Release Monorepo Setup[139][142]
2. System Architecture
2.1 High-Level Architecture
┌─────────────────────────────────────────────────────────────┐
│ Monorepo Dashboard │
├─────────────────────────────────────────────────────────────┤
│ Frontend (React + TypeScript + Vite) │
│ ├── Package Overview ├── Dependency Graph │
│ ├── Health Monitoring ├── Version Management │
│ └── Configuration Inspector │
├─────────────────────────────────────────────────────────────┤
│ Backend API (Node.js + Express) │
│ ├── Package Scanner ├── CI/CD Integration │
│ ├── Git Operations ├── Semantic Release │
│ └── WebSocket Server └── File System Watcher │
├─────────────────────────────────────────────────────────────┤
│ Integration Layer │
│ ├── pnpm/Turborepo/Nx ├── GitHub Actions/GitLab CI │
│ ├── Git Hooks └── NPM Registry │
└─────────────────────────────────────────────────────────────┘
2.2 Technology Stack
| Layer | Technology | Version | Purpose |
|---|---|---|---|
| Frontend | React | ^18.0.0 | UI Framework |
| Frontend | TypeScript | ^5.0.0 | Type Safety |
| Frontend | Vite | ^5.0.0 | Build Tool & Dev Server |
| Frontend | Cytoscape.js | ^3.26.0 | Graph Visualization |
| Backend | Node.js | >=18.0.0 | Runtime Environment |
| Backend | Express | ^4.18.0 | Web Framework |
| Backend | TypeScript | ^5.0.0 | Type Safety |
| Backend | WebSocket | ^8.0.0 | Real-time Updates |
| Package Manager | pnpm | ^8.0.0 | Package Management |
| Build System | Turborepo | ^1.10.0 | Task Orchestration |
| Code Quality | ESLint | ^8.50.0 | Linting |
| Code Quality | Prettier | ^3.0.0 | Code Formatting |
| Git Hooks | Husky | ^8.0.0 | Git Hook Management |
| Commit Validation | Commitlint | ^17.0.0 | Commit Message Validation |
| Release | Semantic Release | ^21.0.0 | Automated Versioning |
3. Project Structure
3.1 Monorepo Directory Structure
monorepo-dashboard/
├── .github/ # GitHub workflows and templates
│ ├── workflows/
│ │ ├── ci.yml # Continuous Integration
│ │ ├── release.yml # Automated releases
│ │ └── codeql.yml # Security scanning
│ └── ISSUE_TEMPLATE/ # Issue templates
├── .husky/ # Git hooks
│ ├── pre-commit # Pre-commit hook
│ ├── commit-msg # Commit message validation
│ └── pre-push # Pre-push validation
├── apps/ # Applications
│ └── dashboard/ # Main dashboard application
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── pages/ # Page components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── services/ # API services
│ │ ├── stores/ # State management
│ │ ├── types/ # TypeScript types
│ │ └── utils/ # Utility functions
│ ├── public/ # Static assets
│ ├── package.json
│ ├── vite.config.ts # Vite configuration
│ └── tsconfig.json # TypeScript config
├── packages/ # Shared packages
│ ├── server/ # Backend server package
│ │ ├── src/
│ │ │ ├── routes/ # Express routes
│ │ │ ├── services/ # Business logic services
│ │ │ ├── utils/ # Server utilities
│ │ │ ├── types/ # Shared types
│ │ │ └── integrations/ # CI/CD integrations
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── shared/ # Shared utilities and types
│ │ ├── src/
│ │ │ ├── types/ # Common TypeScript types
│ │ │ ├── constants/ # Application constants
│ │ │ └── utils/ # Shared utility functions
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── eslint-config/ # Shared ESLint configuration
│ │ ├── index.js # ESLint rules
│ │ └── package.json
│ └── tsconfig/ # Shared TypeScript configurations
│ ├── base.json # Base TS config
│ ├── react.json # React-specific config
│ ├── node.json # Node.js config
│ └── package.json
├── tools/ # Development tools and scripts
│ ├── scripts/ # Build and deployment scripts
│ ├── templates/ # Code generation templates
│ └── configs/ # Tool configurations
├── docs/ # Documentation
│ ├── api/ # API documentation
│ ├── guides/ # User guides
│ └── development/ # Development docs
├── .gitignore # Git ignore rules
├── .prettierrc # Prettier configuration
├── .nvmrc # Node.js version
├── commitlint.config.js # Commitlint configuration
├── package.json # Root package.json
├── pnpm-workspace.yaml # PNPM workspace configuration
├── turbo.json # Turborepo configuration
├── README.md # Project documentation
└── LICENSE # License file
3.2 Package Configuration Files
3.2.1 Root package.json
{
"name": "@org/monorepo-dashboard",
"version": "1.0.0",
"description": "Self-hosted monorepo package manager dashboard",
"private": true,
"packageManager": "pnpm@8.10.0",
"engines": {
"node": ">=18.0.0",
"pnpm": ">=8.0.0"
},
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"prepare": "husky install",
"build": "turbo run build",
"dev": "turbo run dev --parallel",
"lint": "turbo run lint",
"lint:fix": "turbo run lint:fix",
"type-check": "turbo run type-check",
"test": "turbo run test",
"clean": "turbo run clean && rm -rf node_modules/.cache",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
"release": "semantic-release",
"changeset": "changeset",
"version-packages": "changeset version",
"publish-packages": "changeset publish"
},
"devDependencies": {
"@changesets/cli": "^2.26.2",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@org/eslint-config": "workspace:*",
"@org/tsconfig": "workspace:*",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@types/node": "^20.5.0",
"eslint": "^8.50.0",
"husky": "^8.0.3",
"lint-staged": "^14.0.1",
"prettier": "^3.0.3",
"semantic-release": "^21.1.1",
"semantic-release-monorepo": "^7.0.5",
"turbo": "^1.10.14",
"typescript": "^5.2.2"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yaml,yml}": [
"prettier --write"
]
}
}3.2.2 pnpm-workspace.yaml
packages:
# Applications
- "apps/*"
# Shared packages and libraries
- "packages/*"
# Exclude test directories and dist folders
- "!**/dist"
- "!**/node_modules"
- "!**/*.test.*"3.2.3 turbo.json[124][125][140][143][148]
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"//#format": {
"cache": false
},
"//#format:check": {
"cache": false
},
"build": {
"dependsOn": ["^build", "type-check"],
"outputs": ["dist/**", ".next/**", "build/**"],
"env": ["NODE_ENV"]
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
},
"dev": {
"cache": false,
"persistent": true,
"env": ["NODE_ENV", "PORT"]
},
"lint": {
"dependsOn": ["^topo"],
"outputs": []
},
"lint:fix": {
"cache": false,
"outputs": []
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
},
"clean": {
"cache": false
},
"release": {
"dependsOn": ["build", "test"],
"cache": false,
"env": ["GITHUB_TOKEN", "NPM_TOKEN"]
}
},
"remoteCache": {
"enabled": false
}
}4. Development Workflow Setup
4.1 Git Hooks Configuration (Husky)[125][128][130]
4.1.1 .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "🔍 Running pre-commit checks..."
# Run lint-staged
npx lint-staged
# Type checking
echo "📝 Type checking..."
pnpm turbo type-check
# Run tests for changed packages
echo "🧪 Running tests..."
pnpm turbo test --filter='...[HEAD~1]'
echo "✅ Pre-commit checks completed"4.1.2 .husky/commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "📝 Validating commit message..."
npx --no -- commitlint --edit $14.1.3 .husky/pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "🚀 Running pre-push checks..."
# Build all packages
echo "🔨 Building packages..."
pnpm turbo build
# Run full test suite
echo "🧪 Running full test suite..."
pnpm turbo test
# Check formatting
echo "💄 Checking code formatting..."
pnpm format:check
echo "✅ Pre-push checks completed"4.2 Commit Lint Configuration[125][130][133]
4.2.1 commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // New feature
'fix', // Bug fix
'docs', // Documentation changes
'style', // Code style changes (formatting, etc)
'refactor', // Code refactoring
'perf', // Performance improvements
'test', // Adding or updating tests
'chore', // Maintenance tasks
'ci', // CI/CD changes
'build', // Build system changes
'revert' // Reverting changes
]
],
'scope-enum': [
2,
'always',
[
'dashboard', // Dashboard app
'server', // Backend server
'shared', // Shared utilities
'config', // Configuration files
'deps', // Dependencies
'release', // Release related
'docs' // Documentation
]
],
'subject-max-length': [2, 'always', 100],
'subject-min-length': [2, 'always', 10],
'header-max-length': [2, 'always', 120]
}
};4.3 ESLint Configuration (Shared)[125]
4.3.1 packages/eslint-config/index.js
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'react', 'react-hooks'],
extends: [
'eslint:recommended',
'@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'prettier'
],
settings: {
react: {
version: 'detect'
}
},
env: {
browser: true,
node: true,
es2022: true
},
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
rules: {
// TypeScript specific
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
// React specific
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'react/no-unescaped-entities': 'off',
// General
'no-console': 'warn',
'prefer-const': 'error',
'no-var': 'error'
},
ignorePatterns: ['dist', 'node_modules', '*.config.js']
};4.4 Semantic Release Configuration[139][142][145][152]
4.4.1 .releaserc.json
{
"branches": ["main", "master"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
"@semantic-release/npm",
[
"@semantic-release/git",
{
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
"@semantic-release/github"
]
}5. Component Design
5.1 Frontend Architecture
5.1.1 React Component Structure
// apps/dashboard/src/components/PackageOverview/PackageCard.tsx
interface Package {
name: string;
version: string;
description: string;
type: 'app' | 'lib' | 'tool';
dependencies: string[];
devDependencies: string[];
maintainers: string[];
lastUpdated: Date;
buildStatus: 'success' | 'failed' | 'pending';
testCoverage: number;
}
interface PackageCardProps {
package: Package;
onSelect: (packageName: string) => void;
onViewDependencies: (packageName: string) => void;
}
export const PackageCard: React.FC<PackageCardProps> = ({
package: pkg,
onSelect,
onViewDependencies
}) => {
// Component implementation
};5.1.2 State Management (Zustand)
// apps/dashboard/src/stores/packageStore.ts
interface PackageState {
packages: Package[];
selectedPackage: Package | null;
dependencyGraph: DependencyNode[];
loading: boolean;
error: string | null;
// Actions
fetchPackages: () => Promise<void>;
selectPackage: (packageName: string) => void;
updatePackageStatus: (packageName: string, status: BuildStatus) => void;
refreshDependencyGraph: () => Promise<void>;
}
export const usePackageStore = create<PackageState>((set, get) => ({
packages: [],
selectedPackage: null,
dependencyGraph: [],
loading: false,
error: null,
fetchPackages: async () => {
// Implementation
},
selectPackage: (packageName: string) => {
// Implementation
}
}));5.2 Backend API Design
5.2.1 Express Route Structure
// packages/server/src/routes/packages.ts
import express from 'express';
import { PackageService } from '../services/PackageService';
const router = express.Router();
const packageService = new PackageService();
router.get('/packages', async (req, res) => {
try {
const packages = await packageService.getAllPackages();
res.json({ packages });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.get('/packages/:name/dependencies', async (req, res) => {
try {
const { name } = req.params;
const dependencies = await packageService.getDependencyGraph(name);
res.json({ dependencies });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
export { router as packagesRouter };5.2.2 Service Layer
// packages/server/src/services/PackageService.ts
export class PackageService {
private workspaceRoot: string;
private packageManager: 'pnpm' | 'npm' | 'yarn';
constructor() {
this.workspaceRoot = this.detectWorkspaceRoot();
this.packageManager = this.detectPackageManager();
}
async getAllPackages(): Promise<Package[]> {
const packagePaths = await this.findPackageDirectories();
const packages = await Promise.all(
packagePaths.map(path => this.parsePackage(path))
);
return packages;
}
async getDependencyGraph(packageName: string): Promise<DependencyNode[]> {
// Build dependency graph using package manager CLI
return this.buildDependencyGraph(packageName);
}
private detectWorkspaceRoot(): string {
// Implementation to find workspace root
}
private detectPackageManager(): 'pnpm' | 'npm' | 'yarn' {
// Implementation to detect package manager
}
}6. CI/CD Integration
6.1 GitHub Actions Configuration
6.1.1 .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v3
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint
run: pnpm turbo lint
- name: Type check
run: pnpm turbo type-check
- name: Test
run: pnpm turbo test --coverage
- name: Build
run: pnpm turbo build6.1.2 .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm turbo build
- name: Run tests
run: pnpm turbo test
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: pnpm semantic-release7. Package Distribution
7.1 NPM Package Configuration
7.1.1 Main Package package.json (for distribution)
{
"name": "@org/monorepo-dashboard",
"version": "1.0.0",
"description": "Self-hosted monorepo package manager dashboard",
"keywords": ["monorepo", "dashboard", "pnpm", "turborepo", "nx"],
"homepage": "https://github.com/org/monorepo-dashboard",
"bugs": "https://github.com/org/monorepo-dashboard/issues",
"license": "MIT",
"author": "Your Organization",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
"monorepo-dashboard": "./bin/cli.js"
},
"files": [
"dist",
"bin",
"README.md",
"LICENSE"
],
"scripts": {
"start": "node dist/server.js",
"postinstall": "node dist/postinstall.js"
},
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"@types/node": ">=18.0.0"
},
"dependencies": {
"express": "^4.18.0",
"ws": "^8.0.0",
"chokidar": "^3.5.0"
}
}7.1.2 CLI Entry Point
#!/usr/bin/env node
# bin/cli.js
const { spawn } = require('child_process');
const path = require('path');
// Parse command line arguments
const args = process.argv.slice(2);
const command = args[0] || 'dev';
switch (command) {
case 'dev':
case 'start':
const serverPath = path.join(__dirname, '..', 'dist', 'server.js');
const server = spawn('node', [serverPath, ...args.slice(1)], {
stdio: 'inherit'
});
server.on('exit', (code) => {
process.exit(code);
});
break;
default:
console.error(`Unknown command: ${command}`);
console.log('Available commands: dev, start');
process.exit(1);
}8. Testing Strategy
8.1 Testing Configuration
8.1.1 Vitest Configuration
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'clover', 'json'],
exclude: [
'node_modules/',
'dist/',
'coverage/',
'**/*.d.ts',
'**/*.config.*',
'**/test/**'
]
}
}
});8.2 Test Structure
tests/
├── unit/ # Unit tests
│ ├── components/ # Component tests
│ ├── services/ # Service layer tests
│ └── utils/ # Utility function tests
├── integration/ # Integration tests
│ ├── api/ # API endpoint tests
│ └── workflows/ # End-to-end workflow tests
├── fixtures/ # Test data and mocks
├── helpers/ # Test utilities
└── setup.ts # Test setup configuration
9. Performance Considerations
9.1 Build Optimization
9.1.1 Vite Configuration for Production
// apps/dashboard/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
target: 'es2020',
minify: 'esbuild',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
charts: ['cytoscape'],
utils: ['lodash-es']
}
}
}
},
server: {
port: 3000,
host: true
}
});9.2 Caching Strategy
- Turborepo cache for build artifacts
- Browser caching for static assets
- API response caching for package metadata
- WebSocket connection management for real-time updates
10. Security Considerations
10.1 API Security
- Input validation on all endpoints
- Rate limiting for API requests
- Secure token storage for CI/CD integrations
- CORS configuration for cross-origin requests
10.2 Access Control
- Role-based permissions for package actions
- Audit logging for sensitive operations
- Secure credential management
- Environment variable validation
11. Documentation Requirements
11.1 User Documentation
- Installation and setup guide
- Configuration reference
- API documentation
- Troubleshooting guide
11.2 Developer Documentation
- Architecture overview
- Contributing guidelines
- Development setup
- Testing procedures
12. Deployment and Distribution
12.1 Package Publishing
- Automated publishing via semantic-release
- Changelog generation from conventional commits
- Version management across monorepo packages
- Distribution via NPM registry
12.2 Installation Process
npm install -g @org/monorepo-dashboard- Navigate to monorepo root
- Run
monorepo-dashboard dev - Dashboard opens automatically in browser
This comprehensive SDS provides the complete technical blueprint for implementing the self-hosted monorepo package manager dashboard with modern tooling, automated workflows, and production-ready configuration[68][85][126][131].