@defra/forms-engine-plugin 5.0.0-alpha.3 → 5.0.0-alpha.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. package/.public/37c964ca545c240f010a.mjs +2 -0
  2. package/.public/37c964ca545c240f010a.mjs.LICENSE.txt +4 -0
  3. package/.public/assets-manifest.json +3 -0
  4. package/.public/javascripts/application.min.js +1 -1
  5. package/.public/javascripts/application.min.js.LICENSE.txt +7 -0
  6. package/.public/javascripts/application.min.js.map +1 -1
  7. package/.public/javascripts/shared.min.js +1 -1
  8. package/.public/javascripts/shared.min.js.LICENSE.txt +9 -0
  9. package/.public/javascripts/shared.min.js.map +1 -1
  10. package/.public/javascripts/vendor/@defra.min.js +1 -1
  11. package/.public/javascripts/vendor/@defra.min.js.map +1 -1
  12. package/.public/javascripts/vendor/maplibre-gl.min.js +1 -1
  13. package/.public/javascripts/vendor/maplibre-gl.min.js.LICENSE.txt +3 -3
  14. package/.public/javascripts/vendor/maplibre-gl.min.js.map +1 -1
  15. package/.public/stylesheets/application.min.css +1 -1
  16. package/.public/stylesheets/application.min.css.map +1 -1
  17. package/.server/client/javascripts/file-upload.js +6 -3
  18. package/.server/client/javascripts/file-upload.js.map +1 -1
  19. package/.server/client/javascripts/geospatial-map.d.ts +159 -5
  20. package/.server/client/javascripts/geospatial-map.js +161 -81
  21. package/.server/client/javascripts/geospatial-map.js.map +1 -1
  22. package/.server/client/javascripts/geospatial-map.test.js +152 -0
  23. package/.server/client/javascripts/geospatial-map.test.js.map +1 -0
  24. package/.server/client/javascripts/govuk.js +2 -1
  25. package/.server/client/javascripts/govuk.js.map +1 -1
  26. package/.server/client/javascripts/location-map.d.ts +44 -0
  27. package/.server/client/javascripts/location-map.js +55 -6
  28. package/.server/client/javascripts/location-map.js.map +1 -1
  29. package/.server/client/javascripts/map-constants.d.ts +3 -0
  30. package/.server/client/javascripts/map-constants.js +4 -0
  31. package/.server/client/javascripts/map-constants.js.map +1 -0
  32. package/.server/client/javascripts/map.d.ts +17 -4
  33. package/.server/client/javascripts/map.js +59 -6
  34. package/.server/client/javascripts/map.js.map +1 -1
  35. package/.server/client/javascripts/maps.d.ts +5 -0
  36. package/.server/client/javascripts/maps.js +6 -0
  37. package/.server/client/javascripts/maps.js.map +1 -0
  38. package/.server/client/javascripts/shared.d.ts +1 -4
  39. package/.server/client/javascripts/shared.js +1 -4
  40. package/.server/client/javascripts/shared.js.map +1 -1
  41. package/.server/client/javascripts/sssi-dataset.d.ts +18 -20
  42. package/.server/client/javascripts/sssi-dataset.js +37 -39
  43. package/.server/client/javascripts/sssi-dataset.js.map +1 -1
  44. package/.server/client/stylesheets/maps.scss +4 -0
  45. package/.server/client/stylesheets/shared.scss +1 -5
  46. package/.server/server/constants.d.ts +2 -0
  47. package/.server/server/constants.js +2 -0
  48. package/.server/server/constants.js.map +1 -1
  49. package/.server/server/forms/components.json +8 -0
  50. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +4 -0
  51. package/.server/server/plugins/engine/__stubs__/definitions.d.ts +29 -0
  52. package/.server/server/plugins/engine/__stubs__/definitions.js +125 -0
  53. package/.server/server/plugins/engine/__stubs__/definitions.js.map +1 -0
  54. package/.server/server/plugins/engine/components/AutocompleteField.d.ts +8 -3
  55. package/.server/server/plugins/engine/components/AutocompleteField.js +17 -0
  56. package/.server/server/plugins/engine/components/AutocompleteField.js.map +1 -1
  57. package/.server/server/plugins/engine/components/CheckboxesField.d.ts +23 -2
  58. package/.server/server/plugins/engine/components/CheckboxesField.js +30 -5
  59. package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
  60. package/.server/server/plugins/engine/components/ComponentBase.d.ts +2 -1
  61. package/.server/server/plugins/engine/components/ComponentBase.js +2 -0
  62. package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -1
  63. package/.server/server/plugins/engine/components/ComponentCollection.js +12 -7
  64. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
  65. package/.server/server/plugins/engine/components/DatePartsField.d.ts +6 -3
  66. package/.server/server/plugins/engine/components/DatePartsField.js +12 -7
  67. package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
  68. package/.server/server/plugins/engine/components/DeclarationField.d.ts +6 -3
  69. package/.server/server/plugins/engine/components/DeclarationField.js +6 -4
  70. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  71. package/.server/server/plugins/engine/components/Details.d.ts +1 -0
  72. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +33 -2
  73. package/.server/server/plugins/engine/components/EastingNorthingField.js +110 -54
  74. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  75. package/.server/server/plugins/engine/components/EmailAddressField.d.ts +4 -1
  76. package/.server/server/plugins/engine/components/EmailAddressField.js +2 -1
  77. package/.server/server/plugins/engine/components/EmailAddressField.js.map +1 -1
  78. package/.server/server/plugins/engine/components/FileUploadField.d.ts +23 -2
  79. package/.server/server/plugins/engine/components/FileUploadField.js +35 -3
  80. package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
  81. package/.server/server/plugins/engine/components/FormComponent.d.ts +5 -1
  82. package/.server/server/plugins/engine/components/FormComponent.js +5 -2
  83. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  84. package/.server/server/plugins/engine/components/GeospatialField.d.ts +23 -0
  85. package/.server/server/plugins/engine/components/GeospatialField.js +33 -9
  86. package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
  87. package/.server/server/plugins/engine/components/Html.d.ts +1 -0
  88. package/.server/server/plugins/engine/components/InsetText.d.ts +1 -0
  89. package/.server/server/plugins/engine/components/LatLongField.d.ts +32 -2
  90. package/.server/server/plugins/engine/components/LatLongField.js +85 -48
  91. package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
  92. package/.server/server/plugins/engine/components/List.d.ts +1 -0
  93. package/.server/server/plugins/engine/components/ListFormComponent.d.ts +3 -2
  94. package/.server/server/plugins/engine/components/ListFormComponent.js.map +1 -1
  95. package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +8 -2
  96. package/.server/server/plugins/engine/components/LocationFieldBase.js +31 -7
  97. package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
  98. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +12 -3
  99. package/.server/server/plugins/engine/components/LocationFieldHelpers.js +21 -13
  100. package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
  101. package/.server/server/plugins/engine/components/Markdown.d.ts +1 -0
  102. package/.server/server/plugins/engine/components/Markdown.js +1 -1
  103. package/.server/server/plugins/engine/components/Markdown.js.map +1 -1
  104. package/.server/server/plugins/engine/components/MonthYearField.d.ts +5 -2
  105. package/.server/server/plugins/engine/components/MonthYearField.js +13 -9
  106. package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
  107. package/.server/server/plugins/engine/components/MultilineTextField.d.ts +1 -0
  108. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +2 -1
  109. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +4 -3
  110. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -1
  111. package/.server/server/plugins/engine/components/NotificationBanner.d.ts +1 -0
  112. package/.server/server/plugins/engine/components/NumberField.d.ts +5 -0
  113. package/.server/server/plugins/engine/components/NumberField.js +22 -0
  114. package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
  115. package/.server/server/plugins/engine/components/OsGridRefField.d.ts +103 -1
  116. package/.server/server/plugins/engine/components/OsGridRefField.js +13 -3
  117. package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -1
  118. package/.server/server/plugins/engine/components/PaymentField.d.ts +2 -0
  119. package/.server/server/plugins/engine/components/PaymentField.js +9 -5
  120. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
  121. package/.server/server/plugins/engine/components/SelectField.d.ts +1 -0
  122. package/.server/server/plugins/engine/components/SelectionControlField.d.ts +1 -0
  123. package/.server/server/plugins/engine/components/TelephoneNumberField.d.ts +1 -0
  124. package/.server/server/plugins/engine/components/TrackingField.d.ts +21 -0
  125. package/.server/server/plugins/engine/components/TrackingField.js +46 -0
  126. package/.server/server/plugins/engine/components/TrackingField.js.map +1 -0
  127. package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -0
  128. package/.server/server/plugins/engine/components/UkAddressField.js +23 -8
  129. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  130. package/.server/server/plugins/engine/components/YesNoField.d.ts +3 -2
  131. package/.server/server/plugins/engine/components/YesNoField.js +22 -8
  132. package/.server/server/plugins/engine/components/YesNoField.js.map +1 -1
  133. package/.server/server/plugins/engine/components/helpers/components.d.ts +1 -1
  134. package/.server/server/plugins/engine/components/helpers/components.js +6 -3
  135. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  136. package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +8 -1
  137. package/.server/server/plugins/engine/components/helpers/geospatial.js +58 -17
  138. package/.server/server/plugins/engine/components/helpers/geospatial.js.map +1 -1
  139. package/.server/server/plugins/engine/components/helpers/index.d.ts +21 -1
  140. package/.server/server/plugins/engine/components/helpers/index.js +45 -1
  141. package/.server/server/plugins/engine/components/helpers/index.js.map +1 -1
  142. package/.server/server/plugins/engine/components/index.d.ts +1 -0
  143. package/.server/server/plugins/engine/components/index.js +1 -0
  144. package/.server/server/plugins/engine/components/index.js.map +1 -1
  145. package/.server/server/plugins/engine/components/types.d.ts +1 -0
  146. package/.server/server/plugins/engine/components/types.js.map +1 -1
  147. package/.server/server/plugins/engine/errors.d.ts +49 -0
  148. package/.server/server/plugins/engine/errors.js +75 -0
  149. package/.server/server/plugins/engine/errors.js.map +1 -0
  150. package/.server/server/plugins/engine/form-availability.d.ts +3 -2
  151. package/.server/server/plugins/engine/form-availability.js +3 -2
  152. package/.server/server/plugins/engine/form-availability.js.map +1 -1
  153. package/.server/server/plugins/engine/form-context.js +2 -2
  154. package/.server/server/plugins/engine/form-context.js.map +1 -1
  155. package/.server/server/plugins/engine/helpers.d.ts +23 -2
  156. package/.server/server/plugins/engine/helpers.js +44 -5
  157. package/.server/server/plugins/engine/helpers.js.map +1 -1
  158. package/.server/server/plugins/engine/i18n/buildValidationMessages.d.ts +4 -1
  159. package/.server/server/plugins/engine/i18n/buildValidationMessages.js +31 -7
  160. package/.server/server/plugins/engine/i18n/buildValidationMessages.js.map +1 -1
  161. package/.server/server/plugins/engine/i18n/createFormTranslator.d.ts +4 -4
  162. package/.server/server/plugins/engine/i18n/createFormTranslator.js +27 -5
  163. package/.server/server/plugins/engine/i18n/createFormTranslator.js.map +1 -1
  164. package/.server/server/plugins/engine/i18n/extractBaseTranslations.d.ts +1 -1
  165. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js +9 -0
  166. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js.map +1 -1
  167. package/.server/server/plugins/engine/i18n/form.d.ts +13 -0
  168. package/.server/server/plugins/engine/i18n/form.js +35 -0
  169. package/.server/server/plugins/engine/i18n/form.js.map +1 -0
  170. package/.server/server/plugins/engine/i18n/languages.d.ts +10 -0
  171. package/.server/server/plugins/engine/i18n/languages.js +19 -0
  172. package/.server/server/plugins/engine/i18n/languages.js.map +1 -0
  173. package/.server/server/plugins/engine/i18n/translations/cy.json +176 -7
  174. package/.server/server/plugins/engine/i18n/translations/en-GB.json +110 -6
  175. package/.server/server/plugins/engine/i18n/types.d.ts +2 -0
  176. package/.server/server/plugins/engine/i18n/types.js.map +1 -1
  177. package/.server/server/plugins/engine/index.d.ts +4 -0
  178. package/.server/server/plugins/engine/index.js +4 -0
  179. package/.server/server/plugins/engine/index.js.map +1 -1
  180. package/.server/server/plugins/engine/models/FormModel.d.ts +0 -4
  181. package/.server/server/plugins/engine/models/FormModel.js +35 -23
  182. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  183. package/.server/server/plugins/engine/models/SummaryViewModel.js +9 -4
  184. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  185. package/.server/server/plugins/engine/models/types.d.ts +28 -1
  186. package/.server/server/plugins/engine/models/types.js.map +1 -1
  187. package/.server/server/plugins/engine/models/unavailable-view-model.d.ts +11 -2
  188. package/.server/server/plugins/engine/models/unavailable-view-model.js +23 -4
  189. package/.server/server/plugins/engine/models/unavailable-view-model.js.map +1 -1
  190. package/.server/server/plugins/engine/options.js +2 -1
  191. package/.server/server/plugins/engine/options.js.map +1 -1
  192. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +14 -6
  193. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  194. package/.server/server/plugins/engine/outputFormatters/human/v1.js +2 -1
  195. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  196. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +2 -0
  197. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +2 -0
  198. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  199. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +7 -1
  200. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +56 -10
  201. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  202. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -1
  203. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +2 -2
  204. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  205. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +3 -4
  206. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  207. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +10 -4
  208. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  209. package/.server/server/plugins/engine/pageControllers/helpers/pages.js +2 -1
  210. package/.server/server/plugins/engine/pageControllers/helpers/pages.js.map +1 -1
  211. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +35 -2
  212. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +100 -3
  213. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -1
  214. package/.server/server/plugins/engine/pageControllers/validationOptions.d.ts +1 -1
  215. package/.server/server/plugins/engine/pageControllers/validationOptions.js +24 -3
  216. package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
  217. package/.server/server/plugins/engine/plugin.js +3 -0
  218. package/.server/server/plugins/engine/plugin.js.map +1 -1
  219. package/.server/server/plugins/engine/referenceNumbers.d.ts +1 -0
  220. package/.server/server/plugins/engine/referenceNumbers.js +1 -0
  221. package/.server/server/plugins/engine/referenceNumbers.js.map +1 -1
  222. package/.server/server/plugins/engine/routes/index.js +9 -4
  223. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  224. package/.server/server/plugins/engine/types/enums.d.ts +3 -3
  225. package/.server/server/plugins/engine/types/enums.js +5 -4
  226. package/.server/server/plugins/engine/types/enums.js.map +1 -1
  227. package/.server/server/plugins/engine/types/schema.js +15 -4
  228. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  229. package/.server/server/plugins/engine/types.d.ts +24 -2
  230. package/.server/server/plugins/engine/types.js.map +1 -1
  231. package/.server/server/plugins/engine/unavailable-response.js +5 -2
  232. package/.server/server/plugins/engine/unavailable-response.js.map +1 -1
  233. package/.server/server/plugins/engine/views/components/_location-field-base.html +4 -2
  234. package/.server/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  235. package/.server/server/plugins/engine/views/components/geospatialfield.html +1 -0
  236. package/.server/server/plugins/engine/views/components/osgridreffield.html +4 -1
  237. package/.server/server/plugins/engine/views/components/trackingfield.html +3 -0
  238. package/.server/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  239. package/.server/server/plugins/engine/views/file-upload.html +1 -1
  240. package/.server/server/plugins/engine/views/summary.html +1 -0
  241. package/.server/server/plugins/engine/views/unavailable.html +14 -11
  242. package/.server/server/plugins/map/routes/index.d.ts +4 -0
  243. package/.server/server/plugins/map/routes/index.js +12 -4
  244. package/.server/server/plugins/map/routes/index.js.map +1 -1
  245. package/.server/server/plugins/map/routes/index.test.js +7 -0
  246. package/.server/server/plugins/map/routes/index.test.js.map +1 -0
  247. package/.server/server/plugins/map/routes/vts/README.md +38 -0
  248. package/.server/server/plugins/map/routes/vts/countries-5dp.geojson +4 -0
  249. package/.server/server/plugins/map/routes/vts/countries.geojson +10 -39283
  250. package/.server/server/plugins/map/types.d.ts +24 -2
  251. package/.server/server/plugins/map/types.js +9 -2
  252. package/.server/server/plugins/map/types.js.map +1 -1
  253. package/.server/server/plugins/nunjucks/enviroment.test.js +11 -4
  254. package/.server/server/plugins/nunjucks/enviroment.test.js.map +1 -1
  255. package/.server/server/plugins/nunjucks/filters/applyUrlParam.d.ts +9 -0
  256. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  257. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js.map +1 -0
  258. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js +17 -0
  259. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js.map +1 -0
  260. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  261. package/.server/server/plugins/nunjucks/filters/index.d.ts +1 -0
  262. package/.server/server/plugins/nunjucks/filters/index.js +1 -0
  263. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -1
  264. package/.server/server/plugins/payment/helper.d.ts +11 -1
  265. package/.server/server/plugins/payment/helper.js +18 -2
  266. package/.server/server/plugins/payment/helper.js.map +1 -1
  267. package/.server/server/plugins/payment/helper.test.js +2 -1
  268. package/.server/server/plugins/payment/helper.test.js.map +1 -1
  269. package/.server/server/plugins/payment/service.d.ts +2 -1
  270. package/.server/server/plugins/payment/service.js +5 -2
  271. package/.server/server/plugins/payment/service.js.map +1 -1
  272. package/.server/server/plugins/payment/service.test.js +7 -3
  273. package/.server/server/plugins/payment/service.test.js.map +1 -1
  274. package/.server/server/plugins/payment/types.d.ts +4 -0
  275. package/.server/server/plugins/payment/types.js +1 -0
  276. package/.server/server/plugins/payment/types.js.map +1 -1
  277. package/.server/server/plugins/postcode-lookup/models/index.d.ts +249 -3
  278. package/.server/server/plugins/postcode-lookup/models/index.js +103 -36
  279. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -1
  280. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +15 -1
  281. package/.server/server/plugins/postcode-lookup/routes/index.js +64 -18
  282. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -1
  283. package/.server/server/plugins/postcode-lookup/routes/index.test.js +84 -0
  284. package/.server/server/plugins/postcode-lookup/routes/index.test.js.map +1 -0
  285. package/.server/server/plugins/postcode-lookup/types.d.ts +12 -0
  286. package/.server/server/plugins/postcode-lookup/types.js +6 -1
  287. package/.server/server/plugins/postcode-lookup/types.js.map +1 -1
  288. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  289. package/.server/server/utils/type-utils.d.ts +1 -2
  290. package/.server/server/utils/type-utils.js +0 -3
  291. package/.server/server/utils/type-utils.js.map +1 -1
  292. package/.server/server/utils/utils.d.ts +6 -0
  293. package/.server/server/utils/utils.js +18 -1
  294. package/.server/server/utils/utils.js.map +1 -1
  295. package/.server/server/utils/utils.test.js +21 -1
  296. package/.server/server/utils/utils.test.js.map +1 -1
  297. package/.server/typings/geodesy/index.d.js +2 -0
  298. package/.server/typings/geodesy/index.d.js.map +1 -0
  299. package/.server/typings/hapi/index.d.js.map +1 -1
  300. package/package.json +24 -9
  301. package/src/client/javascripts/file-upload.js +11 -3
  302. package/src/client/javascripts/geospatial-map.js +181 -86
  303. package/src/client/javascripts/govuk.js +2 -0
  304. package/src/client/javascripts/location-map.js +62 -6
  305. package/src/client/javascripts/map-constants.js +3 -0
  306. package/src/client/javascripts/map.js +66 -9
  307. package/src/client/javascripts/maps.js +5 -0
  308. package/src/client/javascripts/shared.js +1 -4
  309. package/src/client/javascripts/sssi-dataset.js +39 -41
  310. package/src/client/stylesheets/maps.scss +4 -0
  311. package/src/client/stylesheets/shared.scss +1 -5
  312. package/src/server/constants.js +2 -0
  313. package/src/server/forms/components.json +8 -0
  314. package/src/server/forms/register-as-a-unicorn-breeder.yaml +4 -0
  315. package/src/server/plugins/engine/__stubs__/definitions.ts +155 -0
  316. package/src/server/plugins/engine/components/AutocompleteField.ts +20 -0
  317. package/src/server/plugins/engine/components/CheckboxesField.ts +41 -8
  318. package/src/server/plugins/engine/components/ComponentBase.ts +6 -4
  319. package/src/server/plugins/engine/components/ComponentCollection.ts +17 -15
  320. package/src/server/plugins/engine/components/DatePartsField.ts +20 -9
  321. package/src/server/plugins/engine/components/DeclarationField.ts +10 -10
  322. package/src/server/plugins/engine/components/EastingNorthingField.ts +185 -89
  323. package/src/server/plugins/engine/components/EmailAddressField.ts +2 -1
  324. package/src/server/plugins/engine/components/FileUploadField.ts +46 -5
  325. package/src/server/plugins/engine/components/FormComponent.ts +6 -5
  326. package/src/server/plugins/engine/components/GeospatialField.ts +47 -9
  327. package/src/server/plugins/engine/components/LatLongField.ts +128 -58
  328. package/src/server/plugins/engine/components/ListFormComponent.ts +4 -4
  329. package/src/server/plugins/engine/components/LocationFieldBase.ts +41 -8
  330. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +28 -14
  331. package/src/server/plugins/engine/components/Markdown.ts +2 -3
  332. package/src/server/plugins/engine/components/MonthYearField.ts +20 -15
  333. package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +9 -3
  334. package/src/server/plugins/engine/components/NumberField.ts +24 -0
  335. package/src/server/plugins/engine/components/OsGridRefField.ts +22 -3
  336. package/src/server/plugins/engine/components/PaymentField.ts +9 -4
  337. package/src/server/plugins/engine/components/TrackingField.ts +64 -0
  338. package/src/server/plugins/engine/components/UkAddressField.ts +30 -7
  339. package/src/server/plugins/engine/components/YesNoField.ts +27 -9
  340. package/src/server/plugins/engine/components/helpers/components.ts +8 -3
  341. package/src/server/plugins/engine/components/helpers/geospatial.ts +87 -24
  342. package/src/server/plugins/engine/components/helpers/index.ts +64 -1
  343. package/src/server/plugins/engine/components/index.ts +1 -0
  344. package/src/server/plugins/engine/components/types.ts +1 -0
  345. package/src/server/plugins/engine/errors.ts +78 -0
  346. package/src/server/plugins/engine/form-availability.ts +8 -2
  347. package/src/server/plugins/engine/form-context.ts +4 -2
  348. package/src/server/plugins/engine/helpers.ts +52 -5
  349. package/src/server/plugins/engine/i18n/buildValidationMessages.ts +16 -8
  350. package/src/server/plugins/engine/i18n/createFormTranslator.ts +43 -12
  351. package/src/server/plugins/engine/i18n/extractBaseTranslations.ts +7 -1
  352. package/src/server/plugins/engine/i18n/form.ts +54 -0
  353. package/src/server/plugins/engine/i18n/languages.ts +19 -0
  354. package/src/server/plugins/engine/i18n/translations/cy.json +176 -7
  355. package/src/server/plugins/engine/i18n/translations/en-GB.json +110 -6
  356. package/src/server/plugins/engine/i18n/types.ts +2 -0
  357. package/src/server/plugins/engine/index.ts +7 -0
  358. package/src/server/plugins/engine/models/FormModel.ts +46 -23
  359. package/src/server/plugins/engine/models/SummaryViewModel.ts +6 -5
  360. package/src/server/plugins/engine/models/types.ts +31 -0
  361. package/src/server/plugins/engine/models/unavailable-view-model.ts +26 -5
  362. package/src/server/plugins/engine/options.js +2 -1
  363. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +18 -7
  364. package/src/server/plugins/engine/outputFormatters/human/v1.ts +2 -1
  365. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +3 -0
  366. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +60 -9
  367. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +2 -2
  368. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +2 -2
  369. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -4
  370. package/src/server/plugins/engine/pageControllers/helpers/pages.ts +2 -1
  371. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +143 -4
  372. package/src/server/plugins/engine/pageControllers/validationOptions.ts +29 -4
  373. package/src/server/plugins/engine/plugin.ts +6 -0
  374. package/src/server/plugins/engine/referenceNumbers.ts +2 -0
  375. package/src/server/plugins/engine/routes/index.ts +9 -3
  376. package/src/server/plugins/engine/types/enums.ts +4 -3
  377. package/src/server/plugins/engine/types/schema.ts +22 -5
  378. package/src/server/plugins/engine/types.ts +28 -1
  379. package/src/server/plugins/engine/unavailable-response.ts +6 -2
  380. package/src/server/plugins/engine/views/components/_location-field-base.html +4 -2
  381. package/src/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  382. package/src/server/plugins/engine/views/components/geospatialfield.html +1 -0
  383. package/src/server/plugins/engine/views/components/osgridreffield.html +4 -1
  384. package/src/server/plugins/engine/views/components/trackingfield.html +3 -0
  385. package/src/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  386. package/src/server/plugins/engine/views/file-upload.html +1 -1
  387. package/src/server/plugins/engine/views/summary.html +1 -0
  388. package/src/server/plugins/engine/views/unavailable.html +14 -11
  389. package/src/server/plugins/map/routes/index.js +29 -4
  390. package/src/server/plugins/map/routes/vts/README.md +38 -0
  391. package/src/server/plugins/map/routes/vts/countries-5dp.geojson +4 -0
  392. package/src/server/plugins/map/routes/vts/countries.geojson +10 -39283
  393. package/src/server/plugins/map/types.js +9 -2
  394. package/src/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  395. package/src/server/plugins/nunjucks/filters/index.js +1 -0
  396. package/src/server/plugins/payment/helper.js +22 -2
  397. package/src/server/plugins/payment/service.js +6 -2
  398. package/src/server/plugins/payment/types.js +1 -0
  399. package/src/server/plugins/postcode-lookup/models/index.js +91 -46
  400. package/src/server/plugins/postcode-lookup/routes/index.js +96 -24
  401. package/src/server/plugins/postcode-lookup/types.js +6 -1
  402. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  403. package/src/server/utils/type-utils.ts +1 -11
  404. package/src/server/utils/utils.js +19 -1
  405. package/src/typings/geodesy/index.d.ts +18 -0
  406. package/src/typings/hapi/index.d.ts +2 -0
