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 🙏

© 2025 – Pkg Stats / Ryan Hefner

puremvc-express

v0.0.27

Published

Express + PureMVC server foundation

Readme

Intruduction

Puremvc-Express module is a bootstrap kit to help you to build backend service. Especially for developers who are familiar with Puremvc and Express.

Usage

Initial

/** index.js */
const fs = require('fs');
const winston = require('winston');
let { CORE_EVENTS, App } = require('puremvc-express');
let options = require('./options');
let app = new App('test', options);

app.start();
/** options.js */
const winston = require('winston');
const { format } = require('winston');
const fs = require('fs');
const options = {
    httpServer: {
        port: 3000,
        sslPort: 3002,
        credentials: {
            key: fs.readFileSync('./ssl/key.pem', 'utf8'),
            cert: fs.readFileSync('./ssl/cert.pem', 'utf8')
        },
        cors: {
            credentials: true,
            origin: [
                'https://localhost:8080'
            ]
        }
    },
    database: {
        host: 'database.your.domain',
        user: 'dbuser',
        password: 'dbpassword',
        database: 'dbname',
        connectionLimit: 2
    },
    memCache: {
        host: 'redis.your.domain',
        port: 6379
    },
    firebase: {
        apiKey: 'yourFirebaseApiKey',
        authDomain: 'yourAuthDomain.firebaseapp.com',
        databaseURL: 'https://yourFirebaseProjectId.firebaseio.com',
        projectId: 'yourFirebaseProjectId',
        storageBucket: 'yourFirebaseProjectId.appspot.com',
        messagingSenderId: '1234567890',
        appId: '1:1234567890:web:0123456789abcdef',
        measurementId: 'G-0123456789'
    },
    passport: {
        jwt: {
            secret: 'yourJwtSecret',
            signOptions: {
                algorithm: 'algorithm-you-want',
                expiresIn: '900s'
            }
        },
        local: {
            usernameField: 'username',
            passwordField: 'password'
        },
        facebook: {
            clientID: 'facebookID',
            secret: 'facebookSecret',
            callbackURL: 'https://localhost:3000/auth/facebook/callback'
        },
        twitter: {
            consumerKey: 'twitterKey',
            consumerSecret: '',
            callbackURL: ''
        },
        google: {
            consumerKey: 'googleKey',
            consumerSecret: '',
            callbackURL: ''
        }
    },
    session: {
        secret: 'some secret',
        resave: false,
        saveUninitialized: false
    },
    di: {
        m_appData: 'CORE:APP-DATA',
        m_httpServer: 'CORE:HTTP-SERVER',
        m_database: 'CORE:DATABASE',
        m_passport: 'CORE:PASSPORT',
        m_memCache: 'CORE:MEM-CACHE',
        m_logger: 'CORE:LOGGER',
        m_users: 'CORE:USERS'
    },
    logger: {
        level: 'info',
        transports: [
            new winston.transports.Console({
                format: format.combine(
                    format.colorize(),
                    format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
                    format.printf(info => `${info.timestamp} ${info.level} [${info.class}]: ${info.message}`)
                )
            }),
            new winston.transports.File({
                format: format.combine(
                    format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
                    format.prettyPrint(),
                ),
                filename: 'logs/app.log'
            })
        ]
    },
    users: [
        { id: 1, username: 'root', password: 'p4s5w0rD', role: 'admin' },
        { id: 2, username: 'trumpjojo', password: '9453', role: 'author' },
        { id: 3, username: 'ingrid', password: '0908', role: 'people\'s gf' },
        { id: 4, username: 'dora', password: '9ido', role: 'amon' }
    ]
}

export default options;

Dependency Injection

This feature supports inject object which extends {puremvc.Proxy} For example

import { Mediator } from 'puremvc';
import { CORE_EVENTS } from 'puremvc-express';

export default class MyMediator extends Mediator {
    m_logger = undefined;
    logger;
    constructor () {
        super('NAME')
    }

