@defra/forms-engine-plugin 0.0.4 → 0.0.6

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 (260) hide show
  1. package/.server/server/index.js +0 -4
  2. package/.server/server/index.js.map +1 -1
  3. package/.server/server/plugins/engine/helpers.js +3 -0
  4. package/.server/server/plugins/engine/helpers.js.map +1 -1
  5. package/.server/server/plugins/engine/index.js +27 -1
  6. package/.server/server/plugins/engine/index.js.map +1 -1
  7. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +2 -4
  8. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  9. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +4 -10
  10. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  11. package/.server/server/plugins/engine/pageControllers/StatusPageController.js +2 -3
  12. package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
  13. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +2 -4
  14. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  15. package/.server/server/plugins/engine/plugin.js +65 -6
  16. package/.server/server/plugins/engine/plugin.js.map +1 -1
  17. package/.server/server/plugins/engine/types.js.map +1 -1
  18. package/.server/server/{views → plugins/engine/views}/components/service-banner/template.test.js +1 -1
  19. package/.server/server/plugins/engine/views/components/service-banner/template.test.js.map +1 -0
  20. package/.server/server/{views → plugins/engine/views}/components/tag-env/template.test.js +1 -1
  21. package/.server/server/plugins/engine/views/components/tag-env/template.test.js.map +1 -0
  22. package/.server/server/services/cacheService.js +5 -2
  23. package/.server/server/services/cacheService.js.map +1 -1
  24. package/.server/typings/hapi/index.d.js.map +1 -1
  25. package/README.md +215 -4
  26. package/package.json +3 -3
  27. package/src/client/javascripts/application.js +87 -0
  28. package/src/client/javascripts/file-upload.js +386 -0
  29. package/src/client/stylesheets/_code.scss +33 -0
  30. package/src/client/stylesheets/_govuk-frontend.scss +4 -0
  31. package/src/client/stylesheets/_prose.scss +56 -0
  32. package/src/client/stylesheets/_service-banner.scss +24 -0
  33. package/src/client/stylesheets/_summary-list.scss +28 -0
  34. package/src/client/stylesheets/_tag-env.scss +24 -0
  35. package/src/client/stylesheets/application.scss +14 -0
  36. package/src/common/cookies.js +58 -0
  37. package/src/common/cookies.test.js +23 -0
  38. package/src/common/types.js +5 -0
  39. package/src/config/index.ts +271 -0
  40. package/src/index.ts +31 -0
  41. package/src/server/common/helpers/logging/logger-options.test.ts +50 -0
  42. package/src/server/common/helpers/logging/logger-options.ts +46 -0
  43. package/src/server/common/helpers/logging/logger.ts +7 -0
  44. package/src/server/common/helpers/logging/request-logger.ts +9 -0
  45. package/src/server/common/helpers/logging/request-tracing.js +10 -0
  46. package/src/server/common/helpers/redis-client.js +70 -0
  47. package/src/server/constants.js +1 -0
  48. package/src/server/forms/README.md +10 -0
  49. package/src/server/forms/components.json +1015 -0
  50. package/src/server/forms/report-a-terrorist.json +270 -0
  51. package/src/server/forms/runner-components-test.json +365 -0
  52. package/src/server/forms/test.json +581 -0
  53. package/src/server/index.test.ts +582 -0
  54. package/src/server/index.ts +135 -0
  55. package/src/server/plugins/blankie.test.ts +73 -0
  56. package/src/server/plugins/blankie.ts +48 -0
  57. package/src/server/plugins/crumb.ts +20 -0
  58. package/src/server/plugins/engine/README.md +87 -0
  59. package/src/server/plugins/engine/components/AutocompleteField.test.ts +294 -0
  60. package/src/server/plugins/engine/components/AutocompleteField.ts +49 -0
  61. package/src/server/plugins/engine/components/CheckboxesField.test.ts +379 -0
  62. package/src/server/plugins/engine/components/CheckboxesField.ts +106 -0
  63. package/src/server/plugins/engine/components/ComponentBase.ts +97 -0
  64. package/src/server/plugins/engine/components/ComponentCollection.ts +278 -0
  65. package/src/server/plugins/engine/components/DatePartsField.test.ts +822 -0
  66. package/src/server/plugins/engine/components/DatePartsField.ts +264 -0
  67. package/src/server/plugins/engine/components/Details.test.ts +49 -0
  68. package/src/server/plugins/engine/components/Details.ts +30 -0
  69. package/src/server/plugins/engine/components/EmailAddressField.test.ts +395 -0
  70. package/src/server/plugins/engine/components/EmailAddressField.ts +55 -0
  71. package/src/server/plugins/engine/components/FileUploadField.test.ts +778 -0
  72. package/src/server/plugins/engine/components/FileUploadField.ts +262 -0
  73. package/src/server/plugins/engine/components/FormComponent.ts +249 -0
  74. package/src/server/plugins/engine/components/Html.test.ts +48 -0
  75. package/src/server/plugins/engine/components/Html.ts +29 -0
  76. package/src/server/plugins/engine/components/InsetText.test.ts +48 -0
  77. package/src/server/plugins/engine/components/InsetText.ts +27 -0
  78. package/src/server/plugins/engine/components/List.test.ts +76 -0
  79. package/src/server/plugins/engine/components/List.ts +72 -0
  80. package/src/server/plugins/engine/components/ListFormComponent.ts +140 -0
  81. package/src/server/plugins/engine/components/MonthYearField.test.ts +567 -0
  82. package/src/server/plugins/engine/components/MonthYearField.ts +222 -0
  83. package/src/server/plugins/engine/components/MultilineTextField.test.ts +558 -0
  84. package/src/server/plugins/engine/components/MultilineTextField.ts +138 -0
  85. package/src/server/plugins/engine/components/NumberField.test.ts +701 -0
  86. package/src/server/plugins/engine/components/NumberField.ts +163 -0
  87. package/src/server/plugins/engine/components/RadiosField.test.ts +288 -0
  88. package/src/server/plugins/engine/components/RadiosField.ts +24 -0
  89. package/src/server/plugins/engine/components/SelectField.test.ts +288 -0
  90. package/src/server/plugins/engine/components/SelectField.ts +47 -0
  91. package/src/server/plugins/engine/components/SelectionControlField.ts +43 -0
  92. package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +356 -0
  93. package/src/server/plugins/engine/components/TelephoneNumberField.ts +67 -0
  94. package/src/server/plugins/engine/components/TextField.test.ts +489 -0
  95. package/src/server/plugins/engine/components/TextField.ts +96 -0
  96. package/src/server/plugins/engine/components/UkAddressField.test.ts +623 -0
  97. package/src/server/plugins/engine/components/UkAddressField.ts +172 -0
  98. package/src/server/plugins/engine/components/YesNoField.test.ts +248 -0
  99. package/src/server/plugins/engine/components/YesNoField.ts +31 -0
  100. package/src/server/plugins/engine/components/constants.ts +1 -0
  101. package/src/server/plugins/engine/components/helpers.ts +330 -0
  102. package/src/server/plugins/engine/components/index.ts +24 -0
  103. package/src/server/plugins/engine/components/types.ts +117 -0
  104. package/src/server/plugins/engine/configureEnginePlugin.ts +47 -0
  105. package/src/server/plugins/engine/helpers.test.ts +791 -0
  106. package/src/server/plugins/engine/helpers.ts +384 -0
  107. package/src/server/plugins/engine/index.ts +47 -0
  108. package/src/server/plugins/engine/models/FormModel.test.ts +42 -0
  109. package/src/server/plugins/engine/models/FormModel.ts +443 -0
  110. package/src/server/plugins/engine/models/RepeatingSummaryViewModel.ts +0 -0
  111. package/src/server/plugins/engine/models/Section.ts +0 -0
  112. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +209 -0
  113. package/src/server/plugins/engine/models/SummaryViewModel.ts +220 -0
  114. package/src/server/plugins/engine/models/index.ts +2 -0
  115. package/src/server/plugins/engine/models/types.ts +114 -0
  116. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +143 -0
  117. package/src/server/plugins/engine/outputFormatters/human/v1.ts +73 -0
  118. package/src/server/plugins/engine/outputFormatters/index.test.ts +17 -0
  119. package/src/server/plugins/engine/outputFormatters/index.ts +44 -0
  120. package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +229 -0
  121. package/src/server/plugins/engine/outputFormatters/machine/v1.ts +140 -0
  122. package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +229 -0
  123. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +153 -0
  124. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +1116 -0
  125. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +447 -0
  126. package/src/server/plugins/engine/pageControllers/PageController.test.ts +205 -0
  127. package/src/server/plugins/engine/pageControllers/PageController.ts +176 -0
  128. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +1264 -0
  129. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +565 -0
  130. package/src/server/plugins/engine/pageControllers/README.md +28 -0
  131. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +264 -0
  132. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +458 -0
  133. package/src/server/plugins/engine/pageControllers/StartPageController.ts +18 -0
  134. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +51 -0
  135. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +262 -0
  136. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +28 -0
  137. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +19 -0
  138. package/src/server/plugins/engine/pageControllers/helpers.test.ts +198 -0
  139. package/src/server/plugins/engine/pageControllers/helpers.ts +101 -0
  140. package/src/server/plugins/engine/pageControllers/index.ts +10 -0
  141. package/src/server/plugins/engine/pageControllers/validationOptions.ts +89 -0
  142. package/src/server/plugins/engine/plugin.ts +753 -0
  143. package/src/server/plugins/engine/services/formSubmissionService.js +46 -0
  144. package/src/server/plugins/engine/services/formsService.js +46 -0
  145. package/src/server/plugins/engine/services/formsService.test.js +90 -0
  146. package/src/server/plugins/engine/services/index.js +3 -0
  147. package/src/server/plugins/engine/services/notifyService.test.ts +132 -0
  148. package/src/server/plugins/engine/services/notifyService.ts +64 -0
  149. package/src/server/plugins/engine/services/uploadService.js +60 -0
  150. package/src/server/plugins/engine/types.ts +317 -0
  151. package/src/server/plugins/engine/views/components/autocompletefield.html +5 -0
  152. package/src/server/plugins/engine/views/components/checkboxesfield.html +5 -0
  153. package/src/server/plugins/engine/views/components/datepartsfield.html +5 -0
  154. package/src/server/plugins/engine/views/components/debug/macro.njk +3 -0
  155. package/src/server/plugins/engine/views/components/debug/template.njk +13 -0
  156. package/src/server/plugins/engine/views/components/details.html +6 -0
  157. package/src/server/plugins/engine/views/components/emailaddressfield.html +5 -0
  158. package/src/server/plugins/engine/views/components/fileuploadfield-key.html +8 -0
  159. package/src/server/plugins/engine/views/components/fileuploadfield-value.html +3 -0
  160. package/src/server/plugins/engine/views/components/fileuploadfield.html +24 -0
  161. package/src/server/plugins/engine/views/components/html.html +3 -0
  162. package/src/server/plugins/engine/views/components/insettext.html +7 -0
  163. package/src/server/plugins/engine/views/components/list.html +36 -0
  164. package/src/server/plugins/engine/views/components/monthyearfield.html +5 -0
  165. package/src/server/plugins/engine/views/components/multilinetextfield.html +10 -0
  166. package/src/server/plugins/engine/views/components/numberfield.html +5 -0
  167. package/src/server/plugins/engine/views/components/radiosfield.html +5 -0
  168. package/src/server/plugins/engine/views/components/selectfield.html +5 -0
  169. package/src/server/plugins/engine/views/components/service-banner/macro.njk +3 -0
  170. package/src/server/plugins/engine/views/components/service-banner/template.njk +20 -0
  171. package/src/server/plugins/engine/views/components/service-banner/template.test.js +43 -0
  172. package/src/server/plugins/engine/views/components/tag-env/macro.njk +3 -0
  173. package/src/server/plugins/engine/views/components/tag-env/template.njk +30 -0
  174. package/src/server/plugins/engine/views/components/tag-env/template.test.js +66 -0
  175. package/src/server/plugins/engine/views/components/telephonenumberfield.html +5 -0
  176. package/src/server/plugins/engine/views/components/textfield.html +5 -0
  177. package/src/server/plugins/engine/views/components/ukaddressfield.html +25 -0
  178. package/src/server/plugins/engine/views/components/yesnofield.html +5 -0
  179. package/src/server/plugins/engine/views/confirmation.html +19 -0
  180. package/src/server/plugins/engine/views/file-upload.html +45 -0
  181. package/src/server/plugins/engine/views/index.html +39 -0
  182. package/src/server/plugins/engine/views/item-delete.html +56 -0
  183. package/src/server/plugins/engine/views/layout.html +199 -0
  184. package/src/server/plugins/engine/views/partials/components.html +6 -0
  185. package/src/server/plugins/engine/views/partials/conditional-components.html +3 -0
  186. package/src/server/plugins/engine/views/partials/debug.html +44 -0
  187. package/src/server/plugins/engine/views/partials/form.html +15 -0
  188. package/src/server/plugins/engine/views/partials/heading.html +16 -0
  189. package/src/server/plugins/engine/views/partials/preview-banner.html +32 -0
  190. package/src/server/plugins/engine/views/partials/preview-banner.test.js +122 -0
  191. package/src/server/plugins/engine/views/partials/warn-missing-notification-email.html +10 -0
  192. package/src/server/plugins/engine/views/repeat-list-summary.html +53 -0
  193. package/src/server/plugins/engine/views/summary.html +50 -0
  194. package/src/server/plugins/errorPages.ts +58 -0
  195. package/src/server/plugins/nunjucks/context.js +88 -0
  196. package/src/server/plugins/nunjucks/context.test.js +142 -0
  197. package/src/server/plugins/nunjucks/enviroment.test.js +201 -0
  198. package/src/server/plugins/nunjucks/environment.js +116 -0
  199. package/src/server/plugins/nunjucks/filters/answer.js +27 -0
  200. package/src/server/plugins/nunjucks/filters/answer.test.js +89 -0
  201. package/src/server/plugins/nunjucks/filters/evaluate.js +21 -0
  202. package/src/server/plugins/nunjucks/filters/field.js +28 -0
  203. package/src/server/plugins/nunjucks/filters/field.test.js +75 -0
  204. package/src/server/plugins/nunjucks/filters/highlight.js +11 -0
  205. package/src/server/plugins/nunjucks/filters/href.js +30 -0
  206. package/src/server/plugins/nunjucks/filters/href.test.js +80 -0
  207. package/src/server/plugins/nunjucks/filters/index.js +8 -0
  208. package/src/server/plugins/nunjucks/filters/inspect.js +15 -0
  209. package/src/server/plugins/nunjucks/filters/page.js +24 -0
  210. package/src/server/plugins/nunjucks/filters/page.test.js +65 -0
  211. package/src/server/plugins/nunjucks/index.js +3 -0
  212. package/src/server/plugins/nunjucks/plugin.js +40 -0
  213. package/src/server/plugins/nunjucks/render.js +42 -0
  214. package/src/server/plugins/nunjucks/types.js +40 -0
  215. package/src/server/plugins/pulse.ts +11 -0
  216. package/src/server/plugins/router.ts +201 -0
  217. package/src/server/plugins/session.ts +28 -0
  218. package/src/server/routes/health.js +13 -0
  219. package/src/server/routes/health.test.js +35 -0
  220. package/src/server/routes/index.test.ts +125 -0
  221. package/src/server/routes/index.ts +2 -0
  222. package/src/server/routes/public.ts +47 -0
  223. package/src/server/routes/types.ts +48 -0
  224. package/src/server/schemas/index.ts +34 -0
  225. package/src/server/secure-context.js +43 -0
  226. package/src/server/services/cacheService.test.ts +277 -0
  227. package/src/server/services/cacheService.ts +138 -0
  228. package/src/server/services/httpService.test.js +491 -0
  229. package/src/server/services/httpService.ts +50 -0
  230. package/src/server/services/index.ts +1 -0
  231. package/src/server/types.ts +54 -0
  232. package/src/server/utils/notify.test.ts +37 -0
  233. package/src/server/utils/notify.ts +50 -0
  234. package/src/server/utils/secure-context/get-trust-store-certs.js +11 -0
  235. package/src/server/utils/secure-context/get-trust-store-certs.test.js +19 -0
  236. package/src/server/utils/utils.js +24 -0
  237. package/src/server/utils/utils.test.js +54 -0
  238. package/src/server/views/404.html +16 -0
  239. package/src/server/views/500.html +19 -0
  240. package/src/server/views/help/accessibility-statement.html +58 -0
  241. package/src/server/views/help/cookie-preferences.html +57 -0
  242. package/src/server/views/help/cookies.html +71 -0
  243. package/src/server/views/help/get-support.html +37 -0
  244. package/src/server/views/help/privacy-notice.html +68 -0
  245. package/src/server/views/help/terms-and-conditions.html +83 -0
  246. package/src/typings/hapi/index.d.ts +87 -0
  247. package/src/typings/hapi-tracing/index.d.ts +6 -0
  248. package/src/typings/index.d.ts +3 -0
  249. package/src/typings/joi/index.d.ts +22 -0
  250. package/.server/server/views/components/service-banner/template.test.js.map +0 -1
  251. package/.server/server/views/components/tag-env/template.test.js.map +0 -1
  252. /package/.server/server/{views → plugins/engine/views}/components/debug/macro.njk +0 -0
  253. /package/.server/server/{views → plugins/engine/views}/components/debug/template.njk +0 -0
  254. /package/.server/server/{views → plugins/engine/views}/components/service-banner/macro.njk +0 -0
  255. /package/.server/server/{views → plugins/engine/views}/components/service-banner/template.njk +0 -0
  256. /package/.server/server/{views → plugins/engine/views}/components/tag-env/macro.njk +0 -0
  257. /package/.server/server/{views → plugins/engine/views}/components/tag-env/template.njk +0 -0
  258. /package/.server/server/{views → plugins/engine/views}/confirmation.html +0 -0
  259. /package/.server/server/{views → plugins/engine/views}/layout.html +0 -0
  260. /package/.server/server/{views → plugins/engine/views}/summary.html +0 -0