@@ -21,11 +21,18 @@
21
21
  * @property {string} query - name query
22
22
  */
23
23
 
24
+ export const CountryMap = {
25
+ england: 1,
26
+ 'northern-ireland': 2,
27
+ scotland: 3,
28
+ wales: 4
29
+ }
30
+
24
31
  /**
25
32
  * Geospatial countries query params
26
33
  * @typedef {object} GeospatialCountriesQuery
27
- * @property {string} omit - the country to omit
28
- * @property {string} only - the country to include
34
+ * @property {keyof typeof CountryMap} [omit] - the country to omit
35
+ * @property {keyof typeof CountryMap} [only] - the country to include
29
36
  */
30
37
 
31
38
  /**
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Nunjucks filter to add/replace a parameter on a query string of a url.
3
+ * Currently only used in forms-runner.
4
+ * @param {string} urlPath - existing relative url with query string
5
+ * @param {string} paramName - name of parameter
6
+ * @param {string} paramValue - value of parameter
7
+ * @returns {string}
8
+ */
9
+ export function applyUrlParam(urlPath, paramName, paramValue) {
10
+ if (typeof urlPath !== 'string') {
11
+ return ''
12
+ }
13
+ const url = new URL(urlPath, 'https://dummy')
14
+ url.searchParams.set(paramName, paramValue)
15
+
16
+ return url.pathname + url.search
17
+ }
@@ -6,3 +6,4 @@ export { href } from './href.js'
6
6
  export { field } from './field.js'