    onRegister () {
        this.sendNotification(CORE_EVENTS.DI, this);
        // Now you can access this.m_logger
    }
}

Object injected depends on values under di of options, corresponding to the string assigned to super method. This feature use retrieveProxy in puremvc class to approach.

    consturctor () {
        super('NAME USE TO INJECT');
    }

Logger

Puremvc-Express uses Winston as logging tool, we wrap it to similar as Log4js. By following example, you can learn how to use it.

class YourClass extends SimpleCommand {
    m_logger = undefined;
    logger;

    constructor () {
        super('NAME');
    }

    execute (notification) {
        this.sendNotification(CORE_EVENTS.DI, this);
        this.logger = this.m_logger.getLogger(this.constructor.name);
        this.logger.info('Hello World');
        //2021-03-02 13:44:24.643 info [YourClass]: Hello World
    }
}

Register your customized Proxy, Mediator, Command

After puremvc-express was constructed, you can access its facade member then register following three commnand to prepare your Proxy, Mediator and Command.

let { CORE_EVENTS, App } = require('puremvc-express');

let app = new App('AppName');
let facade = app.facade;

facade.registerCommand(CORE_EVENTS.INIT_COMMANDS, YourCommandRegistor);
facade.registerCommand(CORE_EVENTS.INIT_MEDIATORS, YourMediatorRegistor);
facade.registerCommand(CORE_EVENTS.INIT_PROXIES, YourProxyRegistor);

app.options = {...options};
app.start();

Serving static files

Puremvc-express defaults serve 'public' folder

Local stretagy of Passport

In order to maping to you user data verifing, please register a command whic listening to CORE_EVENTS.USER_VERIFY.

class UserVerify extends SimpleCommand {
    m_database = undefined;
    m_logger = undefined;
    logger;

    constructor() {
        super()
    };

    async execute(notification) {
        this.sendNotification(CORE_EVENTS.DI, this);
        this.logger = this.m_logger.getLogger('UserVerify');
        let { username, password, done } = notification.body;
        let result = await this.m_database.query(
            `SELECT * from users where username like "${username}";`
        );

        let hit = await result.filter((obj, idx, ary) => {
            return obj.loginID == username;
        })
        if (hit.length && await bcrypt.compare(password, hit[0].password)) {
            done(null, hit[0]);
        } else
            done(null, false);

    }
}

facade.registerCommand(CORE_EVENTS.USER_VERIFY, UserVerify);

Authenticate required path

Puremvc-Express default provides bearer token checking mechanism for every request paths under http://localhost:3000/auth/..., the request header will need bearer token curl -H 'authorization: bearer <token>' ...

Simple Users

This kit provides you a simple way to setup your user list, assign a users key in option json, and assign an array to it.

    users: [
        { "id": 1, "username": "root", "password": "p4s5w0rD", "role": "admin" },
        { "id": 2, "username": "user001", "password": "letmein", "role": "user" },
        { "id": 3, "username": "audit001", "password": "iamwaching", "role": "audit" },
        { "id": 4, "username": "guest001", "password": "takealook", "role": "guest" }
    ]

Included API's

Sign in

This API sends notifcation CORE_EVENTS.AUTH_SIGNIN with parameter req, res, next. If there has the event name registered.

  • Path: /auth/signin
  • Method: POST

Sign up

This API sends notification CORE_EVENTS.AUTH_SIGNUP with parameter req, res, next. If there has the event name registered.

  • Path: /auth/signup
  • Method: POST

Sign JWT

  • Path: /auth/signjwt
  • Method: POST
  • Header: content-type: application/json
  • Body:
    {
        username: 'user', 
        password: 'pwd', 
        payload: {
            your:'data'
        }
    }
  • Return: Bearer token using payload object.
  • CURL:
    curl -H "content-type: application/json" -d '{"username":"root","password":"p4s5w0rD","payload":{"your":"data"}}' localhost:3000/auth/signjwt

Echo message

  • Path: /echo/:message
  • Method: ALL
  • Return: message
  • CURL:
    curl localhost:3000/echo/hello%20world