@@ -0,0 +1,271 @@
1
+ import { resolve } from 'node:path'
2
+
3
+ import convict, { type SchemaObj } from 'convict'
4
+ import { type LevelWithSilent } from 'pino'
5
+
6
+ import 'dotenv/config'
7
+
8
+ const isProduction = process.env.NODE_ENV === 'production'
9
+ const isDev = process.env.NODE_ENV !== 'production'
10
+ const isTest = process.env.NODE_ENV === 'test'
11
+
12
+ const oneMinute = 1000 * 60
13
+ const oneHour = oneMinute * 60
14
+
15
+ export const config = convict({
16
+ appDir: {
17
+ format: String,
18
+ default: resolve(import.meta.dirname, '../server')
19
+ },
20
+ publicDir: {
21
+ format: String,
22
+ default: isTest
23
+ ? resolve(import.meta.dirname, '../../test/fixtures')
24
+ : resolve(import.meta.dirname, '../../.public')
25
+ },
26
+
27
+ /**
28
+ * Server
29
+ */
30
+ port: {
31
+ format: 'port',
32
+ default: 3009,
33
+ env: 'PORT'
34
+ },
35
+ env: {
36
+ doc: 'The application environment.',
37
+ format: ['production', 'development', 'test'],
38
+ default: 'development',
39
+ env: 'NODE_ENV'
40
+ },
41
+ cdpEnvironment: {
42
+ doc: 'The CDP environment the app is currently in, with the addition of "local"',
43
+ format: [
44
+ 'local',
45
+ 'infra-dev',
46
+ 'management',
47
+ 'dev',
48
+ 'test',
49
+ 'perf-test',
50
+ 'ext-test',
51
+ 'prod'
52
+ ],
53
+ default: 'local',
54
+ env: 'ENVIRONMENT'
55
+ },
56
+ enforceCsrf: {
57
+ format: Boolean,
58
+ default: isProduction,
59
+ env: 'ENFORCE_CSRF'
60
+ },
61
+
62
+ /**
63
+ * Helper flags
64
+ */
65
+ isProduction: {
66
+ doc: 'If this application running in the production environment',
67
+ format: Boolean,
68
+ default: isProduction
69
+ },
70
+ isDevelopment: {
71
+ doc: 'If this application running in the development environment',
72
+ format: Boolean,
73
+ default: isDev
74
+ },
75
+ isTest: {
76
+ doc: 'If this application running in the test environment',
77
+ format: Boolean,
78
+ default: isTest
79
+ },
80
+
81
+ /**
82
+ * Service
83
+ */
84
+ serviceName: {
85
+ doc: 'Applications Service Name',
86
+ format: String,
87
+ default: 'Submit a form to Defra'
88
+ },
89
+ serviceVersion: {
90
+ doc: 'The service version, this variable is injected into your docker container in CDP environments',
91
+ format: String,
92
+ nullable: true,
93
+ default: null,
94
+ env: 'SERVICE_VERSION'
95
+ } as SchemaObj<string>,
96
+ feedbackLink: {
97
+ doc: 'Used in your phase banner. Can be a URL or more commonly mailto mailto:feedback@department.gov.uk',
98
+ format: String,
99
+ default: null,
100
+ env: 'FEEDBACK_LINK'
101
+ } as SchemaObj<string>,
102
+ phaseTag: {
103
+ format: String,
104
+ default: 'beta', // Accepts "alpha" |"beta" | ""
105
+ env: 'PHASE_TAG'
106
+ },
107
+
108
+ /**
109
+ * Session storage
110
+ * Redis integration is optional, but recommended for production environments.
111
+ */
112
+ sessionTimeout: {
113
+ format: Number,
114
+ default: oneHour * 24, // 1 day
115
+ env: 'SESSION_TIMEOUT'
116
+ },
117
+ confirmationSessionTimeout: {
118
+ format: Number,
119
+ default: oneMinute * 20,
120
+ env: 'CONFIRMATION_SESSION_TIMEOUT'
121
+ },
122
+ sessionCookiePassword: {
123
+ format: String,
124
+ default: null,
125
+ sensitive: true,
126
+ env: 'SESSION_COOKIE_PASSWORD'
127
+ } as SchemaObj<string>,
128
+ redis: {
129
+ host: {
130
+ doc: 'Redis cache host',
131
+ format: String,
132
+ default: null,
133
+ env: 'REDIS_HOST'
134
+ } as SchemaObj<string>,
135
+ username: {
136
+ doc: 'Redis cache username',
137
+ format: String,
138
+ default: null,
139
+ env: 'REDIS_USERNAME'
140
+ } as SchemaObj<string>,
141
+ password: {
142
+ doc: 'Redis cache password',
143
+ format: '*',
144
+ default: null,
145
+ sensitive: true,
146
+ env: 'REDIS_PASSWORD'
147
+ } as SchemaObj<string>,
148
+ keyPrefix: {
149
+ doc: 'Redis cache key prefix name used to isolate the cached results across multiple clients',
150
+ format: String,
151
+ default: null,
152
+ env: 'REDIS_KEY_PREFIX'
153
+ } as SchemaObj<string>
154
+ },
155
+ tracing: {
156
+ header: {
157
+ doc: 'Tracing header name',
158
+ format: String,
159
+ default: 'x-cdp-request-id',
160
+ env: 'TRACING_HEADER'
161
+ } as SchemaObj<string>
162
+ },
163
+
164
+ /**
165
+ * Email outputs
166
+ * Email outputs will use notify to send an email to a single inbox.
167
+ */
168
+ notifyTemplateId: {
169
+ format: String,
170
+ default: null,
171
+ env: 'NOTIFY_TEMPLATE_ID'
172
+ } as SchemaObj<string>,
173
+ notifyAPIKey: {
174
+ format: String,
175
+ default: null,
176
+ env: 'NOTIFY_API_KEY'
177
+ } as SchemaObj<string>,
178
+
179
+ /**
180
+ * API integrations
181
+ */
182
+ managerUrl: {
183
+ format: String,
184
+ default: null,
185
+ env: 'MANAGER_URL'
186
+ } as SchemaObj<string>,
187
+
188
+ designerUrl: {
189
+ format: String,
190
+ default: null,
191
+ env: 'DESIGNER_URL'
192
+ } as SchemaObj<string>,
193
+
194
+ submissionUrl: {
195
+ format: String,
196
+ default: null,
197
+ env: 'SUBMISSION_URL'
198
+ } as SchemaObj<string>,
199
+
200
+ uploaderUrl: {
201
+ format: String,
202
+ default: null,
203
+ env: 'UPLOADER_URL'
204
+ } as SchemaObj<string>,
205
+
206
+ uploaderBucketName: {
207
+ format: String,
208
+ default: 'files',
209
+ env: 'UPLOADER_BUCKET_NAME'
210
+ },
211
+
212
+ /**
213
+ * Logging
214
+ */
215
+ log: {
216
+ enabled: {
217
+ doc: 'Is logging enabled',
218
+ format: Boolean,
219
+ default: !isTest,
220
+ env: 'LOG_ENABLED'
221
+ },
222
+ level: {
223
+ doc: 'Logging level',
224
+ format: ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent'],
225
+ default: 'info',
226
+ env: 'LOG_LEVEL'
227
+ } as SchemaObj<LevelWithSilent>,
228
+ format: {
229
+ doc: 'Format to output logs in.',
230
+ format: ['ecs', 'pino-pretty'],
231
+ default: isProduction ? 'ecs' : 'pino-pretty',
232
+ env: 'LOG_FORMAT'
233
+ } as SchemaObj<'ecs' | 'pino-pretty'>,
234
+ redact: {
235
+ doc: 'Log paths to redact',
236
+ format: Array,
237
+ default: isProduction
238
+ ? ['req.headers.authorization', 'req.headers.cookie', 'res.headers']
239
+ : ['req', 'res', 'responseTime']
240
+ }
241
+ },
242
+
243
+ safelist: {
244
+ format: Array,
245
+ default: ['61bca17e-fe74-40e0-9c15-a901ad120eca.mock.pstmn.io'],
246
+ env: 'SAFELIST'
247
+ },
248
+
249
+ stagingPrefix: {
250
+ doc: 'Prefix for staging files in S3',
251
+ format: String,
252
+ default: 'staging',
253
+ env: 'STAGING_PREFIX'
254
+ },
255
+
256
+ serviceBannerText: {
257
+ doc: 'Service banner text used to show a maintenance message on all pages when set',
258
+ format: String,
259
+ default: '',
260
+ env: 'SERVICE_BANNER_TEXT'
261
+ },
262
+
263
+ googleAnalyticsTrackingId: {
264
+ doc: 'Google analytics tracking ID to be used when a user has opted in to additional cookies',
265
+ format: String,
266
+ default: '',
267
+ env: 'GOOGLE_ANALYTICS_TRACKING_ID'
268
+ }
269
+ })
270
+
271
+ config.validate({ allowed: 'strict' })
package/src/index.ts ADDED
@@ -0,0 +1,31 @@
1
+ import { config } from '~/src/config/index.js'
2
+ import { createLogger } from '~/src/server/common/helpers/logging/logger.js'
3
+ import { createServer } from '~/src/server/index.js'
4
+
5
+ const logger = createLogger()
6
+
7
+ process.on('unhandledRejection', (error) => {
8
+ logger.info('Unhandled rejection')
9
+ logger.error(error)
10
+ throw error
11
+ })
12
+
13
+ /**
14
+ * Main entrypoint to the application.
15
+ */
16
+ async function startServer() {
17
+ const server = await createServer()
18
+ await server.start()
19
+
20
+ process.send?.('online')
21
+
22
+ server.logger.info('Server started successfully')
23
+ server.logger.info(
24
+ `Access your frontend on http://localhost:${config.get('port')}`
25
+ )
26
+ }
27
+
28
+ startServer().catch((error: unknown) => {
29
+ logger.info('Server failed to start :(')
30
+ logger.error(error)
31
+ })
@@ -0,0 +1,50 @@
1
+ import { getTraceId } from '@defra/hapi-tracing'
2
+
3
+ import { loggerOptions } from '~/src/server/common/helpers/logging/logger-options.js'
4
+
5
+ jest.mock('@defra/hapi-tracing', () => ({
6
+ getTraceId: jest.fn()
7
+ }))
8
+
9
+ describe('logger-options', () => {
10
+ beforeEach(() => {
11
+ jest.clearAllMocks()
12
+ })
13
+
14
+ describe('configuration', () => {
15
+ it('has the expected properties', () => {
16
+ expect(loggerOptions).toHaveProperty('enabled')
17
+ expect(loggerOptions).toHaveProperty('ignorePaths')
18
+ expect(loggerOptions).toHaveProperty('redact')
19
+ expect(loggerOptions).toHaveProperty('level')
20
+ })
21
+
22
+ it('ignores health endpoint', () => {
23
+ expect(loggerOptions.ignorePaths).toContain('/health')
24
+ })
25
+
26
+ it('has redaction configuration', () => {
27
+ expect(loggerOptions.redact).toBeDefined()
28
+ })
29
+ })
30
+
31
+ describe('mixin function', () => {
32
+ it('returns an empty object when no trace ID is available', () => {
33
+ jest.mocked(getTraceId).mockReturnValue(null)
34
+
35
+ const result = loggerOptions.mixin()
36
+ expect(result).toEqual({})
37
+ })
38
+
39
+ it('includes trace ID when available', () => {
40
+ jest.mocked(getTraceId).mockReturnValue('some-trace-id')
41
+
42
+ const result = loggerOptions.mixin()
43
+ expect(result).toEqual({
44
+ trace: {
45
+ id: 'some-trace-id'
46
+ }
47
+ })
48
+ })
49
+ })
50
+ })
@@ -0,0 +1,46 @@
1
+ import { getTraceId } from '@defra/hapi-tracing'
2
+ import { ecsFormat } from '@elastic/ecs-pino-format'
3
+ import { type Options } from 'hapi-pino'
4
+ import { type LoggerOptions, type TransportSingleOptions } from 'pino'
5
+
6
+ import { config } from '~/src/config/index.js'
7
+
8
+ const logConfig = config.get('log')
9
+ const serviceName = config.get('serviceName')
10
+ const serviceVersion = config.get('serviceVersion')
11
+
12
+ const formatters: {
13
+ ecs: Omit<LoggerOptions, 'mixin' | 'transport'>
14
+ 'pino-pretty': { transport: TransportSingleOptions }
15
+ } = {
16
+ ecs: {
17
+ ...ecsFormat(),
18
+ base: {
19
+ service: {
20
+ name: serviceName,
21
+ type: 'nodeJs',
22
+ version: serviceVersion
23
+ }
24
+ }
25
+ },
26
+ 'pino-pretty': { transport: { target: 'pino-pretty' } }
27
+ }
28
+
29
+ export const loggerOptions = {
30
+ enabled: logConfig.enabled,
31
+ ignorePaths: ['/health'],
32
+ redact: {
33
+ paths: logConfig.redact,
34
+ remove: true
35
+ },
36
+ level: logConfig.level,
37
+ ...formatters[logConfig.format],
38
+ mixin() {
39
+ const mixinValues: { trace?: { id: string } } = {}
40
+ const traceId = getTraceId()
41
+ if (traceId) {
42
+ mixinValues.trace = { id: traceId }
43
+ }
44
+ return mixinValues
45
+ }
46
+ } satisfies Options
@@ -0,0 +1,7 @@
1
+ import { pino } from 'pino'
2
+
3
+ import { loggerOptions } from '~/src/server/common/helpers/logging/logger-options.js'
4
+
5
+ export function createLogger() {
6
+ return pino(loggerOptions)
7
+ }
@@ -0,0 +1,9 @@
1
+ import { type ServerRegisterPluginObject } from '@hapi/hapi'
2
+ import hapiPino, { type Options } from 'hapi-pino'
3
+
4
+ import { loggerOptions } from '~/src/server/common/helpers/logging/logger-options.js'
5
+
6
+ export const requestLogger = {
7
+ plugin: hapiPino,
8
+ options: loggerOptions
9
+ } satisfies ServerRegisterPluginObject<Options>
@@ -0,0 +1,10 @@
1
+ import { tracing } from '@defra/hapi-tracing'
2
+
3
+ import { config } from '~/src/config/index.js'
4
+
5
+ // Explicitly declare the expected type
6
+
7
+ export const requestTracing = {
8
+ plugin: tracing,
9
+ options: { tracingHeader: config.get('tracing').header }
10
+ }
@@ -0,0 +1,70 @@
1
+ import { Cluster, Redis } from 'ioredis'
2
+
3
+ import { config } from '~/src/config/index.js'
4
+ import { createLogger } from '~/src/server/common/helpers/logging/logger.js'
5
+
6
+ /**
7
+ * Setup Redis and provide a redis client
8
+ *
9
+ * Local development - 1 Redis instance
10
+ * Out in the wild - Elasticache / Redis Cluster with username and password
11
+ */
12
+ export function buildRedisClient() {
13
+ const logger = createLogger()
14
+
15
+ const port = 6379
16
+ const db = 0
17
+ const redisConfig = config.get('redis')
18
+ const keyPrefix = redisConfig.keyPrefix
19
+ const host = redisConfig.host
20
+ let redisClient
21
+
22
+ if (!config.get('isProduction')) {
23
+ logger.info('Connecting to Redis using single instance')
24
+
25
+ redisClient = new Redis({
26
+ port,
27
+ host,
28
+ db,
29
+ keyPrefix
30
+ })
31
+ } else {
32
+ logger.info('Connecting to Redis using cluster')
33
+
34
+ redisClient = new Cluster(
35
+ [
36
+ {
37
+ host,
38
+ port
39
+ }
40
+ ],
41
+ {
42
+ keyPrefix,
43
+ slotsRefreshTimeout: 2000,
44
+ dnsLookup: (address, callback) => callback(null, address),
45
+ redisOptions: {
46
+ username: redisConfig.username,
47
+ password: redisConfig.password,
48
+ db,
49
+ tls: {}
50
+ }
51
+ }
52
+ )
53
+ }
54
+
55
+ redisClient.on('connect', () => {
56
+ logger.info('Connected to Redis server')
57
+ })
58
+
59
+ redisClient.on('close', () => {
60
+ logger.warn(
61
+ 'Redis connection closed attempting reconnect with default behavior'
62
+ )
63
+ })
64
+
65
+ redisClient.on('error', (error) => {
66
+ logger.error(error, `Redis connection error ${error}.`)
67
+ })
68
+
69
+ return redisClient
70
+ }
@@ -0,0 +1 @@
1
+ export const PREVIEW_PATH_PREFIX = '/preview'
@@ -0,0 +1,10 @@
1
+ # Pre-configured Forms
2
+
3
+ This folder holds pre-configured form definitions that can be loaded by the runner:
4
+
5
+ ```js
6
+ const server = await createServer({
7
+ formFileName: 'example.js',
8
+ formFilePath: join(cwd(), 'server/forms'),
9
+ })
10
+ ```