diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..add1e2df0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,33 @@ +#.dockerignore +*~ +Dockerfile +.git/ + +#.gitignore +*.dylib +*.so +*.o +*.lo +*.Makefile +*.target.gyp.mk +lib/binding +build +out +Release +Debug +node_modules +.deps +Makefile.gyp +gyp-mac-tool +.dirstamp +npm-debug.log +test/support/big.db +test/tmp +test/nw/app.nw +.DS_Store +.idea +.dtps +local.env +.mason +.eslintrc.js +setup.sh \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..08043675f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + "extends": "eslint:recommended", + "env": { + "node": true + }, + "rules": { + "indent": ["error", 4], + "linebreak-style": ["error", "unix"], + "semi": ["error", "always"], + "no-cond-assign": ["error", "always"] + } +} diff --git a/.gitignore b/.gitignore index 1197bbe86..b3634130d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,10 @@ test/tmp test/nw/app.nw .DS_Store .idea +.dtps +local.env +.mason +.eslintrc.js +setup.sh +/build-tmp-napi-v3 +/build-tmp-napi-v6 diff --git a/.npmignore b/.npmignore index 733dbe72a..05b181283 100644 --- a/.npmignore +++ b/.npmignore @@ -10,3 +10,11 @@ Makefile scripts .travis.yml examples +.dtps +local.env +.mason +.eslintrc.js +setup.sh +cloudformation/ci.template.js +appveyor.yml +.vscode \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 429dd4bf5..d07a1d556 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ sudo: false language: generic +dist: precise + git: depth: 10 @@ -13,87 +15,160 @@ addons: packages: - clang-3.5 -# don't re-build for tags so that [publish binary] is not re-run -# https://github.com/travis-ci/travis-ci/issues/1532 -branches: - except: - - /^v[0-9]/ - matrix: include: # Linux - os: linux + dist: trusty compiler: clang - env: NODE_VERSION="6" + env: NODE_VERSION="14" addons: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux + dist: trusty compiler: clang - env: NODE_VERSION="5" + env: NODE_VERSION="13" addons: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux + dist: trusty compiler: clang - env: NODE_VERSION="4" + env: NODE_VERSION="12" addons: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="0.12" + env: NODE_VERSION="11" addons: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="0.10" + env: NODE_VERSION="10" addons: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - # test building against external sqlite + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + # OS X + - os: osx + compiler: clang + env: NODE_VERSION="14" # node abi 83 + - os: osx + compiler: clang + env: NODE_VERSION="13" # node abi 79 + - os: osx + compiler: clang + env: NODE_VERSION="12" # node abi 72 + - os: osx + compiler: clang + env: NODE_VERSION="11" # node abi 67 + - os: osx + compiler: clang + env: NODE_VERSION="10" # node abi 67 + # electron Linux - os: linux compiler: clang - env: NODE_VERSION="0.10.40" EXTERNAL_SQLITE=true PUBLISHABLE=false + env: NODE_VERSION="12.13.0" ELECTRON_VERSION="8.2.0" + dist: trusty addons: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5','libsqlite3-dev'] - # OS X + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + - os: linux + compiler: clang + env: NODE_VERSION="12.13.0" ELECTRON_VERSION="8.1.0" + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + - os: linux + compiler: clang + env: NODE_VERSION="12.13.0" ELECTRON_VERSION="8.0.0" + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + - os: linux + compiler: clang + env: NODE_VERSION="12.8.1" ELECTRON_VERSION="7.2.0" + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + - os: linux + compiler: clang + env: NODE_VERSION="12.8.1" ELECTRON_VERSION="7.1.0" + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + - os: linux + compiler: clang + env: NODE_VERSION="12.8.1" ELECTRON_VERSION="7.0.0" + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + - os: linux + compiler: clang + env: NODE_VERSION="12.4.0" ELECTRON_VERSION="6.1.0" + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + - os: linux + compiler: clang + env: NODE_VERSION="12.4.0" ELECTRON_VERSION="6.0.0" + dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] + # electron MacOs + - os: osx + compiler: clang + env: NODE_VERSION="12.13.0" ELECTRON_VERSION="8.2.0" + - os: osx + compiler: clang + env: NODE_VERSION="12.13.0" ELECTRON_VERSION="8.1.0" - os: osx compiler: clang - env: NODE_VERSION="5" COVERAGE=true PUBLISHABLE=false # node abi 47 + env: NODE_VERSION="12.13.0" ELECTRON_VERSION="8.0.0" - os: osx compiler: clang - env: NODE_VERSION="6" # node abi 48 + env: NODE_VERSION="12.8.1" ELECTRON_VERSION="7.2.0" - os: osx compiler: clang - env: NODE_VERSION="5" # node abi 47 + env: NODE_VERSION="12.8.1" ELECTRON_VERSION="7.1.0" - os: osx compiler: clang - env: NODE_VERSION="4" # node abi 46 + env: NODE_VERSION="12.8.1" ELECTRON_VERSION="7.0.0" - os: osx compiler: clang - env: NODE_VERSION="0.12" # node abi 14 + env: NODE_VERSION="12.4.0" ELECTRON_VERSION="6.1.0" - os: osx compiler: clang - env: NODE_VERSION="0.10" # node abi 11 + env: NODE_VERSION="12.4.0" ELECTRON_VERSION="6.0.0" env: global: - JOBS: "8" - - secure: PifMOSnn+mWR1RUptXse+fLvWiTrzg0R/mazO7RWhXHWBKv0uAJ/qV3dI0GIRBLtjG10Iy+tT5RNh1TIbBzB9Y67wMcGvylUPG1+3EOKoBMEPnOD9AgCEQw4SOXfGPx0cq2N6ueSKieCgu1yKN9Wq7XCbE+zTk/DiRNIdLirVoo= - - secure: cc4esJY1vPXL31IeumAJoKWDDO2BTGFiltwfO1jbTbiV7QT911QUjTUasxXIVpOaHNCpxSTyevPwwTWfzt2EtF92Lli+qhQ2bbzMiDSBZstSrHdAe62Ai2M1oYYUwk/0cABB/2nO9uRyYwITCxpTSNzZBrYhn3C29WqBhPeVDmM= before_install: - export PUBLISHABLE=${PUBLISHABLE:-true} -- export COVERAGE=${COVERAGE:-false} - if [[ $(uname -s) == 'Linux' ]]; then export CXX="clang++-3.5"; export CC="clang-3.5"; @@ -111,15 +186,15 @@ install: before_script: # get commit message - export COMMIT_MESSAGE=$(git show -s --format=%B $TRAVIS_COMMIT | tr -d '\n') -- if [[ ${COVERAGE} == true ]]; then - if [[ $(uname -s) == 'Linux' ]]; then - PYTHONUSERBASE=$(pwd)/py-local pip install --user cpp-coveralls; - else - PYTHONUSERBASE=$(pwd)/py-local easy_install --user cpp-coveralls; - fi; - fi script: -- if [[ "${NODE_VERSION}" ]]; then ./scripts/build_against_node.sh; fi; +- if [[ "${NODE_VERSION}" ]]; then + if [[ "${ELECTRON_VERSION}" ]]; then + ./scripts/build_against_electron.sh; + else + ./scripts/build_against_node.sh; + fi; + fi +- if [[ "${NODE_VERSION}" -eq "4" ]]; then ./node_modules/.bin/eslint lib; fi; # disabled for now: need to port to sudo:false #- if [[ "${NODE_WEBKIT}" ]]; then ./scripts/build_against_node_webkit.sh; fi; diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bcf12c43..a3553895c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,140 @@ # Changelog +## 5.0.2 +- build: rebuild binaries before publishing [#1426](https://github.com/mapbox/node-sqlite3/pull/1426) + +## 5.0.1 +- dep: node-addon-api to ^3.0.0 [#1367](https://github.com/mapbox/node-sqlite3/pull/1367) +- bug: bad comparison of c string [#1347](https://github.com/mapbox/node-sqlite3/pull/1347) +- build: Install files to be deployed [#1352](https://github.com/mapbox/node-sqlite3/pull/1352) +- sqlite3: upgrade to 3.32.3 [#1351](https://github.com/mapbox/node-sqlite3/pull/1351) +- bug: worker threads crash [#1367](https://github.com/mapbox/node-sqlite3/pull/1367) +- bug: segfaults [#1368](https://github.com/mapbox/node-sqlite3/pull/1368) +- typo: broken link to MapBox site [#1369](https://github.com/mapbox/node-sqlite3/pull/1369) + +## 5.0.0 +- prebuilt: Node 14 support, dropped support for all version of Node < 10 [#1304](https://github.com/mapbox/node-sqlite3/pull/1304) +- prebuilt: add electron 7.2 [#1324](https://github.com/mapbox/node-sqlite3/pull/1324) +- napi: refactor codebase to use N-API instead of NAN (+ various improvements) [#1304](https://github.com/mapbox/node-sqlite3/pull/1304) +- trace: don't require throw to add trace info for verbose [#1317](https://github.com/mapbox/node-sqlite3/pull/1317) +- ci: remove permission setting [#1319](https://github.com/mapbox/node-sqlite3/pull/1319) + +## 4.2.0 +- electron: Electron v8, v8.1.x & v8.2.x [#1294](https://github.com/mapbox/node-sqlite3/pull/1294) [#1308](https://github.com/mapbox/node-sqlite3/pull/1308) +- sqlite3: update to 3.31.1 (3310100) [#1289](https://github.com/mapbox/node-sqlite3/pull/1289) +- webpack: split sqlite3-binding.js out so that it could be override by webpack [#1268](https://github.com/mapbox/node-sqlite3/pull/1268) +- sqlite3: enable 'SQLITE_ENABLE_DBSTAT_VTAB=1' [#1281](https://github.com/mapbox/node-sqlite3/pull/1281) +- deps: remove request [#1287](https://github.com/mapbox/node-sqlite3/pull/1287) +- deps: alternative update of node-gyp for electron (v1 - v4), windows [#1283](https://github.com/mapbox/node-sqlite3/pull/1283) +- electron: fix dist url [#1282](https://github.com/mapbox/node-sqlite3/pull/1282) +- docs: Added json1 support note [#1303](https://github.com/mapbox/node-sqlite3/pull/1303) + +## 4.1.1 +- Electron v6.1 and v7 support [#1237](https://github.com/mapbox/node-sqlite3/pull/1237) +- Electron v7.1 support [#1254](https://github.com/mapbox/node-sqlite3/pull/1254) +- SQLite3 update to 3.30.1 [#1238](https://github.com/mapbox/node-sqlite3/pull/1238) +- Overwrite 'msbuild_toolset' only if 'toolset' is defined [#1242](https://github.com/mapbox/node-sqlite3/pull/1242) +- Upgrade CI to node-gyp 6.x for Windows Electron v5 & v6 builds [#1245](https://github.com/mapbox/node-sqlite3/pull/1245) +- Node v13 support [#1247](https://github.com/mapbox/node-sqlite3/pull/1247) +- Use minimum supported node version for Electron 7 [#1255](https://github.com/mapbox/node-sqlite3/pull/1255) + +## 4.1.0 + +- Electron v6 support [#1195](https://github.com/mapbox/node-sqlite3/pull/1195) +- Electron v4.1 and v4.2 support [#1180](https://github.com/mapbox/node-sqlite3/pull/1180) +- Custom file header with `--sqlite_magic` [#1144](https://github.com/mapbox/node-sqlite3/pull/1144) +- https everywhere [#1177](https://github.com/mapbox/node-sqlite3/pull/1177) + +## 4.0.9 +- Use trusty as the base for prebuilts [#1167](https://github.com/mapbox/node-sqlite3/pull/1167) + +## 4.0.8 +- Rerelease of 4.0.7 but removed excess .vscode files [0df90c7](https://github.com/mapbox/node-sqlite3/commit/0df90c7811331169ad5f8fbad396422e72757af3) + +## 4.0.7 + +- Node v12 support +- Electron v5 support +- Fix backup API tests +- HAVE_USLEEP=1 for all platforms +- docker suport + +## 4.0.6 +- Release of 4.0.5 (again due CI) + +## 4.0.5 +- **SECURITY:** Upgrade SQLite to 3.26.0 [#1088](https://github.com/mapbox/node-sqlite3/pull/1088) +- add constants for file open (shared databases) [#1078](https://github.com/mapbox/node-sqlite3/pull/1078) +- Allow specifying the python to use [#1089](https://github.com/mapbox/node-sqlite3/pull/1089) + +## 4.0.4 +- Add NodeJS 11 support [#1072](https://github.com/mapbox/node-sqlite3/pull/1072) +- Add electron osx 3.0.0 support [#1071](https://github.com/mapbox/node-sqlite3/pull/1071) + +## 4.0.3 + +- Increase electron/osx binary coverage [#1041](https://github.com/mapbox/node-sqlite3/pull/1041) (@kewde) + +## 4.0.2 + +- Fixed HTTP proxy support by using `request` over `needle` in node-pre-gyp + +## 4.0.1 + +- Node v10 support +- Upgrade to node-pre-gyp@0.10.1 +- Upgrade to nan@2.10.0 +- Upgrade to sqlite v3.24.0 +- Stopped bundling node-pre-gyp +- Upgrade to mocha@5 +- Now building electron binaries (@kewde) +- Add OPEN_FULLMUTEX constant + +## 4.0.0 + + - Drop support for Node v0.10 and v.12 + - Upgrade to node-pre-gyp@0.9.0 + - Upgrade to nan@2.9.2 + +## 3.1.13 + +- Attempt to fix regression of #866 + +## 3.1.12 + +- Fixed to ensure the binaries do not rely on `GLIBC_2.14` and only `GLIBC_2.2.5`. This regressed in v3.1.11. + +## 3.1.11 + +- Fixed building from source on alpine linux + +## 3.1.10 + +- Removed `npm ls` from `prepublish` hook per mapbox/node-pre-gyp#291 +- Upgraded node-pre-gyp to v0.6.37 +- Removed accidentally committed large file + +## 3.1.9 + +- Added support for node v8 and upgraded `nan`, `node-pre-gyp` deps. + +## 3.1.8 + +- Added support for node v7 (pre-compiled binaries available) + +## 3.1.7 + +- Upgrade sqlite to 3.15, enable FTS4, FTS5 (@wmertens) +- Upgrade to node-pre-gyp@0.6.31 and nan@2.4.0 + +## 3.1.6 + +- Starts bundling node-pre-gyp again to avoid #720 + +## 3.1.5 + +- [Added support for sqlite3_interrupt](https://github.com/mapbox/node-sqlite3/pull/518): this makes + it possible to interrupt a long-running query. +- [Fixes uv_ref race](https://github.com/mapbox/node-sqlite3/pull/705). ## 3.1.4 @@ -19,7 +155,7 @@ - Support for node 5.x - Upgraded SQLite to 3.9.1: https://www.sqlite.org/releaselog/3_9_1.html - - Enabled json1 extension by default + - Enabled json1 extension by default ## 3.1.0 @@ -57,7 +193,7 @@ - Upgraded node-pre-gyp@0.6.5 - Upgraded nan@1.8.4 - Fixed binaries to work on older linux systems (circa GLIBC_2.2.5 like centos 6) @bnoordhuis - - Updated internal libsqlite3 from 3.8.7.1 -> 3.8.9 (http://www.sqlite.org/news.html) + - Updated internal libsqlite3 from 3.8.7.1 -> 3.8.9 (https://www.sqlite.org/news.html) ## 3.0.5 @@ -116,7 +252,7 @@ Released July 14th, 2014 - Now supporting node v0.11.x (specifically >=0.11.13) - Fix db opening error with absolute path on windows - Updated to node-pre-gyp@0.5.18 - - updated internal libsqlite3 from 3.8.4.3 -> 3.8.5 (http://www.sqlite.org/news.html) + - updated internal libsqlite3 from 3.8.4.3 -> 3.8.5 (https://www.sqlite.org/news.html) ## 2.2.3 @@ -140,7 +276,7 @@ Released July 14th, 2014 Released Jan 13th, 2014 - - updated internal libsqlite3 from 3.7.17 -> 3.8.2 (http://www.sqlite.org/news.html) which includes the next-generation query planner http://www.sqlite.org/queryplanner-ng.html + - updated internal libsqlite3 from 3.7.17 -> 3.8.2 (https://www.sqlite.org/news.html) which includes the next-generation query planner http://www.sqlite.org/queryplanner-ng.html - improved binary deploy system using https://github.com/springmeyer/node-pre-gyp - binary install now supports http proxies - source compile now supports freebsd diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3801e3031..0a75e352b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ Note: NEVER republish binaries for an existing released version. An official release requires: - Updating the CHANGELOG.md - - Create and push github tag like `git tag v3.1.1` -m "v3.1.1" && git push --tags` + - Create and push github tag like `git tag v3.1.1 -m "v3.1.1" && git push --tags` - Ensure you have a clean checkout (no extra files in your check that are not known by git). You need to be careful, for instance, to avoid a large accidental file being packaged by npm. You can get a view of what npm will publish by running `make testpack` - Fully rebuild and ensure install from binary works: `make clean && npm install --fallback-to-build=false` - Then publish the module to npm repositories by running `npm publish` diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 000000000..331d06ef6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,84 @@ +#!/bin/echo docker build . -f +# -*- coding: utf-8 -*- +#{ +# ISC License +# Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") +# Copyright (c) 1995-2003 by Internet Software Consortium +# Permission to use, copy, modify, and /or distribute this software +# for any purpose with or without fee is hereby granted, +# provided that the above copyright notice +# and this permission notice appear in all copies. +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS. +# IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, +# ARISING OUT OF OR IN CONNECTION WITH THE USE +# OR PERFORMANCE OF THIS SOFTWARE. +#} + +FROM ubuntu:latest +MAINTAINER Philippe Coval (p.coval@samsung.com) + +ENV DEBIAN_FRONTEND noninteractive +ENV LC_ALL en_US.UTF-8 +ENV LANG ${LC_ALL} + +RUN echo "#log: Configuring locales" \ + && set -x \ + && apt-get update -y \ + && apt-get install -y locales \ + && echo "${LC_ALL} UTF-8" | tee /etc/locale.gen \ + && locale-gen ${LC_ALL} \ + && dpkg-reconfigure locales \ + && sync + +ENV project node-sqlite3 + +RUN echo "#log: ${project}: Setup system" \ + && set -x \ + && apt-get update -y \ + && apt-get install -y \ + curl \ + sudo \ + build-essential \ + python \ + && apt-get clean \ + && NVM_VERSION="v0.33.8" \ + && NODE_VERSION="--lts=carbon" \ + && curl -o- https://raw.githubusercontent.com/creationix/nvm/${NVM_VERSION}/install.sh | bash \ + && which nvm || . ~/.bashrc \ + && nvm install ${NODE_VERSION} \ + && nvm use ${NODE_VERSION} \ + && sync + +ADD . /usr/local/${project}/${project} +WORKDIR /usr/local/${project}/${project} +RUN echo "#log: ${project}: Preparing sources" \ + && set -x \ + && which npm || . ~/.bashrc \ + && npm install || cat npm-debug.log \ + && npm install \ + && npm install --unsafe-perm --build-from-source \ + && sync + +WORKDIR /usr/local/${project}/${project} +RUN echo "#log: ${project}: Building sources" \ + && set -x \ + && which npm || . ~/.bashrc \ + && npm run pack \ + && npm pack \ + && find build/stage/ -type f \ + && sync + +WORKDIR /usr/local/${project}/${project} +RUN echo "#log: ${project}: Installing sources" \ + && set -x \ + && install -d /usr/local/src/${project}/deploy/ \ + && install *.tgz /usr/local/src/${project}/deploy/ \ + && cp -rfva ./build/stage/ /usr/local/src/${project}/deploy/ \ + && find /usr/local/src/${project}/deploy/ -type f \ + && sync diff --git a/Makefile b/Makefile index 56fcb276a..48118eb8b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -#http://www.gnu.org/prep/standards/html_node/Standard-Targets.html#Standard-Targets +#https://www.gnu.org/prep/standards/html_node/Standard-Targets.html#Standard-Targets all: build @@ -33,13 +33,8 @@ rebuild: @make clean @make -ifndef only test: - @PATH="./node_modules/mocha/bin:${PATH}" && NODE_PATH="./lib:$(NODE_PATH)" mocha -R spec -else -test: - @PATH="./node_modules/mocha/bin:${PATH}" && NODE_PATH="./lib:$(NODE_PATH)" mocha -R spec test/${only}.test.js -endif + npm test check: test diff --git a/README.md b/README.md index 6f755fda7..e9b5deee6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -Asynchronous, non-blocking [SQLite3](http://sqlite.org/) bindings for [Node.js](http://nodejs.org/). +Asynchronous, non-blocking [SQLite3](https://sqlite.org/) bindings for [Node.js](http://nodejs.org/). + [![NPM](https://nodei.co/npm/sqlite3.png?downloads=true&downloadRank=true)](https://nodei.co/npm/sqlite3/) @@ -7,10 +8,26 @@ Asynchronous, non-blocking [SQLite3](http://sqlite.org/) bindings for [Node.js]( [![Coverage Status](https://coveralls.io/repos/mapbox/node-sqlite3/badge.svg?branch=master&service=github)](https://coveralls.io/github/mapbox/node-sqlite3?branch=master) [![Dependencies](https://david-dm.org/mapbox/node-sqlite3.svg)](https://david-dm.org/mapbox/node-sqlite3) +## Sense Tecnic Changes + +**Aug 10, 2016** + +This forked version (3.1.4-0.0.1) of the module has been modified slightly for use with the Sense Tecnic private Node-RED node `sts-node-red-contrib-litedb` node. + +Specifically we have changed the maximum page count to be 1024 limiting the database size, and have disabled the use of PRAGMA statements that allow the client to make configuration changes to the database. + +The maximum database size is 4MB assuming a page count of 4096 (1024 * 4096). + +To ensure these changes are made to the sqlite C code the node must be installed with `--build-from-source` option. + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3?ref=badge_shield) +[![N-API v3 Badge](https://img.shields.io/badge/N--API-v3-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api) ## Supported platforms -The `sqlite3` module works with Node.js v0.10.x, v0.12.x, v4.x, and v5.x. +The `sqlite3` module works with: +* Node.js v11.x, v12.x, v13.x and v14.x. +* Electron v6.0.x, v6.1.x, v7.0.x, v7.1.x, v8.0.x, v8.1.x and v8.2.x Binaries for most Node versions and platforms are provided by default via [node-pre-gyp](https://github.com/mapbox/node-pre-gyp). @@ -49,16 +66,15 @@ db.close(); - Full Buffer/Blob support - Extensive [debugging support](https://github.com/mapbox/node-sqlite3/wiki/Debugging) - [Query serialization](https://github.com/mapbox/node-sqlite3/wiki/Control-Flow) API - - [Extension support](https://github.com/mapbox/node-sqlite3/wiki/Extensions) + - [Extension support](https://github.com/mapbox/node-sqlite3/wiki/Extensions), including bundled support for the [json1 extension](https://www.sqlite.org/json1.html). - Big test suite - Written in modern C++ and tested for memory leaks - + - Bundles SQLite3 3.32.3 as a fallback if the installing system doesn't include SQLite # API See the [API documentation](https://github.com/mapbox/node-sqlite3/wiki) in the wiki. - # Installing You can use [`npm`](https://github.com/isaacs/npm) to download and install: @@ -91,6 +107,26 @@ Note, if building against homebrew-installed sqlite on OS X you can do: npm install --build-from-source --sqlite=/usr/local/opt/sqlite/ +By default the node-gyp install will use `python` as part of the installation. A +different python executable can be specified on the command line. + + npm install --build-from-source --python=/usr/bin/python2 + +This uses the npm_config_python config, so values in .npmrc will be honoured: + + python=/usr/bin/python2 + +## Custom file header (magic) + +The default sqlite file header is "SQLite format 3". +You can specify a different magic, though this will make standard tools and libraries unable to work with your files. + + + npm install --build-from-source --sqlite_magic="MyCustomMagic15" + + +Note that the magic *must* be exactly 15 characters long (16 bytes including null terminator). + ## Building for node-webkit Because of ABI differences, `sqlite3` must be built in a custom to be used with [node-webkit](https://github.com/rogerwang/node-webkit). @@ -131,7 +167,7 @@ For instructions for building sqlcipher see To run node-sqlite3 against sqlcipher you need to compile from source by passing build options like: npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/ - + node -e 'require("sqlite3")' If your sqlcipher is installed in a custom location (if you compiled and installed it yourself), @@ -144,7 +180,7 @@ Set the location where `brew` installed it: export LDFLAGS="-L`brew --prefix`/opt/sqlcipher/lib" export CPPFLAGS="-I`brew --prefix`/opt/sqlcipher/include" npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=`brew --prefix` - + node -e 'require("sqlite3")' ### On most Linuxes (including Raspberry Pi) @@ -155,9 +191,19 @@ Set the location where `make` installed it: export CPPFLAGS="-I/usr/local/include -I/usr/local/include/sqlcipher" export CXXFLAGS="$CPPFLAGS" npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/local --verbose - + node -e 'require("sqlite3")' +### Custom builds and Electron + +Running sqlite3 through [electron-rebuild](https://github.com/electron/electron-rebuild) does not preserve the sqlcipher extension, so some additional flags are needed to make this build Electron compatible. Your `npm install sqlite3 --build-from-source` command needs these additional flags (be sure to replace the target version with the current Electron version you are working with): + + --runtime=electron --target=1.7.6 --dist-url=https://electronjs.org/headers + +In the case of MacOS with Homebrew, the command should look like the following: + + npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=`brew --prefix` --runtime=electron --target=1.7.6 --dist-url=https://electronjs.org/headers + # Testing [mocha](https://github.com/visionmedia/mocha) is required to run unit tests. @@ -167,7 +213,6 @@ In sqlite3's directory (where its `package.json` resides) run the following: npm install mocha npm test - # Contributors * [Konstantin Käfer](https://github.com/kkaefer) @@ -184,16 +229,16 @@ In sqlite3's directory (where its `package.json` resides) run the following: * [Johannes Schauer](https://github.com/pyneo) * [Mithgol](https://github.com/Mithgol) - # Acknowledgments Thanks to [Orlando Vazquez](https://github.com/orlandov), [Eric Fredricksen](https://github.com/grumdrig) and [Ryan Dahl](https://github.com/ry) for their SQLite bindings for node, and to mraleph on Freenode's #v8 for answering questions. -Development of this module is sponsored by [MapBox](http://mapbox.org/). - +Development of this module is sponsored by [MapBox](https://mapbox.com/). # License `node-sqlite3` is [BSD licensed](https://github.com/mapbox/node-sqlite3/raw/master/LICENSE). + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3?ref=badge_large) diff --git a/appveyor.yml b/appveyor.yml index 1f63dab13..80bdbbb09 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,58 +1,109 @@ environment: - node_pre_gyp_accessKeyId: - secure: 7DrSVc5eIGtmMcki5H+iRft+Tk3MJTwDBQEUuJHWaQ4= - node_pre_gyp_secretAccessKey: - secure: 1amwJJw9fu0j6dXnc5KsAQbSYf7Cjw/dapT6OZWABa6nc52grkKeLQ+DGaOfQz8i matrix: - - nodejs_version: 0.10.40 + - nodejs_version: 10 + platform: x64 + - nodejs_version: 10 + platform: x86 + - nodejs_version: 11 + platform: x64 + - nodejs_version: 11 platform: x86 - msvs_toolset: 12 - - nodejs_version: 0.10.40 + - nodejs_version: 12 platform: x64 - msvs_toolset: 12 - - nodejs_version: 0.12.7 + - nodejs_version: 12 platform: x86 - msvs_toolset: 12 - - nodejs_version: 0.12.7 + - nodejs_version: 13 platform: x64 - msvs_toolset: 12 - - nodejs_version: 4.0.0 + - nodejs_version: 13 + platform: x86 + - nodejs_version: 14 platform: x64 - msvs_toolset: 12 - - nodejs_version: 4.0.0 + - nodejs_version: 14 platform: x86 - msvs_toolset: 12 - - nodejs_version: 5.0.0 + # electron + - nodejs_version: 12.13.0 platform: x64 - msvs_toolset: 12 - - nodejs_version: 5.0.0 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 8.2.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.13.0 platform: x86 - msvs_toolset: 12 - - nodejs_version: 6.0.0 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 8.2.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.13.0 platform: x64 - msvs_toolset: 12 - - nodejs_version: 6.0.0 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 8.1.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.13.0 platform: x86 - msvs_toolset: 12 - # custom visual studio 2015 builds - - nodejs_version: 0.10.40 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 8.1.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.13.0 + platform: x64 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 8.0.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.13.0 + platform: x86 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 8.0.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.8.1 + platform: x64 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 7.2.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.8.1 platform: x86 - msvs_toolset: 14 - TOOLSET_ARGS: --dist-url=https://s3.amazonaws.com/mapbox/node-cpp11 --toolset=v140 - - nodejs_version: 0.10.40 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 7.2.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.8.1 platform: x64 - msvs_toolset: 14 - TOOLSET_ARGS: --dist-url=https://s3.amazonaws.com/mapbox/node-cpp11 --toolset=v140 - - nodejs_version: 0.12.7 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 7.1.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.8.1 platform: x86 - msvs_toolset: 14 - TOOLSET_ARGS: --dist-url=https://s3.amazonaws.com/mapbox/node-cpp11 --toolset=v140 - - nodejs_version: 0.12.7 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 7.1.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.8.1 platform: x64 - msvs_toolset: 14 - TOOLSET_ARGS: --dist-url=https://s3.amazonaws.com/mapbox/node-cpp11 --toolset=v140 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 7.0.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.8.1 + platform: x86 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 7.0.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.4.0 + platform: x64 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 6.1.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.4.0 + platform: x86 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 6.1.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.4.0 + platform: x64 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 6.0.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + - nodejs_version: 12.4.0 + platform: x86 + NODE_RUNTIME: electron + NODE_RUNTIME_VERSION: 6.0.0 + TOOLSET_ARGS: --dist-url=https://electronjs.org/headers + +image: Visual Studio 2017 -os: Visual Studio 2015 install: - scripts\build-appveyor.bat diff --git a/binding.gyp b/binding.gyp index a0c5faa53..f1336f6f7 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,10 +7,21 @@ "targets": [ { "target_name": "<(module_name)", - "include_dirs": [" in ' + name; - err.stack += '\n' + filter(error).slice(1).join('\n'); - err.__augmented = true; - } - throw err; + var err = arguments[0]; + if (err && err.stack && !err.__augmented) { + err.stack = filter(err).join('\n'); + err.stack += '\n--> in ' + name; + err.stack += '\n' + filter(error).slice(1).join('\n'); + err.__augmented = true; } + return cb.apply(this, arguments); }; } return old.apply(this, arguments); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..eb59bd1ff --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1933 @@ +{ + "name": "sqlite3", + "version": "5.0.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@mapbox/cloudfriend": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@mapbox/cloudfriend/-/cloudfriend-1.10.0.tgz", + "integrity": "sha512-3QVx4SH8qx4FF2OzL1NRjwpQwCHcLGwOCyK7h1pz5YVu+a9IQZPS0KkdBuzEaXql/GD0KAYiBGMGtPmRkR+v4A==", + "dev": true, + "requires": { + "aws-sdk": "^2.4.11" + } + }, + "@mapbox/node-pre-gyp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", + "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "optional": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "optional": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "aws-sdk": { + "version": "2.938.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.938.0.tgz", + "integrity": "sha512-e+KWYRyJ4Tvlg+6kQTze9Hxmkn+4/H8m+D8AXlfgUbtzyc1OBDf7cMCcl8IvCD3xoqELsgEunPC014v2JTTfZg==", + "dev": true, + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "optional": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "optional": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.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 + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "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=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + }, + "dependencies": { + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.5.0.tgz", + "integrity": "sha1-IvyfeA6lvKEwb6srbTM2sPpix1Q=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "concat-stream": "^1.4.6", + "debug": "^2.1.1", + "doctrine": "^1.2.2", + "escope": "^3.6.0", + "espree": "^3.1.6", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "glob": "^7.0.3", + "globals": "^9.2.0", + "ignore": "^3.1.5", + "imurmurhash": "^0.1.4", + "inquirer": "^0.12.0", + "is-my-json-valid": "^2.10.0", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.5.1", + "json-stable-stringify": "^1.0.0", + "levn": "^0.3.0", + "lodash": "^4.0.0", + "mkdirp": "^0.5.0", + "natural-compare": "^1.4.0", + "optionator": "^0.8.1", + "path-is-inside": "^1.0.1", + "pluralize": "^1.2.1", + "progress": "^1.1.8", + "require-uncached": "^1.0.2", + "shelljs": "^0.6.0", + "strip-bom": "^3.0.0", + "strip-json-comments": "~1.0.1", + "table": "^3.7.8", + "text-table": "~0.2.0", + "user-home": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "optional": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "optional": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "optional": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "optional": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "optional": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "requires": { + "is-property": "^1.0.2" + } + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "^1.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "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" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "optional": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "optional": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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==" + }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "requires": { + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "dev": true + }, + "is-my-json-valid": { + "version": "2.20.5", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.5.tgz", + "integrity": "sha512-VTPuvvGQtxvCeghwspQu1rBgjYUT6FGxPlvFKbYuFtgc4ADsX3U5ihZOYN0qyU6u+d4X9xXb0IT5O6QpXKt87A==", + "dev": true, + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "optional": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "optional": true + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "optional": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", + "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "mime-db": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "optional": true + }, + "mime-types": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "optional": true, + "requires": { + "mime-db": "1.48.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "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" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-gyp": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", + "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", + "optional": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "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=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "optional": true + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "optional": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "mute-stream": "0.0.5" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "optional": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true, + "requires": { + "once": "^1.3.0" + } + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "shelljs": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz", + "integrity": "sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "optional": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "dev": true, + "requires": { + "ajv": "^4.7.0", + "ajv-keywords": "^1.0.0", + "chalk": "^1.1.1", + "lodash": "^4.0.0", + "slice-ansi": "0.0.4", + "string-width": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "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 + }, + "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" + } + } + } + }, + "tar": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", + "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "optional": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "optional": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "optional": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": 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==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/package.json b/package.json index 77d890f16..d3f2ddd70 100644 --- a/package.json +++ b/package.json @@ -1,62 +1,77 @@ { - "name": "sqlite3", - "description": "Asynchronous, non-blocking SQLite3 bindings", - "version": "3.1.4", - "homepage": "http://github.com/mapbox/node-sqlite3", - "author": { - "name": "MapBox", - "url": "https://mapbox.com/" - }, - "binary": { - "module_name" : "node_sqlite3", - "module_path" : "./lib/binding/{node_abi}-{platform}-{arch}", - "host" : "https://mapbox-node-binary.s3.amazonaws.com", - "remote_path" : "./{name}/v{version}/{toolset}/", - "package_name": "{node_abi}-{platform}-{arch}.tar.gz" - }, - "contributors": [ - "Konstantin Käfer ", - "Dane Springmeyer ", - "Will White ", - "Orlando Vazquez ", - "Artem Kustikov ", - "Eric Fredricksen ", - "John Wright ", - "Ryan Dahl ", - "Tom MacWright ", - "Carter Thaxton ", - "Audrius Kažukauskas ", - "Johannes Schauer ", - "Nathan Rajlich ", - "AJ ONeal ", - "Mithgol", - "Ben Noordhuis " - ], - "repository": { - "type": "git", - "url": "git://github.com/mapbox/node-sqlite3.git" - }, - "dependencies": { - "nan": "~2.3.3", - "node-pre-gyp": "~0.6.28" - }, - "devDependencies": { - "mocha": "2.x", - "aws-sdk": "2.x" - }, - "scripts": { - "prepublish":"npm ls", - "preinstall":"npm install node-pre-gyp", - "install": "node-pre-gyp install --fallback-to-build", - "pretest": "node test/support/createdb.js", - "test": "mocha -R spec --timeout 480000" - }, - "license": "BSD-3-Clause", - "keywords": [ - "sql", - "sqlite", - "sqlite3", - "database" - ], - "main": "./lib/sqlite3" + "name": "sqlite3", + "description": "Asynchronous, non-blocking SQLite3 bindings", + "version": "5.0.2", + "homepage": "https://github.com/mapbox/node-sqlite3", + "author": { + "name": "MapBox", + "url": "https://mapbox.com/" + }, + "binary": { + "module_name": "node_sqlite3", + "module_path": "./lib/binding/napi-v{napi_build_version}-{platform}-{arch}", + "host": "https://mapbox-node-binary.s3.amazonaws.com", + "remote_path": "./{name}/v{version}/{toolset}/", + "package_name": "napi-v{napi_build_version}-{platform}-{arch}.tar.gz", + "napi_versions": [ + 3 + ] + }, + "contributors": [ + "Konstantin Käfer ", + "Dane Springmeyer ", + "Will White ", + "Orlando Vazquez ", + "Artem Kustikov ", + "Eric Fredricksen ", + "John Wright ", + "Ryan Dahl ", + "Tom MacWright ", + "Carter Thaxton ", + "Audrius Kažukauskas ", + "Johannes Schauer ", + "Nathan Rajlich ", + "AJ ONeal ", + "Mithgol", + "Ben Noordhuis " + ], + "repository": { + "type": "git", + "url": "git://github.com/mapbox/node-sqlite3.git" + }, + "dependencies": { + "node-addon-api": "^3.0.0", + "@mapbox/node-pre-gyp": "^1.0.0" + }, + "devDependencies": { + "@mapbox/cloudfriend": "^1.9.0", + "aws-sdk": "2.x", + "eslint": "3.5.0", + "mocha": "^5.2.0" + }, + "peerDependencies": { + "node-gyp": "7.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + }, + "optionalDependencies": { + "node-gyp": "7.x" + }, + "scripts": { + "install": "node-pre-gyp install --fallback-to-build", + "pretest": "node test/support/createdb.js", + "test": "mocha -R spec --timeout 480000", + "pack": "node-pre-gyp package" + }, + "license": "BSD-3-Clause", + "keywords": [ + "sql", + "sqlite", + "sqlite3", + "database" + ], + "main": "./lib/sqlite3" } diff --git a/scripts/build-appveyor.bat b/scripts/build-appveyor.bat index 020508927..c724c11cb 100644 --- a/scripts/build-appveyor.bat +++ b/scripts/build-appveyor.bat @@ -4,22 +4,22 @@ SET EL=0 ECHO ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %~f0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +IF /I "%msvs_version%"=="" ECHO msvs_version unset, defaulting to 2017 && SET msvs_version=2017 + SET PATH=%CD%;%PATH% -SET msvs_version=2013 -IF "%msvs_toolset%"=="14" SET msvs_version=2015 +IF NOT "%NODE_RUNTIME%"=="" SET "TOOLSET_ARGS=%TOOLSET_ARGS% --runtime=%NODE_RUNTIME%" +IF NOT "%NODE_RUNTIME_VERSION%"=="" SET "TOOLSET_ARGS=%TOOLSET_ARGS% --target=%NODE_RUNTIME_VERSION%" ECHO APPVEYOR^: %APPVEYOR% ECHO nodejs_version^: %nodejs_version% ECHO platform^: %platform% -ECHO msvs_toolset^: %msvs_toolset% ECHO msvs_version^: %msvs_version% ECHO TOOLSET_ARGS^: %TOOLSET_ARGS% - ECHO activating VS command prompt :: NOTE this call makes the x64 -> X64 -IF /I "%platform%"=="x64" ECHO x64 && CALL "C:\Program Files (x86)\Microsoft Visual Studio %msvs_toolset%.0\VC\vcvarsall.bat" amd64 -IF /I "%platform%"=="x86" ECHO x86 && CALL "C:\Program Files (x86)\Microsoft Visual Studio %msvs_toolset%.0\VC\vcvarsall.bat" x86 +IF /I "%platform%"=="x64" ECHO x64 && CALL "C:\Program Files (x86)\Microsoft Visual Studio\%msvs_version%\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +IF /I "%platform%"=="x86" ECHO x86 && CALL "C:\Program Files (x86)\Microsoft Visual Studio\%msvs_version%\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 IF %ERRORLEVEL% NEQ 0 GOTO ERROR ECHO using compiler^: && CALL cl @@ -28,65 +28,10 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR ECHO using MSBuild^: && CALL msbuild /version && ECHO. IF %ERRORLEVEL% NEQ 0 GOTO ERROR - ECHO downloading/installing node -::only use Install-Product when using VS2013 -::IF /I "%APPVEYOR%"=="True" IF /I "%msvs_toolset%"=="12" powershell Install-Product node $env:nodejs_version $env:Platform -::TESTING: -::always install (get npm matching node), but delete installed programfiles node.exe afterwards for VS2015 (using custom node.exe) -IF /I "%APPVEYOR%"=="True" GOTO APPVEYOR_INSTALL -GOTO SKIP_APPVEYOR_INSTALL - -:APPVEYOR_INSTALL -IF /I "%platform%"=="x64" powershell Install-Product node $env:nodejs_version x64 -IF /I "%platform%"=="x86" powershell Install-Product node $env:nodejs_version x86 -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -SET NODE_MAJOR=%nodejs_version:~0,1% -ECHO node major version^: %NODE_MAJOR% -IF %NODE_MAJOR% GTR 0 ECHO node version greater than zero, not updating npm && GOTO SKIP_APPVEYOR_INSTALL - -powershell Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -CALL npm install --global --production npm-windows-upgrade -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -REM https://ci.appveyor.com/project/Mapbox/node-sqlite3/build/1.0.500/job/n2y9fo4eg316db56#L289 -REM error C2373: '__pfnDliNotifyHook2': redefinition; different type modifiers -REM at least 2.15.9 is needed: https://github.com/nodejs/node-gyp/issues/972#issuecomment-231055109 -CALL npm-windows-upgrade --npm-version 2.15.9 --no-dns-check --no-prompt +powershell Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) $env:PLATFORM IF %ERRORLEVEL% NEQ 0 GOTO ERROR -:SKIP_APPVEYOR_INSTALL -IF /I "%msvs_toolset%"=="12" GOTO NODE_INSTALLED - - -::custom node for VS2015 -SET ARCHPATH= -IF "%platform%"=="X64" (SET ARCHPATH=x64/) -IF "%platform%"=="x64" (SET ARCHPATH=x64/) -SET NODE_URL=https://mapbox.s3.amazonaws.com/node-cpp11/v%nodejs_version%/%ARCHPATH%node.exe -ECHO downloading node^: %NODE_URL% -powershell Invoke-WebRequest "${env:NODE_URL}" -OutFile node.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO deleting node ... -SET NODE_EXE_PRG=%ProgramFiles%\nodejs\node.exe -IF EXIST "%NODE_EXE_PRG%" ECHO found %NODE_EXE_PRG%, deleting... && DEL /F "%NODE_EXE_PRG%" -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -IF EXIST "%ProgramFiles%\nodejs" ECHO copy custom node.exe to %ProgramFiles%\nodejs\ && COPY node.exe "%ProgramFiles%\nodejs\" -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -SET NODE_EXE_PRG=%ProgramFiles(x86)%\nodejs\node.exe -IF EXIST "%NODE_EXE_PRG%" ECHO found %NODE_EXE_PRG%, deleting... && DEL /F "%NODE_EXE_PRG%" -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -IF EXIST "%ProgramFiles(x86)%\nodejs" ECHO copy custom node.exe to %ProgramFiles(x86)%\nodejs\ && COPY node.exe "%ProgramFiles(x86)%\nodejs\" -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO delete node.exe in current directory && DEL node.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -:NODE_INSTALLED - ECHO available node.exe^: call where node ECHO available npm^: @@ -116,15 +61,39 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR IF /I "%NPM_BIN_DIR%"=="%CD%" ECHO ERROR npm bin -g equals local directory && SET ERRORLEVEL=1 && GOTO ERROR ECHO ===== where npm puts stuff END ============ - -ECHO installing node-gyp -CALL npm install -g node-gyp -IF %ERRORLEVEL% NEQ 0 GOTO ERROR +IF "%nodejs_version:~0,1%"=="4" CALL npm install node-gyp@3.x +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +IF "%nodejs_version:~0,1%"=="5" CALL npm install node-gyp@3.x +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +::Need to force update node-gyp to v6+ for electron v6 and v5 +ECHO ===== conditional node-gyp upgrade START ============ +:: Find the folder to install the node-gyp in +SET npm_in_nodejs_dir="%ProgramFiles%\nodejs\node_modules\npm" +ECHO npm_in_nodejs_dir^: %npm_in_nodejs_dir% +IF /I "%platform%"=="x86" SET npm_in_nodejs_dir="%ProgramFiles(x86)%\nodejs\node_modules\npm" +ECHO npm_in_nodejs_dir^: %npm_in_nodejs_dir% +:: Set boolean whether the update has to happen +SET "needs_patch=" +IF DEFINED NODE_RUNTIME_VERSION ( + ECHO NODE_RUNTIME_VERSION_REDUCED^: %NODE_RUNTIME_VERSION:~0,1% + IF "%NODE_RUNTIME_VERSION:~0,1%"=="1" SET "needs_patch=y" + IF "%NODE_RUNTIME_VERSION:~0,1%"=="2" SET "needs_patch=y" + IF "%NODE_RUNTIME_VERSION:~0,1%"=="3" SET "needs_patch=y" + IF "%NODE_RUNTIME_VERSION:~0,1%"=="4" SET "needs_patch=y" + IF "%NODE_RUNTIME_VERSION:~0,1%"=="5" SET "needs_patch=y" + IF "%NODE_RUNTIME_VERSION:~0,1%"=="6" SET "needs_patch=y" +) +:: Check if electron and install +ECHO NODE_RUNTIME^: %NODE_RUNTIME% +IF DEFINED needs_patch CALL npm install --prefix %npm_in_nodejs_dir% node-gyp@6.x +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +ECHO ===== conditional node-gyp upgrade END ============ CALL npm install --build-from-source --msvs_version=%msvs_version% %TOOLSET_ARGS% --loglevel=http IF %ERRORLEVEL% NEQ 0 GOTO ERROR -FOR /F "tokens=*" %%i in ('CALL node_modules\.bin\node-pre-gyp reveal module --silent') DO SET MODULE=%%i +FOR /F "tokens=*" %%i in ('"CALL node_modules\.bin\node-pre-gyp reveal module %TOOLSET_ARGS% --silent"') DO SET MODULE=%%i IF %ERRORLEVEL% NEQ 0 GOTO ERROR FOR /F "tokens=*" %%i in ('node -e "console.log(process.execPath)"') DO SET NODE_EXE=%%i IF %ERRORLEVEL% NEQ 0 GOTO ERROR @@ -135,6 +104,8 @@ dumpbin /DEPENDENTS "%MODULE%" IF %ERRORLEVEL% NEQ 0 GOTO ERROR +IF "%NODE_RUNTIME%"=="electron" GOTO CHECK_ELECTRON_TEST_ERRORLEVEL + ::skipping check for errorlevel npm test result when using io.js ::@springmeyer: how to proceed? IF NOT "%nodejs_version%"=="1.8.1" IF NOT "%nodejs_version%"=="2.0.0" GOTO CHECK_NPM_TEST_ERRORLEVEL @@ -144,22 +115,34 @@ CALL npm test ECHO ========================================== ECHO ========================================== ECHO ========================================== -ECHO using iojs, not checking test result!!!!!!!!! -ECHO ========================================== -ECHO ========================================== -ECHO ========================================== GOTO NPM_TEST_FINISHED +:CHECK_ELECTRON_TEST_ERRORLEVEL +ECHO installing electron +CALL npm install -g "electron@%NODE_RUNTIME_VERSION%" +ECHO installing electron-mocha +IF "%nodejs_version%" LEQ 6 CALL npm install -g "electron-mocha@7" +IF "%nodejs_version%" GTR 6 CALL npm install -g "electron-mocha" +ECHO preparing tests +CALL electron "test/support/createdb-electron.js" +DEL "test\support\createdb-electron.js" +ECHO calling electron-mocha +CALL electron-mocha -R spec --timeout 480000 +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +GOTO NPM_TEST_FINISHED + + :CHECK_NPM_TEST_ERRORLEVEL ECHO calling npm test CALL npm test IF %ERRORLEVEL% NEQ 0 GOTO ERROR :NPM_TEST_FINISHED - - +CALL node_modules\.bin\node-pre-gyp rebuild --msvs_version=%msvs_version% %TOOLSET_ARGS% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +ECHO packaging for node-gyp CALL node_modules\.bin\node-pre-gyp package %TOOLSET_ARGS% ::make commit message env var shorter SET CM=%APPVEYOR_REPO_COMMIT_MESSAGE% diff --git a/scripts/build_against_electron.sh b/scripts/build_against_electron.sh new file mode 100755 index 000000000..7334b12e1 --- /dev/null +++ b/scripts/build_against_electron.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +source ~/.nvm/nvm.sh + +set -e -u + +export DISPLAY=":99.0" +GYP_ARGS="--runtime=electron --target=${ELECTRON_VERSION} --dist-url=https://electronjs.org/headers" +NPM_BIN_DIR="$(npm bin -g 2>/dev/null)" + +function publish() { + if [[ ${PUBLISHABLE:-false} == true ]] && [[ ${COMMIT_MESSAGE} =~ "[publish binary]" ]]; then + node-pre-gyp rebuild --clang=1 $GYP_ARGS + node-pre-gyp package $GYP_ARGS + node-pre-gyp publish $GYP_ARGS + node-pre-gyp info $GYP_ARGS + fi +} + +function electron_pretest() { + npm install -g electron@${ELECTRON_VERSION} + if [ "$NODE_VERSION" -le 6 ]; then + npm install -g electron-mocha@7 + else + npm install -g electron-mocha + fi + if [ "${TRAVIS_OS_NAME}" = "osx" ]; then + (sudo Xvfb :99 -ac -screen 0 1024x768x8; echo ok )& + else + sh -e /etc/init.d/xvfb start + fi + + sleep 3 +} + +function electron_test() { + "$NPM_BIN_DIR"/electron test/support/createdb-electron.js + "$NPM_BIN_DIR"/electron-mocha -R spec --timeout 480000 +} + +# test installing from source +npm install --build-from-source --clang=1 $GYP_ARGS + +electron_pretest +electron_test + +publish +make clean + +# now test building against shared sqlite +export NODE_SQLITE3_JSON1=no +if [[ $(uname -s) == 'Darwin' ]]; then + brew update + brew install sqlite + npm install --build-from-source --sqlite=$(brew --prefix) --clang=1 $GYP_ARGS +else + npm install --build-from-source --sqlite=/usr --clang=1 $GYP_ARGS +fi +electron_test +export NODE_SQLITE3_JSON1=yes diff --git a/scripts/build_against_node.sh b/scripts/build_against_node.sh index 2982740f2..6a217943d 100755 --- a/scripts/build_against_node.sh +++ b/scripts/build_against_node.sh @@ -6,6 +6,7 @@ set -e -u function publish() { if [[ ${PUBLISHABLE:-false} == true ]] && [[ ${COMMIT_MESSAGE} =~ "[publish binary]" ]]; then + CFLAGS="${CFLAGS:-} -include $(pwd)/src/gcc-preinclude.h" CXXFLAGS="${CXXFLAGS:-} -include $(pwd)/src/gcc-preinclude.h" node-pre-gyp rebuild --clang=1 node-pre-gyp package testpackage node-pre-gyp publish node-pre-gyp info @@ -13,22 +14,19 @@ function publish() { fi } -# test installing from source -if [[ ${COVERAGE} == true ]]; then - CXXFLAGS="--coverage" LDFLAGS="--coverage" npm install --build-from-source --clang=1 - npm test - ./py-local/bin/cpp-coveralls --exclude node_modules --exclude tests --build-root build --gcov-options '\-lp' --exclude docs --exclude build/Release/obj/gen --exclude deps > /dev/null -else - npm install --build-from-source --clang=1 - npm test -fi - +echo "building binaries for publishing" +CFLAGS="${CFLAGS:-} -include $(pwd)/src/gcc-preinclude.h" CXXFLAGS="${CXXFLAGS:-} -include $(pwd)/src/gcc-preinclude.h" V=1 npm install --build-from-source --clang=1 +nm lib/binding/*/node_sqlite3.node | grep "GLIBCXX_" | c++filt || true +nm lib/binding/*/node_sqlite3.node | grep "GLIBC_" | c++filt || true +npm test publish # now test building against shared sqlite +echo "building from source to test against external libsqlite3" export NODE_SQLITE3_JSON1=no if [[ $(uname -s) == 'Darwin' ]]; then + brew update brew install sqlite npm install --build-from-source --sqlite=$(brew --prefix) --clang=1 else @@ -52,7 +50,7 @@ if [[ $(uname -s) == 'Linux' ]]; then # enable 32 bit iojs export PATH=$(pwd)/iojs-${NVER}-${platform}-x86/bin:$(pwd)/iojs-${NVER}-${platform}-ia32/bin:$PATH else - wget http://nodejs.org/dist/${NVER}/node-${NVER}-${platform}-x86.tar.gz + wget https://nodejs.org/dist/${NVER}/node-${NVER}-${platform}-x86.tar.gz tar xf node-${NVER}-${platform}-x86.tar.gz # enable 32 bit node export PATH=$(pwd)/node-${NVER}-${platform}-x86/bin:$(pwd)/node-${NVER}-${platform}-ia32/bin:$PATH diff --git a/scripts/build_against_node_webkit.sh b/scripts/build_against_node_webkit.sh index 94706b4fd..584fc7d82 100755 --- a/scripts/build_against_node_webkit.sh +++ b/scripts/build_against_node_webkit.sh @@ -23,7 +23,7 @@ fi if [[ $(uname -s) == 'Darwin' ]]; then export NW_DOWNLOAD=node-webkit-v${NODE_WEBKIT}-osx-${TARGET_ARCH} - wget http://dl.node-webkit.org/v${NODE_WEBKIT}/${NW_DOWNLOAD}.zip + wget https://dl.node-webkit.org/v${NODE_WEBKIT}/${NW_DOWNLOAD}.zip unzip -q ${NW_DOWNLOAD}.zip export PATH=$(pwd)/node-webkit.app/Contents/MacOS/:${PATH} # v0.10.0-rc1 unzips with extra folder @@ -36,9 +36,9 @@ else # for testing node-webkit, launch a virtual display export DISPLAY=:99.0 # NOTE: travis already has xvfb installed - # http://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-GUI-%28e.g.-a-Web-browser%29 + # https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-GUI-%28e.g.-a-Web-browser%29 sh -e /etc/init.d/xvfb start +extension RANDR - wget http://dl.node-webkit.org/v${NODE_WEBKIT}/${NW_DOWNLOAD}.tar.gz + wget https://dl.node-webkit.org/v${NODE_WEBKIT}/${NW_DOWNLOAD}.tar.gz tar xf ${NW_DOWNLOAD}.tar.gz export PATH=$(pwd)/${NW_DOWNLOAD}:${PATH} if [[ "${TARGET_ARCH}" == 'ia32' ]]; then diff --git a/scripts/build_for_node_webkit.cmd b/scripts/build_for_node_webkit.cmd index 2a0878fc4..ef809be15 100644 --- a/scripts/build_for_node_webkit.cmd +++ b/scripts/build_for_node_webkit.cmd @@ -5,7 +5,7 @@ if not "%1" == "x86" goto end if "%nw_version%" == "" goto end call npm install nw-gyp call cinst wget 7zip.commandline -call wget http://dl.node-webkit.org/v%nw_version%/node-webkit-v%nw_version%-win-ia32.zip +call wget https://dl.node-webkit.org/v%nw_version%/node-webkit-v%nw_version%-win-ia32.zip call 7z e -onw node-webkit-v%nw_version%-win-ia32.zip dir nw set PATH=nw;%PATH% diff --git a/scripts/install_node.sh b/scripts/install_node.sh index d534f77a5..d1ae0670e 100755 --- a/scripts/install_node.sh +++ b/scripts/install_node.sh @@ -15,7 +15,7 @@ nvm unload || true rm -rf ./__nvm/ && git clone --depth 1 https://github.com/creationix/nvm.git ./__nvm source ./__nvm/nvm.sh nvm install ${NODE_VERSION} -nvm use ${NODE_VERSION} +nvm use --delete-prefix ${NODE_VERSION} node --version npm --version -which node \ No newline at end of file +which node diff --git a/src/async.h b/src/async.h index 2b167cec2..9c4d8926a 100644 --- a/src/async.h +++ b/src/async.h @@ -1,8 +1,10 @@ #ifndef NODE_SQLITE3_SRC_ASYNC_H #define NODE_SQLITE3_SRC_ASYNC_H +#include +#include + #include "threading.h" -#include #if defined(NODE_SQLITE3_BOOST_THREADING) #include @@ -26,21 +28,18 @@ template class Async { : callback(cb_), parent(parent_) { watcher.data = this; NODE_SQLITE3_MUTEX_INIT - uv_async_init(uv_default_loop(), &watcher, reinterpret_cast(listener)); + uv_loop_t *loop; + napi_get_uv_event_loop(parent_->Env(), &loop); + uv_async_init(loop, &watcher, reinterpret_cast(listener)); } - static void listener(uv_async_t* handle, int status) { + static void listener(uv_async_t* handle) { Async* async = static_cast(handle->data); std::vector rows; NODE_SQLITE3_MUTEX_LOCK(&async->mutex) rows.swap(async->data); NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex) for (unsigned int i = 0, size = rows.size(); i < size; i++) { -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_unref((uv_handle_t *)&async->watcher); -#else - uv_unref(uv_default_loop()); -#endif async->callback(async->parent, rows[i]); } } @@ -56,17 +55,11 @@ template class Async { // Need to call the listener again to ensure all items have been // processed. Is this a bug in uv_async? Feels like uv_close // should handle that. - listener(&watcher, 0); + listener(&watcher); uv_close((uv_handle_t*)&watcher, close); } void add(Item* item) { - // Make sure node runs long enough to deliver the messages. -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_ref((uv_handle_t *)&watcher); -#else - uv_ref(uv_default_loop()); -#endif NODE_SQLITE3_MUTEX_LOCK(&mutex); data.push_back(item); NODE_SQLITE3_MUTEX_UNLOCK(&mutex) diff --git a/src/backup.cc b/src/backup.cc new file mode 100644 index 000000000..78570cddb --- /dev/null +++ b/src/backup.cc @@ -0,0 +1,420 @@ +#include +#include + +#include "macros.h" +#include "database.h" +#include "backup.h" + +using namespace node_sqlite3; + +Napi::Object Backup::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + + Napi::Function t = DefineClass(env, "Backup", { + InstanceMethod("step", &Backup::Step), + InstanceMethod("finish", &Backup::Finish), + InstanceAccessor("idle", &Backup::IdleGetter, nullptr), + InstanceAccessor("completed", &Backup::CompletedGetter, nullptr), + InstanceAccessor("failed", &Backup::FailedGetter, nullptr), + InstanceAccessor("remaining", &Backup::RemainingGetter, nullptr), + InstanceAccessor("pageCount", &Backup::PageCountGetter, nullptr), + InstanceAccessor("retryErrors", &Backup::RetryErrorGetter, &Backup::RetryErrorSetter), + }); + + exports.Set("Backup", t); + return exports; +} + +void Backup::Process() { + if (finished && !queue.empty()) { + return CleanQueue(); + } + + while (inited && !locked && !queue.empty()) { + std::unique_ptr call(queue.front()); + queue.pop(); + + call->callback(call->baton); + } +} + +void Backup::Schedule(Work_Callback callback, Baton* baton) { + if (finished) { + queue.push(new Call(callback, baton)); + CleanQueue(); + } + else if (!inited || locked || !queue.empty()) { + queue.push(new Call(callback, baton)); + } + else { + callback(baton); + } +} + +template void Backup::Error(T* baton) { + Napi::Env env = baton->backup->Env(); + Napi::HandleScope scope(env); + + Backup* backup = baton->backup; + // Fail hard on logic errors. + assert(backup->status != 0); + EXCEPTION(Napi::String::New(env, backup->message), backup->status, exception); + + Napi::Function cb = baton->callback.Value(); + + if (!cb.IsEmpty() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(backup->Value(), cb, 1, argv); + } + else { + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(backup->Value(), 2, argv); + } +} + +void Backup::CleanQueue() { + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); + + if (inited && !queue.empty()) { + // This backup has already been initialized and is now finished. + // Fire error for all remaining items in the queue. + EXCEPTION(Napi::String::New(env, "Backup is already finished"), SQLITE_MISUSE, exception); + Napi::Value argv[] = { exception }; + bool called = false; + + // Clear out the queue so that this object can get GC'ed. + while (!queue.empty()) { + std::unique_ptr call(queue.front()); + queue.pop(); + + std::unique_ptr baton(call->baton); + Napi::Function cb = baton->callback.Value(); + + if (inited && !cb.IsEmpty() && + cb.IsFunction()) { + TRY_CATCH_CALL(Value(), cb, 1, argv); + called = true; + } + } + + // When we couldn't call a callback function, emit an error on the + // Backup object. + if (!called) { + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, info); + } + } + else while (!queue.empty()) { + // Just delete all items in the queue; we already fired an event when + // initializing the backup failed. + std::unique_ptr call(queue.front()); + queue.pop(); + + // We don't call the actual callback, so we have to make sure that + // the baton gets destroyed. + delete call->baton; + } +} + +Backup::Backup(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + if (!info.IsConstructCall()) { + Napi::TypeError::New(env, "Use the new operator to create new Backup objects").ThrowAsJavaScriptException(); + return; + } + + int length = info.Length(); + + if (length <= 0 || !Database::HasInstance(info[0])) { + Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); + return; + } + else if (length <= 1 || !info[1].IsString()) { + Napi::TypeError::New(env, "Filename expected").ThrowAsJavaScriptException(); + return; + } + else if (length <= 2 || !info[2].IsString()) { + Napi::TypeError::New(env, "Source database name expected").ThrowAsJavaScriptException(); + return; + } + else if (length <= 3 || !info[3].IsString()) { + Napi::TypeError::New(env, "Destination database name expected").ThrowAsJavaScriptException(); + return; + } + else if (length <= 4 || !info[4].IsBoolean()) { + Napi::TypeError::New(env, "Direction flag expected").ThrowAsJavaScriptException(); + return; + } + else if (length > 5 && !info[5].IsUndefined() && !info[5].IsFunction()) { + Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); + return; + } + + Database* db = Napi::ObjectWrap::Unwrap(info[0].As()); + Napi::String filename = info[1].As(); + Napi::String sourceName = info[2].As(); + Napi::String destName = info[3].As(); + Napi::Boolean filenameIsDest = info[4].As(); + + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filename", filename)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("sourceName", sourceName)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("destName", destName)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filenameIsDest", filenameIsDest)); + + init(db); + + InitializeBaton* baton = new InitializeBaton(db, info[5].As(), this); + baton->filename = filename.Utf8Value(); + baton->sourceName = sourceName.Utf8Value(); + baton->destName = destName.Utf8Value(); + baton->filenameIsDest = filenameIsDest.Value(); + db->Schedule(Work_BeginInitialize, baton); +} + +void Backup::Work_BeginInitialize(Database::Baton* baton) { + assert(baton->db->open); + baton->db->pending++; + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Backup.Initialize"), + Work_Initialize, Work_AfterInitialize, baton, &baton->request + ); + assert(status == 0); + napi_queue_async_work(env, baton->request); +} + +void Backup::Work_Initialize(napi_env e, void* data) { + BACKUP_INIT(InitializeBaton); + + // In case stepping fails, we use a mutex to make sure we get the associated + // error message. + sqlite3_mutex* mtx = sqlite3_db_mutex(baton->db->_handle); + sqlite3_mutex_enter(mtx); + + backup->status = sqlite3_open(baton->filename.c_str(), &backup->_otherDb); + + if (backup->status == SQLITE_OK) { + backup->_handle = sqlite3_backup_init( + baton->filenameIsDest ? backup->_otherDb : backup->db->_handle, + baton->destName.c_str(), + baton->filenameIsDest ? backup->db->_handle : backup->_otherDb, + baton->sourceName.c_str()); + } + backup->_destDb = baton->filenameIsDest ? backup->_otherDb : backup->db->_handle; + + if (backup->status != SQLITE_OK) { + backup->message = std::string(sqlite3_errmsg(backup->_destDb)); + sqlite3_close(backup->_otherDb); + backup->_otherDb = NULL; + backup->_destDb = NULL; + } + + sqlite3_mutex_leave(mtx); +} + +void Backup::Work_AfterInitialize(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Backup* backup = baton->backup; + + Napi::Env env = backup->Env(); + Napi::HandleScope scope(env); + + if (backup->status != SQLITE_OK) { + Error(baton.get()); + backup->FinishAll(); + } + else { + backup->inited = true; + Napi::Function cb = baton->callback.Value(); + if (!cb.IsEmpty() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(backup->Value(), cb, 1, argv); + } + } + BACKUP_END(); +} + +Napi::Value Backup::Step(const Napi::CallbackInfo& info) { + Backup* backup = this; + Napi::Env env = backup->Env(); + + REQUIRE_ARGUMENT_INTEGER(0, pages); + OPTIONAL_ARGUMENT_FUNCTION(1, callback); + + StepBaton* baton = new StepBaton(backup, callback, pages); + backup->GetRetryErrors(baton->retryErrorsSet); + backup->Schedule(Work_BeginStep, baton); + return info.This(); +} + +void Backup::Work_BeginStep(Baton* baton) { + BACKUP_BEGIN(Step); +} + +void Backup::Work_Step(napi_env e, void* data) { + BACKUP_INIT(StepBaton); + if (backup->_handle) { + backup->status = sqlite3_backup_step(backup->_handle, baton->pages); + backup->remaining = sqlite3_backup_remaining(backup->_handle); + backup->pageCount = sqlite3_backup_pagecount(backup->_handle); + } + if (backup->status != SQLITE_OK) { + // Text of message is a little awkward to get, since the error is not associated + // with a db connection. +#if SQLITE_VERSION_NUMBER >= 3007015 + // sqlite3_errstr is a relatively new method + backup->message = std::string(sqlite3_errstr(backup->status)); +#else + backup->message = "Sqlite error"; +#endif + if (baton->retryErrorsSet.size() > 0) { + if (baton->retryErrorsSet.find(backup->status) == baton->retryErrorsSet.end()) { + backup->FinishSqlite(); + } + } + } +} + +void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Backup* backup = baton->backup; + + Napi::Env env = backup->Env(); + Napi::HandleScope scope(env); + + if (backup->status == SQLITE_DONE) { + backup->completed = true; + } else if (!backup->_handle) { + backup->failed = true; + } + + if (backup->status != SQLITE_OK && backup->status != SQLITE_DONE) { + Error(baton.get()); + } + else { + // Fire callbacks. + Napi::Function cb = baton->callback.Value(); + if (!cb.IsEmpty() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null(), Napi::Boolean::New(env, backup->status == SQLITE_DONE) }; + TRY_CATCH_CALL(backup->Value(), cb, 2, argv); + } + } + + BACKUP_END(); +} + +Napi::Value Backup::Finish(const Napi::CallbackInfo& info) { + Backup* backup = this; + Napi::Env env = backup->Env(); + + OPTIONAL_ARGUMENT_FUNCTION(0, callback); + + Baton* baton = new Baton(backup, callback); + backup->Schedule(Work_BeginFinish, baton); + return info.This(); +} + +void Backup::Work_BeginFinish(Baton* baton) { + BACKUP_BEGIN(Finish); +} + +void Backup::Work_Finish(napi_env e, void* data) { + BACKUP_INIT(Baton); + backup->FinishSqlite(); +} + +void Backup::Work_AfterFinish(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Backup* backup = baton->backup; + + Napi::Env env = backup->Env(); + Napi::HandleScope scope(env); + + backup->FinishAll(); + + // Fire callback in case there was one. + Napi::Function cb = baton->callback.Value(); + if (!cb.IsEmpty() && cb.IsFunction()) { + TRY_CATCH_CALL(backup->Value(), cb, 0, NULL); + } + + BACKUP_END(); +} + +void Backup::FinishAll() { + assert(!finished); + if (!completed && !failed) { + failed = true; + } + finished = true; + CleanQueue(); + FinishSqlite(); + db->Unref(); +} + +void Backup::FinishSqlite() { + if (_handle) { + sqlite3_backup_finish(_handle); + _handle = NULL; + } + if (_otherDb) { + sqlite3_close(_otherDb); + _otherDb = NULL; + } + _destDb = NULL; +} + +Napi::Value Backup::IdleGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + bool idle = backup->inited && !backup->locked && backup->queue.empty(); + return Napi::Boolean::New(this->Env(), idle); +} + +Napi::Value Backup::CompletedGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Boolean::New(this->Env(), backup->completed); +} + +Napi::Value Backup::FailedGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Boolean::New(this->Env(), backup->failed); +} + +Napi::Value Backup::RemainingGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Number::New(this->Env(), backup->remaining); +} + +Napi::Value Backup::PageCountGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Number::New(this->Env(), backup->pageCount); +} + +Napi::Value Backup::RetryErrorGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return backup->retryErrors.Value(); +} + +void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value) { + Backup* backup = this; + Napi::Env env = backup->Env(); + if (!value.IsArray()) { + Napi::Error::New(env, "retryErrors must be an array").ThrowAsJavaScriptException(); + return; + } + Napi::Array array = value.As(); + backup->retryErrors.Reset(array, 1); +} + +void Backup::GetRetryErrors(std::set& retryErrorsSet) { + retryErrorsSet.clear(); + Napi::Array array = retryErrors.Value(); + int length = array.Length(); + for (int i = 0; i < length; i++) { + Napi::Value code = (array).Get(i); + if (code.IsNumber()) { + retryErrorsSet.insert(code.As().Int32Value()); + } + } +} diff --git a/src/backup.h b/src/backup.h new file mode 100644 index 000000000..c15b77bfe --- /dev/null +++ b/src/backup.h @@ -0,0 +1,221 @@ +#ifndef NODE_SQLITE3_SRC_BACKUP_H +#define NODE_SQLITE3_SRC_BACKUP_H + +#include "database.h" + +#include +#include +#include + +#include +#include + +using namespace Napi; + +namespace node_sqlite3 { + +/** + * + * A class for managing an sqlite3_backup object. For consistency + * with other node-sqlite3 classes, it maintains an internal queue + * of calls. + * + * Intended usage from node: + * + * var db = new sqlite3.Database('live.db'); + * var backup = db.backup('backup.db'); + * ... + * // in event loop, move backup forward when we have time. + * if (backup.idle) { backup.step(NPAGES); } + * if (backup.completed) { ... success ... } + * if (backup.failed) { ... sadness ... } + * // do other work in event loop - fine to modify live.db + * ... + * + * Here is how sqlite's backup api is exposed: + * + * - `sqlite3_backup_init`: This is implemented as + * `db.backup(filename, [callback])` or + * `db.backup(filename, destDbName, sourceDbName, filenameIsDest, [callback])`. + * - `sqlite3_backup_step`: `backup.step(pages, [callback])`. + * - `sqlite3_backup_finish`: `backup.finish([callback])`. + * - `sqlite3_backup_remaining`: `backup.remaining`. + * - `sqlite3_backup_pagecount`: `backup.pageCount`. + * + * There are the following read-only properties: + * + * - `backup.completed` is set to `true` when the backup + * succeeeds. + * - `backup.failed` is set to `true` when the backup + * has a fatal error. + * - `backup.idle` is set to `true` when no operation + * is currently in progress or queued for the backup. + * - `backup.remaining` is an integer with the remaining + * number of pages after the last call to `backup.step` + * (-1 if `step` not yet called). + * - `backup.pageCount` is an integer with the total number + * of pages measured during the last call to `backup.step` + * (-1 if `step` not yet called). + * + * There is the following writable property: + * + * - `backup.retryErrors`: an array of sqlite3 error codes + * that are treated as non-fatal - meaning, if they occur, + * backup.failed is not set, and the backup may continue. + * By default, this is `[sqlite3.BUSY, sqlite3.LOCKED]`. + * + * The `db.backup(filename, [callback])` shorthand is sufficient + * for making a backup of a database opened by node-sqlite3. If + * using attached or temporary databases, or moving data in the + * opposite direction, the more complete (but daunting) + * `db.backup(filename, destDbName, sourceDbName, filenameIsDest, [callback])` + * signature is provided. + * + * A backup will finish automatically when it succeeds or a fatal + * error occurs, meaning it is not necessary to call `db.finish()`. + * By default, SQLITE_LOCKED and SQLITE_BUSY errors are not + * treated as failures, and the backup will continue if they + * occur. The set of errors that are tolerated can be controlled + * by setting `backup.retryErrors`. To disable automatic + * finishing and stick strictly to sqlite's raw api, set + * `backup.retryErrors` to `[]`. In that case, it is necessary + * to call `backup.finish()`. + * + * In the same way as node-sqlite3 databases and statements, + * backup methods can be called safely without callbacks, due + * to an internal call queue. So for example this naive code + * will correctly back up a db, if there are no errors: + * + * var backup = db.backup('backup.db'); + * backup.step(-1); + * backup.finish(); + * + */ +class Backup : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports); + + struct Baton { + napi_async_work request = NULL; + Backup* backup; + Napi::FunctionReference callback; + + Baton(Backup* backup_, Napi::Function cb_) : backup(backup_) { + backup->Ref(); + callback.Reset(cb_, 1); + } + virtual ~Baton() { + if (request) napi_delete_async_work(backup->Env(), request); + backup->Unref(); + callback.Reset(); + } + }; + + struct InitializeBaton : Database::Baton { + Backup* backup; + std::string filename; + std::string sourceName; + std::string destName; + bool filenameIsDest; + InitializeBaton(Database* db_, Napi::Function cb_, Backup* backup_) : + Baton(db_, cb_), backup(backup_), filenameIsDest(true) { + backup->Ref(); + } + virtual ~InitializeBaton() { + backup->Unref(); + if (!db->IsOpen() && db->IsLocked()) { + // The database handle was closed before the backup could be opened. + backup->FinishAll(); + } + } + }; + + struct StepBaton : Baton { + int pages; + std::set retryErrorsSet; + StepBaton(Backup* backup_, Napi::Function cb_, int pages_) : + Baton(backup_, cb_), pages(pages_) {} + }; + + typedef void (*Work_Callback)(Baton* baton); + + struct Call { + Call(Work_Callback cb_, Baton* baton_) : callback(cb_), baton(baton_) {}; + Work_Callback callback; + Baton* baton; + }; + + void init(Database* db_) { + db = db_; + _handle = NULL; + _otherDb = NULL; + _destDb = NULL; + inited = false; + locked = true; + completed = false; + failed = false; + remaining = -1; + pageCount = -1; + finished = false; + db->Ref(); + } + + Backup(const Napi::CallbackInfo& info); + + ~Backup() { + if (!finished) { + FinishAll(); + } + retryErrors.Reset(); + } + + WORK_DEFINITION(Step); + WORK_DEFINITION(Finish); + Napi::Value IdleGetter(const Napi::CallbackInfo& info); + Napi::Value CompletedGetter(const Napi::CallbackInfo& info); + Napi::Value FailedGetter(const Napi::CallbackInfo& info); + Napi::Value PageCountGetter(const Napi::CallbackInfo& info); + Napi::Value RemainingGetter(const Napi::CallbackInfo& info); + Napi::Value FatalErrorGetter(const Napi::CallbackInfo& info); + Napi::Value RetryErrorGetter(const Napi::CallbackInfo& info); + + void FatalErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); + void RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); + +protected: + static void Work_BeginInitialize(Database::Baton* baton); + static void Work_Initialize(napi_env env, void* data); + static void Work_AfterInitialize(napi_env env, napi_status status, void* data); + + void Schedule(Work_Callback callback, Baton* baton); + void Process(); + void CleanQueue(); + template static void Error(T* baton); + + void FinishAll(); + void FinishSqlite(); + void GetRetryErrors(std::set& retryErrorsSet); + + Database* db; + + sqlite3_backup* _handle; + sqlite3* _otherDb; + sqlite3* _destDb; + int status; + std::string message; + + bool inited; + bool locked; + bool completed; + bool failed; + int remaining; + int pageCount; + bool finished; + std::queue queue; + + Napi::Reference retryErrors; +}; + +} + +#endif diff --git a/src/database.cc b/src/database.cc index e7891cbd2..1b319ec77 100644 --- a/src/database.cc +++ b/src/database.cc @@ -1,4 +1,5 @@ #include +#include #include "macros.h" #include "database.h" @@ -6,94 +7,101 @@ using namespace node_sqlite3; -Nan::Persistent Database::constructor_template; - -NAN_MODULE_INIT(Database::Init) { - Nan::HandleScope scope; - - Local t = Nan::New(New); - - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Database").ToLocalChecked()); - - Nan::SetPrototypeMethod(t, "close", Close); - Nan::SetPrototypeMethod(t, "exec", Exec); - Nan::SetPrototypeMethod(t, "wait", Wait); - Nan::SetPrototypeMethod(t, "loadExtension", LoadExtension); - Nan::SetPrototypeMethod(t, "serialize", Serialize); - Nan::SetPrototypeMethod(t, "parallelize", Parallelize); - Nan::SetPrototypeMethod(t, "configure", Configure); - Nan::SetPrototypeMethod(t, "interrupt", Interrupt); - - NODE_SET_GETTER(t, "open", OpenGetter); - - constructor_template.Reset(t); - - Nan::Set(target, Nan::New("Database").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); +#if NAPI_VERSION < 6 +Napi::FunctionReference Database::constructor; +#endif + +Napi::Object Database::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + + Napi::Function t = DefineClass(env, "Database", { + InstanceMethod("close", &Database::Close), + InstanceMethod("exec", &Database::Exec), + InstanceMethod("wait", &Database::Wait), + InstanceMethod("loadExtension", &Database::LoadExtension), + InstanceMethod("serialize", &Database::Serialize), + InstanceMethod("parallelize", &Database::Parallelize), + InstanceMethod("configure", &Database::Configure), + InstanceMethod("interrupt", &Database::Interrupt), + InstanceAccessor("open", &Database::OpenGetter, nullptr) + }); + +#if NAPI_VERSION < 6 + constructor = Napi::Persistent(t); + constructor.SuppressDestruct(); +#else + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(t); + env.SetInstanceData(constructor); +#endif + + exports.Set("Database", t); + return exports; } void Database::Process() { - Nan::HandleScope scope; + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); if (!open && locked && !queue.empty()) { - EXCEPTION(Nan::New("Database handle is closed").ToLocalChecked(), SQLITE_MISUSE, exception); - Local argv[] = { exception }; + EXCEPTION(Napi::String::New(env, "Database handle is closed"), SQLITE_MISUSE, exception); + Napi::Value argv[] = { exception }; bool called = false; // Call all callbacks with the error object. while (!queue.empty()) { - Call* call = queue.front(); - Local cb = Nan::New(call->baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(this->handle(), cb, 1, argv); + std::unique_ptr call(queue.front()); + queue.pop(); + std::unique_ptr baton(call->baton); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(this->Value(), cb, 1, argv); called = true; } - queue.pop(); - // We don't call the actual callback, so we have to make sure that - // the baton gets destroyed. - delete call->baton; - delete call; } // When we couldn't call a callback function, emit an error on the // Database object. if (!called) { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, info); } return; } while (open && (!locked || pending == 0) && !queue.empty()) { - Call* call = queue.front(); + Call *c = queue.front(); - if (call->exclusive && pending > 0) { + if (c->exclusive && pending > 0) { break; } queue.pop(); + std::unique_ptr call(c); locked = call->exclusive; call->callback(call->baton); - delete call; if (locked) break; } } void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { - Nan::HandleScope scope; + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); if (!open && locked) { - EXCEPTION(Nan::New("Database is closed").ToLocalChecked(), SQLITE_MISUSE, exception); - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(handle(), cb, 1, argv); + EXCEPTION(Napi::String::New(env, "Database is closed"), SQLITE_MISUSE, exception); + Napi::Function cb = baton->callback.Value(); + // We don't call the actual callback, so we have to make sure that + // the baton gets destroyed. + delete baton; + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(Value(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(handle(), 2, argv); + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, argv); } return; } @@ -107,47 +115,51 @@ void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { } } -NAN_METHOD(Database::New) { - if (!info.IsConstructCall()) { - return Nan::ThrowTypeError("Use the new operator to create new Database objects"); +Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { + init(); + Napi::Env env = info.Env(); + + if (info.Length() <= 0 || !info[0].IsString()) { + Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException(); + return; } + std::string filename = info[0].As(); - REQUIRE_ARGUMENT_STRING(0, filename); - int pos = 1; + unsigned int pos = 1; int mode; - if (info.Length() >= pos && info[pos]->IsInt32()) { - mode = Nan::To(info[pos++]).FromJust(); - } else { + if (info.Length() >= pos && info[pos].IsNumber() && OtherIsInt(info[pos].As())) { + mode = info[pos++].As().Int32Value(); + } + else { mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; } - Local callback; - if (info.Length() >= pos && info[pos]->IsFunction()) { - callback = Local::Cast(info[pos++]); + Napi::Function callback; + if (info.Length() >= pos && info[pos].IsFunction()) { + callback = info[pos++].As(); } - Database* db = new Database(); - db->Wrap(info.This()); - - info.This()->ForceSet(Nan::New("filename").ToLocalChecked(), info[0].As(), ReadOnly); - info.This()->ForceSet(Nan::New("mode").ToLocalChecked(), Nan::New(mode), ReadOnly); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filename", info[0].As(), napi_default)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("mode", Napi::Number::New(env, mode), napi_default)); // Start opening the database. - OpenBaton* baton = new OpenBaton(db, callback, *filename, mode); + OpenBaton* baton = new OpenBaton(this, callback, filename.c_str(), mode); Work_BeginOpen(baton); - - info.GetReturnValue().Set(info.This()); } void Database::Work_BeginOpen(Baton* baton) { - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Open, (uv_after_work_cb)Work_AfterOpen); + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.Open"), + Work_Open, Work_AfterOpen, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_Open(uv_work_t* req) { - OpenBaton* baton = static_cast(req->data); +void Database::Work_Open(napi_env e, void* data) { + OpenBaton* baton = static_cast(data); Database* db = baton->db; baton->status = sqlite3_open_v2( @@ -165,57 +177,61 @@ void Database::Work_Open(uv_work_t* req) { else { // Set default database handle values. sqlite3_busy_timeout(db->_handle, 1000); + // set athorizer to not allow PRAGMA + sqlite3_set_authorizer(db->_handle, AuthorizerCallback, NULL); } } -void Database::Work_AfterOpen(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); - OpenBaton* baton = static_cast(req->data); Database* db = baton->db; - Local argv[1]; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + + Napi::Value argv[1]; if (baton->status != SQLITE_OK) { - EXCEPTION(Nan::New(baton->message.c_str()).ToLocalChecked(), baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); argv[0] = exception; } else { db->open = true; - argv[0] = Nan::Null(); + argv[0] = env.Null(); } - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else if (!db->open) { - Local info[] = { Nan::New("error").ToLocalChecked(), argv[0] }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; + EMIT_EVENT(db->Value(), 2, info); } if (db->open) { - Local info[] = { Nan::New("open").ToLocalChecked() }; - EMIT_EVENT(db->handle(), 1, info); + Napi::Value info[] = { Napi::String::New(env, "open") }; + EMIT_EVENT(db->Value(), 1, info); db->Process(); } - - delete baton; } -NAN_GETTER(Database::OpenGetter) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(db->open); +Napi::Value Database::OpenGetter(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; + return Napi::Boolean::New(env, db->open); } -NAN_METHOD(Database::Close) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Close(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(db, callback); db->Schedule(Work_BeginClose, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginClose(Baton* baton) { @@ -224,16 +240,22 @@ void Database::Work_BeginClose(Baton* baton) { assert(baton->db->_handle); assert(baton->db->pending == 0); + baton->db->pending++; baton->db->RemoveCallbacks(); baton->db->closing = true; - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Close, (uv_after_work_cb)Work_AfterClose); + Napi::Env env = baton->db->Env(); + + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.Close"), + Work_Close, Work_AfterClose, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_Close(uv_work_t* req) { - Baton* baton = static_cast(req->data); +void Database::Work_Close(napi_env e, void* data) { + Baton* baton = static_cast(data); Database* db = baton->db; baton->status = sqlite3_close(db->_handle); @@ -246,142 +268,155 @@ void Database::Work_Close(uv_work_t* req) { } } -void Database::Work_AfterClose(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterClose(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); - Baton* baton = static_cast(req->data); Database* db = baton->db; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + + db->pending--; db->closing = false; - Local argv[1]; + Napi::Value argv[1]; if (baton->status != SQLITE_OK) { - EXCEPTION(Nan::New(baton->message.c_str()).ToLocalChecked(), baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); argv[0] = exception; } else { db->open = false; // Leave db->locked to indicate that this db object has reached // the end of its life. - argv[0] = Nan::Null(); + argv[0] = env.Null(); } - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); // Fire callbacks. - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else if (db->open) { - Local info[] = { Nan::New("error").ToLocalChecked(), argv[0] }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; + EMIT_EVENT(db->Value(), 2, info); } if (!db->open) { - Local info[] = { Nan::New("close").ToLocalChecked(), argv[0] }; - EMIT_EVENT(db->handle(), 1, info); + Napi::Value info[] = { Napi::String::New(env, "close"), argv[0] }; + EMIT_EVENT(db->Value(), 1, info); db->Process(); } - - delete baton; } -NAN_METHOD(Database::Serialize) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = true; - if (!callback.IsEmpty() && callback->IsFunction()) { - TRY_CATCH_CALL(info.This(), callback, 0, NULL); + if (!callback.IsEmpty() && callback.IsFunction()) { + TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This()); db->serialize = before; } db->Process(); - info.GetReturnValue().Set(info.This()); + return info.This(); } -NAN_METHOD(Database::Parallelize) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Parallelize(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = false; - if (!callback.IsEmpty() && callback->IsFunction()) { - TRY_CATCH_CALL(info.This(), callback, 0, NULL); + if (!callback.IsEmpty() && callback.IsFunction()) { + TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This()); db->serialize = before; } db->Process(); - info.GetReturnValue().Set(info.This()); + return info.This(); } -NAN_METHOD(Database::Configure) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Configure(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; REQUIRE_ARGUMENTS(2); - if (Nan::Equals(info[0], Nan::New("trace").ToLocalChecked()).FromJust()) { - Local handle; + if (info[0].StrictEquals( Napi::String::New(env, "trace"))) { + Napi::Function handle; Baton* baton = new Baton(db, handle); db->Schedule(RegisterTraceCallback, baton); } - else if (Nan::Equals(info[0], Nan::New("profile").ToLocalChecked()).FromJust()) { - Local handle; + else if (info[0].StrictEquals( Napi::String::New(env, "profile"))) { + Napi::Function handle; Baton* baton = new Baton(db, handle); db->Schedule(RegisterProfileCallback, baton); } - else if (Nan::Equals(info[0], Nan::New("busyTimeout").ToLocalChecked()).FromJust()) { - if (!info[1]->IsInt32()) { - return Nan::ThrowTypeError("Value must be an integer"); + else if (info[0].StrictEquals( Napi::String::New(env, "busyTimeout"))) { + if (!info[1].IsNumber()) { + Napi::TypeError::New(env, "Value must be an integer").ThrowAsJavaScriptException(); + return env.Null(); } - Local handle; + Napi::Function handle; Baton* baton = new Baton(db, handle); - baton->status = Nan::To(info[1]).FromJust(); + baton->status = info[1].As().Int32Value(); db->Schedule(SetBusyTimeout, baton); } else { - return Nan::ThrowError(Exception::Error(String::Concat( - Nan::To(info[0]).ToLocalChecked(), - Nan::New(" is not a valid configuration option").ToLocalChecked() - ))); + Napi::TypeError::New(env, (StringConcat( +#if V8_MAJOR_VERSION > 6 + info.GetIsolate(), +#endif + info[0].As(), + Napi::String::New(env, " is not a valid configuration option") + )).Utf8Value().c_str()).ThrowAsJavaScriptException(); + return env.Null(); } db->Process(); - info.GetReturnValue().Set(info.This()); + return info.This(); } -NAN_METHOD(Database::Interrupt) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Interrupt(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; if (!db->open) { - return Nan::ThrowError("Database is not open"); + Napi::Error::New(env, "Database is not open").ThrowAsJavaScriptException(); + return env.Null(); } if (db->closing) { - return Nan::ThrowError("Database is closing"); + Napi::Error::New(env, "Database is closing").ThrowAsJavaScriptException(); + return env.Null(); } sqlite3_interrupt(db->_handle); - info.GetReturnValue().Set(info.This()); + return info.This(); } -void Database::SetBusyTimeout(Baton* baton) { +void Database::SetBusyTimeout(Baton* b) { + std::unique_ptr baton(b); + assert(baton->db->open); assert(baton->db->_handle); // Abuse the status field for passing the timeout. sqlite3_busy_timeout(baton->db->_handle, baton->status); - - delete baton; } -void Database::RegisterTraceCallback(Baton* baton) { +void Database::RegisterTraceCallback(Baton* b) { + std::unique_ptr baton(b); assert(baton->db->open); assert(baton->db->_handle); Database* db = baton->db; @@ -397,8 +432,6 @@ void Database::RegisterTraceCallback(Baton* baton) { db->debug_trace->finish(); db->debug_trace = NULL; } - - delete baton; } void Database::TraceCallback(void* db, const char* sql) { @@ -407,19 +440,21 @@ void Database::TraceCallback(void* db, const char* sql) { static_cast(db)->debug_trace->send(new std::string(sql)); } -void Database::TraceCallback(Database* db, std::string* sql) { +void Database::TraceCallback(Database* db, std::string* s) { + std::unique_ptr sql(s); // Note: This function is called in the main V8 thread. - Nan::HandleScope scope; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); - Local argv[] = { - Nan::New("trace").ToLocalChecked(), - Nan::New(sql->c_str()).ToLocalChecked() + Napi::Value argv[] = { + Napi::String::New(env, "trace"), + Napi::String::New(env, sql->c_str()) }; - EMIT_EVENT(db->handle(), 2, argv); - delete sql; + EMIT_EVENT(db->Value(), 2, argv); } -void Database::RegisterProfileCallback(Baton* baton) { +void Database::RegisterProfileCallback(Baton* b) { + std::unique_ptr baton(b); assert(baton->db->open); assert(baton->db->_handle); Database* db = baton->db; @@ -435,8 +470,6 @@ void Database::RegisterProfileCallback(Baton* baton) { db->debug_profile->finish(); db->debug_profile = NULL; } - - delete baton; } void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) { @@ -448,19 +481,21 @@ void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) static_cast(db)->debug_profile->send(info); } -void Database::ProfileCallback(Database *db, ProfileInfo* info) { - Nan::HandleScope scope; +void Database::ProfileCallback(Database *db, ProfileInfo* i) { + std::unique_ptr info(i); + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); - Local argv[] = { - Nan::New("profile").ToLocalChecked(), - Nan::New(info->sql.c_str()).ToLocalChecked(), - Nan::New((double)info->nsecs / 1000000.0) + Napi::Value argv[] = { + Napi::String::New(env, "profile"), + Napi::String::New(env, info->sql.c_str()), + Napi::Number::New(env, (double)info->nsecs / 1000000.0) }; - EMIT_EVENT(db->handle(), 3, argv); - delete info; + EMIT_EVENT(db->Value(), 3, argv); } -void Database::RegisterUpdateCallback(Baton* baton) { +void Database::RegisterUpdateCallback(Baton* b) { + std::unique_ptr baton(b); assert(baton->db->open); assert(baton->db->_handle); Database* db = baton->db; @@ -476,8 +511,6 @@ void Database::RegisterUpdateCallback(Baton* baton) { db->update_event->finish(); db->update_event = NULL; } - - delete baton; } void Database::UpdateCallback(void* db, int type, const char* database, @@ -492,29 +525,46 @@ void Database::UpdateCallback(void* db, int type, const char* database, static_cast(db)->update_event->send(info); } -void Database::UpdateCallback(Database *db, UpdateInfo* info) { - Nan::HandleScope scope; +void Database::UpdateCallback(Database *db, UpdateInfo* i) { + std::unique_ptr info(i); + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); - Local argv[] = { - Nan::New(sqlite_authorizer_string(info->type)).ToLocalChecked(), - Nan::New(info->database.c_str()).ToLocalChecked(), - Nan::New(info->table.c_str()).ToLocalChecked(), - Nan::New(info->rowid), + Napi::Value argv[] = { + Napi::String::New(env, sqlite_authorizer_string(info->type)), + Napi::String::New(env, info->database.c_str()), + Napi::String::New(env, info->table.c_str()), + Napi::Number::New(env, info->rowid), }; - EMIT_EVENT(db->handle(), 4, argv); - delete info; + EMIT_EVENT(db->Value(), 4, argv); +} + +int Database::AuthorizerCallback(void* db, int action_type, + const char* details1, + const char* details2, + const char* details3, + const char* details4) { + // deny all PRAGMA calls, they allow the user to change db configuration + if (action_type == SQLITE_PRAGMA) { + return SQLITE_DENY; + } + return SQLITE_OK; } NAN_METHOD(Database::Exec) { Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Exec(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; + REQUIRE_ARGUMENT_STRING(0, sql); OPTIONAL_ARGUMENT_FUNCTION(1, callback); - Baton* baton = new ExecBaton(db, callback, *sql); + Baton* baton = new ExecBaton(db, callback, sql.c_str()); db->Schedule(Work_BeginExec, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginExec(Baton* baton) { @@ -522,13 +572,18 @@ void Database::Work_BeginExec(Baton* baton) { assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Exec, (uv_after_work_cb)Work_AfterExec); + baton->db->pending++; + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.Exec"), + Work_Exec, Work_AfterExec, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_Exec(uv_work_t* req) { - ExecBaton* baton = static_cast(req->data); +void Database::Work_Exec(napi_env e, void* data) { + ExecBaton* baton = static_cast(data); char* message = NULL; baton->status = sqlite3_exec( @@ -545,76 +600,80 @@ void Database::Work_Exec(uv_work_t* req) { } } -void Database::Work_AfterExec(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterExec(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); - ExecBaton* baton = static_cast(req->data); Database* db = baton->db; + db->pending--; + + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); if (baton->status != SQLITE_OK) { - EXCEPTION(Nan::New(baton->message.c_str()).ToLocalChecked(), baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(db->Value(), 2, info); } } - else if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + else if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); - - delete baton; } -NAN_METHOD(Database::Wait) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Wait(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(db, callback); db->Schedule(Work_Wait, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } -void Database::Work_Wait(Baton* baton) { - Nan::HandleScope scope; +void Database::Work_Wait(Baton* b) { + std::unique_ptr baton(b); + + Napi::Env env = baton->db->Env(); + Napi::HandleScope scope(env); assert(baton->db->locked); assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(baton->db->handle(), cb, 1, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(baton->db->Value(), cb, 1, argv); } baton->db->Process(); - - delete baton; } -NAN_METHOD(Database::LoadExtension) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::LoadExtension(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; REQUIRE_ARGUMENT_STRING(0, filename); OPTIONAL_ARGUMENT_FUNCTION(1, callback); - Baton* baton = new LoadExtensionBaton(db, callback, *filename); + Baton* baton = new LoadExtensionBaton(db, callback, filename.c_str()); db->Schedule(Work_BeginLoadExtension, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginLoadExtension(Baton* baton) { @@ -622,13 +681,18 @@ void Database::Work_BeginLoadExtension(Baton* baton) { assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_LoadExtension, reinterpret_cast(Work_AfterLoadExtension)); + baton->db->pending++; + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.LoadExtension"), + Work_LoadExtension, Work_AfterLoadExtension, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_LoadExtension(uv_work_t* req) { - LoadExtensionBaton* baton = static_cast(req->data); +void Database::Work_LoadExtension(napi_env e, void* data) { + LoadExtensionBaton* baton = static_cast(data); sqlite3_enable_load_extension(baton->db->_handle, 1); @@ -648,33 +712,35 @@ void Database::Work_LoadExtension(uv_work_t* req) { } } -void Database::Work_AfterLoadExtension(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); - LoadExtensionBaton* baton = static_cast(req->data); Database* db = baton->db; - Local cb = Nan::New(baton->callback); + db->pending--; + + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + + Napi::Function cb = baton->callback.Value(); if (baton->status != SQLITE_OK) { - EXCEPTION(Nan::New(baton->message.c_str()).ToLocalChecked(), baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(db->Value(), 2, info); } } - else if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + else if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); - - delete baton; } void Database::RemoveCallbacks() { diff --git a/src/database.h b/src/database.h index c5455abe3..984f1c6a7 100644 --- a/src/database.h +++ b/src/database.h @@ -3,47 +3,59 @@ #define NODE_SQLITE3_SRC_DATABASE_H +#include #include #include #include -#include +#include #include "async.h" -using namespace v8; +using namespace Napi; namespace node_sqlite3 { class Database; -class Database : public Nan::ObjectWrap { +class Database : public Napi::ObjectWrap { public: - static Nan::Persistent constructor_template; - static NAN_MODULE_INIT(Init); - - static inline bool HasInstance(Local val) { - Nan::HandleScope scope; - if (!val->IsObject()) return false; - Local obj = val.As(); - return Nan::New(constructor_template)->HasInstance(obj); +#if NAPI_VERSION < 6 + static Napi::FunctionReference constructor; +#endif + static Napi::Object Init(Napi::Env env, Napi::Object exports); + + static inline bool HasInstance(Napi::Value val) { + Napi::Env env = val.Env(); + Napi::HandleScope scope(env); + if (!val.IsObject()) return false; + Napi::Object obj = val.As(); +#if NAPI_VERSION < 6 + return obj.InstanceOf(constructor.Value()); +#else + Napi::FunctionReference* constructor = + env.GetInstanceData(); + return obj.InstanceOf(constructor->Value()); +#endif } struct Baton { - uv_work_t request; + napi_async_work request = NULL; Database* db; - Nan::Persistent callback; + Napi::FunctionReference callback; int status; std::string message; - Baton(Database* db_, Local cb_) : + Baton(Database* db_, Napi::Function cb_) : db(db_), status(SQLITE_OK) { db->Ref(); - request.data = this; - callback.Reset(cb_); + if (!cb_.IsUndefined() && cb_.IsFunction()) { + callback.Reset(cb_, 1); + } } virtual ~Baton() { + if (request) napi_delete_async_work(db->Env(), request); db->Unref(); callback.Reset(); } @@ -52,19 +64,19 @@ class Database : public Nan::ObjectWrap { struct OpenBaton : Baton { std::string filename; int mode; - OpenBaton(Database* db_, Local cb_, const char* filename_, int mode_) : + OpenBaton(Database* db_, Napi::Function cb_, const char* filename_, int mode_) : Baton(db_, cb_), filename(filename_), mode(mode_) {} }; struct ExecBaton : Baton { std::string sql; - ExecBaton(Database* db_, Local cb_, const char* sql_) : + ExecBaton(Database* db_, Napi::Function cb_, const char* sql_) : Baton(db_, cb_), sql(sql_) {} }; struct LoadExtensionBaton : Baton { std::string filename; - LoadExtensionBaton(Database* db_, Local cb_, const char* filename_) : + LoadExtensionBaton(Database* db_, Napi::Function cb_, const char* filename_) : Baton(db_, cb_), filename(filename_) {} }; @@ -98,20 +110,22 @@ class Database : public Nan::ObjectWrap { typedef Async AsyncUpdate; friend class Statement; + friend class Backup; -protected: - Database() : Nan::ObjectWrap(), - _handle(NULL), - open(false), - closing(false), - locked(false), - pending(0), - serialize(false), - debug_trace(NULL), - debug_profile(NULL), - update_event(NULL) { + void init() { + _handle = NULL; + open = false; + closing = false; + locked = false; + pending = 0; + serialize = false; + debug_trace = NULL; + debug_profile = NULL; + update_event = NULL; } + Database(const Napi::CallbackInfo& info); + ~Database() { RemoveCallbacks(); sqlite3_close(_handle); @@ -119,40 +133,40 @@ class Database : public Nan::ObjectWrap { open = false; } - static NAN_METHOD(New); +protected: static void Work_BeginOpen(Baton* baton); - static void Work_Open(uv_work_t* req); - static void Work_AfterOpen(uv_work_t* req); + static void Work_Open(napi_env env, void* data); + static void Work_AfterOpen(napi_env env, napi_status status, void* data); - static NAN_GETTER(OpenGetter); + Napi::Value OpenGetter(const Napi::CallbackInfo& info); void Schedule(Work_Callback callback, Baton* baton, bool exclusive = false); void Process(); - static NAN_METHOD(Exec); + Napi::Value Exec(const Napi::CallbackInfo& info); static void Work_BeginExec(Baton* baton); - static void Work_Exec(uv_work_t* req); - static void Work_AfterExec(uv_work_t* req); + static void Work_Exec(napi_env env, void* data); + static void Work_AfterExec(napi_env env, napi_status status, void* data); - static NAN_METHOD(Wait); + Napi::Value Wait(const Napi::CallbackInfo& info); static void Work_Wait(Baton* baton); - static NAN_METHOD(Close); + Napi::Value Close(const Napi::CallbackInfo& info); static void Work_BeginClose(Baton* baton); - static void Work_Close(uv_work_t* req); - static void Work_AfterClose(uv_work_t* req); + static void Work_Close(napi_env env, void* data); + static void Work_AfterClose(napi_env env, napi_status status, void* data); - static NAN_METHOD(LoadExtension); + Napi::Value LoadExtension(const Napi::CallbackInfo& info); static void Work_BeginLoadExtension(Baton* baton); - static void Work_LoadExtension(uv_work_t* req); - static void Work_AfterLoadExtension(uv_work_t* req); + static void Work_LoadExtension(napi_env env, void* data); + static void Work_AfterLoadExtension(napi_env env, napi_status status, void* data); - static NAN_METHOD(Serialize); - static NAN_METHOD(Parallelize); + Napi::Value Serialize(const Napi::CallbackInfo& info); + Napi::Value Parallelize(const Napi::CallbackInfo& info); - static NAN_METHOD(Configure); + Napi::Value Configure(const Napi::CallbackInfo& info); - static NAN_METHOD(Interrupt); + Napi::Value Interrupt(const Napi::CallbackInfo& info); static void SetBusyTimeout(Baton* baton); @@ -168,6 +182,12 @@ class Database : public Nan::ObjectWrap { static void UpdateCallback(void* db, int type, const char* database, const char* table, sqlite3_int64 rowid); static void UpdateCallback(Database* db, UpdateInfo* info); + static int AuthorizerCallback(void* db, int action_type, + const char* details1, + const char* details2, + const char* details3, + const char* details4); + void RemoveCallbacks(); protected: diff --git a/src/gcc-preinclude.h b/src/gcc-preinclude.h index 38c91385c..9ea8c5765 100644 --- a/src/gcc-preinclude.h +++ b/src/gcc-preinclude.h @@ -1,5 +1,5 @@ -// https://rjpower9000.wordpress.com/2012/04/09/fun-with-shared-libraries-version-glibc_2-14-not-found/ +// http://web.archive.org/web/20140401031018/http://rjpower9000.wordpress.com:80/2012/04/09/fun-with-shared-libraries-version-glibc_2-14-not-found/ #if defined(__linux__) && defined(__x86_64__) __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); diff --git a/src/macros.h b/src/macros.h index 38399ee86..1c1a7c445 100644 --- a/src/macros.h +++ b/src/macros.h @@ -3,42 +3,71 @@ const char* sqlite_code_string(int code); const char* sqlite_authorizer_string(int type); - +#include + +// TODO: better way to work around StringConcat? +#include +inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) { + return Napi::String::New(str1.Env(), str1.As().Utf8Value() + + str2.As().Utf8Value() ); +} + +// A Napi substitute IsInt32() +inline bool OtherIsInt(Napi::Number source) { + double orig_val = source.DoubleValue(); + double int_val = (double)source.Int32Value(); + if (orig_val == int_val) { + return true; + } else { + return false; + } +} #define REQUIRE_ARGUMENTS(n) \ if (info.Length() < (n)) { \ - return Nan::ThrowTypeError("Expected " #n "arguments"); \ + Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \ + return env.Null(); \ } #define REQUIRE_ARGUMENT_EXTERNAL(i, var) \ - if (info.Length() <= (i) || !info[i]->IsExternal()) { \ - return Nan::ThrowTypeError("Argument " #i " invalid"); \ + if (info.Length() <= (i) || !info[i].IsExternal()) { \ + Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - Local var = Local::Cast(info[i]); + Napi::External var = info[i].As(); #define REQUIRE_ARGUMENT_FUNCTION(i, var) \ - if (info.Length() <= (i) || !info[i]->IsFunction()) { \ - return Nan::ThrowTypeError("Argument " #i " must be a function"); \ + if (info.Length() <= (i) || !info[i].IsFunction()) { \ + Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - Local var = Local::Cast(info[i]); + Napi::Function var = info[i].As(); #define REQUIRE_ARGUMENT_STRING(i, var) \ - if (info.Length() <= (i) || !info[i]->IsString()) { \ - return Nan::ThrowTypeError("Argument " #i " must be a string"); \ + if (info.Length() <= (i) || !info[i].IsString()) { \ + Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - Nan::Utf8String var(info[i]); + std::string var = info[i].As(); +#define REQUIRE_ARGUMENT_INTEGER(i, var) \ + if (info.Length() <= (i) || !info[i].IsNumber()) { \ + Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ + return env.Null(); \ + } \ + int var(info[i].As().Int32Value()); #define OPTIONAL_ARGUMENT_FUNCTION(i, var) \ - Local var; \ - if (info.Length() > i && !info[i]->IsUndefined()) { \ - if (!info[i]->IsFunction()) { \ - return Nan::ThrowTypeError("Argument " #i " must be a function"); \ + Napi::Function var; \ + if (info.Length() > i && !info[i].IsUndefined()) { \ + if (!info[i].IsFunction()) { \ + Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - var = Local::Cast(info[i]); \ + var = info[i].As(); \ } @@ -47,72 +76,63 @@ const char* sqlite_authorizer_string(int type); if (info.Length() <= (i)) { \ var = (default); \ } \ - else if (info[i]->IsInt32()) { \ - var = Nan::To(info[i]).FromJust(); \ + else if (info[i].IsNumber()) { \ + if (OtherIsInt(info[i].As())) { \ + var = info[i].As().Int32Value(); \ + } \ } \ else { \ - return Nan::ThrowTypeError("Argument " #i " must be an integer"); \ + Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ + return env.Null(); \ } #define DEFINE_CONSTANT_INTEGER(target, constant, name) \ - Nan::ForceSet(target, \ - Nan::New(#name).ToLocalChecked(), \ - Nan::New(constant), \ - static_cast(ReadOnly | DontDelete) \ - ); + Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant), \ + static_cast(napi_enumerable | napi_configurable)), #define DEFINE_CONSTANT_STRING(target, constant, name) \ - Nan::ForceSet(target, \ - Nan::New(#name).ToLocalChecked(), \ - Nan::New(constant).ToLocalChecked(), \ - static_cast(ReadOnly | DontDelete) \ - ); - - -#define NODE_SET_GETTER(target, name, function) \ - Nan::SetAccessor((target)->InstanceTemplate(), \ - Nan::New(name).ToLocalChecked(), (function)); - -#define GET_STRING(source, name, property) \ - Nan::Utf8String name(Nan::Get(source, \ - Nan::New(prop).ToLocalChecked()).ToLocalChecked()); - -#define GET_INTEGER(source, name, prop) \ - int name = Nan::To(Nan::Get(source, \ - Nan::New(property).ToLocalChecked()).ToLocalChecked()).FromJust(); + Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \ + static_cast(napi_enumerable | napi_configurable)), #define EXCEPTION(msg, errno, name) \ - Local name = Exception::Error( \ - String::Concat( \ - String::Concat( \ - Nan::New(sqlite_code_string(errno)).ToLocalChecked(), \ - Nan::New(": ").ToLocalChecked() \ + Napi::Value name = Napi::Error::New(env, \ + StringConcat( \ + StringConcat( \ + Napi::String::New(env, sqlite_code_string(errno)), \ + Napi::String::New(env, ": ") \ ), \ (msg) \ - ) \ - ); \ - Local name ##_obj = name.As(); \ - Nan::Set(name ##_obj, Nan::New("errno").ToLocalChecked(), Nan::New(errno));\ - Nan::Set(name ##_obj, Nan::New("code").ToLocalChecked(), \ - Nan::New(sqlite_code_string(errno)).ToLocalChecked()); + ).Utf8Value() \ + ).Value(); \ + Napi::Object name ##_obj = name.As(); \ + (name ##_obj).Set( Napi::String::New(env, "errno"), Napi::Number::New(env, errno)); \ + (name ##_obj).Set( Napi::String::New(env, "code"), \ + Napi::String::New(env, sqlite_code_string(errno))); #define EMIT_EVENT(obj, argc, argv) \ TRY_CATCH_CALL((obj), \ - Nan::Get(obj, \ - Nan::New("emit").ToLocalChecked()).ToLocalChecked().As(),\ + (obj).Get("emit").As(),\ argc, argv \ ); -#define TRY_CATCH_CALL(context, callback, argc, argv) \ - Nan::MakeCallback((context), (callback), (argc), (argv)) +// The Mac OS compiler complains when argv is NULL unless we +// first assign it to a locally defined variable. +#define TRY_CATCH_CALL(context, callback, argc, argv, ...) \ + Napi::Value* passed_argv = argv;\ + std::vector args;\ + if ((argc != 0) && (passed_argv != NULL)) {\ + args.assign(passed_argv, passed_argv + argc);\ + }\ + Napi::Value res = (callback).MakeCallback(Napi::Value(context), args); \ + if (res.IsEmpty()) return __VA_ARGS__; #define WORK_DEFINITION(name) \ - static NAN_METHOD(name); \ + Napi::Value name(const Napi::CallbackInfo& info); \ static void Work_Begin##name(Baton* baton); \ - static void Work_##name(uv_work_t* req); \ - static void Work_After##name(uv_work_t* req); + static void Work_##name(napi_env env, void* data); \ + static void Work_After##name(napi_env env, napi_status status, void* data); #define STATEMENT_BEGIN(type) \ assert(baton); \ @@ -122,23 +142,61 @@ const char* sqlite_authorizer_string(int type); assert(baton->stmt->prepared); \ baton->stmt->locked = true; \ baton->stmt->db->pending++; \ - int status = uv_queue_work(uv_default_loop(), \ - &baton->request, \ - Work_##type, reinterpret_cast(Work_After##type)); \ - assert(status == 0); + Napi::Env env = baton->stmt->Env(); \ + int status = napi_create_async_work( \ + env, NULL, Napi::String::New(env, "sqlite3.Statement."#type), \ + Work_##type, Work_After##type, baton, &baton->request \ + ); \ + assert(status == 0); \ + napi_queue_async_work(env, baton->request); #define STATEMENT_INIT(type) \ - type* baton = static_cast(req->data); \ + type* baton = static_cast(data); \ Statement* stmt = baton->stmt; +#define STATEMENT_MUTEX(name) \ + if (!stmt->db->_handle) { \ + stmt->status = SQLITE_MISUSE; \ + stmt->message = "Database handle is closed"; \ + return; \ + } \ + sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle); + #define STATEMENT_END() \ assert(stmt->locked); \ assert(stmt->db->pending); \ stmt->locked = false; \ stmt->db->pending--; \ stmt->Process(); \ - stmt->db->Process(); \ - delete baton; + stmt->db->Process(); + +#define BACKUP_BEGIN(type) \ + assert(baton); \ + assert(baton->backup); \ + assert(!baton->backup->locked); \ + assert(!baton->backup->finished); \ + assert(baton->backup->inited); \ + baton->backup->locked = true; \ + baton->backup->db->pending++; \ + Napi::Env env = baton->backup->Env(); \ + int status = napi_create_async_work( \ + env, NULL, Napi::String::New(env, "sqlite3.Backup."#type), \ + Work_##type, Work_After##type, baton, &baton->request \ + ); \ + assert(status == 0); \ + napi_queue_async_work(env, baton->request); + +#define BACKUP_INIT(type) \ + type* baton = static_cast(data); \ + Backup* backup = baton->backup; + +#define BACKUP_END() \ + assert(backup->locked); \ + assert(backup->db->pending); \ + backup->locked = false; \ + backup->db->pending--; \ + backup->Process(); \ + backup->db->Process(); #define DELETE_FIELD(field) \ if (field != NULL) { \ diff --git a/src/node_sqlite3.cc b/src/node_sqlite3.cc index d8b459a51..b101b451f 100644 --- a/src/node_sqlite3.cc +++ b/src/node_sqlite3.cc @@ -7,53 +7,63 @@ #include "macros.h" #include "database.h" #include "statement.h" +#include "backup.h" using namespace node_sqlite3; namespace { -NAN_MODULE_INIT(RegisterModule) { - Nan::HandleScope scope; +Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); - Database::Init(target); - Statement::Init(target); + Database::Init(env, exports); + Statement::Init(env, exports); + Backup::Init(env, exports); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READONLY, OPEN_READONLY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READWRITE, OPEN_READWRITE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_CREATE, OPEN_CREATE); - DEFINE_CONSTANT_STRING(target, SQLITE_VERSION, VERSION); + exports.DefineProperties({ + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READONLY, OPEN_READONLY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READWRITE, OPEN_READWRITE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_CREATE, OPEN_CREATE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_FULLMUTEX, OPEN_FULLMUTEX) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_URI, OPEN_URI) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_SHAREDCACHE, OPEN_SHAREDCACHE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_PRIVATECACHE, OPEN_PRIVATECACHE) + DEFINE_CONSTANT_STRING(exports, SQLITE_VERSION, VERSION) #ifdef SQLITE_SOURCE_ID - DEFINE_CONSTANT_STRING(target, SQLITE_SOURCE_ID, SOURCE_ID); + DEFINE_CONSTANT_STRING(exports, SQLITE_SOURCE_ID, SOURCE_ID) #endif - DEFINE_CONSTANT_INTEGER(target, SQLITE_VERSION_NUMBER, VERSION_NUMBER); + DEFINE_CONSTANT_INTEGER(exports, SQLITE_VERSION_NUMBER, VERSION_NUMBER) - DEFINE_CONSTANT_INTEGER(target, SQLITE_OK, OK); - DEFINE_CONSTANT_INTEGER(target, SQLITE_ERROR, ERROR); - DEFINE_CONSTANT_INTEGER(target, SQLITE_INTERNAL, INTERNAL); - DEFINE_CONSTANT_INTEGER(target, SQLITE_PERM, PERM); - DEFINE_CONSTANT_INTEGER(target, SQLITE_ABORT, ABORT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_BUSY, BUSY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_LOCKED, LOCKED); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOMEM, NOMEM); - DEFINE_CONSTANT_INTEGER(target, SQLITE_READONLY, READONLY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_INTERRUPT, INTERRUPT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_IOERR, IOERR); - DEFINE_CONSTANT_INTEGER(target, SQLITE_CORRUPT, CORRUPT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOTFOUND, NOTFOUND); - DEFINE_CONSTANT_INTEGER(target, SQLITE_FULL, FULL); - DEFINE_CONSTANT_INTEGER(target, SQLITE_CANTOPEN, CANTOPEN); - DEFINE_CONSTANT_INTEGER(target, SQLITE_PROTOCOL, PROTOCOL); - DEFINE_CONSTANT_INTEGER(target, SQLITE_EMPTY, EMPTY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_SCHEMA, SCHEMA); - DEFINE_CONSTANT_INTEGER(target, SQLITE_TOOBIG, TOOBIG); - DEFINE_CONSTANT_INTEGER(target, SQLITE_CONSTRAINT, CONSTRAINT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_MISMATCH, MISMATCH); - DEFINE_CONSTANT_INTEGER(target, SQLITE_MISUSE, MISUSE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOLFS, NOLFS); - DEFINE_CONSTANT_INTEGER(target, SQLITE_AUTH, AUTH); - DEFINE_CONSTANT_INTEGER(target, SQLITE_FORMAT, FORMAT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_RANGE, RANGE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOTADB, NOTADB); + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OK, OK) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_ERROR, ERROR) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERNAL, INTERNAL) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_PERM, PERM) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_ABORT, ABORT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_BUSY, BUSY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LOCKED, LOCKED) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOMEM, NOMEM) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_READONLY, READONLY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERRUPT, INTERRUPT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_IOERR, IOERR) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_CORRUPT, CORRUPT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTFOUND, NOTFOUND) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_FULL, FULL) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_CANTOPEN, CANTOPEN) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_PROTOCOL, PROTOCOL) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_EMPTY, EMPTY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_SCHEMA, SCHEMA) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_TOOBIG, TOOBIG) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_CONSTRAINT, CONSTRAINT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISMATCH, MISMATCH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISUSE, MISUSE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOLFS, NOLFS) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_AUTH, AUTH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_FORMAT, FORMAT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_RANGE, RANGE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTADB, NOTADB) + }); + + return exports; } } @@ -102,4 +112,4 @@ const char* sqlite_authorizer_string(int type) { } } -NODE_MODULE(node_sqlite3, RegisterModule) +NODE_API_MODULE(node_sqlite3, RegisterModule) diff --git a/src/statement.cc b/src/statement.cc index 0166b72dc..9f6a9cd1e 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -1,7 +1,6 @@ #include -#include -#include -#include +#include +#include #include "macros.h" #include "database.h" @@ -9,27 +8,32 @@ using namespace node_sqlite3; -Nan::Persistent Statement::constructor_template; +Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); -NAN_MODULE_INIT(Statement::Init) { - Nan::HandleScope scope; + Napi::Function t = DefineClass(env, "Statement", { + InstanceMethod("bind", &Statement::Bind), + InstanceMethod("get", &Statement::Get), + InstanceMethod("run", &Statement::Run), + InstanceMethod("all", &Statement::All), + InstanceMethod("each", &Statement::Each), + InstanceMethod("reset", &Statement::Reset), + InstanceMethod("finalize", &Statement::Finalize_), + }); - Local t = Nan::New(New); - - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Statement").ToLocalChecked()); + exports.Set("Statement", t); + return exports; +} - Nan::SetPrototypeMethod(t, "bind", Bind); - Nan::SetPrototypeMethod(t, "get", Get); - Nan::SetPrototypeMethod(t, "run", Run); - Nan::SetPrototypeMethod(t, "all", All); - Nan::SetPrototypeMethod(t, "each", Each); - Nan::SetPrototypeMethod(t, "reset", Reset); - Nan::SetPrototypeMethod(t, "finalize", Finalize); +// A Napi InstanceOf for Javascript Objects "Date" and "RegExp" +bool OtherInstanceOf(Napi::Object source, const char* object_type) { + if (strncmp(object_type, "Date", 4) == 0) { + return source.InstanceOf(source.Env().Global().Get("Date").As()); + } else if (strncmp(object_type, "RegExp", 6) == 0) { + return source.InstanceOf(source.Env().Global().Get("RegExp").As()); + } - constructor_template.Reset(t); - Nan::Set(target, Nan::New("Statement").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + return false; } void Statement::Process() { @@ -38,11 +42,10 @@ void Statement::Process() { } while (prepared && !locked && !queue.empty()) { - Call* call = queue.front(); + std::unique_ptr call(queue.front()); queue.pop(); call->callback(call->baton); - delete call; } } @@ -60,72 +63,76 @@ void Statement::Schedule(Work_Callback callback, Baton* baton) { } template void Statement::Error(T* baton) { - Nan::HandleScope scope; - Statement* stmt = baton->stmt; + + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + // Fail hard on logic errors. assert(stmt->status != 0); - EXCEPTION(Nan::New(stmt->message.c_str()).ToLocalChecked(), stmt->status, exception); + EXCEPTION(Napi::String::New(env, stmt->message.c_str()), stmt->status, exception); - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(stmt->handle(), 2, argv); + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(stmt->Value(), 2, argv); } } // { Database db, String sql, Array params, Function callback } -NAN_METHOD(Statement::New) { - if (!info.IsConstructCall()) { - return Nan::ThrowTypeError("Use the new operator to create new Statement objects"); - } - +Statement::Statement(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); int length = info.Length(); if (length <= 0 || !Database::HasInstance(info[0])) { - return Nan::ThrowTypeError("Database object expected"); + Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); + return; } - else if (length <= 1 || !info[1]->IsString()) { - return Nan::ThrowTypeError("SQL query expected"); + else if (length <= 1 || !info[1].IsString()) { + Napi::TypeError::New(env, "SQL query expected").ThrowAsJavaScriptException(); + return; } - else if (length > 2 && !info[2]->IsUndefined() && !info[2]->IsFunction()) { - return Nan::ThrowTypeError("Callback expected"); + else if (length > 2 && !info[2].IsUndefined() && !info[2].IsFunction()) { + Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); + return; } - Database* db = Nan::ObjectWrap::Unwrap(info[0].As()); - Local sql = Local::Cast(info[1]); + Database* db = Napi::ObjectWrap::Unwrap(info[0].As()); + Napi::String sql = info[1].As(); - info.This()->ForceSet(Nan::New("sql").ToLocalChecked(), sql, ReadOnly); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("sql", sql, napi_default)); - Statement* stmt = new Statement(db); - stmt->Wrap(info.This()); + init(db); + Statement* stmt = this; - PrepareBaton* baton = new PrepareBaton(db, Local::Cast(info[2]), stmt); - baton->sql = std::string(*Nan::Utf8String(sql)); + PrepareBaton* baton = new PrepareBaton(db, info[2].As(), stmt); + baton->sql = std::string(sql.As().Utf8Value().c_str()); db->Schedule(Work_BeginPrepare, baton); - - info.GetReturnValue().Set(info.This()); } void Statement::Work_BeginPrepare(Database::Baton* baton) { assert(baton->db->open); baton->db->pending++; - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Prepare, (uv_after_work_cb)Work_AfterPrepare); + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Statement.Prepare"), + Work_Prepare, Work_AfterPrepare, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Statement::Work_Prepare(uv_work_t* req) { +void Statement::Work_Prepare(napi_env e, void* data) { STATEMENT_INIT(PrepareBaton); // In case preparing fails, we use a mutex to make sure we get the associated // error message. - sqlite3_mutex* mtx = sqlite3_db_mutex(baton->db->_handle); + STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); stmt->status = sqlite3_prepare_v2( @@ -144,21 +151,23 @@ void Statement::Work_Prepare(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterPrepare(uv_work_t* req) { - Nan::HandleScope scope; +void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Statement* stmt = baton->stmt; - STATEMENT_INIT(PrepareBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); if (stmt->status != SQLITE_OK) { - Error(baton); - stmt->Finalize(); + Error(baton.get()); + stmt->Finalize_(); } else { stmt->prepared = true; - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + if (!baton->callback.IsEmpty() && baton->callback.Value().IsFunction()) { + Napi::Function cb = baton->callback.Value(); + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } @@ -166,77 +175,94 @@ void Statement::Work_AfterPrepare(uv_work_t* req) { } template Values::Field* - Statement::BindParameter(const Local source, T pos) { - if (source->IsString() || source->IsRegExp()) { - Nan::Utf8String val(source); - return new Values::Text(pos, val.length(), *val); - } - else if (source->IsInt32()) { - return new Values::Integer(pos, Nan::To(source).FromJust()); - } - else if (source->IsNumber()) { - return new Values::Float(pos, Nan::To(source).FromJust()); + Statement::BindParameter(const Napi::Value source, T pos) { + if (source.IsString()) { + std::string val = source.As().Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); + } + else if (OtherInstanceOf(source.As(), "RegExp")) { + std::string val = source.ToString().Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); + } + else if (source.IsNumber()) { + if (OtherIsInt(source.As())) { + return new Values::Integer(pos, source.As().Int32Value()); + } else { + return new Values::Float(pos, source.As().DoubleValue()); + } } - else if (source->IsBoolean()) { - return new Values::Integer(pos, Nan::To(source).FromJust() ? 1 : 0); + else if (source.IsBoolean()) { + return new Values::Integer(pos, source.As().Value() ? 1 : 0); } - else if (source->IsNull()) { + else if (source.IsNull()) { return new Values::Null(pos); } - else if (Buffer::HasInstance(source)) { - Local buffer = Nan::To(source).ToLocalChecked(); - return new Values::Blob(pos, Buffer::Length(buffer), Buffer::Data(buffer)); + else if (source.IsBuffer()) { + Napi::Buffer buffer = source.As>(); + return new Values::Blob(pos, buffer.Length(), buffer.Data()); + } + else if (OtherInstanceOf(source.As(), "Date")) { + return new Values::Float(pos, source.ToNumber().DoubleValue()); } - else if (source->IsDate()) { - return new Values::Float(pos, Nan::To(source).FromJust()); + else if (source.IsObject()) { + Napi::String napiVal = source.ToString(); + // Check whether toString returned a value that is not undefined. + if(napiVal.Type() == 0) { + return NULL; + } + + std::string val = napiVal.Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); } else { return NULL; } } -template T* Statement::Bind(Nan::NAN_METHOD_ARGS_TYPE info, int start, int last) { - Nan::HandleScope scope; +template T* Statement::Bind(const Napi::CallbackInfo& info, int start, int last) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); if (last < 0) last = info.Length(); - Local callback; - if (last > start && info[last - 1]->IsFunction()) { - callback = Local::Cast(info[last - 1]); + Napi::Function callback; + if (last > start && info[last - 1].IsFunction()) { + callback = info[last - 1].As(); last--; } T* baton = new T(this, callback); if (start < last) { - if (info[start]->IsArray()) { - Local array = Local::Cast(info[start]); - int length = array->Length(); + if (info[start].IsArray()) { + Napi::Array array = info[start].As(); + int length = array.Length(); // Note: bind parameters start with 1. for (int i = 0, pos = 1; i < length; i++, pos++) { - baton->parameters.push_back(BindParameter(Nan::Get(array, i).ToLocalChecked(), pos)); + baton->parameters.push_back(BindParameter((array).Get(i), pos)); } } - else if (!info[start]->IsObject() || info[start]->IsRegExp() || info[start]->IsDate() || Buffer::HasInstance(info[start])) { + else if (!info[start].IsObject() || OtherInstanceOf(info[start].As(), "RegExp") || OtherInstanceOf(info[start].As(), "Date") || info[start].IsBuffer()) { // Parameters directly in array. // Note: bind parameters start with 1. for (int i = start, pos = 1; i < last; i++, pos++) { baton->parameters.push_back(BindParameter(info[i], pos)); } } - else if (info[start]->IsObject()) { - Local object = Local::Cast(info[start]); - Local array = Nan::GetPropertyNames(object).ToLocalChecked(); - int length = array->Length(); + else if (info[start].IsObject()) { + Napi::Object object = info[start].As(); + Napi::Array array = object.GetPropertyNames(); + int length = array.Length(); for (int i = 0; i < length; i++) { - Local name = Nan::Get(array, i).ToLocalChecked(); + Napi::Value name = (array).Get(i); + Napi::Number num = name.ToNumber(); - if (name->IsInt32()) { + if (num.Int32Value() == num.DoubleValue()) { baton->parameters.push_back( - BindParameter(Nan::Get(object, name).ToLocalChecked(), Nan::To(name).FromJust())); + BindParameter((object).Get(name), num.Int32Value())); } else { - baton->parameters.push_back(BindParameter(Nan::Get(object, name).ToLocalChecked(), - *Nan::Utf8String(name))); + baton->parameters.push_back(BindParameter((object).Get(name), + name.As().Utf8Value().c_str())); } } } @@ -263,7 +289,7 @@ bool Statement::Bind(const Parameters & parameters) { Values::Field* field = *it; if (field != NULL) { - int pos; + unsigned int pos; if (field->index > 0) { pos = field->index; } @@ -305,16 +331,18 @@ bool Statement::Bind(const Parameters & parameters) { return true; } -NAN_METHOD(Statement::Bind) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Bind(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowTypeError("Data type is not supported"); + Napi::TypeError::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginBind, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -322,29 +350,31 @@ void Statement::Work_BeginBind(Baton* baton) { STATEMENT_BEGIN(Bind); } -void Statement::Work_Bind(uv_work_t* req) { +void Statement::Work_Bind(napi_env e, void* data) { STATEMENT_INIT(Baton); - sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); + STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); stmt->Bind(baton->parameters); sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterBind(uv_work_t* req) { - Nan::HandleScope scope; +void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Statement* stmt = baton->stmt; - STATEMENT_INIT(Baton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); if (stmt->status != SQLITE_OK) { - Error(baton); + Error(baton.get()); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } @@ -353,16 +383,18 @@ void Statement::Work_AfterBind(uv_work_t* req) { -NAN_METHOD(Statement::Get) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Get(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginGet, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -370,11 +402,11 @@ void Statement::Work_BeginGet(Baton* baton) { STATEMENT_BEGIN(Get); } -void Statement::Work_Get(uv_work_t* req) { +void Statement::Work_Get(napi_env e, void* data) { STATEMENT_INIT(RowBaton); if (stmt->status != SQLITE_DONE || baton->parameters.size()) { - sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); + STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); if (stmt->Bind(baton->parameters)) { @@ -394,26 +426,28 @@ void Statement::Work_Get(uv_work_t* req) { } } -void Statement::Work_AfterGet(uv_work_t* req) { - Nan::HandleScope scope; +void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Statement* stmt = baton->stmt; - STATEMENT_INIT(RowBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { - Error(baton); + Error(baton.get()); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { if (stmt->status == SQLITE_ROW) { // Create the result array from the data we acquired. - Local argv[] = { Nan::Null(), RowToJS(&baton->row) }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + Napi::Value argv[] = { env.Null(), RowToJS(env, &baton->row) }; + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } else { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } } @@ -421,16 +455,18 @@ void Statement::Work_AfterGet(uv_work_t* req) { STATEMENT_END(); } -NAN_METHOD(Statement::Run) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Run(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginRun, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -438,10 +474,10 @@ void Statement::Work_BeginRun(Baton* baton) { STATEMENT_BEGIN(Run); } -void Statement::Work_Run(uv_work_t* req) { +void Statement::Work_Run(napi_env e, void* data) { STATEMENT_INIT(RunBaton); - sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); + STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); // Make sure that we also reset when there are no parameters. @@ -464,39 +500,43 @@ void Statement::Work_Run(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterRun(uv_work_t* req) { - Nan::HandleScope scope; +void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Statement* stmt = baton->stmt; - STATEMENT_INIT(RunBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { - Error(baton); + Error(baton.get()); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Nan::Set(stmt->handle(), Nan::New("lastID").ToLocalChecked(), Nan::New(baton->inserted_id)); - Nan::Set(stmt->handle(), Nan::New("changes").ToLocalChecked(), Nan::New(baton->changes)); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + (stmt->Value()).Set(Napi::String::New(env, "lastID"), Napi::Number::New(env, baton->inserted_id)); + (stmt->Value()).Set( Napi::String::New(env, "changes"), Napi::Number::New(env, baton->changes)); - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } STATEMENT_END(); } -NAN_METHOD(Statement::All) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::All(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginAll, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -504,10 +544,10 @@ void Statement::Work_BeginAll(Baton* baton) { STATEMENT_BEGIN(All); } -void Statement::Work_All(uv_work_t* req) { +void Statement::Work_All(napi_env e, void* data) { STATEMENT_INIT(RowsBaton); - sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); + STATEMENT_MUTEX(mtx); sqlite3_mutex_enter(mtx); // Make sure that we also reset when there are no parameters. @@ -530,38 +570,40 @@ void Statement::Work_All(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterAll(uv_work_t* req) { - Nan::HandleScope scope; +void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Statement* stmt = baton->stmt; - STATEMENT_INIT(RowsBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); if (stmt->status != SQLITE_DONE) { - Error(baton); + Error(baton.get()); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { if (baton->rows.size()) { // Create the result array from the data we acquired. - Local result(Nan::New(baton->rows.size())); + Napi::Array result(Napi::Array::New(env, baton->rows.size())); Rows::const_iterator it = baton->rows.begin(); Rows::const_iterator end = baton->rows.end(); for (int i = 0; it < end; ++it, i++) { - Nan::Set(result, i, RowToJS(*it)); - delete *it; + std::unique_ptr row(*it); + (result).Set(i, RowToJS(env,row.get())); } - Local argv[] = { Nan::Null(), result }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + Napi::Value argv[] = { env.Null(), result }; + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } else { // There were no result rows. - Local argv[] = { - Nan::Null(), - Nan::New(0) + Napi::Value argv[] = { + env.Null(), + Napi::Array::New(env, 0) }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } } } @@ -569,24 +611,26 @@ void Statement::Work_AfterAll(uv_work_t* req) { STATEMENT_END(); } -NAN_METHOD(Statement::Each) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Each(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; int last = info.Length(); - Local completed; - if (last >= 2 && info[last - 1]->IsFunction() && info[last - 2]->IsFunction()) { - completed = Local::Cast(info[--last]); + Napi::Function completed; + if (last >= 2 && info[last - 1].IsFunction() && info[last - 2].IsFunction()) { + completed = info[--last].As(); } EachBaton* baton = stmt->Bind(info, 0, last); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { - baton->completed.Reset(completed); + baton->completed.Reset(completed, 1); stmt->Schedule(Work_BeginEach, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -595,18 +639,18 @@ void Statement::Work_BeginEach(Baton* baton) { // the event loop. This prevents dangling events. EachBaton* each_baton = static_cast(baton); each_baton->async = new Async(each_baton->stmt, reinterpret_cast(AsyncEach)); - each_baton->async->item_cb.Reset(each_baton->callback); - each_baton->async->completed_cb.Reset(each_baton->completed); + each_baton->async->item_cb.Reset(each_baton->callback.Value(), 1); + each_baton->async->completed_cb.Reset(each_baton->completed.Value(), 1); STATEMENT_BEGIN(Each); } -void Statement::Work_Each(uv_work_t* req) { +void Statement::Work_Each(napi_env e, void* data) { STATEMENT_INIT(EachBaton); Async* async = baton->async; - sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); + STATEMENT_MUTEX(mtx); int retrieved = 0; @@ -651,11 +695,12 @@ void Statement::CloseCallback(uv_handle_t* handle) { delete async; } -void Statement::AsyncEach(uv_async_t* handle, int status) { - Nan::HandleScope scope; - +void Statement::AsyncEach(uv_async_t* handle) { Async* async = static_cast(handle->data); + Napi::Env env = async->stmt->Env(); + Napi::HandleScope scope(env); + while (true) { // Get the contents out of the data cache for us to process in the JS callback. Rows rows; @@ -667,116 +712,121 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { break; } - Local cb = Nan::New(async->item_cb); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[2]; - argv[0] = Nan::Null(); + Napi::Function cb = async->item_cb.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[2]; + argv[0] = env.Null(); Rows::const_iterator it = rows.begin(); Rows::const_iterator end = rows.end(); for (int i = 0; it < end; ++it, i++) { - argv[1] = RowToJS(*it); + std::unique_ptr row(*it); + argv[1] = RowToJS(env,row.get()); async->retrieved++; - TRY_CATCH_CALL(async->stmt->handle(), cb, 2, argv); - delete *it; + TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); } } } - Local cb = Nan::New(async->completed_cb); + Napi::Function cb = async->completed_cb.Value(); if (async->completed) { if (!cb.IsEmpty() && - cb->IsFunction()) { - Local argv[] = { - Nan::Null(), - Nan::New(async->retrieved) + cb.IsFunction()) { + Napi::Value argv[] = { + env.Null(), + Napi::Number::New(env, async->retrieved) }; - TRY_CATCH_CALL(async->stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); } uv_close(reinterpret_cast(handle), CloseCallback); } } -void Statement::Work_AfterEach(uv_work_t* req) { - Nan::HandleScope scope; +void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Statement* stmt = baton->stmt; - STATEMENT_INIT(EachBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); if (stmt->status != SQLITE_DONE) { - Error(baton); + Error(baton.get()); } STATEMENT_END(); } -NAN_METHOD(Statement::Reset) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Reset(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(stmt, callback); stmt->Schedule(Work_BeginReset, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Statement::Work_BeginReset(Baton* baton) { STATEMENT_BEGIN(Reset); } -void Statement::Work_Reset(uv_work_t* req) { +void Statement::Work_Reset(napi_env e, void* data) { STATEMENT_INIT(Baton); sqlite3_reset(stmt->_handle); stmt->status = SQLITE_OK; } -void Statement::Work_AfterReset(uv_work_t* req) { - Nan::HandleScope scope; +void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) { + std::unique_ptr baton(static_cast(data)); + Statement* stmt = baton->stmt; - STATEMENT_INIT(Baton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } STATEMENT_END(); } -Local Statement::RowToJS(Row* row) { - Nan::EscapableHandleScope scope; +Napi::Value Statement::RowToJS(Napi::Env env, Row* row) { + Napi::EscapableHandleScope scope(env); - Local result = Nan::New(); + Napi::Object result = Napi::Object::New(env); Row::const_iterator it = row->begin(); Row::const_iterator end = row->end(); for (int i = 0; it < end; ++it, i++) { Values::Field* field = *it; - Local value; + Napi::Value value; switch (field->type) { case SQLITE_INTEGER: { - value = Nan::New(((Values::Integer*)field)->value); + value = Napi::Number::New(env, ((Values::Integer*)field)->value); } break; case SQLITE_FLOAT: { - value = Nan::New(((Values::Float*)field)->value); + value = Napi::Number::New(env, ((Values::Float*)field)->value); } break; case SQLITE_TEXT: { - value = Nan::New(((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()).ToLocalChecked(); + value = Napi::String::New(env, ((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()); } break; case SQLITE_BLOB: { - value = Nan::CopyBuffer(((Values::Blob*)field)->value, ((Values::Blob*)field)->length).ToLocalChecked(); + value = Napi::Buffer::Copy(env, ((Values::Blob*)field)->value, ((Values::Blob*)field)->length); } break; case SQLITE_NULL: { - value = Nan::Null(); + value = env.Null(); } break; } - Nan::Set(result, Nan::New(field->name.c_str()).ToLocalChecked(), value); + (result).Set(Napi::String::New(env, field->name.c_str()), value); DELETE_FIELD(field); } @@ -816,31 +866,32 @@ void Statement::GetRow(Row* row, sqlite3_stmt* stmt) { } } -NAN_METHOD(Statement::Finalize) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Finalize_(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(stmt, callback); - stmt->Schedule(Finalize, baton); + stmt->Schedule(Finalize_, baton); - info.GetReturnValue().Set(stmt->db->handle()); + return stmt->db->Value(); } -void Statement::Finalize(Baton* baton) { - Nan::HandleScope scope; +void Statement::Finalize_(Baton* b) { + std::unique_ptr baton(b); + Napi::Env env = baton->stmt->Env(); + Napi::HandleScope scope(env); - baton->stmt->Finalize(); + baton->stmt->Finalize_(); // Fire callback in case there was one. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(baton->stmt->handle(), cb, 0, NULL); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(baton->stmt->Value(), cb, 0, NULL); } - - delete baton; } -void Statement::Finalize() { +void Statement::Finalize_() { assert(!finalized); finalized = true; CleanQueue(); @@ -852,50 +903,45 @@ void Statement::Finalize() { } void Statement::CleanQueue() { - Nan::HandleScope scope; + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); if (prepared && !queue.empty()) { // This statement has already been prepared and is now finalized. // Fire error for all remaining items in the queue. - EXCEPTION(Nan::New("Statement is already finalized").ToLocalChecked(), SQLITE_MISUSE, exception); - Local argv[] = { exception }; + EXCEPTION(Napi::String::New(env, "Statement is already finalized"), SQLITE_MISUSE, exception); + Napi::Value argv[] = { exception }; bool called = false; // Clear out the queue so that this object can get GC'ed. while (!queue.empty()) { - Call* call = queue.front(); + std::unique_ptr call(queue.front()); queue.pop(); - Local cb = Nan::New(call->baton->callback); + std::unique_ptr baton(call->baton); + Napi::Function cb = baton->callback.Value(); if (prepared && !cb.IsEmpty() && - cb->IsFunction()) { - TRY_CATCH_CALL(handle(), cb, 1, argv); + cb.IsFunction()) { + TRY_CATCH_CALL(Value(), cb, 1, argv); called = true; } - - // We don't call the actual callback, so we have to make sure that - // the baton gets destroyed. - delete call->baton; - delete call; } // When we couldn't call a callback function, emit an error on the // Statement object. if (!called) { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, info); } } else while (!queue.empty()) { // Just delete all items in the queue; we already fired an event when // preparing the statement failed. - Call* call = queue.front(); + std::unique_ptr call(queue.front()); queue.pop(); - // We don't call the actual callback, so we have to make sure that // the baton gets destroyed. delete call->baton; - delete call; } } diff --git a/src/statement.h b/src/statement.h index 90d295b70..904e52175 100644 --- a/src/statement.h +++ b/src/statement.h @@ -1,10 +1,6 @@ #ifndef NODE_SQLITE3_SRC_STATEMENT_H #define NODE_SQLITE3_SRC_STATEMENT_H - -#include "database.h" -#include "threading.h" - #include #include #include @@ -12,10 +8,13 @@ #include #include -#include +#include +#include -using namespace v8; -using namespace node; +#include "database.h" +#include "threading.h" + +using namespace Napi; namespace node_sqlite3 { @@ -71,49 +70,47 @@ typedef Row Parameters; -class Statement : public Nan::ObjectWrap { +class Statement : public Napi::ObjectWrap { public: - static Nan::Persistent constructor_template; - - static NAN_MODULE_INIT(Init); - static NAN_METHOD(New); + static Napi::Object Init(Napi::Env env, Napi::Object exports); + static Napi::Value New(const Napi::CallbackInfo& info); struct Baton { - uv_work_t request; + napi_async_work request = NULL; Statement* stmt; - Nan::Persistent callback; + Napi::FunctionReference callback; Parameters parameters; - Baton(Statement* stmt_, Local cb_) : stmt(stmt_) { + Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) { stmt->Ref(); - request.data = this; - callback.Reset(cb_); + callback.Reset(cb_, 1); } virtual ~Baton() { for (unsigned int i = 0; i < parameters.size(); i++) { Values::Field* field = parameters[i]; DELETE_FIELD(field); } + if (request) napi_delete_async_work(stmt->Env(), request); stmt->Unref(); callback.Reset(); } }; struct RowBaton : Baton { - RowBaton(Statement* stmt_, Local cb_) : + RowBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Row row; }; struct RunBaton : Baton { - RunBaton(Statement* stmt_, Local cb_) : + RunBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_), inserted_id(0), changes(0) {} sqlite3_int64 inserted_id; int changes; }; struct RowsBaton : Baton { - RowsBaton(Statement* stmt_, Local cb_) : + RowsBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Rows rows; }; @@ -121,10 +118,10 @@ class Statement : public Nan::ObjectWrap { struct Async; struct EachBaton : Baton { - Nan::Persistent completed; + Napi::FunctionReference completed; Async* async; // Isn't deleted when the baton is deleted. - EachBaton(Statement* stmt_, Local cb_) : + EachBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} virtual ~EachBaton() { completed.Reset(); @@ -134,7 +131,7 @@ class Statement : public Nan::ObjectWrap { struct PrepareBaton : Database::Baton { Statement* stmt; std::string sql; - PrepareBaton(Database* db_, Local cb_, Statement* stmt_) : + PrepareBaton(Database* db_, Napi::Function cb_, Statement* stmt_) : Baton(db_, cb_), stmt(stmt_) { stmt->Ref(); } @@ -143,7 +140,7 @@ class Statement : public Nan::ObjectWrap { if (!db->IsOpen() && db->IsLocked()) { // The database handle was closed before the statement could be // prepared. - stmt->Finalize(); + stmt->Finalize_(); } } }; @@ -166,15 +163,17 @@ class Statement : public Nan::ObjectWrap { // Store the callbacks here because we don't have // access to the baton in the async callback. - Nan::Persistent item_cb; - Nan::Persistent completed_cb; + Napi::FunctionReference item_cb; + Napi::FunctionReference completed_cb; Async(Statement* st, uv_async_cb async_cb) : stmt(st), completed(false), retrieved(0) { watcher.data = this; NODE_SQLITE3_MUTEX_INIT stmt->Ref(); - uv_async_init(uv_default_loop(), &watcher, async_cb); + uv_loop_t *loop; + napi_get_uv_event_loop(stmt->Env(), &loop); + uv_async_init(loop, &watcher, async_cb); } ~Async() { @@ -185,18 +184,20 @@ class Statement : public Nan::ObjectWrap { } }; - Statement(Database* db_) : Nan::ObjectWrap(), - db(db_), - _handle(NULL), - status(SQLITE_OK), - prepared(false), - locked(true), - finalized(false) { + void init(Database* db_) { + db = db_; + _handle = NULL; + status = SQLITE_OK; + prepared = false; + locked = true; + finalized = false; db->Ref(); } + Statement(const Napi::CallbackInfo& info); + ~Statement() { - if (!finalized) Finalize(); + if (!finalized) Finalize_(); } WORK_DEFINITION(Bind); @@ -206,25 +207,25 @@ class Statement : public Nan::ObjectWrap { WORK_DEFINITION(Each); WORK_DEFINITION(Reset); - static NAN_METHOD(Finalize); + Napi::Value Finalize_(const Napi::CallbackInfo& info); protected: static void Work_BeginPrepare(Database::Baton* baton); - static void Work_Prepare(uv_work_t* req); - static void Work_AfterPrepare(uv_work_t* req); + static void Work_Prepare(napi_env env, void* data); + static void Work_AfterPrepare(napi_env env, napi_status status, void* data); - static void AsyncEach(uv_async_t* handle, int status); + static void AsyncEach(uv_async_t* handle); static void CloseCallback(uv_handle_t* handle); - static void Finalize(Baton* baton); - void Finalize(); + static void Finalize_(Baton* baton); + void Finalize_(); - template inline Values::Field* BindParameter(const Local source, T pos); - template T* Bind(Nan::NAN_METHOD_ARGS_TYPE info, int start = 0, int end = -1); + template inline Values::Field* BindParameter(const Napi::Value source, T pos); + template T* Bind(const Napi::CallbackInfo& info, int start = 0, int end = -1); bool Bind(const Parameters ¶meters); static void GetRow(Row* row, sqlite3_stmt* stmt); - static Local RowToJS(Row* row); + static Napi::Value RowToJS(Napi::Env env, Row* row); void Schedule(Work_Callback callback, Baton* baton); void Process(); void CleanQueue(); diff --git a/test/affected.test.js b/test/affected.test.js index f0bf192e6..031dc1b9c 100644 --- a/test/affected.test.js +++ b/test/affected.test.js @@ -11,7 +11,7 @@ describe('query properties', function() { it('should return the correct lastID', function(done) { var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); var j = 1; - for (var i = 0; i < 1000; i++) { + for (var i = 0; i < 5000; i++) { stmt.run(i, "demo", function(err) { if (err) throw err; // Relies on SQLite's row numbering to be gapless and starting @@ -25,7 +25,7 @@ describe('query properties', function() { it('should return the correct changes count', function(done) { db.run("UPDATE foo SET id = id + 1 WHERE id % 2 = 0", function(err) { if (err) throw err; - assert.equal(500, this.changes); + assert.equal(2500, this.changes); done(); }); }); diff --git a/test/backup.test.js b/test/backup.test.js new file mode 100644 index 000000000..c05c298f8 --- /dev/null +++ b/test/backup.test.js @@ -0,0 +1,279 @@ +var sqlite3 = require('..'); +var assert = require('assert'); +var fs = require('fs'); +var helper = require('./support/helper'); + +// Check that the number of rows in two tables matches. +function assertRowsMatchDb(db1, table1, db2, table2, done) { + db1.get("SELECT COUNT(*) as count FROM " + table1, function(err, row) { + if (err) throw err; + db2.get("SELECT COUNT(*) as count FROM " + table2, function(err, row2) { + if (err) throw err; + assert.equal(row.count, row2.count); + done(); + }); + }); +} + +// Check that the number of rows in the table "foo" is preserved in a backup. +function assertRowsMatchFile(db, backupName, done) { + var db2 = new sqlite3.Database(backupName, sqlite3.OPEN_READONLY, function(err) { + if (err) throw err; + assertRowsMatchDb(db, 'foo', db2, 'foo', function() { + db2.close(done); + }); + }); +} + +describe('backup', function() { + before(function() { + helper.ensureExists('test/tmp'); + }); + + var db; + beforeEach(function(done) { + helper.deleteFile('test/tmp/backup.db'); + helper.deleteFile('test/tmp/backup2.db'); + db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); + }); + + afterEach(function(done) { + if (!db) { done(); } + db.close(done); + }); + + it ('output db created once step is called', function(done) { + var backup = db.backup('test/tmp/backup.db', function(err) { + if (err) throw err; + backup.step(1, function(err) { + if (err) throw err; + assert.fileExists('test/tmp/backup.db'); + backup.finish(done); + }); + }); + }); + + it ('copies source fully with step(-1)', function(done) { + var backup = db.backup('test/tmp/backup.db'); + backup.step(-1, function(err) { + if (err) throw err; + assert.fileExists('test/tmp/backup.db'); + backup.finish(function(err) { + if (err) throw err; + assertRowsMatchFile(db, 'test/tmp/backup.db', done); + }); + }); + }); + + it ('backup db not created if finished immediately', function(done) { + var backup = db.backup('test/tmp/backup.db'); + backup.finish(function(err) { + if (err) throw err; + assert.fileDoesNotExist('test/tmp/backup.db'); + done(); + }); + }); + + it ('error closing db if backup not finished', function(done) { + var backup = db.backup('test/tmp/backup.db'); + db.close(function(err) { + db = null; + if (!err) throw new Error('should have an error'); + if (err.errno == sqlite3.BUSY) { + done(); + } + else throw err; + }); + }); + + it ('using the backup after finished is an error', function(done) { + var backup = db.backup('test/tmp/backup.db'); + backup.finish(function(err) { + if (err) throw err; + backup.step(1, function(err) { + if (!err) throw new Error('should have an error'); + if (err.errno == sqlite3.MISUSE && + err.message === 'SQLITE_MISUSE: Backup is already finished') { + done(); + } + else throw err; + }); + }); + }); + + it ('remaining/pageCount are available after call to step', function(done) { + var backup = db.backup('test/tmp/backup.db'); + backup.step(0, function(err) { + if (err) throw err; + assert.equal(typeof this.pageCount, 'number'); + assert.equal(typeof this.remaining, 'number'); + assert.equal(this.remaining, this.pageCount); + var prevRemaining = this.remaining; + var prevPageCount = this.pageCount; + backup.step(1, function(err) { + if (err) throw err; + assert.notEqual(this.remaining, prevRemaining); + assert.equal(this.pageCount, prevPageCount); + backup.finish(done); + }); + }); + }); + + it ('backup works if database is modified half-way through', function(done) { + var backup = db.backup('test/tmp/backup.db'); + backup.step(-1, function(err) { + if (err) throw err; + backup.finish(function(err) { + if (err) throw err; + var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { + if (err) throw err; + var backup2 = db2.backup('test/tmp/backup2.db'); + backup2.step(1, function(err, completed) { + if (err) throw err; + assert.equal(completed, false); // Page size for the test db + // should not be raised to high. + db2.exec("insert into foo(txt) values('hello')", function(err) { + if (err) throw err; + backup2.step(-1, function(err, completed) { + if (err) throw err; + assert.equal(completed, true); + assertRowsMatchFile(db2, 'test/tmp/backup2.db', function() { + backup2.finish(function(err) { + if (err) throw err; + db2.close(done); + }); + }); + }); + }); + }); + }); + }); + }); + }); + + (sqlite3.VERSION_NUMBER < 3026000 ? it.skip : it) ('can backup from temp to main', function(done) { + db.exec("CREATE TEMP TABLE space (txt TEXT)", function(err) { + if (err) throw err; + db.exec("INSERT INTO space(txt) VALUES('monkey')", function(err) { + if (err) throw err; + var backup = db.backup('test/tmp/backup.db', 'temp', 'main', true, function(err) { + if (err) throw err; + backup.step(-1, function(err) { + if (err) throw err; + backup.finish(function(err) { + if (err) throw err; + var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { + if (err) throw err; + db2.get("SELECT * FROM space", function(err, row) { + if (err) throw err; + assert.equal(row.txt, 'monkey'); + db2.close(done); + }); + }); + }); + }); + }); + }); + }); + }); + + (sqlite3.VERSION_NUMBER < 3026000 ? it.skip : it) ('can backup from main to temp', function(done) { + var backup = db.backup('test/support/prepare.db', 'main', 'temp', false, function(err) { + if (err) throw err; + backup.step(-1, function(err) { + if (err) throw err; + backup.finish(function(err) { + if (err) throw err; + assertRowsMatchDb(db, 'temp.foo', db, 'main.foo', done); + }); + }); + }); + }); + + it ('cannot backup to a locked db', function(done) { + var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { + db2.exec("PRAGMA locking_mode = EXCLUSIVE"); + db2.exec("BEGIN EXCLUSIVE", function(err) { + if (err) throw err; + var backup = db.backup('test/tmp/backup.db'); + backup.step(-1, function(stepErr) { + db2.close(function(err) { + if (err) throw err; + if (stepErr.errno == sqlite3.BUSY) { + backup.finish(done); + } + else throw stepErr; + }); + }); + }); + }); + }); + + it ('fuss-free incremental backups work', function(done) { + var backup = db.backup('test/tmp/backup.db'); + var timer; + function makeProgress() { + if (backup.idle) { + backup.step(1); + } + if (backup.completed || backup.failed) { + clearInterval(timer); + assert.equal(backup.completed, true); + assert.equal(backup.failed, false); + done(); + } + } + timer = setInterval(makeProgress, 2); + }); + + it ('setting retryErrors to empty disables automatic finishing', function(done) { + var backup = db.backup('test/tmp/backup.db'); + backup.retryErrors = []; + backup.step(-1, function(err) { + if (err) throw err; + db.close(function(err) { + db = null; + if (!err) throw new Error('should have an error'); + assert.equal(err.errno, sqlite3.BUSY); + done(); + }); + }); + }); + + it ('setting retryErrors enables automatic finishing', function(done) { + var backup = db.backup('test/tmp/backup.db'); + backup.retryErrors = [sqlite3.OK]; + backup.step(-1, function(err) { + if (err) throw err; + db.close(function(err) { + if (err) throw err; + db = null; + done(); + }); + }); + }); + + it ('default retryErrors will retry on a locked/busy db', function(done) { + var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) { + db2.exec("PRAGMA locking_mode = EXCLUSIVE"); + db2.exec("BEGIN EXCLUSIVE", function(err) { + if (err) throw err; + var backup = db.backup('test/tmp/backup.db'); + backup.step(-1, function(stepErr) { + db2.close(function(err) { + if (err) throw err; + assert.equal(stepErr.errno, sqlite3.BUSY); + assert.equal(backup.completed, false); + assert.equal(backup.failed, false); + backup.step(-1, function(err) { + if (err) throw err; + assert.equal(backup.completed, true); + assert.equal(backup.failed, false); + done(); + }); + }); + }); + }); + }); + }); +}); diff --git a/test/constants.test.js b/test/constants.test.js index 126d10141..7e1affdda 100644 --- a/test/constants.test.js +++ b/test/constants.test.js @@ -6,6 +6,10 @@ describe('constants', function() { assert.ok(sqlite3.OPEN_READONLY === 1); assert.ok(sqlite3.OPEN_READWRITE === 2); assert.ok(sqlite3.OPEN_CREATE === 4); + assert.ok(sqlite3.OPEN_URI === 0x00000040); + assert.ok(sqlite3.OPEN_FULLMUTEX === 0x00010000); + assert.ok(sqlite3.OPEN_SHAREDCACHE === 0x00020000); + assert.ok(sqlite3.OPEN_PRIVATECACHE === 0x00040000); }); it('should have the right error flags', function() { diff --git a/test/database_fail.test.js b/test/database_fail.test.js index 35589ece5..8b936769e 100644 --- a/test/database_fail.test.js +++ b/test/database_fail.test.js @@ -10,11 +10,11 @@ describe('error handling', function() { it('throw when calling Database() without new', function() { assert.throws(function() { sqlite3.Database(':memory:'); - }, (/Use the new operator to create new Database objects/)); + }, (/Class constructors cannot be invoked without 'new'/)); assert.throws(function() { sqlite3.Statement(); - }, (/Use the new operator to create new Statement objects/)); + }, (/Class constructors cannot be invoked without 'new'/)); }); it('should error when calling Database#get on a missing table', function(done) { diff --git a/test/each.test.js b/test/each.test.js index 02729c64e..27df32c8c 100644 --- a/test/each.test.js +++ b/test/each.test.js @@ -10,15 +10,16 @@ describe('each', function() { it('retrieve 100,000 rows with Statement#each', function(done) { var total = 100000; var retrieved = 0; + db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) { if (err) throw err; retrieved++; - }); - - db.wait(function() { - assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows."); - done(); + + if(retrieved === total) { + assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows."); + done(); + } }); }); diff --git a/test/issue-108.test.js b/test/issue-108.test.js index 2ea69a30c..637d2c0f4 100644 --- a/test/issue-108.test.js +++ b/test/issue-108.test.js @@ -18,7 +18,7 @@ describe('buffer', function() { }); var buff = new Buffer(2); - stmt.run('a'); + stmt.run(buff); stmt.finalize(); }); diff --git a/test/open_close.test.js b/test/open_close.test.js index 649e21789..49d2af791 100644 --- a/test/open_close.test.js +++ b/test/open_close.test.js @@ -30,6 +30,64 @@ describe('open/close', function() { helper.deleteFile('test/tmp/test_create.db'); }); }); + + describe('open and close non-existant shared database', function() { + before(function() { + helper.deleteFile('test/tmp/test_create_shared.db'); + }); + + var db; + it('should open the database', function(done) { + db = new sqlite3.Database('file:./test/tmp/test_create_shared.db', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); + }); + + it('should close the database', function(done) { + db.close(done); + }); + + it('should have created the file', function() { + assert.fileExists('test/tmp/test_create_shared.db'); + }); + + after(function() { + helper.deleteFile('test/tmp/test_create_shared.db'); + }); + }); + + + (sqlite3.VERSION_NUMBER < 3008000 ? describe.skip : describe)('open and close shared memory database', function() { + + var db1; + var db2; + + it('should open the first database', function(done) { + db1 = new sqlite3.Database('file:./test/tmp/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); + }); + + it('should open the second database', function(done) { + db2 = new sqlite3.Database('file:./test/tmp/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done); + }); + + it('first database should set the user_version', function(done) { + db1.exec('PRAGMA user_version=42', done); + }); + + it('second database should get the user_version', function(done) { + db2.get('PRAGMA user_version', function(err, row) { + if (err) throw err; + assert.equal(row.user_version, 42); + done(); + }); + }); + + it('should close the first database', function(done) { + db1.close(done); + }); + + it('should close the second database', function(done) { + db2.close(done); + }); + }); it('should not be unable to open an inaccessible database', function(done) { // NOTE: test assumes that the user is not allowed to create new files diff --git a/test/other_objects.test.js b/test/other_objects.test.js index 155951791..718598768 100644 --- a/test/other_objects.test.js +++ b/test/other_objects.test.js @@ -86,4 +86,13 @@ describe('data types', function() { }); }); }); + + it('should ignore faulty toString', function(done) { + const faulty = { toString: 23 }; + db.run("INSERT INTO txt_table VALUES(?)", faulty, function (err) { + assert.notEqual(err, undefined); + done(); + }); + }); + }); diff --git a/test/support/createdb-electron.js b/test/support/createdb-electron.js new file mode 100644 index 000000000..c2cac4183 --- /dev/null +++ b/test/support/createdb-electron.js @@ -0,0 +1,10 @@ + +var {app} = require('electron'); +var createdb = require('./createdb.js'); + +createdb(function () { + setTimeout(function () { + app.quit(); + }, 20000); +}); + diff --git a/test/support/createdb.js b/test/support/createdb.js index 2cb24c6a6..0c35871da 100755 --- a/test/support/createdb.js +++ b/test/support/createdb.js @@ -1,36 +1,47 @@ #!/usr/bin/env node -var existsSync = require('fs').existsSync || require('path').existsSync; -var path = require('path'); +function createdb(callback) { + var existsSync = require('fs').existsSync || require('path').existsSync; + var path = require('path'); -var sqlite3 = require('../../lib/sqlite3'); + var sqlite3 = require('../../lib/sqlite3'); -var count = 1000000; -var db_path = path.join(__dirname,'big.db'); + var count = 1000000; + var db_path = path.join(__dirname,'big.db'); -function randomString() { - var str = ''; - var chars = 'abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789 '; - for (var i = Math.random() * 100; i > 0; i--) { - str += chars[Math.floor(Math.random() * chars.length)]; + function randomString() { + var str = ''; + var chars = 'abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789 '; + for (var i = Math.random() * 100; i > 0; i--) { + str += chars[Math.floor(Math.random() * chars.length)]; + } + return str; + }; + + + if (existsSync(db_path)) { + console.log('okay: database already created (' + db_path + ')'); + if (callback) callback(); + } else { + console.log("Creating test database... This may take several minutes."); + var db = new sqlite3.Database(db_path); + db.serialize(function() { + db.run("CREATE TABLE foo (id INT, txt TEXT)"); + db.run("BEGIN TRANSACTION"); + var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); + for (var i = 0; i < count; i++) { + stmt.run(i, randomString()); + } + stmt.finalize(); + db.run("COMMIT TRANSACTION", [], function () { + db.close(callback); + }); + }); } - return str; }; - -if (existsSync(db_path)) { - console.log('okay: database already created (' + db_path + ')'); -} else { - console.log("Creating test database... This may take several minutes."); - var db = new sqlite3.Database(db_path); - db.serialize(function() { - db.run("CREATE TABLE foo (id INT, txt TEXT)"); - db.run("BEGIN TRANSACTION"); - var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); - for (var i = 0; i < count; i++) { - stmt.run(i, randomString()); - } - stmt.finalize(); - db.run("COMMIT TRANSACTION"); - }); +if (require.main === module) { + createdb(); } + +module.exports = createdb; diff --git a/test/upsert.test.js b/test/upsert.test.js new file mode 100644 index 000000000..f3e7e8c17 --- /dev/null +++ b/test/upsert.test.js @@ -0,0 +1,27 @@ +var sqlite3 = require('..'); +var assert = require('assert'); + +describe('query properties', function() { + var db; + before(function(done) { + db = new sqlite3.Database(':memory:'); + db.run("CREATE TABLE foo (id INT PRIMARY KEY, count INT)", done); + }); + + (sqlite3.VERSION_NUMBER < 3024000 ? it.skip : it)('should upsert', function(done) { + var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); + stmt.run(1, 1, function(err) { // insert 1 + if (err) throw err; + var upsert_stmt = db.prepare("INSERT INTO foo VALUES(?, ?) ON CONFLICT (id) DO UPDATE SET count = count + excluded.count"); + upsert_stmt.run(1, 2, function(err) { // add 2 + if (err) throw err; + var select_stmt = db.prepare("SELECT count FROM foo WHERE id = ?"); + select_stmt.get(1, function(err, row) { + if (err) throw err; + assert.equal(row.count, 3); // equals 3 + }); + }) + }); + db.wait(done); + }); +}); diff --git a/test/verbose.test.js b/test/verbose.test.js new file mode 100644 index 000000000..b680280c4 --- /dev/null +++ b/test/verbose.test.js @@ -0,0 +1,60 @@ +var sqlite3 = require('..'); +var assert = require('assert'); + +var invalid_sql = 'update non_existent_table set id=1'; + +var originalMethods = { + Database: {}, + Statement: {}, +}; + +function backupOriginalMethods() { + for (var obj in originalMethods) { + for (var attr in sqlite3[obj].prototype) { + originalMethods[obj][attr] = sqlite3[obj].prototype[attr]; + } + } +} + +function resetVerbose() { + for (var obj in originalMethods) { + for (var attr in originalMethods[obj]) { + sqlite3[obj].prototype[attr] = originalMethods[obj][attr]; + } + } +} + +describe('verbose', function() { + it('Shoud add trace info to error when verbose is called', function(done) { + var db = new sqlite3.Database(':memory:'); + backupOriginalMethods(); + sqlite3.verbose(); + + db.run(invalid_sql, function(err) { + assert(err instanceof Error); + + assert( + err.stack.indexOf(`Database#run('${invalid_sql}'`) > -1, + `Stack shoud contain trace info, stack = ${err.stack}` + ); + + done(); + resetVerbose(); + }); + }); + + it('Shoud not add trace info to error when verbose is not called', function(done) { + var db = new sqlite3.Database(':memory:'); + + db.run(invalid_sql, function(err) { + assert(err instanceof Error); + + assert( + err.stack.indexOf(invalid_sql) === -1, + `Stack shoud not contain trace info, stack = ${err.stack}` + ); + + done(); + }); + }); +}); diff --git a/tools/docker/architecture/linux-arm/Dockerfile b/tools/docker/architecture/linux-arm/Dockerfile new file mode 100755 index 000000000..ed587a774 --- /dev/null +++ b/tools/docker/architecture/linux-arm/Dockerfile @@ -0,0 +1,81 @@ +#!/bin/echo docker build . -f +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: ISC +# Copyright 2019-present Samsung Electronics Co., Ltd. and other contributors +#{ +# ISC License +# Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") +# Copyright (c) 1995-2003 by Internet Software Consortium +# Permission to use, copy, modify, and /or distribute this software +# for any purpose with or without fee is hereby granted, +# provided that the above copyright notice +# and this permission notice appear in all copies. +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS. +# IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, +# ARISING OUT OF OR IN CONNECTION WITH THE USE +# OR PERFORMANCE OF THIS SOFTWARE. +#} + +FROM resin/rpi-raspbian:stretch +MAINTAINER Philippe Coval (p.coval@samsung.com) + +RUN [ "cross-build-start" ] + +ENV DEBIAN_FRONTEND noninteractive +ENV LC_ALL en_US.UTF-8 +ENV LANG ${LC_ALL} + +RUN echo "#log: Configuring locales" \ + && set -x \ + && apt-get update -y \ + && apt-get install -y locales \ + && echo "${LC_ALL} UTF-8" | tee /etc/locale.gen \ + && locale-gen ${LC_ALL} \ + && dpkg-reconfigure locales \ + && sync + +ENV project node-sqlite3 + +RUN echo "#log: ${project}: Setup system" \ + && set -x \ + && apt-get update -y \ + && apt-get install -y \ + curl \ + sudo \ + build-essential \ + python \ + && apt-get clean \ + && NVM_VERSION="v0.33.8" \ + && NODE_VERSION="--lts=carbon" \ + && curl -o- https://raw.githubusercontent.com/creationix/nvm/${NVM_VERSION}/install.sh | bash \ + && which nvm || . ~/.bashrc \ + && nvm install ${NODE_VERSION} \ + && nvm use ${NODE_VERSION} \ + && sync + +ADD . /usr/local/opt/${project}/src/${project} +WORKDIR /usr/local/opt/${project}/src/${project} +RUN echo "#log: ${project}: Preparing sources" \ + && set -x \ + && which npm || . ~/.bashrc \ + && npm install || cat npm-debug.log \ + && npm install \ + && npm install --unsafe-perm --build-from-source \ + && sync + +WORKDIR /usr/local/opt/${project}/src/${project} +RUN echo "#log: ${project}: Building sources" \ + && set -x \ + && which npm || . ~/.bashrc \ + && npm run pack \ + && npm pack \ + && find ${PWD}/build/stage/ -type f \ + && sync + +RUN [ "cross-build-end" ] diff --git a/tools/docker/architecture/linux-arm/run.sh b/tools/docker/architecture/linux-arm/run.sh new file mode 100755 index 000000000..4f5c88566 --- /dev/null +++ b/tools/docker/architecture/linux-arm/run.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: ISC +# Copyright 2019-present Samsung Electronics Co., Ltd. and other contributors +#{ +# ISC License +# Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") +# Copyright (c) 1995-2003 by Internet Software Consortium +# Permission to use, copy, modify, and /or distribute this software +# for any purpose with or without fee is hereby granted, +# provided that the above copyright notice +# and this permission notice appear in all copies. +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS. +# IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, +# ARISING OUT OF OR IN CONNECTION WITH THE USE +# OR PERFORMANCE OF THIS SOFTWARE. +#} + +set -e +set -x + +this_dir=$(dirname -- "$0") +this_dir=$(realpath "${this_dir}") +this_name=$(basename -- "$0") +top_dir="${this_dir}/../../.." + +module_name="sqlite3" +project="node-${module_name}" +arch="arm" +architecture=$(basename "${this_dir}") +name="${project}-${architecture}" +dir="/usr/local/opt/${project}/" +dist_dir="${dir}/src/${project}/build" +tag=$(git describe --tags || echo v0.0.0) +version=$(echo "${tag}" | cut -dv -f2 | cut -d'-' -f1) + +mkdir -p "${this_dir}/local" "${this_dir}/tmp" +cp -a "/usr/bin/qemu-${arch}-static" "${this_dir}/local" +time docker build -t "${name}" -f "${this_dir}/Dockerfile" . +container=$(docker create "${name}") +mkdir -p "${this_dir}/tmp/${dist_dir}" +rm -rf "${this_dir}/tmp/${dist_dir}" +docker cp "${container}:${dist_dir}" "${this_dir}/tmp/${dist_dir}" +file=$(ls "${this_dir}/tmp/${dist_dir}/stage/${module_name}/"*/*".tar.gz" | head -n1 \ + || echo "/tmp/${USER}/failure.tmp") + +sha256sum "${file}" diff --git a/tools/docker/architecture/linux-arm64/Dockerfile b/tools/docker/architecture/linux-arm64/Dockerfile new file mode 100755 index 000000000..5a19c7311 --- /dev/null +++ b/tools/docker/architecture/linux-arm64/Dockerfile @@ -0,0 +1,80 @@ +#!/bin/echo docker build . -f +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: ISC +# Copyright 2019-present Samsung Electronics Co., Ltd. and other contributors +#{ +# ISC License +# Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") +# Copyright (c) 1995-2003 by Internet Software Consortium +# Permission to use, copy, modify, and /or distribute this software +# for any purpose with or without fee is hereby granted, +# provided that the above copyright notice +# and this permission notice appear in all copies. +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS. +# IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, +# ARISING OUT OF OR IN CONNECTION WITH THE USE +# OR PERFORMANCE OF THIS SOFTWARE. +#} + +FROM arm64v8/node:carbon +ADD tools/docker/architecture/linux-arm64/local/qemu-aarch64-static /usr/bin/qemu-aarch64-static + +MAINTAINER Philippe Coval (p.coval@samsung.com) + +ENV DEBIAN_FRONTEND noninteractive +ENV LC_ALL en_US.UTF-8 +ENV LANG ${LC_ALL} + +RUN echo "#log: Configuring locales" \ + && set -x \ + && apt-get update -y \ + && apt-get install -y locales \ + && echo "${LC_ALL} UTF-8" | tee /etc/locale.gen \ + && locale-gen ${LC_ALL} \ + && dpkg-reconfigure locales \ + && sync + +ENV project node-sqlite3 + +RUN echo "#log: ${project}: Setup system" \ + && set -x \ + && apt-get update -y \ + && apt-get install -y \ + curl \ + sudo \ + build-essential \ + python \ + && apt-get clean \ + && NVM_VERSION="v0.33.8" \ + && NODE_VERSION="--lts=carbon" \ + && curl -o- https://raw.githubusercontent.com/creationix/nvm/${NVM_VERSION}/install.sh | bash \ + && which nvm || . ~/.bashrc \ + && nvm install ${NODE_VERSION} \ + && nvm use ${NODE_VERSION} \ + && sync + +ADD . /usr/local/opt/${project}/src/${project} +WORKDIR /usr/local/opt/${project}/src/${project} +RUN echo "#log: ${project}: Preparing sources" \ + && set -x \ + && which npm || . ~/.bashrc \ + && npm install || cat npm-debug.log \ + && npm install \ + && npm install --unsafe-perm --build-from-source \ + && sync + +WORKDIR /usr/local/opt/${project}/src/${project} +RUN echo "#log: ${project}: Building sources" \ + && set -x \ + && which npm || . ~/.bashrc \ + && npm run pack \ + && npm pack \ + && find ${PWD}/build/stage/ -type f \ + && sync + diff --git a/tools/docker/architecture/linux-arm64/run.sh b/tools/docker/architecture/linux-arm64/run.sh new file mode 100755 index 000000000..6bdda58fd --- /dev/null +++ b/tools/docker/architecture/linux-arm64/run.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: ISC +# Copyright 2019-present Samsung Electronics Co., Ltd. and other contributors +#{ +# ISC License +# Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") +# Copyright (c) 1995-2003 by Internet Software Consortium +# Permission to use, copy, modify, and /or distribute this software +# for any purpose with or without fee is hereby granted, +# provided that the above copyright notice +# and this permission notice appear in all copies. +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS. +# IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, +# ARISING OUT OF OR IN CONNECTION WITH THE USE +# OR PERFORMANCE OF THIS SOFTWARE. +#} + +set -e +set -x + +this_dir=$(dirname -- "$0") +this_dir=$(realpath "${this_dir}") +this_name=$(basename -- "$0") +top_dir="${this_dir}/../../.." + +module_name="sqlite3" +project="node-${module_name}" +arch="aarch64" # AKA: arm64, arm64v8 +architecture=$(basename "${this_dir}") +name="${project}-${architecture}" +dir="/usr/local/opt/${project}/" +dist_dir="${dir}/src/${project}/build" +tag=$(git describe --tags || echo v0.0.0) +version=$(echo "${tag}" | cut -dv -f2 | cut -d'-' -f1) + +mkdir -p "${this_dir}/local" "${this_dir}/tmp" +cp -a "/usr/bin/qemu-${arch}-static" "${this_dir}/local" +time docker build -t "${name}" -f "${this_dir}/Dockerfile" . +container=$(docker create "${name}") +mkdir -p "${this_dir}/tmp/${dist_dir}" +rm -rf "${this_dir}/tmp/${dist_dir}" +docker cp "${container}:${dist_dir}" "${this_dir}/tmp/${dist_dir}" +file=$(ls "${this_dir}/tmp/${dist_dir}/stage/${module_name}/"*/*".tar.gz" | head -n1 \ + || echo "/tmp/${USER}/failure.tmp") + +sha256sum "${file}"