7
7
  export { page } from './page.js'
8
8
  export { merge } from './merge.js'
9
+ export { applyUrlParam } from './applyUrlParam.js'
@@ -1,5 +1,6 @@
1
1
  import { format } from 'date-fns'
2
2
 
3
+ import { toLanguageLocale } from '../engine/helpers.js'
3
4
  import { PaymentService } from './service.js'
4
5
 
5
6
  export const DEFAULT_PAYMENT_HELP_URL =
@@ -28,10 +29,29 @@ export async function createPaymentService(
28
29
  /**
29
30
  * Formats a payment date for display
30
31
  * @param {string} isoString - ISO date string
32
+ * @param {string} language - the target language
31
33
  * @returns {string} Formatted date string (e.g., "26 January 2026 5:01pm")
32
34
  */
33
- export function formatPaymentDate(isoString) {
34
- return format(new Date(isoString), 'd MMMM yyyy h:mmaaa')
35
+ export function formatPaymentDate(isoString, language) {
36
+ return formatDateForLanguage(
37
+ new Date(isoString),
38
+ 'd MMMM yyyy h:mmaaa',
39
+ language
40
+ )
41
+ }
42
+
43
+ /**
44
+ * Formats a date for display in a specific language.
45
+ * To use additional languages, the appropriate module must be imported from 'date-fns/locale'
46
+ * @param { Date | string } date - the date
47
+ * @param {string} formatMask - the format mask
48
+ * @param {string} language - the target language
49
+ * @returns {string} Formatted date string (e.g., "26 January 2026 5:01pm")
50
+ */
51
+ export function formatDateForLanguage(date, formatMask, language) {
52
+ return format(date, formatMask, {
53
+ locale: toLanguageLocale(language)
54
+ })
35
55
  }
36
56
 
37
57
  /**
@@ -1,6 +1,7 @@
1
1
  import { StatusCodes } from 'http-status-codes'
2
2
 
3
3
  import { logger } from '../../common/helpers/logging/logger.js'
4
+ import { toPayLanguageCode } from '../engine/helpers.js'
4
5
  import {
5
6
  buildPaymentInfo,
6
7
  convertPenceToPounds
@@ -38,7 +39,7 @@ export class PaymentService {
38
39
  * @param {string} returnUrl
39
40
  * @param {string} reference
40
41
  * @param {boolean} isLivePayment
41
- * @param {{ formId: string, slug: string } | undefined } metadata
42
+ * @param {{ formId: string, slug: string, language: string } | undefined } metadata
42
43
  * @param {string} [email] - optional email to prepopulate on GOV.UK Pay
43
44
  */
44
45
  async createPayment(
@@ -50,6 +51,8 @@ export class PaymentService {
50
51
  metadata,
51
52
  email
52
53
  ) {
54
+ const language = toPayLanguageCode(metadata?.language)
55
+
53
56
  try {
54
57
  /** @type {CreatePaymentRequest} */
55
58
  const payload = {
@@ -58,7 +61,8 @@ export class PaymentService {
58
61
  reference,
59
62
  metadata,
60
63
  return_url: returnUrl,
61
- delayed_capture: true
64
+ delayed_capture: true,
65
+ language
62
66
  }
63
67
 
64
68
  // Prepopulate email on GOV.UK Pay if provided
@@ -18,6 +18,7 @@
18
18
  * @property {string} reference - Unique reference for the payment
19
19
  * @property {string} description - Human-readable description of the payment
20
20
  * @property {string} return_url - URL to redirect the user to after payment
21
+ * @property {string} language - the desired language for translations
21
22
  * @property {boolean} [delayed_capture] - Whether to delay capturing the payment
22
23
  * @property {{ formId: string, slug: string }} [metadata] - Additional metadata for the payment
23
24
  * @property {string} [email] - Email to prepopulate on GOV.UK Pay (max 254 chars)
@@ -16,6 +16,7 @@ const countyFieldName = 'county'
16
16
  const postcodeFieldName = 'postcode'
17
17
 
18
18
  const GOVUK_MARGIN_RIGHT_1 = 'govuk-!-margin-right-1'
19
+ const COMMON_BACK = 'common.back'
19
20
 
20
21
  export const steps = {
21
22
  // Step 1: Postcode/building name input
@@ -297,6 +298,19 @@ export const stepSchema = Joi.string()
297
298
  .valid(...Object.keys(steps))
298
299
  .required()
299
300
 
301
+ /**
302
+ * Joi object schema factories typed with their payload, as JavaScript files
303
+ * cannot pass type arguments to `Joi.object<T>()`
304
+ */
305
+ /** @type {() => ObjectSchema<PostcodeLookupDetailsPayload>} */
306
+ const detailsObjectSchema = Joi.object.bind(Joi)
307
+
308
+ /** @type {() => ObjectSchema<PostcodeLookupSelectPayload>} */
309
+ const selectObjectSchema = Joi.object.bind(Joi)
310
+
311
+ /** @type {() => ObjectSchema<PostcodeLookupManualPayload>} */
312
+ const manualObjectSchema = Joi.object.bind(Joi)
313
+
300
314
  const sharedPayloadSchemaKeys = {
301
315
  crumb: crumbSchema,
302
316
  step: stepSchema
@@ -308,7 +322,7 @@ const sharedPayloadSchemaKeys = {
308
322
  * @returns {ObjectSchema<PostcodeLookupDetailsPayload>}
309
323
  */
310
324
  export function createDetailsPayloadSchema(language = 'en-GB') {
311
- return Joi.object()
325
+ return detailsObjectSchema()
312
326
  .keys({
313
327
  ...sharedPayloadSchemaKeys,
314
328
  [postcodeQueryFieldName]: Joi.string()
@@ -337,7 +351,7 @@ export function createDetailsPayloadSchema(language = 'en-GB') {
337
351
  * @returns {ObjectSchema<PostcodeLookupSelectPayload>}
338
352
  */
339
353
  export function createSelectPayloadSchema(language = 'en-GB') {
340
- return Joi.object()
354
+ return selectObjectSchema()
341
355
  .keys({
342
356
  ...sharedPayloadSchemaKeys,
343
357
  [uprnFieldName]: Joi.string()
@@ -355,7 +369,7 @@ export function createSelectPayloadSchema(language = 'en-GB') {
355
369
  * @returns {ObjectSchema<PostcodeLookupManualPayload>}
356
370
  */
357
371
  export function createManualPayloadSchema(language = 'en-GB') {
358
- return Joi.object()
372
+ return manualObjectSchema()
359
373
  .keys({
360
374
  ...sharedPayloadSchemaKeys,
361
375
  [line1FieldName]: Joi.string()
@@ -403,19 +417,20 @@ function getHref(step) {
403
417
  /**
404
418
  * The postcode lookup details form view model
405
419
  * @param {PostcodeLookupSessionData} data
420
+ * @param {Translator} translator
406
421
  * @param {PostcodeLookupDetailsData} [payload]
407
422
  * @param {Error} [err]
408
423
  */
409
- export function detailsViewModel(data, payload, err) {
410
- const {
411
- componentTitle: pageTitle,
412
- formName,
413
- sourceUrl,
414
- language = 'en-GB'
415
- } = data.initial
424
+ export function detailsViewModel(data, translator, payload, err) {
425
+ const { componentId, componentHint, sourceUrl, languages } = data.initial
426
+
427
+ const { t, tComponent, tForm } = translator
428
+
429
+ const component = /** @type {ComponentDef} */ ({ id: componentId })
416
430
 
417
431
  const backLink = {
418
- href: sourceUrl
432
+ href: sourceUrl,
433
+ text: t(COMMON_BACK)
419
434
  }
420
435
 
421
436
  const { errors, postcodeQueryError, buildingNameQueryError } =
@@ -425,42 +440,53 @@ export function detailsViewModel(data, payload, err) {
425
440
  payload ?? data.details,
426
441
  postcodeQueryError,
427
442
  buildingNameQueryError,
428
- language
443
+ translator.language
429
444
  )
430
445
 
446
+ const hint = componentHint && {
447
+ text: tComponent(component, 'hint')
448
+ }
449
+
431
450
  const continueButton = {
432
- text: i18nT('postcodeLookup.findAddress', language),
451
+ text: t('postcodeLookup.findAddress'),
433
452
  classes: GOVUK_MARGIN_RIGHT_1
434
453
  }
435
454
  const manualLink = {
436
- text: i18nT('postcodeLookup.enterManually', language),
455
+ text: t('postcodeLookup.enterManually'),
437
456
  href: getHref(steps.manual)
438
457
  }
439
458
 
459
+ const selector = languages?.length ? { languages } : {}
460
+
440
461
  return {
441
462
  step: steps.details,
442
463
  showTitle: true,
443
- name: formName,
464
+ name: tForm('title'),
444
465
  serviceUrl: sourceUrl,
445
- pageTitle,
466
+ pageTitle: tComponent(component, 'title'),
467
+ hint,
446
468
  backLink,
447
469
  errors,
448
470
  fields,
449
- buttons: { continueButton, manualLink }
471
+ buttons: { continueButton, manualLink },
472
+ t,
473
+ language: translator.language,
474
+ ...selector
450
475
  }
451
476
  }
452
477
 
453
478
  /**
454
479
  * The postcode lookup select form view model
455
480
  * @param {{ session: PostcodeLookupSessionData, apiKey: string }} data
481
+ * @param {Translator} translator
456
482
  * @param {PostcodeLookupSelectPayload} [payload]
457
483
  * @param {Error} [err]
458
484
  */
459
- export async function selectViewModel(data, payload, err) {
485
+ export async function selectViewModel(data, translator, payload, err) {
460
486
  const { session, apiKey } = data
461
487
  const { details, initial } = session
462
488
  const { postcodeQuery, buildingNameQuery } = details
463
- const language = initial.language ?? 'en-GB'
489
+ const { language, t, tComponent, tForm } = translator
464
490
 
465
491
  const {
466
492
  hasAddresses,
@@ -471,12 +497,18 @@ export async function selectViewModel(data, payload, err) {
471
497
  } = await getAddresses(postcodeQuery, buildingNameQuery, apiKey)
472
498
 
473
499
  const title = hasAddresses
474
- ? initial.componentTitle
475
- : i18nT('postcodeLookup.noAddressFoundTitle', language)
500
+ ? tComponent(
501
+ /** @type {ComponentDef} */ ({ id: initial.componentId }),
502
+ 'title'
503
+ )
504
+ : t('postcodeLookup.noAddressFoundTitle')
476
505
  const formPath = initial.sourceUrl
477
506
  const href = getHref()
478
507
 
479
- const backLink = { href }
508
+ const backLink = {
509
+ href,
510
+ text: t(COMMON_BACK)
511
+ }
480
512
 
481
513
  const { errors, uprnError } = buildErrors(err)
482
514
 
@@ -490,7 +522,7 @@ export async function selectViewModel(data, payload, err) {
490
522
  language
491
523
  )
492
524
 
493
- const searchAgainText = i18nT('postcodeLookup.searchAgain', language)
525
+ const searchAgainText = t('postcodeLookup.searchAgain')
494
526
 
495
527
  const searchAgainLink = {
496
528
  text: searchAgainText,
@@ -499,27 +531,29 @@ export async function selectViewModel(data, payload, err) {
499
531
 
500
532
  const continueButton = {
501
533
  href: hasAddresses ? undefined : href,
502
- text: hasAddresses
503
- ? i18nT('postcodeLookup.useThisAddress', language)
504
- : searchAgainText,
534
+ text: hasAddresses ? t('postcodeLookup.useThisAddress') : searchAgainText,
505
535
  classes: GOVUK_MARGIN_RIGHT_1
506
536
  }
507
537
  const manualLink = {
508
- text: i18nT('postcodeLookup.enterManually', language),
538
+ text: t('postcodeLookup.enterManually'),
509
539
  href: `${href}?step=${steps.manual}`
510
540
  }
511
541
 
512
542
  const addressesFoundText = hasAddresses
513
- ? i18nT('postcodeLookup.addressFound', language, { count: addressCount })
543
+ ? t('postcodeLookup.addressFound', { count: addressCount })
514
544
  : undefined
515
545
  const noAddressFoundText = !hasAddresses
516
- ? i18nT('postcodeLookup.noAddressFoundBody', language)
546
+ ? t('postcodeLookup.noAddressFoundBody')
517
547
  : undefined
518
548
 
549
+ const selector = initial.languages?.length
550
+ ? { languages: initial.languages }
551
+ : {}
552
+
519
553
  return {
520
554
  step: steps.select,
521
555
  showTitle: true,
522
- name: title,
556
+ name: tForm('title'),
523
557
  serviceUrl: formPath,
524
558
  pageTitle: title,
525
559
  backLink,
@@ -533,28 +567,32 @@ export async function selectViewModel(data, payload, err) {
533
567
  hasMultipleAddresses,
534
568
  addressesFoundText,
535
569
  noAddressFoundText,
536
- buttons: { continueButton, manualLink }
570
+ buttons: { continueButton, manualLink },
571
+ t,
572
+ language,
573
+ ...selector
537
574
  }
538
575
  }
539
576
 
540
577
  /**
541
578
  * The postcode lookup manual form view model
542
579
  * @param {PostcodeLookupSessionData} data
580
+ * @param {Translator} translator
543
581
  * @param {PostcodeLookupManualPayload} [payload]
544
582
  * @param {Error} [err]
545
583
  */
546
- export function manualViewModel(data, payload, err) {
547
- const {
548
- componentTitle,
549
- sourceUrl,
550
- componentHint,
551
- language = 'en-GB'
552
- } = data.initial
584
+ export function manualViewModel(data, translator, payload, err) {
585
+ const { componentId, sourceUrl, componentHint, languages } = data.initial
553
586
  const formPath = sourceUrl
554
587
  const href = getHref()
555
588
 
589
+ const { t, tComponent, tForm } = translator
590
+
591
+ const component = /** @type {ComponentDef} */ ({ id: componentId })
592
+
556
593
  const backLink = {
557
- href
594
+ href,
595
+ text: t(COMMON_BACK)
558
596
  }
559
597
 
560
598
  const {
@@ -567,7 +605,7 @@ export function manualViewModel(data, payload, err) {
567
605
  } = buildErrors(err)
568
606
 
569
607
  const hint = componentHint && {
570
- text: componentHint
608
+ text: tComponent(component, 'hint')
571
609
  }
572
610
 
573
611
  const fields = getManualFields(
@@ -577,29 +615,34 @@ export function manualViewModel(data, payload, err) {
577
615
  townError,
578
616
  countyError,
579
617
  postcodeError,
580
- language
618
+ translator.language
581
619
  )
582
620
 
583
621
  const continueButton = {
584
- text: i18nT('postcodeLookup.useThisAddress', language),
622
+ text: t('postcodeLookup.useThisAddress'),
585
623
  classes: GOVUK_MARGIN_RIGHT_1
586
624
  }
587
625
  const detailsLink = {
588
- text: i18nT('postcodeLookup.findAnAddressInstead', language),
626
+ text: t('postcodeLookup.findAnAddressInstead'),
589
627
  href
590
628
  }
591
629
 
630
+ const selector = languages?.length ? { languages } : {}
631
+
592
632
  return {
593
633
  step: steps.manual,
594
634
  showTitle: true,
595
- name: componentTitle,
635
+ name: tForm('title'),
596
636
  serviceUrl: formPath,
597
- pageTitle: componentTitle,
637
+ pageTitle: tComponent(component, 'title'),
598
638
  backLink,
599
639
  errors,
600
640
  hint,
601
641
  fields,
602
- buttons: { continueButton, detailsLink }
642
+ buttons: { continueButton, detailsLink },
643
+ t,
644
+ language: translator.language,
645
+ ...selector
603
646
  }
604
647
  }
605
648
 
@@ -607,5 +650,7 @@ export function manualViewModel(data, payload, err) {
607
650
 
608
651
  /**
609
652
  * @import { ObjectSchema, ValidationErrorItem } from 'joi'
653
+ * @import { ComponentDef } from '@defra/forms-model'
610
654
  * @import { Address, PostcodeLookupDetailsData, PostcodeLookupDetailsPayload, PostcodeLookupManualPayload, PostcodeLookupSelectPayload, PostcodeLookupSessionData } from '../types.js'
655
+ * @import { Translator } from '../../engine/i18n/types.js'
611
656
  */
@@ -3,6 +3,7 @@ import { StatusCodes } from 'http-status-codes'
3
3
  import Joi from 'joi'
4
4
 
5
5
  import { EXTERNAL_STATE_APPENDAGE } from '../../../constants.js'
6
+ import { getCachedPluginTranslator } from '../../engine/i18n/form.js'
6
7
  import {
7
8
  JOURNEY_BASE_URL,
8
9
  createDetailsPayloadSchema,
@@ -15,6 +16,7 @@ import {
15
16
  steps
16
17
  } from '../models/index.js'
17
18
  import * as service from '../service.js'
19
+ import { resolveLanguage } from '../../../utils/utils.js'
18
20
 
19
21
  const viewName = 'postcode-lookup-details'
20
22
 
@@ -22,7 +24,7 @@ const viewName = 'postcode-lookup-details'
22
24
  * Get the session state associated with this journey
23
25
  * @param {PostcodeLookupRequest} request
24
26
  */
25
- function getSessionState(request) {
27
+ export function getSessionState(request) {
26
28
  /**
27
29
  * @type {PostcodeLookupSessionData | null | undefined}
28
30
  */
@@ -63,6 +65,32 @@ function flashComponentState(request, componentName, address) {
63
65
 
64
66
  request.yar.flash(EXTERNAL_STATE_APPENDAGE, appendage, true)
65
67
  }
68
+ /**
69
+ * Get a cached translator, or create a new one if not in the cache
70
+ * @param {PostcodeLookupRequest} request
71
+ * @param {PostcodeLookupDispatchData} initial
72
+ * @param { string | string[] | undefined } language
73
+ */
74
+ export async function getDispatchTranslator(request, initial, language) {
75
+ const { formId, formStatus } = initial
76
+
77
+ // Override language if passed as a query param
78
+ if (language) {
79
+ request.yar.set('language', language)
80
+ }
81
+
82
+ const typedReq = /** @type {AnyFormRequest} */ (/** @type {any} */ (request))
83
+ const lang = resolveLanguage(typedReq)
84
+
85
+ const translator = await getCachedPluginTranslator(
86
+ typedReq,
87
+ /** @type {FormMetadata} */ ({ id: formId }),
88
+ formStatus,
89
+ lang
90
+ )
91
+
92
+ return translator
93
+ }
66
94
 
67
95
  /**
68
96
  * Initialises and dispatches the request to the postcode lookup journey
@@ -101,15 +129,21 @@ function getRoute() {
101
129
  return {
102
130
  method: 'GET',
103
131
  path: JOURNEY_BASE_URL,
104
- handler(request, h) {
132
+ async handler(request, h) {
105
133
  const { query } = request
106
- const { step } = query
134
+ const { step, language } = query
107
135
  const session = getSessionState(request)
108
136
 
137
+ const translator = await getDispatchTranslator(
138
+ request,
139
+ session.initial,
140
+ language
141
+ )
142
+
109
143
  const model =
110
144
  step === steps.manual
111
- ? manualViewModel(session)
112
- : detailsViewModel(session)
145
+ ? manualViewModel(session, translator)
146
+ : detailsViewModel(session, translator)
113
147
 
114
148
  return h.view(viewName, model)
115
149
  },
@@ -117,7 +151,8 @@ function getRoute() {
117
151
  validate: {
118
152
  query: Joi.object()
119
153
  .keys({
120
- step: Joi.string().allow(steps.details, steps.manual).optional()
154
+ step: Joi.string().allow(steps.details, steps.manual).optional(),
155
+ language: Joi.string().allow('en-GB', 'cy').optional()
121
156
  })
122
157
  .optional()
123
158
  }
@@ -163,6 +198,19 @@ function postRoute(options) {
163
198
  }
164
199
  }
165
200
 
201
+ /**
202
+ * Validate a payload against a schema. Joi types the value as `any` when
203
+ * validation fails, so it is typed here as the partially converted payload.
204
+ * @template TSchema
205
+ * @param {ObjectSchema<TSchema>} schema
206
+ * @param {unknown} payload
207
+ * @param {ValidationOptions} [options]
208
+ * @returns {{ value: TSchema, error: undefined } | { value: TSchema | undefined, error: ValidationError }}
209
+ */
210
+ function validatePayload(schema, payload, options) {
211
+ return schema.validate(payload, options)
212
+ }
213
+
166
214
  /**
167
215
  * Post handler for the details step
168
216
  * @param {PostcodeLookupPostRequest} request
@@ -172,15 +220,22 @@ function postRoute(options) {
172
220
  async function detailsPostHandler(request, h, options) {
173
221
  const session = getSessionState(request)
174
222
  const { ordnanceSurveyApiKey: apiKey } = options
175
- const language = session.initial.language ?? 'en-GB'
176
- const { value: details, error } = createDetailsPayloadSchema(
177
- language
178
- ).validate(request.payload)
223
+ const translator = await getDispatchTranslator(
224
+ request,
225
+ session.initial,
226
+ request.query.language
227
+ )
228
+ const language = translator.language
229
+
230
+ const { value: details, error } = validatePayload(
231
+ createDetailsPayloadSchema(language),
232
+ request.payload
233
+ )
179
234
 
180
235
  let model
181
236
 
182
237
  if (error) {
183
- model = detailsViewModel(session, details, error)
238
+ model = detailsViewModel(session, translator, details, error)
184
239
 
185
240
  return h.view(viewName, model)
186
241
  }
@@ -191,7 +246,7 @@ async function detailsPostHandler(request, h, options) {
191
246
  // Store the updated session
192
247
  request.yar.set(JOURNEY_BASE_URL, session)
193
248
 
194
- model = await selectViewModel({ session, apiKey })
249
+ model = await selectViewModel({ session, apiKey }, translator)
195
250
 
196
251
  return h.view(viewName, model)
197
252
  }
@@ -205,13 +260,24 @@ async function detailsPostHandler(request, h, options) {
205
260
  async function selectPostHandler(request, h, options) {
206
261
  const session = getSessionState(request)
207
262
  const { ordnanceSurveyApiKey: apiKey } = options
208
- const language = session.initial.language ?? 'en-GB'
209
- const { value: select, error } = createSelectPayloadSchema(language).validate(
263
+ const translator = await getDispatchTranslator(
264
+ request,
265
+ session.initial,
266
+ request.query.language
267
+ )
268
+ const language = translator.language
269
+ const { value: select, error } = validatePayload(
270
+ createSelectPayloadSchema(language),
210
271
  request.payload
211
272
  )
212
273
 
213
274
  if (error) {
214
- const model = await selectViewModel({ session, apiKey }, select, error)
275
+ const model = await selectViewModel(
276
+ { session, apiKey },
277
+ translator,
278
+ select,
279
+ error
280
+ )
215
281
 
216
282
  return h.view(viewName, model)
217
283
  }
@@ -235,19 +301,23 @@ async function selectPostHandler(request, h, options) {
235
301
  * @param {PostcodeLookupPostRequest} request
236
302
  * @param {ResponseToolkit<PostcodeLookupPostRequestRefs>} h
237
303
  */
238
- function manualPostHandler(request, h) {
304
+ async function manualPostHandler(request, h) {
239
305
  const session = getSessionState(request)
240
- const language = session.initial.language ?? 'en-GB'
306
+ const translator = await getDispatchTranslator(
307
+ request,
308
+ session.initial,
309
+ request.query.language
310
+ )
311
+ const language = translator.language
241
312
 
242
- const { value: manual, error } = createManualPayloadSchema(language).validate(
313
+ const { value: manual, error } = validatePayload(
314
+ createManualPayloadSchema(language),
243
315
  request.payload,
244
- {
245
- abortEarly: false
246
- }
316
+ { abortEarly: false }
247
317
  )
248
318
 
249
319
  if (error) {
250
- const model = manualViewModel(session, manual, error)
320
+ const model = manualViewModel(session, translator, manual, error)
251
321
 
252
322
  return h.view(viewName, model)
253
323
  }
@@ -261,7 +331,9 @@ function manualPostHandler(request, h) {
261
331
 
262
332
  /**
263
333
  * @import { ResponseToolkit, ServerRoute } from '@hapi/hapi'
264
- * @import { PostcodeLookupManualPayload, PostcodeLookupDetailsPayload, PostcodeLookupSelectPayload, Address, PostcodeLookupGetRequestRefs, PostcodeLookupPostRequestRefs, PostcodeLookupRequest, PostcodeLookupPostRequest, PostcodeLookupConfiguration, PostcodeLookupDispatchData, PostcodeLookupSessionData } from '../types.js'
334
+ * @import { ObjectSchema, ValidationError, ValidationOptions } from 'joi'
335
+ * @import { FormMetadata } from '@defra/forms-model'
336
+ * @import { PostcodeLookupManualPayload, Address, PostcodeLookupGetRequestRefs, PostcodeLookupPostRequestRefs, PostcodeLookupRequest, PostcodeLookupPostRequest, PostcodeLookupConfiguration, PostcodeLookupDispatchData, PostcodeLookupSessionData } from '../types.js'
265
337
  * @import { FormRequestPayload, FormResponseToolkit } from '../../../routes/types.js'
266
- * @import { ExternalStateAppendage } from '../../engine/types.js'
338
+ * @import { AnyFormRequest, ExternalStateAppendage } from '../../engine/types.js'
267
339
  */
@@ -14,12 +14,16 @@
14
14
  /**
15
15
  * @typedef {{
16
16
  * sourceUrl: string,
17
+ * formId: string
17
18
  * formName: string
19
+ * formStatus: FormStatus
20
+ * componentId: string
18
21
  * componentName: string
19
22
  * componentTitle: string,
20
23
  * componentHint?: string
21
24
  * step?: string
22
25
  * language?: string
26
+ * languages?: { name: string, code: string}[]
23
27
  * }} PostcodeLookupDispatchData
24
28
  */
25
29
 
@@ -49,6 +53,7 @@
49
53
  * Postcode lookup query params
50
54
  * @typedef {object} PostcodeLookupQuery
51
55
  * @property {string} [step] - step
56
+ * @property {string} [language] - language
52
57
  */
53
58
 
54
59
  /**
@@ -140,5 +145,5 @@
140
145
 
141
146
  /**
142
147
  * @import { Request } from '@hapi/hapi'
143
- * @import { FormPayload } from '../engine/types.js'
148
+ * @import { FormStatus } from '@defra/forms-model'
144
149
  */