@defra/fcp-sfd-frontend-engine 0.6.0 → 0.7.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/index.cjs +107 -78
- package/dist/index.js +105 -76
- package/package.json +3 -3
- package/src/constants/index.js +5 -4
- package/src/constants/patterns.js +9 -1
- package/src/schemas/business/business-email-schema.js +3 -2
- package/src/schemas/business/business-name-schema.js +4 -2
- package/src/schemas/business/business-phone-schema.js +7 -3
- package/src/schemas/business/business-sbi-schema.js +3 -2
- package/src/schemas/business/business-schemas.js +1 -1
- package/src/schemas/business/business-vat-schema.js +3 -2
- package/src/schemas/customer/customer-crn-schema.js +3 -2
- package/src/schemas/personal/personal-dob-schema.js +16 -6
- package/src/schemas/personal/personal-email-schema.js +3 -2
- package/src/schemas/personal/personal-name-schema.js +10 -4
- package/src/schemas/personal/personal-phone-schema.js +9 -8
- package/src/schemas/personal/personal-schemas.js +1 -1
- package/src/schemas/{address-schema.js → shared/address-schema.js} +23 -9
- package/src/utils/joi.js +21 -0
- package/src/constants/validation-fields-export.js +0 -17
package/dist/index.cjs
CHANGED
|
@@ -129,17 +129,36 @@ var mappers = {
|
|
|
129
129
|
businessDetails: mapBusinessDetails
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
-
// src/
|
|
132
|
+
// src/utils/joi.js
|
|
133
133
|
var import_joi = __toESM(require("joi"), 1);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
|
|
135
|
+
// src/constants/patterns.js
|
|
136
|
+
var PHONE_NUMBER_PATTERN = /^\+?(?=[\d ()]*\d)[\d ()]*$/;
|
|
137
|
+
var NO_CONTROL_CHARS_PATTERN = /^[^\x00-\x1f\x7f-\x9f]*$/;
|
|
138
|
+
|
|
139
|
+
// src/utils/joi.js
|
|
140
|
+
var Joi = import_joi.default.extend((joi) => ({
|
|
141
|
+
type: "string",
|
|
142
|
+
base: joi.string(),
|
|
143
|
+
messages: {
|
|
144
|
+
"string.noControlChars": "Field must not contain invalid characters"
|
|
145
|
+
},
|
|
146
|
+
validate(value, helpers) {
|
|
147
|
+
if (typeof value === "string" && !NO_CONTROL_CHARS_PATTERN.test(value)) {
|
|
148
|
+
return { value, errors: helpers.error("string.noControlChars") };
|
|
149
|
+
}
|
|
150
|
+
return { value };
|
|
151
|
+
}
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
// src/schemas/business/business-sbi-schema.js
|
|
155
|
+
var businessSbiSchema = Joi.object({
|
|
156
|
+
sbi: Joi.string().pattern(/^\d{9}$/).allow("").optional().messages({
|
|
157
|
+
"string.pattern.base": "Enter the full SBI",
|
|
158
|
+
"string.noControlChars": "SBI must not contain invalid characters"
|
|
137
159
|
})
|
|
138
160
|
});
|
|
139
161
|
|
|
140
|
-
// src/schemas/address-schema.js
|
|
141
|
-
var import_joi2 = __toESM(require("joi"), 1);
|
|
142
|
-
|
|
143
162
|
// src/constants/validation-fields.js
|
|
144
163
|
var FIRST_NAME_MAX = 100;
|
|
145
164
|
var LAST_NAME_MAX = 100;
|
|
@@ -155,53 +174,59 @@ var COUNTY_MAX = 60;
|
|
|
155
174
|
var COUNTRY_MAX = 60;
|
|
156
175
|
var POSTCODE_MAX = 8;
|
|
157
176
|
|
|
158
|
-
// src/schemas/address-schema.js
|
|
159
|
-
var addressSchema =
|
|
160
|
-
address1:
|
|
177
|
+
// src/schemas/shared/address-schema.js
|
|
178
|
+
var addressSchema = Joi.object({
|
|
179
|
+
address1: Joi.string().trim().required().max(ADDRESS_LINE_MAX).messages({
|
|
161
180
|
"string.empty": "Enter address line 1, typically the building and street",
|
|
162
181
|
"string.max": `Address line 1 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
163
|
-
"any.required": "Enter address line 1, typically the building and street"
|
|
182
|
+
"any.required": "Enter address line 1, typically the building and street",
|
|
183
|
+
"string.noControlChars": "Address line 1 must not contain invalid characters"
|
|
164
184
|
}),
|
|
165
|
-
address2:
|
|
166
|
-
"string.max": `Address line 2 must be ${ADDRESS_LINE_MAX} characters or less
|
|
185
|
+
address2: Joi.string().trim().allow("").max(ADDRESS_LINE_MAX).messages({
|
|
186
|
+
"string.max": `Address line 2 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
187
|
+
"string.noControlChars": "Address line 2 must not contain invalid characters"
|
|
167
188
|
}),
|
|
168
|
-
address3:
|
|
169
|
-
"string.max": `Address line 3 must be ${ADDRESS_LINE_MAX} characters or less
|
|
189
|
+
address3: Joi.string().trim().allow("").max(ADDRESS_LINE_MAX).messages({
|
|
190
|
+
"string.max": `Address line 3 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
191
|
+
"string.noControlChars": "Address line 3 must not contain invalid characters"
|
|
170
192
|
}),
|
|
171
|
-
city:
|
|
193
|
+
city: Joi.string().trim().required().max(TOWN_CITY_MAX).messages({
|
|
172
194
|
"string.empty": "Enter town or city",
|
|
173
195
|
"string.max": `Town or city must be ${TOWN_CITY_MAX} characters or less`,
|
|
174
|
-
"any.required": "Enter town or city"
|
|
196
|
+
"any.required": "Enter town or city",
|
|
197
|
+
"string.noControlChars": "Town or city must not contain invalid characters"
|
|
175
198
|
}),
|
|
176
|
-
county:
|
|
177
|
-
"string.max": `County must be ${COUNTY_MAX} characters or less
|
|
199
|
+
county: Joi.string().trim().allow("").max(COUNTY_MAX).messages({
|
|
200
|
+
"string.max": `County must be ${COUNTY_MAX} characters or less`,
|
|
201
|
+
"string.noControlChars": "County must not contain invalid characters"
|
|
178
202
|
}),
|
|
179
|
-
postcode:
|
|
203
|
+
postcode: Joi.string().trim().required().max(POSTCODE_MAX).messages({
|
|
180
204
|
"any.required": "Enter a postal code or zip code",
|
|
181
205
|
"string.empty": "Enter a postal code or zip code",
|
|
182
|
-
"string.max": `Postal code or zip code must be ${POSTCODE_MAX} characters or less
|
|
206
|
+
"string.max": `Postal code or zip code must be ${POSTCODE_MAX} characters or less`,
|
|
207
|
+
"string.noControlChars": "Postal code or zip code must not contain invalid characters"
|
|
183
208
|
}),
|
|
184
|
-
country:
|
|
209
|
+
country: Joi.string().trim().required().max(COUNTRY_MAX).messages({
|
|
185
210
|
"string.empty": "Enter a country",
|
|
186
211
|
"string.max": `Country must be ${COUNTRY_MAX} characters or less`,
|
|
187
|
-
"any.required": "Enter a country"
|
|
212
|
+
"any.required": "Enter a country",
|
|
213
|
+
"string.noControlChars": "Country must not contain invalid characters"
|
|
188
214
|
})
|
|
189
215
|
});
|
|
190
216
|
|
|
191
217
|
// src/schemas/business/business-name-schema.js
|
|
192
|
-
var
|
|
193
|
-
|
|
194
|
-
businessName: import_joi3.default.string().required().max(BUSINESS_NAME_MAX).messages({
|
|
218
|
+
var businessNameSchema = Joi.object({
|
|
219
|
+
businessName: Joi.string().trim().required().max(BUSINESS_NAME_MAX).messages({
|
|
195
220
|
"string.empty": "Enter business name",
|
|
196
221
|
"string.max": `Business name must be ${BUSINESS_NAME_MAX} characters or less`,
|
|
197
|
-
"any.required": "Enter business name"
|
|
222
|
+
"any.required": "Enter business name",
|
|
223
|
+
"string.noControlChars": "Business name must not contain invalid characters"
|
|
198
224
|
})
|
|
199
225
|
});
|
|
200
226
|
|
|
201
227
|
// src/schemas/business/business-email-schema.js
|
|
202
|
-
var
|
|
203
|
-
|
|
204
|
-
businessEmail: import_joi4.default.string().required().max(EMAIL_MAX).email({
|
|
228
|
+
var businessEmailSchema = Joi.object({
|
|
229
|
+
businessEmail: Joi.string().required().max(EMAIL_MAX).email({
|
|
205
230
|
minDomainSegments: 2,
|
|
206
231
|
tlds: {
|
|
207
232
|
allow: true
|
|
@@ -209,37 +234,34 @@ var businessEmailSchema = import_joi4.default.object({
|
|
|
209
234
|
}).messages({
|
|
210
235
|
"string.max": `Business email address must be ${EMAIL_MAX} characters or less`,
|
|
211
236
|
"string.empty": "Enter business email address",
|
|
212
|
-
"string.email": "Enter an email address, like name@example.com"
|
|
237
|
+
"string.email": "Enter an email address, like name@example.com",
|
|
238
|
+
"string.noControlChars": "Business email address must not contain invalid characters"
|
|
213
239
|
})
|
|
214
240
|
});
|
|
215
241
|
|
|
216
242
|
// src/schemas/business/business-phone-schema.js
|
|
217
|
-
var
|
|
218
|
-
|
|
219
|
-
// src/constants/patterns.js
|
|
220
|
-
var PHONE_NUMBER_PATTERN = /^\+?[0-9 ()]+$/;
|
|
221
|
-
|
|
222
|
-
// src/schemas/business/business-phone-schema.js
|
|
223
|
-
var businessPhoneSchema = import_joi5.default.object({
|
|
224
|
-
businessTelephone: import_joi5.default.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
243
|
+
var businessPhoneSchema = Joi.object({
|
|
244
|
+
businessTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
225
245
|
"string.min": `Business telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
226
246
|
"string.max": `Business telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
227
|
-
"string.pattern.base": "Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
247
|
+
"string.pattern.base": "Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
248
|
+
"string.noControlChars": "Business telephone number must not contain invalid characters"
|
|
228
249
|
}),
|
|
229
|
-
businessMobile:
|
|
250
|
+
businessMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
230
251
|
"string.min": `Business mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
231
252
|
"string.max": `Business mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
232
|
-
"string.pattern.base": "Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
253
|
+
"string.pattern.base": "Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
254
|
+
"string.noControlChars": "Business mobile phone number must not contain invalid characters"
|
|
233
255
|
})
|
|
234
256
|
}).or("businessTelephone", "businessMobile").messages({
|
|
235
257
|
"object.missing": "Enter at least one phone number"
|
|
236
258
|
});
|
|
237
259
|
|
|
238
260
|
// src/schemas/business/business-vat-schema.js
|
|
239
|
-
var
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
"string.
|
|
261
|
+
var businessVatSchema = Joi.object({
|
|
262
|
+
vatNumber: Joi.string().pattern(/^\d{9}$/).allow("").optional().messages({
|
|
263
|
+
"string.pattern.base": "Enter a VAT registration number, like 123456789",
|
|
264
|
+
"string.noControlChars": "VAT registration number must not contain invalid characters"
|
|
243
265
|
})
|
|
244
266
|
});
|
|
245
267
|
|
|
@@ -256,10 +278,10 @@ var businessSchemas = {
|
|
|
256
278
|
};
|
|
257
279
|
|
|
258
280
|
// src/schemas/customer/customer-crn-schema.js
|
|
259
|
-
var
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
"string.
|
|
281
|
+
var customerCrnSchema = Joi.object({
|
|
282
|
+
crn: Joi.string().pattern(/^\d{10}$/).allow("").optional().messages({
|
|
283
|
+
"string.pattern.base": "Enter the full CRN",
|
|
284
|
+
"string.noControlChars": "CRN must not contain invalid characters"
|
|
263
285
|
})
|
|
264
286
|
});
|
|
265
287
|
|
|
@@ -269,26 +291,25 @@ var customerSchemas = {
|
|
|
269
291
|
};
|
|
270
292
|
|
|
271
293
|
// src/schemas/personal/personal-name-schema.js
|
|
272
|
-
var
|
|
273
|
-
|
|
274
|
-
first: import_joi8.default.string().required().max(FIRST_NAME_MAX).messages({
|
|
294
|
+
var personalNameSchema = Joi.object({
|
|
295
|
+
first: Joi.string().trim().required().max(FIRST_NAME_MAX).messages({
|
|
275
296
|
"string.empty": "Enter first name",
|
|
276
297
|
"string.max": `First name must be ${FIRST_NAME_MAX} characters or less`,
|
|
277
|
-
"any.required": "Enter first name"
|
|
298
|
+
"any.required": "Enter first name",
|
|
299
|
+
"string.noControlChars": "First name must not contain invalid characters"
|
|
278
300
|
}),
|
|
279
|
-
last:
|
|
301
|
+
last: Joi.string().trim().required().max(LAST_NAME_MAX).messages({
|
|
280
302
|
"string.empty": "Enter last name",
|
|
281
303
|
"string.max": `Last name must be ${LAST_NAME_MAX} characters or less`,
|
|
282
|
-
"any.required": "Enter last name"
|
|
304
|
+
"any.required": "Enter last name",
|
|
305
|
+
"string.noControlChars": "Last name must not contain invalid characters"
|
|
283
306
|
}),
|
|
284
|
-
middle:
|
|
285
|
-
"string.max": `Middle names must be ${MIDDLE_NAMES_MAX} characters or less
|
|
307
|
+
middle: Joi.string().trim().allow("").max(MIDDLE_NAMES_MAX).messages({
|
|
308
|
+
"string.max": `Middle names must be ${MIDDLE_NAMES_MAX} characters or less`,
|
|
309
|
+
"string.noControlChars": "Middle names must not contain invalid characters"
|
|
286
310
|
})
|
|
287
311
|
});
|
|
288
312
|
|
|
289
|
-
// src/schemas/personal/personal-dob-schema.js
|
|
290
|
-
var import_joi9 = __toESM(require("joi"), 1);
|
|
291
|
-
|
|
292
313
|
// src/constants/month-map.js
|
|
293
314
|
var MONTH_MAP = {
|
|
294
315
|
january: 1,
|
|
@@ -318,10 +339,10 @@ var MONTH_MAP = {
|
|
|
318
339
|
};
|
|
319
340
|
|
|
320
341
|
// src/schemas/personal/personal-dob-schema.js
|
|
321
|
-
var personalDobSchema =
|
|
322
|
-
day:
|
|
323
|
-
month:
|
|
324
|
-
year:
|
|
342
|
+
var personalDobSchema = Joi.object({
|
|
343
|
+
day: Joi.string().allow(""),
|
|
344
|
+
month: Joi.string().allow(""),
|
|
345
|
+
year: Joi.string().allow("")
|
|
325
346
|
}).custom((value, helpers) => {
|
|
326
347
|
const { day, month, year } = value;
|
|
327
348
|
const missingFieldsError = checkMissingFields(day, month, year, helpers);
|
|
@@ -339,7 +360,9 @@ var personalDobSchema = import_joi9.default.object({
|
|
|
339
360
|
if (fullDate.isJoiError) {
|
|
340
361
|
return fullDate;
|
|
341
362
|
}
|
|
342
|
-
|
|
363
|
+
const today = /* @__PURE__ */ new Date();
|
|
364
|
+
today.setUTCHours(0, 0, 0, 0);
|
|
365
|
+
if (fullDate >= today) {
|
|
343
366
|
return makeError(helpers, "dob.future", ["day", "month", "year"]);
|
|
344
367
|
}
|
|
345
368
|
const tooOldError = checkNotTooOld(fullDate, helpers);
|
|
@@ -358,7 +381,8 @@ var personalDobSchema = import_joi9.default.object({
|
|
|
358
381
|
"dob.yearLength": "Enter a year with 4 numbers, like 1975",
|
|
359
382
|
"dob.invalid": "Date of birth must be a real date",
|
|
360
383
|
"dob.future": "Date of birth must be in the past",
|
|
361
|
-
"dob.tooOld": "Date of birth must be on or after {{#oldest}}"
|
|
384
|
+
"dob.tooOld": "Date of birth must be on or after {{#oldest}}",
|
|
385
|
+
"string.noControlChars": "Date of birth must not contain invalid characters"
|
|
362
386
|
});
|
|
363
387
|
var checkNotTooOld = (fullDate, helpers) => {
|
|
364
388
|
const oldestDateAllowed = getOldestAllowedDate();
|
|
@@ -398,7 +422,11 @@ var getMonthNumber = (month, helpers) => {
|
|
|
398
422
|
}
|
|
399
423
|
return mapped;
|
|
400
424
|
}
|
|
401
|
-
|
|
425
|
+
const parsedMonth = Number.parseInt(month, 10);
|
|
426
|
+
if (parsedMonth < 1 || parsedMonth > 12) {
|
|
427
|
+
return makeError(helpers, "dob.invalid", ["month"]);
|
|
428
|
+
}
|
|
429
|
+
return parsedMonth;
|
|
402
430
|
};
|
|
403
431
|
var checkMissingFields = (day, month, year, helpers) => {
|
|
404
432
|
if (!day && !month && !year) {
|
|
@@ -432,9 +460,8 @@ var makeError = (helpers, code, fields) => {
|
|
|
432
460
|
};
|
|
433
461
|
|
|
434
462
|
// src/schemas/personal/personal-email-schema.js
|
|
435
|
-
var
|
|
436
|
-
|
|
437
|
-
personalEmail: import_joi10.default.string().required().max(EMAIL_MAX).email({
|
|
463
|
+
var personalEmailSchema = Joi.object({
|
|
464
|
+
personalEmail: Joi.string().required().max(EMAIL_MAX).email({
|
|
438
465
|
minDomainSegments: 2,
|
|
439
466
|
tlds: {
|
|
440
467
|
allow: true,
|
|
@@ -443,22 +470,24 @@ var personalEmailSchema = import_joi10.default.object({
|
|
|
443
470
|
}).messages({
|
|
444
471
|
"string.max": `Email address must be ${EMAIL_MAX} characters or less`,
|
|
445
472
|
"string.empty": "Enter a personal email address",
|
|
446
|
-
"string.email": "Enter an email address, like name@example.com"
|
|
473
|
+
"string.email": "Enter an email address, like name@example.com",
|
|
474
|
+
"string.noControlChars": "Personal email address must not contain invalid characters"
|
|
447
475
|
})
|
|
448
476
|
});
|
|
449
477
|
|
|
450
478
|
// src/schemas/personal/personal-phone-schema.js
|
|
451
|
-
var
|
|
452
|
-
|
|
453
|
-
personalTelephone: import_joi11.default.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
479
|
+
var personalPhoneSchema = Joi.object({
|
|
480
|
+
personalTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
454
481
|
"string.min": `Personal telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
455
482
|
"string.max": `Personal telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
456
|
-
"string.pattern.base": "Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
483
|
+
"string.pattern.base": "Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
484
|
+
"string.noControlChars": "Personal telephone number must not contain invalid characters"
|
|
457
485
|
}),
|
|
458
|
-
personalMobile:
|
|
486
|
+
personalMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
459
487
|
"string.min": `Personal mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
460
488
|
"string.max": `Personal mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
461
|
-
"string.pattern.base": "Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
489
|
+
"string.pattern.base": "Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
490
|
+
"string.noControlChars": "Personal mobile phone number must not contain invalid characters"
|
|
462
491
|
})
|
|
463
492
|
}).or("personalTelephone", "personalMobile").messages({
|
|
464
493
|
"object.missing": "Enter at least one phone number"
|
package/dist/index.js
CHANGED
|
@@ -91,17 +91,36 @@ var mappers = {
|
|
|
91
91
|
businessDetails: mapBusinessDetails
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
+
// src/utils/joi.js
|
|
95
|
+
import BaseJoi from "joi";
|
|
96
|
+
|
|
97
|
+
// src/constants/patterns.js
|
|
98
|
+
var PHONE_NUMBER_PATTERN = /^\+?(?=[\d ()]*\d)[\d ()]*$/;
|
|
99
|
+
var NO_CONTROL_CHARS_PATTERN = /^[^\x00-\x1f\x7f-\x9f]*$/;
|
|
100
|
+
|
|
101
|
+
// src/utils/joi.js
|
|
102
|
+
var Joi = BaseJoi.extend((joi) => ({
|
|
103
|
+
type: "string",
|
|
104
|
+
base: joi.string(),
|
|
105
|
+
messages: {
|
|
106
|
+
"string.noControlChars": "Field must not contain invalid characters"
|
|
107
|
+
},
|
|
108
|
+
validate(value, helpers) {
|
|
109
|
+
if (typeof value === "string" && !NO_CONTROL_CHARS_PATTERN.test(value)) {
|
|
110
|
+
return { value, errors: helpers.error("string.noControlChars") };
|
|
111
|
+
}
|
|
112
|
+
return { value };
|
|
113
|
+
}
|
|
114
|
+
}));
|
|
115
|
+
|
|
94
116
|
// src/schemas/business/business-sbi-schema.js
|
|
95
|
-
import Joi from "joi";
|
|
96
117
|
var businessSbiSchema = Joi.object({
|
|
97
118
|
sbi: Joi.string().pattern(/^\d{9}$/).allow("").optional().messages({
|
|
98
|
-
"string.pattern.base": "Enter the full SBI"
|
|
119
|
+
"string.pattern.base": "Enter the full SBI",
|
|
120
|
+
"string.noControlChars": "SBI must not contain invalid characters"
|
|
99
121
|
})
|
|
100
122
|
});
|
|
101
123
|
|
|
102
|
-
// src/schemas/address-schema.js
|
|
103
|
-
import Joi2 from "joi";
|
|
104
|
-
|
|
105
124
|
// src/constants/validation-fields.js
|
|
106
125
|
var FIRST_NAME_MAX = 100;
|
|
107
126
|
var LAST_NAME_MAX = 100;
|
|
@@ -117,53 +136,59 @@ var COUNTY_MAX = 60;
|
|
|
117
136
|
var COUNTRY_MAX = 60;
|
|
118
137
|
var POSTCODE_MAX = 8;
|
|
119
138
|
|
|
120
|
-
// src/schemas/address-schema.js
|
|
121
|
-
var addressSchema =
|
|
122
|
-
address1:
|
|
139
|
+
// src/schemas/shared/address-schema.js
|
|
140
|
+
var addressSchema = Joi.object({
|
|
141
|
+
address1: Joi.string().trim().required().max(ADDRESS_LINE_MAX).messages({
|
|
123
142
|
"string.empty": "Enter address line 1, typically the building and street",
|
|
124
143
|
"string.max": `Address line 1 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
125
|
-
"any.required": "Enter address line 1, typically the building and street"
|
|
144
|
+
"any.required": "Enter address line 1, typically the building and street",
|
|
145
|
+
"string.noControlChars": "Address line 1 must not contain invalid characters"
|
|
126
146
|
}),
|
|
127
|
-
address2:
|
|
128
|
-
"string.max": `Address line 2 must be ${ADDRESS_LINE_MAX} characters or less
|
|
147
|
+
address2: Joi.string().trim().allow("").max(ADDRESS_LINE_MAX).messages({
|
|
148
|
+
"string.max": `Address line 2 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
149
|
+
"string.noControlChars": "Address line 2 must not contain invalid characters"
|
|
129
150
|
}),
|
|
130
|
-
address3:
|
|
131
|
-
"string.max": `Address line 3 must be ${ADDRESS_LINE_MAX} characters or less
|
|
151
|
+
address3: Joi.string().trim().allow("").max(ADDRESS_LINE_MAX).messages({
|
|
152
|
+
"string.max": `Address line 3 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
153
|
+
"string.noControlChars": "Address line 3 must not contain invalid characters"
|
|
132
154
|
}),
|
|
133
|
-
city:
|
|
155
|
+
city: Joi.string().trim().required().max(TOWN_CITY_MAX).messages({
|
|
134
156
|
"string.empty": "Enter town or city",
|
|
135
157
|
"string.max": `Town or city must be ${TOWN_CITY_MAX} characters or less`,
|
|
136
|
-
"any.required": "Enter town or city"
|
|
158
|
+
"any.required": "Enter town or city",
|
|
159
|
+
"string.noControlChars": "Town or city must not contain invalid characters"
|
|
137
160
|
}),
|
|
138
|
-
county:
|
|
139
|
-
"string.max": `County must be ${COUNTY_MAX} characters or less
|
|
161
|
+
county: Joi.string().trim().allow("").max(COUNTY_MAX).messages({
|
|
162
|
+
"string.max": `County must be ${COUNTY_MAX} characters or less`,
|
|
163
|
+
"string.noControlChars": "County must not contain invalid characters"
|
|
140
164
|
}),
|
|
141
|
-
postcode:
|
|
165
|
+
postcode: Joi.string().trim().required().max(POSTCODE_MAX).messages({
|
|
142
166
|
"any.required": "Enter a postal code or zip code",
|
|
143
167
|
"string.empty": "Enter a postal code or zip code",
|
|
144
|
-
"string.max": `Postal code or zip code must be ${POSTCODE_MAX} characters or less
|
|
168
|
+
"string.max": `Postal code or zip code must be ${POSTCODE_MAX} characters or less`,
|
|
169
|
+
"string.noControlChars": "Postal code or zip code must not contain invalid characters"
|
|
145
170
|
}),
|
|
146
|
-
country:
|
|
171
|
+
country: Joi.string().trim().required().max(COUNTRY_MAX).messages({
|
|
147
172
|
"string.empty": "Enter a country",
|
|
148
173
|
"string.max": `Country must be ${COUNTRY_MAX} characters or less`,
|
|
149
|
-
"any.required": "Enter a country"
|
|
174
|
+
"any.required": "Enter a country",
|
|
175
|
+
"string.noControlChars": "Country must not contain invalid characters"
|
|
150
176
|
})
|
|
151
177
|
});
|
|
152
178
|
|
|
153
179
|
// src/schemas/business/business-name-schema.js
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
businessName: Joi3.string().required().max(BUSINESS_NAME_MAX).messages({
|
|
180
|
+
var businessNameSchema = Joi.object({
|
|
181
|
+
businessName: Joi.string().trim().required().max(BUSINESS_NAME_MAX).messages({
|
|
157
182
|
"string.empty": "Enter business name",
|
|
158
183
|
"string.max": `Business name must be ${BUSINESS_NAME_MAX} characters or less`,
|
|
159
|
-
"any.required": "Enter business name"
|
|
184
|
+
"any.required": "Enter business name",
|
|
185
|
+
"string.noControlChars": "Business name must not contain invalid characters"
|
|
160
186
|
})
|
|
161
187
|
});
|
|
162
188
|
|
|
163
189
|
// src/schemas/business/business-email-schema.js
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
businessEmail: Joi4.string().required().max(EMAIL_MAX).email({
|
|
190
|
+
var businessEmailSchema = Joi.object({
|
|
191
|
+
businessEmail: Joi.string().required().max(EMAIL_MAX).email({
|
|
167
192
|
minDomainSegments: 2,
|
|
168
193
|
tlds: {
|
|
169
194
|
allow: true
|
|
@@ -171,37 +196,34 @@ var businessEmailSchema = Joi4.object({
|
|
|
171
196
|
}).messages({
|
|
172
197
|
"string.max": `Business email address must be ${EMAIL_MAX} characters or less`,
|
|
173
198
|
"string.empty": "Enter business email address",
|
|
174
|
-
"string.email": "Enter an email address, like name@example.com"
|
|
199
|
+
"string.email": "Enter an email address, like name@example.com",
|
|
200
|
+
"string.noControlChars": "Business email address must not contain invalid characters"
|
|
175
201
|
})
|
|
176
202
|
});
|
|
177
203
|
|
|
178
204
|
// src/schemas/business/business-phone-schema.js
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// src/constants/patterns.js
|
|
182
|
-
var PHONE_NUMBER_PATTERN = /^\+?[0-9 ()]+$/;
|
|
183
|
-
|
|
184
|
-
// src/schemas/business/business-phone-schema.js
|
|
185
|
-
var businessPhoneSchema = Joi5.object({
|
|
186
|
-
businessTelephone: Joi5.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
205
|
+
var businessPhoneSchema = Joi.object({
|
|
206
|
+
businessTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
187
207
|
"string.min": `Business telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
188
208
|
"string.max": `Business telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
189
|
-
"string.pattern.base": "Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
209
|
+
"string.pattern.base": "Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
210
|
+
"string.noControlChars": "Business telephone number must not contain invalid characters"
|
|
190
211
|
}),
|
|
191
|
-
businessMobile:
|
|
212
|
+
businessMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
192
213
|
"string.min": `Business mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
193
214
|
"string.max": `Business mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
194
|
-
"string.pattern.base": "Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
215
|
+
"string.pattern.base": "Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
216
|
+
"string.noControlChars": "Business mobile phone number must not contain invalid characters"
|
|
195
217
|
})
|
|
196
218
|
}).or("businessTelephone", "businessMobile").messages({
|
|
197
219
|
"object.missing": "Enter at least one phone number"
|
|
198
220
|
});
|
|
199
221
|
|
|
200
222
|
// src/schemas/business/business-vat-schema.js
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"string.
|
|
223
|
+
var businessVatSchema = Joi.object({
|
|
224
|
+
vatNumber: Joi.string().pattern(/^\d{9}$/).allow("").optional().messages({
|
|
225
|
+
"string.pattern.base": "Enter a VAT registration number, like 123456789",
|
|
226
|
+
"string.noControlChars": "VAT registration number must not contain invalid characters"
|
|
205
227
|
})
|
|
206
228
|
});
|
|
207
229
|
|
|
@@ -218,10 +240,10 @@ var businessSchemas = {
|
|
|
218
240
|
};
|
|
219
241
|
|
|
220
242
|
// src/schemas/customer/customer-crn-schema.js
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"string.
|
|
243
|
+
var customerCrnSchema = Joi.object({
|
|
244
|
+
crn: Joi.string().pattern(/^\d{10}$/).allow("").optional().messages({
|
|
245
|
+
"string.pattern.base": "Enter the full CRN",
|
|
246
|
+
"string.noControlChars": "CRN must not contain invalid characters"
|
|
225
247
|
})
|
|
226
248
|
});
|
|
227
249
|
|
|
@@ -231,26 +253,25 @@ var customerSchemas = {
|
|
|
231
253
|
};
|
|
232
254
|
|
|
233
255
|
// src/schemas/personal/personal-name-schema.js
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
first: Joi8.string().required().max(FIRST_NAME_MAX).messages({
|
|
256
|
+
var personalNameSchema = Joi.object({
|
|
257
|
+
first: Joi.string().trim().required().max(FIRST_NAME_MAX).messages({
|
|
237
258
|
"string.empty": "Enter first name",
|
|
238
259
|
"string.max": `First name must be ${FIRST_NAME_MAX} characters or less`,
|
|
239
|
-
"any.required": "Enter first name"
|
|
260
|
+
"any.required": "Enter first name",
|
|
261
|
+
"string.noControlChars": "First name must not contain invalid characters"
|
|
240
262
|
}),
|
|
241
|
-
last:
|
|
263
|
+
last: Joi.string().trim().required().max(LAST_NAME_MAX).messages({
|
|
242
264
|
"string.empty": "Enter last name",
|
|
243
265
|
"string.max": `Last name must be ${LAST_NAME_MAX} characters or less`,
|
|
244
|
-
"any.required": "Enter last name"
|
|
266
|
+
"any.required": "Enter last name",
|
|
267
|
+
"string.noControlChars": "Last name must not contain invalid characters"
|
|
245
268
|
}),
|
|
246
|
-
middle:
|
|
247
|
-
"string.max": `Middle names must be ${MIDDLE_NAMES_MAX} characters or less
|
|
269
|
+
middle: Joi.string().trim().allow("").max(MIDDLE_NAMES_MAX).messages({
|
|
270
|
+
"string.max": `Middle names must be ${MIDDLE_NAMES_MAX} characters or less`,
|
|
271
|
+
"string.noControlChars": "Middle names must not contain invalid characters"
|
|
248
272
|
})
|
|
249
273
|
});
|
|
250
274
|
|
|
251
|
-
// src/schemas/personal/personal-dob-schema.js
|
|
252
|
-
import Joi9 from "joi";
|
|
253
|
-
|
|
254
275
|
// src/constants/month-map.js
|
|
255
276
|
var MONTH_MAP = {
|
|
256
277
|
january: 1,
|
|
@@ -280,10 +301,10 @@ var MONTH_MAP = {
|
|
|
280
301
|
};
|
|
281
302
|
|
|
282
303
|
// src/schemas/personal/personal-dob-schema.js
|
|
283
|
-
var personalDobSchema =
|
|
284
|
-
day:
|
|
285
|
-
month:
|
|
286
|
-
year:
|
|
304
|
+
var personalDobSchema = Joi.object({
|
|
305
|
+
day: Joi.string().allow(""),
|
|
306
|
+
month: Joi.string().allow(""),
|
|
307
|
+
year: Joi.string().allow("")
|
|
287
308
|
}).custom((value, helpers) => {
|
|
288
309
|
const { day, month, year } = value;
|
|
289
310
|
const missingFieldsError = checkMissingFields(day, month, year, helpers);
|
|
@@ -301,7 +322,9 @@ var personalDobSchema = Joi9.object({
|
|
|
301
322
|
if (fullDate.isJoiError) {
|
|
302
323
|
return fullDate;
|
|
303
324
|
}
|
|
304
|
-
|
|
325
|
+
const today = /* @__PURE__ */ new Date();
|
|
326
|
+
today.setUTCHours(0, 0, 0, 0);
|
|
327
|
+
if (fullDate >= today) {
|
|
305
328
|
return makeError(helpers, "dob.future", ["day", "month", "year"]);
|
|
306
329
|
}
|
|
307
330
|
const tooOldError = checkNotTooOld(fullDate, helpers);
|
|
@@ -320,7 +343,8 @@ var personalDobSchema = Joi9.object({
|
|
|
320
343
|
"dob.yearLength": "Enter a year with 4 numbers, like 1975",
|
|
321
344
|
"dob.invalid": "Date of birth must be a real date",
|
|
322
345
|
"dob.future": "Date of birth must be in the past",
|
|
323
|
-
"dob.tooOld": "Date of birth must be on or after {{#oldest}}"
|
|
346
|
+
"dob.tooOld": "Date of birth must be on or after {{#oldest}}",
|
|
347
|
+
"string.noControlChars": "Date of birth must not contain invalid characters"
|
|
324
348
|
});
|
|
325
349
|
var checkNotTooOld = (fullDate, helpers) => {
|
|
326
350
|
const oldestDateAllowed = getOldestAllowedDate();
|
|
@@ -360,7 +384,11 @@ var getMonthNumber = (month, helpers) => {
|
|
|
360
384
|
}
|
|
361
385
|
return mapped;
|
|
362
386
|
}
|
|
363
|
-
|
|
387
|
+
const parsedMonth = Number.parseInt(month, 10);
|
|
388
|
+
if (parsedMonth < 1 || parsedMonth > 12) {
|
|
389
|
+
return makeError(helpers, "dob.invalid", ["month"]);
|
|
390
|
+
}
|
|
391
|
+
return parsedMonth;
|
|
364
392
|
};
|
|
365
393
|
var checkMissingFields = (day, month, year, helpers) => {
|
|
366
394
|
if (!day && !month && !year) {
|
|
@@ -394,9 +422,8 @@ var makeError = (helpers, code, fields) => {
|
|
|
394
422
|
};
|
|
395
423
|
|
|
396
424
|
// src/schemas/personal/personal-email-schema.js
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
personalEmail: Joi10.string().required().max(EMAIL_MAX).email({
|
|
425
|
+
var personalEmailSchema = Joi.object({
|
|
426
|
+
personalEmail: Joi.string().required().max(EMAIL_MAX).email({
|
|
400
427
|
minDomainSegments: 2,
|
|
401
428
|
tlds: {
|
|
402
429
|
allow: true,
|
|
@@ -405,22 +432,24 @@ var personalEmailSchema = Joi10.object({
|
|
|
405
432
|
}).messages({
|
|
406
433
|
"string.max": `Email address must be ${EMAIL_MAX} characters or less`,
|
|
407
434
|
"string.empty": "Enter a personal email address",
|
|
408
|
-
"string.email": "Enter an email address, like name@example.com"
|
|
435
|
+
"string.email": "Enter an email address, like name@example.com",
|
|
436
|
+
"string.noControlChars": "Personal email address must not contain invalid characters"
|
|
409
437
|
})
|
|
410
438
|
});
|
|
411
439
|
|
|
412
440
|
// src/schemas/personal/personal-phone-schema.js
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
personalTelephone: Joi11.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
441
|
+
var personalPhoneSchema = Joi.object({
|
|
442
|
+
personalTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
416
443
|
"string.min": `Personal telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
417
444
|
"string.max": `Personal telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
418
|
-
"string.pattern.base": "Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
445
|
+
"string.pattern.base": "Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
446
|
+
"string.noControlChars": "Personal telephone number must not contain invalid characters"
|
|
419
447
|
}),
|
|
420
|
-
personalMobile:
|
|
448
|
+
personalMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
421
449
|
"string.min": `Personal mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
422
450
|
"string.max": `Personal mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
423
|
-
"string.pattern.base": "Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
451
|
+
"string.pattern.base": "Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
|
|
452
|
+
"string.noControlChars": "Personal mobile phone number must not contain invalid characters"
|
|
424
453
|
})
|
|
425
454
|
}).or("personalTelephone", "personalMobile").messages({
|
|
426
455
|
"object.missing": "Enter at least one phone number"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/fcp-sfd-frontend-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Shared frontend engine used by both the internal and external frontend applications.",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"exports": {
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/DEFRA/fcp-sfd-frontend-engine#readme",
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@vitest/coverage-v8": "4.1.
|
|
41
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
42
42
|
"eslint": "9.39.2",
|
|
43
43
|
"neostandard": "0.13.0",
|
|
44
44
|
"tsup": "8.5.1",
|
|
45
|
-
"vitest": "4.1.
|
|
45
|
+
"vitest": "4.1.10"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"joi": "18.2.1"
|
package/src/constants/index.js
CHANGED
|
@@ -10,7 +10,8 @@ export {
|
|
|
10
10
|
TOWN_CITY_MAX,
|
|
11
11
|
COUNTY_MAX,
|
|
12
12
|
COUNTRY_MAX,
|
|
13
|
-
POSTCODE_MAX
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from './
|
|
13
|
+
POSTCODE_MAX
|
|
14
|
+
} from './validation-fields.js'
|
|
15
|
+
|
|
16
|
+
export { PHONE_NUMBER_PATTERN } from './patterns.js'
|
|
17
|
+
export { MONTH_MAP } from './month-map.js'
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// Allows digits, spaces, brackets, and an optional leading +, but requires at least one digit.
|
|
2
|
+
// The (?=...) part checks a digit exists first for efficiency and time complexity.
|
|
3
|
+
export const PHONE_NUMBER_PATTERN = /^\+?(?=[\d ()]*\d)[\d ()]*$/
|
|
4
|
+
|
|
5
|
+
// Rejects control characters — C0 (0x00–0x1f), DEL (0x7f) and C1 (0x80–0x9f) — which have no valid
|
|
6
|
+
// use in text input fields. The Unicode C1 block contains only control characters, so this does not
|
|
7
|
+
// reject legitimate input — accented letters, symbols and NBSP all sit at U+00A0 and above.
|
|
8
|
+
// eslint-disable-next-line no-control-regex
|
|
9
|
+
export const NO_CONTROL_CHARS_PATTERN = /^[^\x00-\x1f\x7f-\x9f]*$/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
import { EMAIL_MAX } from '../../constants/validation-fields.js'
|
|
3
3
|
|
|
4
4
|
export const businessEmailSchema = Joi.object({
|
|
@@ -14,6 +14,7 @@ export const businessEmailSchema = Joi.object({
|
|
|
14
14
|
.messages({
|
|
15
15
|
'string.max': `Business email address must be ${EMAIL_MAX} characters or less`,
|
|
16
16
|
'string.empty': 'Enter business email address',
|
|
17
|
-
'string.email': 'Enter an email address, like name@example.com'
|
|
17
|
+
'string.email': 'Enter an email address, like name@example.com',
|
|
18
|
+
'string.noControlChars': 'Business email address must not contain invalid characters'
|
|
18
19
|
})
|
|
19
20
|
})
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
import { BUSINESS_NAME_MAX } from '../../constants/validation-fields.js'
|
|
3
3
|
|
|
4
4
|
export const businessNameSchema = Joi.object({
|
|
5
5
|
businessName: Joi.string()
|
|
6
|
+
.trim()
|
|
6
7
|
.required()
|
|
7
8
|
.max(BUSINESS_NAME_MAX)
|
|
8
9
|
.messages({
|
|
9
10
|
'string.empty': 'Enter business name',
|
|
10
11
|
'string.max': `Business name must be ${BUSINESS_NAME_MAX} characters or less`,
|
|
11
|
-
'any.required': 'Enter business name'
|
|
12
|
+
'any.required': 'Enter business name',
|
|
13
|
+
'string.noControlChars': 'Business name must not contain invalid characters'
|
|
12
14
|
})
|
|
13
15
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
import {
|
|
3
3
|
PHONE_NUMBER_MIN,
|
|
4
4
|
PHONE_NUMBER_MAX
|
|
@@ -7,6 +7,7 @@ import { PHONE_NUMBER_PATTERN } from '../../constants/patterns.js'
|
|
|
7
7
|
|
|
8
8
|
export const businessPhoneSchema = Joi.object({
|
|
9
9
|
businessTelephone: Joi.string()
|
|
10
|
+
.trim()
|
|
10
11
|
.empty('')
|
|
11
12
|
.min(PHONE_NUMBER_MIN)
|
|
12
13
|
.max(PHONE_NUMBER_MAX)
|
|
@@ -14,9 +15,11 @@ export const businessPhoneSchema = Joi.object({
|
|
|
14
15
|
.messages({
|
|
15
16
|
'string.min': `Business telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
16
17
|
'string.max': `Business telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
17
|
-
'string.pattern.base': 'Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +'
|
|
18
|
+
'string.pattern.base': 'Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +',
|
|
19
|
+
'string.noControlChars': 'Business telephone number must not contain invalid characters'
|
|
18
20
|
}),
|
|
19
21
|
businessMobile: Joi.string()
|
|
22
|
+
.trim()
|
|
20
23
|
.empty('')
|
|
21
24
|
.min(PHONE_NUMBER_MIN)
|
|
22
25
|
.max(PHONE_NUMBER_MAX)
|
|
@@ -24,7 +27,8 @@ export const businessPhoneSchema = Joi.object({
|
|
|
24
27
|
.messages({
|
|
25
28
|
'string.min': `Business mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
26
29
|
'string.max': `Business mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
27
|
-
'string.pattern.base': 'Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +'
|
|
30
|
+
'string.pattern.base': 'Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +',
|
|
31
|
+
'string.noControlChars': 'Business mobile phone number must not contain invalid characters'
|
|
28
32
|
})
|
|
29
33
|
})
|
|
30
34
|
.or('businessTelephone', 'businessMobile')
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
|
|
3
3
|
export const businessSbiSchema = Joi.object({
|
|
4
4
|
sbi: Joi.string()
|
|
@@ -6,6 +6,7 @@ export const businessSbiSchema = Joi.object({
|
|
|
6
6
|
.allow('')
|
|
7
7
|
.optional()
|
|
8
8
|
.messages({
|
|
9
|
-
'string.pattern.base': 'Enter the full SBI'
|
|
9
|
+
'string.pattern.base': 'Enter the full SBI',
|
|
10
|
+
'string.noControlChars': 'SBI must not contain invalid characters'
|
|
10
11
|
})
|
|
11
12
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { businessSbiSchema } from './business-sbi-schema.js'
|
|
2
|
-
import { addressSchema } from '../address-schema.js'
|
|
2
|
+
import { addressSchema } from '../shared/address-schema.js'
|
|
3
3
|
import { businessNameSchema } from './business-name-schema.js'
|
|
4
4
|
import { businessEmailSchema } from './business-email-schema.js'
|
|
5
5
|
import { businessPhoneSchema } from './business-phone-schema.js'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
|
|
3
3
|
export const businessVatSchema = Joi.object({
|
|
4
4
|
vatNumber: Joi.string()
|
|
@@ -6,6 +6,7 @@ export const businessVatSchema = Joi.object({
|
|
|
6
6
|
.allow('')
|
|
7
7
|
.optional()
|
|
8
8
|
.messages({
|
|
9
|
-
'string.pattern.base': 'Enter a VAT registration number, like 123456789'
|
|
9
|
+
'string.pattern.base': 'Enter a VAT registration number, like 123456789',
|
|
10
|
+
'string.noControlChars': 'VAT registration number must not contain invalid characters'
|
|
10
11
|
})
|
|
11
12
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
|
|
3
3
|
export const customerCrnSchema = Joi.object({
|
|
4
4
|
crn: Joi.string()
|
|
@@ -6,6 +6,7 @@ export const customerCrnSchema = Joi.object({
|
|
|
6
6
|
.allow('')
|
|
7
7
|
.optional()
|
|
8
8
|
.messages({
|
|
9
|
-
'string.pattern.base': 'Enter the full CRN'
|
|
9
|
+
'string.pattern.base': 'Enter the full CRN',
|
|
10
|
+
'string.noControlChars': 'CRN must not contain invalid characters'
|
|
10
11
|
})
|
|
11
12
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
2
|
-
import {
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
|
+
import { MAX_AGE_YEARS } from '../../constants/validation-fields.js'
|
|
3
|
+
import { MONTH_MAP } from '../../constants/month-map.js'
|
|
3
4
|
|
|
4
5
|
export const personalDobSchema = Joi.object({
|
|
5
6
|
day: Joi.string().allow(''),
|
|
@@ -32,7 +33,9 @@ export const personalDobSchema = Joi.object({
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
// Date must be in the past
|
|
35
|
-
|
|
36
|
+
const today = new Date()
|
|
37
|
+
today.setUTCHours(0, 0, 0, 0)
|
|
38
|
+
if (fullDate >= today) {
|
|
36
39
|
return makeError(helpers, 'dob.future', ['day', 'month', 'year'])
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -54,7 +57,8 @@ export const personalDobSchema = Joi.object({
|
|
|
54
57
|
'dob.yearLength': 'Enter a year with 4 numbers, like 1975',
|
|
55
58
|
'dob.invalid': 'Date of birth must be a real date',
|
|
56
59
|
'dob.future': 'Date of birth must be in the past',
|
|
57
|
-
'dob.tooOld': 'Date of birth must be on or after {{#oldest}}'
|
|
60
|
+
'dob.tooOld': 'Date of birth must be on or after {{#oldest}}',
|
|
61
|
+
'string.noControlChars': 'Date of birth must not contain invalid characters'
|
|
58
62
|
})
|
|
59
63
|
|
|
60
64
|
const checkNotTooOld = (fullDate, helpers) => {
|
|
@@ -119,7 +123,7 @@ const getFullDate = (day, monthValue, year, helpers) => {
|
|
|
119
123
|
*
|
|
120
124
|
* If a valid mapping is found, the mapped number is returned.
|
|
121
125
|
* If no mapping exists, an error is returned via `makeError`, as the input is likely invalid.
|
|
122
|
-
* If the month is already numeric, it is
|
|
126
|
+
* If the month is already numeric, it is parsed and validated to be within the range 1–12.
|
|
123
127
|
*/
|
|
124
128
|
const getMonthNumber = (month, helpers) => {
|
|
125
129
|
if (Number.isNaN(Number(month))) {
|
|
@@ -133,7 +137,13 @@ const getMonthNumber = (month, helpers) => {
|
|
|
133
137
|
return mapped
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
|
|
140
|
+
const parsedMonth = Number.parseInt(month, 10)
|
|
141
|
+
|
|
142
|
+
if (parsedMonth < 1 || parsedMonth > 12) {
|
|
143
|
+
return makeError(helpers, 'dob.invalid', ['month'])
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return parsedMonth
|
|
137
147
|
}
|
|
138
148
|
|
|
139
149
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
import { EMAIL_MAX } from '../../constants/validation-fields.js'
|
|
3
3
|
|
|
4
4
|
export const personalEmailSchema = Joi.object({
|
|
@@ -15,6 +15,7 @@ export const personalEmailSchema = Joi.object({
|
|
|
15
15
|
.messages({
|
|
16
16
|
'string.max': `Email address must be ${EMAIL_MAX} characters or less`,
|
|
17
17
|
'string.empty': 'Enter a personal email address',
|
|
18
|
-
'string.email': 'Enter an email address, like name@example.com'
|
|
18
|
+
'string.email': 'Enter an email address, like name@example.com',
|
|
19
|
+
'string.noControlChars': 'Personal email address must not contain invalid characters'
|
|
19
20
|
})
|
|
20
21
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
import {
|
|
3
3
|
FIRST_NAME_MAX,
|
|
4
4
|
LAST_NAME_MAX,
|
|
@@ -7,25 +7,31 @@ import {
|
|
|
7
7
|
|
|
8
8
|
export const personalNameSchema = Joi.object({
|
|
9
9
|
first: Joi.string()
|
|
10
|
+
.trim()
|
|
10
11
|
.required()
|
|
11
12
|
.max(FIRST_NAME_MAX)
|
|
12
13
|
.messages({
|
|
13
14
|
'string.empty': 'Enter first name',
|
|
14
15
|
'string.max': `First name must be ${FIRST_NAME_MAX} characters or less`,
|
|
15
|
-
'any.required': 'Enter first name'
|
|
16
|
+
'any.required': 'Enter first name',
|
|
17
|
+
'string.noControlChars': 'First name must not contain invalid characters'
|
|
16
18
|
}),
|
|
17
19
|
last: Joi.string()
|
|
20
|
+
.trim()
|
|
18
21
|
.required()
|
|
19
22
|
.max(LAST_NAME_MAX)
|
|
20
23
|
.messages({
|
|
21
24
|
'string.empty': 'Enter last name',
|
|
22
25
|
'string.max': `Last name must be ${LAST_NAME_MAX} characters or less`,
|
|
23
|
-
'any.required': 'Enter last name'
|
|
26
|
+
'any.required': 'Enter last name',
|
|
27
|
+
'string.noControlChars': 'Last name must not contain invalid characters'
|
|
24
28
|
}),
|
|
25
29
|
middle: Joi.string()
|
|
30
|
+
.trim()
|
|
26
31
|
.allow('')
|
|
27
32
|
.max(MIDDLE_NAMES_MAX)
|
|
28
33
|
.messages({
|
|
29
|
-
'string.max': `Middle names must be ${MIDDLE_NAMES_MAX} characters or less
|
|
34
|
+
'string.max': `Middle names must be ${MIDDLE_NAMES_MAX} characters or less`,
|
|
35
|
+
'string.noControlChars': 'Middle names must not contain invalid characters'
|
|
30
36
|
})
|
|
31
37
|
})
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
PHONE_NUMBER_MAX,
|
|
5
|
-
PHONE_NUMBER_PATTERN
|
|
6
|
-
} from '../../constants/validation-fields-export.js'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
|
+
import { PHONE_NUMBER_MIN, PHONE_NUMBER_MAX } from '../../constants/validation-fields.js'
|
|
3
|
+
import { PHONE_NUMBER_PATTERN } from '../../constants/patterns.js'
|
|
7
4
|
|
|
8
5
|
export const personalPhoneSchema = Joi.object({
|
|
9
6
|
personalTelephone: Joi.string()
|
|
7
|
+
.trim()
|
|
10
8
|
.empty('')
|
|
11
9
|
.min(PHONE_NUMBER_MIN)
|
|
12
10
|
.max(PHONE_NUMBER_MAX)
|
|
@@ -14,9 +12,11 @@ export const personalPhoneSchema = Joi.object({
|
|
|
14
12
|
.messages({
|
|
15
13
|
'string.min': `Personal telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
16
14
|
'string.max': `Personal telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
17
|
-
'string.pattern.base': 'Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +'
|
|
15
|
+
'string.pattern.base': 'Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +',
|
|
16
|
+
'string.noControlChars': 'Personal telephone number must not contain invalid characters'
|
|
18
17
|
}),
|
|
19
18
|
personalMobile: Joi.string()
|
|
19
|
+
.trim()
|
|
20
20
|
.empty('')
|
|
21
21
|
.min(PHONE_NUMBER_MIN)
|
|
22
22
|
.max(PHONE_NUMBER_MAX)
|
|
@@ -24,7 +24,8 @@ export const personalPhoneSchema = Joi.object({
|
|
|
24
24
|
.messages({
|
|
25
25
|
'string.min': `Personal mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
26
26
|
'string.max': `Personal mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
27
|
-
'string.pattern.base': 'Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +'
|
|
27
|
+
'string.pattern.base': 'Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +',
|
|
28
|
+
'string.noControlChars': 'Personal mobile phone number must not contain invalid characters'
|
|
28
29
|
})
|
|
29
30
|
})
|
|
30
31
|
.or('personalTelephone', 'personalMobile')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { personalNameSchema } from './personal-name-schema.js'
|
|
2
2
|
import { personalDobSchema } from './personal-dob-schema.js'
|
|
3
|
-
import { addressSchema } from '../address-schema.js'
|
|
3
|
+
import { addressSchema } from '../shared/address-schema.js'
|
|
4
4
|
import { personalEmailSchema } from './personal-email-schema.js'
|
|
5
5
|
import { personalPhoneSchema } from './personal-phone-schema.js'
|
|
6
6
|
|
|
@@ -1,61 +1,75 @@
|
|
|
1
|
-
import Joi from 'joi'
|
|
1
|
+
import { Joi } from '../../utils/joi.js'
|
|
2
2
|
import {
|
|
3
3
|
ADDRESS_LINE_MAX,
|
|
4
4
|
TOWN_CITY_MAX,
|
|
5
5
|
COUNTY_MAX,
|
|
6
6
|
POSTCODE_MAX,
|
|
7
7
|
COUNTRY_MAX
|
|
8
|
-
} from '
|
|
8
|
+
} from '../../constants/validation-fields.js'
|
|
9
9
|
|
|
10
10
|
export const addressSchema = Joi.object({
|
|
11
11
|
address1: Joi.string()
|
|
12
|
+
.trim()
|
|
12
13
|
.required()
|
|
13
14
|
.max(ADDRESS_LINE_MAX)
|
|
14
15
|
.messages({
|
|
15
16
|
'string.empty': 'Enter address line 1, typically the building and street',
|
|
16
17
|
'string.max': `Address line 1 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
17
|
-
'any.required': 'Enter address line 1, typically the building and street'
|
|
18
|
+
'any.required': 'Enter address line 1, typically the building and street',
|
|
19
|
+
'string.noControlChars': 'Address line 1 must not contain invalid characters'
|
|
18
20
|
}),
|
|
19
21
|
address2: Joi.string()
|
|
22
|
+
.trim()
|
|
20
23
|
.allow('')
|
|
21
24
|
.max(ADDRESS_LINE_MAX)
|
|
22
25
|
.messages({
|
|
23
|
-
'string.max': `Address line 2 must be ${ADDRESS_LINE_MAX} characters or less
|
|
26
|
+
'string.max': `Address line 2 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
27
|
+
'string.noControlChars': 'Address line 2 must not contain invalid characters'
|
|
24
28
|
}),
|
|
25
29
|
address3: Joi.string()
|
|
30
|
+
.trim()
|
|
26
31
|
.allow('')
|
|
27
32
|
.max(ADDRESS_LINE_MAX)
|
|
28
33
|
.messages({
|
|
29
|
-
'string.max': `Address line 3 must be ${ADDRESS_LINE_MAX} characters or less
|
|
34
|
+
'string.max': `Address line 3 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
35
|
+
'string.noControlChars': 'Address line 3 must not contain invalid characters'
|
|
30
36
|
}),
|
|
31
37
|
city: Joi.string()
|
|
38
|
+
.trim()
|
|
32
39
|
.required()
|
|
33
40
|
.max(TOWN_CITY_MAX)
|
|
34
41
|
.messages({
|
|
35
42
|
'string.empty': 'Enter town or city',
|
|
36
43
|
'string.max': `Town or city must be ${TOWN_CITY_MAX} characters or less`,
|
|
37
|
-
'any.required': 'Enter town or city'
|
|
44
|
+
'any.required': 'Enter town or city',
|
|
45
|
+
'string.noControlChars': 'Town or city must not contain invalid characters'
|
|
38
46
|
}),
|
|
39
47
|
county: Joi.string()
|
|
48
|
+
.trim()
|
|
40
49
|
.allow('')
|
|
41
50
|
.max(COUNTY_MAX)
|
|
42
51
|
.messages({
|
|
43
|
-
'string.max': `County must be ${COUNTY_MAX} characters or less
|
|
52
|
+
'string.max': `County must be ${COUNTY_MAX} characters or less`,
|
|
53
|
+
'string.noControlChars': 'County must not contain invalid characters'
|
|
44
54
|
}),
|
|
45
55
|
postcode: Joi.string()
|
|
56
|
+
.trim()
|
|
46
57
|
.required()
|
|
47
58
|
.max(POSTCODE_MAX)
|
|
48
59
|
.messages({
|
|
49
60
|
'any.required': 'Enter a postal code or zip code',
|
|
50
61
|
'string.empty': 'Enter a postal code or zip code',
|
|
51
|
-
'string.max': `Postal code or zip code must be ${POSTCODE_MAX} characters or less
|
|
62
|
+
'string.max': `Postal code or zip code must be ${POSTCODE_MAX} characters or less`,
|
|
63
|
+
'string.noControlChars': 'Postal code or zip code must not contain invalid characters'
|
|
52
64
|
}),
|
|
53
65
|
country: Joi.string()
|
|
66
|
+
.trim()
|
|
54
67
|
.required()
|
|
55
68
|
.max(COUNTRY_MAX)
|
|
56
69
|
.messages({
|
|
57
70
|
'string.empty': 'Enter a country',
|
|
58
71
|
'string.max': `Country must be ${COUNTRY_MAX} characters or less`,
|
|
59
|
-
'any.required': 'Enter a country'
|
|
72
|
+
'any.required': 'Enter a country',
|
|
73
|
+
'string.noControlChars': 'Country must not contain invalid characters'
|
|
60
74
|
})
|
|
61
75
|
})
|
package/src/utils/joi.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import BaseJoi from 'joi'
|
|
2
|
+
import { NO_CONTROL_CHARS_PATTERN } from '../constants/patterns.js'
|
|
3
|
+
|
|
4
|
+
// Extends Joi's string type so every Joi.string() automatically rejects ASCII control
|
|
5
|
+
// characters (0x00–0x1f and 0x7f), which have no valid use in text input fields. Using a
|
|
6
|
+
// dedicated 'string.noControlChars' error keeps it distinct from the generic pattern rule
|
|
7
|
+
// used by numeric and phone fields. Fields may override the message via their own .messages().
|
|
8
|
+
export const Joi = BaseJoi.extend((joi) => ({
|
|
9
|
+
type: 'string',
|
|
10
|
+
base: joi.string(),
|
|
11
|
+
messages: {
|
|
12
|
+
'string.noControlChars': 'Field must not contain invalid characters'
|
|
13
|
+
},
|
|
14
|
+
validate (value, helpers) {
|
|
15
|
+
if (typeof value === 'string' && !NO_CONTROL_CHARS_PATTERN.test(value)) {
|
|
16
|
+
return { value, errors: helpers.error('string.noControlChars') }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { value }
|
|
20
|
+
}
|
|
21
|
+
}))
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
FIRST_NAME_MAX,
|
|
3
|
-
LAST_NAME_MAX,
|
|
4
|
-
MIDDLE_NAMES_MAX,
|
|
5
|
-
EMAIL_MAX,
|
|
6
|
-
PHONE_NUMBER_MIN,
|
|
7
|
-
PHONE_NUMBER_MAX,
|
|
8
|
-
MAX_AGE_YEARS,
|
|
9
|
-
ADDRESS_LINE_MAX,
|
|
10
|
-
TOWN_CITY_MAX,
|
|
11
|
-
COUNTY_MAX,
|
|
12
|
-
COUNTRY_MAX,
|
|
13
|
-
POSTCODE_MAX
|
|
14
|
-
} from './validation-fields.js'
|
|
15
|
-
|
|
16
|
-
export { PHONE_NUMBER_PATTERN } from './patterns.js'
|
|
17
|
-
export { MONTH_MAP } from './month-map.js'
|