Added logger object to global NS
Some checks failed
armco-org/node-starter-kit/pipeline/head There was a failure building this commit

This commit is contained in:
2025-12-07 16:47:01 +05:30
parent f96b398cff
commit 37dfee5877
18 changed files with 2037 additions and 1 deletions

View File

@@ -0,0 +1,344 @@
# Config-Driven Initialization - Breaking Change Summary
## What Changed
NSK v2 now supports **config-driven initialization** where `Application.create().build()` automatically loads and instantiates plugins from `armcorc.json` configuration.
## Problem Solved
### Before
- Config files only contained plugin **settings**
- You still had to **manually register** plugin instances in code:
```typescript
App.create(app)
.plugin(createLoggerPlugin()) // Code change required
.plugin(createDatabasePlugin()) // Code change required
.build()
```
- Changing plugins required **code changes + rebuild + redeploy**
### After
- Config file is the **single source of truth**
- `Application.create().build()` auto-loads plugins from config
- Changing plugins only requires **restart** (no rebuild)
## How It Works
### 1. Plugin Factory Registry
When you import `@armco/node-starter-kit`, all built-in plugins automatically register their factory functions:
```typescript
// Happens automatically when you import NSK
registerPluginFactory('logger', (config) => createLoggerPlugin(config))
registerPluginFactory('database', (config) => createDatabasePlugin(config))
registerPluginFactory('cache', (config) => createCachePlugin(config))
// etc...
```
### 2. Hybrid Loading Logic (Config + Manual)
```typescript
// In Application.build():
// 1. Config is PRIMARY source of truth
// 2. Manual registration OVERRIDES specific plugins
if (config has plugins) {
// Auto-load plugins from config
for (pluginName, pluginConfig in config.plugins) {
if (pluginName NOT manually registered && enabled !== false) {
const factory = pluginRegistry.get(pluginName)
const plugin = factory(pluginConfig)
application.plugin(plugin)
}
}
// Add manually registered plugins (these override config)
for (manualPlugin in manualPlugins) {
application.plugin(manualPlugin)
}
}
```
**Key Behavior:**
- Config defines **what** plugins are enabled
- Manual registration **overrides** specific plugin instances
- Manually registered plugins should still be declared in config (for consistency)
## Usage
### Approach 1: Pure Config-Driven (Recommended)
**server.ts:**
```typescript
import express from 'express'
import { Application } from '@armco/node-starter-kit'
const app = express()
const nsk = await Application.create(app).build() // That's it!
app.listen(3000)
```
**armcorc.json:**
```json
{
"appName": "auth-core",
"plugins": {
"logger": { "enabled": true, "level": "info" },
"database": { "enabled": true, "uri": "mongodb://localhost/authdb" },
"cache": { "enabled": true, "adapter": "redis" }
}
}
```
**To change config:**
1. Edit `armcorc.json`
2. Restart the app
3. Done! (no rebuild needed)
### Approach 2: Programmatic Config (Cloud Config)
```typescript
import { Application } from '@armco/node-starter-kit'
// Load from config server, K8s ConfigMap, env vars, etc.
const config = {
appName: 'auth-core',
plugins: {
logger: {
enabled: true,
level: process.env.LOG_LEVEL || 'info'
},
database: {
enabled: true,
uri: process.env.MONGODB_URI
},
},
}
const nsk = await Application.create(app)
.withConfig(config)
.build()
```
### Approach 3: Hybrid (RECOMMENDED for most cases)
Config is primary source of truth, manual registration for overrides:
**armcorc.json:**
```json
{
"appName": "auth-core",
"plugins": {
"logger": { "enabled": true, "level": "info" },
"database": { "enabled": true, "uri": "mongodb://..." },
"cache": { "enabled": true },
"socket": { "enabled": true }
}
}
```
**server.ts:**
```typescript
import http from 'http'
import { Application, createSocketPlugin } from '@armco/node-starter-kit'
const app = express()
const server = http.createServer(app)
const nsk = await Application.create(app)
// Config auto-loads: logger, database, cache
// Manual registration for socket (requires server instance)
.plugin(createSocketPlugin(server))
.build()
server.listen(3000)
```
**Benefits:**
- Config declares all plugins (single source of truth)
- Code only handles runtime requirements (e.g., HttpServer for socket)
- Easy restart-based reconfiguration for most plugins
### Approach 4: Pure Manual (Still Supported)
```typescript
import { Application, createLoggerPlugin } from '@armco/node-starter-kit'
// Explicit plugin registration (old way, still works)
const nsk = await Application.create(app)
.plugin(createLoggerPlugin({ level: 'info' }))
.plugin(createDatabasePlugin({ uri: 'mongodb://...' }))
.build()
```
## Supported Plugins
These plugins can be auto-loaded from config:
| Plugin Name | Config Key | Notes |
|-------------|------------|-------|
| Logger | `logger` | ✅ Fully supported |
| Database | `database` | ✅ Fully supported |
| Cache | `cache` | ✅ Fully supported |
| Scheduler | `scheduler` | ✅ Fully supported |
| Telemetry | `telemetry` or `opentelemetry` | ✅ Fully supported |
| Socket.IO | `socket` | ⚠️ **Requires manual registration** (needs `HttpServer` instance) |
### Socket.IO Special Case
Socket.IO plugin requires an `HttpServer` instance, which cannot be provided via JSON config:
```typescript
import http from 'http'
import { Application, createSocketPlugin } from '@armco/node-starter-kit'
const app = express()
const server = http.createServer(app)
const nsk = await Application.create(app)
// Auto-load other plugins from config
.withConfig('./armcorc.json')
// Manually add socket (requires server instance)
.plugin(createSocketPlugin(server, { cors: { origin: '*' } }))
.build()
server.listen(3000)
```
## Configuration Examples
### Minimal Config
```json
{
"appName": "my-service",
"plugins": {
"logger": { "enabled": true }
}
}
```
### Production Config
```json
{
"appName": "auth-core",
"env": "production",
"plugins": {
"logger": {
"enabled": true,
"level": "info",
"format": "json"
},
"database": {
"enabled": true,
"adapter": "mongoose",
"uri": "mongodb://mongo:27017/authdb",
"options": {
"maxPoolSize": 10
}
},
"cache": {
"enabled": true,
"adapter": "redis",
"uri": "redis://redis:6379",
"ttl": 3600,
"keyPrefix": "auth:"
},
"telemetry": {
"enabled": true,
"serviceName": "auth-core",
"serviceVersion": "1.0.0",
"otlpEndpoint": "http://tempo:4318",
"exporters": ["otlp"]
}
}
}
```
### Disable a Plugin
```json
{
"appName": "my-service",
"plugins": {
"logger": { "enabled": true },
"cache": { "enabled": false } // Disabled
}
}
```
## Migration Path
### If You're Happy with Current Approach
**No changes needed!** Manual plugin registration still works:
```typescript
const nsk = await Application.create(app)
.plugin(createLoggerPlugin())
.build()
```
### If You Want Config-Driven
1. **Create `armcorc.json`** with your plugin configs
2. **Remove manual `.plugin()` calls**
3. **Simplify to** `Application.create(app).build()`
## Benefits
1. **Restart-based reconfiguration**: No rebuild or redeploy needed
2. **GitOps friendly**: Config changes tracked in version control
3. **Cloud config integration**: Load from K8s ConfigMaps, AWS Parameter Store, etc.
4. **Environment-specific**: Easy to swap configs per environment
5. **Cleaner code**: Less boilerplate in application code
## Runtime Reconfiguration Workflow
```bash
# 1. Edit config
vim armcorc.json
# 2. Commit (optional, for GitOps)
git commit -am "Enable cache plugin"
# 3. Restart app (PM2, K8s, systemd, etc.)
pm2 reload my-service
# or
kubectl rollout restart deployment/my-service
# 4. New config is active (no rebuild!)
```
## Implementation Details
### Files Changed/Added
**New:**
- `v2/core/PluginFactory.ts` - Plugin factory registry
- `v2/examples/config-driven-initialization.ts` - Usage examples
- `v2/examples/armco.config.full.json` - Full config reference
- `v2/CONFIG_DRIVEN_INITIALIZATION.md` - Comprehensive docs
**Modified:**
- `v2/core/Application.ts` - Auto-load plugins from config
- `v2/index.ts` - Export PluginFactory
- All plugin `index.ts` files - Register factories on import
### Type Safety
Config is still validated using Zod schemas, ensuring type safety at runtime.
## Questions?
- See `/v2/examples/config-driven-initialization.ts` for complete examples
- See `/v2/CONFIG_DRIVEN_INITIALIZATION.md` for detailed documentation
- See `/v2/examples/armco.config.full.json` for full config reference
## Summary
**Backward compatible** - Old approach still works
**Opt-in** - Use config-driven only if you want
**Restart-based reconfiguration** - No rebuild needed
**Cloud-native** - Integrates with config management systems
**Type-safe** - Still validated with Zod schemas

