OSGi-Style Framework for TypeScript
npm install @pandino/root


> A lightweight TypeScript framework that brings modular architecture to your applications. Build loosely-coupled, maintainable applications where different parts can communicate without knowing about each other directly.
Traditional DI vs Pandino Service Registry:
| Traditional DI | Pandino Service Registry |
|----------------|-------------------------|
| Static dependency injection | Dynamic service discovery |
| Compile-time wiring | Runtime service resolution |
| Hard-coded dependencies | LDAP-filtered service selection |
| Single service per interface | Multiple ranked services |
| Manual lifecycle management | Automatic service lifecycle |
| Feature | What it Solves | Benefit |
|---------|----------------|---------|
| 🔌 Service Registry | Hard-coded dependencies between modules | Services discover each other dynamically |
| 📦 Bundle System | Monolithic application architecture | Modular containers with independent lifecycles |
| 🔄 Dynamic Dependencies | Startup order dependencies | Bundles start in any order, dependencies resolve automatically |
| 📡 Event System | Tight coupling between modules | Publish-subscribe messaging with topic-based routing |
| ⚙️ Configuration Management | Static application configuration | Runtime configuration updates without restarts |
| 🏗️ Declarative Services | Complex service wiring boilerplate | Decorator-based dependency injection |
| ⚛️ React Integration | Framework complexity in React apps | Hook-based service discovery in components |
| 📦 Rollup Bundle Plugin | Automated bundling of modules | Simplifies and automates the bundling process |
``typescript
import { Component, Service, Reference, Activate } from '@pandino/pandino';
import type { ComponentContext } from '@pandino/pandino';
// 1. Define service interfaces
interface UserService {
findUser(id: string): User | null;
createUser(data: UserData): User;
}
interface NotificationService {
notify(message: string): void;
}
// 2. Create service implementations with SCR decorators
@Component({ name: 'user.service', immediate: true })
@Service({ interfaces: ['UserService'] })
class UserServiceImpl implements UserService {
private users = new Map
@Activate
activate(context: ComponentContext): void {
console.log('UserService activated');
}
findUser(id: string): User | null {
return this.users.get(id) || null;
}
createUser(data: UserData): User {
const user = new User(data);
this.users.set(user.id, user);
return user;
}
}
@Component({ name: 'notification.service', immediate: true })
@Service({ interfaces: ['NotificationService'] })
class NotificationServiceImpl implements NotificationService {
@Activate
activate(): void {
console.log('NotificationService activated');
}
notify(message: string): void {
console.log(📧 Notification: ${message});
}
}
@Component({ name: 'order.service' })
@Service({ interfaces: ['OrderService'] })
class OrderService {
// Services are injected automatically when available
@Reference({ interface: 'UserService' })
private userService?: UserService;
@Reference({ interface: 'NotificationService' })
private notificationService?: NotificationService;
@Activate
activate(): void {
console.log('OrderService activated with dependencies');
}
async createOrder(userId: string, items: Item[]): Promise
// Dependencies resolved automatically - no imports needed!
const user = this.userService?.findUser(userId);
if (!user) throw new Error('User not found');
const order = new Order(user, items);
this.notificationService?.notify(Order confirmed for ${user.email});
return order;
}
}
// 3. Register components with SCR
const scr = context.getService(context.getServiceReference('ServiceComponentRuntime')!);
const bundleId = context.getBundle().getBundleId();
await scr.registerComponent(UserServiceImpl, bundleId);
await scr.registerComponent(NotificationServiceImpl, bundleId);
await scr.registerComponent(OrderService, bundleId);
`
The Magic: All three services discover each other automatically. OrderService gets both dependencies injected without knowing how they're implemented!
| Package | Purpose | Documentation |
|---------|---------|---------------|
| @pandino/pandino | Core framework with service registry, bundles, and built-in services | Core Documentation |
| @pandino/react-hooks | React integration with hooks and components | React Documentation |
`typescript
// Register with metadata
context.registerService('DatabaseService', new MySQLService(), {
'db.type': 'mysql',
'service.ranking': 100
});
// Discover by capabilities
const dbRefs = context.getServiceReferences('DatabaseService', '(db.type=mysql)');
`
`typescript`
// Each bundle manages its own services
const databaseBundle = {
activator: {
start(context) { / register database services / },
stop(context) { / cleanup / }
}
};
`typescript`
await apiBundle.start(); // ✅ Starts immediately
await databaseBundle.start(); // ✅ API bundle automatically gets database service
→ Extender Pattern Documentation
→ Whiteboard Pattern Documentation
→ Fragment Pattern Documentation
| Scenario | Traditional Approach | Pandino Approach |
|----------|---------------------|------------------|
| Microservices Architecture | Hard-coded service URLs | Dynamic service discovery |
| Plugin Systems | Manual plugin loading | Bundle-based plugins with auto-discovery |
| Feature Flags | Code-level toggles | Service-level feature activation |
| Multi-tenant Apps | Complex configuration management | Service filtering by tenant properties |
| A/B Testing | Conditional code blocks | Multiple service implementations with ranking |
Choose your integration approach:
bash
npm install @pandino/pandino
`
→ Core Framework Guide$3
`bash
npm install @pandino/pandino @pandino/react-hooks
`
→ React Integration Guide$3
`bash
npm install -D @pandino/rollup-bundle-plugin
``We welcome contributions! Please see our Contributing Guide for details.
Eclipse Public License - v 2.0