@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,10 +1,12 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/prefer-string-starts-ends-with */
|
|
2
|
-
/* eslint-disable unicorn/prefer-code-point */
|
|
3
1
|
import { _lazyValue } from '@naturalcycles/js-lib'
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
2
|
+
import { Set2 } from '@naturalcycles/js-lib/object'
|
|
3
|
+
import { _substringAfterLast } from '@naturalcycles/js-lib/string'
|
|
4
|
+
import { _, Ajv, type Options, type ValidateFunction } from 'ajv'
|
|
5
|
+
import type { JsonSchemaStringEmailOptions } from './jsonSchemaBuilder.js'
|
|
6
|
+
import { validTLDs } from './tlds.js'
|
|
7
|
+
|
|
8
|
+
/* eslint-disable @typescript-eslint/prefer-string-starts-ends-with */
|
|
9
|
+
// oxlint-disable unicorn/prefer-code-point
|
|
8
10
|
|
|
9
11
|
const AJV_OPTIONS: Options = {
|
|
10
12
|
removeAdditional: true,
|
|
@@ -51,101 +53,256 @@ export function createAjv(opt?: Options): Ajv {
|
|
|
51
53
|
...opt,
|
|
52
54
|
})
|
|
53
55
|
|
|
54
|
-
// Add custom formats
|
|
55
|
-
addCustomAjvFormats(ajv)
|
|
56
|
-
|
|
57
|
-
// todo: review and possibly cherry-pick/vendor the formats
|
|
58
|
-
// Adds ajv "formats"
|
|
59
|
-
// https://ajv.js.org/guide/formats.html
|
|
60
|
-
// @ts-expect-error types are wrong
|
|
61
|
-
ajvFormats(ajv)
|
|
62
|
-
|
|
63
|
-
// https://ajv.js.org/packages/ajv-keywords.html
|
|
64
|
-
// @ts-expect-error types are wrong
|
|
65
|
-
ajvKeywords(ajv, [
|
|
66
|
-
'transform', // trim, toLowerCase, etc.
|
|
67
|
-
'uniqueItemProperties',
|
|
68
|
-
'instanceof',
|
|
69
|
-
])
|
|
70
|
-
|
|
71
56
|
// Adds $merge, $patch keywords
|
|
72
57
|
// https://github.com/ajv-validator/ajv-merge-patch
|
|
73
58
|
// Kirill: temporarily disabled, as it creates a noise of CVE warnings
|
|
74
59
|
// require('ajv-merge-patch')(ajv)
|
|
75
60
|
|
|
76
|
-
|
|
77
|
-
|
|
61
|
+
ajv.addKeyword({
|
|
62
|
+
keyword: 'transform',
|
|
63
|
+
type: 'string',
|
|
64
|
+
modifying: true,
|
|
65
|
+
schemaType: 'object',
|
|
66
|
+
errors: true,
|
|
67
|
+
code(ctx) {
|
|
68
|
+
const { gen, data, schema, it } = ctx
|
|
69
|
+
const { parentData, parentDataProperty } = it
|
|
70
|
+
|
|
71
|
+
if (schema.trim) {
|
|
72
|
+
gen.assign(_`${data}`, _`${data}.trim()`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (schema.toLowerCase) {
|
|
76
|
+
gen.assign(_`${data}`, _`${data}.toLowerCase()`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (schema.toUpperCase) {
|
|
80
|
+
gen.assign(_`${data}`, _`${data}.toUpperCase()`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeof schema.truncate === 'number' && schema.truncate >= 0) {
|
|
84
|
+
gen.assign(_`${data}`, _`${data}.slice(0, ${schema.truncate})`)
|
|
85
|
+
|
|
86
|
+
if (schema.trim) {
|
|
87
|
+
gen.assign(_`${data}`, _`${data}.trim()`)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
gen.if(_`${parentData} !== undefined`, () => {
|
|
92
|
+
gen.assign(_`${parentData}[${parentDataProperty}]`, data)
|
|
93
|
+
})
|
|
94
|
+
},
|
|
95
|
+
})
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
ajv.addKeyword({
|
|
98
|
+
keyword: 'instanceof',
|
|
99
|
+
modifying: true,
|
|
100
|
+
schemaType: 'string',
|
|
101
|
+
validate(instanceOf: string, data: unknown, _schema, _ctx) {
|
|
102
|
+
if (typeof data !== 'object') return false
|
|
103
|
+
if (data === null) return false
|
|
104
|
+
|
|
105
|
+
let proto = Object.getPrototypeOf(data)
|
|
106
|
+
while (proto) {
|
|
107
|
+
if (proto.constructor?.name === instanceOf) return true
|
|
108
|
+
proto = Object.getPrototypeOf(proto)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return false
|
|
112
|
+
},
|
|
113
|
+
})
|
|
83
114
|
|
|
84
|
-
|
|
115
|
+
ajv.addKeyword({
|
|
116
|
+
keyword: 'Set2',
|
|
117
|
+
type: ['array', 'object'],
|
|
118
|
+
modifying: true,
|
|
119
|
+
errors: true,
|
|
120
|
+
schemaType: 'object',
|
|
121
|
+
compile(innerSchema, _parentSchema, _it) {
|
|
122
|
+
const validateItem: ValidateFunction = ajv.compile(innerSchema)
|
|
123
|
+
|
|
124
|
+
function validateSet(data: any, ctx: any): boolean {
|
|
125
|
+
let set: Set2
|
|
126
|
+
|
|
127
|
+
const isIterable = data === null || typeof data[Symbol.iterator] === 'function'
|
|
128
|
+
|
|
129
|
+
if (data instanceof Set2) {
|
|
130
|
+
set = data
|
|
131
|
+
} else if (isIterable && ctx?.parentData) {
|
|
132
|
+
set = new Set2(data)
|
|
133
|
+
} else if (isIterable && !ctx?.parentData) {
|
|
134
|
+
;(validateSet as any).errors = [
|
|
135
|
+
{
|
|
136
|
+
instancePath: ctx?.instancePath ?? '',
|
|
137
|
+
message:
|
|
138
|
+
'can only transform an Iterable into a Set2 when the schema is in an object or an array schema. This is an Ajv limitation.',
|
|
139
|
+
},
|
|
140
|
+
]
|
|
141
|
+
return false
|
|
142
|
+
} else {
|
|
143
|
+
;(validateSet as any).errors = [
|
|
144
|
+
{
|
|
145
|
+
instancePath: ctx?.instancePath ?? '',
|
|
146
|
+
message: 'must be a Set2 object (or optionally an Iterable in some cases)',
|
|
147
|
+
},
|
|
148
|
+
]
|
|
149
|
+
return false
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let idx = 0
|
|
153
|
+
for (const value of set.values()) {
|
|
154
|
+
if (!validateItem(value)) {
|
|
155
|
+
;(validateSet as any).errors = [
|
|
156
|
+
{
|
|
157
|
+
instancePath: (ctx?.instancePath ?? '') + '/' + idx,
|
|
158
|
+
message: `invalid set item at index ${idx}`,
|
|
159
|
+
params: { errors: validateItem.errors },
|
|
160
|
+
},
|
|
161
|
+
]
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
idx++
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (ctx?.parentData && ctx.parentDataProperty) {
|
|
168
|
+
ctx.parentData[ctx.parentDataProperty] = set
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return true
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return validateSet
|
|
175
|
+
},
|
|
176
|
+
})
|
|
85
177
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
178
|
+
ajv.addKeyword({
|
|
179
|
+
keyword: 'Buffer',
|
|
180
|
+
modifying: true,
|
|
181
|
+
errors: true,
|
|
182
|
+
schemaType: 'boolean',
|
|
183
|
+
compile(_innerSchema, _parentSchema, _it) {
|
|
184
|
+
function validateBuffer(data: any, ctx: any): boolean {
|
|
185
|
+
let buffer: Buffer
|
|
186
|
+
|
|
187
|
+
if (data === null) return false
|
|
188
|
+
|
|
189
|
+
const isValid =
|
|
190
|
+
data instanceof Buffer ||
|
|
191
|
+
data instanceof ArrayBuffer ||
|
|
192
|
+
Array.isArray(data) ||
|
|
193
|
+
typeof data === 'string'
|
|
194
|
+
if (!isValid) return false
|
|
195
|
+
|
|
196
|
+
if (data instanceof Buffer) {
|
|
197
|
+
buffer = data
|
|
198
|
+
} else if (isValid && ctx?.parentData) {
|
|
199
|
+
buffer = Buffer.from(data as any)
|
|
200
|
+
} else if (isValid && !ctx?.parentData) {
|
|
201
|
+
;(validateBuffer as any).errors = [
|
|
202
|
+
{
|
|
203
|
+
instancePath: ctx?.instancePath ?? '',
|
|
204
|
+
message:
|
|
205
|
+
'can only transform data into a Buffer when the schema is in an object or an array schema. This is an Ajv limitation.',
|
|
206
|
+
},
|
|
207
|
+
]
|
|
208
|
+
return false
|
|
209
|
+
} else {
|
|
210
|
+
;(validateBuffer as any).errors = [
|
|
211
|
+
{
|
|
212
|
+
instancePath: ctx?.instancePath ?? '',
|
|
213
|
+
message:
|
|
214
|
+
'must be a Buffer object (or optionally an Array-like object or ArrayBuffer in some cases)',
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (ctx?.parentData && ctx.parentDataProperty) {
|
|
221
|
+
ctx.parentData[ctx.parentDataProperty] = buffer
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return true
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return validateBuffer
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
ajv.addKeyword({
|
|
232
|
+
keyword: 'email',
|
|
233
|
+
type: 'string',
|
|
234
|
+
modifying: false,
|
|
235
|
+
errors: true,
|
|
236
|
+
schemaType: 'object',
|
|
237
|
+
validate: function validate(opt: JsonSchemaStringEmailOptions, data: string, _schema, ctx) {
|
|
238
|
+
const { checkTLD } = opt
|
|
239
|
+
if (!checkTLD) return true
|
|
240
|
+
|
|
241
|
+
const tld = _substringAfterLast(data, '.')
|
|
242
|
+
if (validTLDs.has(tld)) return true
|
|
243
|
+
;(validate as any).errors = [
|
|
244
|
+
{
|
|
245
|
+
instancePath: ctx?.instancePath ?? '',
|
|
246
|
+
message: `has an invalid TLD`,
|
|
118
247
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
248
|
+
]
|
|
249
|
+
return false
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
ajv.addKeyword({
|
|
254
|
+
keyword: 'IsoDate',
|
|
255
|
+
type: 'string',
|
|
256
|
+
modifying: false,
|
|
257
|
+
errors: true,
|
|
258
|
+
schemaType: 'boolean',
|
|
259
|
+
validate: function validate(_opt: true, data: string, _schema, ctx) {
|
|
260
|
+
const isValid = isIsoDateValid(data)
|
|
261
|
+
if (isValid) return true
|
|
262
|
+
;(validate as any).errors = [
|
|
263
|
+
{
|
|
264
|
+
instancePath: ctx?.instancePath ?? '',
|
|
265
|
+
message: `is an invalid IsoDate`,
|
|
127
266
|
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
267
|
+
]
|
|
268
|
+
return false
|
|
269
|
+
},
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
ajv.addKeyword({
|
|
273
|
+
keyword: 'IsoDateTime',
|
|
274
|
+
type: 'string',
|
|
275
|
+
modifying: false,
|
|
276
|
+
errors: true,
|
|
277
|
+
schemaType: 'boolean',
|
|
278
|
+
validate: function validate(_opt: true, data: string, _schema, ctx) {
|
|
279
|
+
const isValid = isIsoDateTimeValid(data)
|
|
280
|
+
if (isValid) return true
|
|
281
|
+
;(validate as any).errors = [
|
|
282
|
+
{
|
|
283
|
+
instancePath: ctx?.instancePath ?? '',
|
|
284
|
+
message: `is an invalid IsoDateTime`,
|
|
136
285
|
},
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
286
|
+
]
|
|
287
|
+
return false
|
|
288
|
+
},
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
ajv.addKeyword({
|
|
292
|
+
keyword: 'errorMessages',
|
|
293
|
+
schemaType: 'object',
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
ajv.addKeyword({
|
|
297
|
+
keyword: 'hasIsOfTypeCheck',
|
|
298
|
+
schemaType: 'boolean',
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
return ajv
|
|
147
302
|
}
|
|
148
303
|
|
|
304
|
+
const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
305
|
+
|
|
149
306
|
const DASH_CODE = '-'.charCodeAt(0)
|
|
150
307
|
const ZERO_CODE = '0'.charCodeAt(0)
|
|
151
308
|
const PLUS_CODE = '+'.charCodeAt(0)
|