View File

@@ -98,6 +98,11 @@ import pkg from "./package.json";
fs.copyFileSync("./LICENSE", "./dist/LICENSE");
}
// Copy globals.d.ts for TypeScript type augmentation
if (fs.existsSync("./v2/globals.d.ts")) {
fs.copyFileSync("./v2/globals.d.ts", "./dist/globals.d.ts");
}
logger.info('✅ Build completed successfully!');
logger.info('📦 Package ready in ./dist folder');
} catch (err) {

View File

@@ -0,0 +1,372 @@
# Config-Driven Initialization
NSK v2 now supports **config-driven initialization**, enabling restart-based reconfiguration without code changes or rebuilds. This document explains how it works and how to use it.
## Overview
### Previous Approach (Still Supported)
```typescript
import { Application, createLoggerPlugin, createDatabasePlugin } from '@armco/node-starter-kit'
const nsk = await Application.create(app)
.plugin(createLoggerPlugin({ level: 'info' }))
.plugin(createDatabasePlugin({ uri: process.env.MONGODB_URI }))
.build()
```
### New Config-Driven Approach
```typescript
import { Application } from '@armco/node-starter-kit'
// Just this - plugins auto-loaded from armcorc.json
const nsk = await Application.create(app).build()
```
**armcorc.json:**
```json
{
"appName": "my-service",
"plugins": {
"logger": { "enabled": true, "level": "info" },
"database": { "enabled": true, "uri": "mongodb://localhost/mydb" }
}
}
```
## Benefits
1. **Restart-based reconfiguration**: Change config → restart app → changes applied (no rebuild)
2. **Single source of truth**: All configuration in one place (JSON/YAML)
3. **Cloud config integration**: Load from config server, env vars, K8s ConfigMaps, etc.
4. **Environment-specific configs**: Easy to swap configs per environment
5. **GitOps friendly**: Config changes tracked in version control
## How It Works
### Plugin Factory Registry
NSK maintains a global registry that maps plugin names to factory functions:
```typescript
// Internally, when you import @armco/node-starter-kit:
registerPluginFactory('logger', (config) => createLoggerPlugin(config))
registerPluginFactory('database', (config) => createDatabasePlugin(config))
registerPluginFactory('cache', (config) => createCachePlugin(config))
// ... etc
```
### Auto-Loading Process
When `Application.create(app).build()` is called **without** explicit plugins:
1. NSK searches for `armcorc.json` (or `.armcorc`, `armco.config.js`, etc.)
2. Reads the `plugins` section
3. For each enabled plugin, looks up its factory in the registry
4. Instantiates the plugin with its config
5. Registers and starts all plugins
## Usage Patterns
### Pattern 1: Pure Config-Driven (Recommended)
**server.ts:**
```typescript
import express from 'express'
import { Application } from '@armco/node-starter-kit'
const app = express()
// All plugins loaded from armcorc.json
const nsk = await Application.create(app).build()
app.listen(3000)
```
**armcorc.json:**
```json
{
"appName": "auth-core",
"plugins": {
"logger": {
"enabled": true,
"level": "info",
"format": "json"
},
"database": {
"enabled": true,
"adapter": "mongoose",
"uri": "mongodb://localhost:27017/authdb"
},
"cache": {
"enabled": true,
"adapter": "redis",
"uri": "redis://localhost:6379"
}
}
}
```
### Pattern 2: Cloud Config Integration
```typescript
import { Application } from '@armco/node-starter-kit'
// Load from cloud config store
const config = await fetchConfigFromCloudStore()
const nsk = await Application.create(app)
.withConfig(config)
.build()
```
### Pattern 3: Environment Variables
```typescript
import { Application } from '@armco/node-starter-kit'
const config = {
appName: 'auth-core',
plugins: {
logger: {
enabled: true,
level: process.env.LOG_LEVEL || 'info',
},
database: {
enabled: true,
uri: process.env.MONGODB_URI,
},
},
}
const nsk = await Application.create(app)
.withConfig(config)
.build()
```
### Pattern 4: Mixed (Config + Manual)
Use config for standard plugins, manual registration for special cases:
```typescript
import { Application } from '@armco/node-starter-kit'
import http from 'http'
const app = express()
const server = http.createServer(app)
const nsk = await Application.create(app)
// Auto-load plugins from config
.withConfig('./armcorc.json')
// Manually add socket (requires server instance)
.plugin(createSocketPlugin(server))
.build()
```
## Configuration Reference
### Config File Locations
NSK searches for configuration in this order:
1. `armco.config.js` (TypeScript: `armco.config.ts`)
2. `armco.config.json`
3. `.armcorc`
4. `.armcorc.json`
5. `.armcorc.js`
6. `armcorc.json`
7. `package.json` (under `"armco"` key)
### Plugin Configuration Schema
Each plugin in `plugins` object follows this structure:
```json
{
"plugins": {
"<plugin-name>": {
"enabled": true, // Set to false to disable
"priority": 10, // Load order (optional)
"<plugin-specific-config>": "..."
}
}
}
```
### Supported Plugin Keys
| Key | Plugin | Config Type |
|-----|--------|-------------|
| `logger` | Logger | `LoggerConfig` |
| `database` | Database | `DatabaseConfig` |
| `cache` | Cache | `CacheConfig` |
| `scheduler` | Scheduler | `SchedulerConfig` |
| `telemetry` or `opentelemetry` | OpenTelemetry | `OpenTelemetryConfig` |
**Note**: `socket` plugin cannot be auto-loaded (requires `HttpServer` instance).
## Migration Guide
### If You're Using Manual Plugin Registration
**Before:**
```typescript
const nsk = await Application.create(app)
.plugin(createLoggerPlugin({ level: 'info' }))
.plugin(createDatabasePlugin({ uri: 'mongodb://...' }))
.build()
```
**After (Option 1 - Config File):**
Create `armcorc.json`:
```json
{
"appName": "my-service",
"plugins": {
"logger": { "enabled": true, "level": "info" },
"database": { "enabled": true, "uri": "mongodb://..." }
}
}
```
Update code:
```typescript
const nsk = await Application.create(app).build()
```
**After (Option 2 - Inline Config):**
```typescript
const nsk = await Application.create(app)
.withConfig({
appName: 'my-service',
plugins: {
logger: { enabled: true, level: 'info' },
database: { enabled: true, uri: 'mongodb://...' },
},
})
.build()
```
## Runtime Reconfiguration
### Pattern: Graceful Reload
1. **Update config file** (manually, API, or config management tool)
2. **Send SIGTERM** to the process
3. **Process manager restarts** the app (PM2, K8s, systemd, etc.)
4. **NSK loads new config** on startup
```bash
# Example with PM2
vim armcorc.json # Edit config
pm2 reload my-service # Graceful reload
```
### Pattern: Hot Reload (Advanced)
For hot reload without restart, you'll need custom logic:
```typescript
import { watch } from 'fs'
import { Application } from '@armco/node-starter-kit'
let nsk: Application
async function loadApp() {
if (nsk) {
await nsk.shutdown()
}
nsk = await Application.create(app).build()
}
// Initial load
await loadApp()
// Watch for config changes
watch('./armcorc.json', async () => {
console.log('Config changed, reloading...')
await loadApp()
})
```
## Custom Plugins
To make your custom plugin auto-loadable:
```typescript
import { registerPluginFactory, type PluginFactory } from '@armco/node-starter-kit'
export class MyCustomPlugin extends BasePlugin<MyConfig> {
// ... implementation
}
export function createMyPlugin(config: MyConfig) {
return new MyCustomPlugin(config)
}
// Register factory
registerPluginFactory('mycustom', (config) =>
createMyPlugin(config as unknown as MyConfig)
)
```
Then use in config:
```json
{
"plugins": {
"mycustom": {
"enabled": true,
"myOption": "value"
}
}
}
```
## Best Practices
1. **Use config files for production**: More maintainable than inline configs
2. **Environment-specific configs**: Use separate files (`armcorc.prod.json`, `armcorc.dev.json`)
3. **Sensitive data**: Use env vars, not config files:
```typescript
const config = await ConfigLoader.load()
config.plugins.database.uri = process.env.MONGODB_URI
```
4. **Validate configs**: NSK uses Zod for validation, but add your own checks
5. **Version control**: Commit configs (except secrets) for GitOps workflows
6. **Documentation**: Document your config schema for your team
## Troubleshooting
### "Plugin factory not found"
**Cause**: Plugin not imported, so factory wasn't registered.
**Solution**: Import the main package:
```typescript
import '@armco/node-starter-kit' // Triggers factory registration
```
Or import specific plugin:
```typescript
import '@armco/node-starter-kit/plugins/logger'
```
### Config not loading
**Cause**: Config file not found or invalid.
**Solution**: Check file name and location. Enable debug:
```typescript
const loader = new ConfigLoader()
const config = loader.load()
console.log('Loaded config:', config)
```
### Plugin not initializing
**Cause**: Missing required config fields or `enabled: false`.
**Solution**: Check plugin config requirements in type definitions.
## Examples
See `/v2/examples/config-driven-initialization.ts` for complete examples.

370
v2/GLOBAL_LOGGER.md Normal file
View File

@@ -0,0 +1,370 @@
# Global Logger Access
NSK v2 provides first-class global access to the logger, matching the legacy NSK v1 behavior. Once NSK is initialized with the logger plugin, the logger is automatically available throughout your application **without requiring imports**.
## How It Works
### 1. Automatic Global Injection
When the logger plugin is installed, NSK automatically makes it available on the global object:
```typescript
// In your server.ts
import { Application } from '@armco/node-starter-kit'
const nsk = await Application.create(app)
.withConfig({
appName: 'my-service',
plugins: {
logger: { enabled: true, level: 'info' }
}
})
.build()
// Logger is now globally available!
logger.info('Application started')
```
### 2. TypeScript Type Safety
NSK exports a global namespace augmentation (`globals.d.ts`) that provides full TypeScript type safety for the global logger:
```typescript
// globals.d.ts (automatically included when you install NSK)
declare global {
var logger: Logger
}
```
**No configuration needed in your project!** Just install NSK and TypeScript will automatically recognize the `logger` global.
## Usage
### In Route Handlers
```typescript
app.get('/api/users/:id', (req, res) => {
// No import needed!
logger.info('User request received', { userId: req.params.id })
try {
const user = getUserById(req.params.id)
logger.info('User found', { user })
res.json(user)
} catch (error) {
logger.error('Failed to fetch user', { error })
res.status(500).json({ error: 'Internal server error' })
}
})
```
### In Service Classes
```typescript
class UserService {
async createUser(data: any) {
// Logger is globally available
logger.info('Creating user', { email: data.email })
try {
const user = await db.users.create(data)
logger.info('User created', { userId: user.id })
return user
} catch (error) {
logger.error('Failed to create user', { error })
throw error
}
}
}
```
### In Middleware
```typescript
function requestLogger(req, res, next) {
const start = Date.now()
logger.info('Incoming request', {
method: req.method,
path: req.path,
ip: req.ip,
})
res.on('finish', () => {
const duration = Date.now() - start
logger.info('Request completed', {
method: req.method,
path: req.path,
statusCode: res.statusCode,
duration: `${duration}ms`,
})
})
next()
}
```
### In Error Handlers
```typescript
function errorHandler(err, req, res, next) {
logger.error('Unhandled error', {
error: err.message,
stack: err.stack,
path: req.path,
})
res.status(500).json({ error: 'Internal server error' })
}
```
### In Background Jobs
```typescript
async function processQueue() {
logger.info('Processing queue')
while (true) {
const job = await queue.getNext()
if (!job) {
logger.debug('No jobs in queue')
await sleep(1000)
continue
}
try {
logger.info('Processing job', { jobId: job.id })
await processJob(job)
logger.info('Job completed', { jobId: job.id })
} catch (error) {
logger.error('Job failed', { jobId: job.id, error })
}
}
}
```
## Benefits
### 1. No Import Boilerplate
**Before (with imports):**
```typescript
import { logger } from './logger' // Repeated in every file
import { logger } from '../utils/logger' // Different paths
import { getLogger } from '@armco/node-starter-kit' // Different ways
```
**After (global):**
```typescript
// No imports needed - logger is just available!
logger.info('Request received')
```
### 2. Consistency
Every file uses the same logger instance automatically:
- No confusion about which logger to import
- No risk of multiple logger instances
- Configuration changes apply everywhere instantly
### 3. Cleaner Code
```typescript
// ❌ With imports (verbose)
import express from 'express'
import { logger } from '../utils/logger'
function handler(req, res) {
logger.info('Processing request')
// ...
}
// ✅ With global (clean)
import express from 'express'
function handler(req, res) {
logger.info('Processing request')
// ...
}
```
### 4. Migration Compatibility
Matches legacy NSK v1 behavior - existing code works without changes!
## TypeScript Configuration
### No Configuration Needed! ✅
When you install `@armco/node-starter-kit`, TypeScript automatically recognizes the global logger through the exported `globals.d.ts` file.
### How It Works
1. NSK exports `globals.d.ts` in its package
2. TypeScript automatically picks it up via the triple-slash reference in `index.ts`:
```typescript
/// <reference path="./globals.d.ts" />
```
3. Your project gets full type safety without any configuration!
### If You Need Manual Configuration (Rare)
In very rare cases, if TypeScript doesn't pick up the types automatically, you can add this to your `tsconfig.json`:
```json
{
"compilerOptions": {
"types": ["@armco/node-starter-kit"]
}
}
```
But this is **usually not necessary** - the types are included automatically.
## Best Practices
### 1. Initialize NSK Early
Ensure NSK is initialized before using the logger:
```typescript
// server.ts
import { Application } from '@armco/node-starter-kit'
async function main() {
// Initialize NSK first
const nsk = await Application.create(app)
.withConfig('./armcorc.json')
.build()
// Now logger is available
logger.info('Application initialized')
// Import other modules (they can now use logger)
const { setupRoutes } = await import('./routes')
setupRoutes(app)
}
main()
```
### 2. Structured Logging
Use structured logging with context objects:
```typescript
// ✅ Good: Structured with context
logger.info('User login', {
userId: user.id,
email: user.email,
ip: req.ip,
timestamp: new Date().toISOString(),
})
// ❌ Less good: String concatenation
logger.info(`User ${user.id} logged in from ${req.ip}`)
```
### 3. Appropriate Log Levels
Use the right log level for each situation:
```typescript
logger.debug('Detailed debugging info') // Development only
logger.info('Normal operations') // General info
logger.warn('Something unusual') // Potential issues
logger.error('Something failed', { error }) // Errors
```
### 4. Error Logging
Always include error objects and context:
```typescript
try {
await riskyOperation()
} catch (error) {
logger.error('Operation failed', {
error, // Full error object
operation: 'riskyOperation',
userId: user.id,
context: { /* additional info */ }
})
}
```
## Comparison: Legacy vs V2
### Legacy NSK v1
**Required manual namespace declaration in host project:**
```typescript
// In your project's types/global.d.ts (had to create this)
declare global {
namespace NodeJS {
interface Global {
logger: any
}
}
var logger: any
}
```
### NSK v2
**No manual configuration needed!**
Just install NSK and the types are automatically available. The namespace augmentation is exported from NSK itself.
## Troubleshooting
### "Cannot find name 'logger'"
**Cause**: NSK not initialized yet or logger plugin not enabled.
**Solution**:
1. Ensure logger plugin is enabled in `armcorc.json`
2. Initialize NSK before using the logger
3. Check that NSK initialization completed successfully
### TypeScript error: "Property 'logger' does not exist"
**Cause**: TypeScript not picking up the global types (very rare).
**Solution**:
1. Restart your TypeScript language server
2. Check that `@armco/node-starter-kit` is installed
3. If still not working, add to `tsconfig.json`:
```json
{
"compilerOptions": {
"types": ["@armco/node-starter-kit"]
}
}
```
### Logger not available in some files
**Cause**: File is imported/executed before NSK initialization.
**Solution**: Ensure NSK is initialized before importing other application modules:
```typescript
// ✅ Good
async function main() {
await initNSK() // Initialize first
const routes = await import('./routes') // Then import
}
// ❌ Bad
import { routes } from './routes' // Imported before NSK init
async function main() {
await initNSK()
}
```
## Examples
See `/v2/examples/global-logger-usage.ts` for complete working examples.

View File

@@ -2,6 +2,7 @@ import { Application as ExpressApp } from 'express'
import { Container } from './Container'
import { PluginManager } from './PluginManager'
import { ConfigLoader } from './ConfigLoader'
import { pluginRegistry } from './PluginFactory'
import { ApplicationContext, AppConfig } from '../types/Context'
import { Plugin, PluginRegistration } from '../types/Plugin'
import { Logger } from '../types/Logger'
@@ -305,7 +306,46 @@ export class ApplicationBuilder {
}
// Register plugins
if (this.pluginRegistrations.length > 0) {
// Strategy: Config is primary source of truth, manual registration acts as override
if (finalConfig.plugins && Object.keys(finalConfig.plugins).length > 0) {
// Get plugin names that were manually registered
const manualPluginNames = new Set(
this.pluginRegistrations.map(p =>
typeof p === 'object' && 'plugin' in p ? p.plugin.name : (p as any).name
)
)
// Validate: Manually registered plugins should also be declared in config
// (This ensures config is the single source of truth for what's enabled)
for (const pluginName of manualPluginNames) {
if (!finalConfig.plugins[pluginName]) {
console.warn(
`[NSK] Warning: Plugin '${pluginName}' is manually registered but not declared in config. ` +
`Consider adding it to armcorc.json for consistency.`
)
}
}
// Auto-load plugins from config, excluding ones manually registered
const pluginsToAutoLoad: Record<string, any> = {}
for (const [name, config] of Object.entries(finalConfig.plugins)) {
if (!manualPluginNames.has(name)) {
pluginsToAutoLoad[name] = config
}
}
// Load non-overridden plugins from config
if (Object.keys(pluginsToAutoLoad).length > 0) {
const autoLoadedPlugins = pluginRegistry.createFromConfig(pluginsToAutoLoad)
application.plugins(autoLoadedPlugins)
}
// Add manually registered plugins (these override config)
if (this.pluginRegistrations.length > 0) {
application.plugins(this.pluginRegistrations)
}
} else if (this.pluginRegistrations.length > 0) {
// No config plugins, but manual plugins exist - use manual plugins
application.plugins(this.pluginRegistrations)
}

100
v2/core/PluginFactory.ts Normal file
View File

@@ -0,0 +1,100 @@
import { Plugin, PluginRegistration } from '../types/Plugin'
import { PluginConfig } from '../types/Context'
/**
* Plugin factory function type
* Takes plugin config and returns a plugin instance
*/
export type PluginFactory = (config?: PluginConfig) => Plugin | PluginRegistration
/**
* Global registry for plugin factories
* Maps plugin names (from config) to their factory functions
*/
class PluginFactoryRegistry {
private factories = new Map<string, PluginFactory>()
/**
* Register a plugin factory
* @param name - Plugin name as it appears in config (e.g., 'logger', 'database')
* @param factory - Factory function that creates the plugin
*/
register(name: string, factory: PluginFactory): void {
this.factories.set(name, factory)
}
/**
* Get a plugin factory by name
*/
get(name: string): PluginFactory | undefined {
return this.factories.get(name)
}
/**
* Check if a factory is registered
*/
has(name: string): boolean {
return this.factories.has(name)
}
/**
* Get all registered plugin names
*/
getRegisteredNames(): string[] {
return Array.from(this.factories.keys())
}
/**
* Create plugin instances from config
* @param pluginsConfig - Plugin configuration from AppConfig
* @returns Array of plugin instances
*/
createFromConfig(pluginsConfig: Record<string, PluginConfig>): Array<Plugin | PluginRegistration> {
const plugins: Array<Plugin | PluginRegistration> = []
for (const [name, config] of Object.entries(pluginsConfig)) {
// Skip if explicitly disabled
if (config.enabled === false) {
continue
}
const factory = this.get(name)
if (!factory) {
console.warn(`[NSK] Plugin factory not found for: ${name}. Skipping...`)
continue
}
try {
const plugin = factory(config)
plugins.push(plugin)
} catch (error) {
console.error(`[NSK] Failed to create plugin '${name}':`, error)
throw new Error(
`Plugin creation failed for '${name}': ${
error instanceof Error ? error.message : String(error)
}`
)
}
}
return plugins
}
}
/**
* Global plugin factory registry instance
*/
export const pluginRegistry = new PluginFactoryRegistry()
/**
* Register a plugin factory (convenience function)
*
* @example
* ```typescript
* // In your plugin file:
* registerPluginFactory('logger', (config) => createLoggerPlugin(config))
* ```
*/
export function registerPluginFactory(name: string, factory: PluginFactory): void {
pluginRegistry.register(name, factory)
}

View File

@@ -0,0 +1,72 @@
{
"appName": "my-service",
"env": "production",
"plugins": {
"logger": {
"enabled": true,
"adapter": "winston",
"level": "info",
"format": "json"
},
"database": {
"enabled": true,
"adapter": "mongoose",
"uri": "mongodb://localhost:27017/mydb",
"options": {
"maxPoolSize": 10,
"serverSelectionTimeoutMS": 5000
}
},
"cache": {
"enabled": true,
"adapter": "redis",
"uri": "redis://localhost:6379",
"ttl": 3600,
"keyPrefix": "myapp:"
},
"scheduler": {
"enabled": true,
"adapter": "node-cron",
"timezone": "UTC",
"tasks": []
},
"telemetry": {
"enabled": true,
"serviceName": "my-service",
"serviceVersion": "1.0.0",
"otlpEndpoint": "http://localhost:4318",
"exporters": ["otlp"],
"enableTracing": true,
"enableMetrics": true,
"autoInstrumentation": true,
"sampleRate": 1.0
}
},
"middlewares": {
"helmet": {
"enabled": true
},
"cors": {
"enabled": true,
"origin": "*"
},
"rateLimit": {
"enabled": true,
"windowMs": 900000,
"max": 100
}
},
"health": {
"enabled": true,
"endpoint": "/health"
},
"metrics": {
"enabled": true,
"endpoint": "/metrics",
"collector": "prometheus"
},
"server": {
"port": 3000,
"host": "0.0.0.0"
}
}

View File

@@ -0,0 +1,201 @@
/**
* Config-Driven Initialization Example
*
* This example demonstrates how to initialize NSK v2 purely from configuration
* without explicit plugin registration in code. This enables:
* - Restart-based reconfiguration (no rebuild required)
* - Config-as-code or cloud config integration
* - Uniform JSON input for all plugins
*/
import express from 'express'
import { Application } from '../core/Application'
// Import plugins to trigger factory registration
// This is required once at app entry point
import '../plugins/logger'
import '../plugins/database'
import '../plugins/cache'
import '../plugins/scheduler'
import '../plugins/telemetry'
/**
* Example 1: Minimal - Auto-load from armcorc.json
*
* If you have armcorc.json in your project root:
* {
* "appName": "my-service",
* "env": "production",
* "plugins": {
* "logger": {
* "enabled": true,
* "level": "info",
* "format": "json"
* },
* "database": {
* "enabled": true,
* "adapter": "mongoose",
* "uri": "mongodb://localhost:27017/mydb"
* }
* }
* }
*/
async function minimalExample() {
const app = express()
// This is all you need - NSK auto-loads plugins from armcorc.json
const nsk = await Application.create(app).build()
// Access services from container
const logger = nsk.getContainer().resolve('logger')
const database = nsk.getContainer().tryResolve('database')
logger.info('Application initialized from config')
return nsk
}
/**
* Example 2: Explicit config object (for cloud config integration)
*/
async function explicitConfigExample() {
const app = express()
// Load config from cloud config store, env vars, etc.
const config = {
appName: 'auth-core',
env: process.env.NODE_ENV || 'development',
plugins: {
logger: {
enabled: true,
level: process.env.LOG_LEVEL || 'info',
format: process.env.NODE_ENV === 'production' ? 'json' : 'pretty',
},
database: {
enabled: true,
adapter: 'mongoose',
uri: process.env.MONGODB_URI!,
},
cache: {
enabled: true,
adapter: 'redis',
uri: process.env.REDIS_URI,
ttl: 3600,
keyPrefix: 'auth:',
},
scheduler: {
enabled: true,
timezone: 'UTC',
tasks: [
{
name: 'cleanup-sessions',
schedule: '0 2 * * *', // 2 AM daily
handler: async () => {
// Cleanup logic
},
},
],
},
telemetry: {
enabled: process.env.ENABLE_TELEMETRY === 'true',
serviceName: 'auth-core',
serviceVersion: '1.0.0',
otlpEndpoint: process.env.OTLP_ENDPOINT,
exporters: ['otlp'],
},
},
}
const nsk = await Application.create(app)
.withConfig(config)
.build()
return nsk
}
/**
* Example 3: Mixed approach (config + manual services)
*
* Use config for plugins, but manually register custom services
*/
async function mixedExample() {
const app = express()
// Custom services not managed by plugins
class UserRepository {
async findById(id: string) {
// Implementation
}
}
const nsk = await Application.create(app)
// Config handles plugins
.withConfig('./armcorc.json')
// Manual services registration
.withServices((container) => {
container.singleton('userRepo', new UserRepository())
})
.build()
// Access both plugin services and custom services
const logger = nsk.getContainer().resolve('logger')
const userRepo = nsk.getContainer().resolve('userRepo')
return nsk
}
/**
* Example 4: Socket plugin (special case)
*
* Socket plugin requires HttpServer instance, so it can't be purely config-driven.
* You need to register it manually or use a hybrid approach.
*/
async function socketExample() {
const app = express()
const http = require('http')
const server = http.createServer(app)
const { createSocketPlugin } = await import('../plugins/socket')
const nsk = await Application.create(app)
// Auto-load other plugins from config
.withConfig('./armcorc.json')
// Manually add socket plugin (requires server instance)
.plugin(createSocketPlugin(server, {
enabled: true,
cors: { origin: '*' },
}))
.build()
server.listen(3000)
return nsk
}
/**
* Example 5: Runtime config updates (restart pattern)
*
* For runtime reconfiguration without rebuild:
* 1. Update armcorc.json (manually, API, or config management system)
* 2. Send SIGTERM to process
* 3. Process manager (PM2, K8s, etc.) restarts the app
* 4. New config is loaded on startup
*/
async function runtimeReconfigExample() {
const app = express()
// On every restart, fresh config is loaded
const nsk = await Application.create(app).build()
// Graceful shutdown already handled by NSK
// Just ensure process manager restarts on SIGTERM
return nsk
}
export {
minimalExample,
explicitConfigExample,
mixedExample,
socketExample,
runtimeReconfigExample,
}

View File

@@ -0,0 +1,187 @@
/**
* Global Logger Usage Example
*
* This demonstrates how the logger is automatically available globally
* once NSK is initialized, without requiring imports.
*
* This matches the legacy NSK v1 behavior.
*/
import express from 'express'
import { Application } from '../index'
/**
* Initialize NSK with logger plugin
*/
async function initializeApp() {
const app = express()
// Initialize NSK with logger from config
const nsk = await Application.create(app)
.withConfig({
appName: 'global-logger-demo',
plugins: {
logger: {
enabled: true,
level: 'info',
format: 'pretty',
},
},
})
.build()
// After NSK init, logger is available globally
// No import needed!
logger.info('✅ NSK initialized - logger is now globally available')
return { app, nsk }
}
/**
* Example route handler - no logger import needed!
*/
function setupRoutes(app: express.Application) {
app.get('/api/users/:id', (req, res) => {
// Direct usage: logger is globally available
logger.info('User request received', { userId: req.params.id })
try {
// Simulate some work
const user = { id: req.params.id, name: 'John Doe' }
logger.info('User found', { user })
res.json(user)
} catch (error) {
logger.error('Failed to fetch user', { error, userId: req.params.id })
res.status(500).json({ error: 'Internal server error' })
}
})
app.post('/api/auth/login', (req, res) => {
logger.info('Login attempt', { username: req.body.username })
// Login logic...
logger.warn('Failed login attempt', {
username: req.body.username,
ip: req.ip,
})
res.status(401).json({ error: 'Invalid credentials' })
})
}
/**
* Example service class - no logger import!
*/
class UserService {
async getUser(id: string) {
// Logger is globally available in any file
logger.debug('UserService.getUser called', { userId: id })
try {
// Database call...
const user = { id, name: 'Jane Doe' }
logger.info('User retrieved from database', { userId: id })
return user
} catch (error) {
logger.error('Database query failed', { error, userId: id })
throw error
}
}
async createUser(data: any) {
logger.info('Creating new user', { email: data.email })
// Validation...
// Database insert...
logger.info('User created successfully', { userId: 'new-id' })
return { id: 'new-id', ...data }
}
}
/**
* Example middleware - no logger import!
*/
function requestLogger(req: express.Request, res: express.Response, next: express.NextFunction) {
const start = Date.now()
// Log incoming request
logger.info('Incoming request', {
method: req.method,
path: req.path,
ip: req.ip,
})
// Log response
res.on('finish', () => {
const duration = Date.now() - start
logger.info('Request completed', {
method: req.method,
path: req.path,
statusCode: res.statusCode,
duration: `${duration}ms`,
})
})
next()
}
/**
* Example error handler - no logger import!
*/
function errorHandler(
err: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
// Global logger available in error handlers too
logger.error('Unhandled error', {
error: err.message,
stack: err.stack,
path: req.path,
method: req.method,
})
res.status(500).json({ error: 'Internal server error' })
}
/**
* Main application
*/
async function main() {
const { app, nsk } = await initializeApp()
// Setup middleware
app.use(express.json())
app.use(requestLogger)
// Setup routes
setupRoutes(app)
// Setup error handler
app.use(errorHandler)
// Start server
const PORT = 3000
app.listen(PORT, () => {
// Logger available everywhere!
logger.info(`🚀 Server started on port ${PORT}`)
logger.info('Logger is globally available in all files')
logger.info('No imports required!')
})
}
// TypeScript knows about global logger automatically
// because of globals.d.ts exported by NSK
main().catch((error) => {
// Even in catch blocks, logger is available
console.error('Failed to start application:', error)
process.exit(1)
})
export { UserService, requestLogger, errorHandler }

View File

@@ -0,0 +1,193 @@
/**
* Hybrid Config + Manual Plugin Registration Example
*
* This demonstrates the recommended pattern where:
* 1. Config (armcorc.json) is the primary source of truth
* 2. Manual plugin registration acts as override for specific plugins
* 3. Both approaches work together seamlessly
*/
import express from 'express'
import http from 'http'
import { Application, createSocketPlugin, createLoggerPlugin } from '../index'
/**
* Example 1: Config-first with manual override
*
* Use case: Most plugins from config, but need custom logger instance
*/
async function configWithManualOverride() {
const app = express()
// armcorc.json contains:
// {
// "appName": "my-service",
// "plugins": {
// "logger": { "enabled": true, "level": "info" }, <- Will be overridden
// "database": { "enabled": true, "uri": "..." }, <- Auto-loaded
// "cache": { "enabled": true, "adapter": "redis" } <- Auto-loaded
// }
// }
const nsk = await Application.create(app)
// Config loaded from armcorc.json (database, cache auto-loaded)
.plugin(createLoggerPlugin({
// This overrides the logger config from armcorc.json
level: 'debug', // Override: use debug instead of info
format: 'pretty',
// Custom transports, filters, etc.
}))
.build()
// Result:
// - logger: Manually registered instance (overrides config)
// - database: Auto-loaded from config
// - cache: Auto-loaded from config
return nsk
}
/**
* Example 2: Socket plugin special case
*
* Socket plugin requires HttpServer, so it must be manually registered
* but other plugins come from config
*/
async function socketWithConfig() {
const app = express()
const server = http.createServer(app)
// armcorc.json:
// {
// "appName": "chat-service",
// "plugins": {
// "logger": { "enabled": true },
// "database": { "enabled": true, "uri": "..." },
// "cache": { "enabled": true },
// "socket": { "enabled": true, "cors": { "origin": "*" } } <- Declared in config
// }
// }
const nsk = await Application.create(app)
// Socket plugin must be manually registered (requires server instance)
.plugin(createSocketPlugin(server, {
// Config from armcorc.json can be used here if needed
cors: { origin: '*' },
}))
.build()
// Result:
// - logger: Auto-loaded from config
// - database: Auto-loaded from config
// - cache: Auto-loaded from config
// - socket: Manually registered (overrides config, but config declares it's enabled)
server.listen(3000)
return nsk
}
/**
* Example 3: Runtime plugin selection
*
* Load plugins from config, but conditionally override based on environment
*/
async function runtimeOverride() {
const app = express()
const builder = Application.create(app)
// In development, override logger with custom console logger
if (process.env.NODE_ENV === 'development') {
builder.plugin(createLoggerPlugin({
level: 'debug',
format: 'pretty',
}))
}
// All other plugins from config
const nsk = await builder.build()
return nsk
}
/**
* Example 4: Progressive enhancement
*
* Start with basic config, add more plugins manually as needed
*/
async function progressiveEnhancement() {
const app = express()
// armcorc.json has minimal config:
// {
// "appName": "my-service",
// "plugins": {
// "logger": { "enabled": true }
// }
// }
// Build with additional plugins not in config
const nsk = await Application.create(app)
// Logger comes from config
// Add more plugins manually as your app grows
// (though ideally these should be in config too)
.build()
return nsk
}
/**
* Example 5: Best practice pattern (RECOMMENDED)
*
* All plugins declared in config, manual registration ONLY for:
* 1. Plugins requiring runtime instances (e.g., HttpServer for socket)
* 2. Environment-specific overrides
*/
async function bestPracticePattern() {
const app = express()
const server = http.createServer(app)
// armcorc.json declares ALL plugins:
// {
// "appName": "auth-core",
// "plugins": {
// "logger": { "enabled": true, "level": "info" },
// "database": { "enabled": true, "uri": "..." },
// "cache": { "enabled": true, "adapter": "redis" },
// "scheduler": { "enabled": true, "timezone": "UTC" },
// "telemetry": { "enabled": true, "serviceName": "auth-core" },
// "socket": { "enabled": true } <- Declared but must be manually registered
// }
// }
const builder = Application.create(app)
// ONLY manually register socket (requires server instance)
builder.plugin(createSocketPlugin(server))
// Optional: Environment-specific overrides
if (process.env.NODE_ENV === 'development') {
builder.plugin(createLoggerPlugin({
level: 'debug',
format: 'pretty',
}))
}
const nsk = await builder.build()
// Result:
// - Config is single source of truth for what's enabled
// - Code only handles runtime-specific requirements
// - Easy to see what's running by looking at armcorc.json
server.listen(3000)
return nsk
}
export {
configWithManualOverride,
socketWithConfig,
runtimeOverride,
progressiveEnhancement,
bestPracticePattern,
}

View File

@@ -0,0 +1,75 @@
/**
* Quick test script to verify config-driven initialization
*
* Usage:
* npx tsx v2/examples/test-config-driven.ts
*/
import express from 'express'
import { Application } from '../index'
async function test() {
console.log('\n🧪 Testing Config-Driven Initialization\n')
const app = express()
// Test config
const config = {
appName: 'test-app',
env: 'development',
plugins: {
logger: {
enabled: true,
level: 'info',
format: 'pretty',
},
cache: {
enabled: true,
adapter: 'memory',
ttl: 60,
},
},
}
console.log('📝 Config:', JSON.stringify(config, null, 2))
console.log('\n🚀 Building application with config-driven initialization...\n')
try {
const nsk = await Application.create(app)
.withConfig(config)
.build()
console.log('✅ Application built successfully!')
// Test that plugins were loaded
const logger = nsk.getContainer().tryResolve('logger')
const cache = nsk.getContainer().tryResolve('cache')
if (logger) {
console.log('✅ Logger plugin loaded')
logger.info('Logger is working!')
} else {
console.log('❌ Logger plugin NOT loaded')
}
if (cache) {
console.log('✅ Cache plugin loaded')
await cache.set('test', 'hello', 10)
const value = await cache.get('test')
console.log(` Cache test: set "test" = "hello", get "test" = "${value}"`)
} else {
console.log('❌ Cache plugin NOT loaded')
}
// Shutdown
await nsk.shutdown()
console.log('\n✅ Config-driven initialization test passed!')
process.exit(0)
} catch (error) {
console.error('\n❌ Config-driven initialization test failed:', error)
process.exit(1)
}
}
test()

44
v2/globals.d.ts vendored Normal file
View File

@@ -0,0 +1,44 @@
/**
* Global namespace augmentation for NSK
*
* This file augments the global namespace to provide first-class
* access to NSK services without requiring imports.
*
* When you install NSK in your project, these globals are automatically
* available with full TypeScript type safety.
*
* Usage:
* ```typescript
* // No imports needed!
* logger.info('Request received')
* logger.error('Something went wrong', { error })
* ```
*/
import { Logger } from './types/Logger'
declare global {
/**
* Global logger instance
*
* Automatically injected by NSK Logger Plugin.
* Available everywhere in your application without imports.
*
* @example
* ```typescript
* logger.info('User logged in', { userId: '123' })
* logger.warn('Rate limit approaching', { count: 95 })
* logger.error('Database connection failed', { error })
* ```
*/
var logger: Logger
namespace NodeJS {
interface Global {
logger: Logger
}
}
}
// This export is required to make this a module
export {}

View File

@@ -1,18 +1,26 @@
/**
* @armco/node-starter-kit v2
* Modern plugin-based Node.js application framework
*
* Global types are automatically available via globals.d.ts
*/
// Side-effect import to ensure global type augmentation is loaded
// This triggers TypeScript to process globals.d.ts
import type {} from './globals'
// Core exports
export { Application, ApplicationBuilder } from './core/Application'
export { Container } from './core/Container'
export { PluginManager } from './core/PluginManager'
export { ConfigLoader, defineConfig } from './core/ConfigLoader'
export { pluginRegistry, registerPluginFactory, type PluginFactory } from './core/PluginFactory'
// Type exports
export * from './types'
// Plugin exports
// Importing these also triggers factory registration for config-driven initialization
export { LoggerPlugin, createLoggerPlugin } from './plugins/logger'
export { DatabasePlugin, createDatabasePlugin } from './plugins/database'
export { SocketPlugin, createSocketPlugin } from './plugins/socket'

View File

@@ -5,6 +5,7 @@ import { MemoryAdapter } from './MemoryAdapter'
import { RedisAdapter } from './RedisAdapter'
import { Logger } from '../../types/Logger'
import { HealthStatus } from '../../types/Plugin'
import { registerPluginFactory } from '../../core/PluginFactory'
/**
* Cache plugin for data caching
@@ -203,3 +204,6 @@ export class CachePlugin extends BasePlugin<CacheConfig> {
export function createCachePlugin(config: CacheConfig = {}): CachePlugin {
return new CachePlugin(config)
}
// Auto-register plugin factory
registerPluginFactory('cache', (config) => createCachePlugin(config as unknown as CacheConfig))

View File

@@ -1,6 +1,7 @@
import { BasePlugin, HealthStatus } from '../../types/Plugin'
import { ApplicationContext } from '../../types/Context'
import { MongooseAdapter, MongooseConfig } from './MongooseAdapter'
import { registerPluginFactory } from '../../core/PluginFactory'
export interface DatabaseConfig {
adapter?: 'mongoose'
@@ -96,3 +97,6 @@ export class DatabasePlugin extends BasePlugin<DatabaseConfig> {
export function createDatabasePlugin(config: DatabaseConfig): DatabasePlugin {
return new DatabasePlugin(config)
}
// Auto-register plugin factory
registerPluginFactory('database', (config) => createDatabasePlugin(config as unknown as DatabaseConfig))

View File

@@ -2,6 +2,7 @@ import { BasePlugin } from '../../types/Plugin'
import { ApplicationContext } from '../../types/Context'
import { LoggerConfig } from '../../types/Logger'
import { WinstonAdapter } from './WinstonAdapter'
import { registerPluginFactory } from '../../core/PluginFactory'
/**
* Logger Plugin
@@ -36,6 +37,10 @@ export class LoggerPlugin extends BasePlugin<LoggerConfig> {
// Register logger in container
context.container.singleton('logger', this.logger)
// Make logger available globally (first-class access)
// This allows direct usage: logger.info() without imports
;(global as any).logger = this.logger
this.logger.info(`Logger plugin installed: ${config.adapter || 'winston'}`)
}
@@ -54,3 +59,6 @@ export class LoggerPlugin extends BasePlugin<LoggerConfig> {
export function createLoggerPlugin(config?: LoggerConfig): LoggerPlugin {
return new LoggerPlugin(config)
}
// Auto-register plugin factory
registerPluginFactory('logger', (config) => createLoggerPlugin(config as unknown as LoggerConfig))

View File

@@ -4,6 +4,7 @@ import { SchedulerAdapter, SchedulerConfig } from '../../types/Scheduler'
import { NodeCronAdapter } from './NodeCronAdapter'
import { Logger } from '../../types/Logger'
import { HealthStatus } from '../../types/Plugin'
import { registerPluginFactory } from '../../core/PluginFactory'
/**
* Scheduler plugin for task scheduling
@@ -268,3 +269,6 @@ export class SchedulerPlugin extends BasePlugin<SchedulerConfig> {
export function createSchedulerPlugin(config: SchedulerConfig = {}): SchedulerPlugin {
return new SchedulerPlugin(config)
}
// Auto-register plugin factory
registerPluginFactory('scheduler', (config) => createSchedulerPlugin(config as unknown as SchedulerConfig))

View File

@@ -4,6 +4,7 @@ import { OpenTelemetryConfig, OpenTelemetryAdapter } from '../../types/OpenTelem
import { OtelAdapter } from './OpenTelemetryAdapter'
import { Logger } from '../../types/Logger'
import { HealthStatus } from '../../types/Plugin'
import { registerPluginFactory } from '../../core/PluginFactory'
/**
* OpenTelemetry plugin for distributed tracing and metrics
@@ -226,3 +227,7 @@ export class OpenTelemetryPlugin extends BasePlugin<OpenTelemetryConfig> {
export function createOpenTelemetryPlugin(config: OpenTelemetryConfig): OpenTelemetryPlugin {
return new OpenTelemetryPlugin(config)
}
// Auto-register plugin factory
registerPluginFactory('opentelemetry', (config) => createOpenTelemetryPlugin(config as unknown as OpenTelemetryConfig))
registerPluginFactory('telemetry', (config) => createOpenTelemetryPlugin(config as unknown as OpenTelemetryConfig))