Comparing version 0.12.5 to 0.13.0-rc1
160
lib/api.js
@@ -10,3 +10,3 @@ const assert = require('assert'); | ||
function pathToRegexp(path){ | ||
let r = { params: [] }; | ||
const r = { params: [] }; | ||
let regexp = ''; | ||
@@ -41,20 +41,6 @@ | ||
function buildStack(context, chain){ | ||
chain.length > 1 && | ||
context.log.warn('Building routes with multiple functions is deprecated. ' | ||
+ 'This behavior is going to be dropped on v0.13.0. Use `call` instead.'); | ||
let nextHandler = null; | ||
return chain.reverse().map(h => { | ||
assert(typeof h == 'function', | ||
new TypeError('Invalid route option \'' + typeof h + '\'')); | ||
h = normalizeHandler(h.bind(context)); | ||
h.next = nextHandler; | ||
return nextHandler = h; | ||
}).reverse(); | ||
} | ||
function matchRoute(method, path, params){ | ||
// this => API | ||
let route = method + ' ' + path; | ||
const route = method + ' ' + path; | ||
if(route in this.static) | ||
@@ -64,4 +50,4 @@ return this.static[route]; | ||
if(this.dynamic[method]) | ||
for(let r of this.dynamic[method]){ | ||
let match = r.regexp.exec(path); | ||
for(const r of this.dynamic[method]){ | ||
const match = r.regexp.exec(path); | ||
if(match){ | ||
@@ -76,18 +62,2 @@ r.params.forEach( (p, i) => params[p] = match[i + 1]); | ||
function runHandler(input, handler, done){ | ||
if(input.res.stackAborted) | ||
return; | ||
input.next = handler.next | ||
? () => { | ||
input.log.warn('`next()` is deprecated. ' | ||
+ 'This parameter will be dropped on v0.13.0. Use `call` instead.'); | ||
return runHandler(input, handler.next, done) | ||
} | ||
: () => done(); | ||
handler(input).catch(err => handleError(err, input.res)); | ||
} | ||
function parseSignedCookies(cconf, input){ | ||
@@ -97,4 +67,4 @@ if(!cconf?.secret) | ||
for(let key in input.cookies){ | ||
var val = cookieSignature.unsign(input.cookies[key], cconf?.secret); | ||
for(const key in input.cookies){ | ||
const val = cookieSignature.unsign(input.cookies[key], cconf?.secret); | ||
if(val){ | ||
@@ -107,20 +77,2 @@ input.signedCookies[key] = val; | ||
function fork(input, func){ | ||
// this => app | ||
this.log.warn('`fork` is deprecated. This method will be dropped on v0.13.0.'); | ||
return new Promise((resolve, reject) => { | ||
func = normalizeHandler(func.bind(this)); | ||
func({ ...input, next: resolve }).catch(reject); | ||
}); | ||
} | ||
function buildHook(hookName, ...chain){ | ||
chain.length > 1 && | ||
this.context.log.warn('`pre()` and `pos()` hooks are deprecated. ' | ||
+ 'Those methods will be dropped on v0.13.0. Use `call` instead.'); | ||
let stack = buildStack(this.context, chain); | ||
this[hookName] = stack[0]; | ||
stack.slice(-1)[0].tail = true; | ||
} | ||
module.exports = class API { | ||
@@ -136,3 +88,3 @@ | ||
// Generate HTTP verb shortcut route methods | ||
let proxy = METHODS.reduce( (o, m) => | ||
const proxy = METHODS.reduce( (o, m) => | ||
({ ...o, [m.toLowerCase()]: this.addEndpoint.bind(this, m.toLowerCase()) }), {}); | ||
@@ -143,28 +95,25 @@ | ||
proxy.all = (...chain) => { | ||
proxy.all = handler => { | ||
assert(!this.fallbackRoute, new Error('Route for \'ALL\' is already defined')); | ||
assert(chain.length > 0, new Error('Route is empty at \'ALL\'')); | ||
let stack = buildStack(this.context, chain); | ||
stack.slice(-1)[0].tail = true; | ||
this.fallbackRoute = stack[0]; | ||
assert(typeof handler == 'function', | ||
new TypeError(`'ALL' handler must be a function. Found '${typeof handler}'`)); | ||
this.fallbackRoute = normalizeHandler(handler.bind(this.context)); | ||
}; | ||
proxy.pre = buildHook.bind(this, 'preHook'); | ||
proxy.pos = buildHook.bind(this, 'posHook'); | ||
spec.call(context, proxy); | ||
spec?.call(context, proxy); | ||
} | ||
addEndpoint(method, path, ...chain){ | ||
addEndpoint(method, path, handler){ | ||
let m = method.toUpperCase(); | ||
let route = m + ' ' + path; | ||
const m = method.toUpperCase(); | ||
const route = m + ' ' + path; | ||
let dup = route in this.routes; | ||
const dup = route in this.routes; | ||
assert(!dup, new Error('Route for \'' + route + '\' is already defined')); | ||
assert(chain.length > 0, new Error('Route is empty at \'' + path + '\'')); | ||
let stack = buildStack(this.context, chain); | ||
assert(typeof handler == 'function', | ||
new TypeError(`'${route}' handler must be a function. Found '${typeof handler}'`)); | ||
stack.slice(-1)[0].tail = true; | ||
const nmHandler = normalizeHandler(handler.bind(this.context)); | ||
@@ -174,10 +123,10 @@ this.routes[route] = true; | ||
if(path.indexOf('/:') < 0 && path.indexOf('*') < 0) | ||
return this.static[route] = stack[0]; | ||
return this.static[route] = nmHandler; | ||
this.dynamic[m] = this.dynamic[m] || []; | ||
let { regexp, params } = pathToRegexp(path); | ||
this.dynamic[m].push({ regexp, handler: stack[0], params }); | ||
const { regexp, params } = pathToRegexp(path); | ||
this.dynamic[m].push({ regexp, handler: nmHandler, params }); | ||
} | ||
async trigger(method, path, input = {}){ | ||
async trigger(method, path, input){ | ||
method = method.toUpperCase(); | ||
@@ -188,45 +137,22 @@ const params = {}; | ||
const flash = new Proxy({}, { | ||
get() { | ||
app.log.warn('`flash` is deprecated. ' | ||
+ 'This parameter will be dropped on v0.13.0.'); | ||
return Reflect.get(...arguments); | ||
}, | ||
set(){ | ||
app.log.warn('`flash` is deprecated. ' | ||
+ 'This parameter will be dropped on v0.13.0.'); | ||
return Reflect.set(...arguments); | ||
} | ||
}); | ||
input = { | ||
...app.global, conf: app.conf, flash, 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 | ||
}; | ||
input.fork = fork.bind(app, input); | ||
input.call = (fn, ...args) => fn.call(app, input, ...args); | ||
let origReq = input.req || {}; | ||
input.req = new Proxy({ | ||
method, path, host: origReq.host, 'user-agent': origReq['user-agent'] | ||
}, { | ||
get() { | ||
app.log.warn('`req` is deprecated. ' | ||
+ 'This parameter will be dropped on v0.13.0.'); | ||
return Reflect.get(...arguments); | ||
} | ||
}); | ||
app.log.debug({ | ||
method: method, path, | ||
host: origReq.host, | ||
agent: origReq['user-agent'], | ||
const reqInfo = { | ||
method, path, | ||
host: input.headers?.host, | ||
agent: input.headers?.['user-agent'], | ||
type: 'request', | ||
msg: 'Received ' + method + ' request to ' + path | ||
}); | ||
}; | ||
let res = input.res = new Response(input); | ||
app.log.debug(reqInfo); | ||
const res = input.res = new Response(input, reqInfo); | ||
const handler = matchRoute.call(this, method, path, params); | ||
@@ -239,4 +165,4 @@ | ||
input.body = new RequestBody(input, origReq); | ||
if(app._autoParseBody) | ||
input.body = new RequestBody(input); | ||
if(app._autoParseBody && !input.websocket) | ||
try{ | ||
@@ -247,3 +173,3 @@ input.body = await input.body.parse(); | ||
res.status(400).end(); | ||
app.log.warn({ req: res.req, err, type: 'request' }); | ||
app.log.warn({ ...reqInfo, err }); | ||
return res.ended; | ||
@@ -254,10 +180,4 @@ } | ||
this.preHook && await new Promise(done => runHandler(input, this.preHook, done)); | ||
handler(input).catch(err => handleError(err, input)); | ||
const warn = () => app.log.warn({ type: 'route' }, | ||
'next() was called when the chain is finished'); | ||
runHandler(input, handler, this.posHook | ||
? () => runHandler(input, this.posHook, warn) : warn); | ||
return res.ended; | ||
@@ -264,0 +184,0 @@ } |
const contentType = require('content-type'); | ||
const { getDataTypeFromContentType, parseBuffer } = require('./types'); | ||
const FALLBACK_CONTENT_TYPE = { type: 'text/plain', parameters: { charset: 'utf-8' } }; | ||
function parseContentType(headers){ | ||
try{ | ||
var ct = contentType.parse(headers['content-type']); | ||
} | ||
catch(err){ | ||
ct = FALLBACK_CONTENT_TYPE; | ||
} | ||
ct.textCharset = ct.parameters.charset || 'utf-8'; | ||
ct.originalCharset = ct.parameters.charset; | ||
return ct; | ||
} | ||
function readStream(){ | ||
var complete, buffer = []; | ||
this.req.on('data', chunk => buffer.push(chunk)); | ||
this.stream.on('data', chunk => buffer.push(chunk)); | ||
this.req.on('close', () => { | ||
this.stream.on('close', () => { | ||
buffer = null; | ||
this.req.removeAllListeners('aborted'); | ||
this.req.removeAllListeners('data'); | ||
this.req.removeAllListeners('end'); | ||
this.req.removeAllListeners('error'); | ||
this.req.removeAllListeners('close'); | ||
this.stream.removeAllListeners('aborted'); | ||
this.stream.removeAllListeners('data'); | ||
this.stream.removeAllListeners('end'); | ||
this.stream.removeAllListeners('error'); | ||
this.stream.removeAllListeners('close'); | ||
}); | ||
@@ -36,6 +20,6 @@ | ||
this.req.on('aborted', () => | ||
this.req.emit('error', new Error('Request aborted by the client'))); | ||
this.stream.on('aborted', () => | ||
this.stream.emit('error', new Error('Request aborted by the client'))); | ||
this.req.on('end', () => { | ||
this.stream.on('end', () => { | ||
!complete && resolve(Buffer.concat(buffer)); | ||
@@ -45,3 +29,3 @@ complete = true; | ||
this.req.on('error', err => { | ||
this.stream.on('error', err => { | ||
!complete && reject(err); | ||
@@ -55,5 +39,2 @@ complete = true; | ||
if(!this.res.res) | ||
return this.origBody; | ||
let out = await readStream.call(this); | ||
@@ -69,7 +50,8 @@ | ||
constructor({ res, headers, body }, stream){ | ||
Object.assign(this, parseContentType(headers)); | ||
this.req = stream; | ||
constructor({ res, headers, body }){ | ||
const t = getDataTypeFromContentType(headers['content-type']); | ||
this.type = t.type; | ||
this.charset = t.charset; | ||
this.stream = body; | ||
this.res = res; | ||
this.origBody = body; | ||
this.length = headers['content-length']; | ||
@@ -82,33 +64,22 @@ } | ||
async text(){ | ||
this.res.badType(!this.originalCharset && this.type.slice(0, 4) != 'text'); | ||
return await read.call(this, raw => raw.toString(this.textCharset)); | ||
text(){ | ||
this.res.badType(!this.charset && this.type != 'text'); | ||
return read.call(this, raw => raw.toString(this.charset)); | ||
} | ||
async urlencoded(){ | ||
this.res.badType(this.type != 'application/x-www-form-urlencoded'); | ||
return await read.call(this, raw => | ||
Object.fromEntries(new URLSearchParams(raw.toString(this.textCharset)).entries())); | ||
json(){ | ||
this.res.badType(this.type != 'json'); | ||
return read.call(this, raw => JSON.parse(raw.toString(this.charset))); | ||
} | ||
async json(){ | ||
this.res.badType(this.type.slice(-4) != 'json'); | ||
return await read.call(this, raw => | ||
JSON.parse(raw.toString(this.textCharset))); | ||
urlencoded(){ | ||
this.res.badType(this.type != 'urlencoded'); | ||
return read.call(this, raw => | ||
Object.fromEntries(new URLSearchParams(raw.toString(this.charset)).entries())); | ||
} | ||
parse(){ | ||
if(this.type.slice(-4) == 'json') | ||
return this.json(); | ||
if(this.type == 'application/x-www-form-urlencoded') | ||
return this.urlencoded(); | ||
if(this.originalCharset || this.type.slice(0, 4) == 'text') | ||
return this.text(); | ||
return this.raw(); | ||
return read.call(this, raw => parseBuffer(raw, this.type, this.charset)); | ||
} | ||
} |
@@ -25,3 +25,3 @@ | ||
// and from res.error() when calling it with an error | ||
function handleError(err, res){ | ||
function handleError(err, { method, path, res, log, headers }){ | ||
res.failed = true; | ||
@@ -37,4 +37,3 @@ | ||
res.log.error({ method: res.req.method, path: res.req.path, | ||
headers: res.req.headers, type: 'route', err: res.err }); | ||
log.error({ method, path, headers, type: 'route', err: res.err }); | ||
@@ -41,0 +40,0 @@ if(!res.finished) |
const cookie = require('cookie'); | ||
const { buildWebSocketServer } = require('./ws'); | ||
const normalizePath = p => (p.slice(-1) == '/' ? p.slice(0, -1) : p) || '/'; | ||
@@ -12,6 +13,6 @@ | ||
let [ path, query ] = req.url.split('?'); | ||
const [ path, query ] = req.url.split('?'); | ||
await this._api.trigger(req.method, normalizePath(path), { | ||
req, res, headers: req.headers, | ||
body: req, res, headers: req.headers, | ||
query: Object.fromEntries(new URLSearchParams(query).entries()), | ||
@@ -25,3 +26,3 @@ cookies: cookie.parse(req.headers.cookie || '') | ||
async startServer(){ | ||
let handler = handleRequest.bind(this); | ||
const handler = handleRequest.bind(this); | ||
this._server = this._serverBuilder(this).on('request', handler); | ||
@@ -31,4 +32,7 @@ await new Promise(done => this._server.listen(this.conf.port, done)); | ||
'%s v%s is ready on port %s', this._name, this._version, this.conf.port); | ||
if(this._websocket) | ||
this._wss = buildWebSocketServer(this); | ||
} | ||
} |
@@ -91,3 +91,3 @@ import { Server } from 'http'; | ||
class RequestBody { | ||
req: Request | ||
stream: Request | ||
raw(): Promise<Buffer | unknown> | ||
@@ -97,3 +97,3 @@ text(): Promise<string> | ||
json(): Promise<unknown> | ||
parse(): Promise<Buffer | string | Record<string, string> | unknown> | ||
parse(): Promise<unknown> | ||
} | ||
@@ -116,19 +116,4 @@ | ||
res: Response, | ||
/** | ||
* Object containing some request info | ||
* @deprecated This property is going to be removed on v0.13.0. | ||
*/ | ||
req: { | ||
method: string, | ||
path: string, | ||
'user-agent'?: string, | ||
host?: string | ||
}, | ||
/** Call `fn` with the request handler args as the first parameter and spreading `args`. */ | ||
call: <T>(fn: (input: RouteHandlerArgs, ...args: unknown[]) => T, ...args: unknown[]) => T | ||
/** | ||
* Run an express middleware `fn` and returns a `Promise` resolving when `next` is called, or rejecting in case of exceptions | ||
* @deprecated This property is going to be removed on v0.13.0. | ||
*/ | ||
fork: (fn: RouteHandler) => Promise<void>, | ||
/** The current app configuration. */ | ||
@@ -141,13 +126,3 @@ conf: ConfObject, | ||
/** Object containing params parsed from URL segments as key-values. */ | ||
params: Record<string, string>, | ||
/** | ||
* Run next function in the handler chain | ||
* @deprecated This method is going to be removed on v0.13.0. | ||
*/ | ||
next: <T>(fn: (input: RouteHandlerArgs, ...args: unknown[]) => T, ...args: unknown[]) => T, | ||
/** | ||
* Object where you can store values to be persisted across the middleware chain | ||
* @deprecated This property is going to be removed on v0.13.0. | ||
*/ | ||
flash: Record<string, string> | ||
params: Record<string, string> | ||
} & Record<string, unknown>; | ||
@@ -163,15 +138,2 @@ | ||
del: (path: string, handler: RouteHandler) => void, | ||
/** | ||
* Set a function to be run before all route handler | ||
* @deprecated This method is going to be removed on v0.13.0. | ||
*/ | ||
pre: (handler: RouteHandler) => void, | ||
/** | ||
* Set a function to be run after all route handlers | ||
* @deprecated This method is going to be removed on v0.13.0. | ||
*/ | ||
pos: (handler: RouteHandler) => void, | ||
all: (handler: RouteHandler) => void | ||
@@ -195,9 +157,4 @@ } | ||
autoParseBody?: boolean, | ||
/** A function tthat returns a custom HTTP server to be used by the app */ | ||
server?: (args: Nodecaf) => Server, | ||
/** | ||
* When set change api building to happen on every `app.start()` | ||
* @deprecated This option is going to be removed on v0.13.0. | ||
*/ | ||
alwaysRebuildAPI: boolean | ||
/** A function that returns a custom HTTP server to be used by the app */ | ||
server?: (args: Nodecaf) => Server | ||
} | ||
@@ -204,0 +161,0 @@ } |
@@ -10,6 +10,5 @@ const | ||
const API = require('./api'); | ||
const { Readable, Writable } = require('stream'); | ||
const { getDataTypeFromContentType, getContentTypeFromDataType, parseBuffer } = require('./types'); | ||
const noop = function(){}; | ||
noop.noop = true; | ||
function findPkgInfo(){ | ||
@@ -33,18 +32,19 @@ try{ | ||
this._apiSpec = opts.api || noop; | ||
this._startup = opts.startup || noop; | ||
this._shutdown = opts.shutdown || noop; | ||
this._serverBuilder = opts.server || (() => http.createServer()); | ||
this._websocket = opts.websocket; | ||
this._apiSpec = opts.api; | ||
this._startup = opts.startup; | ||
this._shutdown = opts.shutdown; | ||
this._serverBuilder = opts.server ?? (() => http.createServer()); | ||
let { name, version } = findPkgInfo(); | ||
this._name = opts.name || name; | ||
this._version = opts.version || version; | ||
const { name, version } = findPkgInfo(); | ||
this._name = opts.name ?? name; | ||
this._version = opts.version ?? version; | ||
assert(typeof this._apiSpec == 'function', | ||
assert(!opts.api || typeof this._apiSpec == 'function', | ||
new TypeError('API builder must be a function')); | ||
assert(typeof this._startup == 'function', | ||
assert(!opts.startup || typeof this._startup == 'function', | ||
new TypeError('Startup handler must be a function')); | ||
assert(typeof this._shutdown == 'function', | ||
assert(!opts.shutdown || typeof this._shutdown == 'function', | ||
new TypeError('Shutdown handler must be a function')); | ||
@@ -61,6 +61,4 @@ | ||
this._autoParseBody = opts.autoParseBody || false; | ||
this._autoParseBody = opts.autoParseBody ?? false; | ||
this._alwaysRebuildAPI = opts.alwaysRebuildAPI || false; | ||
this.call = (fn, ...args) => fn.call(this, { ...this.global, ...this }, ...args); | ||
@@ -73,12 +71,7 @@ | ||
opts.alwaysRebuildAPI && | ||
this.log.warn('`alwaysRebuildAPI` is deprecated. ' | ||
+ 'This parameter will be dropped on v0.13.0.'); | ||
if(!this._alwaysRebuildAPI) | ||
this._api = new API(this, this._apiSpec); | ||
this._api = new API(this, this._apiSpec); | ||
} | ||
setup(objectOrPath){ | ||
this.conf = confort(this.conf, objectOrPath || {}); | ||
setup(objectOrPath = {}){ | ||
this.conf = confort(this.conf, objectOrPath); | ||
this._cors = cors(this.conf.cors); | ||
@@ -105,6 +98,3 @@ | ||
if(this._alwaysRebuildAPI) | ||
this._api = new API(this, this._apiSpec); | ||
if(!this._startup.noop) | ||
if(this._startup) | ||
this.log.debug({ type: 'app' }, 'Starting up %s...', this._name); | ||
@@ -114,7 +104,7 @@ | ||
try{ | ||
await this._startup(this); | ||
await this._startup?.(this); | ||
} | ||
catch(err){ | ||
this.state = 'stuck'; | ||
await this.stop().catch(() => {}); | ||
await this.stop().catch(Function.prototype); | ||
throw err; | ||
@@ -140,2 +130,5 @@ } | ||
if(this._wss) | ||
this._wss.close(); | ||
if(this._server) | ||
@@ -146,3 +139,3 @@ var actualHTTPClose = new Promise(done => this._server.close(done)); | ||
try{ | ||
await this._shutdown(this); | ||
await this._shutdown?.(this); | ||
} | ||
@@ -162,4 +155,38 @@ catch(err){ | ||
trigger(method, path, input){ | ||
return this._api.trigger(method, path, input); | ||
async trigger(method, path, input = {}){ | ||
if(input.body && !(input.body instanceof Readable)){ | ||
const ob = input.body; | ||
const oh = input.headers ?? {}; | ||
input = { ...input }; | ||
const type = getContentTypeFromDataType(ob); | ||
if(ob instanceof Buffer) | ||
input.body = Readable.from(ob); | ||
else if(typeof ob == 'object') | ||
input.body = Readable.from(Buffer.from(JSON.stringify(ob))); | ||
else | ||
input.body = Readable.from(Buffer.from(String(ob))); | ||
if(!oh['content-type'] && !oh['Content-Type'] && type) | ||
input.headers = { ...oh, 'content-type': type }; | ||
} | ||
const chunks = []; | ||
input.res = new Writable({ | ||
write: function(chunk, _encoding, next) { | ||
chunks.push(chunk); | ||
next(); | ||
} | ||
}); | ||
const r = await this._api.trigger(method, path, input); | ||
if(chunks.length > 0){ | ||
const ct = r.headers?.['content-type'] ?? r.headers?.['Content-Type']; | ||
const { type, charset } = getDataTypeFromContentType(ct); | ||
r.body = parseBuffer(Buffer.concat(chunks), type, charset); | ||
} | ||
return r; | ||
} | ||
@@ -166,0 +193,0 @@ |
@@ -6,7 +6,7 @@ const { sign } = require('cookie-signature'); | ||
const { HTTPError, handleError } = require('./error'); | ||
const { getContentTypeFromDataType } = require('./types'); | ||
const SHORT_CONTENT_TYPES = { | ||
'text': 'text/plain', | ||
'json': 'application/json', | ||
'binary': 'application/octet-stream' | ||
'json': 'application/json' | ||
}; | ||
@@ -26,7 +26,6 @@ | ||
constructor({ res, req, log, conf }){ | ||
this.conf = conf; | ||
this.res = res; | ||
this.log = log; | ||
this.req = req; | ||
constructor(input, reqInfo){ | ||
this.reqInfo = reqInfo; | ||
this.resStream = input.res; | ||
this.input = input; | ||
this.ended = new Promise((resolve, reject) => { | ||
@@ -38,3 +37,3 @@ this.resolve = resolve; | ||
this.statusCode = 200; | ||
for(let name in ASSERTS) | ||
for(const name in ASSERTS) | ||
this[name] = this.assert.bind(this, ASSERTS[name]); | ||
@@ -44,9 +43,3 @@ } | ||
write(chunk){ | ||
this.res && this.res.write(chunk); | ||
if(!this.body) | ||
this.body = chunk; | ||
else if(this.body instanceof Buffer) | ||
this.body = Buffer.concat([ this.body, chunk ]); | ||
else | ||
this.body += String(chunk); | ||
this.resStream.write(chunk); | ||
} | ||
@@ -56,13 +49,10 @@ | ||
body && this.write(body); | ||
this.res && this.res.end(); | ||
this.resStream.end(); | ||
this.log.debug({ | ||
path: this.req.path, | ||
method: this.req.method, | ||
host: this.req.host, | ||
agent: this.req['user-agent'], | ||
this.input.log.debug({ | ||
...this.reqInfo, | ||
status: this.statusCode, | ||
level: this.statusCode > 499 ? 'warn' : 'debug', | ||
type: 'response', | ||
msg: 'Sent ' + this.statusCode + ' response to ' + this.req.method + ' ' + this.req.path | ||
msg: 'Sent ' + this.statusCode + ' response to ' + this.reqInfo.method + ' ' + this.reqInfo.path | ||
}); | ||
@@ -74,3 +64,3 @@ | ||
headers: this.headers, | ||
body: this.body | ||
body: this.resStream | ||
}); | ||
@@ -84,18 +74,12 @@ } | ||
if(!Number.isInteger(status)) | ||
return handleError(status, this); | ||
return handleError(status, this.input); | ||
this.status(status); | ||
let type = 'text'; | ||
const type = getContentTypeFromDataType(message); | ||
if(typeof message == 'string') | ||
message = format(message, ...args); | ||
else if(message instanceof Buffer) | ||
type = 'binary'; | ||
else if(message && typeof message == 'object'){ | ||
type = 'json'; | ||
else if(type == 'application/json') | ||
message = JSON.stringify(message); | ||
} | ||
else if(message == null) | ||
type = null; | ||
else | ||
else if(type == 'text/plain') | ||
message = String(message); | ||
@@ -117,3 +101,3 @@ | ||
set(k, v){ | ||
this.res && this.res.setHeader(k, v); | ||
this.resStream.setHeader?.(k, v); | ||
this.headers[k] = v; | ||
@@ -124,3 +108,3 @@ return this; | ||
append(k, v){ | ||
let prev = this.headers[k]; | ||
const prev = this.headers[k]; | ||
prev && (v = Array.isArray(prev) | ||
@@ -133,3 +117,3 @@ ? prev.concat(v) | ||
status(s){ | ||
this.res && (this.res.statusCode = s); | ||
this.resStream.statusCode = s; | ||
this.statusCode = s; | ||
@@ -168,7 +152,7 @@ return this; | ||
if(value && opts.signed && !this.conf.cookie?.secret) | ||
if(value && opts.signed && !this.input.conf.cookie?.secret) | ||
throw new Error('Trying to sign cookies when secret is not defined'); | ||
if(opts.signed && value) | ||
value = sign(value, this.conf.cookie.secret); | ||
value = sign(value, this.input.conf.cookie.secret); | ||
@@ -175,0 +159,0 @@ if('maxAge' in opts) { |
{ | ||
"name": "nodecaf", | ||
"version": "0.12.5", | ||
"version": "0.13.0-rc1", | ||
"description": "Nodecaf is a light framework for developing RESTful Apps in a quick and convenient manner.", | ||
@@ -42,3 +42,2 @@ "main": "lib/main.js", | ||
"confort": "^0.2.0", | ||
"content-type": "^1.0.4", | ||
"cookie": "^0.4.1", | ||
@@ -50,5 +49,5 @@ "cookie-signature": "^1.1.0", | ||
"devDependencies": { | ||
"muhb": "^3.0.6", | ||
"muhb": "^3.1.0", | ||
"toml": "^3.0.0" | ||
} | ||
} |
@@ -157,4 +157,3 @@ # [Nodecaf](https://gitlab.com/GCSBOSS/nodecaf) | ||
## Manual | ||
Formerly based on Express, Nodecaf preserves the same interface for defining routes | ||
through middleware chains. Check out how to use all the awesome goodies Nodecaf introduces. | ||
Formerly based on Express, Nodecaf has a simpler approach to defining routes, offloading much of the complexity to the already existing code partitioning idioms (i.e. functions). Check out how to use all the awesome goodies Nodecaf introduces. | ||
@@ -168,3 +167,3 @@ ### Handler Args | ||
```js | ||
function({ req, res, next, query, params, body, flash, conf, log, headers, call }){ | ||
function({ method, path, res, query, params, body, conf, log, headers, call }){ | ||
// Do your stuff. | ||
@@ -176,9 +175,5 @@ } | ||
- `req`, `res`, `next`: The good old parameters used regularly in middleware-like frameworks. | ||
- `method`, `path`, `query`, `parameters`, `body`, `headers`: Shortcuts to the homonymous properties of `req`. | ||
They contain respectively the HTTP method, request URL path, query string, the URL parameters, and the request | ||
body data. | ||
- `flash`: Is an object where you can store arbitrary values. Keys inserted in this | ||
object are preserved for the lifetime of a request and can be accessed in all | ||
handlers of a route chain. | ||
- `res`: An object containing the functions to send a response to the client. | ||
- `path`, `method`, `query`, `params`, `body`, `headers`: Properties of the request. | ||
They contain respectively the requested path, HTTP method, query string, the URL parameters, and the request body data. | ||
- `conf`: This object contains the entire | ||
@@ -277,3 +272,2 @@ [application configuration data](#settings-file). | ||
| route | error | An error happened inside a route and was not caught | | ||
| route | warn | next() used after stack has ended | | ||
| crash | fatal | An error happened that crashed the server process | | ||
@@ -309,12 +303,10 @@ | request | debug | A request has arrived | | ||
```js | ||
get('/my/thing', | ||
function({ res, next }){ | ||
res.send('My regular function works!'); | ||
next(); | ||
}, | ||
async function({ res }){ | ||
await myAsyncThing(); | ||
res.end('My async function works too!');s | ||
} | ||
); | ||
get('/my/thing', function({ res }){ | ||
res.end('My regular function works!'); | ||
}); | ||
get('/my/other/thing', async function({ res }){ | ||
await myAsyncThing(); | ||
res.end('My async function works too!');s | ||
}); | ||
``` | ||
@@ -333,4 +325,3 @@ | ||
To support the callback error pattern, use the `res.error()` function arg. This | ||
function will stop the middleware chain from being executed any further. | ||
To support the callback error pattern, use the `res.error()` function arg. | ||
@@ -484,2 +475,1 @@ ```js | ||
| `opts.shouldParseBody` | Boolean | Wether supported request body types should be parsed | `true` | | ||
| `opts.alwaysRebuildAPI` | Boolean | Wether the API should be rebuilt dynamically for every start or setup operation | `false` | |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5
12
50010
815
469
3
- Removedcontent-type@^1.0.4
- Removedcontent-type@1.0.5(transitive)