@axiosleo/orm-mysql 0.9.4 → 0.9.6

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.4',
12
+ version: '0.9.6',
13
13
  commands_dir: path.join(__dirname, '../commands'),
14
14
  });
15
15
 
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,15 @@ 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';
37
41
 
38
42
  export interface JoinOption {
39
43
  table: string | Query;
40
44
  table_alias?: string;
41
- self_column: string;
42
- foreign_column: string;
45
+ self_column?: string;
46
+ foreign_column?: string;
43
47
  join_type?: 'left' | 'right' | 'inner';
48
+ on?: string;
44
49
  }
45
50
 
46
51
  export interface TableOption {
@@ -54,7 +59,7 @@ export type QueryOperatorBaseOptions = {
54
59
  };
55
60
 
56
61
  export type AttrSubQuery = () => Query;
57
- export type Attr = string | AttrSubQuery;
62
+ export type Attr = string | AttrSubQuery | Query;
58
63
 
59
64
  export type QueryOperatorOptions = QueryOperatorBaseOptions & {
60
65
  conditions: WhereOptions[];
@@ -110,7 +115,7 @@ export declare class Query {
110
115
  join(opt: JoinOption): this;
111
116
  }
112
117
 
113
- export type QueryResult = any | undefined | RowDataPacket[] | RowDataPacket | OkPacket;
118
+ export type QueryResult = any | undefined | RowDataPacket[] | RowDataPacket | MySQLQueryResult;
114
119
 
115
120
  export declare class QueryOperator extends Query {
116
121
  conn: Connection | Pool;
@@ -126,9 +131,9 @@ export declare class QueryOperator extends Query {
126
131
 
127
132
  find<T>(): Promise<T>;
128
133
 
129
- update(data?: any): Promise<OkPacket>;
134
+ update(data?: any): Promise<MySQLQueryResult>;
130
135
 
131
- insert(data?: any): Promise<OkPacket>;
136
+ insert(data?: any): Promise<MySQLQueryResult>;
132
137
 
133
138
  count(): Promise<number>;
134
139
 
@@ -137,7 +142,7 @@ export declare class QueryOperator extends Query {
137
142
  * @param id
138
143
  * @param index_field_name default is 'id'
139
144
  */
140
- delete(id?: number, index_field_name?: string): Promise<OkPacket>;
145
+ delete(id?: number, index_field_name?: string): Promise<MySQLQueryResult>;
141
146
  }
142
147
 
143
148
  export declare class QueryHandler {
@@ -165,7 +170,7 @@ export declare class QueryHandler {
165
170
  * @param data
166
171
  * @param condition
167
172
  */
168
- upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<OkPacket>;
173
+ upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<MySQLQueryResult>;
169
174
 
170
175
  /**
171
176
  * @param database default is options.database
@@ -211,7 +216,7 @@ export declare class TransactionHandler {
211
216
 
212
217
  rollback(): Promise<void>;
213
218
 
214
- upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<OkPacket>;
219
+ upsert(tableName: string, data: any, condition: Record<string, ConditionValueType>): Promise<MySQLQueryResult>;
215
220
  }
216
221
 
217
222
  export function createClient(options: ConnectionOptions, name?: string | null | undefined): Connection;
@@ -319,12 +324,6 @@ interface CreateIndexOptions {
319
324
  spatial?: boolean
320
325
  }
321
326
 
322
- export type ManageBuilderOptions = {
323
- operator: 'create' | 'drop' | 'alter';
324
- columns: Record<string, ColumnItem>;
325
- target: 'table' | 'column' | 'index' | 'foreign_key';
326
- }
327
-
328
327
  export declare class MigrationInterface {
329
328
 
330
329
  /**
@@ -348,7 +347,7 @@ export declare class MigrationInterface {
348
347
  unsigned?: boolean,
349
348
  allowNull?: boolean,
350
349
  default?: string | number | boolean | null | 'timestamp',
351
- onUpdate?: string,
350
+ onUpdate?: boolean,
352
351
  comment?: string,
353
352
  autoIncrement?: boolean,
354
353
  primaryKey?: boolean,
@@ -370,8 +369,8 @@ export declare class MigrationInterface {
370
369
  reference: {
371
370
  tableName: string,
372
371
  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',
372
+ onDelete?: CascadeType,
373
+ onUpdate?: CascadeType,
375
374
  }
376
375
  }): void;
377
376
 
@@ -383,6 +382,3 @@ export declare class MigrationInterface {
383
382
 
384
383
  dropForeignKey(foreign_key: string, tableName: string): void;
385
384
  }
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.4",
3
+ "version": "0.9.6",
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,12 +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);
44
+ this.values = this.values.concat(builder.values);
40
45
  let s = `(${builder.sql})`;
41
- if (query.alias) {
42
- return query.alias.indexOf(' ') > -1 ? s + ' ' + this._buildFieldKey(query.alias)
43
- : 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);
44
49
  }
45
50
  return s;
46
51
  }
@@ -48,9 +53,9 @@ class Builder {
48
53
  });
49
54
  emit(tmp, `SELECT ${attrs.length ? attrs.map((a) => this._buildFieldKey(a)).join(',') : '*'} FROM ${this._buildTables(options.tables)}`);
50
55
  emit(tmp, this._buildJoins(options.joins));
51
- emit(tmp, this._buildContidion(options.conditions));
56
+ emit(tmp, this._buildCondition(options.conditions));
52
57
  emit(tmp, this._buildOrders(options.orders));
53
- emit(tmp, this._buldPagenation(options.pageLimit, options.pageOffset));
58
+ emit(tmp, this._buildPagination(options.pageLimit, options.pageOffset));
54
59
  if (options.having && options.having.length && !options.groupField.length) {
55
60
  throw new Error('having is not allowed without "GROUP BY"');
56
61
  }
@@ -70,13 +75,16 @@ class Builder {
70
75
  break;
71
76
  }
72
77
  case 'update': {
78
+ if (is.invalid(options.data)) {
79
+ throw new Error('Data is required for update operation');
80
+ }
73
81
  const fields = this._buildValues(options.data);
74
82
  emit(tmp, `UPDATE ${this._buildTables(options.tables)}`);
75
83
  emit(tmp, `SET ${fields.map((f) => `\`${f}\` = ?`).join(',')}`);
76
84
  if (!options.conditions.length) {
77
85
  throw new Error('At least one condition is required for update operation');
78
86
  }
79
- emit(tmp, this._buildContidion(options.conditions));
87
+ emit(tmp, this._buildCondition(options.conditions));
80
88
  sql = tmp.join(' ');
81
89
  break;
82
90
  }
@@ -85,16 +93,20 @@ class Builder {
85
93
  if (!options.conditions.length) {
86
94
  throw new Error('At least one where condition is required for delete operation');
87
95
  }
88
- emit(tmp, this._buildContidion(options.conditions));
96
+ emit(tmp, this._buildCondition(options.conditions));
89
97
  sql = tmp.join(' ');
90
98
  break;
91
99
  }
92
100
  case 'count': {
93
- emit(tmp, `SELECT COUNT(*) AS count FROM ${this._buildTables(options.tables)}`);
101
+ let fieldName = 'count';
102
+ if (!is.empty(options.attrs)) {
103
+ fieldName = this._buildFieldKey(options.attrs[0]);
104
+ }
105
+ emit(tmp, `SELECT COUNT(*) AS ${fieldName} FROM ${this._buildTables(options.tables)}`);
94
106
  emit(tmp, this._buildJoins(options.joins));
95
- emit(tmp, this._buildContidion(options.conditions));
107
+ emit(tmp, this._buildCondition(options.conditions));
96
108
  if (options.having && options.having.length && !options.groupField.length) {
97
- throw new Error('having is not allowed without "GROUP BY"');
109
+ throw new Error('"HAVING" is not allowed without "GROUP BY"');
98
110
  }
99
111
  emit(tmp, this._buildGroupField(options.groupField));
100
112
  emit(tmp, this._buildHaving(options.having));
@@ -122,7 +134,7 @@ class Builder {
122
134
  if (!having || !having.length) {
123
135
  return '';
124
136
  }
125
- return this._buildContidion(having, 'HAVING ');
137
+ return this._buildCondition(having, 'HAVING ');
126
138
  }
127
139
 
128
140
  _buildJoins(joins = []) {
@@ -130,7 +142,7 @@ class Builder {
130
142
  let { table, alias, self_column, foreign_column, join_type } = j;
131
143
  if (table instanceof Query) {
132
144
  if (!alias) {
133
- throw new Error('Alias is required for subquery');
145
+ throw new Error('Alias is required for subQuery');
134
146
  }
135
147
  const builder = new Builder(table.options);
136
148
  this.values = this.values.concat(builder.values);
@@ -153,7 +165,11 @@ class Builder {
153
165
  sql = 'INNER JOIN ';
154
166
  break;
155
167
  }
156
- sql += `${table} ON ${this._buildFieldWithTableName(self_column)} = ${this._buildFieldWithTableName(foreign_column)}`;
168
+ if (j.on) {
169
+ sql += `${table} ON ${j.on}`;
170
+ } else {
171
+ sql += `${table} ON ${this._buildFieldWithTableName(self_column)} = ${this._buildFieldWithTableName(foreign_column)}`;
172
+ }
157
173
  return sql;
158
174
  }).join(' ');
159
175
  }
@@ -186,7 +202,7 @@ class Builder {
186
202
  }).join(' , ');
187
203
  }
188
204
 
189
- _buldPagenation(limit, offset) {
205
+ _buildPagination(limit, offset) {
190
206
  let sql = '';
191
207
  if (limit) {
192
208
  sql += ` LIMIT ${limit}`;
@@ -227,7 +243,7 @@ class Builder {
227
243
  return null;
228
244
  }
229
245
 
230
- _buildContidionIn(condition, isNot = false) {
246
+ _buildConditionIn(condition, isNot = false) {
231
247
  if (Array.isArray(condition.value) && !condition.value.length) {
232
248
  throw new Error('Value must not be empty for "IN" condition');
233
249
  } else if (!Array.isArray(condition.value) && !(condition.value instanceof Query)) {
@@ -244,7 +260,7 @@ class Builder {
244
260
  return res ? `${this._buildFieldKey(condition.key)} ${opt} (${res})` : `${this._buildFieldKey(condition.key)} ${opt} (?)`;
245
261
  }
246
262
 
247
- _buildContidion(conditions, prefix) {
263
+ _buildCondition(conditions, prefix) {
248
264
  if (!conditions || !conditions.length) {
249
265
  return '';
250
266
  }
@@ -265,7 +281,7 @@ class Builder {
265
281
  }
266
282
  if (c.key && c.key.indexOf('->') !== -1) {
267
283
  const keys = c.key.split('->');
268
- return this._buildContidion([
284
+ return this._buildCondition([
269
285
  {
270
286
  key: `JSON_EXTRACT(${this._buildFieldKey(keys[0])}, '${keys[1]}')`,
271
287
  opt: c.opt,
@@ -275,11 +291,11 @@ class Builder {
275
291
  }
276
292
  const opt = c.opt.toLowerCase();
277
293
  if (opt === 'in') {
278
- return this._buildContidionIn(c);
294
+ return this._buildConditionIn(c);
279
295
  } else if (opt === 'not in') {
280
- return this._buildContidionIn(c, true);
296
+ return this._buildConditionIn(c, true);
281
297
  } else if (opt === 'group' && Array.isArray(c.value)) {
282
- return `(${this._buildContidion(c.value, '')})`;
298
+ return `(${this._buildCondition(c.value, '')})`;
283
299
  }
284
300
  let res = this._buildConditionValues(c.value);
285
301
  if (!is.empty(res)) {
@@ -325,21 +341,24 @@ class Builder {
325
341
 
326
342
  class ManageSQLBuilder extends Builder {
327
343
  /**
328
- * @param {import('../index').ManageBuilderOptions} options
344
+ * @param {import('./migration').ManageBuilderOptions} options
329
345
  */
330
346
  constructor(options) {
331
- super({ operator: 'manage' });
332
- // const emitter = new Emitter();
333
- const action = `${options.operator}_${options.target}`;
334
- const method = _caml_case(action, false);
335
- if (!this[method]) {
336
- throw new Error(`'${options.target}' Unsupported '${options.operator}' operation.`);
337
- }
338
- try {
339
- this.sql = this[method].call(this, options);
340
- } catch (err) {
341
- debug.dump(`${options.operator} ${options.target} error: ${err.message}`);
342
- throw err;
347
+ if (operations.indexOf(options.operator) > -1) {
348
+ super(options);
349
+ } else {
350
+ super({ operator: 'manage' });
351
+ const action = `${options.operator}_${options.target}`;
352
+ const method = _caml_case(action, false);
353
+ if (!this[method]) {
354
+ throw new Error(`'${options.target}' Unsupported '${options.operator}' operation.`);
355
+ }
356
+ try {
357
+ this.sql = this[method].call(this, options);
358
+ } catch (err) {
359
+ debug.dump(`${options.operator} ${options.target} error: ${err.message}`);
360
+ throw err;
361
+ }
343
362
  }
344
363
  }
345
364
 
@@ -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/query.js CHANGED
@@ -180,6 +180,29 @@ class Query {
180
180
  this.options.joins.push({ table, alias: table_alias, self_column, foreign_column, join_type });
181
181
  return this;
182
182
  }
183
+
184
+ joinOn(table, alias, on, type = 'LEFT') {
185
+ if (!table) {
186
+ throw new Error('table is required');
187
+ }
188
+ if (!on) {
189
+ throw new Error('on is required');
190
+ }
191
+ this.options.joins.push({ table, alias, on, join_type: type });
192
+ return this;
193
+ }
194
+
195
+ leftJoin(table, alias, on) {
196
+ return this.joinOn(table, alias, on, 'LEFT');
197
+ }
198
+
199
+ rightJoin(table, alias, on) {
200
+ return this.joinOn(table, alias, on, 'RIGHT');
201
+ }
202
+
203
+ innerJoin(table, alias, on) {
204
+ return this.joinOn(table, alias, on, 'INNER');
205
+ }
183
206
  }
184
207
 
185
208
  module.exports = Query;