From 8bdb72df53c6fbd3fdb221f67cb9e93e387db4c9 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Wed, 8 Dec 2021 12:18:02 +0300 Subject: [PATCH 01/19] add simple binding for `get` --- Cargo.toml | 1 + node-grove/Cargo.toml | 18 + node-grove/README.md | 40 + node-grove/index.js | 29 + node-grove/package-lock.json | 1825 ++++++++++++++++++++++++++++++++++ node-grove/package.json | 29 + node-grove/src/lib.rs | 158 +++ 7 files changed, 2100 insertions(+) create mode 100644 node-grove/Cargo.toml create mode 100644 node-grove/README.md create mode 100644 node-grove/index.js create mode 100644 node-grove/package-lock.json create mode 100644 node-grove/package.json create mode 100644 node-grove/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 02230bae7..29dd29877 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,5 @@ members = [ "grovedb", "merk", + "node-grove" ] diff --git a/node-grove/Cargo.toml b/node-grove/Cargo.toml new file mode 100644 index 000000000..8d0719ca3 --- /dev/null +++ b/node-grove/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "node-grove" +version = "0.1.0" +description = "GroveDB node.js bindings" +edition = "2021" +license = "MIT" +exclude = ["index.node"] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +grovedb = { path = "../grovedb" } + +[dependencies.neon] +version = "0.9" +default-features = false +features = ["napi-6", "event-queue-api", "try-catch-api"] diff --git a/node-grove/README.md b/node-grove/README.md new file mode 100644 index 000000000..a3285b90e --- /dev/null +++ b/node-grove/README.md @@ -0,0 +1,40 @@ +# node-grove + +[![Rayon crate](https://img.shields.io/crates/v/rs_merkle.svg)](https://crates.io/crates/rs_merkle) +[![Rayon documentation](https://docs.rs/rs_merkle/badge.svg)](https://docs.rs/rs_merkle) +[![Build and test](https://github.com/antouhou/rs-merkle/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/antouhou/rs-merkle/actions) + +`node-grove` is a groveDB binding for node.js + +`node-grove` is +[available on crates.io](https://crates.io/crates/rs_merkle), and +[API Documentation is available on docs.rs](https://docs.rs/rs_merkle/). + +## Usage + +Add the following to your `Cargo.toml`: + +```toml +[dependencies] +rs_merkle = "1.0" +``` + +## Documentation + +[Documentation is available on docs.rs](https://docs.rs/rs_merkle/). + +## Contributing + +Everyone is welcome to contribute in any way or form! For further details, +please read [CONTRIBUTING.md](./CONTRIBUTING.md) (Which doesn't really exist in +this repo lol) + +## Authors +- [Anton Suprunchuk](https://github.com/antouhou) - [Website](https://antouhou.com) + +Also, see the list of contributors who participated in this project. + +## License + +This project is licensed under the MIT License - see the +[LICENSE.md](./LICENSE.md) file for details \ No newline at end of file diff --git a/node-grove/index.js b/node-grove/index.js new file mode 100644 index 000000000..b7425109b --- /dev/null +++ b/node-grove/index.js @@ -0,0 +1,29 @@ +"use strict"; + +const { promisify } = require("util"); + +const { groveDbOpen, groveDbGet } = require("./index.node"); + +// Convert the DB methods from using callbacks to returning promises +const groveDbGetAsync = promisify(groveDbGet); + +// Wrapper class for the boxed `Database` for idiomatic JavaScript usage +class GroveDB { + constructor(db) { + this.db = db; + } + + static open(path) { + const db = groveDbOpen(path); + return new GroveDB(db); + } + + // Wrap each method with a delegate to `this.db` + // This could be node in several other ways, for example binding assignment + // in the constructor + get(name) { + return groveDbGetAsync.call(this.db, name); + } +} + +module.exports = GroveDB; \ No newline at end of file diff --git a/node-grove/package-lock.json b/node-grove/package-lock.json new file mode 100644 index 000000000..25762830b --- /dev/null +++ b/node-grove/package-lock.json @@ -0,0 +1,1825 @@ +{ + "name": "node-grove", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "node-grove", + "version": "0.1.0", + "hasInstallScript": true, + "license": "MIT", + "devDependencies": { + "cargo-cp-artifact": "^0.1", + "mocha": "^8.4.0" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cargo-cp-artifact": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz", + "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==", + "dev": true, + "bin": { + "cargo-cp-artifact": "bin/cargo-cp-artifact.js" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "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 + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true + }, + "cargo-cp-artifact": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz", + "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/node-grove/package.json b/node-grove/package.json new file mode 100644 index 000000000..1bc7568d5 --- /dev/null +++ b/node-grove/package.json @@ -0,0 +1,29 @@ +{ + "name": "node-grove", + "version": "0.1.0", + "description": "Neon binding for GroveDb", + "main": "index.js", + "scripts": { + "build": "cargo-cp-artifact -nc index.node -- cargo +nightly build --message-format=json-render-diagnostics", + "install": "npm run build", + "test": "mocha" + }, + "license": "MIT", + "devDependencies": { + "cargo-cp-artifact": "^0.1", + "mocha": "^8.4.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dashevo/grovedb.git" + }, + "keywords": [ + "GroveDB", + "Database", + "Authenticated database" + ], + "bugs": { + "url": "https://github.com/dashevo/grovedb/issues" + }, + "homepage": "https://github.com/dashevo/grovedb#readme" +} \ No newline at end of file diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs new file mode 100644 index 000000000..1e2fef1ec --- /dev/null +++ b/node-grove/src/lib.rs @@ -0,0 +1,158 @@ +use std::path::Path; +use std::sync::mpsc; +use std::thread; + +use grovedb::GroveDb; +use neon::prelude::*; + +type DbCallback = Box; + +// Wraps a SQLite connection a channel, allowing concurrent access +struct Database { + tx: mpsc::Sender, +} + +// Messages sent on the database channel +enum DbMessage { + // Callback to be executed + Callback(DbCallback), + // Indicates that the thread should be stopped and connection closed + Close, +} + +struct GroveDbWrapper { + tx: mpsc::Sender, +} + +// Internal wrapper logic +impl GroveDbWrapper { + // Creates a new instance of `Database` + // + // 1. Creates a connection and a channel + // 2. Spawns a thread and moves the channel receiver and connection to it + // 3. On a separate thread, read closures off the channel and execute with access + // to the connection. + fn new<'a, C>(cx: &mut C) -> rusqlite::Result + where + C: Context<'a>, + { + let path = cx.argument::(0)?.value(); + let path = Path::new(&path); + + // Channel for sending callbacks to execute on the GroveDb connection thread + let (tx, rx) = mpsc::channel::(); + + // Open a connection to groveDb, this will be moved to a separate thread + let mut grove_db = GroveDb::open(path)?; + + // Create an `Channel` for calling back to JavaScript. It is more efficient + // to create a single channel and re-use it for all database callbacks. + // The JavaScript process will not exit as long as this channel has not been + // dropped. + let channel = cx.channel(); + + // Spawn a thread for processing database queries + // This will not block the JavaScript main thread and will continue executing + // concurrently. + thread::spawn(move || { + // Blocks until a callback is available + // When the instance of `Database` is dropped, the channel will be closed + // and `rx.recv()` will return an `Err`, ending the loop and terminating + // the thread. + while let Ok(message) = rx.recv() { + match message { + DbMessage::Callback(f) => { + // The connection and channel are owned by the thread, but _lent_ to + // the callback. The callback has exclusive access to the connection + // for the duration of the callback. + f(&mut grove_db, &channel); + } + // Immediately close the connection, even if there are pending messages + DbMessage::Close => break, + } + } + }); + + Ok(Self { tx }) + } + + // Idiomatic rust would take an owned `self` to prevent use after close + // However, it's not possible to prevent JavaScript from continuing to hold a closed database + fn close(&self) -> Result<(), mpsc::SendError> { + self.tx.send(DbMessage::Close) + } + + fn send( + &self, + callback: impl FnOnce(&mut Connection, &Channel) + Send + 'static, + ) -> Result<(), mpsc::SendError> { + self.tx.send(DbMessage::Callback(Box::new(callback))) + } +} + +// Ensures that GroveDbWrapper is properly disposed when the corresponding JS object +// gets garbage collected +impl Finalize for GroveDbWrapper {} + +// External wrapper logic +impl GroveDbWrapper { + // Create a new instance of `Database` and place it inside a `JsBox` + // JavaScript can hold a reference to a `JsBox`, but the contents are opaque + fn js_open(mut cx: FunctionContext) -> JsResult> { + let dbWrapper = + GroveDbWrapper::new(&mut cx).or_else(|err| cx.throw_error(err.to_string()))?; + + Ok(cx.boxed(dbWrapper)) + } + + fn js_get(mut cx: FunctionContext) -> JsResult { + // Get the first argument as a `JsNumber` and convert to an `f64` + let id = cx.argument::(0)?.value(&mut cx); + + // Get the second argument as a `JsFunction` + let callback = cx.argument::(1)?.root(&mut cx); + + // Get the `this` value as a `JsBox` + let db = cx + .this() + .downcast_or_throw::, _>(&mut cx)?; + + db.send(move |grove_db: &mut GroveDb, channel| { + let result: Result = grove_db.get(); + + channel.send(move |mut cx| { + let callback = callback.into_inner(&mut cx); + let this = cx.undefined(); + let args: Vec> = match result { + // Convert the name to a `JsString` on success and upcast to a `JsValue` + Ok(name) => vec![cx.null().upcast(), cx.string(name).upcast()], + + // If the row was not found, return `undefined` as a success instead + // of throwing an exception + Err(rusqlite::Error::QueryReturnedNoRows) => { + vec![cx.null().upcast(), cx.undefined().upcast()] + } + + // Convert the error to a JavaScript exception on failure + Err(err) => vec![cx.error(err.to_string())?.upcast()], + }; + + callback.call(&mut cx, this, args)?; + + Ok(()) + }); + }) + .or_else(|err| cx.throw_error(err.to_string()))?; + + // This function does not have a return value + Ok(cx.undefined()) + } +} + +#[neon::main] +fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("groveDbOpen", GroveDbWrapper::js_open)?; + cx.export_function("groveDbGet", GroveDbWrapper::js_get)?; + + Ok(()) +} From e1844cb2f61f5b786d535efe31050c3ad14bee95 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Wed, 8 Dec 2021 16:55:45 +0300 Subject: [PATCH 02/19] rename connection to grove_db --- .gitignore | 1 + node-grove/src/lib.rs | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 8ff0eb369..47e81955a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target .idea Cargo.lock +node-grove/node_modules \ No newline at end of file diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index 1e2fef1ec..1f745f3eb 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -2,10 +2,10 @@ use std::path::Path; use std::sync::mpsc; use std::thread; -use grovedb::GroveDb; +use grovedb::{GroveDb, Error}; use neon::prelude::*; -type DbCallback = Box; +type DbCallback = Box; // Wraps a SQLite connection a channel, allowing concurrent access struct Database { @@ -32,12 +32,11 @@ impl GroveDbWrapper { // 2. Spawns a thread and moves the channel receiver and connection to it // 3. On a separate thread, read closures off the channel and execute with access // to the connection. - fn new<'a, C>(cx: &mut C) -> rusqlite::Result - where - C: Context<'a>, + fn new(cx: &mut FunctionContext) -> Result { - let path = cx.argument::(0)?.value(); - let path = Path::new(&path); + // TODO: error handling + let pathString = cx.argument::(0)?.value(cx); + let path = Path::new(&pathString); // Channel for sending callbacks to execute on the GroveDb connection thread let (tx, rx) = mpsc::channel::(); @@ -84,7 +83,7 @@ impl GroveDbWrapper { fn send( &self, - callback: impl FnOnce(&mut Connection, &Channel) + Send + 'static, + callback: impl FnOnce(&mut GroveDb, &Channel) + Send + 'static, ) -> Result<(), mpsc::SendError> { self.tx.send(DbMessage::Callback(Box::new(callback))) } @@ -127,11 +126,12 @@ impl GroveDbWrapper { // Convert the name to a `JsString` on success and upcast to a `JsValue` Ok(name) => vec![cx.null().upcast(), cx.string(name).upcast()], - // If the row was not found, return `undefined` as a success instead - // of throwing an exception - Err(rusqlite::Error::QueryReturnedNoRows) => { - vec![cx.null().upcast(), cx.undefined().upcast()] - } + // TODO: figure out what to do for the empty result + // // If the row was not found, return `undefined` as a success instead + // // of throwing an exception + // Err(rusqlite::Error::QueryReturnedNoRows) => { + // vec![cx.null().upcast(), cx.undefined().upcast()] + // } // Convert the error to a JavaScript exception on failure Err(err) => vec![cx.error(err.to_string())?.upcast()], From 0a79dd804cec171995f5918d2da06659d68f134f Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Wed, 8 Dec 2021 17:31:12 +0300 Subject: [PATCH 03/19] add newlines at the end of files --- .gitignore | 2 +- node-grove/index.js | 2 +- node-grove/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 47e81955a..ff67cd538 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /target .idea Cargo.lock -node-grove/node_modules \ No newline at end of file +node-grove/node_modules diff --git a/node-grove/index.js b/node-grove/index.js index b7425109b..c6fb58ac5 100644 --- a/node-grove/index.js +++ b/node-grove/index.js @@ -26,4 +26,4 @@ class GroveDB { } } -module.exports = GroveDB; \ No newline at end of file +module.exports = GroveDB; diff --git a/node-grove/package.json b/node-grove/package.json index 1bc7568d5..4a36c302c 100644 --- a/node-grove/package.json +++ b/node-grove/package.json @@ -26,4 +26,4 @@ "url": "https://github.com/dashevo/grovedb/issues" }, "homepage": "https://github.com/dashevo/grovedb#readme" -} \ No newline at end of file +} From 71075bf6a265e8a4b346d45bae98673a786c312e Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Tue, 14 Dec 2021 18:48:41 +0300 Subject: [PATCH 04/19] add correct argument parsing for the `get` method --- node-grove/index.js | 19 ++++++++++------ node-grove/src/lib.rs | 50 ++++++++++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/node-grove/index.js b/node-grove/index.js index c6fb58ac5..7f2874b93 100644 --- a/node-grove/index.js +++ b/node-grove/index.js @@ -2,7 +2,7 @@ const { promisify } = require("util"); -const { groveDbOpen, groveDbGet } = require("./index.node"); +const { groveDbOpen, groveDbGet, groveDbInsert, groveDbProof } = require("./index.node"); // Convert the DB methods from using callbacks to returning promises const groveDbGetAsync = promisify(groveDbGet); @@ -18,12 +18,19 @@ class GroveDB { return new GroveDB(db); } - // Wrap each method with a delegate to `this.db` - // This could be node in several other ways, for example binding assignment - // in the constructor - get(name) { - return groveDbGetAsync.call(this.db, name); + /** + * + * @param {Buffer[]} path + * @param {Buffer} key + * @returns {*} + */ + get(path, key) { + return groveDbGetAsync.call(this.db, path, key); } + + insert() {} + + proof() {} } module.exports = GroveDB; diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index 1f745f3eb..ff63a6c08 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::path::Path; use std::sync::mpsc; use std::thread; @@ -7,11 +8,6 @@ use neon::prelude::*; type DbCallback = Box; -// Wraps a SQLite connection a channel, allowing concurrent access -struct Database { - tx: mpsc::Sender, -} - // Messages sent on the database channel enum DbMessage { // Callback to be executed @@ -24,9 +20,11 @@ struct GroveDbWrapper { tx: mpsc::Sender, } -// Internal wrapper logic +// Internal wrapper logic. Needed to avoid issues with passing threads to node.js. +// Avoid thread conflicts bu having a dedicated thread thread for the groveDB +// and uses event to communicate with it impl GroveDbWrapper { - // Creates a new instance of `Database` + // Creates a new instance of `GroveDbWrapper` // // 1. Creates a connection and a channel // 2. Spawns a thread and moves the channel receiver and connection to it @@ -35,8 +33,8 @@ impl GroveDbWrapper { fn new(cx: &mut FunctionContext) -> Result { // TODO: error handling - let pathString = cx.argument::(0)?.value(cx); - let path = Path::new(&pathString); + let path_string = cx.argument::(0)?.value(cx); + let path = Path::new(&path_string); // Channel for sending callbacks to execute on the GroveDb connection thread let (tx, rx) = mpsc::channel::(); @@ -105,8 +103,24 @@ impl GroveDbWrapper { } fn js_get(mut cx: FunctionContext) -> JsResult { - // Get the first argument as a `JsNumber` and convert to an `f64` - let id = cx.argument::(0)?.value(&mut cx); + + let path_js_array_of_buffers = cx.argument::(0)?; + let buf_vec = path_js_array_of_buffers.to_vec(&mut cx)?; + let mut path_slices: Vec<&[u8]> = Vec::new(); + + let guard = cx.lock(); + + for buf in buf_vec { + let js_buf = buf.downcast_or_throw::(&mut cx)?; + let buf_handle = key_buffer.borrow(&guard); + let buf_slice = buf_handle.as_slice::(); + path_slices.push(buf_slice); + } + + // Converting JS key buffer to + let key_buffer = cx.argument::(1)?; + let key_handle = key_buffer.borrow(&guard); + let key_slice = key_handle.as_slice::(); // Get the second argument as a `JsFunction` let callback = cx.argument::(1)?.root(&mut cx); @@ -117,14 +131,14 @@ impl GroveDbWrapper { .downcast_or_throw::, _>(&mut cx)?; db.send(move |grove_db: &mut GroveDb, channel| { - let result: Result = grove_db.get(); + let result = grove_db.get(&path_slices, key_slice); channel.send(move |mut cx| { let callback = callback.into_inner(&mut cx); let this = cx.undefined(); let args: Vec> = match result { // Convert the name to a `JsString` on success and upcast to a `JsValue` - Ok(name) => vec![cx.null().upcast(), cx.string(name).upcast()], + Ok(element) => vec![cx.null().upcast(), cx.string(element).upcast()], // TODO: figure out what to do for the empty result // // If the row was not found, return `undefined` as a success instead @@ -147,12 +161,22 @@ impl GroveDbWrapper { // This function does not have a return value Ok(cx.undefined()) } + + fn js_insert(mut cx: FunctionContext) -> JsResult { + + } + + fn js_proof(mut cx: FunctionContext) -> JsResult { + Ok(cx.undefined()) + } } #[neon::main] fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("groveDbOpen", GroveDbWrapper::js_open)?; + cx.export_function("groveDbInsert", GroveDbWrapper::js_insert)?; cx.export_function("groveDbGet", GroveDbWrapper::js_get)?; + cx.export_function("groveDbProof", GroveDbWrapper::js_proof)?; Ok(()) } From 160962ec72c61095363840a9a7e4c372a8f5a300 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Tue, 14 Dec 2021 22:57:31 +0300 Subject: [PATCH 05/19] add some utilities to convert some values --- grovedb/src/lib.rs | 2 +- node-grove/index.js | 17 +++++++---- node-grove/src/converter.rs | 27 +++++++++++++++++ node-grove/src/lib.rs | 59 +++++++++++++++++++++++-------------- 4 files changed, 77 insertions(+), 28 deletions(-) create mode 100644 node-grove/src/converter.rs diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index fdbefe868..86cea2e3a 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -11,7 +11,7 @@ use std::{ use merk::{self, rocksdb, Merk}; use rs_merkle::{algorithms::Sha256, MerkleTree}; -use subtree::Element; +pub use subtree::Element; /// Limit of possible indirections const MAX_REFERENCE_HOPS: usize = 10; diff --git a/node-grove/index.js b/node-grove/index.js index 7f2874b93..923aa6df3 100644 --- a/node-grove/index.js +++ b/node-grove/index.js @@ -6,6 +6,9 @@ const { groveDbOpen, groveDbGet, groveDbInsert, groveDbProof } = require("./inde // Convert the DB methods from using callbacks to returning promises const groveDbGetAsync = promisify(groveDbGet); +const groveDbInsertAsync = promisify(groveDbInsert); +const groveDbOpenAsync = promisify(groveDbOpen); +const groveDbProofAsync = promisify(groveDbProof); // Wrapper class for the boxed `Database` for idiomatic JavaScript usage class GroveDB { @@ -13,8 +16,8 @@ class GroveDB { this.db = db; } - static open(path) { - const db = groveDbOpen(path); + static async open(path) { + const db = await groveDbOpenAsync(path); return new GroveDB(db); } @@ -24,13 +27,17 @@ class GroveDB { * @param {Buffer} key * @returns {*} */ - get(path, key) { + async get(path, key) { return groveDbGetAsync.call(this.db, path, key); } - insert() {} + async insert() { + return groveDbInsertAsync.call(this.db); + } - proof() {} + async proof() { + return groveDbProofAsync.call(this.db); + } } module.exports = GroveDB; diff --git a/node-grove/src/converter.rs b/node-grove/src/converter.rs new file mode 100644 index 000000000..270a90e62 --- /dev/null +++ b/node-grove/src/converter.rs @@ -0,0 +1,27 @@ +use grovedb::Element; +use neon::prelude::*; + +pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> NeonResult> { + let js_value = match element { + Element::Item(item) => { + let js_element = JsBuffer::external(cx, item.clone()); + js_element.upcast() + } + Element::Reference(reference) => { + let js_array: Handle = cx.empty_array(); + + for (index, bytes) in reference.iter().enumerate() { + let js_buffer = JsBuffer::external(cx, bytes.clone()); + js_array.set(cx, index as u32, js_buffer.as_value(cx))?; + } + + js_array.upcast() + } + Element::Tree(tree) => { + let js_element = JsBuffer::external(cx, tree.clone()); + js_element.upcast() + } + }; + + NeonResult::Ok(js_value) +} \ No newline at end of file diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index ff63a6c08..e056ca4cf 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -1,10 +1,13 @@ -use std::borrow::Borrow; +mod converter; + +use std::ops::Deref; use std::path::Path; use std::sync::mpsc; use std::thread; -use grovedb::{GroveDb, Error}; +use grovedb::{GroveDb, Error, Element}; use neon::prelude::*; +use neon::borrow::Borrow; type DbCallback = Box; @@ -30,7 +33,7 @@ impl GroveDbWrapper { // 2. Spawns a thread and moves the channel receiver and connection to it // 3. On a separate thread, read closures off the channel and execute with access // to the connection. - fn new(cx: &mut FunctionContext) -> Result + fn new(cx: &mut FunctionContext) -> NeonResult { // TODO: error handling let path_string = cx.argument::(0)?.value(cx); @@ -40,8 +43,18 @@ impl GroveDbWrapper { let (tx, rx) = mpsc::channel::(); // Open a connection to groveDb, this will be moved to a separate thread + // TODO: Convert grovedb error to neon error here + + + // match GroveDb::open(path) { + // Ok(grove_db) => {} + // Err(grove_db_error) => { + // return cx.error(grove_db_error.to_string())) + // } + // } let mut grove_db = GroveDb::open(path)?; + // Create an `Channel` for calling back to JavaScript. It is more efficient // to create a single channel and re-use it for all database callbacks. // The JavaScript process will not exit as long as this channel has not been @@ -103,7 +116,6 @@ impl GroveDbWrapper { } fn js_get(mut cx: FunctionContext) -> JsResult { - let path_js_array_of_buffers = cx.argument::(0)?; let buf_vec = path_js_array_of_buffers.to_vec(&mut cx)?; let mut path_slices: Vec<&[u8]> = Vec::new(); @@ -111,19 +123,21 @@ impl GroveDbWrapper { let guard = cx.lock(); for buf in buf_vec { - let js_buf = buf.downcast_or_throw::(&mut cx)?; - let buf_handle = key_buffer.borrow(&guard); - let buf_slice = buf_handle.as_slice::(); + let js_buffer_handle = buf.downcast_or_throw::(&mut cx)?; + let js_buffer = js_buffer_handle.deref(); + let path_fragment_memory_view = js_buffer.borrow(&guard); + let buf_slice = path_fragment_memory_view.as_slice::(); path_slices.push(buf_slice); } // Converting JS key buffer to - let key_buffer = cx.argument::(1)?; - let key_handle = key_buffer.borrow(&guard); - let key_slice = key_handle.as_slice::(); + let key_buffer_handle = cx.argument::(1)?; + let key_buffer = key_buffer_handle.deref(); + let key_memory_view = key_buffer.borrow(&guard); + let key_slice = key_memory_view.as_slice::(); - // Get the second argument as a `JsFunction` - let callback = cx.argument::(1)?.root(&mut cx); + // Get the JS callback as a `JsFunction` + let callback = cx.argument::(2)?.root(&mut cx); // Get the `this` value as a `JsBox` let db = cx @@ -138,17 +152,18 @@ impl GroveDbWrapper { let this = cx.undefined(); let args: Vec> = match result { // Convert the name to a `JsString` on success and upcast to a `JsValue` - Ok(element) => vec![cx.null().upcast(), cx.string(element).upcast()], - - // TODO: figure out what to do for the empty result - // // If the row was not found, return `undefined` as a success instead - // // of throwing an exception - // Err(rusqlite::Error::QueryReturnedNoRows) => { - // vec![cx.null().upcast(), cx.undefined().upcast()] - // } + Ok(element) => { + // First parameter of JS callbacks is error, which is null in this case + vec![ + cx.null().upcast(), + converter::element_to_js_value(element, &mut cx)? + ] + }, // Convert the error to a JavaScript exception on failure - Err(err) => vec![cx.error(err.to_string())?.upcast()], + Err(err) => vec![ + cx.error(err.to_string())?.upcast() + ], }; callback.call(&mut cx, this, args)?; @@ -163,7 +178,7 @@ impl GroveDbWrapper { } fn js_insert(mut cx: FunctionContext) -> JsResult { - + Ok(cx.undefined()) } fn js_proof(mut cx: FunctionContext) -> JsResult { From fc35fc6f09f8f425de75e9c84670b4ef4d3f63c9 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Wed, 15 Dec 2021 14:26:39 +0300 Subject: [PATCH 06/19] make it to compile finally!! --- node-grove/src/converter.rs | 26 ++++++++++++-- node-grove/src/lib.rs | 72 +++++++++++++------------------------ 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/node-grove/src/converter.rs b/node-grove/src/converter.rs index 270a90e62..1431aa881 100644 --- a/node-grove/src/converter.rs +++ b/node-grove/src/converter.rs @@ -1,5 +1,6 @@ -use grovedb::Element; +use grovedb::{Element, Error}; use neon::prelude::*; +use neon::borrow::Borrow; pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> NeonResult> { let js_value = match element { @@ -12,7 +13,8 @@ pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> for (index, bytes) in reference.iter().enumerate() { let js_buffer = JsBuffer::external(cx, bytes.clone()); - js_array.set(cx, index as u32, js_buffer.as_value(cx))?; + let js_value = js_buffer.as_value(cx); + js_array.set(cx, index as u32, js_value)?; } js_array.upcast() @@ -24,4 +26,24 @@ pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> }; NeonResult::Ok(js_value) +} + +pub fn js_buffer_to_vec_u8<'a, C: Context<'a>>(js_buffer: Handle, cx: &mut C) -> Vec { + let guard = cx.lock(); + // let key_buffer = js_buffer.deref(); + let key_memory_view = js_buffer.borrow(&guard); + let key_slice = key_memory_view.as_slice::(); + key_slice.to_vec() +} + +pub fn js_array_of_buffers_to_vec<'a, C: Context<'a>>(js_array: Handle, cx: &mut C) -> NeonResult>> { + let buf_vec = js_array.to_vec(cx)?; + let mut vec: Vec> = Vec::new(); + + for buf in buf_vec { + let js_buffer_handle = buf.downcast_or_throw::(cx)?; + vec.push(js_buffer_to_vec_u8(js_buffer_handle, cx)); + } + + Ok(vec) } \ No newline at end of file diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index e056ca4cf..55bef2b13 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -1,5 +1,6 @@ mod converter; +use std::fs::create_dir; use std::ops::Deref; use std::path::Path; use std::sync::mpsc; @@ -8,6 +9,7 @@ use std::thread; use grovedb::{GroveDb, Error, Element}; use neon::prelude::*; use neon::borrow::Borrow; +use neon::result::Throw; type DbCallback = Box; @@ -37,24 +39,10 @@ impl GroveDbWrapper { { // TODO: error handling let path_string = cx.argument::(0)?.value(cx); - let path = Path::new(&path_string); // Channel for sending callbacks to execute on the GroveDb connection thread let (tx, rx) = mpsc::channel::(); - // Open a connection to groveDb, this will be moved to a separate thread - // TODO: Convert grovedb error to neon error here - - - // match GroveDb::open(path) { - // Ok(grove_db) => {} - // Err(grove_db_error) => { - // return cx.error(grove_db_error.to_string())) - // } - // } - let mut grove_db = GroveDb::open(path)?; - - // Create an `Channel` for calling back to JavaScript. It is more efficient // to create a single channel and re-use it for all database callbacks. // The JavaScript process will not exit as long as this channel has not been @@ -65,6 +53,11 @@ impl GroveDbWrapper { // This will not block the JavaScript main thread and will continue executing // concurrently. thread::spawn(move || { + let path = Path::new(&path_string); + // Open a connection to groveDb, this will be moved to a separate thread + // TODO: think how to pass this error to JS + let mut grove_db = GroveDb::open(path).unwrap(); + // Blocks until a callback is available // When the instance of `Database` is dropped, the channel will be closed // and `rx.recv()` will return an `Err`, ending the loop and terminating @@ -92,7 +85,7 @@ impl GroveDbWrapper { self.tx.send(DbMessage::Close) } - fn send( + fn send_to_db_thread( &self, callback: impl FnOnce(&mut GroveDb, &Channel) + Send + 'static, ) -> Result<(), mpsc::SendError> { @@ -116,64 +109,49 @@ impl GroveDbWrapper { } fn js_get(mut cx: FunctionContext) -> JsResult { - let path_js_array_of_buffers = cx.argument::(0)?; - let buf_vec = path_js_array_of_buffers.to_vec(&mut cx)?; - let mut path_slices: Vec<&[u8]> = Vec::new(); - - let guard = cx.lock(); - - for buf in buf_vec { - let js_buffer_handle = buf.downcast_or_throw::(&mut cx)?; - let js_buffer = js_buffer_handle.deref(); - let path_fragment_memory_view = js_buffer.borrow(&guard); - let buf_slice = path_fragment_memory_view.as_slice::(); - path_slices.push(buf_slice); - } - - // Converting JS key buffer to - let key_buffer_handle = cx.argument::(1)?; - let key_buffer = key_buffer_handle.deref(); - let key_memory_view = key_buffer.borrow(&guard); - let key_slice = key_memory_view.as_slice::(); + let js_path = cx.argument::(0)?; + let js_key = cx.argument::(1)?; + let js_callback = cx.argument::(2)?.root(&mut cx); - // Get the JS callback as a `JsFunction` - let callback = cx.argument::(2)?.root(&mut cx); + let path = converter::js_array_of_buffers_to_vec(js_path, &mut cx)?; + let key = converter::js_buffer_to_vec_u8(js_key, &mut cx); // Get the `this` value as a `JsBox` let db = cx .this() .downcast_or_throw::, _>(&mut cx)?; - db.send(move |grove_db: &mut GroveDb, channel| { - let result = grove_db.get(&path_slices, key_slice); + db.send_to_db_thread(move |grove_db: &mut GroveDb, channel| { + let path_slice: Vec<&[u8]> = path.iter().map(|fragment| fragment.as_slice()).collect(); + let result = grove_db.get(&path_slice, &key); - channel.send(move |mut cx| { - let callback = callback.into_inner(&mut cx); - let this = cx.undefined(); - let args: Vec> = match result { + channel.send(move |mut task_context| { + let callback = js_callback.into_inner(&mut task_context); + let this = task_context.undefined(); + let callback_arguments: Vec> = match result { // Convert the name to a `JsString` on success and upcast to a `JsValue` Ok(element) => { // First parameter of JS callbacks is error, which is null in this case vec![ - cx.null().upcast(), - converter::element_to_js_value(element, &mut cx)? + task_context.null().upcast(), + converter::element_to_js_value(element, &mut task_context)? ] }, // Convert the error to a JavaScript exception on failure Err(err) => vec![ - cx.error(err.to_string())?.upcast() + task_context.error(err.to_string())?.upcast() ], }; - callback.call(&mut cx, this, args)?; + callback.call(&mut task_context, this, callback_arguments)?; Ok(()) }); }) .or_else(|err| cx.throw_error(err.to_string()))?; - // This function does not have a return value + // The result is returned through the callback, not through direct return Ok(cx.undefined()) } From d7ca4f41d06f763b9c9389f04f546904f06c8777 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Wed, 15 Dec 2021 14:29:43 +0300 Subject: [PATCH 07/19] remove unused imports --- node-grove/src/converter.rs | 2 +- node-grove/src/lib.rs | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/node-grove/src/converter.rs b/node-grove/src/converter.rs index 1431aa881..d28b9c459 100644 --- a/node-grove/src/converter.rs +++ b/node-grove/src/converter.rs @@ -1,4 +1,4 @@ -use grovedb::{Element, Error}; +use grovedb::{Element}; use neon::prelude::*; use neon::borrow::Borrow; diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index 55bef2b13..05591d63a 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -1,15 +1,11 @@ mod converter; -use std::fs::create_dir; -use std::ops::Deref; use std::path::Path; use std::sync::mpsc; use std::thread; -use grovedb::{GroveDb, Error, Element}; +use grovedb::{GroveDb}; use neon::prelude::*; -use neon::borrow::Borrow; -use neon::result::Throw; type DbCallback = Box; @@ -102,10 +98,10 @@ impl GroveDbWrapper { // Create a new instance of `Database` and place it inside a `JsBox` // JavaScript can hold a reference to a `JsBox`, but the contents are opaque fn js_open(mut cx: FunctionContext) -> JsResult> { - let dbWrapper = + let grove_db_wrapper = GroveDbWrapper::new(&mut cx).or_else(|err| cx.throw_error(err.to_string()))?; - Ok(cx.boxed(dbWrapper)) + Ok(cx.boxed(grove_db_wrapper)) } fn js_get(mut cx: FunctionContext) -> JsResult { From d9c381150e8e1265e9d330d1f6b5d4d61ab799f1 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Wed, 15 Dec 2021 18:23:23 +0300 Subject: [PATCH 08/19] implement a proper close method --- node-grove/index.js | 35 ++++++++++++--- node-grove/index.spec.js | 23 ++++++++++ node-grove/package-lock.json | 27 ++++++++++- node-grove/package.json | 6 ++- node-grove/src/lib.rs | 87 +++++++++++++++++++++++++++++++++--- 5 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 node-grove/index.spec.js diff --git a/node-grove/index.js b/node-grove/index.js index 923aa6df3..6752c2dc5 100644 --- a/node-grove/index.js +++ b/node-grove/index.js @@ -2,13 +2,15 @@ const { promisify } = require("util"); -const { groveDbOpen, groveDbGet, groveDbInsert, groveDbProof } = require("./index.node"); +// This file is crated when run `npm run build`. The actual source file that +// exports those functions is ./src/lib.rs +const { groveDbOpen, groveDbGet, groveDbInsert, groveDbProof, groveDbClose } = require("./index.node"); // Convert the DB methods from using callbacks to returning promises const groveDbGetAsync = promisify(groveDbGet); const groveDbInsertAsync = promisify(groveDbInsert); -const groveDbOpenAsync = promisify(groveDbOpen); const groveDbProofAsync = promisify(groveDbProof); +const groveDbCloseAsync = promisify(groveDbClose); // Wrapper class for the boxed `Database` for idiomatic JavaScript usage class GroveDB { @@ -16,8 +18,8 @@ class GroveDB { this.db = db; } - static async open(path) { - const db = await groveDbOpenAsync(path); + static open(path) { + const db = groveDbOpen(path); return new GroveDB(db); } @@ -31,13 +33,34 @@ class GroveDB { return groveDbGetAsync.call(this.db, path, key); } - async insert() { - return groveDbInsertAsync.call(this.db); + /** + * + * @param {Buffer[]} path + * @param {Buffer} key + * @param {Buffer} value + * @returns {Promise<*>} + */ + async insert(path, key, value) { + return groveDbInsertAsync.call(this.db, path, key, value); } + /** + * Not implemented in GroveDB yet + * + * @returns {Promise<*>} + */ async proof() { return groveDbProofAsync.call(this.db); } + + /** + * Closes connection to the DB + * + * @returns {Promise} + */ + async close() { + return groveDbCloseAsync.call(this.db); + } } module.exports = GroveDB; diff --git a/node-grove/index.spec.js b/node-grove/index.spec.js new file mode 100644 index 000000000..c2680a217 --- /dev/null +++ b/node-grove/index.spec.js @@ -0,0 +1,23 @@ +const GroveDB = require('./index.js'); +const rimraf = require('rimraf'); +const { promisify } = require("util"); +const removeTestDataFiles = promisify(rimraf); + +const testDataPath = './test_data'; + +describe('GroveDB', () => { + let groveDb; + + beforeEach(() => { + groveDb = GroveDB.open(testDataPath); + }); + + afterEach(async () => { + await groveDb.close(); + await removeTestDataFiles(testDataPath); + }); + + it('should store and retrieve a value', async function createGroveDb() { + console.log('Wow, so much test!'); + }); +}); \ No newline at end of file diff --git a/node-grove/package-lock.json b/node-grove/package-lock.json index 25762830b..dad79cf7e 100644 --- a/node-grove/package-lock.json +++ b/node-grove/package-lock.json @@ -11,7 +11,8 @@ "license": "MIT", "devDependencies": { "cargo-cp-artifact": "^0.1", - "mocha": "^8.4.0" + "mocha": "^8.4.0", + "rimraf": "^3.0.2" } }, "node_modules/@ungap/promise-all-settled": { @@ -763,6 +764,21 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1609,6 +1625,15 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", diff --git a/node-grove/package.json b/node-grove/package.json index 4a36c302c..c5b065099 100644 --- a/node-grove/package.json +++ b/node-grove/package.json @@ -6,12 +6,14 @@ "scripts": { "build": "cargo-cp-artifact -nc index.node -- cargo +nightly build --message-format=json-render-diagnostics", "install": "npm run build", - "test": "mocha" + "clean": "cargo +nightly clean", + "test": "mocha index.spec.js" }, "license": "MIT", "devDependencies": { "cargo-cp-artifact": "^0.1", - "mocha": "^8.4.0" + "mocha": "^8.4.0", + "rimraf": "^3.0.2" }, "repository": { "type": "git", diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index 05591d63a..dd6a5c49b 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -8,13 +8,14 @@ use grovedb::{GroveDb}; use neon::prelude::*; type DbCallback = Box; +type CloseCallback = Box; // Messages sent on the database channel enum DbMessage { // Callback to be executed Callback(DbCallback), // Indicates that the thread should be stopped and connection closed - Close, + Close(CloseCallback), } struct GroveDbWrapper { @@ -60,14 +61,17 @@ impl GroveDbWrapper { // the thread. while let Ok(message) = rx.recv() { match message { - DbMessage::Callback(f) => { + DbMessage::Callback(callback) => { // The connection and channel are owned by the thread, but _lent_ to // the callback. The callback has exclusive access to the connection // for the duration of the callback. - f(&mut grove_db, &channel); + callback(&mut grove_db, &channel); } // Immediately close the connection, even if there are pending messages - DbMessage::Close => break, + DbMessage::Close(callback) => { + callback(&channel); + break; + } } } }); @@ -77,8 +81,11 @@ impl GroveDbWrapper { // Idiomatic rust would take an owned `self` to prevent use after close // However, it's not possible to prevent JavaScript from continuing to hold a closed database - fn close(&self) -> Result<(), mpsc::SendError> { - self.tx.send(DbMessage::Close) + fn close( + &self, + callback: impl FnOnce(&Channel) + Send + 'static, + ) -> Result<(), mpsc::SendError> { + self.tx.send(DbMessage::Close(Box::new(callback))) } fn send_to_db_thread( @@ -152,12 +159,79 @@ impl GroveDbWrapper { } fn js_insert(mut cx: FunctionContext) -> JsResult { + let js_path = cx.argument::(0)?; + let js_key = cx.argument::(1)?; + let js_element = cx.argument::(2)?; + let js_callback = cx.argument::(3)?.root(&mut cx); + + let path = converter::js_array_of_buffers_to_vec(js_path, &mut cx)?; + let key = converter::js_buffer_to_vec_u8(js_key, &mut cx); + + // Get the `this` value as a `JsBox` + let db = cx + .this() + .downcast_or_throw::, _>(&mut cx)?; + + db.send_to_db_thread(move |grove_db: &mut GroveDb, channel| { + let path_slice: Vec<&[u8]> = path.iter().map(|fragment| fragment.as_slice()).collect(); + let result = grove_db.get(&path_slice, &key); + + channel.send(move |mut task_context| { + let callback = js_callback.into_inner(&mut task_context); + let this = task_context.undefined(); + let callback_arguments: Vec> = match result { + // Convert the name to a `JsString` on success and upcast to a `JsValue` + Ok(element) => { + // First parameter of JS callbacks is error, which is null in this case + vec![ + task_context.null().upcast(), + converter::element_to_js_value(element, &mut task_context)? + ] + }, + + // Convert the error to a JavaScript exception on failure + Err(err) => vec![ + task_context.error(err.to_string())?.upcast() + ], + }; + + callback.call(&mut task_context, this, callback_arguments)?; + + Ok(()) + }); + }) + .or_else(|err| cx.throw_error(err.to_string()))?; + Ok(cx.undefined()) } fn js_proof(mut cx: FunctionContext) -> JsResult { Ok(cx.undefined()) } + + fn js_close(mut cx: FunctionContext) -> JsResult { + let js_callback = cx.argument::(0)?.root(&mut cx); + + let db = cx + .this() + .downcast_or_throw::, _>(&mut cx)?; + + db.close(|channel| { + channel.send(move |mut task_context| { + let callback = js_callback.into_inner(&mut task_context); + let this = task_context.undefined(); + let callback_arguments: Vec> = vec![ + task_context.null().upcast(), + ]; + + callback.call(&mut task_context, this, callback_arguments)?; + + Ok(()) + }); + }).or_else(|err| cx.throw_error(err.to_string()))?; + + Ok(cx.undefined()) + } } #[neon::main] @@ -166,6 +240,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("groveDbInsert", GroveDbWrapper::js_insert)?; cx.export_function("groveDbGet", GroveDbWrapper::js_get)?; cx.export_function("groveDbProof", GroveDbWrapper::js_proof)?; + cx.export_function("groveDbClose", GroveDbWrapper::js_close)?; Ok(()) } From 102e51f3efac903c3a73ba3b89fb5532353a5263 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Wed, 15 Dec 2021 18:37:29 +0300 Subject: [PATCH 09/19] add some docs to the README.md --- node-grove/README.md | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/node-grove/README.md b/node-grove/README.md index a3285b90e..2d1f17560 100644 --- a/node-grove/README.md +++ b/node-grove/README.md @@ -6,22 +6,43 @@ `node-grove` is a groveDB binding for node.js -`node-grove` is -[available on crates.io](https://crates.io/crates/rs_merkle), and -[API Documentation is available on docs.rs](https://docs.rs/rs_merkle/). +`node-grove` is [available on npm](https://npmjs.org/node-grove) ## Usage -Add the following to your `Cargo.toml`: +Add the module to your project with `npm install node-grove`. -```toml -[dependencies] -rs_merkle = "1.0" +## Example + +```javascript +const GroveDB = require('node-grove'); + +async function main() { + const groveDb = GroveDB.open('./test.db'); + + await groveDb.insert(path, key, value); + + const result = await groveDb.get(path, key); + + // Don't forget to close connection when you no longer need it + await groveDb.close(); +} ``` -## Documentation +## Building and testing + +Run `npm run build` to build the package, `npm test` to test it. + +## How it works + +The main file that is used form the node.js side is `index.js`. It contains +class names `GroveDb`. The actual functions this class makes calls to are +stored in the `./src/lib.rs`. When building the project, it is compiled to +a file called `index.node`, that is imported into the `index.js` file. -[Documentation is available on docs.rs](https://docs.rs/rs_merkle/). +Please note that the binding itself contains a lot of code. This is due to +the fact that GroveDB is not thread-safe, and needs to live in its own thread. +It communicates with the main binding thread through messages. ## Contributing From a61f0e2cf82b78dc0f82a66779d25386b81b1e41 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Thu, 16 Dec 2021 18:05:20 +0300 Subject: [PATCH 10/19] Made bindings and all tests work --- grovedb/src/lib.rs | 2 +- node-grove/README.md | 27 +++++++- node-grove/index.js | 10 ++- node-grove/index.spec.js | 57 +++++++++++++++- node-grove/package-lock.json | 128 +++++++++++++++++++++++++++++++++++ node-grove/package.json | 1 + node-grove/src/converter.rs | 53 ++++++++++++++- node-grove/src/lib.rs | 25 +++---- 8 files changed, 278 insertions(+), 25 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 86cea2e3a..eaaabb150 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -27,7 +27,7 @@ pub enum Error { RocksDBError(#[from] merk::rocksdb::Error), #[error("unable to open Merk db")] MerkError(merk::Error), - #[error("invalid path")] + #[error("invalid path: {0}")] InvalidPath(&'static str), #[error("unable to decode")] BincodeError(#[from] bincode::Error), diff --git a/node-grove/README.md b/node-grove/README.md index 2d1f17560..ce3f8ad29 100644 --- a/node-grove/README.md +++ b/node-grove/README.md @@ -20,9 +20,32 @@ const GroveDB = require('node-grove'); async function main() { const groveDb = GroveDB.open('./test.db'); - await groveDb.insert(path, key, value); + const tree_key = Buffer.from("test_tree"); - const result = await groveDb.get(path, key); + const item_key = Buffer.from("test_key"); + const item_value = Buffer.from("very nice test value"); + + const root_tree_path = []; + const item_tree_path = [tree_key]; + + // Making a subtree to insert items into + await groveDb.insert( + root_tree_path, + tree_key, + { type: "tree", value: Buffer.alloc(32) + }); + + // Inserting an item into the subtree + await groveDb.insert( + item_tree_path, + item_key, + { type: "item", value: item_value } + ); + + const result = await groveDb.get(item_tree_path, item_key); + + // -> "very nice test value" + console.log(result.toString()); // Don't forget to close connection when you no longer need it await groveDb.close(); diff --git a/node-grove/index.js b/node-grove/index.js index 6752c2dc5..de435d871 100644 --- a/node-grove/index.js +++ b/node-grove/index.js @@ -27,7 +27,7 @@ class GroveDB { * * @param {Buffer[]} path * @param {Buffer} key - * @returns {*} + * @returns {Promise} */ async get(path, key) { return groveDbGetAsync.call(this.db, path, key); @@ -37,7 +37,7 @@ class GroveDB { * * @param {Buffer[]} path * @param {Buffer} key - * @param {Buffer} value + * @param {Element} value * @returns {Promise<*>} */ async insert(path, key, value) { @@ -63,4 +63,10 @@ class GroveDB { } } +/** + * @typedef Element + * @property {string} type - element type. Can be "item", "reference" or "tree" + * @property {Buffer|Buffer[]} value - element value + */ + module.exports = GroveDB; diff --git a/node-grove/index.spec.js b/node-grove/index.spec.js index c2680a217..7c1b87d2e 100644 --- a/node-grove/index.spec.js +++ b/node-grove/index.spec.js @@ -2,6 +2,7 @@ const GroveDB = require('./index.js'); const rimraf = require('rimraf'); const { promisify } = require("util"); const removeTestDataFiles = promisify(rimraf); +const { expect } = require('chai'); const testDataPath = './test_data'; @@ -18,6 +19,60 @@ describe('GroveDB', () => { }); it('should store and retrieve a value', async function createGroveDb() { - console.log('Wow, so much test!'); + const tree_key = Buffer.from("test_tree"); + + const item_key = Buffer.from("test_key"); + const item_value = Buffer.from("very nice test value"); + + const root_tree_path = []; + const item_tree_path = [tree_key]; + + // Making a subtree to insert items into + await groveDb.insert( + root_tree_path, + tree_key, + { type: "tree", value: Buffer.alloc(32) + }); + + // Inserting an item into the subtree + await groveDb.insert( + item_tree_path, + item_key, + { type: "item", value: item_value } + ); + + const result = await groveDb.get(item_tree_path, item_key); + + expect(result.toString()).to.be.equal("very nice test value"); }); + + describe('#insert', () => { + it('should be able to insert a tree', async () => { + await groveDb.insert([], Buffer.from("test_tree"), { type: "tree", value: Buffer.alloc(32) }) + }); + + it('should throw when trying to insert non-existent element type', async () => { + const path = []; + const key = Buffer.from("test_key"); + + try { + await groveDb.insert(path, key, { type: "not_a_tree", value: Buffer.alloc(32) }) + expect.fail("Expected to throw en error"); + } catch (e) { + expect(e.message).to.be.equal("Unexpected element type not_a_tree"); + } + }); + + it('should throw when trying to insert a tree that is not 32 bytes', async () => { + const path = []; + const key = Buffer.from("test_key"); + + try { + await groveDb.insert(path, key, { type: "tree", value: Buffer.alloc(1) }) + expect.fail("Expected to throw en error"); + } catch (e) { + expect(e.message).to.be.equal("Tree buffer is expected to be 32 bytes long, but got 1"); + } + }); + }) }); \ No newline at end of file diff --git a/node-grove/package-lock.json b/node-grove/package-lock.json index dad79cf7e..32ef78794 100644 --- a/node-grove/package-lock.json +++ b/node-grove/package-lock.json @@ -11,6 +11,7 @@ "license": "MIT", "devDependencies": { "cargo-cp-artifact": "^0.1", + "chai": "^4.3.4", "mocha": "^8.4.0", "rimraf": "^3.0.2" } @@ -73,6 +74,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -137,6 +147,23 @@ "cargo-cp-artifact": "bin/cargo-cp-artifact.js" } }, + "node_modules/chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -165,6 +192,15 @@ "node": ">=8" } }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", @@ -300,6 +336,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -402,6 +450,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -722,6 +779,15 @@ "node": ">=0.10.0" } }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -872,6 +938,15 @@ "node": ">=8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1121,6 +1196,12 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1170,6 +1251,20 @@ "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==", "dev": true }, + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1191,6 +1286,12 @@ } } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", @@ -1296,6 +1397,15 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -1364,6 +1474,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1595,6 +1711,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -1692,6 +1814,12 @@ "is-number": "^7.0.0" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/node-grove/package.json b/node-grove/package.json index c5b065099..465dd2ee9 100644 --- a/node-grove/package.json +++ b/node-grove/package.json @@ -12,6 +12,7 @@ "license": "MIT", "devDependencies": { "cargo-cp-artifact": "^0.1", + "chai": "^4.3.4", "mocha": "^8.4.0", "rimraf": "^3.0.2" }, diff --git a/node-grove/src/converter.rs b/node-grove/src/converter.rs index d28b9c459..612e16f8a 100644 --- a/node-grove/src/converter.rs +++ b/node-grove/src/converter.rs @@ -1,6 +1,53 @@ use grovedb::{Element}; -use neon::prelude::*; -use neon::borrow::Borrow; +use neon::{prelude::*, borrow::Borrow}; + +fn element_to_string(element: Element) -> String { + match element { + Element::Item(_) => { "item".to_string() } + Element::Reference(_) => { "reference".to_string() } + Element::Tree(_) => { "tree".to_string() } + } +} + +pub fn js_object_to_element<'a, C: Context<'a>>(js_object: Handle, cx: &mut C) -> NeonResult { + let js_element_string = js_object.get(cx, "type")?.to_string(cx)?; + let value = js_object.get(cx, "value")?; + + let element_string: String = js_element_string.value(cx); + + match element_string.as_str() { + "item" => { + let js_buffer = value.downcast_or_throw::(cx)?; + let item = js_buffer_to_vec_u8(js_buffer, cx); + Ok(Element::Item(item)) + }, + "reference" => { + let js_array = value.downcast_or_throw::(cx)?; + let reference = js_array_of_buffers_to_vec(js_array, cx)?; + Ok(Element::Reference(reference)) + }, + "tree" => { + let js_buffer = value.downcast_or_throw::(cx)?; + let tree_vec = js_buffer_to_vec_u8(js_buffer, cx); + Ok(Element::Tree( + tree_vec + .try_into() + .or_else(|v: Vec| { + cx.throw_error( + format!("Tree buffer is expected to be 32 bytes long, but got {}", v.len()) + ) + })? + )) + } + _ => { + cx.throw_error(format!("Unexpected element type {}", element_string)) + } + } +} + +pub fn element_to_js_object<'a, C: Context<'a>>(element: Element, cx: &mut C) -> NeonResult> { + Ok(cx.string("Nothing to look at").upcast()) +} pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> NeonResult> { let js_value = match element { @@ -46,4 +93,4 @@ pub fn js_array_of_buffers_to_vec<'a, C: Context<'a>>(js_array: Handle, } Ok(vec) -} \ No newline at end of file +} diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index dd6a5c49b..57ec4abdb 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -161,11 +161,12 @@ impl GroveDbWrapper { fn js_insert(mut cx: FunctionContext) -> JsResult { let js_path = cx.argument::(0)?; let js_key = cx.argument::(1)?; - let js_element = cx.argument::(2)?; + let js_element = cx.argument::(2)?; let js_callback = cx.argument::(3)?.root(&mut cx); let path = converter::js_array_of_buffers_to_vec(js_path, &mut cx)?; let key = converter::js_buffer_to_vec_u8(js_key, &mut cx); + let element = converter::js_object_to_element(js_element, &mut cx)?; // Get the `this` value as a `JsBox` let db = cx @@ -174,29 +175,17 @@ impl GroveDbWrapper { db.send_to_db_thread(move |grove_db: &mut GroveDb, channel| { let path_slice: Vec<&[u8]> = path.iter().map(|fragment| fragment.as_slice()).collect(); - let result = grove_db.get(&path_slice, &key); + let result = grove_db.insert(&path_slice, key, element); channel.send(move |mut task_context| { let callback = js_callback.into_inner(&mut task_context); let this = task_context.undefined(); let callback_arguments: Vec> = match result { - // Convert the name to a `JsString` on success and upcast to a `JsValue` - Ok(element) => { - // First parameter of JS callbacks is error, which is null in this case - vec![ - task_context.null().upcast(), - converter::element_to_js_value(element, &mut task_context)? - ] - }, - - // Convert the error to a JavaScript exception on failure - Err(err) => vec![ - task_context.error(err.to_string())?.upcast() - ], + Ok(_) => vec![task_context.null().upcast()], + Err(err) => vec![task_context.error(err.to_string())?.upcast()], }; callback.call(&mut task_context, this, callback_arguments)?; - Ok(()) }); }) @@ -205,10 +194,14 @@ impl GroveDbWrapper { Ok(cx.undefined()) } + /// Not implemented fn js_proof(mut cx: FunctionContext) -> JsResult { Ok(cx.undefined()) } + /// Sends a message to the DB thread to stop the thread and dispose the + /// groveDb instance owned by it, then calls js callback passed as a first + /// argument to the function fn js_close(mut cx: FunctionContext) -> JsResult { let js_callback = cx.argument::(0)?.root(&mut cx); From d8ed042139b36d9203f8a8c919b22a4247e44ae7 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Thu, 16 Dec 2021 18:07:53 +0300 Subject: [PATCH 11/19] Move npm package to the @dashevo org --- node-grove/README.md | 2 +- node-grove/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node-grove/README.md b/node-grove/README.md index ce3f8ad29..1cff529c9 100644 --- a/node-grove/README.md +++ b/node-grove/README.md @@ -59,7 +59,7 @@ Run `npm run build` to build the package, `npm test` to test it. ## How it works The main file that is used form the node.js side is `index.js`. It contains -class names `GroveDb`. The actual functions this class makes calls to are +class named `GroveDb`. The actual functions this class makes calls to are stored in the `./src/lib.rs`. When building the project, it is compiled to a file called `index.node`, that is imported into the `index.js` file. diff --git a/node-grove/package.json b/node-grove/package.json index 465dd2ee9..b1c3471df 100644 --- a/node-grove/package.json +++ b/node-grove/package.json @@ -1,5 +1,5 @@ { - "name": "node-grove", + "name": "@dashevo/node-grove", "version": "0.1.0", "description": "Neon binding for GroveDb", "main": "index.js", From 6c536c227149eb675db1af3a1ebfa04d37a050f8 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Thu, 16 Dec 2021 18:18:38 +0300 Subject: [PATCH 12/19] fix README.md --- node-grove/README.md | 28 +++++++++++++++------------- node-grove/index.spec.js | 5 +++-- node-grove/src/converter.rs | 17 +++++++++++------ 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/node-grove/README.md b/node-grove/README.md index 1cff529c9..fe7c53bab 100644 --- a/node-grove/README.md +++ b/node-grove/README.md @@ -15,37 +15,39 @@ Add the module to your project with `npm install node-grove`. ## Example ```javascript -const GroveDB = require('node-grove'); +const GroveDB = require('@dashevo/node-grove'); async function main() { const groveDb = GroveDB.open('./test.db'); - + const tree_key = Buffer.from("test_tree"); - + const item_key = Buffer.from("test_key"); const item_value = Buffer.from("very nice test value"); const root_tree_path = []; const item_tree_path = [tree_key]; - + // Making a subtree to insert items into await groveDb.insert( - root_tree_path, - tree_key, - { type: "tree", value: Buffer.alloc(32) - }); - + root_tree_path, + tree_key, + { type: "tree", value: Buffer.alloc(32) + }); + // Inserting an item into the subtree await groveDb.insert( - item_tree_path, - item_key, + item_tree_path, + item_key, { type: "item", value: item_value } ); - const result = await groveDb.get(item_tree_path, item_key); + const element = await groveDb.get(item_tree_path, item_key); + // -> "item" + console.log(element.type); // -> "very nice test value" - console.log(result.toString()); + console.log(element.value.toString()); // Don't forget to close connection when you no longer need it await groveDb.close(); diff --git a/node-grove/index.spec.js b/node-grove/index.spec.js index 7c1b87d2e..cf2ec505a 100644 --- a/node-grove/index.spec.js +++ b/node-grove/index.spec.js @@ -41,9 +41,10 @@ describe('GroveDB', () => { { type: "item", value: item_value } ); - const result = await groveDb.get(item_tree_path, item_key); + const element = await groveDb.get(item_tree_path, item_key); - expect(result.toString()).to.be.equal("very nice test value"); + expect(element.type).to.be.equal("item"); + expect(element.value.toString()).to.be.equal("very nice test value"); }); describe('#insert', () => { diff --git a/node-grove/src/converter.rs b/node-grove/src/converter.rs index 612e16f8a..32fcb39ee 100644 --- a/node-grove/src/converter.rs +++ b/node-grove/src/converter.rs @@ -50,10 +50,14 @@ pub fn element_to_js_object<'a, C: Context<'a>>(element: Element, cx: &mut C) -> } pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> NeonResult> { - let js_value = match element { + let js_object = cx.empty_object(); + let js_type_string = cx.string(element_to_string(element.clone())); + js_object.set(cx, "type", js_type_string); + + let js_value: Handle = match element { Element::Item(item) => { - let js_element = JsBuffer::external(cx, item.clone()); - js_element.upcast() + let js_buffer = JsBuffer::external(cx, item.clone()); + js_buffer.upcast() } Element::Reference(reference) => { let js_array: Handle = cx.empty_array(); @@ -67,12 +71,13 @@ pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> js_array.upcast() } Element::Tree(tree) => { - let js_element = JsBuffer::external(cx, tree.clone()); - js_element.upcast() + let js_buffer = JsBuffer::external(cx, tree.clone()); + js_buffer.upcast() } }; - NeonResult::Ok(js_value) + js_object.set(cx, "value", js_value); + NeonResult::Ok(js_object.upcast()) } pub fn js_buffer_to_vec_u8<'a, C: Context<'a>>(js_buffer: Handle, cx: &mut C) -> Vec { From 2224e0157859e8e99b97b127ff65358a3e8a4e28 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Thu, 16 Dec 2021 21:54:31 +0300 Subject: [PATCH 13/19] Cleanup files and prepare the build --- README.md | 1 + node-grove/README.md | 6 ++--- node-grove/index.js | 2 +- node-grove/src/converter.rs | 8 ++---- node-grove/src/lib.rs | 2 +- .../package-lock.json => package-lock.json | 16 +++++------ node-grove/package.json => package.json | 27 +++++++++++++++---- 7 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 README.md rename node-grove/package-lock.json => package-lock.json (99%) rename node-grove/package.json => package.json (50%) diff --git a/README.md b/README.md new file mode 100644 index 000000000..c9c07705e --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# GroveDB \ No newline at end of file diff --git a/node-grove/README.md b/node-grove/README.md index fe7c53bab..41f6b409e 100644 --- a/node-grove/README.md +++ b/node-grove/README.md @@ -17,7 +17,7 @@ Add the module to your project with `npm install node-grove`. ```javascript const GroveDB = require('@dashevo/node-grove'); -async function main() { +(async function main() { const groveDb = GroveDB.open('./test.db'); const tree_key = Buffer.from("test_tree"); @@ -48,10 +48,10 @@ async function main() { console.log(element.type); // -> "very nice test value" console.log(element.value.toString()); - + // Don't forget to close connection when you no longer need it await groveDb.close(); -} +})().catch(console.error); ``` ## Building and testing diff --git a/node-grove/index.js b/node-grove/index.js index de435d871..1f55db046 100644 --- a/node-grove/index.js +++ b/node-grove/index.js @@ -4,7 +4,7 @@ const { promisify } = require("util"); // This file is crated when run `npm run build`. The actual source file that // exports those functions is ./src/lib.rs -const { groveDbOpen, groveDbGet, groveDbInsert, groveDbProof, groveDbClose } = require("./index.node"); +const { groveDbOpen, groveDbGet, groveDbInsert, groveDbProof, groveDbClose } = require("../index.node"); // Convert the DB methods from using callbacks to returning promises const groveDbGetAsync = promisify(groveDbGet); diff --git a/node-grove/src/converter.rs b/node-grove/src/converter.rs index 32fcb39ee..4eccd54b1 100644 --- a/node-grove/src/converter.rs +++ b/node-grove/src/converter.rs @@ -46,13 +46,9 @@ pub fn js_object_to_element<'a, C: Context<'a>>(js_object: Handle, cx: } pub fn element_to_js_object<'a, C: Context<'a>>(element: Element, cx: &mut C) -> NeonResult> { - Ok(cx.string("Nothing to look at").upcast()) -} - -pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> NeonResult> { let js_object = cx.empty_object(); let js_type_string = cx.string(element_to_string(element.clone())); - js_object.set(cx, "type", js_type_string); + js_object.set(cx, "type", js_type_string)?; let js_value: Handle = match element { Element::Item(item) => { @@ -76,7 +72,7 @@ pub fn element_to_js_value<'a, C: Context<'a>>(element: Element, cx: &mut C) -> } }; - js_object.set(cx, "value", js_value); + js_object.set(cx, "value", js_value)?; NeonResult::Ok(js_object.upcast()) } diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index 57ec4abdb..0a864581e 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -137,7 +137,7 @@ impl GroveDbWrapper { // First parameter of JS callbacks is error, which is null in this case vec![ task_context.null().upcast(), - converter::element_to_js_value(element, &mut task_context)? + converter::element_to_js_object(element, &mut task_context)? ] }, diff --git a/node-grove/package-lock.json b/package-lock.json similarity index 99% rename from node-grove/package-lock.json rename to package-lock.json index 32ef78794..a4b68b526 100644 --- a/node-grove/package-lock.json +++ b/package-lock.json @@ -1,16 +1,18 @@ { - "name": "node-grove", - "version": "0.1.0", + "name": "@dashevo/node-grove", + "version": "0.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "node-grove", - "version": "0.1.0", + "name": "@dashevo/node-grove", + "version": "0.0.2", "hasInstallScript": true, "license": "MIT", + "dependencies": { + "cargo-cp-artifact": "^0.1.6" + }, "devDependencies": { - "cargo-cp-artifact": "^0.1", "chai": "^4.3.4", "mocha": "^8.4.0", "rimraf": "^3.0.2" @@ -142,7 +144,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz", "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==", - "dev": true, "bin": { "cargo-cp-artifact": "bin/cargo-cp-artifact.js" } @@ -1248,8 +1249,7 @@ "cargo-cp-artifact": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz", - "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==", - "dev": true + "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==" }, "chai": { "version": "4.3.4", diff --git a/node-grove/package.json b/package.json similarity index 50% rename from node-grove/package.json rename to package.json index b1c3471df..6733134b5 100644 --- a/node-grove/package.json +++ b/package.json @@ -1,17 +1,31 @@ { "name": "@dashevo/node-grove", - "version": "0.1.0", + "version": "0.0.9", "description": "Neon binding for GroveDb", - "main": "index.js", + "main": "node-grove/index.js", + "readme": "node-grove/README.md", "scripts": { "build": "cargo-cp-artifact -nc index.node -- cargo +nightly build --message-format=json-render-diagnostics", + "prepare": "mv README.md README.md.old && cp node-grove/README.md README.md", "install": "npm run build", "clean": "cargo +nightly clean", - "test": "mocha index.spec.js" + "test": "mocha node-grove/index.spec.js", + "postpublish": "rm README.md && mv README.md.old README.md" }, + "files": [ + "node-grove/index.js", + "node-grove/src", + "node-grove/Cargo.toml", + "node-grove/README.md", + "grovedb/src", + "grovedb/Cargo.toml", + "merk/src", + "merk/Cargo.toml", + "Cargo.toml", + "Cargo.lock" + ], "license": "MIT", "devDependencies": { - "cargo-cp-artifact": "^0.1", "chai": "^4.3.4", "mocha": "^8.4.0", "rimraf": "^3.0.2" @@ -28,5 +42,8 @@ "bugs": { "url": "https://github.com/dashevo/grovedb/issues" }, - "homepage": "https://github.com/dashevo/grovedb#readme" + "homepage": "https://github.com/dashevo/grovedb#readme", + "dependencies": { + "cargo-cp-artifact": "^0.1.6" + } } From d552aed5c3b0c923ae4eae0081047d515cbc72aa Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Thu, 16 Dec 2021 21:55:07 +0300 Subject: [PATCH 14/19] Add index.node to the .gitignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ff67cd538..f01a2e6ba 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea Cargo.lock node-grove/node_modules +index.node \ No newline at end of file From 6d8154337ba6160b1e6b802b43202f63c6708748 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Thu, 16 Dec 2021 21:59:08 +0300 Subject: [PATCH 15/19] add a newline at the end of .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f01a2e6ba..33e98ea11 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ .idea Cargo.lock node-grove/node_modules -index.node \ No newline at end of file +index.node From 3d98e670b31614b7b2cbffa0761ccf3ebe341dca Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Thu, 16 Dec 2021 22:08:58 +0300 Subject: [PATCH 16/19] fix links in the node-grove README.md --- node-grove/README.md | 10 ++++------ package.json | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/node-grove/README.md b/node-grove/README.md index 41f6b409e..74a28a6a6 100644 --- a/node-grove/README.md +++ b/node-grove/README.md @@ -1,16 +1,14 @@ # node-grove -[![Rayon crate](https://img.shields.io/crates/v/rs_merkle.svg)](https://crates.io/crates/rs_merkle) -[![Rayon documentation](https://docs.rs/rs_merkle/badge.svg)](https://docs.rs/rs_merkle) -[![Build and test](https://github.com/antouhou/rs-merkle/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/antouhou/rs-merkle/actions) +[![GroveDB npm package](https://img.shields.io/npm/v/@dashevo/node-grove.svg)](https://www.npmjs.com/package/@dashevo/node-grove) -`node-grove` is a groveDB binding for node.js +`node-grove` is a GroveDB binding for node.js -`node-grove` is [available on npm](https://npmjs.org/node-grove) +`node-grove` is [available on npm](https://www.npmjs.com/package/@dashevo/node-grove) ## Usage -Add the module to your project with `npm install node-grove`. +Add the module to your project with `npm install @dashevo/node-grove`. ## Example diff --git a/package.json b/package.json index 6733134b5..67cb68457 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dashevo/node-grove", - "version": "0.0.9", + "version": "0.0.10", "description": "Neon binding for GroveDb", "main": "node-grove/index.js", "readme": "node-grove/README.md", From aeec49f8dcc306e6de202db05699554dfdbede4b Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Fri, 17 Dec 2021 11:56:45 +0300 Subject: [PATCH 17/19] add a newline at the end of bindings test --- .gitignore | 2 +- node-grove/index.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 33e98ea11..55cd4f693 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target .idea Cargo.lock -node-grove/node_modules index.node +node_modules \ No newline at end of file diff --git a/node-grove/index.spec.js b/node-grove/index.spec.js index cf2ec505a..fc160c7cf 100644 --- a/node-grove/index.spec.js +++ b/node-grove/index.spec.js @@ -76,4 +76,4 @@ describe('GroveDB', () => { } }); }) -}); \ No newline at end of file +}); From 33da2df49bf3b1ce40352bbb54bd6910166857ad Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Fri, 17 Dec 2021 11:57:20 +0300 Subject: [PATCH 18/19] add a newline at the end of .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 55cd4f693..0d4139261 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ .idea Cargo.lock index.node -node_modules \ No newline at end of file +node_modules From 06af033d632a962a67cdd2d4c06d3344b5e7d738 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Fri, 17 Dec 2021 13:14:00 +0300 Subject: [PATCH 19/19] fix grammar in some comments --- node-grove/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/node-grove/src/lib.rs b/node-grove/src/lib.rs index 0a864581e..f1130295a 100644 --- a/node-grove/src/lib.rs +++ b/node-grove/src/lib.rs @@ -23,8 +23,8 @@ struct GroveDbWrapper { } // Internal wrapper logic. Needed to avoid issues with passing threads to node.js. -// Avoid thread conflicts bu having a dedicated thread thread for the groveDB -// and uses event to communicate with it +// Avoiding thread conflicts by having a dedicated thread for the groveDB instance +// and uses events to communicate with it impl GroveDbWrapper { // Creates a new instance of `GroveDbWrapper` // @@ -34,7 +34,6 @@ impl GroveDbWrapper { // to the connection. fn new(cx: &mut FunctionContext) -> NeonResult { - // TODO: error handling let path_string = cx.argument::(0)?.value(cx); // Channel for sending callbacks to execute on the GroveDb connection thread @@ -132,7 +131,6 @@ impl GroveDbWrapper { let callback = js_callback.into_inner(&mut task_context); let this = task_context.undefined(); let callback_arguments: Vec> = match result { - // Convert the name to a `JsString` on success and upcast to a `JsValue` Ok(element) => { // First parameter of JS callbacks is error, which is null in this case vec![