knex 1.0.6 → 2.1.0

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.
package/CHANGELOG.md CHANGED
@@ -1,13 +1,50 @@
1
1
  # Master (Unreleased)
2
2
 
3
- # 1.0.6 - 12 March, 2022
3
+ # 2.1.0 - 26 May, 2022
4
+
5
+ ### New features:
6
+
7
+ - Improve bundling experience to safely import dialects while using static paths #5142
8
+ - Implement extendable builders #5041
9
+ - PostgreSQL: Refresh materialized view concurrently #5166
10
+
11
+ ### Bug fixes:
12
+
13
+ - Use correct paths in package.json browser field #5174
14
+ - MariaDB: Fix 'NULL' returned instead of NULL on MariaDB 10.2.6+ #5181
15
+ - MySQL: fix hasColumn Error (hasColumn ('a_id') is true, but hasColumn('a_Id') is false) #5148
16
+ - MSSQL: Fix .hasTable result when using .withSchema #5176
17
+ - Oracle: correctly INSERTS Buffer #4869
18
+
19
+ ### Typings:
20
+
21
+ - Update type definitions for pg connection #5139
22
+
23
+ # 2.0.0 - 21 April, 2022
24
+
25
+ ### Breaking changes
26
+
27
+ - Restore sqlite3 package #5136
28
+
29
+ ### Test / internal changes:
30
+
31
+ - Migrate Husky from 4 to 7 #5137
32
+ - Migrate Jake to 10.8.5 #5138
33
+
34
+ # 1.0.7 - 13 April, 2022
35
+
36
+ ### Bug fixes:
37
+
38
+ - CLI: Fix cli migrate:make SQLite dependency #5106
39
+
40
+ # 1.0.6 - 12 April, 2022
4
41
 
5
42
  ### Bug fixes:
6
43
 
7
44
  - PostgreSQL: Wait for search path to be set before returning connection #5107
8
45
  - CLI: No client override during migrate:make #5109
9
46
 
10
- # 1.0.5 - 05 March, 2022
47
+ # 1.0.5 - 05 April, 2022
11
48
 
12
49
  ### New features:
13
50
 
package/CONTRIBUTING.md CHANGED
@@ -169,6 +169,16 @@ Once this is done, check it works by attempting to login:
169
169
  psql -h localhost -U postgres -d knex_test
