@defra/forms-engine-plugin 0.0.1

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 (615) hide show
  1. package/.browserslistrc +16 -0
  2. package/.editorconfig +9 -0
  3. package/.eslintrc.cjs +266 -0
  4. package/.github/dependabot.yml +85 -0
  5. package/.github/workflows/check-pull-request.yml +209 -0
  6. package/.github/workflows/pr-notifier.yml +98 -0
  7. package/.github/workflows/publish.yml +111 -0
  8. package/.husky/pre-commit +1 -0
  9. package/.lintstagedrc.js +4 -0
  10. package/.nvmrc +1 -0
  11. package/.prettierignore +8 -0
  12. package/.prettierrc.cjs +26 -0
  13. package/.server/client/javascripts/application.js +68 -0
  14. package/.server/client/javascripts/application.js.map +1 -0
  15. package/.server/client/javascripts/file-upload.js +292 -0
  16. package/.server/client/javascripts/file-upload.js.map +1 -0
  17. package/.server/client/stylesheets/_code.scss +33 -0
  18. package/.server/client/stylesheets/_govuk-frontend.scss +4 -0
  19. package/.server/client/stylesheets/_prose.scss +56 -0
  20. package/.server/client/stylesheets/_service-banner.scss +24 -0
  21. package/.server/client/stylesheets/_summary-list.scss +28 -0
  22. package/.server/client/stylesheets/_tag-env.scss +24 -0
  23. package/.server/client/stylesheets/application.scss +14 -0
  24. package/.server/common/cookies.js +55 -0
  25. package/.server/common/cookies.js.map +1 -0
  26. package/.server/common/cookies.test.js +15 -0
  27. package/.server/common/cookies.test.js.map +1 -0
  28. package/.server/common/types.js +6 -0
  29. package/.server/common/types.js.map +1 -0
  30. package/.server/config/index.js +241 -0
  31. package/.server/config/index.js.map +1 -0
  32. package/.server/index.js +25 -0
  33. package/.server/index.js.map +1 -0
  34. package/.server/server/common/helpers/logging/logger-options.js +44 -0
  35. package/.server/server/common/helpers/logging/logger-options.js.map +1 -0
  36. package/.server/server/common/helpers/logging/logger.js +6 -0
  37. package/.server/server/common/helpers/logging/logger.js.map +1 -0
  38. package/.server/server/common/helpers/logging/request-logger.js +7 -0
  39. package/.server/server/common/helpers/logging/request-logger.js.map +1 -0
  40. package/.server/server/common/helpers/logging/request-tracing.js +12 -0
  41. package/.server/server/common/helpers/logging/request-tracing.js.map +1 -0
  42. package/.server/server/common/helpers/redis-client.js +55 -0
  43. package/.server/server/common/helpers/redis-client.js.map +1 -0
  44. package/.server/server/constants.js +2 -0
  45. package/.server/server/constants.js.map +1 -0
  46. package/.server/server/forms/README.md +10 -0
  47. package/.server/server/forms/components.json +1015 -0
  48. package/.server/server/forms/report-a-terrorist.json +270 -0
  49. package/.server/server/forms/runner-components-test.json +365 -0
  50. package/.server/server/forms/test.json +581 -0
  51. package/.server/server/index.js +116 -0
  52. package/.server/server/index.js.map +1 -0
  53. package/.server/server/plugins/blankie.js +29 -0
  54. package/.server/server/plugins/blankie.js.map +1 -0
  55. package/.server/server/plugins/crumb.js +15 -0
  56. package/.server/server/plugins/crumb.js.map +1 -0
  57. package/.server/server/plugins/engine/README.md +87 -0
  58. package/.server/server/plugins/engine/components/AutocompleteField.js +37 -0
  59. package/.server/server/plugins/engine/components/AutocompleteField.js.map +1 -0
  60. package/.server/server/plugins/engine/components/CheckboxesField.js +79 -0
  61. package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -0
  62. package/.server/server/plugins/engine/components/ComponentBase.js +55 -0
  63. package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -0
  64. package/.server/server/plugins/engine/components/ComponentCollection.js +198 -0
  65. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -0
  66. package/.server/server/plugins/engine/components/DatePartsField.js +208 -0
  67. package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -0
  68. package/.server/server/plugins/engine/components/Details.js +26 -0
  69. package/.server/server/plugins/engine/components/Details.js.map +1 -0
  70. package/.server/server/plugins/engine/components/EmailAddressField.js +40 -0
  71. package/.server/server/plugins/engine/components/EmailAddressField.js.map +1 -0
  72. package/.server/server/plugins/engine/components/FileUploadField.js +195 -0
  73. package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -0
  74. package/.server/server/plugins/engine/components/FormComponent.js +210 -0
  75. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -0
  76. package/.server/server/plugins/engine/components/Html.js +24 -0
  77. package/.server/server/plugins/engine/components/Html.js.map +1 -0
  78. package/.server/server/plugins/engine/components/InsetText.js +22 -0
  79. package/.server/server/plugins/engine/components/InsetText.js.map +1 -0
  80. package/.server/server/plugins/engine/components/List.js +65 -0
  81. package/.server/server/plugins/engine/components/List.js.map +1 -0
  82. package/.server/server/plugins/engine/components/ListFormComponent.js +87 -0
  83. package/.server/server/plugins/engine/components/ListFormComponent.js.map +1 -0
  84. package/.server/server/plugins/engine/components/MonthYearField.js +172 -0
  85. package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -0
  86. package/.server/server/plugins/engine/components/MultilineTextField.js +115 -0
  87. package/.server/server/plugins/engine/components/MultilineTextField.js.map +1 -0
  88. package/.server/server/plugins/engine/components/NumberField.js +137 -0
  89. package/.server/server/plugins/engine/components/NumberField.js.map +1 -0
  90. package/.server/server/plugins/engine/components/RadiosField.js +18 -0
  91. package/.server/server/plugins/engine/components/RadiosField.js.map +1 -0
  92. package/.server/server/plugins/engine/components/SelectField.js +33 -0
  93. package/.server/server/plugins/engine/components/SelectField.js.map +1 -0
  94. package/.server/server/plugins/engine/components/SelectionControlField.js +43 -0
  95. package/.server/server/plugins/engine/components/SelectionControlField.js.map +1 -0
  96. package/.server/server/plugins/engine/components/TelephoneNumberField.js +43 -0
  97. package/.server/server/plugins/engine/components/TelephoneNumberField.js.map +1 -0
  98. package/.server/server/plugins/engine/components/TextField.js +63 -0
  99. package/.server/server/plugins/engine/components/TextField.js.map +1 -0
  100. package/.server/server/plugins/engine/components/UkAddressField.js +130 -0
  101. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -0
  102. package/.server/server/plugins/engine/components/YesNoField.js +28 -0
  103. package/.server/server/plugins/engine/components/YesNoField.js.map +1 -0
  104. package/.server/server/plugins/engine/components/constants.js +2 -0
  105. package/.server/server/plugins/engine/components/constants.js.map +1 -0
  106. package/.server/server/plugins/engine/components/helpers.js +219 -0
  107. package/.server/server/plugins/engine/components/helpers.js.map +1 -0
  108. package/.server/server/plugins/engine/components/index.js +25 -0
  109. package/.server/server/plugins/engine/components/index.js.map +1 -0
  110. package/.server/server/plugins/engine/components/types.js +2 -0
  111. package/.server/server/plugins/engine/components/types.js.map +1 -0
  112. package/.server/server/plugins/engine/configureEnginePlugin.js +44 -0
  113. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -0
  114. package/.server/server/plugins/engine/helpers.js +268 -0
  115. package/.server/server/plugins/engine/helpers.js.map +1 -0
  116. package/.server/server/plugins/engine/index.js +6 -0
  117. package/.server/server/plugins/engine/index.js.map +1 -0
  118. package/.server/server/plugins/engine/models/FormModel.js +363 -0
  119. package/.server/server/plugins/engine/models/FormModel.js.map +1 -0
  120. package/.server/server/plugins/engine/models/RepeatingSummaryViewModel.js +2 -0
  121. package/.server/server/plugins/engine/models/RepeatingSummaryViewModel.js.map +1 -0
  122. package/.server/server/plugins/engine/models/Section.js +2 -0
  123. package/.server/server/plugins/engine/models/Section.js.map +1 -0
  124. package/.server/server/plugins/engine/models/SummaryViewModel.js +183 -0
  125. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -0
  126. package/.server/server/plugins/engine/models/index.js +3 -0
  127. package/.server/server/plugins/engine/models/index.js.map +1 -0
  128. package/.server/server/plugins/engine/models/types.js +2 -0
  129. package/.server/server/plugins/engine/models/types.js.map +1 -0
  130. package/.server/server/plugins/engine/outputFormatters/human/v1.js +44 -0
  131. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -0
  132. package/.server/server/plugins/engine/outputFormatters/index.js +24 -0
  133. package/.server/server/plugins/engine/outputFormatters/index.js.map +1 -0
  134. package/.server/server/plugins/engine/outputFormatters/machine/v1.js +104 -0
  135. package/.server/server/plugins/engine/outputFormatters/machine/v1.js.map +1 -0
  136. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +110 -0
  137. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -0
  138. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +401 -0
  139. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -0
  140. package/.server/server/plugins/engine/pageControllers/PageController.js +133 -0
  141. package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -0
  142. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +482 -0
  143. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -0
  144. package/.server/server/plugins/engine/pageControllers/README.md +28 -0
  145. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +372 -0
  146. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -0
  147. package/.server/server/plugins/engine/pageControllers/StartPageController.js +16 -0
  148. package/.server/server/plugins/engine/pageControllers/StartPageController.js.map +1 -0
  149. package/.server/server/plugins/engine/pageControllers/StatusPageController.js +43 -0
  150. package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -0
  151. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +195 -0
  152. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -0
  153. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js +8 -0
  154. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js.map +1 -0
  155. package/.server/server/plugins/engine/pageControllers/helpers.js +69 -0
  156. package/.server/server/plugins/engine/pageControllers/helpers.js.map +1 -0
  157. package/.server/server/plugins/engine/pageControllers/index.js +10 -0
  158. package/.server/server/plugins/engine/pageControllers/index.js.map +1 -0
  159. package/.server/server/plugins/engine/pageControllers/validationOptions.js +78 -0
  160. package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -0
  161. package/.server/server/plugins/engine/plugin.js +562 -0
  162. package/.server/server/plugins/engine/plugin.js.map +1 -0
  163. package/.server/server/plugins/engine/services/formSubmissionService.js +39 -0
  164. package/.server/server/plugins/engine/services/formSubmissionService.js.map +1 -0
  165. package/.server/server/plugins/engine/services/formsService.js +41 -0
  166. package/.server/server/plugins/engine/services/formsService.js.map +1 -0
  167. package/.server/server/plugins/engine/services/formsService.test.js +71 -0
  168. package/.server/server/plugins/engine/services/formsService.test.js.map +1 -0
  169. package/.server/server/plugins/engine/services/index.js +4 -0
  170. package/.server/server/plugins/engine/services/index.js.map +1 -0
  171. package/.server/server/plugins/engine/services/notifyService.js +45 -0
  172. package/.server/server/plugins/engine/services/notifyService.js.map +1 -0
  173. package/.server/server/plugins/engine/services/uploadService.js +51 -0
  174. package/.server/server/plugins/engine/services/uploadService.js.map +1 -0
  175. package/.server/server/plugins/engine/types.js +53 -0
  176. package/.server/server/plugins/engine/types.js.map +1 -0
  177. package/.server/server/plugins/engine/views/components/autocompletefield.html +5 -0
  178. package/.server/server/plugins/engine/views/components/checkboxesfield.html +5 -0
  179. package/.server/server/plugins/engine/views/components/datepartsfield.html +5 -0
  180. package/.server/server/plugins/engine/views/components/details.html +6 -0
  181. package/.server/server/plugins/engine/views/components/emailaddressfield.html +5 -0
  182. package/.server/server/plugins/engine/views/components/fileuploadfield-key.html +8 -0
  183. package/.server/server/plugins/engine/views/components/fileuploadfield-value.html +3 -0
  184. package/.server/server/plugins/engine/views/components/fileuploadfield.html +24 -0
  185. package/.server/server/plugins/engine/views/components/html.html +3 -0
  186. package/.server/server/plugins/engine/views/components/insettext.html +7 -0
  187. package/.server/server/plugins/engine/views/components/list.html +36 -0
  188. package/.server/server/plugins/engine/views/components/monthyearfield.html +5 -0
  189. package/.server/server/plugins/engine/views/components/multilinetextfield.html +10 -0
  190. package/.server/server/plugins/engine/views/components/numberfield.html +5 -0
  191. package/.server/server/plugins/engine/views/components/radiosfield.html +5 -0
  192. package/.server/server/plugins/engine/views/components/selectfield.html +5 -0
  193. package/.server/server/plugins/engine/views/components/telephonenumberfield.html +5 -0
  194. package/.server/server/plugins/engine/views/components/textfield.html +5 -0
  195. package/.server/server/plugins/engine/views/components/ukaddressfield.html +25 -0
  196. package/.server/server/plugins/engine/views/components/yesnofield.html +5 -0
  197. package/.server/server/plugins/engine/views/file-upload.html +45 -0
  198. package/.server/server/plugins/engine/views/index.html +39 -0
  199. package/.server/server/plugins/engine/views/item-delete.html +56 -0
  200. package/.server/server/plugins/engine/views/partials/components.html +6 -0
  201. package/.server/server/plugins/engine/views/partials/conditional-components.html +3 -0
  202. package/.server/server/plugins/engine/views/partials/debug.html +44 -0
  203. package/.server/server/plugins/engine/views/partials/form.html +15 -0
  204. package/.server/server/plugins/engine/views/partials/heading.html +16 -0
  205. package/.server/server/plugins/engine/views/partials/preview-banner.html +32 -0
  206. package/.server/server/plugins/engine/views/partials/preview-banner.test.js +108 -0
  207. package/.server/server/plugins/engine/views/partials/preview-banner.test.js.map +1 -0
  208. package/.server/server/plugins/engine/views/partials/warn-missing-notification-email.html +10 -0
  209. package/.server/server/plugins/engine/views/repeat-list-summary.html +53 -0
  210. package/.server/server/plugins/errorPages.js +49 -0
  211. package/.server/server/plugins/errorPages.js.map +1 -0
  212. package/.server/server/plugins/nunjucks/context.js +79 -0
  213. package/.server/server/plugins/nunjucks/context.js.map +1 -0
  214. package/.server/server/plugins/nunjucks/context.test.js +134 -0
  215. package/.server/server/plugins/nunjucks/context.test.js.map +1 -0
  216. package/.server/server/plugins/nunjucks/enviroment.test.js +166 -0
  217. package/.server/server/plugins/nunjucks/enviroment.test.js.map +1 -0
  218. package/.server/server/plugins/nunjucks/environment.js +95 -0
  219. package/.server/server/plugins/nunjucks/environment.js.map +1 -0
  220. package/.server/server/plugins/nunjucks/filters/answer.js +26 -0
  221. package/.server/server/plugins/nunjucks/filters/answer.js.map +1 -0
  222. package/.server/server/plugins/nunjucks/filters/answer.test.js +70 -0
  223. package/.server/server/plugins/nunjucks/filters/answer.test.js.map +1 -0
  224. package/.server/server/plugins/nunjucks/filters/evaluate.js +22 -0
  225. package/.server/server/plugins/nunjucks/filters/evaluate.js.map +1 -0
  226. package/.server/server/plugins/nunjucks/filters/field.js +26 -0
  227. package/.server/server/plugins/nunjucks/filters/field.js.map +1 -0
  228. package/.server/server/plugins/nunjucks/filters/field.test.js +64 -0
  229. package/.server/server/plugins/nunjucks/filters/field.test.js.map +1 -0
  230. package/.server/server/plugins/nunjucks/filters/highlight.js +14 -0
  231. package/.server/server/plugins/nunjucks/filters/highlight.js.map +1 -0
  232. package/.server/server/plugins/nunjucks/filters/href.js +28 -0
  233. package/.server/server/plugins/nunjucks/filters/href.js.map +1 -0
  234. package/.server/server/plugins/nunjucks/filters/href.test.js +63 -0
  235. package/.server/server/plugins/nunjucks/filters/href.test.js.map +1 -0
  236. package/.server/server/plugins/nunjucks/filters/index.js +9 -0
  237. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -0
  238. package/.server/server/plugins/nunjucks/filters/inspect.js +16 -0
  239. package/.server/server/plugins/nunjucks/filters/inspect.js.map +1 -0
  240. package/.server/server/plugins/nunjucks/filters/page.js +23 -0
  241. package/.server/server/plugins/nunjucks/filters/page.js.map +1 -0
  242. package/.server/server/plugins/nunjucks/filters/page.test.js +51 -0
  243. package/.server/server/plugins/nunjucks/filters/page.test.js.map +1 -0
  244. package/.server/server/plugins/nunjucks/index.js +4 -0
  245. package/.server/server/plugins/nunjucks/index.js.map +1 -0
  246. package/.server/server/plugins/nunjucks/plugin.js +38 -0
  247. package/.server/server/plugins/nunjucks/plugin.js.map +1 -0
  248. package/.server/server/plugins/nunjucks/render.js +41 -0
  249. package/.server/server/plugins/nunjucks/render.js.map +1 -0
  250. package/.server/server/plugins/nunjucks/types.js +41 -0
  251. package/.server/server/plugins/nunjucks/types.js.map +1 -0
  252. package/.server/server/plugins/pulse.js +8 -0
  253. package/.server/server/plugins/pulse.js.map +1 -0
  254. package/.server/server/plugins/router.js +169 -0
  255. package/.server/server/plugins/router.js.map +1 -0
  256. package/.server/server/plugins/session.js +28 -0
  257. package/.server/server/plugins/session.js.map +1 -0
  258. package/.server/server/routes/health.js +15 -0
  259. package/.server/server/routes/health.js.map +1 -0
  260. package/.server/server/routes/health.test.js +32 -0
  261. package/.server/server/routes/health.test.js.map +1 -0
  262. package/.server/server/routes/index.js +3 -0
  263. package/.server/server/routes/index.js.map +1 -0
  264. package/.server/server/routes/public.js +37 -0
  265. package/.server/server/routes/public.js.map +1 -0
  266. package/.server/server/routes/types.js +14 -0
  267. package/.server/server/routes/types.js.map +1 -0
  268. package/.server/server/schemas/index.js +15 -0
  269. package/.server/server/schemas/index.js.map +1 -0
  270. package/.server/server/secure-context.js +36 -0
  271. package/.server/server/secure-context.js.map +1 -0
  272. package/.server/server/services/cacheService.js +85 -0
  273. package/.server/server/services/cacheService.js.map +1 -0
  274. package/.server/server/services/httpService.js +44 -0
  275. package/.server/server/services/httpService.js.map +1 -0
  276. package/.server/server/services/httpService.test.js +504 -0
  277. package/.server/server/services/httpService.test.js.map +1 -0
  278. package/.server/server/services/index.js +2 -0
  279. package/.server/server/services/index.js.map +1 -0
  280. package/.server/server/types.js +2 -0
  281. package/.server/server/types.js.map +1 -0
  282. package/.server/server/utils/notify.js +40 -0
  283. package/.server/server/utils/notify.js.map +1 -0
  284. package/.server/server/utils/secure-context/get-trust-store-certs.js +7 -0
  285. package/.server/server/utils/secure-context/get-trust-store-certs.js.map +1 -0
  286. package/.server/server/utils/secure-context/get-trust-store-certs.test.js +14 -0
  287. package/.server/server/utils/secure-context/get-trust-store-certs.test.js.map +1 -0
  288. package/.server/server/utils/utils.js +20 -0
  289. package/.server/server/utils/utils.js.map +1 -0
  290. package/.server/server/utils/utils.test.js +49 -0
  291. package/.server/server/utils/utils.test.js.map +1 -0
  292. package/.server/server/views/404.html +16 -0
  293. package/.server/server/views/500.html +19 -0
  294. package/.server/server/views/components/debug/macro.njk +3 -0
  295. package/.server/server/views/components/debug/template.njk +13 -0
  296. package/.server/server/views/components/service-banner/macro.njk +3 -0
  297. package/.server/server/views/components/service-banner/template.njk +20 -0
  298. package/.server/server/views/components/service-banner/template.test.js +36 -0
  299. package/.server/server/views/components/service-banner/template.test.js.map +1 -0
  300. package/.server/server/views/components/tag-env/macro.njk +3 -0
  301. package/.server/server/views/components/tag-env/template.njk +30 -0
  302. package/.server/server/views/components/tag-env/template.test.js +59 -0
  303. package/.server/server/views/components/tag-env/template.test.js.map +1 -0
  304. package/.server/server/views/confirmation.html +19 -0
  305. package/.server/server/views/help/accessibility-statement.html +58 -0
  306. package/.server/server/views/help/cookie-preferences.html +57 -0
  307. package/.server/server/views/help/cookies.html +71 -0
  308. package/.server/server/views/help/get-support.html +37 -0
  309. package/.server/server/views/help/privacy-notice.html +68 -0
  310. package/.server/server/views/help/terms-and-conditions.html +83 -0
  311. package/.server/server/views/layout.html +199 -0
  312. package/.server/server/views/summary.html +50 -0
  313. package/.server/typings/hapi/index.d.js +2 -0
  314. package/.server/typings/hapi/index.d.js.map +1 -0
  315. package/.server/typings/hapi-tracing/index.d.js +2 -0
  316. package/.server/typings/hapi-tracing/index.d.js.map +1 -0
  317. package/.server/typings/index.d.js +2 -0
  318. package/.server/typings/index.d.js.map +1 -0
  319. package/.server/typings/joi/index.d.js +2 -0
  320. package/.server/typings/joi/index.d.js.map +1 -0
  321. package/Dockerfile +61 -0
  322. package/LICENCE +8 -0
  323. package/Procfile +1 -0
  324. package/README.md +87 -0
  325. package/babel.config.cjs +55 -0
  326. package/compose/aws.env +4 -0
  327. package/compose/start-localstack.sh +26 -0
  328. package/docker-compose.yaml +86 -0
  329. package/globals.d.ts +1 -0
  330. package/jest.config.cjs +54 -0
  331. package/jest.environment.js +4 -0
  332. package/jest.setup.cjs +14 -0
  333. package/package.json +163 -0
  334. package/postcss.config.js +26 -0
  335. package/sonar-project.properties +17 -0
  336. package/src/client/javascripts/application.js +87 -0
  337. package/src/client/javascripts/file-upload.js +386 -0
  338. package/src/client/stylesheets/_code.scss +33 -0
  339. package/src/client/stylesheets/_govuk-frontend.scss +4 -0
  340. package/src/client/stylesheets/_prose.scss +56 -0
  341. package/src/client/stylesheets/_service-banner.scss +24 -0
  342. package/src/client/stylesheets/_summary-list.scss +28 -0
  343. package/src/client/stylesheets/_tag-env.scss +24 -0
  344. package/src/client/stylesheets/application.scss +14 -0
  345. package/src/common/cookies.js +58 -0
  346. package/src/common/cookies.test.js +23 -0
  347. package/src/common/types.js +5 -0
  348. package/src/config/index.ts +271 -0
  349. package/src/index.ts +31 -0
  350. package/src/server/common/helpers/logging/logger-options.test.ts +50 -0
  351. package/src/server/common/helpers/logging/logger-options.ts +46 -0
  352. package/src/server/common/helpers/logging/logger.ts +7 -0
  353. package/src/server/common/helpers/logging/request-logger.ts +9 -0
  354. package/src/server/common/helpers/logging/request-tracing.js +10 -0
  355. package/src/server/common/helpers/redis-client.js +70 -0
  356. package/src/server/constants.js +1 -0
  357. package/src/server/forms/README.md +10 -0
  358. package/src/server/forms/components.json +1015 -0
  359. package/src/server/forms/report-a-terrorist.json +270 -0
  360. package/src/server/forms/runner-components-test.json +365 -0
  361. package/src/server/forms/test.json +581 -0
  362. package/src/server/index.test.ts +582 -0
  363. package/src/server/index.ts +140 -0
  364. package/src/server/plugins/blankie.test.ts +73 -0
  365. package/src/server/plugins/blankie.ts +48 -0
  366. package/src/server/plugins/crumb.ts +20 -0
  367. package/src/server/plugins/engine/README.md +87 -0
  368. package/src/server/plugins/engine/components/AutocompleteField.test.ts +294 -0
  369. package/src/server/plugins/engine/components/AutocompleteField.ts +49 -0
  370. package/src/server/plugins/engine/components/CheckboxesField.test.ts +379 -0
  371. package/src/server/plugins/engine/components/CheckboxesField.ts +106 -0
  372. package/src/server/plugins/engine/components/ComponentBase.ts +97 -0
  373. package/src/server/plugins/engine/components/ComponentCollection.ts +278 -0
  374. package/src/server/plugins/engine/components/DatePartsField.test.ts +822 -0
  375. package/src/server/plugins/engine/components/DatePartsField.ts +264 -0
  376. package/src/server/plugins/engine/components/Details.test.ts +49 -0
  377. package/src/server/plugins/engine/components/Details.ts +30 -0
  378. package/src/server/plugins/engine/components/EmailAddressField.test.ts +395 -0
  379. package/src/server/plugins/engine/components/EmailAddressField.ts +55 -0
  380. package/src/server/plugins/engine/components/FileUploadField.test.ts +778 -0
  381. package/src/server/plugins/engine/components/FileUploadField.ts +262 -0
  382. package/src/server/plugins/engine/components/FormComponent.ts +249 -0
  383. package/src/server/plugins/engine/components/Html.test.ts +48 -0
  384. package/src/server/plugins/engine/components/Html.ts +29 -0
  385. package/src/server/plugins/engine/components/InsetText.test.ts +48 -0
  386. package/src/server/plugins/engine/components/InsetText.ts +27 -0
  387. package/src/server/plugins/engine/components/List.test.ts +76 -0
  388. package/src/server/plugins/engine/components/List.ts +72 -0
  389. package/src/server/plugins/engine/components/ListFormComponent.ts +140 -0
  390. package/src/server/plugins/engine/components/MonthYearField.test.ts +567 -0
  391. package/src/server/plugins/engine/components/MonthYearField.ts +222 -0
  392. package/src/server/plugins/engine/components/MultilineTextField.test.ts +558 -0
  393. package/src/server/plugins/engine/components/MultilineTextField.ts +138 -0
  394. package/src/server/plugins/engine/components/NumberField.test.ts +701 -0
  395. package/src/server/plugins/engine/components/NumberField.ts +163 -0
  396. package/src/server/plugins/engine/components/RadiosField.test.ts +288 -0
  397. package/src/server/plugins/engine/components/RadiosField.ts +24 -0
  398. package/src/server/plugins/engine/components/SelectField.test.ts +288 -0
  399. package/src/server/plugins/engine/components/SelectField.ts +47 -0
  400. package/src/server/plugins/engine/components/SelectionControlField.ts +43 -0
  401. package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +356 -0
  402. package/src/server/plugins/engine/components/TelephoneNumberField.ts +67 -0
  403. package/src/server/plugins/engine/components/TextField.test.ts +489 -0
  404. package/src/server/plugins/engine/components/TextField.ts +96 -0
  405. package/src/server/plugins/engine/components/UkAddressField.test.ts +623 -0
  406. package/src/server/plugins/engine/components/UkAddressField.ts +172 -0
  407. package/src/server/plugins/engine/components/YesNoField.test.ts +248 -0
  408. package/src/server/plugins/engine/components/YesNoField.ts +31 -0
  409. package/src/server/plugins/engine/components/constants.ts +1 -0
  410. package/src/server/plugins/engine/components/helpers.ts +330 -0
  411. package/src/server/plugins/engine/components/index.ts +24 -0
  412. package/src/server/plugins/engine/components/types.ts +117 -0
  413. package/src/server/plugins/engine/configureEnginePlugin.ts +47 -0
  414. package/src/server/plugins/engine/helpers.test.ts +791 -0
  415. package/src/server/plugins/engine/helpers.ts +379 -0
  416. package/src/server/plugins/engine/index.ts +7 -0
  417. package/src/server/plugins/engine/models/FormModel.test.ts +42 -0
  418. package/src/server/plugins/engine/models/FormModel.ts +443 -0
  419. package/src/server/plugins/engine/models/RepeatingSummaryViewModel.ts +0 -0
  420. package/src/server/plugins/engine/models/Section.ts +0 -0
  421. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +209 -0
  422. package/src/server/plugins/engine/models/SummaryViewModel.ts +220 -0
  423. package/src/server/plugins/engine/models/index.ts +2 -0
  424. package/src/server/plugins/engine/models/types.ts +114 -0
  425. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +143 -0
  426. package/src/server/plugins/engine/outputFormatters/human/v1.ts +73 -0
  427. package/src/server/plugins/engine/outputFormatters/index.test.ts +17 -0
  428. package/src/server/plugins/engine/outputFormatters/index.ts +44 -0
  429. package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +229 -0
  430. package/src/server/plugins/engine/outputFormatters/machine/v1.ts +140 -0
  431. package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +229 -0
  432. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +153 -0
  433. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +1108 -0
  434. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +446 -0
  435. package/src/server/plugins/engine/pageControllers/PageController.test.ts +205 -0
  436. package/src/server/plugins/engine/pageControllers/PageController.ts +176 -0
  437. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +1264 -0
  438. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +561 -0
  439. package/src/server/plugins/engine/pageControllers/README.md +28 -0
  440. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +264 -0
  441. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +458 -0
  442. package/src/server/plugins/engine/pageControllers/StartPageController.ts +18 -0
  443. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +50 -0
  444. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +261 -0
  445. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +28 -0
  446. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +19 -0
  447. package/src/server/plugins/engine/pageControllers/helpers.test.ts +198 -0
  448. package/src/server/plugins/engine/pageControllers/helpers.ts +101 -0
  449. package/src/server/plugins/engine/pageControllers/index.ts +10 -0
  450. package/src/server/plugins/engine/pageControllers/validationOptions.ts +89 -0
  451. package/src/server/plugins/engine/plugin.ts +673 -0
  452. package/src/server/plugins/engine/services/formSubmissionService.js +46 -0
  453. package/src/server/plugins/engine/services/formsService.js +46 -0
  454. package/src/server/plugins/engine/services/formsService.test.js +90 -0
  455. package/src/server/plugins/engine/services/index.js +3 -0
  456. package/src/server/plugins/engine/services/notifyService.test.ts +132 -0
  457. package/src/server/plugins/engine/services/notifyService.ts +64 -0
  458. package/src/server/plugins/engine/services/uploadService.js +60 -0
  459. package/src/server/plugins/engine/types.ts +315 -0
  460. package/src/server/plugins/engine/views/components/autocompletefield.html +5 -0
  461. package/src/server/plugins/engine/views/components/checkboxesfield.html +5 -0
  462. package/src/server/plugins/engine/views/components/datepartsfield.html +5 -0
  463. package/src/server/plugins/engine/views/components/details.html +6 -0
  464. package/src/server/plugins/engine/views/components/emailaddressfield.html +5 -0
  465. package/src/server/plugins/engine/views/components/fileuploadfield-key.html +8 -0
  466. package/src/server/plugins/engine/views/components/fileuploadfield-value.html +3 -0
  467. package/src/server/plugins/engine/views/components/fileuploadfield.html +24 -0
  468. package/src/server/plugins/engine/views/components/html.html +3 -0
  469. package/src/server/plugins/engine/views/components/insettext.html +7 -0
  470. package/src/server/plugins/engine/views/components/list.html +36 -0
  471. package/src/server/plugins/engine/views/components/monthyearfield.html +5 -0
  472. package/src/server/plugins/engine/views/components/multilinetextfield.html +10 -0
  473. package/src/server/plugins/engine/views/components/numberfield.html +5 -0
  474. package/src/server/plugins/engine/views/components/radiosfield.html +5 -0
  475. package/src/server/plugins/engine/views/components/selectfield.html +5 -0
  476. package/src/server/plugins/engine/views/components/telephonenumberfield.html +5 -0
  477. package/src/server/plugins/engine/views/components/textfield.html +5 -0
  478. package/src/server/plugins/engine/views/components/ukaddressfield.html +25 -0
  479. package/src/server/plugins/engine/views/components/yesnofield.html +5 -0
  480. package/src/server/plugins/engine/views/file-upload.html +45 -0
  481. package/src/server/plugins/engine/views/index.html +39 -0
  482. package/src/server/plugins/engine/views/item-delete.html +56 -0
  483. package/src/server/plugins/engine/views/partials/components.html +6 -0
  484. package/src/server/plugins/engine/views/partials/conditional-components.html +3 -0
  485. package/src/server/plugins/engine/views/partials/debug.html +44 -0
  486. package/src/server/plugins/engine/views/partials/form.html +15 -0
  487. package/src/server/plugins/engine/views/partials/heading.html +16 -0
  488. package/src/server/plugins/engine/views/partials/preview-banner.html +32 -0
  489. package/src/server/plugins/engine/views/partials/preview-banner.test.js +122 -0
  490. package/src/server/plugins/engine/views/partials/warn-missing-notification-email.html +10 -0
  491. package/src/server/plugins/engine/views/repeat-list-summary.html +53 -0
  492. package/src/server/plugins/errorPages.ts +58 -0
  493. package/src/server/plugins/nunjucks/context.js +88 -0
  494. package/src/server/plugins/nunjucks/context.test.js +142 -0
  495. package/src/server/plugins/nunjucks/enviroment.test.js +201 -0
  496. package/src/server/plugins/nunjucks/environment.js +116 -0
  497. package/src/server/plugins/nunjucks/filters/answer.js +27 -0
  498. package/src/server/plugins/nunjucks/filters/answer.test.js +89 -0
  499. package/src/server/plugins/nunjucks/filters/evaluate.js +21 -0
  500. package/src/server/plugins/nunjucks/filters/field.js +28 -0
  501. package/src/server/plugins/nunjucks/filters/field.test.js +75 -0
  502. package/src/server/plugins/nunjucks/filters/highlight.js +11 -0
  503. package/src/server/plugins/nunjucks/filters/href.js +30 -0
  504. package/src/server/plugins/nunjucks/filters/href.test.js +80 -0
  505. package/src/server/plugins/nunjucks/filters/index.js +8 -0
  506. package/src/server/plugins/nunjucks/filters/inspect.js +15 -0
  507. package/src/server/plugins/nunjucks/filters/page.js +24 -0
  508. package/src/server/plugins/nunjucks/filters/page.test.js +65 -0
  509. package/src/server/plugins/nunjucks/index.js +3 -0
  510. package/src/server/plugins/nunjucks/plugin.js +40 -0
  511. package/src/server/plugins/nunjucks/render.js +42 -0
  512. package/src/server/plugins/nunjucks/types.js +40 -0
  513. package/src/server/plugins/pulse.ts +11 -0
  514. package/src/server/plugins/router.ts +201 -0
  515. package/src/server/plugins/session.ts +28 -0
  516. package/src/server/routes/health.js +13 -0
  517. package/src/server/routes/health.test.js +35 -0
  518. package/src/server/routes/index.test.ts +125 -0
  519. package/src/server/routes/index.ts +2 -0
  520. package/src/server/routes/public.ts +47 -0
  521. package/src/server/routes/types.ts +48 -0
  522. package/src/server/schemas/index.ts +34 -0
  523. package/src/server/secure-context.js +43 -0
  524. package/src/server/services/cacheService.test.ts +276 -0
  525. package/src/server/services/cacheService.ts +131 -0
  526. package/src/server/services/httpService.test.js +491 -0
  527. package/src/server/services/httpService.ts +50 -0
  528. package/src/server/services/index.ts +1 -0
  529. package/src/server/types.ts +54 -0
  530. package/src/server/utils/notify.test.ts +37 -0
  531. package/src/server/utils/notify.ts +50 -0
  532. package/src/server/utils/secure-context/get-trust-store-certs.js +11 -0
  533. package/src/server/utils/secure-context/get-trust-store-certs.test.js +19 -0
  534. package/src/server/utils/utils.js +24 -0
  535. package/src/server/utils/utils.test.js +54 -0
  536. package/src/server/views/404.html +16 -0
  537. package/src/server/views/500.html +19 -0
  538. package/src/server/views/components/debug/macro.njk +3 -0
  539. package/src/server/views/components/debug/template.njk +13 -0
  540. package/src/server/views/components/service-banner/macro.njk +3 -0
  541. package/src/server/views/components/service-banner/template.njk +20 -0
  542. package/src/server/views/components/service-banner/template.test.js +43 -0
  543. package/src/server/views/components/tag-env/macro.njk +3 -0
  544. package/src/server/views/components/tag-env/template.njk +30 -0
  545. package/src/server/views/components/tag-env/template.test.js +66 -0
  546. package/src/server/views/confirmation.html +19 -0
  547. package/src/server/views/help/accessibility-statement.html +58 -0
  548. package/src/server/views/help/cookie-preferences.html +57 -0
  549. package/src/server/views/help/cookies.html +71 -0
  550. package/src/server/views/help/get-support.html +37 -0
  551. package/src/server/views/help/privacy-notice.html +68 -0
  552. package/src/server/views/help/terms-and-conditions.html +83 -0
  553. package/src/server/views/layout.html +199 -0
  554. package/src/server/views/summary.html +50 -0
  555. package/src/typings/hapi/index.d.ts +95 -0
  556. package/src/typings/hapi-tracing/index.d.ts +6 -0
  557. package/src/typings/index.d.ts +3 -0
  558. package/src/typings/joi/index.d.ts +22 -0
  559. package/stylelint.config.js +10 -0
  560. package/test/client/javascripts/file-upload.test.js +1197 -0
  561. package/test/condition/checkboxes.test.js +112 -0
  562. package/test/condition/radios.test.js +112 -0
  563. package/test/condition/text.test.js +103 -0
  564. package/test/fixtures/assets-manifest.json +4 -0
  565. package/test/fixtures/form.js +86 -0
  566. package/test/fixtures/index.js +2 -0
  567. package/test/fixtures/list.js +92 -0
  568. package/test/form/cookies.test.js +338 -0
  569. package/test/form/csrf.test.js +87 -0
  570. package/test/form/definitions/basic.js +101 -0
  571. package/test/form/definitions/blank.js +10 -0
  572. package/test/form/definitions/checkboxes.json +88 -0
  573. package/test/form/definitions/components.json +452 -0
  574. package/test/form/definitions/conditional-reveal.js +140 -0
  575. package/test/form/definitions/conditions-basic.js +187 -0
  576. package/test/form/definitions/conditions-complex.js +338 -0
  577. package/test/form/definitions/conditions-dates.js +78 -0
  578. package/test/form/definitions/conditions-escaping.js +143 -0
  579. package/test/form/definitions/demo-cph-number.js +3099 -0
  580. package/test/form/definitions/feedback.json +45 -0
  581. package/test/form/definitions/fields-optional.js +402 -0
  582. package/test/form/definitions/fields-required.js +402 -0
  583. package/test/form/definitions/file-upload-basic.js +44 -0
  584. package/test/form/definitions/file-upload.js +66 -0
  585. package/test/form/definitions/minimal.js +39 -0
  586. package/test/form/definitions/phase-alpha.json +33 -0
  587. package/test/form/definitions/phase-default.json +26 -0
  588. package/test/form/definitions/radios.json +88 -0
  589. package/test/form/definitions/repeat-mixed.js +54 -0
  590. package/test/form/definitions/repeat.js +70 -0
  591. package/test/form/definitions/status.json +126 -0
  592. package/test/form/definitions/templates.js +183 -0
  593. package/test/form/definitions/test.json +581 -0
  594. package/test/form/definitions/text.json +75 -0
  595. package/test/form/definitions/titles.json +170 -0
  596. package/test/form/definitions.test.js +47 -0
  597. package/test/form/exit-page.test.js +210 -0
  598. package/test/form/feedback.test.js +68 -0
  599. package/test/form/fields-optional.test.js +237 -0
  600. package/test/form/fields-required.test.js +294 -0
  601. package/test/form/file-upload.test.js +313 -0
  602. package/test/form/govuk-notify.test.js +449 -0
  603. package/test/form/journey-basic.test.js +444 -0
  604. package/test/form/persist-files.test.js +227 -0
  605. package/test/form/phase-banner.test.js +71 -0
  606. package/test/form/repeat.test.js +628 -0
  607. package/test/form/summary-submission-email.test.js +95 -0
  608. package/test/form/template.test.js +288 -0
  609. package/test/form/titles.test.js +204 -0
  610. package/test/helpers/component-helpers.js +74 -0
  611. package/test/utils/get-cookie.js +42 -0
  612. package/test/utils/get-form-definitions.js +18 -0
  613. package/tmp.pdf +1 -0
  614. package/tsconfig.json +28 -0
  615. package/webpack.config.js +208 -0
