joi 14.2.0 → 17.2.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/LICENSE.md +10 -0
- package/README.md +9 -118
- package/dist/joi-browser.min.js +1 -0
- package/lib/annotate.js +175 -0
- package/lib/base.js +1068 -0
- package/lib/cache.js +143 -0
- package/lib/common.js +216 -0
- package/lib/compile.js +283 -0
- package/lib/errors.js +160 -269
- package/lib/extend.js +312 -0
- package/lib/index.d.ts +2200 -0
- package/lib/index.js +179 -347
- package/lib/manifest.js +476 -0
- package/lib/messages.js +178 -0
- package/lib/modify.js +267 -0
- package/lib/ref.js +386 -25
- package/lib/schemas.js +291 -15
- package/lib/state.js +152 -0
- package/lib/template.js +427 -0
- package/lib/trace.js +346 -0
- package/lib/types/alternatives.js +329 -0
- package/lib/types/any.js +174 -0
- package/lib/types/array.js +775 -0
- package/lib/types/binary.js +98 -0
- package/lib/types/boolean.js +150 -0
- package/lib/types/date.js +233 -0
- package/lib/types/function.js +93 -0
- package/lib/types/keys.js +1043 -0
- package/lib/types/link.js +168 -0
- package/lib/types/number.js +335 -0
- package/lib/types/object.js +22 -0
- package/lib/types/string.js +820 -0
- package/lib/types/symbol.js +102 -0
- package/lib/validator.js +650 -0
- package/lib/values.js +263 -0
- package/package.json +34 -29
- package/CHANGELOG.md +0 -3
- package/LICENSE +0 -25
- package/lib/cast.js +0 -64
- package/lib/language.js +0 -166
- package/lib/set.js +0 -191
- package/lib/types/alternatives/index.js +0 -218
- package/lib/types/any/index.js +0 -978
- package/lib/types/any/settings.js +0 -36
- package/lib/types/array/index.js +0 -707
- package/lib/types/binary/index.js +0 -100
- package/lib/types/boolean/index.js +0 -100
- package/lib/types/date/index.js +0 -182
- package/lib/types/func/index.js +0 -90
- package/lib/types/lazy/index.js +0 -82
- package/lib/types/number/index.js +0 -248
- package/lib/types/object/index.js +0 -957
- package/lib/types/state.js +0 -11
- package/lib/types/string/index.js +0 -703
- package/lib/types/string/ip.js +0 -54
- package/lib/types/string/rfc3986.js +0 -219
- package/lib/types/string/uri.js +0 -46
- package/lib/types/symbol/index.js +0 -93
- package/lib/types/symbols.js +0 -5
package/lib/types/state.js
DELETED
|
@@ -1,703 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Load modules
|
|
4
|
-
|
|
5
|
-
const Net = require('net');
|
|
6
|
-
const Hoek = require('hoek');
|
|
7
|
-
const Any = require('../any');
|
|
8
|
-
const Ref = require('../../ref');
|
|
9
|
-
const JoiDate = require('../date');
|
|
10
|
-
const Uri = require('./uri');
|
|
11
|
-
const Ip = require('./ip');
|
|
12
|
-
|
|
13
|
-
let Isemail; // Loaded on demand
|
|
14
|
-
|
|
15
|
-
// Declare internals
|
|
16
|
-
|
|
17
|
-
const internals = {
|
|
18
|
-
uriRegex: Uri.createUriRegex(),
|
|
19
|
-
ipRegex: Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], 'optional'),
|
|
20
|
-
guidBrackets: {
|
|
21
|
-
'{': '}', '[': ']', '(': ')', '': ''
|
|
22
|
-
},
|
|
23
|
-
guidVersions: {
|
|
24
|
-
uuidv1: '1',
|
|
25
|
-
uuidv2: '2',
|
|
26
|
-
uuidv3: '3',
|
|
27
|
-
uuidv4: '4',
|
|
28
|
-
uuidv5: '5'
|
|
29
|
-
},
|
|
30
|
-
cidrPresences: ['required', 'optional', 'forbidden'],
|
|
31
|
-
normalizationForms: ['NFC', 'NFD', 'NFKC', 'NFKD']
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
internals.String = class extends Any {
|
|
35
|
-
|
|
36
|
-
constructor() {
|
|
37
|
-
|
|
38
|
-
super();
|
|
39
|
-
this._type = 'string';
|
|
40
|
-
this._invalids.add('');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
_base(value, state, options) {
|
|
44
|
-
|
|
45
|
-
if (typeof value === 'string' &&
|
|
46
|
-
options.convert) {
|
|
47
|
-
|
|
48
|
-
if (this._flags.normalize) {
|
|
49
|
-
value = value.normalize(this._flags.normalize);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (this._flags.case) {
|
|
53
|
-
value = (this._flags.case === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase());
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (this._flags.trim) {
|
|
57
|
-
value = value.trim();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (this._inner.replacements) {
|
|
61
|
-
|
|
62
|
-
for (let i = 0; i < this._inner.replacements.length; ++i) {
|
|
63
|
-
const replacement = this._inner.replacements[i];
|
|
64
|
-
value = value.replace(replacement.pattern, replacement.replacement);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (this._flags.truncate) {
|
|
69
|
-
for (let i = 0; i < this._tests.length; ++i) {
|
|
70
|
-
const test = this._tests[i];
|
|
71
|
-
if (test.name === 'max') {
|
|
72
|
-
value = value.slice(0, test.arg);
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (this._flags.byteAligned && value.length % 2 !== 0) {
|
|
79
|
-
value = `0${value}`;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
value,
|
|
85
|
-
errors: (typeof value === 'string') ? null : this.createError('string.base', { value }, state, options)
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
insensitive() {
|
|
90
|
-
|
|
91
|
-
if (this._flags.insensitive) {
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const obj = this.clone();
|
|
96
|
-
obj._flags.insensitive = true;
|
|
97
|
-
return obj;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
creditCard() {
|
|
101
|
-
|
|
102
|
-
return this._test('creditCard', undefined, function (value, state, options) {
|
|
103
|
-
|
|
104
|
-
let i = value.length;
|
|
105
|
-
let sum = 0;
|
|
106
|
-
let mul = 1;
|
|
107
|
-
|
|
108
|
-
while (i--) {
|
|
109
|
-
const char = value.charAt(i) * mul;
|
|
110
|
-
sum = sum + (char - (char > 9) * 9);
|
|
111
|
-
mul = mul ^ 3;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const check = (sum % 10 === 0) && (sum > 0);
|
|
115
|
-
return check ? value : this.createError('string.creditCard', { value }, state, options);
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
regex(pattern, patternOptions) {
|
|
120
|
-
|
|
121
|
-
Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp');
|
|
122
|
-
Hoek.assert(!pattern.flags.includes('g') && !pattern.flags.includes('y'), 'pattern should not use global or sticky mode');
|
|
123
|
-
|
|
124
|
-
const patternObject = { pattern };
|
|
125
|
-
|
|
126
|
-
if (typeof patternOptions === 'string') {
|
|
127
|
-
patternObject.name = patternOptions;
|
|
128
|
-
}
|
|
129
|
-
else if (typeof patternOptions === 'object') {
|
|
130
|
-
patternObject.invert = !!patternOptions.invert;
|
|
131
|
-
|
|
132
|
-
if (patternOptions.name) {
|
|
133
|
-
patternObject.name = patternOptions.name;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const errorCode = ['string.regex', patternObject.invert ? '.invert' : '', patternObject.name ? '.name' : '.base'].join('');
|
|
138
|
-
|
|
139
|
-
return this._test('regex', patternObject, function (value, state, options) {
|
|
140
|
-
|
|
141
|
-
const patternMatch = patternObject.pattern.test(value);
|
|
142
|
-
|
|
143
|
-
if (patternMatch ^ patternObject.invert) {
|
|
144
|
-
return value;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return this.createError(errorCode, { name: patternObject.name, pattern: patternObject.pattern, value }, state, options);
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
alphanum() {
|
|
152
|
-
|
|
153
|
-
return this._test('alphanum', undefined, function (value, state, options) {
|
|
154
|
-
|
|
155
|
-
if (/^[a-zA-Z0-9]+$/.test(value)) {
|
|
156
|
-
return value;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return this.createError('string.alphanum', { value }, state, options);
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
token() {
|
|
164
|
-
|
|
165
|
-
return this._test('token', undefined, function (value, state, options) {
|
|
166
|
-
|
|
167
|
-
if (/^\w+$/.test(value)) {
|
|
168
|
-
return value;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return this.createError('string.token', { value }, state, options);
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
email(isEmailOptions) {
|
|
176
|
-
|
|
177
|
-
if (isEmailOptions) {
|
|
178
|
-
Hoek.assert(typeof isEmailOptions === 'object', 'email options must be an object');
|
|
179
|
-
Hoek.assert(typeof isEmailOptions.checkDNS === 'undefined', 'checkDNS option is not supported');
|
|
180
|
-
Hoek.assert(typeof isEmailOptions.tldWhitelist === 'undefined' ||
|
|
181
|
-
typeof isEmailOptions.tldWhitelist === 'object', 'tldWhitelist must be an array or object');
|
|
182
|
-
Hoek.assert(
|
|
183
|
-
typeof isEmailOptions.minDomainAtoms === 'undefined' ||
|
|
184
|
-
Number.isSafeInteger(isEmailOptions.minDomainAtoms) &&
|
|
185
|
-
isEmailOptions.minDomainAtoms > 0,
|
|
186
|
-
'minDomainAtoms must be a positive integer'
|
|
187
|
-
);
|
|
188
|
-
Hoek.assert(
|
|
189
|
-
typeof isEmailOptions.errorLevel === 'undefined' ||
|
|
190
|
-
typeof isEmailOptions.errorLevel === 'boolean' ||
|
|
191
|
-
(
|
|
192
|
-
Number.isSafeInteger(isEmailOptions.errorLevel) &&
|
|
193
|
-
isEmailOptions.errorLevel >= 0
|
|
194
|
-
),
|
|
195
|
-
'errorLevel must be a non-negative integer or boolean'
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return this._test('email', isEmailOptions, function (value, state, options) {
|
|
200
|
-
|
|
201
|
-
Isemail = Isemail || require('isemail');
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
const result = Isemail.validate(value, isEmailOptions);
|
|
205
|
-
if (result === true || result === 0) {
|
|
206
|
-
return value;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
catch (e) { }
|
|
210
|
-
|
|
211
|
-
return this.createError('string.email', { value }, state, options);
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
ip(ipOptions = {}) {
|
|
216
|
-
|
|
217
|
-
let regex = internals.ipRegex;
|
|
218
|
-
Hoek.assert(typeof ipOptions === 'object', 'options must be an object');
|
|
219
|
-
|
|
220
|
-
if (ipOptions.cidr) {
|
|
221
|
-
Hoek.assert(typeof ipOptions.cidr === 'string', 'cidr must be a string');
|
|
222
|
-
ipOptions.cidr = ipOptions.cidr.toLowerCase();
|
|
223
|
-
|
|
224
|
-
Hoek.assert(Hoek.contain(internals.cidrPresences, ipOptions.cidr), 'cidr must be one of ' + internals.cidrPresences.join(', '));
|
|
225
|
-
|
|
226
|
-
// If we only received a `cidr` setting, create a regex for it. But we don't need to create one if `cidr` is "optional" since that is the default
|
|
227
|
-
if (!ipOptions.version && ipOptions.cidr !== 'optional') {
|
|
228
|
-
regex = Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], ipOptions.cidr);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
|
|
233
|
-
// Set our default cidr strategy
|
|
234
|
-
ipOptions.cidr = 'optional';
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
let versions;
|
|
238
|
-
if (ipOptions.version) {
|
|
239
|
-
if (!Array.isArray(ipOptions.version)) {
|
|
240
|
-
ipOptions.version = [ipOptions.version];
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
Hoek.assert(ipOptions.version.length >= 1, 'version must have at least 1 version specified');
|
|
244
|
-
|
|
245
|
-
versions = [];
|
|
246
|
-
for (let i = 0; i < ipOptions.version.length; ++i) {
|
|
247
|
-
let version = ipOptions.version[i];
|
|
248
|
-
Hoek.assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
|
|
249
|
-
version = version.toLowerCase();
|
|
250
|
-
Hoek.assert(Ip.versions[version], 'version at position ' + i + ' must be one of ' + Object.keys(Ip.versions).join(', '));
|
|
251
|
-
versions.push(version);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Make sure we have a set of versions
|
|
255
|
-
versions = Array.from(new Set(versions));
|
|
256
|
-
|
|
257
|
-
regex = Ip.createIpRegex(versions, ipOptions.cidr);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return this._test('ip', ipOptions, function (value, state, options) {
|
|
261
|
-
|
|
262
|
-
if (regex.test(value)) {
|
|
263
|
-
return value;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (versions) {
|
|
267
|
-
return this.createError('string.ipVersion', { value, cidr: ipOptions.cidr, version: versions }, state, options);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return this.createError('string.ip', { value, cidr: ipOptions.cidr }, state, options);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
uri(uriOptions) {
|
|
275
|
-
|
|
276
|
-
let customScheme = '';
|
|
277
|
-
let allowRelative = false;
|
|
278
|
-
let relativeOnly = false;
|
|
279
|
-
let allowQuerySquareBrackets = false;
|
|
280
|
-
let regex = internals.uriRegex;
|
|
281
|
-
|
|
282
|
-
if (uriOptions) {
|
|
283
|
-
Hoek.assert(typeof uriOptions === 'object', 'options must be an object');
|
|
284
|
-
|
|
285
|
-
const unknownOptions = Object.keys(uriOptions).filter((key) => !['scheme', 'allowRelative', 'relativeOnly', 'allowQuerySquareBrackets'].includes(key));
|
|
286
|
-
Hoek.assert(unknownOptions.length === 0, `options contain unknown keys: ${unknownOptions}`);
|
|
287
|
-
|
|
288
|
-
if (uriOptions.scheme) {
|
|
289
|
-
Hoek.assert(uriOptions.scheme instanceof RegExp || typeof uriOptions.scheme === 'string' || Array.isArray(uriOptions.scheme), 'scheme must be a RegExp, String, or Array');
|
|
290
|
-
|
|
291
|
-
if (!Array.isArray(uriOptions.scheme)) {
|
|
292
|
-
uriOptions.scheme = [uriOptions.scheme];
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
Hoek.assert(uriOptions.scheme.length >= 1, 'scheme must have at least 1 scheme specified');
|
|
296
|
-
|
|
297
|
-
// Flatten the array into a string to be used to match the schemes.
|
|
298
|
-
for (let i = 0; i < uriOptions.scheme.length; ++i) {
|
|
299
|
-
const scheme = uriOptions.scheme[i];
|
|
300
|
-
Hoek.assert(scheme instanceof RegExp || typeof scheme === 'string', 'scheme at position ' + i + ' must be a RegExp or String');
|
|
301
|
-
|
|
302
|
-
// Add OR separators if a value already exists
|
|
303
|
-
customScheme = customScheme + (customScheme ? '|' : '');
|
|
304
|
-
|
|
305
|
-
// If someone wants to match HTTP or HTTPS for example then we need to support both RegExp and String so we don't escape their pattern unknowingly.
|
|
306
|
-
if (scheme instanceof RegExp) {
|
|
307
|
-
customScheme = customScheme + scheme.source;
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
310
|
-
Hoek.assert(/[a-zA-Z][a-zA-Z0-9+-\.]*/.test(scheme), 'scheme at position ' + i + ' must be a valid scheme');
|
|
311
|
-
customScheme = customScheme + Hoek.escapeRegex(scheme);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (uriOptions.allowRelative) {
|
|
317
|
-
allowRelative = true;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (uriOptions.relativeOnly) {
|
|
321
|
-
relativeOnly = true;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (uriOptions.allowQuerySquareBrackets) {
|
|
325
|
-
allowQuerySquareBrackets = true;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (customScheme || allowRelative || relativeOnly || allowQuerySquareBrackets) {
|
|
330
|
-
regex = Uri.createUriRegex(customScheme, allowRelative, relativeOnly, allowQuerySquareBrackets);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return this._test('uri', uriOptions, function (value, state, options) {
|
|
334
|
-
|
|
335
|
-
if (regex.test(value)) {
|
|
336
|
-
return value;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (relativeOnly) {
|
|
340
|
-
return this.createError('string.uriRelativeOnly', { value }, state, options);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if (customScheme) {
|
|
344
|
-
return this.createError('string.uriCustomScheme', { scheme: customScheme, value }, state, options);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return this.createError('string.uri', { value }, state, options);
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
isoDate() {
|
|
352
|
-
|
|
353
|
-
return this._test('isoDate', undefined, function (value, state, options) {
|
|
354
|
-
|
|
355
|
-
if (JoiDate._isIsoDate(value)) {
|
|
356
|
-
if (!options.convert) {
|
|
357
|
-
return value;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const d = new Date(value);
|
|
361
|
-
if (!isNaN(d.getTime())) {
|
|
362
|
-
return d.toISOString();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return this.createError('string.isoDate', { value }, state, options);
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
guid(guidOptions) {
|
|
371
|
-
|
|
372
|
-
let versionNumbers = '';
|
|
373
|
-
|
|
374
|
-
if (guidOptions && guidOptions.version) {
|
|
375
|
-
if (!Array.isArray(guidOptions.version)) {
|
|
376
|
-
guidOptions.version = [guidOptions.version];
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
Hoek.assert(guidOptions.version.length >= 1, 'version must have at least 1 valid version specified');
|
|
380
|
-
const versions = new Set();
|
|
381
|
-
|
|
382
|
-
for (let i = 0; i < guidOptions.version.length; ++i) {
|
|
383
|
-
let version = guidOptions.version[i];
|
|
384
|
-
Hoek.assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
|
|
385
|
-
version = version.toLowerCase();
|
|
386
|
-
const versionNumber = internals.guidVersions[version];
|
|
387
|
-
Hoek.assert(versionNumber, 'version at position ' + i + ' must be one of ' + Object.keys(internals.guidVersions).join(', '));
|
|
388
|
-
Hoek.assert(!(versions.has(versionNumber)), 'version at position ' + i + ' must not be a duplicate.');
|
|
389
|
-
|
|
390
|
-
versionNumbers += versionNumber;
|
|
391
|
-
versions.add(versionNumber);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const guidRegex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}([:-]?)[0-9A-F]{4}\\2?[${versionNumbers || '0-9A-F'}][0-9A-F]{3}\\2?[${versionNumbers ? '89AB' : '0-9A-F'}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`, 'i');
|
|
396
|
-
|
|
397
|
-
return this._test('guid', guidOptions, function (value, state, options) {
|
|
398
|
-
|
|
399
|
-
const results = guidRegex.exec(value);
|
|
400
|
-
|
|
401
|
-
if (!results) {
|
|
402
|
-
return this.createError('string.guid', { value }, state, options);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Matching braces
|
|
406
|
-
if (internals.guidBrackets[results[1]] !== results[results.length - 1]) {
|
|
407
|
-
return this.createError('string.guid', { value }, state, options);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return value;
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
hex(hexOptions = {}) {
|
|
415
|
-
|
|
416
|
-
Hoek.assert(typeof hexOptions === 'object', 'hex options must be an object');
|
|
417
|
-
Hoek.assert(typeof hexOptions.byteAligned === 'undefined' || typeof hexOptions.byteAligned === 'boolean',
|
|
418
|
-
'byteAligned must be boolean');
|
|
419
|
-
|
|
420
|
-
const byteAligned = hexOptions.byteAligned === true;
|
|
421
|
-
const regex = /^[a-f0-9]+$/i;
|
|
422
|
-
|
|
423
|
-
const obj = this._test('hex', regex, function (value, state, options) {
|
|
424
|
-
|
|
425
|
-
if (regex.test(value)) {
|
|
426
|
-
if (byteAligned && value.length % 2 !== 0) {
|
|
427
|
-
return this.createError('string.hexAlign', { value }, state, options);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
return value;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return this.createError('string.hex', { value }, state, options);
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
if (byteAligned) {
|
|
437
|
-
obj._flags.byteAligned = true;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return obj;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
base64(base64Options = {}) {
|
|
444
|
-
|
|
445
|
-
// Validation.
|
|
446
|
-
Hoek.assert(typeof base64Options === 'object', 'base64 options must be an object');
|
|
447
|
-
Hoek.assert(typeof base64Options.paddingRequired === 'undefined' || typeof base64Options.paddingRequired === 'boolean',
|
|
448
|
-
'paddingRequired must be boolean');
|
|
449
|
-
|
|
450
|
-
// Determine if padding is required.
|
|
451
|
-
const paddingRequired = base64Options.paddingRequired === false ?
|
|
452
|
-
base64Options.paddingRequired
|
|
453
|
-
: base64Options.paddingRequired || true;
|
|
454
|
-
|
|
455
|
-
// Set validation based on preference.
|
|
456
|
-
const regex = paddingRequired ?
|
|
457
|
-
// Padding is required.
|
|
458
|
-
/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
|
|
459
|
-
// Padding is optional.
|
|
460
|
-
: /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/;
|
|
461
|
-
|
|
462
|
-
return this._test('base64', regex, function (value, state, options) {
|
|
463
|
-
|
|
464
|
-
if (regex.test(value)) {
|
|
465
|
-
return value;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
return this.createError('string.base64', { value }, state, options);
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
dataUri(dataUriOptions = {}) {
|
|
473
|
-
|
|
474
|
-
const regex = /^data:[\w+.-]+\/[\w+.-]+;((charset=[\w-]+|base64),)?(.*)$/;
|
|
475
|
-
|
|
476
|
-
// Determine if padding is required.
|
|
477
|
-
const paddingRequired = dataUriOptions.paddingRequired === false ?
|
|
478
|
-
dataUriOptions.paddingRequired
|
|
479
|
-
: dataUriOptions.paddingRequired || true;
|
|
480
|
-
|
|
481
|
-
const base64regex = paddingRequired ?
|
|
482
|
-
/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
|
|
483
|
-
: /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/;
|
|
484
|
-
|
|
485
|
-
return this._test('dataUri', regex, function (value, state, options) {
|
|
486
|
-
|
|
487
|
-
const matches = value.match(regex);
|
|
488
|
-
|
|
489
|
-
if (matches) {
|
|
490
|
-
if (!matches[2]) {
|
|
491
|
-
return value;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (matches[2] !== 'base64') {
|
|
495
|
-
return value;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (base64regex.test(matches[3])) {
|
|
499
|
-
return value;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
return this.createError('string.dataUri', { value }, state, options);
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
hostname() {
|
|
508
|
-
|
|
509
|
-
const regex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
|
|
510
|
-
|
|
511
|
-
return this._test('hostname', undefined, function (value, state, options) {
|
|
512
|
-
|
|
513
|
-
if ((value.length <= 255 && regex.test(value)) ||
|
|
514
|
-
Net.isIPv6(value)) {
|
|
515
|
-
|
|
516
|
-
return value;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
return this.createError('string.hostname', { value }, state, options);
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
normalize(form = 'NFC') {
|
|
524
|
-
|
|
525
|
-
Hoek.assert(Hoek.contain(internals.normalizationForms, form), 'normalization form must be one of ' + internals.normalizationForms.join(', '));
|
|
526
|
-
|
|
527
|
-
const obj = this._test('normalize', form, function (value, state, options) {
|
|
528
|
-
|
|
529
|
-
if (options.convert ||
|
|
530
|
-
value === value.normalize(form)) {
|
|
531
|
-
|
|
532
|
-
return value;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return this.createError('string.normalize', { value, form }, state, options);
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
obj._flags.normalize = form;
|
|
539
|
-
return obj;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
lowercase() {
|
|
543
|
-
|
|
544
|
-
const obj = this._test('lowercase', undefined, function (value, state, options) {
|
|
545
|
-
|
|
546
|
-
if (options.convert ||
|
|
547
|
-
value === value.toLocaleLowerCase()) {
|
|
548
|
-
|
|
549
|
-
return value;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return this.createError('string.lowercase', { value }, state, options);
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
obj._flags.case = 'lower';
|
|
556
|
-
return obj;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
uppercase() {
|
|
560
|
-
|
|
561
|
-
const obj = this._test('uppercase', undefined, function (value, state, options) {
|
|
562
|
-
|
|
563
|
-
if (options.convert ||
|
|
564
|
-
value === value.toLocaleUpperCase()) {
|
|
565
|
-
|
|
566
|
-
return value;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return this.createError('string.uppercase', { value }, state, options);
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
obj._flags.case = 'upper';
|
|
573
|
-
return obj;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
trim(enabled = true) {
|
|
577
|
-
|
|
578
|
-
Hoek.assert(typeof enabled === 'boolean', 'option must be a boolean');
|
|
579
|
-
|
|
580
|
-
if ((this._flags.trim && enabled) || (!this._flags.trim && !enabled)) {
|
|
581
|
-
return this;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
let obj;
|
|
585
|
-
if (enabled) {
|
|
586
|
-
obj = this._test('trim', undefined, function (value, state, options) {
|
|
587
|
-
|
|
588
|
-
if (options.convert ||
|
|
589
|
-
value === value.trim()) {
|
|
590
|
-
|
|
591
|
-
return value;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
return this.createError('string.trim', { value }, state, options);
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
obj = this.clone();
|
|
599
|
-
obj._tests = obj._tests.filter((test) => test.name !== 'trim');
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
obj._flags.trim = enabled;
|
|
603
|
-
return obj;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
replace(pattern, replacement) {
|
|
607
|
-
|
|
608
|
-
if (typeof pattern === 'string') {
|
|
609
|
-
pattern = new RegExp(Hoek.escapeRegex(pattern), 'g');
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp');
|
|
613
|
-
Hoek.assert(typeof replacement === 'string', 'replacement must be a String');
|
|
614
|
-
|
|
615
|
-
// This can not be considere a test like trim, we can't "reject"
|
|
616
|
-
// anything from this rule, so just clone the current object
|
|
617
|
-
const obj = this.clone();
|
|
618
|
-
|
|
619
|
-
if (!obj._inner.replacements) {
|
|
620
|
-
obj._inner.replacements = [];
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
obj._inner.replacements.push({
|
|
624
|
-
pattern,
|
|
625
|
-
replacement
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
return obj;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
truncate(enabled) {
|
|
632
|
-
|
|
633
|
-
const value = enabled === undefined ? true : !!enabled;
|
|
634
|
-
|
|
635
|
-
if (this._flags.truncate === value) {
|
|
636
|
-
return this;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
const obj = this.clone();
|
|
640
|
-
obj._flags.truncate = value;
|
|
641
|
-
return obj;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
internals.compare = function (type, compare) {
|
|
647
|
-
|
|
648
|
-
return function (limit, encoding) {
|
|
649
|
-
|
|
650
|
-
const isRef = Ref.isRef(limit);
|
|
651
|
-
|
|
652
|
-
Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
|
|
653
|
-
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);
|
|
654
|
-
|
|
655
|
-
return this._test(type, limit, function (value, state, options) {
|
|
656
|
-
|
|
657
|
-
let compareTo;
|
|
658
|
-
if (isRef) {
|
|
659
|
-
compareTo = limit(state.reference || state.parent, options);
|
|
660
|
-
|
|
661
|
-
if (!Number.isSafeInteger(compareTo)) {
|
|
662
|
-
return this.createError('string.ref', { ref: limit, value: compareTo }, state, options);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
else {
|
|
666
|
-
compareTo = limit;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
if (compare(value, compareTo, encoding)) {
|
|
670
|
-
return value;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
return this.createError('string.' + type, { limit: compareTo, value, encoding }, state, options);
|
|
674
|
-
});
|
|
675
|
-
};
|
|
676
|
-
};
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
internals.String.prototype.min = internals.compare('min', (value, limit, encoding) => {
|
|
680
|
-
|
|
681
|
-
const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
|
|
682
|
-
return length >= limit;
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
internals.String.prototype.max = internals.compare('max', (value, limit, encoding) => {
|
|
687
|
-
|
|
688
|
-
const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
|
|
689
|
-
return length <= limit;
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
internals.String.prototype.length = internals.compare('length', (value, limit, encoding) => {
|
|
694
|
-
|
|
695
|
-
const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
|
|
696
|
-
return length === limit;
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
// Aliases
|
|
700
|
-
|
|
701
|
-
internals.String.prototype.uuid = internals.String.prototype.guid;
|
|
702
|
-
|
|
703
|
-
module.exports = new internals.String();
|