170
170
  ```
171
171
 
172
+ ## Typescript source files
173
+
174
+ > TL;DR: Starting with release 2.0.0 Knex is adding support for Typescript source files. Thus to develop in this repo you will need to run `npm run build` each time you edit `.ts` files to generate the resulting `.js` files. This is automatically run whenever you run `npm install` or checkout a new Git branch so when developing in Javascript you don't have to worry about it. It is encouraged that new functionality and sources be written in Typescript but this is not required.
175
+
176
+ Starting with release 2.0.0, Knex is support source additions in Typescript! This allows for better safety in the code added. However, pre-2.0.0 Knex was always written in pure Javascript and thus a "hybrid" approach is being used for 2.0.0 to allow for the new `.ts` files to exist along `.js` files that make up the majority of this repository.
177
+
178
+ To develop in this repository use the `npm run build` and `npm run clean` commands to compile and delete the `.js` and related files from `.ts` files. If you wish to have the `tsc` compiled watch and recompile on changes then run `npm run build:ts -- --watch`. Note that for easy integration with Javascript the outputted files are done in a "side-by-side" manner meaning that `lib/foo/bar.ts` will result in `lib/foo/bar.js`. This is done automatically via the npm script command `"prepare"` whenever you run `npm install` and Git hook for `post-checkout` (added by Husky) which executes when you run commands like `git checkout` , thus making it easier to not have to worry about this if you're working in pure Javascript.
179
+
180
+ The script file `./scripts/update_gitignore_for_tsc_output.js` file is called as part of the `npm run build` command which will update the `lib/.gitignore` file which is used to ensure generated `.js` and related files from `tsc` compilation are not checked into the git repo.
181
+
172
182
  ## Want to be Collaborator?
173
183
 
174
184
  There is always room for more collaborators. Be active on resolving github issues / sending pull requests / reviewing code and we will ask you to join.
package/UPGRADING.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## Upgrading to new knex.js versions
2
2
 
3
+ ### Upgrading to version 2.0.0+
4
+
5
+ * Since `sqlite3` is maintained again, we switched back to it. If you are using `@vscode/sqlite3` driver dependency, please replace it with `sqlite3` in your `package.json`;
6
+
3
7
  ### Upgrading to version 1.0.0+
4
8
 
5
9
  * Node.js older than 12 is no longer supported, make sure to update your environment;
package/bin/cli.js CHANGED
@@ -16,6 +16,7 @@ const {
16
16
  exit,
17
17
  success,
18
18
  checkLocalModule,
19
+ checkConfigurationOptions,
19
20
  getMigrationExtension,
20
21
  getSeedExtension,
21
22
  getStubPath,
@@ -42,7 +43,7 @@ async function openKnexfile(configPath) {
42
43
  return config;
43
44
  }
44
45
 
45
- async function initKnex(env, opts, noClientOverride) {
46
+ async function initKnex(env, opts, useDefaultClientIfNotSpecified) {
46
47
  checkLocalModule(env);
47
48
  if (process.cwd() !== env.cwd) {
48
49
  process.chdir(env.cwd);
@@ -52,29 +53,35 @@ async function initKnex(env, opts, noClientOverride) {
52
53
  );
53
54
  }
54
55
 
56
+ if (!useDefaultClientIfNotSpecified) {
57
+ checkConfigurationOptions(env, opts);
58
+ }
59
+
55
60
  env.configuration = env.configPath
56
61
  ? await openKnexfile(env.configPath)
57
- : mkConfigObj(opts, noClientOverride);
62
+ : mkConfigObj(opts);
58
63
 
59
- let resolvedConfig = resolveEnvironmentConfig(
64
+ const resolvedConfig = resolveEnvironmentConfig(
60
65
  opts,
61
66
  env.configuration,
62
67
  env.configPath
63
68
  );
64
69
 
65
- // In the other case, config is already override in mkConfigObj.
66
- if (env.configPath) {
67
- const optionsConfig = parseConfigObj(opts, noClientOverride);
68
- resolvedConfig = merge(resolvedConfig, optionsConfig);
69
- }
70
+ const optionsConfig = parseConfigObj(opts);
71
+ const config = merge(resolvedConfig, optionsConfig);
70
72
 
71
73
  // Migrations directory gets defaulted if it is undefined.
72
- if (!env.configPath && !resolvedConfig.migrations.directory) {
73
- resolvedConfig.migrations.directory = null;
74
+ if (!env.configPath && !config.migrations.directory) {
75
+ config.migrations.directory = null;
76
+ }
77
+
78
+ // Client gets defaulted if undefined and it's allowed
79
+ if (useDefaultClientIfNotSpecified && config.client === undefined) {
80
+ config.client = 'sqlite3';
74
81
  }
75
82
 
76
83
  const knex = require(env.modulePath);
77
- return knex(resolvedConfig);
84
+ return knex(config);
78
85
  }
79
86
 
80
87
  function invoke() {
@@ -209,23 +216,26 @@ function invoke() {
209
216
  'Specify the migration stub to use. If using <name> the file must be located in config.migrations.directory'
210
217
  )
211
218
  .action(async (name) => {
212
- const opts = commander.opts();
213
- opts.client = opts.client || 'sqlite3'; // We don't really care about client when creating migrations
214
- const instance = await initKnex(env, opts, true);
215
- const ext = getMigrationExtension(env, opts);
216
- const configOverrides = { extension: ext };
217
-
218
- const stub = getStubPath('migrations', env, opts);
219
- if (stub) {
220
- configOverrides.stub = stub;
221
- }
219
+ try {
220
+ const opts = commander.opts();
221
+ const instance = await initKnex(env, opts, true); // Skip config check, we don't really care about client when creating migrations
222
+ const ext = getMigrationExtension(env, opts);
223
+ const configOverrides = { extension: ext };
224
+
225
+ const stub = getStubPath('migrations', env, opts);
226
+ if (stub) {
227
+ configOverrides.stub = stub;
228
+ }
222
229
 
223
- instance.migrate
224
- .make(name, configOverrides)
225
- .then((name) => {
226
- success(color.green(`Created Migration: ${name}`));
227
- })
228
- .catch(exit);
230
+ instance.migrate
231
+ .make(name, configOverrides)
232
+ .then((name) => {
233
+ success(color.green(`Created Migration: ${name}`));
234
+ })
235
+ .catch(exit);
236
+ } catch(err) {
237
+ exit(err);
238
+ }
229
239
  });
230
240
 
231
241
  commander
@@ -376,26 +386,29 @@ function invoke() {
376
386
  false
377
387
  )
378
388
  .action(async (name) => {
379
- const opts = commander.opts();
380
- opts.client = opts.client || 'sqlite3'; // We don't really care about client when creating seeds
381
- const instance = await initKnex(env, opts);
382
- const ext = getSeedExtension(env, opts);
383
- const configOverrides = { extension: ext };
384
- const stub = getStubPath('seeds', env, opts);
385
- if (stub) {
386
- configOverrides.stub = stub;
387
- }
389
+ try {
390
+ const opts = commander.opts();
391
+ const instance = await initKnex(env, opts, true); // Skip config check, we don't really care about client when creating seeds
392
+ const ext = getSeedExtension(env, opts);
393
+ const configOverrides = { extension: ext };
394
+ const stub = getStubPath('seeds', env, opts);
395
+ if (stub) {
396
+ configOverrides.stub = stub;
397
+ }
388
398
 
389
- if (opts.timestampFilenamePrefix) {
390
- configOverrides.timestampFilenamePrefix = opts.timestampFilenamePrefix;
391
- }
399
+ if (opts.timestampFilenamePrefix) {
400
+ configOverrides.timestampFilenamePrefix = opts.timestampFilenamePrefix;
401
+ }
392
402
 
393
- instance.seed
394
- .make(name, configOverrides)
395
- .then((name) => {
396
- success(color.green(`Created seed file: ${name}`));
397
- })
398
- .catch(exit);
403
+ instance.seed
404
+ .make(name, configOverrides)
405
+ .then((name) => {
406
+ success(color.green(`Created seed file: ${name}`));
407
+ })
408
+ .catch(exit);
409
+ } catch(err) {
410
+ exit(err);
411
+ }
399
412
  });
400
413
 
401
414
  commander
@@ -6,10 +6,10 @@ const tildify = require('tildify');
6
6
  const color = require('colorette');
7
7
  const argv = require('getopts')(process.argv.slice(2));
8
8
 
9
- function parseConfigObj(opts, noClientOverride) {
9
+ function parseConfigObj(opts) {
10
10
  const config = { migrations: {} };
11
11
 
12
- if (opts.client && (!noClientOverride || !config.client)) {
12
+ if (opts.client) {
13
13
  config.client = opts.client;
14
14
  }
15
15
 
@@ -28,17 +28,11 @@ function parseConfigObj(opts, noClientOverride) {
28
28
  return config;
29
29
  }
30
30
 
31
- function mkConfigObj(opts, noClientOverride) {
32
- if (!opts.client) {
33
- throw new Error(
34
- `No configuration file found and no commandline connection parameters passed`
35
- );
36
- }
37
-
31
+ function mkConfigObj(opts) {
38
32
  const envName = opts.env || process.env.NODE_ENV || 'development';
39
33
  const resolvedClientName = resolveClientNameWithAliases(opts.client);
40
34
  const useNullAsDefault = resolvedClientName === 'sqlite3';
41
- const parsedConfig = parseConfigObj(opts, noClientOverride);
35
+ const parsedConfig = parseConfigObj(opts);
42
36
 
43
37
  return {
44
38
  ext: DEFAULT_EXT,
@@ -105,6 +99,14 @@ function checkLocalModule(env) {
105
99
  }
106
100
  }
107
101
 
102
+ function checkConfigurationOptions(env, opts) {
103
+ if (!env.configPath && !opts.client) {
104
+ throw new Error(
105
+ `No configuration file found and no commandline connection parameters passed`
106
+ );
107
+ }
108
+ }
109
+
108
110
  function getMigrationExtension(env, opts) {
109
111
  const config = resolveEnvironmentConfig(
110
112
  opts,
@@ -199,6 +201,7 @@ module.exports = {
199
201
  exit,
200
202
  success,
201
203
  checkLocalModule,
204
+ checkConfigurationOptions,
202
205
  getSeedExtension,
203
206
  getMigrationExtension,
204
207
  getStubPath,
package/lib/client.js CHANGED
@@ -189,10 +189,7 @@ class Client extends EventEmitter {
189
189
  try {
190
190
  this.driver = this._driver();
191
191
  } catch (e) {
192
- const driverName = this.aliasDriverName
193
- ? this.aliasDriverName
194
- : this.driverName;
195
- const message = `Knex: run\n$ npm install ${driverName} --save`;
192
+ const message = `Knex: run\n$ npm install ${this.driverName} --save`;
196
193
  this.logger.error(`${message}\n${e.message}\n${e.stack}`);
197
194
  throw new Error(`${message}\n${e.message}`);
198
195
  }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDialectByNameOrAlias = void 0;
4
+ const { resolveClientNameWithAliases } = require('../util/helpers');
5
+ const dbNameToDialectLoader = Object.freeze({
6
+ 'better-sqlite3': () => require('./better-sqlite3'),
7
+ cockroachdb: () => require('./cockroachdb'),
8
+ mssql: () => require('./mssql'),
9
+ mysql: () => require('./mysql'),
10
+ mysql2: () => require('./mysql2'),
11
+ oracle: () => require('./oracle'),
12
+ oracledb: () => require('./oracledb'),
13
+ pgnative: () => require('./pgnative'),
14
+ postgres: () => require('./postgres'),
15
+ redshift: () => require('./redshift'),
16
+ sqlite3: () => require('./sqlite3'),
17
+ });
18
+ /**
19
+ * Gets the Dialect object with the given client name or throw an
20
+ * error if not found.
21
+ *
22
+ * NOTE: This is a replacement for prior practice of doing dynamic
23
+ * string construction for imports of Dialect objects.
24
+ */
25
+ function getDialectByNameOrAlias(clientName) {
26
+ const resolvedClientName = resolveClientNameWithAliases(clientName);
27
+ const dialectLoader = dbNameToDialectLoader[resolvedClientName];
28
+ if (!dialectLoader) {
29
+ throw new Error(`Invalid clientName given: ${clientName}`);
30
+ }
31
+ return dialectLoader();
32
+ }
33
+ exports.getDialectByNameOrAlias = getDialectByNameOrAlias;
34
+ //# sourceMappingURL=index.js.map
@@ -49,10 +49,17 @@ class SchemaCompiler_MSSQL extends SchemaCompiler {
49
49
  this.builder,
50
50
  this.bindingsHolder
51
51
  );
52
- const sql =
52
+ const bindings = [tableName];
53
+ let sql =
53
54
  `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES ` +
54
55
  `WHERE TABLE_NAME = ${formattedTable}`;
55
- this.pushQuery({ sql, output: (resp) => resp.length > 0 });
56
+
57
+ if (this.schema) {
58
+ sql += ' AND TABLE_SCHEMA = ?';
59
+ bindings.push(this.schema);
60
+ }
61
+
62
+ this.pushQuery({ sql, bindings, output: (resp) => resp.length > 0 });
56
63
  }
57
64
 
58
65
  // Check whether a column exists on the schema.
@@ -131,7 +131,7 @@ class QueryCompiler_MySQL extends QueryCompiler {
131
131
  output(resp) {
132
132
  const out = resp.reduce(function (columns, val) {
133
133
  columns[val.COLUMN_NAME] = {
134
- defaultValue: val.COLUMN_DEFAULT,
134
+ defaultValue: val.COLUMN_DEFAULT === 'NULL' ? null : val.COLUMN_DEFAULT,
135
135
  type: val.DATA_TYPE,
136
136
  maxLength: val.CHARACTER_MAXIMUM_LENGTH,
137
137
  nullable: val.IS_NULLABLE === 'YES',
@@ -48,8 +48,8 @@ class SchemaCompiler_MySQL extends SchemaCompiler {
48
48
  output(resp) {
49
49
  return resp.some((row) => {
50
50
  return (
51
- this.client.wrapIdentifier(row.Field) ===
52
- this.client.wrapIdentifier(column)
51
+ this.client.wrapIdentifier(row.Field.toLowerCase()) ===
52
+ this.client.wrapIdentifier(column.toLowerCase())
53
53
  );
54
54
  });
55
55
  },
@@ -111,7 +111,8 @@ class Client_Oracledb extends Client_Oracle {
111
111
  this
112
112
  );
113
113
  } else if (value instanceof BlobHelper) {
114
- return 'EMPTY_BLOB()';
114
+ formatter.bindings.push(value.value);
115
+ return '?';
115
116
  }
116
117
  return unwrapRaw(value, true, builder, this, formatter) || '?';
117
118
  }
@@ -118,9 +118,9 @@ class SchemaCompiler_PG extends SchemaCompiler {
118
118
  );
119
119
  }
120
120
 
121
- refreshMaterializedView(viewName) {
121
+ refreshMaterializedView(viewName, concurrently = false) {
122
122
  this.pushQuery({
123
- sql: `refresh materialized view ${this.formatter.wrap(viewName)}`,
123
+ sql: `refresh materialized view${concurrently ? " concurrently" : ""} ${this.formatter.wrap(viewName)}`,
124
124
  });
125
125
  }
126
126
 
@@ -30,7 +30,7 @@ class Client_SQLite3 extends Client {
30
30
  }
31
31
 
32
32
  _driver() {
33
- return require('@vscode/sqlite3');
33
+ return require('sqlite3');
34
34
  }
35
35
 
36
36
  schemaCompiler() {
@@ -232,8 +232,6 @@ Object.assign(Client_SQLite3.prototype, {
232
232
  dialect: 'sqlite3',
233
233
 
234
234
  driverName: 'sqlite3',
235
- // SqlLite3 driver package name is different from driver name.
236
- aliasDriverName: '@vscode/sqlite3',
237
235
  });
238
236
 
239
237
  module.exports = Client_SQLite3;
@@ -5,6 +5,10 @@ const QueryInterface = require('../query/method-constants');
5
5
  const makeKnex = require('./make-knex');
6
6
  const { KnexTimeoutError } = require('../util/timeout');
7
7
  const { resolveConfig } = require('./internal/config-resolver');
8
+ const SchemaBuilder = require('../schema/builder');
9
+ const ViewBuilder = require('../schema/viewbuilder');
10
+ const ColumnBuilder = require('../schema/columnbuilder');
11
+ const TableBuilder = require('../schema/tablebuilder');
8
12
 
9
13
  function knex(config) {
10
14
  const { resolvedConfig, Dialect } = resolveConfig(...arguments);
@@ -28,4 +32,28 @@ knex.QueryBuilder = {
28
32
  },
29
33
  };
30
34
 
35
+ knex.SchemaBuilder = {
36
+ extend: function (methodName, fn) {
37
+ SchemaBuilder.extend(methodName, fn);
38
+ },
39
+ };
40
+
41
+ knex.ViewBuilder = {
42
+ extend: function (methodName, fn) {
43
+ ViewBuilder.extend(methodName, fn);
44
+ },
45
+ };
46
+
47
+ knex.ColumnBuilder = {
48
+ extend: function (methodName, fn) {
49
+ ColumnBuilder.extend(methodName, fn);
50
+ },
51
+ };
52
+
53
+ knex.TableBuilder = {
54
+ extend: function (methodName, fn) {
55
+ TableBuilder.extend(methodName, fn);
56
+ },
57
+ };
58
+
31
59
  module.exports = knex;
@@ -2,7 +2,7 @@ const Client = require('../../client');
2
2
  const { SUPPORTED_CLIENTS } = require('../../constants');
3
3
 
4
4
  const parseConnection = require('./parse-connection');
5
- const { resolveClientNameWithAliases } = require('../../util/helpers');
5
+ const { getDialectByNameOrAlias } = require('../../dialects');
6
6
 
7
7
  function resolveConfig(config) {
8
8
  let Dialect;
@@ -34,8 +34,7 @@ function resolveConfig(config) {
34
34
  );
35
35
  }
36
36
 
37
- const resolvedClientName = resolveClientNameWithAliases(clientName);
38
- Dialect = require(`../../dialects/${resolvedClientName}/index.js`);
37
+ Dialect = getDialectByNameOrAlias(clientName);
39
38
  }
40
39
 
41
40
  // If config connection parameter is passed as string, try to parse it
@@ -41,7 +41,7 @@ class MigrationGenerator {
41
41
 
42
42
  _getNewMigrationName(name) {
43
43
  if (name[0] === '-') name = name.slice(1);
44
- return yyyymmddhhmmss() + '_' + name + '.' + this.config.extension;
44
+ return yyyymmddhhmmss() + '_' + name + '.' + this.config.extension.split('-')[0];
45
45
  }
46
46
 
47
47
  _getNewMigrationPath(name) {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ exports.up = function({schema}) {
6
+ <% if (d.tableName) { %>
7
+ return schema.createTable("<%= d.tableName %>", function(t) {
8
+ t.increments();
9
+ t.timestamp();
10
+ });
11
+ <% } %>
12
+ };
13
+
14
+ /**
15
+ * @param { import("knex").Knex } knex
16
+ * @returns { Promise<void> }
17
+ */
18
+ exports.down = function({schema}) {
19
+ <% if (d.tableName) { %>
20
+ return schema.dropTable("<%= d.tableName %>");
21
+ <% } %>
22
+ };
@@ -0,0 +1,21 @@
1
+ import { Knex } from "knex";
2
+
3
+ <% if (d.tableName) { %>
4
+ export async function up({schema}: Knex): Promise<Knex.SchemaBuilder> {
5
+ return schema.createTable("<%= d.tableName %>", (t) => {
6
+ t.increments();
7
+ t.timestamps();
8
+ });
9
+ }
10
+ <% } else { %>
11
+ export async function up({schema}: Knex): Promise<void> {
12
+ }
13
+ <% } %>
14
+ <% if (d.tableName) { %>
15
+ export async function down({schema}: Knex): Promise<Knex.SchemaBuilder> {
16
+ return schema.dropTable("<%= d.tableName %>");
17
+ }
18
+ <% } else { %>
19
+ export async function down({schema}: Knex): Promise<void> {
20
+ }
21
+ <% } %>
@@ -1,5 +1,6 @@
1
1
  const { EventEmitter } = require('events');
2
2
  const toArray = require('lodash/toArray');
3
+ const assign = require('lodash/assign');
3
4
  const { addQueryContext } = require('../util/helpers');
4
5
  const saveAsyncStack = require('../util/save-async-stack');
5
6
  const {
@@ -96,6 +97,17 @@ class SchemaBuilder extends EventEmitter {
96
97
  };
97
98
  });
98
99
 
100
+
101
+ SchemaBuilder.extend = (methodName, fn) => {
102
+ if (Object.prototype.hasOwnProperty.call(SchemaBuilder.prototype, methodName)) {
103
+ throw new Error(
104
+ `Can't extend SchemaBuilder with existing method ('${methodName}').`
105
+ );
106
+ }
107
+
108
+ assign(SchemaBuilder.prototype, { [methodName]: fn });
109
+ };
110
+
99
111
  augmentWithBuilderInterface(SchemaBuilder);
