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 🙏

© 2024 – Pkg Stats / Ryan Hefner

interlayer

v0.13.7

Published

Minimalistic and fast ES6 Node.js web server with file monitor

Downloads

90

Readme

interlayer

npm version npm downloads github license Build Status Code Climate

At this point, the server version is still in alpha. You can suggest an idea or report an error here: New issue

The stable version of the server will be implemented after writing all the necessary features and tests to them.

Changelog

CHANGELOG.md

Features

  • Serving Static Content
  • Upload files
  • Auto-reload server on file change (reload on new files not supported)
  • Clusterization
  • Postgres\mysql\redis built-in DAL's for data storage
  • Mailgun\sparkpost\smtp build-in mail sender packages
  • Localization
  • WebScoket

Install

npm install --save interlayer

or

yarn add interlayer

Project tree example

  • /node_modules/
  • package.json
  • /files/ - this folder will be served by config.serve
    • index.html
    • style.css
    • script.js
    • /images/
      • logo.jpeg
  • index.js
  • /i18n/
    • en-US.json
  • /middleware/
    • auth.js
  • /modules/
    • weather.js
  • config.json

Request processing steps

  1. request preparation - filling with functions and objects, see here
  2. search for a module. if not found - search for a file in config.serve and give it to the client
  3. if the module is found, parse the data if the request method is POST
  4. for all matching triggers run middlewares, more see here
  5. if the found module has a prerun in its meta, run it, more see here
  6. finally launch our module(that can use any data from here), in response we wait for see here
  7. convert to json text if meta.toJson || meta.contentType == 'json' || headers['Content-Type'] == 'application/json'
  8. done

/modules/weather.js example

exports._obtain = {
    checkIp: true,
    prerun(request, moduleMeta, cb){

    }
};
exports.obtain = (request, cb){
    cb(null, 'good weather');
};

/middleware/auth.js example

exports.triggers = {
    'meta.checkIp': (request, moduleMeta, cb) => {
        if(request.ip === '127.0.0.1'){
            return cb('bad ip');
        }
        cb();
    },
    'request.params.city': (request, moduleMeta, cb) => {
        if(request.params.city === 'london'){
            return cb('bad weather');
        }
        cb();
    }
}

Variants to start server

const config = {
    port: 80,
    serve: ['files']
};
require('interlayer')(config);

or

require('interlayer')('config.json');

or

const server = require('interlayer').server();
server
    .setRootPath(__dirname)
    .loadConfigFile('config.json')
    .start()

config object or config.json file configuration

Avaliable params:

Avaliable properties in config object or config.json file

