Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nodecaf

Package Overview
Dependencies
Maintainers
1
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nodecaf - npm Package Compare versions

Comparing version 0.13.0-rc2 to 0.13.0

44

lib/api.js

@@ -30,12 +30,2 @@ const assert = require('assert');

function normalizeHandler(func){
if(func.constructor.name === 'AsyncFunction')
return func;
return input => new Promise((resolve, reject) => {
try{ func(input); resolve(); }
catch(err){ reject(err); }
});
}
function matchRoute(method, path, params){

@@ -94,3 +84,3 @@ // this => API

this.fallbackRoute = normalizeHandler(handler.bind(this.context));
this.fallbackRoute = handler.bind(this.context);
};

@@ -112,3 +102,3 @@

const nmHandler = normalizeHandler(handler.bind(this.context));
const nmHandler = handler.bind(this.context);

@@ -132,5 +122,4 @@ this.routes[route] = true;

input = {
...app.global, conf: app.conf, cookies: {}, headers: {},
query: {}, ...input, params, log: app.log, signedCookies: {}, method,
path
...app.global, conf: app.conf, cookies: {}, headers: {}, query: {},
...input, params, log: app.log, signedCookies: {}, method, path
};

@@ -158,21 +147,16 @@

if(!handler){
res.status(404).end();
return res.ended;
}
try{
res.notFound(!handler);
input.body = new RequestBody(input);
if(app._autoParseBody && !input.websocket)
try{
input.body = new RequestBody(input);
if(app._autoParseBody && !input.websocket)
input.body = await input.body.parse();
}
catch(err){
res.status(400).end();
app.log.warn({ ...reqInfo, err });
return res.ended;
}
parseSignedCookies(app.conf.cookie, input);
parseSignedCookies(app.conf.cookie, input);
handler(input).catch(err => handleError(err, input));
await handler(input);
}
catch(err){
handleError(err, input);
}

@@ -179,0 +163,0 @@ return res.ended;

@@ -5,20 +5,23 @@

