Просмотр исходного кода

chore: adds local validators and transformers

e22m4u 3 месяцев назад
Родитель
Сommit
f4ad5ed320
85 измененных файлов с 3787 добавлено и 1435 удалено
  1. 475 60
      README.md
  2. 192 165
      dist/cjs/index.cjs
  3. 1 1
      src/adapter/adapter-loader.js
  4. 1 1
      src/adapter/adapter-loader.spec.js
  5. 1 1
      src/adapter/decorator/data-sanitizing-decorator.js
  6. 1 1
      src/adapter/decorator/data-transformation-decorator.js
  7. 1 1
      src/adapter/decorator/data-validation-decorator.js
  8. 1 1
      src/adapter/decorator/default-values-decorator.js
  9. 1 1
      src/adapter/decorator/fields-filtering-decorator.js
  10. 1 1
      src/adapter/decorator/inclusion-decorator.js
  11. 1 1
      src/adapter/decorator/property-uniqueness-decorator.js
  12. 3 3
      src/definition/datasource/datasource-definition-validator.js
  13. 3 3
      src/definition/datasource/datasource-definition-validator.spec.js
  14. 2 2
      src/definition/model/model-data-sanitizer.js
  15. 71 13
      src/definition/model/model-data-transformer.js
  16. 2073 407
      src/definition/model/model-data-transformer.spec.js
  17. 125 37
      src/definition/model/model-data-validator.js
  18. 271 236
      src/definition/model/model-data-validator.spec.js
  19. 5 5
      src/definition/model/model-definition-utils.js
  20. 7 7
      src/definition/model/model-definition-utils.spec.js
  21. 7 7
      src/definition/model/model-definition-validator.js
  22. 10 7
      src/definition/model/model-definition-validator.spec.js
  23. 129 54
      src/definition/model/properties/properties-definition-validator.js
  24. 85 25
      src/definition/model/properties/properties-definition-validator.spec.js
  25. 0 1
      src/definition/model/properties/property-transformer/builtin/index.d.ts
  26. 0 1
      src/definition/model/properties/property-transformer/builtin/index.js
  27. 1 1
      src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.d.ts
  28. 1 1
      src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.js
  29. 1 1
      src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.spec.js
  30. 0 6
      src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.d.ts
  31. 0 22
      src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.js
  32. 0 41
      src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.spec.js
  33. 1 1
      src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.d.ts
  34. 1 1
      src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.js
  35. 1 1
      src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.spec.js
  36. 1 1
      src/definition/model/properties/property-transformer/builtin/trim-transformer.js
  37. 1 1
      src/definition/model/properties/property-transformer/builtin/trim-transformer.spec.js
  38. 2 4
      src/definition/model/properties/property-transformer/property-transformer-registry.js
  39. 2 4
      src/definition/model/properties/property-transformer/property-transformer-registry.spec.js
  40. 2 2
      src/definition/model/properties/property-transformer/property-transformer.d.ts
  41. 4 4
      src/definition/model/properties/property-uniqueness-validator.js
  42. 4 4
      src/definition/model/properties/property-uniqueness-validator.spec.js
  43. 1 1
      src/definition/model/properties/property-validator/builtin/max-length-validator.d.ts
  44. 2 2
      src/definition/model/properties/property-validator/builtin/max-length-validator.js
  45. 2 2
      src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js
  46. 1 1
      src/definition/model/properties/property-validator/builtin/min-length-validator.d.ts
  47. 2 2
      src/definition/model/properties/property-validator/builtin/min-length-validator.js
  48. 2 2
      src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js
  49. 1 1
      src/definition/model/properties/property-validator/builtin/regexp-validator.d.ts
  50. 2 2
      src/definition/model/properties/property-validator/builtin/regexp-validator.js
  51. 2 2
      src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js
  52. 2 2
      src/definition/model/properties/property-validator/property-validator-registry.js
  53. 2 2
      src/definition/model/properties/property-validator/property-validator-registry.spec.js
  54. 2 2
      src/definition/model/properties/property-validator/property-validator.d.ts
  55. 23 23
      src/definition/model/relations/relations-definition-validator.js
  56. 24 24
      src/definition/model/relations/relations-definition-validator.spec.js
  57. 1 1
      src/errors/invalid-operator-value-error.js
  58. 1 1
      src/errors/invalid-operator-value-error.spec.js
  59. 5 5
      src/filter/fields-clause-tool.js
  60. 16 16
      src/filter/fields-clause-tool.spec.js
  61. 6 6
      src/filter/include-clause-tool.js
  62. 2 2
      src/filter/include-clause-tool.spec.js
  63. 13 13
      src/filter/operator-clause-tool.js
  64. 13 13
      src/filter/operator-clause-tool.spec.js
  65. 3 3
      src/filter/order-clause-tool.js
  66. 4 4
      src/filter/order-clause-tool.spec.js
  67. 5 5
      src/filter/slice-clause-tool.js
  68. 5 5
      src/filter/slice-clause-tool.spec.js
  69. 4 4
      src/filter/where-clause-tool.js
  70. 3 3
      src/filter/where-clause-tool.spec.js
  71. 14 14
      src/relations/belongs-to-resolver.js
  72. 14 14
      src/relations/belongs-to-resolver.spec.js
  73. 22 22
      src/relations/has-many-resolver.js
  74. 23 23
      src/relations/has-many-resolver.spec.js
  75. 22 22
      src/relations/has-one-resolver.js
  76. 23 23
      src/relations/has-one-resolver.spec.js
  77. 7 7
      src/relations/references-many-resolver.js
  78. 7 7
      src/relations/references-many-resolver.spec.js
  79. 1 1
      src/repository/repository-registry.js
  80. 1 1
      src/utils/exclude-object-keys.js
  81. 1 1
      src/utils/exclude-object-keys.spec.js
  82. 1 1
      src/utils/model-name-to-model-key.js
  83. 7 7
      src/utils/model-name-to-model-key.spec.js
  84. 3 3
      src/utils/select-object-keys.js
  85. 3 3
      src/utils/select-object-keys.spec.js

+ 475 - 60
README.md

