1// umd boilerplate for CommonJS and AMD 2if (typeof exports === "object" && typeof define !== "function") { 3 var define = function (factory) { 4 factory(require, exports, module); 5 }; 6} 7 8define(function(require, exports, module) { 9 let crypto = require("crypto"); 10 11 let objectLib = require("@sv/objectLib"); 12 13 let async = require("async"); 14 15 // Before/After sort with parallelism 16 // data = array of strings: ["a","b","c"] 17 // rules = array of arrays: [["a", "b"],["c","b"]] indicates that b comes after a, and b comes after c 18 // baSort will organize that result set into an array or object based on running the rules on the items 19 let baSort = function(data, rules, returnType) { 20 returnType = returnType || "array"; 21 22 let nodes = {}; 23 24 let node = function(name) { 25 this.depth = null; 26 this.name = name; 27 this.befores = []; 28 }; 29 30 node.prototype.getDepth = function(temp) { 31 if (this.depth !== null) { 32 // if we have already calculated this nodes depth, return that value 33 return this.depth; 34 } 35 36 if (typeof temp[this.name] !== "undefined") { 37 // if we have already attempted to process this node and do not have a value we are infinitely recursive 38 throw new Error("baSort: Infinitely recursive ruleset declared."); 39 } 40 41 let tempDepth = 0; 42 43 // add name to object to prevent recursion 44 temp[this.name] = true; 45 46 // loop through befores and determine which before has the highest depth, and add one to that 47 this.befores.forEach(function(val, i) { 48 let depth = val.getDepth(temp) + 1; 49 50 if (depth > tempDepth) { 51 tempDepth = depth; 52 } 53 }); 54 55 // set depth so that another node calling this node will return the pre-calculated value 56 this.depth = tempDepth; 57 58 return this.depth; 59 } 60 61 // loop through data and initialize nodes 62 data.forEach(function(val, i) { 63 nodes[val] = new node(val); 64 }); 65 66 // loop through rules adding the nodes which come before each node
67 rules.forEach(function(val, i) { 68 nodes[val[1]].befores.push(nodes[val[0]]); 69 }); 70 71 let result = returnType === "object" ? {} : []; 72 73 // loop through data points and calculate their depth 74 data.forEach(function(val, i) { 75 let depth = nodes[val].getDepth({}); 76 77 if (returnType === "object") { 78 result[val] = depth; 79 } else { 80 result[depth] = result[depth] || []; 81 result[depth].push(val); 82 } 83 }); 84 85 return result; 86 } 87 88 // given a variable, it will recurse down that path until it finds the value 89 // in example varLookup({ foo : { bar : "barValue" } }, "foo.bar") === "barValue" 90 // if any undefined value is found it will return undefined 91 let varLookup = function(variable, path) { 92 if (variable === undefined) { return undefined; } 93 94 let arr = (path instanceof Array) ? path : path.split("."); 95 let current = variable; 96 97 for (let i = 0; i < arr.length; i++) { 98 current = current[arr[i]]; 99 100 if (current === undefined) { 101 return undefined; 102 } 103 } 104 105 return current; 106 } 107 108 // given a variable, it will recurse down that path and set it to the given value 109 // if the path is valid. If the path is invalid it will throw an error 110 let setValue = function(variable, path, value) { 111 if (variable === undefined) { throw new Error("Unable to setValue. Variable undefined."); } 112 113 let arr = (path instanceof Array) ? path : path.split("."); 114 let current = variable; 115 let last = arr.pop(); 116 117 for (let i = 0; i < arr.length; i++) { 118 current = current[arr[i]]; 119 120 if (current === undefined) { throw new Error("Unable to setValue. Interim object at key '" + arr.slice(0, i + 1).join(".") + "' is undefined.") } 121 } 122 123 current[last] = value; 124 125 return variable; 126 } 127 128 // hash arguments down to a md5 hash 129 let hashObject = function(args) { 130 let data = convertObjectsToArrays(args); 131 let md5sum = crypto.createHash("md5"); 132 md5sum.update(JSON.stringify(data)); 133 134 return md5sum.digest("hex"); 135 } 136 137 var convertObjectsToArrays = function(args) { 138 let data = args; 139 140 if (data instanceof Array) { 141 data = Array.prototype.slice.call(args); 142 data.forEach(function(val, i) { 143 data[i] = convertObjectsToArrays(val); 144 }); 145 } else if (typeof args === "object") { 146 data = []; 147 148 objectLib.forEach(args, function(val, i) { 149 data.push([ i, convertObjectsToArrays(val) ]); 150 }); 151 152 // sorts based on the key name, case matters 153 data.sort(function(a, b) { 154 return (a[0] < b[0]) ? -1 : (a[0] > b[0]) ? 1 : 0; 155 }); 156 } 157 158 return data; 159 } 160 161 // memoize wrapper which handles serializing multi-argument functions. async.memoize in core only handles the first arg 162 // https://github.com/caolan/async/issues/575#issuecomment-61915640 163 let memoize = function(fn, args) { 164 args = args || {}; 165 166 let wrappedFn = async.memoize(fn, function() { 167 // v8 - argumentsToArray one-liner 168 let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } 169 return JSON.stringify(args); 170 }); 171 172 if (args.cacheErrors === false) { 173 let newFn = function() { 174 // v8 - argumentsToArray one-liner 175 let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } 176 177 let key = JSON.stringify(args.slice(0, args.length - 1)); 178 179 let cb = args.pop(); 180 args.push(function() { 181 // v8 - argumentsToArray one-liner 182 let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } 183 184 if (args[0] !== null) { 185 delete wrappedFn.memo[key]; 186 } 187 188 cb.apply(null, args); 189 }); 190 191 wrappedFn.apply(null, args); 192 } 193 194 // pass along memo values so downstream code works 195 newFn.memo = wrappedFn.memo; 196 newFn.unmemoized = wrappedFn.unmemoized; 197 198 return newFn; 199 } else { 200 return wrappedFn; 201 } 202 } 203 204 let memoizeSync = function(fn, hasher) { 205 return _memoizeSyncHandler.bind(null, {}, fn, hasher || _memoizeSyncDefaultHasher); 206 } 207
208 var _memoizeSyncHandler = function(cache, fn, hasher) { 209 let args = []; 210 for (let i = 3; i < arguments.length; i++) { args.push(arguments[i]); } 211 212 let key = hasher.apply(null, args); 213 if (key in cache) { return cache[key]; } 214 215 cache[key] = fn.apply(null, args); 216 217 return cache[key]; 218 } 219 220 var _memoizeSyncDefaultHasher = function() { 221 let args = []; 222 for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } 223 224 let key = JSON.stringify(args); 225 return key; 226 } 227 228 // given an array of cookie headers convert them into meaningful objects 229 let extractCookies = function(setCookie) { 230 let result = { 231 simple: {}, 232 cookies: [] 233 }; 234 235 setCookie.forEach(function(val, i) { 236 let cookie = {}; 237 238 val.split(/;[\s]*/).forEach(function(val2, i2) { 239 let labelValue = val2.split("="); 240 241 if (i2 === 0) { 242 cookie.label = labelValue[0]; 243 cookie.value = labelValue[1]; 244 } else { 245 let tempValue = labelValue[1]; 246 247 if (labelValue[0] === "httponly") { 248 tempValue = true; 249 } else if (labelValue[0] === "expires") { 250 cookie.expires_date = new Date(tempValue); 251 } 252 253 cookie[labelValue[0]] = tempValue; 254 } 255 }); 256 257 result.cookies.push(cookie); 258 result.simple[cookie.label] = cookie.value; 259 }); 260 261 return result; 262 } 263 264 // syntactic sugar for binding to a method and preserve the object as the this value, useful when doing a lot of async binds 265 // miscLib.bind(foo.bar.baz.method, arg1, arg2) is equivalent to foo.bar.baz.method.bind(foo.bar.baz, "arg1", "arg2"); 266 let bind = function(obj, method) { 267 // v8 - argumentsToArray one-liner 268 let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } 269 args.splice(0, 2); 270 271 return obj[method].bind.apply(obj[method], [obj].concat(args)); 272 } 273 274 // trim the return of a callback so downstream functions don't get more values than they need which can throw object based async series/parallel in a tizzy 275 let trimReturn = function(count, cb) { 276 return function() { 277 // v8 - argumentsToArray one-liner 278 let args = new Array(arguments.length); for (let i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } 279 cb.apply(null, args.slice(0, count)); 280 } 281 } 282 283 // try/catch causes a v8 deopt so we move it to it's own function so that we don't force larger functions to deopt 284 let tryCatch = function(fn) { 285 try { 286 return fn(); 287 } catch (e) { 288 return e; 289 } 290 } 291 292 // This function allows us to capture stack traces in callbacks from native systems which provide poor stack traces (fs, http, dns etc) 293 let wrapErrorCb = function(cb) { 294 // stash the existing state of the stack 295 let frame = {}; 296 Error.captureStackTrace(frame); 297 return function(err) { 298 if (err) { 299 // use the constructor of the original error, so it may be SyntaxError, Error or who knows 300 let newErr = new err.constructor(err.message, err.code); 301 302 if (err.code !== undefined) { 303 newErr.code = err.code; 304 } 305 306 // replace the first line on the stack so that it properly says Error: Message based on the error type and message 307 newErr.stack = frame.stack.replace(/.*?\n/, err.name + ": " + err.message + "\n"); 308 return cb(newErr); 309 } 310 311 return cb.apply(null, arguments); 312 } 313 } 314 315 module.exports = { 316 baSort: baSort, 317 varLookup: varLookup, 318 setValue: setValue, 319 hashObject: hashObject, 320 memoize: memoize, 321 memoizeSync: memoizeSync, 322 extractCookies: extractCookies, 323 bind: bind, 324 trimReturn: trimReturn, 325 tryCatch: tryCatch, 326 wrapErrorCb: wrapErrorCb 327 }; 328});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.