npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@maiyunnet/kebab

v9.29.0

Published

Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.

Readme

A simple, easy-to-use, full-featured Node.js framework, ready to use out of the box.

Project statement: This project was first committed to GitHub on February 25, 2019. Its predecessor, the Mutton framework, was first committed on July 13, 2015, after several years of development. In 2026, the project formally introduced AI-assisted development and adaptations for AI integration, but software engineers still lead, review, verify and test the project; it is not automatically generated by simply telling AI what we want. Non-Chinese content is translated with assistance from Byterun Translator, so the wording may have an AI-like tone.

The releases on GitHub may be incomplete. For versions after 2.0.0, refer to NPM as the source of record: NPM

Languages

简体中文 | 繁體中文

Requirements

Node.js 24+

Installation

Run npm i @maiyunnet/kebab to install the latest version. After installation, run npx kebab init to initialize, then run npx kebab to start the project.

You can also start the project with pm2 by running pm2 start npx --name "kebab" -- kebab.

Libraries

Ai, Buffer, Captcha, Consistent, Cookie, Core, Cron, Crypto, Db (MySQL, PostgreSQL), Dns (DNSPod, Alibaba Cloud), Fs, Kv (Redis), Lan, Lang, Net, Ratelimit, S3, Scan, Session, Socket, Sql, Ssh (Shell, Sftp), Text, Time, Turnstile (Cloudflare, Tencent Cloud), Undici, Vector (Milvus), Ws, Zip, Zlib.

Use Undici for new HTTP requests; Net remains available for compatibility with existing code.

Selected features

Ready to use

Following the principle of being ready to use out of the box, Kebab wraps commonly used libraries in a consistent style.

Hot updates

Call lCore.sendRestart() to initiate a graceful restart: new processes accept connections, while old processes exit once their existing connections have ended.

Multiple processes

Create multiple processes based on the number of CPU cores to make full use of a multi-core CPU.

Shared connection pools

Different sites in the same process share a connection pool when connecting to the same Db, Kv or other server, maximizing efficiency and reducing overhead.

Easy-to-use Undici library

import * as lUndici from '@maiyunnet/kebab/lib/undici.js';

const res = await lUndici.open('https://example.com/test').post().data({ 'a': '1', 'b': '2' }).request();
const content = await res.getContent();

You can also use it like this:

const res = await lUndici.get('https://example.com/test');
const content = await res.getContent();

When you only need a JSON response, use the corresponding shortcut method. Request or read failures return null; JSON parsing failures return false:

const data = await lUndici.getResponseJson('https://example.com/data');

You can customize hostname resolution. Here, hostsMap is a caller-provided mapping of hostnames to IP addresses:

const res = await lUndici.get('https://example.com/test', {
    'hosts': hostsMap,
});
const content = await res.getContent();

You can also use another local network interface. Here, localAddress is the local IP address from your configuration:

const res = await lUndici.get('https://example.com/test', {
    'local': localAddress,
});
const content = await res.getContent();

It also has a complete cookie manager that stores received cookies in a caller-provided object. When sending requests, the system selects cookies based on their domain, path and other attributes, and discards Set-Cookie values with invalid cross-domain settings:

import * as lCookie from '@maiyunnet/kebab/lib/cookie.js';

const cookie: Record<string, lCookie.ICookie> = {};
const res1 = await lUndici.get('https://example.com/login', { 'cookie': cookie });
await res1.getContent();
const res2 = await lUndici.get('https://example.com/profile', { 'cookie': cookie });
await res2.getContent();

Note: Undici supports both options and chained open calls, such as await lUndici.open('https://example.com/file').follow().timeout(60).save(filePath).request(); filePath is a caller-provided local file path.

Easy-to-use Db library

A wide range of useful interfaces makes it easy to select the data you need from a database. In the following controller method snippets, db is a database connection pool, and mOrder and mUser are application-defined models:

