db-migrate 1.0.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +487 -0
  2. package/LICENSE +22 -0
  3. package/README.md +100 -0
  4. package/api.js +463 -0
  5. package/bin/db-migrate +39 -0
  6. package/connect.js +198 -0
  7. package/get.js +67 -0
  8. package/index.js +85 -0
  9. package/lib/chain.js +157 -0
  10. package/lib/class.js +75 -0
  11. package/lib/commands/check.js +52 -0
  12. package/lib/commands/create-migration.js +252 -0
  13. package/lib/commands/db.js +63 -0
  14. package/lib/commands/down.js +45 -0
  15. package/lib/commands/dump.js +22 -0
  16. package/lib/commands/fn/plugin.js +45 -0
  17. package/lib/commands/generated.js +32 -0
  18. package/lib/commands/helper/assert.js +15 -0
  19. package/lib/commands/helper/load-config.js +26 -0
  20. package/lib/commands/helper/migration-hook.js +4 -0
  21. package/lib/commands/helper/register-events.js +19 -0
  22. package/lib/commands/index.js +9 -0
  23. package/lib/commands/on-complete.js +28 -0
  24. package/lib/commands/run.js +122 -0
  25. package/lib/commands/seed.js +48 -0
  26. package/lib/commands/set-default-argv.js +169 -0
  27. package/lib/commands/sync.js +52 -0
  28. package/lib/commands/undo-seed.js +54 -0
  29. package/lib/commands/up.js +52 -0
  30. package/lib/config.js +207 -0
  31. package/lib/driver/index.js +156 -0
  32. package/lib/driver/shadow.js +74 -0
  33. package/lib/executors/versioned/v1.js +48 -0
  34. package/lib/executors/versioned/v2.js +78 -0
  35. package/lib/file.js +190 -0
  36. package/lib/interface/migratorInterface.js +127 -0
  37. package/lib/interface/seederInterface.js +145 -0
  38. package/lib/learn.js +369 -0
  39. package/lib/methods/v2/conventions.js +47 -0
  40. package/lib/methods/v2/migrate.js +5 -0
  41. package/lib/methods/v2/statetravel.js +86 -0
  42. package/lib/methods/v2/translatestate.js +117 -0
  43. package/lib/relation.js +37 -0
  44. package/lib/state.js +154 -0
  45. package/lib/template.js +312 -0
  46. package/lib/templates/v2.js +12 -0
  47. package/lib/temputils.js +62 -0
  48. package/lib/walker.js +266 -0
  49. package/package.json +87 -0
  50. package/pnpm-lock.yaml +2574 -0
  51. package/te.js +70 -0
@@ -0,0 +1,52 @@
1
+ var path = require('path');
2
+ var log = require('db-migrate-shared').log;
3
+ var migrationHook = require('./helper/migration-hook.js');
4
+
5
+ async function prepare (internals, config) {
6
+ try {
7
+ await migrationHook(internals);
8
+ var Migrator = require('../walker.js');
9
+ var index = require('../../connect');
10
+
11
+ if (!internals.argv.count) {
12
+ internals.argv.count = Number.MAX_VALUE;
13
+ }
14
+ const migrator = await index.connect(
15
+ {
16
+ config: config.getCurrent().settings,
17
+ internals: internals,
18
+ prefix: 'migration'
19
+ },
20
+ Migrator
21
+ );
22
+
23
+ if (internals.locTitle) {
24
+ migrator.migrationsDir = path.resolve(
25
+ internals.argv['migrations-dir'],
26
+ internals.locTitle
27
+ );
28
+ } else {
29
+ migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']);
30
+ }
31
+
32
+ internals.migrationsDir = migrator.migrationsDir;
33
+
34
+ await migrator.createMigrationsTable();
35
+ log.verbose('migration table created');
36
+
37
+ return migrator;
38
+ } catch (err) {
39
+ throw err;
40
+ }
41
+ }
42
+
43
+ module.exports = async function (internals, config) {
44
+ const migrator = await prepare(internals, config);
45
+
46
+ try {
47
+ const res = await migrator.sync(internals.argv);
48
+ return internals.onComplete(migrator, internals, null, res);
49
+ } catch (err) {
50
+ return internals.onComplete(migrator, internals, err);
51
+ }
52
+ };
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ var assert = require('assert');
4
+ var path = require('path');
5
+ var log = require('db-migrate-shared').log;
6
+ var _assert = require('./helper/assert');
7
+
8
+ function executeUndoSeed (internals, config, callback) {
9
+ var index = require('./connect');
10
+ var Seeder = require('./lib/walker.js');
11
+
12
+ if (!internals.argv.count) {
13
+ log.info('Defaulting to running 1 down seed.');
14
+ internals.argv.count = 1;
15
+ }
16
+
17
+ if (internals.argv._.length > 0) {
18
+ internals.argv.destination = internals.argv._.shift().toString();
19
+ }
20
+
21
+ index.connect(
22
+ {
23
+ config: config.getCurrent().settings,
24
+ internals: internals,
25
+ prefix: 'seed'
26
+ },
27
+ Seeder,
28
+ function (err, seeder) {
29
+ assert.ifError(err);
30
+
31
+ var seedDir =
32
+ internals.mode !== 'static' ? 'vcseeder-dir' : 'staticseeder-dir';
33
+
34
+ seeder.seedDir = path.resolve(internals.argv[seedDir]);
35
+
36
+ if (internals.mode === 'static') {
37
+ internals.onComplete(seeder, callback, {
38
+ stack: "Static seeders can't be undone. Use VC Seeders instead!"
39
+ });
40
+ } else {
41
+ seeder.createSeedsTable(function (err) {
42
+ if (_assert(err, callback)) {
43
+ seeder.down(
44
+ internals.argv,
45
+ internals.onComplete.bind(this, seeder, internals, callback)
46
+ );
47
+ }
48
+ });
49
+ }
50
+ }
51
+ );
52
+ }
53
+
54
+ module.exports = executeUndoSeed;
@@ -0,0 +1,52 @@
1
+ var path = require('path');
2
+ var log = require('db-migrate-shared').log;
3
+ var migrationHook = require('./helper/migration-hook.js');
4
+
5
+ async function prepare (internals, config) {
6
+ try {
7
+ await migrationHook(internals);
8
+ var Migrator = require('../walker.js');
9
+ var index = require('../../connect');
10
+
11
+ if (!internals.argv.count) {
12
+ internals.argv.count = Number.MAX_VALUE;
13
+ }
14
+ const migrator = await index.connect(
15
+ {
16
+ config: config.getCurrent().settings,
17
+ internals: internals,
18
+ prefix: 'migration'
19
+ },
20
+ Migrator
21
+ );
22
+
23
+ if (internals.locTitle) {
24
+ migrator.migrationsDir = path.resolve(
25
+ internals.argv['migrations-dir'],
26
+ internals.locTitle
27
+ );
28
+ } else {
29
+ migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']);
30
+ }
31
+
32
+ internals.migrationsDir = migrator.migrationsDir;
33
+
34
+ await migrator.createMigrationsTable();
35
+ log.verbose('migration table created');
36
+
37
+ return migrator;
38
+ } catch (err) {
39
+ throw err;
40
+ }
41
+ }
42
+
43
+ module.exports = async function (internals, config) {
44
+ const migrator = await prepare(internals, config);
45
+
46
+ try {
47
+ const res = await migrator.up(internals.argv);
48
+ return internals.onComplete(migrator, internals, null, res);
49
+ } catch (err) {
50
+ return internals.onComplete(migrator, internals, err);
51
+ }
52
+ };
package/lib/config.js ADDED
@@ -0,0 +1,207 @@
1
+ var fs = require('fs');
2
+ var path = require('path');
3
+ var parseDatabaseUrl = require('parse-database-url');
4
+ var dbmUtil = require('db-migrate-shared').util;
5
+ var log = require('db-migrate-shared').log;
6
+
7
+ var setCurrent = (exports.setCurrent = function (env) {
8
+ env = dbmUtil.isArray(env) ? env : [env];
9
+ env.forEach(
10
+ function (current) {
11
+ if (dbmUtil.isString(current) && this[current]) {
12
+ this.getCurrent = function () {
13
+ return {
14
+ env: current,
15
+ settings: this[current]
16
+ };
17
+ };
18
+ }
19
+ }.bind(this)
20
+ );
21
+
22
+ if (!this.getCurrent) {
23
+ throw new Error("Environment(s) '" + env.join(', ') + "' not found.");
24
+ }
25
+ });
26
+
27
+ function Config () {}
28
+
29
+ Config.prototype = {
30
+ setCurrent: setCurrent
31
+ };
32
+
33
+ exports.load = function (config, currentEnv) {
34
+ if (typeof config === 'object') {
35
+ return exports.loadObject(config, currentEnv);
36
+ } else {
37
+ return exports.loadFile(config, currentEnv);
38
+ }
39
+ };
40
+
41
+ exports.loadFile = function (fileName, currentEnv, plugins) {
42
+ var config;
43
+
44
+ try {
45
+ fs.statSync(fileName);
46
+ } catch (e) {
47
+ if (!process.env.DATABASE_URL) {
48
+ throw new Error("Could not find database config file '" + fileName + "'");
49
+ } else {
50
+ return exports.loadUrl(process.env.DATABASE_URL, currentEnv);
51
+ }
52
+ }
53
+
54
+ try {
55
+ var plugin = false;
56
+
57
+ if (plugins) {
58
+ plugin = plugins.overwrite('init:config:overwrite:require');
59
+ }
60
+
61
+ if (plugin !== false) {
62
+ try {
63
+ config = plugin['init:config:overwrite:require'](fileName);
64
+ } catch (ex) {
65
+ log.warn(
66
+ 'Plugin failure "' +
67
+ plugin.name +
68
+ '", falling back to default behavior!'
69
+ );
70
+ log.verbose(ex);
71
+
72
+ config = require(fileName);
73
+ }
74
+ } else {
75
+ config = require(fileName);
76
+ }
77
+ } catch (e) {
78
+ // distinguish broken files from missing ones
79
+ if (e instanceof SyntaxError) {
80
+ throw e;
81
+ }
82
+
83
+ try {
84
+ config = require(path.join(process.cwd(), fileName));
85
+ } catch (e) {
86
+ if (!process.env.DATABASE_URL) {
87
+ throw e;
88
+ } else {
89
+ return exports.loadUrl(process.env.DATABASE_URL, currentEnv);
90
+ }
91
+ }
92
+ }
93
+
94
+ return exports.loadObject(config, currentEnv);
95
+ };
96
+
97
+ function walkConfig (level) {
98
+ for (var configEntry in level) {
99
+ if (level[configEntry] && level[configEntry].ENV) {
100
+ if (!process.env[level[configEntry].ENV]) {
101
+ log.verbose(
102
+ 'Environment variable ' + level[configEntry].ENV + ' is empty!'
103
+ );
104
+ }
105
+
106
+ level[configEntry] = process.env[level[configEntry].ENV];
107
+ } else if (level[configEntry] && typeof level[configEntry] === 'object') {
108
+ level[configEntry] = walkConfig(level[configEntry]);
109
+ }
110
+ }
111
+
112
+ return level;
113
+ }
114
+
115
+ exports.loadObject = function (_config, currentEnv) {
116
+ var out = new Config();
117
+ // do not overwrite the users config
118
+ var config = JSON.parse(JSON.stringify(_config));
119
+
120
+ for (var env in config) {
121
+ if (config[env].ENV) {
122
+ if (!process.env[config[env].ENV]) {
123
+ log.verbose('Environment variable ' + config[env].ENV + ' is empty!');
124
+ } else {
125
+ out[env] = parseDatabaseUrl(process.env[config[env].ENV]);
126
+ }
127
+ } else if (typeof config[env] === 'string') {
128
+ out[env] = parseDatabaseUrl(config[env]);
129
+ } else {
130
+ // Check config entry's for ENV objects
131
+ // which will tell us to grab configuration from the environment
132
+ config[env] = walkConfig(config[env]);
133
+ out[env] = config[env];
134
+ }
135
+
136
+ if (typeof config[env].url === 'string' || process.env.DATABASE_URL) {
137
+ if (typeof config[env].url !== 'string') {
138
+ config[env] = Object.assign(
139
+ config[env],
140
+ parseDatabaseUrl(process.env.DATABASE_URL)
141
+ );
142
+ } else {
143
+ config[env] = Object.assign(
144
+ config[env],
145
+ parseDatabaseUrl(config[env].url)
146
+ );
147
+ }
148
+ delete config[env].url;
149
+ } else if (config[env].url && config[env].url.value) {
150
+ config[env].url = config[env].url.value;
151
+ }
152
+
153
+ if (config[env].overwrite || config[env].addIfNotExists) {
154
+ var overwrite = config[env].overwrite || {};
155
+
156
+ if (config[env].addIfNotExists) {
157
+ var addIfNotExists = config[env].addIfNotExists;
158
+ Object.keys(addIfNotExists)
159
+ .filter(function (key) {
160
+ return !overwrite[key] && !config[env][key];
161
+ })
162
+ .forEach(function (key) {
163
+ config[env][key] = addIfNotExists[key];
164
+ });
165
+
166
+ delete config[env].addIfNotExists;
167
+ }
168
+
169
+ Object.keys(overwrite).forEach(function (key) {
170
+ config[env][key] = overwrite[key];
171
+ });
172
+
173
+ delete config[env].overwrite;
174
+ }
175
+ }
176
+
177
+ if (currentEnv) {
178
+ out.setCurrent(currentEnv);
179
+ } else if (config.default) {
180
+ out.setCurrent(config.default);
181
+ } else if (config.defaultEnv) {
182
+ if (config.defaultEnv.ENV) {
183
+ out.setCurrent(process.env[config.defaultEnv.ENV]);
184
+ } else {
185
+ out.setCurrent(config.defaultEnv);
186
+ }
187
+ } else {
188
+ out.setCurrent(['dev', 'development']);
189
+ }
190
+
191
+ return out;
192
+ };
193
+
194
+ exports.loadUrl = function (databaseUrl, currentEnv) {
195
+ var config = parseDatabaseUrl(databaseUrl);
196
+ var out = new Config();
197
+
198
+ if (currentEnv) {
199
+ out[currentEnv] = config;
200
+ out.setCurrent(currentEnv);
201
+ } else {
202
+ out.urlConfig = config;
203
+ out.setCurrent('urlConfig');
204
+ }
205
+
206
+ return out;
207
+ };
@@ -0,0 +1,156 @@
1
+ var internals = {};
2
+
3
+ internals.mod = {};
4
+ internals.mod.log = require('db-migrate-shared').log;
5
+ internals.mod.type = require('db-migrate-shared').dataType;
6
+ var Shadow = require('./shadow');
7
+ var log = internals.mod.log;
8
+ var Promise = require('bluebird');
9
+ var SeederInterface = require('../interface/seederInterface.js');
10
+ var MigratorInterface = require('../interface/migratorInterface.js');
11
+ var resolve = require('resolve');
12
+
13
+ var ShadowProto = {
14
+ createTable: function () {
15
+ return Promise.resolve();
16
+ }
17
+ };
18
+
19
+ exports.connect = function (config, intern, callback) {
20
+ var driver, req;
21
+ let plugin = false;
22
+ const { plugins } = intern;
23
+
24
+ var mod = internals.mod;
25
+ internals = intern;
26
+ internals.mod = mod;
27
+
28
+ // add interface extensions to allow drivers to add new methods
29
+ internals.interfaces = {
30
+ SeederInterface: SeederInterface.extending,
31
+ MigratorInterface: MigratorInterface.extending
32
+ };
33
+
34
+ if (!config.user && config.username) {
35
+ config.user = config.username;
36
+ }
37
+
38
+ if (config.driver === undefined) {
39
+ throw new Error(
40
+ 'config must include a driver key specifing which driver to use'
41
+ );
42
+ }
43
+
44
+ if (config.driver && typeof config.driver === 'object') {
45
+ log.verbose('require:', config.driver.require);
46
+ driver = require(config.driver.require);
47
+ } else {
48
+ switch (config.driver) {
49
+ case 'sqlite':
50
+ config.driver = 'sqlite3';
51
+ break;
52
+
53
+ case 'postgres':
54
+ case 'postgresql':
55
+ config.driver = 'pg';
56
+ break;
57
+ }
58
+
59
+ try {
60
+ req = 'db-migrate-' + config.driver;
61
+ log.verbose('require:', req);
62
+ try {
63
+ driver = require(resolve.sync(req, { basedir: process.cwd() }));
64
+ } catch (e1) {
65
+ try {
66
+ driver = require(req);
67
+ } catch (e2) {
68
+ driver = require('../../../' + req);
69
+ }
70
+ }
71
+ } catch (e3) {
72
+ try {
73
+ // Fallback to internal drivers, while moving drivers to new repos
74
+ req = './' + config.driver;
75
+ log.verbose('require:', req);
76
+ driver = require(req);
77
+ } catch (e4) {
78
+ return callback(
79
+ new Error(
80
+ 'No such driver found, please try to install it via ' +
81
+ 'npm install db-migrate-' +
82
+ config.driver +
83
+ ' or ' +
84
+ 'npm install -g db-migrate-' +
85
+ config.driver
86
+ )
87
+ );
88
+ }
89
+ }
90
+ }
91
+
92
+ log.verbose('connecting');
93
+
94
+ var connect = function (config) {
95
+ driver.connect(config, intern, function (err, db) {
96
+ if (err) {
97
+ callback(err);
98
+ return;
99
+ }
100
+ log.verbose('connected');
101
+
102
+ if (!global.immunity) {
103
+ db = Shadow.infect(db, internals, ShadowProto);
104
+ }
105
+
106
+ callback(null, db);
107
+ });
108
+ };
109
+
110
+ if (config.tunnel) {
111
+ var tunnel = require('tunnel-ssh');
112
+ var tunnelConfig = JSON.parse(JSON.stringify(config.tunnel));
113
+ const { tunnelType } = tunnelConfig;
114
+
115
+ if (plugins) {
116
+ plugin = plugins.overwrite(
117
+ `connection:tunnel:${
118
+ tunnelType && tunnelType !== 'ssh' ? tunnelType : 'ssh'
119
+ }`
120
+ );
121
+ }
122
+
123
+ tunnelConfig.dstHost = config.host;
124
+ tunnelConfig.dstPort = config.port;
125
+
126
+ if (plugin) {
127
+ tunnel = plugin[
128
+ `connection:tunnel:${
129
+ tunnelType && tunnelType !== 'ssh' ? tunnelType : 'ssh'
130
+ }`
131
+ ](tunnelConfig);
132
+ }
133
+
134
+ if (tunnelConfig.privateKeyPath) {
135
+ tunnelConfig.privateKey = require('fs').readFileSync(
136
+ tunnelConfig.privateKeyPath
137
+ );
138
+ }
139
+
140
+ // Reassign the db host/port to point to our local ssh tunnel
141
+ config.host = '127.0.0.1';
142
+ config.port = tunnelConfig.localPort;
143
+
144
+ tunnel(tunnelConfig, function (err) {
145
+ if (err) {
146
+ callback(err);
147
+ return;
148
+ }
149
+ log.verbose('SSH tunnel connected on port ', tunnelConfig.localPort);
150
+
151
+ connect(config);
152
+ });
153
+ } else {
154
+ connect(config);
155
+ }
156
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The shadow driver is basically a MITM object. Or in other words:
3
+ *
4
+ * This shadow is very infectius. It infects other objects and overwrite their
5
+ * behavior. It gets a shadow part of this object and executes various
6
+ * actions.
7
+ *
8
+ * In our case, it records the execution of methods.
9
+ */
10
+
11
+ /**
12
+ * 'Infect' the original class
13
+ */
14
+ exports.infect = function (db, intern, ShadowProto) {
15
+ const newDb = {};
16
+ if (db._shadowsHost || db._shadowProto) {
17
+ throw new Error("Can't shadow a shadow!");
18
+ }
19
+ newDb._shadowsHost = {};
20
+ newDb._shadowProto = {};
21
+
22
+ for (var prop in db) {
23
+ if (
24
+ typeof ShadowProto[prop] === 'function' &&
25
+ typeof db[prop] === 'function'
26
+ ) {
27
+ newDb._shadowsHost[prop] = db[prop];
28
+ newDb._shadowProto[prop] = ShadowProto[prop];
29
+
30
+ (function (property) {
31
+ newDb[property] = function () {
32
+ var params = arguments;
33
+ var self = this;
34
+
35
+ return self._shadowProto[property]
36
+ .apply(self, params)
37
+ .then(function () {
38
+ return self._shadowsHost[property].apply(self, params);
39
+ });
40
+ };
41
+ })(prop);
42
+ } else {
43
+ newDb[prop] = db[prop];
44
+ }
45
+ }
46
+
47
+ return newDb;
48
+ };
49
+
50
+ /**
51
+ * 'Overshadow' the original class
52
+ *
53
+ * This basically overwrites methods existent from the original
54
+ * with a provided replacement. If no replacement was found, it will overwrite
55
+ * with an error instead, which needs to be supplied as a fallback.
56
+ */
57
+
58
+ exports.overshadow = function (db, ShadowProto, ShadowError) {
59
+ const newDb = Object.assign({}, ShadowProto);
60
+ newDb.prototype = Object.assign({}, ShadowProto.prototype);
61
+
62
+ for (var prop in db) {
63
+ if (
64
+ typeof ShadowProto[prop] === 'function' &&
65
+ typeof db[prop] === 'function'
66
+ ) {
67
+ newDb[prop] = ShadowProto[prop];
68
+ } else if (typeof db[prop] === 'function') {
69
+ newDb[prop] = ShadowError(prop);
70
+ }
71
+ }
72
+
73
+ return newDb;
74
+ };
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const Promise = require('bluebird');
4
+ const maybePromised = require('../../temputils.js').maybePromised;
5
+
6
+ const execUnit = {
7
+ up: function (context, driver, file) {
8
+ return context.driver
9
+ .startMigration()
10
+ .then(() => {
11
+ const _file = file.get();
12
+
13
+ if (typeof _file.setup === 'function') {
14
+ _file.setup(context.internals.safeOptions, context.seedLink);
15
+ }
16
+
17
+ return maybePromised(file, _file.up, [context.driver]);
18
+ })
19
+ .then(() => {
20
+ return Promise.promisify(context.writeMigrationRecord.bind(context))(
21
+ file
22
+ );
23
+ })
24
+ .then(context.driver.endMigration.bind(context.driver));
25
+ },
26
+
27
+ down: function (context, driver, file) {
28
+ return driver
29
+ .startMigration()
30
+ .then(() => {
31
+ const _file = file.get();
32
+
33
+ if (typeof _file.setup === 'function') {
34
+ _file.setup(context.internals.safeOptions, context.seedLink);
35
+ }
36
+
37
+ return maybePromised(file, _file.down, [context.driver]);
38
+ })
39
+ .then(() => {
40
+ return Promise.promisify(context.deleteMigrationRecord.bind(context))(
41
+ file
42
+ );
43
+ })
44
+ .then(context.driver.endMigration.bind(context.driver));
45
+ }
46
+ };
47
+
48
+ module.exports = execUnit;