@@ -6,12 +6,18 @@
 - [Импорт](#импорт)
 - [Описание](#описание)
 - [Пример](#пример)
-- [Схема базы данных](#схема-базы-данных)
+- [Схема](#схема)
 - [Источник данных](#источник-данных)
 - [Модель](#модель)
 - [Свойства](#свойства)
 - [Валидаторы](#валидаторы)
+  - [Глобальные валидаторы](#глобальные-валидаторы)
+  - [Регистрация глобальных валидаторов](#регистрация-глобальных-валидаторов)
+  - [Локальные валидаторы](#локальные-валидаторы)
 - [Трансформеры](#трансформеры)
+  - [Глобальные трансформеры](#глобальные-трансформеры)
+  - [Регистрация глобальных трансформеров](#регистрация-глобальных-трансформеров)
+  - [Локальные трансформеры](#локальные-трансформеры)
 - [Пустые значения](#пустые-значения)
 - [Репозиторий](#репозиторий)
 - [Фильтрация](#фильтрация)
@@ -129,10 +135,10 @@ dbs.defineModel({
   },
 })
 
-// получение репозитория модели "country"
+// получение репозитория модели
 const countryRep = dbs.getRepository('country');
 
-// добавление нового документа в коллекцию "country"
+// добавление нового документа в коллекцию
 const country = await countryRep.create({
   name: 'Russia',
   population: 143400000,
@@ -141,16 +147,15 @@ const country = await countryRep.create({
 // вывод нового документа
 console.log(country);
 // {
-//   "id": 1,
-//   "name": "Russia",
-//   "population": 143400000,
+//   id: 1,
+//   name: 'Russia',
+//   population: 143400000,
 // }
 ```
 
-## Схема базы данных
+## Схема
 
-Экземпляр класса `DatabaseSchema` хранит определения источников и моделей
-данных.
+Экземпляр класса `DatabaseSchema` хранит определения источников данных и моделей.
 
 **Методы**
 
@@ -285,7 +290,7 @@ dbs.defineModel({
 - `columnType: string` тип колонки (определяется адаптером)
 - `required: boolean` объявить свойство обязательным
 - `default: any` значение по умолчанию
-- `validate: string | array | object` см. [Валидаторы](#Валидаторы)
+- `validate: string | Function | array | object` см. [Валидаторы](#Валидаторы)
 - `unique: boolean | string` проверять значение на уникальность
 
 **Параметр `unique`**
@@ -341,7 +346,7 @@ dbs.defineModel({
     },
     code: {
       type: DataType.NUMBER, // тип свойства "number" (обязательно)
-      unique: PropertyUniqueness.UNIQUE, // проверять уникальность
+      unique: PropertyUniqueness.STRICT, // проверять уникальность
     },
   },
 });
@@ -369,19 +374,64 @@ dbs.defineModel({
 
 ## Валидаторы
 
-Кроме проверки типа, дополнительные условия можно задать с помощью
-валидаторов, через которые будет проходить значение свойства перед
-записью в базу. Исключением являются [пустые значения](#Пустые-значения),
-которые не подлежат проверке.
+Валидаторы используются для проверки значения свойства перед записью в базу.
+Проверка значения валидатором выполняется сразу после проверки типа, указанного
+в определении свойства модели. [Пустые значения](#пустые-значения) пропускают
+проверку валидаторами, так как не имеют полезной нагрузки.
+
+### Глобальные валидаторы
+
+Модуль поставляется с набором глобальных валидаторов:
+
+- `regexp` проверка по регулярному выражению,  
+  *параметр: `string | RegExp` - рег. выражение;*
+
+- `minLength` минимальная длина строки или массива,  
+  *параметр: `number` - минимальная длина;*
+
+- `maxLength` максимальная длина строки или массива,  
+  *параметр: `number` - максимальная длина;*
+
+Валидаторы указанные ниже находятся в разработке:
+
+- `isLowerCase` проверка регистра (только прописные буквы);
+- `isUpperCase` проверка регистра (только строчные буквы);
+- `isEmail` проверка формата электронного адреса;
+
+**Примеры**
+
+Использование глобального валидатора.
+
+```js
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    email: {
+      type: DataType.STRING,
+      validate: 'isEmail',
+    },
+  },
+});
+```
 
-- `minLength: number` минимальная длинна строки или массива
-- `maxLength: number` максимальная длинна строки или массива
-- `regexp: string | RegExp` проверка по регулярному выражению
+Использование глобальных валидаторов в виде массива.
 
-**Пример**
+```js
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    email: {
+      type: DataType.STRING,
+      validate: [
+        'isEmail',
+        'isLowerCase',
+      ],
+    },
+  },
+});
+```
 
-Валидаторы указываются в объявлении свойства модели параметром
-`validate`, который принимает объект с их названиями и настройками.
+Использование глобальных валидаторов с передачей аргументов.
 
 ```js
 dbs.defineModel({
@@ -389,46 +439,206 @@ dbs.defineModel({
   properties: {
     name: {
       type: DataType.STRING,
-      validate: { // валидаторы свойства "name"
-        minLength: 2, // минимальная длинна строки
-        maxLength: 24, // максимальная длинна строки
+      validate: {
+        minLength: 2,
+        maxLength: 24,
+        regexp: /^[a-zA-Z-']+$/,
+      },
+    },
+  },
+});
+```
+
+Глобальные валидаторы без параметров могут принимать любые аргументы.
+
+```js
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    email: {
+      type: DataType.STRING,
+      validate: {
+        maxLength: 100,
+        // так как валидатор "isEmail" не имеет параметров,
+        // его определение допускает передачу любого значения
+        // в качестве аргумента
+        isEmail: true,
       },
     },
   },
 });
 ```
 
-### Пользовательские валидаторы
+### Регистрация глобальных валидаторов
 
 Валидатором является функция, в которую передается значение соответствующего
 поля перед записью в базу. Если во время проверки функция возвращает `false`,
 то выбрасывается стандартная ошибка. Подмена стандартной ошибки возможна
 с помощью выброса пользовательской ошибки непосредственно внутри функции.
 
-Регистрация пользовательского валидатора выполняется методом `addValidator`
-сервиса `PropertyValidatorRegistry`, который принимает новое название
-и функцию для проверки значения.
+Регистрация глобального валидатора выполняется методом `addValidator` сервиса
+`PropertyValidatorRegistry`, который принимает название валидатора и функцию
+для проверки значения.
 
-**Пример**
+**Примеры**
+
+Регистрация глобального валидатора для проверки формата UUID.
 
 ```js
-// создание валидатора для запрета
-// всех символов кроме чисел
-const numericValidator = (input) => {
-  return /^[0-9]+$/.test(String(input));
+import {createError} from 'http-errors';
+import {format} from '@e22m4u/js-format';
+import {Errorf} from '@e22m4u/js-format';
+import {PropertyValidatorRegistry} from '@e22m4u/js-repository';
+
+// получение экземпляра сервиса
+const pvr = dbs.get(PropertyValidatorRegistry);
+
+// регулярные выражения для разных версий UUID
+const uuidRegex = {
+  any: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
+  v4: /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
+};
+
+// регистрация глобального валидатора "isUuid",
+// принимающего объект настроек со свойством "version"
+pvr.addValidator('isUuid', (value, options, context) => {
+  // value   - проверяемое значение;
+  // options - настройки валидатора;
+  // context - информация о проверяемом свойстве;
+  console.log(options);
+  // {
+  //   version: 'v4'
+  // }
+  console.log(context);
+  // {
+  //   validatorName: 'isUuid',
+  //   modelName: 'device',
+  //   propName: 'deviceId'
+  // }
+
+  // пустые значения не передаются в валидаторы
+  if (typeof value !== 'string') return false;
+  // поиск регулярного выражения для нужной версии
+  const version = options?.version || 'any';
+  const regex = uuidRegex[version];
+  // если регулярное выражение не найдено,
+  // то выбрасывается внутренняя ошибка
+  if (!regex)
+    throw new Errorf(
+      'Invalid UUID version %v specified for validator.',
+      version,
+    );
+  // при неудачной проверке выбрасывается
+  // ошибка 400 BadRequest
+  if (!regex.test(value)) {
+    const versionString = version !== 'any' ? ` (version ${version})` : '';
+    throw createError(400, format(
+      'The property %v of the model %v must be a valid UUID%s.',
+      context.propName,
+      context.modelName,
+      versionString,
+    ));
+  }
+  // при успешной проверке возвращается true,
+  // в противном случае выбрасывается стандартная
+  // ошибка проверки
+  return true;
+});
+```
+
+Использование глобального валидатора в определении свойства.
+
+```js
+// определение модели "device"
+dbs.defineModel({
+  name: 'device',
+  properties: {
+    deviceId: {
+      type: DataType.STRING,
+      required: true,
+      validate: {
+        // значение {version: 'v4'} будет передаваться
+        // вторым аргументом в функцию-валидатор
+        isUuid: {version: 'v4'},
+      },
+    },
+  },
+});
+```
+
+### Локальные валидаторы
+
+Функция-валидатор может быть передана непосредственно в определении свойства
+без предварительной регистрации. Для этого достаточно передать функцию
+в параметр `validate` в качестве значения или элемента массива наряду
+с другими валидаторами.
+
+**Примеры**
+
+Использование локального валидатора для проверки сложности пароля.
+
+```js
+// валидатор `passwordStrength` проверяет сложность пароля
+function passwordStrength(value, options, context) {
+  // value   - проверяемое значение;
+  // options - не используется;
+  // context - информация о проверяемом свойстве;
+  console.log(context);
+  // {
+  //   validatorName: 'passwordStrength',
+  //   modelName: 'user',
+  //   propName: 'password'
+  // }
+  const errors = [];
+  if (value.length < 8)
+    errors.push('must be at least 8 characters long');
+  if (!/\d/.test(value))
+    errors.push('must contain at least one number');
+  if (!/[a-zA-Z]/.test(value))
+    errors.push('must contain at least one letter');
+  // если одно из условий сработало,
+  // то выбрасывается ошибка
+  if (errors.length > 0)
+    throw createError(400, format(
+      'Value of the property %v of the model %v %s.',
+      context.propName,
+      context.modelName,
+      errors.join(', '),
+    ));
+  // при успешной проверке возвращается true,
+  // в противном случае выбрасывается стандартная
+  // ошибка проверки
+  return true;
 }
 
-// регистрация валидатора "numeric"
-dbs.get(PropertyValidatorRegistry).addValidator('numeric', numericValidator);
+// определение модели "user"
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    password: { // <--- Правильное свойство
+      type: DataType.STRING,
+      required: true, // Пароль обычно обязателен
+      validate: passwordStrength, // <=
+      // или
+      // validate: [passwordStrength, ...]
+    },
+  },
+});
+```
 
-// использование валидатора в определении
-// свойства "code" для новой модели
+Использование анонимной функции-валидатора.
+
+```js
+// определение модели "article"
 dbs.defineModel({
-  name: 'document',
+  name: 'article',
   properties: {
-    code: {
+    slug: {
       type: DataType.STRING,
-      validate: 'numeric',
+      validate: (value) => {
+        const re = /^[a-z0-9]+(-[a-z0-9]+)*$/;
+        return re.test(value);
+      },
     },
   },
 });
@@ -436,39 +646,244 @@ dbs.defineModel({
 
 ## Трансформеры
 
-С помощью трансформеров производится модификация значений определенных
-полей перед записью в базу. Трансформеры позволяют указать какие изменения
-нужно производить с входящими данными. Исключением являются
-[пустые значения](#Пустые-значения), которые не подлежат трансформации.
+Трансформеры используются для модификации значения свойства перед проверкой
+типа и передачей значения в базу. Трансформеры позволяют автоматически очищать
+или приводить данные к нужному формату.
+
+### Глобальные трансформеры
+
+Модуль поставляется с набором глобальных трансформеров:
+
+- `trim` удаление пробельных символов с начала и конца строки;
+- `toUpperCase` перевод строки в верхний регистр;
+- `toLowerCase` перевод строки в нижний регистр;
+
+Трансформеры указанные ниже находятся в разработке:
 
-- `trim` удаление пробельных символов с начала и конца строки
-- `toUpperCase` перевод строки в верхний регистр
-- `toLowerCase` перевод строки в нижний регистр
-- `toTitleCase` перевод строки в регистр заголовка
+- `cut` усечение строки или массива до указанной длины,  
+  *параметр: `number` - максимальная длина;*
 
-**Пример**
+- `truncate` усечение строки с добавлением троеточия,  
+  *параметр: `number` - максимальная длина;*
 
-Трансформеры указываются в объявлении свойства модели параметром
-`transform`, который принимает название трансформера. Если требуется
-указать несколько названий, то используется массив. Если трансформер
-имеет настройки, то используется объект, где ключом является название
-трансформера, а значением его параметры.
+- `capitalize` перевод первой буквы каждого слова в верхний регистр,  
+  *параметр: `{firstWordOnly?: boolean}`;*
+
+**Примеры**
+
+Использование глобального трансформера.
 
 ```js
 dbs.defineModel({
   name: 'user',
   properties: {
-    name: {
+    username: {
       type: DataType.STRING,
-      transform: [ // трансформеры свойства "name"
-        'trim', // удалить пробелы в начале и конце строки
-        'toTitleCase', // перевод строки в регистр заголовка
+      transform: 'toLowerCase',
+    },
+  },
+});
+```
+
+Использование глобальных трансформеров в виде массива.
+
+```js
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    firstName: {
+      type: DataType.STRING,
+      transform: [
+        'trim',
+        'capitalize',
       ],
     },
   },
 });
 ```
 
+Использование глобальных трансформеров с передачей аргументов.
+
+```js
+dbs.defineModel({
+  name: 'article',
+  properties: {
+    annotation: {
+      type: DataType.STRING,
+      transform: {
+        truncate: 200,
+        capitalize: {firstWordOnly: true},
+      },
+    },
+  },
+});
+```
+
+Глобальные трансформеры без параметров могут принимать любые аргументы.
+
+```js
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    firstName: {
+      type: DataType.STRING,
+      transform: {
+        cut: 60,
+        // так как трансформер "trim" не имеет параметров,
+        // его определение допускает передачу любого значения
+        // в качестве аргумента
+        trim: true,
+      },
+    },
+  },
+});
+```
+
+### Регистрация глобальных трансформеров
+
+Трансформером является функция, которая принимает значение свойства и возвращает
+новое значение. Функция может быть как синхронной, так и асинхронной (возвращать
+`Promise`).
+
+Регистрация глобального трансформера выполняется методом `addTransformer`
+сервиса `PropertyTransformerRegistry`, который принимает название трансформера
+и саму функцию.
+
+**Примеры**
+
+Регистрация глобального трансформера для удаления HTML-тегов.
+
+```js
+import {PropertyTransformerRegistry} from '@e22m4u/js-repository';
+
+// получение экземпляра сервиса
+const ptr = dbs.get(PropertyTransformerRegistry);
+
+// регистрация глобального трансформера "stripTags"
+ptr.addTransformer('stripTags', (value, options, context) => {
+  // value   - трансформируемое значение;
+  // options - настройки трансформера (если переданы);
+  // context - информация о свойстве;
+  console.log(context);
+  // {
+  //   transformerName: 'stripTags',
+  //   modelName: 'comment',
+  //   propName: 'text'
+  // }
+  
+  if (typeof value !== 'string')
+    return value; // возвращаем как есть, если не строка
+  
+  return value.replace(/<[^>]*>?/gm, '');
+});
+```
+
+Использование глобального трансформера в определении модели.
+
+```js
+dbs.defineModel({
+  name: 'comment',
+  properties: {
+    text: {
+      type: DataType.STRING,
+      transform: 'stripTags',
+    },
+  },
+});
+```
+
+### Локальные трансформеры
+
+Функция-трансформер может быть передана непосредственно в определении свойства без предварительной регистрации. Для этого достаточно передать функцию в параметр `transform` в качестве значения или элемента массива.
+
+**Примеры**
+
+Использование локального трансформера для нормализации имен.
+
+```js
+// функция для нормализации имени
+function normalizeName(value, options, context) {
+  // value   - трансформируемое значение
+  // options - не используется
+  // context - информация о свойстве
+  if (!value || typeof value !== 'string') return value;
+  return value
+    .trim()        // удаление пробелов в начале и конце
+    .toLowerCase() // перевод к нижнему регистру
+    .split(' ')    // разделение на слова
+    // перевод к верхнему регистру первой буквы каждого слова
+    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
+    .join(' ');    // сборка массива в строку
+}
+
+// определение модели "user"
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    firstName: {
+      type: DataType.STRING,
+      transform: normalizeName, // <=
+    },
+    lastName: {
+      type: DataType.STRING,
+      transform: normalizeName, // <=
+    },
+  },
+});
+```
+
+Использование локального асинхронного трансформера для хэширования пароля.
+
+```js
+import * as bcrypt from 'bcrypt';
+
+// асинхронная функция для хеширования значения
+async function hash(value, options, context) {
+  // value   - трансформируемое значение
+  // options - не используется
+  // context - информация о свойстве
+  console.log(context);
+  // {
+  //   transformerName: 'hash',
+  //   modelName: 'user',
+  //   propName: 'password'
+  // }
+  const saltRounds = 10;
+  return bcrypt.hash(value, saltRounds);
+}
+
+// определение модели "user"
+dbs.defineModel({
+  name: 'user',
+  properties: {
+    password: {
+      type: DataType.STRING,
+      transform: hash, // <=
+      // или
+      // transform: [hash, ...]
+    },
+  },
+});
+```
+
+Использование анонимной функции-трансформера.
+
+```js
+dbs.defineModel({
+  name: 'article',
+  properties: {
+    slug: {
+      type: DataType.STRING,
+      transform: (value) => {
+        if (typeof value !== 'string') return value;
+        return value.toLowerCase().replace(/\s+/g, '-');
+      },
+    },
+  },
+});
+```
+
+
 ## Пустые значения
 
 Разные типы свойств имеют свои наборы пустых значений. Эти наборы
@@ -924,7 +1339,7 @@ dbs.defineModel({
   name: 'city',
   datasource: 'myDatasource',
   properties: {
-    title: DataType.STRING,
+    name: DataType.STRING,
     timeZone: DataType.STRING,
   },
   relations: {
@@ -938,7 +1353,7 @@ dbs.defineModel({
 // определение интерфейса "city"
 interface City {
   id: number;
-  title?: string;
+  name?: string;
   timeZone?: string;
   countryId?: number;
   country?: Country;

Разница между файлами не показана из-за своего большого размера
+ 192 - 165
dist/cjs/index.cjs


+ 1 - 1
src/adapter/adapter-loader.js

@@ -17,7 +17,7 @@ export class AdapterLoader extends Service {
   async loadByName(adapterName, settings = undefined) {
     if (!adapterName || typeof adapterName !== 'string')
       throw new InvalidArgumentError(
-        'The adapter name should be a non-empty String, but %v given.',
+        'The adapter name should be a non-empty String, but %v was given.',
         adapterName,
       );
     let adapterCtor;

+ 1 - 1
src/adapter/adapter-loader.spec.js

@@ -9,7 +9,7 @@ describe('AdapterLoader', function () {
     it('requires an adapter name as a non-empty string', async function () {
       const promise = S.loadByName('');
       await expect(promise).to.be.rejectedWith(
-        'The adapter name should be a non-empty String, but "" given.',
+        'The adapter name should be a non-empty String, but "" was given.',
       );
     });
 

+ 1 - 1
src/adapter/decorator/data-sanitizing-decorator.js

@@ -16,7 +16,7 @@ export class DataSanitizingDecorator extends Service {
     if (!adapter || !(adapter instanceof Adapter))
       throw new InvalidArgumentError(
         'The first argument of DataSanitizingDecorator.decorate should be ' +
-          'an Adapter instance, but %v given.',
+          'an Adapter instance, but %v was given.',
         adapter,
       );
 

+ 1 - 1
src/adapter/decorator/data-transformation-decorator.js

@@ -16,7 +16,7 @@ export class DataTransformationDecorator extends Service {
     if (!adapter || !(adapter instanceof Adapter))
       throw new InvalidArgumentError(
         'The first argument of DataTransformerDecorator.decorate should be ' +
-          'an Adapter instance, but %v given.',
+          'an Adapter instance, but %v was given.',
         adapter,
       );
     const transformer = this.getService(ModelDataTransformer);

+ 1 - 1
src/adapter/decorator/data-validation-decorator.js

@@ -16,7 +16,7 @@ export class DataValidationDecorator extends Service {
     if (!adapter || !(adapter instanceof Adapter))
       throw new InvalidArgumentError(
         'The first argument of DataValidationDecorator.decorate should be ' +
-          'an Adapter instance, but %v given.',
+          'an Adapter instance, but %v was given.',
         adapter,
       );
     const validator = this.getService(ModelDataValidator);

+ 1 - 1
src/adapter/decorator/default-values-decorator.js

@@ -16,7 +16,7 @@ export class DefaultValuesDecorator extends Service {
     if (!adapter || !(adapter instanceof Adapter))
       throw new InvalidArgumentError(
         'The first argument of DefaultValuesDecorator.decorate should be ' +
-          'an Adapter instance, but %v given.',
+          'an Adapter instance, but %v was given.',
         adapter,
       );
 

+ 1 - 1
src/adapter/decorator/fields-filtering-decorator.js

@@ -16,7 +16,7 @@ export class FieldsFilteringDecorator extends Service {
     if (!adapter || !(adapter instanceof Adapter))
       throw new InvalidArgumentError(
         'The first argument of FieldsFilteringDecorator.decorate should be ' +
-          'an Adapter instance, but %v given.',
+          'an Adapter instance, but %v was given.',
         adapter,
       );
 

+ 1 - 1
src/adapter/decorator/inclusion-decorator.js

@@ -16,7 +16,7 @@ export class InclusionDecorator extends Service {
     if (!adapter || !(adapter instanceof Adapter))
       throw new InvalidArgumentError(
         'The first argument of InclusionDecorator.decorate should be ' +
-          'an Adapter instance, but %v given.',
+          'an Adapter instance, but %v was given.',
         adapter,
       );
 

+ 1 - 1
src/adapter/decorator/property-uniqueness-decorator.js

@@ -16,7 +16,7 @@ export class PropertyUniquenessDecorator extends Service {
     if (!adapter || !(adapter instanceof Adapter))
       throw new InvalidArgumentError(
         'The first argument of PropertyUniquenessDecorator.decorate should be ' +
-          'an Adapter instance, but %v given.',
+          'an Adapter instance, but %v was given.',
         adapter,
       );
     const validator = this.getService(PropertyUniquenessValidator);

+ 3 - 3
src/definition/datasource/datasource-definition-validator.js

@@ -13,19 +13,19 @@ export class DatasourceDefinitionValidator extends Service {
   validate(datasourceDef) {
     if (!datasourceDef || typeof datasourceDef !== 'object')
       throw new InvalidArgumentError(
-        'The datasource definition should be an Object, but %v given.',
+        'The datasource definition should be an Object, but %v was given.',
         datasourceDef,
       );
     if (!datasourceDef.name || typeof datasourceDef.name !== 'string')
       throw new InvalidArgumentError(
         'The datasource definition requires the option "name" ' +
-          'as a non-empty String, but %v given.',
+          'as a non-empty String, but %v was given.',
         datasourceDef.name,
       );
     if (!datasourceDef.adapter || typeof datasourceDef.adapter !== 'string')
       throw new InvalidArgumentError(
         'The datasource %v requires the option "adapter" ' +
-          'as a non-empty String, but %v given.',
+          'as a non-empty String, but %v was given.',
         datasourceDef.name,
         datasourceDef.adapter,
       );

+ 3 - 3
src/definition/datasource/datasource-definition-validator.spec.js

@@ -10,7 +10,7 @@ describe('DatasourceDefinitionValidator', function () {
       const validate = v => () => S.validate(v);
       const error = v =>
         format(
-          'The datasource definition should be an Object, but %s given.',
+          'The datasource definition should be an Object, but %s was given.',
           v,
         );
       expect(validate('str')).to.throw(error('"str"'));
@@ -27,7 +27,7 @@ describe('DatasourceDefinitionValidator', function () {
       const error = v =>
         format(
           'The datasource definition requires the option "name" ' +
-            'as a non-empty String, but %s given.',
+            'as a non-empty String, but %s was given.',
           v,
         );
       expect(validate('')).to.throw(error('""'));
@@ -46,7 +46,7 @@ describe('DatasourceDefinitionValidator', function () {
       const error = v =>
         format(
           'The datasource "datasource" requires the option "adapter" ' +
-            'as a non-empty String, but %s given.',
+            'as a non-empty String, but %s was given.',
           v,
         );
       expect(validate('')).to.throw(error('""'));

+ 2 - 2
src/definition/model/model-data-sanitizer.js

@@ -17,13 +17,13 @@ export class ModelDataSanitizer extends Service {
     if (!modelName || typeof modelName !== 'string')
       throw new InvalidArgumentError(
         'The first argument of ModelDataSanitizer.sanitize ' +
-          'should be a string, but %v given.',
+          'should be a string, but %v was given.',
         modelName,
       );
     if (!modelData || typeof modelData !== 'object')
       throw new InvalidArgumentError(
         'The second argument of ModelDataSanitizer.sanitize ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         modelData,
       );
     return this.getService(

+ 71 - 13
src/definition/model/model-data-transformer.js

@@ -22,7 +22,7 @@ export class ModelDataTransformer extends Service {
   transform(modelName, modelData, isPartial = false) {
     if (!isPureObject(modelData))
       throw new InvalidArgumentError(
-        'The data of the model %v should be an Object, but %v given.',
+        'The data of the model %v should be an Object, but %v was given.',
         modelName,
         modelData,
       );
@@ -64,7 +64,7 @@ export class ModelDataTransformer extends Service {
    * @param {string} propName
    * @param {string|object} propDef
    * @param {*} propValue
-   * @returns {*}
+   * @returns {*|Promise<*>}
    */
   _transformPropertyValue(modelName, propName, propDef, propValue) {
     if (typeof propDef === 'string' || propDef.transform == null)
@@ -73,22 +73,76 @@ export class ModelDataTransformer extends Service {
     const transformerRegistry = this.getService(PropertyTransformerRegistry);
     const transformFn = (
       value,
-      transformerName,
+      transformerOrName,
       transformerOptions = undefined,
     ) => {
-      const transformer = transformerRegistry.getTransformer(transformerName);
+      let transformerName, transformerFn;
+      // если второй аргумент является строкой, то строка
+      // воспринимается как название зарегистрированного
+      // трансформера
+      if (typeof transformerOrName === 'string') {
+        transformerName = transformerOrName;
+        transformerFn = transformerRegistry.getTransformer(transformerName);
+      }
+      // если второй аргумент является функцией,
+      // то функция воспринимается как трансформер
+      else if (typeof transformerOrName === 'function') {
+        transformerName =
+          transformerOrName.name && transformerOrName.name !== 'transform'
+            ? transformerOrName.name
+            : undefined;
+        transformerFn = transformerOrName;
+      }
+      // если второй аргумент не является строкой
+      // и функцией, то выбрасывается ошибка
+      else {
+        throw new InvalidArgumentError(
+          'Transformer must be a non-empty String or ' +
+            'a Function, but %v was given.',
+          transformerOrName,
+        );
+      }
       const context = {transformerName, modelName, propName};
-      return transformer(value, transformerOptions, context);
+      return transformerFn(value, transformerOptions, context);
     };
+    // если значением опции "transform" является строка,
+    // то строка воспринимается как название трансформера
     if (transformDef && typeof transformDef === 'string') {
       return transformFn(propValue, transformDef);
-    } else if (Array.isArray(transformDef)) {
-      return transformDef.reduce((valueOrPromise, transformerName) => {
+    }
+    // если значением опции "transform" является функция,
+    // то функция воспринимается как трансформер
+    else if (transformDef && typeof transformDef === 'function') {
+      return transformFn(propValue, transformDef);
+    }
+    // если значение опции "transform" является массив, то каждый
+    // элемент массива воспринимается как название трансформера
+    // или функция-валидатор
+    else if (Array.isArray(transformDef)) {
+      return transformDef.reduce((valueOrPromise, transformerOrName) => {
+        if (
+          !transformerOrName ||
+          (typeof transformerOrName !== 'string' &&
+            typeof transformerOrName !== 'function')
+        ) {
+          throw new InvalidArgumentError(
+            'The provided option "transform" for the property %v ' +
+              'in the model %v has an Array value that should contain ' +
+              'transformer names or transformer functions, but %v was given.',
+            propName,
+            modelName,
+            transformerOrName,
+          );
+        }
         return transformPromise(valueOrPromise, value => {
-          return transformFn(value, transformerName);
+          return transformFn(value, transformerOrName);
         });
       }, propValue);
-    } else if (transformDef !== null && typeof transformDef === 'object') {
+    }
+    // если значение опции "transform" является объектом,
+    // то ключи объекта воспринимаются как названия трансформеров,
+    // а их значения аргументами
+    else if (transformDef !== null && typeof transformDef === 'object') {
       return Object.keys(transformDef).reduce(
         (valueOrPromise, transformerName) => {
           const transformerOptions = transformDef[transformerName];
@@ -98,11 +152,15 @@ export class ModelDataTransformer extends Service {
         },
         propValue,
       );
-    } else {
+    }
+    // если значение опции "transform" не является строкой,
+    // функцией и массивом, то выбрасывается ошибка
+    else {
       throw new InvalidArgumentError(
-        'The provided option "transform" of the property %v in the model %v ' +
-          'should be a non-empty String, an Array of String or an Object, ' +
-          'but %v given.',
+        'The provided option "transform" for the property %v in the model %v ' +
+          'should be either a transformer name, a transformer function, an array ' +
+          'of transformer names or functions, or an object mapping transformer ' +
+          'names to their arguments, but %v was given.',
         propName,
         modelName,
         transformDef,

+ 2073 - 407
src/definition/model/model-data-transformer.spec.js

@@ -2,6 +2,7 @@ import {expect} from 'chai';
 import {format} from '@e22m4u/js-format';
 import {DataType} from './properties/index.js';
 import {DatabaseSchema} from '../../database-schema.js';
+import {EmptyValuesService} from '@e22m4u/js-empty-values';
 import {DefinitionRegistry} from '../definition-registry.js';
 import {ModelDataTransformer} from './model-data-transformer.js';
 import {PropertyTransformerRegistry} from './properties/index.js';
@@ -21,7 +22,7 @@ describe('ModelDataTransformer', function () {
       const throwable = v => () => T.transform('model', v);
       const error = v =>
         format(
-          'The data of the model "model" should be an Object, but %s given.',
+          'The data of the model "model" should be an Object, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -54,42 +55,58 @@ describe('ModelDataTransformer', function () {
       expect(res).to.be.eql(modelData);
     });
 
-    describe('the option "transform" with a string value', function () {
-      it('transforms the property value by its transformer', function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer = value => String(value);
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.ANY,
-              transform: 'myTransformer',
-            },
+    it('the option "transform" requires a non-empty String, a Function, an Array or an Object', function () {
+      const dbs = new DatabaseSchema();
+      dbs
+        .getService(PropertyTransformerRegistry)
+        .addTransformer('myTransformer', () => 'transformed');
+      dbs.defineModel({
+        name: 'model',
+        properties: {
+          foo: {
+            type: DataType.ANY,
+            transform: undefined,
           },
-        });
-        const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 10});
-        expect(res).to.be.eql({foo: '10'});
+        },
       });
+      const T = dbs.getService(ModelDataTransformer);
+      const throwable = v => () => {
+        const models = dbs.getService(DefinitionRegistry)['_models'];
+        models.model.properties.foo.transform = v;
+        T.transform('model', {foo: 'bar'});
+      };
+      const error = v =>
+        format(
+          'The provided option "transform" for the property "foo" in the model "model" ' +
+            'should be either a transformer name, a transformer function, an array ' +
+            'of transformer names or functions, or an object mapping transformer ' +
+            'names to their arguments, but %s was given.',
+          v,
+        );
+      expect(throwable('')).to.throw(error('""'));
+      expect(throwable(10)).to.throw(error('10'));
+      expect(throwable(0)).to.throw(error('0'));
+      expect(throwable(true)).to.throw(error('true'));
+      expect(throwable(false)).to.throw(error('false'));
+      throwable('myTransformer')();
+      throwable(() => 10)();
+      throwable(() => Promise.resolve('bar'))();
+      throwable(['myTransformer'])();
+      throwable([() => Promise.resolve('bar')])();
+      throwable([])();
+      throwable({myTransformer: true})();
+      throwable({})();
+    });
 
-      it('passes specific arguments to the transformer function', function () {
+    describe('when the option "transform" is a String', function () {
+      it('should not transform the non-provided property', function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = (value, options, context) => {
-          expect(value).to.be.eq('input');
-          expect(options).to.be.undefined;
-          expect(context).to.be.eql({
-            transformerName: 'myTransformer',
-            modelName: 'model',
-            propName: 'foo',
-          });
-          return 'transformed';
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('Should not to be called.');
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
@@ -100,41 +117,51 @@ describe('ModelDataTransformer', function () {
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 'input'});
-        expect(res).to.be.eql({foo: 'transformed'});
+        const res = T.transform('model', {});
+        expect(res).to.be.eql({});
+        expect(calls).to.be.eq(0);
       });
 
-      it('does not transform a property value if it is not provided', function () {
+      it('should not transform non-provided properties when the option "isPartial" is true', function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return String(value);
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
-              type: DataType.STRING,
+              type: DataType.ANY,
+              transform: 'myTransformer',
+            },
+            bar: {
+              type: DataType.ANY,
               transform: 'myTransformer',
             },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {});
+        const res = T.transform('model', {}, true);
         expect(res).to.be.eql({});
+        expect(calls).to.be.eq(0);
       });
 
-      it('does not transform undefined and null values', function () {
+      it('should not transform undefined and null values', function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('Should not to be called.');
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
-              type: DataType.STRING,
+              type: DataType.ANY,
               transform: 'myTransformer',
             },
           },
@@ -144,70 +171,64 @@ describe('ModelDataTransformer', function () {
         const res2 = T.transform('model', {foo: null});
         expect(res1).to.be.eql({foo: undefined});
         expect(res2).to.be.eql({foo: null});
+        expect(calls).to.be.eq(0);
       });
 
-      it('the parameter "isPartial" prevents to transform values of not provided properties', function () {
+      it('should not transform the empty value', function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('Should not to be called.');
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
-              type: DataType.STRING,
+              type: DataType.ANY,
               transform: 'myTransformer',
             },
           },
         });
+        dbs.getService(EmptyValuesService).setEmptyValuesOf(DataType.ANY, [10]);
         const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {}, true);
-        expect(res).to.be.eql({});
+        const res = T.transform('model', {foo: 10});
+        expect(res).to.be.eql({foo: 10});
+        expect(calls).to.be.eq(0);
       });
 
-      it('transforms the property value by its asynchronous transformer', async function () {
+      it('should transform the property by the transformer', function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer1 = (value, options) => {
-          expect(options).to.be.undefined;
-          return Promise.resolve(`${value}2`);
-        };
-        const myTransformer2 = (value, options) => {
-          expect(options).to.be.undefined;
-          return Promise.resolve(`${value}3`);
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer1', myTransformer1)
-          .addTransformer('myTransformer2', myTransformer2);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return String(value);
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
-              type: DataType.STRING,
-              transform: 'myTransformer1',
-            },
-            bar: {
-              type: DataType.STRING,
-              transform: 'myTransformer2',
+              type: DataType.ANY,
+              transform: 'myTransformer',
             },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const promise = T.transform('model', {foo: '1', bar: '2'});
-        expect(promise).to.be.instanceof(Promise);
-        const res = await promise;
-        expect(res).to.be.eql({foo: '12', bar: '23'});
+        const res = T.transform('model', {foo: 10});
+        expect(res).to.be.eql({foo: '10'});
+        expect(calls).to.be.eq(1);
       });
-    });
 
-    describe('the option "transform" with an array value', function () {
-      it('transforms given properties by their transformers', function () {
+      it('should transform properties by transformers', function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = value => String(value);
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return String(value);
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
@@ -219,428 +240,2073 @@ describe('ModelDataTransformer', function () {
               type: DataType.ANY,
               transform: ['myTransformer'],
             },
-            baz: {
-              type: DataType.ANY,
-              transform: ['myTransformer'],
-            },
-          },
-        });
-        const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 1, bar: 2, baz: 3});
-        expect(res).to.be.eql({foo: '1', bar: '2', baz: '3'});
-      });
-
-      it('transforms the property value by its transformers in specified order', function () {
-        const dbs = new DatabaseSchema();
-        const order = [];
-        const myTransformer1 = value => {
-          order.push('myTransformer1');
-          return value + '1';
-        };
-        const myTransformer2 = value => {
-          order.push('myTransformer2');
-          return value + '2';
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer1', myTransformer1)
-          .addTransformer('myTransformer2', myTransformer2);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.ANY,
-              transform: ['myTransformer1', 'myTransformer2'],
-            },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 'value'});
-        expect(res).to.be.eql({foo: 'value12'});
-        expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+        const res = T.transform('model', {foo: 1, bar: 2});
+        expect(res).to.be.eql({foo: '1', bar: '2'});
+        expect(calls).to.be.eq(2);
       });
 
-      it('passes specific arguments to the transformer function', function () {
+      it('should pass arguments to the transformer', function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = (value, options, context) => {
-          expect(value).to.be.eq('input');
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', (value, options, context) => {
+          calls++;
+          expect(value).to.be.eq(10);
           expect(options).to.be.undefined;
           expect(context).to.be.eql({
             transformerName: 'myTransformer',
             modelName: 'model',
             propName: 'foo',
           });
-          return 'transformed';
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
+          return String(value);
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
               type: DataType.ANY,
-              transform: ['myTransformer'],
+              transform: 'myTransformer',
             },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 'input'});
-        expect(res).to.be.eql({foo: 'transformed'});
+        const res = T.transform('model', {foo: 10});
+        expect(res).to.be.eql({foo: '10'});
+        expect(calls).to.be.eq(1);
       });
 
-      it('does not transform a property value if it is not provided', function () {
+      it('should transform the property by the asynchronous transformer', async function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.STRING,
-              transform: ['myTransformer'],
-            },
-          },
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return Promise.resolve(String(value));
         });
-        const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {});
-        expect(res).to.be.eql({});
-      });
-
-      it('transforms undefined and null values', function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
-              type: DataType.STRING,
-              transform: ['myTransformer'],
+              type: DataType.ANY,
+              transform: 'myTransformer',
             },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const res1 = T.transform('model', {foo: undefined});
-        const res2 = T.transform('model', {foo: null});
-        expect(res1).to.be.eql({foo: undefined});
-        expect(res2).to.be.eql({foo: null});
+        const promise = T.transform('model', {foo: 10});
+        expect(promise).to.be.instanceof(Promise);
+        const res = await promise;
+        expect(res).to.be.eql({foo: '10'});
+        expect(calls).to.be.eq(1);
       });
 
-      it('the parameter "isPartial" prevents to transform values of not provided properties', function () {
+      it('should transform properties by the asynchronous transformer', async function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.STRING,
-              transform: ['myTransformer'],
-            },
-          },
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return Promise.resolve(String(value));
         });
-        const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {}, true);
-        expect(res).to.be.eql({});
-      });
-
-      it('transforms the property value by its asynchronous transformers', async function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer1 = (value, options) => {
-          expect(options).to.be.undefined;
-          return Promise.resolve(`${value}2`);
-        };
-        const myTransformer2 = (value, options) => {
-          expect(options).to.be.undefined;
-          return Promise.resolve(`${value}3`);
-        };
-        const myTransformer3 = (value, options) => {
-          expect(options).to.be.undefined;
-          return Promise.resolve(`${value}4`);
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer1', myTransformer1)
-          .addTransformer('myTransformer2', myTransformer2)
-          .addTransformer('myTransformer3', myTransformer3);
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
-              type: DataType.STRING,
-              transform: ['myTransformer1', 'myTransformer2'],
+              type: DataType.ANY,
+              transform: 'myTransformer',
             },
             bar: {
-              type: DataType.STRING,
-              transform: ['myTransformer2', 'myTransformer3'],
+              type: DataType.ANY,
+              transform: 'myTransformer',
             },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const promise = T.transform('model', {foo: '1', bar: '2'});
+        const promise = T.transform('model', {foo: 10, bar: 20});
         expect(promise).to.be.instanceof(Promise);
         const res = await promise;
-        expect(res).to.be.eql({foo: '123', bar: '234'});
+        expect(res).to.be.eql({foo: '10', bar: '20'});
+        expect(calls).to.be.eq(2);
       });
-    });
 
-    describe('the option "transform" with an object value', function () {
-      it('transforms given properties by their transformers', function () {
+      it('should throw an error from the transformer', async function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const myTransformer = value => String(value);
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('My error');
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
               type: DataType.ANY,
-              transform: {myTransformer: true},
-            },
-            bar: {
-              type: DataType.ANY,
-              transform: {myTransformer: true},
-            },
-            baz: {
-              type: DataType.ANY,
-              transform: {myTransformer: true},
+              transform: 'myTransformer',
             },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 1, bar: 2, baz: 3});
-        expect(res).to.be.eql({foo: '1', bar: '2', baz: '3'});
+        const throwable = () => T.transform('model', {foo: 10});
+        expect(throwable).to.throw('My error');
+        expect(calls).to.be.eq(1);
       });
 
-      it('transforms the property value by its transformers in specified order', function () {
+      it('should throw an error from the asynchronous transformer', async function () {
+        let calls = 0;
         const dbs = new DatabaseSchema();
-        const order = [];
-        const myTransformer1 = value => {
-          order.push('myTransformer1');
-          return value + '1';
-        };
-        const myTransformer2 = value => {
-          order.push('myTransformer2');
-          return value + '2';
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer1', myTransformer1)
-          .addTransformer('myTransformer2', myTransformer2);
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', async () => {
+          calls++;
+          throw new Error('My error');
+        });
         dbs.defineModel({
           name: 'model',
           properties: {
             foo: {
               type: DataType.ANY,
-              transform: {
-                myTransformer1: true,
-                myTransformer2: true,
-              },
+              transform: 'myTransformer',
             },
           },
         });
         const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 'value'});
-        expect(res).to.be.eql({foo: 'value12'});
-        expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+        const promise = T.transform('model', {foo: 10});
+        await expect(promise).to.rejectedWith('My error');
+        expect(calls).to.be.eq(1);
       });
+    });
 
-      it('passes specific arguments to the transformer function', function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer = (value, options, context) => {
-          expect(value).to.be.eq('input');
-          expect(options).to.be.eql({
-            option1: 'value1',
-            option2: 'value2',
+    describe('when the option "transform" is a Function', function () {
+      describe('named transformers', function () {
+        it('should not transform the non-provided property', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function () {
+            calls++;
+            throw new Error('Should not to be called.');
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+            },
           });
-          expect(context).to.be.eql({
-            transformerName: 'myTransformer',
-            modelName: 'model',
-            propName: 'foo',
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {});
+          expect(res).to.be.eql({});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform non-provided properties when the option "isPartial" is true', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function () {
+            calls++;
+            throw new Error('Should not to be called.');
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+              bar: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+            },
           });
-          return 'transformed';
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.ANY,
-              transform: {
-                myTransformer: {
-                  option1: 'value1',
-                  option2: 'value2',
-                },
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {}, true);
+          expect(res).to.be.eql({});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform undefined and null values', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function () {
+            calls++;
+            throw new Error('Should not to be called.');
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
               },
             },
-          },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res1 = T.transform('model', {foo: undefined});
+          const res2 = T.transform('model', {foo: null});
+          expect(res1).to.be.eql({foo: undefined});
+          expect(res2).to.be.eql({foo: null});
+          expect(calls).to.be.eq(0);
         });
-        const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {foo: 'input'});
-        expect(res).to.be.eql({foo: 'transformed'});
-      });
 
-      it('does not transform a property value if it is not provided', function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.STRING,
-              transform: {
-                myTransformer: true,
+        it('should not transform the empty value', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function () {
+            calls++;
+            throw new Error('Should not to be called.');
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
               },
             },
-          },
+          });
+          dbs
+            .getService(EmptyValuesService)
+            .setEmptyValuesOf(DataType.ANY, [10]);
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: 10});
+          expect(calls).to.be.eq(0);
         });
-        const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {});
-        expect(res).to.be.eql({});
-      });
 
-      it('transforms undefined and null values', function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.STRING,
-              transform: {
-                myTransformer: true,
+        it('should transform the property by the transformer', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function (value) {
+            calls++;
+            return String(value);
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
               },
             },
-          },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
         });
-        const T = dbs.getService(ModelDataTransformer);
-        const res1 = T.transform('model', {foo: undefined});
-        const res2 = T.transform('model', {foo: null});
-        expect(res1).to.be.eql({foo: undefined});
-        expect(res2).to.be.eql({foo: null});
-      });
 
-      it('the parameter "isPartial" prevents to transform values of not provided properties', function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer = () => 'transformed';
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer', myTransformer);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.STRING,
-              transform: {
-                myTransformer: true,
+        it('should transform properties by transformers', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function (value) {
+            calls++;
+            return String(value);
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+              bar: {
+                type: DataType.ANY,
+                transform: myTransformer,
               },
             },
-          },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 1, bar: 2});
+          expect(res).to.be.eql({foo: '1', bar: '2'});
+          expect(calls).to.be.eq(2);
         });
-        const T = dbs.getService(ModelDataTransformer);
-        const res = T.transform('model', {}, true);
-        expect(res).to.be.eql({});
-      });
 
-      it('transforms the property value by its asynchronous transformers', async function () {
-        const dbs = new DatabaseSchema();
-        const myTransformer1 = (value, options) => {
-          expect(options).to.be.eq('foo');
-          return Promise.resolve(`${value}2`);
-        };
-        const myTransformer2 = (value, options) => {
-          expect(options).to.be.eq('bar');
-          return Promise.resolve(`${value}3`);
-        };
-        const myTransformer3 = (value, options) => {
-          expect(options).to.be.eq('baz');
-          return Promise.resolve(`${value}4`);
-        };
-        dbs
-          .getService(PropertyTransformerRegistry)
-          .addTransformer('myTransformer1', myTransformer1)
-          .addTransformer('myTransformer2', myTransformer2)
-          .addTransformer('myTransformer3', myTransformer3);
-        dbs.defineModel({
-          name: 'model',
-          properties: {
-            foo: {
-              type: DataType.STRING,
-              transform: {
-                myTransformer1: 'foo',
-                myTransformer2: 'bar',
+        it('should pass arguments to the transformer', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function (value, options, context) {
+            calls++;
+            expect(value).to.be.eq(10);
+            expect(options).to.be.undefined;
+            expect(context).to.be.eql({
+              transformerName: 'myTransformer',
+              modelName: 'model',
+              propName: 'foo',
+            });
+            return String(value);
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
               },
             },
-            bar: {
-              type: DataType.STRING,
-              transform: {
-                myTransformer2: 'bar',
-                myTransformer3: 'baz',
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform the property by the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function (value) {
+            calls++;
+            return Promise.resolve(String(value));
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
               },
             },
-          },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10});
+          expect(promise).to.be.instanceof(Promise);
+          const res = await promise;
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
         });
-        const T = dbs.getService(ModelDataTransformer);
-        const promise = T.transform('model', {foo: '1', bar: '2'});
-        expect(promise).to.be.instanceof(Promise);
-        const res = await promise;
-        expect(res).to.be.eql({foo: '123', bar: '234'});
-      });
-    });
 
-    it('the option "transform" requires a non-empty String, an Array or an Object', function () {
-      const dbs = new DatabaseSchema();
-      dbs
-        .getService(PropertyTransformerRegistry)
-        .addTransformer('myTransformer', () => 'transformed');
-      dbs.defineModel({
-        name: 'model',
-        properties: {
-          foo: {
-            type: DataType.ANY,
-            transform: undefined,
+        it('should transform properties by the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function (value) {
+            calls++;
+            return Promise.resolve(String(value));
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+              bar: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10, bar: 20});
+          expect(promise).to.be.instanceof(Promise);
+          const res = await promise;
+          expect(res).to.be.eql({foo: '10', bar: '20'});
+          expect(calls).to.be.eq(2);
+        });
+
+        it('should throw an error from the transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = function () {
+            calls++;
+            throw new Error('My error');
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const throwable = () => T.transform('model', {foo: 10});
+          expect(throwable).to.throw('My error');
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should throw an error from the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const myTransformer = async function () {
+            calls++;
+            throw new Error('My error');
+          };
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: myTransformer,
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10});
+          await expect(promise).to.rejectedWith('My error');
+          expect(calls).to.be.eq(1);
+        });
+      });
+
+      describe('anonymous transformers', function () {
+        it('should not transform the non-provided property', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform() {
+                  calls++;
+                  throw new Error('Should not to be called.');
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {});
+          expect(res).to.be.eql({});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform non-provided properties when the option "isPartial" is true', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform() {
+                  calls++;
+                  throw new Error('Should not to be called.');
+                },
+              },
+              bar: {
+                type: DataType.ANY,
+                transform() {
+                  calls++;
+                  throw new Error('Should not to be called.');
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {}, true);
+          expect(res).to.be.eql({});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform undefined and null values', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform() {
+                  calls++;
+                  throw new Error('Should not to be called.');
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res1 = T.transform('model', {foo: undefined});
+          const res2 = T.transform('model', {foo: null});
+          expect(res1).to.be.eql({foo: undefined});
+          expect(res2).to.be.eql({foo: null});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform the empty value', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform() {
+                  calls++;
+                  throw new Error('Should not to be called.');
+                },
+              },
+            },
+          });
+          dbs
+            .getService(EmptyValuesService)
+            .setEmptyValuesOf(DataType.ANY, [10]);
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: 10});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should transform the property by the transformer', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform(value) {
+                  calls++;
+                  return String(value);
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform properties by transformers', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform(value) {
+                  calls++;
+                  return String(value);
+                },
+              },
+              bar: {
+                type: DataType.ANY,
+                transform(value) {
+                  calls++;
+                  return String(value);
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 1, bar: 2});
+          expect(res).to.be.eql({foo: '1', bar: '2'});
+          expect(calls).to.be.eq(2);
+        });
+
+        it('should pass arguments to the transformer', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform(value, options, context) {
+                  calls++;
+                  expect(value).to.be.eq(10);
+                  expect(options).to.be.undefined;
+                  expect(context).to.be.eql({
+                    transformerName: undefined,
+                    modelName: 'model',
+                    propName: 'foo',
+                  });
+                  return String(value);
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform the property by the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform(value) {
+                  calls++;
+                  return Promise.resolve(String(value));
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10});
+          expect(promise).to.be.instanceof(Promise);
+          const res = await promise;
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform properties by the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform(value) {
+                  calls++;
+                  return Promise.resolve(String(value));
+                },
+              },
+              bar: {
+                type: DataType.ANY,
+                transform(value) {
+                  calls++;
+                  return Promise.resolve(String(value));
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10, bar: 20});
+          expect(promise).to.be.instanceof(Promise);
+          const res = await promise;
+          expect(res).to.be.eql({foo: '10', bar: '20'});
+          expect(calls).to.be.eq(2);
+        });
+
+        it('should throw an error from the transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform() {
+                  calls++;
+                  throw new Error('My error');
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const throwable = () => T.transform('model', {foo: 10});
+          expect(throwable).to.throw('My error');
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should throw an error from the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                async transform() {
+                  calls++;
+                  throw new Error('My error');
+                },
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10});
+          await expect(promise).to.rejectedWith('My error');
+          expect(calls).to.be.eq(1);
+        });
+      });
+    });
+
+    describe('when the option "transform" is an Array', function () {
+      describe('when an Array element is a String', function () {
+        it('should not transform the non-provided property', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', () => {
+            calls++;
+            throw new Error('Should not to be called.');
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {});
+          expect(res).to.be.eql({});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform non-provided properties when the option "isPartial" is true', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', () => {
+            calls++;
+            throw new Error('Should not to be called.');
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+              bar: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {}, true);
+          expect(res).to.be.eql({});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform undefined and null values', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', () => {
+            calls++;
+            throw new Error('Should not to be called.');
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res1 = T.transform('model', {foo: undefined});
+          const res2 = T.transform('model', {foo: null});
+          expect(res1).to.be.eql({foo: undefined});
+          expect(res2).to.be.eql({foo: null});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should not transform the empty value', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', () => {
+            calls++;
+            throw new Error('Should not to be called.');
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          dbs
+            .getService(EmptyValuesService)
+            .setEmptyValuesOf(DataType.ANY, [10]);
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: 10});
+          expect(calls).to.be.eq(0);
+        });
+
+        it('should transform the property by the transformer', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', value => {
+            calls++;
+            return String(value);
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform properties by transformers', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', value => {
+            calls++;
+            return String(value);
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+              bar: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 1, bar: 2});
+          expect(res).to.be.eql({foo: '1', bar: '2'});
+          expect(calls).to.be.eq(2);
+        });
+
+        it('should pass arguments to the transformer', function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', (value, options, context) => {
+            calls++;
+            expect(value).to.be.eq(10);
+            expect(options).to.be.undefined;
+            expect(context).to.be.eql({
+              transformerName: 'myTransformer',
+              modelName: 'model',
+              propName: 'foo',
+            });
+            return String(value);
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: 10});
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform the property by the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', value => {
+            calls++;
+            return Promise.resolve(String(value));
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10});
+          expect(promise).to.be.instanceof(Promise);
+          const res = await promise;
+          expect(res).to.be.eql({foo: '10'});
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform properties by the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', value => {
+            calls++;
+            return Promise.resolve(String(value));
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+              bar: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10, bar: 20});
+          expect(promise).to.be.instanceof(Promise);
+          const res = await promise;
+          expect(res).to.be.eql({foo: '10', bar: '20'});
+          expect(calls).to.be.eq(2);
+        });
+
+        it('should throw an error from the transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', () => {
+            calls++;
+            throw new Error('My error');
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const throwable = () => T.transform('model', {foo: 10});
+          expect(throwable).to.throw('My error');
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should throw an error from the asynchronous transformer', async function () {
+          let calls = 0;
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer', async () => {
+            calls++;
+            throw new Error('My error');
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const promise = T.transform('model', {foo: 10});
+          await expect(promise).to.rejectedWith('My error');
+          expect(calls).to.be.eq(1);
+        });
+
+        it('should transform the property by transformers with the correct order', function () {
+          const order = [];
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer1', value => {
+            order.push('myTransformer1');
+            return String(value);
+          });
+          reg.addTransformer('myTransformer2', value => {
+            order.push('myTransformer2');
+            return value.toUpperCase();
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer1', 'myTransformer2'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = T.transform('model', {foo: true});
+          expect(res).to.be.eql({foo: 'TRUE'});
+          expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+        });
+
+        it('should transform the property by asynchronous transformers with the correct order', async function () {
+          const order = [];
+          const dbs = new DatabaseSchema();
+          const reg = dbs.getService(PropertyTransformerRegistry);
+          reg.addTransformer('myTransformer1', async value => {
+            order.push('myTransformer1');
+            return String(value);
+          });
+          reg.addTransformer('myTransformer2', async value => {
+            order.push('myTransformer2');
+            return value.toUpperCase();
+          });
+          dbs.defineModel({
+            name: 'model',
+            properties: {
+              foo: {
+                type: DataType.ANY,
+                transform: ['myTransformer1', 'myTransformer2'],
+              },
+            },
+          });
+          const T = dbs.getService(ModelDataTransformer);
+          const res = await T.transform('model', {foo: true});
+          expect(res).to.be.eql({foo: 'TRUE'});
+          expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+        });
+      });
+
+      describe('when an Array element is a Function', function () {
+        describe('named transformers', function () {
+          it('should not transform the non-provided property', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function () {
+              calls++;
+              throw new Error('Should not to be called.');
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {});
+            expect(res).to.be.eql({});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should not transform non-provided properties when the option "isPartial" is true', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function () {
+              calls++;
+              throw new Error('Should not to be called.');
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+                bar: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {}, true);
+            expect(res).to.be.eql({});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should not transform undefined and null values', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function () {
+              calls++;
+              throw new Error('Should not to be called.');
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res1 = T.transform('model', {foo: undefined});
+            const res2 = T.transform('model', {foo: null});
+            expect(res1).to.be.eql({foo: undefined});
+            expect(res2).to.be.eql({foo: null});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should not transform the empty value', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function () {
+              calls++;
+              throw new Error('Should not to be called.');
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            dbs
+              .getService(EmptyValuesService)
+              .setEmptyValuesOf(DataType.ANY, [10]);
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 10});
+            expect(res).to.be.eql({foo: 10});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should transform the property by the transformer', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function (value) {
+              calls++;
+              return String(value);
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 10});
+            expect(res).to.be.eql({foo: '10'});
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform properties by transformers', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function (value) {
+              calls++;
+              return String(value);
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+                bar: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 1, bar: 2});
+            expect(res).to.be.eql({foo: '1', bar: '2'});
+            expect(calls).to.be.eq(2);
+          });
+
+          it('should pass arguments to the transformer', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function (value, options, context) {
+              calls++;
+              expect(value).to.be.eq(10);
+              expect(options).to.be.undefined;
+              expect(context).to.be.eql({
+                transformerName: 'myTransformer',
+                modelName: 'model',
+                propName: 'foo',
+              });
+              return String(value);
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 10});
+            expect(res).to.be.eql({foo: '10'});
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform the property by the asynchronous transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function (value) {
+              calls++;
+              return Promise.resolve(String(value));
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const promise = T.transform('model', {foo: 10});
+            expect(promise).to.be.instanceof(Promise);
+            const res = await promise;
+            expect(res).to.be.eql({foo: '10'});
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform properties by the asynchronous transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function (value) {
+              calls++;
+              return Promise.resolve(String(value));
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+                bar: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const promise = T.transform('model', {foo: 10, bar: 20});
+            expect(promise).to.be.instanceof(Promise);
+            const res = await promise;
+            expect(res).to.be.eql({foo: '10', bar: '20'});
+            expect(calls).to.be.eq(2);
+          });
+
+          it('should throw an error from the transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = function () {
+              calls++;
+              throw new Error('My error');
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const throwable = () => T.transform('model', {foo: 10});
+            expect(throwable).to.throw('My error');
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should throw an error from the asynchronous transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            const myTransformer = async function () {
+              calls++;
+              throw new Error('My error');
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const promise = T.transform('model', {foo: 10});
+            await expect(promise).to.rejectedWith('My error');
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform the property by transformers with the correct order', function () {
+            const order = [];
+            const dbs = new DatabaseSchema();
+            const myTransformer1 = function (value) {
+              order.push('myTransformer1');
+              return String(value);
+            };
+            const myTransformer2 = function (value) {
+              order.push('myTransformer2');
+              return value.toUpperCase();
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer1, myTransformer2],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: true});
+            expect(res).to.be.eql({foo: 'TRUE'});
+            expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+          });
+
+          it('should transform the property by asynchronous transformers with the correct order', async function () {
+            const order = [];
+            const dbs = new DatabaseSchema();
+            const myTransformer1 = async function (value) {
+              order.push('myTransformer1');
+              return String(value);
+            };
+            const myTransformer2 = async function (value) {
+              order.push('myTransformer2');
+              return value.toUpperCase();
+            };
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [myTransformer1, myTransformer2],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = await T.transform('model', {foo: true});
+            expect(res).to.be.eql({foo: 'TRUE'});
+            expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+          });
+        });
+
+        describe('anonymous transformers', function () {
+          it('should not transform the non-provided property', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    () => {
+                      calls++;
+                      throw new Error('Should not to be called.');
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {});
+            expect(res).to.be.eql({});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should not transform non-provided properties when the option "isPartial" is true', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    () => {
+                      calls++;
+                      throw new Error('Should not to be called.');
+                    },
+                  ],
+                },
+                bar: {
+                  type: DataType.ANY,
+                  transform: [
+                    () => {
+                      calls++;
+                      throw new Error('Should not to be called.');
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {}, true);
+            expect(res).to.be.eql({});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should not transform undefined and null values', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    () => {
+                      calls++;
+                      throw new Error('Should not to be called.');
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res1 = T.transform('model', {foo: undefined});
+            const res2 = T.transform('model', {foo: null});
+            expect(res1).to.be.eql({foo: undefined});
+            expect(res2).to.be.eql({foo: null});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should not transform the empty value', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    () => {
+                      calls++;
+                      throw new Error('Should not to be called.');
+                    },
+                  ],
+                },
+              },
+            });
+            dbs
+              .getService(EmptyValuesService)
+              .setEmptyValuesOf(DataType.ANY, [10]);
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 10});
+            expect(res).to.be.eql({foo: 10});
+            expect(calls).to.be.eq(0);
+          });
+
+          it('should transform the property by the transformer', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    value => {
+                      calls++;
+                      return String(value);
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 10});
+            expect(res).to.be.eql({foo: '10'});
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform properties by transformers', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    value => {
+                      calls++;
+                      return String(value);
+                    },
+                  ],
+                },
+                bar: {
+                  type: DataType.ANY,
+                  transform: [
+                    value => {
+                      calls++;
+                      return String(value);
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 1, bar: 2});
+            expect(res).to.be.eql({foo: '1', bar: '2'});
+            expect(calls).to.be.eq(2);
+          });
+
+          it('should pass arguments to the transformer', function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    (value, options, context) => {
+                      calls++;
+                      expect(value).to.be.eq(10);
+                      expect(options).to.be.undefined;
+                      expect(context).to.be.eql({
+                        transformerName: undefined,
+                        modelName: 'model',
+                        propName: 'foo',
+                      });
+                      return String(value);
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: 10});
+            expect(res).to.be.eql({foo: '10'});
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform the property by the asynchronous transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    value => {
+                      calls++;
+                      return Promise.resolve(String(value));
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const promise = T.transform('model', {foo: 10});
+            expect(promise).to.be.instanceof(Promise);
+            const res = await promise;
+            expect(res).to.be.eql({foo: '10'});
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform properties by the asynchronous transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    value => {
+                      calls++;
+                      return Promise.resolve(String(value));
+                    },
+                  ],
+                },
+                bar: {
+                  type: DataType.ANY,
+                  transform: [
+                    value => {
+                      calls++;
+                      return Promise.resolve(String(value));
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const promise = T.transform('model', {foo: 10, bar: 20});
+            expect(promise).to.be.instanceof(Promise);
+            const res = await promise;
+            expect(res).to.be.eql({foo: '10', bar: '20'});
+            expect(calls).to.be.eq(2);
+          });
+
+          it('should throw an error from the transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    () => {
+                      calls++;
+                      throw new Error('My error');
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const throwable = () => T.transform('model', {foo: 10});
+            expect(throwable).to.throw('My error');
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should throw an error from the asynchronous transformer', async function () {
+            let calls = 0;
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    async () => {
+                      calls++;
+                      throw new Error('My error');
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const promise = T.transform('model', {foo: 10});
+            await expect(promise).to.rejectedWith('My error');
+            expect(calls).to.be.eq(1);
+          });
+
+          it('should transform the property by transformers with the correct order', function () {
+            const order = [];
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    value => {
+                      order.push('myTransformer1');
+                      return String(value);
+                    },
+                    value => {
+                      order.push('myTransformer2');
+                      return value.toUpperCase();
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = T.transform('model', {foo: true});
+            expect(res).to.be.eql({foo: 'TRUE'});
+            expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+          });
+
+          it('should transform the property by asynchronous transformers with the correct order', async function () {
+            const order = [];
+            const dbs = new DatabaseSchema();
+            dbs.defineModel({
+              name: 'model',
+              properties: {
+                foo: {
+                  type: DataType.ANY,
+                  transform: [
+                    async value => {
+                      order.push('myTransformer1');
+                      return String(value);
+                    },
+                    async value => {
+                      order.push('myTransformer2');
+                      return value.toUpperCase();
+                    },
+                  ],
+                },
+              },
+            });
+            const T = dbs.getService(ModelDataTransformer);
+            const res = await T.transform('model', {foo: true});
+            expect(res).to.be.eql({foo: 'TRUE'});
+            expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+          });
+        });
+      });
+    });
+
+    describe('when the option "transform" is an Object', function () {
+      it('should not transform the non-provided property', function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('Should not to be called.');
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res = T.transform('model', {});
+        expect(res).to.be.eql({});
+        expect(calls).to.be.eq(0);
+      });
+
+      it('should not transform non-provided properties when the option "isPartial" is true', function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('Should not to be called.');
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+            bar: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
           },
-        },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res = T.transform('model', {}, true);
+        expect(res).to.be.eql({});
+        expect(calls).to.be.eq(0);
+      });
+
+      it('should not transform undefined and null values', function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('Should not to be called.');
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res1 = T.transform('model', {foo: undefined});
+        const res2 = T.transform('model', {foo: null});
+        expect(res1).to.be.eql({foo: undefined});
+        expect(res2).to.be.eql({foo: null});
+        expect(calls).to.be.eq(0);
+      });
+
+      it('should not transform the empty value', function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('Should not to be called.');
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        dbs.getService(EmptyValuesService).setEmptyValuesOf(DataType.ANY, [10]);
+        const T = dbs.getService(ModelDataTransformer);
+        const res = T.transform('model', {foo: 10});
+        expect(res).to.be.eql({foo: 10});
+        expect(calls).to.be.eq(0);
+      });
+
+      it('should transform the property by the transformer', function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return String(value);
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res = T.transform('model', {foo: 10});
+        expect(res).to.be.eql({foo: '10'});
+        expect(calls).to.be.eq(1);
+      });
+
+      it('should transform properties by transformers', function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return String(value);
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+            bar: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res = T.transform('model', {foo: 1, bar: 2});
+        expect(res).to.be.eql({foo: '1', bar: '2'});
+        expect(calls).to.be.eq(2);
+      });
+
+      it('should pass arguments to the transformer', function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', (value, options, context) => {
+          calls++;
+          expect(value).to.be.eq(10);
+          expect(options).to.be.eq('test');
+          expect(context).to.be.eql({
+            transformerName: 'myTransformer',
+            modelName: 'model',
+            propName: 'foo',
+          });
+          return String(value);
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: 'test',
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res = T.transform('model', {foo: 10});
+        expect(res).to.be.eql({foo: '10'});
+        expect(calls).to.be.eq(1);
+      });
+
+      it('should transform the property by the asynchronous transformer', async function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return Promise.resolve(String(value));
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const promise = T.transform('model', {foo: 10});
+        expect(promise).to.be.instanceof(Promise);
+        const res = await promise;
+        expect(res).to.be.eql({foo: '10'});
+        expect(calls).to.be.eq(1);
+      });
+
+      it('should transform properties by the asynchronous transformer', async function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', value => {
+          calls++;
+          return Promise.resolve(String(value));
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+            bar: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const promise = T.transform('model', {foo: 10, bar: 20});
+        expect(promise).to.be.instanceof(Promise);
+        const res = await promise;
+        expect(res).to.be.eql({foo: '10', bar: '20'});
+        expect(calls).to.be.eq(2);
+      });
+
+      it('should throw an error from the transformer', async function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', () => {
+          calls++;
+          throw new Error('My error');
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const throwable = () => T.transform('model', {foo: 10});
+        expect(throwable).to.throw('My error');
+        expect(calls).to.be.eq(1);
+      });
+
+      it('should throw an error from the asynchronous transformer', async function () {
+        let calls = 0;
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer', async () => {
+          calls++;
+          throw new Error('My error');
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const promise = T.transform('model', {foo: 10});
+        await expect(promise).to.rejectedWith('My error');
+        expect(calls).to.be.eq(1);
+      });
+
+      it('should transform the property by transformers with the correct order', function () {
+        const order = [];
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer1', value => {
+          order.push('myTransformer1');
+          return String(value);
+        });
+        reg.addTransformer('myTransformer2', value => {
+          order.push('myTransformer2');
+          return value.toUpperCase();
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer1: true,
+                myTransformer2: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res = T.transform('model', {foo: true});
+        expect(res).to.be.eql({foo: 'TRUE'});
+        expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
+      });
+
+      it('should transform the property by asynchronous transformers with the correct order', async function () {
+        const order = [];
+        const dbs = new DatabaseSchema();
+        const reg = dbs.getService(PropertyTransformerRegistry);
+        reg.addTransformer('myTransformer1', async value => {
+          order.push('myTransformer1');
+          return String(value);
+        });
+        reg.addTransformer('myTransformer2', async value => {
+          order.push('myTransformer2');
+          return value.toUpperCase();
+        });
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.ANY,
+              transform: {
+                myTransformer1: true,
+                myTransformer2: true,
+              },
+            },
+          },
+        });
+        const T = dbs.getService(ModelDataTransformer);
+        const res = await T.transform('model', {foo: true});
+        expect(res).to.be.eql({foo: 'TRUE'});
+        expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
       });
