├── .eslintrc.json ├── .gitignore ├── binding.gyp ├── examples ├── client.js ├── http.js ├── server.js ├── timers.js └── udp.js ├── libs ├── assert.js ├── bintools.js ├── codec-http.js └── codec-tools.js ├── package-lock.json ├── package.json ├── src ├── nuv.c └── nuv.js └── tests ├── test-tcp.js └── test-udp.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 9, 4 | "ecmaFeatures": { 5 | "jsx": true 6 | }, 7 | "sourceType": "module" 8 | }, 9 | 10 | "env": { 11 | "es6": true, 12 | "node": true 13 | }, 14 | 15 | "plugins": [ 16 | "import", 17 | "node", 18 | "promise", 19 | "standard" 20 | ], 21 | 22 | "globals": { 23 | "document": false, 24 | "navigator": false, 25 | "window": false 26 | }, 27 | 28 | "rules": { 29 | "accessor-pairs": "error", 30 | "arrow-spacing": ["error", { "before": true, "after": true }], 31 | "block-spacing": ["error", "always"], 32 | "brace-style": ["error", "1tbs", { "allowSingleLine": true }], 33 | "camelcase": ["error", { "properties": "never" }], 34 | "comma-dangle": ["error", { 35 | "arrays": "never", 36 | "objects": "never", 37 | "imports": "never", 38 | "exports": "never", 39 | "functions": "never" 40 | }], 41 | "comma-spacing": ["error", { "before": false, "after": true }], 42 | "comma-style": ["error", "last"], 43 | "constructor-super": "error", 44 | "curly": ["error", "multi-line"], 45 | "dot-location": ["error", "property"], 46 | "eol-last": "error", 47 | "eqeqeq": ["error", "always", { "null": "ignore" }], 48 | "func-call-spacing": ["error", "never"], 49 | "generator-star-spacing": ["error", { "before": true, "after": true }], 50 | "handle-callback-err": ["error", "^(err|error)$" ], 51 | "indent": ["error", 2, { 52 | "SwitchCase": 1, 53 | "VariableDeclarator": 1, 54 | "outerIIFEBody": 1, 55 | "MemberExpression": 1, 56 | "FunctionDeclaration": { "parameters": 1, "body": 1 }, 57 | "FunctionExpression": { "parameters": 1, "body": 1 }, 58 | "CallExpression": { "arguments": 1 }, 59 | "ArrayExpression": 1, 60 | "ObjectExpression": 1, 61 | "ImportDeclaration": 1, 62 | "flatTernaryExpressions": false, 63 | "ignoreComments": false 64 | }], 65 | "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], 66 | "keyword-spacing": ["error", { "before": true, "after": true }], 67 | "new-cap": ["error", { "newIsCap": true, "capIsNew": false }], 68 | "new-parens": "error", 69 | "no-array-constructor": "error", 70 | "no-caller": "error", 71 | "no-class-assign": "error", 72 | "no-compare-neg-zero": "error", 73 | "no-cond-assign": "error", 74 | "no-const-assign": "error", 75 | "no-constant-condition": ["error", { "checkLoops": false }], 76 | "no-control-regex": "error", 77 | "no-debugger": "error", 78 | "no-delete-var": "error", 79 | "no-dupe-args": "error", 80 | "no-dupe-class-members": "error", 81 | "no-dupe-keys": "error", 82 | "no-duplicate-case": "error", 83 | "no-empty-character-class": "error", 84 | "no-empty-pattern": "error", 85 | "no-eval": "error", 86 | "no-ex-assign": "error", 87 | "no-extend-native": "error", 88 | "no-extra-bind": "error", 89 | "no-extra-boolean-cast": "error", 90 | "no-extra-parens": ["error", "functions"], 91 | "no-fallthrough": "error", 92 | "no-floating-decimal": "error", 93 | "no-func-assign": "error", 94 | "no-global-assign": "error", 95 | "no-implied-eval": "error", 96 | "no-inner-declarations": ["error", "functions"], 97 | "no-invalid-regexp": "error", 98 | "no-irregular-whitespace": "error", 99 | "no-iterator": "error", 100 | "no-label-var": "error", 101 | "no-labels": ["error", { "allowLoop": false, "allowSwitch": false }], 102 | "no-lone-blocks": "error", 103 | "no-mixed-operators": ["error", { 104 | "groups": [ 105 | ["==", "!=", "===", "!==", ">", ">=", "<", "<="], 106 | ["&&", "||"], 107 | ["in", "instanceof"] 108 | ], 109 | "allowSamePrecedence": true 110 | }], 111 | "no-mixed-spaces-and-tabs": "error", 112 | "no-multi-spaces": "error", 113 | "no-multi-str": "error", 114 | "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }], 115 | "no-negated-in-lhs": "error", 116 | "no-new": "error", 117 | "no-new-func": "error", 118 | "no-new-object": "error", 119 | "no-new-require": "error", 120 | "no-new-symbol": "error", 121 | "no-new-wrappers": "error", 122 | "no-obj-calls": "error", 123 | "no-octal": "error", 124 | "no-octal-escape": "error", 125 | "no-path-concat": "error", 126 | "no-proto": "error", 127 | "no-redeclare": "error", 128 | "no-regex-spaces": "error", 129 | "no-return-assign": ["error", "except-parens"], 130 | "no-return-await": "error", 131 | "no-self-assign": "error", 132 | "no-self-compare": "error", 133 | "no-sequences": "error", 134 | "no-shadow-restricted-names": "error", 135 | "no-sparse-arrays": "error", 136 | "no-tabs": "error", 137 | "no-template-curly-in-string": "error", 138 | "no-this-before-super": "error", 139 | "no-throw-literal": "error", 140 | "no-trailing-spaces": "error", 141 | "no-undef": "error", 142 | "no-undef-init": "error", 143 | "no-unexpected-multiline": "error", 144 | "no-unmodified-loop-condition": "error", 145 | "no-unneeded-ternary": ["error", { "defaultAssignment": false }], 146 | "no-unreachable": "error", 147 | "no-unsafe-finally": "error", 148 | "no-unsafe-negation": "error", 149 | "no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }], 150 | "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }], 151 | "no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }], 152 | "no-useless-call": "error", 153 | "no-useless-computed-key": "error", 154 | "no-useless-constructor": "error", 155 | "no-useless-escape": "error", 156 | "no-useless-rename": "error", 157 | "no-useless-return": "error", 158 | "no-whitespace-before-property": "error", 159 | "no-with": "error", 160 | "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], 161 | "one-var": ["error", { "initialized": "never" }], 162 | "operator-linebreak": ["error", "after", { "overrides": { "?": "before", ":": "before" } }], 163 | "padded-blocks": ["error", { "blocks": "never", "switches": "never", "classes": "never" }], 164 | "prefer-promise-reject-errors": "error", 165 | "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }], 166 | "rest-spread-spacing": ["error", "never"], 167 | "semi": ["error", "never"], 168 | "semi-spacing": ["error", { "before": false, "after": true }], 169 | "space-before-blocks": ["error", "always"], 170 | "space-before-function-paren": ["error", "always"], 171 | "space-in-parens": ["error", "never"], 172 | "space-infix-ops": "error", 173 | "space-unary-ops": ["error", { "words": true, "nonwords": false }], 174 | "spaced-comment": ["error", "always", { 175 | "line": { "markers": ["*package", "!", "/", ",", "="] }, 176 | "block": { "balanced": true, "markers": ["*package", "!", ",", ":", "::", "flow-include"], "exceptions": ["*"] } 177 | }], 178 | "symbol-description": "error", 179 | "template-curly-spacing": ["error", "never"], 180 | "template-tag-spacing": ["error", "never"], 181 | "unicode-bom": ["error", "never"], 182 | "use-isnan": "error", 183 | "valid-typeof": ["error", { "requireStringLiterals": true }], 184 | "wrap-iife": ["error", "any", { "functionPrototypeMethods": true }], 185 | "yield-star-spacing": ["error", "both"], 186 | "yoda": ["error", "never"], 187 | 188 | "import/export": "error", 189 | "import/first": "error", 190 | "import/no-duplicates": "error", 191 | "import/no-webpack-loader-syntax": "error", 192 | 193 | "node/no-deprecated-api": "error", 194 | "node/process-exit-as-throw": "error", 195 | 196 | "promise/param-names": "error", 197 | 198 | "standard/array-bracket-even-spacing": ["error", "either"], 199 | "standard/computed-property-even-spacing": ["error", "even"], 200 | "standard/no-callback-literal": "error", 201 | "standard/object-curly-even-spacing": ["error", "either"] 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules 3 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [{ 3 | "target_name": "nuv", 4 | "sources": [ "./src/nuv.c" ], 5 | "include_dirs": [ 6 | " socket.read() 15 | const socketWrite = async value => socket.write(value) 16 | const read = decoder(socketRead) 17 | const write = encoder(socketWrite) 18 | 19 | // Use async iterators to read TCP packets off the socket. 20 | console.log(read) 21 | for await (const head of read) { 22 | console.log(read) 23 | const chunks = [] 24 | console.log(head) 25 | chunks.push(Buffer.from(`${head.method} ${head.path} HTTP/${head.version}\n`)) 26 | let i = 0 27 | for (const val of head.headers) { 28 | chunks.push(Buffer.from(val + (i++ % 2 ? '\n' : ': '))) 29 | } 30 | chunks.push(Buffer.from('\n')) 31 | for await (const chunk of read) { 32 | console.log(read) 33 | console.log(chunk) 34 | chunks.push(chunk) 35 | chunks.push(Buffer.from('\n')) 36 | } 37 | const body = Buffer.concat(chunks) 38 | console.log(read) 39 | console.log(body) 40 | console.log(write) 41 | await write({ 42 | code: 200, 43 | headers: [ 44 | 'Content-Length', body.length 45 | ] 46 | }) 47 | console.log(write) 48 | await write(body) 49 | console.log(write) 50 | } 51 | await socket.write() 52 | } 53 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | const { Server } = require('..') 2 | 3 | // Easy server creation with chainable methods. 4 | console.log('Listening:', new Server({ onConnection }) 5 | .bind('0.0.0.0', 8080) 6 | .listen(128) 7 | .sockname) 8 | 9 | async function onConnection () { 10 | // Accept the incoming connection and create a socket 11 | let socket = this.accept() 12 | console.log('Accepted:', socket.sockname, socket.peername) 13 | 14 | // Use async iterators to read TCP packets off the socket. 15 | for await (let chunk of socket) { 16 | console.log('Echo:', { chunk: chunk.toString() }) 17 | 18 | // Since we're waiting on writes to flush, there is automatic backpressure 19 | // all the way back. The iterator automatically calls read_start/read-stop. 20 | await socket.write(chunk) 21 | } 22 | 23 | // When the iterator exits, it means the remote side sent an EOF 24 | console.log('Client sent EOF, shutting down...') 25 | 26 | // Send an extra message to client 27 | await socket.write('Server shutting down...') 28 | 29 | // Writing nothing flushes any pending data and shuts down the write side. 30 | await socket.write() 31 | 32 | // We can now close our socket and the server in general 33 | await socket.close() 34 | await this.close() 35 | } 36 | -------------------------------------------------------------------------------- /examples/timers.js: -------------------------------------------------------------------------------- 1 | const { Timer } = require('..') 2 | 3 | function setTimeout (fn, ms = 0, ...args) { 4 | let timer = new Timer({ 5 | onTimeout () { 6 | clearTimeout(timer) 7 | fn(...args) 8 | } 9 | }) 10 | timer.start(ms) 11 | return timer 12 | } 13 | 14 | function setInterval (fn, ms = 0, ...args) { 15 | let timer = new Timer({ 16 | onTimeout () { fn(...args) } 17 | }) 18 | timer.start(ms, ms) 19 | return timer 20 | } 21 | 22 | function clearTimeout (timer) { 23 | timer.stop() 24 | timer.close() 25 | } 26 | 27 | let clearInterval = clearTimeout 28 | 29 | let idle = new Timer({ 30 | onTimeout () { 31 | console.log('Finally idle') 32 | idle.close() 33 | } 34 | }) 35 | idle.start(0, 100) 36 | console.log('Starting idle timer...', idle) 37 | 38 | console.log('This timeout should cancel') 39 | let timeout = setTimeout(() => { 40 | throw new Error("OOPS! This shouldn't happen") 41 | }, 1000) 42 | console.log('First timeout', timeout) 43 | console.log('first repeat', timeout.repeat) 44 | 45 | console.log('Starting another timeout...') 46 | setTimeout((...args) => { 47 | idle.again() 48 | console.log('Canceling first timeout') 49 | clearTimeout(timeout) 50 | console.log('Second timeout fired', args) 51 | }, 500, 1, 2, 3) 52 | 53 | console.log('Starting interval') 54 | let count = 10 55 | let interval 56 | interval = setInterval((...args) => { 57 | idle.again() 58 | console.log('tick...', count, args) 59 | if (!(--count)) { 60 | clearInterval(interval) 61 | console.log('done') 62 | } 63 | }, 100, 1, 2, 3) 64 | console.log('interval', interval) 65 | 66 | console.log('Shrinking interval') 67 | let shrink = new Timer() 68 | shrink.start(0, 400) 69 | shrink.onTimeout = () => { 70 | idle.again() 71 | console.log('Shrink', shrink.repeat) 72 | shrink.repeat >>>= 1 73 | if (!shrink.repeat) shrink.close() 74 | } 75 | idle.again() 76 | -------------------------------------------------------------------------------- /examples/udp.js: -------------------------------------------------------------------------------- 1 | const { Udp } = require('..') 2 | 3 | async function serverMain () { 4 | let server = new Udp() 5 | server.bind('0.0.0.0', 1337) 6 | console.log(server.sockname) 7 | for await (let frame of server) { 8 | console.log(frame) 9 | server.send(frame.ip, frame.port, frame.data) 10 | await server.close() 11 | } 12 | } 13 | 14 | async function clientMain () { 15 | let client = new Udp() 16 | console.log('Sending') 17 | await client.send('127.0.0.1', 1337, 'Hello World') 18 | console.log('Sent!') 19 | console.log('Echo back', await client.recv()) 20 | await client.close() 21 | } 22 | 23 | serverMain().catch(console.error) 24 | clientMain().catch(console.error) 25 | -------------------------------------------------------------------------------- /libs/assert.js: -------------------------------------------------------------------------------- 1 | exports.assert = assert 2 | 3 | // lua-style assert helper 4 | function assert (val, message) { 5 | if (!val) throw new Error(message || 'Assertion Failed') 6 | return val 7 | } 8 | -------------------------------------------------------------------------------- /libs/bintools.js: -------------------------------------------------------------------------------- 1 | 2 | // TYPES: 3 | // bin - a node Buffer containing binary data. 4 | // str - a normal unicode string. 5 | // raw - a string where each character's charCode is a byte value. (utf-8) 6 | // hex - a string holding binary data as lowercase hexadecimal. 7 | // b64 - a string holding binary data in base64 encoding. 8 | 9 | // Convert a raw string into a Binary 10 | exports.rawToBin = rawToBin 11 | function rawToBin (raw, start, end) { 12 | raw = '' + raw 13 | start = start == null ? 0 : start | 0 14 | end = end == null ? raw.length : end | 0 15 | let len = end - start 16 | let bin = Buffer.alloc(len) 17 | for (let i = 0; i < len; i++) { 18 | bin[i] = raw.charCodeAt(i + start) 19 | } 20 | return bin 21 | } 22 | 23 | exports.binToRaw = binToRaw 24 | function binToRaw (bin, start, end) { 25 | start = start == null ? 0 : start | 0 26 | end = end == null ? bin.length : end | 0 27 | let raw = '' 28 | for (let i = start || 0; i < end; i++) { 29 | raw += String.fromCharCode(bin[i]) 30 | } 31 | return raw 32 | } 33 | 34 | exports.binToHex = binToHex 35 | function binToHex (bin, start, end) { 36 | start = start == null ? 0 : start | 0 37 | end = end == null ? bin.length : end | 0 38 | let hex = '' 39 | for (let i = start; i < end; i++) { 40 | let byte = bin[i] 41 | hex += (byte < 0x10 ? '0' : '') + byte.toString(16) 42 | } 43 | return hex 44 | } 45 | 46 | exports.hexToBin = hexToBin 47 | function hexToBin (hex, start, end) { 48 | hex = '' + hex 49 | start = start == null ? 0 : start | 0 50 | end = end == null ? hex.length : end | 0 51 | let len = (end - start) >> 1 52 | let bin = Buffer.alloc(len) 53 | let offset = 0 54 | for (let i = start; i < end; i += 2) { 55 | bin[offset++] = parseInt(hex.substr(i, 2), 16) 56 | } 57 | return bin 58 | } 59 | 60 | exports.strToRaw = strToRaw 61 | function strToRaw (str) { 62 | return unescape(encodeURIComponent(str)) 63 | } 64 | 65 | exports.rawToStr = rawToStr 66 | function rawToStr (raw) { 67 | return decodeURIComponent(escape(raw)) 68 | } 69 | 70 | function getCodes () { 71 | return 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' 72 | } 73 | let map 74 | function getMap () { 75 | if (map) return map 76 | map = [] 77 | let codes = getCodes() 78 | for (let i = 0, l = codes.length; i < l; i++) { 79 | map[codes.charCodeAt(i)] = i 80 | } 81 | return map 82 | } 83 | 84 | // Loop over input 3 bytes at a time 85 | // a,b,c are 3 x 8-bit numbers 86 | // they are encoded into groups of 4 x 6-bit numbers 87 | // aaaaaa aabbbb bbbbcc cccccc 88 | // if there is no c, then pad the 4th with = 89 | // if there is also no b then pad the 3rd with = 90 | exports.binToB64 = binToB64 91 | function binToB64 (bin) { 92 | let b64 = '' 93 | let codes = getCodes() 94 | for (let i = 0, l = bin.length; i < l; i += 3) { 95 | let a = bin[i] 96 | let b = i + 1 < l ? bin[i + 1] : -1 97 | let c = i + 2 < l ? bin[i + 2] : -1 98 | b64 += 99 | // Higher 6 bits of a 100 | codes[a >> 2] + 101 | // Lower 2 bits of a + high 4 bits of b 102 | codes[((a & 3) << 4) | (b >= 0 ? b >> 4 : 0)] + 103 | // Low 4 bits of b + High 2 bits of c 104 | (b >= 0 ? codes[((b & 15) << 2) | (c >= 0 ? c >> 6 : 0)] : '=') + 105 | // Lower 6 bits of c 106 | (c >= 0 ? codes[c & 63] : '=') 107 | } 108 | return b64 109 | } 110 | 111 | // loop over input 4 characters at a time 112 | // The characters are mapped to 4 x 6-bit integers a,b,c,d 113 | // They need to be reassembled into 3 x 8-bit bytes 114 | // aaaaaabb bbbbcccc ccdddddd 115 | // if d is padding then there is no 3rd byte 116 | // if c is padding then there is no 2nd byte 117 | exports.b64ToBin = b64ToBin 118 | function b64ToBin (b64) { 119 | let map = getMap() 120 | let bytes = [] 121 | let j = 0 122 | for (let i = 0, l = b64.length; i < l; i += 4) { 123 | let a = map[b64.charCodeAt(i)] 124 | let b = map[b64.charCodeAt(i + 1)] 125 | let c = map[b64.charCodeAt(i + 2)] 126 | let d = map[b64.charCodeAt(i + 3)] 127 | 128 | // higher 6 bits are the first char 129 | // lower 2 bits are upper 2 bits of second char 130 | bytes[j] = (a << 2) | (b >> 4) 131 | 132 | // if the third char is not padding, we have a second byte 133 | if (c < 64) { 134 | // high 4 bits come from lower 4 bits in b 135 | // low 4 bits come from high 4 bits in c 136 | bytes[j + 1] = ((b & 0xf) << 4) | (c >> 2) 137 | 138 | // if the fourth char is not padding, we have a third byte 139 | if (d < 64) { 140 | // Upper 2 bits come from Lower 2 bits of c 141 | // Lower 6 bits come from d 142 | bytes[j + 2] = ((c & 3) << 6) | d 143 | } 144 | } 145 | j = j + 3 146 | } 147 | return Buffer.alloc(bytes) 148 | } 149 | 150 | exports.strToBin = strToBin 151 | function strToBin (str) { 152 | return rawToBin(strToRaw(str)) 153 | } 154 | 155 | exports.binToStr = binToStr 156 | function binToStr (bin, start, end) { 157 | return rawToStr(binToRaw(bin, start, end)) 158 | } 159 | 160 | exports.rawToHex = rawToHex 161 | function rawToHex (raw, start, end) { 162 | return binToHex(rawToBin(raw, start, end)) 163 | } 164 | 165 | exports.hexToRaw = hexToRaw 166 | function hexToRaw (hex) { 167 | return binToRaw(hexToBin(hex)) 168 | } 169 | 170 | exports.strToHex = strToHex 171 | function strToHex (str) { 172 | return binToHex(strToBin(str)) 173 | } 174 | 175 | exports.hexToStr = hexToStr 176 | function hexToStr (hex) { 177 | return binToStr(hexToBin(hex)) 178 | } 179 | 180 | exports.b64ToStr = b64ToStr 181 | function b64ToStr (b64) { 182 | return binToStr(b64ToBin(b64)) 183 | } 184 | 185 | exports.strToB64 = strToB64 186 | function strToB64 (str) { 187 | return binToB64(strToBin(str)) 188 | } 189 | 190 | exports.b64ToHex = b64ToHex 191 | function b64ToHex (b64) { 192 | return binToHex(b64ToBin(b64)) 193 | } 194 | 195 | exports.hexToB64 = hexToB64 196 | function hexToB64 (hex) { 197 | return binToB64(hexToBin(hex)) 198 | } 199 | 200 | exports.b64ToRaw = b64ToRaw 201 | function b64ToRaw (b64) { 202 | return binToRaw(b64ToBin(b64)) 203 | } 204 | 205 | exports.rawToB64 = rawToB64 206 | function rawToB64 (raw, start, end) { 207 | return binToB64(rawToBin(raw, start, end)) 208 | } 209 | 210 | // This takes nested lists of numbers, strings and array buffers and returns 211 | // a single buffer. Numbers represent single bytes, strings are raw 8-bit 212 | // strings, and buffers represent themselves. 213 | // EX: 214 | // 1 -> <01> 215 | // "Hi" -> <48 69> 216 | // [1, "Hi"] -> <01 48 69> 217 | // [[1],2,[3]] -> <01 02 03> 218 | exports.flatten = flatten 219 | function flatten (parts) { 220 | if (typeof parts === 'number') return Buffer.alloc([parts]) 221 | if (parts instanceof Buffer) return parts 222 | let buffer = Buffer.alloc(count(parts)) 223 | copy(buffer, 0, parts) 224 | return buffer 225 | } 226 | 227 | function count (value) { 228 | if (value == null) return 0 229 | if (typeof value === 'number') return 1 230 | if (typeof value === 'string') return value.length 231 | if (value instanceof Buffer) return value.length 232 | if (!Array.isArray(value)) { 233 | console.log('VALUE', value) 234 | throw new TypeError('Bad type for flatten: ' + typeof value) 235 | } 236 | let sum = 0 237 | for (let piece of value) { 238 | sum += count(piece) 239 | } 240 | return sum 241 | } 242 | 243 | function copy (buffer, offset, value) { 244 | if (value == null) return offset 245 | if (typeof value === 'number') { 246 | buffer[offset++] = value 247 | return offset 248 | } 249 | if (typeof value === 'string') { 250 | for (let i = 0, l = value.length; i < l; i++) { 251 | buffer[offset++] = value.charCodeAt(i) 252 | } 253 | return offset 254 | } 255 | if (value instanceof ArrayBuffer) { 256 | value = Buffer.alloc(value) 257 | } 258 | for (let piece of value) { 259 | offset = copy(buffer, offset, piece) 260 | } 261 | return offset 262 | } 263 | 264 | // indexOf for arrays/buffers. Raw is a string in raw encoding. 265 | // returns -1 when not found. 266 | // start and end are indexes into buffer. Default is 0 and length. 267 | exports.indexOf = indexOf 268 | function indexOf (bin, raw, start, end) { 269 | /* eslint no-labels: 0 */ 270 | start = start == null ? 0 : start | 0 271 | end = end == null ? bin.length : end | 0 272 | outer: for (let i = start || 0; i < end; i++) { 273 | for (let j = 0, l = raw.length; j < l; j++) { 274 | if (i + j >= end || bin[i + j] !== raw.charCodeAt(j)) { 275 | continue outer 276 | } 277 | } 278 | return i 279 | } 280 | return -1 281 | } 282 | 283 | exports.uint8 = uint8 284 | function uint8 (num) { 285 | return (num >>> 0) & 0xff 286 | } 287 | 288 | exports.uint16 = uint16 289 | function uint16 (num) { 290 | num = (num >>> 0) & 0xffff 291 | return [ 292 | num >> 8, 293 | num & 0xff 294 | ] 295 | } 296 | exports.uint32 = uint32 297 | function uint32 (num) { 298 | num >>>= 0 299 | return [ 300 | num >> 24, 301 | (num >> 16) & 0xff, 302 | (num >> 8) & 0xff, 303 | num & 0xff 304 | ] 305 | } 306 | 307 | exports.uint64 = uint64 308 | function uint64 (value) { 309 | if (value < 0) value += 0x10000000000000000 310 | return [ 311 | uint32(value / 0x100000000), 312 | uint32(value % 0x100000000) 313 | ] 314 | } 315 | 316 | // If the first 1 bit of the byte is 0,that character is 1 byte width and this is the byte. 317 | // If the first 2 bit of the byte is 10,that byte is not the first byte of a character 318 | // If the first 3 bit is 110,that character is 2 byte width and this is the first byte 319 | // If the first 4 bit is 1110,that character is 3 byte width and this is the first byte 320 | // If the first 5 bit is 11110,that character is 4 byte width and this is the first byte 321 | // If the first 6 bit is 111110,that character is 5 byte width and this is the first byte 322 | exports.isUTF8 = isUTF8 323 | function isUTF8 (bin) { 324 | let i = 0 325 | let l = bin.length 326 | while (i < l) { 327 | if (bin[i] < 0x80) i++ 328 | else if (bin[i] < 0xc0) return false 329 | else if (bin[i] < 0xe0) i += 2 330 | else if (bin[i] < 0xf0) i += 3 331 | else if (bin[i] < 0xf8) i += 4 332 | else if (bin[i] < 0xfc) i += 5 333 | } 334 | return i === l 335 | } 336 | 337 | exports.parseOct = parseOct 338 | function parseOct (bin, start, end) { 339 | let val = 0 340 | let sign = 1 341 | if (bin[start] === 0x2d) { 342 | start++ 343 | sign = -1 344 | } 345 | while (start < end) { 346 | val = (val << 3) + bin[start++] - 0x30 347 | } 348 | return sign * val 349 | } 350 | 351 | exports.parseDec = parseDec 352 | function parseDec (bin, start, end) { 353 | let val = 0 354 | let sign = 1 355 | if (bin[start] === 0x2d) { 356 | start++ 357 | sign = -1 358 | } 359 | while (start < end) { 360 | val = val * 10 + bin[start++] - 0x30 361 | } 362 | return sign * val 363 | } 364 | -------------------------------------------------------------------------------- /libs/codec-http.js: -------------------------------------------------------------------------------- 1 | const { indexOf, binToRaw } = require('./bintools') 2 | const { assert } = require('./assert') 3 | const { makeEncoder, makeDecoder } = require('./codec-tools') 4 | 5 | module.exports = { encoder: httpEncoder, decoder: httpDecoder } 6 | 7 | const STATUS_CODES = { 8 | '100': 'Continue', 9 | '101': 'Switching Protocols', 10 | '102': 'Processing', // RFC 2518, obsoleted by RFC 4918 11 | '200': 'OK', 12 | '201': 'Created', 13 | '202': 'Accepted', 14 | '203': 'Non-Authoritative Information', 15 | '204': 'No Content', 16 | '205': 'Reset Content', 17 | '206': 'Partial Content', 18 | '207': 'Multi-Status', // RFC 4918 19 | '300': 'Multiple Choices', 20 | '301': 'Moved Permanently', 21 | '302': 'Moved Temporarily', 22 | '303': 'See Other', 23 | '304': 'Not Modified', 24 | '305': 'Use Proxy', 25 | '307': 'Temporary Redirect', 26 | '400': 'Bad Request', 27 | '401': 'Unauthorized', 28 | '402': 'Payment Required', 29 | '403': 'Forbidden', 30 | '404': 'Not Found', 31 | '405': 'Method Not Allowed', 32 | '406': 'Not Acceptable', 33 | '407': 'Proxy Authentication Required', 34 | '408': 'Request Time-out', 35 | '409': 'Conflict', 36 | '410': 'Gone', 37 | '411': 'Length Required', 38 | '412': 'Precondition Failed', 39 | '413': 'Request Entity Too Large', 40 | '414': 'Request-URI Too Large', 41 | '415': 'Unsupported Media Type', 42 | '416': 'Requested Range Not Satisfiable', 43 | '417': 'Expectation Failed', 44 | '418': "I'm a teapot", // RFC 2324 45 | '422': 'Unprocessable Entity', // RFC 4918 46 | '423': 'Locked', // RFC 4918 47 | '424': 'Failed Dependency', // RFC 4918 48 | '425': 'Unordered Collection', // RFC 4918 49 | '426': 'Upgrade Required', // RFC 2817 50 | '500': 'Internal Server Error', 51 | '501': 'Not Implemented', 52 | '502': 'Bad Gateway', 53 | '503': 'Service Unavailable', 54 | '504': 'Gateway Time-out', 55 | '505': 'HTTP Version not supported', 56 | '506': 'Variant Also Negotiates', // RFC 2295 57 | '507': 'Insufficient Storage', // RFC 4918 58 | '509': 'Bandwidth Limit Exceeded', 59 | '510': 'Not Extended' // RFC 2774 60 | } 61 | 62 | function httpEncoder (write) { 63 | const newWrite = makeEncoder(encodeHead)(write) 64 | return newWrite 65 | 66 | function encodeHead (item) { 67 | if (!item || item.constructor !== Object) { 68 | return item 69 | } else if (typeof item !== 'object') { 70 | throw new Error( 71 | 'expected an object but got a ' + (typeof item) + ' when encoding data' 72 | ) 73 | } 74 | let head, chunkedEncoding 75 | const version = item.version || 1.1 76 | if (item.method) { 77 | const path = item.path 78 | assert(path && path.length > 0, 'expected non-empty path') 79 | head = [ item.method + ' ' + item.path + ' HTTP/' + version + '\r\n' ] 80 | } else { 81 | const reason = item.reason || STATUS_CODES[item.code] 82 | head = [ 'HTTP/' + version + ' ' + item.code + ' ' + reason + '\r\n' ] 83 | } 84 | const headers = item.headers 85 | if (Array.isArray(headers)) { 86 | for (let i = 0, l = headers.length; i < l; i += 2) { 87 | processHeader(headers[i], headers[i + 1]) 88 | } 89 | } else { 90 | for (const key in headers) { 91 | processHeader(key, headers[key]) 92 | } 93 | } 94 | function processHeader (key, value) { 95 | const lowerKey = key.toLowerCase() 96 | if (lowerKey === 'transfer-encoding') { 97 | chunkedEncoding = value.toLowerCase() === 'chunked' 98 | } 99 | value = ('' + value).replace(/[\r\n]+/, ' ') 100 | head[head.length] = key + ': ' + value + '\r\n' 101 | } 102 | 103 | head[head.length] = '\r\n' 104 | 105 | newWrite.encode = chunkedEncoding ? encodeChunked : encodeRaw 106 | return head.join('') 107 | } 108 | 109 | function encodeRaw (item) { 110 | if (typeof item !== 'string') { 111 | newWrite.encode = encodeHead 112 | return encodeHead(item) 113 | } 114 | return item 115 | } 116 | 117 | function encodeChunked (item) { 118 | if (typeof item !== 'string') { 119 | newWrite.encode = encodeHead 120 | const extra = encodeHead(item) 121 | if (extra) { 122 | return '0\r\n\r\n' + extra 123 | } else { 124 | return '0\r\n\r\n' 125 | } 126 | } 127 | if (item.length === 0) { 128 | newWrite.encode = encodeHead 129 | } 130 | return item.length.toString(16) + '\r\n' + item + '\r\n' 131 | } 132 | } 133 | 134 | function httpDecoder (read) { 135 | let bytesLeft // For counted decoder 136 | const newRead = makeDecoder(decodeHead)(read) 137 | 138 | newRead[Symbol.asyncIterator] = () => ({ 139 | next: () => new Promise((resolve, reject) => 140 | newRead().then( 141 | value => resolve(isEmpty(value) ? { done: true } : { value }), 142 | err => err.code === 'EOF' ? resolve({ done: true }) : reject(err) 143 | ) 144 | ) 145 | }) 146 | 147 | return newRead 148 | 149 | // This state is for decoding the status line and headers. 150 | function decodeHead (chunk, offset) { 151 | if (!chunk || chunk.length <= offset) return 152 | 153 | // First make sure we have all the head before continuing 154 | let index = indexOf(chunk, '\r\n\r\n', offset) 155 | if (index < 0) { 156 | if ((chunk.length - offset) < 8 * 1024) return 157 | // But protect against evil clients by refusing heads over 8K long. 158 | throw new Error('entity too large') 159 | } 160 | // Remember where the head ended and the body started 161 | let bodyStart = index + 4 162 | 163 | // Parse the status/request line 164 | let head = {} 165 | 166 | index = indexOf(chunk, '\n', offset) + 1 167 | let line = binToRaw(chunk, offset, index) 168 | let match = line.match(/^HTTP\/(\d\.\d) (\d+) ([^\r\n]+)/) 169 | let version 170 | if (match) { 171 | version = match[1] 172 | head.code = parseInt(match[2], 10) 173 | head.reason = match[3] 174 | } else { 175 | match = line.match(/^([A-Z]+) ([^ ]+) HTTP\/(\d\.\d)/) 176 | if (match) { 177 | head.method = match[1] 178 | head.path = match[2] 179 | version = match[3] 180 | } else { 181 | throw new Error('expected HTTP data') 182 | } 183 | } 184 | head.version = parseFloat(version) 185 | head.keepAlive = head.version > 1.0 186 | 187 | // We need to inspect some headers to know how to parse the body. 188 | let contentLength 189 | let chunkedEncoding 190 | 191 | const headers = head.headers = [] 192 | // Parse the header lines 193 | let start = index 194 | while ((index = indexOf(chunk, '\n', index) + 1)) { 195 | line = binToRaw(chunk, start, index) 196 | if (line === '\r\n') break 197 | start = index 198 | const match = line.match(/^([^:\r\n]+): *([^\r\n]+)/) 199 | if (!match) { 200 | throw new Error('Malformed HTTP header: ' + line) 201 | } 202 | const key = match[1] 203 | const value = match[2] 204 | const lowerKey = key.toLowerCase() 205 | 206 | // Inspect a few headers and remember the values 207 | if (lowerKey === 'content-length') { 208 | contentLength = parseInt(value) 209 | } else if (lowerKey === 'transfer-encoding') { 210 | chunkedEncoding = value.toLowerCase() === 'chunked' 211 | } else if (lowerKey === 'connection') { 212 | head.keepAlive = value.toLowerCase() === 'keep-alive' 213 | } 214 | headers.push(key, value) 215 | } 216 | 217 | if (head.keepAlive 218 | ? !(chunkedEncoding || (contentLength !== undefined && contentLength > 0)) 219 | : (head.method === 'GET' || head.method === 'HEAD') 220 | ) { 221 | newRead.decode = decodeEmpty 222 | } else if (chunkedEncoding) { 223 | newRead.decode = decodeChunked 224 | } else if (contentLength !== undefined) { 225 | bytesLeft = contentLength 226 | newRead.decode = decodeCounted 227 | } else if (!head.keepAlive) { 228 | newRead.decode = decodeRaw 229 | } 230 | return [head, bodyStart] 231 | } 232 | 233 | // This is used for inserting a single empty string into the output string for known empty bodies 234 | function decodeEmpty (chunk, offset) { 235 | newRead.decode = decodeHead 236 | return [Buffer.alloc(0), offset] 237 | } 238 | 239 | function decodeRaw (chunk, offset) { 240 | if (!chunk || chunk.length >= offset) return [Buffer.alloc(0)] 241 | if (chunk.length === 0) return 242 | return [chunk.slice(offset), chunk.length] 243 | } 244 | 245 | function decodeChunked (chunk, offset) { 246 | // Make sure we have at least the length header 247 | const index = indexOf(chunk, '\r\n', offset) 248 | if (index < 0) return 249 | 250 | // And parse it 251 | const hex = binToRaw(chunk, offset, index) 252 | const length = parseInt(hex, 16) 253 | 254 | // Wait till we have the rest of the body 255 | const start = hex.length + 2 256 | const end = start + length 257 | if ((chunk.length - offset) < end + 2) return 258 | 259 | // An empty chunk means end of stream; reset state. 260 | if (length === 0) newRead.decode = decodeHead 261 | 262 | // Make sure the chunk ends in '\r\n' 263 | assert(binToRaw(chunk, end, end + 2) === '\r\n', 'Invalid chunk tail') 264 | 265 | return [chunk.slice(start, end), end + 2] 266 | } 267 | 268 | function decodeCounted (chunk, offset) { 269 | if (bytesLeft === 0) { 270 | return decodeEmpty(chunk, offset) 271 | } 272 | const length = chunk.length - offset 273 | // Make sure we have at least one byte to process 274 | if (length <= 0) return 275 | 276 | if (length >= bytesLeft) { 277 | newRead.decode = decodeEmpty 278 | } 279 | 280 | // If the entire chunk fits, pass it all through 281 | if (length <= bytesLeft) { 282 | bytesLeft -= length 283 | return [chunk.slice(offset), chunk.length] 284 | } 285 | 286 | return [chunk.slice(offset, bytesLeft), offset + bytesLeft + 1] 287 | } 288 | } 289 | 290 | function isEmpty (value) { 291 | return (value instanceof Buffer) && !value.length 292 | } 293 | -------------------------------------------------------------------------------- /libs/codec-tools.js: -------------------------------------------------------------------------------- 1 | // read / write - stream prmitives 2 | // await read() -> value 3 | // await read() -> undefined 4 | // await write(value) 5 | // await write() 6 | 7 | // encode / decode - low-level codecs 8 | // decode(input, offset) -> [value, offset] 9 | // decode(input, offset) -> undefined 10 | // encode(value) -> encoded 11 | // encode() -> encoded? 12 | 13 | // encoder / decode - higher-level codecs 14 | // encoder(write) -> newWrite (with updateEncode and updateWrite) 15 | // decoder(read) -> newRead (with updateDecode and updateRead) 16 | 17 | // makeEncoder(encode) -> encoder (with updateEncode) 18 | exports.makeEncoder = encode => { 19 | const encoder = inner => { 20 | const write = async value => write.inner(write.encode(value)) 21 | write.encode = encoder.encode 22 | write.inner = inner 23 | return write 24 | } 25 | encoder.encode = encode 26 | return encoder 27 | } 28 | 29 | // makeDecoder(decode) -> decoder (with updateDecode) 30 | exports.makeDecoder = decode => { 31 | const decoder = inner => { 32 | let buffer = null 33 | let offset = null 34 | let done = false 35 | const read = async () => { 36 | while (true) { 37 | let result = read.decode(buffer, offset) 38 | if (result) { 39 | offset = result[1] 40 | if (buffer && offset >= buffer.length) { 41 | buffer = null 42 | offset = null 43 | } 44 | return result[0] 45 | } 46 | if (done) return 47 | const next = await read.inner() 48 | if (!next) { 49 | done = true 50 | continue 51 | } 52 | if (!buffer) { 53 | buffer = next 54 | offset = 0 55 | continue 56 | } 57 | const bytesLeft = buffer.length - offset 58 | const last = buffer 59 | buffer = Buffer.alloc(bytesLeft + next.length) 60 | last.copy(buffer, 0, offset) 61 | next.copy(buffer, bytesLeft) 62 | offset = 0 63 | } 64 | } 65 | read.decode = decoder.decode 66 | read.inner = inner 67 | return read 68 | } 69 | decoder.decode = decode 70 | return decoder 71 | } 72 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuv", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "acorn": { 8 | "version": "5.5.3", 9 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", 10 | "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", 11 | "dev": true 12 | }, 13 | "acorn-jsx": { 14 | "version": "3.0.1", 15 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", 16 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", 17 | "dev": true, 18 | "requires": { 19 | "acorn": "^3.0.4" 20 | }, 21 | "dependencies": { 22 | "acorn": { 23 | "version": "3.3.0", 24 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 25 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", 26 | "dev": true 27 | } 28 | } 29 | }, 30 | "ajv": { 31 | "version": "5.5.2", 32 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 33 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 34 | "dev": true, 35 | "requires": { 36 | "co": "^4.6.0", 37 | "fast-deep-equal": "^1.0.0", 38 | "fast-json-stable-stringify": "^2.0.0", 39 | "json-schema-traverse": "^0.3.0" 40 | } 41 | }, 42 | "ajv-keywords": { 43 | "version": "2.1.1", 44 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", 45 | "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", 46 | "dev": true 47 | }, 48 | "ansi-escapes": { 49 | "version": "3.1.0", 50 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", 51 | "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", 52 | "dev": true 53 | }, 54 | "ansi-regex": { 55 | "version": "2.1.1", 56 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 57 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 58 | "dev": true 59 | }, 60 | "ansi-styles": { 61 | "version": "2.2.1", 62 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 63 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 64 | "dev": true 65 | }, 66 | "argparse": { 67 | "version": "1.0.10", 68 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 69 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 70 | "dev": true, 71 | "requires": { 72 | "sprintf-js": "~1.0.2" 73 | } 74 | }, 75 | "array-union": { 76 | "version": "1.0.2", 77 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 78 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 79 | "dev": true, 80 | "requires": { 81 | "array-uniq": "^1.0.1" 82 | } 83 | }, 84 | "array-uniq": { 85 | "version": "1.0.3", 86 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 87 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 88 | "dev": true 89 | }, 90 | "arrify": { 91 | "version": "1.0.1", 92 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 93 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 94 | "dev": true 95 | }, 96 | "babel-code-frame": { 97 | "version": "6.26.0", 98 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 99 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 100 | "dev": true, 101 | "requires": { 102 | "chalk": "^1.1.3", 103 | "esutils": "^2.0.2", 104 | "js-tokens": "^3.0.2" 105 | }, 106 | "dependencies": { 107 | "chalk": { 108 | "version": "1.1.3", 109 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 110 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 111 | "dev": true, 112 | "requires": { 113 | "ansi-styles": "^2.2.1", 114 | "escape-string-regexp": "^1.0.2", 115 | "has-ansi": "^2.0.0", 116 | "strip-ansi": "^3.0.0", 117 | "supports-color": "^2.0.0" 118 | } 119 | }, 120 | "strip-ansi": { 121 | "version": "3.0.1", 122 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 123 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 124 | "dev": true, 125 | "requires": { 126 | "ansi-regex": "^2.0.0" 127 | } 128 | } 129 | } 130 | }, 131 | "balanced-match": { 132 | "version": "1.0.0", 133 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 134 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 135 | "dev": true 136 | }, 137 | "bindings": { 138 | "version": "1.3.0", 139 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", 140 | "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" 141 | }, 142 | "brace-expansion": { 143 | "version": "1.1.11", 144 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 145 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 146 | "dev": true, 147 | "requires": { 148 | "balanced-match": "^1.0.0", 149 | "concat-map": "0.0.1" 150 | } 151 | }, 152 | "buffer-from": { 153 | "version": "1.0.0", 154 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", 155 | "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", 156 | "dev": true 157 | }, 158 | "builtin-modules": { 159 | "version": "1.1.1", 160 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 161 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 162 | "dev": true 163 | }, 164 | "caller-path": { 165 | "version": "0.1.0", 166 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 167 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 168 | "dev": true, 169 | "requires": { 170 | "callsites": "^0.2.0" 171 | } 172 | }, 173 | "callsites": { 174 | "version": "0.2.0", 175 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 176 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", 177 | "dev": true 178 | }, 179 | "chalk": { 180 | "version": "2.4.1", 181 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 182 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 183 | "dev": true, 184 | "requires": { 185 | "ansi-styles": "^3.2.1", 186 | "escape-string-regexp": "^1.0.5", 187 | "supports-color": "^5.3.0" 188 | }, 189 | "dependencies": { 190 | "ansi-styles": { 191 | "version": "3.2.1", 192 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 193 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 194 | "dev": true, 195 | "requires": { 196 | "color-convert": "^1.9.0" 197 | } 198 | }, 199 | "supports-color": { 200 | "version": "5.4.0", 201 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 202 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 203 | "dev": true, 204 | "requires": { 205 | "has-flag": "^3.0.0" 206 | } 207 | } 208 | } 209 | }, 210 | "chardet": { 211 | "version": "0.4.2", 212 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", 213 | "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", 214 | "dev": true 215 | }, 216 | "circular-json": { 217 | "version": "0.3.3", 218 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", 219 | "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", 220 | "dev": true 221 | }, 222 | "cli-cursor": { 223 | "version": "2.1.0", 224 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", 225 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", 226 | "dev": true, 227 | "requires": { 228 | "restore-cursor": "^2.0.0" 229 | } 230 | }, 231 | "cli-width": { 232 | "version": "2.2.0", 233 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 234 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", 235 | "dev": true 236 | }, 237 | "co": { 238 | "version": "4.6.0", 239 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 240 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 241 | "dev": true 242 | }, 243 | "color-convert": { 244 | "version": "1.9.1", 245 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 246 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 247 | "dev": true, 248 | "requires": { 249 | "color-name": "^1.1.1" 250 | } 251 | }, 252 | "color-name": { 253 | "version": "1.1.3", 254 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 255 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 256 | "dev": true 257 | }, 258 | "concat-map": { 259 | "version": "0.0.1", 260 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 261 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 262 | "dev": true 263 | }, 264 | "concat-stream": { 265 | "version": "1.6.2", 266 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 267 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 268 | "dev": true, 269 | "requires": { 270 | "buffer-from": "^1.0.0", 271 | "inherits": "^2.0.3", 272 | "readable-stream": "^2.2.2", 273 | "typedarray": "^0.0.6" 274 | } 275 | }, 276 | "contains-path": { 277 | "version": "0.1.0", 278 | "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", 279 | "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", 280 | "dev": true 281 | }, 282 | "core-util-is": { 283 | "version": "1.0.2", 284 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 285 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 286 | "dev": true 287 | }, 288 | "cross-spawn": { 289 | "version": "5.1.0", 290 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 291 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 292 | "dev": true, 293 | "requires": { 294 | "lru-cache": "^4.0.1", 295 | "shebang-command": "^1.2.0", 296 | "which": "^1.2.9" 297 | } 298 | }, 299 | "debug": { 300 | "version": "3.1.0", 301 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 302 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 303 | "dev": true, 304 | "requires": { 305 | "ms": "2.0.0" 306 | } 307 | }, 308 | "deep-is": { 309 | "version": "0.1.3", 310 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 311 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 312 | "dev": true 313 | }, 314 | "del": { 315 | "version": "2.2.2", 316 | "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", 317 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", 318 | "dev": true, 319 | "requires": { 320 | "globby": "^5.0.0", 321 | "is-path-cwd": "^1.0.0", 322 | "is-path-in-cwd": "^1.0.0", 323 | "object-assign": "^4.0.1", 324 | "pify": "^2.0.0", 325 | "pinkie-promise": "^2.0.0", 326 | "rimraf": "^2.2.8" 327 | } 328 | }, 329 | "doctrine": { 330 | "version": "2.1.0", 331 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 332 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 333 | "dev": true, 334 | "requires": { 335 | "esutils": "^2.0.2" 336 | } 337 | }, 338 | "error-ex": { 339 | "version": "1.3.1", 340 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", 341 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", 342 | "dev": true, 343 | "requires": { 344 | "is-arrayish": "^0.2.1" 345 | } 346 | }, 347 | "escape-string-regexp": { 348 | "version": "1.0.5", 349 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 350 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 351 | "dev": true 352 | }, 353 | "eslint": { 354 | "version": "4.19.1", 355 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", 356 | "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", 357 | "dev": true, 358 | "requires": { 359 | "ajv": "^5.3.0", 360 | "babel-code-frame": "^6.22.0", 361 | "chalk": "^2.1.0", 362 | "concat-stream": "^1.6.0", 363 | "cross-spawn": "^5.1.0", 364 | "debug": "^3.1.0", 365 | "doctrine": "^2.1.0", 366 | "eslint-scope": "^3.7.1", 367 | "eslint-visitor-keys": "^1.0.0", 368 | "espree": "^3.5.4", 369 | "esquery": "^1.0.0", 370 | "esutils": "^2.0.2", 371 | "file-entry-cache": "^2.0.0", 372 | "functional-red-black-tree": "^1.0.1", 373 | "glob": "^7.1.2", 374 | "globals": "^11.0.1", 375 | "ignore": "^3.3.3", 376 | "imurmurhash": "^0.1.4", 377 | "inquirer": "^3.0.6", 378 | "is-resolvable": "^1.0.0", 379 | "js-yaml": "^3.9.1", 380 | "json-stable-stringify-without-jsonify": "^1.0.1", 381 | "levn": "^0.3.0", 382 | "lodash": "^4.17.4", 383 | "minimatch": "^3.0.2", 384 | "mkdirp": "^0.5.1", 385 | "natural-compare": "^1.4.0", 386 | "optionator": "^0.8.2", 387 | "path-is-inside": "^1.0.2", 388 | "pluralize": "^7.0.0", 389 | "progress": "^2.0.0", 390 | "regexpp": "^1.0.1", 391 | "require-uncached": "^1.0.3", 392 | "semver": "^5.3.0", 393 | "strip-ansi": "^4.0.0", 394 | "strip-json-comments": "~2.0.1", 395 | "table": "4.0.2", 396 | "text-table": "~0.2.0" 397 | } 398 | }, 399 | "eslint-import-resolver-node": { 400 | "version": "0.3.2", 401 | "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", 402 | "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", 403 | "dev": true, 404 | "requires": { 405 | "debug": "^2.6.9", 406 | "resolve": "^1.5.0" 407 | }, 408 | "dependencies": { 409 | "debug": { 410 | "version": "2.6.9", 411 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 412 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 413 | "dev": true, 414 | "requires": { 415 | "ms": "2.0.0" 416 | } 417 | } 418 | } 419 | }, 420 | "eslint-module-utils": { 421 | "version": "2.2.0", 422 | "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz", 423 | "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=", 424 | "dev": true, 425 | "requires": { 426 | "debug": "^2.6.8", 427 | "pkg-dir": "^1.0.0" 428 | }, 429 | "dependencies": { 430 | "debug": { 431 | "version": "2.6.9", 432 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 433 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 434 | "dev": true, 435 | "requires": { 436 | "ms": "2.0.0" 437 | } 438 | } 439 | } 440 | }, 441 | "eslint-plugin-import": { 442 | "version": "2.11.0", 443 | "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.11.0.tgz", 444 | "integrity": "sha1-Fa7qN6Z0mdhI6OmBgG1GJ7VQOBY=", 445 | "dev": true, 446 | "requires": { 447 | "contains-path": "^0.1.0", 448 | "debug": "^2.6.8", 449 | "doctrine": "1.5.0", 450 | "eslint-import-resolver-node": "^0.3.1", 451 | "eslint-module-utils": "^2.2.0", 452 | "has": "^1.0.1", 453 | "lodash": "^4.17.4", 454 | "minimatch": "^3.0.3", 455 | "read-pkg-up": "^2.0.0", 456 | "resolve": "^1.6.0" 457 | }, 458 | "dependencies": { 459 | "debug": { 460 | "version": "2.6.9", 461 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 462 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 463 | "dev": true, 464 | "requires": { 465 | "ms": "2.0.0" 466 | } 467 | }, 468 | "doctrine": { 469 | "version": "1.5.0", 470 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", 471 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", 472 | "dev": true, 473 | "requires": { 474 | "esutils": "^2.0.2", 475 | "isarray": "^1.0.0" 476 | } 477 | } 478 | } 479 | }, 480 | "eslint-plugin-node": { 481 | "version": "6.0.1", 482 | "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz", 483 | "integrity": "sha512-Q/Cc2sW1OAISDS+Ji6lZS2KV4b7ueA/WydVWd1BECTQwVvfQy5JAi3glhINoKzoMnfnuRgNP+ZWKrGAbp3QDxw==", 484 | "dev": true, 485 | "requires": { 486 | "ignore": "^3.3.6", 487 | "minimatch": "^3.0.4", 488 | "resolve": "^1.3.3", 489 | "semver": "^5.4.1" 490 | } 491 | }, 492 | "eslint-plugin-promise": { 493 | "version": "3.7.0", 494 | "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz", 495 | "integrity": "sha512-2WO+ZFh7vxUKRfR0cOIMrWgYKdR6S1AlOezw6pC52B6oYpd5WFghN+QHxvrRdZMtbo8h3dfUZ2o1rWb0UPbKtg==", 496 | "dev": true 497 | }, 498 | "eslint-plugin-standard": { 499 | "version": "3.1.0", 500 | "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", 501 | "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", 502 | "dev": true 503 | }, 504 | "eslint-scope": { 505 | "version": "3.7.1", 506 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", 507 | "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", 508 | "dev": true, 509 | "requires": { 510 | "esrecurse": "^4.1.0", 511 | "estraverse": "^4.1.1" 512 | } 513 | }, 514 | "eslint-visitor-keys": { 515 | "version": "1.0.0", 516 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", 517 | "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", 518 | "dev": true 519 | }, 520 | "espree": { 521 | "version": "3.5.4", 522 | "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", 523 | "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", 524 | "dev": true, 525 | "requires": { 526 | "acorn": "^5.5.0", 527 | "acorn-jsx": "^3.0.0" 528 | } 529 | }, 530 | "esprima": { 531 | "version": "4.0.0", 532 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", 533 | "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", 534 | "dev": true 535 | }, 536 | "esquery": { 537 | "version": "1.0.1", 538 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", 539 | "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", 540 | "dev": true, 541 | "requires": { 542 | "estraverse": "^4.0.0" 543 | } 544 | }, 545 | "esrecurse": { 546 | "version": "4.2.1", 547 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 548 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 549 | "dev": true, 550 | "requires": { 551 | "estraverse": "^4.1.0" 552 | } 553 | }, 554 | "estraverse": { 555 | "version": "4.2.0", 556 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 557 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 558 | "dev": true 559 | }, 560 | "esutils": { 561 | "version": "2.0.2", 562 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 563 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 564 | "dev": true 565 | }, 566 | "external-editor": { 567 | "version": "2.2.0", 568 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", 569 | "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", 570 | "dev": true, 571 | "requires": { 572 | "chardet": "^0.4.0", 573 | "iconv-lite": "^0.4.17", 574 | "tmp": "^0.0.33" 575 | } 576 | }, 577 | "fast-deep-equal": { 578 | "version": "1.1.0", 579 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 580 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", 581 | "dev": true 582 | }, 583 | "fast-json-stable-stringify": { 584 | "version": "2.0.0", 585 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 586 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 587 | "dev": true 588 | }, 589 | "fast-levenshtein": { 590 | "version": "2.0.6", 591 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 592 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 593 | "dev": true 594 | }, 595 | "figures": { 596 | "version": "2.0.0", 597 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", 598 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", 599 | "dev": true, 600 | "requires": { 601 | "escape-string-regexp": "^1.0.5" 602 | } 603 | }, 604 | "file-entry-cache": { 605 | "version": "2.0.0", 606 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", 607 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", 608 | "dev": true, 609 | "requires": { 610 | "flat-cache": "^1.2.1", 611 | "object-assign": "^4.0.1" 612 | } 613 | }, 614 | "find-up": { 615 | "version": "1.1.2", 616 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 617 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 618 | "dev": true, 619 | "requires": { 620 | "path-exists": "^2.0.0", 621 | "pinkie-promise": "^2.0.0" 622 | } 623 | }, 624 | "flat-cache": { 625 | "version": "1.3.0", 626 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", 627 | "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", 628 | "dev": true, 629 | "requires": { 630 | "circular-json": "^0.3.1", 631 | "del": "^2.0.2", 632 | "graceful-fs": "^4.1.2", 633 | "write": "^0.2.1" 634 | } 635 | }, 636 | "fs.realpath": { 637 | "version": "1.0.0", 638 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 639 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 640 | "dev": true 641 | }, 642 | "function-bind": { 643 | "version": "1.1.1", 644 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 645 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 646 | "dev": true 647 | }, 648 | "functional-red-black-tree": { 649 | "version": "1.0.1", 650 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 651 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 652 | "dev": true 653 | }, 654 | "glob": { 655 | "version": "7.1.2", 656 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 657 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 658 | "dev": true, 659 | "requires": { 660 | "fs.realpath": "^1.0.0", 661 | "inflight": "^1.0.4", 662 | "inherits": "2", 663 | "minimatch": "^3.0.4", 664 | "once": "^1.3.0", 665 | "path-is-absolute": "^1.0.0" 666 | } 667 | }, 668 | "globals": { 669 | "version": "11.5.0", 670 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz", 671 | "integrity": "sha512-hYyf+kI8dm3nORsiiXUQigOU62hDLfJ9G01uyGMxhc6BKsircrUhC4uJPQPUSuq2GrTmiiEt7ewxlMdBewfmKQ==", 672 | "dev": true 673 | }, 674 | "globby": { 675 | "version": "5.0.0", 676 | "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", 677 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", 678 | "dev": true, 679 | "requires": { 680 | "array-union": "^1.0.1", 681 | "arrify": "^1.0.0", 682 | "glob": "^7.0.3", 683 | "object-assign": "^4.0.1", 684 | "pify": "^2.0.0", 685 | "pinkie-promise": "^2.0.0" 686 | } 687 | }, 688 | "graceful-fs": { 689 | "version": "4.1.11", 690 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 691 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 692 | "dev": true 693 | }, 694 | "has": { 695 | "version": "1.0.1", 696 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", 697 | "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", 698 | "dev": true, 699 | "requires": { 700 | "function-bind": "^1.0.2" 701 | } 702 | }, 703 | "has-ansi": { 704 | "version": "2.0.0", 705 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 706 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 707 | "dev": true, 708 | "requires": { 709 | "ansi-regex": "^2.0.0" 710 | } 711 | }, 712 | "has-flag": { 713 | "version": "3.0.0", 714 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 715 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 716 | "dev": true 717 | }, 718 | "hosted-git-info": { 719 | "version": "2.6.0", 720 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", 721 | "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", 722 | "dev": true 723 | }, 724 | "iconv-lite": { 725 | "version": "0.4.23", 726 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 727 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 728 | "dev": true, 729 | "requires": { 730 | "safer-buffer": ">= 2.1.2 < 3" 731 | } 732 | }, 733 | "ignore": { 734 | "version": "3.3.8", 735 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", 736 | "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==", 737 | "dev": true 738 | }, 739 | "imurmurhash": { 740 | "version": "0.1.4", 741 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 742 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 743 | "dev": true 744 | }, 745 | "inflight": { 746 | "version": "1.0.6", 747 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 748 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 749 | "dev": true, 750 | "requires": { 751 | "once": "^1.3.0", 752 | "wrappy": "1" 753 | } 754 | }, 755 | "inherits": { 756 | "version": "2.0.3", 757 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 758 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 759 | "dev": true 760 | }, 761 | "inquirer": { 762 | "version": "3.3.0", 763 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", 764 | "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", 765 | "dev": true, 766 | "requires": { 767 | "ansi-escapes": "^3.0.0", 768 | "chalk": "^2.0.0", 769 | "cli-cursor": "^2.1.0", 770 | "cli-width": "^2.0.0", 771 | "external-editor": "^2.0.4", 772 | "figures": "^2.0.0", 773 | "lodash": "^4.3.0", 774 | "mute-stream": "0.0.7", 775 | "run-async": "^2.2.0", 776 | "rx-lite": "^4.0.8", 777 | "rx-lite-aggregates": "^4.0.8", 778 | "string-width": "^2.1.0", 779 | "strip-ansi": "^4.0.0", 780 | "through": "^2.3.6" 781 | } 782 | }, 783 | "is-arrayish": { 784 | "version": "0.2.1", 785 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 786 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 787 | "dev": true 788 | }, 789 | "is-builtin-module": { 790 | "version": "1.0.0", 791 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 792 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 793 | "dev": true, 794 | "requires": { 795 | "builtin-modules": "^1.0.0" 796 | } 797 | }, 798 | "is-fullwidth-code-point": { 799 | "version": "2.0.0", 800 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 801 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 802 | "dev": true 803 | }, 804 | "is-path-cwd": { 805 | "version": "1.0.0", 806 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", 807 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", 808 | "dev": true 809 | }, 810 | "is-path-in-cwd": { 811 | "version": "1.0.1", 812 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", 813 | "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", 814 | "dev": true, 815 | "requires": { 816 | "is-path-inside": "^1.0.0" 817 | } 818 | }, 819 | "is-path-inside": { 820 | "version": "1.0.1", 821 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 822 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 823 | "dev": true, 824 | "requires": { 825 | "path-is-inside": "^1.0.1" 826 | } 827 | }, 828 | "is-promise": { 829 | "version": "2.1.0", 830 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 831 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", 832 | "dev": true 833 | }, 834 | "is-resolvable": { 835 | "version": "1.1.0", 836 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", 837 | "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", 838 | "dev": true 839 | }, 840 | "isarray": { 841 | "version": "1.0.0", 842 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 843 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 844 | "dev": true 845 | }, 846 | "isexe": { 847 | "version": "2.0.0", 848 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 849 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 850 | "dev": true 851 | }, 852 | "js-tokens": { 853 | "version": "3.0.2", 854 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 855 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 856 | "dev": true 857 | }, 858 | "js-yaml": { 859 | "version": "3.11.0", 860 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", 861 | "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", 862 | "dev": true, 863 | "requires": { 864 | "argparse": "^1.0.7", 865 | "esprima": "^4.0.0" 866 | } 867 | }, 868 | "json-schema-traverse": { 869 | "version": "0.3.1", 870 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 871 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", 872 | "dev": true 873 | }, 874 | "json-stable-stringify-without-jsonify": { 875 | "version": "1.0.1", 876 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 877 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 878 | "dev": true 879 | }, 880 | "levn": { 881 | "version": "0.3.0", 882 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 883 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 884 | "dev": true, 885 | "requires": { 886 | "prelude-ls": "~1.1.2", 887 | "type-check": "~0.3.2" 888 | } 889 | }, 890 | "load-json-file": { 891 | "version": "2.0.0", 892 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", 893 | "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", 894 | "dev": true, 895 | "requires": { 896 | "graceful-fs": "^4.1.2", 897 | "parse-json": "^2.2.0", 898 | "pify": "^2.0.0", 899 | "strip-bom": "^3.0.0" 900 | } 901 | }, 902 | "locate-path": { 903 | "version": "2.0.0", 904 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", 905 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", 906 | "dev": true, 907 | "requires": { 908 | "p-locate": "^2.0.0", 909 | "path-exists": "^3.0.0" 910 | }, 911 | "dependencies": { 912 | "path-exists": { 913 | "version": "3.0.0", 914 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 915 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 916 | "dev": true 917 | } 918 | } 919 | }, 920 | "lodash": { 921 | "version": "4.17.10", 922 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", 923 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", 924 | "dev": true 925 | }, 926 | "lru-cache": { 927 | "version": "4.1.3", 928 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", 929 | "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", 930 | "dev": true, 931 | "requires": { 932 | "pseudomap": "^1.0.2", 933 | "yallist": "^2.1.2" 934 | } 935 | }, 936 | "mimic-fn": { 937 | "version": "1.2.0", 938 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", 939 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", 940 | "dev": true 941 | }, 942 | "minimatch": { 943 | "version": "3.0.4", 944 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 945 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 946 | "dev": true, 947 | "requires": { 948 | "brace-expansion": "^1.1.7" 949 | } 950 | }, 951 | "minimist": { 952 | "version": "0.0.8", 953 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 954 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 955 | "dev": true 956 | }, 957 | "mkdirp": { 958 | "version": "0.5.1", 959 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 960 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 961 | "dev": true, 962 | "requires": { 963 | "minimist": "0.0.8" 964 | } 965 | }, 966 | "ms": { 967 | "version": "2.0.0", 968 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 969 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 970 | "dev": true 971 | }, 972 | "mute-stream": { 973 | "version": "0.0.7", 974 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", 975 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", 976 | "dev": true 977 | }, 978 | "napi-macros": { 979 | "version": "1.3.0", 980 | "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-1.3.0.tgz", 981 | "integrity": "sha512-7OLSzp/c7/aG/juB6qxgtflVT2qkqs+uBL1Pieu76gTrS76HKEa39dMHB4EI7gebIVTgG3GYcSY2qEDUJbQzSQ==" 982 | }, 983 | "natural-compare": { 984 | "version": "1.4.0", 985 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 986 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 987 | "dev": true 988 | }, 989 | "normalize-package-data": { 990 | "version": "2.4.0", 991 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 992 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 993 | "dev": true, 994 | "requires": { 995 | "hosted-git-info": "^2.1.4", 996 | "is-builtin-module": "^1.0.0", 997 | "semver": "2 || 3 || 4 || 5", 998 | "validate-npm-package-license": "^3.0.1" 999 | } 1000 | }, 1001 | "object-assign": { 1002 | "version": "4.1.1", 1003 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1004 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1005 | "dev": true 1006 | }, 1007 | "once": { 1008 | "version": "1.4.0", 1009 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1010 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1011 | "dev": true, 1012 | "requires": { 1013 | "wrappy": "1" 1014 | } 1015 | }, 1016 | "onetime": { 1017 | "version": "2.0.1", 1018 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", 1019 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", 1020 | "dev": true, 1021 | "requires": { 1022 | "mimic-fn": "^1.0.0" 1023 | } 1024 | }, 1025 | "optionator": { 1026 | "version": "0.8.2", 1027 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 1028 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 1029 | "dev": true, 1030 | "requires": { 1031 | "deep-is": "~0.1.3", 1032 | "fast-levenshtein": "~2.0.4", 1033 | "levn": "~0.3.0", 1034 | "prelude-ls": "~1.1.2", 1035 | "type-check": "~0.3.2", 1036 | "wordwrap": "~1.0.0" 1037 | } 1038 | }, 1039 | "os-tmpdir": { 1040 | "version": "1.0.2", 1041 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1042 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 1043 | "dev": true 1044 | }, 1045 | "p-limit": { 1046 | "version": "1.2.0", 1047 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", 1048 | "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", 1049 | "dev": true, 1050 | "requires": { 1051 | "p-try": "^1.0.0" 1052 | } 1053 | }, 1054 | "p-locate": { 1055 | "version": "2.0.0", 1056 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", 1057 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", 1058 | "dev": true, 1059 | "requires": { 1060 | "p-limit": "^1.1.0" 1061 | } 1062 | }, 1063 | "p-try": { 1064 | "version": "1.0.0", 1065 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", 1066 | "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", 1067 | "dev": true 1068 | }, 1069 | "parse-json": { 1070 | "version": "2.2.0", 1071 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 1072 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 1073 | "dev": true, 1074 | "requires": { 1075 | "error-ex": "^1.2.0" 1076 | } 1077 | }, 1078 | "path-exists": { 1079 | "version": "2.1.0", 1080 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 1081 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 1082 | "dev": true, 1083 | "requires": { 1084 | "pinkie-promise": "^2.0.0" 1085 | } 1086 | }, 1087 | "path-is-absolute": { 1088 | "version": "1.0.1", 1089 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1090 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1091 | "dev": true 1092 | }, 1093 | "path-is-inside": { 1094 | "version": "1.0.2", 1095 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 1096 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 1097 | "dev": true 1098 | }, 1099 | "path-parse": { 1100 | "version": "1.0.5", 1101 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 1102 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", 1103 | "dev": true 1104 | }, 1105 | "path-type": { 1106 | "version": "2.0.0", 1107 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", 1108 | "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", 1109 | "dev": true, 1110 | "requires": { 1111 | "pify": "^2.0.0" 1112 | } 1113 | }, 1114 | "pify": { 1115 | "version": "2.3.0", 1116 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1117 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 1118 | "dev": true 1119 | }, 1120 | "pinkie": { 1121 | "version": "2.0.4", 1122 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1123 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 1124 | "dev": true 1125 | }, 1126 | "pinkie-promise": { 1127 | "version": "2.0.1", 1128 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1129 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1130 | "dev": true, 1131 | "requires": { 1132 | "pinkie": "^2.0.0" 1133 | } 1134 | }, 1135 | "pkg-dir": { 1136 | "version": "1.0.0", 1137 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", 1138 | "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", 1139 | "dev": true, 1140 | "requires": { 1141 | "find-up": "^1.0.0" 1142 | } 1143 | }, 1144 | "pluralize": { 1145 | "version": "7.0.0", 1146 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", 1147 | "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", 1148 | "dev": true 1149 | }, 1150 | "prelude-ls": { 1151 | "version": "1.1.2", 1152 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1153 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1154 | "dev": true 1155 | }, 1156 | "process-nextick-args": { 1157 | "version": "2.0.0", 1158 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1159 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", 1160 | "dev": true 1161 | }, 1162 | "progress": { 1163 | "version": "2.0.0", 1164 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", 1165 | "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", 1166 | "dev": true 1167 | }, 1168 | "pseudomap": { 1169 | "version": "1.0.2", 1170 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 1171 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 1172 | "dev": true 1173 | }, 1174 | "read-pkg": { 1175 | "version": "2.0.0", 1176 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", 1177 | "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", 1178 | "dev": true, 1179 | "requires": { 1180 | "load-json-file": "^2.0.0", 1181 | "normalize-package-data": "^2.3.2", 1182 | "path-type": "^2.0.0" 1183 | } 1184 | }, 1185 | "read-pkg-up": { 1186 | "version": "2.0.0", 1187 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", 1188 | "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", 1189 | "dev": true, 1190 | "requires": { 1191 | "find-up": "^2.0.0", 1192 | "read-pkg": "^2.0.0" 1193 | }, 1194 | "dependencies": { 1195 | "find-up": { 1196 | "version": "2.1.0", 1197 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", 1198 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", 1199 | "dev": true, 1200 | "requires": { 1201 | "locate-path": "^2.0.0" 1202 | } 1203 | } 1204 | } 1205 | }, 1206 | "readable-stream": { 1207 | "version": "2.3.6", 1208 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1209 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1210 | "dev": true, 1211 | "requires": { 1212 | "core-util-is": "~1.0.0", 1213 | "inherits": "~2.0.3", 1214 | "isarray": "~1.0.0", 1215 | "process-nextick-args": "~2.0.0", 1216 | "safe-buffer": "~5.1.1", 1217 | "string_decoder": "~1.1.1", 1218 | "util-deprecate": "~1.0.1" 1219 | } 1220 | }, 1221 | "regexpp": { 1222 | "version": "1.1.0", 1223 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", 1224 | "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", 1225 | "dev": true 1226 | }, 1227 | "require-uncached": { 1228 | "version": "1.0.3", 1229 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 1230 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 1231 | "dev": true, 1232 | "requires": { 1233 | "caller-path": "^0.1.0", 1234 | "resolve-from": "^1.0.0" 1235 | } 1236 | }, 1237 | "resolve": { 1238 | "version": "1.7.1", 1239 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", 1240 | "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", 1241 | "dev": true, 1242 | "requires": { 1243 | "path-parse": "^1.0.5" 1244 | } 1245 | }, 1246 | "resolve-from": { 1247 | "version": "1.0.1", 1248 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 1249 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", 1250 | "dev": true 1251 | }, 1252 | "restore-cursor": { 1253 | "version": "2.0.0", 1254 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", 1255 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", 1256 | "dev": true, 1257 | "requires": { 1258 | "onetime": "^2.0.0", 1259 | "signal-exit": "^3.0.2" 1260 | } 1261 | }, 1262 | "rimraf": { 1263 | "version": "2.6.2", 1264 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1265 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1266 | "dev": true, 1267 | "requires": { 1268 | "glob": "^7.0.5" 1269 | } 1270 | }, 1271 | "run-async": { 1272 | "version": "2.3.0", 1273 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 1274 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 1275 | "dev": true, 1276 | "requires": { 1277 | "is-promise": "^2.1.0" 1278 | } 1279 | }, 1280 | "rx-lite": { 1281 | "version": "4.0.8", 1282 | "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", 1283 | "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", 1284 | "dev": true 1285 | }, 1286 | "rx-lite-aggregates": { 1287 | "version": "4.0.8", 1288 | "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", 1289 | "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", 1290 | "dev": true, 1291 | "requires": { 1292 | "rx-lite": "*" 1293 | } 1294 | }, 1295 | "safe-buffer": { 1296 | "version": "5.1.2", 1297 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1298 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1299 | "dev": true 1300 | }, 1301 | "safer-buffer": { 1302 | "version": "2.1.2", 1303 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1304 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1305 | "dev": true 1306 | }, 1307 | "semver": { 1308 | "version": "5.5.0", 1309 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 1310 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", 1311 | "dev": true 1312 | }, 1313 | "shebang-command": { 1314 | "version": "1.2.0", 1315 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1316 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1317 | "dev": true, 1318 | "requires": { 1319 | "shebang-regex": "^1.0.0" 1320 | } 1321 | }, 1322 | "shebang-regex": { 1323 | "version": "1.0.0", 1324 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1325 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 1326 | "dev": true 1327 | }, 1328 | "signal-exit": { 1329 | "version": "3.0.2", 1330 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1331 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 1332 | "dev": true 1333 | }, 1334 | "slice-ansi": { 1335 | "version": "1.0.0", 1336 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", 1337 | "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", 1338 | "dev": true, 1339 | "requires": { 1340 | "is-fullwidth-code-point": "^2.0.0" 1341 | } 1342 | }, 1343 | "spdx-correct": { 1344 | "version": "3.0.0", 1345 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", 1346 | "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", 1347 | "dev": true, 1348 | "requires": { 1349 | "spdx-expression-parse": "^3.0.0", 1350 | "spdx-license-ids": "^3.0.0" 1351 | } 1352 | }, 1353 | "spdx-exceptions": { 1354 | "version": "2.1.0", 1355 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", 1356 | "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", 1357 | "dev": true 1358 | }, 1359 | "spdx-expression-parse": { 1360 | "version": "3.0.0", 1361 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1362 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1363 | "dev": true, 1364 | "requires": { 1365 | "spdx-exceptions": "^2.1.0", 1366 | "spdx-license-ids": "^3.0.0" 1367 | } 1368 | }, 1369 | "spdx-license-ids": { 1370 | "version": "3.0.0", 1371 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", 1372 | "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", 1373 | "dev": true 1374 | }, 1375 | "sprintf-js": { 1376 | "version": "1.0.3", 1377 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1378 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1379 | "dev": true 1380 | }, 1381 | "string-width": { 1382 | "version": "2.1.1", 1383 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1384 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1385 | "dev": true, 1386 | "requires": { 1387 | "is-fullwidth-code-point": "^2.0.0", 1388 | "strip-ansi": "^4.0.0" 1389 | } 1390 | }, 1391 | "string_decoder": { 1392 | "version": "1.1.1", 1393 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1394 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1395 | "dev": true, 1396 | "requires": { 1397 | "safe-buffer": "~5.1.0" 1398 | } 1399 | }, 1400 | "strip-ansi": { 1401 | "version": "4.0.0", 1402 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1403 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1404 | "dev": true, 1405 | "requires": { 1406 | "ansi-regex": "^3.0.0" 1407 | }, 1408 | "dependencies": { 1409 | "ansi-regex": { 1410 | "version": "3.0.0", 1411 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1412 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 1413 | "dev": true 1414 | } 1415 | } 1416 | }, 1417 | "strip-bom": { 1418 | "version": "3.0.0", 1419 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 1420 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 1421 | "dev": true 1422 | }, 1423 | "strip-json-comments": { 1424 | "version": "2.0.1", 1425 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1426 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1427 | "dev": true 1428 | }, 1429 | "supports-color": { 1430 | "version": "2.0.0", 1431 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1432 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 1433 | "dev": true 1434 | }, 1435 | "table": { 1436 | "version": "4.0.2", 1437 | "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", 1438 | "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", 1439 | "dev": true, 1440 | "requires": { 1441 | "ajv": "^5.2.3", 1442 | "ajv-keywords": "^2.1.0", 1443 | "chalk": "^2.1.0", 1444 | "lodash": "^4.17.4", 1445 | "slice-ansi": "1.0.0", 1446 | "string-width": "^2.1.1" 1447 | } 1448 | }, 1449 | "text-table": { 1450 | "version": "0.2.0", 1451 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1452 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1453 | "dev": true 1454 | }, 1455 | "through": { 1456 | "version": "2.3.8", 1457 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1458 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1459 | "dev": true 1460 | }, 1461 | "tmp": { 1462 | "version": "0.0.33", 1463 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 1464 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 1465 | "dev": true, 1466 | "requires": { 1467 | "os-tmpdir": "~1.0.2" 1468 | } 1469 | }, 1470 | "type-check": { 1471 | "version": "0.3.2", 1472 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1473 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1474 | "dev": true, 1475 | "requires": { 1476 | "prelude-ls": "~1.1.2" 1477 | } 1478 | }, 1479 | "typedarray": { 1480 | "version": "0.0.6", 1481 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1482 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1483 | "dev": true 1484 | }, 1485 | "util-deprecate": { 1486 | "version": "1.0.2", 1487 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1488 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1489 | "dev": true 1490 | }, 1491 | "validate-npm-package-license": { 1492 | "version": "3.0.3", 1493 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", 1494 | "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", 1495 | "dev": true, 1496 | "requires": { 1497 | "spdx-correct": "^3.0.0", 1498 | "spdx-expression-parse": "^3.0.0" 1499 | } 1500 | }, 1501 | "which": { 1502 | "version": "1.3.0", 1503 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", 1504 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", 1505 | "dev": true, 1506 | "requires": { 1507 | "isexe": "^2.0.0" 1508 | } 1509 | }, 1510 | "wordwrap": { 1511 | "version": "1.0.0", 1512 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1513 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1514 | "dev": true 1515 | }, 1516 | "wrappy": { 1517 | "version": "1.0.2", 1518 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1519 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1520 | "dev": true 1521 | }, 1522 | "write": { 1523 | "version": "0.2.1", 1524 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1525 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1526 | "dev": true, 1527 | "requires": { 1528 | "mkdirp": "^0.5.1" 1529 | } 1530 | }, 1531 | "yallist": { 1532 | "version": "2.1.2", 1533 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1534 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 1535 | "dev": true 1536 | } 1537 | } 1538 | } 1539 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuv", 3 | "version": "1.0.0", 4 | "description": "N-API bindings for subset of libuv", 5 | "main": "src/nuv.js", 6 | "author": "Tim Caswell ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "bindings": "^1.3.0", 10 | "napi-macros": "^1.3.0" 11 | }, 12 | "devDependencies": { 13 | "eslint": "^4.19.1", 14 | "eslint-plugin-import": "^2.11.0", 15 | "eslint-plugin-node": "^6.0.1", 16 | "eslint-plugin-promise": "^3.7.0", 17 | "eslint-plugin-standard": "^3.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/nuv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct 9 | { 10 | napi_env env; // The JS environment. 11 | napi_ref this; // Event handlers, read buffer, and `this` for callbacks. 12 | } nuv_context_t; 13 | 14 | #define NUV_CALLBACK_START(handle, name) \ 15 | nuv_context_t *context = handle->data; \ 16 | napi_env env = context->env; \ 17 | napi_handle_scope scope; \ 18 | napi_open_handle_scope(env, &scope); \ 19 | napi_value this; \ 20 | napi_get_reference_value(env, context->this, &this); \ 21 | napi_value handler; \ 22 | napi_get_named_property(env, this, name, &handler); 23 | 24 | #define NUV_CALLBACK_ARGS(count) \ 25 | int argc = count; \ 26 | napi_value argv[count]; 27 | 28 | #define NUV_CALLBACK_CALL() \ 29 | napi_value result; \ 30 | napi_make_callback(env, NULL, this, handler, argc, argv, &result); 31 | 32 | #define NUV_CALLBACK_END() \ 33 | napi_close_handle_scope(env, scope); 34 | 35 | #define NAPI_BOOL(name, val) \ 36 | bool name; \ 37 | napi_get_value_bool(env, val, &name); 38 | 39 | #define NAPI_INT64(name, val) \ 40 | int64_t name; \ 41 | napi_get_value_int64(env, val, &name); 42 | 43 | static napi_status nuv_create_status(napi_env env, int status, napi_value *target) 44 | { 45 | if (status) 46 | { 47 | napi_value code; 48 | napi_value message; 49 | napi_create_string_utf8(env, uv_err_name(status), NAPI_AUTO_LENGTH, &code); 50 | napi_create_string_utf8(env, uv_strerror(status), NAPI_AUTO_LENGTH, &message); 51 | return napi_create_error(env, code, message, target); 52 | } 53 | return napi_get_undefined(env, target); 54 | } 55 | 56 | static napi_value parse_sockaddr(napi_env env, struct sockaddr_storage *address) 57 | { 58 | char ip[INET6_ADDRSTRLEN]; 59 | int port = 0; 60 | napi_value obj; 61 | napi_create_object(env, &obj); 62 | if (address->ss_family == AF_INET) 63 | { 64 | struct sockaddr_in *addrin = (struct sockaddr_in *)address; 65 | uv_inet_ntop(AF_INET, &(addrin->sin_addr), ip, INET6_ADDRSTRLEN); 66 | port = ntohs(addrin->sin_port); 67 | } 68 | else if (address->ss_family == AF_INET6) 69 | { 70 | struct sockaddr_in6 *addrin6 = (struct sockaddr_in6 *)address; 71 | uv_inet_ntop(AF_INET6, &(addrin6->sin6_addr), ip, INET6_ADDRSTRLEN); 72 | port = ntohs(addrin6->sin6_port); 73 | } 74 | napi_value port_value; 75 | napi_create_uint32(env, port, &port_value); 76 | napi_set_named_property(env, obj, "port", port_value); 77 | napi_value ip_value; 78 | napi_create_string_utf8(env, ip, NAPI_AUTO_LENGTH, &ip_value); 79 | napi_set_named_property(env, obj, "ip", ip_value); 80 | return obj; 81 | } 82 | 83 | static napi_status nuv_create_addr(napi_env env, struct sockaddr_storage *address, napi_value *res) 84 | { 85 | *res = parse_sockaddr(env, address); 86 | return napi_ok; 87 | } 88 | 89 | static void on_uv_close(uv_handle_t *handle) 90 | { 91 | // printf("on_uv_close handle=%p\n", handle); 92 | NUV_CALLBACK_START(handle, "onClose") 93 | NUV_CALLBACK_ARGS(0) 94 | NUV_CALLBACK_CALL() 95 | NUV_CALLBACK_END() 96 | 97 | // Clean up memory and release references now that the handle is closed. 98 | napi_delete_reference(env, context->this); 99 | free(handle->data); 100 | handle->data = NULL; 101 | } 102 | 103 | static void on_uv_timeout(uv_timer_t *handle) 104 | { 105 | NUV_CALLBACK_START(handle, "onTimeout") 106 | NUV_CALLBACK_ARGS(0) 107 | NUV_CALLBACK_CALL() 108 | NUV_CALLBACK_END() 109 | } 110 | 111 | static void on_uv_connection(uv_stream_t *handle, int status) 112 | { 113 | // printf("on_uv_connection handle=%p status=%d\n", handle, status); 114 | if (status) 115 | { 116 | NUV_CALLBACK_START(handle, "onError") 117 | NUV_CALLBACK_ARGS(1) 118 | nuv_create_status(env, status, &argv[0]); 119 | NUV_CALLBACK_CALL() 120 | NUV_CALLBACK_END() 121 | } 122 | else 123 | { 124 | NUV_CALLBACK_START(handle, "onConnection") 125 | NUV_CALLBACK_ARGS(0) 126 | NUV_CALLBACK_CALL() 127 | NUV_CALLBACK_END() 128 | } 129 | } 130 | 131 | static void on_uv_alloc(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) 132 | { 133 | // printf("on_uv_alloc handle=%p suggested_size=%zu buf=%p\n", handle, suggested_size, buf); 134 | NUV_CALLBACK_START(handle, "readBuffer") 135 | NAPI_BUFFER_CAST(char *, data, handler) 136 | buf->base = data; 137 | buf->len = data_len; 138 | NUV_CALLBACK_END() 139 | } 140 | 141 | static void on_uv_read(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) 142 | { 143 | // printf("on_uv_read handle=%p nread=%zd buf=%p\n", handle, nread, buf); 144 | if (!nread) 145 | return; 146 | NUV_CALLBACK_START(handle, "onRead") 147 | NUV_CALLBACK_ARGS(2) 148 | if (nread < 0) 149 | nuv_create_status(env, nread, &argv[0]); 150 | else 151 | napi_get_undefined(env, &argv[0]); 152 | if (nread > 0) 153 | napi_create_uint32(env, nread, &argv[1]); 154 | else 155 | napi_get_undefined(env, &argv[1]); 156 | NUV_CALLBACK_CALL() 157 | NUV_CALLBACK_END() 158 | } 159 | 160 | static void on_uv_connect(uv_connect_t *req, int status) 161 | { 162 | // printf("on_uv_connect req=%p status=%d\n", req, status); 163 | uv_stream_t *handle = req->handle; 164 | NUV_CALLBACK_START(handle, "onConnect") 165 | NUV_CALLBACK_ARGS(1) 166 | nuv_create_status(env, status, &argv[0]); 167 | NUV_CALLBACK_CALL() 168 | napi_delete_reference(env, req->data); 169 | req->data = NULL; 170 | NUV_CALLBACK_END() 171 | } 172 | 173 | static void on_uv_shutdown(uv_shutdown_t *req, int status) 174 | { 175 | // printf("on_uv_shutdown req=%p status=%d\n", req, status); 176 | uv_stream_t *handle = req->handle; 177 | NUV_CALLBACK_START(handle, "onShutdown") 178 | NUV_CALLBACK_ARGS(1) 179 | nuv_create_status(env, status, &argv[0]); 180 | NUV_CALLBACK_CALL() 181 | napi_delete_reference(env, req->data); 182 | req->data = NULL; 183 | NUV_CALLBACK_END() 184 | } 185 | 186 | static void on_uv_write(uv_write_t *req, int status) 187 | { 188 | // printf("on_uv_write req=%p status=%d\n", req, status); 189 | uv_stream_t *handle = req->handle; 190 | NUV_CALLBACK_START(handle, "onWrite") 191 | NUV_CALLBACK_ARGS(1) 192 | nuv_create_status(env, status, &argv[0]); 193 | NUV_CALLBACK_CALL() 194 | napi_delete_reference(env, req->data); 195 | req->data = NULL; 196 | NUV_CALLBACK_END() 197 | } 198 | 199 | static void on_uv_send(uv_udp_send_t *req, int status) 200 | { 201 | uv_udp_t *handle = req->handle; 202 | NUV_CALLBACK_START(handle, "onSend") 203 | NUV_CALLBACK_ARGS(1) 204 | nuv_create_status(env, status, &argv[0]); 205 | NUV_CALLBACK_CALL() 206 | napi_delete_reference(env, req->data); 207 | req->data = NULL; 208 | NUV_CALLBACK_END() 209 | } 210 | 211 | static void on_uv_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) 212 | { 213 | // printf("on_uv_recv handle=%p nread=%zd buf=%p addr=%p flags=%u\n", handle, nread, buf, addr, flags); 214 | if (!nread) 215 | return; 216 | NUV_CALLBACK_START(handle, "onRecv") 217 | NUV_CALLBACK_ARGS(3) 218 | if (nread < 0) 219 | nuv_create_status(env, nread, &argv[0]); 220 | else 221 | napi_get_undefined(env, &argv[0]); 222 | if (nread > 0) 223 | { 224 | napi_create_uint32(env, nread, &argv[1]); 225 | nuv_create_addr(env, (struct sockaddr_storage *)addr, &argv[2]); 226 | } 227 | else 228 | { 229 | napi_get_undefined(env, &argv[1]); 230 | napi_get_undefined(env, &argv[2]); 231 | } 232 | NUV_CALLBACK_CALL() 233 | NUV_CALLBACK_END() 234 | } 235 | 236 | // nuv_tcp_init(handle, this) 237 | NAPI_METHOD(nuv_tcp_init) 238 | { 239 | NAPI_ARGV(2) 240 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[0]) 241 | int NAPI_UV_THROWS(err, uv_tcp_init(uv_default_loop(), handle)); 242 | nuv_context_t *context = malloc(sizeof(*context)); 243 | handle->data = context; 244 | context->env = env; 245 | napi_create_reference(env, argv[1], 1, &(context->this)); 246 | napi_set_named_property(env, argv[1], "handle", argv[0]); 247 | return NULL; 248 | } 249 | 250 | // nuv_tcp_nodelay(handle, enable) 251 | NAPI_METHOD(nuv_tcp_nodelay) 252 | { 253 | NAPI_ARGV(2) 254 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[0]) 255 | NAPI_BOOL(enable, argv[1]) 256 | int NAPI_UV_THROWS(err, uv_tcp_nodelay(handle, enable)); 257 | return NULL; 258 | } 259 | 260 | // nuv_tcp_keepalive(handle, enable, delay) 261 | NAPI_METHOD(nuv_tcp_keepalive) 262 | { 263 | NAPI_ARGV(3) 264 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[0]) 265 | NAPI_BOOL(enable, argv[1]) 266 | NAPI_UINT32(delay, argv[2]) 267 | int NAPI_UV_THROWS(err, uv_tcp_keepalive(handle, enable, delay)); 268 | return NULL; 269 | } 270 | 271 | // nuv_tcp_simultaneous_accepts(handle, enable) 272 | NAPI_METHOD(nuv_tcp_simultaneous_accepts) 273 | { 274 | NAPI_ARGV(2) 275 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[0]) 276 | NAPI_BOOL(enable, argv[1]) 277 | int NAPI_UV_THROWS(err, uv_tcp_simultaneous_accepts(handle, enable)); 278 | return NULL; 279 | } 280 | 281 | // nuv_tcp_bind(handle, ip, port) 282 | NAPI_METHOD(nuv_tcp_bind) 283 | { 284 | NAPI_ARGV(3) 285 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[0]) 286 | NAPI_UTF8(ip, 64, argv[1]) 287 | NAPI_UINT32(port, argv[2]) 288 | 289 | struct sockaddr_in addr; 290 | int err; 291 | NAPI_UV_THROWS(err, uv_ip4_addr(ip, port, &addr)); 292 | NAPI_UV_THROWS(err, uv_tcp_bind(handle, (const struct sockaddr *)&addr, 0)); 293 | return NULL; 294 | } 295 | 296 | // nuv_tcp_getsockname(handle) -> { ip, port } 297 | NAPI_METHOD(nuv_tcp_getsockname) 298 | { 299 | NAPI_ARGV(1) 300 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[0]) 301 | struct sockaddr_storage name; 302 | int namelen = sizeof(name); 303 | int NAPI_UV_THROWS(err, uv_tcp_getsockname(handle, (struct sockaddr *)&name, &namelen)); 304 | return parse_sockaddr(env, &name); 305 | } 306 | 307 | // nuv_tcp_getpeername(handle) -> { ip, port } 308 | NAPI_METHOD(nuv_tcp_getpeername) 309 | { 310 | NAPI_ARGV(1) 311 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[0]) 312 | struct sockaddr_storage name; 313 | int namelen = sizeof(name); 314 | int NAPI_UV_THROWS(err, uv_tcp_getpeername(handle, (struct sockaddr *)&name, &namelen)); 315 | // int err = uv_tcp_getpeername(handle, (struct sockaddr*)&name, &namelen); 316 | // printf("err=%d\n", err); 317 | return parse_sockaddr(env, &name); 318 | } 319 | 320 | NAPI_METHOD(nuv_tcp_connect) 321 | { 322 | NAPI_ARGV(4) 323 | NAPI_BUFFER_CAST(uv_connect_t *, req, argv[0]) 324 | NAPI_BUFFER_CAST(uv_tcp_t *, handle, argv[1]) 325 | NAPI_UTF8(ip, 64, argv[2]) 326 | NAPI_UINT32(port, argv[3]) 327 | struct sockaddr_in addr; 328 | int err; 329 | NAPI_UV_THROWS(err, uv_ip4_addr(ip, port, &addr)); 330 | NAPI_UV_THROWS(err, uv_tcp_connect(req, handle, (const struct sockaddr *)&addr, on_uv_connect)); 331 | napi_create_reference(env, argv[0], 1, req->data); 332 | return NULL; 333 | } 334 | 335 | NAPI_METHOD(nuv_shutdown) 336 | { 337 | NAPI_ARGV(2) 338 | NAPI_BUFFER_CAST(uv_shutdown_t *, req, argv[0]) 339 | NAPI_BUFFER_CAST(uv_stream_t *, handle, argv[1]) 340 | int NAPI_UV_THROWS(err, uv_shutdown(req, handle, on_uv_shutdown)); 341 | napi_create_reference(env, argv[0], 1, req->data); 342 | return NULL; 343 | } 344 | 345 | NAPI_METHOD(nuv_listen) 346 | { 347 | NAPI_ARGV(2) 348 | NAPI_BUFFER_CAST(uv_stream_t *, handle, argv[0]) 349 | NAPI_UINT32(backlog, argv[1]) 350 | int NAPI_UV_THROWS(err, uv_listen(handle, backlog, on_uv_connection)); 351 | return NULL; 352 | } 353 | 354 | NAPI_METHOD(nuv_accept) 355 | { 356 | NAPI_ARGV(2) 357 | NAPI_BUFFER_CAST(uv_stream_t *, handle, argv[0]) 358 | NAPI_BUFFER_CAST(uv_stream_t *, client, argv[1]) 359 | int NAPI_UV_THROWS(err, uv_accept(handle, client)); 360 | return NULL; 361 | } 362 | 363 | NAPI_METHOD(nuv_read_start) 364 | { 365 | NAPI_ARGV(1) 366 | NAPI_BUFFER_CAST(uv_stream_t *, handle, argv[0]) 367 | int NAPI_UV_THROWS(err, uv_read_start(handle, on_uv_alloc, on_uv_read)); 368 | return NULL; 369 | } 370 | 371 | NAPI_METHOD(nuv_read_stop) 372 | { 373 | NAPI_ARGV(1) 374 | NAPI_BUFFER_CAST(uv_stream_t *, handle, argv[0]) 375 | int NAPI_UV_THROWS(err, uv_read_stop(handle)); 376 | return NULL; 377 | } 378 | 379 | NAPI_METHOD(nuv_write) 380 | { 381 | NAPI_ARGV(3) 382 | NAPI_BUFFER_CAST(uv_write_t *, req, argv[0]) 383 | NAPI_BUFFER_CAST(uv_stream_t *, handle, argv[1]) 384 | NAPI_BUFFER_CAST(char *, data, argv[2]) 385 | uv_buf_t buf; 386 | buf.base = data; 387 | buf.len = data_len; 388 | int NAPI_UV_THROWS(err, uv_write(req, handle, &buf, 1, on_uv_write)); 389 | napi_create_reference(env, argv[0], 1, req->data); 390 | return NULL; 391 | } 392 | 393 | NAPI_METHOD(nuv_udp_init) 394 | { 395 | NAPI_ARGV(2) 396 | NAPI_BUFFER_CAST(uv_udp_t *, handle, argv[0]) 397 | int NAPI_UV_THROWS(err, uv_udp_init(uv_default_loop(), handle)); 398 | nuv_context_t *context = malloc(sizeof(*context)); 399 | handle->data = context; 400 | context->env = env; 401 | napi_create_reference(env, argv[1], 1, &(context->this)); 402 | napi_set_named_property(env, argv[1], "handle", argv[0]); 403 | return NULL; 404 | } 405 | 406 | NAPI_METHOD(nuv_udp_bind) 407 | { 408 | NAPI_ARGV(3) 409 | NAPI_BUFFER_CAST(uv_udp_t *, handle, argv[0]) 410 | NAPI_UTF8(ip, 64, argv[1]) 411 | NAPI_UINT32(port, argv[2]) 412 | struct sockaddr_in addr; 413 | int err; 414 | NAPI_UV_THROWS(err, uv_ip4_addr(ip, port, &addr)); 415 | NAPI_UV_THROWS(err, uv_udp_bind(handle, (const struct sockaddr *)&addr, 0)); 416 | return NULL; 417 | } 418 | 419 | NAPI_METHOD(nuv_udp_getsockname) 420 | { 421 | NAPI_ARGV(1) 422 | NAPI_BUFFER_CAST(uv_udp_t *, handle, argv[0]) 423 | struct sockaddr_storage name; 424 | int namelen = sizeof(name); 425 | int NAPI_UV_THROWS(err, uv_udp_getsockname(handle, (struct sockaddr *)&name, &namelen)); 426 | return parse_sockaddr(env, &name); 427 | } 428 | 429 | NAPI_METHOD(nuv_udp_send) 430 | { 431 | NAPI_ARGV(5) 432 | NAPI_BUFFER_CAST(uv_udp_send_t *, req, argv[0]) 433 | NAPI_BUFFER_CAST(uv_udp_t *, handle, argv[1]) 434 | NAPI_BUFFER_CAST(char *, data, argv[2]) 435 | NAPI_UTF8(ip, 64, argv[3]) 436 | NAPI_UINT32(port, argv[4]) 437 | struct sockaddr_in addr; 438 | int err; 439 | NAPI_UV_THROWS(err, uv_ip4_addr(ip, port, &addr)); 440 | uv_buf_t buf; 441 | buf.base = data; 442 | buf.len = data_len; 443 | NAPI_UV_THROWS(err, uv_udp_send(req, handle, &buf, 1, (const struct sockaddr *)&addr, on_uv_send)); 444 | napi_create_reference(env, argv[0], 1, req->data); 445 | return NULL; 446 | } 447 | 448 | NAPI_METHOD(nuv_udp_recv_start) 449 | { 450 | NAPI_ARGV(1) 451 | NAPI_BUFFER_CAST(uv_udp_t *, handle, argv[0]) 452 | int NAPI_UV_THROWS(err, uv_udp_recv_start(handle, on_uv_alloc, on_uv_recv)); 453 | return NULL; 454 | } 455 | 456 | NAPI_METHOD(nuv_udp_recv_stop) 457 | { 458 | NAPI_ARGV(1) 459 | NAPI_BUFFER_CAST(uv_udp_t *, handle, argv[0]) 460 | int NAPI_UV_THROWS(err, uv_udp_recv_stop(handle)); 461 | return NULL; 462 | } 463 | 464 | NAPI_METHOD(nuv_timer_init) 465 | { 466 | NAPI_ARGV(2) 467 | NAPI_BUFFER_CAST(uv_timer_t *, handle, argv[0]) 468 | int NAPI_UV_THROWS(err, uv_timer_init(uv_default_loop(), handle)); 469 | nuv_context_t *context = malloc(sizeof(*context)); 470 | handle->data = context; 471 | context->env = env; 472 | napi_create_reference(env, argv[1], 1, &(context->this)); 473 | napi_set_named_property(env, argv[1], "handle", argv[0]); 474 | return NULL; 475 | } 476 | 477 | NAPI_METHOD(nuv_timer_start) 478 | { 479 | NAPI_ARGV(3) 480 | NAPI_BUFFER_CAST(uv_timer_t *, handle, argv[0]) 481 | NAPI_INT64(timeout, argv[1]) 482 | NAPI_INT64(repeat, argv[2]) 483 | int NAPI_UV_THROWS(err, uv_timer_start(handle, on_uv_timeout, timeout, repeat)); 484 | return NULL; 485 | } 486 | 487 | NAPI_METHOD(nuv_timer_stop) 488 | { 489 | NAPI_ARGV(1) 490 | NAPI_BUFFER_CAST(uv_timer_t *, handle, argv[0]) 491 | int NAPI_UV_THROWS(err, uv_timer_stop(handle)); 492 | return NULL; 493 | } 494 | 495 | NAPI_METHOD(nuv_timer_again) 496 | { 497 | NAPI_ARGV(1) 498 | NAPI_BUFFER_CAST(uv_timer_t *, handle, argv[0]) 499 | int NAPI_UV_THROWS(err, uv_timer_again(handle)); 500 | return NULL; 501 | } 502 | 503 | NAPI_METHOD(nuv_timer_set_repeat) 504 | { 505 | NAPI_ARGV(2) 506 | NAPI_BUFFER_CAST(uv_timer_t *, handle, argv[0]) 507 | NAPI_INT64(repeat, argv[1]) 508 | uv_timer_set_repeat(handle, repeat); 509 | return NULL; 510 | } 511 | 512 | NAPI_METHOD(nuv_timer_get_repeat) 513 | { 514 | NAPI_ARGV(1) 515 | NAPI_BUFFER_CAST(uv_timer_t *, handle, argv[0]) 516 | napi_value repeat; 517 | napi_create_int64(env, uv_timer_get_repeat(handle), &repeat); 518 | return repeat; 519 | } 520 | 521 | NAPI_METHOD(nuv_close) 522 | { 523 | NAPI_ARGV(1) 524 | NAPI_ARGV_BUFFER_CAST(uv_tcp_t *, handle, 0) 525 | uv_close((uv_handle_t *)handle, on_uv_close); 526 | return NULL; 527 | } 528 | 529 | NAPI_INIT() 530 | { 531 | NAPI_EXPORT_SIZEOF(uv_tcp_t) 532 | NAPI_EXPORT_SIZEOF(uv_connect_t) 533 | NAPI_EXPORT_SIZEOF(uv_shutdown_t) 534 | NAPI_EXPORT_SIZEOF(uv_write_t) 535 | NAPI_EXPORT_SIZEOF(uv_udp_t) 536 | NAPI_EXPORT_SIZEOF(uv_udp_send_t) 537 | NAPI_EXPORT_SIZEOF(uv_timer_t) 538 | // TCP Functions 539 | NAPI_EXPORT_FUNCTION(nuv_tcp_init) 540 | NAPI_EXPORT_FUNCTION(nuv_tcp_nodelay) 541 | NAPI_EXPORT_FUNCTION(nuv_tcp_keepalive) 542 | NAPI_EXPORT_FUNCTION(nuv_tcp_simultaneous_accepts) 543 | NAPI_EXPORT_FUNCTION(nuv_tcp_bind) 544 | NAPI_EXPORT_FUNCTION(nuv_tcp_getsockname) 545 | NAPI_EXPORT_FUNCTION(nuv_tcp_getpeername) 546 | NAPI_EXPORT_FUNCTION(nuv_tcp_connect) 547 | // Stream Functions 548 | NAPI_EXPORT_FUNCTION(nuv_shutdown) 549 | NAPI_EXPORT_FUNCTION(nuv_listen) 550 | NAPI_EXPORT_FUNCTION(nuv_accept) 551 | NAPI_EXPORT_FUNCTION(nuv_read_start) 552 | NAPI_EXPORT_FUNCTION(nuv_read_stop) 553 | NAPI_EXPORT_FUNCTION(nuv_write) 554 | // UDP Functions 555 | NAPI_EXPORT_FUNCTION(nuv_udp_init) 556 | NAPI_EXPORT_FUNCTION(nuv_udp_bind) 557 | NAPI_EXPORT_FUNCTION(nuv_udp_getsockname) 558 | NAPI_EXPORT_FUNCTION(nuv_udp_send) 559 | NAPI_EXPORT_FUNCTION(nuv_udp_recv_start) 560 | NAPI_EXPORT_FUNCTION(nuv_udp_recv_stop) 561 | // Timer Functions 562 | NAPI_EXPORT_FUNCTION(nuv_timer_init) 563 | NAPI_EXPORT_FUNCTION(nuv_timer_start) 564 | NAPI_EXPORT_FUNCTION(nuv_timer_stop) 565 | NAPI_EXPORT_FUNCTION(nuv_timer_again) 566 | NAPI_EXPORT_FUNCTION(nuv_timer_set_repeat) 567 | NAPI_EXPORT_FUNCTION(nuv_timer_get_repeat) 568 | // Handle Functions 569 | NAPI_EXPORT_FUNCTION(nuv_close) 570 | } 571 | -------------------------------------------------------------------------------- /src/nuv.js: -------------------------------------------------------------------------------- 1 | const bindings = require('bindings')('nuv.node') 2 | 3 | const { 4 | /* eslint camelcase: 0 */ 5 | sizeof_uv_tcp_t, 6 | sizeof_uv_udp_t, 7 | sizeof_uv_connect_t, 8 | sizeof_uv_shutdown_t, 9 | sizeof_uv_write_t, 10 | sizeof_uv_udp_send_t, 11 | sizeof_uv_timer_t, 12 | // TCP Functions 13 | nuv_tcp_init, 14 | nuv_tcp_nodelay, 15 | nuv_tcp_keepalive, 16 | nuv_tcp_simultaneous_accepts, 17 | nuv_tcp_bind, 18 | nuv_tcp_getsockname, 19 | nuv_tcp_getpeername, 20 | nuv_tcp_connect, 21 | // Stream Functions 22 | nuv_shutdown, 23 | nuv_listen, 24 | nuv_accept, 25 | nuv_read_start, 26 | nuv_read_stop, 27 | nuv_write, 28 | // UDP Functions 29 | nuv_udp_init, 30 | nuv_udp_bind, 31 | nuv_udp_getsockname, 32 | nuv_udp_send, 33 | nuv_udp_recv_start, 34 | nuv_udp_recv_stop, 35 | // Timer Functions 36 | nuv_timer_init, 37 | nuv_timer_start, 38 | nuv_timer_stop, 39 | nuv_timer_again, 40 | nuv_timer_set_repeat, 41 | nuv_timer_get_repeat, 42 | 43 | // Handle Functions 44 | nuv_close 45 | } = bindings 46 | 47 | class Handle { 48 | wait (name, self) { 49 | return new Promise((resolve, reject) => { 50 | this[name] = (err, result) => { 51 | delete this[name] 52 | return err ? reject(err) : resolve(self || result) 53 | } 54 | }) 55 | } 56 | 57 | close () { 58 | nuv_close(this.handle) 59 | return this.wait('onClose') 60 | } 61 | } 62 | 63 | class Tcp extends Handle { 64 | constructor (handlers) { 65 | super() 66 | for (const key in handlers) { 67 | this[key] = handlers[key] 68 | } 69 | nuv_tcp_init(Buffer.alloc(sizeof_uv_tcp_t), this) 70 | } 71 | 72 | get sockname () { 73 | return nuv_tcp_getsockname(this.handle) 74 | } 75 | 76 | get peername () { 77 | return nuv_tcp_getpeername(this.handle) 78 | } 79 | } 80 | 81 | class Server extends Tcp { 82 | bind (ip, port) { 83 | nuv_tcp_bind(this.handle, ip, port) 84 | return this 85 | } 86 | 87 | listen (backlog) { 88 | nuv_listen(this.handle, backlog) 89 | return this 90 | } 91 | 92 | accept (options) { 93 | const socket = new Socket(options) 94 | nuv_accept(this.handle, socket.handle) 95 | return socket 96 | } 97 | 98 | set simultaneousAccepts (enable) { 99 | nuv_tcp_simultaneous_accepts(this.handle, enable) 100 | } 101 | } 102 | 103 | class Socket extends Tcp { 104 | constructor () { 105 | super() 106 | if (!this.readBuffer) this.readBuffer = Buffer.alloc(0x10000) 107 | this.queue = [] 108 | this.reader = 0 109 | this.writer = 0 110 | this.paused = true 111 | } 112 | 113 | write (data, ...args) { 114 | if (!data) { 115 | nuv_shutdown(Buffer.alloc(sizeof_uv_shutdown_t), this.handle) 116 | return this.wait('onShutdown') 117 | } 118 | if (!Buffer.isBuffer(data)) data = Buffer.from(data, ...args) 119 | nuv_write(Buffer.alloc(sizeof_uv_write_t), this.handle, data) 120 | return this.wait('onWrite') 121 | } 122 | 123 | onRead (err, nread) { 124 | if (this.reader > this.writer) { 125 | const { resolve, reject } = this.queue[this.writer] 126 | delete this.queue[this.writer++] 127 | return err ? reject(err) : resolve(this.readBuffer.slice(0, nread)) 128 | } 129 | if (!this.paused) { 130 | this.paused = true 131 | nuv_read_stop(this.handle) 132 | } 133 | this.queue[this.writer++] = { err, nread } 134 | } 135 | 136 | read () { 137 | return new Promise((resolve, reject) => { 138 | if (this.writer > this.reader) { 139 | const { err, nread } = this.queue[this.reader] 140 | delete this.queue[this.reader++] 141 | return err ? reject(err) : resolve(this.readBuffer.slice(0, nread)) 142 | } 143 | if (this.paused) { 144 | this.paused = false 145 | nuv_read_start(this.handle) 146 | } 147 | this.queue[this.reader++] = { resolve, reject } 148 | }) 149 | } 150 | 151 | set keepalive (delay) { 152 | nuv_tcp_keepalive(this.handle, !!delay, delay) 153 | } 154 | 155 | set nodelay (enable) { 156 | nuv_tcp_nodelay(this.handle, enable) 157 | } 158 | 159 | [Symbol.asyncIterator] () { 160 | return { 161 | next: () => new Promise((resolve, reject) => { 162 | this.read().then( 163 | value => resolve({ value }), 164 | err => err.code === 'EOF' ? resolve({ done: true }) : reject(err) 165 | ) 166 | }) 167 | } 168 | } 169 | } 170 | 171 | class Client extends Socket { 172 | connect (ip, port) { 173 | nuv_tcp_connect(Buffer.alloc(sizeof_uv_connect_t), this.handle, ip, port) 174 | return this.wait('onConnect', this) 175 | } 176 | } 177 | 178 | class Udp extends Handle { 179 | constructor (handlers) { 180 | super() 181 | for (const key in handlers) { 182 | this[key] = handlers[key] 183 | } 184 | nuv_udp_init(Buffer.alloc(sizeof_uv_udp_t), this) 185 | if (!this.readBuffer) this.readBuffer = Buffer.alloc(0x10000) 186 | this.queue = [] 187 | this.reader = 0 188 | this.writer = 0 189 | this.paused = true 190 | } 191 | 192 | get sockname () { 193 | return nuv_udp_getsockname(this.handle) 194 | } 195 | 196 | bind (ip, port) { 197 | nuv_udp_bind(this.handle, ip, port) 198 | return this 199 | } 200 | 201 | send (ip, port, data, ...args) { 202 | if (!Buffer.isBuffer(data)) data = Buffer.from(data, ...args) 203 | nuv_udp_send(Buffer.alloc(sizeof_uv_udp_send_t), this.handle, data, ip, port) 204 | return this.wait('onSend') 205 | } 206 | 207 | onRecv (err, nread, addr) { 208 | if (this.reader > this.writer) { 209 | const { resolve, reject } = this.queue[this.writer] 210 | delete this.queue[this.writer++] 211 | return err ? reject(err) : resolve({ 212 | data: this.readBuffer.slice(0, nread), 213 | ip: addr.ip, 214 | port: addr.port 215 | }) 216 | } 217 | if (!this.paused) { 218 | this.paused = true 219 | nuv_udp_recv_stop(this.handle) 220 | } 221 | this.queue[this.writer++] = { err, nread, addr } 222 | } 223 | 224 | recv () { 225 | return new Promise((resolve, reject) => { 226 | if (this.writer > this.reader) { 227 | const { err, nread, addr } = this.queue[this.reader] 228 | delete this.queue[this.reader++] 229 | return err ? reject(err) : resolve({ 230 | data: this.readBuffer.slice(0, nread), 231 | ip: addr.ip, 232 | port: addr.port 233 | }) 234 | } 235 | if (this.paused) { 236 | this.paused = false 237 | nuv_udp_recv_start(this.handle) 238 | } 239 | this.queue[this.reader++] = { resolve, reject } 240 | }) 241 | } 242 | 243 | [Symbol.asyncIterator] () { 244 | return { 245 | next: () => new Promise((resolve, reject) => { 246 | this.recv().then( 247 | value => resolve({ value }), 248 | err => reject(err) 249 | ) 250 | }) 251 | } 252 | } 253 | } 254 | 255 | class Timer extends Handle { 256 | constructor (handlers) { 257 | super() 258 | for (const key in handlers) { 259 | this[key] = handlers[key] 260 | } 261 | nuv_timer_init(Buffer.alloc(sizeof_uv_timer_t), this) 262 | } 263 | 264 | start (timeout = 0, repeat = 0) { 265 | nuv_timer_start(this.handle, timeout, repeat) 266 | } 267 | 268 | stop () { 269 | nuv_timer_stop(this.handle) 270 | } 271 | 272 | again () { 273 | nuv_timer_again(this.handle) 274 | } 275 | 276 | get repeat () { 277 | return nuv_timer_get_repeat(this.handle) 278 | } 279 | 280 | set repeat (newRepeat) { 281 | nuv_timer_set_repeat(this.handle, newRepeat) 282 | } 283 | } 284 | 285 | exports.bindings = bindings 286 | exports.Server = Server 287 | exports.Client = Client 288 | exports.Udp = Udp 289 | exports.Timer = Timer 290 | -------------------------------------------------------------------------------- /tests/test-tcp.js: -------------------------------------------------------------------------------- 1 | const { 2 | /* eslint camelcase: 0 */ 3 | sizeof_uv_tcp_t, 4 | sizeof_uv_connect_t, 5 | sizeof_uv_shutdown_t, 6 | sizeof_uv_write_t, 7 | // TCP Functions 8 | nuv_tcp_init, 9 | nuv_tcp_nodelay, 10 | nuv_tcp_keepalive, 11 | nuv_tcp_simultaneous_accepts, 12 | nuv_tcp_bind, 13 | nuv_tcp_getsockname, 14 | nuv_tcp_getpeername, 15 | nuv_tcp_connect, 16 | // Stream Functions 17 | nuv_shutdown, 18 | nuv_listen, 19 | nuv_accept, 20 | nuv_read_start, 21 | nuv_read_stop, 22 | nuv_write, 23 | // Handle Functions 24 | nuv_close 25 | } = require('bindings')('nuv.node') 26 | 27 | let server = {} 28 | nuv_tcp_init(Buffer.alloc(sizeof_uv_tcp_t), server) 29 | 30 | nuv_tcp_simultaneous_accepts(server.handle, true) 31 | nuv_tcp_bind(server.handle, '0.0.0.0', 0) 32 | let addr = nuv_tcp_getsockname(server.handle) 33 | console.log('server-sock', addr) 34 | 35 | server.onConnection = err => { 36 | console.log('on_connection', {err}) 37 | let socket = {} 38 | nuv_tcp_init(Buffer.alloc(sizeof_uv_tcp_t), socket) 39 | nuv_tcp_nodelay(socket.handle, true) 40 | nuv_tcp_keepalive(socket.handle, true, 300) 41 | nuv_accept(server.handle, socket.handle) 42 | console.log('client-sock', nuv_tcp_getsockname(socket.handle)) 43 | console.log('client-peer', nuv_tcp_getpeername(socket.handle)) 44 | socket.readBuffer = Buffer.alloc(1024) 45 | socket.onRead = (nread) => { 46 | console.log('socket.onRead', {nread}) 47 | if (!nread) { 48 | nuv_read_stop(socket.handle) 49 | socket.onClose = () => { 50 | console.log('socket.onClose') 51 | } 52 | nuv_close(socket.handle) 53 | } 54 | } 55 | nuv_read_start(socket.handle) 56 | } 57 | nuv_listen(server.handle, 128) 58 | 59 | let client = {} 60 | nuv_tcp_init(Buffer.alloc(sizeof_uv_tcp_t), client) 61 | client.onConnect = err => { 62 | delete client.onConnect 63 | console.log('client.onConnect', {err}) 64 | nuv_write(Buffer.alloc(sizeof_uv_write_t), client.handle, Buffer.from('Hello World\n')) 65 | } 66 | client.onWrite = err => { 67 | console.log('client.onWrite', {err}) 68 | nuv_shutdown(Buffer.alloc(sizeof_uv_shutdown_t), client.handle) 69 | } 70 | client.onShutdown = err => { 71 | delete client.onShutdown 72 | delete client.onWrite 73 | console.log('client.onShutdown', {err}) 74 | nuv_close(client.handle) 75 | } 76 | client.onClose = () => { 77 | delete client.onClose 78 | delete client.handle 79 | console.log('client.onClose') 80 | nuv_close(server.handle) 81 | } 82 | 83 | nuv_tcp_connect(Buffer.alloc(sizeof_uv_connect_t), client.handle, '127.0.0.1', addr.port) 84 | -------------------------------------------------------------------------------- /tests/test-udp.js: -------------------------------------------------------------------------------- 1 | const { 2 | /* eslint camelcase: 0 */ 3 | sizeof_uv_udp_t, 4 | sizeof_uv_udp_send_t, 5 | nuv_udp_init, 6 | nuv_udp_bind, 7 | nuv_udp_getsockname, 8 | nuv_udp_send, 9 | nuv_udp_recv_start, 10 | nuv_udp_recv_stop, 11 | nuv_close 12 | } = require('bindings')('nuv.node') 13 | 14 | let server = {} 15 | nuv_udp_init(Buffer.alloc(sizeof_uv_udp_t), server) 16 | nuv_udp_bind(server.handle, '0.0.0.0', 0) 17 | console.log(server) 18 | let addr = nuv_udp_getsockname(server.handle) 19 | console.log(addr) 20 | 21 | server.readBuffer = Buffer.alloc(1024) 22 | server.onRecv = (...args) => { 23 | console.log('onRecv', args) 24 | nuv_udp_recv_stop(server.handle) 25 | nuv_close(server.handle) 26 | } 27 | nuv_udp_recv_start(server.handle) 28 | server.onClose = () => { 29 | console.log('Closed') 30 | } 31 | 32 | let client = {} 33 | nuv_udp_init(Buffer.alloc(sizeof_uv_udp_t), client) 34 | client.onSend = (...args) => { 35 | console.log('onSend', args) 36 | } 37 | nuv_udp_send(Buffer.alloc(sizeof_uv_udp_send_t), client.handle, Buffer.from('Hello'), '127.0.0.1', addr.port) 38 | nuv_close(client.handle) 39 | --------------------------------------------------------------------------------