100
112
  addQueryContext(SchemaBuilder);
101
113
 
@@ -1,4 +1,5 @@
1
1
  const extend = require('lodash/extend');
2
+ const assign = require('lodash/assign');
2
3
  const toArray = require('lodash/toArray');
3
4
  const { addQueryContext } = require('../util/helpers');
4
5
 
@@ -87,6 +88,17 @@ ColumnBuilder.prototype.notNull = ColumnBuilder.prototype.notNullable =
87
88
  };
88
89
  });
89
90
 
91
+
92
+ ColumnBuilder.extend = (methodName, fn) => {
93
+ if (Object.prototype.hasOwnProperty.call(ColumnBuilder.prototype, methodName)) {
94
+ throw new Error(
95
+ `Can't extend ColumnBuilder with existing method ('${methodName}').`
96
+ );
97
+ }
98
+
99
+ assign(ColumnBuilder.prototype, { [methodName]: fn });
100
+ };
101
+
90
102
  const AlterMethods = {};
91
103
 
92
104
  // Specify that the column is to be dropped. This takes precedence
@@ -8,6 +8,7 @@
8
8
  // ------
9
9
  const each = require('lodash/each');
10
10
  const extend = require('lodash/extend');
11
+ const assign = require('lodash/assign');
11
12
  const toArray = require('lodash/toArray');
