leoric 2.0.3 → 2.1.1

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/History.md CHANGED
@@ -1,4 +1,38 @@
1
- 2.0.3 / 2021-02-11
1
+ 2.1.1 / 2022-02-23
2
+ ==================
3
+
4
+ ## What's Changed
5
+ * fix: fix #274 update with fields option by @JimmyDaddy in https://github.com/cyjake/leoric/pull/275
6
+ * fix: upsert should set createdAt by default while createdAt not set by @JimmyDaddy in https://github.com/cyjake/leoric/pull/277
7
+ * fix: previousChanges should check instance is new record or not while specific attributes' values were undefined by @JimmyDaddy in https://github.com/cyjake/leoric/pull/276
8
+ * docs: add types for realm by @luckydrq in https://github.com/cyjake/leoric/pull/278
9
+
10
+ ## New Contributors
11
+ * @luckydrq made their first contribution in https://github.com/cyjake/leoric/pull/278
12
+
13
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v2.1.0...v2.1.1
14
+
15
+ 2.1.0 / 2022-02-17
16
+ ==================
17
+
18
+ ## What's Changed
19
+ * feat: fix #270 sequelize mode bulkBuild by @JimmyDaddy in https://github.com/cyjake/leoric/pull/273
20
+ * fix: mysql delete/remove/destroy with limit and orders by @JimmyDaddy in https://github.com/cyjake/leoric/pull/272
21
+
22
+
23
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v2.0.4...v2.1.0
24
+
25
+ 2.0.4 / 2022-02-16
26
+ ==================
27
+
28
+ ## What's Changed
29
+ * fix: fix unit test error by @LB4027221 in https://github.com/cyjake/leoric/pull/269
30
+ * fix: attribute.defaultValue should be set when init attributes by @cyjake in https://github.com/cyjake/leoric/pull/271
31
+
32
+
33
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v2.0.3...v2.0.4
34
+
35
+ 2.0.3 / 2022-02-11
2
36
  ==================
3
37
 
4
38
  ## What's Changed
@@ -9,7 +43,7 @@
9
43
 
10
44
  **Full Changelog**: https://github.com/cyjake/leoric/compare/v2.0.2...v2.0.3
11
45
 
12
- 2.0.2 / 2021-02-10
46
+ 2.0.2 / 2022-02-10
13
47
  ==================
14
48
 
15
49
  ## What's Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leoric",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "JavaScript Object-relational mapping alchemy",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -257,6 +257,17 @@ module.exports = Bone => {
257
257
  return instance;
258
258
  }
259
259
 
260
+ /**
261
+ * see https://github.com/sequelize/sequelize/blob/a729c4df41fa3a58fbecaf879265d2fb73d80e5f/src/model.js#L2299
262
+ * @param {Array<Object>} valueSets
263
+ * @param {Object} options
264
+ * @returns
265
+ */
266
+ static bulkBuild(valueSets, options = {}) {
267
+ if (!valueSets.length) return [];
268
+ return valueSets.map(value => this.build(value, options));
269
+ }
270
+
260
271
  // EXISTS
261
272
  // static bulkCreate() {}
262
273
 
