ninelivesjs
v0.1.0
Published
A modular, extensible JavaScript framework — starting with a powerful logging utility and growing into a full toolkit for building Node.js applications.
Maintainers
Readme
🐱 ninelivesjs
ninelivesjs is a modular, extensible JavaScript framework — starting with a powerful logging utility and growing into a full toolkit for building Node.js applications.
The philosophy is simple: every piece of ninelivesjs should be small, focused, and easy to replace or extend. Nothing is bolted on for the sake of it. Pick up only what you need.
Modules
| Module | Import | Description |
|---|---|---|
| logger | require('ninelivesjs') | Color-coded, leveled logging with file output |
| config | require('ninelivesjs/config') | Env-aware configuration loader |
| events | require('ninelivesjs/events') | Typed event bus / pub-sub system |
| validate | require('ninelivesjs/validate') | Schema-based input validation |
| router | require('ninelivesjs/router') | Lightweight HTTP router |
| cli | require('ninelivesjs/cli') | CLI builder & utilities |
| ping | require('ninelivesjs/ping') | HTTP uptime checker with auto-logging |
Installation
npm install ninelivesjsZero external dependencies — pure Node.js built-ins only.
Logger
const { info, warn, error, debug } = require('ninelivesjs');
info('Server started on port 3000');
warn('Config file missing, using defaults');
error('Database connection failed', { code: 'ECONNREFUSED' });
debug('Request payload:', { userId: 42 });const { createLogger } = require('ninelivesjs');
const log = createLogger({
level: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
timestamp: true, // ISO-8601 prefix (default: true)
output: 'both', // 'console' | 'file' | 'both'
filePath: 'app.log',
});
// Child loggers — namespace sub-systems
const db = log.child('db');
db.info('Connected'); // [INFO ] [db] ConnectedConfig
Load configuration from environment variables, JSON files, or plain objects — then access everything with dot-notation.
const { createConfig } = require('ninelivesjs/config');
const config = createConfig()
.fromObject({ server: { port: 3000 }, debug: false }) // defaults
.fromFile('./config.json') // file overrides
.fromEnv('APP_'); // env overrides (APP_PORT=8080)
config.get('server.port'); // → 8080 (from env)
config.get('missing', 'default'); // → 'default'
config.require('server.port'); // throws if key is absent
config.set('debug', true);
config.has('server.port'); // → true
config.all(); // → full config objectfromEnv automatically casts "true" → true, "42" → 42, etc.
Events
A lightweight pub-sub event bus.
const { createEventBus } = require('ninelivesjs/events');
const bus = createEventBus();
// Subscribe
bus.on('user:created', user => console.log('New user:', user.name));
// Fire once then auto-unsubscribe
bus.once('app:ready', () => console.log('Ready!'));
// Emit
bus.emit('user:created', { name: 'Alice' });
// Async — wait for the next occurrence of an event
const payload = await bus.waitFor('job:done');
// Inspect
bus.listenerCount('user:created'); // → 1
bus.eventNames(); // → ['user:created']
// Unsubscribe / cleanup
bus.off('user:created', myHandler);
bus.clear('user:created'); // clear one event
bus.clear(); // clear all eventsValidate
Schema-based validation with a fluent builder API.
const { createSchema, ValidationError } = require('ninelivesjs/validate');
const schema = createSchema();
schema.field('name').required().string().min(2).max(50);
schema.field('age').required().number().min(0).max(120);
schema.field('role').enum(['admin', 'user', 'guest']);
schema.field('email').pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
schema.field('code').custom(v => v === 'secret' || 'Invalid code.');
// Returns { valid, errors }
const result = schema.validate({ name: 'Alice', age: 30, role: 'admin' });
result.valid; // → true
result.errors; // → {}
// Throws ValidationError with an errors map on failure
try {
schema.assert({ name: 'X', age: -1 });
} catch (e) {
if (e instanceof ValidationError) {
console.log(e.errors); // { name: ['name must be at least 2 characters.'], age: [...] }
}
}Available rules: required · string · number · boolean · min · max · pattern · enum · custom
Router
A zero-dependency HTTP router built on Node's http module. Supports middleware, route params, query strings, and JSON body parsing out of the box.
const { createRouter } = require('ninelivesjs/router');
const router = createRouter();
// Middleware
router.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// Routes
router.get('/users', (req, res) => res.json({ users: [] }));
router.get('/users/:id', (req, res) => res.json({ id: req.params.id }));
router.post('/users', (req, res) => res.status(201).json(req.body));
router.delete('/users/:id', (req, res) => res.status(204).send(''));
// Query strings — GET /search?q=hello
router.get('/search', (req, res) => res.json({ q: req.query.q }));
// 404 / error handlers
router.notFound((req, res) => res.status(404).json({ error: 'Not Found' }));
router.onError((err, req, res) => res.status(500).json({ error: err.message }));
// Start server
router.listen(3000, () => console.log('Listening on port 3000'));Response helpers: res.json(data) · res.send(body) · res.html(markup) · res.status(code)
CLI
Build command-line tools with argument parsing, interactive prompts, and formatted output.
const { createCLI, parseArgs, prompt, confirm, select, print, table } = require('ninelivesjs/cli');
// ── Argument parsing ──────────────────────────────────────────────────
// node app.js build --env prod --watch --port=8080 -vx
const args = parseArgs();
args._args; // ['build']
args.env; // 'prod'
args.watch; // true
args.port; // '8080'
args.v; // true
// ── CLI builder ───────────────────────────────────────────────────────
const cli = createCLI({ name: 'myapp', version: '1.0.0', description: 'My tool' });
cli.command('build', 'Build the project', async (args, flags) => {
print.info(`Building for env: ${flags.env || 'development'}`);
// ... build logic
print.success('Build complete!');
}, 'myapp build --env <prod|dev>');
cli.command('init', 'Scaffold a new project', async () => {
const name = await prompt('Project name: ');
const ts = await confirm('Use TypeScript?', true);
const pkg = await select('Package manager:', ['npm', 'pnpm', 'yarn']);
print.success(`Created ${name} with ${pkg}`);
});
cli.run(); // reads process.argv automatically
// ── Output helpers ────────────────────────────────────────────────────
print.info('Informational message');
print.success('Everything worked!');
print.warn('Something looks off');
print.error('Something went wrong');
print.bold('Section header');
print.dim('Subtle note');
// ── Table rendering ───────────────────────────────────────────────────
table(
['Name', 'Version', 'Status'],
[
['logger', '1.1.0', '✅ stable'],
['router', '1.1.0', '✅ stable'],
['validate', '1.1.0', '✅ stable'],
]
);Ping
Check whether HTTP/HTTPS servers are up. Automatically logs errors via any ninelivesjs logger (or any object with .error()).
const { ping, pingAll, watch } = require('ninelivesjs/ping');
const { createLogger } = require('ninelivesjs');
const log = createLogger({ level: 'warn' });
// ── Single ping ─────────────────────────────────────────────────────────────
const result = await ping('https://example.com', { logger: log });
// result.up → true / false
// result.status → 200 (or null if unreachable)
// result.latency → 42 (ms)
// result.error → null (or error string if down)
// result.timestamp → '2024-06-01T12:00:00.000Z'
// ── Options ─────────────────────────────────────────────────────────────────
await ping('https://example.com', {
timeout: 3000, // ms before giving up (default: 5000)
method: 'GET', // HTTP method (default: 'HEAD')
okStatus: [404], // extra codes = "up" (200-399 always ok)
headers: { 'X-Token': 'abc' },
logger: log, // logs errors automatically
logSuccess: true, // also log successful pings
});
// ── Ping many URLs in parallel ───────────────────────────────────────────────
const results = await pingAll([
'https://api.example.com/health',
'https://cdn.example.com',
'https://db.internal:5432',
], { logger: log, timeout: 2000 });
results.forEach(r => {
console.log(`${r.url} — ${r.up ? `UP ${r.latency}ms` : `DOWN: ${r.error}`}`);
});
// ── Watch a URL on a repeating interval ─────────────────────────────────────
const watcher = watch('https://example.com', {
interval: 60_000, // ping every 60s (default: 30000)
logger: log,
logSuccess: false,
onResult: result => {
if (!result.up) notifySlack(result);
},
});
// Later — stop watching
watcher.stop();TypeScript
All modules ship with bundled .d.ts declarations — no @types/ package needed.
import { createLogger } from 'ninelivesjs';
import { createConfig } from 'ninelivesjs/config';
import { createEventBus } from 'ninelivesjs/events';
import { createSchema, ValidationError } from 'ninelivesjs/validate';
import { createRouter, Request, Response } from 'ninelivesjs/router';
import { createCLI, parseArgs } from 'ninelivesjs/cli';Roadmap
ninelivesjs is actively growing. Modules planned for upcoming releases:
ninelivesjs/middleware— composable middleware helpers (CORS, rate limiting, auth)ninelivesjs/test— minimal test runner built into the frameworkninelivesjs/cache— in-memory and file-backed cachingninelivesjs/task— async task queue and scheduler
Have an idea? Open an issue — contributions are very welcome.
Contributing
- Fork the repo and create a feature branch
- Write your code and tests (
npm test) - Open a pull request with a clear description
Please keep modules small and focused. ninelivesjs is opinionated about staying lean.
License
MIT — see LICENSE for details.
