@defra/fcp-sfd-frontend-engine 0.18.0 → 0.20.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.js CHANGED
@@ -184,338 +184,6 @@ var mapBusinessDetails = (value) => {
184
184
  };
185
185
  };
186
186
 
187
- // src/mappers/mappers.js
188
- var mappers = {
189
- personalBusinessDetails: mapPersonalBusinessDetails,
190
- address: mapAddress,
191
- customerName: mapCustomerName,
192
- businessDetails: mapBusinessDetails
193
- };
194
-
195
- // src/mutations/business/update-business-email.js
196
- var updateBusinessEmailMutation = `
197
- mutation Mutation($input: UpdateBusinessEmailInput!) {
198
- updateBusinessEmail(input: $input) {
199
- business {
200
- info {
201
- email {
202
- address
203
- }
204
- }
205
- }
206
- success
207
- }
208
- }
209
- `;
210
-
211
- // src/mutations/business/update-business-name.js
212
- var updateBusinessNameMutation = `
213
- mutation UpdateBusinessName($input: UpdateBusinessNameInput!) {
214
- updateBusinessName(input: $input) {
215
- business {
216
- info {
217
- name
218
- }
219
- }
220
- success
221
- }
222
- }
223
- `;
224
-
225
- // src/mutations/personal/update-customer-name.js
226
- var updateCustomerNameMutation = `
227
- mutation UpdateCustomerName($input: UpdateCustomerNameInput!) {
228
- updateCustomerName(input: $input) {
229
- customer {
230
- info {
231
- name {
232
- first
233
- last
234
- middle
235
- }
236
- }
237
- }
238
- }
239
- }
240
- `;
241
-
242
- // src/mutations/personal/update-customer-dob.js
243
- var updateCustomerDobMutation = `
244
- mutation UpdateCustomerDateOfBirth($input: UpdateCustomerDateOfBirthInput!) {
245
- updateCustomerDateOfBirth(input: $input) {
246
- customer {
247
- info {
248
- dateOfBirth
249
- }
250
- }
251
- }
252
- }
253
- `;
254
-
255
- // src/mutations/personal/update-customer-phone.js
256
- var updateCustomerPhoneMutation = `
257
- mutation UpdateCustomerPhone($input: UpdateCustomerPhoneInput!) {
258
- updateCustomerPhone(input: $input) {
259
- customer {
260
- info {
261
- phone {
262
- landline
263
- mobile
264
- }
265
- }
266
- }
267
- }
268
- }
269
- `;
270
-
271
- // src/mutations/personal/update-customer-email.js
272
- var updateCustomerEmailMutation = `
273
- mutation UpdateCustomerEmail($input: UpdateCustomerEmailInput!) {
274
- updateCustomerEmail(input: $input) {
275
- customer {
276
- info {
277
- email {
278
- address
279
- }
280
- }
281
- }
282
- }
283
- }
284
- `;
285
-
286
- // src/mutations/mutations.js
287
- var mutations = {
288
- updateBusinessEmail: updateBusinessEmailMutation,
289
- updateBusinessName: updateBusinessNameMutation,
290
- updateCustomerName: updateCustomerNameMutation,
291
- updateCustomerDob: updateCustomerDobMutation,
292
- updateCustomerPhone: updateCustomerPhoneMutation,
293
- updateCustomerEmail: updateCustomerEmailMutation
294
- };
295
-
296
- // src/presenters/base-presenter.js
297
- var BACK_LINK_DISPLAY_MAX = 50;
298
- var formatBackLink = (businessName) => {
299
- if (businessName.length > BACK_LINK_DISPLAY_MAX) {
300
- return `Back to ${businessName.slice(0, BACK_LINK_DISPLAY_MAX)}\u2026`;
301
- }
302
- return `Back to ${businessName}`;
303
- };
304
- var formatNumber = (payloadNumber, changedNumber, originalNumber) => {
305
- if (payloadNumber !== void 0) {
306
- return payloadNumber;
307
- }
308
- if (changedNumber !== void 0) {
309
- return changedNumber;
310
- }
311
- return originalNumber;
312
- };
313
- var formatDatePart = (changed, original) => {
314
- return changed ?? (original == null ? void 0 : original.toString()) ?? "";
315
- };
316
- var formatDateInputValues = (payloadDob, changedDob, originalDob) => {
317
- if (payloadDob) {
318
- return {
319
- day: payloadDob.day ?? "",
320
- month: payloadDob.month ?? "",
321
- year: payloadDob.year ?? ""
322
- };
323
- }
324
- return {
325
- day: formatDatePart(changedDob == null ? void 0 : changedDob.day, originalDob == null ? void 0 : originalDob.day),
326
- month: formatDatePart(changedDob == null ? void 0 : changedDob.month, originalDob == null ? void 0 : originalDob.month),
327
- year: formatDatePart(changedDob == null ? void 0 : changedDob.year, originalDob == null ? void 0 : originalDob.year)
328
- };
329
- };
330
- var sortErrorsBySectionOrder = (errors, orderedSectionsToFix, SECTION_FIELD_ORDER) => {
331
- const sortedErrors = [];
332
- for (const section of orderedSectionsToFix) {
333
- const fieldsInSection = SECTION_FIELD_ORDER[section] || [];
334
- for (const field of fieldsInSection) {
335
- if (errors[field]) {
336
- sortedErrors.push({
337
- field,
338
- ...errors[field]
339
- });
340
- }
341
- }
342
- }
343
- return sortedErrors;
344
- };
345
-
346
- // src/presenters/address-presenter.js
347
- var addressChangeLink = (postcodeLookup, context) => {
348
- if (context === "business") {
349
- if (postcodeLookup) {
350
- return "/business-address-change";
351
- }
352
- return "/business-address-enter";
353
- }
354
- if (context === "personal") {
355
- if (postcodeLookup) {
356
- return "/account-address-change";
357
- }
358
- return "/account-address-enter";
359
- }
360
- return null;
361
- };
362
- var addressBackLink = (postcodeLookup, context) => {
363
- if (context === "business") {
364
- if (postcodeLookup) {
365
- return { href: "/business-address-select" };
366
- }
367
- return { href: "/business-address-enter" };
368
- }
369
- if (context === "personal") {
370
- if (postcodeLookup) {
371
- return { href: "/account-address-select" };
372
- }
373
- return { href: "/account-address-enter" };
374
- }
375
- return null;
376
- };
377
- var buildAddressLine = (parts) => {
378
- return parts.filter(Boolean).join(", ") || null;
379
- };
380
- var buildStreetLine = (buildingRange, street) => {
381
- return [buildingRange, street].filter(Boolean).join(" ") || null;
382
- };
383
- var formatLookupAddress = (lookup, city, country, postcode) => ({
384
- address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
385
- address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
386
- address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
387
- county: lookup.county ?? null,
388
- city: city ?? null,
389
- country: country ?? null,
390
- postcode: postcode ?? null
391
- });
392
- var formatManualAddress = (manual, city, country, postcode) => ({
393
- address1: manual.line1 ?? null,
394
- address2: manual.line2 ?? null,
395
- address3: manual.line3 ?? null,
396
- city: city ?? null,
397
- county: manual.line4 ?? null,
398
- country: country ?? null,
399
- postcode: postcode ?? null
400
- });
401
- var formatDisplayAddress = (address) => {
402
- const { lookup, manual, postcode, country, city } = address;
403
- let addressLines = [];
404
- if (lookup.uprn) {
405
- const buildingAndStreet = [
406
- lookup.buildingNumberRange,
407
- lookup.street
408
- ].filter(Boolean).join(" ");
409
- addressLines = [
410
- lookup.pafOrganisationName,
411
- lookup.flatName,
412
- lookup.buildingName,
413
- buildingAndStreet,
414
- lookup.doubleDependentLocality,
415
- lookup.dependentLocality,
416
- city,
417
- lookup.county
418
- ];
419
- } else {
420
- addressLines = [
421
- manual.line1,
422
- manual.line2,
423
- manual.line3,
424
- city,
425
- manual.line4,
426
- // County
427
- manual.line5
428
- ];
429
- }
430
- return [
431
- ...addressLines.filter(Boolean),
432
- postcode,
433
- country
434
- ];
435
- };
436
- var formatOriginalAddress = (originalAddress) => {
437
- const { lookup, manual, city, country, postcode } = originalAddress;
438
- return lookup.uprn ? formatLookupAddress(lookup, city, country, postcode) : formatManualAddress(manual, city, country, postcode);
439
- };
440
- var formatChangedAddress = (changeBusinessAddress) => {
441
- if (!changeBusinessAddress.uprn) {
442
- return changeBusinessAddress;
443
- }
444
- const {
445
- pafOrganisationName,
446
- flatName,
447
- buildingName,
448
- buildingNumberRange,
449
- street,
450
- doubleDependentLocality,
451
- dependentLocality,
452
- city,
453
- county,
454
- country,
455
- postcode
456
- } = changeBusinessAddress;
457
- return {
458
- address1: buildAddressLine([pafOrganisationName, flatName, buildingName]),
459
- address2: buildStreetLine(buildingNumberRange, street),
460
- address3: buildAddressLine([doubleDependentLocality, dependentLocality]),
461
- city: city ?? null,
462
- county: county ?? null,
463
- country: country ?? null,
464
- postcode: postcode ?? null
465
- };
466
- };
467
- var formatDisplayAddresses = (addresses, previouslyPickedAddress) => {
468
- const displayAddresses = addresses.map((address) => ({
469
- value: `${address.uprn}${address.displayAddress}`,
470
- text: address.displayAddress,
471
- selected: (previouslyPickedAddress == null ? void 0 : previouslyPickedAddress.uprn) === address.uprn && (previouslyPickedAddress == null ? void 0 : previouslyPickedAddress.displayAddress) === address.displayAddress
472
- }));
473
- const hasSelectedAddress = displayAddresses.some((addr) => addr.selected);
474
- const text = addresses.length === 1 ? "1 address found" : `${addresses.length} addresses found`;
475
- displayAddresses.unshift({
476
- value: "display",
477
- text,
478
- selected: !hasSelectedAddress
479
- });
480
- return displayAddresses;
481
- };
482
-
483
- // src/presenters/business-details-presenter.js
484
- var getActionText = (value) => {
485
- return value ? "Change" : "Add";
486
- };
487
- var formatCph = (countyParishHoldings) => {
488
- return (countyParishHoldings || []).filter((cph) => cph == null ? void 0 : cph.cphNumber).map((cph) => cph.cphNumber);
489
- };
490
- var formatCphText = (count) => {
491
- return `County Parish Holding (CPH) number${count !== 1 ? "s" : ""}`;
492
- };
493
- var formatBusinessAddress = (businessAddress) => {
494
- var _a, _b;
495
- if (((_a = businessAddress == null ? void 0 : businessAddress.lookup) == null ? void 0 : _a.uprn) || ((_b = businessAddress == null ? void 0 : businessAddress.manual) == null ? void 0 : _b.line1)) {
496
- return formatDisplayAddress(businessAddress);
497
- }
498
- return [];
499
- };
500
-
501
- // src/presenters/presenters.js
502
- var presenters = {
503
- formatBackLink,
504
- formatNumber,
505
- formatDateInputValues,
506
- formatDisplayAddress,
507
- formatOriginalAddress,
508
- formatChangedAddress,
509
- formatDisplayAddresses,
510
- sortErrorsBySectionOrder,
511
- getActionText,
512
- formatCph,
513
- formatCphText,
514
- formatBusinessAddress,
515
- addressBackLink,
516
- addressChangeLink
517
- };
518
-
519
187
  // src/utils/joi.js
