@axiosleo/orm-mysql 0.9.5 → 0.9.7

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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "recommendations": [
3
+ "vscode-icons-team.vscode-icons",
4
+ "formulahendry.docker-extension-pack",
5
+ "mikestead.dotenv",
6
+ "dbaeumer.vscode-eslint",
7
+ "codezombiech.gitignore",
8
+ "eamodio.gitlens",
9
+ "esbenp.prettier-vscode",
10
+ "wayou.vscode-todo-highlight",
11
+ "vue.vscode-typescript-vue-plugin",
12
+ "octref.vetur",
13
+ "pflannery.vscode-versionlens"
14
+ ]
15
+ }
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  [![NPM version](https://img.shields.io/npm/v/@axiosleo/orm-mysql.svg?style=flat-square)](https://npmjs.org/package/@axiosleo/orm-mysql)
5
5
  [![npm download](https://img.shields.io/npm/dm/@axiosleo/orm-mysql.svg?style=flat-square)](https://npmjs.org/package/@axiosleo/orm-mysql)
6
+ [![node version](https://img.shields.io/badge/node.js-%3E=_16.0-green.svg?style=flat-square)](http://nodejs.org/download/)
6
7
  [![License](https://img.shields.io/github/license/AxiosLeo/node-orm-mysql?color=%234bc524)](LICENSE)
7
8
  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FAxiosLeo%2Fnode-orm-mysql.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FAxiosLeo%2Fnode-orm-mysql?ref=badge_shield)
8
9
 
package/bin/orm-mysql.js CHANGED
@@ -9,7 +9,7 @@ const app = new App({
9
9
  name: 'MySQL ORM CLI',
10
10
  desc: 'migrate, model, seed, etc.',
11
11
  bin: 'orm-mysql',
12
- version: '0.9.5',
12
+ version: '0.9.7',
13
13
  commands_dir: path.join(__dirname, '../commands'),
14
14
  });
15
15
 
@@ -69,10 +69,18 @@ function up(migration) {
69
69
  account_id: {
70
70
  type: 'int',
71
71
  allowNull: false,
72
+ references: {
73
+ table: 'account',
74
+ column: 'id'
75
+ }
72
76
  },
73
77
  org_id: {
74
78
  type: 'int',
75
79
  allowNull: false,
80
+ references: {
81
+ table: 'orgs',
82
+ column: 'id'
83
+ }
76
84
  },
77
85
  type: {
78
86
  type: 'varchar',
@@ -83,24 +91,6 @@ function up(migration) {
83
91
  }
84
92
  });
85
93
  migration.createColumn('created_at', 'TIMESTAMP', 'account_orgs');
86
- migration.createForeignKey({
87
- tableName: 'account_orgs',
88
- columnName: 'account_id',
89
- reference: {
90
- tableName: 'account',
91
- columnName: 'id',
92
- onDelete: 'CASCADE'
93
- }
94
- });
95
- migration.createForeignKey({
96
- tableName: 'account_orgs',
97
- columnName: 'org_id',
98
- reference: {
99
- tableName: 'orgs',
100
- columnName: 'id',
101
- onDelete: 'CASCADE'
102
- }
103
- });
104
94
  }
105
95
 
106
96
  /**
package/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  PoolOptions,
6
6
  QueryOptions,
7
7
  RowDataPacket,
8
+ ResultSetHeader,
8
9
  ConnectionOptions
9
10
  } from 'mysql2';
10
11
 
@@ -12,6 +13,8 @@ import {
12
13
  Connection as PromiseConnection,
13
14
  } from 'mysql2/promise';
14
15
 
16
+ type MySQLQueryResult = OkPacket | ResultSetHeader;
17
+
15
18
  export type Clients = {
16
19
  [key: string]: Connection | Pool
17
20
  }
@@ -34,13 +37,16 @@ export interface OrderByOptions {
34
37
  }
35
38
 
36
39
  export type OperatorType = 'select' | 'find' | 'insert' | 'update' | 'delete' | 'count';
40
+ export type CascadeType = 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'NO ACTION' | 'restrict' | 'cascade' | 'set null' | 'no action';
41
+ export type JoinType = 'left' | 'right' | 'inner' | 'LEFT' | 'RIGHT' | 'INNER';
37
42
 
38
43
  export interface JoinOption {
39
44
  table: string | Query;
40
45
  table_alias?: string;
41
- self_column: string;
42
- foreign_column: string;
43
- join_type?: 'left' | 'right' | 'inner';
46
+ self_column?: string;
47
+ foreign_column?: string;
48
+ join_type?: JoinType;
49
+ on?: string;
44
50
  }
45
51
 
46
52
  export interface TableOption {
@@ -54,7 +60,7 @@ export type QueryOperatorBaseOptions = {
54
60
  };
55
61
 
56
62
  export type AttrSubQuery = () => Query;
57
- export type Attr = string | AttrSubQuery;
63
+ export type Attr = string | AttrSubQuery | Query;
58
64
 
59
65
  export type QueryOperatorOptions = QueryOperatorBaseOptions & {
60
66
  conditions: WhereOptions[];
@@ -108,9 +114,15 @@ export declare class Query {
108
114
  set(data: any): this;
109
115
 
110
116
  join(opt: JoinOption): this;
117
+
118
+ leftJoin(table: string, on: string, options?: { alias?: string }): this;
119
+
120
+ rightJoin(table: string, on: string, options?: { alias?: string }): this;
121
+
122
+ innerJoin(table: string, on: string, options?: { alias?: string }): this;
111
123
  }
112
124
 
113
- export type QueryResult = any | undefined | RowDataPacket[] | RowDataPacket | OkPacket;
125
+ export type QueryResult = any | undefined | RowDataPacket[] | RowDataPacket | MySQLQueryResult;
114
126
 
115
127
  export declare class QueryOperator extends Query {
116
128
  conn: Connection | Pool;
@@ -126,9 +138,9 @@ export declare class QueryOperator extends Query {
126
138
 
127
139
  find<T>(): Promise<T>;
128
140
 
129
- update(data?: any): Promise<OkPacket>;
141
+ update(data?: any): Promise<MySQLQueryResult>;
130
142
 
131
- insert(data?: any): Promise<OkPacket>;
143
+ insert(data?: any): Promise<MySQLQueryResult>;
132
144
 
133
145
  count(): Promise<number>;
134
146
 
@@ -137,7 +149,7 @@ export declare class QueryOperator extends Query {
137
149
  * @param id
138
150
  * @param index_field_name default is 'id'
139
151
  */
140
- delete(id?: number, index_field_name?: string): Promise<OkPacket>;
152
+ delete(id?: number, index_field_name?: string): Promise<MySQLQueryResult>;
141
153
  }
142
154
 
143
155
  export declare class QueryHandler {
@@ -165,7 +177,7 @@ export declare class QueryHandler {
165
177
  * @param data
166
178
  * @param condition
167
179
  */
168
- upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<OkPacket>;
180
+ upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<MySQLQueryResult>;
169
181
 
170
182
  /**
171
183
  * @param database default is options.database
@@ -211,7 +223,7 @@ export declare class TransactionHandler {
211
223
 
212
224
  rollback(): Promise<void>;
213
225
 
214
- upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<OkPacket>;
226
+ upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<MySQLQueryResult>;
215
227
  }
216
228
 
217
229
  export function createClient(options: ConnectionOptions, name?: string | null | undefined): Connection;
@@ -299,7 +311,13 @@ interface ColumnItem {
299
311
  comment?: string,
300
312
  autoIncrement?: boolean,
301
313
  primaryKey?: boolean,
302
- uniqIndex?: boolean
314
+ uniqIndex?: boolean,
315
+ references?: {
316
+ table: string,
317
+ column: string,
318
+ onDelete?: CascadeType,
319
+ onUpdate?: CascadeType
320
+ }
303
321
  }
304
322
 
305
323
  interface CreateColumnOptions {
@@ -319,12 +337,6 @@ interface CreateIndexOptions {
319
337
  spatial?: boolean
320
338
  }
321
339
 
322
- export type ManageBuilderOptions = {
323
- operator: 'create' | 'drop' | 'alter';
324
- columns: Record<string, ColumnItem>;
325
- target: 'table' | 'column' | 'index' | 'foreign_key';
326
- }
327
-
328
340
  export declare class MigrationInterface {
329
341
 
330
342
  /**
@@ -348,7 +360,7 @@ export declare class MigrationInterface {
348
360
  unsigned?: boolean,
349
361
  allowNull?: boolean,
350
362
  default?: string | number | boolean | null | 'timestamp',
351
- onUpdate?: string,
363
+ onUpdate?: boolean,
352
364
  comment?: string,
353
365
  autoIncrement?: boolean,
354
366
  primaryKey?: boolean,
@@ -370,8 +382,8 @@ export declare class MigrationInterface {
370
382
  reference: {
371
383
  tableName: string,
372
384
  columnName: string,
373
- onDelete?: 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'NO ACTION' | 'restrict' | 'cascade' | 'set null' | 'no action',
374
- onUpdate?: 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'NO ACTION' | 'restrict' | 'cascade' | 'set null' | 'no action',
385
+ onDelete?: CascadeType,
386
+ onUpdate?: CascadeType,
375
387
  }
376
388
  }): void;
377
389
 
@@ -383,6 +395,3 @@ export declare class MigrationInterface {
383
395
 
384
396
  dropForeignKey(foreign_key: string, tableName: string): void;
385
397
  }
386
-
387
- export declare function up(migration: MigrationInterface): Promise<void>;
388
- export declare function down(migration: MigrationInterface): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiosleo/orm-mysql",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
4
4
  "description": "MySQL ORM tool",
5
5
  "keywords": [
6
6
  "mysql",
@@ -19,12 +19,15 @@
19
19
  "test-cov": "nyc -r=lcov -r=html -r=text -r=json mocha -t 10000 -R spec tests/*.tests.js",
20
20
  "test-one": "mocha --reporter spec --timeout 3000 ",
21
21
  "ci": "npm run lint && npm run test-cov",
22
- "clear": "rm -rf ./nyc_output ./coverage"
22
+ "clear": "rm -rf ./nyc_output ./coverage && rm -rf ./node_modules && npm cache clean --force"
23
23
  },
24
24
  "license": "MIT",
25
+ "engines": {
26
+ "node": ">=16.0.0"
27
+ },
25
28
  "dependencies": {
26
- "@axiosleo/cli-tool": "^1.6.0",
27
- "mysql2": "^2.3.3",
29
+ "@axiosleo/cli-tool": "^1.6.1",
30
+ "mysql2": "^3.9.1",
28
31
  "validatorjs": "^3.22.1"
29
32
  },
30
33
  "repository": {
@@ -32,19 +35,18 @@
32
35
  "url": "https://github.com/AxiosLeo/node-orm-mysql"
33
36
  },
34
37
  "devDependencies": {
35
- "@types/node": "^20.8.3",
36
- "chai": "^4.2.0",
37
- "chai-as-promised": "^7.1.1",
38
- "eslint": "^7.0",
38
+ "@types/node": "^20.11.16",
39
+ "chai": "^5.0.3",
40
+ "eslint": "^8.56.0",
39
41
  "expect.js": "^0.3.1",
40
- "has-flag": "^4.0.0",
41
- "mm": "^3.2.0",
42
- "mocha": "^9.1.3",
42
+ "has-flag": "^5.0.1",
43
+ "mm": "^3.4.0",
44
+ "mocha": "^10.2.0",
43
45
  "mocha-sinon": "^2.1.2",
44
46
  "nyc": "^15.1.0",
45
47
  "pre-commit": "^1.2.2",
46
- "sinon": "^9.0.2",
47
- "typescript": "^4.3.2"
48
+ "sinon": "^17.0.1",
49
+ "typescript": "^5.3.3"
48
50
  },
49
51
  "pre-commit": {
50
52
  "silent": false,
package/src/builder.js CHANGED
@@ -17,6 +17,8 @@ const emit = (arr, res) => {
17
17
  }
18
18
  };
19
19
 
20
+ const operations = ['find', 'select', 'insert', 'update', 'delete', 'count', 'manage'];
21
+
20
22
  class Builder {
21
23
  /**
22
24
  * @param {import('../index').QueryOperatorOptions} options
@@ -35,13 +37,15 @@ class Builder {
35
37
  options.attrs = options.attrs || [];
36
38
  const attrs = options.attrs.map((attr) => {
37
39
  if (attr instanceof Function) {
38
- const query = attr();
39
- const builder = new Builder(query.options);
40
+ attr = attr();
41
+ }
42
+ if (attr instanceof Query) {
43
+ const builder = new Builder(attr.options);
40
44
  this.values = this.values.concat(builder.values);
41
45
  let s = `(${builder.sql})`;
42
- if (query.alias) {
43
- return query.alias.indexOf(' ') > -1 ? s + ' ' + this._buildFieldKey(query.alias)
44
- : s + ' AS ' + this._buildFieldKey(query.alias);
46
+ if (attr.alias) {
47
+ return attr.alias.indexOf(' ') > -1 ? s + ' ' + this._buildFieldKey(attr.alias)
48
+ : s + ' AS ' + this._buildFieldKey(attr.alias);
45
49
  }
46
50
  return s;
47
51
  }
@@ -71,6 +75,9 @@ class Builder {
71
75
  break;
72
76
  }
73
77
  case 'update': {
78
+ if (is.invalid(options.data)) {
79
+ throw new Error('Data is required for update operation');
80
+ }
74
81
  const fields = this._buildValues(options.data);
75
82
  emit(tmp, `UPDATE ${this._buildTables(options.tables)}`);
76
83
  emit(tmp, `SET ${fields.map((f) => `\`${f}\` = ?`).join(',')}`);
@@ -95,7 +102,7 @@ class Builder {
95
102
  emit(tmp, this._buildJoins(options.joins));
96
103
  emit(tmp, this._buildCondition(options.conditions));
97
104
  if (options.having && options.having.length && !options.groupField.length) {
98
- throw new Error('having is not allowed without "GROUP BY"');
105
+ throw new Error('"HAVING" is not allowed without "GROUP BY"');
99
106
  }
100
107
  emit(tmp, this._buildGroupField(options.groupField));
101
108
  emit(tmp, this._buildHaving(options.having));
@@ -154,7 +161,11 @@ class Builder {
154
161
  sql = 'INNER JOIN ';
155
162
  break;
156
163
  }
157
- sql += `${table} ON ${this._buildFieldWithTableName(self_column)} = ${this._buildFieldWithTableName(foreign_column)}`;
164
+ if (j.on) {
165
+ sql += `${table} ON ${j.on}`;
166
+ } else {
167
+ sql += `${table} ON ${this._buildFieldWithTableName(self_column)} = ${this._buildFieldWithTableName(foreign_column)}`;
168
+ }
158
169
  return sql;
159
170
  }).join(' ');
160
171
  }
@@ -326,21 +337,24 @@ class Builder {
326
337
 
327
338
  class ManageSQLBuilder extends Builder {
328
339
  /**
329
- * @param {import('../index').ManageBuilderOptions} options
340
+ * @param {import('./migration').ManageBuilderOptions} options
330
341
  */
331
342
  constructor(options) {
332
- super({ operator: 'manage' });
333
- // const emitter = new Emitter();
334
- const action = `${options.operator}_${options.target}`;
335
- const method = _caml_case(action, false);
336
- if (!this[method]) {
337
- throw new Error(`'${options.target}' Unsupported '${options.operator}' operation.`);
338
- }
339
- try {
340
- this.sql = this[method].call(this, options);
341
- } catch (err) {
342
- debug.dump(`${options.operator} ${options.target} error: ${err.message}`);
343
- throw err;
343
+ if (operations.indexOf(options.operator) > -1) {
344
+ super(options);
345
+ } else {
346
+ super({ operator: 'manage' });
347
+ const action = `${options.operator}_${options.target}`;
348
+ const method = _caml_case(action, false);
349
+ if (!this[method]) {
350
+ throw new Error(`'${options.target}' Unsupported '${options.operator}' operation.`);
351
+ }
352
+ try {
353
+ this.sql = this[method].call(this, options);
354
+ } catch (err) {
355
+ debug.dump(`${options.operator} ${options.target} error: ${err.message}`);
356
+ throw err;
357
+ }
344
358
  }
345
359
  }
346
360
 
@@ -366,7 +380,7 @@ class ManageSQLBuilder extends Builder {
366
380
  engine: 'InnoDB',
367
381
  charset: 'utf8mb4'
368
382
  }, options, {
369
- columns: this.createColumns(columns)
383
+ columns: this.createColumns(columns, options.name)
370
384
  });
371
385
  return _render('CREATE TABLE `${name}` ( ${columns} ) ENGINE=${engine} DEFAULT CHARSET=${charset}', options);
372
386
  }
@@ -468,9 +482,10 @@ class ManageSQLBuilder extends Builder {
468
482
  return _render('ALTER TABLE `${table}` DROP FOREIGN KEY `${name}`', options);
469
483
  }
470
484
 
471
- createColumns(columns) {
485
+ createColumns(columns, table) {
472
486
  let primaryColumn = null;
473
487
  let indexColumns = [];
488
+ let referenceColumns = [];
474
489
  let strs = columns.map(column => {
475
490
  let str = this.renderSingleColumn(column);
476
491
  if (column.primaryKey === true) {
@@ -478,6 +493,28 @@ class ManageSQLBuilder extends Builder {
478
493
  } else if (column.uniqIndex === true) {
479
494
  indexColumns.push(column);
480
495
  }
496
+ if (column.reference) {
497
+ column.reference.onDelete = column.reference.onDelete ? column.reference.onDelete.toUpperCase() : 'NO ACTION';
498
+ column.reference.onUpdate = column.reference.onUpdate ? column.reference.onUpdate.toUpperCase() : 'NO ACTION';
499
+
500
+ _validate(column.reference, {
501
+ table: 'required|string',
502
+ column: 'required|string',
503
+ onDelete: [{ in: ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION'] }],
504
+ onUpdate: [{ in: ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION'] }]
505
+ });
506
+ referenceColumns.push({
507
+ name: 'fk_' + table + '_' + column.name,
508
+ table,
509
+ column: column.name,
510
+ reference: {
511
+ tableName: column.reference.table,
512
+ columnName: column.reference.column,
513
+ onDelete: column.reference.onDelete,
514
+ onUpdate: column.reference.onUpdate
515
+ }
516
+ });
517
+ }
481
518
  return str;
482
519
  });
483
520
  if (primaryColumn) {
@@ -489,6 +526,11 @@ class ManageSQLBuilder extends Builder {
489
526
  strs.push(`UNIQUE INDEX \`${i.name}\` (\`${i.name}\` ASC) VISIBLE`);
490
527
  });
491
528
  }
529
+ if (referenceColumns.length) {
530
+ referenceColumns.forEach((r) => {
531
+ strs.push(this.createForeignKey(r));
532
+ });
533
+ }
492
534
  return strs.join(', ');
493
535
  }
494
536
 
@@ -1,8 +1,19 @@
1
+ import { ColumnItem } from '../index';
2
+
1
3
  type QueryItem = {
2
4
  sql: string,
3
5
  values: any[],
4
6
  };
5
7
 
8
+ export type ManageBuilderOptions = {
9
+ operator: 'create' | 'drop' | 'alter';
10
+ columns: Record<string, ColumnItem>;
11
+ target: 'table' | 'column' | 'index' | 'foreign_key';
12
+ name?: string;
13
+ engine?: 'InnoDB' | 'MyISAM' | 'MEMORY',
14
+ charset?: string
15
+ }
16
+
6
17
  export type Context = {
7
18
  action: 'up' | 'down',
8
19
  connection: {
package/src/migration.js CHANGED
@@ -114,16 +114,16 @@ async function _exec(context, queries) {
114
114
  try {
115
115
  const files = Object.keys(queries);
116
116
  await _foreach(files, async (file) => {
117
- const hasMigarated = await transaction.table(context.task_key)
117
+ const hasMigrated = await transaction.table(context.task_key)
118
118
  .where('migration_key', context.task_key)
119
119
  .where('filename', file)
120
120
  .count();
121
- if (context.action === 'up' && hasMigarated) {
122
- if (hasMigarated) {
121
+ if (context.action === 'up' && hasMigrated) {
122
+ if (hasMigrated) {
123
123
  printer.yellow(`Migration file "${file}" has been migrated.`).println();
124
124
  return;
125
125
  }
126
- } else if (context.action === 'down' && !hasMigarated) {
126
+ } else if (context.action === 'down' && !hasMigrated) {
127
127
  return;
128
128
  }
129
129
 
package/src/query.js CHANGED
@@ -1,5 +1,21 @@
1
1
  'use strict';
2
2
 
3
+ const { _assign } = require('@axiosleo/cli-tool/src/helper/obj');
4
+ const { _validate } = require('./utils');
5
+ const is = require('@axiosleo/cli-tool/src/helper/is');
6
+
7
+ function joinOn(table, on, options = {}) {
8
+ let o = _assign({ alias: null, join_type: 'INNER', table, on }, options);
9
+ if (!table) {
10
+ throw new Error('table is required');
11
+ }
12
+ if (!on) {
13
+ throw new Error('on is required');
14
+ }
15
+ this.options.joins.push(o);
16
+ return this;
17
+ }
18
+
3
19
  class Query {
4
20
  constructor(operator = 'select', alias = null) {
5
21
  this.options = {
@@ -109,6 +125,10 @@ class Query {
109
125
  }
110
126
 
111
127
  attr(...attr) {
128
+ if (attr.length === 1 && is.array(attr)) {
129
+ this.options.attrs = attr;
130
+ return this;
131
+ }
112
132
  if (!attr.length) {
113
133
  return this;
114
134
  }
@@ -164,22 +184,33 @@ class Query {
164
184
  * @returns
165
185
  */
166
186
  join(opt = {}) {
187
+ let types = ['left', 'right', 'inner'];
188
+ opt.join_type = opt.join_type ? opt.join_type.toLowerCase() : 'inner';
189
+ if (types.indexOf(opt.join_type) === -1) {
190
+ throw new Error('Invalid join type : ' + opt.join_type + '; only supported ' + types.join(', '));
191
+ }
192
+ _validate(opt, {
193
+ table: 'required',
194
+ self_column: 'required',
195
+ foreign_column: 'required_if:on',
196
+ join_type: [{ in: types }]
197
+ });
167
198
  let { table, table_alias, self_column, foreign_column, join_type } = opt;
168
- if (!table) {
169
- throw new Error('table is required');
170
- }
171
- if (!self_column) {
172
- throw new Error('self_column is required');
173
- }
174
- if (!foreign_column) {
175
- throw new Error('foreign_column is required');
176
- }
177
- if (join_type && ['left', 'right', 'inner'].indexOf(join_type) === -1) {
178
- throw new Error('Invalid join type : ' + join_type + '; only supported left, right, inner');
179
- }
180
199
  this.options.joins.push({ table, alias: table_alias, self_column, foreign_column, join_type });
181
200
  return this;
182
201
  }
202
+
203
+ leftJoin(table, on, options = {}) {
204
+ return joinOn.call(this, table, on, { ...options, join_type: 'LEFT' });
205
+ }
206
+
207
+ rightJoin(table, on, options = {}) {
208
+ return joinOn.call(this, table, on, { ...options, join_type: 'RIGHT' });
209
+ }
210
+
211
+ innerJoin(table, on, options = {}) {
212
+ return joinOn.call(this, table, on, { ...options, join_type: 'INNER' });
213
+ }
183
214
  }
184
215
 
185
216
  module.exports = Query;