@naturalcycles/nodejs-lib 15.37.2 → 15.39.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.
- package/dist/log/log.util.d.ts +1 -1
- package/dist/log/log.util.js +1 -1
- package/dist/security/nanoid.js +1 -1
- package/dist/validation/ajv/ajvSchema.d.ts +54 -55
- package/dist/validation/ajv/ajvSchema.js +30 -13
- package/dist/validation/ajv/getAjv.d.ts +1 -2
- package/dist/validation/ajv/getAjv.js +226 -78
- package/dist/validation/ajv/index.d.ts +1 -0
- package/dist/validation/ajv/index.js +1 -0
- package/dist/validation/ajv/jsonSchemaBuilder.d.ts +225 -0
- package/dist/validation/ajv/jsonSchemaBuilder.js +453 -0
- package/dist/validation/ajv/tlds.d.ts +1 -0
- package/dist/validation/ajv/tlds.js +1445 -0
- package/dist/validation/joi/joi.validation.util.d.ts +1 -1
- package/package.json +1 -3
- package/src/log/log.util.ts +1 -1
- package/src/security/nanoid.ts +1 -1
- package/src/validation/ajv/ajvSchema.ts +131 -92
- package/src/validation/ajv/getAjv.ts +245 -88
- package/src/validation/ajv/index.ts +1 -0
- package/src/validation/ajv/jsonSchemaBuilder.ts +752 -0
- package/src/validation/ajv/tlds.ts +1448 -0
- package/src/validation/joi/joi.extensions.ts +2 -2
- package/src/validation/joi/joi.validation.util.ts +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ValidationFunction, type ValidationFunctionResult } from '@naturalcycles/js-lib';
|
|
2
2
|
import type { AnySchema, ValidationOptions } from 'joi';
|
|
3
3
|
import { JoiValidationError } from './joi.validation.error.js';
|
|
4
|
-
export declare function getJoiValidationFunction<T>(schema: AnySchema<T>): ValidationFunction<T, JoiValidationError>;
|
|
4
|
+
export declare function getJoiValidationFunction<T>(schema: AnySchema<T>): ValidationFunction<T, T, JoiValidationError>;
|
|
5
5
|
/**
|
|
6
6
|
* Validates with Joi.
|
|
7
7
|
* Throws JoiValidationError if invalid.
|
package/package.json
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/nodejs-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.39.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@naturalcycles/js-lib": "^15",
|
|
7
7
|
"@types/js-yaml": "^4",
|
|
8
8
|
"@types/jsonwebtoken": "^9",
|
|
9
9
|
"@types/yargs": "^16",
|
|
10
10
|
"ajv": "^8",
|
|
11
|
-
"ajv-formats": "^3",
|
|
12
|
-
"ajv-keywords": "^5",
|
|
13
11
|
"chalk": "^5",
|
|
14
12
|
"dotenv": "^17",
|
|
15
13
|
"joi": "^18",
|
package/src/log/log.util.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { commonLoggerCreate } from '@naturalcycles/js-lib/log'
|
|
|
2
2
|
import { _inspect } from '../string/inspect.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* CommonLogger that logs to process.stdout directly (bypassing console.log)
|
|
5
|
+
* CommonLogger that logs to process.stdout directly (bypassing console.log)!
|
|
6
6
|
*/
|
|
7
7
|
export const stdoutLogger = commonLoggerCreate((_level, args) => {
|
|
8
8
|
process.stdout.write(args.map(a => _inspect(a)).join(' ') + '\n')
|
package/src/security/nanoid.ts
CHANGED
|
@@ -4,72 +4,25 @@ import {
|
|
|
4
4
|
type ValidationFunction,
|
|
5
5
|
type ValidationFunctionResult,
|
|
6
6
|
} from '@naturalcycles/js-lib'
|
|
7
|
-
import
|
|
8
|
-
import { JsonSchemaAnyBuilder } from '@naturalcycles/js-lib/json-schema'
|
|
7
|
+
import { _assert } from '@naturalcycles/js-lib/error'
|
|
9
8
|
import { _deepCopy, _filterNullishValues } from '@naturalcycles/js-lib/object'
|
|
10
9
|
import { _substringBefore } from '@naturalcycles/js-lib/string'
|
|
11
|
-
import type
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
10
|
+
import { _typeCast, type AnyObject } from '@naturalcycles/js-lib/types'
|
|
11
|
+
import type { ZodType } from '@naturalcycles/js-lib/zod'
|
|
12
|
+
import { z } from '@naturalcycles/js-lib/zod'
|
|
13
|
+
import type { Ajv, ErrorObject } from 'ajv'
|
|
14
14
|
import { _inspect } from '../../string/inspect.js'
|
|
15
15
|
import { AjvValidationError } from './ajvValidationError.js'
|
|
16
16
|
import { getAjv } from './getAjv.js'
|
|
17
|
-
|
|
18
|
-
export type SchemaHandledByAjv<T> = JsonSchemaBuilder<T> | JsonSchema<T> | AjvSchema<T> | ZodType<T>
|
|
19
|
-
|
|
20
|
-
export interface AjvValidationOptions {
|
|
21
|
-
/**
|
|
22
|
-
* Defaults to true,
|
|
23
|
-
* because that's how AJV works by default,
|
|
24
|
-
* and what gives it performance advantage.
|
|
25
|
-
* (Because we have found that deep-clone is surprisingly slow,
|
|
26
|
-
* nearly as slow as Joi validation).
|
|
27
|
-
*
|
|
28
|
-
* If set to true - AJV will mutate the input in case it needs to apply transformations
|
|
29
|
-
* (strip unknown properties, convert types, etc).
|
|
30
|
-
*
|
|
31
|
-
* If false - it will deep-clone (using JSON.stringify+parse) the input to prevent its mutation.
|
|
32
|
-
* Will return the cloned/mutated object.
|
|
33
|
-
* Please note that JSON.stringify+parse has side-effects,
|
|
34
|
-
* e.g it will transform Buffer into a weird object.
|
|
35
|
-
*/
|
|
36
|
-
mutateInput?: boolean
|
|
37
|
-
inputName?: string
|
|
38
|
-
inputId?: string
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface AjvSchemaCfg {
|
|
42
|
-
/**
|
|
43
|
-
* Pass Ajv instance, otherwise Ajv will be created with
|
|
44
|
-
* AjvSchema default (not the same as Ajv defaults) parameters
|
|
45
|
-
*/
|
|
46
|
-
ajv: Ajv
|
|
47
|
-
|
|
48
|
-
inputName?: string
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Option of Ajv.
|
|
52
|
-
* If set to true - will mutate your input objects!
|
|
53
|
-
* Defaults to false.
|
|
54
|
-
*
|
|
55
|
-
* This option is a "shortcut" to skip creating and passing Ajv instance.
|
|
56
|
-
*/
|
|
57
|
-
// coerceTypes?: boolean
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* If true - schema will be compiled on-demand (lazily).
|
|
61
|
-
* Default: false.
|
|
62
|
-
*/
|
|
63
|
-
lazy?: boolean
|
|
64
|
-
}
|
|
17
|
+
import { type JsonSchema, JsonSchemaAnyBuilder } from './jsonSchemaBuilder.js'
|
|
65
18
|
|
|
66
19
|
/**
|
|
67
20
|
* On creation - compiles ajv validation function.
|
|
68
21
|
* Provides convenient methods, error reporting, etc.
|
|
69
22
|
*/
|
|
70
|
-
export class AjvSchema<
|
|
23
|
+
export class AjvSchema<IN = unknown, OUT = IN> {
|
|
71
24
|
private constructor(
|
|
72
|
-
public schema: JsonSchema<
|
|
25
|
+
public schema: JsonSchema<IN, OUT>,
|
|
73
26
|
cfg: Partial<AjvSchemaCfg> = {},
|
|
74
27
|
) {
|
|
75
28
|
this.cfg = {
|
|
@@ -88,10 +41,10 @@ export class AjvSchema<T = unknown> {
|
|
|
88
41
|
/**
|
|
89
42
|
* Shortcut for AjvSchema.create(schema, { lazy: true })
|
|
90
43
|
*/
|
|
91
|
-
static createLazy<
|
|
92
|
-
schema:
|
|
44
|
+
static createLazy<IN, OUT>(
|
|
45
|
+
schema: SchemaHandledByAjv<IN, OUT>,
|
|
93
46
|
cfg?: Partial<AjvSchemaCfg>,
|
|
94
|
-
): AjvSchema<
|
|
47
|
+
): AjvSchema<IN, OUT> {
|
|
95
48
|
return AjvSchema.create(schema, {
|
|
96
49
|
lazy: true,
|
|
97
50
|
...cfg,
|
|
@@ -107,46 +60,50 @@ export class AjvSchema<T = unknown> {
|
|
|
107
60
|
* Implementation note: JsonSchemaBuilder goes first in the union type, otherwise TypeScript fails to infer <T> type
|
|
108
61
|
* correctly for some reason.
|
|
109
62
|
*/
|
|
110
|
-
static create<
|
|
63
|
+
static create<IN, OUT = IN>(
|
|
64
|
+
schema: SchemaHandledByAjv<IN, OUT>,
|
|
65
|
+
cfg?: Partial<AjvSchemaCfg>,
|
|
66
|
+
): AjvSchema<IN, OUT> {
|
|
111
67
|
if (schema instanceof AjvSchema) return schema
|
|
112
68
|
|
|
113
|
-
if (AjvSchema.isSchemaWithCachedAjvSchema<typeof schema,
|
|
114
|
-
return AjvSchema.requireCachedAjvSchema<typeof schema,
|
|
69
|
+
if (AjvSchema.isSchemaWithCachedAjvSchema<typeof schema, IN, OUT>(schema)) {
|
|
70
|
+
return AjvSchema.requireCachedAjvSchema<typeof schema, IN, OUT>(schema)
|
|
115
71
|
}
|
|
116
72
|
|
|
117
|
-
let jsonSchema: JsonSchema<
|
|
73
|
+
let jsonSchema: JsonSchema<IN, OUT>
|
|
118
74
|
|
|
119
75
|
if (AjvSchema.isJsonSchemaBuilder(schema)) {
|
|
120
|
-
jsonSchema = schema.build()
|
|
121
|
-
|
|
122
|
-
jsonSchema = z.toJSONSchema(schema, { target: 'draft-7' }) as JsonSchema<T>
|
|
76
|
+
jsonSchema = (schema as JsonSchemaAnyBuilder<IN, OUT, any>).build()
|
|
77
|
+
AjvSchema.requireValidJsonSchema(jsonSchema)
|
|
123
78
|
} else {
|
|
124
|
-
jsonSchema = schema
|
|
79
|
+
jsonSchema = schema as JsonSchema<IN, OUT>
|
|
125
80
|
}
|
|
126
81
|
|
|
127
|
-
const ajvSchema = new AjvSchema<
|
|
82
|
+
const ajvSchema = new AjvSchema<IN, OUT>(jsonSchema, cfg)
|
|
128
83
|
AjvSchema.cacheAjvSchema(schema, ajvSchema)
|
|
129
84
|
|
|
130
85
|
return ajvSchema
|
|
131
86
|
}
|
|
132
87
|
|
|
133
88
|
/**
|
|
134
|
-
* @deprecated
|
|
135
|
-
*
|
|
136
|
-
* Use `AjvSchema.create`
|
|
89
|
+
* @deprecated Use `j` to build schemas, not `z` or `zod`.
|
|
137
90
|
*/
|
|
138
|
-
static createFromZod<T
|
|
139
|
-
|
|
91
|
+
static createFromZod<T extends ZodType<any, any, any>>(
|
|
92
|
+
schema: T,
|
|
93
|
+
): AjvSchema<T['_input'], T['_output']> {
|
|
94
|
+
const jsonSchema = z.toJSONSchema(schema, {
|
|
95
|
+
target: 'draft-7',
|
|
96
|
+
}) as unknown as JsonSchema<T['_input'], T['_output']>
|
|
97
|
+
|
|
98
|
+
return AjvSchema.create(jsonSchema)
|
|
140
99
|
}
|
|
141
100
|
|
|
142
|
-
static isJsonSchemaBuilder<
|
|
101
|
+
static isJsonSchemaBuilder<IN, OUT>(
|
|
102
|
+
schema: unknown,
|
|
103
|
+
): schema is JsonSchemaAnyBuilder<IN, OUT, any> {
|
|
143
104
|
return schema instanceof JsonSchemaAnyBuilder
|
|
144
105
|
}
|
|
145
106
|
|
|
146
|
-
static isZodSchema<T>(schema: unknown): schema is ZodType<T> {
|
|
147
|
-
return schema instanceof ZodType
|
|
148
|
-
}
|
|
149
|
-
|
|
150
107
|
readonly cfg: AjvSchemaCfg
|
|
151
108
|
|
|
152
109
|
/**
|
|
@@ -157,13 +114,13 @@ export class AjvSchema<T = unknown> {
|
|
|
157
114
|
*
|
|
158
115
|
* Returned object is always the same object (`===`) that was passed, so it is returned just for convenience.
|
|
159
116
|
*/
|
|
160
|
-
validate(input:
|
|
117
|
+
validate(input: IN, opt: AjvValidationOptions = {}): OUT {
|
|
161
118
|
const [err, output] = this.getValidationResult(input, opt)
|
|
162
119
|
if (err) throw err
|
|
163
120
|
return output
|
|
164
121
|
}
|
|
165
122
|
|
|
166
|
-
isValid(input:
|
|
123
|
+
isValid(input: IN, opt?: AjvValidationOptions): boolean {
|
|
167
124
|
// todo: we can make it both fast and non-mutating by using Ajv
|
|
168
125
|
// with "removeAdditional" and "useDefaults" disabled.
|
|
169
126
|
const [err] = this.getValidationResult(input, opt)
|
|
@@ -171,9 +128,9 @@ export class AjvSchema<T = unknown> {
|
|
|
171
128
|
}
|
|
172
129
|
|
|
173
130
|
getValidationResult(
|
|
174
|
-
input:
|
|
131
|
+
input: IN,
|
|
175
132
|
opt: AjvValidationOptions = {},
|
|
176
|
-
): ValidationFunctionResult<
|
|
133
|
+
): ValidationFunctionResult<OUT, AjvValidationError> {
|
|
177
134
|
const fn = this.getAJVValidateFunction()
|
|
178
135
|
|
|
179
136
|
const item =
|
|
@@ -182,16 +139,19 @@ export class AjvSchema<T = unknown> {
|
|
|
182
139
|
: _deepCopy(input) // not mutate
|
|
183
140
|
|
|
184
141
|
const valid = fn(item) // mutates item
|
|
142
|
+
_typeCast<OUT>(item)
|
|
185
143
|
if (valid) return [null, item]
|
|
186
144
|
|
|
187
145
|
const errors = fn.errors!
|
|
188
146
|
|
|
189
147
|
const {
|
|
190
|
-
inputId = _isObject(input) ?
|
|
148
|
+
inputId = _isObject(input) ? input['id' as keyof IN] : undefined,
|
|
191
149
|
inputName = this.cfg.inputName || 'Object',
|
|
192
150
|
} = opt
|
|
193
151
|
const dataVar = [inputName, inputId].filter(Boolean).join('.')
|
|
194
152
|
|
|
153
|
+
this.applyImprovementsOnErrorMessages(errors)
|
|
154
|
+
|
|
195
155
|
let message = this.cfg.ajv.errorsText(errors, {
|
|
196
156
|
dataVar,
|
|
197
157
|
separator,
|
|
@@ -213,7 +173,7 @@ export class AjvSchema<T = unknown> {
|
|
|
213
173
|
return [err, item]
|
|
214
174
|
}
|
|
215
175
|
|
|
216
|
-
getValidationFunction(): ValidationFunction<
|
|
176
|
+
getValidationFunction(): ValidationFunction<IN, OUT, AjvValidationError> {
|
|
217
177
|
return (input, opt) => {
|
|
218
178
|
return this.getValidationResult(input, {
|
|
219
179
|
mutateInput: opt?.mutateInput,
|
|
@@ -223,30 +183,109 @@ export class AjvSchema<T = unknown> {
|
|
|
223
183
|
}
|
|
224
184
|
}
|
|
225
185
|
|
|
226
|
-
static isSchemaWithCachedAjvSchema<Base,
|
|
186
|
+
static isSchemaWithCachedAjvSchema<Base, IN, OUT>(
|
|
227
187
|
schema: Base,
|
|
228
|
-
): schema is WithCachedAjvSchema<Base,
|
|
188
|
+
): schema is WithCachedAjvSchema<Base, IN, OUT> {
|
|
229
189
|
return !!(schema as any)?.[HIDDEN_AJV_SCHEMA]
|
|
230
190
|
}
|
|
231
191
|
|
|
232
|
-
static cacheAjvSchema<Base extends AnyObject,
|
|
192
|
+
static cacheAjvSchema<Base extends AnyObject, IN, OUT>(
|
|
233
193
|
schema: Base,
|
|
234
|
-
ajvSchema: AjvSchema<
|
|
235
|
-
): WithCachedAjvSchema<Base,
|
|
194
|
+
ajvSchema: AjvSchema<IN, OUT>,
|
|
195
|
+
): WithCachedAjvSchema<Base, IN, OUT> {
|
|
236
196
|
return Object.assign(schema, { [HIDDEN_AJV_SCHEMA]: ajvSchema })
|
|
237
197
|
}
|
|
238
198
|
|
|
239
|
-
static requireCachedAjvSchema<Base,
|
|
199
|
+
static requireCachedAjvSchema<Base, IN, OUT>(
|
|
200
|
+
schema: WithCachedAjvSchema<Base, IN, OUT>,
|
|
201
|
+
): AjvSchema<IN, OUT> {
|
|
240
202
|
return schema[HIDDEN_AJV_SCHEMA]
|
|
241
203
|
}
|
|
242
204
|
|
|
243
|
-
private getAJVValidateFunction = _lazyValue(() => this.cfg.ajv.compile
|
|
205
|
+
private getAJVValidateFunction = _lazyValue(() => this.cfg.ajv.compile(this.schema as any))
|
|
206
|
+
|
|
207
|
+
private static requireValidJsonSchema(schema: JsonSchema): void {
|
|
208
|
+
// For object schemas we require that it is type checked against an external type, e.g.:
|
|
209
|
+
// interface Foo { name: string }
|
|
210
|
+
// const schema = j.object({ name: j.string() }).ofType<Foo>()
|
|
211
|
+
_assert(
|
|
212
|
+
schema.type !== 'object' || schema.hasIsOfTypeCheck,
|
|
213
|
+
'The schema must be type checked against a type or interface, using the `.isOfType()` helper in `j`.',
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private applyImprovementsOnErrorMessages(
|
|
218
|
+
errors: ErrorObject<string, Record<string, any>, unknown>[] | null | undefined,
|
|
219
|
+
): void {
|
|
220
|
+
if (!errors) return
|
|
221
|
+
|
|
222
|
+
const { errorMessages } = this.schema
|
|
223
|
+
|
|
224
|
+
for (const error of errors) {
|
|
225
|
+
if (errorMessages?.[error.keyword]) {
|
|
226
|
+
error.message = errorMessages[error.keyword]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
error.instancePath = error.instancePath.replaceAll(/\/(\d+)/g, `[$1]`).replaceAll('/', '.')
|
|
230
|
+
}
|
|
231
|
+
}
|
|
244
232
|
}
|
|
245
233
|
|
|
246
234
|
const separator = '\n'
|
|
247
235
|
|
|
248
236
|
export const HIDDEN_AJV_SCHEMA = Symbol('HIDDEN_AJV_SCHEMA')
|
|
249
237
|
|
|
250
|
-
export type WithCachedAjvSchema<Base,
|
|
251
|
-
[HIDDEN_AJV_SCHEMA]: AjvSchema<
|
|
238
|
+
export type WithCachedAjvSchema<Base, IN, OUT> = Base & {
|
|
239
|
+
[HIDDEN_AJV_SCHEMA]: AjvSchema<IN, OUT>
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface AjvValidationOptions {
|
|
243
|
+
/**
|
|
244
|
+
* Defaults to true,
|
|
245
|
+
* because that's how AJV works by default,
|
|
246
|
+
* and what gives it performance advantage.
|
|
247
|
+
* (Because we have found that deep-clone is surprisingly slow,
|
|
248
|
+
* nearly as slow as Joi validation).
|
|
249
|
+
*
|
|
250
|
+
* If set to true - AJV will mutate the input in case it needs to apply transformations
|
|
251
|
+
* (strip unknown properties, convert types, etc).
|
|
252
|
+
*
|
|
253
|
+
* If false - it will deep-clone (using JSON.stringify+parse) the input to prevent its mutation.
|
|
254
|
+
* Will return the cloned/mutated object.
|
|
255
|
+
* Please note that JSON.stringify+parse has side-effects,
|
|
256
|
+
* e.g it will transform Buffer into a weird object.
|
|
257
|
+
*/
|
|
258
|
+
mutateInput?: boolean
|
|
259
|
+
inputName?: string
|
|
260
|
+
inputId?: string
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface AjvSchemaCfg {
|
|
264
|
+
/**
|
|
265
|
+
* Pass Ajv instance, otherwise Ajv will be created with
|
|
266
|
+
* AjvSchema default (not the same as Ajv defaults) parameters
|
|
267
|
+
*/
|
|
268
|
+
ajv: Ajv
|
|
269
|
+
|
|
270
|
+
inputName?: string
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Option of Ajv.
|
|
274
|
+
* If set to true - will mutate your input objects!
|
|
275
|
+
* Defaults to false.
|
|
276
|
+
*
|
|
277
|
+
* This option is a "shortcut" to skip creating and passing Ajv instance.
|
|
278
|
+
*/
|
|
279
|
+
// coerceTypes?: boolean
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* If true - schema will be compiled on-demand (lazily).
|
|
283
|
+
* Default: false.
|
|
284
|
+
*/
|
|
285
|
+
lazy?: boolean
|
|
252
286
|
}
|
|
287
|
+
|
|
288
|
+
export type SchemaHandledByAjv<IN, OUT = IN> =
|
|
289
|
+
| JsonSchemaAnyBuilder<IN, OUT, any>
|
|
290
|
+
| JsonSchema<IN, OUT>
|
|
291
|
+
| AjvSchema<IN, OUT>
|