520
188
  import BaseJoi from "joi";
521
189
  var Joi = BaseJoi.extend((joi) => ({
@@ -590,332 +258,789 @@ var businessNameSchema = Joi.object({
590
258
  })
591
259
  });
592
260
 
593
- // src/schemas/business/business-email-schema.js
594
- var businessEmailSchema = Joi.object({
595
- businessEmail: Joi.string().required().max(EMAIL_MAX).email({
261
+ // src/schemas/business/business-email-schema.js
262
+ var businessEmailSchema = Joi.object({
263
+ businessEmail: Joi.string().required().max(EMAIL_MAX).email({
264
+ minDomainSegments: 2,
265
+ tlds: {
266
+ allow: true
267
+ }
268
+ }).messages({
269
+ "string.max": `Business email address must be ${EMAIL_MAX} characters or less`,
270
+ "string.empty": "Enter business email address",
271
+ "string.email": "Enter an email address, like name@example.com",
272
+ "string.noControlChars": "Business email address must not contain invalid characters"
273
+ })
274
+ });
275
+
276
+ // src/schemas/business/business-phone-schema.js
277
+ var businessPhoneSchema = Joi.object({
278
+ businessTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
279
+ "string.min": `Business telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
280
+ "string.max": `Business telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
281
+ "string.pattern.base": "Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
282
+ "string.noControlChars": "Business telephone number must not contain invalid characters"
283
+ }),
284
+ businessMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
285
+ "string.min": `Business mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
286
+ "string.max": `Business mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
287
+ "string.pattern.base": "Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
288
+ "string.noControlChars": "Business mobile phone number must not contain invalid characters"
289
+ })
290
+ }).or("businessTelephone", "businessMobile").messages({
291
+ "object.missing": "Enter at least one phone number"
292
+ });
293
+
294
+ // src/schemas/business/business-vat-schema.js
295
+ var businessVatSchema = Joi.object({
296
+ vatNumber: Joi.string().pattern(/^\d{9}$/).allow("").optional().messages({
297
+ "string.pattern.base": "Enter a VAT registration number, like 123456789",
298
+ "string.noControlChars": "VAT registration number must not contain invalid characters"
299
+ })
300
+ });
301
+
302
+ // src/schemas/business/business-vat-change-schema.js
303
+ var businessVatChangeSchema = Joi.object({
304
+ vatNumber: Joi.string().pattern(/^\d{9}$/).required().messages({
305
+ "string.pattern.base": "Enter a VAT registration number, like 123456789",
306
+ "string.empty": "Enter a VAT registration number",
307
+ "any.required": "Enter a VAT registration number",
308
+ "string.noControlChars": "VAT registration number must not contain invalid characters"
309
+ })
310
+ });
311
+
312
+ // src/schemas/business/business-vat-remove-schema.js
313
+ var businessVatRemoveSchema = Joi.object({
314
+ confirmRemove: Joi.string().valid("yes", "no").required().messages({
315
+ "any.required": "Select yes if you want to remove your VAT registration number",
316
+ "any.only": "Select yes if you want to remove your VAT registration number"
317
+ })
318
+ });
319
+
320
+ // src/schemas/business/business-schemas.js
321
+ var businessSchemas = {
322
+ sbi: businessSbiSchema,
323
+ details: {
324
+ name: businessNameSchema,
325
+ address: addressSchema,
326
+ phone: businessPhoneSchema,
327
+ email: businessEmailSchema,
328
+ vat: businessVatSchema
329
+ },
330
+ vat: {
331
+ change: businessVatChangeSchema,
332
+ remove: businessVatRemoveSchema
333
+ }
334
+ };
335
+
336
+ // src/schemas/customer/customer-crn-schema.js
337
+ var customerCrnSchema = Joi.object({
338
+ crn: Joi.string().pattern(/^\d{10}$/).allow("").optional().messages({
339
+ "string.pattern.base": "Enter the full CRN",
340
+ "string.noControlChars": "CRN must not contain invalid characters"
341
+ })
342
+ });
343
+
344
+ // src/schemas/customer/customer-schemas.js
345
+ var customerSchemas = {
346
+ crn: customerCrnSchema
347
+ };
348
+
349
+ // src/schemas/personal/personal-name-schema.js
350
+ var personalNameSchema = Joi.object({
351
+ first: Joi.string().trim().required().max(FIRST_NAME_MAX).messages({
352
+ "string.empty": "Enter first name",
353
+ "string.max": `First name must be ${FIRST_NAME_MAX} characters or less`,
354
+ "any.required": "Enter first name",
355
+ "string.noControlChars": "First name must not contain invalid characters"
356
+ }),
357
+ last: Joi.string().trim().required().max(LAST_NAME_MAX).messages({
358
+ "string.empty": "Enter last name",
359
+ "string.max": `Last name must be ${LAST_NAME_MAX} characters or less`,
360
+ "any.required": "Enter last name",
361
+ "string.noControlChars": "Last name must not contain invalid characters"
362
+ }),
363
+ middle: Joi.string().trim().allow("").max(MIDDLE_NAMES_MAX).messages({
364
+ "string.max": `Middle names must be ${MIDDLE_NAMES_MAX} characters or less`,
365
+ "string.noControlChars": "Middle names must not contain invalid characters"
366
+ })
367
+ });
368
+
369
+ // src/schemas/personal/personal-dob-schema.js
370
+ var personalDobSchema = Joi.object({
371
+ day: Joi.string().allow(""),
372
+ month: Joi.string().allow(""),
373
+ year: Joi.string().allow("")
374
+ }).custom((value, helpers) => {
375
+ const { day, month, year } = value;
376
+ const missingFieldsError = checkMissingFields(day, month, year, helpers);
377
+ if (missingFieldsError) {
378
+ return missingFieldsError;
379
+ }
380
+ if (year && year.length !== 4) {
381
+ return makeError(helpers, "dob.yearLength", ["year"]);
382
+ }
383
+ const monthValue = getMonthNumber(month, helpers);
384
+ if (monthValue.isJoiError) {
385
+ return monthValue;
386
+ }
387
+ const fullDate = getFullDate(day, monthValue, year, helpers);
388
+ if (fullDate.isJoiError) {
389
+ return fullDate;
390
+ }
391
+ const today = /* @__PURE__ */ new Date();
392
+ today.setUTCHours(0, 0, 0, 0);
393
+ if (fullDate >= today) {
394
+ return makeError(helpers, "dob.future", ["day", "month", "year"]);
395
+ }
396
+ const tooOldError = checkNotTooOld(fullDate, helpers);
397
+ if (tooOldError) {
398
+ return tooOldError;
399
+ }
400
+ return value;
401
+ }).messages({
402
+ "dob.missingAll": "Enter your date of birth",
403
+ "dob.missingDay": "Date of birth must include a day",
404
+ "dob.missingMonth": "Date of birth must include a month",
405
+ "dob.missingYear": "Date of birth must include a year",
406
+ "dob.missingDayMonth": "Date of birth must include a day and month",
407
+ "dob.missingDayYear": "Date of birth must include a day and year",
408
+ "dob.missingMonthYear": "Date of birth must include a month and year",
409
+ "dob.yearLength": "Enter a year with 4 numbers, like 1975",
410
+ "dob.invalid": "Date of birth must be a real date",
411
+ "dob.future": "Date of birth must be in the past",
412
+ "dob.tooOld": "Date of birth must be on or after {{#oldest}}",
413
+ "string.noControlChars": "Date of birth must not contain invalid characters"
414
+ });
415
+ var checkNotTooOld = (fullDate, helpers) => {
416
+ const oldestDateAllowed = getOldestAllowedDate();
417
+ if (fullDate < oldestDateAllowed) {
418
+ const oldestDateString = oldestDateAllowed.toLocaleDateString("en-GB", {
419
+ day: "numeric",
420
+ month: "long",
421
+ year: "numeric"
422
+ });
423
+ const error = helpers.error("dob.tooOld", { oldest: oldestDateString });
424
+ error.path = ["day", "month", "year"];
425
+ return error;
426
+ }
427
+ return null;
428
+ };
429
+ var getOldestAllowedDate = () => {
430
+ const date = /* @__PURE__ */ new Date();
431
+ date.setUTCHours(0, 0, 0, 0);
432
+ date.setUTCFullYear(date.getUTCFullYear() - MAX_AGE_YEARS);
433
+ return date;
434
+ };
435
+ var getFullDate = (day, monthValue, year, helpers) => {
436
+ const dayValue = Number.parseInt(day, 10);
437
+ const yearValue = Number.parseInt(year, 10);
438
+ const date = new Date(Date.UTC(yearValue, monthValue - 1, dayValue));
439
+ if (date.getUTCFullYear() !== yearValue || date.getUTCMonth() + 1 !== monthValue || date.getUTCDate() !== dayValue) {
440
+ return makeError(helpers, "dob.invalid", ["day", "month", "year"]);
441
+ }
442
+ return date;
443
+ };
444
+ var getMonthNumber = (month, helpers) => {
445
+ if (Number.isNaN(Number(month))) {
446
+ const lower = month.toLowerCase();
447
+ const mapped = MONTH_MAP[lower];
448
+ if (!mapped) {
449
+ return makeError(helpers, "dob.invalid", ["month"]);
450
+ }
451
+ return mapped;
452
+ }
453
+ const parsedMonth = Number.parseInt(month, 10);
454
+ if (parsedMonth < 1 || parsedMonth > 12) {
455
+ return makeError(helpers, "dob.invalid", ["month"]);
456
+ }
457
+ return parsedMonth;
458
+ };
459
+ var checkMissingFields = (day, month, year, helpers) => {
460
+ if (!day && !month && !year) {
461
+ return makeError(helpers, "dob.missingAll", ["day", "month", "year"]);
462
+ }
463
+ if (!day && month && year) {
464
+ return makeError(helpers, "dob.missingDay", ["day"]);
465
+ }
466
+ if (day && !month && year) {
467
+ return makeError(helpers, "dob.missingMonth", ["month"]);
468
+ }
469
+ if (day && month && !year) {
470
+ return makeError(helpers, "dob.missingYear", ["year"]);
471
+ }
472
+ if (!day && !month && year) {
473
+ return makeError(helpers, "dob.missingDayMonth", ["day", "month"]);
474
+ }
475
+ if (!day && month && !year) {
476
+ return makeError(helpers, "dob.missingDayYear", ["day", "year"]);
477
+ }
478
+ if (day && !month && !year) {
479
+ return makeError(helpers, "dob.missingMonthYear", ["month", "year"]);
480
+ }
481
+ return null;
482
+ };
483
+ var makeError = (helpers, code, fields) => {
484
+ const error = helpers.error(code);
485
+ error.path = fields;
486
+ error.isJoiError = true;
487
+ return error;
488
+ };
489
+
490
+ // src/schemas/personal/personal-email-schema.js
491
+ var personalEmailSchema = Joi.object({
492
+ personalEmail: Joi.string().required().max(EMAIL_MAX).email({
596
493
  minDomainSegments: 2,
597
494
  tlds: {
598
- allow: true
495
+ allow: true,
496
+ min: 2
599
497
  }
600
498
  }).messages({
601
- "string.max": `Business email address must be ${EMAIL_MAX} characters or less`,
602
- "string.empty": "Enter business email address",
499
+ "string.max": `Email address must be ${EMAIL_MAX} characters or less`,
500
+ "string.empty": "Enter a personal email address",
603
501
  "string.email": "Enter an email address, like name@example.com",
604
- "string.noControlChars": "Business email address must not contain invalid characters"
502
+ "string.noControlChars": "Personal email address must not contain invalid characters"
605
503
  })
606
504
  });
607
505
 
608
- // src/schemas/business/business-phone-schema.js
609
- var businessPhoneSchema = Joi.object({
610
- businessTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
611
- "string.min": `Business telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
612
- "string.max": `Business telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
613
- "string.pattern.base": "Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
614
- "string.noControlChars": "Business telephone number must not contain invalid characters"
506
+ // src/schemas/personal/personal-phone-schema.js
507
+ var personalPhoneSchema = Joi.object({
508
+ personalTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
509
+ "string.min": `Personal telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
510
+ "string.max": `Personal telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
511
+ "string.pattern.base": "Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
512
+ "string.noControlChars": "Personal telephone number must not contain invalid characters"
615
513
  }),
616
- businessMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
617
- "string.min": `Business mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
618
- "string.max": `Business mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
619
- "string.pattern.base": "Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
620
- "string.noControlChars": "Business mobile phone number must not contain invalid characters"
514
+ personalMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
515
+ "string.min": `Personal mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
516
+ "string.max": `Personal mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
517
+ "string.pattern.base": "Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
518
+ "string.noControlChars": "Personal mobile phone number must not contain invalid characters"
621
519
  })
622
- }).or("businessTelephone", "businessMobile").messages({
520
+ }).or("personalTelephone", "personalMobile").messages({
623
521
  "object.missing": "Enter at least one phone number"
624
522
  });