Authorized echo message

  • Path: /api/echo/:message
  • Method: ALL
  • Header: authorization: bearer <token>
  • Return: message
  • CURL:
    curl -H "authorization: bearer <token>" localhost:3000/api/echo/hello%20world

2 Factor Authorization

Puremve-express's 2FA scheme needs two commands registered via puremvc's registerCommand. Which events was CORE_EVENTS.AUTH_REGISTER_TOTP_SECRET and CORE_EVENTS.AUTH_RETRIEVE_TOTP_SECRET, you have to use these two events to manipulate your totp secret of user.

Options
    {
        auth: {
            twofa: {
                qrCodeFormat: '[png | svg] default: png'
            }
        }
    }
Router middleware
    routerAuth.get('/path/to', passport.authenticate('2fa-totp'), (req,res,next) => yourMethod);)
class RegisterTotpSec extends SimpleCommand {
    //...
    execute(notifcation) {
        let {res, username, totpSecret} = notifcation.body;
        let user = Users.findOne(x => x.username == username);
        user.totpSecret = totpSecret;
        // Your logic here to decide what http code should be return.
        res.status(200).end()
    }
}

class RetrieveTotpSec extends SimpleCommand {
    //...
    execute(notification) {
        let {user, done} = notification.body;
        let dbUser = Users.findOne(x => x.username == user.username);
        user.totpSecret = dbUser.totpSecret;
        done(user);
    }
}

And a resource under /api/auth/2fa, it's means request to this needs with bearer token.

Retrieve QR code.

  • Path: /api/auth/2fa
  • Method: GET
  • Header: authorization: bearer <token>
  • Return: png image
    • Header: set-cookie: <cookie>
  • CURL:
    curl -H "authorizatoin: bearer <token>" localhost:3000/api/auth/2fa

Confirm and register totp secret.

  • Path: /api/auth/2fa
  • Method: POST
  • Header: authorization: bearer <token>, cookie: <cookie>
  • Return: 200
  • CURL:
    curl -H "authorization: bearer <token>" -H "cookie: <cookie>" -XPOST localhost:3000/api/auth/2fa

Verify totp

  • Path: /api/echo/2fa/:message
  • Method: GET
  • Header: authorization: bearer <token>
  • Return: message
  • CURL:
    curl -H "authorization: bearer <token>" -d "code=<code>" localhost:3000/api/echo/2fa/message

Significant events

Puremvc-express fires following events after some specific process done. Before using these events, simply import CORE_EVENTS e.g. let { CORE_EVENTS } from 'puremvc-express'; and listen to it.

  • DATABASE_CONNECTED
    Database connected.
  • INIT_COMMANDS
    Time to register your own commands.
  • INIT_DATABASE_TABLE
    When no tables exists in database.
  • INIT_MEDIATORS
    Time to register your own mediators.
  • INIT_PROXIES
    Time to register your own proxies.
  • PRE_INIT_PROXIES
    Useless event unless you want override logger what we provided.
  • SERVER_STARTED
    HTTP server started, it's time to register your http resource.
  • USER_VERIFY
    Verfiy user via passport's local strategy, listen this event and query your own user database.

Log levels

Winston's default log levels.

  • error
  • warn
  • info
  • http
  • verbose
  • debug
  • silly

Environment preparation

Database

docker run -d --name db \
-p 3306:3306 \
-p 33060:33060 \
-e MYSQL_ROOT_PASSWORD=rootpwd \
-e MYSQL_DATABASE=mydb \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=userpwd \
mariadb \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci \
--default-authentication-plugin=mysql_native_password

Sign your own SSL certificate

refer: https://github.com/FiloSottile/mkcert

docker run \
 -d \
 --rm \
 -e "domain=*.yourdo.main,yourdo.main,127.0.0.1,localhost" \
 --name mkcert \
 -v $(pwd)/ssl/:/root/.local/share/mkcert \
 vishnunair/docker-mkcert