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

@fuzionx/framework

v0.1.23

Published

Full-stack MVC framework built on @fuzionx/core — Controller, Service, Model, Middleware, DI, EventBus

Readme

@fuzionx/framework

Laravel-inspired full-stack MVC framework powered by Rust N-API bridge — 500K+ RPS

@fuzionx/framework          ← npm install (개발자가 설치)
  └── @fuzionx/core          ← HTTP 엔진 (libuv Fusion, WebSocket, 세션, 크립토)
      └── @fuzionx/bridge    ← Rust N-API 네이티브 모듈

Quick Start

# 글로벌 CLI 설치 (fx + create-fuzionx)
npm install -g create-fuzionx

# 프로젝트 생성
fx new my-app
# 또는: npx create-fuzionx my-app

cd my-app
npm install

# 개발 서버
fx dev              # 또는: npx fx dev

# 또는 직접 실행
node app.js

서버가 http://localhost:49080 에서 시작됩니다.


프로젝트 구조

my-app/
├── fuzionx.yaml              # 서버 + DB + 세션 설정
├── .env                      # 환경변수
├── app.js                    # 엔트리포인트
│
├── routes/                   # 라우트 정의
│   ├── api.js
│   └── web.js
│
├── controllers/              # HTTP 컨트롤러 (자동 스캔)
│   └── UserController.js
│
├── models/                   # ORM 모델 (자동 스캔)
│   └── User.js
│
├── services/                 # 비즈니스 로직 (자동 스캔)
│   └── UserService.js
│
├── middleware/                # 미들웨어 클래스 (자동 스캔)
│   └── AuthMiddleware.js
│
├── ws/                       # WebSocket 핸들러 (자동 스캔)
├── jobs/                     # 스케줄러/큐 Job (자동 스캔)
├── events/                   # 이벤트 리스너
│
├── views/                    # 템플릿 (Tera SSR / Bridge 렌더링)
│   └── default/
│       ├── layouts/
│       │   └── main.html
│       └── pages/
│           └── home.html
│
├── locales/                  # i18n 번역 파일
│   ├── ko.yaml
│   └── en.yaml
│
├── storage/                  # 로그, 업로드
└── public/                   # 정적 파일

엔트리포인트 (app.js)

import { Application } from '@fuzionx/framework';
import apiRoutes from './routes/api.js';
import webRoutes from './routes/web.js';

const app = new Application({ configPath: './fuzionx.yaml' });

// 라우트 등록
app.routes(apiRoutes);
app.routes(webRoutes);

// 서비스 등록 (Lazy 싱글톤)
app.register('mailer', () => new MailService(app.config.get('app.mail')));

// 라이프사이클 훅
app.on('booted', async () => {
    console.log(`DB 연결 완료`);
});

// 서버 시작
app.listen(49080, () => {
    console.log('🚀 FuzionX running on http://localhost:49080');
});

라우팅 & 컨트롤러

라우트 파일

// routes/api.js
import UserController from '../controllers/UserController.js';
import Joi from 'joi';

export default (r) => {
    r.group('/api', { middleware: ['apiAuth'] }, (r) => {
        r.group('/users', (r) => {
            r.get('/',       UserController.index);
            r.get('/:id',   UserController.show);
            r.post('/',     UserController.store, {
                validate: { body: Joi.object({
                    name: Joi.string().min(2).required(),
                    email: Joi.string().email().required(),
                }) }
            });
            r.put('/:id',    UserController.update);
            r.delete('/:id', UserController.destroy);
        });
    });
};
// routes/web.js
import HomeController from '../controllers/HomeController.js';

export default (r) => {
    r.get('/', HomeController.index);
    r.get('/about', HomeController.about);
};

컨트롤러

// controllers/UserController.js
import { Controller } from '@fuzionx/framework';

export default class UserController extends Controller {

    async index(ctx) {
        const users = await this.db.User.paginate(ctx.query.page || 1, 20);
        ctx.json(users);
    }

    async show(ctx) {
        const user = await this.db.User.findOrFail(ctx.params.id);
        ctx.json(user);
    }

    async store(ctx) {
        const user = await this.service('UserService').register(ctx.body);
        ctx.status(201).json(user);
    }

    async update(ctx) {
        const user = await this.db.User.findOrFail(ctx.params.id);
        await user.update(ctx.body);
        ctx.json(user);
    }

    async destroy(ctx) {
        await this.service('UserService').deactivate(ctx.params.id);
        ctx.status(204).end();
    }
}

미들웨어는 글로벌 → 그룹 → 라우트별 3단계로 적용됩니다.