function readStream(){
var complete, buffer = [];
let complete, buffer = [];
this.stream.on('data', chunk => buffer.push(chunk));
const fbto = setTimeout(() => {
this.stream.emit('error', new Error('Client took too long before starting to send request body'), 408)
}, 3000);
this.stream.on('data', chunk => {
clearTimeout(fbto);
buffer.push(chunk);
});
this.stream.on('close', () => {
buffer = null;
this.stream.removeAllListeners('aborted');
this.stream.removeAllListeners('data');
this.stream.removeAllListeners('end');
this.stream.removeAllListeners('error');
this.stream.removeAllListeners('close');
this.stream.removeAllListeners();
});
this.stream.on('aborted', () =>
this.stream.emit('error', new Error('Request aborted by the client')));
return new Promise((resolve, reject) => {
this.stream.on('aborted', () =>
this.stream.emit('error', new Error('Request aborted by the client')));
this.stream.on('end', () => {

@@ -29,4 +32,4 @@ !complete && resolve(Buffer.concat(buffer));

this.stream.on('error', err => {
!complete && reject(err);
this.stream.on('error', (err, code) => {
!complete && reject(this.res.error(code ?? 400, err.message));
complete = true;

@@ -38,7 +41,11 @@ });

async function read(parse){
let out = await readStream.call(this);
if(typeof parse == 'function')
out = parse(out);
try{
if(typeof parse == 'function')
out = parse(out);
}
catch(err){
throw this.res.error(400, 'Invalid format');
}

@@ -45,0 +52,0 @@ return out;

import { Server } from 'http';
import WebSocket from 'ws';

@@ -124,6 +125,8 @@ declare namespace Nodecaf {

params: Record<string, string>
/** The remote address of the client performing the request. Standard proxy headers are considered.*/
/** The remote address of the client performing the request. Standard proxy headers are considered. */
ip: string,
/** Store `value` under the name `key` in the handler args for the lifetime of the request.*/
keep: (key: string, value: unknown) => void
/** Store `value` under the name `key` in the handler args for the lifetime of the request. */
keep: (key: string, value: unknown) => void,
/** Accept WebSocket connection on upgrade. Only available when `opts.websocket` is set. */
websocket: () => Promise<WebSocket.WebSocket>
} & Record<string, unknown>;

@@ -142,3 +145,14 @@

type Route = {
/** Endpoint HTTP method */
method: string,
/** Endpoint path starting with slash (e.g `/foo/:bar`) */
path: string,
/** Function to be called when endpoint is triggered */
handler: RouteHandlerArgs
};
type AppOpts = {
/** An array with your api endpoints */
routes: Route[],
/** A function to build your api endpoints */

@@ -156,7 +170,10 @@ api?: (this: Nodecaf, methods: Nodecaf.EndpointBuilders) => void,

conf?: Nodecaf.ConfObject | string,
/** whether request bodies should be parsed for known mime-types (json, text, urlencoded) */
/** Whether request bodies should be parsed for known mime-types (json, text, urlencoded). Defaults to `false`. */
autoParseBody?: boolean,
/** A function that returns a custom HTTP server to be used by the app */
server?: (args: Nodecaf) => Server
server?: (args: Nodecaf) => Server,
/** Whether to handle websocket upgrade requests. Defaults to `false`. */
websocket?: boolean
}
}

@@ -184,2 +201,15 @@

/** Define a POST endpoint to `path` that when triggered will run the `handler` function */
static post: (path: string, handler: Nodecaf.RouteHandler) => Nodecaf.Route
/** Define a PUT endpoint to `path` that when triggered will run the `handler` function */
static put: (path: string, handler: Nodecaf.RouteHandler) => Nodecaf.Route
/** Define a PATCH endpoint to `path` that when triggered will run the `handler` function */
static patch: (path: string, handler: Nodecaf.RouteHandler) => Nodecaf.Route
/** Define a GET endpoint to `path` that when triggered will run the `handler` function */
static get: (path: string, handler: Nodecaf.RouteHandler) => Nodecaf.Route
/** Define a DELETE endpoint to `path` that when triggered will run the `handler` function */
static del: (path: string, handler: Nodecaf.RouteHandler) => Nodecaf.Route
/** Define a fallback `handler` function to be triggered when there are no matching routes */
static all: (handler: Nodecaf.RouteHandler) => Nodecaf.Route
/** A user controlled object whose properties wil be spread in route handler args. */

@@ -186,0 +216,0 @@ global: Record<string, unknown>

@@ -33,2 +33,3 @@ const

this._apiSpec = opts.api;
this._routes = opts.routes;
this._startup = opts.startup;

@@ -70,2 +71,3 @@ this._shutdown = opts.shutdown;

this._api = new API(this, this._apiSpec);
opts.routes?.forEach(r => this._api.addEndpoint(r.method.toLowerCase(), r.path, r.handler));
}

@@ -126,7 +128,5 @@

if(this._wss)
this._wss.close();
let actualHTTPClose
if(this._server)
var actualHTTPClose = new Promise(done => this._server.close(done));
actualHTTPClose = new Promise(done => this._server.close(done));

@@ -198,1 +198,5 @@ // Handle exceptions in user code to maintain proper app state

}
http.METHODS.forEach(m => module.exports[m.toLowerCase()] = function(path, handler, opts){
return { ...opts, method: m, path, handler };
});

@@ -44,2 +44,6 @@ const { sign } = require('cookie-signature');

end(body){
if(this.finished)
this.log.warn({ err: new Error('Called `res.end()` after response was already finished') });
body && this.write(body);

@@ -46,0 +50,0 @@ this.resStream.end();

@@ -27,7 +27,3 @@ const cookie = require('cookie');

// TODO MISSING THE RESPONSE OBJECT
const res = new ServerResponse(req);
const [ path, query ] = req.url.split('?');

@@ -47,5 +43,5 @@

wss.on('close', function() {
app._server.on('close', function() {
clearInterval(interval);
this.clients.forEach(ws => ws.terminate());
wss.clients.forEach(ws => ws.terminate());
});

@@ -58,3 +54,2 @@

module.exports = { buildWebSocketServer };
{
"name": "nodecaf",
"version": "0.13.0-rc2",
"version": "0.13.0",
"description": "Nodecaf is a light framework for developing RESTful Apps in a quick and convenient manner.",

@@ -45,3 +45,4 @@ "main": "lib/main.js",

"cors": "^2.8.5",
"golog": "^0.5.0"
"golog": "^0.5.0",
"ws": "^8.6.0"
},

@@ -48,0 +49,0 @@ "devDependencies": {

# [Nodecaf](https://gitlab.com/GCSBOSS/nodecaf)
> Docs for version v0.12.x.
> Docs for version v0.13.x
Nodecaf is a light framework for developing RESTful Apps in a quick and convenient manner.
Using Nodecaf you'll get:
- Familiar middleware style routes declaration
- Useful [handler arguments](#handlers-args).
- Built-in [settings file support](#settings-file) with layering.
- [Logging functions](#logging).
- Seamless support for [async functions as route handlers](#async-handlers).
## Highlights
- URL path pattern routing
- Each route accepts a single function as an argument ([see why](#simple-routing))
- Useful [handler arguments](#handlers-args)
- Optional automatic body parsing for popular formats
- Support for [Settings files](#settings-file) or objects with straightforward layering
- [Stdout logging](#logging)
- Seamless support for [async functions as route handlers](#async-handlers)
- [Uncaught exceptions](#error-handling) in routes always produce proper REST
responses.
- Built-in [assertions for readable RESTful error handling](#rest-assertions).
- Function to [expose global objects](#expose-globals) to all routes (eg.:
database connections).
- Shortcut for [CORS Settings](#cors) on all routes.
- Functions to [describe your API](#api-description) making your code the main
source of truth.
- Helpful [command line interface](https://gitlab.com/GCSBOSS/nodecaf-cli).
responses
- [Assertions for readable RESTful error handling](#rest-assertions)
- Facility for [exposing global objects](#expose-globals) to all routes (eg.:
database connections)
- [CORS Settings](#cors)
- Allow calling all endpoints programmatically with complete feature parity (awesome for unit testing)
- Helper to [handle WebSocket](#handling-websocket) connections.
- Helpful [command line interface](https://gitlab.com/GCSBOSS/nodecaf-cli)

@@ -57,5 +60,5 @@ ## Get Started

// Define routes and a list of middleware functions (async or regular no matter).
get('/foo/:f/bar/:b', Foo.read, Bar.read);
post('/foo/:f/bar', Foo.read, Bar.write);
// Define routes with their handler functions (async or regular no matter).
get('/foo/:f/bar/:b', FooBar.read);
post('/foo/:f/bar', FooBar.write);
// ...

@@ -167,3 +170,3 @@

```js
function({ method, path, res, query, params, body, conf, log, headers, call }){
function({ method, path, res, query, params, body, conf, log, headers, call, websocket }){
// Do your stuff.

@@ -278,2 +281,3 @@ }

| app | info | The application configuration has been reloaded |
| event | warn | Called `res.end()` after response was already finished |

@@ -296,6 +300,5 @@ Additionally, you can filter log entries by level and type with the following

Nodecaf brings the useful feature of accepting async functions as route handlers
with zero configuration. All rejections/error within your async handler will be
gracefully handled by the same routine the deals with regular functions. You will
be able to avoid callback hell without creating bogus adapters for your promises.
Nodecaf accepts async functions as well as regular functions as route handlers.
All rejections/error within your async handler will be gracefully handled.
You will be able to avoid callback hell without creating bogus adapters for your promises.

@@ -418,47 +421,19 @@ ```js

### API Description
### Handling Websocket
Nodecaf allows you to descibe your api and it's functionality, effectively turning
your code in the single source of truth. The described API can later be used to
[generate](https://gitlab.com/GCSBOSS/nodecaf-cli#open-api-support) an
[Open API](https://www.openapis.org/) compatible
document.
Use the `websocket` handler argument to expect a Websocket upgrade.
In `lib/api.js` describe your API as whole through the `info` parameter:
```js
module.exports = function({ get, info }){
get('/my/ws/endpoint', async ({ websocket }) => {
info({
description: 'My awesome API that foos the bars and bazes the bahs'
});
// Wait till ws connection is open
const ws = await websocket();
get('/my/thing/:id', function(){
// ...
ws.on('message', m => {
ws.send('Hello World!');
ws.close();
});
}
})
```
The `info` funciton expects an object argument on the OpenAPI
[Info Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#infoObject)
format. If not defined the `title` and `version` keys will default to your server's.
Describe your API endpoints by chaining a `desc` method to each route definition.
```js
module.exports = function({ get }){
get('/my/thing/:id', function(){
// ...
}).desc('Retrieves a thing from the database\n' +
`Searches the database for the thing with the given :id. Returns a
NotFound error in case no thing is found.`);
}
```
The `desc` method takes a single string argument and uses it's first line (before `\n`)
to set the
[Operation object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject)'s
`summary` property and the rest of the text to set the `description` (CommonMark).
### Other Settings

@@ -465,0 +440,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc