@defra/forms-engine-plugin 4.14.2 → 4.14.3

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.
@@ -1,15 +1,19 @@
1
- import joi from 'joi';
2
1
  import { FormComponent } from "./FormComponent.js";
3
2
  import { addClassOptionIfNone } from "./helpers/index.js";
3
+ import { INTERNATIONAL_ERROR_CODE, INVALID_ERROR_CODE, UK_ERROR_CODE, joi } from "./helpers/telephone.js";
4
4
  import { messageTemplate } from "../pageControllers/validationOptions.js";
5
- const PATTERN = /^[0-9\\\s+()-]*$/;
6
5
  export class TelephoneNumberField extends FormComponent {
7
6
  constructor(def, props) {
8
7
  super(def, props);
9
8
  const {
10
9
  options
11
10
  } = def;
12
- let formSchema = joi.string().trim().pattern(PATTERN).label(this.label).required();
11
+ const {
12
+ format
13
+ } = options;
14
+ let formSchema = joi.string().trim().phoneNumber({
15
+ format
16
+ }).label(this.label).required();
13
17
  if (options.required === false) {
14
18
  formSchema = formSchema.allow('');
15
19
  }
@@ -18,7 +22,10 @@ export class TelephoneNumberField extends FormComponent {
18
22
  formSchema = formSchema.messages({
19
23
  'any.required': message,
20
24
  'string.empty': message,
21
- 'string.pattern.base': message
25
+ 'string.pattern.base': message,
26
+ [INVALID_ERROR_CODE]: message,
27
+ [UK_ERROR_CODE]: message,
28
+ [INTERNATIONAL_ERROR_CODE]: message
22
29
  });
23
30
  } else if (options.customValidationMessages) {
24
31
  formSchema = formSchema.messages(options.customValidationMessages);
@@ -1 +1 @@
1
- {"version":3,"file":"TelephoneNumberField.js","names":["joi","FormComponent","addClassOptionIfNone","messageTemplate","PATTERN","TelephoneNumberField","constructor","def","props","options","formSchema","string","trim","pattern","label","required","allow","customValidationMessage","message","messages","customValidationMessages","default","stateSchema","getViewModel","payload","errors","viewModel","attributes","autocomplete","type","getAllPossibleErrors","baseErrors","template","format","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/TelephoneNumberField.ts"],"sourcesContent":["import { type TelephoneNumberFieldComponent } from '@defra/forms-model'\nimport joi, { type StringSchema } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { addClassOptionIfNone } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nconst PATTERN = /^[0-9\\\\\\s+()-]*$/\n\nexport class TelephoneNumberField extends FormComponent {\n declare options: TelephoneNumberFieldComponent['options']\n declare formSchema: StringSchema\n declare stateSchema: StringSchema\n\n constructor(\n def: TelephoneNumberFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options } = def\n\n let formSchema = joi\n .string()\n .trim()\n .pattern(PATTERN)\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (options.customValidationMessage) {\n const message = options.customValidationMessage\n\n formSchema = formSchema.messages({\n 'any.required': message,\n 'string.empty': message,\n 'string.pattern.base': message\n })\n } else if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n }\n\n addClassOptionIfNone(options, 'govuk-input--width-20')\n\n this.formSchema = formSchema.default('')\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n const { attributes } = viewModel\n\n attributes.autocomplete = 'tel'\n\n return {\n ...viewModel,\n type: 'tel'\n }\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return TelephoneNumberField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n { type: 'format', template: messageTemplate.format }\n ],\n advancedSettingsErrors: []\n }\n }\n}\n"],"mappings":"AACA,OAAOA,GAAG,MAA6B,KAAK;AAE5C,SAASC,aAAa;AACtB,SAASC,oBAAoB;AAC7B,SAASC,eAAe;AAOxB,MAAMC,OAAO,GAAG,kBAAkB;AAElC,OAAO,MAAMC,oBAAoB,SAASJ,aAAa,CAAC;EAKtDK,WAAWA,CACTC,GAAkC,EAClCC,KAAqD,EACrD;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAM;MAAEC;IAAQ,CAAC,GAAGF,GAAG;IAEvB,IAAIG,UAAU,GAAGV,GAAG,CACjBW,MAAM,CAAC,CAAC,CACRC,IAAI,CAAC,CAAC,CACNC,OAAO,CAACT,OAAO,CAAC,CAChBU,KAAK,CAAC,IAAI,CAACA,KAAK,CAAC,CACjBC,QAAQ,CAAC,CAAC;IAEb,IAAIN,OAAO,CAACM,QAAQ,KAAK,KAAK,EAAE;MAC9BL,UAAU,GAAGA,UAAU,CAACM,KAAK,CAAC,EAAE,CAAC;IACnC;IAEA,IAAIP,OAAO,CAACQ,uBAAuB,EAAE;MACnC,MAAMC,OAAO,GAAGT,OAAO,CAACQ,uBAAuB;MAE/CP,UAAU,GAAGA,UAAU,CAACS,QAAQ,CAAC;QAC/B,cAAc,EAAED,OAAO;QACvB,cAAc,EAAEA,OAAO;QACvB,qBAAqB,EAAEA;MACzB,CAAC,CAAC;IACJ,CAAC,MAAM,IAAIT,OAAO,CAACW,wBAAwB,EAAE;MAC3CV,UAAU,GAAGA,UAAU,CAACS,QAAQ,CAACV,OAAO,CAACW,wBAAwB,CAAC;IACpE;IAEAlB,oBAAoB,CAACO,OAAO,EAAE,uBAAuB,CAAC;IAEtD,IAAI,CAACC,UAAU,GAAGA,UAAU,CAACW,OAAO,CAAC,EAAE,CAAC;IACxC,IAAI,CAACC,WAAW,GAAGZ,UAAU,CAACW,OAAO,CAAC,IAAI,CAAC,CAACL,KAAK,CAAC,IAAI,CAAC;IACvD,IAAI,CAACP,OAAO,GAAGA,OAAO;EACxB;EAEAc,YAAYA,CAACC,OAAoB,EAAEC,MAA8B,EAAE;IACjE,MAAMC,SAAS,GAAG,KAAK,CAACH,YAAY,CAACC,OAAO,EAAEC,MAAM,CAAC;IACrD,MAAM;MAAEE;IAAW,CAAC,GAAGD,SAAS;IAEhCC,UAAU,CAACC,YAAY,GAAG,KAAK;IAE/B,OAAO;MACL,GAAGF,SAAS;MACZG,IAAI,EAAE;IACR,CAAC;EACH;;EAEA;AACF;AACA;EACEC,oBAAoBA,CAAA,EAA6B;IAC/C,OAAOzB,oBAAoB,CAACyB,oBAAoB,CAAC,CAAC;EACpD;;EAEA;AACF;AACA;EACE,OAAOA,oBAAoBA,CAAA,EAA6B;IACtD,OAAO;MACLC,UAAU,EAAE,CACV;QAAEF,IAAI,EAAE,UAAU;QAAEG,QAAQ,EAAE7B,eAAe,CAACY;MAAS,CAAC,EACxD;QAAEc,IAAI,EAAE,QAAQ;QAAEG,QAAQ,EAAE7B,eAAe,CAAC8B;MAAO,CAAC,CACrD;MACDC,sBAAsB,EAAE;IAC1B,CAAC;EACH;AACF","ignoreList":[]}
1
+ {"version":3,"file":"TelephoneNumberField.js","names":["FormComponent","addClassOptionIfNone","INTERNATIONAL_ERROR_CODE","INVALID_ERROR_CODE","UK_ERROR_CODE","joi","messageTemplate","TelephoneNumberField","constructor","def","props","options","format","formSchema","string","trim","phoneNumber","label","required","allow","customValidationMessage","message","messages","customValidationMessages","default","stateSchema","getViewModel","payload","errors","viewModel","attributes","autocomplete","type","getAllPossibleErrors","baseErrors","template","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/TelephoneNumberField.ts"],"sourcesContent":["import { type TelephoneNumberFieldComponent } from '@defra/forms-model'\nimport { type StringSchema } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { addClassOptionIfNone } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport {\n INTERNATIONAL_ERROR_CODE,\n INVALID_ERROR_CODE,\n UK_ERROR_CODE,\n joi\n} from '~/src/server/plugins/engine/components/helpers/telephone.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nexport class TelephoneNumberField extends FormComponent {\n declare options: TelephoneNumberFieldComponent['options']\n declare formSchema: StringSchema\n declare stateSchema: StringSchema\n\n constructor(\n def: TelephoneNumberFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options } = def\n const { format } = options\n\n let formSchema = joi\n .string()\n .trim()\n .phoneNumber({ format })\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (options.customValidationMessage) {\n const message = options.customValidationMessage\n\n formSchema = formSchema.messages({\n 'any.required': message,\n 'string.empty': message,\n 'string.pattern.base': message,\n [INVALID_ERROR_CODE]: message,\n [UK_ERROR_CODE]: message,\n [INTERNATIONAL_ERROR_CODE]: message\n })\n } else if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n }\n\n addClassOptionIfNone(options, 'govuk-input--width-20')\n\n this.formSchema = formSchema.default('')\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n const { attributes } = viewModel\n\n attributes.autocomplete = 'tel'\n\n return {\n ...viewModel,\n type: 'tel'\n }\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return TelephoneNumberField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n { type: 'format', template: messageTemplate.format }\n ],\n advancedSettingsErrors: []\n }\n }\n}\n"],"mappings":"AAGA,SAASA,aAAa;AACtB,SAASC,oBAAoB;AAC7B,SACEC,wBAAwB,EACxBC,kBAAkB,EAClBC,aAAa,EACbC,GAAG;AAEL,SAASC,eAAe;AAOxB,OAAO,MAAMC,oBAAoB,SAASP,aAAa,CAAC;EAKtDQ,WAAWA,CACTC,GAAkC,EAClCC,KAAqD,EACrD;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAM;MAAEC;IAAQ,CAAC,GAAGF,GAAG;IACvB,MAAM;MAAEG;IAAO,CAAC,GAAGD,OAAO;IAE1B,IAAIE,UAAU,GAAGR,GAAG,CACjBS,MAAM,CAAC,CAAC,CACRC,IAAI,CAAC,CAAC,CACNC,WAAW,CAAC;MAAEJ;IAAO,CAAC,CAAC,CACvBK,KAAK,CAAC,IAAI,CAACA,KAAK,CAAC,CACjBC,QAAQ,CAAC,CAAC;IAEb,IAAIP,OAAO,CAACO,QAAQ,KAAK,KAAK,EAAE;MAC9BL,UAAU,GAAGA,UAAU,CAACM,KAAK,CAAC,EAAE,CAAC;IACnC;IAEA,IAAIR,OAAO,CAACS,uBAAuB,EAAE;MACnC,MAAMC,OAAO,GAAGV,OAAO,CAACS,uBAAuB;MAE/CP,UAAU,GAAGA,UAAU,CAACS,QAAQ,CAAC;QAC/B,cAAc,EAAED,OAAO;QACvB,cAAc,EAAEA,OAAO;QACvB,qBAAqB,EAAEA,OAAO;QAC9B,CAAClB,kBAAkB,GAAGkB,OAAO;QAC7B,CAACjB,aAAa,GAAGiB,OAAO;QACxB,CAACnB,wBAAwB,GAAGmB;MAC9B,CAAC,CAAC;IACJ,CAAC,MAAM,IAAIV,OAAO,CAACY,wBAAwB,EAAE;MAC3CV,UAAU,GAAGA,UAAU,CAACS,QAAQ,CAACX,OAAO,CAACY,wBAAwB,CAAC;IACpE;IAEAtB,oBAAoB,CAACU,OAAO,EAAE,uBAAuB,CAAC;IAEtD,IAAI,CAACE,UAAU,GAAGA,UAAU,CAACW,OAAO,CAAC,EAAE,CAAC;IACxC,IAAI,CAACC,WAAW,GAAGZ,UAAU,CAACW,OAAO,CAAC,IAAI,CAAC,CAACL,KAAK,CAAC,IAAI,CAAC;IACvD,IAAI,CAACR,OAAO,GAAGA,OAAO;EACxB;EAEAe,YAAYA,CAACC,OAAoB,EAAEC,MAA8B,EAAE;IACjE,MAAMC,SAAS,GAAG,KAAK,CAACH,YAAY,CAACC,OAAO,EAAEC,MAAM,CAAC;IACrD,MAAM;MAAEE;IAAW,CAAC,GAAGD,SAAS;IAEhCC,UAAU,CAACC,YAAY,GAAG,KAAK;IAE/B,OAAO;MACL,GAAGF,SAAS;MACZG,IAAI,EAAE;IACR,CAAC;EACH;;EAEA;AACF;AACA;EACEC,oBAAoBA,CAAA,EAA6B;IAC/C,OAAO1B,oBAAoB,CAAC0B,oBAAoB,CAAC,CAAC;EACpD;;EAEA;AACF;AACA;EACE,OAAOA,oBAAoBA,CAAA,EAA6B;IACtD,OAAO;MACLC,UAAU,EAAE,CACV;QAAEF,IAAI,EAAE,UAAU;QAAEG,QAAQ,EAAE7B,eAAe,CAACY;MAAS,CAAC,EACxD;QAAEc,IAAI,EAAE,QAAQ;QAAEG,QAAQ,EAAE7B,eAAe,CAACM;MAAO,CAAC,CACrD;MACDwB,sBAAsB,EAAE;IAC1B,CAAC;EACH;AACF","ignoreList":[]}
@@ -2,6 +2,3 @@ import { type GeospatialFieldComponent } from '@defra/forms-model';
2
2
  import JoiBase from 'joi';
3
3
  import { type Feature } from '../../../../../server/plugins/engine/types.js';
4
4
  export declare function getGeospatialSchema(def: GeospatialFieldComponent): JoiBase.ArraySchema<Feature[]>;
5
- /**
6
- * @import { CustomHelpers } from 'joi'
7
- */
@@ -122,8 +122,4 @@ export function getGeospatialSchema(def) {
122
122
  };
123
123
  return applySchemaConstraints(Joi.array().items(featureSchema.custom(validateCountryBounds)).unique('id'), def);
124
124
  }
125
-
126
- /**
127
- * @import { CustomHelpers } from 'joi'
128
- */
129
125
  //# sourceMappingURL=geospatial.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"geospatial.js","names":["GeospatialFieldOptionsCountryEnum","Bourne","booleanWithin","JoiBase","countries","countriesDesc","England","NorthernIreland","Scotland","Wales","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trimmed","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","applySchemaConstraints","schema","def","options","constraints","isOptional","length","max","optional","getGeospatialSchema","country","at","unique","validateCountryBounds","countryFeature","features","find","feature","custom"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n GeospatialFieldOptionsCountryEnum,\n type GeospatialFieldComponent,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst countriesDesc: Record<GeospatialFieldOptionsCountryEnum, string> = {\n [GeospatialFieldOptionsCountryEnum.England]: 'England',\n [GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',\n [GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',\n [GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'\n}\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed === '' || trimmed === '[]') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nfunction applySchemaConstraints(\n schema: JoiBase.ArraySchema<Feature[]>,\n def: GeospatialFieldComponent\n) {\n const { options, schema: constraints } = def\n const isOptional = options.required === false\n\n if (typeof constraints?.length === 'number') {\n schema = schema.length(constraints.length)\n } else {\n if (typeof constraints?.min === 'number') {\n schema = schema.min(constraints.min)\n } else if (!isOptional) {\n schema = schema.min(1)\n }\n\n schema = schema.max(\n typeof constraints?.max === 'number' ? constraints.max : 50\n )\n }\n\n if (isOptional) {\n schema = schema.optional()\n } else {\n schema = schema.required()\n }\n\n return schema\n}\n\nexport function getGeospatialSchema(\n def: GeospatialFieldComponent\n): JoiBase.ArraySchema<Feature[]> {\n const { options = {} } = def\n const country: GeospatialFieldOptionsCountry | undefined =\n options.countries?.at(0)\n\n if (!country) {\n return applySchemaConstraints(\n Joi.array<Feature[]>().items(featureSchema).unique('id'),\n def\n )\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return applySchemaConstraints(\n Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id'),\n def\n )\n}\n\n/**\n * @import { CustomHelpers } from 'joi'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"geospatial.js","names":["GeospatialFieldOptionsCountryEnum","Bourne","booleanWithin","JoiBase","countries","countriesDesc","England","NorthernIreland","Scotland","Wales","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trimmed","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","applySchemaConstraints","schema","def","options","constraints","isOptional","length","max","optional","getGeospatialSchema","country","at","unique","validateCountryBounds","countryFeature","features","find","feature","custom"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n GeospatialFieldOptionsCountryEnum,\n type GeospatialFieldComponent,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst countriesDesc: Record<GeospatialFieldOptionsCountryEnum, string> = {\n [GeospatialFieldOptionsCountryEnum.England]: 'England',\n [GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',\n [GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',\n [GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'\n}\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed === '' || trimmed === '[]') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nfunction applySchemaConstraints(\n schema: JoiBase.ArraySchema<Feature[]>,\n def: GeospatialFieldComponent\n) {\n const { options, schema: constraints } = def\n const isOptional = options.required === false\n\n if (typeof constraints?.length === 'number') {\n schema = schema.length(constraints.length)\n } else {\n if (typeof constraints?.min === 'number') {\n schema = schema.min(constraints.min)\n } else if (!isOptional) {\n schema = schema.min(1)\n }\n\n schema = schema.max(\n typeof constraints?.max === 'number' ? constraints.max : 50\n )\n }\n\n if (isOptional) {\n schema = schema.optional()\n } else {\n schema = schema.required()\n }\n\n return schema\n}\n\nexport function getGeospatialSchema(\n def: GeospatialFieldComponent\n): JoiBase.ArraySchema<Feature[]> {\n const { options = {} } = def\n const country: GeospatialFieldOptionsCountry | undefined =\n options.countries?.at(0)\n\n if (!country) {\n return applySchemaConstraints(\n Joi.array<Feature[]>().items(featureSchema).unique('id'),\n def\n )\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return applySchemaConstraints(\n Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id'),\n def\n )\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,10 @@
1
+ import { TelephoneNumberFieldOptionsFormatEnum } from '@defra/forms-model';
2
+ import LibPhoneNumber from 'google-libphonenumber';
3
+ import JoiBase from 'joi';
4
+ export declare const COUNTRY = "GB";
5
+ export declare const INVALID_ERROR_CODE = "phoneNumber.invalid";
6
+ export declare const UK_ERROR_CODE = "phoneNumber.uk";
7
+ export declare const INTERNATIONAL_ERROR_CODE = "phoneNumber.international";
8
+ export declare const isUKNumber: (value: LibPhoneNumber.PhoneNumber) => boolean;
9
+ export declare function getErrorCode(format?: TelephoneNumberFieldOptionsFormatEnum): "phoneNumber.invalid" | "phoneNumber.uk" | "phoneNumber.international";
10
+ export declare const joi: JoiBase.Root;
@@ -0,0 +1,71 @@
1
+ import { TelephoneNumberFieldOptionsFormatEnum } from '@defra/forms-model';
2
+ import LibPhoneNumber from 'google-libphonenumber';
3
+ import JoiBase from 'joi';
4
+ import { opts } from "../../pageControllers/validationOptions.js";
5
+ const phoneUtil = LibPhoneNumber.PhoneNumberUtil.getInstance();
6
+ export const COUNTRY = 'GB';
7
+ export const INVALID_ERROR_CODE = 'phoneNumber.invalid';
8
+ export const UK_ERROR_CODE = 'phoneNumber.uk';
9
+ export const INTERNATIONAL_ERROR_CODE = 'phoneNumber.international';
10
+ export const isUKNumber = value => {
11
+ return phoneUtil.isValidNumberForRegion(value, COUNTRY);
12
+ };
13
+ export function getErrorCode(format) {
14
+ if (format === TelephoneNumberFieldOptionsFormatEnum.UK) {
15
+ return UK_ERROR_CODE;
16
+ } else if (format === TelephoneNumberFieldOptionsFormatEnum.International) {
17
+ return INTERNATIONAL_ERROR_CODE;
18
+ }
19
+ return INVALID_ERROR_CODE;
20
+ }
21
+ export const joi = JoiBase.extend({
22
+ type: 'string',
23
+ base: JoiBase.string(),
24
+ messages: {
25
+ [INVALID_ERROR_CODE]: JoiBase.expression('Enter {{lowerFirst(#label)}}, like 01632 960 001, 07700 900 982, +44 808 157 0192 or +924568456136', opts),
26
+ [UK_ERROR_CODE]: JoiBase.expression('Enter {{lowerFirst(#label)}}, like 01632 960 001, 07700 900 982 or +44 808 157 0192', opts),
27
+ [INTERNATIONAL_ERROR_CODE]: JoiBase.expression('Enter {{lowerFirst(#label)}}, starting with + and the country code, for example +92333 1234567 or 00923331234567', opts)
28
+ },
29
+ rules: {
30
+ phoneNumber: {
31
+ method({
32
+ format
33
+ } = {}) {
34
+ return this.$_addRule({
35
+ name: 'phoneNumber',
36
+ args: {
37
+ format
38
+ }
39
+ });
40
+ },
41
+ args: [{
42
+ name: 'format',
43
+ ref: true,
44
+ assert: JoiBase.valid(TelephoneNumberFieldOptionsFormatEnum.International, TelephoneNumberFieldOptionsFormatEnum.UK)
45
+ }],
46
+ validate(value, {
47
+ error
48
+ }, args) {
49
+ const format = args.format;
50
+ try {
51
+ const parsed = phoneUtil.parse(value, COUNTRY);
52
+ if (!phoneUtil.isValidNumber(parsed)) {
53
+ return error(getErrorCode(format));
54
+ }
55
+ if (format) {
56
+ const isUK = isUKNumber(parsed);
57
+ if (!isUK && format === TelephoneNumberFieldOptionsFormatEnum.UK) {
58
+ return error(UK_ERROR_CODE);
59
+ } else if (isUK && format === TelephoneNumberFieldOptionsFormatEnum.International) {
60
+ return error(INTERNATIONAL_ERROR_CODE);
61
+ }
62
+ }
63
+ return value;
64
+ } catch {
65
+ return error(getErrorCode(format));
66
+ }
67
+ }
68
+ }
69
+ }
70
+ });
71
+ //# sourceMappingURL=telephone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telephone.js","names":["TelephoneNumberFieldOptionsFormatEnum","LibPhoneNumber","JoiBase","opts","phoneUtil","PhoneNumberUtil","getInstance","COUNTRY","INVALID_ERROR_CODE","UK_ERROR_CODE","INTERNATIONAL_ERROR_CODE","isUKNumber","value","isValidNumberForRegion","getErrorCode","format","UK","International","joi","extend","type","base","string","messages","expression","rules","phoneNumber","method","$_addRule","name","args","ref","assert","valid","validate","error","parsed","parse","isValidNumber","isUK"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/telephone.ts"],"sourcesContent":["import { TelephoneNumberFieldOptionsFormatEnum } from '@defra/forms-model'\nimport LibPhoneNumber from 'google-libphonenumber'\nimport JoiBase, { type JoiExpression, type LanguageMessages } from 'joi'\n\nimport { opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\n\nconst phoneUtil = LibPhoneNumber.PhoneNumberUtil.getInstance()\n\nexport const COUNTRY = 'GB'\nexport const INVALID_ERROR_CODE = 'phoneNumber.invalid'\nexport const UK_ERROR_CODE = 'phoneNumber.uk'\nexport const INTERNATIONAL_ERROR_CODE = 'phoneNumber.international'\n\nexport const isUKNumber = (value: LibPhoneNumber.PhoneNumber) => {\n return phoneUtil.isValidNumberForRegion(value, COUNTRY)\n}\n\nexport function getErrorCode(format?: TelephoneNumberFieldOptionsFormatEnum) {\n if (format === TelephoneNumberFieldOptionsFormatEnum.UK) {\n return UK_ERROR_CODE\n } else if (format === TelephoneNumberFieldOptionsFormatEnum.International) {\n return INTERNATIONAL_ERROR_CODE\n }\n\n return INVALID_ERROR_CODE\n}\n\nexport const joi = JoiBase.extend({\n type: 'string',\n base: JoiBase.string(),\n messages: {\n [INVALID_ERROR_CODE]: JoiBase.expression(\n 'Enter {{lowerFirst(#label)}}, like 01632 960 001, 07700 900 982, +44 808 157 0192 or +924568456136',\n opts\n ) as JoiExpression,\n [UK_ERROR_CODE]: JoiBase.expression(\n 'Enter {{lowerFirst(#label)}}, like 01632 960 001, 07700 900 982 or +44 808 157 0192',\n opts\n ) as JoiExpression,\n [INTERNATIONAL_ERROR_CODE]: JoiBase.expression(\n 'Enter {{lowerFirst(#label)}}, starting with + and the country code, for example +92333 1234567 or 00923331234567',\n opts\n ) as JoiExpression\n } as unknown as LanguageMessages,\n rules: {\n phoneNumber: {\n method({\n format\n }: { format?: TelephoneNumberFieldOptionsFormatEnum } = {}) {\n return this.$_addRule({\n name: 'phoneNumber',\n args: { format }\n })\n },\n args: [\n {\n name: 'format',\n ref: true,\n assert: JoiBase.valid(\n TelephoneNumberFieldOptionsFormatEnum.International,\n TelephoneNumberFieldOptionsFormatEnum.UK\n )\n }\n ],\n validate(value, { error }, args) {\n const format = args.format\n\n try {\n const parsed = phoneUtil.parse(value, COUNTRY)\n\n if (!phoneUtil.isValidNumber(parsed)) {\n return error(getErrorCode(format))\n }\n\n if (format) {\n const isUK = isUKNumber(parsed)\n\n if (!isUK && format === TelephoneNumberFieldOptionsFormatEnum.UK) {\n return error(UK_ERROR_CODE)\n } else if (\n isUK &&\n format === TelephoneNumberFieldOptionsFormatEnum.International\n ) {\n return error(INTERNATIONAL_ERROR_CODE)\n }\n }\n\n return value\n } catch {\n return error(getErrorCode(format))\n }\n }\n }\n }\n}) as JoiBase.Root\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,90 @@
1
+ import { TelephoneNumberFieldOptionsFormatEnum } from '@defra/forms-model';
2
+ import { joi } from "./telephone.js";
3
+ describe('Telephone validation helpers', () => {
4
+ describe('UK numbers', () => {
5
+ test('it should not have errors for valid telephone number', () => {
6
+ const telephoneSchema = joi.string().phoneNumber({
7
+ format: TelephoneNumberFieldOptionsFormatEnum.UK
8
+ }).label('Home phone');
9
+ const result = telephoneSchema.validate('0160676477');
10
+ expect(result.error).toBeUndefined();
11
+ expect(result.value).toBe('0160676477');
12
+ });
13
+ test('it should not have errors for valid UK telephone number in international format', () => {
14
+ const telephoneSchema = joi.string().phoneNumber({
15
+ format: TelephoneNumberFieldOptionsFormatEnum.UK
16
+ }).label('Home phone');
17
+ const result = telephoneSchema.validate('+44 1606 76477');
18
+ expect(result.error).toBeUndefined();
19
+ expect(result.value).toBe('+44 1606 76477');
20
+ });
21
+ test('it should have errors for invalid telephone number', () => {
22
+ const telephoneSchema = joi.string().phoneNumber({
23
+ format: TelephoneNumberFieldOptionsFormatEnum.UK
24
+ }).label('Home phone');
25
+ const result = telephoneSchema.validate('ABC');
26
+ expect(result.error).toBeDefined();
27
+ expect(result.error?.message).toBe('Enter home phone, like 01632 960 001, 07700 900 982 or +44 808 157 0192');
28
+ expect(result.value).toBe('ABC');
29
+ });
30
+ test('it should have errors for international telephone number', () => {
31
+ const telephoneSchema = joi.string().phoneNumber({
32
+ format: TelephoneNumberFieldOptionsFormatEnum.UK
33
+ }).label('Home phone');
34
+ const result = telephoneSchema.validate('+1-212-456-7890');
35
+ expect(result.error).toBeDefined();
36
+ expect(result.error?.message).toBe('Enter home phone, like 01632 960 001, 07700 900 982 or +44 808 157 0192');
37
+ expect(result.value).toBe('+1-212-456-7890');
38
+ });
39
+ });
40
+ describe('International numbers', () => {
41
+ test('it should not have errors for valid telephone number', () => {
42
+ const telephoneSchema = joi.string().phoneNumber({
43
+ format: TelephoneNumberFieldOptionsFormatEnum.International
44
+ }).label('Home phone');
45
+ const result = telephoneSchema.validate('+1-212-456-7890');
46
+ expect(result.error).toBeUndefined();
47
+ expect(result.value).toBe('+1-212-456-7890');
48
+ });
49
+ test('it should have errors for invalid telephone number', () => {
50
+ const telephoneSchema = joi.string().phoneNumber({
51
+ format: TelephoneNumberFieldOptionsFormatEnum.International
52
+ }).label('Home phone');
53
+ const result = telephoneSchema.validate('ABC');
54
+ expect(result.error).toBeDefined();
55
+ expect(result.error?.message).toBe('Enter home phone, starting with + and the country code, for example +92333 1234567 or 00923331234567');
56
+ expect(result.value).toBe('ABC');
57
+ });
58
+ test('it should have errors for UK phone number in international format', () => {
59
+ const telephoneSchema = joi.string().phoneNumber({
60
+ format: TelephoneNumberFieldOptionsFormatEnum.International
61
+ }).label('Home phone');
62
+ const result = telephoneSchema.validate('+44 1606 76477');
63
+ expect(result.error).toBeDefined();
64
+ expect(result.error?.message).toBe('Enter home phone, starting with + and the country code, for example +92333 1234567 or 00923331234567');
65
+ expect(result.value).toBe('+44 1606 76477');
66
+ });
67
+ });
68
+ describe('Any format', () => {
69
+ test('it should not have errors for valid national telephone number', () => {
70
+ const telephoneSchema = joi.string().phoneNumber().label('Home phone');
71
+ const result = telephoneSchema.validate('0160676477');
72
+ expect(result.error).toBeUndefined();
73
+ expect(result.value).toBe('0160676477');
74
+ });
75
+ test('it should not have errors for valid international telephone number', () => {
76
+ const telephoneSchema = joi.string().phoneNumber().label('Home phone');
77
+ const result = telephoneSchema.validate('+1-212-456-7890');
78
+ expect(result.error).toBeUndefined();
79
+ expect(result.value).toBe('+1-212-456-7890');
80
+ });
81
+ test('it should have errors for invalid telephone number', () => {
82
+ const telephoneSchema = joi.string().phoneNumber().label('Home phone');
83
+ const result = telephoneSchema.validate('ABC');
84
+ expect(result.error).toBeDefined();
85
+ expect(result.error?.message).toBe('Enter home phone, like 01632 960 001, 07700 900 982, +44 808 157 0192 or +924568456136');
86
+ expect(result.value).toBe('ABC');
87
+ });
88
+ });
89
+ });
90
+ //# sourceMappingURL=telephone.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telephone.test.js","names":["TelephoneNumberFieldOptionsFormatEnum","joi","describe","test","telephoneSchema","string","phoneNumber","format","UK","label","result","validate","expect","error","toBeUndefined","value","toBe","toBeDefined","message","International"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/telephone.test.js"],"sourcesContent":["import { TelephoneNumberFieldOptionsFormatEnum } from '@defra/forms-model'\n\nimport { joi } from '~/src/server/plugins/engine/components/helpers/telephone.js'\n\ndescribe('Telephone validation helpers', () => {\n describe('UK numbers', () => {\n test('it should not have errors for valid telephone number', () => {\n const telephoneSchema = joi\n .string()\n .phoneNumber({ format: TelephoneNumberFieldOptionsFormatEnum.UK })\n .label('Home phone')\n\n const result = telephoneSchema.validate('0160676477')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBe('0160676477')\n })\n\n test('it should not have errors for valid UK telephone number in international format', () => {\n const telephoneSchema = joi\n .string()\n .phoneNumber({ format: TelephoneNumberFieldOptionsFormatEnum.UK })\n .label('Home phone')\n\n const result = telephoneSchema.validate('+44 1606 76477')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBe('+44 1606 76477')\n })\n\n test('it should have errors for invalid telephone number', () => {\n const telephoneSchema = joi\n .string()\n .phoneNumber({ format: TelephoneNumberFieldOptionsFormatEnum.UK })\n .label('Home phone')\n\n const result = telephoneSchema.validate('ABC')\n\n expect(result.error).toBeDefined()\n expect(result.error?.message).toBe(\n 'Enter home phone, like 01632 960 001, 07700 900 982 or +44 808 157 0192'\n )\n expect(result.value).toBe('ABC')\n })\n\n test('it should have errors for international telephone number', () => {\n const telephoneSchema = joi\n .string()\n .phoneNumber({ format: TelephoneNumberFieldOptionsFormatEnum.UK })\n .label('Home phone')\n\n const result = telephoneSchema.validate('+1-212-456-7890')\n\n expect(result.error).toBeDefined()\n expect(result.error?.message).toBe(\n 'Enter home phone, like 01632 960 001, 07700 900 982 or +44 808 157 0192'\n )\n expect(result.value).toBe('+1-212-456-7890')\n })\n })\n\n describe('International numbers', () => {\n test('it should not have errors for valid telephone number', () => {\n const telephoneSchema = joi\n .string()\n .phoneNumber({\n format: TelephoneNumberFieldOptionsFormatEnum.International\n })\n .label('Home phone')\n\n const result = telephoneSchema.validate('+1-212-456-7890')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBe('+1-212-456-7890')\n })\n\n test('it should have errors for invalid telephone number', () => {\n const telephoneSchema = joi\n .string()\n .phoneNumber({\n format: TelephoneNumberFieldOptionsFormatEnum.International\n })\n .label('Home phone')\n\n const result = telephoneSchema.validate('ABC')\n\n expect(result.error).toBeDefined()\n expect(result.error?.message).toBe(\n 'Enter home phone, starting with + and the country code, for example +92333 1234567 or 00923331234567'\n )\n expect(result.value).toBe('ABC')\n })\n\n test('it should have errors for UK phone number in international format', () => {\n const telephoneSchema = joi\n .string()\n .phoneNumber({\n format: TelephoneNumberFieldOptionsFormatEnum.International\n })\n .label('Home phone')\n\n const result = telephoneSchema.validate('+44 1606 76477')\n\n expect(result.error).toBeDefined()\n expect(result.error?.message).toBe(\n 'Enter home phone, starting with + and the country code, for example +92333 1234567 or 00923331234567'\n )\n expect(result.value).toBe('+44 1606 76477')\n })\n })\n\n describe('Any format', () => {\n test('it should not have errors for valid national telephone number', () => {\n const telephoneSchema = joi.string().phoneNumber().label('Home phone')\n\n const result = telephoneSchema.validate('0160676477')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBe('0160676477')\n })\n\n test('it should not have errors for valid international telephone number', () => {\n const telephoneSchema = joi.string().phoneNumber().label('Home phone')\n\n const result = telephoneSchema.validate('+1-212-456-7890')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBe('+1-212-456-7890')\n })\n\n test('it should have errors for invalid telephone number', () => {\n const telephoneSchema = joi.string().phoneNumber().label('Home phone')\n\n const result = telephoneSchema.validate('ABC')\n\n expect(result.error).toBeDefined()\n expect(result.error?.message).toBe(\n 'Enter home phone, like 01632 960 001, 07700 900 982, +44 808 157 0192 or +924568456136'\n )\n expect(result.value).toBe('ABC')\n })\n })\n})\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,5 @@
1
- import { type JoiExpression, type LanguageMessages, type LanguageMessagesExt, type ValidationOptions } from 'joi';
1
+ import { type JoiExpression, type LanguageMessages, type LanguageMessagesExt, type ReferenceOptions, type ValidationOptions } from 'joi';
2
+ export declare const opts: ReferenceOptions;
2
3
  /**
3
4
  * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax
4
5
  */
@@ -2,7 +2,7 @@
2
2
 
3
3
  import joi from 'joi';
4
4
  import { lowerFirstPreserveProperNouns } from "../components/helpers/index.js";
5
- const opts = {
5
+ export const opts = {
6
6
  functions: {
7
7
  lowerFirst: lowerFirstPreserveProperNouns
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","arrayMin","arrayMax","arrayLength","featuresMin","featuresMax","featuresLength","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}',\n arrayMin: 'Select at least {{#limit}} options from the list',\n arrayMax: 'Only {{#limit}} can be selected from the list',\n arrayLength: 'Select only {{#limit}} options from the list',\n featuresMin: 'Define at least {{#limit}} features',\n featuresMax: 'Only {{#limit}} features can be defined',\n featuresLength: 'Define exactly {{#limit}} features'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","arrayMin","arrayMax","arrayLength","featuresMin","featuresMax","featuresLength","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nexport const opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}',\n arrayMin: 'Select at least {{#limit}} options from the list',\n arrayMax: 'Only {{#limit}} can be selected from the list',\n arrayLength: 'Select only {{#limit}} options from the list',\n featuresMin: 'Define at least {{#limit}} features',\n featuresMax: 'Only {{#limit}} features can be defined',\n featuresLength: 'Define exactly {{#limit}} features'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.js","names":[],"sources":["../../../src/typings/joi/index.d.ts"],"sourcesContent":["import { type FormPayload } from '~/src/server/plugins/engine/types.ts'\n\ndeclare module 'joi' {\n interface Context {\n present?: string[]\n presentWithLabels?: string[]\n missing?: string[]\n missingWithLabels?: string[]\n }\n\n /**\n * Add context types for `object.and` error reports\n * {@link https://joi.dev/api/?v=17.13.3#objectand}\n */\n interface ErrorReportCollection extends ErrorReport {\n local: Context & {\n value: FormPayload\n label?: string\n title?: string\n }\n }\n\n interface JoiExpressionReturn {\n render: (p1, p2, p3, p4, p5) => string\n }\n\n type JoiExpression = JoiExpressionReturn | string\n\n type LanguageMessagesExt = Record<string, JoiExpression>\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"index.d.js","names":[],"sources":["../../../src/typings/joi/index.d.ts"],"sourcesContent":["import { type FormPayload } from '~/src/server/plugins/engine/types.ts'\n\ndeclare module 'joi' {\n interface Context {\n present?: string[]\n presentWithLabels?: string[]\n missing?: string[]\n missingWithLabels?: string[]\n }\n\n /**\n * Add context types for `object.and` error reports\n * {@link https://joi.dev/api/?v=17.13.3#objectand}\n */\n interface ErrorReportCollection extends ErrorReport {\n local: Context & {\n value: FormPayload\n label?: string\n title?: string\n }\n }\n\n interface JoiExpressionReturn {\n render: (p1, p2, p3, p4, p5) => string\n }\n\n type JoiExpression = JoiExpressionReturn | string\n\n type LanguageMessagesExt = Record<string, JoiExpression>\n\n interface PhoneNumberOptions {\n format?: 'international' | 'uk' | undefined\n }\n\n interface StringSchema {\n phoneNumber(options?: PhoneNumberOptions): this\n }\n}\n"],"mappings":"","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "4.14.2",
3
+ "version": "4.14.3",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -87,7 +87,7 @@
87
87
  },
88
88
  "license": "SEE LICENSE IN LICENSE",
89
89
  "dependencies": {
90
- "@defra/forms-model": "^3.0.668",
90
+ "@defra/forms-model": "^3.0.674",
91
91
  "@defra/hapi-tracing": "^1.29.0",
92
92
  "@defra/interactive-map": "^0.0.22-alpha",
93
93
  "@elastic/ecs-pino-format": "^1.5.0",
@@ -119,6 +119,7 @@
119
119
  "dotenv": "^17.2.3",
120
120
  "expr-eval-fork": "^3.0.0",
121
121
  "geodesy": "^2.4.0",
122
+ "google-libphonenumber": "^3.2.44",
122
123
  "govuk-frontend": "^5.13.0",
123
124
  "hapi-pino": "^13.0.0",
124
125
  "hapi-pulse": "^3.0.1",
@@ -157,6 +158,7 @@
157
158
  "@types/btoa": "^1.2.5",
158
159
  "@types/convict": "^6.1.6",
159
160
  "@types/eslint": "^9.6.1",
161
+ "@types/google-libphonenumber": "^7.4.30",
160
162
  "@types/govuk-frontend": "^5.11.0",
161
163
  "@types/hapi": "^18.0.15",
162
164
  "@types/hapi__catbox-memory": "^6.0.2",
@@ -1,8 +1,14 @@
1
1
  import { type TelephoneNumberFieldComponent } from '@defra/forms-model'
2
- import joi, { type StringSchema } from 'joi'
2
+ import { type StringSchema } from 'joi'
3
3
 
4
4
  import { FormComponent } from './FormComponent.js'
5
5
  import { addClassOptionIfNone } from './helpers/index.js'
6
+ import {
7
+ INTERNATIONAL_ERROR_CODE,
8
+ INVALID_ERROR_CODE,
9
+ UK_ERROR_CODE,
10
+ joi
11
+ } from './helpers/telephone.js'
6
12
  import { messageTemplate } from '../pageControllers/validationOptions.js'
7
13
  import {
8
14
  type ErrorMessageTemplateList,
@@ -10,8 +16,6 @@ import {
10
16
  type FormSubmissionError
11
17
  } from '../types.js'
12
18
 
13
- const PATTERN = /^[0-9\\\s+()-]*$/
14
-
15
19
  export class TelephoneNumberField extends FormComponent {
16
20
  declare options: TelephoneNumberFieldComponent['options']
17
21
  declare formSchema: StringSchema
@@ -24,11 +28,12 @@ export class TelephoneNumberField extends FormComponent {
24
28
  super(def, props)
25
29
 
26
30
  const { options } = def
31
+ const { format } = options
27
32
 
28
33
  let formSchema = joi
29
34
  .string()
30
35
  .trim()
31
- .pattern(PATTERN)
36
+ .phoneNumber({ format })
32
37
  .label(this.label)
33
38
  .required()
34
39
 
@@ -42,7 +47,10 @@ export class TelephoneNumberField extends FormComponent {
42
47
  formSchema = formSchema.messages({
43
48
  'any.required': message,
44
49
  'string.empty': message,
45
- 'string.pattern.base': message
50
+ 'string.pattern.base': message,
51
+ [INVALID_ERROR_CODE]: message,
52
+ [UK_ERROR_CODE]: message,
53
+ [INTERNATIONAL_ERROR_CODE]: message
46
54
  })
47
55
  } else if (options.customValidationMessages) {
48
56
  formSchema = formSchema.messages(options.customValidationMessages)
@@ -171,7 +171,3 @@ export function getGeospatialSchema(
171
171
  def
172
172
  )
173
173
  }
174
-
175
- /**
176
- * @import { CustomHelpers } from 'joi'
177
- */
@@ -0,0 +1,95 @@
1
+ import { TelephoneNumberFieldOptionsFormatEnum } from '@defra/forms-model'
2
+ import LibPhoneNumber from 'google-libphonenumber'
3
+ import JoiBase, { type JoiExpression, type LanguageMessages } from 'joi'
4
+
5
+ import { opts } from '../../pageControllers/validationOptions.js'
6
+
7
+ const phoneUtil = LibPhoneNumber.PhoneNumberUtil.getInstance()
8
+
9
+ export const COUNTRY = 'GB'
10
+ export const INVALID_ERROR_CODE = 'phoneNumber.invalid'
11
+ export const UK_ERROR_CODE = 'phoneNumber.uk'
12
+ export const INTERNATIONAL_ERROR_CODE = 'phoneNumber.international'
13
+
14
+ export const isUKNumber = (value: LibPhoneNumber.PhoneNumber) => {
15
+ return phoneUtil.isValidNumberForRegion(value, COUNTRY)
16
+ }
17
+
18
+ export function getErrorCode(format?: TelephoneNumberFieldOptionsFormatEnum) {
19
+ if (format === TelephoneNumberFieldOptionsFormatEnum.UK) {
20
+ return UK_ERROR_CODE
21
+ } else if (format === TelephoneNumberFieldOptionsFormatEnum.International) {
22
+ return INTERNATIONAL_ERROR_CODE
23
+ }
24
+
25
+ return INVALID_ERROR_CODE
26
+ }
27
+
28
+ export const joi = JoiBase.extend({
29
+ type: 'string',
30
+ base: JoiBase.string(),
31
+ messages: {
32
+ [INVALID_ERROR_CODE]: JoiBase.expression(
33
+ 'Enter {{lowerFirst(#label)}}, like 01632 960 001, 07700 900 982, +44 808 157 0192 or +924568456136',
34
+ opts
35
+ ) as JoiExpression,
36
+ [UK_ERROR_CODE]: JoiBase.expression(
37
+ 'Enter {{lowerFirst(#label)}}, like 01632 960 001, 07700 900 982 or +44 808 157 0192',
38
+ opts
39
+ ) as JoiExpression,
40
+ [INTERNATIONAL_ERROR_CODE]: JoiBase.expression(
41
+ 'Enter {{lowerFirst(#label)}}, starting with + and the country code, for example +92333 1234567 or 00923331234567',
42
+ opts
43
+ ) as JoiExpression
44
+ } as unknown as LanguageMessages,
45
+ rules: {
46
+ phoneNumber: {
47
+ method({
48
+ format
49
+ }: { format?: TelephoneNumberFieldOptionsFormatEnum } = {}) {
50
+ return this.$_addRule({
51
+ name: 'phoneNumber',
52
+ args: { format }
53
+ })
54
+ },
55
+ args: [
56
+ {
57
+ name: 'format',
58
+ ref: true,
59
+ assert: JoiBase.valid(
60
+ TelephoneNumberFieldOptionsFormatEnum.International,
61
+ TelephoneNumberFieldOptionsFormatEnum.UK
62
+ )
63
+ }
64
+ ],
65
+ validate(value, { error }, args) {
66
+ const format = args.format
67
+
68
+ try {
69
+ const parsed = phoneUtil.parse(value, COUNTRY)
70
+
71
+ if (!phoneUtil.isValidNumber(parsed)) {
72
+ return error(getErrorCode(format))
73
+ }
74
+
75
+ if (format) {
76
+ const isUK = isUKNumber(parsed)
77
+
78
+ if (!isUK && format === TelephoneNumberFieldOptionsFormatEnum.UK) {
79
+ return error(UK_ERROR_CODE)
80
+ } else if (
81
+ isUK &&
82
+ format === TelephoneNumberFieldOptionsFormatEnum.International
83
+ ) {
84
+ return error(INTERNATIONAL_ERROR_CODE)
85
+ }
86
+ }
87
+
88
+ return value
89
+ } catch {
90
+ return error(getErrorCode(format))
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }) as JoiBase.Root
@@ -10,7 +10,7 @@ import joi, {
10
10
 
11
11
  import { lowerFirstPreserveProperNouns } from '../components/helpers/index.js'
12
12
 
13
- const opts = {
13
+ export const opts = {
14
14
  functions: {
15
15
  lowerFirst: lowerFirstPreserveProperNouns
16
16
  }
@@ -27,4 +27,12 @@ declare module 'joi' {
27
27
  type JoiExpression = JoiExpressionReturn | string
28
28
 
29
29
  type LanguageMessagesExt = Record<string, JoiExpression>
30
+
31
+ interface PhoneNumberOptions {
32
+ format?: 'international' | 'uk' | undefined
33
+ }
34
+
35
+ interface StringSchema {
36
+ phoneNumber(options?: PhoneNumberOptions): this
37
+ }
30
38
  }