@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 @@
1
+ {"version":3,"file":"formSubmissionService.js","names":["config","postJson","submissionUrl","get","persistFiles","files","persistedRetrievalKey","postJsonByType","payload","result","submit","data"],"sources":["../../../../../src/server/plugins/engine/services/formSubmissionService.js"],"sourcesContent":["import { config } from '~/src/config/index.js'\nimport { postJson } from '~/src/server/services/httpService.js'\n\nconst submissionUrl = config.get('submissionUrl')\n\n/**\n * Persist files by extending the time-to-live to 30 days\n * @param {{fileId: string, initiatedRetrievalKey: string}[]} files - batch of files to persist\n * @param {string} persistedRetrievalKey - final retrieval key when submitting\n */\nexport async function persistFiles(files, persistedRetrievalKey) {\n const postJsonByType = /** @type {typeof postJson<object>} */ (postJson)\n\n const payload = {\n files,\n persistedRetrievalKey\n }\n\n const result = await postJsonByType(`${submissionUrl}/files/persist`, {\n payload\n })\n\n return result\n}\n\n/**\n * Submit form\n * @param {SubmitPayload} data - submission data\n */\nexport async function submit(data) {\n const postJsonByType = /** @type {typeof postJson<SubmitResponsePayload>} */ (\n postJson\n )\n\n const payload = data\n\n const result = await postJsonByType(`${submissionUrl}/submit`, {\n payload\n })\n\n return result.payload\n}\n\n/**\n * @import { SubmitPayload, SubmitResponsePayload } from '@defra/forms-model'\n */\n"],"mappings":"AAAA,SAASA,MAAM;AACf,SAASC,QAAQ;AAEjB,MAAMC,aAAa,GAAGF,MAAM,CAACG,GAAG,CAAC,eAAe,CAAC;;AAEjD;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,YAAYA,CAACC,KAAK,EAAEC,qBAAqB,EAAE;EAC/D,MAAMC,cAAc,GAAG,sCAAwCN,QAAS;EAExE,MAAMO,OAAO,GAAG;IACdH,KAAK;IACLC;EACF,CAAC;EAED,MAAMG,MAAM,GAAG,MAAMF,cAAc,CAAC,GAAGL,aAAa,gBAAgB,EAAE;IACpEM;EACF,CAAC,CAAC;EAEF,OAAOC,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA,OAAO,eAAeC,MAAMA,CAACC,IAAI,EAAE;EACjC,MAAMJ,cAAc,GAAG;EACrBN,QACD;EAED,MAAMO,OAAO,GAAGG,IAAI;EAEpB,MAAMF,MAAM,GAAG,MAAMF,cAAc,CAAC,GAAGL,aAAa,SAAS,EAAE;IAC7DM;EACF,CAAC,CAAC;EAEF,OAAOC,MAAM,CAACD,OAAO;AACvB;;AAEA;AACA;AACA","ignoreList":[]}
@@ -0,0 +1,41 @@
1
+ import { formMetadataSchema } from '@defra/forms-model';
2
+ import { config } from "../../../../config/index.js";
3
+ import { FormStatus } from "../../../routes/types.js";
4
+ import { getJson } from "../../../services/httpService.js";
5
+
6
+ /**
7
+ * Retrieves a form definition from the form manager for a given slug
8
+ * @param {string} slug - the slug of the form
9
+ */
10
+ export async function getFormMetadata(slug) {
11
+ const getJsonByType = /** @type {typeof getJson<FormMetadata>} */getJson;
12
+ const {
13
+ payload: metadata
14
+ } = await getJsonByType(`${config.get('managerUrl')}/forms/slug/${slug}`);
15
+
16
+ // Run it through the schema to coerce dates
17
+ const result = formMetadataSchema.validate(metadata);
18
+ if (result.error) {
19
+ throw result.error;
20
+ }
21
+ return result.value;
22
+ }
23
+
24
+ /**
25
+ * Retrieves a form definition from the form manager for a given id
26
+ * @param {string} id - the id of the form
27
+ * @param {FormStatus} state - the state of the form
28
+ */
29
+ export async function getFormDefinition(id, state) {
30
+ const getJsonByType = /** @type {typeof getJson<FormDefinition>} */getJson;
31
+ const suffix = state === FormStatus.Draft ? `/${state}` : '';
32
+ const {
33
+ payload: definition
34
+ } = await getJsonByType(`${config.get('managerUrl')}/forms/${id}/definition${suffix}`);
35
+ return definition;
36
+ }
37
+
38
+ /**
39
+ * @import { FormDefinition, FormMetadata } from '@defra/forms-model'
40
+ */
41
+ //# sourceMappingURL=formsService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formsService.js","names":["formMetadataSchema","config","FormStatus","getJson","getFormMetadata","slug","getJsonByType","payload","metadata","get","result","validate","error","value","getFormDefinition","id","state","suffix","Draft","definition"],"sources":["../../../../../src/server/plugins/engine/services/formsService.js"],"sourcesContent":["import { formMetadataSchema } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { getJson } from '~/src/server/services/httpService.js'\n\n/**\n * Retrieves a form definition from the form manager for a given slug\n * @param {string} slug - the slug of the form\n */\nexport async function getFormMetadata(slug) {\n const getJsonByType = /** @type {typeof getJson<FormMetadata>} */ (getJson)\n\n const { payload: metadata } = await getJsonByType(\n `${config.get('managerUrl')}/forms/slug/${slug}`\n )\n\n // Run it through the schema to coerce dates\n const result = formMetadataSchema.validate(metadata)\n\n if (result.error) {\n throw result.error\n }\n\n return result.value\n}\n\n/**\n * Retrieves a form definition from the form manager for a given id\n * @param {string} id - the id of the form\n * @param {FormStatus} state - the state of the form\n */\nexport async function getFormDefinition(id, state) {\n const getJsonByType = /** @type {typeof getJson<FormDefinition>} */ (getJson)\n\n const suffix = state === FormStatus.Draft ? `/${state}` : ''\n const { payload: definition } = await getJsonByType(\n `${config.get('managerUrl')}/forms/${id}/definition${suffix}`\n )\n\n return definition\n}\n\n/**\n * @import { FormDefinition, FormMetadata } from '@defra/forms-model'\n */\n"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,oBAAoB;AAEvD,SAASC,MAAM;AACf,SAASC,UAAU;AACnB,SAASC,OAAO;;AAEhB;AACA;AACA;AACA;AACA,OAAO,eAAeC,eAAeA,CAACC,IAAI,EAAE;EAC1C,MAAMC,aAAa,GAAG,2CAA6CH,OAAQ;EAE3E,MAAM;IAAEI,OAAO,EAAEC;EAAS,CAAC,GAAG,MAAMF,aAAa,CAC/C,GAAGL,MAAM,CAACQ,GAAG,CAAC,YAAY,CAAC,eAAeJ,IAAI,EAChD,CAAC;;EAED;EACA,MAAMK,MAAM,GAAGV,kBAAkB,CAACW,QAAQ,CAACH,QAAQ,CAAC;EAEpD,IAAIE,MAAM,CAACE,KAAK,EAAE;IAChB,MAAMF,MAAM,CAACE,KAAK;EACpB;EAEA,OAAOF,MAAM,CAACG,KAAK;AACrB;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,iBAAiBA,CAACC,EAAE,EAAEC,KAAK,EAAE;EACjD,MAAMV,aAAa,GAAG,6CAA+CH,OAAQ;EAE7E,MAAMc,MAAM,GAAGD,KAAK,KAAKd,UAAU,CAACgB,KAAK,GAAG,IAAIF,KAAK,EAAE,GAAG,EAAE;EAC5D,MAAM;IAAET,OAAO,EAAEY;EAAW,CAAC,GAAG,MAAMb,aAAa,CACjD,GAAGL,MAAM,CAACQ,GAAG,CAAC,YAAY,CAAC,UAAUM,EAAE,cAAcE,MAAM,EAC7D,CAAC;EAED,OAAOE,UAAU;AACnB;;AAEA;AACA;AACA","ignoreList":[]}
@@ -0,0 +1,71 @@
1
+ import { StatusCodes } from 'http-status-codes';
2
+ import { getFormDefinition, getFormMetadata } from "./formsService.js";
3
+ import { FormStatus } from "../../../routes/types.js";
4
+ import { getJson } from "../../../services/httpService.js";
5
+ import * as fixtures from "../../../../../test/fixtures/index.js";
6
+ const {
7
+ MANAGER_URL
8
+ } = process.env;
9
+ jest.mock("../../../services/httpService");
10
+ describe('Forms service', () => {
11
+ const {
12
+ definition,
13
+ metadata
14
+ } = fixtures.form;
15
+ describe('getFormMetadata', () => {
16
+ beforeEach(() => {
17
+ jest.mocked(getJson).mockResolvedValue({
18
+ res: (/** @type {IncomingMessage} */{
19
+ statusCode: StatusCodes.OK
20
+ }),
21
+ payload: metadata
22
+ });
23
+ });
24
+ it('requests JSON via form slug', async () => {
25
+ await getFormMetadata(metadata.slug);
26
+ expect(getJson).toHaveBeenCalledWith(`${MANAGER_URL}/forms/slug/${metadata.slug}`);
27
+ });
28
+ it('coerces timestamps from string to Date', async () => {
29
+ const payload = {
30
+ ...structuredClone(metadata),
31
+ // JSON payload uses string dates in transit
32
+ createdAt: metadata.createdAt.toISOString(),
33
+ updatedAt: metadata.updatedAt.toISOString()
34
+ };
35
+ jest.mocked(getJson).mockResolvedValue({
36
+ res: (/** @type {IncomingMessage} */{
37
+ statusCode: StatusCodes.OK
38
+ }),
39
+ payload
40
+ });
41
+ await expect(getFormMetadata(metadata.slug)).resolves.toEqual({
42
+ ...metadata,
43
+ createdAt: expect.any(Date),
44
+ updatedAt: expect.any(Date)
45
+ });
46
+ });
47
+ });
48
+ describe('getFormDefinition', () => {
49
+ beforeEach(() => {
50
+ jest.mocked(getJson).mockResolvedValue({
51
+ res: (/** @type {IncomingMessage} */{
52
+ statusCode: StatusCodes.OK
53
+ }),
54
+ payload: definition
55
+ });
56
+ });
57
+ it('requests JSON via form ID (draft)', async () => {
58
+ await getFormDefinition(metadata.id, FormStatus.Draft);
59
+ expect(getJson).toHaveBeenCalledWith(`${MANAGER_URL}/forms/${metadata.id}/definition/draft`);
60
+ });
61
+ it('requests JSON via form ID (live)', async () => {
62
+ await getFormDefinition(metadata.id, FormStatus.Live);
63
+ expect(getJson).toHaveBeenCalledWith(`${MANAGER_URL}/forms/${metadata.id}/definition`);
64
+ });
65
+ });
66
+ });
67
+
68
+ /**
69
+ * @import { IncomingMessage } from 'node:http'
70
+ */
71
+ //# sourceMappingURL=formsService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formsService.test.js","names":["StatusCodes","getFormDefinition","getFormMetadata","FormStatus","getJson","fixtures","MANAGER_URL","process","env","jest","mock","describe","definition","metadata","form","beforeEach","mocked","mockResolvedValue","res","statusCode","OK","payload","it","slug","expect","toHaveBeenCalledWith","structuredClone","createdAt","toISOString","updatedAt","resolves","toEqual","any","Date","id","Draft","Live"],"sources":["../../../../../src/server/plugins/engine/services/formsService.test.js"],"sourcesContent":["import { StatusCodes } from 'http-status-codes'\n\nimport {\n getFormDefinition,\n getFormMetadata\n} from '~/src/server/plugins/engine/services/formsService.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { getJson } from '~/src/server/services/httpService.js'\nimport * as fixtures from '~/test/fixtures/index.js'\n\nconst { MANAGER_URL } = process.env\n\njest.mock('~/src/server/services/httpService')\n\ndescribe('Forms service', () => {\n const { definition, metadata } = fixtures.form\n\n describe('getFormMetadata', () => {\n beforeEach(() => {\n jest.mocked(getJson).mockResolvedValue({\n res: /** @type {IncomingMessage} */ ({\n statusCode: StatusCodes.OK\n }),\n payload: metadata\n })\n })\n\n it('requests JSON via form slug', async () => {\n await getFormMetadata(metadata.slug)\n\n expect(getJson).toHaveBeenCalledWith(\n `${MANAGER_URL}/forms/slug/${metadata.slug}`\n )\n })\n\n it('coerces timestamps from string to Date', async () => {\n const payload = {\n ...structuredClone(metadata),\n\n // JSON payload uses string dates in transit\n createdAt: metadata.createdAt.toISOString(),\n updatedAt: metadata.updatedAt.toISOString()\n }\n\n jest.mocked(getJson).mockResolvedValue({\n res: /** @type {IncomingMessage} */ ({\n statusCode: StatusCodes.OK\n }),\n payload\n })\n\n await expect(getFormMetadata(metadata.slug)).resolves.toEqual({\n ...metadata,\n createdAt: expect.any(Date),\n updatedAt: expect.any(Date)\n })\n })\n })\n\n describe('getFormDefinition', () => {\n beforeEach(() => {\n jest.mocked(getJson).mockResolvedValue({\n res: /** @type {IncomingMessage} */ ({\n statusCode: StatusCodes.OK\n }),\n payload: definition\n })\n })\n\n it('requests JSON via form ID (draft)', async () => {\n await getFormDefinition(metadata.id, FormStatus.Draft)\n\n expect(getJson).toHaveBeenCalledWith(\n `${MANAGER_URL}/forms/${metadata.id}/definition/draft`\n )\n })\n\n it('requests JSON via form ID (live)', async () => {\n await getFormDefinition(metadata.id, FormStatus.Live)\n\n expect(getJson).toHaveBeenCalledWith(\n `${MANAGER_URL}/forms/${metadata.id}/definition`\n )\n })\n })\n})\n\n/**\n * @import { IncomingMessage } from 'node:http'\n */\n"],"mappings":"AAAA,SAASA,WAAW,QAAQ,mBAAmB;AAE/C,SACEC,iBAAiB,EACjBC,eAAe;AAEjB,SAASC,UAAU;AACnB,SAASC,OAAO;AAChB,OAAO,KAAKC,QAAQ;AAEpB,MAAM;EAAEC;AAAY,CAAC,GAAGC,OAAO,CAACC,GAAG;AAEnCC,IAAI,CAACC,IAAI,gCAAoC,CAAC;AAE9CC,QAAQ,CAAC,eAAe,EAAE,MAAM;EAC9B,MAAM;IAAEC,UAAU;IAAEC;EAAS,CAAC,GAAGR,QAAQ,CAACS,IAAI;EAE9CH,QAAQ,CAAC,iBAAiB,EAAE,MAAM;IAChCI,UAAU,CAAC,MAAM;MACfN,IAAI,CAACO,MAAM,CAACZ,OAAO,CAAC,CAACa,iBAAiB,CAAC;QACrCC,GAAG,GAAE,8BAAgC;UACnCC,UAAU,EAAEnB,WAAW,CAACoB;QAC1B,CAAC,CAAC;QACFC,OAAO,EAAER;MACX,CAAC,CAAC;IACJ,CAAC,CAAC;IAEFS,EAAE,CAAC,6BAA6B,EAAE,YAAY;MAC5C,MAAMpB,eAAe,CAACW,QAAQ,CAACU,IAAI,CAAC;MAEpCC,MAAM,CAACpB,OAAO,CAAC,CAACqB,oBAAoB,CAClC,GAAGnB,WAAW,eAAeO,QAAQ,CAACU,IAAI,EAC5C,CAAC;IACH,CAAC,CAAC;IAEFD,EAAE,CAAC,wCAAwC,EAAE,YAAY;MACvD,MAAMD,OAAO,GAAG;QACd,GAAGK,eAAe,CAACb,QAAQ,CAAC;QAE5B;QACAc,SAAS,EAAEd,QAAQ,CAACc,SAAS,CAACC,WAAW,CAAC,CAAC;QAC3CC,SAAS,EAAEhB,QAAQ,CAACgB,SAAS,CAACD,WAAW,CAAC;MAC5C,CAAC;MAEDnB,IAAI,CAACO,MAAM,CAACZ,OAAO,CAAC,CAACa,iBAAiB,CAAC;QACrCC,GAAG,GAAE,8BAAgC;UACnCC,UAAU,EAAEnB,WAAW,CAACoB;QAC1B,CAAC,CAAC;QACFC;MACF,CAAC,CAAC;MAEF,MAAMG,MAAM,CAACtB,eAAe,CAACW,QAAQ,CAACU,IAAI,CAAC,CAAC,CAACO,QAAQ,CAACC,OAAO,CAAC;QAC5D,GAAGlB,QAAQ;QACXc,SAAS,EAAEH,MAAM,CAACQ,GAAG,CAACC,IAAI,CAAC;QAC3BJ,SAAS,EAAEL,MAAM,CAACQ,GAAG,CAACC,IAAI;MAC5B,CAAC,CAAC;IACJ,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFtB,QAAQ,CAAC,mBAAmB,EAAE,MAAM;IAClCI,UAAU,CAAC,MAAM;MACfN,IAAI,CAACO,MAAM,CAACZ,OAAO,CAAC,CAACa,iBAAiB,CAAC;QACrCC,GAAG,GAAE,8BAAgC;UACnCC,UAAU,EAAEnB,WAAW,CAACoB;QAC1B,CAAC,CAAC;QACFC,OAAO,EAAET;MACX,CAAC,CAAC;IACJ,CAAC,CAAC;IAEFU,EAAE,CAAC,mCAAmC,EAAE,YAAY;MAClD,MAAMrB,iBAAiB,CAACY,QAAQ,CAACqB,EAAE,EAAE/B,UAAU,CAACgC,KAAK,CAAC;MAEtDX,MAAM,CAACpB,OAAO,CAAC,CAACqB,oBAAoB,CAClC,GAAGnB,WAAW,UAAUO,QAAQ,CAACqB,EAAE,mBACrC,CAAC;IACH,CAAC,CAAC;IAEFZ,EAAE,CAAC,kCAAkC,EAAE,YAAY;MACjD,MAAMrB,iBAAiB,CAACY,QAAQ,CAACqB,EAAE,EAAE/B,UAAU,CAACiC,IAAI,CAAC;MAErDZ,MAAM,CAACpB,OAAO,CAAC,CAACqB,oBAAoB,CAClC,GAAGnB,WAAW,UAAUO,QAAQ,CAACqB,EAAE,aACrC,CAAC;IACH,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC;;AAEF;AACA;AACA","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ export * as formsService from "./formsService.js";
2
+ export * as formSubmissionService from "./formSubmissionService.js";
3
+ export * as outputService from "./notifyService.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["formsService","formSubmissionService","outputService"],"sources":["../../../../../src/server/plugins/engine/services/index.js"],"sourcesContent":["export * as formsService from '~/src/server/plugins/engine/services/formsService.js'\nexport * as formSubmissionService from '~/src/server/plugins/engine/services/formSubmissionService.js'\nexport * as outputService from '~/src/server/plugins/engine/services/notifyService.js'\n"],"mappings":"AAAA,OAAO,KAAKA,YAAY;AACxB,OAAO,KAAKC,qBAAqB;AACjC,OAAO,KAAKC,aAAa","ignoreList":[]}
@@ -0,0 +1,45 @@
1
+ import { config } from "../../../../config/index.js";
2
+ import { escapeMarkdown } from "../components/helpers.js";
3
+ import { checkFormStatus } from "../helpers.js";
4
+ import { getFormatter } from "../outputFormatters/index.js";
5
+ import { sendNotification } from "../../../utils/notify.js";
6
+ const templateId = config.get('notifyTemplateId');
7
+ export async function submit(request, model, emailAddress, items, submitResponse) {
8
+ const logTags = ['submit', 'email'];
9
+ const {
10
+ path
11
+ } = request;
12
+ const formStatus = checkFormStatus(path);
13
+
14
+ // Get submission email personalisation
15
+ request.logger.info(logTags, 'Getting personalisation data');
16
+ const formName = escapeMarkdown(model.name);
17
+ const subject = formStatus.isPreview ? `TEST FORM SUBMISSION: ${formName}` : `Form submission: ${formName}`;
18
+ const outputAudience = model.def.output?.audience ?? 'human';
19
+ const outputVersion = model.def.output?.version ?? '1';
20
+ const outputFormatter = getFormatter(outputAudience, outputVersion);
21
+ let body = outputFormatter(items, model, submitResponse, formStatus);
22
+
23
+ // GOV.UK Notify transforms quotes into curly quotes, so we can't just send the raw payload
24
+ // This is logic specific to Notify, so we include the logic here rather than in the formatter
25
+ if (outputAudience === 'machine') {
26
+ body = Buffer.from(body).toString('base64');
27
+ }
28
+ request.logger.info(logTags, 'Sending email');
29
+ try {
30
+ // Send submission email
31
+ await sendNotification({
32
+ templateId,
33
+ emailAddress,
34
+ personalisation: {
35
+ subject,
36
+ body
37
+ }
38
+ });
39
+ request.logger.info(logTags, 'Email sent successfully');
40
+ } catch (err) {
41
+ request.logger.error(logTags, 'Error sending email', err);
42
+ throw err;
43
+ }
44
+ }
45
+ //# sourceMappingURL=notifyService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifyService.js","names":["config","escapeMarkdown","checkFormStatus","getFormatter","sendNotification","templateId","get","submit","request","model","emailAddress","items","submitResponse","logTags","path","formStatus","logger","info","formName","name","subject","isPreview","outputAudience","def","output","audience","outputVersion","version","outputFormatter","body","Buffer","from","toString","personalisation","err","error"],"sources":["../../../../../src/server/plugins/engine/services/notifyService.ts"],"sourcesContent":["import { type SubmitResponsePayload } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers.js'\nimport { checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { getFormatter } from '~/src/server/plugins/engine/outputFormatters/index.js'\nimport { type FormRequestPayload } from '~/src/server/routes/types.js'\nimport { sendNotification } from '~/src/server/utils/notify.js'\n\nconst templateId = config.get('notifyTemplateId')\n\nexport async function submit(\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload\n) {\n const logTags = ['submit', 'email']\n const { path } = request\n const formStatus = checkFormStatus(path)\n\n // Get submission email personalisation\n request.logger.info(logTags, 'Getting personalisation data')\n\n const formName = escapeMarkdown(model.name)\n const subject = formStatus.isPreview\n ? `TEST FORM SUBMISSION: ${formName}`\n : `Form submission: ${formName}`\n\n const outputAudience = model.def.output?.audience ?? 'human'\n const outputVersion = model.def.output?.version ?? '1'\n\n const outputFormatter = getFormatter(outputAudience, outputVersion)\n let body = outputFormatter(items, model, submitResponse, formStatus)\n\n // GOV.UK Notify transforms quotes into curly quotes, so we can't just send the raw payload\n // This is logic specific to Notify, so we include the logic here rather than in the formatter\n if (outputAudience === 'machine') {\n body = Buffer.from(body).toString('base64')\n }\n\n request.logger.info(logTags, 'Sending email')\n\n try {\n // Send submission email\n await sendNotification({\n templateId,\n emailAddress,\n personalisation: {\n subject,\n body\n }\n })\n\n request.logger.info(logTags, 'Email sent successfully')\n } catch (err) {\n request.logger.error(logTags, 'Error sending email', err)\n\n throw err\n }\n}\n"],"mappings":"AAEA,SAASA,MAAM;AACf,SAASC,cAAc;AACvB,SAASC,eAAe;AAGxB,SAASC,YAAY;AAErB,SAASC,gBAAgB;AAEzB,MAAMC,UAAU,GAAGL,MAAM,CAACM,GAAG,CAAC,kBAAkB,CAAC;AAEjD,OAAO,eAAeC,MAAMA,CAC1BC,OAA2B,EAC3BC,KAAgB,EAChBC,YAAoB,EACpBC,KAAmB,EACnBC,cAAqC,EACrC;EACA,MAAMC,OAAO,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;EACnC,MAAM;IAAEC;EAAK,CAAC,GAAGN,OAAO;EACxB,MAAMO,UAAU,GAAGb,eAAe,CAACY,IAAI,CAAC;;EAExC;EACAN,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,8BAA8B,CAAC;EAE5D,MAAMK,QAAQ,GAAGjB,cAAc,CAACQ,KAAK,CAACU,IAAI,CAAC;EAC3C,MAAMC,OAAO,GAAGL,UAAU,CAACM,SAAS,GAChC,yBAAyBH,QAAQ,EAAE,GACnC,oBAAoBA,QAAQ,EAAE;EAElC,MAAMI,cAAc,GAAGb,KAAK,CAACc,GAAG,CAACC,MAAM,EAAEC,QAAQ,IAAI,OAAO;EAC5D,MAAMC,aAAa,GAAGjB,KAAK,CAACc,GAAG,CAACC,MAAM,EAAEG,OAAO,IAAI,GAAG;EAEtD,MAAMC,eAAe,GAAGzB,YAAY,CAACmB,cAAc,EAAEI,aAAa,CAAC;EACnE,IAAIG,IAAI,GAAGD,eAAe,CAACjB,KAAK,EAAEF,KAAK,EAAEG,cAAc,EAAEG,UAAU,CAAC;;EAEpE;EACA;EACA,IAAIO,cAAc,KAAK,SAAS,EAAE;IAChCO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACF,IAAI,CAAC,CAACG,QAAQ,CAAC,QAAQ,CAAC;EAC7C;EAEAxB,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,eAAe,CAAC;EAE7C,IAAI;IACF;IACA,MAAMT,gBAAgB,CAAC;MACrBC,UAAU;MACVK,YAAY;MACZuB,eAAe,EAAE;QACfb,OAAO;QACPS;MACF;IACF,CAAC,CAAC;IAEFrB,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,yBAAyB,CAAC;EACzD,CAAC,CAAC,OAAOqB,GAAG,EAAE;IACZ1B,OAAO,CAACQ,MAAM,CAACmB,KAAK,CAACtB,OAAO,EAAE,qBAAqB,EAAEqB,GAAG,CAAC;IAEzD,MAAMA,GAAG;EACX;AACF","ignoreList":[]}
@@ -0,0 +1,51 @@
1
+ import { config } from "../../../../config/index.js";
2
+ import { getJson, postJson } from "../../../services/httpService.js";
3
+ const uploaderUrl = config.get('uploaderUrl');
4
+ const submissionUrl = config.get('submissionUrl');
5
+ const uploaderBucketName = config.get('uploaderBucketName');
6
+ const stagingPrefix = config.get('stagingPrefix');
7
+
8
+ /**
9
+ * Initiates a CDP file upload
10
+ * @param {string} path - the path of the page in the form
11
+ * @param {string} retrievalKey - the retrieval key for the files
12
+ * @param {string} [mimeTypes] - the csv string of accepted mimeTypes
13
+ */
14
+ export async function initiateUpload(path, retrievalKey, mimeTypes) {
15
+ const postJsonByType = /** @type {typeof postJson<UploadInitiateResponse>} */postJson;
16
+ const payload = {
17
+ redirect: path,
18
+ callback: `${submissionUrl}/file`,
19
+ s3Bucket: uploaderBucketName,
20
+ s3Path: stagingPrefix,
21
+ metadata: {
22
+ retrievalKey
23
+ },
24
+ mimeTypes: mimeTypes?.split(',').map(type => type.trim()).filter(type => type !== '')
25
+ // maxFileSize: 25 * 1000 * 1000
26
+ };
27
+ const {
28
+ payload: initiate
29
+ } = await postJsonByType(`${uploaderUrl}/initiate`, {
30
+ payload
31
+ });
32
+ return initiate;
33
+ }
34
+
35
+ /**
36
+ * Get the status of a CDP file upload
37
+ * @param {string} uploadId - the ID of the upload
38
+ */
39
+ export async function getUploadStatus(uploadId) {
40
+ const getJsonByType = /** @type {typeof getJson<UploadStatusResponse>} */
41
+ getJson;
42
+ const {
43
+ payload: status
44
+ } = await getJsonByType(`${uploaderUrl}/status/${uploadId}`);
45
+ return status;
46
+ }
47
+
48
+ /**
49
+ * @import { UploadInitiateResponse, UploadStatusResponse } from '~/src/server/plugins/engine/types.js'
50
+ */
51
+ //# sourceMappingURL=uploadService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadService.js","names":["config","getJson","postJson","uploaderUrl","get","submissionUrl","uploaderBucketName","stagingPrefix","initiateUpload","path","retrievalKey","mimeTypes","postJsonByType","payload","redirect","callback","s3Bucket","s3Path","metadata","split","map","type","trim","filter","initiate","getUploadStatus","uploadId","getJsonByType","status"],"sources":["../../../../../src/server/plugins/engine/services/uploadService.js"],"sourcesContent":["import { config } from '~/src/config/index.js'\nimport { getJson, postJson } from '~/src/server/services/httpService.js'\n\nconst uploaderUrl = config.get('uploaderUrl')\nconst submissionUrl = config.get('submissionUrl')\nconst uploaderBucketName = config.get('uploaderBucketName')\nconst stagingPrefix = config.get('stagingPrefix')\n\n/**\n * Initiates a CDP file upload\n * @param {string} path - the path of the page in the form\n * @param {string} retrievalKey - the retrieval key for the files\n * @param {string} [mimeTypes] - the csv string of accepted mimeTypes\n */\nexport async function initiateUpload(path, retrievalKey, mimeTypes) {\n const postJsonByType =\n /** @type {typeof postJson<UploadInitiateResponse>} */ (postJson)\n\n const payload = {\n redirect: path,\n callback: `${submissionUrl}/file`,\n s3Bucket: uploaderBucketName,\n s3Path: stagingPrefix,\n metadata: {\n retrievalKey\n },\n mimeTypes: mimeTypes\n ?.split(',')\n .map((type) => type.trim())\n .filter((type) => type !== '')\n // maxFileSize: 25 * 1000 * 1000\n }\n\n const { payload: initiate } = await postJsonByType(\n `${uploaderUrl}/initiate`,\n { payload }\n )\n\n return initiate\n}\n\n/**\n * Get the status of a CDP file upload\n * @param {string} uploadId - the ID of the upload\n */\nexport async function getUploadStatus(uploadId) {\n const getJsonByType = /** @type {typeof getJson<UploadStatusResponse>} */ (\n getJson\n )\n\n const { payload: status } = await getJsonByType(\n `${uploaderUrl}/status/${uploadId}`\n )\n\n return status\n}\n\n/**\n * @import { UploadInitiateResponse, UploadStatusResponse } from '~/src/server/plugins/engine/types.js'\n */\n"],"mappings":"AAAA,SAASA,MAAM;AACf,SAASC,OAAO,EAAEC,QAAQ;AAE1B,MAAMC,WAAW,GAAGH,MAAM,CAACI,GAAG,CAAC,aAAa,CAAC;AAC7C,MAAMC,aAAa,GAAGL,MAAM,CAACI,GAAG,CAAC,eAAe,CAAC;AACjD,MAAME,kBAAkB,GAAGN,MAAM,CAACI,GAAG,CAAC,oBAAoB,CAAC;AAC3D,MAAMG,aAAa,GAAGP,MAAM,CAACI,GAAG,CAAC,eAAe,CAAC;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeI,cAAcA,CAACC,IAAI,EAAEC,YAAY,EAAEC,SAAS,EAAE;EAClE,MAAMC,cAAc,GAClB,sDAAwDV,QAAS;EAEnE,MAAMW,OAAO,GAAG;IACdC,QAAQ,EAAEL,IAAI;IACdM,QAAQ,EAAE,GAAGV,aAAa,OAAO;IACjCW,QAAQ,EAAEV,kBAAkB;IAC5BW,MAAM,EAAEV,aAAa;IACrBW,QAAQ,EAAE;MACRR;IACF,CAAC;IACDC,SAAS,EAAEA,SAAS,EAChBQ,KAAK,CAAC,GAAG,CAAC,CACXC,GAAG,CAAEC,IAAI,IAAKA,IAAI,CAACC,IAAI,CAAC,CAAC,CAAC,CAC1BC,MAAM,CAAEF,IAAI,IAAKA,IAAI,KAAK,EAAE;IAC/B;EACF,CAAC;EAED,MAAM;IAAER,OAAO,EAAEW;EAAS,CAAC,GAAG,MAAMZ,cAAc,CAChD,GAAGT,WAAW,WAAW,EACzB;IAAEU;EAAQ,CACZ,CAAC;EAED,OAAOW,QAAQ;AACjB;;AAEA;AACA;AACA;AACA;AACA,OAAO,eAAeC,eAAeA,CAACC,QAAQ,EAAE;EAC9C,MAAMC,aAAa,GAAG;EACpB1B,OACD;EAED,MAAM;IAAEY,OAAO,EAAEe;EAAO,CAAC,GAAG,MAAMD,aAAa,CAC7C,GAAGxB,WAAW,WAAWuB,QAAQ,EACnC,CAAC;EAED,OAAOE,MAAM;AACf;;AAEA;AACA;AACA","ignoreList":[]}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Form submission state stores the following in Redis:
3
+ * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`
4
+ * a) . e.g:
5
+ * ```ts
6
+ * {
7
+ * _C9PRHmsgt: 'Ben',
8
+ * WfLk9McjzX: 'Music',
9
+ * IK7jkUFCBL: 'Royal Academy of Music'
10
+ * }
11
+ * ```
12
+ *
13
+ * b)
14
+ * ```ts
15
+ * {
16
+ * checkBeforeYouStart: { ukPassport: true },
17
+ * applicantDetails: {
18
+ * numberOfApplicants: 1,
19
+ * phoneNumber: '77777777',
20
+ * emailAddress: 'aaa@aaa.com'
21
+ * },
22
+ * applicantOneDetails: {
23
+ * firstName: 'a',
24
+ * middleName: 'a',
25
+ * lastName: 'a',
26
+ * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }
27
+ * }
28
+ * }
29
+ * ```
30
+ */
31
+
32
+ /**
33
+ * Form submission state
34
+ */
35
+
36
+ /**
37
+ * Form POST for question pages
38
+ * (after Joi has converted value types)
39
+ */
40
+
41
+ export let UploadStatus = /*#__PURE__*/function (UploadStatus) {
42
+ UploadStatus["initiated"] = "initiated";
43
+ UploadStatus["pending"] = "pending";
44
+ UploadStatus["ready"] = "ready";
45
+ return UploadStatus;
46
+ }({});
47
+ export let FileStatus = /*#__PURE__*/function (FileStatus) {
48
+ FileStatus["complete"] = "complete";
49
+ FileStatus["rejected"] = "rejected";
50
+ FileStatus["pending"] = "pending";
51
+ return FileStatus;
52
+ }({});
53
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":["UploadStatus","FileStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Item,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport { type FormAction, type FormRequest } from '~/src/server/routes/types.js'\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<FormRequest, 'app' | 'method' | 'params' | 'path' | 'query' | 'url'>\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport enum UploadStatus {\n initiated = 'initiated',\n pending = 'pending',\n ready = 'ready'\n}\n\nexport enum FileStatus {\n complete = 'complete',\n rejected = 'rejected',\n pending = 'pending'\n}\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n"],"mappings":"AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAmBA;AACA;AACA;AACA;;AAiGA,WAAYA,YAAY,0BAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;AAMxB,WAAYC,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/select/macro.njk" import govukSelect %}
2
+
3
+ {% macro AutocompleteField(component) %}
4
+ {{ govukSelect(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/checkboxes/macro.njk" import govukCheckboxes %}
2
+
3
+ {% macro CheckboxesField(component) %}
4
+ {{ govukCheckboxes(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/date-input/macro.njk" import govukDateInput %}
2
+
3
+ {% macro DatePartsField(component) %}
4
+ {{ govukDateInput(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,6 @@
1
+ {% from "govuk/components/details/macro.njk" import govukDetails %}
2
+
3
+ {% macro Details(component) %}
4
+ {# {{ getContext(component) | dump | safe }} #}
5
+ {{ govukDetails(component.model)}}
6
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "components/textfield.html" import TextField %}
2
+
3
+ {% macro EmailAddressField(component) %}
4
+ {{ TextField(component) }}
5
+ {% endmacro %}
@@ -0,0 +1,8 @@
1
+ {% if params.errorMessage %}
2
+ <div class="govuk-form-group govuk-form-group--error govuk-!-margin-bottom-0">
3
+ <div class="govuk-!-margin-bottom-3">{{ params.name }}</div>
4
+ <p class="govuk-error-message">{{ params.errorMessage }}</p>
5
+ </div>
6
+ {% else %}
7
+ {{ params.name }}
8
+ {% endif %}
@@ -0,0 +1,3 @@
1
+ {% from "govuk/components/tag/macro.njk" import govukTag -%}
2
+
3
+ {{ govukTag(params.tag) }}
@@ -0,0 +1,24 @@
1
+ {% from "govuk/components/summary-list/macro.njk" import govukSummaryList -%}
2
+
3
+ {% macro FileUploadField(component) %}
4
+ {% set upload = component.model.upload %}
5
+
6
+ <div id="uploadedFilesContainer">
7
+ <h2 class="govuk-heading-m">Uploaded files</h2>
8
+ {% if upload.count %}
9
+ <p class="govuk-body">{{upload.count}} file{{ "s" if upload.count > 1}} uploaded</p>
10
+ <p class="govuk-body">
11
+ </p>
12
+
13
+ <div class="govuk-visually-hidden" aria-live="polite" id="statusInformation">
14
+ {{upload.count}} file{{ "s" if upload.count > 1}} uploaded
15
+ </div>
16
+
17
+ {% if upload.summaryList.rows | length %}
18
+ {{ govukSummaryList(upload.summaryList) }}
19
+ {% endif %}
20
+ {% else %}
21
+ <p class="govuk-body">{{upload.count}} files uploaded</p>
22
+ {% endif %}
23
+ </div>
24
+ {% endmacro %}
@@ -0,0 +1,3 @@
1
+ {% macro Html(component) %}
2
+ {{ component.model.content | safe }}
3
+ {% endmacro %}
@@ -0,0 +1,7 @@
1
+ {% from "govuk/components/inset-text/macro.njk" import govukInsetText %}
2
+
3
+ {% macro InsetText(component) %}
4
+ {{ govukInsetText({
5
+ html: component.model.content
6
+ }) }}
7
+ {% endmacro %}
@@ -0,0 +1,36 @@
1
+ {% macro List(component) %}
2
+ {% set params = component.model %}
3
+
4
+ <div class="govuk-form-group">
5
+ {% if params.content.title %}
6
+ <h2 class="govuk-heading-m govuk-!-margin-bottom-3">
7
+ {{ params.content.title }}
8
+ </h2>
9
+ {% endif %}
10
+
11
+ {% if params.content.text %}
12
+ <p class="govuk-body">
13
+ {{ params.content.text }}
14
+ </p>
15
+ {% endif %}
16
+
17
+ {% if params.type == 'numbered' %}
18
+ <ol class="govuk-list govuk-list--number {%- if params.classes %} {{ params.classes }}{% endif %}">
19
+ {% else %}
20
+ <ul class="govuk-list govuk-list--bullet {%- if params.classes %} {{ params.classes }}{% endif %}">
21
+ {% endif %}
22
+ {% for item in params.items %}
23
+ <li>
24
+ {{ item.text }}
25
+ {% if item.hint.text | length %}
26
+ <br><span class="govuk-hint govuk-!-display-inline-block">{{ item.hint.text }}</span>
27
+ {% endif %}
28
+ </li>
29
+ {% endfor %}
30
+ {% if params.type == 'numbered' %}
31
+ </ol>
32
+ {% else %}
33
+ </ul>
34
+ {% endif %}
35
+ </div>
36
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/date-input/macro.njk" import govukDateInput %}
2
+
3
+ {% macro MonthYearField(component) %}
4
+ {{ govukDateInput(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,10 @@
1
+ {% from "govuk/components/textarea/macro.njk" import govukTextarea %}
2
+ {% from "govuk/components/character-count/macro.njk" import govukCharacterCount %}
3
+
4
+ {% macro MultilineTextField(component) %}
5
+ {% if component.model.isCharacterOrWordCount == true %}
6
+ {{ govukCharacterCount(component.model) }}
7
+ {% else %}
8
+ {{ govukTextarea(component.model) }}
9
+ {% endif %}
10
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "components/textfield.html" import TextField %}
2
+
3
+ {% macro NumberField(component) %}
4
+ {{ TextField(component) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/radios/macro.njk" import govukRadios %}
2
+
3
+ {% macro RadiosField(component) %}
4
+ {{ govukRadios(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/select/macro.njk" import govukSelect %}
2
+
3
+ {% macro SelectField(component) %}
4
+ {{ govukSelect(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "components/textfield.html" import TextField %}
2
+
3
+ {% macro TelephoneNumberField(component) %}
4
+ {{ TextField(component) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/input/macro.njk" import govukInput %}
2
+
3
+ {% macro TextField(component) %}
4
+ {{ govukInput(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,25 @@
1
+ {% from "partials/components.html" import componentList %}
2
+ {% from "govuk/components/fieldset/macro.njk" import govukFieldset %}
3
+ {% from "govuk/components/hint/macro.njk" import govukHint %}
4
+
5
+ {% macro UkAddressField(component) %}
6
+ {% set fieldset = component.model.fieldset %}
7
+ {% set addressFieldHtml = componentList(component.model.components) %}
8
+
9
+ {% if component.model.hint %}
10
+ {% set addressHintHtml %}
11
+ {{ govukHint({
12
+ id: component.model.name + "-hint",
13
+ text: component.model.hint.text
14
+ } if fieldset else component.model.hint) }}
15
+ {% endset %}
16
+
17
+ {% set addressFieldHtml = addressHintHtml + addressFieldHtml %}
18
+ {% endif %}
19
+
20
+ {{ govukFieldset({
21
+ legend: fieldset.legend,
22
+ attributes: fieldset.attributes,
23
+ html: addressFieldHtml
24
+ }) if fieldset else addressFieldHtml }}
25
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "components/radiosfield.html" import RadiosField %}
2
+
3
+ {% macro YesNoField(component) %}
4
+ {{ RadiosField(component) }}
5
+ {% endmacro %}
@@ -0,0 +1,45 @@
1
+ {% extends "index.html" %}
2
+
3
+ {% from "govuk/components/button/macro.njk" import govukButton %}
4
+ {% from "govuk/components/file-upload/macro.njk" import govukFileUpload %}
5
+ {% from "govuk/components/warning-text/macro.njk" import govukWarningText %}
6
+
7
+ {% block form %}
8
+ {{ componentList(componentsBefore) }}
9
+
10
+ <div class="govuk-error-summary-container"></div>
11
+
12
+ <form method="post" {%- if formAction and not context.isForceAccess %} action="{{ formAction }}" enctype="multipart/form-data" data-upload-id="{{ uploadId }}" {% if proxyUrl %}data-proxy-url="{{ proxyUrl }}"{% endif %} {%- endif %} novalidate>
13
+ {{ govukFileUpload(formComponent.model) }}
14
+
15
+ {% if formAction %}
16
+ {{ govukButton({
17
+ text: "Upload file",
18
+ classes: "govuk-button--secondary upload-file-button",
19
+ preventDoubleClick: true
20
+ }) }}
21
+ {% else %}
22
+ {{ govukWarningText({
23
+ text: "You have reached the maximum number of files. Please remove a file to upload more.",
24
+ iconFallbackText: "Warning"
25
+ }) }}
26
+ {% endif %}
27
+ </form>
28
+
29
+ {{ super() }}
30
+
31
+ <div class="govuk-summary-list-container"></div>
32
+
33
+ {% endblock %}
34
+
35
+ {% block bodyEnd %}
36
+ {{ super() }}
37
+ <script type="module" nonce="{{ cspNonce }}">
38
+ import { initFileUpload } from '{{ getAssetPath("file-upload.js") }}';
39
+ if (document.readyState === 'loading') {
40
+ document.addEventListener('DOMContentLoaded', initFileUpload);
41
+ } else {
42
+ initFileUpload();
43
+ }
44
+ </script>
45
+ {% endblock %}