hook-app
v1.0.0
Published
A dynamic framework to supercharge your Node.js applications with hooks.
Downloads
4
Maintainers
Readme
hook-app
A WordPress-style hooks system for Node.js applications. Build modular, traceable, and extensible apps with lifecycle hooks, services, and features.
Installation
npm install hook-appimport hookApp from 'hook-app';Quick Start
Create a simple app with a feature that runs during initialization:
import hookApp, { type RegisterContext } from 'hook-app';
const myFeature = ({ registerAction }: RegisterContext) => {
registerAction({
hook: '$INIT_FEATURE',
handler: () => {
console.log('Hello, Hook App!');
},
});
};
await hookApp({ features: [myFeature] });Core Concepts
Hooks
Named extension points where actions can attach. Reference built-in hooks with $HOOK_NAME syntax:
registerAction({
hook: '$INIT_FEATURE', // Built-in lifecycle hook
handler: () => { /* ... */ },
});Actions
Functions that execute when a hook is triggered. Actions have a name, priority, and handler:
registerAction({
hook: '$START',
name: 'my-action',
priority: 10, // Higher = runs first
handler: (args, ctx) => {
// Your logic here
},
});Services & Features
- Services - Core integrations that set up shared functionality (databases, logging, etc.)
- Features - Application-specific logic built on top of services
Both use the same RegisterContext interface:
const myService = ({ registerAction, registerHook, createHook }: RegisterContext) => {
// Register custom hooks, actions, etc.
};
await hookApp({
services: [myService],
features: [myFeature],
});Lifecycle
Boot phases execute in this order:
START → SETTINGS → INIT_SERVICES → INIT_SERVICE → INIT_FEATURES → INIT_FEATURE
→ START_SERVICES → START_SERVICE → START_FEATURES → START_FEATURE → FINISHKey Patterns
Custom Hook Communication
Features communicate through custom hooks:
import hookApp, { type RegisterContext } from 'hook-app';
// Feature 1: Registers and triggers a custom hook
const notifier = ({ registerHook, registerAction, createHook }: RegisterContext) => {
registerHook({ NOTIFY: 'notify' });
registerAction({
hook: '$INIT_FEATURE',
handler: () => {
createHook.sync('notify', { message: 'Hello from notifier!' });
},
});
};
// Feature 2: Listens to the custom hook
const listener = ({ registerAction }: RegisterContext) => {
registerAction({
hook: '$NOTIFY',
handler: (args) => {
console.log('Received:', (args as { message: string }).message);
},
});
};
await hookApp({ features: [notifier, listener] });Settings & Configuration
Access and modify settings using dot-notation paths:
import hookApp, { type RegisterContext } from 'hook-app';
const app = await hookApp({
settings: {
api: {
baseUrl: 'https://api.example.com',
timeout: 5000,
},
},
features: [
({ registerAction }: RegisterContext) => {
registerAction({
hook: '$INIT_FEATURE',
handler: ({ getConfig, setConfig }: RegisterContext) => {
const url = getConfig<string>('api.baseUrl');
console.log('API URL:', url);
// Modify settings
setConfig('api.timeout', 10000);
},
});
},
],
});
// Access final settings
console.log(app.settings);Hook Execution Modes
Choose how actions execute when a hook is triggered:
| Mode | Method | Description |
|------|--------|-------------|
| sync | createHook.sync(name, args?) | Synchronous execution |
| serie | createHook.serie(name, args?) | Async, one at a time |
| parallel | createHook.parallel(name, args?) | Async, all at once |
| waterfall | createHook.waterfall(name, initial) | Pass result to next handler |
Waterfall example:
registerAction('transform', (value: number) => value + 1);
registerAction('transform', (value: number) => value * 2);
const result = createHook.waterfall('transform', 5);
// result.value === 12 (5 + 1 = 6, then 6 * 2 = 12)Lifecycle Hooks Reference
| Hook | Execution | Description |
|------|-----------|-------------|
| $START | serie | App starting |
| $SETTINGS | serie | Configure settings |
| $INIT_SERVICES | parallel | Initialize all services |
| $INIT_SERVICE | serie | Initialize each service |
| $INIT_FEATURES | parallel | Initialize all features |
| $INIT_FEATURE | serie | Initialize each feature |
| $START_SERVICES | parallel | Start all services |
| $START_SERVICE | serie | Start each service |
| $START_FEATURES | parallel | Start all features |
| $START_FEATURE | serie | Start each feature |
| $FINISH | serie | App ready |
API Quick Reference
Main Entry
import hookApp from 'hook-app';
const app = await hookApp({
services?: ServiceDef[],
features?: FeatureDef[],
settings?: Record<string, unknown> | ((ctx: RegisterContext) => Record<string, unknown>),
context?: Record<string, unknown>,
trace?: string | null,
});
// Returns: { settings, context }Registration (inside services/features)
// Register an action on a hook
registerAction({
hook: '$INIT_FEATURE',
handler: (args, ctx) => { /* ... */ },
name?: string,
priority?: number,
});
// Shorthand forms
registerAction('hook-name', handler);
registerAction('hook-name', handler, { priority: 10 });
// Register a custom hook
registerHook({ MY_HOOK: 'my-hook' });Hook Execution
// Synchronous
const results = createHook.sync('hook-name', args?);
// Async sequential
const results = await createHook.serie('hook-name', args?);
// Async parallel
const results = await createHook.parallel('hook-name', args?);
// Waterfall (pass value through handlers)
const { value, results } = createHook.waterfall('hook-name', initialValue);Config & Context Access
// Get/set configuration
const value = getConfig<T>('path.to.value', defaultValue?);
setConfig('path.to.value', newValue);
// Get/set custom context
const value = getContext<T>('path.to.value', defaultValue?);
setContext('path.to.value', newValue);Tracing
Enable boot tracing to debug your app's lifecycle:
await hookApp({
trace: 'compact', // 'full' | 'normal' | 'compact' | null
features: [/* ... */],
});License
ISC
