• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Haixing-Hu / js-common-decorator / 214d62ca-32aa-44ba-af6c-f0078b9541b4

29 Oct 2024 06:30PM UTC coverage: 82.353% (-0.7%) from 83.06%
214d62ca-32aa-44ba-af6c-f0078b9541b4

push

circleci

Haixing-Hu
build: upgrade dependencies and rebuild the library

473 of 581 branches covered (81.41%)

Branch coverage included in aggregate %.

633 of 762 relevant lines covered (83.07%)

174.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.61
/src/model.js
1
////////////////////////////////////////////////////////////////////////////////
2
//
3
//    Copyright (c) 2022 - 2023.
4
//    Haixing Hu, Qubit Co. Ltd.
5
//
6
//    All rights reserved.
7
//
8
////////////////////////////////////////////////////////////////////////////////
9
import classMetadataCache from './impl/class-metadata-cache';
10
import { KEY_CLASS_CATEGORY, KEY_CLASS_NEXT_ID } from './impl/metadata-keys';
11
import assignImpl from './impl/model/assign-impl';
12
import isEmptyImpl from './impl/model/is-empty-impl';
13
import equalsImpl from './impl/model/equals-impl';
14
import generateIdImpl from './impl/model/generate-id-impl';
15
import clearImpl from './impl/model/clear-impl';
16
import cloneImpl from './impl/model/clone-impl';
17
import normalizeFieldImpl from './impl/model/normalize-field-impl';
18
import normalizeImpl from './impl/model/normalize-impl';
19
import validateFieldImpl from './impl/model/validate-field-impl';
20
import validateImpl from './impl/model/validate-impl';
21
import createImpl from './impl/model/create-impl';
22
import createArrayImpl from './impl/model/create-array-impl';
23
import createPageImpl from './impl/model/create-page-impl';
24
import isNullishOrEmptyImpl from './impl/model/is-nullish-or-empty-impl';
25
import parseJsonStringImpl from './impl/model/parse-json-string-impl';
26
import toJsonImpl from './impl/model/to-json-impl';
27
import toJsonStringImpl from './impl/model/to-json-string-impl';
28
import setClassMetadata from './impl/utils/set-class-metadata';
29
import hasOwnClassField from './impl/utils/has-own-class-field';
30
import hasOwnPrototypeFunction from './impl/utils/has-own-prototype-function';
31
import hasPrototypeFunction from './impl/utils/has-prototype-function';
32

