From 5d152b56dc7e661b312059b370b5556284dcc03b Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 6 May 2026 00:47:11 +0200 Subject: [PATCH 1/4] Enhanced dispose methods in various node classes to prevent infinite recursion during disposal in shared graphs. --- .mise.toml | 2 + lib/nodes/adapterNode.js | 10 + lib/nodes/index.js | 4 +- lib/nodes/leftAdapterNode.js | 4 - lib/nodes/node.js | 15 +- lib/nodes/rightAdapterNode.js | 4 - lib/nodes/typeNode.js | 10 +- mise.lock | 13 + package-lock.json | 1984 +++++++++++++++++++++++++++++++++ test/flow/from.test.js | 38 +- test/issues.test.js | 19 +- 11 files changed, 2075 insertions(+), 28 deletions(-) create mode 100644 .mise.toml create mode 100644 mise.lock create mode 100644 package-lock.json diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..e3cc1a4 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +node = "22.22.1" diff --git a/lib/nodes/adapterNode.js b/lib/nodes/adapterNode.js index dbefbab..c47c499 100644 --- a/lib/nodes/adapterNode.js +++ b/lib/nodes/adapterNode.js @@ -29,6 +29,16 @@ Node.extend({ } else { this.__propagateNoPaths(method, context); } + }, + + // Pass `visited` through from RootNode.dispose (see node.js). + dispose: function (context, visited) { + visited = visited || {}; + if (visited[this.__count]) { + return; + } + visited[this.__count] = true; + this.propagateDispose(context, undefined, visited); } } }).as(module); \ No newline at end of file diff --git a/lib/nodes/index.js b/lib/nodes/index.js index 2f4e6ed..0e89f26 100644 --- a/lib/nodes/index.js +++ b/lib/nodes/index.js @@ -96,10 +96,12 @@ declare({ }); }, + // One visited map for the whole dispose walk (cycles / sharing). dispose: function () { + var visited = {}; var typeNodes = this.typeNodes, i = typeNodes.length - 1; for (; i >= 0; i--) { - typeNodes[i].dispose(); + typeNodes[i].dispose(visited); } }, diff --git a/lib/nodes/leftAdapterNode.js b/lib/nodes/leftAdapterNode.js index 1b542c7..0a7aab9 100644 --- a/lib/nodes/leftAdapterNode.js +++ b/lib/nodes/leftAdapterNode.js @@ -22,10 +22,6 @@ Node.extend({ this.__propagate("retractResolve", match); }, - dispose: function (context) { - this.propagateDispose(context); - }, - toString: function () { return "LeftAdapterNode " + this.__count; } diff --git a/lib/nodes/node.js b/lib/nodes/node.js index b486aa2..3f9722b 100644 --- a/lib/nodes/node.js +++ b/lib/nodes/node.js @@ -84,20 +84,27 @@ declare({ } }, - dispose: function (assertable) { - this.propagateDispose(assertable); + // Pass `visited` through from RootNode.dispose. + dispose: function (assertable, visited) { + visited = visited || {}; + if (visited[this.__count]) { + return; + } + visited[this.__count] = true; + this.propagateDispose(assertable, undefined, visited); }, retract: function (assertable) { this.propagateRetract(assertable); }, - propagateDispose: function (assertable, outNodes) { + propagateDispose: function (assertable, outNodes, visited) { + visited = visited || {}; outNodes = outNodes || this.nodes; var entrySet = this.__entrySet, i = entrySet.length - 1; for (; i >= 0; i--) { var entry = entrySet[i], outNode = entry.key; - outNode.dispose(assertable); + outNode.dispose(assertable, visited); } }, diff --git a/lib/nodes/rightAdapterNode.js b/lib/nodes/rightAdapterNode.js index 100570a..40911ad 100644 --- a/lib/nodes/rightAdapterNode.js +++ b/lib/nodes/rightAdapterNode.js @@ -7,10 +7,6 @@ Node.extend({ this.__propagate("retractResolve", match); }, - dispose: function (context) { - this.propagateDispose(context); - }, - propagateAssert: function (context) { this.__propagate("assertRight", context); }, diff --git a/lib/nodes/typeNode.js b/lib/nodes/typeNode.js index dbcf821..a9a39dc 100644 --- a/lib/nodes/typeNode.js +++ b/lib/nodes/typeNode.js @@ -26,11 +26,17 @@ AlphaNode.extend({ return "TypeNode" + this.__count; }, - dispose: function () { + // Root passes visited; optional otherwise. + dispose: function (visited) { + visited = visited || {}; + if (visited[this.__count]) { + return; + } + visited[this.__count] = true; var es = this.__entrySet, i = es.length - 1; for (; i >= 0; i--) { var e = es[i], outNode = e.key, paths = e.value; - outNode.dispose({paths: paths}); + outNode.dispose({paths: paths}, visited); } }, diff --git a/mise.lock b/mise.lock new file mode 100644 index 0000000..3ad85d8 --- /dev/null +++ b/mise.lock @@ -0,0 +1,13 @@ +# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html + +[[tools.node]] +version = "22.22.1" +backend = "core:node" + +[tools.node."platforms.linux-x64"] +checksum = "sha256:07c8aafa60644fb81adefa1ee7da860eb1920851ffdc9a37020ab0be47fbc10e" +url = "https://nodejs.org/dist/v22.22.1/node-v22.22.1-linux-x64.tar.gz" + +[tools.node."platforms.macos-arm64"] +checksum = "sha256:679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2" +url = "https://nodejs.org/dist/v22.22.1/node-v22.22.1-darwin-arm64.tar.gz" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..08c76bc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1984 @@ +{ + "name": "nools", + "version": "0.4.4-immuta2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nools", + "version": "0.4.4-immuta2", + "dependencies": { + "arguments-extended": "~0.0.3", + "array-extended": "~0.0.4", + "commander": "^1.1.1", + "date-extended": "~0.0.3", + "declare.js": "~0.0.3", + "extended": "~0.0.3", + "function-extended": "~0.0.3", + "ht": "~0.0.2", + "is-extended": "~0.0.4", + "leafy": "~0.0.3", + "object-extended": "~0.0.3", + "promise-extended": "~0.0.3", + "string-extended": "~0.0.3", + "uglify-js": "^3.14.3" + }, + "bin": { + "nools": "bin/nools" + }, + "devDependencies": { + "coddoc": "0.0.3", + "grunt": "~0.4.1", + "grunt-browserify": "1.2.4", + "grunt-contrib-jshint": "~0.5.3", + "grunt-contrib-uglify": "~0.2.0", + "grunt-exec": "^0.4.5", + "grunt-it": "~0.3.0", + "it": "~0.2.0", + "jison": "~0.4.17" + }, + "engines": { + "node": ">= 0.6.1" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "license": "BSD-3-Clause OR MIT", + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "integrity": "sha512-LjmC2dNpdn2L4UzyoaIr11ELYoLn37ZFy9zObrQFHsSuOepeUEMKnM8w5KL4Tnrp2gy88rRuQt6Ky8Bjml+Baw==", + "dev": true, + "license": "MIT", + "dependencies": { + "underscore": "~1.7.0", + "underscore.string": "~2.4.0" + } + }, + "node_modules/argparse/node_modules/underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", + "integrity": "sha512-yxkabuCaIBnzfIvX3kBxQqCs0ar/bfJwDnFEHJUm/ZrRVhT3IItdRF5cZjARLzEnyQYtIUhsZ2LG2j3HidFOFQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/arguments-extended": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/arguments-extended/-/arguments-extended-0.0.3.tgz", + "integrity": "sha512-MNYdPKgCiywbgHAmNsYr1tSNLtfbSdwE1akZV+33hU9A8RG0lO5HAK9oMnw7y7bjYUhc04dJpcIBMUaPPYYtXg==", + "license": "MIT", + "dependencies": { + "extended": "~0.0.3", + "is-extended": "~0.0.8" + } + }, + "node_modules/array-extended": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/array-extended/-/array-extended-0.0.11.tgz", + "integrity": "sha512-Fe4Ti2YgM1onQgrcCD8dUhFuZgHQxzqylSl1C5IDJVVVqY5D07h8RghIXL9sZ6COZ0e+oTL5IusTv5eXABJ9Kw==", + "license": "MIT", + "dependencies": { + "arguments-extended": "~0.0.3", + "extended": "~0.0.3", + "is-extended": "~0.0.3" + } + }, + "node_modules/astw": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/astw/-/astw-0.0.0.tgz", + "integrity": "sha512-1U7z5mTBIkFRMXG+ujTN9dPRVIb+LPk18Du32JLP7gdZqtDILyVQvyflb7BNJf4R5GQxNPcJl45A2vs6Ouu+3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "1.0.2" + } + }, + "node_modules/astw/node_modules/esprima": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.2.tgz", + "integrity": "sha512-j2ZAN1Cm/rgsIEHgNa6eqvZjEtqFh8WEUdVswEpxj03AyzR1R6CHfunOpd9NOmLg0U18aAO2FunAjs49f5w0Yg==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/Base64": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.1.4.tgz", + "integrity": "sha512-rHuyFTuK3/IH8tMcDB0BVrfpVM8+YQ7XSsICoYUu+JUUjnbbSyPfHok/NWLFgUbKt8sPeOIEFX6YJJTO2vgt7w==", + "dev": true + }, + "node_modules/base64-js": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.2.tgz", + "integrity": "sha512-Pj9L87dCdGcKlSqPVUjD+q96pbIx1zQQLb2CUiWURfjiBELv84YX+0nGnKmyT/9KkC7PQk7UN1w+Al8bBozaxQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bops": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/bops/-/bops-0.0.6.tgz", + "integrity": "sha512-EWD8/Ei9o/h/wmR3w/YL/8dGKe4rSFHlaO8VNNcuXnjXjeTgxdcmhjPf9hRCYlqTrBPZbKaht+FxZKahcob5UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "0.0.2", + "to-utf8": "0.0.1" + } + }, + "node_modules/browser-builtins": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/browser-builtins/-/browser-builtins-1.0.7.tgz", + "integrity": "sha512-n0WKhmaH3mb4ywJDk5FdAiMzTZgjFR6Zl0fhVs0E+FiJQHjyH27EUy2c7FstA2GDGNDIvS93bgFxyzd2e0Cwwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-browserify": "0.1.x", + "console-browserify": "0.1.x", + "constants-browserify": "0.0.x", + "crypto-browserify": "1.0.x", + "http-browserify": "0.1.x", + "punycode": "1.2.x", + "resolve": "0.3.x", + "vm-browserify": "0.0.x", + "zlib-browserify": "0.0.x" + } + }, + "node_modules/browser-pack": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-0.10.2.tgz", + "integrity": "sha512-z60w7N4KAX4W0FdKM0aEIla1aD7kTZGtQwJWUjUG1BrcrnkpnqY4/zYcMjd0g5SfEWNeuHiCAMW3OEYR7asrsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "combine-source-map": "~0.1.1", + "JSONStream": "~0.6.4", + "through": "~2.3.4" + }, + "bin": { + "browser-pack": "bin/cmd.js" + } + }, + "node_modules/browser-resolve": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.1.4.tgz", + "integrity": "sha512-Rg/mZ9Op2d/fASmfJ02htvkyPSeNBEpnJxmyntlu7t+Bub2nC64dOrWb9S0Zzo10p8wQPVAYkAgzCbNQHnqCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "0.5.1" + } + }, + "node_modules/browser-resolve/node_modules/resolve": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.5.1.tgz", + "integrity": "sha512-PgoPtxVz3j45jqtNbMbxcBG+5FhjLLa425zzNBf50//c4XJDx/FC0fbAWJiVPsXOV/MLhbQslSYuEv6RFf7p3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-2.27.1.tgz", + "integrity": "sha512-a2rV9fJ/0B3JzOK+RQejXYOFuBXl96HJR51M7DBPzuFl1P3YOYPmHEBAptX1Ea33t6ZwK1348GzcJzpPdgsIeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-builtins": "~1.0.1", + "browser-pack": "~0.10.0", + "browser-resolve": "~1.1.0", + "concat-stream": "~1.0.0", + "deps-sort": "~0.1.1", + "duplexer": "~0.1.1", + "event-stream": "~3.0.15", + "inherits": "~1.0.0", + "insert-module-globals": "~1.2.0", + "JSONStream": "~0.6.4", + "module-deps": "~1.0.2", + "optimist": "~0.5.1", + "parents": "~0.0.1", + "shell-quote": "~0.0.1", + "syntax-error": "~0.0.0", + "through": "~2.3.4", + "umd": "~1.1.0" + }, + "bin": { + "browserify": "bin/cmd.js" + } + }, + "node_modules/browserify-shim": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/browserify-shim/-/browserify-shim-2.0.10.tgz", + "integrity": "sha512-FM0V6Rxf2enBVLu/LRSo7h8g0tANHYMd555z2w1VTp5lgofxpCi9h9vOIGlMXw6mHhuLTHHwqGWezQPyWKXetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "~2.3.4" + }, + "peerDependencies": { + "browserify": ">= 2.3.0 < 4" + } + }, + "node_modules/buffer-browserify": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/buffer-browserify/-/buffer-browserify-0.1.0.tgz", + "integrity": "sha512-X/ElZKujhD75e83Y8ZkYOx42ryvdsGgsL4dnyPu6/94xSb9sd/dswpYITDrDPzvbUJgFFuc4eFKQ6VMdsrYEDg==", + "deprecated": "Package not maintained. Recent browserify uses https://github.com/feross/buffer", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "base64-js": "0.0.2" + } + }, + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cjson": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/cjson/-/cjson-0.3.0.tgz", + "integrity": "sha512-bBRQcCIHzI1IVH59fR0bwGrFmi3Btb/JNwM/n401i1DnYgWndpsUBiQRAddLflkZage20A2d25OAWZZk0vBRlA==", + "dev": true, + "dependencies": { + "jsonlint": "1.6.0" + }, + "engines": { + "node": ">= 0.3.0" + } + }, + "node_modules/cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "integrity": "sha512-dbn5HyeJWSOU58RwOEiF1VWrl7HRvDsKLpu0uiI/vExH6iNoyUzjB5Mr3IJY5DVUfnbpe9793xw4DFJVzC9nWQ==", + "dev": true, + "dependencies": { + "glob": ">= 3.1.4" + }, + "engines": { + "node": ">=0.2.5" + } + }, + "node_modules/coddoc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/coddoc/-/coddoc-0.0.3.tgz", + "integrity": "sha512-JbEhW32QfRh4SXHhSwKB/imJ3Id2GICrvIetsms1s72B1rIXzESChWZ3PDcGcvjEpfDdV6c02dnImNTViDCZVA==", + "dev": true, + "dependencies": { + "commander": ">=0.5.1", + "handlebars": "~1.0.10", + "marked": "~0.2.8", + "wrench": "~1.4.4" + }, + "bin": { + "coddoc": "bin/coddoc" + }, + "engines": { + "node": ">= 0.6.1" + } + }, + "node_modules/coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", + "integrity": "sha512-QjQ1T4BqyHv19k6XSfdhy/QLlIOhywz0ekBUCa9h71zYMJlfDTGan/Z1JXzYkZ6v8R+GhvL/p4FZPbPW8WNXlg==", + "deprecated": "CoffeeScript on NPM has moved to \"coffeescript\" (no hyphen)", + "dev": true, + "bin": { + "cake": "bin/cake", + "coffee": "bin/coffee" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combine-source-map": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.1.3.tgz", + "integrity": "sha512-X/YyrdPhB7vomP32FuThG9SbTdv/7MrYLs4uZAsYgs6jcgvCTWMsVEcKt2zSDX0qSaeZUDZtAwhvdz1NC6zg2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-source-map": "~0.2.3", + "inline-source-map": "~0.2.1", + "parse-base64vlq-mappings": "~0.1.1" + } + }, + "node_modules/commander": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-1.3.2.tgz", + "integrity": "sha512-uoVVA5dchmxZeTMv2Qsd0vhn/RebJYsWo4all1qtrUL3BBhQFn4AQDF4PL+ZvOeK7gczXKEZaSCyMDMwFBlpBg==", + "dependencies": { + "keypress": "0.1.x" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/commondir": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-0.0.2.tgz", + "integrity": "sha512-s4oS0jIUZ1t1LJCN7MI2cEDEd25cCSEW0vluKuDEsYcR0KGIiX7HmTtPMbxqGlaqGaihsGlmko1P3vNWzV7cOg==", + "dev": true, + "license": "MIT/X11" + }, + "node_modules/concat-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.0.1.tgz", + "integrity": "sha512-nAHFsgeRVVvZ+aB3S1gLeN73fQ+tdOcw075BHbXMbC6MY0h6nqAkEeqPVCw8kRuDJJZDvaUjxI4jZv2FD0Tl8A==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "BSD", + "dependencies": { + "bops": "0.0.6" + } + }, + "node_modules/console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz", + "integrity": "sha512-FJahZyF+dLKrC7h4DOq5JsHA+f0cLJD3TR1+0CK3n6phtdrVAPsZZKq+PZRmo2RYSOHvvs8kNhU4uRiSZUbSbA==", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-0.0.1.tgz", + "integrity": "sha512-FL+diDS9AKR5BAA2M+GNk8lnH64tRE3zepTG9hucxc7o04LgCRhkQZhF7u/OKHZT8LLRT+sZEi9qFzXUchq9pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.2.6.tgz", + "integrity": "sha512-FdRIx8lQ7clo1L19NtZ1ldvK6pxX5+JTuj3uIm1um3NTuXunJWEIf2XjEm1oqsYratjNHlCrzT/TraGwLBZjjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/crypto-browserify": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", + "integrity": "sha512-fWmkaZPmccreTmANMdpvI0UrF34pzTAZDLKDcF0n5ThwpyeAs+DtSVxyhrZc6kHFiOFdyzjW5uZ8jAWE3kNY6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/date-extended": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/date-extended/-/date-extended-0.0.6.tgz", + "integrity": "sha512-v9a2QLTVn1GQGXf02TQaSvNfeXA/V1FL2Tr0OQYqjI5+L9T5jEtCpLYG01sxFk+m1OtwMxydkKa8NKcflANAoQ==", + "license": "MIT", + "dependencies": { + "array-extended": "~0.0.3", + "extended": "~0.0.3", + "is-extended": "~0.0.3" + } + }, + "node_modules/dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", + "integrity": "sha512-AXvW8g7tO4ilk5HgOWeDmPi/ZPaCnMJ+9Cg1I3p19w6mcvAAXBuuGEXAxybC+Djj1PSZUiHUcyoYu7WneCX8gQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/declare.js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/declare.js/-/declare.js-0.0.8.tgz", + "integrity": "sha512-O659hy1gcHef7JnwtqdQlrj2c5DAEgtxm8pgFXofW7eUE1L4FjsSLlziovWcrOJAOFlEPaOJshY+0hBWCG/AnA==", + "license": "MIT" + }, + "node_modules/deep-equal": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.0.0.tgz", + "integrity": "sha512-p1bI/kkDPT6auUI0U+WLuIIrzmDIDo80I406J8tT4y6I4ZGtBuMeTudrKDtBdMJFAcxqrQdx27gosqPVyY3IvQ==", + "dev": true, + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, + "node_modules/defined": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", + "integrity": "sha512-zpqiCT8bODLu3QSmLLic8xJnYWBFjOSu/fBCm189oAiTtPq/PSanNACKZDS7kgSyCJY7P+IcODzlIogBK/9RBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deps-sort": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-0.1.2.tgz", + "integrity": "sha512-bF5sJp2YeGQAx+vI3KBQwn6wHHyuCcsrPS0qvqnNLgGF1NrjhdvopP3exfdLLKaFtS6V5K/CMjQLtzR7C3Wa6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "JSONStream": "~0.6.4", + "minimist": "~0.0.1", + "through": "~2.3.4" + }, + "bin": { + "deps-sort": "bin/cmd.js" + } + }, + "node_modules/detective": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detective/-/detective-2.1.2.tgz", + "integrity": "sha512-hedmjOYPLbb9/WS8D8v1yRCMGnAvGBu8gQwyzODnpQ0PjvLybHpq4iiz3BQQ8UjvodC3r1qryOuo32PZgoSS8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "escodegen": "0.0.15", + "esprima": "1.0.2" + } + }, + "node_modules/detective/node_modules/escodegen": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.15.tgz", + "integrity": "sha512-ALdTVKqhEj6bDf4pC0fXWRiUvhyJSJ0Ic8AdPuMGKckoa3gMXG+MPduI8TlDoSS84swDkfXeogXapbkErAHvDA==", + "dev": true, + "dependencies": { + "esprima": ">= 1.0.0" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.4.0" + }, + "optionalDependencies": { + "source-map": ">= 0.1.2" + } + }, + "node_modules/detective/node_modules/esprima": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.2.tgz", + "integrity": "sha512-j2ZAN1Cm/rgsIEHgNa6eqvZjEtqFh8WEUdVswEpxj03AyzR1R6CHfunOpd9NOmLg0U18aAO2FunAjs49f5w0Yg==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ebnf-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz", + "integrity": "sha512-urvSxVQ6XJcoTpc+/x2pWhhuOX4aljCNQpwzw+ifZvV1andZkAmiJc3Rq1oGEAQmcjiLceyMXOy1l8ms8qs2fQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escodegen": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", + "integrity": "sha512-z9FWgKc48wjMlpzF5ymKS1AF8OIgnKLp9VyN7KbdtyrP/9lndwUFqCtMm+TAJmJf7KJFFYc4cFJfVTTGkKEwsA==", + "dev": true, + "dependencies": { + "esprima": "~1.1.1", + "estraverse": "~1.5.0", + "esutils": "~1.0.0" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.10.0" + }, + "optionalDependencies": { + "source-map": "~0.1.33" + } + }, + "node_modules/esprima": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz", + "integrity": "sha512-qxxB994/7NtERxgXdFgLHIs9M6bhLXc6qtUmWZ3L8+gTQ9qaoyki2887P2IqAYsoENyr8SUbTutStDniOHSDHg==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-stream": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.0.20.tgz", + "integrity": "sha512-u2P2ZUf2wN0LBDY6gDNMH+GvLer2Fpb0IAw7i6abjy8HLZnhdobM10Qohn86mwTKy3lcV9VuUms0aU9kpfBrow==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.0.3", + "pause-stream": "0.0.11", + "split": "0.2", + "stream-combiner": "~0.0.3", + "through": "~2.3.1" + } + }, + "node_modules/eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/extended": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/extended/-/extended-0.0.6.tgz", + "integrity": "sha512-rvAV3BDGsV1SYGzUOu7aO0k82quhfl0QAyZudYhAcTeIr1rPbBnyOhOlkCLwLpDfP7HyKAWAPNSjRb9p7lE3rg==", + "license": "MIT", + "dependencies": { + "extender": "~0.0.5" + } + }, + "node_modules/extender": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/extender/-/extender-0.0.10.tgz", + "integrity": "sha512-iPLUHZJaNW6RuOShQX33ZpewEUIlijFBcsXnKWyiYERKWPsFxfKgx8J0xRz29hKQWPFFPACgBW6cHM7Ke1pfaA==", + "license": "MIT", + "dependencies": { + "declare.js": "~0.0.4" + } + }, + "node_modules/findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "integrity": "sha512-yjftfYnF4ThYEvKEV/kEFR15dmtyXTAh3vQnzpJUoc7Naj5y1P0Ck7Zs1+Vroa00E3KT3IYsk756S+8WA5dNLw==", + "dev": true, + "dependencies": { + "glob": "~3.2.9", + "lodash": "~2.4.1" + }, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/findup-sync/node_modules/glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha512-hVb0zwEZwC1FXSKRPFTeOtN7AArJcJlI6ULGLtrstaswKNlrTJqAA+1lYlSUop4vjA423xlBzqfVS3iWGlqJ+g==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BSD", + "dependencies": { + "inherits": "2", + "minimatch": "0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/findup-sync/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/findup-sync/node_modules/lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha512-Kak1hi6/hYHGVPmdyiZijoQyz5x2iGVzs6w9GYB/HiXEtylY7tIoYEROMjvM1d9nXJqPOrG2MNPMn01bJ+S0Rw==", + "dev": true, + "engines": [ + "node", + "rhino" + ], + "license": "MIT" + }, + "node_modules/findup-sync/node_modules/minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha512-WFX1jI1AaxNTZVOHLBVazwTWKaQjoykSzCBNXB72vDTCzopQGtyP91tKdFK5cv1+qMwPyiTu1HqUriqplI8pcA==", + "deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "2", + "sigmund": "~1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true, + "license": "MIT" + }, + "node_modules/function-extended": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/function-extended/-/function-extended-0.0.9.tgz", + "integrity": "sha512-Mg9A/TbQzGh6kzHSOTaNVD1Ox9hHDl2PUEPj7Kl3z/HvnW8Q28aqDGTzcyxT96wPQJOtLdgJwmMp887msgeKOg==", + "license": "MIT", + "dependencies": { + "arguments-extended": "~0.0.3", + "extended": "~0.0.3", + "is-extended": "~0.0.3" + } + }, + "node_modules/getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha512-hIGEBfnHcZpWkXPsAVeVmpYDvfy/matVl03yOY91FPmnpCC12Lm5izNxCjO3lHAeO6uaTwMxu7g450Siknlhig==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha512-ANhy2V2+tFpRajE3wN4DhkNQ08KDr0Ir1qL12/cUe5+a7STEK8jkW4onUYuY8/06qAFuT5je7mjAqzx0eKI2tQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BSD", + "dependencies": { + "graceful-fs": "~1.2.0", + "inherits": "1", + "minimatch": "~0.2.11" + }, + "engines": { + "node": "*" + } + }, + "node_modules/graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha512-iiTUZ5vZ+2ZV+h71XAgwCSu6+NAizhFU3Yw8aC/hH5SQ3SnISqEqAek40imAFGtDcwJKNhXvSY+hzIolnLwcdQ==", + "deprecated": "please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js", + "dev": true, + "license": "BSD", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "integrity": "sha512-1iq3ylLjzXqz/KSq1OAE2qhnpcbkF2WyhsQcavZt+YmgvHu0EbPMEhGhy2gr0FP67isHpRdfwjB5WVeXXcJemQ==", + "dev": true, + "dependencies": { + "async": "~0.1.22", + "coffee-script": "~1.3.3", + "colors": "~0.6.2", + "dateformat": "1.0.2-1.2.3", + "eventemitter2": "~0.4.13", + "exit": "~0.1.1", + "findup-sync": "~0.1.2", + "getobject": "~0.1.0", + "glob": "~3.1.21", + "grunt-legacy-log": "~0.1.0", + "grunt-legacy-util": "~0.2.0", + "hooker": "~0.2.3", + "iconv-lite": "~0.2.11", + "js-yaml": "~2.0.5", + "lodash": "~0.9.2", + "minimatch": "~0.2.12", + "nopt": "~1.0.10", + "rimraf": "~2.2.8", + "underscore.string": "~2.2.1", + "which": "~1.0.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/grunt-browserify": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/grunt-browserify/-/grunt-browserify-1.2.4.tgz", + "integrity": "sha512-rRkM3QElYALBde+a18O3kfrVomld83hKJzEot9tiyPU0KoWN8lNLNDN5tU94yTuF0mBejQwm4CKPY6ORfP+tyw==", + "dev": true, + "dependencies": { + "browserify": "~2.27.0", + "browserify-shim": "~2.0.8" + }, + "engines": { + "node": ">= 0.8.x" + }, + "peerDependencies": { + "grunt": "~0.4.0" + } + }, + "node_modules/grunt-contrib-jshint": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.5.4.tgz", + "integrity": "sha512-Ad55MgNBEl392hPKWTzXjgXXYE/6n8wMl7vAEo2ZjCB2hv6B0T04mPlrfvPfxa9j//psMP6WBgVujZCqDYcFHA==", + "dev": true, + "dependencies": { + "jshint": "~2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + }, + "peerDependencies": { + "grunt": "~0.4.0" + } + }, + "node_modules/grunt-contrib-uglify": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-0.2.7.tgz", + "integrity": "sha512-KXKM2UNLsCiUI6/DYfAIPm3i26UJJN6Cf6KD8fFa2TKllj7yLPC853IxtWBJ/3jX66QtXHGtdCORuuA6sAFvvA==", + "dev": true, + "dependencies": { + "grunt-lib-contrib": "~0.6.1", + "uglify-js": "~2.4.0" + }, + "engines": { + "node": ">= 0.8.0" + }, + "peerDependencies": { + "grunt": "~0.4.0" + } + }, + "node_modules/grunt-contrib-uglify/node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", + "dev": true + }, + "node_modules/grunt-contrib-uglify/node_modules/source-map": { + "version": "0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "integrity": "sha512-yfCwDj0vR9RTwt3pEzglgb3ZgmcXHt6DjG3bjJvzPwTL+5zDQ2MhmSzAcTy0GTiQuCiriSWXvWM1/NhKdXuoQA==", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/grunt-contrib-uglify/node_modules/uglify-js": { + "version": "2.4.24", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz", + "integrity": "sha512-tktIjwackfZLd893KGJmXc1hrRHH1vH9Po3xFh1XBjjeGAnN02xJ3SuoA+n1L29/ZaCA18KzCFlckS+vfPugiA==", + "dev": true, + "license": "BSD", + "dependencies": { + "async": "~0.2.6", + "source-map": "0.1.34", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.5.4" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/grunt-exec": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.7.tgz", + "integrity": "sha512-AJw+ccLkJnycdyiTXFpt1ELpRbiMDn0uj3b04W0kmmjEZCz3j1LSO2bH9GxgQniUZc2mQFMEew9Bw9tWCu+1zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "grunt": ">=0.4" + } + }, + "node_modules/grunt-it": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/grunt-it/-/grunt-it-0.3.1.tgz", + "integrity": "sha512-YDvG0Uwcj+7HamJ36KreIem0Pv2dTFQHcbZp4gFCXMAkhUBAcoClKD4qp7oHr9YKVTCOAgRls37/ZYQRUUe/xQ==", + "dev": true, + "bin": { + "grunt-it": "bin/grunt-it" + }, + "engines": { + "node": "*" + }, + "peerDependencies": { + "it": "~0.2.0" + } + }, + "node_modules/grunt-legacy-log": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", + "integrity": "sha512-qYs/uM0ImdzwIXLhS4O5WLV5soAM+PEqqHI/hzSxlo450ERSccEhnXqoeDA9ZozOdaWuYnzTOTwRcVRogleMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "~0.6.2", + "grunt-legacy-log-utils": "~0.1.1", + "hooker": "~0.2.3", + "lodash": "~2.4.1", + "underscore.string": "~2.3.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/grunt-legacy-log-utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", + "integrity": "sha512-D0vbUX00TFYCKNZtcZzemMpwT8TR/FdRs1pmfiBw6qnUw80PfsjV+lhIozY/3eJ3PSG2zj89wd2mH/7f4tNAlw==", + "dev": true, + "dependencies": { + "colors": "~0.6.2", + "lodash": "~2.4.1", + "underscore.string": "~2.3.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/grunt-legacy-log-utils/node_modules/lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha512-Kak1hi6/hYHGVPmdyiZijoQyz5x2iGVzs6w9GYB/HiXEtylY7tIoYEROMjvM1d9nXJqPOrG2MNPMn01bJ+S0Rw==", + "dev": true, + "engines": [ + "node", + "rhino" + ], + "license": "MIT" + }, + "node_modules/grunt-legacy-log-utils/node_modules/underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha512-hbD5MibthuDAu4yA5wxes5bzFgqd3PpBJuClbRxaNddxfdsz+qf+1kHwrGQFrmchmDHb9iNU+6EHDn8uj0xDJg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/grunt-legacy-log/node_modules/lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha512-Kak1hi6/hYHGVPmdyiZijoQyz5x2iGVzs6w9GYB/HiXEtylY7tIoYEROMjvM1d9nXJqPOrG2MNPMn01bJ+S0Rw==", + "dev": true, + "engines": [ + "node", + "rhino" + ], + "license": "MIT" + }, + "node_modules/grunt-legacy-log/node_modules/underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha512-hbD5MibthuDAu4yA5wxes5bzFgqd3PpBJuClbRxaNddxfdsz+qf+1kHwrGQFrmchmDHb9iNU+6EHDn8uj0xDJg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", + "integrity": "sha512-cXPbfF8aM+pvveQeN1K872D5fRm30xfJWZiS63Y8W8oyIPLClCsmI8bW96Txqzac9cyL4lRqEBhbhJ3n5EzUUQ==", + "dev": true, + "dependencies": { + "async": "~0.1.22", + "exit": "~0.1.1", + "getobject": "~0.1.0", + "hooker": "~0.2.3", + "lodash": "~0.9.2", + "underscore.string": "~2.2.1", + "which": "~1.0.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/grunt-lib-contrib": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/grunt-lib-contrib/-/grunt-lib-contrib-0.6.1.tgz", + "integrity": "sha512-HdCtJuMmmkSAVrAfsG7lZWE0YabrsPWwzcCCUgWQOAaQsQSUNhw/IwD2YjCSLh5y9NXSPzHTYFLL4ro7QbAJMA==", + "dev": true, + "dependencies": { + "zlib-browserify": "0.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/grunt-lib-contrib/node_modules/zlib-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz", + "integrity": "sha512-fheIDCKXU0YAGZMv4FFwVTBMQRSv2ZjNqRN1VkZjetZDK/BC/hViEhasTh0kTeogcsIAl5gYE04GN53trT+cFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-1.0.12.tgz", + "integrity": "sha512-lI6MusMTp9cP1nI2cbSKxAWW6mTkuBqGD3RLHaTBfUaDCFtyJ1RMcEunXiGrlsEObuOQ1oruzaI0i8PqEbu1/Q==", + "dev": true, + "dependencies": { + "optimist": "~0.3", + "uglify-js": "~2.3" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + } + }, + "node_modules/handlebars/node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", + "dev": true + }, + "node_modules/handlebars/node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/handlebars/node_modules/uglify-js": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.3.6.tgz", + "integrity": "sha512-T2LWWydxf5+Btpb0S/Gg/yKFmYjnX9jtQ4mdN9YRq73BhN21EhU0Dvw3wYDLqd3TooGUJlCKf3Gfyjjy/RTcWA==", + "dev": true, + "dependencies": { + "async": "~0.2.6", + "optimist": "~0.3.5", + "source-map": "~0.1.7" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ht": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ht/-/ht-0.0.2.tgz", + "integrity": "sha512-eYULiMGWs9kEz7xPeC+NP97maMi/k8uPd9Xqbd7B0JDVQkIfAlqudsYb5NP5AopG5QqPbW/H51Xv/5l8xBpfnA==", + "license": "MIT", + "dependencies": { + "array-extended": "~0.0.3", + "declare.js": "~0.0.3", + "extended": "~0.0.3", + "is-extended": "~0.0.3" + } + }, + "node_modules/http-browserify": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-0.1.14.tgz", + "integrity": "sha512-q9Gd3S/dVbLBhRsEQh3R7/Y1aHUvdqdfgHWt/QQRwInX5Cji6bCQhD5yXbcV2c03bApRdYpTDQ0XZSIR2o7nWg==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "Base64": "~0.1.2", + "concat-stream": "~1.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha512-KhmFWgaQZY83Cbhi+ADInoUQ8Etn6BG5fikM9syeOjQltvR45h7cRKJ/9uvQEuD61I3Uju77yYce0/LhKVClQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==", + "dev": true + }, + "node_modules/inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha512-Al67oatbRSo3RV5hRqIoln6Y5yMVbJSIn4jEJNL7VCImzq/kLr7vvb6sFRJXqr8rpHc/2kJOM+y0sPKN47VdzA==", + "dev": true + }, + "node_modules/inline-source-map": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.2.5.tgz", + "integrity": "sha512-t4ZmlgEk83uozBYdjfoqG/IWMI/PzjdaLonqhCSYVBoFrgPU6CuBl8w5x7n/YUkjhdSftwB+41vydc8T8Qocwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.1.25" + } + }, + "node_modules/insert-module-globals": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-1.2.1.tgz", + "integrity": "sha512-fGdZuN+z9b9hMjOTHePW76k9b4skrAlO47jlmCDTUxx/DjBGbDu3v+yxb9O4A8H+zIc4bRhYc8Wt8mHMd7XPow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "~0.0.1", + "duplexer": "~0.0.3", + "JSONStream": "~0.4.3", + "lexical-scope": "~0.0.5", + "process": "~0.5.1", + "through": "~2.2.0" + }, + "bin": { + "insert-module-globals": "bin/cmd.js" + } + }, + "node_modules/insert-module-globals/node_modules/duplexer": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.0.4.tgz", + "integrity": "sha512-nO0WWuIDTde3CWK/8IPpG50dyhUilgpsqzYSIP+w20Yh+4iDgb/2Gs75QItcp0Hmx/JtxtTXBalj+LSTD1VemA==", + "dev": true + }, + "node_modules/insert-module-globals/node_modules/JSONStream": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.4.4.tgz", + "integrity": "sha512-0PZdD2nQCa76Q+So8SX6au7v19Bp4veBTqD91kqfJiYPhG0uwumVmz0PBcwFfXDeVlN7JjlgHbAZejUvfUBzBQ==", + "dev": true, + "dependencies": { + "jsonparse": "0.0.5" + }, + "engines": { + "node": "*" + } + }, + "node_modules/insert-module-globals/node_modules/through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha512-JIR0m0ybkmTcR8URann+HbwKmodP+OE8UCbsifQDYMLD5J3em1Cdn3MYPpbEd5elGDwmP98T+WbqP/tvzA5Mjg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extended": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/is-extended/-/is-extended-0.0.10.tgz", + "integrity": "sha512-qp+HR+L9QXbgFurvqiVgD+JiGyUboRgICNzCXmbiLtZBFVSNFbxRsI4q7Be9mCWTO5PoO1IxoWp5sl+j5b83FA==", + "license": "MIT", + "dependencies": { + "extended": "~0.0.3" + } + }, + "node_modules/it": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/it/-/it-0.2.7.tgz", + "integrity": "sha512-Yw8TSn8KONlxl4GjHga90CuOqvbsSh54orh6pvZ9RvDZhzqsj9SaO+fqttt3hpXxNBaaNf5mdckQdRHpsOcWTw==", + "dev": true, + "dependencies": { + "array-extended": "~0.0.3", + "commander": "~1.1.1", + "date-extended": "~0.0.3", + "declare.js": "~0.0.3", + "extended": "~0.0.3", + "function-extended": "~0.0.3", + "glob": "~3.1.21", + "grunt": "~0.4.1", + "is-extended": "~0.0.3", + "object-extended": "~0.0.3", + "promise-extended": "~0.0.3", + "string-extended": "~0.0.3" + }, + "bin": { + "it": "bin/it" + }, + "engines": { + "node": ">= 0.6.1" + } + }, + "node_modules/it/node_modules/commander": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-1.1.1.tgz", + "integrity": "sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==", + "dev": true, + "dependencies": { + "keypress": "0.1.x" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/jison": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/jison/-/jison-0.4.18.tgz", + "integrity": "sha512-FKkCiJvozgC7VTHhMJ00a0/IApSxhlGsFIshLW6trWJ8ONX2TQJBBz6DlcO1Gffy4w9LT+uL+PA+CVnUSJMF7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cjson": "0.3.0", + "ebnf-parser": "0.1.10", + "escodegen": "1.3.x", + "esprima": "1.1.x", + "jison-lex": "0.3.x", + "JSONSelect": "0.4.0", + "lex-parser": "~0.1.3", + "nomnom": "1.5.2" + }, + "bin": { + "jison": "lib/cli.js" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/jison-lex": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/jison-lex/-/jison-lex-0.3.4.tgz", + "integrity": "sha512-EBh5wrXhls1cUwROd5DcDHR1sG7CdsCFSqY1027+YA1RGxz+BX2TDLAhdsQf40YEtFDGoiO0Qm8PpnBl2EzDJw==", + "dev": true, + "dependencies": { + "lex-parser": "0.1.x", + "nomnom": "1.5.2" + }, + "bin": { + "jison-lex": "cli.js" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "integrity": "sha512-VEKcIksckDBUhg2JS874xVouiPkywVUh4yyUmLCDe1Zg3bCd6M+F1eGPenPeHLc2XC8pp9G8bsuofK0NeEqRkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "~ 0.1.11", + "esprima": "~ 1.0.2" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + }, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jshint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.0.1.tgz", + "integrity": "sha512-omDT+7QnLYmqklBtr8CSRtcT8/Ze6wKUcChTJXCnpEUYWDWcUuel40P/PT48q+uxQTxRJGzsuEic5SMuVrD3Ow==", + "dev": true, + "dependencies": { + "cli": "0.4.x", + "console-browserify": "0.1.x", + "minimatch": "0.x.x", + "shelljs": "0.1.x", + "underscore": "1.4.x" + }, + "bin": { + "jshint": "bin/jshint" + } + }, + "node_modules/jshint/node_modules/underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ==", + "dev": true + }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "license": "Public Domain", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jsonlint": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.0.tgz", + "integrity": "sha512-x6YLBe6NjdpmIeiklwQOxsZuYj/SOWkT33GlTpaG1UdFGjdWjPcxJ1CWZAX3wA7tarz8E2YHF6KiW5HTapPlXw==", + "dev": true, + "dependencies": { + "JSV": ">= 4.0.x", + "nomnom": ">= 1.5.x" + }, + "bin": { + "jsonlint": "lib/cli.js" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha512-fw7Q/8gFR8iSekUi9I+HqWIap6mywuoe7hQIg3buTVjuZgALKj4HAmm0X6f+TaL4c9NJbvyFQdaI2ppr5p6dnQ==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONSelect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz", + "integrity": "sha512-VRLR3Su35MH+XV2lrvh9O7qWoug/TUyj9tLDjn9rtpUCNnILLrHjgd/tB0KrhugCxUpj3UqoLqfYb3fLJdIQQQ==", + "dev": true, + "engines": { + "node": ">=0.4.7" + } + }, + "node_modules/JSONStream": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.6.4.tgz", + "integrity": "sha512-ER8YVJ+Xk4a1g+d8Xq9RFe2rjsUHV9eSRqfwe9DS5J5ga8bKWx4FwXZNWXpGDYchuOfqf4NFmDlwuloqHIj/5A==", + "dev": true, + "dependencies": { + "jsonparse": "0.0.5", + "through": "~2.2.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/JSONStream/node_modules/through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha512-JIR0m0ybkmTcR8URann+HbwKmodP+OE8UCbsifQDYMLD5J3em1Cdn3MYPpbEd5elGDwmP98T+WbqP/tvzA5Mjg==", + "dev": true, + "license": "MIT" + }, + "node_modules/JSV": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", + "integrity": "sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/keypress": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", + "integrity": "sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==", + "license": "MIT" + }, + "node_modules/leafy": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/leafy/-/leafy-0.0.3.tgz", + "integrity": "sha512-2jI6Boq8DXhYQEEg/1lKknWDq7IVWmv6W7kmUHv2k2Y/mrK4s4JmINHPA9xRlDIvXB5YJJytQhNWyvOB6DNd2w==", + "license": "MIT", + "dependencies": { + "array-extended": "~0.0.3", + "declare.js": "~0.0.3", + "extended": "~0.0.3", + "is-extended": "~0.0.3", + "string-extended": "~0.0.3" + } + }, + "node_modules/lex-parser": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz", + "integrity": "sha512-DuAEISsr1H4LOpmFLkyMc8YStiRWZCO8hMsoXAXSbgyfvs2WQhSt0+/FBv3ZU/JBFZMGcE+FWzEBSzwUU7U27w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lexical-scope": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-0.0.15.tgz", + "integrity": "sha512-w2GlDzW3WYJcytiVfNeFKYQ4CUZJDBP8vtxkqD3jCjgqei4IbvMJu8EMhNXHbH//yW2hTVz04/Zbhi7eNeGEaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "astw": "~0.0.0" + } + }, + "node_modules/lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha512-LVbt/rjK62gSbhehDVKL0vlaime4Y1IBixL+bKeNfoY4L2zab/jGrxU6Ka05tMA/zBxkTk5t3ivtphdyYupczw==", + "dev": true, + "engines": [ + "node", + "rhino" + ], + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/marked": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.2.10.tgz", + "integrity": "sha512-LyFB4QvdBaJFfEIn33plrxtBuRjeHoDE2QJdP58i2EWMUTpa6GK6MnjJh3muCvVibFJompyr6IxecK2fjp4RDw==", + "dev": true, + "bin": { + "marked": "bin/marked" + } + }, + "node_modules/minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha512-zZ+Jy8lVWlvqqeM8iZB7w7KmQkoJn8djM585z88rywrEbzoqawVa9FR5p2hwD+y74nfuKOjmNvi9gtWJNLqHvA==", + "deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "2", + "sigmund": "~1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/module-deps": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-1.0.2.tgz", + "integrity": "sha512-ArcVFLj57xBmTDjBNRacFDAhF9YRwtnBpUv62MiBkab3/G4QCJblrguvcZayYztn+vaIVx5/qtYmdeb8qrKhew==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-resolve": "~1.1.0", + "concat-stream": "~1.0.0", + "detective": "~2.1.2", + "JSONStream": "~0.6.4", + "minimist": "~0.0.1", + "resolve": "~0.4.0", + "through": "~2.3.4" + }, + "bin": { + "module-deps": "cmd.js" + } + }, + "node_modules/module-deps/node_modules/resolve": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.4.3.tgz", + "integrity": "sha512-VRdITglwtstb0Hqag3iAVSYWgS/HfeuQIfUcq5Iv9Dmt+kzevtN7i5IZTJrZA+KcDHq8lSVOH8UewJxD9EMa7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/nomnom": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz", + "integrity": "sha512-fiVbT7BqxiQqjlR9U3FDGOSERFCKoXVCdxV2FwZuNN7/cmJ42iQx35nUFOAFDcyvemu9Adp+IlsCGlKQYLmBKw==", + "deprecated": "Package no longer supported. Contact support@npmjs.com for more info.", + "dev": true, + "dependencies": { + "colors": "0.5.x", + "underscore": "1.1.x" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nomnom/node_modules/colors": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "integrity": "sha512-XjsuUwpDeY98+yz959OlUK6m7mLBM+1MEG5oaenfuQnNnrQk1WvtcvFgN3FNDP3f2NmZ211t0mNEfSEN1h0eIg==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/nomnom/node_modules/underscore": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", + "integrity": "sha512-w4QtCHoLBXw1mjofIDoMyexaEdWGMedWNDhlWTtT1V1lCRqi65Pnoygkh6+WRdr+Bm8ldkBNkNeCsXGMlQS9HQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/object-extended": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/object-extended/-/object-extended-0.0.7.tgz", + "integrity": "sha512-2LJYIacEXoZ1glGkAZuvA/4pfJM4Y1ShReAo9jWpBSuz89TiUCdiPqhGJJ6m97F3WjhCSRwrbgaxYEAm9dRYBw==", + "license": "MIT", + "dependencies": { + "array-extended": "~0.0.4", + "extended": "~0.0.3", + "is-extended": "~0.0.3" + } + }, + "node_modules/optimist": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.5.2.tgz", + "integrity": "sha512-r9M8ZpnM9SXV5Wii7TCqienfcaY3tAiJe9Jchof87icbmbruKgK0xKXngmrnowTDnEawmmI1Qbha59JEoBkBGA==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/parents": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/parents/-/parents-0.0.3.tgz", + "integrity": "sha512-ASkdjFPS2nrxujzSBZGt8ZCKeG0/K2ZZVKveqXt7XGtXfu+ssnk4DQhnK91KRvt83f36LjfxOfwi0cv1+Re0eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-platform": "^0.0.1" + } + }, + "node_modules/parse-base64vlq-mappings": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/parse-base64vlq-mappings/-/parse-base64vlq-mappings-0.1.4.tgz", + "integrity": "sha512-nGqNfBUNlynVYVAeVTtrtyRLGYnT/xQWbl00RCzZ5RvnAZ2zv0MA0JlBz9P0O6GkG6QpvWLeYJGZwzMNy0uKUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-platform": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.0.1.tgz", + "integrity": "sha512-ydK1VKZFYwy0mT2JvimJfxt5z6Z6sjBbLfsFMoJczbwZ/ul0AjgpXLHinUzclf4/XYC8mtsWGuFERZ95Rnm8wA==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "license": [ + "MIT", + "Apache2" + ], + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/promise-extended": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/promise-extended/-/promise-extended-0.0.9.tgz", + "integrity": "sha512-br+k0tjjx6beU8encQQAZomTXJjoECFPOxeZnxBmi50f9VuLVQDWgW4XAsQzm9svQX4BrD6/TQQfiQEXr9z1eA==", + "license": "MIT", + "dependencies": { + "arguments-extended": "~0.0.3", + "array-extended": "~0.0.3", + "declare.js": "~0.0.3", + "extended": "~0.0.3", + "function-extended": "~0.0.3", + "is-extended": "~0.0.3" + } + }, + "node_modules/punycode": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.2.4.tgz", + "integrity": "sha512-h/vscxLPvI2l7k/0dFUKZ5I5TgMCJ/Pl+J6rw77PDuQM6UApf/GaRVkjv/YSm2k+fbp7Yw8dxsoe29DolT7h7w==", + "dev": true, + "engines": [ + "node", + "rhino" + ] + }, + "node_modules/resolve": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.3.1.tgz", + "integrity": "sha512-mxx/I/wLjxtryDBtrrb0ZNzaYERVWaHpJ0W0Arm8N4l8b+jiX/U5yKcsj0zQpF9UuKN1uz80EUTOudON6OPuaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/rfile": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rfile/-/rfile-1.0.0.tgz", + "integrity": "sha512-aNeTpY8g6DYmqPvakau22B0SipQTskO8FtYXzn8qg4X4bN9ExIH8VAhq/L9w7N8HvESYeSSwk3e4GmW+rLLAxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsite": "~1.0.0", + "resolve": "~0.3.0" + } + }, + "node_modules/rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "MIT", + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ruglify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ruglify/-/ruglify-1.0.0.tgz", + "integrity": "sha512-XfRj1YJdm/gnZNvmpQ5L+2YGRHglDGMPgJRbitgCxC3GzKVQF/t+ij1aNcNg2AnEXGtLHJDwoSWrAq3TUm0EVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfile": "~1.0", + "uglify-js": "~2.2" + } + }, + "node_modules/ruglify/node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/ruglify/node_modules/uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha512-viLk+/8G0zm2aKt1JJAVcz5J/5ytdiNaIsKgrre3yvSUjwVG6ZUujGH7E2TiPigZUwLYCe7eaIUEP2Zka2VJPA==", + "dev": true, + "dependencies": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/shell-quote": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-0.0.1.tgz", + "integrity": "sha512-uEWz7wa9vnCi9w4mvKZMgbHFk3DCKjLQlZcy0tJxUH4NwZjRrPPHXAYIEt2TmJs600Dcgj0Z3fZLZKVPVdGNbQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz", + "integrity": "sha512-UkXLBuUzAJwkal/0eYnQs8LpXQ4grKL5kPtA0RkUzhj1khUvw5Z2d717GRTRclDWQ+Y14yWkiM9cJX2CwSHxpw==", + "dev": true, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/split": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "integrity": "sha512-e0pKq+UUH2Xq/sXbYpZBZc3BawsfDZ7dgv+JtRTUPNcvF5CMR4Y9cvJqkMY0MoxWzTHvZuz1beg6pNEKlszPiQ==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/string-extended": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/string-extended/-/string-extended-0.0.8.tgz", + "integrity": "sha512-CK46p3AxBvBhJbBi6WrF9bCcaWH20E4NwlLSzpooG2nXWvcP2gy2YR8VN6fSwZyrbcvL4S4zoNKbR0QG52X4rw==", + "license": "MIT", + "dependencies": { + "array-extended": "~0.0.5", + "date-extended": "~0.0.3", + "extended": "~0.0.3", + "is-extended": "~0.0.3" + } + }, + "node_modules/syntax-error": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-0.0.1.tgz", + "integrity": "sha512-Pg2FzUaDR1Zlkl5ol+6LGAES7PjqcvfUpa2OKZZhww4VrRyjZyU2v+4C0dcYfhwNl2FMaZW9mXHzX0kS9x3xXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "~0.9.9" + } + }, + "node_modules/syntax-error/node_modules/esprima": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-0.9.9.tgz", + "integrity": "sha512-uTFHqyoMus4csxVp8FSqPajg59VwNt0PshVERqiIjPed6L9IG0pYz/zbhZ2HFFvn8AKzduipZP6mFxr3dr18ag==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/tape": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/tape/-/tape-0.2.2.tgz", + "integrity": "sha512-bfyf/0yv2FZVsf80b7oo50Lyi35sfjE7VM6206du7LtpbdQP8rbLhZy/stuS/Dcq4w6jE1Pz2zFrHtfeOKbaUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-equal": "~0.0.0", + "defined": "~0.0.0", + "jsonify": "~0.0.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-utf8": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", + "integrity": "sha512-zks18/TWT1iHO3v0vFp5qLKOG27m67ycq/Y7a7cTiRuUNlc4gf3HGnkRgMv0NyhnfTamtkYBJl+YeD1/j07gBQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/umd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/umd/-/umd-1.1.1.tgz", + "integrity": "sha512-enmeQO53e7Ra7A6xT7poPdqapuy5RJo+w+fFl50rpj0NsQ3TrCT6tENe6OljDXQ0MAAC70raDWewA0mEeMMH0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfile": "~1.0.0", + "ruglify": "~1.0.0", + "through": "~2.3.1", + "uglify-js": "~2.2.5" + } + }, + "node_modules/umd/node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/umd/node_modules/uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha512-viLk+/8G0zm2aKt1JJAVcz5J/5ytdiNaIsKgrre3yvSUjwVG6ZUujGH7E2TiPigZUwLYCe7eaIUEP2Zka2VJPA==", + "dev": true, + "dependencies": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==", + "dev": true + }, + "node_modules/underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", + "integrity": "sha512-3FVmhXqelrj6gfgp3Bn6tOavJvW0dNH2T+heTD38JRxIrAbiuzbqjknszoOYj3DyFB1nWiLj208Qt2no/L4cIA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha512-NyZNR3WDah+NPkjh/YmhuWSsT4a0mF0BJYgUmvrJ70zxjTXh5Y2Asobxlh0Nfs0PCFB5FVpRJft7NozAWFMwLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "indexof": "0.0.1" + } + }, + "node_modules/which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", + "integrity": "sha512-E87fdQ/eRJr9W1X4wTPejNy9zTW3FI2vpCZSJ/HAY+TkjKVC0TUm1jk6vn2Z7qay0DQy0+RBGdXxj+RmmiGZKQ==", + "dev": true, + "license": "ISC", + "bin": { + "which": "bin/which" + } + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wrench": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.4.4.tgz", + "integrity": "sha512-TdSi5IdWmtu6G+1DFpRxuo4SAFQ+IAlaTFz6SbcUMZNhmG37CpeGV+h38NRvdfmmCep7vpZzt/gfV6EaPvO7eA==", + "deprecated": "wrench.js is deprecated! You should check out fs-extra (https://github.com/jprichardson/node-fs-extra) for any operations you were using wrench for. Thanks for all the usage over the years.", + "dev": true, + "engines": { + "node": ">=0.1.97" + } + }, + "node_modules/yargs": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", + "integrity": "sha512-5j382E4xQSs71p/xZQsU1PtRA2HXPAjX0E0DkoGLxwNASMOKX6A9doV1NrZmj85u2Pjquz402qonBzz/yLPbPA==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "camelcase": "^1.0.2", + "decamelize": "^1.0.0", + "window-size": "0.1.0", + "wordwrap": "0.0.2" + } + }, + "node_modules/yargs/node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==", + "dev": true, + "license": "MIT/X11", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/zlib-browserify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.3.tgz", + "integrity": "sha512-KW42YGoQKq7+oU46deeWMUsrPyBruEWV1DoObBTMfEC2LnTqQIrwKVKrPoz2mn5DXESW4Ca/lIP2MqGHrt/WFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tape": "~0.2.2" + } + } + } +} diff --git a/test/flow/from.test.js b/test/flow/from.test.js index 355945a..1e9a00c 100644 --- a/test/flow/from.test.js +++ b/test/flow/from.test.js @@ -139,20 +139,20 @@ it.describe("from condition", function (it) { it.describe("with js source", function (it) { - var called = 0; - function MyValue(n2) { this.value = n2; } - var flow = nools.flow("from flow js source", function (flow) { + // Pin the date fact and the from-clause source to the same value (avoids flake from re-evaluating daysFromNow). + var dateFact = dateExtended.daysFromNow(1); + + function addFromJsSourceRules(flow) { flow.rule("from rule 1", [ [MyValue, "n1"], [Number, "n2", "n1.value == n2", "from [1,2,3,4,5]"] ], function (facts) { assert.equal(facts.n1.value, facts.n2); assert.isTrue([1, 2, 3, 4, 5].indexOf(facts.n2) !== -1); - called++; }); flow.rule("from rule 2", [ @@ -161,16 +161,20 @@ it.describe("from condition", function (it) { ], function (facts) { assert.equal(facts.n1.value, facts.n2); assert.isTrue(['a' , 'b', 'c', 'd', 'e', 'f'].indexOf(facts.n2) !== -1); - called++; }); - flow.rule("from rule 3 with function", [ + flow.rule("from rule 3 with function", { + scope: { + daysFromNow: function () { + return dateFact; + } + } + }, [ [MyValue, "n1", "isDate(n1.value)"], [Date, "n2", "dateCmp(n1.value, n2)", "from daysFromNow(1)"] ], function (facts) { assert.isDate(facts.n1.value); assert.isDate(facts.n2); - called++; }); flow.rule("from rule 4 with scope function", { @@ -185,12 +189,20 @@ it.describe("from condition", function (it) { ], function (facts) { assert.equal(facts.n1.value, facts.n2); assert.isTrue(["f", "g", "h", "i", "j"].indexOf(facts.n2) !== -1); - called++; }); + } + + var flow = nools.flow("from flow js source", addFromJsSourceRules); + + // deleteFlows() removes global registration; put this flow back for getSession(). + it.beforeEach(function () { + if (!nools.hasFlow("from flow js source")) { + flow = nools.flow("from flow js source", addFromJsSourceRules); + } }); it.should("create the proper match contexts", function () { - + var fireCount = 0; var session = flow.getSession( new MyValue(1), new MyValue(2), @@ -202,16 +214,18 @@ it.describe("from condition", function (it) { new MyValue('c'), new MyValue('d'), new MyValue('e'), - new MyValue(dateExtended.daysFromNow(1)), + new MyValue(dateFact), new MyValue('f'), new MyValue('g'), new MyValue('h'), new MyValue('i'), new MyValue('j') - ); + ).on("fire", function () { + fireCount++; + }); return session.match().then(function () { - assert.equal(called, 16); + assert.equal(fireCount, 16); }); }); diff --git a/test/issues.test.js b/test/issues.test.js index 2b27c2a..7ce20f0 100644 --- a/test/issues.test.js +++ b/test/issues.test.js @@ -1,10 +1,27 @@ "use strict"; var it = require("it"), assert = require("assert"), - nools = require("../index"); + nools = require("../index"), + GraphNode = require("../lib/nodes/node"); it.describe("issues", function (it) { + it.describe("dispose shared graph", function (it) { + + it.should("not recurse infinitely when dispose encounters a directed cycle through shared nodes", function () { + var a = new GraphNode(), + b = new GraphNode(), + pattern = {} + ; + a.addOutNode(b, pattern); + b.addOutNode(a, pattern); + assert.doesNotThrow(function () { + a.dispose(); + }); + }); + + }); + it.describe("62", function (it) { it.should("allow rule names with \" character in constraints", function () { assert.isTrue(/"s == \\"hello\\""/.test(nools.transpile('rule "issue62" {when {s : String s == "hello";}then {emit("s", s);}}', {name: "issue62"}))); From 59670a58b02bf5b806b41059471b96512860ab43 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 6 May 2026 23:37:07 +0200 Subject: [PATCH 2/4] Refactor dispose methods across node classes to enhance memory management and prevent infinite recursion in shared graphs. Added comprehensive tests to validate disposal behavior and memory clearing. --- lib/nodes/betaNode.js | 11 +- lib/nodes/fromNode.js | 6 + lib/nodes/fromNotNode.js | 6 + lib/nodes/notNode.js | 6 + test/dispose.memory.test.js | 499 ++++++++++++++++++++++++++++++++++++ test/issues.test.js | 77 ++++++ 6 files changed, 604 insertions(+), 1 deletion(-) create mode 100644 test/dispose.memory.test.js diff --git a/lib/nodes/betaNode.js b/lib/nodes/betaNode.js index 81a1c8c..90a332b 100644 --- a/lib/nodes/betaNode.js +++ b/lib/nodes/betaNode.js @@ -26,20 +26,29 @@ Node.extend({ } }, - dispose: function () { + // visited: skip shared betas; propagateDispose: downstream joins get dispose + Memory#clear. + dispose: function (assertable, visited) { + visited = visited || {}; + if (visited[this.__count]) { + return; + } + visited[this.__count] = true; this.leftMemory = {}; this.rightMemory = {}; this.leftTuples.clear(); this.rightTuples.clear(); + this.propagateDispose(assertable, undefined, visited); }, disposeLeft: function (fact) { + // Fact-scoped retract path: clear left side only, then notify downstream. this.leftMemory = {}; this.leftTuples.clear(); this.propagateDispose(fact); }, disposeRight: function (fact) { + // Mirror of disposeLeft for right-input retractions. this.rightMemory = {}; this.rightTuples.clear(); this.propagateDispose(fact); diff --git a/lib/nodes/fromNode.js b/lib/nodes/fromNode.js index 3b2135c..21f7ce7 100644 --- a/lib/nodes/fromNode.js +++ b/lib/nodes/fromNode.js @@ -41,6 +41,12 @@ JoinNode.extend({ this.__variables = vars; }, + // Drop from-derived fact index; super clears join tuples and walks out-edges. + dispose: function (assertable, visited) { + this.fromMemory = {}; + this._super(arguments); + }, + __createMatches: function (context) { var fh = context.factHash, o = this.from(fh); if (isArray(o)) { diff --git a/lib/nodes/fromNotNode.js b/lib/nodes/fromNotNode.js index 66900dc..6dd0ab5 100644 --- a/lib/nodes/fromNotNode.js +++ b/lib/nodes/fromNotNode.js @@ -35,6 +35,12 @@ JoinNode.extend({ }, + // Same as FromNode: fromMemory is outside BetaNode hash/tuples. + dispose: function (assertable, visited) { + this.fromMemory = {}; + this._super(arguments); + }, + retractLeft: function (context) { var ctx = this.removeFromLeftMemory(context); if (ctx) { diff --git a/lib/nodes/notNode.js b/lib/nodes/notNode.js index e788862..bde1940 100644 --- a/lib/nodes/notNode.js +++ b/lib/nodes/notNode.js @@ -16,6 +16,12 @@ JoinNode.extend({ this.notMatch = new Context(new InitialFact()).match; }, + // BetaNode.dispose does not reset blocked-tuple map; clear before super chain. + dispose: function (assertable, visited) { + this.leftTupleMemory = {}; + this._super(arguments); + }, + __cloneContext: function (context) { return context.clone(null, null, context.match.merge(this.notMatch)); }, diff --git a/test/dispose.memory.test.js b/test/dispose.memory.test.js new file mode 100644 index 0000000..82dd35f --- /dev/null +++ b/test/dispose.memory.test.js @@ -0,0 +1,499 @@ +"use strict"; +// Regression suite for session.dispose: beta memories, Not/From maps, agenda, working memory. +var it = require("it"), + assert = require("assert"), + declare = require("declare.js"), + nools = require("../index"), + Memory = require("../lib/nodes/misc/memory"); + +/** DFS from type nodes; picks up beta-like nodes (Not/From are not only in root.joinNodes). */ +function collectBetaLikeNodes(root) { + var seen = {}; + var out = []; + + function visit(n) { + if (!n || seen[n.__count]) { + return; + } + seen[n.__count] = true; + if (n.leftTuples && n.rightTuples) { + out.push(n); + } + var es = n.__entrySet, i; + if (es) { + for (i = es.length - 1; i >= 0; i--) { + visit(es[i].key); + } + } + } + + var tns = root.typeNodes, j; + for (j = tns.length - 1; j >= 0; j--) { + visit(tns[j]); + } + return out; +} + +/** Assert join hashes, tuple stores, and optional Not/From side maps. */ +function assertBetaStoresEmpty(n) { + assert.equal(Object.keys(n.leftMemory).length, 0, "leftMemory keys for " + (n.nodeType || n.toString())); + assert.equal(Object.keys(n.rightMemory).length, 0, "rightMemory keys"); + assert.equal(n.leftTuples.length, 0, "leftTuples.length"); + assert.equal(n.rightTuples.length, 0, "rightTuples.length"); + if (n.leftTupleMemory) { + assert.equal(Object.keys(n.leftTupleMemory).length, 0, "leftTupleMemory"); + } + if (n.fromMemory) { + assert.equal(Object.keys(n.fromMemory).length, 0, "fromMemory"); + } +} + +/** Facts list + agenda rules cleared (public session surface). */ +function assertSessionStoresDisposed(session) { + assert.strictEqual(session.workingMemory.facts.head, null, "workingMemory facts list head"); + assert.strictEqual(session.workingMemory.facts.tail, null); + assert.strictEqual(session.workingMemory.facts.length, 0); + assert.deepEqual(session.getFacts(), []); + assert.deepEqual(session.agenda.rules, {}, "agenda.rules"); +} + +/** Wrap session.dispose; count Memory#clear / clearIndexes (left+right per beta). */ +function disposeWithMemorySpies(session) { + var originalClear = Memory.prototype.clear; + var originalClearIndexes = Memory.prototype.clearIndexes; + var clearCount = 0; + var clearIndexesCount = 0; + Memory.prototype.clear = function () { + clearCount++; + return originalClear.apply(this, arguments); + }; + Memory.prototype.clearIndexes = function () { + clearIndexesCount++; + return originalClearIndexes.apply(this, arguments); + }; + try { + session.dispose(); + } finally { + Memory.prototype.clear = originalClear; + Memory.prototype.clearIndexes = originalClearIndexes; + } + return { clearCount: clearCount, clearIndexesCount: clearIndexesCount }; +} + +/** Flow names registered in this file (deleteFlow in afterAll). */ +var FLOW_NAMES = [ + "disposeMemJoin", + "disposeMemChain", + "disposeMemNot", + "disposeMemPlainBeta", + "disposeMemRefJoin", + "disposeMemTwoRules", + "disposeMemFrom", + "disposeMemExists", + "disposeMemExistsFrom", + "disposeMemFromNot", + "disposeMemOr", + "disposeCompileMem", + "disposeMemNoMatch", + "disposeMemIdempotent", + "disposeMemAgenda" +]; + +it.describe("session.dispose clears beta memory", function (it) { + + // Compiled + flow() names; must delete or second test run throws “already defined”. + it.afterAll(function () { + var i = FLOW_NAMES.length; + while (--i >= 0) { + nools.deleteFlow(FLOW_NAMES[i]); + } + }); + + // Simple join, chains, Not, Beta vs Join, multi-rule. + it.describe("core join networks", function (it) { + + // Proves left/right Memory.clear per beta and WM + agenda flush. + it.should("invoke Memory#clear / clearIndexes twice per beta-like node (simple join)", function () { + var flow = nools.flow("disposeMemJoin", function () { + this.rule("r", [[String, "a"], [Number, "n"]], function () {}); + }); + var session = flow.getSession("x", 1); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.length >= 1, "expected at least one join node"); + + var spies = disposeWithMemorySpies(session); + + assert.equal(spies.clearCount, betas.length * 2); + assert.equal(spies.clearIndexesCount, spies.clearCount, "clear() always calls clearIndexes()"); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + assertSessionStoresDisposed(session); + }); + }); + + // Chained joins: first beta must propagate dispose to downstream betas. + it.should("clear every beta-like node in a multi-join rule", function () { + var flow = nools.flow("disposeMemChain", function () { + this.rule("r", [ + [String, "a"], + [String, "b"], + [Number, "n"] + ], function () {}); + }); + var session = flow.getSession("x", "y", 7); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.length >= 2, "chained patterns should create multiple joins"); + + var spies = disposeWithMemorySpies(session); + + assert.equal(spies.clearCount, betas.length * 2); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // NotNode.leftTupleMemory is extra state on top of BetaNode disposal. + it.should("clear NotNode auxiliary maps and tuple stores", function () { + var flow = nools.flow("disposeMemNot", function () { + this.rule("order", [ + [Number, "n1"], + ["not", Number, "n2", "n1 != n2 && n1 > n2"] + ], function () {}); + }); + var session = flow.getSession(3, 1, 5, 2, 4); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.length >= 1); + var hasNot = false; + var i = betas.length; + while (--i >= 0) { + if (betas[i].nodeType === "NotNode") { + hasNot = true; + } + } + assert.isTrue(hasNot, "fixture should include a NotNode"); + + session.dispose(); + + i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // No ReferenceConstraint on either pattern side => plain BetaNode (not JoinNode). + it.should("clear plain BetaNode networks (no reference constraints on either side)", function () { + var flow = nools.flow("disposeMemPlainBeta", function () { + this.rule("r", [ + [String, "a", "a == 'hi'"], + [String, "b", "b == 'there'"] + ], function () {}); + }); + var session = flow.getSession("hi", "there"); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.some(function (b) { + return b.nodeType === "BetaNode"; + }), "expected a plain BetaNode join"); + session.dispose(); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // Cross-pattern reference => JoinNode; memories must still empty after dispose. + it.should("clear JoinNode networks with cross-pattern references", function () { + var flow = nools.flow("disposeMemRefJoin", function () { + this.rule("r", [ + [String, "s1"], + [String, "s2", "s2 == s1"] + ], function () {}); + }); + var session = flow.getSession("same", "same"); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.some(function (b) { + return b.nodeType === "JoinNode"; + }), "reference constraints should use JoinNode"); + session.dispose(); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // Two rules => two alpha subgraphs; every beta in the session graph clears. + it.should("clear all join nodes when the flow defines multiple rules", function () { + var flow = nools.flow("disposeMemTwoRules", function () { + this.rule("a", [[String, "x"], [Number, "n"]], function () {}); + this.rule("b", [[String, "y"], [Boolean, "b"]], function () {}); + }); + var session = flow.getSession("p", 1, "q", true); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.length >= 2, "two rules should contribute disjoint join subgraphs"); + var spies = disposeWithMemorySpies(session); + assert.equal(spies.clearCount, betas.length * 2); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + }); + + // FromNode, exists variants, from+not, or-replicated rules, compile() path. + it.describe("from / exists / or / compiled flows", function (it) { + + var Address = declare({ + instance: { + constructor: function (zip) { + this.zipcode = zip; + } + } + }); + var Person = declare({ + instance: { + constructor: function (first, last, address) { + this.firstName = first; + this.lastName = last; + this.address = address; + } + } + }); + + // fromMemory fills during match; FromNode.dispose must drop it before super. + it.should("clear FromNode fromMemory and tuple stores", function () { + var flow = nools.flow("disposeMemFrom", function () { + this.rule("fromdispose", [ + [Person, "p"], + [Address, "a", "a.zipcode == 88847", "from p.address"] + ], function () {}); + }); + var session = flow.getSession(new Person("bob", "yukon", new Address(88847))); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + var hasFrom = false; + var i = betas.length; + while (--i >= 0) { + if (betas[i].nodeType === "FromNode") { + hasFrom = true; + } + } + assert.isTrue(hasFrom, "fixture should include FromNode"); + var fromNodes = betas.filter(function (b) { + return b.nodeType === "FromNode"; + }); + assert.ok(Object.keys(fromNodes[0].fromMemory).length > 0, "fromMemory populated before dispose"); + session.dispose(); + i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // ExistsNode subclasses NotNode (blocking / leftTupleMemory paths). + it.should("clear ExistsNode (extends NotNode) tuple and blocking maps", function () { + var C = function () { + this.n = 0; + }; + var flow = nools.flow("disposeMemExists", function () { + this.rule("ex", [ + ["exists", String, "s"], + [C, "c"] + ], function () {}); + }); + var session = flow.getSession(new C(), "a", "b"); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.some(function (b) { + return b.nodeType === "ExistsNode"; + })); + session.dispose(); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // Exists + from uses ExistsFromNode; inherits fromMemory cleanup from FromNot path. + it.should("clear ExistsFromNode fromMemory", function () { + function PersonZip(z) { + this.zipcodes = z; + } + var C = function () { + this.n = 0; + }; + var flow = nools.flow("disposeMemExistsFrom", function () { + this.rule("exf", [ + [PersonZip, "p"], + ["exists", Number, "zip", "zip == 11111", "from p.zipcodes"], + [C, "c"] + ], function () {}); + }); + var session = flow.getSession(new C(), new PersonZip([33333, 22222, 11111])); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.some(function (b) { + return b.nodeType === "ExistsFromNode"; + })); + session.dispose(); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // not + from collection: FromNotNode keeps fromMemory for generated facts. + it.should("clear FromNotNode (not … from …) fromMemory", function () { + var FriendPerson = declare({ + instance: { + constructor: function (first, last, friends) { + this.firstName = first; + this.lastName = last; + this.friends = friends || []; + } + } + }); + var flow = nools.flow("disposeMemFromNot", function () { + this.rule("fn", [ + [FriendPerson, "p"], + ["not", FriendPerson, "friend", "p !== friend && friend.lastName !== p.lastName", "from p.friends"] + ], function () {}); + }); + var a = new FriendPerson("a", "yuk", []); + var b = new FriendPerson("b", "yuko", []); + a.friends.push(b); + var session = flow.getSession(a, b); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.some(function (bn) { + return bn.nodeType === "FromNotNode"; + })); + session.dispose(); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // or() clones into multiple rules; session root still owns one combined network walk. + it.should("clear networks produced by or patterns (multiple rule replicas)", function () { + var Cntr = function () { + this.k = 0; + }; + var flow = nools.flow("disposeMemOr", function () { + this.rule("orr", [ + ["or", + [String, "s", "s == 'hello'"], + [String, "s", "s == 'world'"] + ], + [Cntr, "c"] + ], function () {}); + }); + var session = flow.getSession("world", new Cntr()); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.length >= 1); + session.dispose(); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + }); + }); + + // compile() flow + getSession: same dispose contract as nools.flow(). + it.should("dispose compiled DSL flow and clear beta memory", function () { + var flow = nools.compile( + "rule disposeDsl { when { a: String; b: Number; } then { } }", + { name: "disposeCompileMem" } + ); + var session = flow.getSession("z", 9); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + assert.ok(betas.length >= 1); + var spies = disposeWithMemorySpies(session); + assert.equal(spies.clearCount, betas.length * 2); + assertSessionStoresDisposed(session); + }); + }); + }); + + // Activations and per-rule trees dropped while beta dispose runs separately. + it.describe("agenda", function (it) { + + // Fired rule leaves agenda.rules populated; dispose must zero activations. + it.should("remove all activations and per-rule agenda bookkeeping", function () { + var fired = false; + var flow = nools.flow("disposeMemAgenda", function () { + this.rule("fires", [[String, "a"]], function () { + fired = true; + }); + }); + var session = flow.getSession("x"); + return session.match().then(function () { + assert.isTrue(fired); + assert.ok(Object.keys(session.agenda.rules).length > 0, "rules registered activations"); + session.dispose(); + assert.deepEqual(session.agenda.rules, {}); + assert.isTrue(session.agenda.isEmpty()); + }); + }); + }); + + // No match path, double dispose. + it.describe("edge cases and invariants", function (it) { + + // No match run: still safe to dispose (InitialFact cleared with WM). + it.should("dispose a session that never ran match()", function () { + var flow = nools.flow("disposeMemNoMatch", function () { + this.rule("r", [[String, "a"]], function () {}); + }); + var session = flow.getSession("only"); + var betas = collectBetaLikeNodes(session.rootNode); + assert.doesNotThrow(function () { + session.dispose(); + }); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + assertSessionStoresDisposed(session); + }); + + // Defensive: callers may invoke dispose more than once. + it.should("allow dispose() twice without throwing (idempotent)", function () { + var flow = nools.flow("disposeMemIdempotent", function () { + this.rule("r", [[String, "a"], [Number, "n"]], function () {}); + }); + var session = flow.getSession("q", 3); + return session.match().then(function () { + var betas = collectBetaLikeNodes(session.rootNode); + session.dispose(); + assert.doesNotThrow(function () { + session.dispose(); + }); + var i = betas.length; + while (--i >= 0) { + assertBetaStoresEmpty(betas[i]); + } + assertSessionStoresDisposed(session); + }); + }); + }); +}); diff --git a/test/issues.test.js b/test/issues.test.js index 7ce20f0..55bc5a1 100644 --- a/test/issues.test.js +++ b/test/issues.test.js @@ -6,8 +6,10 @@ var it = require("it"), it.describe("issues", function (it) { + // Node.dispose visited map: cycles and diamonds must not blow the stack. it.describe("dispose shared graph", function (it) { + // Smoke: 2-node cycle must terminate (stack overflow = bug). it.should("not recurse infinitely when dispose encounters a directed cycle through shared nodes", function () { var a = new GraphNode(), b = new GraphNode(), @@ -20,6 +22,81 @@ it.describe("issues", function (it) { }); }); + // External visited map: each node exactly once in a large ring. + it.should("visit each node in a large directed cycle once (visited map matches graph size)", function () { + var n = 250, + nodes = [], + pattern = {}, + i + ; + for (i = 0; i < n; i++) { + nodes.push(new GraphNode()); + } + for (i = 0; i < n; i++) { + nodes[i].addOutNode(nodes[(i + 1) % n], pattern); + } + var visited = {}; + nodes[0].dispose(undefined, visited); + assert.equal(Object.keys(visited).length, n); + for (i = 0; i < n; i++) { + assert.isTrue(visited[nodes[i].__count], "node index " + i + " __count " + nodes[i].__count); + } + }); + + // Wrapper counts entries: ring closure causes one extra call that hits visited[] and returns. + it.should("short-circuit when dispose re-enters a node (visited prevents unbounded recursion)", function () { + var n = 100, + nodes = [], + pattern = {}, + i, + originalDispose = GraphNode.prototype.dispose, + entries = {} + ; + GraphNode.prototype.dispose = function (assertable, visited) { + var c = this.__count; + entries[c] = (entries[c] || 0) + 1; + return originalDispose.call(this, assertable, visited); + }; + try { + for (i = 0; i < n; i++) { + nodes.push(new GraphNode()); + } + for (i = 0; i < n; i++) { + nodes[i].addOutNode(nodes[(i + 1) % n], pattern); + } + nodes[0].dispose(); + var totalCalls = Object.keys(entries).reduce(function (acc, k) { + return acc + entries[k]; + }, 0); + assert.equal(totalCalls, n + 1, "one extra dispose entry when the cycle closes; without visited this never finishes"); + assert.equal(entries[nodes[0].__count], 2, "start node is entered again and must return immediately"); + for (i = 1; i < n; i++) { + assert.equal(entries[nodes[i].__count], 1); + } + } finally { + GraphNode.prototype.dispose = originalDispose; + } + }); + + // RootNode-style: two type trees, one visited object; self-edge on shared node. + it.should("mark every reachable node when two roots share one visited map (like RootNode.dispose)", function () { + var pattern = {}, + a = new GraphNode(), + b = new GraphNode(), + shared = new GraphNode() + ; + a.addOutNode(shared, pattern); + b.addOutNode(shared, pattern); + shared.addOutNode(shared, pattern); + var visited = {}; + a.dispose(undefined, visited); + b.dispose(undefined, visited); + assert.equal(Object.keys(visited).length, 3); + assert.isTrue(visited[a.__count]); + assert.isTrue(visited[b.__count]); + assert.isTrue(visited[shared.__count]); + }); + }); it.describe("62", function (it) { From f0541f30562c750bfc226720aa0bb0d758bcd2b6 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 6 May 2026 23:55:51 +0200 Subject: [PATCH 3/4] Remove unused dispose method from adapterNode --- lib/nodes/adapterNode.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/nodes/adapterNode.js b/lib/nodes/adapterNode.js index c47c499..dbefbab 100644 --- a/lib/nodes/adapterNode.js +++ b/lib/nodes/adapterNode.js @@ -29,16 +29,6 @@ Node.extend({ } else { this.__propagateNoPaths(method, context); } - }, - - // Pass `visited` through from RootNode.dispose (see node.js). - dispose: function (context, visited) { - visited = visited || {}; - if (visited[this.__count]) { - return; - } - visited[this.__count] = true; - this.propagateDispose(context, undefined, visited); } } }).as(module); \ No newline at end of file From 0b272b786278bf1a67ca9a47e6d8a8629f4a21ad Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 7 May 2026 00:27:51 +0200 Subject: [PATCH 4/4] Run tests in github actions --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ .travis.yml | 8 -------- lib/nodes/fromNode.js | 2 +- lib/nodes/fromNotNode.js | 2 +- lib/nodes/notNode.js | 2 +- package.json | 2 +- readme.md | 2 +- 7 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4e3bb4c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + pull_request: + push: + branches: [main, master] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Install Grunt CLI + run: npm install -g grunt-cli + + - name: JSHint + run: grunt jshint + + - name: Tests + run: npm test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 22a212b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: node_js -node_js: - - 0.10 -before_install: - - npm install -g grunt-cli -script: - - grunt jshint - - npm test diff --git a/lib/nodes/fromNode.js b/lib/nodes/fromNode.js index 21f7ce7..11f1fb9 100644 --- a/lib/nodes/fromNode.js +++ b/lib/nodes/fromNode.js @@ -42,7 +42,7 @@ JoinNode.extend({ }, // Drop from-derived fact index; super clears join tuples and walks out-edges. - dispose: function (assertable, visited) { + dispose: function () { this.fromMemory = {}; this._super(arguments); }, diff --git a/lib/nodes/fromNotNode.js b/lib/nodes/fromNotNode.js index 6dd0ab5..b0696a2 100644 --- a/lib/nodes/fromNotNode.js +++ b/lib/nodes/fromNotNode.js @@ -36,7 +36,7 @@ JoinNode.extend({ }, // Same as FromNode: fromMemory is outside BetaNode hash/tuples. - dispose: function (assertable, visited) { + dispose: function () { this.fromMemory = {}; this._super(arguments); }, diff --git a/lib/nodes/notNode.js b/lib/nodes/notNode.js index bde1940..28d3141 100644 --- a/lib/nodes/notNode.js +++ b/lib/nodes/notNode.js @@ -17,7 +17,7 @@ JoinNode.extend({ }, // BetaNode.dispose does not reset blocked-tuple map; clear before super chain. - dispose: function (assertable, visited) { + dispose: function () { this.leftTupleMemory = {}; this._super(arguments); }, diff --git a/package.json b/package.json index 7372b5c..80387c0 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,6 @@ "lib": "lib" }, "engines": { - "node": ">= 0.6.1" + "node": ">= 22" } } diff --git a/readme.md b/readme.md index aa8897c..5bb28c3 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ C2FO is no longer maintaining this project. Please use accordingly. If you would like to help maintain or take over the project please let us know. -[![Build Status](https://travis-ci.org/C2FO/nools.png)](https://travis-ci.org/C2FO/nools) +[![CI](https://github.com/C2FO/nools/actions/workflows/ci.yml/badge.svg)](https://github.com/C2FO/nools/actions/workflows/ci.yml) [![browser support](https://ci.testling.com/C2FO/nools.png)](https://ci.testling.com/C2FO/nools)