
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A WordPress-style hooks system for Node.js applications. Build modular, traceable, and extensible apps with lifecycle hooks, services, and features.
npm install hook-app
import hookApp from 'hook-app';
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] });
Named extension points where actions can attach. Reference built-in hooks with $HOOK_NAME syntax:
registerAction({
hook: '$INIT_FEATURE', // Built-in lifecycle hook
handler: () => { /* ... */ },
});
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
},
});
Both use the same RegisterContext interface:
const myService = ({ registerAction, registerHook, createHook }: RegisterContext) => {
// Register custom hooks, actions, etc.
};
await hookApp({
services: [myService],
features: [myFeature],
});
Boot phases execute in this order:
START → SETTINGS → INIT_SERVICES → INIT_SERVICE → INIT_FEATURES → INIT_FEATURE
→ START_SERVICES → START_SERVICE → START_FEATURES → START_FEATURE → FINISH
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] });
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);
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)
| 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 |
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 }
// 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' });
// 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);
// 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);
Enable boot tracing to debug your app's lifecycle:
await hookApp({
trace: 'compact', // 'full' | 'normal' | 'compact' | null
features: [/* ... */],
});
ISC
FAQs
A dynamic framework to supercharge your Node.js applications with hooks.
The npm package hook-app receives a total of 2 weekly downloads. As such, hook-app popularity was classified as not popular.
We found that hook-app demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.