@@ -0,0 +1,386 @@
1
+ export const MAX_POLLING_DURATION = 300 // 5 minutes
2
+
3
+ /**
4
+ * Creates or updates status announcer for screen readers
5
+ * @param {HTMLElement | null} form - The form element
6
+ * @param {HTMLElement | null} fileCountP - The file count paragraph element
7
+ * @returns {HTMLElement} The status announcer element
8
+ */
9
+ function createOrUpdateStatusAnnouncer(form, fileCountP) {
10
+ let statusAnnouncer = form?.querySelector('#statusInformation')
11
+
12
+ if (!statusAnnouncer) {
13
+ statusAnnouncer = document.createElement('div')
14
+ statusAnnouncer.id = 'statusInformation'
15
+ statusAnnouncer.className = 'govuk-visually-hidden'
16
+ statusAnnouncer.setAttribute('aria-live', 'polite')
17
+
18
+ // multiple fallbacks to ensure the status announcer is always added to the DOM
19
+ // this helps with cross-browser compatibility and unexpected DOM structures encountered during QA
20
+ try {
21
+ addStatusAnnouncerToDOM(
22
+ asHTMLElement(form),
23
+ asHTMLElement(fileCountP),
24
+ asHTMLElement(statusAnnouncer)
25
+ )
26
+ } catch {
27
+ try {
28
+ form?.appendChild(statusAnnouncer)
29
+ } catch {
30
+ document.body.appendChild(statusAnnouncer)
31
+ }
32
+ }
33
+ }
34
+
35
+ return /** @type {HTMLElement} */ (statusAnnouncer)
36
+ }
37
+
38
+ /**
39
+ * Helper function to add the status announcer to the DOM
40
+ * @param {HTMLElement} form - The form element
41
+ * @param {HTMLElement | null} fileCountP - The file count paragraph element
42
+ * @param {HTMLElement} statusAnnouncer - The status announcer element to add
43
+ */
44
+ function addStatusAnnouncerToDOM(form, fileCountP, statusAnnouncer) {
45
+ if (fileCountP?.nextSibling && fileCountP.parentNode === form) {
46
+ form.insertBefore(statusAnnouncer, fileCountP.nextSibling)
47
+ return
48
+ }
49
+
50
+ const parentElement = fileCountP?.parentNode ?? form
51
+ parentElement.appendChild(statusAnnouncer)
52
+ }
53
+
54
+ /**
55
+ * Finds an existing summary list or creates a new one
56
+ * @param {HTMLFormElement} form - The form element
57
+ * @param {HTMLElement} fileCountP - The file count paragraph element
58
+ * @returns {HTMLElement} The summary list element
59
+ */
60
+ function findOrCreateSummaryList(form, fileCountP) {
61
+ let summaryList = form.querySelector('dl.govuk-summary-list')
62
+
63
+ if (!summaryList) {
64
+ summaryList = document.createElement('dl')
65
+ summaryList.className = 'govuk-summary-list govuk-summary-list--long-key'
66
+
67
+ const continueButton = form.querySelector('.govuk-button')
68
+
69
+ if (continueButton) {
70
+ form.insertBefore(summaryList, continueButton)
71
+ } else {
72
+ form.insertBefore(summaryList, fileCountP.nextSibling)
73
+ }
74
+ }
75
+
76
+ return /** @type {HTMLElement} */ (summaryList)
77
+ }
78
+
79
+ /**
80
+ * Creates a file row element for the summary list
81
+ * @param {File | null} selectedFile - The selected file
82
+ * @param {string} statusText - The status to display
83
+ * @returns {HTMLElement} The created row element
84
+ */
85
+ function createFileRow(selectedFile, statusText) {
86
+ const row = document.createElement('div')
87
+ row.className = 'govuk-summary-list__row'
88
+ row.setAttribute('data-filename', selectedFile?.name ?? '')
89
+ row.innerHTML = `
90
+ <dt class="govuk-summary-list__key">
91
+ ${selectedFile?.name ?? ''}
92
+ </dt>
93
+ <dd class="govuk-summary-list__value">
94
+ <strong class="govuk-tag govuk-tag--yellow">${statusText}</strong>
95
+ </dd>
96
+ <dd class="govuk-summary-list__actions">
97
+ </dd>
98
+ `
99
+ return row
100
+ }
101
+
102
+ /**
103
+ * Renders or updates the file summary box for the selected file
104
+ * @param {File | null} selectedFile - The selected file
105
+ * @param {string} statusText - The status to display
106
+ * @param {HTMLElement} form - The form element
107
+ */
108
+ function renderSummary(selectedFile, statusText, form) {
109
+ const container = document.getElementById('uploadedFilesContainer')
110
+ const uploadForm = container ? container.closest('form') : null
111
+
112
+ if (!uploadForm || !(uploadForm instanceof HTMLFormElement)) {
113
+ return
114
+ }
115
+
116
+ const fileCountP = uploadForm.querySelector('p.govuk-body')
117
+
118
+ if (!fileCountP) {
119
+ return
120
+ }
121
+
122
+ const statusAnnouncer = createOrUpdateStatusAnnouncer(
123
+ /** @type {HTMLElement} */ (uploadForm),
124
+ /** @type {HTMLElement | null} */ (fileCountP)
125
+ )
126
+
127
+ const fileInput = form.querySelector('input[type="file"]')
128
+
129
+ if (fileInput) {
130
+ fileInput.setAttribute('aria-describedby', 'statusInformation')
131
+ }
132
+
133
+ const summaryList = findOrCreateSummaryList(
134
+ /** @type {HTMLFormElement} */ (uploadForm),
135
+ /** @type {HTMLElement} */ (fileCountP)
136
+ )
137
+
138
+ const existingRow = document.querySelector(
139
+ `[data-filename="${selectedFile?.name}"]`
140
+ )
141
+
142
+ if (existingRow) {
143
+ existingRow.remove()
144
+ }
145
+
146
+ const row = createFileRow(selectedFile, statusText)
147
+ summaryList.insertBefore(row, summaryList.firstChild)
148
+ statusAnnouncer.textContent = `${selectedFile?.name ?? ''} ${statusText}`
149
+ }
150
+
151
+ /**
152
+ * Shows an error message using the GOV.UK error summary component
153
+ * @param {string} message - The error message to display
154
+ * @param {HTMLElement | null} errorSummary - The error summary container
155
+ * @param {HTMLInputElement} fileInput - The file input element
156
+ * @returns {void}
157
+ */
158
+ function showError(message, errorSummary, fileInput) {
159
+ if (errorSummary) {
160
+ errorSummary.innerHTML = `
161
+ <div class="govuk-error-summary" data-module="govuk-error-summary">
162
+ <div role="alert">
163
+ <h2 class="govuk-error-summary__title" id="error-summary-title">
164
+ There is a problem
165
+ </h2>
166
+ <div class="govuk-error-summary__body">
167
+ <ul class="govuk-list govuk-error-summary__list">
168
+ <li>
169
+ <a href="#file-upload">${message}</a>
170
+ </li>
171
+ </ul>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ `
176
+ fileInput.setAttribute('aria-describedby', 'error-summary-title')
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Helper to safely convert an Element to HTMLElement
182
+ * @param {Element | null} element - The element to convert
183
+ */
184
+ function asHTMLElement(element) {
185
+ return /** @type {HTMLElement} */ (element)
186
+ }
187
+
188
+ function reloadPage() {
189
+ window.history.replaceState(null, '', window.location.href)
190
+ window.location.href = window.location.pathname
191
+ }
192
+
193
+ /**
194
+ * Polls the upload status endpoint until the file is ready or timeout occurs
195
+ * @param {string} uploadId - The upload ID to check
196
+ */
197
+ function pollUploadStatus(uploadId) {
198
+ let attempts = 0
199
+ const interval = setInterval(() => {
200
+ attempts++
201
+
202
+ if (attempts >= MAX_POLLING_DURATION) {
203
+ clearInterval(interval)
204
+ reloadPage()
205
+ return
206
+ }
207
+
208
+ fetch(`/upload-status/${uploadId}`, {
209
+ headers: {
210
+ Accept: 'application/json'
211
+ }
212
+ })
213
+ .then((response) => {
214
+ if (!response.ok) {
215
+ throw new Error('Network response was not ok')
216
+ }
217
+ return response.json()
218
+ })
219
+ .then((data) => {
220
+ if (data.uploadStatus === 'ready') {
221
+ clearInterval(interval)
222
+ reloadPage()
223
+ }
224
+ })
225
+ .catch(() => {
226
+ clearInterval(interval)
227
+ reloadPage()
228
+ })
229
+ }, 1000)
230
+ }
231
+
232
+ /**
233
+ * Handle standard form submission for file upload
234
+ * @param {HTMLFormElement} formElement - The form element
235
+ * @param {HTMLInputElement} fileInput - The file input element
236
+ * @param {HTMLButtonElement} uploadButton - The upload button
237
+ * @param {HTMLButtonElement} continueButton - The continue button
238
+ * @param {File | null} selectedFile - The selected file
239
+ */
240
+ function handleStandardFormSubmission(
241
+ formElement,
242
+ fileInput,
243
+ uploadButton,
244
+ continueButton,
245
+ selectedFile
246
+ ) {
247
+ renderSummary(selectedFile, 'Uploading…', formElement)
248
+
249
+ fileInput.focus()
250
+
251
+ setTimeout(() => {
252
+ fileInput.disabled = true
253
+ uploadButton.disabled = true
254
+ continueButton.disabled = true
255
+ }, 100)
256
+ }
257
+
258
+ /**
259
+ * Handle AJAX form submission with upload ID
260
+ * @param {Event} event - The click event
261
+ * @param {HTMLFormElement} formElement - The form element
262
+ * @param {HTMLInputElement} fileInput - The file input element
263
+ * @param {HTMLButtonElement} uploadButton - The upload button
264
+ * @param {HTMLElement | null} errorSummary - The error summary container
265
+ * @param {string | undefined} uploadId - The upload ID
266
+ * @returns {boolean} Whether the event was handled
267
+ */
268
+ function handleAjaxFormSubmission(
269
+ event,
270
+ formElement,
271
+ fileInput,
272
+ uploadButton,
273
+ errorSummary,
274
+ uploadId
275
+ ) {
276
+ if (!formElement.action || !uploadId) {
277
+ return false
278
+ }
279
+
280
+ event.preventDefault()
281
+
282
+ const formData = new FormData(formElement)
283
+ const isLocalDev = !!formElement.dataset.proxyUrl
284
+ const uploadUrl = formElement.dataset.proxyUrl ?? formElement.action
285
+
286
+ const fetchOptions = /** @type {RequestInit} */ ({
287
+ method: 'POST',
288
+ body: formData,
289
+ redirect: isLocalDev ? 'follow' : 'manual' // follow mode if local development with the proxy
290
+ })
291
+
292
+ // no-cors mode if needed local development with the proxy
293
+ if (isLocalDev) {
294
+ fetchOptions.mode = 'no-cors'
295
+ }
296
+
297
+ fetch(uploadUrl, fetchOptions)
298
+ .then(() => {
299
+ pollUploadStatus(uploadId)
300
+ })
301
+ .catch(() => {
302
+ fileInput.disabled = false
303
+ uploadButton.disabled = false
304
+
305
+ showError(
306
+ 'There was a problem uploading the file',
307
+ errorSummary,
308
+ fileInput
309
+ )
310
+
311
+ return null
312
+ })
313
+
314
+ return true
315
+ }
316
+
317
+ export function initFileUpload() {
318
+ const form = document.querySelector('form:has(input[type="file"])')
319
+ /** @type {HTMLInputElement | null} */
320
+ const fileInput = form ? form.querySelector('input[type="file"]') : null
321
+ /** @type {HTMLButtonElement | null} */
322
+ const uploadButton = form ? form.querySelector('.upload-file-button') : null
323
+ const continueButton =
324
+ /** @type {HTMLButtonElement} */ (
325
+ Array.from(document.querySelectorAll('button.govuk-button')).find(
326
+ (button) => button.textContent?.trim() === 'Continue'
327
+ )
328
+ ) ?? null
329
+
330
+ const errorSummary = document.querySelector('.govuk-error-summary-container')
331
+
332
+ if (!form || !fileInput || !uploadButton) {
333
+ return
334
+ }
335
+
336
+ const formElement = /** @type {HTMLFormElement} */ (form)
337
+ /** @type {File | null} */
338
+ let selectedFile = null
339
+ let isSubmitting = false
340
+ const uploadId = formElement.dataset.uploadId
341
+
342
+ fileInput.addEventListener('change', () => {
343
+ if (errorSummary) {
344
+ errorSummary.innerHTML = ''
345
+ }
346
+ if (fileInput.files && fileInput.files.length > 0) {
347
+ selectedFile = fileInput.files[0]
348
+ }
349
+ })
350
+
351
+ uploadButton.addEventListener('click', (event) => {
352
+ if (!selectedFile) {
353
+ event.preventDefault()
354
+ showError(
355
+ 'Select a file',
356
+ /** @type {HTMLElement | null} */ (errorSummary),
357
+ fileInput
358
+ )
359
+ return
360
+ }
361
+
362
+ if (isSubmitting) {
363
+ event.preventDefault()
364
+ return
365
+ }
366
+
367
+ isSubmitting = true
368
+
369
+ handleStandardFormSubmission(
370
+ formElement,
371
+ fileInput,
372
+ uploadButton,
373
+ continueButton,
374
+ selectedFile
375
+ )
376
+
377
+ handleAjaxFormSubmission(
378
+ event,
379
+ formElement,
380
+ fileInput,
381
+ uploadButton,
382
+ /** @type {HTMLElement | null} */ (errorSummary),
383
+ uploadId
384
+ )
385
+ })
386
+ }
@@ -0,0 +1,33 @@
1
+ @use "govuk-frontend" as *;
2
+ @use "pkg:highlight.js/styles/github.css";
3
+
4
+ .app-code {
5
+ @include govuk-responsive-margin(8, "bottom");
6
+
7
+ &__container {
8
+ display: block;
9
+ margin: 0;
10
+ padding: govuk-spacing(4);
11
+ overflow-x: auto;
12
+ border: $govuk-focus-width solid transparent;
13
+ outline: 1px solid transparent;
14
+ background-color: govuk-colour("light-grey");
15
+ @include govuk-responsive-margin(4, "bottom");
16
+
17
+ &:focus {
18
+ border: $govuk-focus-width solid $govuk-input-border-colour;
19
+ outline: $govuk-focus-width solid $govuk-focus-colour;
20
+ }
21
+ }
22
+
23
+ &:last-child &__container {
24
+ margin-bottom: 0;
25
+ }
26
+ }
27
+
28
+ pre,
29
+ code {
30
+ $app-code-font: ui-monospace, menlo, "Cascadia Mono", "Segoe UI Mono", consolas, "Liberation Mono", monospace;
31
+ @include govuk-typography-common($font-family: $app-code-font);
32
+ @include govuk-font-size($size: 16);
33
+ }
@@ -0,0 +1,4 @@
1
+ @forward "pkg:govuk-frontend" with (
2
+ $govuk-assets-path: "/assets/",
3
+ $govuk-new-organisation-colours: true
4
+ );
@@ -0,0 +1,56 @@
1
+ @use "govuk-frontend" as *;
2
+
3
+ .app-prose-scope {
4
+ // @extend inheritance
5
+
6
+ // Contextual heading and paragraph combinations are inherited
7
+ // through the use of @extend
8
+
9
+ h1 {
10
+ @extend %govuk-heading-xl;
11
+ }
12
+
13
+ h2 {
14
+ @extend %govuk-heading-l;
15
+ }
16
+
17
+ h3 {
18
+ @extend %govuk-heading-m;
19
+ }
20
+
21
+ h4 {
22
+ @extend %govuk-heading-s;
23
+ }
24
+
25
+ p {
26
+ @extend %govuk-body-m;
27
+ }
28
+
29
+ strong,
30
+ b {
31
+ @include govuk-typography-weight-bold;
32
+ }
33
+
34
+ ul,
35
+ ol {
36
+ @extend %govuk-list;
37
+ }
38
+
39
+ ol {
40
+ @extend %govuk-list--number;
41
+ }
42
+
43
+ ul {
44
+ @extend %govuk-list--bullet;
45
+ }
46
+
47
+ a {
48
+ @extend %govuk-link;
49
+ }
50
+
51
+ hr {
52
+ @extend %govuk-section-break;
53
+ @extend %govuk-section-break--visible;
54
+ @extend %govuk-section-break--xl;
55
+ }
56
+ }
@@ -0,0 +1,24 @@
1
+ @use "govuk-frontend" as *;
2
+
3
+ .app-service-banner {
4
+ display: block;
5
+ background: govuk-colour("yellow");
6
+ color: govuk-colour("black");
7
+
8
+ @include govuk-responsive-padding(2, "top");
9
+ @include govuk-responsive-padding(2, "bottom");
10
+
11
+ &__content {
12
+ margin: 0;
13
+ }
14
+
15
+ .govuk-warning-text__text {
16
+ font-weight: $govuk-font-weight-regular;
17
+ }
18
+
19
+ .govuk-skip-link:active + &,
20
+ .govuk-skip-link:focus + & {
21
+ // Ofset service banner from skip link
22
+ margin-top: $govuk-border-width + $govuk-focus-width;
23
+ }
24
+ }
@@ -0,0 +1,28 @@
1
+ @use "govuk-frontend" as *;
2
+
3
+ // Taken from https://github.com/hmrc/hmrc-frontend/blob/main/src/components/summary-list/_summary-list.scss
4
+ @include govuk-media-query($from: tablet) {
5
+ .govuk-summary-list.govuk-summary-list--long-key {
6
+ .govuk-summary-list__row {
7
+ display: flex;
8
+ justify-content: space-between;
9
+ flex-wrap: wrap;
10
+ }
11
+
12
+ .govuk-summary-list__key {
13
+ flex-grow: 1;
14
+ margin-bottom: 0;
15
+ }
16
+
17
+ .govuk-summary-list__actions {
18
+ width: auto;
19
+ margin-bottom: 0;
20
+ }
21
+ }
22
+
23
+ .govuk-summary-list.govuk-summary-list--long-actions {
24
+ .govuk-summary-list__actions {
25
+ width: auto;
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ @use "govuk-frontend" as *;
2
+
3
+ .app-tag--env {
4
+ margin: -6px 0 0;
5
+ vertical-align: top;
6
+
7
+ // Override product name font size
8
+ @include govuk-font-size($size: 16);
9
+
10
+ // Align with product name
11
+ @include govuk-media-query($from: tablet) {
12
+ margin: -3px 0 0;
13
+ }
14
+
15
+ // Override service header alignment
16
+ .cross-service-header & {
17
+ margin: -2px 0 0 govuk-spacing(1);
18
+
19
+ // Align with product name
20
+ @include govuk-media-query($from: tablet) {
21
+ margin: 0 0 0 govuk-spacing(2);
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,14 @@
1
+ @use "govuk-frontend" as *;
2
+ @use "pkg:accessible-autocomplete";
3
+ @use "code";
4
+ @use "prose";
5
+ @use "service-banner";
6
+ @use "summary-list";
7
+ @use "tag-env";
8
+
9
+ // Use default GDS Transport font for autocomplete
10
+ .autocomplete__hint,
11
+ .autocomplete__input,
12
+ .autocomplete__option {
13
+ @include govuk-typography-common;
14
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ @type {CookieConsent}
3
+ */
4
+ export const defaultConsent = {
5
+ analytics: null,
6
+ dismissed: false
7
+ }
8
+
9
+ /**
10
+ * Parses the cookie consent policy
11
+ * @param {string} value
12
+ */
13
+ export function parseCookieConsent(value) {
14
+ /** @type {CookieConsent} */
15
+ let cookieConsent
16
+
17
+ try {
18
+ const encodedValue = decodeURIComponent(value)
19
+
20
+ // eslint-disable-next-line -- Allow JSON type 'any'
21
+ const decodedValue = JSON.parse(encodedValue)
22
+
23
+ if (isValidConsent(decodedValue)) {
24
+ cookieConsent = decodedValue
25
+ } else {
26
+ cookieConsent = defaultConsent
27
+ }
28
+ } catch {
29
+ cookieConsent = defaultConsent
30
+ }
31
+
32
+ return cookieConsent
33
+ }
34
+
35
+ /**
36
+ * Serialises the cookie consent policy
37
+ * @param {CookieConsent} consent
38
+ * @returns {string} cookie value
39
+ */
40
+ export function serialiseCookieConsent(consent) {
41
+ return encodeURIComponent(JSON.stringify(consent))
42
+ }
43
+
44
+ /**
45
+ * @param {unknown} consent
46
+ * @returns {consent is CookieConsent}
47
+ */
48
+ function isValidConsent(consent) {
49
+ if (consent === null || Array.isArray(consent)) {
50
+ return false
51
+ }
52
+
53
+ return typeof consent === 'object' && 'analytics' in consent
54
+ }
55
+
56
+ /**
57
+ * @import {CookieConsent} from '~/src/common/types.js'
58
+ */
@@ -0,0 +1,23 @@
1
+ import { parseCookieConsent } from '~/src/common/cookies.js'
2
+
3
+ describe('cookies', () => {
4
+ it('parses a valid policy', () => {
5
+ expect(parseCookieConsent('{"analytics":true}')).toEqual({
6
+ analytics: true
7
+ })
8
+ })
9
+
10
+ it.each([
11
+ "['not', 'an', 'object']",
12
+ '{{ not: "an object" }}',
13
+ '{ additional: AAA }',
14
+ '{ marketing: 100 }',
15
+ '',
16
+ 'null'
17
+ ])('converts a malformed policy to the default', (value) => {
18
+ expect(parseCookieConsent(value)).toEqual({
19
+ analytics: null,
20
+ dismissed: false
21
+ })
22
+ })
23
+ })
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @typedef CookieConsent
3
+ * @property {boolean | null} analytics - whether analytics cookies are allowed
4
+ * @property {boolean} dismissed - whether cookie banner has been dismissed
5
+ */