knex 0.95.0 → 0.95.4

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,7 +1,59 @@
1
1
  # Master (Unreleased)
2
2
 
3
+ # 0.95.4 - 26 March, 2021
4
+
5
+ ### Typings:
6
+
7
+ - Fix mistyping of stream #4400
8
+
9
+ # 0.95.3 - 25 March, 2021
10
+
11
+ ### New features:
12
+
13
+ - PostgreSQL: Add "same" as operator #4372
14
+ - MSSQL: Improve an estimate of the max comment length #4362
15
+ - Throw an error if negative offset is provided #4361
16
+
17
+ ### Bug fixes:
18
+
19
+ - Fix timeout method #4324
20
+ - SQLite: prevent dropForeign from being silently ignored #4376
21
+
22
+ ### Typings:
23
+
24
+ - Allow config.client to be non-client instance #4367
25
+ - Add dropForeign arg type for single column #4363
26
+ - Update typings for TypePreservingAggregation and stream #4377
27
+
28
+ # 0.95.2 - 11 March, 2021
29
+
30
+ ### New features:
31
+
32
+ - Improve ESM import support #4350
33
+
34
+ ### Bug fixes:
35
+
36
+ - CLI: update ts.stub files to new TypeScript namespace #4344
37
+ - CLI: fix TypeScript migration stub after 0.95.0 changes #4366
38
+
39
+ ### Typings:
40
+
41
+ - Move QueryBuilder and KnexTimeoutError into knex namespace #4358
42
+
43
+ ### Test / internal changes:
44
+
45
+ - Unify db test helpers #4356
46
+
47
+ # 0.95.1 - 04 March, 2021
48
+
49
+ ### Bug fixes:
50
+
51
+ - CLI: fix `knex init` not finding default knexfile #4339
52
+
3
53
  # 0.95.0 - 03 March, 2021
4
54
 
55
+ Note: there are many breaking changes in this version, particularly in TypeScript support. Please see `UPGRADING.md` for details.
56
+
5
57
  ### New features:
6
58
 
7
59
  - Add transaction isolation support #4185
@@ -41,6 +93,7 @@
41
93
 
42
94
  ### Typings:
43
95
 
96
+ - TypeScript 4.1+ is now required
44
97
  - Add missing onConflict overrides #4182
45
98
  - Introduce the "infamous triplet" export #4181
46
99
  - Fix type definition of Transaction #4172
package/CONTRIBUTING.md CHANGED
@@ -103,7 +103,7 @@ One easy way to setup database for your reproduction is to use database from kne
103
103
 
104
104
  By default, Knex runs tests against sqlite3, postgresql, mysql, mysql2, mssql and oracledb drivers. All databases can be initialized and ran with docker.
105
105
 
106
- Docker databases can be started and initialized and started with:
106
+ Docker databases can be started and initialized with:
107
107
 
108
108
  ```bash
109
109
  npm run db:start
package/README.md CHANGED
@@ -116,3 +116,33 @@ try {
116
116
  // error handling
117
117
  }
118
118
  ```
119
+
120
+ ## Usage as ESM module
121
+
122
+ If you are launching your Node application with `--experimental-modules`, `knex.mjs` should be picked up automatically and named ESM import should work out-of-the-box.
123
+ Otherwise, if you want to use named imports, you'll have to import knex like this:
124
+ ```js
125
+ import { knex } from 'knex/knex.mjs'
126
+ ```
127
+
128
+ You can also just do the default import:
129
+ ```js
130
+ import knex from 'knex'
131
+ ```
132
+
133
+ If you are not using TypeScript and would like the IntelliSense of your IDE to work correctly, it is recommended to set the type explicitly:
134
+ ```js
135
+ /**
136
+ * @type {Knex}
137
+ */
138
+ const database = knex({
139
+ client: 'mysql',
140
+ connection: {
141
+ host : '127.0.0.1',
142
+ user : 'your_database_user',
143
+ password : 'your_database_password',
144
+ database : 'myapp_test'
145
+ }
146
+ });
147
+ database.migrate.latest();
148
+ ```
package/UPGRADING.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ### Upgrading to version 0.95.0+
4
4
 