@@ -308,7 +319,9 @@ module.exports = Bone => {
308
319
  // proxy to class.destroy({ individualHooks=false }) see https://github.com/sequelize/sequelize/blob/4063c2ab627ad57919d5b45cc7755f077a69fa5e/lib/model.js#L2895 before(after)BulkDestroy
309
320
  static async bulkDestroy(options = {}) {
310
321
  const { where, force } = options;
311
- return await this.remove(where || {}, force, { ...options });
322
+ const spell = this._remove(where || {}, force, { ...options });
323
+ translateOptions(spell, options);
324
+ return spell;
312
325
  }
313
326
 
314
327
  // EXISTS
@@ -495,11 +508,11 @@ module.exports = Bone => {
495
508
  // get isNewRecord() {}
496
509
 
497
510
  async update(values = {}, options = {}) {
498
- const { fields } = options;
511
+ const { fields = [] } = options;
499
512
  const changeValues = {};
500
513
  const originalValues = Object.assign({}, this.getRaw());
501
514
  for (const name in values) {
502
- if (values[name] !== undefined && this.hasAttribute(name)) {
515
+ if (values[name] !== undefined && this.hasAttribute(name) && (!fields.length || fields.includes(name))) {
503
516
  // exec custom setters in case it exist
504
517
  this[name] = values[name];
505
518
  changeValues[name] = this.attribute(name);
package/src/bone.js CHANGED
@@ -152,7 +152,6 @@ class Bone {
152
152
  }
153
153
  }
154
154
 
155
-
156
155
  /**
157
156
  * Get or set attribute value by name. This method is quite similiar to `jQuery.attr()`. If the attribute isn't selected when queried from database, an error will be thrown when accessing it.
158
157
  *
@@ -184,7 +183,6 @@ class Bone {
184
183
  }
185
184
 
186
185
  if (args.length > 1) {
187
- // execute validators
188
186
  this.#raw[name] = value instanceof Raw ? value : attribute.cast(value);
189
187
  this.#rawUnset.delete(name);
190
188
  return this;
@@ -620,9 +618,10 @@ class Bone {
620
618
  async update(values, options = {}) {
621
619
  const changes = {};
622
620
  const originalValues = Object.assign({}, this.#raw);
621
+ const { fields = [] } = options;
623
622
  if (typeof values === 'object') {
624
623
  for (const name in values) {
625
- if (values[name] !== undefined && this.hasAttribute(name)) {
624
+ if (values[name] !== undefined && this.hasAttribute(name) && (!fields.length || fields.includes(name))) {
626
625
  // exec custom setters in case it exist
627
626
  this[name] = values[name];
628
627
  changes[name] = this.attribute(name);
@@ -790,7 +789,7 @@ class Bone {
790
789
  }, opts);
791
790
  return result;
792
791
  }
793
- return await Model.remove(condition, forceDelete, { hooks: false, ...opts });
792
+ return await Model._remove(condition, forceDelete, opts);
794
793
  }
795
794
 
796
795
  /**
@@ -1455,6 +1454,24 @@ class Bone {
1455
1454
  * @return {Spell}
1456
1455
  */
1457
1456
  static remove(conditions, forceDelete = false, options) {
1457
+ return this._remove(conditions, forceDelete, options);
1458
+ }
1459
+
1460
+ /**
1461
+ * private method for internal calling
1462
+ * Remove any record that matches `conditions`.
1463
+ * - If `forceDelete` is true, `DELETE` records from database permanently.
1464
+ * - If not, update `deletedAt` attribute with current date.
1465
+ * - If `forceDelete` isn't true and `deleteAt` isn't around, throw an Error.
1466
+ * @example
1467
+ * Post.remove({ title: 'Leah' }) // mark Post { title: 'Leah' } as deleted
1468
+ * Post.remove({ title: 'Leah' }, true) // delete Post { title: 'Leah' }
1469
+ * Post.remove({}, true) // delete all data of posts
1470
+ * @param {Object} conditions
1471
+ * @param {boolean} forceDelete
1472
+ * @return {Spell}
1473
+ */
1474
+ static _remove(conditions, forceDelete = false, options) {
1458
1475
  const { deletedAt } = this.timestamps;
1459
1476
  if (forceDelete !== true && this.attributes[deletedAt]) {
1460
1477
  return Bone.update.call(this, conditions, { [deletedAt]: new Date() }, {
@@ -123,7 +123,8 @@ class Attribute {
123
123
  }
124
124
 
125
125
  cast(value) {
126
- return this.type.cast(value);
126
+ const castedValue = this.type.cast(value);
127
+ return castedValue == null? null : castedValue;
127
128
  }
128
129
 
129
130
  uncast(value, strict = true) {
@@ -356,15 +356,14 @@ function formatInsert(spell) {
356
356
  }
357
357
  } else {
358
358
  for (const name in involved) {
359
- // upsert should not update createdAt
360
- if (updateOnDuplicate && createdAt && name === createdAt) continue;
361
359
  attributes.push(Model.attributes[name]);
362
360
  }
363
361
  }
364
362
 
365
363
  for (const entry of attributes) {
366
364
  columns.push(entry.columnName);
367
- if (updateOnDuplicate && createdAt && entry.name === createdAt) continue;
365
+ if (updateOnDuplicate && createdAt && entry.name === createdAt
366
+ && !(Array.isArray(updateOnDuplicate) && updateOnDuplicate.includes(createdAt))) continue;
368
367
  updateOnDuplicateColumns.push(entry.columnName);
369
368
  }
370
369
 
@@ -385,7 +384,6 @@ function formatInsert(spell) {
385
384
  }
386
385
  for (const name in sets) {
387
386
  const value = sets[name];
388
- // upsert should not update createdAt
389
387
  columns.push(Model.unalias(name));
390
388
  if (value instanceof Raw) {
391
389
  values.push(SqlString.raw(value.value));
@@ -49,7 +49,10 @@ module.exports = {
49
49
  const sets = [];
50
50
  // Make sure the correct LAST_INSERT_ID is returned.
51
51
  // - https://stackoverflow.com/questions/778534/mysql-on-duplicate-key-last-insert-id
52
- sets.push(`${escapeId(primaryColumn)} = LAST_INSERT_ID(${escapeId(primaryColumn)})`);
52
+ // if insert attributes include primary column, `primaryKey = LAST_INSERT_ID(primaryKey)` is not need any more
53
+ if (!columns.includes(primaryColumn)) {
54
+ sets.push(`${escapeId(primaryColumn)} = LAST_INSERT_ID(${escapeId(primaryColumn)})`);
55
+ }
53
56
  sets.push(...columns.map(column => `${escapeId(column)}=VALUES(${escapeId(column)})`));
54
57
 
55
58
  return `ON DUPLICATE KEY UPDATE ${sets.join(', ')}`;
@@ -75,4 +78,19 @@ module.exports = {
75
78
 
76
79
  return result;
77
80
  },
81
+ /**
82
+ * DELETE ... ORDER BY ...LIMIT
83
+ * @param {Spell} spell
84
+ */
85
+ formatDelete(spell) {
86
+ const result = spellbook.formatDelete.call(this, spell);
87
+ const { rowCount, orders } = spell;
88
+ const chunks = [];
89
+
90
+ if (orders.length > 0) chunks.push(`ORDER BY ${this.formatOrders(spell, orders).join(', ')}`);
91
+ if (rowCount > 0) chunks.push(`LIMIT ${rowCount}`);
92
+ if (chunks.length > 0) result.sql += ` ${chunks.join(' ')}`;
93
+
94
+ return result;
95
+ }
78
96
  };
@@ -57,13 +57,18 @@ module.exports = {
57
57
 
58
58
  if (dataType === 'character varying') dataType = 'varchar';
59
59
  if (dataType === 'timestamp without time zone') dataType = 'timestamp';
60
+
61
+ let columnDefault = row.column_default;
62
+ if (/^NULL::/i.test(columnDefault)) columnDefault = null;
63
+ if (dataType === 'boolean') columnDefault = columnDefault === 'true';
64
+
60
65
  const primaryKey = row.constraint_type === 'PRIMARY KEY';
61
66
  const precision = row.datetime_precision;
62
67
 
63
68
  columns.push({
64
69
  columnName: row.column_name,
65
70
  columnType: length > 0 ? `${dataType}(${length})` : dataType,
66
- defaultValue: primaryKey ? null : row.column_default,
71
+ defaultValue: primaryKey ? null : columnDefault,
67
72
  dataType,
68
73
  allowNull: row.is_nullable !== 'NO',
69
74
  // https://www.postgresql.org/docs/9.5/infoschema-table-constraints.html
@@ -75,8 +75,9 @@ async function alterTableWithChangeColumn(driver, table, changes) {
75
75
  }
76
76
 
77
77
  // eslint-disable-next-line no-unused-vars
78
- function parseDefaultValue(text) {
78
+ function parseDefaultValue(text, type) {
79
79
  if (typeof text !== 'string') return text;
80
+ if (type === 'boolean') return text === 'true';
80
81
 
81
82
  try {
82
83
  const ast = parseExpr(text);
@@ -84,7 +85,7 @@ function parseDefaultValue(text) {
84
85
  return ast.value;
85
86
  }
86
87
  } catch (err) {
87
- debug('[parseDefaultValue] [%s] %s', text, err.stack);
88
+ debug('[parseDefaultValue] [%s] %s', text, err);
88
89
  }
89
90
 
90
91
  return text;
@@ -111,10 +112,11 @@ module.exports = {
111
112
  const columnType = type.toLowerCase();
112
113
  const [, dataType, precision ] = columnType.match(rColumnType);
113
114
  const primaryKey = pk === 1;
115
+
114
116
  const result = {
115
117
  columnName: name,
116
118
  columnType,
117
- defaultValue: parseDefaultValue(dflt_value),
119
+ defaultValue: parseDefaultValue(dflt_value, type),
118
120
  dataType: dataType,
119
121
  allowNull: primaryKey ? false : notnull == 0,
120
122
  primaryKey,
package/src/realm.js CHANGED
@@ -49,9 +49,8 @@ function initAttributes(model, columns) {
49
49
  const attributes = {};
50
50
 
51
51
  for (const columnInfo of columns) {
52
- const { columnName, columnType, defaultValue, ...restInfo } = columnInfo;
52
+ const { columnName, columnType, ...restInfo } = columnInfo;
53
53
  const name = columnName === '_id' ? columnName : camelCase(columnName);
54
- // leave out defaultValue to let database take over the default
55
54
  attributes[name] = {
56
55
  ...restInfo,
57
56
  columnName,
package/types/index.d.ts CHANGED
@@ -593,13 +593,15 @@ export class Bone {
593
593
  toObject(): InstanceValues<this>;
594
594
  }
595
595
 
596
- interface ConnectOptions {
596
+ export interface ConnectOptions {
597
597
  client?: 'mysql' | 'mysql2' | 'pg' | 'sqlite3' | '@journeyapps/sqlcipher';
598
598
  dialect?: 'mysql' | 'postgres' | 'sqlite';
599
599
  host?: string;
600
+ port?: number | string;
600
601
  user?: string;
601
602
  database: string;
602
603
  models?: string | (typeof Bone)[];
604
+ subclass?: boolean;
603
605
  }
604
606
 
605
607
  interface InitOptions {
@@ -612,6 +614,11 @@ interface InitOptions {
612
614
  };
613
615
  }
614
616
 
617
+ interface SyncOptions {
618
+ force?: boolean;
619
+ alter?: boolean;
620
+ }
621
+
615
622
  type RawSql = {
616
623
  __raw: true,
617
624
  value: string,
@@ -626,6 +633,7 @@ interface RawQueryOptions {
626
633
 
627
634
  export default class Realm {
628
635
  Bone: typeof Bone;
636
+ DataTypes: typeof DataType;
629
637
  driver: Driver;
630
638
  models: Record<string, Bone>;
631
639
 
@@ -633,9 +641,9 @@ export default class Realm {
633
641
 
634
642
  define(
635
643
  name: string,
636
- attributes: Record<string, AttributeMeta>,
637
- options: InitOptions,
638
- descriptors: Record<string, Function>,
644
+ attributes: Record<string, DataTypes<DataType> | AttributeMeta>,
645
+ options?: InitOptions,
646
+ descriptors?: Record<string, Function>,
639
647
  ): Bone;
640
648
 
641
649
  raw(sql: string): RawSql;
@@ -646,6 +654,8 @@ export default class Realm {
646
654
 
647
655
  transaction(callback: GeneratorFunction): Promise<void>;
648
656
  transaction(callback: (connection: Connection) => Promise<void>): Promise<void>;
657
+
658
+ sync(options?: SyncOptions): Promise<void>;
649
659
  }
650
660
 
651
661
  /**