leoric 2.0.4 → 2.2.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/History.md +34 -0
- package/index.js +1 -1
- package/package.json +1 -1
- package/src/adapters/sequelize.js +16 -3
- package/src/bone.js +21 -3
- package/src/data_types.js +61 -3
- package/src/drivers/abstract/attribute.js +10 -1
- package/src/drivers/abstract/spellbook.js +2 -4
- package/src/drivers/mysql/spellbook.js +19 -1
- package/src/drivers/postgres/data_types.js +10 -0
- package/src/realm.js +5 -0
- package/types/index.d.ts +16 -5
package/History.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
2.2.0 / 2022-02-24
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
## What's Changed
|
|
5
|
+
* fix: add missing `password` field for `ConnectOptions` by @luckydrq in https://github.com/cyjake/leoric/pull/280
|
|
6
|
+
* feat: integer types (mostly mysql specific) by @cyjake in https://github.com/cyjake/leoric/pull/281
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.1.1...v2.2.0
|
|
10
|
+
|
|
11
|
+
2.1.1 / 2022-02-23
|
|
12
|
+
==================
|
|
13
|
+
|
|
14
|
+
## What's Changed
|
|
15
|
+
* fix: fix #274 update with fields option by @JimmyDaddy in https://github.com/cyjake/leoric/pull/275
|
|
16
|
+
* fix: upsert should set createdAt by default while createdAt not set by @JimmyDaddy in https://github.com/cyjake/leoric/pull/277
|
|
17
|
+
* 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
|
|
18
|
+
* docs: add types for realm by @luckydrq in https://github.com/cyjake/leoric/pull/278
|
|
19
|
+
|
|
20
|
+
## New Contributors
|
|
21
|
+
* @luckydrq made their first contribution in https://github.com/cyjake/leoric/pull/278
|
|
22
|
+
|
|
23
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.1.0...v2.1.1
|
|
24
|
+
|
|
25
|
+
2.1.0 / 2022-02-17
|
|
26
|
+
==================
|
|
27
|
+
|
|
28
|
+
## What's Changed
|
|
29
|
+
* feat: fix #270 sequelize mode bulkBuild by @JimmyDaddy in https://github.com/cyjake/leoric/pull/273
|
|
30
|
+
* fix: mysql delete/remove/destroy with limit and orders by @JimmyDaddy in https://github.com/cyjake/leoric/pull/272
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
**Full Changelog**: https://github.com/cyjake/leoric/compare/v2.0.4...v2.1.0
|
|
34
|
+
|
|
1
35
|
2.0.4 / 2022-02-16
|
|
2
36
|
==================
|
|
3
37
|
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -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
|
-
|
|
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
|
@@ -183,7 +183,6 @@ class Bone {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
if (args.length > 1) {
|
|
186
|
-
// execute validators
|
|
187
186
|
this.#raw[name] = value instanceof Raw ? value : attribute.cast(value);
|
|
188
187
|
this.#rawUnset.delete(name);
|
|
189
188
|
return this;
|
|
@@ -619,9 +618,10 @@ class Bone {
|
|
|
619
618
|
async update(values, options = {}) {
|
|
620
619
|
const changes = {};
|
|
621
620
|
const originalValues = Object.assign({}, this.#raw);
|
|
621
|
+
const { fields = [] } = options;
|
|
622
622
|
if (typeof values === 'object') {
|
|
623
623
|
for (const name in values) {
|
|
624
|
-
if (values[name] !== undefined && this.hasAttribute(name)) {
|
|
624
|
+
if (values[name] !== undefined && this.hasAttribute(name) && (!fields.length || fields.includes(name))) {
|
|
625
625
|
// exec custom setters in case it exist
|
|
626
626
|
this[name] = values[name];
|
|
627
627
|
changes[name] = this.attribute(name);
|
|
@@ -789,7 +789,7 @@ class Bone {
|
|
|
789
789
|
}, opts);
|
|
790
790
|
return result;
|
|
791
791
|
}
|
|
792
|
-
return await Model.
|
|
792
|
+
return await Model._remove(condition, forceDelete, opts);
|
|
793
793
|
}
|
|
794
794
|
|
|
795
795
|
/**
|
|
@@ -1454,6 +1454,24 @@ class Bone {
|
|
|
1454
1454
|
* @return {Spell}
|
|
1455
1455
|
*/
|
|
1456
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) {
|
|
1457
1475
|
const { deletedAt } = this.timestamps;
|
|
1458
1476
|
if (forceDelete !== true && this.attributes[deletedAt]) {
|
|
1459
1477
|
return Bone.update.call(this, conditions, { [deletedAt]: new Date() }, {
|
package/src/data_types.js
CHANGED
|
@@ -14,7 +14,13 @@ const Raw = require('./raw');
|
|
|
14
14
|
|
|
15
15
|
class DataType {
|
|
16
16
|
static findType(columnType) {
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
STRING, TEXT,
|
|
19
|
+
DATE, DATEONLY,
|
|
20
|
+
TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT,
|
|
21
|
+
BOOLEAN,
|
|
22
|
+
BINARY, VARBINARY, BLOB,
|
|
23
|
+
} = this;
|
|
18
24
|
const [ , dataType, appendix ] = columnType.match(/(\w+)(?:\((\d+)\))?/);
|
|
19
25
|
const length = appendix && parseInt(appendix, 10);
|
|
20
26
|
|
|
@@ -39,10 +45,13 @@ class DataType {
|
|
|
39
45
|
case 'int':
|
|
40
46
|
case 'integer':
|
|
41
47
|
case 'numeric':
|
|
48
|
+
return new INTEGER(length);
|
|
42
49
|
case 'mediumint':
|
|
50
|
+
return new MEDIUMINT(length);
|
|
43
51
|
case 'smallint':
|
|
52
|
+
return new SMALLINT(length);
|
|
44
53
|
case 'tinyint':
|
|
45
|
-
return new
|
|
54
|
+
return new TINYINT(length);
|
|
46
55
|
case 'bigint':
|
|
47
56
|
return new BIGINT(length);
|
|
48
57
|
case 'boolean':
|
|
@@ -203,12 +212,58 @@ class INTEGER extends DataType {
|
|
|
203
212
|
}
|
|
204
213
|
}
|
|
205
214
|
|
|
215
|
+
/**
|
|
216
|
+
* 8 bit integer
|
|
217
|
+
* @example
|
|
218
|
+
* TINYINT
|
|
219
|
+
* TINYINT.UNSIGNED
|
|
220
|
+
* TINYINT(1)
|
|
221
|
+
* @param {number} length
|
|
222
|
+
*/
|
|
223
|
+
class TINYINT extends INTEGER {
|
|
224
|
+
constructor(length) {
|
|
225
|
+
super(length);
|
|
226
|
+
this.dataType = 'tinyint';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 16 bit integer
|
|
232
|
+
* @example
|
|
233
|
+
* SMALLINT
|
|
234
|
+
* SMALLINT.UNSIGNED
|
|
235
|
+
* SMALLINT(2)
|
|
236
|
+
* @param {number} length
|
|
237
|
+
*/
|
|
238
|
+
class SMALLINT extends INTEGER {
|
|
239
|
+
constructor(length) {
|
|
240
|
+
super(length);
|
|
241
|
+
this.dataType = 'smallint';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 24 bit integer
|
|
247
|
+
* @example
|
|
248
|
+
* MEDIUMINT
|
|
249
|
+
* MEDIUMINT.UNSIGNED
|
|
250
|
+
* MEDIUMINT(3)
|
|
251
|
+
* @param {number} length
|
|
252
|
+
*/
|
|
253
|
+
class MEDIUMINT extends INTEGER {
|
|
254
|
+
constructor(length) {
|
|
255
|
+
super(length);
|
|
256
|
+
this.dataType = 'mediumint';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
206
261
|
/**
|
|
207
262
|
* 64 bit integer
|
|
208
263
|
* @example
|
|
209
264
|
* BIGINT
|
|
210
265
|
* BIGINT.UNSIGNED
|
|
211
|
-
* BIGINT(
|
|
266
|
+
* BIGINT(8)
|
|
212
267
|
* @param {number} length
|
|
213
268
|
*/
|
|
214
269
|
class BIGINT extends INTEGER {
|
|
@@ -422,6 +477,9 @@ class JSONB extends JSON {
|
|
|
422
477
|
|
|
423
478
|
const DataTypes = {
|
|
424
479
|
STRING,
|
|
480
|
+
TINYINT,
|
|
481
|
+
SMALLINT,
|
|
482
|
+
MEDIUMINT,
|
|
425
483
|
INTEGER,
|
|
426
484
|
BIGINT,
|
|
427
485
|
DATE,
|
|
@@ -65,6 +65,14 @@ function createType(DataTypes, params) {
|
|
|
65
65
|
switch (type.constructor.name) {
|
|
66
66
|
case 'DATE':
|
|
67
67
|
return new DataType(type.precision, type.timezone);
|
|
68
|
+
case 'TINYINT':
|
|
69
|
+
case 'SMALLINT':
|
|
70
|
+
case 'MEDIUMINT':
|
|
71
|
+
case 'INTEGER':
|
|
72
|
+
case 'BIGINT':
|
|
73
|
+
case 'BINARY':
|
|
74
|
+
case 'VARBINARY':
|
|
75
|
+
return new DataType(type.length);
|
|
68
76
|
default:
|
|
69
77
|
return new DataType();
|
|
70
78
|
}
|
|
@@ -123,7 +131,8 @@ class Attribute {
|
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
cast(value) {
|
|
126
|
-
|
|
134
|
+
const castedValue = this.type.cast(value);
|
|
135
|
+
return castedValue == null? null : castedValue;
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
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
|
|
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
|
-
|
|
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
|
};
|
|
@@ -61,12 +61,22 @@ class Postgres_BIGINT extends Postgres_INTEGER {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
class Postgres_SMALLINT extends Postgres_INTEGER {
|
|
65
|
+
constructor() {
|
|
66
|
+
super();
|
|
67
|
+
this.dataType = 'smallint';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
64
71
|
class Postgres_DataTypes extends DataTypes {
|
|
65
72
|
static DATE = Postgres_DATE;
|
|
66
73
|
static JSONB = Postgres_JSONB;
|
|
67
74
|
static BINARY = Postgres_BINARY;
|
|
68
75
|
static VARBINARY = Postgres_BINARY;
|
|
69
76
|
static BLOB = Postgres_BINARY;
|
|
77
|
+
static TINYINT = Postgres_SMALLINT;
|
|
78
|
+
static SMALLINT = Postgres_SMALLINT;
|
|
79
|
+
static MEDIUMINT = Postgres_INTEGER;
|
|
70
80
|
static INTEGER = Postgres_INTEGER;
|
|
71
81
|
static BIGINT = Postgres_BIGINT;
|
|
72
82
|
}
|
package/src/realm.js
CHANGED
|
@@ -129,6 +129,11 @@ class Realm {
|
|
|
129
129
|
this.options = Spine.options = options;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
get DataTypes() {
|
|
133
|
+
if (!this.driver) throw new Error('database not connected yet');
|
|
134
|
+
return this.driver.DataTypes;
|
|
135
|
+
}
|
|
136
|
+
|
|
132
137
|
define(name, attributes, opts = {}, descriptors = {}) {
|
|
133
138
|
const Model = class extends this.Bone {
|
|
134
139
|
static name = name;
|
package/types/index.d.ts
CHANGED
|
@@ -593,13 +593,16 @@ 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;
|
|
602
|
+
password?: string;
|
|
601
603
|
database: string;
|
|
602
604
|
models?: string | (typeof Bone)[];
|
|
605
|
+
subclass?: boolean;
|
|
603
606
|
}
|
|
604
607
|
|
|
605
608
|
interface InitOptions {
|
|
@@ -612,6 +615,11 @@ interface InitOptions {
|
|
|
612
615
|
};
|
|
613
616
|
}
|
|
614
617
|
|
|
618
|
+
interface SyncOptions {
|
|
619
|
+
force?: boolean;
|
|
620
|
+
alter?: boolean;
|
|
621
|
+
}
|
|
622
|
+
|
|
615
623
|
type RawSql = {
|
|
616
624
|
__raw: true,
|
|
617
625
|
value: string,
|
|
@@ -626,6 +634,7 @@ interface RawQueryOptions {
|
|
|
626
634
|
|
|
627
635
|
export default class Realm {
|
|
628
636
|
Bone: typeof Bone;
|
|
637
|
+
DataTypes: typeof DataType;
|
|
629
638
|
driver: Driver;
|
|
630
639
|
models: Record<string, Bone>;
|
|
631
640
|
|
|
@@ -633,10 +642,10 @@ export default class Realm {
|
|
|
633
642
|
|
|
634
643
|
define(
|
|
635
644
|
name: string,
|
|
636
|
-
attributes: Record<string, AttributeMeta>,
|
|
637
|
-
options
|
|
638
|
-
descriptors
|
|
639
|
-
): Bone;
|
|
645
|
+
attributes: Record<string, DataTypes<DataType> | AttributeMeta>,
|
|
646
|
+
options?: InitOptions,
|
|
647
|
+
descriptors?: Record<string, Function>,
|
|
648
|
+
): typeof Bone;
|
|
640
649
|
|
|
641
650
|
raw(sql: string): RawSql;
|
|
642
651
|
|
|
@@ -646,6 +655,8 @@ export default class Realm {
|
|
|
646
655
|
|
|
647
656
|
transaction(callback: GeneratorFunction): Promise<void>;
|
|
648
657
|
transaction(callback: (connection: Connection) => Promise<void>): Promise<void>;
|
|
658
|
+
|
|
659
|
+
sync(options?: SyncOptions): Promise<void>;
|
|
649
660
|
}
|
|
650
661
|
|
|
651
662
|
/**
|