5
5
  * TypeScript type exports changed significantly. While `import Knex from 'knex';` used to import the knex instantiation function, the namespace and the interface for the knex instantiation function/object, there is now a clear distinction between them:
6
- ```
6
+ ```typescript
7
7
  import { knex } from 'knex' // this is a function that you call to instantiate knex
8
8
  import { Knex } from 'knex' // this is a namespace, and a type of a knex object
9
9
  import KnexTimeoutError = Knex.KnexTimeoutError; // this is a class from the Knex namespace
@@ -12,6 +12,60 @@ const config: Knex.Config = {} // this is a type from the Knex namespace
12
12
  const knexInstance: Knex = knex(config)
13
13
  ```
14
14
 
15
+ If your code looked like this:
16
+ ```typescript
17
+ import knex from 'knex'
18
+
19
+ const config: knex.Config = {} // this is a type from the Knex namespace
20
+ const knexInstance = knex(config)
21
+ ```
22
+
23
+ Change it to
24
+ ```typescript
25
+ import { knex, Knex } from 'knex'
26
+
27
+ const config: Knex.Config = {} // this is a type from the Knex namespace
28
+ const knexInstance = knex(config)
29
+ ```
30
+
31
+ * If you were importing types such as `Config` or `QueryBuilder` directly, use `Knex` namespace instead.
32
+
33
+ So change this:
34
+ ```ts
35
+ import { QueryBuilder } from 'knex'
36
+
37
+ const qb: QueryBuilder = knex('table').select('*')
38
+ ```
39
+
40
+ to this:
41
+ ```ts
42
+ import { Knex } from 'knex'
43
+
44
+ const qb: Knex.QueryBuilder = knex('table').select('*')
45
+ ```
46
+
47
+ * Syntax for QueryBuilder augmentation changed. Previously it looked like this:
48
+
49
+ ```ts
50
+ declare module 'knex' {
51
+ interface QueryBuilder {
52
+ paginate<TResult = any[]>(params: IPaginateParams): KnexQB<any, IWithPagination<TResult>>;
53
+ }
54
+ }
55
+ ```
56
+
57
+ This should be changed into this:
58
+
59
+ ```ts
60
+ declare module 'knex' {
61
+ namespace Knex {
62
+ interface QueryBuilder {
63
+ paginate<TResult = any[]>(params: IPaginateParams): KnexQB<any, IWithPagination<TResult>>;
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
15
69
  * TypeScript version 4.1+ is needed when using knex types now.
16
70
 
17
71
  * MSSQL driver was completely reworked in order to address the multitude of connection pool, error handling and performance issues. Since the new implementation uses `tedious` library directly instead of `mssql`, please replace `mssql` with `tedious` in your dependencies if you are using a MSSQL database.
package/bin/cli.js CHANGED
@@ -178,7 +178,7 @@ function invoke() {
178
178
  const stubPath = `./knexfile.${type}`;
179
179
  readFile(
180
180
  path.dirname(env.modulePath) +
181
- '/lib/migrate/stub/knexfile-' +
181
+ '/lib/migrations/migrate/stub/knexfile-' +
182
182
  type +
183
183
  '.stub'
184
184
  )
package/lib/constants.js CHANGED
@@ -17,6 +17,16 @@ const SUPPORTED_CLIENTS = Object.freeze(
17
17
  ].concat(Object.keys(CLIENT_ALIASES))
18
18
  );
19
19
 
20
+ const DRIVER_NAMES = Object.freeze({
21
+ MsSQL: 'mssql',
22
+ MySQL: 'mysql',
23
+ MySQL2: 'mysql2',
24
+ Oracle: 'oracledb',
25
+ PostgreSQL: 'pg',
26
+ Redshift: 'pg-redshift',
27
+ SQLite: 'sqlite3',
28
+ });
29
+
20
30
  const POOL_CONFIG_OPTIONS = Object.freeze([
21
31
  'maxWaitingClients',
22
32
  'testOnBorrow',
@@ -41,4 +51,5 @@ module.exports = {
41
51
  SUPPORTED_CLIENTS,
42
52
  POOL_CONFIG_OPTIONS,
43
53
  COMMA_NO_PAREN_REGEX,
54
+ DRIVER_NAMES,
44
55
  };
@@ -93,9 +93,12 @@ class ColumnCompiler_MSSQL extends ColumnCompiler {
93
93
  }
94
94
 
95
95
  comment(comment) {
96
- if (comment && comment.length > 255) {
96
+ // XXX: This is a byte limit, not character, so we cannot definitively say they'll exceed the limit without database collation info.
97
+ // (Yes, even if the column has its own collation, the sqlvariant still uses the database collation.)
98
+ // I'm not sure we even need to raise a warning, as MSSQL will return an error when the limit is exceeded itself.
99
+ if (comment && comment.length > 7500 / 2) {
97
100
  this.client.logger.warn(
98
- 'Your comment is longer than the max comment length for MSSQL'
101
+ 'Your comment might be longer than the max comment length for MSSQL of 7,500 bytes.'
99
102
  );
100
103
  }
101
104
  return '';
@@ -26,9 +26,10 @@ class TableCompiler_MSSQL extends TableCompiler {
26
26
 
27
27
  if (this.single.comment) {
28
28
  const { comment } = this.single;
29
- if (comment.length > 60)
29
+ // XXX: This is a byte limit, not character, so we cannot definitively say they'll exceed the limit without database collation info.
30
+ if (comment.length > 7500 / 2)
30
31
  this.client.logger.warn(
31
- 'The max length for a table comment is 60 characters'
32
+ 'Your comment might be longer than the max comment length for MSSQL of 7,500 bytes.'
32
33
  );
33
34
  }
34
35
 
@@ -162,16 +162,18 @@ class Client_MySQL extends Client {
162
162
  }
163
163
 
164
164
  async cancelQuery(connectionToKill) {
165
- const conn = await this.acquireConnection();
165
+ const conn = await this.acquireRawConnection();
166
166
  try {
167
- return await this.query(conn, {
168
- method: 'raw',
167
+ return await this._query(conn, {
169
168
  sql: 'KILL QUERY ?',
170
169
  bindings: [connectionToKill.threadId],
171
170
  options: {},
172
171
  });
173
172
  } finally {
174
- await this.releaseConnection(conn);
173
+ await this.destroyRawConnection(conn);
174
+ if (conn.__knex__disposed) {
175
+ this.logger.warn(`Connection Error: ${conn.__knex__disposed}`);
176
+ }
175
177
  }
176
178
  }
177
179
  }
@@ -237,24 +237,19 @@ class Client_PG extends Client {
237
237
  }
238
238
 
239
239
  async cancelQuery(connectionToKill) {
240
- // Error out if we can't acquire connection in time.
241
- // Purposely not putting timeout on `pg_cancel_backend` execution because erroring
242
- // early there would release the `connectionToKill` back to the pool with
243
- // a `KILL QUERY` command yet to finish.
244
- const conn = await this.acquireConnection();
240
+ const conn = await this.acquireRawConnection();
245
241
 
246
242
  try {
247
243
  return await this._wrappedCancelQueryCall(conn, connectionToKill);
248
244
  } finally {
249
- // NOT returning this promise because we want to release the connection
250
- // in a non-blocking fashion
251
- this.releaseConnection(conn);
245
+ await this.destroyRawConnection(conn).catch((err) => {
246
+ this.logger.warn(`Connection Error: ${err}`);
247
+ });
252
248
  }
253
249
  }
254
250
  _wrappedCancelQueryCall(conn, connectionToKill) {
255
- return this.query(conn, {
256
- method: 'raw',
257
- sql: 'SELECT pg_cancel_backend(?);',
251
+ return this._query(conn, {
252
+ sql: 'SELECT pg_cancel_backend($1);',
258
253
  bindings: [connectionToKill.processID],
259
254
  options: {},
260
255
  });
@@ -334,49 +334,40 @@ class SQLite3_DDL {
334
334
  );
335
335
  }
336
336
 
337
- async dropForeign(columns, indexName) {
337
+ async dropForeign(columns, foreignKeyName) {
338
338
  return this.client.transaction(
339
339
  async (trx) => {
340
340
  this.trx = trx;
341
341
 
342
342
  const { createTable, createIndices } = await this.getTableSql();
343
343
 
344
- const oneLineSql = createTable.replace(/\s+/g, ' ');
345
- const matched = oneLineSql.match(/^CREATE TABLE\s+(\S+)\s*\((.*)\)/);
346
-
347
- const defs = matched[2];
344
+ const parsedTable = parseCreateTable(createTable);
348
345
 
349
- if (!defs) {
350
- throw new Error('No column definitions in this statement!');
346
+ if (!foreignKeyName) {
347
+ parsedTable.columns = parsedTable.columns.map((column) => ({
348
+ ...column,
349
+ references: columns.includes(column.name)
350
+ ? null
351
+ : column.references,
352
+ }));
351
353
  }
352
354
 
353
- const updatedDefs = defs
354
- .split(COMMA_NO_PAREN_REGEX)
355
- .map((line) => line.trim())
356
- .filter((defLine) => {
357
- if (
358
- defLine.toLowerCase().startsWith('constraint') === false &&
359
- defLine.toLowerCase().includes('foreign key') === false
360
- )
361
- return true;
362
-
363
- if (indexName) {
364
- if (defLine.includes(indexName)) return false;
365
- return true;
366
- } else {
367
- const matched = defLine.match(/\(`([^)]+)`\)/);
368
- const columnNames = matched[1].split(', ');
369
-
370
- const unknownColumnIncludedCheck = (col) =>
371
- !columns.includes(col);
372
- return columnNames.every(unknownColumnIncludedCheck) === false;
355
+ parsedTable.constraints = parsedTable.constraints.filter(
356
+ (constraint) => {
357
+ if (foreignKeyName) {
358
+ return constraint.name !== foreignKeyName;
373
359
  }
374
- })
375
- .join(', ');
376
360
 
377
- const newSql = oneLineSql.replace(defs, updatedDefs);
361
+ return (
362
+ constraint.columns.some((column) => columns.includes(column)) ===
363
+ false
364
+ );
365
+ }
366
+ );
378
367
 
379
- return this.alter(newSql, createIndices, (row) => {
368
+ const newTable = compileCreateTable(parsedTable, this.wrap);
369
+
370
+ return this.alter(newTable, createIndices, (row) => {
380
371
  return row;
381
372
  });
382
373
  },
@@ -34,6 +34,7 @@ const operators = transform(
34
34
  '<<',
35
35
  '>>',
36
36
  '~',
37
+ '~=',
37
38
  '~*',
38
39
  '!~',
39
40
  '!~*',
@@ -6,7 +6,7 @@ const makeKnex = require('./make-knex');
6
6
  const { KnexTimeoutError } = require('../util/timeout');
7
7
  const { resolveConfig } = require('./internal/config-resolver');
8
8
 
9
- function Knex(config) {
9
+ function knex(config) {
10
10
  const { resolvedConfig, Dialect } = resolveConfig(...arguments);
11
11
 
12
12
  const newKnex = makeKnex(new Dialect(resolvedConfig));
@@ -17,15 +17,15 @@ function Knex(config) {
17
17
  }
18
18
 
19
19
  // Expose Client on the main Knex namespace.
20
- Knex.Client = Client;
20
+ knex.Client = Client;
21
21
 
22
- Knex.KnexTimeoutError = KnexTimeoutError;
22
+ knex.KnexTimeoutError = KnexTimeoutError;
23
23
 
24
- Knex.QueryBuilder = {
24
+ knex.QueryBuilder = {
25
25
  extend: function (methodName, fn) {
26
26
  QueryBuilder.extend(methodName, fn);
27
27
  QueryInterface.push(methodName);
28
28
  },
29
29
  };
30
30
 
31
- module.exports = Knex;
31
+ module.exports = knex;
@@ -23,8 +23,7 @@ function resolveConfig(config) {
23
23
  }
24
24
  // If user provided Client constructor as a parameter, use it
25
25
  else if (
26
- typeof parsedConfig.client === 'function' &&
27
- parsedConfig.client.prototype instanceof Client
26
+ typeof parsedConfig.client === 'function'
28
27
  ) {
29
28
  Dialect = parsedConfig.client;
30
29
  }
@@ -1,8 +1,8 @@
1
- import * as Knex from "knex";
1
+ import { Knex } from "knex";
2
2
 
3
3
  <% if (d.tableName) { %>
4
4
  export async function up(knex: Knex): Promise<Knex.SchemaBuilder> {
5
- return knex.schema.createTable("<%= d.tableName %>", (t: Knex.AlterTableBuilder) => {
5
+ return knex.schema.createTable("<%= d.tableName %>", (t) => {
6
6
  t.increments();
7
7
  t.timestamps();
8
8
  });
@@ -1,4 +1,4 @@
1
- import * as Knex from "knex";
1
+ import { Knex } from "knex";
2
2
 
3
3
  export async function seed(knex: Knex): Promise<void> {
4
4
  // Deletes ALL existing entries
@@ -908,6 +908,8 @@ class Builder extends EventEmitter {
908
908
  const val = parseInt(value, 10);
909
909
  if (isNaN(val)) {
910
910
  this.client.logger.warn('A valid integer must be provided to offset');
911
+ } else if (val < 0) {
912
+ throw new Error(`A non-negative integer must be provided to offset.`);
911
913
  } else {
912
914
  this._single.offset = val;
913
915
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "knex",
3
- "version": "0.95.0",
3
+ "version": "0.95.4",
4
4
  "description": "A batteries-included SQL query & schema builder for Postgres, MySQL and SQLite3 and the Browser",
5
- "main": "knex.js",
5
+ "main": "knex",
6
6
  "types": "types/index.d.ts",
7
7
  "engines": {
8
8
  "node": ">=10"
@@ -49,7 +49,7 @@
49
49
  "esm": "^3.2.25",
50
50
  "getopts": "2.2.5",
51
51
  "interpret": "^2.2.0",
52
- "lodash": "^4.17.20",
52
+ "lodash": "^4.17.21",
53
53
  "pg-connection-string": "2.4.0",
54
54
  "rechoir": "^0.7.0",
55
55
  "resolve-from": "^5.0.0",
@@ -57,11 +57,11 @@
57
57
  "tildify": "2.0.0"
58
58
  },
59
59
  "peerDependencies": {
60
- "tedious": "^11.0.3",
61
60
  "mysql": "^2.18.1",
62
61
  "mysql2": "^2.2.5",
63
62
  "pg": "^8.5.1",
64
- "sqlite3": "^5.0.0"
63
+ "sqlite3": "^5.0.0",
64
+ "tedious": "^11.0.5"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "tedious": {
@@ -88,19 +88,19 @@
88
88
  },
89
89
  "devDependencies": {
90
90
  "@types/node": "^14.14.28",
91
- "JSONStream": "^1.3.5",
92
- "chai": "^4.3.0",
91
+ "chai": "^4.3.3",
93
92
  "chai-as-promised": "^7.1.1",
94
93
  "chai-subset-in-order": "^2.1.4",
95
94
  "cli-testlab": "^2.2.0",
96
95
  "coveralls": "^3.1.0",
97
96
  "cross-env": "^7.0.3",
98
- "dtslint": "4.0.7",
97
+ "dtslint": "4.0.8",
99
98
  "eslint": "^7.20.0",
100
99
  "eslint-config-prettier": "^8.1.0",
101
100
  "eslint-plugin-import": "^2.22.1",
102
101
  "husky": "^4.3.8",
103
102
  "jake": "^8.1.1",
103
+ "JSONStream": "^1.3.5",
104
104
  "lint-staged": "^10.5.4",
105
105
  "mocha": "^8.3.0",
106
106
  "mock-fs": "^4.13.0",
@@ -117,12 +117,12 @@
117
117
  "source-map-support": "^0.5.19",
118
118
  "sqlite3": "^5.0.1",
119
119
  "tap-spec": "^5.0.0",
120
- "tape": "^5.1.1",
121
- "tedious": "^11.0.3",
120
+ "tape": "^5.2.2",
121
+ "tedious": "^11.0.5",
122
122
  "toxiproxy-node-client": "^2.0.6",
123
123
  "ts-node": "^9.1.1",
124
124
  "tsd": "^0.14.0",
125
- "typescript": "4.2.2"
125
+ "typescript": "4.2.3"
126
126
  },
127
127
  "buildDependencies": [
128
128
  "rimraf"
package/types/index.d.ts CHANGED
@@ -397,6 +397,20 @@ export declare function knex<TRecord extends {} = any, TResult = unknown[]>(
397
397
  config: Knex.Config | string
398
398
  ): Knex<TRecord, TResult>;
399
399
 
400
+ export declare namespace knex {
401
+ class QueryBuilder {
402
+ static extend(
403
+ methodName: string,
404
+ fn: <TRecord extends {} = any, TResult = unknown[]>(
405
+ this: Knex.QueryBuilder<TRecord, TResult>,
406
+ ...args: any[]
407
+ ) => Knex.QueryBuilder<TRecord, TResult>
408
+ ): void;
409
+ }
410
+
411
+ export class KnexTimeoutError extends Error {}
412
+ }
413
+
400
414
  export declare namespace Knex {
401
415
  //
402
416
  // Utility Types
@@ -1403,6 +1417,16 @@ export declare namespace Knex {
1403
1417
  }
1404
1418
 
1405
1419
  interface TypePreservingAggregation<TRecord = any, TResult = unknown[], TValue = any> {
1420
+ <
1421
+ TKey extends keyof ResolveTableType<TRecord>,
1422
+ TOptions extends { "as": string },
1423
+ TResult2 = AggregationQueryResult<TResult, {
1424
+ [k in TOptions["as"]]: ResolveTableType<TRecord>[TKey]
1425
+ }>
1426
+ >(
1427
+ columnName: Readonly<TKey>,
1428
+ options: Readonly<TOptions>
1429
+ ): QueryBuilder<TRecord, TResult2>;
1406
1430
  <
1407
1431
  TKey extends keyof ResolveTableType<TRecord>,
1408
1432
  TResult2 = AggregationQueryResult<TResult, Dict<ResolveTableType<TRecord>[TKey]>>
@@ -1718,7 +1742,7 @@ export declare namespace Knex {
1718
1742
  options: Readonly<{ [key: string]: any }>,
1719
1743
  handler: (readable: stream.PassThrough) => any
1720
1744
  ): Promise<any>;
1721
- stream(options?: Readonly<{ [key: string]: any }>): stream.PassThrough;
1745
+ stream(options?: Readonly<{ [key: string]: any }>): stream.PassThrough & AsyncIterable<ArrayMember<T>>;
1722
1746
  pipe<T extends NodeJS.WritableStream>(
1723
1747
  writable: T,
1724
1748
  options?: Readonly<{ [key: string]: any }>
@@ -1845,7 +1869,7 @@ export declare namespace Knex {
1845
1869
  columns: readonly string[],
1846
1870
  foreignKeyName?: string
1847
1871
  ): MultikeyForeignConstraintBuilder;
1848
- dropForeign(columnNames: readonly string[], foreignKeyName?: string): TableBuilder;
1872
+ dropForeign(columnNames: string | readonly string[], foreignKeyName?: string): TableBuilder;
1849
1873
  dropUnique(columnNames: readonly (string | Raw)[], indexName?: string): TableBuilder;
1850
1874
  dropPrimary(constraintName?: string): TableBuilder;
1851
1875
  dropIndex(columnNames: string | readonly (string | Raw)[], indexName?: string): TableBuilder;
@@ -2350,10 +2374,6 @@ interface MsSqlConnectionConfigBase {
2350
2374
  constraintName?: string;
2351
2375
  }
2352
2376
 
2353
- //
2354
- // Clients
2355
- //
2356
-
2357
2377
  class Client extends events.EventEmitter {
2358
2378
  constructor(config: Config);
2359
2379
  config: Config;
@@ -2408,18 +2428,6 @@ interface MsSqlConnectionConfigBase {
2408
2428
  assertCanCancelQuery(): void;
2409
2429
  cancelQuery(): void;
2410
2430
  }
2411
-
2412
- class QueryBuilder {
2413
- static extend(
2414
- methodName: string,
2415
- fn: <TRecord extends {} = any, TResult = unknown[]>(
2416
- this: QueryBuilder<TRecord, TResult>,
2417
- ...args: any[]
2418
- ) => QueryBuilder<TRecord, TResult>
2419
- ): void;
2420
- }
2421
-
2422
- export class KnexTimeoutError extends Error {}
2423
2431
  }
2424
2432
 
2425
2433
  export default knex;