diff --git a/README.md b/README.md index 38f7e35..97de200 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,15 @@ main fields of the resource ### meta meta fields of the resource, such as created\_at, created\_by + + +## TODO +- fix eslint (migrate to latest) +- explain it is middleware first architecture +- explain how to create a middleware +- explain module vs middleware +- explain how to create a modules +- explain create module to be use as middleware or in middleware +- explain how to branch +- explain how to detour (await next) +- how to handle error. define midduleware to handle route "ERR" and throw error by this.next(this.web.Error(status, message, headers)) diff --git a/index.js b/index.js index cebd4b8..66ef387 100755 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ #!/usr/bin/env node -// In case picos was installed globally +// In case picos was installed globally, +// dynamically updates where Node.js looks for modules without changing any permanent configuration const NP = process.env.NODE_PATH || '' process.env.NODE_PATH = (NP ? NP + ':' : NP) + process.cwd() + '/node_modules' require('module').Module._initPaths() @@ -18,14 +19,20 @@ function run(opt, cb){ // Is run from cmd? if (require.main === module) { const args = require('pico-args') - const opt = args.parse({ - service: ['service/index', 'path to service script'], + const params = { + service: ['', 'path to the service script, e.g: "service/index.json"'], s: '@service', - mod: ['mod/', 'module path'], - m: '@service', - ratelimit: [64, 'ratelimit'], - r: '@ratelimit' - }) + mod: ['mod/', 'path to the module folder'], + m: '@mod', + ratelimit: [64, 'ratelimit the concurrent requests'], + r: '@ratelimit', + help: [false, 'Usage'], + h: '@help', + } + const opt = args.parse(params) + if (!opt.service || opt.help){ + return args.usage(params) + } run(opt, err => { if (err) return console.error(err) }) diff --git a/mod/util.js b/mod/util.js index 696afe1..9497cec 100644 --- a/mod/util.js +++ b/mod/util.js @@ -63,7 +63,7 @@ module.exports = { routerByRSC: (rsc, prefix = '/i') => async function(method, params) { const rs = rsc[params.rsc] - if (!rs) return this.next(`unsupprted resource: ${params.rsc}`) + if (!rs) return this.next(null, `ERR/RESOURCE-NOT-FOUND/${params.rsc}`) const idx = params.i ? prefix : '' const name = `${method}/${params.rsc}${idx}` await this.next(null, name, Object.assign({ @@ -75,13 +75,13 @@ module.exports = { input: spec => function(input, output, ext) { const err = pObj.validate(spec, input, output, ext) - if (err) return this.next(`invalid params [${err}]`) + if (err) return this.next(null, `ERR/INVALID-INPUT/[${err}]`) return this.next() }, inputNoCurry(input, spec, output, ext) { const err = pObj.validate(spec, input, output, ext) - if (err) return this.next(`invalid params [${err}]`) + if (err) return this.next(null, `ERR/INVALID-INPUT/[${err}]`) return this.next() }, diff --git a/mod/web.js b/mod/web.js index a593888..0ff3f96 100644 --- a/mod/web.js +++ b/mod/web.js @@ -9,6 +9,10 @@ const HAS_DATA = obj => obj && (Array.isArray(obj) || Object.keys(obj).length) const CREATE_BODY = (body, meta) => JSON.stringify(Object.assign({}, meta, {body})) const GET_CONTENT_TYPE = (value = '') => value.split(';')[0].trim().toLowerCase() +function Error(status, message, headers){ + return {status, message, headers} +} + module.exports = { setup(cfg, rsc, paths){ const cors = cfg.cors @@ -33,6 +37,8 @@ module.exports = { const url = URL.parse(req.url, 1) this.go(url.pathname, {req, res, url}) }).listen(cfg.port, cfg.host, () => process.stdout.write(`listening to ${cfg.host}:${cfg.port}\n`)) + + return {Error} }, queryParser(req, query){ @@ -126,15 +132,11 @@ module.exports = { res.writeHead(head.status || 200, Object.assign(headers, head.headers)) res.end(createBody(output, meta)) } else { - res.writeHead(head.status || 200, head.headers) + res.writeHead(head.status || 204, head.headers) res.end() } } catch(exp) { - if (exp.isAxiosError){ - console.error(exp.toJSON()) - } else { - console.error(exp) - } + console.error('web error', exp.isAxiosError ? exp.toJSON() : exp) // exp in head format? {status, headers, message} const status = exp.status || 500 res.writeHead(status, exp.headers) diff --git a/package.json b/package.json index e638b9d..20335b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "picos", - "version": "0.10.9", + "version": "0.11.0", "description": "pico api service", "main": "index.js", "bin": { @@ -13,11 +13,11 @@ "randexp": "^0.5.3" }, "scripts": { - "start": "node index.js -s okapi/index.js", + "start": "node index.js -s test/service.json", "test": "node test/index.js", "test:cp": "jscpd --ignore 'node_modules,test' .", - "lint": "eslint . --ext .js", - "lint:fix": "eslint . --ext .js --fix" + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "repository": { "type": "git", diff --git a/src/book.js b/src/book.js index fcaac78..f823f6b 100644 --- a/src/book.js +++ b/src/book.js @@ -9,21 +9,21 @@ pico.ajax = psUtil.ajax const fopt = {encoding: 'utf8'} -let CWD +let PD /** - * Get current working directory + * Get picos directory. the PD is a abs, none symbolic link and contains the picos index.js * * @param {Function} cb - call back * * @returns {void} - undefined */ -function getWD(cb){ - if (CWD) return cb(CWD) - fs.readlink(symPath, (err, realPath) => { - if (err) realPath = symPath - CWD = path.dirname(realPath) - cb(CWD) +function getPD(cb){ + if (PD) return cb(PD) + fs.readlink(symPath, (err, relPath) => { + if (err) PD = symPath + else PD = path.resolve(symPath, realPath) + cb(path.dirname(PD)) }) } @@ -76,22 +76,20 @@ function readBook(wd, index, cb){ readPages(wd, [index], [], (err, res) => { if (err) return cb(err) if (!res.length) return cb(`not found: ${index}`) - if (!res[0].charAt) return cb(null, res) + if (!Array.isArray(res[0])) return cb(null, res) readPages(wd, res[0], [], cb) }) } module.exports = { open(bname, cb){ - getWD(cwd => { - const bpath = getPath(cwd, bname) - const wd = path.dirname(bpath) - const name = path.basename(bpath) - readBook(wd, name, (err, book) => { - if (err) return cb(err) - cb(null, pObj.extends({}, book, {flat: 1})) - }) + const bpath = getPath('.', bname) + const wd = path.dirname(bpath) + const name = path.basename(bpath) + readBook(wd, name, (err, book) => { + if (err) return cb(err) + cb(null, pObj.extends({}, book, {flat: 1})) }) }, - getWD + getPD } diff --git a/src/pipeline.js b/src/pipeline.js index ffb1475..bcba2a3 100644 --- a/src/pipeline.js +++ b/src/pipeline.js @@ -44,7 +44,7 @@ function _host(radix, libs, routes, threshold){ */ async function next(err, named, data = this.data || {}){ if (err) { - overtime.decr(1) + overtime.decr() throw err } if (null != named) { @@ -53,7 +53,11 @@ function _host(radix, libs, routes, threshold){ let route = routes[key] if (!route) { route = named && routes[ERROR_ROUTE] - if (!route) return console.error(`route[${named}] not found`) + if (!route) { + // no console.error if named is an empty string + named && console.error(`route[${named}] not found`) + return + } } overtime.incr() return next.call(Object.assign({}, libs, {named, params, next, route, data, ptr: 0})) @@ -61,7 +65,7 @@ function _host(radix, libs, routes, threshold){ const middleware = this.route[this.ptr++] if (!middleware) { - return overtime.decr(3) + return overtime.decr() } const args = middleware.slice(1).map(key => { diff --git a/test/util.js b/test/util.js index 414492d..a807f5d 100644 --- a/test/util.js +++ b/test/util.js @@ -18,6 +18,6 @@ module.exports = { }, sayNow(out){ Object.assign(out, {now: Date.now()}) - this.next() + return this.next() } }