diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..657d4a56 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": "standard", + "rules": { + "semi": ["error", "always"], + "no-unused-vars": "warn", + "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }] + } +} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..e0da2d98 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,36 @@ +## I'm submitting a... + + + - [ ] Bug report + - [ ] Feature request + - [ ] Question + + +## Current behavior + + + +## Expected behavior + + + +## Minimal reproduction of the problem with instructions + +## What is the motivation / use case for changing the behavior? + + + +## Environment + +
+db-migrate version: X.Y.Z
+plugins with versions: X.Y.Z
+db-migrate driver with versions:
+
+Additional information:
+- Node version: XX
+- Platform:
+
+Others:
+
+
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 00000000..d2e1474e
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,23 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 30
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 7
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - feature
+ - Refactoring
+ - RFC
+ - bug
+ - documentation
+ - bounty
+ - pinned
+ - security
+# Label to use when marking an issue as stale
+staleLabel: stale
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs. Thank you
+ for your contributions.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
diff --git a/.gitignore b/.gitignore
index caedbc7f..a9a6cc35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,9 @@ Seeder/
test/db.config.json
database.json
*.sublime-project
-*.sublime-workspace
\ No newline at end of file
+*.sublime-workspace
+archive
+.db-migraterc
+
+# Vim swap files
+.*.sw[a-z]
diff --git a/.jscsrc b/.jscsrc
new file mode 100644
index 00000000..e69de29b
diff --git a/.jshintignore b/.jshintignore
index b512c09d..998bc454 100644
--- a/.jshintignore
+++ b/.jshintignore
@@ -1 +1,2 @@
-node_modules
\ No newline at end of file
+node_modules
+lib/inflection.js
diff --git a/.jshintrc b/.jshintrc
index 4d91c6d0..12884343 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -1,6 +1,14 @@
{
- "es5": true,
"expr": true,
"-W092": false,
- "quotmark": true
+ "quotmark": true,
+ "curly": false,
+ "bitwise": false,
+ "eqeqeq": true,
+ "funcscope": true,
+ "futurehostile": true,
+ "nonew": true,
+ "unused": true,
+ "shadow": true,
+ "loopfunc": true
}
diff --git a/.npmignore b/.npmignore
index 65003c3d..a8319eaa 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,3 +1,4 @@
+.github
test
migrations
VCSeeder
@@ -6,4 +7,6 @@ node_modules
*.db
database.json
*.sublime-project
-*.sublime-workspace
\ No newline at end of file
+*.sublime-workspace
+archive
+.db-migraterc
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..544138be
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,3 @@
+{
+ "singleQuote": true
+}
diff --git a/.tern-project b/.tern-project
new file mode 100644
index 00000000..352ae8be
--- /dev/null
+++ b/.tern-project
@@ -0,0 +1,7 @@
+{
+ "plugins": {
+ "node": {},
+ "lint": {},
+ "node-extension": {}
+ }
+}
diff --git a/.travis.yml b/.travis.yml
index 3d472d24..f88edef4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,34 +1,46 @@
language: node_js
node_js:
- - 0.10
- - 0.12
- - io.js
+ - 4
+ - 6
+ - 8
+ - node
notifications:
email:
recipients:
- magic@wizardtales.com
+ webhooks:
+ urls:
+ - https://webhooks.gitter.im/e/509c88551a18006fe93f
+ on_success: change
+ on_failure: always
+ on_start: false
-services:
- - mongodb
+os:
+ - linux
-env:
- - DB=mysql
- - DB=mariadb
- - DB=postgres92
- - DB=postgres93
+matrix:
+ allow_failures:
+ - node_js: node
+ fast_finish: true
+
+sudo: false
+
+cache:
+ npm: true
+ directories:
+ - node_modules
+
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
+
+before_install:
+ - export CXX=g++-4.8; export CC=gcc-4.8;
before_script:
- - until nc -z localhost 27017 ; do echo Waiting for MongoDB; sleep 1; done
- - sh -c "if [ '$DB' = 'mariadb' ]; then sudo apt-get install python-software-properties; fi"
- - sh -c "if [ '$DB' = 'mariadb' ]; then sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db; fi"
- - sh -c "if [ '$DB' = 'mariadb' ]; then sudo add-apt-repository 'deb http://ftp.osuosl.org/pub/mariadb/repo/10.0/ubuntu precise main'; fi"
- - sh -c "if [ '$DB' = 'mariadb' ]; then sudo apt-get update; fi"
- - sh -c "if [ '$DB' = 'mariadb' ]; then sudo apt-get purge mysql*; fi"
- - sh -c "if [ '$DB' = 'mariadb' ]; then sudo apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install mariadb-server-10.0 -y; fi"
- - sh -c "if [ '$DB' = 'postgres92' ]; then sudo /etc/init.d/postgresql stop && sudo /etc/init.d/postgresql start 9.2; fi"
- - sh -c "if [ '$DB' = 'postgres93' ]; then sudo /etc/init.d/postgresql stop && sudo /etc/init.d/postgresql start 9.3; fi"
- - mysql -e "create database db_migrate_test;"
- - createdb db_migrate_test
- - cp test/db.config.ci test/db.config.json
+ - cp test/db.config.ci test/db.config.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f1c6596..d53609bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,127 @@
+
+## [0.11.1](https://github.com/db-migrate/node-db-migrate/compare/v0.11.0...v0.11.1) (2018-04-10)
+
+
+### Bug Fixes
+
+* **reset:** regression introduced in check functionality ([d8a735d](https://github.com/db-migrate/node-db-migrate/commit/d8a735d)), closes [#552](https://github.com/db-migrate/node-db-migrate/issues/552)
+
+
+
+
+# [0.11.0](https://github.com/db-migrate/node-db-migrate/compare/v0.10.7...v0.11.0) (2018-04-10)
+
+
+### Features
+
+* **check:** add check functionality to determine migrations to run ([93e9f18](https://github.com/db-migrate/node-db-migrate/commit/93e9f18))
+
+
+
+
+## [0.10.7](https://github.com/db-migrate/node-db-migrate/compare/v0.10.6...v0.10.7) (2018-03-27)
+
+
+### Bug Fixes
+
+* **progamableApi:** cmdOptions get passed into setDefaultArgv now ([ebdd75d](https://github.com/db-migrate/node-db-migrate/commit/ebdd75d))
+
+
+
+
+## [0.10.6](https://github.com/db-migrate/node-db-migrate/compare/v0.10.5...v0.10.6) (2018-03-21)
+
+
+### Bug Fixes
+
+* **ci:** add ignores for backported features ([53dedc2](https://github.com/db-migrate/node-db-migrate/commit/53dedc2))
+
+
+### Features
+
+* **contribution:** enrich contribution instructions ([93b5cea](https://github.com/db-migrate/node-db-migrate/commit/93b5cea)), closes [#549](https://github.com/db-migrate/node-db-migrate/issues/549)
+* **contribution:** enrich contribution instructions, issues ([d87a734](https://github.com/db-migrate/node-db-migrate/commit/d87a734))
+* **progamableApi:** CMD options can be passed programatically now ([69d7605](https://github.com/db-migrate/node-db-migrate/commit/69d7605))
+* **progamableApi:** using const now ([a69e221](https://github.com/db-migrate/node-db-migrate/commit/a69e221))
+
+
+
+
+## [0.10.4](https://github.com/db-migrate/node-db-migrate/compare/v0.10.3...v0.10.4) (2018-02-06)
+
+
+### Bug Fixes
+
+* **insert:** add missing insert entry to interface ([899b8bc](https://github.com/db-migrate/node-db-migrate/commit/899b8bc)), closes [#542](https://github.com/db-migrate/node-db-migrate/issues/542)
+
+
+
+
+## [0.10.3](https://github.com/db-migrate/node-db-migrate/compare/v0.10.2...v0.10.3) (2018-02-03)
+
+
+### Bug Fixes
+
+* **db:** wrong reference to connect causes db:create to fail ([991ee76](https://github.com/db-migrate/node-db-migrate/commit/991ee76)), closes [#520](https://github.com/db-migrate/node-db-migrate/issues/520)
+* **exitCode:** wrong check for existence fixed ([3c6fc33](https://github.com/db-migrate/node-db-migrate/commit/3c6fc33))
+* **exitCode:** wrong exit code on db methods ([486cb78](https://github.com/db-migrate/node-db-migrate/commit/486cb78)), closes [#534](https://github.com/db-migrate/node-db-migrate/issues/534)
+* **log:** error ended up in unreadable errors ([16512f6](https://github.com/db-migrate/node-db-migrate/commit/16512f6)), closes [#524](https://github.com/db-migrate/node-db-migrate/issues/524) [#521](https://github.com/db-migrate/node-db-migrate/issues/521)
+* **switchDatabase:** no error was thrown on scope switch ([392d88c](https://github.com/db-migrate/node-db-migrate/commit/392d88c)), closes [#470](https://github.com/db-migrate/node-db-migrate/issues/470)
+
+
+### Features
+
+* **issuetemplate:** added a github issue template ([3c0fcbf](https://github.com/db-migrate/node-db-migrate/commit/3c0fcbf))
+
+
+
+
+## [0.10.2](https://github.com/db-migrate/node-db-migrate/compare/v0.10.1...v0.10.2) (2017-12-01)
+
+
+### Bug Fixes
+
+* **log:** error ended up in unreadable errors ([97de65d](https://github.com/db-migrate/node-db-migrate/commit/97de65d)), closes [#524](https://github.com/db-migrate/node-db-migrate/issues/524) [#521](https://github.com/db-migrate/node-db-migrate/issues/521)
+
+
+
+
+## [0.10.1](https://github.com/db-migrate/node-db-migrate/compare/v0.10.0...v0.10.1) (2017-11-27)
+
+
+### Bug Fixes
+
+* **db:** wrong reference to connect causes db:create to fail ([56cb75a](https://github.com/db-migrate/node-db-migrate/commit/56cb75a)), closes [#520](https://github.com/db-migrate/node-db-migrate/issues/520)
+
+## 0.10.0
+
+Note:
+
+This is a cornerstone release. It provides groundwork for many things to come and has worked
+on stability and flexibility, while mostly retaining backwards compatibility.
+
+This release was a rewrite of nearly the whole module. However backwards compatibility
+was mostly preeserved. Bug fixes wont be listed for this release, all subsequent releases
+will follow the angular standard to automatically generate changelogs.
+
+New Features:
+ - Sync
+ - Driverless Core
+ - Plugin Hooks and overwrites
+ - Adjusted migration schema, to allow specific setup routines
+ - Version migration schemas itself for future iterations
+ - Promise style migrations
+ - Programmatic API
+ - Restructered major parts of db-migrate
+ - Transactional migrations
+ - New configuration options
+
+Find a full list of features added here:
+
+https://github.com/db-migrate/node-db-migrate/issues?utf8=%E2%9C%93&q=milestone%3Av0.10.0
+
+# Old Changelogs
+
## 0.1.0
New Features:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..1eea9489
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dbmigrate@wizardtales.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7b0d6553..026d3ac2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,3 +1,40 @@
+# Contributing by opening an Issue or Discussing
+
+Whenever you find a bug, which you do not have the time to fix, it is still important
+to get your input. Same goes for feature suggestions and everything else that helps
+a project to get better.
+
+Here are a few points to consider before creating an issue or discussing within one:
+
+1. Make sure you have proper description of your problem or what exactly you want:
+
+We have issue templates in place that should help you with this one and this is really
+important, since we may not just agree with you, if we don't understand your use case.
+
+2. Be constructive and before you post think once more if this is going to waste the maintainers
+ time, or if it is going to actually help them.
+
+Since being constructive, is a big vague, here some simple points that should help you:
+
+2.1. Just stating something is wrong, is not constructive.
+
+When you've got the feeling something is wrong, try to understand first if it is really wrong
+and if it is make suggestions on how to improve, or even better open a PR and make the open source
+world once again even better!
+
+2.2. "Does not work", is not a proper problem description
+
+Neither is, "see title". When you follow the issue template and provide all informations asked for
+you should be pretty fine, in providing us exactly the information we need to help you.
+
+2.3. Follow the CoC and contribute, not attack
+
+An example when you're not contributing, but attacking is, should you just impose you're right
+and you should be followed. It is always about the dialogue, that makes the difference. After all
+you should be fine, by just using your common sense.
+
+# Contributing a Pull Request
+
Without pull requests from generous people like you, this project
wouldn't be what it is today. You are planning to contribute aren't you?
If so, here's a quick guide to creating a great pull request for
@@ -8,16 +45,21 @@ this project:
2. Copy the test/db.config.example.json to test/db.config.json
3. Run the tests. Pull requests without tests are much less likely to be
-merged, and it's great to know you're starting with a clean slate: `npm test`.
-Be sure to check the README for setting up your test databases first.
+ merged, and it's great to know you're starting with a clean slate: `npm test`.
+ Be sure to check the README for setting up your test databases first.
4. Add a test for your change. Refactoring and documentation changes
-require no new tests. If you are adding functionality or fixing a bug,
-please include a test.
+ require no new tests. If you are adding functionality or fixing a bug,
+ please include a test.
5. Make the test pass.
-6. Push to your fork and submit a pull request.
+6. Create a commit that complies with our conventions, which you can view
+ [here](https://github.com/conventional-changelog/conventional-changelog/tree/35e60b5be6027fb2784c5103eee111f6f99b045e/packages/conventional-changelog-angular)
+ and last but not least also comply to the [DCO](https://github.com/probot/dco#how-it-works).
+ Which easiest work with just commiting via `git commit -s -m 'your commit message'`.
+
+7. Push to your fork and submit a pull request.
At this point you're waiting on me. In a perfect world I'd commit to
responding to your request in some short period of time. Unfortunately,
diff --git a/LICENSE b/LICENSE
index db39a866..93ccf176 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,5 @@
+Copyright (c) 2015 Tobias Gurtzick
+
Copyright (c) 2013 Jeff Kunkle
Permission is hereby granted, free of charge, to any person obtaining
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..55155416
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+test:
+ @node node_modules/lab/bin/lab -I verbose,dryRun,SharedArrayBuffer,Atomics --coverage-exclude lib/interface --coverage-exclude lib/transitions
+test-cov:
+ @node node_modules/lab/bin/lab -t 66 -I verbose,dryRun,SharedArrayBuffer,Atomics --coverage-exclude lib/interface --coverage-exclude lib/transitions
+test-cov-html:
+ @node node_modules/lab/bin/lab -r html -o coverage.html -I verbose,dryRun,SharedArrayBuffer,Atomics --coverage-exclude lib/interface --coverage-exclude lib/transitions
+
+.PHONY: test test-cov test-cov-html
diff --git a/README.md b/README.md
index 126ab9cb..49de3025 100644
--- a/README.md
+++ b/README.md
@@ -29,764 +29,30 @@ DB-Migrate is now available to you via:
$ node node_modules/db-migrate/bin/db-migrate
-## Supported Databases
+## Officially Supported Databases
* Mysql (https://github.com/felixge/node-mysql)
+https://github.com/db-migrate/mysql
* PostgreSQL (https://github.com/brianc/node-postgres)
+https://github.com/db-migrate/pg
* sqlite3 (https://github.com/developmentseed/node-sqlite3)
+https://github.com/db-migrate/sqlite
* Mongodb (https://github.com/mongodb/node-mongodb-native)
+https://github.com/db-migrate/mongodb
-## Usage
+## Resources and usage instructions
-```
-Usage: db-migrate [up|down|reset|create|db] [[dbname/]migrationName|all] [options]
+Please follow the link below, for usage instructions examples and the full
+documentation of db-migrate.
-Down migrations are run in reverse run order, so migrationName is ignored for down migrations.
-Use the --count option to control how many down migrations are run (default is 1).
-
-Options:
- --env, -e The environment to run the migrations under. [default: "dev"]
- --migrations-dir, -m The directory containing your migration files. [default: "./migrations"]
- --count, -c Max number of migrations to run.
- --dry-run Prints the SQL but doesn't run it. [boolean]
- --verbose, -v Verbose mode. [default: false]
- --config Location of the database.json file. [default: "./database.json"]
- --force-exit Call system.exit() after migration run [default: false]
- --sql-file Create sql files for up and down. [default: false]
- --coffee-file Create a coffeescript migration file [default: false]
- --migration-table Set the name of the migration table.
- --table, --migration-table [default: "migrations"]
-```
-
-## Creating Migrations
-
-To create a migration, execute `db-migrate create` with a title. `node-db-migrate` will create a node module within `./migrations/` which contains the following two exports:
-
-```javascript
-exports.up = function (db, callback) {
- callback();
-};
-
-exports.down = function (db, callback) {
- callback();
-};
-```
-
-All you have to do is populate these, invoking `callback()` when complete, and you are ready to migrate!
-
-For example:
-
- $ db-migrate create add-pets
- $ db-migrate create add-owners
-
-The first call creates `./migrations/20111219120000-add-pets.js`, which we can populate:
-
-```javascript
-exports.up = function (db, callback) {
- db.createTable('pets', {
- id: { type: 'int', primaryKey: true },
- name: 'string'
- }, callback);
-};
-
-exports.down = function (db, callback) {
- db.dropTable('pets', callback);
-};
-```
-
-The second creates `./migrations/20111219120005-add-owners.js`, which we can populate:
-
-```javascript
-exports.up = function (db, callback) {
- db.createTable('owners', {
- id: { type: 'int', primaryKey: true },
- name: 'string'
- }, callback);
-};
-
-exports.down = function (db, callback) {
- db.dropTable('owners', callback);
-};
-```
-
-Executing multiple statements against the database within a single migration requires a bit more care. You can either nest the migrations like:
-
-```javascript
-exports.up = function (db, callback) {
- db.createTable('pets', {
- id: { type: 'int', primaryKey: true },
- name: 'string'
- }, createOwners);
-
- function createOwners(err) {
- if (err) { callback(err); return; }
- db.createTable('owners', {
- id: { type: 'int', primaryKey: true },
- name: 'string'
- }, callback);
- }
-};
-
-exports.down = function (db, callback) {
- db.dropTable('pets', function(err) {
- if (err) { callback(err); return; }
- db.dropTable('owners', callback);
- });
-};
-```
-
-or use the async library to simplify things a bit, such as:
-
-```javascript
-var async = require('async');
-
-exports.up = function (db, callback) {
- async.series([
- db.createTable.bind(db, 'pets', {
- id: { type: 'int', primaryKey: true },
- name: 'string'
- }),
- db.createTable.bind(db, 'owners', {
- id: { type: 'int', primaryKey: true },
- name: 'string'
- });
- ], callback);
-};
-
-exports.down = function (db, callback) {
- async.series([
- db.dropTable.bind(db, 'pets'),
- db.dropTable.bind(db, 'owners')
- ], callback);
-};
-```
-
-### Using files for sqls
-
-If you prefer to use sql files for your up and down statements, you can use the `--sql-file` option to automatically generate these files and the javascript code that load them.
-
-For example:
-
- $ db-migrate create add-people --sql-file
-
-This call creates 3 files:
-
-```
-./migrations/20111219120000-add-people.js
-./migrations/sqls/20111219120000-add-people-up.sql
-./migrations/sqls/20111219120000-add-people-down.sql
-```
-
-The sql files will have the following content:
-```sql
-/* Replace with your SQL commands */
-```
-
-And the javascript file with the following code that load these sql files:
-
-```javascript
-dbm = dbm || require('db-migrate');
-var type = dbm.dataType;
-var fs = require('fs');
-var path = require('path');
-
-exports.up = function(db, callback) {
- var filePath = path.join(__dirname + '/sqls/20111219120000-add-people-up.sql');
- fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
- if (err) return console.log(err);
- db.runSql(data, function(err) {
- if (err) return console.log(err);
- callback();
- });
- });
-};
-
-exports.down = function(db, callback) {
- var filePath = path.join(__dirname + '/sqls/20111219120000-add-people-down.sql');
- fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
- if (err) return console.log(err);
- db.runSql(data, function(err) {
- if (err) return console.log(err);
- callback();
- });
- });
-};
-```
-
-** Making it as default **
-
-To not need to always specify the `sql-file` option in your `db-migrate create` commands, you can set a property in your `database.json` as follows:
-
-```
-{
- "dev": {
- "host": "localhost",
- ...
- },
- "sql-file" : true
-}
-```
-
-** Important - For MySQL users **
-
-If you use MySQL, to be able to use multiple statements in your sql file, you have to set the property `multiple-statements: true` when creating the connection object. You can set it in your `database.json` as follows:
-
-```
-{
- "dev": {
- "host": "localhost",
- "user": { "ENV" : "DB_USER" },
- "password" : { "ENV" : "DB_PASS" },
- "database": "database-name",
- "driver": "mysql",
- "multipleStatements": true
- }
-}
-```
-
-## Running Migrations
-
-When first running the migrations, all will be executed in sequence. A table named `migrations` will also be created in your database to track which migrations have been applied.
-
- $ db-migrate up
- [INFO] Processed migration 20111219120000-add-pets
- [INFO] Processed migration 20111219120005-add-owners
- [INFO] Done
-
-Subsequent attempts to run these migrations will result in the following output
-
- $ db-migrate up
- [INFO] No migrations to run
- [INFO] Done
-
-If we were to create another migration using `db-migrate create`, and then execute migrations again, we would execute only those not previously executed:
-
- $ db-migrate up
- [INFO] Processed migration 20111220120210-add-kennels
- [INFO] Done
-
-You can also run migrations incrementally by specifying a date substring. The example below will run all migrations created on or before December 19, 2011:
-
- $ db-migrate up 20111219
- [INFO] Processed migration 20111219120000-add-pets
- [INFO] Processed migration 20111219120005-add-owners
- [INFO] Done
-
-You can also run a specific number of migrations with the -c option:
-
- $ db-migrate up -c 1
- [INFO] Processed migration 20111219120000-add-pets
- [INFO] Done
-
-All of the down migrations work identically to the up migrations by substituting the word `down` for `up`.
-
-## Configuration
-
-db-migrate supports the concept of environments. For example, you might have a dev, test, and prod environment where you need to run the migrations at different times. Environment settings are loaded from a database.json file like the one shown below:
-
-```javascript
-{
- "dev": {
- "driver": "sqlite3",
- "filename": "~/dev.db"
- },
-
- "test": {
- "driver": "sqlite3",
- "filename": ":memory:"
- },
-
- "prod": {
- "driver": "mysql",
- "user": "root",
- "password": "root"
- },
-
- "pg": {
- "driver": "pg",
- "user": "test",
- "password": "test",
- "host": "localhost",
- "database": "mydb",
- "schema": "my_schema"
- },
-
- "mongo": {
- "driver": "mongodb",
- "database": "my_db",
- "host": "localhost"
- },
-
- "other": "postgres://uname:pw@server.com/dbname"
-}
-```
-
-You can also specify environment variables in your config file by using a special notation. Here is an example:
-```javascript
-{
- "prod": {
- "driver": "mysql",
- "user": {"ENV": "PRODUCTION_USERNAME"},
- "password": {"ENV": "PRODUCTION_PASSWORD"}
- },
-}
-```
-In this case, db-migrate will search your environment for variables
-called `PRODUCTION_USERNAME` and `PRODUCTION_PASSWORD`, and use those values for the corresponding configuration entry.
-
-Note that if the settings for an environment are represented by a single string that string will be parsed as a database URL. You can also provide a database URL through environmental variable like this:
-```javascript
-{
- "prod": {"ENV": "PRODUCTION_URL"}
-}
-```
-
-You can pass the -e or --env option to db-migrate to select the environment you want to run migrations against. The --config option can be used to specify the path to your database.json file if it's not in the current working directory.
-
- db-migrate up --config config/database.json -e prod
-
-The above will run all migrations that haven't yet been run in the prod environment, grabbing the settings from config/database.json.
-
-Alternatively, you can specify a DATABASE_URL
-environment variable that will be used in place of the configuration
-file settings. This is helpful for use with Heroku.
-
-## Multiple migration scopes
-
-You can have multiple migration scopes, which are subfolders within your migrations folder. A scope gets called like the following:
-
- $ db-migrate up:myScope
-
-#### Executing all scopes together
-
-If you want to execute all scopes with one command, you can execute the following:
-
- $ db-migrate up:all
-
-Obviously this means you **CAN'T** create scope which is named all.
-
-#### Scope Configuration
-
-You can also configure the scope to specify a sub configuration. Currently you can only define database and schema within this config.
-
-This config file is used to tell db-migrate to switch to the `database` or
-`schema`. Databases is used for most databases, except **postgres**
-which needs the schema variable.
-
-It's currently also not possible to switch the database over this config with **postgres**.
-
-```json
-{
- "database": "test",
- "schema": "test"
-}
-```
-## Defaults
-
-## Generic Datatypes
-
-There is currently a small list of generic Datatypes you can use, to make your
-migrations more database independent.
-
-Find the list of supported types [here](https://github.com/kunklejr/node-db-migrate/blob/master/lib/data_type.js).
-
-## Migrations API - SQL
-
-Below are examples of all the different migrations supported by db-migrate. Please note that not all migrations are supported by all databases. For example, SQLite does not support dropping columns.
-
-### createTable(tableName, columnSpec, callback)
-
-Creates a new table with the specified columns.
-
-__Arguments__
-
-* tableName - the name of the table to create
-* columnSpec - a hash of column definitions
-* callback(err) - callback that will be invoked after table creation
-
-__Examples__
-
-```javascript
-// with no table options
-exports.up = function (db, callback) {
- db.createTable('pets', {
- id: { type: 'int', primaryKey: true, autoIncrement: true },
- name: 'string' // shorthand notation
- }, callback);
-}
-
-// with table options
-exports.up = function (db, callback) {
- db.createTable('pets', {
- columns: {
- id: { type: 'int', primaryKey: true, autoIncrement: true },
- name: 'string' // shorthand notation
- },
- ifNotExists: true
- }, callback);
-}
-```
-
-__Column Specs__
-
-The following options are available on column specs
-
-* type - the column data type. Supported types can be found in lib/data_type.js
-* length - the column data length, where supported
-* primaryKey - true to set the column as a primary key. Compound primary keys are supported by setting the `primaryKey` option to true on multiple columns
-* autoIncrement - true to mark the column as auto incrementing
-* notNull - true to mark the column as non-nullable, omit it archive database default behavior and false to mark explicitly as nullable
-* unique - true to add unique constraint to the column
-* defaultValue - set the column default value
-* foreignKey - set a foreign key to the column
-
-__Column ForeignKey Spec Examples__
-
-```javascript
-exports.up = function(db, callback) {
-
- //automatic mapping, the mapping key resolves to the column
- db.createTable( 'product_variant',
- {
- id:
- {
- type: 'int',
- unsigned: true,
- notNull: true,
- primaryKey: true,
- autoIncrement: true,
- length: 10
- },
- product_id:
- {
- type: 'int',
- unsigned: true,
- length: 10,
- notNull: true,
- foreignKey: {
- name: 'product_variant_product_id_fk',
- table: 'product',
- rules: {
- onDelete: 'CASCADE',
- onUpdate: 'RESTRICT'
- },
- mapping: 'id'
- }
- },
- }, callback );
-};
-
-exports.up = function(db, callback) {
-
- //explicit mapping
- db.createTable( 'product_variant',
- {
- id:
- {
- type: 'int',
- unsigned: true,
- notNull: true,
- primaryKey: true,
- autoIncrement: true,
- length: 10
- },
- product_id:
- {
- type: 'int',
- unsigned: true,
- length: 10,
- notNull: true,
- foreignKey: {
- name: 'product_variant_product_id_fk',
- table: 'product',
- rules: {
- onDelete: 'CASCADE',
- onUpdate: 'RESTRICT'
- },
- mapping: {
- product_id: 'id'
- }
- }
- },
- }, callback );
-};
-```
-
-### dropTable(tableName, [options,] callback)
-
-Drop a database table
-
-__Arguments__
-
-* tableName - name of the table to drop
-* options - table options
-* callback(err) - callback that will be invoked after dropping the table
-
-__Table Options__
-
-* ifExists - Only drop the table if it already exists
-
-### renameTable(tableName, newTableName, callback)
-
-Rename a database table
-
-__Arguments__
-
-* tableName - existing table name
-* options - new table name
-* callback(err) - callback that will be invoked after renaming the table
-
-### addColumn(tableName, columnName, columnSpec, callback)
-
-Add a column to a database table
-
-__Arguments__
-
-* tableName - name of table to add a column to
-* columnName - name of the column to add
-* columnSpec - a hash of column definitions
-* callback(err) - callback that will be invoked after adding the column
-
-Column spec is the same as that described in createTable
-
-### removeColumn(tableName, columnName, callback)
-
-Remove a column from an existing database table
-
-* tableName - name of table to remove a column from
-* columnName - name of the column to remove
-* callback(err) - callback that will be invoked after removing the column
-
-### renameColumn(tableName, oldColumnName, newColumnName, callback)
-
-Rename a column
-
-__Arguments__
-
-* tableName - table containing column to rename
-* oldColumnName - existing column name
-* newColumnName - new name of the column
-* callback(err) - callback that will be invoked after renaming the column
-
-### changeColumn(tableName, columnName, columnSpec, callback)
-
-Change the definition of a column
-
-__Arguments__
-
-* tableName - table containing column to change
-* columnName - existing column name
-* columnSpec - a hash containing the column spec
-* callback(err) - callback that will be invoked after changing the column
-
-### addIndex(tableName, indexName, columns, [unique], callback)
-
-Add an index
-
-__Arguments__
-
-* tableName - table to add the index too
-* indexName - the name of the index
-* columns - an array of column names contained in the index
-* unique - whether the index is unique (optional, default false)
-* callback(err) - callback that will be invoked after adding the index
-
-### addForeignKey
-
-Adds a foreign Key
-
-__Arguments__
-
-* tableName - table on which the foreign key gets applied
-* referencedTableName - table where the referenced key is located
-* keyName - name of the foreign key
-* fieldMapping - mapping of the foreign key to referenced key
-* rules - ondelete, onupdate constraints
-* callback(err) - callback that will be invoked after adding the foreign key
-
-__Example__
-
-```javascript
-exports.up = function (db, callback)
-{
- db.addForeignKey('module_user', 'modules', 'module_user_module_id_foreign',
- {
- 'module_id': 'id'
- },
- {
- onDelete: 'CASCADE',
- onUpdate: 'RESTRICT'
- }, callback);
-};
-```
-
-### removeForeignKey
-
-__Arguments__
-
-* tableName - table in which the foreign key should be deleted
-* keyName - the name of the foreign key
-* options - object of options, see below
-* callback - callback that will be invoked once the foreign key was deleted
-
-__Options__
-
-* dropIndex (default: false) - deletes the index with the same name as the foreign key
-
-__Examples__
-
-```javascript
-//without options object
-exports.down = function (db, callback)
-{
- db.removeForeignKey('module_user', 'module_user_module_id_foreign', callback);
-};
-
-//with options object
-exports.down = function (db, callback)
-{
- db.removeForeignKey('module_user', 'module_user_module_id_foreign',
- {
- dropIndex: true,
- }, callback);
-};
-```
-
-### insert(tableName, columnNameArray, valueArray, callback)
-
-Insert an item into a given column
-
-__Arguments__
-
-* tableName - table to insert the item into
-* columnNameArray - the array existing column names for each item being inserted
-* valueArray - the array of values to be inserted into the associated column
-* callback(err) - callback that will be invoked once the insert has been completed.
-
-### removeIndex([tableName], indexName, callback)
-
-Remove an index
-
-__Arguments__
-
-* tableName - name of the table that has the index (Required for mySql)
-* indexName - the name of the index
-* callback(err) - callback that will be invoked after removing the index
-
-### runSql(sql, [params,] callback)
-
-Run arbitrary SQL
-
-__Arguments__
-
-* sql - the SQL query string, possibly with ? replacement parameters
-* params - zero or more ? replacement parameters
-* callback(err) - callback that will be invoked after executing the SQL
-
-### all(sql, [params,] callback)
-
-Execute a select statement
-
-__Arguments__
-
-* sql - the SQL query string, possibly with ? replacement parameters
-* params - zero or more ? replacement parameters
-* callback(err, results) - callback that will be invoked after executing the SQL
-
-## Migrations API - NoSQL
-
-Below are examples of all the different migrations supported by db-migrate for NoSQL databases.
-
-### createCollection(collectionName, callback)
-
-Creates a new collection.
-
-__Arguments__
-
-* collectionName - the name of the collection to create
-* callback(err) - callback that will be invoked after table creation
-
-__Examples__
-
-```javascript
-exports.up = function (db, callback) {
- db.createCollection('pets', callback);
-}
-```
-
-### dropCollection(collectionName, callback)
-
-Drop a database collection
-
-__Arguments__
-
-* collectionName - name of the collection to drop
-* callback(err) - callback that will be invoked after dropping the collection
-
-### renameCollection(collectionName, newCollectionName, callback)
-
-Rename a database table
-
-__Arguments__
-
-* collectionName - existing collection name
-* newCollectionName - new collection name
-* callback(err) - callback that will be invoked after renaming the collection
-
-### addIndex(collectionName, indexName, columns, unique, callback)
-
-Add an index
-
-__Arguments__
-
-* collectionName - collection to add the index too
-* indexName - the name of the index
-* columns - an array of column names contained in the index
-* unique - whether the index is unique
-* callback(err) - callback that will be invoked after adding the index
-
-### removeIndex(collectionName, indexName, callback)
-
-Remove an index
-
-__Arguments__
-
-* collectionName - name of the collection that has the index
-* indexName - the name of the index
-* callback(err) - callback that will be invoked after removing the index
-
-### insert(collectionName, toInsert, callback)
-
-Insert an item into a given collection
-
-__Arguments__
-
-* collectionName - collection to insert the item into
-* toInsert - an object or array of objects to be inserted into the associated collection
-* callback(err) - callback that will be invoked once the insert has been completed.
-
-## Development
-
-The following command runs the vows tests.
-
-```bash
-npm test
-```
-
-Running the tests requires a one-time setup of the **MySQL**, **MongoDB** and **Postgres** databases.
-
-```bash
-mysql -u root -e "CREATE DATABASE db_migrate_test;"
-createdb db_migrate_test
-```
-
-You will also need to copy `test/db.config.example.json` to `test/db.config.json`
-and adjust appropriate to setup configuration for your database instances.
+Documentation: https://db-migrate.readthedocs.io/en/latest/
## License
(The MIT License)
+Copyright (c) 2015 Tobias Gurtzick
+
Copyright (c) 2013 Jeff Kunkle
Permission is hereby granted, free of charge, to any person obtaining
diff --git a/api.js b/api.js
index 03109611..ccf3e973 100644
--- a/api.js
+++ b/api.js
@@ -1,101 +1,154 @@
-var assert = require('assert');
-var fs = require('fs');
-var path = require('path');
-var util = require('util');
-var mkdirp = require('mkdirp');
-var optimist = require('optimist');
-var config = require('./lib/config.js');
-var index = require('./index');
-var Migration = require('./lib/migration.js');
-var Seeder = require('./lib/seeder.js');
-var Migrator = require('./lib/migrator.js');
-var log = require('./lib/log');
-var pkginfo = require('pkginfo')(module, 'version');
-var dotenv = require('dotenv');
-
-//global declaration for detection like it's done in umigrate
-dbm = require( './' ); //deprecated
-async = require( 'async' ); //deprecated
-
-
-var internals = {};
-
-function dbmigrate(isModule, callback) {
-
- if(typeof(callback) === 'function')
- internals.onComplete = callback;
-
- dotenv.load();
- registerEvents();
-
- if(typeof(isModule) === 'function')
- {
- internals.onComplete = isModule;
- setDefaultArgv();
+'use strict';
+
+var load = require('./lib/commands');
+var log = require('db-migrate-shared').log;
+require('pkginfo')(module, 'version'); // jshint ignore:line
+var Promise;
+var onComplete = load('on-complete');
+
+// constant hooks for this file
+var APIHooks = {
+ 'init:api:addfunction:hook': function (name, fn) {
+ this[name] = fn;
+ },
+ 'init:api:accessapi:hook': function (cb) {
+ return this;
}
- else
- setDefaultArgv(isModule);
-
- loadConfig();
- index.exportInternals(internals);
- internals.dbm = dbm;
- global.dbm = dbm; //deprecated
- internals.migrationOptions = { dbmigrate: internals.dbm };
-}
+};
+
+function dbmigrate (plugins, isModule, options, callback) {
+ var dotenv = require('dotenv');
+ var setDefaultArgv = load('set-default-argv');
+
+ this.internals = {
+ onComplete: onComplete,
+ migrationProtocol: 1
+ };
+ if (typeof isModule !== 'function') {
+ this.internals.isModule = isModule;
+ }
+ var internals = this.internals;
+ this.internals.plugins = load('fn/plugin')(plugins);
-function registerEvents() {
+ if (typeof callback === 'function') this.internals.onComplete = callback;
+ else if (typeof options === 'function') this.internals.onComplete = options;
- process.on('uncaughtException', function(err) {
- log.error(err.stack);
- process.exit(1);
+ this.internals.dbm = require('./');
+ this.dataType = this.internals.dbm.dataType;
+ this.version = this.internals.dbm.version;
+ dotenv.load({
+ silent: true
});
+
+ /* $lab:coverage:off$ */
+ if (!options || !options.throwUncatched) load('helper/register-events')();
+ /* $lab:coverage:on$ */
+
+ if (typeof options === 'object') {
+ if (typeof options.config === 'string') {
+ internals.configFile = options.config;
+ } else if (typeof options.config === 'object') {
+ internals.configObject = options.config;
+ }
+
+ if (typeof options.env === 'string') internals.currentEnv = options.env;
+
+ if (typeof options.cwd === 'string') internals.cwd = options.cwd;
+ else internals.cwd = process.cwd();
+
+ if (typeof options.cmdOptions === 'object') internals.cmdOptions = options.cmdOptions;
+ } else internals.cwd = process.cwd();
+
+ if (typeof isModule === 'function') {
+ this.internals.onComplete = isModule;
+ setDefaultArgv(this.internals);
+ } else setDefaultArgv(this.internals, isModule);
+
+ this.config = load('helper/load-config')(
+ require('./lib/config.js'),
+ this.internals
+ );
+
+ // delayed loading of bluebird
+ Promise = require('bluebird');
+ this.internals.migrationOptions = {
+ dbmigrate: this.internals.dbm,
+ ignoreOnInit: this.internals.argv['ignore-on-init'],
+ Promise: Promise
+ };
+ this.internals.seederOptions = {
+ dbmigrate: this.internals.dbm,
+ Promise: Promise
+ };
}
dbmigrate.prototype = {
-
/**
- * Add a global defined variable to db-migrate, to enable access from
- * local migrations without configuring pathes.
- *
- * @return boolean
- */
- addGlobal: function(library) {
-
+ * Add a global defined variable to db-migrate, to enable access from
+ * local migrations without configuring pathes.
+ *
+ * @return boolean
+ */
+ addGlobal: function (library) {
try {
require(library);
- } catch(e) {
+ } catch (e) {
return false;
}
return true;
},
- _internals: internals,
-
/**
- * Add a configuration option to dbmigrate.
+ * Registers and initializes hooks.
*
- * @return boolean
+ * @returns Promise
*/
- addConfiguration: function(description, args, type) {
+ registerAPIHook: function (callback) {
+ var plugins = this.internals.plugins;
+ var self = this;
+
+ return Promise.resolve(Object.keys(APIHooks))
+ .each(function (hook) {
+ var plugin = plugins.hook(hook);
+ if (!plugin) return;
+
+ var APIHook = APIHooks[hook].bind(self);
+
+ return Promise.resolve(plugin)
+ .map(function (plugin) {
+ return plugin[hook]();
+ })
+ .each(function (args) {
+ return APIHook.apply(self, args);
+ });
+ })
+ .asCallback(callback);
+ },
- var name = args.shift();
- internals.argv.describe(name, description);
+ _internals: this.internals,
- for(var i = 0; i < args.length; ++i) {
+ /**
+ * Add a configuration option to dbmigrate.
+ *
+ * @return boolean
+ */
+ addConfiguration: function (description, args, type) {
+ var name = args.shift();
+ this.internals.argv.describe(name, description);
- internals.argv.alias(args[i], name);
+ for (var i = 0; i < args.length; ++i) {
+ this.internals.argv.alias(args[i], name);
}
- switch(type) {
-
+ switch (type) {
case 'string':
- internals.argv.string(name);
+ this.internals.argv.string(name);
break;
case 'boolean':
- internals.argv.boolean(name);
+ this.internals.argv.boolean(name);
break;
default:
@@ -105,549 +158,313 @@ dbmigrate.prototype = {
return true;
},
-
/**
- * Resets and sets argv to a specified new argv.
- */
- resetConfiguration: function(argv) {
- internals.argv = argv;
+ * Resets and sets argv to a specified new argv.
+ */
+ resetConfiguration: function (argv) {
+ this.internals.argv = argv;
},
/**
- * Executes up a given number of migrations or a specific one.
- *
- * Defaults to up all migrations if no count is given.
- */
- up: function(specification, scope) {
-
- if(arguments.length > 0)
- {
- if(typeof(specification) === 'string') {
-
- internals.argv.destination = specification;
- }
- else if(typeof(specification) === 'number') {
-
- internals.argv.count = specification;
+ * Executes up a given number of migrations or a specific one.
+ *
+ * Defaults to up all migrations if no count is given.
+ */
+ up: function (specification, opts, callback) {
+ var executeUp = load('up');
+
+ if (arguments.length > 0) {
+ if (typeof specification === 'string') {
+ this.internals.argv.destination = specification;
+ } else if (typeof specification === 'number') {
+ this.internals.argv.count = specification;
+ } else if (typeof specification === 'function') {
+ callback = specification;
}
- if(scope) {
-
- internals.migrationMode = scope;
+ if (typeof opts === 'string') {
+ this.internals.migrationMode = opts;
+ this.internals.matching = opts;
+ } else if (typeof opts === 'function') {
+ callback = opts;
}
}
- executeUp();
+ return Promise.fromCallback(
+ function (callback) {
+ executeUp(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
/**
- * Executes up a given number of migrations or a specific one.
- *
- * Defaults to up all migrations if no count is given.
- */
- down: function(specification, scope) {
-
- if(arguments.length > 0)
- {
- if(typeof(arguments[0]) === 'number') {
-
- internals.argv.count = arguments[0];
+ * Executes up a given number of migrations or a specific one.
+ *
+ * Defaults to up all migrations if no count is given.
+ */
+ down: function (specification, opts, callback) {
+ var executeDown = load('down');
+
+ if (arguments.length > 0) {
+ if (typeof specification === 'number') {
+ this.internals.argv.count = arguments[0];
+ } else if (typeof specification === 'function') {
+ callback = specification;
}
- if(scope) {
-
- internals.migrationMode = scope;
+ if (typeof opts === 'string') {
+ this.internals.migrationMode = opts;
+ this.internals.matching = opts;
+ } else if (typeof opts === 'function') {
+ callback = opts;
}
}
- executeDown();
+ return Promise.fromCallback(
+ function (callback) {
+ executeDown(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
- /**
- * Executes down for all currently migrated migrations.
- */
- reset: function(scope) {
+ check: function (specification, opts, callback) {
+ var executeCheck = load('check');
- if(scope) {
+ if (arguments.length > 0) {
+ if (typeof specification === 'number') {
+ this.internals.argv.count = arguments[0];
+ } else if (typeof specification === 'function') {
+ callback = specification;
+ }
- internals.migrationMode = scope;
+ if (typeof opts === 'string') {
+ this.internals.migrationMode = opts;
+ this.internals.matching = opts;
+ } else if (typeof opts === 'function') {
+ callback = opts;
+ }
}
- internals.argv.count = Number.MAX_VALUE;
- executeDown();
+ return Promise.fromCallback(
+ function (callback) {
+ executeCheck(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
/**
- * Creates a correctly formatted migration
- */
- create: function(migrationName, scope) {
-
- if(scope) {
+ * Executes up a given number of migrations or a specific one.
+ *
+ * Defaults to up all migrations if no count is given.
+ */
+ sync: function (specification, opts, callback) {
+ var executeSync = load('sync');
+
+ if (arguments.length > 0) {
+ if (typeof specification === 'string') {
+ this.internals.argv.destination = specification;
+ }
- internals.migrationMode = scope;
+ if (typeof opts === 'string') {
+ this.internals.migrationMode = opts;
+ this.internals.matching = opts;
+ } else if (typeof opts === 'function') {
+ callback = opts;
+ }
}
- internals.argv._.push(migrationName);
- executeCreate();
+ return Promise.fromCallback(
+ function (callback) {
+ executeSync(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
/**
- * Creates a database of the given dbname.
- */
- createDatabase: function(dbname) {
+ * Executes down for all currently migrated migrations.
+ */
+ reset: function (scope, callback) {
+ var executeDown = load('down');
+
+ if (typeof scope === 'string') {
+ this.internals.migrationMode = scope;
+ this.internals.matching = scope;
+ } else if (typeof scope === 'function') {
+ callback = scope;
+ }
- internals.argv._.push(dbname);
- internals.mode = 'create';
+ this.internals.argv.count = Number.MAX_VALUE;
+ return Promise.fromCallback(
+ function (callback) {
+ executeDown(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
/**
- * Drops a database of the given dbname.
- */
- dropDatabase: function(dbname) {
-
- internals.argv._.push(dbname);
- internals.mode = 'drop';
+ * Silence the log output completely.
+ */
+ silence: function (isSilent) {
+ return log.silence(isSilent);
},
/**
- * Sets a config variable to the given value.
- *
- * @return value
+ * Transition migrations to the latest defined protocol.
*/
- setConfigParam: function(param, value) {
-
- return (argv[param] = value);
+ transition: function () {
+ load('transition')(this.internals);
},
-
/**
- * Sets the callback to the default onComplete
- */
- setDefaultCallback: function() {
+ * Creates a correctly formatted migration
+ */
+ create: function (migrationName, scope, callback) {
+ var executeCreateMigration = load('create-migration');
+ if (typeof scope === 'function') {
+ callback = scope;
+ } else if (scope) {
+ this.internals.migrationMode = scope;
+ this.internals.matching = scope;
+ }
- internals.onComplete = onComplete;
+ this.internals.argv._.push(migrationName);
+ return Promise.fromCallback(
+ function (callback) {
+ executeCreateMigration(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
/**
- * Let's the user customize the callback, which gets called after all
- * migrations have been done.
- */
- setCustomCallback: function(callback) {
-
- internals.onComplete = callback;
+ * Creates a database of the given dbname.
+ */
+ createDatabase: function (dbname, callback) {
+ var executeDB = load('db');
+ this.internals.argv._.push(dbname);
+ this.internals.mode = 'create';
+ return Promise.fromCallback(
+ function (callback) {
+ executeDB(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
/**
- * Seeds either the static or version controlled seeders, controlled by
- * the passed mode.
- */
- seed: function(mode, scope) {
-
- if(scope) {
-
- internals.migrationMode = scope;
- }
-
- internals.mode = mode || 'vc';
- executeSeed();
+ * Drops a database of the given dbname.
+ */
+ dropDatabase: function (dbname, callback) {
+ var executeDB = load('db');
+ this.internals.argv._.push(dbname);
+ this.internals.mode = 'drop';
+ return Promise.fromCallback(
+ function (callback) {
+ executeDB(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
},
/**
- * Executes the default routine.
- */
- run: function() {
-
- run();
-
- if (internals.argv['force-exit']) {
- log.verbose('Forcing exit');
- process.exit(0);
- }
- }
-
-};
-
-function setDefaultArgv(isModule) {
-
- internals.argv = optimist
- .default({
- verbose: false,
- table: 'migrations',
- 'seeds-table': 'seeds',
- 'force-exit': false,
- 'sql-file': false,
- 'no-transactions': false,
- config: process.cwd() + '/database.json',
- 'migrations-dir': process.cwd() + '/migrations',
- 'vcseeder-dir': process.cwd() + '/VCSeeder',
- 'staticseeder-dir': process.cwd() + '/Seeder'})
- .usage('Usage: db-migrate [up|down|reset|create|db] [[dbname/]migrationName|all] [options]')
-
- .describe('env', 'The environment to run the migrations under (dev, test, prod).')
- .alias('e', 'env')
- .string('e')
-
- .describe('migrations-dir', 'The directory containing your migration files.')
- .alias('m', 'migrations-dir')
- .string('m')
-
- .describe('count', 'Max number of migrations to run.')
- .alias('c', 'count')
- .string('c')
-
- .describe('dry-run', 'Prints the SQL but doesn\'t run it.')
- .boolean('dry-run')
-
- .describe('force-exit', 'Forcibly exit the migration process on completion.')
- .boolean('force-exit')
-
- .describe('verbose', 'Verbose mode.')
- .alias('v', 'verbose')
- .boolean('v')
-
- .alias('h', 'help')
- .alias('h', '?')
- .boolean('h')
-
- .describe('version', 'Print version info.')
- .alias('i', 'version')
- .boolean('version')
-
- .describe('config', 'Location of the database.json file.')
- .string('config')
-
- .describe('sql-file', 'Automatically create two sql files for up and down statements in /sqls and generate the javascript code that loads them.')
- .boolean('sql-file')
-
- .describe('coffee-file', 'Create a coffeescript migration file')
- .boolean('coffee-file')
-
- .describe('migration-table', 'Set the name of the migration table, which stores the migration history.')
- .alias('table', 'migration-table')
- .alias('t', 'table')
- .string('t')
-
- .describe('seeds-table', 'Set the name of the seeds table, which stores the seed history.')
- .string('seeds-table')
-
- .describe('vcseeder-dir', 'Set the path to the Version Controlled Seeder directory.')
- .string('vcseeder-dir')
-
- .describe('staticseeder-dir', 'Set the path to the Seeder directory.')
- .string('staticseeder-dir')
-
- .describe('no-transactions', 'Explicitly disable transactions')
- .boolean('no-transactions')
-
- .argv;
-
- if (internals.argv.version) {
- console.log(module.exports.version);
- process.exit(0);
- }
-
- if (!isModule && (internals.argv.help || internals.argv._.length === 0)) {
- optimist.showHelp();
- process.exit(1);
- }
-
- internals.migrationTable = internals.argv.table;
- internals.seedsTable = internals.argv['seeds-table'];
- internals.matching = '';
- internals.verbose = internals.argv.verbose;
- global.verbose = internals.verbose;
- internals.notransactions = internals.argv['no-transactions']
- internals.dryRun = internals.argv['dry-run'];
- if(internals.dryRun) {
- log.info('dry run');
- }
-
-}
-
-function createMigrationDir(dir, callback) {
- fs.stat(dir, function(err, stat) {
- if (err) {
- mkdirp(dir, callback);
- } else {
- callback();
- }
- });
-}
-
-function loadConfig() {
- if (process.env.DATABASE_URL) {
- config.loadUrl(process.env.DATABASE_URL, internals.argv.env);
- } else {
- config.load(internals.argv.config, internals.argv.env);
- }
- if(internals.verbose) {
- var current = config.getCurrent();
- var s = JSON.parse(JSON.stringify(current.settings));
-
- if (s.password)
- s.password = '******';
-
- log.info('Using', current.env, 'settings:', s);
- }
-}
-
-function executeCreate() {
- var folder, path;
-
- if(internals.argv._.length === 0) {
- log.error('\'migrationName\' is required.');
- optimist.showHelp();
- process.exit(1);
- }
-
- createMigrationDir(internals.argv['migrations-dir'], function(err) {
- if (err) {
- log.error('Failed to create migration directory at ', internals.argv['migrations-dir'], err);
- process.exit(1);
- }
-
- internals.argv.title = internals.argv._.shift();
- folder = internals.argv.title.split('/');
-
- internals.argv.title = folder[folder.length - 2] || folder[0];
- path = internals.argv['migrations-dir'];
-
- if(folder.length > 1) {
-
- path += '/';
-
- for(var i = 0; i < folder.length - 1; ++i) {
-
- path += folder[i] + '/';
- }
- }
-
- var templateType = Migration.TemplateType.DEFAULT_JS;
- if (shouldCreateSqlFiles()) {
- templateType = Migration.TemplateType.SQL_FILE_LOADER;
- } else if (shouldCreateCoffeeFile()) {
- templateType = Migration.TemplateType.DEFAULT_COFFEE;
- }
- var migration = new Migration(internals.argv.title + (shouldCreateCoffeeFile() ? '.coffee' : '.js'), path, new Date(), templateType);
- index.createMigration(migration, function(err, migration) {
- assert.ifError(err);
- log.info(util.format('Created migration at %s', migration.path));
- });
- });
-
- if (shouldCreateSqlFiles()) {
- createSqlFiles();
- }
-}
+ * Sets a config variable to the given value.
+ *
+ * @return value
+ */
+ setConfigParam: function (param, value) {
+ return (this.internals.argv[param] = value);
+ },
-function shouldCreateSqlFiles() {
- return internals.argv['sql-file'] || config['sql-file'];
-}
+ /**
+ * Sets the callback to the default onComplete
+ */
+ setDefaultCallback: function () {
+ this.internals.onComplete = onComplete;
+ },
-function shouldCreateCoffeeFile() {
- return internals.argv['coffee-file'] || config['coffee-file'];
-}
+ /**
+ * Let's the user customize the callback, which gets called after all
+ * migrations have been done.
+ */
+ setCustomCallback: function (callback) {
+ this.internals.onComplete = callback;
+ },
-function createSqlFiles() {
- var sqlDir = internals.argv['migrations-dir'] + '/sqls';
- createMigrationDir(sqlDir, function(err) {
- if (err) {
- log.error('Failed to create migration directory at ', sqlDir, err);
- process.exit(1);
+ /**
+ * Seeds either the static or version controlled seeders, controlled by
+ * the passed mode.
+ */
+ seed: function (mode, scope, callback) {
+ var executeSeed = load('seed');
+ if (scope) {
+ this.internals.migrationMode = scope;
+ this.internals.matching = scope;
}
- var templateTypeDefaultSQL = Migration.TemplateType.DEFAULT_SQL;
- var migrationUpSQL = new Migration(internals.argv.title + '-up.sql', sqlDir, new Date(), templateTypeDefaultSQL);
- index.createMigration(migrationUpSQL, function(err, migration) {
- assert.ifError(err);
- log.info(util.format('Created migration up sql file at %s', migration.path));
- });
- var migrationDownSQL = new Migration(internals.argv.title + '-down.sql', sqlDir, new Date(), templateTypeDefaultSQL);
- index.createMigration(migrationDownSQL, function(err, migration) {
- assert.ifError(err);
- log.info(util.format('Created migration down sql file at %s', migration.path));
- });
- });
-}
-
-function executeUp() {
-
- if(!internals.argv.count) {
- internals.argv.count = Number.MAX_VALUE;
- }
-
- index.connect(config.getCurrent().settings, Migrator, function(err, migrator) {
- assert.ifError(err);
-
- if(internals.locTitle)
- migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], internals.locTitle);
- else
- migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']);
-
- migrator.driver.createMigrationsTable(function(err) {
- assert.ifError(err);
- log.verbose('migration table created');
- migrator.up(internals.argv, internals.onComplete.bind(this, migrator));
- });
- });
-}
-
-function executeDown() {
-
- if(!internals.argv.count) {
- log.info('Defaulting to running 1 down migration.');
- internals.argv.count = 1;
- }
-
- index.connect(config.getCurrent().settings, Migrator, function(err, migrator) {
- assert.ifError(err);
-
- migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']);
-
- migrator.driver.createMigrationsTable(function(err) {
- assert.ifError(err);
- migrator.down(internals.argv, internals.onComplete.bind(this, migrator));
- });
- });
-}
-
-function executeDB() {
-
- if(internals.argv._.length > 0) {
- internals.argv.dbname = internals.argv._.shift().toString();
- }
- else {
-
- log.info('Error: You must enter a database name!');
- return;
- }
-
- index.driver(config.getCurrent().settings, function(err, db)
- {
- if(internals.mode === 'create')
- {
- db.createDatabase(internals.argv.dbname, { ifNotExists: true }, function()
- {
- if(err) {
- log.info('Error: Failed to create database!');
- }
- else {
- log.info('Created database "' + internals.argv.dbname + '"');
- }
+ this.internals.mode = mode || 'vc';
+ return Promise.fromCallback(
+ function (callback) {
+ executeSeed(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
+ },
- db.close();
- });
- }
- else if(internals.mode === 'drop')
- {
- db.dropDatabase(internals.argv.dbname, { ifExists: true }, function()
- {
- if(err) {
- log.info('Error: Failed to drop database!');
- }
- else {
- log.info('Deleted database "' + internals.argv.dbname + '"');
+ /**
+ * Execute the down function of currently executed seeds.
+ */
+ undoSeed: function (specification, scope, callback) {
+ var executeUndoSeed = load('undo-seed');
+ if (arguments.length > 0) {
+ if (typeof specification === 'number') {
+ this.internals.argv.count = specification;
+
+ if (scope) {
+ this.internals.migrationMode = scope;
+ this.internals.matching = scope;
}
-
- db.close();
- });
+ } else if (typeof specification === 'string') {
+ this.internals.migrationMode = scope;
+ this.internals.matching = scope;
+ }
}
- else
- return;
- });
-
-}
-
-function executeSeed() {
-
- if(internals.argv._.length > 0) {
- internals.argv.destination = internals.argv._.shift().toString();
- }
- index.connect(config.getCurrent().settings, Seeder, function(err, seeder)
- {
- assert.ifError(err);
-
- seeder.seedDir = path.resolve(internals.argv[(internals.mode !== 'static') ? 'vcseeder-dir': 'staticseeder-dir']);
- seeder.seed(internals.argv, internals.onComplete.bind(this, seeder));
- });
-}
-
-internals.onComplete = onComplete;
-
-function onComplete(migrator, originalErr) {
- migrator.driver.close(function(err) {
- assert.ifError(originalErr);
- assert.ifError(err);
- log.info('Done');
- });
-}
+ return Promise.fromCallback(
+ function (callback) {
+ executeUndoSeed(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
+ },
-function run() {
- var action = internals.argv._.shift(),
- folder = action.split(':');
-
- action = folder[0];
-
- switch(action) {
- case 'create':
- executeCreate();
- break;
- case 'up':
- case 'down':
- case 'reset':
-
- if(action === 'reset')
- internals.argv.count = Number.MAX_VALUE;
-
- if(internals.argv._.length > 0) {
- if (action === 'down') {
- log.info('Ignoring migration name for down migrations. Use --count to control how many down migrations are run.');
- internals.argv.destination = null;
- } else {
- internals.argv.destination = internals.argv._.shift().toString();
+ /**
+ * Execute the reset function of currently executed seeds.
+ */
+ resetSeed: function (specification, scope, callback) {
+ var executeUndoSeed = load('undo-seed');
+ if (arguments.length > 0) {
+ if (typeof specification === 'number') {
+ this.internals.argv.count = specification;
+
+ if (scope) {
+ this.internals.migrationMode = scope;
+ this.internals.matching = scope;
}
+ } else if (typeof specification === 'string') {
+ this.internals.migrationMode = scope;
+ this.internals.matching = scope;
}
+ }
- if(folder[1])
- {
- internals.matching = folder[1];
- internals.migrationMode = folder[1];
- }
-
- if(action == 'up') {
- executeUp();
- } else {
- executeDown();
- }
- break;
-
- case 'db':
-
- if(folder.length < 1) {
-
- log.info('Please enter a valid command, i.e. db:create|db:drop');
- }
- else {
+ this.internals.argv.count = Number.MAX_VALUE;
+ return Promise.fromCallback(
+ function (callback) {
+ executeUndoSeed(this.internals, this.config, callback);
+ }.bind(this)
+ ).asCallback(callback);
+ },
- internals.mode = folder[1];
- executeDB();
- }
- break;
- case 'seed':
-
- internals.mode = folder[1] || 'vc';
- internals.migrationMode = folder[2];
- executeSeed();
- break;
-
- default:
- log.error('Invalid Action: Must be [up|down|create|reset|seed|db].');
- optimist.showHelp();
- process.exit(1);
- break;
+ /**
+ * Executes the default routine.
+ */
+ run: function () {
+ load('run')(this.internals, this.config);
}
-}
-
+};
-module.exports = dbmigrate;
\ No newline at end of file
+module.exports = dbmigrate;
diff --git a/bin/db-migrate b/bin/db-migrate
index 642e2579..65f41f9e 100755
--- a/bin/db-migrate
+++ b/bin/db-migrate
@@ -1,6 +1,39 @@
#!/usr/bin/env node
-var DBMigrate = require('../api.js'),
- dbmigrate = new DBMigrate();
+var resolve = require('resolve');
+var log = require('db-migrate-shared').log;
-dbmigrate.run();
+process.title = 'db-migrate';
+if (process.argv.indexOf('--verbose') !== -1 ||
+ process.argv.indexOf('-v') !== -1
+) { global.verbose = true; }
+
+resolve('db-migrate', {
+
+ basedir: process.cwd()
+}, function (error, localModule) {
+ var DBMigrate, dbmigrate;
+
+ if (error) {
+ DBMigrate = require('../');
+ } else {
+ DBMigrate = require(localModule);
+ log.verbose('Detected and using the projects local version of db-migrate. ' +
+ '\'' + localModule + '\'');
+ }
+
+ if (typeof (DBMigrate.getInstance) !== 'function') {
+ DBMigrate = require('../');
+
+ log.warn('Using global instead of local detected version as you have a ' +
+ 'version older than 0.10.0 in your projects package.json!');
+ }
+
+ dbmigrate = DBMigrate.getInstance();
+ if (dbmigrate.registerAPIHook) {
+ dbmigrate.registerAPIHook()
+ .then(function () {
+ dbmigrate.run();
+ });
+ } else { dbmigrate.run(); }
+});
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 00000000..69b18d9f
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1 @@
+module.exports = { extends: ['@commitlint/config-angular'] };
diff --git a/connect.js b/connect.js
new file mode 100644
index 00000000..590aadc9
--- /dev/null
+++ b/connect.js
@@ -0,0 +1,194 @@
+var recursive = require('final-fs').readdirRecursive;
+var fs = require('fs');
+var driver = require('./lib/driver');
+var path = require('path');
+var log = require('db-migrate-shared').log;
+
+exports.connect = function (config, PassedClass, callback) {
+ var internals = {};
+
+ if (config.config) {
+ internals = config.internals;
+ config = config.config;
+ }
+
+ driver.connect(config, internals, function (err, db) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ if (internals.migrationMode) {
+ var dirPath = path.resolve(config['migrations-dir'] || 'migrations');
+
+ if (internals.migrationMode !== 'all') {
+ var switched = false;
+ var newConf;
+
+ try {
+ newConf = require(path.resolve(
+ config['migrations-dir'] || 'migrations',
+ internals.migrationMode
+ ) + '/config.json');
+ log.info(
+ 'loaded extra config for migration subfolder: "' +
+ internals.migrationMode +
+ '/config.json"'
+ );
+ switched = true;
+ } catch (e) {}
+
+ if (switched) {
+ db.switchDatabase(newConf, function (err) {
+ if (err) {
+ return callback(err);
+ }
+ internals.locTitle = internals.migrationMode;
+ callback(
+ null,
+ new PassedClass(
+ db,
+ config['migrations-dir'],
+ internals.mode !== 'static',
+ internals
+ )
+ );
+ });
+ } else {
+ internals.locTitle = internals.migrationMode;
+ callback(
+ null,
+ new PassedClass(
+ db,
+ config['migrations-dir'],
+ internals.mode !== 'static',
+ internals
+ )
+ );
+ }
+ } else {
+ recursive(
+ dirPath,
+ false,
+ config['migrations-dir'] || 'migrations'
+ ).then(function (files) {
+ var oldClose = db.close;
+
+ files = files.filter(function (file) {
+ return file !== 'migrations' && fs.statSync(file).isDirectory();
+ });
+
+ files.push('');
+
+ db.close = function (cb) {
+ migrationFiles(
+ files,
+ callback,
+ config,
+ internals,
+ PassedClass,
+ db,
+ oldClose,
+ cb
+ );
+ };
+
+ db.close();
+ });
+ }
+ } else {
+ callback(
+ null,
+ new PassedClass(
+ db,
+ config['migrations-dir'],
+ internals.mode !== 'static',
+ internals
+ )
+ );
+ }
+ });
+};
+
+exports.driver = function (config, callback) {
+ var internals = {};
+ if (config.config) {
+ internals = config.internals;
+ config = config.config;
+ }
+
+ driver.connect(config, internals, callback);
+};
+
+function migrationFiles (
+ files,
+ callback,
+ config,
+ internals,
+ PassedClass,
+ db,
+ close,
+ cb
+) {
+ var file;
+ var switched = false;
+ var newConf;
+
+ if (files.length === 1) {
+ db.close = close;
+ }
+
+ file = files.pop();
+ log.info('Enter scope "' + (file !== '' ? file : '/') + '"');
+
+ if (file !== '') {
+ try {
+ fs.statSync(path.resolve(file + '/config.json'));
+ newConf = require(path.resolve(file + '/config.json'));
+ log.info(
+ 'loaded extra config for migration subfolder: "' +
+ file +
+ '/config.json"'
+ );
+ switched = true;
+ } catch (e) {}
+ }
+
+ db.switchDatabase(switched ? newConf : config.database, function () {
+ internals.matching = file.substr(
+ file.indexOf(config['migrations-dir'] || 'migrations') +
+ (config['migrations-dir'] || 'migrations').length +
+ 1
+ );
+
+ if (internals.matching.length === 0) {
+ internals.matching = '';
+ }
+
+ internals.locTitle = internals.matching;
+ callback(
+ null,
+ new PassedClass(
+ db,
+ config['migrations-dir'],
+ internals.mode !== 'static',
+ internals
+ )
+ );
+
+ if (typeof cb === 'function') {
+ cb();
+ }
+ });
+}
+
+exports.createMigration = function (migration, callback) {
+ migration.write(function (err) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ callback(null, migration);
+ });
+};
diff --git a/index.js b/index.js
index 93188c54..f2eee0fe 100644
--- a/index.js
+++ b/index.js
@@ -1,136 +1,63 @@
-var recursive = require('final-fs').readdirRecursive;
+require('pkginfo')(module, 'version'); // jshint ignore:line
var fs = require('fs');
-var driver = require('./lib/driver');
var path = require('path');
-var log = require('./lib/log');
-exports.dataType = require('./lib/data_type');
-exports.config = require('./lib/config');
+exports.dataType = require('db-migrate-shared').dataType;
-var internals = {};
+function loadPluginList () {
+ var plugins = JSON.parse(
+ fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8')
+ );
+ var targets = [];
-exports.connect = function(config, passedClass, callback) {
- driver.connect(config, internals, function(err, db) {
- if (err) { callback(err); return; }
+ plugins = Object.assign(plugins.dependencies, plugins.devDependencies);
- if(internals.migrationMode)
- {
- var dirPath = path.resolve(config['migrations-dir'] || 'migrations');
-
- if(internals.migrationMode !== 'all')
- {
- var switched = false,
- newConf;
-
- try {
- newConf = require(path.resolve(config['migrations-dir'] || 'migrations', internals.migrationMode) + '/config.json');
- log.info('loaded extra config for migration subfolder: "' + internals.migrationMode + '/config.json"');
- switched = true;
- } catch(e) {}
-
- if(switched) {
-
- db.switchDatabase(newConf, function()
- {
- internals.locTitle = internals.migrationMode;
- callback(null, new passedClass(db, config['migrations-dir'], internals.mode !== 'static', internals));
- });
- }
- else
- {
- internals.locTitle = internals.migrationMode;
- callback(null, new passedClass(db, config['migrations-dir'], internals.mode !== 'static', internals));
- }
- }
- else
- {
- recursive(dirPath, false, config['migrations-dir'] || 'migrations')
- .then(function(files) {
- var oldClose = db.close;
-
- files = files.filter(function (file) {
- return file !== 'migrations' && fs.statSync(file).isDirectory();
- });
-
- files.push('');
-
- db.close = function(cb) { migrationFiles(files, callback, config, passedClass, db, oldClose, cb); };
-
- db.close();
- });
- }
- }
- else
- callback(null, new passedClass(db, config['migrations-dir'], internals.mode !== 'static', internals));
-
- });
-};
-
-exports.driver = function(config, callback) {
-
- driver.connect(config, internals, callback);
-};
-
-function migrationFiles(files, callback, config, passedClass, db, close, cb) {
- var file,
- switched = false,
- newConf;
-
- if(files.length === 1)
- {
- db.close = close;
+ for (var plugin in plugins) {
+ if (plugin.startsWith('db-migrate-plugin')) targets.push(plugin);
}
- file = files.pop();
+ return targets;
+}
- if(file !== '')
- {
+function loadPlugins () {
+ var plugins = loadPluginList();
+ var i = 0;
+ var length = plugins.length;
+ var hooks = {};
+
+ for (; i < length; ++i) {
+ var plugin = require(path.join(process.cwd(), 'node_modules', plugins[i]));
+
+ if (
+ typeof plugin.name !== 'string' ||
+ !plugin.hooks ||
+ !plugin.loadPlugin
+ ) {
+ continue;
+ }
- try {
- newConf = require(path.resolve(file + '/config.json'));
- log.info('loaded extra config for migration subfolder: "' + file + '/config.json"');
- switched = true;
- } catch(e) {}
+ plugin.hooks.map(function (hook) {
+ hooks[hook] = hooks[hook] || [];
+ hooks[hook].push(plugin);
+ });
}
- db.switchDatabase((switched) ? newConf : config.database, function()
- {
- internals.matching = file.substr(file.indexOf(config['migrations-dir'] || 'migrations') +
- (config['migrations-dir'] || 'migrations').length + 1);
-
- if(internals.matching.length === 0)
- internals.matching = '';
-
-
- internals.locTitle = internals.matching;
- callback(null, new passedClass(db, config['migrations-dir'], internals.mode !== 'static', internals));
-
- if(typeof(cb) === 'function')
- cb();
-
- });
+ return hooks;
}
-exports.createMigration = function(migration, callback) {
-
- migration.write(function(err) {
+module.exports.getInstance = function (isModule, options, callback) {
+ delete require.cache[require.resolve('./api.js')];
+ delete require.cache[require.resolve('optimist')];
+ var Mod = require('./api.js');
+ var plugins = {};
- if (err) {
+ try {
+ if (!options || !options.noPlugins) plugins = loadPlugins();
+ } catch (ex) {}
- callback(err);
- return;
- }
-
- callback(null, migration);
- });
-};
-
-exports.exportInternals = function( intern ) {
+ if (options && options.plugins) {
+ plugins = Object.assign(plugins, options.plugins);
+ }
- internals = intern;
+ return new Mod(plugins, isModule, options, callback);
};
-
-exports.importInternals = function() {
-
- return internals;
-};
\ No newline at end of file
diff --git a/lib/class.js b/lib/class.js
index 5dd9eae1..a534f940 100644
--- a/lib/class.js
+++ b/lib/class.js
@@ -3,13 +3,21 @@
* MIT Licensed.
*/
// Inspired by base2 and Prototype
-var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+var initializing = false;
+
+/*eslint-disable */
+var fnTest = /xyz/.test(function() {
+ xyz;
+})
+ ? /\b_super\b/
+ : /.*/;
+/* eslint-enable */
// The base Class implementation (does nothing)
-Class = function(){};
+var Class = function () {};
// Create a new Class that inherits from this class
-Class.extend = function(prop) {
+Class.extend = function ext (prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
@@ -21,32 +29,35 @@ Class.extend = function(prop) {
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
- prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" && fnTest.test(prop[name]) ?
- (function(name, fn){
- return function() {
- var tmp = this._super;
+ prototype[name] =
+ typeof prop[name] === 'function' &&
+ typeof _super[name] === 'function' &&
+ fnTest.test(prop[name])
+ ? (function (name, fn) {
+ return function () {
+ var tmp = this._super;
- // Add a new ._super() method that is the same method
- // but on the super-class
- this._super = _super[name];
+ // Add a new ._super() method that is the same method
+ // but on the super-class
+ this._super = _super[name];
- // The method only need to be bound temporarily, so we
- // remove it when we're done executing
- var ret = fn.apply(this, arguments);
- this._super = tmp;
+ // The method only need to be bound temporarily, so we
+ // remove it when we're done executing
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
- return ret;
- };
- })(name, prop[name]) :
- prop[name];
+ return ret;
+ };
+ })(name, prop[name])
+ : prop[name];
}
// The dummy class constructor
- function Class() {
+ function Class () {
// All construction is actually done in the init method
- if ( !initializing && this.init )
+ if (!initializing && this.init) {
this.init.apply(this, arguments);
+ }
}
// Populate our constructed prototype object
@@ -56,7 +67,7 @@ Class.extend = function(prop) {
Class.prototype.constructor = Class;
// And make this class extendable
- Class.extend = arguments.callee;
+ Class.extend = ext;
return Class;
};
diff --git a/lib/commands/check.js b/lib/commands/check.js
new file mode 100644
index 00000000..63891ff6
--- /dev/null
+++ b/lib/commands/check.js
@@ -0,0 +1,37 @@
+var path = require('path');
+var log = require('db-migrate-shared').log;
+var assert = require('./helper/assert.js');
+var migrationHook = require('./helper/migration-hook.js');
+
+module.exports = function (internals, config, callback) {
+ migrationHook(internals)
+ .then(function () {
+ var Migrator = require('../migrator.js');
+ var index = require('../../connect');
+
+ if (!internals.argv.count) {
+ internals.argv.count = Number.MAX_VALUE;
+ }
+ index.connect({
+ config: config.getCurrent().settings,
+ internals: internals
+ }, Migrator, function (err, migrator) {
+ if (!assert(err, callback)) return;
+
+ if (internals.locTitle) {
+ migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'],
+ internals.locTitle);
+ } else { migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); }
+
+ internals.migrationsDir = migrator.migrationsDir;
+
+ migrator.driver.createMigrationsTable(function (err) {
+ if (!assert(err, callback)) return;
+ log.verbose('migration table created');
+
+ migrator.check(internals.argv, internals.onComplete.bind(this,
+ migrator, internals, callback));
+ });
+ });
+ });
+};
diff --git a/lib/commands/create-migration.js b/lib/commands/create-migration.js
new file mode 100644
index 00000000..92adc3fb
--- /dev/null
+++ b/lib/commands/create-migration.js
@@ -0,0 +1,180 @@
+'use strict';
+
+var _assert = require('./helper/assert');
+var log = require('db-migrate-shared').log;
+var mkdirp = require('mkdirp');
+var fs = require('fs');
+var optimist = require('optimist');
+var util = require('util');
+
+function createMigrationDir (dir, callback) {
+ fs.stat(dir, function (err) {
+ if (err) {
+ mkdirp(dir, callback);
+ } else {
+ callback();
+ }
+ });
+}
+
+function executeCreateMigration (internals, config, callback) {
+ var migrationsDir = internals.argv['migrations-dir'];
+
+ internals.runTimestamp = new Date();
+
+ if (internals.migrationMode && internals.migrationMode !== 'all') {
+ migrationsDir =
+ internals.argv['migrations-dir'] + '/' + internals.migrationMode;
+ }
+
+ var folder, path;
+
+ if (internals.argv._.length === 0) {
+ log.error("'migrationName' is required.");
+ if (!internals.isModule) {
+ optimist.showHelp();
+ }
+
+ if (typeof callback !== 'function') {
+ process.exit(1);
+ } else {
+ return callback(new Error("'migrationName' is required."));
+ }
+ }
+
+ createMigrationDir(migrationsDir, function (err) {
+ var index = require('../../connect');
+ var Migration = require('../migration.js');
+
+ if (err) {
+ log.error('Failed to create migration directory at ', migrationsDir, err);
+ if (typeof callback !== 'function') {
+ process.exit(1);
+ } else {
+ return callback(new Error('Failed to create migration directory.'));
+ }
+ }
+
+ internals.argv.title = internals.argv._.shift();
+ folder = internals.argv.title.split('/');
+
+ internals.argv.title = folder[folder.length - 2] || folder[0];
+ path = migrationsDir;
+
+ if (folder.length > 1) {
+ path += '/';
+
+ for (var i = 0; i < folder.length - 1; ++i) {
+ path += folder[i] + '/';
+ }
+ }
+
+ var templateType = Migration.TemplateType.DEFAULT_JS;
+ if (
+ shouldCreateSqlFiles(internals, config) &&
+ shouldCreateCoffeeFile(internals, config)
+ ) {
+ templateType = Migration.TemplateType.COFFEE_SQL_FILE_LOADER;
+ } else if (
+ shouldCreateSqlFiles(internals, config) &&
+ shouldIgnoreOnInitFiles(internals, config)
+ ) {
+ templateType = Migration.TemplateType.SQL_FILE_LOADER_IGNORE_ON_INIT;
+ } else if (shouldCreateSqlFiles(internals, config)) {
+ templateType = Migration.TemplateType.SQL_FILE_LOADER;
+ } else if (shouldCreateCoffeeFile(internals, config)) {
+ templateType = Migration.TemplateType.DEFAULT_COFFEE;
+ }
+ var migration = new Migration(
+ internals.argv.title +
+ (shouldCreateCoffeeFile(internals, config) ? '.coffee' : '.js'),
+ path,
+ internals.runTimestamp,
+ templateType
+ );
+ index.createMigration(migration, function (err, migration) {
+ if (_assert(err, callback)) {
+ log.info(util.format('Created migration at %s', migration.path));
+ if (shouldCreateSqlFiles(internals, config)) {
+ createSqlFiles(internals, config, callback);
+ } else {
+ if (typeof callback === 'function') {
+ return callback();
+ }
+ }
+ }
+ });
+ });
+}
+
+function shouldCreateSqlFiles (internals, config) {
+ return internals.argv['sql-file'] || config['sql-file'];
+}
+
+function shouldIgnoreOnInitFiles (internals, config) {
+ return internals.argv['ignore-on-init'] || config['ignore-on-init'];
+}
+
+function shouldCreateCoffeeFile (internals, config) {
+ return internals.argv['coffee-file'] || config['coffee-file'];
+}
+
+function createSqlFiles (internals, config, callback) {
+ var migrationsDir = internals.argv['migrations-dir'];
+
+ if (internals.migrationMode && internals.migrationMode !== 'all') {
+ migrationsDir =
+ internals.argv['migrations-dir'] + '/' + internals.migrationMode;
+ }
+
+ var sqlDir = migrationsDir + '/sqls';
+ createMigrationDir(sqlDir, function (err) {
+ var index = require('../../connect');
+ var Migration = require('../migration.js');
+
+ if (err) {
+ log.error('Failed to create migration directory at ', sqlDir, err);
+
+ if (typeof callback !== 'function') {
+ process.exit(1);
+ } else {
+ return callback(err);
+ }
+ }
+
+ var templateTypeDefaultSQL = Migration.TemplateType.DEFAULT_SQL;
+ var migrationUpSQL = new Migration(
+ internals.argv.title + '-up.sql',
+ sqlDir,
+ internals.runTimestamp,
+ templateTypeDefaultSQL
+ );
+ index.createMigration(migrationUpSQL, function (err, migration) {
+ if (_assert(err, callback)) {
+ log.info(
+ util.format('Created migration up sql file at %s', migration.path)
+ );
+
+ var migrationDownSQL = new Migration(
+ internals.argv.title + '-down.sql',
+ sqlDir,
+ internals.runTimestamp,
+ templateTypeDefaultSQL
+ );
+ index.createMigration(migrationDownSQL, function (err, migration) {
+ if (_assert(err, callback)) {
+ log.info(
+ util.format(
+ 'Created migration down sql file at %s',
+ migration.path
+ )
+ );
+ if (typeof callback === 'function') callback();
+ }
+ });
+ }
+ });
+ });
+}
+
+module.exports = executeCreateMigration;
diff --git a/lib/commands/db.js b/lib/commands/db.js
new file mode 100644
index 00000000..de0cab86
--- /dev/null
+++ b/lib/commands/db.js
@@ -0,0 +1,61 @@
+'use strict';
+
+var log = require('db-migrate-shared').log;
+var assert = require('assert');
+
+function executeDB (internals, config, callback) {
+ var index = require('../../connect');
+
+ if (internals.argv._.length > 0) {
+ internals.argv.dbname = internals.argv._.shift().toString();
+ } else {
+ log.info('Error: You must enter a database name!');
+ return;
+ }
+
+ index.driver(config.getCurrent().settings, function (err, db) {
+ assert.ifError(err);
+
+ if (internals.mode === 'create') {
+ db.createDatabase(
+ internals.argv.dbname,
+ {
+ ifNotExists: true
+ },
+ function (err) {
+ if (err) {
+ if (err.error) err = err.error;
+ log.error('Error: Failed to create database!', err);
+ } else {
+ log.info('Created database "' + internals.argv.dbname + '"');
+ }
+
+ db.close();
+ if (typeof callback === 'function') callback(err);
+ else process.exit(1);
+ }
+ );
+ } else if (internals.mode === 'drop') {
+ db.dropDatabase(
+ internals.argv.dbname,
+ {
+ ifExists: true
+ },
+ function (err) {
+ if (err) {
+ if (err.error) err = err.error;
+ log.error('Error: Failed to drop database!', err);
+ } else {
+ log.info('Deleted database "' + internals.argv.dbname + '"');
+ }
+
+ db.close();
+ if (typeof callback === 'function') callback(err);
+ process.exit(1);
+ }
+ );
+ }
+ });
+}
+
+module.exports = executeDB;
diff --git a/lib/commands/down.js b/lib/commands/down.js
new file mode 100644
index 00000000..4454f1ed
--- /dev/null
+++ b/lib/commands/down.js
@@ -0,0 +1,32 @@
+var path = require('path');
+var log = require('db-migrate-shared').log;
+var assert = require('./helper/assert.js');
+var migrationHook = require('./helper/migration-hook.js');
+
+module.exports = function (internals, config, callback) {
+ migrationHook(internals)
+ .then(function () {
+ var Migrator = require('../migrator.js');
+ var index = require('../../connect');
+
+ if (!internals.argv.count) {
+ log.info('Defaulting to running 1 down migration.');
+ internals.argv.count = 1;
+ }
+
+ index.connect({
+ config: config.getCurrent().settings,
+ internals: internals
+ }, Migrator, function (err, migrator) {
+ if (!assert(err)) return;
+
+ migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']);
+
+ migrator.driver.createMigrationsTable(function (err) {
+ if (!assert(err)) return;
+ migrator.down(internals.argv, internals.onComplete.bind(this,
+ migrator, internals, callback));
+ });
+ });
+ });
+};
diff --git a/lib/commands/fn/plugin.js b/lib/commands/fn/plugin.js
new file mode 100644
index 00000000..a581c7fe
--- /dev/null
+++ b/lib/commands/fn/plugin.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const log = require('db-migrate-shared').log;
+
+function registerPluginLoader (plugins) {
+ return {
+ overwrite: function (name) {
+ if (plugins[name] && plugins[name].length) {
+ var plugin = plugins[name];
+
+ if (plugin.length !== 1) {
+ log.warn(
+ 'Attention, multiple overwrites registered for %s, we are ' +
+ 'only loading the first plugin %s!',
+ name,
+ plugin.name
+ );
+ }
+
+ plugin = plugin[0];
+ if (typeof plugin.loadPlugin === 'function') plugin.loadPlugin();
+
+ return plugin;
+ }
+
+ return false;
+ },
+
+ hook: function (name) {
+ if (plugins[name] && plugins[name].length) {
+ var plugin = plugins[name];
+
+ plugin.map(function (plugin) {
+ if (typeof plugin.loadPlugin === 'function') plugin.loadPlugin();
+ });
+
+ return plugin;
+ }
+
+ return false;
+ }
+ };
+}
+
+module.exports = registerPluginLoader;
diff --git a/lib/commands/helper/assert.js b/lib/commands/helper/assert.js
new file mode 100644
index 00000000..ad9d0888
--- /dev/null
+++ b/lib/commands/helper/assert.js
@@ -0,0 +1,15 @@
+var assert = require('assert');
+
+module.exports = function (err, callback) {
+ if (err) {
+ if (typeof (callback) === 'function') {
+ callback(err);
+ return false;
+ } else {
+ assert.ifError(err);
+ return false;
+ }
+ }
+
+ return true;
+};
diff --git a/lib/commands/helper/load-config.js b/lib/commands/helper/load-config.js
new file mode 100644
index 00000000..1ddbeed3
--- /dev/null
+++ b/lib/commands/helper/load-config.js
@@ -0,0 +1,26 @@
+'use strict';
+
+var log = require('db-migrate-shared').log;
+
+function loadConfig (config, internals) {
+ var out;
+ var currentEnv = internals.currentEnv || internals.argv.env;
+
+ if (internals.configObject) {
+ out = config.loadObject(internals.configObject, currentEnv);
+ } else {
+ out = config.loadFile(internals.argv.config, currentEnv, internals.plugins);
+ }
+ if (internals.verbose) {
+ var current = out.getCurrent();
+ var s = JSON.parse(JSON.stringify(current.settings));
+
+ if (s.password) s.password = '******';
+
+ log.info('Using', current.env, 'settings:', s);
+ }
+
+ return out;
+}
+
+module.exports = loadConfig;
diff --git a/lib/commands/helper/migration-hook.js b/lib/commands/helper/migration-hook.js
new file mode 100644
index 00000000..cb69a90e
--- /dev/null
+++ b/lib/commands/helper/migration-hook.js
@@ -0,0 +1,5 @@
+
+module.exports = function (internals) {
+ var Migration = require('../../migration.js');
+ return Migration.registerHook(internals.plugins, internals);
+};
diff --git a/lib/commands/helper/register-events.js b/lib/commands/helper/register-events.js
new file mode 100644
index 00000000..5a4a0eb2
--- /dev/null
+++ b/lib/commands/helper/register-events.js
@@ -0,0 +1,17 @@
+'use strict';
+
+var log = require('db-migrate-shared').log;
+
+function registerEvents () {
+ process.on('uncaughtException', function (err) {
+ log.error(err.stack || err);
+ process.exit(1);
+ });
+
+ process.on('unhandledRejection', function (reason) {
+ log.error(reason.stack || reason);
+ process.exit(1);
+ });
+}
+
+module.exports = registerEvents;
diff --git a/lib/commands/index.js b/lib/commands/index.js
new file mode 100644
index 00000000..e00162ef
--- /dev/null
+++ b/lib/commands/index.js
@@ -0,0 +1,9 @@
+'use strict';
+
+var path = require('path');
+
+function register (module) {
+ return require(path.resolve(__dirname, module));
+}
+
+module.exports = register;
diff --git a/lib/commands/on-complete.js b/lib/commands/on-complete.js
new file mode 100644
index 00000000..a0d0ad8d
--- /dev/null
+++ b/lib/commands/on-complete.js
@@ -0,0 +1,29 @@
+var assert = require('assert');
+var log = require('db-migrate-shared').log;
+
+module.exports = function (migrator, internals, callback, originalErr, results) {
+ if (typeof callback !== 'function') {
+ originalErr = originalErr || callback;
+ results = results || originalErr;
+ }
+
+ migrator.driver.close(function (err) {
+ if ((err || originalErr) && typeof callback === 'function') {
+ callback(originalErr || err);
+ return;
+ } else {
+ assert.ifError(originalErr);
+ assert.ifError(err);
+ log.info('Done');
+ }
+
+ if (internals.argv['force-exit']) {
+ log.verbose('Forcing exit');
+ return process.exit(0);
+ }
+
+ if (typeof callback === 'function') {
+ callback(null, results);
+ }
+ });
+};
diff --git a/lib/commands/run.js b/lib/commands/run.js
new file mode 100644
index 00000000..bfdfe819
--- /dev/null
+++ b/lib/commands/run.js
@@ -0,0 +1,127 @@
+'use strict';
+
+var log = require('db-migrate-shared').log;
+var optimist = require('optimist');
+var load = require('./');
+var transition = load('transition');
+
+function run (internals, config) {
+ var action = internals.argv._.shift();
+ var folder = action.split(':');
+
+ action = folder[0];
+
+ switch (action) {
+ case 'transition':
+ transition(internals);
+ break;
+ case 'create':
+ if (folder[1]) {
+ internals.matching = folder[1];
+ internals.migrationMode = folder[1];
+ }
+ load('create-migration')(internals, config);
+ break;
+ case 'sync':
+ var executeSync = load('sync');
+
+ if (internals.argv._.length === 0) {
+ log.error('Missing sync destination!');
+ process.exit(1);
+ }
+
+ internals.argv.count = Number.MAX_VALUE;
+ internals.argv.destination = internals.argv._.shift().toString();
+
+ if (folder[1]) {
+ internals.matching = folder[1];
+ internals.migrationMode = folder[1];
+ }
+
+ executeSync(internals, config);
+ break;
+ case 'up':
+ case 'down':
+ case 'reset':
+ if (action === 'reset') internals.argv.count = Number.MAX_VALUE;
+
+ if (internals.argv._.length > 0) {
+ if (action === 'down') {
+ internals.argv.count = internals.argv.count || Number.MAX_VALUE;
+ internals.argv.destination = internals.argv._.shift().toString();
+ } else {
+ internals.argv.destination = internals.argv._.shift().toString();
+ }
+ }
+
+ if (folder[1]) {
+ internals.matching = folder[1];
+ internals.migrationMode = folder[1];
+ }
+
+ if (action === 'up') {
+ var executeUp = load('up');
+ executeUp(internals, config);
+ } else {
+ var executeDown = load('down');
+ executeDown(internals, config);
+ }
+ break;
+
+ case 'check':
+ var executeCheck = load('check');
+
+ if (folder[1]) {
+ internals.matching = folder[1];
+ internals.migrationMode = folder[1];
+ }
+
+ executeCheck(internals, config);
+ break;
+ case 'db':
+ if (folder.length < 1) {
+ log.info('Please enter a valid command, i.e. db:create|db:drop');
+ } else {
+ internals.mode = folder[1];
+ load('db')(internals, config);
+ }
+ break;
+ case 'seed':
+ internals.mode = folder[1] || 'vc';
+ internals.migrationMode = folder[2];
+
+ if (internals.argv._[0] === 'down' || internals.argv._[0] === 'reset') {
+ if (internals.argv._[0] === 'reset') {
+ internals.argv.count = Number.MAX_VALUE;
+ }
+
+ internals.argv._.shift();
+ load('undo-seed')(internals, config);
+ } else {
+ load('seed')(internals, config);
+ }
+ break;
+
+ default:
+ var plugins = internals.plugins;
+ var plugin = plugins.overwrite(
+ 'run:default:action:' + action + ':overwrite'
+ );
+ if (plugin) {
+ plugin['run:default:action:' + action + ':overwrite'](
+ internals,
+ config
+ );
+ } else {
+ log.error(
+ 'Invalid Action: Must be [up|down|check|create|reset|sync|' +
+ 'db|transition].'
+ );
+ optimist.showHelp();
+ process.exit(1);
+ }
+ break;
+ }
+}
+
+module.exports = run;
diff --git a/lib/commands/seed.js b/lib/commands/seed.js
new file mode 100644
index 00000000..72b18a66
--- /dev/null
+++ b/lib/commands/seed.js
@@ -0,0 +1,47 @@
+'use strict';
+
+var assert = require('assert');
+var path = require('path');
+var _assert = require('./helper/assert');
+
+function executeSeed (internals, config, callback) {
+ var index = require('../../connect');
+ var Seeder = require('../seeder.js');
+
+ if (internals.argv._.length > 0) {
+ internals.argv.destination = internals.argv._.shift().toString();
+ }
+
+ index.connect(
+ {
+ config: config.getCurrent().settings,
+ internals: internals
+ },
+ Seeder,
+ function (err, seeder) {
+ assert.ifError(err);
+ var seedDir =
+ internals.mode !== 'static' ? 'vcseeder-dir' : 'staticseeder-dir';
+
+ seeder.seedDir = path.resolve(internals.argv[seedDir]);
+
+ if (internals.mode === 'static') {
+ seeder.seed(
+ internals.argv,
+ internals.onComplete.bind(this, seeder, internals, callback)
+ );
+ } else {
+ seeder.createSeedsTable(function (err) {
+ if (_assert(err, callback)) {
+ seeder.seed(
+ internals.argv,
+ internals.onComplete.bind(this, seeder, internals, callback)
+ );
+ }
+ });
+ }
+ }
+ );
+}
+
+module.exports = executeSeed;
diff --git a/lib/commands/set-default-argv.js b/lib/commands/set-default-argv.js
new file mode 100644
index 00000000..bd89db37
--- /dev/null
+++ b/lib/commands/set-default-argv.js
@@ -0,0 +1,158 @@
+var optimist = require('optimist');
+var log = require('db-migrate-shared').log;
+
+module.exports = function (internals, isModule) {
+ var rc = require('rc');
+ var deepExtend = require('deep-extend');
+ var defaultConfig = {
+ verbose: false,
+ table: 'migrations',
+ 'seeds-table': 'seeds',
+ 'force-exit': false,
+ 'sql-file': false,
+ 'non-transactional': false,
+ config: internals.configFile || internals.cwd + '/database.json',
+ 'migrations-dir': internals.cwd + '/migrations',
+ 'vcseeder-dir': internals.cwd + '/VCSeeder',
+ 'staticseeder-dir': internals.cwd + '/Seeder',
+ 'ignore-completed-migrations': false
+ };
+
+ if (!isModule) {
+ internals.argv = optimist
+ .default(defaultConfig)
+ .usage(
+ 'Usage: db-migrate [up|down|check|reset|sync|create|db|transition] ' +
+ '[[dbname/]migrationName|all] [options]'
+ )
+ .describe(
+ 'env',
+ 'The environment to run the migrations under (dev, test, prod).'
+ )
+ .alias('e', 'env')
+ .string('e')
+ .describe(
+ 'migrations-dir',
+ 'The directory containing your migration files.'
+ )
+ .alias('m', 'migrations-dir')
+ .string('m')
+ .describe('count', 'Max number of migrations to run.')
+ .alias('c', 'count')
+ .string('c')
+ .describe('dry-run', "Prints the SQL but doesn't run it.")
+ .boolean('dry-run')
+ .describe('check', 'Prints the migrations to be run without running them.')
+ .boolean('check')
+ .describe(
+ 'force-exit',
+ 'Forcibly exit the migration process on completion.'
+ )
+ .boolean('force-exit')
+ .describe('verbose', 'Verbose mode.')
+ .alias('v', 'verbose')
+ .boolean('v')
+ .alias('h', 'help')
+ .alias('h', '?')
+ .boolean('h')
+ .describe('version', 'Print version info.')
+ .alias('i', 'version')
+ .boolean('version')
+ .describe('config', 'Location of the database.json file.')
+ .string('config')
+ .describe(
+ 'sql-file',
+ 'Automatically create two sql files for up and down statements in ' +
+ '/sqls and generate the javascript code that loads them.'
+ )
+ .boolean('sql-file')
+ .describe('coffee-file', 'Create a coffeescript migration file')
+ .boolean('coffee-file')
+ .describe(
+ 'ignore-on-init',
+ 'Create files that will run only if ignore-on-init in the env is set ' +
+ 'to false (currently works only with SQL)'
+ )
+ .boolean('ignore-on-init')
+ .describe(
+ 'migration-table',
+ 'Set the name of the migration table, which stores the migration history.'
+ )
+ .alias('table', 'migration-table')
+ .alias('t', 'table')
+ .string('t')
+ .describe(
+ 'seeds-table',
+ 'Set the name of the seeds table, which stores the seed history.'
+ )
+ .string('seeds-table')
+ .describe(
+ 'vcseeder-dir',
+ 'Set the path to the Version Controlled Seeder directory.'
+ )
+ .string('vcseeder-dir')
+ .describe('staticseeder-dir', 'Set the path to the Seeder directory.')
+ .string('staticseeder-dir')
+ .describe('non-transactional', 'Explicitly disable transactions')
+ .boolean('non-transactional')
+ .describe('ignore-completed-migrations', 'Start at the first migration')
+ .boolean('ignore-completed-migrations')
+ .describe('log-level', 'Set the log-level, for example sql|warn')
+ .string('log-level');
+ } else {
+ const _internalsArgv = Object.assign(defaultConfig, internals.cmdOptions);
+ internals.argv = {
+ get argv () {
+ return _internalsArgv;
+ }
+ };
+ }
+
+ var plugins = internals.plugins;
+ var plugin = plugins.hook('init:cli:config:hook');
+ var _config = internals.argv.argv.config;
+
+ if (plugin) {
+ plugin.forEach(function (plugin) {
+ // var configs = plugin['init:cli:config:hook']();
+ // if (!configs) return;
+ // hook not yet used, we look into migrating away from optimist first
+ });
+ }
+
+ internals.argv = deepExtend(internals.argv.argv, rc('db-migrate', {}));
+ internals.argv.rcconfig = internals.argv.config;
+ internals.argv.config = internals.argv.configFile || _config;
+
+ if (internals.argv.version) {
+ console.log(internals.dbm.version);
+ process.exit(0);
+ }
+
+ if (!isModule && (internals.argv.help || internals.argv._.length === 0)) {
+ optimist.showHelp();
+ process.exit(1);
+ }
+
+ if (internals.argv['log-level']) {
+ log.setLogLevel(internals.argv['log-level']);
+ }
+
+ internals.ignoreCompleted = internals.argv['ignore-completed-migrations'];
+ internals.migrationTable = internals.argv.table;
+ internals.seedTable = internals.argv['seeds-table'];
+ internals.matching = '';
+ internals.verbose = internals.argv.verbose;
+ global.verbose = internals.verbose;
+ internals.notransactions = internals.argv['non-transactional'];
+ internals.dryRun = internals.argv['dry-run'];
+ global.dryRun = internals.dryRun;
+ internals.check = internals.argv['check'];
+
+ if (internals.dryRun) {
+ log.info('dry run');
+ }
+ if (internals.check) {
+ log.info('check');
+ }
+};
diff --git a/lib/commands/sync.js b/lib/commands/sync.js
new file mode 100644
index 00000000..ed1d3fc0
--- /dev/null
+++ b/lib/commands/sync.js
@@ -0,0 +1,37 @@
+var path = require('path');
+var log = require('db-migrate-shared').log;
+var assert = require('./helper/assert.js');
+var migrationHook = require('./helper/migration-hook.js');
+
+module.exports = function (internals, config, callback) {
+ migrationHook(internals)
+ .then(function () {
+ var Migrator = require('../migrator.js');
+ var index = require('../../connect');
+
+ if (!internals.argv.count) {
+ internals.argv.count = Number.MAX_VALUE;
+ }
+ index.connect({
+ config: config.getCurrent().settings,
+ internals: internals
+ }, Migrator, function (err, migrator) {
+ if (!assert(err)) return;
+
+ if (internals.locTitle) {
+ migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'],
+ internals.locTitle);
+ } else { migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); }
+
+ internals.migrationsDir = migrator.migrationsDir;
+
+ migrator.driver.createMigrationsTable(function (err) {
+ if (!assert(err)) return;
+ log.verbose('migration table created');
+
+ migrator.sync(internals.argv, internals.onComplete.bind(this,
+ migrator, internals, callback));
+ });
+ });
+ });
+};
diff --git a/lib/commands/transition.js b/lib/commands/transition.js
new file mode 100644
index 00000000..5a3814ad
--- /dev/null
+++ b/lib/commands/transition.js
@@ -0,0 +1,5 @@
+'use strict';
+
+module.exports = function (internals) {
+ require('../transitions/transitioner.js')(internals);
+};
diff --git a/lib/commands/undo-seed.js b/lib/commands/undo-seed.js
new file mode 100644
index 00000000..6f2cd09f
--- /dev/null
+++ b/lib/commands/undo-seed.js
@@ -0,0 +1,53 @@
+'use strict';
+
+var assert = require('assert');
+var path = require('path');
+var log = require('db-migrate-shared').log;
+var _assert = require('./helper/assert');
+
+function executeUndoSeed (internals, config, callback) {
+ var index = require('./connect');
+ var Seeder = require('./lib/seeder.js');
+
+ if (!internals.argv.count) {
+ log.info('Defaulting to running 1 down seed.');
+ internals.argv.count = 1;
+ }
+
+ if (internals.argv._.length > 0) {
+ internals.argv.destination = internals.argv._.shift().toString();
+ }
+
+ index.connect(
+ {
+ config: config.getCurrent().settings,
+ internals: internals
+ },
+ Seeder,
+ function (err, seeder) {
+ assert.ifError(err);
+
+ var seedDir =
+ internals.mode !== 'static' ? 'vcseeder-dir' : 'staticseeder-dir';
+
+ seeder.seedDir = path.resolve(internals.argv[seedDir]);
+
+ if (internals.mode === 'static') {
+ internals.onComplete(seeder, callback, {
+ stack: "Static seeders can't be undone. Use VC Seeders instead!"
+ });
+ } else {
+ seeder.createSeedsTable(function (err) {
+ if (_assert(err, callback)) {
+ seeder.down(
+ internals.argv,
+ internals.onComplete.bind(this, seeder, internals, callback)
+ );
+ }
+ });
+ }
+ }
+ );
+}
+
+module.exports = executeUndoSeed;
diff --git a/lib/commands/up.js b/lib/commands/up.js
new file mode 100644
index 00000000..fccd9348
--- /dev/null
+++ b/lib/commands/up.js
@@ -0,0 +1,37 @@
+var path = require('path');
+var log = require('db-migrate-shared').log;
+var assert = require('./helper/assert.js');
+var migrationHook = require('./helper/migration-hook.js');
+
+module.exports = function (internals, config, callback) {
+ migrationHook(internals)
+ .then(function () {
+ var Migrator = require('../migrator.js');
+ var index = require('../../connect');
+
+ if (!internals.argv.count) {
+ internals.argv.count = Number.MAX_VALUE;
+ }
+ index.connect({
+ config: config.getCurrent().settings,
+ internals: internals
+ }, Migrator, function (err, migrator) {
+ if (!assert(err, callback)) return;
+
+ if (internals.locTitle) {
+ migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'],
+ internals.locTitle);
+ } else { migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); }
+
+ internals.migrationsDir = migrator.migrationsDir;
+
+ migrator.driver.createMigrationsTable(function (err) {
+ if (!assert(err, callback)) return;
+ log.verbose('migration table created');
+
+ migrator.up(internals.argv, internals.onComplete.bind(this,
+ migrator, internals, callback));
+ });
+ });
+ });
+};
diff --git a/lib/config.js b/lib/config.js
index d50d1faa..85fc229c 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -1,87 +1,207 @@
var fs = require('fs');
var path = require('path');
var parseDatabaseUrl = require('parse-database-url');
-var dbmUtil = require('./util');
+var dbmUtil = require('db-migrate-shared').util;
+var log = require('db-migrate-shared').log;
+
+var setCurrent = (exports.setCurrent = function (env) {
+ env = dbmUtil.isArray(env) ? env : [env];
+ env.forEach(
+ function (current) {
+ if (dbmUtil.isString(current) && this[current]) {
+ this.getCurrent = function () {
+ return {
+ env: current,
+ settings: this[current]
+ };
+ };
+ }
+ }.bind(this)
+ );
+
+ if (!this.getCurrent) {
+ throw new Error("Environment(s) '" + env.join(', ') + "' not found.");
+ }
+});
+
+function Config () {}
+
+Config.prototype = {
+ setCurrent: setCurrent
+};
+
+exports.load = function (config, currentEnv) {
+ if (typeof config === 'object') {
+ return exports.loadObject(config, currentEnv);
+ } else {
+ return exports.loadFile(config, currentEnv);
+ }
+};
+
+exports.loadFile = function (fileName, currentEnv, plugins) {
+ var config;
-exports.load = function(fileName, currentEnv) {
try {
fs.statSync(fileName);
- } catch(e) {
- throw new Error("Could not find database config file '" + fileName + "'");
+ } catch (e) {
+ if (!process.env.DATABASE_URL) {
+ throw new Error("Could not find database config file '" + fileName + "'");
+ } else {
+ return exports.loadUrl(process.env.DATABASE_URL, currentEnv);
+ }
}
- var config;
try {
- config = require(fileName);
- } catch(e) {
+ var plugin = false;
+
+ if (plugins) {
+ plugin = plugins.overwrite('init:config:overwrite:require');
+ }
+
+ if (plugin !== false) {
+ try {
+ config = plugin['init:config:overwrite:require'](fileName);
+ } catch (ex) {
+ log.warn(
+ 'Plugin failure "' +
+ plugin.name +
+ '", falling back to default behavior!'
+ );
+ log.verbose(ex);
+
+ config = require(fileName);
+ }
+ } else {
+ config = require(fileName);
+ }
+ } catch (e) {
// distinguish broken files from missing ones
- if (e instanceof SyntaxError){
+ if (e instanceof SyntaxError) {
throw e;
}
- config = require(path.join(process.cwd(), fileName));
+ try {
+ config = require(path.join(process.cwd(), fileName));
+ } catch (e) {
+ if (!process.env.DATABASE_URL) {
+ throw e;
+ } else {
+ return exports.loadUrl(process.env.DATABASE_URL, currentEnv);
+ }
+ }
}
+ return exports.loadObject(config, currentEnv);
+};
+
+function walkConfig (level) {
+ for (var configEntry in level) {
+ if (level[configEntry] && level[configEntry].ENV) {
+ if (!process.env[level[configEntry].ENV]) {
+ log.verbose(
+ 'Environment variable ' + level[configEntry].ENV + ' is empty!'
+ );
+ }
+
+ level[configEntry] = process.env[level[configEntry].ENV];
+ } else if (level[configEntry] && typeof level[configEntry] === 'object') {
+ level[configEntry] = walkConfig(level[configEntry]);
+ }
+ }
+
+ return level;
+}
+
+exports.loadObject = function (_config, currentEnv) {
+ var out = new Config();
+ // do not overwrite the users config
+ var config = JSON.parse(JSON.stringify(_config));
+
for (var env in config) {
if (config[env].ENV) {
- exports[env] = parseDatabaseUrl(process.env[config[env].ENV]);
- } else if (typeof(config[env]) === 'string') {
- exports[env] = parseDatabaseUrl(config[env]);
+ if (!process.env[config[env].ENV]) {
+ log.verbose('Environment variable ' + config[env].ENV + ' is empty!');
+ } else {
+ out[env] = parseDatabaseUrl(process.env[config[env].ENV]);
+ }
+ } else if (typeof config[env] === 'string') {
+ out[env] = parseDatabaseUrl(config[env]);
} else {
- //Check config entry's for ENV objects
- //which will tell us to grab configuration from the environment
- for (var configEntry in config[env]) {
- if (config[env][configEntry] && config[env][configEntry].ENV){
- config[env][configEntry] = process.env[config[env][configEntry].ENV];
- }
+ // Check config entry's for ENV objects
+ // which will tell us to grab configuration from the environment
+ config[env] = walkConfig(config[env]);
+ out[env] = config[env];
+ }
+
+ if (typeof config[env].url === 'string' || process.env.DATABASE_URL) {
+ if (typeof config[env].url !== 'string') {
+ config[env] = Object.assign(
+ config[env],
+ parseDatabaseUrl(process.env.DATABASE_URL)
+ );
+ } else {
+ config[env] = Object.assign(
+ config[env],
+ parseDatabaseUrl(config[env].url)
+ );
+ }
+ delete config[env].url;
+ } else if (config[env].url && config[env].url.value) {
+ config[env].url = config[env].url.value;
+ }
+
+ if (config[env].overwrite || config[env].addIfNotExists) {
+ var overwrite = config[env].overwrite || {};
+
+ if (config[env].addIfNotExists) {
+ var addIfNotExists = config[env].addIfNotExists;
+ Object.keys(addIfNotExists)
+ .filter(function (key) {
+ return !overwrite[key] && !config[env][key];
+ })
+ .forEach(function (key) {
+ config[env][key] = addIfNotExists[key];
+ });
+
+ delete config[env].addIfNotExists;
}
- exports[env] = config[env];
+
+ Object.keys(overwrite).forEach(function (key) {
+ config[env][key] = overwrite[key];
+ });
+
+ delete config[env].overwrite;
}
}
- if(currentEnv) {
- setCurrent(currentEnv);
- } else if(config['default']) {
- setCurrent(config['default']);
- } else if(config.env) {
- setCurrent(config.env);
+ if (currentEnv) {
+ out.setCurrent(currentEnv);
+ } else if (config['default']) {
+ out.setCurrent(config['default']);
+ } else if (config.defaultEnv) {
+ if (config.defaultEnv.ENV) {
+ out.setCurrent(process.env[config.defaultEnv.ENV]);
+ } else {
+ out.setCurrent(config.defaultEnv);
+ }
} else {
- setCurrent(['dev', 'development']);
+ out.setCurrent(['dev', 'development']);
}
- delete exports.load;
- delete exports.loadUrl;
+ return out;
};
-exports.loadUrl = function(databaseUrl, currentEnv) {
+exports.loadUrl = function (databaseUrl, currentEnv) {
var config = parseDatabaseUrl(databaseUrl);
+ var out = new Config();
+
if (currentEnv) {
- exports[currentEnv] = config;
- setCurrent(currentEnv);
+ out[currentEnv] = config;
+ out.setCurrent(currentEnv);
} else {
- exports.urlConfig = config;
- setCurrent('urlConfig');
+ out.urlConfig = config;
+ out.setCurrent('urlConfig');
}
- delete exports.load;
- delete exports.loadUrl;
-}
-
-var setCurrent = exports.setCurrent = function (env) {
- env = dbmUtil.isArray(env) ? env : [env];
-
- env.forEach(function (current) {
- if (dbmUtil.isString(current) && exports[current]) {
- exports.getCurrent = function () {
- return {
- env: current,
- settings: exports[current]
- };
- };
- }
- });
-
- if (!exports.getCurrent) {
- throw new Error("Environment(s) '" + env.join(', ') + "' not found.");
- }
+ return out;
};
diff --git a/lib/data_type.js b/lib/data_type.js
deleted file mode 100644
index 5f8f1222..00000000
--- a/lib/data_type.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = {
- CHAR: 'char',
- STRING: 'string',
- TEXT: 'text',
- SMALLINT: 'smallint',
- INTEGER: 'int',
- BIG_INTEGER: 'bigint',
- REAL: 'real',
- DATE: 'date',
- DATE_TIME: 'datetime',
- TIME: 'time',
- BLOB: 'blob',
- TIMESTAMP: 'timestamp',
- BINARY: 'binary',
- BOOLEAN: 'boolean',
- DECIMAL: 'decimal'
-};
diff --git a/lib/driver/base.js b/lib/driver/base.js
deleted file mode 100644
index 7da06930..00000000
--- a/lib/driver/base.js
+++ /dev/null
@@ -1,338 +0,0 @@
-var util = require('util');
-var events = require('events');
-var type = require('../data_type');
-var log = require('../log');
-var Class = require('../class');
-var Promise = require('bluebird');
-
-var internals = {};
-
-module.exports = Base = Class.extend({
- init: function(intern) {
- internals = intern;
- this.eventEmmiter = new events.EventEmitter();
- for(var n in events.EventEmitter.prototype) {
- this[n] = events.EventEmitter.prototype[n];
- }
- },
-
- close: function() {
- throw new Error('not yet implemented');
- },
-
- mapDataType: function(str) {
- switch(str) {
- case type.STRING:
- return 'VARCHAR';
- case type.TEXT:
- return 'TEXT';
- case type.INTEGER:
- return 'INTEGER';
- case type.BIG_INTEGER:
- return 'BIGINT';
- case type.DATE_TIME:
- return 'INTEGER';
- case type.REAL:
- return 'REAL';
- case type.BLOB:
- return 'BLOB';
- case type.TIMESTAMP:
- return 'TIMESTAMP';
- case type.BINARY:
- return 'BINARY';
- case type.BOOLEAN:
- return 'BOOLEAN';
- case type.DECIMAL:
- return 'DECIMAL';
- case type.CHAR:
- return 'CHAR';
- case type.DATE:
- return 'DATE';
- case type.SMALLINT:
- return 'SMALLINT';
- default:
- var unknownType = str.toUpperCase();
- log.warn('Using unknown data type', unknownType);
- return unknownType;
- }
- },
-
- createDatabase: function() {
-
- throw new Error('not implemented');
- },
-
- switchDatabase: function() {
- throw new Error('not implemented');
- },
-
- dropDatabase: function() {
- throw new Error('not implemented');
- },
-
- recurseCallbackArray: function(foreignKeys, callback)
- {
- var self = this;
-
- if (foreignKeys.length > 0)
- (foreignKeys.pop())(function() { self.recurseCallbackArray(foreignKeys, callback); } );
- else
- callback();
- },
-
- bindForeignKey: function(tableName, columnName, fkOptions) {
- var self = this,
- mapping = {};
-
- if(typeof(fkOptions.mapping) === 'string')
- mapping[columnName] = fkOptions.mapping;
- else
- mapping = fkOptions.mapping;
-
- return function (callback) { self.addForeignKey(tableName, fkOptions.table,
- fkOptions.name, mapping, fkOptions.rules, callback); };
- },
-
- createColumnDef: function(name, spec, options) {
- name = '"' + name + '"';
- var type = this.mapDataType(spec.type);
- var len = spec.length ? util.format('(%s)', spec.length) : '';
- var constraint = this.createColumnConstraint(spec, options);
-
- return { foreignKey: null,
- constraints: [name, type, len, constraint].join(' ') };
- },
-
- createMigrationsTable: function(callback) {
- var options = {
- columns: {
- 'id': { type: type.INTEGER, notNull: true, primaryKey: true, autoIncrement: true },
- 'name': { type: type.STRING, length: 255, notNull: true},
- 'run_on': { type: type.DATE_TIME, notNull: true}
- },
- ifNotExists: true
- };
- this.createTable(internals.migrationTable, options, callback);
- },
-
- createTable: function(tableName, options, callback) {
- log.verbose('creating table:', tableName);
- var columnSpecs = options;
- var tableOptions = {};
-
- if (options.columns !== undefined) {
- columnSpecs = options.columns;
- delete options.columns;
- tableOptions = options;
- }
-
- var ifNotExistsSql = "";
- if(tableOptions.ifNotExists) {
- ifNotExistsSql = "IF NOT EXISTS";
- }
-
- var primaryKeyColumns = [];
- var columnDefOptions = {
- emitPrimaryKey: false
- };
-
- for (var columnName in columnSpecs) {
- var columnSpec = this.normalizeColumnSpec(columnSpecs[columnName]);
- columnSpecs[columnName] = columnSpec;
- if (columnSpec.primaryKey) {
- primaryKeyColumns.push(columnName);
- }
- }
-
- var pkSql = '';
- if (primaryKeyColumns.length > 1) {
- pkSql = util.format(', PRIMARY KEY (%s)', this.quoteArr(primaryKeyColumns).join(', '));
- } else {
- columnDefOptions.emitPrimaryKey = true;
- }
-
- var columnDefs = [];
- var foreignKeys = [];
-
- for (var columnName in columnSpecs) {
- var columnSpec = columnSpecs[columnName];
- var constraint = this.createColumnDef(columnName, columnSpec, columnDefOptions, tableName);
-
- columnDefs.push(constraint.constraints);
- if (constraint.foreignKey)
- foreignKeys.push(constraint.foreignKey);
- }
-
- var sql = util.format('CREATE TABLE %s "%s" (%s%s)', ifNotExistsSql, tableName, columnDefs.join(', '), pkSql);
-
- this.runSql(sql, function()
- {
-
- this.recurseCallbackArray(foreignKeys, callback);
- }.bind(this));
- },
-
- dropTable: function(tableName, options, callback) {
-
- if (arguments.length < 3) {
- callback = options;
- options = {};
- }
-
- var ifExistsSql = '';
- if (options.ifExists) {
- ifExistsSql = 'IF EXISTS';
- }
- var sql = util.format('DROP TABLE %s "%s"', ifExistsSql, tableName);
- this.runSql(sql, callback);
- },
-
- renameTable: function(tableName, newTableName, callback) {
- throw new Error('not yet implemented');
- },
-
- addColumn: function(tableName, columnName, columnSpec, callback) {
-
- var def = this.createColumnDef(columnName, this.normalizeColumnSpec(columnSpec, tableName));
- var sql = util.format('ALTER TABLE "%s" ADD COLUMN %s', tableName, def.constraints);
-
- this.runSql(sql, function()
- {
- if(def.foreignKey)
- def.foreignKey(callback);
- else
- callback();
- });
- },
-
- removeColumn: function(tableName, columnName, callback) {
- throw new Error('not yet implemented');
- },
-
- renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
- throw new Error('not yet implemented');
- },
-
- changeColumn: function(tableName, columnName, columnSpec, callback) {
- throw new Error('not yet implemented');
- },
-
- quoteArr: function(arr) {
-
- for(var i = 0; i < arr.length; ++i)
- arr[i] = '"' + arr[i] + '"';
-
- return arr;
- },
-
- addIndex: function(tableName, indexName, columns, unique, callback) {
- if (typeof(unique) === 'function') {
- callback = unique;
- unique = false;
- }
-
- if (!Array.isArray(columns)) {
- columns = [columns];
- }
- var sql = util.format('CREATE %s INDEX "%s" ON "%s" (%s)', (unique ? 'UNIQUE' : ''),
- indexName, tableName, this.quoteArr(columns).join(', '));
-
- this.runSql(sql, callback);
- },
-
- insert: function(tableName, columnNameArray, valueArray, callback) {
- if (columnNameArray.length !== valueArray.length) {
- return callback(new Error('The number of columns does not match the number of values.'));
- }
-
- var sql = util.format('INSERT INTO "%s" ', tableName);
- var columnNames = '(';
- var values = 'VALUES (';
-
- for (var index in columnNameArray) {
- columnNames += columnNameArray[index];
-
- if (typeof(valueArray[index]) === 'string') {
- values += "'" + this.escape(valueArray[index]) + "'";
- } else {
- values += valueArray[index];
- }
-
- if (index != columnNameArray.length - 1) {
- columnNames += ",";
- values += ",";
- }
- }
-
- sql += columnNames + ') '+ values + ');';
- this.runSql(sql, callback);
- },
-
- removeIndex: function(tableName, indexName, callback) {
- if (arguments.length === 2 && typeof(indexName) === 'function') {
- callback = indexName;
- indexName = tableName;
- } else if (arguments.length === 1 && typeof(tableName) === 'string') {
- indexName = tableName;
- }
-
- var sql = util.format('DROP INDEX "%s"', indexName);
- this.runSql(sql, callback);
- },
-
- addForeignKey: function() {
- throw new Error('not implemented');
- },
-
- removeForeignKey: function() {
- throw new Error('not implemented');
- },
-
- normalizeColumnSpec: function(obj) {
- if (typeof(obj) === 'string') {
- return { type: obj };
- } else {
- return obj;
- }
- },
-
- addMigrationRecord: function (name, callback) {
- this.runSql('INSERT INTO "' + internals.migrationTable + '" (name, run_on) VALUES (?, ?)', [name, new Date()], callback);
- },
-
- startMigration: function(cb){ return Promise.resolve().nodeify(cb); },
- endMigration: function(cb){ return Promise.resolve().nodeify(cb); },
- // sql, params, callback
- // sql, callback
- runSql: function() {
- throw new Error('not implemented');
- },
-
- /**
- * Queries the migrations table
- *
- * @param callback
- */
- allLoadedMigrations: function(callback) {
- var sql = 'SELECT * FROM "' + internals.migrationTable + '" ORDER BY run_on DESC, name DESC';
- return this.all(sql, callback);
- },
-
- /**
- * Deletes a migration
- *
- * @param migrationName - The name of the migration to be deleted
- */
- deleteMigration: function(migrationName, callback) {
- var sql = 'DELETE FROM "' + internals.migrationTable + '" WHERE name = ?';
- this.runSql(sql, [migrationName], callback);
- },
-
- all: function(sql, params, callback) {
- throw new Error('not implemented');
- },
-
- escape: function(str) {
- return str.replace(/'/g, "''");
- }
-});
diff --git a/lib/driver/index.js b/lib/driver/index.js
index bdf3c331..d927a51b 100644
--- a/lib/driver/index.js
+++ b/lib/driver/index.js
@@ -1,12 +1,20 @@
var internals = {};
-internals.mod = internals.mod || {};
-internals.mod.log = require('../log');
-internals.mod.type = require('../data_type');
-internals.mod.Class = require('../class');
+internals.mod = {};
+internals.mod.log = require('db-migrate-shared').log;
+internals.mod.type = require('db-migrate-shared').dataType;
var Shadow = require('./shadow');
var log = internals.mod.log;
-
+var Promise = require('bluebird');
+var SeederInterface = require('../interface/seederInterface.js');
+var MigratorInterface = require('../interface/migratorInterface.js');
+var resolve = require('resolve');
+
+var ShadowProto = {
+ createTable: function () {
+ return Promise.resolve();
+ }
+};
exports.connect = function (config, intern, callback) {
var driver, req;
@@ -15,54 +23,114 @@ exports.connect = function (config, intern, callback) {
internals = intern;
internals.mod = mod;
+ // add interface extensions to allow drivers to add new methods
+ internals.interfaces = {
+ SeederInterface: SeederInterface.extending,
+ MigratorInterface: MigratorInterface.extending
+ };
+
+ if (!config.user && config.username) {
+ config.user = config.username;
+ }
+
if (config.driver === undefined) {
throw new Error(
- 'config must include a driver key specifing which driver to use');
+ 'config must include a driver key specifing which driver to use'
+ );
}
- if (config.driver && typeof (config.driver) === 'object') {
-
+ if (config.driver && typeof config.driver === 'object') {
log.verbose('require:', config.driver.require);
driver = require(config.driver.require);
+ } else {
+ switch (config.driver) {
+ case 'sqlite':
+ config.driver = 'sqlite3';
+ break;
+
+ case 'postgres':
+ case 'postgresql':
+ config.driver = 'pg';
+ break;
+ }
- }
- else {
try {
-
req = 'db-migrate-' + config.driver;
log.verbose('require:', req);
-
try {
-
- driver = require(req);
+ driver = require(resolve.sync(req, { basedir: process.cwd() }));
+ } catch (e1) {
+ try {
+ driver = require(req);
+ } catch (e2) {
+ driver = require('../../../' + req);
+ }
}
- catch (Exception) {
-
- driver = require('../../../' + req);
+ } catch (e3) {
+ try {
+ // Fallback to internal drivers, while moving drivers to new repos
+ req = './' + config.driver;
+ log.verbose('require:', req);
+ driver = require(req);
+ } catch (e4) {
+ return callback(
+ new Error(
+ 'No such driver found, please try to install it via ' +
+ 'npm install db-migrate-' +
+ config.driver +
+ ' or ' +
+ 'npm install -g db-migrate-' +
+ config.driver
+ )
+ );
}
}
- catch (Exception) {
-
- //Fallback to internal drivers, while moving drivers to new repos
- req = './' + config.driver;
- log.verbose('require:', req);
- driver = require(req);
- }
}
log.verbose('connecting');
- driver.connect(config, intern, function (err, db) {
- if (err) {
+ var connect = function (config) {
+ driver.connect(config, intern, function (err, db) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ log.verbose('connected');
- callback(err);
- return;
+ if (!global.immunity) {
+ db = Shadow.infect(db, internals, ShadowProto);
+ }
+
+ callback(null, db);
+ });
+ };
+
+ if (config.tunnel) {
+ var tunnel = require('tunnel-ssh');
+ var tunnelConfig = JSON.parse(JSON.stringify(config.tunnel));
+ tunnelConfig.dstHost = config.host;
+ tunnelConfig.dstPort = config.port;
+
+ if (tunnelConfig.privateKeyPath) {
+ tunnelConfig.privateKey = require('fs').readFileSync(
+ tunnelConfig.privateKeyPath
+ );
}
- log.verbose('connected');
- if(!global.immunity)
- db = Shadow.infect(db);
+ // Reassign the db host/port to point to our local ssh tunnel
+ config.host = '127.0.0.1';
+ config.port = tunnelConfig.localPort;
- callback(null, db);
- });
-};
\ No newline at end of file
+ tunnel(tunnelConfig, function (err) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ log.verbose('SSH tunnel connected on port ', tunnelConfig.localPort);
+
+ connect(config);
+ });
+ } else {
+ connect(config);
+ }
+};
diff --git a/lib/driver/mongodb.js b/lib/driver/mongodb.js
deleted file mode 100644
index a846e768..00000000
--- a/lib/driver/mongodb.js
+++ /dev/null
@@ -1,410 +0,0 @@
-var util = require('util');
-var moment = require('moment');
-var MongoClient = require('mongodb').MongoClient;
-var Server = require('mongodb').Server;
-var Base = require('./base');
-var type = require('../data_type');
-var log = require('../log');
-
-var connectionString, internals = {};
-
-var MongodbDriver = Base.extend({
-
- init: function(connection, mongoString) {
- this._super(internals);
- this.connection = connection;
- connectionString = mongoString;
- },
-
- /**
- * Creates the migrations collection
- *
- * @param callback
- */
- _createMigrationsCollection: function(callback) {
- this._run('createCollection', internals.migrationTable, null, callback);
- },
-
-
- /**
- * Creates the seeder collection
- *
- * @param callback
- */
- _createSeedsCollection: function(callback) {
- this._run('createCollection', internals.seedsTable, null, callback);
- },
-
- /**
- * An alias for _createMigrationsCollection
- */
- createMigrationsTable: function(callback) {
- this._createMigrationsCollection(callback);
- },
-
- /**
- * An alias for _createSeederCollection
- */
- createSeedsTable: function(callback) {
- this._createMigrationsCollection(callback);
- },
-
- /**
- * Creates a collection
- *
- * @param collectionName - The name of the collection to be created
- * @param callback
- */
- createCollection: function(collectionName, callback) {
- this._run('createCollection', collectionName, null, callback);
- },
-
- switchDatabase: function(options, callback) {
-
- if(typeof(options) === 'object')
- {
- if(typeof(options.database) === 'string')
- this._run('use', options, null, callback);
- }
- else if(typeof(options) === 'string')
- {
- this._run('use', options, null, callback);
- }
- else
- callback(null);
- },
-
- createDatabase: function(dbName, options, callback) {
- //Don't care at all, MongoDB auto creates databases
- if(typeof(options) === 'function')
- callback = options;
-
- callback(null);
- },
-
- dropDatabase: function(dbName, options, callback) {
-
- if(typeof(options) === 'function')
- callback = options;
-
- this._run('dropDatabase', dbName, null, callback);
- },
-
- /**
- * An alias for createCollection
- *
- * @param collectionName - The name of the collection to be created
- * @param callback
- */
- createTable: function(collectionName, callback) {
- this.createCollection(collectionName, callback);
- },
-
- /**
- * Drops a collection
- *
- * @param collectionName - The name of the collection to be dropped
- * @param callback
- */
- dropCollection: function(collectionName, callback) {
- this._run('dropCollection', collectionName, null, callback);
- },
-
- /**
- * An alias for dropCollection
- *
- * @param collectionName - The name of the collection to be dropped
- * @param callback
- */
- dropTable: function(collectionName, callback) {
- this.dropCollection(collectionName, callback);
- },
-
- /**
- * Renames a collection
- *
- * @param collectionName - The name of the existing collection to be renamed
- * @param newCollectionName - The new name of the collection
- * @param callback
- */
- renameCollection: function(collectionName, newCollectionName, callback) {
- this._run('renameCollection', collectionName, {newCollection: newCollectionName}, callback);
- },
-
- /**
- * An alias for renameCollection
- *
- * @param collectionName - The name of the existing collection to be renamed
- * @param newCollectionName - The new name of the collection
- * @param callback
- */
- renameTable: function(collectionName, newCollectionName, callback) {
- this.renameCollection(collectionName, newCollectionName, callback);
- },
-
- /**
- * Adds an index to a collection
- *
- * @param collectionName - The collection to add the index to
- * @param indexName - The name of the index to add
- * @param columns - The columns to add an index on
- * @param unique - A boolean whether this creates a unique index
- */
- addIndex: function(collectionName, indexName, columns, unique, callback) {
-
- var options = {
- indexName: indexName,
- columns: columns,
- unique: unique
- };
-
- this._run('createIndex', collectionName, options, callback);
- },
-
- /**
- * Removes an index from a collection
- *
- * @param collectionName - The collection to remove the index
- * @param indexName - The name of the index to remove
- * @param columns
- */
- removeIndex: function(collectionName, indexName, callback) {
- this._run('dropIndex', collectionName, {indexName: indexName}, callback);
- },
-
- /**
- * Inserts a record(s) into a collection
- *
- * @param collectionName - The collection to insert into
- * @param toInsert - The record(s) to insert
- * @param callback
- */
- insert: function(collectionName, toInsert, callback) {
- this._run('insert', collectionName, toInsert, callback);
- },
-
- /**
- * Inserts a migration record into the migration collection
- *
- * @param name - The name of the migration being run
- * @param callback
- */
- addMigrationRecord: function (name, callback) {
- this._run('insert', internals.migrationTable, {name: name, run_on: new Date}, callback);
- },
-
- /**
- * Runs a query
- *
- * @param collectionName - The collection to query on
- * @param query - The query to run
- * @param callback
- */
- _find: function(collectionName, query, callback) {
- this._run('find', collectionName, query, callback);
- },
-
- /**
- * Gets all the collection names in mongo
- *
- * @param callback - The callback to call with the collection names
- */
- _getCollectionNames: function(callback) {
- this._run('collections', null, null, callback);
- },
-
- /**
- * Gets all the indexes for a specific collection
- *
- * @param collectionName - The name of the collection to get the indexes for
- * @param callback - The callback to call with the collection names
- */
- _getIndexes: function(collectionName, callback) {
- this._run('indexInformation', collectionName, null, callback);
- },
-
- /**
- * Gets a connection and runs a mongo command and returns the results
- *
- * @param command - The command to run against mongo
- * @param collection - The collection to run the command on
- * @param options - An object of options to be used based on the command
- * @arapm callback - A callback to return the results
- */
- _run: function(command, collection, options, callback) {
-
- var args = this._makeParamArgs(arguments),
- sort = null,
- callback = args[2];
-
- log.sql.apply(null, arguments);
-
- if(options && typeof(options) === 'object') {
-
- if(options.sort)
- sort = options.sort;
- }
-
- if(internals.dryRun) {
- return callback();
- }
-
- // Get a connection to mongo
- this.connection.connect(connectionString, function(err, db) {
-
- if(err) {
- return callback(err);
- }
-
- // Callback function to return mongo records
- var callbackFunction = function(err, data) {
-
- if(err) {
- return callback(err);
- }
-
- callback(null, data);
- db.close();
- };
-
- // Depending on the command, we need to use different mongo methods
- switch(command) {
- case 'find':
-
- if(sort) {
- db.collection(collection)[command](options.query).sort(sort).toArray(callbackFunction);
- }
- else {
- db.collection(collection)[command](options).toArray(callbackFunction);
- }
- break;
- case 'renameCollection':
- db[command](collection, options.newCollection, callbackFunction);
- break;
- case 'createIndex':
- db[command](collection, options.columns, {name: options.indexName, unique: options.unique}, callbackFunction);
- break;
- case 'dropIndex':
- db.collection(collection)[command](options.indexName, callbackFunction);
- break;
- case 'insert':
- // options is the records to insert in this case
- if(util.isArray(options))
- db.collection(collection).insertMany(options, {}, callbackFunction);
- else
- db.collection(collection).insertOne(options, {}, callbackFunction);
- break;
- case 'remove':
- // options is the records to insert in this case
- if(util.isArray(options))
- db.collection(collection).deleteMany(options, callbackFunction);
- else
- db.collection(collection).deleteOne(options, callbackFunction);
- break;
- case 'collections':
- db.collections(callbackFunction);
- break;
- case 'indexInformation':
- db.indexInformation(collection, callbackFunction);
- break;
- case 'dropDatabase':
- db.dropDatabase(callbackFunction);
- break;
- case 'use':
- db.db(collection, callbackFunction);
- break;
- default:
- db[command](collection, callbackFunction);
- break;
- }
- });
- },
-
- _makeParamArgs: function(args) {
- var params = Array.prototype.slice.call(args);
- var sql = params.shift();
- var callback = params.pop();
-
- if (params.length > 0 && Array.isArray(params[0])) {
- params = params[0];
- }
-
- return [sql, params, callback];
- },
-
- /**
- * Runs a NoSQL command regardless of the dry-run param
- */
- _all: function() {
- var args = this._makeParamArgs(arguments);
- return this.connection.query.apply(this.connection, args);
- },
-
- /**
- * Queries the migrations collection
- *
- * @param callback
- */
- allLoadedMigrations: function(callback) {
- this._run('find', internals.migrationTable, { sort: { run_on: -1 } }, callback);
- },
-
- /**
- * Deletes a migration
- *
- * @param migrationName - The name of the migration to be deleted
- * @param callback
- */
- deleteMigration: function(migrationName, callback) {
- this._run('remove', internals.migrationTable, {name: migrationName}, callback);
- },
-
- /**
- * Closes the connection to mongodb
- */
- close: function(callback) {
- this.connection.close(callback);
- }
-});
-
-/**
- * Gets a connection to mongo
- *
- * @param config - The config to connect to mongo
- * @param callback - The callback to call with a MongodbDriver object
- */
-exports.connect = function(config, intern, callback) {
- var db;
- var port;
- var host;
-
- internals = intern;
-
- // Make sure the database is defined
- if(config.database === undefined) {
- throw new Error('database must be defined in database.json');
- }
-
- if(config.host === undefined) {
- host = 'localhost';
- } else {
- host = config.host;
- }
-
- if(config.port === undefined) {
- port = 27017;
- } else {
- port = config.port;
- }
-
- var mongoString = 'mongodb://';
-
- if(config.user !== undefined && config.password !== undefined) {
- mongoString += config.user + ':' + config.password + '@';
- }
-
- mongoString += host + ':' + port + '/' + config.database;
-
- db = config.db || new MongoClient(new Server(host, port));
- callback(null, new MongodbDriver(db, mongoString));
-};
diff --git a/lib/driver/mysql.js b/lib/driver/mysql.js
deleted file mode 100644
index 43c89b93..00000000
--- a/lib/driver/mysql.js
+++ /dev/null
@@ -1,495 +0,0 @@
-var util = require('util');
-var moment = require('moment');
-var mysql = require('mysql');
-var Base = require('./base');
-var type = require('../data_type');
-var log = require('../log');
-var Promise = require('bluebird');
-
-var internals = {};
-
-var MysqlDriver = Base.extend({
- init: function(connection) {
- this._super(internals);
- this.connection = connection;
- },
-
- startMigration: function(cb){
-
- var self = this;
-
- if(!internals.notansactions) {
-
- return this.runSql('SET AUTOCOMMIT=0;')
- .then(function() {
- return self.runSql('START TRANSACTION;');
- })
- .nodeify(cb);
- }
- else
- return Promise.resolve().nodeify(cb);
- },
-
- endMigration: function(cb){
-
- if(!internals.notransactions) {
-
- return this.runSql('COMMIT;').nodeify(cb);
- }
- else
- return Promise.resolve(null).nodeify(cb);
- },
-
- mapDataType: function(spec) {
- var len;
- switch(spec.type) {
- case type.TEXT:
- len = parseInt(spec.length, 10) || 1000;
- if(len > 16777216) {
- return 'LONGTEXT';
- }
- if(len > 65536) {
- return 'MEDIUMTEXT';
- }
- if(len > 256) {
- return 'TEXT';
- }
- return 'TINYTEXT';
- case type.DATE_TIME:
- return 'DATETIME';
- case type.BLOB:
- len = parseInt(spec.length, 10) || 1000;
- if(len > 16777216) {
- return 'LONGBLOB';
- }
- if(len > 65536) {
- return 'MEDIUMBLOB';
- }
- if(len > 256) {
- return 'BLOB';
- }
- return 'TINYBLOB';
- case type.BOOLEAN:
- return 'TINYINT(1)';
- }
- return this._super(spec.type);
- },
-
- createColumnDef: function(name, spec, options, tableName) {
- var escapedName = util.format('`%s`', name),
- t = this.mapDataType(spec),
- len;
- if(spec.type !== type.TEXT && spec.type !== type.BLOB) {
- len = spec.length ? util.format('(%s)', spec.length) : '';
- if (t === 'VARCHAR' && len === '') {
- len = '(255)';
- }
- }
- var constraint = this.createColumnConstraint(spec, options, tableName, name);
- return { foreignKey: constraint.foreignKey,
- constraints: [escapedName, t, len, constraint.constraints].join(' ') };
- },
-
- createColumnConstraint: function(spec, options, tableName, columnName) {
- var constraint = [];
- var cb;
-
- if (spec.unsigned) {
- constraint.push('UNSIGNED');
- }
-
- if (spec.primaryKey) {
- if (!options || options.emitPrimaryKey) {
- constraint.push('PRIMARY KEY');
- }
- }
-
- if(spec.primaryKey || spec.unique) {
- if (spec.autoIncrement) {
- constraint.push('AUTO_INCREMENT');
- }
- }
-
- if (spec.notNull === true) {
- constraint.push('NOT NULL');
- }
-
- if (spec.unique) {
- constraint.push('UNIQUE');
- }
-
- if (spec.engine && typeof(spec.engine) === 'string') {
- constraint.push('ENGINE=\'' + spec.engine + '\'')
- }
-
- if (spec.rowFormat && typeof(spec.rowFormat) === 'string') {
- constraint.push('ROW_FORMAT=\'' + spec.rowFormat + '\'')
- }
-
- if (spec.null || spec.notNull === false) {
- constraint.push('NULL');
- }
-
- if (spec.defaultValue !== undefined) {
- constraint.push('DEFAULT');
-
- if (typeof spec.defaultValue === 'string'){
- constraint.push("'" + spec.defaultValue + "'");
- } else {
- constraint.push(spec.defaultValue);
- }
- }
-
- if (spec.foreignKey) {
-
- cb = this.bindForeignKey(tableName, columnName, spec.foreignKey);
- }
-
- return { foreignKey: cb, constraints: constraint.join(' ') };
- },
-
- createTable: function(tableName, options, callback) {
- log.verbose('creating table:', tableName);
- var columnSpecs = options,
- tableOptions = {};
-
- if (options.columns !== undefined) {
- columnSpecs = options.columns;
- delete options.columns;
- tableOptions = options;
- }
-
- var ifNotExistsSql = "";
- if(tableOptions.ifNotExists) {
- ifNotExistsSql = "IF NOT EXISTS";
- }
-
- var primaryKeyColumns = [];
- var columnDefOptions = {
- emitPrimaryKey: false
- };
-
- for (var columnName in columnSpecs) {
- var columnSpec = this.normalizeColumnSpec(columnSpecs[columnName]);
- columnSpecs[columnName] = columnSpec;
- if (columnSpec.primaryKey) {
- primaryKeyColumns.push(columnName);
- }
- }
-
- var pkSql = '';
- if (primaryKeyColumns.length > 1) {
- pkSql = util.format(', PRIMARY KEY (`%s`)', primaryKeyColumns.join('`, `'));
- } else {
- columnDefOptions.emitPrimaryKey = true;
- }
-
- var columnDefs = [];
- var foreignKeys = [];
-
- for (var columnName in columnSpecs) {
- var columnSpec = columnSpecs[columnName];
- var constraint = this.createColumnDef(columnName, columnSpec, columnDefOptions, tableName);
-
- columnDefs.push(constraint.constraints);
- if (constraint.foreignKey)
- foreignKeys.push(constraint.foreignKey);
- }
-
- var sql = util.format('CREATE TABLE %s `%s` (%s%s)', ifNotExistsSql, tableName, columnDefs.join(', '), pkSql);
-
- this.runSql(sql, function()
- {
-
- this.recurseCallbackArray(foreignKeys, callback);
- }.bind(this));
- },
-
- renameTable: function(tableName, newTableName, callback) {
- var sql = util.format('RENAME TABLE `%s` TO `%s`', tableName, newTableName);
- this.runSql(sql, callback);
- },
-
- addColumn: function(tableName, columnName, columnSpec, callback) {
- var def = this.createColumnDef(columnName, this.normalizeColumnSpec(columnSpec), tableName);
- var sql = util.format('ALTER TABLE `%s` ADD COLUMN %s', tableName, def.constraints);
- this.runSql(sql, function()
- {
- if(def.foreignKey)
- def.foreignKey(callback);
- else
- callback();
- });
- },
-
- createDatabase: function(dbName, options, callback) {
-
- var spec = '',
- ifNotExists = '';
-
- if(typeof(options) === 'function')
- callback = options;
- else
- {
- ifNotExists = (options.ifNotExists === true) ? 'IF NOT EXISTS' : '';
- }
-
- this.runSql(util.format('CREATE DATABASE %s `%s` %s', ifNotExists, dbName, spec), callback);
- },
-
- switchDatabase: function(options, callback) {
-
- if(typeof(options) === 'object')
- {
- if(typeof(options.database) === 'string')
- this.runSql(util.format('USE `%s`', options.database), callback);
- }
- else if(typeof(options) === 'string')
- {
- this.runSql(util.format('USE `%s`', options), callback);
- }
- else
- callback(null);
- },
-
- dropDatabase: function(dbName, options, callback) {
-
- var ifExists = '';
-
- if(typeof(options) === 'function')
- callback = options;
- else
- {
- ifExists = (options.ifExists === true) ? 'IF EXISTS' : '';
- }
-
- this.runSql(util.format('DROP DATABASE %s `%s`', ifExists, dbName), callback);
- },
-
- removeColumn: function(tableName, columnName, callback) {
- var sql = util.format('ALTER TABLE `%s` DROP COLUMN `%s`', tableName, columnName);
- this.runSql(sql, callback);
- },
-
- addIndex: function(tableName, indexName, columns, unique, callback) {
- if (typeof(unique) === 'function') {
- callback = unique;
- unique = false;
- }
-
- if (!Array.isArray(columns)) {
- columns = [columns];
- }
- var sql = util.format('ALTER TABLE `%s` ADD %s INDEX `%s` (`%s`)', tableName, (unique ? 'UNIQUE ' : ''), indexName, columns.join('`, `'));
- this.runSql(sql, callback);
- },
-
- insert: function(tableName, columnNameArray, valueArray, callback) {
- if (columnNameArray.length !== valueArray.length) {
- return callback(new Error('The number of columns does not match the number of values.'));
- }
-
- var sql = util.format('INSERT INTO `%s` ', tableName);
- var columnNames = '(';
- var values = 'VALUES (';
-
- for (var index in columnNameArray) {
- columnNames += '`' + columnNameArray[index] + '`';
-
- if (typeof(valueArray[index]) === 'string') {
- values += "'" + this.escape(valueArray[index]) + "'";
- } else {
- values += valueArray[index];
- }
-
- if (index != columnNameArray.length - 1) {
- columnNames += ",";
- values += ",";
- }
- }
-
- sql += columnNames + ') '+ values + ');';
- this.runSql(sql, callback);
- },
-
- removeIndex: function(tableName, indexName, callback) {
- // tableName is optional for other drivers, but required for mySql. So, check the args to ensure they are valid
- if (arguments.length === 2 && typeof(indexName) === 'function') {
- callback = indexName;
- process.nextTick(function () {
- callback(new Error('Illegal arguments, must provide "tableName" and "indexName"'));
- });
-
- return;
- }
-
- var sql = util.format('DROP INDEX `%s` ON `%s`', indexName, tableName);
- this.runSql(sql, callback);
- },
-
- dropTable: function(tableName, options, callback) {
- if (arguments.length < 3) {
- callback = options;
- options = {};
- }
-
- var ifExistsSql = '';
- if (options.ifExists) {
- ifExistsSql = 'IF EXISTS';
- }
- var sql = util.format('DROP TABLE %s `%s`', ifExistsSql, tableName);
- this.runSql(sql, callback);
- },
-
- renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
- var self = this, columnTypeSql = util.format("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '%s' AND COLUMN_NAME = '%s'", tableName, oldColumnName);
-
- this.all(columnTypeSql, function(err, result) {
- var columnType = result[0].COLUMN_TYPE;
- var alterSql = util.format("ALTER TABLE `%s` CHANGE `%s` `%s` %s", tableName, oldColumnName, newColumnName, columnType);
- self.runSql(alterSql, callback);
- });
- },
-
- changeColumn: function(tableName, columnName, columnSpec, callback) {
- var constraint = this.createColumnDef(columnName, columnSpec);
- var sql = util.format('ALTER TABLE `%s` CHANGE COLUMN `%s` %s', tableName, columnName, constraint.constraints);
-
- var exec = function() {
-
- this.runSql(sql, function()
- {
- if(constraint.foreignKey)
- constraint.foreignKey(callback);
- else
- callback();
- });
- }.bind(this);
-
- if(columnSpec.unique === false)
- this.removeIndex(tableName, columnName, exec);
- else
- exec();
- },
-
- addMigrationRecord: function (name, callback) {
- var formattedDate = moment(new Date()).format('YYYY-MM-DD HH:mm:ss');
- this.runSql('INSERT INTO `' + internals.migrationTable + '` (`name`, `run_on`) VALUES (?, ?)', [name, formattedDate], callback);
- },
-
- addForeignKey: function(tableName, referencedTableName, keyName, fieldMapping, rules, callback) {
- if(arguments.length === 5 && typeof(rules) === 'function') {
- callback = rules;
- rules = {};
- }
- var columns = Object.keys(fieldMapping);
- var referencedColumns = columns.map(function (key) { return fieldMapping[key]; });
- var sql = util.format('ALTER TABLE `%s` ADD CONSTRAINT `%s` FOREIGN KEY (%s) REFERENCES `%s` (%s) ON DELETE %s ON UPDATE %s',
- tableName, keyName, this.tableQuoteArr( columns ), referencedTableName,
- this.tableQuoteArr( referencedColumns ), rules.onDelete || 'NO ACTION', rules.onUpdate || 'NO ACTION');
- this.runSql(sql, callback);
- },
-
- removeForeignKey: function(tableName, keyName, options, callback) {
- var sql = util.format('ALTER TABLE `%s` DROP FOREIGN KEY `%s`', tableName, keyName);
- this.runSql(sql, function () {
-
- if( typeof(options) === 'function' ) {
-
- options();
- }
- else if(options.dropIndex === true) {
-
- sql = util.format('ALTER TABLE `%s` DROP INDEX `%s`', tableName, keyName);
- this.runSql(sql, function () {
- callback();
- });
- }
- else
- callback();
-
- }.bind(this));
- },
-
- tableQuoteArr: function(arr) {
-
- for(var i = 0; i < arr.length; ++i)
- arr[i] = '`' + arr[i] + '`';
-
- return arr;
- },
-
- runSql: function() {
-
- var self = this;
- var args = this._makeParamArgs(arguments);
- var callback = args.pop();
- log.sql.apply(null, arguments);
- if(internals.dryRun) {
- return callback();
- }
-
- return new Promise(function(resolve, reject) {
- args.push(function(err, data) {
- return (err ? reject(err) : resolve(data));
- });
-
- self.connection.query.apply(self.connection, args);
- }).nodeify(callback);
- },
-
- _makeParamArgs: function(args) {
- var params = Array.prototype.slice.call(args);
- var sql = params.shift();
- var callback = params.pop();
-
- if (params.length > 0 && Array.isArray(params[0])) {
- params = params[0];
- }
- return [sql, params, callback];
- },
-
- all: function() {
- var args = this._makeParamArgs(arguments);
- return this.connection.query.apply(this.connection, args);
- },
-
- /**
- * Queries the migrations table
- *
- * @param callback
- */
- allLoadedMigrations: function(callback) {
- var sql = 'SELECT * FROM `' + internals.migrationTable + '` ORDER BY run_on DESC, name DESC';
- this.all(sql, callback);
- },
-
- /**
- * Deletes a migration
- *
- * @param migrationName - The name of the migration to be deleted
- * @param callback
- */
- deleteMigration: function(migrationName, callback) {
- var sql = 'DELETE FROM `' + internals.migrationTable + '` WHERE name = ?';
- this.runSql(sql, [migrationName], callback);
- },
-
- close: function(callback) {
- this.connection.end(callback);
- }
-
-});
-
-exports.connect = function(config, intern, callback) {
- var db;
-
- internals = intern;
-
- if (typeof(mysql.createConnection) === 'undefined') {
- db = config.db || new mysql.createClient(config);
- } else {
- db = config.db || new mysql.createConnection(config);
- }
- callback(null, new MysqlDriver(db));
-};
diff --git a/lib/driver/pg.js b/lib/driver/pg.js
deleted file mode 100644
index bca44e0e..00000000
--- a/lib/driver/pg.js
+++ /dev/null
@@ -1,373 +0,0 @@
-var util = require('util');
-var pg = require('pg');
-var semver = require('semver');
-var Base = require('./base');
-var type = require('../data_type');
-var log = require('../log');
-var Promise = require('bluebird');
-
-var internals = {};
-
-var PgDriver = Base.extend({
- init: function(connection, schema) {
- this._super(internals);
- this.connection = connection;
- this.schema = schema || "public";
- this.connection.connect();
- },
-
- startMigration: function(cb){
-
- if(!internals.notansactions) {
-
- return Promise.promisify(this.runSql.bind(this))('BEGIN;').nodeify(cb);
- }
- else
- return Promise.resolve().nodeify(cb);
- },
-
- endMigration: function(cb){
-
- if(!internals.notransactions) {
-
- return Promise.promisify(this.runSql.bind(this))('COMMIT;').nodeify(cb);
- }
- else
- return Promise.resolve(null).nodeify(cb);
- },
-
- createColumnDef: function(name, spec, options, tableName) {
- var type = spec.autoIncrement ? '' : this.mapDataType(spec.type);
- var len = spec.length ? util.format('(%s)', spec.length) : '';
- var constraint = this.createColumnConstraint(spec, options, tableName, name);
- if (name.charAt(0) != '"') {
- name = '"' + name + '"';
- }
-
- return { foreignKey: constraint.foreignKey,
- constraints: [name, type, len, constraint.constraints].join(' ') };
- },
-
- mapDataType: function(str) {
- switch(str) {
- case type.STRING:
- return 'VARCHAR';
- case type.DATE_TIME:
- return 'TIMESTAMP';
- case type.BLOB:
- return 'BYTEA';
- }
- return this._super(str);
- },
-
- createDatabase: function(dbName, options, callback) {
-
- var spec = '';
-
- if(typeof(options) === 'function')
- callback = options;
-
- this.runSql(util.format('CREATE DATABASE `%s` %s', dbName, spec), callback);
- },
-
- dropDatabase: function(dbName, options, callback) {
-
- var ifExists = '';
-
- if(typeof(options) === 'function')
- callback = options;
- else
- {
- ifExists = (options.ifExists === true) ? 'IF EXISTS' : '';
- }
-
- this.runSql(util.format('DROP DATABASE %s `%s`', ifExists, dbName), callback);
- },
-
- createSequence: function(sqName, options, callback) {
-
- var spec = '',
- temp = '';
-
- if(typeof(options) === 'function')
- callback = options;
- else
- {
- temp = (options.temp === true) ? 'TEMP' : '';
- }
-
- this.runSql(util.format('CREATE %s SEQUENCE `%s` %s', temp, sqName, spec), callback);
- },
-
- switchDatabase: function(options, callback) {
-
- if(typeof(options) === 'object')
- {
- if(typeof(options.database) === 'string')
- {
- log.info('Ignore database option, not available with postgres. Use schema instead!');
- this.runSql(util.format('SET search_path TO `%s`', options.database), callback);
- }
- }
- else if(typeof(options) === 'string')
- {
- this.runSql(util.format('SET search_path TO `%s`', options), callback);
- }
- else
- callback(null);
- },
-
- dropSequence: function(dbName, options, callback) {
-
- var ifExists = '',
- rule = '';
-
- if(typeof(options) === 'function')
- callback = options;
- else
- {
- ifExists = (options.ifExists === true) ? 'IF EXISTS' : '';
-
- if(options.cascade === true)
- rule = 'CASCADE';
- else if(options.restrict === true)
- rule = 'RESTRICT';
- }
-
- this.runSql(util.format('DROP SEQUENCE %s `%s` %s', ifExists, dbName, rule), callback);
- },
-
- createMigrationsTable: function(callback) {
- var options = {
- columns: {
- 'id': { type: type.INTEGER, notNull: true, primaryKey: true, autoIncrement: true },
- 'name': { type: type.STRING, length: 255, notNull: true},
- 'run_on': { type: type.DATE_TIME, notNull: true}
- },
- ifNotExists: false
- };
-
- this.runSql('select version() as version', function(err, result) {
- if (err) {
- return callback(err);
- }
-
- if (result && result.rows && result.rows.length > 0 && result.rows[0].version) {
- var version = result.rows[0].version;
- var match = version.match(/\d+\.\d+\.\d+/);
- if (match && match[0] && semver.gte(match[0], '9.1.0')) {
- options.ifNotExists = true;
- }
- }
-
- // Get the current search path so we can change the current schema
- // if necessary
- this.runSql("SHOW search_path", function(err, result) {
- if (err) {
- return callback(err);
- }
-
- var searchPath;
-
- // if the user specified a different schema, prepend it to the
- // search path. This will make all DDL/DML/SQL operate on the specified
- // schema.
- if (this.schema === 'public') {
- searchPath = result.rows[0].search_path;
- } else {
- searchPath = this.schema + ',' + result.rows[0].search_path;
- }
-
- this.runSql('SET search_path TO ' + searchPath, function() {
- this.runSql("SELECT table_name FROM information_schema.tables WHERE table_name = '" + internals.migrationTable + "'", function(err, result) {
- if (err) {
- return callback(err);
- }
-
- if (result && result.rows && result.rows.length < 1) {
- this.createTable(internals.migrationTable, options, callback);
- } else {
- callback();
- }
- }.bind(this));
- }.bind(this));
- }.bind(this));
- }.bind(this));
- },
-
- createColumnConstraint: function(spec, options, tableName, columnName) {
- var constraint = [],
- cb;
-
- if (spec.primaryKey && options.emitPrimaryKey) {
- if (spec.autoIncrement) {
- constraint.push('SERIAL');
- }
- constraint.push('PRIMARY KEY');
- }
-
- if (spec.notNull === true) {
- constraint.push('NOT NULL');
- }
-
- if (spec.unique) {
- constraint.push('UNIQUE');
- }
-
- if (typeof spec.defaultValue != 'undefined') {
- constraint.push('DEFAULT');
- if (typeof spec.defaultValue == 'string'){
- constraint.push("'" + spec.defaultValue + "'");
- } else {
- constraint.push(spec.defaultValue);
- }
- }
-
- if (spec.foreignKey) {
-
- cb = this.bindForeignKey(tableName, columnName, spec.foreignKey);
- }
-
- return { foreignKey: cb, constraints: constraint.join(' ') };
- },
-
- renameTable: function(tableName, newTableName, callback) {
- var sql = util.format('ALTER TABLE "%s" RENAME TO "%s"', tableName, newTableName);
- this.runSql(sql, callback);
- },
-
- removeColumn: function(tableName, columnName, callback) {
- var sql = util.format('ALTER TABLE "%s" DROP COLUMN "%s"', tableName, columnName);
- this.runSql(sql, callback);
- },
-
- renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
- var sql = util.format('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"', tableName, oldColumnName, newColumnName);
- this.runSql(sql, callback);
- },
-
- changeColumn: function(tableName, columnName, columnSpec, callback) {
- setNotNull.call(this);
-
- function setNotNull() {
- var setOrDrop = columnSpec.notNull === true ? 'SET' : 'DROP';
- var sql = util.format('ALTER TABLE "%s" ALTER COLUMN "%s" %s NOT NULL', tableName, columnName, setOrDrop);
- this.runSql(sql, setUnique.bind(this));
- }
-
- function setUnique(err) {
- if (err) {
- callback(err);
- }
-
- var sql;
- var constraintName = tableName + '_' + columnName + '_key';
-
- if (columnSpec.unique === true) {
- sql = util.format('ALTER TABLE "%s" ADD CONSTRAINT "%s" UNIQUE ("%s")', tableName, constraintName, columnName);
- this.runSql(sql, setDefaultValue.bind(this));
- } else if (columnSpec.unique === false) {
- sql = util.format('ALTER TABLE "%s" DROP CONSTRAINT "%s"', tableName, constraintName);
- this.runSql(sql, setDefaultValue.bind(this));
- } else {
- setDefaultValue.call(this);
- }
- }
-
- function setDefaultValue(err) {
- if (err) {
- return callback(err);
- }
-
- var sql;
-
- if (columnSpec.defaultValue !== undefined) {
- var defaultValue = null;
- if (typeof columnSpec.defaultValue == 'string'){
- defaultValue = "'" + columnSpec.defaultValue + "'";
- } else {
- defaultValue = columnSpec.defaultValue;
- }
- sql = util.format('ALTER TABLE "%s" ALTER COLUMN "%s" SET DEFAULT %s', tableName, columnName, defaultValue);
- } else {
- sql = util.format('ALTER TABLE "%s" ALTER COLUMN "%s" DROP DEFAULT', tableName, columnName);
- }
-
- this.runSql(sql, callback);
- }
- },
-
- addForeignKey: function(tableName, referencedTableName, keyName, fieldMapping, rules, callback) {
- if(arguments.length === 5 && typeof(rules) === 'function') {
- callback = rules;
- rules = {};
- }
- var columns = Object.keys(fieldMapping);
- var referencedColumns = columns.map(function (key) { return '"' + fieldMapping[key] + '"'; });
- var sql = util.format('ALTER TABLE "%s" ADD CONSTRAINT "%s" FOREIGN KEY (%s) REFERENCES "%s" (%s) ON DELETE %s ON UPDATE %s',
- tableName, keyName, this.quoteArr(columns), referencedTableName, referencedColumns, rules.onDelete || 'NO ACTION', rules.onUpdate || 'NO ACTION');
- this.runSql(sql, callback);
- },
-
- removeForeignKey: function(tableName, keyName, callback) {
- var sql = util.format('ALTER TABLE "%s" DROP CONSTRAINT "%s"', tableName, keyName);
- this.runSql(sql, callback);
- },
-
- insert: function(tableName, columnNameArray, valueArray, callback) {
- columnNameArray = columnNameArray.map(function(columnName) {
- return (columnName.charAt(0) != '"') ? '"' + columnName + '"' : columnName;
- });
-
- valueArray = valueArray.map(function(value) {
- return 'string' === typeof value ? value : JSON.stringify(value);
- });
-
- return this._super(tableName, columnNameArray, valueArray, callback);
- },
-
- runSql: function() {
- var callback = arguments[arguments.length - 1];
-
- params = arguments;
- if (params.length > 2){
- // We have parameters, but db-migrate uses "?" for param substitutions.
- // PG uses "$1", "$2", etc so fix up the "?" into "$1", etc
- var param = params[0].split('?'),
- new_param = [];
- for (var i = 0; i < param.length-1; i++){
- new_param.push(param[i], "$" + (i+1));
- }
- new_param.push(param[param.length-1]);
- params[0] = new_param.join('');
- }
-
- log.sql.apply(null, params);
- if(internals.dryRun) {
- return callback();
- }
- this.connection.query.apply(this.connection, params);
- },
-
- all: function() {
- params = arguments;
- this.connection.query.apply(this.connection, [params[0], function(err, result){
- params[1](err, result.rows);
- }]);
- },
-
- close: function(callback) {
- this.connection.end();
- callback(null);
- }
-
-});
-
-exports.connect = function(config, intern, callback) {
-
- internals = intern;
-
- if (config.native) { pg = pg.native; }
- var db = config.db || new pg.Client(config);
- callback(null, new PgDriver(db, config.schema));
-};
diff --git a/lib/driver/postgres.js b/lib/driver/postgres.js
deleted file mode 100644
index a0e5ea61..00000000
--- a/lib/driver/postgres.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('./pg');
diff --git a/lib/driver/postgresql.js b/lib/driver/postgresql.js
deleted file mode 100644
index a0e5ea61..00000000
--- a/lib/driver/postgresql.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('./pg');
diff --git a/lib/driver/shadow.js b/lib/driver/shadow.js
index 29c67dc7..6779aa98 100644
--- a/lib/driver/shadow.js
+++ b/lib/driver/shadow.js
@@ -8,42 +8,33 @@
* In our case, it records the execution of methods.
*/
-var internals = {};
-
-function Shadow(db) {
-
- this.db = db;
-}
-
-
-var ShadowProto = {
-
- createTable: function() {},
- addForeignKey: function() {},
- createCollection: function() {}
-
-};
-
-
/**
* 'Infect' the original class
*/
-exports.infect = function(db, intern) {
-
- internals = intern;
-
+exports.infect = function (db, intern, ShadowProto) {
db._shadowsHost = {};
+ db._shadowProto = {};
- for(var prop in db) {
-
- if (typeof ShadowProto[prop] === "function" &&
- typeof db[prop] === "function") {
-
+ for (var prop in db) {
+ if (
+ typeof ShadowProto[prop] === 'function' &&
+ typeof db[prop] === 'function'
+ ) {
db._shadowsHost[prop] = db[prop];
-
- (function(property) {
- db[property] = function() { this._shadowsHost[property].apply(this, arguments); };
- }(prop));
+ db._shadowProto[prop] = ShadowProto[prop];
+
+ (function (property) {
+ db[property] = function () {
+ var params = arguments;
+ var self = this;
+
+ return self._shadowProto[property]
+ .apply(self, params)
+ .then(function () {
+ return self._shadowsHost[property].apply(self, params);
+ });
+ };
+ })(prop);
}
}
diff --git a/lib/driver/sqlite3.js b/lib/driver/sqlite3.js
deleted file mode 100644
index b031954a..00000000
--- a/lib/driver/sqlite3.js
+++ /dev/null
@@ -1,148 +0,0 @@
-var util = require('util');
-var sqlite3 = require('sqlite3').verbose();
-var Base = require('./base');
-var log = require('../log');
-var type = require('../data_type');
-
-var defaultMode = sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
- internals = {};
-
-var Sqlite3Driver = Base.extend({
- init: function(connection) {
- this._super(internals);
- this.connection = connection;
- },
-
- startMigration: function(cb){
-
- if(!internals.notansactions) {
-
- return Promise.promisify(this.runSql.bind(this))('BEGIN TRANSACTION;').nodeify(cb);
- }
- else
- return Promise.resolve().nodeify(cb);
- },
-
- endMigration: function(cb){
-
- if(!internals.notransactions) {
-
- return Promise.promisify(this.runSql.bind(this))('COMMIT;').nodeify(cb);
- }
- else
- return Promise.resolve(null).nodeify(cb);
- },
-
- mapDataType: function(str) {
- switch(str) {
- case type.DATE_TIME:
- return 'datetime';
- case type.TIME:
- return 'time';
- }
- return this._super(str);
- },
-
- switchDatabase: function(options, callback) {
- callback(null);
- },
-
- createColumnDef: function(name, spec, options) {
- name = '"' + name + '"';
- var dType = this.mapDataType(spec.type);
- var len = spec.length ? util.format('(%s)', spec.length) : '';
- var constraint = this.createColumnConstraint(spec, options);
-
- if(spec.type === type.INTEGER)
- len = '';
-
- return { foreignKey: null,
- constraints: [name, dType, len, constraint].join(' ') };
- },
-
- createColumnConstraint: function(spec, options) {
- var constraint = [];
- if (spec.primaryKey && options.emitPrimaryKey) {
- constraint.push('PRIMARY KEY');
- if (spec.autoIncrement) {
- constraint.push('AUTOINCREMENT');
- }
- }
-
- if (spec.notNull === true) {
- constraint.push('NOT NULL');
- }
-
- if (spec.unique) {
- constraint.push('UNIQUE');
- }
-
- if (spec.defaultValue) {
- constraint.push('DEFAULT');
-
- if(typeof(spec.defaultValue) === 'string')
- constraint.push('"' + spec.defaultValue + '"');
- else
- constraint.push(spec.defaultValue);
- }
-
- return constraint.join(' ');
- },
-
- renameTable: function(tableName, newTableName, callback) {
- var sql = util.format('ALTER TABLE %s RENAME TO %s', tableName, newTableName);
- this.runSql(sql, callback);
- },
-
- //removeColumn: function(tableName, columnName, callback) {
- //},
-
- //renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
- //};
-
- //changeColumn: function(tableName, columnName, columnSpec, callback) {
- //},
-
- runSql: function() {
- var callback = arguments[arguments.length - 1];
- log.sql.apply(null, arguments);
- if(internals.dryRun) {
- return callback();
- }
-
- if(typeof(arguments[1]) === 'function')
- this.connection.exec.apply(this.connection, arguments);
- else
- this.connection.run.apply(this.connection, arguments);
- },
-
- all: function() {
- this.connection.all.apply(this.connection, arguments);
- },
-
- close: function(callback) {
- this.connection.close();
- callback(null);
- }
-
-});
-
-exports.connect = function(config, intern, callback) {
- var mode = config.mode || defaultMode;
-
- internals = intern;
-
- if (config.db) {
- callback(null, new Sqlite3Driver(config.db));
- } else {
- if (typeof(config.filename) === 'undefined') {
- console.error('filename is required in database.json');
- return;
- }
- var db = new sqlite3.Database(config.filename, mode);
- db.on("error", callback);
- db.on("open", function() {
- callback(null, new Sqlite3Driver(db));
- });
- }
-};
diff --git a/lib/inflection.js b/lib/inflection.js
deleted file mode 100644
index 6d4a20b4..00000000
--- a/lib/inflection.js
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
-Copyright (c) 2010 Ryan Schuft (ryan.schuft@gmail.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-/*
- This code is based in part on the work done in Ruby to support
- infection as part of Ruby on Rails in the ActiveSupport's Inflector
- and Inflections classes. It was initally ported to Javascript by
- Ryan Schuft (ryan.schuft@gmail.com) in 2007. It has since been modified
- to not alter the String prototype.
-
- Currently implemented functions:
-
- pluralize(str, plural) == String
- renders a singular English language noun into its plural form
- normal results can be overridden by passing in an alternative
-
- singularize(str, singular) == String
- renders a plural English language noun into its singular form
- normal results can be overridden by passing in an alterative
-
- camelize(str, lowFirstLetter) == String
- renders a lower case underscored word into camel case
- the first letter of the result will be upper case unless you pass true
- also translates "/" into "::" (underscore does the opposite)
-
- underscore(str) == String
- renders a camel cased word into words seperated by underscores
- also translates "::" back into "/" (camelize does the opposite)
-
- humanize(str, lowFirstLetter) == String
- renders a lower case and underscored word into human readable form
- defaults to making the first letter capitalized unless you pass true
-
- capitalize(str) == String
- renders all characters to lower case and then makes the first upper
-
- dasherize(str) == String
- renders all underbars and spaces as dashes
-
- titleize(str) == String
- renders words into title casing (as for book titles)
-
- demodulize(str) == String
- renders class names that are prepended by modules into just the class
-
- tableize(str) == String
- renders camel cased singular words into their underscored plural form
-
- classify(str) == String
- renders an underscored plural word into its camel cased singular form
-
- foreignKey(str, dropIdUbar) == String
- renders a class name (camel cased singular noun) into a foreign key
- defaults to seperating the class from the id with an underbar unless
- you pass true
-
- ordinalize(str) == String
- renders all numbers found in the string into their sequence like "22nd"
-*/
-
-/*
- This is a list of nouns that use the same form for both singular and plural.
- This list should remain entirely in lower case to correctly match Strings.
-*/
-var uncountableWords = [
- 'equipment', 'information', 'rice', 'money', 'species', 'series',
- 'fish', 'sheep', 'moose', 'deer', 'news'
-];
-
-/*
- These rules translate from the singular form of a noun to its plural form.
-*/
-var pluralRules = [
- [new RegExp('(m)an$', 'gi'), '$1en'],
- [new RegExp('(pe)rson$', 'gi'), '$1ople'],
- [new RegExp('(child)$', 'gi'), '$1ren'],
- [new RegExp('^(ox)$', 'gi'), '$1en'],
- [new RegExp('(ax|test)is$', 'gi'), '$1es'],
- [new RegExp('(octop|vir)us$', 'gi'), '$1i'],
- [new RegExp('(alias|status)$', 'gi'), '$1es'],
- [new RegExp('(bu)s$', 'gi'), '$1ses'],
- [new RegExp('(buffal|tomat|potat)o$', 'gi'), '$1oes'],
- [new RegExp('([ti])um$', 'gi'), '$1a'],
- [new RegExp('sis$', 'gi'), 'ses'],
- [new RegExp('(?:([^f])fe|([lr])f)$', 'gi'), '$1$2ves'],
- [new RegExp('(hive)$', 'gi'), '$1s'],
- [new RegExp('([^aeiouy]|qu)y$', 'gi'), '$1ies'],
- [new RegExp('(x|ch|ss|sh)$', 'gi'), '$1es'],
- [new RegExp('(matr|vert|ind)ix|ex$', 'gi'), '$1ices'],
- [new RegExp('([m|l])ouse$', 'gi'), '$1ice'],
- [new RegExp('(quiz)$', 'gi'), '$1zes'],
- [new RegExp('s$', 'gi'), 's'],
- [new RegExp('$', 'gi'), 's']
-];
-
-/*
- These rules translate from the plural form of a noun to its singular form.
-*/
-var singularRules = [
- [new RegExp('(m)en$', 'gi'), '$1an'],
- [new RegExp('(pe)ople$', 'gi'), '$1rson'],
- [new RegExp('(child)ren$', 'gi'), '$1'],
- [new RegExp('([ti])a$', 'gi'), '$1um'],
- [new RegExp('((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$','gi'), '$1$2sis'],
- [new RegExp('(hive)s$', 'gi'), '$1'],
- [new RegExp('(tive)s$', 'gi'), '$1'],
- [new RegExp('(curve)s$', 'gi'), '$1'],
- [new RegExp('([lr])ves$', 'gi'), '$1f'],
- [new RegExp('([^fo])ves$', 'gi'), '$1fe'],
- [new RegExp('([^aeiouy]|qu)ies$', 'gi'), '$1y'],
- [new RegExp('(s)eries$', 'gi'), '$1eries'],
- [new RegExp('(m)ovies$', 'gi'), '$1ovie'],
- [new RegExp('(x|ch|ss|sh)es$', 'gi'), '$1'],
- [new RegExp('([m|l])ice$', 'gi'), '$1ouse'],
- [new RegExp('(bus)es$', 'gi'), '$1'],
- [new RegExp('(o)es$', 'gi'), '$1'],
- [new RegExp('(shoe)s$', 'gi'), '$1'],
- [new RegExp('(cris|ax|test)es$', 'gi'), '$1is'],
- [new RegExp('(octop|vir)i$', 'gi'), '$1us'],
- [new RegExp('(alias|status)es$', 'gi'), '$1'],
- [new RegExp('^(ox)en', 'gi'), '$1'],
- [new RegExp('(vert|ind)ices$', 'gi'), '$1ex'],
- [new RegExp('(matr)ices$', 'gi'), '$1ix'],
- [new RegExp('(quiz)zes$', 'gi'), '$1'],
- [new RegExp('s$', 'gi'), '']
-];
-
-/*
- This is a list of words that should not be capitalized for title case
-*/
-var nonTitlecasedWords = [
- 'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at',
- 'by', 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over',
- 'with', 'for'
-];
-
-/*
- These are regular expressions used for converting between String formats
-*/
-var idSuffix = new RegExp('(_ids|_id)$', 'g');
-var underbar = new RegExp('_', 'g');
-var spaceOrUnderbar = new RegExp('[ _]', 'g');
-var dashOrUnderbar = new RegExp('[-_]', 'g');
-var uppercase = new RegExp('([A-Z])', 'g');
-var underbarPrefix = new RegExp('^_');
-
-/*
- This is a helper method that applies rules based replacement to a String
- Signature:
- applyRules(str, rules, skip, override) == String
- Arguments:
- str - String - String to modify and return based on the passed rules
- rules - Array: [RegExp, String] - Regexp to match paired with String to use for replacement
- skip - Array: [String] - Strings to skip if they match
- override - String (optional) - String to return as though this method succeeded (used to conform to APIs)
- Returns:
- String - passed String modified by passed rules
- Examples:
- applyRules("cows", InflectionJs.singularRules) === 'cow'
-*/
-var applyRules = function(str, rules, skip, override) {
- if (override) {
- str = override;
- } else {
- var ignore = (skip.indexOf(str.toLowerCase()) > -1);
- if (!ignore) {
- for (var x = 0; x < rules.length; x++) {
- if (str.match(rules[x][0])) {
- str = str.replace(rules[x][0], rules[x][1]);
- break;
- }
- }
- }
- }
- return str;
-};
-
-/*
- This lets us detect if an Array contains a given element
- Signature:
- indexOf(array, item, fromIndex, compareFunc) == Integer
- Arguments:
- array - Array - array to find object in
- item - Object - object to locate in the Array
- fromIndex - Integer (optional) - starts checking from this position in the Array
- compareFunc - Function (optional) - function used to compare Array item vs passed item
- Returns:
- Integer - index position in the Array of the passed item
- Examples:
- ['hi','there'].indexOf("guys") === -1
- ['hi','there'].indexOf("hi") === 0
-*/
-exports.indexOf = function(array, item, fromIndex, compareFunc) {
- if (!fromIndex) {
- fromIndex = -1;
- }
- var index = -1;
- for (var i = fromIndex; i < array.length; i++) {
- if (array[i] === item || compareFunc && compareFunc(array[i], item)) {
- index = i;
- break;
- }
- }
- return index;
-};
-
-/*
- This function adds plurilization support to every String object
- Signature:
- pluralize(str, plural) == String
- Arguments:
- str - String - string to apply inflection on
- plural - String (optional) - overrides normal output with said String
- Returns:
- String - singular English language nouns are returned in plural form
- Examples:
- "person".pluralize() == "people"
- "octopus".pluralize() == "octopi"
- "Hat".pluralize() == "Hats"
- "person".pluralize("guys") == "guys"
-*/
-exports.pluralize = function(str, plural) {
- return applyRules(str, pluralRules, uncountableWords, plural);
-};
-
-/*
- This function adds singularization support to every String object
- Signature:
- singularize(str, singular) == String
- Arguments:
- str - String - string to apply inflection on
- singular - String (optional) - overrides normal output with said String
- Returns:
- String - plural English language nouns are returned in singular form
- Examples:
- "people".singularize() == "person"
- "octopi".singularize() == "octopus"
- "Hats".singularize() == "Hat"
- "guys".singularize("person") == "person"
-*/
-exports.singularize = function(str, singular) {
- return applyRules(str, singularRules, uncountableWords, singular);
-};
-
-/*
- This function adds camelization support to every String object
- Signature:
- camelize(str, lowFirstLetter) == String
- Arguments:
- str - String - string to apply inflection on
- lowFirstLetter - boolean (optional) - default is to capitalize the first
- letter of the results... passing true will lowercase it
- Returns:
- String - lower case underscored words will be returned in camel case
- additionally '/' is translated to '::'
- Examples:
- "message_properties".camelize() == "MessageProperties"
- "message_properties".camelize(true) == "messageProperties"
-*/
-exports.camelize = function(str, lowFirstLetter) {
- str = str.toLowerCase();
- var str_path = str.split('/');
- for (var i = 0; i < str_path.length; i++) {
- var str_arr = str_path[i].split('_');
- var initX = ((lowFirstLetter && i + 1 === str_path.length) ? (1) : (0));
- for (var x = initX; x < str_arr.length; x++) {
- str_arr[x] = str_arr[x].charAt(0).toUpperCase() + str_arr[x].substring(1);
- }
- str_path[i] = str_arr.join('');
- }
- str = str_path.join('::');
- return str;
-};
-
-/*
- This function adds underscore support to every String object
- Signature:
- underscore(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - camel cased words are returned as lower cased and underscored
- additionally '::' is translated to '/'
- Examples:
- "MessageProperties".camelize() == "message_properties"
- "messageProperties".underscore() == "message_properties"
-*/
-exports.underscore = function(str) {
- var str_path = str.split('::');
- for (var i = 0; i < str_path.length; i++) {
- str_path[i] = str_path[i].replace(uppercase, '_$1');
- str_path[i] = str_path[i].replace(underbarPrefix, '');
- }
- str = str_path.join('/').toLowerCase();
- return str;
-};
-
-/*
- This function adds humanize support to every String object
- Signature:
- humanize(str, lowFirstLetter) == String
- Arguments:
- str - String - string to apply inflection on
- lowFirstLetter - boolean (optional) - default is to capitalize the first
- letter of the results... passing true will lowercase it
- Returns:
- String - lower case underscored words will be returned in humanized form
- Examples:
- "message_properties".humanize() == "Message properties"
- "message_properties".humanize(true) == "message properties"
-*/
-exports.humanize = function(str, lowFirstLetter) {
- str = str.toLowerCase();
- str = str.replace(idSuffix, '');
- str = str.replace(dashOrUnderbar, ' ');
- if (!lowFirstLetter) {
- str = exports.capitalize(str);
- }
- return str;
-};
-
-/*
- This function adds capitalization support to every String object
- Signature:
- capitalize(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - all characters will be lower case and the first will be upper
- Examples:
- "message_properties".capitalize() == "Message_properties"
- "message properties".capitalize() == "Message properties"
-*/
-exports.capitalize = function(str) {
- str = str.toLowerCase();
- str = str.substring(0, 1).toUpperCase() + str.substring(1);
- return str;
-};
-
-/*
- This function adds dasherization support to every String object
- Signature:
- dasherize(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - replaces all spaces or underbars with dashes
- Examples:
- "message_properties".capitalize() == "message-properties"
- "Message Properties".capitalize() == "Message-Properties"
-*/
-exports.dasherize = function(str) {
- str = str.replace(spaceOrUnderbar, '-');
- return str;
-};
-
-/*
- This function adds titleize support to every String object
- Signature:
- titleize(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - capitalizes words as you would for a book title
- Examples:
- "message_properties".titleize() == "Message Properties"
- "message properties to keep".titleize() == "Message Properties to Keep"
-*/
-exports.titleize = function(str) {
- str = str.toLowerCase();
- str = str.replace(underbar, ' ');
- var str_arr = str.split(' ');
- for (var x = 0; x < str_arr.length; x++) {
- var d = str_arr[x].split('-');
- for (var i = 0; i < d.length; i++) {
- if (nonTitlecasedWords.indexOf(d[i].toLowerCase()) < 0) {
- d[i] = exports.capitalize(d[i]);
- }
- }
- str_arr[x] = d.join('-');
- }
- str = str_arr.join(' ');
- str = str.substring(0, 1).toUpperCase() + str.substring(1);
- return str;
-};
-
-/*
- This function adds demodulize support to every String object
- Signature:
- demodulize(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - removes module names leaving only class names (Ruby style)
- Examples:
- "Message::Bus::Properties".demodulize() == "Properties"
-*/
-exports.demodulize = function(str) {
- var str_arr = str.split('::');
- str = str_arr[str_arr.length - 1];
- return str;
-};
-
-/*
- This function adds tableize support to every String object
- Signature:
- tableize(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - renders camel cased words into their underscored plural form
- Examples:
- "MessageBusProperty".tableize() == "message_bus_properties"
-*/
-exports.tableize = function(str) {
- str = exports.underscore(str);
- str = exports.pluralize(str);
- return str;
-};
-
-/*
- This function adds classification support to every String object
- Signature:
- classify(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - underscored plural nouns become the camel cased singular form
- Examples:
- "message_bus_properties".classify() == "MessageBusProperty"
-*/
-exports.classify = function(str) {
- str = exports.camelize(str);
- str = exports.singularize(str);
- return str;
-};
-
-/*
- This function adds foreign key support to every String object
- Signature:
- foreignKey(str, dropIdUbar) == String
- Arguments:
- str - String - string to apply inflection on
- dropIdUbar - boolean (optional) - default is to seperate id with an
- underbar at the end of the class name, you can pass true to skip it
- Returns:
- String - camel cased singular class names become underscored with id
- Examples:
- "MessageBusProperty".foreign_key() == "message_bus_property_id"
- "MessageBusProperty".foreign_key(true) == "message_bus_propertyid"
-*/
-exports.foreignKey = function(str, dropIdUbar) {
- str = exports.demodulize(str);
- str = exports.underscore(str);
- str = str + ((dropIdUbar) ? ('') : ('_')) + 'id';
- return str;
-};
-
-/*
- This function adds ordinalize support to every String object
- Signature:
- ordinalize(str) == String
- Arguments:
- str - String - string to apply inflection on
- Returns:
- String - renders all found numbers their sequence like "22nd"
- Examples:
- "the 1 pitch".ordinalize() == "the 1st pitch"
-*/
-exports.ordinalize = function(str) {
- var str_arr = str.split(' ');
- for (var x = 0; x < str_arr.length; x++) {
- var i = parseInt(str_arr[x], 10);
- if (isNan(i)) {
- var ltd = str_arr[x].substring(str_arr[x].length - 2);
- var ld = str_arr[x].substring(str_arr[x].length - 1);
- var suf = "th";
- if (ltd != "11" && ltd != "12" && ltd != "13") {
- if (ld === "1") {
- suf = "st";
- } else if (ld === "2") {
- suf = "nd";
- } else if (ld === "3") {
- suf = "rd";
- }
- }
- str_arr[x] += suf;
- }
- }
- str = str_arr.join(' ');
- return str;
-};
-
diff --git a/lib/interface/migratorInterface.js b/lib/interface/migratorInterface.js
new file mode 100644
index 00000000..ba5d4a1d
--- /dev/null
+++ b/lib/interface/migratorInterface.js
@@ -0,0 +1,127 @@
+/**
+ * The migrator interface provides the ability to handle all abilities which
+ * are needed to successfully modify the DD of your tables.
+ * This includes all DDL methods provided by SQL naturally.
+ *
+ * Considering the zero downtime deployment we are handling migrations like
+ * the following:
+ *
+ * Zero Downtime notes
+ *
+ * *) The very first step is always a full dump, for the sake of security
+ *
+ * - all operations that are deleting a currently existing column, e.g. renaming a column:
+ * - 1. duplicate the column
+ * - if renamed:
+ * - 1. create a trigger to update both columns
+ * - 2. disable/delete trigger after successful deployment
+ * - if deleted:
+ * - 1. apply steps from the renaming process
+ * - 2. create a backup of this column (+PK)/table and delete it afterwards (after the successful deployment)
+ * - all operations that modify content
+ * - 1. Retrieve data to be applied
+ * - 2. Extract PKs of to be updated rows
+ * - 3. Create backup of to be updated rows
+ * - 4. Apply changes
+ */
+
+function dummy () {
+ arguments[arguments.length - 1]('not implemented');
+}
+
+function MigratorInterface () {}
+
+MigratorInterface.prototype = {
+ renameCollection: dummy,
+
+ dropCollection: dummy,
+
+ createCollection: dummy,
+
+ checkDBMS: dummy,
+
+ createDatabase: dummy,
+
+ switchDatabase: dummy,
+
+ dropDatabase: dummy,
+
+ bindForeignKey: dummy,
+
+ createTable: dummy,
+
+ dropTable: dummy,
+
+ renameTable: dummy,
+
+ addColumn: dummy,
+
+ removeColumn: dummy,
+
+ renameColumn: dummy,
+
+ changeColumn: dummy,
+
+ addIndex: dummy,
+
+ removeIndex: dummy,
+
+ addForeignKey: dummy,
+
+ removeForeignKey: dummy,
+
+ createMigrationsTable: dummy,
+
+ createSeedsTable: dummy,
+
+ normalizeColumnSpec: dummy,
+
+ runSql: dummy,
+
+ createColumnDef: dummy,
+
+ mapDataType: dummy,
+
+ createColumnConstraint: dummy,
+
+ recurseCallbackArray: dummy,
+
+ allLoadedMigrations: dummy,
+
+ allLoadedSeeds: dummy,
+
+ addSeedRecord: dummy,
+
+ startMigration: dummy,
+
+ endMigration: dummy,
+
+ addMigrationRecord: dummy,
+
+ deleteMigration: dummy,
+
+ deleteSeed: dummy,
+
+ quoteDDLArr: dummy,
+
+ quoteArr: dummy,
+
+ escapeString: dummy,
+
+ escape: dummy,
+
+ escapeDDL: dummy,
+
+ all: dummy,
+
+ close: dummy,
+
+ insert: dummy
+};
+
+module.exports = MigratorInterface;
+module.exports.deprecated = {};
+
+module.exports.extending = {};
+
+module.exports.exclude = {};
diff --git a/lib/interface/seederInterface.js b/lib/interface/seederInterface.js
index f22c67f3..d5aa53bc 100644
--- a/lib/interface/seederInterface.js
+++ b/lib/interface/seederInterface.js
@@ -1,74 +1,145 @@
-var Promise = require('bluebird');
+/**
+ * The seeder interface provides the ability to handle all operations, that
+ * are not DDL specific and thus not a migration.
+ *
+ * These operations are currently, but not limited to:
+ *
+ * inserting data
+ * removing data
+ * searching data
+ * truncating whole tables
+ *
+ * This functionality is provided in two ways to the user. First there are
+ * traditional seeder. You can call them whenever you want, and how often
+ * you want. This might be handy when initializing a new development
+ * environment, but not if you want to use it for the production environment.
+ *
+ * The second seeder type, the version controlled seeders, short VC Seeds,
+ * is targeting the production environment and development environment.
+ * There is technically no big difference between them, except the following
+ * details:
+ *
+ * A VC Seed can be called from a migration, via seed.execute and seed.link. A
+ * normal seeder can not. Also A VC Seeder has a down and up function, like
+ * the way the migrations work, the static has a truncate function instead,
+ * which gets called before the seed function. This is because a seeder is
+ * mostly used to initialize something, a VC Seeder instead is used to
+ * populate changes made to the dataset and to have a clear separation of
+ * DDL and Data Maniupulations.
+ * And last but not least, a VC Seed can not be executed if it was already
+ * executed. You will need to roll back it first, it just acts pretty similar
+ * to migrations. A normal seed can be executed just as often as you want
+ * without the need to rollback your data.
+ *
+ * To note: If you rollback a migration, linked to a seeder, db-migrate will
+ * also rollback the seed. This is also a reason why you can't rollback a
+ * specific migration, you would going to break that much, you probably loose
+ * a bunch of valueable time.
+ */
-function insert(table, options, callback) {
+var Promise = require('bluebird');
+function insert (table, options, callback) {
var foreignLinkage = this.foreignLinkage[table];
- for(var o = 0; o < options.length; ++o)
- {
+ for (var o = 0; o < options.length; ++o) {
var option = Object.keys(options[o]);
- for(var i = 0; i < option.length; ++i) {
-
- if (typeof(options[option]) === 'object') {
-
- if (options[option].type === 'lookup') {
+ for (var i = 0; i < option.length; ++i) {
+ if (typeof options[option[i]] === 'object') {
+ if (options[option[i]].type === 'lookup') {
+ if (!options[option[i]].table) {
+ if (foreignLinkage[option[i]]) {
+ options[option[i]].table = foreignLinkage[option[i]].table;
+ options[option[i]].field = foreignLinkage[option[i]].field;
+ } else {
+ return Promise.reject(
+ new Error('missing foreign key linkage!')
+ ).nodeify(callback);
+ }
+ }
+ }
+ }
+ }
+ }
- if (!options[option].table) {
+ return lookup(options)
+ .then(function () {
+ return this.driver.insert(options);
+ })
+ .catch(function (e) {
+ throw e;
+ })
+ .nodeify(callback);
+}
- if (foreignLinkage[option]) {
+function lookup (options) {
+ var lookups = [];
+ var i = 0;
- options[option].table = foreignLinkage[option].table;
- options[option].field = foreignLinkage[option].field;
- }
- else {
+ for (var o = 0; o < options.length; ++o) {
+ var option = Object.keys(options[o]);
- return Promise.reject('missing foreign key linkage!').
- nodeify(callback);
- }
- }
+ for (; i < option.length; ++i) {
+ if (typeof options[option] === 'object') {
+ if (options[option].type === 'lookup') {
+ lookups.push(
+ this.driver.lookup(options[option]).catch(function (err) {
+ throw err;
+ })
+ );
}
}
}
}
- return lookup(options, 0)
- .catch(callback)
- .then(function(){
+ return Promise.settle(lookups);
+}
- return this.driver.massInsert(options);
- })
- .catch(callback)
- .nodeify(callback);
+function dummy () {
+ arguments[arguments.length - 1]('not implemented');
}
-function lookup(options, i, callback) {
+var SeederInterface = {
+ lookup: dummy,
- var lookups = [];
+ insert: dummy,
- for(var o = 0; o < options.length; ++o)
- {
- var option = Object.keys(options[o]);
+ delete: dummy,
- for(; i < option.length; ++i) {
+ runSql: dummy,
- if (typeof(options[option]) === 'object') {
+ buildWhereClause: dummy,
- if (options[option].type === 'lookup') {
+ quoteDDLArr: dummy,
- lookups.push(this.driver.lookup(options[option])
- .catch(function(err) {
+ quoteArr: dummy,
- console.log(err);
- })
- .then(function() {
+ escapeString: dummy,
- return loockup(options, i);
- }));
- }
- }
- }
- }
+ escape: dummy,
- return Promise.all(lookups).nodeify(callback);
-}
+ escapeDDL: dummy,
+
+ checkDBMS: dummy,
+
+ update: dummy,
+
+ truncate: dummy,
+
+ switchDatabase: dummy,
+
+ remove: dummy,
+
+ close: dummy
+};
+
+module.exports = SeederInterface;
+module.exports.deprecated = {};
+module.exports.extending = {
+ _l: function (field) {
+ return { type: 'lookup', field: field };
+ },
+ __Test__insert: insert,
+ __Test__lookup: lookup
+};
diff --git a/lib/log.js b/lib/log.js
deleted file mode 100644
index f88cbcfc..00000000
--- a/lib/log.js
+++ /dev/null
@@ -1,49 +0,0 @@
-this.isSilent = false;
-
-exports.silence = function (isSilent) {
- return this.isSilent = isSilent;
-};
-exports.info = function () {
- if (!this.isSilent || global.verbose) {
- Array.prototype.unshift.call(arguments, '[INFO]');
- console.info.apply(console, arguments);
- }
-};
-exports.warn = function () {
- if (!this.isSilent || global.verbose) {
- var args = Array.prototype.unshift.call(arguments, '[WARN]');
- console.warn.apply(console, arguments);
- }
-};
-exports.error = function () {
- if (!this.isSilent || global.verbose) {
- var args = Array.prototype.unshift.call(arguments, '[ERROR]');
- console.error.apply(console, arguments);
- }
-};
-exports.sql = function(sql) {
- if (!this.isSilent && (global.dryRun || global.verbose)) {
- var args = Array.prototype.slice.call(arguments).slice(1);
- args = args.slice(0, args.length - 1);
- if(global.verbose) {
- if(args.length > 0) {
- console.log('[SQL]', sql, args);
- } else {
- console.log('[SQL]', sql);
- }
- }
- if (global.dryRun) {
- if(args.length > 0) {
- console.log(sql, args);
- } else {
- console.log(sql);
- }
- }
- }
-};
-exports.verbose = function() {
- if (global.verbose) {
- Array.prototype.unshift.call(arguments, '[INFO]');
- console.log.apply(console, arguments);
- }
-};
diff --git a/lib/migration.js b/lib/migration.js
index a647814f..d94ae5e2 100644
--- a/lib/migration.js
+++ b/lib/migration.js
@@ -1,150 +1,308 @@
var fs = require('fs');
var path = require('path');
-var config = require('./config');
-var log = require('./log');
+var log = require('db-migrate-shared').log;
var Skeleton = require('./skeleton');
+var Promise = require('bluebird');
var filesRegEx = /\.js$/;
-var coffeeSupported = false;
-var coffeeModule = null;
-try {
- coffeeModule = require('coffee-script');
- if (coffeeModule && coffeeModule.register) coffeeModule.register();
- coffeeSupported = true;
- filesRegEx = /\.(js|coffee)$/;
-} catch (e) {}
-
-var internals = {};
-
-function writeMigrationRecord(db, migration, callback) {
- db._runSql('INSERT INTO ' + internals.migrationTable + ' (name, run_on) VALUES (?, ?)', [migration.name, new Date()], callback);
-}
var Migration = Skeleton.extend({
-
- init: function() {
+ init: function () {
if (arguments.length >= 3) {
this.title = arguments[0];
this.date = arguments[2];
this.name = this.formatName(this.title, this.date);
this.path = this.formatPath(arguments[1], this.name);
this.templateType = arguments[3];
- internals = arguments[4];
- } else if (arguments.length == 2) {
+ this.internals = arguments[4];
+ } else if (arguments.length === 2) {
this.path = arguments[0];
this.name = this.parseName(this.path);
this.date = this.parseDate(this.name);
this.title = this.parseTitle(this.name);
- internals = arguments[1];
+ this.internals = arguments[1];
}
- this._super(internals);
+ this._super(this.internals);
}
});
-Migration.prototype.defaultCoffeeTemplate = function() {
- return [
- "dbm = undefined",
- "type = undefined",
- "",
- "exports.setup = (options) ->",
- " dbm = options.dbmigrate;",
- " type = dbm.dataType;",
- " return",
- "",
- "exports.up = (db, callback) ->",
- "",
- "",
- "exports.down = (db, callback) ->",
- "",
- "",
- ""
- ].join("\n");
+Migration.registerHook = function (Plugin, internals) {
+ var plugin = Plugin.hook('migrator:migration:hook:require');
+ internals.parser = internals.parser || {
+ filesRegEx: filesRegEx,
+ extensions: 'js'
+ };
+
+ if (!plugin) {
+ return Promise.resolve(null);
+ }
+
+ return Promise.resolve(plugin)
+ .map(function (plugin) {
+ return plugin['migrator:migration:hook:require']();
+ })
+ .each(function (parser) {
+ if (parser && parser.extensions) {
+ internals.parser.extensions =
+ internals.parser.extensions + '|' + parser.extensions;
+ }
+ })
+ .then(function () {
+ internals.parser.filesRegEx = new RegExp(
+ '\\.(' + internals.parser.extensions + ')$'
+ );
+
+ return internals.parser;
+ });
};
-Migration.prototype.defaultJsTemplate = function() {
+Migration.prototype.defaultCoffeeTemplate = function () {
return [
- "var dbm;",
- "var type;",
- "",
- "/**",
- " * We receive the dbmigrate dependency from dbmigrate initially.",
- " * This enables us to not have to rely on NODE_PATH.",
- " */",
- "exports.setup = function(options) {",
- " dbm = options.dbmigrate;",
- " type = dbm.dataType;",
- "};",
- "",
- "exports.up = function(db, callback) {",
- " callback();",
- "};",
- "",
- "exports.down = function(db, callback) {",
- " callback();",
- "};",
- ""
- ].join("\n");
+ "'use strict';",
+ '',
+ 'dbm = undefined',
+ 'type = undefined',
+ 'seed = undefined',
+ '',
+ 'exports.setup = (options, seedLink) ->',
+ ' dbm = options.dbmigrate',
+ ' type = dbm.dataType',
+ ' seed = seedLink',
+ '',
+ 'exports.up = (db) ->',
+ ' null',
+ '',
+ 'exports.down = (db) ->',
+ ' null',
+ '',
+ ''
+ ].join('\n');
};
-Migration.prototype.defaultSqlTemplate = function() {
- return "/* Replace with your SQL commands */";
+Migration.prototype.defaultJsTemplate = function () {
+ return [
+ "'use strict';",
+ '',
+ 'var dbm;',
+ 'var type;',
+ 'var seed;',
+ '',
+ '/**',
+ ' * We receive the dbmigrate dependency from dbmigrate initially.',
+ ' * This enables us to not have to rely on NODE_PATH.',
+ ' */',
+ 'exports.setup = function(options, seedLink) {',
+ ' dbm = options.dbmigrate;',
+ ' type = dbm.dataType;',
+ ' seed = seedLink;',
+ '};',
+ '',
+ 'exports.up = function(db) {',
+ ' return null;',
+ '};',
+ '',
+ 'exports.down = function(db) {',
+ ' return null;',
+ '};',
+ '',
+ 'exports._meta = {',
+ ' "version": 1',
+ '};',
+ ''
+ ].join('\n');
};
+Migration.prototype.defaultSqlTemplate = function () {
+ return '/* Replace with your SQL commands */';
+};
-Migration.prototype.sqlFileLoaderTemplate = function() {
+Migration.prototype.sqlFileLoaderTemplate = function () {
return [
- "var dbm;",
- "var type;",
+ "'use strict';",
+ '',
+ 'var dbm;',
+ 'var type;',
+ 'var seed;',
"var fs = require('fs');",
"var path = require('path');",
- "",
- "/**",
- " * We receive the dbmigrate dependency from dbmigrate initially.",
- " * This enables us to not have to rely on NODE_PATH.",
- " */",
- "exports.setup = function(options) {",
- " dbm = options.dbmigrate;",
- " type = dbm.dataType;",
- "};",
- "",
- "exports.up = function(db, callback) {",
- " var filePath = path.join(__dirname + '/sqls/"+this.name.replace('.js', '')+"-up.sql');",
- " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){",
- " if (err) return callback(err);",
+ 'var Promise;',
+ '',
+ '/**',
+ ' * We receive the dbmigrate dependency from dbmigrate initially.',
+ ' * This enables us to not have to rely on NODE_PATH.',
+ ' */',
+ 'exports.setup = function(options, seedLink) {',
+ ' dbm = options.dbmigrate;',
+ ' type = dbm.dataType;',
+ ' seed = seedLink;',
+ ' Promise = options.Promise;',
+ '};',
+ '',
+ 'exports.up = function(db) {',
+ " var filePath = path.join(__dirname, 'sqls', '" +
+ this.name.replace('.js', '') +
+ "-up.sql');",
+ ' return new Promise( function( resolve, reject ) {',
+ " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){",
+ ' if (err) return reject(err);',
" console.log('received data: ' + data);",
- "",
- " db.runSql(data, function(err) {",
- " if (err) return callback(err);",
- " callback();",
- " });",
- " });",
- "};",
- "",
- "exports.down = function(db, callback) {",
- " var filePath = path.join(__dirname + '/sqls/"+this.name.replace('.js', '')+"-down.sql');",
- " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){",
- " if (err) return callback(err);",
+ '',
+ ' resolve(data);',
+ ' });',
+ ' })',
+ ' .then(function(data) {',
+ ' return db.runSql(data);',
+ ' });',
+ '};',
+ '',
+ 'exports.down = function(db) {',
+ " var filePath = path.join(__dirname, 'sqls', '" +
+ this.name.replace('.js', '') +
+ "-down.sql');",
+ ' return new Promise( function( resolve, reject ) {',
+ " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){",
+ ' if (err) return reject(err);',
" console.log('received data: ' + data);",
- "",
- " db.runSql(data, function(err) {",
- " if (err) return callback(err);",
- " callback();",
- " });",
- " });",
- "};",
- ""
- ].join("\n");
+ '',
+ ' resolve(data);',
+ ' });',
+ ' })',
+ ' .then(function(data) {',
+ ' return db.runSql(data);',
+ ' });',
+ '};',
+ '',
+ 'exports._meta = {',
+ ' "version": 1',
+ '};',
+ ''
+ ].join('\n');
+};
+
+Migration.prototype.sqlFileLoaderIgnoreOnInitTemplate = function () {
+ return [
+ "'use strict';",
+ '',
+ 'var dbm;',
+ 'var type;',
+ 'var seed;',
+ "var fs = require('fs');",
+ "var path = require('path');",
+ 'var ignoreOnInit = false;',
+ 'var Promise;',
+ '',
+ '/**',
+ ' * We receive the dbmigrate dependency from dbmigrate initially.',
+ ' * This enables us to not have to rely on NODE_PATH.',
+ ' */',
+ 'exports.setup = function(options, seedLink) {',
+ ' dbm = options.dbmigrate;',
+ ' ignoreOnInit = options.ignoreOnInit;',
+ ' type = dbm.dataType;',
+ ' seed = seedLink;',
+ ' Promise = options.Promise;',
+ '};',
+ '',
+ 'exports.up = function(db, callback) {',
+ " var filePath = path.join(__dirname + '/sqls/" +
+ this.name.replace('.js', '') +
+ "-up.sql');",
+ ' if (!ignoreOnInit) {',
+ ' return new Promise( function( resolve, reject ) {',
+ " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){",
+ ' if (err) return reject(err);',
+ " console.log('received data: ' + data);",
+ '',
+ ' resolve(data);',
+ ' });',
+ ' })',
+ ' .then(function(data) {',
+ ' return db.runSql(data);',
+ ' });',
+ ' }',
+ ' else {',
+ " console.log('ignoring on init: ' + filePath)",
+ ' return null;',
+ ' }',
+ '};',
+ '',
+ 'exports.down = function(db, callback) {',
+ " var filePath = path.join(__dirname + '/sqls/" +
+ this.name.replace('.js', '') +
+ "-down.sql');",
+ ' return new Promise( function( resolve, reject ) {',
+ " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){",
+ ' if (err) return reject(err);',
+ " console.log('received data: ' + data);",
+ '',
+ ' resolve(data);',
+ ' });',
+ ' })',
+ ' .then(function(data) {',
+ ' return db.runSql(data);',
+ ' });',
+ '};',
+ '',
+ 'exports._meta = {',
+ ' "version": 1',
+ '};',
+ ''
+ ].join('\n');
+};
+
+Migration.prototype.coffeeSqlFileLoaderTemplate = function () {
+ return [
+ "'use strict';",
+ '',
+ 'dbm = undefined',
+ 'type = undefined',
+ 'seed = undefined',
+ "fs = require 'fs'",
+ "path = require 'path'",
+ '',
+ '#',
+ '# We receive the dbmigrate dependency from dbmigrate initially.',
+ '# This enables us to not have to rely on NODE_PATH.',
+ '#',
+ 'exports.setup = (options, seedLink) ->',
+ ' dbm = options.dbmigrate',
+ ' type = dbm.dataType',
+ ' seed = seedLink',
+ '',
+ '',
+ 'exports.up = (db, callback) ->',
+ ' filePath = path.join "#{__dirname}/sqls/' +
+ this.name.replace('.coffee', '') +
+ '-up.sql"',
+ " fs.readFile filePath, {encoding: 'utf-8'}, (err,data) ->",
+ ' return callback err if err',
+ '',
+ ' db.runSql data, callback',
+ '',
+ 'exports.down = (db, callback) ->',
+ ' filePath = path.join "#{__dirname}/sqls/' +
+ this.name.replace('.coffee', '') +
+ '-down.sql"',
+
+ " fs.readFile filePath, {encoding: 'utf-8'}, (err,data) ->",
+ ' return callback err if err',
+ '',
+ ' db.runSql data, callback',
+ ''
+ ].join('\n');
};
Migration.TemplateType = {
DEFAULT_JS: 0,
DEFAULT_SQL: 1,
SQL_FILE_LOADER: 2,
- DEFAULT_COFFEE: 3
+ DEFAULT_COFFEE: 3,
+ COFFEE_SQL_FILE_LOADER: 4,
+ SQL_FILE_LOADER_IGNORE_ON_INIT: 5
};
-Migration.prototype.getTemplate = function() {
+Migration.prototype.getTemplate = function () {
switch (this.templateType) {
case Migration.TemplateType.DEFAULT_SQL:
return this.defaultSqlTemplate();
@@ -152,55 +310,64 @@ Migration.prototype.getTemplate = function() {
return this.sqlFileLoaderTemplate();
case Migration.TemplateType.DEFAULT_COFFEE:
return this.defaultCoffeeTemplate();
+ case Migration.TemplateType.COFFEE_SQL_FILE_LOADER:
+ return this.coffeeSqlFileLoaderTemplate();
+ case Migration.TemplateType.SQL_FILE_LOADER_IGNORE_ON_INIT:
+ return this.sqlFileLoaderIgnoreOnInitTemplate();
case Migration.TemplateType.DEFAULT_JS:
+ /* falls through */
default:
return this.defaultJsTemplate();
}
};
-Migration.prototype.write = function(callback) {
+Migration.prototype.write = function (callback) {
fs.writeFile(this.path, this.getTemplate(), callback);
};
-Migration.loadFromFilesystem = function(dir, callback) {
+Migration.loadFromFilesystem = function (dir, internals, callback) {
log.verbose('loading migrations from dir', dir);
- fs.readdir(dir, function(err, files) {
- if (err) { callback(err); return; }
- var coffeeWarn = true;
- files = files.filter(function(file) {
- if (coffeeWarn && !coffeeSupported && /\.coffee$/.test(file)) {
- log.warn('CoffeeScript not installed');
- coffeeWarn = false;
- }
- return filesRegEx.test(file);
+ fs.readdir(dir, function (err, files) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ files = files.filter(function (file) {
+ return internals.parser.filesRegEx.test(file);
});
- var migrations = files.sort().map(function(file) {
+ var migrations = files.sort().map(function (file) {
return new Migration(path.join(dir, file), internals);
});
callback(null, migrations);
});
};
-Migration.loadFromDatabase = function(dir, driver, callback) {
- log.verbose('loading migrations from database');
- driver.allLoadedMigrations(function(err, dbResults) {
- if (err && !internals.dryRun) { callback(err); return; }
- else if (err && internals.dryRun) {
- dbResults = []
- }
- var migrations = dbResults.filter(function(result) {
- return result.name.substr(0,result.name.lastIndexOf('/')) === internals.matching;
- }).map(function(result) {
- return new Migration(path.join(dir, result.name), internals);
- });
+Migration.loadFromDatabase = function (dir, driver, internals, callback) {
+ if (internals.ignoreCompleted) {
+ callback(null, []);
+ } else {
+ log.verbose('loading migrations from database');
+ driver.allLoadedMigrations(function (err, dbResults) {
+ if (err && !internals.dryRun) {
+ callback(err);
+ return;
+ } else if (err && internals.dryRun) {
+ dbResults = [];
+ }
+ var migrations = dbResults
+ .filter(function (result) {
+ return (
+ result.name.substr(0, result.name.lastIndexOf('/')) ===
+ internals.matching
+ );
+ })
+ .map(function (result) {
+ return new Migration(path.join(dir, result.name), internals);
+ });
- callback(null, migrations);
- });
+ callback(null, migrations);
+ });
+ }
};
-Migration.exportInternals = function(intern) {
-
- internals = intern;
-}
-
module.exports = Migration;
diff --git a/lib/migrator.js b/lib/migrator.js
index 8999e364..02a62909 100644
--- a/lib/migrator.js
+++ b/lib/migrator.js
@@ -1,26 +1,74 @@
-var async = require('async');
-var dbmUtil = require('./util');
+var dbmUtil = require('db-migrate-shared').util;
var Migration = require('./migration');
-var log = require('./log');
+var log = require('db-migrate-shared').log;
var Promise = require('bluebird');
+var MigratorInterface = require('./interface/migratorInterface.js');
-var internals = {};
+function SeedLink (driver, internals) {
+ this.seeder = require('./seeder.js')(
+ driver,
+ internals.argv['vcseeder-dir'],
+ true,
+ internals
+ );
+ this.internals = internals;
+ this.links = [];
+}
+SeedLink.prototype = {
+ seed: function (partialName) {
+ var reset = !this.internals.notransactions;
-Migrator = function(driver, migrationsDir, empty, intern) {
- this.driver = driver;
+ this.internals.notransactions = true;
+
+ return new Promise(function (resolve, reject) {
+ this.seeder.up(partialName, function (err) {
+ if (reset) {
+ this.internals.notransactions = false;
+ }
+
+ if (err) {
+ reject(err);
+ } else {
+ resolve(err);
+ }
+ });
+ });
+ },
+
+ link: function (partialName) {
+ this.links.push(partialName);
+ },
+
+ process: function () {
+ this.clear();
+ },
+
+ clear: function () {
+ this.links = [];
+ }
+};
+
+var Migrator = function (driver, migrationsDir, empty, intern) {
+ this.driver = dbmUtil.reduceToInterface(driver, MigratorInterface);
+ this._driver = driver;
this.migrationsDir = migrationsDir;
- internals = intern;
+ this.internals = intern;
+
+ if (intern.linked === false) {
+ this.seedLink = new SeedLink(driver, intern);
+ intern.linked = true;
+ }
- Migration.exportInternals(intern);
+ this.internals.migrationOptions.relation = require('./relation');
};
Migrator.prototype = {
- createMigrationTable: function(callback) {
- this.driver.createMigrationTable(callback);
+ createMigrationsTable: function (callback) {
+ this._driver.createMigrationsTable(callback);
},
- writeMigrationRecord: function(migration, callback) {
- function onComplete(err) {
+ writeMigrationRecord: function (migration, callback) {
+ function onComplete (err) {
if (err) {
log.error(migration.name, err);
} else {
@@ -28,11 +76,14 @@ Migrator.prototype = {
}
callback(err);
}
- this.driver.addMigrationRecord(internals.matching + '/' + migration.name, onComplete);
+ this._driver.addMigrationRecord(
+ this.internals.matching + '/' + migration.name,
+ onComplete
+ );
},
- deleteMigrationRecord: function(migration, callback) {
- function onComplete(err) {
+ deleteMigrationRecord: function (migration, callback) {
+ function onComplete (err) {
if (err) {
log.error(migration.name, err);
} else {
@@ -40,109 +91,229 @@ Migrator.prototype = {
}
callback(err);
}
- this.driver.deleteMigration(internals.matching + '/' + migration.name, function(err) {
-
- if(!internals.matching) {
-
- this.driver.deleteMigration(migration.name, onComplete);
- }
- else {
-
- onComplete.apply(err);
- }
- }.bind(this));
+ this._driver.deleteMigration(
+ this.internals.matching + '/' + migration.name,
+ function (err) {
+ if (!this.internals.matching) {
+ this._driver.deleteMigration(migration.name, onComplete);
+ } else {
+ onComplete.apply(err);
+ }
+ }.bind(this)
+ );
},
- up: function(funcOrOpts, callback) {
+ up: function (funcOrOpts, callback) {
if (dbmUtil.isFunction(funcOrOpts)) {
- funcOrOpts(this.driver, callback);
+ return funcOrOpts(this.driver, callback);
} else {
this.upToBy(funcOrOpts.destination, funcOrOpts.count, callback);
}
},
- down: function(funcOrOpts, callback) {
+ down: function (funcOrOpts, callback) {
if (dbmUtil.isFunction(funcOrOpts)) {
- funcOrOpts(this.driver, callback);
+ return funcOrOpts(this.driver, callback);
} else {
- this.downToBy(funcOrOpts.count, callback);
+ this.downToBy(funcOrOpts.destination, funcOrOpts.count, callback);
}
},
- upToBy: function(partialName, count, callback) {
+ check: function (funcOrOpts, callback) {
var self = this;
- Migration.loadFromFilesystem(self.migrationsDir, function(err, allMigrations) {
- if (err) { callback(err); return; }
+ Migration.loadFromFilesystem(self.migrationsDir, self.internals, function (
+ err,
+ allMigrations
+ ) {
+ if (err) {
+ callback(err);
+ return;
+ }
- Migration.loadFromDatabase(self.migrationsDir, self.driver, function(err, completedMigrations) {
- if (err) { callback(err); return; }
- var toRun = dbmUtil.filterUp(allMigrations, completedMigrations, partialName, count);
+ Migration.loadFromDatabase(
+ self.migrationsDir,
+ self._driver,
+ self.internals,
+ function (err, completedMigrations) {
+ if (err) {
+ callback(err);
+ return;
+ }
- if (toRun.length === 0) {
- log.info('No migrations to run');
- callback(null);
- return;
- }
+ // Requires pr to export filterCompleted from db-migrate-shared
+ var toRun = dbmUtil.filterCompleted(
+ allMigrations,
+ completedMigrations
+ );
- async.forEachSeries(toRun, function(migration, next) {
- log.verbose('preparing to run up migration:', migration.name);
- self.driver.startMigration()
- .catch(callback)
- .then(function() {
-
- if(typeof(migration.setup) === 'function')
- migration.setup(internals.migrationOptions);
-
- self.up(migration.up.bind(migration), function(err) {
- if (err) { callback(err); return; }
- self.writeMigrationRecord(migration, function() { self.driver.endMigration(next); });
- });
- });
- }, callback);
- });
+ log.info('Migrations to run:', toRun.map(migration => migration.name));
+ callback(null, toRun);
+ }
+ );
});
},
- downToBy: function(count, callback) {
+ sync: function (funcOrOpts, callback) {
var self = this;
- Migration.loadFromDatabase(self.migrationsDir, self.driver, function(err, completedMigrations) {
- if (err) { return callback(err); }
- var toRun = dbmUtil.filterDown(completedMigrations, count);
+ Migration.loadFromDatabase(
+ self.migrationsDir,
+ self._driver,
+ self.internals,
+ function (err, completedMigrations) {
+ if (err) {
+ callback(err);
+ return;
+ }
- if (toRun.length === 0) {
- log.info('No migrations to run');
- callback(null);
+ var mode = dbmUtil.syncMode(
+ completedMigrations,
+ funcOrOpts.destination
+ );
+ if (mode === 1) {
+ log.info('Syncing upwards.');
+ self.up(funcOrOpts, callback);
+ } else {
+ log.info('Syncing downwards.');
+ self.down(funcOrOpts, callback);
+ }
+ }
+ );
+ },
+
+ upToBy: function (partialName, count, callback) {
+ var self = this;
+ Migration.loadFromFilesystem(self.migrationsDir, self.internals, function (
+ err,
+ allMigrations
+ ) {
+ if (err) {
+ callback(err);
return;
}
- async.forEachSeries(toRun, function(migration, next) {
- log.verbose('preparing to run down migration:', migration.name);
+ Migration.loadFromDatabase(
+ self.migrationsDir,
+ self._driver,
+ self.internals,
+ function (err, completedMigrations) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ var toRun = dbmUtil.filterUp(
+ allMigrations,
+ completedMigrations,
+ partialName,
+ count
+ );
- if(typeof(migration.setup) === 'function')
- migration.setup(internals.migrationOptions);
+ if (toRun.length === 0) {
+ log.info('No migrations to run');
+ callback(null);
+ return;
+ }
- self.driver.startMigration()
+ if (self.internals.check) {
+ var toRunNames = toRun.map(migration => migration.name);
+ log.info('Migrations to run:', toRunNames);
+ callback(null, toRunNames);
+ return;
+ }
- .then(function() {
- self.down(migration.down.bind(migration), function(err) {
- if (err) {
+ return Promise.resolve(toRun)
+ .each(function (migration) {
+ log.verbose('preparing to run up migration:', migration.name);
- callback(err);
- return;
- }
- self.deleteMigrationRecord(migration, function(err) {
- if(err) {
+ return self.driver
+ .startMigration()
+ .then(function () {
+ var setup = migration.setup();
- return callback(err);
- }
+ if (typeof setup === 'function') {
+ setup(self.internals.migrationOptions, self.seedLink);
+ }
- self.driver.endMigration(next);
- });
- });
- });
- }, callback);
+ return self.up(migration.up.bind(migration));
+ })
+ .then(function () {
+ if (self.seedLink && self.seedLink.links.length) {
+ log.info('Calling linked seeds');
+
+ return self.seedLink.process();
+ }
+ })
+ .then(function () {
+ return Promise.promisify(
+ self.writeMigrationRecord.bind(self)
+ )(migration);
+ })
+ .then(self.driver.endMigration.bind(self.driver));
+ })
+ .nodeify(callback);
+ }
+ );
});
+ },
+
+ downToBy: function (partialName, count, callback) {
+ var self = this;
+ Migration.loadFromDatabase(
+ self.migrationsDir,
+ self._driver,
+ self.internals,
+ function (err, completedMigrations) {
+ if (err) {
+ return callback(err);
+ }
+
+ var toRun = dbmUtil.filterDown(completedMigrations, partialName, count);
+
+ if (toRun.length === 0) {
+ log.info('No migrations to run');
+ callback(null);
+ return;
+ }
+
+ if (self.internals.check) {
+ var toRunNames = toRun.map(migration => migration.name);
+ log.info('Migrations to run:', toRunNames);
+ callback(null, toRunNames);
+ return;
+ }
+
+ return Promise.resolve(toRun)
+ .each(function (migration) {
+ log.verbose('preparing to run down migration:', migration.name);
+
+ return self.driver
+ .startMigration()
+ .then(function () {
+ var setup = migration.setup();
+
+ if (typeof setup === 'function') {
+ setup(self.internals.migrationOptions, self.seedLink);
+ }
+
+ return self.down(migration.down.bind(migration));
+ })
+ .then(function () {
+ if (self.seedLink && self.seedLink.links.length) {
+ log.info('Calling linked seeds');
+
+ return self.seedLink.process();
+ }
+ })
+ .then(function () {
+ return Promise.promisify(
+ self.deleteMigrationRecord.bind(self)
+ )(migration);
+ })
+ .then(self.driver.endMigration.bind(self.driver));
+ })
+ .nodeify(callback);
+ }
+ );
}
};
diff --git a/lib/relation.js b/lib/relation.js
new file mode 100644
index 00000000..c3a42721
--- /dev/null
+++ b/lib/relation.js
@@ -0,0 +1,37 @@
+var fs = require('fs');
+var log = require('db-migrate-shared').log;
+
+function Relation () {
+ this.relations = {};
+}
+
+Relation.prototype = {
+ addRelation: function (table, column, relatedTable, relatedColumn) {
+ this.relations[table] = this.relations[table] || {};
+ this.relations[table][column] = relatedColumn;
+ },
+
+ getRelation: function (table, column) {
+ return this.relations[table][column];
+ },
+
+ fromJson: function (file) {
+ // Relation = require(file);
+
+ log.info('Read Relations from file ' + file);
+ },
+
+ toJson: function (file) {
+ fs.writeFile(file, JSON.stringify(this.relations), function (err) {
+ if (err) {
+ throw err;
+ }
+
+ log.info('Wrote Relations to file ' + file);
+ });
+ },
+
+ clearRelations: function () {
+ this.relations = {};
+ }
+};
diff --git a/lib/seed.js b/lib/seed.js
index f5cfc928..5bdbf6fc 100644
--- a/lib/seed.js
+++ b/lib/seed.js
@@ -1,135 +1,136 @@
var fs = require('fs');
var path = require('path');
-var config = require('./config');
-var log = require('./log');
+var log = require('db-migrate-shared').log;
var Skeleton = require('./skeleton');
-var filesRegEx = /\.js$/;
-var coffeeSupported = false;
-var coffeeModule = null;
-try {
- coffeeModule = require('coffee-script');
- if (coffeeModule && coffeeModule.register) coffeeModule.register();
- coffeeSupported = true;
- filesRegEx = /\.(js|coffee)$/;
-} catch (e) {}
-
-var internals = {};
-
var Seed = Skeleton.extend({
-
- init: function() {
+ init: function () {
if (arguments.length >= 3) {
this.title = arguments[0];
this.date = arguments[2];
this.name = this.formatName(this.title, this.date);
this.path = this.formatPath(arguments[1], this.name);
this.templateType = arguments[3];
- internals = arguments[4];
- } else if (arguments.length == 2) {
+ this.internals = arguments[4];
+ } else if (arguments.length === 2) {
this.path = arguments[0];
this.name = this.parseName(this.path);
this.date = this.parseDate(this.name);
this.title = this.parseTitle(this.name);
- internals = arguments[1];
+ this.internals = arguments[1];
}
- this._super(internals);
+ this._super(this.internals);
},
- up: function(db, static, callback) {
-
- if(static) {
-
+ up: function (db, isStatic) {
+ if (isStatic) {
var seed = require(this.path);
-
- seed.truncate(db, function(err) {
-
- if(err) {
-
- console.log('Error while truncating static seed.');
- callback(err);
- }
- else {
-
- seed.seed(db, function(err) {
-
- if(err) {
-
- console.log('Error while seeding static seed.');
- callback(err);
+ var cbExecuted = false;
+
+ return new Promise(
+ function (resolve, reject) {
+ var r = function (err) {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+
+ if (err) {
+ log.error('Error while truncating static seed.');
+ reject(err);
+ } else resolve();
}
- else {
-
- callback(null);
+ };
+
+ Promise.resolve(seed.truncate.apply(this, [db, r]))
+ .then(function (Promise) {
+ if (Promise !== undefined && cbExecuted === false) {
+ cbExecuted = true;
+ resolve();
+ }
+ })
+ .catch(function (err) {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+ reject(err);
+ }
+ });
+ }.bind(this)
+ ).then(function () {
+ var seedExecuted = false;
+
+ return new Promise(function (resolve, reject) {
+ var r = function (err) {
+ if (seedExecuted === false) {
+ seedExecuted = true;
+
+ if (err) {
+ log.error('Error while seeding static seed.');
+ reject(err);
+ } else resolve();
}
- });
- }
+ };
+
+ Promise.resolve(seed.seed.apply(this, [db, r]))
+ .then(function (Promise) {
+ if (Promise !== undefined && seedExecuted === false) {
+ seedExecuted = true;
+ resolve();
+ }
+ })
+ .catch(function (err) {
+ if (seedExecuted === false) {
+ seedExecuted = true;
+ reject(err);
+ }
+ });
+ });
});
- }
- else {
-
- this._up(db, callback);
+ } else {
+ return this._up(db);
}
},
- down: function(db, static, callback) {
-
- if(static) {
-
- var seed = require(this.path);
-
- seed.truncate(db, function(err) {
-
- if(err) {
-
- console.log('Error while truncating static seed.');
- callback(err);
- }
- else {
-
- callback(null);
- }
- });
- }
- else {
-
- this._down(db, callback);
- }
+ down: function (db) {
+ return this._down(db);
}
});
-Seed.loadFromFilesystem = function(dir, callback) {
+Seed.loadFromFilesystem = function (dir, internals, callback) {
log.verbose('loading seeds from dir', dir);
- fs.readdir(dir, function(err, files) {
- if (err) { callback(err); return; }
- var coffeeWarn = true;
- files = files.filter(function(file) {
- if (coffeeWarn && !coffeeSupported && /\.coffee$/.test(file)) {
- log.warn('CoffeeScript not installed');
- coffeeWarn = false;
- }
- return filesRegEx.test(file);
+ fs.readdir(dir, function (err, files) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ files = files.filter(function (file) {
+ return internals.parser.filesRegEx.test(file);
});
- var seeds = files.sort().map(function(file) {
- return new Seed(path.join(dir, file));
+ var seeds = files.sort().map(function (file) {
+ return new Seed(path.join(dir, file), internals);
});
callback(null, seeds);
});
};
-Seed.loadFromDatabase = function(dir, driver, callback) {
+Seed.loadFromDatabase = function (dir, driver, internals, callback) {
log.verbose('loading seeds from database');
- driver.allLoadedseeds(function(err, dbResults) {
- if (err && !internals.dryRun) { callback(err); return; }
- else if (err && internals.dryRun) {
+ driver.allLoadedSeeds(function (err, dbResults) {
+ if (err && !internals.dryRun) {
+ callback(err);
+ return;
+ } else if (err && internals.dryRun) {
dbResults = [];
}
- var seeds = dbResults.filter(function(result) {
- return result.name.substr(0,result.name.lastIndexOf('/')) === internals.matching;
- }).map(function(result) {
- return new Seed(path.join(dir, result.name));
- });
+ var seeds = dbResults
+ .filter(function (result) {
+ return (
+ result.name.substr(0, result.name.lastIndexOf('/')) ===
+ internals.matching
+ );
+ })
+ .map(function (result) {
+ return new Seed(path.join(dir, result.name), internals);
+ });
callback(null, seeds);
});
diff --git a/lib/seeder.js b/lib/seeder.js
index c090b887..072fc7ff 100644
--- a/lib/seeder.js
+++ b/lib/seeder.js
@@ -1,38 +1,134 @@
var Seed = require('./seed');
-var log = require('./log');
-var dbmUtil = require('./util');
+var log = require('db-migrate-shared').log;
+var dbmUtil = require('db-migrate-shared').util;
var Promise = require('bluebird');
+var SeederInterface = require('./interface/seederInterface.js');
var internals = {};
-Seeder = function (driver, seedsDir, versionControlled, intern) {
- this.driver = driver;
+function MigrationLink (driver, internals) {
+ this.migrator = require('./migrator.js')(
+ driver,
+ internals.migrationsDir,
+ null,
+ internals
+ );
+ this.links = [];
+}
+
+MigrationLink.prototype = {
+ link: function (partialName) {
+ this.links.push(partialName);
+ },
+
+ migrate: function (partialName) {
+ var reset = !internals.notransactions;
+
+ internals.notransactions = true;
+
+ return new Promise(function (resolve, reject) {
+ this.migrator.up(partialName, function (err) {
+ if (reset) {
+ internals.notransactions = false;
+ }
+
+ if (err) {
+ reject(err);
+ } else {
+ resolve(err);
+ }
+ });
+ });
+ },
+
+ process: function () {
+ var reset = !internals.notransactions;
+
+ internals.notransactions = true;
+
+ return new Promise(
+ function (resolve, reject) {
+ var keys = Object.keys(this.links);
+ var i = 0;
+
+ var migrate = function (i) {
+ if (i < keys.length) {
+ if (reset) {
+ internals.notransactions = false;
+ }
+
+ resolve();
+ this.clear();
+ }
+
+ this.migrator.up(
+ {
+ destination: this.links[keys[i]]
+ },
+ function (err) {
+ if (err) {
+ if (reset) {
+ internals.notransactions = false;
+ }
+
+ reject(err);
+ } else {
+ migrate(++i);
+ }
+ }
+ );
+ }.bind(this);
+
+ migrate(i);
+ }.bind(this)
+ );
+ },
+
+ clear: function () {
+ this.links = [];
+ }
+};
+
+var Seeder = function (driver, seedsDir, versionControlled, intern) {
+ SeederInterface.extending = intern.interfaces.SeederInterface;
+ this.driver = dbmUtil.reduceToInterface(driver, SeederInterface);
+ this._driver = driver;
this.seedDir = seedsDir;
this.isVC = versionControlled;
+
+ if (intern.linked === false) {
+ intern.linked = true;
+ this.migrationLink = new MigrationLink(driver, intern);
+ }
+
internals = intern;
+ this.internals = intern;
};
Seeder.prototype = {
+ createSeedsTable: function (callback) {
+ this._driver.createSeedsTable(callback);
+ },
seed: function (argv, callback) {
-
- if (this.isVC)
+ if (this.isVC) {
this.up(argv, callback);
- else
+ } else {
this._staticSeed(argv.destination, callback);
+ }
},
- up: function(funcOrOpts, callback) {
+ up: function (funcOrOpts, callback) {
if (dbmUtil.isFunction(funcOrOpts)) {
- funcOrOpts(this.driver, false, callback);
+ return funcOrOpts(this.driver, false, callback);
} else {
this.upToBy(funcOrOpts.destination, funcOrOpts.count, callback);
}
},
- down: function(funcOrOpts, callback) {
+ down: function (funcOrOpts, callback) {
if (dbmUtil.isFunction(funcOrOpts)) {
- funcOrOpts(this.driver, callback);
+ return funcOrOpts(this.driver, callback);
} else {
this.downToBy(funcOrOpts.count, callback);
}
@@ -58,13 +154,13 @@ Seeder.prototype = {
*
*/
_staticSeed: function (partialName, callback) {
-
var self = this;
- return Seed.loadFromFilesystem(self.seedDir, function(err, allSeeds) {
-
+ return Seed.loadFromFilesystem(self.seedDir, self.internals, function (
+ err,
+ allSeeds
+ ) {
if (err) {
-
callback(err);
return;
}
@@ -77,67 +173,152 @@ Seeder.prototype = {
return;
}
- return Promise.resolve(toRun).each(function(seeder) {
- log.verbose('preparing to run up seeder:', seeder.name);
-
- return self.driver.startMigration()
- .catch(callback)
- .then(function() {
+ return Promise.resolve(toRun)
+ .each(function (seeder) {
+ log.verbose('preparing to run up seeder:', seeder.name);
- return (Promise.promisify(seeder.up.bind(seeder)))(self.driver, true);
- });
- })
- .catch(callback)
- .then(self.driver.endMigration.bind(self.driver))
- .catch(callback)
- .nodeify(callback);
+ var setup = seeder.setup();
+ if (typeof setup === 'function') {
+ setup(self.internals.seederOptions);
+ }
+
+ return self._driver
+ .startMigration()
+ .catch(callback)
+ .then(function () {
+ return seeder.up(self.driver, true);
+ });
+ })
+ .then(self._driver.endMigration.bind(self.driver))
+ .then(function () {
+ callback();
+ })
+ .catch(function (e) {
+ throw e;
+ });
});
},
- upToBy: function(partialName, count, callback) {
-
- var self = this;
-
- return Migration.loadFromFilesystem(self.seedDir, function(err, allMigrations) {
-
- if (err) { callback(err); return; }
-
- return Migration.loadFromDatabase(self.seedDir, self.driver, function(err, completedMigrations) {
-
- if (err) { callback(err); return; }
- var toRun = dbmUtil.filterUp(allMigrations, completedMigrations, partialName, count);
+ writeSeedRecord: function (seed, callback) {
+ function onComplete (err) {
+ if (err) {
+ log.error(seed.name, err);
+ } else {
+ log.info('Processed seed', seed.name);
+ }
+ callback(err);
+ }
+ this._driver.addSeedRecord(
+ this.internals.matching + '/' + seed.name,
+ onComplete
+ );
+ },
- if (toRun.length === 0) {
- log.info('No seeds to run');
- callback(null);
- return;
+ deleteSeedRecord: function (seed, callback) {
+ function onComplete (err) {
+ if (err) {
+ log.error(seed.name, err);
+ } else {
+ log.info('Processed seed', seed.name);
+ }
+ callback(err);
+ }
+ this._driver.deleteSeed(
+ this.internals.matching + '/' + seed.name,
+ function (err) {
+ if (!this.internals.matching) {
+ this._driver.deleteSeed(seed.name, onComplete);
+ } else {
+ onComplete.apply(err);
}
+ }.bind(this)
+ );
+ },
- return Promise.resolve(toRun).each(function(seeder) {
-
- log.verbose('preparing to run up seeder:', seeder.name);
+ upToBy: function (partialName, count, callback) {
+ var self = this;
- return self.driver.startMigration()
- .catch(callback)
- .then(function() {
- return (Promise.promisify(self.up.bind(self)))(seeder.up.bind(seeder));
- });
- })
- .catch(callback)
- .then(self.driver.endMigration.bind(self.driver))
- .catch(callback)
- .nodeify(callback);
+ return Seed.loadFromFilesystem(self.seedDir, self.internals, function (
+ err,
+ allMigrations
+ ) {
+ if (err) {
+ callback(err);
+ return;
+ }
- });
+ return Seed.loadFromDatabase(
+ self.seedDir,
+ self._driver,
+ self.internals,
+ function (err, completedSeeds) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ var toRun = dbmUtil.filterUp(
+ allMigrations,
+ completedSeeds,
+ partialName,
+ count
+ );
+
+ if (toRun.length === 0) {
+ log.info('No seeds to run');
+ callback(null);
+ return;
+ }
+
+ return Promise.resolve(toRun)
+ .each(function (seeder) {
+ log.verbose('preparing to run up seeder:', seeder.name);
+
+ return self._driver
+ .startMigration()
+ .then(function () {
+ var setup = seeder.setup();
+ if (typeof setup === 'function') {
+ setup(self.internals.seederOptions, self.migrationLink);
+ }
+
+ return self.up(seeder.up.bind(seeder));
+ })
+ .then(function () {
+ if (self.seedLink && self.seedLink.links.length) {
+ log.info('Calling linked migrations');
+
+ return self.seedLink.process(self.migrationLink);
+ }
+ })
+ .then(function () {
+ return Promise.promisify(
+ self.writeSeedRecord.bind(self)
+ )(seeder);
+ })
+ .then(self._driver.endMigration.bind(self.driver))
+ .catch(function (e) {
+ throw e;
+ });
+ })
+ .then(function () {
+ callback();
+ });
+ }
+ );
});
},
- downToBy: function(count, callback) {
+ downToBy: function (count, callback) {
var self = this;
- Migration.loadFromDatabase(self.seedDir, self.driver, function(err, completedMigrations) {
- if (err) { return callback(err); }
+ Seed.loadFromDatabase(self.seedDir, self._driver, self.internals, function (
+ err,
+ completedSeeds
+ ) {
+ if (err) {
+ return callback(err);
+ }
- var toRun = dbmUtil.filterDown(completedMigrations, count);
+ var toRun = dbmUtil.filterDown(completedSeeds, count);
if (toRun.length === 0) {
log.info('No migrations to run');
@@ -145,23 +326,42 @@ Seeder.prototype = {
return;
}
- return Promise.resolve(toRun).each(function(seeder) {
-
- log.verbose('preparing to run down seeder:', seeder.name);
-
- return self.driver.startMigration()
- .catch(callback)
- .then(function() {
- return (Promise.promisify(self.down.bind(self)))(seeder.down.bind(seeder));
- });
-
- })
- .catch(callback)
- .then(self.driver.endMigration.bind(self.driver))
- .catch(callback)
- .nodeify(callback);
+ return Promise.resolve(toRun)
+ .each(function (seeder) {
+ log.verbose('preparing to run down seeder:', seeder.name);
+
+ return self._driver
+ .startMigration()
+ .then(function () {
+ var setup = seeder.setup();
+
+ if (typeof setup === 'function') {
+ setup(self.internals.seederOptions, self.migrationLink);
+ }
+
+ return self.down(seeder.down.bind(seeder));
+ })
+ .then(function () {
+ if (self.seedLink && self.seedLink.links.length) {
+ log.info('Calling linked migrations');
+
+ return self.seedLink.process();
+ }
+ })
+ .then(function () {
+ return Promise.promisify(
+ self.deleteSeedRecord.bind(self)
+ )(seeder);
+ })
+ .then(self._driver.endMigration.bind(self.driver))
+ .catch(function (e) {
+ throw e;
+ });
+ })
+ .then(function () {
+ callback();
+ });
});
}
};
-
module.exports = Seeder;
diff --git a/lib/skeleton.js b/lib/skeleton.js
index 5fd6b03c..0adca10b 100644
--- a/lib/skeleton.js
+++ b/lib/skeleton.js
@@ -1,19 +1,28 @@
var path = require('path');
-var inflection = require('./inflection');
-var lpad = require('./util').lpad;
+var inflection = require('inflection');
+var Promise = require('bluebird');
+var lpad = require('db-migrate-shared').util.lpad;
var Class = require('./class');
-var internals = {};
+function isPromise (probe) {
+ return (
+ probe instanceof Promise ||
+ (probe &&
+ probe.then &&
+ probe.constructor &&
+ probe.constructor.name === 'Promise')
+ );
+}
-function formatPath(dir, name) {
+function formatPath (dir, name) {
return path.join(dir, name);
}
-function formatName(title, date) {
+function formatName (title, date) {
return formatDate(date) + '-' + formatTitle(title);
}
-function formatDate(date) {
+function formatDate (date) {
return [
date.getUTCFullYear(),
lpad(date.getUTCMonth() + 1, '0', 2),
@@ -24,53 +33,134 @@ function formatDate(date) {
].join('');
}
-function formatTitle(title) {
+function formatTitle (title) {
return inflection.dasherize(title);
}
-function parseDate(name) {
+function parseDate (name) {
var date = new Date();
- var match = name.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})-[^\.]+/);
+ var match = name.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})-[^.]+/);
date.setUTCFullYear(match[1]);
- date.setUTCMonth(match[2] - 1);
date.setUTCDate(match[3]);
+ date.setUTCMonth(match[2] - 1);
date.setUTCHours(match[4]);
date.setUTCMinutes(match[5]);
date.setUTCSeconds(match[6]);
return date;
}
-function parseTitle(name) {
- var match = name.match(/\d{14}-([^\.]+)/);
+function parseTitle (name) {
+ var match = name.match(/\d{14}-([^.]+)/);
var dashed = match[1];
return inflection.humanize(dashed, true);
}
var Skeleton = Class.extend({
+ init: function (intern) {
+ this.internals = intern;
+ },
- init: function(intern) {
- internals = intern;
+ _up: function () {
+ var params = arguments;
+
+ var cbExecuted = false;
+
+ return new Promise(
+ function (resolve, reject) {
+ var migration;
+ var r = function (err) {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ }
+ };
+
+ params[params.length++] = r;
+
+ migration = require(this.path).up.apply(this, params);
+
+ if (migration === null) migration = Promise.resolve();
+ if (isPromise(migration)) {
+ migration
+ .then(function () {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+ resolve();
+ }
+ })
+ .catch(function (err) {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+ reject(err);
+ }
+ });
+ }
+ }.bind(this)
+ );
},
- _up: function() {
- return require(this.path).up.apply(this, arguments);
+ _down: function () {
+ var params = arguments;
+ var cbExecuted = false;
+
+ return new Promise(
+ function (resolve, reject) {
+ var migration;
+ var r = function (err) {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ }
+ };
+
+ params[params.length++] = r;
+ migration = require(this.path).down.apply(this, params);
+
+ if (migration === null) migration = Promise.resolve();
+ if (isPromise(migration)) {
+ migration
+ .then(function () {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+ resolve();
+ }
+ })
+ .catch(function (err) {
+ if (cbExecuted === false) {
+ cbExecuted = true;
+ reject(err);
+ }
+ });
+ }
+ }.bind(this)
+ );
},
- _down: function() {
- return require(this.path).down.apply(this, arguments);
+ up: function (db) {
+ return this._up(db);
},
- up: function(db, callback) {
- this._up(db, callback);
+ down: function (db) {
+ return this._down(db);
},
- down: function(db, callback) {
- this._down(db, callback);
+ setup: function () {
+ return require(this.path).setup;
},
- parseName: function(path) {
- var match = path.match(/(\d{14}-[^.]+)(?:\.*?)?/);
- return match[1];
+ parseName: function (path) {
+ var match = path.match(/(\d{14}-[^.]+)(?:\.*?)?/);
+ return match[1];
},
parseTitle: parseTitle,
@@ -78,7 +168,6 @@ var Skeleton = Class.extend({
formatTitle: formatTitle,
formatPath: formatPath,
formatName: formatName
-
});
module.exports = Skeleton;
diff --git a/lib/transitions/1.js b/lib/transitions/1.js
new file mode 100644
index 00000000..3da3e43a
--- /dev/null
+++ b/lib/transitions/1.js
@@ -0,0 +1,33 @@
+
+/**
+ * Our first transition is actually the removal of globals and
+ * introducing the setup routine.
+ * Thus the body of this transition is just an empty function, but displays
+ * as all transitions an explanation to the user.
+ */
+module.exports = {
+
+ transition: function () {
+
+ },
+
+ message: function () {
+ console.log('Congratulations!\n\n' +
+ 'You just migrated from protocol 0 to 1!\n' +
+ 'What does that mean for you?\n' +
+ 'Quite simple, from 0 to 1 we introduced the setup function and we' +
+ 'deprecated some globals, like async or dbm. What this transition does is' +
+ ' grabbing your migrations, replacing the async and dbm definitions and ' +
+ 'inserting the setup body if not present.\n' +
+ 'It might be possible, that you still run into errors, for example if ' +
+ 'you already have had a setup body, it wont be replaced and you might ' +
+ 'be missing the default routine of the setup, which provides for example ' +
+ 'the dataTypes to your migration.\n\n' +
+ 'What do I need to do now?\n' +
+ 'Try to rexecute all of your migrations and watch if you run into any ' +
+ 'Errors, if you encounter any, you need to manually fix those ' +
+ 'problems. But don\'t worry, this protocol transitions happen very ' +
+ 'rarely!\n'
+ );
+ }
+};
diff --git a/lib/transitions/ask.js b/lib/transitions/ask.js
new file mode 100644
index 00000000..9098eca5
--- /dev/null
+++ b/lib/transitions/ask.js
@@ -0,0 +1,22 @@
+var prompt = require('prompt');
+
+function ask (schema, callback) {
+ console.log('Starting transition helper...\n');
+
+ prompt.start();
+
+ prompt.get(schema, function (err, result) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (result.safe === 'n') {
+ console.log('Aborted transition helper!');
+ callback(new Error('Aborted transition.'));
+ } else {
+ callback(null, result);
+ }
+ });
+}
+
+module.exports = ask;
diff --git a/lib/transitions/snippets/setup.sjs b/lib/transitions/snippets/setup.sjs
new file mode 100644
index 00000000..03d5700a
--- /dev/null
+++ b/lib/transitions/snippets/setup.sjs
@@ -0,0 +1,8 @@
+/**
+ * We receive the dbmigrate dependency from dbmigrate initially.
+ * This enables us to not have to rely on NODE_PATH.
+ */
+exports.setup = function(options, seedLink) {
+ dbm = options.dbmigrate;
+ type = dbm.dataType;
+};
diff --git a/lib/transitions/transitioner.js b/lib/transitions/transitioner.js
new file mode 100644
index 00000000..ea19caa1
--- /dev/null
+++ b/lib/transitions/transitioner.js
@@ -0,0 +1,78 @@
+var Migration = require('../migration.js');
+var tryRequire = require('./try-require.js');
+var updateVersion = require('./update-version.js');
+var ask = require('./ask.js');
+var log = require('db-migrate-shared').log;
+
+module.exports = function (internals) {
+ ask(
+ {
+ properties: {
+ safe: {
+ description:
+ 'This process is going to alter your migrations. We ' +
+ 'highly recommend you to backup your migrations or even better safe ' +
+ "the current state with git versioning.\nPlease make sure you don't " +
+ 'blow up yourself.\n\nDo you want to continue? [y/n]',
+ message: 'Invalid answer! Do you want to continue? [y/n]',
+ type: 'string',
+ default: 'n',
+ conform: function (value) {
+ return value === 'y' || value === 'n';
+ }
+ }
+ }
+ },
+ function (err) {
+ if (err) {
+ return;
+ }
+
+ internals.parser = {
+ filesRegEx: /\.js$/
+ };
+
+ Migration.loadFromFilesystem('migrations/', internals, function (
+ err,
+ migrations
+ ) {
+ var messages = [];
+ if (err) {
+ return;
+ }
+
+ migrations.forEach(function (migration) {
+ var required = tryRequire(migration, internals);
+ var version =
+ required._meta && required._meta.version
+ ? required._meta.version
+ : 0;
+
+ if (version !== internals.migrationProtocol) {
+ var i;
+
+ for (i = 0; i < internals.migrationProtocol; ++i) {
+ var transition = require('./' + (i + 1));
+ transition.transition();
+ messages[i + 1] = transition.message;
+
+ updateVersion(required, migration, internals, i + 1);
+ log.info('Transitioned ' + migration.name + '.');
+ }
+ } else {
+ log.verbose(
+ migration.name,
+ 'was already transitioned to version ' +
+ internals.migrationProtocol +
+ '.'
+ );
+ }
+ });
+
+ messages.forEach(function (message) {
+ message();
+ });
+ });
+ }
+ );
+};
diff --git a/lib/transitions/try-require.js b/lib/transitions/try-require.js
new file mode 100644
index 00000000..eb8f532d
--- /dev/null
+++ b/lib/transitions/try-require.js
@@ -0,0 +1,113 @@
+var log = require('db-migrate-shared').log;
+var path = require('path');
+var fs = require('fs');
+var cp = require('child_process');
+
+var handled = false;
+var installed = false;
+var removedGlobals = {
+ 'dbm is not defined': globalHandler,
+ 'async is not defined': globalHandler,
+ "Cannot find module 'db-migrate'": missingDBMigrate,
+ "Cannot find module 'async'": installAsync
+};
+
+function missingDBMigrate (migration, retry) {
+ return globalHandler(migration.path, retry);
+}
+
+function installAsync (migration, retry) {
+ var cmd = ['install', '--save', 'async'];
+
+ if (installed) {
+ retry();
+ } else {
+ installed = true;
+ }
+
+ log.info('Installing async...');
+ cp.spawnSync('npm', cmd, {
+ cwd: migration.internals.cwd,
+ stdio: 'inherit'
+ });
+
+ return retry();
+}
+
+function globalHandler (migration, retry) {
+ var data = fs.readFileSync(migration, 'utf8');
+ data = data.replace(
+ /^dbm = dbm \|\| require\((?!\s)?'db-migrate'(?!\s)?\)/m,
+ 'var dbm'
+ );
+
+ data = data.replace(
+ /^var dbm = global\.dbm \|\| require\((?!\s)?'db-migrate'(?!\s)?\)/m,
+ 'var dbm'
+ );
+
+ if (data.indexOf('async = async || require') !== -1) {
+ handled = true;
+ data = data.replace(
+ /^async = async \|\| require\((?!\s)?'async'(?!\s)?\)/m,
+ "var async = require( 'async' )"
+ );
+ }
+ data = data.replace(/^var type = dbm.dataType/m, 'var type');
+
+ if (data.indexOf('exports.setup = ') === -1) {
+ var snippet = fs.readFileSync(
+ path.join(__dirname, '/snippets/setup.sjs'),
+ 'utf8'
+ );
+ data = data.replace(/exports.up/, snippet + '\nexports.up');
+ }
+
+ fs.writeFileSync(migration, data, 'utf8');
+
+ return retry();
+}
+
+function tryRequire (migration) {
+ try {
+ if (handled && !installed) {
+ return installAsync(migration, tryRequire.bind(this, migration));
+ }
+
+ return require(migration.internals.cwd + '/' + migration.path);
+ } catch (ex) {
+ if (ex instanceof ReferenceError) {
+ if (removedGlobals[ex.message]) {
+ log.info(ex.message, 'Initiating removal of old globals...');
+ return removedGlobals[ex.message](
+ migration.path,
+ tryRequire.bind(this, migration)
+ );
+ } else {
+ log.error(
+ ex.stack,
+ 'Unknown failure, please check your file',
+ migration
+ );
+
+ throw new Error(
+ 'Unhandled ReferenceError while transition. Please ' +
+ 'fix the issues and rerun the transitioner again.'
+ );
+ }
+ } else if (removedGlobals[ex.message]) {
+ return removedGlobals[ex.message](
+ migration,
+ tryRequire.bind(this, migration)
+ );
+ } else {
+ log.error(ex.stack, migration);
+ throw new Error(
+ 'Unhandled Error while transition. Please ' +
+ 'fix the issues and rerun the transitioner again.'
+ );
+ }
+ }
+}
+
+module.exports = tryRequire;
diff --git a/lib/transitions/update-version.js b/lib/transitions/update-version.js
new file mode 100644
index 00000000..e67744b8
--- /dev/null
+++ b/lib/transitions/update-version.js
@@ -0,0 +1,48 @@
+var balanced = require('balanced-match');
+var fs = require('fs');
+
+function updateVersion (required, migration, internals, version) {
+ var data = fs.readFileSync(migration.path, 'utf8');
+ var searchString = 'exports.down';
+ var balance;
+ var metaIndex;
+ var plus = 1;
+ var sub;
+
+ if (required._meta) {
+ searchString = 'exports._meta';
+ }
+
+ metaIndex = data.indexOf(searchString);
+ sub = data.substring(metaIndex);
+ balance = balanced('{', '}', sub);
+
+ if (sub[balance.end + 1] === ';') {
+ ++plus;
+ }
+
+ sub = sub.substring(0, balanced.end);
+
+ if (required._meta) {
+ required._meta.version = version;
+
+ data =
+ data.substring(0, metaIndex) +
+ sub.replace(
+ sub.substring(balance.start, balance.end + 1),
+ JSON.stringify(required._meta, null, 2)
+ ) +
+ data.substring(metaIndex + balance.end + plus);
+ } else {
+ data =
+ data.substring(0, metaIndex + balance.end + plus) +
+ '\n\nexports._meta = ' +
+ JSON.stringify({ version: version }, null, 2) +
+ ';' +
+ data.substring(metaIndex + balance.end + plus);
+ }
+
+ fs.writeFileSync(migration.path, data, 'utf8');
+}
+
+module.exports = updateVersion;
diff --git a/lib/util.js b/lib/util.js
deleted file mode 100644
index fa9b83a2..00000000
--- a/lib/util.js
+++ /dev/null
@@ -1,69 +0,0 @@
-
-function isIncludedInUp(migration, destination) {
- if(!destination) {
- return true;
- }
- var migrationTest = migration.name.substring(0, Math.min(migration.name.length, destination.length));
- var destinationTest = destination.substring(0, Math.min(migration.name.length, destination.length));
- return migrationTest <= destinationTest;
-}
-
-exports.filterUp = function(allMigrations, completedMigrations, destination, count) {
- var sortFn = function(a, b) {
- return a.name.slice(0, a.name.indexOf('-')) - b.name.slice(0, b.name.indexOf('-'));
- };
-
- return allMigrations.sort(sortFn)
- .filter(function(migration) {
- var hasRun = completedMigrations.some(function(completedMigration) {
- return completedMigration.name === migration.name;
- });
- return !hasRun;
- })
- .filter(function(migration) {
- return isIncludedInUp(migration, destination);
- })
- .slice(0, count);
-};
-
-exports.filterDown = function(completedMigrations, count) {
- return completedMigrations.slice(0, count);
-};
-
-
-exports.lpad = function(str, padChar, totalLength) {
- str = str.toString();
- var neededPadding = totalLength - str.length;
- for (var i = 0; i < neededPadding; i++) {
- str = padChar + str;
- }
- return str;
-};
-
-exports.shallowCopy = function(obj) {
- var newObj = {};
- for (var prop in obj) {
- newObj[prop] = obj[prop];
- }
- return newObj;
-};
-
-exports.toArray = function(obj) {
- var arr = [];
- for (var prop in obj) {
- arr[prop] = obj[prop];
- }
- return arr;
-};
-
-exports.isArray = function(obj) {
- return Object.prototype.toString.call(obj) == '[object Array]';
-};
-
-exports.isFunction = function(obj) {
- return typeof(obj) == 'function';
-};
-
-exports.isString = function(obj) {
- return typeof(obj) == 'string';
-};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..fb6d59ed
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3209 @@
+{
+ "name": "db-migrate",
+ "version": "0.11.1",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@sinonjs/formatio": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
+ "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==",
+ "dev": true,
+ "requires": {
+ "samsam": "1.3.0"
+ }
+ },
+ "acorn": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
+ "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
+ "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
+ }
+ }
+ },
+ "ajv": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "dev": true,
+ "requires": {
+ "co": "^4.6.0",
+ "fast-deep-equal": "^1.0.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.3.0"
+ }
+ },
+ "ajv-keywords": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
+ "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
+ "dev": true
+ },
+ "align-text": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2",
+ "longest": "^1.0.1",
+ "repeat-string": "^1.5.2"
+ }
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
+ "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+ },
+ "async": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+ "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "balanced-match": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+ "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
+ },
+ "bignumber.js": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.4.tgz",
+ "integrity": "sha512-LDXpJKVzEx2/OqNbG9mXBNvHuiRL4PzHCGfnANHMJ+fv68Ads3exDVJeGDJws+AoNEuca93bU3q+S0woeUaCdg==",
+ "dev": true
+ },
+ "bluebird": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
+ "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
+ },
+ "bossy": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/bossy/-/bossy-3.0.4.tgz",
+ "integrity": "sha1-+a6fJugbQaMY9O4Ng2huSlwlB7k=",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x",
+ "joi": "10.x.x"
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ }
+ }
+ },
+ "buffer-from": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
+ "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "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
+ },
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+ "dev": true,
+ "optional": true
+ },
+ "center-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "align-text": "^0.1.3",
+ "lazy-cache": "^1.0.3"
+ }
+ },
+ "chalk": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
+ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "supports-color": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
+ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "chardet": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
+ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
+ "dev": true
+ },
+ "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": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "center-align": "^0.1.1",
+ "right-align": "^0.1.1",
+ "wordwrap": "0.0.2"
+ },
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+ "dev": true,
+ "optional": 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": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/code/-/code-4.1.0.tgz",
+ "integrity": "sha1-IJrRHQWvigwceq9pTZ+k0sfZW4U=",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
+ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.1.1"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.1.tgz",
+ "integrity": "sha512-s8+wktIuDSLffCywiwSxQOMqtPxML11a/dtHE17tMn4B1MSWw/C22EKf7M2KGUBcDaVFEGT+S8N02geDXeuNKg=="
+ },
+ "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"
+ }
+ },
+ "contains-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "dev": true
+ },
+ "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=",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "cycle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+ "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI="
+ },
+ "db-migrate-base": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/db-migrate-base/-/db-migrate-base-1.5.3.tgz",
+ "integrity": "sha512-dbJHFVYIY75vSOL3MD4qbpjilcwrYn7ZnTqsdA2AGs6w1mYugspfWEBea+uMgJVH/YsUFxw2AYPKRHCHUjhirw==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.1.1"
+ }
+ },
+ "db-migrate-mysql": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/db-migrate-mysql/-/db-migrate-mysql-1.1.10.tgz",
+ "integrity": "sha1-TB1e8R9ZO1qPHxIkkIO+5xWnzKw=",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.2.1",
+ "db-migrate-base": "^1.2.5",
+ "moment": "^2.11.2",
+ "mysql": "^2.10.2"
+ }
+ },
+ "db-migrate-shared": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/db-migrate-shared/-/db-migrate-shared-1.2.0.tgz",
+ "integrity": "sha512-65k86bVeHaMxb2L0Gw3y5V+CgZSRwhVQMwDMydmw5MvIpHHwD6SmBciqIwHsZfzJ9yzV/yYhdRefRM6FV5/siw=="
+ },
+ "db-migrate-sqlite3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/db-migrate-sqlite3/-/db-migrate-sqlite3-0.2.1.tgz",
+ "integrity": "sha1-ox1nudNtz5lWSymLz9C5hx+2bmc=",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.1.1",
+ "db-migrate-base": "^1.2.5",
+ "sqlite3": "^3.1.4"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true,
+ "optional": true
+ },
+ "deep-equal": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz",
+ "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0="
+ },
+ "deep-extend": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.0.tgz",
+ "integrity": "sha1-bvSgmwX5iw41jW2T1Mo8rsZnKAM="
+ },
+ "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
+ },
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true,
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "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": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dotenv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-2.0.0.tgz",
+ "integrity": "sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk="
+ },
+ "error-ex": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.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
+ },
+ "eslint": {
+ "version": "4.19.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
+ "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^5.3.0",
+ "babel-code-frame": "^6.22.0",
+ "chalk": "^2.1.0",
+ "concat-stream": "^1.6.0",
+ "cross-spawn": "^5.1.0",
+ "debug": "^3.1.0",
+ "doctrine": "^2.1.0",
+ "eslint-scope": "^3.7.1",
+ "eslint-visitor-keys": "^1.0.0",
+ "espree": "^3.5.4",
+ "esquery": "^1.0.0",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^2.0.0",
+ "functional-red-black-tree": "^1.0.1",
+ "glob": "^7.1.2",
+ "globals": "^11.0.1",
+ "ignore": "^3.3.3",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^3.0.6",
+ "is-resolvable": "^1.0.0",
+ "js-yaml": "^3.9.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.4",
+ "minimatch": "^3.0.2",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.2",
+ "path-is-inside": "^1.0.2",
+ "pluralize": "^7.0.0",
+ "progress": "^2.0.0",
+ "regexpp": "^1.0.1",
+ "require-uncached": "^1.0.3",
+ "semver": "^5.3.0",
+ "strip-ansi": "^4.0.0",
+ "strip-json-comments": "~2.0.1",
+ "table": "4.0.2",
+ "text-table": "~0.2.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"
+ }
+ }
+ }
+ },
+ "eslint-config-hapi": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-hapi/-/eslint-config-hapi-10.1.0.tgz",
+ "integrity": "sha512-tAUedyvZla1qKt6jhOx7mj5tYDVCwdSyImpEK7wk/A/atKUjg18aHUK6Q6qWWM6rq21I1F/A8JAhIpkk0SvFMQ==",
+ "dev": true
+ },
+ "eslint-config-standard": {
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz",
+ "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=",
+ "dev": true
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
+ "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "resolve": "^1.5.0"
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz",
+ "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.8",
+ "pkg-dir": "^1.0.0"
+ }
+ },
+ "eslint-plugin-hapi": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-hapi/-/eslint-plugin-hapi-4.1.0.tgz",
+ "integrity": "sha512-z1yUoSWArx6pXaC0FoWRFpqjbHn8QWonJiTVhJmiC14jOAT7FZKdKWCkhM4jQrgrkEK9YEv3p2HuzSf5dtWmuQ==",
+ "dev": true,
+ "requires": {
+ "hapi-capitalize-modules": "1.x.x",
+ "hapi-for-you": "1.x.x",
+ "hapi-no-var": "1.x.x",
+ "hapi-scope-start": "2.x.x",
+ "no-arrowception": "1.x.x"
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.10.0.tgz",
+ "integrity": "sha1-+gkIPVp1KI35xsfQn+EiVZhWVec=",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^1.1.1",
+ "contains-path": "^0.1.0",
+ "debug": "^2.6.8",
+ "doctrine": "1.5.0",
+ "eslint-import-resolver-node": "^0.3.1",
+ "eslint-module-utils": "^2.2.0",
+ "has": "^1.0.1",
+ "lodash": "^4.17.4",
+ "minimatch": "^3.0.3",
+ "read-pkg-up": "^2.0.0"
+ },
+ "dependencies": {
+ "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"
+ }
+ }
+ }
+ },
+ "eslint-plugin-node": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz",
+ "integrity": "sha512-xhPXrh0Vl/b7870uEbaumb2Q+LxaEcOQ3kS1jtIXanBAwpMre1l5q/l2l/hESYJGEFKuI78bp6Uw50hlpr7B+g==",
+ "dev": true,
+ "requires": {
+ "ignore": "^3.3.6",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.3.3",
+ "semver": "5.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+ "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-promise": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz",
+ "integrity": "sha512-2WO+ZFh7vxUKRfR0cOIMrWgYKdR6S1AlOezw6pC52B6oYpd5WFghN+QHxvrRdZMtbo8h3dfUZ2o1rWb0UPbKtg==",
+ "dev": true
+ },
+ "eslint-plugin-standard": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz",
+ "integrity": "sha1-NNDJFbRe3G8BA5PH7vOCOwhWXPI=",
+ "dev": true
+ },
+ "eslint-scope": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
+ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+ "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.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
+ "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "external-editor": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
+ "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.4.0",
+ "iconv-lite": "^0.4.17",
+ "tmp": "^0.0.33"
+ }
+ },
+ "eyes": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+ "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
+ },
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": 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": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "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"
+ }
+ },
+ "fill-keys": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz",
+ "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=",
+ "dev": true,
+ "requires": {
+ "is-object": "~1.0.1",
+ "merge-descriptors": "~1.0.0"
+ }
+ },
+ "final-fs": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/final-fs/-/final-fs-1.6.1.tgz",
+ "integrity": "sha1-1tzZLvb+T+jAer1WjHE1YQ7eMjY=",
+ "requires": {
+ "node-fs": "~0.1.5",
+ "when": "~2.0.1"
+ }
+ },
+ "find-rc": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/find-rc/-/find-rc-3.0.1.tgz",
+ "integrity": "sha1-VKQXg3DxC8k3H6jRssKAmir6DM4=",
+ "dev": true
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
+ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
+ "dev": true,
+ "requires": {
+ "circular-json": "^0.3.1",
+ "del": "^2.0.2",
+ "graceful-fs": "^4.1.2",
+ "write": "^0.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "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": "11.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.4.0.tgz",
+ "integrity": "sha512-Dyzmifil8n/TmSqYDEXbm+C8yitzJQqQIlJQLNRMwa+BOUJpRC19pyVeN12JAjt61xonvXjtff+hJruTRXn5HA==",
+ "dev": true
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+ "dev": true
+ },
+ "handlebars": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz",
+ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
+ "dev": true,
+ "requires": {
+ "async": "^1.4.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.4.4",
+ "uglify-js": "^2.6"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "hapi-capitalize-modules": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/hapi-capitalize-modules/-/hapi-capitalize-modules-1.1.6.tgz",
+ "integrity": "sha1-eZEXFBXhXmqjIx5k3ac8gUZmUxg=",
+ "dev": true
+ },
+ "hapi-for-you": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hapi-for-you/-/hapi-for-you-1.0.0.tgz",
+ "integrity": "sha1-02L77o172pwseAHiB+WlzRoLans=",
+ "dev": true
+ },
+ "hapi-no-var": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hapi-no-var/-/hapi-no-var-1.0.1.tgz",
+ "integrity": "sha512-kk2xyyTzI+eQ/oA1rO4eVdCpYsrPHVERHa6+mTHD08XXFLaAkkaEs6reMg1VyqGh2o5xPt//DO4EhCacLx/cRA==",
+ "dev": true
+ },
+ "hapi-scope-start": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/hapi-scope-start/-/hapi-scope-start-2.1.1.tgz",
+ "integrity": "sha1-dJWnJv5yt7yo3izcwdh82M5qtPI=",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+ "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.0.2"
+ }
+ },
+ "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
+ },
+ "hoek": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
+ "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
+ "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==",
+ "dev": true
+ },
+ "i": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz",
+ "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0="
+ },
+ "iconv-lite": {
+ "version": "0.4.21",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz",
+ "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ignore": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz",
+ "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==",
+ "dev": true
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflection": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
+ "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY="
+ },
+ "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.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
+ },
+ "inquirer": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
+ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.0",
+ "cli-cursor": "^2.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^2.0.4",
+ "figures": "^2.0.0",
+ "lodash": "^4.3.0",
+ "mute-stream": "0.0.7",
+ "run-async": "^2.2.0",
+ "rx-lite": "^4.0.8",
+ "rx-lite-aggregates": "^4.0.8",
+ "string-width": "^2.1.0",
+ "strip-ansi": "^4.0.0",
+ "through": "^2.3.6"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
+ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "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
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isemail": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz",
+ "integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "items": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/items/-/items-2.1.1.tgz",
+ "integrity": "sha1-i9FtnIOxlSneWuoyGsqtp4NkoZg=",
+ "dev": true
+ },
+ "joi": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz",
+ "integrity": "sha512-hBF3LcqyAid+9X/pwg+eXjD2QBZI5eXnBFJYaAkH4SK3mp9QSRiiQnDYlmlz5pccMvnLcJRS4whhDOTCkmsAdQ==",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x",
+ "isemail": "2.x.x",
+ "items": "2.x.x",
+ "topo": "2.x.x"
+ }
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz",
+ "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+ "dev": 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-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "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=",
+ "dev": true
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "just-extend": {
+ "version": "1.1.27",
+ "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz",
+ "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "lab": {
+ "version": "14.3.4",
+ "resolved": "https://registry.npmjs.org/lab/-/lab-14.3.4.tgz",
+ "integrity": "sha512-IFnuYVRd6CtnFTFgUbjPCFrrCIked5BvGH/dX+/h+6pi3IrQrK21JsKy/J1CshEm6sMe980+oswtK8lZCusHSA==",
+ "dev": true,
+ "requires": {
+ "bossy": "3.x.x",
+ "code": "4.1.x",
+ "diff": "3.5.x",
+ "eslint": "4.19.x",
+ "eslint-config-hapi": "10.x.x",
+ "eslint-plugin-hapi": "4.x.x",
+ "espree": "3.5.x",
+ "find-rc": "3.0.x",
+ "handlebars": "4.x.x",
+ "hoek": "4.x.x",
+ "items": "2.x.x",
+ "json-stable-stringify": "1.x.x",
+ "json-stringify-safe": "5.x.x",
+ "mkdirp": "0.5.x",
+ "seedrandom": "2.4.x",
+ "source-map": "0.6.x",
+ "source-map-support": "0.4.x",
+ "supports-color": "4.4.x"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
+ "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^2.0.0"
+ }
+ }
+ }
+ },
+ "lazy-cache": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+ "dev": true,
+ "optional": true
+ },
+ "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"
+ }
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "dependencies": {
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ }
+ }
+ },
+ "lodash": {
+ "version": "4.17.5",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
+ "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==",
+ "dev": true
+ },
+ "lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+ },
+ "lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
+ "dev": true
+ },
+ "lolex": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.3.2.tgz",
+ "integrity": "sha512-A5pN2tkFj7H0dGIAM6MFvHKMJcPnjZsOMvR7ujCjfgW5TbV6H9vb1PgxLtHvjqNZTHsUolz+6/WEO0N1xNx2ng==",
+ "dev": true
+ },
+ "longest": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
+ "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+ "dev": true
+ },
+ "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": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "module-not-found-error": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz",
+ "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=",
+ "dev": true
+ },
+ "moment": {
+ "version": "2.22.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.0.tgz",
+ "integrity": "sha512-1muXCh8jb1N/gHRbn9VDUBr0GYb8A/aVcHlII9QSB68a50spqEVLIGN6KVmCOnSvJrUhC0edGgKU5ofnGXdYdg==",
+ "dev": true
+ },
+ "mongodb-uri": {
+ "version": "0.9.7",
+ "resolved": "https://registry.npmjs.org/mongodb-uri/-/mongodb-uri-0.9.7.tgz",
+ "integrity": "sha1-D3ca0W9IOuZfQoeWlCjp+8SqYYE="
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
+ },
+ "mysql": {
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.15.0.tgz",
+ "integrity": "sha512-C7tjzWtbN5nzkLIV+E8Crnl9bFyc7d3XJcBAvHKEVkjrYjogz3llo22q6s/hw+UcsE4/844pDob9ac+3dVjQSA==",
+ "dev": true,
+ "requires": {
+ "bignumber.js": "4.0.4",
+ "readable-stream": "2.3.3",
+ "safe-buffer": "5.1.1",
+ "sqlstring": "2.3.0"
+ }
+ },
+ "nan": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
+ "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=",
+ "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
+ },
+ "ncp": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz",
+ "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY="
+ },
+ "nise": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/nise/-/nise-1.3.2.tgz",
+ "integrity": "sha512-KPKb+wvETBiwb4eTwtR/OsA2+iijXP+VnlSFYJo3EHjm2yjek1NWxHOUQat3i7xNLm1Bm18UA5j5Wor0yO2GtA==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/formatio": "^2.0.0",
+ "just-extend": "^1.1.27",
+ "lolex": "^2.3.2",
+ "path-to-regexp": "^1.7.0",
+ "text-encoding": "^0.6.4"
+ }
+ },
+ "no-arrowception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/no-arrowception/-/no-arrowception-1.0.0.tgz",
+ "integrity": "sha1-W/PpXrnEG1c4SoBTM9qjtzTuMno=",
+ "dev": true
+ },
+ "node-fs": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/node-fs/-/node-fs-0.1.7.tgz",
+ "integrity": "sha1-MjI8zLRsn78PwRgS1FAhzDHTJbs="
+ },
+ "normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "is-builtin-module": "^1.0.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ },
+ "dependencies": {
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ }
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
+ "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "parse-database-url": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/parse-database-url/-/parse-database-url-0.3.0.tgz",
+ "integrity": "sha1-NpZmMh6SfJreY838Gqr2+zdFPQ0=",
+ "requires": {
+ "mongodb-uri": ">= 0.9.7"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "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
+ },
+ "path-parse": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
+ },
+ "path-to-regexp": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+ "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+ "dev": true,
+ "requires": {
+ "isarray": "0.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ }
+ }
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
+ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0"
+ }
+ },
+ "pkginfo": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz",
+ "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8="
+ },
+ "pluralize": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+ "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": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
+ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+ "dev": true
+ },
+ "prompt": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz",
+ "integrity": "sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4=",
+ "requires": {
+ "colors": "^1.1.2",
+ "pkginfo": "0.x.x",
+ "read": "1.0.x",
+ "revalidator": "0.1.x",
+ "utile": "0.3.x",
+ "winston": "2.1.x"
+ }
+ },
+ "proxyquire": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-1.8.0.tgz",
+ "integrity": "sha1-AtUUpb7ZhvBMuyCTrxZ0FTX3ntw=",
+ "dev": true,
+ "requires": {
+ "fill-keys": "^1.0.2",
+ "module-not-found-error": "^1.0.0",
+ "resolve": "~1.1.7"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "rc": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz",
+ "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=",
+ "requires": {
+ "deep-extend": "~0.4.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "deep-extend": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
+ "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ }
+ }
+ },
+ "read": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+ "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
+ "requires": {
+ "mute-stream": "~0.0.4"
+ }
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.0.3",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "regexpp": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
+ "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "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": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.0.tgz",
+ "integrity": "sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA==",
+ "requires": {
+ "path-parse": "^1.0.5"
+ }
+ },
+ "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": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "revalidator": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz",
+ "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs="
+ },
+ "right-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "align-text": "^0.1.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "requires": {
+ "glob": "^7.0.5"
+ }
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "rx-lite": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
+ "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
+ "dev": true
+ },
+ "rx-lite-aggregates": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
+ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+ "dev": true,
+ "requires": {
+ "rx-lite": "*"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "samsam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz",
+ "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==",
+ "dev": true
+ },
+ "seedrandom": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",
+ "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw=",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "sinon": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz",
+ "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/formatio": "^2.0.0",
+ "diff": "^3.1.0",
+ "lodash.get": "^4.4.2",
+ "lolex": "^2.2.0",
+ "nise": "^1.2.0",
+ "supports-color": "^5.1.0",
+ "type-detect": "^4.0.5"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
+ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "slice-ansi": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
+ "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "spdx-correct": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
+ "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
+ "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
+ "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==",
+ "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
+ },
+ "sqlite3": {
+ "version": "3.1.13",
+ "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-3.1.13.tgz",
+ "integrity": "sha512-JxXKPJnkZ6NuHRojq+g2WXWBt3M1G9sjZaYiHEWSTGijDM3cwju/0T2XbWqMXFmPqDgw+iB7zKQvnns4bvzXlw==",
+ "dev": true,
+ "requires": {
+ "nan": "~2.7.0",
+ "node-pre-gyp": "~0.6.38"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "ajv": {
+ "version": "4.11.8",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "co": "^4.6.0",
+ "json-stable-stringify": "^1.0.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "bundled": true,
+ "dev": true
+ },
+ "assert-plus": {
+ "version": "0.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "bundled": true,
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.6.0",
+ "bundled": true,
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.6.0",
+ "bundled": true,
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "block-stream": {
+ "version": "0.0.9",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.0"
+ }
+ },
+ "boom": {
+ "version": "2.10.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.8",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "bundled": true,
+ "dev": true
+ },
+ "co": {
+ "version": "4.6.0",
+ "bundled": true,
+ "dev": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "cryptiles": {
+ "version": "2.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "boom": "2.x.x"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "deep-extend": {
+ "version": "0.4.2",
+ "bundled": true,
+ "dev": true
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "ecc-jsbn": {
+ "version": "0.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "jsbn": "~0.1.0"
+ }
+ },
+ "extend": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "bundled": true,
+ "dev": true
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "bundled": true,
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.1.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.5",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "fstream": {
+ "version": "1.0.11",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "inherits": "~2.0.0",
+ "mkdirp": ">=0.5 0",
+ "rimraf": "2"
+ }
+ },
+ "fstream-ignore": {
+ "version": "1.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "fstream": "^1.0.0",
+ "inherits": "2",
+ "minimatch": "^3.0.0"
+ }
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "dev": true,
+ "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"
+ }
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "glob": {
+ "version": "7.1.2",
+ "bundled": true,
+ "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"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "bundled": true,
+ "dev": true
+ },
+ "har-schema": {
+ "version": "1.0.5",
+ "bundled": true,
+ "dev": true
+ },
+ "har-validator": {
+ "version": "4.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ajv": "^4.9.1",
+ "har-schema": "^1.0.5"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "hawk": {
+ "version": "3.1.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "boom": "2.x.x",
+ "cryptiles": "2.x.x",
+ "hoek": "2.x.x",
+ "sntp": "1.x.x"
+ }
+ },
+ "hoek": {
+ "version": "2.16.3",
+ "bundled": true,
+ "dev": true
+ },
+ "http-signature": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "assert-plus": "^0.2.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.4",
+ "bundled": true,
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "bundled": true,
+ "dev": true
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "bundled": true,
+ "dev": true
+ },
+ "json-stable-stringify": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "mime-db": {
+ "version": "1.30.0",
+ "bundled": true,
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.17",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.30.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true,
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "node-pre-gyp": {
+ "version": "0.6.38",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hawk": "3.1.3",
+ "mkdirp": "^0.5.1",
+ "nopt": "^4.0.1",
+ "npmlog": "^4.0.2",
+ "rc": "^1.1.7",
+ "request": "2.81.0",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^2.2.1",
+ "tar-pack": "^3.4.0"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "dev": true,
+ "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",
+ "bundled": true,
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "bundled": true,
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "osenv": {
+ "version": "0.1.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "performance-now": {
+ "version": "0.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "bundled": true,
+ "dev": true
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "bundled": true,
+ "dev": true
+ },
+ "qs": {
+ "version": "6.4.0",
+ "bundled": true,
+ "dev": true
+ },
+ "rc": {
+ "version": "1.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "deep-extend": "~0.4.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.0.3",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "request": {
+ "version": "2.81.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.6.0",
+ "aws4": "^1.2.1",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.5",
+ "extend": "~3.0.0",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.1.1",
+ "har-validator": "~4.2.1",
+ "hawk": "~3.1.3",
+ "http-signature": "~1.1.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.7",
+ "oauth-sign": "~0.8.1",
+ "performance-now": "^0.2.0",
+ "qs": "~6.4.0",
+ "safe-buffer": "^5.0.1",
+ "stringstream": "~0.0.4",
+ "tough-cookie": "~2.3.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.0.0"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.5"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "semver": {
+ "version": "5.4.1",
+ "bundled": true,
+ "dev": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "sntp": {
+ "version": "1.0.9",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ },
+ "sshpk": {
+ "version": "1.13.1",
+ "bundled": true,
+ "dev": 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",
+ "tweetnacl": "~0.14.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "stringstream": {
+ "version": "0.0.5",
+ "bundled": true,
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "tar": {
+ "version": "2.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "block-stream": "*",
+ "fstream": "^1.0.2",
+ "inherits": "2"
+ }
+ },
+ "tar-pack": {
+ "version": "3.4.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "debug": "^2.2.0",
+ "fstream": "^1.0.10",
+ "fstream-ignore": "^1.0.5",
+ "once": "^1.3.3",
+ "readable-stream": "^2.1.4",
+ "rimraf": "^2.5.1",
+ "tar": "^2.2.1",
+ "uid-number": "^0.0.6"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.3.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "punycode": "^1.4.1"
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "uid-number": {
+ "version": "0.0.6",
+ "bundled": true,
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "wide-align": {
+ "version": "1.1.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "sqlstring": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.0.tgz",
+ "integrity": "sha1-UluKT9Jtb3GqYegipsr5dtMa0qg=",
+ "dev": true
+ },
+ "ssh2": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.5.4.tgz",
+ "integrity": "sha1-G/a2soyW6u8mf01sRqWiUXpZnic=",
+ "requires": {
+ "ssh2-streams": "~0.1.15"
+ }
+ },
+ "ssh2-streams": {
+ "version": "0.1.20",
+ "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.1.20.tgz",
+ "integrity": "sha1-URGNFUVV31Rp7h9n4M8efoosDjo=",
+ "requires": {
+ "asn1": "~0.2.0",
+ "semver": "^5.1.0",
+ "streamsearch": "~0.1.2"
+ }
+ },
+ "stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
+ },
+ "streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+ },
+ "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"
+ }
+ },
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.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"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ }
+ }
+ },
+ "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": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+ },
+ "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": "4.0.2",
+ "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
+ "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^5.2.3",
+ "ajv-keywords": "^2.1.0",
+ "chalk": "^2.1.0",
+ "lodash": "^4.17.4",
+ "slice-ansi": "1.0.0",
+ "string-width": "^2.1.1"
+ }
+ },
+ "text-encoding": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
+ "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
+ "dev": true
+ },
+ "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
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "topo": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz",
+ "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ },
+ "tunnel-ssh": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/tunnel-ssh/-/tunnel-ssh-4.1.4.tgz",
+ "integrity": "sha512-CjBqboGvAbM7iXSX2F95kzoI+c2J81YkrHbyyo4SWNKCzU6w5LfEvXBCHu6PPriYaNvfhMKzD8bFf5Vl14YTtg==",
+ "requires": {
+ "debug": "2.6.9",
+ "lodash.defaults": "^4.1.0",
+ "ssh2": "0.5.4"
+ }
+ },
+ "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"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "2.8.29",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "source-map": "~0.5.1",
+ "uglify-to-browserify": "~1.0.0",
+ "yargs": "~3.10.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "dev": true,
+ "optional": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "utile": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/utile/-/utile-0.3.0.tgz",
+ "integrity": "sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo=",
+ "requires": {
+ "async": "~0.9.0",
+ "deep-equal": "~0.2.1",
+ "i": "0.3.x",
+ "mkdirp": "0.x.x",
+ "ncp": "1.0.x",
+ "rimraf": "2.x.x"
+ }
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
+ "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "when": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/when/-/when-2.0.1.tgz",
+ "integrity": "sha1-jYcv4V5oQkyRtLck6EjggH2rZkI="
+ },
+ "which": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
+ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+ "dev": true,
+ "optional": true
+ },
+ "winston": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz",
+ "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=",
+ "requires": {
+ "async": "~1.0.0",
+ "colors": "1.0.x",
+ "cycle": "1.0.x",
+ "eyes": "0.1.x",
+ "isstream": "0.1.x",
+ "pkginfo": "0.3.x",
+ "stack-trace": "0.0.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
+ "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k="
+ },
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
+ },
+ "pkginfo": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
+ "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE="
+ }
+ }
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ },
+ "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"
+ }
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "camelcase": "^1.0.2",
+ "cliui": "^2.1.0",
+ "decamelize": "^1.0.0",
+ "window-size": "0.1.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 05285080..4a7ad5b5 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "db-migrate",
"description": "Database migration framework for node.js",
- "author": "Jeff Kunkle",
+ "author": "Tobias Gurtick",
"bin": "./bin/db-migrate",
"keywords": [
"database",
@@ -9,45 +9,76 @@
"migrate",
"migration",
"sqlite",
- "mysql"
+ "mysql",
+ "pg",
+ "postgre",
+ "sql",
+ "migrations",
+ "db-migrate",
+ "postgresql",
+ "extensible",
+ "expandable",
+ "nosql",
+ "coffee",
+ "coffescript",
+ "mongo",
+ "mongodb",
+ "seed",
+ "seeds",
+ "seeder",
+ "migrator",
+ "programatic",
+ "programable",
+ "api"
],
- "version": "0.9.11",
+ "version": "0.11.1",
"engines": {
"node": ">=0.6.0"
},
"bugs": {
- "url": "https://github.com/kunklejr/node-db-migrate/issues"
+ "url": "https://github.com/db-migrate/node-db-migrate/issues"
},
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/kunklejr/node-db-migrate.git"
+ "url": "https://github.com/db-migrate/node-db-migrate.git"
},
"dependencies": {
- "async": "~0.9.0",
- "bluebird": "^2.9.12",
- "dotenv": "~1.0.0",
+ "balanced-match": "^0.4.2",
+ "bluebird": "^3.1.1",
+ "db-migrate-shared": "^1.2.0",
+ "deep-extend": "^0.5.0",
+ "dotenv": "^2.0.0",
"final-fs": "^1.6.0",
+ "inflection": "^1.10.0",
"mkdirp": "~0.5.0",
- "moment": "~2.9.0",
- "mongodb": "^2.0.23",
- "mysql": "~2.5.4",
"optimist": "~0.6.1",
- "parse-database-url": "~0.2.2",
- "pg": "~4.3.0",
- "pg-native": "^1.8.0",
- "pkginfo": "~0.3.0",
- "semver": "~4.3.0",
- "sqlite3": "~3.0.4"
+ "parse-database-url": "~0.3.0",
+ "pkginfo": "^0.4.0",
+ "prompt": "^1.0.0",
+ "rc": "^1.1.6",
+ "resolve": "^1.1.6",
+ "semver": "^5.3.0",
+ "tunnel-ssh": "^4.0.0"
},
"devDependencies": {
- "code": "^1.3.0",
- "db-meta": "~0.4.1",
- "lab": "^5.2.1",
- "rimraf": "~2.3.2",
- "vows": "0.8.0"
+ "code": "^4.1.0",
+ "db-migrate-base": "^1.2.5",
+ "db-migrate-mysql": "^1.1.10",
+ "db-migrate-sqlite3": "^0.2.1",
+ "eslint": "^4.10.0",
+ "eslint-config-standard": "^10.2.1",
+ "eslint-plugin-import": "^2.8.0",
+ "eslint-plugin-node": "^5.2.1",
+ "eslint-plugin-promise": "^3.6.0",
+ "eslint-plugin-standard": "^3.0.1",
+ "lab": "^14.3.1",
+ "proxyquire": "^1.4.0",
+ "rimraf": "^2.6.2",
+ "sinon": "^4.1.2"
},
"scripts": {
- "test": "node node_modules/.bin/vows"
+ "pretest": "eslint *.js lib/*.js test/*.js bin/*",
+ "test": "make test-cov"
}
}
diff --git a/test/config_test.js b/test/config_test.js
index 1a33a88e..500b3837 100644
--- a/test/config_test.js
+++ b/test/config_test.js
@@ -1,148 +1,360 @@
-var vows = require('vows');
-var assert = require('assert');
+var Code = require('code');
+var Lab = require('lab');
+var lab = (exports.lab = Lab.script());
var config = require('../lib/config');
var path = require('path');
var _configLoad = config.load;
var _configLoadUrl = config.loadUrl;
-vows.describe('config').addBatch({
- 'loading from a file': {
- topic: function() {
- var configPath = path.join(__dirname, 'database.json');
- config.load = _configLoad;
- config.loadUrl = _configLoadUrl;
- config.load(configPath, 'dev');
- return config;
- },
-
- 'should remove the load function': function (config) {
- assert.isUndefined(config.load);
- },
-
- 'should remove the loadUrl function': function (config) {
- assert.isUndefined(config.loadUrl);
- },
-
- 'should export all environment settings': function (config) {
- assert.isDefined(config.dev);
- assert.isDefined(config.test);
- assert.isDefined(config.prod);
- },
-
- 'should export a getCurrent function with all current environment settings': function (config) {
- assert.isDefined(config.getCurrent);
- var current = config.getCurrent();
- assert.equal(current.env, 'dev');
- assert.equal(current.settings.driver, 'sqlite3');
- assert.equal(current.settings.filename, ':memory:');
- }
- },
-}).addBatch({
- 'loading from a broken config file': {
- topic: function() {
- var configPath = path.join(__dirname, 'database_with_syntax_error.json');
- config.load = _configLoad;
- config.loadUrl = _configLoadUrl;
- try {
- config.load(configPath, 'dev');
- } catch (e) {
- return e;
+lab.experiment('config', function () {
+ lab.experiment('loading from a file', function () {
+ var configPath = path.join(__dirname, 'database.json');
+ var _config = config.load(configPath, 'dev');
+
+ lab.test(
+ 'should export all environment settings',
+
+ function (done) {
+ Code.expect(_config.dev).to.exists();
+ Code.expect(_config.test).to.exists();
+ Code.expect(_config.prod).to.exists();
+ done();
}
- return;
- },
+ );
+
+ lab.test(
+ 'should export a getCurrent function with all current ' +
+ 'environment settings',
- 'should throw a syntax error': function (error) {
- assert.isDefined(error);
- assert.ok(error instanceof SyntaxError, "Expected broken file to produce syntax error");
+ function (done) {
+ var current;
+ Code.expect(_config.getCurrent).to.exists();
+ current = _config.getCurrent();
+ Code.expect(current.env).to.equal('dev');
+ Code.expect(current.settings.driver).to.equal('sqlite3');
+ Code.expect(current.settings.filename).to.equal(':memory:');
+ done();
+ }
+ );
+ });
+
+ lab.experiment(
+ 'loading from a broken config file',
+
+ function () {
+ var configPath = path.join(__dirname, 'database_with_syntax_error.json');
+
+ lab.test('should throw a syntax error', function (done) {
+ Code.expect(
+ config.load.bind(this, configPath, 'dev'),
+ 'Expected broken file to produce syntax error'
+ ).to.throw(SyntaxError);
+ done();
+ });
+ }
+ );
+
+ lab.experiment(
+ 'loading from a file with default env option',
+
+ function () {
+ var configPath = path.join(__dirname, 'database_with_default_env.json');
+ var _config = config.load(configPath);
+
+ lab.test(
+ 'should load a value from the default env',
+
+ function (done) {
+ var current = _config.getCurrent();
+ Code.expect(current.env).to.equal('local');
+ Code.expect(current.settings.driver).to.equal('sqlite3');
+ Code.expect(current.settings.filename).to.equal(':memory:');
+ done();
+ }
+ );
}
- }
-}).addBatch({
- 'loading from a file with ENV vars': {
- topic: function() {
- process.env['DB_MIGRATE_TEST_VAR'] = 'username_from_env';
+ );
+
+ lab.experiment(
+ 'loading from a file with default env option in ENV variable',
+
+ function () {
+ process.env.NODE_ENV = 'local';
+ var configPath = path.join(
+ __dirname,
+ 'database_with_default_env_from_env.json'
+ );
+ var _config = config.load(configPath);
+
+ lab.test(
+ 'should load a value from the env set in NODE_ENV',
+
+ function (done) {
+ var current = _config.getCurrent();
+ Code.expect(current.settings.driver).to.equal('sqlite3');
+ Code.expect(current.settings.filename).to.equal(':memory:');
+ done();
+ }
+ );
+ }
+ );
+
+ lab.experiment(
+ 'loading from a file with ENV vars',
+
+ function () {
+ process.env.DB_MIGRATE_TEST_VAR = 'username_from_env';
var configPath = path.join(__dirname, 'database_with_env.json');
- config.load = _configLoad;
- config.loadUrl = _configLoadUrl;
- config.load(configPath, 'prod');
- return config;
- },
-
- 'should load a value from the environments': function (config) {
- assert.equal(config.prod.username, 'username_from_env');
- },
-}
-
-}).addBatch({
- 'loading from a file with ENV URL': {
- topic: function() {
- process.env['DB_MIGRATE_TEST_VAR'] = 'postgres://uname:pw@server.com/dbname';
+ var _config = config.load(configPath, 'prod');
+
+ lab.test(
+ 'should load a value from the environments',
+
+ function (done) {
+ Code.expect(_config.prod.username).to.equal('username_from_env');
+ done();
+ }
+ );
+ }
+ );
+
+ lab.experiment(
+ 'loading from a file with ENV URL',
+
+ function () {
+ process.env.DB_MIGRATE_TEST_VAR = 'postgres://uname:pw@server.com/dbname';
var configPath = path.join(__dirname, 'database_with_env_url.json');
- config.load = _configLoad;
- config.loadUrl = _configLoadUrl;
- config.load(configPath, 'prod');
- return config;
- },
-
- 'should load a value from the environments': function (config) {
- var current = config.getCurrent();
- assert.equal(current.settings.driver, 'postgres');
- assert.equal(current.settings.user, 'uname');
- assert.equal(current.settings.password, 'pw');
- assert.equal(current.settings.host, 'server.com');
- assert.equal(current.settings.database, 'dbname');
- },
-}
-
-}).addBatch({
- 'loading from an URL': {
- topic: function() {
- var databaseUrl = 'postgres://uname:pw@server.com/dbname';
- config.load = _configLoad;
- config.loadUrl = _configLoadUrl;
- config.loadUrl(databaseUrl, 'dev');
- return config;
- },
-
- 'should remove the load function': function (config) {
- assert.isUndefined(config.load);
- },
-
- 'should remove the loadUrl function': function (config) {
- assert.isUndefined(config.loadUrl);
- },
-
- 'should export the settings as the current environment': function (config) {
- assert.isDefined(config.dev);
- },
-
- 'should export a getCurrent function with all current environment settings': function (config) {
- assert.isDefined(config.getCurrent);
- var current = config.getCurrent();
- assert.equal(current.env, 'dev');
- assert.equal(current.settings.driver, 'postgres');
- assert.equal(current.settings.user, 'uname');
- assert.equal(current.settings.password, 'pw');
- assert.equal(current.settings.host, 'server.com');
- assert.equal(current.settings.database, 'dbname');
+ var _config = config.load(configPath, 'prod');
+
+ lab.test(
+ 'should load a value from the environments',
+
+ function (done) {
+ var current = _config.getCurrent();
+ Code.expect(current.settings.driver).to.equal('postgres');
+ Code.expect(current.settings.user).to.equal('uname');
+ Code.expect(current.settings.password).to.equal('pw');
+ Code.expect(current.settings.host).to.equal('server.com');
+ Code.expect(current.settings.database).to.equal('dbname');
+ done();
+ }
+ );
}
- }
-}).addBatch({
- 'loading a config with null values': {
- topic: function() {
- var configPath = path.join(__dirname, 'database_with_null_values.json');
- config.load = _configLoad;
- config.loadUrl = _configLoadUrl;
- try {
- config.load(configPath, 'dev');
- }catch(e) {
- return e;
+ );
+
+ lab.experiment('loading from an URL', function () {
+ var databaseUrl = 'postgres://uname:pw@server.com/dbname';
+ var _config = config.loadUrl(databaseUrl, 'dev');
+
+ lab.test(
+ 'should export the settings as the current environment',
+
+ function (done) {
+ Code.expect(_config.dev).to.exists();
+ done();
+ }
+ );
+
+ lab.test(
+ 'should export a getCurrent function with all current ' +
+ 'environment settings',
+
+ function (done) {
+ var current;
+ Code.expect(_config.getCurrent).to.exists();
+ current = _config.getCurrent();
+ Code.expect(current.env).to.equal('dev');
+ Code.expect(current.settings.driver).to.equal('postgres');
+ Code.expect(current.settings.user).to.equal('uname');
+ Code.expect(current.settings.password).to.equal('pw');
+ Code.expect(current.settings.host).to.equal('server.com');
+ Code.expect(current.settings.database).to.equal('dbname');
+ done();
+ }
+ );
+ });
+
+ lab.experiment('loading a config with null values', function () {
+ var configPath = path.join(__dirname, 'database_with_null_values.json');
+ config.load = _configLoad;
+ config.loadUrl = _configLoadUrl;
+
+ lab.test('should something', function (done) {
+ Code.expect(config.load.bind(this, configPath, 'dev')).to.not.throw();
+ done();
+ });
+ });
+
+ lab.experiment('loading a url from url property', function () {
+ lab.test('should export a valid config', function (done) {
+ var databaseUrl = {
+ dev: {
+ url: 'postgres://uname:pw@server.com/dbname'
+ }
+ };
+ var cfg = config.loadObject(databaseUrl, 'dev');
+
+ Code.expect(cfg.getCurrent).to.exists();
+ var current = cfg.getCurrent();
+ Code.expect(current.env).to.equal('dev');
+ Code.expect(current.settings.url).to.not.exists();
+ Code.expect(current.settings.driver).to.equal('postgres');
+ Code.expect(current.settings.user).to.equal('uname');
+ Code.expect(current.settings.password).to.equal('pw');
+ Code.expect(current.settings.host).to.equal('server.com');
+ Code.expect(current.settings.database).to.equal('dbname');
+
+ done();
+ });
+
+ lab.test('should export the value if specified in suboject', function (
+ done
+ ) {
+ var databaseUrl = {
+ dev: {
+ url: {
+ value: 'http://example.com'
+ }
}
- return null;
- },
+ };
+ var cfg = config.loadObject(databaseUrl, 'dev');
+
+ Code.expect(cfg.getCurrent).to.exists();
+ var current = cfg.getCurrent();
+ Code.expect(current.env).to.equal('dev');
+ Code.expect(current.settings.url).to.equal('http://example.com');
+
+ done();
+ });
+ });
+
+ lab.experiment('loading from an URL and overwriting it', function () {
+ var databaseUrl = {
+ dev: {
+ url: 'postgres://uname:pw@server.com/dbname',
+ overwrite: {
+ ssl: true
+ }
+ }
+ };
+
+ var cfg = config.loadObject(databaseUrl, 'dev');
+
+ lab.test('should export the settings as the current environment', function (
+ done
+ ) {
+ Code.expect(cfg.dev).to.exists();
+ done();
+ });
+
+ lab.test(
+ 'should export a getCurrent function with all current environment settings',
+ function (done) {
+ Code.expect(cfg.getCurrent).to.exists();
+ var current = cfg.getCurrent();
+ Code.expect(current.env).to.equal('dev');
+ Code.expect(current.settings.url).to.not.exists();
+ Code.expect(current.settings.overwrite).to.not.exists();
+ Code.expect(current.settings.driver).to.equal('postgres');
+ Code.expect(current.settings.user).to.equal('uname');
+ Code.expect(current.settings.password).to.equal('pw');
+ Code.expect(current.settings.host).to.equal('server.com');
+ Code.expect(current.settings.database).to.equal('dbname');
+ Code.expect(current.settings.ssl).to.equal(true);
+
+ done();
+ }
+ );
+ });
+
+ lab.experiment(
+ 'loading from an ENV URL within the object and overwriting it',
+ function () {
+ lab.test(
+ 'should export a getCurrent function with all current environment settings',
+ function (done, cleanup) {
+ process.env.DATABASE_URL = 'postgres://uname:pw@server.com/dbname';
+ var databaseUrl = {
+ dev: {
+ url: { ENV: 'DATABASE_URL' },
+ overwrite: {
+ ssl: true
+ }
+ }
+ };
+ var cfg = config.loadObject(databaseUrl, 'dev');
+
+ cleanup(function (next) {
+ delete process.env.DATABASE_URL;
+ next();
+ });
+
+ Code.expect(cfg.getCurrent).to.exists();
+ var current = cfg.getCurrent();
+ Code.expect(current.env).to.equal('dev');
+ Code.expect(current.settings.url).to.not.exists();
+ Code.expect(current.settings.overwrite).to.not.exists();
+ Code.expect(current.settings.driver).to.equal('postgres');
+ Code.expect(current.settings.user).to.equal('uname');
+ Code.expect(current.settings.password).to.equal('pw');
+ Code.expect(current.settings.host).to.equal('server.com');
+ Code.expect(current.settings.database).to.equal('dbname');
+ Code.expect(current.settings.ssl).to.equal(true);
+
+ done();
+ }
+ );
+ }
+ );
+
+ lab.experiment(
+ 'loading from an ENV URL within the object and extending it from the ENV',
+ function () {
+ lab.test('', function (done, cleanup) {
+ process.env.DATABASE_URL =
+ 'postgres://uname:pw@server.com/dbname?ssl=false&testing=false';
+ var databaseUrl = {
+ dev: {
+ url: {
+ ENV: 'DATABASE_URL'
+ },
+ overwrite: {
+ ssl: true,
+ cache: false
+ },
+ addIfNotExists: {
+ native: true, // this on is new
+ cache: true, // overwrite should have higher priority
+ testing: true // already in config do not overwrite
+ }
+ }
+ };
+ var cfg = config.loadObject(databaseUrl, 'dev');
+
+ cleanup(function (next) {
+ delete process.env.DATABASE_URL;
+ next();
+ });
+
+ Code.expect(cfg.getCurrent).to.exists();
+ var current = cfg.getCurrent();
+ Code.expect(current.env).to.equal('dev');
+ Code.expect(current.settings.url).to.not.exists();
+ Code.expect(current.settings.overwrite).to.not.exists();
+ Code.expect(current.settings.addIfNotExists).to.not.exists();
+ Code.expect(current.settings.driver).to.equal('postgres');
+ Code.expect(current.settings.user).to.equal('uname');
+ Code.expect(current.settings.password).to.equal('pw');
+ Code.expect(current.settings.host).to.equal('server.com');
+ Code.expect(current.settings.database).to.equal('dbname');
+ Code.expect(current.settings.native).to.equal(true);
+ Code.expect(current.settings.testing).to.equal('false');
+ Code.expect(current.settings.cache).to.equal(false);
+ Code.expect(current.settings.ssl).to.equal(true);
- 'should something': function(err) {
- assert.isNull(err);
+ done();
+ });
}
- }
-}).export(module);
+ );
+});
diff --git a/test/database_with_default_env.json b/test/database_with_default_env.json
new file mode 100644
index 00000000..1c03578e
--- /dev/null
+++ b/test/database_with_default_env.json
@@ -0,0 +1,7 @@
+{
+ "defaultEnv": "local",
+ "local": {
+ "driver": "sqlite3",
+ "filename": ":memory:"
+ }
+}
diff --git a/test/database_with_default_env_from_env.json b/test/database_with_default_env_from_env.json
new file mode 100644
index 00000000..12100ca0
--- /dev/null
+++ b/test/database_with_default_env_from_env.json
@@ -0,0 +1,7 @@
+{
+ "defaultEnv": {"ENV": "NODE_ENV"},
+ "local": {
+ "driver": "sqlite3",
+ "filename": ":memory:"
+ }
+}
diff --git a/test/driver/base_test.js b/test/driver/base_test.js
index 080f0750..5c6462f1 100644
--- a/test/driver/base_test.js
+++ b/test/driver/base_test.js
@@ -1,80 +1,91 @@
-var vows = require('vows');
-var assert = require('assert');
-var Base = require('../../lib/driver/base');
-
-var internals = {};
-internals.migrationTable = 'migrations';
+var Code = require('code');
+var Lab = require('lab');
+var lab = (exports.lab = Lab.script());
+var Base = require('db-migrate-base');
+
+var internals = {
+ migrationTable: 'migrations',
+ mod: {
+ log: require('db-migrate-shared').log,
+ type: require('db-migrate-shared').dataType
+ }
+};
-vows.describe('base').addBatch({
- 'default implementation': {
- topic: new Base(internals),
+lab.experiment('base', function () {
+ lab.experiment('default implementation', function () {
+ var base = new Base(internals);
- 'inherits from EventEmitter': function(base) {
- assert.isNotNull(base.on);
- assert.isNotNull(base.emit);
- },
+ lab.test('inherits from EventEmitter', function (done) {
+ Code.expect(base.on).to.be.not.null();
+ Code.expect(base.emit).to.be.not.null();
+ done();
+ });
- 'throws errors for all API methods': function(base) {
- assert.throws(function() {
+ lab.test('throws errors for all API methods', function (done) {
+ Code.expect(function () {
base.createTable();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.dropTable();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.addColumn();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.removeColumn();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.renameColumn();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.changeColumn();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.addIndex();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.insert();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.removeIndex();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.addAssociation();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.removeAssociation();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.addForeignKey();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.removeForeignKey();
- }, Error);
+ }).to.throw(Error);
- assert.throws(function() {
+ Code.expect(function () {
base.runSql();
- }, Error);
- },
-
- 'escapes single quotes': function(base) {
- assert.equal("Bill''s Mother''s House", base.escape("Bill's Mother's House"));
- }
- }
-}).export(module);
-
+ }).to.throw(Error);
+
+ done();
+ });
+
+ lab.test('escapes single quotes', function (done) {
+ Code.expect("Bill''s Mother''s House").to.equal(
+ base.escape("Bill's Mother's House")
+ );
+ done();
+ });
+ });
+});
diff --git a/test/driver/index_test.js b/test/driver/index_test.js
new file mode 100644
index 00000000..a5ccc2b8
--- /dev/null
+++ b/test/driver/index_test.js
@@ -0,0 +1,207 @@
+var proxyquire = require('proxyquire').noPreserveCache();
+var sinon = require('sinon');
+var Code = require('code');
+var Lab = require('lab');
+var lab = exports.lab = Lab.script();
+var validDbConfigWithTunnel = {
+ driver: 'mysql',
+ host: 'dbHost',
+ port: 'dbPort',
+ tunnel: {
+ localPort: 'localPort',
+ host: 'sshHost',
+ port: 'sshPort'
+ }
+};
+
+var internals = {
+ migrationTable: 'migrations'
+};
+
+var indexConnectCallback = function (tunnelStub, driverSpy, callback) {
+ return function (err, db) {
+ if (err) {
+ callback(err, db, tunnelStub, driverSpy);
+ return;
+ }
+
+ callback(err, db, tunnelStub, driverSpy);
+ };
+};
+
+lab.experiment('index', function () {
+ lab.test('a successful connection with ssh tunnel with expected parameters',
+ function (done, cleanup) {
+ // Ensure that require gets a new copy of the module for each test
+ delete require.cache[require.resolve('db-migrate-mysql')];
+ var driver = require('db-migrate-mysql');
+
+ // Set up stubs/spies to verify correct flow
+ var driverSpy = sinon.stub(driver, 'connect').yields(null, {});
+ var tunnelStub = sinon.stub().callsArg(1);
+
+ var index = proxyquire('../../lib/driver/index', {
+ 'tunnel-ssh': tunnelStub,
+ './mysql': driver
+ });
+ // register clean up
+ cleanup(function (next) {
+ driverSpy.restore();
+ delete require.cache[require.resolve('tunnel-ssh')];
+ delete require.cache[require.resolve('db-migrate-mysql')];
+ next();
+ });
+
+ index.connect(
+ validDbConfigWithTunnel,
+ {},
+ indexConnectCallback(tunnelStub, driverSpy, validate)
+ );
+
+ function validate (err, db, tunnelStub, driverSpy) {
+ var expectedTunnelConfig = {
+ localPort: 'localPort',
+ host: 'sshHost',
+ port: 'sshPort',
+ dstHost: 'dbHost',
+ dstPort: 'dbPort'
+ };
+ var expectedDbConfig = {
+ driver: 'mysql',
+ host: '127.0.0.1',
+ port: 'localPort',
+ tunnel: {
+ localPort: 'localPort',
+ host: 'sshHost',
+ port: 'sshPort'
+ }
+ };
+
+ Code.expect(err).to.be.null();
+ Code.expect(db).to.not.be.null();
+ Code.expect(
+ tunnelStub.withArgs(expectedTunnelConfig).calledOnce
+ ).to.be.true();
+ Code.expect(
+ driverSpy.withArgs(expectedDbConfig).calledOnce
+ ).to.be.true();
+
+ done();
+ }
+ });
+
+ lab.test('a failed connection with ssh tunnel', function (done, cleanup) {
+ // Ensure that require gets a new copy of the module for each test
+ delete require.cache[require.resolve('db-migrate-mysql')];
+ var driver = require('db-migrate-mysql');
+
+ // Set up stubs/spies to verify correct flow
+ var tunnelStub = sinon.stub().callsArgWith(1, new Error('error'));
+ var driverSpy = sinon.stub(driver, 'connect').yields(null, {});
+ var index = proxyquire('../../lib/driver/index', {
+ 'tunnel-ssh': tunnelStub,
+ './mysql': driver
+ });
+
+ index.connect(
+ validDbConfigWithTunnel,
+ {},
+ indexConnectCallback(tunnelStub, driverSpy, validate)
+ );
+
+ // register clean up
+ cleanup(function (next) {
+ driverSpy.restore();
+ delete require.cache[require.resolve('tunnel-ssh')];
+ delete require.cache[require.resolve('db-migrate-mysql')];
+ next();
+ });
+
+ function validate (err, db, tunnelStub, driverSpy) {
+ Code.expect(err, 'err should be non-null')
+ .to.exists();
+ Code.expect(db, 'driver should be null or undefined')
+ .to.not.exists();
+
+ Code.expect(tunnelStub.calledOnce, 'tunnel should be called once')
+ .to.be.true();
+ Code.expect(driverSpy.notCalled, 'driver.connect should not be called')
+ .to.be.true();
+
+ done();
+ }
+ });
+
+ lab.test('privateKey gets set as expected',
+ function (done, cleanup) {
+ // Ensure that require gets a new copy of the module for each test
+ delete require.cache[require.resolve('db-migrate-mysql')];
+ var driver = require('db-migrate-mysql');
+ var fs = { readFileSync: sinon.stub().returns('sshkey') };
+
+ // Set up stubs/spies to verify correct flow
+ var driverSpy = sinon.stub(driver, 'connect').yields(null, {});
+ var tunnelStub = sinon.stub().callsArg(1);
+
+ var index = proxyquire('../../lib/driver/index', {
+ 'tunnel-ssh': tunnelStub,
+ 'fs': fs,
+ './mysql': driver
+ });
+
+ // register clean up
+ cleanup(function (next) {
+ driverSpy.restore();
+
+ delete require.cache[require.resolve('tunnel-ssh')];
+ delete require.cache[require.resolve('db-migrate-mysql')];
+ next();
+ });
+
+ validDbConfigWithTunnel.tunnel.privateKeyPath = '/test/key';
+
+ index.connect(
+ validDbConfigWithTunnel,
+ {},
+ indexConnectCallback(tunnelStub, driverSpy, validate)
+ );
+
+ function validate (err, db, tunnelStub, driverSpy) {
+ var expectedTunnelConfig = {
+ localPort: 'localPort',
+ host: 'sshHost',
+ port: 'sshPort',
+ privateKeyPath: '/test/key',
+ dstHost: '127.0.0.1',
+ dstPort: 'localPort',
+ privateKey: 'sshkey'
+ };
+ var expectedDbConfig = {
+ driver: 'mysql',
+ host: '127.0.0.1',
+ port: 'localPort',
+ tunnel: {
+ localPort: 'localPort',
+ host: 'sshHost',
+ port: 'sshPort',
+ privateKeyPath: '/test/key'
+ }
+ };
+
+ Code.expect(err).to.be.null();
+ Code.expect(db).to.not.be.null();
+ Code.expect(
+ fs.readFileSync.withArgs(validDbConfigWithTunnel.tunnel.privateKeyPath)
+ .calledOnce
+ ).to.be.true();
+ Code.expect(
+ tunnelStub.withArgs(expectedTunnelConfig).calledOnce
+ ).to.be.true();
+ Code.expect(
+ driverSpy.withArgs(expectedDbConfig).calledOnce
+ ).to.be.true();
+
+ done();
+ }
+ });
+});
diff --git a/test/driver/mongodb_test.js b/test/driver/mongodb_test.js
deleted file mode 100644
index df60d372..00000000
--- a/test/driver/mongodb_test.js
+++ /dev/null
@@ -1,326 +0,0 @@
-var vows = require('vows');
-var assert = require('assert');
-var dbmeta = require('db-meta');
-var dataType = require('../../lib/data_type');
-var driver = require('../../lib/driver');
-
-var config = require('../db.config.json').mongodb;
-
-var internals = {};
-internals.migrationTable = 'migrations';
-
-var dbName = config.database;
-driver.connect(config, internals, function(err, db) {
- assert.isNull(err);
- vows.describe('mongodb')
- .addBatch({
- 'createCollection': {
- topic: function() {
- db.createCollection('event', this.callback);
- },
-
- teardown: function() {
- db.dropCollection('event', this.callback);
- },
-
- 'has table metadata': {
- topic: function() {
- db._getCollectionNames(this.callback);
- },
-
- 'containing the event table': function(err, tables) {
- assert.equal(tables.length, 2); // Should be 2 b/c of the system collection
- }
- }
- }
- })
- .addBatch({
- 'dropCollection': {
- topic: function() {
- db.createCollection('event', function(err, collection) {
- if(err) {
- return this.callback(err);
- }
-
- db.dropCollection('event', this.callback);
- }.bind(this));
- },
-
- 'has table metadata': {
- topic: function() {
- db._getCollectionNames(this.callback);
- },
-
- 'containing no tables': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 1); // Should be 1 b/c of the system collection
- }
- }
- }
- })
- .addBatch({
- 'renameCollection': {
- topic: function() {
- db.createCollection('event', function(err, collection) {
- if(err) {
- return this.callback(err);
- }
-
- db.renameCollection('event', 'functions', this.callback);
- }.bind(this));
- },
-
- teardown: function() {
- db.dropCollection('functions', this.callback);
- },
-
-
- 'has table metadata': {
- topic: function() {
- db._getCollectionNames(this.callback);
- },
-
- 'containing the functions table': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 2); // Should be 2 b/c of the system collection
- assert.equal(tables[1].collectionName, 'functions');
- }
- }
- }
- })
- .addBatch({
- 'addIndex': {
- topic: function() {
- db.createCollection('event', function(err, collection) {
- if(err) {
- return this.callback(err);
- }
-
- db.addIndex('event', 'event_title', 'title', false, this.callback);
- }.bind(this));
- },
-
- teardown: function() {
- db.dropCollection('event', this.callback);
- },
-
- 'preserves case': {
- topic: function() {
- db._getCollectionNames(this.callback);
- },
-
- 'of the functions original table': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 2); // Should be 2 b/c of the system collection
- assert.equal(tables[1].collectionName, 'event');
- }
- },
-
- 'has resulting index metadata': {
- topic: function() {
- db._getIndexes('event', this.callback);
- },
-
- 'with additional index': function(err, indexes) {
- assert.isDefined(indexes);
- assert.isNotNull(indexes);
- assert.include(indexes, 'event_title');
- }
- }
- }
- })
- .addBatch({
- 'insertOne': {
- topic: function() {
- db.createCollection('event', function(err, collection) {
- if(err) {
- return this.callback(err);
- }
- db.insert('event', {id: 2, title: 'title'}, function(err) {
-
- if(err) {
-
- return this.callback(err);
- }
-
- db._find('event', {title: 'title'}, this.callback);
-
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropCollection('event', this.callback);
- },
-
- 'with additional row' : function(err, data) {
-
- assert.equal(data.length, 1);
- }
- }
- })
- .addBatch({
- 'insertMany': {
- topic: function() {
- db.createCollection('event', function(err, collection) {
- if(err) {
- return this.callback(err);
- }
- db.insert('event', [{id: 2, title: 'title'},
- {id: 3, title: 'lol'},
- {id: 4, title: 'title'}], function(err) {
-
- if(err) {
-
- return this.callback(err);
- }
-
- db._find('event', {title: 'title'}, this.callback);
-
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropCollection('event', this.callback);
- },
-
- 'with additional row' : function(err, data) {
-
- assert.equal(data.length, 2);
- }
- }
- })
- .addBatch({
- 'removeIndex': {
- topic: function() {
- db.createCollection('event', function(err, collection) {
- if(err) {
- return this.callback(err);
- }
-
- db.addIndex('event', 'event_title', 'title', false, function(err, data) {
-
- if(err) {
- return this.callback(err);
- }
-
- db.removeIndex('event', 'event_title', this.callback);
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropCollection('event', this.callback);
- },
-
-
- 'has resulting index metadata': {
- topic: function() {
- db._getIndexes('event', this.callback);
- },
-
- 'without index': function(err, indexes) {
- if(err) {
- return this.callback(err);
- }
-
- assert.isDefined(indexes);
- assert.isNotNull(indexes);
- assert.notInclude(indexes, 'event_title');
- }
- }
- }
- })
- .addBatch({
- 'createMigrationsTable': {
- topic: function() {
- db._createMigrationsCollection(this.callback);
- },
-
- teardown: function() {
- db.dropCollection('migrations', this.callback);
- },
-
- 'has migrations table': {
- topic: function() {
- db._getCollectionNames(this.callback);
- },
-
- 'has migrations table' : function(err, tables) {
- assert.isNull(err);
- assert.isNotNull(tables);
- assert.equal(tables.length, 2); // Should be 2 b/c of the system collection
- assert.equal(tables[1].collectionName, 'migrations');
- }
- }
- }
- })
- .addBatch({
- 'removeIndex': {
- topic: function() {
- db.createCollection('event', function(err, collection) {
- if(err) {
- return this.callback(err);
- }
-
- db.addIndex('event', 'event_title', 'title', false, function(err, data) {
-
- if(err) {
- return this.callback(err);
- }
-
- db.removeIndex('event', 'event_title', this.callback);
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropCollection('event', this.callback);
- },
-
-
- 'has resulting index metadata': {
- topic: function() {
- db._getIndexes('event', this.callback);
- },
-
- 'without index': function(err, indexes) {
- if(err) {
- return this.callback(err);
- }
-
- assert.isDefined(indexes);
- assert.isNotNull(indexes);
- assert.notInclude(indexes, 'event_title');
- }
- }
- }
- })
- .addBatch({
- 'createMigrationsTable': {
- topic: function() {
- db._createMigrationsCollection(this.callback);
- },
-
- teardown: function() {
- db.dropCollection('migrations', this.callback);
- },
-
- 'has migrations table': {
- topic: function() {
- db._getCollectionNames(this.callback);
- },
-
- 'has migrations table' : function(err, tables) {
- assert.isNull(err);
- assert.isNotNull(tables);
- assert.equal(tables.length, 2); // Should be 2 b/c of the system collection
- assert.equal(tables[1].collectionName, 'migrations');
- }
- }
- }
- })
- .export(module);
-});
-
diff --git a/test/driver/mysql_test.js b/test/driver/mysql_test.js
deleted file mode 100644
index 5d442217..00000000
--- a/test/driver/mysql_test.js
+++ /dev/null
@@ -1,838 +0,0 @@
-var vows = require('vows');
-var assert = require('assert');
-var dbmeta = require('db-meta');
-var dataType = require('../../lib/data_type');
-var driver = require('../../lib/driver');
-
-var config = require('../db.config.json').mysql;
-
-var internals = {};
-internals.migrationTable = 'migrations';
-
-var dbName = config.database;
-driver.connect(config, internals, function(err, db) {
- assert.isNull(err);
- vows.describe('mysql').addBatch({
- 'createTable': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- str: { type: dataType.STRING, unique: true, defaultValue: 'foo' },
- txt: { type: dataType.TEXT, notNull: true },
- intg: dataType.INTEGER,
- rel: dataType.REAL,
- dt: dataType.DATE_TIME,
- ts: dataType.TIMESTAMP,
- bin: dataType.BINARY,
- bl: { type: dataType.BOOLEAN, defaultValue: false }
- }, this.callback);
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'has table metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing the event table': function(err, tables) {
- assert.equal(tables.length, 1);
- assert.equal(tables[0].getName(), 'event');
- }
- },
-
- 'has column metadata for the event table': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with 9 columns': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 9);
- },
-
- 'that has integer id column that is primary key, non-nullable, and auto increments': function(err, columns) {
- var column = findByName(columns, 'id');
- assert.equal(column.getDataType(), 'INT');
- assert.equal(column.isPrimaryKey(), true);
- assert.equal(column.isNullable(), false);
- // assert.equal(column.isAutoIncrementing(), true);
- },
-
- 'that has text str column that is unique and has a default value': function(err, columns) {
- var column = findByName(columns, 'str');
- assert.equal(column.getDataType(), 'VARCHAR');
- assert.equal(column.getDefaultValue(), 'foo');
- // assert.equal(column.isUnique(), true);
- },
-
- 'that has text txt column that is non-nullable': function(err, columns) {
- var column = findByName(columns, 'txt');
- assert.equal(column.getDataType(), 'TEXT');
- assert.equal(column.isNullable(), false);
- },
-
- 'that has integer intg column': function(err, columns) {
- var column = findByName(columns, 'intg');
- assert.equal(column.getDataType(), 'INT');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has real rel column': function(err, columns) {
- var column = findByName(columns, 'rel');
- assert.equal(column.getDataType(), 'DOUBLE');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has datetime dt column': function(err, columns) {
- var column = findByName(columns, 'dt');
- assert.equal(column.getDataType(), 'DATETIME');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has timestamp ts column': function(err, columns) {
- var column = findByName(columns, 'ts');
- assert.equal(column.getDataType(), 'TIMESTAMP');
- assert.equal(column.isNullable(), false);
- },
-
- 'that has binary bin column': function(err, columns) {
- var column = findByName(columns, 'bin');
- assert.equal(column.getDataType(), 'BINARY');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has boolean bl column with a default value': function(err, columns) {
- var column = findByName(columns, 'bl');
- assert.equal(column.getDataType(), 'TINYINT');
- assert.equal(column.isNullable(), true);
- assert.equal(column.getDefaultValue(), 0);
- }
- }
- }
- }).addBatch({
- 'dropTable': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function(err) {
- if (err) {
- return this.callback(err);
- }
- db.dropTable('event', this.callback.bind(this, null));
- }.bind(this));
- },
-
- 'has table metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing no tables': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 0);
- }
- }
- }
- }).addBatch({
- 'renameTable': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.renameTable('event', 'functions', this.callback.bind(this, null));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('functions', this.callback);
- },
-
- 'has table metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing the functions table': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 1);
- assert.equal(tables[0].getName(), 'functions');
- }
- }
- }
- }).addBatch({
- 'addColumn': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.addColumn('event', 'title', 'string', this.callback.bind(this, null));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with additional title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 2);
- var column = findByName(columns, 'title');
- assert.equal(column.getName(), 'title');
- assert.equal(column.getDataType(), 'VARCHAR');
- }
- }
- }
- }).addBatch({
- 'removeColumn': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.addColumn('event', 'title', 'string', function(err) {
- db.removeColumn('event', 'title', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'without title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 1);
- assert.notEqual(columns[0].getName(), 'title');
- }
- }
- }
- }).addBatch({
- 'renameColumn': {
- topic: function() {
- driver.connect(config, internals, function(err) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: dataType.STRING
- }, function() {
- db.renameColumn('event', 'title', 'new_title', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('mysql', { connection: db.connection }, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with renamed title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 2);
- var column = findByName(columns, 'new_title');
- assert.isNotNull(column);
- assert.equal(column.getName(), 'new_title');
- }
- }
- }
- }).addBatch({
- 'changeColumn': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- txt: { type: dataType.STRING, notNull: true, defaultValue: "foo", unique: true },
- keep_id: { type: dataType.INTEGER, notNull: false, unique: true }
- }, function(err) {
- if (err) {
- return this.callback(err);
- }
- var spec = { type: dataType.STRING, notNull: false, unique: false, defaultValue: 'foo2' },
- spec2 = { type: dataType.INTEGER, notNull: true, unsigned: true };
-
- db.changeColumn('event', 'txt', spec, function() {
- db.changeColumn('event', 'keep_id', spec2, this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with changed title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 3);
- var column = findByName(columns, 'txt');
- assert.equal(column.getName(), 'txt');
- assert.equal(column.isNullable(), true);
- assert.equal(column.getDefaultValue(), "foo2");
- assert.equal(column.isUnique(), false);
-
- column = findByName(columns, 'keep_id');
- assert.equal(column.getName(), 'keep_id');
- assert.equal(column.isNullable(), false);
- assert.equal(column.isUnique(), true);
- }
- }
- }
- }).addBatch({
- 'addIndex': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
- db.addIndex('event', 'event_title', 'title', this.callback.bind(this, null));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'preserves case': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'of the functions original table': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 1);
- assert.equal(tables[0].getName(), 'event');
- }
- },
-
- 'has resulting index metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getIndexes('event', this.callback);
- }.bind(this));
- },
-
- 'with additional index': function(err, indexes) {
- assert.isNotNull(indexes);
- assert.equal(indexes.length, 2);
- var index = findByName(indexes, 'event_title');
- assert.equal(index.getName(), 'event_title');
- assert.equal(index.getTableName(), 'event');
- assert.equal(index.getColumnName(), 'title');
- }
- }
- }
- }).addBatch({
- 'columnForeignKeySpec': {
- topic: function() {
- db.createTable('event_type', {
-
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
-
- db.createTable('event', {
- id: {
- type: dataType.INTEGER,
- primaryKey: true,
- autoIncrement: true
- },
- event_id: {
- type: dataType.INTEGER,
- notNull: true,
- foreignKey: {
- name: 'fk_event_event_type',
- table: 'event_type',
- mapping: 'id',
- rules: {
- onDelete: 'CASCADE'
- },
- } },
- title: {
- type: dataType.STRING
- }
- }, this.callback.bind(this, null));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event');
- db.dropTable('event_type', this.callback);
- },
-
- 'sets usage and constraints': {
- topic: function() {
- var metaQuery = ['SELECT',
- ' usg.REFERENCED_TABLE_NAME,',
- ' usg.REFERENCED_COLUMN_NAME,',
- ' cstr.UPDATE_RULE,',
- ' cstr.DELETE_RULE',
- 'FROM',
- ' `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` AS usg',
- 'INNER JOIN',
- ' `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS` AS cstr',
- ' ON cstr.CONSTRAINT_SCHEMA = usg.TABLE_SCHEMA',
- ' AND cstr.CONSTRAINT_NAME = usg.CONSTRAINT_NAME',
- 'WHERE',
- ' usg.TABLE_SCHEMA = ?',
- ' AND usg.TABLE_NAME = ?',
- ' AND usg.COLUMN_NAME = ?'].join('\n');
- db.runSql(metaQuery, dbName, 'event', 'event_id', this.callback);
- },
-
- 'with correct references': function(err, rows) {
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.REFERENCED_TABLE_NAME, 'event_type');
- assert.equal(row.REFERENCED_COLUMN_NAME, 'id');
- },
-
- 'and correct rules': function(err, rows) {
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.UPDATE_RULE, 'NO ACTION');
- assert.equal(row.DELETE_RULE, 'CASCADE');
- }
- }
- }
- }).addBatch({
- 'explicitColumnForeignKeySpec': {
- topic: function() {
- db.createTable('event_type', {
-
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
-
- db.createTable('event', {
- id: {
- type: dataType.INTEGER,
- primaryKey: true,
- autoIncrement: true
- },
- event_id: {
- type: dataType.INTEGER,
- notNull: true,
- foreignKey: {
- name: 'fk_event_event_type',
- table: 'event_type',
- mapping: {
- event_id: 'id'
- },
- rules: {
- onDelete: 'CASCADE'
- },
- } },
- title: {
- type: dataType.STRING
- }
- }, this.callback.bind(this, null));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event');
- db.dropTable('event_type', this.callback);
- },
-
- 'sets usage and constraints': {
- topic: function() {
- var metaQuery = ['SELECT',
- ' usg.REFERENCED_TABLE_NAME,',
- ' usg.REFERENCED_COLUMN_NAME,',
- ' cstr.UPDATE_RULE,',
- ' cstr.DELETE_RULE',
- 'FROM',
- ' `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` AS usg',
- 'INNER JOIN',
- ' `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS` AS cstr',
- ' ON cstr.CONSTRAINT_SCHEMA = usg.TABLE_SCHEMA',
- ' AND cstr.CONSTRAINT_NAME = usg.CONSTRAINT_NAME',
- 'WHERE',
- ' usg.TABLE_SCHEMA = ?',
- ' AND usg.TABLE_NAME = ?',
- ' AND usg.COLUMN_NAME = ?'].join('\n');
- db.runSql(metaQuery, dbName, 'event', 'event_id', this.callback);
- },
-
- 'with correct references': function(err, rows) {
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.REFERENCED_TABLE_NAME, 'event_type');
- assert.equal(row.REFERENCED_COLUMN_NAME, 'id');
- },
-
- 'and correct rules': function(err, rows) {
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.UPDATE_RULE, 'NO ACTION');
- assert.equal(row.DELETE_RULE, 'CASCADE');
- }
- }
- }
- }).addBatch({
- 'addForeignKey': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- event_id: { type: dataType.INTEGER, notNull: true },
- title: { type: dataType.STRING }
- }, function() {
- db.createTable('event_type', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function () {
- db.addForeignKey('event', 'event_type', 'fk_event_event_type', {
- 'event_id': 'id'
- }, {
- onDelete: 'CASCADE'
- }, this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event');
- db.dropTable('event_type', this.callback);
- },
-
- 'sets usage and constraints': {
- topic: function() {
- var metaQuery = ['SELECT',
- ' usg.REFERENCED_TABLE_NAME,',
- ' usg.REFERENCED_COLUMN_NAME,',
- ' cstr.UPDATE_RULE,',
- ' cstr.DELETE_RULE',
- 'FROM',
- ' `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` AS usg',
- 'INNER JOIN',
- ' `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS` AS cstr',
- ' ON cstr.CONSTRAINT_SCHEMA = usg.TABLE_SCHEMA',
- ' AND cstr.CONSTRAINT_NAME = usg.CONSTRAINT_NAME',
- 'WHERE',
- ' usg.TABLE_SCHEMA = ?',
- ' AND usg.TABLE_NAME = ?',
- ' AND usg.COLUMN_NAME = ?'].join('\n');
- db.runSql(metaQuery, dbName, 'event', 'event_id', this.callback);
- },
-
- 'with correct references': function(err, rows) {
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.REFERENCED_TABLE_NAME, 'event_type');
- assert.equal(row.REFERENCED_COLUMN_NAME, 'id');
- },
-
- 'and correct rules': function(err, rows) {
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.UPDATE_RULE, 'NO ACTION');
- assert.equal(row.DELETE_RULE, 'CASCADE');
- }
- }
- }
- }).addBatch({
- 'removeForeignKey': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- event_id: { type: dataType.INTEGER, notNull: true },
- title: { type: dataType.STRING }
- }, function() {
- db.createTable('event_type', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function () {
- db.addForeignKey('event', 'event_type', 'fk_event_event_type', {
- 'event_id': 'id'
- }, {
- onDelete: 'CASCADE'
- }, function () {
- db.removeForeignKey('event', 'fk_event_event_type', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event');
- db.dropTable('event_type', this.callback);
- },
- },
-
- 'removes usage and constraints': {
- topic: function() {
- var metaQuery = ['SELECT',
- ' usg.REFERENCED_TABLE_NAME,',
- ' usg.REFERENCED_COLUMN_NAME,',
- ' cstr.UPDATE_RULE,',
- ' cstr.DELETE_RULE',
- 'FROM',
- ' `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` AS usg',
- 'INNER JOIN',
- ' `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS` AS cstr',
- ' ON cstr.CONSTRAINT_SCHEMA = usg.TABLE_SCHEMA',
- ' AND cstr.CONSTRAINT_NAME = usg.CONSTRAINT_NAME',
- 'WHERE',
- ' usg.TABLE_SCHEMA = ?',
- ' AND usg.TABLE_NAME = ?',
- ' AND usg.COLUMN_NAME = ?'].join('\n');
- db.runSql(metaQuery, dbName, 'event', 'event_id', this.callback);
- },
-
- 'completely': function(err, rows) {
- assert.isNotNull(rows);
- assert.equal(rows.length, 0);
- }
- }
- }).addBatch({
- 'runSql': {
- 'accepts vararg parameters': function() {
- db.runSql("SELECT 1 = ?, 2 = ?", 1, 2, function(err, data) {
- assert.equal(data.length, 1);
- });
- },
- 'accepts array parameters': function() {
- db.runSql("SELECT 1 = ?, 2 = ?", [1, 2], function(err, data) {
- assert.equal(data.length, 1);
- });
- }
- }
- }).addBatch({
- 'all': {
- 'accepts vararg parameters': function() {
- db.all("SELECT 1 = ?, 2 = ?", 1, 2, function(err, data) {
- assert.equal(data.length, 1);
- });
- }
- }
- }).addBatch({
- 'insert': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
- db.insert('event', ['id','title'], [2,'title'], this.callback.bind(this, null));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'with additional row' : function() {
- db.runSql("SELECT * from event", function(err, data) {
- assert.equal(data.length, 1);
- });
- }
- }
- }).addBatch({
- 'insertWithSingleQuotes': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
- db.insert('event', ['id','title'], [2,"Bill's Mother's House"], this.callback.bind(this, null));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'with additional row' : function() {
- db.runSql("SELECT * from event", function(err, data) {
- assert.equal(data.length, 1);
- });
- }
- }
- }).addBatch({
- 'removeIndex': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
- db.addIndex('event', 'event_title', 'title', function(err) {
- db.removeIndex('event', 'event_title', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- },
-
- 'has resulting index metadata': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getIndexes('event', this.callback);
- }.bind(this));
- },
-
- 'without index': function(err, indexes) {
- assert.isNotNull(indexes);
- assert.equal(indexes.length, 1); // first index is primary key
- }
- }
- }
- }).addBatch({
- 'removeIndexInvalidArgs': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
- db.addIndex('event', 'event_title', 'title', function(err) {
- db.removeIndex('event_title', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- 'removeIndex has errored': function (err) {
- assert.isNotNull(err);
- assert.equal(err.message, 'Illegal arguments, must provide "tableName" and "indexName"');
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'createMigrationsTable': {
- topic: function() {
- db.createMigrationsTable(this.callback.bind(this, null));
- },
-
- teardown: function() {
- db.dropTable('migrations', this.callback);
- },
-
- 'has migrations table': {
- topic: function() {
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'has migrations table' : function(err, tables) {
- assert.isNull(err);
- assert.isNotNull(tables);
- assert.equal(tables.length,1);
- assert.equal(tables[0].getName(), 'migrations');
- },
-
- 'that has columns':{
- topic:function(){
- dbmeta('mysql', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('migrations',this.callback);
- }.bind(this));
- },
-
- 'with names': function(err, columns){
- assert.isNotNull(columns);
- assert.equal(columns.length, 3);
- var column = findByName(columns, 'id');
- assert.equal(column.getName(), 'id');
- assert.equal(column.getDataType(), 'INT');
- column = findByName(columns, 'name');
- assert.equal(column.getName(), 'name');
- assert.equal(column.getDataType(), 'VARCHAR');
- column = findByName(columns, 'run_on');
- assert.equal(column.getName(), 'run_on');
- assert.equal(column.getDataType(), 'DATETIME');
- }
- }
- }
- }
- }).export(module);
-});
-function findByName(columns, name) {
- for (var i = 0; i < columns.length; i++) {
- if (columns[i].getName() === name) {
- return columns[i];
- }
- }
- return null;
-}
diff --git a/test/driver/pg_schema_test.js b/test/driver/pg_schema_test.js
deleted file mode 100644
index 7270b242..00000000
--- a/test/driver/pg_schema_test.js
+++ /dev/null
@@ -1,50 +0,0 @@
-var vows = require('vows');
-var assert = require('assert');
-var dbmeta = require('db-meta');
-var pg = require('pg');
-var dataType = require('../../lib/data_type');
-var driver = require('../../lib/driver');
-
-var client = new pg.Client('postgres://localhost/db_migrate_test');
-
-var internals = {};
-internals.migrationTable = 'migrations';
-
-vows.describe('pg').addBatch({
- 'create schema and connect': {
- topic: function() {
- var callback = this.callback;
-
- client.connect(function(err) {
- client.query('CREATE SCHEMA test_schema', function(err) {
- driver.connect({ driver: 'pg', database: 'db_migrate_test', schema: 'test_schema' }, internals, function(err, db) {
- callback(null, db);
- });
- });
- });
- },
-
- 'migrations table': {
- topic: function(db) {
- var callback = this.callback;
-
- db.createMigrationsTable(function() {
- client.query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'test_schema' AND table_name = 'migrations'", function(err, result) {
- callback(err, result);
- });
- });
- },
-
- 'is in test_schema': function(err, result) {
- assert.isNull(err);
- assert.isNotNull(result);
- assert.equal(result.rowCount, 1);
- }
- },
-
- teardown: function() {
- client.query('DROP SCHEMA test_schema CASCADE', this.callback);
- }
- }
-}).export(module);
-
diff --git a/test/driver/pg_test.js b/test/driver/pg_test.js
deleted file mode 100644
index ac308d29..00000000
--- a/test/driver/pg_test.js
+++ /dev/null
@@ -1,636 +0,0 @@
-var vows = require('vows');
-var assert = require('assert');
-var dbmeta = require('db-meta');
-var dataType = require('../../lib/data_type');
-var driver = require('../../lib/driver');
-
-var config = require('../db.config.json').pg;
-
-var internals = {};
-internals.migrationTable = 'migrations';
-
-driver.connect(config, internals, function(err, db) {
- vows.describe('pg').addBatch({
- 'createTable': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- str: { type: dataType.STRING, unique: true },
- txt: { type: dataType.TEXT, notNull: true, defaultValue: "foo" },
- chr: dataType.CHAR,
- intg: dataType.INTEGER,
- rel: dataType.REAL,
- smalint: dataType.SMALLINT,
- dt: dataType.DATE,
- dti: dataType.DATE_TIME,
- bl: dataType.BOOLEAN
- }, this.callback.bind(this));
- },
-
- 'has table metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing the event table': function(err, tables) {
- assert.equal(tables.length, 1);
- assert.equal(tables[0].getName(), 'event');
- }
- },
-
- 'has column metadata for the event table': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with 10 columns': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 10);
- },
-
- 'that has integer id column that is primary key, non-nullable, and auto increments': function(err, columns) {
- var column = findByName(columns, 'id');
- assert.equal(column.getDataType(), 'INTEGER');
- assert.equal(column.isPrimaryKey(), true);
- assert.equal(column.isNullable(), false);
- assert.equal(column.isAutoIncrementing(), true);
- },
-
- 'that has text str column that is unique': function(err, columns) {
- var column = findByName(columns, 'str');
- assert.equal(column.getDataType(), 'CHARACTER VARYING');
- assert.equal(column.isUnique(), true);
- },
-
- 'that has text txt column that is non-nullable': function(err, columns) {
- var column = findByName(columns, 'txt');
- assert.equal(column.getDataType(), 'TEXT');
- assert.equal(column.isNullable(), false);
- // assert.equal(column.getDefaultValue(), 'foo');
- },
-
- 'that has integer intg column': function(err, columns) {
- var column = findByName(columns, 'intg');
- assert.equal(column.getDataType(), 'INTEGER');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has real rel column': function(err, columns) {
- var column = findByName(columns, 'rel');
- assert.equal(column.getDataType(), 'REAL');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has integer dt column': function(err, columns) {
- var column = findByName(columns, 'dt');
- assert.equal(column.getDataType(), 'DATE');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has integer dti column': function(err, columns) {
- var column = findByName(columns, 'dti');
- assert.equal(column.getDataType(), 'TIMESTAMP WITHOUT TIME ZONE');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has boolean bl column': function(err, columns) {
- var column = findByName(columns, 'bl');
- assert.equal(column.getDataType(), 'BOOLEAN');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has character chr column': function(err, columns) {
- var column = findByName(columns, 'chr');
- assert.equal(column.getDataType(), 'CHARACTER');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has small integer smalint column': function(err, columns) {
- var column = findByName(columns, 'smalint');
- assert.equal(column.getDataType(), 'SMALLINT');
- assert.equal(column.isNullable(), true);
- }
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'dropTable': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function(err) {
- if (err) {
- return this.callback(err);
- }
- db.dropTable('event', this.callback.bind(this, null));
- }.bind(this));
- },
-
- 'has table metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing no tables': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 0);
- }
- }
- }
- }).addBatch({
- 'renameTable': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.renameTable('event', 'functions', this.callback.bind(this, null));
- }.bind(this));
- },
-
- 'has table metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing the functions table': function(err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 1);
- assert.equal(tables[0].getName(), 'functions');
- }
- },
-
- teardown: function() {
- db.dropTable('functions', this.callback);
- }
- }
- }).addBatch({
- 'addColumn': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.addColumn('event', 'title', 'string', this.callback.bind(this, null));
- }.bind(this));
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with additional title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 2);
- var column = findByName(columns, 'title');
- assert.equal(column.getName(), 'title');
- assert.equal(column.getDataType(), 'CHARACTER VARYING');
- }
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'removeColumn': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.addColumn('event', 'title', 'string', function(err) {
- db.removeColumn('event', 'title', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'without title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 1);
- assert.notEqual(columns[0].getName(), 'title');
- }
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'renameColumn': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.addColumn('event', 'title', 'string', function(err) {
- db.renameColumn('event', 'title', 'new_title', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with renamed title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 2);
- var column = findByName(columns, 'new_title');
- assert.equal(column.getName(), 'new_title');
- }
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'changeColumn': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- txt: { type: dataType.TEXT, notNull: true, unique: true, defaultValue: "foo" },
- keep_id: { type: dataType.INTEGER, notNull: true, unique: true }
- }, function() {
- var spec = { notNull: false, defaultValue: "foo2", unique: false },
- spec2 = { notNull: true, unsigned: true };
-
- db.changeColumn('event', 'txt', spec, function() {
- db.changeColumn('event', 'keep_id', spec2, this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- 'has column metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with changed title column': function(err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 3);
- var column = findByName(columns, 'txt');
- assert.equal(column.getName(), 'txt');
- assert.equal(column.isNullable(), true);
- assert.equal(column.getDefaultValue(), "'foo2'::text");
- assert.equal(column.isUnique(), false);
-
- column = findByName(columns, 'keep_id');
- assert.equal(column.getName(), 'keep_id');
- assert.equal(column.isNullable(), false);
- assert.equal(column.isUnique(), true);
- }
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'addIndex': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function() {
- db.addIndex('event', 'event_title', 'title', this.callback.bind(this, null));
- }.bind(this));
- },
-
- 'has resulting index metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getIndexes('event', this.callback);
- }.bind(this));
- },
-
- 'with additional index': function(err, indexes) {
- assert.isNotNull(indexes);
- assert.equal(indexes.length, 2);
- var index = findByName(indexes, 'event_title');
- assert.equal(index.getName(), 'event_title');
- assert.equal(index.getTableName(), 'event');
- assert.equal(index.getColumnName(), 'title');
- }
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'addForeignKey': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- event_id: { type: dataType.INTEGER, notNull: true },
- title: { type: dataType.STRING }
- }, function() {
- db.createTable('event_type', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function () {
- // lowercase table names because they are quoted in the function
- // and pg uses lowercase internally
- db.addForeignKey('event', 'event_type', 'fk_event_event_type', {
- 'event_id': 'id'
- }, {
- onDelete: 'CASCADE'
- }, this.callback);
- }.bind(this));
- }.bind(this));
- },
-
- 'sets usage and constraints': {
- topic: function() {
- var metaQuery = ['SELECT',
- ' tc.table_schema, tc.table_name as ortn, kcu.column_name orcn, ccu.table_name,',
- ' ccu.column_name,',
- ' cstr.update_rule,',
- ' cstr.delete_rule',
- 'FROM',
- ' information_schema.table_constraints AS tc',
- 'JOIN information_schema.key_column_usage AS kcu',
- ' ON tc.constraint_name = kcu.constraint_name',
- 'JOIN information_schema.constraint_column_usage AS ccu',
- ' ON ccu.constraint_name = tc.constraint_name',
- 'JOIN information_schema.referential_constraints AS cstr',
- ' ON cstr.constraint_schema = tc.table_schema',
- ' AND cstr.constraint_name = tc.constraint_name',
- 'WHERE',
- ' tc.table_schema = ?',
- ' AND tc.table_name = ?',
- ' AND kcu.column_name = ?'].join('\n');
- db.runSql(metaQuery, ['public', 'event', 'event_id'], this.callback);
- },
-
- 'with correct references': function(err, result) {
- var rows = result.rows;
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.table_name, 'event_type');
- assert.equal(row.column_name, 'id');
- },
-
- 'and correct rules': function(err, result) {
- var rows = result.rows;
- assert.isNotNull(rows);
- assert.equal(rows.length, 1);
- var row = rows[0];
- assert.equal(row.update_rule, 'NO ACTION');
- assert.equal(row.delete_rule, 'CASCADE');
- }
- },
-
- teardown: function() {
- db.dropTable('event');
- db.dropTable('event_type', this.callback);
- }
- }
- }).addBatch({
- 'removeForeignKey': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- event_id: { type: dataType.INTEGER, notNull: true },
- title: { type: dataType.STRING }
- }, function() {
- db.createTable('event_type', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function () {
- db.addForeignKey('event', 'event_type', 'fk_event_event_type', {
- 'event_id': 'id'
- }, {
- onDelete: 'CASCADE'
- }, function () {
- db.removeForeignKey('event', 'fk_event_event_type', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function() {
- db.dropTable('event');
- db.dropTable('event_type', this.callback);
- },
-
- 'removes usage and constraints': {
- topic: function() {
- var metaQuery = ['SELECT',
- ' tc.table_schema, tc.table_name as ortn, kcu.column_name orcn, ccu.table_name,',
- ' ccu.column_name,',
- ' cstr.update_rule,',
- ' cstr.delete_rule',
- 'FROM',
- ' information_schema.table_constraints AS tc',
- 'JOIN information_schema.key_column_usage AS kcu',
- ' ON tc.constraint_name = kcu.constraint_name',
- 'JOIN information_schema.constraint_column_usage AS ccu',
- ' ON ccu.constraint_name = tc.constraint_name',
- 'JOIN information_schema.referential_constraints AS cstr',
- ' ON cstr.constraint_schema = tc.table_schema',
- ' AND cstr.constraint_name = tc.constraint_name',
- 'WHERE',
- ' tc.table_schema = ?',
- ' AND tc.table_name = ?',
- ' AND kcu.column_name = ?'].join('\n');
- db.runSql(metaQuery, ['public', 'event', 'event_id'], this.callback);
- },
-
- 'completely': function(err, result) {
- assert.isNotNull(result.rows);
- assert.equal(result.rows.length, 0);
- }
- }
- }
- }).addBatch({
- 'insert': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function(err) {
- db.insert('event', ['id','title'], [2,'title'], this.callback.bind(this, null));
- }.bind(this));
- },
-
- 'with additional row' : function() {
- db.runSql("SELECT * from event", function(err, data) {
- assert.equal(data.rowCount, 1);
- });
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'insertWithSingleQuotes': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function(err) {
- db.insert('event', ['id','title'], [2,"Bill's Mother's House"], this.callback.bind(this, null));
- }.bind(this));
- },
-
- 'with additional row' : function() {
- db.runSql("SELECT * from event", function(err, data) {
- assert.equal(data.rowCount, 1);
- });
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'removeIndex': {
- topic: function() {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function() {
- db.addIndex('event', 'event_title', 'title', function(err) {
- db.removeIndex('event_title', this.callback.bind(this, null));
- }.bind(this));
- }.bind(this));
- },
-
- 'has resulting index metadata': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getIndexes('event', this.callback);
- }.bind(this));
- },
-
- 'without index': function(err, indexes) {
- assert.isNotNull(indexes);
- assert.equal(indexes.length, 1); // first index is primary key
- }
- },
-
- teardown: function() {
- db.dropTable('event', this.callback);
- }
- }
- }).addBatch({
- 'createMigrationsTable': {
- topic: function() {
- db.createMigrationsTable(this.callback.bind(this, null));
- },
-
- 'has migrations table': {
- topic: function() {
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'has migrations table' : function(err, tables) {
- assert.isNull(err);
- assert.isNotNull(tables);
- assert.equal(tables.length,1);
- assert.equal(tables[0].getName(), 'migrations');
- },
-
- 'that has columns':{
- topic:function(){
- dbmeta('pg', { connection:db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('migrations', this.callback);
- }.bind(this));
- },
-
- 'with names': function(err, columns){
- assert.isNotNull(columns);
- assert.equal(columns.length, 3);
- var column = findByName(columns, 'id');
- assert.equal(column.getName(), 'id');
- assert.equal(column.getDataType(), 'INTEGER');
- column = findByName(columns, 'name');
- assert.equal(column.getName(), 'name');
- assert.equal(column.getDataType(), 'CHARACTER VARYING');
- column = findByName(columns, 'run_on');
- assert.equal(column.getName(), 'run_on');
- assert.equal(column.getDataType(), 'TIMESTAMP WITHOUT TIME ZONE');
- }
- }
- },
-
- teardown: function() {
- db.dropTable('migrations', this.callback);
- }
- }
- }).export(module);
-});
-
-function findByName(columns, name) {
- for (var i = 0; i < columns.length; i++) {
- if (columns[i].getName() === name) {
- return columns[i];
- }
- }
- return null;
-}
-
diff --git a/test/driver/shadow_test.js b/test/driver/shadow_test.js
new file mode 100644
index 00000000..79aa7196
--- /dev/null
+++ b/test/driver/shadow_test.js
@@ -0,0 +1,25 @@
+var Code = require('code'); // assertion library
+var Lab = require('lab');
+var lab = exports.lab = Lab.script();
+var sinon = require('sinon');
+var Bluebird = require('bluebird');
+var shadow = require('../../lib/driver/shadow.js');
+
+lab.experiment('shadow', function () {
+ lab.test('shadow function and original function get called in serial',
+ function (done) {
+ var stub = sinon.stub().callsArg(0);
+ var shadowStub = sinon.stub().resolves();
+ var infected = shadow.infect({
+ test: stub
+ }, {}, {
+ test: shadowStub
+ });
+
+ infected.test(function () {
+ Code.expect(shadowStub.calledOnce).to.be.true();
+ Code.expect(stub.calledOnce).to.be.true();
+ done();
+ });
+ });
+});
diff --git a/test/driver/sqlite3_test.js b/test/driver/sqlite3_test.js
deleted file mode 100644
index a553452e..00000000
--- a/test/driver/sqlite3_test.js
+++ /dev/null
@@ -1,455 +0,0 @@
-var fs = require('fs');
-var vows = require('vows');
-var assert = require('assert');
-var dbmeta = require('db-meta');
-var dataType = require('../../lib/data_type');
-var driver = require('../../lib/driver');
-
-var config = require('../db.config.json').sqlite3;
-
-var internals = {};
-internals.migrationTable = 'migrations';
-
-vows.describe('sqlite3').addBatch({
- 'createTable': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true, notNull: true },
- str: { type: dataType.STRING, unique: true },
- txt: { type: dataType.TEXT, notNull: true, defaultValue: "foo" },
- intg: dataType.INTEGER,
- rel: dataType.REAL,
- dt: dataType.DATE_TIME,
- bl: dataType.BOOLEAN
- }, this.callback.bind(this, null, db));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has resulting table metadata': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing the event table': function (err, tables) {
- assert.isNull(err);
- var table = findByName(tables, 'event');
- assert.isNotNull(table);
- assert.equal(table.getName(), 'event');
- }
- },
-
- 'has column metadata for the event table': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with 7 columns': function (err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 7);
- },
-
- 'that has integer id column that is primary key, non-nullable, and auto increments': function (err, columns) {
- var column = findByName(columns, 'id');
- assert.equal(column.getDataType(), 'INTEGER');
- assert.equal(column.isPrimaryKey(), true);
- assert.equal(column.isNullable(), false);
- assert.equal(column.isAutoIncrementing(), true);
- },
-
- 'that has text str column that is unique': function (err, columns) {
- var column = findByName(columns, 'str');
- assert.equal(column.getDataType(), 'VARCHAR');
- assert.equal(column.isUnique(), true);
- },
-
- 'that has text txt column that is non-nullable': function (err, columns) {
- var column = findByName(columns, 'txt');
- assert.equal(column.getDataType(), 'TEXT');
- assert.equal(column.isNullable(), false);
-// assert.equal(column.getDefaultValue(), 'foo');
- },
-
- 'that has integer intg column': function (err, columns) {
- var column = findByName(columns, 'intg');
- assert.equal(column.getDataType(), 'INTEGER');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has real rel column': function (err, columns) {
- var column = findByName(columns, 'rel');
- assert.equal(column.getDataType(), 'REAL');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has integer dt column': function (err, columns) {
- var column = findByName(columns, 'dt');
- assert.equal(column.getDataType(), 'DATETIME');
- assert.equal(column.isNullable(), true);
- },
-
- 'that has boolean bl column': function (err, columns) {
- var column = findByName(columns, 'bl');
- assert.equal(column.getDataType(), 'BOOLEAN');
- assert.equal(column.isNullable(), true);
- }
- }
- }
-}).addBatch({
- 'dropTable': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function (err) {
- if (err) {
- return this.callback(err);
- }
- db.dropTable('event', this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has table metadata': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing no tables': function (err, tables) {
- assert.isNotNull(tables);
- assert.equal(tables.length, 1);
- }
- }
- }
- }).addBatch({
- 'renameTable': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function () {
- db.renameTable('event', 'functions', this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has table metadata': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback);
- }.bind(this));
- },
-
- 'containing the functions table': function (err, tables) {
- assert.isNotNull(tables);
- var table = findByName(tables, 'functions');
- assert.equal(table.getName(), 'functions');
- assert.isNull(findByName(tables, 'event'));
- }
- }
- }
- }).addBatch({
- 'addColumn': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true }
- }, function () {
- db.addColumn('event', 'title', 'string', this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has column metadata': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('event', this.callback);
- }.bind(this));
- },
-
- 'with additional title column': function (err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 2);
- var column = findByName(columns, 'title');
- assert.equal(column.getName(), 'title');
- assert.equal(column.getDataType(), 'VARCHAR');
- }
- }
- }
-// removeColumn
-// renameColumn
-// changeColumn
- }).addBatch({
- 'addIndex': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function () {
- db.addIndex('event', 'event_title', 'title', this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has resulting index metadata': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getIndexes('event', this.callback);
- }.bind(this));
- },
-
- 'with additional index': function (err, indexes) {
- assert.isNotNull(indexes);
- var index = findByName(indexes, 'event_title');
- assert.equal(index.getName(), 'event_title');
- assert.equal(index.getTableName(), 'event');
- assert.equal(index.getColumnName(), 'title');
- }
- }
- }
- }).addBatch({
- 'insert': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function () {
- db.insert('event', ['id', 'title'], [2, 'title'], this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'with additional row': function (db) {
- db.all("SELECT * from event;", function (err, data) {
- assert.equal(data.length, 1);
- });
- }
- }
- }).addBatch({
- 'insertWithSingleQuotes': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function () {
- db.insert('event', ['id', 'title'], [2, "Bill's Mother's House"], this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'with additional row': function (db) {
- db.all("SELECT * from event;", function (err, data) {
- assert.equal(data.length, 1);
- });
- }
- }
- }).addBatch({
- 'removeIndex': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function (err) {
- db.addIndex('event', 'event_title', 'title', function (err) {
- db.removeIndex('event_title', this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has resulting index metadata': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getIndexes('event', this.callback);
- }.bind(this));
- },
-
- 'without index': function (err, indexes) {
- assert.isNotNull(indexes);
- assert.equal(indexes.length, 0);
- }
- }
- }
- }).addBatch({
- 'removeIndexWithTableName': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createTable('event', {
- id: { type: dataType.INTEGER, primaryKey: true, autoIncrement: true },
- title: { type: dataType.STRING }
- }, function (err) {
- db.addIndex('event', 'event_title', 'title', function (err) {
- db.removeIndex('event', 'event_title', this.callback.bind(this, null, db));
- }.bind(this));
- }.bind(this));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has resulting index metadata': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getIndexes('event', this.callback);
- }.bind(this));
- },
-
- 'without index': function (err, indexes) {
- assert.isNotNull(indexes);
- assert.equal(indexes.length, 0);
- }
- }
- }
- }).addBatch({
- 'createMigrationsTable': {
- topic: function () {
- driver.connect(config, internals, function (err, db) {
- db.createMigrationsTable(this.callback.bind(this, null, db));
- }.bind(this));
- },
-
- teardown: function (db) {
- db.close(function (err) {
- fs.unlink(config.filename, this.callback);
- });
- },
-
- 'has migrations table': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getTables(this.callback.bind(this));
- }.bind(this));
- },
-
- 'has migrations table': function (err, tables) {
- assert.isNull(err);
- assert.isNotNull(tables);
- assert.equal(tables.length, 2);
- assert.equal(tables[0].getName(), 'migrations');
- },
- },
-
- 'that has columns': {
- topic: function (db) {
- dbmeta('sqlite3', {connection: db.connection}, function (err, meta) {
- if (err) {
- return this.callback(err);
- }
- meta.getColumns('migrations', this.callback);
- }.bind(this));
- },
-
- 'with names': function (err, columns) {
- assert.isNotNull(columns);
- assert.equal(columns.length, 3);
- var column = findByName(columns, 'id');
- assert.equal(column.getName(), 'id');
- assert.equal(column.getDataType(), 'INTEGER');
- column = findByName(columns, 'name');
- assert.equal(column.getName(), 'name');
- assert.equal(column.getDataType(), 'VARCHAR (255)');
- column = findByName(columns, 'run_on');
- assert.equal(column.getName(), 'run_on');
- assert.equal(column.getDataType(), 'DATETIME');
- }
- }
- }
- }).export(module);
-
-function findByName(columns, name) {
- for (var i = 0; i < columns.length; i++) {
- if (columns[i].getName() === name) {
- return columns[i];
- }
- }
- return null;
-}
diff --git a/test/integration/api_test.js b/test/integration/api_test.js
new file mode 100644
index 00000000..14524e53
--- /dev/null
+++ b/test/integration/api_test.js
@@ -0,0 +1,280 @@
+var Code = require('code');
+var Lab = require('lab');
+var lab = (exports.lab = Lab.script());
+var sinon = require('sinon');
+var proxyquire = require('proxyquire').noPreserveCache();
+var Promise = require('bluebird');
+
+lab.experiment('api', function () {
+ lab.test(
+ 'force process exit after migrations have been run',
+
+ function (done, onCleanup) {
+ var processExit = process.exit;
+ var argv = process.argv;
+ var called = false;
+ var config = {};
+
+ // register cleanup method and start preparing the test
+ onCleanup(teardown);
+ overwriteExit();
+
+ var dbmigrate = stubApiInstance(
+ true,
+ {
+ up: upStub
+ },
+ config
+ );
+
+ dbmigrate.setConfigParam('force-exit', true);
+ dbmigrate.silence(true);
+
+ /**
+ * We have set force-exit above, this should end up in db-migrate
+ * executing process.exit on the final callback.
+ * Process.exit has been overwritten and will finally call validate.
+ *
+ * The test validation takes place in validate()
+ */
+ dbmigrate.up();
+
+ /**
+ * Final validation after process.exit should have been called.
+ */
+ function validate () {
+ Code.expect(called).to.be.true();
+ done();
+ }
+
+ function upStub (internals) {
+ internals.onComplete(
+ {
+ driver: {
+ close: sinon.stub().callsArg(0)
+ }
+ },
+ internals
+ );
+ }
+
+ /**
+ * Create a migration with the programatic API and overwrite process.exit.
+ */
+ function overwriteExit () {
+ process.exit = function (err) {
+ var ret = called;
+ called = true;
+
+ process.exit = processExit;
+
+ if (err) process.exit.apply(arguments);
+
+ Code.expect(ret).to.be.false();
+ validate();
+ };
+ }
+
+ function teardown (next) {
+ process.exit = processExit;
+ process.argv = argv;
+ return next();
+ }
+ }
+ );
+
+ lab.test('should load config from parameter', function (done) {
+ var options = {
+ env: 'dev',
+ cwd: process.cwd() + '/test/integration',
+ config: {
+ dev: {
+ driver: 'sqlite3',
+ filename: ':memory:'
+ },
+ pg: {
+ driver: 'pg',
+ database: 'db_api_test'
+ }
+ }
+ };
+
+ var api = stubApiInstance(true, {}, options);
+ var actual = api.config;
+ var expected = options.config;
+
+ delete expected.getCurrent;
+ delete actual.getCurrent;
+
+ Code.expect(actual).to.equal(expected);
+ done();
+ });
+
+ lab.test('should load commandline options from options parameter', function (
+ done
+ ) {
+ var options = {
+ cmdOptions: {
+ 'migrations-dir': './test'
+ }
+ };
+
+ var api = stubApiInstance(true, {}, options);
+ var actual = api.internals.argv['migrations-dir'];
+ var expected = options.cmdOptions['migrations-dir'];
+
+ delete expected.getCurrent;
+ delete actual.getCurrent;
+
+ Code.expect(actual).to.equal(expected);
+ done();
+ });
+
+ lab.test(
+ 'should handle all up parameter variations properly',
+
+ function () {
+ return Promise.resolve([
+ [], // promise
+ [sinon.spy()],
+ ['nameatargetmigration', sinon.spy()], // targeted migration
+ ['nameatargetmigration'], // promise targeted migration
+ [1, sinon.spy()], // targeted migration
+ [1], // promise targeted migration
+ ['nameatargetmigration', 'testscope', sinon.spy()], // scoped target
+ ['nameatargetmigration', 'testscope'], // promise scope target
+ [1, 'testscope', sinon.spy()], // scoped target
+ [1, 'testscope'] // promise scope target
+ ])
+ .each(defaultExecParams('up'))
+ .each(spyCallback);
+ }
+ );
+
+ lab.test(
+ 'should handle all down parameter variations properly',
+
+ function () {
+ return Promise.resolve([
+ [], // promise
+ [sinon.spy()],
+ [1, sinon.spy()], // targeted migration
+ [1], // promise targeted migration
+ [1, 'testscope', sinon.spy()], // scoped target
+ [1, 'testscope'] // promise scope target
+ ])
+ .each(defaultExecParams('down'))
+ .each(spyCallback);
+ }
+ );
+
+ lab.test(
+ 'should handle all check parameter variations properly',
+
+ function () {
+ return Promise.resolve([
+ [], // promise
+ [sinon.spy()],
+ [1, sinon.spy()], // targeted migration
+ [1], // promise targeted migration
+ [1, 'testscope', sinon.spy()], // scoped target
+ [1, 'testscope'] // promise scope target
+ ])
+ .each(defaultExecParams('check'))
+ .each(spyCallback);
+ }
+ );
+
+ lab.test(
+ 'should handle all reset parameter variations properly',
+
+ function () {
+ return Promise.resolve([
+ [], // promise
+ [sinon.spy()],
+ ['testscope', sinon.spy()], // scoped target
+ ['testscope'] // promise scope target
+ ])
+ .each(defaultExecParams('reset'))
+ .each(spyCallback);
+ }
+ );
+
+ lab.test(
+ 'should handle all sync parameter variations properly',
+
+ function () {
+ return Promise.resolve([
+ [],
+ ['nameatargetmigration', sinon.spy()], // targeted migration
+ ['nameatargetmigration'], // promise targeted migration
+ ['nameatargetmigration', 'testscope', sinon.spy()], // scoped target
+ ['nameatargetmigration', 'testscope'] // promise scope target
+ ])
+ .each(defaultExecParams('sync'))
+ .each(spyCallback);
+ }
+ );
+});
+
+function defaultExecParams (method) {
+ return function (args, index) {
+ var stubs = {};
+ stubs[method] = stub;
+
+ var api = stubApiInstance(true, stubs);
+
+ return [api[method].apply(api, args), args];
+
+ function stub (internals, config, callback) {
+ if (typeof args[0] === 'string') {
+ Code.expect(internals.argv.destination).to.equal(args[0]);
+ } else if (typeof args[0] === 'number') {
+ Code.expect(internals.argv.count).to.equal(args[0]);
+ }
+
+ if (typeof args[1] === 'string') {
+ Code.expect(internals.migrationMode).to.equal(args[1]);
+ Code.expect(internals.matching).to.equal(args[1]);
+ }
+
+ callback();
+ }
+ };
+}
+
+function spyCallback (api, args) {
+ if (typeof args[args.length - 1] === 'function') {
+ var spy = args[args.length - 1];
+ Code.expect(spy.called).to.be.true();
+ }
+}
+
+function loader (stubs) {
+ var load = require('../../lib/commands');
+ var keys = Object.keys(stubs);
+ return function (module) {
+ var index = keys.indexOf(module);
+ if (index !== -1) {
+ return stubs[keys[index]];
+ }
+ return load(module);
+ };
+}
+
+function stubApiInstance (isModule, stubs, options, callback) {
+ delete require.cache[require.resolve('../../api.js')];
+ delete require.cache[require.resolve('optimist')];
+ var Mod = proxyquire('../../api.js', {
+ './lib/commands': loader(stubs)
+ });
+ var plugins = {};
+ options = options || {};
+
+ options = Object.assign(options, {
+ throwUncatched: true,
+ cwd: __dirname
+ });
+
+ return new Mod(plugins, isModule, options, callback);
+}
diff --git a/test/integration/create_test.js b/test/integration/create_test.js
index 5e59973f..dd18106c 100644
--- a/test/integration/create_test.js
+++ b/test/integration/create_test.js
@@ -1,192 +1,307 @@
-var vows = require('vows');
-var assert = require('assert');
+var Code = require('code');
+var Lab = require('lab');
+var lab = (exports.lab = Lab.script());
var fs = require('fs');
var path = require('path');
var cp = require('child_process');
-var dbmUtil = require('../../lib/util');
+var dbmUtil = require('db-migrate-shared').util;
var rmdir = require('rimraf');
-
-function wipeMigrations(callback) {
+function wipeMigrations (callback) {
var dir = path.join(__dirname, 'migrations');
rmdir(dir, callback);
}
-function dbMigrate() {
+function dbMigrate () {
var args = dbmUtil.toArray(arguments);
var dbm = path.join(__dirname, '..', '..', 'bin', 'db-migrate');
args.unshift(dbm);
return cp.spawn('node', args, { cwd: __dirname });
}
-vows.describe('create').addBatch({
- 'without a migration directory': {
- topic: function() {
- wipeMigrations(function(err) {
- assert.isNull(err);
- dbMigrate('create', 'first migration').on('exit', this.callback);
- }.bind(this));
- },
+lab.experiment('create', function () {
+ lab.experiment('without a migration directory', function () {
+ var exitCode;
+
+ lab.before(function (done) {
+ wipeMigrations(function (err) {
+ Code.expect(err).to.be.null();
+ dbMigrate('create', 'first migration').on('exit', function (code) {
+ exitCode = code;
+ done();
+ });
+ });
+ });
- 'does not cause an error': function(code) {
- assert.isNull(code);
- },
+ lab.test('does not cause an error', function (done) {
+ Code.expect(exitCode).to.equal(0);
+ done();
+ });
- 'will create a new migration directory': function(code) {
+ lab.test('will create a new migration directory', function (done) {
var stats = fs.statSync(path.join(__dirname, 'migrations'));
- assert.isTrue(stats.isDirectory());
- },
+ Code.expect(stats.isDirectory()).to.be.true();
+ done();
+ });
- 'will create a new migration': function(code) {
+ lab.test('will create a new migration', function (done) {
var files = fs.readdirSync(path.join(__dirname, 'migrations'));
- assert.equal(files.length, 1);
+ Code.expect(files.length).to.equal(1);
var file = files[0];
- assert.match(file, /first-migration\.js$/);
+ Code.expect(file).to.match(/first-migration\.js$/);
+ done();
+ });
+ });
+
+ lab.experiment(
+ 'with sql-file option set to true from config file',
+ function () {
+ var exitCode;
+
+ lab.before(function (done) {
+ var configOption = path.join(
+ '--config=',
+ __dirname,
+ 'database_with_sql_file.json'
+ );
+
+ wipeMigrations(function (err) {
+ Code.expect(err).to.not.exist();
+ dbMigrate('create', 'second migration', configOption).on(
+ 'exit',
+ function (code) {
+ exitCode = code;
+ done();
+ }
+ );
+ });
+ });
+
+ lab.test('does not cause an error', function (done) {
+ Code.expect(exitCode).to.equal(0);
+ done();
+ });
+
+ lab.test('will create a new migration', function (done) {
+ var files = fs.readdirSync(path.join(__dirname, 'migrations'));
+
+ for (var i = 0; i < files.length; i++) {
+ var file = files[i];
+ var stats = fs.statSync(path.join(__dirname, 'migrations', file));
+ if (stats.isFile()) { Code.expect(file).to.match(/second-migration\.js$/); }
+ }
+
+ done();
+ });
+
+ lab.test('will create a new migration/sqls directory', function (done) {
+ var stats = fs.statSync(path.join(__dirname, 'migrations/sqls'));
+ Code.expect(stats.isDirectory()).to.be.true();
+ done();
+ });
+
+ lab.test('will create a new migration sql up file', function (done) {
+ var files = fs.readdirSync(path.join(__dirname, 'migrations/sqls'));
+ Code.expect(files.length).to.equal(2);
+ var file = files[1];
+ Code.expect(file).to.match(/second-migration-up\.sql$/);
+ done();
+ });
}
- }
-}).addBatch({
- 'with sql-file option set to true from config file' : {
- topic: function() {
- var configOption = path.join("--config=", __dirname, 'database_with_sql_file.json');
- wipeMigrations(function(err) {
- assert.isNull(err);
- dbMigrate( 'create', 'second migration', configOption).on('exit', this.callback);
- }.bind(this));
- },
- 'does not cause an error': function(code) {
- assert.isNull(code);
- },
- 'will create a new migration': function(code) {
- var files = fs.readdirSync(path.join(__dirname, 'migrations'));
+ );
+
+ lab.experiment(
+ 'with sql-file option set to true as a command parameter',
+ function () {
+ var exitCode;
+
+ lab.before(function (done) {
+ var configOption = path.join('--sql-file');
+ wipeMigrations(function (err) {
+ Code.expect(err).to.not.exist();
+ dbMigrate('create', 'third migration', configOption).on(
+ 'exit',
+ function (code) {
+ exitCode = code;
+ done();
+ }
+ );
+ });
+ });
+
+ lab.test('does not cause an error', function (done) {
+ Code.expect(exitCode).to.equal(0);
+ done();
+ });
+
+ lab.test('will create a new migration', function (done) {
+ var files = fs.readdirSync(path.join(__dirname, 'migrations'));
- for (var i = 0; i