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 +36 -2
- package/package.json +1 -1
- package/src/adapters/sequelize.js +16 -3
- package/src/bone.js +21 -4
- package/src/drivers/abstract/attribute.js +2 -1
- package/src/drivers/abstract/spellbook.js +2 -4
- package/src/drivers/mysql/spellbook.js +19 -1
- package/src/drivers/postgres/schema.js +6 -1
- package/src/drivers/sqlite/schema.js +5 -3
- package/src/realm.js +1 -2
- package/types/index.d.ts +14 -4
package/History.md
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
|
-
2.
|
|
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 /
|
|
46
|
+
2.0.2 / 2022-02-10
|
|
13
47
|
==================
|
|
14
48
|
|
|
15
49
|
## What's 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
|
@@ -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.
|
|
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() }, {
|
|
@@ -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
|
};
|
|
@@ -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 :
|
|
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
|
|
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,
|
|
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
|
|
638
|
-
descriptors
|
|
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
|
/**
|