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/.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/.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/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..11f1fb9 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 () { + 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..b0696a2 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 () { + this.fromMemory = {}; + this._super(arguments); + }, + retractLeft: function (context) { var ctx = this.removeFromLeftMemory(context); if (ctx) { 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/notNode.js b/lib/nodes/notNode.js index e788862..28d3141 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 () { + this.leftTupleMemory = {}; + this._super(arguments); + }, + __cloneContext: function (context) { return context.clone(null, null, context.match.merge(this.notMatch)); }, 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/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) 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/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..55bc5a1 100644 --- a/test/issues.test.js +++ b/test/issues.test.js @@ -1,10 +1,104 @@ "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) { + // 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(), + pattern = {} + ; + a.addOutNode(b, pattern); + b.addOutNode(a, pattern); + assert.doesNotThrow(function () { + a.dispose(); + }); + }); + + // 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) { 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"})));