-      const T = dbs.getService(ModelDataTransformer);
-      const throwable = v => () => {
-        const models = dbs.getService(DefinitionRegistry)['_models'];
-        models.model.properties.foo.transform = v;
-        T.transform('model', {foo: 'bar'});
-      };
-      const error = v =>
-        format(
-          'The provided option "transform" of the property "foo" in the model "model" ' +
-            'should be a non-empty String, an Array of String or an Object, ' +
-            'but %s given.',
-          v,
-        );
-      expect(throwable('')).to.throw(error('""'));
-      expect(throwable(10)).to.throw(error('10'));
-      expect(throwable(0)).to.throw(error('0'));
-      expect(throwable(true)).to.throw(error('true'));
-      expect(throwable(false)).to.throw(error('false'));
-      throwable('myTransformer')();
-      throwable(['myTransformer'])();
-      throwable([])();
-      throwable({myTransformer: true})();
-      throwable({})();
     });
   });
 });

+ 125 - 37
src/definition/model/model-data-validator.js

@@ -22,7 +22,7 @@ export class ModelDataValidator extends Service {
   validate(modelName, modelData, isPartial = false) {
     if (!isPureObject(modelData))
       throw new InvalidArgumentError(
-        'The data of the model %v should be an Object, but %v given.',
+        'The data of the model %v should be an Object, but %v was given.',
         modelName,
         modelData,
       );
@@ -70,22 +70,21 @@ export class ModelDataValidator extends Service {
       // a required property should
       // not have an empty value
       throw new InvalidArgumentError(
-        'The property %v of the model %v is required, but %v given.',
+        'The property %v of the model %v is required, but %v was given.',
         propName,
         modelName,
         propValue,
       );
     }
-    // passes the property value
-    // through property validators
+    // проверка соответствия типа значения
+    this._validateValueByPropertyType(modelName, propName, propDef, propValue);
+    // проверка значения валидаторами
     this._validateValueByPropertyValidators(
       modelName,
       propName,
       propDef,
       propValue,
     );
-    // type checking of the property value
-    this._validateValueByPropertyType(modelName, propName, propDef, propValue);
   }
 
   /**
@@ -118,8 +117,8 @@ export class ModelDataValidator extends Service {
 
     const createError = expected => {
       const pattern = isArrayValue
-        ? 'The array property %v of the model %v must have %s element, but %s given.'
-        : 'The property %v of the model %v must have %s, but %s given.';
+        ? 'The array property %v of the model %v must have %s element, but %s was given.'
+        : 'The property %v of the model %v must have %s, but %s was given.';
       const ctorName = getCtorName(propValue);
       const givenStr = ctorName ?? typeof propValue;
       return new InvalidArgumentError(
@@ -182,45 +181,134 @@ export class ModelDataValidator extends Service {
     if (typeof propDef === 'string' || propDef.validate == null) return;
     const validateDef = propDef.validate;
     const validatorRegistry = this.getService(PropertyValidatorRegistry);
-    const createError = validatorName =>
-      new InvalidArgumentError(
-        'The property %v of the model %v has the invalid value %v ' +
-          'that caught by the validator %v.',
-        propName,
-        modelName,
-        propValue,
-        validatorName,
-      );
-    const validateBy = (validatorName, validatorOptions = undefined) => {
-      const validator = validatorRegistry.getValidator(validatorName);
-      const context = {validatorName, modelName, propName};
-      const valid = validator(propValue, validatorOptions, context);
-      if (valid instanceof Promise) {
-        throw new InvalidArgumentError(
-          'Asynchronous property validators are not supported, ' +
-            'but the property validator %v returns a Promise.',
+    const createError = validatorName => {
+      if (validatorName) {
+        return new InvalidArgumentError(
+          'The property %v of the model %v has the invalid value %v ' +
+            'that caught by the property validator %v.',
+          propName,
+          modelName,
+          propValue,
           validatorName,
         );
-      } else if (valid !== true) {
+      } else {
+        return new InvalidArgumentError(
+          'The property %v of the model %v has the invalid value %v ' +
+            'that caught by a property validator.',
+          propName,
+          modelName,
+          propValue,
+        );
+      }
+    };
+    // объявление функции для проверки значения
+    // с помощью указанного валидатора
+    const validateBy = (validatorOrName, validatorOptions = undefined) => {
+      let validatorName, validatorFn;
+      // если первый аргумент является строкой, то строка
+      // воспринимается как название зарегистрированного
+      // валидатора
+      if (typeof validatorOrName === 'string') {
+        validatorName = validatorOrName;
+        validatorFn = validatorRegistry.getValidator(validatorName);
+      }
+      // если первый аргумент является функцией,
+      // то функция воспринимается как валидатор
+      else if (typeof validatorOrName === 'function') {
+        validatorName =
+          validatorOrName.name && validatorOrName.name !== 'validate'
+            ? validatorOrName.name
+            : undefined;
+        validatorFn = validatorOrName;
+      }
+      // если первый аргумент не является строкой
+      // и функцией, то выбрасывается ошибка
+      else {
+        throw new InvalidArgumentError(
+          'Validator must be a non-empty String or ' +
+            'a Function, but %v was given.',
+          validatorOrName,
+        );
+      }
+      const context = {validatorName, modelName, propName};
+      const valid = validatorFn(propValue, validatorOptions, context);
+      // если валидатор возвращает Promise,
+      // то выбрасывается ошибка
+      if (valid instanceof Promise) {
+        if (validatorName) {
+          throw new InvalidArgumentError(
+            'Asynchronous property validators are not supported, ' +
+              'but the property %v of the model %v has the property ' +
+              'validator %v that returns a Promise.',
+            propName,
+            modelName,
+            validatorName,
+          );
+        } else {
+          throw new InvalidArgumentError(
+            'Asynchronous property validators are not supported, ' +
+              'but the property %v of the model %v has a property ' +
+              'validator that returns a Promise.',
+            propName,
+            modelName,
+          );
+        }
+      }
+      // если валидатор вернул значение отличное
+      // от true, то выбрасывается ошибка
+      else if (valid !== true) {
         throw createError(validatorName);
       }
     };
+    // если значением опции "validate" является строка,
+    // то строка воспринимается как название валидатора
     if (validateDef && typeof validateDef === 'string') {
       validateBy(validateDef);
-    } else if (Array.isArray(validateDef)) {
-      validateDef.forEach(validatorName => validateBy(validatorName));
-    } else if (validateDef !== null && typeof validateDef === 'object') {
-      Object.keys(validateDef).forEach(validatorName => {
-        if (Object.prototype.hasOwnProperty.call(validateDef, validatorName)) {
-          const validatorOptions = validateDef[validatorName];
-          validateBy(validatorName, validatorOptions);
+    }
+    // если значение опции "validate" является функция,
+    // то функция воспринимается как валидатор
+    else if (validateDef && typeof validateDef === 'function') {
+      validateBy(validateDef);
+    }
+    // если значение опции "validate" является массив, то каждый
+    // элемент массива воспринимается как название валидатора
+    // или функция-валидатор
+    else if (Array.isArray(validateDef)) {
+      validateDef.forEach(validatorOrName => {
+        if (
+          !validatorOrName ||
+          (typeof validatorOrName !== 'string' &&
+            typeof validatorOrName !== 'function')
+        ) {
+          throw new InvalidArgumentError(
+            'The provided option "validate" for the property %v in the model %v ' +
+              'has an Array value that should contain validator names or validator ' +
+              'functions, but %v was given.',
+            propName,
+            modelName,
+            validatorOrName,
+          );
         }
+        validateBy(validatorOrName);
       });
-    } else {
+    }
+    // если значение опции "validate" является объектом,
+    // то ключи объекта воспринимаются как названия валидаторов,
+    // а их значения аргументами
+    else if (validateDef !== null && typeof validateDef === 'object') {
+      Object.keys(validateDef).forEach(validatorName => {
+        const validatorOptions = validateDef[validatorName];
+        validateBy(validatorName, validatorOptions);
+      });
+    }
+    // если значение опции "validate" не является строкой,
+    // функцией и массивом, то выбрасывается ошибка
+    else {
       throw new InvalidArgumentError(
-        'The provided option "validate" of the property %v in the model %v ' +
-          'should be a non-empty String, an Array of String or an Object, ' +
-          'but %v given.',
+        'The provided option "validate" for the property %v in the model %v ' +
+          'should be either a validator name, a validator function, an array ' +
+          'of validator names or functions, or an object mapping validator ' +
+          'names to their arguments, but %v was given.',
         propName,
         modelName,
         validateDef,

Разница между файлами не показана из-за своего большого размера
+ 271 - 236
src/definition/model/model-data-validator.spec.js


+ 5 - 5
src/definition/model/model-definition-utils.js

@@ -317,7 +317,7 @@ export class ModelDefinitionUtils extends Service {
     ) {
       throw new InvalidArgumentError(
         'The argument "propDef" of the ModelDefinitionUtils.getDataTypeFromPropertyDefinition ' +
-          'should be an Object or the DataType enum, but %v given.',
+          'should be an Object or the DataType enum, but %v was given.',
         propDef,
       );
     }
@@ -326,7 +326,7 @@ export class ModelDefinitionUtils extends Service {
     if (!Object.values(DataType).includes(dataType))
       throw new InvalidArgumentError(
         'The given Object to the ModelDefinitionUtils.getDataTypeFromPropertyDefinition ' +
-          'should have the "type" property with one of values: %l, but %v given.',
+          'should have the "type" property with one of values: %l, but %v was given.',
         Object.values(DataType),
         propDef.type,
       );
@@ -467,7 +467,7 @@ export class ModelDefinitionUtils extends Service {
     if (!modelData || typeof modelData !== 'object' || Array.isArray(modelData))
       throw new InvalidArgumentError(
         'The second argument of ModelDefinitionUtils.excludeObjectKeysByRelationNames ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         modelData,
       );
     const relDefs = this.getRelationsDefinitionInBaseModelHierarchy(modelName);
@@ -487,14 +487,14 @@ export class ModelDefinitionUtils extends Service {
       throw new InvalidArgumentError(
         'Parameter "modelName" of ' +
           'ModelDefinitionUtils.getModelNameOfPropertyValueIfDefined ' +
-          'requires a non-empty String, but %v given.',
+          'requires a non-empty String, but %v was given.',
         modelName,
       );
     if (!propertyName || typeof propertyName !== 'string')
       throw new InvalidArgumentError(
         'Parameter "propertyName" of ' +
           'ModelDefinitionUtils.getModelNameOfPropertyValueIfDefined ' +
-          'requires a non-empty String, but %v given.',
+          'requires a non-empty String, but %v was given.',
         propertyName,
       );
     // если определение свойства не найдено,

+ 7 - 7
src/definition/model/model-definition-utils.spec.js

@@ -1224,7 +1224,7 @@ describe('ModelDefinitionUtils', function () {
       const error = v =>
         format(
           'The argument "propDef" of the ModelDefinitionUtils.getDataTypeFromPropertyDefinition ' +
-            'should be an Object or the DataType enum, but %s given.',
+            'should be an Object or the DataType enum, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -1247,7 +1247,7 @@ describe('ModelDefinitionUtils', function () {
       const error = v =>
         format(
           'The given Object to the ModelDefinitionUtils.getDataTypeFromPropertyDefinition ' +
-            'should have the "type" property with one of values: %l, but %s given.',
+            'should have the "type" property with one of values: %l, but %s was given.',
           Object.values(DataType),
           v,
         );
@@ -1927,7 +1927,7 @@ describe('ModelDefinitionUtils', function () {
       const error = v =>
         format(
           'The second argument of ModelDefinitionUtils.excludeObjectKeysByRelationNames ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       expect(throwable('')).to.throw(error('""'));
@@ -1968,7 +1968,7 @@ describe('ModelDefinitionUtils', function () {
           format(
             'Parameter "modelName" of ' +
               'ModelDefinitionUtils.getModelNameOfPropertyValueIfDefined ' +
-              'requires a non-empty String, but %s given.',
+              'requires a non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));
@@ -1997,7 +1997,7 @@ describe('ModelDefinitionUtils', function () {
           format(
             'Parameter "propertyName" of ' +
               'ModelDefinitionUtils.getModelNameOfPropertyValueIfDefined ' +
-              'requires a non-empty String, but %s given.',
+              'requires a non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));
@@ -2050,7 +2050,7 @@ describe('ModelDefinitionUtils', function () {
           format(
             'Parameter "modelName" of ' +
               'ModelDefinitionUtils.getModelNameOfPropertyValueIfDefined ' +
-              'requires a non-empty String, but %s given.',
+              'requires a non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));
@@ -2081,7 +2081,7 @@ describe('ModelDefinitionUtils', function () {
           format(
             'Parameter "propertyName" of ' +
               'ModelDefinitionUtils.getModelNameOfPropertyValueIfDefined ' +
-              'requires a non-empty String, but %s given.',
+              'requires a non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));

+ 7 - 7
src/definition/model/model-definition-validator.js

@@ -15,33 +15,33 @@ export class ModelDefinitionValidator extends Service {
   validate(modelDef) {
     if (!modelDef || typeof modelDef !== 'object' || Array.isArray(modelDef))
       throw new InvalidArgumentError(
-        'The model definition should be an Object, but %v given.',
+        'The model definition should be an Object, but %v was given.',
         modelDef,
       );
     if (!modelDef.name || typeof modelDef.name !== 'string')
       throw new InvalidArgumentError(
         'The model definition requires the option "name" ' +
-          'as a non-empty String, but %v given.',
+          'as a non-empty String, but %v was given.',
         modelDef.name,
       );
     if (modelDef.datasource && typeof modelDef.datasource !== 'string')
       throw new InvalidArgumentError(
         'The provided option "datasource" of the model %v ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         modelDef.name,
         modelDef.datasource,
       );
     if (modelDef.base && typeof modelDef.base !== 'string')
       throw new InvalidArgumentError(
         'The provided option "base" of the model %v ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         modelDef.name,
         modelDef.base,
       );
     if (modelDef.tableName && typeof modelDef.tableName !== 'string')
       throw new InvalidArgumentError(
         'The provided option "tableName" of the model %v ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         modelDef.name,
         modelDef.tableName,
       );
@@ -52,7 +52,7 @@ export class ModelDefinitionValidator extends Service {
       ) {
         throw new InvalidArgumentError(
           'The provided option "properties" of the model %v ' +
-            'should be an Object, but %v given.',
+            'should be an Object, but %v was given.',
           modelDef.name,
           modelDef.properties,
         );
@@ -69,7 +69,7 @@ export class ModelDefinitionValidator extends Service {
       ) {
         throw new InvalidArgumentError(
           'The provided option "relations" of the model %v ' +
-            'should be an Object, but %v given.',
+            'should be an Object, but %v was given.',
           modelDef.name,
           modelDef.relations,
         );

+ 10 - 7
src/definition/model/model-definition-validator.spec.js

@@ -17,7 +17,10 @@ describe('ModelDefinitionValidator', function () {
     it('requires the given definition to be an object', function () {
       const validate = v => () => S.validate(v);
       const error = v =>
-        format('The model definition should be an Object, but %s given.', v);
+        format(
+          'The model definition should be an Object, but %s was given.',
+          v,
+        );
       expect(validate('str')).to.throw(error('"str"'));
       expect(validate(10)).to.throw(error('10'));
       expect(validate(true)).to.throw(error('true'));
@@ -33,7 +36,7 @@ describe('ModelDefinitionValidator', function () {
       const error = v =>
         format(
           'The model definition requires the option "name" ' +
-            'as a non-empty String, but %s given.',
+            'as a non-empty String, but %s was given.',
           v,
         );
       expect(validate('')).to.throw(error('""'));
@@ -52,7 +55,7 @@ describe('ModelDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "datasource" of the model "model" ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -67,7 +70,7 @@ describe('ModelDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "base" of the model "model" ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -82,7 +85,7 @@ describe('ModelDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "tableName" of the model "model" ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -97,7 +100,7 @@ describe('ModelDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "properties" of the model "model" ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       expect(validate('str')).to.throw(error('"str"'));
@@ -112,7 +115,7 @@ describe('ModelDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "relations" of the model "model" ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       expect(validate('str')).to.throw(error('"str"'));

+ 129 - 54
src/definition/model/properties/properties-definition-validator.js

@@ -21,13 +21,13 @@ export class PropertiesDefinitionValidator extends Service {
     if (!modelName || typeof modelName !== 'string')
       throw new InvalidArgumentError(
         'The first argument of PropertiesDefinitionValidator.validate ' +
-          'should be a non-empty String, but %v given.',
+          'should be a non-empty String, but %v was given.',
         modelName,
       );
     if (!propDefs || typeof propDefs !== 'object' || Array.isArray(propDefs)) {
       throw new InvalidArgumentError(
         'The provided option "properties" of the model %v ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         modelName,
         propDefs,
       );
@@ -54,20 +54,20 @@ export class PropertiesDefinitionValidator extends Service {
     if (!modelName || typeof modelName !== 'string')
       throw new InvalidArgumentError(
         'The first argument of PropertiesDefinitionValidator._validateProperty ' +
-          'should be a non-empty String, but %v given.',
+          'should be a non-empty String, but %v was given.',
         modelName,
       );
     if (!propName || typeof propName !== 'string')
       throw new InvalidArgumentError(
         'The property name of the model %v should be ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         modelName,
         propName,
       );
     if (!propDef)
       throw new InvalidArgumentError(
         'The property %v of the model %v should have ' +
-          'a property definition, but %v given.',
+          'a property definition, but %v was given.',
         propName,
         modelName,
         propDef,
@@ -76,7 +76,7 @@ export class PropertiesDefinitionValidator extends Service {
       if (!Object.values(Type).includes(propDef))
         throw new InvalidArgumentError(
           'In case of a short property definition, the property %v ' +
-            'of the model %v should have one of data types: %l, but %v given.',
+            'of the model %v should have one of data types: %l, but %v was given.',
           propName,
           modelName,
           Object.values(Type),
@@ -87,7 +87,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (!propDef || typeof propDef !== 'object' || Array.isArray(propDef)) {
       throw new InvalidArgumentError(
         'In case of a full property definition, the property %v ' +
-          'of the model %v should be an Object, but %v given.',
+          'of the model %v should be an Object, but %v was given.',
         propName,
         modelName,
         propDef,
@@ -96,7 +96,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (!propDef.type || !Object.values(Type).includes(propDef.type))
       throw new InvalidArgumentError(
         'The property %v of the model %v requires the option "type" ' +
-          'to have one of data types: %l, but %v given.',
+          'to have one of data types: %l, but %v was given.',
         propName,
         modelName,
         Object.values(Type),
@@ -105,7 +105,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (propDef.itemType && !Object.values(Type).includes(propDef.itemType)) {
       throw new InvalidArgumentError(
         'The provided option "itemType" of the property %v in the model %v ' +
-          'should have one of data types: %l, but %v given.',
+          'should have one of data types: %l, but %v was given.',
         propName,
         modelName,
         Object.values(Type),
@@ -115,7 +115,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (propDef.itemModel && typeof propDef.itemModel !== 'string') {
       throw new InvalidArgumentError(
         'The provided option "itemModel" of the property %v in the model %v ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         propName,
         modelName,
         propDef.itemModel,
@@ -124,7 +124,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (propDef.model && typeof propDef.model !== 'string')
       throw new InvalidArgumentError(
         'The provided option "model" of the property %v in the model %v ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         propName,
         modelName,
         propDef.model,
@@ -132,7 +132,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (propDef.primaryKey && typeof propDef.primaryKey !== 'boolean')
       throw new InvalidArgumentError(
         'The provided option "primaryKey" of the property %v in the model %v ' +
-          'should be a Boolean, but %v given.',
+          'should be a Boolean, but %v was given.',
         propName,
         modelName,
         propDef.primaryKey,
@@ -140,7 +140,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (propDef.columnName && typeof propDef.columnName !== 'string')
       throw new InvalidArgumentError(
         'The provided option "columnName" of the property %v in the model %v ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         propName,
         modelName,
         propDef.columnName,
@@ -148,7 +148,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (propDef.columnType && typeof propDef.columnType !== 'string')
       throw new InvalidArgumentError(
         'The provided option "columnType" of the property %v in the model %v ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         propName,
         modelName,
         propDef.columnType,
@@ -156,7 +156,7 @@ export class PropertiesDefinitionValidator extends Service {
     if (propDef.required && typeof propDef.required !== 'boolean')
       throw new InvalidArgumentError(
         'The provided option "required" of the property %v in the model %v ' +
-          'should be a Boolean, but %v given.',
+          'should be a Boolean, but %v was given.',
         propName,
         modelName,
         propDef.required,
@@ -228,92 +228,167 @@ export class PropertiesDefinitionValidator extends Service {
         propName,
         modelName,
       );
+    // если определена опция "validate", то проверяется значение
+    // опции, которое может являться строкой, массивом или объектом
     if (propDef.validate != null) {
       const propertyValidatorRegistry = this.getService(
         PropertyValidatorRegistry,
       );
+      // если опция "validate" содержит строку, то проверяется
+      // наличие зарегистрированного валидатора по названию
       if (propDef.validate && typeof propDef.validate === 'string') {
+        // если название валидатора не зарегистрировано,
+        // то выбрасывается ошибка
         if (!propertyValidatorRegistry.hasValidator(propDef.validate))
           throw new InvalidArgumentError(
             'The property validator %v is not found.',
             propDef.validate,
           );
-      } else if (Array.isArray(propDef.validate)) {
-        for (const validatorName of propDef.validate) {
-          if (typeof validatorName !== 'string')
+      }
+      // если опция "validate" содержит функцию, то данная функция
+      // воспринимается как пользовательский валидатор
+      else if (propDef.validate && typeof propDef.validate === 'function') {
+        // (допустимо)
+      }
+      // если опция "validate" содержит массив, то проверяется
+      // каждый элемент массива
+      else if (Array.isArray(propDef.validate)) {
+        for (const validatorOrName of propDef.validate) {
+          // если элемент массива является строкой, то проверяется
+          // наличие зарегистрированного валидатора по названию
+          if (validatorOrName && typeof validatorOrName === 'string') {
+            if (!propertyValidatorRegistry.hasValidator(validatorOrName))
+              throw new InvalidArgumentError(
+                'The property validator %v is not found.',
+                validatorOrName,
+              );
+          }
+          // если элемент массива является функцией, то данная функция
+          // воспринимается как пользовательский валидатор
+          else if (validatorOrName && typeof validatorOrName === 'function') {
+            // (допустимо)
+          }
+          // если элемент массива не является названием зарегистрированного
+          // валидатора или функцией-валидатором, то выбрасывается ошибка
+          else {
             throw new InvalidArgumentError(
-              'The provided option "validate" of the property %v in the model %v ' +
-                'has an Array value that should have a non-empty String, ' +
-                'but %v given.',
+              'The provided option "validate" for the property %v in the model %v ' +
+                'has an Array value that should contain validator names or validator ' +
+                'functions, but %v was given.',
               propName,
               modelName,
-              validatorName,
-            );
-          if (!propertyValidatorRegistry.hasValidator(validatorName))
-            throw new InvalidArgumentError(
-              'The property validator %v is not found.',
-              validatorName,
+              validatorOrName,
             );
+          }
         }
-      } else if (typeof propDef.validate === 'object') {
-        for (const validatorName in propDef.validate) {
+      }
+      // если опция "validate" содержит объект, то проверяются ключи данного
+      // объекта, которые должны являться названиями зарегистрированных
+      // валидаторов, а значения их аргументами
+      else if (typeof propDef.validate === 'object') {
+        Object.keys(propDef.validate).forEach(validatorName => {
+          // если ключ объекта не является названием зарегистрированного
+          // валидатора, то выбрасывается ошибка
           if (!propertyValidatorRegistry.hasValidator(validatorName))
             throw new InvalidArgumentError(
               'The property validator %v is not found.',
               validatorName,
             );
-        }
-      } else {
+        });
+      }
+      // если опция "validate" не является не пустой строкой, функцией,
+      // массивом и объектом, то выбрасывается ошибка
+      else {
         throw new InvalidArgumentError(
-          'The provided option "validate" of the property %v in the model %v ' +
-            'should be a non-empty String, an Array of String or an Object, ' +
-            'but %v given.',
+          'The provided option "validate" for the property %v in the model %v ' +
+            'should be either a validator name, a validator function, an array ' +
+            'of validator names or functions, or an object mapping validator ' +
+            'names to their arguments, but %v was given.',
           propName,
           modelName,
           propDef.validate,
         );
       }
     }
+    // если определена опция "transform", то проверяется значение
+    // опции, которое может являться строкой, массивом или объектом
     if (propDef.transform != null) {
       const propertyTransformerRegistry = this.getService(
         PropertyTransformerRegistry,
       );
+      // если опция "transform" содержит строку, то проверяется
+      // наличие зарегистрированного трансформера по названию
       if (propDef.transform && typeof propDef.transform === 'string') {
+        // если название трансформера не зарегистрировано,
+        // то выбрасывается ошибка
         if (!propertyTransformerRegistry.hasTransformer(propDef.transform))
           throw new InvalidArgumentError(
             'The property transformer %v is not found.',
             propDef.transform,
           );
-      } else if (Array.isArray(propDef.transform)) {
-        for (const transformerName of propDef.transform) {
-          if (typeof transformerName !== 'string')
+      }
+      // если опция "transform" содержит функцию, то данная функция
+      // воспринимается как пользовательский трансформер
+      else if (propDef.transform && typeof propDef.transform === 'function') {
+        // (допустимо)
+      }
+      // если опция "transform" содержит массив, то проверяется
+      // каждый элемент массива
+      else if (Array.isArray(propDef.transform)) {
+        for (const transformerOrName of propDef.transform) {
+          // если элемент массива является строкой, то проверяется
+          // наличие зарегистрированного трансформера по названию
+          if (transformerOrName && typeof transformerOrName === 'string') {
+            if (!propertyTransformerRegistry.hasTransformer(transformerOrName))
+              throw new InvalidArgumentError(
+                'The property transformer %v is not found.',
+                transformerOrName,
+              );
+          }
+          // если элемент массива является функцией, то данная функция
+          // воспринимается как пользовательский трансформер
+          else if (
+            transformerOrName &&
+            typeof transformerOrName === 'function'
+          ) {
+            // (допустимо)
+          }
+          // если элемент массива не является названием зарегистрированного
+          // трансформера или функцией-трансформером, то выбрасывается ошибка
+          else {
             throw new InvalidArgumentError(
-              'The provided option "transform" of the property %v in the model %v ' +
-                'has an Array value that should have a non-empty String, ' +
-                'but %v given.',
+              'The provided option "transform" for the property %v in the model %v ' +
+                'has an Array value that should contain transformer names or transformer ' +
+                'functions, but %v was given.',
               propName,
               modelName,
-              transformerName,
-            );
-          if (!propertyTransformerRegistry.hasTransformer(transformerName))
-            throw new InvalidArgumentError(
-              'The property transformer %v is not found.',
-              transformerName,
+              transformerOrName,
             );
+          }
         }
-      } else if (typeof propDef.transform === 'object') {
-        for (const transformerName in propDef.transform) {
+      }
+      // если опция "transform" содержит объект, то проверяются ключи данного
+      // объекта, которые должны являться названиями зарегистрированных
+      // трансформеров, а значения их аргументами
+      else if (typeof propDef.transform === 'object') {
+        Object.keys(propDef.transform).forEach(transformerName => {
+          // если ключ объекта не является названием зарегистрированного
+          // валидатора, то выбрасывается ошибка
           if (!propertyTransformerRegistry.hasTransformer(transformerName))
             throw new InvalidArgumentError(
               'The property transformer %v is not found.',
               transformerName,
             );
-        }
-      } else {
+        });
+      }
+      // если опция "transform" не является не пустой строкой, функцией,
+      // массивом и объектом, то выбрасывается ошибка
+      else {
         throw new InvalidArgumentError(
-          'The provided option "transform" of the property %v in the model %v ' +
-            'should be a non-empty String, an Array of String or an Object, ' +
-            'but %v given.',
+          'The provided option "transform" for the property %v in the model %v ' +
+            'should be either a transformer name, a transformer function, an array ' +
+            'of transformer names or functions, or an object mapping transformer ' +
+            'names to their arguments, but %v was given.',
           propName,
           modelName,
           propDef.transform,
@@ -327,7 +402,7 @@ export class PropertiesDefinitionValidator extends Service {
       ) {
         throw new InvalidArgumentError(
           'The provided option "unique" of the property %v in the model %v ' +
-            'should be a Boolean or one of values: %l, but %v given.',
+            'should be a Boolean or one of values: %l, but %v was given.',
           propName,
           modelName,
           Object.values(PropertyUniqueness),

+ 85 - 25
src/definition/model/properties/properties-definition-validator.spec.js

@@ -28,7 +28,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The first argument of PropertiesDefinitionValidator.validate ' +
-            'should be a non-empty String, but %s given.',
+            'should be a non-empty String, but %s was given.',
           v,
         );
       expect(validate('')).to.throw(error('""'));
@@ -47,7 +47,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "properties" of the model "model" ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       expect(validate('str')).to.throw(error('"str"'));
@@ -65,7 +65,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The property name of the model "model" should be ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       expect(validate({['']: {}})).to.throw(error('""'));
@@ -77,7 +77,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The property "foo" of the model "model" should have ' +
-            'a property definition, but %s given.',
+            'a property definition, but %s was given.',
           v,
         );
       expect(validate(undefined)).to.throw(error('undefined'));
@@ -91,7 +91,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'In case of a short property definition, the property "foo" ' +
-            'of the model "model" should have one of data types: %l, but %s given.',
+            'of the model "model" should have one of data types: %l, but %s was given.',
           Object.values(DataType),
           v,
         );
@@ -104,7 +104,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'In case of a full property definition, the property "foo" ' +
-            'of the model "model" should be an Object, but %s given.',
+            'of the model "model" should be an Object, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -118,7 +118,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The property "foo" of the model "model" requires the option "type" ' +
-            'to have one of data types: %l, but %s given.',
+            'to have one of data types: %l, but %s was given.',
           Object.values(DataType),
           v,
         );
@@ -141,7 +141,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "itemType" of the property "foo" in the model "model" ' +
-            'should have one of data types: %l, but %s given.',
+            'should have one of data types: %l, but %s was given.',
           Object.values(DataType),
           v,
         );
@@ -165,7 +165,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "itemModel" of the property "foo" ' +
-            'in the model "model" should be a String, but %s given.',
+            'in the model "model" should be a String, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -186,7 +186,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "model" of the property "foo" in the model "model" ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -207,7 +207,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "primaryKey" of the property "foo" in the model "model" ' +
-            'should be a Boolean, but %s given.',
+            'should be a Boolean, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -228,7 +228,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "columnName" of the property "foo" in the model "model" ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -249,7 +249,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "columnType" of the property "foo" in the model "model" ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       expect(validate(10)).to.throw(error('10'));
@@ -270,7 +270,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "required" of the property "foo" in the model "model" ' +
-            'should be a Boolean, but %s given.',
+            'should be a Boolean, but %s was given.',
           v,
         );
       expect(validate('str')).to.throw(error('"str"'));
@@ -426,7 +426,7 @@ describe('PropertiesDefinitionValidator', function () {
       expect(V.validate).to.have.been.called.with.exactly('model', propDefs);
     });
 
-    it('the option "validate" should have a non-empty String, an Array of String or an Object', function () {
+    it('the option "validate" should have a non-empty String, a Function, an Array or an Object', function () {
       const validate = v => () => {
         const foo = {
           type: DataType.ANY,
@@ -436,9 +436,10 @@ describe('PropertiesDefinitionValidator', function () {
       };
       const error = v =>
         format(
-          'The provided option "validate" of the property "foo" in the model "model" ' +
-            'should be a non-empty String, an Array of String or an Object, ' +
-            'but %s given.',
+          'The provided option "validate" for the property "foo" in the model "model" ' +
+            'should be either a validator name, a validator function, an array ' +
+            'of validator names or functions, or an object mapping validator ' +
+            'names to their arguments, but %s was given.',
           v,
         );
       expect(validate('')).to.throw(error('""'));
@@ -446,9 +447,10 @@ describe('PropertiesDefinitionValidator', function () {
       expect(validate(0)).to.throw(error('0'));
       expect(validate(true)).to.throw(error('true'));
       expect(validate(false)).to.throw(error('false'));
-      expect(validate(() => undefined)).to.throw(error('Function'));
       validate('myValidator')();
+      validate(() => true)();
       validate(['myValidator'])();
+      validate([() => true])();
       validate([])();
       validate({myValidator: true})();
       validate({})();
@@ -456,6 +458,34 @@ describe('PropertiesDefinitionValidator', function () {
       validate(undefined)();
     });
 
+    it('the option "validate" with an Array value requires elements to be a non-empty String or a Function', function () {
+      const validate = v => () => {
+        const foo = {
+          type: DataType.ANY,
+          validate: [v],
+        };
+        S.validate('model', {foo});
+      };
+      const error = v =>
+        format(
+          'The provided option "validate" for the property "foo" in the model "model" ' +
+            'has an Array value that should contain validator names or validator functions, ' +
+            'but %s was given.',
+          v,
+        );
+      expect(validate('')).to.throw(error('""'));
+      expect(validate(10)).to.throw(error('10'));
+      expect(validate(0)).to.throw(error('0'));
+      expect(validate(true)).to.throw(error('true'));
+      expect(validate(false)).to.throw(error('false'));
+      expect(validate([1, 2, 3])).to.throw(error('Array'));
+      expect(validate({foo: 'bar'})).to.throw(error('Object'));
+      expect(validate(null)).to.throw(error('null'));
+      expect(validate(undefined)).to.throw(error('undefined'));
+      validate('myValidator')();
+      validate(() => true)();
+    });
+
     it('the option "validate" requires only existing validator names', function () {
       const validate = v => () => {
         const foo = {
@@ -473,7 +503,7 @@ describe('PropertiesDefinitionValidator', function () {
       validate({myValidator: true})();
     });
 
-    it('the option "transform" should have a non-empty String, an Array of String or an Object', function () {
+    it('the option "transform" should have a non-empty String, a Function, an Array or an Object', function () {
       const validate = v => () => {
         const foo = {
           type: DataType.ANY,
@@ -483,9 +513,10 @@ describe('PropertiesDefinitionValidator', function () {
       };
       const error = v =>
         format(
-          'The provided option "transform" of the property "foo" in the model "model" ' +
-            'should be a non-empty String, an Array of String or an Object, ' +
-            'but %s given.',
+          'The provided option "transform" for the property "foo" in the model "model" ' +
+            'should be either a transformer name, a transformer function, an array ' +
+            'of transformer names or functions, or an object mapping transformer ' +
+            'names to their arguments, but %s was given.',
           v,
         );
       expect(validate('')).to.throw(error('""'));
@@ -493,9 +524,10 @@ describe('PropertiesDefinitionValidator', function () {
       expect(validate(0)).to.throw(error('0'));
       expect(validate(true)).to.throw(error('true'));
       expect(validate(false)).to.throw(error('false'));
-      expect(validate(() => undefined)).to.throw(error('Function'));
       validate('myTransformer')();
+      validate(v => v)();
       validate(['myTransformer'])();
+      validate([v => v])();
       validate([])();
       validate({myTransformer: true})();
       validate({})();
@@ -503,6 +535,34 @@ describe('PropertiesDefinitionValidator', function () {
       validate(undefined)();
     });
 
+    it('the option "transform" with an Array value requires elements to be a non-empty String or a Function', function () {
+      const validate = v => () => {
+        const foo = {
+          type: DataType.ANY,
+          transform: [v],
+        };
+        S.validate('model', {foo});
+      };
+      const error = v =>
+        format(
+          'The provided option "transform" for the property "foo" in the model "model" ' +
+            'has an Array value that should contain transformer names or transformer functions, ' +
+            'but %s was given.',
+          v,
+        );
+      expect(validate('')).to.throw(error('""'));
+      expect(validate(10)).to.throw(error('10'));
+      expect(validate(0)).to.throw(error('0'));
+      expect(validate(true)).to.throw(error('true'));
+      expect(validate(false)).to.throw(error('false'));
+      expect(validate([1, 2, 3])).to.throw(error('Array'));
+      expect(validate({foo: 'bar'})).to.throw(error('Object'));
+      expect(validate(null)).to.throw(error('null'));
+      expect(validate(undefined)).to.throw(error('undefined'));
+      validate('myTransformer')();
+      validate(v => v)();
+    });
+
     it('the option "transform" requires only existing transformer names', function () {
       const validate = v => () => {
         const foo = {
@@ -531,7 +591,7 @@ describe('PropertiesDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "unique" of the property "foo" in the model "model" ' +
-            'should be a Boolean or one of values: %l, but %s given.',
+            'should be a Boolean or one of values: %l, but %s was given.',
           Object.values(PropertyUniqueness),
           v,
         );

+ 0 - 1
src/definition/model/properties/property-transformer/builtin/index.d.ts

@@ -1,4 +1,3 @@
 export * from './trim-transformer.js';
 export * from './to-lower-case-transformer.js';
 export * from './to-upper-case-transformer.js';
-export * from './to-title-case-transformer.js';

+ 0 - 1
src/definition/model/properties/property-transformer/builtin/index.js

@@ -1,4 +1,3 @@
 export * from './trim-transformer.js';
 export * from './to-lower-case-transformer.js';
 export * from './to-upper-case-transformer.js';
-export * from './to-title-case-transformer.js';

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.d.ts

@@ -3,4 +3,4 @@ import {PropertyTransformer} from '../property-transformer.js';
 /**
  * To lower case transformer.
  */
-export declare type toLowerCaseTransformer = PropertyTransformer;
+export const toLowerCaseTransformer: PropertyTransformer;

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.js

@@ -12,7 +12,7 @@ export function toLowerCaseTransformer(value, options, context) {
   if (value == null) return value;
   if (typeof value === 'string') return value.toLowerCase();
   throw new InvalidArgumentError(
-    'The property transformer %v requires a String value, but %v given.',
+    'The property transformer %v requires a String value, but %v was given.',
     context.transformerName,
     value,
   );

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.spec.js

@@ -22,7 +22,7 @@ describe('toLowerCaseTransformer', function () {
       });
     const error = v =>
       format(
-        'The property transformer "toLowerCase" requires a String value, but %s given.',
+        'The property transformer "toLowerCase" requires a String value, but %s was given.',
         v,
       );
     expect(throwable(10)).to.throw(error('10'));

+ 0 - 6
src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.d.ts

@@ -1,6 +0,0 @@
-import {PropertyTransformer} from '../property-transformer.js';
-
-/**
- * To title case transformer.
- */
-export declare type toTitleCaseTransformer = PropertyTransformer;

+ 0 - 22
src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.js

@@ -1,22 +0,0 @@
-import {InvalidArgumentError} from '../../../../../errors/index.js';
-
-/**
- * To title case transformer.
- *
- * @param {*} value
- * @param {undefined} options
- * @param {object} context
- * @returns {string|undefined|null}
- */
-export function toTitleCaseTransformer(value, options, context) {
-  if (value == null) return value;
-  if (typeof value === 'string')
-    return value.replace(/\p{L}\S*/gu, text => {
-      return text.charAt(0).toUpperCase() + text.substring(1).toLowerCase();
-    });
-  throw new InvalidArgumentError(
-    'The property transformer %v requires a String value, but %v given.',
-    context.transformerName,
-    value,
-  );
-}

+ 0 - 41
src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.spec.js

@@ -1,41 +0,0 @@
-import {expect} from 'chai';
-import {format} from '@e22m4u/js-format';
-import {toTitleCaseTransformer} from './to-title-case-transformer.js';
-
-describe('toTitleCaseTransformer', function () {
-  it('returns undefined and null values as is', function () {
-    const res1 = toTitleCaseTransformer(undefined, undefined, {});
-    const res2 = toTitleCaseTransformer(null, undefined, {});
-    expect(res1).to.be.undefined;
-    expect(res2).to.be.null;
-  });
-
-  it('converts the given string to title case', function () {
-    const res1 = toTitleCaseTransformer('TEST', undefined, {});
-    const res2 = toTitleCaseTransformer('test', undefined, {});
-    expect(res1).to.be.eq('Test');
-    expect(res2).to.be.eq('Test');
-  });
-
-  it('throws an error if the given value is not a string', function () {
-    const throwable = v => () =>
-      toTitleCaseTransformer(v, undefined, {
-        transformerName: 'toTitleCase',
-      });
-    const error = v =>
-      format(
-        'The property transformer "toTitleCase" requires a String value, but %s given.',
-        v,
-      );
-    expect(throwable(10)).to.throw(error('10'));
-    expect(throwable(0)).to.throw(error('0'));
-    expect(throwable(true)).to.throw(error('true'));
-    expect(throwable(false)).to.throw(error('false'));
-    expect(throwable({})).to.throw(error('Object'));
-    expect(throwable([])).to.throw(error('Array'));
-    throwable('str')();
-    throwable('')();
-    throwable(undefined)();
-    throwable(null)();
-  });
-});

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.d.ts

@@ -3,4 +3,4 @@ import {PropertyTransformer} from '../property-transformer.js';
 /**
  * To upper case transformer.
  */
-export declare type toUpperCaseTransformer = PropertyTransformer;
+export const toUpperCaseTransformer: PropertyTransformer;

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.js

@@ -12,7 +12,7 @@ export function toUpperCaseTransformer(value, options, context) {
   if (value == null) return value;
   if (typeof value === 'string') return value.toUpperCase();
   throw new InvalidArgumentError(
-    'The property transformer %v requires a String value, but %v given.',
+    'The property transformer %v requires a String value, but %v was given.',
     context.transformerName,
     value,
   );

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.spec.js

@@ -22,7 +22,7 @@ describe('toUpperCaseTransformer', function () {
       });
     const error = v =>
       format(
-        'The property transformer "toUpperCase" requires a String value, but %s given.',
+        'The property transformer "toUpperCase" requires a String value, but %s was given.',
         v,
       );
     expect(throwable(10)).to.throw(error('10'));

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/trim-transformer.js

@@ -12,7 +12,7 @@ export function trimTransformer(value, options, context) {
   if (value == null) return value;
   if (typeof value === 'string') return value.trim();
   throw new InvalidArgumentError(
-    'The property transformer %v requires a String value, but %v given.',
+    'The property transformer %v requires a String value, but %v was given.',
     context.transformerName,
     value,
   );

+ 1 - 1
src/definition/model/properties/property-transformer/builtin/trim-transformer.spec.js

@@ -22,7 +22,7 @@ describe('trimTransformer', function () {
       });
     const error = v =>
       format(
-        'The property transformer "trim" requires a String value, but %s given.',
+        'The property transformer "trim" requires a String value, but %s was given.',
         v,
       );
     expect(throwable(10)).to.throw(error('10'));

+ 2 - 4
src/definition/model/properties/property-transformer/property-transformer-registry.js

@@ -2,7 +2,6 @@ import {Service} from '@e22m4u/js-service';
 import {trimTransformer} from './builtin/index.js';
 import {toUpperCaseTransformer} from './builtin/index.js';
 import {toLowerCaseTransformer} from './builtin/index.js';
-import {toTitleCaseTransformer} from './builtin/index.js';
 import {InvalidArgumentError} from '../../../../errors/index.js';
 
 /**
@@ -18,7 +17,6 @@ export class PropertyTransformerRegistry extends Service {
     trim: trimTransformer,
     toUpperCase: toUpperCaseTransformer,
     toLowerCase: toLowerCaseTransformer,
-    toTitleCase: toTitleCaseTransformer,
   };
 
   /**
@@ -32,7 +30,7 @@ export class PropertyTransformerRegistry extends Service {
     if (!name || typeof name !== 'string')
       throw new InvalidArgumentError(
         'A name of the property transformer must ' +
-          'be a non-empty String, but %v given.',
+          'be a non-empty String, but %v was given.',
         name,
       );
     if (name in this._transformers)
@@ -42,7 +40,7 @@ export class PropertyTransformerRegistry extends Service {
       );
     if (typeof transformer !== 'function')
       throw new InvalidArgumentError(
-        'The property transformer %v must be a Function, but %v given.',
+        'The property transformer %v must be a Function, but %v was given.',
         name,
         transformer,
       );

+ 2 - 4
src/definition/model/properties/property-transformer/property-transformer-registry.spec.js

@@ -3,7 +3,6 @@ import {format} from '@e22m4u/js-format';
 import {trimTransformer} from './builtin/index.js';
 import {toUpperCaseTransformer} from './builtin/index.js';
 import {toLowerCaseTransformer} from './builtin/index.js';
-import {toTitleCaseTransformer} from './builtin/index.js';
 import {PropertyTransformerRegistry} from './property-transformer-registry.js';
 
 describe('PropertyTransformerRegistry', function () {
@@ -14,7 +13,6 @@ describe('PropertyTransformerRegistry', function () {
         trim: trimTransformer,
         toUpperCase: toUpperCaseTransformer,
         toLowerCase: toLowerCaseTransformer,
-        toTitleCase: toTitleCaseTransformer,
       });
     });
 
@@ -32,7 +30,7 @@ describe('PropertyTransformerRegistry', function () {
       const error = v =>
         format(
           'A name of the property transformer must ' +
-            'be a non-empty String, but %s given.',
+            'be a non-empty String, but %s was given.',
           v,
         );
       expect(throwable('')).to.throw(error('""'));
@@ -61,7 +59,7 @@ describe('PropertyTransformerRegistry', function () {
       const throwable = v => () => ptr.addTransformer('test', v);
       const error = v =>
         format(
-          'The property transformer "test" must be a Function, but %s given.',
+          'The property transformer "test" must be a Function, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));

+ 2 - 2
src/definition/model/properties/property-transformer/property-transformer.d.ts

@@ -22,6 +22,6 @@ export declare type PropertyTransformer = (
  * Property transform options.
  */
 export type PropertyTransformOptions =
-  | string
-  | string[]
+  | (string | PropertyTransformer)
+  | (string | PropertyTransformer)[]
   | {[key: string]: unknown};

+ 4 - 4
src/definition/model/properties/property-uniqueness-validator.js

@@ -30,24 +30,24 @@ export class PropertyUniquenessValidator extends Service {
     if (typeof countMethod !== 'function')
       throw new InvalidArgumentError(
         'The parameter "countMethod" of the PropertyUniquenessValidator ' +
-          'must be a Function, but %v given.',
+          'must be a Function, but %v was given.',
         countMethod,
       );
     if (!methodName || typeof methodName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "methodName" of the PropertyUniquenessValidator ' +
-          'must be a non-empty String, but %v given.',
+          'must be a non-empty String, but %v was given.',
         methodName,
       );
     if (!modelName || typeof modelName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "modelName" of the PropertyUniquenessValidator ' +
-          'must be a non-empty String, but %v given.',
+          'must be a non-empty String, but %v was given.',
         modelName,
       );
     if (!isPureObject(modelData))
       throw new InvalidArgumentError(
-        'The data of the model %v should be an Object, but %v given.',
+        'The data of the model %v should be an Object, but %v was given.',
         modelName,
         modelData,
       );

+ 4 - 4
src/definition/model/properties/property-uniqueness-validator.spec.js

@@ -25,7 +25,7 @@ describe('PropertyUniquenessValidator', function () {
       const error = v =>
         format(
           'The parameter "countMethod" of the PropertyUniquenessValidator ' +
-            'must be a Function, but %s given.',
+            'must be a Function, but %s was given.',
           v,
         );
       await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
@@ -60,7 +60,7 @@ describe('PropertyUniquenessValidator', function () {
       const error = v =>
         format(
           'The parameter "methodName" of the PropertyUniquenessValidator ' +
-            'must be a non-empty String, but %s given.',
+            'must be a non-empty String, but %s was given.',
           v,
         );
       await expect(throwable('')).to.be.rejectedWith(error('""'));
@@ -95,7 +95,7 @@ describe('PropertyUniquenessValidator', function () {
       const error = v =>
         format(
           'The parameter "modelName" of the PropertyUniquenessValidator ' +
-            'must be a non-empty String, but %s given.',
+            'must be a non-empty String, but %s was given.',
           v,
         );
       await expect(throwable('')).to.be.rejectedWith(error('""'));
@@ -129,7 +129,7 @@ describe('PropertyUniquenessValidator', function () {
       const throwable = v => puv.validate(() => 0, 'create', 'model', v);
       const error = v =>
         format(
-          'The data of the model "model" should be an Object, but %s given.',
+          'The data of the model "model" should be an Object, but %s was given.',
           v,
         );
       await expect(throwable('str')).to.be.rejectedWith(error('"str"'));

+ 1 - 1
src/definition/model/properties/property-validator/builtin/max-length-validator.d.ts

@@ -3,4 +3,4 @@ import {PropertyValidator} from '../property-validator.js';
 /**
  * Max length validator.
  */
-export declare type maxLengthValidator = PropertyValidator;
+export const maxLengthValidator: PropertyValidator;

+ 2 - 2
src/definition/model/properties/property-validator/builtin/max-length-validator.js

@@ -13,7 +13,7 @@ export function maxLengthValidator(value, options, context) {
   if (typeof options !== 'number')
     throw new InvalidArgumentError(
       'The validator %v requires the "options" argument ' +
-        'as a Number, but %v given.',
+        'as a Number, but %v was given.',
       context.validatorName,
       options,
     );
@@ -21,7 +21,7 @@ export function maxLengthValidator(value, options, context) {
     return value.length <= options;
   throw new InvalidArgumentError(
     'The property validator %v requires a String ' +
-      'or an Array value, but %v given.',
+      'or an Array value, but %v was given.',
     context.validatorName,
     value,
   );

+ 2 - 2
src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js

@@ -23,7 +23,7 @@ describe('maxLengthValidator', function () {
     const error = v =>
       format(
         'The property validator "myValidator" requires a String ' +
-          'or an Array value, but %s given.',
+          'or an Array value, but %s was given.',
         v,
       );
     expect(throwable(10)).to.throw(error('10'));
@@ -48,7 +48,7 @@ describe('maxLengthValidator', function () {
     const error = v =>
       format(
         'The validator "myValidator" requires the "options" argument ' +
-          'as a Number, but %s given.',
+          'as a Number, but %s was given.',
         v,
       );
     expect(throwable('str')).to.throw(error('"str"'));

+ 1 - 1
src/definition/model/properties/property-validator/builtin/min-length-validator.d.ts

@@ -3,4 +3,4 @@ import {PropertyValidator} from '../property-validator.js';
 /**
  * Min length validator.
  */
-export declare type minLengthValidator = PropertyValidator;
+export const minLengthValidator: PropertyValidator;

+ 2 - 2
src/definition/model/properties/property-validator/builtin/min-length-validator.js

@@ -13,7 +13,7 @@ export function minLengthValidator(value, options, context) {
   if (typeof options !== 'number')
     throw new InvalidArgumentError(
       'The validator %v requires the "options" argument ' +
-        'as a Number, but %v given.',
+        'as a Number, but %v was given.',
       context.validatorName,
       options,
     );
@@ -21,7 +21,7 @@ export function minLengthValidator(value, options, context) {
     return value.length >= options;
   throw new InvalidArgumentError(
     'The property validator %v requires a String ' +
-      'or an Array value, but %v given.',
+      'or an Array value, but %v was given.',
     context.validatorName,
     value,
   );

+ 2 - 2
src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js

@@ -23,7 +23,7 @@ describe('minLengthValidator', function () {
     const error = v =>
       format(
         'The property validator "myValidator" requires a String ' +
-          'or an Array value, but %s given.',
+          'or an Array value, but %s was given.',
         v,
       );
     expect(throwable(10)).to.throw(error('10'));
@@ -48,7 +48,7 @@ describe('minLengthValidator', function () {
     const error = v =>
       format(
         'The validator "myValidator" requires the "options" argument ' +
-          'as a Number, but %s given.',
+          'as a Number, but %s was given.',
         v,
       );
     expect(throwable('str')).to.throw(error('"str"'));

+ 1 - 1
src/definition/model/properties/property-validator/builtin/regexp-validator.d.ts

@@ -3,4 +3,4 @@ import {PropertyValidator} from '../property-validator.js';
 /**
  * Regexp validator.
  */
-export declare type regexpValidator = PropertyValidator;
+export const regexpValidator: PropertyValidator;

+ 2 - 2
src/definition/model/properties/property-validator/builtin/regexp-validator.js

@@ -14,7 +14,7 @@ export function regexpValidator(value, options, context) {
   if (typeof options !== 'string' && !(options instanceof RegExp))
     throw new InvalidArgumentError(
       'The validator %v requires the "options" argument ' +
-        'as a String or RegExp, but %v given.',
+        'as a String or RegExp, but %v was given.',
       context.validatorName,
       options,
     );
@@ -23,7 +23,7 @@ export function regexpValidator(value, options, context) {
     return regexp.test(value);
   }
   throw new InvalidArgumentError(
-    'The property validator %v requires ' + 'a String value, but %v given.',
+    'The property validator %v requires ' + 'a String value, but %v was given.',
     context.validatorName,
     value,
   );

+ 2 - 2
src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js

@@ -23,7 +23,7 @@ describe('regexpValidator', function () {
     const error = v =>
       format(
         'The property validator "myValidator" requires ' +
-          'a String value, but %s given.',
+          'a String value, but %s was given.',
         v,
       );
     expect(throwable(true)).to.throw(error('true'));
@@ -45,7 +45,7 @@ describe('regexpValidator', function () {
     const error = v =>
       format(
         'The validator "myValidator" requires the "options" argument ' +
-          'as a String or RegExp, but %s given.',
+          'as a String or RegExp, but %s was given.',
         v,
       );
     expect(throwable(true)).to.throw(error('true'));

+ 2 - 2
src/definition/model/properties/property-validator/property-validator-registry.js

@@ -30,7 +30,7 @@ export class PropertyValidatorRegistry extends Service {
     if (!name || typeof name !== 'string')
       throw new InvalidArgumentError(
         'A name of the property validator must ' +
-          'be a non-empty String, but %v given.',
+          'be a non-empty String, but %v was given.',
         name,
       );
     if (name in this._validators)
@@ -40,7 +40,7 @@ export class PropertyValidatorRegistry extends Service {
       );
     if (typeof validator !== 'function')
       throw new InvalidArgumentError(
-        'The property validator %v must be a Function, but %v given.',
+        'The property validator %v must be a Function, but %v was given.',
         name,
         validator,
       );

+ 2 - 2
src/definition/model/properties/property-validator/property-validator-registry.spec.js

@@ -30,7 +30,7 @@ describe('PropertyValidatorRegistry', function () {
       const error = v =>
         format(
           'A name of the property validator must ' +
-            'be a non-empty String, but %s given.',
+            'be a non-empty String, but %s was given.',
           v,
         );
       expect(throwable('')).to.throw(error('""'));
@@ -59,7 +59,7 @@ describe('PropertyValidatorRegistry', function () {
       const throwable = v => () => pvr.addValidator('test', v);
       const error = v =>
         format(
-          'The property validator "test" must be a Function, but %s given.',
+          'The property validator "test" must be a Function, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));

+ 2 - 2
src/definition/model/properties/property-validator/property-validator.d.ts

@@ -20,6 +20,6 @@ export type PropertyValidator = (
  * Property validate options.
  */
 export type PropertyValidateOptions =
-  | string
-  | string[]
+  | (string | PropertyValidator)
+  | (string | PropertyValidator)[]
   | {[key: string]: unknown};

+ 23 - 23
src/definition/model/relations/relations-definition-validator.js

@@ -17,13 +17,13 @@ export class RelationsDefinitionValidator extends Service {
     if (!modelName || typeof modelName !== 'string')
       throw new InvalidArgumentError(
         'The first argument of RelationsDefinitionValidator.validate ' +
-          'should be a non-empty String, but %v given.',
+          'should be a non-empty String, but %v was given.',
         modelName,
       );
     if (!relDefs || typeof relDefs !== 'object' || Array.isArray(relDefs))
       throw new InvalidArgumentError(
         'The provided option "relations" of the model %v ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         modelName,
         relDefs,
       );
@@ -45,19 +45,19 @@ export class RelationsDefinitionValidator extends Service {
     if (!modelName || typeof modelName !== 'string')
       throw new InvalidArgumentError(
         'The first argument of RelationsDefinitionValidator._validateRelation ' +
-          'should be a non-empty String, but %v given.',
+          'should be a non-empty String, but %v was given.',
         modelName,
       );
     if (!relName || typeof relName !== 'string')
       throw new InvalidArgumentError(
         'The relation name of the model %v should be ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         modelName,
         relName,
       );
     if (!relDef || typeof relDef !== 'object' || Array.isArray(relDef))
       throw new InvalidArgumentError(
-        'The relation %v of the model %v should be an Object, but %v given.',
+        'The relation %v of the model %v should be an Object, but %v was given.',
         relName,
         modelName,
         relDef,
@@ -65,7 +65,7 @@ export class RelationsDefinitionValidator extends Service {
     if (!relDef.type || !Object.values(Type).includes(relDef.type))
       throw new InvalidArgumentError(
         'The relation %v of the model %v requires the option "type" ' +
-          'to have one of relation types: %l, but %v given.',
+          'to have one of relation types: %l, but %v was given.',
         relName,
         modelName,
         Object.values(Type),
@@ -112,7 +112,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v has the type "belongsTo", ' +
             'so it expects the option "polymorphic" to be a Boolean, ' +
-            'but %v given.',
+            'but %v was given.',
           relName,
           modelName,
           relDef.polymorphic,
@@ -121,7 +121,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v is a polymorphic "belongsTo" relation, ' +
             'so it expects the provided option "foreignKey" to be a String, ' +
-            'but %v given.',
+            'but %v was given.',
           relName,
           modelName,
           relDef.foreignKey,
@@ -130,7 +130,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v is a polymorphic "belongsTo" relation, ' +
             'so it expects the provided option "discriminator" to be a String, ' +
-            'but %v given.',
+            'but %v was given.',
           relName,
           modelName,
           relDef.discriminator,
@@ -141,7 +141,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v has the type "belongsTo", ' +
             'so it requires the option "model" to be a non-empty String, ' +
-            'but %v given.',
+            'but %v was given.',
           relName,
           modelName,
           relDef.model,
@@ -150,7 +150,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v has the type "belongsTo", ' +
             'so it expects the provided option "foreignKey" to be a String, ' +
-            'but %v given.',
+            'but %v was given.',
           relName,
           modelName,
           relDef.foreignKey,
@@ -208,7 +208,7 @@ export class RelationsDefinitionValidator extends Service {
       throw new InvalidArgumentError(
         'The relation %v of the model %v has the type "hasOne", ' +
           'so it requires the option "model" to be a non-empty String, ' +
-          'but %v given.',
+          'but %v was given.',
         relName,
         modelName,
         relDef.model,
@@ -238,7 +238,7 @@ export class RelationsDefinitionValidator extends Service {
           throw new InvalidArgumentError(
             'The relation %v of the model %v has the option "polymorphic" ' +
               'with "true" value, so it requires the option "foreignKey" ' +
-              'to be a non-empty String, but %v given.',
+              'to be a non-empty String, but %v was given.',
             relName,
             modelName,
             relDef.foreignKey,
@@ -247,7 +247,7 @@ export class RelationsDefinitionValidator extends Service {
           throw new InvalidArgumentError(
             'The relation %v of the model %v has the option "polymorphic" ' +
               'with "true" value, so it requires the option "discriminator" ' +
-              'to be a non-empty String, but %v given.',
+              'to be a non-empty String, but %v was given.',
             relName,
             modelName,
             relDef.discriminator,
@@ -256,7 +256,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v has the type "hasOne", ' +
             'so it expects the provided option "polymorphic" to be ' +
-            'a String or a Boolean, but %v given.',
+            'a String or a Boolean, but %v was given.',
           relName,
           modelName,
           relDef.polymorphic,
@@ -268,7 +268,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v has the type "hasOne", ' +
             'so it requires the option "foreignKey" to be a non-empty String, ' +
-            'but %v given.',
+            'but %v was given.',
           relName,
           modelName,
           relDef.foreignKey,
@@ -326,7 +326,7 @@ export class RelationsDefinitionValidator extends Service {
       throw new InvalidArgumentError(
         'The relation %v of the model %v has the type "hasMany", ' +
           'so it requires the option "model" to be a non-empty String, ' +
-          'but %v given.',
+          'but %v was given.',
         relName,
         modelName,
         relDef.model,
@@ -356,7 +356,7 @@ export class RelationsDefinitionValidator extends Service {
           throw new InvalidArgumentError(
             'The relation %v of the model %v has the option "polymorphic" ' +
               'with "true" value, so it requires the option "foreignKey" ' +
-              'to be a non-empty String, but %v given.',
+              'to be a non-empty String, but %v was given.',
             relName,
             modelName,
             relDef.foreignKey,
@@ -365,7 +365,7 @@ export class RelationsDefinitionValidator extends Service {
           throw new InvalidArgumentError(
             'The relation %v of the model %v has the option "polymorphic" ' +
               'with "true" value, so it requires the option "discriminator" ' +
-              'to be a non-empty String, but %v given.',
+              'to be a non-empty String, but %v was given.',
             relName,
             modelName,
             relDef.discriminator,
@@ -374,7 +374,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v has the type "hasMany", ' +
             'so it expects the provided option "polymorphic" to be ' +
-            'a String or a Boolean, but %v given.',
+            'a String or a Boolean, but %v was given.',
           relName,
           modelName,
           relDef.polymorphic,
@@ -386,7 +386,7 @@ export class RelationsDefinitionValidator extends Service {
         throw new InvalidArgumentError(
           'The relation %v of the model %v has the type "hasMany", ' +
             'so it requires the option "foreignKey" to be a non-empty String, ' +
-            'but %v given.',
+            'but %v was given.',
           relName,
           modelName,
           relDef.foreignKey,
@@ -424,7 +424,7 @@ export class RelationsDefinitionValidator extends Service {
       throw new InvalidArgumentError(
         'The relation %v of the model %v has the type "referencesMany", ' +
           'so it requires the option "model" to be a non-empty String, ' +
-          'but %v given.',
+          'but %v was given.',
         relName,
         modelName,
         relDef.model,
@@ -433,7 +433,7 @@ export class RelationsDefinitionValidator extends Service {
       throw new InvalidArgumentError(
         'The relation %v of the model %v has the type "referencesMany", ' +
           'so it expects the provided option "foreignKey" to be a String, ' +
-          'but %v given.',
+          'but %v was given.',
         relName,
         modelName,
         relDef.foreignKey,

+ 24 - 24
src/definition/model/relations/relations-definition-validator.spec.js

@@ -12,7 +12,7 @@ describe('RelationsDefinitionValidator', function () {
       const error = v =>
         format(
           'The first argument of RelationsDefinitionValidator.validate ' +
-            'should be a non-empty String, but %s given.',
+            'should be a non-empty String, but %s was given.',
           v,
         );
       expect(validate('')).to.throw(error('""'));
@@ -31,7 +31,7 @@ describe('RelationsDefinitionValidator', function () {
       const error = v =>
         format(
           'The provided option "relations" of the model "model" ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       expect(validate('str')).to.throw(error('"str"'));
@@ -49,7 +49,7 @@ describe('RelationsDefinitionValidator', function () {
       const error = v =>
         format(
           'The relation name of the model "model" should be ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       expect(validate({['']: {}})).to.throw(error('""'));
@@ -61,7 +61,7 @@ describe('RelationsDefinitionValidator', function () {
       const error = v =>
         format(
           'The relation "foo" of the model "model" should ' +
-            'be an Object, but %s given.',
+            'be an Object, but %s was given.',
           v,
         );
       expect(validate('str')).to.throw(error('"str"'));
@@ -82,7 +82,7 @@ describe('RelationsDefinitionValidator', function () {
       const error = v =>
         format(
           'The relation "foo" of the model "model" requires the option "type" ' +
-            'to have one of relation types: %l, but %s given.',
+            'to have one of relation types: %l, but %s was given.',
           Object.values(RelationType),
           v,
         );
@@ -111,7 +111,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "belongsTo", ' +
                 'so it requires the option "model" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -138,7 +138,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "belongsTo", ' +
                 'so it expects the provided option "foreignKey" to be a String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate(10)).to.throw(error('10'));
@@ -181,7 +181,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "belongsTo", ' +
                 'so it expects the option "polymorphic" to be a Boolean, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('str')).to.throw(error('"str"'));
@@ -204,7 +204,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" is a polymorphic "belongsTo" relation, ' +
                 'so it expects the provided option "foreignKey" to be a String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate(10)).to.throw(error('10'));
@@ -231,7 +231,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" is a polymorphic "belongsTo" relation, ' +
                 'so it expects the provided option "discriminator" to be a String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate(10)).to.throw(error('10'));
@@ -262,7 +262,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasOne", ' +
                 'so it requires the option "model" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -289,7 +289,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasOne", ' +
                 'so it requires the option "foreignKey" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -334,7 +334,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasOne", ' +
                 'so it requires the option "model" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -399,7 +399,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasOne", ' +
                 'so it requires the option "model" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -428,7 +428,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the option "polymorphic" ' +
                 'with "true" value, so it requires the option "foreignKey" ' +
-                'to be a non-empty String, but %s given.',
+                'to be a non-empty String, but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -457,7 +457,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the option "polymorphic" ' +
                 'with "true" value, so it requires the option "discriminator" ' +
-                'to be a non-empty String, but %s given.',
+                'to be a non-empty String, but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -488,7 +488,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasMany", ' +
                 'so it requires the option "model" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -515,7 +515,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasMany", ' +
                 'so it requires the option "foreignKey" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -560,7 +560,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasMany", ' +
                 'so it requires the option "model" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -625,7 +625,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the type "hasMany", ' +
                 'so it requires the option "model" to be a non-empty String, ' +
-                'but %s given.',
+                'but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -654,7 +654,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the option "polymorphic" ' +
                 'with "true" value, so it requires the option "foreignKey" ' +
-                'to be a non-empty String, but %s given.',
+                'to be a non-empty String, but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -683,7 +683,7 @@ describe('RelationsDefinitionValidator', function () {
             format(
               'The relation "foo" of the model "model" has the option "polymorphic" ' +
                 'with "true" value, so it requires the option "discriminator" ' +
-                'to be a non-empty String, but %s given.',
+                'to be a non-empty String, but %s was given.',
               v,
             );
           expect(validate('')).to.throw(error('""'));
@@ -712,7 +712,7 @@ describe('RelationsDefinitionValidator', function () {
           format(
             'The relation "foo" of the model "model" has the type "referencesMany", ' +
               'so it requires the option "model" to be a non-empty String, ' +
-              'but %s given.',
+              'but %s was given.',
             v,
           );
         expect(validate('')).to.throw(error('""'));
@@ -739,7 +739,7 @@ describe('RelationsDefinitionValidator', function () {
           format(
             'The relation "foo" of the model "model" has the type "referencesMany", ' +
               'so it expects the provided option "foreignKey" to be a String, ' +
-              'but %s given.',
+              'but %s was given.',
             v,
           );
         expect(validate(10)).to.throw(error('10'));

+ 1 - 1
src/errors/invalid-operator-value-error.js

@@ -14,7 +14,7 @@ export class InvalidOperatorValueError extends Error {
   constructor(operator, expected, value) {
     super(
       format(
-        'Condition of {%s: ...} should have %s, but %v given.',
+        'Condition of {%s: ...} should have %s, but %v was given.',
         operator,
         expected,
         value,

+ 1 - 1
src/errors/invalid-operator-value-error.spec.js

@@ -5,7 +5,7 @@ describe('InvalidOperatorValueError', function () {
   it('sets specific message', function () {
     const error = new InvalidOperatorValueError('exists', 'a boolean', '');
     const message =
-      'Condition of {exists: ...} should have a boolean, but "" given.';
+      'Condition of {exists: ...} should have a boolean, but "" was given.';
     expect(error.message).to.be.eq(message);
   });
 });

+ 5 - 5
src/filter/fields-clause-tool.js

@@ -22,7 +22,7 @@ export class FieldsClauseTool extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The first argument of FieldsClauseTool.filter should be an Object or ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
     });
@@ -30,7 +30,7 @@ export class FieldsClauseTool extends Service {
     if (!modelName || typeof modelName !== 'string')
       throw new InvalidArgumentError(
         'The second argument of FieldsClauseTool.filter should be ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         modelName,
       );
 
@@ -42,7 +42,7 @@ export class FieldsClauseTool extends Service {
       if (!field || typeof field !== 'string')
         throw new InvalidArgumentError(
           'The provided option "fields" should be a non-empty String ' +
-            'or an Array of non-empty String, but %v given.',
+            'or an Array of non-empty String, but %v was given.',
           field,
         );
     });
@@ -70,7 +70,7 @@ export class FieldsClauseTool extends Service {
       if (!field || typeof field !== 'string')
         throw new InvalidArgumentError(
           'The provided option "fields" should be a non-empty String ' +
-            'or an Array of non-empty String, but %v given.',
+            'or an Array of non-empty String, but %v was given.',
           field,
         );
     });
@@ -90,7 +90,7 @@ export class FieldsClauseTool extends Service {
       if (!field || typeof field !== 'string')
         throw new InvalidArgumentError(
           'The provided option "fields" should be a non-empty String ' +
-            'or an Array of non-empty String, but %v given.',
+            'or an Array of non-empty String, but %v was given.',
           field,
         );
     });

+ 16 - 16
src/filter/fields-clause-tool.spec.js

@@ -24,7 +24,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The first argument of FieldsClauseTool.filter should be an Object or ' +
-                'an Array of Object, but %s given.',
+                'an Array of Object, but %s was given.',
               v,
             );
           expect(throwable('str')).to.throw(error('"str"'));
@@ -45,7 +45,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The second argument of FieldsClauseTool.filter should be ' +
-                'a non-empty String, but %s given.',
+                'a non-empty String, but %s was given.',
               v,
             );
           expect(throwable('')).to.throw(error('""'));
@@ -64,7 +64,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The provided option "fields" should be a non-empty String ' +
-                'or an Array of non-empty String, but %s given.',
+                'or an Array of non-empty String, but %s was given.',
               v,
             );
           expect(throwable('')).to.throw(error('""'));
@@ -103,7 +103,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The first argument of FieldsClauseTool.filter should be an Object or ' +
-                'an Array of Object, but %s given.',
+                'an Array of Object, but %s was given.',
               v,
             );
           expect(throwable('str')).to.throw(error('"str"'));
@@ -124,7 +124,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The second argument of FieldsClauseTool.filter should be ' +
-                'a non-empty String, but %s given.',
+                'a non-empty String, but %s was given.',
               v,
             );
           expect(throwable('')).to.throw(error('""'));
@@ -143,7 +143,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The provided option "fields" should be a non-empty String ' +
-                'or an Array of non-empty String, but %s given.',
+                'or an Array of non-empty String, but %s was given.',
               v,
             );
           expect(throwable([''])).to.throw(error('""'));
@@ -189,7 +189,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The first argument of FieldsClauseTool.filter should be an Object or ' +
-                'an Array of Object, but %s given.',
+                'an Array of Object, but %s was given.',
               v,
             );
           expect(throwable(['str'])).to.throw(error('"str"'));
@@ -216,7 +216,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The second argument of FieldsClauseTool.filter should be ' +
-                'a non-empty String, but %s given.',
+                'a non-empty String, but %s was given.',
               v,
             );
           expect(throwable('')).to.throw(error('""'));
@@ -243,7 +243,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The provided option "fields" should be a non-empty String ' +
-                'or an Array of non-empty String, but %s given.',
+                'or an Array of non-empty String, but %s was given.',
               v,
             );
           expect(throwable('')).to.throw(error('""'));
@@ -306,7 +306,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The first argument of FieldsClauseTool.filter should be an Object or ' +
-                'an Array of Object, but %s given.',
+                'an Array of Object, but %s was given.',
               v,
             );
           expect(throwable(['str'])).to.throw(error('"str"'));
@@ -333,7 +333,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The second argument of FieldsClauseTool.filter should be ' +
-                'a non-empty String, but %s given.',
+                'a non-empty String, but %s was given.',
               v,
             );
           expect(throwable('')).to.throw(error('""'));
@@ -360,7 +360,7 @@ describe('FieldsClauseTool', function () {
           const error = v =>
             format(
               'The provided option "fields" should be a non-empty String ' +
-                'or an Array of non-empty String, but %s given.',
+                'or an Array of non-empty String, but %s was given.',
               v,
             );
           expect(throwable([''])).to.throw(error('""'));
@@ -414,7 +414,7 @@ describe('FieldsClauseTool', function () {
         const error = v =>
           format(
             'The provided option "fields" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));
@@ -435,7 +435,7 @@ describe('FieldsClauseTool', function () {
         const error = v =>
           format(
             'The provided option "fields" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable([''])).to.throw(error('""'));
@@ -460,7 +460,7 @@ describe('FieldsClauseTool', function () {
         const error = v =>
           format(
             'The provided option "fields" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));
@@ -487,7 +487,7 @@ describe('FieldsClauseTool', function () {
         const error = v =>
           format(
             'The provided option "fields" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable([''])).to.throw(error('""'));

+ 6 - 6
src/filter/include-clause-tool.js

@@ -197,7 +197,7 @@ export class IncludeClauseTool extends Service {
         if (!clause.relation || typeof clause.relation !== 'string')
           throw new InvalidArgumentError(
             'The provided option "relation" should be ' +
-              'a non-empty String, but %v given.',
+              'a non-empty String, but %v was given.',
             clause.relation,
           );
         if ('scope' in clause && clause) this.validateScopeClause(clause.scope);
@@ -213,7 +213,7 @@ export class IncludeClauseTool extends Service {
       // unsupported
       throw new InvalidArgumentError(
         'The provided option "include" should have a non-empty String, ' +
-          'an Object or an Array, but %v given.',
+          'an Object or an Array, but %v was given.',
         clause,
       );
     }
@@ -228,7 +228,7 @@ export class IncludeClauseTool extends Service {
     if (clause == null) return;
     if (typeof clause !== 'object' || Array.isArray(clause))
       throw new InvalidArgumentError(
-        'The provided option "scope" should be an Object, but %v given.',
+        'The provided option "scope" should be an Object, but %v was given.',
         clause,
       );
     // {where: ...}
@@ -294,7 +294,7 @@ export class IncludeClauseTool extends Service {
         if (!clause.relation || typeof clause.relation !== 'string')
           throw new InvalidArgumentError(
             'The provided option "relation" should be ' +
-              'a non-empty String, but %v given.',
+              'a non-empty String, but %v was given.',
             clause.relation,
           );
         const normalized = {relation: clause.relation};
@@ -316,7 +316,7 @@ export class IncludeClauseTool extends Service {
       // unsupported
       throw new InvalidArgumentError(
         'The provided option "include" should have a non-empty String, ' +
-          'an Object or an Array, but %v given.',
+          'an Object or an Array, but %v was given.',
         clause,
       );
     }
@@ -333,7 +333,7 @@ export class IncludeClauseTool extends Service {
     if (clause == null) return;
     if (typeof clause !== 'object' || Array.isArray(clause))
       throw new InvalidArgumentError(
-        'The provided option "scope" should be an Object, but %v given.',
+        'The provided option "scope" should be an Object, but %v was given.',
         clause,
       );
     const result = {};

+ 2 - 2
src/filter/include-clause-tool.spec.js

@@ -41,7 +41,7 @@ describe('IncludeClauseTool', function () {
       const createError = v =>
         format(
           'The provided option "include" should have a non-empty String, ' +
-            'an Object or an Array, but %v given.',
+            'an Object or an Array, but %v was given.',
           v,
         );
       const testOf = v => {
@@ -105,7 +105,7 @@ describe('IncludeClauseTool', function () {
       const createError = v =>
         format(
           'The provided option "include" should have a non-empty String, ' +
-            'an Object or an Array, but %v given.',
+            'an Object or an Array, but %v was given.',
           v,
         );
       const testOf = v => {

+ 13 - 13
src/filter/operator-clause-tool.js

@@ -59,7 +59,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object' || Array.isArray(clause))
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testAll ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
 
@@ -137,7 +137,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testEqNeq ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('eq' in clause) return this.compare(clause.eq, value) === 0;
@@ -183,7 +183,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testGtLt ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('gt' in clause) return this.compare(value, clause.gt) > 0;
@@ -210,7 +210,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testInq ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('inq' in clause && clause.inq !== undefined) {
@@ -246,7 +246,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testNin ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('nin' in clause && clause.nin !== undefined) {
@@ -282,7 +282,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testBetween ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('between' in clause && clause.between !== undefined) {
@@ -318,7 +318,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testExists ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('exists' in clause && clause.exists !== undefined) {
@@ -351,7 +351,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testLike ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('like' in clause && clause.like !== undefined) {
@@ -379,7 +379,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testNlike ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('nlike' in clause && clause.nlike !== undefined) {
@@ -411,7 +411,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testIlike ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('ilike' in clause && clause.ilike !== undefined) {
@@ -443,7 +443,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testNilike ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('nilike' in clause && clause.nilike !== undefined) {
@@ -487,7 +487,7 @@ export class OperatorClauseTool extends Service {
     if (!clause || typeof clause !== 'object')
       throw new InvalidArgumentError(
         'The first argument of OperatorUtils.testRegexp ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         clause,
       );
     if ('regexp' in clause && clause.regexp !== undefined) {
@@ -504,7 +504,7 @@ export class OperatorClauseTool extends Service {
       const flags = clause.flags || undefined;
       if (flags && typeof flags !== 'string')
         throw new InvalidArgumentError(
-          'RegExp flags should be a String, but %v given.',
+          'RegExp flags should be a String, but %v was given.',
           clause.flags,
         );
       if (!value || typeof value !== 'string') return false;

+ 13 - 13
src/filter/operator-clause-tool.spec.js

@@ -116,7 +116,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testAll(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testAll ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
   });
@@ -131,7 +131,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testEqNeq(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testEqNeq ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -216,7 +216,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testGtLt(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testGtLt ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -391,7 +391,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testInq(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testInq ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -446,7 +446,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testNin(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testNin ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -515,7 +515,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testBetween(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testBetween ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -572,7 +572,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testExists(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testExists ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -695,7 +695,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testLike(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testLike ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -774,7 +774,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testNlike(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testNlike ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -853,7 +853,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testIlike(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testIlike ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -932,7 +932,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testNilike(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testNilike ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -1023,7 +1023,7 @@ describe('OperatorClauseTool', function () {
       const throwable = () => S.testRegexp(10);
       expect(throwable).to.throw(
         'The first argument of OperatorUtils.testRegexp ' +
-          'should be an Object, but 10 given.',
+          'should be an Object, but 10 was given.',
       );
     });
 
@@ -1049,7 +1049,7 @@ describe('OperatorClauseTool', function () {
       const throwable = v => () =>
         S.testRegexp({regexp: 'Val.+', flags: v}, 'val');
       const error = v =>
-        format('RegExp flags should be a String, but %s given.', v);
+        format('RegExp flags should be a String, but %s was given.', v);
       expect(throwable(10)).to.throw(error('10'));
       expect(throwable(true)).to.throw(error('true'));
       expect(throwable([])).to.throw(error('Array'));

+ 3 - 3
src/filter/order-clause-tool.js

@@ -21,7 +21,7 @@ export class OrderClauseTool extends Service {
       if (!key || typeof key !== 'string')
         throw new InvalidArgumentError(
           'The provided option "order" should be a non-empty String ' +
-            'or an Array of non-empty String, but %v given.',
+            'or an Array of non-empty String, but %v was given.',
           key,
         );
       let reverse = 1;
@@ -48,7 +48,7 @@ export class OrderClauseTool extends Service {
       if (!field || typeof field !== 'string')
         throw new InvalidArgumentError(
           'The provided option "order" should be a non-empty String ' +
-            'or an Array of non-empty String, but %v given.',
+            'or an Array of non-empty String, but %v was given.',
           field,
         );
     });
@@ -68,7 +68,7 @@ export class OrderClauseTool extends Service {
       if (!field || typeof field !== 'string')
         throw new InvalidArgumentError(
           'The provided option "order" should be a non-empty String ' +
-            'or an Array of non-empty String, but %v given.',
+            'or an Array of non-empty String, but %v was given.',
           field,
         );
     });

+ 4 - 4
src/filter/order-clause-tool.spec.js

@@ -602,7 +602,7 @@ describe('OrderClauseTool', function () {
         const error = v =>
           format(
             'The provided option "order" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));
@@ -623,7 +623,7 @@ describe('OrderClauseTool', function () {
         const error = v =>
           format(
             'The provided option "order" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable([''])).to.throw(error('""'));
@@ -647,7 +647,7 @@ describe('OrderClauseTool', function () {
         const error = v =>
           format(
             'The provided option "order" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable('')).to.throw(error('""'));
@@ -673,7 +673,7 @@ describe('OrderClauseTool', function () {
         const error = v =>
           format(
             'The provided option "order" should be a non-empty String ' +
-              'or an Array of non-empty String, but %s given.',
+              'or an Array of non-empty String, but %s was given.',
             v,
           );
         expect(throwable([''])).to.throw(error('""'));

+ 5 - 5
src/filter/slice-clause-tool.js

@@ -17,17 +17,17 @@ export class SliceClauseTool extends Service {
     if (!Array.isArray(entities))
       throw new InvalidArgumentError(
         'The first argument of SliceClauseTool.slice ' +
-          'should be an Array, but %v given.',
+          'should be an Array, but %v was given.',
         entities,
       );
     if (skip != null && typeof skip !== 'number')
       throw new InvalidArgumentError(
-        'The provided option "skip" should be a Number, but %v given.',
+        'The provided option "skip" should be a Number, but %v was given.',
         skip,
       );
     if (limit != null && typeof limit !== 'number')
       throw new InvalidArgumentError(
-        'The provided option "limit" should be a Number, but %v given.',
+        'The provided option "limit" should be a Number, but %v was given.',
         limit,
       );
     skip = skip || 0;
@@ -44,7 +44,7 @@ export class SliceClauseTool extends Service {
     if (skip == null) return;
     if (typeof skip !== 'number')
       throw new InvalidArgumentError(
-        'The provided option "skip" should be a Number, but %v given.',
+        'The provided option "skip" should be a Number, but %v was given.',
         skip,
       );
   }
@@ -58,7 +58,7 @@ export class SliceClauseTool extends Service {
     if (limit == null) return;
     if (typeof limit !== 'number')
       throw new InvalidArgumentError(
-        'The provided option "limit" should be a Number, but %v given.',
+        'The provided option "limit" should be a Number, but %v was given.',
         limit,
       );
   }

+ 5 - 5
src/filter/slice-clause-tool.spec.js

@@ -11,7 +11,7 @@ describe('SliceClauseTool', function () {
       const error = v =>
         format(
           'The first argument of SliceClauseTool.slice ' +
-            'should be an Array, but %s given.',
+            'should be an Array, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -32,7 +32,7 @@ describe('SliceClauseTool', function () {
       const throwable = v => () => S.slice(items, v);
       const error = v =>
         format(
-          'The provided option "skip" should be a Number, but %s given.',
+          'The provided option "skip" should be a Number, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -52,7 +52,7 @@ describe('SliceClauseTool', function () {
       const throwable = v => () => S.slice(items, undefined, v);
       const error = v =>
         format(
-          'The provided option "limit" should be a Number, but %s given.',
+          'The provided option "limit" should be a Number, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -119,7 +119,7 @@ describe('SliceClauseTool', function () {
       const throwable = v => () => SliceClauseTool.validateSkipClause(v);
       const error = v =>
         format(
-          'The provided option "skip" should be a Number, but %s given.',
+          'The provided option "skip" should be a Number, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -140,7 +140,7 @@ describe('SliceClauseTool', function () {
       const throwable = v => () => SliceClauseTool.validateLimitClause(v);
       const error = v =>
         format(
-          'The provided option "limit" should be a Number, but %s given.',
+          'The provided option "limit" should be a Number, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));

+ 4 - 4
src/filter/where-clause-tool.js

@@ -42,7 +42,7 @@ export class WhereClauseTool extends Service {
     if (!Array.isArray(entities))
       throw new InvalidArgumentError(
         'The first argument of WhereClauseTool.filter should be ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (where == null) return entities;
@@ -58,7 +58,7 @@ export class WhereClauseTool extends Service {
   _createFilter(whereClause) {
     if (typeof whereClause !== 'object' || Array.isArray(whereClause))
       throw new InvalidArgumentError(
-        'The provided option "where" should be an Object, but %v given.',
+        'The provided option "where" should be an Object, but %v was given.',
         whereClause,
       );
     const keys = Object.keys(whereClause);
@@ -66,7 +66,7 @@ export class WhereClauseTool extends Service {
       if (typeof data !== 'object')
         throw new InvalidArgumentError(
           'The first argument of WhereClauseTool.filter should be ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           data,
         );
       return keys.every(key => {
@@ -161,7 +161,7 @@ export class WhereClauseTool extends Service {
     if (clause == null || typeof clause === 'function') return;
     if (typeof clause !== 'object' || Array.isArray(clause))
       throw new InvalidArgumentError(
-        'The provided option "where" should be an Object, but %v given.',
+        'The provided option "where" should be an Object, but %v was given.',
         clause,
       );
   }

+ 3 - 3
src/filter/where-clause-tool.spec.js

@@ -49,7 +49,7 @@ describe('WhereClauseTool', function () {
       const error = v =>
         format(
           'The first argument of WhereClauseTool.filter should be ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -69,7 +69,7 @@ describe('WhereClauseTool', function () {
       const throwable = v => () => S.filter(OBJECTS, v);
       const error = v =>
         format(
-          'The provided option "where" should be an Object, but %s given.',
+          'The provided option "where" should be an Object, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));
@@ -284,7 +284,7 @@ describe('WhereClauseTool', function () {
       const throwable = v => () => WhereClauseTool.validateWhereClause(v);
       const error = v =>
         format(
-          'The provided option "where" should be an Object, but %s given.',
+          'The provided option "where" should be an Object, but %s was given.',
           v,
         );
       expect(throwable('str')).to.throw(error('"str"'));

+ 14 - 14
src/relations/belongs-to-resolver.js

@@ -31,37 +31,37 @@ export class BelongsToResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of BelongsToResolver.includeTo requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of BelongsToResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of BelongsToResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of BelongsToResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (foreignKey && typeof foreignKey !== 'string')
       throw new InvalidArgumentError(
         'The provided parameter "foreignKey" of BelongsToResolver.includeTo ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         foreignKey,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of BelongsToResolver.includeTo ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
     if (foreignKey == null) foreignKey = `${relationName}Id`;
@@ -69,7 +69,7 @@ export class BelongsToResolver extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The parameter "entities" of BelongsToResolver.includeTo requires ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
       const targetId = entity[foreignKey];
@@ -120,37 +120,37 @@ export class BelongsToResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of BelongsToResolver.includePolymorphicTo ' +
-          'requires an Array of Object, but %v given.',
+          'requires an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of BelongsToResolver.includePolymorphicTo ' +
-          'requires a non-empty String, but %v given.',
+          'requires a non-empty String, but %v was given.',
         sourceName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of BelongsToResolver.includePolymorphicTo ' +
-          'requires a non-empty String, but %v given.',
+          'requires a non-empty String, but %v was given.',
         relationName,
       );
     if (foreignKey && typeof foreignKey !== 'string')
       throw new InvalidArgumentError(
         'The provided parameter "foreignKey" of BelongsToResolver.includePolymorphicTo ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         foreignKey,
       );
     if (discriminator && typeof discriminator !== 'string')
       throw new InvalidArgumentError(
         'The provided parameter "discriminator" of BelongsToResolver.includePolymorphicTo ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         discriminator,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of BelongsToResolver.includePolymorphicTo ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
     if (foreignKey == null) {
@@ -166,7 +166,7 @@ export class BelongsToResolver extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The parameter "entities" of BelongsToResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
       const targetId = entity[foreignKey];

+ 14 - 14
src/relations/belongs-to-resolver.spec.js

@@ -14,7 +14,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of BelongsToResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -35,7 +35,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of BelongsToResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -56,7 +56,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of BelongsToResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v => R.includeTo([], v, 'targetName', 'relationName');
@@ -76,7 +76,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of BelongsToResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v => R.includeTo([], 'sourceName', v, 'relationName');
@@ -96,7 +96,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of BelongsToResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v => R.includeTo([], 'sourceName', 'targetName', v);
@@ -116,7 +116,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The provided parameter "foreignKey" of BelongsToResolver.includeTo ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -133,7 +133,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of BelongsToResolver.includeTo ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -528,7 +528,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of BelongsToResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -549,7 +549,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of BelongsToResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -570,7 +570,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of BelongsToResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -591,7 +591,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of BelongsToResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v => R.includePolymorphicTo([], 'sourceName', v);
@@ -611,7 +611,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The provided parameter "foreignKey" of BelongsToResolver.includePolymorphicTo ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -628,7 +628,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The provided parameter "discriminator" of BelongsToResolver.includePolymorphicTo ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -645,7 +645,7 @@ describe('BelongsToResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of BelongsToResolver.includePolymorphicTo ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>

+ 22 - 22
src/relations/has-many-resolver.js

@@ -31,37 +31,37 @@ export class HasManyResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of HasManyResolver.includeTo requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of HasManyResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of HasManyResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of HasManyResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (!foreignKey || typeof foreignKey !== 'string')
       throw new InvalidArgumentError(
         'The parameter "foreignKey" of HasManyResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         foreignKey,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of HasManyResolver.includeTo ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
 
@@ -74,7 +74,7 @@ export class HasManyResolver extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The parameter "entities" of HasManyResolver.includeTo requires ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
       const sourceId = entity[sourcePkPropName];
@@ -134,43 +134,43 @@ export class HasManyResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of HasManyResolver.includePolymorphicTo requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of HasManyResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of HasManyResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of HasManyResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (!foreignKey || typeof foreignKey !== 'string')
       throw new InvalidArgumentError(
         'The parameter "foreignKey" of HasManyResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         foreignKey,
       );
     if (!discriminator || typeof discriminator !== 'string')
       throw new InvalidArgumentError(
         'The parameter "discriminator" of HasManyResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         discriminator,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of HasManyResolver.includePolymorphicTo ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
 
@@ -183,7 +183,7 @@ export class HasManyResolver extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The parameter "entities" of HasManyResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
       const sourceId = entity[sourcePkPropName];
@@ -244,37 +244,37 @@ export class HasManyResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of HasManyResolver.includePolymorphicByRelationName requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (!targetRelationName || typeof targetRelationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetRelationName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetRelationName,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of HasManyResolver.includePolymorphicByRelationName ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
 
@@ -285,7 +285,7 @@ export class HasManyResolver extends Service {
       throw new InvalidArgumentError(
         'The relation %v of the model %v is a polymorphic "hasMany" relation, ' +
           'so it requires the target relation %v to be a polymorphic "belongsTo", ' +
-          'but %v type given.',
+          'but %v type was given.',
         relationName,
         sourceName,
         targetRelationName,

+ 23 - 23
src/relations/has-many-resolver.spec.js

@@ -14,7 +14,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasManyResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -42,7 +42,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasManyResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -63,7 +63,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of HasManyResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -84,7 +84,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of HasManyResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -105,7 +105,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of HasManyResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -126,7 +126,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "foreignKey" of HasManyResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -147,7 +147,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of HasManyResolver.includeTo ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -726,7 +726,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasManyResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -755,7 +755,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasManyResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -783,7 +783,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of HasManyResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -811,7 +811,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of HasManyResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -839,7 +839,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of HasManyResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -867,7 +867,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "foreignKey" of HasManyResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -895,7 +895,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "discriminator" of HasManyResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -923,7 +923,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of HasManyResolver.includePolymorphicTo ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1685,7 +1685,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasManyResolver.includePolymorphicByRelationName requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1722,7 +1722,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasManyResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1749,7 +1749,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1776,7 +1776,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1803,7 +1803,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1830,7 +1830,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The parameter "targetRelationName" of HasManyResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1857,7 +1857,7 @@ describe('HasManyResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of HasManyResolver.includePolymorphicByRelationName ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1934,7 +1934,7 @@ describe('HasManyResolver', function () {
       await expect(promise).to.be.rejectedWith(
         'The relation "children" of the model "source" is a polymorphic "hasMany" relation, ' +
           'so it requires the target relation "parent" to be a polymorphic "belongsTo", ' +
-          'but "referencesMany" type given.',
+          'but "referencesMany" type was given.',
       );
     });
 

+ 22 - 22
src/relations/has-one-resolver.js

@@ -31,37 +31,37 @@ export class HasOneResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of HasOneResolver.includeTo requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of HasOneResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of HasOneResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of HasOneResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (!foreignKey || typeof foreignKey !== 'string')
       throw new InvalidArgumentError(
         'The parameter "foreignKey" of HasOneResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         foreignKey,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of HasOneResolver.includeTo ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
 
@@ -74,7 +74,7 @@ export class HasOneResolver extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The parameter "entities" of HasOneResolver.includeTo requires ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
       const sourceId = entity[sourcePkPropName];
@@ -131,43 +131,43 @@ export class HasOneResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of HasOneResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of HasOneResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of HasOneResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (!foreignKey || typeof foreignKey !== 'string')
       throw new InvalidArgumentError(
         'The parameter "foreignKey" of HasOneResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         foreignKey,
       );
     if (!discriminator || typeof discriminator !== 'string')
       throw new InvalidArgumentError(
         'The parameter "discriminator" of HasOneResolver.includePolymorphicTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         discriminator,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of HasOneResolver.includePolymorphicTo ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
 
@@ -180,7 +180,7 @@ export class HasOneResolver extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
       const sourceId = entity[sourcePkPropName];
@@ -238,37 +238,37 @@ export class HasOneResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of HasOneResolver.includePolymorphicByRelationName requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (!targetRelationName || typeof targetRelationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetRelationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetRelationName,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of HasOneResolver.includePolymorphicByRelationName ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
 
@@ -279,7 +279,7 @@ export class HasOneResolver extends Service {
       throw new InvalidArgumentError(
         'The relation %v of the model %v is a polymorphic "hasOne" relation, ' +
           'so it requires the target relation %v to be a polymorphic "belongsTo", ' +
-          'but %v type given.',
+          'but %v type was given.',
         relationName,
         sourceName,
         targetRelationName,

+ 23 - 23
src/relations/has-one-resolver.spec.js

@@ -14,7 +14,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasOneResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -42,7 +42,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasOneResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -63,7 +63,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of HasOneResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -84,7 +84,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of HasOneResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -105,7 +105,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of HasOneResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -126,7 +126,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "foreignKey" of HasOneResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -147,7 +147,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of HasOneResolver.includeTo ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -557,7 +557,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -586,7 +586,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -614,7 +614,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of HasOneResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -642,7 +642,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of HasOneResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -670,7 +670,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of HasOneResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -698,7 +698,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "foreignKey" of HasOneResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -726,7 +726,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "discriminator" of HasOneResolver.includePolymorphicTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -754,7 +754,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of HasOneResolver.includePolymorphicTo ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1307,7 +1307,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasOneResolver.includePolymorphicByRelationName requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1344,7 +1344,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1371,7 +1371,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1398,7 +1398,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1425,7 +1425,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1452,7 +1452,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The parameter "targetRelationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1479,7 +1479,7 @@ describe('HasOneResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of HasOneResolver.includePolymorphicByRelationName ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -1556,7 +1556,7 @@ describe('HasOneResolver', function () {
       await expect(promise).to.be.rejectedWith(
         'The relation "child" of the model "source" is a polymorphic "hasOne" relation, ' +
           'so it requires the target relation "parent" to be a polymorphic "belongsTo", ' +
-          'but "referencesMany" type given.',
+          'but "referencesMany" type was given.',
       );
     });
 

+ 7 - 7
src/relations/references-many-resolver.js

@@ -31,37 +31,37 @@ export class ReferencesManyResolver extends Service {
     if (!entities || !Array.isArray(entities))
       throw new InvalidArgumentError(
         'The parameter "entities" of ReferencesManyResolver.includeTo requires ' +
-          'an Array of Object, but %v given.',
+          'an Array of Object, but %v was given.',
         entities,
       );
     if (!sourceName || typeof sourceName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "sourceName" of ReferencesManyResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         sourceName,
       );
     if (!targetName || typeof targetName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "targetName" of ReferencesManyResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         targetName,
       );
     if (!relationName || typeof relationName !== 'string')
       throw new InvalidArgumentError(
         'The parameter "relationName" of ReferencesManyResolver.includeTo requires ' +
-          'a non-empty String, but %v given.',
+          'a non-empty String, but %v was given.',
         relationName,
       );
     if (foreignKey && typeof foreignKey !== 'string')
       throw new InvalidArgumentError(
         'The provided parameter "foreignKey" of ReferencesManyResolver.includeTo ' +
-          'should be a String, but %v given.',
+          'should be a String, but %v was given.',
         foreignKey,
       );
     if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
       throw new InvalidArgumentError(
         'The provided parameter "scope" of ReferencesManyResolver.includeTo ' +
-          'should be an Object, but %v given.',
+          'should be an Object, but %v was given.',
         scope,
       );
     if (foreignKey == null) {
@@ -72,7 +72,7 @@ export class ReferencesManyResolver extends Service {
       if (!entity || typeof entity !== 'object' || Array.isArray(entity))
         throw new InvalidArgumentError(
           'The parameter "entities" of ReferencesManyResolver.includeTo requires ' +
-            'an Array of Object, but %v given.',
+            'an Array of Object, but %v was given.',
           entity,
         );
       const ids = entity[foreignKey];

+ 7 - 7
src/relations/references-many-resolver.spec.js

@@ -14,7 +14,7 @@ describe('ReferencesManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of ReferencesManyResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -35,7 +35,7 @@ describe('ReferencesManyResolver', function () {
       const error = v =>
         format(
           'The parameter "entities" of ReferencesManyResolver.includeTo requires ' +
-            'an Array of Object, but %s given.',
+            'an Array of Object, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -56,7 +56,7 @@ describe('ReferencesManyResolver', function () {
       const error = v =>
         format(
           'The parameter "sourceName" of ReferencesManyResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v => R.includeTo([], v, 'targetName', 'relationName');
@@ -76,7 +76,7 @@ describe('ReferencesManyResolver', function () {
       const error = v =>
         format(
           'The parameter "targetName" of ReferencesManyResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v => R.includeTo([], 'sourceName', v, 'relationName');
@@ -96,7 +96,7 @@ describe('ReferencesManyResolver', function () {
       const error = v =>
         format(
           'The parameter "relationName" of ReferencesManyResolver.includeTo requires ' +
-            'a non-empty String, but %s given.',
+            'a non-empty String, but %s was given.',
           v,
         );
       const throwable = v => R.includeTo([], 'sourceName', 'targetName', v);
@@ -116,7 +116,7 @@ describe('ReferencesManyResolver', function () {
       const error = v =>
         format(
           'The provided parameter "foreignKey" of ReferencesManyResolver.includeTo ' +
-            'should be a String, but %s given.',
+            'should be a String, but %s was given.',
           v,
         );
       const throwable = v =>
@@ -133,7 +133,7 @@ describe('ReferencesManyResolver', function () {
       const error = v =>
         format(
           'The provided parameter "scope" of ReferencesManyResolver.includeTo ' +
-            'should be an Object, but %s given.',
+            'should be an Object, but %s was given.',
           v,
         );
       const throwable = v =>

+ 1 - 1
src/repository/repository-registry.js

@@ -35,7 +35,7 @@ export class RepositoryRegistry extends Service {
     ) {
       throw new InvalidArgumentError(
         'The first argument of RepositoryRegistry.setRepositoryCtor ' +
-          'must inherit from Repository class, but %v given.',
+          'must inherit from Repository class, but %v was given.',
         ctor,
       );
     }

+ 1 - 1
src/utils/exclude-object-keys.js

@@ -10,7 +10,7 @@ import {InvalidArgumentError} from '../errors/index.js';
 export function excludeObjectKeys(obj, keys) {
   if (typeof obj !== 'object' || !obj || Array.isArray(obj))
     throw new InvalidArgumentError(
-      'Cannot exclude keys from a non-Object value, %v given.',
+      'Cannot exclude keys from a non-Object value, %v was given.',
       obj,
     );
   const result = {...obj};

+ 1 - 1
src/utils/exclude-object-keys.spec.js

@@ -37,7 +37,7 @@ describe('excludeObjectKeys', function () {
   it('throws an error for a non-object values', function () {
     const throwable = v => () => excludeObjectKeys(v, 'key');
     const error = v =>
-      format('Cannot exclude keys from a non-Object value, %s given.', v);
+      format('Cannot exclude keys from a non-Object value, %s was given.', v);
     expect(throwable('str')).to.throw(error('"str"'));
     expect(throwable(10)).to.throw(error('10'));
     expect(throwable(true)).to.throw(error('true'));

+ 1 - 1
src/utils/model-name-to-model-key.js

@@ -10,7 +10,7 @@ export function modelNameToModelKey(modelName) {
   if (!modelName || typeof modelName !== 'string' || /\s/.test(modelName))
     throw new InvalidArgumentError(
       'The model name should be a non-empty String ' +
-        'without spaces, but %v given.',
+        'without spaces, but %v was given.',
       modelName,
     );
   return modelName.toLowerCase().replace(/[-_]/g, '');

+ 7 - 7
src/utils/model-name-to-model-key.spec.js

@@ -38,7 +38,7 @@ describe('modelNameToModelKey', function () {
     const throwable = () => modelNameToModelKey('');
     expect(throwable).to.throw(
       'The model name should be a non-empty String ' +
-        'without spaces, but "" given.',
+        'without spaces, but "" was given.',
     );
   });
 
@@ -46,7 +46,7 @@ describe('modelNameToModelKey', function () {
     const throwable = () => modelNameToModelKey('user profile');
     expect(throwable).to.throw(
       'The model name should be a non-empty String ' +
-        'without spaces, but "user profile" given.',
+        'without spaces, but "user profile" was given.',
     );
   });
 
@@ -54,7 +54,7 @@ describe('modelNameToModelKey', function () {
     const throwable = () => modelNameToModelKey(null);
     expect(throwable).to.throw(
       'The model name should be a non-empty String ' +
-        'without spaces, but null given.',
+        'without spaces, but null was given.',
     );
   });
 
@@ -62,7 +62,7 @@ describe('modelNameToModelKey', function () {
     const throwable = () => modelNameToModelKey(undefined);
     expect(throwable).to.throw(
       'The model name should be a non-empty String ' +
-        'without spaces, but undefined given.',
+        'without spaces, but undefined was given.',
     );
   });
 
@@ -70,7 +70,7 @@ describe('modelNameToModelKey', function () {
     const throwable = () => modelNameToModelKey(123);
     expect(throwable).to.throw(
       'The model name should be a non-empty String ' +
-        'without spaces, but 123 given.',
+        'without spaces, but 123 was given.',
     );
   });
 
@@ -78,7 +78,7 @@ describe('modelNameToModelKey', function () {
     const throwable = () => modelNameToModelKey({name: 'test'});
     expect(throwable).to.throw(
       'The model name should be a non-empty String ' +
-        'without spaces, but Object given.',
+        'without spaces, but Object was given.',
     );
   });
 
@@ -86,7 +86,7 @@ describe('modelNameToModelKey', function () {
     const throwable = () => modelNameToModelKey(['test']);
     expect(throwable).to.throw(
       'The model name should be a non-empty String ' +
-        'without spaces, but Array given.',
+        'without spaces, but Array was given.',
     );
   });
 });

+ 3 - 3
src/utils/select-object-keys.js

@@ -11,20 +11,20 @@ export function selectObjectKeys(obj, keys) {
   if (!obj || typeof obj !== 'object' || Array.isArray(obj))
     throw new InvalidArgumentError(
       'The first argument of selectObjectKeys ' +
-        'should be an Object, but %v given.',
+        'should be an Object, but %v was given.',
       obj,
     );
   if (!Array.isArray(keys))
     throw new InvalidArgumentError(
       'The second argument of selectObjectKeys ' +
-        'should be an Array of String, but %v given.',
+        'should be an Array of String, but %v was given.',
       keys,
     );
   keys.forEach(key => {
     if (typeof key !== 'string')
       throw new InvalidArgumentError(
         'The second argument of selectObjectKeys ' +
-          'should be an Array of String, but %v given.',
+          'should be an Array of String, but %v was given.',
         key,
       );
   });

+ 3 - 3
src/utils/select-object-keys.spec.js

@@ -18,7 +18,7 @@ describe('selectObjectKeys', function () {
     const throwable = () => selectObjectKeys(10, ['key']);
     expect(throwable).to.throw(
       'The first argument of selectObjectKeys ' +
-        'should be an Object, but 10 given.',
+        'should be an Object, but 10 was given.',
     );
   });
 
@@ -26,7 +26,7 @@ describe('selectObjectKeys', function () {
     const throwable = () => selectObjectKeys({});
     expect(throwable).to.throw(
       'The second argument of selectObjectKeys ' +
-        'should be an Array of String, but undefined given.',
+        'should be an Array of String, but undefined was given.',
     );
   });
 
@@ -34,7 +34,7 @@ describe('selectObjectKeys', function () {
     const throwable = () => selectObjectKeys({}, [10]);
     expect(throwable).to.throw(
       'The second argument of selectObjectKeys ' +
-        'should be an Array of String, but 10 given.',
+        'should be an Array of String, but 10 was given.',
     );
   });
 });

Некоторые файлы не были показаны из-за большого количества измененных файлов