625
523
 
626
- // src/schemas/business/business-vat-schema.js
627
- var businessVatSchema = Joi.object({
628
- vatNumber: Joi.string().pattern(/^\d{9}$/).allow("").optional().messages({
629
- "string.pattern.base": "Enter a VAT registration number, like 123456789",
630
- "string.noControlChars": "VAT registration number must not contain invalid characters"
631
- })
632
- });
524
+ // src/schemas/personal/personal-schemas.js
525
+ var personalSchemas = {
526
+ name: personalNameSchema,
527
+ dob: personalDobSchema,
528
+ address: addressSchema,
529
+ phone: personalPhoneSchema,
530
+ email: personalEmailSchema
531
+ };
633
532
 
634
- // src/schemas/business/business-vat-change-schema.js
635
- var businessVatChangeSchema = Joi.object({
636
- vatNumber: Joi.string().pattern(/^\d{9}$/).required().messages({
637
- "string.pattern.base": "Enter a VAT registration number, like 123456789",
638
- "string.empty": "Enter a VAT registration number",
639
- "any.required": "Enter a VAT registration number",
640
- "string.noControlChars": "VAT registration number must not contain invalid characters"
533
+ // src/schemas/os-places/address-lookup-schema.js
534
+ var addressLookupSchema = Joi.object({
535
+ properties: Joi.object({
536
+ UPRN: Joi.string().required(),
537
+ ADDRESS: Joi.string().required(),
538
+ ORGANISATION_NAME: Joi.string().allow(null),
539
+ DEPARTMENT_NAME: Joi.string().allow(null),
540
+ SUB_BUILDING_NAME: Joi.string().allow(null),
541
+ BUILDING_NAME: Joi.string().allow(null),
542
+ BUILDING_NUMBER: Joi.string().allow(null),
543
+ DEPENDENT_THOROUGHFARE_NAME: Joi.string().allow(null),
544
+ THOROUGHFARE_NAME: Joi.string().allow(null),
545
+ DOUBLE_DEPENDENT_LOCALITY: Joi.string().allow(null),
546
+ DEPENDENT_LOCALITY: Joi.string().allow(null),
547
+ POST_TOWN: Joi.string().required(),
548
+ POSTCODE: Joi.string().required(),
549
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: Joi.string().allow(null),
550
+ COUNTRY_CODE: Joi.string().required()
551
+ }).unknown(true).required()
552
+ }).unknown(true);
553
+
554
+ // src/schemas/os-places/addresses-schema.js
555
+ var CHOOSE_ADDRESS_ERROR = "Choose an address";
556
+ var addressesSchema = Joi.object({
557
+ addresses: Joi.string().invalid("display").required().messages({
558
+ "any.required": CHOOSE_ADDRESS_ERROR,
559
+ "string.empty": CHOOSE_ADDRESS_ERROR,
560
+ "any.invalid": CHOOSE_ADDRESS_ERROR
641
561
  })
642
562
  });
643
563
 
644
- // src/schemas/business/business-vat-remove-schema.js
645
- var businessVatRemoveSchema = Joi.object({
646
- confirmRemove: Joi.string().valid("yes", "no").required().messages({
647
- "any.required": "Select yes if you want to remove your VAT registration number",
648
- "any.only": "Select yes if you want to remove your VAT registration number"
564
+ // src/schemas/os-places/uk-postcode-schema.js
565
+ var ukPostcodeSchema = Joi.object({
566
+ postcode: Joi.string().trim().required().uppercase().max(POSTCODE_MAX).pattern(/^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$/).messages({
567
+ "any.required": "Enter a postcode",
568
+ "string.empty": "Enter a postcode",
569
+ "string.max": `Postal code must be ${POSTCODE_MAX} characters or less`,
570
+ "string.pattern.base": "Enter a full UK postcode, like AA3 1AB"
649
571
  })
650
572
  });
651
573
 
652
- // src/schemas/business/business-schemas.js
653
- var businessSchemas = {
654
- sbi: businessSbiSchema,
655
- details: {
656
- name: businessNameSchema,
657
- address: addressSchema,
658
- phone: businessPhoneSchema,
659
- email: businessEmailSchema,
660
- vat: businessVatSchema
661
- },
662
- vat: {
663
- change: businessVatChangeSchema,
664
- remove: businessVatRemoveSchema
665
- }
574
+ // src/schemas/os-places/os-places-schemas.js
575
+ var osPlacesSchemas = {
576
+ addressLookup: addressLookupSchema,
577
+ addresses: addressesSchema,
578
+ ukPostcode: ukPostcodeSchema
666
579
  };
667
580
 
668
- // src/schemas/customer/customer-crn-schema.js
669
- var customerCrnSchema = Joi.object({
670
- crn: Joi.string().pattern(/^\d{10}$/).allow("").optional().messages({
671
- "string.pattern.base": "Enter the full CRN",
672
- "string.noControlChars": "CRN must not contain invalid characters"
673
- })
674
- });
581
+ // src/schemas/schemas.js
582
+ var schemas = {
583
+ business: businessSchemas,
584
+ customer: customerSchemas,
585
+ personal: personalSchemas,
586
+ osPlaces: osPlacesSchemas
587
+ };
675
588
 
676
- // src/schemas/customer/customer-schemas.js
677
- var customerSchemas = {
678
- crn: customerCrnSchema
589
+ // src/constants/country-names.js
590
+ var COUNTRY_NAMES = {
591
+ E: "ENGLAND",
592
+ W: "WALES",
593
+ S: "SCOTLAND",
594
+ N: "NORTHERN IRELAND",
595
+ L: "CHANNEL ISLANDS",
596
+ M: "ISLE OF MAN"
679
597
  };
680
598
 
681
- // src/schemas/personal/personal-name-schema.js
682
- var personalNameSchema = Joi.object({
683
- first: Joi.string().trim().required().max(FIRST_NAME_MAX).messages({
684
- "string.empty": "Enter first name",
685
- "string.max": `First name must be ${FIRST_NAME_MAX} characters or less`,
686
- "any.required": "Enter first name",
687
- "string.noControlChars": "First name must not contain invalid characters"
688
- }),
689
- last: Joi.string().trim().required().max(LAST_NAME_MAX).messages({
690
- "string.empty": "Enter last name",
691
- "string.max": `Last name must be ${LAST_NAME_MAX} characters or less`,
692
- "any.required": "Enter last name",
693
- "string.noControlChars": "Last name must not contain invalid characters"
694
- }),
695
- middle: Joi.string().trim().allow("").max(MIDDLE_NAMES_MAX).messages({
696
- "string.max": `Middle names must be ${MIDDLE_NAMES_MAX} characters or less`,
697
- "string.noControlChars": "Middle names must not contain invalid characters"
698
- })
699
- });
599
+ // src/mappers/address-lookup-mapper.js
600
+ var addressLookupMapper = (addresses) => {
601
+ if (!Array.isArray(addresses)) {
602
+ return [];
603
+ }
604
+ return addresses.map((address) => {
605
+ const { error } = schemas.osPlaces.addressLookup.validate(address);
606
+ if (error) {
607
+ return null;
608
+ }
609
+ const {
610
+ UPRN,
611
+ ADDRESS,
612
+ PO_BOX_NUMBER,
613
+ ORGANISATION_NAME,
614
+ DEPARTMENT_NAME,
615
+ SUB_BUILDING_NAME,
616
+ BUILDING_NAME,
617
+ BUILDING_NUMBER,
618
+ DEPENDENT_THOROUGHFARE_NAME,
619
+ THOROUGHFARE_NAME,
620
+ DOUBLE_DEPENDENT_LOCALITY,
621
+ DEPENDENT_LOCALITY,
622
+ POST_TOWN,
623
+ POSTCODE,
624
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION,
625
+ COUNTRY_CODE
626
+ } = address.properties;
627
+ const buildingName = PO_BOX_NUMBER ? `PO BOX ${PO_BOX_NUMBER}` : BUILDING_NAME || null;
628
+ return {
629
+ displayAddress: ADDRESS,
630
+ pafOrganisationName: filterAndJoin([ORGANISATION_NAME, DEPARTMENT_NAME]),
631
+ flatName: SUB_BUILDING_NAME ?? null,
632
+ buildingName,
633
+ buildingNumberRange: BUILDING_NUMBER ?? null,
634
+ street: filterAndJoin([DEPENDENT_THOROUGHFARE_NAME, THOROUGHFARE_NAME]),
635
+ dependentLocality: DEPENDENT_LOCALITY ?? null,
636
+ doubleDependentLocality: DOUBLE_DEPENDENT_LOCALITY ?? null,
637
+ city: POST_TOWN,
638
+ county: formatCounty(LOCAL_CUSTODIAN_CODE_DESCRIPTION, POST_TOWN),
639
+ postcode: POSTCODE,
640
+ country: COUNTRY_NAMES[COUNTRY_CODE] ?? null,
641
+ uprn: UPRN
642
+ };
643
+ }).filter(Boolean);
644
+ };
645
+ var formatCounty = (localCustodianCodeDescription, postTown) => {
646
+ if (localCustodianCodeDescription === "ORDNANCE SURVEY" || localCustodianCodeDescription === postTown) {
647
+ return null;
648
+ }
649
+ return localCustodianCodeDescription;
650
+ };
651
+ var filterAndJoin = (addressesProperties) => {
652
+ return addressesProperties.filter(Boolean).join(", ") || null;
653
+ };
654
+
655
+ // src/mappers/mappers.js
656
+ var mappers = {
657
+ personalBusinessDetails: mapPersonalBusinessDetails,
658
+ address: mapAddress,
659
+ customerName: mapCustomerName,
660
+ businessDetails: mapBusinessDetails,
661
+ addressLookup: addressLookupMapper
662
+ };
663
+
664
+ // src/mutations/business/update-business-email.js
665
+ var updateBusinessEmailMutation = `
666
+ mutation Mutation($input: UpdateBusinessEmailInput!) {
667
+ updateBusinessEmail(input: $input) {
668
+ business {
669
+ info {
670
+ email {
671
+ address
672
+ }
673
+ }
674
+ }
675
+ success
676
+ }
677
+ }
678
+ `;
679
+
680
+ // src/mutations/business/update-business-name.js
681
+ var updateBusinessNameMutation = `
682
+ mutation UpdateBusinessName($input: UpdateBusinessNameInput!) {
683
+ updateBusinessName(input: $input) {
684
+ business {
685
+ info {
686
+ name
687
+ }
688
+ }
689
+ success
690
+ }
691
+ }
692
+ `;
693
+
694
+ // src/mutations/personal/update-customer-name.js
695
+ var updateCustomerNameMutation = `
696
+ mutation UpdateCustomerName($input: UpdateCustomerNameInput!) {
697
+ updateCustomerName(input: $input) {
698
+ customer {
699
+ info {
700
+ name {
701
+ first
702
+ last
703
+ middle
704
+ }
705
+ }
706
+ }
707
+ }
708
+ }
709
+ `;
710
+
711
+ // src/mutations/personal/update-customer-dob.js
712
+ var updateCustomerDobMutation = `
713
+ mutation UpdateCustomerDateOfBirth($input: UpdateCustomerDateOfBirthInput!) {
714
+ updateCustomerDateOfBirth(input: $input) {
715
+ customer {
716
+ info {
717
+ dateOfBirth
718
+ }
719
+ }
720
+ }
721
+ }
722
+ `;
700
723
 
701
- // src/schemas/personal/personal-dob-schema.js
702
- var personalDobSchema = Joi.object({
703
- day: Joi.string().allow(""),
704
- month: Joi.string().allow(""),
705
- year: Joi.string().allow("")
706
- }).custom((value, helpers) => {
707
- const { day, month, year } = value;
708
- const missingFieldsError = checkMissingFields(day, month, year, helpers);
709
- if (missingFieldsError) {
710
- return missingFieldsError;
724
+ // src/mutations/personal/update-customer-phone.js
725
+ var updateCustomerPhoneMutation = `
726
+ mutation UpdateCustomerPhone($input: UpdateCustomerPhoneInput!) {
727
+ updateCustomerPhone(input: $input) {
728
+ customer {
729
+ info {
730
+ phone {
731
+ landline
732
+ mobile
733
+ }
734
+ }
735
+ }
736
+ }
711
737
  }
712
- if (year && year.length !== 4) {
713
- return makeError(helpers, "dob.yearLength", ["year"]);
738
+ `;
739
+
740
+ // src/mutations/personal/update-customer-email.js
741
+ var updateCustomerEmailMutation = `
742
+ mutation UpdateCustomerEmail($input: UpdateCustomerEmailInput!) {
743
+ updateCustomerEmail(input: $input) {
744
+ customer {
745
+ info {
746
+ email {
747
+ address
748
+ }
749
+ }
750
+ }
751
+ }
714
752
  }
715
- const monthValue = getMonthNumber(month, helpers);
716
- if (monthValue.isJoiError) {
717
- return monthValue;
753
+ `;
754
+
755
+ // src/mutations/personal/update-customer-address.js
756
+ var updateCustomerAddressMutation = `
757
+ mutation UpdateCustomerAddress($input: UpdateCustomerAddressInput!) {
758
+ updateCustomerAddress(input: $input) {
759
+ customer {
760
+ info {
761
+ address {
762
+ line1
763
+ line2
764
+ line3
765
+ line4
766
+ line5
767
+ postalCode
768
+ country
769
+ city
770
+ buildingNumberRange
771
+ buildingName
772
+ flatName
773
+ street
774
+ county
775
+ uprn
776
+ }
777
+ }
778
+ }
779
+ }
718
780
  }
719
- const fullDate = getFullDate(day, monthValue, year, helpers);
720
- if (fullDate.isJoiError) {
721
- return fullDate;
781
+ `;
782
+
783
+ // src/mutations/business/update-business-address.js
784
+ var updateBusinessAddressMutation = `
785
+ mutation UpdateBusinessAddress($input: UpdateBusinessAddressInput!) {
786
+ updateBusinessAddress(input: $input) {
787
+ business {
788
+ info {
789
+ address {
790
+ line1
791
+ line2
792
+ line3
793
+ line4
794
+ line5
795
+ postalCode
796
+ country
797
+ city
798
+ buildingNumberRange
799
+ buildingName
800
+ flatName
801
+ street
802
+ county
803
+ uprn
804
+ }
805
+ }
806
+ }
807
+ }
722
808
  }
723
- const today = /* @__PURE__ */ new Date();
724
- today.setUTCHours(0, 0, 0, 0);
725
- if (fullDate >= today) {
726
- return makeError(helpers, "dob.future", ["day", "month", "year"]);
809
+ `;
810
+
811
+ // src/mutations/mutations.js
812
+ var mutations = {
813
+ updateBusinessEmail: updateBusinessEmailMutation,
814
+ updateBusinessName: updateBusinessNameMutation,
815
+ updateCustomerName: updateCustomerNameMutation,
816
+ updateCustomerDob: updateCustomerDobMutation,
817
+ updateCustomerPhone: updateCustomerPhoneMutation,
818
+ updateCustomerEmail: updateCustomerEmailMutation,
819
+ updateCustomerAddress: updateCustomerAddressMutation,
820
+ updateBusinessAddress: updateBusinessAddressMutation
821
+ };
822
+
823
+ // src/presenters/base-presenter.js
824
+ var BACK_LINK_DISPLAY_MAX = 50;
825
+ var formatBackLink = (businessName) => {
826
+ if (businessName.length > BACK_LINK_DISPLAY_MAX) {
827
+ return `Back to ${businessName.slice(0, BACK_LINK_DISPLAY_MAX)}\u2026`;
727
828
  }
728
- const tooOldError = checkNotTooOld(fullDate, helpers);
729
- if (tooOldError) {
730
- return tooOldError;
829
+ return `Back to ${businessName}`;
830
+ };
831
+ var formatNumber = (payloadNumber, changedNumber, originalNumber) => {
832
+ if (payloadNumber !== void 0) {
833
+ return payloadNumber;
731
834
  }
732
- return value;
733
- }).messages({
734
- "dob.missingAll": "Enter your date of birth",
735
- "dob.missingDay": "Date of birth must include a day",
736
- "dob.missingMonth": "Date of birth must include a month",
737
- "dob.missingYear": "Date of birth must include a year",
738
- "dob.missingDayMonth": "Date of birth must include a day and month",
739
- "dob.missingDayYear": "Date of birth must include a day and year",
740
- "dob.missingMonthYear": "Date of birth must include a month and year",
741
- "dob.yearLength": "Enter a year with 4 numbers, like 1975",
742
- "dob.invalid": "Date of birth must be a real date",
743
- "dob.future": "Date of birth must be in the past",
744
- "dob.tooOld": "Date of birth must be on or after {{#oldest}}",
745
- "string.noControlChars": "Date of birth must not contain invalid characters"
746
- });
747
- var checkNotTooOld = (fullDate, helpers) => {
748
- const oldestDateAllowed = getOldestAllowedDate();
749
- if (fullDate < oldestDateAllowed) {
750
- const oldestDateString = oldestDateAllowed.toLocaleDateString("en-GB", {
751
- day: "numeric",
752
- month: "long",
753
- year: "numeric"
754
- });
755
- const error = helpers.error("dob.tooOld", { oldest: oldestDateString });
756
- error.path = ["day", "month", "year"];
757
- return error;
835
+ if (changedNumber !== void 0) {
836
+ return changedNumber;
758
837
  }
759
- return null;
838
+ return originalNumber;
760
839
  };
761
- var getOldestAllowedDate = () => {
762
- const date = /* @__PURE__ */ new Date();
763
- date.setUTCHours(0, 0, 0, 0);
764
- date.setUTCFullYear(date.getUTCFullYear() - MAX_AGE_YEARS);
765
- return date;
840
+ var formatDatePart = (changed, original) => {
841
+ return changed ?? (original == null ? void 0 : original.toString()) ?? "";
766
842
  };
767
- var getFullDate = (day, monthValue, year, helpers) => {
768
- const dayValue = Number.parseInt(day, 10);
769
- const yearValue = Number.parseInt(year, 10);
770
- const date = new Date(Date.UTC(yearValue, monthValue - 1, dayValue));
771
- if (date.getUTCFullYear() !== yearValue || date.getUTCMonth() + 1 !== monthValue || date.getUTCDate() !== dayValue) {
772
- return makeError(helpers, "dob.invalid", ["day", "month", "year"]);
843
+ var formatDateInputValues = (payloadDob, changedDob, originalDob) => {
844
+ if (payloadDob) {
845
+ return {
846
+ day: payloadDob.day ?? "",
847
+ month: payloadDob.month ?? "",
848
+ year: payloadDob.year ?? ""
849
+ };
773
850
  }
774
- return date;
851
+ return {
852
+ day: formatDatePart(changedDob == null ? void 0 : changedDob.day, originalDob == null ? void 0 : originalDob.day),
853
+ month: formatDatePart(changedDob == null ? void 0 : changedDob.month, originalDob == null ? void 0 : originalDob.month),
854
+ year: formatDatePart(changedDob == null ? void 0 : changedDob.year, originalDob == null ? void 0 : originalDob.year)
855
+ };
775
856
  };
776
- var getMonthNumber = (month, helpers) => {
777
- if (Number.isNaN(Number(month))) {
778
- const lower = month.toLowerCase();
779
- const mapped = MONTH_MAP[lower];
780
- if (!mapped) {
781
- return makeError(helpers, "dob.invalid", ["month"]);
857
+ var sortErrorsBySectionOrder = (errors, orderedSectionsToFix, SECTION_FIELD_ORDER) => {
858
+ const sortedErrors = [];
859
+ for (const section of orderedSectionsToFix) {
860
+ const fieldsInSection = SECTION_FIELD_ORDER[section] || [];
861
+ for (const field of fieldsInSection) {
862
+ if (errors[field]) {
863
+ sortedErrors.push({
864
+ field,
865
+ ...errors[field]
866
+ });
867
+ }
782
868
  }
783
- return mapped;
784
- }
785
- const parsedMonth = Number.parseInt(month, 10);
786
- if (parsedMonth < 1 || parsedMonth > 12) {
787
- return makeError(helpers, "dob.invalid", ["month"]);
788
869
  }
789
- return parsedMonth;
870
+ return sortedErrors;
790
871
  };
791
- var checkMissingFields = (day, month, year, helpers) => {
792
- if (!day && !month && !year) {
793
- return makeError(helpers, "dob.missingAll", ["day", "month", "year"]);
794
- }
795
- if (!day && month && year) {
796
- return makeError(helpers, "dob.missingDay", ["day"]);
872
+
873
+ // src/presenters/address-presenter.js
874
+ var addressChangeLink = (postcodeLookup, context) => {
875
+ if (context === "business") {
876
+ if (postcodeLookup) {
877
+ return "/business-address-change";
878
+ }
879
+ return "/business-address-enter";
797
880
  }
798
- if (day && !month && year) {
799
- return makeError(helpers, "dob.missingMonth", ["month"]);
881
+ if (context === "personal") {
882
+ if (postcodeLookup) {
883
+ return "/account-address-change";
884
+ }
885
+ return "/account-address-enter";
800
886
  }
801
- if (day && month && !year) {
802
- return makeError(helpers, "dob.missingYear", ["year"]);
887
+ return null;
888
+ };
889
+ var addressBackLink = (postcodeLookup, context) => {
890
+ if (context === "business") {
891
+ if (postcodeLookup) {
892
+ return { href: "/business-address-select" };
893
+ }
894
+ return { href: "/business-address-enter" };
803
895
  }
804
- if (!day && !month && year) {
805
- return makeError(helpers, "dob.missingDayMonth", ["day", "month"]);
896
+ if (context === "personal") {
897
+ if (postcodeLookup) {
898
+ return { href: "/account-address-select" };
899
+ }
900
+ return { href: "/account-address-enter" };
806
901
  }
807
- if (!day && month && !year) {
808
- return makeError(helpers, "dob.missingDayYear", ["day", "year"]);
902
+ return null;
903
+ };
904
+ var buildAddressLine = (parts) => {
905
+ return parts.filter(Boolean).join(", ") || null;
906
+ };
907
+ var buildStreetLine = (buildingRange, street) => {
908
+ return [buildingRange, street].filter(Boolean).join(" ") || null;
909
+ };
910
+ var formatLookupAddress = (lookup, city, country, postcode) => ({
911
+ address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
912
+ address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
913
+ address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
914
+ county: lookup.county ?? null,
915
+ city: city ?? null,
916
+ country: country ?? null,
917
+ postcode: postcode ?? null
918
+ });
919
+ var formatManualAddress = (manual, city, country, postcode) => ({
920
+ address1: manual.line1 ?? null,
921
+ address2: manual.line2 ?? null,
922
+ address3: manual.line3 ?? null,
923
+ city: city ?? null,
924
+ county: manual.line4 ?? null,
925
+ country: country ?? null,
926
+ postcode: postcode ?? null
927
+ });
928
+ var formatDisplayAddress = (address) => {
929
+ const { lookup, manual, postcode, country, city } = address;
930
+ let addressLines = [];
931
+ if (lookup.uprn) {
932
+ const buildingAndStreet = [
933
+ lookup.buildingNumberRange,
934
+ lookup.street
935
+ ].filter(Boolean).join(" ");
936
+ addressLines = [
937
+ lookup.pafOrganisationName,
938
+ lookup.flatName,
939
+ lookup.buildingName,
940
+ buildingAndStreet,
941
+ lookup.doubleDependentLocality,
942
+ lookup.dependentLocality,
943
+ city,
944
+ lookup.county
945
+ ];
946
+ } else {
947
+ addressLines = [
948
+ manual.line1,
949
+ manual.line2,
950
+ manual.line3,
951
+ city,
952
+ manual.line4,
953
+ // County
954
+ manual.line5
955
+ ];
809
956
  }
810
- if (day && !month && !year) {
811
- return makeError(helpers, "dob.missingMonthYear", ["month", "year"]);
957
+ return [
958
+ ...addressLines.filter(Boolean),
959
+ postcode,
960
+ country
961
+ ];
962
+ };
963
+ var formatOriginalAddress = (originalAddress) => {
964
+ const { lookup, manual, city, country, postcode } = originalAddress;
965
+ return lookup.uprn ? formatLookupAddress(lookup, city, country, postcode) : formatManualAddress(manual, city, country, postcode);
966
+ };
967
+ var formatChangedAddress = (changeBusinessAddress) => {
968
+ if (!changeBusinessAddress.uprn) {
969
+ return changeBusinessAddress;
812
970
  }
813
- return null;
971
+ const {
972
+ pafOrganisationName,
973
+ flatName,
974
+ buildingName,
975
+ buildingNumberRange,
976
+ street,
977
+ doubleDependentLocality,
978
+ dependentLocality,
979
+ city,
980
+ county,
981
+ country,
982
+ postcode
983
+ } = changeBusinessAddress;
984
+ return {
985
+ address1: buildAddressLine([pafOrganisationName, flatName, buildingName]),
986
+ address2: buildStreetLine(buildingNumberRange, street),
987
+ address3: buildAddressLine([doubleDependentLocality, dependentLocality]),
988
+ city: city ?? null,
989
+ county: county ?? null,
990
+ country: country ?? null,
991
+ postcode: postcode ?? null
992
+ };
814
993
  };
815
- var makeError = (helpers, code, fields) => {
816
- const error = helpers.error(code);
817
- error.path = fields;
818
- error.isJoiError = true;
819
- return error;
994
+ var formatDisplayAddresses = (addresses, previouslyPickedAddress) => {
995
+ const displayAddresses = addresses.map((address) => ({
996
+ value: `${address.uprn}${address.displayAddress}`,
997
+ text: address.displayAddress,
998
+ selected: (previouslyPickedAddress == null ? void 0 : previouslyPickedAddress.uprn) === address.uprn && (previouslyPickedAddress == null ? void 0 : previouslyPickedAddress.displayAddress) === address.displayAddress
999
+ }));
1000
+ const hasSelectedAddress = displayAddresses.some((addr) => addr.selected);
1001
+ const text = addresses.length === 1 ? "1 address found" : `${addresses.length} addresses found`;
1002
+ displayAddresses.unshift({
1003
+ value: "display",
1004
+ text,
1005
+ selected: !hasSelectedAddress
1006
+ });
1007
+ return displayAddresses;
820
1008
  };
821
1009
 
822
- // src/schemas/personal/personal-email-schema.js
823
- var personalEmailSchema = Joi.object({
824
- personalEmail: Joi.string().required().max(EMAIL_MAX).email({
825
- minDomainSegments: 2,
826
- tlds: {
827
- allow: true,
828
- min: 2
829
- }
830
- }).messages({
831
- "string.max": `Email address must be ${EMAIL_MAX} characters or less`,
832
- "string.empty": "Enter a personal email address",
833
- "string.email": "Enter an email address, like name@example.com",
834
- "string.noControlChars": "Personal email address must not contain invalid characters"
835
- })
836
- });
837
-
838
- // src/schemas/personal/personal-phone-schema.js
839
- var personalPhoneSchema = Joi.object({
840
- personalTelephone: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
841
- "string.min": `Personal telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
842
- "string.max": `Personal telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
843
- "string.pattern.base": "Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
844
- "string.noControlChars": "Personal telephone number must not contain invalid characters"
845
- }),
846
- personalMobile: Joi.string().trim().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
847
- "string.min": `Personal mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
848
- "string.max": `Personal mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
849
- "string.pattern.base": "Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +",
850
- "string.noControlChars": "Personal mobile phone number must not contain invalid characters"
851
- })
852
- }).or("personalTelephone", "personalMobile").messages({
853
- "object.missing": "Enter at least one phone number"
854
- });
855
-
856
- // src/schemas/personal/personal-schemas.js
857
- var personalSchemas = {
858
- name: personalNameSchema,
859
- dob: personalDobSchema,
860
- address: addressSchema,
861
- phone: personalPhoneSchema,
862
- email: personalEmailSchema
1010
+ // src/presenters/business-details-presenter.js
1011
+ var getActionText = (value) => {
1012
+ return value ? "Change" : "Add";
863
1013
  };
864
-
865
- // src/schemas/os-places/address-lookup-schema.js
866
- var addressLookupSchema = Joi.object({
867
- properties: Joi.object({
868
- UPRN: Joi.string().required(),
869
- ADDRESS: Joi.string().required(),
870
- ORGANISATION_NAME: Joi.string().allow(null),
871
- DEPARTMENT_NAME: Joi.string().allow(null),
872
- SUB_BUILDING_NAME: Joi.string().allow(null),
873
- BUILDING_NAME: Joi.string().allow(null),
874
- BUILDING_NUMBER: Joi.string().allow(null),
875
- DEPENDENT_THOROUGHFARE_NAME: Joi.string().allow(null),
876
- THOROUGHFARE_NAME: Joi.string().allow(null),
877
- DOUBLE_DEPENDENT_LOCALITY: Joi.string().allow(null),
878
- DEPENDENT_LOCALITY: Joi.string().allow(null),
879
- POST_TOWN: Joi.string().required(),
880
- POSTCODE: Joi.string().required(),
881
- LOCAL_CUSTODIAN_CODE_DESCRIPTION: Joi.string().allow(null),
882
- COUNTRY_CODE: Joi.string().required()
883
- }).unknown(true).required()
884
- }).unknown(true);
885
-
886
- // src/schemas/os-places/addresses-schema.js
887
- var CHOOSE_ADDRESS_ERROR = "Choose an address";
888
- var addressesSchema = Joi.object({
889
- addresses: Joi.string().invalid("display").required().messages({
890
- "any.required": CHOOSE_ADDRESS_ERROR,
891
- "string.empty": CHOOSE_ADDRESS_ERROR,
892
- "any.invalid": CHOOSE_ADDRESS_ERROR
893
- })
894
- });
895
-
896
- // src/schemas/os-places/uk-postcode-schema.js
897
- var ukPostcodeSchema = Joi.object({
898
- postcode: Joi.string().trim().required().uppercase().max(POSTCODE_MAX).pattern(/^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$/).messages({
899
- "any.required": "Enter a postcode",
900
- "string.empty": "Enter a postcode",
901
- "string.max": `Postal code must be ${POSTCODE_MAX} characters or less`,
902
- "string.pattern.base": "Enter a full UK postcode, like AA3 1AB"
903
- })
904
- });
905
-
906
- // src/schemas/os-places/os-places-schemas.js
907
- var osPlacesSchemas = {
908
- addressLookup: addressLookupSchema,
909
- addresses: addressesSchema,
910
- ukPostcode: ukPostcodeSchema
1014
+ var formatCph = (countyParishHoldings) => {
1015
+ return (countyParishHoldings || []).filter((cph) => cph == null ? void 0 : cph.cphNumber).map((cph) => cph.cphNumber);
1016
+ };
1017
+ var formatCphText = (count) => {
1018
+ return `County Parish Holding (CPH) number${count !== 1 ? "s" : ""}`;
1019
+ };
1020
+ var formatBusinessAddress = (businessAddress) => {
1021
+ var _a, _b;
1022
+ if (((_a = businessAddress == null ? void 0 : businessAddress.lookup) == null ? void 0 : _a.uprn) || ((_b = businessAddress == null ? void 0 : businessAddress.manual) == null ? void 0 : _b.line1)) {
1023
+ return formatDisplayAddress(businessAddress);
1024
+ }
1025
+ return [];
911
1026
  };
912
1027
 
913
- // src/schemas/schemas.js
914
- var schemas = {
915
- business: businessSchemas,
916
- customer: customerSchemas,
917
- personal: personalSchemas,
918
- osPlaces: osPlacesSchemas
1028
+ // src/presenters/presenters.js
1029
+ var presenters = {
1030
+ formatBackLink,
1031
+ formatNumber,
1032
+ formatDateInputValues,
1033
+ formatDisplayAddress,
1034
+ formatOriginalAddress,
1035
+ formatChangedAddress,
1036
+ formatDisplayAddresses,
1037
+ sortErrorsBySectionOrder,
1038
+ getActionText,
1039
+ formatCph,
1040
+ formatCphText,
1041
+ formatBusinessAddress,
1042
+ addressBackLink,
1043
+ addressChangeLink
919
1044
  };
920
1045
 
921
1046
  // src/utils/format-full-name.js
@@ -970,11 +1095,314 @@ var utils = {
970
1095
  buildUpdateBusinessEmailVariables,
971
1096
  buildUpdateBusinessNameVariables
972
1097
  };
1098
+
1099
+ // src/services/os-places/address-lookup-service.js
1100
+ import { placesAPI } from "osdatahub";
1101
+
1102
+ // src/mock-data/mock-os-places-addresses.js
1103
+ var mockAddresses = [
1104
+ {
1105
+ properties: {
1106
+ UPRN: "10000001",
1107
+ ADDRESS: "APARTMENT 1, REDBRIDGE HOUSE, 1 ROSE COURT, LONDON, SW1A 1AA",
1108
+ SUB_BUILDING_NAME: "APARTMENT 1",
1109
+ BUILDING_NAME: "REDBRIDGE HOUSE",
1110
+ BUILDING_NUMBER: "1",
1111
+ THOROUGHFARE_NAME: "ROSE COURT",
1112
+ POST_TOWN: "LONDON",
1113
+ POSTCODE: "SW1A 1AA",
1114
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "GREATER LONDON",
1115
+ COUNTRY_CODE: "E"
1116
+ }
1117
+ },
1118
+ {
1119
+ properties: {
1120
+ UPRN: "10000002",
1121
+ ADDRESS: "FLAT 2B, KINGSLEY APARTMENTS, 12 VICTORIA SQUARE, LONDON, SW1A 1AA",
1122
+ SUB_BUILDING_NAME: "FLAT 2B",
1123
+ BUILDING_NAME: "KINGSLEY APARTMENTS",
1124
+ BUILDING_NUMBER: "12",
1125
+ THOROUGHFARE_NAME: "VICTORIA SQUARE",
1126
+ DEPENDENT_LOCALITY: "",
1127
+ POST_TOWN: "LONDON",
1128
+ POSTCODE: "SW1A 1AA",
1129
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "GREATER LONDON",
1130
+ COUNTRY_CODE: "E"
1131
+ }
1132
+ },
1133
+ {
1134
+ properties: {
1135
+ UPRN: "10000003",
1136
+ ADDRESS: "THE GROUND FLOOR, THE OLD GRANARY, 5 CHURCH LANE, LONDON, SW1A 1AA",
1137
+ ORGANISATION_NAME: "OLD GRANARY STUDIOS",
1138
+ SUB_BUILDING_NAME: "GROUND FLOOR",
1139
+ BUILDING_NAME: "THE OLD GRANARY",
1140
+ BUILDING_NUMBER: "5",
1141
+ THOROUGHFARE_NAME: "CHURCH LANE",
1142
+ POST_TOWN: "LONDON",
1143
+ POSTCODE: "SW1A 1AA",
1144
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "GREATER LONDON",
1145
+ COUNTRY_CODE: "E"
1146
+ }
1147
+ },
1148
+ {
1149
+ properties: {
1150
+ UPRN: "10000004",
1151
+ ADDRESS: "SUITE 3, WELLINGTON HOUSE, 20 QUEEN STREET, LONDON, SW1A 1AA",
1152
+ ORGANISATION_NAME: "WELLINGTON CONSULTING LTD",
1153
+ DEPARTMENT_NAME: "CLIENT SERVICES",
1154
+ SUB_BUILDING_NAME: "SUITE 3",
1155
+ BUILDING_NAME: "WELLINGTON HOUSE",
1156
+ BUILDING_NUMBER: "20",
1157
+ THOROUGHFARE_NAME: "QUEEN STREET",
1158
+ POST_TOWN: "LONDON",
1159
+ POSTCODE: "SW1A 1AA",
1160
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "GREATER LONDON",
1161
+ COUNTRY_CODE: "E"
1162
+ }
1163
+ },
1164
+ {
1165
+ properties: {
1166
+ UPRN: "10000005",
1167
+ ADDRESS: "UNIT 6, MARKET ROW, 3 MARKET YARD, LONDON, SW1A 1AA",
1168
+ ORGANISATION_NAME: "MARKET ROW TRADERS",
1169
+ SUB_BUILDING_NAME: "UNIT 6",
1170
+ BUILDING_NAME: "MARKET ROW",
1171
+ BUILDING_NUMBER: "3",
1172
+ THOROUGHFARE_NAME: "MARKET YARD",
1173
+ POST_TOWN: "LONDON",
1174
+ POSTCODE: "SW1A 1AA",
1175
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "GREATER LONDON",
1176
+ COUNTRY_CODE: "E"
1177
+ }
1178
+ },
1179
+ {
1180
+ properties: {
1181
+ UPRN: "20000001",
1182
+ ADDRESS: "1 ORCHARD COTTAGES, WESTFIELD LANE, SHEPTON MALLET, BS14 8XX",
1183
+ BUILDING_NAME: "ORCHARD COTTAGES",
1184
+ BUILDING_NUMBER: "1",
1185
+ THOROUGHFARE_NAME: "WESTFIELD LANE",
1186
+ POST_TOWN: "SHEPTON MALLET",
1187
+ POSTCODE: "BS14 8XX",
1188
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "SOMERSET",
1189
+ COUNTRY_CODE: "E"
1190
+ }
1191
+ },
1192
+ {
1193
+ properties: {
1194
+ UPRN: "20000002",
1195
+ ADDRESS: "THE STABLES, MANOR FARM, HOLLOW ROAD, SHEPTON MALLET, BS14 8XX",
1196
+ ORGANISATION_NAME: "MANOR FARM",
1197
+ SUB_BUILDING_NAME: "THE STABLES",
1198
+ BUILDING_NAME: "MANOR FARM",
1199
+ THOROUGHFARE_NAME: "HOLLOW ROAD",
1200
+ POST_TOWN: "SHEPTON MALLET",
1201
+ POSTCODE: "BS14 8XX",
1202
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "SOMERSET",
1203
+ COUNTRY_CODE: "E"
1204
+ }
1205
+ },
1206
+ {
1207
+ properties: {
1208
+ UPRN: "30000001",
1209
+ ADDRESS: "FLAT 4, BISHOP COURT, 9 FLEET STREET, LONDON, EC1A 1BB",
1210
+ SUB_BUILDING_NAME: "FLAT 4",
1211
+ BUILDING_NAME: "BISHOP COURT",
1212
+ BUILDING_NUMBER: "9",
1213
+ THOROUGHFARE_NAME: "FLEET STREET",
1214
+ POST_TOWN: "LONDON",
1215
+ POSTCODE: "EC1A 1BB",
1216
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "CITY OF LONDON",
1217
+ COUNTRY_CODE: "E"
1218
+ }
1219
+ },
1220
+ {
1221
+ properties: {
1222
+ UPRN: "40000001",
1223
+ ADDRESS: "2A PICCADILLY ARCADE, MANCHESTER, M1 1AE",
1224
+ ORGANISATION_NAME: "PICCADILLY ARCADE LTD",
1225
+ SUB_BUILDING_NAME: "2A",
1226
+ BUILDING_NAME: "PICCADILLY ARCADE",
1227
+ BUILDING_NUMBER: "2A",
1228
+ THOROUGHFARE_NAME: "PICCADILLY",
1229
+ POST_TOWN: "MANCHESTER",
1230
+ POSTCODE: "M1 1AE",
1231
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "GREATER MANCHESTER",
1232
+ COUNTRY_CODE: "E"
1233
+ }
1234
+ },
1235
+ {
1236
+ properties: {
1237
+ UPRN: "50000001",
1238
+ ADDRESS: "3 WESTBOURNE TERRACE, LONDON, W1A 0AX",
1239
+ BUILDING_NAME: "WESTBOURNE TERRACE",
1240
+ BUILDING_NUMBER: "3",
1241
+ THOROUGHFARE_NAME: "WESTBOURNE TERRACE",
1242
+ POST_TOWN: "LONDON",
1243
+ POSTCODE: "W1A 0AX",
1244
+ LOCAL_CUSTODIAN_CODE_DESCRIPTION: "WESTMINSTER",
1245
+ COUNTRY_CODE: "E"
1246
+ }
1247
+ }
1248
+ ];
1249
+
1250
+ // src/services/os-places/os-places-stub.js
1251
+ var mockPostcode = (postcode) => {
1252
+ const formattedPostcode = postcode == null ? void 0 : postcode.toUpperCase().replaceAll(" ", "");
1253
+ const matchingAddresses = mockAddresses.filter((address) => {
1254
+ var _a;
1255
+ const addressPostcode = (_a = address.properties.POSTCODE) == null ? void 0 : _a.toUpperCase().replaceAll(" ", "");
1256
+ return addressPostcode === formattedPostcode;
1257
+ });
1258
+ return {
1259
+ features: matchingAddresses
1260
+ };
1261
+ };
1262
+
1263
+ // src/services/os-places/address-lookup-service.js
1264
+ var addressLookupService = async (postcode, osPlacesConfig) => {
1265
+ const addresses = await fetchAddressesFromPostcodeLookup(postcode, osPlacesConfig);
1266
+ if (addresses.error) {
1267
+ return addresses;
1268
+ }
1269
+ if (!(addresses == null ? void 0 : addresses.length)) {
1270
+ return {
1271
+ error: [
1272
+ {
1273
+ message: "No addresses found for this postcode",
1274
+ path: ["postcode"]
1275
+ }
1276
+ ]
1277
+ };
1278
+ }
1279
+ const mappedAddresses = addressLookupMapper(addresses);
1280
+ return mappedAddresses;
1281
+ };
1282
+ var fetchAddressesFromPostcodeLookup = async (postcode, osPlacesConfig) => {
1283
+ const MAX_RETRIES = 3;
1284
+ const INITIAL_BACKOFF_MS = 100;
1285
+ for (let attemptNumber = 1; attemptNumber <= MAX_RETRIES; attemptNumber++) {
1286
+ try {
1287
+ const { clientId, osPlacesStub } = osPlacesConfig;
1288
+ if (osPlacesStub) {
1289
+ const response2 = mockPostcode(postcode);
1290
+ return response2.features ?? [];
1291
+ }
1292
+ const response = await fetchFromPlacesAPI(clientId, postcode);
1293
+ return response.features ?? [];
1294
+ } catch (error) {
1295
+ const shouldRetry = isRetryable(error) && attemptNumber < MAX_RETRIES;
1296
+ if (!shouldRetry) {
1297
+ return buildErrorResponse(error.message);
1298
+ }
1299
+ const backoffMs = calculateExponentialBackoff(attemptNumber, INITIAL_BACKOFF_MS);
1300
+ await delayBeforeRetry(backoffMs);
1301
+ }
1302
+ }
1303
+ };
1304
+ var calculateExponentialBackoff = (attemptNumber, baseMs) => {
1305
+ const exponent = attemptNumber - 1;
1306
+ const powerOf2 = Math.pow(2, exponent);
1307
+ const backoffDuration = baseMs * powerOf2;
1308
+ return backoffDuration;
1309
+ };
1310
+ function delayBeforeRetry(ms) {
1311
+ const callback = (resolve) => {
1312
+ setTimeout(resolve, ms);
1313
+ };
1314
+ return new Promise(callback);
1315
+ }
1316
+ var buildErrorResponse = (message) => {
1317
+ return {
1318
+ error: [
1319
+ {
1320
+ message: message || "Failed to fetch addresses",
1321
+ path: ["postcode"]
1322
+ }
1323
+ ]
1324
+ };
1325
+ };
1326
+ var fetchFromPlacesAPI = async (clientId, postcode) => {
1327
+ const response = await placesAPI.postcode(clientId, postcode, { limit: 150 });
1328
+ return response;
1329
+ };
1330
+ var isRetryable = (error) => {
1331
+ const networkErrors = ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT"];
1332
+ if (networkErrors.includes(error.code)) {
1333
+ return true;
1334
+ }
1335
+ if (error.status && error.status >= INTERNAL_SERVER_ERROR) {
1336
+ return true;
1337
+ }
1338
+ return false;
1339
+ };
1340
+
1341
+ // src/services/build-address-variables-service.js
1342
+ var buildUprnAddress = (change) => {
1343
+ return {
1344
+ pafOrganisationName: nullIfUndefined(change.pafOrganisationName),
1345
+ buildingNumberRange: nullIfUndefined(change.buildingNumberRange),
1346
+ buildingName: nullIfUndefined(change.buildingName),
1347
+ flatName: nullIfUndefined(change.flatName),
1348
+ street: nullIfUndefined(change.street),
1349
+ city: nullIfUndefined(change.city),
1350
+ county: nullIfUndefined(change.county),
1351
+ postalCode: nullIfUndefined(change.postcode),
1352
+ country: nullIfUndefined(change.country),
1353
+ dependentLocality: nullIfUndefined(change.dependentLocality),
1354
+ doubleDependentLocality: nullIfUndefined(change.doubleDependentLocality),
1355
+ line1: null,
1356
+ line2: null,
1357
+ line3: null,
1358
+ line4: null,
1359
+ line5: null,
1360
+ uprn: change.uprn
1361
+ // required for DAL/v1
1362
+ };
1363
+ };
1364
+ var buildManualAddress = (change) => {
1365
+ return {
1366
+ pafOrganisationName: null,
1367
+ buildingNumberRange: null,
1368
+ buildingName: null,
1369
+ flatName: null,
1370
+ street: null,
1371
+ dependentLocality: null,
1372
+ doubleDependentLocality: null,
1373
+ county: null,
1374
+ uprn: null,
1375
+ line1: change.address1,
1376
+ // required for DAL/v1
1377
+ line2: nullIfUndefined(change.address2),
1378
+ line3: nullIfUndefined(change.address3),
1379
+ line4: nullIfUndefined(change.county),
1380
+ // manual county mapped for validation
1381
+ line5: null,
1382
+ city: change.city,
1383
+ // required for DAL/v1
1384
+ postalCode: change.postcode,
1385
+ // required for DAL/v1
1386
+ country: change.country
1387
+ // required for DAL/v1
1388
+ };
1389
+ };
1390
+ var nullIfUndefined = (value) => {
1391
+ return value ?? null;
1392
+ };
1393
+
1394
+ // src/services/services.js
1395
+ var services = {
1396
+ addressLookup: addressLookupService,
1397
+ buildUprnAddress,
1398
+ buildManualAddress
1399
+ };
973
1400
  export {
974
1401
  constants,
975
1402
  mappers,
976
1403
  mutations,
977
1404
  presenters,
978
1405
  schemas,
1406
+ services,
979
1407
  utils
980
1408
  };