A powerful, extensible service engine for JavaScript applications with middleware support, async operations, and comprehensive lifecycle management.
npm install @jucie-engine/coreA powerful, extensible service engine for JavaScript applications with middleware support, async operations, and comprehensive lifecycle management.
This package is made publicly available for use but comes with no support, maintenance commitments, or warranty of any kind. It is provided strictly "as-is" under the MIT license.
- Issues and feature requests may not be addressed
- No timeline or commitment for updates
- Not actively seeking contributions
- Use at your own risk
If you choose to use this package, please ensure it meets your needs through your own testing and evaluation.
- ๐ Service Architecture: Clean, modular service system with lifecycle management
- ๐ง Middleware Support: Composable middleware with sync/async support
- โก Async Operations: Full async/await support for actions and middleware
- ๐ก๏ธ Security: Built-in protection against prototype pollution and namespace conflicts
- ๐ Dependency Management: Automatic service dependency resolution
- โ๏ธ Configuration: Flexible service configuration with defaults merging
- ๐งช Well Tested: Comprehensive test suite with 33+ test cases
- ๐ฆ TypeScript Ready: Full TypeScript definitions included
``bash`
npm install @jucie-engine/core
`javascript
import { Engine, ServiceProvider } from '@jucie-engine/core';
// Create an engine instance
const engine = Engine.create();
// Define a service using method pattern
class MyService extends ServiceProvider {
static manifest = {
name: 'My Service',
namespace: 'myService',
version: '1.0.0'
};
actions(useContext, config) {
return {
greet: (name) => Hello, ${name}!,
fetchData: async () => {
// Async operations supported
return await fetch('/api/data').then(r => r.json());
}
};
}
middleware(useContext, config) {
return (action, ctx, next) => {
console.log(Executing action: ${action});
return next();
};
}
}
// Install the service
await engine.install(MyService);
// Use the service
const greeting = engine.myService.greet('World');
console.log(greeting); // "Hello, World!"
`
`javascript
import { Engine, ServiceProvider } from '@jucie-engine/core';
// Create an engine instance
const engine = Engine.create();
// Define a service using setup pattern
class MyService extends ServiceProvider {
static manifest = {
name: 'My Service',
namespace: 'myService',
version: '1.0.0'
};
setup({ defineActions, defineMiddleware }) {
defineActions((useContext, config) => {
return {
greet: (name) => Hello, ${name}!,
fetchData: async () => {
return await fetch('/api/data').then(r => r.json());
}
};
});
defineMiddleware((useContext, config) => {
return (action, ctx, next) => {
console.log(Executing action: ${action});
return next();
};
});
}
}
// Install the service
await engine.install(MyService);
// Use the service
const greeting = engine.myService.greet('World');
console.log(greeting); // "Hello, World!"
`
All services extend the ServiceProvider base class and implement the following lifecycle methods:
#### Required Properties
`javascript`
static manifest = {
name: 'Service Name',
namespace: 'serviceName', // Must be valid JS identifier
version: '1.0.0'
};
#### Service Definition Patterns
The ServiceProvider supports two patterns for defining service functionality:
Method Pattern (Traditional approach):
- actions(useContext, config) - Define service actions (required)
- middleware(useContext, config) - Define middleware functions (optional)
- getters(useContext, config) - Define read-only properties (optional)
- initialize(useContext, config) - Setup logic called directly (optional)
- uninstall(useContext, config) - Cleanup logic called directly (optional)
Setup Pattern (Declarative approach):
- setup({ defineActions, defineMiddleware, defineGetters, defineInitialize, defineUninstall }) - Declarative service definition
`javascript
class DatabaseService extends ServiceProvider {
static manifest = {
name: 'Database Service',
namespace: 'db',
version: '1.0.0',
defaults: { connectionString: 'sqlite:memory:' }
};
#connection = null;
initialize(useContext, config) {
this.#connection = createConnection(this.config.connectionString);
}
getters(useContext, config) {
return {
isConnected: () => this.#connection !== null
};
}
middleware(useContext, config) {
return (action, ctx, next) => {
console.log(Database action: ${action});
return next();
};
}
actions(useContext, config) {
return {
async query: (sql, params = []) => {
return await this.#connection.query(sql, params);
},
async transaction: async (callback) => {
const tx = await this.#connection.beginTransaction();
try {
const result = await callback(tx);
await tx.commit();
return result;
} catch (error) {
await tx.rollback();
throw error;
}
}
};
}
uninstall(useContext, config) {
if (this.#connection) {
this.#connection.close();
this.#connection = null;
}
}
}
`
`javascript
class CacheService extends ServiceProvider {
static manifest = {
name: 'Cache Service',
namespace: 'cache',
version: '1.0.0',
defaults: { ttl: 300000, maxSize: 1000 }
};
#cache = new Map();
setup({ defineMiddleware, defineGetters, defineActions, defineInitialize, defineUninstall }) {
// Define middleware
defineMiddleware((useContext, config) => {
return (action, ctx, next) => {
console.log(Cache action: ${action});
return next();
};
});
// Define getters
defineGetters((useContext, config) => {
return {
size: () => this.#cache.size,
isEnabled: () => this.config.maxSize > 0
};
});
// Define actions
defineActions((useContext, config) => {
return {
get: (key) => {
const item = this.#cache.get(key);
if (!item) return null;
if (Date.now() > item.expires) {
this.#cache.delete(key);
return null;
}
return item.value;
},
set: (key, value, ttl = this.config.ttl) => {
if (this.#cache.size >= this.config.maxSize) {
// Remove oldest entry
const firstKey = this.#cache.keys().next().value;
this.#cache.delete(firstKey);
}
this.#cache.set(key, {
value,
expires: Date.now() + ttl
});
},
clear: () => {
this.#cache.clear();
}
};
});
// Define initialize
defineInitialize((useContext, config) => {
console.log('Cache service initialized');
});
// Define uninstall
defineUninstall((useContext, config) => {
this.#cache.clear();
console.log('Cache service cleaned up');
});
}
}
`
| Feature | Method Pattern | Setup Pattern |
|---------|---------------|---------------|
| Syntax | Class methods | Single setup function |
| Organization | Separate methods | Centralized definition |
| Flexibility | High - conditional methods | Medium - declarative |
| Readability | Good for simple services | Excellent for complex services |
| Type Safety | Standard class methods | Requires proper typing |
| Use Case | Simple to medium services | Complex services with many definitions |
The engine supports powerful middleware composition for intercepting and processing action calls:
`javascript
class LoggingService extends ServiceProvider {
static manifest = {
name: 'Logging Service',
namespace: 'logging',
version: '1.0.0'
};
middleware(useContext, config) {
return [
// Sync middleware
(action, ctx, next) => {
console.log([${new Date().toISOString()}] Starting ${action});[${new Date().toISOString()}] Completed ${action} in ${Date.now() - start}ms
const start = Date.now();
const result = next();
console.log();
return result;
},
// Async middleware
async (action, ctx, next) => {
// Authentication check
const user = await authenticateUser();
if (!user) {
throw new Error('Unauthorized');
}
ctx.user = user;
return next();
}
];
}
actions(useContext, config) {
return {
log: (message) => console.log(message)
};
}
}
`
Services can declare dependencies that are automatically installed:
`javascript
class AuthService extends ServiceProvider {
static manifest = {
name: 'Auth Service',
namespace: 'auth',
version: '1.0.0'
};
actions(useContext, config) {
const { engine } = useContext();
return {
login: (credentials) => {
// Use dependency
return engine.db.query('SELECT * FROM users WHERE email = ?', [credentials.email]);
}
};
}
}
class ApiService extends ServiceProvider {
static manifest = {
name: 'API Service',
namespace: 'api',
version: '1.0.0',
dependencies: [AuthService, DatabaseService] // Auto-install dependencies
};
actions(useContext, config) {
const { engine } = useContext();
return {
handleRequest: (req, res) => {
// Dependencies are available
const user = engine.auth.getCurrentUser();
return engine.db.query('SELECT * FROM posts WHERE userId = ?', [user.id]);
}
};
}
}
`
Services support configuration with defaults:
`javascript
class ConfigurableService extends ServiceProvider {
static manifest = {
name: 'Configurable Service',
namespace: 'configurable',
version: '1.0.0',
defaults: {
timeout: 5000,
retries: 3,
debug: false
}
};
actions(useContext, config) {
return {
getConfig: () => this.config,
getTimeout: () => this.config.timeout
};
}
}
// Install with custom configuration
const configuredService = ConfigurableService.configure({
timeout: 10000,
debug: true
});
await engine.install(configuredService);
console.log(engine.configurable.getTimeout()); // 10000
`
The engine provides global context management:
`javascript
import { provideEngine, useEngine, hasEngine } from '@jucie-engine/core';
// Provide engine globally
provideEngine(engine);
// Use engine from anywhere
const globalEngine = useEngine();
const hasGlobalEngine = hasEngine();
`
The engine provides comprehensive error handling with context:
`javascript
class ErrorProneService extends ServiceProvider {
static manifest = {
name: 'Error Service',
namespace: 'error',
version: '1.0.0'
};
actions(useContext, config) {
return {
riskyOperation: () => {
throw new Error('Something went wrong');
}
};
}
}
try {
await engine.install(ErrorProneService);
engine.error.riskyOperation();
} catch (error) {
console.log(error.action); // 'riskyOperation'
console.log(error.namespace); // 'error'
console.log(error.message); // 'Something went wrong'
}
`
The engine includes built-in security protections:
- Prototype Pollution Prevention: Blocks __proto__ and constructor namespace usage
- Namespace Validation: Ensures valid JavaScript identifiers
- Reserved Namespace Protection: Prevents conflicts with engine methods
- Input Sanitization: Validates service manifests and configurations
#### Engine.create(config?)
Creates a new engine instance.
#### engine.install(...services)
Installs one or more services.
#### engine.uninstall(...namespaces)
Uninstalls services by namespace.
#### engine.use(...definitions)
Adds middleware or other definitions directly.
#### ServiceProvider.configure(options)
Creates a configured service instance.
#### Static Properties
- manifest - Service metadata and configuration
#### Lifecycle Methods
- actions(useContext, config) - Define service actionsmiddleware(useContext, config)
- - Define middleware functionsgetters(useContext, config)
- - Define read-only propertiesinitialize(useContext, config)
- - Setup logic called directlyuninstall(useContext, config)
- - Cleanup logic called directly
The engine includes a comprehensive test suite:
`bashRun all tests
npm test
Development
`bash
Install dependencies
npm installBuild the project
npm run buildWatch for changes
npm run watchRun benchmarks
npm run bench
``MIT License with Commons Clause - See LICENSE file for details.
TL;DR: Free to use in your projects, but you cannot sell this software as a competing product or service.