@naturalcycles/nodejs-lib 15.12.0 → 15.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,12 +5,19 @@ import type { Ajv } from 'ajv';
5
5
  import { AjvValidationError } from './ajvValidationError.js';
6
6
  export interface AjvValidationOptions {
7
7
  /**
8
- * Defaults to false.
8
+ * Defaults to true,
9
+ * because that's how AJV works by default,
10
+ * and what gives it performance advantage.
11
+ * (Because we have found that deep-clone is surprisingly slow,
12
+ * nearly as slow as Joi validation).
9
13
  *
10
14
  * If set to true - AJV will mutate the input in case it needs to apply transformations
11
15
  * (strip unknown properties, convert types, etc).
12
16
  *
13
- * If false - it will deep-clone the input to prevent its mutation. Will return the cloned/mutated object.
17
+ * If false - it will deep-clone (using JSON.stringify+parse) the input to prevent its mutation.
18
+ * Will return the cloned/mutated object.
19
+ * Please note that JSON.stringify+parse has side-effects,
20
+ * e.g it will transform Buffer into a weird object.
14
21
  */
15
22
  mutateInput?: boolean;
16
23
  inputName?: string;
@@ -57,6 +64,9 @@ export declare class AjvSchema<T = unknown> {
57
64
  * correctly for some reason.
58
65
  */
59
66
  static create<T>(schema: JsonSchemaBuilder<T> | JsonSchema<T> | AjvSchema<T>, cfg?: Partial<AjvSchemaCfg>): AjvSchema<T>;
67
+ /**
68
+ * @experimental
69
+ */
60
70
  static createFromZod<T>(zodSchema: ZodType<T>, cfg?: Partial<AjvSchemaCfg>): AjvSchema<T>;
61
71
  readonly cfg: AjvSchemaCfg;
62
72
  /**
@@ -1,6 +1,6 @@
1
1
  import { _isObject, _lazyValue, } from '@naturalcycles/js-lib';
2
2
  import { JsonSchemaAnyBuilder } from '@naturalcycles/js-lib/json-schema';
3
- import { _filterNullishValues } from '@naturalcycles/js-lib/object';
3
+ import { _deepCopy, _filterNullishValues } from '@naturalcycles/js-lib/object';
4
4
  import { _substringBefore } from '@naturalcycles/js-lib/string';
5
5
  import { z } from '@naturalcycles/js-lib/zod';
6
6
  import { _inspect } from '../../string/inspect.js';
@@ -51,6 +51,9 @@ export class AjvSchema {
51
51
  }
52
52
  return new AjvSchema(schema, cfg);
53
53
  }
54
+ /**
55
+ * @experimental
56
+ */
54
57
  static createFromZod(zodSchema, cfg) {
55
58
  const jsonSchema = z.toJSONSchema(zodSchema, { target: 'draft-7' });
56
59
  return new AjvSchema(jsonSchema, cfg);
@@ -71,12 +74,16 @@ export class AjvSchema {
71
74
  return output;
72
75
  }
73
76
  isValid(input, opt) {
77
+ // todo: we can make it both fast and non-mutating by using Ajv
78
+ // with "removeAdditional" and "useDefaults" disabled.
74
79
  const [err] = this.getValidationResult(input, opt);
75
80
  return !err;
76
81
  }
77
82
  getValidationResult(input, opt = {}) {
78
83
  const fn = this.getAJVValidateFunction();
79
- const item = opt.mutateInput || typeof input !== 'object' ? input : structuredClone(input);
84
+ const item = opt.mutateInput !== false || typeof input !== 'object'
85
+ ? input // mutate
86
+ : _deepCopy(input); // not mutate
80
87
  const valid = fn(item); // mutates item
81
88
  if (valid)
82
89
  return [null, item];
@@ -87,6 +94,8 @@ export class AjvSchema {
87
94
  dataVar,
88
95
  separator,
89
96
  });
97
+ // Note: if we mutated the input already, e.g stripped unknown properties,
98
+ // the error message Input would contain already mutated object print, such as Input: {}
90
99
  const inputStringified = _inspect(input, { maxLen: 4000 });
91
100
  message = [message, 'Input: ' + inputStringified].join(separator);
92
101
  const err = new AjvValidationError(message, _filterNullishValues({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.12.0",
4
+ "version": "15.14.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -6,7 +6,7 @@ import {
6
6
  } from '@naturalcycles/js-lib'
7
7
  import type { JsonSchema, JsonSchemaBuilder } from '@naturalcycles/js-lib/json-schema'
8
8
  import { JsonSchemaAnyBuilder } from '@naturalcycles/js-lib/json-schema'
9
- import { _filterNullishValues } from '@naturalcycles/js-lib/object'
9
+ import { _deepCopy, _filterNullishValues } from '@naturalcycles/js-lib/object'
10
10
  import { _substringBefore } from '@naturalcycles/js-lib/string'
11
11
  import { z, type ZodType } from '@naturalcycles/js-lib/zod'
12
12
  import type { Ajv } from 'ajv'
@@ -16,12 +16,19 @@ import { getAjv } from './getAjv.js'
16
16
 
17
17
  export interface AjvValidationOptions {
18
18
  /**
19
- * Defaults to false.
19
+ * Defaults to true,
20
+ * because that's how AJV works by default,
21
+ * and what gives it performance advantage.
22
+ * (Because we have found that deep-clone is surprisingly slow,
23
+ * nearly as slow as Joi validation).
20
24
  *
21
25
  * If set to true - AJV will mutate the input in case it needs to apply transformations
22
26
  * (strip unknown properties, convert types, etc).
23
27
  *
24
- * If false - it will deep-clone the input to prevent its mutation. Will return the cloned/mutated object.
28
+ * If false - it will deep-clone (using JSON.stringify+parse) the input to prevent its mutation.
29
+ * Will return the cloned/mutated object.
30
+ * Please note that JSON.stringify+parse has side-effects,
31
+ * e.g it will transform Buffer into a weird object.
25
32
  */
26
33
  mutateInput?: boolean
27
34
  inputName?: string
@@ -108,6 +115,9 @@ export class AjvSchema<T = unknown> {
108
115
  return new AjvSchema<T>(schema as JsonSchema<T>, cfg)
109
116
  }
110
117
 
118
+ /**
119
+ * @experimental
120
+ */
111
121
  static createFromZod<T>(zodSchema: ZodType<T>, cfg?: Partial<AjvSchemaCfg>): AjvSchema<T> {
112
122
  const jsonSchema = z.toJSONSchema(zodSchema, { target: 'draft-7' })
113
123
  return new AjvSchema<T>(jsonSchema as JsonSchema<T>, cfg)
@@ -130,6 +140,8 @@ export class AjvSchema<T = unknown> {
130
140
  }
131
141
 
132
142
  isValid(input: T, opt?: AjvValidationOptions): boolean {
143
+ // todo: we can make it both fast and non-mutating by using Ajv
144
+ // with "removeAdditional" and "useDefaults" disabled.
133
145
  const [err] = this.getValidationResult(input, opt)
134
146
  return !err
135
147
  }
@@ -140,7 +152,10 @@ export class AjvSchema<T = unknown> {
140
152
  ): ValidationFunctionResult<T, AjvValidationError> {
141
153
  const fn = this.getAJVValidateFunction()
142
154
 
143
- const item = opt.mutateInput || typeof input !== 'object' ? input : structuredClone(input)
155
+ const item =
156
+ opt.mutateInput !== false || typeof input !== 'object'
157
+ ? input // mutate
158
+ : _deepCopy(input) // not mutate
144
159
 
145
160
  const valid = fn(item) // mutates item
146
161
  if (valid) return [null, item]
@@ -158,6 +173,8 @@ export class AjvSchema<T = unknown> {
158
173
  separator,
159
174
  })
160
175
 
176
+ // Note: if we mutated the input already, e.g stripped unknown properties,
177
+ // the error message Input would contain already mutated object print, such as Input: {}
161
178
  const inputStringified = _inspect(input, { maxLen: 4000 })
162
179
  message = [message, 'Input: ' + inputStringified].join(separator)
163
180