@naturalcycles/nodejs-lib 15.89.1 → 15.90.1

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.
Files changed (28) hide show
  1. package/dist/validation/ajv/ajvSchema.d.ts +0 -5
  2. package/dist/validation/ajv/ajvSchema.js +0 -10
  3. package/package.json +1 -3
  4. package/src/validation/ajv/ajvSchema.ts +0 -15
  5. package/dist/validation/joi/index.d.ts +0 -9
  6. package/dist/validation/joi/index.js +0 -5
  7. package/dist/validation/joi/joi.extensions.d.ts +0 -11
  8. package/dist/validation/joi/joi.extensions.js +0 -25
  9. package/dist/validation/joi/joi.model.d.ts +0 -7
  10. package/dist/validation/joi/joi.model.js +0 -5
  11. package/dist/validation/joi/joi.shared.schemas.d.ts +0 -91
  12. package/dist/validation/joi/joi.shared.schemas.js +0 -135
  13. package/dist/validation/joi/joi.validation.error.d.ts +0 -29
  14. package/dist/validation/joi/joi.validation.error.js +0 -8
  15. package/dist/validation/joi/joi.validation.util.d.ts +0 -33
  16. package/dist/validation/joi/joi.validation.util.js +0 -139
  17. package/dist/validation/joi/number.extensions.d.ts +0 -6
  18. package/dist/validation/joi/number.extensions.js +0 -38
  19. package/dist/validation/joi/string.extensions.d.ts +0 -11
  20. package/dist/validation/joi/string.extensions.js +0 -104
  21. package/src/validation/joi/index.ts +0 -32
  22. package/src/validation/joi/joi.extensions.ts +0 -38
  23. package/src/validation/joi/joi.model.ts +0 -12
  24. package/src/validation/joi/joi.shared.schemas.ts +0 -207
  25. package/src/validation/joi/joi.validation.error.ts +0 -35
  26. package/src/validation/joi/joi.validation.util.ts +0 -183
  27. package/src/validation/joi/number.extensions.ts +0 -46
  28. package/src/validation/joi/string.extensions.ts +0 -129