12
13
  const helpers = require('../util/helpers');
13
14
  const { isString, isFunction, isObject } = require('../util/is');
@@ -360,4 +361,15 @@ AlterMethods.dropColumn = AlterMethods.dropColumns = function () {
360
361
  return this;
361
362
  };
362
363
 
364
+
365
+ TableBuilder.extend = (methodName, fn) => {
366
+ if (Object.prototype.hasOwnProperty.call(TableBuilder.prototype, methodName)) {
367
+ throw new Error(
368
+ `Can't extend TableBuilder with existing method ('${methodName}').`
369
+ );
370
+ }
371
+
372
+ assign(TableBuilder.prototype, { [methodName]: fn });
373
+ };
374
+
363
375
  module.exports = TableBuilder;
@@ -1,5 +1,6 @@
1
1
  const helpers = require('../util/helpers');
2
2
  const extend = require('lodash/extend');
3
+ const assign = require('lodash/assign');
3
4
 
4
5
  class ViewBuilder {
5
6
  constructor(client, method, viewName, fn) {
@@ -78,4 +79,15 @@ const AlterMethods = {
78
79
 
79
80
  helpers.addQueryContext(ViewBuilder);
80
81
 
82
+
83
+ ViewBuilder.extend = (methodName, fn) => {
84
+ if (Object.prototype.hasOwnProperty.call(ViewBuilder.prototype, methodName)) {
85
+ throw new Error(
86
+ `Can't extend ViewBuilder with existing method ('${methodName}').`
87
+ );
88
+ }
89
+
90
+ assign(ViewBuilder.prototype, { [methodName]: fn });
91
+ };
92
+
81
93
  module.exports = ViewBuilder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knex",
3
- "version": "1.0.6",
3
+ "version": "2.1.0",
4
4
  "description": "A batteries-included SQL query & schema builder for PostgresSQL, MySQL, CockroachDB, MSSQL and SQLite3",
5
5
  "main": "knex",
6
6
  "types": "types/index.d.ts",
@@ -8,6 +8,10 @@
8
8
  "node": ">=12"
9
9
  },
10
10
  "scripts": {
11
+ "build": "npm run build:gitignore && npm run build:ts",
12
+ "clean": "node scripts/clean.js",
13
+ "build:ts": "tsc",
14
+ "build:gitignore": "node scripts/update_gitignore_for_tsc_output.js run",
11
15
  "format": "prettier --write \"{lib,bin,scripts,test}/**/*.js\"",
12
16
  "debug:test": "mocha --inspect-brk --exit -t 0 test/all-tests-suite.js",
13
17
  "debug:tape": "node --inspect-brk test/tape/index.js",
@@ -51,7 +55,9 @@
51
55
  "db:stop:oracle": "docker-compose -f scripts/docker-compose.yml down",
52
56
  "stress:init": "docker-compose -f scripts/stress-test/docker-compose.yml up --no-start && docker-compose -f scripts/stress-test/docker-compose.yml start",
53
57
  "stress:test": "node scripts/stress-test/knex-stress-test.js | grep -A 5 -B 60 -- '- STATS '",
54
- "stress:destroy": "docker-compose -f scripts/stress-test/docker-compose.yml stop"
58
+ "stress:destroy": "docker-compose -f scripts/stress-test/docker-compose.yml stop",
59
+ "prepare": "husky install && npm run clean && npm run build",
60
+ "prepublishOnly": "npm run clean && npm run build"
55
61
  },
56
62
  "dependencies": {
57
63
  "colorette": "2.0.16",
@@ -85,7 +91,7 @@
85
91
  "pg-native": {
86
92
  "optional": true
87
93
  },
88
- "@vscode/sqlite3": {
94
+ "sqlite3": {
89
95
  "optional": true
90
96
  },
91
97
  "better-sqlite3": {
@@ -94,13 +100,12 @@
94
100
  },
95
101
  "lint-staged": {
96
102
  "*.{js,json}": [
97
- "prettier --write",
98
- "git add"
103
+ "prettier --write"
99
104
  ]
100
105
  },
101
106
  "devDependencies": {
102
- "@types/node": "^16.11.22",
103
- "@vscode/sqlite3": "^5.0.7",
107
+ "@tsconfig/recommended": "^1.0.1",
108
+ "@types/node": "^17.0.25",
104
109
  "better-sqlite3": "^7.5.1",
105
110
  "chai": "^4.3.6",
106
111
  "chai-as-promised": "^7.1.1",
@@ -113,11 +118,11 @@
113
118
  "eslint-config-prettier": "^8.5.0",
114
119
  "eslint-plugin-import": "^2.26.0",
115
120
  "eslint-plugin-mocha-no-only": "^1.1.1",
116
- "husky": "^4.3.8",
117
- "jake": "^8.1.1",
121
+ "husky": "^8.0.1",
122
+ "jake": "^10.8.5",
118
123
  "JSONStream": "^1.3.5",
119
124
  "lint-staged": "^12.3.7",
120
- "mocha": "^9.2.2",
125
+ "mocha": "^10.0.0",
121
126
  "mock-fs": "^5.1.2",
122
127
  "mysql": "^2.18.1",
123
128
  "mysql2": "^2.3.3",
@@ -127,16 +132,17 @@
127
132
  "pg-query-stream": "^4.2.1",
128
133
  "prettier": "2.6.2",
129
134
  "rimraf": "^3.0.2",
130
- "sinon": "^13.0.1",
135
+ "sinon": "^14.0.0",
131
136
  "sinon-chai": "^3.7.0",
132
137
  "source-map-support": "^0.5.21",
138
+ "sqlite3": "^5.0.4",
133
139
  "tap-spec": "^5.0.0",
134
140
  "tape": "^5.5.3",
135
141
  "tedious": "^14.4.0",
136
142
  "toxiproxy-node-client": "^2.0.6",
137
143
  "ts-node": "^10.7.0",
138
144
  "tsd": "^0.20.0",
139
- "typescript": "4.6.3"
145
+ "typescript": "4.7.2"
140
146
  },
141
147
  "buildDependencies": [
142
148
  "rimraf"
@@ -193,9 +199,9 @@
193
199
  }
194
200
  ],
195
201
  "browser": {
196
- "./lib/migrate/Migrator.js": "./lib/util/noop.js",
202
+ "./lib/migrations/migrate/Migrator.js": "./lib/util/noop.js",
197
203
  "./lib/bin/cli.js": "./lib/util/noop.js",
198
- "./lib/seed/Seeder.js": "./lib/util/noop.js",
204
+ "./lib/migrations/seed/Seeder.js": "./lib/util/noop.js",
199
205
  "tedious": false,
200
206
  "mysql": false,
201
207
  "mysql2": false,
@@ -204,7 +210,6 @@
204
210
  "pg-query-stream": false,
205
211
  "oracle": false,
206
212
  "sqlite3": false,
207
- "@vscode/sqlite3": false,
208
213
  "better-sqlite3": false,
209
214
  "oracledb": false
210
215
  },
@@ -214,7 +219,12 @@
214
219
  },
215
220
  "files": [
216
221
  "bin/*",
217
- "lib/*",
222
+ "lib/",
223
+ "lib/**/*.js",
224
+ "!lib/**/*.ts",
225
+ "!lib/**/*.d.ts",
226
+ "!lib/**/*.js.map",
227
+ "!lib/.gitignore",
218
228
  "scripts/*",
219
229
  "types/index.d.ts",
220
230
  "types/result.d.ts",
@@ -242,11 +252,6 @@
242
252
  "lib/dialects/oracledb"
243
253
  ]
244
254
  },
245
- "husky": {
246
- "hooks": {
247
- "pre-commit": "lint-staged"
248
- }
249
- },
250
255
  "tsd": {
251
256
  "directory": "test-tsd",
252
257
  "compilerOptions": {
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const { execSync } = require("child_process");
6
+
7
+ function main() {
8
+ const repoDir = path.dirname(__dirname)
9
+ const gitDir = path.join(repoDir, '.git')
10
+ const gitDirExists = doesDirectoryExist(gitDir)
11
+ if (!gitDirExists) {
12
+ console.log("No .git directory detected so can not clean 'lib/'. Exiting.")
13
+ process.exit(0)
14
+ }
15
+ console.log("Cleaning 'lib/' of outputted files from Typescript compilation ...")
16
+ const cmd = 'git clean -f -X lib/'
17
+ const output = execSync(cmd, { cwd: repoDir })
18
+ console.log(output.toString('utf8'))
19
+ console.log('Done')
20
+ }
21
+
22
+ function doesDirectoryExist(p) {
23
+ if (fs.existsSync(p)) {
24
+ return fs.lstatSync(p).isDirectory()
25
+ }
26
+ return false
27
+ }
28
+
29
+ main()
@@ -1,4 +1,4 @@
1
- require('@vscode/sqlite3');
1
+ require('sqlite3');
2
2
  const Knex = require('knex');
3
3
 
4
4
  const knexSqlite = Knex({
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path')
4
+ const fs = require('fs')
5
+
6
+ // Directory constants
7
+ const scriptDirectory = __dirname
8
+ const repoDirectory = path.join(scriptDirectory, '..')
9
+ const libDirectory = path.join(repoDirectory, 'lib')
10
+
11
+ const helpText = `
12
+ Helper script to update lib/.gitignore for all .js files from .ts files.
13
+
14
+ update_gitignore_for_tsc_output.js COMMAND
15
+
16
+ COMMAND:
17
+ run: Update lib/.gitignore file.
18
+ help: Print this menu.
19
+
20
+ NOTES FOR USAGE:
21
+ 1. This script is tested to work on Ubuntu 18.04 LTS.
22
+ `
23
+
24
+ const gitignoreHeader = `# DO NOT EDIT, GENERATED BY: scripts/update_gitignore_for_tsc_output.js
25
+
26
+ # Do not include tsc generated type definitions
27
+ **/*.d.ts
28
+
29
+ # Do not include tsc source maps
30
+ **/*.js.map
31
+
32
+ # Do not include .js files from .ts files
33
+ `
34
+
35
+ function main(cliCommand) {
36
+ if (cliCommand === 'run') {
37
+ console.log('Generating lib/.gitignore ...')
38
+
39
+ // Find all .ts files in lib/
40
+ const directoriesToProcess = [libDirectory]
41
+ const tsFiles = []
42
+ while (directoriesToProcess.length > 0) {
43
+ const directory = directoriesToProcess.pop()
44
+ if (!fs.existsSync(directory)) {
45
+ throw new Error("Directory doesn't exist:", directory)
46
+ }
47
+
48
+ const files = fs.readdirSync(directory)
49
+ files.forEach((file) => {
50
+ const filename = path.join(directory, file)
51
+ const stat = fs.lstatSync(filename)
52
+ if (stat.isDirectory()) {
53
+ directoriesToProcess.push(filename)
54
+ } else if (filename.endsWith('.ts') && !filename.endsWith('.d.ts')) {
55
+ tsFiles.push(filename)
56
+ console.log('Found .ts file:', filename)
57
+ }
58
+ })
59
+ }
60
+
61
+ // Get paths of .js files to ignore
62
+ const jsFilesToIgnore = tsFiles.map((filepath) => {
63
+ // Cuts off `${libDirectory}/`
64
+ const relativeTsPath = filepath.slice(libDirectory.length + 1)
65
+ // Swaps .ts for .js file ending
66
+ const relativeJsPath = relativeTsPath.slice(0, relativeTsPath.length - 3) + '.js'
67
+ return relativeJsPath
68
+ })
69
+ const jsFilesToIgnoreString = jsFilesToIgnore.join('\n')
70
+ const libGitignorePath = path.join(libDirectory, '.gitignore')
71
+ fs.writeFileSync(libGitignorePath, gitignoreHeader + jsFilesToIgnoreString + '\n')
72
+ console.log('DONE')
73
+ } else if (['help', '--help', '-h', undefined].includes(cliCommand)) {
74
+ console.log(helpText)
75
+ } else {
76
+ console.log(`Unsupported command: ${cliCommand}`)
77
+ console.log("Try running with 'help' to see supported commands.")
78
+ process.exit(1)
79
+ }
80
+ }
81
+
82
+ // Main script logic
83
+ const cliCommand = process.argv[2]
84
+ // Start the bash app's main function
85
+ main(cliCommand)
package/types/index.d.ts CHANGED
@@ -384,6 +384,43 @@ export declare namespace knex {
384
384
  ): void;
385
385
  }
386
386
 
387
+ class TableBuilder {
388
+ static extend(
389
+ methodName: string,
390
+ fn: (
391
+ this: Knex.TableBuilder,
392
+ ...args: any[]
393
+ ) => Knex.TableBuilder
394
+ ): void;
395
+ }
396
+ class ViewBuilder {
397
+ static extend(
398
+ methodName: string,
399
+ fn: (
400
+ this: Knex.ViewBuilder,
401
+ ...args: any[]
402
+ ) => Knex.ViewBuilder
403
+ ): void;
404
+ }
405
+ class SchemaBuilder {
406
+ static extend(
407
+ methodName: string,
408
+ fn: (
409
+ this: Knex.SchemaBuilder,
410
+ ...args: any[]
411
+ ) => Knex.SchemaBuilder
412
+ ): void;
413
+ }
414
+ class ColumnBuilder {
415
+ static extend(
416
+ methodName: string,
417
+ fn: (
418
+ this: Knex.ColumnBuilder,
419
+ ...args: any[]
420
+ ) => Knex.ColumnBuilder
421
+ ): void;
422
+ }
423
+
387
424
  export class KnexTimeoutError extends Error {}
388
425
 
389
426
  export const Client: typeof Knex.Client;
@@ -1983,7 +2020,7 @@ export declare namespace Knex {
1983
2020
  viewName: string,
1984
2021
  callback: (viewBuilder: ViewBuilder) => any
1985
2022
  ): SchemaBuilder;
1986
- refreshMaterializedView(viewName: string): SchemaBuilder;
2023
+ refreshMaterializedView(viewName: string, concurrently?: boolean): SchemaBuilder;
1987
2024
  dropView(viewName: string): SchemaBuilder;
1988
2025
  dropViewIfExists(viewName: string): SchemaBuilder;
1989
2026
  dropMaterializedView(viewName: string): SchemaBuilder;
@@ -2097,12 +2134,12 @@ export declare namespace Knex {
2097
2134
  binary(columnName: string, length?: number): ColumnBuilder;
2098
2135
  enum(
2099
2136
  columnName: string,
2100
- values: readonly Value[],
2137
+ values: (readonly Value[]) | null,
2101
2138
  options?: EnumOptions
2102
2139
  ): ColumnBuilder;
2103
2140
  enu(
2104
2141
  columnName: string,
2105
- values: readonly Value[],
2142
+ values: (readonly Value[]) | null,
2106
2143
  options?: EnumOptions
2107
2144
  ): ColumnBuilder;
2108
2145
  json(columnName: string): ColumnBuilder;
@@ -2570,17 +2607,28 @@ export declare namespace Knex {
2570
2607
  interface PgConnectionConfig {
2571
2608
  user?: string;
2572
2609
  database?: string;
2573
- password?: string;
2610
+ password?: string | (() => string | Promise<string>);
2574
2611
  port?: number;
2575
2612
  host?: string;
2576
2613
  connectionString?: string;
2577
2614
  keepAlive?: boolean;
2578
2615
  stream?: stream.Duplex;
2579
2616
  statement_timeout?: false | number;
2580
- connectionTimeoutMillis?: number;
2581
- keepAliveInitialDelayMillis?: number;
2617
+ parseInputDatesAsUTC?: boolean;
2582
2618
  ssl?: boolean | ConnectionOptions;
2619
+ query_timeout?: number;
2620
+ keepAliveInitialDelayMillis?: number;
2621
+ idle_in_transaction_session_timeout?: number;
2583
2622
  application_name?: string;
2623
+ connectionTimeoutMillis?: number;
2624
+ types?: PgCustomTypesConfig;
2625
+ options?: string;
2626
+ }
2627
+
2628
+ type PgGetTypeParser = (oid: number, format: string) => any;
2629
+
2630
+ interface PgCustomTypesConfig {
2631
+ getTypeParser: PgGetTypeParser;
2584
2632
  }
2585
2633
 
2586
2634
  type RedshiftConnectionConfig = PgConnectionConfig;