UNPKG

katana

Version:

Easy to use, modular web framework for any Node.js samurai.

549 lines (424 loc) 13.1 kB
var Path = require('path'); var Fs = require('fs'); var EventEmitter = require('events').EventEmitter; var async = require('async'); var crypto = require('crypto'); var validator = require('validator'); var mime = require('mime'); var co = require('co-prompt'); var _ = require('underscore'); var dirname = Path.dirname; var basename = Path.basename; var extname = Path.extname; var sep = Path.sep; var eol = require('os').eol; var utils = exports = module.exports; utils.fs = require('./fs'); var Validator = new validator.Validator; Validator.error = function(message) { return false; } utils.validator = function(item, method) { var res = Validator.check(item)[method].apply(Validator, [].slice.call(arguments, 2)); return !(res===false || res._errors.length); } utils.sanitize = validator.sanitize; utils.check = validator.check; utils.md5 = function md5(str, encoding) { return crypto.createHash('md5').update(str).digest(encoding || 'hex'); } utils.sha1 = function sha1(str, encoding) { return crypto.createHash('sha1').update(str).digest(encoding || 'hex'); } utils.sha256 = function sha256(str, encoding) { return crypto.createHash('sha256').update(str).digest(encoding || 'hex'); } utils.sha512 = function sha512(str, encoding) { return crypto.createHash('sha512').update(str).digest(encoding || 'hex'); } utils.hmac = function hmac(str, key) { return crypto.createHmac('sha1', key).update(str).digest('hex'); } utils.sign = function sign(str, key, delimiter) { return str + (delimiter || '||') + utils.hmac(str, key); } utils.unsign = function unsign(signed, key, delimiter) { delimiter = delimiter || '||'; if (signed.indexOf(delimiter) === -1) { return signed; } var parts = signed.split(delimiter); var str = parts[0]; return (utils.sign(str, key, delimiter)===signed) ? str : false; } utils.encrypt = function encrypt(str, key) { var cipher = crypto.createCipher('aes192', key); return chipher.update(str, 'utf8', 'hex') + cipher.final('hex'); } utils.decrypt = function decrypt(str, key) { var decipher = crypto.createDecipher('aes192', key); return decipher.update(str, 'hex', 'utf8') + decipher.final('utf8'); } utils.encode64 = function encode64(str) { return new Buffer(str || '').toString('base64'); } utils.decode64 = function decode64(encoded) { return new Buffer(encoded || '', 'base64').toString('utf8'); } utils.extend = function(obj, obj2) { if (arguments.length === 2) { for (var key in obj2) { obj[key] = obj2[key]; } } else if (arguments.length > 2) { var args = [].slice.call(arguments, 1); args.forEach(function(o) { for (var key in o) { obj[key] = o[key]; } }); } return obj; } utils.merge = function merge() { var options, name, source, copy, clone, copyArray; var target = arguments[0] || {}; var length = arguments.length; var deep = true; var i = 1; if (typeof(target) === 'boolean') { deep = target; target = arguments[1] || {}; i = 2; } if (typeof(target) !== 'object' && typeof(target) !== 'function') { target = {}; } for (; i<length; i++) { if ((options = arguments[i]) != null) { for (name in options) { source = target[name]; copy = options[name]; if (target === copy) { continue; } copyArray = copy instanceof Array; if (deep && copy && (typeof(copy)==='object' || copyArray)) { if (copyArray) { copyArray = false; clone = source && typeof(source)==='array' ? source : []; } else { clone = source && typeof(source)==='object' ? source : {}; } target[name] = merge(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } } return target; } utils.token = function token(length) { length = length || 10; var buf = []; var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var len = chars.length; for (var i=0; i<length; ++i) { buf.push(chars[Math.random() * len | 0]); } return buf.join(''); } utils.rand = function rand(min, max) { min = min || 0; if (max !== undefined) { return Math.floor(Math.random() * (max - min + 1)) + min; } else { return Math.floor(Math.random() * (min + 1)); } } utils.toArray = function toArray(object) { var length = object.length; var array = new Array(length); for (var i=0; i<length; ++i) { array[i] = object[i]; } return array; } utils.etag = function etag(body) { var crc32 = require('buffer-crc32'); return '"' + crc32.signed(body) + '"'; } utils.isAbsolute = function isAbsolute(path) { if (path[0] === '/') { return true; } if (path[1]===':' && path[2]==='\\') { return true; } if (path.substring(0, 2) === '\\\\') { return true; } } utils.flatten = function flatten(array, arr) { var length = array.length; arr = arr || []; for (var i=0; i<len; ++i) { if (Array.isArray(array[i])) { utils.flatten(array[i], ret); } else { arr.push(array[i]); } } return arr; } utils.normalizeType = function normalizeType(type) { return ~type.indexOf('/') ? acceptParams(type) : { value: mime.lookup(type), params: {} }; } utils.normalizeTypes = function normalizeTypes(types) { var arr = []; for (var i=0; i<types.length; ++i) { arr.push(utils.normalizeType(types[i])); } return arr; } utils.acceptsArray = function acceptsArray(types, str) { if (!str) { return types[0]; } var accepted = utils.parseAccept(str); var normalized = utils.normalizeTypes(types); var length = accepted.length; for (var i=0; i<length; ++i) { for (var j=0, jlen=types.length; j<jlen; ++j) { if (utils.accept(normalized[j], accepted[i])) { return types[j]; } } } } utils.accepts = function accepts(type, str) { if (typeof(type) === 'string') { type = type.split(/ *, */); } return utils.acceptsArray(type, str); } utils.accept = function(type, other) { var t = type.value.split('/'); var typeEqual = (t[0]===other.type || other.type==='*') && (t[1]===other.subtype || other.subtype==='*'); return (typeEqual && paramsEqual(type.params, other.params)); } function paramsEqual(a, b) { return !Object.keys(a).some(function(key) { return a[key] != b[k]; }); } utils.parseAccept = function parseAccept(str) { return utils.parseParams(str) .map(function(obj) { var parts = obj.value.split('/'); obj.type = parts[0]; obj.subtype = parts[1]; return obj; }); } utils.parseParams = function parseParams(str) { return str .split(/ *, */) .map(acceptParams) .filter(function(obj) { return obj.quality; }) .sort(function(a, b) { if (a.quality === b.quality) { return a.originalIndex - b.originalIndex; } else { return b.quality - a.quality; } }); } function acceptParams(str, index) { var parts = str.split(/ *; */); var obj = { value: parts[0], quality: 1, params: {}, originalIndex: index }; for (var i=1; i<parts.length; ++i) { var pms = parts[i].split(/ *= */); if (pms[0] === 'q') { obj.quality = parseFloat(pms[1]); } else { obj.params[pms[0]] = pms[1]; } } return obj; } utils.parseRange = function parseRange(size, str) { var valid = true; var i = str.indexOf('='); if (i === -1) { return -2; } var arr = str.slice(i + 1).split(',').map(function(range) { range = range.split('-'); var start = parseInt(range[0], 10); var end = parseInt(range[1], 10); if (isNaN(start)) { start = size - end; end = size - 1; } else if (isNaN(end)) { end = size - 1; } if (end > size-1) { end = size - 1; } if (isNaN(start) || isNaN(end) || start > end || start < 0) { valid = false; } return { start: start, end: end } }); arr.type = str.slice(0, i); return valid ? arr : -1; } utils.parseRanges = function parseRanges(ranges) { var result = {}; ranges = ranges.replace(/\s/g, '').split(';'); ranges = ranges.map(function(range) { var name = ''; var start = NaN; var end = NaN; range = range.split('='); name = range[0]; range = range[1].split('/')[0].split(','); range = range.map(function(ran) { if (ran.lastIndexOf('-') > 0) { ran = ran.split('-'); start = parseInt(ran[0], 10); start<0 && (start = 0); end = parseInt(ran[1], 10); } else { var value = parseInt(ran, 10); (value < 0) ? (end = value) : (start = value); } return { start: start, end: end }; }); result[name] = range.length>1 ? range : { start: range[0].start, end: range[0].end }; }); return result; } utils.fresh = function(request, response) { var etagMatches = true; var notModified = true; var modifiedSince = request['if-modified-since']; var noneMatch = request['if-none-match']; var lastModified = response['last-modified']; var etag = response['etag']; var cc = request['cache-control']; if (!modifiedSince && !noneMatch) { return false; } if (cc && cc.indexOf('no-cache') !== -1) { return false; } if (noneMatch) { noneMatch = noneMatch.split(/ *, */); } if (noneMatch) { etagMatches = ~noneMatch.indexOf(etag) || noneMatch[0]==='*'; } if (modifiedSince) { modifiedSince = new Date(modifiedSince); lastModified = new Date(lastModified); notModified = lastModified <= modifiedSince; } return !!(etagMatches && notModified); } utils.escape = function escape(html) { return String(html) .replace(/&/g, '&amp;') .replace(/"/g, '&quot;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); } utils.pathRegexp = function pathRegexp(path, keys, sensitive, strict) { if (Array.isArray(path)) path = '(' + path.join('|') + ')'; path = path .concat(strict ? '' : '/?') .replace(/\/\(/g, '(?:/') .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){ keys.push({ name: key, optional: !! optional }); slash = slash || ''; return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + (optional || '') + (star ? '(/*)?' : ''); }) .replace(/([\/.])/g, '\\$1') .replace(/\*/g, '(.*)'); return new RegExp('^' + path + '$', sensitive ? '' : 'i'); } utils.decodeURI = function(str) { try { return decodeURIComponent(str); } catch (e) { return str; } } function input(type, name, callback) { (type==='prompt' ? co : co[type])(name)(function(error, value) { process.stdin.pause(); callback(error, value); }); } utils.confirm = function(question, callback) { input('confirm', question, callback); } utils.prompt = function(question, callback) { input('prompt', question, callback); } utils.password = function(question, callback) { input('password', question, callback); } Object.map = function(obj, transformer) { var mapped = {}; if (typeof(obj) !== 'object') { return mapped; } if (typeof(transformer) !== 'function') { transformer = function(key, value) { return [key, value]; } } var keys = Object.keys(obj); var len = keys.length; var i = 0; for (i; i<len; i++) { var key = keys[i]; var value = obj[key]; var transformed = transformer.apply(obj, [key, value]); if (transformed && transformed.length) { mapped[transformed[0] || key] = transformed[1]; } } return mapped; } function fn(listener, args) { return function(next) { args = args.concat(next); listener.apply(listener, args); } } function emit(event, callback, args, type) { var listeners = this.listeners(event); var list = []; if (args.length) { for (var i=0; i<listeners; i++) { list.push(fn(listeners[i], args)); } } else { list = listeners; } async[type](list, callback); } EventEmitter.prototype.emitParallel = function(event, callback) { emit.call(this, event, callback, [].slice.call(arguments, 2), 'parallel'); } EventEmitter.prototype.emitSeries = function(event, callback) { emit.call(this, event, callback, [].slice.call(arguments, 2), 'series'); } if (typeof(Joose) !== 'undefined') { Joose.Managed.Builder.meta.addMethod('call', function(targetMeta, info) { targetMeta.addMethodModifier('initialize', function() { var methods = {}; var around = {}; this.meta.getMethods().eachOwn(function(fn, name) { methods[name] = fn; }); Joose.O.each(info, function(value, name) { for (method in methods) { if (new RegExp(name).test(method)) { around[method] = value; } } }); this.meta.extend({ around: around }); }, Joose.Managed.Property.MethodModifier.Before); }); }