leoric 2.15.0-alpha.3 → 2.15.0-beta.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/Readme.md +13 -13
- package/dist/abstract_bone.d.ts +22 -2
- package/dist/abstract_bone.js +143 -47
- package/dist/abstract_bone.js.map +1 -1
- package/dist/adapters/sequelize.d.ts +62 -391
- package/dist/adapters/sequelize.js +15 -8
- package/dist/adapters/sequelize.js.map +1 -1
- package/dist/bone.js +3 -1
- package/dist/bone.js.map +1 -1
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +2 -0
- package/dist/browser.js.map +1 -1
- package/dist/collection.js +0 -1
- package/dist/collection.js.map +1 -1
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/decorators.d.ts +3 -0
- package/dist/decorators.js +5 -0
- package/dist/decorators.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +8 -0
- package/dist/model.js +74 -0
- package/dist/model.js.map +1 -0
- package/dist/query_object.d.ts +5 -1
- package/dist/raw.d.ts +2 -0
- package/dist/raw.js +8 -3
- package/dist/raw.js.map +1 -1
- package/dist/realm/base.d.ts +5 -1
- package/dist/realm/base.js +48 -19
- package/dist/realm/base.js.map +1 -1
- package/dist/spell.js +12 -17
- package/dist/spell.js.map +1 -1
- package/dist/types/common.d.ts +8 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +6 -1
- package/dist/utils/index.js.map +1 -1
- package/lib/abstract_bone.d.ts +22 -2
- package/lib/abstract_bone.js +150 -47
- package/lib/abstract_bone.js.map +1 -1
- package/lib/adapters/sequelize.d.ts +62 -391
- package/lib/adapters/sequelize.js +12 -7
- package/lib/adapters/sequelize.js.map +1 -1
- package/lib/bone.js +2 -0
- package/lib/bone.js.map +1 -1
- package/lib/browser.d.ts +2 -0
- package/lib/browser.js +6 -1
- package/lib/browser.js.map +1 -1
- package/lib/collection.js +0 -1
- package/lib/collection.js.map +1 -1
- package/lib/constants.d.ts +2 -1
- package/lib/constants.js +3 -1
- package/lib/constants.js.map +1 -1
- package/lib/decorators.d.ts +3 -0
- package/lib/decorators.js +6 -0
- package/lib/decorators.js.map +1 -1
- package/lib/drivers/sqljs/sqljs-connection.js +34 -1
- package/lib/drivers/sqljs/sqljs-connection.js.map +1 -1
- package/lib/index.d.ts +6 -2
- package/lib/index.js +11 -2
- package/lib/index.js.map +1 -1
- package/lib/model.d.ts +8 -0
- package/lib/model.js +80 -0
- package/lib/model.js.map +1 -0
- package/lib/query_object.d.ts +5 -1
- package/lib/raw.d.ts +2 -0
- package/lib/raw.js +4 -0
- package/lib/raw.js.map +1 -1
- package/lib/realm/base.d.ts +5 -1
- package/lib/realm/base.js +48 -19
- package/lib/realm/base.js.map +1 -1
- package/lib/spell.js +11 -16
- package/lib/spell.js.map +1 -1
- package/lib/types/common.d.ts +8 -1
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +6 -0
- package/lib/utils/index.js.map +1 -1
- package/package.json +6 -1
- package/types/ts4.9/abstract_bone.d.ts +22 -2
- package/types/ts4.9/browser.d.ts +2 -0
- package/types/ts4.9/constants.d.ts +2 -1
- package/types/ts4.9/decorators.d.ts +3 -0
- package/types/ts4.9/index.d.ts +6 -2
- package/types/ts4.9/model.d.ts +8 -0
- package/types/ts4.9/query_object.d.ts +5 -1
- package/types/ts4.9/raw.d.ts +2 -0
- package/types/ts4.9/realm/base.d.ts +5 -1
- package/types/ts4.9/types/common.d.ts +8 -1
- package/types/ts4.9/utils/index.d.ts +1 -0
package/Readme.md
CHANGED
|
@@ -13,19 +13,19 @@ Leoric is an object-relational mapping library for Node.js, which is heavily inf
|
|
|
13
13
|
Assume the tables of posts, users, and comments were setup already. We may declare the models as classes by extending from the base class `Bone` of Leoric. After the models are connected to the database, the columns of the tables are mapped as attributes, the associations are setup, feel free to start querying.
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
|
-
import { Bone
|
|
16
|
+
import Realm, { Bone } from 'leoric'
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
class Post extends Bone {
|
|
18
|
+
const realm = new Realm({ host: 'example.com' })
|
|
19
|
+
const Post = realm.define(class Post extends Bone {
|
|
20
20
|
static initialize() {
|
|
21
21
|
this.belongsTo('author', { Model: 'User' })
|
|
22
22
|
this.hasMany('comments')
|
|
23
23
|
}
|
|
24
|
-
}
|
|
24
|
+
})
|
|
25
25
|
|
|
26
26
|
async function main() {
|
|
27
27
|
// connect models to database
|
|
28
|
-
await connect(
|
|
28
|
+
await realm.connect()
|
|
29
29
|
|
|
30
30
|
// CRUD
|
|
31
31
|
await Post.create({ title: 'New Post' })
|
|
@@ -47,15 +47,15 @@ If table structures were intended to be maintained in the models, Leoric can be
|
|
|
47
47
|
```js
|
|
48
48
|
import Realm, { Bone, DataTypes } from 'leoric';
|
|
49
49
|
const { BIGINT, STRING } = DataTypes;
|
|
50
|
-
|
|
50
|
+
const realm = new Realm();
|
|
51
|
+
const Post = realm.define(class Post extends Bone {
|
|
51
52
|
static attributes = {
|
|
52
53
|
id: { type: BIGINT, primaryKey: true },
|
|
53
54
|
email: { type: STRING, allowNull: false },
|
|
54
55
|
nickname: { type: STRING, allowNull: false },
|
|
55
56
|
}
|
|
56
|
-
}
|
|
57
|
+
});
|
|
57
58
|
|
|
58
|
-
const realm = new Realm({ models: [ Post ] });
|
|
59
59
|
await realm.sync();
|
|
60
60
|
```
|
|
61
61
|
|
|
@@ -83,19 +83,19 @@ import User from './user';
|
|
|
83
83
|
|
|
84
84
|
export default class Post extends Bone {
|
|
85
85
|
@Column({ autoIncrement: true })
|
|
86
|
-
id: bigint;
|
|
86
|
+
declare id: bigint;
|
|
87
87
|
|
|
88
88
|
@Column(TEXT)
|
|
89
|
-
content: string;
|
|
89
|
+
declare content: string;
|
|
90
90
|
|
|
91
91
|
@Column()
|
|
92
|
-
description: string;
|
|
92
|
+
declare description: string;
|
|
93
93
|
|
|
94
94
|
@Column()
|
|
95
|
-
userId: bigint;
|
|
95
|
+
declare userId: bigint;
|
|
96
96
|
|
|
97
97
|
@BelongsTo()
|
|
98
|
-
user: User;
|
|
98
|
+
declare user: User;
|
|
99
99
|
}
|
|
100
100
|
```
|
|
101
101
|
|
package/dist/abstract_bone.d.ts
CHANGED
|
@@ -24,16 +24,26 @@ export interface InitOptions {
|
|
|
24
24
|
export declare const columnAttributesKey: unique symbol;
|
|
25
25
|
export declare const synchronizedKey: unique symbol;
|
|
26
26
|
export declare const tableKey: unique symbol;
|
|
27
|
+
export declare const hasLoadedAttributesKey: unique symbol;
|
|
28
|
+
export declare class LeoricModelDefinitionError extends Error {
|
|
29
|
+
constructor(modelName: string);
|
|
30
|
+
}
|
|
31
|
+
export declare class LeoricClassFieldError extends Error {
|
|
32
|
+
modelName: string;
|
|
33
|
+
attributeName: string;
|
|
34
|
+
constructor(modelName: string, attributeName: string);
|
|
35
|
+
}
|
|
27
36
|
export declare class AbstractBone {
|
|
28
|
-
#private;
|
|
29
37
|
static DataTypes: typeof DataTypes;
|
|
30
38
|
[key: string]: any;
|
|
39
|
+
[key: symbol]: any;
|
|
31
40
|
isNewRecord: boolean;
|
|
32
41
|
static [columnAttributesKey]: {
|
|
33
42
|
[key: string]: Attribute;
|
|
34
43
|
} | null;
|
|
35
44
|
static [synchronizedKey]: boolean;
|
|
36
45
|
static [tableKey]: string;
|
|
46
|
+
static [hasLoadedAttributesKey]: boolean;
|
|
37
47
|
static get synchronized(): boolean;
|
|
38
48
|
static set synchronized(value: boolean);
|
|
39
49
|
/**
|
|
@@ -199,6 +209,7 @@ export declare class AbstractBone {
|
|
|
199
209
|
static select<T extends typeof AbstractBone>(this: T, ...names: Array<BoneColumns<T>> | string[]): Spell<T>;
|
|
200
210
|
static select<T extends typeof AbstractBone>(this: T, ...names: string[]): Spell<T>;
|
|
201
211
|
static select<T extends typeof AbstractBone>(this: T, ...names: Raw[]): Spell<T>;
|
|
212
|
+
static select<T extends typeof AbstractBone>(this: T, ...names: Array<string | Raw>): Spell<T>;
|
|
202
213
|
static select<T extends typeof AbstractBone>(this: T, filter: (name: string) => boolean): Spell<T>;
|
|
203
214
|
/**
|
|
204
215
|
* JOIN arbitrary models with given ON conditions
|
|
@@ -317,6 +328,8 @@ export declare class AbstractBone {
|
|
|
317
328
|
attribute<T, Key extends keyof T, U extends T[Key]>(this: T, name: Key): U extends Literal ? U : Literal;
|
|
318
329
|
attribute<T, Key extends keyof Values<T>>(this: T, name: Key, value: Literal): this;
|
|
319
330
|
attribute<T, Key extends keyof T>(this: T, name: Key, value: Literal): this;
|
|
331
|
+
attribute(name: string): Literal;
|
|
332
|
+
attribute(name: string, value: Literal): this;
|
|
320
333
|
/**
|
|
321
334
|
* instance.hasAttribute(name)
|
|
322
335
|
* @param name
|
|
@@ -437,7 +450,9 @@ export declare class AbstractBone {
|
|
|
437
450
|
*/
|
|
438
451
|
reload(): Promise<this>;
|
|
439
452
|
/**
|
|
440
|
-
* Protected clone
|
|
453
|
+
* Protected clone — replaces internal state with merged data from target.
|
|
454
|
+
* Note: `this` here is the proxy (all method calls go through proxy). The proxy
|
|
455
|
+
* has no `set` trap, so Symbol-key assignments delegate to the underlying target.
|
|
441
456
|
*/
|
|
442
457
|
_clone(target: any): void;
|
|
443
458
|
/**
|
|
@@ -479,3 +494,8 @@ export declare class AbstractBone {
|
|
|
479
494
|
*/
|
|
480
495
|
toObject(): any;
|
|
481
496
|
}
|
|
497
|
+
export declare function markModelClassReady<T extends typeof AbstractBone>(Model: T): T;
|
|
498
|
+
export declare function isModelClassReady(Model: typeof AbstractBone): boolean;
|
|
499
|
+
export declare function markModelClassFieldsChecked<T extends typeof AbstractBone>(Model: T): T;
|
|
500
|
+
export declare function hasCheckedModelClassFields(Model: typeof AbstractBone): boolean;
|
|
501
|
+
export declare function assertModelClassFields(instance: AbstractBone): void;
|
package/dist/abstract_bone.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet, __rest } from "tslib";
|
|
1
|
+
import { __awaiter, __rest } from "tslib";
|
|
3
2
|
import util, { isDeepStrictEqual } from 'util';
|
|
4
3
|
import Debug from 'debug';
|
|
5
4
|
import pluralize from 'pluralize';
|
|
@@ -17,6 +16,36 @@ export const columnAttributesKey = Symbol('leoric#columnAttributes');
|
|
|
17
16
|
export const synchronizedKey = Symbol('leoric#synchronized');
|
|
18
17
|
export const tableKey = Symbol('leoric#table');
|
|
19
18
|
const debug = Debug('leoric');
|
|
19
|
+
// Module-private Symbol keys for internal instance state. The properties are initialized
|
|
20
|
+
// with enumerable: false so they are invisible to assert.deepStrictEqual (which only compares
|
|
21
|
+
// own enumerable Symbol keys), matching the behavior of the original private fields.
|
|
22
|
+
const RAW = Symbol('leoric#raw');
|
|
23
|
+
const RAW_SAVED = Symbol('leoric#rawSaved');
|
|
24
|
+
const RAW_UNSET = Symbol('leoric#rawUnset');
|
|
25
|
+
const RAW_PREVIOUS = Symbol('leoric#rawPrevious');
|
|
26
|
+
const MODEL_READY = Symbol('leoric#modelReady');
|
|
27
|
+
const MODEL_CLASS_FIELDS_CHECKED = Symbol('leoric#modelClassFieldsChecked');
|
|
28
|
+
// Short accessors for internal state
|
|
29
|
+
function $raw(self) { return self[RAW]; }
|
|
30
|
+
function $rawSaved(self) { return self[RAW_SAVED]; }
|
|
31
|
+
function $rawUnset(self) { return self[RAW_UNSET]; }
|
|
32
|
+
function $rawPrevious(self) { return self[RAW_PREVIOUS]; }
|
|
33
|
+
// Kept as a compatibility export for adapters and consumers that reference the symbol.
|
|
34
|
+
export const hasLoadedAttributesKey = Symbol('leoric#hasLoadedAttributes');
|
|
35
|
+
export class LeoricModelDefinitionError extends Error {
|
|
36
|
+
constructor(modelName) {
|
|
37
|
+
super(`${modelName} is not a registered Leoric model. Add it to the realm models, decorate it with @Model(), or define it through realm.define().`);
|
|
38
|
+
this.name = 'LeoricModelDefinitionError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class LeoricClassFieldError extends Error {
|
|
42
|
+
constructor(modelName, attributeName) {
|
|
43
|
+
super(`${modelName}.${attributeName} is emitted as an ES class field and shadows Leoric's attribute accessor. Add \`declare\`, decorate ${modelName} with @Model(), or define it through realm.define(${modelName}, attributes).`);
|
|
44
|
+
this.name = 'LeoricClassFieldError';
|
|
45
|
+
this.modelName = modelName;
|
|
46
|
+
this.attributeName = attributeName;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
20
49
|
export class AbstractBone {
|
|
21
50
|
static get synchronized() {
|
|
22
51
|
return this[synchronizedKey];
|
|
@@ -44,12 +73,17 @@ export class AbstractBone {
|
|
|
44
73
|
return this[tableKey];
|
|
45
74
|
}
|
|
46
75
|
constructor(values, opts = {}) {
|
|
47
|
-
// private state
|
|
48
|
-
_AbstractBone_raw.set(this, {});
|
|
49
|
-
_AbstractBone_rawSaved.set(this, {});
|
|
50
|
-
_AbstractBone_rawUnset.set(this, new Set());
|
|
51
|
-
_AbstractBone_rawPrevious.set(this, {});
|
|
52
76
|
this.isNewRecord = true;
|
|
77
|
+
const Model = new.target;
|
|
78
|
+
if (Model !== AbstractBone && !isModelClassReady(Model)) {
|
|
79
|
+
throw new LeoricModelDefinitionError(Model.name);
|
|
80
|
+
}
|
|
81
|
+
// Initialize internal Symbol-keyed properties as non-enumerable so they are
|
|
82
|
+
// invisible to assert.deepStrictEqual (which only compares own enumerable Symbol keys).
|
|
83
|
+
Object.defineProperty(this, RAW, { value: {}, writable: true, configurable: true, enumerable: false });
|
|
84
|
+
Object.defineProperty(this, RAW_SAVED, { value: {}, writable: true, configurable: true, enumerable: false });
|
|
85
|
+
Object.defineProperty(this, RAW_UNSET, { value: new Set(), writable: true, configurable: true, enumerable: false });
|
|
86
|
+
Object.defineProperty(this, RAW_PREVIOUS, { value: {}, writable: true, configurable: true, enumerable: false });
|
|
53
87
|
Object.defineProperty(this, 'isNewRecord', {
|
|
54
88
|
value: opts.isNewRecord !== undefined ? opts.isNewRecord : true,
|
|
55
89
|
configurable: true,
|
|
@@ -190,6 +224,7 @@ export class AbstractBone {
|
|
|
190
224
|
set(value) {
|
|
191
225
|
return this.attribute(name, value);
|
|
192
226
|
} }, customDescriptor), { enumerable: true, configurable: true }));
|
|
227
|
+
this[hasLoadedAttributesKey] = true;
|
|
193
228
|
}
|
|
194
229
|
static hasOne(name, options) {
|
|
195
230
|
options = (Object.assign(Object.assign({ className: capitalize(name), foreignKey: camelCase(`${this.name}Id`) }, options), { hasMany: false }));
|
|
@@ -236,6 +271,7 @@ export class AbstractBone {
|
|
|
236
271
|
opts = opts !== null && opts !== void 0 ? opts : {};
|
|
237
272
|
const data = Object.assign({}, values);
|
|
238
273
|
const instance = new this(data);
|
|
274
|
+
assertModelClassFields(instance);
|
|
239
275
|
return instance.create(Object.assign({}, opts));
|
|
240
276
|
}
|
|
241
277
|
/**
|
|
@@ -296,7 +332,11 @@ export class AbstractBone {
|
|
|
296
332
|
const autoIncrement = attribute.autoIncrement || (attribute.jsType == Number && attribute.primaryKey);
|
|
297
333
|
if (options.validate !== false)
|
|
298
334
|
records.map(entry => this._validateAttributes(entry));
|
|
299
|
-
const instances = records.map(entry =>
|
|
335
|
+
const instances = records.map(entry => {
|
|
336
|
+
const instance = new this(entry);
|
|
337
|
+
assertModelClassFields(instance);
|
|
338
|
+
return instance;
|
|
339
|
+
});
|
|
300
340
|
if (options.individualHooks) {
|
|
301
341
|
yield Promise.all(instances.map(instance => instance.save(options)));
|
|
302
342
|
return instances;
|
|
@@ -474,6 +514,7 @@ export class AbstractBone {
|
|
|
474
514
|
if (!options || options.validate !== false) {
|
|
475
515
|
const validateData = copyValues(values);
|
|
476
516
|
const instance = new this(validateData);
|
|
517
|
+
assertModelClassFields(instance);
|
|
477
518
|
instance._validateAttributes(validateData);
|
|
478
519
|
}
|
|
479
520
|
let spell = new Spell(this, options).$where(conditions).$update(data);
|
|
@@ -674,6 +715,7 @@ export class AbstractBone {
|
|
|
674
715
|
var _a;
|
|
675
716
|
const { attributes, attributeMap } = this;
|
|
676
717
|
const instance = new this();
|
|
718
|
+
assertModelClassFields(instance);
|
|
677
719
|
const skipCloneValue = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.skipCloneValue) === true;
|
|
678
720
|
for (const columnName in row) {
|
|
679
721
|
const value = row[columnName];
|
|
@@ -778,9 +820,10 @@ export class AbstractBone {
|
|
|
778
820
|
}
|
|
779
821
|
}
|
|
780
822
|
this[columnAttributesKey] = null;
|
|
823
|
+
markModelClassReady(this);
|
|
781
824
|
}
|
|
782
825
|
static from(table) {
|
|
783
|
-
return new Spell(this).$from(table);
|
|
826
|
+
return new Spell(this).$from(table).later(Collection.init);
|
|
784
827
|
}
|
|
785
828
|
/**
|
|
786
829
|
* Restore soft-deleted rows by clearing deletedAt.
|
|
@@ -799,33 +842,33 @@ export class AbstractBone {
|
|
|
799
842
|
// raw accessors
|
|
800
843
|
getRaw(key) {
|
|
801
844
|
if (key)
|
|
802
|
-
return
|
|
803
|
-
return
|
|
845
|
+
return $raw(this)[key];
|
|
846
|
+
return $raw(this);
|
|
804
847
|
}
|
|
805
848
|
getRawSaved(key) {
|
|
806
849
|
if (key)
|
|
807
|
-
return
|
|
808
|
-
return
|
|
850
|
+
return $rawSaved(this)[key];
|
|
851
|
+
return $rawSaved(this);
|
|
809
852
|
}
|
|
810
853
|
getRawPrevious(key) {
|
|
811
854
|
if (key)
|
|
812
|
-
return
|
|
813
|
-
return
|
|
855
|
+
return $rawPrevious(this)[key];
|
|
856
|
+
return $rawPrevious(this);
|
|
814
857
|
}
|
|
815
858
|
_setRaw(...args) {
|
|
816
859
|
const [name, value] = args;
|
|
817
860
|
if (args.length > 1) {
|
|
818
|
-
|
|
861
|
+
$raw(this)[name] = value;
|
|
819
862
|
}
|
|
820
863
|
else if (args.length === 1 && name !== undefined && typeof name === 'object') {
|
|
821
|
-
|
|
864
|
+
this[RAW] = name;
|
|
822
865
|
}
|
|
823
866
|
}
|
|
824
867
|
_getRawUnset() {
|
|
825
|
-
return
|
|
868
|
+
return $rawUnset(this);
|
|
826
869
|
}
|
|
827
870
|
_setRawSaved(key, value) {
|
|
828
|
-
|
|
871
|
+
$rawSaved(this)[key] = value;
|
|
829
872
|
}
|
|
830
873
|
/**
|
|
831
874
|
* @example
|
|
@@ -838,13 +881,13 @@ export class AbstractBone {
|
|
|
838
881
|
if (!attribute)
|
|
839
882
|
throw new Error(`${this.constructor.name} has no attribute "${name}"`);
|
|
840
883
|
if (arguments.length > 1) {
|
|
841
|
-
|
|
842
|
-
|
|
884
|
+
$raw(this)[name] = value instanceof Raw ? value : attribute.cast(value);
|
|
885
|
+
$rawUnset(this).delete(name);
|
|
843
886
|
return this;
|
|
844
887
|
}
|
|
845
|
-
if (
|
|
888
|
+
if ($rawUnset(this).has(name))
|
|
846
889
|
return;
|
|
847
|
-
const rawValue =
|
|
890
|
+
const rawValue = $raw(this)[name];
|
|
848
891
|
return rawValue == null ? null : rawValue;
|
|
849
892
|
}
|
|
850
893
|
/**
|
|
@@ -863,7 +906,7 @@ export class AbstractBone {
|
|
|
863
906
|
* bone.attributeWas('foo') // => 1
|
|
864
907
|
*/
|
|
865
908
|
attributeWas(name) {
|
|
866
|
-
const value =
|
|
909
|
+
const value = $rawSaved(this)[name];
|
|
867
910
|
return value == null ? null : value;
|
|
868
911
|
}
|
|
869
912
|
/**
|
|
@@ -873,7 +916,7 @@ export class AbstractBone {
|
|
|
873
916
|
* bone.attributeChanged('foo')
|
|
874
917
|
*/
|
|
875
918
|
attributeChanged(name) {
|
|
876
|
-
if (
|
|
919
|
+
if ($rawUnset(this).has(name) || !this.hasAttribute(name))
|
|
877
920
|
return false;
|
|
878
921
|
const value = this.attribute(name);
|
|
879
922
|
const valueWas = this.attributeWas(name);
|
|
@@ -890,7 +933,7 @@ export class AbstractBone {
|
|
|
890
933
|
*/
|
|
891
934
|
changes(name) {
|
|
892
935
|
if (name != null) {
|
|
893
|
-
if (
|
|
936
|
+
if ($rawUnset(this).has(name) || !this.hasAttribute(name))
|
|
894
937
|
return {};
|
|
895
938
|
const value = this.attribute(name);
|
|
896
939
|
const valueWas = this.attributeWas(name);
|
|
@@ -900,7 +943,7 @@ export class AbstractBone {
|
|
|
900
943
|
}
|
|
901
944
|
const result = {};
|
|
902
945
|
for (const attrKey of Object.keys(this.constructor.attributes)) {
|
|
903
|
-
if (
|
|
946
|
+
if ($rawUnset(this).has(attrKey))
|
|
904
947
|
continue;
|
|
905
948
|
const value = this.attribute(attrKey);
|
|
906
949
|
const valueWas = this.attributeWas(attrKey);
|
|
@@ -920,21 +963,21 @@ export class AbstractBone {
|
|
|
920
963
|
}
|
|
921
964
|
previousChanges(name) {
|
|
922
965
|
if (name != null) {
|
|
923
|
-
if (
|
|
966
|
+
if ($rawUnset(this).has(name) || $rawPrevious(this)[name] === undefined || !this.hasAttribute(name)) {
|
|
924
967
|
return {};
|
|
925
968
|
}
|
|
926
969
|
const value = this.attribute(name);
|
|
927
|
-
const valueWas =
|
|
970
|
+
const valueWas = $rawPrevious(this)[name] == null ? null : $rawPrevious(this)[name];
|
|
928
971
|
if (isDeepStrictEqual(value, valueWas))
|
|
929
972
|
return {};
|
|
930
973
|
return { [name]: [valueWas, value] };
|
|
931
974
|
}
|
|
932
975
|
const result = {};
|
|
933
976
|
for (const attrKey of Object.keys(this.constructor.attributes)) {
|
|
934
|
-
if (
|
|
977
|
+
if ($rawUnset(this).has(attrKey) || $rawPrevious(this)[attrKey] === undefined)
|
|
935
978
|
continue;
|
|
936
979
|
const value = this.attribute(attrKey);
|
|
937
|
-
const valueWas =
|
|
980
|
+
const valueWas = $rawPrevious(this)[attrKey] == null ? null : $rawPrevious(this)[attrKey];
|
|
938
981
|
if (!isDeepStrictEqual(value, valueWas))
|
|
939
982
|
result[attrKey] = [valueWas, value];
|
|
940
983
|
}
|
|
@@ -958,8 +1001,9 @@ export class AbstractBone {
|
|
|
958
1001
|
*/
|
|
959
1002
|
_save() {
|
|
960
1003
|
return __awaiter(this, arguments, void 0, function* (opts = {}) {
|
|
1004
|
+
assertModelClassFields(this);
|
|
961
1005
|
const { primaryKey } = this.constructor;
|
|
962
|
-
if (
|
|
1006
|
+
if ($rawUnset(this).has(primaryKey))
|
|
963
1007
|
throw new Error(`unset primary key ${primaryKey}`);
|
|
964
1008
|
if (this[primaryKey] == null) {
|
|
965
1009
|
yield this.create(opts);
|
|
@@ -989,17 +1033,17 @@ export class AbstractBone {
|
|
|
989
1033
|
const attribute = attributes[name];
|
|
990
1034
|
let value;
|
|
991
1035
|
try {
|
|
992
|
-
value = attribute.uncast(
|
|
1036
|
+
value = attribute.uncast($raw(this)[name]);
|
|
993
1037
|
}
|
|
994
1038
|
catch (error) {
|
|
995
1039
|
console.error(error);
|
|
996
|
-
value =
|
|
1040
|
+
value = $raw(this)[name];
|
|
997
1041
|
}
|
|
998
|
-
if (
|
|
999
|
-
|
|
1000
|
-
else if (
|
|
1001
|
-
|
|
1002
|
-
|
|
1042
|
+
if ($rawSaved(this)[name] !== undefined)
|
|
1043
|
+
$rawPrevious(this)[name] = $rawSaved(this)[name];
|
|
1044
|
+
else if ($rawPrevious(this)[name] === undefined && $raw(this)[name] != null)
|
|
1045
|
+
$rawPrevious(this)[name] = null;
|
|
1046
|
+
$rawSaved(this)[name] = attribute.cast(value);
|
|
1003
1047
|
}
|
|
1004
1048
|
}
|
|
1005
1049
|
/**
|
|
@@ -1057,6 +1101,7 @@ export class AbstractBone {
|
|
|
1057
1101
|
* Internal upsert implementation
|
|
1058
1102
|
*/
|
|
1059
1103
|
_upsert(opts) {
|
|
1104
|
+
assertModelClassFields(this);
|
|
1060
1105
|
const data = {};
|
|
1061
1106
|
const Model = this.constructor;
|
|
1062
1107
|
const { attributes, primaryKey } = Model;
|
|
@@ -1091,6 +1136,7 @@ export class AbstractBone {
|
|
|
1091
1136
|
*/
|
|
1092
1137
|
_update(values, options) {
|
|
1093
1138
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1139
|
+
assertModelClassFields(this);
|
|
1094
1140
|
const Model = this.constructor;
|
|
1095
1141
|
const { attributes, primaryKey, shardingKey } = Model;
|
|
1096
1142
|
const changes = {};
|
|
@@ -1195,6 +1241,7 @@ export class AbstractBone {
|
|
|
1195
1241
|
* Internal create implementation
|
|
1196
1242
|
*/
|
|
1197
1243
|
_create(opts = {}) {
|
|
1244
|
+
assertModelClassFields(this);
|
|
1198
1245
|
const Model = this.constructor;
|
|
1199
1246
|
const { primaryKey, attributes } = Model;
|
|
1200
1247
|
const data = {};
|
|
@@ -1250,13 +1297,15 @@ export class AbstractBone {
|
|
|
1250
1297
|
});
|
|
1251
1298
|
}
|
|
1252
1299
|
/**
|
|
1253
|
-
* Protected clone
|
|
1300
|
+
* Protected clone — replaces internal state with merged data from target.
|
|
1301
|
+
* Note: `this` here is the proxy (all method calls go through proxy). The proxy
|
|
1302
|
+
* has no `set` trap, so Symbol-key assignments delegate to the underlying target.
|
|
1254
1303
|
*/
|
|
1255
1304
|
_clone(target) {
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1305
|
+
this[RAW] = Object.assign({}, this.getRaw(), target.getRaw());
|
|
1306
|
+
this[RAW_SAVED] = Object.assign({}, this.getRawSaved(), target.getRawSaved());
|
|
1307
|
+
this[RAW_PREVIOUS] = Object.assign({}, this.getRawPrevious(), target.getRawPrevious());
|
|
1308
|
+
this[RAW_UNSET] = target._getRawUnset();
|
|
1260
1309
|
}
|
|
1261
1310
|
/**
|
|
1262
1311
|
* Validate current changes
|
|
@@ -1314,13 +1363,13 @@ export class AbstractBone {
|
|
|
1314
1363
|
* post.toJSON() // => { id: 1, ... }
|
|
1315
1364
|
* @return {Object}
|
|
1316
1365
|
*/
|
|
1317
|
-
[
|
|
1366
|
+
[util.inspect.custom]() {
|
|
1318
1367
|
return this.constructor.name + ' ' + util.inspect(this.toJSON());
|
|
1319
1368
|
}
|
|
1320
1369
|
toJSON() {
|
|
1321
1370
|
const obj = {};
|
|
1322
1371
|
for (const key in this) {
|
|
1323
|
-
if (
|
|
1372
|
+
if ($rawUnset(this).has(key))
|
|
1324
1373
|
continue;
|
|
1325
1374
|
if (typeof this[key] !== 'function') {
|
|
1326
1375
|
const value = this[key];
|
|
@@ -1342,7 +1391,7 @@ export class AbstractBone {
|
|
|
1342
1391
|
toObject() {
|
|
1343
1392
|
const obj = {};
|
|
1344
1393
|
for (const key in this) {
|
|
1345
|
-
if (
|
|
1394
|
+
if ($rawUnset(this).has(key))
|
|
1346
1395
|
continue;
|
|
1347
1396
|
if (typeof this[key] !== 'function') {
|
|
1348
1397
|
const value = this[key];
|
|
@@ -1357,6 +1406,48 @@ AbstractBone.DataTypes = DataTypes.invokable;
|
|
|
1357
1406
|
* The primary key of the model, defaults to `id`.
|
|
1358
1407
|
*/
|
|
1359
1408
|
AbstractBone.primaryKey = 'id';
|
|
1409
|
+
export function markModelClassReady(Model) {
|
|
1410
|
+
if (isModelClassReady(Model))
|
|
1411
|
+
return Model;
|
|
1412
|
+
Object.defineProperty(Model, MODEL_READY, {
|
|
1413
|
+
value: true,
|
|
1414
|
+
configurable: false,
|
|
1415
|
+
enumerable: false,
|
|
1416
|
+
writable: false,
|
|
1417
|
+
});
|
|
1418
|
+
return Model;
|
|
1419
|
+
}
|
|
1420
|
+
export function isModelClassReady(Model) {
|
|
1421
|
+
return Object.prototype.hasOwnProperty.call(Model, MODEL_READY);
|
|
1422
|
+
}
|
|
1423
|
+
export function markModelClassFieldsChecked(Model) {
|
|
1424
|
+
if (hasCheckedModelClassFields(Model))
|
|
1425
|
+
return Model;
|
|
1426
|
+
Object.defineProperty(Model, MODEL_CLASS_FIELDS_CHECKED, {
|
|
1427
|
+
value: true,
|
|
1428
|
+
configurable: false,
|
|
1429
|
+
enumerable: false,
|
|
1430
|
+
writable: false,
|
|
1431
|
+
});
|
|
1432
|
+
return Model;
|
|
1433
|
+
}
|
|
1434
|
+
export function hasCheckedModelClassFields(Model) {
|
|
1435
|
+
return Object.prototype.hasOwnProperty.call(Model, MODEL_CLASS_FIELDS_CHECKED);
|
|
1436
|
+
}
|
|
1437
|
+
export function assertModelClassFields(instance) {
|
|
1438
|
+
const Model = instance.constructor;
|
|
1439
|
+
if (hasCheckedModelClassFields(Model))
|
|
1440
|
+
return;
|
|
1441
|
+
const { attributes } = Model;
|
|
1442
|
+
if (!attributes)
|
|
1443
|
+
return;
|
|
1444
|
+
for (const name of Object.keys(attributes)) {
|
|
1445
|
+
if (Object.prototype.hasOwnProperty.call(instance, name)) {
|
|
1446
|
+
throw new LeoricClassFieldError(Model.name, name);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
markModelClassFieldsChecked(Model);
|
|
1450
|
+
}
|
|
1360
1451
|
function looseReadonly(props) {
|
|
1361
1452
|
return Object.keys(props).reduce((result, name) => {
|
|
1362
1453
|
result[name] = { value: props[name], writable: false, enumerable: false, configurable: true };
|
|
@@ -1429,6 +1520,11 @@ function valuesValidate(values, attributes, ctx) {
|
|
|
1429
1520
|
function cloneValue(value) {
|
|
1430
1521
|
if (value instanceof Date && isNaN(value))
|
|
1431
1522
|
return value;
|
|
1523
|
+
// structuredClone converts Buffer to Uint8Array, breaking isDeepStrictEqual comparisons.
|
|
1524
|
+
// Preserve Buffer type by copying manually.
|
|
1525
|
+
if (Buffer.isBuffer(value))
|
|
1526
|
+
return Buffer.from(value);
|
|
1527
|
+
// eslint-disable-next-line no-undef
|
|
1432
1528
|
return typeof structuredClone === 'function' ? structuredClone(value) : JSON.parse(JSON.stringify(value));
|
|
1433
1529
|
}
|
|
1434
1530
|
Reflect.defineMetadata(IS_LEORIC_BONE, true, AbstractBone);
|