const query = mOrder.where<mOrder>(db, {
    'state': '1',
}, { 'ctr': this }).by('id', 'DESC').page(10, 1);
const list = await query.all();
if (list === false) {
    return [0, 'Query failed.'];
}
const count = list.length;
const total = await query.total();

Get a user:

const user = await mUser.select<mUser>(db, ['id', 'user'], { 'ctr': this }).filter([
    ['time_add', '>=', '1583405134']
]).first();

XSRF checks

For data-changing endpoints that rely on browser session cookies, use _valibotx to validate input and check XSRF, preventing cross-site request forgery. Pages generate a token with _enabledXsrf() and submit it as _xsrf; read-only endpoints use _valibot.

QR code login

The Scan library makes QR code login easy to implement.

Reverse proxy

Use the Undici library’s rproxy method with the current controller and a route-prefix mapping to implement a reverse proxy.

More features await your exploration

Code examples

Generate a random 16-digit string

import * as lCore from '@maiyunnet/kebab/lib/core.js';

const str = lCore.random(16, lCore.RANDOM_N);

Generate a CAPTCHA image

import * as lCaptcha from '@maiyunnet/kebab/lib/captcha.js';

const captcha = lCaptcha.get(400, 100);
const svg = captcha.getBuffer();
const phrase = captcha.getPhrase();

getBuffer() returns an SVG string; phrase is the corresponding CAPTCHA text, which should be stored on the server for subsequent verification.

Get a list

const userList = await mUser.where<mUser>(db, [
    ['state', '!=', '0'],
    {
        'type': ['1', '2', '3'],
        'is_lock': '0',
    },
], { 'ctr': this }).all();

Note: Values in query conditions are passed through parameter binding. Handwritten SQL should also bind user input, and dynamic table and column names should be restricted to trusted values.

Automatic table prefixes and identifier quoting with Sql

import * as lSql from '@maiyunnet/kebab/lib/sql.js';

const sql = lSql.get({ 'service': lSql.ESERVICE.MYSQL, 'pre': 'test' });
sql.select(['SUM(user.age) age'], 'order').leftJoin('user', { 'order.user_id': lSql.column('user.id') });
const statement = sql.getSql();

The following output is for MySQL with the test_ table prefix; PostgreSQL uses schemas and double quotes:

SELECT SUM(`test_user`.`age`) AS `age` FROM `test_order` LEFT JOIN `test_user` ON `test_order`.`user_id` = `test_user`.`id`

Localization

In the following controller method snippet, lang is a validated language code:

if (!await this._loadLocale(lang, 'test')) {
    return 'Could not load locale.';
}
return this._l('copy');

Depending on the value of lang, the output will be Copy, 复制, 複製, コピー, etc., configured in the site’s /data/locale/ directory.

Data validation

New endpoints use Valibot to validate strings, numbers, value ranges and regular expressions. On success, use output to obtain the converted data. Define rules in an application module for reuse, such as lib/schema/input.ts:

import * as lCore from '@maiyunnet/kebab/lib/core.js';

const v = lCore.v;
export const inputSchema = v.strictObject({
    'he': v.pipe(v.string(), v.nonEmpty()),
    'num': v.pipe(v.string(), v.toNumber(), v.gtValue(10)),
    'reg': v.pipe(v.string(), v.regex(/^[A-CX-Z5-7]+$/)),
    'arr': v.picklist(['a', 'x', 'hehe']),
});

Import the rules in the ctr/main.ts controller and validate within a method:

import * as lInputSchema from '../lib/schema/input.js';

const parsed = this._valibot(lInputSchema.inputSchema, this._post);
if (!parsed.success) {
    return parsed.response;
}
return [1, parsed.output];

Use _valibotx instead for data-changing endpoints that rely on browser session cookies. See the quick start for multilingual messages and XSRF usage, and /test/ctr-valibot for a working example.

Other examples

Visit /test/ to see more examples.

License

This framework is licensed under Apache-2.0.