| Property | Default | Type | Description | | ------ | ------ | ------ | ------ | | port | 8080 | Number | Web server port number | | secure | --- | Object | SSL configuration object with paths to files: {key:'',cert:''} | initPath | ./ | String | Web server root path | | logPath | ./ | String | Path to create the logs.log file | | timeout | 60(sec) | Number | Timeout in seconds, then user will see {error: 'TIMEOUT'} Note, execution of the method is not interrupted | | workers | 1 | Number | Number of instances for load balancing. If number more than 1, uses node.js cluster | | restartOnChange | false | Boolean | Flag determine is server will restart automatically when files in the folder with modules was changed | | useDals | --- | Object[dalName] = dalConfig | The configuration object for dal modules to be used. Supports redis(redis:{}), mysql(mysql:{} and default will be {host: '127.0.0.1',user: 'root'}), postgress(postgress:{} and default will be {host: '127.0.0.1',user: 'root'}). For config built-in redis see here🌍, mysql see here🌍, postgres see here🌍 (Example of dal's config: useDals: {mysql: {port: 6375, user: 'admin'}, redis: {}}) | | useEmailSenders | --- | Object[emailSenderName] = emailSenderConfig | The configuration object for the mail senders to be used. Supports mailgun(mailgun:{}), sparkpost(sparkpost:{}), smtp(smtp:{}). For config built-in mailgun see here🌍, sparkpost see here🌍, smtp see here🌍| | serve | --- | Array[Strings[]] | An array folders to serve. Priority over the last folder | | modules | ./modules | Array[Strings[]] | An array folders with modules. Priority over the last folder. (Default directory is './modules' unless otherwise specified.) How to create | | views | ./files | Array[Strings[]] | An array of folders with files, which you can be uses as templates, or returned through the api(by using request.getView). Priority over the last folder. (Default directory is './files' unless otherwise specified.) | | i18n | ./i18n | Array[Strings[]] | An array of folders with localization files. Priority over the last folder. (Default directory is './i18n' unless otherwise specified.) How to create | | dals | --- | Array[Strings[]] | An array of folders with your dal(Data Access Layer) modules. Priority over the last folder. How to create | | emailSenders | --- | Array[Strings[]] | An array of folders with your email senders. Priority over the last folder. How to create | | middleware | --- | Array[Strings[]] | An array of folders with middlewares. Priority over the last folder. How to create | | middlewareOrder | --- | Array[Strings[]] | An array with ordered names of middlewares | | middlewareTimeout | 10(sec) | Number | Timeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted | | skipDbWarning | false | Boolean | Skip warning in console if useDals not defined in config | | defaultHeaders | --- | Object[headerName] = headerValue | An object with default headers, which have to be added to the every response | | debug | false | Boolean | Allow to display log.d in console and add to the logs.log file | | instantShutdownDelay | 1500(ms) | Number | Delay in milliseconds after server will shutdown on process SIGINT or SIGTERM signal, or process message: shutdown | | retryAter | 10(sec) | Number | Time in seconds for Retry-After response header with server HTTP 503 status. Works until instantShutdownDelay | | noDelay | true | Boolean | Flag to enable/disable Nagle algoritm for all connections. See here🌍 | | websocket | --- | Boolean/Object | Start websocket. If true then on the same port as server, except as stated in the Object. See here🌍. Initialized server instance can be found in initFunction simpleRequest.websocket| | useHttpErrorFiles | false | Boolean | Possible errors will be given as files if they are found in directories specified in addViewPath | | skipParsePost| false | Boolean | Skip parse POST | | formidableOptions| {} | Object | Set formidable options on parse data when headers['content-type'] not null, list of options see here🌍 | | startInits | true | Boolean | Start functions that were added via Module app.setInit | | disableLogFile | false | boolean | Disable to write log file, the console.log and others continues to be written by the console |


Intrlayer instance configure

let serverInstance = require('interlayer').server();

How to use

let server = require('interlayer').server();
server.setRootPath(__dirname);
server.loadConfigFile('config.json');
server.start();

Avaliable methods:

| Property | Default | Type | Description | | ------ | ------ | ------ | ------ | | start(configObject / null) | --- | Object | Starting the server with/without the configuration object | | loadConfigFile(path) | --- | String | Initializing configuration from file | | setConfig(configObject) | --- | Object | Setting a configuration from an object | | getConfig(configObject) | --- | Object | Get the resulting configuration | | setRootPath(path) | ./ | String | Set root directory | | setLogPath(path) | ./ | String | Set a directory of the log file | | setPort(port) | 8080 | Number | Set the server port | | setSecure(secureObject) | --- | Object | SSL configuration object with paths to files: {key:'',cert:''} | | setWorkersCount(workerNumber) | 1 | Number | Number of instances for load balancing. If number more than 1, uses node.js cluster | | setTimeout(timeout) | 60(sec) | Number | Timeout in seconds, then user will see {error: 'TIMEOUT'} Note, execution of the method is not interrupted | | setDefaultHeaders(headersObject) | --- | Object | An object with default headers, which have to be added to the every response | | setRestartOnChange([true / false]) | false | Boolean | Boolean value determine is server will restart automatically when files in the folder with modules was changed | | setSkipDbWarning([true / false]) | false | Boolean | Skip warning in console if useDals not defined in config | | setDebugMode([true / false]) | false | Boolean | Allow to display log.d in console | | setNoDelay([true / false]) | true | Boolean | Flag to disable/enable Nagle algoritm for all connections. See here🌍 | | setInstantShutdownDelay(timeout) | 1500(ms) | Number | Delay in milliseconds after server will shutdown on process SIGINT or SIGTERM signal, or process message: shutdown | | setRetryAter(timeout) | 10(sec) | Number | Time in seconds for Retry-After response header with server HTTP 503 status. Works until config.instantShutdownDelay | | addEmailSender(emailSenderName, emailSenderConfig) | --- | String, Object | Add an email sender. Priority over the last folder. How to create | | addDalPath(path, [path, [path]]) | --- | String | Add path to DAL's(Data Access Layer) modules. Priority over the last added path | | addDal(dalName, dalConfig) | --- | String, Object | The configuration(dalConfig) for dal module(dalName) to be used. Out of the box is available redis(for use specify redis, {}), mysql(for use specify mysql, {} and default dalConfig will be {host: '127.0.0.1',user: 'root'}), postgress(for use specify postgress, {} and default dalConfig will be {host: '127.0.0.1',user: 'root'}). For configure redis see here🌍, mysql see here🌍, postgres see here🌍 | | addMiddlewarePath(path, [path, [path]]) | --- | String, ... | Add path to middleware modules. Priority over the last added path. How to create | | setMiddlewareOrder(middlwareName, middlwareName) | --- | String or Array[Strings] | An array(or arguments) with ordered names of middlewares | | setMiddlewareTimeout(timeout) | 10(sec) | Number | Timeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted | | addModulesPath(path, [path, [path]]) | ./modules |String, ... | Add path to modules. Priority over the last added path. (Default directory is './modules' unless otherwise specified.) How to create | | addI18nPath(path, [path, [path]]) | ./i18n | String, ... | Add path to localization files. Priority over the last added path. (Default directory is './i18n' unless otherwise specified.) How to create | | addServePath(path, [path, [path]]) | --- | String, ... | Add path to Serving Static Content. Priority over the last added path | | addViewPath(path, [path, [path]]) | ./files | String, ... | Folders with files, which you can be uses as templates, or returned through the api(by using request.getView). Priority over the last folder. (Default directory is './files' unless otherwise specified.) | | setWebsocketConfig(websocket) | --- | Boolean/Object | Start websocket. If true then on the same port as server, except as stated in the Object. See here🌍. Initialized server instance can be found in initFunction simpleRequest.websocket | | setUseFilesAsHTTPErrors([true / false]) | false | Boolean | Possible errors will be given as files if they are found in directories specified in addViewPath | | setSkipParsePost([true / false]) | false | Boolean | Set skip parse POST | | setFormidableOptions({}) | {} | Object | Set formidable options on parse data when headers['content-type'] not null, list of options see here🌍 | | disableInits([true / false]) | true | Start functions that were added via Module app.setInit | | disableLogFile([true / false]) | false | boolean | Disable to write log file, the console.log and others continues to be written by the console |


Module creation

Example of modules/myModule.js

const app = require('interlayer').module();
exports.module = app;

let log = app.getLog('myModuleId');
app.setMeta({asJson: true});
app.setInit((request, requestCallback)=>{
    log.i('Module inited');
    requestCallback();
});

app.addMethod('myMethod', {toJson: true}, (request, requestCallback)=>{
    let fullLog = request.modifyLog(log);
    log.i('I am log without requestId but with myModuleId');
    request.log.i('I am log with requestId but without myModuleId');
    fullLog.i('I am log with requestId and with myModuleId');
    requestCallback(null, {ok: true}, 200, {}, false);
});//Could be called in the path of /myModule/myMethod

Avaliable app methods

const app = require('interlayer').module();

| Method | Property types | Description | | --- | --- | --- | | getLog(name) | String | Get the object to output messages to the console. Object of {i:funcion, e:function, d: function, w: function, c: function} type | | setMeta(metaObject) | Object | Set the default parameters for all methods of this module. metaObject | | setInit(initFunction)| Function | Set the function to initialize the module at server start. initFunction | | addMethod(methodUrl, [methodMeta,] methodFunction)| String, [Object,] Function | Adds a new method with/without info(meta). methodMeta and methodFunction| | add(methodUrl, [methodMeta,] methodFunction)| String, [Object,] Function | Alias for addMethod | | setMethodInfo(methodUrl, methodMeta)| String, Object | Sets info(meta) for method. methodMeta | | info(methodUrl, methodMeta)| String, Object | Alias for setMethodInfo | | getMethod(methodUrl)| String | Returns the method function | | getMethodInfo(methodUrl, [withGlobalMeta])| String[, Boolean] | Returns method info(meta) |

initFunction(simpleRequest)

  • simpleRequest.url - Empty string
  • simpleRequest.headers - Empty object
  • simpleRequest.DAL - DAL objects if initialised
  • simpleRequest.config - Configuration object
  • simpleRequest.websocket - websocket server instanse if initialised

... and functions as in methodFunction request except getResponse, getRequest and other http request methods See here🌍

metaObject and methodMeta default paramerets

| Key | Type | Description | | --- | --- | --- | | default = methodFunction | Function | Module(not method) function, can be used to output HTML, Available at /moduleUrl. Only for metaObject. methodFunction | | html = methodFunction | Function | Same as default. Only for metaObject | | find = methodFunction | Function | The method search function is only triggered if no other methods have been processed. Only for metaObject. See methodFunction | | path | String | Changes methodUrl to path | | addToRoot | Boolean | Skip moduleUrl and use methodUrl or path as url to method | | alias | String | Alias path to method | | timeout | Number | Seconds until HTTP 408(Request timeout) | | noDelay | Boolean | Disable/enable the use of Nagle's algorithm. See here🌍 | | middlewareTimeout | Number | Timeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted | | prerun = prerunFunction | Function | Function or link to function which will be runned before method. May be usefull for preparing request. prerunFunction | | toJson | Boolean | Convert response to JSON string | | contentType | String | Same as toJson if contentType==json | | skipRequestLog | String | Skip request log output | | hidden | Boolean | Skip method from return information while calling request.getMethodsInfo| | skipParsePost | Boolean | Skip parse POST |

prerunFunction(request, moduleMeta, requestCallback)

methodFunction(request, requestCallback):

request: | Methods | Property types | Description | | --- | --- | --- | | modifyLog(log) | Object | Add to log object requestId for log created with global.logger | | getView(file, callback) | String, Function | Return file in callback from paths specified in config.views or in server.setViewPath(). callback = (error, data) | | getViewSync(file) | String | Synchronous request.getView | | getFile(file, callback) | String, Function | Return file as is. callback = (error, data, {'Content-type':''}) | | addCookies(key, value) | String, String | Set coockie for response | | rmCookies(key) | String | Remove coockie from responce | | i18n(key[, defaultValue]) | String[, String] | Return translate for key or return defaultValue| | obtainI18n() | --- | Return object with languages | | getMethodsInfo(showHidden) | Boolean | Returns all methods (except hidden methods if showHidden is not specified) | | lockShutdown() | --- | Blocks the termination of the process until the request is completed | | unlockShutdown() | --- | Unlock the termination of the process | | getResponse() | --- | Returns the original responce | | getRequest() | --- | Returns the original request | | error(text) | String | Returns 503 http code | | end(text[, code[, headers[, type]]]) | String[, Number[, Object[, String]]] | Returns code http code with text(as binary if type==bin) and headers|

request: | Property | Type | Description | | --- | --- | --- | | config | Object | An object of configuration specified at start of server | | ip | String | Client ip adress | | url | String | Request url | | path | String | Request path(module/method) | | method | String | Uppercased type of request - POST|GET|... | | isPost | Boolean | true|false | | params | Object | An object of parsed GET params | | post | Object | An object of parsed POST params(with formidable🌍) | | files | Object | An object of uploaded files(with formidable🌍) | | cookies | Object | An object of parsed cookies | | headers | Object | An object of request headers | | DAL | Object | An object with DALs, which was initialized by config.useDals or server.addDal() | | mail | Object | An object with mail senders, which was initialized by config.useEmails or server.addEmailSender() | | id | String | requestId | | log | Object | The same as global.logger.create(moduleID), but with requestID included(not include moduleID) | | helpers | Object | requiest.helpers |

request.helpers

| Methods | Property types | Description | | --- | --- | --- | | helpers.generateId() | --- | Geneate 8-character identifier(a-zA-Z0-9) | | helpers.toJson(obj) | * | Convert obj to JSON string | | helpers.clearObj(obj, toRemove) | Object, Array | Delete parameters of obj from toRemove array of strings | | helpers.isBoolean(val) | * | Check is val string is Boolean(true|false) | | helpers.JSV(json, schema, envId) | Object, Object, String | See here🌍. Create environment with envId and call validate with json and schema | helpers.mime() | Object | return mime type by file extension or fallback or 'application/octet-stream' |

requestCallback(error, data, httpCode, responseHeaders, isBinary)

  • error - null or undefined or String or Object
  • data - null or String or Object or Binary(if isBinary = true)
  • httpCode - null or Number See here🌍
  • responseHeaders - null or Object See here🌍 If Content-Type = application/json then data will be returned as JSON
  • type - null or 'bin'. If 'bin' then data will be returned as Buffer

Global objects added

global.logger

Object to create log with global.logger.create(logName) or global.logger(logName) were logName is String. log avaliable methods: | Methods | Description | | --- | --- | | i | Parameters same as for console.log. See here🌍 | | w | Parameters same as for console.warn. See here🌍 | | e | Parameters same as for console.error. See here🌍 | | d | Parameters same as for console.debug. See here🌍 | | c | Parameters same as for console.log. See here | Note that this type of logging don't allow to track the request id.

To have ability track the request id use the request.modifyLog method:

let log = global.logger.create('moduleID');
exports.myMethod = (request, cb)=>{
    let log = request.modifyLog(log);
}

global.intervals

Object with methods: | Methods | Property types | Description | | --- | --- | --- | | add(function, timeout) | Function, Number | Return key | | add(function, timeout = {year: '*', month: '*', date: '*', day: '*', hour: '*', minute: '*', second: '*'}) | Function, Object | Any of the parameters can be "*" or "2020,2040" - the options are listed in commas. Return key | | del(key) | String | Remove by key | | disable(key, flag) | String, Boolean | Disable/Enable interval by key and flag | | enable(key) | String | Enable interval by key | The startup interval is every second. If the start conditions (timeout) match, function is called with a parameter as a function to delete the interval - similar call to global.intervals.del(key).

Often used inside the function initFunction

Remember, if at server startup config.startInits = false or disableInits(false) then functions added via setInit(initFunction) will not be added to global.intervals


Features

Logging:

const log = global.logger('moduleID');
log.i(); // Usual log - displayed in green
log.w(); // Warn - displayed in yellow
log.e(); // Error - displayed in red
log.c(); // Critical error - displayed in white

Note that this type of logging don't allow to track the request id. To have ability track the request id use the request.modifyLog method:


const log = global.logger('moduleID');
exports.myMethod = (request, cb)=>{
    let log = request.modifyLog(log);
}

Or use the request.log instead, if 'moduleID'(identifier specified in global.logger.create function) not required.

let log = global.logger.create('moduleID');
exports.myMethod = (request, cb)=>{
    let log = request.log;
}

---

Use dals:

request.DAL.redis.get('somekey', (err, data) => {
    if(err){
        request.log.e('redis.get somekey', err);
        return cb(err);
    }
    ...
});
request.DAL.mysql.query('SELECT * FROM users WHERE login = ?', ['admin'], (err, data, fields) => {
    if(err){
        request.log.e('mysql.query', err);
        return cb(err);
    }
})

Use email senders

request.mail.mailgun.send({}, callback) -> see params here https://documentation.mailgun.com/api-sending.html#sending
request.mail.sparkpost.send({}, callback) -> see params here https://developers.sparkpost.com/api/transmissions.html#header-transmission-attributes
//or use initialized senders as you want
request.mail.mailgun.client -> https://www.npmjs.com/package/mailgun-js
request.mail.sparkpost.client -> https://www.npmjs.com/package/sparkpost

Create dal

Example of dals/nameofdal.js Then you can add nameofdal to config.useDals array (ex: config.useDals = {nameofdal: {...config}};)

// init is not required
exports.init = (config, dalConfig) => {

};

// but methods is required
exports.methods = {
    get: () => {},
    set: () => {}
}

Create email sender

Example of emailSenders/nameofsender.js Then you can add nameofsender to config.useEmailSenders array (ex: config.useEmailSenders = {nameofsender: {...config}};)

// init is required
exports.init = (config, emailConfig) => {

};

// send is required
exports.send = (email, cb)=>{

}

Create middleware

Example of middleware/session.js


exports.triggers = {
    'meta.checkSessions': 'checkSession', // you can specified string - name of function exported in module
    'request.params.test': exports.test // also you can specified function or method of object
};

// but methods is required
// request context and callback described in module
exports.checkSession = (request, moduleMeta, cb) => {

};

exports.test = (request, moduleMeta, cb) => {

};

or

exports.run = (request, moduleMeta, cb)=>{

};
// this creates
// exports.triggers = {
//     '*': exports.run
// };

Localization

Example of i18n/en.js

Note! You have to use double quotes, instead single quotes, because it's json file These are the actual keys used for error output.

{
    "Not found": "Nothing found, 404, Bill Gates site",
    "<center>Error 404<br>Not found</center>": "<h1>404 HTTP error.<h1>Not found</center>",
    "Service Unavailable. Try again another time.": "503 HTTP error."
}