├── .gitignore ├── README.MD ├── example-1.js ├── example-2.js ├── example-3.js ├── example-4.js ├── lib └── async-debugger.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Debugging async operations in Node.js 2 | 3 | Example code for [my blog post](http://www.the-data-wrangler.com/debugging-async-operations-node-js). 4 | 5 | This repo contains multiple examples of tracking and debugging asynchronous operation. 6 | 7 | [Click here to support my work](https://www.codecapers.com.au/about#support-my-work) 8 | -------------------------------------------------------------------------------- /example-1.js: -------------------------------------------------------------------------------- 1 | // 2 | // Run me with --expose-gc 3 | // 4 | // node --expose-gc example.js 5 | // 6 | 7 | const { AsyncDebugger } = require("./lib/async-debugger"); 8 | 9 | function doTimeout() { 10 | console.log("Starting timeout."); 11 | 12 | setTimeout(() => { 13 | console.log("Timeout finished."); 14 | }, 2000); 15 | } 16 | 17 | const asyncDebugger = new AsyncDebugger(); 18 | asyncDebugger.notifyComplete(() => console.log("********** All done! **********")); 19 | asyncDebugger.startTracking("test-1", doTimeout); 20 | 21 | console.log("End of script"); 22 | -------------------------------------------------------------------------------- /example-2.js: -------------------------------------------------------------------------------- 1 | // 2 | // Run me with --expose-gc 3 | // 4 | // node --expose-gc example.js 5 | // 6 | 7 | const { AsyncDebugger } = require("./lib/async-debugger"); 8 | 9 | console.log("Starting up!"); 10 | 11 | function doTimeout() { 12 | console.log("Starting timeout."); 13 | 14 | setTimeout(() => { 15 | console.log("Timeout finished."); 16 | }, 2000); 17 | } 18 | 19 | const asyncDebugger = new AsyncDebugger(); 20 | asyncDebugger.notifyComplete(() => console.log("********** All done! **********")); 21 | asyncDebugger.startTracking("test-1", doTimeout); 22 | 23 | console.log("End of script"); 24 | -------------------------------------------------------------------------------- /example-3.js: -------------------------------------------------------------------------------- 1 | // 2 | // Run me with --expose-gc 3 | // 4 | // node --expose-gc example.js 5 | // 6 | 7 | const { AsyncDebugger } = require("./lib/async-debugger"); 8 | 9 | console.log("Starting up!"); 10 | 11 | function doTimeout() { 12 | console.log("Starting timeout."); 13 | 14 | setTimeout(() => { 15 | 16 | setTimeout(() => { 17 | console.log("Timeout finished."); 18 | }, 2000); 19 | 20 | }, 2000); 21 | } 22 | 23 | const asyncDebugger = new AsyncDebugger(); 24 | asyncDebugger.notifyComplete(() => console.log("********** All done! **********")); 25 | asyncDebugger.startTracking("test-1", doTimeout); 26 | 27 | console.log("End of script"); 28 | -------------------------------------------------------------------------------- /example-4.js: -------------------------------------------------------------------------------- 1 | // 2 | // Run me with --expose-gc 3 | // 4 | // node --expose-gc example.js 5 | // 6 | 7 | const { AsyncDebugger } = require("./lib/async-debugger"); 8 | 9 | console.log("Starting up!"); 10 | 11 | function doTimeout() { 12 | console.log("Starting timeout."); 13 | 14 | setTimeout(() => { 15 | 16 | setTimeout(() => { 17 | console.log("Timeout finished."); 18 | }, 2000); 19 | 20 | }, 2000); 21 | } 22 | 23 | const asyncDebugger = new AsyncDebugger(); 24 | asyncDebugger.notifyComplete(() => { 25 | asyncDebugger.debug("test-1"); 26 | }); 27 | asyncDebugger.startTracking("test-1", doTimeout); 28 | 29 | console.log("End of script"); 30 | -------------------------------------------------------------------------------- /lib/async-debugger.js: -------------------------------------------------------------------------------- 1 | const async_hooks = require("async_hooks"); 2 | const fs = require("fs"); 3 | 4 | // 5 | // Invoke a callback function when all async operations have completed. 6 | // 7 | function checkAsyncOps(asyncDebugger, callback) { 8 | 9 | if (global.gc) { 10 | global.gc(); 11 | } 12 | 13 | if (asyncDebugger.getNumAsyncOps() === 0) { 14 | callback(); 15 | return; 16 | } 17 | 18 | setTimeout(() => checkAsyncOps(asyncDebugger, callback), 0); 19 | } 20 | 21 | // 22 | // Tracks asynchronous operations in specific segments of code. 23 | // 24 | class AsyncDebugger { 25 | 26 | // 27 | // Tracks async operations currently in progress. 28 | // 29 | curAsyncOps = new Map(); 30 | 31 | // 32 | // Async operations at the root of the hierarchy. 33 | // 34 | rootAsyncOps = new Map(); 35 | 36 | // 37 | // Records all async operations that have ever happened. 38 | // 39 | allAsyncOps = new Map(); 40 | 41 | // 42 | // Records the top-level execution contexts currently being tracked. 43 | // 44 | executionContexts = new Set(); 45 | 46 | // 47 | // Maps execution context ids to labels. 48 | // 49 | labelMap = new Map(); 50 | 51 | // 52 | // Records the number of async operations in progress for each label. 53 | // 54 | numAsyncOps = new Map(); 55 | 56 | // 57 | // Execute a callback when all async operations have completed. 58 | // Don't call this from inside the tracking context! 59 | // It includes async operations that will hang the tracking process. 60 | // 61 | notifyComplete(callback) { 62 | setTimeout(() => checkAsyncOps(this, callback), 0); 63 | } 64 | 65 | // 66 | // Track async operations initiated by the passed in function in a new async context. 67 | // 68 | startTracking(label, userCode) { 69 | this.lazyInit(); // Lazy initialise. 70 | 71 | const executionContext = new async_hooks.AsyncResource(label); 72 | executionContext.runInAsyncScope(() => { // Initiate a new execution context whose child async operations we will track. 73 | const executionContextAsyncId = async_hooks.executionAsyncId(); // Record the async ID for the new execution context. 74 | this.executionContexts.add(executionContextAsyncId); 75 | this.labelMap.set(executionContextAsyncId, label); 76 | userCode(); // Run user code in the new async execution context. 77 | }); 78 | } 79 | 80 | // 81 | // Determine the number of async operations for the requested label. 82 | // 83 | getNumAsyncOps(label) { 84 | if (label !== undefined) { 85 | return this.numAsyncOps.get(label) || 0; 86 | } 87 | else { 88 | return this.curAsyncOps.size; 89 | } 90 | } 91 | 92 | // 93 | // Debug print details of async operations. 94 | // 95 | dump() { 96 | const numAsyncOps = {}; 97 | const asyncOps = {}; 98 | const allOps = Array.from(this.curAsyncOps.values()); 99 | for (const entry of this.numAsyncOps.entries()) { // Translate to a hash of labels. 100 | const [ executionContextId, numOps ] = entry; 101 | const label = this.labelMap.get(executionContextId); 102 | numAsyncOps[label] = numOps; 103 | asyncOps[label] = allOps.filter(asyncOp => this.labelMap.get(asyncOp.executionContextId) === label); 104 | } 105 | 106 | fs.writeSync(1, `total #ops: ${this.getNumAsyncOps()}\n`); 107 | fs.writeSync(1, `#ops by label: ${JSON.stringify(numAsyncOps, null, 4)}\n`); 108 | fs.writeSync(1, `ops by label: ${JSON.stringify(asyncOps, null, 4)}\n`); 109 | } 110 | 111 | // 112 | // Write lines of output to the console. 113 | // 114 | writeLines(indentLevel, lines) { 115 | if (lines.length === 0) { 116 | return; 117 | } 118 | 119 | fs.writeSync(1, indentLevel + ` |- ${lines[0]}\n`); 120 | 121 | for (let i = 1; i < lines.length; ++i) { 122 | fs.writeSync(1, indentLevel + ` | ${lines[i]}\n`); 123 | } 124 | } 125 | 126 | // 127 | // Traverse a tree of async operations. 128 | // 129 | traverse(indentLevel, asyncOp) { 130 | const lines = [`${asyncOp.asyncId} - ${asyncOp.type} - ${asyncOp.status}`] 131 | .concat(asyncOp.stack 132 | .slice(1) 133 | .map(stackLine => " " + stackLine) // Indent the stack. 134 | ); 135 | this.writeLines(indentLevel, lines); 136 | 137 | for (const childOp of asyncOp.children.values()) { 138 | this.traverse(indentLevel + " | ", childOp); 139 | } 140 | } 141 | 142 | // 143 | // Debug a particular asynchronous operation. 144 | // 145 | debug(label) { 146 | for (const rootOp of this.rootAsyncOps.values()) { 147 | if (this.labelMap.get(rootOp.executionContextId) === label) { 148 | this.traverse("", rootOp); 149 | } 150 | } 151 | } 152 | 153 | // 154 | // Lazily initialise the tracker. 155 | // 156 | lazyInit() { 157 | if (!this.asyncHook) { 158 | // 159 | // Create the async hook. 160 | // 161 | this.asyncHook = async_hooks.createHook({ 162 | init: (asyncId, type, triggerAsyncId, resource) => { 163 | this.addAsyncOperation(asyncId, type, triggerAsyncId, resource); 164 | }, 165 | destroy: asyncId => { 166 | this.removeAsyncOperation(asyncId, "it was destroyed"); 167 | }, 168 | promiseResolve: asyncId => { 169 | this.removeAsyncOperation(asyncId, "it was resolved"); 170 | }, 171 | }); 172 | } 173 | 174 | this.asyncHook.enable(); 175 | } 176 | 177 | // 178 | // Disable the tracker. 179 | // 180 | deinit() { 181 | if (this.asyncHook) { 182 | this.asyncHook.disable(); 183 | } 184 | } 185 | 186 | // 187 | // Gets the execution context (if found) for a particular async ID. 188 | // Returns undefined if not found. 189 | // 190 | findExecutionContextId(asyncId) { 191 | if (this.executionContexts.has(asyncId)) { 192 | return asyncId; // This is the id of the execution context! 193 | } 194 | 195 | const asyncOp = this.allAsyncOps.get(asyncId); 196 | if (asyncOp) { 197 | return asyncOp.executionContextId; 198 | } 199 | 200 | return undefined; // This async operation is not being tracked! 201 | } 202 | 203 | // 204 | // Records an async operation. 205 | // 206 | addAsyncOperation(asyncId, type, triggerAsyncId, resource) { 207 | 208 | const executionContextId = this.findExecutionContextId(triggerAsyncId); 209 | if (executionContextId === undefined) { 210 | return; // This async operation is not being tracked! 211 | } 212 | 213 | // The triggering async operation has been traced back to a particular execution context. 214 | 215 | const error = {}; 216 | Error.captureStackTrace(error); 217 | 218 | const stack = error.stack.split("\n").map(line => line.trim()); 219 | 220 | const asyncOp = { 221 | asyncId, 222 | type, 223 | triggerAsyncId, 224 | children: new Map(), 225 | stack, 226 | status: "in-flight", 227 | executionContextId, 228 | }; 229 | 230 | const parentOperation = this.allAsyncOps.get(triggerAsyncId); 231 | if (parentOperation) { 232 | parentOperation.children.set(asyncId, asyncOp); // Record the hierarchy of asynchronous operations. 233 | } 234 | else { 235 | this.rootAsyncOps.set(asyncId, asyncOp); 236 | } 237 | 238 | this.curAsyncOps.set(asyncId, asyncOp); 239 | this.allAsyncOps.set(asyncId, asyncOp); 240 | 241 | // 242 | // Track the async operations for each execution context. 243 | // 244 | this.numAsyncOps.set(executionContextId, (this.numAsyncOps.get(executionContextId) || 0) + 1); 245 | 246 | fs.writeSync(1, `%% add ${asyncId}, type = ${type}, parent = ${triggerAsyncId}, context = ${executionContextId}, ${this.labelMap.get(executionContextId)} #ops = ${this.numAsyncOps.get(executionContextId)}, total #ops = ${this.curAsyncOps.size}\n`); 247 | // fs.writeSync(1, `-- stack\n${stack.join('\n')}\n`); 248 | 249 | // this.dump(); //todo: verbose only 250 | } 251 | 252 | // 253 | // Removes an async operation. 254 | // 255 | removeAsyncOperation(asyncId, reason) { 256 | const asyncOp = this.curAsyncOps.get(asyncId) 257 | if (!asyncOp) { 258 | // This async operation is not tracked. 259 | return; 260 | } 261 | 262 | asyncOp.status = "completed"; 263 | 264 | this.curAsyncOps.delete(asyncId); 265 | 266 | const executionContextId = this.findExecutionContextId(asyncId); 267 | if (executionContextId !== undefined) { 268 | const numAsyncOps = this.numAsyncOps.get(executionContextId); 269 | if (numAsyncOps !== undefined) { 270 | this.numAsyncOps.set(executionContextId, numAsyncOps-1); 271 | fs.writeSync(1, `%% remove ${asyncId}, reason = ${reason}, context = ${executionContextId}, ${this.labelMap.get(executionContextId)} #ops = ${this.numAsyncOps.get(executionContextId)}, total #ops = ${this.curAsyncOps.size}\n`); 272 | } 273 | } 274 | 275 | // this.dump(); //todo: verbose only 276 | } 277 | } 278 | 279 | module.exports = { 280 | AsyncDebugger, 281 | }; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "debugging-async-operations-in-nodejs", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.1.1", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 10 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 11 | }, 12 | "ansi-align": { 13 | "version": "2.0.0", 14 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", 15 | "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", 16 | "requires": { 17 | "string-width": "^2.0.0" 18 | } 19 | }, 20 | "ansi-regex": { 21 | "version": "3.0.0", 22 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 23 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 24 | }, 25 | "ansi-styles": { 26 | "version": "3.2.1", 27 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 28 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 29 | "requires": { 30 | "color-convert": "^1.9.0" 31 | } 32 | }, 33 | "anymatch": { 34 | "version": "3.1.1", 35 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 36 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 37 | "requires": { 38 | "normalize-path": "^3.0.0", 39 | "picomatch": "^2.0.4" 40 | } 41 | }, 42 | "balanced-match": { 43 | "version": "1.0.0", 44 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 45 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 46 | }, 47 | "binary-extensions": { 48 | "version": "2.0.0", 49 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", 50 | "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" 51 | }, 52 | "boxen": { 53 | "version": "1.3.0", 54 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", 55 | "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", 56 | "requires": { 57 | "ansi-align": "^2.0.0", 58 | "camelcase": "^4.0.0", 59 | "chalk": "^2.0.1", 60 | "cli-boxes": "^1.0.0", 61 | "string-width": "^2.0.0", 62 | "term-size": "^1.2.0", 63 | "widest-line": "^2.0.0" 64 | } 65 | }, 66 | "brace-expansion": { 67 | "version": "1.1.11", 68 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 69 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 70 | "requires": { 71 | "balanced-match": "^1.0.0", 72 | "concat-map": "0.0.1" 73 | } 74 | }, 75 | "braces": { 76 | "version": "3.0.2", 77 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 78 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 79 | "requires": { 80 | "fill-range": "^7.0.1" 81 | } 82 | }, 83 | "camelcase": { 84 | "version": "4.1.0", 85 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 86 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" 87 | }, 88 | "capture-stack-trace": { 89 | "version": "1.0.1", 90 | "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", 91 | "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" 92 | }, 93 | "chalk": { 94 | "version": "2.4.2", 95 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 96 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 97 | "requires": { 98 | "ansi-styles": "^3.2.1", 99 | "escape-string-regexp": "^1.0.5", 100 | "supports-color": "^5.3.0" 101 | } 102 | }, 103 | "chokidar": { 104 | "version": "3.3.1", 105 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", 106 | "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", 107 | "requires": { 108 | "anymatch": "~3.1.1", 109 | "braces": "~3.0.2", 110 | "fsevents": "~2.1.2", 111 | "glob-parent": "~5.1.0", 112 | "is-binary-path": "~2.1.0", 113 | "is-glob": "~4.0.1", 114 | "normalize-path": "~3.0.0", 115 | "readdirp": "~3.3.0" 116 | } 117 | }, 118 | "ci-info": { 119 | "version": "1.6.0", 120 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", 121 | "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" 122 | }, 123 | "cli-boxes": { 124 | "version": "1.0.0", 125 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 126 | "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" 127 | }, 128 | "color-convert": { 129 | "version": "1.9.3", 130 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 131 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 132 | "requires": { 133 | "color-name": "1.1.3" 134 | } 135 | }, 136 | "color-name": { 137 | "version": "1.1.3", 138 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 139 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 140 | }, 141 | "concat-map": { 142 | "version": "0.0.1", 143 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 144 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 145 | }, 146 | "configstore": { 147 | "version": "3.1.2", 148 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", 149 | "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", 150 | "requires": { 151 | "dot-prop": "^4.1.0", 152 | "graceful-fs": "^4.1.2", 153 | "make-dir": "^1.0.0", 154 | "unique-string": "^1.0.0", 155 | "write-file-atomic": "^2.0.0", 156 | "xdg-basedir": "^3.0.0" 157 | } 158 | }, 159 | "create-error-class": { 160 | "version": "3.0.2", 161 | "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", 162 | "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", 163 | "requires": { 164 | "capture-stack-trace": "^1.0.0" 165 | } 166 | }, 167 | "cross-spawn": { 168 | "version": "5.1.0", 169 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 170 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 171 | "requires": { 172 | "lru-cache": "^4.0.1", 173 | "shebang-command": "^1.2.0", 174 | "which": "^1.2.9" 175 | } 176 | }, 177 | "crypto-random-string": { 178 | "version": "1.0.0", 179 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", 180 | "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" 181 | }, 182 | "debug": { 183 | "version": "3.2.6", 184 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 185 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 186 | "requires": { 187 | "ms": "^2.1.1" 188 | } 189 | }, 190 | "deep-extend": { 191 | "version": "0.6.0", 192 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 193 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 194 | }, 195 | "dot-prop": { 196 | "version": "4.2.0", 197 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", 198 | "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", 199 | "requires": { 200 | "is-obj": "^1.0.0" 201 | } 202 | }, 203 | "duplexer3": { 204 | "version": "0.1.4", 205 | "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", 206 | "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" 207 | }, 208 | "escape-string-regexp": { 209 | "version": "1.0.5", 210 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 211 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 212 | }, 213 | "execa": { 214 | "version": "0.7.0", 215 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 216 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 217 | "requires": { 218 | "cross-spawn": "^5.0.1", 219 | "get-stream": "^3.0.0", 220 | "is-stream": "^1.1.0", 221 | "npm-run-path": "^2.0.0", 222 | "p-finally": "^1.0.0", 223 | "signal-exit": "^3.0.0", 224 | "strip-eof": "^1.0.0" 225 | } 226 | }, 227 | "fill-range": { 228 | "version": "7.0.1", 229 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 230 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 231 | "requires": { 232 | "to-regex-range": "^5.0.1" 233 | } 234 | }, 235 | "fsevents": { 236 | "version": "2.1.2", 237 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", 238 | "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", 239 | "optional": true 240 | }, 241 | "get-stream": { 242 | "version": "3.0.0", 243 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 244 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 245 | }, 246 | "glob-parent": { 247 | "version": "5.1.0", 248 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", 249 | "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", 250 | "requires": { 251 | "is-glob": "^4.0.1" 252 | } 253 | }, 254 | "global-dirs": { 255 | "version": "0.1.1", 256 | "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", 257 | "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", 258 | "requires": { 259 | "ini": "^1.3.4" 260 | } 261 | }, 262 | "got": { 263 | "version": "6.7.1", 264 | "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", 265 | "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", 266 | "requires": { 267 | "create-error-class": "^3.0.0", 268 | "duplexer3": "^0.1.4", 269 | "get-stream": "^3.0.0", 270 | "is-redirect": "^1.0.0", 271 | "is-retry-allowed": "^1.0.0", 272 | "is-stream": "^1.0.0", 273 | "lowercase-keys": "^1.0.0", 274 | "safe-buffer": "^5.0.1", 275 | "timed-out": "^4.0.0", 276 | "unzip-response": "^2.0.1", 277 | "url-parse-lax": "^1.0.0" 278 | } 279 | }, 280 | "graceful-fs": { 281 | "version": "4.2.3", 282 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 283 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" 284 | }, 285 | "has-flag": { 286 | "version": "3.0.0", 287 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 288 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 289 | }, 290 | "ignore-by-default": { 291 | "version": "1.0.1", 292 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 293 | "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" 294 | }, 295 | "import-lazy": { 296 | "version": "2.1.0", 297 | "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", 298 | "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" 299 | }, 300 | "imurmurhash": { 301 | "version": "0.1.4", 302 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 303 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 304 | }, 305 | "ini": { 306 | "version": "1.3.5", 307 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 308 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 309 | }, 310 | "is-binary-path": { 311 | "version": "2.1.0", 312 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 313 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 314 | "requires": { 315 | "binary-extensions": "^2.0.0" 316 | } 317 | }, 318 | "is-ci": { 319 | "version": "1.2.1", 320 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", 321 | "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", 322 | "requires": { 323 | "ci-info": "^1.5.0" 324 | } 325 | }, 326 | "is-extglob": { 327 | "version": "2.1.1", 328 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 329 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" 330 | }, 331 | "is-fullwidth-code-point": { 332 | "version": "2.0.0", 333 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 334 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 335 | }, 336 | "is-glob": { 337 | "version": "4.0.1", 338 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 339 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 340 | "requires": { 341 | "is-extglob": "^2.1.1" 342 | } 343 | }, 344 | "is-installed-globally": { 345 | "version": "0.1.0", 346 | "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", 347 | "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", 348 | "requires": { 349 | "global-dirs": "^0.1.0", 350 | "is-path-inside": "^1.0.0" 351 | } 352 | }, 353 | "is-npm": { 354 | "version": "1.0.0", 355 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", 356 | "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" 357 | }, 358 | "is-number": { 359 | "version": "7.0.0", 360 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 361 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 362 | }, 363 | "is-obj": { 364 | "version": "1.0.1", 365 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", 366 | "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" 367 | }, 368 | "is-path-inside": { 369 | "version": "1.0.1", 370 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 371 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 372 | "requires": { 373 | "path-is-inside": "^1.0.1" 374 | } 375 | }, 376 | "is-redirect": { 377 | "version": "1.0.0", 378 | "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", 379 | "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" 380 | }, 381 | "is-retry-allowed": { 382 | "version": "1.2.0", 383 | "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", 384 | "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" 385 | }, 386 | "is-stream": { 387 | "version": "1.1.0", 388 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 389 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 390 | }, 391 | "isexe": { 392 | "version": "2.0.0", 393 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 394 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 395 | }, 396 | "latest-version": { 397 | "version": "3.1.0", 398 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", 399 | "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", 400 | "requires": { 401 | "package-json": "^4.0.0" 402 | } 403 | }, 404 | "lowercase-keys": { 405 | "version": "1.0.1", 406 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", 407 | "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" 408 | }, 409 | "lru-cache": { 410 | "version": "4.1.5", 411 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 412 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 413 | "requires": { 414 | "pseudomap": "^1.0.2", 415 | "yallist": "^2.1.2" 416 | } 417 | }, 418 | "make-dir": { 419 | "version": "1.3.0", 420 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", 421 | "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", 422 | "requires": { 423 | "pify": "^3.0.0" 424 | } 425 | }, 426 | "minimatch": { 427 | "version": "3.0.4", 428 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 429 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 430 | "requires": { 431 | "brace-expansion": "^1.1.7" 432 | } 433 | }, 434 | "minimist": { 435 | "version": "1.2.0", 436 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 437 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 438 | }, 439 | "ms": { 440 | "version": "2.1.2", 441 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 442 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 443 | }, 444 | "nodemon": { 445 | "version": "2.0.2", 446 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz", 447 | "integrity": "sha512-GWhYPMfde2+M0FsHnggIHXTqPDHXia32HRhh6H0d75Mt9FKUoCBvumNHr7LdrpPBTKxsWmIEOjoN+P4IU6Hcaw==", 448 | "requires": { 449 | "chokidar": "^3.2.2", 450 | "debug": "^3.2.6", 451 | "ignore-by-default": "^1.0.1", 452 | "minimatch": "^3.0.4", 453 | "pstree.remy": "^1.1.7", 454 | "semver": "^5.7.1", 455 | "supports-color": "^5.5.0", 456 | "touch": "^3.1.0", 457 | "undefsafe": "^2.0.2", 458 | "update-notifier": "^2.5.0" 459 | } 460 | }, 461 | "nopt": { 462 | "version": "1.0.10", 463 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 464 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", 465 | "requires": { 466 | "abbrev": "1" 467 | } 468 | }, 469 | "normalize-path": { 470 | "version": "3.0.0", 471 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 472 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 473 | }, 474 | "npm-run-path": { 475 | "version": "2.0.2", 476 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 477 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 478 | "requires": { 479 | "path-key": "^2.0.0" 480 | } 481 | }, 482 | "oo-ascii-tree": { 483 | "version": "1.0.0", 484 | "resolved": "https://registry.npmjs.org/oo-ascii-tree/-/oo-ascii-tree-1.0.0.tgz", 485 | "integrity": "sha512-881ufRp1k77ZDObBjABZYgf6/EG8UXFaiZPZJILkwgeZp/xeXZDnBagHHn+rLCTc6dmyOKJf56iT7raK3nb47A==" 486 | }, 487 | "p-finally": { 488 | "version": "1.0.0", 489 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 490 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 491 | }, 492 | "package-json": { 493 | "version": "4.0.1", 494 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", 495 | "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", 496 | "requires": { 497 | "got": "^6.7.1", 498 | "registry-auth-token": "^3.0.1", 499 | "registry-url": "^3.0.3", 500 | "semver": "^5.1.0" 501 | } 502 | }, 503 | "path-is-inside": { 504 | "version": "1.0.2", 505 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 506 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" 507 | }, 508 | "path-key": { 509 | "version": "2.0.1", 510 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 511 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 512 | }, 513 | "picomatch": { 514 | "version": "2.2.1", 515 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", 516 | "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==" 517 | }, 518 | "pify": { 519 | "version": "3.0.0", 520 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 521 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" 522 | }, 523 | "prepend-http": { 524 | "version": "1.0.4", 525 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", 526 | "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" 527 | }, 528 | "pseudomap": { 529 | "version": "1.0.2", 530 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 531 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 532 | }, 533 | "pstree.remy": { 534 | "version": "1.1.7", 535 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", 536 | "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==" 537 | }, 538 | "rc": { 539 | "version": "1.2.8", 540 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 541 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 542 | "requires": { 543 | "deep-extend": "^0.6.0", 544 | "ini": "~1.3.0", 545 | "minimist": "^1.2.0", 546 | "strip-json-comments": "~2.0.1" 547 | } 548 | }, 549 | "readdirp": { 550 | "version": "3.3.0", 551 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", 552 | "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", 553 | "requires": { 554 | "picomatch": "^2.0.7" 555 | } 556 | }, 557 | "registry-auth-token": { 558 | "version": "3.4.0", 559 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", 560 | "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", 561 | "requires": { 562 | "rc": "^1.1.6", 563 | "safe-buffer": "^5.0.1" 564 | } 565 | }, 566 | "registry-url": { 567 | "version": "3.1.0", 568 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 569 | "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", 570 | "requires": { 571 | "rc": "^1.0.1" 572 | } 573 | }, 574 | "safe-buffer": { 575 | "version": "5.2.0", 576 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", 577 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" 578 | }, 579 | "semver": { 580 | "version": "5.7.1", 581 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 582 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 583 | }, 584 | "semver-diff": { 585 | "version": "2.1.0", 586 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", 587 | "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", 588 | "requires": { 589 | "semver": "^5.0.3" 590 | } 591 | }, 592 | "shebang-command": { 593 | "version": "1.2.0", 594 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 595 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 596 | "requires": { 597 | "shebang-regex": "^1.0.0" 598 | } 599 | }, 600 | "shebang-regex": { 601 | "version": "1.0.0", 602 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 603 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 604 | }, 605 | "signal-exit": { 606 | "version": "3.0.2", 607 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 608 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 609 | }, 610 | "string-width": { 611 | "version": "2.1.1", 612 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 613 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 614 | "requires": { 615 | "is-fullwidth-code-point": "^2.0.0", 616 | "strip-ansi": "^4.0.0" 617 | } 618 | }, 619 | "strip-ansi": { 620 | "version": "4.0.0", 621 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 622 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 623 | "requires": { 624 | "ansi-regex": "^3.0.0" 625 | } 626 | }, 627 | "strip-eof": { 628 | "version": "1.0.0", 629 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 630 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 631 | }, 632 | "strip-json-comments": { 633 | "version": "2.0.1", 634 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 635 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 636 | }, 637 | "supports-color": { 638 | "version": "5.5.0", 639 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 640 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 641 | "requires": { 642 | "has-flag": "^3.0.0" 643 | } 644 | }, 645 | "term-size": { 646 | "version": "1.2.0", 647 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", 648 | "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", 649 | "requires": { 650 | "execa": "^0.7.0" 651 | } 652 | }, 653 | "timed-out": { 654 | "version": "4.0.1", 655 | "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", 656 | "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" 657 | }, 658 | "to-regex-range": { 659 | "version": "5.0.1", 660 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 661 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 662 | "requires": { 663 | "is-number": "^7.0.0" 664 | } 665 | }, 666 | "touch": { 667 | "version": "3.1.0", 668 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 669 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 670 | "requires": { 671 | "nopt": "~1.0.10" 672 | } 673 | }, 674 | "undefsafe": { 675 | "version": "2.0.3", 676 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", 677 | "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", 678 | "requires": { 679 | "debug": "^2.2.0" 680 | }, 681 | "dependencies": { 682 | "debug": { 683 | "version": "2.6.9", 684 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 685 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 686 | "requires": { 687 | "ms": "2.0.0" 688 | } 689 | }, 690 | "ms": { 691 | "version": "2.0.0", 692 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 693 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 694 | } 695 | } 696 | }, 697 | "unique-string": { 698 | "version": "1.0.0", 699 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", 700 | "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", 701 | "requires": { 702 | "crypto-random-string": "^1.0.0" 703 | } 704 | }, 705 | "unzip-response": { 706 | "version": "2.0.1", 707 | "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", 708 | "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" 709 | }, 710 | "update-notifier": { 711 | "version": "2.5.0", 712 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", 713 | "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", 714 | "requires": { 715 | "boxen": "^1.2.1", 716 | "chalk": "^2.0.1", 717 | "configstore": "^3.0.0", 718 | "import-lazy": "^2.1.0", 719 | "is-ci": "^1.0.10", 720 | "is-installed-globally": "^0.1.0", 721 | "is-npm": "^1.0.0", 722 | "latest-version": "^3.0.0", 723 | "semver-diff": "^2.0.0", 724 | "xdg-basedir": "^3.0.0" 725 | } 726 | }, 727 | "url-parse-lax": { 728 | "version": "1.0.0", 729 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", 730 | "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", 731 | "requires": { 732 | "prepend-http": "^1.0.1" 733 | } 734 | }, 735 | "which": { 736 | "version": "1.3.1", 737 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 738 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 739 | "requires": { 740 | "isexe": "^2.0.0" 741 | } 742 | }, 743 | "widest-line": { 744 | "version": "2.0.1", 745 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", 746 | "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", 747 | "requires": { 748 | "string-width": "^2.1.1" 749 | } 750 | }, 751 | "write-file-atomic": { 752 | "version": "2.4.3", 753 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", 754 | "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", 755 | "requires": { 756 | "graceful-fs": "^4.1.11", 757 | "imurmurhash": "^0.1.4", 758 | "signal-exit": "^3.0.2" 759 | } 760 | }, 761 | "xdg-basedir": { 762 | "version": "3.0.0", 763 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", 764 | "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" 765 | }, 766 | "yallist": { 767 | "version": "2.1.2", 768 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 769 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 770 | } 771 | } 772 | } 773 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "debugging-async-operations-in-nodejs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "Nodejs", 11 | "Node.js", 12 | "async", 13 | "asynchronous", 14 | "debug", 15 | "debugging" 16 | ], 17 | "author": "ashley@codecapers.com.au", 18 | "license": "MIT", 19 | "dependencies": { 20 | "nodemon": "^2.0.2", 21 | "oo-ascii-tree": "^1.0.0" 22 | } 23 | } 24 | --------------------------------------------------------------------------------