33
/**
34
 * This decorator is used to add common methods to a domain model class.
35
 *
36
 * It must decorate a class.
37
 *
38
 * It adds the following methods to the decorated class:
39
 *
40
 * - Instance method `assign(obj, options)`: Copies the properties of the object
41
 *   `obj` to this object, only copying properties defined in the class of this
42
 *   object. If a property in the `obj` object is `undefined` or `null`, it sets
43
 *   the property of this object to the default value. The function returns this
44
 *   object itself. Note that `obj` can have a different prototype than this object.
45
 *   The `options` parameter is the additional options for the assignment.
46
 *   Available options will be explained below. If the `options` parameter is
47
 *   `undefined` or `null`, the default options will be used. The default
48
 *   options can be retrieved by calling `DefaultOptions.get('assign')`.
49
 * - Instance method `clear()`: Sets all the properties of this object to their
50
 *   default values.
51
 * - Instance method `clone()`: Returns a deep clone of this object.
52
 * - Instance method `isEmpty()`: Checks if this object is empty, meaning that all
53
 *   of its properties have default values.
54
 * - Instance method `equals(obj)`: Determines if this object is deeply equal
55
 *   to `obj`.
56
 * - Instance method `normalize(fields)`: Normalizes specified fields of this
57
 *   object. The `fields` parameter specifies the names of fields to be normalized.
58
 *   If `fields` is `undefined`, `null`, or the string `"*"`, it normalizes all
59
 *   the fields that can be normalized for this object. If `fields` is an array of
60
 *   strings, it normalizes all the fields specified in the array. Note that a
61
 *   field is normalizable if and only if it is decorated with the
62
 *   `@{@link Normalizable}` decorator.
63
 * - Instance method `normalizeField(field)`: Normalizes the specified field of
64
 *   this object. The `field` parameter specifies the name of the field to be
65
 *   normalized. If the specified exists and is normalizable, the function
66
 *   normalizes it and returns `true`; otherwise, the function does nothing and
67
 *   returns `false`.
68
 * - Instance method `validate(fields, options)`: Validates the specified fields of
69
 *   this object. The `fields` parameter is the names of the fields to be validated.
70
 *   If `fields` is `undefined`, `null`, or the string `"*"`, it validates all the
71
 *   fields that can be validated for this object. If `fields` is an array of
72
 *   strings, it validates all the fields specified in the array. Note that a field
73
 *   must be specified as validatable using the `@{@link Validatable}` decorator.
74
 *   The `options` parameter is an object comprising additional parameters, and its
75
 *   property values are passed as the second argument to the validation function.
76
 *   Refer to the documentation of `@{@link Validatable}` for more details.
77
 * - Instance method `generateId()`: If the decorated class defines a property
78
 *   named `id`, this decorator automatically adds a `generateId()` instance method.
79
 *   Each call to this method generates a globally unique ID (represented as an
80
 *   integer) for the class of this object, sets it to this object and returns
81
 *   the generated ID. Note that if a parent class `A` defines the `id` field,
82
 *   and a subclass `B` inherits  the `id` field but does not define its own
83
 *   `id` field, the `generateId()` method is only added to class `A`, not to
84
 *   class `B`.
85
 * - Static class method `create(obj, options)`: Creates a new instance object
86
 *   based on the `obj` object. It copies the property values from the corresponding
87
 *   properties of `obj`, maintaining the same prototype and class definition. This
88
 *   method is used to transform a plain JSON object into the specified domain
89
 *   object. The `options` parameter is the additional options for the creation,
90
 *   which is the same as the `options` parameter of the `create()` method.
91
 * - Static class method `createArray(array, options)`: Creates a new instance
92
 *   array based on an object array `array`, where each element's property values
93
 *   are copied from the corresponding elements in `array`, maintaining the same
94
 *   prototype and class definition. This method is used to transform an array of
95
 *   plain JSON objects into an array of specified domain objects. The `options`
96
 *   parameter is the additional options for the creation, which is the same
97
 *   as the `options` parameter of the `create()` method.
98
 * - Static class method `createPage(page, options)`: Creates a new page object
99
 *   based on a `page` pagination object. Typically, `page` is a list of domain
100
 *   objects obtained from a server using the GET method, and the object should
101
 *   conform to the `{@link Page}` class definition. This static class method
102
 *   returns a new `{@link Page}` object, with the `content` property being the
103
 *   result of `createArray(page.content, options)`, and the other properties
104
 *   matching those of the `page` object. If the input is not a valid
105
 *   `{@link Page}` object, it returns `null`. The `options` parameter is the
106
 *   additional options for the creation, which is the same as the `options`
107
 *   parameter of the `create()` method.
108
 * - Static class method `isNullishOrEmpty(obj)`: Determines if the given instance
109
 *   is `undefined`, `null`, or an empty object constructed with default values.
110
 *
111
 * **NOTE:** If the decorated class already implements any of the above methods,
112
 * this decorator will not override the methods already implemented by the
113
 * decorated class.
114
 *
115
 * ##### Usage example:
116
 *
117
 * ```js
118
 * @Model
119
 * class Credential {
120
 *
121
 *   @Normalizable
122
 *   @Validator(validateCredentialTypeField)
123
 *   @Type(CredentialType)
124
 *   @Label('证件类型')
125
 *   type = CredentialType.IDENTITY_CARD;
126
 *
127
 *   @Normalizable(trimUppercaseString)
128
 *   @Validator(validateCredentialNumberField)
129
 *   @Label('证件号码')
130
 *   number = '';
131
 *
132
 *   constructor(type = CredentialType.DEFAULT.value, number = '') {
133
 *     this.type = type;
134
 *     this.number = number;
135
 *   }
136
 *
137
 *   isIdentityCard() {
138
 *     return (this.type === 'IDENTITY_CARD');
139
 *   }
140
 * }
141
 *
142
 * @Model
143
 * class Person {
144
 *
145
 *   @Normalizable(trimString)
146
 *   @Label('ID')
147
 *   id = null;
148
 *
149
 *   @Normalizable(trimUppercaseString)
150
 *   @Validatable(validatePersonNameField)
151
 *   @Label('姓名')
152
 *   name = '';
153
 *
154
 *   @Normalizable
155
 *   @DefaultValidator
156
 *   @Type(Credential)
157
 *   @Label('证件')
158
 *   credential = null;
159
 *
160
 *   @Normalizable
161
 *   @Validatable(validatePersonGenderField)
162
 *   @Type(Gender)
163
 *   @Label('性别')
164
 *   gender = '';
165
 *
166
 *   @Normalizable(trimString)
167
 *   @Validatable(validatePersonBirthdayField)
168
 *   @Label('出生日期')
169
 *   birthday = '';
170
 *
171
 *   @Normalizable(trimUppercaseString)
172
 *   @Validatable(validateMobileField)
173
 *   @Label('手机号码')
174
 *   mobile = '';
175
 *
176
 *   @Normalizable(trimString)
177
 *   @Validatable(validateEmailField)
178
 *   @Label('电子邮件地址')
179
 *   @Nullable
180
 *   email = '';
181
 *
182
 *   equals(other) {
183
 *     if (!(other instanceof PersonWithEquals)) {
184
 *       return false;
185
 *     }
186
 *    if ((this.credential === null) || (other.credential === null)) {
187
 *       // If one of the two people does not have an ID information, it is
188
 *       // impossible to compare whether they are the same person thus they
189
 *       // will be considered different.
190
 *       return false;
191
 *     }
192
 *     // Two persons are logically equals if and only if they have the same
193
 *     // credential.
194
 *     return (this.credential.type === other.credential.type)
195
 *         && (this.credential.number === other.credential.number);
196
 *   }
197
 * }
198
 * ```
199
 *
200
 * After applying the `@Model` decorator, the following methods will be
201
 * automatically added:
202
 *
203
 * - `Credential.prototype.assign(obj, options = undefined)`
204
 * - `Credential.prototype.clear()`
205
 * - `Credential.prototype.clone()`
206
 * - `Credential.prototype.isEmpty()`
207
 * - `Credential.prototype.equals(obj)`
208
 * - `Credential.prototype.normalize(fields)`
209
 * - `Credential.prototype.validate(fields, options)`
210
 * - `Credential.prototype.toJSON(key, options = undefined)`
211
 * - `Credential.prototype.toJsonString(options = undefined)`
212
 * - `Credential.create(obj, options = undefined)`
213
 * - `Credential.createArray(array, options = undefined)`
214
 * - `Credential.createPage(page, options = undefined)`
215
 * - `Credential.isNullishOrEmpty(obj)`
216
 * - `Credential.parseJsonString(json, options = undefined)`
217
 * - `Person.prototype.assign(obj, normalized)`
218
 * - `Person.prototype.clear()`
219
 * - `Person.prototype.clone()`
220
 * - `Person.prototype.isEmpty()`
221
 * - `Person.prototype.normalize(fields)`
222
 * - `Person.prototype.validate(fields, options)`
223
 * - `Person.prototype.generateId()`
224
 * - `Person.prototype.toJSON(key, options = undefined)`
225
 * - `Person.prototype.toJsonString(options = undefined)`
226
 * - `Person.create(obj, options = undefined)`
227
 * - `Person.createArray(array, options = undefined)`
228
 * - `Person.createPage(page, options = undefined)`
229
 * - `Person.isNullishOrEmpty(obj)`
230
 * - `Person.parseJsonString(json, options = undefined)`
231
 *
232
 * **NOTE:**
233
 *
234
 * - Because the `Credential` class does not have an `id` attribute, the `@Model`
235
 *   decorator does not add a `generateId()` instance method to it.
236
 * - Because `Person` already implements the `Person.prototype.equals()` method,
237
 *   the `@Model` decorator will **not** override its own implementation of
238
 *   the `Person.prototype.equals()` method.
239
 *
240
 * @param {function} Class
241
 *     The constructor of the class being decorated.
242
 * @param {object} context
243
 *     The context object containing information about the class being decorated.
244
 * @author Haixing Hu
245
 * @see Type
246
 * @see ElementType
247
 * @see Nullable
248
 * @see Label
249
 * @see NameField
250
 * @see Normalizable
251
 * @see Validatable
252
 * @see ValidationResult
253
 * @see DefaultOptions.get('assign')
254
 * @see DefaultOptions.get('toJSON')
255
 */