@@ -1,104 +0,0 @@
1
- import { localTime } from '@naturalcycles/js-lib/datetime/localTime.js';
2
- export function stringExtensions(joi) {
3
- return {
4
- type: 'string',
5
- base: joi.string(),
6
- messages: {
7
- 'string.dateString': '"{{#label}}" must be an ISO8601 date (yyyy-mm-dd)',
8
- 'string.dateStringMin': '"{{#label}}" must be not earlier than {{#min}}',
9
- 'string.dateStringMax': '"{{#label}}" must be not later than {{#max}}',
10
- 'string.dateStringCalendarAccuracy': '"{{#label}}" must be a VALID calendar date',
11
- 'string.stripHTML': '"{{#label}}" must NOT contain any HTML tags',
12
- },
13
- rules: {
14
- dateString: {
15
- method(min, max) {
16
- return this.$_addRule({
17
- name: 'dateString',
18
- args: { min, max },
19
- });
20
- },
21
- args: [
22
- {
23
- name: 'min',
24
- // ref: true, // check false
25
- assert: v => v === undefined || typeof v === 'string',
26
- message: 'must be a string',
27
- },
28
- {
29
- name: 'max',
30
- // ref: true,
31
- assert: v => v === undefined || typeof v === 'string',
32
- message: 'must be a string',
33
- },
34
- ],
35
- validate(v, helpers, args) {
36
- // console.log('dateString validate called', {v, args})
37
- let err;
38
- let { min, max } = args;
39
- // Today allows +-14 hours gap to account for different timezones
40
- if (max === 'today') {
41
- max = getTodayStrPlus15();
42
- }
43
- if (min === 'today') {
44
- min = getTodayStrMinus15();
45
- }
46
- // console.log('min/max', min, max)
47
- const parts = /^(\d{4})-(\d{2})-(\d{2})$/.exec(v);
48
- if (!parts || parts.length < 4) {
49
- err = 'string.dateString';
50
- }
51
- else if (min && v < min) {
52
- err = 'string.dateStringMin';
53
- }
54
- else if (max && v > max) {
55
- err = 'string.dateStringMax';
56
- }
57
- else if (!isValidDate(parts)) {
58
- err = 'string.dateStringCalendarAccuracy';
59
- }
60
- if (err) {
61
- return helpers.error(err, args);
62
- }
63
- return v; // validation passed
64
- },
65
- },
66
- },
67
- };
68
- }
69
- const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
70
- // Based on: https://github.com/ajv-validator
71
- function isValidDate(parts) {
72
- const year = Number(parts[1]);
73
- const month = Number(parts[2]);
74
- const day = Number(parts[3]);
75
- return (month >= 1 &&
76
- month <= 12 &&
77
- day >= 1 &&
78
- day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]));
79
- }
80
- function isLeapYear(year) {
81
- return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
82
- }
83
- let lastCheckedPlus = 0;
84
- let todayStrPlusCached;
85
- let lastCheckedMinus = 0;
86
- let todayStrMinusCached;
87
- function getTodayStrPlus15() {
88
- const now = Date.now();
89
- if (now - lastCheckedPlus < 3_600_000) {
90
- // cached for 1 hour
91
- return todayStrPlusCached;
92
- }
93
- lastCheckedPlus = now;
94
- return (todayStrPlusCached = localTime.now().plus(15, 'hour').toISODate());
95
- }
96
- function getTodayStrMinus15() {
97
- const now = Date.now();
98
- if (now - lastCheckedMinus < 3_600_000) {
99
- // cached for 1 hour
100
- return todayStrMinusCached;
101
- }
102
- lastCheckedMinus = now;
103
- return (todayStrMinusCached = localTime.now().plus(-15, 'hour').toISODate());
104
- }
@@ -1,32 +0,0 @@
1
- import type {
2
- AlternativesSchema,
3
- AnySchema,
4
- ArraySchema,
5
- BinarySchema,
6
- BooleanSchema,
7
- DateSchema,
8
- FunctionSchema,
9
- ObjectSchema,
10
- ValidationErrorItem,
11
- } from 'joi'
12
-
13
- export * from './joi.extensions.js'
14
- export * from './joi.model.js'
15
- export * from './joi.shared.schemas.js'
16
- export * from './joi.validation.error.js'
17
- export * from './joi.validation.util.js'
18
-
19
- export type {
20
- AlternativesSchema,
21
- AnySchema,
22
- ArraySchema,
23
- BinarySchema,
24
- BooleanSchema,
25
- DateSchema,
26
- FunctionSchema,
27
- ObjectSchema,
28
- ValidationErrorItem,
29
- }
30
- // extended
31
- export type { NumberSchema } from './number.extensions.js'
32
- export type { StringSchema } from './string.extensions.js'
@@ -1,38 +0,0 @@
1
- import JoiLib from 'joi'
2
- import type { NumberSchema } from './number.extensions.js'
3
- import { numberExtensions } from './number.extensions.js'
4
- import type { StringSchema } from './string.extensions.js'
5
- import { stringExtensions } from './string.extensions.js'
6
-
7
- export interface ExtendedJoi extends JoiLib.Root {
8
- // eslint-disable-next-line id-denylist
9
- string: <TSchema = string>() => StringSchema<TSchema>
10
- // eslint-disable-next-line id-denylist
11
- number: <TSchema = number>() => NumberSchema<TSchema>
12
- }
13
-
14
- /**
15
- * This is the only right place to import Joi from
16
- */
17
- // eslint-disable-next-line @typescript-eslint/naming-convention
18
- export const Joi: ExtendedJoi = JoiLib.defaults(schema => {
19
- // hack to prevent infinite recursion due to .empty('') where '' is a stringSchema itself
20
- if (schema.type === 'string') {
21
- return (
22
- (schema as StringSchema)
23
- .trim() // trim all strings by default
24
- // 2020-09-21: null values are NOT treated as EMPTY enymore
25
- // .empty([schema.valid('', null)]) // treat '' or null as empty (undefined, will be stripped out)
26
- // treat '' as empty (undefined, will be stripped out)
27
- .empty([schema.valid('')])
28
- )
29
- }
30
-
31
- // Treat `null` as undefined for all schema types
32
- // undefined values will be stripped by default from object values
33
- // 2020-09-21: breaking change: null values are NOT treated as EMPTY anymore
34
- // return schema.empty(null)
35
- return schema
36
- })
37
- .extend((joi: typeof JoiLib) => stringExtensions(joi))
38
- .extend((joi: typeof JoiLib) => numberExtensions(joi))
@@ -1,12 +0,0 @@
1
- // export interface ObjectSchema<T = any> extends JoiObjectSchema<T> {
2
- // // Open-ended index signature to allow for easier .concat(baseDBEntitySchema)
3
- // [k: string]: any
4
- // }
5
-
6
- /**
7
- * This type is useful to allow "joi schema merging".
8
- * Because by default Joi doesn't allow normal merging.
9
- * E.g `joiSchema.concat` doesn't play well when some property exists
10
- * in both left and right side.
11
- */
12
- export type JoiSchemaObject<T> = Partial<Record<keyof T, any>>
@@ -1,207 +0,0 @@
1
- import {
2
- _numberEnumKeys,
3
- _numberEnumValues,
4
- _stringEnumKeys,
5
- _stringEnumValues,
6
- } from '@naturalcycles/js-lib'
7
- import {
8
- type AnyObject,
9
- type BaseDBEntity,
10
- type IANATimezone,
11
- type IsoDate,
12
- type IsoDateTime,
13
- JWT_REGEX,
14
- type NumberEnum,
15
- type StringEnum,
16
- type StringMap,
17
- type UnixTimestamp,
18
- type UnixTimestampMillis,
19
- } from '@naturalcycles/js-lib/types'
20
- import type { AlternativesSchema, AnySchema, ArraySchema, ObjectSchema } from 'joi'
21
- import {
22
- BASE62_REGEX,
23
- BASE64_REGEX,
24
- BASE64URL_REGEX,
25
- ID_REGEX,
26
- MAC_ADDRESS_REGEX,
27
- SEMVER_REGEX,
28
- SLUG_REGEX,
29
- } from '../regexes.js'
30
- import { Joi } from './joi.extensions.js'
31
- import type { NumberSchema } from './number.extensions.js'
32
- import type { StringSchema } from './string.extensions.js'
33
-
34
- export const booleanSchema = Joi.boolean()
35
- export const booleanDefaultToFalseSchema = Joi.boolean().default(false)
36
- export const stringSchema = Joi.string()
37
- export const stringSchemaTyped = <T>(): StringSchema<T> => Joi.string<T>()
38
- export const numberSchema = Joi.number()
39
- export const numberSchemaTyped = <T>(): NumberSchema<T> => Joi.number<T>()
40
- export const integerSchema = Joi.number().integer()
41
- export const percentageSchema = Joi.number().integer().min(0).max(100)
42
- export const dateStringSchema: StringSchema<IsoDate> = stringSchema.dateString()
43
- export const binarySchema = Joi.binary()
44
- export const dateObjectSchema = Joi.object().instance(Date)
45
-
46
- const DATE_INTERVAL_REGEX = /^\d{4}-\d{2}-\d{2}\/\d{4}-\d{2}-\d{2}$/
47
- export const dateIntervalStringSchema = stringSchema.regex(DATE_INTERVAL_REGEX).messages({
48
- 'string.pattern.base': `must be a DateInterval string`,
49
- })
50
-
51
- export const DATE_TIME_STRING_REGEX =
52
- /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})?)?$/
53
-
54
- export const dateTimeStringSchema = stringSchema.regex(DATE_TIME_STRING_REGEX).messages({
55
- 'string.pattern.base': `must be a DateTime string`,
56
- }) as StringSchema<IsoDateTime>
57
-
58
- /**
59
- * Allows all values of a String Enum.
60
- */
61
- export const stringEnumValueSchema = <ENUM extends StringEnum>(
62
- en: ENUM,
63
- ): StringSchema<ENUM[keyof ENUM]> => Joi.string<ENUM[keyof ENUM]>().valid(..._stringEnumValues(en))
64
-
65
- /**
66
- * Allows all keys of a String Enum.
67
- */
68
- export const stringEnumKeySchema = <ENUM extends StringEnum>(en: ENUM): StringSchema =>
69
- Joi.string().valid(..._stringEnumKeys(en))
70
-
71
- /**
72
- * Allows all values of a String Enum.
73
- */
74
- export const numberEnumValueSchema = <ENUM extends NumberEnum>(
75
- en: ENUM,
76
- ): NumberSchema<ENUM[keyof ENUM]> => Joi.number<ENUM[keyof ENUM]>().valid(..._numberEnumValues(en))
77
-
78
- /**
79
- * Allows all keys of a Number Enum.
80
- */
81
- export const numberEnumKeySchema = <ENUM extends NumberEnum>(en: ENUM): StringSchema =>
82
- Joi.string().valid(..._numberEnumKeys(en))
83
-
84
- export const urlSchema = (scheme: string | string[] = 'https'): StringSchema =>
85
- Joi.string().uri({ scheme })
86
-
87
- export function arraySchema<T>(items: AnySchema<T>): ArraySchema<T[]> {
88
- return Joi.array().items(items)
89
- }
90
-
91
- export function anyObjectSchema<T extends AnyObject>(): ObjectSchema<T> {
92
- return Joi.object().options({ stripUnknown: false })
93
- }
94
-
95
- export function objectSchema<T extends AnyObject>(schema: {
96
- [key in keyof Partial<T>]: AnySchema<T[key]>
97
- }): ObjectSchema<T> {
98
- return Joi.object(schema)
99
- }
100
-
101
- export function stringMapSchema<T>(
102
- key: AnySchema,
103
- value: AnySchema<T>,
104
- ): ObjectSchema<StringMap<T>> {
105
- return Joi.object().pattern(key, value)
106
- }
107
-
108
- export function oneOfSchema<T = any>(...schemas: AnySchema[]): AlternativesSchema<T> {
109
- return Joi.alternatives(schemas)
110
- }
111
-
112
- export const anySchema = Joi.any()
113
- export const base62Schema = stringSchema.regex(BASE62_REGEX)
114
- export const base64Schema = stringSchema.regex(BASE64_REGEX)
115
- export const base64UrlSchema = stringSchema.regex(BASE64URL_REGEX)
116
-
117
- export const jwtSchema = stringSchema.regex(JWT_REGEX)
118
-
119
- // 1g498efj5sder3324zer
120
- /**
121
- * [a-zA-Z0-9_]*
122
- * 6-64 length
123
- */
124
- export const idSchema = stringSchema.regex(ID_REGEX)
125
-
126
- export const idBase62Schema = base62Schema.min(8).max(64)
127
- export const idBase64Schema = base64Schema.min(8).max(64)
128
- export const idBase64UrlSchema = base64UrlSchema.min(8).max(64)
129
-
130
- /**
131
- * "Slug" - a valid URL, filename, etc.
132
- */
133
- export const slugSchema = stringSchema.regex(SLUG_REGEX).min(1).max(255)
134
-
135
- const TS_2500 = 16725225600 // 2500-01-01
136
- const TS_2000 = 946684800 // 2000-01-01
137
-
138
- /**
139
- * Between years 1970 and 2050
140
- */
141
- export const unixTimestampSchema = numberSchema
142
- .integer()
143
- .min(0)
144
- .max(TS_2500) as NumberSchema<UnixTimestamp>
145
- /**
146
- * Between years 2000 and 2050
147
- */
148
- export const unixTimestamp2000Schema = numberSchema
149
- .integer()
150
- .min(TS_2000)
151
- .max(TS_2500) as NumberSchema<UnixTimestamp>
152
- /**
153
- * Between years 1970 and 2050
154
- */
155
- export const unixTimestampMillisSchema = numberSchema
156
- .integer()
157
- .min(0)
158
- .max(TS_2500 * 1000) as NumberSchema<UnixTimestampMillis>
159
- /**
160
- * Between years 2000 and 2050
161
- */
162
- export const unixTimestampMillis2000Schema = numberSchema
163
- .integer()
164
- .min(TS_2000 * 1000)
165
- .max(TS_2500 * 1000) as NumberSchema<UnixTimestampMillis>
166
-
167
- // 2
168
- export const verSchema = numberSchema.optional().integer().min(1).max(100)
169
-
170
- /**
171
- * Be careful, by default emailSchema does TLD validation. To disable it - use `stringSchema.email({tld: false}).lowercase()`
172
- */
173
- export const emailSchema = stringSchema.email().lowercase()
174
-
175
- /**
176
- * Pattern is simplified for our use, it's not a canonical SemVer.
177
- */
178
- export const semVerSchema = stringSchema.regex(SEMVER_REGEX)
179
- // todo: .error(() => 'should be SemVer')
180
-
181
- export const userAgentSchema = stringSchema
182
- .min(5) // I've seen UA of `Android` (7 characters)
183
- .max(400)
184
-
185
- export const utcOffsetSchema = numberSchema
186
- .min(-14 * 60)
187
- .max(14 * 60)
188
- .dividable(15)
189
-
190
- export const ianaTimezoneSchema = stringSchema
191
- // UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
192
- .valid(...Intl.supportedValuesOf('timeZone'), 'UTC')
193
- .messages({
194
- 'any.only': `must be a valid IANA timezone string`,
195
- }) as StringSchema<IANATimezone>
196
-
197
- export const ipAddressSchema = stringSchema.ip()
198
-
199
- export const baseDBEntitySchema: ObjectSchema<BaseDBEntity> = objectSchema<BaseDBEntity>({
200
- id: stringSchema.optional(),
201
- created: unixTimestamp2000Schema.optional(),
202
- updated: unixTimestamp2000Schema.optional(),
203
- })
204
-
205
- export const macAddressSchema = stringSchema.regex(MAC_ADDRESS_REGEX)
206
-
207
- export const uuidSchema = stringSchema.uuid()
@@ -1,35 +0,0 @@
1
- import type { ErrorData } from '@naturalcycles/js-lib/error'
2
- import { AppError } from '@naturalcycles/js-lib/error/error.util.js'
3
- import type { ValidationErrorItem } from 'joi'
4
-
5
- /**
6
- * Example of ValidationErrorItem:
7
- *
8
- * {
9
- * message: '"temperature" must be larger than or equal to 33',
10
- * path: [ 'entries', 10, 'temperature' ],
11
- * type: 'number.min',
12
- * context: { limit: 33, value: 30, key: 'temperature', label: 'temperature' }
13
- * }
14
- */
15
- export interface JoiValidationErrorData extends ErrorData {
16
- joiValidationErrorItems: ValidationErrorItem[]
17
- joiValidationInputName?: string
18
- joiValidationInputId?: string
19
- /**
20
- * Error "annotation" is stripped in Error.message.
21
- * This field contains the "full" annotation.
22
- *
23
- * This field is non-enumerable, won't be printed or included in JSON by default,
24
- * but still accessible programmatically (via `err.data.annotation`) when needed!
25
- */
26
- annotation?: string
27
- }
28
-
29
- export class JoiValidationError extends AppError<JoiValidationErrorData> {
30
- constructor(message: string, data: JoiValidationErrorData) {
31
- super(message, data, {
32
- name: 'JoiValidationError',
33
- })
34
- }
35
- }
@@ -1,183 +0,0 @@
1
- /*
2
- * Does 2 things:
3
- * 1. Validates the value according to Schema passed.
4
- * 2. Converts the value (also according to Schema).
5
- *
6
- * "Converts" mean e.g trims all strings from leading/trailing spaces.
7
- */
8
-
9
- import {
10
- _hb,
11
- _isObject,
12
- type ValidationFunction,
13
- type ValidationFunctionResult,
14
- } from '@naturalcycles/js-lib'
15
- import { _assert } from '@naturalcycles/js-lib/error/assert.js'
16
- import { _truncateMiddle } from '@naturalcycles/js-lib/string/string.util.js'
17
- import type { AnySchema, ValidationError, ValidationOptions } from 'joi'
18
- import type { JoiValidationErrorData } from './joi.validation.error.js'
19
- import { JoiValidationError } from './joi.validation.error.js'
20
-
21
- // Strip colors in production (for e.g Sentry reporting)
22
- // const stripColors = process.env.NODE_ENV === 'production' || !!process.env.GAE_INSTANCE
23
- // Currently colors do more bad than good, so let's strip them always for now
24
- const stripColors = true
25
-
26
- const defaultOptions: ValidationOptions = {
27
- abortEarly: false,
28
- convert: true,
29
- allowUnknown: true,
30
- stripUnknown: {
31
- objects: true,
32
- // true: it will SILENTLY strip invalid values from arrays. Very dangerous! Can lead to data loss!
33
- // false: it will THROW validation error if any of array items is invalid
34
- // Q: is it invalid if it has unknown properties?
35
- // A: no, unknown properties are just stripped (in both 'false' and 'true' states), array is still valid
36
- // Q: will it strip or keep unknown properties in array items?..
37
- // A: strip
38
- arrays: false, // let's be very careful with that! https://github.com/hapijs/joi/issues/658
39
- },
40
- presence: 'required',
41
- // errors: {
42
- // stack: true,
43
- // }
44
- }
45
-
46
- export function getJoiValidationFunction<T>(
47
- schema: AnySchema<T>,
48
- ): ValidationFunction<T, T, JoiValidationError> {
49
- return (input, opt) => {
50
- _assert(!opt?.mutateInput, 'mutateInput=true is not yet supported with Joi')
51
- return getValidationResult(input, schema, opt?.inputName)
52
- }
53
- }
54
-
55
- /**
56
- * Validates with Joi.
57
- * Throws JoiValidationError if invalid.
58
- * Returns *converted* value.
59
- *
60
- * If `schema` is undefined - returns value as is.
61
- */
62
- export function validate<T>(
63
- input: any,
64
- schema?: AnySchema<T>,
65
- inputName?: string,
66
- opt: ValidationOptions = {},
67
- ): T {
68
- const [error, returnValue] = getValidationResult(input, schema, inputName, opt)
69
- if (error) throw error
70
- return returnValue
71
- }
72
-
73
- /**
74
- * Validates with Joi.
75
- * Returns JoiValidationResult with converted value and error (if any).
76
- * Does not throw.
77
- *
78
- * Joi does NOT mutate the input.
79
- *
80
- * If `schema` is undefined - returns value as is.
81
- */
82
- export function getValidationResult<T>(
83
- input: T,
84
- schema?: AnySchema<T>,
85
- inputName?: string,
86
- options: ValidationOptions = {},
87
- ): ValidationFunctionResult<T, JoiValidationError> {
88
- if (!schema) return [null, input]
89
-
90
- const { value, error } = schema.validate(input, {
91
- ...defaultOptions,
92
- ...options,
93
- })
94
-
95
- const err = error ? createError(input, error, inputName) : null
96
- return [err, value]
97
- }
98
-
99
- /**
100
- * Convenience function that returns true if !error.
101
- */
102
- export function isValid<T>(input: T, schema?: AnySchema<T>): boolean {
103
- if (!schema) return true
104
-
105
- const { error } = schema.validate(input, defaultOptions)
106
- return !error
107
- }
108
-
109
- export function undefinedIfInvalid<T>(input: any, schema?: AnySchema<T>): T | undefined {
110
- if (!schema) return input
111
-
112
- const { value, error } = schema.validate(input, defaultOptions)
113
-
114
- return error ? undefined : value
115
- }
116
-
117
- /**
118
- * Will do joi-conversion, regardless of error/validity of value.
119
- *
120
- * @returns converted value
121
- */
122
- export function convert<T>(input: any, schema?: AnySchema<T>): T {
123
- if (!schema) return input
124
- const { value } = schema.validate(input, defaultOptions)
125
- return value
126
- }
127
-
128
- function createError(value: any, err: ValidationError, inputName?: string): JoiValidationError {
129
- const tokens: string[] = []
130
-
131
- const inputId = _isObject(value) ? (value['id'] as string) : undefined
132
-
133
- if (inputId || inputName) {
134
- inputName ||= value?.constructor?.name
135
-
136
- tokens.push('Invalid ' + [inputName, inputId].filter(Boolean).join('.'))
137
- }
138
-
139
- const annotation = err.annotate(stripColors)
140
-
141
- if (annotation.length > 100) {
142
- // For rather large annotations - we include up to 5 errors up front, before printing the whole object.
143
-
144
- // Up to 5 `details`
145
- tokens.push(
146
- ...err.details.slice(0, 5).map(i => {
147
- return i.message
148
- // Currently not specifying the path, to not "overwhelm" the message
149
- // Can be reverted if needed.
150
- // let msg = i.message
151
- // const paths = i.path.filter(Boolean).join('.')
152
- // if (paths) msg += ` @ .${paths}`
153
- // return msg
154
- }),
155
- )
156
-
157
- if (err.details.length > 5) tokens.push(`... ${err.details.length} errors in total`)
158
- tokens.push('')
159
- }
160
-
161
- tokens.push(
162
- _truncateMiddle(annotation, 4000, `\n... ${_hb(annotation.length)} message truncated ...\n`),
163
- )
164
-
165
- const msg = tokens.join('\n')
166
-
167
- const data: JoiValidationErrorData = {
168
- joiValidationErrorItems: err.details,
169
- ...(inputName && { joiValidationInputName: inputName }),
170
- ...(inputId && { joiValidationInputId: inputId }),
171
- }
172
-
173
- // Make annotation non-enumerable, to not get it automatically printed,
174
- // but still accessible
175
- Object.defineProperty(data, 'annotation', {
176
- writable: true,
177
- configurable: true,
178
- enumerable: false,
179
- value: annotation,
180
- })
181
-
182
- return new JoiValidationError(msg, data)
183
- }
@@ -1,46 +0,0 @@
1
- import type Joi from 'joi'
2
- import type { Extension, NumberSchema as JoiNumberSchema } from 'joi'
3
-
4
- export interface NumberSchema<TSchema = number> extends JoiNumberSchema<TSchema> {
5
- dividable: (q: number) => this
6
- }
7
-
8
- export function numberExtensions(joi: typeof Joi): Extension {
9
- return {
10
- base: joi.number(),
11
- type: 'number',
12
- messages: {
13
- 'number.dividable': `"{{#label}}" must be dividable by {{#q}}`,
14
- },
15
- // validate (v, helpers) {
16
- // console.log('number validate called', {v})
17
- // },
18
- rules: {
19
- // Based on: https://github.com/hapijs/joi/blob/master/API.md#extensions
20
- dividable: {
21
- multi: true,
22
- method(q: number) {
23
- return this.$_addRule({
24
- name: 'dividable',
25
- args: { q },
26
- })
27
- },
28
- args: [
29
- {
30
- name: 'q',
31
- ref: true,
32
- assert: v => typeof v === 'number' && !Number.isNaN(v),
33
- message: 'must be a number',
34
- },
35
- ],
36
- validate(v: number, helpers, args) {
37
- if (v % args['q'] === 0) {
38
- return v
39
- }
40
-
41
- return helpers.error('number.dividable', args)
42
- },
43
- },
44
- },
45
- }
46
- }