Context (ctx)

모든 핸들러, 미들웨어에 전달되는 통합 객체:

Request

ctx.method              // 'GET', 'POST', ...
ctx.url                 // '/users/42?page=1'
ctx.path                // '/users/42'
ctx.params              // { id: '42' }
ctx.query               // { page: '1' }
ctx.body                // JSON 자동 파싱된 요청 바디
ctx.headers             // 요청 헤더
ctx.ip                  // 클라이언트 IP
ctx.user                // 인증된 사용자 (미들웨어에서 설정)
ctx.session             // 세션
ctx.cookies             // 파싱된 쿠키 객체
ctx.locale              // 현재 locale ('ko', 'en')
ctx.files               // 업로드된 파일 메타데이터 배열

ctx.get(header)         // 헤더 값
ctx.is(type)            // Content-Type 확인
ctx.accepts('json','html') // Accept 협상
ctx.t(key, vars)        // i18n 번역

Response

ctx.json(data)                     // JSON 응답
ctx.status(201).json(data)         // 상태 코드 + JSON
ctx.send(html)                     // HTML 응답
ctx.text(string)                   // text/plain
ctx.render('home', { title })      // 템플릿 렌더링 (Rust Bridge SSR)
ctx.redirect('/login')             // 302 리다이렉트
ctx.redirect('/new', 301)          // 301 영구 리다이렉트
ctx.back()                         // Referer로 리다이렉트
ctx.error(404, 'Not found')       // 에러 응답
ctx.download(filePath)             // 파일 다운로드
ctx.stream(readable)               // 스트림 응답
ctx.setHeader(key, value)          // 헤더 설정
ctx.cookie(name, value, opts)      // 쿠키 설정
ctx.end()                          // 응답 종료

Model (ORM)

// models/User.js
import { Model } from '@fuzionx/framework';

export default class User extends Model {
    static table = 'users';
    static timestamps = true;
    static hidden = ['password'];

    static columns = {
        id:       { type: 'increments' },
        name:     { type: 'string', length: 100 },
        email:    { type: 'string', length: 150, unique: true },
        password: { type: 'string', length: 255 },
        active:   { type: 'boolean', default: true },
    };

    posts()   { return this.hasMany('Post'); }
    profile() { return this.hasOne('Profile'); }

    static findByEmail(email) {
        return this.where('email', email).first();
    }
}

지원 드라이버: SQLite · MariaDB · PostgreSQL · MongoDB


Service

// services/UserService.js
import { Service } from '@fuzionx/framework';

export default class UserService extends Service {

    async register(data) {
        if (await this.db.User.findByEmail(data.email)) {
            throw this.error('이미 존재하는 이메일');
        }
        const user = await this.transaction(async (trx) => {
            const user = await this.db.User.create(data, { trx });
            await this.db.Profile.create({ userId: user.id }, { trx });
            return user;
        });
        this.emit('user:created', user);
        return user;
    }
}

Middleware

// middleware/AuthMiddleware.js
import { Middleware } from '@fuzionx/framework';

export default class AuthMiddleware extends Middleware {
    static name = 'auth';

    handle(ctx, next) {
        const token = ctx.headers.authorization?.replace('Bearer ', '');
        if (!token) return ctx.error(401, 'Unauthorized');
        ctx.user = this.service('AuthService').verify(token);
        next();
    }
}

내장 미들웨어

| 이름 | 기능 | 처리 | |------|------|------| | bodyParser | JSON/Form 바디 파싱 | JS | | cors | CORS 헤더 | Rust | | csrf | CSRF 토큰 검증 | JS | | session | 세션 로드/저장 | Rust | | rateLimit | 요청 제한 | Rust | | static | 정적 파일 서빙 | Rust |


WebSocket

// ws/ChatHandler.js
import { WsHandler } from '@fuzionx/framework';

export default class ChatHandler extends WsHandler {
    static namespace = '/chat';
    static middleware = ['auth'];

    static events(e) {
        e.on('chat', this.handleChat);
        e.on('typing', this.handleTyping);
    }

    handleChat(socket, data) {
        socket.to(`room:${data.roomId}`).send({
            type: 'chat',
            data: { user: socket.user, message: data.message },
        });
    }
}

설정 (fuzionx.yaml)

bridge:
  port: 49080
  workers: 4              # 0 = CPU 수 자동
  cors:
    enabled: true
    origins: ["*"]
  rate_limit:
    enabled: true
    per_ip: 1000
  session:
    enabled: true
    store: memory          # memory | redis
    ttl: 3600
  logging:
    level: info

database:
  main:
    driver: sqlite
    path: ./storage/database.sqlite

app:
  name: 'My App'
  environment: ${NODE_ENV:development}
  auth:
    secret: ${JWT_SECRET:change-me}
    accessTtl: '15m'
  i18n:
    default_locale: 'ko'

환경변수 치환

host: ${DB_HOST:127.0.0.1}     # 없으면 기본값 사용
password: ${DB_PASSWORD}        # 필수 — 없으면 부트 시 에러
redis_url: ${REDIS_URL:}        # 선택 — 없으면 빈 문자열

Config 접근

app.config.get('bridge.port')                    // 49080
app.config.get('app.auth.secret')                // JWT secret
app.config.get('app.payment.api_key', null)      // 커스텀 설정 + 기본값

CLI

프로젝트 생성 (create-fuzionx)

npx create-fuzionx my-app            # 새 프로젝트 스캐폴딩
cd my-app
npm install

프로젝트 내 명령어 (fx)

npx fx make:controller User          # CRUD 컨트롤러
npx fx make:model User               # ORM 모델
npx fx make:service User             # 서비스
npx fx make:middleware Auth           # 미들웨어
npx fx make:job Cleanup              # 스케줄 Job
npx fx make:task SendEmail           # 큐 Task
npx fx make:ws Chat                  # WebSocket 핸들러
npx fx make:event user               # 이벤트 핸들러
npx fx make:worker Heavy             # Worker (worker_threads)
npx fx make:test User                # 테스트

# DB
npx fx db:sync                       # 모델 ↔ DB 스키마 diff
npx fx db:sync --apply               # 안전 변경 적용

# 개발
npx fx dev                           # 개발 서버 (--watch)
npx fx dev --port=4000               # 포트 지정
npx fx test                          # 테스트 실행 (vitest)
npx fx routes                        # 라우트 테이블 출력
npx fx config                        # 설정 파싱 결과 출력

부트스트랩 순서

app.js 실행
  ├── 1. Config 로드           fuzionx.yaml 파싱
  ├── 2. .env 로드             환경변수 주입
  ├── 3. Bridge 부트            Rust 네이티브 초기화
  │      ★ emit('booting')
  ├── 4. DB 연결                Knex/Mongoose
  ├── 5. 모델 로드              models/ 자동 스캔
  │      ★ emit('booted')
  ├── 6. i18n 로드              locales/ 번역 파일
  ├── 7~12. 자동 스캔           services, middleware, controllers,
  │                             ws, jobs, events
  │      ★ emit('ready')
  └── 13. 서버 시작              startFusionServer(port)
         ★ emit('listening')

라이프사이클 훅

| 훅 | 시점 | 용도 | |---|------|------| | booting | Config 로드 직후 | 서비스 등록, 환경 설정 | | booted | DB + 모델 로드 완료 | 캐시 워밍 | | ready | 모든 초기화 완료 | 준비 로그 | | listening | 서버 시작 완료 | 포트 표시 | | shutdown | SIGTERM/SIGINT | 리소스 정리 |


에러 처리

| 에러 타입 | HTTP | JSON | HTML | |-----------|:----:|------|------| | ValidationError | 422 | { error, fields } | flash + redirect | | ServiceError | 400/403/404 | { error } | 테마 에러 페이지 | | Error (dev) | 500 | { error, stack } | 에러 페이지 + 스택 | | Error (prod) | 500 | { error } | 에러 페이지 |


주요 특징

  • 500K+ RPS — Rust N-API Bridge (libuv + SO_REUSEPORT)
  • 🦀 Rust 네이티브 — HTTP, 세션, CORS, Rate Limit, 암호화 모두 Rust 처리
  • 🎯 MVC 아키텍처 — Controller, Service, Model 분리
  • 📦 자동 스캔 — controllers/, models/, services/ 등 자동 로드
  • 🔌 WebSocket — 네임스페이스 기반 이벤트 라우팅
  • 🗄️ Multi-DB ORM — SQLite, MariaDB, PostgreSQL, MongoDB
  • 🔐 Auth & Session — JWT + 세션 (Memory/Redis)
  • 📡 EventBus — 도메인 이벤트 + Hub 연동 (멀티서버)
  • Scheduler & Queue — Job (cron), Task (큐)
  • 🌍 i18n — 다국어 지원, ctx.t(), locale 자동 감지
  • 🎨 Tera SSR — Rust 기반 템플릿 렌더링
  • 📄 OpenAPI — 자동 Swagger 문서 생성

License

MIT