256
function Model(Class, context) {
257
  if (context === null || typeof context !== 'object') {
100!
258
    throw new TypeError('The context must be an object.');
×
259
  }
260
  if (typeof Class !== 'function' || context.kind !== 'class') {
100✔
261
    throw new TypeError('The `@Model` can only decorate a class.');
1✔
262
  }
263
  // put the context.metadata to the cache
264
  classMetadataCache.set(Class, context.metadata);
99✔
265
  // The category of the class modified by `@Model` is set to 'model'
266
  setClassMetadata(Class, KEY_CLASS_CATEGORY, 'model');
99✔
267
  // Add the instance method `assign()`
268
  if (!hasOwnPrototypeFunction(Class, 'assign')) {
99✔
269
    /**
270
     * Copies the properties from a specified data object to this object, only
271
     * copying properties defined in the class of this object.
272
     *
273
     * If a property in the data object is `undefined` or `null`, the function
274
     * sets the property of this object to the default value.
275
     *
276
     * Note that the data object may have a different prototype than this object.
277
     *
278
     * @param {object} obj
279
     *     the data object, which may have a different prototype than this object.
280
     * @param {null|undefined|object} options
281
     *     the additional options for the assignment. If this argument is
282
     *     `undefined` or `null`, the default options will be used. The default
283
     *     options can be retrieved by calling `DefaultOptions.get('assign')`.
284
     *     Available options are:
285
     *     - `normalize: boolean`, indicates whether to normalize this object
286
     *       after the assignment. The default value is `true`.
287
     *     - `convertNaming: boolean`, indicates whether to convert the naming
288
     *       style of the target object. The default value is `false`.
289
     *     - `sourceNamingStyle: string`, the naming style of the source object,
290
     *       i.e., the first argument of the `assign()` method. The default value
291
     *       of this argument is `'LOWER_UNDERSCORE'`.
292
     *     - `targetNamingStyle: string`, the naming style of the target object,
293
     *       i.e., the object calling the `assign()` method. The default value
294
     *       of this argument is `'LOWER_CAMEL'`.
295
     *     - `targetTypes: object`, the additional information about types of
296
     *       fields of classes. The keys of this object are the path of the fields
297
     *       or sub-fields of the target object, the values are the type of the
298
     *       fields, represented as the constructor function of the type.
299
     *       The default value is `{}`.
300
     *     - `targetElementTypes: object`, the additional information about types of
301
     *       elements of fields of classes. The keys of this object are the path of
302
     *       the fields or sub-fields of the target object, the values are the type
303
     *       of the elements, represented as the constructor function of the type.
304
     *       The default value is `{}`.
305
     * @returns {object}
306
     *     the reference to this object.
307
     * @see DefaultOptions.get('assign')
308
     */
309
    Class.prototype.assign = function assign(obj, options = undefined) {
96✔
310
      return assignImpl(Class, this, obj, options);
103✔
311
    };
312
  }
313
  // Add the instance method `clear()`
314
  if (!hasOwnPrototypeFunction(Class, 'clear')) {
99!
315
    /**
316
     * Sets all the properties of this object to their default values.
317
     *
318
     * @returns {object}
319
     *     the reference to this object.
320
     */
321
    Class.prototype.clear = function clear() {
99✔
322
      return clearImpl(Class, this);
2✔
323
    };
324
  }
325
  // Add the instance method `clone()`
326
  if (!hasOwnPrototypeFunction(Class, 'clone')) {
99!
327
    /**
328
     * Clones this object.
329
     *
330
     * @returns {object}
331
     *     the cloned copy of this object.
332
     */
333
    Class.prototype.clone = function clone() {
99✔
334
      return cloneImpl(Class, this);
3✔
335
    };
336
  }
337
  // Add the instance method `isEmpty()`
338
  if (!hasOwnPrototypeFunction(Class, 'isEmpty')) {
99!
339
    /**
340
     * Checks whether this object is empty, i.e., whether all of its properties
341
     * have default values.
342
     *
343
     * @returns {boolean}
344
     *     `true` if this object is empty; `false` otherwise.
345
     */
346
    Class.prototype.isEmpty = function isEmpty() {
99✔
347
      return isEmptyImpl(Class, this);
24✔
348
    };
349
  }
350
  // Add the instance method `equals()`
351
  if (!hasOwnPrototypeFunction(Class, 'equals')) {
99✔
352
    /**
353
     * Determines whether this object is deeply equal to the other object.
354
     *
355
     * @param {object} obj
356
     *     the other object.
357
     * @returns {boolean}
358
     *     `true` if this object is deeply equal to the other object; `false`
359
     *     otherwise.
360
     */
361
    Class.prototype.equals = function equals(obj) {
98✔
362
      return equalsImpl(this, obj);
6✔
363
    };
364
  }
365
  // Add the instance method `normalize()`
366
  if (!hasOwnPrototypeFunction(Class, 'normalize')) {
99✔
367
    /**
368
     * Normalizes all normalizable fields or specified normalizable fields of
369
     * this object.
370
     *
371
     * A field is normalizable if and only if it is decorated with the
372
     * `@{@link Normalizable}` decorator.
373
     *
374
     * @param {undefined|string|array} fields
375
     *     the names of fields to be normalized. If this argument is not specified,
376
     *     or `undefined`, or `null`, or a string `'*'`, this function normalizes
377
     *     all normalizable fields of this object; If this argument is an array of
378
     *     strings, this function normalizes all normalizable fields specified
379
     *     in the array. If this argument is a string other than `'*'`, this
380
     *     function normalizes the field with the name equals to this argument;
381
     *     if the specified field does not exist nor non-normalizable, this
382
     *     function does nothing.
383
     * @returns {object}
384
     *     the reference to this object.
385
     */
386
    Class.prototype.normalize = function normalize(fields = '*') {
78✔
387
      return normalizeImpl(Class, this, fields);
224✔
388
    };
389
  }
390
  // Add the instance method `normalizeField()`
391
  if (!hasOwnPrototypeFunction(Class, 'normalizeField')) {
99✔
392
    /**
393
     * Normalizes the specified normalizable fields of this object.
394
     *
395
     * A field is normalizable if and only if it is decorated with the
396
     * `@{@link Normalizable}` decorator.
397
     *
398
     * @param {string} field
399
     *     the names of fields to be normalized. If the specified field does not
400
     *     exist nor non-normalizable, this function does nothing and returns
401
     *     `false`.
402
     * @returns {boolean}
403
     *     `true` if the specified field exists and is normalizable; `false`
404
     *     otherwise.
405
     */
406
    Class.prototype.normalizeField = function normalizeField(field) {
96✔
407
      return normalizeFieldImpl(Class, this, field);
909✔
408
    };
409
  }
410
  // Add the instance method `validate()`
411
  if (!hasOwnPrototypeFunction(Class, 'validate')) {
99!
412
    /**
413
     * Validates this object.
414
     *
415
     * @param {undefined|string|array} fields
416
     *     the names of fields to be validated. If this argument is not specified,
417
     *     or `undefined`, or `null`, or a string `'*'`, this function validates
418
     *     all validatable fields of this object; If this argument is an array of
419
     *     strings, this function validates all validatable fields specified
420
     *     in the array. If this argument is a string other than `'*'`, this
421
     *     function validates the field with the name equals to this argument;
422
     *     if the specified field does not exist nor non-validatable, this
423
     *     function does nothing.
424
     * @param {object} context
425
     *     The validation context. If this argument is not specified, an empty
426
     *     context is used.
427
     * @returns {ValidationResult}
428
     *     The result of validation.
429
     */
430
    Class.prototype.validate = function validate(fields = '*', context = {}) {
99✔
431
      return validateImpl(Class, this, fields, context);
94✔
432
    };
433
  }
434
  // Add the instance method `validateField()`
435
  if (!hasOwnPrototypeFunction(Class, 'validateField')) {
99!
436
    /**
437
     * Validates the specified validatable fields of this object.
438
     *
439
     * A field is validatable if and only if it is decorated with the
440
     * `@{@link Validatable}` decorator.
441
     *
442
     * @param {string} field
443
     *     the names of fields to be validated. If the specified field does not
444
     *     exist nor non-validatable, this function does nothing and returns
445
     *     `null`.
446
     * @param {object} context
447
     *     The validation context. If this argument is not specified, an empty
448
     *     context is used.
449
     * @returns {ValidationResult|null}
450
     *     The validation result if the specified field exists; `null` otherwise.
451
     *     If the specified field exist but is non-validatable, returns the success
452
     *     validation result.
453
     */
454
    Class.prototype.validateField = function validateField(field, context = {}) {
99✔
455
      return validateFieldImpl(Class, this, field, context);
349✔
456
    };
457
  }
458
  // Add the instance method `generateId()` to the class containing the `id` field
459
  if (hasOwnClassField(Class, 'id') && !hasPrototypeFunction(Class, 'generateId')) {
99✔
460
    // If its own instance has an `id` field, and there is no `generateId()`
461
    // method on itself or its parent class prototype
462
    setClassMetadata(Class, KEY_CLASS_NEXT_ID, 0);
23✔
463
    /**
464
     * Generates the next unique identifier for instances of this class and set
465
     * it to this object.
466
     *
467
     * Each call to this method generates a globally unique ID (represented as
468
     * an integer) for the class of this object, sets it to this object and
469
     * returns the generated ID.
470
     *
471
     * Note hat if a parent class `A` defines the `id` field, and a subclass `B`
472
     * inherits the `id` field but does not define its own `id` field, the
473
     * `generateId()` method is only added to class `A`, not to class `B`.
474
     *
475
     * @returns {number}
476
     *     the generated unique ID.
477
     */
478
    Class.prototype.generateId = function generateId() {
23✔
479
      return generateIdImpl(Class, this);
4✔
480
    };
481
  }
482
  // Add the instance method `toJSON()`
483
  if (!hasOwnPrototypeFunction(Class, 'toJSON')) {
99!
484
    /**
485
     * Gets the object to be serialized by `JSON.stringify()`.
486
     *
487
     * If the value has a `toJSON()` method, it's responsible to define what
488
     * data will be serialized. Instead of the object being serialized, the value
489
     * returned by the `toJSON()` method when called will be serialized.
490
     *
491
     * **NOTE:** this function returns an object to be serialized by
492
     * `JSON.stringify()`, instead of a JSON string. Use `JSON.stringify()`
493
     * or `this.toJsonString()` methods to serialize this object into a JSON
494
     * string.
495
     *
496
     * @param {string} key
497
     *     `JSON.stringify()` calls `toJSON()` with one parameter, the `key`,
498
     *     which takes the following values:
499
     *     - if this object is a property value, this argument is the property
500
     *       name;
501
     *     - if this object is in an array, this argument is the index in the
502
     *       array, as a string;
503
     *     - if `JSON.stringify()` was directly called on this object, this
504
     *       argument is an empty string.
505
     * @param {null|undefined|object} options
506
     *     the additional options for the serialization. If this argument is
507
     *     `undefined` or `null`, the default options will be used. The default
508
     *     options can be retrieved by calling `DefaultOptions.get('toJSON')`.
509
     *     Available options are:
510
     *     - `normalize: boolean`, indicates whether to normalize this object
511
     *       before serializing. The default value is `true`.
512
     *     - `removeEmptyFields: boolean`, indicates whether to ignore the empty
513
     *       fields of the object. If it is `true`, the empty fields of the object
514
     *       will be removed before serialization. The default value is `false`.
515
     *     - `convertNaming: boolean`, indicates whether to convert the naming
516
     *       of properties of the object represented by the result JSON string.
517
     *       The default value is `false`.
518
     *     - `sourceNamingStyle: string`, the naming style of the source object,
519
     *       i.e., the object calling the `toJSON()` method. The default value
520
     *       of this argument is `'LOWER_CAMEL'`.
521
     *     - `targetNamingStyle: string`, the naming style of the target object,
522
     *       i.e., the object represented by the result JSON string of the
523
     *       `toJSON()` method. The default value of this argument is
524
     *       `'LOWER_UNDERSCORE'`.
525
     *     - `space: string | number`, a string or number that's used to insert
526
     *       white space (including indentation, line break characters, etc.) into
527
     *       the output JSON string for readability purposes. If this is a number,
528
     *       it indicates the number of space characters to be used as indentation,
529
     *       clamped to 10 (that is, any number greater than 10 is treated as if
530
     *       it were 10). Values less than 1 indicate that no space should be used.
531
     *       If this is a string, the string (or the first 10 characters of the
532
     *       string, if it's longer than that) is inserted before every nested
533
     *       object or array. If this is anything other than a string or number
534
     *       (can be either a primitive or a wrapper object) — for example, is
535
     *       `null` or not provided — no white space is used. The default value
536
     *       of this option is `null`.
537
     * @returns {object}
538
     *     the object to be serialized by `JSON.stringify()`, which may be a
539
     *     modify copy of this object.
540
     * @see toJsonString()
541
     * @see DefaultOptions.get('toJSON')
542
     */
543
    Class.prototype.toJSON = function toJSON(key, options = undefined) {
99✔
544
      return toJsonImpl(this, key, {
31✔
545
        ...options,
546
        skipRootToJSON: true,
547
      });
548
    };
549
  }
550
  // Add the instance method `toJSON()`
551
  if (!hasOwnPrototypeFunction(Class, 'toJsonString')) {
99!
552
    /**
553
     * Serializes this object into a JSON string.
554
     *
555
     * **NOTE:** This method supports native `bigint` value. For example, the
556
     * `bigint` value `9223372036854775807n` will be stringify as
557
     * `9223372036854775807`.
558
     *
559
     * @param {null|undefined|object} options
560
     *     the additional options for the serialization. If this argument is
561
     *     `undefined` or `null`, the default options will be used. The default
562
     *     options can be retrieved by calling `DefaultOptions.get('toJSON')`.
563
     *     Available options are:
564
     *     - `normalize: boolean`, indicates whether to normalize this object
565
     *       before serializing. The default value is `true`.
566
     *     - `removeEmptyFields: boolean`, indicates whether to ignore the empty
567
     *       fields of the object. If it is `true`, the empty fields of the object
568
     *       will be removed before serialization. The default value is `false`.
569
     *     - `convertNaming: boolean`, indicates whether to convert the naming
570
     *       of properties of the object represented by the result JSON string.
571
     *       The default value is `false`.
572
     *     - `sourceNamingStyle: string`, the naming style of the source object,
573
     *       i.e., the object calling the `toJSON()` method. The default value
574
     *       of this argument is `'LOWER_CAMEL'`.
575
     *     - `targetNamingStyle: string`, the naming style of the target object,
576
     *       i.e., the object represented by the result JSON string of the
577
     *       `toJSON()` method. The default value of this argument is
578
     *       `'LOWER_UNDERSCORE'`.
579
     *     - `space: string | number`, a string or number that's used to insert
580
     *       white space (including indentation, line break characters, etc.) into
581
     *       the output JSON string for readability purposes. If this is a number,
582
     *       it indicates the number of space characters to be used as indentation,
583
     *       clamped to 10 (that is, any number greater than 10 is treated as if
584
     *       it were 10). Values less than 1 indicate that no space should be used.
585
     *       If this is a string, the string (or the first 10 characters of the
586
     *       string, if it's longer than that) is inserted before every nested
587
     *       object or array. If this is anything other than a string or number
588
     *       (can be either a primitive or a wrapper object) — for example, is
589
     *       `null` or not provided — no white space is used. The default value
590
     *       of this option is `null`.
591
     * @returns {string}
592
     *     the JSON string serialized from this object, as `JSON.stringify()`
593
     *     does, except that this function provides additional stringification
594
     *     options.
595
     * @see toJSON()
596
     * @see DefaultOptions.get('toJSON')
597
     */
598
    Class.prototype.toJsonString = function toJsonString(options = undefined) {
99✔
599
      return toJsonStringImpl(Class, this, options);
4✔
600
    };
601
  }
602
  // Add the class method `create()`
603
  if (!Object.prototype.hasOwnProperty.call(Class, 'create')) {
99!
604
    /**
605
     * Creates a new instance of this class based on the specified data object.
606
     *
607
     * It copies the property values from the corresponding properties of the
608
     * specified data object maintaining the same prototype and class definition.
609
     *
610
     * If a property in the data object is `undefined` or `null`, the function
611
     * sets the property of the created instance to the default value.
612
     *
613
     * This method is usually used to transform a plain JSON object into the
614
     * specified domain object.
615
     *
616
     * @param {object} obj
617
     *     the specified data object.
618
     * @param {null|undefined|object} options
619
     *     the additional options for the creation. If this argument is
620
     *     `undefined` or `null`, the default options will be used. The default
621
     *     options can be retrieved by calling `DefaultOptions.get('assign')`.
622
     *     Available options are:
623
     *     - `normalize: boolean`, indicates whether to normalize this object
624
     *       after the assignment. The default value is `true`.
625
     *     - `convertNaming: boolean`, indicates whether to convert the naming
626
     *       style of the target object. The default value is `false`.
627
     *     - `sourceNamingStyle: string`, the naming style of the source object,
628
     *       i.e., the first argument of the `create()` method. The default
629
     *       value of this argument is `'LOWER_UNDERSCORE'`.
630
     *     - `targetNamingStyle: string`, the naming style of the target object,
631
     *       i.e., the object returned by the `create()` method. The default
632
     *       value of this argument is `'LOWER_CAMEL'`.
633
     *     - `targetTypes: object`, the additional information about types of
634
     *       fields of classes. The keys of this object are the path of the fields
635
     *       or sub-fields of the target object, the values are the type of the
636
     *       fields, represented as the constructor function of the type.
637
     *       The default value is `{}`.
638
     *     - `targetElementTypes: object`, the additional information about types of
639
     *       elements of fields of classes. The keys of this object are the path of
640
     *       the fields or sub-fields of the target object, the values are the type
641
     *       of the elements, represented as the constructor function of the type.
642
     *       The default value is `{}`.
643
     * @returns {Class|null}
644
     *     the new instance of this class created from the specified data object,
645
     *     or `null` if the specified object is `null` or `undefined`.
646
     * @see DefaultOptions.get('assign')
647
     */
648
    Class.create = function create(obj, options = undefined) {
99✔
649
      return createImpl(Class, obj, options);
21✔
650
    };
651
  }
652
  // Add the class method `createArray()`
653
  if (!Object.prototype.hasOwnProperty.call(Class, 'createArray')) {
99!
654
    /**
655
     * Creates a new array of instances of this class based on an array of data
656
     * objects.
657
     *
658
     * The property values of each element in the created instances array are
659
     * copied from the corresponding elements in the data object array,
660
     * maintaining the same prototype and class definition.
661
     *
662
     * This method is usually used to transform an array of plain JSON objects
663
     * into an array of specified domain objects.
664
     *
665
     * @param {Array<object>}  array
666
     *     the specified array of data objects.
667
     * @param {null|undefined|object} options
668
     *     the additional options for the creation. If this argument is
669
     *     `undefined` or `null`, the default options will be used. The default
670
     *     options can be retrieved by calling `DefaultOptions.get('assign')`.
671
     *     Available options are:
672
     *     - `normalize: boolean`, indicates whether to normalize this object
673
     *       after the assignment. The default value is `true`.
674
     *     - `convertNaming: boolean`, indicates whether to convert the naming
675
     *       style of the target object. The default value is `false`.
676
     *     - `sourceNamingStyle: string`, the naming style of the source object,
677
     *       i.e., the elements in the first argument of the `createArray()`
678
     *       method. The default value of this argument is `'LOWER_UNDERSCORE'`.
679
     *     - `targetNamingStyle: string`, the naming style of the target object,
680
     *       i.e., the elements in the array returned by the `createArray()`
681
     *       method. The default value of this argument is `'LOWER_CAMEL'`.
682
     *     - `targetTypes: object`, the additional information about types of
683
     *       fields of classes. The keys of this object are the path of the fields
684
     *       or sub-fields of the target object, the values are the type of the
685
     *       fields, represented as the constructor function of the type.
686
     *       The default value is `{}`.
687
     *     - `targetElementTypes: object`, the additional information about types of
688
     *       elements of fields of classes. The keys of this object are the path of
689
     *       the fields or sub-fields of the target object, the values are the type
690
     *       of the elements, represented as the constructor function of the type.
691
     *       The default value is `{}`.
692
     * @returns {Array<Class>|null}
693
     *     the new array of instances of this class created from the specified
694
     *     data object array, or `null` if the specified data object array is
695
     *     `null` or `undefined`.
696
     * @see DefaultOptions.get('assign')
697
     */
698
    Class.createArray = function createArray(array, options = undefined) {
99✔
699
      return createArrayImpl(Class, array, options);
13✔
700
    };
701
  }
702
  // Add the class method `createPage()`
703
  if (!Object.prototype.hasOwnProperty.call(Class, 'createPage')) {
99!
704
    /**
705
     *  Creates a new page object based on the specified pagination data object.
706
     *
707
     *  Typically, the pagination data object is the JSON representation of a
708
     *  list of domain objects obtained from a server using the GET method, and
709
     *  the object should conform to the `Page` class definition.
710
     *
711
     * @param {object} page
712
     *     the specified pagination data object, which must conform to the
713
     *     `Page` class definition.
714
     * @param {null|undefined|object} options
715
     *     the additional options for the creation. If this argument is
716
     *     `undefined` or `null`, the default options will be used. The default
717
     *     options can be retrieved by calling `DefaultOptions.get('assign')`.
718
     *     Available options are:
719
     *     - `normalize: boolean`, indicates whether to normalize this object
720
     *       after the assignment. The default value is `true`.
721
     *     - `convertNaming: boolean`, indicates whether to convert the naming
722
     *       style of the target object. The default value is `false`.
723
     *     - `sourceNamingStyle: string`, the naming style of the source object,
724
     *       i.e., the elements in the `content` array of the first argument of
725
     *       the `createPage()` method. The default value of this argument is
726
     *       `'LOWER_UNDERSCORE'`.
727
     *     - `targetNamingStyle: string`, the naming style of the target object,
728
     *       i.e., the elements in the `content` array of the `Page` object
729
     *       returned by the `createPage()` method. The default value of this
730
     *       argument is `'LOWER_CAMEL'`.
731
     *     - `targetTypes: object`, the additional information about types of
732
     *       fields of classes. The keys of this object are the path of the fields
733
     *       or sub-fields of the target object, the values are the type of the
734
     *       fields, represented as the constructor function of the type.
735
     *       The default value is `{}`.
736
     *     - `targetElementTypes: object`, the additional information about types of
737
     *       elements of fields of classes. The keys of this object are the path of
738
     *       the fields or sub-fields of the target object, the values are the type
739
     *       of the elements, represented as the constructor function of the type.
740
     *       The default value is `{}`.
741
     * @returns {Page|null}
742
     *     A new `Page` object, whose `content` property is the result of
743
     *     `this.createArray(page.content, true)`, and the other properties
744
     *     matching those of the `page` object. If the argument `page` is not a
745
     *     valid `Page` object, this function returns `null`.
746
     * @see DefaultOptions.get('assign')
747
     */
748
    Class.createPage = function createPage(page, options = undefined) {
99✔
749
      return createPageImpl(Class, page, options);
12✔
750
    };
751
  }
752
  // Add the class method `isNullishOrEmpty()`
753
  if (!Object.prototype.hasOwnProperty.call(Class, 'isNullishOrEmpty')) {
99!
754
    /**
755
     * Determines whether the given instance of this class is `undefined`,
756
     * `null`, or an empty object constructed with default values.
757
     *
758
     * @param {object} obj
759
     *     the specified object.
760
     * @returns {boolean}
761
     *     `true` if the specified object is `undefined`, or `null` or an empty
762
     *     instance of this class; `false` otherwise.
763
     * @throws TypeError
764
     *     if the specified object is not nullish and is not a instance of this
765
     *     class.
766
     */
767
    Class.isNullishOrEmpty = function isNullishOrEmpty(obj) {
99✔
768
      return isNullishOrEmptyImpl(Class, obj);
5✔
769
    };
770
  }
771
  // Add the class method `parseJsonString()`
772
  if (!Object.prototype.hasOwnProperty.call(Class, 'parseJsonString')) {
99!
773
    /**
774
     * Parses an object of this class from a JSON string.
775
     *
776
     * **NOTE:** This method supports integer values fall out of IEEE 754 integer
777
     * precision. For example, the integer value `9223372036854775807` will be
778
     * parsed as the native `bigint` value `9223372036854775807n`.
779
     *
780
     * @param {string} json
781
     *     the JSON string to be parsed.
782
     * @param {null|undefined|object} options
783
     *     the additional options for the parsing. If this argument is
784
     *     `undefined` or `null`, the default options will be used. The default
785
     *     options can be retrieved by calling `DefaultOptions.get('assign')`.
786
     *     Available options are:
787
     *     - `normalize: boolean`, indicates whether to normalize this object
788
     *       after the assignment. The default value is `true`.
789
     *     - `convertNaming: boolean`, indicates whether to convert the naming
790
     *       style of the target object. The default value is `false`.
791
     *     - `sourceNamingStyle: string`, the naming style of the source object,
792
     *       i.e., the first argument of the `assign()` method. The default
793
     *       value of this argument is `'LOWER_UNDERSCORE'`.
794
     *     - `targetNamingStyle: string`, the naming style of the target object,
795
     *       i.e., the object calling the `assign()` method. The default value
796
     *       of this argument is `'LOWER_CAMEL'`.
797
     * @returns {object}
798
     *     the object deserialized from the specified JSON string.
799
     * @see toJsonString()
800
     */
801
    Class.parseJsonString = function parseJsonString(json, options = undefined) {
99✔
802
      return parseJsonStringImpl(Class, json, options);
4✔
803
    };
804
  }
805

806
  // console.log('@Model: Class = ', Class, ', Class.prototype = ', Class.prototype);
807
}
808

809
export default Model;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc